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(U01rel = U01 / U01[1],
L90rel = L90 / L90[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 .
library(ggrepel)
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:
library(gganimate)
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%") +
transition_reveal(year)
animate(p, height = 350, width = 500,
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 4.
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 fpp
):
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 = "fpp")
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:
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/CgotIEhvdyBhcmUgdGhlIHNlcmllcyByZWxhdGVkPwoKLSBEb2VzIG9uZSBzZXJpZXMgbGVhZCBvciBsYWcgdGhlIG90aGVycz8KClNvbWUgb3B0aW9ucyBmb3IgcGxvdHRpbmcgbXVsdGlwbGUgc2VyaWVzOgoKLSBzZXBhcmF0ZSBwYW5lbHMgaW4gYSB0cmVsbGlzIGRpc3BsYXk7CgotIG11bHRpcGxlIHNlcmllcyBpbiBhIHNpbmdsZSBwbG90IC0gdGhpcyB3aWxsIHJlcXVpcmUgc3RhbmRhcmRpemluZwogIGlmIHRoZSBzY2FsZXMgdmFyeSBzdWJzdGFudGlhbGx5OwoKLSBhIG11bHRpdmF0aWF0ZSBwbG90IHdpdGggdGltZSByZXByZXNlbnRlZCBieSBjb25uZWN0aW5nIGxpbmUKICBzZWdtZW50cyBvciBhbmltYXRpb24uCgpBbm90aGVyIG9wdGlvbiBmb3IgdHdvIHRpbWUgc2VyaWVzOgoKLSB1c2UgYSBzZXBhcmF0ZSBgeWAgYXhpcyBmb3IgZWFjaC4KClRoaXMgaXMgdXN1YWxseSBhIGJhZCBpZGVhLgoKU29tZSBkYXRhIHN0cnVjdHVyZXMgZm9yIG11bHRpcGxlIHRpbWUgc2VyaWVzOgoKKiBgbXRzYCBvYmplY3RzIGNyZWF0ZWQgYnkgYHRzKClgLgoKICAgIC0gQWxsIHNlcmllcyBoYXZlIHRvIGJlIG51bWVyaWMuCiAgICAtIGBhdXRvcGxvdGAgbWV0aG9kcyBtYXkgYmUgc3VmZmljaWVudC4KCiogUmVndWxhciBkYXRhIGZyYW1lcy4KCiAgICAtIFRoZSBzZXJpZXMgd2lsbCBoYXZlIHRvIGJlIHBsb3R0ZWQgaW4gc2VwYXJhdGUgbGF5ZXJzLAogICAgLSBvciB0aGUgZGF0YSBmcmFtZXMgd2lsbCBuZWVkIGNvbnZlcnRpbmcgaW50byBsb25nIGZvcm0uCgoKIyMgQ3JpbWVhbiBXYXIgQ2FzdWFsdGllcwoKVGhlIGBIaXN0RGF0YWAgcGFja2FnZSBjb250YWlucyB0aGUgZGF0YSB1c2VkIGJ5IEZsb3JlbmNlIE5pZ2h0aW5nYWxlCnRvIGNhbGwgYXR0ZW50aW9uIHRvIHRlcnJpYmxlIHNhbml0YXJ5IGNvbmRpdGlvbnMgZm9yIHRoZSBCcml0aWNoIGFybXkKaW4gQ3JpbWVhLgoKYGBge3J9CmRhdGEoTmlnaHRpbmdhbGUsIHBhY2thZ2UgPSAiSGlzdERhdGEiKQpoZWFkKHNlbGVjdChOaWdodGluZ2FsZSwgLURhdGUpKQpgYGAKCkEgcGxvdCBvZiB0aGUgYERpc2Vhc2VgLCBgV291bmRzYCwgYW5kIGBPdGhlcmA6CgpgYGB7ciBuaWdodC10cy1hdXRvLCBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDcuNX0KbkRXTyA8LSBzZWxlY3QoTmlnaHRpbmdhbGUsCiAgICAgICAgICAgICAgIERpc2Vhc2UsIFdvdW5kcywgT3RoZXIpICU+JQogICAgdHMoc3RhcnQgPSBjKDE4NTQsIDQpLCBmcmVxdWVuY3kgPSAxMikKCmF1dG9wbG90KG5EV08pCmBgYApgYGB7ciBuaWdodC10cy1hdXRvLCBldmFsID0gRkFMU0V9CmBgYAoKVXNpbmcgdGhlIHJhdGUgdmFyaWFibGVzIHByb2R1Y2VzIGEgdmVyeSBzaW1pbGFyIHBhdHRlcm46CgpgYGB7ciBuaWdodC1yYXRlLXRzLWF1dG8sIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gNy41fQpuRFdPLnJhdGUgPC0gc2VsZWN0KE5pZ2h0aW5nYWxlLAogICAgICAgICAgICAgICAgICAgIGVuZHNfd2l0aCgicmF0ZSIpKSAlPiUKICAgIHRzKHN0YXJ0ID0gYygxODU0LCA0KSwgZnJlcXVlbmN5ID0gMTIpCgphdXRvcGxvdChuRFdPLnJhdGUpCmBgYApgYGB7ciBuaWdodC1yYXRlLXRzLWF1dG8sIGV2YWwgPSBGQUxTRX0KYGBgCgpUaGUgYXJteSBzaXplIGRvZXMgbm90IHZhcnkgZW5vdWdoIG92ZXIgdGhlIHRpbWUgd2hlcmUgdGhlIGNhc3VhbHRpZXMKYXJlIGhpZ2ggdG8gYWZmZWN0IHRoZSBwYXR0ZXJuIG11Y2g6CgpgYGB7ciBuaWdodC1hcm15LXRzLWF1dG8sIGVjaG8gPSBGQUxTRX0KbkFybXkgPC0gc2VsZWN0KE5pZ2h0aW5nYWxlLCBBcm15KSAlPiUKICAgIHRzKHN0YXJ0ID0gYygxODU0LCA0KSwgZnJlcXVlbmN5ID0gMTIpCgphdXRvcGxvdChuQXJteSkKYGBgCmBgYHtyIG5pZ2h0LWFybXktdHMtYXV0bywgZXZhbCA9IEZBTFNFfQpgYGAKCkluc3RlYWQgb2Ygc2hvd2luZyB0aHJlZSBzZXJpZXMgaW4gb25lIHBsb3QgeW91IGNhbiBhbHNvIHVzZSBmYWNldGluZy4KCmBgYHtyIG5pZ2h0LWF1dG8tZmFjZXQsIGVjaG8gPSBGQUxTRX0KYXV0b3Bsb3QobkRXTywgZmFjZXRzID0gVFJVRSkKYGBgCmBgYHtyIG5pZ2h0LWF1dG8tZmFjZXQsIGV2YWwgPSBGQUxTRX0KYGBgCgpUbyBjcmVhdGUgdGhlIHBsb3Qgb2YgYERpc2Vhc2VgLCBgV291bmRzYCwgYW5kIGBPdGhlcmAgdXNpbmcgYmFzaWMKYGdncGxvdGAgaXQgaXMgYmVzdCB0byBmaXJzdCBjb252ZXJ0IHRoZSBkYXRhIGludG8gbG9uZyBmb3JtLgoKWW91IGNhbiBjcmVhdGUgYSBsb25nIHZlcnNpb24gb2YgdGhlIGRhdGEsIHVzaW5nIGVpdGhlciBgcGl2b3RfbG9uZ2VyYApvciBgYnJvb206OnRpZHlgLgoKVXNpbmcgYGJyb29tOnRpZHlgIG9uIHRoZSBgbXRzYCBvYmplY3Q6CgpgYGB7ciBuaWdodC1nZ3Bsb3QtYnJvb20sIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gNy41fQpicm9vbTo6dGlkeShuRFdPKSAlPiUKICAgIHJlbmFtZShEYXRlID0gaW5kZXgsCiAgICAgICAgICAgRGVhdGhzID0gdmFsdWUsCiAgICAgICAgICAgQ2F1c2UgPSBzZXJpZXMpICU+JQogICAgZ2dwbG90KGFlcyh4ID0gRGF0ZSwKICAgICAgICAgICAgICAgeSA9IERlYXRocywKICAgICAgICAgICAgICAgY29sb3IgPSBDYXVzZSkpICsKICAgIGdlb21fbGluZSgpCmBgYApgYGB7ciBuaWdodC1nZ3Bsb3QtYnJvb20sIGV2YWwgPSBGQUxTRX0KYGBgCgpVc2luZyBgcGl2b3RfbG9uZ2VyYCBvbiB0aGUgb3JpZ2luYWwgZGF0YSBmcmFtZToKCmBgYHtyIG5pZ2h0LWdncGxvdC1waXZvdC1sb25nZXIsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gNy41fQpzZWxlY3QoTmlnaHRpbmdhbGUsCiAgICAgICBEYXRlLCBEaXNlYXNlLCBXb3VuZHMsIE90aGVyKSAlPiUKICAgIHBpdm90X2xvbmdlcigtRGF0ZSwKICAgICAgICAgICAgICAgICBuYW1lc190byA9ICJDYXVzZSIsCiAgICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIkRlYXRocyIpICU+JQogICAgZ2dwbG90KGFlcyh4ID0gRGF0ZSwKICAgICAgICAgICAgICAgeSA9IERlYXRocywKICAgICAgICAgICAgICAgY29sb3IgPSBDYXVzZSkpICsKICAgIGdlb21fbGluZSgpCmBgYApgYGB7ciBuaWdodC1nZ3Bsb3QtcGl2b3QtbG9uZ2VyLCBldmFsID0gRkFMU0V9CmBgYAoKVXNpbmcgZmFjZXRpbmcgaW5zdGVhZCBvZiBhIHNpbmdsZSBwbG90OgoKYGBge3IgbmlnaHQtZ2dwbG90LWZhY2V0LCBlY2hvID0gRkFMU0V9CnNlbGVjdChOaWdodGluZ2FsZSwKICAgICAgIERhdGUsIERpc2Vhc2UsIFdvdW5kcywgT3RoZXIpICU+JQogICAgcGl2b3RfbG9uZ2VyKC1EYXRlLAogICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkNhdXNlIiwKICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiRGVhdGhzIikgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBEYXRlLCB5ID0gRGVhdGhzKSkgKwogICAgZ2VvbV9saW5lKCkgKwogICAgZmFjZXRfd3JhcCh+IENhdXNlLCBuY29sID0gMSkKYGBgCmBgYHtyIG5pZ2h0LWdncGxvdC1mYWNldCwgZXZhbCA9IEZBTFNFfQpgYGAKClRvIGZvY3VzIG9uIHRoZSBzaGFwZSBvZiB0aGUgY3VydmVzIGl0IGlzIG9mdGVuIHVzZWZ1bCB0byBhbGxvdyB0aGUKdmVydGljYWwgYXhpcyByYW5nZXMgdG8gYWRqdXN0IHRvIHRoZSBkYXRhOgoKYGBge3IgbmlnaHQtZ2dwbG90LWZhY2V0LWZyZWUteSwgZWNobyA9IEZBTFNFfQpzZWxlY3QoTmlnaHRpbmdhbGUsCiAgICAgICBEYXRlLCBEaXNlYXNlLCBXb3VuZHMsIE90aGVyKSAlPiUKICAgIHBpdm90X2xvbmdlcigtRGF0ZSwKICAgICAgICAgICAgICAgICBuYW1lc190byA9ICJDYXVzZSIsCiAgICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIkRlYXRocyIpICU+JQogICAgZ2dwbG90KGFlcyh4ID0gRGF0ZSwgeSA9IERlYXRocykpICsKICAgIGdlb21fbGluZSgpICsKICAgIGZhY2V0X3dyYXAofiBDYXVzZSwgbmNvbCA9IDEsCiAgICAgICAgICAgICAgIHNjYWxlcyA9ICJmcmVlX3kiKQpgYGAKYGBge3IgbmlnaHQtZ2dwbG90LWZhY2V0LWZyZWUteSwgZXZhbCA9IEZBTFNFfQpgYGAKClRoaXMgaXMgcGFydGljdWxhcmx5IHVzZWZ1bCB3aGVuIHRoZSByYW5nZXMgYXJlIHZlcnkgZGlmZmVyZW50LCBmb3IKZXhhbXBsZSBpZiB0aGUgYEFybXlgIHNlcmllcyBpcyBpbmNsdWRlZDoKCmBgYHtyIG5pZ2h0LWdncGxvdC1mYWNldC1mcmVlLXktYXJteSwgZWNobyA9IEZBTFNFfQpzZWxlY3QoTmlnaHRpbmdhbGUsCiAgICAgICBBcm15LCBEYXRlLCBEaXNlYXNlLCBXb3VuZHMsIE90aGVyKSAlPiUKICAgIHBpdm90X2xvbmdlcigtRGF0ZSwKICAgICAgICAgICAgICAgICBuYW1lc190byA9ICJzZXJpZXMiLAogICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJEZWF0aHMiKSAlPiUKICAgIGdncGxvdChhZXMoRGF0ZSwgRGVhdGhzKSkgKwogICAgZ2VvbV9saW5lKCkgKwogICAgZmFjZXRfd3JhcCh+IHNlcmllcywgbmNvbCA9IDEsCiAgICAgICAgICAgICAgIHNjYWxlcyA9ICJmcmVlX3kiKQpgYGAKYGBge3IgbmlnaHQtZ2dwbG90LWZhY2V0LWZyZWUteS1hcm15LCBldmFsID0gRkFMU0V9CmBgYAoKCiMjIFNvbWUgTm90ZXMKClVzaW5nIGBhdXRvcGxvdGAgaXMgb2Z0ZW4gYSBxdWljayB3YXkgdG8gZ2V0IHVzZWZ1bCBwbG90cy4KClVzaW5nIGJhc2ljIGBnZ3Bsb3RgIGdyYXBoaWNzIGlzIGEgYml0IG1vcmUgd29yayBidXQgcHJvdmlkZXMgbW9yZQpmbGV4aWJpbGl0eS4KCgojIyBNYW55IFRpbWUgU2VyaWVzCgpBbm51YWwgZmxvdyBmb3IgZGFtcyBpbiBJdGFseToKCmBgYHtyIGl0YWx5LWRhbXMtc2luZ2xlLCBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDh9CmRhdGEoaHlkcm9TSU1OLCBwYWNrYWdlID0gIm5zUkZBIikKYW5udWFsZmxvd3MgPC0gbXV0YXRlKGFubnVhbGZsb3dzLAogICAgICAgICAgICAgICAgICAgICAgY29kID0gZmFjdG9yKGNvZCkpCgpnZ3Bsb3QoYW5udWFsZmxvd3MsCiAgICAgICBhZXMoeCA9IGFubm8sIHkgPSBkYXRvLAogICAgICAgICAgIGdyb3VwID0gY29kLAogICAgICAgICAgIGNvbG9yID0gZmFjdG9yKGNvZCkpKSArCiAgICBnZW9tX2xpbmUoKQpgYGAKQ2xlYXJseSBzaG93aW5nIGFsbCBzZXJpZXMgaW4gYSBzaW5nbGUgcGxvdCBpcyBub3QgaGVscGZ1bCEKCmBgYHtyIGl0YWx5LWRhbXMtc2luZ2xlLCBldmFsID0gRkFMU0V9CmBgYAoKV2l0aCBzbWFsbCBtdWx0aXBsZXMgc29tZSBwYXR0ZXJucyBjYW4gYmUgc2VlbjoKCmBgYHtyIGl0YWx5LWRhbXMtZmFjZXQsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDl9CmdncGxvdChhbm51YWxmbG93cywKICAgICAgIGFlcyh4ID0gYW5ubywgeSA9IGRhdG8sCiAgICAgICAgICAgZ3JvdXAgPSBjb2QpKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICBmYWNldF93cmFwKH4gY29kKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9CiAgICAgICAgICAgICAgZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMSkpCmBgYApgYGB7ciBpdGFseS1kYW1zLWZhY2V0LCBldmFsID0gRkFMU0V9CmBgYAoKU21hbGwgbXVsdGlwbGVzIGNhbiBhbHNvIGJlIGFycmFuZ2VkIGluIHdheXMgdGhhdCBjb252ZXkgc3BhdGlhbCBpbmZvcm1hdGlvbjoKCmBgYHtyIHVuZW1wLWdlb2ZhY2V0LCBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDksIGZpZy5oZWlnaHQgPSA5fQpsaWJyYXJ5KGdlb2ZhY2V0KQpnZ3Bsb3Qoc3RhdGVfdW5lbXAsIGFlcyh5ZWFyLCByYXRlKSkgKwogICAgZ2VvbV9saW5lKCkgKwogICAgZmFjZXRfZ2VvKH4gc3RhdGUsCiAgICAgICAgICAgICAgZ3JpZCA9ICJ1c19zdGF0ZV9ncmlkMiIsCiAgICAgICAgICAgICAgbGFiZWwgPSAibmFtZSIpICsKICAgIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBmdW5jdGlvbih4KSBwYXN0ZTAoIiciLCBzdWJzdHIoeCwgMywgNCkpKSArCiAgICBsYWJzKHRpdGxlID0gIlNlYXNvbmFsbHkgQWRqdXN0ZWQgVVMgVW5lbXBsb3ltZW50IFJhdGUgMjAwMC0yMDE2IiwKICAgICAgICAgY2FwdGlvbiA9ICJEYXRhIFNvdXJjZTogYmxzLmdvdiIsCiAgICAgICAgIHggPSAiWWVhciIsCiAgICAgICAgIHkgPSAiVW5lbXBsb3ltZW50IFJhdGUgKCUpIikgKwogICAgdGhlbWUoc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA2KSwKICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA2KSwKICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKQpgYGAKYGBge3IgdW5lbXAtZ2VvZmFjZXQsIGV2YWwgPSBGQUxTRX0KYGBgCgoKIyMgVGhlIEZhbGwgYW5kIFJpc2Ugb2YgSW5jb21lIEluZXF1YWxpdHkKCkEgWzIwMTUKcG9zdF0oaHR0cHM6Ly93d3cubnByLm9yZy9zZWN0aW9ucy9tb25leS8yMDE1LzAyLzExLzM4NDk4ODEyOC90aGUtZmFsbC1hbmQtcmlzZS1vZi11LXMtaW5lcXVhbGl0eS1pbi0yLWdyYXBocykKb24gdGhlIE5QUiBQbGFuZXQgTW9uZXkgc2l0ZSB1c2VzIHR3byBncmFwaHMgdG8gc2hvdyB0cmFuc2l0aW9ucyBpbgppbmNvbWUgaW5lcXVhbGl0eS4KClRoZSBwYWdlIGluY2x1ZGVzIGEgbGluayB0byB0aGUKPCEtLSB0aGlzIHRpbWVzIG91dCBpbiB0aGUgbGluayBjaGVjayAtLT4KW2RhdGEgc291cmNlXShodHRwOi8vdG9waW5jb21lcy5wYXJpc3NjaG9vbG9mZWNvbm9taWNzLmV1LykuCgpUaGlzIGhhcyBiZWVuIHVuYXZhaWxhYmxlIGZvciBzb21lIHRpbWUuCgpJIHRyaWVkIHRvIG9idGFpbiB0aGUgc2FtZSBkYXRhIGJ1dCBtYXkgbm90IGhhdmUgZG9uZSBpdCBxdWl0ZSByaWdodC4KClRoZSBjbG9zZXN0IHNvdXJjZSBJIGNvdWxkIGZpbmQgaXMgPGh0dHBzOi8vd2lkLndvcmxkL2RhdGEvPi4KClRoZSBkYXRhIGlzIGF2YWlsYWJsZSBsb2NhbGx5OgoKYGBge3J9CmluY29tZSA8LSByZWFkLnRhYmxlKCJodHRwczovL3d3dy5zdGF0LnVpb3dhLmVkdS9+bHVrZS9kYXRhL25wcmluY29tZS5kYXQiKQpgYGAKClBsb3RzIG92ZXIgdGltZToKCmBgYHtyIGluY29tZS1zaW5nbGUsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gNy41fQpzZWxlY3QoaW5jb21lLCAteWVhcikgJT4lCiAgICB0cyhzdGFydCA9IDE5MTMpICU+JQogICAgYXV0b3Bsb3QoKQpgYGAKCmBgYHtyIGluY29tZS1zaW5nbGUsIGV2YWwgPSBGQUxTRX0KYGBgCgpUaGUgcmFuZ2VzIGFyZSB0b28gZGlmZmVyZW50IGZvciB0aGlzIHRvIGJlIGVmZmVjdGl2ZS4KCk9uZSBhbHRlcm5hdGl2ZTogc3RhbmRhcmRpemUgdGhlIHNlcmllcyB0byBvbmUsIG9yIDEwMCUsIGF0IHNvbWUgcG9pbnQuCgpIZXJlIHRoZSBpbml0aWFsIHZhbHVlIGluIDE5MTMgaXMgdXNlZDoKCmBgYHtyIGluY29tZS1zaW5nbGUtcmVsLCBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDcuNX0Kc2VsZWN0KGluY29tZSwgLXllYXIpICU+JQogICAgdHJhbnNtdXRlKFUwMXJlbCA9IFUwMSAvIFUwMVsxXSwKICAgICAgICAgICAgICBMOTByZWwgPSBMOTAgLyBMOTBbMV0pICU+JQogICAgdHMoc3RhcnQgPSAxOTEzKSAlPiUKICAgIGF1dG9wbG90KCkKYGBgCmBgYHtyIGluY29tZS1zaW5nbGUtcmVsLCBldmFsID0gRkFMU0V9CmBgYAoKRmFjZXRlZCBwbG90cywgd2l0aCBmcmVlICR5JCBheGVzLCBhcmUgYW5vdGhlciBvcHRpb246CgpgYGB7ciBpbmNvbWUtZmFjZXQsIGVjaG8gPSBGQUxTRX0Kc2VsZWN0KGluY29tZSwgLXllYXIpICU+JQogICAgdHMoc3RhcnQgPSAxOTEzKSAlPiUKICAgIGF1dG9wbG90KGZhY2V0cyA9IFRSVUUpCmBgYApgYGB7ciBpbmNvbWUtZmFjZXQsIGV2YWwgPSBGQUxTRX0KYGBgCgpXaXRoIHR3byBzZXJpZXMgYW5vdGhlciBvcHRpb24gaXMgYSBfY29ubmVjdGVkIHNjYXR0ZXIgcGxvdF8uCgpgYGB7ciBpbmNvbWUtY29ubmVjdGVkLTEsIGVjaG8gPSBGQUxTRX0KbGlicmFyeShnZ3JlcGVsKQpnZ3Bsb3QoaW5jb21lLCBhZXMoVTAxLCBMOTApKSArCiAgICBnZW9tX3BhdGgobGluZXdpZHRoID0gMikgKwogICAgbGFicyh4ID0gIkF2ZXJhZ2UgaW5jb21lIGZvciB0aGUgdG9wIDElIiwKICAgICAgICAgeSA9ICJBdmVyYWdlIGluY29tZSBmb3IgdGhlIGJvdHRvbSA5MCUiKQpgYGAKYGBge3IgaW5jb21lLWNvbm5lY3RlZC0xLCBldmFsID0gRkFMU0V9CmBgYAoKVGhpcyBzaW1wbGUgZm9ybSBkb2VzIG5vdCBzaG93IHRoZSBkaXJlY3Rpb24gb3Igc2NhbGUgb2YgdGltZS4KCkNvbG9yIG9yIGFubm90YXRpb24gY2FuIGJlIHVzZWQgdG8gc2hvdyBkaXJlY3Rpb24gYW5kIHNjYWxlIG9mIHRpbWU6CgpgYGB7ciBpbmNvbWUtY29ubmVjdGVkLTIsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gNy41LCB3YXJuaW5nID0gRkFMU0V9CmxpYnJhcnkoZ2dyZXBlbCkKZ2dwbG90KGluY29tZSwgYWVzKFUwMSwgTDkwLCBjb2xvciA9IHllYXIpKSArCiAgICBnZW9tX3BhdGgoc2l6ZSA9IDIpICsKICAgIGdlb21fdGV4dF9yZXBlbCgKICAgICAgICBhZXMobGFiZWwgPQogICAgICAgICAgICAgICAgaWZlbHNlKHllYXIgJSUgNSA9PSAwIHwKICAgICAgICAgICAgICAgICAgICAgICB5ZWFyID09IG1heCh5ZWFyKSwKICAgICAgICAgICAgICAgICAgICAgICB5ZWFyLAogICAgICAgICAgICAgICAgICAgICAgICIiKSksCiAgICAgICAgY29sb3IgPSAiZ3JleTUwIikgKwogICAgbGFicyh4ID0gIkF2ZXJhZ2UgaW5jb21lIGZvciB0aGUgdG9wIDElIiwKICAgICAgICAgeSA9ICJBdmVyYWdlIGluY29tZSBmb3IgdGhlIGJvdHRvbSA5MCUiKQpgYGAKYGBge3IgaW5jb21lLWNvbm5lY3RlZC0yLCBldmFsID0gRkFMU0V9CmBgYAoKQW5pbWF0ZWQgaXMgYW5vdGhlciBvcHRpb246Cgo8IS0tICoqKiogY291bGQgbG9vayBpbnRvIGdldHRpbmcgY2xvc2VyIHRvIHRoZSBvcmlnaW5hbCBOUFIgdmVyc2lvbiAtLT4KCmBgYHtyIGluY29tZS1nZ2FuaW0sIGVjaG8gPSBGQUxTRSwgY2FjaGUgPSBUUlVFLCBtZXNzYWdlID0gRkFMU0V9CmxpYnJhcnkoZ2dhbmltYXRlKQpwIDwtIGdncGxvdChpbmNvbWUsCiAgICAgICAgICAgIGFlcyh4ID0gVTAxLCB5ID0gTDkwLAogICAgICAgICAgICAgICAgY29sb3IgPSB5ZWFyKSkgKwogICAgZ2VvbV9wYXRoKHNpemUgPSAyLAogICAgICAgICAgICAgIGNvbG9yID0gImxpZ2h0Z3JleSIsCiAgICAgICAgICAgICAgZGF0YSA9IG11dGF0ZShpbmNvbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyID0gTlVMTCkpICsKICAgIGdlb21fcGF0aChzaXplID0gMikgKwogICAgbGFicyh0aXRsZSA9ICJZZWFyOiB7ZnJhbWVfYWxvbmd9IiwKICAgICAgICAgeCA9ICJBdmVyYWdlIGluY29tZSBmb3IgdGhlIHRvcCAxJSIsCiAgICAgICAgIHkgPSAiQXZlcmFnZSBpbmNvbWUgZm9yIHRoZSBib3R0b20gOTAlIikgKwogICAgdHJhbnNpdGlvbl9yZXZlYWwoeWVhcikKYW5pbWF0ZShwLCBoZWlnaHQgPSAzNTAsIHdpZHRoID0gNTAwLAogICAgICAgIGZwcyA9IDUsIGVuZF9wYXVzZSA9IDEwKQpgYGAKYGBge3IgaW5jb21lLWdnYW5pbSwgZXZhbCA9IEZBTFNFfQpgYGAKCgojIyBTdGFjayBMb3NzIERhdGEKCk9wZXJhdGlvbmFsIGRhdGEgb2J0YWluZWQgRnJvbSAyMSBkYXlzIG9mIG9wZXJhdGlvbiBvZiBhIHBsYW50IGZvciB0aGUKb3hpZGF0aW9uIG9mIGFtbW9uaWEgKE5IMykgdG8gbml0cmljIGFjaWQgKEhOTzMpLgoKVGhlIGRlcGVuZGVudCB2YXJpYWJsZSwgYHN0YWNrLmxvc3NgIGlzIDEwIHRpbWVzIHRoZSBwZXJjZW50YWdlIG9mIHRoZQppbmdvaW5nIGFtbW9uaWEgdG8gdGhlIHBsYW50IHRoYXQgZXNjYXBlcyBmcm9tIHRoZSBhYnNvcnB0aW9uIGNvbHVtbgp1bmFic29yYmVkLgoKYGBge3Igc3RhY2tsb3NzLXNwbG9tLCBlY2hvID0gRkFMU0V9CmxpYnJhcnkobGF0dGljZSkKc3Bsb20oc3RhY2tsb3NzKQpgYGAKYGBge3Igc3RhY2tsb3NzLXNwbG9tLCBldmFsID0gRkFMU0V9CmBgYAoKVGhlIGRhdGEgYXJlIGNvbGxlY3RlZCBvdmVyIHRpbWU6CgpgYGB7ciBzdGFja2xvc3Mtc3Bsb20tbGluZSwgZWNobyA9IEZBTFNFLCBmaWcuaGVpZ2h0ID0gN30KbGlicmFyeShkcGx5cikKc2wgPC0gbXV0YXRlKHN0YWNrbG9zcywKICAgICAgICAgICAgIGludGV4ID0gc2VxX2xlbihucm93KHN0YWNrbG9zcykpKQpjcGFsIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygiIzEzMkI0MyIsICIjNTZCMUY3IikpCmNvbHMgPC0gY3BhbChucm93KHN0YWNrbG9zcykgLSAxKQpwZnVuIDwtIGZ1bmN0aW9uKHgsIHksIC4uLikgewogICAgbiA8LSBsZW5ndGgoeCkKICAgIGxzZWdtZW50cyh4Wy1uXSwgeVstbl0sIHhbLTFdLCB5Wy0xXSwKICAgICAgICAgICAgICBjb2wgPSBjb2xzLCBsd2QgPSAzKQp9CnNwbG9tKHN0YWNrbG9zcywgcGFuZWwgPSBwZnVuKQpgYGAKYGBge3Igc3RhY2tsb3NzLXNwbG9tLWxpbmUsIGV2YWwgPSBGQUxTRX0KYGBgCgpSYW5kb21seSByZW9yZGVyaW5nIHRoZSBpbmRpY2VzIGNhbiBoZWxwIGNhbGlicmF0ZSB3aGV0aGVyIHRoZXJlIGlzIGEKdGltZSBlZmZlY3Q6CgpgYGB7ciBzdGFja2xvc3Mtc3Bsb20tbGluZS1ybmQsIGVjaG8gPSBGQUxTRSwgZmlnLmhlaWdodCA9IDd9CnNwbG9tKHNhbXBsZV9uKHN0YWNrbG9zcywgbnJvdyhzdGFja2xvc3MpKSwKICAgICAgcGFuZWwgPSBwZnVuKQpgYGAKYGBge3Igc3RhY2tsb3NzLXNwbG9tLWxpbmUtcm5kLCBldmFsID0gRkFMU0V9CmBgYAoKVGhpcyBpcyBzb21ldGltZXMgY2FsbGVkIF9ncmFwaGljYWwgaW5mZXJlbmNlXy4KCj4gSGFkbGV5IFdpY2toYW0sIERpYW5uZSBDb29rLCBIZWlrZSBIb2ZtYW5uLCBhbmQgQW5kcmVhcyBCdWphLAo+ICJHcmFwaGljYWwgSW5mZXJlbmNlIGZvciBJbmZvdmlzLCIgX0lFRUUgVHJhbnNhY3Rpb25zIG9uIFZpc3VhbGl6YXRpb24KPiBhbmQgQ29tcHV0ZXIgR3JhcGhpY3NfLCBWb2wuIDE2LCBOby4gNiwgTm92ZW1iZXIvRGVjZW1iZXIgMjAxMC4KClBpY2tpbmcgb25lIHBsb3QgYW5kIGVtYmVkZGluZyB0aGUgdHJ1ZSBvcmRlciBhbW9uZyAxOSByYW5kb20gb3JkZXJpbmdzOgoKYGBge3Igc3RhY2stbG9zcy1lbWJlZCwgZWNobyA9IEZBTFNFfQpOIDwtIDIwCmsgPC0gc2FtcGxlKE4sIDEpCgpvcmQgPC0gc2VxX2xlbihucm93KHN0YWNrbG9zcykpCnNzIDwtIGZ1bmN0aW9uKGkpIHsKICAgIGlmIChpID09IGspCiAgICAgICAgc2wgPC0gc3RhY2tsb3NzCiAgICBlbHNlCiAgICAgICAgc2wgPC0gc2FtcGxlX24oc3RhY2tsb3NzLCBucm93KHN0YWNrbG9zcykpCiAgICBtdXRhdGUoc2wsIG9yZGVyID0gb3JkLCBzYW1wbGUgPSBpKQp9CgpkIDwtIGxhcHBseSgxIDogTiwgc3MpCmQgPC0gZG8uY2FsbChyYmluZCwgZCkKCmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KGQsIGFlcyh4ID0gQWNpZC5Db25jLiwgeSA9IHN0YWNrLmxvc3MsIGNvbG9yID0gb3JkZXIpKSArCiAgICBnZW9tX3BhdGgoc2l6ZSA9IDAuOCkgKyBmYWNldF93cmFwKH4gc2FtcGxlKSArCiAgICBndWlkZXMoY29sb3IgPSAibm9uZSIpCmBgYApUcnVlIG9yZGVyOiBwbG90IGByIGtgLgoKVGhlIGNvZGU6CgpgYGB7ciBzdGFjay1sb3NzLWVtYmVkLCBldmFsID0gRkFMU0V9CmBgYAoKCiMjIEx5bngtSGFyZSBEYXRhCgpUcmFwcGluZyBkYXRhIG9uIENhbmFkaWFuIGx5bnggYW5kIHNub3dzaG9lIGhhcmU6Cgo8IS0tCmh0dHA6Ly9wZW9wbGUud2hpdG1hbi5lZHUvfmh1bmRsZWRyL2NvdXJzZXMvTTI1MEYwMy9NMjUwLmh0bWwKaHR0cDovL3Blb3BsZS53aGl0bWFuLmVkdS9+aHVuZGxlZHIvY291cnNlcy9NMjUwRjAzL0x5bnhIYXJlLnR4dAotLT4KCmBgYHtyIGx5bnhoYXJlLWZhY2V0cywgZWNobyA9IEZBTFNFfQpseW54aGFyZSA8LSByZWFkLnRhYmxlKCJodHRwczovL3d3dy5zdGF0LnVpb3dhLmVkdS9+bHVrZS9kYXRhL0x5bnhIYXJlLnR4dCIpCm5hbWVzKGx5bnhoYXJlKSA8LSBjKCJ5ZWFyIiwgImhhcmUiLCAibHlueCIpCmxodHMgPC0gdHMoc2VsZWN0KGx5bnhoYXJlLCAteWVhciksIHN0YXJ0ID0gMTg0NSkKYXV0b3Bsb3QobGh0cywgZmFjZXRzID0gVFJVRSkKYGBgCgpDeWNsZSBwYXR0ZXJucyBhcmUgc2ltaWxhci4KCmBgYHtyIGx5bnhoYXJlLWZhY2V0cywgZXZhbCA9IEZBTFNFfQpgYGAKCkx5bnggY3ljbGVzIG9mdGVuIGxhZyBiZWhpbmQ6CgpgYGB7ciBseW54aGFyZS1zaW5nbGUsIGVjaG8gPSBGQUxTRX0KYXV0b3Bsb3QobGh0cykKYGBgCmBgYHtyIGx5bnhoYXJlLXNpbmdsZSwgZXZhbCA9IEZBTFNFfQpgYGAKCgojIyBUaGluZ3MgdG8gTG9vayBGb3IKCkNoYW5nZXMgaW4gZGF0YSBkZWZpbml0aW9uLgoKKiBBZG1pbmlzdHJhdGl2ZSBkZWZpbml0aW9ucywgc3VjaCBhcyBlbXBsb3ltZW50L3VuZW1wbG95bWVudCBjYW4KICBjaGFuZ2Ugb3ZlciB0aW1lLgoKKiBBZG1pbmlzdHJhdGl2ZSBlbnRpdGllcywgc3VjaCBhcyBjb3VudHJpZXMgYW5kIHN0YXRlcywgY2FuCiAgY2hhbmdlIG92ZXIgdGltZS4KCiogSW5zdHJ1bWVudHMgY2FuIGNoYW5nZSAoc2F0ZWxpdGUgaW1hZ2VyeSByZXBsYWNpbmcgc3VyZmFjZQogIG1lYXN1cmVtZW50cykuCgpPdXRsaWVycyBuZWVkIG5vdCBiZSBnbG9iYWxseSBvdXRseWluZzogZGVwYXJ0dXJlcyBmcm9tIGxvY2FsCnBhdHRlcm5zIG1heSBhbHNvIGJlIGltcG9ydGFudC4KCkxldmVsIHNoaWZ0cyBhbmQgY2hhbmdlcyBpbiB2b2xhdGlsaXR5LgoKU2hvY2tzIGFmZmVjdGluZyBsZXZlbCBvciB2b2xhdGlsaXR5LgoKCiMjIEF1c3RyYWxpYW4gQWlybGluZSBEYXRhCgpbV2Vla2x5IHBhc3NlbmdlciBsb2Fkc10oaHR0cHM6Ly9vdGV4dHMuY29tL2ZwcDIvZ3JhcGhpY3MuaHRtbCksIGluCnRob3VzYW5kcywgb24gQW5zZXR0IEFpcmxpbmVzIGJldHdlZW4gQXVzdHJhbGlh4oCZcyB0d28gbGFyZ2VzdCBjaXRpZXMKKGF2YWlsYWJsZSBhcyBgbWVsc3lkYCBpbiBwYWNrYWdlIGBmcHBgKToKCmBgYHtyIGF1c3RyYWxpYW4tYWlybGluZXMsIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpkYXRhKG1lbHN5ZCwgcGFja2FnZSA9ICJmcHAiKQphdXRvcGxvdChtZWxzeWQsIGZhY2V0cyA9IFRSVUUpCmBgYAoKU29tZSBmZWF0dXJlczoKCiogU3RyaWtlIGluIDE5ODkuCgoqIEluY3JlYXNlIGluIHBhc3NlbmdlciBsb2FkIHN0YXJ0aW5nIGluIHNlY29uZCBoYWxmIG9mIDE5OTEuCgoqIERpcCBpbiBlY29ub215LCByaXNlIGluIGJ1c2luZXNzIGluIDE5OTIgY29ycmVzcG9uZGluZyB0byBhbiBleHBlcmltZW50CiAgd2l0aCBmZXdlciBlY29ub215L21vcmUgYnVzaW5lc3Mgc2VhdHMuCgpgYGB7ciBhdXN0cmFsaWFuLWFpcmxpbmVzLCBldmFsID0gRkFMU0V9CmBgYAoKCiMjIFdlZWtseSBTUCA1MDAgQ2xvc2luZyBSZXR1cm5zCgpXZWVrbHkgY2xvc2luZyByZXR1cm5zIG9mIHRoZSBTUCA1MDAgZnJvbSAyMDAzIHRvIFNlcHRlbWJlciwgMjAxMjoKCmBgYHtyIFNQNTAwLCBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KbGlicmFyeSh4dHMpCmxpYnJhcnkoYXN0c2EpCmF1dG9wbG90KHNwNTAwdykKYGBgCgpTb21lIGZlYXR1cmVzOgoKKiBJbmNyZWFzZWQgdm9sYXRpbGl0eSBhdCB0aGUgc3RhcnQgb2YgdGhlIGZpbmFuY2lhbCBjcmlzaXMuCgoqIFZvbGF0aWxpdHkgc2VlbXMgdG8gYmUgZGVjcmVhc2luZywgYnV0IG5vdCB2ZXJ5IGZhc3QuCgpgYGB7ciBTUDUwMCwgZXZhbCA9IEZBTFNFfQpgYGAKCgojIyBGb3JlY2FzdGluZwoKU3R1ZHlpbmcgdGhlIHBhc3QgbWF5IGhlbHAgcHJlZGljdCB0aGUgZnV0dXJlLgoKRm9yZWNhc3RpbmcgYSB0aW1lIHNlcmllcyB1c3VhbGx5IGludm9sdmVzIGNob29zaW5nIGEgbW9kZWwgYW5kCnJ1bm5pbmcgdGhlIG1vZGVsIGZvcndhcmQuCgpBY2N1cmFjeSBvZiBmb3JlY2FzdHMgZGVjcmVhc2VzIHJhcGlkbHkgdGhlIGZhcnRoZXIgYWhlYWQgdGhlIGZvcmVjYXN0CmlzIG1hZGUuCgpBbiBleGFtcGxlIGZyb20gdGhlIGBmb3JlY2FzdGAgcGFja2FnZTogYFdXV3VzYWdlYCBpcyBhIHRpbWUgc2VyaWVzIG9mCnRoZSBudW1iZXJzIG9mIHVzZXJzIGNvbm5lY3RlZCB0byB0aGUgSW50ZXJuZXQuCgpgYGB7ciBXV1d1c2FnZSwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KGZvcmVjYXN0KQpmaXQgPC0gZXRzKFdXV3VzYWdlKQpmYyA8LSBmb3JlY2FzdChXV1d1c2FnZSwgbW9kZWwgPSBmaXQpCmF1dG9wbG90KGZjKQpgYGAKCmBgYHtyIFdXV3VzYWdlLCBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KYGBgCgoKIyMgTGFiZWxpbmcgVHJlbmQgTGluZXMKCkZvciB0cmVuZCBsaW5lcyB1c2luZyBhIHN0YW5kYXJkIGd1aWRlIGNhbiBiZSBhIGxpdHRsZSBhd2t3YXJkLgoKQXZlcmFnZSBsaWZlIGV4cGVjdGFuY3kgYWdhaW5zdCB5ZWFyIGZvciB0aGUgY29udGluZW50cyBpbiB0aGUKYGdhcG1pbmRlcmAgZGF0YToKCmBgYHtyIHRyZW5kLWxpbmVzLWd1aWRlLCBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gNy41fQpsaWJyYXJ5KGdhcG1pbmRlcikKZCA8LSBncm91cF9ieShnYXBtaW5kZXIsCiAgICAgICAgICAgICAgY29udGluZW50LCB5ZWFyKSAlPiUKICAgIHN1bW1hcml6ZShhdmdMaWZlRXhwID0gbWVhbihsaWZlRXhwKSkgJT4lCiAgICB1bmdyb3VwKCkKCnAgPC0gZ2dwbG90KGQsCiAgICAgICAgICAgIGFlcyh4ID0geWVhciwKICAgICAgICAgICAgICAgIHkgPSBhdmdMaWZlRXhwLAogICAgICAgICAgICAgICAgY29sb3IgPSBjb250aW5lbnQpKSArCiAgICBnZW9tX2xpbmUoc2l6ZSA9IDEpCnAKYGBgCmBgYHtyIHRyZW5kLWxpbmVzLWd1aWRlLCBldmFsID0gRkFMU0V9CmBgYAoKV2l0aCB0aGUgbGVnZW5kIG9uIHRoZSByaWdodCwgYSBzbWFsbCBpbXByb3ZlbWVudCBpcyB0byBvcmRlciB0aGUKbGV2ZWxzIHRvIG1hdGNoIHRoZSBvcmRlciBvZiB0aGUgcmlnaHQgaGFuZCBjdXJ2ZSB2YWx1ZXM6CgpgYGB7ciB0cmVuZC1saW5lcy1ndWlkZS1yZW9yZGVyLCBlY2hvID0gRkFMU0V9CmRfbGFzdCA8LSBmaWx0ZXIoZCwgeWVhciA9PSBtYXgoeWVhcikpICU+JQogICAgbXV0YXRlKGNvbnRpbmVudCA9CiAgICAgICAgICAgICAgIHJlb3JkZXIoY29udGluZW50LCAtYXZnTGlmZUV4cCkpCgpwICUrJQogICAgbXV0YXRlKGQsCiAgICAgICAgICAgY29udGluZW50ID0KICAgICAgICAgICAgICAgZmFjdG9yKGNvbnRpbmVudCwKICAgICAgICAgICAgICAgICAgICAgIGxldmVscyhkX2xhc3QkY29udGluZW50KSkpCmBgYAoKYGBge3IgdHJlbmQtbGluZXMtZ3VpZGUtcmVvcmRlciwgZXZhbCA9IEZBTFNFfQpgYGAKCl9EaXJlY3QgbGFiZWxpbmdfIGlzIG9mdGVuIGEgYmV0dGVyIGNob2ljZS4KCk9uZSBvcHRpb24gaXMgdG8gdXNlIGBnZW9tX3RleHRgIGF0IHRoZSBlbmRzIG9mIHRoZSBsaW5lcy4KClRoaXMgbWF5IG5lZWQgc29tZSBhZGRpdGlvbmFsIGFkanVzdG1lbnRzIHRvIG1hcmdpbnMgYW5kIGNsaXBwaW5nCmJlaGF2aW9yOgoKYGBge3IgdHJlbmQtbGluZXMtZGlyZWN0LCBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDcuNX0KcCA8LSBwICsgZ3VpZGVzKGNvbG9yID0gIm5vbmUiKQoKZF9sYXN0IDwtIGZpbHRlcihkLCB5ZWFyID09IG1heCh5ZWFyKSkKCnAgKyBnZW9tX3RleHQoYWVzKGxhYmVsID0gY29udGluZW50KSwKICAgICAgICAgICAgICBkYXRhID0gZF9sYXN0LAogICAgICAgICAgICAgIHNpemUgPSA1LAogICAgICAgICAgICAgIGhqdXN0ID0gLTAuMSkgKwogICAgY29vcmRfY2FydGVzaWFuKGNsaXAgPSAib2ZmIiwKICAgICAgICAgICAgICAgICAgICBleHBhbmQgPSBGQUxTRSkgKwogICAgdGhlbWUocGxvdC5tYXJnaW4gPQogICAgICAgICAgICAgIG1hcmdpbigzMCwgNjAsIDEwLCAxMCkpCmBgYApgYGB7ciB0cmVuZC1saW5lcy1kaXJlY3QsIGV2YWwgPSBGQUxTRX0KYGBgCgpBbm90aGVyIG9wdGlvbiBpcyB0byB1c2UgYSBzZWNvbmQgYXhpczoKPCEtLQpBZGFwdGVkIGZyb20gaHR0cHM6Ly93aWxrZWxhYi5vcmcvU0RTMzc1L3NsaWRlcy9yZWR1bmRhbnQtY29kaW5nLmh0bWwjMTYKLS0+CgpgYGB7ciB0cmVuZC1saW5lcy1zZWMtYXhpcywgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSA3LjV9CnAgKyBzY2FsZV95X2NvbnRpbnVvdXMoCiAgICAgICAgc2VjLmF4aXMgPSBkdXBfYXhpcygKICAgICAgICAgICAgYnJlYWtzID0gZF9sYXN0JGF2Z0xpZmVFeHAsCiAgICAgICAgICAgIGxhYmVscyA9IGRfbGFzdCRjb250aW5lbnQsCiAgICAgICAgICAgIG5hbWUgPSBOVUxMKSkgKwogICAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCkpCmBgYApgYGB7ciB0cmVuZC1saW5lcy1zZWMtYXhpcywgZXZhbCA9IEZBTFNFfQpgYGAKCkl0IGlzIGFsc28gcG9zc2libGUgdG8gcGxhY2UgdGhlIGxhYmVscyBhbG9uZyB0aGUgbGluZXMuCgpUaGUgW2BnZW9tdGV4dHBhdGhgCnBhY2thZ2VdKGh0dHBzOi8vYWxsYW5jYW1lcm9uLmdpdGh1Yi5pby9nZW9tdGV4dHBhdGgvKSBwcm92aWRlcwpzdXBwb3J0IGZvciB0aGlzLgoKYGBge3IgdHJlbmQtbGluZXMtdGV4dC1wYXRoLCBlY2hvID0gRkFMU0V9CmxpYnJhcnkoZ2VvbXRleHRwYXRoKQpnZ3Bsb3QoZCwgYWVzKHggPSB5ZWFyLAogICAgICAgICAgICAgIHkgPSBhdmdMaWZlRXhwLAogICAgICAgICAgICAgIGNvbG9yID0gY29udGluZW50KSkgKwogICAgZ2VvbV90ZXh0bGluZShhZXMobGFiZWwgPSBjb250aW5lbnQpLAogICAgICAgICAgICAgICAgICBsaW5ld2lkdGggPSAxLAogICAgICAgICAgICAgICAgICB2anVzdCA9IC0wLjUsCiAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMC45NSkgKwogICAgZ3VpZGVzKGNvbG9yID0gIm5vbmUiKQpgYGAKCmBgYHtyIHRyZW5kLWxpbmVzLXRleHQtcGF0aCwgZXZhbCA9IEZBTFNFfQpgYGAKCgojIyBCdW1wIENoYXJ0cwoKVG9wIDEwIGNvdW50cmllcyBpbiB0ZXJtcyBvZiBHRFAgcGVyIGNhcGl0YSBpbiAyMDA3OgoKYGBge3IsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQp0b3BfZ2RwIDwtIGZpbHRlcihnYXBtaW5kZXIsCiAgICAgICAgICAgICAgICAgIGNvbnRpbmVudCA9PSAiRXVyb3BlIiwKICAgICAgICAgICAgICAgICAgeWVhciA9PSBtYXgoeWVhcikpICU+JQogICAgc2xpY2VfbWF4KGdkcFBlcmNhcCwgbiA9IDEwKSAlPiUKICAgIHB1bGwoY291bnRyeSkKZCA8LSBmaWx0ZXIoZ2FwbWluZGVyLAogICAgICAgICAgICBjb3VudHJ5ICVpbiUgdG9wX2dkcCkgJT4lCiAgICBncm91cF9ieSh5ZWFyKSAlPiUKICAgIG11dGF0ZSgKICAgICAgICByYW5rID0gbWluX3JhbmsoZGVzYyhnZHBQZXJjYXApKSkgJT4lCiAgICB1bmdyb3VwKCkKZF9sYXN0IDwtIGZpbHRlcihkLCB5ZWFyID09IG1heCh5ZWFyKSkKCmdncGxvdChkLCBhZXMoeWVhciwKICAgICAgICAgICAgICBnZHBQZXJjYXAsCiAgICAgICAgICAgICAgY29sb3IgPSBjb3VudHJ5KSkgKwogICAgZ2VvbV9saW5lKCkKYGBgCgpUaGUgZG9taW5hbnQgZmVhdHVyZSBpcyB0aGUgb3ZlcmFsbCB0cmVuZC4KCk9uZSBvcHRpb246IHNob3cgZGV2aWF0aW9ucyBmcm9tIHRoZSBvdmVyYWxsIHRyZW5kLgoKQW5vdGhlciBvcHRpb246IHNob3cgcmFua3Mgd2l0aGluIHllYXJzLgoKVGhpcyBpcyBjYWxsZWQgYSBfYnVtcCBjaGFydF8uCgpBIHNpbXBsZSBidW1wIGNoYXJ0OgoKYGBge3IsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpnZ3Bsb3QoZCwgYWVzKHllYXIsIHJhbmssIGNvbG9yID0gY291bnRyeSkpICsKICAgIGdlb21fbGluZShzaXplID0gMSkgKwogICAgc2NhbGVfeV9yZXZlcnNlKGJyZWFrcyA9IDE6MTApICsKICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBjb3VudHJ5KSwKICAgICAgICAgICAgICBkYXRhID0gZF9sYXN0LAogICAgICAgICAgICAgIHNpemUgPSA1LAogICAgICAgICAgICAgIGhqdXN0ID0gLTAuMSkgKwogICAgY29vcmRfY2FydGVzaWFuKGNsaXAgPSAib2ZmIiwKICAgICAgICAgICAgICAgICAgICBleHBhbmQgPSBGQUxTRSkgKwogICAgdGhlbWUocGxvdC5tYXJnaW4gPQogICAgICAgICAgICAgIG1hcmdpbigzMCwgOTAsIDEwLCAxMCkpICsKICAgIGd1aWRlcyhjb2xvciA9ICJub25lIikKYGBgCgpUaGUgYGdnYnVtcGAgcGFja2FnZSBwcm92aWRlcyBgZ2VvbV9idW1wYCBmb3IgZHJhd2luZyBzbW9vdGggYnVtcApjaGFydCBsaW5lczoKYGBge3IsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpsaWJyYXJ5KGdnYnVtcCkKZ2dwbG90KGQsIGFlcyh5ZWFyLCByYW5rLCBjb2xvciA9IGNvdW50cnkpKSArCiAgICBnZW9tX2J1bXAoc2l6ZSA9IDEpICsKICAgIHNjYWxlX3lfcmV2ZXJzZShicmVha3MgPSAxOjEwKSArCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gY291bnRyeSksCiAgICAgICAgICAgICAgZGF0YSA9IGRfbGFzdCwKICAgICAgICAgICAgICBzaXplID0gNSwKICAgICAgICAgICAgICBoanVzdCA9IC0wLjEpICsKICAgIGNvb3JkX2NhcnRlc2lhbihjbGlwID0gIm9mZiIsCiAgICAgICAgICAgICAgICAgICAgZXhwYW5kID0gRkFMU0UpICsKICAgIHRoZW1lKHBsb3QubWFyZ2luID0KICAgICAgICAgICAgICBtYXJnaW4oMzAsIDkwLCAxMCwgMTApKSArCiAgICBndWlkZXMoY29sb3IgPSAibm9uZSIpCmBgYAoKCiMjIE90aGVyIEFwcHJvYWNoZXMKCkEgW3RhbGsgZnJvbQoyMDE5XShodHRwczovL3d3dy52aXN1YWxpc2luZ2RhdGEuY29tLzIwMTkvMDQvdGFsay1zbGlkZXMtZGVzaWduLW9mLXRpbWUvKQpkZXNjcmliZXMgYSB3aWRlIHJhbmdlIG9mIG9wdGlvbnMgZm9yIHZpc3VhbGl6aW5nIHRlbXBvcmFsIGRhdGEuCgoKIyMgUmVhZGluZwoKQ2hhcHRlcnMgW19WaXN1YWxpemluZyB0aW1lIHNlcmllcyBhbmQgb3RoZXIgZnVuY3Rpb25zIG9mIGFuCiAgaW5kZXBlbmRlbnQKICB2YXJpYWJsZV9dKGh0dHBzOi8vY2xhdXN3aWxrZS5jb20vZGF0YXZpei90aW1lLXNlcmllcy5odG1sKSBhbmQKICBbX1Zpc3VhbGl6aW5nCiAgdHJlbmRzX10oaHR0cHM6Ly9jbGF1c3dpbGtlLmNvbS9kYXRhdml6L3Zpc3VhbGl6aW5nLXRyZW5kcy5odG1sKSBpbgogIFtfRnVuZGFtZW50YWxzIG9mIERhdGEKICBWaXN1YWxpemF0aW9uX10oaHR0cHM6Ly9jbGF1c3dpbGtlLmNvbS9kYXRhdml6LykuCgoKIyMgRXhlcmNpc2VzCgoxLiBDcmVhdGluZyBhIHRpbWUgc2VyaWVzIG9yIG11bHRpcGxlIHRpbWUgc2VyaWVzIG9iamVjdCBhbmQgdXNpbmcKICAgIGF1dG9wbG90IGNhbiBiZSBhbiBlZmZpY2llbnQgd2F5IHRvIG9idGFpbiBsaW5lIHBsb3RzIG9mIHRpbWUKICAgIHNlcmllcyBkYXRhLiBUaGUgZm9sbG93aW5nIGNvZGUgdXNlcyB0aGUgYHdlYXRoZXJgIHRhYmxlIGluIHRoZQogICAgYG55Y2ZsaWdodHMxM2AgcGFja2FnZSB0byBjcmVhdGUgYSBkYXRhIGZyYW1lIGNvbnRhaW5pbmcgbWluaW11bQogICAgYW5kIG1heGltdW0gdGVtcGVyYXR1cmVzIGZvciBlYWNoIG1vbnRoIGluIDIwMTM6CgogICAgYGBge3J9CiAgICBsaWJyYXJ5KHRpZHl2ZXJzZSkKICAgIGxpYnJhcnkobnljZmxpZ2h0czEzKQogICAgbGlicmFyeShsdWJyaWRhdGUpCiAgICBsaWJyYXJ5KGZvcmVjYXN0KQoKICAgIHRlbXBzIDwtIGdyb3VwX2J5KHdlYXRoZXIsIHllYXIsIG1vbnRoKSAlPiUKICAgICAgICBzdW1tYXJpemUobWF4X3RlbXAgPSBtYXgodGVtcCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgbWluX3RlbXAgPSBtaW4odGVtcCwgbmEucm0gPSBUUlVFKSkgJT4lCiAgICAgICAgdW5ncm91cCgpCiAgICBgYGAKCiAgICBXaGljaCBvZiB0aGUgZm9sbG93aW5nIGV4cHJlc3Npb25zIHByb2R1Y2VzIGEgbGluZSBwbG90IHNob3dpbmcKICAgIHRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIHRlbXBlcmF0dXJlIHNlcmllcyAoYW5kIG5vIG90aGVyIHNlcmllcyk/CgogICAgYS4gYGF1dG9wbG90KHRlbXBzKWAKICAgIGIuIGBhdXRvcGxvdCh0ZW1wcyRtYXhfdGVtcCwgdGVtcHMkbWluX3RlbXApYAogICAgYy4gYHRzKHRlbXBzKSAlPiUgYXV0b3Bsb3QoKWAKICAgIGQuIGBzZWxlY3QodGVtcHMsIG1heF90ZW1wLCBtaW5fdGVtcCkgJT4lIHRzKCkgJT4lIGF1dG9wbG90KClgCgoKPCEtLQpMeW54LUhhcmUgRGF0YSBWYXJpYW50cwpodHRwczovL2dpc3QuZ2l0aHViLmNvbS9tYWdlcy8xZjBmMGQ1YmJlNTBhZjgxY2MxOQpodHRwczovL2dpdGh1Yi5jb20vc3Rhbi1kZXYvZXhhbXBsZS1tb2RlbHMvdHJlZS9tYXN0ZXIva25pdHIvbG90a2Etdm9sdGVycmEKCkhhcmVzTHlueE9ic2VydmF0aW9ucyA8LSAiWWVhciAgIEhhcmVzLnguMTAwMCAgICBMeW54LnguMTAwMAoxOTAwICAgIDMwICAgICAgNAoxOTAxICAgIDQ3LjIgICAgNi4xCjE5MDIgICAgNzAuMiAgICA5LjgKMTkwMyAgICA3Ny40ICAgIDM1LjIKMTkwNCAgICAzNi4zICAgIDU5LjQKMTkwNSAgICAyMC42ICAgIDQxLjcKMTkwNiAgICAxOC4xICAgIDE5CjE5MDcgICAgMjEuNCAgICAxMwoxOTA4ICAgIDIyICAgICAgOC4zCjE5MDkgICAgMjUuNCAgICA5LjEKMTkxMCAgICAyNy4xICAgIDcuNAoxOTExICAgIDQwLjMgICAgOAoxOTEyICAgIDU3ICAgICAgMTIuMwoxOTEzICAgIDc2LjYgICAgMTkuNQoxOTE0ICAgIDUyLjMgICAgNDUuNwoxOTE1ICAgIDE5LjUgICAgNTEuMQoxOTE2ICAgIDExLjIgICAgMjkuNwoxOTE3ICAgIDcuNiAgICAgMTUuOAoxOTE4ICAgIDE0LjYgICAgOS43CjE5MTkgICAgMTYuMiAgICAxMC4xCjE5MjAgICAgMjQuNyAgICA4LjYiCgpobC5vYnMgPC0gcmVhZC50YWJsZSh0ZXh0PUhhcmVzTHlueE9ic2VydmF0aW9ucywgaGVhZGVyPVRSVUUpCm5hbWVzKGhsLm9icykgPC0gYygiWWVhciIsICJIYXJlcyIsICJMeW54IikgIyMgUG9wdWxhdGlvbiBpbiAwMDAncwpsaWJyYXJ5KGxhdHRpY2UpCnh5cGxvdChIYXJlcytMeW54fiBZZWFyLCBkYXRhID0gaGwub2JzLHR5cGUgPSAibCIsIGF1dG8ua2V5ID0gVFJVRSkKCmh0dHBzOi8vb3BlbmRhdGEuc3RhY2tleGNoYW5nZS5jb20vcXVlc3Rpb25zLzY3Njcvd2hlcmUtY2FuLWktZmluZC1iaW9sb2dpY2FsLXRpbWUtc2VyaWVzLWRhdGE/dXRtX21lZGl1bT1vcmdhbmljJnV0bV9zb3VyY2U9Z29vZ2xlX3JpY2hfcWEmdXRtX2NhbXBhaWduPWdvb2dsZV9yaWNoX3FhCi0tPgo=