Multiple Time Series

Some questions for multiple time series:

Some options for plotting multiple series:

Another option for two time series:

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.

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:

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:

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.

Reading

Chapters Visualizing time series and other functions of an independent variable and Visualizing trends in Fundamentals of Data Visualization.

Exercises

  1. 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)?

    1. autoplot(temps)
    2. autoplot(temps$max_temp, temps$min_temp)
    3. ts(temps) %>% autoplot()
    4. 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=