Import
From csv
library(tidyverse)
data <- read_csv("folder/filename.csv")
From a googlesheet
library(googlesheets)
sheet <- gs_title(“Google sheet name”)
data <- sheet %>% gs_read(ws = “Worksheet name”)
From a table on a webpage
library(rvest)
url <- read_html('url')
data_raw <- url %>%
html_node("table") %>%
html_table(fill=TRUE)
From a PDF
library(SPARQL) # SPARQL querying package
library(ggplot2)
# Step 1 - Set up preliminaries and define query
# Define the data.gov endpoint
endpoint <- "http://services.data.gov/sparql"
# create query statement
query <-
"PREFIX dgp1187: <http://data-gov.tw.rpi.edu/vocab/p/1187/>
SELECT ?ye ?fi ?ac
WHERE {
?s dgp1187:year ?ye .
?s dgp1187:fires ?fi .
?s dgp1187:acres ?ac .
}"
# Step 2 - Use SPARQL package to submit query and save results to a data frame
qd <- SPARQL(endpoint,query)
df <- qd$results
From multiple csvs
- Create a vector of all the datasets
all_datasets <- tibble("file" = c('dataset_name_1', 'dataset_name_2'))
- Create a function to import one dataset
import <- function(df){
df <- read_csv(str_c("df, ".csv"))
}
- Use purrr::map() to run the function on (each element) of all the datasets
all_data <- all_datasets %>% mutate(dataset = purrr::map(file, import))
- Run a ‘for loop’ to get R to assign each tibble to an object
for(i in 1:nrow(all_data)){
assign(all_data$file[[i]], all_data$dataset[[i]])
}
From multiple webpages
- Create a vector with the URL paths needed, eg:
url_unique <- # function to list these, eg using seq() if numerical
url_base <- "https://abc"
url_end <- "/abc.html"
url_path <- url_unique %>% str_c(url_base, . , url_end)
data <- tibble(url_path)
- Define a function to scrape (and clean) data from one url
scrape <- function(url){
xml <- read_html(url) %>%
html_node("table") %>%
html_table(fill=TRUE) %>%
xml
- (if needed) Prevent error messages from URL paths that don’t exist
extract_results <- function(df){
df$result
}
- Loop scrape function over all URLs as nested tibbles
data <- data %>%
mutate(new_column = purrr::map(url_path, safely(scrape)),
new_column = map(new_column, extract_results))
Save as csv
write_csv(dataset, "filename.csv")
Tidy
Inspect
Get list of all column headings in a dataset
colnames(dataset)
Get the number of rows and columns in a dataset
summarise()
Get the breakdown of results within a column
table(dataset$column)
Get the number of values in a column
summary(dataset$column)
Get the number of NA values in a column
sum(is.na(dataset$column))
tidyr
Wide –> skinny
gather(dataset, "new_column_name1", "new_column_name2", column range eg '2:8')
Skinny –> wide
spread(dataset, $keycolumn, $valuecolumn)
Combine datasets by a common column
merge(dataset1, dataset2, by = "common column name")
Combine datasets by attaching rows
append(dataset1, dataset2)
Create a new column
mutate(old_variable, new_variable = (calculation))
Rename a column
rename(dataset, new name = old name)
Rename categories within a column
data %>%
mutate(variable = fct_recode(variable, "new name" = "old name",
"new name = "old name"))
Alternatives (more for correcting typos like Find & Replace): dataset$column[dataset$column == "old"] <- "new"
"new_name" <- length(grep("existing_label", dataset$column))
Drop column(s) / row(s)
subset(dataset, select = -c(columns))
subset(dataset, column != "")
Remove NAs
na.omit(dataset)
Arrange a column in descending order
arrange(dataset, desc(column)))
stringr
Filter a column if contains text string
grepl(“text string”, variable)
Remove symbols, eg % sign
dataset$column <- gsub("\\%","", dataset$column)
Convert character column to numeric
dataset$column <- as.numeric(as.character(dataset$column))
Round numbers
mutate(variable = round(variable))
Clean column heading names (to lowercase with underscores
clean_names()
Create a quick pivot table to summarise variables
tabyl(variable1, variable2) %>%
adorn_*
Remove empty rows and columns
remove_empty()
Fix dates stored as serial numbers
excel_numeric_to_date()
Visualise 🤩
Basics
Classic code structure
ggplot(dataset) +
viz_function(aes(x = variable,
y = variable,
group = variable,
size = variable,
fill = variable) +
scale_x/y_discrete/continuous(breaks = c(),
limits = c(),
labels = c()) +
labs(title = "...",
subtitle = "...",
x = "...",
y = "...",
caption = "...") +
theme() +
ggsave("name.filetype", height = , width = , unit = "")
Additional layers
geom_hline()
geom_vline()
geom_segment()
geom_text()
geom_rect()
Transform data within a ggplot function
plot1 <- ggplot(filter(dataset, variable == criteria))
Legends
Reorder
- First create a factor:
dataset$newcolumn <- factor(dataset$column, levels = c(“item1”, “item2”) , ordered = TRUE)
- Then in ggplot() aesthetic mappings, use this new column, rather than the pre-existing one.
Remove
theme(legend.position = "none")
Axes
Arrange in descending order
ggplot(aes(x = reorder(variable1, -variable2), y = variable2)
Text
Remove / style axes labels
axis.title.x/y = element_blank() or element_text(size = , family = , font = )
axis.text.x/y = element_blank() or element_text(size = , family = , font = )
Wrap long labels
scale_x_discrete(labels = function(x) str_wrap(x, width = 10))
Rotate labels 45 degrees
theme(axis.text.x=element_text(angle=45))
Themes
From ggplot
theme_gray() – signature ggplot2 theme
theme_bw() – dark on light ggplot2 theme
theme_linedraw() – uses black lines on white backgrounds only
theme_light() – similar to linedraw() but with grey lines aswell
theme_dark() – lines on a dark background instead of light
theme_minimal() – no background annotations, minimal feel.
theme_classic() – theme with no grid lines
theme_void() – empty theme with no elements
From ggthemes
theme_solid() - Theme with nothing other than a background color
theme_map() - Clean theme for maps
theme_igray() - Inverse gray theme
theme_economist()
theme_fivethirtyeight()
theme_wsj()
theme_few()
theme_tufte()
theme_excel()
theme_gdocs()
theme_stata()
theme_solarized()
theme_hc() - Highcharts JS theme
Interactive ggplots
- iraph
library(iraph)
ggplot(data, aes(x = , y = , tooltip = variable) +
geom_???_interactive()
- plotly
library(plotly)
ggplotly(plot, tooltip = variable)
highcharter
htmlwidgets
RShiny
To make a shiny app, you need to create script with a filename ending “app.R”
The basic structure of an R Shiny app:
library(shiny)
ui <- fluidPage()
input() functions, eg sliderInput()
output() functions, eg plotOutput()
server <- function(input, output) { }
output$xxxx <- render*(), eg renderPlot()
shinyApp(ui = ui, server = server)
Geographical maps
Introduction
There seems to be lots of different ways you can make maps in R: partly because the best approach will depend on what type of map you’re making; and partly just because there’s lots of competing packages available.
A note on shapefiles
Examples
- Create a basic map of the world (minus Antarctica)
library(maps)
library(ggplot2)
world_map <- map_data("world") %>%
filter(region != "Antarctica") %>%
ggplot(aes(x = long, y = lat, group = group)) +
geom_polygon(fill = "light blue") +
coord_fixed() +
theme_void()
world_map
- Create a basic map of a country, eg UK
library(maps)
library(ggplot2)
UK_map <- map_data("world", region = "UK") %>%
ggplot(aes(x = long, y = lat, group = group)) +
geom_polygon(fill = "dark blue") +
coord_fixed() +
theme_void()
UK_map
- Create a detailed map of a country, eg counties within UK Using sf and ggplot packages as per the following tutorials:
library(maps)
library(tidyverse)
library(sf)
UK_map <- map_data("world", region = "UK") %>%
ggplot(aes(x = long, y = lat, group = group)) +
geom_polygon(fill = "dark blue") +
coord_fixed() +
theme_void()
UK_map
Tile grid maps
Package for creating tile grid maps for regions including:
Related: countrycode package
Model
“All models are wrong, but some are useful”
The goal of a model is not to uncover truth, but to discover a simple approximation that is useful (in order to understand the behaviour of something).
- linear model takes the general form:
y = a_0 + a_1 * x
where
(NB this is another way of expressing y = mx + c)
Linear models also assume that the ‘residuals’ (difference between observed and predicted values) have a normal distribution.
lm
or, how to apply a linear model to data
- Create a model (a type of list) with the function
lm(), where x_variable is the independent ‘cause’ and y_variable is the dependent ‘consequence’:
model_name <- lm(y_variable ~ x_variable, data = datset)
- Create a ‘grid’ of your data (evenly spaced grid of points from the data)
grid_name <- data %>% data_grid(x_variable)
- Add predictions to your gridded data using the model you created
grid_name <- grid_name %>% add_preidictions(model_name)
- To visualise the linear model, do the usual ggplot of the distribution of the
dataset, then add a geom_line layer using the grid_name as its data, eg
ggplot(dataset, aes(x_variable, y_variable)) +
geom_hex(bins = ###) +
geom_line(grid_name)
geom_smooth
or, modelling with ggplot (notes taken from Data visualization: a practical introduction).
Basic linear model:
geom_smooth(method = "lm")
Specify linear model:
geom_smooth(method = "lm", formula = y_variable ~ x_variable)
Iterate
Using the book ‘R for Data Science’ and the tidyverse package
Functions
Functions allow you to automate tasks, as opposed to copy-and-pasting similar bits of code
There are huge advantages in using a function rather than copy-and-paste: if you want to make a change, you only need to do it once; and you’re less likely to make typos and mistakes.
Use base R to write functions
There are 3 basic building blocks for creating a function.
A name
The inputs (or arguments)
The ‘body’ of the function, ie code that goes between {}
…which comes together as:
function_name <- function(arguments){ function code }
What you get back from the function will by default be the last statement the function evaluates.
BUT you can also choose to use:
return() in the middle of the function code to signal to make it easier to understand, eg if you’ve written very long if statements.
invisible() to ensure that something doesn’t get printed out as a result of the function, eg a new dataframe that you’re using to create a plot
Examples
If we were to create a function that adds 2 to the input, it would look like this:
add_two <- function(x){
x+2
}
and you can apply it like this
add_two(2)
you can also apply it to every element of a vector
b <- c(1:5)
add_two(b)
Here’s another example. We can create a fizzbuzz function that takes a single number as input and returns “fizz” if it’s divisible by three and “buzz” if it’s divisible by 5 and “fizzbuzz” if it’s divisible by 3 and 5.
fizzbuzz <- function(x) {
# stopifnot() function ensures the truth of an r expression
stopifnot(length(x) == 1)
stopifnot(is.numeric(x))
# %% and && are logical operators, where %% is 'remainder from division' and && is 'and'
if (!(x %% 3) && !(x %% 5)) {
"fizzbuzz"
} else if (!(x %% 3)) {
"fizz"
} else if (!(x %% 5)) {
"buzz"
} else {
x
}
}
fizzbuzz(378)
It’s useful to have some naming conventions for your function arguments. The following are recommended:
x, y and z for vectors
df for data frames
i for the row number (indices)
j for the column number (indices)
n for the number of rows
p for the numer of columns
It’s also useful to add a generic error message to your functions with stopifnot(), which you can use to assert what SHOULD be true (rather than checking for what might be wrong). For example:
function_name <- function(x, y, na.rm = FALSE) {
stopifnot(is.logical(na.rm), length(na.rm) == 1)
stopifnot(length(x) == length(y))
function code
}
For loops
Functions help to reduce duplication of repeated patterns of code
Iteration helps to reduce duplication of repeated operations on multiple inputs, eg performing the same operation on different columns or different datasets
There are 2 types of iteration:
Imperative programming, eg for loops and while loops
Functional programming means even less duplication than you get in imperative programming
For loops: the basics
Examples An example of a simple for loop
output <- vector("double", ncol(mtcars)) # The vector should be of the type 'double' and with the same number of elements as there are columns in mtcars
names(output) <- names(mtcars) # The names of the elements should be the names of the columns in mtcars
for (i in names(mtcars)) { # For every column in mtcars
output[i] <- mean(mtcars[[i]]) # make the i'th element of the vector output the mean of the i'th column in mtcars (where i'th = 1st, 2nd, 3rd etc)
}
output # Print output
A similar example that doesn’t require the line names(output) <- names(mtcars) but gives results without column headings would be:
output <- vector("double", ncol(mtcars)) # The vector should be of the type 'double' and with the same number of elements as there are columns in mtcars
for (i in 1:ncol(mtcars)) { # For every column in the number of columns that mtcars has
output[i] <- mean(mtcars[[i]]) # make the i'th element of the vector output the mean of the i'th column in mtcars (where i'th = 1st, 2nd, 3rd etc)
}
output # Print output
For loops: variations
For loops aren’t limited to creating new objects; you can also use them to change existing ones by just removing the assignment at the start
For loops can loop over names and values (not just indices / number of columns)
For loops can handle outputs and sequences of unknown length with ‘while’ loops (although this is most useful in context of simulation, so unlikely to need)
Examples
How to load a directory full of csvs into a single data frame using a for loop.
- Create a vector with the filename paths
files <- dir("data/", pattern = "\\.csv$", full.names = TRUE)
- Use a for loop that preallocates a list to the filenames and loads them in with read_csv()
df <- vector("list"", length(files))
for (fname in seq_along(files)) {
df[[i]] <- read_csv(files[[i]])
}
- Combine them into a single dataframe
df <- bind_rows(df)
Purrr
purrr = better than for loops because it makes code easier to write and to read by focusing on the operation being performed (and not the bookkeeping required to loop over every element and store the output!)
Key idea of functional programming = passing one function to another function
It eliminates the need for many common for loops and is similar to the apply() family of functions
Key idea is to have functions that perform the common pattern of:
Looping over a vector
Doing something to each element
Saving the results
There are different purrr functions for different types of output, as follows:
map() to make a list
map_lgl() to make a logical vector
map_int() to make an integer vector
map_dbl() to make a double vector
map_chr() to make a character vector
There are also useful shortcuts you can use within purrr:
Here’s the basic structure for using a map function:
map_vectortype(dataset, function)
Examples Calculate the mean of every column in mtcars
map_dbl(mtcars, mean)
Compute the number of unique values in each column of iris
# V1 WITHOUT SHORTCUTS
map_int(iris, function(x) length(unique(x)))
# V2 WITH SHORTCUTS
map_int(iris, ~ length(unique(.)))
Use a map function within a mutate, using a function you’ve created, ie applying a function to every variable within a dataset
# Example dataset
a <- tibble(x = c(1:4), y = c(100:103))
a
# Example function (the old add_two created earlier in the Functions chapter notes)
add_two <- function(x){
x+2
}
# Example map function
c <- a %>%
mutate(z = map_dbl(x, add_two),
z2 = map(x, add_two))
c
c$z2
Apply multiple map functions to nested tibbles, ie applying a set of functions to separate datasets
# Create function, in this case to filter a dataset by Petal.Length > 5
filter_petal <- function(df){
df %>%
filter(Petal.Length > 5)
}
# Turn a normal dataframe into a dataframe of nested tibbles, in this case the iris dataset nested by species
iris_mod <- iris %>%
group_by(Species) %>%
nest()
# Create 3 new columns in all of the nested tibbles
iris_mod %>%
# Use map to apply the new function filter_petal to each of the separate datasets for Species within iris_mod (We use map() because the output from applying filter_petal to data is a list.)
mutate(fl_petal = purrr::map(data, filter_petal),
# If we want to add the number of rows in each dataset, we would then use map_dbl(), because the output from nrow is a single number.
n_full_data = purrr::map_dbl(data, nrow),
# Ditto - use map_dbl() for this output, which is the number of rows in our new column, fl_petal, which we created above
n_petal_large = purrr::map_dbl(fl_petal, nrow))
Iterate with ggplot
Add creating a function to repeat the same ggplot & programmatically writing titles, captions etc <<
Iterate with walk
Iteratively save files
Use walk() and its variants walk2 and pwalk() when you want to call a function for its side effects, rather than its return value.
This is the case when you want to render output to the screen or save a file to disk.
For example, if you had a list of plots and a vector of filenames, you can use pwalk() as follows:
# Create a list of multiple plots using the map() function
plots <- mtcars %>%
split(.$cyl) %>%
map(~ggplot(., aes(mpg, wt)) + geom_point())
plots
# Create an iterative filenaming structure for each plot, in this case the names of the plots followed by .pdf
paths <- stringr::str_c(names(plots), ".pdf")
# Save each plot to the path names defined in 'paths' by combining the plots and the pathnames in a list and saving that list
pwalk(list(paths, plots), ggsave, path = tempdir())
Notes
Factors
Factors are used for categories
Factors can either have an order that’s “arbitrary” (eg hair colour) or “principled” (eg months)
They’re esp useful if you want to display character vectors in non-alphabetical order
Use forcats package (not part of tidyverse) for working with factors
For example, say you have a string of months
library(forcats)
x1 <- c("Dec", "Apr", "Jan", "Mar")
To create a “factor” of months over a year:
- First create a list of the different months, or levels, in the order they should appear
month_levels <- c("Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec")
- Then create a factor with the function factor() and levels =
y1 <- factor(x1, levels = month_levels)
You now have a factor that you can sort according to the order you set with sort()
sort(y1)
You can check the levels in your factor with levels()
levels(y1)
You can reorder your factor in at least 2 ways:
- By another variable, eg which month has the most birthdays, with fct_reorder():
fct_reorder(factor, variable)
- By moving one or more levels to the end, eg showing “Not applicable” at the end, with fct_relevel():
fct_relevel(factor, "level to pull out", "level to pull out")
You can also edit your factor with fct_recode. This is particularly useful for editing labels for publication. And you can use this technique to combine multiple “old” levels into the same “new” level, eg grouping several levels under “Other”.
data %>% mutate(variable = fct_recode(variable, "new name" = "old name", "new name" = "old name"))
Other modifications you can make to your factor include:
- fct_collapse(), which is like fct_recode(), but works so that for each new variable you can provide a vector of old levels, eg
data %>% mutate(variable = fct_collapse(variable, "new name" = c("old name", "old name", "old name"))
- fct_lump, which crudely lumps together all the “small” groups to simplify a plot or table
data %>% mutate(variable = fct_lump(variable))
Vectors
Note about integers vs doubles: if you just enter 2 in R, it interprets this as 2.000000, ie a double. To enter 2 as an integer you have to write 2L.
typeof(2)
typeof(2L)
typeof(2.5L)
So what can you do with vectors?
You can find out what type of vector a vector is with `typeof()
You can find out how many items there are in a vector with `length()
You can specify how to read a vector when you import with readr by using col_types specifications, for example:
data <- read_csv("data.csv"),
col_types = cols(column_name = col_logical(),
column_name = col_integer(),
column_name = col_double(),
column_name = col_character(),
column_name = col_date(format = ""),
column_name = col_time(format = ""),
column_name = col_datetime(format = ""),
column_name = col_number(), # Isn't fussy about numbers containing commas, $ etc
column_name = col_skip(), # Skips importing this column
column_name = col_guess() # Guesses how to parse based on input
)
You can convert one type of vector to another with as.logical(), as.integer(), as.double() or as.character()
You can perform basic maths operations on vectors, ie don’t need to iterate for every item in the vector
You can create and name a vector with c()
Lists
Can contain a mix of atomic vector types and other lists
It’s useful to use str() because this will give you the structure of your list, rather than the contents
To subset items within a list use double brackets [[]] rather than single [].
Augmented vectors
Factors, dates, date-times and tibbles are augmented vectors, because they have additional attributes (on top of the usual vector attributes of names, dimensions and class)
Factors have a ‘levels’ attribute
Dates and date-times have additional ‘class’ attributes
Tibbles have additional ‘class’ attributes (tbl_df, tbl and data.frame) as well as column names and row.names
LS0tCnRpdGxlOiAiQ29va2Jvb2siCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdGhlbWU6IGRhcmtseQogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCi0tLQoKIyBTZXR1cAoKIyMjIEluc3RhbGwKYGBgCmluc3RhbGwucGFja2FnZSgicGFja2FnZV9uYW1lIikKYGBgCgojIyMgTG9hZApgYGAKbGlicmFyeShwYWNrYWdlX25hbWUpCmBgYAoKIyBJbXBvcnQKCiMjIyBGcm9tIGNzdgpgYGAKbGlicmFyeSh0aWR5dmVyc2UpCmRhdGEgPC0gcmVhZF9jc3YoImZvbGRlci9maWxlbmFtZS5jc3YiKQpgYGAKCiMjIyBGcm9tIGEgZ29vZ2xlc2hlZXQKYGBgCmxpYnJhcnkoZ29vZ2xlc2hlZXRzKQpzaGVldCA8LSBnc190aXRsZSjigJxHb29nbGUgc2hlZXQgbmFtZeKAnSkKZGF0YSA8LSBzaGVldCAlPiUgZ3NfcmVhZCh3cyA9IOKAnFdvcmtzaGVldCBuYW1l4oCdKQpgYGAKCiMjIyBGcm9tIGEgdGFibGUgb24gYSB3ZWJwYWdlCmBgYApsaWJyYXJ5KHJ2ZXN0KQp1cmwgPC0gcmVhZF9odG1sKCd1cmwnKQpkYXRhX3JhdyA8LSB1cmwgJT4lCiAgICAgICAgICAgICAgaHRtbF9ub2RlKCJ0YWJsZSIpICU+JQogICAgICAgICAgICAgIGh0bWxfdGFibGUoZmlsbD1UUlVFKQpgYGAKCiMjIyBGcm9tIGEgUERGCi0gVHJ5IGBwZGZ0b29sc2AgcGFja2FnZSAmIFt0aGlzIHR1dG9yaWFsXShodHRwczovL3d3dy5icm9kcmlndWVzLmNvL2Jsb2cvMjAxOC0wNi0xMC1zY3JhcGluZ19wZGZzLz91dG1fY2FtcGFpZ249RGF0YV9FbGl4aXImdXRtX21lZGl1bT1lbWFpbCZ1dG1fc291cmNlPURhdGFfRWxpeGlyXzE4NykKCi0gVXNpbmcgU1BBUlFMIChjb2RlIGZyb20gW1IgYmxvZ2dlcnNdKGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tL3NwYXJxbC13aXRoLXItaW4tbGVzcy10aGFuLTUtbWludXRlcy9hbXAvKSk6CmBgYApsaWJyYXJ5KFNQQVJRTCkgIyBTUEFSUUwgcXVlcnlpbmcgcGFja2FnZQpsaWJyYXJ5KGdncGxvdDIpCgojIFN0ZXAgMSAtIFNldCB1cCBwcmVsaW1pbmFyaWVzIGFuZCBkZWZpbmUgcXVlcnkKIyBEZWZpbmUgdGhlIGRhdGEuZ292IGVuZHBvaW50CmVuZHBvaW50IDwtICJodHRwOi8vc2VydmljZXMuZGF0YS5nb3Yvc3BhcnFsIgoKIyBjcmVhdGUgcXVlcnkgc3RhdGVtZW50CnF1ZXJ5IDwtCiJQUkVGSVggIGRncDExODc6IDxodHRwOi8vZGF0YS1nb3YudHcucnBpLmVkdS92b2NhYi9wLzExODcvPgpTRUxFQ1QgP3llID9maSA/YWMKV0hFUkUgewo/cyBkZ3AxMTg3OnllYXIgP3llIC4KP3MgZGdwMTE4NzpmaXJlcyA/ZmkgLgo/cyBkZ3AxMTg3OmFjcmVzID9hYyAuCn0iCgojIFN0ZXAgMiAtIFVzZSBTUEFSUUwgcGFja2FnZSB0byBzdWJtaXQgcXVlcnkgYW5kIHNhdmUgcmVzdWx0cyB0byBhIGRhdGEgZnJhbWUKcWQgPC0gU1BBUlFMKGVuZHBvaW50LHF1ZXJ5KQpkZiA8LSBxZCRyZXN1bHRzCmBgYAoKIyMjIEZyb20gbXVsdGlwbGUgY3N2cwoKICAxLiBDcmVhdGUgYSB2ZWN0b3Igb2YgYWxsIHRoZSBkYXRhc2V0cwogIApgYGAKICBhbGxfZGF0YXNldHMgPC0gdGliYmxlKCJmaWxlIiA9IGMoJ2RhdGFzZXRfbmFtZV8xJywgJ2RhdGFzZXRfbmFtZV8yJykpIApgYGAKCiAgMi4gQ3JlYXRlIGEgZnVuY3Rpb24gdG8gaW1wb3J0IG9uZSBkYXRhc2V0CmBgYAogIGltcG9ydCA8LSBmdW5jdGlvbihkZil7CiAgICBkZiA8LSByZWFkX2NzdihzdHJfYygiZGYsICIuY3N2IikpCiAgfQpgYGAKCiAgMy4gVXNlIHB1cnJyOjptYXAoKSB0byBydW4gdGhlIGZ1bmN0aW9uIG9uIChlYWNoIGVsZW1lbnQpIG9mIGFsbCB0aGUgZGF0YXNldHMKYGBgCiAgYWxsX2RhdGEgPC0gYWxsX2RhdGFzZXRzICU+JSBtdXRhdGUoZGF0YXNldCA9IHB1cnJyOjptYXAoZmlsZSwgaW1wb3J0KSkKYGBgCiAgCiAgNC4gUnVuIGEgJ2ZvciBsb29wJyB0byBnZXQgUiB0byBhc3NpZ24gZWFjaCB0aWJibGUgdG8gYW4gb2JqZWN0CmBgYAogIGZvcihpIGluIDE6bnJvdyhhbGxfZGF0YSkpewogIGFzc2lnbihhbGxfZGF0YSRmaWxlW1tpXV0sIGFsbF9kYXRhJGRhdGFzZXRbW2ldXSkKICB9CmBgYAogIAojIyMgRnJvbSBtdWx0aXBsZSB3ZWJwYWdlcwoKICAxLiBDcmVhdGUgYSB2ZWN0b3Igd2l0aCB0aGUgVVJMIHBhdGhzIG5lZWRlZCwgZWc6CmBgYAogIHVybF91bmlxdWUgPC0gIyBmdW5jdGlvbiB0byBsaXN0IHRoZXNlLCBlZyB1c2luZyBzZXEoKSBpZiBudW1lcmljYWwKICB1cmxfYmFzZSA8LSAiaHR0cHM6Ly9hYmMiCiAgdXJsX2VuZCA8LSAiL2FiYy5odG1sIgogIHVybF9wYXRoIDwtIHVybF91bmlxdWUgJT4lIHN0cl9jKHVybF9iYXNlLCAuICwgdXJsX2VuZCkKICBkYXRhIDwtIHRpYmJsZSh1cmxfcGF0aCkKYGBgCiAgCiAgMi4gRGVmaW5lIGEgZnVuY3Rpb24gdG8gc2NyYXBlIChhbmQgY2xlYW4pIGRhdGEgZnJvbSBvbmUgdXJsCmBgYAogIHNjcmFwZSA8LSBmdW5jdGlvbih1cmwpewogIHhtbCA8LSByZWFkX2h0bWwodXJsKSAlPiUKICAgICAgICAgIGh0bWxfbm9kZSgidGFibGUiKSAlPiUKICAgICAgICAgIGh0bWxfdGFibGUoZmlsbD1UUlVFKSAlPiUKICB4bWwKYGBgCiAgCiAgMy4gKGlmIG5lZWRlZCkgUHJldmVudCBlcnJvciBtZXNzYWdlcyBmcm9tIFVSTCBwYXRocyB0aGF0IGRvbid0IGV4aXN0CmBgYAogIGV4dHJhY3RfcmVzdWx0cyA8LSBmdW5jdGlvbihkZil7CiAgICBkZiRyZXN1bHQKICB9CmBgYAogIAogIDQuIExvb3Agc2NyYXBlIGZ1bmN0aW9uIG92ZXIgYWxsIFVSTHMgYXMgbmVzdGVkIHRpYmJsZXMKYGBgCiAgZGF0YSA8LSBkYXRhICU+JQogICAgICAgICAgICBtdXRhdGUobmV3X2NvbHVtbiA9IHB1cnJyOjptYXAodXJsX3BhdGgsIHNhZmVseShzY3JhcGUpKSwKICAgICAgICAgICAgbmV3X2NvbHVtbiA9IG1hcChuZXdfY29sdW1uLCBleHRyYWN0X3Jlc3VsdHMpKQpgYGAKCiMjIyBTYXZlIGFzIGNzdgpgYGAKd3JpdGVfY3N2KGRhdGFzZXQsICJmaWxlbmFtZS5jc3YiKQpgYGAKCiMgVGlkeQojIyMgSW5zcGVjdApHZXQgbGlzdCBvZiBhbGwgY29sdW1uIGhlYWRpbmdzIGluIGEgZGF0YXNldApgYGAKY29sbmFtZXMoZGF0YXNldCkKYGBgCgpHZXQgdGhlIG51bWJlciBvZiByb3dzIGFuZCBjb2x1bW5zIGluIGEgZGF0YXNldApgYGAKc3VtbWFyaXNlKCkgCmBgYAoKR2V0IHRoZSBicmVha2Rvd24gb2YgcmVzdWx0cyB3aXRoaW4gYSBjb2x1bW4KYGBgCnRhYmxlKGRhdGFzZXQkY29sdW1uKQpgYGAKCkdldCB0aGUgbnVtYmVyIG9mIHZhbHVlcyBpbiBhIGNvbHVtbgpgYGAKc3VtbWFyeShkYXRhc2V0JGNvbHVtbikKYGBgCgpHZXQgdGhlIG51bWJlciBvZiBOQSB2YWx1ZXMgaW4gYSBjb2x1bW4KYGBgCnN1bShpcy5uYShkYXRhc2V0JGNvbHVtbikpCmBgYAoKIyMjIHRpZHlyCgpXaWRlIC0tPiBza2lubnkKYGBgCmdhdGhlcihkYXRhc2V0LCAibmV3X2NvbHVtbl9uYW1lMSIsICJuZXdfY29sdW1uX25hbWUyIiwgY29sdW1uIHJhbmdlIGVnICcyOjgnKQpgYGAKClNraW5ueSAtLT4gd2lkZQpgYGAKc3ByZWFkKGRhdGFzZXQsICRrZXljb2x1bW4sICR2YWx1ZWNvbHVtbikKYGBgIAoKQ29tYmluZSBkYXRhc2V0cyBieSBhIGNvbW1vbiBjb2x1bW4KYGBgCm1lcmdlKGRhdGFzZXQxLCBkYXRhc2V0MiwgYnkgPSAiY29tbW9uIGNvbHVtbiBuYW1lIikKYGBgIAoKQ29tYmluZSBkYXRhc2V0cyBieSBhdHRhY2hpbmcgcm93cwpgYGAKYXBwZW5kKGRhdGFzZXQxLCBkYXRhc2V0MikKYGBgIAoKQ3JlYXRlIGEgbmV3IGNvbHVtbgpgYGAKbXV0YXRlKG9sZF92YXJpYWJsZSwgbmV3X3ZhcmlhYmxlID0gKGNhbGN1bGF0aW9uKSkKYGBgCgpSZW5hbWUgYSBjb2x1bW4KYGBgCnJlbmFtZShkYXRhc2V0LCBuZXcgbmFtZSA9IG9sZCBuYW1lKQpgYGAKClJlbmFtZSBjYXRlZ29yaWVzIHdpdGhpbiBhIGNvbHVtbgpgYGAKZGF0YSAlPiUgCiAgbXV0YXRlKHZhcmlhYmxlID0gZmN0X3JlY29kZSh2YXJpYWJsZSwgIm5ldyBuYW1lIiA9ICJvbGQgbmFtZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuZXcgbmFtZSA9ICJvbGQgbmFtZSIpKQpgYGAKCkFsdGVybmF0aXZlcyAobW9yZSBmb3IgY29ycmVjdGluZyB0eXBvcyBsaWtlIEZpbmQgJiBSZXBsYWNlKToKICBgYGAKICBkYXRhc2V0JGNvbHVtbltkYXRhc2V0JGNvbHVtbiA9PSAib2xkIl0gPC0gIm5ldyIKICBgYGAKCiAgYGBgCiAgIm5ld19uYW1lIiA8LSBsZW5ndGgoZ3JlcCgiZXhpc3RpbmdfbGFiZWwiLCBkYXRhc2V0JGNvbHVtbikpCiAgYGBgCgpEcm9wIGNvbHVtbihzKSAvIHJvdyhzKQpgYGAgCnN1YnNldChkYXRhc2V0LCBzZWxlY3QgPSAtYyhjb2x1bW5zKSkKc3Vic2V0KGRhdGFzZXQsIGNvbHVtbiAhPSAiIikKYGBgIAoKUmVtb3ZlIE5BcwpgYGAKbmEub21pdChkYXRhc2V0KQpgYGAgCgpBcnJhbmdlIGEgY29sdW1uIGluIGRlc2NlbmRpbmcgb3JkZXIKYGBgCmFycmFuZ2UoZGF0YXNldCwgZGVzYyhjb2x1bW4pKSkKYGBgCgojIyMgc3RyaW5ncgoKKipGaWx0ZXIgYSBjb2x1bW4gaWYgY29udGFpbnMgdGV4dCBzdHJpbmcqKgoKYGBgCmdyZXBsKOKAnHRleHQgc3RyaW5n4oCdLCB2YXJpYWJsZSkKYGBgIAoKKipSZW1vdmUgc3ltYm9scywgZWcgJSBzaWduKioKCmBgYApkYXRhc2V0JGNvbHVtbiA8LSBnc3ViKCJcXCUiLCIiLCBkYXRhc2V0JGNvbHVtbikKYGBgCgoqKkNvbnZlcnQgY2hhcmFjdGVyIGNvbHVtbiB0byBudW1lcmljKioKCmBgYApkYXRhc2V0JGNvbHVtbiA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkYXRhc2V0JGNvbHVtbikpCmBgYAoKKipSb3VuZCBudW1iZXJzKioKCmBgYAptdXRhdGUodmFyaWFibGUgPSByb3VuZCh2YXJpYWJsZSkpCmBgYAoKIyMjIFtqYW5pdG9yXShodHRwczovL2dpdGh1Yi5jb20vc2ZpcmtlL2phbml0b3IpCgoqKkNsZWFuIGNvbHVtbiBoZWFkaW5nIG5hbWVzICh0byBsb3dlcmNhc2Ugd2l0aCB1bmRlcnNjb3JlcyoqCgpgYGAKY2xlYW5fbmFtZXMoKQpgYGAKCioqQ3JlYXRlIGEgcXVpY2sgcGl2b3QgdGFibGUgdG8gc3VtbWFyaXNlIHZhcmlhYmxlcyoqCgpgYGAKdGFieWwodmFyaWFibGUxLCB2YXJpYWJsZTIpICU+JSAKYWRvcm5fKgpgYGAKCioqUmVtb3ZlIGVtcHR5IHJvd3MgYW5kIGNvbHVtbnMqKgoKYGBgCnJlbW92ZV9lbXB0eSgpIApgYGAKCioqRml4IGRhdGVzIHN0b3JlZCBhcyBzZXJpYWwgbnVtYmVycyoqCgpgYGAKZXhjZWxfbnVtZXJpY190b19kYXRlKCkKYGBgCgojIFRyYW5zZm9ybQojIyMgZHBseXIKClNlbGVjdCBjb2x1bW5zCgpgYGAKc2VsZWN0KHZhcmlhYmxlLCB2YXJpYWJsZSwgdmFyaWFibGUpCmBgYAoKU2VsZWN0IHJvd3MgdGhhdCBtZWV0IGNyaXRlcmlhCgpgYGAKZmlsdGVyKHZhcmlhYmxlID09ICIuLi4iIHwgdmFyaWFibGUgPT0gIi4uLiIpCmBgYAoKQW5hbHlzZSAobGlrZSBhIHBpdm90IHRhYmxlKQoKYGBgCmdyb3VwX2J5KGNvbHVtbikgJT4lIAogIHN1bW1hcmlzZShjYWxjdWxhdGlvbikKYGBgCgoqKkNsYXNzaWMgY29kZSBzdHJ1Y3R1cmVzKioKCi0gUGl2b3QgYW5kIHBlcmZvcm0gY2FsY3VsYXRpb25zIG9uIGRhdGEKCmBgYApkYXRhX2FuYWx5c2lzIDwtIHJhd19kYXRhICU+JQogICAgICAgICAgCSAgICAgICAgICAgZmlsdGVyKCksCiAgICAgICAgICAJICAgICAgICAgICBncm91cF9ieSgpLAogICAgICAgICAgCSAgICAgICAgICAgc3VtbWFyaXNlKCkKYGBgCi4uLndoaWNoIHNvdW5kcyBsaWtlOgoiVGhlIG9yaWdpbmFsIGRhdGFmcmFtZSBpcyBYWFgsCglub3cgZmlsdGVyIGRhdGEgdG8gb25seSBpbmNsdWRlIHJvd3MgdGhhdCBzYXRpc2Z5IHRoZSBjb25kaXRpb25zIFlZWSwKCW5vdyBncm91cCB0aGUgZGF0YSBhdCBlYWNoIGxldmVsIG9mIHRoZSB2YXJpYWJsZShzKSBaWlosCglub3cgc3VtbWFyaXplIHRoZSBkYXRhIGFuZCBjYWxjdWxhdGUgc3VtbWFyeSBmdW5jdGlvbnMgWFhY4oCmIgoKLSBQaXZvdCBkYXRhIHRvIHNob3cgcGVyY2VudGFnZSBicmVha2Rvd24gb2YgZWFjaCBjb2x1bW4KCmBgYApkYXRhX2FuYWx5c2lzIDwtIHJhd19kYXRhICU+JQogICAgICAgICAgICAgICAgICAgICAgIGdyb3VwXyBieShjb2x1bW4gMSwgY29sdW1uIDIpICU+JQogICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShuID0gbigpKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUocGN0ID0gbiAvIHN1bShuKSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgc3ByZWFkKGNvbHVtbiAyLCBwY3QpCmBgYAoKKipDYWxjdWxhdGlvbnMqKgoKYGBgCnN1bSgpCmNvdW50KCkKdW5pcXVlKCkKbl9kaXN0aW5jdCgpCmBgYAoKQ291bnQgdW5pcXVlIHZhbHVlcyBpbiBhIGNvbHVtbiBhbmQgYXJyYW5nZSBpbiBkZXNjZW5kaW5nIG9yZGVyIHdpdGgKYGBgCmRhdGEgJT4lIGNvdW50KGNvbHVtbikgJT4lIGFycmFuZ2UoZGVzYyhuKSkKYGBgCgojIyMgYXBwbHkKClBpdm90IG9uZSBjb2x1bW4gb2YgZGF0YSAodXNlIDIgZm9yIHJvd3MpIHdpdGggCmBgYApzYXBwbHkoZGF0YXNldCwgZnVuY3Rpb24sIDEpCmBgYAoKR2V0IHRoZSBudW1iZXIgb2YgdW5pcXVlIHZhbHVlcyBpbiBldmVyeSBjb2x1bW4KYGBgCnJhcHBseShkYXRhLGZ1bmN0aW9uKHgpbGVuZ3RoKHVuaXF1ZSh4KSkpCmBgYAoKIyMjIHN0cmluZ3IKCkpPSU4gd2l0aCBgc3RyX2MoKWAKCi0gYHNlcCA9YCBob3cgeW91IGNvbWJpbmUgdGhlIGNvbHVtbnMsIGVnIHNlcCA9ICIsIiBmb3IgY29tbWEgc2VwYXJhdGVkIHZhbHVlcwoKLSBgY29sbGFwc2UgPWAgaG93IHlvdSBjb21iaW5lIHRoZSByb3dzLCBlZyBjb2xsYXBzZSA9ICItIiwgdG8gYWRkIGEgaHlwaGVuIGJldHdlZW4gZWFjaCB2ZWN0b3IKClNQTElUIHdpdGggYHN0cl9zcGxpdCgpYAoKR0VUIE1BVENIRVMKCi0gYHN0cl92aWV3KClgIHRvIHNlZSByZXN1bHRzIGluIFZpZXdlciB3aW5kb3cKCi0gYHN0cl9kZXRlY3QoKWAgdG8gZ2V0IFRSVUUvRkFMU0UgcmVzcG9uc2UKCi0gYHN0cl9jb3VudCgpYCB0byBnZXQgY291bnRlZCByZXNwb25zZQoKR0VUIE1BVENIRVMgV0hFTiBXT1JLSU5HIFdJVEggQSBUQUJMRSB3aXRoIGB0aWR5cjo6ZXh0cmFjdChvbGRfY29sdW1uLCAibmV3X2NvbHVtbiIsICJyZWd1bGFyIGV4cHJlc3Npb24iKWAKCkdFVCBMT0NBVElPTiB3aXRoIGBzdHJfbG9jYXRlKClgCgpTVUJTRVQgd2l0aCBgc3RyX3N1YigpYAoKQ09VTlQgTEVOR1RIIHdpdGggYHN0cl9sZW5ndGgoKWAKCldSQVAgd2l0aCBgc3RyX3dyYXBgCgotIGB3aWR0aCA9YCBsaW5lIHdpZHRoIGluIGNoYXJhY3RlcnMKCi0gYGluZGVudCA9YCBpbmRlbnRhdGlvbiBvZiBmaXJzdCBsaW5lIGluIGVhY2ggcGFyYWdyYXBoCgotIGBleGRlbnQgPWAgaW5kZW50YXRpb24gb2YgZm9sbG93aW5nIGxpbmVzIGluIGVhY2ggcGFyYWdyYXBoCgojIyMgcmVnZXgKYWthICJyZWd1bGFyIGV4cHJlc3Npb25zIgoKCS0gYC5gID0gYW55IGNoYXJhY3RlciAod2lsZGNhcmQpCgkKCS0gYF5gID0gc3RhcnQgb2YgdGhlIHN0cmluZwoJCgktIGAkYCA9IGVuZCBvZiB0aGUgc3RyaW5nCgkKCS0gYG1hdGNoID0gVFJVRWAgPSB2aWV3IGp1c3QgdGhvc2UgbWF0Y2hlcyB0aGF0IG1lZXQgY3JpdGVyaWEKCQoJLSBgXGQ6YCBhbnkgZGlnaXQKCQoJLSBgXHM6YCBhbnkgd2hpdGVzcGFjZSAoc3BhY2UsIHRhYiwgbmV3bGluZSkKCQoJLSBgW2FiY106YCBtYXRjaGVzIGEsIGIsIG9yIGMKCQoJLSBgW15hYmNdOmAgbWF0Y2hlcyBhbnl0aGluZyBleGNlcHQgYSwgYiwgb3IgYwoKIyBWaXN1YWxpc2Ug8J+kqSAKCiMjIyBCYXNpY3MKCkNsYXNzaWMgY29kZSBzdHJ1Y3R1cmUKYGBgCmdncGxvdChkYXRhc2V0KSArCgkgdml6X2Z1bmN0aW9uKGFlcyh4ID0gdmFyaWFibGUsCgkJCQkgICAgICAgICAgICB5ID0gdmFyaWFibGUsCgkJCQkgICAgICAgICAgICBncm91cCA9IHZhcmlhYmxlLAogICAgICAgICAgICAgICAgICAgIHNpemUgPSB2YXJpYWJsZSwKICAgICAgICAgICAgICAgICAgICBmaWxsID0gdmFyaWFibGUpICsKICAgc2NhbGVfeC95X2Rpc2NyZXRlL2NvbnRpbnVvdXMoYnJlYWtzID0gYygpLCAKICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoKSwgCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCkpICsKICAgbGFicyh0aXRsZSA9ICIuLi4iLAogICAgICAgIHN1YnRpdGxlID0gIi4uLiIsCiAgICAgICAgeCA9ICIuLi4iLAogICAgICAgIHkgPSAiLi4uIiwKICAgICAgICBjYXB0aW9uID0gIi4uLiIpICsKICAgdGhlbWUoKSArCiAgIGdnc2F2ZSgibmFtZS5maWxldHlwZSIsIGhlaWdodCA9ICwgd2lkdGggPSAsIHVuaXQgPSAiIikKYGBgCgpBZGRpdGlvbmFsIGxheWVycwpgYGAKZ2VvbV9obGluZSgpIApnZW9tX3ZsaW5lKCkKZ2VvbV9zZWdtZW50KCkKZ2VvbV90ZXh0KCkKZ2VvbV9yZWN0KCkKYGBgCgpUcmFuc2Zvcm0gZGF0YSB3aXRoaW4gYSBnZ3Bsb3QgZnVuY3Rpb24KYGBgCnBsb3QxIDwtIGdncGxvdChmaWx0ZXIoZGF0YXNldCwgdmFyaWFibGUgPT0gY3JpdGVyaWEpKQpgYGAKCiMjIyBMZWdlbmRzCgpSZW9yZGVyCgogIDEuIEZpcnN0IGNyZWF0ZSBhIGZhY3RvcjoKICBgYGAKICBkYXRhc2V0JG5ld2NvbHVtbiA8LSBmYWN0b3IoZGF0YXNldCRjb2x1bW4sICBsZXZlbHMgPSBjKOKAnGl0ZW0x4oCdLCDigJxpdGVtMuKAnSkgLCBvcmRlcmVkID0gVFJVRSkKICBgYGAKICAKICAyLiBUaGVuIGluIGdncGxvdCgpIGFlc3RoZXRpYyBtYXBwaW5ncywgdXNlIHRoaXMgbmV3IGNvbHVtbiwgcmF0aGVyIHRoYW4gdGhlIHByZS1leGlzdGluZyBvbmUuCgpSZW1vdmUKYGBgCnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgIAoKIyMjIEF4ZXMKCkFycmFuZ2UgaW4gZGVzY2VuZGluZyBvcmRlcgpgYGAKZ2dwbG90KGFlcyh4ID0gcmVvcmRlcih2YXJpYWJsZTEsIC12YXJpYWJsZTIpLCB5ID0gdmFyaWFibGUyKQpgYGAKCiMjIyBUZXh0CgpSZW1vdmUgLyBzdHlsZSBheGVzIGxhYmVscwpgYGAKYXhpcy50aXRsZS54L3kgPSBlbGVtZW50X2JsYW5rKCkgb3IgZWxlbWVudF90ZXh0KHNpemUgPSAsIGZhbWlseSA9ICwgZm9udCA9ICkKYXhpcy50ZXh0LngveSA9IGVsZW1lbnRfYmxhbmsoKSBvciBlbGVtZW50X3RleHQoc2l6ZSA9ICwgZmFtaWx5ID0gLCBmb250ID0gKQpgYGAKCldyYXAgbG9uZyBsYWJlbHMKYGBgCnNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyX3dyYXAoeCwgd2lkdGggPSAxMCkpCmBgYAoKUm90YXRlIGxhYmVscyA0NSBkZWdyZWVzCmBgYAp0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUpKQpgYGAgCgojIyMgVGhlbWVzCgpGcm9tIGdncGxvdAoKLSBgdGhlbWVfZ3JheSgpYCDigJMgc2lnbmF0dXJlIGdncGxvdDIgdGhlbWUKCi0gYHRoZW1lX2J3KClgIOKAkyBkYXJrIG9uIGxpZ2h0IGdncGxvdDIgdGhlbWUKCi0gYHRoZW1lX2xpbmVkcmF3KClgIOKAkyB1c2VzIGJsYWNrIGxpbmVzIG9uIHdoaXRlIGJhY2tncm91bmRzIG9ubHkKCi0gYHRoZW1lX2xpZ2h0KClgIOKAkyBzaW1pbGFyIHRvIGxpbmVkcmF3KCkgYnV0IHdpdGggZ3JleSBsaW5lcyBhc3dlbGwKCi0gYHRoZW1lX2RhcmsoKWAg4oCTIGxpbmVzIG9uIGEgZGFyayBiYWNrZ3JvdW5kIGluc3RlYWQgb2YgbGlnaHQKCi0gYHRoZW1lX21pbmltYWwoKWAg4oCTIG5vIGJhY2tncm91bmQgYW5ub3RhdGlvbnMsIG1pbmltYWwgZmVlbC4KCi0gYHRoZW1lX2NsYXNzaWMoKWAg4oCTIHRoZW1lIHdpdGggbm8gZ3JpZCBsaW5lcwoKLSBgdGhlbWVfdm9pZCgpYCDigJMgZW1wdHkgdGhlbWUgd2l0aCBubyBlbGVtZW50cwoKRnJvbSBnZ3RoZW1lcwoKLSBgdGhlbWVfc29saWQoKWAgLSBUaGVtZSB3aXRoIG5vdGhpbmcgb3RoZXIgdGhhbiBhIGJhY2tncm91bmQgY29sb3IKCi0gYHRoZW1lX21hcCgpYCAtIENsZWFuIHRoZW1lIGZvciBtYXBzCgotIGB0aGVtZV9pZ3JheSgpYCAtICBJbnZlcnNlIGdyYXkgdGhlbWUKCi0gYHRoZW1lX2Vjb25vbWlzdCgpYAoKLSBgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KClgCgotIGB0aGVtZV93c2ooKWAgCgotIGB0aGVtZV9mZXcoKWAKCi0gYHRoZW1lX3R1ZnRlKClgCgotIGB0aGVtZV9leGNlbCgpYAoKLSBgdGhlbWVfZ2RvY3MoKWAKCi0gYHRoZW1lX3N0YXRhKClgIAoKLSBgdGhlbWVfc29sYXJpemVkKClgCgotIGB0aGVtZV9oYygpYCAtIEhpZ2hjaGFydHMgSlMgdGhlbWUKCiMjIyBJbnRlcmFjdGl2ZSBnZ3Bsb3RzCgoxLiAqKmlyYXBoKioKICBgYGAKICBsaWJyYXJ5KGlyYXBoKQogIGdncGxvdChkYXRhLCBhZXMoeCA9ICwgeSA9ICwgdG9vbHRpcCA9IHZhcmlhYmxlKSArCiAgICAgIGdlb21fPz8/X2ludGVyYWN0aXZlKCkKICBgYGAKCjIuICoqcGxvdGx5KioKICBgYGAKICBsaWJyYXJ5KHBsb3RseSkKICBnZ3Bsb3RseShwbG90LCB0b29sdGlwID0gdmFyaWFibGUpCiAgYGBgCgozLiAqKltoaWdoY2hhcnRlcl0oaHR0cDovL2prdW5zdC5jb20vaGlnaGNoYXJ0ZXIvaW5kZXguaHRtbCkqKgoKNC4gKipbaHRtbHdpZGdldHNdKGh0dHA6Ly93d3cuaHRtbHdpZGdldHMub3JnLy5odG1sKSoqCgo1LiAqKlJTaGlueSoqCgogIC0gVG8gbWFrZSBhIHNoaW55IGFwcCwgeW91IG5lZWQgdG8gY3JlYXRlIHNjcmlwdCB3aXRoIGEgZmlsZW5hbWUgZW5kaW5nICJhcHAuUiIKICAKICAtIFRoZSBiYXNpYyBzdHJ1Y3R1cmUgb2YgYW4gUiBTaGlueSBhcHA6CiAgCiAgYGBgCiAgbGlicmFyeShzaGlueSkKICB1aSA8LSBmbHVpZFBhZ2UoKQogIGlucHV0KCkgZnVuY3Rpb25zLCBlZyBzbGlkZXJJbnB1dCgpCiAgb3V0cHV0KCkgZnVuY3Rpb25zLCBlZyBwbG90T3V0cHV0KCkKICAKICBzZXJ2ZXIgPC0gZnVuY3Rpb24oaW5wdXQsIG91dHB1dCkgeyB9CiAgb3V0cHV0JHh4eHggPC0gcmVuZGVyKigpLCBlZyByZW5kZXJQbG90KCkKICAKICBzaGlueUFwcCh1aSA9IHVpLCBzZXJ2ZXIgPSBzZXJ2ZXIpCiAgYGBgCgojIyMgR2VvZ3JhcGhpY2FsIG1hcHMKCkludHJvZHVjdGlvbgoKVGhlcmUgc2VlbXMgdG8gYmUgbG90cyBvZiBkaWZmZXJlbnQgd2F5cyB5b3UgY2FuIG1ha2UgbWFwcyBpbiBSOiBwYXJ0bHkgYmVjYXVzZSB0aGUgYmVzdCBhcHByb2FjaCB3aWxsIGRlcGVuZCBvbiB3aGF0IHR5cGUgb2YgbWFwIHlvdSdyZSBtYWtpbmc7IGFuZCBwYXJ0bHkganVzdCBiZWNhdXNlIHRoZXJlJ3MgbG90cyBvZiBjb21wZXRpbmcgcGFja2FnZXMgYXZhaWxhYmxlLgoKLSBXYXlzIG9mIGdldHRpbmcgbWFwIGRhdGEKCiAgICAxLiBWZWN0b3IgaW1hZ2UgbWFwcywgZWcgc2YgcGFja2FnZSBvciBtYXBzIHBhY2thZ2UgYW5kIG1hcF9kYXRhKCkgZnVuY3Rpb24KCiAgICAyLiBSYXN0ZXIgaW1hZ2UgbWFwcywgZWcgcmFzdGVyIHBhY2thZ2UKCi0gUGFja2FnZXMgZm9yIHBsb3R0aW5nIG1hcCBkYXRhCgogICAgMS4gZ2dwbG90LCBlZyBnZW9tX3BvbHlnb24oKSBhbmQgZ2VvbV9zZigpCgogICAgMi4gdG1hcCBhbmQgdG1hcHRvb2xzIChkdW5ubykKCiAgICAzLiBsZWFmbGV0IChkdW5ubykKCioqQSBub3RlIG9uIHNoYXBlZmlsZXMqKgoKLSBSYXN0ZXIKCiAgLSBzdGF0aWMgaW1hZ2UgZmlsZXMgZ2VuZXJhdGVkIHByZXZpb3VzbHkgYnkgdGhlIG1hcHBpbmcgc2VydmljZSwgd2hpY2ggbGltaXRzIHlvdXIgYWJpbGl0eSB0byByZWRyYXcgb3IgY2hhbmdlIHRoZSBhcHBlYXJhbmNlIG9mIHRoZSBnZW9ncmFwaGljIG1hcAoKICAtIHRyYWRlb2ZmIG1lYW5zIHlvdSBjYW4gaW1tZWRpYXRlbHkgZm9jdXMgb24gaW5jb3Jwb3JhdGluZyBhZGRpdGlvbmFsIGRhdGEgaW50byB0aGUgbWFwOyBiZXR0ZXIgZm9yIGdyaWRkZWQgZGF0YSwgbGlrZSBzYXRlbGxpdGUgaW1hZ2VyeQoKLSBWZWN0b3IgCgogIC0gc3BhdGlhbCBkYXRhIGZpbGVzIHdoaWNoIGNvbnRhaW4gZGV0YWlsZWQgaW5mb3JtYXRpb24gbmVjZXNzYXJ5IHRvIGRyYXcgYWxsIHRoZSBjb21wb25lbnRzIG9mIGEgbWFwIChlLmcuIHBvaW50cywgbGluZXMsIHBvbHlnb25zKQoKICAtIHJlcXVpcmVzIGltcG9ydGluZyB0aGlzIGRhdGEgaW50byBSOyBvbmNlIGltcG9ydGVkIHRob3VnaCwgZ2dwbG90MiB3b3JrcyB3aXRoIHNpbXBsZSBmZWF0dXJlcyAoc2YpIGRhdGEgZnJhbWVzIHRvIGVhc2lseSBnZW5lcmF0ZSBnZW9zcGF0aWFsIHZpc3VhbGl6YXRpb25zIHVzaW5nIGFsbCB0aGUgY29yZSBlbGVtZW50cyBhbmQgYXBwcm9hY2hlcyBvZiBnZ3Bsb3QoKQoKLSBXaGVyZSB0byBnZXQgc2hhcGVmaWxlczogaHR0cDovL3d3dy5uYXR1cmFsZWFydGhkYXRhLmNvbS9kb3dubG9hZHMvCgpFeGFtcGxlcwoKMS4gQ3JlYXRlIGEgYmFzaWMgbWFwIG9mIHRoZSB3b3JsZCAobWludXMgQW50YXJjdGljYSkKYGBge3Igd29ybGQgbWFwfQpsaWJyYXJ5KG1hcHMpCmxpYnJhcnkoZ2dwbG90MikKd29ybGRfbWFwIDwtIG1hcF9kYXRhKCJ3b3JsZCIpICU+JSAKICBmaWx0ZXIocmVnaW9uICE9ICJBbnRhcmN0aWNhIikgJT4lIAogIGdncGxvdChhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApKSArIAogIGdlb21fcG9seWdvbihmaWxsID0gImxpZ2h0IGJsdWUiKSArIAogIGNvb3JkX2ZpeGVkKCkgKwogIHRoZW1lX3ZvaWQoKQp3b3JsZF9tYXAKYGBgCgoyLiBDcmVhdGUgYSBiYXNpYyBtYXAgb2YgYSBjb3VudHJ5LCBlZyBVSwpgYGB7ciBjb3VudHJ5IG1hcH0KbGlicmFyeShtYXBzKQpsaWJyYXJ5KGdncGxvdDIpClVLX21hcCA8LSBtYXBfZGF0YSgid29ybGQiLCByZWdpb24gPSAiVUsiKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCkpICsgCiAgZ2VvbV9wb2x5Z29uKGZpbGwgPSAiZGFyayBibHVlIikgKyAKICBjb29yZF9maXhlZCgpICsKICB0aGVtZV92b2lkKCkKVUtfbWFwCmBgYAoKMy4gQ3JlYXRlIGEgZGV0YWlsZWQgbWFwIG9mIGEgY291bnRyeSwgZWcgY291bnRpZXMgd2l0aGluIFVLIApVc2luZyBzZiBhbmQgZ2dwbG90IHBhY2thZ2VzIGFzIHBlciB0aGUgZm9sbG93aW5nIHR1dG9yaWFsczoKCi0gaHR0cHM6Ly93d3cuci1zcGF0aWFsLm9yZy9yLzIwMTgvMTAvMjUvZ2dwbG90Mi1zZi5odG1sCgotIGh0dHBzOi8vY2Zzcy51Y2hpY2Fnby5lZHUvZ2Vvdml6X3Bsb3QuaHRtbApgYGB7ciBzaGFwZWZpbGUgbWFwfQpsaWJyYXJ5KG1hcHMpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHNmKQoKVUtfbWFwIDwtIG1hcF9kYXRhKCJ3b3JsZCIsIHJlZ2lvbiA9ICJVSyIpICU+JQogIAogIGdncGxvdChhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApKSArIAogIGdlb21fcG9seWdvbihmaWxsID0gImRhcmsgYmx1ZSIpICsgCiAgY29vcmRfZml4ZWQoKSArCiAgdGhlbWVfdm9pZCgpClVLX21hcApgYGAKCiMjIyBUaWxlIGdyaWQgbWFwcwoKIyMjIyBbZ2VvZmFjZXRdKGh0dHBzOi8vaGFmZW4uZ2l0aHViLmlvL2dlb2ZhY2V0LykKClBhY2thZ2UgZm9yIGNyZWF0aW5nIHRpbGUgZ3JpZCBtYXBzIGZvciByZWdpb25zIGluY2x1ZGluZzoKCi0gYGxvbmRvbl9ib3JvdWdoc19ncmlkYAoKLSBgdWtfcmVnaW9uczFgIAoKLSBgd29ybGRfODZjb3VudHJpZXNfZ3JpZGAgJiBgd29ybGRfY291bnRyaWVzX2dyaWQxYAoKLSBgY2hpbmFfcHJvdl9ncmlkMWAKClJlbGF0ZWQ6IFtjb3VudHJ5Y29kZV0oaHR0cHM6Ly9naXRodWIuY29tL3ZpbmNlbnRhcmVsYnVuZG9jay9jb3VudHJ5Y29kZSkgcGFja2FnZQoKIyBNb2RlbAoKKioiQWxsIG1vZGVscyBhcmUgd3JvbmcsIGJ1dCBzb21lIGFyZSB1c2VmdWwiKioKClRoZSBnb2FsIG9mIGEgbW9kZWwgaXMgbm90IHRvIHVuY292ZXIgdHJ1dGgsIGJ1dCB0byBkaXNjb3ZlciBhIHNpbXBsZSBhcHByb3hpbWF0aW9uIHRoYXQgaXMgdXNlZnVsIChpbiBvcmRlciB0byB1bmRlcnN0YW5kIHRoZSBiZWhhdmlvdXIgb2Ygc29tZXRoaW5nKS4KCiogbCoqaW5lYXIgKiptb2RlbCB0YWtlcyB0aGUgZ2VuZXJhbCBmb3JtOgoKYGBgCnkgPSBhXzAgKyBhXzEgKiB4CmBgYAoKd2hlcmUgCgotIGBhXzBgID0gc3RhcnRpbmcgcG9pbnQKCi0gYGFfMWAgPSBkaWZmZXJlbmNlIGJldHdlZW4geCBhbmQgeSBhdCBhIGdpdmVuIHBvaW50CgooTkIgdGhpcyBpcyBhbm90aGVyIHdheSBvZiBleHByZXNzaW5nIGB5ID0gbXggKyBjYCkKCkxpbmVhciBtb2RlbHMgYWxzbyBhc3N1bWUgdGhhdCB0aGUgJ3Jlc2lkdWFscycgKGRpZmZlcmVuY2UgYmV0d2VlbiBvYnNlcnZlZCBhbmQgcHJlZGljdGVkIHZhbHVlcykgaGF2ZSBhIG5vcm1hbCBkaXN0cmlidXRpb24uCgojIyMgbG0KCm9yLCBob3cgdG8gYXBwbHkgYSBsaW5lYXIgbW9kZWwgdG8gZGF0YQoKMS4gQ3JlYXRlIGEgbW9kZWwgKGEgdHlwZSBvZiBsaXN0KSB3aXRoIHRoZSBmdW5jdGlvbiBgbG0oKWAsIHdoZXJlIGB4X3ZhcmlhYmxlYCBpcyB0aGUgaW5kZXBlbmRlbnQgJ2NhdXNlJyBhbmQgYHlfdmFyaWFibGVgIGlzIHRoZSBkZXBlbmRlbnQgJ2NvbnNlcXVlbmNlJzoKCmBgYAptb2RlbF9uYW1lIDwtIGxtKHlfdmFyaWFibGUgfiB4X3ZhcmlhYmxlLCBkYXRhID0gZGF0c2V0KQpgYGAKCjIuIENyZWF0ZSBhICdncmlkJyBvZiB5b3VyIGRhdGEgKGV2ZW5seSBzcGFjZWQgZ3JpZCBvZiBwb2ludHMgZnJvbSB0aGUgZGF0YSkKCmBgYApncmlkX25hbWUgPC0gZGF0YSAlPiUgZGF0YV9ncmlkKHhfdmFyaWFibGUpCmBgYAoKMy4gQWRkIHByZWRpY3Rpb25zIHRvIHlvdXIgZ3JpZGRlZCBkYXRhIHVzaW5nIHRoZSBtb2RlbCB5b3UgY3JlYXRlZAoKYGBgCmdyaWRfbmFtZSA8LSBncmlkX25hbWUgJT4lIGFkZF9wcmVpZGljdGlvbnMobW9kZWxfbmFtZSkKYGBgCgo0LiBUbyB2aXN1YWxpc2UgdGhlIGxpbmVhciBtb2RlbCwgZG8gdGhlIHVzdWFsIGdncGxvdCBvZiB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBgZGF0YXNldGAsIHRoZW4gYWRkIGEgYGdlb21fbGluZWAgbGF5ZXIgdXNpbmcgdGhlIGBncmlkX25hbWVgIGFzIGl0cyBkYXRhLCBlZwoKYGBgCmdncGxvdChkYXRhc2V0LCBhZXMoeF92YXJpYWJsZSwgeV92YXJpYWJsZSkpICsKICBnZW9tX2hleChiaW5zID0gIyMjKSArCiAgZ2VvbV9saW5lKGdyaWRfbmFtZSkKYGBgCgojIyMgZ2VvbV9zbW9vdGgKCm9yLCBtb2RlbGxpbmcgd2l0aCBnZ3Bsb3QgKG5vdGVzIHRha2VuIGZyb20gW0RhdGEgdmlzdWFsaXphdGlvbjogYSBwcmFjdGljYWwgaW50cm9kdWN0aW9uXShodHRwczovL3NvY3Zpei5jby9tb2RlbGluZy5odG1sKSkuCgpCYXNpYyBsaW5lYXIgbW9kZWw6CmBgYApnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKQpgYGAKClNwZWNpZnkgbGluZWFyIG1vZGVsOgpgYGAKZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9IHlfdmFyaWFibGUgfiB4X3ZhcmlhYmxlKQpgYGAKCiMgSXRlcmF0ZQoKVXNpbmcgdGhlIGJvb2sgJ1IgZm9yIERhdGEgU2NpZW5jZScgYW5kIHRoZSB0aWR5dmVyc2UgcGFja2FnZQoKIyMjIEZ1bmN0aW9ucwoKLSBGdW5jdGlvbnMgYWxsb3cgeW91IHRvIGF1dG9tYXRlIHRhc2tzLCBhcyBvcHBvc2VkIHRvIGNvcHktYW5kLXBhc3Rpbmcgc2ltaWxhciBiaXRzIG9mIGNvZGUKCi0gVGhlcmUgYXJlIGh1Z2UgYWR2YW50YWdlcyBpbiB1c2luZyBhIGZ1bmN0aW9uIHJhdGhlciB0aGFuIGNvcHktYW5kLXBhc3RlOiBpZiB5b3Ugd2FudCB0byBtYWtlIGEgY2hhbmdlLCB5b3Ugb25seSBuZWVkIHRvIGRvIGl0IG9uY2U7IGFuZCB5b3UncmUgbGVzcyBsaWtlbHkgdG8gbWFrZSB0eXBvcyBhbmQgbWlzdGFrZXMuCgotIFVzZSBiYXNlIFIgdG8gd3JpdGUgZnVuY3Rpb25zCgpUaGVyZSBhcmUgKiozIGJhc2ljIGJ1aWxkaW5nIGJsb2NrcyoqIGZvciBjcmVhdGluZyBhIGZ1bmN0aW9uLgoKMS4gQSBuYW1lCgoyLiBUaGUgaW5wdXRzIChvciBhcmd1bWVudHMpCgozLiBUaGUgJ2JvZHknIG9mIHRoZSBmdW5jdGlvbiwgaWUgY29kZSB0aGF0IGdvZXMgYmV0d2VlbiB7fQoKLi4ud2hpY2ggY29tZXMgdG9nZXRoZXIgYXM6CgpgYGBmdW5jdGlvbl9uYW1lIDwtIGZ1bmN0aW9uKGFyZ3VtZW50cyl7IGZ1bmN0aW9uIGNvZGUgfWBgYAoKV2hhdCB5b3UgZ2V0IGJhY2sgZnJvbSB0aGUgZnVuY3Rpb24gd2lsbCBieSBkZWZhdWx0IGJlIHRoZSBsYXN0IHN0YXRlbWVudCB0aGUgZnVuY3Rpb24gZXZhbHVhdGVzLiAKCkJVVCB5b3UgY2FuIGFsc28gY2hvb3NlIHRvIHVzZToKCi0gKipgcmV0dXJuKClgKiogaW4gdGhlIG1pZGRsZSBvZiB0aGUgZnVuY3Rpb24gY29kZSB0byBzaWduYWwgdG8gbWFrZSBpdCBlYXNpZXIgdG8gdW5kZXJzdGFuZCwgZWcgaWYgeW91J3ZlIHdyaXR0ZW4gdmVyeSBsb25nIGBpZmAgc3RhdGVtZW50cy4KCi0gKipgaW52aXNpYmxlKClgKiogdG8gZW5zdXJlIHRoYXQgc29tZXRoaW5nIGRvZXNuJ3QgZ2V0IHByaW50ZWQgb3V0IGFzIGEgcmVzdWx0IG9mIHRoZSBmdW5jdGlvbiwgZWcgYSBuZXcgZGF0YWZyYW1lIHRoYXQgeW91J3JlIHVzaW5nIHRvIGNyZWF0ZSBhIHBsb3QKCioqRXhhbXBsZXMqKgoKSWYgd2Ugd2VyZSB0byBjcmVhdGUgYSBmdW5jdGlvbiB0aGF0IGFkZHMgMiB0byB0aGUgaW5wdXQsIGl0IHdvdWxkIGxvb2sgbGlrZSB0aGlzOgpgYGB7cn0KYWRkX3R3byA8LSBmdW5jdGlvbih4KXsKICB4KzIKfQpgYGAKCmFuZCB5b3UgY2FuIGFwcGx5IGl0IGxpa2UgdGhpcwpgYGB7cn0KYWRkX3R3bygyKQpgYGAKCnlvdSBjYW4gYWxzbyBhcHBseSBpdCB0byBldmVyeSBlbGVtZW50IG9mIGEgdmVjdG9yCmBgYHtyfQpiIDwtIGMoMTo1KQphZGRfdHdvKGIpCmBgYAoKSGVyZSdzIGFub3RoZXIgZXhhbXBsZS4gV2UgY2FuIGNyZWF0ZSBhIGBmaXp6YnV6emAgZnVuY3Rpb24gdGhhdCB0YWtlcyBhIHNpbmdsZSBudW1iZXIgYXMgaW5wdXQgYW5kIHJldHVybnMgImZpenoiIGlmIGl0J3MgZGl2aXNpYmxlIGJ5IHRocmVlIGFuZCAiYnV6eiIgaWYgaXQncyBkaXZpc2libGUgYnkgNSBhbmQgImZpenpidXp6IiBpZiBpdCdzIGRpdmlzaWJsZSBieSAzIGFuZCA1LgpgYGB7cn0KZml6emJ1enogPC0gZnVuY3Rpb24oeCkgewogIAogICMgc3RvcGlmbm90KCkgZnVuY3Rpb24gZW5zdXJlcyB0aGUgdHJ1dGggb2YgYW4gciBleHByZXNzaW9uCiAgc3RvcGlmbm90KGxlbmd0aCh4KSA9PSAxKQogIHN0b3BpZm5vdChpcy5udW1lcmljKHgpKQoKICAjICUlIGFuZCAmJiBhcmUgbG9naWNhbCBvcGVyYXRvcnMsIHdoZXJlICUlIGlzICdyZW1haW5kZXIgZnJvbSBkaXZpc2lvbicgYW5kICYmIGlzICdhbmQnICAKICBpZiAoISh4ICUlIDMpICYmICEoeCAlJSA1KSkgewogICAgImZpenpidXp6IgogIH0gZWxzZSBpZiAoISh4ICUlIDMpKSB7CiAgICAiZml6eiIKICB9IGVsc2UgaWYgKCEoeCAlJSA1KSkgewogICAgImJ1enoiCiAgfSBlbHNlIHsKICAgIHgKICB9Cn0KCmZpenpidXp6KDM3OCkKYGBgCgpJdCdzIHVzZWZ1bCB0byBoYXZlIHNvbWUgKipuYW1pbmcgY29udmVudGlvbnMqKiBmb3IgeW91ciBmdW5jdGlvbiBhcmd1bWVudHMuIFRoZSBmb2xsb3dpbmcgYXJlIHJlY29tbWVuZGVkOgoKLSBgeGAsIGB5YCBhbmQgYHpgIGZvciB2ZWN0b3JzCgotIGBkZmAgZm9yIGRhdGEgZnJhbWVzCgotIGBpYCBmb3IgdGhlIHJvdyBudW1iZXIgKGluZGljZXMpCgotIGBqYCBmb3IgdGhlIGNvbHVtbiBudW1iZXIgKGluZGljZXMpCgotIGBuYCBmb3IgdGhlIG51bWJlciBvZiByb3dzCgotIGBwYCBmb3IgdGhlIG51bWVyIG9mIGNvbHVtbnMKCkl0J3MgYWxzbyB1c2VmdWwgdG8gYWRkIGEgZ2VuZXJpYyBlcnJvciBtZXNzYWdlIHRvIHlvdXIgZnVuY3Rpb25zIHdpdGggKipgc3RvcGlmbm90KClgKiosIHdoaWNoIHlvdSBjYW4gdXNlIHRvIGFzc2VydCB3aGF0IFNIT1VMRCBiZSB0cnVlIChyYXRoZXIgdGhhbiBjaGVja2luZyBmb3Igd2hhdCBtaWdodCBiZSB3cm9uZykuIEZvciBleGFtcGxlOgoKYGBgCmZ1bmN0aW9uX25hbWUgPC0gZnVuY3Rpb24oeCwgeSwgbmEucm0gPSBGQUxTRSkgewogIHN0b3BpZm5vdChpcy5sb2dpY2FsKG5hLnJtKSwgbGVuZ3RoKG5hLnJtKSA9PSAxKQogIHN0b3BpZm5vdChsZW5ndGgoeCkgPT0gbGVuZ3RoKHkpKQogIAogIGZ1bmN0aW9uIGNvZGUKfQpgYGAKCiMjIyBGb3IgbG9vcHMKCi0gRnVuY3Rpb25zIGhlbHAgdG8gcmVkdWNlIGR1cGxpY2F0aW9uIG9mIHJlcGVhdGVkIHBhdHRlcm5zIG9mIGNvZGUKCi0gSXRlcmF0aW9uIGhlbHBzIHRvIHJlZHVjZSBkdXBsaWNhdGlvbiBvZiByZXBlYXRlZCBvcGVyYXRpb25zIG9uIG11bHRpcGxlIGlucHV0cywgZWcgcGVyZm9ybWluZyB0aGUgc2FtZSBvcGVyYXRpb24gb24gZGlmZmVyZW50IGNvbHVtbnMgb3IgZGlmZmVyZW50IGRhdGFzZXRzCgotIFRoZXJlIGFyZSAyIHR5cGVzIG9mIGl0ZXJhdGlvbjoKCiAgMS4gKipJbXBlcmF0aXZlIHByb2dyYW1taW5nKiosIGVnIGZvciBsb29wcyBhbmQgd2hpbGUgbG9vcHMKICAKICAyLiAqKkZ1bmN0aW9uYWwgcHJvZ3JhbW1pbmcqKiBtZWFucyBldmVuIGxlc3MgZHVwbGljYXRpb24gdGhhbiB5b3UgZ2V0IGluIGltcGVyYXRpdmUgcHJvZ3JhbW1pbmcKCioqRm9yIGxvb3BzOiB0aGUgYmFzaWNzKioKCi0gRXZlcnkgZm9yIGxvb3AgaGFzIDMgY29tcG9uZW50czoKCiAgMS4gQW4gb3V0cHV0LCBpZSAqKnNwZWNpZnkgd2hhdCB5b3Ugd2FudCB0aGUgb3V0cHV0IG9mIHlvdXIgZm9yIGxvb3AgdG8gYmUqKiwgZWcgYSB2ZWN0b3IgYW5kIGRldGFpbHMgbGlrZSBpdHMgdmVjdG9yIHR5cGU7IG9yIHlvdSBjb3VsZCBhbHNvIHNwZWNpZnkgeW91ciBvdXRwdXQgYXMgYmVpbmcgYSB0aWJibGUsIGEgZmFjdG9yIGV0Yy4KICAKICAyLiBBIHNlcXVlbmNlLCBpZSB3aGF0IHRvIGxvb3Agb3ZlciAmIGludm9sdmVzIGFzc2lnbmluZyBgaWAgKGxpa2UgJ2l0JykgdG8gYSBkaWZmZXJlbnQgdmFsdWUsIHNvIGBmb3IgaSBpbiBfX19fX2AKICAKICAzLiBUaGUgYm9keSB7fSwgaWUgdGhlIGNvZGUgdGhhdCBkb2VzIHRoZSB3b3JrLCBlYWNoIHRpbWUgd2l0aCBhIGRpZmZlcmVudCB2YWx1ZSBmb3IgYGlgCgoqKkV4YW1wbGVzKioKQW4gZXhhbXBsZSBvZiBhIHNpbXBsZSBmb3IgbG9vcCAgCmBgYHtyfQpvdXRwdXQgPC0gdmVjdG9yKCJkb3VibGUiLCBuY29sKG10Y2FycykpICMgVGhlIHZlY3RvciBzaG91bGQgYmUgb2YgdGhlIHR5cGUgJ2RvdWJsZScgYW5kIHdpdGggdGhlIHNhbWUgbnVtYmVyIG9mIGVsZW1lbnRzIGFzIHRoZXJlIGFyZSBjb2x1bW5zIGluIG10Y2FycwogICAgICAgICAgICBuYW1lcyhvdXRwdXQpIDwtIG5hbWVzKG10Y2FycykgIyBUaGUgbmFtZXMgb2YgdGhlIGVsZW1lbnRzIHNob3VsZCBiZSB0aGUgbmFtZXMgb2YgdGhlIGNvbHVtbnMgaW4gbXRjYXJzCiAgICAgICAgICAgIGZvciAoaSBpbiBuYW1lcyhtdGNhcnMpKSB7ICMgRm9yIGV2ZXJ5IGNvbHVtbiBpbiBtdGNhcnMKICAgICAgICAgICAgICAgICAgIG91dHB1dFtpXSA8LSBtZWFuKG10Y2Fyc1tbaV1dKSAjIG1ha2UgdGhlIGkndGggZWxlbWVudCBvZiB0aGUgdmVjdG9yIG91dHB1dCB0aGUgbWVhbiBvZiB0aGUgaSd0aCBjb2x1bW4gaW4gbXRjYXJzICh3aGVyZSBpJ3RoID0gMXN0LCAybmQsIDNyZCBldGMpCiAgICAgICAgICAgIH0Kb3V0cHV0ICMgUHJpbnQgb3V0cHV0CmBgYAoKQSBzaW1pbGFyIGV4YW1wbGUgdGhhdCBkb2Vzbid0IHJlcXVpcmUgdGhlIGxpbmUgYG5hbWVzKG91dHB1dCkgPC0gbmFtZXMobXRjYXJzKWAgYnV0IGdpdmVzIHJlc3VsdHMgd2l0aG91dCBjb2x1bW4gaGVhZGluZ3Mgd291bGQgYmU6CmBgYHtyfQpvdXRwdXQgPC0gdmVjdG9yKCJkb3VibGUiLCBuY29sKG10Y2FycykpICMgVGhlIHZlY3RvciBzaG91bGQgYmUgb2YgdGhlIHR5cGUgJ2RvdWJsZScgYW5kIHdpdGggdGhlIHNhbWUgbnVtYmVyIG9mIGVsZW1lbnRzIGFzIHRoZXJlIGFyZSBjb2x1bW5zIGluIG10Y2FycwogICAgICAgICAgICBmb3IgKGkgaW4gMTpuY29sKG10Y2FycykpIHsgIyBGb3IgZXZlcnkgY29sdW1uIGluIHRoZSBudW1iZXIgb2YgY29sdW1ucyB0aGF0IG10Y2FycyBoYXMKICAgICAgICAgICAgICAgICAgIG91dHB1dFtpXSA8LSBtZWFuKG10Y2Fyc1tbaV1dKSAjIG1ha2UgdGhlIGkndGggZWxlbWVudCBvZiB0aGUgdmVjdG9yIG91dHB1dCB0aGUgbWVhbiBvZiB0aGUgaSd0aCBjb2x1bW4gaW4gbXRjYXJzICh3aGVyZSBpJ3RoID0gMXN0LCAybmQsIDNyZCBldGMpCiAgICAgICAgICAgIH0Kb3V0cHV0ICMgUHJpbnQgb3V0cHV0CmBgYAoKKipGb3IgbG9vcHM6IHZhcmlhdGlvbnMqKgoKLSBGb3IgbG9vcHMgYXJlbid0IGxpbWl0ZWQgdG8gY3JlYXRpbmcgbmV3IG9iamVjdHM7IHlvdSBjYW4gYWxzbyB1c2UgdGhlbSB0byBjaGFuZ2UgZXhpc3Rpbmcgb25lcyBieSBqdXN0IHJlbW92aW5nIHRoZSBhc3NpZ25tZW50IGF0IHRoZSBzdGFydAoKLSBGb3IgbG9vcHMgY2FuIGxvb3Agb3ZlciBuYW1lcyBhbmQgdmFsdWVzIChub3QganVzdCBpbmRpY2VzIC8gbnVtYmVyIG9mIGNvbHVtbnMpCiAgCiAgLSBsb29wIG92ZXIgZWxlbWVudHMgd2l0aCBgZm9yICh4IGluIHhzKWAKICAKICAtIGxvb3Agb3ZlciBuYW1lcyB3aXRoIGBmb3IgKG5tIGluIG5hbWVzKHhzKSlgCgotIEZvciBsb29wcyBjYW4gaGFuZGxlIG91dHB1dHMgYW5kIHNlcXVlbmNlcyBvZiB1bmtub3duIGxlbmd0aCB3aXRoICd3aGlsZScgbG9vcHMgKGFsdGhvdWdoIHRoaXMgaXMgbW9zdCB1c2VmdWwgaW4gY29udGV4dCBvZiBzaW11bGF0aW9uLCBzbyB1bmxpa2VseSB0byBuZWVkKQoKKipFeGFtcGxlcyoqCgpIb3cgdG8gbG9hZCBhIGRpcmVjdG9yeSBmdWxsIG9mIGNzdnMgaW50byBhIHNpbmdsZSBkYXRhIGZyYW1lIHVzaW5nIGEgZm9yIGxvb3AuCgoxKSBDcmVhdGUgYSB2ZWN0b3Igd2l0aCB0aGUgZmlsZW5hbWUgcGF0aHMKYGBgCmZpbGVzIDwtIGRpcigiZGF0YS8iLCBwYXR0ZXJuID0gIlxcLmNzdiQiLCBmdWxsLm5hbWVzID0gVFJVRSkKYGBgCgoyKSBVc2UgYSBmb3IgbG9vcCB0aGF0IHByZWFsbG9jYXRlcyBhIGxpc3QgdG8gdGhlIGZpbGVuYW1lcyBhbmQgbG9hZHMgdGhlbSBpbiB3aXRoIHJlYWRfY3N2KCkKYGBgCmRmIDwtIHZlY3RvcigibGlzdCIiLCBsZW5ndGgoZmlsZXMpKSAKZm9yIChmbmFtZSBpbiBzZXFfYWxvbmcoZmlsZXMpKSB7CmRmW1tpXV0gPC0gcmVhZF9jc3YoZmlsZXNbW2ldXSkKfQpgYGAKCjMpIENvbWJpbmUgdGhlbSBpbnRvIGEgc2luZ2xlIGRhdGFmcmFtZQpgYGAKZGYgPC0gYmluZF9yb3dzKGRmKQpgYGAKCiMjIyBQdXJycgoKLSAqKnB1cnJyID0gYmV0dGVyIHRoYW4gZm9yIGxvb3BzKiogYmVjYXVzZSBpdCBtYWtlcyBjb2RlIGVhc2llciB0byB3cml0ZSBhbmQgdG8gcmVhZCBieSBmb2N1c2luZyBvbiB0aGUgb3BlcmF0aW9uIGJlaW5nIHBlcmZvcm1lZCAoYW5kIG5vdCB0aGUgYm9va2tlZXBpbmcgcmVxdWlyZWQgdG8gbG9vcCBvdmVyIGV2ZXJ5IGVsZW1lbnQgYW5kIHN0b3JlIHRoZSBvdXRwdXQhKQoKLSBLZXkgaWRlYSBvZiBmdW5jdGlvbmFsIHByb2dyYW1taW5nID0gcGFzc2luZyBvbmUgZnVuY3Rpb24gdG8gYW5vdGhlciBmdW5jdGlvbgoKLSBJdCBlbGltaW5hdGVzIHRoZSBuZWVkIGZvciBtYW55IGNvbW1vbiBmb3IgbG9vcHMgYW5kIGlzIHNpbWlsYXIgdG8gdGhlIGBhcHBseSgpYCBmYW1pbHkgb2YgZnVuY3Rpb25zCgotIEtleSBpZGVhIGlzIHRvIGhhdmUgZnVuY3Rpb25zIHRoYXQgcGVyZm9ybSB0aGUgY29tbW9uIHBhdHRlcm4gb2Y6CiAgCiAgMS4gTG9vcGluZyBvdmVyIGEgdmVjdG9yCiAgCiAgMi4gRG9pbmcgc29tZXRoaW5nIHRvIGVhY2ggZWxlbWVudAogIAogIDMuIFNhdmluZyB0aGUgcmVzdWx0cyAKCi0gVGhlcmUgYXJlIGRpZmZlcmVudCBwdXJyciBmdW5jdGlvbnMgZm9yIGRpZmZlcmVudCB0eXBlcyBvZiBvdXRwdXQsIGFzIGZvbGxvd3M6CgogIC0gYG1hcCgpYCB0byBtYWtlIGEgbGlzdAogIAogIC0gYG1hcF9sZ2woKWAgdG8gbWFrZSBhIGxvZ2ljYWwgdmVjdG9yCiAgCiAgLSBgbWFwX2ludCgpYCB0byBtYWtlIGFuIGludGVnZXIgdmVjdG9yCiAgCiAgLSBgbWFwX2RibCgpYCB0byBtYWtlIGEgZG91YmxlIHZlY3RvcgogIAogIC0gYG1hcF9jaHIoKWAgdG8gbWFrZSBhIGNoYXJhY3RlciB2ZWN0b3IKICAKLSBUaGVyZSBhcmUgYWxzbyB1c2VmdWwgc2hvcnRjdXRzIHlvdSBjYW4gdXNlIHdpdGhpbiBwdXJycjoKCiAgLSBgfmAgd2lsbCByZXBsYWNlIGBmdW5jdGlvbih4KWAgd2hlbiB5b3Ugd2FudCB0byBjcmVhdGUgYW4gYW5vbnltb3VzIGZ1bmN0aW9uCiAgCiAgLSBgLmAgd2lsbCByZWZlciB0byB0aGUgY3VycmVudCBsaXN0IGVsZW1lbnQgKGEgYml0IGxpa2UgaG93IGBpYCB3b3JrcyBpbiBhIGZvciBsb29wKQogIApIZXJlJ3MgdGhlIGJhc2ljIHN0cnVjdHVyZSBmb3IgdXNpbmcgYSBtYXAgZnVuY3Rpb246CmBgYAptYXBfdmVjdG9ydHlwZShkYXRhc2V0LCBmdW5jdGlvbikKYGBgCgoqKkV4YW1wbGVzKioKQ2FsY3VsYXRlIHRoZSBtZWFuIG9mIGV2ZXJ5IGNvbHVtbiBpbiBtdGNhcnMKYGBge3J9Cm1hcF9kYmwobXRjYXJzLCBtZWFuKQpgYGAKCkNvbXB1dGUgdGhlIG51bWJlciBvZiB1bmlxdWUgdmFsdWVzIGluIGVhY2ggY29sdW1uIG9mIGlyaXMKYGBge3J9CiMgVjEgV0lUSE9VVCBTSE9SVENVVFMKbWFwX2ludChpcmlzLCBmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHgpKSkKIyBWMiBXSVRIIFNIT1JUQ1VUUwptYXBfaW50KGlyaXMsIH4gbGVuZ3RoKHVuaXF1ZSguKSkpCmBgYAoKVXNlIGEgbWFwIGZ1bmN0aW9uIHdpdGhpbiBhIG11dGF0ZSwgdXNpbmcgYSBmdW5jdGlvbiB5b3UndmUgY3JlYXRlZCwgaWUgYXBwbHlpbmcgYSBmdW5jdGlvbiB0byBldmVyeSB2YXJpYWJsZSB3aXRoaW4gYSBkYXRhc2V0CmBgYHtyfQojIEV4YW1wbGUgZGF0YXNldAphIDwtIHRpYmJsZSh4ID0gYygxOjQpLCB5ID0gYygxMDA6MTAzKSkKYQoKIyBFeGFtcGxlIGZ1bmN0aW9uICh0aGUgb2xkIGFkZF90d28gY3JlYXRlZCBlYXJsaWVyIGluIHRoZSBGdW5jdGlvbnMgY2hhcHRlciBub3RlcykKYWRkX3R3byA8LSBmdW5jdGlvbih4KXsKICB4KzIKfQoKIyBFeGFtcGxlIG1hcCBmdW5jdGlvbgpjIDwtIGEgJT4lCiAgbXV0YXRlKHogPSBtYXBfZGJsKHgsIGFkZF90d28pLAogICAgICAgICB6MiA9IG1hcCh4LCBhZGRfdHdvKSkKYwpjJHoyCmBgYAoKQXBwbHkgbXVsdGlwbGUgbWFwIGZ1bmN0aW9ucyB0byBuZXN0ZWQgdGliYmxlcywgaWUgYXBwbHlpbmcgYSBzZXQgb2YgZnVuY3Rpb25zIHRvIHNlcGFyYXRlIGRhdGFzZXRzCmBgYHtyfQojIENyZWF0ZSBmdW5jdGlvbiwgaW4gdGhpcyBjYXNlIHRvIGZpbHRlciBhIGRhdGFzZXQgYnkgUGV0YWwuTGVuZ3RoID4gNQpmaWx0ZXJfcGV0YWwgPC0gZnVuY3Rpb24oZGYpewogIGRmICU+JQogIGZpbHRlcihQZXRhbC5MZW5ndGggPiA1KQp9CgojIFR1cm4gYSBub3JtYWwgZGF0YWZyYW1lIGludG8gYSBkYXRhZnJhbWUgb2YgbmVzdGVkIHRpYmJsZXMsIGluIHRoaXMgY2FzZSB0aGUgaXJpcyBkYXRhc2V0IG5lc3RlZCBieSBzcGVjaWVzCmlyaXNfbW9kIDwtIGlyaXMgJT4lCiAgZ3JvdXBfYnkoU3BlY2llcykgJT4lCiAgbmVzdCgpCgojIENyZWF0ZSAzIG5ldyBjb2x1bW5zIGluIGFsbCBvZiB0aGUgbmVzdGVkIHRpYmJsZXMKaXJpc19tb2QgJT4lCiAgCiAgIyBVc2UgbWFwIHRvIGFwcGx5IHRoZSBuZXcgZnVuY3Rpb24gZmlsdGVyX3BldGFsIHRvIGVhY2ggb2YgdGhlIHNlcGFyYXRlIGRhdGFzZXRzIGZvciBTcGVjaWVzIHdpdGhpbiBpcmlzX21vZCAoV2UgdXNlIG1hcCgpIGJlY2F1c2UgdGhlIG91dHB1dCBmcm9tIGFwcGx5aW5nIGZpbHRlcl9wZXRhbCB0byBkYXRhIGlzIGEgbGlzdC4pCiAgbXV0YXRlKGZsX3BldGFsID0gcHVycnI6Om1hcChkYXRhLCBmaWx0ZXJfcGV0YWwpLCAKICAgICAgICAgCiAgIyBJZiB3ZSB3YW50IHRvIGFkZCB0aGUgbnVtYmVyIG9mIHJvd3MgaW4gZWFjaCBkYXRhc2V0LCB3ZSB3b3VsZCB0aGVuIHVzZSBtYXBfZGJsKCksIGJlY2F1c2UgdGhlIG91dHB1dCBmcm9tIG5yb3cgaXMgYSBzaW5nbGUgbnVtYmVyLgogIG5fZnVsbF9kYXRhID0gcHVycnI6Om1hcF9kYmwoZGF0YSwgbnJvdyksIAogIAogICMgRGl0dG8gLSB1c2UgbWFwX2RibCgpIGZvciB0aGlzIG91dHB1dCwgd2hpY2ggaXMgdGhlIG51bWJlciBvZiByb3dzIGluIG91ciBuZXcgY29sdW1uLCBmbF9wZXRhbCwgd2hpY2ggd2UgY3JlYXRlZCBhYm92ZQogIG5fcGV0YWxfbGFyZ2UgPSBwdXJycjo6bWFwX2RibChmbF9wZXRhbCwgbnJvdykpIApgYGAKCiMjIyBJdGVyYXRlIHdpdGggZ2dwbG90Cgo+PiBBZGQgY3JlYXRpbmcgYSBmdW5jdGlvbiB0byByZXBlYXQgdGhlIHNhbWUgZ2dwbG90ICYgcHJvZ3JhbW1hdGljYWxseSB3cml0aW5nIHRpdGxlcywgY2FwdGlvbnMgZXRjIDw8CgojIyMgSXRlcmF0ZSB3aXRoIHdhbGsKCioqSXRlcmF0aXZlbHkgc2F2ZSBmaWxlcyoqCgpVc2UgKipgd2FsaygpYCoqIGFuZCBpdHMgdmFyaWFudHMgKipgd2FsazJgKiogYW5kICoqYHB3YWxrKClgKiogd2hlbiB5b3Ugd2FudCB0byBjYWxsIGEgZnVuY3Rpb24gZm9yIGl0cyBzaWRlIGVmZmVjdHMsIHJhdGhlciB0aGFuIGl0cyByZXR1cm4gdmFsdWUuCgpUaGlzIGlzIHRoZSBjYXNlIHdoZW4geW91IHdhbnQgdG8gcmVuZGVyIG91dHB1dCB0byB0aGUgc2NyZWVuIG9yIHNhdmUgYSBmaWxlIHRvIGRpc2suCgpGb3IgZXhhbXBsZSwgaWYgeW91IGhhZCBhIGxpc3Qgb2YgcGxvdHMgYW5kIGEgdmVjdG9yIG9mIGZpbGVuYW1lcywgeW91IGNhbiB1c2UgYHB3YWxrKClgIGFzIGZvbGxvd3M6CmBgYHtyfQojIENyZWF0ZSBhIGxpc3Qgb2YgbXVsdGlwbGUgcGxvdHMgdXNpbmcgdGhlIG1hcCgpIGZ1bmN0aW9uCnBsb3RzIDwtIG10Y2FycyAlPiUgCiAgICAgICAgICBzcGxpdCguJGN5bCkgJT4lIAogICAgICAgICAgbWFwKH5nZ3Bsb3QoLiwgYWVzKG1wZywgd3QpKSArIGdlb21fcG9pbnQoKSkKcGxvdHMKCiMgQ3JlYXRlIGFuIGl0ZXJhdGl2ZSBmaWxlbmFtaW5nIHN0cnVjdHVyZSBmb3IgZWFjaCBwbG90LCBpbiB0aGlzIGNhc2UgdGhlIG5hbWVzIG9mIHRoZSBwbG90cyBmb2xsb3dlZCBieSAucGRmCnBhdGhzIDwtIHN0cmluZ3I6OnN0cl9jKG5hbWVzKHBsb3RzKSwgIi5wZGYiKQoKIyBTYXZlIGVhY2ggcGxvdCB0byB0aGUgcGF0aCBuYW1lcyBkZWZpbmVkIGluICdwYXRocycgYnkgY29tYmluaW5nIHRoZSBwbG90cyBhbmQgdGhlIHBhdGhuYW1lcyBpbiBhIGxpc3QgYW5kIHNhdmluZyB0aGF0IGxpc3QKcHdhbGsobGlzdChwYXRocywgcGxvdHMpLCBnZ3NhdmUsIHBhdGggPSB0ZW1wZGlyKCkpCmBgYAoKIyBDb21tdW5pY2F0ZQoKIyMjIFJNYXJrZG93bgoKQ2hvb3NlIGEgbWFya2Rvd24gdGhlbWVzIG9uICoqW0Jvb3Rzd2F0Y2hdKGh0dHBzOi8vYm9vdHN3YXRjaC5jb20vMy8pKiosIGluY2x1ZGluZzoKCi0gY29zbW8g4q2Q77iPCgotIGpvdXJuYWwKCi0gZGFya2x5IChibGFjaykKCi0gZmxhdGx5CgotIGx1bWVuCgotIHBhcGVyCgotIHJlYWRhYmxlCgotIHNhbmRzdG9uZQoKLSBzaW1wbGV4CgotIHNsYXRlIChncmV5KQoKLSBzdXBlcmhlcm8gKGJsdWUpCgpDcmVhdGUgYSB3ZWJzaXRlIGluIFJNYXJrZG93biB3aXRoICoqW2Jvb2tkb3duXShodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ibG9nZG93bi9nZXQtc3RhcnRlZC5odG1sKSoqCgojIEJvb2ttYXJrcwoKLSBbUiBmb3IgZGF0YSBzY2llbmNlXShodHRwczovL3I0ZHMuaGFkLmNvLm56LykKCi0gW0RhdGEgdmlzdWFsaXphdGlvbjogYSBwcmFjdGljYWwgaW50cm9kdWN0aW9uXShodHRwczovL3NvY3Zpei5jby9pbmRleC5odG1sI3ByZWZhY2UpCgotIFtSIE1hcmtkb3duXShodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ybWFya2Rvd24vKQoKLSBbUiBhbmQgR2l0aHViXShodHRwczovL2hhcHB5Z2l0d2l0aHIuY29tL2dpdC1jbGllbnQuaHRtbCkKCi0gW1RpZHl2ZXJzZTogZ2dwbG90XShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvaW5kZXguaHRtbCMpCgojIE5vdGVzCiMjIyBGYWN0b3JzCgotIEZhY3RvcnMgYXJlIHVzZWQgZm9yICoqY2F0ZWdvcmllcyoqCgotIEZhY3RvcnMgY2FuIGVpdGhlciBoYXZlIGFuIG9yZGVyIHRoYXQncyAiYXJiaXRyYXJ5IiAoZWcgaGFpciBjb2xvdXIpIG9yICJwcmluY2lwbGVkIiAoZWcgbW9udGhzKQoKLSBUaGV5J3JlIGVzcCB1c2VmdWwgaWYgeW91IHdhbnQgdG8gZGlzcGxheSBjaGFyYWN0ZXIgdmVjdG9ycyBpbiBub24tYWxwaGFiZXRpY2FsIG9yZGVyCgotIFVzZSAqKmZvcmNhdHMqKiBwYWNrYWdlIChub3QgcGFydCBvZiB0aWR5dmVyc2UpIGZvciB3b3JraW5nIHdpdGggZmFjdG9ycwoKRm9yIGV4YW1wbGUsIHNheSB5b3UgaGF2ZSBhIHN0cmluZyBvZiBtb250aHMKYGBge3J9CmxpYnJhcnkoZm9yY2F0cykKeDEgPC0gYygiRGVjIiwgIkFwciIsICJKYW4iLCAiTWFyIikKYGBgCgpUbyBjcmVhdGUgYSAiZmFjdG9yIiBvZiBtb250aHMgb3ZlciBhIHllYXI6CgogIDEuIEZpcnN0IGNyZWF0ZSBhIGxpc3Qgb2YgdGhlIGRpZmZlcmVudCBtb250aHMsIG9yICoqbGV2ZWxzKiosIGluIHRoZSBvcmRlciB0aGV5IHNob3VsZCBhcHBlYXIKYGBge3J9Cm1vbnRoX2xldmVscyA8LSBjKCJKYW4iLCAiRmViIiwgIk1hciIsICJBcHIiLCAiTWF5IiwgIkp1biIsCiAgICAgICAgICAgICAgICAgICJKdWwiLCAiQXVnIiwgIlNlcCIsICJPY3QiLCAiTm92IiwgIkRlYyIpCmBgYAoKICAyLiBUaGVuIGNyZWF0ZSBhIGZhY3RvciB3aXRoIHRoZSBmdW5jdGlvbiBmYWN0b3IoKSBhbmQgbGV2ZWxzID0gCmBgYHtyfQp5MSA8LSBmYWN0b3IoeDEsIGxldmVscyA9IG1vbnRoX2xldmVscykKYGBgCgpZb3Ugbm93IGhhdmUgYSBmYWN0b3IgdGhhdCB5b3UgY2FuICoqc29ydCoqIGFjY29yZGluZyB0byB0aGUgb3JkZXIgeW91IHNldCB3aXRoIHNvcnQoKQpgYGB7cn0Kc29ydCh5MSkKYGBgCgpZb3UgY2FuICoqY2hlY2sqKiB0aGUgbGV2ZWxzIGluIHlvdXIgZmFjdG9yIHdpdGggbGV2ZWxzKCkKYGBge3J9CmxldmVscyh5MSkKYGBgCgpZb3UgY2FuICoqcmVvcmRlcioqIHlvdXIgZmFjdG9yIGluIGF0IGxlYXN0IDIgd2F5czoKCiAgMS4gQnkgYW5vdGhlciB2YXJpYWJsZSwgZWcgd2hpY2ggbW9udGggaGFzIHRoZSBtb3N0IGJpcnRoZGF5cywgd2l0aCAqKmZjdF9yZW9yZGVyKCkqKjoKYGBge3J9CmZjdF9yZW9yZGVyKGZhY3RvciwgdmFyaWFibGUpCmBgYAogIAogIDIuIEJ5IG1vdmluZyBvbmUgb3IgbW9yZSBsZXZlbHMgdG8gdGhlIGVuZCwgZWcgc2hvd2luZyAiTm90IGFwcGxpY2FibGUiIGF0IHRoZSBlbmQsIHdpdGggKipmY3RfcmVsZXZlbCgpKio6CmBgYHtyfQpmY3RfcmVsZXZlbChmYWN0b3IsICJsZXZlbCB0byBwdWxsIG91dCIsICJsZXZlbCB0byBwdWxsIG91dCIpCmBgYAoKWW91IGNhbiBhbHNvICoqZWRpdCoqIHlvdXIgZmFjdG9yIHdpdGggKipmY3RfcmVjb2RlKiouIFRoaXMgaXMgcGFydGljdWxhcmx5IHVzZWZ1bCBmb3IgZWRpdGluZyBsYWJlbHMgZm9yIHB1YmxpY2F0aW9uLiBBbmQgeW91IGNhbiB1c2UgdGhpcyB0ZWNobmlxdWUgdG8gY29tYmluZSBtdWx0aXBsZSAib2xkIiBsZXZlbHMgaW50byB0aGUgc2FtZSAibmV3IiBsZXZlbCwgZWcgZ3JvdXBpbmcgc2V2ZXJhbCBsZXZlbHMgdW5kZXIgIk90aGVyIi4KCmBgYHtyfQpkYXRhICU+JSBtdXRhdGUodmFyaWFibGUgPSBmY3RfcmVjb2RlKHZhcmlhYmxlLCAibmV3IG5hbWUiID0gIm9sZCBuYW1lIiwgIm5ldyBuYW1lIiA9ICJvbGQgbmFtZSIpKQpgYGAKCk90aGVyIG1vZGlmaWNhdGlvbnMgeW91IGNhbiBtYWtlIHRvIHlvdXIgZmFjdG9yIGluY2x1ZGU6CgotICoqZmN0X2NvbGxhcHNlKCkqKiwgd2hpY2ggaXMgbGlrZSBmY3RfcmVjb2RlKCksIGJ1dCB3b3JrcyBzbyB0aGF0IGZvciBlYWNoIG5ldyB2YXJpYWJsZSB5b3UgY2FuIHByb3ZpZGUgYSB2ZWN0b3Igb2Ygb2xkIGxldmVscywgZWcKYGBge3J9CmRhdGEgJT4lIG11dGF0ZSh2YXJpYWJsZSA9IGZjdF9jb2xsYXBzZSh2YXJpYWJsZSwgIm5ldyBuYW1lIiA9IGMoIm9sZCBuYW1lIiwgIm9sZCBuYW1lIiwgIm9sZCBuYW1lIikpCmBgYAoKLSAqKmZjdF9sdW1wKiosIHdoaWNoIGNydWRlbHkgbHVtcHMgdG9nZXRoZXIgYWxsIHRoZSAic21hbGwiIGdyb3VwcyB0byBzaW1wbGlmeSBhIHBsb3Qgb3IgdGFibGUKYGBge3J9CmRhdGEgJT4lIG11dGF0ZSh2YXJpYWJsZSA9IGZjdF9sdW1wKHZhcmlhYmxlKSkKYGBgCgojIyMgVmVjdG9ycwoKLSBWZWN0b3JzIGFyZSBlc3NlbnRpYWxseSB2YXJpYWJsZXMgd2l0aGluIGEgdGliYmxlCgotIFRoZXJlIGFyZSAzIHR5cGVzIG9mIHZlY3RvcjoKICAKICAxLiAqKkF0b21pYyoqIHdoaWNoIGNhbiBiZSBmdXJ0aGVyIHN1YmNhdGVnb3Jpc2VkIGludG86CiAgCiAgICAgIC0gKipsb2dpY2FsKiogKFRSVUUsIEZBTFNFIGFuZCBOQSkKICAgICAgCiAgICAgIC0gKippbnRlZ2VyKiogKG51bWVyaWMgd2l0aG91dCBkZWNpbWFsIHBsYWNlcywgZWcgMjQzKQogICAgICAKICAgICAgLSAqKmRvdWJsZSoqIChudW1lcmljIHdpdGggZGVjaW1hbCBwbGFjZXMsIGVnIDI0My41Njc4KSAKICAgICAgCiAgICAgIC0gKipjaGFyYWN0ZXIqKiAobWFkZSB1cCBvZiBzdHJpbmdzKQogICAgICAKICAgICAgLSAqKmNvbXBsZXgqKiAocmFyZWx5IHVzZWQgZHVyaW5nIGRhdGEgYW5hbHlzaXMpCiAgICAgIAogICAgICAtICoqcmF3KiogKGRpdHRvKQogIAogIDIuICoqTGlzdHMqKiAoYWthICdyZWN1cnNpdmUgdmVjdG9ycycgYW5kIGNhbiBjb250YWluIG90aGVyIGxpc3RzLCBzbyB0aGV5J3JlIGdvb2QgZm9yIHJlcHJlc2VudGluZyBoaWVyYXJjaGllcyBpbiBkYXRhKQoKICAzLiAqKmBOVUxMYCoqIHdoaWNoIGlzIHRoZSBhYnNlbmNlIG9mIGEgdmVjdG9yIChhbmQgZGlzdGluY3QgZnJvbSBgTkFgIHdoaWNoIGlzIHRoZSBhYnNlbmNlIG9mIGEgdmFsdWUgaW4gYSB2ZWN0b3IpCgoqKk5vdGUgYWJvdXQgaW50ZWdlcnMgdnMgZG91YmxlcyoqOiBpZiB5b3UganVzdCBlbnRlciBgMmAgaW4gUiwgaXQgaW50ZXJwcmV0cyB0aGlzIGFzIGAyLjAwMDAwMGAsIGllIGEgZG91YmxlLiBUbyBlbnRlciBgMmAgYXMgYW4gaW50ZWdlciB5b3UgaGF2ZSB0byB3cml0ZSBgMkxgLiAKYGBge3J9CnR5cGVvZigyKQp0eXBlb2YoMkwpCnR5cGVvZigyLjVMKQpgYGAKClNvIHdoYXQgY2FuIHlvdSBkbyB3aXRoIHZlY3RvcnM/CgotIFlvdSBjYW4gZmluZCBvdXQgd2hhdCB0eXBlIG9mIHZlY3RvciBhIHZlY3RvciBpcyB3aXRoICoqYHR5cGVvZigpKiogCgotIFlvdSBjYW4gZmluZCBvdXQgaG93IG1hbnkgaXRlbXMgdGhlcmUgYXJlIGluIGEgdmVjdG9yIHdpdGggKipgbGVuZ3RoKCkqKgoKLSBZb3UgY2FuIHNwZWNpZnkgaG93IHRvIHJlYWQgYSB2ZWN0b3Igd2hlbiB5b3UgaW1wb3J0IHdpdGggYHJlYWRyYCBieSB1c2luZyBgY29sX3R5cGVzYCBzcGVjaWZpY2F0aW9ucywgZm9yIGV4YW1wbGU6CgpgYGB7cn0KZGF0YSA8LSByZWFkX2NzdigiZGF0YS5jc3YiKSwgCiAgICAgICAgICAgICAgY29sX3R5cGVzID0gY29scyhjb2x1bW5fbmFtZSA9IGNvbF9sb2dpY2FsKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZSA9IGNvbF9pbnRlZ2VyKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZSA9IGNvbF9kb3VibGUoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lID0gY29sX2NoYXJhY3RlcigpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX25hbWUgPSBjb2xfZGF0ZShmb3JtYXQgPSAiIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZSA9IGNvbF90aW1lKGZvcm1hdCA9ICIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lID0gY29sX2RhdGV0aW1lKGZvcm1hdCA9ICIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lID0gY29sX251bWJlcigpLCAjIElzbid0IGZ1c3N5IGFib3V0IG51bWJlcnMgY29udGFpbmluZyBjb21tYXMsICQgZXRjCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZSA9IGNvbF9za2lwKCksICMgU2tpcHMgaW1wb3J0aW5nIHRoaXMgY29sdW1uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZSA9IGNvbF9ndWVzcygpICMgR3Vlc3NlcyBob3cgdG8gcGFyc2UgYmFzZWQgb24gaW5wdXQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKYGBgCgotIFlvdSBjYW4gY29udmVydCBvbmUgdHlwZSBvZiB2ZWN0b3IgdG8gYW5vdGhlciB3aXRoICoqYGFzLmxvZ2ljYWwoKWAqKiwgKipgYXMuaW50ZWdlcigpYCoqLCAqKmBhcy5kb3VibGUoKWAqKiBvciAqKmBhcy5jaGFyYWN0ZXIoKWAqKgoKLSBZb3UgY2FuIHBlcmZvcm0gYmFzaWMgbWF0aHMgb3BlcmF0aW9ucyBvbiB2ZWN0b3JzLCBpZSBkb24ndCBuZWVkIHRvIGl0ZXJhdGUgZm9yIGV2ZXJ5IGl0ZW0gaW4gdGhlIHZlY3RvcgoKLSBZb3UgY2FuIGNyZWF0ZSBhbmQgbmFtZSBhIHZlY3RvciB3aXRoIGBjKClgCgoqKkxpc3RzKioKCi0gQ2FuIGNvbnRhaW4gYSBtaXggb2YgYXRvbWljIHZlY3RvciB0eXBlcyBhbmQgb3RoZXIgbGlzdHMKCi0gSXQncyB1c2VmdWwgdG8gdXNlIGBzdHIoKWAgYmVjYXVzZSB0aGlzIHdpbGwgZ2l2ZSB5b3UgdGhlIHN0cnVjdHVyZSBvZiB5b3VyIGxpc3QsIHJhdGhlciB0aGFuIHRoZSBjb250ZW50cwoKLSBUbyBzdWJzZXQgaXRlbXMgd2l0aGluIGEgbGlzdCB1c2UgZG91YmxlIGJyYWNrZXRzIGBbW11dYCByYXRoZXIgdGhhbiBzaW5nbGUgYFtdYC4KCioqQXVnbWVudGVkIHZlY3RvcnMqKgoKLSBGYWN0b3JzLCBkYXRlcywgZGF0ZS10aW1lcyBhbmQgdGliYmxlcyBhcmUgYXVnbWVudGVkIHZlY3RvcnMsIGJlY2F1c2UgdGhleSBoYXZlIGFkZGl0aW9uYWwgYXR0cmlidXRlcyAob24gdG9wIG9mIHRoZSB1c3VhbCB2ZWN0b3IgYXR0cmlidXRlcyBvZiBuYW1lcywgZGltZW5zaW9ucyBhbmQgY2xhc3MpCgogIC0gRmFjdG9ycyBoYXZlIGEgJ2xldmVscycgYXR0cmlidXRlCiAgCiAgLSBEYXRlcyBhbmQgZGF0ZS10aW1lcyBoYXZlIGFkZGl0aW9uYWwgJ2NsYXNzJyBhdHRyaWJ1dGVzCiAgCiAgLSBUaWJibGVzIGhhdmUgYWRkaXRpb25hbCAnY2xhc3MnIGF0dHJpYnV0ZXMgKGB0YmxfZGZgLCBgdGJsYCBhbmQgYGRhdGEuZnJhbWVgKSBhcyB3ZWxsIGFzIGNvbHVtbiBgbmFtZXNgIGFuZCBgcm93Lm5hbWVzYAo=