Learning Objectives
- Get some basic familiarity with R and RStudio
- Learn why and how to use RStudio Projects
- Understand objects, functions, and how to use them
- Understand the concepts of
vector
and data.frame
- Explore the structure and the content of a
data.frame
- Learn about how R handles missing data
Before We Get Started
Why R?
R is a versatile, open source programming/scripting language that’s particularly useful for statistics and data visualization.
Yes, there is a learning curve, and many of us just want to get on with our analysis –
but investing in learning R will pay off:
R gives you greater flexibility to do anything you want.
A greater reproducibility of scripting vs clicking.
R is highly interdisciplinary – e.g. very useful for analyzing sequencing data
but can also be used to create maps and perform GIS analyses, and so on!
R is more than a platform to perform analysis. When combined with Markdown (a very simple text markup language), you can easily use R to produce reports that integrate code, results, and text, as well as for websites, slide decks, books, and so on!
Furthermore, R:
Getting Started
Start an RStudio Server session at OSC
- Your job should start running pretty soon, and when it’s ready the box should look like this:
- Click
Connect to RStudio Server
at the bottom of the box, and an RStudio Server instance will open. You’re ready to go!
R vs. RStudio
R simply provides a “console” to type your commands. However, because you want to save your sequences of commands in scripts, regularly examine output such a graphics, and so on, you can work much more effectively in an environment that provides all of this side-by-side.
We will use RStudio, an excellent “Integrated Development Environment” (IDE) for R. In RStudio, we have a single interface to write code, navigate the files found on our computer, inspect the objects we create, and visualize the plots we generate.
RStudio is divided into 4 “panes”:
- Top-left: The editor for your scripts and documents.
- Bottom-left: The R console.
- Top-right: Your environment/history.
- Bottom-left: Your files/plots/packages/help/viewer.
The placement of these panes and their content can be customized.
Interacting with R
R as a calculator
The lower-left RStudio pane, i.e. the R console, is where you can interact with R directly.
The >
sign is the R “prompt”. It indicates that R is ready for you to type something.
Let’s start by performing a division:
203 / 2.54
## [1] 79.92126
R does the calculation and prints the result, and then you get the >
prompt again. (The [1]
may look a bit weird when there is only one output element; this is how you can keep count of output elements.)
With the standard symbols you can use R as a general calculator:
203 * 2.54 # Multiplication
## [1] 515.62
203 + 2.54 # Addition
## [1] 205.54
Trying some random things…
203 - 2.54
## [1] 200.46
This works: so R just ignores any extra spaces.
How about:
203 +
Now the prompt is a +
. What is going on?
R is waiting for you to finish the command, since you typed an incomplete command: something has to come after the +
to be added to what came before.
While it was obvious here, you will often type incomplete commands without realizing you did so. Just remember that when you see the +
prompt, you are missing something in your command: often, you’ll have forgotten a closing parenthesis )
or you accidentally opened up an unwanted opening parenthesis (
.
Note also that RStudio will alert you in the script window when you are making syntax errors:
And if we just type a number:
203
## [1] 203
R will print the number back to us! It turns out that the default, implicit action that R will perform on anything you type is to print it back to us (it is calling a function called print()
under the hood).
What if we try that with some text?
Awesome
## Error in eval(expr, envir, enclos): object 'Awesome' not found
What seems to be going wrong here?
Whenever you type a character string, R expects to find an object with that name. (Or, if you would use parentheses after the string, like string()
, it will expect a function.)
We can get R to print character strings back to us, and work with them in other ways, as long as we quote the strings:
"Well, I'm really liking R so far."
## [1] "Well, I'm really liking R so far."
Getting Organized
Need for Scripts
We can go along like this, typing commands directly into the R console. But to better keep track of what you’re doing, it’s a good idea to write your code in files, i.e. “scripts”. And when we start creating scripts, we need to worry about how we organize the scripts and data for a project.
So let’s pause for a moment and talk about file organization.
Need for RStudio Projects
It is good practice to keep a set of related data, analyses, and text self-contained in a single folder, and use that folder as the working directory.
Then, all of the scripts within this folder can use relative paths to files that indicate where inside the project a file is located (as opposed to absolute paths, which point to where a file is on a specific computer).
More on absolute vs relative paths (click here)
An absolute path always contains the root element (/
on Unix systems such as Linux and Mac; typically C:\
on Windows) and the complete directory list to locate file. For that reason, if you move a file around on your computer, or move it to a different computer, an absolute path will practically never work. On the other hand, as long as the files remains in the same location, you can refer to it with an absolute path regardless of where you are on your computer.
C:\Documents\2020-12_microbiomics-workshop\scripts\intro-to-R.R # An absolute path
scripts/intro-to-R.R # A relative path
A relative path assumes a certain starting point (the “working directory”), so you need to be at the correct starting point for the path to work. On the other hand, if you keep all files associated with a project within a single parent directory, and keep the internal directory structures constant, a relative path will keep working, even if you move the project directory, open it on a different computer, or share it with a collaborator.
You should avoid using absolute paths as much as possible.
In R you can always use a forward slash /
to delimit directories, even if you are on a Windows machine.*
Working this way makes it a lot easier to move your project around on your computer and share it with others without worrying whether the paths in the scripts will still work.
A challenge of using relative paths is that you need to make sure you are always at the same starting point (your working directory). RStudio provides a helpful way to keep your working directory constant through its “Projects”. When you use a Project, your working directory will always be the top-level directory of that project, and you can safely use relative paths to point to other files.
Create an RStudio Project:
We’ll use the create_project()
function from the usethis package to create a new RStudio Project. The directory should contain our user name, so we first get our user name and build the path:
# Get your user name (by running a shell command via the `system()` function:
me <- system("echo $USER", intern = TRUE)
# Build the path to the target dir:
proj_dir <- file.path('/fs/project/PAS0471/workshops/2020-12_micro', me)
# Create the Project:
library(usethis)
create_project(path = proj_dir)
Now, RStudio will reload with the newly created Project open.
If you get the pop-up below, click Don't Save
(do this whenever you get that pop-up):
Your working directory should be the Project’s directory. We can check this using getwd()
:
getwd()
From now on, we will not change our working directory, and refer to all files relative to our project’s top-level directory.
Creating an R script
Create a new R script (File
> New File
> R Script
)
Click File
> Save As
to save the script in the scripts
dir that you should see within your personal dir. Give it a descriptive name like intro-to-R.R
.
Interacting with the R console from your script
I recommend always typing your commands into a script and executing the commands from there.
We want to make sure to save our division, so start by typing the following into the R script in the top-left pane:
203 / 2.54
With the cursor still on this line, press Ctrl
+ Enter
. The command will be copied to the R console and executed, and then the cursor will move to the next line. (You can also highlight multiple lines of code and execute them all at once with Ctrl
+ Enter
.)
Objects
Assigning stuff to objects
We can assign pretty much anything to an object with the assignment operator, <-
. (This is a smaller-than sign <
followed by a dash -
.) For example:
wingspan_in <- 203 / 2.54
Type that into your script, and use Ctrl
+ Enter
to send it to the console.
If you’ve assigned a number to an object, you can then use it in other calculations:
wingspan_in * 2.54
## [1] 203
Object names
Objects can be given any name such as x
, current_temperature
, or subject_id
.
Some pointers on object names:
Make them descriptive yet not too long.
They cannot start with a number (2x
is not valid, but x2
is).
Note: R is case sensitive (e.g., weight
is different from Weight
).
A style recommendation is to avoid capital letters (e.g., totalWeight
) and dots (total.weight
) to separate words. Instead, stick to lowercase, and use underscores (total_weight
) to separate words.
Challenge
What is the value of y
after doing all of the following?
x <- 50
y <- x * 2
x <- 80
Solution (click here)
Objects don’t get linked to each other, so if you change one, it won’t affect the values of any others. Therefore, y
will keep the value 100
.
Objects in your workspace
The objects you create get added to your “workspace”. You can list the current objects with ls()
. RStudio also shows the objects in the Environment panel.
Functions
Earlier, we divided 203
by 2.54
, but what if we wanted to round the resulting number? Like for many things you may want to do in R, there is a function for that.
Functions are used by typing their name followed by parentheses:
round(203 / 2.54)
## [1] 80
Here, round()
is a function that rounds a number. The value in the parentheses is called a function “argument”, which is used in the execution of the function.
Using named arguments
Functions can have more than one argument, and some of them may have default values.
There are some functions that take many arguments and it can get confusing trying to keep them in order. In that case, it is better to explicitly name the arguments.
When you type a function name and pause for a moment, the arguments, their names, and their default values (i.e., the value if the argument is left unspecified) will be shown.
What is the second argument for round()
and what is its default value? (Click here)
round
has a second argument digits
whose default is 0
, such that numbers will be rounded to whole integers.
Below is an example using named arguments with round()
. When the arguments are named, the order doesn’t matter! You might also enter the first few important arguments positionally, and later ones by naming them.
round(x = 1.538462, digits = 2)
round(digits = 2, x = 1.538462)
round(1.538462, digits = 2)
Also here, we can directly plug in objects:
wingspan_in <- 203 / 2.54
round(wingspan_in)
Or nest functions – here we are adding the log()
function:
log(round(203 / 2.54 ))
What is the order of execution in the last command? (Click here)
round()
is executed first, and the output of round()
is used as the input of log()
.
Getting help
As we saw, when we typed round
and paused for a moment, we got a pop-up with information about the function.
Alternatively, you could type:
?round
… and the documentation for the function will show up in the lower-right pane.
This documentation is often a bit too detailed, and can be terse, so it takes some practice to read. Usage, Arguments, and at the bottom, Examples, are most useful.
Googling what you want to do, even if you don’t know whether a function exists, will work too (e.g. “rounding a number in r”).
Vectors
A vector is the most common and basic data structure in R, and is composed of a series of values of the same type.
We can assign a series of values to a vector using the c()
function (for “combine”). For example:
wingspan_cm <- c(11.8, 203, 18.2, 27.9)
A vector can also contain characters – but again, quoting is important, or R will think the strings are objects:
birds <- c("hummingbird", "bald_eagle", "chickadee", "cardinal")
As mentioned, all of a vector’s elements are of the same type of data.
The function class()
indicates what kind of data you are working with:
class(wingspan_cm)
## [1] "numeric"
class(birds)
## [1] "character"
Data types in R
The classes we saw above are different types of atomic vectors, R’s simplest data type. The 4 most common atomic vector types are:
"numeric"
(or "double"
) – floating point numbers
"character"
– character strings
"logical"
– TRUE
and FALSE
(also known as boolean)
"integer"
– integer numbers
Vector coercion: when not all elements are of the same type. (Click here)
What happens if we try to mix vector types (e.g., “character and numeric”) in a single vector? R converts them to all be the same type, and it does so without telling us about it.
What will happen in each of the following examples?
(Hint: use class()
to check the data type of your objects)
num_char <- c(1, 2, 3, "a")
num_logical <- c(1, 2, 3, TRUE)
char_logical <- c("a", "b", "c", TRUE)
tricky <- c(1, 2, 3, "4")
You’ve probably noticed that objects of different types get converted into a single, shared type within a vector. In R, we call converting objects from one class into another class coercion. These conversions happen according to a hierarchy, whereby some types get preferentially coerced into other types.
Vectorization!
Let’s say we wanted to convert our vector of wingspans to inches: dividing each length in centimeters by 2.54.
How could we do this quickly?
# wingspan_cm <- c(11.8, 203, 18.2, 27.9) # Still working with the same wingspan vector
wingspan_in <- wingspan_cm / 2.54
wingspan_in
## [1] 4.645669 79.921260 7.165354 10.984252
Why does this work?
R “vectorizes” operations whenever it can. This means that in this case, each element in the vector weights_cm
will be divided by 2.54 – this number is recycled to match the number of weights.
For a programming language, this is unusual behavior – but it is very useful!
Similarly, we can use two vectors of equal length to quickly operate on each element of the vector:
size_cm <- c(7.62, 90, 13.1, 21.8)
ratio <- wingspan_cm / size_cm
ratio
## [1] 1.548556 2.255556 1.389313 1.279817
Data structures in R
While vectors can be composed of one of several data types, they, in turn, are one of several data structures that R uses. Other important ones are:
data.frame
– A rectangular data structure where each column can be a different data type.
matrix
– A rectangular data structure of a single data type.
list
– A very flexible data structure that we will not further discuss here.
factor
– Character strings with a discrete set of possible values, used mostly for statistical tests and when plotting.
Data Frames
A data frame (formal object type: data.frame
) is a rectangular data structure in which:
- Rows are observations and columns are variables.
- Each column can be of a different type (numeric, character, etc.),
- Since each column is a vector, all the values (cells) within a column are of the same type.
- All columns have the same length.
Create, write, and read a data frame
We can easily create a data frame by hand using the data.frame()
function and “column_name = column_vector
” notation for each column:
birds_df <- data.frame(species = birds,
wingspan = wingspan_cm,
size = size_cm,
n_eggs = c(2, 2, 7, 4))
Most often, however, you’ll be reading your data frames from files. And you’ll also want to save your modified data frames.
So let’s practice writing and reading a data frame:
# Write a data frame to CSV format:
write.csv(x = birds_df, file = "bird-data.csv", row.names = FALSE)
Now we read our data frame back in:
birds_df_2 <- read.csv('bird-data.csv')
## Let's compare the two data frames:
birds_df
birds_df_2
Inspecting a Data Frame
Use str()
to look at the structure of the data.
str(birds_df)
This tells us the number rows and columns, and for each column, gives information about the data type and shows the first few values.
Another useful function is summary()
.
summary(birds_df)
Finally, in RStudio, we can click on an object in the Files
pane, or equivalently, type:
View(birds_df)
An Overview of Functions to Get an Overview
- Size:
dim()
– Dimensions: c(number of rows, number of columns)
nrow()
– Number of rows
ncol()
– Number of columns
length()
– For a dataframe: number of columns. For a vector: number of elements.
- Content:
head()
– shows the first 6 rows
tail()
– shows the last 6 rows
- Names:
names()
or colnames()
– column names
rownames()
– row names
- Summary:
str()
– structure of the object and information about the class, length and content of each column
summary()
– summary statistics for each column
skimr::skim()
– very nice summary, need to install skimr with install.packages(skimr)
.
Miscellaneous
Missing data
As R was designed to analyze datasets, it includes the concept of missing data (which is uncommon in other programming languages). Missing data are represented as NA
(for “Not Available”).
heights <- c(2, 4, 4, NA, 6)
When doing operations on numbers, most functions will return NA
if the data you are working with include missing values. It is a safer behavior as otherwise you may overlook that you are dealing with missing data. You can add the argument na.rm=TRUE
to calculate the result while ignoring the missing values.
mean(heights)
mean(heights, na.rm = TRUE)
If your data includes missing values, you may want to become familiar with the functions is.na()
and na.omit()
.
## Extract those elements which are not missing values:
heights[!is.na(heights)]
## Shortcut to do the same:
na.omit(heights)
Packages
The functions that we have been using so far (and many, many more) are available in any R session as soon as you start R (we refer to this functionality as “base R”). However, when doing specialized analyses such as in microbiomics, rather than coding up everything using the basic building blocks in R, we can load add-on code that will allow us to use “high-level” functions specifically geared towards the effective analyses of such data.
This type of add-on code is distributed in R packages. The default repository for R packages is CRAN. The packages on CRAN have undergone a certain level of vetting, and can be easily installed with install.packages()
function, for instance:
install.packages("tidyverse")
If you’re doing bioinformatic analyses in R, as we will be doing, you will encounter packages that are not on CRAN but are on “Bioconductor”. To install a package from Bioconductor, use the BiocManager package – for example:
install.packages("BiocManager") # Install the BiocManager package
BiocManager::install("dada2") # Install the edgeR package from Bioconductor
Saving your data
Some very brief notes on saving your data in R:
We already saw the use of write.csv()
to save data frames, and you can also use one of readr’s writing functions.
To save R objects “as is”, which can be useful when you’re working with complex S4 objects that may have taken a long time to generate, like a phyloseq object, you can use:
# Save an object:
saveRDS(my_phyloseq_object, "my_phyloseq_object.RDS")
# Load it again in a new R session:
my_phyloseq_object <- readRDS("my_phyloseq_object.RDS")
A general recommendation is to not rely on your R session to keep things around, especially “overnight”. Devise your workflow such that you are always saving important objects and results outside of R, and can always use your R script to restart from where you left off.
S4 Objects
While the object types we have discussed so far are so-called “S3” object, we will also be seeing “S4” objects in this workshop. S4 object are commonly used by bioinformatics packages, for instance phyloseq.
In a nutshell, S4 objects allow for complicated, multifaceted datasets (e.g. multiple dataframes with and metadata) to be represented in a single object in a standardized way.
Unlike S3 objects, S4 objects are usually not manipulated by simple assignment with <-
, but with specialized functions that are sure to adhere to the strict object definitions.
Its elements are called slots, which are accessed with @
(object_name@slot_name
).
Where to go from here
This document only scratched the surface of R, but it has hopefully provided a good starting point for working with R.
My recommendations on where to go from here would be exactly the same regardless of whether you just wanted to make general progress with R, or wanted learn R things that you can apply when working with microbiomics packages. They are:
Learn about data wrangling with tidyverse packages, especially dplyr and tidyr. Start here
Learn about plotting with ggplot2. Start here
Both of those topics and some other material are also covered in this excellent Carpentries workshop R for Reproducible Scientific Analysis.
If you want to start with a book, I would recommend Wickham & Grolemund’s “R for Data Science”, which is freely available on the web in a really nice format here.
Bonus Material: Subsetting
Basic subsetting of data frames and vectors
We can pull out parts of a data frame using square brackets. We need to provide two values: row and column, with a comma between them.
For example, to get the element in the 1st row, 1st column:
surveys[1, 1]
To get the element in the 2nd row, 7th column:
surveys[2, 7]
To get the entire 2nd row, leave the column part blank:
surveys[2, ]
And to get the entire 7th column, leave the row part blank:
sex <- surveys[, 7]
You can also refer to columns by name, in multiple ways:
sex <- surveys$sex # Very commonly used syntax!
sex <- surveys[, "sex"]
When we pull out a single column, the result is a vector. To pull out individual values from a vector, we can again use square brackets, but now we only provide a single number:
sex[1]
sex[10000]
Slicing
You can pull out larger slices from the vector by providing vectors of indices:
sex[c(1,3,5)]
The :
operator gives you a sequence of consecutive values, which you can also using for slicing:
surveys[1:3, ] # First three rows, all columns
surveys[1, 1:3] # First row, first three columns
surveys[2:4, c(6, 8)] # Rows 2-4, columns 6 and 8
Conditional subsetting
Another common way of subsetting is by using a logical vector. TRUE
will select the element with the same index, while FALSE
will not:
weight_g <- c(21, 34, 39, 54, 55)
weight_g[c(TRUE, FALSE, FALSE, TRUE, TRUE)]
Typically, these logical vectors are not typed by hand, but are the output of other functions or logical tests. For instance, if you wanted to select only the values above 50:
weight_g > 50 # will return logicals with TRUE for the indices that meet the condition
## so we can use this to select only the values above 50
weight_g[weight_g > 50]
You can combine multiple tests using &
(both conditions are true, AND) or |
(at least one of the conditions is true, OR):
weight_g[weight_g > 30 & weight_g < 50]
weight_g[weight_g <= 30 | weight_g == 55]
weight_g[weight_g >= 30 & weight_g == 21]
Here, >
for “greater than”, <
stands for “less than”, <=
for “less than or equal to”, and ==
for “equal to”. The double equal sign ==
is a test for numerical equality between the left and right hand sides, and should not be confused with the single =
sign, which performs variable assignment (similar to <-
).
A common task is to search for certain strings in a vector. One could use the “or” operator |
to test for equality to multiple values, but this can quickly become tedious. The function %in%
allows you to test if any of the elements of a search vector are found:
animals <- c("mouse", "rat", "dog", "cat", "cat")
# return both rat and cat
animals[animals == "cat" | animals == "rat"]
animals[animals %in% c("cat", "rat")]
LS0tCnRpdGxlOiAiPGJyPkFuIEludHJvZHVjdGlvbiB0byBSIgpvdXRwdXQ6CiAgcm1hcmtkb3duOjpodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdGhlbWU6IGNlcnVsZWFuCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY3NzOiBteS5jc3MKICAgIGFuY2hvcl9zZWN0aW9uczogdHJ1ZQotLS0KCmBgYHtyIGtuaXRyX29wdGlvbnMsIGVjaG89RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChyZXN1bHRzPSdoaWRlJywKICAgICAgICAgICAgICAgICAgICAgIGV2YWwgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGNsYXNzLnNvdXJjZT0icl9jb2RlIiwKICAgICAgICAgICAgICAgICAgICAgIGNsYXNzLm91dHB1dD0icl9vdXRwdXQiLAogICAgICAgICAgICAgICAgICAgICAgY2xhc3Mud2FybmluZz0icl93YXJuaW5nIiwKICAgICAgICAgICAgICAgICAgICAgIGNsYXNzLm1lc3NhZ2U9InJfd2FybmluZyIsCiAgICAgICAgICAgICAgICAgICAgICBjbGFzcy5lcnJvcj0icl9lcnJvciIpCgpmb2xkZXJfbmFtZSA8LSAiMjAyMC0xMl9taWNyb2Jpb21pY3Mtd29ya3Nob3AiCmBgYAoKYGBge3Iga2xpcHB5LCBlY2hvPUZBTFNFLCBpbmNsdWRlPVRSVUV9CmtsaXBweTo6a2xpcHB5KHBvc2l0aW9uID0gJ3JpZ2h0JywKICAgICAgICAgICAgICAgdG9vbHRpcF9tZXNzYWdlID0gIkNvcHkgY29kZSB0byBjbGlwYm9hcmQiLAogICAgICAgICAgICAgICB0b29sdGlwX3N1Y2Nlc3MgPSAiQ29waWVkISIpCmBgYAoKPGJyPgoKPiAjIyBMZWFybmluZyBPYmplY3RpdmVzCj4KPiAqIEdldCBzb21lIGJhc2ljIGZhbWlsaWFyaXR5IHdpdGggUiBhbmQgUlN0dWRpbwo+ICogTGVhcm4gd2h5IGFuZCBob3cgdG8gdXNlIFJTdHVkaW8gUHJvamVjdHMKPiAqIFVuZGVyc3RhbmQgb2JqZWN0cywgZnVuY3Rpb25zLCBhbmQgaG93IHRvIHVzZSB0aGVtCj4gKiBVbmRlcnN0YW5kIHRoZSBjb25jZXB0cyBvZiBgdmVjdG9yYCBhbmQgYGRhdGEuZnJhbWVgCj4gKiBFeHBsb3JlIHRoZSBzdHJ1Y3R1cmUgYW5kIHRoZSBjb250ZW50IG9mIGEgYGRhdGEuZnJhbWVgCj4gKiBMZWFybiBhYm91dCBob3cgUiBoYW5kbGVzIG1pc3NpbmcgZGF0YQoKLS0tLS0tLS0tLS0tCgojIyBCZWZvcmUgV2UgR2V0IFN0YXJ0ZWQKCiMjIyBBdHRyaWJ1dGlvbgoKVGhpcyBtYXRlcmlhbCB3YXMgbW9kaWZpZWQgYWZ0ZXIgbWF0ZXJpYWwgZnJvbSBbVGhlIENhcnBlbnRyaWVzXShodHRwczovL2NhcnBlbnRyaWVzLm9yZy8pLAplc3BlY2lhbGx5IGZyb20gW3RoaXMgRGF0YSBDYXJwZW50cnkgd29ya3Nob3BdKGh0dHA6Ly91dy1tYWRpc29uLWFjaS5naXRodWIuaW8vMjAxNi0wNi0wMS11d21hZGlzb24vKQphbmQgW3RoaXMgIlIgZm9yIEVjb2xvZ3kiIHdvcmtzaG9wXShodHRwczovL2RhdGFjYXJwZW50cnkub3JnL1ItZWNvbG9neS1sZXNzb24pLgoKIyMjIFdoeSBSPwoKUiBpcyBhIHZlcnNhdGlsZSwgb3BlbiBzb3VyY2UgcHJvZ3JhbW1pbmcvc2NyaXB0aW5nIGxhbmd1YWdlIHRoYXQncyBwYXJ0aWN1bGFybHkKdXNlZnVsIGZvciAqc3RhdGlzdGljcyogYW5kICpkYXRhIHZpc3VhbGl6YXRpb24qLgoKKipZZXMsIHRoZXJlIGlzIGEgbGVhcm5pbmcgY3VydmUsIGFuZCBtYW55IG9mIHVzIGp1c3Qgd2FudCB0byBnZXQgb24gd2l0aCBvdXIgYW5hbHlzaXMgLS0gICAgCmJ1dCBpbnZlc3RpbmcgaW4gbGVhcm5pbmcgUiB3aWxsIHBheSBvZmY6KioKCi0gUiBnaXZlcyB5b3UgZ3JlYXRlciAqKmZsZXhpYmlsaXR5KiogdG8gZG8gYW55dGhpbmcgeW91IHdhbnQuCgotIEEgZ3JlYXRlciAqKnJlcHJvZHVjaWJpbGl0eSoqIG9mIHNjcmlwdGluZyB2cyBjbGlja2luZy4KCi0gUiBpcyBoaWdobHkgKippbnRlcmRpc2NpcGxpbmFyeSoqIC0tIGUuZy4gdmVyeSB1c2VmdWwgZm9yIGFuYWx5emluZyBzZXF1ZW5jaW5nIGRhdGEgIAogIGJ1dCBjYW4gYWxzbyBiZSB1c2VkIHRvIGNyZWF0ZSBtYXBzIGFuZCBwZXJmb3JtIEdJUyBhbmFseXNlcywgYW5kIHNvIG9uIQoKLSBSIGlzIG1vcmUgdGhhbiBhIHBsYXRmb3JtIHRvIHBlcmZvcm0gYW5hbHlzaXMuCiAgV2hlbiBjb21iaW5lZCB3aXRoICpNYXJrZG93biogKGEgdmVyeSBzaW1wbGUgdGV4dCBtYXJrdXAgbGFuZ3VhZ2UpLAogIHlvdSBjYW4gZWFzaWx5IHVzZSBSIHRvIHByb2R1Y2UgcmVwb3J0cyB0aGF0IGludGVncmF0ZSBjb2RlLCByZXN1bHRzLCBhbmQgdGV4dCwKICBhcyB3ZWxsIGFzIGZvciB3ZWJzaXRlcywgc2xpZGUgZGVja3MsIGJvb2tzLCBhbmQgc28gb24hCgpGdXJ0aGVybW9yZSwgUjoKCi0gSXMgZnJlZWx5IGF2YWlsYWJsZSBvbiBhbGwgcGxhdGZvcm1zLCBhbmQgb3BlbiBzb3VyY2UuCgotIEhhcyBhIGxhcmdlIGFuZCB3ZWxjb21pbmcgdXNlciBjb21tdW5pdHkuCgotLS0tLQoKIyMgR2V0dGluZyBTdGFydGVkCgojIyMgU3RhcnQgYW4gUlN0dWRpbyBTZXJ2ZXIgc2Vzc2lvbiBhdCBPU0MgeyNyc3R1ZGlvLWF0LU9TQ30KCi0gTG9naW4gdG8gT1NDIGF0IDxodHRwczovL29uZGVtYW5kLm9zYy5lZHU+LgoKLSBDbGljayBvbiBgSW50ZXJhY3RpdmUgQXBwc2AgKHRvcCBiYXIpID4gYFJTdHVkaW8gU2VydmVyIChPd2VucyBhbmQgUGl0emVyKWAKCjxwIGFsaWduPSJjZW50ZXIiPgo8aW1nIHNyYz1zbGlkZXMvZmlnc19PU0MvYXBwcy5wbmcgd2lkdGg9IjUwMCI+CjwvcD4KCi0gRmlsbCBvdXQgdGhlIGZvcm0gYXMgc2hvd24gW2hlcmVdKHNsaWRlcy8wMy1PU0Mtc2xpZGVzLmh0bWwjcnN0dWRpb19zZXJ2ZXJfam9iKS4KCi0gTm93LCB5b3Ugc2hvdWxkIHNlZSBhIGJveCBsaWtlIHRoaXM6Cgo8cCBhbGlnbj0iY2VudGVyIj4KPGltZyBzcmM9aW1nL29zY19xdWV1ZWQucG5nIHdpZHRoPSI3MDAiPgo8L3A+CgotIFlvdXIgam9iIHNob3VsZCBzdGFydCBydW5uaW5nIHByZXR0eSBzb29uLCBhbmQgd2hlbiBpdCdzIHJlYWR5IHRoZSBib3ggc2hvdWxkIGxvb2sgbGlrZSB0aGlzOiAKCjxwIGFsaWduPSJjZW50ZXIiPgo8aW1nIHNyYz1pbWcvb3NjX3J1bm5pbmcucG5nIHdpZHRoPSI3MDAiPgo8L3A+CgotIENsaWNrIGBDb25uZWN0IHRvIFJTdHVkaW8gU2VydmVyYCBhdCB0aGUgYm90dG9tIG9mIHRoZSBib3gsIGFuZCBhbiBSU3R1ZGlvIFNlcnZlciBpbnN0YW5jZSB3aWxsIG9wZW4uIFlvdSdyZSByZWFkeSB0byBnbyEKCiMjIyBSIHZzLiBSU3R1ZGlvCgpSIHNpbXBseSBwcm92aWRlcyBhICIqY29uc29sZSoiIHRvIHR5cGUgeW91ciBjb21tYW5kcy4KSG93ZXZlciwgYmVjYXVzZSB5b3Ugd2FudCB0byBzYXZlIHlvdXIgc2VxdWVuY2VzIG9mIGNvbW1hbmRzIGluIHNjcmlwdHMsCnJlZ3VsYXJseSBleGFtaW5lIG91dHB1dCBzdWNoIGEgZ3JhcGhpY3MsIGFuZCBzbyBvbiwKeW91IGNhbiB3b3JrIG11Y2ggbW9yZSBlZmZlY3RpdmVseSBpbiBhbiBlbnZpcm9ubWVudCB0aGF0IHByb3ZpZGVzIGFsbCBvZiB0aGlzCnNpZGUtYnktc2lkZS4KCldlIHdpbGwgdXNlIFJTdHVkaW8sIGFuIGV4Y2VsbGVudCAiSW50ZWdyYXRlZCBEZXZlbG9wbWVudCBFbnZpcm9ubWVudCIgKElERSkgZm9yIFIuCkluIFJTdHVkaW8sIHdlIGhhdmUgYSAqc2luZ2xlIGludGVyZmFjZSogdG8gd3JpdGUgY29kZSwgbmF2aWdhdGUgdGhlIGZpbGVzIGZvdW5kIG9uIG91ciBjb21wdXRlciwKaW5zcGVjdCB0aGUgb2JqZWN0cyB3ZSBjcmVhdGUsIGFuZCB2aXN1YWxpemUgdGhlIHBsb3RzIHdlIGdlbmVyYXRlLgoKUlN0dWRpbyBpcyBkaXZpZGVkIGludG8gNCAicGFuZXMiOgoKLSAqKl9Ub3AtbGVmdF8qKjogVGhlICoqZWRpdG9yKiogZm9yIHlvdXIgc2NyaXB0cyBhbmQgZG9jdW1lbnRzLgotICoqX0JvdHRvbS1sZWZ0XyoqOiBUaGUgKipSIGNvbnNvbGUqKi4KLSAqKl9Ub3AtcmlnaHRfKio6IFlvdXIgZW52aXJvbm1lbnQvaGlzdG9yeS4KLSAqKl9Cb3R0b20tbGVmdF8qKjogWW91ciBmaWxlcy9wbG90cy9wYWNrYWdlcy9oZWxwL3ZpZXdlci4KICAKVGhlIHBsYWNlbWVudCBvZiB0aGVzZSBwYW5lcyBhbmQgdGhlaXIgY29udGVudCBjYW4gYmUgY3VzdG9taXplZC4KCgotLS0tLQoKIyMgSW50ZXJhY3Rpbmcgd2l0aCBSCgojIyMgUiBhcyBhIGNhbGN1bGF0b3IKClRoZSBsb3dlci1sZWZ0IFJTdHVkaW8gcGFuZSwgaS5lLiAqKnRoZSBSIGNvbnNvbGUqKiwKaXMgd2hlcmUgeW91IGNhbiBpbnRlcmFjdCB3aXRoIFIgZGlyZWN0bHkuCgpUaGUgYD5gIHNpZ24gaXMgdGhlIFIgInByb21wdCIuCkl0IGluZGljYXRlcyB0aGF0IFIgaXMgcmVhZHkgZm9yIHlvdSB0byB0eXBlIHNvbWV0aGluZy4KCkxldCdzIHN0YXJ0IGJ5IHBlcmZvcm1pbmcgYSBkaXZpc2lvbjoKCmBgYHtyIGFkZCwgZXZhbD1UUlVFLCByZXN1bHRzPSdzaG93J30KMjAzIC8gMi41NApgYGAKClIgZG9lcyB0aGUgY2FsY3VsYXRpb24gYW5kIHByaW50cyB0aGUgcmVzdWx0LCBhbmQgdGhlbiB5b3UgZ2V0IHRoZSBgPmAgcHJvbXB0IGFnYWluLgooVGhlIGBbMV1gIG1heSBsb29rIGEgYml0IHdlaXJkIHdoZW4gdGhlcmUgaXMgb25seSBvbmUgb3V0cHV0IGVsZW1lbnQ7IAp0aGlzIGlzIGhvdyB5b3UgY2FuIGtlZXAgY291bnQgb2Ygb3V0cHV0IGVsZW1lbnRzLikKCldpdGggdGhlIHN0YW5kYXJkIHN5bWJvbHMgeW91IGNhbiB1c2UgUiBhcyBhIGdlbmVyYWwgY2FsY3VsYXRvcjoKCmBgYHtyIGNhbGN1bGF0aW9ucywgZXZhbCA9IFRSVUUsIHJlc3VsdHM9J3Nob3cnfQoyMDMgKiAyLjU0ICAgIyBNdWx0aXBsaWNhdGlvbgoyMDMgKyAyLjU0ICAgIyBBZGRpdGlvbgpgYGAKCgojIyMgVHJ5aW5nIHNvbWUgcmFuZG9tIHRoaW5ncy4uLgoKYGBge3Igc3BhY2UsIGV2YWw9VFJVRSwgcmVzdWx0cz0ic2hvdyJ9CiAgICAgICAgIDIwMyAgICAgICAgICAgICAgICAgICAgIC0gMi41NApgYGAKClRoaXMgd29ya3M6IHNvIFIganVzdCBpZ25vcmVzIGFueSBleHRyYSBzcGFjZXMuCgo8YnI+CgpIb3cgYWJvdXQ6CgpgYGB7ciBoYW5naW5nX3Byb21wdH0KMjAzICsKYGBgCgo8ZGV0YWlscz4KPHN1bW1hcnk+CiZuYnNwOyAgYHIgaWNvbjo6ZmEoInF1ZXN0aW9uLWNpcmNsZSIpYCAmbmJzcDsgKipOb3cgdGhlIHByb21wdCBpcyBhIGArYC4gV2hhdCBpcyBnb2luZyBvbj8qKgo8L3N1bW1hcnk+CgpSIGlzIHdhaXRpbmcgZm9yIHlvdSB0byBmaW5pc2ggdGhlIGNvbW1hbmQsIHNpbmNlIHlvdSB0eXBlZCBhbiBpbmNvbXBsZXRlIGNvbW1hbmQ6CnNvbWV0aGluZyBoYXMgdG8gY29tZSBhZnRlciB0aGUgYCtgIHRvIGJlIGFkZGVkIHRvIHdoYXQgY2FtZSBiZWZvcmUuCgpXaGlsZSBpdCB3YXMgb2J2aW91cyBoZXJlLCB5b3Ugd2lsbCBvZnRlbiB0eXBlIGluY29tcGxldGUgY29tbWFuZHMgd2l0aG91dApyZWFsaXppbmcgeW91IGRpZCBzby4KSnVzdCByZW1lbWJlciB0aGF0IHdoZW4geW91IHNlZSB0aGUgYCtgIHByb21wdCwKeW91IGFyZSBtaXNzaW5nIHNvbWV0aGluZyBpbiB5b3VyIGNvbW1hbmQ6Cm9mdGVuLCB5b3UnbGwgaGF2ZSBmb3Jnb3R0ZW4gYSBjbG9zaW5nIHBhcmVudGhlc2lzIGApYApvciB5b3UgYWNjaWRlbnRhbGx5IG9wZW5lZCB1cCBhbiB1bndhbnRlZCBvcGVuaW5nIHBhcmVudGhlc2lzIGAoYC4KCk5vdGUgYWxzbyB0aGF0IFJTdHVkaW8gd2lsbCBhbGVydCB5b3UgaW4gdGhlIHNjcmlwdCB3aW5kb3cgd2hlbiB5b3UgYXJlIG1ha2luZyBzeW50YXggZXJyb3JzOgoKPHAgYWxpZ249ImNlbnRlciI+CjxpbWcgc3JjPSJpbWcvUnN0dWRpb19lcnJvci5wbmciIHN0eWxlPSJ3aWR0aDo1MCU7IG1hcmdpbi10b3A6IDEwcHg7IG1hcmdpbi1ib3R0b206IDEwcHg7Ii8+CjwvcD4KCjwvZGV0YWlscz4KCjxicj4KCkFuZCBpZiB3ZSBqdXN0IHR5cGUgYSBudW1iZXI6CgpgYGB7ciBudW1iZXIsIGV2YWw9VFJVRSwgcmVzdWx0cz0nc2hvdyd9CjIwMwpgYGAKClIgd2lsbCBwcmludCB0aGUgbnVtYmVyIGJhY2sgdG8gdXMhCkl0IHR1cm5zIG91dCB0aGF0IHRoZSBkZWZhdWx0LCBpbXBsaWNpdCBhY3Rpb24gdGhhdCBSIHdpbGwgcGVyZm9ybQpvbiBhbnl0aGluZyB5b3UgdHlwZSBpcyB0byBwcmludCBpdCBiYWNrIHRvIHVzCihpdCBpcyBjYWxsaW5nIGEgZnVuY3Rpb24gY2FsbGVkIGBwcmludCgpYCB1bmRlciB0aGUgaG9vZCkuCgo8YnI+CgpXaGF0IGlmIHdlIHRyeSB0aGF0IHdpdGggc29tZSB0ZXh0PwoKYGBge3IgZ2liYmVyaXNoLCBldmFsPVRSVUUsIHJlc3VsdHM9J3Nob3cnLCBlcnJvcj1UUlVFfQpBd2Vzb21lCmBgYAoKCjxkZXRhaWxzPgo8c3VtbWFyeT4KJm5ic3A7ICBgciBpY29uOjpmYSgicXVlc3Rpb24tY2lyY2xlIilgICZuYnNwOyAqKldoYXQgc2VlbXMgdG8gYmUgZ29pbmcgd3JvbmcgaGVyZT8qKgo8L3N1bW1hcnk+CgpXaGVuZXZlciB5b3UgdHlwZSBhIGNoYXJhY3RlciBzdHJpbmcsIFIgZXhwZWN0cyB0byBmaW5kIGFuIG9iamVjdCB3aXRoIHRoYXQgbmFtZS4KKE9yLCBpZiB5b3Ugd291bGQgdXNlIHBhcmVudGhlc2VzIGFmdGVyIHRoZSBzdHJpbmcsIGxpa2UgYHN0cmluZygpYCwKaXQgd2lsbCBleHBlY3QgYSBmdW5jdGlvbi4pCgo8L2RldGFpbHM+Cgo8YnI+CgoKV2UgKmNhbiogZ2V0IFIgdG8gcHJpbnQgY2hhcmFjdGVyIHN0cmluZ3MgYmFjayB0byB1cywKYW5kIHdvcmsgd2l0aCB0aGVtIGluIG90aGVyIHdheXMsIGFzIGxvbmcgYXMgd2UgcXVvdGUgdGhlIHN0cmluZ3M6CgpgYGB7ciBwcmludF9jaGFyLCBldmFsPVRSVUUsIHJlc3VsdHM9J3Nob3cnfQoiV2VsbCwgSSdtIHJlYWxseSBsaWtpbmcgUiBzbyBmYXIuIgpgYGAKCi0tLS0KCiMjIEdldHRpbmcgT3JnYW5pemVkCgojIyMgTmVlZCBmb3IgU2NyaXB0cwoKV2UgY2FuIGdvIGFsb25nIGxpa2UgdGhpcywgdHlwaW5nIGNvbW1hbmRzIGRpcmVjdGx5IGludG8gdGhlIFIgY29uc29sZS4KQnV0IHRvIGJldHRlciBrZWVwIHRyYWNrIG9mIHdoYXQgeW91J3JlIGRvaW5nLAppdCdzIGEgZ29vZCBpZGVhIHRvIHdyaXRlIHlvdXIgY29kZSBpbiBmaWxlcywgaS5lLiAiKipzY3JpcHRzKioiLgpBbmQgd2hlbiB3ZSBzdGFydCBjcmVhdGluZyBzY3JpcHRzLCB3ZSBuZWVkIHRvIHdvcnJ5IGFib3V0IGhvdyB3ZQpvcmdhbml6ZSB0aGUgc2NyaXB0cyBhbmQgZGF0YSBmb3IgYSBwcm9qZWN0LgoKU28gbGV0J3MgcGF1c2UgZm9yIGEgbW9tZW50IGFuZCB0YWxrIGFib3V0IGZpbGUgb3JnYW5pemF0aW9uLgoKIyMjIE5lZWQgZm9yIFJTdHVkaW8gUHJvamVjdHMKCkl0IGlzIGdvb2QgcHJhY3RpY2UgdG8ga2VlcCBhIHNldCBvZiByZWxhdGVkIGRhdGEsIGFuYWx5c2VzLCBhbmQgdGV4dApzZWxmLWNvbnRhaW5lZCBpbiBhIHNpbmdsZSBmb2xkZXIsCmFuZCB1c2UgdGhhdCBmb2xkZXIgYXMgdGhlICoqd29ya2luZyBkaXJlY3RvcnkqKi4KClRoZW4sIGFsbCBvZiB0aGUgc2NyaXB0cyB3aXRoaW4gdGhpcyBmb2xkZXIgY2FuIHVzZSAqcmVsYXRpdmUgcGF0aHMqIHRvIGZpbGVzCnRoYXQgaW5kaWNhdGUgd2hlcmUgaW5zaWRlIHRoZSBwcm9qZWN0IGEgZmlsZSBpcyBsb2NhdGVkCihhcyBvcHBvc2VkIHRvICphYnNvbHV0ZSBwYXRocyosCndoaWNoIHBvaW50IHRvIHdoZXJlIGEgZmlsZSBpcyAqb24gYSBzcGVjaWZpYyBjb21wdXRlciopLgoKPGRldGFpbHM+CjxzdW1tYXJ5PgombmJzcDsgIGByIGljb246OmZhKCJpbmZvLWNpcmNsZSIpYCAmbmJzcDsgTW9yZSBvbiBhYnNvbHV0ZSB2cyByZWxhdGl2ZSBwYXRocyAoY2xpY2sgaGVyZSkKPC9zdW1tYXJ5PgoKQW4gYWJzb2x1dGUgcGF0aCBhbHdheXMgY29udGFpbnMgdGhlICpyb290IGVsZW1lbnQqIChgL2Agb24gVW5peCBzeXN0ZW1zIHN1Y2ggYXMKTGludXggYW5kIE1hYzsgdHlwaWNhbGx5IGBDOlxgIG9uIFdpbmRvd3MpIGFuZCB0aGUgY29tcGxldGUgZGlyZWN0b3J5IGxpc3QKdG8gbG9jYXRlIGZpbGUuIEZvciB0aGF0IHJlYXNvbiwgaWYgeW91IG1vdmUgYSBmaWxlIGFyb3VuZCBvbiB5b3VyIGNvbXB1dGVyLApvciBtb3ZlIGl0IHRvIGEgZGlmZmVyZW50IGNvbXB1dGVyLCBhbiBhYnNvbHV0ZSBwYXRoIHdpbGwgcHJhY3RpY2FsbHkgbmV2ZXIgd29yay4KT24gdGhlIG90aGVyIGhhbmQsIGFzIGxvbmcgYXMgdGhlIGZpbGVzIHJlbWFpbnMgaW4gdGhlIHNhbWUgbG9jYXRpb24sCnlvdSBjYW4gcmVmZXIgdG8gaXQgd2l0aCBhbiBhYnNvbHV0ZSBwYXRoIHJlZ2FyZGxlc3Mgb2Ygd2hlcmUgeW91IGFyZSBvbiB5b3VyIGNvbXB1dGVyLgoKYGBge2Jhc2gsIGV2YWw9RkFMU0V9CkM6XERvY3VtZW50c1wyMDIwLTEyX21pY3JvYmlvbWljcy13b3Jrc2hvcFxzY3JpcHRzXGludHJvLXRvLVIuUiAgIyBBbiBhYnNvbHV0ZSBwYXRoCgpzY3JpcHRzL2ludHJvLXRvLVIuUiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgQSByZWxhdGl2ZSBwYXRoCmBgYAoKPGJyPgoKQSByZWxhdGl2ZSBwYXRoIGFzc3VtZXMgYSBjZXJ0YWluIHN0YXJ0aW5nIHBvaW50ICh0aGUgIndvcmtpbmcgZGlyZWN0b3J5IiksCnNvIHlvdSBuZWVkIHRvIGJlIGF0IHRoZSBjb3JyZWN0IHN0YXJ0aW5nIHBvaW50IGZvciB0aGUgcGF0aCB0byB3b3JrLgpPbiB0aGUgb3RoZXIgaGFuZCwgaWYgeW91IGtlZXAgYWxsIGZpbGVzIGFzc29jaWF0ZWQgd2l0aCBhIHByb2plY3Qgd2l0aGluCmEgc2luZ2xlIHBhcmVudCBkaXJlY3RvcnksIGFuZCBrZWVwIHRoZSBpbnRlcm5hbCBkaXJlY3Rvcnkgc3RydWN0dXJlcyBjb25zdGFudCwKYSByZWxhdGl2ZSBwYXRoIHdpbGwga2VlcCB3b3JraW5nLCBldmVuIGlmIHlvdSBtb3ZlIHRoZSBwcm9qZWN0IGRpcmVjdG9yeSwKb3BlbiBpdCBvbiBhIGRpZmZlcmVudCBjb21wdXRlciwgb3Igc2hhcmUgaXQgd2l0aCBhIGNvbGxhYm9yYXRvci4KCioqWW91IHNob3VsZCBhdm9pZCB1c2luZyBhYnNvbHV0ZSBwYXRocyBhcyBtdWNoIGFzIHBvc3NpYmxlLioqCgpgciBpY29uOjpmYSgiaW5mby1jaXJjbGUiKWAgSW4gUiB5b3UgY2FuIGFsd2F5cyB1c2UgYQpmb3J3YXJkIHNsYXNoIGAvYCB0byBkZWxpbWl0IGRpcmVjdG9yaWVzLCBldmVuIGlmIHlvdSBhcmUgb24gYSBXaW5kb3dzIG1hY2hpbmUuKgoKPC9kZXRhaWxzPgoKPGJyPgoKV29ya2luZyB0aGlzIHdheSBtYWtlcyBpdCBhIGxvdCBlYXNpZXIgdG8gbW92ZSB5b3VyIHByb2plY3QgYXJvdW5kIG9uIHlvdXIgY29tcHV0ZXIKYW5kIHNoYXJlIGl0IHdpdGggb3RoZXJzIHdpdGhvdXQgd29ycnlpbmcgd2hldGhlciB0aGUgcGF0aHMgaW4gdGhlIHNjcmlwdHMKd2lsbCBzdGlsbCB3b3JrLgoKQSBjaGFsbGVuZ2Ugb2YgdXNpbmcgcmVsYXRpdmUgcGF0aHMgaXMgdGhhdCB5b3UgbmVlZCB0byBtYWtlIHN1cmUgeW91IGFyZQphbHdheXMgYXQgdGhlIHNhbWUgc3RhcnRpbmcgcG9pbnQgKHlvdXIgKndvcmtpbmcgZGlyZWN0b3J5KikuClJTdHVkaW8gcHJvdmlkZXMgYSBoZWxwZnVsIHdheSB0bwoqKmtlZXAgeW91ciB3b3JraW5nIGRpcmVjdG9yeSBjb25zdGFudCB0aHJvdWdoIGl0cyAiUHJvamVjdHMiKiouCldoZW4geW91IHVzZSBhIFByb2plY3QsIHlvdXIgd29ya2luZyBkaXJlY3Rvcnkgd2lsbCAqYWx3YXlzKiBiZSB0aGUgdG9wLWxldmVsCmRpcmVjdG9yeSBvZiB0aGF0IHByb2plY3QsCmFuZCB5b3UgY2FuIHNhZmVseSB1c2UgcmVsYXRpdmUgcGF0aHMgdG8gcG9pbnQgdG8gb3RoZXIgZmlsZXMuCgojIyMgQ3JlYXRlIGFuIFJTdHVkaW8gUHJvamVjdDoKCldlJ2xsIHVzZSB0aGUgYGNyZWF0ZV9wcm9qZWN0KClgIGZ1bmN0aW9uIGZyb20gdGhlICp1c2V0aGlzKiBwYWNrYWdlCnRvIGNyZWF0ZSBhIG5ldyBSU3R1ZGlvIFByb2plY3QuIFRoZSBkaXJlY3Rvcnkgc2hvdWxkIGNvbnRhaW4gb3VyIHVzZXIgbmFtZSwKc28gd2UgZmlyc3QgZ2V0IG91ciB1c2VyIG5hbWUgYW5kIGJ1aWxkIHRoZSBwYXRoOgoKYGBge3J9CiMgR2V0IHlvdXIgdXNlciBuYW1lIChieSBydW5uaW5nIGEgc2hlbGwgY29tbWFuZCB2aWEgdGhlIGBzeXN0ZW0oKWAgZnVuY3Rpb246Cm1lIDwtIHN5c3RlbSgiZWNobyAkVVNFUiIsIGludGVybiA9IFRSVUUpCiMgQnVpbGQgdGhlIHBhdGggdG8gdGhlIHRhcmdldCBkaXI6CnByb2pfZGlyIDwtIGZpbGUucGF0aCgnL2ZzL3Byb2plY3QvUEFTMDQ3MS93b3Jrc2hvcHMvMjAyMC0xMl9taWNybycsIG1lKQoKIyBDcmVhdGUgdGhlIFByb2plY3Q6CmxpYnJhcnkodXNldGhpcykKY3JlYXRlX3Byb2plY3QocGF0aCA9IHByb2pfZGlyKQpgYGAKCk5vdywgUlN0dWRpbyB3aWxsIHJlbG9hZCB3aXRoIHRoZSBuZXdseSBjcmVhdGVkIFByb2plY3Qgb3Blbi4KCklmIHlvdSBnZXQgdGhlIHBvcC11cCBiZWxvdywgY2xpY2sgYERvbid0IFNhdmVgIChkbyB0aGlzIHdoZW5ldmVyIHlvdSBnZXQgdGhhdCBwb3AtdXApOgoKPHAgYWxpZ249ImNlbnRlciI+CjxpbWcgc3JjPWltZy9yZGF0YS1wb3B1cC5wbmcgd2lkdGg9IjM1MCI+CjwvcD4KCgpZb3VyIHdvcmtpbmcgZGlyZWN0b3J5IHNob3VsZCBiZSB0aGUgUHJvamVjdCdzIGRpcmVjdG9yeS4KV2UgY2FuIGNoZWNrIHRoaXMgdXNpbmcgYGdldHdkKClgOgoKYGBge3J9CmdldHdkKCkKYGBgCgpGcm9tIG5vdyBvbiwgd2Ugd2lsbCBub3QgY2hhbmdlIG91ciB3b3JraW5nIGRpcmVjdG9yeSwKYW5kIHJlZmVyIHRvIGFsbCBmaWxlcyByZWxhdGl2ZSB0byBvdXIgcHJvamVjdCdzIHRvcC1sZXZlbCBkaXJlY3RvcnkuCgoKIyMjIENyZWF0aW5nIGFuIFIgc2NyaXB0CgotIENyZWF0ZSBhIG5ldyBSIHNjcmlwdCAoYEZpbGVgID4gYE5ldyBGaWxlYCA+IGBSIFNjcmlwdGApCgotIENsaWNrIGBGaWxlYCA+IGBTYXZlIEFzYCB0byBzYXZlIHRoZSBzY3JpcHQgaW4gdGhlIGBzY3JpcHRzYCBkaXIgdGhhdCB5b3UKICBzaG91bGQgc2VlIHdpdGhpbiB5b3VyIHBlcnNvbmFsIGRpci4KICBHaXZlIGl0IGEgZGVzY3JpcHRpdmUgbmFtZSBsaWtlIGBpbnRyby10by1SLlJgLgogIAoKIyMjIEludGVyYWN0aW5nIHdpdGggdGhlIFIgY29uc29sZSBmcm9tIHlvdXIgc2NyaXB0CgpJIHJlY29tbWVuZCBhbHdheXMgKnR5cGluZyB5b3VyIGNvbW1hbmRzIGludG8gYSBzY3JpcHQqCmFuZCBleGVjdXRpbmcgdGhlIGNvbW1hbmRzIGZyb20gdGhlcmUuCgpXZSB3YW50IHRvIG1ha2Ugc3VyZSB0byBzYXZlIG91ciBkaXZpc2lvbiwKc28gc3RhcnQgYnkgdHlwaW5nIHRoZSBmb2xsb3dpbmcgaW50byB0aGUgUiBzY3JpcHQgaW4gdGhlIHRvcC1sZWZ0IHBhbmU6CgpgYGB7cn0KMjAzIC8gMi41NApgYGAKCldpdGggdGhlIGN1cnNvciBzdGlsbCBvbiB0aGlzIGxpbmUsCnByZXNzIDxrYmQ+YEN0cmxgPC9rYmQ+ICsgPGtiZD5gRW50ZXJgPC9rYmQ+LgpUaGUgY29tbWFuZCB3aWxsIGJlIGNvcGllZCB0byB0aGUgUiBjb25zb2xlIGFuZCBleGVjdXRlZCwKYW5kIHRoZW4gdGhlIGN1cnNvciB3aWxsIG1vdmUgdG8gdGhlIG5leHQgbGluZS4KKFlvdSBjYW4gYWxzbyBoaWdobGlnaHQgbXVsdGlwbGUgbGluZXMgb2YgY29kZSBhbmQgZXhlY3V0ZSB0aGVtIGFsbCBhdApvbmNlIHdpdGggPGtiZD5gQ3RybGA8L2tiZD4gKyA8a2JkPmBFbnRlcmA8L2tiZD4uKQoKIyMjIENvbW1lbnRpbmcKClVzZSBgI2Agc2lnbnMgdG8gY29tbWVudCB5b3VyIGNvZGUuCkFueXRoaW5nIHRvIHRoZSByaWdodCBvZiBhIGAjYCBpcyBpZ25vcmVkIGJ5IFIsIG1lYW5pbmcgaXQgd29uJ3QgYmUgZXhlY3V0ZWQuCllvdSBjYW4gdXNlIGAjYCBib3RoIGF0IHRoZSBzdGFydCBvZiBhIGxpbmUgb3IgYW55d2hlcmUgaW4gYSBsaW5lIGZvbGxvd2luZyBjb2RlLgoKQ29tbWVudHMgYXJlIGEgZ3JlYXQgd2F5IHRvIGRlc2NyaWJlIHdoYXQgeW91ciBjb2RlIGRvZXMgd2l0aGluIHRoZSBjb2RlIGl0c2VsZiwKc28gY29tbWVudCBsaWJlcmFsbHkgaW4geW91ciBSIHNjcmlwdHMhCgotLS0tCgojIyBPYmplY3RzCgojIyMgQXNzaWduaW5nIHN0dWZmIHRvIG9iamVjdHMKCldlIGNhbiBhc3NpZ24gcHJldHR5IG11Y2ggYW55dGhpbmcgdG8gYW4gb2JqZWN0IHdpdGggdGhlIGFzc2lnbm1lbnQKb3BlcmF0b3IsIGA8LWBeW0luIFJTdHVkaW8sIHR5cGluZyA8a2JkPkFsdDwva2JkPiArIDxrYmQ+LTwva2JkPiB3aWxsIHdyaXRlIGAgPC0gYCBpbgphIHNpbmdsZSBrZXlzdHJva2UuIFlvdSBjYW4gYWxzbyB1c2UgYD1gIGFzIGFzc2lnbm1lbnQsIGJ1dCB0aGF0IHN5bWJvbCBjYW4gaGF2ZSBvdGhlcgptZWFuaW5ncywgYW5kIHNvIEkgcmVjb21tZW5kIHN0aWNraW5nIHdpdGggdGhlIGA8LWAgY29tYmluYXRpb24uXS4KKFRoaXMgaXMgYSBzbWFsbGVyLXRoYW4gc2lnbiBgPGAgZm9sbG93ZWQgYnkgYSBkYXNoIGAtYC4pCkZvciBleGFtcGxlOgoKYGBge3IgYXNzaWduLCBldmFsPVRSVUV9CndpbmdzcGFuX2luIDwtIDIwMyAvIDIuNTQKYGBgCgpUeXBlIHRoYXQgaW50byB5b3VyIHNjcmlwdCwKYW5kIHVzZSA8a2JkPmBDdHJsYDwva2JkPiArIDxrYmQ+YEVudGVyYDwva2JkPiB0byBzZW5kIGl0IHRvIHRoZSBjb25zb2xlLgoKSWYgeW91J3ZlIGFzc2lnbmVkIGEgbnVtYmVyIHRvIGFuIG9iamVjdCwgeW91IGNhbiB0aGVuIHVzZSBpdCBpbgpvdGhlciBjYWxjdWxhdGlvbnM6CgpgYGB7ciBzcXJ0LCBldmFsPVRSVUUsIHJlc3VsdHM9J3Nob3cnfQp3aW5nc3Bhbl9pbiAqIDIuNTQKYGBgCgojIyMgT2JqZWN0IG5hbWVzCgpPYmplY3RzIGNhbiBiZSBnaXZlbiBhbnkgbmFtZSBzdWNoIGFzIGB4YCwgYGN1cnJlbnRfdGVtcGVyYXR1cmVgLCBvcgpgc3ViamVjdF9pZGAuCgpTb21lIHBvaW50ZXJzIG9uIG9iamVjdCBuYW1lczoKCi0gTWFrZSB0aGVtIGRlc2NyaXB0aXZlIHlldCBub3QgdG9vIGxvbmcuCgotIFRoZXkgY2Fubm90IHN0YXJ0IHdpdGggYSBudW1iZXIgKGAyeGAgaXMgbm90IHZhbGlkLCBidXQgYHgyYCBpcyleW1RoZXJlIGFyZSBzb21lIG5hbWVzIHRoYXQKY2Fubm90IGJlIHVzZWQgYmVjYXVzZSB0aGV5IGFyZSB0aGUgbmFtZXMgb2YgZnVuZGFtZW50YWwgZnVuY3Rpb25zIGluIFIgKGUuZy4sCmBpZmAsIGBlbHNlYCwgYGZvcmAsIHNlZQpbaGVyZV0oaHR0cHM6Ly9zdGF0LmV0aHouY2gvUi1tYW51YWwvUi1kZXZlbC9saWJyYXJ5L2Jhc2UvaHRtbC9SZXNlcnZlZC5odG1sKQpmb3IgYSBjb21wbGV0ZSBsaXN0KS4gSW4gZ2VuZXJhbCwgZXZlbiBpZiBpdCdzIGFsbG93ZWQsIGl0J3MgYmVzdCB0byBub3QgdXNlCm90aGVyIGZ1bmN0aW9uIG5hbWVzIChlLmcuLCBgY2AsIGBUYCwgYG1lYW5gLCBgZGF0YWAsIGBkZmAsIGB3ZWlnaHRzYCkuIEluIGRvdWJ0CmNoZWNrIHRoZSBoZWxwIHRvIHNlZSBpZiB0aGUgbmFtZSBpcyBhbHJlYWR5IGluIHVzZS5dLgoKLSBOb3RlOiBSIGlzIGNhc2Ugc2Vuc2l0aXZlIChlLmcuLCBgd2VpZ2h0YCBpcyBkaWZmZXJlbnQgZnJvbSBgV2VpZ2h0YCkuCgotIEEgc3R5bGUgcmVjb21tZW5kYXRpb24gaXMgdG8gYXZvaWQgY2FwaXRhbCBsZXR0ZXJzIChlLmcuLCBgdG90YWxXZWlnaHRgKQogIGFuZCBkb3RzIChgdG90YWwud2VpZ2h0YCkgdG8gc2VwYXJhdGUgd29yZHMuIEluc3RlYWQsIHN0aWNrIHRvIGxvd2VyY2FzZSwKICBhbmQgdXNlIHVuZGVyc2NvcmVzIChgdG90YWxfd2VpZ2h0YCkgdG8gc2VwYXJhdGUgd29yZHMuXltJdCBpcyBhbHNvIHJlY29tbWVuZGVkCiAgdG8gdXNlIG5vdW5zIGZvciB2YXJpYWJsZSBuYW1lcywgYW5kIHZlcmJzIGZvciBmdW5jdGlvbiBuYW1lcy4KICBJdCdzIGltcG9ydGFudCB0byBiZSBjb25zaXN0ZW50IGluIHRoZSBzdHlsaW5nIG9mIHlvdXIgY29kZQogICh3aGVyZSB5b3UgcHV0IHNwYWNlcywgaG93IHlvdSBuYW1lIHZhcmlhYmxlcywgZXRjLikuCiAgSW4gUiwgdHdvIHBvcHVsYXIgc3R5bGUgZ3VpZGVzIGFyZSBbSGFkbGV5IFdpY2toYW0nc10oaHR0cDovL2Fkdi1yLmhhZC5jby5uei9TdHlsZS5odG1sKQogIGFuZCBbR29vZ2xlJ3NdKGh0dHBzOi8vZ29vZ2xlLmdpdGh1Yi5pby9zdHlsZWd1aWRlL1JndWlkZS54bWwpLl0KCjxkaXYgY2xhc3M9ImFsZXJ0IHB1enpsZSI+CjxkaXY+CgojIyMjIENoYWxsZW5nZQoKV2hhdCBpcyB0aGUgdmFsdWUgb2YgYHlgIGFmdGVyIGRvaW5nIGFsbCBvZiB0aGUgZm9sbG93aW5nPwoKYGBge3IsIGV2YWw9RkFMU0V9CnggPC0gNTAKeSA8LSB4ICogMgp4IDwtIDgwCmBgYAoKPGRldGFpbHM+Cgo8c3VtbWFyeT4KJm5ic3A7ICBgciBpY29uOjpmYSgiY2hlY2stY2lyY2xlIilgICZuYnNwOyBTb2x1dGlvbiAoY2xpY2sgaGVyZSkKPC9zdW1tYXJ5PgoKT2JqZWN0cyBkb24ndCBnZXQgbGlua2VkIHRvIGVhY2ggb3RoZXIsIHNvIGlmIHlvdSBjaGFuZ2Ugb25lLAppdCB3b24ndCBhZmZlY3QgdGhlIHZhbHVlcyBvZiBhbnkgb3RoZXJzLgpUaGVyZWZvcmUsIGB5YCB3aWxsIGtlZXAgdGhlIHZhbHVlIGAxMDBgLgoKPC9kZXRhaWxzPgoKPC9kaXY+CjwvZGl2PgoKIyMjIE9iamVjdHMgaW4geW91ciB3b3Jrc3BhY2UKClRoZSBvYmplY3RzIHlvdSBjcmVhdGUgZ2V0IGFkZGVkIHRvIHlvdXIgIndvcmtzcGFjZSIuIFlvdSBjYW4gbGlzdCB0aGUKY3VycmVudCBvYmplY3RzIHdpdGggYGxzKClgLiBSU3R1ZGlvIGFsc28gc2hvd3MgdGhlIG9iamVjdHMgaW4gdGhlCioqRW52aXJvbm1lbnQgcGFuZWwuKioKCgotLS0tCgojIyBGdW5jdGlvbnMKCkVhcmxpZXIsIHdlIGRpdmlkZWQgYDIwM2AgYnkgYDIuNTRgLApidXQgd2hhdCBpZiB3ZSB3YW50ZWQgdG8gcm91bmQgdGhlIHJlc3VsdGluZyBudW1iZXI/Ckxpa2UgZm9yIG1hbnkgdGhpbmdzIHlvdSBtYXkgd2FudCB0byBkbyBpbiBSLCB0aGVyZSBpcyBhICoqZnVuY3Rpb24qKiBmb3IgdGhhdC4KCkZ1bmN0aW9ucyBhcmUgdXNlZCBieSB0eXBpbmcgdGhlaXIgbmFtZSBmb2xsb3dlZCBieSBwYXJlbnRoZXNlczoKCmBgYHtyIGxvZywgZXZhbD1UUlVFLCByZXN1bHRzPSdzaG93J30Kcm91bmQoMjAzIC8gMi41NCkKYGBgCgpIZXJlLCBgcm91bmQoKWAgaXMgYSBfZnVuY3Rpb25fIHRoYXQgcm91bmRzIGEgbnVtYmVyLgpUaGUgdmFsdWUgaW4gdGhlIHBhcmVudGhlc2VzIGlzIGNhbGxlZCBhIGZ1bmN0aW9uICIqYXJndW1lbnQqIiwKd2hpY2ggaXMgdXNlZCBpbiB0aGUgZXhlY3V0aW9uIG9mIHRoZSBmdW5jdGlvbi4KCiMjIyBVc2luZyBuYW1lZCBhcmd1bWVudHMKCkZ1bmN0aW9ucyBjYW4gaGF2ZSBtb3JlIHRoYW4gb25lIGFyZ3VtZW50LCBhbmQgc29tZSBvZiB0aGVtIG1heSBoYXZlIGRlZmF1bHQgdmFsdWVzLgoKVGhlcmUgYXJlIHNvbWUgZnVuY3Rpb25zIHRoYXQgdGFrZSBtYW55IGFyZ3VtZW50cyBhbmQgaXQgY2FuIGdldCBjb25mdXNpbmcKdHJ5aW5nIHRvIGtlZXAgdGhlbSBpbiBvcmRlci4KSW4gdGhhdCBjYXNlLCBpdCBpcyBiZXR0ZXIgdG8gZXhwbGljaXRseSBuYW1lIHRoZSBhcmd1bWVudHMuCgpXaGVuIHlvdSB0eXBlIGEgZnVuY3Rpb24gbmFtZSBhbmQgcGF1c2UgZm9yIGEgbW9tZW50LAp0aGUgYXJndW1lbnRzLCB0aGVpciBuYW1lcywgYW5kIHRoZWlyIGRlZmF1bHQgdmFsdWVzCihpLmUuLCB0aGUgdmFsdWUgaWYgdGhlIGFyZ3VtZW50IGlzIGxlZnQgdW5zcGVjaWZpZWQpIHdpbGwgYmUgc2hvd24uCgo8ZGV0YWlscz4KPHN1bW1hcnk+CiZuYnNwOyAgYHIgaWNvbjo6ZmEoInF1ZXN0aW9uLWNpcmNsZSIpYCAmbmJzcDsgV2hhdCBpcyB0aGUgc2Vjb25kIGFyZ3VtZW50IGZvciBgcm91bmQoKWAgYW5kIHdoYXQgaXMgaXRzIGRlZmF1bHQgdmFsdWU/IChDbGljayBoZXJlKQo8L3N1bW1hcnk+Cgpgcm91bmRgIGhhcyBhIHNlY29uZCBhcmd1bWVudCBgZGlnaXRzYCB3aG9zZSBkZWZhdWx0IAppcyBgMGAsIHN1Y2ggdGhhdCBudW1iZXJzIHdpbGwgYmUgcm91bmRlZCB0byB3aG9sZSBpbnRlZ2Vycy4KCjwvZGV0YWlscz4KCjxicj4KCkJlbG93IGlzIGFuIGV4YW1wbGUgdXNpbmcgbmFtZWQgYXJndW1lbnRzIHdpdGggYHJvdW5kKClgLgpXaGVuIHRoZSBhcmd1bWVudHMgYXJlIG5hbWVkLCB0aGUgb3JkZXIgZG9lc24ndCBtYXR0ZXIhCllvdSBtaWdodCBhbHNvIGVudGVyIHRoZSBmaXJzdCBmZXcgaW1wb3J0YW50IGFyZ3VtZW50cyBwb3NpdGlvbmFsbHksCmFuZCBsYXRlciBvbmVzIGJ5IG5hbWluZyB0aGVtLgoKYGBge3Igcm91bmRfd19uYW1lZF9hcmdzfQpyb3VuZCh4ID0gMS41Mzg0NjIsIGRpZ2l0cyA9IDIpCgpyb3VuZChkaWdpdHMgPSAyLCB4ID0gMS41Mzg0NjIpCgpyb3VuZCgxLjUzODQ2MiwgZGlnaXRzID0gMikKYGBgCgo8YnI+CgpBbHNvIGhlcmUsIHdlIGNhbiBkaXJlY3RseSBwbHVnIGluIG9iamVjdHM6CgpgYGByCndpbmdzcGFuX2luIDwtIDIwMyAvIDIuNTQKcm91bmQod2luZ3NwYW5faW4pCmBgYAoKT3IgbmVzdCBmdW5jdGlvbnMgLS0gaGVyZSB3ZSBhcmUgYWRkaW5nIHRoZSBgbG9nKClgIGZ1bmN0aW9uOgoKYGBgcgpsb2cocm91bmQoMjAzIC8gMi41NCApKQpgYGAKCjxkZXRhaWxzPgoKPHN1bW1hcnk+CiZuYnNwOyAgYHIgaWNvbjo6ZmEoInF1ZXN0aW9uLWNpcmNsZSIpYCAmbmJzcDsgKipXaGF0IGlzIHRoZSBvcmRlciBvZiBleGVjdXRpb24gaW4gdGhlIGxhc3QgY29tbWFuZD8qKiAoQ2xpY2sgaGVyZSkKPC9zdW1tYXJ5PgoKYHJvdW5kKClgIGlzIGV4ZWN1dGVkIGZpcnN0LAphbmQgdGhlIG91dHB1dCBvZiBgcm91bmQoKWAgaXMgdXNlZCBhcyB0aGUgaW5wdXQgb2YgYGxvZygpYC4KCjwvZGV0YWlscz4KCjxicj4KCi0tLS0tCgojIyBHZXR0aW5nIGhlbHAKCkFzIHdlIHNhdywgd2hlbiB3ZSB0eXBlZCBgcm91bmRgIGFuZCBwYXVzZWQgZm9yIGEgbW9tZW50LAp3ZSBnb3QgYSBwb3AtdXAgd2l0aCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZnVuY3Rpb24uCgpBbHRlcm5hdGl2ZWx5LCB5b3UgY291bGQgdHlwZToKCmBgYHtyIGhlbHAsIGV2YWw9RkFMU0V9Cj9yb3VuZApgYGAKCi4uLiBhbmQgdGhlIGRvY3VtZW50YXRpb24gZm9yIHRoZSBmdW5jdGlvbiB3aWxsIHNob3cgdXAgaW4gdGhlIGxvd2VyLXJpZ2h0IHBhbmUuCgpUaGlzIGRvY3VtZW50YXRpb24gaXMgb2Z0ZW4gYSBiaXQgX3Rvb18gZGV0YWlsZWQsIGFuZCBjYW4gYmUgdGVyc2UsCnNvIGl0IHRha2VzIHNvbWUgcHJhY3RpY2UgdG8gcmVhZC4KKlVzYWdlKiwgKkFyZ3VtZW50cyosIGFuZCBhdCB0aGUgYm90dG9tLCAqRXhhbXBsZXMqLCBhcmUgbW9zdCB1c2VmdWwuCgpHb29nbGluZyB3aGF0IHlvdSB3YW50IHRvIGRvLCBldmVuIGlmIHlvdSBkb24ndCBrbm93IHdoZXRoZXIgYSBmdW5jdGlvbgpleGlzdHMsIHdpbGwgd29yayB0b28gKGUuZy4gInJvdW5kaW5nIGEgbnVtYmVyIGluIHIiKS4KCi0tLS0KCiMjIFZlY3RvcnMKCkEgKip2ZWN0b3IqKiBpcyB0aGUgbW9zdCBjb21tb24gYW5kIGJhc2ljIGRhdGEgc3RydWN0dXJlIGluIFIsCmFuZCBpcyBjb21wb3NlZCBvZiBhICoqc2VyaWVzIG9mIHZhbHVlcyBvZiB0aGUgc2FtZSB0eXBlKiouCgpXZSBjYW4gYXNzaWduIGEgc2VyaWVzIG9mIHZhbHVlcyB0byBhIHZlY3RvciB1c2luZyB0aGUgYGMoKWAgZnVuY3Rpb24KKGZvciAiY29tYmluZSIpLiBGb3IgZXhhbXBsZToKCmBgYHtyLCBldmFsID0gVFJVRX0Kd2luZ3NwYW5fY20gPC0gYygxMS44LCAyMDMsIDE4LjIsIDI3LjkpCmBgYAoKQSB2ZWN0b3IgY2FuIGFsc28gY29udGFpbiBjaGFyYWN0ZXJzIC0tIGJ1dCBhZ2FpbiwgcXVvdGluZyBpcyBpbXBvcnRhbnQsCm9yIFIgd2lsbCB0aGluayB0aGUgc3RyaW5ncyBhcmUgb2JqZWN0czoKCmBgYHtyLCBldmFsID0gVFJVRX0KYmlyZHMgPC0gYygiaHVtbWluZ2JpcmQiLCAiYmFsZF9lYWdsZSIsICJjaGlja2FkZWUiLCAiY2FyZGluYWwiKQpgYGAKCjxicj4KCkFzIG1lbnRpb25lZCwgYWxsIG9mIGEgdmVjdG9yJ3MgZWxlbWVudHMgYXJlIG9mIHRoZSBzYW1lIHR5cGUgb2YgZGF0YS4gICAKVGhlIGZ1bmN0aW9uIGBjbGFzcygpYCBpbmRpY2F0ZXMgd2hhdCBraW5kIG9mIGRhdGEgeW91IGFyZSB3b3JraW5nIHdpdGg6CgpgYGB7ciwgZXZhbCA9IFRSVUUsIHJlc3VsdHM9J3Nob3cnfQpjbGFzcyh3aW5nc3Bhbl9jbSkKY2xhc3MoYmlyZHMpCmBgYAoKIyMjIERhdGEgdHlwZXMgaW4gUgoKVGhlIGNsYXNzZXMgd2Ugc2F3IGFib3ZlIGFyZSBkaWZmZXJlbnQgdHlwZXMgb2YgKiphdG9taWMgdmVjdG9ycyoqLApSJ3Mgc2ltcGxlc3QgKipkYXRhIHR5cGUqKi4KVGhlIDQgbW9zdCBjb21tb24gYXRvbWljIHZlY3RvciB0eXBlcyBhcmU6CgotICoqYCJudW1lcmljImAqKiAob3IgYCJkb3VibGUiYCkgLS0gZmxvYXRpbmcgcG9pbnQgbnVtYmVycwoKLSAqKmAiY2hhcmFjdGVyImAqKiAtLSBjaGFyYWN0ZXIgc3RyaW5ncwoKLSAqKmAibG9naWNhbCJgKiogLS0gYFRSVUVgIGFuZCBgRkFMU0VgIChhbHNvIGtub3duIGFzIGJvb2xlYW4pCgotICoqYCJpbnRlZ2VyImAqKiAtLSBpbnRlZ2VyIG51bWJlcnMKCjxkZXRhaWxzPgo8c3VtbWFyeT4KJm5ic3A7IGByIGljb246OmZhKCJpbmZvLWNpcmNsZSIpYCAmbmJzcDsgKipWZWN0b3IgY29lcmNpb24qKjogd2hlbiBub3QgYWxsIGVsZW1lbnRzIGFyZSBvZiB0aGUgc2FtZSB0eXBlLiAoQ2xpY2sgaGVyZSkKPC9zdW1tYXJ5PgoKV2hhdCBoYXBwZW5zIGlmIHdlIHRyeSB0byBtaXggdmVjdG9yIHR5cGVzIChlLmcuLCAiY2hhcmFjdGVyIGFuZCBudW1lcmljIikKaW4gYSBzaW5nbGUgdmVjdG9yPwoqKlIgY29udmVydHMgdGhlbSB0byBhbGwgYmUgdGhlIHNhbWUgdHlwZSwgYW5kIGl0IGRvZXMgc28gd2l0aG91dCB0ZWxsaW5nIHVzIGFib3V0IGl0LioqCgpXaGF0IHdpbGwgaGFwcGVuIGluIGVhY2ggb2YgdGhlIGZvbGxvd2luZyBleGFtcGxlcz8gICAgCihIaW50OiB1c2UgYGNsYXNzKClgIHRvIGNoZWNrIHRoZSBkYXRhIHR5cGUgb2YgeW91ciBvYmplY3RzKQoKYGBgcgpudW1fY2hhciA8LSBjKDEsIDIsIDMsICJhIikKCm51bV9sb2dpY2FsIDwtIGMoMSwgMiwgMywgVFJVRSkKCmNoYXJfbG9naWNhbCA8LSBjKCJhIiwgImIiLCAiYyIsIFRSVUUpCgp0cmlja3kgPC0gYygxLCAyLCAzLCAiNCIpCmBgYAoKWW91J3ZlIHByb2JhYmx5IG5vdGljZWQgdGhhdCBvYmplY3RzIG9mIGRpZmZlcmVudCB0eXBlcyBnZXQKY29udmVydGVkIGludG8gYSBzaW5nbGUsIHNoYXJlZCB0eXBlIHdpdGhpbiBhIHZlY3Rvci4gSW4gUiwgd2UKY2FsbCBjb252ZXJ0aW5nIG9iamVjdHMgZnJvbSBvbmUgY2xhc3MgaW50byBhbm90aGVyIGNsYXNzCl9jb2VyY2lvbl8uIFRoZXNlIGNvbnZlcnNpb25zIGhhcHBlbiBhY2NvcmRpbmcgdG8gYSBoaWVyYXJjaHksCndoZXJlYnkgc29tZSB0eXBlcyBnZXQgcHJlZmVyZW50aWFsbHkgY29lcmNlZCBpbnRvIG90aGVyCnR5cGVzLgoKPC9kZXRhaWxzPgoKPGJyPgoKIyMjIFZlY3Rvcml6YXRpb24hCgpMZXQncyBzYXkgd2Ugd2FudGVkIHRvIGNvbnZlcnQgb3VyIHZlY3RvciBvZiB3aW5nc3BhbnMgdG8gaW5jaGVzOgpkaXZpZGluZyBlYWNoIGxlbmd0aCBpbiBjZW50aW1ldGVycyBieSAyLjU0LgoKSG93IGNvdWxkIHdlIGRvIHRoaXMgcXVpY2tseT8KCmBgYHtyLCBldmFsID0gVFJVRSwgcmVzdWx0cyA9ICJzaG93In0KIyB3aW5nc3Bhbl9jbSA8LSBjKDExLjgsIDIwMywgMTguMiwgMjcuOSkgICMgU3RpbGwgd29ya2luZyB3aXRoIHRoZSBzYW1lIHdpbmdzcGFuIHZlY3RvcgoKd2luZ3NwYW5faW4gPC0gd2luZ3NwYW5fY20gLyAyLjU0CndpbmdzcGFuX2luCmBgYAoKPGRldGFpbHM+Cgo8c3VtbWFyeT4KJm5ic3A7ICBgciBpY29uOjpmYSgiaW5mby1jaXJjbGUiKWAgJm5ic3A7IFdoeSBkb2VzIHRoaXMgd29yaz8KPC9zdW1tYXJ5PgoKUiAiKnZlY3Rvcml6ZXMqIiBvcGVyYXRpb25zIHdoZW5ldmVyIGl0IGNhbi4KVGhpcyBtZWFucyB0aGF0IGluIHRoaXMgY2FzZSwgZWFjaCBlbGVtZW50IGluIHRoZSB2ZWN0b3IgYHdlaWdodHNfY21gIHdpbGwgYmUgZGl2aWRlZApieSAyLjU0IC0tIHRoaXMgbnVtYmVyIGlzICoqcmVjeWNsZWQqKiB0byBtYXRjaCB0aGUgbnVtYmVyIG9mIHdlaWdodHMuCgpGb3IgYSBwcm9ncmFtbWluZyBsYW5ndWFnZSwgdGhpcyBpcyB1bnVzdWFsIGJlaGF2aW9yIC0tIGJ1dCBpdCBpcyB2ZXJ5IHVzZWZ1bCEKCjwvZGV0YWlscz4KCjxicj4KClNpbWlsYXJseSwgd2UgY2FuIHVzZSB0d28gdmVjdG9ycyBvZiBlcXVhbCBsZW5ndGggdG8gcXVpY2tseSBvcGVyYXRlIG9uIGVhY2ggZWxlbWVudApvZiB0aGUgdmVjdG9yOgoKYGBge3IgZXZhbCA9IFRSVUUsIHJlc3VsdHMgPSAgInNob3cifQpzaXplX2NtIDwtIGMoNy42MiwgOTAsIDEzLjEsIDIxLjgpCgpyYXRpbyA8LSB3aW5nc3Bhbl9jbSAvIHNpemVfY20KcmF0aW8KCmBgYAo8YnI+CgoKIyMjIERhdGEgc3RydWN0dXJlcyBpbiBSCgpXaGlsZSB2ZWN0b3JzIGNhbiBiZSBjb21wb3NlZCBvZiBvbmUgb2Ygc2V2ZXJhbCAqZGF0YSB0eXBlcyosIHRoZXksIGluIHR1cm4sCmFyZSBvbmUgb2Ygc2V2ZXJhbCAqKmRhdGEgc3RydWN0dXJlcyoqIHRoYXQgUiB1c2VzLgpPdGhlciBpbXBvcnRhbnQgb25lcyBhcmU6CgotICoqYGRhdGEuZnJhbWVgKiogLS0gQSByZWN0YW5ndWxhciBkYXRhIHN0cnVjdHVyZSB3aGVyZSBlYWNoIGNvbHVtbiBjYW4gYmUgYQogIGRpZmZlcmVudCBkYXRhIHR5cGUuCgotICoqYG1hdHJpeGAqKiAtLSBBIHJlY3Rhbmd1bGFyIGRhdGEgc3RydWN0dXJlIG9mIGEgc2luZ2xlIGRhdGEgdHlwZS4KCi0gKipgbGlzdGAqKiAtLSBBIHZlcnkgZmxleGlibGUgZGF0YSBzdHJ1Y3R1cmUgdGhhdCB3ZSB3aWxsIG5vdCBmdXJ0aGVyIGRpc2N1c3MgaGVyZS4KCi0gKipgZmFjdG9yYCoqIC0tIENoYXJhY3RlciBzdHJpbmdzIHdpdGggYSBkaXNjcmV0ZSBzZXQgb2YgcG9zc2libGUgdmFsdWVzLAogIHVzZWQgbW9zdGx5IGZvciBzdGF0aXN0aWNhbCB0ZXN0cyBhbmQgd2hlbiBwbG90dGluZy4KCgotLS0tCgojIyBEYXRhIEZyYW1lcwoKQSBkYXRhIGZyYW1lIChmb3JtYWwgb2JqZWN0IHR5cGU6IGBkYXRhLmZyYW1lYCkgaXMgYSByZWN0YW5ndWxhciBkYXRhIHN0cnVjdHVyZQppbiB3aGljaDoKCiAgLSBSb3dzIGFyZSBvYnNlcnZhdGlvbnMgYW5kIGNvbHVtbnMgYXJlIHZhcmlhYmxlcy4KICAtIEVhY2ggY29sdW1uIGNhbiBiZSBvZiBhIGRpZmZlcmVudCB0eXBlIChudW1lcmljLCBjaGFyYWN0ZXIsIGV0Yy4pLAogIC0gU2luY2UgZWFjaCBjb2x1bW4gaXMgYSB2ZWN0b3IsCiAgICBhbGwgdGhlIHZhbHVlcyAoY2VsbHMpIHdpdGhpbiBhIGNvbHVtbiBhcmUgb2YgdGhlIHNhbWUgdHlwZS4KICAtIEFsbCBjb2x1bW5zIGhhdmUgdGhlIHNhbWUgbGVuZ3RoLgoKIyMjIENyZWF0ZSwgd3JpdGUsIGFuZCByZWFkIGEgZGF0YSBmcmFtZQoKV2UgY2FuIGVhc2lseSBjcmVhdGUgYSBkYXRhIGZyYW1lIGJ5IGhhbmQgdXNpbmcgdGhlIGBkYXRhLmZyYW1lKClgIGZ1bmN0aW9uIGFuZAoiYGNvbHVtbl9uYW1lID0gY29sdW1uX3ZlY3RvcmAiIG5vdGF0aW9uIGZvciBlYWNoIGNvbHVtbjoKCmBgYHtyIGV2YWw9VFJVRSwgcmVzdWx0cz0nc2hvdyd9CmJpcmRzX2RmIDwtIGRhdGEuZnJhbWUoc3BlY2llcyA9IGJpcmRzLAogICAgICAgICAgICAgICAgICAgICAgIHdpbmdzcGFuID0gd2luZ3NwYW5fY20sCiAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IHNpemVfY20sCiAgICAgICAgICAgICAgICAgICAgICAgbl9lZ2dzID0gYygyLCAyLCA3LCA0KSkgCmBgYAoKTW9zdCBvZnRlbiwgaG93ZXZlciwgeW91J2xsIGJlIHJlYWRpbmcgeW91ciBkYXRhIGZyYW1lcyBmcm9tIGZpbGVzLgpBbmQgeW91J2xsIGFsc28gd2FudCB0byBzYXZlIHlvdXIgbW9kaWZpZWQgZGF0YSBmcmFtZXMuCgpTbyBsZXQncyBwcmFjdGljZSB3cml0aW5nIGFuZCByZWFkaW5nIGEgZGF0YSBmcmFtZToKCmBgYHtyfQojIFdyaXRlIGEgZGF0YSBmcmFtZSB0byBDU1YgZm9ybWF0Ogp3cml0ZS5jc3YoeCA9IGJpcmRzX2RmLCBmaWxlID0gImJpcmQtZGF0YS5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKYGBgCgpOb3cgd2UgcmVhZCBvdXIgZGF0YSBmcmFtZSBiYWNrIGluOgoKYGBge3J9CmJpcmRzX2RmXzIgPC0gcmVhZC5jc3YoJ2JpcmQtZGF0YS5jc3YnKQoKIyMgTGV0J3MgY29tcGFyZSB0aGUgdHdvIGRhdGEgZnJhbWVzOgpiaXJkc19kZgoKYmlyZHNfZGZfMgpgYGAKCiMjIyBJbnNwZWN0aW5nIGEgRGF0YSBGcmFtZQoKVXNlIGBzdHIoKWAgdG8gbG9vayBhdCB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhLgoKYGBge3Igc3RyLCBldmFsPUZBTFNFfQpzdHIoYmlyZHNfZGYpCmBgYAoKVGhpcyB0ZWxscyB1cyB0aGUgbnVtYmVyIHJvd3MgYW5kIGNvbHVtbnMsCmFuZCBmb3IgZWFjaCBjb2x1bW4sIGdpdmVzIGluZm9ybWF0aW9uIGFib3V0IHRoZSBkYXRhCnR5cGUgYW5kIHNob3dzIHRoZSBmaXJzdCBmZXcgdmFsdWVzLgoKQW5vdGhlciB1c2VmdWwgZnVuY3Rpb24gaXMgYHN1bW1hcnkoKWAuCgpgYGB7ciBzdW1tYXJ5LCBldmFsPUZBTFNFfQpzdW1tYXJ5KGJpcmRzX2RmKQpgYGAKCkZpbmFsbHksIGluIFJTdHVkaW8sIHdlIGNhbiBjbGljayBvbiBhbiBvYmplY3QgaW4gdGhlIGBGaWxlc2AgcGFuZSwgb3IKZXF1aXZhbGVudGx5LCB0eXBlOgogIAogIGBgYHtyIFZpZXcsIGV2YWw9RkFMU0V9ClZpZXcoYmlyZHNfZGYpCmBgYAoKIyMjIEFuIE92ZXJ2aWV3IG9mIEZ1bmN0aW9ucyB0byBHZXQgYW4gT3ZlcnZpZXcKCiogKipTaXplOioqCiAgKiBgZGltKClgIC0tICpEaW0qZW5zaW9uczogYyhudW1iZXIgb2Ygcm93cywgbnVtYmVyIG9mIGNvbHVtbnMpCiAgKiBgbnJvdygpYCAtLSBOdW1iZXIgb2Ygcm93cwogICogYG5jb2woKWAgLS0gTnVtYmVyIG9mIGNvbHVtbnMKICAqIGBsZW5ndGgoKWAgLS0gRm9yIGEgZGF0YWZyYW1lOiBudW1iZXIgb2YgY29sdW1ucy4gRm9yIGEgdmVjdG9yOiBudW1iZXIgb2YgZWxlbWVudHMuCgoqICoqQ29udGVudDoqKgogICogYGhlYWQoKWAgLS0gc2hvd3MgdGhlIGZpcnN0IDYgcm93cwogICogYHRhaWwoKWAgLS0gc2hvd3MgdGhlIGxhc3QgNiByb3dzCgoqICoqTmFtZXM6KioKICAqIGBuYW1lcygpYCBvciBgY29sbmFtZXMoKWAgLS0gY29sdW1uIG5hbWVzCiAgKiBgcm93bmFtZXMoKWAgLS0gcm93IG5hbWVzCgoqICoqU3VtbWFyeToqKgogICogYHN0cigpYCAtLSBzdHJ1Y3R1cmUgb2YgdGhlIG9iamVjdCBhbmQgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGNsYXNzLCBsZW5ndGggYW5kIGNvbnRlbnQgb2YgZWFjaCBjb2x1bW4KICAqIGBzdW1tYXJ5KClgIC0tIHN1bW1hcnkgc3RhdGlzdGljcyBmb3IgZWFjaCBjb2x1bW4KICAqIGBza2ltcjo6c2tpbSgpYCAtLSB2ZXJ5IG5pY2Ugc3VtbWFyeSwgbmVlZCB0byBpbnN0YWxsICpza2ltciogd2l0aCBgaW5zdGFsbC5wYWNrYWdlcyhza2ltcilgLgoKCi0tLS0KCiMjIE1pc2NlbGxhbmVvdXMKCiMjIyBNaXNzaW5nIGRhdGEKCkFzIFIgd2FzIGRlc2lnbmVkIHRvIGFuYWx5emUgZGF0YXNldHMsIGl0IGluY2x1ZGVzIHRoZSBjb25jZXB0IG9mIG1pc3NpbmcgZGF0YQood2hpY2ggaXMgdW5jb21tb24gaW4gb3RoZXIgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzKS4KTWlzc2luZyBkYXRhIGFyZSByZXByZXNlbnRlZCBhcyBgTkFgIChmb3IgIk5vdCBBdmFpbGFibGUiKS4KCmBgYHtyIGNyZWF0ZV92ZWNfd2l0aF9taXNzaW5nfQpoZWlnaHRzIDwtIGMoMiwgNCwgNCwgTkEsIDYpCmBgYAoKV2hlbiBkb2luZyBvcGVyYXRpb25zIG9uIG51bWJlcnMsIG1vc3QgZnVuY3Rpb25zIHdpbGwgcmV0dXJuIGBOQWAgaWYgdGhlIGRhdGEKeW91IGFyZSB3b3JraW5nIHdpdGggaW5jbHVkZSBtaXNzaW5nIHZhbHVlcy4gSXQgaXMgYSBzYWZlciBiZWhhdmlvciBhcyBvdGhlcndpc2UKeW91IG1heSBvdmVybG9vayB0aGF0IHlvdSBhcmUgZGVhbGluZyB3aXRoIG1pc3NpbmcgZGF0YS4KWW91IGNhbiBhZGQgdGhlIGFyZ3VtZW50IGBuYS5ybT1UUlVFYCB0byBjYWxjdWxhdGUgdGhlIHJlc3VsdCB3aGlsZSBpZ25vcmluZyB0aGUgbWlzc2luZyB2YWx1ZXMuCgpgYGB7ciBuYV9ybX0KbWVhbihoZWlnaHRzKQoKbWVhbihoZWlnaHRzLCBuYS5ybSA9IFRSVUUpCmBgYAoKPGJyPgoKSWYgeW91ciBkYXRhIGluY2x1ZGVzIG1pc3NpbmcgdmFsdWVzLCB5b3UgbWF5IHdhbnQgdG8gYmVjb21lIGZhbWlsaWFyIHdpdGggdGhlCmZ1bmN0aW9ucyBgaXMubmEoKWAgYW5kIGBuYS5vbWl0KClgLgoKYGBge3IgZXh0cmFjdF9ub25fbWlzc2luZ30KIyMgRXh0cmFjdCB0aG9zZSBlbGVtZW50cyB3aGljaCBhcmUgbm90IG1pc3NpbmcgdmFsdWVzOgpoZWlnaHRzWyFpcy5uYShoZWlnaHRzKV0KCiMjIFNob3J0Y3V0IHRvIGRvIHRoZSBzYW1lOgpuYS5vbWl0KGhlaWdodHMpCmBgYAoKIyMjIFBhY2thZ2VzCgpUaGUgZnVuY3Rpb25zIHRoYXQgd2UgaGF2ZSBiZWVuIHVzaW5nIHNvIGZhciAoYW5kICptYW55KiwgKm1hbnkqIG1vcmUpIGFyZSBhdmFpbGFibGUKaW4gYW55IFIgc2Vzc2lvbiBhcyBzb29uIGFzIHlvdSBzdGFydCBSICh3ZSByZWZlciB0byB0aGlzIGZ1bmN0aW9uYWxpdHkgYXMgKiJiYXNlIFIiKikuCkhvd2V2ZXIsIHdoZW4gZG9pbmcgc3BlY2lhbGl6ZWQgYW5hbHlzZXMgc3VjaCBhcyBpbiBtaWNyb2Jpb21pY3MsCnJhdGhlciB0aGFuIGNvZGluZyB1cCBldmVyeXRoaW5nIHVzaW5nIHRoZSBiYXNpYyBidWlsZGluZyBibG9ja3MgaW4gUiwKd2UgY2FuIGxvYWQgYWRkLW9uIGNvZGUgdGhhdCB3aWxsIGFsbG93IHVzIHRvIHVzZSAqImhpZ2gtbGV2ZWwiKiBmdW5jdGlvbnMKc3BlY2lmaWNhbGx5IGdlYXJlZCB0b3dhcmRzIHRoZSBlZmZlY3RpdmUgYW5hbHlzZXMgb2Ygc3VjaCBkYXRhLgoKVGhpcyB0eXBlIG9mIGFkZC1vbiBjb2RlIGlzIGRpc3RyaWJ1dGVkIGluIFIgKnBhY2thZ2VzKi4KVGhlIGRlZmF1bHQgKnJlcG9zaXRvcnkqIGZvciBSIHBhY2thZ2VzIGlzICoqQ1JBTioqLgpUaGUgcGFja2FnZXMgb24gQ1JBTiBoYXZlIHVuZGVyZ29uZSBhIGNlcnRhaW4gbGV2ZWwgb2YgdmV0dGluZywKYW5kIGNhbiBiZSBlYXNpbHkgaW5zdGFsbGVkIHdpdGggYGluc3RhbGwucGFja2FnZXMoKWAgZnVuY3Rpb24sIGZvciBpbnN0YW5jZToKCmBgYHtyLCBldmFsPUZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQpgYGAKCklmIHlvdeKAmXJlIGRvaW5nIGJpb2luZm9ybWF0aWMgYW5hbHlzZXMgaW4gUiwgYXMgd2Ugd2lsbCBiZSBkb2luZywKeW91IHdpbGwgZW5jb3VudGVyIHBhY2thZ2VzIHRoYXQgYXJlIG5vdCBvbiBDUkFOIGJ1dCBhcmUgb24gIkJpb2NvbmR1Y3RvciIuClRvIGluc3RhbGwgYSBwYWNrYWdlIGZyb20gQmlvY29uZHVjdG9yLCB1c2UgdGhlIEJpb2NNYW5hZ2VyIHBhY2thZ2Ug4oCTIGZvciBleGFtcGxlOgoKYGBge3IsIGV2YWw9RkFMU0V9Cmluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikgICMgSW5zdGFsbCB0aGUgQmlvY01hbmFnZXIgcGFja2FnZQpCaW9jTWFuYWdlcjo6aW5zdGFsbCgiZGFkYTIiKSAgICAjIEluc3RhbGwgdGhlIGVkZ2VSIHBhY2thZ2UgZnJvbSBCaW9jb25kdWN0b3IKYGBgCgojIyMgU2F2aW5nIHlvdXIgZGF0YQoKU29tZSB2ZXJ5IGJyaWVmIG5vdGVzIG9uIHNhdmluZyB5b3VyIGRhdGEgaW4gUjoKCi0gV2UgYWxyZWFkeSBzYXcgdGhlIHVzZSBvZiBgd3JpdGUuY3N2KClgIHRvIHNhdmUgZGF0YSBmcmFtZXMsCiAgYW5kIHlvdSBjYW4gYWxzbyB1c2Ugb25lIG9mICpyZWFkcioncyBbd3JpdGluZyAgICAgZnVuY3Rpb25zXShodHRwczovL3JlYWRyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3dyaXRlX2RlbGltLmh0bWwpLgogIAotIFRvIHNhdmUgUiBvYmplY3RzICJhcyBpcyIsCiAgd2hpY2ggY2FuIGJlIHVzZWZ1bCB3aGVuIHlvdSdyZSB3b3JraW5nIHdpdGggY29tcGxleCBTNCBvYmplY3RzIHRoYXQgbWF5IGhhdmUKICB0YWtlbiBhIGxvbmcgdGltZSB0byBnZW5lcmF0ZSwgbGlrZSBhICpwaHlsb3NlcSBvYmplY3QqLCB5b3UgY2FuIHVzZToKICAKICBgYGB7ciwgZXZhbD1GQUxTRX0KICAjIFNhdmUgYW4gb2JqZWN0OgogIHNhdmVSRFMobXlfcGh5bG9zZXFfb2JqZWN0LCAibXlfcGh5bG9zZXFfb2JqZWN0LlJEUyIpCiAgCiAgIyBMb2FkIGl0IGFnYWluIGluIGEgbmV3IFIgc2Vzc2lvbjoKICBteV9waHlsb3NlcV9vYmplY3QgPC0gcmVhZFJEUygibXlfcGh5bG9zZXFfb2JqZWN0LlJEUyIpCiAgYGBgCgotIEEgZ2VuZXJhbCByZWNvbW1lbmRhdGlvbiBpcyB0byBub3QgcmVseSBvbiB5b3VyIFIgc2Vzc2lvbiB0byBrZWVwIHRoaW5ncyBhcm91bmQsCiAgZXNwZWNpYWxseSAib3Zlcm5pZ2h0Ii4gRGV2aXNlIHlvdXIgd29ya2Zsb3cgc3VjaCB0aGF0IHlvdSBhcmUgYWx3YXlzIHNhdmluZwogIGltcG9ydGFudCBvYmplY3RzIGFuZCByZXN1bHRzIG91dHNpZGUgb2YgUiwKICBhbmQgY2FuIGFsd2F5cyB1c2UgeW91ciBSIHNjcmlwdCB0byByZXN0YXJ0IGZyb20gd2hlcmUgeW91IGxlZnQgb2ZmLgoKIyMjIFM0IE9iamVjdHMKCldoaWxlIHRoZSBvYmplY3QgdHlwZXMgd2UgaGF2ZSBkaXNjdXNzZWQgc28gZmFyIGFyZSBzby1jYWxsZWQgIipTMyoiIG9iamVjdCwKd2Ugd2lsbCBhbHNvIGJlIHNlZWluZyAiKlM0KiIgb2JqZWN0cyBpbiB0aGlzIHdvcmtzaG9wLiAKUzQgb2JqZWN0IGFyZSBjb21tb25seSB1c2VkIGJ5IGJpb2luZm9ybWF0aWNzIHBhY2thZ2VzLCBmb3IgaW5zdGFuY2UgKnBoeWxvc2VxKi4KCkluIGEgbnV0c2hlbGwsIFM0IG9iamVjdHMgYWxsb3cgZm9yIGNvbXBsaWNhdGVkLCBtdWx0aWZhY2V0ZWQgZGF0YXNldHMKKGUuZy4gbXVsdGlwbGUgZGF0YWZyYW1lcyB3aXRoIGFuZCBtZXRhZGF0YSkgdG8gYmUgcmVwcmVzZW50ZWQgaW4gYSBzaW5nbGUgb2JqZWN0CmluIGEgc3RhbmRhcmRpemVkIHdheS4KClVubGlrZSBTMyBvYmplY3RzLCBTNCBvYmplY3RzIGFyZSB1c3VhbGx5IG5vdCBtYW5pcHVsYXRlZCBieSBzaW1wbGUgYXNzaWdubWVudAp3aXRoIGA8LWAsIGJ1dCB3aXRoIHNwZWNpYWxpemVkIGZ1bmN0aW9ucyB0aGF0IGFyZSBzdXJlIHRvIGFkaGVyZSB0byB0aGUKc3RyaWN0IG9iamVjdCBkZWZpbml0aW9ucy4KCkl0cyBlbGVtZW50cyBhcmUgY2FsbGVkICpzbG90cyosIHdoaWNoIGFyZSBhY2Nlc3NlZCB3aXRoIGBAYCAoYG9iamVjdF9uYW1lQHNsb3RfbmFtZWApLiAKCi0tLS0KCiMjIFdoZXJlIHRvIGdvIGZyb20gaGVyZQoKVGhpcyBkb2N1bWVudCBvbmx5IHNjcmF0Y2hlZCB0aGUgc3VyZmFjZSBvZiBSLApidXQgaXQgaGFzIGhvcGVmdWxseSBwcm92aWRlZCBhIGdvb2Qgc3RhcnRpbmcgcG9pbnQgZm9yIHdvcmtpbmcgd2l0aCBSLgoKTXkgcmVjb21tZW5kYXRpb25zIG9uIHdoZXJlIHRvIGdvIGZyb20gaGVyZSB3b3VsZCBiZSBleGFjdGx5IHRoZSBzYW1lCnJlZ2FyZGxlc3Mgb2Ygd2hldGhlciB5b3UganVzdCB3YW50ZWQgdG8gbWFrZSBnZW5lcmFsIHByb2dyZXNzIHdpdGggUiwKb3Igd2FudGVkIGxlYXJuIFIgdGhpbmdzIHRoYXQgeW91IGNhbiBhcHBseSB3aGVuIHdvcmtpbmcgd2l0aCBtaWNyb2Jpb21pY3MgcGFja2FnZXMuClRoZXkgYXJlOgoKMS4gTGVhcm4gYWJvdXQgZGF0YSB3cmFuZ2xpbmcgd2l0aCB0aWR5dmVyc2UgcGFja2FnZXMsIGVzcGVjaWFsbHkgKmRwbHlyKiBhbmQgKnRpZHlyKi4gU3RhcnQgW2hlcmVdKGh0dHBzOi8vZGF0YWNhcnBlbnRyeS5vcmcvUi1lY29sb2d5LWxlc3Nvbi8wMy1kcGx5ci5odG1sKQoKMi4gTGVhcm4gYWJvdXQgcGxvdHRpbmcgd2l0aCAqZ2dwbG90MiouIFN0YXJ0IFtoZXJlXShodHRwczovL2RhdGFjYXJwZW50cnkub3JnL1ItZWNvbG9neS1sZXNzb24vMDQtdmlzdWFsaXphdGlvbi1nZ3Bsb3QyLmh0bWwpCgpCb3RoIG9mIHRob3NlIHRvcGljcyBhbmQgc29tZSBvdGhlciBtYXRlcmlhbCBhcmUgYWxzbyBjb3ZlcmVkIGluIHRoaXMgZXhjZWxsZW50CkNhcnBlbnRyaWVzIHdvcmtzaG9wIFtSIGZvciBSZXByb2R1Y2libGUgU2NpZW50aWZpYyBBbmFseXNpc10oaHR0cHM6Ly9zd2NhcnBlbnRyeS5naXRodWIuaW8vci1ub3ZpY2UtZ2FwbWluZGVyLykuCgpJZiB5b3Ugd2FudCB0byBzdGFydCB3aXRoIGEgYm9vaywgSSB3b3VsZCByZWNvbW1lbmQgV2lja2hhbSAmIEdyb2xlbXVuZCdzCiJSIGZvciBEYXRhIFNjaWVuY2UiLCB3aGljaCBpcyBmcmVlbHkgYXZhaWxhYmxlIG9uIHRoZSB3ZWIgaW4gYSByZWFsbHkgbmljZSBmb3JtYXQKW2hlcmVdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovKS4KCi0tLS0KCiMjIEJvbnVzIE1hdGVyaWFsOiBTdWJzZXR0aW5nCgojIyMgQmFzaWMgc3Vic2V0dGluZyBvZiBkYXRhIGZyYW1lcyBhbmQgdmVjdG9ycwoKV2UgY2FuIHB1bGwgb3V0IHBhcnRzIG9mIGEgZGF0YSBmcmFtZSB1c2luZyBzcXVhcmUgYnJhY2tldHMuCldlIG5lZWQgdG8gcHJvdmlkZSB0d28gdmFsdWVzOiByb3cgYW5kIGNvbHVtbiwgd2l0aCBhIGNvbW1hIGJldHdlZW4gdGhlbS4KCkZvciBleGFtcGxlLCB0byBnZXQgdGhlIGVsZW1lbnQgaW4gdGhlIDFzdCByb3csIDFzdCBjb2x1bW46CgpgYGB7ciBvbmVfb25lX2VsZW1lbnR9CnN1cnZleXNbMSwgMV0KYGBgCgpUbyBnZXQgdGhlIGVsZW1lbnQgaW4gdGhlIDJuZCByb3csIDd0aCBjb2x1bW46CgpgYGB7ciB0d29fc2V2ZW5fZWxlbWVudH0Kc3VydmV5c1syLCA3XQpgYGAKClRvIGdldCB0aGUgZW50aXJlIDJuZCByb3csIGxlYXZlIHRoZSBjb2x1bW4gcGFydCBibGFuazoKCmBgYHtyIHNlY29uZF9yb3d9CnN1cnZleXNbMiwgXQpgYGAKCkFuZCB0byBnZXQgdGhlIGVudGlyZSA3dGggY29sdW1uLCBsZWF2ZSB0aGUgcm93IHBhcnQgYmxhbms6CgpgYGB7ciBzZXZlbnRoX2NvbHVtbn0Kc2V4IDwtIHN1cnZleXNbLCA3XQpgYGAKCllvdSBjYW4gYWxzbyByZWZlciB0byBjb2x1bW5zIGJ5IG5hbWUsIGluIG11bHRpcGxlIHdheXM6CgpgYGB7ciBncmFiX3NleCwgZXZhbCA9IEZBTFNFfQpzZXggPC0gc3VydmV5cyRzZXggICAgICAgIyBWZXJ5IGNvbW1vbmx5IHVzZWQgc3ludGF4IQoKc2V4IDwtIHN1cnZleXNbLCAic2V4Il0KYGBgCgpXaGVuIHdlIHB1bGwgb3V0IGEgc2luZ2xlIGNvbHVtbiwgdGhlIHJlc3VsdCBpcyBhICp2ZWN0b3IqLgpUbyBwdWxsIG91dCBpbmRpdmlkdWFsIHZhbHVlcyBmcm9tIGEgdmVjdG9yLCB3ZSBjYW4gYWdhaW4gdXNlIHNxdWFyZSBicmFja2V0cywKYnV0IG5vdyB3ZSBvbmx5IHByb3ZpZGUgYSBzaW5nbGUgbnVtYmVyOgoKYGBge3IgZ3JhYl9pbmRpdmlkdWFsX3NleGVzfQpzZXhbMV0Kc2V4WzEwMDAwXQpgYGAKCgojIyMgU2xpY2luZwoKWW91IGNhbiBwdWxsIG91dCBsYXJnZXIgc2xpY2VzIGZyb20gdGhlIHZlY3RvciBieSBwcm92aWRpbmcgdmVjdG9ycyBvZiBpbmRpY2VzOgoKYGBge3IgZ3JhYl9zb21lX3NleGVzfQpzZXhbYygxLDMsNSldCmBgYAoKVGhlIGA6YCBvcGVyYXRvciBnaXZlcyB5b3UgYSBzZXF1ZW5jZSBvZiBjb25zZWN1dGl2ZSB2YWx1ZXMsCndoaWNoIHlvdSBjYW4gYWxzbyB1c2luZyBmb3Igc2xpY2luZzoKCmBgYHtyfQpzdXJ2ZXlzWzE6MywgXSAgICAgICAgIyBGaXJzdCB0aHJlZSByb3dzLCBhbGwgY29sdW1ucwpzdXJ2ZXlzWzEsIDE6M10gICAgICAgIyBGaXJzdCByb3csIGZpcnN0IHRocmVlIGNvbHVtbnMKc3VydmV5c1syOjQsIGMoNiwgOCldICMgUm93cyAyLTQsIGNvbHVtbnMgNiBhbmQgOApgYGAKCiMjIyBDb25kaXRpb25hbCBzdWJzZXR0aW5nCgpBbm90aGVyIGNvbW1vbiB3YXkgb2Ygc3Vic2V0dGluZyBpcyBieSB1c2luZyBhIGxvZ2ljYWwgdmVjdG9yLiBgVFJVRWAgd2lsbApzZWxlY3QgdGhlIGVsZW1lbnQgd2l0aCB0aGUgc2FtZSBpbmRleCwgd2hpbGUgYEZBTFNFYCB3aWxsIG5vdDoKCmBgYHtyLCByZXN1bHRzID0gJ3Nob3cnLCBwdXJsID0gRkFMU0V9CndlaWdodF9nIDwtIGMoMjEsIDM0LCAzOSwgNTQsIDU1KQp3ZWlnaHRfZ1tjKFRSVUUsIEZBTFNFLCBGQUxTRSwgVFJVRSwgVFJVRSldCmBgYAoKVHlwaWNhbGx5LCB0aGVzZSBsb2dpY2FsIHZlY3RvcnMgYXJlIG5vdCB0eXBlZCBieSBoYW5kLCBidXQgYXJlIHRoZSBvdXRwdXQgb2YKb3RoZXIgZnVuY3Rpb25zIG9yIGxvZ2ljYWwgdGVzdHMuIEZvciBpbnN0YW5jZSwgaWYgeW91IHdhbnRlZCB0byBzZWxlY3Qgb25seSB0aGUKdmFsdWVzIGFib3ZlIDUwOgoKYGBge3IsIHJlc3VsdHMgPSAnc2hvdycsIHB1cmwgPSBGQUxTRX0Kd2VpZ2h0X2cgPiA1MCAgICAjIHdpbGwgcmV0dXJuIGxvZ2ljYWxzIHdpdGggVFJVRSBmb3IgdGhlIGluZGljZXMgdGhhdCBtZWV0IHRoZSBjb25kaXRpb24KIyMgc28gd2UgY2FuIHVzZSB0aGlzIHRvIHNlbGVjdCBvbmx5IHRoZSB2YWx1ZXMgYWJvdmUgNTAKd2VpZ2h0X2dbd2VpZ2h0X2cgPiA1MF0KYGBgCgpZb3UgY2FuIGNvbWJpbmUgbXVsdGlwbGUgdGVzdHMgdXNpbmcgYCZgIChib3RoIGNvbmRpdGlvbnMgYXJlIHRydWUsIEFORCkgb3IgYHxgCihhdCBsZWFzdCBvbmUgb2YgdGhlIGNvbmRpdGlvbnMgaXMgdHJ1ZSwgT1IpOgoKYGBge3IsIHJlc3VsdHMgPSAnc2hvdycsIHB1cmwgPSBGQUxTRX0Kd2VpZ2h0X2dbd2VpZ2h0X2cgPiAzMCAmIHdlaWdodF9nIDwgNTBdCndlaWdodF9nW3dlaWdodF9nIDw9IDMwIHwgd2VpZ2h0X2cgPT0gNTVdCndlaWdodF9nW3dlaWdodF9nID49IDMwICYgd2VpZ2h0X2cgPT0gMjFdCmBgYAoKSGVyZSwgYD5gIGZvciAiZ3JlYXRlciB0aGFuIiwgYDxgIHN0YW5kcyBmb3IgImxlc3MgdGhhbiIsIGA8PWAgZm9yICJsZXNzIHRoYW4Kb3IgZXF1YWwgdG8iLCBhbmQgYD09YCBmb3IgImVxdWFsIHRvIi4gVGhlIGRvdWJsZSBlcXVhbCBzaWduIGA9PWAgaXMgYSB0ZXN0IGZvcgpudW1lcmljYWwgZXF1YWxpdHkgYmV0d2VlbiB0aGUgbGVmdCBhbmQgcmlnaHQgaGFuZCBzaWRlcywgYW5kIHNob3VsZCBub3QgYmUKY29uZnVzZWQgd2l0aCB0aGUgc2luZ2xlIGA9YCBzaWduLCB3aGljaCBwZXJmb3JtcyB2YXJpYWJsZSBhc3NpZ25tZW50IChzaW1pbGFyCnRvIGA8LWApLgoKQSBjb21tb24gdGFzayBpcyB0byBzZWFyY2ggZm9yIGNlcnRhaW4gc3RyaW5ncyBpbiBhIHZlY3Rvci4gIE9uZSBjb3VsZCB1c2UgdGhlCiJvciIgb3BlcmF0b3IgYHxgIHRvIHRlc3QgZm9yIGVxdWFsaXR5IHRvIG11bHRpcGxlIHZhbHVlcywgYnV0IHRoaXMgY2FuIHF1aWNrbHkKYmVjb21lIHRlZGlvdXMuIFRoZSBmdW5jdGlvbiBgJWluJWAgYWxsb3dzIHlvdSB0byB0ZXN0IGlmIGFueSBvZiB0aGUgZWxlbWVudHMgb2YKYSBzZWFyY2ggdmVjdG9yIGFyZSBmb3VuZDoKCmBgYHtyLCByZXN1bHRzID0gJ3Nob3cnLCBwdXJsID0gRkFMU0V9CmFuaW1hbHMgPC0gYygibW91c2UiLCAicmF0IiwgImRvZyIsICJjYXQiLCAiY2F0IikKCiMgcmV0dXJuIGJvdGggcmF0IGFuZCBjYXQKYW5pbWFsc1thbmltYWxzID09ICJjYXQiIHwgYW5pbWFscyA9PSAicmF0Il0gCmFuaW1hbHNbYW5pbWFscyAlaW4lIGMoImNhdCIsICJyYXQiKV0gCmBgYAoKCjxicj4gPGJyPgoK