Agenda
- Data structures
- vectors, lists, data frame, tibble, etc
- Iteration
for
loop
- vectorize with
apply
family
- (aside): User-defined functions
dplyr::do()
mosaic::do()
- Data intake
- Dates & Times
Announcements
- Office hours posted in Syllabus
- There are a few more changes to watch for in Ch 5 (see below)
- Keep up with Piazza
- Canvas assignments
- MDSR Chap 3 Exercises
- Programming Notebooks: MDSR Chapter 05
MDSR 05 Programming Notebook Tips/Errata
- MDSR Chapter 5 includes several user-defined functions, make sure they are noted in the front-matter
- in other assignments (hwk, projects, etc), user-defined functions should be defined at the beginning per the Style Guide
- Since this style is awkward to do so in programming notebooks, so a comment/note in the front-matter is sufficient for user-defined functions and data sources.
- p. 91: if
hiv_key
fails, try this one: hiv_key <- "14nH2oKdgDMlgjtLsYM98kxyVMVa5XTkUUkuF0ZrIDgM"
- p. 94: you may need to load
babynames
from the babynames
package (although the book doesn’t)
- No programming required
- p. 99
BP_wide
(but do not skip the help(HELPrct)
on p. 98 & Figure 5.2)
- Section 5.2.1 (include section heading as placeholder)
- Section 5.2.2 (include section heading as placeholder)
- Section 5.2.3 (include section heading as placeholder)
- Section 5.3 (include section heading as placeholder)
- Section 5.5.2 (include section heading as placeholder)
- Section 5.6 (include section heading as placeholder, also note all the great packages for accessing cool data sources!)
- p. 115: rename the variable in
bstrap
as “mean” after your simulation
- p. 127: some variable names from the Wikipedia table have changed, you’ll need to correct them
- p. 127-128: Fig 5.10 is close but not identical due to new Wikipedia data (e.g. reactor shut downs)
Section 5.1 through 5.3
- We discussed principles of tidy data last week
- The rows–called cases or observational units–each refer to a specific, unique, and similar sort of thing
- The columns, called variables, each have the same sort of value recorded for each case (i.e. row)
- “Tidy” is not necessarily a singular form for a given data set…
- judgement of tidy or not depends on the research question & corresponding definition of “case”
- data can be converted from one tidy form to another
- However, many forms are objectively NOT tidy with respect to any research question
- Sec 5.2 discusses reshaping data with
spread()
and gather()
- Sec 5.3 Naming conventions & style guides
Data structures in R
- R data structures can be organized by
- dimensionality (1d, 2d, nd)
- tolerance for heterogeneous contents (i.e. contents can be different types or not)
- Five common data types
1d |
Atomic vector |
List |
2d |
Matrix |
Data frame |
nd |
Array |
|
Atomic vectors
- 1d, homogenous
- usually created with
c()
, short for “combine”
- several types
- character (e.g.,
chr_var <- c("some text", "more stuff", "17")
)
- double (e.g.,
dbl_var <- c(1, 2.5, 4.5)
)–often called “numeric”
- integer (e.g.,
int_var <- c(1L, 6L, 10L)
)–Note L suffix for integer rather than double
- logical (e.g.,
log_var <- c(TRUE, FALSE, T, F)
)
- mixed types results in default coercion to most flexible type present
as.numeric()
and other analogous “as.[type]
” functions can force explicit coercion
- commonly use single square bracket to access element(s) of an atomic vector:
[
# mixed types coerce by default
logical_01 <- c(TRUE, FALSE, T, F, 1, "0")
class(logical_01)
[1] "character"
# explicit coercion
as.logical(logical_01)
[1] TRUE FALSE TRUE FALSE NA NA
# access an element
logical_01[3]
[1] "TRUE"
List
- 1d, heterogenous
- elements can be of any type, including lists
- you can turn a list into an atomic vector with
unlist()
following same coercion rules as c()
- lists are used to build up many of the more complicated data structures like data frames & model objects
- commonly use {
[
, [[
, $
} to access element(s) of a list
[
preserves result as a list, and can access multiple elements
[[
simplifies resulting object (e.g., to vector or data frame)
listName$var
shorthand for listName[["var"]]
# define a model object
model <- lm(mpg ~ wt, data = mtcars)
# is it a list (yes)
is.list(model)
# inspect the data structure
str(model)
# accesses "residuals" from list & simplifies result to a numeric atomic vector
class(model$residuals)
Data frames
- most common way of storing data in R
- under the hood, a data frame is a list of equal-length vectors
- result is a 2d structure that shares properties with both the matrix and the list
- access element(s) with
[
by specifying row and column location
- access and simplify column to vector with
$
(or [[
)
Tibbles (vs. data frame)
- essential part of
tidyverse
- a tibble is sort of a lazy version of the data.frame
- it doesn’t change the types of inputs (i.e. doesn’t convert strings to factors…)
- it permits use of the back-tick ( ` ) as a workaround for “illegal” names
- the terms (and structures) can often be used interchangeably
- Two differences:
- Printing… tibbles just print the first ten rows by default (like a built in
head()
feature)
- Subsetting… tibbles don’t do partial matching & there’s an extra step for use of
[
, $
, or [[
Why the tangent about data structures?
- consider the
Teams
data from the Lahman
package
- inspect the data first
- search the help (
?Teams
)
- review the structure
str(Teams)
, head(Teams)
, tail(Teams)
, etc
- What’s a case?
- What’s a variable?
# package
require(Lahman)
# bring into R environment
data("Teams")
# inspect the data s
?Teams # this only works because it came from an R package...
str(Teams)
Bad idea
- calculate the standard deviation for each variable…
- functional but pretty silly
sd(Teams$R, na.rm = TRUE)
sd(Teams$AB, na.rm = TRUE)
sd(Teams$H, na.rm = TRUE)
sd(Teams$X2B, na.rm = TRUE)
sd(Teams$X3B, na.rm = TRUE)
sd(Teams$HR, na.rm = TRUE)
sd(Teams$BB, na.rm = TRUE)
sd(Teams$SO, na.rm = TRUE)
sd(Teams$SB, na.rm = TRUE)
sd(Teams$CS, na.rm = TRUE)
sd(Teams$HBP, na.rm = TRUE)
sd(Teams$SF, na.rm = TRUE)
sd(Teams$RA, na.rm = TRUE)
sd(Teams$ER, na.rm = TRUE)
sd(Teams$ERA, na.rm = TRUE)
sd(Teams$CG, na.rm = TRUE)
sd(Teams$SHO, na.rm = TRUE)
sd(Teams$SV, na.rm = TRUE)
sd(Teams$IPouts, na.rm = TRUE)
sd(Teams$HA, na.rm = TRUE)
sd(Teams$HRA, na.rm = TRUE)
sd(Teams$BBA, na.rm = TRUE)
sd(Teams$SOA, na.rm = TRUE)
sd(Teams$E, na.rm = TRUE)
sd(Teams$DP, na.rm = TRUE)
sd(Teams$FP, na.rm = TRUE)
# and so on...
Better idea
- we’re just doing the same operation for each column in sequence
for
loop?
- algorithmic solution
- easy to follow intuition
- more flexible inputs
# we're interested summarizing several performance metrics in cols 15:40
stDev <- NULL
# simple loop to calculate some averages across specified columns (by index)
for (i in 15:40) {
stDev[i - 14] <- sd(Teams[, i], na.rm = TRUE)
}
# names(stDev) <- names(Teams)[15:40]
stDev
Vectorized operations
- It’s important to understand that the fundamental architecture of R is based on vectors
- general-purpose languages like C++ & python distinguish between single items (e.g. strings & integers) and arrays of those items
- in R, a “string” is just a character vector of length 1
- for this reason, R is optimized for vectorized operations
- this provides an effient alternative to loop-like operations
apply()
family
- popular functions designed to vectorize such operations
apply(X, MARGIN, FUN, ...)
X
: the data
MARGIN
: rows, columns, or element-wise
FUN
: any function you want (including user-defined functions)
...
: allows you to pass arguments to FUN
lapply(X, FUN, ...)
- returns a list of the same length as X,
- each element is the result of applying FUN to the corresponding element of X
sapply(X, FUN, ..., simplify = TRUE, USE.NAMES = TRUE)
:
- sapply is a user-friendly version of lapply
- returns a vector (or matrix) rather than list
vapply()
, tapply()
, mapply()
, etc.
Teams %>%
select(15:40) %>%
apply(MARGIN = 2, FUN = mean, na.rm = TRUE)
Clean up
- note that we utilize a range 3 different times… we can just do that once to clean things up
- is this code easier to read?
x <- df$a
rng <- range(x, na.rm = TRUE)
(x - rng[1]) / (rng[2] - rng[1])
[1] 0.4262272 0.9805607 0.4878048 0.0000000 0.6645891 1.0000000 0.8473460 0.6163415 0.1764453
[10] 0.9060625
Turn it into a function
- So far, we identified the need for a function, and then
- identified inputs,
- cleaned up code, and
- checked that it still works!
- Now let’s turn it into a function
rescale01 <- function(x) {
rng <- range(x, na.rm = TRUE)
(x - rng[1]) / (rng[2] - rng[1])
}
- Good idea to make notes for yourself
rescale01 <- function(x) {
# purpose: rescale a vector so the result is between 0 and 1
# inputs:
### x: a quantitative vector
# outputs:
### result between 0 and 1 for each element after rescaling
rng <- range(x, na.rm = TRUE)
(x - rng[1]) / (rng[2] - rng[1])
}
- use simple data with known result to check that it works
rescale01(c(0, 5, 10))
[1] 0.0 0.5 1.0
rescale01(df$a)
[1] 0.4262272 0.9805607 0.4878048 0.0000000 0.6645891 1.0000000 0.8473460 0.6163415 0.1764453
[10] 0.9060625
rescale01(df$b)
[1] 0.91187779 0.78164740 0.17160848 0.63315767 0.75244635 0.12229037 1.00000000 0.00000000
[9] 0.41888344 0.07823391
rescale01(df$c)
[1] 0.3448485 0.1425352 1.0000000 0.0000000 0.9492493 0.6268511 0.1585132 0.1611796 0.3321792
[10] 0.9015689
rescale01(df$d)
[1] 0.71008921 0.66003298 0.31870668 0.69551833 1.00000000 0.00000000 0.55299273 0.02136117
[9] 0.26321746 0.24569171
- right, we can vectorize any function!
apply(X = df, MARGIN = 2, FUN = rescale01)
a b c d
[1,] 0.4262272 0.91187779 0.3448485 0.71008921
[2,] 0.9805607 0.78164740 0.1425352 0.66003298
[3,] 0.4878048 0.17160848 1.0000000 0.31870668
[4,] 0.0000000 0.63315767 0.0000000 0.69551833
[5,] 0.6645891 0.75244635 0.9492493 1.00000000
[6,] 1.0000000 0.12229037 0.6268511 0.00000000
[7,] 0.8473460 1.00000000 0.1585132 0.55299273
[8,] 0.6163415 0.00000000 0.1611796 0.02136117
[9,] 0.1764453 0.41888344 0.3321792 0.26321746
[10,] 0.9060625 0.07823391 0.9015689 0.24569171
Recap: Writing a new function
- There are three key parts when writing a new function:
- You need to name the function. (in order–1. informative; 2. concise)
- List the inputs, or arguments, to the function.
- Write code for the body of the function
- Note the overall process actually happens in reverse…
- I only made the function after I figured out how to make it work with a simple input.
- It’s easier to start with working code and turn it into a function; it’s MUCH harder to create a function and then try to make it work.
dplyr::do()
- general purpose complement
filter()
, select()
, mutate()
, summarise()
and arrange()
- apply arbitrary function to groups of data
- return either a data frame or arbitrary objects which will be stored in a list.
- Particularly useful when working with models by group
dplyr::do()
Home Run Leaders Example
- We’re interested in identifying the baseball team in each season that led their league in home runs
- a home run (HR) is when a baseball player hits the ball over the opposing team and out of play
- the result is one or more automatic points for the team that hit the home run
- Major League Baseball teams are organized into two “leagues”
- American League (AL)
- National League (NL)
- Goal: write an R function that identifies the team with the most HR’s for each league, in each year
- Suggest some pseudo code to accomplish this for a specific league (AL) and year (2015)
Home Run Leaders: trial run
- Let’s do a trial run first to make sure we have the body of the code working as intended…
require(Lahman)
data("Teams")
Teams <-
as_tibble(Teams) %>%
mutate(lgID = as.character(lgID))
# trial run of the operation
Teams %>%
filter(yearID == 2015, lgID == "AL") %>%
select(yearID, lgID, teamID, HR) %>%
arrange(desc(HR)) %>%
head(1)
Home Run Leaders: write the function
- Goal: write an R function that identifies the team with the most HR’s for each league, in each year
- we’ll want to
group_by(lgID, yearID)
- then perform our calculation on each group
dplyr::do()
is good at that
- we need a function that takes an arbitrary combination of
lgID
and yearID
and returns the HR leader for that group
hr_leader <- function(x) {
# return team with the most home runs given year and league
### x: a subset of Teams for a single year and league
x %>%
select(yearID, lgID, teamID, HR) %>%
arrange(desc(HR)) %>%
head(n = 1)
}
Home Run Leaders: test the function
- Make sure the function matches our previous result for the American League in 2015
Teams %>%
filter(yearID == 2015, lgID == "AL") %>%
dplyr::do(hr_leader(.))
Home Run Leaders: test the function
- All combinations of
lgID
and yearID
- We’ll store the results this time for later use
HrLeaders <-
Teams %>%
group_by(yearID, lgID) %>%
dplyr::do(hr_leader(.))
HrLeaders %>%
arrange(desc(yearID)) %>%
head()
Home Run Leaders: league comparison
- maybe we want to compare the average and maximum among top teams for each league
- we can
group_by(lgID)
and summarise()
HrLeaders %>%
group_by(lgID) %>%
summarise(seasons = n(),
average = mean(HR, na.rm = TRUE),
maximum = max(HR, na.rm = TRUE))
Home Run Leaders: league comparison
mosaic
package has some great tools for this and other purposes
mosaic
prioritizes the idiom: `function(Y ~ X, data = DATASET)
mosaic::favstats()
as an alternative to summary()
require(mosaic)
favstats(HR ~ lgID, data = HrLeaders) # additional grouping variables are easy to include
# base R
summary(HrLeaders)
summary(HrLeaders$HR ~ HrLeaders$lgID)
# apply family can help
tapply(X = HrLeaders$HR, INDEX = HrLeaders$lgID, FUN = summary)
# additional grouping variables take more effort to include...
# Teams %>%
# filter(lgID %in% c("AL", "NL"), yearID > 2010) %>%
Home Run Leaders: league comparison plot
HrLeaders %>%
filter(yearID >= 1920) %>%
ggplot(aes(x = yearID, y = HR, color = lgID)) +
geom_line() +
geom_smooth(se = 0) +
geom_vline(xintercept = 1973) +
annotate(geom = "text", x = 1974, y = 25, label = "AL adopts designated hitter rule", hjust = "left") +
xlab("Year") +
ylab("Most Home Runs by a Single Team") +
ggtitle("Comparison of top home run performance by league and year")
How large is difference between NL and AL home run production?
- Goal: How large is the difference in HR production between leagues since DH rule change?
- The American League made a rule change to allow designated hitters in 1973.
- How might we approach the problem? (what model assumptions are required of our data?)
How large is difference between NL and AL home run production?
- if we use a nonparametric bootstrap we can be less concerned with Normality, but should pay attention to independence
- one way to handle the independence issue is to average over years for each team.
- That’s a pretty crude approach, but it isn’t unreasonable
HrProduction <-
Teams %>%
filter(yearID >= 1973) %>%
select(yearID, lgID, HR) %>%
group_by(yearID, lgID) %>%
summarise(avgHR = mean(HR))
LeagueDiffAvgHR <-
HrProduction %>%
spread(key = lgID, value = avgHR) %>%
mutate(hrAvgDiff = AL - NL)
How large is difference between NL and AL home run production?
p <-
LeagueDiffAvgHR %>%
ggplot(aes(x = hrAvgDiff)) +
geom_density() +
xlim(-20, 50) +
xlab("Difference in average home run production since 1984 (AL - NL)")
p
favstats(~ hrAvgDiff, data = LeagueDiffAvgHR)
Bootstrapping with mosaic::do()
- on average, AL teams hit about 16.5 more home runs per year than NL teams
- need to estimate the uncertainty of our estimate
- desired result is an interval estimate (e.g. confidence interval)
- bootstrapping
LeagueDiffAvgHR
data has 44 observations
- we want to estimate the variability of the mean difference
- we sample WITH replacement from our 44 observations
- we’ll use our distribution of bootstrap means to estimate a confidence interval
# resampling with replacement (10k bootstrap samples)
bootstrap <-
mosaic::do(750) * mean(~ hrAvgDiff, data = resample(LeagueDiffAvgHR))
head(bootstrap)
# 94% confidence interval--using mosaic::qdata()
civals <- qdata(~ mean, p = c(0.03, 0.97), data = bootstrap)
civals
Bootstrap distribution
bootstrap %>%
ggplot() +
geom_histogram(aes(x = mean)) +
geom_vline(data = civals, aes(xintercept = quantile), color = "red", linetype = 3) +
xlab("Bootstrap mean difference in average home run production since 1973 (AL - NL)")
Data intake
some cool packages for data intake
foreign
: tools to read data from other software
feather
: for storing data frames that can be read and written by BOTH R and Python
twitteR
: twitter API
aRxiv
: arXiv API
Rfacebook
: facebook API
instaR
: instagram API
Rflickr
: flickr API (not found?)
tumblR
: tumblr API
Rlinkedin
: LinkedIn API
RSocrata
: API for querying NYC Open Data platform (among other things)
Dates & Times
- Common classes
- “date”
- “time” within a day
- “date-time” (i.e. ) is an instant in time to nearest second (usually)
- Seems like they should be simple, but can get surprisingly complicated if you are being precise
lubridate
package has some excellent tools to help
today()
and now()
come in handy for various reasons
- parse character strings as dates
- managing individual date/time components (e.g., “day”, “hour”)
- working with existing date/time objects
require(lubridate)
today()
[1] "2019-01-18"
now()
[1] "2019-01-18 13:05:43 EST"
Strings as dates (lubridate
)
# functions that parse dates as strings
ymd("2008-06-27")
[1] "2008-06-27"
mdy("May 15th, 2008")
[1] "2008-05-15"
mdy("5/15/2019")
[1] "2019-05-15"
dmy("31-Jan-2017")
[1] "2017-01-31"
# date-time
ymd_hms("2017-01-31 20:11:59")
[1] "2017-01-31 20:11:59 UTC"
Date/time components (lubridate
)
- combine individual elements
make_date()
make_datetime()
require(nycflights13)
data(flights)
# construct datetime of departure
flights %>%
select(year, month, day, hour, minute) %>%
mutate(departure = make_datetime(year, month, day, hour, minute)) %>%
head(3)
Example: UK Nuclear Reactors
- Data Intake (web)
- Manage data structures (list to data frame)
- date handling (investigate failures)
URL <- "http://en.Wikipedia.org/wiki/List_of_nuclear_reactors"
# scrape all tables from page
tableList <-
read_html(URL) %>%
html_nodes(css = "table")
# locate US table by identifying a famous nuclear reactor
relevantTables <- tableList[grep("Oldbury", tableList)]
# parse html into a data frame
reactorsRaw <- html_table(relevantTables[[1]], fill = TRUE)
# first pass clean up
names(reactorsRaw)[c(3, 4, 6, 7)] <- c("Reactor Type", "Reactor Model", "Capacity Net", "Capacity Gross")
reactors <- reactorsRaw[-1, ]
# additional clean up
reactors <-
reactors %>%
rename(capacity_net = `Capacity Net`, capacity_gross = `Capacity Gross`) %>%
mutate(plantstatus = ifelse(grepl("Shut down", reactors$Status),
"Shut down", "Not formally shut down"),
capacity_net = parse_number(capacity_net),
construct_date = dmy(`Construction start`),
operation_date = dmy(`Commercial operation`),
closure_date = dmy(Closure))
reactors %>%
ggplot(aes(x = operation_date, y = capacity_net, color = plantstatus)) +
geom_point() +
# geom_smooth() +
xlab("Operational date") +
ylab("Net plant capacity (MW)")
LS0tCnRpdGxlOiAiTURTUiBDaCA1OiBUaWR5IGRhdGEgYW5kIGl0ZXJhdGlvbiIKb3V0cHV0OiAKICBzbGlkeV9wcmVzZW50YXRpb246IGRlZmF1bHQKICBodG1sX25vdGVib29rOiBkZWZhdWx0Ci0tLQoKCmBgYHtyIEZyb250IE1hdHRlciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQojIGdsb2JhbCBvcHRpb25zCmtuaXRyOjpvcHRzX2NodW5rJHNldChldmFsPUZBTFNFLCBpbmNsdWRlPVRSVUUpCgojIGNsZWFuIHVwIFIgZW52aXJvbm1lbnQKcm0obGlzdCA9IGxzKCkpCgojIGxvYWQgYWxsIHBhY2thZ2VzIGhlcmUKbGlicmFyeShtZHNyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShueWNmbGlnaHRzMTMpCmxpYnJhcnkoTGFobWFuKQpsaWJyYXJ5KHJ2ZXN0KQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKCgojIHVzZXItZGVmaW5lZCBmdW5jdGlvbnMgaGVyZSAoaWYgYW55KQojIyMgYGhyX2xlYWRlcih4KWAgY2FsY3VsYXRlcyB0ZWFtIHdpdGggbW9zdCBIUidzIGZvciBhIGdpdmVuIGxlYWd1ZSBhbmQgeWVhcgoKCiMgaW5wdXRzIChlLmcuIGRhdGEpIHN1bW1hcnkKZGF0YSgiVGVhbXMiKSAgICAgICMgZnJvbSBMYWhtYW4KZGF0YSgiZmxpZ2h0cyIpICAgICMgZnJvbSBueWNmbGlnaHRzMTMKIyMjIGBVSyByZWFjdG9yc2Agc2NyYXBlZCBmcm9tIHdpa2lwZWRpYQoKYGBgCgoKIyMgQWdlbmRhCgotIERhdGEgc3RydWN0dXJlcwogICAgLSB2ZWN0b3JzLCBsaXN0cywgZGF0YSBmcmFtZSwgdGliYmxlLCBldGMKLSBJdGVyYXRpb24KICAgIC0gYGZvcmAgbG9vcAogICAgLSB2ZWN0b3JpemUgd2l0aCBgYXBwbHlgIGZhbWlseQogICAgLSAoYXNpZGUpOiBVc2VyLWRlZmluZWQgZnVuY3Rpb25zCiAgICAtIGBkcGx5cjo6ZG8oKWAKICAgIC0gYG1vc2FpYzo6ZG8oKWAKLSBEYXRhIGludGFrZQotIERhdGVzICYgVGltZXMKCiMjIyMgQW5ub3VuY2VtZW50cwoKLSBPZmZpY2UgaG91cnMgcG9zdGVkIGluIFN5bGxhYnVzCi0gVGhlcmUgYXJlIGEgZmV3IG1vcmUgY2hhbmdlcyB0byB3YXRjaCBmb3IgaW4gQ2ggNSAoc2VlIGJlbG93KQotIEtlZXAgdXAgd2l0aCBQaWF6emEKLSBDYW52YXMgYXNzaWdubWVudHMgCiAgICAtIE1EU1IgQ2hhcCAzIEV4ZXJjaXNlcwogICAgLSBQcm9ncmFtbWluZyBOb3RlYm9va3M6IE1EU1IgQ2hhcHRlciAwNQoKCiMjIyMgTURTUiAwNSBQcm9ncmFtbWluZyBOb3RlYm9vayBUaXBzL0VycmF0YQoKLSBNRFNSIENoYXB0ZXIgNSBpbmNsdWRlcyBzZXZlcmFsIHVzZXItZGVmaW5lZCBmdW5jdGlvbnMsIG1ha2Ugc3VyZSB0aGV5IGFyZSAqKm5vdGVkKiogaW4gdGhlIGZyb250LW1hdHRlcgogICAgLSBpbiBvdGhlciBhc3NpZ25tZW50cyAoaHdrLCBwcm9qZWN0cywgZXRjKSwgdXNlci1kZWZpbmVkIGZ1bmN0aW9ucyBzaG91bGQgYmUgZGVmaW5lZCBhdCB0aGUgYmVnaW5uaW5nIHBlciB0aGUgU3R5bGUgR3VpZGUKICAgIC0gKipTaW5jZSB0aGlzIHN0eWxlIGlzIGF3a3dhcmQgdG8gZG8gc28gaW4gcHJvZ3JhbW1pbmcgbm90ZWJvb2tzLCBzbyBhIGNvbW1lbnQvbm90ZSBpbiB0aGUgZnJvbnQtbWF0dGVyIGlzIHN1ZmZpY2llbnQgZm9yIHVzZXItZGVmaW5lZCBmdW5jdGlvbnMgYW5kIGRhdGEgc291cmNlcy4qKgotIHAuIDkxOiBpZiBgaGl2X2tleWAgZmFpbHMsIHRyeSB0aGlzIG9uZTogYGhpdl9rZXkgPC0gIjE0bkgyb0tkZ0RNbGdqdExzWU05OGt4eVZNVmE1WFRrVVVrdUYwWnJJRGdNImAKLSBwLiA5NDogeW91IG1heSBuZWVkIHRvIGxvYWQgYGJhYnluYW1lc2AgZnJvbSB0aGUgYGJhYnluYW1lc2AgcGFja2FnZSAoYWx0aG91Z2ggdGhlIGJvb2sgZG9lc24ndCkKLSBObyBwcm9ncmFtbWluZyByZXF1aXJlZCAKICAgIC0gcC4gOTkgYEJQX3dpZGVgIChidXQgZG8gbm90IHNraXAgdGhlIGBoZWxwKEhFTFByY3QpYCBvbiBwLiA5OCAmIEZpZ3VyZSA1LjIpCiAgICAtIFNlY3Rpb24gNS4yLjEgKGluY2x1ZGUgc2VjdGlvbiBoZWFkaW5nIGFzIHBsYWNlaG9sZGVyKQogICAgLSBTZWN0aW9uIDUuMi4yIChpbmNsdWRlIHNlY3Rpb24gaGVhZGluZyBhcyBwbGFjZWhvbGRlcikgCiAgICAtIFNlY3Rpb24gNS4yLjMgKGluY2x1ZGUgc2VjdGlvbiBoZWFkaW5nIGFzIHBsYWNlaG9sZGVyKQogICAgLSBTZWN0aW9uIDUuMyAgIChpbmNsdWRlIHNlY3Rpb24gaGVhZGluZyBhcyBwbGFjZWhvbGRlcikKICAgIC0gU2VjdGlvbiA1LjUuMiAoaW5jbHVkZSBzZWN0aW9uIGhlYWRpbmcgYXMgcGxhY2Vob2xkZXIpIAogICAgLSBTZWN0aW9uIDUuNiAoaW5jbHVkZSBzZWN0aW9uIGhlYWRpbmcgYXMgcGxhY2Vob2xkZXIsIGFsc28gbm90ZSBhbGwgdGhlIGdyZWF0IHBhY2thZ2VzIGZvciBhY2Nlc3NpbmcgY29vbCBkYXRhIHNvdXJjZXMhKQotIHAuIDExNTogcmVuYW1lIHRoZSB2YXJpYWJsZSBpbiBgYnN0cmFwYCBhcyAibWVhbiIgYWZ0ZXIgeW91ciBzaW11bGF0aW9uCi0gcC4gMTI3OiBzb21lIHZhcmlhYmxlIG5hbWVzIGZyb20gdGhlIFdpa2lwZWRpYSB0YWJsZSBoYXZlIGNoYW5nZWQsIHlvdSdsbCBuZWVkIHRvIGNvcnJlY3QgdGhlbQotIHAuIDEyNy0xMjg6IEZpZyA1LjEwIGlzIGNsb3NlIGJ1dCBub3QgaWRlbnRpY2FsIGR1ZSB0byBuZXcgV2lraXBlZGlhIGRhdGEgKGUuZy4gcmVhY3RvciBzaHV0IGRvd25zKQoKCgoKIyMgU2VjdGlvbiA1LjEgdGhyb3VnaCA1LjMKCi0gV2UgZGlzY3Vzc2VkIHByaW5jaXBsZXMgb2YgKnRpZHkgZGF0YSogbGFzdCB3ZWVrCiAgICAxLiBUaGUgcm93cy0tY2FsbGVkICpjYXNlcyogb3Igb2JzZXJ2YXRpb25hbCB1bml0cy0tZWFjaCByZWZlciB0byBhIHNwZWNpZmljLCB1bmlxdWUsIGFuZCBzaW1pbGFyIHNvcnQgb2YgdGhpbmcKICAgIDIuIFRoZSBjb2x1bW5zLCBjYWxsZWQgKnZhcmlhYmxlcyosIGVhY2ggaGF2ZSB0aGUgc2FtZSBzb3J0IG9mIHZhbHVlIHJlY29yZGVkIGZvciBlYWNoIGNhc2UgKGkuZS4gcm93KQotICJUaWR5IiBpcyBub3QgbmVjZXNzYXJpbHkgYSBzaW5ndWxhciBmb3JtIGZvciBhIGdpdmVuIGRhdGEgc2V0Li4uIAogICAgLSBqdWRnZW1lbnQgb2YgdGlkeSBvciBub3QgZGVwZW5kcyBvbiB0aGUgcmVzZWFyY2ggcXVlc3Rpb24gJiBjb3JyZXNwb25kaW5nIGRlZmluaXRpb24gb2YgImNhc2UiCiAgICAtIGRhdGEgY2FuIGJlIGNvbnZlcnRlZCBmcm9tIG9uZSB0aWR5IGZvcm0gdG8gYW5vdGhlciAKICAgIC0gSG93ZXZlciwgbWFueSBmb3JtcyBhcmUgb2JqZWN0aXZlbHkgKipOT1QqKiB0aWR5IHdpdGggcmVzcGVjdCB0byBhbnkgcmVzZWFyY2ggcXVlc3Rpb24KLSBTZWMgNS4yIGRpc2N1c3NlcyByZXNoYXBpbmcgZGF0YSB3aXRoIGBzcHJlYWQoKWAgYW5kIGBnYXRoZXIoKWAKLSBTZWMgNS4zIE5hbWluZyBjb252ZW50aW9ucyAmIHN0eWxlIGd1aWRlcwoKCgojIyBEYXRhIHN0cnVjdHVyZXMgaW4gUgoKLSBSIGRhdGEgc3RydWN0dXJlcyBjYW4gYmUgb3JnYW5pemVkIGJ5IAogICAgLSBkaW1lbnNpb25hbGl0eSAoMWQsIDJkLCBuZCkgCiAgICAtIHRvbGVyYW5jZSBmb3IgaGV0ZXJvZ2VuZW91cyBjb250ZW50cyAoaS5lLiBjb250ZW50cyBjYW4gYmUgZGlmZmVyZW50IHR5cGVzIG9yIG5vdCkKLSBGaXZlIGNvbW1vbiBkYXRhIHR5cGVzCgp8ICAgIHwgSG9tb2dlbmVvdXMgICB8IEhldGVyb2dlbmVvdXMgfAp8Oi0tLXw6LS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tfAp8IDFkIHwgQXRvbWljIHZlY3RvciB8IExpc3QgICAgICAgICAgfCAKfCAyZCB8IE1hdHJpeCAgICAgICAgfCBEYXRhIGZyYW1lICAgIHwKfCBuZCB8IEFycmF5ICAgICAgICAgfCAgICAgICAgICAgICAgIHwKCiMjIEF0b21pYyB2ZWN0b3JzCgotIDFkLCBob21vZ2Vub3VzCi0gdXN1YWxseSBjcmVhdGVkIHdpdGggYGMoKWAsIHNob3J0IGZvciAiY29tYmluZSIKLSBzZXZlcmFsIHR5cGVzCiAgICAtIGNoYXJhY3RlciAoZS5nLiwgYGNocl92YXIgPC0gYygic29tZSB0ZXh0IiwgIm1vcmUgc3R1ZmYiLCAiMTciKWApCiAgICAtIGRvdWJsZSAoZS5nLiwgYGRibF92YXIgPC0gYygxLCAyLjUsIDQuNSlgKS0tb2Z0ZW4gY2FsbGVkICJudW1lcmljIgogICAgLSBpbnRlZ2VyIChlLmcuLCBgaW50X3ZhciA8LSBjKDFMLCA2TCwgMTBMKWApLS1Ob3RlIEwgc3VmZml4IGZvciBpbnRlZ2VyIHJhdGhlciB0aGFuIGRvdWJsZQogICAgLSBsb2dpY2FsIChlLmcuLCBgbG9nX3ZhciA8LSBjKFRSVUUsIEZBTFNFLCBULCBGKWApCi0gbWl4ZWQgdHlwZXMgcmVzdWx0cyBpbiBkZWZhdWx0IGNvZXJjaW9uIHRvIG1vc3QgZmxleGlibGUgdHlwZSBwcmVzZW50Ci0gYGFzLm51bWVyaWMoKWAgYW5kIG90aGVyIGFuYWxvZ291cyAiYGFzLlt0eXBlXWAiIGZ1bmN0aW9ucyBjYW4gZm9yY2UgZXhwbGljaXQgY29lcmNpb24KLSBjb21tb25seSB1c2Ugc2luZ2xlIHNxdWFyZSBicmFja2V0IHRvIGFjY2VzcyBlbGVtZW50KHMpIG9mIGFuIGF0b21pYyB2ZWN0b3I6IGBbYAoKYGBge3IgZXZhbD1UUlVFfQojIG1peGVkIHR5cGVzIGNvZXJjZSBieSBkZWZhdWx0CmxvZ2ljYWxfMDEgPC0gYyhUUlVFLCBGQUxTRSwgVCwgRiwgMSwgIjAiKQpjbGFzcyhsb2dpY2FsXzAxKQoKIyBleHBsaWNpdCBjb2VyY2lvbgphcy5sb2dpY2FsKGxvZ2ljYWxfMDEpCgojIGFjY2VzcyBhbiBlbGVtZW50CmxvZ2ljYWxfMDFbM10KYGBgCgoKIyMgTGlzdAoKLSAxZCwgaGV0ZXJvZ2Vub3VzCi0gZWxlbWVudHMgY2FuIGJlIG9mIGFueSB0eXBlLCBpbmNsdWRpbmcgbGlzdHMKLSB5b3UgY2FuIHR1cm4gYSBsaXN0IGludG8gYW4gYXRvbWljIHZlY3RvciB3aXRoIGB1bmxpc3QoKWAgZm9sbG93aW5nIHNhbWUgY29lcmNpb24gcnVsZXMgYXMgYGMoKWAKLSBsaXN0cyBhcmUgdXNlZCB0byBidWlsZCB1cCBtYW55IG9mIHRoZSBtb3JlIGNvbXBsaWNhdGVkIGRhdGEgc3RydWN0dXJlcyBsaWtlIGRhdGEgZnJhbWVzICYgbW9kZWwgb2JqZWN0cwotIGNvbW1vbmx5IHVzZSB7YFtgLCBgW1tgLCBgJGB9IHRvIGFjY2VzcyBlbGVtZW50KHMpIG9mIGEgbGlzdAogICAgLSBgW2AgcHJlc2VydmVzIHJlc3VsdCBhcyBhIGxpc3QsIGFuZCBjYW4gYWNjZXNzIG11bHRpcGxlIGVsZW1lbnRzCiAgICAtIGBbW2Agc2ltcGxpZmllcyByZXN1bHRpbmcgb2JqZWN0IChlLmcuLCB0byB2ZWN0b3Igb3IgZGF0YSBmcmFtZSkKICAgIC0gYGxpc3ROYW1lJHZhcmAgc2hvcnRoYW5kIGZvciBgbGlzdE5hbWVbWyJ2YXIiXV1gCgpgYGB7cn0KIyBkZWZpbmUgYSBtb2RlbCBvYmplY3QKbW9kZWwgPC0gbG0obXBnIH4gd3QsIGRhdGEgPSBtdGNhcnMpCgojIGlzIGl0IGEgbGlzdCAoeWVzKQppcy5saXN0KG1vZGVsKQoKIyBpbnNwZWN0IHRoZSBkYXRhIHN0cnVjdHVyZQpzdHIobW9kZWwpCgojIGFjY2Vzc2VzICJyZXNpZHVhbHMiIGZyb20gbGlzdCAmIHNpbXBsaWZpZXMgcmVzdWx0IHRvIGEgbnVtZXJpYyBhdG9taWMgdmVjdG9yCmNsYXNzKG1vZGVsJHJlc2lkdWFscykKYGBgCgoKIyMgRGF0YSBmcmFtZXMKCi0gbW9zdCBjb21tb24gd2F5IG9mIHN0b3JpbmcgZGF0YSBpbiBSCi0gdW5kZXIgdGhlIGhvb2QsIGEgZGF0YSBmcmFtZSBpcyBhIGxpc3Qgb2YgZXF1YWwtbGVuZ3RoIHZlY3RvcnMKLSByZXN1bHQgaXMgYSAyZCBzdHJ1Y3R1cmUgdGhhdCBzaGFyZXMgcHJvcGVydGllcyB3aXRoIGJvdGggdGhlIG1hdHJpeCBhbmQgdGhlIGxpc3QKICAgIC0gYWNjZXNzIGVsZW1lbnQocykgd2l0aCBgW2AgYnkgc3BlY2lmeWluZyByb3cgYW5kIGNvbHVtbiBsb2NhdGlvbgogICAgLSBhY2Nlc3MgYW5kIHNpbXBsaWZ5IGNvbHVtbiB0byB2ZWN0b3Igd2l0aCBgJGAgKG9yIGBbW2ApCgojIyMjIFRpYmJsZXMgKHZzLiBkYXRhIGZyYW1lKQoKLSBlc3NlbnRpYWwgcGFydCBvZiBgdGlkeXZlcnNlYAotIGEgdGliYmxlIGlzIHNvcnQgb2YgYSBsYXp5IHZlcnNpb24gb2YgdGhlIGRhdGEuZnJhbWUKICAgIC0gaXQgZG9lc24ndCBjaGFuZ2UgdGhlIHR5cGVzIG9mIGlucHV0cyAoaS5lLiBkb2Vzbid0IGNvbnZlcnQgc3RyaW5ncyB0byBmYWN0b3JzLi4uKQogICAgLSBpdCBwZXJtaXRzIHVzZSBvZiB0aGUgYmFjay10aWNrICggYCApIGFzIGEgd29ya2Fyb3VuZCBmb3IgImlsbGVnYWwiIG5hbWVzCi0gdGhlIHRlcm1zIChhbmQgc3RydWN0dXJlcykgY2FuIG9mdGVuIGJlIHVzZWQgaW50ZXJjaGFuZ2VhYmx5Ci0gVHdvIGRpZmZlcmVuY2VzOiAKICAgIC0gUHJpbnRpbmcuLi4gdGliYmxlcyBqdXN0IHByaW50IHRoZSBmaXJzdCB0ZW4gcm93cyBieSBkZWZhdWx0IChsaWtlIGEgYnVpbHQgaW4gYGhlYWQoKWAgZmVhdHVyZSkKICAgIC0gU3Vic2V0dGluZy4uLiB0aWJibGVzIGRvbid0IGRvIHBhcnRpYWwgbWF0Y2hpbmcgJiB0aGVyZSdzIGFuIGV4dHJhIHN0ZXAgZm9yIHVzZSBvZiBgW2AsIGAkYCwgb3IgYFtbYAoKCiMjIFdoeSB0aGUgdGFuZ2VudCBhYm91dCBkYXRhIHN0cnVjdHVyZXM/ICAKCi0gY29uc2lkZXIgdGhlIGBUZWFtc2AgZGF0YSBmcm9tIHRoZSBgTGFobWFuYCBwYWNrYWdlCi0gaW5zcGVjdCB0aGUgZGF0YSBmaXJzdAogICAgLSBzZWFyY2ggdGhlIGhlbHAgKGA/VGVhbXNgKQogICAgLSByZXZpZXcgdGhlIHN0cnVjdHVyZSBgc3RyKFRlYW1zKWAsIGBoZWFkKFRlYW1zKWAsIGB0YWlsKFRlYW1zKWAsIGV0YwogICAgLSBXaGF0J3MgYSBjYXNlPwogICAgLSBXaGF0J3MgYSB2YXJpYWJsZT8KCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9VFJVRX0KIyBwYWNrYWdlCnJlcXVpcmUoTGFobWFuKQoKIyBicmluZyBpbnRvIFIgZW52aXJvbm1lbnQKZGF0YSgiVGVhbXMiKQoKIyBpbnNwZWN0IHRoZSBkYXRhIHMKP1RlYW1zICAgICAgIyB0aGlzIG9ubHkgd29ya3MgYmVjYXVzZSBpdCBjYW1lIGZyb20gYW4gUiBwYWNrYWdlLi4uIApzdHIoVGVhbXMpCmBgYAoKCiMjIENvbXBhcmlzb24gb2YgdGVhbSBwZXJmb3JtYW5jZSBtZXRyaWNzCgotIFJlc2VhcmNoIHF1ZXN0aW9ucwogICAgLSBIb3cgc2ltaWxhciBhcmUgTUxCIHRlYW1zIHdpdGggcmVzcGVjdCB0byB0aGVzZSBtZXRyaWNzPwogICAgLSBEbyBzb21lIG1ldHJpY3Mgd2lkZWx5IHZhcnkgYW1vbmcgTUxCIHRlYW1zPwotIEhvdyB0byBhcHByb2FjaCB0aGUgcHJvYmxlbT8KCiMjIEJhZCBpZGVhCgotIGNhbGN1bGF0ZSB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIGZvciBlYWNoIHZhcmlhYmxlLi4uCi0gZnVuY3Rpb25hbCBidXQgcHJldHR5IHNpbGx5CgpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPVRSVUV9CgpzZChUZWFtcyRSLCBuYS5ybSA9IFRSVUUpCnNkKFRlYW1zJEFCLCBuYS5ybSA9IFRSVUUpCnNkKFRlYW1zJEgsIG5hLnJtID0gVFJVRSkKc2QoVGVhbXMkWDJCLCBuYS5ybSA9IFRSVUUpCnNkKFRlYW1zJFgzQiwgbmEucm0gPSBUUlVFKQpzZChUZWFtcyRIUiwgbmEucm0gPSBUUlVFKQpzZChUZWFtcyRCQiwgbmEucm0gPSBUUlVFKQpzZChUZWFtcyRTTywgbmEucm0gPSBUUlVFKQpzZChUZWFtcyRTQiwgbmEucm0gPSBUUlVFKQpzZChUZWFtcyRDUywgbmEucm0gPSBUUlVFKQpzZChUZWFtcyRIQlAsIG5hLnJtID0gVFJVRSkKc2QoVGVhbXMkU0YsIG5hLnJtID0gVFJVRSkKc2QoVGVhbXMkUkEsIG5hLnJtID0gVFJVRSkKc2QoVGVhbXMkRVIsIG5hLnJtID0gVFJVRSkKc2QoVGVhbXMkRVJBLCBuYS5ybSA9IFRSVUUpCnNkKFRlYW1zJENHLCBuYS5ybSA9IFRSVUUpCnNkKFRlYW1zJFNITywgbmEucm0gPSBUUlVFKQpzZChUZWFtcyRTViwgbmEucm0gPSBUUlVFKQpzZChUZWFtcyRJUG91dHMsIG5hLnJtID0gVFJVRSkKc2QoVGVhbXMkSEEsIG5hLnJtID0gVFJVRSkKc2QoVGVhbXMkSFJBLCBuYS5ybSA9IFRSVUUpCnNkKFRlYW1zJEJCQSwgbmEucm0gPSBUUlVFKQpzZChUZWFtcyRTT0EsIG5hLnJtID0gVFJVRSkKc2QoVGVhbXMkRSwgbmEucm0gPSBUUlVFKQpzZChUZWFtcyREUCwgbmEucm0gPSBUUlVFKQpzZChUZWFtcyRGUCwgbmEucm0gPSBUUlVFKQoKIyBhbmQgc28gb24uLi4KCmBgYAoKIyMgQmV0dGVyIGlkZWEKCi0gd2UncmUganVzdCBkb2luZyB0aGUgc2FtZSBvcGVyYXRpb24gKipmb3IqKiBlYWNoIGNvbHVtbiBpbiBzZXF1ZW5jZQotIGBmb3JgIGxvb3A/CiAgICAtIGFsZ29yaXRobWljIHNvbHV0aW9uCiAgICAtIGVhc3kgdG8gZm9sbG93IGludHVpdGlvbgogICAgLSBtb3JlIGZsZXhpYmxlIGlucHV0cwoKCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9VFJVRX0KIyB3ZSdyZSBpbnRlcmVzdGVkIHN1bW1hcml6aW5nIHNldmVyYWwgcGVyZm9ybWFuY2UgbWV0cmljcyBpbiBjb2xzIDE1OjQwCnN0RGV2IDwtIE5VTEwKCiMgc2ltcGxlIGxvb3AgdG8gY2FsY3VsYXRlIHNvbWUgYXZlcmFnZXMgYWNyb3NzIHNwZWNpZmllZCBjb2x1bW5zIChieSBpbmRleCkKZm9yIChpIGluIDE1OjQwKSB7CiAgc3REZXZbaSAtIDE0XSA8LSBzZChUZWFtc1ssIGldLCBuYS5ybSA9IFRSVUUpCn0KCiMgbmFtZXMoc3REZXYpIDwtIG5hbWVzKFRlYW1zKVsxNTo0MF0KCnN0RGV2CmBgYAoKIyMgVmVjdG9yaXplZCBvcGVyYXRpb25zCgotIEl0J3MgaW1wb3J0YW50IHRvIHVuZGVyc3RhbmQgdGhhdCB0aGUgZnVuZGFtZW50YWwgYXJjaGl0ZWN0dXJlIG9mIFIgaXMgYmFzZWQgb24gKnZlY3RvcnMqCi0gZ2VuZXJhbC1wdXJwb3NlIGxhbmd1YWdlcyBsaWtlIEMrKyAmIHB5dGhvbiBkaXN0aW5ndWlzaCBiZXR3ZWVuIHNpbmdsZSBpdGVtcyAoZS5nLiBzdHJpbmdzICYgaW50ZWdlcnMpIGFuZCBhcnJheXMgb2YgdGhvc2UgaXRlbXMKLSBpbiBSLCBhICJzdHJpbmciIGlzIGp1c3QgYSBjaGFyYWN0ZXIgdmVjdG9yIG9mIGxlbmd0aCAxCi0gZm9yIHRoaXMgcmVhc29uLCBSIGlzIG9wdGltaXplZCBmb3IgdmVjdG9yaXplZCBvcGVyYXRpb25zIAotIHRoaXMgcHJvdmlkZXMgYW4gZWZmaWVudCBhbHRlcm5hdGl2ZSB0byBsb29wLWxpa2Ugb3BlcmF0aW9ucwoKIyMgYGFwcGx5KClgIGZhbWlseQoKLSBwb3B1bGFyIGZ1bmN0aW9ucyBkZXNpZ25lZCB0byB2ZWN0b3JpemUgc3VjaCBvcGVyYXRpb25zCi0gYGFwcGx5KFgsIE1BUkdJTiwgRlVOLCAuLi4pYAogICAgLSBgWGA6IHRoZSBkYXRhCiAgICAtIGBNQVJHSU5gOiByb3dzLCBjb2x1bW5zLCBvciBlbGVtZW50LXdpc2UKICAgIC0gYEZVTmA6ICoqYW55KiogZnVuY3Rpb24geW91IHdhbnQgKGluY2x1ZGluZyB1c2VyLWRlZmluZWQgZnVuY3Rpb25zKSAKICAgIC0gYC4uLmA6IGFsbG93cyB5b3UgdG8gcGFzcyBhcmd1bWVudHMgdG8gRlVOCi0gYGxhcHBseShYLCBGVU4sIC4uLilgIAogICAgLSAqKnJldHVybnMgYSBsaXN0Kiogb2YgdGhlIHNhbWUgbGVuZ3RoIGFzIFgsIAogICAgLSBlYWNoIGVsZW1lbnQgaXMgdGhlIHJlc3VsdCBvZiBhcHBseWluZyBGVU4gdG8gdGhlIGNvcnJlc3BvbmRpbmcgZWxlbWVudCBvZiBYCi0gYHNhcHBseShYLCBGVU4sIC4uLiwgc2ltcGxpZnkgPSBUUlVFLCBVU0UuTkFNRVMgPSBUUlVFKWA6IAogICAgLSBzYXBwbHkgaXMgYSB1c2VyLWZyaWVuZGx5IHZlcnNpb24gb2YgbGFwcGx5CiAgICAtICoqcmV0dXJucyBhIHZlY3RvcioqIChvciBtYXRyaXgpIHJhdGhlciB0aGFuIGxpc3QKLSBgdmFwcGx5KClgLCBgdGFwcGx5KClgLCBgbWFwcGx5KClgLCBldGMuCgpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPVRSVUV9ClRlYW1zICU+JQogIHNlbGVjdCgxNTo0MCkgJT4lCiAgYXBwbHkoTUFSR0lOID0gMiwgRlVOID0gbWVhbiwgbmEucm0gPSBUUlVFKQpgYGAKCi0tLQoKIyMgQW5vdGhlciBTaWRlYmFyICh1c2VyLWRlZmluZWQgZnVuY3Rpb25zKQoKIVtdKHhrY2RfZ2VuZXJhbCBwcm9ibGVtLnBuZykKCi0gV2hlbj8gY29uc2lkZXIgd3JpdGluZyBhIGZ1bmN0aW9uIHdoZW5ldmVyIHlvdeKAmXZlIGNvcGllZCBhbmQgcGFzdGVkIGEgYmxvY2sgb2YgY29kZSBtb3JlIHRoYW4gdHdpY2UgKGkuZS4geW91IG5vdyBoYXZlIHRocmVlIGNvcGllcyBvZiB0aGUgc2FtZSBjb2RlKS4gCiAgICAtIEV4YW1wbGU6IHdlIHdhbnQgdG8gcmVzY2FsZSBzb21lIGRhdGEgc28gdGhlIHJlc3VsdCBpcyBiZXR3ZWVuIDAgYW5kIDEgCiAgICAtIEhlcmUncyBzb21lIGNvZGUgKC4uLmluY2x1ZGluZyBhIGNvbW1vbiBlcnJvcik6IAoKYGBge3IgZXZhbD1UUlVFfQojIHNvbWUgc2ltdWxhdGVkIGRhdGEKZGYgPC0gdGliYmxlOjp0aWJibGUoCiAgYSA9IHJub3JtKDEwKSwKICBiID0gcm5vcm0oMTApLAogIGMgPSBybm9ybSgxMCksCiAgZCA9IHJub3JtKDEwKQopCmBgYAoKCmBgYHtyfQojIHJlc2NhbGUgZWFjaCB2YXJpYWJsZSBzbyB0aGUgcmVzdWx0IGlzIGJldHdlZW4gMCBhbmQgMSAKZGYkYSA8LSAKICAoZGYkYSAtIG1pbihkZiRhLCBuYS5ybSA9IFRSVUUpKSAvIAogIChtYXgoZGYkYSwgbmEucm0gPSBUUlVFKSAtIG1pbihkZiRhLCBuYS5ybSA9IFRSVUUpKQpkZiRiIDwtIChkZiRiIC0gbWluKGRmJGIsIG5hLnJtID0gVFJVRSkpIC8gCiAgKG1heChkZiRiLCBuYS5ybSA9IFRSVUUpIC0gbWluKGRmJGEsIG5hLnJtID0gVFJVRSkpCmRmJGMgPC0gKGRmJGMgLSBtaW4oZGYkYywgbmEucm0gPSBUUlVFKSkgLyAKICAobWF4KGRmJGMsIG5hLnJtID0gVFJVRSkgLSBtaW4oZGYkYywgbmEucm0gPSBUUlVFKSkKZGYkZCA8LSAoZGYkZCAtIG1pbihkZiRkLCBuYS5ybSA9IFRSVUUpKSAvIAogIChtYXgoZGYkZCwgbmEucm0gPSBUUlVFKSAtIG1pbihkZiRkLCBuYS5ybSA9IFRSVUUpKQpgYGAKCgojIyBJZGVudGlmeSBJbnB1dHMgCgotIGZpcnN0IGluc3BlY3QgdGhlIGNvZGUuLi4gSG93IG1hbnkgaW5wdXRzIGRvZXMgaXQgaGF2ZT8KCmBgYHtyIGV2YWw9VFJVRX0KKGRmJGEgLSBtaW4oZGYkYSwgbmEucm0gPSBUUlVFKSkgLwogIChtYXgoZGYkYSwgbmEucm0gPSBUUlVFKSAtIG1pbihkZiRhLCBuYS5ybSA9IFRSVUUpKQpgYGAKCgoKIyMgR2VuZXJhbGl6ZSBpbnB1dHMKCi0gbm93IGlucHV0IGlzIGEgZ2VuZXJpYyB2ZWN0b3IsIGB4YAoKYGBge3IgZXZhbD1UUlVFfQp4IDwtIGRmJGEKCih4IC0gbWluKHgsIG5hLnJtID0gVFJVRSkpIC8KICAobWF4KHgsIG5hLnJtID0gVFJVRSkgLSBtaW4oeCwgbmEucm0gPSBUUlVFKSkKYGBgCgojIyBDbGVhbiB1cAoKLSBub3RlIHRoYXQgd2UgdXRpbGl6ZSBhIHJhbmdlIDMgZGlmZmVyZW50IHRpbWVzLi4uIHdlIGNhbiBqdXN0IGRvIHRoYXQgb25jZSB0byBjbGVhbiB0aGluZ3MgdXAKLSBpcyB0aGlzIGNvZGUgZWFzaWVyIHRvIHJlYWQ/CgpgYGB7ciBldmFsPVRSVUV9CnggPC0gZGYkYQpybmcgPC0gcmFuZ2UoeCwgbmEucm0gPSBUUlVFKQoKKHggLSBybmdbMV0pIC8gKHJuZ1syXSAtIHJuZ1sxXSkKYGBgCgojIyBUdXJuIGl0IGludG8gYSBmdW5jdGlvbgoKLSBTbyBmYXIsIHdlIGlkZW50aWZpZWQgdGhlIG5lZWQgZm9yIGEgZnVuY3Rpb24sIGFuZCB0aGVuIAogICAgLSBpZGVudGlmaWVkIGlucHV0cywKICAgIC0gY2xlYW5lZCB1cCBjb2RlLCBhbmQgCiAgICAtICoqY2hlY2tlZCB0aGF0IGl0IHN0aWxsIHdvcmtzISoqCi0gTm93IGxldCdzIHR1cm4gaXQgaW50byBhIGZ1bmN0aW9uCgpgYGB7cn0KcmVzY2FsZTAxIDwtIGZ1bmN0aW9uKHgpIHsKICBybmcgPC0gcmFuZ2UoeCwgbmEucm0gPSBUUlVFKQogICh4IC0gcm5nWzFdKSAvIChybmdbMl0gLSBybmdbMV0pCn0KYGBgCgotIEdvb2QgaWRlYSB0byBtYWtlIG5vdGVzIGZvciB5b3Vyc2VsZgpgYGB7ciBldmFsPVRSVUV9CnJlc2NhbGUwMSA8LSBmdW5jdGlvbih4KSB7CiAgIyBwdXJwb3NlOiByZXNjYWxlIGEgdmVjdG9yIHNvIHRoZSByZXN1bHQgaXMgYmV0d2VlbiAwIGFuZCAxCiAgIyBpbnB1dHM6CiAgIyMjIHg6IGEgcXVhbnRpdGF0aXZlIHZlY3RvcgogICMgb3V0cHV0czogCiAgIyMjIHJlc3VsdCBiZXR3ZWVuIDAgYW5kIDEgZm9yIGVhY2ggZWxlbWVudCBhZnRlciByZXNjYWxpbmcKCiAgcm5nIDwtIHJhbmdlKHgsIG5hLnJtID0gVFJVRSkKICAoeCAtIHJuZ1sxXSkgLyAocm5nWzJdIC0gcm5nWzFdKQp9CmBgYAoKLSB1c2Ugc2ltcGxlIGRhdGEgd2l0aCBrbm93biByZXN1bHQgdG8gY2hlY2sgdGhhdCBpdCB3b3JrcwoKYGBge3IgZXZhbD1UUlVFfQpyZXNjYWxlMDEoYygwLCA1LCAxMCkpCmBgYAoKLSBsaXZlIGFtbW8KCmBgYHtyIGV2YWw9VFJVRX0KcmVzY2FsZTAxKGRmJGEpCnJlc2NhbGUwMShkZiRiKQpyZXNjYWxlMDEoZGYkYykKcmVzY2FsZTAxKGRmJGQpCmBgYAoKLSByaWdodCwgd2UgY2FuIHZlY3Rvcml6ZSAqKmFueSoqIGZ1bmN0aW9uIQoKYGBge3IgZXZhbD1UUlVFfQphcHBseShYID0gZGYsIE1BUkdJTiA9IDIsIEZVTiA9IHJlc2NhbGUwMSkKYGBgCgoKIyMgUmVjYXA6IFdyaXRpbmcgYSBuZXcgZnVuY3Rpb24KCi0gVGhlcmUgYXJlIHRocmVlIGtleSBwYXJ0cyB3aGVuIHdyaXRpbmcgYSBuZXcgZnVuY3Rpb246CiAgICAtIFlvdSBuZWVkIHRvIG5hbWUgdGhlIGZ1bmN0aW9uLiAoaW4gb3JkZXItLTEuIGluZm9ybWF0aXZlOyAyLiBjb25jaXNlKQogICAgLSBMaXN0IHRoZSBpbnB1dHMsIG9yIGFyZ3VtZW50cywgdG8gdGhlIGZ1bmN0aW9uLgogICAgLSBXcml0ZSBjb2RlIGZvciB0aGUgYm9keSBvZiB0aGUgZnVuY3Rpb24KCi0gTm90ZSB0aGUgb3ZlcmFsbCBwcm9jZXNzIGFjdHVhbGx5IGhhcHBlbnMgaW4gcmV2ZXJzZS4uLiAKICAgIC0gSSBvbmx5IG1hZGUgdGhlIGZ1bmN0aW9uICphZnRlciogSSBmaWd1cmVkIG91dCBob3cgdG8gbWFrZSBpdCB3b3JrIHdpdGggYSBzaW1wbGUgaW5wdXQuIAogICAgLSBJdOKAmXMgZWFzaWVyIHRvIHN0YXJ0IHdpdGggd29ya2luZyBjb2RlIGFuZCB0dXJuIGl0IGludG8gYSBmdW5jdGlvbjsgKippdOKAmXMgTVVDSCBoYXJkZXIgdG8gY3JlYXRlIGEgZnVuY3Rpb24gYW5kIHRoZW4gdHJ5IHRvIG1ha2UgaXQgd29yay4qKgoKCgojIyBgZHBseXI6OmRvKClgIAoKLSBnZW5lcmFsIHB1cnBvc2UgY29tcGxlbWVudCBgZmlsdGVyKClgLCBgc2VsZWN0KClgLCBgbXV0YXRlKClgLCBgc3VtbWFyaXNlKClgIGFuZCBgYXJyYW5nZSgpYAotIGFwcGx5IGFyYml0cmFyeSBmdW5jdGlvbiB0byAqZ3JvdXBzKiBvZiBkYXRhCiAgICAtIHJldHVybiBlaXRoZXIgYSBkYXRhIGZyYW1lIG9yIGFyYml0cmFyeSBvYmplY3RzIHdoaWNoIHdpbGwgYmUgc3RvcmVkIGluIGEgbGlzdC4gCiAgICAtIFBhcnRpY3VsYXJseSB1c2VmdWwgd2hlbiB3b3JraW5nIHdpdGggbW9kZWxzIGJ5IGdyb3VwCgojIyBgZHBseXI6OmRvKClgIEhvbWUgUnVuIExlYWRlcnMgRXhhbXBsZQoKLSBXZSdyZSBpbnRlcmVzdGVkIGluIGlkZW50aWZ5aW5nIHRoZSBiYXNlYmFsbCB0ZWFtIGluIGVhY2ggc2Vhc29uIHRoYXQgbGVkIHRoZWlyIGxlYWd1ZSBpbiBob21lIHJ1bnMKICAgIC0gYSBob21lIHJ1biAoSFIpIGlzIHdoZW4gYSBiYXNlYmFsbCBwbGF5ZXIgaGl0cyB0aGUgYmFsbCBvdmVyIHRoZSBvcHBvc2luZyB0ZWFtIGFuZCBvdXQgb2YgcGxheQogICAgLSB0aGUgcmVzdWx0IGlzIG9uZSBvciBtb3JlIGF1dG9tYXRpYyBwb2ludHMgZm9yIHRoZSB0ZWFtIHRoYXQgaGl0IHRoZSBob21lIHJ1bgogICAgLSBNYWpvciBMZWFndWUgQmFzZWJhbGwgdGVhbXMgYXJlIG9yZ2FuaXplZCBpbnRvIHR3byAibGVhZ3VlcyIKICAgICAgICAtIEFtZXJpY2FuIExlYWd1ZSAoQUwpCiAgICAgICAgLSBOYXRpb25hbCBMZWFndWUgKE5MKQotICpHb2FsOiB3cml0ZSBhbiBSIGZ1bmN0aW9uIHRoYXQgaWRlbnRpZmllcyB0aGUgdGVhbSB3aXRoIHRoZSBtb3N0IEhSJ3MgZm9yIGVhY2ggbGVhZ3VlLCBpbiBlYWNoIHllYXIqCiAgICAtIFN1Z2dlc3Qgc29tZSBwc2V1ZG8gY29kZSB0byBhY2NvbXBsaXNoIHRoaXMgZm9yIGEgc3BlY2lmaWMgbGVhZ3VlIChBTCkgYW5kIHllYXIgKDIwMTUpCgojIyBIb21lIFJ1biBMZWFkZXJzOiB0cmlhbCBydW4KCi0gTGV0J3MgZG8gYSB0cmlhbCBydW4gZmlyc3QgdG8gbWFrZSBzdXJlIHdlIGhhdmUgdGhlIGJvZHkgb2YgdGhlIGNvZGUgd29ya2luZyBhcyBpbnRlbmRlZC4uLgoKYGBge3IgZXZhbD1UUlVFfQpyZXF1aXJlKExhaG1hbikKZGF0YSgiVGVhbXMiKSAKVGVhbXMgPC0gCiAgYXNfdGliYmxlKFRlYW1zKSAlPiUgCiAgbXV0YXRlKGxnSUQgPSBhcy5jaGFyYWN0ZXIobGdJRCkpCgojIHRyaWFsIHJ1biBvZiB0aGUgb3BlcmF0aW9uClRlYW1zICU+JQogIGZpbHRlcih5ZWFySUQgPT0gMjAxNSwgbGdJRCA9PSAiQUwiKSAlPiUKICBzZWxlY3QoeWVhcklELCBsZ0lELCB0ZWFtSUQsIEhSKSAlPiUKICBhcnJhbmdlKGRlc2MoSFIpKSAlPiUKICBoZWFkKDEpICAKYGBgCgojIyBIb21lIFJ1biBMZWFkZXJzOiB3cml0ZSB0aGUgZnVuY3Rpb24KCi0gKkdvYWw6IHdyaXRlIGFuIFIgZnVuY3Rpb24gdGhhdCBpZGVudGlmaWVzIHRoZSB0ZWFtIHdpdGggdGhlIG1vc3QgSFIncyBmb3IgZWFjaCBsZWFndWUsIGluIGVhY2ggeWVhcioKICAgIC0gd2UnbGwgd2FudCB0byBgZ3JvdXBfYnkobGdJRCwgeWVhcklEKWAKICAgIC0gdGhlbiBwZXJmb3JtIG91ciBjYWxjdWxhdGlvbiBvbiBlYWNoIGdyb3VwCiAgICAtIGBkcGx5cjo6ZG8oKWAgaXMgZ29vZCBhdCB0aGF0CiAgICAtIHdlIG5lZWQgYSBmdW5jdGlvbiB0aGF0IHRha2VzIGFuIGFyYml0cmFyeSBjb21iaW5hdGlvbiBvZiBgbGdJRGAgYW5kIGB5ZWFySURgIGFuZCByZXR1cm5zIHRoZSBIUiBsZWFkZXIgZm9yIHRoYXQgZ3JvdXAKCmBgYHtyIGV2YWw9VFJVRX0KaHJfbGVhZGVyIDwtIGZ1bmN0aW9uKHgpIHsKICAjIHJldHVybiB0ZWFtIHdpdGggdGhlIG1vc3QgaG9tZSBydW5zIGdpdmVuIHllYXIgYW5kIGxlYWd1ZQogICMjIyB4OiBhIHN1YnNldCBvZiBUZWFtcyBmb3IgYSBzaW5nbGUgeWVhciBhbmQgbGVhZ3VlCiAgeCAlPiUKICAgIHNlbGVjdCh5ZWFySUQsIGxnSUQsIHRlYW1JRCwgSFIpICU+JQogICAgYXJyYW5nZShkZXNjKEhSKSkgJT4lCiAgICBoZWFkKG4gPSAxKQp9CmBgYAoKCiMjIEhvbWUgUnVuIExlYWRlcnM6IHRlc3QgdGhlIGZ1bmN0aW9uCgotIE1ha2Ugc3VyZSB0aGUgZnVuY3Rpb24gbWF0Y2hlcyBvdXIgcHJldmlvdXMgcmVzdWx0IGZvciB0aGUgQW1lcmljYW4gTGVhZ3VlIGluIDIwMTUKCmBgYHtyfQpUZWFtcyAlPiUKICBmaWx0ZXIoeWVhcklEID09IDIwMTUsIGxnSUQgPT0gIkFMIikgJT4lCiAgZHBseXI6OmRvKGhyX2xlYWRlciguKSkKYGBgCgoKCiMjIEhvbWUgUnVuIExlYWRlcnM6IHRlc3QgdGhlIGZ1bmN0aW9uCgotIEFsbCBjb21iaW5hdGlvbnMgb2YgYGxnSURgIGFuZCBgeWVhcklEYAotIFdlJ2xsIHN0b3JlIHRoZSByZXN1bHRzIHRoaXMgdGltZSBmb3IgbGF0ZXIgdXNlCgpgYGB7cn0KSHJMZWFkZXJzIDwtIAogIFRlYW1zICU+JQogIGdyb3VwX2J5KHllYXJJRCwgbGdJRCkgJT4lCiAgZHBseXI6OmRvKGhyX2xlYWRlciguKSkKCkhyTGVhZGVycyAlPiUKICBhcnJhbmdlKGRlc2MoeWVhcklEKSkgJT4lCiAgaGVhZCgpCmBgYAoKIyMgSG9tZSBSdW4gTGVhZGVyczogbGVhZ3VlIGNvbXBhcmlzb24gCgotIG1heWJlIHdlIHdhbnQgdG8gY29tcGFyZSB0aGUgYXZlcmFnZSBhbmQgbWF4aW11bSBhbW9uZyB0b3AgdGVhbXMgZm9yIGVhY2ggbGVhZ3VlIAotIHdlIGNhbiBgZ3JvdXBfYnkobGdJRClgIGFuZCBgc3VtbWFyaXNlKClgCgpgYGB7cn0KSHJMZWFkZXJzICU+JQogIGdyb3VwX2J5KGxnSUQpICU+JQogIHN1bW1hcmlzZShzZWFzb25zID0gbigpLCAKICAgICAgICAgICAgYXZlcmFnZSA9IG1lYW4oSFIsIG5hLnJtID0gVFJVRSksIAogICAgICAgICAgICBtYXhpbXVtID0gbWF4KEhSLCBuYS5ybSA9IFRSVUUpKQpgYGAKCiMjIEhvbWUgUnVuIExlYWRlcnM6IGxlYWd1ZSBjb21wYXJpc29uCgotIGBtb3NhaWNgIHBhY2thZ2UgaGFzIHNvbWUgZ3JlYXQgdG9vbHMgZm9yIHRoaXMgYW5kIG90aGVyIHB1cnBvc2VzCi0gYG1vc2FpY2AgcHJpb3JpdGl6ZXMgdGhlIGlkaW9tOiBgZnVuY3Rpb24oWSB+IFgsIGRhdGEgPSBEQVRBU0VUKQotIGBtb3NhaWM6OmZhdnN0YXRzKClgIGFzIGFuIGFsdGVybmF0aXZlIHRvIGBzdW1tYXJ5KClgCgpgYGB7cn0KcmVxdWlyZShtb3NhaWMpCmZhdnN0YXRzKEhSIH4gbGdJRCwgZGF0YSA9IEhyTGVhZGVycykgICMgYWRkaXRpb25hbCBncm91cGluZyB2YXJpYWJsZXMgYXJlIGVhc3kgdG8gaW5jbHVkZQpgYGAKCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9VFJVRX0KIyBiYXNlIFIKc3VtbWFyeShIckxlYWRlcnMpCnN1bW1hcnkoSHJMZWFkZXJzJEhSIH4gSHJMZWFkZXJzJGxnSUQpCgojIGFwcGx5IGZhbWlseSBjYW4gaGVscAp0YXBwbHkoWCA9IEhyTGVhZGVycyRIUiwgSU5ERVggPSBIckxlYWRlcnMkbGdJRCwgRlVOID0gc3VtbWFyeSkKCiMgYWRkaXRpb25hbCBncm91cGluZyB2YXJpYWJsZXMgdGFrZSBtb3JlIGVmZm9ydCB0byBpbmNsdWRlLi4uCgojIFRlYW1zICU+JQojICAgZmlsdGVyKGxnSUQgJWluJSBjKCJBTCIsICJOTCIpLCB5ZWFySUQgPiAyMDEwKSAlPiUKYGBgCgoKIyMgSG9tZSBSdW4gTGVhZGVyczogbGVhZ3VlIGNvbXBhcmlzb24gcGxvdAoKYGBge3J9CkhyTGVhZGVycyAlPiUKICBmaWx0ZXIoeWVhcklEID49IDE5MjApICU+JSAgIAogIGdncGxvdChhZXMoeCA9IHllYXJJRCwgeSA9IEhSLCBjb2xvciA9IGxnSUQpKSArIAogIGdlb21fbGluZSgpICsgCiAgZ2VvbV9zbW9vdGgoc2UgPSAwKSArIAogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDE5NzMpICsgCiAgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgeCA9IDE5NzQsIHkgPSAyNSwgbGFiZWwgPSAiQUwgYWRvcHRzIGRlc2lnbmF0ZWQgaGl0dGVyIHJ1bGUiLCBoanVzdCA9ICJsZWZ0IikgKyAKICB4bGFiKCJZZWFyIikgKyAKICB5bGFiKCJNb3N0IEhvbWUgUnVucyBieSBhIFNpbmdsZSBUZWFtIikgKyAKICBnZ3RpdGxlKCJDb21wYXJpc29uIG9mIHRvcCBob21lIHJ1biBwZXJmb3JtYW5jZSBieSBsZWFndWUgYW5kIHllYXIiKQpgYGAKCgoKIyMgSG93IGxhcmdlIGlzIGRpZmZlcmVuY2UgYmV0d2VlbiBOTCBhbmQgQUwgaG9tZSBydW4gcHJvZHVjdGlvbj8KCi0gKipHb2FsOiBIb3cgbGFyZ2UgaXMgdGhlIGRpZmZlcmVuY2UgaW4gSFIgcHJvZHVjdGlvbiBiZXR3ZWVuIGxlYWd1ZXMgc2luY2UgREggcnVsZSBjaGFuZ2U/KioKICAgIC0gVGhlIEFtZXJpY2FuIExlYWd1ZSBtYWRlIGEgcnVsZSBjaGFuZ2UgdG8gYWxsb3cgZGVzaWduYXRlZCBoaXR0ZXJzIGluIDE5NzMuIAogICAgLSBIb3cgbWlnaHQgd2UgYXBwcm9hY2ggdGhlIHByb2JsZW0/ICAod2hhdCBtb2RlbCBhc3N1bXB0aW9ucyBhcmUgcmVxdWlyZWQgb2Ygb3VyIGRhdGE/KQoKCiMjIEhvdyBsYXJnZSBpcyBkaWZmZXJlbmNlIGJldHdlZW4gTkwgYW5kIEFMIGhvbWUgcnVuIHByb2R1Y3Rpb24/CgotIGlmIHdlIHVzZSBhIG5vbnBhcmFtZXRyaWMgYm9vdHN0cmFwIHdlIGNhbiBiZSBsZXNzIGNvbmNlcm5lZCB3aXRoIE5vcm1hbGl0eSwgYnV0IHNob3VsZCBwYXkgYXR0ZW50aW9uIHRvIGluZGVwZW5kZW5jZQotIG9uZSB3YXkgdG8gaGFuZGxlIHRoZSBpbmRlcGVuZGVuY2UgaXNzdWUgaXMgdG8gYXZlcmFnZSBvdmVyIHllYXJzIGZvciBlYWNoIHRlYW0uICAKICAgIC0gVGhhdCdzIGEgcHJldHR5IGNydWRlIGFwcHJvYWNoLCBidXQgaXQgaXNuJ3QgdW5yZWFzb25hYmxlCgpgYGB7cn0KSHJQcm9kdWN0aW9uIDwtIAogIFRlYW1zICU+JQogIGZpbHRlcih5ZWFySUQgPj0gMTk3MykgJT4lCiAgc2VsZWN0KHllYXJJRCwgbGdJRCwgSFIpICU+JQogIGdyb3VwX2J5KHllYXJJRCwgbGdJRCkgJT4lICAKICBzdW1tYXJpc2UoYXZnSFIgPSBtZWFuKEhSKSkgCgpMZWFndWVEaWZmQXZnSFIgPC0gCiAgSHJQcm9kdWN0aW9uICU+JQogIHNwcmVhZChrZXkgPSBsZ0lELCB2YWx1ZSA9IGF2Z0hSKSAlPiUKICBtdXRhdGUoaHJBdmdEaWZmID0gQUwgLSBOTCkKCmBgYAoKIyMgSG93IGxhcmdlIGlzIGRpZmZlcmVuY2UgYmV0d2VlbiBOTCBhbmQgQUwgaG9tZSBydW4gcHJvZHVjdGlvbj8KCgpgYGB7cn0KCnAgPC0gCiAgTGVhZ3VlRGlmZkF2Z0hSICU+JQogIGdncGxvdChhZXMoeCA9IGhyQXZnRGlmZikpICsgCiAgZ2VvbV9kZW5zaXR5KCkgKyAKICB4bGltKC0yMCwgNTApICsgCiAgeGxhYigiRGlmZmVyZW5jZSBpbiBhdmVyYWdlIGhvbWUgcnVuIHByb2R1Y3Rpb24gc2luY2UgMTk4NCAoQUwgLSBOTCkiKQpwCgpmYXZzdGF0cyh+IGhyQXZnRGlmZiwgZGF0YSA9IExlYWd1ZURpZmZBdmdIUikKYGBgCgoKLS0tCgoKIyMgQm9vdHN0cmFwcGluZyB3aXRoIGBtb3NhaWM6OmRvKClgCgotIG9uIGF2ZXJhZ2UsIEFMIHRlYW1zIGhpdCBhYm91dCAxNi41IG1vcmUgaG9tZSBydW5zIHBlciB5ZWFyIHRoYW4gTkwgdGVhbXMKICAgIC0gbmVlZCB0byBlc3RpbWF0ZSB0aGUgdW5jZXJ0YWludHkgb2Ygb3VyIGVzdGltYXRlCiAgICAtIGRlc2lyZWQgcmVzdWx0IGlzIGFuICppbnRlcnZhbCBlc3RpbWF0ZSogKGUuZy4gY29uZmlkZW5jZSBpbnRlcnZhbCkKLSBib290c3RyYXBwaW5nCiAgICAtIGBMZWFndWVEaWZmQXZnSFJgIGRhdGEgaGFzIDQ0IG9ic2VydmF0aW9ucwogICAgLSB3ZSB3YW50IHRvIGVzdGltYXRlIHRoZSB2YXJpYWJpbGl0eSBvZiB0aGUgbWVhbiBkaWZmZXJlbmNlCiAgICAtIHdlIHNhbXBsZSBXSVRIIHJlcGxhY2VtZW50IGZyb20gb3VyIDQ0IG9ic2VydmF0aW9ucwogICAgLSB3ZSdsbCB1c2Ugb3VyIGRpc3RyaWJ1dGlvbiBvZiBib290c3RyYXAgbWVhbnMgdG8gZXN0aW1hdGUgYSBjb25maWRlbmNlIGludGVydmFsCgpgYGB7cn0KIyByZXNhbXBsaW5nIHdpdGggcmVwbGFjZW1lbnQgKDEwayBib290c3RyYXAgc2FtcGxlcykKYm9vdHN0cmFwIDwtIAogIG1vc2FpYzo6ZG8oNzUwKSAqIG1lYW4ofiBockF2Z0RpZmYsIGRhdGEgPSByZXNhbXBsZShMZWFndWVEaWZmQXZnSFIpKQpoZWFkKGJvb3RzdHJhcCkKCiMgOTQlIGNvbmZpZGVuY2UgaW50ZXJ2YWwtLXVzaW5nIG1vc2FpYzo6cWRhdGEoKQpjaXZhbHMgPC0gcWRhdGEofiBtZWFuLCBwID0gYygwLjAzLCAwLjk3KSwgZGF0YSA9IGJvb3RzdHJhcCkKY2l2YWxzCmBgYAoKIyMgQm9vdHN0cmFwIGRpc3RyaWJ1dGlvbgoKYGBge3J9CmJvb3RzdHJhcCAlPiUKICBnZ3Bsb3QoKSArIAogIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gbWVhbikpICsgCiAgZ2VvbV92bGluZShkYXRhID0gY2l2YWxzLCBhZXMoeGludGVyY2VwdCA9IHF1YW50aWxlKSwgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAzKSArIAogIHhsYWIoIkJvb3RzdHJhcCBtZWFuIGRpZmZlcmVuY2UgaW4gYXZlcmFnZSBob21lIHJ1biBwcm9kdWN0aW9uIHNpbmNlIDE5NzMgKEFMIC0gTkwpIikKICAKYGBgCgoKIyMgRGF0YSBpbnRha2UKCiMjIyMgUiBjYW4gcmVhZCBkYXRhIGluIGxvdHMgb2YgZGlmZmVyZW50IGZvcm1hdHMuLi4KCi0gUidzIG5hdGl2ZSBmaWxlIGZvcm1hdCBpcyAiLlJkYSIKICAgIC0gYHNhdmUoKWAgJiBgbG9hZCgpYCBjb21tYW5kcwogICAgLSBOb3RlIE1EU1IgInBybyB0aXAiIGFib3V0IHNlcGFyYXRpb24gb2YgY2xlYW5pbmcgYW5kIGFuYWx5c2lzIChwLiAxMTYpCi0gQ1NWIAogICAgLSBsb2FkcyBvZiBmdW5jdGlvbnMgcmVhZCBDU1ZzCiAgICAtIGByZWFkX2NzdigpYCBmcm9tIHRoZSBgcmVhZHJgIHBhY2thZ2UgaXMgYSBnb29kIG9uZQogICAgLSBgZnJlYWQoKWAgZnJvbSB0aGUgYGRhdGEudGFibGVgIHBhY2thZ2UgaXMgYSBnb29kIG9uZSAoYW5kIGZhc3QpCi0gT3RoZXIgZGVsaW1pdGVyczogYHJlYWRfZGVsaW0oKWAgZnJvbSB0aGUgYHJlYWRyYCBwYWNrYWdlIAotIHNvZnR3YXJlIHNwZWNpZmljIGZvcm1hdHMgCiAgICAtIE1pbml0YWIsIFNBUywgU1BTUywgV2VrYSwgLi4uCi0gRXhjZWwgJiBHb29nbGUgU2hlZXRzCi0gV2ViIChlLmcuIEhUTUwsIFhNTCwgSlNPTikKLSBBUEkncyAoYXBwbGljYXRpb24gcHJvZ3JhbW1pbmcgaW50ZXJmYWNlKQoKIyMjIyBzb21lIGNvb2wgcGFja2FnZXMgZm9yIGRhdGEgaW50YWtlCgotIGBmb3JlaWduYDogdG9vbHMgdG8gcmVhZCBkYXRhIGZyb20gb3RoZXIgc29mdHdhcmUgCi0gYGZlYXRoZXJgOiBmb3Igc3RvcmluZyBkYXRhIGZyYW1lcyB0aGF0IGNhbiBiZSByZWFkIGFuZCB3cml0dGVuIGJ5IEJPVEggUiBhbmQgUHl0aG9uCi0gYHR3aXR0ZVJgOiB0d2l0dGVyIEFQSQotIGBhUnhpdmA6IGFyWGl2IEFQSQotIGBSZmFjZWJvb2tgOiBmYWNlYm9vayBBUEkKLSBgaW5zdGFSYDogaW5zdGFncmFtIEFQSQotIGBSZmxpY2tyYDogZmxpY2tyIEFQSSAobm90IGZvdW5kPykKLSBgdHVtYmxSYDogdHVtYmxyIEFQSQotIGBSbGlua2VkaW5gOiBMaW5rZWRJbiBBUEkKLSBgUlNvY3JhdGFgOiBBUEkgZm9yIHF1ZXJ5aW5nIE5ZQyBPcGVuIERhdGEgcGxhdGZvcm0gKGFtb25nIG90aGVyIHRoaW5ncykKCgoKIyMgRGF0ZXMgJiBUaW1lcwoKLSBDb21tb24gY2xhc3NlcyAKICAgIC0gImRhdGUiIAogICAgLSAidGltZSIgd2l0aGluIGEgZGF5CiAgICAtICJkYXRlLXRpbWUiIChpLmUuIDxkdHRtPikgaXMgYW4gaW5zdGFudCBpbiB0aW1lIHRvIG5lYXJlc3Qgc2Vjb25kICh1c3VhbGx5KQotIFNlZW1zIGxpa2UgdGhleSBzaG91bGQgYmUgc2ltcGxlLCBidXQgY2FuIGdldCBzdXJwcmlzaW5nbHkgY29tcGxpY2F0ZWQgaWYgeW91IGFyZSBiZWluZyBwcmVjaXNlCiAgICAtICoqd2hhdCBhcmUgc29tZSBleGFtcGxlcz8qKgotIGBsdWJyaWRhdGVgIHBhY2thZ2UgaGFzIHNvbWUgZXhjZWxsZW50IHRvb2xzIHRvIGhlbHAKICAgIC0gYHRvZGF5KClgIGFuZCBgbm93KClgIGNvbWUgaW4gaGFuZHkgZm9yIHZhcmlvdXMgcmVhc29ucwogICAgLSBwYXJzZSBjaGFyYWN0ZXIgc3RyaW5ncyBhcyBkYXRlcwogICAgLSBtYW5hZ2luZyBpbmRpdmlkdWFsIGRhdGUvdGltZSBjb21wb25lbnRzIChlLmcuLCAiZGF5IiwgImhvdXIiKQogICAgLSB3b3JraW5nIHdpdGggZXhpc3RpbmcgZGF0ZS90aW1lIG9iamVjdHMKCmBgYHtyIGV2YWw9VFJVRX0KcmVxdWlyZShsdWJyaWRhdGUpCgp0b2RheSgpCm5vdygpCmBgYAoKIyMgU3RyaW5ncyBhcyBkYXRlcyAoYGx1YnJpZGF0ZWApCgpgYGB7ciBldmFsPVRSVUV9CiMgZnVuY3Rpb25zIHRoYXQgcGFyc2UgZGF0ZXMgYXMgc3RyaW5ncwp5bWQoIjIwMDgtMDYtMjciKQptZHkoIk1heSAxNXRoLCAyMDA4IikKbWR5KCI1LzE1LzIwMTkiKQpkbXkoIjMxLUphbi0yMDE3IikKCiMgZGF0ZS10aW1lCnltZF9obXMoIjIwMTctMDEtMzEgMjA6MTE6NTkiKQpgYGAKCiMjIERhdGUvdGltZSBjb21wb25lbnRzIChgbHVicmlkYXRlYCkKCi0gY29tYmluZSBpbmRpdmlkdWFsIGVsZW1lbnRzCiAgICAtIGBtYWtlX2RhdGUoKWAKICAgIC0gYG1ha2VfZGF0ZXRpbWUoKWAKCmBgYHtyfQpyZXF1aXJlKG55Y2ZsaWdodHMxMykKZGF0YShmbGlnaHRzKQoKIyBjb25zdHJ1Y3QgZGF0ZXRpbWUgb2YgZGVwYXJ0dXJlCmZsaWdodHMgJT4lIAogIHNlbGVjdCh5ZWFyLCBtb250aCwgZGF5LCBob3VyLCBtaW51dGUpICU+JQogIG11dGF0ZShkZXBhcnR1cmUgPSBtYWtlX2RhdGV0aW1lKHllYXIsIG1vbnRoLCBkYXksIGhvdXIsIG1pbnV0ZSkpICU+JQogIGhlYWQoMykKYGBgCgojIyBFeHRyYWN0aW5nIGNvbXBvbmVudHMgb2YgZGF0ZXMKCmBgYHtyIGV2YWw9VFJVRX0KZHQgPC0geW1kX2htcygiMjAxNi0wNy0wOCAxMjozNDo1NiIpCgp5ZWFyKGR0KQptb250aChkdCkKbWRheShkdCkKCnlkYXkoZHQpCndkYXkoZHQpCmBgYAoKCiMjIEV4YW1wbGU6IFVLIE51Y2xlYXIgUmVhY3RvcnMKCi0gRGF0YSBJbnRha2UgKHdlYikKLSBNYW5hZ2UgZGF0YSBzdHJ1Y3R1cmVzIChsaXN0IHRvIGRhdGEgZnJhbWUpCi0gZGF0ZSBoYW5kbGluZyAoaW52ZXN0aWdhdGUgZmFpbHVyZXMpCgpgYGB7cn0KVVJMIDwtICJodHRwOi8vZW4uV2lraXBlZGlhLm9yZy93aWtpL0xpc3Rfb2ZfbnVjbGVhcl9yZWFjdG9ycyIKCiMgc2NyYXBlIGFsbCB0YWJsZXMgZnJvbSBwYWdlCnRhYmxlTGlzdCA8LSAKICByZWFkX2h0bWwoVVJMKSAlPiUKICBodG1sX25vZGVzKGNzcyA9ICJ0YWJsZSIpCgojIGxvY2F0ZSBVUyB0YWJsZSBieSBpZGVudGlmeWluZyBhIGZhbW91cyBudWNsZWFyIHJlYWN0b3IKcmVsZXZhbnRUYWJsZXMgPC0gdGFibGVMaXN0W2dyZXAoIk9sZGJ1cnkiLCB0YWJsZUxpc3QpXQoKIyBwYXJzZSBodG1sIGludG8gYSBkYXRhIGZyYW1lCnJlYWN0b3JzUmF3IDwtIGh0bWxfdGFibGUocmVsZXZhbnRUYWJsZXNbWzFdXSwgZmlsbCA9IFRSVUUpCgojIGZpcnN0IHBhc3MgY2xlYW4gdXAKbmFtZXMocmVhY3RvcnNSYXcpW2MoMywgNCwgNiwgNyldIDwtIGMoIlJlYWN0b3IgVHlwZSIsICJSZWFjdG9yIE1vZGVsIiwgIkNhcGFjaXR5IE5ldCIsICJDYXBhY2l0eSBHcm9zcyIpCnJlYWN0b3JzIDwtIHJlYWN0b3JzUmF3Wy0xLCBdCmBgYAoKYGBge3J9CiMgYWRkaXRpb25hbCBjbGVhbiB1cApyZWFjdG9ycyA8LSAKICByZWFjdG9ycyAlPiUKICByZW5hbWUoY2FwYWNpdHlfbmV0ID0gYENhcGFjaXR5IE5ldGAsIGNhcGFjaXR5X2dyb3NzID0gYENhcGFjaXR5IEdyb3NzYCkgJT4lCiAgbXV0YXRlKHBsYW50c3RhdHVzID0gaWZlbHNlKGdyZXBsKCJTaHV0IGRvd24iLCByZWFjdG9ycyRTdGF0dXMpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNodXQgZG93biIsICJOb3QgZm9ybWFsbHkgc2h1dCBkb3duIiksIAogICAgICAgICBjYXBhY2l0eV9uZXQgPSBwYXJzZV9udW1iZXIoY2FwYWNpdHlfbmV0KSwgCiAgICAgICAgIGNvbnN0cnVjdF9kYXRlID0gZG15KGBDb25zdHJ1Y3Rpb24gc3RhcnRgKSwgCiAgICAgICAgIG9wZXJhdGlvbl9kYXRlID0gZG15KGBDb21tZXJjaWFsIG9wZXJhdGlvbmApLCAKICAgICAgICAgY2xvc3VyZV9kYXRlID0gZG15KENsb3N1cmUpKQpgYGAKCmBgYHtyfQpyZWFjdG9ycyAlPiUKICBnZ3Bsb3QoYWVzKHggPSBvcGVyYXRpb25fZGF0ZSwgeSA9IGNhcGFjaXR5X25ldCwgY29sb3IgPSBwbGFudHN0YXR1cykpICsgCiAgZ2VvbV9wb2ludCgpICsgCiAgIyBnZW9tX3Ntb290aCgpICsgCiAgeGxhYigiT3BlcmF0aW9uYWwgZGF0ZSIpICsgCiAgeWxhYigiTmV0IHBsYW50IGNhcGFjaXR5IChNVykiKQpgYGAKCgoK