Prerequisites
Before you get started, make sure you have all tutorial materials downloaded and R and RStudio installed on your machine. If you don’t have these installed on your machine, the following are instructions for you to install this software.
Installing R
R is a powerful, open-source statistical programming language that anyone can download for free! You can simply go to CRAN’s website and install R by following these steps:
Under the ‘Download’ heading, select ‘CRAN’
Install a CRAN Mirror
Install the CRAN mirror that’s nearest to your geographic location. For example, if you live in Orange County, California, you should install the UCLA CRAN Mirror.
Install your Machine’s Version of R
Installing RStudio
RStudio is an open-source professional software that makes R much easier to use. Download the free, open-source license version from RStudio’s website. The installation steps are very similar to those of R’s for all operating systems.
Updating R and RStudio
If the aforementioned packages and functions start to not work after an extended period of time, you may need to update your versions of R, R packages, and RStudio software to the latest versions.
Updating R
To update your version of R, first close any R or RStudio windows you have open.
Updating R on Windows
Open the R GUI (x64, not i386). This is not the same as RStudio. The R GUI program icon should look very similar to this:
Install the installr
package into R by typing
install.packages("installr")
into the Console.
After the package is finished installing, call the package by typing
library(installr)
into the Console. You can now update your R software and packages to the latest versions by typing
updateR()
into the Console. Then R will walk you through a detailed and intuitive process of updating your R software and packages to the latest versions.
Updating R on (Mac) OS X
Open RStudio again, and type the following lines of code into the Console:
install.packages('devtools') #assuming it isn't already installed
library(devtools)
install_github('andreacirilloac/updateR')
library(updateR)
updateR(admin_password = "os_admin_user_password")
R will then walk you through a detailed and intuitive process of updating your R software and packages to the latest versions.
Updating R on Linux
This resource will walk you through how to update your R software and packages to the latest versions on Linux.
Updating R Packages Without Updating R
Updating out-of-date packages that were installed from CRAN (with install.packages()
) is easy with the update.packages()
function. Type this function into the RStudio Console.
update.packages()
After entering this function, it will ask you what packages you want to update. To update all packages at once, use ask = FALSE
.
update.packages(ask = FALSE)
To update packages installed from devtools::install_github()
, type the following function into your RStudio Console (I would also recommend saving this function in an R Script for later use):
update_github_pkgs <- function() {
# check/load necessary packages
# devtools package
if (!("package:devtools" %in% search())) {
tryCatch(require(devtools), error = function(x) {warning(x); cat("Cannot load devtools package \n")})
on.exit(detach("package:devtools", unload=TRUE))
}
pkgs <- installed.packages(fields = "RemoteType")
github_pkgs <- pkgs[pkgs[, "RemoteType"] %in% "github", "Package"]
print(github_pkgs)
lapply(github_pkgs, function(pac) {
message("Updating ", pac, " from GitHub...")
repo = packageDescription(pac, fields = "GithubRepo")
username = packageDescription(pac, fields = "GithubUsername")
install_github(repo = paste0(username, "/", repo))
})
}
Then call the function.
update_github_pkgs()
Updating RStudio
To update RStudio, open RStudio and go to Help > Check for Updates
to install the newest version.
Data Types
R has a wide variety of data types including vectors (numerical, character, logical), matrices, lists, and data frames. Matrices are composed of vectors, and data frames are composed of lists.
Vectors
a <- c(1,2,5.3,6,-2,4) # numeric vector
b <- c("one","two","three") # character vector
c <- c(TRUE,TRUE,TRUE,FALSE,TRUE,FALSE) #logical vector
You can refer to elements of a vector using subscripts.
a[c(2,4)] # 2nd and 4th elements of vector
## [1] 2 6
Matrices
All columns in a matrix must have the same mode (numeric, character, etc.) and the same length. The general format is
mymatrix <- matrix(vector, nrow=r, ncol=c, byrow=FALSE,
dimnames=list(char_vector_rownames, char_vector_colnames))
byrow=TRUE
indicates that the matrix should be filled by rows. byrow=FALSE
indicates that the matrix should be filled by columns (the default). dimnames provides optional labels for the columns and rows.
# generates 5 x 4 numeric matrix
x <- matrix(1:20, nrow=5,ncol=4)
# another example
cells <- c(1,26,24,68)
rnames <- c("R1", "R2")
cnames <- c("C1", "C2")
mymatrix <- matrix(cells, nrow=2, ncol=2, byrow=TRUE,
dimnames=list(rnames, cnames))
Like in vectors, you can identify rows, columns or elements using subscripts.
x[,4] # 4th column of matrix
## [1] 16 17 18 19 20
x[3,] # 3rd row of matrix
## [1] 3 8 13 18
x[2:4,1:3] # rows 2,3,4 of columns 1,2,3
## [,1] [,2] [,3]
## [1,] 2 7 12
## [2,] 3 8 13
## [3,] 4 9 14
Lists
An ordered collection of objects (components). A list allows you to gather a variety of (possibly unrelated) objects under one name.
# example of a list with 4 components -
# a string, a numeric vector, a matrix, and a scalar
list1 <- list(name="Fred", mynumbers=a, mymatrix=x, age=5.3)
list2 <- list(character="Louise", show="Bob's Burgers", time=830)
# example of a list containing two lists
ultimate_list <- c(list1,list2)
Identify elements of a list using the [[]] convention.
ultimate_list[[2]] # 2nd component of the list
## [1] 1.0 2.0 5.3 6.0 -2.0 4.0
ultimate_list[["mynumbers"]] # component named mynumbers in list
## [1] 1.0 2.0 5.3 6.0 -2.0 4.0
Data Frames
A data frame is more general than a matrix, in that different columns can have different modes (numeric, character, factor, etc.). This is similar to SAS and SPSS datasets.
d <- c(1,2,3,4)
e <- c("red", "white", "red", NA)
f <- c(TRUE,TRUE,TRUE,FALSE)
mydata <- data.frame(d,e,f)
names(mydata) <- c("ID","Color","Passed") # variable names
There are a variety of ways to identify the elements of a data frame.
mydata[2:3] # columns 2,3 of data frame
mydata[c("ID","Passed")] # columns ID and Passed from data frame
mydata$Color # variable Color in the data frame
## [1] red white red <NA>
## Levels: red white
Useful Functions for All Data Types
length(object) # number of elements or components
str(object) # structure of an object
class(object) # class or type of an object
names(object) # names
c(object,object,...) # combine objects into a vector
cbind(object, object, ...) # combine objects as columns
rbind(object, object, ...) # combine objects as rows
object # prints the object
ls() # list current objects
rm(object) # delete an object
newobject <- edit(object) # edit copy and save as newobject
fix(object) # edit in place
Importing and Working with Data
Reading in the Dataset
Today, we’re going to be using one of the over 40,000 open datasets available in NASA’s Open Data Repository. This “Meteorite Landings” dataset is from the Meteoritical Soceity and contains information on all of the known meteorite landings, going as far back as the early 1800’s.
This data can be downloaded at this link.
To read in our data (.csv) file, we can use the following command:
meteorite_landings <- read.csv("data/Meteorite_Landings.csv")
Note 1: The syntax above assumes the .csv file is saved to the working directory. To change the working directory, navigate to Session
> Set Working Directory
> Choose Working Directory
or type setwd("C:/Users/path")
into the Console.
Note 2: read.csv
reads the file, but we can’t use the data unless we assign it to a variable. We can think of a variable as a container with a name, such as x, current_temperature, or subject_id that contains one or more values. We can create a new variable in R and assign a value to it using <-
.
Once you run the above command, you should see a meteorite_landings
object in the “Environment” box in the top right corner of RStudio.
Now, let’s explore the dataset.
Viewing the Dataset
If we want to view our entire dataset, we can type View(meteorite_landings)
into the Console. However, for large data sets it is much faster and more convenient to use the function head
to display only the first few rows of data.
head(meteorite_landings)
Structure
To view the structure of, or data types for, each variable in a dataset, use the str
, or “structure” function.
str(meteorite_landings)
## 'data.frame': 45716 obs. of 10 variables:
## $ name : Factor w/ 45716 levels "Österplana 002",..: 68 69 73 77 473 484 496 497 502 521 ...
## $ id : num 1 2 6 10 370 379 390 392 398 417 ...
## $ nametype : Factor w/ 2 levels "Relict","Valid": 2 2 2 2 2 2 2 2 2 2 ...
## $ recclass : Factor w/ 466 levels "Acapulcoite",..: 333 197 85 1 339 85 360 190 339 242 ...
## $ mass : num 21 720 107000 1914 780 ...
## $ fall : Factor w/ 2 levels "Fell","Found": 1 1 1 1 1 1 1 1 1 1 ...
## $ year : Factor w/ 270 levels "","01/01/1583 12:00:00 AM",..: 124 197 198 223 148 165 195 59 176 166 ...
## $ reclat : num 50.8 56.2 54.2 16.9 -33.2 ...
## $ reclong : num 6.08 10.23 -113 -99.9 -64.95 ...
## $ GeoLocation: Factor w/ 17101 levels "","(-1.002780, 37.150280)",..: 16779 16983 16923 9106 844 14808 16496 16453 784 721 ...
Data Types
Note that meteorite_landings
is a data frame. You can think of this structure as a spreadsheet in MS Excel. Data frames are very useful for storing data, and you will use them frequently when programming in R. A typical data frame of experimental data contains individual observations in rows and variables in columns. Also note that there are 2 different data types in our dataset:
Factor - A Class (not a string or number!)
Num - Numeric, float/number than can include decimal places
Dimensions
We can see the shape, or dimensions, of the data frame with the function dim
:
dim(meteorite_landings)
## [1] 45716 10
This tells us that our data frame, meteorite_landings
, has 45,716 rows and 10 columns.
Indexing
If we want to get a single value from the data frame, we can provide an index in square brackets. The first number specifies the row and the second the column:
# first value in meteorite_landings, row 1, column 1
meteorite_landings[1, 1]
## [1] Aachen
## 45716 Levels: Österplana 002 Österplana 003 ... Zvonkov
# middle value in meteorite_landings, row 22858, column 5
meteorite_landings[22858, 5]
## [1] 4.7
Subsetting
If we want to select more than one row or column, we can use the function c
, which stands for combine. For example, to pick columns 1 and 5 from rows 1, 3, and 5, we can do this:
meteorite_landings[c(1, 3, 5), c(1, 5)]
We frequently want to select contiguous rows or columns, such as the first ten rows, or columns 3 through 7. You can use c
for this, but it’s more convenient to use the :
operator. This special function generates sequences of numbers:
1:5
## [1] 1 2 3 4 5
3:12
## [1] 3 4 5 6 7 8 9 10 11 12
For example, we can select the first 2 columns of values for the first four rows like this:
meteorite_landings[1:4, 1:2]
or the first 5 columns of rows 5 to 10 like this:
meteorite_landings[5:10, 1:5]
If you want to select all rows or all columns, leave that index value empty.
# All columns from row 5
meteorite_landings[5, ]
# All rows from columns 6-8
meteorite_landings[, 6:8]
If you leave both index values empty (i.e., meteorite_landings[,]
), you get the entire data frame.
Mathematical Operations
Now let’s perform some common mathematical operations to learn more about our meteorite data. When analyzing data we often want to look at partial statistics, such as the maximum value per id or the average value per year. One way to do this is to select the data we want to create a new temporary data frame, and then perform the calculation on this subset:
# first 5 rows, columns 1,5
first_fifty_meteors <- meteorite_landings[1:50, 5]
# max mass of first 50 meteors
max(first_fifty_meteors, na.rm = T)
## [1] 2e+06
# also correct:
max(meteorite_landings[1:50, 5], na.rm = T)
## [1] 2e+06
R also has functions for other common calculations, e.g. finding the minimum, mean, median, and standard deviation of the data:
# minimum mass of first 50 meteors
min(first_fifty_meteors, na.rm = T)
## [1] 21
# mean mass of first 50 meteors
mean(first_fifty_meteors, na.rm = T)
## [1] 54289.93
# median mass of first 50 meteors
median(first_fifty_meteors, na.rm = T)
## [1] 2600
# standard deviation of the mass of the first 50 meteors
sd(first_fifty_meteors, na.rm = T)
## [1] 289093.5
R also has a function that summaries the previous common calculations. For every column in the data frame, the function summary
calculates: the minimum value, the first quartile, the median, the mean, the third quartile, and the max value, giving helpful details about the sample distribution.
# Summarize function
summary(meteorite_landings)
## name id nametype recclass
## Österplana 002: 1 Min. : 1 Relict: 75 L6 : 8285
## Österplana 003: 1 1st Qu.:12689 Valid :45641 H5 : 7142
## Österplana 004: 1 Median :24262 L5 : 4796
## Österplana 005: 1 Mean :26890 H6 : 4528
## Österplana 006: 1 3rd Qu.:40657 H4 : 4211
## Österplana 007: 1 Max. :57458 LL5 : 2766
## (Other) :45710 (Other):13988
## mass fall year reclat
## Min. : 0 Fell : 1107 1/1/2003 0:00: 3323 Min. :-87.37
## 1st Qu.: 7 Found:44609 1/1/1979 0:00: 3046 1st Qu.:-76.71
## Median : 33 1/1/1998 0:00: 2697 Median :-71.50
## Mean : 13278 1/1/2006 0:00: 2456 Mean :-39.12
## 3rd Qu.: 203 1/1/1988 0:00: 2296 3rd Qu.: 0.00
## Max. :60000000 1/1/2002 0:00: 2078 Max. : 81.17
## NA's :131 (Other) :29820 NA's :7315
## reclong GeoLocation
## Min. :-165.43 : 7315
## 1st Qu.: 0.00 (0.000000, 0.000000) : 6214
## Median : 35.67 (-71.500000, 35.666670) : 4761
## Mean : 61.07 (-84.000000, 168.000000): 3040
## 3rd Qu.: 157.17 (-72.000000, 26.000000) : 1505
## Max. : 354.47 (-79.683330, 159.750000): 657
## NA's :7315 (Other) :22224
Manipulating and sorting dataframes with dplyr
What is dplyr
?
dplyr
is an R package in the tidyverse
that aims to make data wrangling/cleaning/manipulation more human-readable. The dplyr
package makes these steps fast and easy:
By constraining your options, it helps you think about your data manipulation challenges.
It provides simple “verbs”, functions that correspond to the most common data manipulation tasks, to help you translate your thoughts into code.
It uses efficient backends, so you spend less time waiting for the computer.
This tutorial introduces you to dplyr
’s basic set of tools, and shows you how to apply them to data frames. Aside from this tutorial, a helpful resource for getting familiar with dplyr
is this comprehensive RStudio cheatsheet.
Side Note: dplyr
also supports databases via the dbplyr
package. Once you install dbplyr
, you can read vignette("dbplyr")
to learn more.
Getting Started with dplyr
If you don’t already have dplyr
or the tidyverse
installed, you can install the dplyr
package by typing the following command into the Console:
install.packages("dplyr")
Once you have dplyr
installed, load it into your R session with
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
dplyr
aims to provide a function for each basic verb of data manipulation:
filter() |
to select cases based on their values |
arrange() |
to reorder the cases |
select() , rename() |
to select variables based on their names |
mutate() , transmute() |
to add new variables that are functions of existing variables |
summarise() |
to condense multiple values to a single value |
sample_n() , sample_frac() |
to take random samples |
In this tutorial, we’ll spend some time working with each individual function.
Piping Operator (%>%
)
The operators %>%
pipe their left-hand side values forward into expressions that appear on the right-hand side, i.e. one can replace f(x) with x %>%
f(), where %>%
is the (main) pipe-operator. When coupling several function calls with the pipe-operator, the benefit will become more apparent. Consider this pseudo example:
the_data <- read.csv('/path/to/data/file.csv') %>%
subset(variable_a > x) %>%
transform(variable_c = variable_a/variable_b) %>%
head(100)
Four operations are performed to arrive at the desired data set, and they are written in a natural order: the same as the order of execution. Also, no temporary variables are needed. If yet another operation is required, it is straight-forward to add to the sequence of operations wherever it may be needed.
Filter rows with filter()
filter()
allows you to select a subset of rows in a data frame. Like all single verbs, the first argument is the data frame. The second and subsequent arguments refer to variables within that data frame, selecting rows where the expression is TRUE
.
For example, we can select all meteorites in the L5 class with mass greater than or equal to 10,000 grams with:
meteorite_landings %>%
filter(mass >= 10000 & recclass == "L5")
This is roughly equivalent to this base R code:
meteorite_landings[meteorite_landings$mass >= 10000 & meteorite_landings$recclass == "L5", ]
Arrange rows with arrange()
arrange()
works similarly to filter()
except that instead of filtering or selecting rows, it reorders them. It takes a data frame and a set of column names (or more complicated expressions) to order by. If you provide more than one column name, each additional column will be used to break ties in the values of preceding columns:
meteorite_landings %>%
arrange(reclat, reclong, mass)
Use desc()
to order a column in descending order:
meteorite_landings %>%
arrange(desc(mass))
Select columns with select()
Often you work with large datasets with many columns but only a few are actually of interest to you. select()
allows you to rapidly zoom in on a useful subset using operations that usually only work on numeric variable positions:
# Select columns by name
meteorite_landings %>%
select(name, recclass, mass)
# Select all columns between name and year (inclusive)
meteorite_landings %>%
select(name:year)
# Select all columns except those from name to year (inclusive)
meteorite_landings %>%
select(-(name:year))
There are a number of helper functions you can use within select()
, like starts_with()
, ends_with()
, matches()
and contains()
. These let you quickly match larger blocks of variables that meet some criterion. See ?select
for more details.
You can rename variables with select()
by using named arguments:
meteorite_landings %>%
select(mass_g = mass)
But because select()
drops all the variables not explicitly mentioned, it’s not that useful. Instead, use rename()
:
meteorite_landings %>%
rename(mass_g = mass)
Add new columns with mutate()
Besides selecting sets of existing columns, it’s often useful to add new columns that are functions of existing columns. This is the job of mutate()
:
meteorite_landings %>%
mutate(mass_kg = mass/1000)
dplyr::mutate()
is similar to the base transform()
, but allows you to refer to columns that you’ve just created.
If you only want to keep the new variables, use transmute()
:
meteorite_landings %>%
transmute(mass_kg = mass/1000)
Summarise values with summarise()
The last verb is summarise()
. It collapses a data frame to a single row.
meteorite_landings %>%
summarise(mean_mass = mean(mass, na.rm = T))
It’s not that useful until we learn the group_by()
verb below.
meteorite_landings %>%
group_by(recclass) %>%
summarise(mean_mass = mean(mass, na.rm = T))
Randomly sample rows with sample_n()
and sample_frac()
You can use sample_n()
and sample_frac()
to take a random sample of rows: use sample_n()
for a fixed number and sample_frac()
for a fixed fraction.
meteorite_landings %>%
sample_n(10)
meteorite_landings %>%
sample_frac(0.01)
Use replace = TRUE
to perform a bootstrap sample. If needed, you can weight the sample with the weight
argument.
Ten dplyr
Tricks!
Are you often selecting the same columns over and over again?
You can make a vector of pre-identified columns once and then refer to them using one_of()
or !!
(even shorter).
library(dplyr)
cols <- c("name", "reclat", "reclong")
ex1 <- meteorite_landings %>%
select(!!cols)
head(ex1)
Select columns via regex
If you have matching patterns, you can use starts_with()
, contains()
, or ends_with
. But what if your pattern isn’t that exact? Simple: enter regex into matches()
.
library(dplyr)
ex2 <- iris %>%
select(matches("S.+th"))
head(ex2)
Reordering your columns
If you just want to bring one or more columns to the front, you can use everything()
to add all the remaining columns.
library(dplyr)
ex3 <- meteorite_landings %>%
select(id, everything())
head(ex3)
Renaming all variables in one go
One command to get them all in lower case, and one more to replace “..g.” in the mass
variable.
library(dplyr)
library(stringr)
ex4 <- meteorite_landings %>%
rename_all(tolower) %>%
rename_all(~str_replace(., "..g.", ""))
head(ex4)
Cleaning up your observations in one go
The select_all/if/at
and rename_all/if/at
functions will only modify the variable names, not the observations. If you want to change those, use the mutate
variant.
library(dplyr)
library(stringr)
ex5 <- meteorite_landings %>%
select(name, nametype, fall) %>%
mutate_all(tolower) %>%
mutate_all(~str_replace_all(., " ", "_"))
head(ex5)
Finding the 5 highest/lowest values
You can use top_n
to find the 5 meteorites with the highest mass without ordering them first.
library(dplyr)
meteorite_landings %>%
top_n(5, mass)
Adding the amount of observations
You can add the amount of observations without summarising them yourself. If you don’t like the default column name n
, you can change it again with a rename()
statement.
library(dplyr)
ex7 <- meteorite_landings %>%
add_count(recclass) %>%
rename(n_recclass = n)
head(ex7)
Making new discrete variables
case_when()
can be a very powerful tool to make new discrete variables based on other columns.
library(dplyr)
ex8 <- starwars %>%
select(name, species, homeworld, birth_year, hair_color) %>%
mutate(new_group = case_when(
species == "Droid" ~ "Robot",
homeworld == "Tatooine" & hair_color == "blond" ~ "Blond Tatooinian",
homeworld == "Tatooine" ~ "Other Tatooinian",
hair_color == "blond" ~ "Blond non-Tatooinian",
TRUE ~ "Other Human"))
head(ex8)
Going rowwise…
Mutating with aggregate functions by default will take the average/sum/… of the entire column. Via adding rowwise()
you can aggregate within an observation.
library(dplyr)
ex9 <- iris %>%
select(contains("Length")) %>%
rowwise() %>%
mutate(avg_length = mean(c(Petal.Length, Sepal.Length)))
head(ex9)
Changing your column names after summarise_if
If you’ve used the summarise_all/if/at
variants before, you know that the variable name by default does not get changed. If you want a modified name, you can wrap your function inside funs()
and add a tag that will be added to the variable name.
library(dplyr)
iris %>%
summarise_if(is.numeric, funs(avg = mean))
LS0tDQp0aXRsZTogIjxicj5EYXRhIE1hbmlwdWxhdGlvbiB3aXRoIFRpZHkgVG9vbHMiDQphdXRob3I6ICJBbHlzc2EgQ29sdW1idXMiDQpkYXRlOiAnTWF5IDE4LCAyMDE5Jw0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDoNCiAgICAgIHRvY19jb2xsYXBzZWQ6IGZhbHNlDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIGZpZ19jYXB0aW9uOiB0cnVlDQogICAgY3NzOiBzdHlsZXMuY3NzDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KIyBQcmVyZXF1aXNpdGVzDQpCZWZvcmUgeW91IGdldCBzdGFydGVkLCBtYWtlIHN1cmUgeW91IGhhdmUgW2FsbCB0dXRvcmlhbCBtYXRlcmlhbHNdKGh0dHBzOi8vZ2l0aHViLmNvbS9hY29sdW0vY29uZmVyZW5jZS1wcmVzZW50YXRpb25zL3RyZWUvbWFzdGVyL0RhdGElMjBNYW5pcHVsYXRpb24lMjB3aXRoJTIwVGlkeSUyMFRvb2xzKXt0YXJnZXQ9Il9ibGFuayJ9IGRvd25sb2FkZWQgYW5kIFtSXShodHRwczovL3d3dy5yLXByb2plY3Qub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSBhbmQgW1JTdHVkaW9dKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tLyl7dGFyZ2V0PSJfYmxhbmsifSBpbnN0YWxsZWQgb24geW91ciBtYWNoaW5lLiBJZiB5b3UgZG9uJ3QgaGF2ZSB0aGVzZSBpbnN0YWxsZWQgb24geW91ciBtYWNoaW5lLCB0aGUgZm9sbG93aW5nIGFyZSBpbnN0cnVjdGlvbnMgZm9yIHlvdSB0byBpbnN0YWxsIHRoaXMgc29mdHdhcmUuDQoNCiMjIEluc3RhbGxpbmcgUg0KUiBpcyBhIHBvd2VyZnVsLCBvcGVuLXNvdXJjZSBzdGF0aXN0aWNhbCBwcm9ncmFtbWluZyBsYW5ndWFnZSB0aGF0IGFueW9uZSBjYW4gZG93bmxvYWQgZm9yIGZyZWUhIFlvdSBjYW4gc2ltcGx5IGdvIHRvIFtDUkFOJ3Mgd2Vic2l0ZV0oaHR0cHM6Ly93d3cuci1wcm9qZWN0Lm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gYW5kIGluc3RhbGwgUiBieSBmb2xsb3dpbmcgdGhlc2Ugc3RlcHM6DQoNCiMjIyBVbmRlciB0aGUgJ0Rvd25sb2FkJyBoZWFkaW5nLCBzZWxlY3QgJ0NSQU4nDQohW10oaW1nL0NSQU4ucG5nKQ0KDQojIyMgSW5zdGFsbCBhIENSQU4gTWlycm9yDQoNCkluc3RhbGwgdGhlIENSQU4gbWlycm9yIHRoYXQncyBuZWFyZXN0IHRvIHlvdXIgZ2VvZ3JhcGhpYyBsb2NhdGlvbi4gRm9yIGV4YW1wbGUsIGlmIHlvdSBsaXZlIGluIE9yYW5nZSBDb3VudHksIENhbGlmb3JuaWEsIHlvdSBzaG91bGQgaW5zdGFsbCB0aGUgW1VDTEEgQ1JBTiBNaXJyb3IuXShodHRwOi8vY3Jhbi5zdGF0LnVjbGEuZWR1Lyl7dGFyZ2V0PSJfYmxhbmsifQ0KDQojIyMgSW5zdGFsbCB5b3VyIE1hY2hpbmUncyBWZXJzaW9uIG9mIFINCiFbXShpbWcvRG93bmxvYWRSLnBuZykNCg0KIyMjIyBSIGZvciBXaW5kb3dzDQohW0luc3RhbGwgUiBmb3IgdGhlIGZpcnN0IHRpbWUuXShpbWcvV2luZG93c1IxLnBuZykNCg0KIVtBZnRlciB5b3UgY2xpY2sgdGhpcyBsaW5rLCBmb2xsb3cgdGhlIGluc3RydWN0aW9ucyBnaXZlbiBpbiB0aGUgaW5zdGFsbGF0aW9uLl0oaW1nL1dpbmRvd3NSMi5wbmcpDQoNCiMjIyMgUiBmb3IgKE1hYykgT1MgWA0KIVtBZnRlciB5b3UgY2xpY2sgdGhpcyBsaW5rLCBmb2xsb3cgdGhlIGluc3RydWN0aW9ucyBnaXZlbiBpbiB0aGUgaW5zdGFsbGF0aW9uLl0oaW1nL01hY1IxLnBuZykNCg0KIyMjIyBSIGZvciBMaW51eA0KIVtJbnN0YWxsIHRoZSBSIHZlcnNpb24gcmVsZXZhbnQgdG8geW91ciBMaW51eCBzZXJ2ZXIsIGFuZCBmb2xsb3cgdGhlIGluc3RydWN0aW9ucyBnaXZlbiBpbiB0aGUgaW5zdGFsbGF0aW9uLl0oaW1nL0xpbnV4UjEucG5nKQ0KDQojIyBJbnN0YWxsaW5nIFJTdHVkaW8NClJTdHVkaW8gaXMgYW4gb3Blbi1zb3VyY2UgcHJvZmVzc2lvbmFsIHNvZnR3YXJlIHRoYXQgbWFrZXMgUiBtdWNoIGVhc2llciB0byB1c2UuIERvd25sb2FkIHRoZSBmcmVlLCBvcGVuLXNvdXJjZSBsaWNlbnNlIHZlcnNpb24gZnJvbSBbUlN0dWRpbydzIHdlYnNpdGVdKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3Byb2R1Y3RzL3JzdHVkaW8vZG93bmxvYWQvI2Rvd25sb2FkKXt0YXJnZXQ9Il9ibGFuayJ9LiBUaGUgaW5zdGFsbGF0aW9uIHN0ZXBzIGFyZSB2ZXJ5IHNpbWlsYXIgdG8gdGhvc2Ugb2YgUidzIGZvciBhbGwgb3BlcmF0aW5nIHN5c3RlbXMuDQoNCiMjIFVwZGF0aW5nIFIgYW5kIFJTdHVkaW8NCklmIHRoZSBhZm9yZW1lbnRpb25lZCBwYWNrYWdlcyBhbmQgZnVuY3Rpb25zIHN0YXJ0IHRvIG5vdCB3b3JrIGFmdGVyIGFuIGV4dGVuZGVkIHBlcmlvZCBvZiB0aW1lLCB5b3UgbWF5IG5lZWQgdG8gdXBkYXRlIHlvdXIgdmVyc2lvbnMgb2YgUiwgUiBwYWNrYWdlcywgYW5kIFJTdHVkaW8gc29mdHdhcmUgdG8gdGhlIGxhdGVzdCB2ZXJzaW9ucy4NCg0KIyMjIFVwZGF0aW5nIFINClRvIHVwZGF0ZSB5b3VyIHZlcnNpb24gb2YgUiwgZmlyc3QgY2xvc2UgYW55IFIgb3IgUlN0dWRpbyB3aW5kb3dzIHlvdSBoYXZlIG9wZW4uDQoNCiMjIyMgVXBkYXRpbmcgUiBvbiBXaW5kb3dzDQpPcGVuIHRoZSBSIEdVSSAoeDY0LCBub3QgaTM4NikuIFRoaXMgaXMgKipub3QqKiB0aGUgc2FtZSBhcyBSU3R1ZGlvLiBUaGUgUiBHVUkgcHJvZ3JhbSBpY29uIHNob3VsZCBsb29rIHZlcnkgc2ltaWxhciB0byB0aGlzOg0KDQohW10oaW1nL1JHdWkucG5nKQ0KDQoNCkluc3RhbGwgdGhlIGBpbnN0YWxscmAgcGFja2FnZSBpbnRvIFIgYnkgdHlwaW5nDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KaW5zdGFsbC5wYWNrYWdlcygiaW5zdGFsbHIiKQ0KYGBgICANCg0KaW50byB0aGUgQ29uc29sZS4NCg0KQWZ0ZXIgdGhlIHBhY2thZ2UgaXMgZmluaXNoZWQgaW5zdGFsbGluZywgY2FsbCB0aGUgcGFja2FnZSBieSB0eXBpbmcgDQoNCmBgYHtyLCBldmFsPUZBTFNFfSANCmxpYnJhcnkoaW5zdGFsbHIpDQpgYGAgDQoNCmludG8gdGhlIENvbnNvbGUuIFlvdSBjYW4gbm93IHVwZGF0ZSB5b3VyIFIgc29mdHdhcmUgYW5kIHBhY2thZ2VzIHRvIHRoZSBsYXRlc3QgdmVyc2lvbnMgYnkgdHlwaW5nIA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCnVwZGF0ZVIoKSANCmBgYA0KDQppbnRvIHRoZSBDb25zb2xlLiBUaGVuIFIgd2lsbCB3YWxrIHlvdSB0aHJvdWdoIGEgZGV0YWlsZWQgYW5kIGludHVpdGl2ZSBwcm9jZXNzIG9mIHVwZGF0aW5nIHlvdXIgUiBzb2Z0d2FyZSBhbmQgcGFja2FnZXMgdG8gdGhlIGxhdGVzdCB2ZXJzaW9ucy4NCg0KIyMjIyBVcGRhdGluZyBSIG9uIChNYWMpIE9TIFgNCg0KT3BlbiBSU3R1ZGlvIGFnYWluLCBhbmQgdHlwZSB0aGUgZm9sbG93aW5nIGxpbmVzIG9mIGNvZGUgaW50byB0aGUgQ29uc29sZToNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQppbnN0YWxsLnBhY2thZ2VzKCdkZXZ0b29scycpICNhc3N1bWluZyBpdCBpc24ndCBhbHJlYWR5IGluc3RhbGxlZA0KbGlicmFyeShkZXZ0b29scykNCmluc3RhbGxfZ2l0aHViKCdhbmRyZWFjaXJpbGxvYWMvdXBkYXRlUicpDQpsaWJyYXJ5KHVwZGF0ZVIpDQp1cGRhdGVSKGFkbWluX3Bhc3N3b3JkID0gIm9zX2FkbWluX3VzZXJfcGFzc3dvcmQiKQ0KYGBgDQoNClIgd2lsbCB0aGVuIHdhbGsgeW91IHRocm91Z2ggYSBkZXRhaWxlZCBhbmQgaW50dWl0aXZlIHByb2Nlc3Mgb2YgdXBkYXRpbmcgeW91ciBSIHNvZnR3YXJlIGFuZCBwYWNrYWdlcyB0byB0aGUgbGF0ZXN0IHZlcnNpb25zLg0KDQojIyMjIFVwZGF0aW5nIFIgb24gTGludXgNCg0KW1RoaXMgcmVzb3VyY2VdKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzEwNDc2NzEzL2hvdy10by11cGdyYWRlLXItaW4tdWJ1bnR1KSB3aWxsIHdhbGsgeW91IHRocm91Z2ggaG93IHRvIHVwZGF0ZSB5b3VyIFIgc29mdHdhcmUgYW5kIHBhY2thZ2VzIHRvIHRoZSBsYXRlc3QgdmVyc2lvbnMgb24gTGludXguDQoNCiMjIyBVcGRhdGluZyBSIFBhY2thZ2VzIFdpdGhvdXQgVXBkYXRpbmcgUg0KDQpVcGRhdGluZyBvdXQtb2YtZGF0ZSBwYWNrYWdlcyB0aGF0IHdlcmUgaW5zdGFsbGVkIGZyb20gQ1JBTiAod2l0aCBgaW5zdGFsbC5wYWNrYWdlcygpYCkgaXMgZWFzeSB3aXRoIHRoZSBgdXBkYXRlLnBhY2thZ2VzKClgIGZ1bmN0aW9uLiBUeXBlIHRoaXMgZnVuY3Rpb24gaW50byB0aGUgUlN0dWRpbyBDb25zb2xlLg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCnVwZGF0ZS5wYWNrYWdlcygpDQpgYGANCg0KQWZ0ZXIgZW50ZXJpbmcgdGhpcyBmdW5jdGlvbiwgaXQgd2lsbCBhc2sgeW91IHdoYXQgcGFja2FnZXMgeW91IHdhbnQgdG8gdXBkYXRlLiBUbyB1cGRhdGUgYWxsIHBhY2thZ2VzIGF0IG9uY2UsIHVzZSBgYXNrID0gRkFMU0VgLg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCnVwZGF0ZS5wYWNrYWdlcyhhc2sgPSBGQUxTRSkNCmBgYA0KDQpUbyB1cGRhdGUgcGFja2FnZXMgaW5zdGFsbGVkIGZyb20gYGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigpYCwgdHlwZSB0aGUgZm9sbG93aW5nIGZ1bmN0aW9uIGludG8geW91ciBSU3R1ZGlvIENvbnNvbGUgKEkgd291bGQgYWxzbyByZWNvbW1lbmQgc2F2aW5nIHRoaXMgZnVuY3Rpb24gaW4gYW4gUiBTY3JpcHQgZm9yIGxhdGVyIHVzZSk6DQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KdXBkYXRlX2dpdGh1Yl9wa2dzIDwtIGZ1bmN0aW9uKCkgew0KICAjIGNoZWNrL2xvYWQgbmVjZXNzYXJ5IHBhY2thZ2VzDQogICMgZGV2dG9vbHMgcGFja2FnZQ0KICBpZiAoISgicGFja2FnZTpkZXZ0b29scyIgJWluJSBzZWFyY2goKSkpIHsNCiAgICB0cnlDYXRjaChyZXF1aXJlKGRldnRvb2xzKSwgZXJyb3IgPSBmdW5jdGlvbih4KSB7d2FybmluZyh4KTsgY2F0KCJDYW5ub3QgbG9hZCBkZXZ0b29scyBwYWNrYWdlIFxuIil9KQ0KICAgIG9uLmV4aXQoZGV0YWNoKCJwYWNrYWdlOmRldnRvb2xzIiwgdW5sb2FkPVRSVUUpKQ0KICB9DQoNCiAgcGtncyA8LSBpbnN0YWxsZWQucGFja2FnZXMoZmllbGRzID0gIlJlbW90ZVR5cGUiKQ0KICBnaXRodWJfcGtncyA8LSBwa2dzW3BrZ3NbLCAiUmVtb3RlVHlwZSJdICVpbiUgImdpdGh1YiIsICJQYWNrYWdlIl0NCg0KICBwcmludChnaXRodWJfcGtncykNCiAgbGFwcGx5KGdpdGh1Yl9wa2dzLCBmdW5jdGlvbihwYWMpIHsNCiAgICBtZXNzYWdlKCJVcGRhdGluZyAiLCBwYWMsICIgZnJvbSBHaXRIdWIuLi4iKQ0KDQogICAgcmVwbyA9IHBhY2thZ2VEZXNjcmlwdGlvbihwYWMsIGZpZWxkcyA9ICJHaXRodWJSZXBvIikNCiAgICB1c2VybmFtZSA9IHBhY2thZ2VEZXNjcmlwdGlvbihwYWMsIGZpZWxkcyA9ICJHaXRodWJVc2VybmFtZSIpDQoNCiAgICBpbnN0YWxsX2dpdGh1YihyZXBvID0gcGFzdGUwKHVzZXJuYW1lLCAiLyIsIHJlcG8pKQ0KICB9KQ0KfQ0KYGBgDQoNClRoZW4gY2FsbCB0aGUgZnVuY3Rpb24uDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KdXBkYXRlX2dpdGh1Yl9wa2dzKCkNCmBgYA0KDQojIyMgVXBkYXRpbmcgUlN0dWRpbw0KVG8gdXBkYXRlIFJTdHVkaW8sIG9wZW4gUlN0dWRpbyBhbmQgZ28gdG8gYEhlbHAgPiBDaGVjayBmb3IgVXBkYXRlc2AgdG8gaW5zdGFsbCB0aGUgbmV3ZXN0IHZlcnNpb24uDQoNCiMgRGF0YSBUeXBlcw0KDQpSIGhhcyBhIHdpZGUgdmFyaWV0eSBvZiBkYXRhIHR5cGVzIGluY2x1ZGluZyB2ZWN0b3JzIChudW1lcmljYWwsIGNoYXJhY3RlciwgbG9naWNhbCksIG1hdHJpY2VzLCBsaXN0cywgYW5kIGRhdGEgZnJhbWVzLiBNYXRyaWNlcyBhcmUgY29tcG9zZWQgb2YgdmVjdG9ycywgYW5kIGRhdGEgZnJhbWVzIGFyZSBjb21wb3NlZCBvZiBsaXN0cy4NCg0KIyMgVmVjdG9ycw0KDQpgYGB7cn0NCmEgPC0gYygxLDIsNS4zLDYsLTIsNCkgIyBudW1lcmljIHZlY3Rvcg0KYiA8LSBjKCJvbmUiLCJ0d28iLCJ0aHJlZSIpICMgY2hhcmFjdGVyIHZlY3Rvcg0KYyA8LSBjKFRSVUUsVFJVRSxUUlVFLEZBTFNFLFRSVUUsRkFMU0UpICNsb2dpY2FsIHZlY3Rvcg0KYGBgDQoNCllvdSBjYW4gcmVmZXIgdG8gZWxlbWVudHMgb2YgYSB2ZWN0b3IgdXNpbmcgc3Vic2NyaXB0cy4NCg0KYGBge3J9DQphW2MoMiw0KV0gIyAybmQgYW5kIDR0aCBlbGVtZW50cyBvZiB2ZWN0b3INCmBgYA0KDQojIyBNYXRyaWNlcw0KDQpBbGwgY29sdW1ucyBpbiBhIG1hdHJpeCBtdXN0IGhhdmUgdGhlIHNhbWUgbW9kZSAobnVtZXJpYywgY2hhcmFjdGVyLCBldGMuKSBhbmQgdGhlIHNhbWUgbGVuZ3RoLiBUaGUgZ2VuZXJhbCBmb3JtYXQgaXMNCg0KYGBge3IsIGV2YWwgPSBGfQ0KbXltYXRyaXggPC0gbWF0cml4KHZlY3RvciwgbnJvdz1yLCBuY29sPWMsIGJ5cm93PUZBTFNFLA0KICAgZGltbmFtZXM9bGlzdChjaGFyX3ZlY3Rvcl9yb3duYW1lcywgY2hhcl92ZWN0b3JfY29sbmFtZXMpKQ0KYGBgDQoNCmBieXJvdz1UUlVFYCBpbmRpY2F0ZXMgdGhhdCB0aGUgbWF0cml4IHNob3VsZCBiZSBmaWxsZWQgYnkgcm93cy4gYGJ5cm93PUZBTFNFYCBpbmRpY2F0ZXMgdGhhdCB0aGUgbWF0cml4IHNob3VsZCBiZSBmaWxsZWQgYnkgY29sdW1ucyAodGhlIGRlZmF1bHQpLiBkaW1uYW1lcyBwcm92aWRlcyBvcHRpb25hbCBsYWJlbHMgZm9yIHRoZSBjb2x1bW5zIGFuZCByb3dzLg0KDQpgYGB7cn0NCiMgZ2VuZXJhdGVzIDUgeCA0IG51bWVyaWMgbWF0cml4DQp4IDwtIG1hdHJpeCgxOjIwLCBucm93PTUsbmNvbD00KQ0KDQojIGFub3RoZXIgZXhhbXBsZQ0KY2VsbHMgPC0gYygxLDI2LDI0LDY4KQ0Kcm5hbWVzIDwtIGMoIlIxIiwgIlIyIikNCmNuYW1lcyA8LSBjKCJDMSIsICJDMiIpDQpteW1hdHJpeCA8LSBtYXRyaXgoY2VsbHMsIG5yb3c9MiwgbmNvbD0yLCBieXJvdz1UUlVFLA0KICBkaW1uYW1lcz1saXN0KHJuYW1lcywgY25hbWVzKSkNCmBgYA0KDQpMaWtlIGluIHZlY3RvcnMsIHlvdSBjYW4gaWRlbnRpZnkgcm93cywgY29sdW1ucyBvciBlbGVtZW50cyB1c2luZyBzdWJzY3JpcHRzLg0KDQpgYGB7cn0NCnhbLDRdICMgNHRoIGNvbHVtbiBvZiBtYXRyaXgNCnhbMyxdICMgM3JkIHJvdyBvZiBtYXRyaXgNCnhbMjo0LDE6M10gIyByb3dzIDIsMyw0IG9mIGNvbHVtbnMgMSwyLDMNCmBgYA0KDQojIyBMaXN0cw0KDQpBbiBvcmRlcmVkIGNvbGxlY3Rpb24gb2Ygb2JqZWN0cyAoY29tcG9uZW50cykuIEEgbGlzdCBhbGxvd3MgeW91IHRvIGdhdGhlciBhIHZhcmlldHkgb2YgKHBvc3NpYmx5IHVucmVsYXRlZCkgb2JqZWN0cyB1bmRlciBvbmUgbmFtZS4NCg0KYGBge3J9DQojIGV4YW1wbGUgb2YgYSBsaXN0IHdpdGggNCBjb21wb25lbnRzIC0NCiMgYSBzdHJpbmcsIGEgbnVtZXJpYyB2ZWN0b3IsIGEgbWF0cml4LCBhbmQgYSBzY2FsYXINCmxpc3QxIDwtIGxpc3QobmFtZT0iRnJlZCIsIG15bnVtYmVycz1hLCBteW1hdHJpeD14LCBhZ2U9NS4zKQ0KbGlzdDIgPC0gbGlzdChjaGFyYWN0ZXI9IkxvdWlzZSIsIHNob3c9IkJvYidzIEJ1cmdlcnMiLCB0aW1lPTgzMCkNCg0KIyBleGFtcGxlIG9mIGEgbGlzdCBjb250YWluaW5nIHR3byBsaXN0cw0KdWx0aW1hdGVfbGlzdCA8LSBjKGxpc3QxLGxpc3QyKQ0KYGBgDQoNCklkZW50aWZ5IGVsZW1lbnRzIG9mIGEgbGlzdCB1c2luZyB0aGUgW1tdXSBjb252ZW50aW9uLg0KDQpgYGB7cn0NCnVsdGltYXRlX2xpc3RbWzJdXSAjIDJuZCBjb21wb25lbnQgb2YgdGhlIGxpc3QNCnVsdGltYXRlX2xpc3RbWyJteW51bWJlcnMiXV0gIyBjb21wb25lbnQgbmFtZWQgbXludW1iZXJzIGluIGxpc3QNCmBgYA0KDQojIyBEYXRhIEZyYW1lcw0KDQpBIGRhdGEgZnJhbWUgaXMgbW9yZSBnZW5lcmFsIHRoYW4gYSBtYXRyaXgsIGluIHRoYXQgZGlmZmVyZW50IGNvbHVtbnMgY2FuIGhhdmUgZGlmZmVyZW50IG1vZGVzIChudW1lcmljLCBjaGFyYWN0ZXIsIGZhY3RvciwgZXRjLikuIFRoaXMgaXMgc2ltaWxhciB0byBTQVMgYW5kIFNQU1MgZGF0YXNldHMuDQoNCmBgYHtyfQ0KZCA8LSBjKDEsMiwzLDQpDQplIDwtIGMoInJlZCIsICJ3aGl0ZSIsICJyZWQiLCBOQSkNCmYgPC0gYyhUUlVFLFRSVUUsVFJVRSxGQUxTRSkNCm15ZGF0YSA8LSBkYXRhLmZyYW1lKGQsZSxmKQ0KbmFtZXMobXlkYXRhKSA8LSBjKCJJRCIsIkNvbG9yIiwiUGFzc2VkIikgIyB2YXJpYWJsZSBuYW1lcw0KYGBgDQoNClRoZXJlIGFyZSBhIHZhcmlldHkgb2Ygd2F5cyB0byBpZGVudGlmeSB0aGUgZWxlbWVudHMgb2YgYSBkYXRhIGZyYW1lLg0KDQpgYGB7cn0NCm15ZGF0YVsyOjNdICMgY29sdW1ucyAyLDMgb2YgZGF0YSBmcmFtZQ0KbXlkYXRhW2MoIklEIiwiUGFzc2VkIildICMgY29sdW1ucyBJRCBhbmQgUGFzc2VkIGZyb20gZGF0YSBmcmFtZQ0KbXlkYXRhJENvbG9yICMgdmFyaWFibGUgQ29sb3IgaW4gdGhlIGRhdGEgZnJhbWUNCmBgYA0KDQojIyBVc2VmdWwgRnVuY3Rpb25zIGZvciBBbGwgRGF0YSBUeXBlcw0KDQpgYGB7ciwgZXZhbCA9IEZ9DQpsZW5ndGgob2JqZWN0KSAjIG51bWJlciBvZiBlbGVtZW50cyBvciBjb21wb25lbnRzDQpzdHIob2JqZWN0KSAgICAjIHN0cnVjdHVyZSBvZiBhbiBvYmplY3QNCmNsYXNzKG9iamVjdCkgICMgY2xhc3Mgb3IgdHlwZSBvZiBhbiBvYmplY3QNCm5hbWVzKG9iamVjdCkgICMgbmFtZXMNCg0KYyhvYmplY3Qsb2JqZWN0LC4uLikgICAgICAgIyBjb21iaW5lIG9iamVjdHMgaW50byBhIHZlY3Rvcg0KY2JpbmQob2JqZWN0LCBvYmplY3QsIC4uLikgIyBjb21iaW5lIG9iamVjdHMgYXMgY29sdW1ucw0KcmJpbmQob2JqZWN0LCBvYmplY3QsIC4uLikgIyBjb21iaW5lIG9iamVjdHMgYXMgcm93cw0KDQpvYmplY3QgICAgICMgcHJpbnRzIHRoZSBvYmplY3QNCg0KbHMoKSAgICAgICAjIGxpc3QgY3VycmVudCBvYmplY3RzDQpybShvYmplY3QpICMgZGVsZXRlIGFuIG9iamVjdA0KDQpuZXdvYmplY3QgPC0gZWRpdChvYmplY3QpICMgZWRpdCBjb3B5IGFuZCBzYXZlIGFzIG5ld29iamVjdA0KZml4KG9iamVjdCkgICAgICAgICAgICAgICAjIGVkaXQgaW4gcGxhY2UgDQpgYGANCg0KIyBJbXBvcnRpbmcgYW5kIFdvcmtpbmcgd2l0aCBEYXRhDQoNCiMjIFJlYWRpbmcgaW4gdGhlIERhdGFzZXQNCg0KVG9kYXksIHdlJ3JlIGdvaW5nIHRvIGJlIHVzaW5nIG9uZSBvZiB0aGUgb3ZlciA0MCwwMDAgb3BlbiBkYXRhc2V0cyBhdmFpbGFibGUgaW4gW05BU0EncyBPcGVuIERhdGEgUmVwb3NpdG9yeV0oaHR0cHM6Ly9kYXRhLm5hc2EuZ292L1NwYWNlLVNjaWVuY2UvTWV0ZW9yaXRlLUxhbmRpbmdzL2doNGctOXNmaCl7dGFyZ2V0PSJfYmxhbmsifS4gVGhpcyAiTWV0ZW9yaXRlIExhbmRpbmdzIiBkYXRhc2V0IGlzIGZyb20gdGhlIE1ldGVvcml0aWNhbCBTb2NlaXR5IGFuZCBjb250YWlucyBpbmZvcm1hdGlvbiBvbiAqYWxsKiBvZiB0aGUga25vd24gbWV0ZW9yaXRlIGxhbmRpbmdzLCBnb2luZyBhcyBmYXIgYmFjayBhcyB0aGUgZWFybHkgMTgwMCdzLg0KDQpUaGlzIGRhdGEgY2FuIGJlIGRvd25sb2FkZWQgW2F0IHRoaXMgbGlua10oaHR0cHM6Ly9naXRodWIuY29tL2Fjb2x1bS9jb25mZXJlbmNlLXByZXNlbnRhdGlvbnMvcmF3L21hc3Rlci9EYXRhJTIwTWFuaXB1bGF0aW9uJTIwd2l0aCUyMFRpZHklMjBUb29scy9kYXRhL01ldGVvcml0ZV9MYW5kaW5ncy5jc3Ype3RhcmdldD0iX2JsYW5rIn0uDQoNClRvIHJlYWQgaW4gb3VyIGRhdGEgKC5jc3YpIGZpbGUsIHdlIGNhbiB1c2UgdGhlIGZvbGxvd2luZyBjb21tYW5kOg0KDQpgYGB7cn0NCm1ldGVvcml0ZV9sYW5kaW5ncyA8LSByZWFkLmNzdigiZGF0YS9NZXRlb3JpdGVfTGFuZGluZ3MuY3N2IikNCmBgYA0KDQoqKk5vdGUgMToqKiBUaGUgc3ludGF4IGFib3ZlIGFzc3VtZXMgdGhlIC5jc3YgZmlsZSBpcyBzYXZlZCB0byB0aGUgd29ya2luZyBkaXJlY3RvcnkuIFRvIGNoYW5nZSB0aGUgd29ya2luZyBkaXJlY3RvcnksIG5hdmlnYXRlIHRvIGBTZXNzaW9uYCA+IGBTZXQgV29ya2luZyBEaXJlY3RvcnlgID4gYENob29zZSBXb3JraW5nIERpcmVjdG9yeWAgb3IgdHlwZSBgc2V0d2QoIkM6L1VzZXJzL3BhdGgiKWAgaW50byB0aGUgQ29uc29sZS4NCg0KKipOb3RlIDI6KiogYHJlYWQuY3N2YCByZWFkcyB0aGUgZmlsZSwgYnV0IHdlIGNhbid0IHVzZSB0aGUgZGF0YSB1bmxlc3Mgd2UgYXNzaWduIGl0IHRvIGEgdmFyaWFibGUuIFdlIGNhbiB0aGluayBvZiBhIHZhcmlhYmxlIGFzIGEgY29udGFpbmVyIHdpdGggYSBuYW1lLCBzdWNoIGFzIHgsIGN1cnJlbnRfdGVtcGVyYXR1cmUsIG9yIHN1YmplY3RfaWQgdGhhdCBjb250YWlucyBvbmUgb3IgbW9yZSB2YWx1ZXMuIFdlIGNhbiBjcmVhdGUgYSBuZXcgdmFyaWFibGUgaW4gUiBhbmQgYXNzaWduIGEgdmFsdWUgdG8gaXQgdXNpbmcgYDwtYC4NCg0KT25jZSB5b3UgcnVuIHRoZSBhYm92ZSBjb21tYW5kLCB5b3Ugc2hvdWxkIHNlZSBhIGBtZXRlb3JpdGVfbGFuZGluZ3NgIG9iamVjdCBpbiB0aGUgIkVudmlyb25tZW50IiBib3ggaW4gdGhlIHRvcCByaWdodCBjb3JuZXIgb2YgUlN0dWRpby4NCg0KTm93LCBsZXQncyBleHBsb3JlIHRoZSBkYXRhc2V0Lg0KDQojIyBWaWV3aW5nIHRoZSBEYXRhc2V0DQoNCklmIHdlIHdhbnQgdG8gdmlldyBvdXIgZW50aXJlIGRhdGFzZXQsIHdlIGNhbiB0eXBlIGBWaWV3KG1ldGVvcml0ZV9sYW5kaW5ncylgIGludG8gdGhlIENvbnNvbGUuIEhvd2V2ZXIsIGZvciBsYXJnZSBkYXRhIHNldHMgaXQgaXMgbXVjaCBmYXN0ZXIgYW5kIG1vcmUgY29udmVuaWVudCB0byB1c2UgdGhlIGZ1bmN0aW9uIGBoZWFkYCB0byBkaXNwbGF5IG9ubHkgdGhlIGZpcnN0IGZldyByb3dzIG9mIGRhdGEuDQoNCmBgYHtyfQ0KaGVhZChtZXRlb3JpdGVfbGFuZGluZ3MpDQpgYGANCg0KIyMjIFN0cnVjdHVyZQ0KDQpUbyB2aWV3IHRoZSBzdHJ1Y3R1cmUgb2YsIG9yIGRhdGEgdHlwZXMgZm9yLCBlYWNoIHZhcmlhYmxlIGluIGEgZGF0YXNldCwgdXNlIHRoZSBgc3RyYCwgb3IgInN0cnVjdHVyZSIgZnVuY3Rpb24uDQoNCmBgYHtyfQ0Kc3RyKG1ldGVvcml0ZV9sYW5kaW5ncykNCmBgYA0KDQojIyMjIERhdGEgVHlwZXMNCg0KTm90ZSB0aGF0IGBtZXRlb3JpdGVfbGFuZGluZ3NgIGlzIGEgZGF0YSBmcmFtZS4gWW91IGNhbiB0aGluayBvZiB0aGlzIHN0cnVjdHVyZSBhcyBhIHNwcmVhZHNoZWV0IGluIE1TIEV4Y2VsLiBEYXRhIGZyYW1lcyBhcmUgdmVyeSB1c2VmdWwgZm9yIHN0b3JpbmcgZGF0YSwgYW5kIHlvdSB3aWxsIHVzZSB0aGVtIGZyZXF1ZW50bHkgd2hlbiBwcm9ncmFtbWluZyBpbiBSLiBBIHR5cGljYWwgZGF0YSBmcmFtZSBvZiBleHBlcmltZW50YWwgZGF0YSBjb250YWlucyBpbmRpdmlkdWFsIG9ic2VydmF0aW9ucyBpbiByb3dzIGFuZCB2YXJpYWJsZXMgaW4gY29sdW1ucy4gQWxzbyBub3RlIHRoYXQgdGhlcmUgYXJlIDIgZGlmZmVyZW50IGRhdGEgdHlwZXMgaW4gb3VyIGRhdGFzZXQ6DQoNCjEuICoqRmFjdG9yKiogLSBBIENsYXNzIChub3QgYSBzdHJpbmcgb3IgbnVtYmVyISkNCg0KMi4gKipOdW0qKiAtIE51bWVyaWMsIGZsb2F0L251bWJlciB0aGFuIGNhbiBpbmNsdWRlIGRlY2ltYWwgcGxhY2VzDQoNCiMjIyBEaW1lbnNpb25zDQoNCldlIGNhbiBzZWUgdGhlIHNoYXBlLCBvciBkaW1lbnNpb25zLCBvZiB0aGUgZGF0YSBmcmFtZSB3aXRoIHRoZSBmdW5jdGlvbiBgZGltYDoNCg0KYGBge3J9DQpkaW0obWV0ZW9yaXRlX2xhbmRpbmdzKQ0KYGBgDQoNClRoaXMgdGVsbHMgdXMgdGhhdCBvdXIgZGF0YSBmcmFtZSwgYG1ldGVvcml0ZV9sYW5kaW5nc2AsIGhhcyA0NSw3MTYgcm93cyBhbmQgMTAgY29sdW1ucy4NCg0KIyMjIyBJbmRleGluZw0KDQpJZiB3ZSB3YW50IHRvIGdldCBhIHNpbmdsZSB2YWx1ZSBmcm9tIHRoZSBkYXRhIGZyYW1lLCB3ZSBjYW4gcHJvdmlkZSBhbiBpbmRleCBpbiBzcXVhcmUgYnJhY2tldHMuIFRoZSBmaXJzdCBudW1iZXIgc3BlY2lmaWVzIHRoZSByb3cgYW5kIHRoZSBzZWNvbmQgdGhlIGNvbHVtbjoNCg0KYGBge3J9DQojIGZpcnN0IHZhbHVlIGluIG1ldGVvcml0ZV9sYW5kaW5ncywgcm93IDEsIGNvbHVtbiAxDQptZXRlb3JpdGVfbGFuZGluZ3NbMSwgMV0NCg0KIyBtaWRkbGUgdmFsdWUgaW4gbWV0ZW9yaXRlX2xhbmRpbmdzLCByb3cgMjI4NTgsIGNvbHVtbiA1DQptZXRlb3JpdGVfbGFuZGluZ3NbMjI4NTgsIDVdDQpgYGANCg0KIyMjIyBTdWJzZXR0aW5nDQoNCklmIHdlIHdhbnQgdG8gc2VsZWN0IG1vcmUgdGhhbiBvbmUgcm93IG9yIGNvbHVtbiwgd2UgY2FuIHVzZSB0aGUgZnVuY3Rpb24gYGNgLCB3aGljaCBzdGFuZHMgZm9yIGNvbWJpbmUuIEZvciBleGFtcGxlLCB0byBwaWNrIGNvbHVtbnMgMSBhbmQgNSBmcm9tIHJvd3MgMSwgMywgYW5kIDUsIHdlIGNhbiBkbyB0aGlzOg0KDQpgYGB7cn0NCm1ldGVvcml0ZV9sYW5kaW5nc1tjKDEsIDMsIDUpLCBjKDEsIDUpXQ0KYGBgDQoNCldlIGZyZXF1ZW50bHkgd2FudCB0byBzZWxlY3QgY29udGlndW91cyByb3dzIG9yIGNvbHVtbnMsIHN1Y2ggYXMgdGhlIGZpcnN0IHRlbiByb3dzLCBvciBjb2x1bW5zIDMgdGhyb3VnaCA3LiBZb3UgY2FuIHVzZSBgY2AgZm9yIHRoaXMsIGJ1dCBpdCdzIG1vcmUgY29udmVuaWVudCB0byB1c2UgdGhlIGA6YCBvcGVyYXRvci4gVGhpcyBzcGVjaWFsIGZ1bmN0aW9uIGdlbmVyYXRlcyBzZXF1ZW5jZXMgb2YgbnVtYmVyczoNCg0KYGBge3J9DQoxOjUNCg0KMzoxMg0KYGBgDQoNCkZvciBleGFtcGxlLCB3ZSBjYW4gc2VsZWN0IHRoZSBmaXJzdCAyIGNvbHVtbnMgb2YgdmFsdWVzIGZvciB0aGUgZmlyc3QgZm91ciByb3dzIGxpa2UgdGhpczoNCg0KYGBge3J9DQptZXRlb3JpdGVfbGFuZGluZ3NbMTo0LCAxOjJdDQpgYGANCg0Kb3IgdGhlIGZpcnN0IDUgY29sdW1ucyBvZiByb3dzIDUgdG8gMTAgbGlrZSB0aGlzOg0KDQpgYGB7cn0NCm1ldGVvcml0ZV9sYW5kaW5nc1s1OjEwLCAxOjVdDQpgYGANCg0KSWYgeW91IHdhbnQgdG8gc2VsZWN0IGFsbCByb3dzIG9yIGFsbCBjb2x1bW5zLCBsZWF2ZSB0aGF0IGluZGV4IHZhbHVlIGVtcHR5Lg0KDQpgYGB7cn0NCiMgQWxsIGNvbHVtbnMgZnJvbSByb3cgNQ0KbWV0ZW9yaXRlX2xhbmRpbmdzWzUsIF0NCg0KIyBBbGwgcm93cyBmcm9tIGNvbHVtbnMgNi04DQptZXRlb3JpdGVfbGFuZGluZ3NbLCA2OjhdDQpgYGANCg0KSWYgeW91IGxlYXZlIGJvdGggaW5kZXggdmFsdWVzIGVtcHR5IChpLmUuLCBgbWV0ZW9yaXRlX2xhbmRpbmdzWyxdYCksIHlvdSBnZXQgdGhlIGVudGlyZSBkYXRhIGZyYW1lLg0KDQojIyBNYXRoZW1hdGljYWwgT3BlcmF0aW9ucw0KDQpOb3cgbGV0J3MgcGVyZm9ybSBzb21lIGNvbW1vbiBtYXRoZW1hdGljYWwgb3BlcmF0aW9ucyB0byBsZWFybiBtb3JlIGFib3V0IG91ciBtZXRlb3JpdGUgZGF0YS4gV2hlbiBhbmFseXppbmcgZGF0YSB3ZSBvZnRlbiB3YW50IHRvIGxvb2sgYXQgcGFydGlhbCBzdGF0aXN0aWNzLCBzdWNoIGFzIHRoZSBtYXhpbXVtIHZhbHVlIHBlciBpZCBvciB0aGUgYXZlcmFnZSB2YWx1ZSBwZXIgeWVhci4gT25lIHdheSB0byBkbyB0aGlzIGlzIHRvIHNlbGVjdCB0aGUgZGF0YSB3ZSB3YW50IHRvIGNyZWF0ZSBhIG5ldyB0ZW1wb3JhcnkgZGF0YSBmcmFtZSwgYW5kIHRoZW4gcGVyZm9ybSB0aGUgY2FsY3VsYXRpb24gb24gdGhpcyBzdWJzZXQ6DQoNCmBgYHtyfQ0KIyBmaXJzdCA1IHJvd3MsIGNvbHVtbnMgMSw1DQpmaXJzdF9maWZ0eV9tZXRlb3JzIDwtIG1ldGVvcml0ZV9sYW5kaW5nc1sxOjUwLCA1XQ0KIyBtYXggbWFzcyBvZiBmaXJzdCA1MCBtZXRlb3JzDQptYXgoZmlyc3RfZmlmdHlfbWV0ZW9ycywgbmEucm0gPSBUKQ0KDQojIGFsc28gY29ycmVjdDoNCm1heChtZXRlb3JpdGVfbGFuZGluZ3NbMTo1MCwgNV0sIG5hLnJtID0gVCkNCmBgYA0KDQpSIGFsc28gaGFzIGZ1bmN0aW9ucyBmb3Igb3RoZXIgY29tbW9uIGNhbGN1bGF0aW9ucywgZS5nLiBmaW5kaW5nIHRoZSBtaW5pbXVtLCBtZWFuLCBtZWRpYW4sIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIGRhdGE6DQoNCmBgYHtyfQ0KIyBtaW5pbXVtIG1hc3Mgb2YgZmlyc3QgNTAgbWV0ZW9ycw0KbWluKGZpcnN0X2ZpZnR5X21ldGVvcnMsIG5hLnJtID0gVCkNCg0KIyBtZWFuIG1hc3Mgb2YgZmlyc3QgNTAgbWV0ZW9ycw0KbWVhbihmaXJzdF9maWZ0eV9tZXRlb3JzLCBuYS5ybSA9IFQpDQoNCiMgbWVkaWFuIG1hc3Mgb2YgZmlyc3QgNTAgbWV0ZW9ycw0KbWVkaWFuKGZpcnN0X2ZpZnR5X21ldGVvcnMsIG5hLnJtID0gVCkNCg0KIyBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIG1hc3Mgb2YgdGhlIGZpcnN0IDUwIG1ldGVvcnMNCnNkKGZpcnN0X2ZpZnR5X21ldGVvcnMsIG5hLnJtID0gVCkNCmBgYA0KDQpSIGFsc28gaGFzIGEgZnVuY3Rpb24gdGhhdCBzdW1tYXJpZXMgdGhlIHByZXZpb3VzIGNvbW1vbiBjYWxjdWxhdGlvbnMuIEZvciBldmVyeSBjb2x1bW4gaW4gdGhlIGRhdGEgZnJhbWUsIHRoZSBmdW5jdGlvbiBgc3VtbWFyeWAgY2FsY3VsYXRlczogdGhlIG1pbmltdW0gdmFsdWUsIHRoZSBmaXJzdCBxdWFydGlsZSwgdGhlIG1lZGlhbiwgdGhlIG1lYW4sIHRoZSB0aGlyZCBxdWFydGlsZSwgYW5kIHRoZSBtYXggdmFsdWUsIGdpdmluZyBoZWxwZnVsIGRldGFpbHMgYWJvdXQgdGhlIHNhbXBsZSBkaXN0cmlidXRpb24uDQoNCmBgYHtyfQ0KIyBTdW1tYXJpemUgZnVuY3Rpb24NCnN1bW1hcnkobWV0ZW9yaXRlX2xhbmRpbmdzKQ0KYGBgDQoNCiMgTWFuaXB1bGF0aW5nIGFuZCBzb3J0aW5nIGRhdGFmcmFtZXMgd2l0aCBgZHBseXJgDQoNCiMjIFdoYXQgaXMgYGRwbHlyYD8NCg0KYGRwbHlyYCBpcyBhbiBSIHBhY2thZ2UgaW4gdGhlIGB0aWR5dmVyc2VgIHRoYXQgYWltcyB0byBtYWtlIGRhdGEgd3JhbmdsaW5nL2NsZWFuaW5nL21hbmlwdWxhdGlvbiBtb3JlIGh1bWFuLXJlYWRhYmxlLiBUaGUgYGRwbHlyYCBwYWNrYWdlIG1ha2VzIHRoZXNlIHN0ZXBzIGZhc3QgYW5kIGVhc3k6DQoNCiogQnkgY29uc3RyYWluaW5nIHlvdXIgb3B0aW9ucywgaXQgaGVscHMgeW91IHRoaW5rIGFib3V0IHlvdXIgZGF0YSBtYW5pcHVsYXRpb24gY2hhbGxlbmdlcy4NCg0KKiBJdCBwcm92aWRlcyBzaW1wbGUgInZlcmJzIiwgZnVuY3Rpb25zIHRoYXQgY29ycmVzcG9uZCB0byB0aGUgbW9zdCBjb21tb24gZGF0YSBtYW5pcHVsYXRpb24gdGFza3MsIHRvIGhlbHAgeW91IHRyYW5zbGF0ZSB5b3VyIHRob3VnaHRzIGludG8gY29kZS4NCg0KKiBJdCB1c2VzIGVmZmljaWVudCBiYWNrZW5kcywgc28geW91IHNwZW5kIGxlc3MgdGltZSB3YWl0aW5nIGZvciB0aGUgY29tcHV0ZXIuDQoNClRoaXMgdHV0b3JpYWwgaW50cm9kdWNlcyB5b3UgdG8gYGRwbHlyYCdzIGJhc2ljIHNldCBvZiB0b29scywgYW5kIHNob3dzIHlvdSBob3cgdG8gYXBwbHkgdGhlbSB0byBkYXRhIGZyYW1lcy4gQXNpZGUgZnJvbSB0aGlzIHR1dG9yaWFsLCBhIGhlbHBmdWwgcmVzb3VyY2UgZm9yIGdldHRpbmcgZmFtaWxpYXIgd2l0aCBgZHBseXJgIGlzIFt0aGlzIGNvbXByZWhlbnNpdmUgUlN0dWRpbyBjaGVhdHNoZWV0Ll0oaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvcmF3L21hc3Rlci9kYXRhLXRyYW5zZm9ybWF0aW9uLnBkZil7dGFyZ2V0PSJfYmxhbmsifQ0KDQoqKlNpZGUgTm90ZToqKiBgZHBseXJgIGFsc28gc3VwcG9ydHMgZGF0YWJhc2VzIHZpYSB0aGUgYGRicGx5cmAgcGFja2FnZS4gT25jZSB5b3UgaW5zdGFsbCBgZGJwbHlyYCwgeW91IGNhbiByZWFkIGB2aWduZXR0ZSgiZGJwbHlyIilgIHRvIGxlYXJuIG1vcmUuDQoNCiMjIEdldHRpbmcgU3RhcnRlZCB3aXRoIGBkcGx5cmANCg0KSWYgeW91IGRvbid0IGFscmVhZHkgaGF2ZSBgZHBseXJgIG9yIHRoZSBgdGlkeXZlcnNlYCBpbnN0YWxsZWQsIHlvdSBjYW4gaW5zdGFsbCB0aGUgYGRwbHlyYCBwYWNrYWdlIGJ5IHR5cGluZyB0aGUgZm9sbG93aW5nIGNvbW1hbmQgaW50byB0aGUgQ29uc29sZToNCg0KYGBge3IsIGV2YWwgPSBGfQ0KaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQ0KYGBgDQoNCk9uY2UgeW91IGhhdmUgYGRwbHlyYCBpbnN0YWxsZWQsIGxvYWQgaXQgaW50byB5b3VyIFIgc2Vzc2lvbiB3aXRoIA0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpgYGANCg0KYGRwbHlyYCBhaW1zIHRvIHByb3ZpZGUgYSBmdW5jdGlvbiBmb3IgZWFjaCBiYXNpYyB2ZXJiIG9mIGRhdGEgbWFuaXB1bGF0aW9uOg0KDQp8IEZ1bmN0aW9uIHwgUHVycG9zZSB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgYGZpbHRlcigpYCB8IHRvIHNlbGVjdCBjYXNlcyBiYXNlZCBvbiB0aGVpciB2YWx1ZXMgfA0KfCBgYXJyYW5nZSgpYCB8IHRvIHJlb3JkZXIgdGhlIGNhc2VzIHwNCnwgYHNlbGVjdCgpYCwgYHJlbmFtZSgpYCB8IHRvIHNlbGVjdCB2YXJpYWJsZXMgYmFzZWQgb24gdGhlaXIgbmFtZXMgfA0KfCBgbXV0YXRlKClgLCBgdHJhbnNtdXRlKClgIHwgdG8gYWRkIG5ldyB2YXJpYWJsZXMgdGhhdCBhcmUgZnVuY3Rpb25zIG9mIGV4aXN0aW5nIHZhcmlhYmxlcyB8DQp8IGBzdW1tYXJpc2UoKWAgfCB0byBjb25kZW5zZSBtdWx0aXBsZSB2YWx1ZXMgdG8gYSBzaW5nbGUgdmFsdWUgfA0KfCBgc2FtcGxlX24oKWAsIGBzYW1wbGVfZnJhYygpYCB8IHRvIHRha2UgcmFuZG9tIHNhbXBsZXMgfA0KICAgIA0KSW4gdGhpcyB0dXRvcmlhbCwgd2UnbGwgc3BlbmQgc29tZSB0aW1lIHdvcmtpbmcgd2l0aCBlYWNoIGluZGl2aWR1YWwgZnVuY3Rpb24uDQoNCiMjIyBQaXBpbmcgT3BlcmF0b3IgKGAlPiVgKQ0KDQpUaGUgb3BlcmF0b3JzIGAlPiVgIHBpcGUgdGhlaXIgbGVmdC1oYW5kIHNpZGUgdmFsdWVzIGZvcndhcmQgaW50byBleHByZXNzaW9ucyB0aGF0IGFwcGVhciBvbiB0aGUgcmlnaHQtaGFuZCBzaWRlLCBpLmUuIG9uZSBjYW4gcmVwbGFjZSBmKHgpIHdpdGggeCBgJT4lYCBmKCksIHdoZXJlIGAlPiVgIGlzIHRoZSAobWFpbikgcGlwZS1vcGVyYXRvci4gV2hlbiBjb3VwbGluZyBzZXZlcmFsIGZ1bmN0aW9uIGNhbGxzIHdpdGggdGhlIHBpcGUtb3BlcmF0b3IsIHRoZSBiZW5lZml0IHdpbGwgYmVjb21lIG1vcmUgYXBwYXJlbnQuIENvbnNpZGVyIHRoaXMgcHNldWRvIGV4YW1wbGU6DQoNCmBgYHtyLCBldmFsID0gRn0NCnRoZV9kYXRhIDwtIHJlYWQuY3N2KCcvcGF0aC90by9kYXRhL2ZpbGUuY3N2JykgJT4lDQogIHN1YnNldCh2YXJpYWJsZV9hID4geCkgJT4lDQogIHRyYW5zZm9ybSh2YXJpYWJsZV9jID0gdmFyaWFibGVfYS92YXJpYWJsZV9iKSAlPiUNCiAgaGVhZCgxMDApDQpgYGANCg0KRm91ciBvcGVyYXRpb25zIGFyZSBwZXJmb3JtZWQgdG8gYXJyaXZlIGF0IHRoZSBkZXNpcmVkIGRhdGEgc2V0LCBhbmQgdGhleSBhcmUgd3JpdHRlbiBpbiBhIG5hdHVyYWwgb3JkZXI6IHRoZSBzYW1lIGFzIHRoZSBvcmRlciBvZiBleGVjdXRpb24uIEFsc28sIG5vIHRlbXBvcmFyeSB2YXJpYWJsZXMgYXJlIG5lZWRlZC4gSWYgeWV0IGFub3RoZXIgb3BlcmF0aW9uIGlzIHJlcXVpcmVkLCBpdCBpcyBzdHJhaWdodC1mb3J3YXJkIHRvIGFkZCB0byB0aGUgc2VxdWVuY2Ugb2Ygb3BlcmF0aW9ucyB3aGVyZXZlciBpdCBtYXkgYmUgbmVlZGVkLg0KDQojIyBGaWx0ZXIgcm93cyB3aXRoIGBmaWx0ZXIoKWANCg0KYGZpbHRlcigpYCBhbGxvd3MgeW91IHRvIHNlbGVjdCBhIHN1YnNldCBvZiByb3dzIGluIGEgZGF0YSBmcmFtZS4gTGlrZSBhbGwgc2luZ2xlIHZlcmJzLCB0aGUgZmlyc3QgYXJndW1lbnQgaXMgdGhlIGRhdGEgZnJhbWUuIFRoZSBzZWNvbmQgYW5kIHN1YnNlcXVlbnQgYXJndW1lbnRzIHJlZmVyIHRvIHZhcmlhYmxlcyB3aXRoaW4gdGhhdCBkYXRhIGZyYW1lLCBzZWxlY3Rpbmcgcm93cyB3aGVyZSB0aGUgZXhwcmVzc2lvbiBpcyBgVFJVRWAuDQoNCkZvciBleGFtcGxlLCB3ZSBjYW4gc2VsZWN0IGFsbCBtZXRlb3JpdGVzIGluIHRoZSBMNSBjbGFzcyB3aXRoIG1hc3MgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIDEwLDAwMCBncmFtcyB3aXRoOg0KDQpgYGB7cn0NCm1ldGVvcml0ZV9sYW5kaW5ncyAlPiUgDQogIGZpbHRlcihtYXNzID49IDEwMDAwICYgcmVjY2xhc3MgPT0gIkw1IikNCmBgYA0KDQpUaGlzIGlzIHJvdWdobHkgZXF1aXZhbGVudCB0byB0aGlzIGJhc2UgUiBjb2RlOg0KDQpgYGB7ciwgZXZhbCA9IEZ9DQptZXRlb3JpdGVfbGFuZGluZ3NbbWV0ZW9yaXRlX2xhbmRpbmdzJG1hc3MgPj0gMTAwMDAgJiBtZXRlb3JpdGVfbGFuZGluZ3MkcmVjY2xhc3MgPT0gIkw1IiwgXQ0KYGBgDQoNCiMjIEFycmFuZ2Ugcm93cyB3aXRoIGBhcnJhbmdlKClgDQoNCmBhcnJhbmdlKClgIHdvcmtzIHNpbWlsYXJseSB0byBgZmlsdGVyKClgIGV4Y2VwdCB0aGF0IGluc3RlYWQgb2YgZmlsdGVyaW5nIG9yIHNlbGVjdGluZyByb3dzLCBpdCByZW9yZGVycyB0aGVtLiBJdCB0YWtlcyBhIGRhdGEgZnJhbWUgYW5kIGEgc2V0IG9mIGNvbHVtbiBuYW1lcyAob3IgbW9yZSBjb21wbGljYXRlZCBleHByZXNzaW9ucykgdG8gb3JkZXIgYnkuIElmIHlvdSBwcm92aWRlIG1vcmUgdGhhbiBvbmUgY29sdW1uIG5hbWUsIGVhY2ggYWRkaXRpb25hbCBjb2x1bW4gd2lsbCBiZSB1c2VkIHRvIGJyZWFrIHRpZXMgaW4gdGhlIHZhbHVlcyBvZiBwcmVjZWRpbmcgY29sdW1uczoNCg0KYGBge3J9DQptZXRlb3JpdGVfbGFuZGluZ3MgJT4lIA0KICBhcnJhbmdlKHJlY2xhdCwgcmVjbG9uZywgbWFzcykNCmBgYA0KDQpVc2UgYGRlc2MoKWAgdG8gb3JkZXIgYSBjb2x1bW4gaW4gZGVzY2VuZGluZyBvcmRlcjoNCg0KYGBge3J9DQptZXRlb3JpdGVfbGFuZGluZ3MgJT4lIA0KICBhcnJhbmdlKGRlc2MobWFzcykpDQpgYGANCg0KIyMgU2VsZWN0IGNvbHVtbnMgd2l0aCBgc2VsZWN0KClgDQoNCk9mdGVuIHlvdSB3b3JrIHdpdGggbGFyZ2UgZGF0YXNldHMgd2l0aCBtYW55IGNvbHVtbnMgYnV0IG9ubHkgYSBmZXcgYXJlIGFjdHVhbGx5IG9mIGludGVyZXN0IHRvIHlvdS4gYHNlbGVjdCgpYCBhbGxvd3MgeW91IHRvIHJhcGlkbHkgem9vbSBpbiBvbiBhIHVzZWZ1bCBzdWJzZXQgdXNpbmcgb3BlcmF0aW9ucyB0aGF0IHVzdWFsbHkgb25seSB3b3JrIG9uIG51bWVyaWMgdmFyaWFibGUgcG9zaXRpb25zOg0KDQpgYGB7cn0NCiMgU2VsZWN0IGNvbHVtbnMgYnkgbmFtZQ0KbWV0ZW9yaXRlX2xhbmRpbmdzICU+JSANCiAgc2VsZWN0KG5hbWUsIHJlY2NsYXNzLCBtYXNzKQ0KDQojIFNlbGVjdCBhbGwgY29sdW1ucyBiZXR3ZWVuIG5hbWUgYW5kIHllYXIgKGluY2x1c2l2ZSkNCm1ldGVvcml0ZV9sYW5kaW5ncyAlPiUgDQogIHNlbGVjdChuYW1lOnllYXIpDQoNCiMgU2VsZWN0IGFsbCBjb2x1bW5zIGV4Y2VwdCB0aG9zZSBmcm9tIG5hbWUgdG8geWVhciAoaW5jbHVzaXZlKQ0KbWV0ZW9yaXRlX2xhbmRpbmdzICU+JSANCiAgc2VsZWN0KC0obmFtZTp5ZWFyKSkNCmBgYA0KDQpUaGVyZSBhcmUgYSBudW1iZXIgb2YgaGVscGVyIGZ1bmN0aW9ucyB5b3UgY2FuIHVzZSB3aXRoaW4gYHNlbGVjdCgpYCwgbGlrZSBgc3RhcnRzX3dpdGgoKWAsIGBlbmRzX3dpdGgoKWAsIGBtYXRjaGVzKClgIGFuZCBgY29udGFpbnMoKWAuIFRoZXNlIGxldCB5b3UgcXVpY2tseSBtYXRjaCBsYXJnZXIgYmxvY2tzIG9mIHZhcmlhYmxlcyB0aGF0IG1lZXQgc29tZSBjcml0ZXJpb24uIFNlZSBgP3NlbGVjdGAgZm9yIG1vcmUgZGV0YWlscy4NCg0KWW91IGNhbiByZW5hbWUgdmFyaWFibGVzIHdpdGggYHNlbGVjdCgpYCBieSB1c2luZyBuYW1lZCBhcmd1bWVudHM6DQoNCmBgYHtyfQ0KbWV0ZW9yaXRlX2xhbmRpbmdzICU+JSANCiAgc2VsZWN0KG1hc3NfZyA9IG1hc3MpDQpgYGANCg0KQnV0IGJlY2F1c2UgYHNlbGVjdCgpYCBkcm9wcyBhbGwgdGhlIHZhcmlhYmxlcyBub3QgZXhwbGljaXRseSBtZW50aW9uZWQsIGl0J3Mgbm90IHRoYXQgdXNlZnVsLiBJbnN0ZWFkLCB1c2UgYHJlbmFtZSgpYDoNCg0KYGBge3J9DQptZXRlb3JpdGVfbGFuZGluZ3MgJT4lIA0KICByZW5hbWUobWFzc19nID0gbWFzcykNCmBgYA0KDQojIyBBZGQgbmV3IGNvbHVtbnMgd2l0aCBgbXV0YXRlKClgDQoNCkJlc2lkZXMgc2VsZWN0aW5nIHNldHMgb2YgZXhpc3RpbmcgY29sdW1ucywgaXQncyBvZnRlbiB1c2VmdWwgdG8gYWRkIG5ldyBjb2x1bW5zIHRoYXQgYXJlIGZ1bmN0aW9ucyBvZiBleGlzdGluZyBjb2x1bW5zLiBUaGlzIGlzIHRoZSBqb2Igb2YgYG11dGF0ZSgpYDoNCg0KYGBge3J9DQptZXRlb3JpdGVfbGFuZGluZ3MgJT4lIA0KICBtdXRhdGUobWFzc19rZyA9IG1hc3MvMTAwMCkNCmBgYA0KDQpgZHBseXI6Om11dGF0ZSgpYCBpcyBzaW1pbGFyIHRvIHRoZSBiYXNlIGB0cmFuc2Zvcm0oKWAsIGJ1dCBhbGxvd3MgeW91IHRvIHJlZmVyIHRvIGNvbHVtbnMgdGhhdCB5b3UndmUganVzdCBjcmVhdGVkLg0KDQpJZiB5b3Ugb25seSB3YW50IHRvIGtlZXAgdGhlIG5ldyB2YXJpYWJsZXMsIHVzZSBgdHJhbnNtdXRlKClgOg0KDQpgYGB7cn0NCm1ldGVvcml0ZV9sYW5kaW5ncyAlPiUgDQogIHRyYW5zbXV0ZShtYXNzX2tnID0gbWFzcy8xMDAwKQ0KYGBgDQoNCiMjIFN1bW1hcmlzZSB2YWx1ZXMgd2l0aCBgc3VtbWFyaXNlKClgDQoNClRoZSBsYXN0IHZlcmIgaXMgYHN1bW1hcmlzZSgpYC4gSXQgY29sbGFwc2VzIGEgZGF0YSBmcmFtZSB0byBhIHNpbmdsZSByb3cuDQoNCmBgYHtyfQ0KbWV0ZW9yaXRlX2xhbmRpbmdzICU+JSANCiAgc3VtbWFyaXNlKG1lYW5fbWFzcyA9IG1lYW4obWFzcywgbmEucm0gPSBUKSkNCmBgYA0KDQpJdCdzIG5vdCB0aGF0IHVzZWZ1bCB1bnRpbCB3ZSBsZWFybiB0aGUgYGdyb3VwX2J5KClgIHZlcmIgYmVsb3cuDQoNCmBgYHtyfQ0KbWV0ZW9yaXRlX2xhbmRpbmdzICU+JSANCiAgZ3JvdXBfYnkocmVjY2xhc3MpICU+JSANCiAgc3VtbWFyaXNlKG1lYW5fbWFzcyA9IG1lYW4obWFzcywgbmEucm0gPSBUKSkNCmBgYA0KDQojIyBSYW5kb21seSBzYW1wbGUgcm93cyB3aXRoIGBzYW1wbGVfbigpYCBhbmQgYHNhbXBsZV9mcmFjKClgDQoNCllvdSBjYW4gdXNlIGBzYW1wbGVfbigpYCBhbmQgYHNhbXBsZV9mcmFjKClgIHRvIHRha2UgYSByYW5kb20gc2FtcGxlIG9mIHJvd3M6IHVzZSBgc2FtcGxlX24oKWAgZm9yIGEgZml4ZWQgbnVtYmVyIGFuZCBgc2FtcGxlX2ZyYWMoKWAgZm9yIGEgZml4ZWQgZnJhY3Rpb24uDQoNCmBgYHtyfQ0KbWV0ZW9yaXRlX2xhbmRpbmdzICU+JSANCiAgc2FtcGxlX24oMTApDQoNCm1ldGVvcml0ZV9sYW5kaW5ncyAlPiUgDQogIHNhbXBsZV9mcmFjKDAuMDEpDQpgYGANCg0KVXNlIGByZXBsYWNlID0gVFJVRWAgdG8gcGVyZm9ybSBhIGJvb3RzdHJhcCBzYW1wbGUuIElmIG5lZWRlZCwgeW91IGNhbiB3ZWlnaHQgdGhlIHNhbXBsZSB3aXRoIHRoZSBgd2VpZ2h0YCBhcmd1bWVudC4NCg0KIyMgVGVuIGBkcGx5cmAgVHJpY2tzIQ0KDQojIyMgQXJlIHlvdSBvZnRlbiBzZWxlY3RpbmcgdGhlIHNhbWUgY29sdW1ucyBvdmVyIGFuZCBvdmVyIGFnYWluPw0KDQpZb3UgY2FuIG1ha2UgYSB2ZWN0b3Igb2YgcHJlLWlkZW50aWZpZWQgY29sdW1ucyBvbmNlIGFuZCB0aGVuIHJlZmVyIHRvIHRoZW0gdXNpbmcgYG9uZV9vZigpYCBvciBgISFgIChldmVuIHNob3J0ZXIpLg0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQoNCmNvbHMgPC0gYygibmFtZSIsICJyZWNsYXQiLCAicmVjbG9uZyIpDQoNCmV4MSA8LSBtZXRlb3JpdGVfbGFuZGluZ3MgJT4lIA0KICBzZWxlY3QoISFjb2xzKQ0KDQpoZWFkKGV4MSkNCmBgYA0KDQojIyMgU2VsZWN0IGNvbHVtbnMgdmlhIHJlZ2V4DQoNCklmIHlvdSBoYXZlIG1hdGNoaW5nIHBhdHRlcm5zLCB5b3UgY2FuIHVzZSBgc3RhcnRzX3dpdGgoKWAsIGBjb250YWlucygpYCwgb3IgYGVuZHNfd2l0aGAuIEJ1dCB3aGF0IGlmIHlvdXIgcGF0dGVybiBpc24ndCB0aGF0IGV4YWN0PyBTaW1wbGU6IGVudGVyIHJlZ2V4IGludG8gYG1hdGNoZXMoKWAuDQoNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCg0KZXgyIDwtIGlyaXMgJT4lIA0KICBzZWxlY3QobWF0Y2hlcygiUy4rdGgiKSkNCg0KaGVhZChleDIpDQpgYGANCg0KIyMjIFJlb3JkZXJpbmcgeW91ciBjb2x1bW5zDQoNCklmIHlvdSBqdXN0IHdhbnQgdG8gYnJpbmcgb25lIG9yIG1vcmUgY29sdW1ucyB0byB0aGUgZnJvbnQsIHlvdSBjYW4gdXNlIGBldmVyeXRoaW5nKClgIHRvIGFkZCBhbGwgdGhlIHJlbWFpbmluZyBjb2x1bW5zLg0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQoNCmV4MyA8LSBtZXRlb3JpdGVfbGFuZGluZ3MgJT4lIA0KICBzZWxlY3QoaWQsIGV2ZXJ5dGhpbmcoKSkNCg0KaGVhZChleDMpDQpgYGANCg0KIyMjIFJlbmFtaW5nIGFsbCB2YXJpYWJsZXMgaW4gb25lIGdvDQoNCk9uZSBjb21tYW5kIHRvIGdldCB0aGVtIGFsbCBpbiBsb3dlciBjYXNlLCBhbmQgb25lIG1vcmUgdG8gcmVwbGFjZSAiLi5nLiIgaW4gdGhlIGBtYXNzYCB2YXJpYWJsZS4NCg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShzdHJpbmdyKQ0KDQpleDQgPC0gbWV0ZW9yaXRlX2xhbmRpbmdzICU+JSANCiAgcmVuYW1lX2FsbCh0b2xvd2VyKSAlPiUgDQogIHJlbmFtZV9hbGwofnN0cl9yZXBsYWNlKC4sICIuLmcuIiwgIiIpKQ0KDQpoZWFkKGV4NCkNCmBgYA0KDQojIyMgQ2xlYW5pbmcgdXAgeW91ciBvYnNlcnZhdGlvbnMgaW4gb25lIGdvDQoNClRoZSBgc2VsZWN0X2FsbC9pZi9hdGAgYW5kIGByZW5hbWVfYWxsL2lmL2F0YCBmdW5jdGlvbnMgd2lsbCBvbmx5IG1vZGlmeSB0aGUgdmFyaWFibGUgbmFtZXMsIG5vdCB0aGUgb2JzZXJ2YXRpb25zLiBJZiB5b3Ugd2FudCB0byBjaGFuZ2UgdGhvc2UsIHVzZSB0aGUgYG11dGF0ZWAgdmFyaWFudC4NCg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShzdHJpbmdyKQ0KDQpleDUgPC0gbWV0ZW9yaXRlX2xhbmRpbmdzICU+JSANCiAgc2VsZWN0KG5hbWUsIG5hbWV0eXBlLCBmYWxsKSAlPiUgDQogIG11dGF0ZV9hbGwodG9sb3dlcikgJT4lIA0KICBtdXRhdGVfYWxsKH5zdHJfcmVwbGFjZV9hbGwoLiwgIiAiLCAiXyIpKQ0KDQpoZWFkKGV4NSkNCmBgYA0KDQojIyMgRmluZGluZyB0aGUgNSBoaWdoZXN0L2xvd2VzdCB2YWx1ZXMNCg0KWW91IGNhbiB1c2UgYHRvcF9uYCB0byBmaW5kIHRoZSA1IG1ldGVvcml0ZXMgd2l0aCB0aGUgaGlnaGVzdCBtYXNzIHdpdGhvdXQgb3JkZXJpbmcgdGhlbSBmaXJzdC4NCg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KDQptZXRlb3JpdGVfbGFuZGluZ3MgJT4lIA0KICB0b3Bfbig1LCBtYXNzKQ0KYGBgDQoNCiMjIyBBZGRpbmcgdGhlIGFtb3VudCBvZiBvYnNlcnZhdGlvbnMNCg0KWW91IGNhbiBhZGQgdGhlIGFtb3VudCBvZiBvYnNlcnZhdGlvbnMgd2l0aG91dCBzdW1tYXJpc2luZyB0aGVtIHlvdXJzZWxmLiBJZiB5b3UgZG9uJ3QgbGlrZSB0aGUgZGVmYXVsdCBjb2x1bW4gbmFtZSBgbmAsIHlvdSBjYW4gY2hhbmdlIGl0IGFnYWluIHdpdGggYSBgcmVuYW1lKClgIHN0YXRlbWVudC4NCg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KDQpleDcgPC0gbWV0ZW9yaXRlX2xhbmRpbmdzICU+JSANCiAgYWRkX2NvdW50KHJlY2NsYXNzKSAlPiUgDQogIHJlbmFtZShuX3JlY2NsYXNzID0gbikNCg0KaGVhZChleDcpDQpgYGANCg0KIyMjIE1ha2luZyBuZXcgZGlzY3JldGUgdmFyaWFibGVzDQoNCmBjYXNlX3doZW4oKWAgY2FuIGJlIGEgdmVyeSBwb3dlcmZ1bCB0b29sIHRvIG1ha2UgbmV3IGRpc2NyZXRlIHZhcmlhYmxlcyBiYXNlZCBvbiBvdGhlciBjb2x1bW5zLg0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQoNCmV4OCA8LSBzdGFyd2FycyAlPiUgDQogIHNlbGVjdChuYW1lLCBzcGVjaWVzLCBob21ld29ybGQsIGJpcnRoX3llYXIsIGhhaXJfY29sb3IpICU+JSANCiAgbXV0YXRlKG5ld19ncm91cCA9IGNhc2Vfd2hlbigNCiAgICBzcGVjaWVzID09ICJEcm9pZCIgfiAiUm9ib3QiLA0KICAgIGhvbWV3b3JsZCA9PSAiVGF0b29pbmUiICYgaGFpcl9jb2xvciA9PSAiYmxvbmQiIH4gIkJsb25kIFRhdG9vaW5pYW4iLA0KICAgIGhvbWV3b3JsZCA9PSAiVGF0b29pbmUiIH4gIk90aGVyIFRhdG9vaW5pYW4iLA0KICAgIGhhaXJfY29sb3IgPT0gImJsb25kIiB+ICJCbG9uZCBub24tVGF0b29pbmlhbiIsDQogICAgVFJVRSB+ICJPdGhlciBIdW1hbiIpKQ0KDQpoZWFkKGV4OCkNCmBgYA0KDQojIyMgR29pbmcgcm93d2lzZS4uLg0KDQpNdXRhdGluZyB3aXRoIGFnZ3JlZ2F0ZSBmdW5jdGlvbnMgYnkgZGVmYXVsdCB3aWxsIHRha2UgdGhlIGF2ZXJhZ2Uvc3VtLy4uLiBvZiB0aGUgZW50aXJlIGNvbHVtbi4gVmlhIGFkZGluZyBgcm93d2lzZSgpYCB5b3UgY2FuIGFnZ3JlZ2F0ZSB3aXRoaW4gYW4gb2JzZXJ2YXRpb24uDQoNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCg0KZXg5IDwtIGlyaXMgJT4lIA0KICBzZWxlY3QoY29udGFpbnMoIkxlbmd0aCIpKSAlPiUgDQogIHJvd3dpc2UoKSAlPiUgDQogIG11dGF0ZShhdmdfbGVuZ3RoID0gbWVhbihjKFBldGFsLkxlbmd0aCwgU2VwYWwuTGVuZ3RoKSkpDQoNCmhlYWQoZXg5KQ0KYGBgDQoNCiMjIyBDaGFuZ2luZyB5b3VyIGNvbHVtbiBuYW1lcyBhZnRlciBgc3VtbWFyaXNlX2lmYA0KDQpJZiB5b3UndmUgdXNlZCB0aGUgYHN1bW1hcmlzZV9hbGwvaWYvYXRgIHZhcmlhbnRzIGJlZm9yZSwgeW91IGtub3cgdGhhdCB0aGUgdmFyaWFibGUgbmFtZSBieSBkZWZhdWx0IGRvZXMgbm90IGdldCBjaGFuZ2VkLiBJZiB5b3Ugd2FudCBhIG1vZGlmaWVkIG5hbWUsIHlvdSBjYW4gd3JhcCB5b3VyIGZ1bmN0aW9uIGluc2lkZSBgZnVucygpYCBhbmQgYWRkIGEgdGFnIHRoYXQgd2lsbCBiZSBhZGRlZCB0byB0aGUgdmFyaWFibGUgbmFtZS4NCg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KDQppcmlzICU+JSANCiAgc3VtbWFyaXNlX2lmKGlzLm51bWVyaWMsIGZ1bnMoYXZnID0gbWVhbikpDQpgYGANCg0KIyBBZGRpdGlvbmFsIFJlc291cmNlcw0KDQojIyBIZWxwZnVsIFBhY2thZ2VzIFRvIEdldCBTdGFydGVkIFdpdGgNCg0KVG8gbGVhcm4gbW9yZSBhYm91dCBhbmQgcmVhZCBkb2N1bWVudGF0aW9uIGZvciBlYWNoIG9mIHRoZXNlIHBhY2thZ2VzLCBlaXRoZXIgdHlwZSBgPzxwYWNrYWdlbmFtZT5gIGludG8geW91ciBSU3R1ZGlvIGNvbnNvbGUgb3Igc2VhcmNoIGA8cGFja2FnZW5hbWU+YCBpbiB0aGUgW0NSQU4gcmVwb3NpdG9yeV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2F2YWlsYWJsZV9wYWNrYWdlc19ieV9uYW1lLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0uDQoNCi0gYGJsb2JgIC0gZm9yIHN0b3JpbmcgYmxvYiAoYmluYXJ5KSBkYXRhDQotIGBib290YCAtIGJvb3RzdHJhcCBmdW5jdGlvbnMNCi0gYGJyb29tYCAtIHRpZGllcyBzdGF0aXN0aWNhbCBtb2RlbHMgaW50byBkYXRhIGZyYW1lcw0KLSBgY2FyZXRgIC0gc3RyZWFtbGluZXMgdGhlIHByb2Nlc3MgZm9yIGNyZWF0aW5nIHByZWRpY3RpdmUgbW9kZWxzIFsoSGVscGZ1bCBXZWJzaXRlKV0oaHR0cDovL3RvcGVwby5naXRodWIuaW8vY2FyZXQvaW5kZXguaHRtbCl7dGFyZ2V0PSJfYmxhbmsifQ0KLSBgY2x1c3RlcmAgLSBjbHVzdGVyaW5nIG1ldGhvZHMNCi0gYGNvZWZwbG90YCAtIHBsb3RzIGNvZWZmaWNpZW50cyBmcm9tIGZpdHRlZCBtb2RlbHMNCi0gYGRhdGEudGFibGVgIC0gZXh0ZW5zaW9uIG9mIGRhdGEuZnJhbWVzDQotIGBkZXZ0b29sc2AgLSB0b29scyB0byBtYWtlIGFuIFIgZGV2ZWxvcGVyJ3MgbGlmZSBlYXNpZXINCi0gYGRwbHlyYCAtICoqcGFydCBvZiB0aGUgVGlkeXZlcnNlLCoqIGNvbnZlbmllbnQgZGF0YSBtYW5pcHVsYXRpb24sIG11bmdpbmcsIGFuZCBjbGVhbmluZyBpbiBSDQotIGBmb3JjYXRzYCAtICoqcGFydCBvZiB0aGUgVGlkeXZlcnNlLCoqIHNvbHZlcyBjb21tb24gcHJvYmxlbXMgd2l0aCBmYWN0b3JzDQotIGBnYm1gIC0gZ3JhZGllbnQgYm9vc3RpbmcgbW9kZWxzLCBjYW4gYmUgaW50ZWdyYXRlZCB3aXRoIGBjYXJldGANCi0gYGdncGxvdDJgIC0gKipwYXJ0IG9mIHRoZSBUaWR5dmVyc2UsKiogZGF0YSB2aXN1YWxpemF0aW9uIGluIFIgdGhhdCBmb2xsb3dzIHRoZSBbIkdyYW1tYXIgb2YgR3JhcGhpY3MiXShodHRwczovL3d3dy5hbWF6b24uY29tL0dyYW1tYXItR3JhcGhpY3MtU3RhdGlzdGljcy1Db21wdXRpbmcvZHAvMDM4NzI0NTQ0OCl7dGFyZ2V0PSJfYmxhbmsifQ0KLSBgZ2xtbmV0YCAtIGxhc3NvIGFuZCBlbGFzdGljLW5ldCByZWd1bGFyaXplZCBHTE1zLCBjYW4gYmUgaW50ZWdyYXRlZCB3aXRoIGBjYXJldGANCi0gYGdyaWRFeHRyYWAgLSBtaXNjZWxsYW5lb3VzIGZ1bmN0aW9ucyBmb3IgImdyaWQiIGdyYXBoaWNzDQotIGBobXNgIC0gZm9yIHdvcmtpbmcgd2l0aCB0aW1lLW9mLWRheSB2YWx1ZXMNCi0gYElTTFJgIC0gZGF0YSBmb3IgYW4gIkludHJvZHVjdGlvbiB0byBTdGF0aXN0aWNhbCBMZWFybmluZyB3aXRoIEFwcGxpY2F0aW9ucyBpbiBSIg0KLSBgbHVicmlkYXRlYCAtIGZvciB3b3JraW5nIHdpdGggZGF0ZXMgYW5kIGRhdGUtdGltZXMNCi0gYE1BU1NgIC0gZnVuY3Rpb25zIGFuZCBkYXRhc2V0cyBmb3IgYXBwbGllZCBzdGF0aXN0aWNzDQotIGBwZHBgIC0gcGFydGlhbCBkZXBlbmRlbmNlIHBsb3RzDQotIGBwbHNgIC0gcGFydGlhbCBsZWFzdCBzcXVhcmVzIGFuZCBwcmluY2lwYWwgY29tcG9uZW50IHJlZ3Jlc3Npb24sIGNhbiBiZSBpbnRlZ3JhdGVkIHdpdGggYGNhcmV0YA0KLSBgcGx5cmAgLSAqKnBhcnQgb2YgdGhlIFRpZHl2ZXJzZSwqKiB0b29scyBmb3Igc3BsaXQtYXBwbHktY29tYmluZSBhbmFseXNlcw0KLSBgcFJPQ2AgLSBkaXNwbGF5cyBhbmQgYW5hbHl6ZXMgUk9DIGN1cnZlcw0KLSBgcHVycnJgIC0gKipwYXJ0IG9mIHRoZSBUaWR5dmVyc2UsKiogZW5oYW5jZXMgUidzIGZ1bmN0aW9uYWwgcHJvZ3JhbW1pbmcgc2V0IG9mIHRvb2xzIGZvciB3b3JraW5nIHdpdGggZnVuY3Rpb25zIGFuZCB2ZWN0b3JzDQotIGByYW5kb21Gb3Jlc3RgIC0gcmFuZG9tIGZvcmVzdCBtb2RlbHMsIGNhbiBiZSBpbnRlZ3JhdGVkIHdpdGggYGNhcmV0YA0KLSBgcmVhZHJgIC0gKipwYXJ0IG9mIHRoZSBUaWR5dmVyc2UsKiogcHJvdmlkZXMgYSBmYXN0IGFuZCBmcmllbmRseSB3YXkgdG8gcmVhZCByZWN0YW5ndWxhciBkYXRhDQotIGByZWFkeGxgIC0gKipwYXJ0IG9mIHRoZSBUaWR5dmVyc2UsKiptYWtlcyByZWFkaW5nIEV4Y2VsIGZpbGVzIG11Y2ggZWFzaWVyDQotIGBycGFydGAgLSBkZWNpc2lvbiB0cmVlIG1vZGVscywgY2FuIGJlIGludGVncmF0ZWQgd2l0aCBgY2FyZXRgDQotIGBycGFydC5wbG90YCAtIHBsb3R0aW5nIG9mIGRlY2lzaW9uIHRyZWUgbW9kZWxzDQotIGBzdHJpbmdyYCAtICoqcGFydCBvZiB0aGUgVGlkeXZlcnNlLCoqIG1ha2VzIHdvcmtpbmcgd2l0aCBzdHJpbmcgb2JqZWN0cyBtdWNoIGVhc2llcg0KLSBgdGliYmxlYCAtICoqcGFydCBvZiB0aGUgVGlkeXZlcnNlLCoqIGNyZWF0ZXMgZGF0YS5mcmFtZXMgdGhhdCBhcmUgZWFzaWVyIHRvIHdvcmsgd2l0aA0KLSBgdGlkeXJgIC0gKipwYXJ0IG9mIHRoZSBUaWR5dmVyc2UsKiogcHJvdmlkZXMgYSBzZXQgb2YgZnVuY3Rpb25zIHRvIGhlbHAgeW91IGdldCB0byB0aWR5IGRhdGENCi0gYHRpZHl2ZXJzZWAgLSAqKnNldCBvZiBoZWxwZnVsIHBhY2thZ2VzIGZvciB0aWRpZXIgZGF0YSoqIFsoSGVscGZ1bCBXZWJzaXRlKV0oaHR0cDovL3RpZHl2ZXJzZS5vcmcpe3RhcmdldD0iX2JsYW5rIn0NCi0gYHhnYm9vc3RgIC0gZVh0cmVtZSBHcmFkaWVudCBCb29zdGluZyBtb2RlbHMsIGNhbiBiZSBpbnRlZ3JhdGVkIHdpdGggYGNhcmV0YA0KDQojIyBSIGFuZCBSU3R1ZGlvIFJlc291cmNlcw0KDQojIyMgUlN0dWRpbyBDaGVhdHNoZWV0cw0KDQpbUlN0dWRpbyBDaGVhdHNoZWV0c10oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcmVzb3VyY2VzL2NoZWF0c2hlZXRzLyl7dGFyZ2V0PSJfYmxhbmsifSBjYW4gYmUgZG93bmxvYWRlZCBmcm9tIHRoZSBSU3R1ZGlvIElERSBieSBuYXZpZ2F0aW5nIHRvIGBIZWxwYCA+IGBDaGVhdHNoZWV0c2AuIFRoZSBtb3N0IGNvbW1vbmx5LXVzZWQgY2hlYXRzaGVldHMgaW5jbHVkZToNCg0KLSBbUlN0dWRpbyBJREUgQ2hlYXQgU2hlZXRdKGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL3Jhdy9tYXN0ZXIvcnN0dWRpby1pZGUucGRmKXt0YXJnZXQ9Il9ibGFuayJ9DQotIFtEYXRhIEltcG9ydCBDaGVhdCBTaGVldF0oaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvcmF3L21hc3Rlci9kYXRhLWltcG9ydC5wZGYpe3RhcmdldD0iX2JsYW5rIn0NCi0gW0RhdGEgVHJhbnNmb3JtYXRpb24gd2l0aCBgZHBseXJgXShodHRwczovL2dpdGh1Yi5jb20vcnN0dWRpby9jaGVhdHNoZWV0cy9yYXcvbWFzdGVyL2RhdGEtdHJhbnNmb3JtYXRpb24ucGRmKXt0YXJnZXQ9Il9ibGFuayJ9DQotIFtEYXRhIFZpc3VhbGl6YXRpb24gd2l0aCBgZ2dwbG90MmBdKGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL3Jhdy9tYXN0ZXIvZGF0YS12aXN1YWxpemF0aW9uLTIuMS5wZGYpe3RhcmdldD0iX2JsYW5rIn0NCi0gW0RhdGVzIGFuZCBUaW1lcyB3aXRoIGBsdWJyaWRhdGVgXShodHRwczovL2dpdGh1Yi5jb20vcnN0dWRpby9jaGVhdHNoZWV0cy9yYXcvbWFzdGVyL2x1YnJpZGF0ZS5wZGYpe3RhcmdldD0iX2JsYW5rIn0NCi0gW1N0cmluZyBNYW5pcHVsYXRpb24gd2l0aCBgc3RyaW5ncmBdKGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL3Jhdy9tYXN0ZXIvc3RyaW5ncy5wZGYpe3RhcmdldD0iX2JsYW5rIn0NCi0gW0FwcGx5IGZ1bmN0aW9ucyB3aXRoIGBwdXJycmBdKGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL3Jhdy9tYXN0ZXIvcHVycnIucGRmKXt0YXJnZXQ9Il9ibGFuayJ9DQoNCiMjIyBIZWxwZnVsIExpbmtzDQoNCi0gW0NSQU4gSW50cm8gdG8gUiBNYW51YWxdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL2RvYy9tYW51YWxzL3ItcmVsZWFzZS9SLWludHJvLnBkZil7dGFyZ2V0PSJfYmxhbmsifQ0KLSBbVGhlIFIgR3JhcGggR2FsbGVyeV0oaHR0cHM6Ly93d3cuci1ncmFwaC1nYWxsZXJ5LmNvbS8pe3RhcmdldD0iX2JsYW5rIn0NCi0gW1RvcCA1MCBgZ2dwbG90MmAgdmlzdWFsaXphdGlvbnNdKGh0dHA6Ly9yLXN0YXRpc3RpY3MuY28vVG9wNTAtR2dwbG90Mi1WaXN1YWxpemF0aW9ucy1NYXN0ZXJMaXN0LVItQ29kZS5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9DQoNCiMjIyBHb29nbGUgYW5kIENvbW11bml0eQ0KDQotIFtHb29nbGUgYW5kIFRyeSFdKGh0dHA6Ly9nb29nbGUuY29tKXt0YXJnZXQ9Il9ibGFuayJ9DQotIFtSU2Vlay5vcmcgKEdvb2dsZSBmb3IgUildKGh0dHA6Ly9yc2Vlay5vcmcpe3RhcmdldD0iX2JsYW5rIn0NCi0gW09DIFIgVXNlciBHcm91cF0oaHR0cDovL21lZXR1cC5jb20vT0MtUlVHLyl7dGFyZ2V0PSJfYmxhbmsifQ0KLSBbUi1MYWRpZXMgSXJ2aW5lXShodHRwOi8vbWVldHVwLmNvbS9ybGFkaWVzLWlydmluZS8pe3RhcmdldD0iX2JsYW5rIn0NCi0gWyNyc3RhdHMgY29tbXVuaXR5IG9uIFR3aXR0ZXJdKGh0dHA6Ly90d2l0dGVyLmNvbSl7dGFyZ2V0PSJfYmxhbmsifQ0KDQojIFRoYW5rIHlvdSBmb3IgYXR0ZW5kaW5nIG15IHR1dG9yaWFsIG9uICJEYXRhIE1hbmlwdWxhdGlvbiB3aXRoIFRpZHkgVG9vbHMuIg0KDQpBbGwgdHV0b3JpYWwgbWF0ZXJpYWxzIGNhbiBiZSBmb3VuZCBbaGVyZSBvbiBteSBHaXRIdWIuXShodHRwczovL2dpdGh1Yi5jb20vYWNvbHVtL2NvbmZlcmVuY2UtcHJlc2VudGF0aW9ucy90cmVlL21hc3Rlci9EYXRhJTIwTWFuaXB1bGF0aW9uJTIwd2l0aCUyMFRpZHklMjBUb29scyl7dGFyZ2V0PSJfYmxhbmsifQ0KDQpUaGFua3Mgc28gbXVjaCBmb3IgbGlzdGVuaW5nIGFuZCBmb2xsb3dpbmcgYWxvbmcsIGFuZCBJIGhvcGUgeW91J2xsIGhhdmUgYSBncmVhdCByZXN0IG9mIHlvdXIgaGFja2F0aG9uIQ0KDQo8aHIgLz4NCg0KPGNlbnRlcj4NCg0KQSB3b3JrIGJ5IFtBbHlzc2EgQ29sdW1idXNdKGh0dHBzOi8vYWx5c3NhY29sdW1idXMuY29tLyl7dGFyZ2V0PSJfYmxhbmsifS4NCg0KKltoZWxsb0BhbHlzc2Fjb2x1bWJ1cy5jb21dKG1haWx0bzpoZWxsb0BhbHlzc2Fjb2x1bWJ1cy5jb20pe3RhcmdldD0iX2JsYW5rIn0qDQoNCjxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS9hamF4L2xpYnMvZm9udC1hd2Vzb21lLzQuNy4wL2Nzcy9mb250LWF3ZXNvbWUubWluLmNzcyI+DQogICAgDQo8cCBzdHlsZT0idGV4dC1hbGlnbjogY2VudGVyOyI+DQo8YSBocmVmPSJodHRwczovL2FseXNzYWNvbHVtYnVzLmNvbS8iIGNsYXNzPSJmYSBmYS1saW5rIiB0YXJnZXQ9Il9ibGFuayI+PC9hPg0KPGEgaHJlZj0iaHR0cHM6Ly90d2l0dGVyLmNvbS9hbHljb2x1bWJ1cz9sYW5nPWVuIiBjbGFzcz0iZmEgZmEtdHdpdHRlciIgdGFyZ2V0PSJfYmxhbmsiPjwvYT4NCjxhIGhyZWY9Imh0dHBzOi8vd3d3LmxpbmtlZGluLmNvbS9pbi9hY29sdW0vIiBjbGFzcz0iZmEgZmEtbGlua2VkaW4iIHRhcmdldD0iX2JsYW5rIj48L2E+DQo8YSBocmVmPSJodHRwczovL2dpdGh1Yi5jb20vYWNvbHVtLyIgY2xhc3M9ImZhIGZhLWdpdGh1YiIgdGFyZ2V0PSJfYmxhbmsiPjwvYT4NCjwvcD4NCg0KPC9jZW50ZXI+