Background

The Grammar of Graphics is a language proposed by Leland Wilkinson for describing statistical graphs.

Wilkinson, L. (2005), The Grammar of Graphics, 2nd ed., Springer.

The grammar of graphics has served as the foundation for the graphics frameworks in SPSS, Vega-Lite and several other systems.

ggplot2 represents an implementation and extension of the grammar of graphics for R.

Wickham, H. (2016), ggplot2: Elegant Graphics for Data Analysis, 2nd ed., Springer. 3rd ed. in progress.

On line documentation: https://ggplot2.tidyverse.org/reference/index.html.

Wickham. H., and Grolemund, G. (2016), R for Data Science, O’Reilly.

Data visualization cheatsheet

Winston Chang (2018), R Graphics Cookbook, 2nd edition, O’Reilly. (Book source on GitHub)

The idea is that any basic plot can be built out of a combination of

ggplot2 provides tools for specifying these components and adjusting their features.

Many components and features are provided by default and do not need to be specified explicitly unless the defaults are to be changed.

A Basic Template

The simplest graph needs a data set, a geom, and a mapping:

ggplot(data = <DATA>) + <GEOM>(mapping = aes(<MAPPINGS>))

The appearance of geom objects is controlled by aesthetic features.

Each geom has some required and some optional aesthetics.

For geom_point the required aesthetics are

Optional aesthetics include

geom_point is used to produce a scatter plot.

Scatter Plots Using geom_point

The mpg data set included in the ggpllot2 package includes EPA fuel economy data from 1999 to 2008 for 38 popular models of cars.

mpg
## # A tibble: 234 × 11
##    manufacturer model      displ  year   cyl trans drv     cty   hwy fl    class
##    <chr>        <chr>      <dbl> <int> <int> <chr> <chr> <int> <int> <chr> <chr>
##  1 audi         a4           1.8  1999     4 auto… f        18    29 p     comp…
##  2 audi         a4           1.8  1999     4 manu… f        21    29 p     comp…
##  3 audi         a4           2    2008     4 manu… f        20    31 p     comp…
##  4 audi         a4           2    2008     4 auto… f        21    30 p     comp…
##  5 audi         a4           2.8  1999     6 auto… f        16    26 p     comp…
##  6 audi         a4           2.8  1999     6 manu… f        18    26 p     comp…
##  7 audi         a4           3.1  2008     6 auto… f        18    27 p     comp…
##  8 audi         a4 quattro   1.8  1999     4 manu… 4        18    26 p     comp…
##  9 audi         a4 quattro   1.8  1999     4 auto… 4        16    25 p     comp…
## 10 audi         a4 quattro   2    2008     4 manu… 4        20    28 p     comp…
## # ℹ 224 more rows

A simple scatter plot:

ggplot(mpg) +
    geom_point(aes(x = displ,
                   y = hwy))

Map color to vehicle class:

ggplot(mpg) +
    geom_point(aes(x = displ,
                   y = hwy,
                   color = class))

And map shape to number of cylinders:

ggplot(mpg) +
    geom_point(aes(x = displ,
                   y = hwy,
                   color = class,
                   shape = factor(cyl)))

Perception: * Too many colors; * shapes are too small; * interference between shapes and colors.

Aesthetics can be mapped to a variable or set to a fixed common value.

This can be used to override default settings:

ggplot(mpg) +
    geom_point(aes(x = displ,
                   y = hwy),
               color = "blue",
               shape = 1)

Changing the size aesthetics makes shapes easier to recognize:

ggplot(mpg) +
    geom_point(aes(x = displ,
                   y = hwy,
                   color = class,
                   shape = factor(cyl)),
               size = 3)

Perception: Still too many colors; still have interference.

Available point shapes are specified by number:

Shapes 1-20 have their color set by the color aesthetic and ignore the fill aesthetic.

For shapes 21-25 the color aesthetic specifies the border color and fill specifies the interior color.

Using shape 21 with cyl mapped to the fill aesthetic:

ggplot(mutate(mpg, cyl = factor(cyl))) +
    geom_point(aes(x = displ,
                   y = hwy,
                   fill = cyl),
               shape = 21,
               size = 4)

Perception: Borders, larger symbols, fewer colors help.

Specifying a new default is very different from specifying a constant value as an aesthetic.

Constant aesthetic: Rarely what you want:

ggplot(mpg) +
    geom_point(aes(x = displ,
                   y = hwy,
                   color = "blue"))

Default: Probably what you want:

ggplot(mpg) +
    geom_point(aes(x = displ,
                   y = hwy),
               color = "blue")

Geometric Objects

ggplot2 provides a number of geoms:

geom_abline             geom_area               geom_bar                geom_bin_2d
geom_bin2d              geom_blank              geom_boxplot            geom_col
geom_contour            geom_contour_filled     geom_count              geom_crossbar
geom_curve              geom_density            geom_density_2d         geom_density_2d_filled
geom_density2d          geom_density2d_filled   geom_dotplot            geom_errorbar
geom_errorbarh          geom_freqpoly           geom_function           geom_hex
geom_histogram          geom_hline              geom_jitter             geom_label
geom_line               geom_linerange          geom_map                geom_path
geom_point              geom_pointrange         geom_polygon            geom_qq
geom_qq_line            geom_quantile           geom_raster             geom_rect
geom_ribbon             geom_rug                geom_segment            geom_sf
geom_sf_label           geom_sf_text            geom_smooth             geom_spoke
geom_step               geom_text               geom_tile               geom_violin
geom_vline                                                              

Additional geoms are available in packages like ggforce, ggridges, and others described on the ggplot2 extensions site.

Geoms can be added as layers to a plot.

Mappings common to all, or most, geoms can be specified in the ggplot call:

ggplot(mpg,
       aes(x = displ,
           y = hwy)) +
    geom_smooth() +
    geom_point()

Geoms can also use different data sets.

One way to highlight Europe in a plot of life expectancy against log income for 2007 is to start with a plot of the full data:

library(dplyr)
library(gapminder)
gm_2007 <- filter(gapminder, year == 2007)

(p <- ggplot(gm_2007, aes(x = gdpPercap,
                          y = lifeExp)) +
     geom_point() +
     scale_x_log10())

Then add a layer showing only Europe:

gm_2007_eu <- filter(gm_2007, continent == "Europe")

p + geom_point(data = gm_2007_eu,
               color = "red",
               size = 3)

Statistical Transformations

All geoms use a statistical transformation (stat) to convert raw data to the values to be mapped to the object’s features.

The available stats are

stat_align              stat_bin                stat_bin_2d
stat_bin_hex            stat_bin2d              stat_binhex
stat_boxplot            stat_contour            stat_contour_filled
stat_count              stat_density            stat_density_2d
stat_density_2d_filled  stat_density2d          stat_density2d_filled
stat_ecdf               stat_ellipse            stat_function
stat_identity           stat_qq                 stat_qq_line
stat_quantile           stat_sf                 stat_sf_coordinates
stat_smooth             stat_spoke              stat_sum
stat_summary            stat_summary_2d         stat_summary_bin
stat_summary_hex        stat_summary2d          stat_unique
stat_ydensity                                   

Each geom has a default stat, and each stat has a default geom.

Stats can provide computed variables that can be mapped to aesthetic features.

For stat_bin some of the computed variables are

The density variable can be accessed as after_stat(dentity).

Older approaches that also work but are now discouraged:

By default, geom_histogram uses y = after_stat(count).

ggplot(faithful) +
    geom_histogram(aes(x = eruptions),
                   binwidth = 0.25,
                   fill = "grey",
                   color = "black")

Explicitly specifying y = after_stat(count) produces the same plot:

ggplot(faithful) +
    geom_histogram(aes(x = eruptions,
                       y = after_stat(count)),
                   binwidth = 0.25,
                   fill = "grey",
                   color = "black")

Using y = after_stat(density) produces a density scaled axis.

(p <- ggplot(faithful) +
     geom_histogram(aes(x = eruptions,
                        y = after_stat(density)),
                    binwidth = 0.25,
                    fill = "grey",
                    color = "black"))

stat_function can be used to add a density curve specified as a mixture of two normal densities:

(ms <- mutate(faithful,
              type = ifelse(eruptions < 3,
                            "short",
                            "long")) %>%
     group_by(type) %>%
     summarize(mean = mean(eruptions),
               sd = sd(eruptions),
               n = n()) %>%
     mutate(p = n / sum(n)))
## # A tibble: 2 × 5
##   type   mean    sd     n     p
##   <chr> <dbl> <dbl> <int> <dbl>
## 1 long   4.29 0.411   175 0.643
## 2 short  2.04 0.267    97 0.357
f <- function(x)
    ms$p[1] * dnorm(x, ms$mean[1], ms$sd[1]) +
        ms$p[2] * dnorm(x, ms$mean[2], ms$sd[2])

p + stat_function(fun = f, color = "red")

Position Adjustments

The available position adjustments:

position_dodge        position_dodge2       position_fill
position_identity     position_jitter       position_jitterdodge
position_nudge        position_stack        

A bar chart showing the counts for the different cut categories in the diamonds data:

ggplot(diamonds, aes(x = cut)) +
    geom_bar()

Mapping clarity to fill shows the breakdown by both cut and clarity in a stacked bar chart:

ggplot(diamonds, aes(x = cut,
                     fill = clarity)) +
    geom_bar()

The default position for bar charts is position_stack:

ggplot(diamonds, aes(x = cut,
                     fill = clarity)) +
    geom_bar(position = "stack")

position_dodge produces side-by-side bar charts:

ggplot(diamonds, aes(x = cut,
                     fill = clarity)) +
    geom_bar(position = "dodge")

position_fill rescales all bars to be equal height to help compare proportions within bars.

ggplot(diamonds, aes(x = cut,
                     fill = clarity)) +
    geom_bar(position = "fill")

Using the counts to scale the widths would produce a spine plot, a variant of a mosaic plot.

This is easiest to do with the ggmosaic package.

position_jitter can be used with geom_point to avoid overplotting or break up rounding artifacts.

Another version of the Old Faithful data available as geyser in package MASS has some rounding in the duration variable:

data(geyser, package = "MASS")

## Adjust for different meaning of `waiting` variable
geyser2 <- na.omit(mutate(geyser,
                          duration = lag(duration)))

p <- ggplot(geyser2, aes(x = duration, y = waiting))
p + geom_point()

Jittering can help break up the distracting heaping of values on durations of 2 and 4 minutes.

The default amount of jittering isn’t quite enough in this case:

p + geom_point(position = "jitter")

To jitter only horizontally and by a larger amount you can use

p + geom_point(position =
                   position_jitter(height = 0,
                                   width = 0.1))

Coordinate Systems

Coordinate system functions include

coord_cartesian  coord_equal      coord_fixed      coord_flip
coord_map        coord_munch      coord_polar      coord_quickmap
coord_sf         coord_trans                       

The default coordinate system is coord_cartesian.

Cartesian Coordinates

coord_cartesian can be used to zoom in on a particular regiion:

p + geom_point() +
    coord_cartesian(xlim = c(3, 4))

coord_fixed and coord_equal fix the aspect ratio for a cartesian coordinate system.

The aspect ratio is the ratio of the number physical display units per y unit to the number of physical display units per x unit.

The aspect ratio can be important for recognizing features and patterns.

river <- scan("https://www.stat.uiowa.edu/~luke/data/river.dat")
r <- data.frame(flow = river, month = seq_along(river))
ggplot(r, aes(x = month, y = flow)) +
    geom_point() +
    coord_fixed(ratio = 4)

Polar Coordinates

A filled bar chart

(p <- ggplot(diamonds) +
     geom_bar(aes(x = 1, fill = cut),
              position = "fill"))

is turned into a pie chart by changing to polar coordinates:

p + coord_polar(theta = "y")

Coordinate Systems for Maps

Coordinate systems are particularly important for maps.

Polygons for many political and geographic boundaries are available through the map_data function.

Boundaries for the lower 48 US states can be obtained as

usa <- map_data("state")

Polygon vertices are encoded by longitude and latitude.

Plotting these in the default cartesian coordinate system usually does not work well:

usa <- map_data("state")
m <- ggplot(usa, aes(x = long,
                     y = lat,
                     group = group)) +
    geom_polygon(fill = "white",
                 color = "black")
m

Using a fixed aspect ratio is better, but an aspect ratio of 1 does not work well:

m + coord_equal()

The problem is that away from the equator a one degree change in latitude corresponds to a larger distance than a one degree change in longitude.

The ratio of one degree longitude separation to one degree latitude separation for the latitude at the middle of Iowa of 41 degrees is

longlat <- cos(41 / 90 * pi / 2)
longlat
## [1] 0.7547096

A better map is obtained using the aspect ratio 1 / longlat:

m + coord_fixed(1 / longlat)

The best approach is to use a coordinate system designed specifically for maps.

There are many projections used in map making.

The default projection used by coord_map is the Mercator projection.

m + coord_map()

Proper map projections are non-linear; this is easier to see with an Albers projection:

m + coord_map("albers", 20, 50)

Scales

Scales are used for controlling the mapping of values to physical representations such as colors, shapes, and positions.

Scale functions are also responsible for producing guides for translating physical representations back to values, such as

There are currently 131 scale functions; some examples are

scale_color_gradient      scale_shape_manual     scale_x_log10
scale_color_manual        scale_size_area        scale_y_log10
scale_fill_gradient                              scale_x_sqrt
scale_fill_manual                                scale_y_sqrt

An experimental tool to help choosing scales has recently been introduced.

Start with a basic scatter plot:

(p <- ggplot(mpg, aes(x = displ,
                      y = hwy)) +
     geom_point())

Remove the x tick marks and labels (this can also be done with theme settings):

p + scale_x_continuous(labels = NULL,
                       breaks = NULL)

Change the tick locations and labels:

p + scale_x_continuous(labels =
                           paste(c(2, 4, 6), "ltr"),
                       breaks = c(2, 4, 6))

Use a logarithmic axis:

p + scale_x_log10(labels = paste(c(2, 4, 6), "ltr"),
                  breaks = c(2, 4, 6),
                  minor_breaks = c(3, 5, 7))

The Scales section in R for Data Science provides some more details.

Color assignment can also be controlled by scale functions.

For example, for some presidential approval ratings data

pr_appr
##         pres appr party year
## 1      Obama   79     D 2009
## 2     Carter   78     D 1977
## 3    Clinton   68     D 1993
## 4  G.W. Bush   65     R 2001
## 5     Reagan   58     R 1981
## 6 G.H.W Bush   56     R 1989
## 7      Trump   40     R 2017

the default color scale is not ideal:

ggplot(pr_appr,
       aes(x = appr, y = pres, fill = party)) +
    geom_col()

The common assignment of red for Republican and blue for Democrat can be obtained by

ggplot(pr_appr,
       aes(x = appr, y = pres, fill = party)) +
    geom_col() +
    scale_fill_manual(values
                      = c(R = "red", D = "blue"))

A better choice is to use a well-designed color palette:

ggplot(pr_appr,
       aes(x = appr, y = pres, fill = party)) +
    geom_col() +
    colorspace::scale_fill_discrete_diverging(
                    palette = "Blue-Red 2")

Facets

Faceting uses the small multiples approach to introduce additional variables.

For a single variable facet_wrap is usually used:

p <- ggplot(mpg) +
    geom_point(aes(x = displ,
                   y = hwy))
p + facet_wrap(~ class)

For two variables, each with a modest number of categories, facet_grid can be effective:

p + facet_grid(factor(cyl) ~ drv)

To show common data in all facets make sure the data does not contain the faceting variable.

This was used to show muted views of the full data in faceted plots.

A faceted plot of the gapminder data:

library(gapminder)

years_to_keep <- c(1977, 1987, 1997, 2007)
gd <- filter(gapminder,
             year %in% years_to_keep)

ggplot(gd,
       aes(x = gdpPercap,
           y = lifeExp,
           color = continent)) +
    geom_point(size = 2.5) +
    scale_x_log10() +
    facet_wrap(~ year)

Add a muted version of the full data in the background of each panel:

library(gapminder)

years_to_keep <- c(1977, 1987, 1997, 2007)
gd <- filter(gapminder,
             year %in% years_to_keep)
gd_no_year <- mutate(gd, year = NULL)

ggplot(gd,
       aes(x = gdpPercap,
           y = lifeExp,
           color = continent)) +
    geom_point(data = gd_no_year,
               color = "grey80") +
    geom_point(size = 2.5) +
    scale_x_log10() +
    facet_wrap(~ year)

Usually facets use common axis scales, but one or both can be allowed to vary.

A useful approach for showing time series data with a good aspect ratio can be to split the data into facets for non-overlapping portions of the time axis.

pd <- rep(paste(seq(1, by = 32, length.out = 4),
                seq(32, by = 32, length.out = 4),
                sep = " - "),
         each =  32)
rd <- data.frame(month = seq_along(river),
                 flow = river,
                 panel = pd)
ggplot(rd, aes(x = month,
               y = flow)) +
    geom_point() +
    facet_wrap(~ panel,
               scale = "free_x",
               ncol = 1)

Facet arrangement can also be used to convey other information, such as geographic location.

The geofacet package allows facets to be placed in approximate locations of different geographic regions.

An example for data from US states:

library(geofacet)
ggplot(state_unemp, aes(year, rate)) +
    geom_line() +
    facet_geo(~ state,
              grid = "us_state_grid2",
              label = "code") +
    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 = element_text(size = 5))

Arrangement according to a calendar can also be useful.

Themes

ggplot2 supports the notion of themes for adjusting non-data appearance aspects of a plot, such as

Theme elements can be customized in several ways:

The full documentation of the theme function lists many customizable elements.

One simple example:

ggplot(mutate(mpg, cyl = factor(cyl))) +
    geom_point(aes(x = displ,
                   y = hwy,
                   fill = cyl),
               shape = 21,
               size = 3) +
    theme(legend.position = "top",
          axis.text = element_text(size = 12),
          axis.title = element_text(size = 14,
                                    face = "bold"))

Another example:

gthm <-
    theme(plot.background =
              element_rect(fill = "lightblue",
                           color = NA),
          panel.background =
              element_rect(fill = "pink"))
p + gthm

Some alternate complete themes provided by ggplot2 are

theme_bw        theme_gray      theme_minimal   theme_void
theme_classic   theme_grey      theme_dark      theme_light

Some examples:

p_bw <- p + theme_bw() + ggtitle("BW")

p_classic <- p + theme_classic() + ggtitle("Classic")

p_min <- p + theme_minimal() + ggtitle("Minimal")

p_void <- p + theme_void() + ggtitle("Void")

library(patchwork)
(p_bw + p_classic) / (p_min + p_void)

The ggthemes package provides some additional themes.

Some examples:

library(ggthemes)

p_econ <- p + theme_economist() + ggtitle("Economist")

p_wsj <- p + theme_wsj() + ggtitle("WSJ")

p_tufte <- p + theme_tufte() + ggtitle("Tufte")

p_few <- p + theme_few() + ggtitle("Few")

(p_econ + p_wsj) / (p_tufte + p_few)

ggthemes also provides theme_map that removes unnecessary elements from maps:

m + coord_map() + theme_map()

The Themes section in R for Data Science provides some more details.

A More Complete Template

ggplot(data = <DATA>) +
    <GEOM>(mapping = aes(<MAPPINGS>),
           stat = <STAT>,
           position = <POSITION>) +
    < ... MORE GEOMS ... > +
    <COORDINATE_ADJUSTMENT> +
    <SCALE_ADJUSTMENT> +
    <FACETING> +
    <THEME_ADJUSTMENT>

Labels and Annotations

A basic plot:

p <- ggplot(mpg, aes(x = displ,
                     y = hwy))
p1 <- p + geom_point(aes(color = factor(cyl)),
                     size = 2.5)
p1

Axis labels are based on the expressions given to aes.

This is convenient for exploration but usually not ideal for a report.

The labs() function can be used to change axis and legend labels:

p1 + labs(x = "Displacement (Liters)",
          y = "Highway Miles Per Gallon",
          color = "Cylinders")

The labs() function can also add a title, subtitle, and caption:

p2 <- p1 +
    labs(x = "Displacement (Liters)",
         y = "Highway Miles Per Gallon",
         color = "Cylinders",
         title = "Gas Mileage and Displacement",
         subtitle = paste("For models which had a new release every year",
                           "between 1999 and 2008"),
         caption = "Data Source: https://fueleconomy.gov/")
p2

Annotations can be used to provide popout that draws a viewer’s attention to particular features.

The annotate() function is one option:

p2 +
    annotate("label", x = 2.8, y = 43,
             label = "Volkswagens") +
    annotate("rect",
             xmin = 1.7, xmax = 2.1,
             ymin = 40, ymax = 45,
             fill = NA, color = "black")

Often more convenient are some geom_mark objects provided by the ggforce package:

library(ggforce)
p2 +
    geom_mark_hull(aes(filter = class == "2seater"),
                   description =
                       paste("2-Seaters have high displacement",
                             "values, but also high fuel efficiency",
                             "for their displacement.")) +
    geom_mark_rect(aes(filter = hwy > 40),
                   description =
                       "These are Volkswagens") +
    geom_mark_circle(aes(filter = hwy == 12),
                     description =
                         "Three pickups and an SUV.")

These annotations can be customized in a number of ways.

Arranging Plots

There are several tools available for assembling ensemble plots.

The patchwork package is a good choice.

A simple example:

p1 <- ggplot(mpg, aes(x = displ,
                      y = hwy)) +
    geom_point()
p2 <- ggplot(mpg, aes(x = cyl,
                      y = hwy,
                      group = cyl)) +
    geom_boxplot()
p3 <- ggplot(mpg, aes(x = cyl)) +
    geom_bar()

library(patchwork)
(p1 + p2) / p3

Interaction

Plotly

The ggplotly function in the plotly package can be used to add some interactive features to a plot created with ggplot2.

  • In an R session a call to ggplotly() opens a browser window with the interactive plot.

  • In an Rmarkdown document the interactive plot is embedded in the html file.

Another interactive plotting approach that can be used from R is described in an Infoworld article.

A simple example using ggplotly():

library(ggplot2)
library(plotly)
p <- ggplot(mutate(mpg, cyl = factor(cyl))) +
    geom_point(aes(x = displ,
                   y = hwy,
                   fill = cyl),
               shape = 21,
               size = 3)
ggplotly(p)

Adding a text aesthetic allows the tooltip display to be customized:

p <- ggplot(mutate(mpg, cyl = factor(cyl))) +
    geom_point(aes(x = displ,
                   y = hwy,
                   fill = cyl,
                   text = paste(year,
                                manufacturer,
                                model)),
               shape = 21,
               size = 3)
ggplotly(p, tooltip = "text") %>%
    style(hoverlabel = list(bgcolor = "white"))

Ggiraph

The ggiraph package provides another approach.

library(ggplot2)
library(ggiraph)
p <- ggplot(mutate(mpg, cyl = factor(cyl))) +
    geom_point_interactive(aes(x = displ,
                   y = hwy,
                   fill = cyl,
                   tooltip = paste(year,
                                   manufacturer,
                                   model)),
               shape = 21,
               size = 3)
girafe(ggobj = p)

Grammar of Interactive Graphics

There have been several efforts to develop a grammar of interactive graphics, including ggvis and animint; neither seems to be under active development at this time.

A promising approach is Vega-Lite, with a Python interface Altair and an R interface altair to the Python interface.

An example using the altair package:

rub <- read.csv(here::here("rubber.csv"))

library(altair)

chartTH <- alt$Chart(rub)$
    mark_point()$
    encode(x = alt$X("H:Q", scale = alt$Scale(domain = range(rub$H))),
           y = alt$Y("T:Q", scale = alt$Scale(domain = range(rub$T))))

brush <- alt$selection_interval()

chartTH_brush <- chartTH$add_selection(brush)

chartTH_selection <-
    chartTH_brush$encode(color = alt$condition(brush,
                                               "Origin:N",
                                               alt$value("lightgray")))

chartAT <- chartTH_selection$
    encode(x = alt$X("T:Q", scale = alt$Scale(domain = range(rub$T))),
           y = alt$Y("A:Q", scale = alt$Scale(domain = range(rub$A))))

chartAT | chartTH_selection

The resulting linked plots:

Notes

Reading

Chapters Data visualization and Graphics for communication in R for Data Science, O’Reilly.

Chapter Make a plot in Data Visualization.

Chapter ggplot2 in Introduction to Data Science Data Analysis and Prediction Algorithms with R.

Interactive Tutorial

An interactive learnr tutorial for these notes is available.

You can run the tutorial with

STAT4580::runTutorial("ggplot")

You can install the current version of the STAT4580 package with

remotes::install_gitlab("luke-tierney/STAT4580")

You may need to install the remotes package from CRAN first.

Exercises

  1. In the following expression, which value of the shape aesthetic produces a plot with points represented as triangles outlined in black colored according to the number of cylinders?
```r
library(ggplot2)
ggplot(mpg, aes(x = displ, y = hwy, fill = factor(cyl))) +
    geom_point(size = 4, shape = ---)
```
a. 15
b. 17
c. 21
d. 24
  1. It can sometimes be useful to plot text labels in a scatterplot instead of points. Consider the plot set up as

    library(ggplot2)
    library(dplyr)
    data(gapminder, package = "gapminder")
    p <- filter(gapminder, year == 2007) %>%
        group_by(continent) %>%
        summarize(gdpPercap = mean(gdpPercap), lifeExp = mean(lifeExp)) %>%
        ggplot(aes(x = gdpPercap, y = lifeExp))

    Which of the following produces a plot with continent names on white rectangles?

    1. p + geom_text(aes(label = continent))
    2. p + geom_label(aes(label = continent))
    3. p + geom_label(label = continent)
    4. p + geom_text(text = continent)
  2. The following code plots a kernel density estimate for the eruptions variable in the faithful data set:

    library(ggplot2)
    ggplot(faithful, aes(x = eruptions)) + geom_density(bw = 0.1)

    Look at the help page for geom_density. Which of the following best describes what specifying a value for bw does:

    1. Changes the kernel used to construct the estimate.
    2. Changes the smoothing bandwidth to make the result more or less smooth.
    3. Changes the stat used to stat_bw.
    4. Has no effect on the retult.
  3. This code creates a map of Iowa counties.

    library(ggplot2)
    p <- ggplot(map_data("county", "iowa"),
                aes(x = long, y = lat, group = group)) +
        geom_polygon(, fill = "White", color = "black")

    Which of these produces a plot with an aspect ratio that best matches the map on this page?

    1. p
    2. p + coord_fixed(0.75)
    3. p + coord_fixed(1.25)
    4. p + coord_fixed(2)
  4. Consider the two plots created by this code (print the values of p1 and p2 to see the plots):

    library(ggplot2)
    data(gapminder, package = "gapminder")
    p1 <- ggplot(gapminder, aes(x = log(gdpPercap), y = lifeExp)) +
        geom_point() +
        scale_x_continuous(name = "")
    p2 <- ggplot(gapminder, aes(x = gdpPercap, y = lifeExp)) +
        geom_point() +
        scale_x_log10(labels = scales::comma, name = "") 

    Which of these statements is true?

    1. The x axis labels are identical in both plots.
    2. The x axis labels in p2 are in dollars; the labels in p1 are in log dollars.
    3. The x axis labels in p1 are in dollars; the labels in p2 are in log dollars.
    4. There are no labels on the x axis in p2.
  5. Consider the plot created by

    library(ggplot2)
    data(gapminder, package = "gapminder")
    p <- ggplot(gapminder, aes(x = gdpPercap, y = lifeExp)) +
        geom_point() +
        scale_x_log10(labels = scales::comma) 

    Which of these expressions produces a plot with a white background?

    1. p
    2. p + theme_grey()
    3. p + theme_classic()
    4. p + ggthemes::theme_economist()
  6. There are many different ways to change the x axis label in ggplot. Consider the plot created by

    library(ggplot2)
    p <- ggplot(mpg, aes(x = displ, y = hwy)) +
        geom_point()

    Which of the following does not change the x axis label to Displacement?

    1. p + labs(x = "Displacement")
    2. p + scale_x_continuous("Displacement")
    3. p + xlab("Displacement")
    4. p + theme(axis.title.x = "Displacement")
LS0tCnRpdGxlOiAiVGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgo8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9InN0YXQ0NTgwLmNzcyIgdHlwZT0idGV4dC9jc3MiIC8+CgpgYGB7ciBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQpzb3VyY2UoaGVyZTo6aGVyZSgic2V0dXAuUiIpKQprbml0cjo6b3B0c19jaHVuayRzZXQoY29sbGFwc2UgPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuaGVpZ2h0ID0gNSwgZmlnLndpZHRoID0gNiwgZmlnLmFsaWduID0gImNlbnRlciIpCgpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkobGF0dGljZSkKbGlicmFyeShncmlkRXh0cmEpCnNldC5zZWVkKDEyMzQ1KQpgYGAKCgojIyBCYWNrZ3JvdW5kCgpUaGUgX0dyYW1tYXIgb2YgR3JhcGhpY3NfIGlzIGEgbGFuZ3VhZ2UgcHJvcG9zZWQgYnkgTGVsYW5kIFdpbGtpbnNvbgpmb3IgZGVzY3JpYmluZyBzdGF0aXN0aWNhbCBncmFwaHMuCgo+IFdpbGtpbnNvbiwgTC4gKDIwMDUpLCBfVGhlIEdyYW1tYXIgb2YgR3JhcGhpY3NfLCAybmQgZWQuLCBTcHJpbmdlci4KClRoZSBncmFtbWFyIG9mIGdyYXBoaWNzIGhhcyBzZXJ2ZWQgYXMgdGhlIGZvdW5kYXRpb24gZm9yIHRoZSBncmFwaGljcwpmcmFtZXdvcmtzIGluIFtTUFNTXShodHRwczovL3d3dy5pYm0uY29tL3Byb2R1Y3RzL3Nwc3Mtc3RhdGlzdGljcyksCltWZWdhLUxpdGVdKGh0dHBzOi8vdmVnYS5naXRodWIuaW8vdmVnYS1saXRlLykgYW5kIHNldmVyYWwgb3RoZXIKc3lzdGVtcy4KCmBnZ3Bsb3QyYCByZXByZXNlbnRzIGFuIGltcGxlbWVudGF0aW9uIGFuZCBleHRlbnNpb24gb2YgdGhlIGdyYW1tYXIKb2YgZ3JhcGhpY3MgZm9yIFIuCgo+IFdpY2toYW0sIEguICgyMDE2KSwgX2dncGxvdDI6IEVsZWdhbnQgR3JhcGhpY3MgZm9yIERhdGEgQW5hbHlzaXNfLAo+ICAybmQgZWQuLCBTcHJpbmdlci4gWzNyZCBlZC4gaW4gcHJvZ3Jlc3NdKGh0dHBzOi8vZ2dwbG90Mi1ib29rLm9yZy8pLgoKPiBPbiBsaW5lIGRvY3VtZW50YXRpb246IDxodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvaW5kZXguaHRtbD4uCgo+IFdpY2toYW0uIEguLCBhbmQgR3JvbGVtdW5kLCBHLiAoMjAxNiksCj4gIFtfUiBmb3IgRGF0YSBTY2llbmNlX10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei8pLCBPJ1JlaWxseS4KCj4gW0RhdGEgdmlzdWFsaXphdGlvbiBjaGVhdHNoZWV0XShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcnN0dWRpby9jaGVhdHNoZWV0cy9tYWluL2RhdGEtdmlzdWFsaXphdGlvbi5wZGYpCgo+IFdpbnN0b24gQ2hhbmcgKDIwMTgpLCBbX1IgR3JhcGhpY3MgQ29va2Jvb2tfLCAybmQKPiBlZGl0aW9uXShodHRwczovL3ItZ3JhcGhpY3Mub3JnLyksIE/igJlSZWlsbHkuIChbQm9vayBzb3VyY2Ugb24KPiBHaXRIdWJdKGh0dHBzOi8vZ2l0aHViLmNvbS93Y2gvcmdjb29rYm9vaykpCgpUaGUgaWRlYSBpcyB0aGF0IGFueSBiYXNpYyBwbG90IGNhbiBiZSBidWlsdCBvdXQgb2YgYSBjb21iaW5hdGlvbiBvZgoKKiBhIGRhdGEgc2V0OwoKKiBvbmUgb3IgbW9yZSBnZW9tZXRyaWNhbCByZXByZXNlbnRhdGlvbiAoX2dlb21zXyk7CgoqIG1hcHBpbmdzIG9mIHZhbHVlcyB0byBfYWVzdGhldGljXyBmZWF0dXJlcyBvZiB0aGUgZ2VvbTsKCiogYSBfc3RhdF8gdG8gcHJvZHVjZSB2YWx1ZXMgdG8gYmUgbWFwcGVkOwoKKiBwb3NpdGlvbiBhZGp1c3RtZW50czsKCiogYSBjb29yZGluYXRlIHN5c3RlbTsKCiogYSBzY2FsZSBzcGVjaWZpY2F0aW9uOwoKKiBhIGZhY2V0aW5nIHNjaGVtZS4KCmBnZ3Bsb3QyYCBwcm92aWRlcyB0b29scyBmb3Igc3BlY2lmeWluZyB0aGVzZSBjb21wb25lbnRzIGFuZCBhZGp1c3RpbmcKdGhlaXIgZmVhdHVyZXMuCgpNYW55IGNvbXBvbmVudHMgYW5kIGZlYXR1cmVzIGFyZSBwcm92aWRlZCBieSBkZWZhdWx0IGFuZCBkbyBub3QgbmVlZAp0byBiZSBzcGVjaWZpZWQgZXhwbGljaXRseSB1bmxlc3MgdGhlIGRlZmF1bHRzIGFyZSB0byBiZSBjaGFuZ2VkLgoKCiMjIEEgQmFzaWMgVGVtcGxhdGUKClRoZSBzaW1wbGVzdCBncmFwaCBuZWVkcyBhIGRhdGEgc2V0LCBhIGdlb20sIGFuZCBhIG1hcHBpbmc6CgpgYGByCmdncGxvdChkYXRhID0gPERBVEE+KSArIDxHRU9NPihtYXBwaW5nID0gYWVzKDxNQVBQSU5HUz4pKQpgYGAKClRoZSBhcHBlYXJhbmNlIG9mIGdlb20gb2JqZWN0cyBpcyBjb250cm9sbGVkIGJ5IF9hZXN0aGV0aWNfIGZlYXR1cmVzLgoKRWFjaCBnZW9tIGhhcyBzb21lIHJlcXVpcmVkIGFuZCBzb21lIG9wdGlvbmFsIGFlc3RoZXRpY3MuCgpGb3IgYGdlb21fcG9pbnRgIHRoZSByZXF1aXJlZCBhZXN0aGV0aWNzIGFyZQoKKiBgeGAgcG9zaXRpb24KKiBgeWAgcG9zaXRpb24uCgpPcHRpb25hbCBhZXN0aGV0aWNzIGluY2x1ZGUKCiogYGNvbG9yYAoqIGBmaWxsYAoqIGBzaGFwZWAKKiBgc2l6ZWAKCmBnZW9tX3BvaW50YCBpcyB1c2VkIHRvIHByb2R1Y2UgYSBfc2NhdHRlciBwbG90Xy4KCgojIyBTY2F0dGVyIFBsb3RzIFVzaW5nIGBnZW9tX3BvaW50YAoKVGhlIGBtcGdgIGRhdGEgc2V0IGluY2x1ZGVkIGluIHRoZSBgZ2dwbGxvdDJgIHBhY2thZ2UgaW5jbHVkZXMgRVBBCmZ1ZWwgZWNvbm9teSBkYXRhIGZyb20gMTk5OSB0byAyMDA4IGZvciAzOCBwb3B1bGFyIG1vZGVscyBvZiBjYXJzLgoKYGBge3J9Cm1wZwpgYGAKCkEgc2ltcGxlIHNjYXR0ZXIgcGxvdDoKCmBgYHtyIG1wZy1wbGFpbiwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QobXBnKSArCiAgICBnZW9tX3BvaW50KGFlcyh4ID0gZGlzcGwsCiAgICAgICAgICAgICAgICAgICB5ID0gaHd5KSkKYGBgCgpgYGB7ciBtcGctcGxhaW4sIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gNS43NSwgZmlnLmFsaWduID0gImNlbnRlciJ9CmBgYAoKTWFwIGNvbG9yIHRvIHZlaGljbGUgY2xhc3M6CgpgYGB7ciBtcGctY29sb3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KG1wZykgKwogICAgZ2VvbV9wb2ludChhZXMoeCA9IGRpc3BsLAogICAgICAgICAgICAgICAgICAgeSA9IGh3eSwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY2xhc3MpKQpgYGAKCmBgYHtyIG1wZy1jb2xvciwgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSA3LCBmaWcuYWxpZ24gPSAiY2VudGVyIn0KYGBgCgpBbmQgbWFwIHNoYXBlIHRvIG51bWJlciBvZiBjeWxpbmRlcnM6CgpgYGB7ciBtcGctY29sb3Itc2hhcGUsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KG1wZykgKwogICAgZ2VvbV9wb2ludChhZXMoeCA9IGRpc3BsLAogICAgICAgICAgICAgICAgICAgeSA9IGh3eSwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY2xhc3MsCiAgICAgICAgICAgICAgICAgICBzaGFwZSA9IGZhY3RvcihjeWwpKSkKYGBgCgpgYGB7ciBtcGctY29sb3Itc2hhcGUsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gNywgZmlnLmFsaWduID0gImNlbnRlciJ9CmBgYAoKPCEtLSAtLT4gUGVyY2VwdGlvbjoKKiBUb28gbWFueSBjb2xvcnM7IAoqIHNoYXBlcyBhcmUgdG9vIHNtYWxsOyAKKiBpbnRlcmZlcmVuY2UgYmV0d2VlbiBzaGFwZXMgYW5kIGNvbG9ycy4KCkFlc3RoZXRpY3MgY2FuIGJlIG1hcHBlZCB0byBhIHZhcmlhYmxlIG9yIHNldCB0byBhIGZpeGVkIGNvbW1vbiB2YWx1ZS4KClRoaXMgY2FuIGJlIHVzZWQgdG8gb3ZlcnJpZGUgZGVmYXVsdCBzZXR0aW5nczoKCmBgYHtyIG1wZy1maXhlZCwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QobXBnKSArCiAgICBnZW9tX3BvaW50KGFlcyh4ID0gZGlzcGwsCiAgICAgICAgICAgICAgICAgICB5ID0gaHd5KSwKICAgICAgICAgICAgICAgY29sb3IgPSAiYmx1ZSIsCiAgICAgICAgICAgICAgIHNoYXBlID0gMSkKYGBgCgpgYGB7ciBtcGctZml4ZWQsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gNywgZmlnLmFsaWduID0gImNlbnRlciJ9CmBgYAoKQ2hhbmdpbmcgdGhlIGBzaXplYCBhZXN0aGV0aWNzIG1ha2VzIHNoYXBlcyBlYXNpZXIgdG8gcmVjb2duaXplOgoKYGBge3IgbXBnLWNvbG9yLXNoYXBlLWxhcmdlLCBldmFsID0gRkFMU0V9CmdncGxvdChtcGcpICsKICAgIGdlb21fcG9pbnQoYWVzKHggPSBkaXNwbCwKICAgICAgICAgICAgICAgICAgIHkgPSBod3ksCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IGNsYXNzLAogICAgICAgICAgICAgICAgICAgc2hhcGUgPSBmYWN0b3IoY3lsKSksCiAgICAgICAgICAgICAgIHNpemUgPSAzKQpgYGAKCmBgYHtyIG1wZy1jb2xvci1zaGFwZS1sYXJnZSwgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSA3LCBmaWcuYWxpZ24gPSAiY2VudGVyIn0KYGBgCgo8IS0tIC0tPiBQZXJjZXB0aW9uOiBTdGlsbCB0b28gbWFueSBjb2xvcnM7IHN0aWxsIGhhdmUgaW50ZXJmZXJlbmNlLgoKQXZhaWxhYmxlIHBvaW50IHNoYXBlcyBhcmUgc3BlY2lmaWVkIGJ5IG51bWJlcjoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIGV2YWwgPSBGQUxTRX0KZ2VuZXJhdGVSUG9pbnRTaGFwZXMgPC0gZnVuY3Rpb24oKSB7CiAgICBvbGRQYXIgPC0gcGFyKCkKICAgIHBhcihmb250ID0gMiwgbWFyID0gYygwLjUsIDAsIDAsIDApKQogICAgeSA8LSByZXYoYyhyZXAoMSwgNiksIHJlcCgyLCA1KSwgcmVwKDMsIDUpLCByZXAoNCwgNSksIHJlcCg1LCA1KSkpCiAgICB4IDwtIGMocmVwKDEgOiA1LCA1KSwgNikKICAgIHBsb3QoeCwgeSwgcGNoID0gMCA6IDI1LCBjZXggPSAxLjUsIHlsaW0gPSBjKDEsIDUuNSksIHhsaW0gPSBjKDEsIDYuNSksCiAgICAgICAgIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIsIGJnID0gImJsdWUiKQogICAgdGV4dCh4LCB5LCBsYWJlbHMgPSAwIDogMjUsIHBvcyA9IDMpCiAgICBwYXIobWFyID0gb2xkUGFyJG1hciwgZm9udCA9IG9sZFBhciRmb250KQp9CmdlbmVyYXRlUlBvaW50U2hhcGVzKCkKYGBgCmBgYHtyLCBlY2hvID0gRkFMU0V9CmdncGxvdChOVUxMLCBhZXMoeCA9IHJlcCgxIDogNSwgNSksIHkgPSByZXYocmVwKDEgOiA1LCBlYWNoID0gNSkpKSkgKwogICAgZ2VvbV9wb2ludChzaGFwZSA9IDEgOiAyNSwgc2l6ZSA9IDUsIGZpbGwgPSAiYmx1ZSIpICsKICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSAxIDogMjUpLCBudWRnZV95ID0gMC4yNSwgc2l6ZSA9IDYpICsKICAgIHRoZW1lX3ZvaWQoKQpgYGAKClNoYXBlcyAxLTIwIGhhdmUgdGhlaXIgY29sb3Igc2V0IGJ5IHRoZSBgY29sb3JgIGFlc3RoZXRpYyBhbmQgaWdub3JlCnRoZSBgZmlsbGAgYWVzdGhldGljLgoKRm9yIHNoYXBlcyAyMS0yNSB0aGUgYGNvbG9yYCBhZXN0aGV0aWMgc3BlY2lmaWVzIHRoZSBib3JkZXIgY29sb3IgYW5kCmBmaWxsYCBzcGVjaWZpZXMgdGhlIGludGVyaW9yIGNvbG9yLgoKVXNpbmcgYHNoYXBlYCAyMSB3aXRoIGBjeWxgIG1hcHBlZCB0byB0aGUgYGZpbGxgIGFlc3RoZXRpYzoKCmBgYHtyIG1wZy1maWxsLTIxLCBldmFsID0gRkFMU0V9CmdncGxvdChtdXRhdGUobXBnLCBjeWwgPSBmYWN0b3IoY3lsKSkpICsKICAgIGdlb21fcG9pbnQoYWVzKHggPSBkaXNwbCwKICAgICAgICAgICAgICAgICAgIHkgPSBod3ksCiAgICAgICAgICAgICAgICAgICBmaWxsID0gY3lsKSwKICAgICAgICAgICAgICAgc2hhcGUgPSAyMSwKICAgICAgICAgICAgICAgc2l6ZSA9IDQpCmBgYApgYGB7ciBtcGctZmlsbC0yMSwgZWNobyA9IEZBTFNFfQpgYGAKCjwhLS0gLS0+IFBlcmNlcHRpb246IEJvcmRlcnMsIGxhcmdlciBzeW1ib2xzLCBmZXdlciBjb2xvcnMgaGVscC4KClNwZWNpZnlpbmcgYSBuZXcgZGVmYXVsdCBpcyB2ZXJ5IGRpZmZlcmVudCBmcm9tIHNwZWNpZnlpbmcgYSBjb25zdGFudAp2YWx1ZSBhcyBhbiBhZXN0aGV0aWMuCgpDb25zdGFudCBhZXN0aGV0aWM6IFJhcmVseSB3aGF0IHlvdSB3YW50OgoKYGBge3IgbXBnLWJhZC1jb2xvciwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QobXBnKSArCiAgICBnZW9tX3BvaW50KGFlcyh4ID0gZGlzcGwsCiAgICAgICAgICAgICAgICAgICB5ID0gaHd5LAogICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmx1ZSIpKQpgYGAKCmBgYHtyIG1wZy1iYWQtY29sb3IsIGVjaG8gPSBGQUxTRSwgZmlnLmhlaWdodCA9IDQuMn0KYGBgCgpEZWZhdWx0OiBQcm9iYWJseSB3aGF0IHlvdSB3YW50OgpgYGB7ciBtcGctZ29vZC1jb2xvciwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QobXBnKSArCiAgICBnZW9tX3BvaW50KGFlcyh4ID0gZGlzcGwsCiAgICAgICAgICAgICAgICAgICB5ID0gaHd5KSwKICAgICAgICAgICAgICAgY29sb3IgPSAiYmx1ZSIpCmBgYAoKYGBge3IgbXBnLWdvb2QtY29sb3IsIGVjaG8gPSBGQUxTRSwgZmlnLmhlaWdodCA9IDQuMn0KYGBgCgoKIyMgR2VvbWV0cmljIE9iamVjdHMKCmBnZ3Bsb3QyYCBwcm92aWRlcyBhIG51bWJlciBvZiBnZW9tczoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIHJlc3VsdHMgPSAiYXNpcyJ9CnNob3dMaXN0IDwtIGZ1bmN0aW9uKHYsIG5jb2wgPSA0LCBwYWQgPSAyKSB7CiAgICB3IDwtIG1heChuY2hhcih2KSkgKyBwYWQKICAgIG5yb3cgPC0gY2VpbGluZyhsZW5ndGgodikgLyBuY29sKQogICAgdiA8LSBjKHYsIGNoYXJhY3RlcihuY29sICogbnJvdyAtIGxlbmd0aCh2KSkpCgogICAgY2F0KCJgYGByXG4iKQogICAgZm9yIChpIGluIHNlcV9sZW4obnJvdykpIHsKICAgICAgICBsaW5lIDwtIHZbbmNvbCAqIChpIC0gMSkgKyAoMSA6IG5jb2wpXQogICAgICAgIGZvciAoaiBpbiAxIDogbmNvbCkKICAgICAgICAgICAgaWYgKGogPCBuY29sKQogICAgICAgICAgICAgICAgY2F0KHNwcmludGYoIiUtKnMiLCB3LCBsaW5lW2pdKSkKICAgICAgICAgICAgZWxzZQogICAgICAgICAgICAgICAgY2F0KHNwcmludGYoIiVzXG4iLCBsaW5lW2pdKSkKICAgICAgICAjIyBjYXQoc3ByaW50ZigiJS0qcyUtKnMlLSpzJXNcbiIsCiAgICAgICAgIyMgICAgICAgICAgICAgdywgbGluZVsxXSwgdywgbGluZVsyXSwgdywgbGluZVszXSwgbGluZVs0XSkpCiAgICB9CiAgICBjYXQoImBgYFxuIikKfQpzaG93TGlzdChscygicGFja2FnZTpnZ3Bsb3QyIiwgcGF0ID0gIl5nZW9tXyIpKQpgYGAKCkFkZGl0aW9uYWwgZ2VvbXMgYXJlIGF2YWlsYWJsZSBpbiBwYWNrYWdlcyBsaWtlIGBnZ2ZvcmNlYCwgYGdncmlkZ2VzYCwKYW5kIG90aGVycyBkZXNjcmliZWQgb24gdGhlIFtgZ2dwbG90MmAgZXh0ZW5zaW9ucwpzaXRlXShodHRwczovL2V4dHMuZ2dwbG90Mi50aWR5dmVyc2Uub3JnLykuCgpHZW9tcyBjYW4gYmUgYWRkZWQgYXMgX2xheWVyc18gdG8gYSBwbG90LgoKTWFwcGluZ3MgY29tbW9uIHRvIGFsbCwgb3IgbW9zdCwgZ2VvbXMgY2FuIGJlIHNwZWNpZmllZCBpbiB0aGUKYGdncGxvdGAgY2FsbDoKCmBgYHtyIG1wZy1zbW9vdGgsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KG1wZywKICAgICAgIGFlcyh4ID0gZGlzcGwsCiAgICAgICAgICAgeSA9IGh3eSkpICsKICAgIGdlb21fc21vb3RoKCkgKwogICAgZ2VvbV9wb2ludCgpCmBgYAoKYGBge3IgbXBnLXNtb290aCwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmBgYAoKR2VvbXMgY2FuIGFsc28gdXNlIGRpZmZlcmVudCBkYXRhIHNldHMuCgpPbmUgd2F5IHRvIGhpZ2hsaWdodCBFdXJvcGUgaW4gYSBwbG90IG9mIGxpZmUgZXhwZWN0YW5jeSBhZ2FpbnN0IGxvZwppbmNvbWUgZm9yIDIwMDcgaXMgdG8gc3RhcnQgd2l0aCBhIHBsb3Qgb2YgdGhlIGZ1bGwgZGF0YToKCmBgYHtyIGdtXzIwMDcsIGV2YWwgPSBGQUxTRX0KbGlicmFyeShkcGx5cikKbGlicmFyeShnYXBtaW5kZXIpCmdtXzIwMDcgPC0gZmlsdGVyKGdhcG1pbmRlciwgeWVhciA9PSAyMDA3KQoKKHAgPC0gZ2dwbG90KGdtXzIwMDcsIGFlcyh4ID0gZ2RwUGVyY2FwLAogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBsaWZlRXhwKSkgKwogICAgIGdlb21fcG9pbnQoKSArCiAgICAgc2NhbGVfeF9sb2cxMCgpKQpgYGAKCmBgYHtyIGdtXzIwMDcsIGVjaG8gPSBGQUxTRX0KYGBgCgpUaGVuIGFkZCBhIGxheWVyIHNob3dpbmcgb25seSBFdXJvcGU6CgpgYGB7ciBnbV8yMDA3X2V1LCBldmFsID0gRkFMU0V9CmdtXzIwMDdfZXUgPC0gZmlsdGVyKGdtXzIwMDcsIGNvbnRpbmVudCA9PSAiRXVyb3BlIikKCnAgKyBnZW9tX3BvaW50KGRhdGEgPSBnbV8yMDA3X2V1LAogICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLAogICAgICAgICAgICAgICBzaXplID0gMykKYGBgCgpgYGB7ciBnbV8yMDA3X2V1LCBlY2hvID0gRkFMU0V9CmBgYAoKCiMjIFN0YXRpc3RpY2FsIFRyYW5zZm9ybWF0aW9ucwoKQWxsIGdlb21zIHVzZSBhIHN0YXRpc3RpY2FsIHRyYW5zZm9ybWF0aW9uIChfc3RhdF8pIHRvIGNvbnZlcnQgcmF3CmRhdGEgdG8gdGhlIHZhbHVlcyB0byBiZSBtYXBwZWQgdG8gdGhlIG9iamVjdCdzIGZlYXR1cmVzLgoKVGhlIGF2YWlsYWJsZSBzdGF0cyBhcmUKCmBgYHtyLCBlY2hvID0gRkFMU0UsIHJlc3VsdHMgPSAiYXNpcyJ9CnNob3dMaXN0KGxzKCJwYWNrYWdlOmdncGxvdDIiLCBwYXQgPSAiXnN0YXRfIiksIG5jb2wgPSAzKQpgYGAKCkVhY2ggZ2VvbSBoYXMgYSBkZWZhdWx0IHN0YXQsIGFuZCBlYWNoIHN0YXQgaGFzIGEgZGVmYXVsdCBnZW9tLgoKKiBGb3IgYGdlb21fcG9pbnRgIHRoZSBkZWZhdWx0IHN0YXQgaXMgYHN0YXRfaWRlbnRpdHlgLgoKKiBGb3IgYGdlb21fYmFyYCB0aGUgZGVmYXVsdCBzdGF0IGlzIGBzdGF0X2NvdW50YC4KCiogRm9yIGBnZW9tX2hpc3RvZ3JhbWAgdGhlIGRlZmF1bHQgaXMgYHN0YXRfYmluYC4KClN0YXRzIGNhbiBwcm92aWRlIF9jb21wdXRlZCB2YXJpYWJsZXNfIHRoYXQgY2FuIGJlIG1hcHBlZCB0byBhZXN0aGV0aWMKZmVhdHVyZXMuCgpGb3IgYHN0YXRfYmluYCBzb21lIG9mIHRoZSBjb21wdXRlZCB2YXJpYWJsZXMgYXJlCgoqIGBjb3VudGA6IG51bWJlciBvZiBwb2ludHMgaW4gYmluCiogYGRlbnNpdHlgOiBkZW5zaXR5IG9mIHBvaW50cyBpbiBiaW4sIHNjYWxlZCB0byBpbnRlZ3JhdGUgdG8gMQoKVGhlIGBkZW5zaXR5YCB2YXJpYWJsZSBjYW4gYmUgYWNjZXNzZWQgYXMgYGFmdGVyX3N0YXQoZGVudGl0eSlgLgoKT2xkZXIgYXBwcm9hY2hlcyB0aGF0IGFsc28gd29yayBidXQgYXJlIG5vdyBkaXNjb3VyYWdlZDoKCiogYHN0YXQoZGVudGl0eSlgCiogYC4uZGVuc2l0eS4uYAoKQnkgZGVmYXVsdCwgYGdlb21faGlzdG9ncmFtYCB1c2VzIGB5ID0gYWZ0ZXJfc3RhdChjb3VudClgLgoKYGBge3IgZ2V5c2VyLWNvdW50LCBldmFsID0gRkFMU0V9CmdncGxvdChmYWl0aGZ1bCkgKwogICAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBlcnVwdGlvbnMpLAogICAgICAgICAgICAgICAgICAgYmlud2lkdGggPSAwLjI1LAogICAgICAgICAgICAgICAgICAgZmlsbCA9ICJncmV5IiwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIikKYGBgCmBgYHtyIGdleXNlci1jb3VudCwgZWNobyA9IEZBTFNFfQpgYGAKCkV4cGxpY2l0bHkgc3BlY2lmeWluZyBgeSA9IGFmdGVyX3N0YXQoY291bnQpYCBwcm9kdWNlcyB0aGUgc2FtZSBwbG90OgoKYGBge3IgZ2V5c2VyLWNvdW50LWV4cCwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoZmFpdGhmdWwpICsKICAgIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gZXJ1cHRpb25zLAogICAgICAgICAgICAgICAgICAgICAgIHkgPSBhZnRlcl9zdGF0KGNvdW50KSksCiAgICAgICAgICAgICAgICAgICBiaW53aWR0aCA9IDAuMjUsCiAgICAgICAgICAgICAgICAgICBmaWxsID0gImdyZXkiLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKQpgYGAKYGBge3IgZ2V5c2VyLWNvdW50LWV4cCwgZWNobyA9IEZBTFNFfQpgYGAKClVzaW5nIGB5ID0gYWZ0ZXJfc3RhdChkZW5zaXR5KWAgcHJvZHVjZXMgYSBkZW5zaXR5IHNjYWxlZCBheGlzLgoKYGBge3IgZ2V5c2VyLWRlbnRpdHksIGV2YWwgPSBGQUxTRX0KKHAgPC0gZ2dwbG90KGZhaXRoZnVsKSArCiAgICAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBlcnVwdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBhZnRlcl9zdGF0KGRlbnNpdHkpKSwKICAgICAgICAgICAgICAgICAgICBiaW53aWR0aCA9IDAuMjUsCiAgICAgICAgICAgICAgICAgICAgZmlsbCA9ICJncmV5IiwKICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIpKQpgYGAKYGBge3IgZ2V5c2VyLWRlbnRpdHksIGVjaG8gPSBGQUxTRX0KYGBgCgpgc3RhdF9mdW5jdGlvbmAgY2FuIGJlIHVzZWQgdG8gYWRkIGEgZGVuc2l0eSBjdXJ2ZSBzcGVjaWZpZWQgYXMgYQptaXh0dXJlIG9mIHR3byBub3JtYWwgZGVuc2l0aWVzOgoKYGBge3J9CihtcyA8LSBtdXRhdGUoZmFpdGhmdWwsCiAgICAgICAgICAgICAgdHlwZSA9IGlmZWxzZShlcnVwdGlvbnMgPCAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgInNob3J0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb25nIikpICU+JQogICAgIGdyb3VwX2J5KHR5cGUpICU+JQogICAgIHN1bW1hcml6ZShtZWFuID0gbWVhbihlcnVwdGlvbnMpLAogICAgICAgICAgICAgICBzZCA9IHNkKGVydXB0aW9ucyksCiAgICAgICAgICAgICAgIG4gPSBuKCkpICU+JQogICAgIG11dGF0ZShwID0gbiAvIHN1bShuKSkpCmBgYAoKYGBge3IgZ2V5c2VyLWhpc3QtZGVucywgZXZhbCA9IEZBTFNFfQpmIDwtIGZ1bmN0aW9uKHgpCiAgICBtcyRwWzFdICogZG5vcm0oeCwgbXMkbWVhblsxXSwgbXMkc2RbMV0pICsKICAgICAgICBtcyRwWzJdICogZG5vcm0oeCwgbXMkbWVhblsyXSwgbXMkc2RbMl0pCgpwICsgc3RhdF9mdW5jdGlvbihmdW4gPSBmLCBjb2xvciA9ICJyZWQiKQpgYGAKYGBge3IgZ2V5c2VyLWhpc3QtZGVucywgZWNobyA9IEZBTFNFfQpgYGAKCgojIyBQb3NpdGlvbiBBZGp1c3RtZW50cwoKVGhlIGF2YWlsYWJsZSBwb3NpdGlvbiBhZGp1c3RtZW50czoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIHJlc3VsdHMgPSAiYXNpcyJ9CnNob3dMaXN0KGxzKCJwYWNrYWdlOmdncGxvdDIiLCBwYXQgPSAiXnBvc2l0aW9uXyIpLCBuY29sID0gMykKYGBgCgpBIGJhciBjaGFydCBzaG93aW5nIHRoZSBjb3VudHMgZm9yIHRoZSBkaWZmZXJlbnQgYGN1dGAgY2F0ZWdvcmllcyBpbgp0aGUgYGRpYW1vbmRzYCBkYXRhOgoKYGBge3IgZGlhbW9uZHMtY3V0LCBldmFsID0gRkFMU0V9CmdncGxvdChkaWFtb25kcywgYWVzKHggPSBjdXQpKSArCiAgICBnZW9tX2JhcigpCmBgYApgYGB7ciBkaWFtb25kcy1jdXQsIGVjaG8gPSBGQUxTRX0KYGBgCgpNYXBwaW5nIGBjbGFyaXR5YCB0byBgZmlsbGAgc2hvd3MgdGhlIGJyZWFrZG93biBieSBib3RoIGBjdXRgIGFuZApgY2xhcml0eWAgaW4gYSBfc3RhY2tlZCBiYXIgY2hhcnRfOgoKYGBge3IgZGlhbW9uZHMtc3RhY2sxLCBldmFsID0gRkFMU0V9CmdncGxvdChkaWFtb25kcywgYWVzKHggPSBjdXQsCiAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBjbGFyaXR5KSkgKwogICAgZ2VvbV9iYXIoKQpgYGAKYGBge3IgZGlhbW9uZHMtc3RhY2sxLCBlY2hvID0gRkFMU0V9CmBgYAoKVGhlIGRlZmF1bHQgYHBvc2l0aW9uYCBmb3IgYmFyIGNoYXJ0cyBpcyBgcG9zaXRpb25fc3RhY2tgOgoKYGBge3IgZGlhbW9uZHMtc3RhY2syLCBldmFsID0gRkFMU0V9CmdncGxvdChkaWFtb25kcywgYWVzKHggPSBjdXQsCiAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBjbGFyaXR5KSkgKwogICAgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siKQpgYGAKYGBge3IgZGlhbW9uZHMtc3RhY2syLCBlY2hvID0gRkFMU0V9CmBgYAoKYHBvc2l0aW9uX2RvZGdlYCBwcm9kdWNlcyBfc2lkZS1ieS1zaWRlIGJhciBjaGFydHNfOgoKYGBge3IgZGlhbW9uZHMtZG9kZ2UsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KGRpYW1vbmRzLCBhZXMoeCA9IGN1dCwKICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGNsYXJpdHkpKSArCiAgICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIpCmBgYApgYGB7ciBkaWFtb25kcy1kb2RnZSwgZWNobyA9IEZBTFNFfQpgYGAKCmBwb3NpdGlvbl9maWxsYCByZXNjYWxlcyBhbGwgYmFycyB0byBiZSBlcXVhbCBoZWlnaHQgdG8gaGVscCBjb21wYXJlCnByb3BvcnRpb25zIHdpdGhpbiBiYXJzLgoKYGBge3IgZGlhbW9uZHMtZmlsbCwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoZGlhbW9uZHMsIGFlcyh4ID0gY3V0LAogICAgICAgICAgICAgICAgICAgICBmaWxsID0gY2xhcml0eSkpICsKICAgIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKQpgYGAKYGBge3IgZGlhbW9uZHMtZmlsbCwgZWNobyA9IEZBTFNFfQpgYGAKClVzaW5nIHRoZSBjb3VudHMgdG8gc2NhbGUgdGhlIHdpZHRocyB3b3VsZCBwcm9kdWNlIGEgX3NwaW5lIHBsb3RfLCBhCnZhcmlhbnQgb2YgYSBfbW9zYWljIHBsb3RfLgoKVGhpcyBpcyBlYXNpZXN0IHRvIGRvIHdpdGggdGhlIGBnZ21vc2FpY2AgcGFja2FnZS4KCmBwb3NpdGlvbl9qaXR0ZXJgIGNhbiBiZSB1c2VkIHdpdGggYGdlb21fcG9pbnRgIHRvIGF2b2lkIG92ZXJwbG90dGluZwpvciBicmVhayB1cCByb3VuZGluZyBhcnRpZmFjdHMuCgpBbm90aGVyIHZlcnNpb24gb2YgdGhlIE9sZCBGYWl0aGZ1bCBkYXRhIGF2YWlsYWJsZSBhcyBgZ2V5c2VyYCBpbgpwYWNrYWdlIGBNQVNTYCBoYXMgc29tZSByb3VuZGluZyBpbiB0aGUgYGR1cmF0aW9uYCB2YXJpYWJsZToKCmBgYHtyIGdleXNlcjIsIGV2YWwgPSBGQUxTRX0KZGF0YShnZXlzZXIsIHBhY2thZ2UgPSAiTUFTUyIpCgojIyBBZGp1c3QgZm9yIGRpZmZlcmVudCBtZWFuaW5nIG9mIGB3YWl0aW5nYCB2YXJpYWJsZQpnZXlzZXIyIDwtIG5hLm9taXQobXV0YXRlKGdleXNlciwKICAgICAgICAgICAgICAgICAgICAgICAgICBkdXJhdGlvbiA9IGxhZyhkdXJhdGlvbikpKQoKcCA8LSBnZ3Bsb3QoZ2V5c2VyMiwgYWVzKHggPSBkdXJhdGlvbiwgeSA9IHdhaXRpbmcpKQpwICsgZ2VvbV9wb2ludCgpCmBgYAoKYGBge3IgZ2V5c2VyMiwgZWNobyA9IEZBTFNFfQpgYGAKCl9KaXR0ZXJpbmdfIGNhbiBoZWxwIGJyZWFrIHVwIHRoZSBkaXN0cmFjdGluZyBfaGVhcGluZ18gb2YgdmFsdWVzIG9uCmR1cmF0aW9ucyBvZiAyIGFuZCA0IG1pbnV0ZXMuCgpUaGUgZGVmYXVsdCBhbW91bnQgb2Ygaml0dGVyaW5nIGlzbid0IHF1aXRlIGVub3VnaCBpbiB0aGlzIGNhc2U6CgpgYGB7ciBnZXlzZXIyLWppdCwgZXZhbCA9IEZBTFNFfQpwICsgZ2VvbV9wb2ludChwb3NpdGlvbiA9ICJqaXR0ZXIiKQpgYGAKYGBge3IgZ2V5c2VyMi1qaXQsIGVjaG8gPSBGQUxTRX0KYGBgCgpUbyBqaXR0ZXIgb25seSBob3Jpem9udGFsbHkgYW5kIGJ5IGEgbGFyZ2VyIGFtb3VudCB5b3UgY2FuIHVzZQoKYGBge3IgZ2V5c2VyMi1qaXQyLCBldmFsID0gRkFMU0V9CnAgKyBnZW9tX3BvaW50KHBvc2l0aW9uID0KICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uX2ppdHRlcihoZWlnaHQgPSAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdpZHRoID0gMC4xKSkKYGBgCmBgYHtyIGdleXNlcjItaml0MiwgZWNobyA9IEZBTFNFfQpgYGAKCgojIyBDb29yZGluYXRlIFN5c3RlbXMKCkNvb3JkaW5hdGUgc3lzdGVtIGZ1bmN0aW9ucyBpbmNsdWRlCgpgYGB7ciwgZWNobyA9IEZBTFNFLCByZXN1bHRzID0gImFzaXMifQpzaG93TGlzdChscygicGFja2FnZTpnZ3Bsb3QyIiwgcGF0ID0gIl5jb29yZF8iKSkKYGBgCgpUaGUgZGVmYXVsdCBjb29yZGluYXRlIHN5c3RlbSBpcyBgY29vcmRfY2FydGVzaWFuYC4KCgojIyMgQ2FydGVzaWFuIENvb3JkaW5hdGVzCgpgY29vcmRfY2FydGVzaWFuYCBjYW4gYmUgdXNlZCB0byBfem9vbSBpbl8gb24gYSBwYXJ0aWN1bGFyIHJlZ2lpb246CgpgYGB7ciBnZXlzZXIyLXpvb20sIGV2YWwgPSBGQUxTRX0KcCArIGdlb21fcG9pbnQoKSArCiAgICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMywgNCkpCmBgYApgYGB7ciBnZXlzZXIyLXpvb20sIGVjaG8gPSBGQUxTRX0KYGBgCgpgY29vcmRfZml4ZWRgIGFuZCBgY29vcmRfZXF1YWxgIGZpeCB0aGUgX2FzcGVjdCByYXRpb18gZm9yIGEgY2FydGVzaWFuCmNvb3JkaW5hdGUgc3lzdGVtLgoKVGhlIGFzcGVjdCByYXRpbyBpcyB0aGUgcmF0aW8gb2YgdGhlIG51bWJlciBwaHlzaWNhbCBkaXNwbGF5IHVuaXRzIHBlcgpgeWAgdW5pdCB0byB0aGUgbnVtYmVyIG9mIHBoeXNpY2FsIGRpc3BsYXkgdW5pdHMgcGVyIGB4YCB1bml0LgoKVGhlIGFzcGVjdCByYXRpbyBjYW4gYmUgaW1wb3J0YW50IGZvciByZWNvZ25pemluZyBmZWF0dXJlcyBhbmQgcGF0dGVybnMuCgpgYGB7cn0Kcml2ZXIgPC0gc2NhbigiaHR0cHM6Ly93d3cuc3RhdC51aW93YS5lZHUvfmx1a2UvZGF0YS9yaXZlci5kYXQiKQpyIDwtIGRhdGEuZnJhbWUoZmxvdyA9IHJpdmVyLCBtb250aCA9IHNlcV9hbG9uZyhyaXZlcikpCmBgYAoKYGBge3Igcml2ZXItZmxhdCwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QociwgYWVzKHggPSBtb250aCwgeSA9IGZsb3cpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgY29vcmRfZml4ZWQocmF0aW8gPSA0KQpgYGAKYGBge3Igcml2ZXItZmxhdCwgZWNobyA9IEZBTFNFLCBmaWcuaGVpZ2h0ID0gMiwgZmlnLndpZHRoID0gOH0KYGBgCgoKIyMjIFBvbGFyIENvb3JkaW5hdGVzCgpBIGZpbGxlZCBiYXIgY2hhcnQKCmBgYHtyIGRpYW1vbmRzLWZpbGwtMSwgZXZhbCA9IEZBTFNFfQoocCA8LSBnZ3Bsb3QoZGlhbW9uZHMpICsKICAgICBnZW9tX2JhcihhZXMoeCA9IDEsIGZpbGwgPSBjdXQpLAogICAgICAgICAgICAgIHBvc2l0aW9uID0gImZpbGwiKSkKYGBgCmBgYHtyIGRpYW1vbmRzLWZpbGwtMSwgZWNobyA9IEZBTFNFfQpgYGAKCmlzIHR1cm5lZCBpbnRvIGEgcGllIGNoYXJ0IGJ5IGNoYW5naW5nIHRvIHBvbGFyIGNvb3JkaW5hdGVzOgoKYGBge3IgZGlhbW9uZHMtcGllLCBldmFsID0gRkFMU0V9CnAgKyBjb29yZF9wb2xhcih0aGV0YSA9ICJ5IikKYGBgCmBgYHtyIGRpYW1vbmRzLXBpZSwgZWNobyA9IEZBTFNFfQpgYGAKCgojIyMgQ29vcmRpbmF0ZSBTeXN0ZW1zIGZvciBNYXBzCgpDb29yZGluYXRlIHN5c3RlbXMgYXJlIHBhcnRpY3VsYXJseSBpbXBvcnRhbnQgZm9yIG1hcHMuCgpQb2x5Z29ucyBmb3IgbWFueSBwb2xpdGljYWwgYW5kIGdlb2dyYXBoaWMgYm91bmRhcmllcyBhcmUgYXZhaWxhYmxlCnRocm91Z2ggdGhlIGBtYXBfZGF0YWAgZnVuY3Rpb24uCgpCb3VuZGFyaWVzIGZvciB0aGUgbG93ZXIgNDggVVMgc3RhdGVzIGNhbiBiZSBvYnRhaW5lZCBhcwoKYGBge3J9CnVzYSA8LSBtYXBfZGF0YSgic3RhdGUiKQpgYGAKClBvbHlnb24gdmVydGljZXMgYXJlIGVuY29kZWQgYnkgbG9uZ2l0dWRlIGFuZCBsYXRpdHVkZS4KClBsb3R0aW5nIHRoZXNlIGluIHRoZSBkZWZhdWx0IGNhcnRlc2lhbiBjb29yZGluYXRlIHN5c3RlbSB1c3VhbGx5IGRvZXMKbm90IHdvcmsgd2VsbDoKCmBgYHtyIHVzYS1jYXJ0LCBldmFsID0gRkFMU0V9CnVzYSA8LSBtYXBfZGF0YSgic3RhdGUiKQptIDwtIGdncGxvdCh1c2EsIGFlcyh4ID0gbG9uZywKICAgICAgICAgICAgICAgICAgICAgeSA9IGxhdCwKICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBncm91cCkpICsKICAgIGdlb21fcG9seWdvbihmaWxsID0gIndoaXRlIiwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIpCm0KYGBgCmBgYHtyIHVzYS1jYXJ0LCBlY2hvID0gRkFMU0V9CmBgYAoKVXNpbmcgYSBmaXhlZCBhc3BlY3QgcmF0aW8gaXMgYmV0dGVyLCBidXQgYW4gYXNwZWN0IHJhdGlvIG9mIDEgZG9lcwpub3Qgd29yayB3ZWxsOgoKYGBge3J9Cm0gKyBjb29yZF9lcXVhbCgpCmBgYAoKVGhlIHByb2JsZW0gaXMgdGhhdCBhd2F5IGZyb20gdGhlIGVxdWF0b3IgYSBvbmUgZGVncmVlIGNoYW5nZSBpbgpsYXRpdHVkZSBjb3JyZXNwb25kcyB0byBhIGxhcmdlciBkaXN0YW5jZSB0aGFuIGEgb25lIGRlZ3JlZSBjaGFuZ2UgaW4KbG9uZ2l0dWRlLgoKVGhlIHJhdGlvIG9mIG9uZSBkZWdyZWUgbG9uZ2l0dWRlIHNlcGFyYXRpb24gdG8gb25lIGRlZ3JlZSBsYXRpdHVkZQpzZXBhcmF0aW9uIGZvciB0aGUgbGF0aXR1ZGUgYXQgdGhlIG1pZGRsZSBvZiBJb3dhIG9mIDQxIGRlZ3JlZXMgaXMKCmBgYHtyfQpsb25nbGF0IDwtIGNvcyg0MSAvIDkwICogcGkgLyAyKQpsb25nbGF0CmBgYAoKQSBiZXR0ZXIgbWFwIGlzIG9idGFpbmVkIHVzaW5nIHRoZSBhc3BlY3QgcmF0aW8gYDEgLyBsb25nbGF0YDoKCmBgYHtyIHVzYS1maXhlZCwgZXZhbCA9IEZBTFNFfQptICsgY29vcmRfZml4ZWQoMSAvIGxvbmdsYXQpCmBgYApgYGB7ciB1c2EtZml4ZWQsIGVjaG8gPSBGQUxTRX0KYGBgCgpUaGUgYmVzdCBhcHByb2FjaCBpcyB0byB1c2UgYSBjb29yZGluYXRlIHN5c3RlbSBkZXNpZ25lZCBzcGVjaWZpY2FsbHkgZm9yCm1hcHMuCgpUaGVyZSBhcmUgbWFueSBfcHJvamVjdGlvbnNfIHVzZWQgaW4gbWFwIG1ha2luZy4KClRoZSBkZWZhdWx0IHByb2plY3Rpb24gdXNlZCBieSBgY29vcmRfbWFwYCBpcyB0aGUKW01lcmNhdG9yXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9NZXJjYXRvcl9wcm9qZWN0aW9uKQpwcm9qZWN0aW9uLgoKYGBge3IgdXNhLW1lcmNhdG9yLCBldmFsID0gRkFMU0V9Cm0gKyBjb29yZF9tYXAoKQpgYGAKCmBgYHtyIHVzYS1tZXJjYXRvciwgZWNobyA9IEZBTFNFfQpgYGAKClByb3BlciBtYXAgcHJvamVjdGlvbnMgYXJlIG5vbi1saW5lYXI7IHRoaXMgaXMgZWFzaWVyIHRvIHNlZSB3aXRoIGFuCkFsYmVycyBwcm9qZWN0aW9uOgoKYGBge3IgdXNhLWFsYmVycywgZXZhbCA9IEZBTFNFfQptICsgY29vcmRfbWFwKCJhbGJlcnMiLCAyMCwgNTApCmBgYApgYGB7ciB1c2EtYWxiZXJzLCBlY2hvID0gRkFMU0V9CmBgYAoKCiMjIFNjYWxlcwoKU2NhbGVzIGFyZSB1c2VkIGZvciBjb250cm9sbGluZyB0aGUgbWFwcGluZyBvZiB2YWx1ZXMgdG8gcGh5c2ljYWwKcmVwcmVzZW50YXRpb25zIHN1Y2ggYXMgY29sb3JzLCBzaGFwZXMsIGFuZCBwb3NpdGlvbnMuCgpTY2FsZSBmdW5jdGlvbnMgYXJlIGFsc28gcmVzcG9uc2libGUgZm9yIHByb2R1Y2luZyBfZ3VpZGVzXyBmb3IKdHJhbnNsYXRpbmcgcGh5c2ljYWwgcmVwcmVzZW50YXRpb25zIGJhY2sgdG8gdmFsdWVzLCBzdWNoIGFzCgoqIGF4aXMgbGFiZWxzIGFuZCBtYXJrczsKCiogY29sb3Igb3Igc2hhcGUgbGVnZW5kcy4KClRoZXJlIGFyZSBjdXJyZW50bHkgYHIgbGVuZ3RoKGxzKCJwYWNrYWdlOmdncGxvdDIiLCBwYXQgPSAic2NhbGVfIikpYApzY2FsZSBmdW5jdGlvbnM7IHNvbWUgZXhhbXBsZXMgYXJlCgpgYGByCnNjYWxlX2NvbG9yX2dyYWRpZW50ICAgICAgc2NhbGVfc2hhcGVfbWFudWFsICAgICBzY2FsZV94X2xvZzEwCnNjYWxlX2NvbG9yX21hbnVhbCAgICAgICAgc2NhbGVfc2l6ZV9hcmVhICAgICAgICBzY2FsZV95X2xvZzEwCnNjYWxlX2ZpbGxfZ3JhZGllbnQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZV94X3NxcnQKc2NhbGVfZmlsbF9tYW51YWwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlX3lfc3FydAoKYGBgCgpBbiBbZXhwZXJpbWVudGFsIHRvb2xdKGh0dHBzOi8vZ2dwbG90MnRvci5jb20vc2NhbGVzLykgdG8gaGVscApjaG9vc2luZyBzY2FsZXMgaGFzIHJlY2VudGx5IGJlZW4gaW50cm9kdWNlZC4KClN0YXJ0IHdpdGggYSBiYXNpYyBzY2F0dGVyIHBsb3Q6CgpgYGB7ciBtcGctYmFzaWMsIGV2YWwgPSBGQUxTRX0KKHAgPC0gZ2dwbG90KG1wZywgYWVzKHggPSBkaXNwbCwKICAgICAgICAgICAgICAgICAgICAgIHkgPSBod3kpKSArCiAgICAgZ2VvbV9wb2ludCgpKQpgYGAKYGBge3IgbXBnLWJhc2ljLCBlY2hvID0gRkFMU0V9CmBgYAoKUmVtb3ZlIHRoZSBgeGAgdGljayBtYXJrcyBhbmQgbGFiZWxzICh0aGlzIGNhbiBhbHNvIGJlIGRvbmUgd2l0aCB0aGVtZQpzZXR0aW5ncyk6CgpgYGB7ciBtcGctbm8tdGlja3MtbGFicywgZXZhbCA9IEZBTFNFfQpwICsgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IE5VTEwsCiAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gTlVMTCkKYGBgCmBgYHtyIG1wZy1uby10aWNrcy1sYWJzLCBlY2hvID0gRkFMU0V9CmBgYAoKQ2hhbmdlIHRoZSB0aWNrIGxvY2F0aW9ucyBhbmQgbGFiZWxzOgoKYGBge3IgbXBnLW5ldy10aWNrcy1sYWJzLCBldmFsID0gRkFMU0V9CnAgKyBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0KICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoYygyLCA0LCA2KSwgImx0ciIpLAogICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoMiwgNCwgNikpCmBgYApgYGB7ciBtcGctbmV3LXRpY2tzLWxhYnMsIGVjaG8gPSBGQUxTRX0KYGBgCgpVc2UgYSBsb2dhcml0aG1pYyBheGlzOgoKYGBge3IgbXBnLWxvZy14LCBldmFsID0gRkFMU0V9CnAgKyBzY2FsZV94X2xvZzEwKGxhYmVscyA9IHBhc3RlKGMoMiwgNCwgNiksICJsdHIiKSwKICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygyLCA0LCA2KSwKICAgICAgICAgICAgICAgICAgbWlub3JfYnJlYWtzID0gYygzLCA1LCA3KSkKYGBgCmBgYHtyIG1wZy1sb2cteCwgZWNobyA9IEZBTFNFfQpgYGAKClRoZQpbU2NhbGVzXShodHRwczovL3I0ZHMuaGFkLmNvLm56L2dyYXBoaWNzLWZvci1jb21tdW5pY2F0aW9uLmh0bWwjc2NhbGVzKQpzZWN0aW9uIGluIFtSIGZvciBEYXRhIFNjaWVuY2VdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovKSBwcm92aWRlcyBzb21lCm1vcmUgZGV0YWlscy4KCkNvbG9yIGFzc2lnbm1lbnQgY2FuIGFsc28gYmUgY29udHJvbGxlZCBieSBzY2FsZSBmdW5jdGlvbnMuCgpGb3IgZXhhbXBsZSwgZm9yIHNvbWUgcHJlc2lkZW50aWFsIGFwcHJvdmFsIHJhdGluZ3MgZGF0YQoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0KcHJfYXBwciA8LSBkYXRhLmZyYW1lKHByZXMgPSBjKCJPYmFtYSIsICJDYXJ0ZXIiLCAiQ2xpbnRvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRy5XLiBCdXNoIiwgIlJlYWdhbiIsICJHLkguVyBCdXNoIiwgIlRydW1wIiksCiAgICAgICAgICAgICAgICAgICAgICBhcHByID0gYyg3OSwgNzgsIDY4LCA2NSwgNTgsIDU2LCA0MCksCiAgICAgICAgICAgICAgICAgICAgICBwYXJ0eSA9IGMoIkQiLCAiRCIsICJEIiwgIlIiLCAiUiIsICJSIiwgIlIiKSwKICAgICAgICAgICAgICAgICAgICAgIHllYXIgPSBjKDIwMDksIDE5NzcsIDE5OTMsIDIwMDEsIDE5ODEsIDE5ODksIDIwMTcpKQpwcl9hcHByIDwtIG11dGF0ZShwcl9hcHByLCBwcmVzID0gcmVvcmRlcihwcmVzLCBhcHByKSkKYGBgCmBgYHtyfQpwcl9hcHByCmBgYAoKdGhlIGRlZmF1bHQgY29sb3Igc2NhbGUgaXMgbm90IGlkZWFsOgoKYGBge3IgcHItYXBwcjAsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KHByX2FwcHIsCiAgICAgICBhZXMoeCA9IGFwcHIsIHkgPSBwcmVzLCBmaWxsID0gcGFydHkpKSArCiAgICBnZW9tX2NvbCgpCmBgYAoKYGBge3IgcHItYXBwcjAsIGVjaG8gPSBGQUxTRX0KYGBgCgpUaGUgY29tbW9uIGFzc2lnbm1lbnQgb2YgcmVkIGZvciBSZXB1YmxpY2FuIGFuZCBibHVlIGZvciBEZW1vY3JhdCBjYW4KYmUgb2J0YWluZWQgYnkKCmBgYHtyIHByLWFwcHIsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KHByX2FwcHIsCiAgICAgICBhZXMoeCA9IGFwcHIsIHkgPSBwcmVzLCBmaWxsID0gcGFydHkpKSArCiAgICBnZW9tX2NvbCgpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcwogICAgICAgICAgICAgICAgICAgICAgPSBjKFIgPSAicmVkIiwgRCA9ICJibHVlIikpCmBgYAoKYGBge3IgcHItYXBwciwgZWNobyA9IEZBTFNFfQpgYGAKCkEgYmV0dGVyIGNob2ljZSBpcyB0byB1c2UgYSB3ZWxsLWRlc2lnbmVkIFtjb2xvcgpwYWxldHRlXShodHRwczovL2hjbHdpemFyZC5vcmcvI2NvbG9yLXBhbGV0dGVzKToKCmBgYHtyIHByLWFwcHItMiwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QocHJfYXBwciwKICAgICAgIGFlcyh4ID0gYXBwciwgeSA9IHByZXMsIGZpbGwgPSBwYXJ0eSkpICsKICAgIGdlb21fY29sKCkgKwogICAgY29sb3JzcGFjZTo6c2NhbGVfZmlsbF9kaXNjcmV0ZV9kaXZlcmdpbmcoCiAgICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9ICJCbHVlLVJlZCAyIikKYGBgCgpgYGB7ciBwci1hcHByLTIsIGVjaG8gPSBGQUxTRX0KYGBgCgoKIyMgRmFjZXRzCgpGYWNldGluZyB1c2VzIHRoZSBfc21hbGwgbXVsdGlwbGVzXyBhcHByb2FjaCB0byBpbnRyb2R1Y2UgYWRkaXRpb25hbAp2YXJpYWJsZXMuCgpGb3IgYSBzaW5nbGUgdmFyaWFibGUgYGZhY2V0X3dyYXBgIGlzIHVzdWFsbHkgdXNlZDoKCmBgYHtyIG1wZy1mYWNldC13cmFwLCBldmFsID0gRkFMU0V9CnAgPC0gZ2dwbG90KG1wZykgKwogICAgZ2VvbV9wb2ludChhZXMoeCA9IGRpc3BsLAogICAgICAgICAgICAgICAgICAgeSA9IGh3eSkpCnAgKyBmYWNldF93cmFwKH4gY2xhc3MpCmBgYApgYGB7ciBtcGctZmFjZXQtd3JhcCwgZWNobyA9IEZBTFNFfQpgYGAKCkZvciB0d28gdmFyaWFibGVzLCBlYWNoIHdpdGggYSBtb2Rlc3QgbnVtYmVyIG9mIGNhdGVnb3JpZXMsCmBmYWNldF9ncmlkYCBjYW4gYmUgZWZmZWN0aXZlOgoKYGBge3IgbXBnLWZhY2V0LWdyaWQsIGV2YWwgPSBGQUxTRX0KcCArIGZhY2V0X2dyaWQoZmFjdG9yKGN5bCkgfiBkcnYpCmBgYApgYGB7ciBtcGctZmFjZXQtZ3JpZCwgZWNobyA9IEZBTFNFfQpgYGAKCjwhLS0KVXNpbmcgdGhlIHByZXZpb3VzIG1wZyBmYWNldCBwbG90IHdvdWxkIGJlIGJldHRlciBidXQgdGhpcyBpcyBvbmUgb2YKdGhlIGhvbWV3b3JrIHByb2JsZW1zIGluIEhXMwotLT4KClRvIHNob3cgY29tbW9uIGRhdGEgaW4gYWxsIGZhY2V0cyBtYWtlIHN1cmUgdGhlIGRhdGEgZG9lcyBub3QgY29udGFpbiB0aGUKZmFjZXRpbmcgdmFyaWFibGUuCgpUaGlzIHdhcyB1c2VkIHRvIHNob3cgbXV0ZWQgdmlld3Mgb2YgdGhlIGZ1bGwgZGF0YSBpbiBmYWNldGVkIHBsb3RzLgoKQSBmYWNldGVkIHBsb3Qgb2YgdGhlIGBnYXBtaW5kZXJgIGRhdGE6CgpgYGB7ciBnYXBtaW5kZXItbm90LW11dGVkLCBldmFsID0gRkFMU0V9CmxpYnJhcnkoZ2FwbWluZGVyKQoKeWVhcnNfdG9fa2VlcCA8LSBjKDE5NzcsIDE5ODcsIDE5OTcsIDIwMDcpCmdkIDwtIGZpbHRlcihnYXBtaW5kZXIsCiAgICAgICAgICAgICB5ZWFyICVpbiUgeWVhcnNfdG9fa2VlcCkKCmdncGxvdChnZCwKICAgICAgIGFlcyh4ID0gZ2RwUGVyY2FwLAogICAgICAgICAgIHkgPSBsaWZlRXhwLAogICAgICAgICAgIGNvbG9yID0gY29udGluZW50KSkgKwogICAgZ2VvbV9wb2ludChzaXplID0gMi41KSArCiAgICBzY2FsZV94X2xvZzEwKCkgKwogICAgZmFjZXRfd3JhcCh+IHllYXIpCmBgYAoKYGBge3IgZ2FwbWluZGVyLW5vdC1tdXRlZCwgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSA4fQpgYGAKCkFkZCBhIG11dGVkIHZlcnNpb24gb2YgdGhlIGZ1bGwgZGF0YSBpbiB0aGUgYmFja2dyb3VuZCBvZiBlYWNoIHBhbmVsOgoKPCEtLSB2YXJpYW50IG9mIGNvZGUgaW4gcHJlcmNlcC5SbWQgLS0+CmBgYHtyIGdhcG1pbmRlci1tdXRlZCwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KGdhcG1pbmRlcikKCnllYXJzX3RvX2tlZXAgPC0gYygxOTc3LCAxOTg3LCAxOTk3LCAyMDA3KQpnZCA8LSBmaWx0ZXIoZ2FwbWluZGVyLAogICAgICAgICAgICAgeWVhciAlaW4lIHllYXJzX3RvX2tlZXApCmdkX25vX3llYXIgPC0gbXV0YXRlKGdkLCB5ZWFyID0gTlVMTCkKCmdncGxvdChnZCwKICAgICAgIGFlcyh4ID0gZ2RwUGVyY2FwLAogICAgICAgICAgIHkgPSBsaWZlRXhwLAogICAgICAgICAgIGNvbG9yID0gY29udGluZW50KSkgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZ2Rfbm9feWVhciwKICAgICAgICAgICAgICAgY29sb3IgPSAiZ3JleTgwIikgKwogICAgZ2VvbV9wb2ludChzaXplID0gMi41KSArCiAgICBzY2FsZV94X2xvZzEwKCkgKwogICAgZmFjZXRfd3JhcCh+IHllYXIpCmBgYAoKYGBge3IgZ2FwbWluZGVyLW11dGVkLCBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDh9CmBgYAoKVXN1YWxseSBmYWNldHMgdXNlIGNvbW1vbiBheGlzIHNjYWxlcywgYnV0IG9uZSBvciBib3RoIGNhbiBiZSBhbGxvd2VkCnRvIHZhcnkuCgpBIHVzZWZ1bCBhcHByb2FjaCBmb3Igc2hvd2luZyB0aW1lIHNlcmllcyBkYXRhIHdpdGggYSBnb29kIGFzcGVjdApyYXRpbyBjYW4gYmUgdG8gc3BsaXQgdGhlIGRhdGEgaW50byBmYWNldHMgZm9yIG5vbi1vdmVybGFwcGluZwpwb3J0aW9ucyBvZiB0aGUgdGltZSBheGlzLgoKYGBge3Igcml2ZXItZmFjZXQsIGV2YWwgPSBGQUxTRX0KcGQgPC0gcmVwKHBhc3RlKHNlcSgxLCBieSA9IDMyLCBsZW5ndGgub3V0ID0gNCksCiAgICAgICAgICAgICAgICBzZXEoMzIsIGJ5ID0gMzIsIGxlbmd0aC5vdXQgPSA0KSwKICAgICAgICAgICAgICAgIHNlcCA9ICIgLSAiKSwKICAgICAgICAgZWFjaCA9ICAzMikKcmQgPC0gZGF0YS5mcmFtZShtb250aCA9IHNlcV9hbG9uZyhyaXZlciksCiAgICAgICAgICAgICAgICAgZmxvdyA9IHJpdmVyLAogICAgICAgICAgICAgICAgIHBhbmVsID0gcGQpCmdncGxvdChyZCwgYWVzKHggPSBtb250aCwKICAgICAgICAgICAgICAgeSA9IGZsb3cpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZmFjZXRfd3JhcCh+IHBhbmVsLAogICAgICAgICAgICAgICBzY2FsZSA9ICJmcmVlX3giLAogICAgICAgICAgICAgICBuY29sID0gMSkKYGBgCmBgYHtyIHJpdmVyLWZhY2V0LCBlY2hvID0gRkFMU0V9CmBgYAoKRmFjZXQgYXJyYW5nZW1lbnQgY2FuIGFsc28gYmUgdXNlZCB0byBjb252ZXkgb3RoZXIgaW5mb3JtYXRpb24sIHN1Y2gKYXMgZ2VvZ3JhcGhpYyBsb2NhdGlvbi4KClRoZSBbYGdlb2ZhY2V0YCBwYWNrYWdlXShodHRwczovL2hhZmVuLmdpdGh1Yi5pby9nZW9mYWNldC8pIGFsbG93cwpmYWNldHMgdG8gYmUgcGxhY2VkIGluIGFwcHJveGltYXRlIGxvY2F0aW9ucyBvZiBkaWZmZXJlbnQgZ2VvZ3JhcGhpYwpyZWdpb25zLgoKQW4gZXhhbXBsZSBmb3IgZGF0YSBmcm9tIFVTIHN0YXRlczoKCmBgYHtyIGdlb2ZhY2V0LCBldmFsID0gRkFMU0V9CmxpYnJhcnkoZ2VvZmFjZXQpCmdncGxvdChzdGF0ZV91bmVtcCwgYWVzKHllYXIsIHJhdGUpKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICBmYWNldF9nZW8ofiBzdGF0ZSwKICAgICAgICAgICAgICBncmlkID0gInVzX3N0YXRlX2dyaWQyIiwKICAgICAgICAgICAgICBsYWJlbCA9ICJjb2RlIikgKwogICAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9CiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHBhc3RlMCgiJyIsIHN1YnN0cih4LCAzLCA0KSkpICsKICAgIGxhYnModGl0bGUgPSAiU2Vhc29uYWxseSBBZGp1c3RlZCBVUyBVbmVtcGxveW1lbnQgUmF0ZSAyMDAwLTIwMTYiLAogICAgICAgICBjYXB0aW9uID0gIkRhdGEgU291cmNlOiBibHMuZ292IiwKICAgICAgICAgeCA9ICJZZWFyIiwKICAgICAgICAgeSA9ICJVbmVtcGxveW1lbnQgUmF0ZSAoJSkiKSArCiAgICB0aGVtZShzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLAogICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA1KSkKYGBgCmBgYHtyIGdlb2ZhY2V0LCBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KYGBgCgpBcnJhbmdlbWVudCBhY2NvcmRpbmcgdG8gYSBjYWxlbmRhciBjYW4gYWxzbyBiZSB1c2VmdWwuCgoKIyMgVGhlbWVzCgpgZ2dwbG90MmAgc3VwcG9ydHMgdGhlIG5vdGlvbiBvZiBfdGhlbWVzXyBmb3IgYWRqdXN0aW5nIG5vbi1kYXRhCmFwcGVhcmFuY2UgYXNwZWN0cyBvZiBhIHBsb3QsIHN1Y2ggYXMKCiogcGxvdCB0aXRsZXMKCiogYXhpcyBhbmQgbGVnZW5kIHBsYWNlbWVudCBhbmQgdGl0bGVzCgoqIGJhY2tncm91bmQgY29sb3JzCgoqIGd1aWRlIGxpbmUgcGxhY2VtZW50CgpUaGVtZSBlbGVtZW50cyBjYW4gYmUgY3VzdG9taXplZCBpbiBzZXZlcmFsIHdheXM6CgoqIGB0aGVtZSgpYCBjYW4gYmUgdXNlZCB0byBhZGp1c3QgaW5kaXZpZHVhbCBlbGVtZW50cyBpbiBhIHBsb3QuCgoqIGB0aGVtZV9zZXQoKWAgYWRqdXN0cyBkZWZhdWx0IHNldHRpbmdzIGZvciBhIHNlc3Npb247CgoqIHByZS1kZWZpbmVkIHRoZW1lIGZ1bmN0aW9ucyBhbGxvdyBjb25zaXN0ZW50IHN0eWxlIGNoYW5nZXMuCgpUaGUKW2Z1bGwgZG9jdW1lbnRhdGlvbl0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3RoZW1lLmh0bWwpCm9mIHRoZSBgdGhlbWVgIGZ1bmN0aW9uIGxpc3RzIG1hbnkgY3VzdG9taXphYmxlIGVsZW1lbnRzLgoKT25lIHNpbXBsZSBleGFtcGxlOgoKYGBge3IgdGhlbWUtc2ltcGxlLCBldmFsID0gRkFMU0V9CmdncGxvdChtdXRhdGUobXBnLCBjeWwgPSBmYWN0b3IoY3lsKSkpICsKICAgIGdlb21fcG9pbnQoYWVzKHggPSBkaXNwbCwKICAgICAgICAgICAgICAgICAgIHkgPSBod3ksCiAgICAgICAgICAgICAgICAgICBmaWxsID0gY3lsKSwKICAgICAgICAgICAgICAgc2hhcGUgPSAyMSwKICAgICAgICAgICAgICAgc2l6ZSA9IDMpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJib2xkIikpCmBgYApgYGB7ciB0aGVtZS1zaW1wbGUsIGVjaG8gPSBGQUxTRX0KYGBgCgpBbm90aGVyIGV4YW1wbGU6CgpgYGB7ciB0aGVtZS1zaW1wbGUtMiwgZXZhbCA9IEZBTFNFfQpndGhtIDwtCiAgICB0aGVtZShwbG90LmJhY2tncm91bmQgPQogICAgICAgICAgICAgIGVsZW1lbnRfcmVjdChmaWxsID0gImxpZ2h0Ymx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gTkEpLAogICAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9CiAgICAgICAgICAgICAgZWxlbWVudF9yZWN0KGZpbGwgPSAicGluayIpKQpwICsgZ3RobQpgYGAKYGBge3IgdGhlbWUtc2ltcGxlLTIsIGVjaG8gPSBGQUxTRX0KYGBgCgpTb21lIGFsdGVybmF0ZSBjb21wbGV0ZSB0aGVtZXMgcHJvdmlkZWQgYnkgYGdncGxvdDJgIGFyZQoKYGBgcgp0aGVtZV9idyAgICAgICAgdGhlbWVfZ3JheSAgICAgIHRoZW1lX21pbmltYWwgICB0aGVtZV92b2lkCnRoZW1lX2NsYXNzaWMgICB0aGVtZV9ncmV5ICAgICAgdGhlbWVfZGFyayAgICAgIHRoZW1lX2xpZ2h0CmBgYAoKU29tZSBleGFtcGxlczoKCmBgYHtyIGFsdC10aGVtZXMsIGV2YWwgPSBGQUxTRX0KcF9idyA8LSBwICsgdGhlbWVfYncoKSArIGdndGl0bGUoIkJXIikKCnBfY2xhc3NpYyA8LSBwICsgdGhlbWVfY2xhc3NpYygpICsgZ2d0aXRsZSgiQ2xhc3NpYyIpCgpwX21pbiA8LSBwICsgdGhlbWVfbWluaW1hbCgpICsgZ2d0aXRsZSgiTWluaW1hbCIpCgpwX3ZvaWQgPC0gcCArIHRoZW1lX3ZvaWQoKSArIGdndGl0bGUoIlZvaWQiKQoKbGlicmFyeShwYXRjaHdvcmspCihwX2J3ICsgcF9jbGFzc2ljKSAvIChwX21pbiArIHBfdm9pZCkKYGBgCmBgYHtyIGFsdC10aGVtZXMsIGVjaG8gPSBGQUxTRX0KYGBgCgpUaGUKW2BnZ3RoZW1lc2BdKGh0dHA6Ly93d3cucnB1YnMuY29tL01lbnRvcnNfVWJpcXVtL2dndGhlbWVzXzEpCnBhY2thZ2UgcHJvdmlkZXMgc29tZSBhZGRpdGlvbmFsIHRoZW1lcy4KClNvbWUgZXhhbXBsZXM6CgpgYGB7ciBnZ3RoZW1lcy1leGFtcGxlcywgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KGdndGhlbWVzKQoKcF9lY29uIDwtIHAgKyB0aGVtZV9lY29ub21pc3QoKSArIGdndGl0bGUoIkVjb25vbWlzdCIpCgpwX3dzaiA8LSBwICsgdGhlbWVfd3NqKCkgKyBnZ3RpdGxlKCJXU0oiKQoKcF90dWZ0ZSA8LSBwICsgdGhlbWVfdHVmdGUoKSArIGdndGl0bGUoIlR1ZnRlIikKCnBfZmV3IDwtIHAgKyB0aGVtZV9mZXcoKSArIGdndGl0bGUoIkZldyIpCgoocF9lY29uICsgcF93c2opIC8gKHBfdHVmdGUgKyBwX2ZldykKYGBgCmBgYHtyIGdndGhlbWVzLWV4YW1wbGVzLCBlY2hvID0gRkFMU0V9CmBgYAoKYGdndGhlbWVzYCBhbHNvIHByb3ZpZGVzIGB0aGVtZV9tYXBgIHRoYXQgcmVtb3ZlcyB1bm5lY2Vzc2FyeSBlbGVtZW50cwpmcm9tIG1hcHM6CgpgYGB7cn0KbSArIGNvb3JkX21hcCgpICsgdGhlbWVfbWFwKCkKYGBgCgpUaGUKW1RoZW1lc10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9ncmFwaGljcy1mb3ItY29tbXVuaWNhdGlvbi5odG1sI3RoZW1lcykKc2VjdGlvbiBpbiBbUiBmb3IgRGF0YSBTY2llbmNlXShodHRwczovL3I0ZHMuaGFkLmNvLm56LykgcHJvdmlkZXMgc29tZQptb3JlIGRldGFpbHMuCgoKIyMgQSBNb3JlIENvbXBsZXRlIFRlbXBsYXRlCgpgYGByCmdncGxvdChkYXRhID0gPERBVEE+KSArCiAgICA8R0VPTT4obWFwcGluZyA9IGFlcyg8TUFQUElOR1M+KSwKICAgICAgICAgICBzdGF0ID0gPFNUQVQ+LAogICAgICAgICAgIHBvc2l0aW9uID0gPFBPU0lUSU9OPikgKwogICAgPCAuLi4gTU9SRSBHRU9NUyAuLi4gPiArCiAgICA8Q09PUkRJTkFURV9BREpVU1RNRU5UPiArCiAgICA8U0NBTEVfQURKVVNUTUVOVD4gKwogICAgPEZBQ0VUSU5HPiArCiAgICA8VEhFTUVfQURKVVNUTUVOVD4KYGBgCgoKIyMgTGFiZWxzIGFuZCBBbm5vdGF0aW9ucwoKQSBiYXNpYyBwbG90OgoKYGBge3IgbXBnLWFubiwgZXZhbCA9IEZBTFNFfQpwIDwtIGdncGxvdChtcGcsIGFlcyh4ID0gZGlzcGwsCiAgICAgICAgICAgICAgICAgICAgIHkgPSBod3kpKQpwMSA8LSBwICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBmYWN0b3IoY3lsKSksCiAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAyLjUpCnAxCmBgYApgYGB7ciBtcGctYW5uLCBlY2hvID0gRkFMU0V9CmBgYAoKQXhpcyBsYWJlbHMgYXJlIGJhc2VkIG9uIHRoZSBleHByZXNzaW9ucyBnaXZlbiB0byBgYWVzYC4KClRoaXMgaXMgY29udmVuaWVudCBmb3IgZXhwbG9yYXRpb24gYnV0IHVzdWFsbHkgbm90IGlkZWFsIGZvciBhIHJlcG9ydC4KClRoZSBgbGFicygpYCBmdW5jdGlvbiBjYW4gYmUgdXNlZCB0byBjaGFuZ2UgYXhpcyBhbmQgbGVnZW5kIGxhYmVsczoKCmBgYHtyIG1wZy1hbm4tbGFicywgZXZhbCA9IEZBTFNFfQpwMSArIGxhYnMoeCA9ICJEaXNwbGFjZW1lbnQgKExpdGVycykiLAogICAgICAgICAgeSA9ICJIaWdod2F5IE1pbGVzIFBlciBHYWxsb24iLAogICAgICAgICAgY29sb3IgPSAiQ3lsaW5kZXJzIikKYGBgCmBgYHtyIG1wZy1hbm4tbGFicywgZWNobyA9IEZBTFNFfQpgYGAKClRoZSBgbGFicygpYCBmdW5jdGlvbiBjYW4gYWxzbyBhZGQgYSB0aXRsZSwgc3VidGl0bGUsIGFuZCBjYXB0aW9uOgoKYGBge3IgbXBnLWFubi1sYWJzLTIsIGV2YWwgPSBGQUxTRX0KcDIgPC0gcDEgKwogICAgbGFicyh4ID0gIkRpc3BsYWNlbWVudCAoTGl0ZXJzKSIsCiAgICAgICAgIHkgPSAiSGlnaHdheSBNaWxlcyBQZXIgR2FsbG9uIiwKICAgICAgICAgY29sb3IgPSAiQ3lsaW5kZXJzIiwKICAgICAgICAgdGl0bGUgPSAiR2FzIE1pbGVhZ2UgYW5kIERpc3BsYWNlbWVudCIsCiAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUoIkZvciBtb2RlbHMgd2hpY2ggaGFkIGEgbmV3IHJlbGVhc2UgZXZlcnkgeWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJiZXR3ZWVuIDE5OTkgYW5kIDIwMDgiKSwKICAgICAgICAgY2FwdGlvbiA9ICJEYXRhIFNvdXJjZTogaHR0cHM6Ly9mdWVsZWNvbm9teS5nb3YvIikKcDIKYGBgCmBgYHtyIG1wZy1hbm4tbGFicy0yLCBlY2hvID0gRkFMU0V9CmBgYAoKQW5ub3RhdGlvbnMgY2FuIGJlIHVzZWQgdG8gcHJvdmlkZSBwb3BvdXQgdGhhdCBkcmF3cyBhIHZpZXdlcidzCmF0dGVudGlvbiB0byBwYXJ0aWN1bGFyIGZlYXR1cmVzLgoKVGhlIGBhbm5vdGF0ZSgpYCBmdW5jdGlvbiBpcyBvbmUgb3B0aW9uOgoKYGBge3IgbXBnLWFubi1wb3BvdXQsIGV2YWwgPSBGQUxTRX0KcDIgKwogICAgYW5ub3RhdGUoImxhYmVsIiwgeCA9IDIuOCwgeSA9IDQzLAogICAgICAgICAgICAgbGFiZWwgPSAiVm9sa3N3YWdlbnMiKSArCiAgICBhbm5vdGF0ZSgicmVjdCIsCiAgICAgICAgICAgICB4bWluID0gMS43LCB4bWF4ID0gMi4xLAogICAgICAgICAgICAgeW1pbiA9IDQwLCB5bWF4ID0gNDUsCiAgICAgICAgICAgICBmaWxsID0gTkEsIGNvbG9yID0gImJsYWNrIikKYGBgCmBgYHtyIG1wZy1hbm4tcG9wb3V0LCBlY2hvID0gRkFMU0V9CmBgYAoKT2Z0ZW4gbW9yZSBjb252ZW5pZW50IGFyZSBzb21lIGBnZW9tX21hcmtgIG9iamVjdHMgcHJvdmlkZWQgYnkgdGhlCmBnZ2ZvcmNlYCBwYWNrYWdlOgoKYGBge3IgbXBnLWFubi1wb3BvdXQtMiwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KGdnZm9yY2UpCnAyICsKICAgIGdlb21fbWFya19odWxsKGFlcyhmaWx0ZXIgPSBjbGFzcyA9PSAiMnNlYXRlciIpLAogICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb24gPQogICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKCIyLVNlYXRlcnMgaGF2ZSBoaWdoIGRpc3BsYWNlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlcywgYnV0IGFsc28gaGlnaCBmdWVsIGVmZmljaWVuY3kiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmb3IgdGhlaXIgZGlzcGxhY2VtZW50LiIpKSArCiAgICBnZW9tX21hcmtfcmVjdChhZXMoZmlsdGVyID0gaHd5ID4gNDApLAogICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb24gPQogICAgICAgICAgICAgICAgICAgICAgICJUaGVzZSBhcmUgVm9sa3N3YWdlbnMiKSArCiAgICBnZW9tX21hcmtfY2lyY2xlKGFlcyhmaWx0ZXIgPSBod3kgPT0gMTIpLAogICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbiA9CiAgICAgICAgICAgICAgICAgICAgICAgICAiVGhyZWUgcGlja3VwcyBhbmQgYW4gU1VWLiIpCmBgYApgYGB7ciBtcGctYW5uLXBvcG91dC0yLCBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA1LjV9CiN8IHdhcm5pbmc6IGZhbHNlCmBgYAoKVGhlc2UgYW5ub3RhdGlvbnMgY2FuIGJlIGN1c3RvbWl6ZWQgaW4gYSBudW1iZXIgb2Ygd2F5cy4KCgojIyBBcnJhbmdpbmcgUGxvdHMKClRoZXJlIGFyZSBzZXZlcmFsIHRvb2xzIGF2YWlsYWJsZSBmb3IgYXNzZW1ibGluZyBlbnNlbWJsZSBwbG90cy4KClRoZSBbYHBhdGNod29ya2BdKGh0dHBzOi8vcGF0Y2h3b3JrLmRhdGEtaW1hZ2luaXN0LmNvbS8pIHBhY2thZ2UgaXMgYQpnb29kIGNob2ljZS4KCkEgc2ltcGxlIGV4YW1wbGU6CgpgYGB7ciBtcGctcGF0Y2h3b3JrLCBldmFsID0gRkFMU0V9CnAxIDwtIGdncGxvdChtcGcsIGFlcyh4ID0gZGlzcGwsCiAgICAgICAgICAgICAgICAgICAgICB5ID0gaHd5KSkgKwogICAgZ2VvbV9wb2ludCgpCnAyIDwtIGdncGxvdChtcGcsIGFlcyh4ID0gY3lsLAogICAgICAgICAgICAgICAgICAgICAgeSA9IGh3eSwKICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gY3lsKSkgKwogICAgZ2VvbV9ib3hwbG90KCkKcDMgPC0gZ2dwbG90KG1wZywgYWVzKHggPSBjeWwpKSArCiAgICBnZW9tX2JhcigpCgpsaWJyYXJ5KHBhdGNod29yaykKKHAxICsgcDIpIC8gcDMKYGBgCmBgYHtyIG1wZy1wYXRjaHdvcmssIGVjaG8gPSBGQUxTRX0KYGBgCgoKIyMgSW50ZXJhY3Rpb24KCgojIyMgUGxvdGx5CgpUaGUgYGdncGxvdGx5YCBmdW5jdGlvbiBpbiB0aGUgW2BwbG90bHlgIHBhY2thZ2VdKGh0dHBzOi8vcGxvdGx5LmNvbS9yLykKY2FuIGJlIHVzZWQgdG8gYWRkIHNvbWUgaW50ZXJhY3RpdmUgZmVhdHVyZXMgdG8gYSBwbG90IGNyZWF0ZWQgd2l0aApgZ2dwbG90MmAuCgoqIEluIGFuIFIgc2Vzc2lvbiBhIGNhbGwgdG8gYGdncGxvdGx5KClgIG9wZW5zIGEgYnJvd3NlciB3aW5kb3cgd2l0aCB0aGUKICBpbnRlcmFjdGl2ZSBwbG90LgoKKiBJbiBhbiBSbWFya2Rvd24gZG9jdW1lbnQgdGhlIGludGVyYWN0aXZlIHBsb3QgaXMgZW1iZWRkZWQgaW4gdGhlCiAgYGh0bWxgIGZpbGUuCgpBbm90aGVyIGludGVyYWN0aXZlIHBsb3R0aW5nIGFwcHJvYWNoIHRoYXQgY2FuIGJlIHVzZWQgZnJvbSBSIGlzCmRlc2NyaWJlZCBpbiBhbiBbSW5mb3dvcmxkCmFydGljbGVdKGh0dHBzOi8vd3d3LmluZm93b3JsZC5jb20vYXJ0aWNsZS8zNjA3MDY4L3Bsb3QtaW4tci13aXRoLWVjaGFydHM0ci5odG1sKS4KCkEgc2ltcGxlIGV4YW1wbGUgdXNpbmcgYGdncGxvdGx5KClgOgoKYGBge3IgbXBnLXBsb3RseSwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocGxvdGx5KQpwIDwtIGdncGxvdChtdXRhdGUobXBnLCBjeWwgPSBmYWN0b3IoY3lsKSkpICsKICAgIGdlb21fcG9pbnQoYWVzKHggPSBkaXNwbCwKICAgICAgICAgICAgICAgICAgIHkgPSBod3ksCiAgICAgICAgICAgICAgICAgICBmaWxsID0gY3lsKSwKICAgICAgICAgICAgICAgc2hhcGUgPSAyMSwKICAgICAgICAgICAgICAgc2l6ZSA9IDMpCmdncGxvdGx5KHApCmBgYApgYGB7ciBtcGctcGxvdGx5LCBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KYGBgCgpBZGRpbmcgYSBgdGV4dGAgYWVzdGhldGljIGFsbG93cyB0aGUgdG9vbHRpcCBkaXNwbGF5IHRvIGJlIGN1c3RvbWl6ZWQ6CgpgYGB7ciBtcGctcGxvdGx5LTIsIGV2YWwgPSBGQUxTRX0KcCA8LSBnZ3Bsb3QobXV0YXRlKG1wZywgY3lsID0gZmFjdG9yKGN5bCkpKSArCiAgICBnZW9tX3BvaW50KGFlcyh4ID0gZGlzcGwsCiAgICAgICAgICAgICAgICAgICB5ID0gaHd5LAogICAgICAgICAgICAgICAgICAgZmlsbCA9IGN5bCwKICAgICAgICAgICAgICAgICAgIHRleHQgPSBwYXN0ZSh5ZWFyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hbnVmYWN0dXJlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbCkpLAogICAgICAgICAgICAgICBzaGFwZSA9IDIxLAogICAgICAgICAgICAgICBzaXplID0gMykKZ2dwbG90bHkocCwgdG9vbHRpcCA9ICJ0ZXh0IikgJT4lCiAgICBzdHlsZShob3ZlcmxhYmVsID0gbGlzdChiZ2NvbG9yID0gIndoaXRlIikpCmBgYApgYGB7ciBtcGctcGxvdGx5LTIsIGVjaG8gPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmBgYAoKCiMjIyBHZ2lyYXBoCgpUaGUgW2BnZ2lyYXBoYCBwYWNrYWdlXShodHRwczovL2RhdmlkZ29oZWwuZ2l0aHViLmlvL2dnaXJhcGgvKQpwcm92aWRlcyBhbm90aGVyIGFwcHJvYWNoLgoKYGBge3IgbXBnLWdnaXJhcGgsIGV2YWwgPSBGQUxTRX0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdnaXJhcGgpCnAgPC0gZ2dwbG90KG11dGF0ZShtcGcsIGN5bCA9IGZhY3RvcihjeWwpKSkgKwogICAgZ2VvbV9wb2ludF9pbnRlcmFjdGl2ZShhZXMoeCA9IGRpc3BsLAogICAgICAgICAgICAgICAgICAgeSA9IGh3eSwKICAgICAgICAgICAgICAgICAgIGZpbGwgPSBjeWwsCiAgICAgICAgICAgICAgICAgICB0b29sdGlwID0gcGFzdGUoeWVhciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYW51ZmFjdHVyZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWwpKSwKICAgICAgICAgICAgICAgc2hhcGUgPSAyMSwKICAgICAgICAgICAgICAgc2l6ZSA9IDMpCmdpcmFmZShnZ29iaiA9IHApCmBgYAoKYGBge3IgbXBnLWdnaXJhcGgsIGVjaG8gPSBGQUxTRX0KYGBgCgoKIyMjIEdyYW1tYXIgb2YgSW50ZXJhY3RpdmUgR3JhcGhpY3MKClRoZXJlIGhhdmUgYmVlbiBzZXZlcmFsIGVmZm9ydHMgdG8gZGV2ZWxvcCBhIGdyYW1tYXIgb2YgaW50ZXJhY3RpdmUKZ3JhcGhpY3MsIGluY2x1ZGluZyBbYGdndmlzYF0oaHR0cHM6Ly9nZ3Zpcy5yc3R1ZGlvLmNvbS8pIGFuZApbYGFuaW1pbnRgXShodHRwczovL3RkaG9jay5naXRodWIuaW8vYW5pbWludC8pOyBuZWl0aGVyIHNlZW1zIHRvIGJlCnVuZGVyIGFjdGl2ZSBkZXZlbG9wbWVudCBhdCB0aGlzIHRpbWUuCgpBIHByb21pc2luZyBhcHByb2FjaCBpcwpbVmVnYS1MaXRlXShodHRwczovL3ZlZ2EuZ2l0aHViLmlvL3ZlZ2EtbGl0ZS8pLCB3aXRoIGEgUHl0aG9uCmludGVyZmFjZSBbQWx0YWlyXShodHRwczovL2FsdGFpci12aXouZ2l0aHViLmlvLykgYW5kIGFuIFIgaW50ZXJmYWNlClthbHRhaXJdKGh0dHBzOi8vdmVnYXdpZGdldC5naXRodWIuaW8vYWx0YWlyLykgdG8gdGhlIFB5dGhvbgppbnRlcmZhY2UuCgpBbiBleGFtcGxlIHVzaW5nIHRoZSBgYWx0YWlyYCBwYWNrYWdlOgoKYGBge3IgcnViYmVyLWFsdGFpciwgZXZhbCA9IEZBTFNFfQpydWIgPC0gcmVhZC5jc3YoaGVyZTo6aGVyZSgicnViYmVyLmNzdiIpKQoKbGlicmFyeShhbHRhaXIpCgpjaGFydFRIIDwtIGFsdCRDaGFydChydWIpJAogICAgbWFya19wb2ludCgpJAogICAgZW5jb2RlKHggPSBhbHQkWCgiSDpRIiwgc2NhbGUgPSBhbHQkU2NhbGUoZG9tYWluID0gcmFuZ2UocnViJEgpKSksCiAgICAgICAgICAgeSA9IGFsdCRZKCJUOlEiLCBzY2FsZSA9IGFsdCRTY2FsZShkb21haW4gPSByYW5nZShydWIkVCkpKSkKCmJydXNoIDwtIGFsdCRzZWxlY3Rpb25faW50ZXJ2YWwoKQoKY2hhcnRUSF9icnVzaCA8LSBjaGFydFRIJGFkZF9zZWxlY3Rpb24oYnJ1c2gpCgpjaGFydFRIX3NlbGVjdGlvbiA8LQogICAgY2hhcnRUSF9icnVzaCRlbmNvZGUoY29sb3IgPSBhbHQkY29uZGl0aW9uKGJydXNoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJPcmlnaW46TiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWx0JHZhbHVlKCJsaWdodGdyYXkiKSkpCgpjaGFydEFUIDwtIGNoYXJ0VEhfc2VsZWN0aW9uJAogICAgZW5jb2RlKHggPSBhbHQkWCgiVDpRIiwgc2NhbGUgPSBhbHQkU2NhbGUoZG9tYWluID0gcmFuZ2UocnViJFQpKSksCiAgICAgICAgICAgeSA9IGFsdCRZKCJBOlEiLCBzY2FsZSA9IGFsdCRTY2FsZShkb21haW4gPSByYW5nZShydWIkQSkpKSkKCmNoYXJ0QVQgfCBjaGFydFRIX3NlbGVjdGlvbgpgYGAKClRoZSByZXN1bHRpbmcgbGlua2VkIHBsb3RzOgoKYGBge3IgcnViYmVyLWFsdGFpciwgZWNobyA9IEZBTFNFLCBlcnJvciA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRX0KYGBgCgoKIyMgTm90ZXMKCiogQSByZWNlbnQgcHJvamVjdAogIFtgZ2dhbmltYXRlYF0oaHR0cHM6Ly9naXRodWIuY29tL3Rob21hc3A4NS9nZ2FuaW1hdGUpIHRvIGFkZAogIGFuaW1hdGlvbiB0byBgZ2dwbG90YCBsb29rcyB2ZXJ5IHByb21pc2luZy4KCiogQSBudW1iZXIgb2Ygb3RoZXIgW2BnZ3Bsb3RgCiAgZXh0ZW5zaW9uc10oaHR0cHM6Ly9leHRzLmdncGxvdDIudGlkeXZlcnNlLm9yZy8pIGFyZSBhdmFpbGFibGUuCgoqIEEgW2Jsb2cKICBwb3N0XShodHRwczovL21lZGl1bS5jb20vYmJjLXZpc3VhbC1hbmQtZGF0YS1qb3VybmFsaXNtL2hvdy10aGUtYmJjLXZpc3VhbC1hbmQtZGF0YS1qb3VybmFsaXNtLXRlYW0td29ya3Mtd2l0aC1ncmFwaGljcy1pbi1yLWVkMGIzNTY5MzUzNSkKICBleHBsYWlucyBob3cgdGhlIFtCQkMgVmlzdWFsIGFuZCBEYXRhCiAgSm91cm5hbGlzbV0oaHR0cHM6Ly9tZWRpdW0uY29tL2JiYy12aXN1YWwtYW5kLWRhdGEtam91cm5hbGlzbSkgdGVhbQogIGNyZWF0ZXMgdGhlaXIgZ3JhcGhpY3MuIE1vcmUgZGV0YWlscyBhcmUgcHJvdmlkZWQgaW4gYW4gW19SIGNvb2sKICBib29rX10oaHR0cHM6Ly9iYmMuZ2l0aHViLmlvL3Jjb29rYm9vay8pLgoKKiBBIFtibG9nCiAgcG9zdF0oaHR0cHM6Ly9ibG9nLnJldm9sdXRpb25hbmFseXRpY3MuY29tLzIwMTYvMDcvZGF0YS1qb3VybmFsaXNtLXdpdGgtci1hdC01MzguaHRtbCkKICBvbiB1c2Ugb2YgUiBhbmQgYGdncGxvdGAgYnkKICBbRml2ZVRoaXJ0eUVpZ2h0XShodHRwczovL2ZpdmV0aGlydHllaWdodC5jb20vKS4gIFRoZSBgZ2d0aGVtZXNgCiAgcGFja2FnZXMgaW5jbHVkZXMgYHRoZW1lX2ZpdmV0aGlydHllaWdodGAgdG8gZW11bGF0ZSB0aGVpciBzdHlsZS4KCgojIyBSZWFkaW5nCgpDaGFwdGVycyBbX0RhdGEKdmlzdWFsaXphdGlvbl9dKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovZGF0YS12aXN1YWxpc2F0aW9uLmh0bWwpIGFuZApbX0dyYXBoaWNzIGZvcgpjb21tdW5pY2F0aW9uX10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9ncmFwaGljcy1mb3ItY29tbXVuaWNhdGlvbi5odG1sKQppbiBbX1IgZm9yIERhdGEgU2NpZW5jZV9dKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovKSwgTydSZWlsbHkuCgpDaGFwdGVyIFtfTWFrZSBhIHBsb3RfXShodHRwczovL3NvY3Zpei5jby9tYWtlcGxvdC5odG1sKSBpbiBbX0RhdGEKVmlzdWFsaXphdGlvbl9dKGh0dHBzOi8vc29jdml6LmNvLykuCgpDaGFwdGVyIFtfZ2dwbG90Ml9dKGh0dHBzOi8vcmFmYWxhYi5kZmNpLmhhcnZhcmQuZWR1L2RzYm9vay9nZ3Bsb3QyLmh0bWwpIGluCltfSW50cm9kdWN0aW9uIHRvIERhdGEgU2NpZW5jZSBEYXRhIEFuYWx5c2lzIGFuZCBQcmVkaWN0aW9uIEFsZ29yaXRobXMKd2l0aCBSX10oaHR0cHM6Ly9yYWZhbGFiLmRmY2kuaGFydmFyZC5lZHUvZHNib29rLykuCgoKIyMgSW50ZXJhY3RpdmUgVHV0b3JpYWwKCkFuIGludGVyYWN0aXZlIFtgbGVhcm5yYF0oaHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9sZWFybnIvKSB0dXRvcmlhbApmb3IgdGhlc2Ugbm90ZXMgaXMgW2F2YWlsYWJsZV0oYHIgV0xOSygidHV0b3JpYWxzL2dncGxvdC5SbWQiKWApLgoKWW91IGNhbiBydW4gdGhlIHR1dG9yaWFsIHdpdGgKCmBgYHtyLCBldmFsID0gRkFMU0V9ClNUQVQ0NTgwOjpydW5UdXRvcmlhbCgiZ2dwbG90IikKYGBgCgpZb3UgY2FuIGluc3RhbGwgdGhlIGN1cnJlbnQgdmVyc2lvbiBvZiB0aGUgYFNUQVQ0NTgwYCBwYWNrYWdlIHdpdGgKCmBgYHtyLCBldmFsID0gRkFMU0V9CnJlbW90ZXM6Omluc3RhbGxfZ2l0bGFiKCJsdWtlLXRpZXJuZXkvU1RBVDQ1ODAiKQpgYGAKCllvdSBtYXkgbmVlZCB0byBpbnN0YWxsIHRoZSBgcmVtb3Rlc2AgcGFja2FnZSBmcm9tIENSQU4gZmlyc3QuCgoKIyMgRXhlcmNpc2VzCgoxLiBJbiB0aGUgZm9sbG93aW5nIGV4cHJlc3Npb24sIHdoaWNoIHZhbHVlIG9mIHRoZSBgc2hhcGVgIGFlc3RoZXRpYwogICBwcm9kdWNlcyBhIHBsb3Qgd2l0aCBwb2ludHMgcmVwcmVzZW50ZWQgYXMgdHJpYW5nbGVzIG91dGxpbmVkIGluCiAgIGJsYWNrIGNvbG9yZWQgYWNjb3JkaW5nIHRvIHRoZSBudW1iZXIgb2YgY3lsaW5kZXJzPwoKPCEtLSAjIyBub2xpbnQgc3RhcnQgLS0+CiAgICBgYGByCiAgICBsaWJyYXJ5KGdncGxvdDIpCiAgICBnZ3Bsb3QobXBnLCBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBmaWxsID0gZmFjdG9yKGN5bCkpKSArCiAgICAgICAgZ2VvbV9wb2ludChzaXplID0gNCwgc2hhcGUgPSAtLS0pCiAgICBgYGAKPCEtLSAjIyBub2xpbnQgZW5kIC0tPgoKICAgIGEuIDE1CiAgICBiLiAxNwogICAgYy4gMjEKICAgIGQuIDI0CgoyLiBJdCBjYW4gc29tZXRpbWVzIGJlIHVzZWZ1bCB0byBwbG90IHRleHQgbGFiZWxzIGluIGEgc2NhdHRlcnBsb3QKICAgaW5zdGVhZCBvZiBwb2ludHMuIENvbnNpZGVyIHRoZSBwbG90IHNldCB1cCBhcwoKICAgIGBgYHIKICAgIGxpYnJhcnkoZ2dwbG90MikKICAgIGxpYnJhcnkoZHBseXIpCiAgICBkYXRhKGdhcG1pbmRlciwgcGFja2FnZSA9ICJnYXBtaW5kZXIiKQogICAgcCA8LSBmaWx0ZXIoZ2FwbWluZGVyLCB5ZWFyID09IDIwMDcpICU+JQogICAgICAgIGdyb3VwX2J5KGNvbnRpbmVudCkgJT4lCiAgICAgICAgc3VtbWFyaXplKGdkcFBlcmNhcCA9IG1lYW4oZ2RwUGVyY2FwKSwgbGlmZUV4cCA9IG1lYW4obGlmZUV4cCkpICU+JQogICAgICAgIGdncGxvdChhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHApKQogICAgYGBgCiAgICBXaGljaCBvZiB0aGUgZm9sbG93aW5nIHByb2R1Y2VzIGEgcGxvdCB3aXRoIGNvbnRpbmVudAogICAgbmFtZXMgb24gd2hpdGUgcmVjdGFuZ2xlcz8KCiAgICBhLiBgcCArIGdlb21fdGV4dChhZXMobGFiZWwgPSBjb250aW5lbnQpKWAKICAgIGIuIGBwICsgZ2VvbV9sYWJlbChhZXMobGFiZWwgPSBjb250aW5lbnQpKWAKICAgIGMuIGBwICsgZ2VvbV9sYWJlbChsYWJlbCA9IGNvbnRpbmVudClgCiAgICBkLiBgcCArIGdlb21fdGV4dCh0ZXh0ID0gY29udGluZW50KWAKCjMuIFRoZSBmb2xsb3dpbmcgY29kZSBwbG90cyBhIF9rZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZV8gZm9yIHRoZQogICBgZXJ1cHRpb25zYCB2YXJpYWJsZSBpbiB0aGUgYGZhaXRoZnVsYCBkYXRhIHNldDoKCiAgICBgYGByCiAgICBsaWJyYXJ5KGdncGxvdDIpCiAgICBnZ3Bsb3QoZmFpdGhmdWwsIGFlcyh4ID0gZXJ1cHRpb25zKSkgKyBnZW9tX2RlbnNpdHkoYncgPSAwLjEpCiAgICBgYGAKICAgIExvb2sgYXQgdGhlIGhlbHAgcGFnZSBmb3IgYGdlb21fZGVuc2l0eWAuIFdoaWNoIG9mIHRoZSBmb2xsb3dpbmcgYmVzdAogICAgZGVzY3JpYmVzIHdoYXQgc3BlY2lmeWluZyBhIHZhbHVlIGZvciBgYndgIGRvZXM6CgogICAgYS4gQ2hhbmdlcyB0aGUgX2tlcm5lbF8gdXNlZCB0byBjb25zdHJ1Y3QgdGhlIGVzdGltYXRlLgogICAgYi4gQ2hhbmdlcyB0aGUgX3Ntb290aGluZyBiYW5kd2lkdGhfIHRvIG1ha2UgdGhlIHJlc3VsdCBtb3JlIG9yIGxlc3Mgc21vb3RoLgogICAgYy4gQ2hhbmdlcyB0aGUgYHN0YXRgIHVzZWQgdG8gYHN0YXRfYndgLgogICAgZC4gSGFzIG5vIGVmZmVjdCBvbiB0aGUgcmV0dWx0LgoKNC4gVGhpcyBjb2RlIGNyZWF0ZXMgYSBtYXAgb2YgSW93YSBjb3VudGllcy4KCiAgICBgYGByCiAgICBsaWJyYXJ5KGdncGxvdDIpCiAgICBwIDwtIGdncGxvdChtYXBfZGF0YSgiY291bnR5IiwgImlvd2EiKSwKICAgICAgICAgICAgICAgIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCkpICsKICAgICAgICBnZW9tX3BvbHlnb24oLCBmaWxsID0gIldoaXRlIiwgY29sb3IgPSAiYmxhY2siKQogICAgYGBgCiAgIAogICAgV2hpY2ggb2YgdGhlc2UgcHJvZHVjZXMgYSBwbG90IHdpdGggYW4gYXNwZWN0IHJhdGlvIHRoYXQgYmVzdAogICAgbWF0Y2hlcyB0aGUgbWFwIG9uIFt0aGlzCiAgICBwYWdlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvdy9pbmRleC5waHA/dGl0bGU9TGlzdF9vZl9jb3VudGllc19pbl9Jb3dhJm9sZGlkPTEwMDExNzEwODIpPwoKICAgIGEuIGBwYCAKICAgIGIuIGBwICsgY29vcmRfZml4ZWQoMC43NSlgCiAgICBjLiBgcCArIGNvb3JkX2ZpeGVkKDEuMjUpYAogICAgZC4gYHAgKyBjb29yZF9maXhlZCgyKWAKCjUuIENvbnNpZGVyIHRoZSB0d28gcGxvdHMgY3JlYXRlZCBieSB0aGlzIGNvZGUgKHByaW50IHRoZSB2YWx1ZXMgb2YKICAgYHAxYCBhbmQgYHAyYCB0byBzZWUgdGhlIHBsb3RzKToKCiAgICBgYGByCiAgICBsaWJyYXJ5KGdncGxvdDIpCiAgICBkYXRhKGdhcG1pbmRlciwgcGFja2FnZSA9ICJnYXBtaW5kZXIiKQogICAgcDEgPC0gZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSBsb2coZ2RwUGVyY2FwKSwgeSA9IGxpZmVFeHApKSArCiAgICAgICAgZ2VvbV9wb2ludCgpICsKICAgICAgICBzY2FsZV94X2NvbnRpbnVvdXMobmFtZSA9ICIiKQogICAgcDIgPC0gZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSBnZHBQZXJjYXAsIHkgPSBsaWZlRXhwKSkgKwogICAgICAgIGdlb21fcG9pbnQoKSArCiAgICAgICAgc2NhbGVfeF9sb2cxMChsYWJlbHMgPSBzY2FsZXM6OmNvbW1hLCBuYW1lID0gIiIpIAogICAgYGBgCgogICAgV2hpY2ggb2YgdGhlc2Ugc3RhdGVtZW50cyBpcyB0cnVlPwoKICAgIGEuIFRoZSBgeGAgYXhpcyBsYWJlbHMgYXJlIGlkZW50aWNhbCBpbiBib3RoIHBsb3RzLgogICAgYi4gVGhlIGB4YCBheGlzIGxhYmVscyBpbiBgcDJgIGFyZSBpbiBkb2xsYXJzOyB0aGUgbGFiZWxzIGluIGBwMWAKICAgICAgIGFyZSBpbiBsb2cgZG9sbGFycy4gCiAgICBjLiBUaGUgYHhgIGF4aXMgbGFiZWxzIGluIGBwMWAgYXJlIGluIGRvbGxhcnM7IHRoZSBsYWJlbHMgaW4gYHAyYAogICAgICAgYXJlIGluIGxvZyBkb2xsYXJzLgogICAgZC4gVGhlcmUgYXJlIG5vIGxhYmVscyBvbiB0aGUgYHhgIGF4aXMgaW4gYHAyYC4KCjYuIENvbnNpZGVyIHRoZSBwbG90IGNyZWF0ZWQgYnkKCiAgICBgYGByCiAgICBsaWJyYXJ5KGdncGxvdDIpCiAgICBkYXRhKGdhcG1pbmRlciwgcGFja2FnZSA9ICJnYXBtaW5kZXIiKQogICAgcCA8LSBnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHApKSArCiAgICAgICAgZ2VvbV9wb2ludCgpICsKICAgICAgICBzY2FsZV94X2xvZzEwKGxhYmVscyA9IHNjYWxlczo6Y29tbWEpIAogICAgYGBgCgogICAgV2hpY2ggb2YgdGhlc2UgZXhwcmVzc2lvbnMgcHJvZHVjZXMgYSBwbG90IHdpdGggYSB3aGl0ZSBiYWNrZ3JvdW5kPwoKICAgIGEuIGBwYAogICAgYi4gYHAgKyB0aGVtZV9ncmV5KClgCiAgICBjLiBgcCArIHRoZW1lX2NsYXNzaWMoKWAKICAgIGQuIGBwICsgZ2d0aGVtZXM6OnRoZW1lX2Vjb25vbWlzdCgpYAoKNy4gVGhlcmUgYXJlIG1hbnkgZGlmZmVyZW50IHdheXMgdG8gY2hhbmdlIHRoZSBgeGAgYXhpcyBsYWJlbCBpbgogICBgZ2dwbG90YC4gIENvbnNpZGVyIHRoZSBwbG90IGNyZWF0ZWQgYnkKCiAgICBgYGByCiAgICBsaWJyYXJ5KGdncGxvdDIpCiAgICBwIDwtIGdncGxvdChtcGcsIGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKSArCiAgICAgICAgZ2VvbV9wb2ludCgpCiAgICBgYGAKCiAgICBXaGljaCBvZiB0aGUgZm9sbG93aW5nIGRvZXMgKipub3QqKiBjaGFuZ2UgdGhlIGB4YCBheGlzIGxhYmVsIHRvCiAgICBfRGlzcGxhY2VtZW50Xz8KCiAgICBhLiBgcCArIGxhYnMoeCA9ICJEaXNwbGFjZW1lbnQiKWAKICAgIGIuIGBwICsgc2NhbGVfeF9jb250aW51b3VzKCJEaXNwbGFjZW1lbnQiKWAKICAgIGMuIGBwICsgeGxhYigiRGlzcGxhY2VtZW50IilgCiAgICBkLiBgcCArIHRoZW1lKGF4aXMudGl0bGUueCA9ICJEaXNwbGFjZW1lbnQiKWAKCg==