Multiple Time Series
Some questions for multiple time series:
How do the series behave individually?
How are the series related?
Does one series lead or lag the others?
Some options for plotting multiple series:
separate panels in a trellis display;
multiple series in a single plot - this will require
standardizing if the scales vary substantially;
a multivatiate plot with time represented by connecting line
segments or animation.
Another option for two time series:
use a separate y axis for each.
This is usually a bad idea.
Some data structures for multiple time series:
Crimean War Casualties
The HistData package contains the data used by Florence
Nightingale to call attention to terrible sanitary conditions for the
Britich army in Crimea.
data(Nightingale, package = "HistData")
head(select(Nightingale, -Date))
## Month Year Army Disease Wounds Other Disease.rate Wounds.rate Other.rate
## 1 Apr 1854 8571 1 0 5 1.4 0.0 7.0
## 2 May 1854 23333 12 0 9 6.2 0.0 4.6
## 3 Jun 1854 28333 11 0 6 4.7 0.0 2.5
## 4 Jul 1854 28722 359 0 23 150.0 0.0 9.6
## 5 Aug 1854 30246 828 1 30 328.5 0.4 11.9
## 6 Sep 1854 30290 788 81 70 312.2 32.1 27.7
A plot of the Disease, Wounds, and
Other:
nDWO <- select(Nightingale,
Disease, Wounds, Other) |>
ts(start = c(1854, 4), frequency = 12)
autoplot(nDWO)
Using the rate variables produces a very similar pattern:
nDWO.rate <- select(Nightingale,
ends_with("rate")) |>
ts(start = c(1854, 4), frequency = 12)
autoplot(nDWO.rate)
The army size does not vary enough over the time where the casualties
are high to affect the pattern much:
nArmy <- select(Nightingale, Army) |>
ts(start = c(1854, 4), frequency = 12)
autoplot(nArmy)
Instead of showing three series in one plot you can also use
faceting.
autoplot(nDWO, facets = TRUE)
To create the plot of Disease, Wounds, and
Other using basic ggplot it is best to first
convert the data into long form.
You can create a long version of the data, using either
pivot_longer or broom::tidy.
Using broom:tidy on the mts object:
broom::tidy(nDWO) |>
rename(Date = index,
Deaths = value,
Cause = series) |>
ggplot(aes(x = Date,
y = Deaths,
color = Cause)) +
geom_line()
Using pivot_longer on the original data frame:
select(Nightingale,
Date, Disease, Wounds, Other) |>
pivot_longer(-Date,
names_to = "Cause",
values_to = "Deaths") |>
ggplot(aes(x = Date,
y = Deaths,
color = Cause)) +
geom_line()
Using faceting instead of a single plot:
select(Nightingale,
Date, Disease, Wounds, Other) |>
pivot_longer(-Date,
names_to = "Cause",
values_to = "Deaths") |>
ggplot(aes(x = Date, y = Deaths)) +
geom_line() +
facet_wrap(~ Cause, ncol = 1)
To focus on the shape of the curves it is often useful to allow the
vertical axis ranges to adjust to the data:
select(Nightingale,
Date, Disease, Wounds, Other) |>
pivot_longer(-Date,
names_to = "Cause",
values_to = "Deaths") |>
ggplot(aes(x = Date, y = Deaths)) +
geom_line() +
facet_wrap(~ Cause, ncol = 1,
scales = "free_y")
This is particularly useful when the ranges are very different, for
example if the Army series is included:
select(Nightingale,
Army, Date, Disease, Wounds, Other) |>
pivot_longer(-Date,
names_to = "series",
values_to = "Deaths") |>
ggplot(aes(Date, Deaths)) +
geom_line() +
facet_wrap(~ series, ncol = 1,
scales = "free_y")
Some Notes
Using autoplot is often a quick way to get useful
plots.
Using basic ggplot graphics is a bit more work but
provides more flexibility.
Many Time Series
Annual flow for dams in Italy:
Clearly showing all series in a single plot is not helpful!
data(hydroSIMN, package = "nsRFA")
annualflows <- mutate(annualflows,
cod = factor(cod))
ggplot(annualflows,
aes(x = anno, y = dato,
group = cod,
color = factor(cod))) +
geom_line()
With small multiples some patterns can be seen:
ggplot(annualflows,
aes(x = anno, y = dato,
group = cod)) +
geom_line() +
facet_wrap(~ cod) +
theme(axis.text.x =
element_text(angle = 45,
hjust = 1))
Small multiples can also be arranged in ways that convey spatial
information:
library(geofacet)
ggplot(state_unemp, aes(year, rate)) +
geom_line() +
facet_geo(~ state,
grid = "us_state_grid2",
label = "name") +
scale_x_continuous(labels = function(x) paste0("'", substr(x, 3, 4))) +
labs(title = "Seasonally Adjusted US Unemployment Rate 2000-2016",
caption = "Data Source: bls.gov",
x = "Year",
y = "Unemployment Rate (%)") +
theme(strip.text.x = element_text(size = 6),
axis.text.y = element_text(size = 6),
axis.text.x = element_blank())
The Fall and Rise of Income Inequality
A 2015
post on the NPR Planet Money site uses two graphs to show
transitions in income inequality.
The page includes a link to the
data source .
This has been unavailable for some time.
I tried to obtain the same data but may not have done it quite
right.
The closest source I could find is https://wid.world/data/ .
The data is available locally:
income <- read.table("https://www.stat.uiowa.edu/~luke/data/nprincome.dat")
Plots over time:
select(income, -year) |>
ts(start = 1913) |>
autoplot()
The ranges are too different for this to be effective.
One alternative: standardize the series to one, or 100%, at some
point.
Here the initial value in 1913 is used:
select(income, -year) |>
transmute(L90rel = L90 / L90[1],
U01rel = U01 / U01[1]) |>
ts(start = 1913) |>
autoplot()
Faceted plots, with free \(y\) axes,
are another option:
select(income, -year) |>
ts(start = 1913) |>
autoplot(facets = TRUE)
With two series another option is a connected scatter
plot .
ggplot(income, aes(U01, L90)) +
geom_path(linewidth = 2) +
labs(x = "Average income for the top 1%",
y = "Average income for the bottom 90%")
This simple form does not show the direction or scale of time.
Color or annotation can be used to show direction and scale of
time:
library(ggrepel)
ggplot(income, aes(U01, L90, color = year)) +
geom_path(size = 2) +
geom_text_repel(
aes(label =
ifelse(year %% 5 == 0 |
year == max(year),
year,
"")),
color = "grey50") +
labs(x = "Average income for the top 1%",
y = "Average income for the bottom 90%")
Animated is another option:
p <- ggplot(income,
aes(x = U01, y = L90,
color = year)) +
geom_path(size = 2,
color = "lightgrey",
data = mutate(income,
year = NULL)) +
geom_path(size = 2) +
labs(title = "Year: {frame_along}",
x = "Average income for the top 1%",
y = "Average income for the bottom 90%")
library(gganimate)
animate(p + transition_reveal(year),
fps = 5, end_pause = 10)
Stack Loss Data
Operational data obtained from 21 days of operation of a plant for
the oxidation of ammonia (NH3) to nitric acid (HNO3).
The dependent variable, stack.loss is 10 times the
percentage of the ingoing ammonia to the plant that escapes from the
absorption column unabsorbed.
library(lattice)
splom(stackloss)
The data are collected over time:
library(dplyr)
sl <- mutate(stackloss,
intex = seq_len(nrow(stackloss)))
cpal <- colorRampPalette(c("#132B43", "#56B1F7"))
cols <- cpal(nrow(stackloss) - 1)
pfun <- function(x, y, ...) {
n <- length(x)
lsegments(x[-n], y[-n], x[-1], y[-1],
col = cols, lwd = 3)
}
splom(stackloss, panel = pfun)
Randomly reordering the indices can help calibrate whether there is a
time effect:
splom(sample_n(stackloss, nrow(stackloss)),
panel = pfun)
This is sometimes called graphical inference .
Hadley Wickham, Dianne Cook, Heike Hofmann, and Andreas Buja,
“Graphical Inference for Infovis,” IEEE Transactions on
Visualization and Computer Graphics , Vol. 16, No. 6,
November/December 2010.
Picking one plot and embedding the true order among 19 random
orderings:
True order: plot 20.
The code:
N <- 20
k <- sample(N, 1)
ord <- seq_len(nrow(stackloss))
ss <- function(i) {
if (i == k)
sl <- stackloss
else
sl <- sample_n(stackloss, nrow(stackloss))
mutate(sl, order = ord, sample = i)
}
d <- lapply(1 : N, ss)
d <- do.call(rbind, d)
library(ggplot2)
ggplot(d, aes(x = Acid.Conc., y = stack.loss, color = order)) +
geom_path(size = 0.8) + facet_wrap(~ sample) +
guides(color = "none")
Lynx-Hare Data
Trapping data on Canadian lynx and snowshoe hare:
Cycle patterns are similar.
lynxhare <- read.table("https://www.stat.uiowa.edu/~luke/data/LynxHare.txt")
names(lynxhare) <- c("year", "hare", "lynx")
lhts <- ts(select(lynxhare, -year), start = 1845)
autoplot(lhts, facets = TRUE)
Lynx cycles often lag behind:
autoplot(lhts)
Things to Look For
Changes in data definition.
Administrative definitions, such as employment/unemployment can
change over time.
Administrative entities, such as countries and states, can change
over time.
Instruments can change (satelite imagery replacing surface
measurements).
Outliers need not be globally outlying: departures from local
patterns may also be important.
Level shifts and changes in volatility.
Shocks affecting level or volatility.
Australian Airline Data
Weekly passenger
loads , in thousands, on Ansett Airlines between Australia’s two
largest cities (available as melsyd in package
fpp2):
Some features:
Strike in 1989.
Increase in passenger load starting in second half of
1991.
Dip in economy, rise in business in 1992 corresponding to an
experiment with fewer economy/more business seats.
data(melsyd, package = "fpp2")
autoplot(melsyd, facets = TRUE)
Weekly SP 500 Closing Returns
Weekly closing returns of the SP 500 from 2003 to September,
2012:
Some features:
Increased volatility at the start of the financial
crisis.
Volatility seems to be decreasing, but not very fast.
library(xts)
library(astsa)
autoplot(sp500w)
Forecasting
Studying the past may help predict the future.
Forecasting a time series usually involves choosing a model and
running the model forward.
Accuracy of forecasts decreases rapidly the farther ahead the
forecast is made.
An example from the forecast package:
WWWusage is a time series of the numbers of users connected
to the Internet.
library(forecast)
fit <- ets(WWWusage)
fc <- forecast(WWWusage, model = fit)
autoplot(fc)
Labeling Trend Lines
For trend lines using a standard guide can be a little awkward.
Average life expectancy against year for the continents in the
gapminder data:
library(gapminder)
d <- group_by(gapminder,
continent, year) |>
summarize(avgLifeExp = mean(lifeExp)) |>
ungroup()
p <- ggplot(d,
aes(x = year,
y = avgLifeExp,
color = continent)) +
geom_line(size = 1)
p
With the legend on the right, a small improvement is to order the
levels to match the order of the right hand curve values:
## Warning: <ggplot> %+% x was deprecated in ggplot2 4.0.0.
## ℹ Please use <ggplot> + x instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
d_last <- filter(d, year == max(year)) |>
mutate(continent =
reorder(continent, -avgLifeExp))
p %+%
mutate(d,
continent =
factor(continent,
levels(d_last$continent)))
Direct labeling is often a better choice.
One option is to use geom_text at the ends of the
lines.
This may need some additional adjustments to margins and clipping
behavior:
p <- p + guides(color = "none")
d_last <- filter(d, year == max(year))
p + geom_text(aes(label = continent),
data = d_last,
size = 5,
hjust = -0.1) +
coord_cartesian(clip = "off",
expand = FALSE) +
theme(plot.margin =
margin(30, 60, 10, 10))
Another option is to use a second axis:
p + scale_y_continuous(
sec.axis = dup_axis(
breaks = d_last$avgLifeExp,
labels = d_last$continent,
name = NULL)) +
scale_x_continuous(expand = c(0, 0))
It is also possible to place the labels along the lines.
The geomtextpath
package provides support for this.
library(geomtextpath)
ggplot(d, aes(x = year,
y = avgLifeExp,
color = continent)) +
geom_textline(aes(label = continent),
linewidth = 1,
vjust = -0.5,
hjust = 0.95) +
guides(color = "none")
Bump Charts
Top 10 countries in terms of GDP per capita in 2007:
top_gdp <- filter(gapminder,
continent == "Europe",
year == max(year)) |>
slice_max(gdpPercap, n = 10) |>
pull(country)
d <- filter(gapminder,
country %in% top_gdp) |>
group_by(year) |>
mutate(
rank = min_rank(desc(gdpPercap))) |>
ungroup()
d_last <- filter(d, year == max(year))
ggplot(d, aes(year,
gdpPercap,
color = country)) +
geom_line()
The dominant feature is the overall trend.
One option: show deviations from the overall trend.
Another option: show ranks within years.
This is called a bump chart .
A simple bump chart:
ggplot(d, aes(year, rank, color = country)) +
geom_line(size = 1) +
scale_y_reverse(breaks = 1:10) +
geom_text(aes(label = country),
data = d_last,
size = 5,
hjust = -0.1) +
coord_cartesian(clip = "off",
expand = FALSE) +
theme(plot.margin =
margin(30, 90, 10, 10)) +
guides(color = "none")
The ggbump package provides geom_bump for
drawing smooth bump chart lines:
library(ggbump)
ggplot(d, aes(year, rank, color = country)) +
geom_bump(size = 1) +
scale_y_reverse(breaks = 1:10) +
geom_text(aes(label = country),
data = d_last,
size = 5,
hjust = -0.1) +
coord_cartesian(clip = "off",
expand = FALSE) +
theme(plot.margin =
margin(30, 90, 10, 10)) +
guides(color = "none")
Other Approaches
A talk
from 2019 describes a wide range of options for visualizing temporal
data.
Exercises
Creating a time series or multiple time series object and using
autoplot can be an efficient way to obtain line plots of time series
data. The following code uses the weather table in the
nycflights13 package to create a data frame containing
minimum and maximum temperatures for each month in 2013:
library(tidyverse)
library(nycflights13)
library(lubridate)
library(forecast)
temps <- group_by(weather, year, month) |>
summarize(max_temp = max(temp, na.rm = TRUE),
min_temp = min(temp, na.rm = TRUE)) |>
ungroup()
Which of the following expressions produces a line plot showing the
minimum and maximum temperature series (and no other series)?
autoplot(temps)
autoplot(temps$max_temp, temps$min_temp)
ts(temps) |> autoplot()
select(temps, max_temp, min_temp) |> ts() |> autoplot()
LS0tCnRpdGxlOiAiUGxvdHMgZm9yIE11bHRpcGxlIFRpbWUgU2VyaWVzIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgo8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9InN0YXQ0NTgwLmNzcyIgdHlwZT0idGV4dC9jc3MiIC8+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+IC5yZW1hcmstY29kZSB7IGZvbnQtc2l6ZTogODUlOyB9IDwvc3R5bGU+CmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9CnNvdXJjZShoZXJlOjpoZXJlKCJzZXR1cC5SIikpCmtuaXRyOjpvcHRzX2NodW5rJHNldChjb2xsYXBzZSA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGZpZy5oZWlnaHQgPSA1LCBmaWcud2lkdGggPSA2LCBmaWcuYWxpZ24gPSAiY2VudGVyIikKCm9wdGlvbnMoaHRtbHRvb2xzLmRpci52ZXJzaW9uID0gRkFMU0UpCgpzZXQuc2VlZCgxMjM0NSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGxhdHRpY2UpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShmb3JlY2FzdCkKdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoKSArCiAgICAgICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiksCiAgICAgICAgICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3IgPSAiZ3JleTMwIiwgZmlsbCA9IE5BKSkpCmBgYAoKCiMjIE11bHRpcGxlIFRpbWUgU2VyaWVzCgpTb21lIHF1ZXN0aW9ucyBmb3IgbXVsdGlwbGUgdGltZSBzZXJpZXM6CgotIEhvdyBkbyB0aGUgc2VyaWVzIGJlaGF2ZSBpbmRpdmlkdWFsbHk/CgotIEhvdyBhcmUgdGhlIHNlcmllcyByZWxhdGVkPwoKLSBEb2VzIG9uZSBzZXJpZXMgbGVhZCBvciBsYWcgdGhlIG90aGVycz8KClNvbWUgb3B0aW9ucyBmb3IgcGxvdHRpbmcgbXVsdGlwbGUgc2VyaWVzOgoKLSBzZXBhcmF0ZSBwYW5lbHMgaW4gYSB0cmVsbGlzIGRpc3BsYXk7CgotIG11bHRpcGxlIHNlcmllcyBpbiBhIHNpbmdsZSBwbG90IC0gdGhpcyB3aWxsIHJlcXVpcmUgc3RhbmRhcmRpemluZwogIGlmIHRoZSBzY2FsZXMgdmFyeSBzdWJzdGFudGlhbGx5OwoKLSBhIG11bHRpdmF0aWF0ZSBwbG90IHdpdGggdGltZSByZXByZXNlbnRlZCBieSBjb25uZWN0aW5nIGxpbmUKICBzZWdtZW50cyBvciBhbmltYXRpb24uCgpBbm90aGVyIG9wdGlvbiBmb3IgdHdvIHRpbWUgc2VyaWVzOgoKLSB1c2UgYSBzZXBhcmF0ZSBgeWAgYXhpcyBmb3IgZWFjaC4KClRoaXMgaXMgdXN1YWxseSBhIGJhZCBpZGVhLgoKU29tZSBkYXRhIHN0cnVjdHVyZXMgZm9yIG11bHRpcGxlIHRpbWUgc2VyaWVzOgoKKiBgbXRzYCBvYmplY3RzIGNyZWF0ZWQgYnkgYHRzKClgLgoKICAgIC0gQWxsIHNlcmllcyBoYXZlIHRvIGJlIG51bWVyaWMuCiAgICAtIGBhdXRvcGxvdGAgbWV0aG9kcyBtYXkgYmUgc3VmZmljaWVudC4KCiogUmVndWxhciBkYXRhIGZyYW1lcy4KCiAgICAtIFRoZSBzZXJpZXMgd2lsbCBoYXZlIHRvIGJlIHBsb3R0ZWQgaW4gc2VwYXJhdGUgbGF5ZXJzLAogICAgLSBvciB0aGUgZGF0YSBmcmFtZXMgd2lsbCBuZWVkIGNvbnZlcnRpbmcgaW50byBsb25nIGZvcm0uCgoKIyMgQ3JpbWVhbiBXYXIgQ2FzdWFsdGllcwoKVGhlIGBIaXN0RGF0YWAgcGFja2FnZSBjb250YWlucyB0aGUgZGF0YSB1c2VkIGJ5IEZsb3JlbmNlIE5pZ2h0aW5nYWxlCnRvIGNhbGwgYXR0ZW50aW9uIHRvIHRlcnJpYmxlIHNhbml0YXJ5IGNvbmRpdGlvbnMgZm9yIHRoZSBCcml0aWNoIGFybXkKaW4gQ3JpbWVhLgoKYGBge3J9CmRhdGEoTmlnaHRpbmdhbGUsIHBhY2thZ2UgPSAiSGlzdERhdGEiKQpoZWFkKHNlbGVjdChOaWdodGluZ2FsZSwgLURhdGUpKQpgYGAKCkEgcGxvdCBvZiB0aGUgYERpc2Vhc2VgLCBgV291bmRzYCwgYW5kIGBPdGhlcmA6CgpgYGB7ciBuaWdodC10cy1hdXRvLCBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDcuNX0KbkRXTyA8LSBzZWxlY3QoTmlnaHRpbmdhbGUsCiAgICAgICAgICAgICAgIERpc2Vhc2UsIFdvdW5kcywgT3RoZXIpIHw+CiAgICB0cyhzdGFydCA9IGMoMTg1NCwgNCksIGZyZXF1ZW5jeSA9IDEyKQoKYXV0b3Bsb3QobkRXTykKYGBgCmBgYHtyIG5pZ2h0LXRzLWF1dG8sIGV2YWwgPSBGQUxTRX0KYGBgCgpVc2luZyB0aGUgcmF0ZSB2YXJpYWJsZXMgcHJvZHVjZXMgYSB2ZXJ5IHNpbWlsYXIgcGF0dGVybjoKCmBgYHtyIG5pZ2h0LXJhdGUtdHMtYXV0bywgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSA3LjV9Cm5EV08ucmF0ZSA8LSBzZWxlY3QoTmlnaHRpbmdhbGUsCiAgICAgICAgICAgICAgICAgICAgZW5kc193aXRoKCJyYXRlIikpIHw+CiAgICB0cyhzdGFydCA9IGMoMTg1NCwgNCksIGZyZXF1ZW5jeSA9IDEyKQoKYXV0b3Bsb3QobkRXTy5yYXRlKQpgYGAKYGBge3IgbmlnaHQtcmF0ZS10cy1hdXRvLCBldmFsID0gRkFMU0V9CmBgYAoKVGhlIGFybXkgc2l6ZSBkb2VzIG5vdCB2YXJ5IGVub3VnaCBvdmVyIHRoZSB0aW1lIHdoZXJlIHRoZSBjYXN1YWx0aWVzCmFyZSBoaWdoIHRvIGFmZmVjdCB0aGUgcGF0dGVybiBtdWNoOgoKYGBge3IgbmlnaHQtYXJteS10cy1hdXRvLCBlY2hvID0gRkFMU0V9Cm5Bcm15IDwtIHNlbGVjdChOaWdodGluZ2FsZSwgQXJteSkgfD4KICAgIHRzKHN0YXJ0ID0gYygxODU0LCA0KSwgZnJlcXVlbmN5ID0gMTIpCgphdXRvcGxvdChuQXJteSkKYGBgCmBgYHtyIG5pZ2h0LWFybXktdHMtYXV0bywgZXZhbCA9IEZBTFNFfQpgYGAKCkluc3RlYWQgb2Ygc2hvd2luZyB0aHJlZSBzZXJpZXMgaW4gb25lIHBsb3QgeW91IGNhbiBhbHNvIHVzZSBmYWNldGluZy4KCmBgYHtyIG5pZ2h0LWF1dG8tZmFjZXQsIGVjaG8gPSBGQUxTRX0KYXV0b3Bsb3QobkRXTywgZmFjZXRzID0gVFJVRSkKYGBgCmBgYHtyIG5pZ2h0LWF1dG8tZmFjZXQsIGV2YWwgPSBGQUxTRX0KYGBgCgpUbyBjcmVhdGUgdGhlIHBsb3Qgb2YgYERpc2Vhc2VgLCBgV291bmRzYCwgYW5kIGBPdGhlcmAgdXNpbmcgYmFzaWMKYGdncGxvdGAgaXQgaXMgYmVzdCB0byBmaXJzdCBjb252ZXJ0IHRoZSBkYXRhIGludG8gbG9uZyBmb3JtLgoKWW91IGNhbiBjcmVhdGUgYSBsb25nIHZlcnNpb24gb2YgdGhlIGRhdGEsIHVzaW5nIGVpdGhlciBgcGl2b3RfbG9uZ2VyYApvciBgYnJvb206OnRpZHlgLgoKVXNpbmcgYGJyb29tOnRpZHlgIG9uIHRoZSBgbXRzYCBvYmplY3Q6CgpgYGB7ciBuaWdodC1nZ3Bsb3QtYnJvb20sIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gNy41fQpicm9vbTo6dGlkeShuRFdPKSB8PgogICAgcmVuYW1lKERhdGUgPSBpbmRleCwKICAgICAgICAgICBEZWF0aHMgPSB2YWx1ZSwKICAgICAgICAgICBDYXVzZSA9IHNlcmllcykgfD4KICAgIGdncGxvdChhZXMoeCA9IERhdGUsCiAgICAgICAgICAgICAgIHkgPSBEZWF0aHMsCiAgICAgICAgICAgICAgIGNvbG9yID0gQ2F1c2UpKSArCiAgICBnZW9tX2xpbmUoKQpgYGAKYGBge3IgbmlnaHQtZ2dwbG90LWJyb29tLCBldmFsID0gRkFMU0V9CmBgYAoKVXNpbmcgYHBpdm90X2xvbmdlcmAgb24gdGhlIG9yaWdpbmFsIGRhdGEgZnJhbWU6CgpgYGB7ciBuaWdodC1nZ3Bsb3QtcGl2b3QtbG9uZ2VyLCBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDcuNX0Kc2VsZWN0KE5pZ2h0aW5nYWxlLAogICAgICAgRGF0ZSwgRGlzZWFzZSwgV291bmRzLCBPdGhlcikgfD4KICAgIHBpdm90X2xvbmdlcigtRGF0ZSwKICAgICAgICAgICAgICAgICBuYW1lc190byA9ICJDYXVzZSIsCiAgICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIkRlYXRocyIpIHw+CiAgICBnZ3Bsb3QoYWVzKHggPSBEYXRlLAogICAgICAgICAgICAgICB5ID0gRGVhdGhzLAogICAgICAgICAgICAgICBjb2xvciA9IENhdXNlKSkgKwogICAgZ2VvbV9saW5lKCkKYGBgCmBgYHtyIG5pZ2h0LWdncGxvdC1waXZvdC1sb25nZXIsIGV2YWwgPSBGQUxTRX0KYGBgCgpVc2luZyBmYWNldGluZyBpbnN0ZWFkIG9mIGEgc2luZ2xlIHBsb3Q6CgpgYGB7ciBuaWdodC1nZ3Bsb3QtZmFjZXQsIGVjaG8gPSBGQUxTRX0Kc2VsZWN0KE5pZ2h0aW5nYWxlLAogICAgICAgRGF0ZSwgRGlzZWFzZSwgV291bmRzLCBPdGhlcikgfD4KICAgIHBpdm90X2xvbmdlcigtRGF0ZSwKICAgICAgICAgICAgICAgICBuYW1lc190byA9ICJDYXVzZSIsCiAgICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIkRlYXRocyIpIHw+CiAgICBnZ3Bsb3QoYWVzKHggPSBEYXRlLCB5ID0gRGVhdGhzKSkgKwogICAgZ2VvbV9saW5lKCkgKwogICAgZmFjZXRfd3JhcCh+IENhdXNlLCBuY29sID0gMSkKYGBgCmBgYHtyIG5pZ2h0LWdncGxvdC1mYWNldCwgZXZhbCA9IEZBTFNFfQpgYGAKClRvIGZvY3VzIG9uIHRoZSBzaGFwZSBvZiB0aGUgY3VydmVzIGl0IGlzIG9mdGVuIHVzZWZ1bCB0byBhbGxvdyB0aGUKdmVydGljYWwgYXhpcyByYW5nZXMgdG8gYWRqdXN0IHRvIHRoZSBkYXRhOgoKYGBge3IgbmlnaHQtZ2dwbG90LWZhY2V0LWZyZWUteSwgZWNobyA9IEZBTFNFfQpzZWxlY3QoTmlnaHRpbmdhbGUsCiAgICAgICBEYXRlLCBEaXNlYXNlLCBXb3VuZHMsIE90aGVyKSB8PgogICAgcGl2b3RfbG9uZ2VyKC1EYXRlLAogICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkNhdXNlIiwKICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiRGVhdGhzIikgfD4KICAgIGdncGxvdChhZXMoeCA9IERhdGUsIHkgPSBEZWF0aHMpKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICBmYWNldF93cmFwKH4gQ2F1c2UsIG5jb2wgPSAxLAogICAgICAgICAgICAgICBzY2FsZXMgPSAiZnJlZV95IikKYGBgCmBgYHtyIG5pZ2h0LWdncGxvdC1mYWNldC1mcmVlLXksIGV2YWwgPSBGQUxTRX0KYGBgCgpUaGlzIGlzIHBhcnRpY3VsYXJseSB1c2VmdWwgd2hlbiB0aGUgcmFuZ2VzIGFyZSB2ZXJ5IGRpZmZlcmVudCwgZm9yCmV4YW1wbGUgaWYgdGhlIGBBcm15YCBzZXJpZXMgaXMgaW5jbHVkZWQ6CgpgYGB7ciBuaWdodC1nZ3Bsb3QtZmFjZXQtZnJlZS15LWFybXksIGVjaG8gPSBGQUxTRX0Kc2VsZWN0KE5pZ2h0aW5nYWxlLAogICAgICAgQXJteSwgRGF0ZSwgRGlzZWFzZSwgV291bmRzLCBPdGhlcikgfD4KICAgIHBpdm90X2xvbmdlcigtRGF0ZSwKICAgICAgICAgICAgICAgICBuYW1lc190byA9ICJzZXJpZXMiLAogICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJEZWF0aHMiKSB8PgogICAgZ2dwbG90KGFlcyhEYXRlLCBEZWF0aHMpKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICBmYWNldF93cmFwKH4gc2VyaWVzLCBuY29sID0gMSwKICAgICAgICAgICAgICAgc2NhbGVzID0gImZyZWVfeSIpCmBgYApgYGB7ciBuaWdodC1nZ3Bsb3QtZmFjZXQtZnJlZS15LWFybXksIGV2YWwgPSBGQUxTRX0KYGBgCgoKIyMgU29tZSBOb3RlcwoKVXNpbmcgYGF1dG9wbG90YCBpcyBvZnRlbiBhIHF1aWNrIHdheSB0byBnZXQgdXNlZnVsIHBsb3RzLgoKVXNpbmcgYmFzaWMgYGdncGxvdGAgZ3JhcGhpY3MgaXMgYSBiaXQgbW9yZSB3b3JrIGJ1dCBwcm92aWRlcyBtb3JlCmZsZXhpYmlsaXR5LgoKCiMjIE1hbnkgVGltZSBTZXJpZXMKCkFubnVhbCBmbG93IGZvciBkYW1zIGluIEl0YWx5OgoKYGBge3IgaXRhbHktZGFtcy1zaW5nbGUsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gOH0KZGF0YShoeWRyb1NJTU4sIHBhY2thZ2UgPSAibnNSRkEiKQphbm51YWxmbG93cyA8LSBtdXRhdGUoYW5udWFsZmxvd3MsCiAgICAgICAgICAgICAgICAgICAgICBjb2QgPSBmYWN0b3IoY29kKSkKCmdncGxvdChhbm51YWxmbG93cywKICAgICAgIGFlcyh4ID0gYW5ubywgeSA9IGRhdG8sCiAgICAgICAgICAgZ3JvdXAgPSBjb2QsCiAgICAgICAgICAgY29sb3IgPSBmYWN0b3IoY29kKSkpICsKICAgIGdlb21fbGluZSgpCmBgYApDbGVhcmx5IHNob3dpbmcgYWxsIHNlcmllcyBpbiBhIHNpbmdsZSBwbG90IGlzIG5vdCBoZWxwZnVsIQoKYGBge3IgaXRhbHktZGFtcy1zaW5nbGUsIGV2YWwgPSBGQUxTRX0KYGBgCgpXaXRoIHNtYWxsIG11bHRpcGxlcyBzb21lIHBhdHRlcm5zIGNhbiBiZSBzZWVuOgoKYGBge3IgaXRhbHktZGFtcy1mYWNldCwgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gOX0KZ2dwbG90KGFubnVhbGZsb3dzLAogICAgICAgYWVzKHggPSBhbm5vLCB5ID0gZGF0bywKICAgICAgICAgICBncm91cCA9IGNvZCkpICsKICAgIGdlb21fbGluZSgpICsKICAgIGZhY2V0X3dyYXAofiBjb2QpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0KICAgICAgICAgICAgICBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3QgPSAxKSkKYGBgCmBgYHtyIGl0YWx5LWRhbXMtZmFjZXQsIGV2YWwgPSBGQUxTRX0KYGBgCgpTbWFsbCBtdWx0aXBsZXMgY2FuIGFsc28gYmUgYXJyYW5nZWQgaW4gd2F5cyB0aGF0IGNvbnZleSBzcGF0aWFsIGluZm9ybWF0aW9uOgoKYGBge3IgdW5lbXAtZ2VvZmFjZXQsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDl9CmxpYnJhcnkoZ2VvZmFjZXQpCmdncGxvdChzdGF0ZV91bmVtcCwgYWVzKHllYXIsIHJhdGUpKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICBmYWNldF9nZW8ofiBzdGF0ZSwKICAgICAgICAgICAgICBncmlkID0gInVzX3N0YXRlX2dyaWQyIiwKICAgICAgICAgICAgICBsYWJlbCA9ICJuYW1lIikgKwogICAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHBhc3RlMCgiJyIsIHN1YnN0cih4LCAzLCA0KSkpICsKICAgIGxhYnModGl0bGUgPSAiU2Vhc29uYWxseSBBZGp1c3RlZCBVUyBVbmVtcGxveW1lbnQgUmF0ZSAyMDAwLTIwMTYiLAogICAgICAgICBjYXB0aW9uID0gIkRhdGEgU291cmNlOiBibHMuZ292IiwKICAgICAgICAgeCA9ICJZZWFyIiwKICAgICAgICAgeSA9ICJVbmVtcGxveW1lbnQgUmF0ZSAoJSkiKSArCiAgICB0aGVtZShzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLAogICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLAogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpCmBgYApgYGB7ciB1bmVtcC1nZW9mYWNldCwgZXZhbCA9IEZBTFNFfQpgYGAKCgojIyBUaGUgRmFsbCBhbmQgUmlzZSBvZiBJbmNvbWUgSW5lcXVhbGl0eQoKQSBbMjAxNQpwb3N0XShodHRwczovL3d3dy5ucHIub3JnL3NlY3Rpb25zL21vbmV5LzIwMTUvMDIvMTEvMzg0OTg4MTI4L3RoZS1mYWxsLWFuZC1yaXNlLW9mLXUtcy1pbmVxdWFsaXR5LWluLTItZ3JhcGhzKQpvbiB0aGUgTlBSIFBsYW5ldCBNb25leSBzaXRlIHVzZXMgdHdvIGdyYXBocyB0byBzaG93IHRyYW5zaXRpb25zIGluCmluY29tZSBpbmVxdWFsaXR5LgoKVGhlIHBhZ2UgaW5jbHVkZXMgYSBsaW5rIHRvIHRoZQo8IS0tIHRoaXMgdGltZXMgb3V0IGluIHRoZSBsaW5rIGNoZWNrIC0tPgpbZGF0YSBzb3VyY2VdKGh0dHA6Ly90b3BpbmNvbWVzLnBhcmlzc2Nob29sb2ZlY29ub21pY3MuZXUvKS4KClRoaXMgaGFzIGJlZW4gdW5hdmFpbGFibGUgZm9yIHNvbWUgdGltZS4KCkkgdHJpZWQgdG8gb2J0YWluIHRoZSBzYW1lIGRhdGEgYnV0IG1heSBub3QgaGF2ZSBkb25lIGl0IHF1aXRlIHJpZ2h0LgoKVGhlIGNsb3Nlc3Qgc291cmNlIEkgY291bGQgZmluZCBpcyA8aHR0cHM6Ly93aWQud29ybGQvZGF0YS8+LgoKVGhlIGRhdGEgaXMgYXZhaWxhYmxlIGxvY2FsbHk6CgpgYGB7cn0KaW5jb21lIDwtIHJlYWQudGFibGUoImh0dHBzOi8vd3d3LnN0YXQudWlvd2EuZWR1L35sdWtlL2RhdGEvbnByaW5jb21lLmRhdCIpCmBgYAoKUGxvdHMgb3ZlciB0aW1lOgoKYGBge3IgaW5jb21lLXNpbmdsZSwgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSA3LjV9CnNlbGVjdChpbmNvbWUsIC15ZWFyKSB8PgogICAgdHMoc3RhcnQgPSAxOTEzKSB8PgogICAgYXV0b3Bsb3QoKQpgYGAKCmBgYHtyIGluY29tZS1zaW5nbGUsIGV2YWwgPSBGQUxTRX0KYGBgCgpUaGUgcmFuZ2VzIGFyZSB0b28gZGlmZmVyZW50IGZvciB0aGlzIHRvIGJlIGVmZmVjdGl2ZS4KCk9uZSBhbHRlcm5hdGl2ZTogc3RhbmRhcmRpemUgdGhlIHNlcmllcyB0byBvbmUsIG9yIDEwMCUsIGF0IHNvbWUgcG9pbnQuCgpIZXJlIHRoZSBpbml0aWFsIHZhbHVlIGluIDE5MTMgaXMgdXNlZDoKCmBgYHtyIGluY29tZS1zaW5nbGUtcmVsLCBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDcuNX0Kc2VsZWN0KGluY29tZSwgLXllYXIpIHw+CiAgICB0cmFuc211dGUoTDkwcmVsID0gTDkwIC8gTDkwWzFdLAogICAgICAgICAgICAgIFUwMXJlbCA9IFUwMSAvIFUwMVsxXSkgfD4KICAgIHRzKHN0YXJ0ID0gMTkxMykgfD4KICAgIGF1dG9wbG90KCkKYGBgCmBgYHtyIGluY29tZS1zaW5nbGUtcmVsLCBldmFsID0gRkFMU0V9CmBgYAoKRmFjZXRlZCBwbG90cywgd2l0aCBmcmVlICR5JCBheGVzLCBhcmUgYW5vdGhlciBvcHRpb246CgpgYGB7ciBpbmNvbWUtZmFjZXQsIGVjaG8gPSBGQUxTRX0Kc2VsZWN0KGluY29tZSwgLXllYXIpIHw+CiAgICB0cyhzdGFydCA9IDE5MTMpIHw+CiAgICBhdXRvcGxvdChmYWNldHMgPSBUUlVFKQpgYGAKYGBge3IgaW5jb21lLWZhY2V0LCBldmFsID0gRkFMU0V9CmBgYAoKV2l0aCB0d28gc2VyaWVzIGFub3RoZXIgb3B0aW9uIGlzIGEgX2Nvbm5lY3RlZCBzY2F0dGVyIHBsb3RfLgoKYGBge3IgaW5jb21lLWNvbm5lY3RlZC0xLCBlY2hvID0gRkFMU0V9CmdncGxvdChpbmNvbWUsIGFlcyhVMDEsIEw5MCkpICsKICAgIGdlb21fcGF0aChsaW5ld2lkdGggPSAyKSArCiAgICBsYWJzKHggPSAiQXZlcmFnZSBpbmNvbWUgZm9yIHRoZSB0b3AgMSUiLAogICAgICAgICB5ID0gIkF2ZXJhZ2UgaW5jb21lIGZvciB0aGUgYm90dG9tIDkwJSIpCmBgYApgYGB7ciBpbmNvbWUtY29ubmVjdGVkLTEsIGV2YWwgPSBGQUxTRX0KYGBgCgpUaGlzIHNpbXBsZSBmb3JtIGRvZXMgbm90IHNob3cgdGhlIGRpcmVjdGlvbiBvciBzY2FsZSBvZiB0aW1lLgoKQ29sb3Igb3IgYW5ub3RhdGlvbiBjYW4gYmUgdXNlZCB0byBzaG93IGRpcmVjdGlvbiBhbmQgc2NhbGUgb2YgdGltZToKCmBgYHtyIGluY29tZS1jb25uZWN0ZWQtMiwgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSA3LjUsIHdhcm5pbmcgPSBGQUxTRX0KbGlicmFyeShnZ3JlcGVsKQpnZ3Bsb3QoaW5jb21lLCBhZXMoVTAxLCBMOTAsIGNvbG9yID0geWVhcikpICsKICAgIGdlb21fcGF0aChzaXplID0gMikgKwogICAgZ2VvbV90ZXh0X3JlcGVsKAogICAgICAgIGFlcyhsYWJlbCA9CiAgICAgICAgICAgICAgICBpZmVsc2UoeWVhciAlJSA1ID09IDAgfAogICAgICAgICAgICAgICAgICAgICAgIHllYXIgPT0gbWF4KHllYXIpLAogICAgICAgICAgICAgICAgICAgICAgIHllYXIsCiAgICAgICAgICAgICAgICAgICAgICAgIiIpKSwKICAgICAgICBjb2xvciA9ICJncmV5NTAiKSArCiAgICBsYWJzKHggPSAiQXZlcmFnZSBpbmNvbWUgZm9yIHRoZSB0b3AgMSUiLAogICAgICAgICB5ID0gIkF2ZXJhZ2UgaW5jb21lIGZvciB0aGUgYm90dG9tIDkwJSIpCmBgYApgYGB7ciBpbmNvbWUtY29ubmVjdGVkLTIsIGV2YWwgPSBGQUxTRX0KYGBgCgpBbmltYXRlZCBpcyBhbm90aGVyIG9wdGlvbjoKCjwhLS0gKioqKiBjb3VsZCBsb29rIGludG8gZ2V0dGluZyBjbG9zZXIgdG8gdGhlIG9yaWdpbmFsIE5QUiB2ZXJzaW9uIC0tPgoKYGBge3IgaW5jb21lLWdnYW5pbSwgZWNobyA9IEZBTFNFLCBjYWNoZSA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRX0KcCA8LSBnZ3Bsb3QoaW5jb21lLAogICAgICAgICAgICBhZXMoeCA9IFUwMSwgeSA9IEw5MCwKICAgICAgICAgICAgICAgIGNvbG9yID0geWVhcikpICsKICAgIGdlb21fcGF0aChzaXplID0gMiwKICAgICAgICAgICAgICBjb2xvciA9ICJsaWdodGdyZXkiLAogICAgICAgICAgICAgIGRhdGEgPSBtdXRhdGUoaW5jb21lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVhciA9IE5VTEwpKSArCiAgICBnZW9tX3BhdGgoc2l6ZSA9IDIpICsKICAgIGxhYnModGl0bGUgPSAiWWVhcjoge2ZyYW1lX2Fsb25nfSIsCiAgICAgICAgIHggPSAiQXZlcmFnZSBpbmNvbWUgZm9yIHRoZSB0b3AgMSUiLAogICAgICAgICB5ID0gIkF2ZXJhZ2UgaW5jb21lIGZvciB0aGUgYm90dG9tIDkwJSIpCgpsaWJyYXJ5KGdnYW5pbWF0ZSkKYW5pbWF0ZShwICsgdHJhbnNpdGlvbl9yZXZlYWwoeWVhciksCiAgICAgICAgZnBzID0gNSwgZW5kX3BhdXNlID0gMTApCmBgYApgYGB7ciBpbmNvbWUtZ2dhbmltLCBldmFsID0gRkFMU0V9CmBgYAoKCiMjIFN0YWNrIExvc3MgRGF0YQoKT3BlcmF0aW9uYWwgZGF0YSBvYnRhaW5lZCBmcm9tIDIxIGRheXMgb2Ygb3BlcmF0aW9uIG9mIGEgcGxhbnQgZm9yIHRoZQpveGlkYXRpb24gb2YgYW1tb25pYSAoTkgzKSB0byBuaXRyaWMgYWNpZCAoSE5PMykuCgpUaGUgZGVwZW5kZW50IHZhcmlhYmxlLCBgc3RhY2subG9zc2AgaXMgMTAgdGltZXMgdGhlIHBlcmNlbnRhZ2Ugb2YgdGhlCmluZ29pbmcgYW1tb25pYSB0byB0aGUgcGxhbnQgdGhhdCBlc2NhcGVzIGZyb20gdGhlIGFic29ycHRpb24gY29sdW1uCnVuYWJzb3JiZWQuCgpgYGB7ciBzdGFja2xvc3Mtc3Bsb20sIGVjaG8gPSBGQUxTRX0KbGlicmFyeShsYXR0aWNlKQpzcGxvbShzdGFja2xvc3MpCmBgYApgYGB7ciBzdGFja2xvc3Mtc3Bsb20sIGV2YWwgPSBGQUxTRX0KYGBgCgpUaGUgZGF0YSBhcmUgY29sbGVjdGVkIG92ZXIgdGltZToKCmBgYHtyIHN0YWNrbG9zcy1zcGxvbS1saW5lLCBlY2hvID0gRkFMU0UsIGZpZy5oZWlnaHQgPSA3fQpsaWJyYXJ5KGRwbHlyKQpzbCA8LSBtdXRhdGUoc3RhY2tsb3NzLAogICAgICAgICAgICAgaW50ZXggPSBzZXFfbGVuKG5yb3coc3RhY2tsb3NzKSkpCmNwYWwgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCIjMTMyQjQzIiwgIiM1NkIxRjciKSkKY29scyA8LSBjcGFsKG5yb3coc3RhY2tsb3NzKSAtIDEpCnBmdW4gPC0gZnVuY3Rpb24oeCwgeSwgLi4uKSB7CiAgICBuIDwtIGxlbmd0aCh4KQogICAgbHNlZ21lbnRzKHhbLW5dLCB5Wy1uXSwgeFstMV0sIHlbLTFdLAogICAgICAgICAgICAgIGNvbCA9IGNvbHMsIGx3ZCA9IDMpCn0Kc3Bsb20oc3RhY2tsb3NzLCBwYW5lbCA9IHBmdW4pCmBgYApgYGB7ciBzdGFja2xvc3Mtc3Bsb20tbGluZSwgZXZhbCA9IEZBTFNFfQpgYGAKClJhbmRvbWx5IHJlb3JkZXJpbmcgdGhlIGluZGljZXMgY2FuIGhlbHAgY2FsaWJyYXRlIHdoZXRoZXIgdGhlcmUgaXMgYQp0aW1lIGVmZmVjdDoKCmBgYHtyIHN0YWNrbG9zcy1zcGxvbS1saW5lLXJuZCwgZWNobyA9IEZBTFNFLCBmaWcuaGVpZ2h0ID0gN30Kc3Bsb20oc2FtcGxlX24oc3RhY2tsb3NzLCBucm93KHN0YWNrbG9zcykpLAogICAgICBwYW5lbCA9IHBmdW4pCmBgYApgYGB7ciBzdGFja2xvc3Mtc3Bsb20tbGluZS1ybmQsIGV2YWwgPSBGQUxTRX0KYGBgCgpUaGlzIGlzIHNvbWV0aW1lcyBjYWxsZWQgX2dyYXBoaWNhbCBpbmZlcmVuY2VfLgoKPiBIYWRsZXkgV2lja2hhbSwgRGlhbm5lIENvb2ssIEhlaWtlIEhvZm1hbm4sIGFuZCBBbmRyZWFzIEJ1amEsCj4gIkdyYXBoaWNhbCBJbmZlcmVuY2UgZm9yIEluZm92aXMsIiBfSUVFRSBUcmFuc2FjdGlvbnMgb24gVmlzdWFsaXphdGlvbgo+IGFuZCBDb21wdXRlciBHcmFwaGljc18sIFZvbC4gMTYsIE5vLiA2LCBOb3ZlbWJlci9EZWNlbWJlciAyMDEwLgoKUGlja2luZyBvbmUgcGxvdCBhbmQgZW1iZWRkaW5nIHRoZSB0cnVlIG9yZGVyIGFtb25nIDE5IHJhbmRvbSBvcmRlcmluZ3M6CgpgYGB7ciBzdGFjay1sb3NzLWVtYmVkLCBlY2hvID0gRkFMU0V9Ck4gPC0gMjAKayA8LSBzYW1wbGUoTiwgMSkKCm9yZCA8LSBzZXFfbGVuKG5yb3coc3RhY2tsb3NzKSkKc3MgPC0gZnVuY3Rpb24oaSkgewogICAgaWYgKGkgPT0gaykKICAgICAgICBzbCA8LSBzdGFja2xvc3MKICAgIGVsc2UKICAgICAgICBzbCA8LSBzYW1wbGVfbihzdGFja2xvc3MsIG5yb3coc3RhY2tsb3NzKSkKICAgIG11dGF0ZShzbCwgb3JkZXIgPSBvcmQsIHNhbXBsZSA9IGkpCn0KCmQgPC0gbGFwcGx5KDEgOiBOLCBzcykKZCA8LSBkby5jYWxsKHJiaW5kLCBkKQoKbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QoZCwgYWVzKHggPSBBY2lkLkNvbmMuLCB5ID0gc3RhY2subG9zcywgY29sb3IgPSBvcmRlcikpICsKICAgIGdlb21fcGF0aChzaXplID0gMC44KSArIGZhY2V0X3dyYXAofiBzYW1wbGUpICsKICAgIGd1aWRlcyhjb2xvciA9ICJub25lIikKYGBgClRydWUgb3JkZXI6IHBsb3QgYHIga2AuCgpUaGUgY29kZToKCmBgYHtyIHN0YWNrLWxvc3MtZW1iZWQsIGV2YWwgPSBGQUxTRX0KYGBgCgoKIyMgTHlueC1IYXJlIERhdGEKClRyYXBwaW5nIGRhdGEgb24gQ2FuYWRpYW4gbHlueCBhbmQgc25vd3Nob2UgaGFyZToKCjwhLS0KaHR0cDovL3Blb3BsZS53aGl0bWFuLmVkdS9+aHVuZGxlZHIvY291cnNlcy9NMjUwRjAzL00yNTAuaHRtbApodHRwOi8vcGVvcGxlLndoaXRtYW4uZWR1L35odW5kbGVkci9jb3Vyc2VzL00yNTBGMDMvTHlueEhhcmUudHh0Ci0tPgoKYGBge3IgbHlueGhhcmUtZmFjZXRzLCBlY2hvID0gRkFMU0V9Cmx5bnhoYXJlIDwtIHJlYWQudGFibGUoImh0dHBzOi8vd3d3LnN0YXQudWlvd2EuZWR1L35sdWtlL2RhdGEvTHlueEhhcmUudHh0IikKbmFtZXMobHlueGhhcmUpIDwtIGMoInllYXIiLCAiaGFyZSIsICJseW54IikKbGh0cyA8LSB0cyhzZWxlY3QobHlueGhhcmUsIC15ZWFyKSwgc3RhcnQgPSAxODQ1KQphdXRvcGxvdChsaHRzLCBmYWNldHMgPSBUUlVFKQpgYGAKCkN5Y2xlIHBhdHRlcm5zIGFyZSBzaW1pbGFyLgoKYGBge3IgbHlueGhhcmUtZmFjZXRzLCBldmFsID0gRkFMU0V9CmBgYAoKTHlueCBjeWNsZXMgb2Z0ZW4gbGFnIGJlaGluZDoKCmBgYHtyIGx5bnhoYXJlLXNpbmdsZSwgZWNobyA9IEZBTFNFfQphdXRvcGxvdChsaHRzKQpgYGAKYGBge3IgbHlueGhhcmUtc2luZ2xlLCBldmFsID0gRkFMU0V9CmBgYAoKCiMjIFRoaW5ncyB0byBMb29rIEZvcgoKQ2hhbmdlcyBpbiBkYXRhIGRlZmluaXRpb24uCgoqIEFkbWluaXN0cmF0aXZlIGRlZmluaXRpb25zLCBzdWNoIGFzIGVtcGxveW1lbnQvdW5lbXBsb3ltZW50IGNhbgogIGNoYW5nZSBvdmVyIHRpbWUuCgoqIEFkbWluaXN0cmF0aXZlIGVudGl0aWVzLCBzdWNoIGFzIGNvdW50cmllcyBhbmQgc3RhdGVzLCBjYW4KICBjaGFuZ2Ugb3ZlciB0aW1lLgoKKiBJbnN0cnVtZW50cyBjYW4gY2hhbmdlIChzYXRlbGl0ZSBpbWFnZXJ5IHJlcGxhY2luZyBzdXJmYWNlCiAgbWVhc3VyZW1lbnRzKS4KCk91dGxpZXJzIG5lZWQgbm90IGJlIGdsb2JhbGx5IG91dGx5aW5nOiBkZXBhcnR1cmVzIGZyb20gbG9jYWwKcGF0dGVybnMgbWF5IGFsc28gYmUgaW1wb3J0YW50LgoKTGV2ZWwgc2hpZnRzIGFuZCBjaGFuZ2VzIGluIHZvbGF0aWxpdHkuCgpTaG9ja3MgYWZmZWN0aW5nIGxldmVsIG9yIHZvbGF0aWxpdHkuCgoKIyMgQXVzdHJhbGlhbiBBaXJsaW5lIERhdGEKCltXZWVrbHkgcGFzc2VuZ2VyIGxvYWRzXShodHRwczovL290ZXh0cy5jb20vZnBwMi9ncmFwaGljcy5odG1sKSwgaW4KdGhvdXNhbmRzLCBvbiBBbnNldHQgQWlybGluZXMgYmV0d2VlbiBBdXN0cmFsaWHigJlzIHR3byBsYXJnZXN0IGNpdGllcwooYXZhaWxhYmxlIGFzIGBtZWxzeWRgIGluIHBhY2thZ2UgYGZwcDJgKToKCmBgYHtyIGF1c3RyYWxpYW4tYWlybGluZXMsIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpkYXRhKG1lbHN5ZCwgcGFja2FnZSA9ICJmcHAyIikKYXV0b3Bsb3QobWVsc3lkLCBmYWNldHMgPSBUUlVFKQpgYGAKClNvbWUgZmVhdHVyZXM6CgoqIFN0cmlrZSBpbiAxOTg5LgoKKiBJbmNyZWFzZSBpbiBwYXNzZW5nZXIgbG9hZCBzdGFydGluZyBpbiBzZWNvbmQgaGFsZiBvZiAxOTkxLgoKKiBEaXAgaW4gZWNvbm9teSwgcmlzZSBpbiBidXNpbmVzcyBpbiAxOTkyIGNvcnJlc3BvbmRpbmcgdG8gYW4gZXhwZXJpbWVudAogIHdpdGggZmV3ZXIgZWNvbm9teS9tb3JlIGJ1c2luZXNzIHNlYXRzLgoKYGBge3IgYXVzdHJhbGlhbi1haXJsaW5lcywgZXZhbCA9IEZBTFNFfQpgYGAKCgojIyBXZWVrbHkgU1AgNTAwIENsb3NpbmcgUmV0dXJucwoKV2Vla2x5IGNsb3NpbmcgcmV0dXJucyBvZiB0aGUgU1AgNTAwIGZyb20gMjAwMyB0byBTZXB0ZW1iZXIsIDIwMTI6CgpgYGB7ciBTUDUwMCwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmxpYnJhcnkoeHRzKQpsaWJyYXJ5KGFzdHNhKQphdXRvcGxvdChzcDUwMHcpCmBgYAoKU29tZSBmZWF0dXJlczoKCiogSW5jcmVhc2VkIHZvbGF0aWxpdHkgYXQgdGhlIHN0YXJ0IG9mIHRoZSBmaW5hbmNpYWwgY3Jpc2lzLgoKKiBWb2xhdGlsaXR5IHNlZW1zIHRvIGJlIGRlY3JlYXNpbmcsIGJ1dCBub3QgdmVyeSBmYXN0LgoKYGBge3IgU1A1MDAsIGV2YWwgPSBGQUxTRX0KYGBgCgoKIyMgRm9yZWNhc3RpbmcKClN0dWR5aW5nIHRoZSBwYXN0IG1heSBoZWxwIHByZWRpY3QgdGhlIGZ1dHVyZS4KCkZvcmVjYXN0aW5nIGEgdGltZSBzZXJpZXMgdXN1YWxseSBpbnZvbHZlcyBjaG9vc2luZyBhIG1vZGVsIGFuZApydW5uaW5nIHRoZSBtb2RlbCBmb3J3YXJkLgoKQWNjdXJhY3kgb2YgZm9yZWNhc3RzIGRlY3JlYXNlcyByYXBpZGx5IHRoZSBmYXJ0aGVyIGFoZWFkIHRoZSBmb3JlY2FzdAppcyBtYWRlLgoKQW4gZXhhbXBsZSBmcm9tIHRoZSBgZm9yZWNhc3RgIHBhY2thZ2U6IGBXV1d1c2FnZWAgaXMgYSB0aW1lIHNlcmllcyBvZgp0aGUgbnVtYmVycyBvZiB1c2VycyBjb25uZWN0ZWQgdG8gdGhlIEludGVybmV0LgoKYGBge3IgV1dXdXNhZ2UsIGV2YWwgPSBGQUxTRX0KbGlicmFyeShmb3JlY2FzdCkKZml0IDwtIGV0cyhXV1d1c2FnZSkKZmMgPC0gZm9yZWNhc3QoV1dXdXNhZ2UsIG1vZGVsID0gZml0KQphdXRvcGxvdChmYykKYGBgCgpgYGB7ciBXV1d1c2FnZSwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmBgYAoKCiMjIExhYmVsaW5nIFRyZW5kIExpbmVzCgpGb3IgdHJlbmQgbGluZXMgdXNpbmcgYSBzdGFuZGFyZCBndWlkZSBjYW4gYmUgYSBsaXR0bGUgYXdrd2FyZC4KCkF2ZXJhZ2UgbGlmZSBleHBlY3RhbmN5IGFnYWluc3QgeWVhciBmb3IgdGhlIGNvbnRpbmVudHMgaW4gdGhlCmBnYXBtaW5kZXJgIGRhdGE6CgpgYGB7ciB0cmVuZC1saW5lcy1ndWlkZSwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDcuNX0KbGlicmFyeShnYXBtaW5kZXIpCmQgPC0gZ3JvdXBfYnkoZ2FwbWluZGVyLAogICAgICAgICAgICAgIGNvbnRpbmVudCwgeWVhcikgfD4KICAgIHN1bW1hcml6ZShhdmdMaWZlRXhwID0gbWVhbihsaWZlRXhwKSkgfD4KICAgIHVuZ3JvdXAoKQoKcCA8LSBnZ3Bsb3QoZCwKICAgICAgICAgICAgYWVzKHggPSB5ZWFyLAogICAgICAgICAgICAgICAgeSA9IGF2Z0xpZmVFeHAsCiAgICAgICAgICAgICAgICBjb2xvciA9IGNvbnRpbmVudCkpICsKICAgIGdlb21fbGluZShzaXplID0gMSkKcApgYGAKYGBge3IgdHJlbmQtbGluZXMtZ3VpZGUsIGV2YWwgPSBGQUxTRX0KYGBgCgpXaXRoIHRoZSBsZWdlbmQgb24gdGhlIHJpZ2h0LCBhIHNtYWxsIGltcHJvdmVtZW50IGlzIHRvIG9yZGVyIHRoZQpsZXZlbHMgdG8gbWF0Y2ggdGhlIG9yZGVyIG9mIHRoZSByaWdodCBoYW5kIGN1cnZlIHZhbHVlczoKCmBgYHtyIHRyZW5kLWxpbmVzLWd1aWRlLXJlb3JkZXIsIGVjaG8gPSBGQUxTRX0KZF9sYXN0IDwtIGZpbHRlcihkLCB5ZWFyID09IG1heCh5ZWFyKSkgfD4KICAgIG11dGF0ZShjb250aW5lbnQgPQogICAgICAgICAgICAgICByZW9yZGVyKGNvbnRpbmVudCwgLWF2Z0xpZmVFeHApKQoKcCAlKyUKICAgIG11dGF0ZShkLAogICAgICAgICAgIGNvbnRpbmVudCA9CiAgICAgICAgICAgICAgIGZhY3Rvcihjb250aW5lbnQsCiAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMoZF9sYXN0JGNvbnRpbmVudCkpKQpgYGAKCmBgYHtyIHRyZW5kLWxpbmVzLWd1aWRlLXJlb3JkZXIsIGV2YWwgPSBGQUxTRX0KYGBgCgpfRGlyZWN0IGxhYmVsaW5nXyBpcyBvZnRlbiBhIGJldHRlciBjaG9pY2UuCgpPbmUgb3B0aW9uIGlzIHRvIHVzZSBgZ2VvbV90ZXh0YCBhdCB0aGUgZW5kcyBvZiB0aGUgbGluZXMuCgpUaGlzIG1heSBuZWVkIHNvbWUgYWRkaXRpb25hbCBhZGp1c3RtZW50cyB0byBtYXJnaW5zIGFuZCBjbGlwcGluZwpiZWhhdmlvcjoKCmBgYHtyIHRyZW5kLWxpbmVzLWRpcmVjdCwgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSA3LjV9CnAgPC0gcCArIGd1aWRlcyhjb2xvciA9ICJub25lIikKCmRfbGFzdCA8LSBmaWx0ZXIoZCwgeWVhciA9PSBtYXgoeWVhcikpCgpwICsgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGNvbnRpbmVudCksCiAgICAgICAgICAgICAgZGF0YSA9IGRfbGFzdCwKICAgICAgICAgICAgICBzaXplID0gNSwKICAgICAgICAgICAgICBoanVzdCA9IC0wLjEpICsKICAgIGNvb3JkX2NhcnRlc2lhbihjbGlwID0gIm9mZiIsCiAgICAgICAgICAgICAgICAgICAgZXhwYW5kID0gRkFMU0UpICsKICAgIHRoZW1lKHBsb3QubWFyZ2luID0KICAgICAgICAgICAgICBtYXJnaW4oMzAsIDYwLCAxMCwgMTApKQpgYGAKYGBge3IgdHJlbmQtbGluZXMtZGlyZWN0LCBldmFsID0gRkFMU0V9CmBgYAoKQW5vdGhlciBvcHRpb24gaXMgdG8gdXNlIGEgc2Vjb25kIGF4aXM6CjwhLS0KQWRhcHRlZCBmcm9tIGh0dHBzOi8vd2lsa2VsYWIub3JnL1NEUzM3NS9zbGlkZXMvcmVkdW5kYW50LWNvZGluZy5odG1sIzE2Ci0tPgoKYGBge3IgdHJlbmQtbGluZXMtc2VjLWF4aXMsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gNy41fQpwICsgc2NhbGVfeV9jb250aW51b3VzKAogICAgICAgIHNlYy5heGlzID0gZHVwX2F4aXMoCiAgICAgICAgICAgIGJyZWFrcyA9IGRfbGFzdCRhdmdMaWZlRXhwLAogICAgICAgICAgICBsYWJlbHMgPSBkX2xhc3QkY29udGluZW50LAogICAgICAgICAgICBuYW1lID0gTlVMTCkpICsKICAgIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApKQpgYGAKYGBge3IgdHJlbmQtbGluZXMtc2VjLWF4aXMsIGV2YWwgPSBGQUxTRX0KYGBgCgpJdCBpcyBhbHNvIHBvc3NpYmxlIHRvIHBsYWNlIHRoZSBsYWJlbHMgYWxvbmcgdGhlIGxpbmVzLgoKVGhlIFtgZ2VvbXRleHRwYXRoYApwYWNrYWdlXShodHRwczovL2FsbGFuY2FtZXJvbi5naXRodWIuaW8vZ2VvbXRleHRwYXRoLykgcHJvdmlkZXMKc3VwcG9ydCBmb3IgdGhpcy4KCmBgYHtyIHRyZW5kLWxpbmVzLXRleHQtcGF0aCwgZWNobyA9IEZBTFNFfQpsaWJyYXJ5KGdlb210ZXh0cGF0aCkKZ2dwbG90KGQsIGFlcyh4ID0geWVhciwKICAgICAgICAgICAgICB5ID0gYXZnTGlmZUV4cCwKICAgICAgICAgICAgICBjb2xvciA9IGNvbnRpbmVudCkpICsKICAgIGdlb21fdGV4dGxpbmUoYWVzKGxhYmVsID0gY29udGluZW50KSwKICAgICAgICAgICAgICAgICAgbGluZXdpZHRoID0gMSwKICAgICAgICAgICAgICAgICAgdmp1c3QgPSAtMC41LAogICAgICAgICAgICAgICAgICBoanVzdCA9IDAuOTUpICsKICAgIGd1aWRlcyhjb2xvciA9ICJub25lIikKYGBgCgpgYGB7ciB0cmVuZC1saW5lcy10ZXh0LXBhdGgsIGV2YWwgPSBGQUxTRX0KYGBgCgoKIyMgQnVtcCBDaGFydHMKClRvcCAxMCBjb3VudHJpZXMgaW4gdGVybXMgb2YgR0RQIHBlciBjYXBpdGEgaW4gMjAwNzoKCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KdG9wX2dkcCA8LSBmaWx0ZXIoZ2FwbWluZGVyLAogICAgICAgICAgICAgICAgICBjb250aW5lbnQgPT0gIkV1cm9wZSIsCiAgICAgICAgICAgICAgICAgIHllYXIgPT0gbWF4KHllYXIpKSB8PgogICAgc2xpY2VfbWF4KGdkcFBlcmNhcCwgbiA9IDEwKSB8PgogICAgcHVsbChjb3VudHJ5KQpkIDwtIGZpbHRlcihnYXBtaW5kZXIsCiAgICAgICAgICAgIGNvdW50cnkgJWluJSB0b3BfZ2RwKSB8PgogICAgZ3JvdXBfYnkoeWVhcikgfD4KICAgIG11dGF0ZSgKICAgICAgICByYW5rID0gbWluX3JhbmsoZGVzYyhnZHBQZXJjYXApKSkgfD4KICAgIHVuZ3JvdXAoKQpkX2xhc3QgPC0gZmlsdGVyKGQsIHllYXIgPT0gbWF4KHllYXIpKQoKZ2dwbG90KGQsIGFlcyh5ZWFyLAogICAgICAgICAgICAgIGdkcFBlcmNhcCwKICAgICAgICAgICAgICBjb2xvciA9IGNvdW50cnkpKSArCiAgICBnZW9tX2xpbmUoKQpgYGAKClRoZSBkb21pbmFudCBmZWF0dXJlIGlzIHRoZSBvdmVyYWxsIHRyZW5kLgoKT25lIG9wdGlvbjogc2hvdyBkZXZpYXRpb25zIGZyb20gdGhlIG92ZXJhbGwgdHJlbmQuCgpBbm90aGVyIG9wdGlvbjogc2hvdyByYW5rcyB3aXRoaW4geWVhcnMuCgpUaGlzIGlzIGNhbGxlZCBhIF9idW1wIGNoYXJ0Xy4KCkEgc2ltcGxlIGJ1bXAgY2hhcnQ6CgpgYGB7ciwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CmdncGxvdChkLCBhZXMoeWVhciwgcmFuaywgY29sb3IgPSBjb3VudHJ5KSkgKwogICAgZ2VvbV9saW5lKHNpemUgPSAxKSArCiAgICBzY2FsZV95X3JldmVyc2UoYnJlYWtzID0gMToxMCkgKwogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGNvdW50cnkpLAogICAgICAgICAgICAgIGRhdGEgPSBkX2xhc3QsCiAgICAgICAgICAgICAgc2l6ZSA9IDUsCiAgICAgICAgICAgICAgaGp1c3QgPSAtMC4xKSArCiAgICBjb29yZF9jYXJ0ZXNpYW4oY2xpcCA9ICJvZmYiLAogICAgICAgICAgICAgICAgICAgIGV4cGFuZCA9IEZBTFNFKSArCiAgICB0aGVtZShwbG90Lm1hcmdpbiA9CiAgICAgICAgICAgICAgbWFyZ2luKDMwLCA5MCwgMTAsIDEwKSkgKwogICAgZ3VpZGVzKGNvbG9yID0gIm5vbmUiKQpgYGAKClRoZSBgZ2didW1wYCBwYWNrYWdlIHByb3ZpZGVzIGBnZW9tX2J1bXBgIGZvciBkcmF3aW5nIHNtb290aCBidW1wCmNoYXJ0IGxpbmVzOgpgYGB7ciwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CmxpYnJhcnkoZ2didW1wKQpnZ3Bsb3QoZCwgYWVzKHllYXIsIHJhbmssIGNvbG9yID0gY291bnRyeSkpICsKICAgIGdlb21fYnVtcChzaXplID0gMSkgKwogICAgc2NhbGVfeV9yZXZlcnNlKGJyZWFrcyA9IDE6MTApICsKICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBjb3VudHJ5KSwKICAgICAgICAgICAgICBkYXRhID0gZF9sYXN0LAogICAgICAgICAgICAgIHNpemUgPSA1LAogICAgICAgICAgICAgIGhqdXN0ID0gLTAuMSkgKwogICAgY29vcmRfY2FydGVzaWFuKGNsaXAgPSAib2ZmIiwKICAgICAgICAgICAgICAgICAgICBleHBhbmQgPSBGQUxTRSkgKwogICAgdGhlbWUocGxvdC5tYXJnaW4gPQogICAgICAgICAgICAgIG1hcmdpbigzMCwgOTAsIDEwLCAxMCkpICsKICAgIGd1aWRlcyhjb2xvciA9ICJub25lIikKYGBgCgoKIyMgT3RoZXIgQXBwcm9hY2hlcwoKQSBbdGFsayBmcm9tCjIwMTldKGh0dHBzOi8vdmlzdWFsaXNpbmdkYXRhLmNvbS8yMDE5LzA0L3RhbGstc2xpZGVzLWRlc2lnbi1vZi10aW1lLykKZGVzY3JpYmVzIGEgd2lkZSByYW5nZSBvZiBvcHRpb25zIGZvciB2aXN1YWxpemluZyB0ZW1wb3JhbCBkYXRhLgoKCiMjIFJlYWRpbmcKCkNoYXB0ZXJzIFtfVmlzdWFsaXppbmcgdGltZSBzZXJpZXMgYW5kIG90aGVyIGZ1bmN0aW9ucyBvZiBhbgogIGluZGVwZW5kZW50CiAgdmFyaWFibGVfXShodHRwczovL2NsYXVzd2lsa2UuY29tL2RhdGF2aXovdGltZS1zZXJpZXMuaHRtbCkgYW5kCiAgW19WaXN1YWxpemluZwogIHRyZW5kc19dKGh0dHBzOi8vY2xhdXN3aWxrZS5jb20vZGF0YXZpei92aXN1YWxpemluZy10cmVuZHMuaHRtbCkgaW4KICBbX0Z1bmRhbWVudGFscyBvZiBEYXRhCiAgVmlzdWFsaXphdGlvbl9dKGh0dHBzOi8vY2xhdXN3aWxrZS5jb20vZGF0YXZpei8pLgoKCiMjIEV4ZXJjaXNlcwoKMS4gQ3JlYXRpbmcgYSB0aW1lIHNlcmllcyBvciBtdWx0aXBsZSB0aW1lIHNlcmllcyBvYmplY3QgYW5kIHVzaW5nCiAgICBhdXRvcGxvdCBjYW4gYmUgYW4gZWZmaWNpZW50IHdheSB0byBvYnRhaW4gbGluZSBwbG90cyBvZiB0aW1lCiAgICBzZXJpZXMgZGF0YS4gVGhlIGZvbGxvd2luZyBjb2RlIHVzZXMgdGhlIGB3ZWF0aGVyYCB0YWJsZSBpbiB0aGUKICAgIGBueWNmbGlnaHRzMTNgIHBhY2thZ2UgdG8gY3JlYXRlIGEgZGF0YSBmcmFtZSBjb250YWluaW5nIG1pbmltdW0KICAgIGFuZCBtYXhpbXVtIHRlbXBlcmF0dXJlcyBmb3IgZWFjaCBtb250aCBpbiAyMDEzOgoKICAgIGBgYHtyfQogICAgbGlicmFyeSh0aWR5dmVyc2UpCiAgICBsaWJyYXJ5KG55Y2ZsaWdodHMxMykKICAgIGxpYnJhcnkobHVicmlkYXRlKQogICAgbGlicmFyeShmb3JlY2FzdCkKCiAgICB0ZW1wcyA8LSBncm91cF9ieSh3ZWF0aGVyLCB5ZWFyLCBtb250aCkgfD4KICAgICAgICBzdW1tYXJpemUobWF4X3RlbXAgPSBtYXgodGVtcCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgbWluX3RlbXAgPSBtaW4odGVtcCwgbmEucm0gPSBUUlVFKSkgfD4KICAgICAgICB1bmdyb3VwKCkKICAgIGBgYAoKICAgIFdoaWNoIG9mIHRoZSBmb2xsb3dpbmcgZXhwcmVzc2lvbnMgcHJvZHVjZXMgYSBsaW5lIHBsb3Qgc2hvd2luZwogICAgdGhlIG1pbmltdW0gYW5kIG1heGltdW0gdGVtcGVyYXR1cmUgc2VyaWVzIChhbmQgbm8gb3RoZXIgc2VyaWVzKT8KCiAgICBhLiBgYXV0b3Bsb3QodGVtcHMpYAogICAgYi4gYGF1dG9wbG90KHRlbXBzJG1heF90ZW1wLCB0ZW1wcyRtaW5fdGVtcClgCiAgICBjLiBgdHModGVtcHMpIHw+IGF1dG9wbG90KClgCiAgICBkLiBgc2VsZWN0KHRlbXBzLCBtYXhfdGVtcCwgbWluX3RlbXApIHw+IHRzKCkgfD4gYXV0b3Bsb3QoKWAKCgo8IS0tCkx5bngtSGFyZSBEYXRhIFZhcmlhbnRzCmh0dHBzOi8vZ2lzdC5naXRodWIuY29tL21hZ2VzLzFmMGYwZDViYmU1MGFmODFjYzE5Cmh0dHBzOi8vZ2l0aHViLmNvbS9zdGFuLWRldi9leGFtcGxlLW1vZGVscy90cmVlL21hc3Rlci9rbml0ci9sb3RrYS12b2x0ZXJyYQoKSGFyZXNMeW54T2JzZXJ2YXRpb25zIDwtICJZZWFyICAgSGFyZXMueC4xMDAwICAgIEx5bngueC4xMDAwCjE5MDAgICAgMzAgICAgICA0CjE5MDEgICAgNDcuMiAgICA2LjEKMTkwMiAgICA3MC4yICAgIDkuOAoxOTAzICAgIDc3LjQgICAgMzUuMgoxOTA0ICAgIDM2LjMgICAgNTkuNAoxOTA1ICAgIDIwLjYgICAgNDEuNwoxOTA2ICAgIDE4LjEgICAgMTkKMTkwNyAgICAyMS40ICAgIDEzCjE5MDggICAgMjIgICAgICA4LjMKMTkwOSAgICAyNS40ICAgIDkuMQoxOTEwICAgIDI3LjEgICAgNy40CjE5MTEgICAgNDAuMyAgICA4CjE5MTIgICAgNTcgICAgICAxMi4zCjE5MTMgICAgNzYuNiAgICAxOS41CjE5MTQgICAgNTIuMyAgICA0NS43CjE5MTUgICAgMTkuNSAgICA1MS4xCjE5MTYgICAgMTEuMiAgICAyOS43CjE5MTcgICAgNy42ICAgICAxNS44CjE5MTggICAgMTQuNiAgICA5LjcKMTkxOSAgICAxNi4yICAgIDEwLjEKMTkyMCAgICAyNC43ICAgIDguNiIKCmhsLm9icyA8LSByZWFkLnRhYmxlKHRleHQ9SGFyZXNMeW54T2JzZXJ2YXRpb25zLCBoZWFkZXI9VFJVRSkKbmFtZXMoaGwub2JzKSA8LSBjKCJZZWFyIiwgIkhhcmVzIiwgIkx5bngiKSAjIyBQb3B1bGF0aW9uIGluIDAwMCdzCmxpYnJhcnkobGF0dGljZSkKeHlwbG90KEhhcmVzK0x5bnh+IFllYXIsIGRhdGEgPSBobC5vYnMsdHlwZSA9ICJsIiwgYXV0by5rZXkgPSBUUlVFKQoKaHR0cHM6Ly9vcGVuZGF0YS5zdGFja2V4Y2hhbmdlLmNvbS9xdWVzdGlvbnMvNjc2Ny93aGVyZS1jYW4taS1maW5kLWJpb2xvZ2ljYWwtdGltZS1zZXJpZXMtZGF0YT91dG1fbWVkaXVtPW9yZ2FuaWMmdXRtX3NvdXJjZT1nb29nbGVfcmljaF9xYSZ1dG1fY2FtcGFpZ249Z29vZ2xlX3JpY2hfcWEKLS0+Cg==