Some Other Topics

Some topics we did not have time to look at:

Visualizing Uncertainty

All estimates from data are associated with some degree of uncertainty.

Effectively communicating that uncertainty in visualizations is challenging and an active area of research.

Hurricanes

From Cairo (2019); images from a blog post by the author.

The cone of uncertainty:

The NHC forecast cone is designed that two-thirds of historical official forecast errors over a 5-year sample fall within the code for a particular time point..

When published in the media these visualizations are routinely misinterpreted something like this:

A more effective representation might be something like this, showing an ensemble of possible tracks:

An animated version may be more effective, if the presentation medium permits.

Developing better visualizations for hurricane forecasting, especially targeting the public, is an active area of research.

Chocolate Bars

Expert ratings, on a scale from 0 to 5, for chocolate bars manufactured in several countries:

data(cacao, package = "dviz.supp")
countries <- c("U.S.A.", "Austria", "Belgium", "Canada", "Peru", "Switzerland")

col80 <- desaturate(darken("#0072B2", .2), .3)
col95 <- desaturate(lighten("#0072B2", .2), .3)
col99 <- desaturate(lighten("#0072B2", .4), .3)
colP <- col95
colM <- "#D55E00"

c1 <- filter(cacao, location %in% countries)
c1sums <- group_by(c1, location) %>%
    summarize(m = mean(rating),
              s = sd(rating),
              n = n()) %>%
    ungroup()

c1CI <- mutate(data.frame(level = c(0.8, 0.95, 0.99)),
               df = lapply(level,
                           function(lev)
                               with(c1sums, {
                                   h <- s * qt(1 - (1 - lev) / 2, n - 1) /
                                       sqrt(n)
                                   cbind(c1sums, data.frame(xmin = m - h,
                                                            xmax = m + h))
                               }))) %>%
    unnest("df")

ggplot(c1, aes(rating, reorder(location, rating))) +
    geom_point(position = position_jitter(height = 0.3, width = 0.05),
               size = 0.5, color = colP) +
    ##geom_point(aes(m, location), data = c1sums, size = 2.5, color = colM) +
    geom_segment(aes(x = m, xend = m,
                     y = as.integer(reorder(location, m)) - 0.3,
                     yend = as.integer(reorder(location, m)) + 0.3),
                 size = 2, color = colM, data = c1sums) +
    thm +
    ylab("") +
    ggtitle("Ratings for Chocolate Bars", "Bars are sample means.")

The standard deviations of the data distributions are comparable, but the lengths of confidence intervals for the mean vary because of the different sample sizes:

p <- ggplot(filter(c1CI, level == 0.95),
            aes(m, reorder(location, m), xmin = xmin, xmax = xmax)) +
    geom_errorbarh(height = 0) +
    geom_point(size = 2.5, color = colM) +
    thm +
    ylab("") +
    ggtitle("Confidence Intervals for the Mean", "Confidence level 95%")

p + scale_x_continuous(limits = c(1, 4), name = "mean rating")

The same plot with a reduced horizontal range:

p + scale_x_continuous(limits = c(2.5, 3.8), name = "mean rating")

A more elaborate display with confidence intervals at several levels:

## based on code for Wilke's Fig. 16.7
arrange(c1CI, desc(level)) %>%
    mutate(level = paste0(100 * level, "%"),
           location = reorder(location, m)) %>%
    ggplot(aes(m, location, xmin = xmin, xmax = xmax)) +
    geom_errorbarh(aes(size = level, color = level), height = 0) +
    geom_errorbarh(aes(color = level), height = 0.1) +
    geom_point(size = 2.5, color = colM) +
    scale_x_continuous(limits = c(2.5, 3.8), name = "mean rating") +
    scale_size_manual(name = "confidence level",
                      values = c(`80%` = 2.25, `95%` = 1.5, `99%` = 0.75),
                      guide = guide_legend(direction = "horizontal",
                                           title.position = "top",
                                           label.position = "bottom")) +
    scale_color_manual(name = "confidence level",
                       values = c(`80%` = col80, `95%` = col95, `99%` = col99),
                       guide = guide_legend(direction = "horizontal",
                                            title.position = "top",
                                            label.position = "bottom")) +

    thm +
    theme(legend.position = c(1, 0.01), legend.justification = c(1, 0)) +
    ylab("") +
    ggtitle("Confidence Intervals for the Mean")

Confidence densities, or confidence distributions, as proposed in

Adrian W. Bowman. Graphics for Uncertainty. J. R. Statist. Soc. A 182:1-16, 2018. Link

## based on code for Wilke's Fig. 16.9 (e)
ggplot(filter(c1CI, level == 0.95),
       aes(x = m, y = reorder(location, m))) +
    stat_confidence_density(aes(moe = xmax - m, fill = stat(ndensity)),
                            height = 0.7, confidence = 0.95, alpha = NA) +
    geom_segment(aes(x = m, xend = m,
                     y = as.integer(reorder(location, m)) - 0.35,
                     yend = as.integer(reorder(location, m)) + 0.35),
                 size = 2, color = colM) +
    scale_fill_gradient(low = "#81A7D600", high = "#345A7FD0") +
    scale_x_continuous(limits = c(2.5, 3.8), name = "mean rating") +
    thm +
    ylab("")

One drawback of all of these methods: The least precise measurement draws the most attention.

Old Cars

Using the very old mtcars data set to illustrate estimating a smooth relationship.

A default geom_smooth shows an estimate along with a point-wise confidence band.

p <- ggplot(mtcars, aes(disp, mpg)) +
    geom_point() +
    thm

p + geom_smooth()

This may not give the best sense of the joint uncertainty: if the curve is higher on some places it may need to be lower in others.

Showing an ensemble of curves that all are plausible can be a better choice.

Here the ensemble is generated using a case-based bootstrap.

These plots are called ensemble plots (also spaghetti plots, for obvious reasons).

mts <- lapply(seq_len(10),
              function(i) mutate(sample_frac(mtcars, 1, replace = TRUE),
                                 sample = i)) %>%
    bind_rows

p2 <- p +
    geom_smooth(color = NA) +
    geom_smooth(aes(group = sample),
                se = FALSE, size = 0.3, color = "#3366FF", data = mts)
p2

If animation is available, an alternative is to show the curves one at a time in an animation.

This is an example of a hypothetical outcomes plot, or HOP, as introduced in

Hullman, Jessica, Paul Resnick, and Eytan Adar. “Hypothetical outcome plots outperform error bars and violin plots for inferences about reliability of variable ordering.” PloS one 10, no. 11 (2015).

Again a bootstrap is used to produce the estimates.

library(gganimate)
animate(p2 + transition_states(sample, transition_length = 2, state_length = 1))

Data Quality and Integrity

A visualization can accurately reflect data but still be misleading if the data are faulty.

A recent NY Times article shows a choropleth map of the estimated share of adults who would “definitely” or “probably” get the COVID-19 vaccine.

Cutoffs: 49 60 65 70 75 80 91 %

The map may accurately reflect the estimates, but the estimates have obvious problems.

Plot Annotation, Plot Ensembles, and Dashboards

Less Old Cars

Plot annotations can create popout and help focus the viewer’s attention.

Here are a few examples for the mpg data:

ggplot(mpg, aes(displ, hwy)) +
    geom_point() +
    geom_mark_hull(aes(filter = class == "2seater"),
                   fill = "blue",
                   description = "2-Seaters have high displacement values, but also high fuel efficiency for their displacement.") +
    geom_mark_rect(aes(filter = hwy > 40),
                   fill = "green",
                   description = "These are Volkswagens") +
    geom_mark_circle(aes(filter = hwy == 12),
                     fill = "red",
                     description = "Three pickups and an suv.")
## Warning: The concaveman package is required for geom_mark_hull

Coffee

It is often useful to use several graphics to present an analysis.

Collections of related graphs are sometimes called ensemble graphics.

On line presentations of analyses involving multiple visualizations and, typically, some interactive features are also called dashboards.

To aid the viewer it is usually best to design these visualizations together, with common axis choices and color mappings.

Unwin’s Fig 12.1 provides a simple example:

library(ggplot2)
library(GGally)
library(gridExtra)

data(coffee, package = "pgmm")
coffee <- within(coffee, Type <- ifelse(Variety == 1,
                                        "Arabica", "Robusta"))
names(coffee) <- abbreviate(names(coffee), 8)
a <- ggplot(coffee, aes(x = Type)) + geom_bar(aes(fill = Type)) +
    scale_fill_manual(values = c("grey70", "red")) +
    guides(fill = FALSE) + ylab("")
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
b <- ggplot(coffee, aes(x = Fat, y = Caffine, colour = Type)) +
    geom_point(size = 3) +
    scale_colour_manual(values = c("grey70", "red"))
c <- ggparcoord(coffee[order(coffee$Type), ], columns = 3 : 14,
                groupColumn = "Type", scale = "uniminmax") +
    xlab("") + ylab("") +
    theme(legend.position = "none") +
    scale_colour_manual(values = c("grey", "red")) +
    theme(axis.ticks.y = element_blank(),
          axis.text.y = element_blank())
grid.arrange(arrangeGrob(a, b, ncol = 2, widths = c(1, 2)),
             c, nrow = 2)

Data on the chemical composition of coffee samples collected from around the world, comprising 43 samples from 29 countries. Each sample is either of the Arabica or Robusta variety. Twelve of the thirteen chemical constituents reported in the study are given. The omitted variable is total chlorogenic acid; it is generally the sum of the chlorogenic, neochlorogenic and isochlorogenic acid values.

Streuli, H. (1973). Der heutige stand der kaffeechemie. In Association Scientifique International du Cafe, 6th International Colloquium on Coffee Chemisrty, Bogata, Columbia, pp. 61-72.

Wrapping Up

Some of the areas we covered:

Visualization

  • Many different types of graphs.

    • Strengths, weaknesses.
    • Pitfalls.
    • Scalability.
    • Creating these graphs in R.
  • Perception

    • Channels and mappings; relative effectiveness.
    • Using to assess, design visualizations.
    • Effective use of color.
  • A little on interaction, animation.

  • Emphasis on techniques useful for exploration, scientific reporting.

Data Technologies

  • Reading different data formats.
  • Scraping data from the web.
  • Cleaning data.
  • Rearranging data for analysis.
  • Merging data from several sources.

Reproducible research tools

  • rmarkdown for integrating code and reporting.
  • Version control, git, GitHub.

Learning More

Class notes will remain available, in some form, at the class web site.

Some books to look at:

Some blogs to check out:

Keep a critical eye out for good (and not so good) uses of data visualization in the media.

LS0tCnRpdGxlOiAiRmluYWwgTm90ZXMiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogImhpZGUiCi0tLQoKYGBge3IgZ2xvYmFsX29wdGlvbnMsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChjb2xsYXBzZSA9IFRSVUUsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLAogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLCBmaWcuYWxpZ24gPSAiY2VudGVyIikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoY29sb3JzcGFjZSkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkodW5nZXZpeikKbGlicmFyeShnZ2ZvcmNlKQp0aG0gPC0gdGhlbWVfbWluaW1hbCgpICsKICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSkgKwogICAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG9yID0gImdyZXkzMCIsIGZpbGwgPSBOQSkpCgpzZXQuc2VlZCgxMjM0NSkKYGBgCgoKIyMgU29tZSBPdGhlciBUb3BpY3MKClNvbWUgdG9waWNzIHdlIGRpZCBub3QgaGF2ZSB0aW1lIHRvIGxvb2sgYXQ6CgoqIFdvcmtpbmcgd2l0aCBtb2RlbHMgKFtDaGFwdGVyIDYgaW4gSGVhbHksCiAgMjAxOF0oaHR0cHM6Ly9zb2N2aXouY28vbW9kZWxpbmcuaHRtbCk7IFtDaGFwdGVyIDI1IGluCiAgUjREU10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9tYW55LW1vZGVscy5odG1sKSkuCgoqIFtWaXN1YWxpemluZyBtaXNzaW5nCiAgdmFsdWVzXShodHRwOi8vbmFuaWFyLm5qdGllcm5leS5jb20vYXJ0aWNsZXMvbmFuaWFyLXZpc3VhbGlzYXRpb24uaHRtbCkuCgoqIFZpc3VhbGl6aW5nIHVuY2VydGFpbnR5IChbQ2hhcHRlciAxNiBvZiBXaWxrZSwgMjAxOV0oaHR0cHM6Ly9jbGF1c3dpbGtlLmNvbS9kYXRhdml6L3Zpc3VhbGl6aW5nLXVuY2VydGFpbnR5Lmh0bWwpIGFuZCBbYmVsb3ddKCN2aXN1YWxpemluZy11bmNlcnRhaW50eSkpCgoqIFBsb3QgQW5ub3RhdGlvbiwgcGxvdCBlbnNlbWJsZXMsIGFuZCBkYXNoYm9hcmRzLiAoW1BhcnQgSUkgb2YgV2lsa2UsIDIwMTldKGh0dHBzOi8vY2xhdXN3aWxrZS5jb20vZGF0YXZpei9wcm9wb3J0aW9uYWwtaW5rLmh0bWwpOyBbQ2hhcHRlciA1IG9mIEhlYWx5LCAyMDE4XShodHRwczovL3NvY3Zpei5jby93b3JrZ2VvbXMuaHRtbCk7IFtiZWxvd10oI3Bsb3QtYW5ub3RhdGlvbi1wbG90LWVuc2VtYmxlcy1hbmQtZGFzaGJvYXJkcykpLgoKCiMjIyBWaXN1YWxpemluZyBVbmNlcnRhaW50eQoKQWxsIGVzdGltYXRlcyBmcm9tIGRhdGEgYXJlIGFzc29jaWF0ZWQgd2l0aCBzb21lIGRlZ3JlZSBvZiB1bmNlcnRhaW50eS4KCkVmZmVjdGl2ZWx5IGNvbW11bmljYXRpbmcgdGhhdCB1bmNlcnRhaW50eSBpbiB2aXN1YWxpemF0aW9ucyBpcyBjaGFsbGVuZ2luZyBhbmQgYW4gYWN0aXZlIGFyZWEgb2YgW3Jlc2VhcmNoXShodHRwOi8vc3BhY2UudWNtZXJjZWQuZWR1L2NoYXB0ZXIpLgoKCiMjIyMgSHVycmljYW5lcwoKRnJvbSBDYWlybyAoMjAxOSk7IGltYWdlcyBmcm9tIGEgW2Jsb2cKcG9zdF0oaHR0cDovL3d3dy50aGVmdW5jdGlvbmFsYXJ0LmNvbS8yMDIwLzAxL2FsbC1ncmFwaGljcy1mcm9tLWhvdy1jaGFydHMtbGllLWZyZWVseS5odG1sKQpieSB0aGUgYXV0aG9yLgoKVGhlIF9jb25lIG9mIHVuY2VydGFpbnR5XzoKCjwhLS0KaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vc2gvZDFrYjBqZHJoa2I0M2o5L0FBRFRCZlJ2QWgtbXhtU3hCUk5acExKamEvNS5DSEFQVEVSNT9kbD0wJnByZXZpZXc9UERGMTAuVHJvcGljYWxzdG9ybS5wZGYmc3ViZm9sZGVyX25hdl90cmFja2luZz0xCi0tPgoKYGBge3IsIGVjaG8gPSBGQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImltZy9QREYxMC5Ucm9waWNhbHN0b3JtLnBuZyIpCmBgYAoKVGhlIFtOSEMgZm9yZWNhc3QgY29uZV0oaHR0cHM6Ly93d3cubmhjLm5vYWEuZ292L2Fib3V0Y29uZS5zaHRtbCkgaXMKZGVzaWduZWQgdGhhdCB0d28tdGhpcmRzIG9mIGhpc3RvcmljYWwgb2ZmaWNpYWwgZm9yZWNhc3QgZXJyb3JzIG92ZXIKYSA1LXllYXIgc2FtcGxlIGZhbGwgd2l0aGluIHRoZSBjb2RlIGZvciBhIHBhcnRpY3VsYXIgdGltZSBwb2ludC4uCgoKV2hlbiBwdWJsaXNoZWQgaW4gdGhlIG1lZGlhIHRoZXNlIHZpc3VhbGl6YXRpb25zIGFyZSByb3V0aW5lbHkKbWlzaW50ZXJwcmV0ZWQgc29tZXRoaW5nIGxpa2UgdGhpczoKCjwhLS0KaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vc2gvZDFrYjBqZHJoa2I0M2o5L0FBRFRCZlJ2QWgtbXhtU3hCUk5acExKamEvNS5DSEFQVEVSNT9kbD0wJnByZXZpZXc9UERGMTEuU3Rvcm1XUk9OR1NpemUucGRmJnN1YmZvbGRlcl9uYXZfdHJhY2tpbmc9MQotLT4KCmBgYHtyLCBlY2hvID0gRkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJpbWcvUERGMTEuU3Rvcm1XUk9OR1NpemUucG5nIikKYGBgCgpBIG1vcmUgZWZmZWN0aXZlIHJlcHJlc2VudGF0aW9uIG1pZ2h0IGJlIHNvbWV0aGluZyBsaWtlIHRoaXMsIHNob3dpbmcKYW4gX2Vuc2VtYmxlXyBvZiBwb3NzaWJsZSB0cmFja3M6Cgo8IS0tCmh0dHBzOi8vd3d3LmRyb3Bib3guY29tL3NoL2Qxa2IwamRyaGtiNDNqOS9BQURUQmZSdkFoLW14bVN4QlJOWnBMSmphLzUuQ0hBUFRFUjU/ZGw9MCZwcmV2aWV3PVBERjEzLlN0b3JtTGluZXMucGRmJnN1YmZvbGRlcl9uYXZfdHJhY2tpbmc9MQotLT4KCmBgYHtyLCBlY2hvID0gRkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJpbWcvUERGMTMuU3Rvcm1MaW5lcy5wbmciKQpgYGAKCkFuIGFuaW1hdGVkIHZlcnNpb24gbWF5IGJlIG1vcmUgZWZmZWN0aXZlLCBpZiB0aGUgcHJlc2VudGF0aW9uIG1lZGl1bQpwZXJtaXRzLgoKRGV2ZWxvcGluZyBbYmV0dGVyIHZpc3VhbGl6YXRpb25zIGZvciBodXJyaWNhbmUgZm9yZWNhc3RpbmddKFRoZXJlIGFyZQphY3RpdmUgcmVzZWFyY2ggY29sbGFib3JhdGlvbnMgYmV0d2VlbiBkYXRhIHZpc3VhbGl6YXRpb24gZXhwZXJ0cyBhbmQKdGhlIE5hdGlvbmFsIEh1cnJpY2FuZSBDZW50ZXIgKSwgZXNwZWNpYWxseSB0YXJnZXRpbmcgdGhlIHB1YmxpYywgaXMKYW4gYWN0aXZlIGFyZWEgb2YgcmVzZWFyY2guCgoKIyMjIyBDaG9jb2xhdGUgQmFycwoKW0V4cGVydCByYXRpbmdzXShodHRwOi8vZmxhdm9yc29mY2FjYW8uY29tKSwgb24gYSBzY2FsZSBmcm9tIDAgdG8gNSwKZm9yIGNob2NvbGF0ZSBiYXJzIG1hbnVmYWN0dXJlZCBpbiBzZXZlcmFsIGNvdW50cmllczoKCmBgYHtyfQpkYXRhKGNhY2FvLCBwYWNrYWdlID0gImR2aXouc3VwcCIpCmNvdW50cmllcyA8LSBjKCJVLlMuQS4iLCAiQXVzdHJpYSIsICJCZWxnaXVtIiwgIkNhbmFkYSIsICJQZXJ1IiwgIlN3aXR6ZXJsYW5kIikKCmNvbDgwIDwtIGRlc2F0dXJhdGUoZGFya2VuKCIjMDA3MkIyIiwgLjIpLCAuMykKY29sOTUgPC0gZGVzYXR1cmF0ZShsaWdodGVuKCIjMDA3MkIyIiwgLjIpLCAuMykKY29sOTkgPC0gZGVzYXR1cmF0ZShsaWdodGVuKCIjMDA3MkIyIiwgLjQpLCAuMykKY29sUCA8LSBjb2w5NQpjb2xNIDwtICIjRDU1RTAwIgoKYzEgPC0gZmlsdGVyKGNhY2FvLCBsb2NhdGlvbiAlaW4lIGNvdW50cmllcykKYzFzdW1zIDwtIGdyb3VwX2J5KGMxLCBsb2NhdGlvbikgJT4lCiAgICBzdW1tYXJpemUobSA9IG1lYW4ocmF0aW5nKSwKICAgICAgICAgICAgICBzID0gc2QocmF0aW5nKSwKICAgICAgICAgICAgICBuID0gbigpKSAlPiUKICAgIHVuZ3JvdXAoKQoKYzFDSSA8LSBtdXRhdGUoZGF0YS5mcmFtZShsZXZlbCA9IGMoMC44LCAwLjk1LCAwLjk5KSksCiAgICAgICAgICAgICAgIGRmID0gbGFwcGx5KGxldmVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbihsZXYpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aXRoKGMxc3VtcywgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGggPC0gcyAqIHF0KDEgLSAoMSAtIGxldikgLyAyLCBuIC0gMSkgLwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcXJ0KG4pCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2JpbmQoYzFzdW1zLCBkYXRhLmZyYW1lKHhtaW4gPSBtIC0gaCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeG1heCA9IG0gKyBoKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pKSkgJT4lCiAgICB1bm5lc3QoImRmIikKCmdncGxvdChjMSwgYWVzKHJhdGluZywgcmVvcmRlcihsb2NhdGlvbiwgcmF0aW5nKSkpICsKICAgIGdlb21fcG9pbnQocG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIoaGVpZ2h0ID0gMC4zLCB3aWR0aCA9IDAuMDUpLAogICAgICAgICAgICAgICBzaXplID0gMC41LCBjb2xvciA9IGNvbFApICsKICAgICMjZ2VvbV9wb2ludChhZXMobSwgbG9jYXRpb24pLCBkYXRhID0gYzFzdW1zLCBzaXplID0gMi41LCBjb2xvciA9IGNvbE0pICsKICAgIGdlb21fc2VnbWVudChhZXMoeCA9IG0sIHhlbmQgPSBtLAogICAgICAgICAgICAgICAgICAgICB5ID0gYXMuaW50ZWdlcihyZW9yZGVyKGxvY2F0aW9uLCBtKSkgLSAwLjMsCiAgICAgICAgICAgICAgICAgICAgIHllbmQgPSBhcy5pbnRlZ2VyKHJlb3JkZXIobG9jYXRpb24sIG0pKSArIDAuMyksCiAgICAgICAgICAgICAgICAgc2l6ZSA9IDIsIGNvbG9yID0gY29sTSwgZGF0YSA9IGMxc3VtcykgKwogICAgdGhtICsKICAgIHlsYWIoIiIpICsKICAgIGdndGl0bGUoIlJhdGluZ3MgZm9yIENob2NvbGF0ZSBCYXJzIiwgIkJhcnMgYXJlIHNhbXBsZSBtZWFucy4iKQpgYGAKClRoZSBzdGFuZGFyZCBkZXZpYXRpb25zIG9mIHRoZSBkYXRhIGRpc3RyaWJ1dGlvbnMgYXJlIGNvbXBhcmFibGUsIGJ1dAp0aGUgbGVuZ3RocyBvZiBjb25maWRlbmNlIGludGVydmFscyBmb3IgdGhlIG1lYW4gdmFyeSBiZWNhdXNlIG9mIHRoZQpkaWZmZXJlbnQgc2FtcGxlIHNpemVzOgoKYGBge3J9CnAgPC0gZ2dwbG90KGZpbHRlcihjMUNJLCBsZXZlbCA9PSAwLjk1KSwKICAgICAgICAgICAgYWVzKG0sIHJlb3JkZXIobG9jYXRpb24sIG0pLCB4bWluID0geG1pbiwgeG1heCA9IHhtYXgpKSArCiAgICBnZW9tX2Vycm9yYmFyaChoZWlnaHQgPSAwKSArCiAgICBnZW9tX3BvaW50KHNpemUgPSAyLjUsIGNvbG9yID0gY29sTSkgKwogICAgdGhtICsKICAgIHlsYWIoIiIpICsKICAgIGdndGl0bGUoIkNvbmZpZGVuY2UgSW50ZXJ2YWxzIGZvciB0aGUgTWVhbiIsICJDb25maWRlbmNlIGxldmVsIDk1JSIpCgpwICsgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMSwgNCksIG5hbWUgPSAibWVhbiByYXRpbmciKQpgYGAKClRoZSBzYW1lIHBsb3Qgd2l0aCBhIHJlZHVjZWQgaG9yaXpvbnRhbCByYW5nZToKCmBgYHtyfQpwICsgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMi41LCAzLjgpLCBuYW1lID0gIm1lYW4gcmF0aW5nIikKYGBgCgpBIG1vcmUgZWxhYm9yYXRlIGRpc3BsYXkgd2l0aCBjb25maWRlbmNlIGludGVydmFscyBhdCBzZXZlcmFsIGxldmVsczoKCmBgYHtyfQojIyBiYXNlZCBvbiBjb2RlIGZvciBXaWxrZSdzIEZpZy4gMTYuNwphcnJhbmdlKGMxQ0ksIGRlc2MobGV2ZWwpKSAlPiUKICAgIG11dGF0ZShsZXZlbCA9IHBhc3RlMCgxMDAgKiBsZXZlbCwgIiUiKSwKICAgICAgICAgICBsb2NhdGlvbiA9IHJlb3JkZXIobG9jYXRpb24sIG0pKSAlPiUKICAgIGdncGxvdChhZXMobSwgbG9jYXRpb24sIHhtaW4gPSB4bWluLCB4bWF4ID0geG1heCkpICsKICAgIGdlb21fZXJyb3JiYXJoKGFlcyhzaXplID0gbGV2ZWwsIGNvbG9yID0gbGV2ZWwpLCBoZWlnaHQgPSAwKSArCiAgICBnZW9tX2Vycm9yYmFyaChhZXMoY29sb3IgPSBsZXZlbCksIGhlaWdodCA9IDAuMSkgKwogICAgZ2VvbV9wb2ludChzaXplID0gMi41LCBjb2xvciA9IGNvbE0pICsKICAgIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDIuNSwgMy44KSwgbmFtZSA9ICJtZWFuIHJhdGluZyIpICsKICAgIHNjYWxlX3NpemVfbWFudWFsKG5hbWUgPSAiY29uZmlkZW5jZSBsZXZlbCIsCiAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKGA4MCVgID0gMi4yNSwgYDk1JWAgPSAxLjUsIGA5OSVgID0gMC43NSksCiAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChkaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZS5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwucG9zaXRpb24gPSAiYm90dG9tIikpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gImNvbmZpZGVuY2UgbGV2ZWwiLAogICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoYDgwJWAgPSBjb2w4MCwgYDk1JWAgPSBjb2w5NSwgYDk5JWAgPSBjb2w5OSksCiAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQoZGlyZWN0aW9uID0gImhvcml6b250YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwucG9zaXRpb24gPSAiYm90dG9tIikpICsKCiAgICB0aG0gKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygxLCAwLjAxKSwgbGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDEsIDApKSArCiAgICB5bGFiKCIiKSArCiAgICBnZ3RpdGxlKCJDb25maWRlbmNlIEludGVydmFscyBmb3IgdGhlIE1lYW4iKQpgYGAKCkNvbmZpZGVuY2UgZGVuc2l0aWVzLCBvciBjb25maWRlbmNlIGRpc3RyaWJ1dGlvbnMsIGFzIHByb3Bvc2VkIGluCgo+IEFkcmlhbiBXLiBCb3dtYW4uIEdyYXBoaWNzIGZvciBVbmNlcnRhaW50eS4gSi4gUi4gU3RhdGlzdC4gU29jLiBBCj4gMTgyOjEtMTYsIDIwMTguIFtMaW5rXShodHRwczovL3Jzcy5vbmxpbmVsaWJyYXJ5LndpbGV5LmNvbS9kb2kvZnVsbC8xMC4xMTExL3Jzc2EuMTIzNzkpCgoKYGBge3J9CiMjIGJhc2VkIG9uIGNvZGUgZm9yIFdpbGtlJ3MgRmlnLiAxNi45IChlKQpnZ3Bsb3QoZmlsdGVyKGMxQ0ksIGxldmVsID09IDAuOTUpLAogICAgICAgYWVzKHggPSBtLCB5ID0gcmVvcmRlcihsb2NhdGlvbiwgbSkpKSArCiAgICBzdGF0X2NvbmZpZGVuY2VfZGVuc2l0eShhZXMobW9lID0geG1heCAtIG0sIGZpbGwgPSBzdGF0KG5kZW5zaXR5KSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWlnaHQgPSAwLjcsIGNvbmZpZGVuY2UgPSAwLjk1LCBhbHBoYSA9IE5BKSArCiAgICBnZW9tX3NlZ21lbnQoYWVzKHggPSBtLCB4ZW5kID0gbSwKICAgICAgICAgICAgICAgICAgICAgeSA9IGFzLmludGVnZXIocmVvcmRlcihsb2NhdGlvbiwgbSkpIC0gMC4zNSwKICAgICAgICAgICAgICAgICAgICAgeWVuZCA9IGFzLmludGVnZXIocmVvcmRlcihsb2NhdGlvbiwgbSkpICsgMC4zNSksCiAgICAgICAgICAgICAgICAgc2l6ZSA9IDIsIGNvbG9yID0gY29sTSkgKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiIzgxQTdENjAwIiwgaGlnaCA9ICIjMzQ1QTdGRDAiKSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygyLjUsIDMuOCksIG5hbWUgPSAibWVhbiByYXRpbmciKSArCiAgICB0aG0gKwogICAgeWxhYigiIikKYGBgCgpPbmUgZHJhd2JhY2sgb2YgYWxsIG9mIHRoZXNlIG1ldGhvZHM6IFRoZSBsZWFzdCBwcmVjaXNlIG1lYXN1cmVtZW50CmRyYXdzIHRoZSBtb3N0IGF0dGVudGlvbi4KCgojIyMjIE9sZCBDYXJzCgpVc2luZyB0aGUgdmVyeSBvbGQgYG10Y2Fyc2AgZGF0YSBzZXQgdG8gaWxsdXN0cmF0ZSBlc3RpbWF0aW5nIGEgc21vb3RoCnJlbGF0aW9uc2hpcC4KCkEgZGVmYXVsdCBgZ2VvbV9zbW9vdGhgIHNob3dzIGFuIGVzdGltYXRlIGFsb25nIHdpdGggYSBwb2ludC13aXNlCmNvbmZpZGVuY2UgYmFuZC4KCgpgYGB7cn0KcCA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoZGlzcCwgbXBnKSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIHRobQoKcCArIGdlb21fc21vb3RoKCkKYGBgCgpUaGlzIG1heSBub3QgZ2l2ZSB0aGUgYmVzdCBzZW5zZSBvZiB0aGUgam9pbnQgdW5jZXJ0YWludHk6IGlmIHRoZSBjdXJ2ZQppcyBoaWdoZXIgb24gc29tZSBwbGFjZXMgaXQgbWF5IG5lZWQgdG8gYmUgbG93ZXIgaW4gb3RoZXJzLgoKU2hvd2luZyBhbiBfZW5zZW1ibGVfIG9mIGN1cnZlcyB0aGF0IGFsbCBhcmUgcGxhdXNpYmxlIGNhbiBiZSBhIGJldHRlcgpjaG9pY2UuCgpIZXJlIHRoZSBlbnNlbWJsZSBpcyBnZW5lcmF0ZWQgdXNpbmcgYSBjYXNlLWJhc2VkIGJvb3RzdHJhcC4KClRoZXNlIHBsb3RzIGFyZSBjYWxsZWQgX2Vuc2VtYmxlIHBsb3RzXyAoYWxzbyBzcGFnaGV0dGkgcGxvdHMsIGZvcgpvYnZpb3VzIHJlYXNvbnMpLgoKYGBge3J9Cm10cyA8LSBsYXBwbHkoc2VxX2xlbigxMCksCiAgICAgICAgICAgICAgZnVuY3Rpb24oaSkgbXV0YXRlKHNhbXBsZV9mcmFjKG10Y2FycywgMSwgcmVwbGFjZSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGUgPSBpKSkgJT4lCiAgICBiaW5kX3Jvd3MKCnAyIDwtIHAgKwogICAgZ2VvbV9zbW9vdGgoY29sb3IgPSBOQSkgKwogICAgZ2VvbV9zbW9vdGgoYWVzKGdyb3VwID0gc2FtcGxlKSwKICAgICAgICAgICAgICAgIHNlID0gRkFMU0UsIHNpemUgPSAwLjMsIGNvbG9yID0gIiMzMzY2RkYiLCBkYXRhID0gbXRzKQpwMgpgYGAKCklmIGFuaW1hdGlvbiBpcyBhdmFpbGFibGUsIGFuIGFsdGVybmF0aXZlIGlzIHRvIHNob3cgdGhlIGN1cnZlcyBvbmUgYXQKYSB0aW1lIGluIGFuIGFuaW1hdGlvbi4KClRoaXMgaXMgYW4gZXhhbXBsZSBvZiBhIF9oeXBvdGhldGljYWwgb3V0Y29tZXMgcGxvdF8sIG9yIF9IT1BfLCBhcwppbnRyb2R1Y2VkIGluCgo+IEh1bGxtYW4sIEplc3NpY2EsIFBhdWwgUmVzbmljaywgYW5kIEV5dGFuIEFkYXIuICJIeXBvdGhldGljYWwKPiBvdXRjb21lIHBsb3RzIG91dHBlcmZvcm0gZXJyb3IgYmFycyBhbmQgdmlvbGluIHBsb3RzIGZvciBpbmZlcmVuY2VzCj4gYWJvdXQgcmVsaWFiaWxpdHkgb2YgdmFyaWFibGUgb3JkZXJpbmcuIiBQbG9TIG9uZSAxMCwgbm8uIDExICgyMDE1KS4KCkFnYWluIGEgYm9vdHN0cmFwIGlzIHVzZWQgdG8gcHJvZHVjZSB0aGUgZXN0aW1hdGVzLgoKYGBge3IsIGNhY2hlID0gVFJVRX0KbGlicmFyeShnZ2FuaW1hdGUpCmFuaW1hdGUocDIgKyB0cmFuc2l0aW9uX3N0YXRlcyhzYW1wbGUsIHRyYW5zaXRpb25fbGVuZ3RoID0gMiwgc3RhdGVfbGVuZ3RoID0gMSkpCmBgYAoKCiMjIyBEYXRhIFF1YWxpdHkgYW5kIEludGVncml0eQoKQSB2aXN1YWxpemF0aW9uIGNhbiBhY2N1cmF0ZWx5IHJlZmxlY3QgZGF0YSBidXQgc3RpbGwgYmUgbWlzbGVhZGluZyBpZgp0aGUgZGF0YSBhcmUgZmF1bHR5LgoKQSByZWNlbnQgW05ZIFRpbWVzCmFydGljbGVdKGh0dHBzOi8vd3d3Lm55dGltZXMuY29tLzIwMjEvMDUvMDMvaGVhbHRoL2NvdmlkLWhlcmQtaW1tdW5pdHktdmFjY2luZS5odG1sKQpzaG93cyBhIGNob3JvcGxldGggbWFwIG9mIHRoZSBlc3RpbWF0ZWQgc2hhcmUgb2YgYWR1bHRzIHdobyB3b3VsZAoiZGVmaW5pdGVseSIgb3IgInByb2JhYmx5IiBnZXQgdGhlIENPVklELTE5IHZhY2NpbmUuCgpDdXRvZmZzOiA0OSAgNjAgICA2NSAgNzAgIDc1ICA4MCAgOTEgJQoKIVtdKGltZy9tYXAtMTA1MC5wbmcpCgpUaGUgbWFwIG1heSBhY2N1cmF0ZWx5IHJlZmxlY3QgdGhlIGVzdGltYXRlcywgYnV0IHRoZSBlc3RpbWF0ZXMgaGF2ZQpvYnZpb3VzIHByb2JsZW1zLgoKIyMjIFBsb3QgQW5ub3RhdGlvbiwgUGxvdCBFbnNlbWJsZXMsIGFuZCBEYXNoYm9hcmRzCgoKIyMjIyBMZXNzIE9sZCBDYXJzCgpQbG90IGFubm90YXRpb25zIGNhbiBjcmVhdGUgcG9wb3V0IGFuZCBoZWxwIGZvY3VzIHRoZSB2aWV3ZXIncwphdHRlbnRpb24uCgpIZXJlIGFyZSBhIGZldyBleGFtcGxlcyBmb3IgdGhlIGBtcGdgIGRhdGE6CgpgYGB7cn0KZ2dwbG90KG1wZywgYWVzKGRpc3BsLCBod3kpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZ2VvbV9tYXJrX2h1bGwoYWVzKGZpbHRlciA9IGNsYXNzID09ICIyc2VhdGVyIiksCiAgICAgICAgICAgICAgICAgICBmaWxsID0gImJsdWUiLAogICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb24gPSAiMi1TZWF0ZXJzIGhhdmUgaGlnaCBkaXNwbGFjZW1lbnQgdmFsdWVzLCBidXQgYWxzbyBoaWdoIGZ1ZWwgZWZmaWNpZW5jeSBmb3IgdGhlaXIgZGlzcGxhY2VtZW50LiIpICsKICAgIGdlb21fbWFya19yZWN0KGFlcyhmaWx0ZXIgPSBod3kgPiA0MCksCiAgICAgICAgICAgICAgICAgICBmaWxsID0gImdyZWVuIiwKICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uID0gIlRoZXNlIGFyZSBWb2xrc3dhZ2VucyIpICsKICAgIGdlb21fbWFya19jaXJjbGUoYWVzKGZpbHRlciA9IGh3eSA9PSAxMiksCiAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSAicmVkIiwKICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb24gPSAiVGhyZWUgcGlja3VwcyBhbmQgYW4gc3V2LiIpCmBgYAoKIyMjIyBDb2ZmZWUKCkl0IGlzIG9mdGVuIHVzZWZ1bCB0byB1c2Ugc2V2ZXJhbCBncmFwaGljcyB0byBwcmVzZW50IGFuIGFuYWx5c2lzLgoKQ29sbGVjdGlvbnMgb2YgcmVsYXRlZCBncmFwaHMgYXJlIHNvbWV0aW1lcyBjYWxsZWQgX2Vuc2VtYmxlIGdyYXBoaWNzXy4KCk9uIGxpbmUgcHJlc2VudGF0aW9ucyBvZiBhbmFseXNlcyBpbnZvbHZpbmcgbXVsdGlwbGUgdmlzdWFsaXphdGlvbnMKYW5kLCB0eXBpY2FsbHksIHNvbWUgaW50ZXJhY3RpdmUgZmVhdHVyZXMgYXJlIGFsc28gY2FsbGVkCl9kYXNoYm9hcmRzXy4KClRvIGFpZCB0aGUgdmlld2VyIGl0IGlzIHVzdWFsbHkgYmVzdCB0byBkZXNpZ24gdGhlc2UgdmlzdWFsaXphdGlvbnMKdG9nZXRoZXIsIHdpdGggY29tbW9uIGF4aXMgY2hvaWNlcyBhbmQgY29sb3IgbWFwcGluZ3MuCgpVbndpbidzIEZpZyAxMi4xIHByb3ZpZGVzIGEgc2ltcGxlIGV4YW1wbGU6CgpgYGB7ciwgb3V0LndpZHRoPSAiNjUlIiwgb3V0LmV4dHJhPSdzdHlsZT0iZmxvYXQ6cmlnaHQ7IHBhZGRpbmc6MTBweCInfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoR0dhbGx5KQpsaWJyYXJ5KGdyaWRFeHRyYSkKCmRhdGEoY29mZmVlLCBwYWNrYWdlID0gInBnbW0iKQpjb2ZmZWUgPC0gd2l0aGluKGNvZmZlZSwgVHlwZSA8LSBpZmVsc2UoVmFyaWV0eSA9PSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkFyYWJpY2EiLCAiUm9idXN0YSIpKQpuYW1lcyhjb2ZmZWUpIDwtIGFiYnJldmlhdGUobmFtZXMoY29mZmVlKSwgOCkKYSA8LSBnZ3Bsb3QoY29mZmVlLCBhZXMoeCA9IFR5cGUpKSArIGdlb21fYmFyKGFlcyhmaWxsID0gVHlwZSkpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImdyZXk3MCIsICJyZWQiKSkgKwogICAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkgKyB5bGFiKCIiKQpiIDwtIGdncGxvdChjb2ZmZWUsIGFlcyh4ID0gRmF0LCB5ID0gQ2FmZmluZSwgY29sb3VyID0gVHlwZSkpICsKICAgIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsKICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiZ3JleTcwIiwgInJlZCIpKQpjIDwtIGdncGFyY29vcmQoY29mZmVlW29yZGVyKGNvZmZlZSRUeXBlKSwgXSwgY29sdW1ucyA9IDMgOiAxNCwKICAgICAgICAgICAgICAgIGdyb3VwQ29sdW1uID0gIlR5cGUiLCBzY2FsZSA9ICJ1bmltaW5tYXgiKSArCiAgICB4bGFiKCIiKSArIHlsYWIoIiIpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCJncmV5IiwgInJlZCIpKSArCiAgICB0aGVtZShheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkKZ3JpZC5hcnJhbmdlKGFycmFuZ2VHcm9iKGEsIGIsIG5jb2wgPSAyLCB3aWR0aHMgPSBjKDEsIDIpKSwKICAgICAgICAgICAgIGMsIG5yb3cgPSAyKQpgYGAKCkRhdGEgb24gdGhlIGNoZW1pY2FsIGNvbXBvc2l0aW9uIG9mIGNvZmZlZSBzYW1wbGVzIGNvbGxlY3RlZCBmcm9tCmFyb3VuZCB0aGUgd29ybGQsIGNvbXByaXNpbmcgNDMgc2FtcGxlcyBmcm9tIDI5IGNvdW50cmllcy4gRWFjaCBzYW1wbGUKaXMgZWl0aGVyIG9mIHRoZSBBcmFiaWNhIG9yIFJvYnVzdGEgdmFyaWV0eS4gVHdlbHZlIG9mIHRoZSB0aGlydGVlbgpjaGVtaWNhbCBjb25zdGl0dWVudHMgcmVwb3J0ZWQgaW4gdGhlIHN0dWR5IGFyZSBnaXZlbi4gIFRoZSBvbWl0dGVkCnZhcmlhYmxlIGlzIHRvdGFsIGNobG9yb2dlbmljIGFjaWQ7IGl0IGlzIGdlbmVyYWxseSB0aGUgc3VtIG9mIHRoZQpjaGxvcm9nZW5pYywgbmVvY2hsb3JvZ2VuaWMgYW5kIGlzb2NobG9yb2dlbmljIGFjaWQgdmFsdWVzLgoKU3RyZXVsaSwgSC4gKDE5NzMpLiBEZXIgaGV1dGlnZSBzdGFuZCBkZXIga2FmZmVlY2hlbWllLiBJbgpfQXNzb2NpYXRpb24gU2NpZW50aWZpcXVlIEludGVybmF0aW9uYWwgZHUgQ2FmZSwgNnRoIEludGVybmF0aW9uYWwKQ29sbG9xdWl1bSBvbiBDb2ZmZWUgQ2hlbWlzcnR5XywgQm9nYXRhLCBDb2x1bWJpYSwgcHAuICA2MS03Mi4KCgojIyBXcmFwcGluZyBVcAoKU29tZSBvZiB0aGUgYXJlYXMgd2UgY292ZXJlZDoKCiMjIyBWaXN1YWxpemF0aW9uCgotIE1hbnkgZGlmZmVyZW50IHR5cGVzIG9mIGdyYXBocy4KCiAgICAtIFN0cmVuZ3Rocywgd2Vha25lc3Nlcy4KICAgIC0gUGl0ZmFsbHMuCiAgICAtIFNjYWxhYmlsaXR5LgogICAgLSBDcmVhdGluZyB0aGVzZSBncmFwaHMgaW4gUi4KICAKLSBQZXJjZXB0aW9uCgogICAgIC0gQ2hhbm5lbHMgYW5kIG1hcHBpbmdzOyByZWxhdGl2ZSBlZmZlY3RpdmVuZXNzLgogICAgIC0gVXNpbmcgdG8gYXNzZXNzLCBkZXNpZ24gdmlzdWFsaXphdGlvbnMuCiAgICAgLSBFZmZlY3RpdmUgdXNlIG9mIGNvbG9yLgoKLSBBIGxpdHRsZSBvbiBpbnRlcmFjdGlvbiwgYW5pbWF0aW9uLgoKLSBFbXBoYXNpcyBvbiB0ZWNobmlxdWVzIHVzZWZ1bCBmb3IgZXhwbG9yYXRpb24sIHNjaWVudGlmaWMgcmVwb3J0aW5nLgoKIyMjIERhdGEgVGVjaG5vbG9naWVzCgotIFJlYWRpbmcgZGlmZmVyZW50IGRhdGEgZm9ybWF0cy4KLSBTY3JhcGluZyBkYXRhIGZyb20gdGhlIHdlYi4KLSBDbGVhbmluZyBkYXRhLgotIFJlYXJyYW5naW5nIGRhdGEgZm9yIGFuYWx5c2lzLgotIE1lcmdpbmcgZGF0YSBmcm9tIHNldmVyYWwgc291cmNlcy4KCiMjIyBSZXByb2R1Y2libGUgcmVzZWFyY2ggdG9vbHMKCi0gYHJtYXJrZG93bmAgZm9yIGludGVncmF0aW5nIGNvZGUgYW5kIHJlcG9ydGluZy4KLSBWZXJzaW9uIGNvbnRyb2wsIGBnaXRgLCBgR2l0SHViYC4KCiMjIyBMZWFybmluZyBNb3JlCgpDbGFzcyBub3RlcyB3aWxsIHJlbWFpbiBhdmFpbGFibGUsIGluIHNvbWUgZm9ybSwgYXQgdGhlIGNsYXNzIHdlYiBzaXRlLgoKU29tZSBib29rcyB0byBsb29rIGF0OgoKICAqIEFsYmVydG8gQ2Fpcm8gKDIwMTkpIF9Ib3cgQ2hhcnRzIExpZTogR2V0dGluZyBTbWFydGVyIGFib3V0IFZpc3VhbAogICAgSW5mb3JtYXRpb25fLCBXLiBXLiBOb3J0b24gJiBDb21wYW55LgoKICAqIENsYXVzIE8uIFdpbGtlICgyMDE5KSBbX0Z1bmRhbWVudGFscyBvZiBEYXRhCiAgICBWaXN1YWxpemF0aW9uX10oaHR0cHM6Ly9jbGF1c3dpbGtlLmNvbS9kYXRhdml6LyksIE/igJlSZWlsbHksCiAgICBJbmMuIChbQm9vayBzb3VyY2Ugb24KICAgIEdpdEh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL2NsYXVzd2lsa2UvZGF0YXZpeik7IFtzdXBwb3J0aW5nCiAgICBtYXRlcmlhbHMgb24gR2l0SHViXShodHRwczovL2dpdGh1Yi5jb20vY2xhdXN3aWxrZS9kdml6LnN1cHApKQoKICAqIEtpZXJhbiBIZWFseSAoMjAxOCkgW19EYXRhIFZpc3VhbGl6YXRpb246IEEgcHJhY3RpY2FsCiAgICBpbnRyb2R1Y3Rpb25fXShodHRwOi8vc29jdml6LmNvLyksIFByaW5jZXRvbgoKKiBXaW5zdG9uIENoYW5nIChpbiBwcmVwYXJhdGlvbiksIFtfUiBHcmFwaGljcyBDb29rYm9va18sIDJuZAogICAgZWRpdGlvbl0oaHR0cHM6Ly9yLWdyYXBoaWNzLm9yZy8pLCBP4oCZUmVpbGx5LiAoW0Jvb2sgc291cmNlIG9uCiAgICBHaXRIdWJdKGh0dHBzOi8vZ2l0aHViLmNvbS93Y2gvcmdjb29rYm9vaykpCgpTb21lIGJsb2dzIHRvIGNoZWNrIG91dDoKCiAgKiBbSnVuayBDaGFydHNdKGh0dHA6Ly9qdW5rY2hhcnRzLnR5cGVwYWQuY29tLykKCiAgKiBbVGhlIEZ1bmN0aW9uYWwgQXJ0CiAgICBCbG9nXShodHRwOi8vd3d3LnRoZWZ1bmN0aW9uYWxhcnQuY29tLykKCiAgKiBbRmxvd2luZyBEYXRhXShodHRwczovL2Zsb3dpbmdkYXRhLmNvbS8pCgpLZWVwIGEgY3JpdGljYWwgZXllIG91dCBmb3IgZ29vZCAoYW5kIG5vdCBzbyBnb29kKSB1c2VzIG9mIGRhdGEKdmlzdWFsaXphdGlvbiBpbiB0aGUgbWVkaWEuCg==