Introduction
This tutorial is a quick introduction to R for programmers of other high-level languages. It shows those features of R that are familiar to most programmers so that they can get started programming right away. It is important to note that the programming approach presented here is generally not the most efficient nor most common approach. We show the “R way” whenever possible and simple enough to explain. But the goal of the tutorial is to get a programmers programming. This is especially useful to students in computer science courses that use R but who are new to R.
R is best suited for data projects: data loading, data transformation, databases, data analysis, data visualization, and data science. There is substantial support for statistical analysis, unsupervised data mining, supervised machine learning, and even interactive dashboards.
Often, programming tasks that take dozens of lines of code in most languages can be written with one statement or one line in R. While R is not the fastest language, for many vector processing and mathematical operations it generally outperforms most other high-level languages, particularly when vectorization hardware is present.
As of 2021, R is one of the top languages to learn and for any kind of data-related work it is critical (along with Python, Scala, and perhaps Go).
The tutorial is geared towards students in information science, data science, and database design. It demonstrates basic syntax in R that are most often used for data processing rather than statistics.
The R Language
R is a procedural language similar to C. It is not object-oriented and does not support objects, classes, inheritance, or polymorphism. It has little support for data encapsulation or abstraction, so no equivalent for class or struct in C/C++ or Java.
Programs is R are scripts. There is no “main function” or similar. R “programs” are collections of R statements that are interpreted when executed interactively. There is no compilation step. R does support reusable third-party code “libraries” in the form of packages.
Working in R
To write “programs” in R you will need Base R which you can download for Linux, MacOS, and Windows from R Project. This is the core language with an interactive console. Programs, or more aptly R scripts, can be built in any text editor (TextEdit, Notepad, vi, Sublime, JEdit, etc.).
Most programming is done with an IDE (Integrated Development Environment). The most common is R Studio downloadable from RStudio. There is a hosted version of R Studio available at rstudio.cloud.
Install R from R Project before installing R Studio.
The tutorial below explains how to get started with R Notebooks:
Execute chunks by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter. The code runs in the order in which the chunks are executed, so non-linear code execution is possible unless you instruct R Studio to run all chunks starting at the first chunk.
Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.
When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).
The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.
Projects in R Studio
Projects are a better way to manage code rather than creating individual R Notebooks, R Scripts, and other code files. Projects allows all files, including data files, to be managed as a single unit, shared, and version controlled using services such as git and GitHub.
The tutorial below demonstrates how to create a project in R Studio and add files to the project.
R Code Chunks
While you can also write R Scripts, we will concentrate on how to write R “programs” by composing an R Notebook in R Studio. This is the most common way to use R in data related projects where reproducability is paramount. Programs in R run from start to end. Each chunk should be a step in your analysis or data project. Name your code chunks, so you can quickly navigate to them.
In the chunk below, the variable cars passed to the built-in Base R function plot
is one of the dozens of “built-in” data frames; a data frame being data arranged in rows and columns similar to a spreadsheet or CSV file.
Note that you call a function by using the function’s name followed by the arguments you wish to pass to the function. Of course, you need to follow the definition of the function. Many functions are simply “built-in” while others come from packages that you need to explicitly load into your program.
Note that there is no semicolon at the end of a line.
Expressions
R can be directly used to solve simple or complex mathematical expressions.
# [1] in the above answer indicates the index of your results.
# R always shows the result with index for each row.
((2^3)*5)-1
## [1] 39
# sqrt and exp are built-in functions in R for finding Square root and exponential respectively.
sqrt(4)* exp(2)
## [1] 14.77811
Variables
Variables are not declared in R. When you use a variable for the first time, it is defined and the data type is based on what value you assign to the variable. So, R uses dynamic typing, which also means that when you assign a value of a different type to a previously used variable, it changes type. You can find the type of a variable using the functions typeof
, class
, and mode
.
R supports the usual data types: integer, double, Boolean, and string. It also has pre-defined complex types, including vector, data frame, array, list, and matrix. There is also a Date data type and a few others.
The absence of a value is indicated in R using NA rather than nil, null or NULL. An NA means that there is no value for an integer, character, logical value, or numeric. On the other hand, NULL means that an object is “empty”, i.e. a reference to an object that does not exist.
Values are assigned to variables using either = or <-, the latter being more common but the former more familiar to programmers of other common languages.
a = 10 # defines an integer
d = 9.9 # defines a double
s = "some text" # define a string of characters
g <- 'also text' # single quotes are the same as double quotes
Text can be enclosed in single or double quotes. Which you use depends on preference or if you need to nest quotes.
To echo the value of a variable to the console, either use the variable on a line by itself or use the function print
. If you need to echo multiple values, combine them with paste0
.
## [1] 123.99
print(paste0("Value of d = ", d))
## [1] "Value of d = 123.99"
Missing Values: NA vs NULL
In R, NA
and NULL
are used to represent different types of missing data, and they serve different purposes:
- NA (Not Available):
NA
is used to represent missing values in vectors, lists, or data frames. It acts as a placeholder for an element that does not have a value but is expected to have one.
NA
can be of any data type like numeric, character, or logical, meaning you can have NA_integer_
, NA_real_
, NA_complex_
, and NA_character_
.
- Operations involving
NA
generally result in NA
. For example, 5 + NA
results in NA
.
- A value of
NA
is a value and is counted, e.g., length(c(3,NA,4))
has the value 3.
- NULL:
NULL
is used to represent the absence of a value or no value at all. It is typically used to denote that a variable is empty or uninitialized.
NULL
is often used in list or data frame operations to remove elements or indicate that an element is absent.
NULL
has a different behavior in operations compared to NA
. For instance, adding NULL
to another object or concatenating it generally results in the other object unchanged. For example, c(1, 2, NULL)
results in c(1, 2)
.
Essentially, NA
is used when an element of the data exists but its value is missing, whereas NULL
is used when the data itself does not exist.
Many functions that return an object such as a data frame would return NULL if they could not generate the object.
In R, you can test for NA
and NULL
using specific functions designed for this purpose. Here’s how you can do it:
- Testing for NA:
- Testing for NULL:
These functions are useful in various programming scenarios, such as conditional execution of code depending on the presence of actual data, and handling missing values in data analysis and transformation tasks.
Naming Identifiers
The naming of identifiers, i.e., variable and function names, are the same as in most other languages, except that you can use the period as a valid identifier character. That can be confusing to Java and C++ programmers as they are used to using . as a method or object property access operator. In R, it’s just another character like a or _.
a.val = 10.5 # legal
a_val = 10.5 # legal
aVal1 = 10.5 # legal
a$val = 10.5 # not quite legal, $ is reserved for data frames
The last one is a bit tricky. a$val
will actually create a list object. Let’s just not use it unless you are accessing columns in a data frame, but that’s for another tutorial.
The rules for naming an identifier (variable, function, or package name) for an object are as follows:
identifiers are case-sensitive and cannot contain spaces or special characters such as #, %, $, @, * , &, ^, !, ~
an identifier must start with a letter, but may contain any combination of letters and digits thereafter
special characters dot (.) and underscore (_) are allowed
The dot (.) is a regular character in R and that can be confusing as other languages (e.g., Java) use dot as an operator to designate property or method access, e.g, in Java x.val means that you are accessing the val property of the object x.
Some examples of legal variable names are: df, df2, df.txns, and df_all2017. These are some illegal variable names: 2df (cannot start with a digit), rs$all (cannot contain a $; the $ is used to access columns in a dataframe), rs# (only . and _ are allowed in addition to digits and letters).
It is considered good programming practice to give identifiers a sensible name that hints as to what is stored in the variable rather than using random name like x, val, or i33. Instead use anItem or annualTotal.
Identifiers should be named consistently. Many programmers use one of two styles:
- underscores, e.g., interest_rate
- camelCase, e.g., squareRoot, graphData, currentWorkingDirectory
Note that R is case sensitive which means that R treats the identifiers AP and ap as different objects.
As a side note, files may also be case sensitive but that depends on the operating system. MacOS and Linux are case sensitive, while Windows is case aware but not case sensitive. For example, on MacOS and Linux there is a difference between “AirPassengers.txt” and “airpassengers.txt” while on Windows there is not. SQL is also not case sensitive. It is a best practice to assume case sensitivity.
Expressions
As a language with roots in statistical analysis, R supports most mathematical operators, including many that are not supported through operators in most other languages.
Precedence is like other languages and can be (and should be) specified explicitly using parentheses.
a <- 99
b = a + 20 # addition
b = a - 20 # subtraction
b = a * 20 # multiplication
b = a / 20 # division -- b is now "double"
b = a ^ 3 # exponentiation
b = a %% 2 # modulus (mod)
b = a %/% 2 # integer division (div)
b = (a / 2) ^ 5 # force order of evaluation
There are also numerous built-in functions for mathematics and statistics, but those are beyond this tutorial on base syntax. For the sake of completeness, an example is shown below that calculates the z-score of a vector of numbers. In R, a vector is similar to an array; it contains primitives values (integer, double, Boolean, or string). Notice the automatic vector calculation even without a loop.
v = c(1,4,6,2,8,1,0,3) # vector of integers
m = mean(v) # mean of values in v
s = sd(v) # standard deviation
z = (m - v) / s # z-score of each value in v
print(round(z,2)) # print rounded values
## [1] 0.77 -0.32 -1.05 0.41 -1.77 0.77 1.14 0.05
Boolean Variables and Logical Expressions
Boolean values are TRUE and FALSE; or T and F, respectively.
m = FALSE
w = TRUE
q = (m & w) | (!m & !w)
print(paste0("q is ", q))
## [1] "q is FALSE"
The Boolean operators are & for AND, | for OR, and ! for NOT. These operators perform logical operations on Boolean variables or vector of Boolean elements. Exclusive or (xor) is performed with the function xor, e.g., xor(a,b)
.
There are long forms && and || for programming control-flow and generally preferred for if statements.
Flow Control
The control flow statements provided by R are similar to those found in most other programming languages: loops and if statements.
Loops
R supports the three common types of loops: a counting loop (for), a top-tested loop (while), and a bottom-tested loop (repeat).
Counting Loop: for
The counting loop is actually more like an iterator in R, although it can be set up to mimic the behavior of a typical loop in C/C++, Java, etc., that loops from a low value to a high value. See the examples below.
n = 5
for (i in 1:n) {
print(i)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
In the example above, i is the loop variable and it takes on the values in the vector 1:5, i.e., 1, 2, 3, 4, 5
To count down, it would be 5:1 or n:1 in the above code example.
Like other languages, R uses curly braces to enclose the body of the loop, i.e., the statements that are executed repeatedly, once for each value of i. Of course, the loop counter can be any properly named variable.
The example below accesses a vector using positional access, e.g., v[1] accesses the first element. Note that vector, data frames, lists, and arrays are indexed starting at 1 and not 0 as in C-based languages.
v = rnorm(5) # vector of five random numbers
for (i in 1:length(v))
{
v[i] = v[i] ^ 2
}
print(v)
## [1] 0.003630603 2.683820944 1.535107835 2.112020092 1.549035610
While you can use loops you actually do not need them. If you apply a mathematical operation to a vector, R automatically applies them to each element, but loops might be more natural in the beginning.
# note that you actually do not need loops in R
# this also squares each element in the vector v
v = v ^ 2
As already stated, looping in R is actually iteration over a set: a set of numbers as above or a set of any kind of primitive object, e.g., strings. Note that in the code below, k takes on each value in the vector over which the loop iterates.
s = c("one","two","three","four")
for (k in s) {
print(k)
}
## [1] "one"
## [1] "two"
## [1] "three"
## [1] "four"
We could have written this using a non-iterator approach as well, which is more like what you’d do in C. Note that length
returns the number of elements in a vector, i.e., its “length”.
s = c("one","two","three","four")
for (j in 1:length(s)) {
print(s[j])
}
## [1] "one"
## [1] "two"
## [1] "three"
## [1] "four"
Iteration Continuation
Continuation of the loop to the next iteration and forgoing processing the remainder of the current iteration is done with next in R, similar to continue in C-based languages such as Java.
In the example below we are already reaching ahead to the if statement. The code fragment echoes only odd number. Of course, you could have done this differently by just iterating over the even numbers from one to twenty, but that would not have allowed us to share this example for using next.
for (i in 1:10) {
if (!i %% 2) {
next
}
print(i)
}
## [1] 1
## [1] 3
## [1] 5
## [1] 7
## [1] 9
But, just to show you how to iterate over just odd number, we can use the seq()
function which generates a sequence of numbers in steps. No if and no break needed – much simpler.
for (i in seq(1, 10, 2)) {
print(i)
}
## [1] 1
## [1] 3
## [1] 5
## [1] 7
## [1] 9
Or, more explicitly by specifying the parameters by name.
for (i in seq(from = 1, to = 10, by = 2)) {
print(i)
}
## [1] 1
## [1] 3
## [1] 5
## [1] 7
## [1] 9
To stop the execution of the rest of a loop and to move immediately to the next statement after the loop is done with break in R which is identical to C, C++, Java, etc. In the code below we will find the position of the first occurrence of some number; x is the number we are looking for in v and p is the found position.
v = c(1, 3, 5, 7, 1, 9, 4)
x = 9
p = 0
for (i in 1:length(v)) {
if (v[i] == x) {
p = i
break
}
}
print(p)
## [1] 6
To be clear, there is a quicker and more efficient way to do this using the which
function. This function does not exist is other languages and it shows the power of R. Also, using which
is much faster. Incidentally, the code below finds all occurrences of x in v. Note, once again, the use of a vector variable to refer to all elements.
v = c(1, 3, 5, 7, 1, 9, 4)
x = 9
p = which(v == x)
print(p)
## [1] 6
Conditional Loops: while and repeat
Like many other languages, R supports top and bottom tested loops. In the example below we repeatedly ask for a number from the user and only exit the loop if the number is 42. Of course, the example really should be done with a bottom tested loop.
response <- as.integer(readline(prompt="Enter a number: "))
while (response != 42) {
print("Sorry, not correct");
response <- as.integer(readline(prompt="Enter a number: "));
}
In the above example, we are using two new functions. readline
is used to read input from the console, while as.integer
coerces (or casts in C/C++ terminology) the input to an integer.
The code is above is not very efficient or elegant as it has the code for the user prompt twice. It is really better to ask first and then check the condition: we need a bottom-tested loop.
Let’s look at the same example, but with a repeat loop that runs until a condition is reached and the loop is explicitly exited with break. So, if you want to run the code at least once, use repeat; if the code is run zero or more times, use while. The repeat loop is identical to the do or do while loop found in many other programming languages.
repeat {
response <- as.integer(readline(prompt="Enter a number: "));
if (response == 42) {
break
}
print("Sorry, not correct");
}
There is no equivalent in R for the use of an infinite for loop as in C and C++, e.g., for(;;){ // do something indefinitely }
that runs until a condition is reached and then uses break to exit the loop. The repeat loop construct is used instead. There is no testing of a condition in the repeat loop, so there’s no equivalent to the C-like construct do … while.
Alternation: if
The use of if to selectively execute a block of code based on a condition is the same in R as it is in other languages. We already saw an example and the same example is below:
for (i in 1:10) {
if (!i %% 2) {
next
}
print(i)
}
## [1] 1
## [1] 3
## [1] 5
## [1] 7
## [1] 9
Note that the condition is in parenthesis and that the block executed if the condition is true is in curly braces. This is identical to C, C++, Java, Python, etc.
A more complex example is shown below that squares all numbers in a vector that are less than 5 and cubes them if greater than 5. Not a useful example but one that allows us to show the use of if-else.
for (i in 1:10) {
if (i < 5) {
print(i^2)
} else {
print(i^3)
}
}
## [1] 1
## [1] 4
## [1] 9
## [1] 16
## [1] 125
## [1] 216
## [1] 343
## [1] 512
## [1] 729
## [1] 1000
Functions
Code organization and reuse in R is done using functions. All objects are free methods and are not bound to an object or a class like in Java or C++. It’s the same way as in Python or C.
All functions in R can return a value, although they do not have to. So, R does not distinguish between functions and procedures and there is no void return type as in C, C++, and Java.
Defining a Function
The generic template for defining a function is:
function_name <- function(arg_1, arg_2, ...) {
Function body
}
The example below defines a function called findSmallest() which takes a vector of positive integers as an argument and returns the smallest element in the vector. While it can be solved in several ways, we will show a design that uses loops and should be familiar to programmers of most other languages.
Note that we are using the predefined value Inf with is the largest representable integer. There is also -Inf that is the smallest representable integer.
findSmallest = function(v)
{
s = Inf
for (i in 1:length(v))
{
if (v[i] < s) {
s = v[i]
}
}
return (s)
}
While you can use = to define a function, you should really get used to using the more common <- syntax. So, let’s try again:
findSmallest <- function(v)
{
s = Inf
for (i in 1:length(v))
{
if (v[i] < s) {
s = v[i]
}
}
return (s)
}
Just to be clear, in practice you would use the min()
function to find the smallest element rather writing it yourself.
While there are several ways to return a value from a function, the way that is most congruent with other languages is the use of the return statement.
Note that the type of return value and the type of arguments are not declared. R uses a lazy evaluation mechanism and no type checking is performed until run-time.
Calling a Function
To call a function, you would invoke it with its name and its required arguments.
x = c(3,1,9,7,3,6)
w = findSmallest(x)
print(w)
## [1] 1
Function Parameters
If a function takes several arguments you generally pass them in the order declared; the approach that is used by all other languages. However, in R you can pass the arguments in any order as long as you specify the name of the argument.
Argument matching is a bit different in R compared to other languages. Firstly, R does all argument checking at run-time. Secondly, while arguments can be matched positionally like in other languages, arguments can also be matched by parameter name – a syntax not supported by most other languages.
For example, the built-in function seq
generates a sequence of numbers and returns those numbers in a vector. The definition of the function is as follows: seq(to, from, by, length.out, along.with)
.
Here are examples of using it. Note that by, length.out, and along.with have default values and are therefore optional.
v = seq(1, 10, 2) # integers from 1 to 10 in increments of 2
w = seq(1, 5) # integers from 1 to 5 (by default in increments of 1)
# pass arguments in a different order but specify by name
w = seq(from = 5, by = -0.5, to = 1)
R also supports variable numbers of arguments but that is beyond the scope of this tutorial.
Default Arguments
R functions can have default values for arguments which are then optional when the function is called. When the argument is missing, then the default value is passed. In the example below, the start argument is the position at which the search for the smallest element will start.
findSmallest <- function(v, start = 1)
{
s = Inf
for (i in start:length(v))
{
if (v[i] < s) {
s = v[i]
}
}
return (s)
}
x = c(3,1,9,7,3,6)
w = findSmallest(x, 3)
print(w)
## [1] 3
w = findSmallest(x)
print(w)
## [1] 1
Local Variables
As in most other programming languages, R functions can define local variables that are not known outside the scope of the function. The scope boundaries in R are like other languages: a block enclosed in curly braces.
In the example below, local.var is local to the function and thus is not visible outside of the function. The code below produces the error: “Error in print(local.var) : object ‘local.var’ not found”.
findSmallest <- function(v, start = 1)
{
local.var = Inf
for (i in start:length(v))
{
if (v[i] < local.var) {
local.var = v[i]
}
}
return (local.var)
}
x = c(3,1,9,7,3,6)
w = findSmallest(x, 3)
# we cannot echo or access the local variable "s"
print(local.var)
Recursion
R functions can be called recursively. The example below calculates factorial using recursion rather than a loop.
fac <- function(x)
{
if (x == 1)
return (1)
else
return (x * fac(x-1))
}
print(fac(8))
## [1] 40320
If it hasn’t been obvious yet, just like in other languages, the placement of curly braces makes no difference. For single statement blocks, the curly braces can be omitted.
The parenthesis around the value for return are required.
As an exercise, try writing the above function to calculate factorial using a loop.
Type Coercion
Type coercion (or casting in C/C++ terminology) is done with type conversion functions in R and not through operators like in C, C++, and Java. The example below shows the most common type conversion functions. Note that in some situation you will lose information, just like in other languages.
s = "12.4" # string (character)
i = as.integer(s) # convert to integer: 12
d = as.numeric(s) # convert to double: 12.3
w = "$12.3" # additional characters
k = as.integer(w) # cannot be converted due to $
## Warning: NAs introduced by coercion
If a coercion is not successful the result in NA which indicates a null or missing value.
Vectors
Let’s talk more about vectors. In R, a vector is similar to a list or array in other programming languages. It is a collection of elements of the same basic type: numeric, character, or Boolean. A list in R is a collection of mixed data types. This tutorial applies to vectors only.
There are no specific packages required for these functions.
Creating a Vector
The code below creates an artificial vector of random integers for use in the tutorial. In practice, vectors are generally columns in data frames which are frequently the result of reading data from a CSV file or a database.
# vector of 50 random integers between 0 and 10
# set the seed for the random number generator to ensure same
# sequence of random numbers every time the code is run
set.seed(98788)
v <- round(runif(50, min = 0, max = 10),0)
# arguments do not have to be passed in the order that they are
# declared in the function definition as long as the names of the
# arguments are specified
v <- round(runif(n = 50, max = 10, min = 1),0)
v <- round(runif(max = 10, min = 1, n = 50),0)
print(v)
## [1] 9 6 9 1 9 8 10 2 6 9 8 7 6 1 7 10 4 3 5 9 7 8 4 9 7
## [26] 4 6 5 1 4 4 2 7 9 9 6 3 2 3 7 8 6 3 9 4 10 7 1 4 2
Accessing Elements in a Vector
Elements are accessed positionally, although in R, the access index can be a vector of integers in which case all elements at those positions are retrieved. Positions are numbered from 1 to the number of elements in a vector. The number of elements (or length) of a vector can be obtained using the length()
function.
In the example below, note that n:m
generates a vector of integers from n to m, inclusive. The seq()
generates a sequence of integers at an interval.
# access a single element at position 3
v[3]
# access element 10 through 15
v[10:15]
# access the last element
v[length(v)]
# access every other element
v[seq(from = 1, to = length(v), by = 2)]
# access specific elements at positions 2, 11, 19, and 28
i <- c(2,11,19,28)
v[i]
Testing Predicate Expressions
It is possible in R to apply a predicate expression to every element in a vector. This generates a “Boolean vector” of TRUE/FALSE values that indicate which element matches the predicate expression (TRUE) and which doesn’t (FALSE).
Predicate expressions are built with logical operators (<, >, <=, >=, ==, !=)
## [1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
## [13] FALSE TRUE FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE TRUE FALSE
## [25] FALSE TRUE FALSE FALSE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE
## [37] TRUE TRUE TRUE FALSE FALSE FALSE TRUE FALSE TRUE FALSE FALSE TRUE
## [49] TRUE TRUE
## [1] FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
## [13] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [25] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [37] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE
## [49] FALSE FALSE
## [1] FALSE TRUE FALSE TRUE FALSE FALSE FALSE TRUE TRUE FALSE FALSE TRUE
## [13] TRUE TRUE TRUE FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE FALSE
## [25] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE FALSE FALSE TRUE
## [37] FALSE TRUE FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE TRUE TRUE
## [49] TRUE TRUE
## [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [13] TRUE TRUE TRUE TRUE TRUE TRUE FALSE TRUE TRUE TRUE TRUE TRUE
## [25] TRUE TRUE TRUE FALSE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [37] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [49] TRUE TRUE
## [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [13] FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
## [25] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [37] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [49] FALSE FALSE
Finding Matches
The which()
function returns the positions that are TRUE in a Boolean vector.
# returns positions of vector that matches predicate expression
which(v != 5)
## [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 20 21 22 23 24 25 26
## [26] 27 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
# count the number of matches
length(which(v != 0))
## [1] 50
p <- which(v < 5)
print (v[p])
## [1] 1 2 1 4 3 4 4 1 4 4 2 3 2 3 3 4 1 4 2
# or combine
x <- v[which(v < 5)]
print (x)
## [1] 1 2 1 4 3 4 4 1 4 4 2 3 2 3 3 4 1 4 2
# find all that are not in vector
not.x <- v[-which(v < 5)]
print (x)
## [1] 1 2 1 4 3 4 4 1 4 4 2 3 2 3 3 4 1 4 2
Determining Any Matches
To determine if there are any matches, i.e., at least one element in a vector matches the predicate expression, use the any()
function. The function any()
returns TRUE if there’s at least one match, FALSE otherwise.
## [1] TRUE
Dealing with Missing Values
Missing values in R are generally encoded with the special value NA. NA is not a number, not a character or text, and not a Boolean. Consequently, using == or != to check if a value is NA does not work and results in an error. You must use the function is.na()
to check if a value is NA. This is similar to NULL in SQL and many programming languages.
# copy the vector of random numbers and then randomly
# remove 6 values, i.e., set them to NA
v.na <- v
v.na[round(runif(6, min = 1, max = length(v.na)), 0)] = NA
print(v.na)
## [1] 9 6 9 1 9 8 10 2 NA 9 8 7 NA 1 7 10 4 3 5 9 7 NA 4 NA 7
## [26] 4 6 5 1 4 4 2 7 9 NA 6 3 2 3 7 NA 6 3 9 4 10 7 1 4 2
Many of the functions in R do not work when a value in a vector is NA. Doing so results in a value of NA.
# cannot add values containing NA
sum(v.na)
## [1] NA
# when applying operators, NA remains NA
v.na + 5
## [1] 14 11 14 6 14 13 15 7 NA 14 13 12 NA 6 12 15 9 8 10 14 12 NA 9 NA 12
## [26] 9 11 10 6 9 9 7 12 14 NA 11 8 7 8 12 NA 11 8 14 9 15 12 6 9 7
Some functions have a parameter that allows you to direct a function to ignore NA values. Check the documentation of functions before using them to see what parameters they support. Use ?sum to view the documentation of the sum function.
Accessing Rows, Columns, and Elements (Cells) of a Data Frame
Data frames are very similar to tables in relational databases and spreadsheets. They have rows and columns and the intersection of a row and column is a cell (or element). The order of access is row followed by column, e.g., the third element in the fourth row of the data frame mtcars
is mtcars[4,3]
. Note that this is reversed from the way Excel and other spreadsheets work.
The example code below uses the built-in data frame mtcars. You can find out more about its structure using str(mtcars)
or displaying the first few rows with head(mtcars)
.
## 'data.frame': 32 obs. of 11 variables:
## $ mpg : num 21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
## $ cyl : num 6 6 4 6 8 6 8 4 4 6 ...
## $ disp: num 160 160 108 258 360 ...
## $ hp : num 110 110 93 110 175 105 245 62 95 123 ...
## $ drat: num 3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
## $ wt : num 2.62 2.88 2.32 3.21 3.44 ...
## $ qsec: num 16.5 17 18.6 19.4 17 ...
## $ vs : num 0 0 1 1 0 1 0 1 1 1 ...
## $ am : num 1 1 1 0 0 0 0 0 0 0 ...
## $ gear: num 4 4 4 3 3 3 3 4 4 4 ...
## $ carb: num 4 4 1 1 2 1 4 2 2 4 ...
## mpg cyl disp hp drat wt qsec vs am gear carb
## Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
## Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
## Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
v <- mtcars[4,3]
x = mtcars[4,3]
print(paste0("v = ",v," and x = ",x))
## [1] "v = 258 and x = 258"
Leaving out a dimension (row or column) accesses the entire row or column. The resultant is a data frame with a single row.
Often the values must be converted to a vector data type. Conversions of variables from one type to another is done with the family of as.xxxx
functions, e.g., as.vector
, as.numeric
, or as.factor
. Vectors can contain numeric or character data but all elements must be of the same type. In R, a list is similar to a vector but it may contain a mix of elements. A matrix is similar to a data frame but it can only contain numbers and it can have more than two dimensions.
Some functions expect data frames, some vectors, some lists. You need to read the documentation of a function to find out. Furthermore, some functions will automatically convert a variable from one type to the one it requires.
You can also access a column in data frame by its column name. For an entire column you either use the columns position or its name: df[,column]
or df$columnName
.
# all of row 4; the result is a data frame
r <- mtcars[4,]
sum(r)
## [1] 426.135
## [1] 108
mtcars[c(1,4)] # columns 1 and 4 as a new dataframe
## mpg hp
## Mazda RX4 21.0 110
## Mazda RX4 Wag 21.0 110
## Datsun 710 22.8 93
## Hornet 4 Drive 21.4 110
## Hornet Sportabout 18.7 175
## Valiant 18.1 105
## Duster 360 14.3 245
## Merc 240D 24.4 62
## Merc 230 22.8 95
## Merc 280 19.2 123
## Merc 280C 17.8 123
## Merc 450SE 16.4 180
## Merc 450SL 17.3 180
## Merc 450SLC 15.2 180
## Cadillac Fleetwood 10.4 205
## Lincoln Continental 10.4 215
## Chrysler Imperial 14.7 230
## Fiat 128 32.4 66
## Honda Civic 30.4 52
## Toyota Corolla 33.9 65
## Toyota Corona 21.5 97
## Dodge Challenger 15.5 150
## AMC Javelin 15.2 150
## Camaro Z28 13.3 245
## Pontiac Firebird 19.2 175
## Fiat X1-9 27.3 66
## Porsche 914-2 26.0 91
## Lotus Europa 30.4 113
## Ford Pantera L 15.8 264
## Ferrari Dino 19.7 175
## Maserati Bora 15.0 335
## Volvo 142E 21.4 109
mtcars[,2] # all of column 2
## [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4
mtcars[5:7,] # rows 5 to 7 as a new dataframe
## mpg cyl disp hp drat wt qsec vs am gear carb
## Hornet Sportabout 18.7 8 360 175 3.15 3.44 17.02 0 0 3 2
## Valiant 18.1 6 225 105 2.76 3.46 20.22 1 0 3 1
## Duster 360 14.3 8 360 245 3.21 3.57 15.84 0 0 3 4
mtcars$cyl # column named "cyl"
## [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4
mtcars$cyl[2] # 2nd row in the column "cyl"
## [1] 6
mtcars$cyl[3:9] # rows 3 to 9 for column "cyl" as a vector
## [1] 4 6 8 6 8 4 4
## [1] 20.09062
Adding and Removing Columns from a Data Frame
To add a new column, you simply “access” the column or use a new name for the column. Note in the example below that you can operate on entire columns (as vectors) and the operation is applied to each pair of values in the two vectors in the operation. This is much more efficient than using loops as is necessary in other programming languages.
# copy the data frame mtcars to a new data frame df
df <- mtcars
# create a new column "dispcyl" which is the displacement per cylinder
df$dispcyl <- df$disp / df$cyl
head(df)
## mpg cyl disp hp drat wt qsec vs am gear carb dispcyl
## Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4 26.66667
## Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4 26.66667
## Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1 27.00000
## Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 43.00000
## Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 45.00000
## Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1 37.50000
Create a New Data Frame
Data frames are created in various ways: use the <code<>data.frame function, load a CSV file, execute a SQL query, or as a result of many package functions.
Load a Data Frame from CSV
Quick note: Capitalization in path and file names does not matter in Windows, but does matter on MacOS and Linux. Furthermore, note that even in Windows the path delimiter is a forward slash / and not the usual backwards slash \. The \ is an “escape” character and used to inject non-printable characters into a string (text), e.g., “This string contains”quotes”.” which would be written in R as “this string contains \”quotes\“.”
The parameter header = F
instructs read.csv
not to interpret the first line as header labels. Of course, if there are no labels, then you need to define your own.
Aside from CSV files, R can also load a number of other file format using various packages, including XML, Excel, SPSS, MatLab, among many others.
df <- read.csv(file = "customertxndata.csv", header = F)
head(df)
## V1 V2 V3 V4 V5
## 1 7 0 Android Male 0.0000
## 2 20 1 iOS <NA> 576.8668
## 3 22 1 iOS Female 850.0000
## 4 24 2 iOS Female 1050.0000
## 5 1 0 Android Male 0.0000
## 6 13 1 Android Male 460.0000
df <- read.csv(file = "customertxndata.csv",
header = F,
col.names = c("numVisits","NumTxn","OS","Gender","TotSp"))
head(df)
## numVisits NumTxn OS Gender TotSp
## 1 7 0 Android Male 0.0000
## 2 20 1 iOS <NA> 576.8668
## 3 22 1 iOS Female 850.0000
## 4 24 2 iOS Female 1050.0000
## 5 1 0 Android Male 0.0000
## 6 13 1 Android Male 460.0000
Note that the value of the ‘Male’ column in the first row is NA which is the way that R indicates a missing data value. It is not 0 or an empty string, it is unknown. So, statistical functions and algebraic operations would result in an NA as well.
Strings vs Factors
The factor data type encodes categorical data, e.g., the value of a variable is one of a fixed value set. Many statistical functions in R require categorical variables to be of type factor. However, often, during data processing, we need the actual text rather than having it encoded as a factor (which is actually stored in R as an integer for efficiency). So, when reading a CSV file you need to decide if you want text columns to be character strings or factors by setting the stringsAsFactors
parameter.
You may use either F
and T
or FALSE
and TRUE
.
df <- read.csv(file = "customertxndata.csv",
header = F,
stringsAsFactors = FALSE,
col.names = c("numVisits","NumTxn","OS","Gender","TotSp"))
head(df)
## numVisits NumTxn OS Gender TotSp
## 1 7 0 Android Male 0.0000
## 2 20 1 iOS <NA> 576.8668
## 3 22 1 iOS Female 850.0000
## 4 24 2 iOS Female 1050.0000
## 5 1 0 Android Male 0.0000
## 6 13 1 Android Male 460.0000
Create a new Data Frame
The code below creates a new data frame from column vectors. Notice how the column names are the names of the vectors. A new vector is created with the c
function, e.g., v <- c(3,5,1,9)
.
df1 <- data.frame(state = c('Arizona','Georgia', 'New York','Indiana','Washington','Texas'),
code = as.factor(c('AZ','GA','NY','IN','WA','TX')),
score = c(62,47,55,74,31,85))
head(df1)
## state code score
## 1 Arizona AZ 62
## 2 Georgia GA 47
## 3 New York NY 55
## 4 Indiana IN 74
## 5 Washington WA 31
## 6 Texas TX 85
Search Data Frames
There are two important functions for “searching” data frames: which
and any
. The code below uses the built-in Orange data frame which contains measurements of orange trees. It has three columns: the tree, the age of the tree (days since 1968/12/31), and circumference (in mm).
which
## Tree age circumference
## 1 1 118 30
## 2 1 484 58
## 3 1 664 87
## 4 1 1004 115
## 5 1 1231 120
## 6 1 1372 142
# find all rows where the circumference is more than 200mm
rs <- which(df$circumference > 200)
# display all rows where the circumference is more than 200mm
df[rs,]
## Tree age circumference
## 13 2 1372 203
## 14 2 1582 203
## 27 4 1372 209
## 28 4 1582 214
# compound conditions are possible with & (and), | (or), and ! (not)
rs2 <- which(df$circumference > 200 & df$age < 1500)
rs3 <- which(df$circumference < 200 | !(df$age < 1500))
rs4 <- which(df$circumference > 400 | df$age > 1500)
rs2
## [1] 13 27
## [1] 1 2 3 4 5 6 7 8 9 10 11 12 14 15 16 17 18 19 20 21 22 23 24 25 26
## [26] 28 29 30 31 32 33 34 35
## [1] 7 14 21 28 35
## [1] 1582
## [1] 894.8788
In the above example rs <- which(df$circumference > 200)
finds all rows in the data frame df where circumference > 200. The rows are saved in rs.
any
The any
function returns \(TRUE\) or \(FALSE\) depending on whether any column (or row) in the dataframe satisfies a Boolean expression.
# is there any tree with age > 2000?
any(df$age > 25)
## [1] TRUE
Memory Management
R is similar to Python and other interpreted languages in terms of memory management. Objects and variables remain in memory until you restart R or explicitly delete them. This can sometimes cause conflicts during development. Adding this to the start of an R script or an R Notebook ensures that the program runs with an empty memory environment. This is critical for languages like R and Python, but is not needed for programming languages that run in separate processes such as Java and C++ programs.
Use the code below to find and then delete all objects, and reclaim memory. The function ls()
lists all objects (variables) by name, while the rm()
removes one or more objects from memory. Finally, the function gc()
runs the garbage collector and returns freed memory to the usable memory pool for the process in which R is running.
rm(list = ls(all.names = TRUE))
gc()
Of course, rather than deleting all objects as in the code chunk above, you may wish to release large objects or unused objects selectively by their name, e.g., rm(“objName”)
.
Install Packages on Demand
To make your code portable and reproducible, install packages within your code:
# packages needed in R program
packages <- c("stringr", "RSQLite")
# install packages not yet installed
installed_packages <- packages %in% rownames(installed.packages())
if (any(installed_packages == FALSE)) {
install.packages(packages[!installed_packages])
}
# load all packages by applying 'library' function
invisible(lapply(packages, library, character.only = TRUE))
Conclusion
As you saw, R is not a difficult language to learn as it is similar to other languages and for most language constructs that you are familiar with, there is an equivalent. But it is important that you go beyond this tutorial and learn the “R way” of programming using vectorized operations.
References
No references.
Errata
None collected yet. Let us know.
LS0tCnRpdGxlOiAiUXVpY2sgR3VpZGUgdG8gUiBGb3IgUHJvZ3JhbW1lcnMiCnBhcmFtczoKICBjYXRlZ29yeTogNgogIG51bWJlcjogMTA0CiAgdGltZTogNjAKICBsZXZlbDogYmVnaW5uZXIKICB0YWdzOiAicixwcmltZXIsbG9vcHMiCiAgZGVzY3JpcHRpb246ICJBIHF1aWNrIGd1aWRlIGZvciBwcm9ncmFtbWVycyB0cmFuc2l0aW9uaW5nIHRvIFIgZnJvbSAKICAgICAgICAgICAgICAgIEMsIEMrKywgSmF2YSwgSmF2YVNjcmlwdCwgUHl0aG9uLCAKICAgICAgICAgICAgICAgIGFuZCBvdGhlciBoaWdoLWxldmVsIGxhbmd1YWdlcy4gRXhwbGFpbnMga2V5IGNvbnRyb2wKICAgICAgICAgICAgICAgIHN0cnVjdHVyZXMgYW5kIHByb2dyYW1taW5nIHBhcmFkaWdtcyBmb3IgUi4iCmRhdGU6ICI8c21hbGw+YHIgU3lzLkRhdGUoKWA8L3NtYWxsPiIKYXV0aG9yOiAiPHNtYWxsPk1hcnRpbiBTY2hlZGxiYXVlcjwvc21hbGw+IgplbWFpbDogIm0uc2NoZWRsYmF1ZXJAbmV1LmVkdSIKYWZmaWxpdGF0aW9uOiAiTm9ydGhlYXN0ZXJuIFVuaXZlcnNpdHkiCm91dHB1dDogCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRoZW1lOiBzcGFjZWxhYgogICAgaGlnaGxpZ2h0OiB0YW5nbwotLS0KCi0tLQp0aXRsZTogIjxzbWFsbD5gciBwYXJhbXMkY2F0ZWdvcnlgLmByIHBhcmFtcyRudW1iZXJgPC9zbWFsbD48YnIvPjxzcGFuIHN0eWxlPSdjb2xvcjogIzJFNDA1MzsgZm9udC1zaXplOiAwLjllbSc+YHIgcm1hcmtkb3duOjptZXRhZGF0YSR0aXRsZWA8L3NwYW4+IgotLS0KCmBgYHtyIGNvZGU9eGZ1bjo6cmVhZF91dGY4KHBhc3RlMChoZXJlOjpoZXJlKCksJy9SL19pbnNlcnQyREIuUicpKSwgaW5jbHVkZSA9IEZBTFNFfQpgYGAKCiMjIEludHJvZHVjdGlvbgoKVGhpcyB0dXRvcmlhbCBpcyBhIHF1aWNrIGludHJvZHVjdGlvbiB0byBSIGZvciBwcm9ncmFtbWVycyBvZiBvdGhlciBoaWdoLWxldmVsIGxhbmd1YWdlcy4gSXQgc2hvd3MgdGhvc2UgZmVhdHVyZXMgb2YgUiB0aGF0IGFyZSBmYW1pbGlhciB0byBtb3N0IHByb2dyYW1tZXJzIHNvIHRoYXQgdGhleSBjYW4gZ2V0IHN0YXJ0ZWQgcHJvZ3JhbW1pbmcgcmlnaHQgYXdheS4gSXQgaXMgaW1wb3J0YW50IHRvIG5vdGUgdGhhdCB0aGUgcHJvZ3JhbW1pbmcgYXBwcm9hY2ggcHJlc2VudGVkIGhlcmUgaXMgZ2VuZXJhbGx5IG5vdCB0aGUgbW9zdCBlZmZpY2llbnQgbm9yIG1vc3QgY29tbW9uIGFwcHJvYWNoLiBXZSBzaG93IHRoZSAiUiB3YXkiIHdoZW5ldmVyIHBvc3NpYmxlIGFuZCBzaW1wbGUgZW5vdWdoIHRvIGV4cGxhaW4uIEJ1dCB0aGUgZ29hbCBvZiB0aGUgdHV0b3JpYWwgaXMgdG8gZ2V0IGEgcHJvZ3JhbW1lcnMgcHJvZ3JhbW1pbmcuIFRoaXMgaXMgZXNwZWNpYWxseSB1c2VmdWwgdG8gc3R1ZGVudHMgaW4gY29tcHV0ZXIgc2NpZW5jZSBjb3Vyc2VzIHRoYXQgdXNlIFIgYnV0IHdobyBhcmUgbmV3IHRvIFIuCgpSIGlzIGJlc3Qgc3VpdGVkIGZvciBkYXRhIHByb2plY3RzOiBkYXRhIGxvYWRpbmcsIGRhdGEgdHJhbnNmb3JtYXRpb24sIGRhdGFiYXNlcywgZGF0YSBhbmFseXNpcywgZGF0YSB2aXN1YWxpemF0aW9uLCBhbmQgZGF0YSBzY2llbmNlLiBUaGVyZSBpcyBzdWJzdGFudGlhbCBzdXBwb3J0IGZvciBzdGF0aXN0aWNhbCBhbmFseXNpcywgdW5zdXBlcnZpc2VkIGRhdGEgbWluaW5nLCBzdXBlcnZpc2VkIG1hY2hpbmUgbGVhcm5pbmcsIGFuZCBldmVuIGludGVyYWN0aXZlIGRhc2hib2FyZHMuCgpPZnRlbiwgcHJvZ3JhbW1pbmcgdGFza3MgdGhhdCB0YWtlIGRvemVucyBvZiBsaW5lcyBvZiBjb2RlIGluIG1vc3QgbGFuZ3VhZ2VzIGNhbiBiZSB3cml0dGVuIHdpdGggb25lIHN0YXRlbWVudCBvciBvbmUgbGluZSBpbiBSLiBXaGlsZSBSIGlzIG5vdCB0aGUgZmFzdGVzdCBsYW5ndWFnZSwgZm9yIG1hbnkgdmVjdG9yIHByb2Nlc3NpbmcgYW5kIG1hdGhlbWF0aWNhbCBvcGVyYXRpb25zIGl0IGdlbmVyYWxseSBvdXRwZXJmb3JtcyBtb3N0IG90aGVyIGhpZ2gtbGV2ZWwgbGFuZ3VhZ2VzLCBwYXJ0aWN1bGFybHkgd2hlbiB2ZWN0b3JpemF0aW9uIGhhcmR3YXJlIGlzIHByZXNlbnQuCgpBcyBvZiAyMDIxLCBSIGlzIG9uZSBvZiB0aGUgdG9wIGxhbmd1YWdlcyB0byBsZWFybiBhbmQgZm9yIGFueSBraW5kIG9mIGRhdGEtcmVsYXRlZCB3b3JrIGl0IGlzIGNyaXRpY2FsIChhbG9uZyB3aXRoIFB5dGhvbiwgU2NhbGEsIGFuZCBwZXJoYXBzIEdvKS4KClRoZSB0dXRvcmlhbCBpcyBnZWFyZWQgdG93YXJkcyBzdHVkZW50cyBpbiBpbmZvcm1hdGlvbiBzY2llbmNlLCBkYXRhIHNjaWVuY2UsIGFuZCBkYXRhYmFzZSBkZXNpZ24uIEl0IGRlbW9uc3RyYXRlcyBiYXNpYyBzeW50YXggaW4gUiB0aGF0IGFyZSBtb3N0IG9mdGVuIHVzZWQgZm9yIGRhdGEgcHJvY2Vzc2luZyByYXRoZXIgdGhhbiBzdGF0aXN0aWNzLgoKIyMgVGhlIFIgTGFuZ3VhZ2UKClIgaXMgYSBwcm9jZWR1cmFsIGxhbmd1YWdlIHNpbWlsYXIgdG8gQy4gSXQgaXMgbm90IG9iamVjdC1vcmllbnRlZCBhbmQgZG9lcyBub3Qgc3VwcG9ydCBvYmplY3RzLCBjbGFzc2VzLCBpbmhlcml0YW5jZSwgb3IgcG9seW1vcnBoaXNtLiBJdCBoYXMgbGl0dGxlIHN1cHBvcnQgZm9yIGRhdGEgZW5jYXBzdWxhdGlvbiBvciBhYnN0cmFjdGlvbiwgc28gbm8gZXF1aXZhbGVudCBmb3IgKmNsYXNzKiBvciAqc3RydWN0KiBpbiBDL0MrKyBvciBKYXZhLgoKUHJvZ3JhbXMgaXMgUiBhcmUgc2NyaXB0cy4gVGhlcmUgaXMgbm8gIm1haW4gZnVuY3Rpb24iIG9yIHNpbWlsYXIuIFIgInByb2dyYW1zIiBhcmUgY29sbGVjdGlvbnMgb2YgUiBzdGF0ZW1lbnRzIHRoYXQgYXJlIGludGVycHJldGVkIHdoZW4gZXhlY3V0ZWQgaW50ZXJhY3RpdmVseS4gVGhlcmUgaXMgbm8gY29tcGlsYXRpb24gc3RlcC4gUiBkb2VzIHN1cHBvcnQgcmV1c2FibGUgdGhpcmQtcGFydHkgY29kZSAibGlicmFyaWVzIiBpbiB0aGUgZm9ybSBvZiAqcGFja2FnZXMqLgoKIyMgV29ya2luZyBpbiBSCgpUbyB3cml0ZSAicHJvZ3JhbXMiIGluIFIgeW91IHdpbGwgbmVlZCBCYXNlIFIgd2hpY2ggeW91IGNhbiBkb3dubG9hZCBmb3IgTGludXgsIE1hY09TLCBhbmQgV2luZG93cyBmcm9tIFtSIFByb2plY3RdKGh0dHBzOi8vd3d3LnItcHJvamVjdC5vcmcvKS4gVGhpcyBpcyB0aGUgY29yZSBsYW5ndWFnZSB3aXRoIGFuIGludGVyYWN0aXZlIGNvbnNvbGUuIFByb2dyYW1zLCBvciBtb3JlIGFwdGx5IFIgc2NyaXB0cywgY2FuIGJlIGJ1aWx0IGluIGFueSB0ZXh0IGVkaXRvciAoVGV4dEVkaXQsIE5vdGVwYWQsIHZpLCBTdWJsaW1lLCBKRWRpdCwgZXRjLikuCgpNb3N0IHByb2dyYW1taW5nIGlzIGRvbmUgd2l0aCBhbiBJREUgKEludGVncmF0ZWQgRGV2ZWxvcG1lbnQgRW52aXJvbm1lbnQpLiBUaGUgbW9zdCBjb21tb24gaXMgUiBTdHVkaW8gZG93bmxvYWRhYmxlIGZyb20gW1JTdHVkaW9dKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3Byb2R1Y3RzL3JzdHVkaW8vZG93bmxvYWQvKS4gVGhlcmUgaXMgYSBob3N0ZWQgdmVyc2lvbiBvZiBSIFN0dWRpbyBhdmFpbGFibGUgYXQgW3JzdHVkaW8uY2xvdWRdKGh0dHA6Ly9yc3R1ZGlvLmNsb3VkKS4KCj4gSW5zdGFsbCBSIGZyb20gW1IgUHJvamVjdF0oaHR0cHM6Ly9jbG91ZC5yLXByb2plY3Qub3JnLykgKipiZWZvcmUqKiBpbnN0YWxsaW5nIFtSIFN0dWRpb10oaHR0cHM6Ly9yc3R1ZGlvLmNvbS9wcm9kdWN0cy9yc3R1ZGlvL2Rvd25sb2FkLykuCgpUaGUgdHV0b3JpYWwgYmVsb3cgZXhwbGFpbnMgaG93IHRvIGdldCBzdGFydGVkIHdpdGggUiBOb3RlYm9va3M6Cgo8aWZyYW1lIHNyYz0iaHR0cHM6Ly9ub3J0aGVhc3Rlcm4uaG9zdGVkLnBhbm9wdG8uY29tL1Bhbm9wdG8vUGFnZXMvRW1iZWQuYXNweD9pZD04MGMyY2YwMi0wMGQyLTQyN2MtOGZjZC1hYmUwMDBmMDZmMGQmYW1wO2F1dG9wbGF5PWZhbHNlJmFtcDtvZmZlcnZpZXdlcj10cnVlJmFtcDtzaG93dGl0bGU9dHJ1ZSZhbXA7c2hvd2JyYW5kPWZhbHNlJmFtcDtjYXB0aW9ucz1mYWxzZSZhbXA7aW50ZXJhY3Rpdml0eT1hbGwiIGhlaWdodD0iMTgwIiB3aWR0aD0iMzIwIiBzdHlsZT0iYm9yZGVyOiAxcHggc29saWQgIzQ2NDY0NjsiIGFsbG93ZnVsbHNjcmVlbiBhbGxvdz0iYXV0b3BsYXkiIGRhdGEtZXh0ZXJuYWw9IjEiPgoKPC9pZnJhbWU+CgpFeGVjdXRlIGNodW5rcyBieSBjbGlja2luZyB0aGUgKlJ1biogYnV0dG9uIHdpdGhpbiB0aGUgY2h1bmsgb3IgYnkgcGxhY2luZyB5b3VyIGN1cnNvciBpbnNpZGUgaXQgYW5kIHByZXNzaW5nICpDdHJsK1NoaWZ0K0VudGVyKi4gVGhlIGNvZGUgcnVucyBpbiB0aGUgb3JkZXIgaW4gd2hpY2ggdGhlIGNodW5rcyBhcmUgZXhlY3V0ZWQsIHNvIG5vbi1saW5lYXIgY29kZSBleGVjdXRpb24gaXMgcG9zc2libGUgdW5sZXNzIHlvdSBpbnN0cnVjdCBSIFN0dWRpbyB0byBydW4gYWxsIGNodW5rcyBzdGFydGluZyBhdCB0aGUgZmlyc3QgY2h1bmsuCgpBZGQgYSBuZXcgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpJbnNlcnQgQ2h1bmsqIGJ1dHRvbiBvbiB0aGUgdG9vbGJhciBvciBieSBwcmVzc2luZyAqQ3RybCtBbHQrSSouCgpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkN0cmwrU2hpZnQrSyogdG8gcHJldmlldyB0aGUgSFRNTCBmaWxlKS4KClRoZSBwcmV2aWV3IHNob3dzIHlvdSBhIHJlbmRlcmVkIEhUTUwgY29weSBvZiB0aGUgY29udGVudHMgb2YgdGhlIGVkaXRvci4gQ29uc2VxdWVudGx5LCB1bmxpa2UgKktuaXQqLCAqUHJldmlldyogZG9lcyBub3QgcnVuIGFueSBSIGNvZGUgY2h1bmtzLiBJbnN0ZWFkLCB0aGUgb3V0cHV0IG9mIHRoZSBjaHVuayB3aGVuIGl0IHdhcyBsYXN0IHJ1biBpbiB0aGUgZWRpdG9yIGlzIGRpc3BsYXllZC4KCiMjIyBQcm9qZWN0cyBpbiBSIFN0dWRpbwoKUHJvamVjdHMgYXJlIGEgYmV0dGVyIHdheSB0byBtYW5hZ2UgY29kZSByYXRoZXIgdGhhbiBjcmVhdGluZyBpbmRpdmlkdWFsIFIgTm90ZWJvb2tzLCBSIFNjcmlwdHMsIGFuZCBvdGhlciBjb2RlIGZpbGVzLiBQcm9qZWN0cyBhbGxvd3MgYWxsIGZpbGVzLCBpbmNsdWRpbmcgZGF0YSBmaWxlcywgdG8gYmUgbWFuYWdlZCBhcyBhIHNpbmdsZSB1bml0LCBzaGFyZWQsIGFuZCB2ZXJzaW9uIGNvbnRyb2xsZWQgdXNpbmcgc2VydmljZXMgc3VjaCBhcyAqZ2l0KiBhbmQgKkdpdEh1YiouCgpUaGUgdHV0b3JpYWwgYmVsb3cgZGVtb25zdHJhdGVzIGhvdyB0byBjcmVhdGUgYSBwcm9qZWN0IGluIFIgU3R1ZGlvIGFuZCBhZGQgZmlsZXMgdG8gdGhlIHByb2plY3QuCgpgYGB7PWh0bWx9CjxpZnJhbWUgc3JjPSJodHRwczovL3BsYXllci52aW1lby5jb20vdmlkZW8vNjA3NDUxMzc0P2g9MzA1NmU3MzA3MyIgd2lkdGg9IjQ4MCIgaGVpZ2h0PSIyNzAiIGZyYW1lYm9yZGVyPSIwIiBhbGxvdz0iYXV0b3BsYXk7IGZ1bGxzY3JlZW47IHBpY3R1cmUtaW4tcGljdHVyZSIgYWxsb3dmdWxsc2NyZWVuIGRhdGEtZXh0ZXJuYWw9IjEiPjwvaWZyYW1lPgpgYGAKIyMgUiBDb2RlIENodW5rcwoKV2hpbGUgeW91IGNhbiBhbHNvIHdyaXRlIFIgU2NyaXB0cywgd2Ugd2lsbCBjb25jZW50cmF0ZSBvbiBob3cgdG8gd3JpdGUgUiAicHJvZ3JhbXMiIGJ5IGNvbXBvc2luZyBhbiBSIE5vdGVib29rIGluIFIgU3R1ZGlvLiBUaGlzIGlzIHRoZSBtb3N0IGNvbW1vbiB3YXkgdG8gdXNlIFIgaW4gZGF0YSByZWxhdGVkIHByb2plY3RzIHdoZXJlIHJlcHJvZHVjYWJpbGl0eSBpcyBwYXJhbW91bnQuIFByb2dyYW1zIGluIFIgcnVuIGZyb20gc3RhcnQgdG8gZW5kLiBFYWNoIGNodW5rIHNob3VsZCBiZSBhIHN0ZXAgaW4geW91ciBhbmFseXNpcyBvciBkYXRhIHByb2plY3QuIE5hbWUgeW91ciBjb2RlIGNodW5rcywgc28geW91IGNhbiBxdWlja2x5IG5hdmlnYXRlIHRvIHRoZW0uCgpJbiB0aGUgY2h1bmsgYmVsb3csIHRoZSB2YXJpYWJsZSAqY2FycyogcGFzc2VkIHRvIHRoZSBidWlsdC1pbiBCYXNlIFIgZnVuY3Rpb24gPGNvZGU+cGxvdDwvY29kZT4gaXMgb25lIG9mIHRoZSBkb3plbnMgb2YgImJ1aWx0LWluIiBkYXRhIGZyYW1lczsgYSBkYXRhIGZyYW1lIGJlaW5nIGRhdGEgYXJyYW5nZWQgaW4gcm93cyBhbmQgY29sdW1ucyBzaW1pbGFyIHRvIGEgc3ByZWFkc2hlZXQgb3IgQ1NWIGZpbGUuCgpOb3RlIHRoYXQgeW91IGNhbGwgYSBmdW5jdGlvbiBieSB1c2luZyB0aGUgZnVuY3Rpb24ncyBuYW1lIGZvbGxvd2VkIGJ5IHRoZSBhcmd1bWVudHMgeW91IHdpc2ggdG8gcGFzcyB0byB0aGUgZnVuY3Rpb24uIE9mIGNvdXJzZSwgeW91IG5lZWQgdG8gZm9sbG93IHRoZSBkZWZpbml0aW9uIG9mIHRoZSBmdW5jdGlvbi4gTWFueSBmdW5jdGlvbnMgYXJlIHNpbXBseSAiYnVpbHQtaW4iIHdoaWxlIG90aGVycyBjb21lIGZyb20gcGFja2FnZXMgdGhhdCB5b3UgbmVlZCB0byBleHBsaWNpdGx5IGxvYWQgaW50byB5b3VyIHByb2dyYW0uCgpOb3RlIHRoYXQgdGhlcmUgaXMgbm8gc2VtaWNvbG9uIGF0IHRoZSBlbmQgb2YgYSBsaW5lLgoKYGBge3IgbmFtZWRDaHVua30KcGxvdChjYXJzKQpgYGAKCiMjIyBFeHByZXNzaW9ucwoKUiBjYW4gYmUgZGlyZWN0bHkgdXNlZCB0byBzb2x2ZSBzaW1wbGUgb3IgY29tcGxleCBtYXRoZW1hdGljYWwgZXhwcmVzc2lvbnMuCgpgYGB7cn0KIyBbMV0gaW4gdGhlIGFib3ZlIGFuc3dlciBpbmRpY2F0ZXMgdGhlIGluZGV4IG9mIHlvdXIgcmVzdWx0cy4KIyBSIGFsd2F5cyBzaG93cyB0aGUgcmVzdWx0IHdpdGggaW5kZXggZm9yIGVhY2ggcm93LgoKKCgyXjMpKjUpLTEKYGBgCgpgYGB7cn0KIyBzcXJ0IGFuZCBleHAgYXJlIGJ1aWx0LWluIGZ1bmN0aW9ucyBpbiBSIGZvciBmaW5kaW5nIFNxdWFyZSByb290IGFuZCBleHBvbmVudGlhbCByZXNwZWN0aXZlbHkuCgpzcXJ0KDQpKiBleHAoMikKYGBgCgojIyBWYXJpYWJsZXMKClZhcmlhYmxlcyBhcmUgbm90IGRlY2xhcmVkIGluIFIuIFdoZW4geW91IHVzZSBhIHZhcmlhYmxlIGZvciB0aGUgZmlyc3QgdGltZSwgaXQgaXMgZGVmaW5lZCBhbmQgdGhlIGRhdGEgdHlwZSBpcyBiYXNlZCBvbiB3aGF0IHZhbHVlIHlvdSBhc3NpZ24gdG8gdGhlIHZhcmlhYmxlLiBTbywgUiB1c2VzIGR5bmFtaWMgdHlwaW5nLCB3aGljaCBhbHNvIG1lYW5zIHRoYXQgd2hlbiB5b3UgYXNzaWduIGEgdmFsdWUgb2YgYSBkaWZmZXJlbnQgdHlwZSB0byBhIHByZXZpb3VzbHkgdXNlZCB2YXJpYWJsZSwgaXQgY2hhbmdlcyB0eXBlLiBZb3UgY2FuIGZpbmQgdGhlIHR5cGUgb2YgYSB2YXJpYWJsZSB1c2luZyB0aGUgZnVuY3Rpb25zIDxjb2RlPnR5cGVvZjwvY29kZT4sIDxjb2RlPmNsYXNzPC9jb2RlPiwgYW5kIDxjb2RlPm1vZGU8L2NvZGU+LgoKUiBzdXBwb3J0cyB0aGUgdXN1YWwgZGF0YSB0eXBlczogaW50ZWdlciwgZG91YmxlLCBCb29sZWFuLCBhbmQgc3RyaW5nLiBJdCBhbHNvIGhhcyBwcmUtZGVmaW5lZCBjb21wbGV4IHR5cGVzLCBpbmNsdWRpbmcgdmVjdG9yLCBkYXRhIGZyYW1lLCBhcnJheSwgbGlzdCwgYW5kIG1hdHJpeC4gVGhlcmUgaXMgYWxzbyBhICpEYXRlKiBkYXRhIHR5cGUgYW5kIGEgZmV3IG90aGVycy4KClRoZSBhYnNlbmNlIG9mIGEgdmFsdWUgaXMgaW5kaWNhdGVkIGluIFIgdXNpbmcgKk5BKiByYXRoZXIgdGhhbiAqbmlsKiwgKm51bGwqIG9yICpOVUxMKi4gQW4gKk5BKiBtZWFucyB0aGF0IHRoZXJlIGlzIG5vIHZhbHVlIGZvciBhbiBpbnRlZ2VyLCBjaGFyYWN0ZXIsIGxvZ2ljYWwgdmFsdWUsIG9yIG51bWVyaWMuIE9uIHRoZSBvdGhlciBoYW5kLCAqTlVMTCogbWVhbnMgdGhhdCBhbiBvYmplY3QgaXMgImVtcHR5IiwgKmkuZS4qIGEgcmVmZXJlbmNlIHRvIGFuIG9iamVjdCB0aGF0IGRvZXMgbm90IGV4aXN0LgoKVmFsdWVzIGFyZSBhc3NpZ25lZCB0byB2YXJpYWJsZXMgdXNpbmcgZWl0aGVyICo9KiBvciAqXDwtKiwgdGhlIGxhdHRlciBiZWluZyBtb3JlIGNvbW1vbiBidXQgdGhlIGZvcm1lciBtb3JlIGZhbWlsaWFyIHRvIHByb2dyYW1tZXJzIG9mIG90aGVyIGNvbW1vbiBsYW5ndWFnZXMuCgpgYGB7ciB2YXJpYWJsZXMsIGV2YWw9RkFMU0V9CmEgPSAxMCAgICAgICAgICAgICAgICAgICAgICAgIyBkZWZpbmVzIGFuIGludGVnZXIKZCA9IDkuOSAgICAgICAgICAgICAgICAgICAgICAjIGRlZmluZXMgYSBkb3VibGUKcyA9ICJzb21lIHRleHQiICAgICAgICAgICAgICAjIGRlZmluZSBhIHN0cmluZyBvZiBjaGFyYWN0ZXJzCmcgPC0gJ2Fsc28gdGV4dCcgICAgICAgICAgICAgIyBzaW5nbGUgcXVvdGVzIGFyZSB0aGUgc2FtZSBhcyBkb3VibGUgcXVvdGVzCmBgYAoKVGV4dCBjYW4gYmUgZW5jbG9zZWQgaW4gc2luZ2xlIG9yIGRvdWJsZSBxdW90ZXMuIFdoaWNoIHlvdSB1c2UgZGVwZW5kcyBvbiBwcmVmZXJlbmNlIG9yIGlmIHlvdSBuZWVkIHRvIG5lc3QgcXVvdGVzLgoKYGBge3IgdGV4dFdpdGhRdW90ZXMsIGV2YWw9RkFMU0V9CnogPSAiSXQncyBhIHF1b3RlLiIKYGBgCgpUbyBlY2hvIHRoZSB2YWx1ZSBvZiBhIHZhcmlhYmxlIHRvIHRoZSBjb25zb2xlLCBlaXRoZXIgdXNlIHRoZSB2YXJpYWJsZSBvbiBhIGxpbmUgYnkgaXRzZWxmIG9yIHVzZSB0aGUgZnVuY3Rpb24gPGNvZGU+cHJpbnQ8L2NvZGU+LiBJZiB5b3UgbmVlZCB0byBlY2hvIG11bHRpcGxlIHZhbHVlcywgY29tYmluZSB0aGVtIHdpdGggPGNvZGU+cGFzdGUwPC9jb2RlPi4KCmBgYHtyIHByaW50VmFyc30KZCA8LSAxMjMuOTkKZApwcmludChwYXN0ZTAoIlZhbHVlIG9mIGQgPSAiLCBkKSkKYGBgCgojIyBNaXNzaW5nIFZhbHVlczogTkEgdnMgTlVMTAoKSW4gUiwgYE5BYCBhbmQgYE5VTExgIGFyZSB1c2VkIHRvIHJlcHJlc2VudCBkaWZmZXJlbnQgdHlwZXMgb2YgbWlzc2luZyBkYXRhLCBhbmQgdGhleSBzZXJ2ZSBkaWZmZXJlbnQgcHVycG9zZXM6CgoxLiAgKipOQSAoTm90IEF2YWlsYWJsZSkqKjoKICAgIC0gICBgTkFgIGlzIHVzZWQgdG8gcmVwcmVzZW50IG1pc3NpbmcgdmFsdWVzIGluIHZlY3RvcnMsIGxpc3RzLCBvciBkYXRhIGZyYW1lcy4gSXQgYWN0cyBhcyBhIHBsYWNlaG9sZGVyIGZvciBhbiBlbGVtZW50IHRoYXQgZG9lcyBub3QgaGF2ZSBhIHZhbHVlIGJ1dCBpcyBleHBlY3RlZCB0byBoYXZlIG9uZS4KICAgIC0gICBgTkFgIGNhbiBiZSBvZiBhbnkgZGF0YSB0eXBlIGxpa2UgbnVtZXJpYywgY2hhcmFjdGVyLCBvciBsb2dpY2FsLCBtZWFuaW5nIHlvdSBjYW4gaGF2ZSBgTkFfaW50ZWdlcl9gLCBgTkFfcmVhbF9gLCBgTkFfY29tcGxleF9gLCBhbmQgYE5BX2NoYXJhY3Rlcl9gLgogICAgLSAgIE9wZXJhdGlvbnMgaW52b2x2aW5nIGBOQWAgZ2VuZXJhbGx5IHJlc3VsdCBpbiBgTkFgLiBGb3IgZXhhbXBsZSwgYDUgKyBOQWAgcmVzdWx0cyBpbiBgTkFgLgogICAgLSAgIEEgdmFsdWUgb2YgYE5BYCBpcyBhIHZhbHVlIGFuZCBpcyBjb3VudGVkLCAqZS5nLiosIGBsZW5ndGgoYygzLE5BLDQpKWAgaGFzIHRoZSB2YWx1ZSAzLgoyLiAgKipOVUxMKio6CiAgICAtICAgYE5VTExgIGlzIHVzZWQgdG8gcmVwcmVzZW50IHRoZSBhYnNlbmNlIG9mIGEgdmFsdWUgb3Igbm8gdmFsdWUgYXQgYWxsLiBJdCBpcyB0eXBpY2FsbHkgdXNlZCB0byBkZW5vdGUgdGhhdCBhIHZhcmlhYmxlIGlzIGVtcHR5IG9yIHVuaW5pdGlhbGl6ZWQuCiAgICAtICAgYE5VTExgIGlzIG9mdGVuIHVzZWQgaW4gbGlzdCBvciBkYXRhIGZyYW1lIG9wZXJhdGlvbnMgdG8gcmVtb3ZlIGVsZW1lbnRzIG9yIGluZGljYXRlIHRoYXQgYW4gZWxlbWVudCBpcyBhYnNlbnQuCiAgICAtICAgYE5VTExgIGhhcyBhIGRpZmZlcmVudCBiZWhhdmlvciBpbiBvcGVyYXRpb25zIGNvbXBhcmVkIHRvIGBOQWAuIEZvciBpbnN0YW5jZSwgYWRkaW5nIGBOVUxMYCB0byBhbm90aGVyIG9iamVjdCBvciBjb25jYXRlbmF0aW5nIGl0IGdlbmVyYWxseSByZXN1bHRzIGluIHRoZSBvdGhlciBvYmplY3QgdW5jaGFuZ2VkLiBGb3IgZXhhbXBsZSwgYGMoMSwgMiwgTlVMTClgIHJlc3VsdHMgaW4gYGMoMSwgMilgLgoKRXNzZW50aWFsbHksIGBOQWAgaXMgdXNlZCB3aGVuIGFuIGVsZW1lbnQgb2YgdGhlIGRhdGEgZXhpc3RzIGJ1dCBpdHMgdmFsdWUgaXMgbWlzc2luZywgd2hlcmVhcyBgTlVMTGAgaXMgdXNlZCB3aGVuIHRoZSBkYXRhIGl0c2VsZiBkb2VzIG5vdCBleGlzdC4KCk1hbnkgZnVuY3Rpb25zIHRoYXQgcmV0dXJuIGFuIG9iamVjdCBzdWNoIGFzIGEgZGF0YSBmcmFtZSB3b3VsZCByZXR1cm4gKk5VTEwqIGlmIHRoZXkgY291bGQgbm90IGdlbmVyYXRlIHRoZSBvYmplY3QuCgpJbiBSLCB5b3UgY2FuIHRlc3QgZm9yIGBOQWAgYW5kIGBOVUxMYCB1c2luZyBzcGVjaWZpYyBmdW5jdGlvbnMgZGVzaWduZWQgZm9yIHRoaXMgcHVycG9zZS4gSGVyZeKAmXMgaG93IHlvdSBjYW4gZG8gaXQ6CgoxLiAgKipUZXN0aW5nIGZvciBOQSoqOgogICAgLSAgIFVzZSB0aGUgYGlzLm5hKClgIGZ1bmN0aW9uLCB3aGljaCBjaGVja3MgZm9yIGBOQWAgdmFsdWVzIGluIGFuIG9iamVjdC4gSXQgcmV0dXJucyBhIGxvZ2ljYWwgdmVjdG9yIG9mIHRoZSBzYW1lIHNpemUgYXMgdGhlIGlucHV0LCB3aXRoIGBUUlVFYCBmb3IgZWxlbWVudHMgdGhhdCBhcmUgYE5BYCBhbmQgYEZBTFNFYCBmb3IgdGhvc2UgdGhhdCBhcmUgbm90LgoKICAgIC0gICBFeGFtcGxlOgoKICAgICAgICBgYGAgcgogICAgICAgIHZlYyA8LSBjKDEsIE5BLCAzLCBOQSwgNSkKICAgICAgICBpcy5uYSh2ZWMpCiAgICAgICAgIyBPdXRwdXQ6IEZBTFNFICBUUlVFIEZBTFNFICBUUlVFIEZBTFNFCiAgICAgICAgYGBgCjIuICAqKlRlc3RpbmcgZm9yIE5VTEwqKjoKICAgIC0gICBVc2UgdGhlIGBpcy5udWxsKClgIGZ1bmN0aW9uLCB3aGljaCBjaGVja3MgaWYgYW4gb2JqZWN0IGlzIGBOVUxMYC4gSXQgcmV0dXJucyBgVFJVRWAgaWYgdGhlIG9iamVjdCBpcyBgTlVMTGAgYW5kIGBGQUxTRWAgb3RoZXJ3aXNlLgoKICAgIC0gICBFeGFtcGxlOgoKICAgICAgICBgYGAgcgogICAgICAgIHggPC0gTlVMTAogICAgICAgIGlzLm51bGwoeCkKICAgICAgICAjIE91dHB1dDogVFJVRQogICAgICAgIGBgYAoKVGhlc2UgZnVuY3Rpb25zIGFyZSB1c2VmdWwgaW4gdmFyaW91cyBwcm9ncmFtbWluZyBzY2VuYXJpb3MsIHN1Y2ggYXMgY29uZGl0aW9uYWwgZXhlY3V0aW9uIG9mIGNvZGUgZGVwZW5kaW5nIG9uIHRoZSBwcmVzZW5jZSBvZiBhY3R1YWwgZGF0YSwgYW5kIGhhbmRsaW5nIG1pc3NpbmcgdmFsdWVzIGluIGRhdGEgYW5hbHlzaXMgYW5kIHRyYW5zZm9ybWF0aW9uIHRhc2tzLgoKIyMgTmFtaW5nIElkZW50aWZpZXJzCgpUaGUgbmFtaW5nIG9mIGlkZW50aWZpZXJzLCAqaS5lLiosIHZhcmlhYmxlIGFuZCBmdW5jdGlvbiBuYW1lcywgYXJlIHRoZSBzYW1lIGFzIGluIG1vc3Qgb3RoZXIgbGFuZ3VhZ2VzLCBleGNlcHQgdGhhdCB5b3UgY2FuIHVzZSB0aGUgcGVyaW9kIGFzIGEgdmFsaWQgaWRlbnRpZmllciBjaGFyYWN0ZXIuIFRoYXQgY2FuIGJlIGNvbmZ1c2luZyB0byBKYXZhIGFuZCBDKysgcHJvZ3JhbW1lcnMgYXMgdGhleSBhcmUgdXNlZCB0byB1c2luZyAqLiogYXMgYSBtZXRob2Qgb3Igb2JqZWN0IHByb3BlcnR5IGFjY2VzcyBvcGVyYXRvci4gSW4gUiwgaXQncyBqdXN0IGFub3RoZXIgY2hhcmFjdGVyIGxpa2UgKmEqIG9yICpcXyouCgpgYGB7ciBpZGVudGlmaWVycywgZWNobz1UUlVFLCBldmFsPUZBTFNFfQphLnZhbCA9IDEwLjUgICAgICAgICAgIyBsZWdhbAphX3ZhbCA9IDEwLjUgICAgICAgICAgIyBsZWdhbAphVmFsMSA9IDEwLjUgICAgICAgICAgIyBsZWdhbAphJHZhbCA9IDEwLjUgICAgICAgICAgIyBub3QgcXVpdGUgbGVnYWwsICQgaXMgcmVzZXJ2ZWQgZm9yIGRhdGEgZnJhbWVzCmBgYAoKVGhlIGxhc3Qgb25lIGlzIGEgYml0IHRyaWNreS4gPGNvZGU+YVwkdmFsPC9jb2RlPiB3aWxsIGFjdHVhbGx5IGNyZWF0ZSBhIGxpc3Qgb2JqZWN0LiBMZXQncyBqdXN0IG5vdCB1c2UgaXQgdW5sZXNzIHlvdSBhcmUgYWNjZXNzaW5nIGNvbHVtbnMgaW4gYSBkYXRhIGZyYW1lLCBidXQgdGhhdCdzIGZvciBhbm90aGVyIHR1dG9yaWFsLgoKVGhlIHJ1bGVzIGZvciBuYW1pbmcgYW4gaWRlbnRpZmllciAodmFyaWFibGUsIGZ1bmN0aW9uLCBvciBwYWNrYWdlIG5hbWUpIGZvciBhbiBvYmplY3QgYXJlIGFzIGZvbGxvd3M6CgotICAgaWRlbnRpZmllcnMgYXJlIGNhc2Utc2Vuc2l0aXZlIGFuZCBjYW5ub3QgY29udGFpbiBzcGFjZXMgb3Igc3BlY2lhbCBjaGFyYWN0ZXJzIHN1Y2ggYXMgIywgJSwgXCQsIFxALCBcKiAsICYsIFxeLCAhLCBcfgoKLSAgIGFuIGlkZW50aWZpZXIgbXVzdCBzdGFydCB3aXRoIGEgbGV0dGVyLCBidXQgbWF5IGNvbnRhaW4gYW55IGNvbWJpbmF0aW9uIG9mIGxldHRlcnMgYW5kIGRpZ2l0cyB0aGVyZWFmdGVyCgotICAgc3BlY2lhbCBjaGFyYWN0ZXJzIGRvdCAoLikgYW5kIHVuZGVyc2NvcmUgKFxfKSBhcmUgYWxsb3dlZAoKVGhlIGRvdCAoLikgaXMgYSByZWd1bGFyIGNoYXJhY3RlciBpbiBSIGFuZCB0aGF0IGNhbiBiZSBjb25mdXNpbmcgYXMgb3RoZXIgbGFuZ3VhZ2VzICgqZS5nLiosIEphdmEpIHVzZSBkb3QgYXMgYW4gb3BlcmF0b3IgdG8gZGVzaWduYXRlIHByb3BlcnR5IG9yIG1ldGhvZCBhY2Nlc3MsICplLmcqLCBpbiBKYXZhICp4LnZhbCogbWVhbnMgdGhhdCB5b3UgYXJlIGFjY2Vzc2luZyB0aGUgKnZhbCogcHJvcGVydHkgb2YgdGhlIG9iamVjdCAqeCouCgpTb21lIGV4YW1wbGVzIG9mIGxlZ2FsIHZhcmlhYmxlIG5hbWVzIGFyZTogKmRmKiwgKmRmMiosICpkZi50eG5zKiwgYW5kICpkZl9hbGwyMDE3Ki4gVGhlc2UgYXJlIHNvbWUgaWxsZWdhbCB2YXJpYWJsZSBuYW1lczogKjJkZiogKGNhbm5vdCBzdGFydCB3aXRoIGEgZGlnaXQpLCAqcnNcJGFsbCogKGNhbm5vdCBjb250YWluIGEgXCQ7IHRoZSBcJCBpcyB1c2VkIHRvIGFjY2VzcyBjb2x1bW5zIGluIGEgZGF0YWZyYW1lKSwgKnJzIyogKG9ubHkgLiBhbmQgXF8gYXJlIGFsbG93ZWQgaW4gYWRkaXRpb24gdG8gZGlnaXRzIGFuZCBsZXR0ZXJzKS4KCkl0IGlzIGNvbnNpZGVyZWQgZ29vZCBwcm9ncmFtbWluZyBwcmFjdGljZSB0byBnaXZlIGlkZW50aWZpZXJzIGEgc2Vuc2libGUgbmFtZSB0aGF0IGhpbnRzIGFzIHRvIHdoYXQgaXMgc3RvcmVkIGluIHRoZSB2YXJpYWJsZSByYXRoZXIgdGhhbiB1c2luZyByYW5kb20gbmFtZSBsaWtlICp4KiwgKnZhbCosIG9yICppMzMqLiBJbnN0ZWFkIHVzZSAqYW5JdGVtKiBvciAqYW5udWFsVG90YWwqLgoKSWRlbnRpZmllcnMgc2hvdWxkIGJlIG5hbWVkIGNvbnNpc3RlbnRseS4gTWFueSBwcm9ncmFtbWVycyB1c2Ugb25lIG9mIHR3byBzdHlsZXM6CgotICAgdW5kZXJzY29yZXMsICplLmcuKiwgKmludGVyZXN0X3JhdGUqCi0gICBjYW1lbENhc2UsICplLmcuKiwgKnNxdWFyZVJvb3QqLCAqZ3JhcGhEYXRhKiwgKmN1cnJlbnRXb3JraW5nRGlyZWN0b3J5KgoKTm90ZSB0aGF0IFIgaXMgY2FzZSBzZW5zaXRpdmUgd2hpY2ggbWVhbnMgdGhhdCBSIHRyZWF0cyB0aGUgaWRlbnRpZmllcnMgKkFQKiBhbmQgKmFwKiBhcyBkaWZmZXJlbnQgb2JqZWN0cy4KCkFzIGEgc2lkZSBub3RlLCBmaWxlcyBtYXkgYWxzbyBiZSBjYXNlIHNlbnNpdGl2ZSBidXQgdGhhdCBkZXBlbmRzIG9uIHRoZSBvcGVyYXRpbmcgc3lzdGVtLiBNYWNPUyBhbmQgTGludXggYXJlIGNhc2Ugc2Vuc2l0aXZlLCB3aGlsZSBXaW5kb3dzIGlzIGNhc2UgYXdhcmUgYnV0IG5vdCBjYXNlIHNlbnNpdGl2ZS4gRm9yIGV4YW1wbGUsIG9uIE1hY09TIGFuZCBMaW51eCB0aGVyZSBpcyBhIGRpZmZlcmVuY2UgYmV0d2VlbiAiQWlyUGFzc2VuZ2Vycy50eHQiIGFuZCAiYWlycGFzc2VuZ2Vycy50eHQiIHdoaWxlIG9uIFdpbmRvd3MgdGhlcmUgaXMgbm90LiBTUUwgaXMgYWxzbyBub3QgY2FzZSBzZW5zaXRpdmUuIEl0IGlzIGEgYmVzdCBwcmFjdGljZSB0byBhc3N1bWUgY2FzZSBzZW5zaXRpdml0eS4KCiMjIEV4cHJlc3Npb25zCgpBcyBhIGxhbmd1YWdlIHdpdGggcm9vdHMgaW4gc3RhdGlzdGljYWwgYW5hbHlzaXMsIFIgc3VwcG9ydHMgbW9zdCBtYXRoZW1hdGljYWwgb3BlcmF0b3JzLCBpbmNsdWRpbmcgbWFueSB0aGF0IGFyZSBub3Qgc3VwcG9ydGVkIHRocm91Z2ggb3BlcmF0b3JzIGluIG1vc3Qgb3RoZXIgbGFuZ3VhZ2VzLgoKUHJlY2VkZW5jZSBpcyBsaWtlIG90aGVyIGxhbmd1YWdlcyBhbmQgY2FuIGJlIChhbmQgc2hvdWxkIGJlKSBzcGVjaWZpZWQgZXhwbGljaXRseSB1c2luZyBwYXJlbnRoZXNlcy4KCmBgYHtyIG9wZXJhdG9ycywgZWNobz1UUlVFfQphIDwtIDk5CgpiID0gYSArIDIwICAgICAgICAgICAgICAgICAjIGFkZGl0aW9uCmIgPSBhIC0gMjAgICAgICAgICAgICAgICAgICMgc3VidHJhY3Rpb24KYiA9IGEgKiAyMCAgICAgICAgICAgICAgICAgIyBtdWx0aXBsaWNhdGlvbgpiID0gYSAvIDIwICAgICAgICAgICAgICAgICAjIGRpdmlzaW9uIC0tIGIgaXMgbm93ICJkb3VibGUiCmIgPSBhIF4gMyAgICAgICAgICAgICAgICAgICMgZXhwb25lbnRpYXRpb24KYiA9IGEgJSUgMiAgICAgICAgICAgICAgICAgIyBtb2R1bHVzIChtb2QpCmIgPSBhICUvJSAyICAgICAgICAgICAgICAgICMgaW50ZWdlciBkaXZpc2lvbiAoZGl2KQoKYiA9IChhIC8gMikgXiA1ICAgICAgICAgICAgIyBmb3JjZSBvcmRlciBvZiBldmFsdWF0aW9uCmBgYAoKVGhlcmUgYXJlIGFsc28gbnVtZXJvdXMgYnVpbHQtaW4gZnVuY3Rpb25zIGZvciBtYXRoZW1hdGljcyBhbmQgc3RhdGlzdGljcywgYnV0IHRob3NlIGFyZSBiZXlvbmQgdGhpcyB0dXRvcmlhbCBvbiBiYXNlIHN5bnRheC4gRm9yIHRoZSBzYWtlIG9mIGNvbXBsZXRlbmVzcywgYW4gZXhhbXBsZSBpcyBzaG93biBiZWxvdyB0aGF0IGNhbGN1bGF0ZXMgdGhlICp6Ki1zY29yZSBvZiBhIHZlY3RvciBvZiBudW1iZXJzLiBJbiBSLCBhIHZlY3RvciBpcyBzaW1pbGFyIHRvIGFuIGFycmF5OyBpdCBjb250YWlucyBwcmltaXRpdmVzIHZhbHVlcyAoaW50ZWdlciwgZG91YmxlLCBCb29sZWFuLCBvciBzdHJpbmcpLiBOb3RpY2UgdGhlIGF1dG9tYXRpYyB2ZWN0b3IgY2FsY3VsYXRpb24gZXZlbiB3aXRob3V0IGEgbG9vcC4KCmBgYHtyIGZ1bmNzfQp2ID0gYygxLDQsNiwyLDgsMSwwLDMpICAgICAjIHZlY3RvciBvZiBpbnRlZ2VycwoKbSA9IG1lYW4odikgICAgICAgICAgICAgICAgIyBtZWFuIG9mIHZhbHVlcyBpbiB2CnMgPSBzZCh2KSAgICAgICAgICAgICAgICAgICMgc3RhbmRhcmQgZGV2aWF0aW9uCnogPSAobSAtIHYpIC8gcyAgICAgICAgICAgICMgei1zY29yZSBvZiBlYWNoIHZhbHVlIGluIHYKCnByaW50KHJvdW5kKHosMikpICAgICAgICAgICMgcHJpbnQgcm91bmRlZCB2YWx1ZXMKYGBgCgojIyMgQm9vbGVhbiBWYXJpYWJsZXMgYW5kIExvZ2ljYWwgRXhwcmVzc2lvbnMKCkJvb2xlYW4gdmFsdWVzIGFyZSAqVFJVRSogYW5kICpGQUxTRSo7IG9yICpUKiBhbmQgKkYqLCByZXNwZWN0aXZlbHkuCgpgYGB7ciBib29sZWFuc30KbSA9IEZBTFNFCncgPSBUUlVFCgpxID0gKG0gJiB3KSB8ICghbSAmICF3KQoKcHJpbnQocGFzdGUwKCJxIGlzICIsIHEpKQpgYGAKClRoZSBCb29sZWFuIG9wZXJhdG9ycyBhcmUgKiYqIGZvciBBTkQsICpcfCogZm9yIE9SLCBhbmQgKiEqIGZvciBOT1QuIFRoZXNlIG9wZXJhdG9ycyBwZXJmb3JtIGxvZ2ljYWwgb3BlcmF0aW9ucyBvbiBCb29sZWFuIHZhcmlhYmxlcyBvciB2ZWN0b3Igb2YgQm9vbGVhbiBlbGVtZW50cy4gRXhjbHVzaXZlIG9yICgqeG9yKikgaXMgcGVyZm9ybWVkIHdpdGggdGhlIGZ1bmN0aW9uICp4b3IqLCAqZS5nLiosIDxjb2RlPnhvcihhLGIpPC9jb2RlPi4KClRoZXJlIGFyZSBsb25nIGZvcm1zIComJiogYW5kICpcfFx8KiBmb3IgcHJvZ3JhbW1pbmcgY29udHJvbC1mbG93IGFuZCBnZW5lcmFsbHkgcHJlZmVycmVkIGZvciAqaWYqIHN0YXRlbWVudHMuCgojIyBGbG93IENvbnRyb2wKClRoZSBjb250cm9sIGZsb3cgc3RhdGVtZW50cyBwcm92aWRlZCBieSBSIGFyZSBzaW1pbGFyIHRvIHRob3NlIGZvdW5kIGluIG1vc3Qgb3RoZXIgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzOiBsb29wcyBhbmQgaWYgc3RhdGVtZW50cy4KCiMjIyBMb29wcwoKUiBzdXBwb3J0cyB0aGUgdGhyZWUgY29tbW9uIHR5cGVzIG9mIGxvb3BzOiBhIGNvdW50aW5nIGxvb3AgKCpmb3IqKSwgYSB0b3AtdGVzdGVkIGxvb3AgKCp3aGlsZSopLCBhbmQgYSBib3R0b20tdGVzdGVkIGxvb3AgKCpyZXBlYXQqKS4KCiMjIyMgQ291bnRpbmcgTG9vcDogKmZvcioKClRoZSBjb3VudGluZyBsb29wIGlzIGFjdHVhbGx5IG1vcmUgbGlrZSBhbiBpdGVyYXRvciBpbiBSLCBhbHRob3VnaCBpdCBjYW4gYmUgc2V0IHVwIHRvIG1pbWljIHRoZSBiZWhhdmlvciBvZiBhIHR5cGljYWwgbG9vcCBpbiBDL0MrKywgSmF2YSwgKmV0Yy4qLCB0aGF0IGxvb3BzIGZyb20gYSBsb3cgdmFsdWUgdG8gYSBoaWdoIHZhbHVlLiBTZWUgdGhlIGV4YW1wbGVzIGJlbG93LgoKYGBge3Igc2ltcGxlRm9yTG9vcH0KbiA9IDUKZm9yIChpIGluIDE6bikgewogIHByaW50KGkpCn0KYGBgCgpJbiB0aGUgZXhhbXBsZSBhYm92ZSwgKmkqIGlzIHRoZSBsb29wIHZhcmlhYmxlIGFuZCBpdCB0YWtlcyBvbiB0aGUgdmFsdWVzIGluIHRoZSB2ZWN0b3IgKjE6NSosICppLmUuLCogMSwgMiwgMywgNCwgNSBcZW0gb25lIGF0IGEgdGltZS4gCgpUbyBjb3VudCBkb3duLCBpdCB3b3VsZCBiZSAqNToxKiBvciAqbjoxKiBpbiB0aGUgYWJvdmUgY29kZSBleGFtcGxlLgoKTGlrZSBvdGhlciBsYW5ndWFnZXMsIFIgdXNlcyBjdXJseSBicmFjZXMgdG8gZW5jbG9zZSB0aGUgYm9keSBvZiB0aGUgbG9vcCwgKmkuZS4qLCB0aGUgc3RhdGVtZW50cyB0aGF0IGFyZSBleGVjdXRlZCByZXBlYXRlZGx5LCBvbmNlIGZvciBlYWNoIHZhbHVlIG9mICppKi4gT2YgY291cnNlLCB0aGUgbG9vcCBjb3VudGVyIGNhbiBiZSBhbnkgcHJvcGVybHkgbmFtZWQgdmFyaWFibGUuCgpUaGUgZXhhbXBsZSBiZWxvdyBhY2Nlc3NlcyBhIHZlY3RvciB1c2luZyBwb3NpdGlvbmFsIGFjY2VzcywgKmUuZy4qLCAqdlsxXSogYWNjZXNzZXMgdGhlIGZpcnN0IGVsZW1lbnQuIE5vdGUgdGhhdCB2ZWN0b3IsIGRhdGEgZnJhbWVzLCBsaXN0cywgYW5kIGFycmF5cyBhcmUgaW5kZXhlZCBzdGFydGluZyBhdCAxIGFuZCBub3QgMCBhcyBpbiBDLWJhc2VkIGxhbmd1YWdlcy4KCmBgYHtyIGxvb3BzQ2FsY3N9CnYgPSBybm9ybSg1KSAgICAgICMgdmVjdG9yIG9mIGZpdmUgcmFuZG9tIG51bWJlcnMKZm9yIChpIGluIDE6bGVuZ3RoKHYpKSAKewogIHZbaV0gPSB2W2ldIF4gMgp9CgpwcmludCh2KQpgYGAKCldoaWxlIHlvdSBjYW4gdXNlIGxvb3BzIHlvdSBhY3R1YWxseSBkbyBub3QgbmVlZCB0aGVtLiBJZiB5b3UgYXBwbHkgYSBtYXRoZW1hdGljYWwgb3BlcmF0aW9uIHRvIGEgdmVjdG9yLCBSIGF1dG9tYXRpY2FsbHkgYXBwbGllcyB0aGVtIHRvIGVhY2ggZWxlbWVudCwgYnV0IGxvb3BzIG1pZ2h0IGJlIG1vcmUgbmF0dXJhbCBpbiB0aGUgYmVnaW5uaW5nLgoKYGBge3IgdmVjdG9yUHJvY30KIyBub3RlIHRoYXQgeW91IGFjdHVhbGx5IGRvIG5vdCBuZWVkIGxvb3BzIGluIFIKIyB0aGlzIGFsc28gc3F1YXJlcyBlYWNoIGVsZW1lbnQgaW4gdGhlIHZlY3RvciB2CnYgPSB2IF4gMgpgYGAKCkFzIGFscmVhZHkgc3RhdGVkLCBsb29waW5nIGluIFIgaXMgYWN0dWFsbHkgaXRlcmF0aW9uIG92ZXIgYSBzZXQ6IGEgc2V0IG9mIG51bWJlcnMgYXMgYWJvdmUgb3IgYSBzZXQgb2YgYW55IGtpbmQgb2YgcHJpbWl0aXZlIG9iamVjdCwgKmUuZy4sKiBzdHJpbmdzLiBOb3RlIHRoYXQgaW4gdGhlIGNvZGUgYmVsb3csICprKiB0YWtlcyBvbiBlYWNoIHZhbHVlIGluIHRoZSB2ZWN0b3Igb3ZlciB3aGljaCB0aGUgbG9vcCBpdGVyYXRlcy4KCmBgYHtyIGl0ZXJhdGVPdmVyU3RyaW5nc30KcyA9IGMoIm9uZSIsInR3byIsInRocmVlIiwiZm91ciIpCgpmb3IgKGsgaW4gcykgewogIHByaW50KGspCn0KYGBgCgpXZSBjb3VsZCBoYXZlIHdyaXR0ZW4gdGhpcyB1c2luZyBhIG5vbi1pdGVyYXRvciBhcHByb2FjaCBhcyB3ZWxsLCB3aGljaCBpcyBtb3JlIGxpa2Ugd2hhdCB5b3UnZCBkbyBpbiBDLiBOb3RlIHRoYXQgPGNvZGU+bGVuZ3RoPC9jb2RlPiByZXR1cm5zIHRoZSBudW1iZXIgb2YgZWxlbWVudHMgaW4gYSB2ZWN0b3IsICppLmUuKiwgaXRzICJsZW5ndGgiLgoKYGBge3IgbG9vcE92ZXJTdHJpbmdzfQpzID0gYygib25lIiwidHdvIiwidGhyZWUiLCJmb3VyIikKCmZvciAoaiBpbiAxOmxlbmd0aChzKSkgewogIHByaW50KHNbal0pCn0KYGBgCgojIyMjIyBJdGVyYXRpb24gQ29udGludWF0aW9uCgpDb250aW51YXRpb24gb2YgdGhlIGxvb3AgdG8gdGhlIG5leHQgaXRlcmF0aW9uIGFuZCBmb3Jnb2luZyBwcm9jZXNzaW5nIHRoZSByZW1haW5kZXIgb2YgdGhlIGN1cnJlbnQgaXRlcmF0aW9uIGlzIGRvbmUgd2l0aCAqbmV4dCogaW4gUiwgc2ltaWxhciB0byAqY29udGludWUqIGluIEMtYmFzZWQgbGFuZ3VhZ2VzIHN1Y2ggYXMgSmF2YS4KCkluIHRoZSBleGFtcGxlIGJlbG93IHdlIGFyZSBhbHJlYWR5IHJlYWNoaW5nIGFoZWFkIHRvIHRoZSAqaWYqIHN0YXRlbWVudC4gVGhlIGNvZGUgZnJhZ21lbnQgZWNob2VzIG9ubHkgb2RkIG51bWJlci4gT2YgY291cnNlLCB5b3UgY291bGQgaGF2ZSBkb25lIHRoaXMgZGlmZmVyZW50bHkgYnkganVzdCBpdGVyYXRpbmcgb3ZlciB0aGUgZXZlbiBudW1iZXJzIGZyb20gb25lIHRvIHR3ZW50eSwgYnV0IHRoYXQgd291bGQgbm90IGhhdmUgYWxsb3dlZCB1cyB0byBzaGFyZSB0aGlzIGV4YW1wbGUgZm9yIHVzaW5nICpuZXh0Ki4KCmBgYHtyIG5leHRJbkZvcn0KZm9yIChpIGluIDE6MTApIHsKICBpZiAoIWkgJSUgMikgewogICAgbmV4dAogIH0KICBwcmludChpKQp9CmBgYAoKQnV0LCBqdXN0IHRvIHNob3cgeW91IGhvdyB0byBpdGVyYXRlIG92ZXIganVzdCBvZGQgbnVtYmVyLCB3ZSBjYW4gdXNlIHRoZSA8Y29kZT5zZXEoKTwvY29kZT4gZnVuY3Rpb24gd2hpY2ggZ2VuZXJhdGVzIGEgc2VxdWVuY2Ugb2YgbnVtYmVycyBpbiBzdGVwcy4gTm8gKmlmKiBhbmQgbm8gKmJyZWFrKiBuZWVkZWQgLS0gbXVjaCBzaW1wbGVyLgoKYGBge3IgbG9vcE92ZXJPZGRTZXF9CmZvciAoaSBpbiBzZXEoMSwgMTAsIDIpKSB7CiAgcHJpbnQoaSkKfQpgYGAKCk9yLCBtb3JlIGV4cGxpY2l0bHkgYnkgc3BlY2lmeWluZyB0aGUgcGFyYW1ldGVycyBieSBuYW1lLgoKYGBge3IgbG9vcE92ZXJPZGRTZXFXaXRoUGFybXN9CmZvciAoaSBpbiBzZXEoZnJvbSA9IDEsIHRvID0gMTAsIGJ5ID0gMikpIHsKICBwcmludChpKQp9CmBgYAoKVG8gc3RvcCB0aGUgZXhlY3V0aW9uIG9mIHRoZSByZXN0IG9mIGEgbG9vcCBhbmQgdG8gbW92ZSBpbW1lZGlhdGVseSB0byB0aGUgbmV4dCBzdGF0ZW1lbnQgYWZ0ZXIgdGhlIGxvb3AgaXMgZG9uZSB3aXRoICpicmVhayogaW4gUiB3aGljaCBpcyBpZGVudGljYWwgdG8gQywgQysrLCBKYXZhLCBldGMuIEluIHRoZSBjb2RlIGJlbG93IHdlIHdpbGwgZmluZCB0aGUgcG9zaXRpb24gb2YgdGhlIGZpcnN0IG9jY3VycmVuY2Ugb2Ygc29tZSBudW1iZXI7ICp4KiBpcyB0aGUgbnVtYmVyIHdlIGFyZSBsb29raW5nIGZvciBpbiAqdiogYW5kICpwKiBpcyB0aGUgZm91bmQgcG9zaXRpb24uCgpgYGB7ciBicmVha0Zvcn0KdiA9IGMoMSwgMywgNSwgNywgMSwgOSwgNCkKeCA9IDkKcCA9IDAKCmZvciAoaSBpbiAxOmxlbmd0aCh2KSkgewogIGlmICh2W2ldID09IHgpIHsKICAgIHAgPSBpCiAgICBicmVhawogIH0KfQoKcHJpbnQocCkKYGBgCgpUbyBiZSBjbGVhciwgdGhlcmUgaXMgYSBxdWlja2VyIGFuZCBtb3JlIGVmZmljaWVudCB3YXkgdG8gZG8gdGhpcyB1c2luZyB0aGUgPGNvZGU+d2hpY2g8L2NvZGU+IGZ1bmN0aW9uLiBUaGlzIGZ1bmN0aW9uIGRvZXMgbm90IGV4aXN0IGlzIG90aGVyIGxhbmd1YWdlcyBhbmQgaXQgc2hvd3MgdGhlIHBvd2VyIG9mIFIuIEFsc28sIHVzaW5nIDxjb2RlPndoaWNoPC9jb2RlPiBpcyBtdWNoIGZhc3Rlci4gSW5jaWRlbnRhbGx5LCB0aGUgY29kZSBiZWxvdyBmaW5kcyBhbGwgb2NjdXJyZW5jZXMgb2YgKngqIGluICp2Ki4gTm90ZSwgb25jZSBhZ2FpbiwgdGhlIHVzZSBvZiBhIHZlY3RvciB2YXJpYWJsZSB0byByZWZlciB0byBhbGwgZWxlbWVudHMuCgpgYGB7ciBmaW5kVmFsV2l0aFdoaWNofQp2ID0gYygxLCAzLCA1LCA3LCAxLCA5LCA0KQp4ID0gOQoKcCA9IHdoaWNoKHYgPT0geCkKCnByaW50KHApCmBgYAoKIyMjIyBDb25kaXRpb25hbCBMb29wczogKndoaWxlKiBhbmQgKnJlcGVhdCoKCkxpa2UgbWFueSBvdGhlciBsYW5ndWFnZXMsIFIgc3VwcG9ydHMgdG9wIGFuZCBib3R0b20gdGVzdGVkIGxvb3BzLiBJbiB0aGUgZXhhbXBsZSBiZWxvdyB3ZSByZXBlYXRlZGx5IGFzayBmb3IgYSBudW1iZXIgZnJvbSB0aGUgdXNlciBhbmQgb25seSBleGl0IHRoZSBsb29wIGlmIHRoZSBudW1iZXIgaXMgNDIuIE9mIGNvdXJzZSwgdGhlIGV4YW1wbGUgcmVhbGx5IHNob3VsZCBiZSBkb25lIHdpdGggYSBib3R0b20gdGVzdGVkIGxvb3AuCgpgYGB7ciB3aGlsZUxvb3AsIGV2YWw9Rn0KcmVzcG9uc2UgPC0gYXMuaW50ZWdlcihyZWFkbGluZShwcm9tcHQ9IkVudGVyIGEgbnVtYmVyOiAiKSkKCndoaWxlIChyZXNwb25zZSAhPSA0MikgeyAgIAogIHByaW50KCJTb3JyeSwgbm90IGNvcnJlY3QiKTsKICByZXNwb25zZSA8LSBhcy5pbnRlZ2VyKHJlYWRsaW5lKHByb21wdD0iRW50ZXIgYSBudW1iZXI6ICIpKTsKfQpgYGAKCkluIHRoZSBhYm92ZSBleGFtcGxlLCB3ZSBhcmUgdXNpbmcgdHdvIG5ldyBmdW5jdGlvbnMuIDxjb2RlPnJlYWRsaW5lPC9jb2RlPiBpcyB1c2VkIHRvIHJlYWQgaW5wdXQgZnJvbSB0aGUgY29uc29sZSwgd2hpbGUgPGNvZGU+YXMuaW50ZWdlcjwvY29kZT4gY29lcmNlcyAob3IgY2FzdHMgaW4gQy9DKysgdGVybWlub2xvZ3kpIHRoZSBpbnB1dCB0byBhbiBpbnRlZ2VyLgoKVGhlIGNvZGUgaXMgYWJvdmUgaXMgbm90IHZlcnkgZWZmaWNpZW50IG9yIGVsZWdhbnQgYXMgaXQgaGFzIHRoZSBjb2RlIGZvciB0aGUgdXNlciBwcm9tcHQgdHdpY2UuIEl0IGlzIHJlYWxseSBiZXR0ZXIgdG8gYXNrIGZpcnN0IGFuZCB0aGVuIGNoZWNrIHRoZSBjb25kaXRpb246IHdlIG5lZWQgYSBib3R0b20tdGVzdGVkIGxvb3AuCgpMZXQncyBsb29rIGF0IHRoZSBzYW1lIGV4YW1wbGUsIGJ1dCB3aXRoIGEgKnJlcGVhdCogbG9vcCB0aGF0IHJ1bnMgdW50aWwgYSBjb25kaXRpb24gaXMgcmVhY2hlZCBhbmQgdGhlIGxvb3AgaXMgZXhwbGljaXRseSBleGl0ZWQgd2l0aCAqYnJlYWsqLiBTbywgaWYgeW91IHdhbnQgdG8gcnVuIHRoZSBjb2RlIGF0IGxlYXN0IG9uY2UsIHVzZSAqcmVwZWF0KjsgaWYgdGhlIGNvZGUgaXMgcnVuIHplcm8gb3IgbW9yZSB0aW1lcywgdXNlICp3aGlsZSouIFRoZSAqcmVwZWF0KiBsb29wIGlzIGlkZW50aWNhbCB0byB0aGUgKmRvKiBvciAqZG8gd2hpbGUqIGxvb3AgZm91bmQgaW4gbWFueSBvdGhlciBwcm9ncmFtbWluZyBsYW5ndWFnZXMuCgpgYGB7ciByZXBlYXRMb29wLCBldmFsPUZ9CnJlcGVhdCB7ICAgCiAgcmVzcG9uc2UgPC0gYXMuaW50ZWdlcihyZWFkbGluZShwcm9tcHQ9IkVudGVyIGEgbnVtYmVyOiAiKSk7CiAgaWYgKHJlc3BvbnNlID09IDQyKSB7CiAgICBicmVhawogIH0KICBwcmludCgiU29ycnksIG5vdCBjb3JyZWN0Iik7Cn0KYGBgCgpUaGVyZSBpcyBubyBlcXVpdmFsZW50IGluIFIgZm9yIHRoZSB1c2Ugb2YgYW4gaW5maW5pdGUgKmZvciogbG9vcCBhcyBpbiBDIGFuZCBDKyssICplLmcuKiwgPGNvZGU+Zm9yKDs7KXsgLy8gZG8gc29tZXRoaW5nIGluZGVmaW5pdGVseSB9PC9jb2RlPiB0aGF0IHJ1bnMgdW50aWwgYSBjb25kaXRpb24gaXMgcmVhY2hlZCBhbmQgdGhlbiB1c2VzICpicmVhayogdG8gZXhpdCB0aGUgbG9vcC4gVGhlICpyZXBlYXQqIGxvb3AgY29uc3RydWN0IGlzIHVzZWQgaW5zdGVhZC4gVGhlcmUgaXMgbm8gdGVzdGluZyBvZiBhIGNvbmRpdGlvbiBpbiB0aGUgKnJlcGVhdCogbG9vcCwgc28gdGhlcmUncyBubyBlcXVpdmFsZW50IHRvIHRoZSBDLWxpa2UgY29uc3RydWN0ICpkbyAuLi4gd2hpbGUqLgoKIyMjIEFsdGVybmF0aW9uOiAqaWYqCgpUaGUgdXNlIG9mICppZiogdG8gc2VsZWN0aXZlbHkgZXhlY3V0ZSBhIGJsb2NrIG9mIGNvZGUgYmFzZWQgb24gYSBjb25kaXRpb24gaXMgdGhlIHNhbWUgaW4gUiBhcyBpdCBpcyBpbiBvdGhlciBsYW5ndWFnZXMuIFdlIGFscmVhZHkgc2F3IGFuIGV4YW1wbGUgYW5kIHRoZSBzYW1lIGV4YW1wbGUgaXMgYmVsb3c6CgpgYGB7ciBpZn0KZm9yIChpIGluIDE6MTApIHsKICBpZiAoIWkgJSUgMikgewogICAgbmV4dAogIH0KICBwcmludChpKQp9CmBgYAoKTm90ZSB0aGF0IHRoZSBjb25kaXRpb24gaXMgaW4gcGFyZW50aGVzaXMgYW5kIHRoYXQgdGhlIGJsb2NrIGV4ZWN1dGVkIGlmIHRoZSBjb25kaXRpb24gaXMgdHJ1ZSBpcyBpbiBjdXJseSBicmFjZXMuIFRoaXMgaXMgaWRlbnRpY2FsIHRvIEMsIEMrKywgSmF2YSwgUHl0aG9uLCBldGMuCgpBIG1vcmUgY29tcGxleCBleGFtcGxlIGlzIHNob3duIGJlbG93IHRoYXQgc3F1YXJlcyBhbGwgbnVtYmVycyBpbiBhIHZlY3RvciB0aGF0IGFyZSBsZXNzIHRoYW4gNSBhbmQgY3ViZXMgdGhlbSBpZiBncmVhdGVyIHRoYW4gNS4gTm90IGEgdXNlZnVsIGV4YW1wbGUgYnV0IG9uZSB0aGF0IGFsbG93cyB1cyB0byBzaG93IHRoZSB1c2Ugb2YgKmlmLWVsc2UqLgoKYGBge3IgaWZFbHNlfQpmb3IgKGkgaW4gMToxMCkgewogIGlmIChpIDwgNSkgewogICAgcHJpbnQoaV4yKQogIH0gZWxzZSB7CiAgICBwcmludChpXjMpCiAgfQp9CmBgYAoKIyMgRnVuY3Rpb25zCgpDb2RlIG9yZ2FuaXphdGlvbiBhbmQgcmV1c2UgaW4gUiBpcyBkb25lIHVzaW5nIGZ1bmN0aW9ucy4gQWxsIG9iamVjdHMgYXJlIGZyZWUgbWV0aG9kcyBhbmQgYXJlIG5vdCBib3VuZCB0byBhbiBvYmplY3Qgb3IgYSBjbGFzcyBsaWtlIGluIEphdmEgb3IgQysrLiBJdCdzIHRoZSBzYW1lIHdheSBhcyBpbiBQeXRob24gb3IgQy4KCkFsbCBmdW5jdGlvbnMgaW4gUiBjYW4gcmV0dXJuIGEgdmFsdWUsIGFsdGhvdWdoIHRoZXkgZG8gbm90IGhhdmUgdG8uIFNvLCBSIGRvZXMgbm90IGRpc3Rpbmd1aXNoIGJldHdlZW4gZnVuY3Rpb25zIGFuZCBwcm9jZWR1cmVzIGFuZCB0aGVyZSBpcyBubyAqdm9pZCogcmV0dXJuIHR5cGUgYXMgaW4gQywgQysrLCBhbmQgSmF2YS4KCiMjIyBEZWZpbmluZyBhIEZ1bmN0aW9uCgpUaGUgZ2VuZXJpYyB0ZW1wbGF0ZSBmb3IgZGVmaW5pbmcgYSBmdW5jdGlvbiBpczoKCmBgYHtyIGV2YWw9RkFMU0V9CmZ1bmN0aW9uX25hbWUgPC0gZnVuY3Rpb24oYXJnXzEsIGFyZ18yLCAuLi4pIHsKICAgRnVuY3Rpb24gYm9keSAKfQpgYGAKClRoZSBleGFtcGxlIGJlbG93IGRlZmluZXMgYSBmdW5jdGlvbiBjYWxsZWQgKmZpbmRTbWFsbGVzdCgpKiB3aGljaCB0YWtlcyBhIHZlY3RvciBvZiBwb3NpdGl2ZSBpbnRlZ2VycyBhcyBhbiBhcmd1bWVudCBhbmQgcmV0dXJucyB0aGUgc21hbGxlc3QgZWxlbWVudCBpbiB0aGUgdmVjdG9yLiBXaGlsZSBpdCBjYW4gYmUgc29sdmVkIGluIHNldmVyYWwgd2F5cywgd2Ugd2lsbCBzaG93IGEgZGVzaWduIHRoYXQgdXNlcyBsb29wcyBhbmQgc2hvdWxkIGJlIGZhbWlsaWFyIHRvIHByb2dyYW1tZXJzIG9mIG1vc3Qgb3RoZXIgbGFuZ3VhZ2VzLgoKTm90ZSB0aGF0IHdlIGFyZSB1c2luZyB0aGUgcHJlZGVmaW5lZCB2YWx1ZSAqSW5mKiB3aXRoIGlzIHRoZSBsYXJnZXN0IHJlcHJlc2VudGFibGUgaW50ZWdlci4gVGhlcmUgaXMgYWxzbyAqLUluZiogdGhhdCBpcyB0aGUgc21hbGxlc3QgcmVwcmVzZW50YWJsZSBpbnRlZ2VyLgoKYGBge3IgZnVuY3Rpb25EZWZ9CmZpbmRTbWFsbGVzdCA9IGZ1bmN0aW9uKHYpCnsKICBzID0gSW5mCiAgZm9yIChpIGluIDE6bGVuZ3RoKHYpKQogIHsKICAgIGlmICh2W2ldIDwgcykgewogICAgICBzID0gdltpXQogICAgfQogIH0KICByZXR1cm4gKHMpCn0KYGBgCgpXaGlsZSB5b3UgY2FuIHVzZSAqPSogdG8gZGVmaW5lIGEgZnVuY3Rpb24sIHlvdSBzaG91bGQgcmVhbGx5IGdldCB1c2VkIHRvIHVzaW5nIHRoZSBtb3JlIGNvbW1vbiAqXDwtKiBzeW50YXguIFNvLCBsZXQncyB0cnkgYWdhaW46CgpgYGB7ciBmdW5jdGlvbkRlZkJldHRlcn0KZmluZFNtYWxsZXN0IDwtIGZ1bmN0aW9uKHYpCnsKICBzID0gSW5mCiAgZm9yIChpIGluIDE6bGVuZ3RoKHYpKQogIHsKICAgIGlmICh2W2ldIDwgcykgewogICAgICBzID0gdltpXQogICAgfQogIH0KICByZXR1cm4gKHMpCn0KYGBgCgpKdXN0IHRvIGJlIGNsZWFyLCBpbiBwcmFjdGljZSB5b3Ugd291bGQgdXNlIHRoZSA8Y29kZT5taW4oKTwvY29kZT4gZnVuY3Rpb24gdG8gZmluZCB0aGUgc21hbGxlc3QgZWxlbWVudCByYXRoZXIgd3JpdGluZyBpdCB5b3Vyc2VsZi4KCldoaWxlIHRoZXJlIGFyZSBzZXZlcmFsIHdheXMgdG8gcmV0dXJuIGEgdmFsdWUgZnJvbSBhIGZ1bmN0aW9uLCB0aGUgd2F5IHRoYXQgaXMgbW9zdCBjb25ncnVlbnQgd2l0aCBvdGhlciBsYW5ndWFnZXMgaXMgdGhlIHVzZSBvZiB0aGUgKnJldHVybiogc3RhdGVtZW50LgoKTm90ZSB0aGF0IHRoZSB0eXBlIG9mIHJldHVybiB2YWx1ZSBhbmQgdGhlIHR5cGUgb2YgYXJndW1lbnRzIGFyZSBub3QgZGVjbGFyZWQuIFIgdXNlcyBhIGxhenkgZXZhbHVhdGlvbiBtZWNoYW5pc20gYW5kIG5vIHR5cGUgY2hlY2tpbmcgaXMgcGVyZm9ybWVkIHVudGlsIHJ1bi10aW1lLgoKIyMjIENhbGxpbmcgYSBGdW5jdGlvbgoKVG8gY2FsbCBhIGZ1bmN0aW9uLCB5b3Ugd291bGQgaW52b2tlIGl0IHdpdGggaXRzIG5hbWUgYW5kIGl0cyByZXF1aXJlZCBhcmd1bWVudHMuCgpgYGB7ciBjYWxsRnVuY30KeCA9IGMoMywxLDksNywzLDYpCgp3ID0gZmluZFNtYWxsZXN0KHgpCnByaW50KHcpCmBgYAoKIyMjIEZ1bmN0aW9uIFBhcmFtZXRlcnMKCklmIGEgZnVuY3Rpb24gdGFrZXMgc2V2ZXJhbCBhcmd1bWVudHMgeW91IGdlbmVyYWxseSBwYXNzIHRoZW0gaW4gdGhlIG9yZGVyIGRlY2xhcmVkOyB0aGUgYXBwcm9hY2ggdGhhdCBpcyB1c2VkIGJ5IGFsbCBvdGhlciBsYW5ndWFnZXMuIEhvd2V2ZXIsIGluIFIgeW91IGNhbiBwYXNzIHRoZSBhcmd1bWVudHMgaW4gYW55IG9yZGVyIGFzIGxvbmcgYXMgeW91IHNwZWNpZnkgdGhlIG5hbWUgb2YgdGhlIGFyZ3VtZW50LgoKQXJndW1lbnQgbWF0Y2hpbmcgaXMgYSBiaXQgZGlmZmVyZW50IGluIFIgY29tcGFyZWQgdG8gb3RoZXIgbGFuZ3VhZ2VzLiBGaXJzdGx5LCBSIGRvZXMgYWxsIGFyZ3VtZW50IGNoZWNraW5nIGF0IHJ1bi10aW1lLiBTZWNvbmRseSwgd2hpbGUgYXJndW1lbnRzIGNhbiBiZSBtYXRjaGVkIHBvc2l0aW9uYWxseSBsaWtlIGluIG90aGVyIGxhbmd1YWdlcywgYXJndW1lbnRzIGNhbiBhbHNvIGJlIG1hdGNoZWQgYnkgcGFyYW1ldGVyIG5hbWUgLS0gYSBzeW50YXggbm90IHN1cHBvcnRlZCBieSBtb3N0IG90aGVyIGxhbmd1YWdlcy4KCkZvciBleGFtcGxlLCB0aGUgYnVpbHQtaW4gZnVuY3Rpb24gPGNvZGU+c2VxPC9jb2RlPiBnZW5lcmF0ZXMgYSBzZXF1ZW5jZSBvZiBudW1iZXJzIGFuZCByZXR1cm5zIHRob3NlIG51bWJlcnMgaW4gYSB2ZWN0b3IuIFRoZSBkZWZpbml0aW9uIG9mIHRoZSBmdW5jdGlvbiBpcyBhcyBmb2xsb3dzOiA8Y29kZT5zZXEodG8sIGZyb20sIGJ5LCBsZW5ndGgub3V0LCBhbG9uZy53aXRoKTwvY29kZT4uCgpIZXJlIGFyZSBleGFtcGxlcyBvZiB1c2luZyBpdC4gTm90ZSB0aGF0ICpieSosICpsZW5ndGgub3V0KiwgYW5kICphbG9uZy53aXRoKiBoYXZlIGRlZmF1bHQgdmFsdWVzIGFuZCBhcmUgdGhlcmVmb3JlIG9wdGlvbmFsLgoKYGBge3Igc2VxUGFybVBhc3Npbmd9CnYgPSBzZXEoMSwgMTAsIDIpICAgICMgaW50ZWdlcnMgZnJvbSAxIHRvIDEwIGluIGluY3JlbWVudHMgb2YgMgp3ID0gc2VxKDEsIDUpICAgICAgICAjIGludGVnZXJzIGZyb20gMSB0byA1IChieSBkZWZhdWx0IGluIGluY3JlbWVudHMgb2YgMSkKCiMgcGFzcyBhcmd1bWVudHMgaW4gYSBkaWZmZXJlbnQgb3JkZXIgYnV0IHNwZWNpZnkgYnkgbmFtZQp3ID0gc2VxKGZyb20gPSA1LCBieSA9IC0wLjUsIHRvID0gMSkKYGBgCgpSIGFsc28gc3VwcG9ydHMgdmFyaWFibGUgbnVtYmVycyBvZiBhcmd1bWVudHMgYnV0IHRoYXQgaXMgYmV5b25kIHRoZSBzY29wZSBvZiB0aGlzIHR1dG9yaWFsLgoKIyMjIERlZmF1bHQgQXJndW1lbnRzCgpSIGZ1bmN0aW9ucyBjYW4gaGF2ZSBkZWZhdWx0IHZhbHVlcyBmb3IgYXJndW1lbnRzIHdoaWNoIGFyZSB0aGVuIG9wdGlvbmFsIHdoZW4gdGhlIGZ1bmN0aW9uIGlzIGNhbGxlZC4gV2hlbiB0aGUgYXJndW1lbnQgaXMgbWlzc2luZywgdGhlbiB0aGUgZGVmYXVsdCB2YWx1ZSBpcyBwYXNzZWQuIEluIHRoZSBleGFtcGxlIGJlbG93LCB0aGUgKnN0YXJ0KiBhcmd1bWVudCBpcyB0aGUgcG9zaXRpb24gYXQgd2hpY2ggdGhlIHNlYXJjaCBmb3IgdGhlIHNtYWxsZXN0IGVsZW1lbnQgd2lsbCBzdGFydC4KCmBgYHtyIGZ1bmN0aW9uRGVmQXJnfQpmaW5kU21hbGxlc3QgPC0gZnVuY3Rpb24odiwgc3RhcnQgPSAxKQp7CiAgcyA9IEluZgogIGZvciAoaSBpbiBzdGFydDpsZW5ndGgodikpCiAgewogICAgaWYgKHZbaV0gPCBzKSB7CiAgICAgIHMgPSB2W2ldCiAgICB9CiAgfQogIHJldHVybiAocykKfQpgYGAKCmBgYHtyfQp4ID0gYygzLDEsOSw3LDMsNikKCncgPSBmaW5kU21hbGxlc3QoeCwgMykKcHJpbnQodykKCncgPSBmaW5kU21hbGxlc3QoeCkKcHJpbnQodykKYGBgCgojIyMgTG9jYWwgVmFyaWFibGVzCgpBcyBpbiBtb3N0IG90aGVyIHByb2dyYW1taW5nIGxhbmd1YWdlcywgUiBmdW5jdGlvbnMgY2FuIGRlZmluZSBsb2NhbCB2YXJpYWJsZXMgdGhhdCBhcmUgbm90IGtub3duIG91dHNpZGUgdGhlIHNjb3BlIG9mIHRoZSBmdW5jdGlvbi4gVGhlIHNjb3BlIGJvdW5kYXJpZXMgaW4gUiBhcmUgbGlrZSBvdGhlciBsYW5ndWFnZXM6IGEgYmxvY2sgZW5jbG9zZWQgaW4gY3VybHkgYnJhY2VzLgoKSW4gdGhlIGV4YW1wbGUgYmVsb3csICpsb2NhbC52YXIqIGlzIGxvY2FsIHRvIHRoZSBmdW5jdGlvbiBhbmQgdGh1cyBpcyBub3QgdmlzaWJsZSBvdXRzaWRlIG9mIHRoZSBmdW5jdGlvbi4gVGhlIGNvZGUgYmVsb3cgcHJvZHVjZXMgdGhlIGVycm9yOiAiRXJyb3IgaW4gcHJpbnQobG9jYWwudmFyKSA6IG9iamVjdCAnbG9jYWwudmFyJyBub3QgZm91bmQiLgoKYGBge3IgbG9jYVZhcnMsIGVjaG89VFJVRSwgZXZhbD1GQUxTRX0KZmluZFNtYWxsZXN0IDwtIGZ1bmN0aW9uKHYsIHN0YXJ0ID0gMSkKewogIGxvY2FsLnZhciA9IEluZgogIGZvciAoaSBpbiBzdGFydDpsZW5ndGgodikpCiAgewogICAgaWYgKHZbaV0gPCBsb2NhbC52YXIpIHsKICAgICAgbG9jYWwudmFyID0gdltpXQogICAgfQogIH0KICByZXR1cm4gKGxvY2FsLnZhcikKfQoKeCA9IGMoMywxLDksNywzLDYpCgp3ID0gZmluZFNtYWxsZXN0KHgsIDMpCgojIHdlIGNhbm5vdCBlY2hvIG9yIGFjY2VzcyB0aGUgbG9jYWwgdmFyaWFibGUgInMiCnByaW50KGxvY2FsLnZhcikKYGBgCgojIyMgUmVjdXJzaW9uCgpSIGZ1bmN0aW9ucyBjYW4gYmUgY2FsbGVkIHJlY3Vyc2l2ZWx5LiBUaGUgZXhhbXBsZSBiZWxvdyBjYWxjdWxhdGVzIGZhY3RvcmlhbCB1c2luZyByZWN1cnNpb24gcmF0aGVyIHRoYW4gYSBsb29wLgoKYGBge3IgcmVjdXJzaXZlRnVuY3N9CmZhYyA8LSBmdW5jdGlvbih4KQp7CiAgaWYgKHggPT0gMSkgCiAgICByZXR1cm4gKDEpCiAgZWxzZSAKICAgIHJldHVybiAoeCAqIGZhYyh4LTEpKQp9CgpwcmludChmYWMoOCkpCmBgYAoKPiBJZiBpdCBoYXNuJ3QgYmVlbiBvYnZpb3VzIHlldCwganVzdCBsaWtlIGluIG90aGVyIGxhbmd1YWdlcywgdGhlIHBsYWNlbWVudCBvZiBjdXJseSBicmFjZXMgbWFrZXMgbm8gZGlmZmVyZW5jZS4gRm9yIHNpbmdsZSBzdGF0ZW1lbnQgYmxvY2tzLCB0aGUgY3VybHkgYnJhY2VzIGNhbiBiZSBvbWl0dGVkLgoKPiBUaGUgcGFyZW50aGVzaXMgYXJvdW5kIHRoZSB2YWx1ZSBmb3IgKnJldHVybiogYXJlIHJlcXVpcmVkLgoKQXMgYW4gZXhlcmNpc2UsIHRyeSB3cml0aW5nIHRoZSBhYm92ZSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgZmFjdG9yaWFsIHVzaW5nIGEgbG9vcC4KCiMjIFR5cGUgQ29lcmNpb24KClR5cGUgY29lcmNpb24gKG9yIGNhc3RpbmcgaW4gQy9DKysgdGVybWlub2xvZ3kpIGlzIGRvbmUgd2l0aCB0eXBlIGNvbnZlcnNpb24gZnVuY3Rpb25zIGluIFIgYW5kIG5vdCB0aHJvdWdoIG9wZXJhdG9ycyBsaWtlIGluIEMsIEMrKywgYW5kIEphdmEuIFRoZSBleGFtcGxlIGJlbG93IHNob3dzIHRoZSBtb3N0IGNvbW1vbiB0eXBlIGNvbnZlcnNpb24gZnVuY3Rpb25zLiBOb3RlIHRoYXQgaW4gc29tZSBzaXR1YXRpb24geW91IHdpbGwgbG9zZSBpbmZvcm1hdGlvbiwganVzdCBsaWtlIGluIG90aGVyIGxhbmd1YWdlcy4KCmBgYHtyIHR5cGVDb252ZXJzaW9ufQpzID0gIjEyLjQiICAgICAgICAgICAgICAgICAjIHN0cmluZyAoY2hhcmFjdGVyKQppID0gYXMuaW50ZWdlcihzKSAgICAgICAgICAjIGNvbnZlcnQgdG8gaW50ZWdlcjogMTIKZCA9IGFzLm51bWVyaWMocykgICAgICAgICAgIyBjb252ZXJ0IHRvIGRvdWJsZTogMTIuMwoKdyA9ICIkMTIuMyIgICAgICAgICAgICAgICAgIyBhZGRpdGlvbmFsIGNoYXJhY3RlcnMKayA9IGFzLmludGVnZXIodykgICAgICAgICAgIyBjYW5ub3QgYmUgY29udmVydGVkIGR1ZSB0byAkCmBgYAoKSWYgYSBjb2VyY2lvbiBpcyBub3Qgc3VjY2Vzc2Z1bCB0aGUgcmVzdWx0IGluICpOQSogd2hpY2ggaW5kaWNhdGVzIGEgKm51bGwqIG9yIG1pc3NpbmcgdmFsdWUuCgojIyBWZWN0b3JzCgpMZXQncyB0YWxrIG1vcmUgYWJvdXQgdmVjdG9ycy4gSW4gUiwgYSB2ZWN0b3IgaXMgc2ltaWxhciB0byBhIGxpc3Qgb3IgYXJyYXkgaW4gb3RoZXIgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzLiBJdCBpcyBhIGNvbGxlY3Rpb24gb2YgZWxlbWVudHMgb2YgdGhlIHNhbWUgYmFzaWMgdHlwZTogbnVtZXJpYywgY2hhcmFjdGVyLCBvciBCb29sZWFuLiBBIGxpc3QgaW4gUiBpcyBhIGNvbGxlY3Rpb24gb2YgbWl4ZWQgZGF0YSB0eXBlcy4gVGhpcyB0dXRvcmlhbCBhcHBsaWVzIHRvIHZlY3RvcnMgb25seS4KClRoZXJlIGFyZSBubyBzcGVjaWZpYyBwYWNrYWdlcyByZXF1aXJlZCBmb3IgdGhlc2UgZnVuY3Rpb25zLgoKIyMgQ3JlYXRpbmcgYSBWZWN0b3IKClRoZSBjb2RlIGJlbG93IGNyZWF0ZXMgYW4gYXJ0aWZpY2lhbCB2ZWN0b3Igb2YgcmFuZG9tIGludGVnZXJzIGZvciB1c2UgaW4gdGhlIHR1dG9yaWFsLiBJbiBwcmFjdGljZSwgdmVjdG9ycyBhcmUgZ2VuZXJhbGx5IGNvbHVtbnMgaW4gZGF0YSBmcmFtZXMgd2hpY2ggYXJlIGZyZXF1ZW50bHkgdGhlIHJlc3VsdCBvZiByZWFkaW5nIGRhdGEgZnJvbSBhIENTViBmaWxlIG9yIGEgZGF0YWJhc2UuCgpgYGB7ciBjcmVhdGVTYW1wbGVWZWN0b3J9CiMgdmVjdG9yIG9mIDUwIHJhbmRvbSBpbnRlZ2VycyBiZXR3ZWVuIDAgYW5kIDEwCgojIHNldCB0aGUgc2VlZCBmb3IgdGhlIHJhbmRvbSBudW1iZXIgZ2VuZXJhdG9yIHRvIGVuc3VyZSBzYW1lCiMgc2VxdWVuY2Ugb2YgcmFuZG9tIG51bWJlcnMgZXZlcnkgdGltZSB0aGUgY29kZSBpcyBydW4Kc2V0LnNlZWQoOTg3ODgpCnYgPC0gcm91bmQocnVuaWYoNTAsIG1pbiA9IDAsIG1heCA9IDEwKSwwKQoKIyBhcmd1bWVudHMgZG8gbm90IGhhdmUgdG8gYmUgcGFzc2VkIGluIHRoZSBvcmRlciB0aGF0IHRoZXkgYXJlCiMgZGVjbGFyZWQgaW4gdGhlIGZ1bmN0aW9uIGRlZmluaXRpb24gYXMgbG9uZyBhcyB0aGUgbmFtZXMgb2YgdGhlCiMgYXJndW1lbnRzIGFyZSBzcGVjaWZpZWQKdiA8LSByb3VuZChydW5pZihuID0gNTAsIG1heCA9IDEwLCBtaW4gPSAxKSwwKQp2IDwtIHJvdW5kKHJ1bmlmKG1heCA9IDEwLCBtaW4gPSAxLCBuID0gNTApLDApCgpwcmludCh2KQpgYGAKCiMjIEFjY2Vzc2luZyBFbGVtZW50cyBpbiBhIFZlY3RvcgoKRWxlbWVudHMgYXJlIGFjY2Vzc2VkIHBvc2l0aW9uYWxseSwgYWx0aG91Z2ggaW4gUiwgdGhlIGFjY2VzcyBpbmRleCBjYW4gYmUgYSB2ZWN0b3Igb2YgaW50ZWdlcnMgaW4gd2hpY2ggY2FzZSBhbGwgZWxlbWVudHMgYXQgdGhvc2UgcG9zaXRpb25zIGFyZSByZXRyaWV2ZWQuIFBvc2l0aW9ucyBhcmUgbnVtYmVyZWQgZnJvbSAxIHRvIHRoZSBudW1iZXIgb2YgZWxlbWVudHMgaW4gYSB2ZWN0b3IuIFRoZSBudW1iZXIgb2YgZWxlbWVudHMgKG9yIGxlbmd0aCkgb2YgYSB2ZWN0b3IgY2FuIGJlIG9idGFpbmVkIHVzaW5nIHRoZSA8Y29kZT5sZW5ndGgoKTwvY29kZT4gZnVuY3Rpb24uCgpJbiB0aGUgZXhhbXBsZSBiZWxvdywgbm90ZSB0aGF0IDxjb2RlPm46bTwvY29kZT4gZ2VuZXJhdGVzIGEgdmVjdG9yIG9mIGludGVnZXJzIGZyb20gKm4qIHRvICptKiwgaW5jbHVzaXZlLiBUaGUgPGNvZGU+c2VxKCk8L2NvZGU+IGdlbmVyYXRlcyBhIHNlcXVlbmNlIG9mIGludGVnZXJzIGF0IGFuIGludGVydmFsLgoKYGBge3Igc2ltcGxlVmVjdG9yQWNjZXNzLCBldmFsPUZ9CiMgYWNjZXNzIGEgc2luZ2xlIGVsZW1lbnQgYXQgcG9zaXRpb24gMwp2WzNdCgojIGFjY2VzcyBlbGVtZW50IDEwIHRocm91Z2ggMTUKdlsxMDoxNV0KCiMgYWNjZXNzIHRoZSBsYXN0IGVsZW1lbnQKdltsZW5ndGgodildCgojIGFjY2VzcyBldmVyeSBvdGhlciBlbGVtZW50CnZbc2VxKGZyb20gPSAxLCB0byA9IGxlbmd0aCh2KSwgYnkgPSAyKV0KCiMgYWNjZXNzIHNwZWNpZmljIGVsZW1lbnRzIGF0IHBvc2l0aW9ucyAyLCAxMSwgMTksIGFuZCAyOAppIDwtIGMoMiwxMSwxOSwyOCkKdltpXQpgYGAKCiMjIFRlc3RpbmcgUHJlZGljYXRlIEV4cHJlc3Npb25zCgpJdCBpcyBwb3NzaWJsZSBpbiBSIHRvIGFwcGx5IGEgcHJlZGljYXRlIGV4cHJlc3Npb24gdG8gZXZlcnkgZWxlbWVudCBpbiBhIHZlY3Rvci4gVGhpcyBnZW5lcmF0ZXMgYSAiQm9vbGVhbiB2ZWN0b3IiIG9mICpUUlVFL0ZBTFNFKiB2YWx1ZXMgdGhhdCBpbmRpY2F0ZSB3aGljaCBlbGVtZW50IG1hdGNoZXMgdGhlIHByZWRpY2F0ZSBleHByZXNzaW9uICgqVFJVRSopIGFuZCB3aGljaCBkb2Vzbid0ICgqRkFMU0UqKS4KClByZWRpY2F0ZSBleHByZXNzaW9ucyBhcmUgYnVpbHQgd2l0aCBsb2dpY2FsIG9wZXJhdG9ycyAoXDwsIFw+LCBcPD0sIFw+PSwgPT0sICE9KQoKYGBge3J9CnYgPCA1CgoodiA8IDEgfCB2ID4gOSkKCih2IDw9IDcgJiB2ICE9IDMpCgp2ICE9IDUKCmwgPC0gKHYgPT0gNSkKcHJpbnQobCkKYGBgCgojIyBGaW5kaW5nIE1hdGNoZXMKClRoZSA8Y29kZT53aGljaCgpPC9jb2RlPiBmdW5jdGlvbiByZXR1cm5zIHRoZSBwb3NpdGlvbnMgdGhhdCBhcmUgKlRSVUUqIGluIGEgQm9vbGVhbiB2ZWN0b3IuCgpgYGB7cn0KIyByZXR1cm5zIHBvc2l0aW9ucyBvZiB2ZWN0b3IgdGhhdCBtYXRjaGVzIHByZWRpY2F0ZSBleHByZXNzaW9uCndoaWNoKHYgIT0gNSkKCiMgY291bnQgdGhlIG51bWJlciBvZiBtYXRjaGVzCmxlbmd0aCh3aGljaCh2ICE9IDApKQpgYGAKCmBgYHtyfQpwIDwtIHdoaWNoKHYgPCA1KQpwcmludCAodltwXSkKCiMgb3IgY29tYmluZQp4IDwtIHZbd2hpY2godiA8IDUpXQpwcmludCAoeCkKCiMgZmluZCBhbGwgdGhhdCBhcmUgbm90IGluIHZlY3Rvcgpub3QueCA8LSB2Wy13aGljaCh2IDwgNSldCnByaW50ICh4KQpgYGAKCiMjIERldGVybWluaW5nIEFueSBNYXRjaGVzCgpUbyBkZXRlcm1pbmUgaWYgdGhlcmUgYXJlIGFueSBtYXRjaGVzLCAqaS5lLiosIGF0IGxlYXN0IG9uZSBlbGVtZW50IGluIGEgdmVjdG9yIG1hdGNoZXMgdGhlIHByZWRpY2F0ZSBleHByZXNzaW9uLCB1c2UgdGhlIDxjb2RlPmFueSgpPC9jb2RlPiBmdW5jdGlvbi4gVGhlIGZ1bmN0aW9uIDxjb2RlPmFueSgpPC9jb2RlPiByZXR1cm5zICpUUlVFKiBpZiB0aGVyZSdzIGF0IGxlYXN0IG9uZSBtYXRjaCwgKkZBTFNFKiBvdGhlcndpc2UuCgpgYGB7cn0KYW55KHYgPCA1KQpgYGAKCiMjIERlYWxpbmcgd2l0aCBNaXNzaW5nIFZhbHVlcwoKTWlzc2luZyB2YWx1ZXMgaW4gUiBhcmUgZ2VuZXJhbGx5IGVuY29kZWQgd2l0aCB0aGUgc3BlY2lhbCB2YWx1ZSAqKk5BKiouICoqTkEqKiBpcyBub3QgYSBudW1iZXIsIG5vdCBhIGNoYXJhY3RlciBvciB0ZXh0LCBhbmQgbm90IGEgQm9vbGVhbi4gQ29uc2VxdWVudGx5LCB1c2luZyAqPT0qIG9yICohPSogdG8gY2hlY2sgaWYgYSB2YWx1ZSBpcyAqKk5BKiogZG9lcyBub3Qgd29yayBhbmQgcmVzdWx0cyBpbiBhbiBlcnJvci4gWW91IG11c3QgdXNlIHRoZSBmdW5jdGlvbiA8Y29kZT5pcy5uYSgpPC9jb2RlPiB0byBjaGVjayBpZiBhIHZhbHVlIGlzICoqTkEqKi4gVGhpcyBpcyBzaW1pbGFyIHRvICoqTlVMTCoqIGluIFNRTCBhbmQgbWFueSBwcm9ncmFtbWluZyBsYW5ndWFnZXMuCgpgYGB7cn0KIyBjb3B5IHRoZSB2ZWN0b3Igb2YgcmFuZG9tIG51bWJlcnMgYW5kIHRoZW4gcmFuZG9tbHkgCiMgcmVtb3ZlIDYgdmFsdWVzLCBpLmUuLCBzZXQgdGhlbSB0byBOQQoKdi5uYSA8LSB2Cgp2Lm5hW3JvdW5kKHJ1bmlmKDYsIG1pbiA9IDEsIG1heCA9IGxlbmd0aCh2Lm5hKSksIDApXSA9IE5BCgpwcmludCh2Lm5hKQpgYGAKCk1hbnkgb2YgdGhlIGZ1bmN0aW9ucyBpbiBSIGRvIG5vdCB3b3JrIHdoZW4gYSB2YWx1ZSBpbiBhIHZlY3RvciBpcyAqKk5BKiouIERvaW5nIHNvIHJlc3VsdHMgaW4gYSB2YWx1ZSBvZiAqKk5BKiouCgpgYGB7cn0KIyBjYW5ub3QgYWRkIHZhbHVlcyBjb250YWluaW5nIE5BCnN1bSh2Lm5hKQoKIyB3aGVuIGFwcGx5aW5nIG9wZXJhdG9ycywgTkEgcmVtYWlucyBOQQp2Lm5hICsgNQpgYGAKClNvbWUgZnVuY3Rpb25zIGhhdmUgYSBwYXJhbWV0ZXIgdGhhdCBhbGxvd3MgeW91IHRvIGRpcmVjdCBhIGZ1bmN0aW9uIHRvIGlnbm9yZSAqKk5BKiogdmFsdWVzLiBDaGVjayB0aGUgZG9jdW1lbnRhdGlvbiBvZiBmdW5jdGlvbnMgYmVmb3JlIHVzaW5nIHRoZW0gdG8gc2VlIHdoYXQgcGFyYW1ldGVycyB0aGV5IHN1cHBvcnQuIFVzZSAqP3N1bSogdG8gdmlldyB0aGUgZG9jdW1lbnRhdGlvbiBvZiB0aGUgKnN1bSogZnVuY3Rpb24uCgojIyBBY2Nlc3NpbmcgUm93cywgQ29sdW1ucywgYW5kIEVsZW1lbnRzIChDZWxscykgb2YgYSBEYXRhIEZyYW1lCgpEYXRhIGZyYW1lcyBhcmUgdmVyeSBzaW1pbGFyIHRvIHRhYmxlcyBpbiByZWxhdGlvbmFsIGRhdGFiYXNlcyBhbmQgc3ByZWFkc2hlZXRzLiBUaGV5IGhhdmUgcm93cyBhbmQgY29sdW1ucyBhbmQgdGhlIGludGVyc2VjdGlvbiBvZiBhIHJvdyBhbmQgY29sdW1uIGlzIGEgY2VsbCAob3IgZWxlbWVudCkuIFRoZSBvcmRlciBvZiBhY2Nlc3MgaXMgcm93IGZvbGxvd2VkIGJ5IGNvbHVtbiwgKmUuZy4qLCB0aGUgdGhpcmQgZWxlbWVudCBpbiB0aGUgZm91cnRoIHJvdyBvZiB0aGUgZGF0YSBmcmFtZSA8Y29kZT5tdGNhcnM8L2NvZGU+IGlzIDxjb2RlPm10Y2Fyc1s0LDNdPC9jb2RlPi4gTm90ZSB0aGF0IHRoaXMgaXMgcmV2ZXJzZWQgZnJvbSB0aGUgd2F5IEV4Y2VsIGFuZCBvdGhlciBzcHJlYWRzaGVldHMgd29yay4KClRoZSBleGFtcGxlIGNvZGUgYmVsb3cgdXNlcyB0aGUgYnVpbHQtaW4gZGF0YSBmcmFtZSAqbXRjYXJzKi4gWW91IGNhbiBmaW5kIG91dCBtb3JlIGFib3V0IGl0cyBzdHJ1Y3R1cmUgdXNpbmcgPGNvZGU+c3RyKG10Y2Fycyk8L2NvZGU+IG9yIGRpc3BsYXlpbmcgdGhlIGZpcnN0IGZldyByb3dzIHdpdGggPGNvZGU+aGVhZChtdGNhcnMpPC9jb2RlPi4KCmBgYHtyfQpzdHIobXRjYXJzKQpoZWFkKG10Y2FycywgMykKYGBgCgpgYGB7cn0KdiA8LSBtdGNhcnNbNCwzXQp4ID0gbXRjYXJzWzQsM10KCnByaW50KHBhc3RlMCgidiA9ICIsdiwiIGFuZCB4ID0gIix4KSkKYGBgCgpMZWF2aW5nIG91dCBhIGRpbWVuc2lvbiAocm93IG9yIGNvbHVtbikgYWNjZXNzZXMgdGhlIGVudGlyZSByb3cgb3IgY29sdW1uLiBUaGUgcmVzdWx0YW50IGlzIGEgZGF0YSBmcmFtZSB3aXRoIGEgc2luZ2xlIHJvdy4KCk9mdGVuIHRoZSB2YWx1ZXMgbXVzdCBiZSBjb252ZXJ0ZWQgdG8gYSB2ZWN0b3IgZGF0YSB0eXBlLiBDb252ZXJzaW9ucyBvZiB2YXJpYWJsZXMgZnJvbSBvbmUgdHlwZSB0byBhbm90aGVyIGlzIGRvbmUgd2l0aCB0aGUgZmFtaWx5IG9mIDxjb2RlPmFzLnh4eHg8L2NvZGU+IGZ1bmN0aW9ucywgKmUuZy4qLCA8Y29kZT5hcy52ZWN0b3I8L2NvZGU+LCA8Y29kZT5hcy5udW1lcmljPC9jb2RlPiwgb3IgPGNvZGU+YXMuZmFjdG9yPC9jb2RlPi4gVmVjdG9ycyBjYW4gY29udGFpbiBudW1lcmljIG9yIGNoYXJhY3RlciBkYXRhIGJ1dCBhbGwgZWxlbWVudHMgbXVzdCBiZSBvZiB0aGUgc2FtZSB0eXBlLiBJbiBSLCBhIGxpc3QgaXMgc2ltaWxhciB0byBhIHZlY3RvciBidXQgaXQgbWF5IGNvbnRhaW4gYSBtaXggb2YgZWxlbWVudHMuIEEgbWF0cml4IGlzIHNpbWlsYXIgdG8gYSBkYXRhIGZyYW1lIGJ1dCBpdCBjYW4gb25seSBjb250YWluIG51bWJlcnMgYW5kIGl0IGNhbiBoYXZlIG1vcmUgdGhhbiB0d28gZGltZW5zaW9ucy4KClNvbWUgZnVuY3Rpb25zIGV4cGVjdCBkYXRhIGZyYW1lcywgc29tZSB2ZWN0b3JzLCBzb21lIGxpc3RzLiBZb3UgbmVlZCB0byByZWFkIHRoZSBkb2N1bWVudGF0aW9uIG9mIGEgZnVuY3Rpb24gdG8gZmluZCBvdXQuIEZ1cnRoZXJtb3JlLCBzb21lIGZ1bmN0aW9ucyB3aWxsIGF1dG9tYXRpY2FsbHkgY29udmVydCBhIHZhcmlhYmxlIGZyb20gb25lIHR5cGUgdG8gdGhlIG9uZSBpdCByZXF1aXJlcy4KCllvdSBjYW4gYWxzbyBhY2Nlc3MgYSBjb2x1bW4gaW4gZGF0YSBmcmFtZSBieSBpdHMgY29sdW1uIG5hbWUuIEZvciBhbiBlbnRpcmUgY29sdW1uIHlvdSBlaXRoZXIgdXNlIHRoZSBjb2x1bW5zIHBvc2l0aW9uIG9yIGl0cyBuYW1lOiA8Y29kZT5kZlssY29sdW1uXTwvY29kZT4gb3IgPGNvZGU+ZGZcJGNvbHVtbk5hbWU8L2NvZGU+LgoKYGBge3J9CiMgYWxsIG9mIHJvdyA0OyB0aGUgcmVzdWx0IGlzIGEgZGF0YSBmcmFtZQpyIDwtIG10Y2Fyc1s0LF0Kc3VtKHIpCgpjIDwtIG10Y2Fyc1szLF0KY1sxLDNdCgptdGNhcnNbYygxLDQpXSAgICMgY29sdW1ucyAxIGFuZCA0IGFzIGEgbmV3IGRhdGFmcmFtZQoKbXRjYXJzWywyXSAgICAgICAjIGFsbCBvZiBjb2x1bW4gMgptdGNhcnNbNTo3LF0gICAgICMgcm93cyA1IHRvIDcgYXMgYSBuZXcgZGF0YWZyYW1lCm10Y2FycyRjeWwgICAgICAgIyBjb2x1bW4gbmFtZWQgImN5bCIKbXRjYXJzJGN5bFsyXSAgICAjIDJuZCByb3cgaW4gdGhlIGNvbHVtbiAiY3lsIgoKbXRjYXJzJGN5bFszOjldICAjIHJvd3MgMyB0byA5IGZvciBjb2x1bW4gImN5bCIgYXMgYSB2ZWN0b3IKCncgPC0gbXRjYXJzJG1wZwptZWFuKHcpCmBgYAoKIyMgQWRkaW5nIGFuZCBSZW1vdmluZyBDb2x1bW5zIGZyb20gYSBEYXRhIEZyYW1lCgpUbyBhZGQgYSBuZXcgY29sdW1uLCB5b3Ugc2ltcGx5ICJhY2Nlc3MiIHRoZSBjb2x1bW4gb3IgdXNlIGEgbmV3IG5hbWUgZm9yIHRoZSBjb2x1bW4uIE5vdGUgaW4gdGhlIGV4YW1wbGUgYmVsb3cgdGhhdCB5b3UgY2FuIG9wZXJhdGUgb24gZW50aXJlIGNvbHVtbnMgKGFzIHZlY3RvcnMpIGFuZCB0aGUgb3BlcmF0aW9uIGlzIGFwcGxpZWQgdG8gZWFjaCBwYWlyIG9mIHZhbHVlcyBpbiB0aGUgdHdvIHZlY3RvcnMgaW4gdGhlIG9wZXJhdGlvbi4gVGhpcyBpcyBtdWNoIG1vcmUgZWZmaWNpZW50IHRoYW4gdXNpbmcgbG9vcHMgYXMgaXMgbmVjZXNzYXJ5IGluIG90aGVyIHByb2dyYW1taW5nIGxhbmd1YWdlcy4KCmBgYHtyfQojIGNvcHkgdGhlIGRhdGEgZnJhbWUgbXRjYXJzIHRvIGEgbmV3IGRhdGEgZnJhbWUgZGYKZGYgPC0gbXRjYXJzCgojIGNyZWF0ZSBhIG5ldyBjb2x1bW4gImRpc3BjeWwiIHdoaWNoIGlzIHRoZSBkaXNwbGFjZW1lbnQgcGVyIGN5bGluZGVyCmRmJGRpc3BjeWwgPC0gZGYkZGlzcCAvIGRmJGN5bAoKaGVhZChkZikKYGBgCgojIyBDcmVhdGUgYSBOZXcgRGF0YSBGcmFtZQoKRGF0YSBmcmFtZXMgYXJlIGNyZWF0ZWQgaW4gdmFyaW91cyB3YXlzOiB1c2UgdGhlIFw8Y29kZVw8XD5kYXRhLmZyYW1lPC9jb2RlPiBmdW5jdGlvbiwgbG9hZCBhIENTViBmaWxlLCBleGVjdXRlIGEgU1FMIHF1ZXJ5LCBvciBhcyBhIHJlc3VsdCBvZiBtYW55IHBhY2thZ2UgZnVuY3Rpb25zLgoKIyMjIExvYWQgYSBEYXRhIEZyYW1lIGZyb20gQ1NWCgpRdWljayBub3RlOiBDYXBpdGFsaXphdGlvbiBpbiBwYXRoIGFuZCBmaWxlIG5hbWVzIGRvZXMgbm90IG1hdHRlciBpbiBXaW5kb3dzLCBidXQgKipkb2VzIG1hdHRlcioqIG9uIE1hY09TIGFuZCBMaW51eC4gRnVydGhlcm1vcmUsIG5vdGUgdGhhdCBldmVuIGluIFdpbmRvd3MgdGhlIHBhdGggZGVsaW1pdGVyIGlzIGEgZm9yd2FyZCBzbGFzaCAvIGFuZCBub3QgdGhlIHVzdWFsIGJhY2t3YXJkcyBzbGFzaCBcXC4gVGhlIFxcIGlzIGFuICJlc2NhcGUiIGNoYXJhY3RlciBhbmQgdXNlZCB0byBpbmplY3Qgbm9uLXByaW50YWJsZSBjaGFyYWN0ZXJzIGludG8gYSBzdHJpbmcgKHRleHQpLCAqZS5nLiosICJUaGlzIHN0cmluZyBjb250YWlucyJxdW90ZXMiLiIgd2hpY2ggd291bGQgYmUgd3JpdHRlbiBpbiBSIGFzICJ0aGlzIHN0cmluZyBjb250YWlucyBcXCJxdW90ZXNcXCIuIgoKVGhlIHBhcmFtZXRlciA8Y29kZT5oZWFkZXIgPSBGPC9jb2RlPiBpbnN0cnVjdHMgPGNvZGU+cmVhZC5jc3Y8L2NvZGU+IG5vdCB0byBpbnRlcnByZXQgdGhlIGZpcnN0IGxpbmUgYXMgaGVhZGVyIGxhYmVscy4gT2YgY291cnNlLCBpZiB0aGVyZSBhcmUgbm8gbGFiZWxzLCB0aGVuIHlvdSBuZWVkIHRvIGRlZmluZSB5b3VyIG93bi4KCkFzaWRlIGZyb20gQ1NWIGZpbGVzLCBSIGNhbiBhbHNvIGxvYWQgYSBudW1iZXIgb2Ygb3RoZXIgZmlsZSBmb3JtYXQgdXNpbmcgdmFyaW91cyBwYWNrYWdlcywgaW5jbHVkaW5nIFhNTCwgRXhjZWwsIFNQU1MsIE1hdExhYiwgYW1vbmcgbWFueSBvdGhlcnMuCgpgYGB7cn0KZGYgPC0gcmVhZC5jc3YoZmlsZSA9ICJjdXN0b21lcnR4bmRhdGEuY3N2IiwgaGVhZGVyID0gRikKaGVhZChkZikKCmRmIDwtIHJlYWQuY3N2KGZpbGUgPSAiY3VzdG9tZXJ0eG5kYXRhLmNzdiIsIAogICAgICAgICAgICAgICBoZWFkZXIgPSBGLAogICAgICAgICAgICAgICBjb2wubmFtZXMgPSBjKCJudW1WaXNpdHMiLCJOdW1UeG4iLCJPUyIsIkdlbmRlciIsIlRvdFNwIikpCmhlYWQoZGYpCmBgYAoKPiBOb3RlIHRoYXQgdGhlIHZhbHVlIG9mIHRoZSAnTWFsZScgY29sdW1uIGluIHRoZSBmaXJzdCByb3cgaXMgKk5BKiB3aGljaCBpcyB0aGUgd2F5IHRoYXQgUiBpbmRpY2F0ZXMgYSBtaXNzaW5nIGRhdGEgdmFsdWUuIEl0IGlzIG5vdCAwIG9yIGFuIGVtcHR5IHN0cmluZywgaXQgaXMgdW5rbm93bi4gU28sIHN0YXRpc3RpY2FsIGZ1bmN0aW9ucyBhbmQgYWxnZWJyYWljIG9wZXJhdGlvbnMgd291bGQgcmVzdWx0IGluIGFuICpOQSogYXMgd2VsbC4KCiMjIyMgU3RyaW5ncyB2cyBGYWN0b3JzCgpUaGUgKmZhY3RvciogZGF0YSB0eXBlIGVuY29kZXMgY2F0ZWdvcmljYWwgZGF0YSwgKmUuZy4qLCB0aGUgdmFsdWUgb2YgYSB2YXJpYWJsZSBpcyBvbmUgb2YgYSBmaXhlZCB2YWx1ZSBzZXQuIE1hbnkgc3RhdGlzdGljYWwgZnVuY3Rpb25zIGluIFIgcmVxdWlyZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgdG8gYmUgb2YgdHlwZSAqZmFjdG9yKi4gSG93ZXZlciwgb2Z0ZW4sIGR1cmluZyBkYXRhIHByb2Nlc3NpbmcsIHdlIG5lZWQgdGhlIGFjdHVhbCB0ZXh0IHJhdGhlciB0aGFuIGhhdmluZyBpdCBlbmNvZGVkIGFzIGEgKmZhY3RvciogKHdoaWNoIGlzIGFjdHVhbGx5IHN0b3JlZCBpbiBSIGFzIGFuIGludGVnZXIgZm9yIGVmZmljaWVuY3kpLiBTbywgd2hlbiByZWFkaW5nIGEgQ1NWIGZpbGUgeW91IG5lZWQgdG8gZGVjaWRlIGlmIHlvdSB3YW50IHRleHQgY29sdW1ucyB0byBiZSBjaGFyYWN0ZXIgc3RyaW5ncyBvciBmYWN0b3JzIGJ5IHNldHRpbmcgdGhlIDxjb2RlPnN0cmluZ3NBc0ZhY3RvcnM8L2NvZGU+IHBhcmFtZXRlci4KCllvdSBtYXkgdXNlIGVpdGhlciA8Y29kZT5GPC9jb2RlPiBhbmQgPGNvZGU+VDwvY29kZT4gb3IgPGNvZGU+RkFMU0U8L2NvZGU+IGFuZCA8Y29kZT5UUlVFPC9jb2RlPi4KCmBgYHtyfQpkZiA8LSByZWFkLmNzdihmaWxlID0gImN1c3RvbWVydHhuZGF0YS5jc3YiLCAKICAgICAgICAgICAgICAgaGVhZGVyID0gRiwKICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLAogICAgICAgICAgICAgICBjb2wubmFtZXMgPSBjKCJudW1WaXNpdHMiLCJOdW1UeG4iLCJPUyIsIkdlbmRlciIsIlRvdFNwIikpCmhlYWQoZGYpCmBgYAoKIyMjIENyZWF0ZSBhIG5ldyBEYXRhIEZyYW1lCgpUaGUgY29kZSBiZWxvdyBjcmVhdGVzIGEgbmV3IGRhdGEgZnJhbWUgZnJvbSBjb2x1bW4gdmVjdG9ycy4gTm90aWNlIGhvdyB0aGUgY29sdW1uIG5hbWVzIGFyZSB0aGUgbmFtZXMgb2YgdGhlIHZlY3RvcnMuIEEgbmV3IHZlY3RvciBpcyBjcmVhdGVkIHdpdGggdGhlIDxjb2RlPmM8L2NvZGU+IGZ1bmN0aW9uLCBlLmcuLCA8Y29kZT52IFw8LSBjKDMsNSwxLDkpPC9jb2RlPi4KCmBgYHtyfQpkZjEgPC0gZGF0YS5mcmFtZShzdGF0ZSA9IGMoJ0FyaXpvbmEnLCdHZW9yZ2lhJywgJ05ldyBZb3JrJywnSW5kaWFuYScsJ1dhc2hpbmd0b24nLCdUZXhhcycpLAogICAgICAgICAgICAgICAgICBjb2RlID0gYXMuZmFjdG9yKGMoJ0FaJywnR0EnLCdOWScsJ0lOJywnV0EnLCdUWCcpKSwKICAgICAgICAgICAgICAgICAgc2NvcmUgPSBjKDYyLDQ3LDU1LDc0LDMxLDg1KSkKCmhlYWQoZGYxKQoKYGBgCgojIyBTZWFyY2ggRGF0YSBGcmFtZXMKClRoZXJlIGFyZSB0d28gaW1wb3J0YW50IGZ1bmN0aW9ucyBmb3IgInNlYXJjaGluZyIgZGF0YSBmcmFtZXM6IDxjb2RlPndoaWNoPC9jb2RlPiBhbmQgPGNvZGU+YW55PC9jb2RlPi4gVGhlIGNvZGUgYmVsb3cgdXNlcyB0aGUgYnVpbHQtaW4gWyoqT3JhbmdlKiogZGF0YSBmcmFtZV0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2RhdGFzZXRzL3ZlcnNpb25zLzMuNi4yL3RvcGljcy9PcmFuZ2UpIHdoaWNoIGNvbnRhaW5zIG1lYXN1cmVtZW50cyBvZiBvcmFuZ2UgdHJlZXMuIEl0IGhhcyB0aHJlZSBjb2x1bW5zOiB0aGUgdHJlZSwgdGhlICphZ2UqIG9mIHRoZSB0cmVlIChkYXlzIHNpbmNlIDE5NjgvMTIvMzEpLCBhbmQgKmNpcmN1bWZlcmVuY2UqIChpbiAqbW0qKS4KCiMjIyB3aGljaAoKYGBge3J9CmRmIDwtIE9yYW5nZQoKaGVhZChkZikKCiMgZmluZCBhbGwgcm93cyB3aGVyZSB0aGUgY2lyY3VtZmVyZW5jZSBpcyBtb3JlIHRoYW4gMjAwbW0KcnMgPC0gd2hpY2goZGYkY2lyY3VtZmVyZW5jZSA+IDIwMCkKCiMgZGlzcGxheSBhbGwgcm93cyB3aGVyZSB0aGUgY2lyY3VtZmVyZW5jZSBpcyBtb3JlIHRoYW4gMjAwbW0KZGZbcnMsXQoKIyBjb21wb3VuZCBjb25kaXRpb25zIGFyZSBwb3NzaWJsZSB3aXRoICYgKGFuZCksIHwgKG9yKSwgYW5kICEgKG5vdCkKcnMyIDwtIHdoaWNoKGRmJGNpcmN1bWZlcmVuY2UgPiAyMDAgJiBkZiRhZ2UgPCAxNTAwKQpyczMgPC0gd2hpY2goZGYkY2lyY3VtZmVyZW5jZSA8IDIwMCB8ICEoZGYkYWdlIDwgMTUwMCkpCnJzNCA8LSB3aGljaChkZiRjaXJjdW1mZXJlbmNlID4gNDAwIHwgZGYkYWdlID4gMTUwMCkKCnJzMgpyczMKcnM0CgptZWFuKGRmW3JzNCwyXSkKbWVhbihkZiRhZ2VbcnMzXSkKCmBgYAoKSW4gdGhlIGFib3ZlIGV4YW1wbGUgPGNvZGU+cnMgXDwtIHdoaWNoKGRmXCRjaXJjdW1mZXJlbmNlIFw+IDIwMCk8L2NvZGU+IGZpbmRzIGFsbCByb3dzIGluIHRoZSBkYXRhIGZyYW1lICpkZiogd2hlcmUgKmNpcmN1bWZlcmVuY2UgXD4gMjAwKi4gVGhlIHJvd3MgYXJlIHNhdmVkIGluICpycyouCgojIyMgYW55CgpUaGUgPGNvZGU+YW55PC9jb2RlPiBmdW5jdGlvbiByZXR1cm5zICRUUlVFJCBvciAkRkFMU0UkIGRlcGVuZGluZyBvbiB3aGV0aGVyIGFueSBjb2x1bW4gKG9yIHJvdykgaW4gdGhlIGRhdGFmcmFtZSBzYXRpc2ZpZXMgYSBCb29sZWFuIGV4cHJlc3Npb24uCgpgYGB7cn0KIyBpcyB0aGVyZSBhbnkgdHJlZSB3aXRoIGFnZSA+IDIwMDA/CmFueShkZiRhZ2UgPiAyNSkKYGBgCgojIyBNZW1vcnkgTWFuYWdlbWVudAoKUiBpcyBzaW1pbGFyIHRvIFB5dGhvbiBhbmQgb3RoZXIgaW50ZXJwcmV0ZWQgbGFuZ3VhZ2VzIGluIHRlcm1zIG9mIG1lbW9yeSBtYW5hZ2VtZW50LiBPYmplY3RzIGFuZCB2YXJpYWJsZXMgcmVtYWluIGluIG1lbW9yeSB1bnRpbCB5b3UgcmVzdGFydCBSIG9yIGV4cGxpY2l0bHkgZGVsZXRlIHRoZW0uIFRoaXMgY2FuIHNvbWV0aW1lcyBjYXVzZSBjb25mbGljdHMgZHVyaW5nIGRldmVsb3BtZW50LiBBZGRpbmcgdGhpcyB0byB0aGUgc3RhcnQgb2YgYW4gUiBzY3JpcHQgb3IgYW4gUiBOb3RlYm9vayBlbnN1cmVzIHRoYXQgdGhlIHByb2dyYW0gcnVucyB3aXRoIGFuIGVtcHR5IG1lbW9yeSBlbnZpcm9ubWVudC4gVGhpcyBpcyBjcml0aWNhbCBmb3IgbGFuZ3VhZ2VzIGxpa2UgUiBhbmQgUHl0aG9uLCBidXQgaXMgbm90IG5lZWRlZCBmb3IgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzIHRoYXQgcnVuIGluIHNlcGFyYXRlIHByb2Nlc3NlcyBzdWNoIGFzIEphdmEgYW5kIEMrKyBwcm9ncmFtcy4KClVzZSB0aGUgY29kZSBiZWxvdyB0byBmaW5kIGFuZCB0aGVuIGRlbGV0ZSBhbGwgb2JqZWN0cywgYW5kIHJlY2xhaW0gbWVtb3J5LiBUaGUgZnVuY3Rpb24gPGNvZGU+bHMoKTwvY29kZT4gbGlzdHMgYWxsIG9iamVjdHMgKHZhcmlhYmxlcykgYnkgbmFtZSwgd2hpbGUgdGhlIDxjb2RlPnJtKCk8L2NvZGU+IHJlbW92ZXMgb25lIG9yIG1vcmUgb2JqZWN0cyBmcm9tIG1lbW9yeS4gRmluYWxseSwgdGhlIGZ1bmN0aW9uIDxjb2RlPmdjKCk8L2NvZGU+IHJ1bnMgdGhlIGdhcmJhZ2UgY29sbGVjdG9yIGFuZCByZXR1cm5zIGZyZWVkIG1lbW9yeSB0byB0aGUgdXNhYmxlIG1lbW9yeSBwb29sIGZvciB0aGUgcHJvY2VzcyBpbiB3aGljaCBSIGlzIHJ1bm5pbmcuCgpgYGB7ciBjbGVhbk1lbSwgZWNobz1ULCBldmFsPUZ9CnJtKGxpc3QgPSBscyhhbGwubmFtZXMgPSBUUlVFKSkKZ2MoKQpgYGAKCk9mIGNvdXJzZSwgcmF0aGVyIHRoYW4gZGVsZXRpbmcgYWxsIG9iamVjdHMgYXMgaW4gdGhlIGNvZGUgY2h1bmsgYWJvdmUsIHlvdSBtYXkgd2lzaCB0byByZWxlYXNlIGxhcmdlIG9iamVjdHMgb3IgdW51c2VkIG9iamVjdHMgc2VsZWN0aXZlbHkgYnkgdGhlaXIgbmFtZSwgKmUuZy4qLCA8Y29kZT5ybSgib2JqTmFtZSIpPC9jb2RlPi4KCiMjIEluc3RhbGwgUGFja2FnZXMgb24gRGVtYW5kCgpUbyBtYWtlIHlvdXIgY29kZSBwb3J0YWJsZSBhbmQgcmVwcm9kdWNpYmxlLCBpbnN0YWxsIHBhY2thZ2VzIHdpdGhpbiB5b3VyIGNvZGU6CgpgYGB7cn0KIyBwYWNrYWdlcyBuZWVkZWQgaW4gUiBwcm9ncmFtCnBhY2thZ2VzIDwtIGMoInN0cmluZ3IiLCAiUlNRTGl0ZSIpCgojIGluc3RhbGwgcGFja2FnZXMgbm90IHlldCBpbnN0YWxsZWQKaW5zdGFsbGVkX3BhY2thZ2VzIDwtIHBhY2thZ2VzICVpbiUgcm93bmFtZXMoaW5zdGFsbGVkLnBhY2thZ2VzKCkpCmlmIChhbnkoaW5zdGFsbGVkX3BhY2thZ2VzID09IEZBTFNFKSkgewogIGluc3RhbGwucGFja2FnZXMocGFja2FnZXNbIWluc3RhbGxlZF9wYWNrYWdlc10pCn0KCiMgbG9hZCBhbGwgcGFja2FnZXMgYnkgYXBwbHlpbmcgJ2xpYnJhcnknIGZ1bmN0aW9uCmludmlzaWJsZShsYXBwbHkocGFja2FnZXMsIGxpYnJhcnksIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpCmBgYAoKIyMgQ29uY2x1c2lvbgoKQXMgeW91IHNhdywgUiBpcyBub3QgYSBkaWZmaWN1bHQgbGFuZ3VhZ2UgdG8gbGVhcm4gYXMgaXQgaXMgc2ltaWxhciB0byBvdGhlciBsYW5ndWFnZXMgYW5kIGZvciBtb3N0IGxhbmd1YWdlIGNvbnN0cnVjdHMgdGhhdCB5b3UgYXJlIGZhbWlsaWFyIHdpdGgsIHRoZXJlIGlzIGFuIGVxdWl2YWxlbnQuIEJ1dCBpdCBpcyBpbXBvcnRhbnQgdGhhdCB5b3UgZ28gYmV5b25kIHRoaXMgdHV0b3JpYWwgYW5kIGxlYXJuIHRoZSAiUiB3YXkiIG9mIHByb2dyYW1taW5nIHVzaW5nIHZlY3Rvcml6ZWQgb3BlcmF0aW9ucy4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgRmlsZXMgJiBSZXNvdXJjZXMKCmBgYHtyIHppcEZpbGVzLCBlY2hvPUZBTFNFfQp6aXBOYW1lID0gc3ByaW50ZigiTGVzc29uRmlsZXMtJXMtJXMuemlwIiwgCiAgICAgICAgICAgICAgICAgcGFyYW1zJGNhdGVnb3J5LAogICAgICAgICAgICAgICAgIHBhcmFtcyRudW1iZXIpCgp0ZXh0QUxpbmsgPSBwYXN0ZTAoIkFsbCBGaWxlcyBmb3IgTGVzc29uICIsIAogICAgICAgICAgICAgICBwYXJhbXMkY2F0ZWdvcnksIi4iLHBhcmFtcyRudW1iZXIpCgojIGRvd25sb2FkRmlsZXNMaW5rKCkgaXMgaW5jbHVkZWQgZnJvbSBfaW5zZXJ0MkRCLlIKa25pdHI6OnJhd19odG1sKGRvd25sb2FkRmlsZXNMaW5rKCIuIiwgemlwTmFtZSwgdGV4dEFMaW5rKSkKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIFJlZmVyZW5jZXMKCk5vIHJlZmVyZW5jZXMuCgojIyBFcnJhdGEKCk5vbmUgY29sbGVjdGVkIHlldC4gTGV0IHVzIGtub3cuCgpgYGB7PWh0bWx9CjxzY3JpcHQgc3JjPSJodHRwczovL2Zvcm0uam90Zm9ybS5jb20vc3RhdGljL2ZlZWRiYWNrMi5qcyIgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij4KICBuZXcgSm90Zm9ybUZlZWRiYWNrKHsKICAgIGZvcm1JZDogIjIxMjE4NzA3Mjc4NDE1NyIsCiAgICBidXR0b25UZXh0OiAiRmVlZGJhY2siLAogICAgYmFzZTogImh0dHBzOi8vZm9ybS5qb3Rmb3JtLmNvbS8iLAogICAgYmFja2dyb3VuZDogIiNGNTkyMDIiLAogICAgZm9udENvbG9yOiAiI0ZGRkZGRiIsCiAgICBidXR0b25TaWRlOiAibGVmdCIsCiAgICBidXR0b25BbGlnbjogImNlbnRlciIsCiAgICB0eXBlOiBmYWxzZSwKICAgIHdpZHRoOiA3MDAsCiAgICBoZWlnaHQ6IDUwMCwKICAgIGlzQ2FyZEZvcm06IGZhbHNlCiAgfSk7Cjwvc2NyaXB0PgpgYGAKYGBge3IgY29kZT14ZnVuOjpyZWFkX3V0ZjgocGFzdGUwKGhlcmU6OmhlcmUoKSwnL1IvX2RlcGxveUtuaXQuUicpKSwgaW5jbHVkZSA9IEZBTFNFfQpgYGAK