Agenda

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

Data structures in R

Homogeneous Heterogeneous
1d Atomic vector List
2d Matrix Data frame
nd Array

Atomic vectors

# 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

# 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

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?

# 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)

Comparison of team performance metrics

Bad idea

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 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

apply() family

Teams %>%
  select(15:40) %>%
  apply(MARGIN = 2, FUN = mean, na.rm = TRUE)

Another Sidebar (user-defined functions)

# some simulated data
df <- tibble::tibble(
  a = rnorm(10),
  b = rnorm(10),
  c = rnorm(10),
  d = rnorm(10)
)
# rescale each variable so the result is between 0 and 1 
df$a <- 
  (df$a - min(df$a, na.rm = TRUE)) / 
  (max(df$a, na.rm = TRUE) - min(df$a, na.rm = TRUE))
df$b <- (df$b - min(df$b, na.rm = TRUE)) / 
  (max(df$b, na.rm = TRUE) - min(df$a, na.rm = TRUE))
df$c <- (df$c - min(df$c, na.rm = TRUE)) / 
  (max(df$c, na.rm = TRUE) - min(df$c, na.rm = TRUE))
df$d <- (df$d - min(df$d, na.rm = TRUE)) / 
  (max(df$d, na.rm = TRUE) - min(df$d, na.rm = TRUE))

Identify Inputs

(df$a - min(df$a, na.rm = TRUE)) /
  (max(df$a, na.rm = TRUE) - min(df$a, na.rm = TRUE))
 [1] 0.4262272 0.9805607 0.4878048 0.0000000 0.6645891 1.0000000 0.8473460 0.6163415 0.1764453
[10] 0.9060625

Generalize inputs

x <- df$a
(x - min(x, na.rm = TRUE)) /
  (max(x, na.rm = TRUE) - min(x, na.rm = TRUE))
 [1] 0.4262272 0.9805607 0.4878048 0.0000000 0.6645891 1.0000000 0.8473460 0.6163415 0.1764453
[10] 0.9060625

Clean up

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

rescale01 <- function(x) {
  rng <- range(x, na.rm = TRUE)
  (x - rng[1]) / (rng[2] - rng[1])
}
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])
}
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
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

dplyr::do()

dplyr::do() Home Run Leaders Example

Home Run Leaders: trial run

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

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

Teams %>%
  filter(yearID == 2015, lgID == "AL") %>%
  dplyr::do(hr_leader(.))

Home Run Leaders: test the function

HrLeaders <- 
  Teams %>%
  group_by(yearID, lgID) %>%
  dplyr::do(hr_leader(.))

HrLeaders %>%
  arrange(desc(yearID)) %>%
  head()

Home Run Leaders: league comparison

HrLeaders %>%
  group_by(lgID) %>%
  summarise(seasons = n(), 
            average = mean(HR, na.rm = TRUE), 
            maximum = max(HR, na.rm = TRUE))

Home Run Leaders: league comparison

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?

How large is difference between NL and AL home run production?

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()

# 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

R can read data in lots of different formats…

  • R’s native file format is “.Rda”
    • save() & load() commands
    • Note MDSR “pro tip” about separation of cleaning and analysis (p. 116)
  • CSV
    • loads of functions read CSVs
    • read_csv() from the readr package is a good one
    • fread() from the data.table package is a good one (and fast)
  • Other delimiters: read_delim() from the readr package
  • software specific formats
    • Minitab, SAS, SPSS, Weka, …
  • Excel & Google Sheets
  • Web (e.g. HTML, XML, JSON)
  • API’s (application programming interface)

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

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)

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)

Extracting components of dates

dt <- ymd_hms("2016-07-08 12:34:56")
year(dt)
[1] 2016
month(dt)
[1] 7
mday(dt)
[1] 8
yday(dt)
[1] 190
wday(dt)
[1] 6

Example: UK Nuclear Reactors

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