Today, we’re going to focus on visualizing data over time. This is an area where we can’t use linear regression – if our data is time-dependent, the observations are not independent from one another.

While regression can be adapted for forecasting in time-series (which is called ARIMA: Auto-Regressive Integrated Moving Average), we’ll focus on graphical methods for understanding time series, rather than predicting new observations.

Part 1: Time Series

A time series graph is usually a connected scatterplot. The first part of the lab will walk you through how to make some of the time series plots in Chapter 9 of The Truthful Art. We’ll return to the covid data from the Delphi group for this lab.

covid = read_csv("data/covid_data.csv")
covid
## # A tibble: 65,688 x 6
##    signal       geo_value time_value value stderr sample_size
##    <chr>        <chr>     <date>     <dbl>  <dbl>       <dbl>
##  1 smoothed_cli ak        2020-10-01 1.59  0.429         760 
##  2 smoothed_cli al        2020-10-01 0.983 0.141        3742.
##  3 smoothed_cli ar        2020-10-01 1.06  0.174        2587.
##  4 smoothed_cli az        2020-10-01 0.597 0.0905       5682.
##  5 smoothed_cli ca        2020-10-01 0.450 0.0399      21930.
##  6 smoothed_cli co        2020-10-01 0.561 0.0917       5137.
##  7 smoothed_cli ct        2020-10-01 0.444 0.0978       3866 
##  8 smoothed_cli dc        2020-10-01 0.268 0.224         533 
##  9 smoothed_cli de        2020-10-01 0.203 0.134        1129.
## 10 smoothed_cli fl        2020-10-01 0.516 0.0472      17767.
## # … with 65,678 more rows

This time, we want to examine this data over time. Since there’s a huge number of signals (see options with unique(covid$signal)), and 50 states + DC, we’ll restrict this analysis to Pennsylvania, and focus on the signal smoothed_tested_14d. This signal contains a smoothed estimate of the percentage of people who have been tested for COVID in the last 14 days.

pa_tested = covid %>%
  filter(signal == "smoothed_tested_14d" & geo_value == "pa")
pa_tested
## # A tibble: 92 x 6
##    signal              geo_value time_value value stderr sample_size
##    <chr>               <chr>     <date>     <dbl>  <dbl>       <dbl>
##  1 smoothed_tested_14d pa        2020-10-01  6.17  0.224      11505.
##  2 smoothed_tested_14d pa        2020-10-02  6.17  0.223      11622.
##  3 smoothed_tested_14d pa        2020-10-03  6.12  0.223      11558.
##  4 smoothed_tested_14d pa        2020-10-04  6.09  0.222      11564.
##  5 smoothed_tested_14d pa        2020-10-05  6.11  0.223      11588.
##  6 smoothed_tested_14d pa        2020-10-06  6.04  0.219      11817.
##  7 smoothed_tested_14d pa        2020-10-07  6.14  0.217      12279.
##  8 smoothed_tested_14d pa        2020-10-08  6.09  0.215      12423.
##  9 smoothed_tested_14d pa        2020-10-09  6.11  0.215      12443.
## 10 smoothed_tested_14d pa        2020-10-10  6.16  0.216      12379.
## # … with 82 more rows

Let’s make a basic time series line graph:

ggplot(pa_tested, aes(x = time_value, y = value, ymin = value - 2*stderr, ymax = value + 2*stderr)) +
  geom_line() +
  geom_ribbon(alpha = .5) +
  lab08_theme +
  labs(
    x = "",
    y = "",
    title = "% of Pennsylvanians who were tested for COVID over previous 14 days",
    subtitle = "Smoothed Estimate",
    caption = "Source: Delphi Symptom Survey"
  )

I’ve included a geom_ribbon to emphasize that this is an estimate and so there is some uncertainty associated with it. In order to calculate the ymin and ymax values for the geom_ribbon, I’ve used the formula \(\hat{y} \pm 2 \times SE(\hat{y})\) for a 95% confidence interval.

Now, note that R recognizes that the time_value variable is a <date>. Date variable type comes in handy when making axes. In the graph below, I’ve changed the way that the date is formatted on the x-axis to %Y %b %d, which corresponds to “Year”, “abbreviated month”, “day”. Pull up the help page for scale_x_date, or the table here to see other options.

ggplot(pa_tested, aes(x = time_value, y = value, ymin = value - 2*stderr, ymax = value + 2*stderr)) +
  geom_line() +
  geom_ribbon(alpha = .5) + 
  scale_x_date(date_labels = "%Y %b %d") +
  lab08_theme +
  labs(
    x = "",
    y = "",
    title = "% of Pennsylvanians who were tested for COVID over previous 14 days",
    subtitle = "Smoothed Estimate",
    caption = "Source: Delphi Symptom Survey"
  )

Now, let’s adjust the y-axis of the graph to show the deviation from the average rather than the raw percentage. This does not add much to this graph, and is mostly useful for looking at trends over many years where we expect a lot of seasonality. It’s hard to find seasonality in a 3 month period, but let’s do it anyways! Note that the graph looks exactly the same, except now the y-axis is centered at zero.

pa_tested %>%
  mutate(
    value_dev = value - mean(value)
  ) %>%
  ggplot(., aes(x = time_value, y = value_dev, ymin = value_dev - 2*stderr, ymax = value_dev + 2*stderr)) +
  geom_line() +
  geom_ribbon(alpha = .5) + 
  labs(
    x = "",
    y = "",
    title = "% of Pennsylvanians who were tested for COVID over previous 14 days",
    subtitle = "Deviation of Smoothed Estimate from average over Oct 1 - Dec 31",
    caption = "Source: Delphi Symptom Survey"
  ) + 
  lab08_theme

Even though we shouldn’t fit a linear regression line, we can still add the loess smoother to the graph. All this does is compute a moving average, so it still works pretty well here:

pa_tested %>%
  mutate(
    value_dev = value - mean(value)
  ) %>%
  ggplot(., aes(x = time_value, y = value_dev, ymin = value_dev - 2*stderr, ymax = value_dev + 2*stderr)) +
  geom_line() +
  geom_ribbon(alpha = .5) +
  geom_smooth(method = "loess", se = FALSE) +
  labs(
    x = "",
    y = "",
    title = "% of Pennsylvanians who were tested for COVID over previous 14 days",
    subtitle = "Deviation from average; includes LOESS trend",
    caption = "Source: Delphi Symptom Survey"
  ) +
  lab08_theme 

To annotate this graph, we can use the as.Date() function to pass x values to it:

pa_tested %>%
  mutate(
    value_dev = value - mean(value)
  ) %>%
  ggplot(., aes(x = time_value, y = value_dev, ymin = value_dev - 2*stderr, ymax = value_dev + 2*stderr)) +
  geom_line() +
  geom_ribbon(alpha = .5) +
  annotate("rect", xmin = as.Date("2020-11-26") - 14, xmax = as.Date("2020-11-26"), ymin = -4, ymax = 4, fill = "darkorange2", alpha = .3) +
  annotate("text", x = as.Date("2020-11-26") - 7, y = -3, size = 2.5, label = "2 Week Period\n before Thanksgiving", col = "darkorange4") +
  geom_smooth(method = "loess", se = FALSE) +
  labs(
    title = "% Of Pennsylvanians who were tested for COVID over previous 14 days",
    subtitle = "Deviation from average; includes LOESS trend",
    x = "",
    y = "",
    caption = "Source: Delphi Symptom Survey"
  ) +
  lab08_theme

Finally, we might be interested in calculating the loess trend beforehand, and plotting the deviations from the moving average. In the graph above, this is equivalent to plotting the difference between the black line and the blue line.

We first have to calculate the loess model, and then we can augment it the same way we did with lm and glm. Note that we have to use as.numeric(time_value) in order to get the model to work. I’m also cheating here a little bit and adding a new column to the augmented dataset that contains the original, non-numeric time_value variable so that we can actually plot the results on the same graph.

loess_model = loess(value ~ as.numeric(time_value), data = pa_tested)
loess_aug = augment(loess_model) %>%
  mutate(time_value = pa_tested$time_value)

To plot the difference between the fitted and observed values, we can just make a ggplot using the results from augment:

ggplot(loess_aug) +
  geom_line(aes(x = time_value, y = .fitted - value)) +
  labs(
    title = "% Of Pennsylvanians who were tested for COVID over previous 14 days",
    subtitle = "Deviation from LOESS trend",
    x = "",
    y = "",
    caption = "Source: Delphi Symptom Survey"
  ) +
  lab08_theme

(One thing to be careful of with this data is that we already have a smoothed estimate – we’re basically “double smoothing” when we use LOESS here.)

Decomposing Time Series

Chapter 9 of The Truthful Art spent some time discussing seasonality and decomposing time series into the seasonal, trend, and random components. This is super easy in R! But it doesn’t work for this data (since we only have 3 months of data). Below is some code in “base R” that does not work, but shows the sequence of commands that would make such a plot for you.

pa_tested_ts = ts(pa_tested$value)
pa_tested_decomp = decompose(pa_tested_ts)
plot(pa_testeed_decomp)

You’ll notice this isn’t ggplot2! I don’t work with time series very often, but from what I can tell the ggseas package is the best bet for doing a similar thing in ggplot. Here’s a blog post with a quick example if this is something you’re interested in for any of your projects.

Part 2: Alluvial Diagrams

In order to make this graph, you’ll need to install the ggalluvial package from CRAN. You can do so either by running install.packages('ggalluvial') in the console, or by clicking “Tools –> Install Packages…” in the toolbar and then typing ggalluvial in the search bar. The data we’re using here is built into the package.

data(vaccinations)

First, we’ll do some cleaning so that our data displays nicely:

vaccinations = vaccinations %>%
  mutate(
    response = factor(response, rev(levels(response))),
    survey = case_when(survey == "ms153_NSA" ~ "First Survey",
                      survey == "ms432_NSA" ~ "Second Survey", 
                      TRUE ~ "Third Survey")) 
head(vaccinations)
##         survey freq subject response start_date   end_date
## 1 First Survey   48       1   Always 2010-09-22 2010-10-25
## 2 First Survey    9       2   Always 2010-09-22 2010-10-25
## 3 First Survey   66       3   Always 2010-09-22 2010-10-25
## 4 First Survey    1       4   Always 2010-09-22 2010-10-25
## 5 First Survey   11       5   Always 2010-09-22 2010-10-25
## 6 First Survey    1       6   Always 2010-09-22 2010-10-25

Examine the vaccinations data before the cleaning step, and see if you can figure out what these steps did. The case_when function is especially useful for dealing with factors/text (it’s essentially a nested if-else statement).

The syntax for ggalluvial is different than a lot of other geoms we’ve seen, and frankly it’s still confusing to me. I do think these plots are beautiful and useful, so I wanted to include them. If you want to learn more about how to make them, I recommend the package vignette.

Below is the code for a very basic alluvial diagram. Try commenting out (one line at a time) the geom_flow(), geom_stratum(alpha = .5), and geom_text(stat = "stratum", size = 3) to get a feel for what each of them does.

ggplot(vaccinations,
       aes(x = survey, 
           stratum = response, 
           alluvium = subject,
           y = freq,
           fill = response, 
           label = response)) +
  geom_flow() +
  geom_stratum(alpha = .5) +
  geom_text(stat = "stratum", size = 3) +
  theme(legend.position = "none") +
  ggtitle("vaccination survey responses at three points in time")

This graph is often used to display relationships between categorical data, but since it “feels” like the data is flowing from left to right, I find it especially useful for time-related data. It would of course be too busy for a usual time series, but it’s a good option if you have a few “snapshots” of a variable at different periods of time and want to visualize how the distribution changed.

To clean this up a bit, try some of the following to get as close to the below graph as you can. (I used the Optima font and scale_color_viridis(end = .75 ) color scale, but you are welcome to choose others!)

  • geom_flow(width = 1/6)
  • scale_x_discrete(expand = c(.1, .1))
  • geom_stratum(col = "black", fill = "black", width = 1/6, alpha = .8)
  • geom_text(stat = "stratum", size = 3, col = "white", angle = 45)

Part 3: Open-Ended

Using a different signal from the covid data (or more!), and/or restricting to any states of your choosing, make any of the time-related plots above, or something related to them. It may be helpful to return to the description of Delphi’s signal variables when choosing a signal and creating your titles, axes labels, and legend.

You should make sure to incorporate the following custom elements in your graph:

  • X-axis labels using scale_x_time if making a time series
  • Color palette
  • Theme
  • Informative title/subtitle/axes labels/legends, etc. (If you find you do not need one or more of these elements to fully explain what is going on in your graph, you can leave them out if you prefer!)
LS0tCnRpdGxlOiAiTGFiMDg6IFRpbWUiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDogCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY3NzOiBzdGF0NDEtbGFiLXRoZW1lLmNzcwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh0aWR5bW9kZWxzKQpsaWJyYXJ5KGdnYWxsdXZpYWwpCmtuaXRyOjpvcHRzX2NodW5rJHNldChtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSkKbGFiMDhfdGhlbWUgPSB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIsIHNpemUgPSAxMikpIApgYGAKClRvZGF5LCB3ZSdyZSBnb2luZyB0byBmb2N1cyBvbiB2aXN1YWxpemluZyBkYXRhIG92ZXIgKip0aW1lKiouIFRoaXMgaXMgYW4gYXJlYSB3aGVyZSB3ZSBjYW4ndCB1c2UgbGluZWFyIHJlZ3Jlc3Npb24gLS0gaWYgb3VyIGRhdGEgaXMgdGltZS1kZXBlbmRlbnQsIHRoZSBvYnNlcnZhdGlvbnMgYXJlIG5vdCBpbmRlcGVuZGVudCBmcm9tIG9uZSBhbm90aGVyLiAKCldoaWxlIHJlZ3Jlc3Npb24gY2FuIGJlIGFkYXB0ZWQgZm9yIGZvcmVjYXN0aW5nIGluIHRpbWUtc2VyaWVzICh3aGljaCBpcyBjYWxsZWQgQVJJTUE6IEF1dG8tUmVncmVzc2l2ZSBJbnRlZ3JhdGVkIE1vdmluZyBBdmVyYWdlKSwgd2UnbGwgZm9jdXMgb24gZ3JhcGhpY2FsIG1ldGhvZHMgZm9yICoqdW5kZXJzdGFuZGluZyoqIHRpbWUgc2VyaWVzLCByYXRoZXIgdGhhbiAqKnByZWRpY3RpbmcqKiBuZXcgb2JzZXJ2YXRpb25zLiAKIAojIFBhcnQgMTogVGltZSBTZXJpZXMKCkEgKip0aW1lIHNlcmllcyoqIGdyYXBoIGlzIHVzdWFsbHkgYSBjb25uZWN0ZWQgc2NhdHRlcnBsb3QuIFRoZSBmaXJzdCBwYXJ0IG9mIHRoZSBsYWIgd2lsbCB3YWxrIHlvdSB0aHJvdWdoIGhvdyB0byBtYWtlIHNvbWUgb2YgdGhlIHRpbWUgc2VyaWVzIHBsb3RzIGluIENoYXB0ZXIgOSBvZiAqVGhlIFRydXRoZnVsIEFydCouIFdlJ2xsIHJldHVybiB0byB0aGUgYGNvdmlkYCBkYXRhIGZyb20gdGhlIERlbHBoaSBncm91cCBmb3IgdGhpcyBsYWIuIAoKYGBge3J9CmNvdmlkID0gcmVhZF9jc3YoImRhdGEvY292aWRfZGF0YS5jc3YiKQpjb3ZpZApgYGAKVGhpcyB0aW1lLCB3ZSB3YW50IHRvIGV4YW1pbmUgdGhpcyBkYXRhICoqb3ZlciB0aW1lKiouIFNpbmNlIHRoZXJlJ3MgYSBodWdlIG51bWJlciBvZiBgc2lnbmFsc2AgKHNlZSBvcHRpb25zIHdpdGggYHVuaXF1ZShjb3ZpZCRzaWduYWwpYCksIGFuZCA1MCBzdGF0ZXMgKyBEQywgd2UnbGwgcmVzdHJpY3QgdGhpcyBhbmFseXNpcyB0byBQZW5uc3lsdmFuaWEsIGFuZCBmb2N1cyBvbiB0aGUgc2lnbmFsIGBzbW9vdGhlZF90ZXN0ZWRfMTRkYC4gVGhpcyBzaWduYWwgY29udGFpbnMgYSBzbW9vdGhlZCBlc3RpbWF0ZSBvZiB0aGUgcGVyY2VudGFnZSBvZiBwZW9wbGUgd2hvIGhhdmUgYmVlbiB0ZXN0ZWQgZm9yIENPVklEIGluIHRoZSBsYXN0IDE0IGRheXMuCgpgYGB7cn0KcGFfdGVzdGVkID0gY292aWQgJT4lCiAgZmlsdGVyKHNpZ25hbCA9PSAic21vb3RoZWRfdGVzdGVkXzE0ZCIgJiBnZW9fdmFsdWUgPT0gInBhIikKcGFfdGVzdGVkCmBgYAoKTGV0J3MgbWFrZSBhIGJhc2ljIHRpbWUgc2VyaWVzIGxpbmUgZ3JhcGg6IAoKYGBge3J9CmdncGxvdChwYV90ZXN0ZWQsIGFlcyh4ID0gdGltZV92YWx1ZSwgeSA9IHZhbHVlLCB5bWluID0gdmFsdWUgLSAyKnN0ZGVyciwgeW1heCA9IHZhbHVlICsgMipzdGRlcnIpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcmliYm9uKGFscGhhID0gLjUpICsKICBsYWIwOF90aGVtZSArCiAgbGFicygKICAgIHggPSAiIiwKICAgIHkgPSAiIiwKICAgIHRpdGxlID0gIiUgb2YgUGVubnN5bHZhbmlhbnMgd2hvIHdlcmUgdGVzdGVkIGZvciBDT1ZJRCBvdmVyIHByZXZpb3VzIDE0IGRheXMiLAogICAgc3VidGl0bGUgPSAiU21vb3RoZWQgRXN0aW1hdGUiLAogICAgY2FwdGlvbiA9ICJTb3VyY2U6IERlbHBoaSBTeW1wdG9tIFN1cnZleSIKICApCmBgYAoKSSd2ZSBpbmNsdWRlZCBhIGBnZW9tX3JpYmJvbmAgdG8gZW1waGFzaXplIHRoYXQgdGhpcyBpcyBhbiBlc3RpbWF0ZSBhbmQgc28gdGhlcmUgaXMgc29tZSB1bmNlcnRhaW50eSBhc3NvY2lhdGVkIHdpdGggaXQuIEluIG9yZGVyIHRvIGNhbGN1bGF0ZSB0aGUgYHltaW5gIGFuZCBgeW1heGAgdmFsdWVzIGZvciB0aGUgYGdlb21fcmliYm9uYCwgSSd2ZSB1c2VkIHRoZSBmb3JtdWxhICRcaGF0e3l9IFxwbSAyIFx0aW1lcyBTRShcaGF0e3l9KSQgZm9yIGEgOTVcJSBjb25maWRlbmNlIGludGVydmFsLiAKCk5vdywgbm90ZSB0aGF0IFIgcmVjb2duaXplcyB0aGF0IHRoZSBgdGltZV92YWx1ZWAgdmFyaWFibGUgaXMgYSBgPGRhdGU+YC4gRGF0ZSB2YXJpYWJsZSB0eXBlIGNvbWVzIGluIGhhbmR5IHdoZW4gbWFraW5nIGF4ZXMuIEluIHRoZSBncmFwaCBiZWxvdywgSSd2ZSBjaGFuZ2VkIHRoZSB3YXkgdGhhdCB0aGUgZGF0ZSBpcyBmb3JtYXR0ZWQgb24gdGhlIHgtYXhpcyB0byBgJVkgJWIgJWRgLCB3aGljaCBjb3JyZXNwb25kcyB0byAiWWVhciIsICJhYmJyZXZpYXRlZCBtb250aCIsICJkYXkiLiBQdWxsIHVwIHRoZSBoZWxwIHBhZ2UgZm9yIGBzY2FsZV94X2RhdGVgLCBvciB0aGUgdGFibGUgW2hlcmVdKGh0dHBzOi8vd3d3LnItZ3JhcGgtZ2FsbGVyeS5jb20vMjc5LXBsb3R0aW5nLXRpbWUtc2VyaWVzLXdpdGgtZ2dwbG90Mi5odG1sKSB0byBzZWUgb3RoZXIgb3B0aW9ucy4KCmBgYHtyfQpnZ3Bsb3QocGFfdGVzdGVkLCBhZXMoeCA9IHRpbWVfdmFsdWUsIHkgPSB2YWx1ZSwgeW1pbiA9IHZhbHVlIC0gMipzdGRlcnIsIHltYXggPSB2YWx1ZSArIDIqc3RkZXJyKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3JpYmJvbihhbHBoYSA9IC41KSArIAogIHNjYWxlX3hfZGF0ZShkYXRlX2xhYmVscyA9ICIlWSAlYiAlZCIpICsKICBsYWIwOF90aGVtZSArCiAgbGFicygKICAgIHggPSAiIiwKICAgIHkgPSAiIiwKICAgIHRpdGxlID0gIiUgb2YgUGVubnN5bHZhbmlhbnMgd2hvIHdlcmUgdGVzdGVkIGZvciBDT1ZJRCBvdmVyIHByZXZpb3VzIDE0IGRheXMiLAogICAgc3VidGl0bGUgPSAiU21vb3RoZWQgRXN0aW1hdGUiLAogICAgY2FwdGlvbiA9ICJTb3VyY2U6IERlbHBoaSBTeW1wdG9tIFN1cnZleSIKICApCmBgYAoKTm93LCBsZXQncyBhZGp1c3QgdGhlIHktYXhpcyBvZiB0aGUgZ3JhcGggdG8gc2hvdyB0aGUgZGV2aWF0aW9uIGZyb20gdGhlIGF2ZXJhZ2UgcmF0aGVyIHRoYW4gdGhlIHJhdyBwZXJjZW50YWdlLiBUaGlzIGRvZXMgbm90IGFkZCBtdWNoIHRvIHRoaXMgZ3JhcGgsIGFuZCBpcyBtb3N0bHkgdXNlZnVsIGZvciBsb29raW5nIGF0IHRyZW5kcyBvdmVyIG1hbnkgeWVhcnMgd2hlcmUgd2UgZXhwZWN0IGEgbG90IG9mIHNlYXNvbmFsaXR5LiBJdCdzIGhhcmQgdG8gZmluZCBzZWFzb25hbGl0eSBpbiBhIDMgbW9udGggcGVyaW9kLCBidXQgbGV0J3MgZG8gaXQgYW55d2F5cyEgTm90ZSB0aGF0IHRoZSBncmFwaCBsb29rcyBleGFjdGx5IHRoZSBzYW1lLCBleGNlcHQgbm93IHRoZSB5LWF4aXMgaXMgY2VudGVyZWQgYXQgemVyby4KCmBgYHtyfQpwYV90ZXN0ZWQgJT4lCiAgbXV0YXRlKAogICAgdmFsdWVfZGV2ID0gdmFsdWUgLSBtZWFuKHZhbHVlKQogICkgJT4lCiAgZ2dwbG90KC4sIGFlcyh4ID0gdGltZV92YWx1ZSwgeSA9IHZhbHVlX2RldiwgeW1pbiA9IHZhbHVlX2RldiAtIDIqc3RkZXJyLCB5bWF4ID0gdmFsdWVfZGV2ICsgMipzdGRlcnIpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcmliYm9uKGFscGhhID0gLjUpICsgCiAgbGFicygKICAgIHggPSAiIiwKICAgIHkgPSAiIiwKICAgIHRpdGxlID0gIiUgb2YgUGVubnN5bHZhbmlhbnMgd2hvIHdlcmUgdGVzdGVkIGZvciBDT1ZJRCBvdmVyIHByZXZpb3VzIDE0IGRheXMiLAogICAgc3VidGl0bGUgPSAiRGV2aWF0aW9uIG9mIFNtb290aGVkIEVzdGltYXRlIGZyb20gYXZlcmFnZSBvdmVyIE9jdCAxIC0gRGVjIDMxIiwKICAgIGNhcHRpb24gPSAiU291cmNlOiBEZWxwaGkgU3ltcHRvbSBTdXJ2ZXkiCiAgKSArIAogIGxhYjA4X3RoZW1lCmBgYAoKRXZlbiB0aG91Z2ggd2Ugc2hvdWxkbid0IGZpdCBhIGxpbmVhciByZWdyZXNzaW9uIGxpbmUsIHdlIGNhbiBzdGlsbCBhZGQgdGhlIGBsb2Vzc2Agc21vb3RoZXIgdG8gdGhlIGdyYXBoLiBBbGwgdGhpcyBkb2VzIGlzIGNvbXB1dGUgYSBtb3ZpbmcgYXZlcmFnZSwgc28gaXQgc3RpbGwgd29ya3MgcHJldHR5IHdlbGwgaGVyZTogCgpgYGB7cn0KcGFfdGVzdGVkICU+JQogIG11dGF0ZSgKICAgIHZhbHVlX2RldiA9IHZhbHVlIC0gbWVhbih2YWx1ZSkKICApICU+JQogIGdncGxvdCguLCBhZXMoeCA9IHRpbWVfdmFsdWUsIHkgPSB2YWx1ZV9kZXYsIHltaW4gPSB2YWx1ZV9kZXYgLSAyKnN0ZGVyciwgeW1heCA9IHZhbHVlX2RldiArIDIqc3RkZXJyKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3JpYmJvbihhbHBoYSA9IC41KSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIiwgc2UgPSBGQUxTRSkgKwogIGxhYnMoCiAgICB4ID0gIiIsCiAgICB5ID0gIiIsCiAgICB0aXRsZSA9ICIlIG9mIFBlbm5zeWx2YW5pYW5zIHdobyB3ZXJlIHRlc3RlZCBmb3IgQ09WSUQgb3ZlciBwcmV2aW91cyAxNCBkYXlzIiwKICAgIHN1YnRpdGxlID0gIkRldmlhdGlvbiBmcm9tIGF2ZXJhZ2U7IGluY2x1ZGVzIExPRVNTIHRyZW5kIiwKICAgIGNhcHRpb24gPSAiU291cmNlOiBEZWxwaGkgU3ltcHRvbSBTdXJ2ZXkiCiAgKSArCiAgbGFiMDhfdGhlbWUgCmBgYAoKVG8gYW5ub3RhdGUgdGhpcyBncmFwaCwgd2UgY2FuIHVzZSB0aGUgYGFzLkRhdGUoKWAgZnVuY3Rpb24gdG8gcGFzcyB4IHZhbHVlcyB0byBpdDogCgpgYGB7cn0KcGFfdGVzdGVkICU+JQogIG11dGF0ZSgKICAgIHZhbHVlX2RldiA9IHZhbHVlIC0gbWVhbih2YWx1ZSkKICApICU+JQogIGdncGxvdCguLCBhZXMoeCA9IHRpbWVfdmFsdWUsIHkgPSB2YWx1ZV9kZXYsIHltaW4gPSB2YWx1ZV9kZXYgLSAyKnN0ZGVyciwgeW1heCA9IHZhbHVlX2RldiArIDIqc3RkZXJyKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3JpYmJvbihhbHBoYSA9IC41KSArCiAgYW5ub3RhdGUoInJlY3QiLCB4bWluID0gYXMuRGF0ZSgiMjAyMC0xMS0yNiIpIC0gMTQsIHhtYXggPSBhcy5EYXRlKCIyMDIwLTExLTI2IiksIHltaW4gPSAtNCwgeW1heCA9IDQsIGZpbGwgPSAiZGFya29yYW5nZTIiLCBhbHBoYSA9IC4zKSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gYXMuRGF0ZSgiMjAyMC0xMS0yNiIpIC0gNywgeSA9IC0zLCBzaXplID0gMi41LCBsYWJlbCA9ICIyIFdlZWsgUGVyaW9kXG4gYmVmb3JlIFRoYW5rc2dpdmluZyIsIGNvbCA9ICJkYXJrb3JhbmdlNCIpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzZSA9IEZBTFNFKSArCiAgbGFicygKICAgIHRpdGxlID0gIiUgT2YgUGVubnN5bHZhbmlhbnMgd2hvIHdlcmUgdGVzdGVkIGZvciBDT1ZJRCBvdmVyIHByZXZpb3VzIDE0IGRheXMiLAogICAgc3VidGl0bGUgPSAiRGV2aWF0aW9uIGZyb20gYXZlcmFnZTsgaW5jbHVkZXMgTE9FU1MgdHJlbmQiLAogICAgeCA9ICIiLAogICAgeSA9ICIiLAogICAgY2FwdGlvbiA9ICJTb3VyY2U6IERlbHBoaSBTeW1wdG9tIFN1cnZleSIKICApICsKICBsYWIwOF90aGVtZQpgYGAKCkZpbmFsbHksIHdlIG1pZ2h0IGJlIGludGVyZXN0ZWQgaW4gY2FsY3VsYXRpbmcgdGhlIGBsb2Vzc2AgdHJlbmQgKmJlZm9yZWhhbmQqLCBhbmQgcGxvdHRpbmcgdGhlIGRldmlhdGlvbnMgZnJvbSB0aGUgbW92aW5nIGF2ZXJhZ2UuIEluIHRoZSBncmFwaCBhYm92ZSwgdGhpcyBpcyBlcXVpdmFsZW50IHRvIHBsb3R0aW5nIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGJsYWNrIGxpbmUgYW5kIHRoZSBibHVlIGxpbmUuIAoKV2UgZmlyc3QgaGF2ZSB0byBjYWxjdWxhdGUgdGhlIGBsb2Vzc2AgbW9kZWwsIGFuZCB0aGVuIHdlIGNhbiBgYXVnbWVudGAgaXQgdGhlIHNhbWUgd2F5IHdlIGRpZCB3aXRoIGBsbWAgYW5kIGBnbG1gLiBOb3RlIHRoYXQgd2UgaGF2ZSB0byB1c2UgYGFzLm51bWVyaWModGltZV92YWx1ZSlgIGluIG9yZGVyIHRvIGdldCB0aGUgbW9kZWwgdG8gd29yay4gSSdtIGFsc28gY2hlYXRpbmcgaGVyZSBhIGxpdHRsZSBiaXQgYW5kIGFkZGluZyBhIG5ldyBjb2x1bW4gdG8gdGhlIGF1Z21lbnRlZCBkYXRhc2V0IHRoYXQgY29udGFpbnMgdGhlIG9yaWdpbmFsLCBub24tbnVtZXJpYyBgdGltZV92YWx1ZWAgdmFyaWFibGUgc28gdGhhdCB3ZSBjYW4gYWN0dWFsbHkgcGxvdCB0aGUgcmVzdWx0cyBvbiB0aGUgc2FtZSBncmFwaC4KCmBgYHtyfQpsb2Vzc19tb2RlbCA9IGxvZXNzKHZhbHVlIH4gYXMubnVtZXJpYyh0aW1lX3ZhbHVlKSwgZGF0YSA9IHBhX3Rlc3RlZCkKbG9lc3NfYXVnID0gYXVnbWVudChsb2Vzc19tb2RlbCkgJT4lCiAgbXV0YXRlKHRpbWVfdmFsdWUgPSBwYV90ZXN0ZWQkdGltZV92YWx1ZSkKYGBgCgpUbyBwbG90IHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGZpdHRlZCBhbmQgb2JzZXJ2ZWQgdmFsdWVzLCB3ZSBjYW4ganVzdCBtYWtlIGEgZ2dwbG90IHVzaW5nIHRoZSByZXN1bHRzIGZyb20gYGF1Z21lbnRgOiAKCmBgYHtyfQpnZ3Bsb3QobG9lc3NfYXVnKSArCiAgZ2VvbV9saW5lKGFlcyh4ID0gdGltZV92YWx1ZSwgeSA9IC5maXR0ZWQgLSB2YWx1ZSkpICsKICBsYWJzKAogICAgdGl0bGUgPSAiJSBPZiBQZW5uc3lsdmFuaWFucyB3aG8gd2VyZSB0ZXN0ZWQgZm9yIENPVklEIG92ZXIgcHJldmlvdXMgMTQgZGF5cyIsCiAgICBzdWJ0aXRsZSA9ICJEZXZpYXRpb24gZnJvbSBMT0VTUyB0cmVuZCIsCiAgICB4ID0gIiIsCiAgICB5ID0gIiIsCiAgICBjYXB0aW9uID0gIlNvdXJjZTogRGVscGhpIFN5bXB0b20gU3VydmV5IgogICkgKwogIGxhYjA4X3RoZW1lCmBgYAoKKE9uZSB0aGluZyB0byBiZSBjYXJlZnVsIG9mIHdpdGggdGhpcyBkYXRhIGlzIHRoYXQgd2UgYWxyZWFkeSBoYXZlIGEgc21vb3RoZWQgZXN0aW1hdGUgLS0gd2UncmUgYmFzaWNhbGx5ICJkb3VibGUgc21vb3RoaW5nIiB3aGVuIHdlIHVzZSBMT0VTUyBoZXJlLikKCiMjIyBEZWNvbXBvc2luZyBUaW1lIFNlcmllcwoKQ2hhcHRlciA5IG9mICpUaGUgVHJ1dGhmdWwgQXJ0KiBzcGVudCBzb21lIHRpbWUgZGlzY3Vzc2luZyBzZWFzb25hbGl0eSBhbmQgZGVjb21wb3NpbmcgdGltZSBzZXJpZXMgaW50byB0aGUgc2Vhc29uYWwsIHRyZW5kLCBhbmQgcmFuZG9tIGNvbXBvbmVudHMuIFRoaXMgaXMgc3VwZXIgZWFzeSBpbiBSISBCdXQgaXQgZG9lc24ndCB3b3JrIGZvciB0aGlzIGRhdGEgKHNpbmNlIHdlIG9ubHkgaGF2ZSAzIG1vbnRocyBvZiBkYXRhKS4gQmVsb3cgaXMgc29tZSBjb2RlIGluICJiYXNlIFIiIHRoYXQgZG9lcyBub3Qgd29yaywgYnV0IHNob3dzIHRoZSBzZXF1ZW5jZSBvZiBjb21tYW5kcyB0aGF0IHdvdWxkIG1ha2Ugc3VjaCBhIHBsb3QgZm9yIHlvdS4gCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwYV90ZXN0ZWRfdHMgPSB0cyhwYV90ZXN0ZWQkdmFsdWUpCnBhX3Rlc3RlZF9kZWNvbXAgPSBkZWNvbXBvc2UocGFfdGVzdGVkX3RzKQpwbG90KHBhX3Rlc3RlZWRfZGVjb21wKQpgYGAKCllvdSdsbCBub3RpY2UgdGhpcyBpc24ndCBgZ2dwbG90MmAhIEkgZG9uJ3Qgd29yayB3aXRoIHRpbWUgc2VyaWVzIHZlcnkgb2Z0ZW4sIGJ1dCBmcm9tIHdoYXQgSSBjYW4gdGVsbCB0aGUgYGdnc2Vhc2AgcGFja2FnZSBpcyB0aGUgYmVzdCBiZXQgZm9yIGRvaW5nIGEgc2ltaWxhciB0aGluZyBpbiBnZ3Bsb3QuIFtIZXJlJ3MgYSBibG9nIHBvc3RdKGh0dHA6Ly9mcmVlcmFuZ2VzdGF0cy5pbmZvL2Jsb2cvMjAxNi8wMy8yOC9nZ3NlYXMtdXBkYXRlKSB3aXRoIGEgcXVpY2sgZXhhbXBsZSBpZiB0aGlzIGlzIHNvbWV0aGluZyB5b3UncmUgaW50ZXJlc3RlZCBpbiBmb3IgYW55IG9mIHlvdXIgcHJvamVjdHMuIAoKIyBQYXJ0IDI6IEFsbHV2aWFsIERpYWdyYW1zCgpJbiBvcmRlciB0byBtYWtlIHRoaXMgZ3JhcGgsIHlvdSdsbCBuZWVkIHRvIGluc3RhbGwgdGhlIGBnZ2FsbHV2aWFsYCBwYWNrYWdlIGZyb20gQ1JBTi4gWW91IGNhbiBkbyBzbyBlaXRoZXIgYnkgcnVubmluZyBgaW5zdGFsbC5wYWNrYWdlcygnZ2dhbGx1dmlhbCcpYCBpbiB0aGUgY29uc29sZSwgb3IgYnkgY2xpY2tpbmcgIlRvb2xzIC0tPiBJbnN0YWxsIFBhY2thZ2VzLi4uIiBpbiB0aGUgdG9vbGJhciBhbmQgdGhlbiB0eXBpbmcgYGdnYWxsdXZpYWxgIGluIHRoZSBzZWFyY2ggYmFyLiBUaGUgZGF0YSB3ZSdyZSB1c2luZyBoZXJlIGlzIGJ1aWx0IGludG8gdGhlIHBhY2thZ2UuIAoKCmBgYHtyfQpkYXRhKHZhY2NpbmF0aW9ucykKYGBgCgpGaXJzdCwgd2UnbGwgZG8gc29tZSBjbGVhbmluZyBzbyB0aGF0IG91ciBkYXRhIGRpc3BsYXlzIG5pY2VseTogCgpgYGB7cn0KdmFjY2luYXRpb25zID0gdmFjY2luYXRpb25zICU+JQogIG11dGF0ZSgKICAgIHJlc3BvbnNlID0gZmFjdG9yKHJlc3BvbnNlLCByZXYobGV2ZWxzKHJlc3BvbnNlKSkpLAogICAgc3VydmV5ID0gY2FzZV93aGVuKHN1cnZleSA9PSAibXMxNTNfTlNBIiB+ICJGaXJzdCBTdXJ2ZXkiLAogICAgICAgICAgICAgICAgICAgICAgc3VydmV5ID09ICJtczQzMl9OU0EiIH4gIlNlY29uZCBTdXJ2ZXkiLCAKICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAiVGhpcmQgU3VydmV5IikpIApoZWFkKHZhY2NpbmF0aW9ucykKYGBgCgpFeGFtaW5lIHRoZSBgdmFjY2luYXRpb25zYCBkYXRhICpiZWZvcmUqIHRoZSBjbGVhbmluZyBzdGVwLCBhbmQgc2VlIGlmIHlvdSBjYW4gZmlndXJlIG91dCB3aGF0IHRoZXNlIHN0ZXBzIGRpZC4gVGhlIGBjYXNlX3doZW5gIGZ1bmN0aW9uIGlzIGVzcGVjaWFsbHkgdXNlZnVsIGZvciBkZWFsaW5nIHdpdGggZmFjdG9ycy90ZXh0IChpdCdzIGVzc2VudGlhbGx5IGEgbmVzdGVkIGlmLWVsc2Ugc3RhdGVtZW50KS4gCgpUaGUgc3ludGF4IGZvciBgZ2dhbGx1dmlhbGAgaXMgZGlmZmVyZW50IHRoYW4gYSBsb3Qgb2Ygb3RoZXIgYGdlb21gcyB3ZSd2ZSBzZWVuLCBhbmQgZnJhbmtseSBpdCdzIHN0aWxsIGNvbmZ1c2luZyB0byBtZS4gSSBkbyB0aGluayB0aGVzZSBwbG90cyBhcmUgYmVhdXRpZnVsIGFuZCB1c2VmdWwsIHNvIEkgd2FudGVkIHRvIGluY2x1ZGUgdGhlbS4gSWYgeW91IHdhbnQgdG8gbGVhcm4gbW9yZSBhYm91dCBob3cgdG8gbWFrZSB0aGVtLCBJIHJlY29tbWVuZCB0aGUgW3BhY2thZ2UgdmlnbmV0dGVdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9nZ2FsbHV2aWFsL3ZpZ25ldHRlcy9nZ2FsbHV2aWFsLmh0bWwpLiAKCkJlbG93IGlzIHRoZSBjb2RlIGZvciBhIHZlcnkgYmFzaWMgYWxsdXZpYWwgZGlhZ3JhbS4gVHJ5IGNvbW1lbnRpbmcgb3V0IChvbmUgbGluZSBhdCBhIHRpbWUpIHRoZSBgZ2VvbV9mbG93KClgLCBgZ2VvbV9zdHJhdHVtKGFscGhhID0gLjUpYCwgYW5kIGBnZW9tX3RleHQoc3RhdCA9ICJzdHJhdHVtIiwgc2l6ZSA9IDMpYCB0byBnZXQgYSBmZWVsIGZvciB3aGF0IGVhY2ggb2YgdGhlbSBkb2VzLiAKCmBgYHtyfQpnZ3Bsb3QodmFjY2luYXRpb25zLAogICAgICAgYWVzKHggPSBzdXJ2ZXksIAogICAgICAgICAgIHN0cmF0dW0gPSByZXNwb25zZSwgCiAgICAgICAgICAgYWxsdXZpdW0gPSBzdWJqZWN0LAogICAgICAgICAgIHkgPSBmcmVxLAogICAgICAgICAgIGZpbGwgPSByZXNwb25zZSwgCiAgICAgICAgICAgbGFiZWwgPSByZXNwb25zZSkpICsKICBnZW9tX2Zsb3coKSArCiAgZ2VvbV9zdHJhdHVtKGFscGhhID0gLjUpICsKICBnZW9tX3RleHQoc3RhdCA9ICJzdHJhdHVtIiwgc2l6ZSA9IDMpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBnZ3RpdGxlKCJ2YWNjaW5hdGlvbiBzdXJ2ZXkgcmVzcG9uc2VzIGF0IHRocmVlIHBvaW50cyBpbiB0aW1lIikKYGBgCgpUaGlzIGdyYXBoIGlzIG9mdGVuIHVzZWQgdG8gZGlzcGxheSByZWxhdGlvbnNoaXBzIGJldHdlZW4gY2F0ZWdvcmljYWwgZGF0YSwgYnV0IHNpbmNlIGl0ICJmZWVscyIgbGlrZSB0aGUgZGF0YSBpcyBmbG93aW5nIGZyb20gbGVmdCB0byByaWdodCwgSSBmaW5kIGl0IGVzcGVjaWFsbHkgdXNlZnVsIGZvciB0aW1lLXJlbGF0ZWQgZGF0YS4gSXQgd291bGQgb2YgY291cnNlIGJlIHRvbyBidXN5IGZvciBhIHVzdWFsIHRpbWUgc2VyaWVzLCBidXQgaXQncyBhIGdvb2Qgb3B0aW9uIGlmIHlvdSBoYXZlIGEgZmV3ICJzbmFwc2hvdHMiIG9mIGEgdmFyaWFibGUgYXQgZGlmZmVyZW50IHBlcmlvZHMgb2YgdGltZSBhbmQgd2FudCB0byB2aXN1YWxpemUgaG93IHRoZSBkaXN0cmlidXRpb24gY2hhbmdlZC4gCgpUbyBjbGVhbiB0aGlzIHVwIGEgYml0LCB0cnkgc29tZSBvZiB0aGUgZm9sbG93aW5nIHRvIGdldCBhcyBjbG9zZSB0byB0aGUgYmVsb3cgZ3JhcGggYXMgeW91IGNhbi4gKEkgdXNlZCB0aGUgYE9wdGltYWAgZm9udCBhbmQgYHNjYWxlX2NvbG9yX3ZpcmlkaXMoZW5kID0gLjc1IClgIGNvbG9yIHNjYWxlLCBidXQgeW91IGFyZSB3ZWxjb21lIHRvIGNob29zZSBvdGhlcnMhKQoKICArIGBnZW9tX2Zsb3cod2lkdGggPSAxLzYpYCAKICArIGBzY2FsZV94X2Rpc2NyZXRlKGV4cGFuZCA9IGMoLjEsIC4xKSlgCiAgKyBgZ2VvbV9zdHJhdHVtKGNvbCA9ICJibGFjayIsIGZpbGwgPSAiYmxhY2siLCB3aWR0aCA9IDEvNiwgYWxwaGEgPSAuOClgIAogICsgYGdlb21fdGV4dChzdGF0ID0gInN0cmF0dW0iLCBzaXplID0gMywgY29sID0gIndoaXRlIiwgYW5nbGUgPSA0NSlgCgpgYGB7ciwgZWNobyA9IEZBTFNFfQpnZ3Bsb3QodmFjY2luYXRpb25zLAogICAgICAgYWVzKHggPSBzdXJ2ZXksIAogICAgICAgICAgIHN0cmF0dW0gPSByZXNwb25zZSwgCiAgICAgICAgICAgYWxsdXZpdW0gPSBzdWJqZWN0LCAKICAgICAgICAgICB5ID0gZnJlcSwgCiAgICAgICAgICAgZmlsbCA9IHJlc3BvbnNlLCAKICAgICAgICAgICBsYWJlbCA9IHJlc3BvbnNlKSkgKwogIGdlb21fZmxvdyh3aWR0aCA9IDEvNikgKwogIHNjYWxlX3hfZGlzY3JldGUoZXhwYW5kID0gYyguMSwgLjEpKSArCiAgZ2VvbV9zdHJhdHVtKGNvbCA9ICJibGFjayIsIGZpbGwgPSAiYmxhY2siLCB3aWR0aCA9IDEvNiwgYWxwaGEgPSAuOCkgKwogIGdlb21fdGV4dChzdGF0ID0gInN0cmF0dW0iLCBzaXplID0gMywgY29sID0gIndoaXRlIiwgZmFtaWx5ID0gIk9wdGltYSIsIGFuZ2xlID0gNDUpICsKICBsYWJzKHRpdGxlID0gIkluZmx1ZW56YSBWYWNjaW5lIFByYWN0aWNlcyBhY3Jvc3MgVGltZSIsCiAgICAgICBzdWJ0aXRsZSA9ICJTb3VyY2U6IFJBTkQgQW1lcmljYW4gTGlmZSBQYW5lbCB2aWEgUiBwYWNrYWdlICdnZ2FsbHV2aWFsJyIsCiAgICAgICB4ID0gIiIsIAogICAgICAgeSA9ICIiKSArCiAgdGhlbWVfbWluaW1hbChiYXNlX2ZhbWlseSA9ICJPcHRpbWEiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArIAogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKGVuZCA9IC43NSkgKyAKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QoZW5kID0gLjc1KSAKYGBgCgoKIyBQYXJ0IDM6IE9wZW4tRW5kZWQKClVzaW5nIGEgZGlmZmVyZW50IGBzaWduYWxgIGZyb20gdGhlIGBjb3ZpZGAgZGF0YSAob3IgbW9yZSEpLCBhbmQvb3IgcmVzdHJpY3RpbmcgdG8gYW55IHN0YXRlcyBvZiB5b3VyIGNob29zaW5nLCBtYWtlIGFueSBvZiB0aGUgdGltZS1yZWxhdGVkIHBsb3RzIGFib3ZlLCBvciBzb21ldGhpbmcgcmVsYXRlZCB0byB0aGVtLiBJdCBtYXkgYmUgaGVscGZ1bCB0byByZXR1cm4gdG8gdGhlIGRlc2NyaXB0aW9uIG9mIERlbHBoaSdzIFtzaWduYWwgdmFyaWFibGVzXShodHRwczovL2NtdS1kZWxwaGkuZ2l0aHViLmlvL2RlbHBoaS1lcGlkYXRhL2FwaS9jb3ZpZGNhc3Qtc2lnbmFscy9mYi1zdXJ2ZXkuaHRtbCNiZWhhdmlvci1pbmRpY2F0b3JzKSB3aGVuIGNob29zaW5nIGEgYHNpZ25hbGAgYW5kIGNyZWF0aW5nICB5b3VyIHRpdGxlcywgYXhlcyBsYWJlbHMsIGFuZCBsZWdlbmQuCgpZb3Ugc2hvdWxkIG1ha2Ugc3VyZSB0byBpbmNvcnBvcmF0ZSB0aGUgZm9sbG93aW5nIGN1c3RvbSBlbGVtZW50cyBpbiB5b3VyIGdyYXBoOiAKCiAgKyBYLWF4aXMgbGFiZWxzIHVzaW5nIGBzY2FsZV94X3RpbWVgIGlmIG1ha2luZyBhIHRpbWUgc2VyaWVzCiAgKyBDb2xvciBwYWxldHRlCiAgKyBUaGVtZQogICsgSW5mb3JtYXRpdmUgdGl0bGUvc3VidGl0bGUvYXhlcyBsYWJlbHMvbGVnZW5kcywgZXRjLiAoSWYgeW91IGZpbmQgeW91IGRvIG5vdCBuZWVkIG9uZSBvciBtb3JlIG9mIHRoZXNlIGVsZW1lbnRzIHRvIGZ1bGx5IGV4cGxhaW4gd2hhdCBpcyBnb2luZyBvbiBpbiB5b3VyIGdyYXBoLCB5b3UgY2FuIGxlYXZlIHRoZW0gb3V0IGlmIHlvdSBwcmVmZXIhKQoK