Data Cleaning
After reading data from text files or web pages it is common to have
to
This can be done using
Some examples of strings that need to be processed:
"12%"
"New York *"
"2,100"
"Temp: 12 °F"
Some of the most common cases are covered here.
Much more is available in R for
Data Science , in particular in the chapters
Removing a Percent Sign
Reading the GDP growth rate data from a web
page produced a data frame with a column like
s <- c("12%", "2%")
This can be converted to a numeric variable by
nchar(s)
## [1] 3 2
substr(s, 1, nchar(s) - 1)
## [1] "12" "2"
as.numeric(substr(s, 1, nchar(s) - 1))
## [1] 12 2
An alternative is to use sub() function to replace
"%" by the empty string "".
as.numeric(sub("%", "", s))
## [1] 12 2
The function parse_number() in the readr
package ignores the percent sign and extracts the numbers correctly:
library(readr)
parse_number(s)
## [1] 12 2
Removing Grouping Characters
Numbers are sometimes written using grouping characters :
s1 <- c("800", "2,100")
s2 <- c("800", "2,100", "3,123,500")
The comma is often used as a grouping character in the
US.
Other countries use different characters.
Other countries also use different characters for the decimal
separator.
sub() and gsub() can be used to remove
grouping characters:
sub(",", "", s1)
## [1] "800" "2100"
sub(",", "", s2)
## [1] "800" "2100" "3123,500"
gsub(",", "", s2)
## [1] "800" "2100" "3123500"
as.numeric(gsub(",", "", s2))
## [1] 800 2100 3123500
parse_number() can again be used:
parse_number(s2)
## [1] 800 2100 3123500
parse_number() is convenient but may be less robust:
parse_number(s2, locale = locale(grouping_mark = "'"))
## [1] 800 2 3
Separating City and State
Data often has city and state specified in a variable like
s <- c("Boston, MA", "Iowa City, IA")
If all state specifications are in two-letter form then city and
state can be extracted as sub-strings:
substr(s, 1, nchar(s) - 4)
## [1] "Boston" "Iowa City"
substr(s, nchar(s) - 1, nchar(s))
## [1] "MA" "IA"
This would not work if full state names are used.
An alternative is to use a regular expression .
Regular Expressions
Regular expressions are a language for expressing patterns in
strings.
Regular expressions should be developed carefully, like any program,
starting with simple steps and building up.
The simplest regular expressions are literal strings, like
%.
More complex expressions are built up using meta-characters
that have special meanings in regular expressions.
Many punctuation characters are regular expression
meta-characters.
Paul Murrell’s Introduction to
Data Technologies provides a good introduction in Section 9.9.2
and an extensive reference in Chapter 11.
The Strings chapter
in R for Data Science also
provides an introduction to regular expressions, but uses its own set of
functions from the tidyverse.
The web site Regular-Expressions.info
is a useful on-line resource.
Trimming White Space
If the data file is not consistent on the use of spaces in the
separator another possibility is to
sub(".*,", "", s)
## [1] " MA" " IA"
trimws(sub(".*,", "", s))
## [1] "MA" "IA"
Using separate()
If the city-state variable is already in a data frame or tibble, then
the separate() function from the tidyr package
can be used:
library(tibble)
library(tidyr)
d <- tibble(citystate = s)
d
## # A tibble: 2 × 1
## citystate
## <chr>
## 1 Boston, MA
## 2 Iowa City, IA
separate(d, citystate, c("city", "state"), sep = ", ")
## # A tibble: 2 × 2
## city state
## <chr> <chr>
## 1 Boston MA
## 2 Iowa City IA
Escaping Meta-Characters
Reading data from city
temperatures produces a variable that looks like
s <- c("London *", "Sydney")
The * indicates daylight saving or summer time.
We would like to
The * is a meta-character.
To include a literal meta-character in a pattern the meta-characters
needs to be escaped .
A meta-character is escaped by preceding it by a backslash
\.
But the backslash is a meta-character for R strings!
To put a backslash into an R string it needs to be written as
\\.
The pattern we want to match, a space followed by a *,
is ␣\*, with ␣ denoting a space character.
An R string containing these three characters is written as
" \\*".
It is often useful to write a pattern once and save it in a
variable.:
(pat <- " \\*")
## [1] " \\*"
This string contains three characters:
nchar(pat)
## [1] 3
Standard printing includes the backslash escape, and other escape
characters, so the printed string can be read back into R:
"a, b
and c"
## [1] "a, b\n and c"
The writeLines() function is useful to see the
characters in a string.
writeLines(pat)
## \*
To help make the space more visible we can add a delimiter:
writeLines(paste0("'", pat, "'"))
## ' \*'
Another option is to use sprintf() with
writeLines():
writeLines(sprintf("'%s'", pat))
## ' \*'
This pattern removes the space and asterisk if present:
s
## [1] "London *" "Sydney"
sub(pat, "", s)
## [1] "London" "Sydney"
The grep() and grepl() functions check
whether a pattern matches in elements of a character vector.
grep is short for Get REgular
exPression .
grep is a standard Linux command-line utility for
searching text files.
In R, grep() returns the indices of the elements
that match the pattern.
grepl() returns a logical vector indicating whether
there is a match:
grep(pat, s)
## [1] 1
grepl(pat, s)
## [1] TRUE FALSE
Matching Numbers
Reading temperature data might produce a string like
s <- c("32F", "-11F")
This can be processed as
substr(s, 1, nchar(s) - 1)
## [1] "32" "-11"
or as
sub("F", "", s)
## [1] "32" "-11"
An alternative uses some more regular expression features:
A pattern to match an integer, possibly preceded by a sign, is
intpat <- "[-+]?[[:digit:]]+"
s
## [1] "32F" "-11F"
sub(intpat, "X", s)
## [1] "XF" "XF"
The [ and ] meta-characters define
character sets ; any character between these will match.
[:digit:] specifies a character class of
digits.
There are a number of character classes , including
[:alpha:] alphabetic letters;
[:digit:] digits;
[:space:] white space (spaces, tabs).
[:lower:] lower case letters
[:upper:] upper case letters
[:alnum:] characters from [:alpha:] and
[:digit:]
[:punct:] punctuation characters
A sub_pattern can be extracted using back
references :
sub("([-+]?[[:digit:]]+).*", "\\1", s)
## [1] "32" "-11"
Sub-patterns can be specified with ( and
).
Back references can be used to refer to previous
sub-patterns by number.
The digit needs to be escaped with a \;
In an R string the \ needs to be escaped with a
second \.
A sub-string approach for a temperature embedded in a string:
s <- c("Temp: 32F", "Temp: -11F")
(s1 <- substr(s, 6, nchar(s)))
## [1] " 32F" " -11F"
(s2 <- substr(s1, 1, nchar(s1) - 1))
## [1] " 32" " -11"
as.numeric(s2)
## [1] 32 -11
Using regular expressions, sub-patterns, and back references:
sub(".*[[:space:]]+([-+]?[[:digit:]]+).*", "\\1", s)
## [1] "32" "-11"
parse_number() is again an alternative:
parse_number(s)
## [1] 32 -11
City Temperatures
The city
temperatures data used previously can be read using
library(rvest)
library(dplyr)
weather <- read_html("https://www.timeanddate.com/weather/")
w <- html_table(html_nodes(weather, "table"))[[1]]
w1 <- w[c(1, 4)]; names(w1) <- c("city", "temp")
w2 <- w[c(5, 8)]; names(w2) <- c("city", "temp")
w3 <- w[c(9, 12)]; names(w3) <- c("city", "temp")
ww <- rbind(w1, w2, w3)
ww <- filter(ww, city != "")
head(ww)
## # A tibble: 6 × 2
## city temp
## <chr> <chr>
## 1 Accra 86 °F
## 2 Addis Ababa 63 °F
## 3 Adelaide 55 °F
## 4 Algiers 63 °F
## 5 Almaty 48 °F
## 6 Amman 54 °F
Cleaning up and extracting dst:
www <- mutate(ww,
dst = grepl(" \\*", city),
city = sub(" \\*", "", city),
temp.txt = temp, ## for checking on conversion failures
temp = as.numeric(sub("([-+]?[[:digit:]]+).*", "\\1", temp)))
Check on NA values from conversion:
filter(www, is.na(temp))
## # A tibble: 0 × 4
## # ℹ 4 variables: city <chr>, temp <dbl>, dst <lgl>, temp.txt <chr>
www <- select(www, -temp.txt)
Five highest and lowest temperatures:
slice_max(www, temp, n = 5)
## # A tibble: 9 × 3
## city temp dst
## <chr> <dbl> <lgl>
## 1 Managua 95 FALSE
## 2 San Salvador 90 FALSE
## 3 Santo Domingo 89 FALSE
## 4 Accra 86 FALSE
## 5 Bangkok 86 FALSE
## 6 Kingston 86 FALSE
## 7 Lagos 86 FALSE
## 8 Mumbai 86 FALSE
## 9 San Juan 86 FALSE
slice_min(www, temp, n = 5)
## # A tibble: 6 × 3
## city temp dst
## <chr> <dbl> <lgl>
## 1 Anadyr 4 FALSE
## 2 Calgary 32 TRUE
## 3 Anchorage 33 TRUE
## 4 Edmonton 34 TRUE
## 5 Helsinki 34 TRUE
## 6 St. John's 34 TRUE
Temperatures for northern and southern hemisphere
(approximately):
ggplot(www, aes(x = temp, fill = dst)) +
geom_density(alpha = 0.5)
Tricky Characters
Some examples:
(s <- head(ww$temp))
## [1] "86 °F" "63 °F" "55 °F" "63 °F" "48 °F" "54 °F"
nchar(s)
## [1] 5 5 5 5 5 5
substr(s, 1, nchar(s) - 3)
## [1] "86" "63" "55" "63" "48" "54"
substr(s, 1, nchar(s) - 2)
## [1] "86 " "63 " "55 " "63 " "48 " "54 "
as.numeric(substr(s, 1, nchar(s) - 2))
## Warning: NAs introduced by coercion
## [1] NA NA NA NA NA NA
as.numeric("82 ")
## [1] 82
sub(" .*", "", s)
## [1] "86 °F" "63 °F" "55 °F" "63 °F" "48 °F" "54 °F"
The problem is two non-ascii characters.
The stri_escape_unicode() function from the
stringi package can make these characters more visible:
stringi::stri_escape_unicode(s)
## [1] "86\\u00a0\\u00b0F" "63\\u00a0\\u00b0F" "55\\u00a0\\u00b0F"
## [4] "63\\u00a0\\u00b0F" "48\\u00a0\\u00b0F" "54\\u00a0\\u00b0F"
The troublesome characters are:
Using the unicode specification for the no-break space does work:
sub("\u00a0.*", "", s)
## [1] "86" "63" "55" "63" "48" "54"
Variations in Regular Expression Engines
Many tools and languages support working with regular
expressions.
R supports:
The POSIX standard for extended regular expressions .
This is the default engine.
Perl-compatible regular expressions (PCRE ).
This engine is selected by adding perl = TRUE in calls to
functions using regular expressions.
Different engines can differ in how certain regular expressions are
interpreted, especially when non-ASCII characters are involved.
Different engines also sometimes offer shorthand notations, in
particular for character classes.
Some examples:
[:digit:]
[0-9]
\d
digits
[:upper:]
[A-Z]
\u
upper case letters
[:lower:]
[a-z]
\l
lower-case letters
[:alpha:]
[A-Za-z]
upper- and lower-case letters
[:space:]
[ \t\n]
\s
whitespace characters
The shorthand versions need to have their \ escaped when
used in an R string:
intpat <- ".*\\s([-+]?\\d+).*"
sub(intpat, "\\1", c("Temp: 32F", "Temp: -11F"))
## [1] "32" "-11"
Raw Strings
Raw strings can make writing regular expressions a little
easier.
In R a raw string is specified as r"(...)" or
r"{...}".
The ... characters can be any characters and are taken
literally, without special interpretation that might require
escaping.
For the integer pattern:
intpat <- ".*\\s([-+]?\\d+).*"
intpat_raw <- r"{.*\s([-+]?\d+).*}"
intpat == intpat_raw
## [1] TRUE
Python, C++, and other languages provide similar facilites.
R’s raw string syntax it modeled after the one used in C++.
Regular Expression Modifiers
Regular expression modifiers are of the form (?...).
Some regular expressions implementations default to case sensitive,
others to case insensitive. You can specify what you want with a
modifier :
(?i)... specifies a case-insensitive match.
(?-i)... specifies a case-sensitive match.
Another useful modifier is the non-capturing group
(?:...). This groups elements without saving them for later
use.
The modifier (?x) specifies extended free-space
mode : most whitespace is ignored and text following a
# is treated as a comment. (This requires
perl = TRUE in R.)
intpat_raw2 <-
r"{(?x)
.*\s # zero or more characters followed by a space
( # start a capturing group
[-+]? # optional sign
\d+ # one or more digits
) # end the capturing group
.* # zero or more characters
}"
sub(intpat_raw2, "\\1", c("Temp: 32F", "Temp: -11F"), perl = TRUE)
## [1] "32" "-11"
Using an LLM
Using a Large Language Model (LLM) can help with creating and
understanding regular expressions.
Some possible prompts:
regex to match a number
regex to match a number with an optional sign
regex to match a number with an optional sign and an optional
fractional part
explain the regex ([-+]?\d+).*
As with any use of LLMs in coding, you need to know enough to
recognize whether the answer provided makes sense or not.
A Note on Sorting
Non-ASCII characters can create issues for sorting strings, but even
ASCII character sort order is not the same in all locales .
The LETTERS data set contains the upper-case letters in
alphabetical order for the English sorting convention and most other
locales:
LETTERS
## [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S"
## [20] "T" "U" "V" "W" "X" "Y" "Z"
But in Estonian, Latvian, and Lithuanian:
stringr::str_sort(LETTERS, locale = "est")
## [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S"
## [20] "Z" "T" "U" "V" "W" "X" "Y"
Ordering of lower case and upper case letters in English and most
other locales:
stringr::str_sort(c("A", "a"), locale = "eng")
## [1] "a" "A"
But in Danish, and also Maltese:
stringr::str_sort(c("A", "a"), locale = "dan")
## [1] "A" "a"
Encoding Issues
Files contain a sequence of 8-bit integers, or bytes .
These are the integers from 0 through 255.
For text files, these bytes are interpreted as representing
characters.
The mapping from bytes to characters is called an
encoding .
The encoding for the characters used in American English is ASCII:
the American Standard Code for Information Interchange .
The ASCII encoding uses only
the integers 0 through 127.
The ASCII encoding is adequate for American uses; even UK text files
need more: the pound sign £.
Encodings that use integers 128 through 255:
Many encodings are available to support other alphabets and character
sets.
Fortunately most systems now use Unicode with the UTF-8 encoding for
representing non-ASCII characters.
If you need to read a file with non-ASCII characters using
read.csv() or similar base functions a good place to start
is to specify encoding = "UTF-8".
The functions in the readr package default to assuming
the encoding is UTF-8.
Getting the encoding wrong can result in a few messed up characters
or in an entire string being messed up:
Reading files without specifying the correct encoding might produce
strings like
x1
## [1] "El Ni\xf1o was particularly bad this year"
x2
## [1] "\x82\xb1\x82\xf1\x82ɂ\xbf\x82\xcd"
If the correct encoding is known, then these can be fixed after the
fact with iconv():
iconv(x1, "Latin1", "UTF-8")
## [1] "El Niño was particularly bad this year"
iconv(x2, "Shift-JIS", "UTF-8")
## [1] "こんにちは"
Re-reading the file with the proper encoding specified may be a
better option.
The readr function guess_encoding() may
help identify the correct encoding if it is not specified in the data
documentation.
Handling encoding issues in R used to be more complicated on Windows,
but this has improved with recent releases of R.
Fonts
Once you have the right encoding for character data, you also need a
set of fonts so your data shows up properly in the console, the editor,
and graphs.
Fonts may already be installed.
c("\U{1f600}", "\u3041", "\u4a00")
## [1] "😀" "ぁ" "䨀"
If glyphs are not availabl, the characters may be shown in various
ways.
c("\U{105b8}", "\u3040")
## [1] "𐖸" "\u3040"
How to install fonts will depend on your platform.
A plot with emojis:
data.frame(x = 1:10,
y = runif(10),
z = sapply(0x1f600 + 0:9,
intToUtf8)) |>
ggplot(aes(x, y, label = z)) +
geom_text()
A Note on Line Endings
On Linux and current macOS lines of text in files are terminated by a
line feed (LF) character \n or
\x0a.
On Windows, and in HTML, lines are terminated by a carriage
return (CR, \r, x0d) followed by a
linefeed (CRLF).
On Linux and macOS there is no difference in reading or writing to a
file in text mode or binary mode .
On Windows, reading in text mode translates from CRLF line endings to
LF, and writing translates LF line endings to CRLF.
Many file transfer operations will do this conversion.
Git will check out text files with the appropriate line endings for
the platform.
Most higher-level tools on Linux will work with CRLF line endings,
and on Windows will work for LF line endings.
Occasionally you may see some issues, and need to fix the line
endings yourself.
Getting the Current Temperature
The tools described here come in handy when scraping data from the
web.
This code gets the current
temperature in Iowa City from the National Weather Service:
library(xml2)
url <- "http://forecast.weather.gov/zipcity.php?inputstring=Iowa+City,IA"
page <- read_html(url)
xpath <- "//p[@class=\"myforecast-current-lrg\"]"
tempNode <- xml_find_first(page, xpath)
nodeText <- xml_text(tempNode)
as.numeric(sub("([-+]?\\d+).*", "\\1", nodeText))
An example of creating a current temperature map is described here .
Exercises
Complete all lessons and exercises in the https://regexone.com/
online interactive tutorial.
Consider the code
library(tidyverse)
filter(mpg, grepl(---, model))
For which of the following regular expressions in place of
--- will this code return the subset of rows for all models
that contain either 4wd or awd in their model
names?
“[a4]wd”
“[a4]wd”
“4awd”
“[[4a]]wd”
LS0tCnRpdGxlOiAiU3RyaW5nIFBhcnNpbmcgYW5kIFJlZ3VsYXIgRXhwcmVzc2lvbnMiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCjxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0ic3RhdDQ1ODAuY3NzIiB0eXBlPSJ0ZXh0L2NzcyIgLz4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4gLnJlbWFyay1jb2RlIHsgZm9udC1zaXplOiA4NSU7IH0gPC9zdHlsZT4KYGBge3Igc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0Kc291cmNlKGhlcmU6OmhlcmUoInNldHVwLlIiKSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGNvbGxhcHNlID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgZmlnLmhlaWdodCA9IDUsIGZpZy53aWR0aCA9IDYsIGZpZy5hbGlnbiA9ICJjZW50ZXIiKQoKb3B0aW9ucyhodG1sdG9vbHMuZGlyLnZlcnNpb24gPSBGQUxTRSkKCnNldC5zZWVkKDEyMzQ1KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkobGF0dGljZSkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ3JpZEV4dHJhKQp0aGVtZV9zZXQodGhlbWVfbWluaW1hbCgpICsKICAgICAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwKICAgICAgICAgICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvciA9ICJncmV5MzAiLCBmaWxsID0gTkEpKSkKYGBgCgoKIyMgRGF0YSBDbGVhbmluZwoKQWZ0ZXIgcmVhZGluZyBkYXRhIGZyb20gdGV4dCBmaWxlcyBvciB3ZWIgcGFnZXMgaXQgaXMgY29tbW9uIHRvIGhhdmUgdG8KCiogY2xlYW4gdXAgc3RyaW5nIHZhcmlhYmxlczsKCiogZXh0cmFjdCBudW1iZXJzLgoKVGhpcyBjYW4gYmUgZG9uZSB1c2luZwoKKiBoaWdoLWxldmVsIGZ1bmN0aW9ucyB0aGF0IHVzdWFsbHkgd29yayBidXQgbm90IGFsd2F5czsKCiogdXNpbmcgc3RyaW5nIHN1YnNldHRpbmc7CgoqIHVzaW5nIF9yZWd1bGFyIGV4cHJlc3Npb25zXy4KClNvbWUgZXhhbXBsZXMgb2Ygc3RyaW5ncyB0aGF0IG5lZWQgdG8gYmUgcHJvY2Vzc2VkOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KIjEyJSIKIk5ldyBZb3JrICoiCiIyLDEwMCIKIlRlbXA6IDEyIMKwRiIKYGBgCgpTb21lIG9mIHRoZSBtb3N0IGNvbW1vbiBjYXNlcyBhcmUgY292ZXJlZCBoZXJlLgoKTXVjaCBtb3JlIGlzIGF2YWlsYWJsZSBpbiBbX1IgZm9yIERhdGEKU2NpZW5jZV9dKGh0dHBzOi8vcjRkcy5oYWRsZXkubnovKSwgaW4gcGFydGljdWxhciBpbiB0aGUgY2hhcHRlcnMKCiogW19EYXRhIEltcG9ydF9dKGh0dHBzOi8vcjRkcy5oYWRsZXkubnovZGF0YS1pbXBvcnQuaHRtbCk7CiogW19TdHJpbmdzX10oaHR0cHM6Ly9yNGRzLmhhZGxleS5uei9zdHJpbmdzLmh0bWwpLgoKCiMjIFJlbW92aW5nIGEgUGVyY2VudCBTaWduCgpSZWFkaW5nIHRoZSBHRFAgZ3Jvd3RoIHJhdGUgZGF0YSBmcm9tIGEgClt3ZWIgcGFnZV0oaHR0cHM6Ly93d3cubXVsdHBsLmNvbS91cy1yZWFsLWdkcC1ncm93dGgtcmF0ZS90YWJsZS9ieS1xdWFydGVyKQpwcm9kdWNlZCBhIGRhdGEgZnJhbWUgd2l0aCBhIGNvbHVtbiBsaWtlCgpgYGB7cn0KcyA8LSBjKCIxMiUiLCAiMiUiKQpgYGAKClRoaXMgY2FuIGJlIGNvbnZlcnRlZCB0byBhIG51bWVyaWMgdmFyaWFibGUgYnkKCiogZXh0cmFjdGluZyB0aGUgc3ViLXN0cmluZyB3aXRob3V0IHRoZSBgJWAKCiogYW5kIHRoZW4gdXNpbmcgYGFzLm51bWVyaWMoKWAuCgpgYGB7cn0KbmNoYXIocykKc3Vic3RyKHMsIDEsIG5jaGFyKHMpIC0gMSkKYXMubnVtZXJpYyhzdWJzdHIocywgMSwgbmNoYXIocykgLSAxKSkKYGBgCgpBbiBhbHRlcm5hdGl2ZSBpcyB0byB1c2UgYHN1YigpYCBmdW5jdGlvbiB0byByZXBsYWNlIGAiJSJgIGJ5IHRoZQplbXB0eSBzdHJpbmcgYCIiYC4KCmBgYHtyfQphcy5udW1lcmljKHN1YigiJSIsICIiLCBzKSkKYGBgCgpUaGUgZnVuY3Rpb24gYHBhcnNlX251bWJlcigpYCBpbiB0aGUgYHJlYWRyYCBwYWNrYWdlIGlnbm9yZXMgdGhlIHBlcmNlbnQKc2lnbiBhbmQgZXh0cmFjdHMgdGhlIG51bWJlcnMgY29ycmVjdGx5OgoKYGBge3J9CmxpYnJhcnkocmVhZHIpCnBhcnNlX251bWJlcihzKQpgYGAKCgojIyBSZW1vdmluZyBHcm91cGluZyBDaGFyYWN0ZXJzCgpOdW1iZXJzIGFyZSBzb21ldGltZXMgd3JpdHRlbiB1c2luZyBfZ3JvdXBpbmcgY2hhcmFjdGVyc186CgpgYGB7cn0KczEgPC0gYygiODAwIiwgIjIsMTAwIikKczIgPC0gYygiODAwIiwgIjIsMTAwIiwgIjMsMTIzLDUwMCIpCmBgYAoKKiBUaGUgY29tbWEgaXMgb2Z0ZW4gdXNlZCBhcyBhIGdyb3VwaW5nIGNoYXJhY3RlciBpbiB0aGUgVVMuCgoqIE90aGVyIGNvdW50cmllcyB1c2UgZGlmZmVyZW50IGNoYXJhY3RlcnMuCgoqIE90aGVyIGNvdW50cmllcyBhbHNvIHVzZSBkaWZmZXJlbnQgY2hhcmFjdGVycyBmb3IgdGhlIGRlY2ltYWwgc2VwYXJhdG9yLgoKYHN1YigpYCBhbmQgYGdzdWIoKWAgY2FuIGJlIHVzZWQgdG8gcmVtb3ZlIGdyb3VwaW5nIGNoYXJhY3RlcnM6CgpgYGB7cn0Kc3ViKCIsIiwgIiIsIHMxKQpzdWIoIiwiLCAiIiwgczIpCmBgYAoKYGBge3J9CmdzdWIoIiwiLCAiIiwgczIpCmFzLm51bWVyaWMoZ3N1YigiLCIsICIiLCBzMikpCmBgYAoKKiBgc3ViKClgIHJlcGxhY2VzIHRoZSBmaXJzdCBtYXRjaCB0byBhIHBhdHRlcm47CgoqIGBnc3ViKClgIHJlcGxhY2VzIGFsbCBtYXRjaGVzLgoKYHBhcnNlX251bWJlcigpYCBjYW4gYWdhaW4gYmUgdXNlZDoKCmBgYHtyfQpwYXJzZV9udW1iZXIoczIpCmBgYAoKYHBhcnNlX251bWJlcigpYCBpcyBjb252ZW5pZW50IGJ1dCBtYXkgYmUgbGVzcyByb2J1c3Q6CgoqIEluIFN3aXR6ZXJsYW5kIHRoZSBncm91cGluZyBjaGFyYWN0ZXIgaXMgYCdgLgoKKiBJZiBgcGFyc2VfbnVtYmVyKClgIGhhcyBpdHMgZGVmYXVsdHMgc2V0IHRvIFN3aXNzIGNvbnZlbnRpb25zOgoKYGBge3J9CnBhcnNlX251bWJlcihzMiwgbG9jYWxlID0gbG9jYWxlKGdyb3VwaW5nX21hcmsgPSAiJyIpKQpgYGAKCiMjIFNlcGFyYXRpbmcgQ2l0eSBhbmQgU3RhdGUKCkRhdGEgb2Z0ZW4gaGFzIGNpdHkgYW5kIHN0YXRlIHNwZWNpZmllZCBpbiBhIHZhcmlhYmxlIGxpa2UKCmBgYHtyfQpzIDwtIGMoIkJvc3RvbiwgTUEiLCAiSW93YSBDaXR5LCBJQSIpCmBgYAoKSWYgYWxsIHN0YXRlIHNwZWNpZmljYXRpb25zIGFyZSBpbiB0d28tbGV0dGVyIGZvcm0gdGhlbiBjaXR5IGFuZCBzdGF0ZQpjYW4gYmUgZXh0cmFjdGVkIGFzIHN1Yi1zdHJpbmdzOgoKYGBge3J9CnN1YnN0cihzLCAxLCBuY2hhcihzKSAtIDQpCnN1YnN0cihzLCBuY2hhcihzKSAtIDEsIG5jaGFyKHMpKQpgYGAKClRoaXMgd291bGQgbm90IHdvcmsgaWYgZnVsbCBzdGF0ZSBuYW1lcyBhcmUgdXNlZC4KCkFuIGFsdGVybmF0aXZlIGlzIHRvIHVzZSBhIF9yZWd1bGFyIGV4cHJlc3Npb25fLgoKCiMjIFJlZ3VsYXIgRXhwcmVzc2lvbnMKClJlZ3VsYXIgZXhwcmVzc2lvbnMgYXJlIGEgbGFuZ3VhZ2UgZm9yIGV4cHJlc3NpbmcgcGF0dGVybnMgaW4gc3RyaW5ncy4KClJlZ3VsYXIgZXhwcmVzc2lvbnMgc2hvdWxkIGJlIGRldmVsb3BlZCBjYXJlZnVsbHksIGxpa2UgYW55IHByb2dyYW0sCnN0YXJ0aW5nIHdpdGggc2ltcGxlIHN0ZXBzIGFuZCBidWlsZGluZyB1cC4KClRoZSBzaW1wbGVzdCByZWd1bGFyIGV4cHJlc3Npb25zIGFyZSBsaXRlcmFsIHN0cmluZ3MsIGxpa2UgYCVgLgoKTW9yZSBjb21wbGV4IGV4cHJlc3Npb25zIGFyZSBidWlsdCB1cCB1c2luZyBfbWV0YS1jaGFyYWN0ZXJzXyB0aGF0CmhhdmUgc3BlY2lhbCBtZWFuaW5ncyBpbiByZWd1bGFyIGV4cHJlc3Npb25zLgoKTWFueSBwdW5jdHVhdGlvbiBjaGFyYWN0ZXJzIGFyZSByZWd1bGFyIGV4cHJlc3Npb24gbWV0YS1jaGFyYWN0ZXJzLgoKUGF1bCBNdXJyZWxsJ3MgW19JbnRyb2R1Y3Rpb24gdG8gRGF0YQogIFRlY2hub2xvZ2llc19dKGh0dHA6Ly93d3cuc3RhdC5hdWNrbGFuZC5hYy5uei9+cGF1bC9JdERULykgcHJvdmlkZXMKICBhIGdvb2QgaW50cm9kdWN0aW9uIGluIFNlY3Rpb24gOS45LjIgYW5kIGFuIGV4dGVuc2l2ZSByZWZlcmVuY2UgaW4KICBDaGFwdGVyIDExLgoKVGhlIFtTdHJpbmdzIGNoYXB0ZXJdKGh0dHBzOi8vcjRkcy5oYWRsZXkubnovc3RyaW5ncy5odG1sKSBpbiBbUiBmb3IKRGF0YSBTY2llbmNlXShodHRwczovL3I0ZHMuaGFkbGV5Lm56LykgYWxzbyBwcm92aWRlcyBhbiBpbnRyb2R1Y3Rpb24gdG8KcmVndWxhciBleHByZXNzaW9ucywgYnV0IHVzZXMgaXRzIG93biBzZXQgb2YgZnVuY3Rpb25zIGZyb20gdGhlCmB0aWR5dmVyc2VgLgoKVGhlIHdlYiBzaXRlCltSZWd1bGFyLUV4cHJlc3Npb25zLmluZm9dKGh0dHBzOi8vd3d3LnJlZ3VsYXItZXhwcmVzc2lvbnMuaW5mby8pIGlzIGEKdXNlZnVsIG9uLWxpbmUgcmVzb3VyY2UuCgoKIyMgUmVndWxhciBFeHByZXNzaW9uIE1ldGEtQ2hhcmN0ZXJzCgpTb21lIGltcG9ydGFudCBtZXRhLWNoYXJhY3RlcnMgYXJlIGAuYCwgYCpgLCBgK2AsIGFuZCBgP2A6CgoqIFRoZSBwZXJpb2QgYC5gIHN0YW5kcyBmb3IgYW55IGNoYXJhY3Rlci4KCiogVGhlIGFzdGVyaXNrIGAqYCBtZWFucyB6ZXJvLCBvbmUsIG9yIG1vcmUgb2YgdGhlIHByZWNlZGluZyBjaGFyYWN0ZXIKICBzcGVjaWZpY2F0aW9uLgoKKiBUaGUgcGx1cyBzaWduIGArYCBtZWFucyBvbmUsIG9yIG1vcmUgb2YgdGhlIHByZWNlZGluZyBjaGFyYWN0ZXIKICBzcGVjaWZpY2F0aW9uLgoKKiBUaGUgcXVlc3Rpb24gbWFyayBgP2AgbWVhbnMgemVybyBvciBvbmUgb2YgdGhlIHByZWNlZGluZyBjaGFyYWN0ZXIKICBzcGVjaWZpY2F0aW9uLgoKVGhlIHBhdHRlcm4gYCIsLioiYCBtYXRjaGVzIGEgY29tbWEgYCxgIGZvbGxvd2VkIGJ5IHplcm8gb3IgbW9yZQpjaGFyYWN0ZXJzOgoKYGBge3J9CnMKc3ViKCIsLioiLCAiIiwgcykKYGBgCgpUaGUgcGF0dGVybiBgIi4qLCAiYCBtYXRjaGVzIHplcm8gb3IgbW9yZSBjaGFyYWN0ZXJzIGZvbGxvd2VkIGJ5IGEKY29tbWEgYW5kIGEgc3BhY2U6CgpgYGB7cn0Kc3ViKCIuKiwgIiwgIiIsIHMpCmBgYAoKCiMjIFRyaW1taW5nIFdoaXRlIFNwYWNlCgpJZiB0aGUgZGF0YSBmaWxlIGlzIG5vdCBjb25zaXN0ZW50IG9uIHRoZSB1c2Ugb2Ygc3BhY2VzIGluIHRoZQpzZXBhcmF0b3IgYW5vdGhlciBwb3NzaWJpbGl0eSBpcyB0bwoKKiByZW1vdmUgdGhlIGNoYXJhY3RlcnMgdGhyb3VnaCB0aGUgY29tbWE7CgoqIHRyaW0gdGhlIHdoaXRlIHNwYWNlIGZyb20gdGhlIHJlc3VsdC4KCmBgYHtyfQpzdWIoIi4qLCIsICIiLCBzKQpgYGAKCmBgYHtyfQp0cmltd3Moc3ViKCIuKiwiLCAiIiwgcykpCmBgYAoKCiMjIFVzaW5nIGBzZXBhcmF0ZSgpYAoKSWYgdGhlIGNpdHktc3RhdGUgdmFyaWFibGUgaXMgYWxyZWFkeSBpbiBhIGRhdGEgZnJhbWUgb3IgdGliYmxlLCB0aGVuCnRoZSBgc2VwYXJhdGUoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYHRpZHlyYCBwYWNrYWdlIGNhbiBiZSB1c2VkOgoKYGBge3J9CmxpYnJhcnkodGliYmxlKQpsaWJyYXJ5KHRpZHlyKQpkIDwtIHRpYmJsZShjaXR5c3RhdGUgPSBzKQpkCmBgYAoKYGBge3J9CnNlcGFyYXRlKGQsIGNpdHlzdGF0ZSwgYygiY2l0eSIsICJzdGF0ZSIpLCBzZXAgPSAiLCAiKQpgYGAKCgojIyBFc2NhcGluZyBNZXRhLUNoYXJhY3RlcnMKClJlYWRpbmcgZGF0YSBmcm9tCltjaXR5IHRlbXBlcmF0dXJlc10oaHR0cHM6Ly93d3cudGltZWFuZGRhdGUuY29tL3dlYXRoZXIvKQpwcm9kdWNlcyBhIHZhcmlhYmxlIHRoYXQgbG9va3MgbGlrZQoKYGBge3J9CnMgPC0gYygiTG9uZG9uICoiLCAiU3lkbmV5IikKYGBgCgpUaGUgYCpgIGluZGljYXRlcyBkYXlsaWdodCBzYXZpbmcgb3Igc3VtbWVyIHRpbWUuCgpXZSB3b3VsZCBsaWtlIHRvCgoqIGV4dHJhY3QgdGhlIGNpdHkgbmFtZTsKCiogZXh0cmFjdCB3aGV0aGVyIHRoZXJlIGlzIGEgYCpgLgoKVGhlIGAqYCBpcyBhIG1ldGEtY2hhcmFjdGVyLgoKVG8gaW5jbHVkZSBhIGxpdGVyYWwgbWV0YS1jaGFyYWN0ZXIgaW4gYSBwYXR0ZXJuIHRoZSBtZXRhLWNoYXJhY3RlcnMKbmVlZHMgdG8gYmUgX2VzY2FwZWRfLgoKQSBtZXRhLWNoYXJhY3RlciBpcyBlc2NhcGVkIGJ5IHByZWNlZGluZyBpdCBieSBhIGJhY2tzbGFzaCBgXGAuCgpCdXQgdGhlIGJhY2tzbGFzaCBpcyBhIG1ldGEtY2hhcmFjdGVyIGZvciBSIHN0cmluZ3MhCgpUbyBwdXQgYSBiYWNrc2xhc2ggaW50byBhbiBSIHN0cmluZyBpdCBuZWVkcyB0byBiZSB3cml0dGVuIGFzIGBcXGAuCgpUaGUgcGF0dGVybiB3ZSB3YW50IHRvIG1hdGNoLCBhIHNwYWNlIGZvbGxvd2VkIGJ5IGEgYCpgLCBpcyAKYOKQo1wqYCwgd2l0aCBg4pCjYCBkZW5vdGluZyBhIHNwYWNlIGNoYXJhY3Rlci4KCkFuIFIgc3RyaW5nIGNvbnRhaW5pbmcgdGhlc2UgdGhyZWUgY2hhcmFjdGVycyBpcyB3cml0dGVuIGFzIGAiIFxcKiJgLgoKSXQgaXMgb2Z0ZW4gdXNlZnVsIHRvIHdyaXRlIGEgcGF0dGVybiBvbmNlIGFuZCBzYXZlIGl0IGluIGEgdmFyaWFibGUuXDoKCmBgYHtyfQoocGF0IDwtICIgXFwqIikKYGBgCgpUaGlzIHN0cmluZyBjb250YWlucyB0aHJlZSBjaGFyYWN0ZXJzOgoKYGBge3J9Cm5jaGFyKHBhdCkKYGBgCgpTdGFuZGFyZCBwcmludGluZyBpbmNsdWRlcyB0aGUgYmFja3NsYXNoIGVzY2FwZSwgYW5kIG90aGVyIGVzY2FwZQpjaGFyYWN0ZXJzLCBzbyB0aGUgcHJpbnRlZCBzdHJpbmcgY2FuIGJlIHJlYWQgYmFjayBpbnRvIFI6CgpgYGB7cn0KImEsIGIKIGFuZCBjIgpgYGAKClRoZSBgd3JpdGVMaW5lcygpYCBmdW5jdGlvbiBpcyB1c2VmdWwgdG8gc2VlIHRoZSBjaGFyYWN0ZXJzIGluIGEgc3RyaW5nLgoKYGBge3J9CndyaXRlTGluZXMocGF0KQpgYGAKClRvIGhlbHAgbWFrZSB0aGUgc3BhY2UgbW9yZSB2aXNpYmxlIHdlIGNhbiBhZGQgYSBkZWxpbWl0ZXI6CgpgYGB7cn0Kd3JpdGVMaW5lcyhwYXN0ZTAoIiciLCBwYXQsICInIikpCmBgYAoKQW5vdGhlciBvcHRpb24gaXMgdG8gdXNlIGBzcHJpbnRmKClgIHdpdGggYHdyaXRlTGluZXMoKWA6CgpgYGB7cn0Kd3JpdGVMaW5lcyhzcHJpbnRmKCInJXMnIiwgcGF0KSkKYGBgCgpUaGlzIHBhdHRlcm4gcmVtb3ZlcyB0aGUgc3BhY2UgYW5kIGFzdGVyaXNrIGlmIHByZXNlbnQ6CgpgYGB7cn0KcwpzdWIocGF0LCAiIiwgcykKYGBgCgpUaGUgYGdyZXAoKWAgYW5kIGBncmVwbCgpYCBmdW5jdGlvbnMgY2hlY2sgd2hldGhlciBhIHBhdHRlcm4gbWF0Y2hlcyBpbgplbGVtZW50cyBvZiBhIGNoYXJhY3RlciB2ZWN0b3IuCgoqIGBncmVwYCBpcyBzaG9ydCBmb3IgX0dldCBSRWd1bGFyIGV4UHJlc3Npb25fLgoKKiBgZ3JlcGAgaXMgYSBzdGFuZGFyZCBMaW51eCBjb21tYW5kLWxpbmUgdXRpbGl0eSBmb3Igc2VhcmNoaW5nIHRleHQgZmlsZXMuCgoqIEluIFIsIGBncmVwKClgIHJldHVybnMgdGhlIGluZGljZXMgb2YgdGhlIGVsZW1lbnRzIHRoYXQgbWF0Y2ggdGhlIHBhdHRlcm4uCgoqIGBncmVwbCgpYCByZXR1cm5zIGEgbG9naWNhbCB2ZWN0b3IgaW5kaWNhdGluZyB3aGV0aGVyIHRoZXJlIGlzIGEgbWF0Y2g6CgpgYGB7cn0KZ3JlcChwYXQsIHMpCmdyZXBsKHBhdCwgcykKYGBgCgoKIyMgTWF0Y2hpbmcgTnVtYmVycwoKUmVhZGluZyB0ZW1wZXJhdHVyZSBkYXRhIG1pZ2h0IHByb2R1Y2UgYSBzdHJpbmcgbGlrZQoKYGBge3J9CnMgPC0gYygiMzJGIiwgIi0xMUYiKQpgYGAKClRoaXMgY2FuIGJlIHByb2Nlc3NlZCBhcwoKYGBge3J9CnN1YnN0cihzLCAxLCBuY2hhcihzKSAtIDEpCmBgYAoKb3IgYXMKCmBgYHtyfQpzdWIoIkYiLCAiIiwgcykKYGBgCgpBbiBhbHRlcm5hdGl2ZSB1c2VzIHNvbWUgbW9yZSByZWd1bGFyIGV4cHJlc3Npb24gZmVhdHVyZXM6CgoqIG1hdGNoIHRoZSBudW1iZXIgd2l0aGluIHRoZSBzdHJpbmc7CgoqIGV4dHJhY3QgYSBzdWItbWF0Y2guCgpBIHBhdHRlcm4gdG8gbWF0Y2ggYW4gaW50ZWdlciwgcG9zc2libHkgcHJlY2VkZWQgYnkgYSBzaWduLCBpcwoKYGBge3J9CmludHBhdCA8LSAiWy0rXT9bWzpkaWdpdDpdXSsiCnMKc3ViKGludHBhdCwgIlgiLCBzKQpgYGAKClRoZSBgW2AgYW5kIGBdYCBtZXRhLWNoYXJhY3RlcnMgZGVmaW5lIF9jaGFyYWN0ZXIgc2V0c187IGFueSBjaGFyYWN0ZXIKYmV0d2VlbiB0aGVzZSB3aWxsIG1hdGNoLgoKYFs6ZGlnaXQ6XWAgc3BlY2lmaWVzIGEgX2NoYXJhY3RlciBjbGFzc18gb2YgZGlnaXRzLgoKVGhlcmUgYXJlIGEgbnVtYmVyIG9mIF9jaGFyYWN0ZXIgY2xhc3Nlc18sIGluY2x1ZGluZwoKKiBgWzphbHBoYTpdYCBhbHBoYWJldGljIGxldHRlcnM7CgoqIGBbOmRpZ2l0Ol1gIGRpZ2l0czsKCiogYFs6c3BhY2U6XWAgd2hpdGUgc3BhY2UgKHNwYWNlcywgdGFicykuCgoqIGBbOmxvd2VyOl1gIGxvd2VyIGNhc2UgbGV0dGVycwoKKiBgWzp1cHBlcjpdYCB1cHBlciBjYXNlIGxldHRlcnMKCiogYFs6YWxudW06XWAgY2hhcmFjdGVycyBmcm9tIGBbOmFscGhhOl1gIGFuZCBgWzpkaWdpdDpdYAoKKiBgWzpwdW5jdDpdYCBwdW5jdHVhdGlvbiBjaGFyYWN0ZXJzCgpBIF9zdWJfcGF0dGVybl8gY2FuIGJlIGV4dHJhY3RlZCB1c2luZyBfYmFjayByZWZlcmVuY2VzXzoKCmBgYHtyfQpzdWIoIihbLStdP1tbOmRpZ2l0Ol1dKykuKiIsICJcXDEiLCBzKQpgYGAKCiogU3ViLXBhdHRlcm5zIGNhbiBiZSBzcGVjaWZpZWQgd2l0aCBgKGAgYW5kIGApYC4KCiogX0JhY2sgcmVmZXJlbmNlc18gY2FuIGJlIHVzZWQgdG8gcmVmZXIgdG8gcHJldmlvdXMgc3ViLXBhdHRlcm5zIGJ5IG51bWJlci4KCiogVGhlIGRpZ2l0IG5lZWRzIHRvIGJlIGVzY2FwZWQgd2l0aCBhIGBcYDsKCiogSW4gYW4gUiBzdHJpbmcgdGhlIGBcYCBuZWVkcyB0byBiZSBlc2NhcGVkIHdpdGggYSBzZWNvbmQgYFxgLgoKQSBzdWItc3RyaW5nIGFwcHJvYWNoIGZvciBhIHRlbXBlcmF0dXJlIGVtYmVkZGVkIGluIGEgc3RyaW5nOgoKYGBge3J9CnMgPC0gYygiVGVtcDogIDMyRiIsICJUZW1wOiAtMTFGIikKKHMxIDwtIHN1YnN0cihzLCA2LCBuY2hhcihzKSkpCihzMiA8LSBzdWJzdHIoczEsIDEsIG5jaGFyKHMxKSAtIDEpKQphcy5udW1lcmljKHMyKQpgYGAKClVzaW5nIHJlZ3VsYXIgZXhwcmVzc2lvbnMsIHN1Yi1wYXR0ZXJucywgYW5kIGJhY2sgcmVmZXJlbmNlczoKCmBgYHtyfQpzdWIoIi4qW1s6c3BhY2U6XV0rKFstK10/W1s6ZGlnaXQ6XV0rKS4qIiwgIlxcMSIsIHMpCmBgYAoKYHBhcnNlX251bWJlcigpYCBpcyBhZ2FpbiBhbiBhbHRlcm5hdGl2ZToKCmBgYHtyfQpwYXJzZV9udW1iZXIocykKYGBgCgoKIyMgQ2l0eSBUZW1wZXJhdHVyZXMKClRoZSBbY2l0eSB0ZW1wZXJhdHVyZXNdKGh0dHBzOi8vd3d3LnRpbWVhbmRkYXRlLmNvbS93ZWF0aGVyLykKZGF0YSB1c2VkIHByZXZpb3VzbHkgY2FuIGJlIHJlYWQgdXNpbmcKCmBgYHtyLCBtZXNzYWdlID0gRkFMU0V9CmxpYnJhcnkocnZlc3QpCmxpYnJhcnkoZHBseXIpCndlYXRoZXIgPC0gcmVhZF9odG1sKCJodHRwczovL3d3dy50aW1lYW5kZGF0ZS5jb20vd2VhdGhlci8iKQp3IDwtIGh0bWxfdGFibGUoaHRtbF9ub2Rlcyh3ZWF0aGVyLCAidGFibGUiKSlbWzFdXQoKdzEgPC0gd1tjKDEsIDQpXTsgbmFtZXModzEpIDwtIGMoImNpdHkiLCAidGVtcCIpCncyIDwtIHdbYyg1LCA4KV07IG5hbWVzKHcyKSA8LSBjKCJjaXR5IiwgInRlbXAiKQp3MyA8LSB3W2MoOSwgMTIpXTsgbmFtZXModzMpIDwtIGMoImNpdHkiLCAidGVtcCIpCnd3IDwtIHJiaW5kKHcxLCB3MiwgdzMpCnd3IDwtIGZpbHRlcih3dywgY2l0eSAhPSAiIikKaGVhZCh3dykKYGBgCgpDbGVhbmluZyB1cCBhbmQgZXh0cmFjdGluZyBgZHN0YDoKYGBge3J9Cnd3dyA8LSBtdXRhdGUod3csCiAgICAgICAgICAgICAgZHN0ID0gZ3JlcGwoIiBcXCoiLCBjaXR5KSwKICAgICAgICAgICAgICBjaXR5ID0gc3ViKCIgXFwqIiwgIiIsIGNpdHkpLAogICAgICAgICAgICAgIHRlbXAudHh0ID0gdGVtcCwgICAjIyBmb3IgY2hlY2tpbmcgb24gY29udmVyc2lvbiBmYWlsdXJlcwogICAgICAgICAgICAgIHRlbXAgPSBhcy5udW1lcmljKHN1YigiKFstK10/W1s6ZGlnaXQ6XV0rKS4qIiwgIlxcMSIsIHRlbXApKSkKYGBgCgpDaGVjayBvbiBgTkFgIHZhbHVlcyBmcm9tIGNvbnZlcnNpb246CgpgYGB7cn0KZmlsdGVyKHd3dywgaXMubmEodGVtcCkpCnd3dyA8LSBzZWxlY3Qod3d3LCAtdGVtcC50eHQpCmBgYAoKRml2ZSBoaWdoZXN0IGFuZCBsb3dlc3QgdGVtcGVyYXR1cmVzOiAKYGBge3J9CnNsaWNlX21heCh3d3csIHRlbXAsIG4gPSA1KQpgYGAKYGBge3J9CnNsaWNlX21pbih3d3csIHRlbXAsIG4gPSA1KQpgYGAKClRlbXBlcmF0dXJlcyBmb3Igbm9ydGhlcm4gYW5kIHNvdXRoZXJuIGhlbWlzcGhlcmUgKGFwcHJveGltYXRlbHkpOgoKYGBge3IgdGVtcC1kZW5zaXRpZXMsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KHd3dywgYWVzKHggPSB0ZW1wLCBmaWxsID0gZHN0KSkgKwogICAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC41KQpgYGAKYGBge3IgdGVtcC1kZW5zaXRpZXMsIGVjaG8gPSBGQUxTRX0KYGBgCgoKIyMgVHJpY2t5IENoYXJhY3RlcnMKClNvbWUgZXhhbXBsZXM6CgpgYGB7cn0KKHMgPC0gaGVhZCh3dyR0ZW1wKSkKbmNoYXIocykKc3Vic3RyKHMsIDEsIG5jaGFyKHMpIC0gMykKc3Vic3RyKHMsIDEsIG5jaGFyKHMpIC0gMikKYXMubnVtZXJpYyhzdWJzdHIocywgMSwgbmNoYXIocykgLSAyKSkKYXMubnVtZXJpYygiODIgIikKc3ViKCIgLioiLCAiIiwgcykKYGBgCgpUaGUgcHJvYmxlbSBpcyBfdHdvXyBub24tYXNjaWkgY2hhcmFjdGVycy4KClRoZSBgc3RyaV9lc2NhcGVfdW5pY29kZSgpYCBmdW5jdGlvbiBmcm9tIHRoZSBgc3RyaW5naWAgcGFja2FnZSBjYW4gbWFrZQp0aGVzZSBjaGFyYWN0ZXJzIG1vcmUgdmlzaWJsZToKCmBgYHtyfQpzdHJpbmdpOjpzdHJpX2VzY2FwZV91bmljb2RlKHMpCmBgYAoKVGhlIHRyb3VibGVzb21lIGNoYXJhY3RlcnMgYXJlOgoKKiBbTm8tYnJlYWsgc3BhY2UgVTAwQTBdKGh0dHBzOi8vd3d3LmZpbGVmb3JtYXQuaW5mby9pbmZvL3VuaWNvZGUvY2hhci8wMGEwL2luZGV4Lmh0bSkuCgoqIFtEZWdyZWUgc3ltYm9sCiAgVTAwQjBdKGh0dHBzOi8vd3d3LmZpbGVmb3JtYXQuaW5mby9pbmZvL3VuaWNvZGUvY2hhci8wMGIwL2luZGV4Lmh0bSkuCgpVc2luZyB0aGUgdW5pY29kZSBzcGVjaWZpY2F0aW9uIGZvciB0aGUgbm8tYnJlYWsgc3BhY2UgZG9lcyB3b3JrOgoKYGBge3J9CnN1YigiXHUwMGEwLioiLCAiIiwgcykKYGBgCgoKIyMgVmFyaWF0aW9ucyBpbiBSZWd1bGFyIEV4cHJlc3Npb24gRW5naW5lcwoKPCEtLSBOb3RlIG9uIHVuaWNvZGUgZGlnaXRzOgogaHR0cHM6Ly91bml4LnN0YWNrZXhjaGFuZ2UuY29tL3F1ZXN0aW9ucy80MTQyMjYvZGlmZmVyZW5jZS1iZXR3ZWVuLTAtOS1kaWdpdC1hbmQtZAotLT4KCk1hbnkgdG9vbHMgYW5kIGxhbmd1YWdlcyBzdXBwb3J0IHdvcmtpbmcgd2l0aCByZWd1bGFyIGV4cHJlc3Npb25zLgoKUiBzdXBwb3J0czoKCiogVGhlIFBPU0lYIHN0YW5kYXJkIGZvciBfZXh0ZW5kZWQgcmVndWxhciBleHByZXNzaW9uc18uIFRoaXMgaXMgdGhlCiAgZGVmYXVsdCBlbmdpbmUuCgoqIFBlcmwtY29tcGF0aWJsZSByZWd1bGFyIGV4cHJlc3Npb25zCiAgKFtQQ1JFXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9QZXJsX0NvbXBhdGlibGVfUmVndWxhcl9FeHByZXNzaW9ucykpLgogIFRoaXMgZW5naW5lIGlzIHNlbGVjdGVkIGJ5IGFkZGluZyBgcGVybCA9IFRSVUVgIGluIGNhbGxzIHRvCiAgZnVuY3Rpb25zIHVzaW5nIHJlZ3VsYXIgZXhwcmVzc2lvbnMuCgpEaWZmZXJlbnQgZW5naW5lcyBjYW4gZGlmZmVyIGluIGhvdyBjZXJ0YWluIHJlZ3VsYXIgZXhwcmVzc2lvbnMgYXJlCmludGVycHJldGVkLCBlc3BlY2lhbGx5IHdoZW4gbm9uLUFTQ0lJIGNoYXJhY3RlcnMgYXJlIGludm9sdmVkLgoKRGlmZmVyZW50IGVuZ2luZXMgYWxzbyBzb21ldGltZXMgb2ZmZXIgc2hvcnRoYW5kIG5vdGF0aW9ucywgaW4KcGFydGljdWxhciBmb3IgY2hhcmFjdGVyIGNsYXNzZXMuCgpTb21lIGV4YW1wbGVzOgoKfCBQT1NJWCBjbGFzcyB8ICBzaW1pbGFyIHRvIHwgc2hvcnRoYW5kIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCAtLS0tLS0tLS0tLSB8IC0tLS0tLS0tLS0tIHwgLS0tLS0tLS0tIHwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIHwKfGBbOmRpZ2l0Ol1gIHwgIGBbMC05XWAgICB8ICBgXGRgICAgICB8ICAgZGlnaXRzICAgICAgICAgICAgICAgICAgICAgICB8CnxgWzp1cHBlcjpdYCB8ICAgYFtBLVpdYCAgfCAgYFx1YCAgICAgfCAgIHVwcGVyIGNhc2UgbGV0dGVycyAgICAgICAgICAgfAp8YFs6bG93ZXI6XWAgfCAgYFthLXpdYCAgIHwgIGBcbGAgICAgIHwgIGxvd2VyLWNhc2UgbGV0dGVycyAgICAgICAgICAgIHwKfGBbOmFscGhhOl1gIHwgIGBbQS1aYS16XWB8ICAgICAgICAgICB8ICB1cHBlci0gYW5kIGxvd2VyLWNhc2UgbGV0dGVycyB8CnxgWzpzcGFjZTpdYCB8ICBgWyBcdFxuXWAgfCAgYFxzYCAgICAgfCAgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzICAgICAgICAgfAoKVGhlIHNob3J0aGFuZCB2ZXJzaW9ucyBuZWVkIHRvIGhhdmUgdGhlaXIgYFxgIGVzY2FwZWQgd2hlbiB1c2VkIGluIGFuIFIKc3RyaW5nOgoKYGBge3J9CmludHBhdCA8LSAiLipcXHMoWy0rXT9cXGQrKS4qIgpzdWIoaW50cGF0LCAiXFwxIiwgYygiVGVtcDogIDMyRiIsICJUZW1wOiAtMTFGIikpCmBgYAoKCiMjIFJhdyBTdHJpbmdzCgpfUmF3IHN0cmluZ3NfIGNhbiBtYWtlIHdyaXRpbmcgcmVndWxhciBleHByZXNzaW9ucyBhIGxpdHRsZSBlYXNpZXIuCgpJbiBSIGEgcmF3IHN0cmluZyBpcyBzcGVjaWZpZWQgYXMgYHIiKC4uLikiYCBvciBgciJ7Li4ufSJgLgoKVGhlIGAuLi5gIGNoYXJhY3RlcnMgY2FuIGJlIGFueSBjaGFyYWN0ZXJzIGFuZCBhcmUgdGFrZW4gbGl0ZXJhbGx5LAp3aXRob3V0IHNwZWNpYWwgaW50ZXJwcmV0YXRpb24gdGhhdCBtaWdodCByZXF1aXJlIGVzY2FwaW5nLgoKRm9yIHRoZSBpbnRlZ2VyIHBhdHRlcm46CgpgYGB7cn0KaW50cGF0IDwtICIuKlxccyhbLStdP1xcZCspLioiCmludHBhdF9yYXcgPC0gciJ7LipccyhbLStdP1xkKykuKn0iCmludHBhdCA9PSBpbnRwYXRfcmF3CmBgYAoKUHl0aG9uLCBDKyssIGFuZCBvdGhlciBsYW5ndWFnZXMgcHJvdmlkZSBzaW1pbGFyIGZhY2lsaXRlcy4KClIncyByYXcgc3RyaW5nIHN5bnRheCBpdCBtb2RlbGVkIGFmdGVyIHRoZSBvbmUgdXNlZCBpbiBDKysuCgoKIyMgUmVndWxhciBFeHByZXNzaW9uIE1vZGlmaWVycwoKUmVndWxhciBleHByZXNzaW9uIG1vZGlmaWVycyBhcmUgb2YgdGhlIGZvcm0gYCg/Li4uKWAuCgpTb21lIHJlZ3VsYXIgZXhwcmVzc2lvbnMgaW1wbGVtZW50YXRpb25zIGRlZmF1bHQgdG8gY2FzZSBzZW5zaXRpdmUsCm90aGVycyB0byBjYXNlIGluc2Vuc2l0aXZlLiBZb3UgY2FuIHNwZWNpZnkgd2hhdCB5b3Ugd2FudCB3aXRoIGEKX21vZGlmaWVyXzoKCi0gYCg/aSkuLi5gIHNwZWNpZmllcyBhIGNhc2UtaW5zZW5zaXRpdmUgbWF0Y2guCi0gYCg/LWkpLi4uYCBzcGVjaWZpZXMgYSBjYXNlLXNlbnNpdGl2ZSBtYXRjaC4KCkFub3RoZXIgdXNlZnVsIG1vZGlmaWVyIGlzIHRoZSBfbm9uLWNhcHR1cmluZyBncm91cF8gYCg/Oi4uLilgLiAgVGhpcwpncm91cHMgZWxlbWVudHMgd2l0aG91dCBzYXZpbmcgdGhlbSBmb3IgbGF0ZXIgdXNlLgoKVGhlIG1vZGlmaWVyIGAoP3gpYCBzcGVjaWZpZXMgX2V4dGVuZGVkIGZyZWUtc3BhY2UgbW9kZV86IG1vc3QKd2hpdGVzcGFjZSBpcyBpZ25vcmVkIGFuZCB0ZXh0IGZvbGxvd2luZyBhIGAjYCBpcyB0cmVhdGVkIGFzIGEKY29tbWVudC4gKFRoaXMgcmVxdWlyZXMgYHBlcmwgPSBUUlVFYCBpbiBSLikKCmBgYHtyfQppbnRwYXRfcmF3MiA8LQogICAgciJ7KD94KQogICAgICAgLipccyAgICMgemVybyBvciBtb3JlIGNoYXJhY3RlcnMgZm9sbG93ZWQgYnkgYSBzcGFjZQogICAgICAgKCAgICAgICMgc3RhcnQgYSBjYXB0dXJpbmcgZ3JvdXAKICAgICAgICBbLStdPyAjIG9wdGlvbmFsIHNpZ24KICAgICAgICBcZCsgICAjIG9uZSBvciBtb3JlIGRpZ2l0cwogICAgICAgKSAgICAgICMgZW5kIHRoZSBjYXB0dXJpbmcgZ3JvdXAKICAgICAgIC4qICAgICAjIHplcm8gb3IgbW9yZSBjaGFyYWN0ZXJzCn0iCnN1YihpbnRwYXRfcmF3MiwgIlxcMSIsIGMoIlRlbXA6ICAzMkYiLCAiVGVtcDogLTExRiIpLCBwZXJsID0gVFJVRSkKYGBgCgoKIyMgVXNpbmcgYW4gTExNCgpVc2luZyBhIExhcmdlIExhbmd1YWdlIE1vZGVsIChMTE0pIGNhbiBoZWxwIHdpdGggY3JlYXRpbmcgYW5kCnVuZGVyc3RhbmRpbmcgcmVndWxhciBleHByZXNzaW9ucy4KClNvbWUgcG9zc2libGUgcHJvbXB0czoKCi0gcmVnZXggdG8gbWF0Y2ggYSBudW1iZXIKCi0gcmVnZXggdG8gbWF0Y2ggYSBudW1iZXIgd2l0aCBhbiBvcHRpb25hbCBzaWduCgotIHJlZ2V4IHRvIG1hdGNoIGEgbnVtYmVyIHdpdGggYW4gb3B0aW9uYWwgc2lnbiBhbmQgYW4gb3B0aW9uYWwgZnJhY3Rpb25hbCBwYXJ0CgotIGV4cGxhaW4gdGhlIHJlZ2V4IGAoWy0rXT9cZCspLipgCgpBcyB3aXRoIGFueSB1c2Ugb2YgTExNcyBpbiBjb2RpbmcsIHlvdSBuZWVkIHRvIGtub3cgZW5vdWdoIHRvCnJlY29nbml6ZSB3aGV0aGVyIHRoZSBhbnN3ZXIgcHJvdmlkZWQgbWFrZXMgc2Vuc2Ugb3Igbm90LgoKCiMjIEEgTm90ZSBvbiBTb3J0aW5nCgpOb24tQVNDSUkgY2hhcmFjdGVycyBjYW4gY3JlYXRlIGlzc3VlcyBmb3Igc29ydGluZyBzdHJpbmdzLCBidXQgZXZlbgpBU0NJSSBjaGFyYWN0ZXIgc29ydCBvcmRlciBpcyBub3QgdGhlIHNhbWUgaW4gYWxsCltsb2NhbGVzXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9MaXN0X29mX0lTT182MzktMV9jb2RlcykuCgpUaGUgYExFVFRFUlNgIGRhdGEgc2V0IGNvbnRhaW5zIHRoZSB1cHBlci1jYXNlIGxldHRlcnMgaW4gYWxwaGFiZXRpY2FsCm9yZGVyIGZvciB0aGUgRW5nbGlzaCBzb3J0aW5nIGNvbnZlbnRpb24gYW5kIG1vc3Qgb3RoZXIgbG9jYWxlczoKCmBgYHtyfQpMRVRURVJTCmBgYAoKQnV0IGluIEVzdG9uaWFuLCBMYXR2aWFuLCBhbmQgTGl0aHVhbmlhbjoKCmBgYHtyfQpzdHJpbmdyOjpzdHJfc29ydChMRVRURVJTLCBsb2NhbGUgPSAiZXN0IikKYGBgCgpPcmRlcmluZyBvZiBsb3dlciBjYXNlIGFuZCB1cHBlciBjYXNlIGxldHRlcnMgaW4gRW5nbGlzaCBhbmQgbW9zdCBvdGhlcgpsb2NhbGVzOgoKYGBge1J9CnN0cmluZ3I6OnN0cl9zb3J0KGMoIkEiLCAiYSIpLCBsb2NhbGUgPSAiZW5nIikKYGBgCgpCdXQgaW4gRGFuaXNoLCBhbmQgYWxzbyBNYWx0ZXNlOgoKYGBge1J9CnN0cmluZ3I6OnN0cl9zb3J0KGMoIkEiLCAiYSIpLCBsb2NhbGUgPSAiZGFuIikKYGBgCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBldmFsID0gRkFMU0V9CiMjIHJlYWQgYSB0YWJsZSBmcm9tIFdpa2lwZWRpYSB3aXRoIHRoZSBsb2NhbGUgYWJicmV2aWF0aW9ucwp1cmwgPC0gImh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xpc3Rfb2ZfSVNPXzYzOS0xX2NvZGVzIgpsaWJyYXJ5KHJ2ZXN0KQpsaWJyYXJ5KGRwbHlyKQp3IDwtIHJlYWRfaHRtbCh1cmwpCgojIyBkaWZmZXJlbnQgbGV0dGVyIHNvcnQgb3JkZXIKdGJsIDwtIGh0bWxfdGFibGUoaHRtbF9ub2Rlcyh3LCAidGFibGUiKSwgZmlsbCA9IFRSVUUpCmx0YmwgPC0gdGJsW1syXV0KbGNvZGVzIDwtIGx0YmxbWzZdXQp2IDwtIHNhcHBseShsY29kZXMsCiAgICAgICAgICAgIGZ1bmN0aW9uKGxvYykKICAgICAgICAgICAgICAgIGlkZW50aWNhbChzdHJpbmdyOjpzdHJfc29ydChMRVRURVJTLCBsb2NhbGUgPSBsb2MpLAogICAgICAgICAgICAgICAgICAgICAgICAgIExFVFRFUlMpKQpsdGJsWyF2LCBjKCJMYW5ndWFnZSBmYW1pbHkiLCAiSVNPIGxhbmd1YWdlIG5hbWUiLCAiNjM5LTIvVCIpXQoKIyMgZGlmZmVyZW50IHVwcGVyL2xvd2VyIGNhc2Ugc29ydCBvcmRlcgpzcyA8LSBjKCJhIiwgIkEiKQp2diA8LSBzYXBwbHkobGNvZGVzLAogICAgICAgICAgICAgZnVuY3Rpb24obG9jKQogICAgICAgICAgICAgICAgaWRlbnRpY2FsKHN0cmluZ3I6OnN0cl9zb3J0KHNzLCBsb2NhbGUgPSBsb2MpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHNzKSkKbHRibFshdnYsIGMoIkxhbmd1YWdlIGZhbWlseSIsICJJU08gbGFuZ3VhZ2UgbmFtZSIsICI2MzktMi9UIildCgpgYGAKCgojIyBFbmNvZGluZyBJc3N1ZXMKCkZpbGVzIGNvbnRhaW4gYSBzZXF1ZW5jZSBvZiA4LWJpdCBpbnRlZ2Vycywgb3IgX2J5dGVzXy4KClRoZXNlIGFyZSB0aGUgaW50ZWdlcnMgZnJvbSAwIHRocm91Z2ggMjU1LgoKRm9yIHRleHQgZmlsZXMsIHRoZXNlIGJ5dGVzIGFyZSBpbnRlcnByZXRlZCBhcyByZXByZXNlbnRpbmcgY2hhcmFjdGVycy4KClRoZSBtYXBwaW5nIGZyb20gYnl0ZXMgdG8gY2hhcmFjdGVycyBpcyBjYWxsZWQgYW4gX2VuY29kaW5nXy4KClRoZSBlbmNvZGluZyBmb3IgdGhlIGNoYXJhY3RlcnMgdXNlZCBpbiBBbWVyaWNhbiBFbmdsaXNoIGlzIEFTQ0lJOgp0aGUgX0FtZXJpY2FuIFN0YW5kYXJkIENvZGUgZm9yIEluZm9ybWF0aW9uIEludGVyY2hhbmdlXy4KClRoZSBbQVNDSUkgZW5jb2RpbmddKGh0dHBzOi8vYXNjaWktdGFibGVzLmNvbS8pIHVzZXMgb25seSB0aGUKaW50ZWdlcnMgMCB0aHJvdWdoIDEyNy4KClRoZSBBU0NJSSBlbmNvZGluZyBpcyBhZGVxdWF0ZSBmb3IgQW1lcmljYW4gdXNlczsgZXZlbiBVSyB0ZXh0IGZpbGVzCm5lZWQgbW9yZTogdGhlIHBvdW5kIHNpZ24gwqMuCgpFbmNvZGluZ3MgdGhhdCB1c2UgaW50ZWdlcnMgMTI4IHRocm91Z2ggMjU1OgoKKiBMYXRpbjEgKGFrYSBJU08tODg1OS0xKSBmb3Igd2VzdGVybiBFdXJvcGVhbiBsYW5ndWFnZXMgKGluY2x1ZGVzIMKjKTsKCiogTGF0aW4yIChha2EgSVNPLTg4NTktMikgZm9yIGVhc3Rlcm4gRXVyb3BlYW4gbGFuZ3VhZ2VzLgoKTWFueSBlbmNvZGluZ3MgYXJlIGF2YWlsYWJsZSB0byBzdXBwb3J0IG90aGVyIGFscGhhYmV0cyBhbmQgY2hhcmFjdGVyIHNldHMuCgpGb3J0dW5hdGVseSBtb3N0IHN5c3RlbXMgbm93IHVzZSBbVW5pY29kZV0oaHR0cHM6Ly9ob21lLnVuaWNvZGUub3JnLykKd2l0aCB0aGUgW1VURi04IGVuY29kaW5nXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9VVEYtOCkgZm9yCnJlcHJlc2VudGluZyBub24tQVNDSUkgY2hhcmFjdGVycy4KCklmIHlvdSBuZWVkIHRvIHJlYWQgYSBmaWxlIHdpdGggbm9uLUFTQ0lJIGNoYXJhY3RlcnMgdXNpbmcgYHJlYWQuY3N2KClgCm9yIHNpbWlsYXIgYmFzZSBmdW5jdGlvbnMgYSBnb29kIHBsYWNlIHRvIHN0YXJ0IGlzIHRvIHNwZWNpZnkKYGVuY29kaW5nID0gIlVURi04ImAuCgpUaGUgZnVuY3Rpb25zIGluIHRoZSBgcmVhZHJgIHBhY2thZ2UgZGVmYXVsdCB0byBhc3N1bWluZyB0aGUgZW5jb2RpbmcKaXMgVVRGLTguCgpHZXR0aW5nIHRoZSBlbmNvZGluZyB3cm9uZyBjYW4gcmVzdWx0IGluIGEgZmV3IG1lc3NlZCB1cCBjaGFyYWN0ZXJzCm9yIGluIGFuIGVudGlyZSBzdHJpbmcgYmVpbmcgbWVzc2VkIHVwOgoKYGBge3IgaW5jbHVkZSA9IEZBTFNFfQp4MSA8LSAiRWwgTmlceGYxbyB3YXMgcGFydGljdWxhcmx5IGJhZCB0aGlzIHllYXIiCngyIDwtICJceDgyXHhiMVx4ODJceGYxXHg4Mlx4YzlceDgyXHhiZlx4ODJceGNkIgpgYGAKClJlYWRpbmcgZmlsZXMgd2l0aG91dCBzcGVjaWZ5aW5nIHRoZSBjb3JyZWN0IGVuY29kaW5nCm1pZ2h0IHByb2R1Y2Ugc3RyaW5ncyBsaWtlCgpgYGB7cn0KeDEKeDIKYGBgCgpJZiB0aGUgY29ycmVjdCBlbmNvZGluZyBpcyBrbm93biwgdGhlbiB0aGVzZSBjYW4gYmUgZml4ZWQgYWZ0ZXIgdGhlCmZhY3Qgd2l0aCBgaWNvbnYoKWA6CgpgYGB7cn0KaWNvbnYoeDEsICJMYXRpbjEiLCAiVVRGLTgiKQppY29udih4MiwgIlNoaWZ0LUpJUyIsICJVVEYtOCIpCmBgYAoKUmUtcmVhZGluZyB0aGUgZmlsZSB3aXRoIHRoZSBwcm9wZXIgZW5jb2Rpbmcgc3BlY2lmaWVkIG1heSBiZSBhIGJldHRlcgpvcHRpb24uCgpUaGUgYHJlYWRyYCBmdW5jdGlvbiBgZ3Vlc3NfZW5jb2RpbmcoKWAgbWF5IGhlbHAgaWRlbnRpZnkgdGhlIGNvcnJlY3QKZW5jb2RpbmcgaWYgaXQgaXMgbm90IHNwZWNpZmllZCBpbiB0aGUgZGF0YSBkb2N1bWVudGF0aW9uLgoKSGFuZGxpbmcgZW5jb2RpbmcgaXNzdWVzIGluIFIgdXNlZCB0byBiZSBtb3JlIGNvbXBsaWNhdGVkIG9uIFdpbmRvd3MsIGJ1dAp0aGlzIGhhcyBpbXByb3ZlZCB3aXRoIHJlY2VudCByZWxlYXNlcyBvZiBSLgoKCiMjIEZvbnRzCgpPbmNlIHlvdSBoYXZlIHRoZSByaWdodCBlbmNvZGluZyBmb3IgY2hhcmFjdGVyIGRhdGEsIHlvdSBhbHNvIG5lZWQgYSBzZXQKb2YgZm9udHMgc28geW91ciBkYXRhIHNob3dzIHVwIHByb3Blcmx5IGluIHRoZSBjb25zb2xlLCB0aGUgZWRpdG9yLCBhbmQKZ3JhcGhzLgoKRm9udHMgbWF5IGFscmVhZHkgYmUgaW5zdGFsbGVkLgoKYGBge3J9CmMoIlxVezFmNjAwfSIsICJcdTMwNDEiLCAiXHU0YTAwIikKYGBgCgpJZiBnbHlwaHMgYXJlIG5vdCBhdmFpbGFibCwgdGhlIGNoYXJhY3RlcnMgbWF5IGJlIHNob3duIGluIHZhcmlvdXMgd2F5cy4KCmBgYHtyfQpjKCJcVXsxMDViOH0iLCAiXHUzMDQwIikKYGBgCgpIb3cgdG8gaW5zdGFsbCBmb250cyB3aWxsIGRlcGVuZCBvbiB5b3VyIHBsYXRmb3JtLgoKQSBwbG90IHdpdGggZW1vamlzOgpgYGB7ciwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CmRhdGEuZnJhbWUoeCA9IDE6MTAsCiAgICAgICAgICAgeSA9IHJ1bmlmKDEwKSwKICAgICAgICAgICB6ID0gc2FwcGx5KDB4MWY2MDAgKyAwOjksCiAgICAgICAgICAgICAgICAgICAgICBpbnRUb1V0ZjgpKSB8PgpnZ3Bsb3QoYWVzKHgsIHksIGxhYmVsID0geikpICsKICAgIGdlb21fdGV4dCgpCmBgYAoKCiMjIEEgTm90ZSBvbiBMaW5lIEVuZGluZ3MKCk9uIExpbnV4IGFuZCBjdXJyZW50IG1hY09TIGxpbmVzIG9mIHRleHQgaW4gZmlsZXMgYXJlIHRlcm1pbmF0ZWQgYnkgYQpfbGluZSBmZWVkXyAoTEYpIGNoYXJhY3RlciBgXG5gIG9yIGBceDBhYC4KCk9uIFdpbmRvd3MsIGFuZCBpbiBIVE1MLCBsaW5lcyBhcmUgdGVybWluYXRlZCBieSBhIF9jYXJyaWFnZSByZXR1cm5fCihDUiwgYFxyYCwgYHgwZGApIGZvbGxvd2VkIGJ5IGEgbGluZWZlZWQgKENSTEYpLgoKT24gTGludXggYW5kIG1hY09TIHRoZXJlIGlzIG5vIGRpZmZlcmVuY2UgaW4gcmVhZGluZyBvciB3cml0aW5nIHRvIGEKZmlsZSBpbiBfdGV4dCBtb2RlXyBvciBfYmluYXJ5IG1vZGVfLgoKT24gV2luZG93cywgcmVhZGluZyBpbiB0ZXh0IG1vZGUgdHJhbnNsYXRlcyBmcm9tIENSTEYgbGluZSBlbmRpbmdzCnRvIExGLCBhbmQgd3JpdGluZyB0cmFuc2xhdGVzIExGIGxpbmUgZW5kaW5ncyB0byBDUkxGLgoKTWFueSBmaWxlIHRyYW5zZmVyIG9wZXJhdGlvbnMgd2lsbCBkbyB0aGlzIGNvbnZlcnNpb24uCgpHaXQgd2lsbCBjaGVjayBvdXQgdGV4dCBmaWxlcyB3aXRoIHRoZSBhcHByb3ByaWF0ZSBsaW5lIGVuZGluZ3MgZm9yCnRoZSBwbGF0Zm9ybS4KCk1vc3QgaGlnaGVyLWxldmVsIHRvb2xzIG9uIExpbnV4IHdpbGwgd29yayB3aXRoIENSTEYgbGluZSBlbmRpbmdzLCBhbmQKb24gV2luZG93cyB3aWxsIHdvcmsgZm9yIExGIGxpbmUgZW5kaW5ncy4KCk9jY2FzaW9uYWxseSB5b3UgbWF5IHNlZSBzb21lIGlzc3VlcywgYW5kIG5lZWQgdG8gZml4IHRoZSBsaW5lIGVuZGluZ3MKeW91cnNlbGYuCgoKIyMgR2V0dGluZyB0aGUgQ3VycmVudCBUZW1wZXJhdHVyZSAKClRoZSB0b29scyBkZXNjcmliZWQgaGVyZSBjb21lIGluIGhhbmR5IHdoZW4gc2NyYXBpbmcgZGF0YSBmcm9tIHRoZSB3ZWIuCgpUaGlzIGNvZGUgZ2V0cyB0aGUgW2N1cnJlbnQgdGVtcGVyYXR1cmUgaW4gSW93YQpDaXR5XShodHRwOi8vZm9yZWNhc3Qud2VhdGhlci5nb3YvemlwY2l0eS5waHA/aW5wdXRzdHJpbmc9SW93YStDaXR5LElBKQpmcm9tIHRoZSBOYXRpb25hbCBXZWF0aGVyIFNlcnZpY2U6CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KHhtbDIpCnVybCA8LSAiaHR0cDovL2ZvcmVjYXN0LndlYXRoZXIuZ292L3ppcGNpdHkucGhwP2lucHV0c3RyaW5nPUlvd2ErQ2l0eSxJQSIKcGFnZSA8LSByZWFkX2h0bWwodXJsKQp4cGF0aCA8LSAiLy9wW0BjbGFzcz1cIm15Zm9yZWNhc3QtY3VycmVudC1scmdcIl0iCnRlbXBOb2RlIDwtIHhtbF9maW5kX2ZpcnN0KHBhZ2UsIHhwYXRoKQpub2RlVGV4dCA8LSB4bWxfdGV4dCh0ZW1wTm9kZSkKYXMubnVtZXJpYyhzdWIoIihbLStdP1xcZCspLioiLCAiXFwxIiwgbm9kZVRleHQpKQpgYGAKCkFuIGV4YW1wbGUgb2YgY3JlYXRpbmcgYSBjdXJyZW50IHRlbXBlcmF0dXJlIG1hcCBpcyBkZXNjcmliZWQKW2hlcmVdKGByIFdMTksoIndlYXRoZXIuaHRtbCIpYCkuCgoKIyMgUmVhZGluZwoKQ2hhcHRlcnMgW19EYXRhIEltcG9ydF9dKGh0dHBzOi8vcjRkcy5oYWRsZXkubnovZGF0YS1pbXBvcnQuaHRtbCkgYW5kCltfU3RyaW5nc19dKGh0dHBzOi8vcjRkcy5oYWRsZXkubnovc3RyaW5ncy5odG1sKSBpbiBbX1IgZm9yIERhdGEKU2NpZW5jZV9dKGh0dHBzOi8vcjRkcy5oYWRsZXkubnovKS4KCkNoYXB0ZXIgW19TdHJpbmcKcHJvY2Vzc2luZ19dKGh0dHBzOi8vcmFmYWxhYi5kZmNpLmhhcnZhcmQuZWR1L2RzYm9vay1wYXJ0LTEvd3JhbmdsaW5nL3N0cmluZy1wcm9jZXNzaW5nLmh0bWwpCmluIFtfSW50cm9kdWN0aW9uIHRvIERhdGEgU2NpZW5jZTogRGF0YSBBbmFseXNpcyBhbmQgUHJlZGljdGlvbgpBbGdvcml0aG1zIHdpdGggUl9dKGh0dHBzOi8vcmFmYWxhYi5kZmNpLmhhcnZhcmQuZWR1L2RzYm9vay1wYXJ0LTEvKS4KCgojIyBFeGVyY2lzZXMKCjwhLS0KZnJvbSBodHRwczovL3JhZmFsYWIuZ2l0aHViLmlvL2RzYm9vay9zdHJpbmctcHJvY2Vzc2luZy5odG1sI2V4ZXJjaXNlcy00MQotLT4KMS4gQ29tcGxldGUgYWxsIGxlc3NvbnMgYW5kIGV4ZXJjaXNlcyBpbiB0aGUgaHR0cHM6Ly9yZWdleG9uZS5jb20vCiAgIG9ubGluZSBpbnRlcmFjdGl2ZSB0dXRvcmlhbC4KCjIuIENvbnNpZGVyIHRoZSBjb2RlCgogICAgYGBge3IsIGV2YWwgPSBGQUxTRX0KICAgIGxpYnJhcnkodGlkeXZlcnNlKQogICAgZmlsdGVyKG1wZywgZ3JlcGwoLS0tLCBtb2RlbCkpCiAgICBgYGAKCiAgICBGb3Igd2hpY2ggb2YgdGhlIGZvbGxvd2luZyByZWd1bGFyIGV4cHJlc3Npb25zIGluIHBsYWNlIG9mIGAtLS1gIHdpbGwKICAgIHRoaXMgY29kZSByZXR1cm4gdGhlIHN1YnNldCBvZiByb3dzIGZvciBhbGwgbW9kZWxzIHRoYXQgY29udGFpbiBlaXRoZXIKICAgIGA0d2RgIG9yIGBhd2RgIGluIHRoZWlyIG1vZGVsIG5hbWVzPwoKICAgIGEuICJbYTRdd2QgIgogICAgYi4gIlthNF13ZCIKICAgIGMuICI0YXdkIgogICAgZC4gIltbNGFdXXdkIgo=