Chapter 15 Functions
Functions allow you to automate common tasks in a more powerful and general way than copying and pasting. Writing a function has 3 big advantages:
You can give a function an evocative name that makes your code easier to understand
As requirements change, you only need to update code in one plcae, instead of many
You eliminate the chance of making incidental mistakes when you copy and paste
Pre requisites
library(tidyverse)
df <- tibble(
a = rnorm(10),
b = rnorm(10),
c = rnorm(10),
d = rnorm(10)
)
df
rng1 <- range(df$a, na.rm = TRUE)
rng1
[1] -2.392289 2.051636
None.
When should you write a function?
Answer: when you copy and paste a block of code more than twice
df$a <- (df$a - min(df$a, na.rm = TRUE)) / (max(df$a, na.rm = TRUE) - min(df$a, na.rm = TRUE))
df$b <- (df$b - min(df$b, na.rm = TRUE)) / (max(df$b, na.rm = TRUE) - min(df$b, na.rm = TRUE))
df$c <- (df$c - min(df$c, na.rm = TRUE)) / (max(df$c, na.rm = TRUE) - min(df$c, na.rm = TRUE))
df$d <- (df$d - min(df$a, na.rm = TRUE)) / (max(df$d, na.rm = TRUE) - min(df$d, na.rm = TRUE))
df
To rewrite this as a function, figure out what the input is -> df$a The three steps: 1. Pick a name for the function. 2. List the inputs or arguments to the function 3. place the code you have developed in the body of the function, a { block that immediately follows function(…)
# range gives the lowest and highest values in a range of numbers
rescale01 <- function(x) {
rng <- range(x, na.rm = TRUE)
(x - rng[1]) / (rng[2] - rng[1])
}
rescale01(c(0,5,10))
[1] 0.0 0.5 1.0
# test with different inputs
rescale01(c(1,2,3,NA, 5))
[1] 0.00 0.25 0.50 NA 1.00
We can now simplify the original code with this function
library(tibble)
df <- tibble(
a = rnorm(10),
b = rnorm(10),
c = rnorm(10),
d = rnorm(10)
)
df$a <- rescale01(df$a)
df$b <- rescale01(df$b)
df$c <- rescale01(df$c)
df$d <- rescale01(df$d)
df$a
[1] 0.4185576 0.9440421 0.8372570 0.0000000 0.9109720 0.5342870 0.4982837
[8] 0.3155818 1.0000000 0.7430001
df$b
[1] 0.5817967 0.5698511 0.0000000 0.9808045 1.0000000 0.7010986 0.6378013
[8] 0.5781051 0.2233968 0.2868422
df$c
[1] 0.27002818 1.00000000 0.84519963 0.54433445 0.27935139 0.00000000 0.78181957
[8] 0.65830083 0.69281630 0.07192731
df$d
[1] 0.3093271 0.8444363 0.0000000 0.9475431 0.6310155 1.0000000 0.8329480
[8] 0.4633139 0.9300175 0.4341780
Now, if we discovered that we need to account for infinite values, we only need to fix the function
rescale01 <- function(x) {
rng <- range(x, na.rm = TRUE, finite = TRUE)
(x - rng[1]) / (rng[2] - rng[1])
}
df <- tibble(
a = rnorm(10),
b = rnorm(10),
c = rnorm(10),
d = rnorm(10)
)
df$a <- rescale01(df$a)
df$b <- rescale01(df$b)
df$c <- rescale01(df$c)
df$d <- rescale01(df$d)
df$a
[1] 0.00000000 0.23644565 1.00000000 0.08290783 0.37141227 0.30269086 0.24381466
[8] 0.99788512 0.53050533 0.48883139
df$b
[1] 0.23924610 0.00000000 0.75608943 0.41153726 0.15258224 0.29927755 0.23271249
[8] 1.00000000 0.25795056 0.09907989
df$c
[1] 0.18814529 0.41358598 0.68311228 0.88936122 0.26587056 0.64924870 0.31643881
[8] 0.00000000 1.00000000 0.04308001
df$d
[1] 0.9316141 1.0000000 0.4976616 0.8795419 0.6274105 0.0000000 0.3567989
[8] 0.8379030 0.5763909 0.4061070
Exercie
- Why is TRUE not a parameter to rescale01()? What would happen if x contained a single missing value, and na.rm was FALSE?
By a single missing value, this means that x has at least one NA value. If there were any NA values, and na.rm = FALSE, then the function would return NA. I can confirm this by testing a function that allows for na.rm as an argument
rescale01_alt <- function(x, finite = TRUE) {
rng <- range(x, na.rm = finite, finite = finite)
(x - rng[1]) / (rng[2] - rng[1])
}
rescale01_alt(c(NA, 1:5), finite = FALSE)
[1] NA NA NA NA NA NA
In the second variant of rescale01(), infinite values are left unchanged. Rewrite rescale01() so that -Inf is mapped to 0, and Inf is mapped to 1.
rescale01 <- function(x) {
rng <- range(x, na.rm = TRUE, finite = TRUE)
y <- (x - rng[1]) / (rng[2] - rng[1])
y[y == -Inf] <- 0
y[y == Inf] <- 1
y
}
rescale01(c(Inf, -Inf, 0:5, NA))
[1] 1.0 0.0 0.0 0.2 0.4 0.6 0.8 1.0 NA
Practice turning the following code snippets into functions. Think about what each function does. What would you call it? How many arguments does it need? Can you rewrite it to be more expressive or less duplicative?
mean(is.na(x))
x / sum(x, na.rm = TRUE)
sd(x, na.rm = TRUE) / mean(x, na.rm = TRUE)
This function standardizes a function to its weight. If all elements of x are non-negative, this will ensure the vector sums to 1.
weights <- function(x) {
x / sum(x, na.rm = TRUE)
}
y <- weights(0:5)
y
[1] 0.00000000 0.06666667 0.13333333 0.20000000 0.26666667 0.33333333
sum(y)
[1] 1
Write both_na(), a function that takes two vectors of the same length and returns the number of positions that have an NA in both vectors.
both_na <- function(x, y) {
sum(is.na(x) & is.na(y))
}
both_na(c(NA, NA, 1, 2),
c(NA, 1, NA, 2))
[1] 1
both_na(c(NA, NA, 1, 2, NA, NA, 1),
c(NA, 1, NA, 2, NA, NA, 1))
[1] 3
What do the following functions do? Why are they useful even though they are so short?
is_directory <- function(x) file.info(x)$isdir
Error: unexpected symbol in:
"
is_directory"
The function is_directory checks whether the path in x is a directory. The function is_readable checks whether the path in x is readable, meaning that the file exists and the user has permission to open it. These functions are useful even though they are short because their names make it much clearer what the code is doing.
Functions are for humans and computers
Generally, function names should be verbs, and arguments should be nouns. There are some exceptions: nouns are OKY if the function computes a very well known noun (ie. eman()) is better than compute_mean().
If the function uses multiple words, use the snake_case, where each lowercase word is separate by an underscore. Also avoid the most common names from base R to avoid confusion.
Use comments, lines stating with # to explain the “why” of your code. You generally should avoid comments that explain the “what” or the “how”.
Use long lines of - or = to make it easy to spot the breaks:
# load data -----------------
# Plot data =================
Exercise
f1 <- function(string, prefix) {
substr(string, 1, nchar(prefix)) == prefix
}
f2 <- function(x) {
if (length(x) <= 1) return(NULL)
x[-length(x)]
}
f3 <- function(x, y) {
rep(y, length.out = length(x))
}
f1 returns whether a function has a common prefix. The function f2 drops the last element. A better name for f2 is drop_last(). This is a harder one to name. I would say something like recycle (R’s name for this behavior), or epxand.
Compare and contrast rnorm() and MASS::mvrnorm(). How could you make them more consistent? rnorm samples from the univariate normal distribution, while MASS::mvrnorm samples from the multivariate normal distribution. The main arguments in rnorm are n, mean, sd. The main arguments is MASS::mvrnorm are n, mu, Sigma. To be consistent they should have the same names. However, this is difficult. In general, it is better to be consistent with more widely used functions, e.g. rmvnorm should follow the conventions of rnorm. However, while mean is correct in the multivariate case, sd does not make sense in the multivariate case. Both functions an internally consistent though; it would be bad to have mu and sd or mean and Sigma.
Make a case for why norm_r(), norm_d() etc would be better than rnorm(), dnorm(). Make a case for the opposite.
If named norm_r and norm_d, it groups the family of functions related to the normal distribution. If named rnorm, and dnorm, functions related to are grouped into families by the action they perform. r* functions always sample from distributions: rnorm, rbinom, runif, rexp. d* functions calculate the probability density or mass of a distribution: dnorm, dbinom, dunif, dexp.
Conditional Execution
An if statement allows you to conditionally execute code. It looks like this:
if (condition) {
# code executed when condition is true
} else {
# code executed when condition is false
}
To get help on if, enclose it with backticks and ?
? ‘if’
Remember that the function returns the last value that it computed:
has_name <- function(x) {
nms <- names(x)
if (is.null(nms)) {
rep(FALSE, length(x))
} else {
!is.na(nms) & nms !=""
}
}
has_name(c("wilson", "","robert"))
[1] FALSE FALSE FALSE
Conditions
The condition must evaluate to either TRUE or FALSE. If it’s a vector, you’ll get a warning message; if it’s an NA, you’ll get an error. Watch out for these messages in your own code.
You can use || (or) or && (and) to combine mulitple logical expressions. These operators are “short-circuiting” as soon as || sees the first TRUE it returns TRUE without computing anything else. As soon as %% sees the first FALSE it returns FALSE.
You should never use | or % in an if statement. These are vectorized operations that apply to mulitple values (that is why you use them in filter()). If you do have a logical vector, you can use any() or all() to collapse it to a single value
Be careful when testing for equality. == is vectorized, which means that it’s easy to get more than one output. Either check the length is already 1, collapse with all() or any() , or use the non vectorized identical().
Identical() is very strict. It always returns either a single TRUE or a single FALSE. It doesnt coerce types. This means that you need to be careful when comapring integers and doubles.
Also be wary of floating point numbers. Use dplyr::near() for comparisons. And remember x == NA doesnt do anything useful.
Multiple Conditions
You can chain multiple if statements together:
if (this) {
# do that
} else if (that) {
# do something else
} else {
# do this
}
If you end up with a large series of if statements, better use switch() function. It allows you to eavluate selected code based on position or name.
switch_demo <- function(x,y,op) {
switch(op,
plus = x + y,
minus = x - y,
times = x * y,
divide = x / y,
sqr = x ^ y,
stop("unkown op!")
)
}
switch_demo(4,2,"sqr")
[1] 16
Code Style
Both if and function should (almost) alwasy be followed by squiggly brackets({}) and the contents should be indented by two spaces. This makes it easier to see the hierarcy in your code by skimming the lefthand margin.
An opening curly brace should never go on itw own line and should always be followed by a new line. A closing curly brace should always go on its own line, unless it’s followed by else. Always indent the code inside curly braces ,/p>
Exercises
What’s the difference between if and ifelse()? Carefully read the help and construct three examples that illustrate the key differences. The keyword if tests a single condition, while ifelse tests each element.
Write a greeting function that says “good morning”, “good afternoon”, or “good evening”, depending on the time of day. (Hint: use a time argument that defaults to lubridate::now(). That will make it easier to test your function.)
library(lubridate)
greet <- function(time = lubridate::now()) {
hr <- hour(time)
# I don't know what to do about times after midnight,
# are they evening or morning?
if (hr < 12) {
print("good morning")
} else if (hr < 17) {
print("good afternoon")
} else {
print("good evening")
}
}
greet()
[1] "good afternoon"
greet(ymd_h("2017-01-08:05"))
[1] "good morning"
greet(ymd_h("2017-01-08:13"))
[1] "good afternoon"
greet(ymd_h("2017-01-08:20"))
[1] "good evening"
Implement a fizzbuzz function. It takes a single number as input. If the number is divisible by three, it returns “fizz”. If it’s divisible by five it returns “buzz”. If it’s divisible by three and five, it returns “fizzbuzz”. Otherwise, it returns the number. Make sure you first write working code before you create the function.
fizzbuzz <- function(x) {
stopifnot(length(x) == 1)
stopifnot(is.numeric(x))
# this could be made more efficient by minimizing the
# number of tests
if (!(x %% 3) & !(x %% 5)) {
print("fizzbuzz")
} else if (!(x %% 3)) {
print("fizz")
} else if (!(x %% 5)) {
print("buzz")
}
}
fizzbuzz(5)
[1] "buzz"
fizzbuzz(15)
[1] "fizzbuzz"
fizzbuzz(4)
How could you use cut() to simplify this set of nested if-else statements?
# original code
if (temp <= 0) {
"freezing"
} else if (temp <= 10) {
"cold"
} else if (temp <= 20) {
"cool"
} else if (temp <= 30) {
"warm"
} else {
"hot"
}
Two advantages of using cut is that it works on vectors, whereas if only works on a single value (I already demonstrated this above), and that to change comparisons I only needed to change the argument to right, but I would have had to change four operators in the if expression.
temp <- seq(-10, 50, by = 5)
cut(temp, c(-Inf, 0, 10, 20, 30, Inf), right = TRUE,
labels = c("freezing", "cold", "cool", "warm", "hot"))
[1] freezing freezing freezing cold cold cool cool warm
[9] warm hot hot hot hot
Levels: freezing cold cool warm hot
Function Arguments
The arguments to a function typically fall into two broad sets: one set supplies the data to compute on, and the other supplies arguments that control the details of the compution. for example: in log(), the data is x, and the detail is the base of the logarithm in mean(), the data is x and the details are how much data to trim from the ends (trim) and how to handle missing values (na.rm). In t.test(), the data are x and y, and the details of the test are alternative, mu, paired, var.equal and conf.level. in str_c() you supply any number of strings to … and the details of the concatenation are controlled by sep and collapse
Choosing Names
Th names of the arguments are important to readers. Generally you should prefer longer, more descriptive names, but there are a handful of very common, very short names:
x,y,z: vectors w: a vector of weights df: a data frame i, j : numberic indices n: length, or number of rows p: number of columns
Checking Values
It’s good practice to check importatn preconditions and throw an error with stop() if they are not true
wt_mean <- function(x,w) {
if (length(x) != length(w)){
stop("'x' and 'w' must be the same length", call. = FALSE)
}
sum(w*x)/sum(x)
}
wt_mean(1:6, 2:7)
[1] 5.333333
See also stopifnot() it checks that each argument is TRUE and produces a gneric error message if not.
wt_mean <- function(x,w, na.rm=FALSE) {
stopifnot(is.logical(na.rm), length(na.rm) ==1)
stopifnot(length(x) == length(w))
if (na.rm) {
miss <- is.na(x) | is.na(w)
x <- x[!miss]
w <- w[!miss]
}
sum(w*x)/sum(x)
}
wt_mean(1:6, NA)
Error: length(x) == length(w) is not TRUE
Note that when using stopifnot() you assert what should be true rather than checking for what might be wrong.
h2> Dot-Dot-Dot(…)
Many functions in R take an arbitrary number of inputs. How do these functions work? They rely on a specail argument : … This special argument captures any number of arguments that aren’t otherwise matched.
It’s useful because you can then send those… on to another function. This is useful catch all if your function primarily wraps another function. For example:
commas <- function(...) stringr::str_c(..., collapse = ", ")
commas(letters[1:10])
[1] "a, b, c, d, e, f, g, h, i, j"
rule <- function(..., pad ="-") {
title <- paste0(...)
width <- getOption("width") - nchar(title) - 5
cat(title, " ", stringr::str_dup(pad,width), "\n", sep = " ")
}
rule("Important output from wilson")
Important output from wilson ------------------------------------------------
Lazy Evaluation
Arguments in R are lazily evaluated. This means that they’re not computed until they’re needed. If they are never used, they are never called. This is an important property of R.
Exercise
What does commas(letters, collapse = “-”) do? Why? The argument collapse is passed to str_c as part of …, so it tries to run str_c(letters, collapse = “-”, collapse = “,”). Combines all the alphabets into one string.
It’d be nice if you could supply multiple characters to the pad argument, e.g. rule(“Title”, pad = “-+”). Why doesn’t this currently work? How could you fix it? It does not work because it duplicates pad by the width minus the length of the string. This is implictly assuming that pad is only one character. I could adjust the code to calculate the length of pad. The trickiest part is handling what to do if width is not a multiple of the number of characters of pad.
rule <- function(..., pad ="-+") {
title <- paste0(...)
width <- getOption("width") - nchar(title) - 5
cat(title, " ", stringr::str_dup(pad,width), "\n", sep = " ")
}
rule("Important output from wilson")
Important output from wilson -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
rule("Important output from wilson", pad="*X*")
Important output from wilson *X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X*
So the solution is to figure how much to pad.
rule <- function(..., pad ="-+") {
title <- paste0(...)
width <- getOption("width") - nchar(title) - 5
padchar <- nchar(pad)
cat(title, " ", stringr::str_dup(pad,width %/% padchar),
stringr::str_sub(pad, 1, width %% padchar), "\n", sep = " ")
}
rule("Important output from wilson")
Important output from wilson -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
rule("Important output from wilson", pad="*X*")
Important output from wilson *X**X**X**X**X**X**X**X**X**X**X**X**X**X**X**X*
What does the trim argument to mean() do? When might you use it? The trim arguments trims a fraction of observations from each end of the vector (meaning the range) before calculating the mean. This is useful for calculating a measure of central tendancy that is robust to outliers. Much like 95th percentile?
The default value for the method argument to cor() is c(“pearson”, “kendall”, “spearman”). What does that mean? What value is used by default? It means that the method argument can take one of those three values. The first value, “pearson”, is used by default.
Return Values
Figuring out what your function should return is usually easy. Tere are two things you should ocnsider when returning a value :
does returning early make your function easier ot read? can you make your function pipeable?
Explicit Return statements
You can chose to return early by using return(). A common reason to do this is because the inputs are empty.
complicated_function <- function(x,y,x) {
if (length(x) == 0 || length(y) == 0) {
return(0)
}
}
Another reason is because you have an if statement with one complex block and one simple block.
f <- function () {}
if(x) {
# do
#something
#that
#takes
#many
#lines
# to
# express
} else {
# return something short
}
}
YOu can re-write it
f <- function() {
if(!x) {
return(something_short)
}
# do
#something
#that
#takes
#many
#lines
# to
# express
}
}
Writing Pipeable Functions
There are two types of pipeable functions: transformation and side-effect.
In transformation functions, there’s a clear primary object that is passed in as the first argument and a modified version is returned by the function.
side-effect functions are primairy called to perform an action, like drawing a plot or saving a file, not transforming the object.
Environment
The last component of a function is its environment. This is not something you need to understand deeply when you first start writing functions. However it’s important to know a little bit about environments because they are crucial to how functions work. If a value is not defined inside the function, R will still find it as valid because it uses lexical scoping to find the value associated with a name.
LS0tDQp0aXRsZTogIlIgRm9yIERhdGEgU2NpZW5jZSBDaGFwdGVyIDE1Ig0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCjxoMT4gQ2hhcHRlciAxNSBGdW5jdGlvbnMgPC9oMT4NCg0KRnVuY3Rpb25zIGFsbG93IHlvdSB0byBhdXRvbWF0ZSBjb21tb24gdGFza3MgaW4gYSBtb3JlIHBvd2VyZnVsIGFuZCBnZW5lcmFsIHdheSB0aGFuIGNvcHlpbmcgYW5kIHBhc3RpbmcuIFdyaXRpbmcgYSBmdW5jdGlvbiBoYXMgMyBiaWcgYWR2YW50YWdlczoNCjxsaT4gWW91IGNhbiBnaXZlIGEgZnVuY3Rpb24gYW4gZXZvY2F0aXZlIG5hbWUgdGhhdCBtYWtlcyB5b3VyIGNvZGUgZWFzaWVyIHRvIHVuZGVyc3RhbmQ8L2xpPg0KPGxpPiBBcyByZXF1aXJlbWVudHMgY2hhbmdlLCB5b3Ugb25seSBuZWVkIHRvIHVwZGF0ZSBjb2RlIGluIG9uZSBwbGNhZSwgaW5zdGVhZCBvZiBtYW55IDwvbGk+DQo8bGk+IFlvdSBlbGltaW5hdGUgdGhlIGNoYW5jZSBvZiBtYWtpbmcgaW5jaWRlbnRhbCBtaXN0YWtlcyB3aGVuIHlvdSBjb3B5IGFuZCBwYXN0ZQ0KPC9saT4NCjxoMj4gUHJlIHJlcXVpc2l0ZXMgPC9oMj4NCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmRmIDwtIHRpYmJsZSgNCiAgYSA9IHJub3JtKDEwKSwNCiAgYiA9IHJub3JtKDEwKSwNCiAgYyA9IHJub3JtKDEwKSwNCiAgZCA9IHJub3JtKDEwKQ0KICApDQpkZg0KDQpgYGANCg0KTm9uZS4gDQoNCjxoMj4gV2hlbiBzaG91bGQgeW91IHdyaXRlIGEgZnVuY3Rpb24/IDwvaDI+DQpBbnN3ZXI6IHdoZW4geW91IGNvcHkgYW5kIHBhc3RlIGEgYmxvY2sgb2YgY29kZSBtb3JlIHRoYW4gdHdpY2UgPC9icj4NCg0KYGBge3J9DQoNCg0KZGYkYSA8LSAoZGYkYSAtIG1pbihkZiRhLCBuYS5ybSA9IFRSVUUpKSAvIChtYXgoZGYkYSwgbmEucm0gPSBUUlVFKSAtIG1pbihkZiRhLCBuYS5ybSA9IFRSVUUpKQ0KZGYkYiA8LSAoZGYkYiAtIG1pbihkZiRiLCBuYS5ybSA9IFRSVUUpKSAvIChtYXgoZGYkYiwgbmEucm0gPSBUUlVFKSAtIG1pbihkZiRiLCBuYS5ybSA9IFRSVUUpKQ0KZGYkYyA8LSAoZGYkYyAtIG1pbihkZiRjLCBuYS5ybSA9IFRSVUUpKSAvIChtYXgoZGYkYywgbmEucm0gPSBUUlVFKSAtIG1pbihkZiRjLCBuYS5ybSA9IFRSVUUpKQ0KZGYkZCA8LSAoZGYkZCAtIG1pbihkZiRhLCBuYS5ybSA9IFRSVUUpKSAvIChtYXgoZGYkZCwgbmEucm0gPSBUUlVFKSAtIG1pbihkZiRkLCBuYS5ybSA9IFRSVUUpKQ0KZGYNCg0KYGBgDQoNClRvIHJld3JpdGUgdGhpcyBhcyBhIGZ1bmN0aW9uLCBmaWd1cmUgb3V0IHdoYXQgdGhlIGlucHV0IGlzIC0+IGRmJGEgPC9icj4NClRoZSB0aHJlZSBzdGVwczogPC9icj4NCjEuIFBpY2sgYSBuYW1lIGZvciB0aGUgZnVuY3Rpb24uIDwvYnI+DQoyLiBMaXN0IHRoZSBpbnB1dHMgb3IgYXJndW1lbnRzIHRvIHRoZSBmdW5jdGlvbiA8L2JyPg0KMy4gcGxhY2UgdGhlIGNvZGUgeW91IGhhdmUgZGV2ZWxvcGVkIGluIHRoZSBib2R5IG9mIHRoZSBmdW5jdGlvbiwgYSB7IGJsb2NrIHRoYXQgaW1tZWRpYXRlbHkgZm9sbG93cyBmdW5jdGlvbiguLi4pIDwvcD4NCg0KDQoNCg0KYGBge3J9DQojIHJhbmdlIGdpdmVzIHRoZSBsb3dlc3QgYW5kIGhpZ2hlc3QgdmFsdWVzIGluIGEgcmFuZ2Ugb2YgbnVtYmVycw0KcmVzY2FsZTAxIDwtICBmdW5jdGlvbih4KSB7DQogIHJuZyA8LSAgcmFuZ2UoeCwgbmEucm0gPSBUUlVFKQ0KICAoeCAtIHJuZ1sxXSkgLyAocm5nWzJdIC0gcm5nWzFdKQ0KICB9DQoNCnJlc2NhbGUwMShjKDAsNSwxMCkpDQoNCiMgdGVzdCB3aXRoIGRpZmZlcmVudCBpbnB1dHMNCnJlc2NhbGUwMShjKDEsMiwzLE5BLCA1KSkNCmBgYA0KDQpXZSBjYW4gbm93IHNpbXBsaWZ5IHRoZSBvcmlnaW5hbCBjb2RlIHdpdGggdGhpcyBmdW5jdGlvbg0KDQpgYGB7cn0NCmxpYnJhcnkodGliYmxlKQ0KZGYgPC0gdGliYmxlKA0KICBhID0gcm5vcm0oMTApLA0KICBiID0gcm5vcm0oMTApLA0KICBjID0gcm5vcm0oMTApLA0KICBkID0gcm5vcm0oMTApDQogICkNCg0KZGYkYSA8LSAgcmVzY2FsZTAxKGRmJGEpDQpkZiRiIDwtICByZXNjYWxlMDEoZGYkYikNCmRmJGMgPC0gIHJlc2NhbGUwMShkZiRjKQ0KZGYkZCA8LSAgcmVzY2FsZTAxKGRmJGQpDQoNCg0KZGYkYQ0KZGYkYg0KZGYkYw0KZGYkZA0KDQoNCmBgYA0KDQpOb3csIGlmIHdlIGRpc2NvdmVyZWQgdGhhdCB3ZSBuZWVkIHRvIGFjY291bnQgZm9yIGluZmluaXRlIHZhbHVlcywgd2Ugb25seSBuZWVkIHRvIGZpeCB0aGUgZnVuY3Rpb24NCg0KYGBge3J9DQpyZXNjYWxlMDEgPC0gIGZ1bmN0aW9uKHgpIHsNCiAgcm5nIDwtICByYW5nZSh4LCBuYS5ybSA9IFRSVUUsIGZpbml0ZSA9IFRSVUUpDQogICh4IC0gcm5nWzFdKSAvIChybmdbMl0gLSBybmdbMV0pDQogIH0NCmRmIDwtIHRpYmJsZSgNCiAgYSA9IHJub3JtKDEwKSwNCiAgYiA9IHJub3JtKDEwKSwNCiAgYyA9IHJub3JtKDEwKSwNCiAgZCA9IHJub3JtKDEwKQ0KICApDQoNCmRmJGEgPC0gIHJlc2NhbGUwMShkZiRhKQ0KZGYkYiA8LSAgcmVzY2FsZTAxKGRmJGIpDQpkZiRjIDwtICByZXNjYWxlMDEoZGYkYykNCmRmJGQgPC0gIHJlc2NhbGUwMShkZiRkKQ0KDQoNCmRmJGENCmRmJGINCmRmJGMNCmRmJGQNCg0KYGBgDQoNCjxoMz4gRXhlcmNpZSA8L2gzPg0KMS4gV2h5IGlzIFRSVUUgbm90IGEgcGFyYW1ldGVyIHRvIHJlc2NhbGUwMSgpPyBXaGF0IHdvdWxkIGhhcHBlbiBpZiB4IGNvbnRhaW5lZCBhIHNpbmdsZSBtaXNzaW5nIHZhbHVlLCBhbmQgbmEucm0gd2FzIEZBTFNFPyA8L3A+DQoNCkJ5IGEgc2luZ2xlIG1pc3NpbmcgdmFsdWUsIHRoaXMgbWVhbnMgdGhhdCB4IGhhcyBhdCBsZWFzdCBvbmUgTkEgdmFsdWUuIElmIHRoZXJlIHdlcmUgYW55IE5BIHZhbHVlcywgYW5kIG5hLnJtID0gRkFMU0UsIHRoZW4gdGhlIGZ1bmN0aW9uIHdvdWxkIHJldHVybiBOQS4gSSBjYW4gY29uZmlybSB0aGlzIGJ5IHRlc3RpbmcgYSBmdW5jdGlvbiB0aGF0IGFsbG93cyBmb3IgbmEucm0gYXMgYW4gYXJndW1lbnQgDQoNCmBgYHtyfQ0KDQpyZXNjYWxlMDFfYWx0IDwtIGZ1bmN0aW9uKHgsIGZpbml0ZSA9IFRSVUUpIHsNCiAgcm5nIDwtIHJhbmdlKHgsIG5hLnJtID0gZmluaXRlLCBmaW5pdGUgPSBmaW5pdGUpDQogICh4IC0gcm5nWzFdKSAvIChybmdbMl0gLSBybmdbMV0pDQp9DQpyZXNjYWxlMDFfYWx0KGMoTkEsIDE6NSksIGZpbml0ZSA9IEZBTFNFKQ0KYGBgDQoNCkluIHRoZSBzZWNvbmQgdmFyaWFudCBvZiByZXNjYWxlMDEoKSwgaW5maW5pdGUgdmFsdWVzIGFyZSBsZWZ0IHVuY2hhbmdlZC4gUmV3cml0ZSByZXNjYWxlMDEoKSBzbyB0aGF0IC1JbmYgaXMgbWFwcGVkIHRvIDAsIGFuZCBJbmYgaXMgbWFwcGVkIHRvIDEuDQoNCmBgYHtyfQ0KcmVzY2FsZTAxIDwtIGZ1bmN0aW9uKHgpIHsNCiAgcm5nIDwtIHJhbmdlKHgsIG5hLnJtID0gVFJVRSwgZmluaXRlID0gVFJVRSkNCiAgeSA8LSAoeCAtIHJuZ1sxXSkgLyAocm5nWzJdIC0gcm5nWzFdKQ0KICB5W3kgPT0gLUluZl0gPC0gMA0KICB5W3kgPT0gSW5mXSA8LSAxDQogIHkNCn0NCg0KcmVzY2FsZTAxKGMoSW5mLCAtSW5mLCAwOjUsIE5BKSkNCmBgYA0KDQpQcmFjdGljZSB0dXJuaW5nIHRoZSBmb2xsb3dpbmcgY29kZSBzbmlwcGV0cyBpbnRvIGZ1bmN0aW9ucy4gVGhpbmsgYWJvdXQgd2hhdCBlYWNoIGZ1bmN0aW9uIGRvZXMuIFdoYXQgd291bGQgeW91IGNhbGwgaXQ/IEhvdyBtYW55IGFyZ3VtZW50cyBkb2VzIGl0IG5lZWQ/IENhbiB5b3UgcmV3cml0ZSBpdCB0byBiZSBtb3JlIGV4cHJlc3NpdmUgb3IgbGVzcyBkdXBsaWNhdGl2ZT8NCiANCiANCmBgYHtyfQ0KbWVhbihpcy5uYSh4KSkNCnggLyBzdW0oeCwgbmEucm0gPSBUUlVFKQ0Kc2QoeCwgbmEucm0gPSBUUlVFKSAvIG1lYW4oeCwgbmEucm0gPSBUUlVFKQ0KYGBgDQoNClRoaXMgZnVuY3Rpb24gc3RhbmRhcmRpemVzIGEgZnVuY3Rpb24gdG8gaXRzIHdlaWdodC4gSWYgYWxsIGVsZW1lbnRzIG9mIHggYXJlIG5vbi1uZWdhdGl2ZSwgdGhpcyB3aWxsIGVuc3VyZSB0aGUgdmVjdG9yIHN1bXMgdG8gMS4NCg0KYGBge3J9DQp3ZWlnaHRzIDwtIGZ1bmN0aW9uKHgpIHsNCiAgeCAvIHN1bSh4LCBuYS5ybSA9IFRSVUUpDQp9DQp5IDwtIHdlaWdodHMoMDo1KQ0KeQ0Kc3VtKHkpDQpgYGANCg0KV3JpdGUgYm90aF9uYSgpLCBhIGZ1bmN0aW9uIHRoYXQgdGFrZXMgdHdvIHZlY3RvcnMgb2YgdGhlIHNhbWUgbGVuZ3RoIGFuZCByZXR1cm5zIHRoZSBudW1iZXIgb2YgcG9zaXRpb25zIHRoYXQgaGF2ZSBhbiBOQSBpbiBib3RoIHZlY3RvcnMuDQoNCmBgYHtyfQ0KYm90aF9uYSA8LSBmdW5jdGlvbih4LCB5KSB7DQogIHN1bShpcy5uYSh4KSAmIGlzLm5hKHkpKQ0KfQ0KYm90aF9uYShjKE5BLCBOQSwgIDEsIDIpLA0KICAgICAgICBjKE5BLCAgMSwgTkEsIDIpKQ0KDQpib3RoX25hKGMoTkEsIE5BLCAgMSwgMiwgTkEsIE5BLCAxKSwgDQogICAgICAgIGMoTkEsICAxLCBOQSwgMiwgTkEsIE5BLCAxKSkNCmBgYA0KDQpXaGF0IGRvIHRoZSBmb2xsb3dpbmcgZnVuY3Rpb25zIGRvPyBXaHkgYXJlIHRoZXkgdXNlZnVsIGV2ZW4gdGhvdWdoIHRoZXkgYXJlIHNvIHNob3J0Pw0KDQpgYGB7cn0NCmlzX2RpcmVjdG9yeSA8LSBmdW5jdGlvbih4KSBmaWxlLmluZm8oeCkkaXNkaXINCmlzX3JlYWRhYmxlIDwtIGZ1bmN0aW9uKHgpIGZpbGUuYWNjZXNzKHgsIDQpID09IDANCmBgYA0KDQpUaGUgZnVuY3Rpb24gaXNfZGlyZWN0b3J5IGNoZWNrcyB3aGV0aGVyIHRoZSBwYXRoIGluIHggaXMgYSBkaXJlY3RvcnkuIFRoZSBmdW5jdGlvbiBpc19yZWFkYWJsZSBjaGVja3Mgd2hldGhlciB0aGUgcGF0aCBpbiB4IGlzIHJlYWRhYmxlLCBtZWFuaW5nIHRoYXQgdGhlIGZpbGUgZXhpc3RzIGFuZCB0aGUgdXNlciBoYXMgcGVybWlzc2lvbiB0byBvcGVuIGl0LiBUaGVzZSBmdW5jdGlvbnMgYXJlIHVzZWZ1bCBldmVuIHRob3VnaCB0aGV5IGFyZSBzaG9ydCBiZWNhdXNlIHRoZWlyIG5hbWVzIG1ha2UgaXQgbXVjaCBjbGVhcmVyIHdoYXQgdGhlIGNvZGUgaXMgZG9pbmcuDQoNCg0KPGgyPiBGdW5jdGlvbnMgYXJlIGZvciBodW1hbnMgYW5kIGNvbXB1dGVycyA8L2gyPg0KDQpHZW5lcmFsbHksIGZ1bmN0aW9uIG5hbWVzIHNob3VsZCBiZSB2ZXJicywgYW5kIGFyZ3VtZW50cyBzaG91bGQgYmUgbm91bnMuIFRoZXJlIGFyZSBzb21lIGV4Y2VwdGlvbnM6IG5vdW5zIGFyZSBPS1kgaWYgdGhlIGZ1bmN0aW9uIGNvbXB1dGVzIGEgdmVyeSB3ZWxsIGtub3duIG5vdW4gKGllLiBlbWFuKCkpIGlzIGJldHRlciB0aGFuIGNvbXB1dGVfbWVhbigpLiA8L3A+DQoNCklmIHRoZSBmdW5jdGlvbiB1c2VzIG11bHRpcGxlIHdvcmRzLCB1c2UgdGhlIHNuYWtlX2Nhc2UsIHdoZXJlIGVhY2ggbG93ZXJjYXNlIHdvcmQgaXMgc2VwYXJhdGUgYnkgYW4gdW5kZXJzY29yZS4gQWxzbyBhdm9pZCB0aGUgbW9zdCBjb21tb24gbmFtZXMgZnJvbSBiYXNlIFIgdG8gYXZvaWQgY29uZnVzaW9uLiA8L3A+DQoNClVzZSBjb21tZW50cywgbGluZXMgc3RhdGluZyB3aXRoICMgdG8gZXhwbGFpbiB0aGUgIndoeSIgb2YgeW91ciBjb2RlLiBZb3UgZ2VuZXJhbGx5IHNob3VsZCBhdm9pZCBjb21tZW50cyB0aGF0IGV4cGxhaW4gdGhlICJ3aGF0IiBvciB0aGUgImhvdyIuICA8L3A+DQoNClVzZSBsb25nIGxpbmVzIG9mIC0gb3IgPSB0byBtYWtlIGl0IGVhc3kgdG8gc3BvdCB0aGUgYnJlYWtzOg0KDQpgYGB7cn0NCiMgbG9hZCBkYXRhIC0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgUGxvdCBkYXRhID09PT09PT09PT09PT09PT09DQoNCmBgYA0KDQo8aDM+IEV4ZXJjaXNlIDwvaDM+DQoNCmBgYHtyfQ0KZjEgPC0gZnVuY3Rpb24oc3RyaW5nLCBwcmVmaXgpIHsNCiAgc3Vic3RyKHN0cmluZywgMSwgbmNoYXIocHJlZml4KSkgPT0gcHJlZml4DQp9DQoNCmYyIDwtIGZ1bmN0aW9uKHgpIHsNCiAgaWYgKGxlbmd0aCh4KSA8PSAxKSByZXR1cm4oTlVMTCkNCiAgeFstbGVuZ3RoKHgpXQ0KfQ0KDQpmMyA8LSBmdW5jdGlvbih4LCB5KSB7DQogIHJlcCh5LCBsZW5ndGgub3V0ID0gbGVuZ3RoKHgpKQ0KfQ0KYGBgDQoNCmYxIHJldHVybnMgd2hldGhlciBhIGZ1bmN0aW9uIGhhcyBhIGNvbW1vbiBwcmVmaXguIDwvYnI+DQpUaGUgZnVuY3Rpb24gZjIgZHJvcHMgdGhlIGxhc3QgZWxlbWVudC4gQSBiZXR0ZXIgbmFtZSBmb3IgZjIgaXMgZHJvcF9sYXN0KCkuIDwvYnI+DQpUaGlzIGlzIGEgaGFyZGVyIG9uZSB0byBuYW1lLiBJIHdvdWxkIHNheSBzb21ldGhpbmcgbGlrZSByZWN5Y2xlIChSJ3MgbmFtZSBmb3IgdGhpcyBiZWhhdmlvciksIG9yIGVweGFuZC48L3A+DQoNCkNvbXBhcmUgYW5kIGNvbnRyYXN0IHJub3JtKCkgYW5kIE1BU1M6Om12cm5vcm0oKS4gSG93IGNvdWxkIHlvdSBtYWtlIHRoZW0gbW9yZSBjb25zaXN0ZW50PyA8L2JyPg0Kcm5vcm0gc2FtcGxlcyBmcm9tIHRoZSB1bml2YXJpYXRlIG5vcm1hbCBkaXN0cmlidXRpb24sIHdoaWxlIE1BU1M6Om12cm5vcm0gc2FtcGxlcyBmcm9tIHRoZSBtdWx0aXZhcmlhdGUgbm9ybWFsIGRpc3RyaWJ1dGlvbi4gVGhlIG1haW4gYXJndW1lbnRzIGluIHJub3JtIGFyZSBuLCBtZWFuLCBzZC4gVGhlIG1haW4gYXJndW1lbnRzIGlzIE1BU1M6Om12cm5vcm0gYXJlIG4sIG11LCBTaWdtYS4gVG8gYmUgY29uc2lzdGVudCB0aGV5IHNob3VsZCBoYXZlIHRoZSBzYW1lIG5hbWVzLiBIb3dldmVyLCB0aGlzIGlzIGRpZmZpY3VsdC4gSW4gZ2VuZXJhbCwgaXQgaXMgYmV0dGVyIHRvIGJlIGNvbnNpc3RlbnQgd2l0aCBtb3JlIHdpZGVseSB1c2VkIGZ1bmN0aW9ucywgZS5nLiBybXZub3JtIHNob3VsZCBmb2xsb3cgdGhlIGNvbnZlbnRpb25zIG9mIHJub3JtLiBIb3dldmVyLCB3aGlsZSBtZWFuIGlzIGNvcnJlY3QgaW4gdGhlIG11bHRpdmFyaWF0ZSBjYXNlLCBzZCBkb2VzIG5vdCBtYWtlIHNlbnNlIGluIHRoZSBtdWx0aXZhcmlhdGUgY2FzZS4gQm90aCBmdW5jdGlvbnMgYW4gaW50ZXJuYWxseSBjb25zaXN0ZW50IHRob3VnaDsgaXQgd291bGQgYmUgYmFkIHRvIGhhdmUgbXUgYW5kIHNkIG9yIG1lYW4gYW5kIFNpZ21hLiA8L3A+DQoNCg0KTWFrZSBhIGNhc2UgZm9yIHdoeSBub3JtX3IoKSwgbm9ybV9kKCkgZXRjIHdvdWxkIGJlIGJldHRlciB0aGFuIHJub3JtKCksIGRub3JtKCkuIE1ha2UgYSBjYXNlIGZvciB0aGUgb3Bwb3NpdGUuIDwvcD4NCg0KSWYgbmFtZWQgbm9ybV9yIGFuZCBub3JtX2QsIGl0IGdyb3VwcyB0aGUgZmFtaWx5IG9mIGZ1bmN0aW9ucyByZWxhdGVkIHRvIHRoZSBub3JtYWwgZGlzdHJpYnV0aW9uLiBJZiBuYW1lZCBybm9ybSwgYW5kIGRub3JtLCBmdW5jdGlvbnMgcmVsYXRlZCB0byBhcmUgZ3JvdXBlZCBpbnRvIGZhbWlsaWVzIGJ5IHRoZSBhY3Rpb24gdGhleSBwZXJmb3JtLiByKiBmdW5jdGlvbnMgYWx3YXlzIHNhbXBsZSBmcm9tIGRpc3RyaWJ1dGlvbnM6IHJub3JtLCByYmlub20sIHJ1bmlmLCByZXhwLiBkKiBmdW5jdGlvbnMgY2FsY3VsYXRlIHRoZSBwcm9iYWJpbGl0eSBkZW5zaXR5IG9yIG1hc3Mgb2YgYSBkaXN0cmlidXRpb246IGRub3JtLCBkYmlub20sIGR1bmlmLCBkZXhwLiA8L3A+DQoNCjxoMj4gQ29uZGl0aW9uYWwgRXhlY3V0aW9uIDwvaDI+DQpBbiBpZiBzdGF0ZW1lbnQgYWxsb3dzIHlvdSB0byBjb25kaXRpb25hbGx5IGV4ZWN1dGUgY29kZS4gIEl0IGxvb2tzIGxpa2UgdGhpczoNCg0KYGBge3J9DQppZiAoY29uZGl0aW9uKSB7DQogICMgY29kZSBleGVjdXRlZCB3aGVuIGNvbmRpdGlvbiBpcyB0cnVlDQp9IGVsc2Ugew0KICAjIGNvZGUgZXhlY3V0ZWQgd2hlbiBjb25kaXRpb24gaXMgZmFsc2UNCn0NCg0KYGBgDQoNClRvIGdldCBoZWxwIG9uIGlmLCBlbmNsb3NlIGl0IHdpdGggYmFja3RpY2tzIGFuZCA/IA0KDQo/ICdpZicgPC9icj4NCg0KUmVtZW1iZXIgdGhhdCB0aGUgZnVuY3Rpb24gcmV0dXJucyB0aGUgbGFzdCB2YWx1ZSB0aGF0IGl0IGNvbXB1dGVkOg0KYGBge3J9DQpoYXNfbmFtZSA8LSAgZnVuY3Rpb24oeCkgew0KICBubXMgPC0gbmFtZXMoeCkNCiAgaWYgKGlzLm51bGwobm1zKSkgew0KICAgIHJlcChGQUxTRSwgbGVuZ3RoKHgpKQ0KICB9IGVsc2Ugew0KICAgICFpcy5uYShubXMpICYgbm1zICE9IiINCiAgfQ0KfQ0KDQpoYXNfbmFtZShjKCJ3aWxzb24iLCAiIiwicm9iZXJ0IikpDQpgYGANCg0KPGgyPiBDb25kaXRpb25zIDwvaDI+DQpUaGUgY29uZGl0aW9uIG11c3QgZXZhbHVhdGUgdG8gZWl0aGVyIFRSVUUgb3IgRkFMU0UuIElmIGl0J3MgYSB2ZWN0b3IsIHlvdSdsbCBnZXQgYSB3YXJuaW5nIG1lc3NhZ2U7IGlmIGl0J3MgYW4gTkEsIHlvdSdsbCBnZXQgYW4gZXJyb3IuIFdhdGNoIG91dCBmb3IgdGhlc2UgbWVzc2FnZXMgaW4geW91ciBvd24gY29kZS4gPC9wPg0KWW91IGNhbiB1c2UgfHwgKG9yKSAgb3IgJiYgKGFuZCkgdG8gY29tYmluZSBtdWxpdHBsZSBsb2dpY2FsIGV4cHJlc3Npb25zLiBUaGVzZSBvcGVyYXRvcnMgYXJlICJzaG9ydC1jaXJjdWl0aW5nIiBhcyBzb29uIGFzIHx8IHNlZXMgdGhlIGZpcnN0IFRSVUUgaXQgcmV0dXJucyBUUlVFIHdpdGhvdXQgY29tcHV0aW5nIGFueXRoaW5nIGVsc2UuIEFzIHNvb24gYXMgJSUgc2VlcyB0aGUgZmlyc3QgRkFMU0UgaXQgcmV0dXJucyBGQUxTRS4gPC9wPg0KDQpZb3Ugc2hvdWxkIG5ldmVyIHVzZSB8IG9yICUgaW4gYW4gaWYgc3RhdGVtZW50LiBUaGVzZSBhcmUgdmVjdG9yaXplZCBvcGVyYXRpb25zIHRoYXQgYXBwbHkgdG8gbXVsaXRwbGUgdmFsdWVzICh0aGF0IGlzIHdoeSB5b3UgdXNlIHRoZW0gaW4gZmlsdGVyKCkpLiBJZiB5b3UgZG8gaGF2ZSBhIGxvZ2ljYWwgdmVjdG9yLCB5b3UgY2FuIHVzZSBhbnkoKSBvciBhbGwoKSB0byBjb2xsYXBzZSBpdCB0byBhIHNpbmdsZSB2YWx1ZSA8L3A+DQoNCkJlIGNhcmVmdWwgd2hlbiB0ZXN0aW5nIGZvciBlcXVhbGl0eS4gPT0gaXMgdmVjdG9yaXplZCwgd2hpY2ggbWVhbnMgdGhhdCBpdCdzIGVhc3kgdG8gZ2V0IG1vcmUgdGhhbiBvbmUgb3V0cHV0LiBFaXRoZXIgY2hlY2sgdGhlIGxlbmd0aCBpcyBhbHJlYWR5IDEsIGNvbGxhcHNlIHdpdGggYWxsKCkgb3IgYW55KCkgLCBvciB1c2UgdGhlIG5vbiB2ZWN0b3JpemVkIGlkZW50aWNhbCgpLiA8L3A+DQoNCklkZW50aWNhbCgpIGlzIHZlcnkgc3RyaWN0LiBJdCBhbHdheXMgcmV0dXJucyBlaXRoZXIgYSBzaW5nbGUgVFJVRSBvciBhIHNpbmdsZSBGQUxTRS4gSXQgZG9lc250IGNvZXJjZSB0eXBlcy4gVGhpcyBtZWFucyB0aGF0IHlvdSBuZWVkIHRvIGJlIGNhcmVmdWwgd2hlbiBjb21hcHJpbmcgaW50ZWdlcnMgYW5kIGRvdWJsZXMuIDwvcD4NCg0KQWxzbyBiZSB3YXJ5IG9mIGZsb2F0aW5nIHBvaW50IG51bWJlcnMuIFVzZSBkcGx5cjo6bmVhcigpIGZvciBjb21wYXJpc29ucy4gIEFuZCByZW1lbWJlciB4ID09IE5BIGRvZXNudCBkbyBhbnl0aGluZyB1c2VmdWwuICA8L3A+DQoNCjxoMj4gTXVsdGlwbGUgQ29uZGl0aW9ucyA8L2gyPg0KWW91IGNhbiBjaGFpbiBtdWx0aXBsZSBpZiBzdGF0ZW1lbnRzIHRvZ2V0aGVyOg0KDQpgYGB7cn0NCmlmICh0aGlzKSB7DQogICMgZG8gdGhhdA0KfSBlbHNlIGlmICh0aGF0KSB7DQogICAgIyBkbyBzb21ldGhpbmcgZWxzZSANCn0gZWxzZSB7DQogICAgIyBkbyB0aGlzDQp9DQoNCg0KYGBgDQoNCklmIHlvdSBlbmQgdXAgd2l0aCBhIGxhcmdlIHNlcmllcyBvZiBpZiBzdGF0ZW1lbnRzLCBiZXR0ZXIgdXNlIHN3aXRjaCgpIGZ1bmN0aW9uLiBJdCBhbGxvd3MgeW91IHRvIGVhdmx1YXRlIHNlbGVjdGVkIGNvZGUgYmFzZWQgb24gcG9zaXRpb24gb3IgbmFtZS4gPC9wPg0KDQpgYGB7cn0NCnN3aXRjaF9kZW1vIDwtIGZ1bmN0aW9uKHgseSxvcCkgew0KICBzd2l0Y2gob3AsDQogICAgICAgICBwbHVzICA9IHggKyB5LA0KICAgICAgICAgbWludXMgPSB4IC0geSwNCiAgICAgICAgIHRpbWVzID0geCAqIHksDQogICAgICAgICBkaXZpZGUgPSB4IC8geSwNCiAgICAgICAgIHNxciA9IHggXiB5LA0KICAgICAgICAgc3RvcCgidW5rb3duIG9wISIpDQogICAgICAgICApDQp9DQoNCnN3aXRjaF9kZW1vKDQsMiwic3FyIikNCg0KYGBgDQoNCg0KPGgyPiBDb2RlIFN0eWxlIDwvaDI+DQpCb3RoIGlmIGFuZCBmdW5jdGlvbiBzaG91bGQgKGFsbW9zdCkgYWx3YXN5IGJlIGZvbGxvd2VkIGJ5IHNxdWlnZ2x5IGJyYWNrZXRzKHt9KSAgYW5kIHRoZSBjb250ZW50cyBzaG91bGQgYmUgaW5kZW50ZWQgYnkgdHdvIHNwYWNlcy4gVGhpcyBtYWtlcyBpdCBlYXNpZXIgdG8gc2VlIHRoZSBoaWVyYXJjeSBpbiB5b3VyIGNvZGUgYnkgc2tpbW1pbmcgdGhlIGxlZnRoYW5kIG1hcmdpbi4gDQo8L3A+DQoNCkFuIG9wZW5pbmcgY3VybHkgYnJhY2Ugc2hvdWxkIG5ldmVyIGdvIG9uIGl0dyBvd24gbGluZSBhbmQgc2hvdWxkIGFsd2F5cyBiZSBmb2xsb3dlZCBieSBhIG5ldyBsaW5lLiBBIGNsb3NpbmcgY3VybHkgYnJhY2Ugc2hvdWxkIGFsd2F5cyBnbyBvbiBpdHMgb3duIGxpbmUsIHVubGVzcyBpdCdzIGZvbGxvd2VkIGJ5IGVsc2UuIEFsd2F5cyBpbmRlbnQgdGhlIGNvZGUgaW5zaWRlIGN1cmx5IGJyYWNlcyAsL3A+DQoNCjxoMz4gRXhlcmNpc2VzIDwvaDM+DQpXaGF0J3MgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBpZiBhbmQgaWZlbHNlKCk/IENhcmVmdWxseSByZWFkIHRoZSBoZWxwIGFuZCBjb25zdHJ1Y3QgdGhyZWUgZXhhbXBsZXMgdGhhdCBpbGx1c3RyYXRlIHRoZSBrZXkgZGlmZmVyZW5jZXMuIDwvYnI+DQpUaGUga2V5d29yZCBpZiB0ZXN0cyBhIHNpbmdsZSBjb25kaXRpb24sIHdoaWxlIGlmZWxzZSB0ZXN0cyBlYWNoIGVsZW1lbnQuIDwvcD4NCg0KV3JpdGUgYSBncmVldGluZyBmdW5jdGlvbiB0aGF0IHNheXMgImdvb2QgbW9ybmluZyIsICJnb29kIGFmdGVybm9vbiIsIG9yICJnb29kIGV2ZW5pbmciLCBkZXBlbmRpbmcgb24gdGhlIHRpbWUgb2YgZGF5LiAoSGludDogdXNlIGEgdGltZSBhcmd1bWVudCB0aGF0IGRlZmF1bHRzIHRvIGx1YnJpZGF0ZTo6bm93KCkuIFRoYXQgd2lsbCBtYWtlIGl0IGVhc2llciB0byB0ZXN0IHlvdXIgZnVuY3Rpb24uKSA8L3A+DQogDQpgYGB7cn0NCmxpYnJhcnkobHVicmlkYXRlKQ0KZ3JlZXQgPC0gZnVuY3Rpb24odGltZSA9IGx1YnJpZGF0ZTo6bm93KCkpIHsNCiAgaHIgPC0gaG91cih0aW1lKQ0KICAjIEkgZG9uJ3Qga25vdyB3aGF0IHRvIGRvIGFib3V0IHRpbWVzIGFmdGVyIG1pZG5pZ2h0LCANCiAgIyBhcmUgdGhleSBldmVuaW5nIG9yIG1vcm5pbmc/DQogIGlmIChociA8IDEyKSB7DQogICAgcHJpbnQoImdvb2QgbW9ybmluZyIpDQogIH0gZWxzZSBpZiAoaHIgPCAxNykgew0KICAgIHByaW50KCJnb29kIGFmdGVybm9vbiIpDQogIH0gZWxzZSB7DQogICAgcHJpbnQoImdvb2QgZXZlbmluZyIpDQogIH0NCn0gDQoNCmdyZWV0KCkNCmdyZWV0KHltZF9oKCIyMDE3LTAxLTA4OjA1IikpDQpncmVldCh5bWRfaCgiMjAxNy0wMS0wODoxMyIpKQ0KZ3JlZXQoeW1kX2goIjIwMTctMDEtMDg6MjAiKSkNCmBgYA0KSW1wbGVtZW50IGEgZml6emJ1enogZnVuY3Rpb24uIEl0IHRha2VzIGEgc2luZ2xlIG51bWJlciBhcyBpbnB1dC4gSWYgdGhlIG51bWJlciBpcyBkaXZpc2libGUgYnkgdGhyZWUsIGl0IHJldHVybnMgImZpenoiLiBJZiBpdCdzIGRpdmlzaWJsZSBieSBmaXZlIGl0IHJldHVybnMgImJ1enoiLiBJZiBpdCdzIGRpdmlzaWJsZSBieSB0aHJlZSBhbmQgZml2ZSwgaXQgcmV0dXJucyAiZml6emJ1enoiLiBPdGhlcndpc2UsIGl0IHJldHVybnMgdGhlIG51bWJlci4gTWFrZSBzdXJlIHlvdSBmaXJzdCB3cml0ZSB3b3JraW5nIGNvZGUgYmVmb3JlIHlvdSBjcmVhdGUgdGhlIGZ1bmN0aW9uLg0KDQpgYGB7cn0NCmZpenpidXp6IDwtIGZ1bmN0aW9uKHgpIHsNCiAgc3RvcGlmbm90KGxlbmd0aCh4KSA9PSAxKQ0KICBzdG9waWZub3QoaXMubnVtZXJpYyh4KSkNCiAgIyB0aGlzIGNvdWxkIGJlIG1hZGUgbW9yZSBlZmZpY2llbnQgYnkgbWluaW1pemluZyB0aGUNCiAgIyBudW1iZXIgb2YgdGVzdHMNCiAgaWYgKCEoeCAlJSAzKSAmICEoeCAlJSA1KSkgew0KICAgIHByaW50KCJmaXp6YnV6eiIpDQogIH0gZWxzZSBpZiAoISh4ICUlIDMpKSB7DQogICAgcHJpbnQoImZpenoiKQ0KICB9IGVsc2UgaWYgKCEoeCAlJSA1KSkgew0KICAgIHByaW50KCJidXp6IikNCiAgfQ0KfQ0KDQpmaXp6YnV6eig1KQ0KZml6emJ1enooMTUpDQpmaXp6YnV6eig0KQ0KYGBgDQoNCiBIb3cgY291bGQgeW91IHVzZSBjdXQoKSB0byBzaW1wbGlmeSB0aGlzIHNldCBvZiBuZXN0ZWQgaWYtZWxzZSBzdGF0ZW1lbnRzPw0KIA0KIA0KYGBge3J9DQojIG9yaWdpbmFsIGNvZGUNCmlmICh0ZW1wIDw9IDApIHsNCiAgImZyZWV6aW5nIg0KfSBlbHNlIGlmICh0ZW1wIDw9IDEwKSB7DQogICJjb2xkIg0KfSBlbHNlIGlmICh0ZW1wIDw9IDIwKSB7DQogICJjb29sIg0KfSBlbHNlIGlmICh0ZW1wIDw9IDMwKSB7DQogICJ3YXJtIg0KfSBlbHNlIHsNCiAgImhvdCINCn0NCg0KYGBgDQoNClR3byBhZHZhbnRhZ2VzIG9mIHVzaW5nIGN1dCBpcyB0aGF0IGl0IHdvcmtzIG9uIHZlY3RvcnMsIHdoZXJlYXMgaWYgb25seSB3b3JrcyBvbiBhIHNpbmdsZSB2YWx1ZSAoSSBhbHJlYWR5IGRlbW9uc3RyYXRlZCB0aGlzIGFib3ZlKSwgYW5kIHRoYXQgdG8gY2hhbmdlIGNvbXBhcmlzb25zIEkgb25seSBuZWVkZWQgdG8gY2hhbmdlIHRoZSBhcmd1bWVudCB0byByaWdodCwgYnV0IEkgd291bGQgaGF2ZSBoYWQgdG8gY2hhbmdlIGZvdXIgb3BlcmF0b3JzIGluIHRoZSBpZiBleHByZXNzaW9uLiA8L3A+DQoNCmBgYHtyfQ0KdGVtcCA8LSBzZXEoLTEwLCA1MCwgYnkgPSA1KQ0KY3V0KHRlbXAsIGMoLUluZiwgMCwgMTAsIDIwLCAzMCwgSW5mKSwgcmlnaHQgPSBUUlVFLA0KICAgIGxhYmVscyA9IGMoImZyZWV6aW5nIiwgImNvbGQiLCAiY29vbCIsICJ3YXJtIiwgImhvdCIpKQ0KDQpgYGANCg0KPGgyPiBGdW5jdGlvbiBBcmd1bWVudHMgPC9oMj4NClRoZSBhcmd1bWVudHMgdG8gYSBmdW5jdGlvbiB0eXBpY2FsbHkgZmFsbCBpbnRvIHR3byBicm9hZCBzZXRzOiBvbmUgc2V0IHN1cHBsaWVzIHRoZSBkYXRhIHRvIGNvbXB1dGUgb24sIGFuZCB0aGUgb3RoZXIgc3VwcGxpZXMgYXJndW1lbnRzIHRoYXQgY29udHJvbCB0aGUgZGV0YWlscyBvZiB0aGUgY29tcHV0aW9uLiBmb3IgZXhhbXBsZTogPC9icj4NCmluIGxvZygpLCB0aGUgZGF0YSBpcyB4LCBhbmQgdGhlIGRldGFpbCBpcyB0aGUgYmFzZSBvZiB0aGUgbG9nYXJpdGhtIDwvYnI+DQppbiBtZWFuKCksIHRoZSBkYXRhIGlzIHggYW5kIHRoZSBkZXRhaWxzIGFyZSBob3cgbXVjaCBkYXRhIHRvIHRyaW0gZnJvbSB0aGUgZW5kcyAodHJpbSkgYW5kIGhvdyB0byBoYW5kbGUgbWlzc2luZyB2YWx1ZXMgKG5hLnJtKS4gPC9icj4NCkluIHQudGVzdCgpLCB0aGUgZGF0YSBhcmUgeCBhbmQgeSwgYW5kIHRoZSBkZXRhaWxzIG9mIHRoZSB0ZXN0IGFyZSBhbHRlcm5hdGl2ZSwgbXUsIHBhaXJlZCwgdmFyLmVxdWFsIGFuZCBjb25mLmxldmVsLg0KPC9icj4NCmluIHN0cl9jKCkgeW91IHN1cHBseSBhbnkgbnVtYmVyIG9mIHN0cmluZ3MgdG8gLi4uIGFuZCB0aGUgZGV0YWlscyBvZiB0aGUgY29uY2F0ZW5hdGlvbiBhcmUgY29udHJvbGxlZCBieSBzZXAgYW5kIGNvbGxhcHNlIDwvcD4NCg0KPGgyPiBDaG9vc2luZyBOYW1lcyA8L2gyPg0KVGggbmFtZXMgb2YgdGhlIGFyZ3VtZW50cyBhcmUgaW1wb3J0YW50IHRvIHJlYWRlcnMuIEdlbmVyYWxseSB5b3Ugc2hvdWxkIHByZWZlciBsb25nZXIsIG1vcmUgZGVzY3JpcHRpdmUgbmFtZXMsIGJ1dCB0aGVyZSBhcmUgYSBoYW5kZnVsIG9mIHZlcnkgY29tbW9uLCB2ZXJ5IHNob3J0IG5hbWVzOiA8L2JyPg0KDQp4LHksejogdmVjdG9ycyA8L2JyPg0KdzogYSB2ZWN0b3Igb2Ygd2VpZ2h0cyA8L2JyPg0KZGY6IGEgZGF0YSBmcmFtZSA8L2JyPg0KaSwgaiA6IG51bWJlcmljIGluZGljZXMgPC9icj4NCm46IGxlbmd0aCwgb3IgbnVtYmVyIG9mIHJvd3MgPC9icj4NCnA6IG51bWJlciBvZiBjb2x1bW5zIDwvYnI+DQoNCjxoMj4gQ2hlY2tpbmcgVmFsdWVzIDwvaDI+DQpJdCdzIGdvb2QgcHJhY3RpY2UgdG8gY2hlY2sgaW1wb3J0YXRuIHByZWNvbmRpdGlvbnMgYW5kIHRocm93IGFuIGVycm9yIHdpdGggc3RvcCgpIGlmIHRoZXkgYXJlIG5vdCB0cnVlDQoNCmBgYHtyfQ0Kd3RfbWVhbiA8LSAgZnVuY3Rpb24oeCx3KSB7DQogIGlmIChsZW5ndGgoeCkgIT0gbGVuZ3RoKHcpKXsNCiAgICBzdG9wKCIneCcgYW5kICd3JyBtdXN0IGJlIHRoZSBzYW1lIGxlbmd0aCIsIGNhbGwuID0gRkFMU0UpDQogIH0NCiAgc3VtKHcqeCkvc3VtKHgpDQp9DQoNCnd0X21lYW4oMTo2LCAyOjcpDQpgYGANCg0KU2VlIGFsc28gc3RvcGlmbm90KCkgaXQgY2hlY2tzIHRoYXQgZWFjaCBhcmd1bWVudCBpcyBUUlVFIGFuZCBwcm9kdWNlcyBhIGduZXJpYyBlcnJvciBtZXNzYWdlIGlmIG5vdC4gDQoNCmBgYHtyfQ0Kd3RfbWVhbiA8LSAgZnVuY3Rpb24oeCx3LCBuYS5ybT1GQUxTRSkgew0KICBzdG9waWZub3QoaXMubG9naWNhbChuYS5ybSksIGxlbmd0aChuYS5ybSkgPT0xKQ0KICBzdG9waWZub3QobGVuZ3RoKHgpID09IGxlbmd0aCh3KSkNCiANCiAgaWYgKG5hLnJtKSB7DQogICAgbWlzcyA8LSAgaXMubmEoeCkgfCBpcy5uYSh3KQ0KICAgIHggPC0gIHhbIW1pc3NdDQogICAgdyA8LSAgd1shbWlzc10NCiAgfQ0KICBzdW0odyp4KS9zdW0oeCkNCn0NCg0Kd3RfbWVhbigxOjYsIE5BKQ0KYGBgDQpOb3RlIHRoYXQgd2hlbiB1c2luZyBzdG9waWZub3QoKSB5b3UgYXNzZXJ0IHdoYXQgc2hvdWxkIGJlIHRydWUgcmF0aGVyIHRoYW4gY2hlY2tpbmcgZm9yIHdoYXQgbWlnaHQgYmUgd3JvbmcuIA0KDQpoMj4gRG90LURvdC1Eb3QoLi4uKSA8L2gyPg0KDQpNYW55IGZ1bmN0aW9ucyBpbiBSIHRha2UgYW4gYXJiaXRyYXJ5IG51bWJlciBvZiBpbnB1dHMuICBIb3cgZG8gdGhlc2UgZnVuY3Rpb25zIHdvcms/IFRoZXkgcmVseSBvbiBhIHNwZWNhaWwgYXJndW1lbnQgOiAuLi4gVGhpcyBzcGVjaWFsIGFyZ3VtZW50IGNhcHR1cmVzIGFueSBudW1iZXIgb2YgYXJndW1lbnRzIHRoYXQgYXJlbid0IG90aGVyd2lzZSBtYXRjaGVkLiANCjwvcD4NCkl0J3MgdXNlZnVsIGJlY2F1c2UgeW91IGNhbiB0aGVuIHNlbmQgdGhvc2UuLi4gb24gdG8gYW5vdGhlciBmdW5jdGlvbi4gVGhpcyBpcyB1c2VmdWwgY2F0Y2ggYWxsIGlmIHlvdXIgZnVuY3Rpb24gcHJpbWFyaWx5IHdyYXBzIGFub3RoZXIgZnVuY3Rpb24uICBGb3IgZXhhbXBsZToNCg0KYGBge3J9DQpjb21tYXMgPC0gIGZ1bmN0aW9uKC4uLikgc3RyaW5ncjo6c3RyX2MoLi4uLCBjb2xsYXBzZSA9ICIsICIpDQpjb21tYXMobGV0dGVyc1sxOjEwXSkNCg0KYGBgDQoNCmBgYHtyfQ0KcnVsZSA8LSAgZnVuY3Rpb24oLi4uLCBwYWQgPSItIikgew0KICB0aXRsZSA8LSAgcGFzdGUwKC4uLikNCiAgd2lkdGggPC0gIGdldE9wdGlvbigid2lkdGgiKSAtIG5jaGFyKHRpdGxlKSAtIDUNCiAgY2F0KHRpdGxlLCAiICIsIHN0cmluZ3I6OnN0cl9kdXAocGFkLHdpZHRoKSwgIlxuIiwgc2VwID0gIiAiKQ0KfQ0KDQpydWxlKCJJbXBvcnRhbnQgb3V0cHV0IGZyb20gd2lsc29uIikNCmBgYA0KDQo8aDM+IExhenkgRXZhbHVhdGlvbiA8L2gzPg0KQXJndW1lbnRzIGluIFIgYXJlIGxhemlseSBldmFsdWF0ZWQuIFRoaXMgbWVhbnMgdGhhdCB0aGV5J3JlIG5vdCBjb21wdXRlZCB1bnRpbCB0aGV5J3JlIG5lZWRlZC4gSWYgdGhleSBhcmUgbmV2ZXIgdXNlZCwgdGhleSBhcmUgbmV2ZXIgY2FsbGVkLiBUaGlzIGlzIGFuIGltcG9ydGFudCBwcm9wZXJ0eSBvZiBSLiANCg0KPGgzPiBFeGVyY2lzZSA8L2gzPg0KV2hhdCBkb2VzIGNvbW1hcyhsZXR0ZXJzLCBjb2xsYXBzZSA9ICItIikgZG8/IFdoeT88L2JyPg0KVGhlIGFyZ3VtZW50IGNvbGxhcHNlIGlzIHBhc3NlZCB0byBzdHJfYyBhcyBwYXJ0IG9mIC4uLiwgc28gaXQgdHJpZXMgdG8gcnVuIHN0cl9jKGxldHRlcnMsIGNvbGxhcHNlID0gIi0iLCBjb2xsYXBzZSA9ICIsICIpLiBDb21iaW5lcyBhbGwgdGhlIGFscGhhYmV0cyBpbnRvIG9uZSBzdHJpbmcuIDwvcD4NCg0KDQpJdCdkIGJlIG5pY2UgaWYgeW91IGNvdWxkIHN1cHBseSBtdWx0aXBsZSBjaGFyYWN0ZXJzIHRvIHRoZSBwYWQgYXJndW1lbnQsIGUuZy4gcnVsZSgiVGl0bGUiLCBwYWQgPSAiLSsiKS4gV2h5IGRvZXNuJ3QgdGhpcyBjdXJyZW50bHkgd29yaz8gSG93IGNvdWxkIHlvdSBmaXggaXQ/IDwvYnI+DQpJdCBkb2VzIG5vdCB3b3JrIGJlY2F1c2UgaXQgZHVwbGljYXRlcyBwYWQgYnkgdGhlIHdpZHRoIG1pbnVzIHRoZSBsZW5ndGggb2YgdGhlIHN0cmluZy4gVGhpcyBpcyBpbXBsaWN0bHkgYXNzdW1pbmcgdGhhdCBwYWQgaXMgb25seSBvbmUgY2hhcmFjdGVyLiBJIGNvdWxkIGFkanVzdCB0aGUgY29kZSB0byBjYWxjdWxhdGUgdGhlIGxlbmd0aCBvZiBwYWQuIFRoZSB0cmlja2llc3QgcGFydCBpcyBoYW5kbGluZyB3aGF0IHRvIGRvIGlmIHdpZHRoIGlzIG5vdCBhIG11bHRpcGxlIG9mIHRoZSBudW1iZXIgb2YgY2hhcmFjdGVycyBvZiBwYWQuIDwvcD4NCg0KYGBge3J9DQpydWxlIDwtICBmdW5jdGlvbiguLi4sIHBhZCA9Ii0rIikgew0KICB0aXRsZSA8LSAgcGFzdGUwKC4uLikNCiAgd2lkdGggPC0gIGdldE9wdGlvbigid2lkdGgiKSAtIG5jaGFyKHRpdGxlKSAtIDUNCiAgY2F0KHRpdGxlLCAiICIsIHN0cmluZ3I6OnN0cl9kdXAocGFkLHdpZHRoKSwgIlxuIiwgc2VwID0gIiAiKQ0KfQ0KDQpydWxlKCJJbXBvcnRhbnQgb3V0cHV0IGZyb20gd2lsc29uIikNCnJ1bGUoIkltcG9ydGFudCBvdXRwdXQgZnJvbSB3aWxzb24iLCBwYWQ9IipYKiIpDQoNCmBgYA0KU28gdGhlIHNvbHV0aW9uIGlzIHRvIGZpZ3VyZSBob3cgbXVjaCB0byBwYWQuDQoNCmBgYHtyfQ0KcnVsZSA8LSAgZnVuY3Rpb24oLi4uLCBwYWQgPSItKyIpIHsNCiAgdGl0bGUgPC0gIHBhc3RlMCguLi4pDQogIHdpZHRoIDwtICBnZXRPcHRpb24oIndpZHRoIikgLSBuY2hhcih0aXRsZSkgLSA1DQogIHBhZGNoYXIgPC0gIG5jaGFyKHBhZCkNCiAgICBjYXQodGl0bGUsICIgIiwgc3RyaW5ncjo6c3RyX2R1cChwYWQsd2lkdGggJS8lIHBhZGNoYXIpLA0KICAgICAgICBzdHJpbmdyOjpzdHJfc3ViKHBhZCwgMSwgd2lkdGggJSUgcGFkY2hhciksICJcbiIsIHNlcCA9ICIgIikNCn0NCg0KcnVsZSgiSW1wb3J0YW50IG91dHB1dCBmcm9tIHdpbHNvbiIpDQpydWxlKCJJbXBvcnRhbnQgb3V0cHV0IGZyb20gd2lsc29uIiwgcGFkPSIqWCoiKQ0KDQpgYGANCg0KV2hhdCBkb2VzIHRoZSB0cmltIGFyZ3VtZW50IHRvIG1lYW4oKSBkbz8gV2hlbiBtaWdodCB5b3UgdXNlIGl0PyA8L2JyPg0KVGhlIHRyaW0gYXJndW1lbnRzIHRyaW1zIGEgZnJhY3Rpb24gb2Ygb2JzZXJ2YXRpb25zIGZyb20gZWFjaCBlbmQgb2YgdGhlIHZlY3RvciAobWVhbmluZyB0aGUgcmFuZ2UpIGJlZm9yZSBjYWxjdWxhdGluZyB0aGUgbWVhbi4gVGhpcyBpcyB1c2VmdWwgZm9yIGNhbGN1bGF0aW5nIGEgbWVhc3VyZSBvZiBjZW50cmFsIHRlbmRhbmN5IHRoYXQgaXMgcm9idXN0IHRvIG91dGxpZXJzLiBNdWNoIGxpa2UgOTV0aCBwZXJjZW50aWxlPyAgPC9wPg0KDQpUaGUgZGVmYXVsdCB2YWx1ZSBmb3IgdGhlIG1ldGhvZCBhcmd1bWVudCB0byBjb3IoKSBpcyBjKCJwZWFyc29uIiwgImtlbmRhbGwiLCAic3BlYXJtYW4iKS4gV2hhdCBkb2VzIHRoYXQgbWVhbj8gV2hhdCB2YWx1ZSBpcyB1c2VkIGJ5IGRlZmF1bHQ/IDwvYnI+DQpJdCBtZWFucyB0aGF0IHRoZSBtZXRob2QgYXJndW1lbnQgY2FuIHRha2Ugb25lIG9mIHRob3NlIHRocmVlIHZhbHVlcy4gVGhlIGZpcnN0IHZhbHVlLCAicGVhcnNvbiIsIGlzIHVzZWQgYnkgZGVmYXVsdC4gPC9wPg0KDQoNCjxoMj4gUmV0dXJuIFZhbHVlcyA8L2gyPg0KDQpGaWd1cmluZyBvdXQgd2hhdCB5b3VyIGZ1bmN0aW9uIHNob3VsZCByZXR1cm4gaXMgdXN1YWxseSBlYXN5LiBUZXJlIGFyZSB0d28gdGhpbmdzIHlvdSBzaG91bGQgb2Nuc2lkZXIgd2hlbiByZXR1cm5pbmcgYSB2YWx1ZSA6IDwvYnI+DQoNCmRvZXMgcmV0dXJuaW5nIGVhcmx5IG1ha2UgeW91ciBmdW5jdGlvbiBlYXNpZXIgb3QgcmVhZD8gPC9icj4NCmNhbiB5b3UgbWFrZSB5b3VyIGZ1bmN0aW9uIHBpcGVhYmxlPyA8L3A+DQoNCjxoMj4gRXhwbGljaXQgUmV0dXJuIHN0YXRlbWVudHMgPC9oMj4NCllvdSBjYW4gY2hvc2UgdG8gcmV0dXJuIGVhcmx5IGJ5IHVzaW5nIHJldHVybigpLiBBIGNvbW1vbiByZWFzb24gdG8gZG8gdGhpcyBpcyBiZWNhdXNlIHRoZSBpbnB1dHMgYXJlIGVtcHR5LiANCg0KYGBge3J9DQpjb21wbGljYXRlZF9mdW5jdGlvbiA8LSAgZnVuY3Rpb24oeCx5LHgpIHsNCiAgaWYgKGxlbmd0aCh4KSA9PSAwIHx8IGxlbmd0aCh5KSA9PSAwKSB7DQogICAgcmV0dXJuKDApDQogIH0NCn0NCmBgYA0KDQpBbm90aGVyIHJlYXNvbiBpcyBiZWNhdXNlIHlvdSBoYXZlIGFuIGlmIHN0YXRlbWVudCB3aXRoIG9uZSBjb21wbGV4IGJsb2NrIGFuZCBvbmUgc2ltcGxlIGJsb2NrLiANCg0KYGBge3J9DQpmIDwtICBmdW5jdGlvbiAoKSB7fQ0KaWYoeCkgew0KICAjIGRvDQogICNzb21ldGhpbmcNCiAgI3RoYXQgDQogICN0YWtlcw0KICAjbWFueSANCiAgI2xpbmVzDQogICMgdG8gDQogICMgZXhwcmVzcw0KfSBlbHNlIHsNCiAgICAjIHJldHVybiBzb21ldGhpbmcgc2hvcnQgDQp9DQp9DQoNCmBgYA0KDQpZT3UgY2FuIHJlLXdyaXRlIGl0IA0KYGBge3J9DQoNCmYgPC0gIGZ1bmN0aW9uKCkgew0KICBpZigheCkgew0KICAgIHJldHVybihzb21ldGhpbmdfc2hvcnQpDQogICAgDQogIH0NCiAgICMgZG8NCiAgI3NvbWV0aGluZw0KICAjdGhhdCANCiAgI3Rha2VzDQogICNtYW55IA0KICAjbGluZXMNCiAgIyB0byANCiAgIyBleHByZXNzDQp9DQp9DQoNCmBgYA0KDQo8aDI+IFdyaXRpbmcgUGlwZWFibGUgRnVuY3Rpb25zIDwvaDI+DQpUaGVyZSBhcmUgdHdvIHR5cGVzIG9mIHBpcGVhYmxlIGZ1bmN0aW9uczogdHJhbnNmb3JtYXRpb24gYW5kIHNpZGUtZWZmZWN0LiA8L3A+DQpJbiB0cmFuc2Zvcm1hdGlvbiBmdW5jdGlvbnMsIHRoZXJlJ3MgYSBjbGVhciBwcmltYXJ5IG9iamVjdCB0aGF0IGlzIHBhc3NlZCBpbiBhcyB0aGUgZmlyc3QgYXJndW1lbnQgYW5kIGEgbW9kaWZpZWQgdmVyc2lvbiBpcyByZXR1cm5lZCBieSB0aGUgZnVuY3Rpb24uIDwvcD4NCnNpZGUtZWZmZWN0IGZ1bmN0aW9ucyBhcmUgcHJpbWFpcnkgY2FsbGVkIHRvIHBlcmZvcm0gYW4gYWN0aW9uLCBsaWtlIGRyYXdpbmcgYSBwbG90IG9yIHNhdmluZyBhIGZpbGUsIG5vdCB0cmFuc2Zvcm1pbmcgdGhlIG9iamVjdC4gDQoNCjxoMj4gRW52aXJvbm1lbnQgPC9oMj4NClRoZSBsYXN0IGNvbXBvbmVudCBvZiBhIGZ1bmN0aW9uIGlzIGl0cyBlbnZpcm9ubWVudC4gVGhpcyBpcyBub3Qgc29tZXRoaW5nIHlvdSBuZWVkIHRvIHVuZGVyc3RhbmQgZGVlcGx5IHdoZW4geW91IGZpcnN0IHN0YXJ0IHdyaXRpbmcgZnVuY3Rpb25zLiBIb3dldmVyIGl0J3MgaW1wb3J0YW50IHRvIGtub3cgYSBsaXR0bGUgYml0IGFib3V0IGVudmlyb25tZW50cyBiZWNhdXNlIHRoZXkgYXJlIGNydWNpYWwgdG8gaG93IGZ1bmN0aW9ucyB3b3JrLiBJZiBhIHZhbHVlIGlzIG5vdCBkZWZpbmVkIGluc2lkZSB0aGUgZnVuY3Rpb24sIFIgd2lsbCBzdGlsbCBmaW5kIGl0IGFzIHZhbGlkIGJlY2F1c2UgaXQgdXNlcyBsZXhpY2FsIHNjb3BpbmcgdG8gZmluZCB0aGUgdmFsdWUgYXNzb2NpYXRlZCB3aXRoIGEgbmFtZS4g