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
a data set;
one or more geometrical representation (geoms );
mappings of values to aesthetic features of the geom;
a stat to produce values to be mapped;
position adjustments;
a coordinate system;
a scale specification;
a faceting scheme.
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.
For geom_point
the default stat is stat_identity
.
For geom_bar
the default stat is stat_count
.
For geom_histogram
the default is stat_bin
.
Stats can provide computed variables that can be mapped to aesthetic features.
For stat_bin
some of the computed variables are
count
: number of points in bin
density
: density of points in bin, scaled to integrate to 1
The density
variable can be accessed as after_stat(dentity)
.
Older approaches that also work but are now discouraged:
stat(dentity)
..density..
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
axis labels and marks;
color or shape legends.
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:
theme()
can be used to adjust individual elements in a plot.
theme_set()
adjusts default settings for a session;
pre-defined theme functions allow consistent style changes.
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
.
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:
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
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
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?
p + geom_text(aes(label = continent))
p + geom_label(aes(label = continent))
p + geom_label(label = continent)
p + geom_text(text = continent)
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:
Changes the kernel used to construct the estimate.
Changes the smoothing bandwidth to make the result more or less smooth.
Changes the stat
used to stat_bw
.
Has no effect on the retult.
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 ?
p
p + coord_fixed(0.75)
p + coord_fixed(1.25)
p + coord_fixed(2)
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?
The x
axis labels are identical in both plots.
The x
axis labels in p2
are in dollars; the labels in p1
are in log dollars.
The x
axis labels in p1
are in dollars; the labels in p2
are in log dollars.
There are no labels on the x
axis in p2
.
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?
p
p + theme_grey()
p + theme_classic()
p + ggthemes::theme_economist()
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 ?
p + labs(x = "Displacement")
p + scale_x_continuous("Displacement")
p + xlab("Displacement")
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==