Setup & Configuration
- The code below will load the libraries you will need for this tutorial. If you do not have one, please install with
install.packages("pkg"):
options(warnPartialMatchArgs = FALSE) # don't want these warnings
options(width = 100)
library(tibble) # special type of data frame
library(magrittr) # pipes
library(dplyr) # data manipulation
library(ggplot2) # pretty plots
library(tidyr) # reshape data frames; mostly for ggplots
library(lubridate) # working with dates/times
library(nycflights13) # data set of flight times, departures, and arrivals
- The data we will be using today is available via the
nycflights13 package.
- R environment:
- Now is a good time to check that your version of R is up-to-date. If you are not running version R >= 3.2.3 it is time to update. Use a function from the
devtools package to check your session info:
devtools::session_info()
What is Data Wrangling?
Data wrangling, sometimes referred to as data munging, is the process of transforming and mapping data from one “raw” data form into another format with the intent of making it more appropriate and valuable for a variety of downstream purposes such as analytics. A data wrangler is a person who performs these transformation operations (see wiki).
What’s this tutorial about?
Pipes (magrittr)

This is an idea that has been around for a long time, particularly in the Linux/Unix ecosystem, where it is denoted with the “|” symbol:
- Complex tasks can be accomplished by stringing together many simple tools - syntax is important for efficiency / readability
- The idea of pipes is that the result of the previous command is passed to the next command, which allows many tools to be strung together - from left to right.
- In R,
magrittr introduces the %>% pipe operator which runs the expression of the left and passes the result as the first argument to expression on the right.
You can think about the following sequence of actions:
1. find key
2. unlock car
3. start car
4. drive to school
5. park.
Expressed as a set of nested functions in R pseudocode this would look like:
park(drive(start_car(find("keys")), to="campus"))
Writing it out using pipes give it a more natural (and easier to read!) structure:
find("keys") %>%
start_car() %>%
drive(to="campus") %>%
park()
Approaches
All of the following are fine, it comes down to personal preference (and IMHO, readability):
# Nested:
h( g( f(x), y = 1), z = 1 )
# Piped:
f(x) %>% g(y = 1) %>% h(z = 1)
# Intermediate:
res <- f(x)
res <- g(res, y = 1)
res <- h(res, z = 1)
But what about other arguments? Sometimes we want to send our results to an function argument other than first one or we want to use the previous result for multiple arguments. In these cases we can refer to the previous result using “.”.
data.frame(a = 1:3, b = 3:1) %>% lm(a ~ b, data = .)
Call:
lm(formula = a ~ b, data = .)
Coefficients:
(Intercept) b
4 -1
data.frame(a = 1:3, b = 3:1) %>% .[[1]]
[1] 1 2 3
data.frame(a = 1:3, b = 3:1) %>% .[[length(.)]]
[1] 3 2 1
Tibbles
Modern data frames
Hadley Wickham has a package that modifies data frames to be more modern, or as he calls them surly and lazy. That is they don’t change variable names or types, and don’t do partial matching; and they complain more (e.g. when a variable does not exist). This forces you to confront problems earlier, typically leading to cleaner, more expressive code. Tibbles also have an enhanced print() method which makes them easier to use with large datasets containing complex objects.
class(iris)
[1] "data.frame"
tbl_iris <- as.tibble(iris)
class(tbl_iris)
[1] "tbl_df" "tbl" "data.frame"
Lazy tibbles
tbl_iris[1, ]
tbl_iris[, "Species"]
Lazy tibbles (factors)
tibble(x = 1:3, y = c("A", "B", "C")) # respects input class by default; character
# Annoyingly, the default of `data.frame()` is stringsAsFactors = TRUE
data.frame(x = 1:3, y = c("A", "B", "C"), stringsAsFactors = TRUE) # character -> factor class
Surly Tibbles
tbl_iris[, "Name"] # errors out; invalid index
Error: Column `Name` not found
tbl_iris[["Name"]] # same as above; different syntax
NULL
iris$Name # returns NULL
NULL
tbl_iris$Name # returns NULL (with explanation!)
Unknown or uninitialised column: 'Name'.
NULL
tbl_iris[160, ] # exceeded index; returns 1 row data frame of NAs
head(iris$Spec) # partial `$` match; not always a good thing!
[1] setosa setosa setosa setosa setosa setosa
Levels: setosa versicolor virginica
head(tbl_iris$Spec) # partial `$` match complains with warning(), as it should!
Unknown or uninitialised column: 'Spec'.
NULL
dplyr 
A Grammar of Data Manipulation
The dplyr package is based on the concepts of functions as verbs that manipulate data frames.
Single data frame functions / verbs:
filter(): pick rows matching criteria
slice(): pick rows using index(es)
select(): pick and/or reorder columns by name
pull(): grab a column as a vector
rename(): rename specific columns
arrange(): reorder rows
mutate(): add new variables
transmute(): create new data frame with variables
distinct(): filter for unique rows
sample_n() / sample_frac(): randomly sample rows
summarise(): reduce variables to values
- … and many more
dplyr rules for functions
1. First argument is always a data frame
2. Subsequent arguments say what to do with that data frame
3. Always return a data frame
4. Do not modify in place
5. Performance via lazy evaluation
Example Data
We will demonstrate dplyr functionality using the nycflights13 data which contains data on flights departing from New York City. It was written by Hadley Wickham and includes airline on-time data for all flights departing NYC in 2013 as well as useful metadata on airlines, airports, weather, and planes (see ?nycflights13).
Structure of flights
flights %<>% as.tibble # 'rebound' pipe; aka compound assignment
str(flights)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 336776 obs. of 19 variables:
$ year : int 2013 2013 2013 2013 2013 2013 2013 2013 2013 2013 ...
$ month : int 1 1 1 1 1 1 1 1 1 1 ...
$ day : int 1 1 1 1 1 1 1 1 1 1 ...
$ dep_time : int 517 533 542 544 554 554 555 557 557 558 ...
$ sched_dep_time: int 515 529 540 545 600 558 600 600 600 600 ...
$ dep_delay : num 2 4 2 -1 -6 -4 -5 -3 -3 -2 ...
$ arr_time : int 830 850 923 1004 812 740 913 709 838 753 ...
$ sched_arr_time: int 819 830 850 1022 837 728 854 723 846 745 ...
$ arr_delay : num 11 20 33 -18 -25 12 19 -14 -8 8 ...
$ carrier : chr "UA" "UA" "AA" "B6" ...
$ flight : int 1545 1714 1141 725 461 1696 507 5708 79 301 ...
$ tailnum : chr "N14228" "N24211" "N619AA" "N804JB" ...
$ origin : chr "EWR" "LGA" "JFK" "JFK" ...
$ dest : chr "IAH" "IAH" "MIA" "BQN" ...
$ air_time : num 227 227 160 183 116 150 158 53 140 138 ...
$ distance : num 1400 1416 1089 1576 762 ...
$ hour : num 5 5 5 5 6 5 6 6 6 6 ...
$ minute : num 15 29 40 45 0 58 0 0 0 0 ...
$ time_hour : POSIXct, format: "2013-01-01 05:00:00" "2013-01-01 05:00:00" ...
Look at full tibble data frame
flights
dplyr Verbs
March flights dplyr::filter
flights %>% filter(month == 3)
First week of March flights dplyr::filter
flights %>% filter(month == 3, day <= 7)
Flights to LAX or RDU in March dplyr::filter
flights %>% filter(dest == "LAX" | dest == "RDU", month==3)
First 10 flights dplyr::slice
flights %>% slice(1:10)
Last 5 flights dplyr::slice
flights %>% slice((n() - 5):n())
Select columns dplyr::select
flights %>% select(year, month, day)
Exclude columns dplyr::select
flights %>% select(-year, -month, -day)
Select ranges dplyr::select
flights %>% select(year:day)
Exclude ranges dplyr::select
flights %>% select(-(year:day))
Select matching dplyr::select
flights %>% select(contains("dep"),
contains("arr"))
flights %>% select(starts_with("dep"),
starts_with("arr"))
Reorder (Move) dplyr::select
flights %>% select(origin, everything()) # move `origin` to front
flights %>% select(-origin, everything()) # move `origin` to back
# These are alternatives to the above; which may seem impossible at first
# See below for why this works (`select()` vs `rename()`)
flights %>% select(origin, -origin) # move `origin` to front
flights %>% select(-origin, origin) # move `origin` to back
Using dplyr::pull
names(flights)
[1] "year" "month" "day" "dep_time"
[5] "sched_dep_time" "dep_delay" "arr_time" "sched_arr_time"
[9] "arr_delay" "carrier" "flight" "tailnum"
[13] "origin" "dest" "air_time" "distance"
[17] "hour" "minute" "time_hour"
flights %>% pull("year") %>% head()
[1] 2013 2013 2013 2013 2013 2013
flights %>% pull(1) %>% head()
[1] 2013 2013 2013 2013 2013 2013
flights %>% pull(-1) %>% head()
[1] "2013-01-01 05:00:00 EST" "2013-01-01 05:00:00 EST" "2013-01-01 05:00:00 EST"
[4] "2013-01-01 05:00:00 EST" "2013-01-01 06:00:00 EST" "2013-01-01 05:00:00 EST"
Other helpers:
starts_with()
ends_with()
contains()
matches()
num_range()
one_of()
everything()
last_col()
- The above “helpers” can be found in the new-ish
?tidyselect package which supports the dplyr package.
Change column names dplyr::rename
flights %>% rename(tail_number = tailnum)
select() vs. rename()
You can do some interesting things with dplyr::select(), such as renaming and selecting simultaneously.
flights %>% select(tail_number = tailnum)
This might be confusing, but arguments are evaluated only at the end of the call, and in the order of the arguments (i.e. ...). So you can refer to variables that have been removed or renamed by their original names. This is best seen in an example:
# Rename tailnum -> tail_number
# Then select the new variable `tail_number`
# Then select all variables except `tailnum` (but referring to the original name!)
flights %>% select(tail_number = tailnum, -tailnum) # no rows
# Select all variables except `tailnum`
# Then rename tailnum -> tail_number
# This 'adds back the variable with a new name
# And reorders it to the final column
flights %>% select(-tailnum, tail_number = tailnum) # tailnum -> tail_number + move to end
Sort data dplyr::arrange
flights %>% filter(month == 3, day == 2) %>% arrange(origin, dest)
Descending order dplyr::arrange
flights %>% filter(month == 3, day == 2) %>%
arrange(desc(origin), dest) %>% select(origin, dest, tailnum)
NA
Modify columns dplyr::mutate
flights %>%
select(1:3) %>%
mutate(date = paste(month, day, year, sep="/") %>% mdy())
Create new tibble from existing columns dplyr::transmute
flights %>%
select(1:3) %>%
transmute(date = paste(month, day, year, sep="/") %>% mdy())
Find unique rows dplyr::distinct
flights %>%
select(origin, dest) %>%
distinct() %>%
arrange(origin, dest)
Sample rows dplyr::sample_n
flights %>% sample_n(10)
Sample rows dplyr::sample_frac
flights %>% sample_frac(0.001)
Summary via dplyr::summarise
flights %>% mutate(date = paste(month, day, year, sep = "/") %>% mdy()) %>%
summarize(n(), min(date), max(date))
Set Groupings dplyr::group_by
# grouping parameters stored in attributes
flights %>% group_by(origin) # doesn't appear any different
flights %>% class() # pre-grouping is standard `tibble`
[1] "tbl_df" "tbl" "data.frame"
flights %>% group_by(origin) %>% class() # returned object is a new class `grouped_df`
[1] "grouped_df" "tbl_df" "tbl" "data.frame"
Summarize groups with dplyr::group_by() + dplyr::summarise()
# the `lubridate` package (tidyverse) helps working with dates
flights %>%
group_by(origin) %>%
mutate(date = paste(month, day, year, sep = "/") %>% lubridate::mdy()) %>%
summarize(n = n(), min = min(date), median = median(date), max = max(date))
Plotting
No data wrangling and/or data exploration is complete without at least some visual exploration of the data. Asking yourself: what do I have here? how is my data (particularly my class variable or response variable) distributed? Are there any missing values? Negative values?
par(mfrow = c(2, 2)) # make 2x2 grid for plots
par(mgp = c(2, 0.75, 0), mar = c(3, 4, 3, 1)) # graphics settings; squeeze margins
lapply(names(iris)[-5], function(class)
boxplot(split(iris[[class]], iris$Species), main = class, col = 1:3)) %>%
invisible() # pipe to invisible to suppress output

The same via 
thm <- theme_bw() +
theme(
panel.background = element_rect(fill = "transparent", colour = NA),
plot.background = element_rect(fill = "transparent", colour = NA),
legend.position = "top",
legend.background = element_rect(fill = "transparent", colour = NA),
legend.key = element_rect(fill = "transparent", colour = NA),
plot.title = element_text(hjust = 0.5)
)
theme_set(thm)
iris %>%
tidyr::gather(key = "Feature", value = "cm", -Species) %>%
mutate(Feature = gsub("\\.", " ", Feature)) %>%
ggplot(aes(y = cm, x = Species, fill = Species)) +
geom_boxplot(color = "#1F3552", alpha = 0.75, size = 0.5) +
scale_x_discrete(name = "Species") +
ggtitle("Overall Title") +
facet_wrap(~Feature, ncol = 2) +
NULL

Pairwise plots
Very useful for visually inspecting variables for patterns of interest. You will typically need to know your outcome of interest a priori, i.e. supervised analysis, or you will not know how to color the points.
plot(iris[, -5], col=iris$Species)

Unfortunately there is no simple equivelant for pairwise plots in ggplot2, so we are forced to look at the variabels one at a time, or write our own wrapper function (homework??):
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
geom_point(size = 2)

Exercises
1. Which destinations have the highest average delays?
2. How many flights to Los Angeles (LAX) did each of the legacy carriers (AA, UA, DL or US) have in May from JFK, and what was their average duration?
3. Which plane (check the tail number) flew out of each New York airport the most?
4. What was the shortest flight out of each airport in terms of distance? In terms of duration?
5. Which date should you fly on if you want to have the lowest possible average departure delay? What about arrival delay?
Plot with 
6. Create a time series plot of each of the legacy carriers average departure delay by day and origin airport. It should look something like this:

Now Do It Yourself
7. You have probably had enough of New York City flight times. Now take a look at either the `mtcars` or `mtcars2` data set and execute many of the same commands above while exploring this new, different data set.
Merging Data
No data wrangling tutorial would be complete without a section on merging data. This will happen often. Data will come to you, perhaps from various sources, and you must combine the records (cases, samples, rows, etc.) based on a common indicator variable, usually some sort of key identifier variable (e.g. a name, a patient ID, etc.).
Joining Data
addr <- data.frame(name = c("Alice", "Bob",
"Carol", "dave", "Eve"),
email = c("alice@company.com",
"bob@company.com",
"carol@company.com",
"dave@company.com",
"eve@company.com"),
stringsAsFactors = FALSE)
addr
phone <- data.frame(name = c("Bob", "Carol",
"Eve", "Eve", "Frank"),
phone = c("919 555-1111",
"919 555-2222",
"919 555-3333",
"310 555-3333",
"919 555-4444"),
stringsAsFactors = FALSE)
phone
Outer Join
In dplyr:
full_join(addr, phone)
Joining, by = "name"
In base R:
merge(addr, phone, all = TRUE)
Inner Join
In dplyr:
inner_join(addr, phone)
Joining, by = "name"
In base R:
merge(addr, phone, all = FALSE)
Left Join
In dplyr:
left_join(addr, phone)
Joining, by = "name"
In base R:
merge(addr, phone, all.x = TRUE)
Right Join
In dplyr:
right_join(addr, phone)
Joining, by = "name"
In base R:
merge(addr, phone, all.y = TRUE)
Semi and Anti Joins
semi_join(addr, phone) # semi
Joining, by = "name"
anti_join(addr, phone) # anti
Joining, by = "name"
Special case: many-to-many relationships
addr2 <- data.frame(name = c("Alice", "Alice", "Bob", "Bob"),
email= c("alice@company.com",
"alice@gmail.com",
"bob@company.com",
"bob@hotmail.com"),
stringsAsFactors = FALSE)
phone2 <- data.frame(name = c("Alice", "Alice", "Bob", "Bob"),
phone = c("919 555-1111", "310 555-2222",
"919 555-3333", "310 555-3333"),
stringsAsFactors = FALSE)
In dplyr:
full_join(addr2, phone2, by = "name")
In base R:
merge(addr2, phone2)
Example: Enhancing NYC Flight Data
The nycflights13 package also contains additional information about:
weather |
hourly meterological data for each airport |
planes |
construction information about each plane |
airports |
airport names and locations |
airlines |
translation between two letter carrier codes and names |
Weather and Flight Delays
Let’s take a quick look at the weather data, with an eye towards examining how it might affect things like flight departure delays.
weather
Join by?
Variable defintions in the weather dataset:
origin |
Weather station. Named origin to faciliate merging with flights data |
year, month, day, hour |
Time of recording |
temp, dewp |
Temperature and dewpoint in Fahrenheit |
humid |
Relative humidity |
wind_dir, wind_speed, wind_gust |
Wind direction (in degrees), speed (in mph), and gust speed (in mph) |
precip |
Preciptation, in inches |
pressure |
Sea level pressure in millibars |
visib |
Visibility in miles |
intersect(names(weather), names(flights))
[1] "origin" "year" "month" "day" "hour" "time_hour"
Joining flights and weather
When joining these two data frames I will use an inner join (because I only want the flights that have the weather data, and I only want weather data when there was a flight)
Let’s make a ggplot of the delay time vs. visibility:
flightsw %>%
ggplot(aes(x = visib, y = dep_delay)) +
geom_jitter(alpha = 0.5) + # shake up the points a bit for better viewing
NULL

Let’s take a closer look at the dependent variable -> visibility:
flightsw %>%
ggplot(aes(x = visib)) +
geom_histogram(fill = "blue")

table(flightsw$visib)
0 0.06 0.12 0.25 0.5 0.75 1 1.25 1.5 1.75 2 2.5
110 93 440 1358 1478 496 1474 208 1692 156 3199 2434
3 4 5 6 7 8 9 10
3624 2253 4809 6260 7347 7614 11436 278739
Wow! The vast majority of flights are under clear skies with > 10 miles visibility. Good to know.
flightsw %>%
ggplot(aes(x = visib, y = dep_delay)) +
geom_jitter(shape = 21, color = "blue", fill = "gray",
size = 1.5, width = 0.2, alpha = 0.5) +
geom_smooth(color = "red") +
NULL

Notice a few things:
- There are many negative departure time values (early!)
- The departure time distribution is heavily skewed towards zero
- This makes sense since delays are much more likely than early departures, which means the many points overlap near zero.
- These values stop us from plotting the y-axis on a log-scale (which might give us a better view of the data),
- A boxplot (see here about boxplots) might give us a better representation of the data .
- Let’s remove the negative departure times and replot as a boxplot:
flightsw %>%
filter(dep_delay > 0) %>%
mutate(visib %<>% factor) %>%
ggplot(aes(x = visib, y = dep_delay, group = visib)) +
geom_boxplot(fill = "blue", color = "gray") +
coord_trans(y = "log10") +
xlab("Visibility") +
ylab("Departure Delay") +
NULL

More Exercises
Check some of the other weather variables (e.g. temp, wind_speed, etc) and see if you can find any relationship between them and departure delay (dep_delay).
Merge the flights data with the planes data set (pay attention to what columns are being used for the join). Are older planes more likely to be delayed? What about planes from Airbus vs Boeing vs Embraer?
Acknowledgments
Above materials are derived in part from the following sources:

Created on 2018-11-14 by the Rmarkdown package (v1.10) and R version 3.5.1 (2018-07-02).
LS0tCnRpdGxlOiAiRGF0YSBXcmFuZ2xpbmcgaW4gdGhlIFRpZHl2ZXJzZSIKYXV0aG9yOiAiU2ltb24gVGF2ZW5lciAmIFN0dSBGaWVsZCIKZGF0ZTogImByIGZvcm1hdChTeXMuRGF0ZSgpLCAnJWUgJUIgJVknKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiBubwogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogeWVzCnJhdGlvOiAnOToxNicKdGFibGVzOiB5ZXMKZm9udHNpemU6IDEycHQKLS0tCgoKCiMgU2V0dXAgJiBDb25maWd1cmF0aW9uCgoqIFRoZSBjb2RlIGJlbG93IHdpbGwgbG9hZCB0aGUgbGlicmFyaWVzIHlvdSB3aWxsIG5lZWQgZm9yIHRoaXMgdHV0b3JpYWwuCklmIHlvdSBkbyBub3QgaGF2ZSBvbmUsIHBsZWFzZSBpbnN0YWxsIHdpdGggYGluc3RhbGwucGFja2FnZXMoInBrZyIpYDoKYGBge3Igc2V0dXAsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQpvcHRpb25zKHdhcm5QYXJ0aWFsTWF0Y2hBcmdzID0gRkFMU0UpICAjIGRvbid0IHdhbnQgdGhlc2Ugd2FybmluZ3MKb3B0aW9ucyh3aWR0aCA9IDEwMCkKbGlicmFyeSh0aWJibGUpICAgICAgICAgICAjIHNwZWNpYWwgdHlwZSBvZiBkYXRhIGZyYW1lCmxpYnJhcnkobWFncml0dHIpICAgICAgICAgIyBwaXBlcwpsaWJyYXJ5KGRwbHlyKSAgICAgICAgICAgICMgZGF0YSBtYW5pcHVsYXRpb24KbGlicmFyeShnZ3Bsb3QyKSAgICAgICAgICAjIHByZXR0eSBwbG90cwpsaWJyYXJ5KHRpZHlyKSAgICAgICAgICAgICMgcmVzaGFwZSBkYXRhIGZyYW1lczsgbW9zdGx5IGZvciBnZ3Bsb3RzCmxpYnJhcnkobHVicmlkYXRlKSAgICAgICAgIyB3b3JraW5nIHdpdGggZGF0ZXMvdGltZXMKbGlicmFyeShueWNmbGlnaHRzMTMpICAgICAjIGRhdGEgc2V0IG9mIGZsaWdodCB0aW1lcywgZGVwYXJ0dXJlcywgYW5kIGFycml2YWxzCmBgYAoKKiBUaGUgZGF0YSB3ZSB3aWxsIGJlIHVzaW5nIHRvZGF5IGlzIGF2YWlsYWJsZSB2aWEgdGhlIGBueWNmbGlnaHRzMTNgIHBhY2thZ2UuCiogUiBlbnZpcm9ubWVudDoKICAgICsgIE5vdyBpcyBhIGdvb2QgdGltZSB0byBjaGVjayB0aGF0IHlvdXIgdmVyc2lvbiBvZiBSIGlzIHVwLXRvLWRhdGUuIElmIHlvdSBhcmUgbm90IHJ1bm5pbmcgdmVyc2lvbiBSID49IDMuMi4zIGl0IGlzIHRpbWUgdG8gdXBkYXRlLiBVc2UgYSBmdW5jdGlvbiBmcm9tIHRoZSBgZGV2dG9vbHNgIHBhY2thZ2UgdG8gY2hlY2sgeW91ciBzZXNzaW9uIGluZm86CmBgYHtyLCBldmFsID0gRkFMU0V9CmRldnRvb2xzOjpzZXNzaW9uX2luZm8oKQpgYGAKCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgojIFdoYXQgaXMgRGF0YSBXcmFuZ2xpbmc/CgoqKkRhdGEgd3JhbmdsaW5nKiosIHNvbWV0aW1lcyByZWZlcnJlZCB0byBhcyAqKmRhdGEgbXVuZ2luZyoqLCBpcyB0aGUgcHJvY2VzcyBvZiB0cmFuc2Zvcm1pbmcgYW5kICptYXBwaW5nKiBkYXRhIGZyb20gb25lICJyYXciIGRhdGEgZm9ybSBpbnRvIGFub3RoZXIgZm9ybWF0IHdpdGggdGhlIGludGVudCBvZiBtYWtpbmcgaXQgbW9yZSBhcHByb3ByaWF0ZSBhbmQgdmFsdWFibGUgZm9yIGEgdmFyaWV0eSBvZiBkb3duc3RyZWFtIHB1cnBvc2VzIHN1Y2ggYXMgYW5hbHl0aWNzLiBBIGRhdGEgd3JhbmdsZXIgaXMgYSBwZXJzb24gd2hvIHBlcmZvcm1zIHRoZXNlIHRyYW5zZm9ybWF0aW9uIG9wZXJhdGlvbnMgKHNlZSBbd2lraV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRGF0YV93cmFuZ2xpbmcpKS4KCiMjIyBXaGF0J3MgdGhpcyB0dXRvcmlhbCBhYm91dD8KPGNlbnRlcj4KICA8aW1nIHNyYz0iaW1hZ2VzL2RhdGEtc2NpZW5jZS1leHBsb3JlLnBuZyI+CjwvY2VudGVyPgoKCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgoKCiMgUGlwZXMgKGBtYWdyaXR0cmApICA8aW1nIHNyYz0iaW1hZ2VzL21hZ3JpdHRyLnBuZyIgY2xhc3M9InRpdGxlLWhleCI+ICAhW1BpcGVdKGltYWdlcy9waXBlLmpwZWcpCgpUaGlzIGlzIGFuIGlkZWEgdGhhdCBoYXMgYmVlbiBhcm91bmQgZm9yIGEgbG9uZyB0aW1lLCBwYXJ0aWN1bGFybHkgaW4gdGhlIExpbnV4L1VuaXggZWNvc3lzdGVtLCB3aGVyZSBpdCBpcyBkZW5vdGVkIHdpdGggdGhlICJ8IiBzeW1ib2w6CgoqIENvbXBsZXggdGFza3MgY2FuIGJlIGFjY29tcGxpc2hlZCBieSBzdHJpbmdpbmcgdG9nZXRoZXIgbWFueSBzaW1wbGUgdG9vbHMgLSBzeW50YXggaXMgaW1wb3J0YW50IGZvciBlZmZpY2llbmN5IC8gcmVhZGFiaWxpdHkKKiBUaGUgaWRlYSBvZiBwaXBlcyBpcyB0aGF0IHRoZSByZXN1bHQgb2YgdGhlIHByZXZpb3VzIGNvbW1hbmQgaXMgcGFzc2VkIHRvIHRoZSBuZXh0IGNvbW1hbmQsIHdoaWNoIGFsbG93cyBtYW55IHRvb2xzIHRvIGJlIHN0cnVuZyB0b2dldGhlciAtIGZyb20gbGVmdCB0byByaWdodC4KKiBJbiBSLCBgbWFncml0dHJgIGludHJvZHVjZXMgdGhlIGAlPiVgIHBpcGUgb3BlcmF0b3Igd2hpY2ggcnVucyB0aGUgZXhwcmVzc2lvbiBvZiB0aGUgbGVmdCBhbmQgcGFzc2VzIHRoZSByZXN1bHQgYXMgdGhlICpmaXJzdCogYXJndW1lbnQgdG8gZXhwcmVzc2lvbiBvbiB0aGUgcmlnaHQuCgpZb3UgY2FuIHRoaW5rIGFib3V0IHRoZSBmb2xsb3dpbmcgc2VxdWVuY2Ugb2YgYWN0aW9uczoKCiAgICAxLiBmaW5kIGtleQogICAgMi4gdW5sb2NrIGNhcgogICAgMy4gc3RhcnQgY2FyCiAgICA0LiBkcml2ZSB0byBzY2hvb2wKICAgIDUuIHBhcmsuCgpFeHByZXNzZWQgYXMgYSBzZXQgb2YgbmVzdGVkIGZ1bmN0aW9ucyBpbiBSIGBwc2V1ZG9jb2RlYCB0aGlzIHdvdWxkIGxvb2sgbGlrZToKCmBgYHtyLCBldmFsID0gRkFMU0V9CnBhcmsoZHJpdmUoc3RhcnRfY2FyKGZpbmQoImtleXMiKSksIHRvPSJjYW1wdXMiKSkKYGBgCgpXcml0aW5nIGl0IG91dCB1c2luZyBwaXBlcyBnaXZlIGl0IGEgbW9yZSBuYXR1cmFsIChhbmQgZWFzaWVyIHRvIHJlYWQhKSBzdHJ1Y3R1cmU6CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpmaW5kKCJrZXlzIikgJT4lCiAgICBzdGFydF9jYXIoKSAlPiUKICAgIGRyaXZlKHRvPSJjYW1wdXMiKSAlPiUKICAgIHBhcmsoKQpgYGAKCiMgQXBwcm9hY2hlcwoKQWxsIG9mIHRoZSBmb2xsb3dpbmcgYXJlIGZpbmUsIGl0IGNvbWVzIGRvd24gdG8gcGVyc29uYWwgcHJlZmVyZW5jZSAoYW5kIElNSE8sIHJlYWRhYmlsaXR5KToKCmBgYHtyLCBldmFsID0gRkFMU0V9CiMgTmVzdGVkOgpoKCBnKCBmKHgpLCB5ID0gMSksIHogPSAxICkKCiMgUGlwZWQ6CmYoeCkgJT4lIGcoeSA9IDEpICU+JSBoKHogPSAxKQoKIyBJbnRlcm1lZGlhdGU6CnJlcyA8LSBmKHgpCnJlcyA8LSBnKHJlcywgeSA9IDEpCnJlcyA8LSBoKHJlcywgeiA9IDEpCmBgYAoKCkJ1dCB3aGF0IGFib3V0IG90aGVyIGFyZ3VtZW50cz8gU29tZXRpbWVzIHdlIHdhbnQgdG8gc2VuZCBvdXIgcmVzdWx0cyB0byBhbiBmdW5jdGlvbiBhcmd1bWVudCBvdGhlciB0aGFuIGZpcnN0IG9uZSBvciB3ZSB3YW50IHRvIHVzZSB0aGUgcHJldmlvdXMgcmVzdWx0IGZvciBtdWx0aXBsZSBhcmd1bWVudHMuIEluIHRoZXNlIGNhc2VzIHdlIGNhbiByZWZlciB0byB0aGUgcHJldmlvdXMgcmVzdWx0IHVzaW5nICIuIi4KCmBgYHtyfQpkYXRhLmZyYW1lKGEgPSAxOjMsIGIgPSAzOjEpICU+JSBsbShhIH4gYiwgZGF0YSA9IC4pCmRhdGEuZnJhbWUoYSA9IDE6MywgYiA9IDM6MSkgJT4lIC5bWzFdXQpkYXRhLmZyYW1lKGEgPSAxOjMsIGIgPSAzOjEpICU+JSAuW1tsZW5ndGgoLildXQpgYGAKCgojIFRpYmJsZXMKIyMgTW9kZXJuIGRhdGEgZnJhbWVzCgpIYWRsZXkgV2lja2hhbSBoYXMgYSBwYWNrYWdlIHRoYXQgbW9kaWZpZXMgZGF0YSBmcmFtZXMgdG8gYmUgbW9yZSBtb2Rlcm4sCm9yIGFzIGhlIGNhbGxzIHRoZW0gKnN1cmx5KiBhbmQgKmxhenkqLiBUaGF0IGlzIHRoZXkgZG9uJ3QgY2hhbmdlIHZhcmlhYmxlCm5hbWVzIG9yIHR5cGVzLCBhbmQgZG9uJ3QgZG8gcGFydGlhbCBtYXRjaGluZzsgYW5kIHRoZXkgY29tcGxhaW4gbW9yZQooZS5nLiB3aGVuIGEgdmFyaWFibGUgZG9lcyBub3QgZXhpc3QpLiBUaGlzIGZvcmNlcyB5b3UgdG8gY29uZnJvbnQgcHJvYmxlbXMKZWFybGllciwgdHlwaWNhbGx5IGxlYWRpbmcgdG8gY2xlYW5lciwgbW9yZSBleHByZXNzaXZlIGNvZGUuClRpYmJsZXMgYWxzbyBoYXZlIGFuIGVuaGFuY2VkIGBwcmludCgpYCBtZXRob2Qgd2hpY2ggbWFrZXMgdGhlbSBlYXNpZXIgdG8KdXNlIHdpdGggbGFyZ2UgZGF0YXNldHMgY29udGFpbmluZyBjb21wbGV4IG9iamVjdHMuCgpgYGB7cn0KY2xhc3MoaXJpcykKdGJsX2lyaXMgPC0gYXMudGliYmxlKGlyaXMpCmNsYXNzKHRibF9pcmlzKQpgYGAKCiMjIFByaW50aW5nCmBgYHtyfQp0YmxfaXJpcwpgYGAKCiMjIExhenkgdGliYmxlcwpgYGB7cn0KdGJsX2lyaXNbMSwgXQp0YmxfaXJpc1ssICJTcGVjaWVzIl0KYGBgCgoKIyMgTGF6eSB0aWJibGVzIChmYWN0b3JzKQpgYGB7cn0KdGliYmxlKHggPSAxOjMsIHkgPSBjKCJBIiwgIkIiLCAiQyIpKSAgICMgcmVzcGVjdHMgaW5wdXQgY2xhc3MgYnkgZGVmYXVsdDsgY2hhcmFjdGVyCiMgQW5ub3lpbmdseSwgdGhlIGRlZmF1bHQgb2YgYGRhdGEuZnJhbWUoKWAgaXMgc3RyaW5nc0FzRmFjdG9ycyA9IFRSVUUKZGF0YS5mcmFtZSh4ID0gMTozLCB5ID0gYygiQSIsICJCIiwgIkMiKSwgc3RyaW5nc0FzRmFjdG9ycyA9IFRSVUUpICAgIyBjaGFyYWN0ZXIgLT4gZmFjdG9yIGNsYXNzCmBgYAoKCiMjIFN1cmx5IFRpYmJsZXMKYGBge3IgQmFkVGliYmxlLCBlcnJvciA9IFRSVUV9CnRibF9pcmlzWywgIk5hbWUiXSAgICAgICMgZXJyb3JzIG91dDsgaW52YWxpZCBpbmRleAp0YmxfaXJpc1tbIk5hbWUiXV0gICAgICAjIHNhbWUgYXMgYWJvdmU7IGRpZmZlcmVudCBzeW50YXgKYGBgCgpgYGB7ciBCYWRUaWJibGUyfQppcmlzJE5hbWUgICAgICAgICAgICAgICAjIHJldHVybnMgTlVMTAp0YmxfaXJpcyROYW1lICAgICAgICAgICAjIHJldHVybnMgTlVMTCAod2l0aCBleHBsYW5hdGlvbiEpCmBgYAoKYGBge3IgQmFkVGliYmxlM30KdGJsX2lyaXNbMTYwLCBdICAgICAgICAgIyBleGNlZWRlZCBpbmRleDsgcmV0dXJucyAxIHJvdyBkYXRhIGZyYW1lIG9mIE5BcwpgYGAKCmBgYHtyIEJhZFRpYmJsZTR9CmhlYWQoaXJpcyRTcGVjKSAgICAgICMgcGFydGlhbCBgJGAgbWF0Y2g7IG5vdCBhbHdheXMgYSBnb29kIHRoaW5nIQpoZWFkKHRibF9pcmlzJFNwZWMpICAjIHBhcnRpYWwgYCRgIG1hdGNoIGNvbXBsYWlucyB3aXRoIHdhcm5pbmcoKSwgYXMgaXQgc2hvdWxkIQpgYGAKCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgojIGBkcGx5cmAgPGltZyBzcmM9ImltYWdlcy9kcGx5ci5wbmciIGNsYXNzPSJ0aXRsZS1oZXgiPgojIyBBIEdyYW1tYXIgb2YgRGF0YSBNYW5pcHVsYXRpb24KClRoZSBgZHBseXJgIHBhY2thZ2UgaXMgYmFzZWQgb24gdGhlIGNvbmNlcHRzIG9mIGZ1bmN0aW9ucyBhcyB2ZXJicyB0aGF0IG1hbmlwdWxhdGUgZGF0YSBmcmFtZXMuCgpTaW5nbGUgZGF0YSBmcmFtZSBmdW5jdGlvbnMgLyB2ZXJiczoKCiogYGZpbHRlcigpYDogcGljayByb3dzIG1hdGNoaW5nIGNyaXRlcmlhCiogYHNsaWNlKClgOiBwaWNrIHJvd3MgdXNpbmcgaW5kZXgoZXMpCiogYHNlbGVjdCgpYDogcGljayBhbmQvb3IgcmVvcmRlciBjb2x1bW5zIGJ5IG5hbWUKKiBgcHVsbCgpYDogZ3JhYiBhIGNvbHVtbiBhcyBhIHZlY3RvcgoqIGByZW5hbWUoKWA6IHJlbmFtZSBzcGVjaWZpYyBjb2x1bW5zCiogYGFycmFuZ2UoKWA6IHJlb3JkZXIgcm93cwoqIGBtdXRhdGUoKWA6IGFkZCBuZXcgdmFyaWFibGVzCiogYHRyYW5zbXV0ZSgpYDogY3JlYXRlIG5ldyBkYXRhIGZyYW1lIHdpdGggdmFyaWFibGVzCiogYGRpc3RpbmN0KClgOiBmaWx0ZXIgZm9yIHVuaXF1ZSByb3dzCiogYHNhbXBsZV9uKCkgLyBzYW1wbGVfZnJhYygpYDogcmFuZG9tbHkgc2FtcGxlIHJvd3MKKiBgc3VtbWFyaXNlKClgOiByZWR1Y2UgdmFyaWFibGVzIHRvIHZhbHVlcwoqIC4uLiBhbmQgbWFueSBtb3JlCgoKIyMgZHBseXIgcnVsZXMgZm9yIGZ1bmN0aW9ucwoKICAgIDEuIEZpcnN0IGFyZ3VtZW50IGlzIGFsd2F5cyBhIGRhdGEgZnJhbWUKICAgIDIuIFN1YnNlcXVlbnQgYXJndW1lbnRzIHNheSB3aGF0IHRvIGRvIHdpdGggdGhhdCBkYXRhIGZyYW1lCiAgICAzLiBBbHdheXMgcmV0dXJuIGEgZGF0YSBmcmFtZQogICAgNC4gRG8gbm90IG1vZGlmeSBpbiBwbGFjZQogICAgNS4gUGVyZm9ybWFuY2UgdmlhIGxhenkgZXZhbHVhdGlvbgoKCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCiMgRXhhbXBsZSBEYXRhCldlIHdpbGwgZGVtb25zdHJhdGUgZHBseXIgZnVuY3Rpb25hbGl0eSB1c2luZyB0aGUgYG55Y2ZsaWdodHMxM2AgZGF0YSB3aGljaCBjb250YWlucyBkYXRhIG9uIGZsaWdodHMgZGVwYXJ0aW5nIGZyb20gTmV3IFlvcmsgQ2l0eS4gSXQgd2FzIHdyaXR0ZW4gYnkgSGFkbGV5IFdpY2toYW0gYW5kIGluY2x1ZGVzIGFpcmxpbmUgb24tdGltZSBkYXRhIGZvciBhbGwgZmxpZ2h0cyBkZXBhcnRpbmcgTllDIGluIDIwMTMgYXMgd2VsbCBhcyB1c2VmdWwgKm1ldGFkYXRhKiBvbiBhaXJsaW5lcywgYWlycG9ydHMsIHdlYXRoZXIsIGFuZCBwbGFuZXMgKHNlZSBgP255Y2ZsaWdodHMxM2ApLgoKIyMgU3RydWN0dXJlIG9mIGBmbGlnaHRzYAoKYGBge3IgZmxpZ2h0c19zdHVjdHVyZX0KZmxpZ2h0cyAlPD4lIGFzLnRpYmJsZSAgICAjICdyZWJvdW5kJyBwaXBlOyBha2EgY29tcG91bmQgYXNzaWdubWVudApzdHIoZmxpZ2h0cykKYGBgCgoKIyMgTG9vayBhdCBmdWxsIGB0aWJibGVgIGRhdGEgZnJhbWUKYGBge3IgZmxpZ2h0c190aWJibGV9CmZsaWdodHMKYGBgCgoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCiMgYGRwbHlyYCBWZXJicwojIyBNYXJjaCBmbGlnaHRzIGBkcGx5cjo6ZmlsdGVyYApgYGB7cn0KZmxpZ2h0cyAlPiUgZmlsdGVyKG1vbnRoID09IDMpCmBgYAoKIyMgRmlyc3Qgd2VlayBvZiBNYXJjaCBmbGlnaHRzIGBkcGx5cjo6ZmlsdGVyYApgYGB7cn0KZmxpZ2h0cyAlPiUgZmlsdGVyKG1vbnRoID09IDMsIGRheSA8PSA3KQpgYGAKCiMjIEZsaWdodHMgdG8gTEFYIG9yIFJEVSBpbiBNYXJjaCBgZHBseXI6OmZpbHRlcmAKYGBge3J9CmZsaWdodHMgJT4lIGZpbHRlcihkZXN0ID09ICJMQVgiIHwgZGVzdCA9PSAiUkRVIiwgbW9udGg9PTMpCmBgYAoKIyMgRmlyc3QgMTAgZmxpZ2h0cyBgZHBseXI6OnNsaWNlYApgYGB7cn0KZmxpZ2h0cyAlPiUgc2xpY2UoMToxMCkKYGBgCgojIyBMYXN0IDUgZmxpZ2h0cyBgZHBseXI6OnNsaWNlYApgYGB7cn0KZmxpZ2h0cyAlPiUgc2xpY2UoKG4oKSAtIDUpOm4oKSkKYGBgCgojIyBTZWxlY3QgY29sdW1ucyBgZHBseXI6OnNlbGVjdGAKYGBge3J9CmZsaWdodHMgJT4lIHNlbGVjdCh5ZWFyLCBtb250aCwgZGF5KQpgYGAKCiMjIEV4Y2x1ZGUgY29sdW1ucyBgZHBseXI6OnNlbGVjdGAKYGBge3J9CmZsaWdodHMgJT4lIHNlbGVjdCgteWVhciwgLW1vbnRoLCAtZGF5KQpgYGAKCiMjIFNlbGVjdCByYW5nZXMgYGRwbHlyOjpzZWxlY3RgCmBgYHtyfQpmbGlnaHRzICU+JSBzZWxlY3QoeWVhcjpkYXkpCmBgYAoKIyMgRXhjbHVkZSByYW5nZXMgYGRwbHlyOjpzZWxlY3RgCmBgYHtyfQpmbGlnaHRzICU+JSBzZWxlY3QoLSh5ZWFyOmRheSkpCmBgYAoKIyMgU2VsZWN0IG1hdGNoaW5nIGBkcGx5cjo6c2VsZWN0YApgYGB7cn0KZmxpZ2h0cyAlPiUgc2VsZWN0KGNvbnRhaW5zKCJkZXAiKSwKICAgICAgICAgICAgICAgICAgIGNvbnRhaW5zKCJhcnIiKSkKCmZsaWdodHMgJT4lIHNlbGVjdChzdGFydHNfd2l0aCgiZGVwIiksCiAgICAgICAgICAgICAgICAgICBzdGFydHNfd2l0aCgiYXJyIikpCmBgYAoKIyMgUmVvcmRlciAoTW92ZSkgYGRwbHlyOjpzZWxlY3RgCmBgYHtyfQpmbGlnaHRzICU+JSBzZWxlY3Qob3JpZ2luLCBldmVyeXRoaW5nKCkpICAgICMgbW92ZSBgb3JpZ2luYCB0byBmcm9udApmbGlnaHRzICU+JSBzZWxlY3QoLW9yaWdpbiwgZXZlcnl0aGluZygpKSAgICMgbW92ZSBgb3JpZ2luYCB0byBiYWNrCmBgYAoKCmBgYHtyLCBldmFsID0gRkFMU0V9CiMgVGhlc2UgYXJlIGFsdGVybmF0aXZlcyB0byB0aGUgYWJvdmU7IHdoaWNoIG1heSBzZWVtIGltcG9zc2libGUgYXQgZmlyc3QKIyBTZWUgYmVsb3cgZm9yIHdoeSB0aGlzIHdvcmtzIChgc2VsZWN0KClgIHZzIGByZW5hbWUoKWApCmZsaWdodHMgJT4lIHNlbGVjdChvcmlnaW4sIC1vcmlnaW4pICAgIyBtb3ZlIGBvcmlnaW5gIHRvIGZyb250CmZsaWdodHMgJT4lIHNlbGVjdCgtb3JpZ2luLCBvcmlnaW4pICAgIyBtb3ZlIGBvcmlnaW5gIHRvIGJhY2sKYGBgCgoKIyMgVXNpbmcgYGRwbHlyOjpwdWxsYApgYGB7ciBwdWxsfQpuYW1lcyhmbGlnaHRzKQpmbGlnaHRzICU+JSBwdWxsKCJ5ZWFyIikgJT4lIGhlYWQoKQpmbGlnaHRzICU+JSBwdWxsKDEpICU+JSBoZWFkKCkKZmxpZ2h0cyAlPiUgcHVsbCgtMSkgJT4lIGhlYWQoKQpgYGAKCgojIyBPdGhlciBoZWxwZXJzOgogICAtIGBzdGFydHNfd2l0aCgpYAogICAtIGBlbmRzX3dpdGgoKWAKICAgLSBgY29udGFpbnMoKWAKICAgLSBgbWF0Y2hlcygpYAogICAtIGBudW1fcmFuZ2UoKWAKICAgLSBgb25lX29mKClgCiAgIC0gYGV2ZXJ5dGhpbmcoKWAKICAgLSBgbGFzdF9jb2woKWAKICAgLSBUaGUgYWJvdmUgImhlbHBlcnMiIGNhbiBiZSBmb3VuZCBpbiB0aGUgbmV3LWlzaCBgP3RpZHlzZWxlY3RgIHBhY2thZ2UKICAgICB3aGljaCBzdXBwb3J0cyB0aGUgYGRwbHlyYCBwYWNrYWdlLgoKCgojIyBDaGFuZ2UgY29sdW1uIG5hbWVzIGBkcGx5cjo6cmVuYW1lYApgYGB7cn0KZmxpZ2h0cyAlPiUgcmVuYW1lKHRhaWxfbnVtYmVyID0gdGFpbG51bSkKYGBgCgoKCgojIyBgc2VsZWN0KClgIHZzLiBgcmVuYW1lKClgCllvdSBjYW4gZG8gc29tZSBpbnRlcmVzdGluZyB0aGluZ3Mgd2l0aCBgZHBseXI6OnNlbGVjdCgpYCwgc3VjaCBhcyByZW5hbWluZwoqYW5kKiBzZWxlY3Rpbmcgc2ltdWx0YW5lb3VzbHkuCmBgYHtyfQpmbGlnaHRzICU+JSBzZWxlY3QodGFpbF9udW1iZXIgPSB0YWlsbnVtKQpgYGAKCgpUaGlzIG1pZ2h0IGJlIGNvbmZ1c2luZywgYnV0IGFyZ3VtZW50cyBhcmUgZXZhbHVhdGVkIG9ubHkgYXQgdGhlIGVuZCBvZiB0aGUKY2FsbCwgYW5kIGluIHRoZSBvcmRlciBvZiB0aGUgYXJndW1lbnRzIChpLmUuIGAuLi5gKS4gU28geW91IGNhbiByZWZlciB0byAKdmFyaWFibGVzIHRoYXQgaGF2ZSBiZWVuIHJlbW92ZWQgb3IgcmVuYW1lZCBieSB0aGVpciBvcmlnaW5hbCBuYW1lcy4KVGhpcyBpcyBiZXN0IHNlZW4gaW4gYW4gZXhhbXBsZToKCmBgYHtyfQojIFJlbmFtZSB0YWlsbnVtIC0+IHRhaWxfbnVtYmVyCiMgVGhlbiBzZWxlY3QgdGhlIG5ldyB2YXJpYWJsZSBgdGFpbF9udW1iZXJgCiMgVGhlbiBzZWxlY3QgYWxsIHZhcmlhYmxlcyBleGNlcHQgYHRhaWxudW1gIChidXQgcmVmZXJyaW5nIHRvIHRoZSBvcmlnaW5hbCBuYW1lISkKZmxpZ2h0cyAlPiUgc2VsZWN0KHRhaWxfbnVtYmVyID0gdGFpbG51bSwgLXRhaWxudW0pICAgIyBubyByb3dzCiMgU2VsZWN0IGFsbCB2YXJpYWJsZXMgZXhjZXB0IGB0YWlsbnVtYAojIFRoZW4gcmVuYW1lIHRhaWxudW0gLT4gdGFpbF9udW1iZXIKIyBUaGlzICdhZGRzIGJhY2sgdGhlIHZhcmlhYmxlIHdpdGggYSBuZXcgbmFtZQojIEFuZCByZW9yZGVycyBpdCB0byB0aGUgZmluYWwgY29sdW1uCmZsaWdodHMgJT4lIHNlbGVjdCgtdGFpbG51bSwgdGFpbF9udW1iZXIgPSB0YWlsbnVtKSAgICMgdGFpbG51bSAtPiB0YWlsX251bWJlciArIG1vdmUgdG8gZW5kCmBgYAoKCgojIyBTb3J0IGRhdGEgYGRwbHlyOjphcnJhbmdlYApgYGB7cn0KZmxpZ2h0cyAlPiUgZmlsdGVyKG1vbnRoID09IDMsIGRheSA9PSAyKSAlPiUgYXJyYW5nZShvcmlnaW4sIGRlc3QpCmBgYAoKCiMjIERlc2NlbmRpbmcgb3JkZXIgYGRwbHlyOjphcnJhbmdlYApgYGB7cn0KZmxpZ2h0cyAlPiUgZmlsdGVyKG1vbnRoID09IDMsIGRheSA9PSAyKSAlPiUKICBhcnJhbmdlKGRlc2Mob3JpZ2luKSwgZGVzdCkgJT4lIHNlbGVjdChvcmlnaW4sIGRlc3QsIHRhaWxudW0pCgpgYGAKCiMjIE1vZGlmeSBjb2x1bW5zIGBkcGx5cjo6bXV0YXRlYApgYGB7cn0KZmxpZ2h0cyAlPiUKICAgc2VsZWN0KDE6MykgJT4lCiAgIG11dGF0ZShkYXRlID0gcGFzdGUobW9udGgsIGRheSwgeWVhciwgc2VwPSIvIikgJT4lIG1keSgpKQpgYGAKCiMjIENyZWF0ZSBuZXcgdGliYmxlIGZyb20gZXhpc3RpbmcgY29sdW1ucyBgZHBseXI6OnRyYW5zbXV0ZWAKYGBge3J9CmZsaWdodHMgJT4lCiAgIHNlbGVjdCgxOjMpICU+JQogICB0cmFuc211dGUoZGF0ZSA9IHBhc3RlKG1vbnRoLCBkYXksIHllYXIsIHNlcD0iLyIpICU+JSBtZHkoKSkKYGBgCgojIyBGaW5kIHVuaXF1ZSByb3dzIGBkcGx5cjo6ZGlzdGluY3RgCmBgYHtyfQpmbGlnaHRzICU+JQogICBzZWxlY3Qob3JpZ2luLCBkZXN0KSAlPiUKICAgZGlzdGluY3QoKSAlPiUKICAgYXJyYW5nZShvcmlnaW4sIGRlc3QpCmBgYAoKIyMgU2FtcGxlIHJvd3MgYGRwbHlyOjpzYW1wbGVfbmAKYGBge3J9CmZsaWdodHMgJT4lIHNhbXBsZV9uKDEwKQpgYGAKCiMjIFNhbXBsZSByb3dzIGBkcGx5cjo6c2FtcGxlX2ZyYWNgCmBgYHtyfQpmbGlnaHRzICU+JSBzYW1wbGVfZnJhYygwLjAwMSkKYGBgCgojIyBTdW1tYXJ5IHZpYSBgZHBseXI6OnN1bW1hcmlzZWAKYGBge3J9CmZsaWdodHMgJT4lIG11dGF0ZShkYXRlID0gcGFzdGUobW9udGgsIGRheSwgeWVhciwgc2VwID0gIi8iKSAlPiUgbWR5KCkpICU+JQogICBzdW1tYXJpemUobigpLCBtaW4oZGF0ZSksIG1heChkYXRlKSkKYGBgCgojIyBTZXQgR3JvdXBpbmdzIGBkcGx5cjo6Z3JvdXBfYnlgCmBgYHtyfQojIGdyb3VwaW5nIHBhcmFtZXRlcnMgc3RvcmVkIGluIGF0dHJpYnV0ZXMKZmxpZ2h0cyAlPiUgZ3JvdXBfYnkob3JpZ2luKSAgICAjIGRvZXNuJ3QgYXBwZWFyIGFueSBkaWZmZXJlbnQKZmxpZ2h0cyAlPiUgY2xhc3MoKSAgICAgICAgICAgICAjIHByZS1ncm91cGluZyBpcyBzdGFuZGFyZCBgdGliYmxlYApmbGlnaHRzICU+JSBncm91cF9ieShvcmlnaW4pICU+JSBjbGFzcygpICAjIHJldHVybmVkIG9iamVjdCBpcyBhIG5ldyBjbGFzcyBgZ3JvdXBlZF9kZmAKYGBgCgojIyBTdW1tYXJpemUgZ3JvdXBzIHdpdGggYGRwbHlyOjpncm91cF9ieSgpYCArIGBkcGx5cjo6c3VtbWFyaXNlKClgCmBgYHtyfQojIHRoZSBgbHVicmlkYXRlYCBwYWNrYWdlICh0aWR5dmVyc2UpIGhlbHBzIHdvcmtpbmcgd2l0aCBkYXRlcwpmbGlnaHRzICU+JQogIGdyb3VwX2J5KG9yaWdpbikgJT4lCiAgbXV0YXRlKGRhdGUgPSBwYXN0ZShtb250aCwgZGF5LCB5ZWFyLCBzZXAgPSAiLyIpICU+JSBsdWJyaWRhdGU6Om1keSgpKSAlPiUKICBzdW1tYXJpemUobiA9IG4oKSwgbWluID0gbWluKGRhdGUpLCBtZWRpYW4gPSBtZWRpYW4oZGF0ZSksIG1heCA9IG1heChkYXRlKSkKYGBgCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgojIFBsb3R0aW5nCgpObyBkYXRhIHdyYW5nbGluZyBhbmQvb3IgZGF0YSBleHBsb3JhdGlvbiBpcyBjb21wbGV0ZSB3aXRob3V0IGF0IGxlYXN0IHNvbWUgdmlzdWFsIGV4cGxvcmF0aW9uIG9mIHRoZSBkYXRhLiBBc2tpbmcgeW91cnNlbGY6IHdoYXQgZG8gSSBoYXZlIGhlcmU/IGhvdyBpcyBteSBkYXRhIChwYXJ0aWN1bGFybHkgbXkgY2xhc3MgdmFyaWFibGUgb3IgcmVzcG9uc2UgdmFyaWFibGUpIGRpc3RyaWJ1dGVkPyBBcmUgdGhlcmUgYW55IG1pc3NpbmcgdmFsdWVzPyBOZWdhdGl2ZSB2YWx1ZXM/CgpgYGB7cn0KcGFyKG1mcm93ID0gYygyLCAyKSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgbWFrZSAyeDIgZ3JpZCBmb3IgcGxvdHMKcGFyKG1ncCA9IGMoMiwgMC43NSwgMCksIG1hciA9IGMoMywgNCwgMywgMSkpICAgICMgZ3JhcGhpY3Mgc2V0dGluZ3M7IHNxdWVlemUgbWFyZ2lucwpsYXBwbHkobmFtZXMoaXJpcylbLTVdLCBmdW5jdGlvbihjbGFzcykKICAgYm94cGxvdChzcGxpdChpcmlzW1tjbGFzc11dLCBpcmlzJFNwZWNpZXMpLCBtYWluID0gY2xhc3MsIGNvbCA9IDE6MykpICU+JQogICBpbnZpc2libGUoKSAgICMgcGlwZSB0byBpbnZpc2libGUgdG8gc3VwcHJlc3Mgb3V0cHV0CmBgYAoKIyMgVGhlIHNhbWUgdmlhIDxpbWcgc3JjPSJpbWFnZXMvZ2dwbG90Mi5wbmciIGNsYXNzPSJ0aXRsZS1oZXgiPgpgYGB7cn0KdGhtIDwtIHRoZW1lX2J3KCkgKwogIHRoZW1lKAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3VyID0gTkEpLAogICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvdXIgPSBOQSksCiAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwKICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvdXIgPSBOQSksCiAgICBsZWdlbmQua2V5ID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvdXIgPSBOQSksCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KQogICkKdGhlbWVfc2V0KHRobSkKCmlyaXMgJT4lCiAgdGlkeXI6OmdhdGhlcihrZXkgPSAiRmVhdHVyZSIsIHZhbHVlID0gImNtIiwgLVNwZWNpZXMpICU+JQogIG11dGF0ZShGZWF0dXJlID0gZ3N1YigiXFwuIiwgIiAiLCBGZWF0dXJlKSkgJT4lCiAgZ2dwbG90KGFlcyh5ID0gY20sIHggPSBTcGVjaWVzLCBmaWxsID0gU3BlY2llcykpICsKICBnZW9tX2JveHBsb3QoY29sb3IgPSAiIzFGMzU1MiIsIGFscGhhID0gMC43NSwgc2l6ZSA9IDAuNSkgKwogIHNjYWxlX3hfZGlzY3JldGUobmFtZSA9ICJTcGVjaWVzIikgKwogIGdndGl0bGUoIk92ZXJhbGwgVGl0bGUiKSArCiAgZmFjZXRfd3JhcCh+RmVhdHVyZSwgbmNvbCA9IDIpICsKICBOVUxMCmBgYAoKCiMjIFBhaXJ3aXNlIHBsb3RzCgpWZXJ5IHVzZWZ1bCBmb3IgdmlzdWFsbHkgaW5zcGVjdGluZyB2YXJpYWJsZXMgZm9yIHBhdHRlcm5zIG9mIGludGVyZXN0LiBZb3Ugd2lsbCB0eXBpY2FsbHkgbmVlZCB0byBrbm93IHlvdXIgb3V0Y29tZSBvZiBpbnRlcmVzdCAqYSBwcmlvcmkqLCBpLmUuIHN1cGVydmlzZWQgYW5hbHlzaXMsIG9yIHlvdSB3aWxsIG5vdCBrbm93IGhvdyB0byBjb2xvciB0aGUgcG9pbnRzLgoKYGBge3J9CnBsb3QoaXJpc1ssIC01XSwgY29sPWlyaXMkU3BlY2llcykKYGBgCgpVbmZvcnR1bmF0ZWx5IHRoZXJlIGlzIG5vIHNpbXBsZSBlcXVpdmVsYW50IGZvciBwYWlyd2lzZSBwbG90cyBpbiBgZ2dwbG90MmAsIHNvIHdlIGFyZSBmb3JjZWQgdG8gbG9vayBhdCB0aGUgdmFyaWFiZWxzIG9uZSBhdCBhIHRpbWUsIG9yIHdyaXRlIG91ciBvd24gd3JhcHBlciBmdW5jdGlvbiAoaG9tZXdvcms/Pyk6CgpgYGB7cn0KZ2dwbG90KGlyaXMsIGFlcyh4ID0gU2VwYWwuTGVuZ3RoLCB5ID0gU2VwYWwuV2lkdGgsIGNvbG9yID0gU3BlY2llcykpICsKICBnZW9tX3BvaW50KHNpemUgPSAyKQpgYGAKCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgojIEV4ZXJjaXNlcwoKICAgIDEuIFdoaWNoIGRlc3RpbmF0aW9ucyBoYXZlIHRoZSBoaWdoZXN0IGF2ZXJhZ2UgZGVsYXlzPwoKICAgIDIuIEhvdyBtYW55IGZsaWdodHMgdG8gTG9zIEFuZ2VsZXMgKExBWCkgZGlkIGVhY2ggb2YgdGhlIGxlZ2FjeSBjYXJyaWVycyAoQUEsIFVBLCBETCBvciBVUykgaGF2ZSBpbiBNYXkgZnJvbSBKRkssIGFuZCB3aGF0IHdhcyB0aGVpciBhdmVyYWdlIGR1cmF0aW9uPwoKICAgIDMuIFdoaWNoIHBsYW5lIChjaGVjayB0aGUgdGFpbCBudW1iZXIpIGZsZXcgb3V0IG9mIGVhY2ggTmV3IFlvcmsgYWlycG9ydCB0aGUgbW9zdD8KCiAgICA0LiBXaGF0IHdhcyB0aGUgc2hvcnRlc3QgZmxpZ2h0IG91dCBvZiBlYWNoIGFpcnBvcnQgaW4gdGVybXMgb2YgZGlzdGFuY2U/IEluIHRlcm1zIG9mIGR1cmF0aW9uPwoKICAgIDUuIFdoaWNoIGRhdGUgc2hvdWxkIHlvdSBmbHkgb24gaWYgeW91IHdhbnQgdG8gaGF2ZSB0aGUgbG93ZXN0IHBvc3NpYmxlIGF2ZXJhZ2UgZGVwYXJ0dXJlIGRlbGF5PyBXaGF0IGFib3V0IGFycml2YWwgZGVsYXk/CgoKIyMjIFBsb3Qgd2l0aCA8aW1nIHNyYz0iaW1hZ2VzL2dncGxvdDIucG5nIiBjbGFzcz0idGl0bGUtaGV4Ij4KCiAgICA2LiBDcmVhdGUgYSB0aW1lIHNlcmllcyBwbG90IG9mIGVhY2ggb2YgdGhlIGxlZ2FjeSBjYXJyaWVycyBhdmVyYWdlIGRlcGFydHVyZSBkZWxheSBieSBkYXkgYW5kIG9yaWdpbiBhaXJwb3J0LiBJdCBzaG91bGQgbG9vayBzb21ldGhpbmcgbGlrZSB0aGlzOgoKYGBge3IgZmxpZ2h0c19nZ3Bsb3QsIGVjaG8gPSBGQUxTRX0KZmxpZ2h0cyAlPiUKICBmaWx0ZXIoY2FycmllciA9PSBjKCJBQSIsICJVQSIsICJETCIsICJVUyIpKSAlPiUKICBncm91cF9ieShjYXJyaWVyLCBkYXksIG9yaWdpbikgJT4lCiAgc3VtbWFyaXNlKG4gPSBuKCksIGRlbGF5ID0gbWVhbihkZXBfZGVsYXksIG5hLnJtID0gVFJVRSkpICU+JQogIGdncGxvdChhZXMoeCA9IGRheSwgeSA9IGRlbGF5KSkgKwogICAgZ2VvbV9saW5lKCkgKwogICAgZmFjZXRfd3JhcChjYXJyaWVyIH4gb3JpZ2luKQpgYGAKCiMjIyBOb3cgRG8gSXQgWW91cnNlbGYKCiAgICA3LiBZb3UgaGF2ZSBwcm9iYWJseSBoYWQgZW5vdWdoIG9mIE5ldyBZb3JrIENpdHkgZmxpZ2h0IHRpbWVzLiBOb3cgdGFrZSBhIGxvb2sgYXQgZWl0aGVyIHRoZSBgbXRjYXJzYCBvciBgbXRjYXJzMmAgZGF0YSBzZXQgYW5kIGV4ZWN1dGUgbWFueSBvZiB0aGUgc2FtZSBjb21tYW5kcyBhYm92ZSB3aGlsZSBleHBsb3JpbmcgdGhpcyBuZXcsIGRpZmZlcmVudCBkYXRhIHNldC4KCgoKCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgoKCgojIE1lcmdpbmcgRGF0YQoKTm8gZGF0YSB3cmFuZ2xpbmcgdHV0b3JpYWwgd291bGQgYmUgY29tcGxldGUgd2l0aG91dCBhIHNlY3Rpb24gb24gbWVyZ2luZyBkYXRhLiBUaGlzIHdpbGwgaGFwcGVuIG9mdGVuLiBEYXRhIHdpbGwgY29tZSB0byB5b3UsIHBlcmhhcHMgZnJvbSB2YXJpb3VzIHNvdXJjZXMsIGFuZCB5b3UgbXVzdCBjb21iaW5lIHRoZSByZWNvcmRzIChjYXNlcywgc2FtcGxlcywgcm93cywgZXRjLikgYmFzZWQgb24gYSBjb21tb24gaW5kaWNhdG9yIHZhcmlhYmxlLCB1c3VhbGx5IHNvbWUgc29ydCBvZiAqa2V5KiBpZGVudGlmaWVyIHZhcmlhYmxlIChlLmcuIGEgbmFtZSwgYSBwYXRpZW50IElELCBldGMuKS4KCiMjIERhdGEgbWFuaXB1bGF0aW9uIGlzIG5vdCBjb21wbGV0ZSB3aXRob3V0IG1lcmdpbmcgaW5mb3JtYXRpb24KClR3byB0YWJsZSBmdW5jdGlvbnMgLyB2ZXJicywgYWxsIGZ1bmN0aW9ucyBoYXZlIHRoZSBmb3JtIGBmKGEsIGIpYDoKCnwgICBGdW5jdGlvbiAgICB8ICAgV2hhdCBpdCBkb2VzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCAtLS0tLS0tLS0tLS0tIHwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gfAp8IGBsZWZ0X2pvaW5gICAgfCBKb2luIG1hdGNoaW5nIHJvd3MgZnJvbSBgYmAgdG8gYGFgLCBwcmVzZXJ2aW5nIGFsbCByb3dzIG9mIGBhYCB8CnwgYHJpZ2h0X2pvaW5gICB8IEpvaW4gbWF0Y2hpbmcgcm93cyBmcm9tIGBhYCB0byBgYmAsIHByZXNlcnZpbmcgYWxsIHJvd3Mgb2YgYGJgIHwKfCBgaW5uZXJfam9pbmAgIHwgSm9pbiBkYXRhLCBwcmVzZXJ2aW5nIG9ubHkgcm93cyB3aXRoIGtleXMgaW4gYm90aCBgYWAgYW5kIGBiYCAgfAp8IGBmdWxsX2pvaW5gICAgfCBKb2luIGRhdGEsIHByZXNlcnZpbmcgYWxsIHJvd3MgaW4gYm90aCBgYWAgYW5kIGBiYCAgICAgICAgICAgICB8CnwgYHNlbWlfam9pbmAgICB8IFN1YnNldCByb3dzIGluIGBhYCB0aGF0IGhhdmUgYSBtYXRjaCBpbiBgYmAgICAgICAgICAgICAgICAgICAgIHwKfCBgYW50aV9qb2luYCAgIHwgU3Vic2V0IHJvd3MgaW4gYGFgIHRoYXQgZG8gbm90IGhhdmUgYSBtYXRjaCBpbiBgYmAgICAgICAgICAgICAgfAoKIyMgSm9pbmluZyBEYXRhCgpgYGB7ciBhZGRyfQphZGRyIDwtIGRhdGEuZnJhbWUobmFtZSA9IGMoIkFsaWNlIiwgIkJvYiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2Fyb2wiLCAiZGF2ZSIsICJFdmUiKSwKICAgICAgICAgICAgICAgICAgIGVtYWlsID0gYygiYWxpY2VAY29tcGFueS5jb20iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJib2JAY29tcGFueS5jb20iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjYXJvbEBjb21wYW55LmNvbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImRhdmVAY29tcGFueS5jb20iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJldmVAY29tcGFueS5jb20iKSwKICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKYWRkcgpgYGAKCmBgYHtyIHBob25lfQpwaG9uZSA8LSBkYXRhLmZyYW1lKG5hbWUgPSBjKCJCb2IiLCAiQ2Fyb2wiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFdmUiLCAiRXZlIiwgIkZyYW5rIiksCiAgICAgICAgICAgICAgICAgICAgcGhvbmUgPSBjKCI5MTkgNTU1LTExMTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICI5MTkgNTU1LTIyMjIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICI5MTkgNTU1LTMzMzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICIzMTAgNTU1LTMzMzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICI5MTkgNTU1LTQ0NDQiKSwKICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCnBob25lCmBgYAoKCiMjIE91dGVyIEpvaW4KCkluIGBkcGx5cmA6CmBgYHtyfQpmdWxsX2pvaW4oYWRkciwgcGhvbmUpCmBgYAoKSW4gYGJhc2UgUmA6CmBgYHtyfQptZXJnZShhZGRyLCBwaG9uZSwgYWxsID0gVFJVRSkKYGBgCgojIyBJbm5lciBKb2luCgpJbiBgZHBseXJgOgpgYGB7cn0KaW5uZXJfam9pbihhZGRyLCBwaG9uZSkKYGBgCgpJbiBgYmFzZSBSYDoKYGBge3J9Cm1lcmdlKGFkZHIsIHBob25lLCBhbGwgPSBGQUxTRSkKYGBgCgojIyBMZWZ0IEpvaW4KCkluIGBkcGx5cmA6CmBgYHtyfQpsZWZ0X2pvaW4oYWRkciwgcGhvbmUpCmBgYAoKSW4gYGJhc2UgUmA6CmBgYHtyfQptZXJnZShhZGRyLCBwaG9uZSwgYWxsLnggPSBUUlVFKQpgYGAKCiMjIFJpZ2h0IEpvaW4KCkluIGBkcGx5cmA6CmBgYHtyfQpyaWdodF9qb2luKGFkZHIsIHBob25lKQpgYGAKCkluIGBiYXNlIFJgOgpgYGB7cn0KbWVyZ2UoYWRkciwgcGhvbmUsIGFsbC55ID0gVFJVRSkKYGBgCgoKIyMgU2VtaSBhbmQgQW50aSBKb2lucwoKYGBge3Igc2VtaX0Kc2VtaV9qb2luKGFkZHIsIHBob25lKSAgICMgc2VtaQpgYGAKCmBgYHtyIGFudGl9CmFudGlfam9pbihhZGRyLCBwaG9uZSkgICAjIGFudGkKYGBgCgoKIyMgU3BlY2lhbCBjYXNlOiBtYW55LXRvLW1hbnkgcmVsYXRpb25zaGlwcwoKYGBge3J9CmFkZHIyIDwtIGRhdGEuZnJhbWUobmFtZSA9IGMoIkFsaWNlIiwgIkFsaWNlIiwgIkJvYiIsICJCb2IiKSwKICAgICAgICAgICAgICAgICAgICBlbWFpbD0gYygiYWxpY2VAY29tcGFueS5jb20iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhbGljZUBnbWFpbC5jb20iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJib2JAY29tcGFueS5jb20iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJib2JAaG90bWFpbC5jb20iKSwKICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmBgYAoKYGBge3J9CnBob25lMiA8LSBkYXRhLmZyYW1lKG5hbWUgPSBjKCJBbGljZSIsICJBbGljZSIsICJCb2IiLCAiQm9iIiksCiAgICAgICAgICAgICAgICAgICAgIHBob25lID0gYygiOTE5IDU1NS0xMTExIiwgIjMxMCA1NTUtMjIyMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiOTE5IDU1NS0zMzMzIiwgIjMxMCA1NTUtMzMzMyIpLAogICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmBgYAoKSW4gYGRwbHlyYDoKYGBge3J9CmZ1bGxfam9pbihhZGRyMiwgcGhvbmUyLCBieSA9ICJuYW1lIikKYGBgCgpJbiBgYmFzZSBSYDoKYGBge3J9Cm1lcmdlKGFkZHIyLCBwaG9uZTIpCmBgYAoKCiMjIEV4YW1wbGU6IEVuaGFuY2luZyBOWUMgRmxpZ2h0IERhdGEKClRoZSBgbnljZmxpZ2h0czEzYCBwYWNrYWdlIGFsc28gY29udGFpbnMgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBhYm91dDoKCnwgICBEYXRhc2V0ICAgICB8ICAgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgLS0tLS0tLS0tLS0tLSB8IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8CnwgYHdlYXRoZXJgICAgICB8IGhvdXJseSBtZXRlcm9sb2dpY2FsIGRhdGEgZm9yIGVhY2ggYWlycG9ydCAgICAgICAgICAgICB8CnwgYHBsYW5lc2AgICAgICB8IGNvbnN0cnVjdGlvbiBpbmZvcm1hdGlvbiBhYm91dCBlYWNoIHBsYW5lICAgICAgICAgICAgICB8CnwgYGFpcnBvcnRzYCAgICB8IGFpcnBvcnQgbmFtZXMgYW5kIGxvY2F0aW9ucyAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgYGFpcmxpbmVzYCAgICB8IHRyYW5zbGF0aW9uIGJldHdlZW4gdHdvIGxldHRlciBjYXJyaWVyIGNvZGVzIGFuZCBuYW1lcyB8CgoKIyMgV2VhdGhlciBhbmQgRmxpZ2h0IERlbGF5cwoKTGV0J3MgdGFrZSBhIHF1aWNrIGxvb2sgYXQgdGhlIHdlYXRoZXIgZGF0YSwgd2l0aCBhbiBleWUgdG93YXJkcyBleGFtaW5pbmcgaG93IGl0IG1pZ2h0IGFmZmVjdCB0aGluZ3MgbGlrZSBmbGlnaHQgZGVwYXJ0dXJlIGRlbGF5cy4KCmBgYHtyfQp3ZWF0aGVyCmBgYAoKCiMjIEpvaW4gYnk/CgpWYXJpYWJsZSBkZWZpbnRpb25zIGluIHRoZSBgd2VhdGhlcmAgZGF0YXNldDoKCnwgICBWYXJpYWJsZSAgICB8ICAgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgLS0tLS0tLS0tLS0tLSB8IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8CnwgYG9yaWdpbmAgICAgICB8IFdlYXRoZXIgc3RhdGlvbi4gTmFtZWQgb3JpZ2luIHRvIGZhY2lsaWF0ZSBtZXJnaW5nIHdpdGggZmxpZ2h0cyBkYXRhIHwKfCBgeWVhcmAsIGBtb250aGAsIGBkYXlgLCBgaG91cmAgfCBUaW1lIG9mIHJlY29yZGluZyAgICAgICAgIHwKfCBgdGVtcGAsIGBkZXdwYCB8IFRlbXBlcmF0dXJlIGFuZCBkZXdwb2ludCBpbiBGYWhyZW5oZWl0ICAgIHwKfCBgaHVtaWRgICAgICAgfCBSZWxhdGl2ZSBodW1pZGl0eSAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBgd2luZF9kaXJgLCBgd2luZF9zcGVlZGAsIGB3aW5kX2d1c3RgIHwgV2luZCBkaXJlY3Rpb24gKGluIGRlZ3JlZXMpLCBzcGVlZCAoaW4gbXBoKSwgYW5kIGd1c3Qgc3BlZWQgKGluIG1waCkgfAp8IGBwcmVjaXBgICAgICB8IFByZWNpcHRhdGlvbiwgaW4gaW5jaGVzICAgICAgICAgICAgICAgICAgICAgfAp8IGBwcmVzc3VyZWAgICB8IFNlYSBsZXZlbCBwcmVzc3VyZSBpbiBtaWxsaWJhcnMgICAgICAgICAgICAgfAp8IGB2aXNpYmAgICAgICB8IFZpc2liaWxpdHkgaW4gbWlsZXMgICAgICAgICAgICAgICAgICAgICAgICAgfAoKCgo8IS0tClZhcmlhYmxlIGRlZmludGlvbnMgaW4gdGhlIGBmbGlnaHRzYCBkYXRhc2V0OgoKfCAgIFZhcmlhYmxlICAgICB8ICAgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICB8CnwgLS0tLS0tLS0tLS0tLS0gfCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gfAp8IGB0YWlsbnVtYCAgICAgIHwgVGFpbCBudW1iZXIgICAgICAgICAgICAgICAgICAgICAgIHwKfCBgeWVhcmAgICAgICAgICB8IFllYXIgbWFudWZhY3R1cmVkICAgICAgICAgICAgICAgICB8CnwgYHR5cGVgICAgICAgICAgfCBUeXBlIG9mIHBsYW5lICAgICAgICAgICAgICAgICAgICAgfAp8IGBtYW51ZmFjdHVyZXJgIHwgTWFudWZhY3R1cmVyICAgICAgICAgICAgICAgICAgICAgIHwKfCBgbW9kZWxgICAgICAgICB8IE1vZGVsICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgYGVuZ2luZXNgICAgICAgfCBOdW1iZXIgb2YgZW5naW5lcyAgICAgICAgICAgICAgICAgfAp8IGBzZWF0c2AgICAgICAgIHwgTnVtYmVyIG9mIHNlYXRzICAgICAgICAgICAgICAgICAgIHwKfCBgc3BlZWRgICAgICAgICB8IEF2ZXJhZ2UgY3J1aXNpbmcgc3BlZWQgaW4gbXBoICAgICB8CnwgYGVuZ2luZWAgICAgICAgfCBUeXBlIG9mIGVuZ2luZSAgICAgICAgICAgICAgICAgICAgfAotLT4KCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCmBgYHtyfQppbnRlcnNlY3QobmFtZXMod2VhdGhlciksIG5hbWVzKGZsaWdodHMpKQpgYGAKCgoKCiMjIEpvaW5pbmcgZmxpZ2h0cyBhbmQgd2VhdGhlcgoKV2hlbiBqb2luaW5nIHRoZXNlIHR3byBkYXRhIGZyYW1lcyBJIHdpbGwgdXNlIGFuIGlubmVyIGpvaW4gKGJlY2F1c2UgSSBvbmx5IHdhbnQgdGhlIGZsaWdodHMgdGhhdCBoYXZlIHRoZSB3ZWF0aGVyIGRhdGEsIGFuZCBJIG9ubHkgd2FudCB3ZWF0aGVyIGRhdGEgd2hlbiB0aGVyZSB3YXMgYSBmbGlnaHQpCgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQooZmxpZ2h0c3cgPC0gaW5uZXJfam9pbihmbGlnaHRzLCB3ZWF0aGVyKSkKYGBgCgpMZXQncyBtYWtlIGEgYGdncGxvdGAgb2YgdGhlIGRlbGF5IHRpbWUgdnMuIHZpc2liaWxpdHk6CgpgYGB7cn0KZmxpZ2h0c3cgJT4lCiAgZ2dwbG90KGFlcyh4ID0gdmlzaWIsIHkgPSBkZXBfZGVsYXkpKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjUpICsgICAgIyBzaGFrZSB1cCB0aGUgcG9pbnRzIGEgYml0IGZvciBiZXR0ZXIgdmlld2luZwogIE5VTEwKYGBgCgoKCkxldCdzIHRha2UgYSBjbG9zZXIgbG9vayBhdCB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIC0+IHZpc2liaWxpdHk6CgpgYGB7cn0KZmxpZ2h0c3cgJT4lCiAgZ2dwbG90KGFlcyh4ID0gdmlzaWIpKSArCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJibHVlIikKdGFibGUoZmxpZ2h0c3ckdmlzaWIpCmBgYAoKCldvdyEgVGhlIHZhc3QgbWFqb3JpdHkgb2YgZmxpZ2h0cyBhcmUgdW5kZXIgY2xlYXIgc2tpZXMgd2l0aCBgPiAxMGAgbWlsZXMgdmlzaWJpbGl0eS4gR29vZCB0byBrbm93LgoKYGBge3J9CmZsaWdodHN3ICU+JQogIGdncGxvdChhZXMoeCA9IHZpc2liLCB5ID0gZGVwX2RlbGF5KSkgKwogIGdlb21faml0dGVyKHNoYXBlID0gMjEsIGNvbG9yID0gImJsdWUiLCBmaWxsID0gImdyYXkiLAogICAgICAgICAgICAgIHNpemUgPSAxLjUsIHdpZHRoID0gMC4yLCBhbHBoYSA9IDAuNSkgKwogIGdlb21fc21vb3RoKGNvbG9yID0gInJlZCIpICsKICBOVUxMCmBgYAoKCk5vdGljZSBhIGZldyB0aGluZ3M6CgogICogVGhlcmUgYXJlIG1hbnkgKm5lZ2F0aXZlKiBkZXBhcnR1cmUgdGltZSB2YWx1ZXMgKGVhcmx5ISkKICAqIFRoZSBkZXBhcnR1cmUgdGltZSBkaXN0cmlidXRpb24gaXMgaGVhdmlseSBza2V3ZWQgdG93YXJkcyB6ZXJvIAogICogVGhpcyBtYWtlcyBzZW5zZSBzaW5jZSBkZWxheXMgYXJlIG11Y2ggbW9yZSBsaWtlbHkgdGhhbiBlYXJseSBkZXBhcnR1cmVzLAogICAgd2hpY2ggbWVhbnMgdGhlICoqbWFueSoqIHBvaW50cyBvdmVybGFwIG5lYXIgemVyby4KICAqIFRoZXNlIHZhbHVlcyBzdG9wIHVzIGZyb20gcGxvdHRpbmcgdGhlIHktYXhpcyBvbiBhIGxvZy1zY2FsZSAod2hpY2ggbWlnaHQgCiAgICBnaXZlIHVzIGEgYmV0dGVyIHZpZXcgb2YgdGhlIGRhdGEpLCAKICAqIEEgYm94cGxvdCAoc2VlIFtoZXJlXShodHRwczovL3Rvd2FyZHNkYXRhc2NpZW5jZS5jb20vdW5kZXJzdGFuZGluZy1ib3hwbG90cy01ZTJkZjdiY2JkNTEpIAogICAgYWJvdXQgYm94cGxvdHMpIG1pZ2h0IGdpdmUgdXMgYSBiZXR0ZXIgcmVwcmVzZW50YXRpb24gb2YgdGhlIGRhdGEgLgogICogTGV0J3MgcmVtb3ZlIHRoZSBuZWdhdGl2ZSBkZXBhcnR1cmUgdGltZXMgYW5kIHJlcGxvdCBhcyBhICpib3hwbG90KjoKCmBgYHtyfQpmbGlnaHRzdyAlPiUKICBmaWx0ZXIoZGVwX2RlbGF5ID4gMCkgICU+JQogIG11dGF0ZSh2aXNpYiAlPD4lIGZhY3RvcikgJT4lCiAgZ2dwbG90KGFlcyh4ID0gdmlzaWIsIHkgPSBkZXBfZGVsYXksIGdyb3VwID0gdmlzaWIpKSArCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAiYmx1ZSIsIGNvbG9yID0gImdyYXkiKSArCiAgY29vcmRfdHJhbnMoeSA9ICJsb2cxMCIpICsKICB4bGFiKCJWaXNpYmlsaXR5IikgKwogIHlsYWIoIkRlcGFydHVyZSBEZWxheSIpICsKICBOVUxMCmBgYAoKCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgojIE1vcmUgRXhlcmNpc2VzCgoxLiBDaGVjayBzb21lIG9mIHRoZSBvdGhlciB3ZWF0aGVyIHZhcmlhYmxlcyAoZS5nLiBgdGVtcGAsIGB3aW5kX3NwZWVkYCwgYGV0Y2ApCmFuZCBzZWUgaWYgeW91IGNhbiBmaW5kIGFueSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGVtIGFuZCBkZXBhcnR1cmUKZGVsYXkgKGBkZXBfZGVsYXlgKS4KCjIuIE1lcmdlIHRoZSBgZmxpZ2h0c2AgZGF0YSB3aXRoIHRoZSBgcGxhbmVzYCBkYXRhIHNldCAocGF5IGF0dGVudGlvbiB0bwp3aGF0IGNvbHVtbnMgYXJlIGJlaW5nIHVzZWQgZm9yIHRoZSBqb2luKS4gQXJlIG9sZGVyIHBsYW5lcyBtb3JlIGxpa2VseQp0byBiZSBkZWxheWVkPyBXaGF0IGFib3V0IHBsYW5lcyBmcm9tIEFpcmJ1cyB2cyBCb2VpbmcgdnMgRW1icmFlcj8KCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgojIEFja25vd2xlZGdtZW50cwoKQWJvdmUgbWF0ZXJpYWxzIGFyZSBkZXJpdmVkIGluIHBhcnQgZnJvbSB0aGUgZm9sbG93aW5nIHNvdXJjZXM6CgoqIFtSU3R1ZGlvIERhdGEgV3JhbmdsaW5nIENoZWF0IFNoZWV0XShodHRwOi8vd3d3LnJzdHVkaW8uY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE1LzAyL2RhdGEtd3JhbmdsaW5nLWNoZWF0c2hlZXQucGRmKQoKKiBQYWNrYWdlIFZpZ25ldHRlczoKICAgICsgW2RwbHlyXShodHRwOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9kcGx5ci92aWduZXR0ZXMvaW50cm9kdWN0aW9uLmh0bWwpCiAgICArIFt0aWR5cl0oaHR0cDovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvdGlkeXIvdmlnbmV0dGVzL3RpZHktZGF0YS5odG1sKQoKKiBIYWRsZXkgV2lja2hhbSdzIG5ldyBib29rOgogICAgKyBbUiBmb3IgRGF0YSBTY2llbmNlXShodHRwOi8vcjRkcy5oYWQuY28ubnovKQoKPGltZyBzcmM9ImltYWdlcy9yNGRzLmpwZyIgd2lkdGg9MjAwLCBoZWlnaHQ9NDAwPgoKCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCkNyZWF0ZWQgb24gYHIgU3lzLkRhdGUoKWAgYnkgdGhlIFtSbWFya2Rvd24gcGFja2FnZV0oaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vcm1hcmtkb3duKSAodmByIHV0aWxzOjpwYWNrYWdlVmVyc2lvbigicm1hcmtkb3duIilgKSBhbmQgYHIgUi52ZXJzaW9uJHZlcnNpb24uc3RyaW5nYC4K