As Training Workbook for ggplot2
Author: Naki Cam, myBI, DPDHL IT Services GmbH Reference: Hadley Wickham, R for Data Science
notice: feel free to share !
Prerequisites:
Download & Install RStudio Download & Install Microsoft R Open or other R Distribution install packages with using following code: install.packages(tidyverse)
# loading tidyverse framework
library(tidyverse)
Loading tidyverse: ggplot2
Loading tidyverse: tibble
Loading tidyverse: tidyr
Loading tidyverse: readr
Loading tidyverse: purrr
Loading tidyverse: dplyr
Conflicts with tidy packages -----------------------------------------------------------------------------
filter(): dplyr, stats
lag(): dplyr, stats
head(mpg)
# creating ggplot chart
ggplot(data = mpg, mapping = aes(displ, hwy)) + geom_point()

# exercise
# will still be empty
ggplot(data = mpg)

# how many rows and columns
dim(mpg)
[1] 234 11
# asking for help to mpg dataset
?mpg
# sample
ggplot(data = mpg) + geom_point(aes(hwy, cyl))


?geom_point
ggplot(data = mpg) + geom_point(aes(x = displ, y = hwy, color = class), stroke = 3)

ggplot(data = mpg) + geom_point(aes(displ, hwy, color = class, stroke = displ > 5))

ggplot(data = mpg) + geom_point(aes(displ, hwy, color = hwy > 30 & displ > 2)) + facet_wrap(~class)

ggplot(data = mpg) + geom_point(aes(displ, hwy, color = hwy > 30 & displ > 2)) + facet_wrap(~class, nrow = 2)

head(mpg)
ggplot(data = mpg) + geom_point(aes(displ, hwy), color = "blue") + facet_wrap(drv~cyl, nrow = 2)


ggplot(data = mpg) +
geom_point(aes(displ, hwy, color = class)) +
facet_grid(.~cyl)

# exercise
# 1 nach einer kontinuirlichen Variablen facetten erzeugen -> keine gute idee ..
ggplot(data = mpg) +
geom_point(aes(displ, hwy, color = class)) +
facet_grid(.~hwy)

# 2
ggplot(data = mpg) +
geom_point(aes(drv, cyl)) +
facet_grid(drv~cyl)

# 3
ggplot(data = mpg) +
geom_point(aes(displ, hwy)) +
facet_grid(drv~.)

ggplot(data = mpg) +
geom_point(aes(displ, hwy)) +
facet_grid(.~drv)

ggplot(data = mpg) +
geom_point(aes(displ, hwy)) +
facet_grid(.~cyl)

ggplot(data = mpg) +
geom_point(aes(displ, hwy)) +
facet_grid(cyl~.)


?facet_wrap
# nrow
# ncol
# welche anderen parameter
Facet Wrap
facet_wrap wraps a 1d sequence of panels into 2d. This is generally a better use of screen space than facet_grid because most displays are roughly rectangular.
Template
facet_wrap(facets, nrow = NULL, ncol = NULL, scales = “fixed”,shrink = TRUE, labeller = “label_value”, as.table = TRUE,switch = NULL, drop = TRUE, dir = “h”, strip.position = “top”)
?facet_grid
# why no nrow and ncol parameters?
Facet Grid
facet_grid forms a matrix of panels defined by row and column facetting variables. It is most useful when you have two discrete variables, and all combinations of the variables exist in the data.
Template
facet_grid(facets, margins = FALSE, scales = “fixed”, space = “fixed”,shrink = TRUE, labeller = “label_value”, as.table = TRUE,switch = NULL, drop = TRUE)
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy))

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

NA
# different lines in geom_smooth by parameter "linetype = value""
ggplot(data = mpg) +
geom_smooth(mapping = aes(x = displ, y = hwy, linetype=drv))

# # 0 = blank, 1 = solid, 2 = dashed, 3 = dotted, 4 = dotdash, 5 = longdash, 6 = twodash
ggplot(data = mpg) +
geom_smooth(mapping = aes(x = displ, y = hwy), linetype=2)

Description
Aids the eye in seeing patterns in the presence of overplotting. geom_smooth and stat_smooth are effectively aliases: they both use the same arguments. Use geom_smooth unless you want to display the results with a non-standard geom.
Usage
geom_smooth(mapping = NULL, data = NULL, stat = “smooth”, position = “identity”, …, method = “auto”, formula = y ~ x, se = TRUE, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE)
stat_smooth(mapping = NULL, data = NULL, geom = “smooth”, position = “identity”, …, method = “auto”, formula = y ~ x, se = TRUE, n = 80, span = 0.75, fullrange = FALSE, level = 0.95, method.args = list(), na.rm = FALSE, show.legend = NA, inherit.aes = TRUE)
# using stat_smoth
ggplot(data = mpg) + stat_smooth(aes(displ, hwy, linetype=drv),
show.legend = FALSE)

# using multiple geom functions in one chart
ggplot(data = mpg) +
geom_point(aes(displ, hwy)) +
geom_smooth(aes(displ, hwy))

# same but global and less code
ggplot(data = mpg, mapping = aes(x = displ, y= hwy)) + geom_point() + geom_smooth()

# another example but added parameters
ggplot(data = mpg, mapping = aes(x = displ, y= hwy)) +
## changed colors
geom_point( mapping = aes(color=class)) +
## changes to geom line only for subset!
geom_smooth(data = filter(mpg, class == "subcompact"),se = FALSE)

#exercise
# brainstorming
g <- ggplot(data = mpg, mapping = aes(x = displ, y= hwy, color = drv))
g + geom_point() + geom_smooth(se=FALSE)

# Comparison between programming logic -> almost the same output chart
ggplot(data = mpg, mapping = aes(x = displ, y= hwy, color = drv)) + geom_point() + geom_smooth()

ggplot(data = mpg, mapping = aes(x = displ, y= hwy, color = drv)) +
geom_point( data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_smooth(data = mpg, mapping = aes(x = displ, y = hwy))

# exercise: Program Code for shown plots
# without splitting into legends
ggplot(data = mpg, mapping = aes(x = displ, y= hwy)) + geom_point() + geom_smooth(se=FALSE)

# with legends
ggplot(data = mpg, mapping = aes(x = displ, y= hwy)) + geom_point() + geom_smooth(data = filter(mpg, drv == "4"), se=FALSE) +
geom_smooth(data = filter(mpg, drv == "f"),se=FALSE) +
geom_smooth(data = filter(mpg, drv == "r"),se=FALSE)

Bar charts
Description:
There are two types of bar charts: geom_bar makes the height of the bar proportional to the number of cases in each group (or if the weight aethetic is supplied, the sum of the weights). If you want the heights of the bars to represent values in the data, use geom_col instead. geom_bar uses stat_count by default: it counts the number of cases at each x position. geom_col uses stat_identity: it leaves the data as is.
Usage:
geom_bar(mapping = NULL, data = NULL, stat = “count”, position = “stack”, …, width = NULL, binwidth = NULL, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE)
geom_col(mapping = NULL, data = NULL, position = “stack”, …, width = NULL, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE)
stat_count(mapping = NULL, data = NULL, geom = “bar”, position = “stack”, …, width = NULL, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE)
# using geom_bar function
ggplot(data = diamonds) + geom_bar(mapping = aes(x = cut))

# changing related computed variables
ggplot(data = diamonds) +
geom_bar(aes(cut, ..prop.., group = 1))

# using stat_summary for descriptive statistics
ggplot(data = diamonds) + stat_summary(aes(cut, depth), fun.ymin = min, fun.ymax = max, fun.y = median)

# list of other transformations available in ggplot2
# f.e.
?stat_bin
Summarise y values at unique/binned x
Description:
stat_summary operates on unique x; stat_summary_bin operators on binned x. They are more flexible versions of stat_bin: instead of just counting, they can compute any aggregate.
Usage:
stat_summary_bin(mapping = NULL, data = NULL, geom = “pointrange”, position = “identity”, …, fun.data = NULL, fun.y = NULL, fun.ymax = NULL, fun.ymin = NULL, fun.args = list(), na.rm = FALSE, show.legend = NA, inherit.aes = TRUE)
stat_summary(mapping = NULL, data = NULL, geom = “pointrange”, position = “identity”, …, fun.data = NULL, fun.y = NULL, fun.ymax = NULL, fun.ymin = NULL, fun.args = list(), na.rm = FALSE, show.legend = NA, inherit.aes = TRUE)
# exersice
# using geom_pointrange for same plot like before
ggplot(data = diamonds) +
geom_pointrange(mapping = aes(x = cut, y = depth),
stat = "summary",
fun.ymin = min,
fun.ymax = max,
fun.y = median)

# using geom_col vs geom_bar
ggplot(data = diamonds) + geom_col(aes(cut, depth)) + scale_y_continuous(labels = scales::comma)

ggplot(data = diamonds) + geom_bar(aes(cut))

?stat_smooth
# what is wrong with this plots
ggplot(diamonds) + geom_bar(aes(cut,y = ..prop..))

ggplot(diamonds) + geom_bar(aes(cut, fill = color,y = ..prop..))

ggplot(diamonds) + geom_bar(aes(cut,y = ..prop.., group = TRUE))

ggplot(diamonds) + geom_bar(aes(cut, fill = color,y = ..prop..,group = 1))

ggplot(diamonds) + geom_bar(aes(cut, fill = color))

4 Position Functions
# further color functions on geom_bar
# stacked bar chart
ggplot(diamonds) + geom_bar(aes(cut, fill = clarity),position = "identity")

# clustered bar chart
ggplot(diamonds) + geom_bar(aes(cut, fill = clarity),position = "dodge")

# 100% stacked bar chart
ggplot(diamonds) + geom_bar(aes(cut, fill = clarity),position = "fill")

# Jitter Chart with "position = jitter" & geom_point
ggplot(diamonds) + geom_point(aes(cut,depth, color = clarity),position = "jitter")

# Or short version
# Jitter Chart with "position = jitter" & geom_point
ggplot(diamonds) + geom_jitter(aes(cut,depth, color = clarity))


#exercise
# 1
ggplot(mpg, mapping = aes(cty, hwy)) + geom_point(aes(color = class)) + geom_smooth(method = lm, se=F)

ggplot(mpg, mapping = aes(cty, hwy)) + geom_jitter(aes(cty,hwy, color = class), alpha = 0.6, size = 4) + geom_smooth(method = lm, se=F)

# 3
# standard geom_jitter chart
ggplot(mpg, mapping = aes(cty, hwy)) + geom_jitter(aes(color = class))

# bubble chart with geom_jitter
ggplot(mpg, mapping = aes(cty, hwy)) + geom_count(aes(color = class))

# 4
# default position = "dodge"
ggplot(mpg, aes(cty, hwy)) + geom_boxplot(aes(drv, color = drv))


Cartesian coordinates with x and y flipped
Description:
Flip cartesian coordinates so that horizontal becomes vertical, and vertical, horizontal. This is primarily useful for converting geoms and statistics which display y conditional on x, to x conditional on y.
Usage:
coord_flip(xlim = NULL, ylim = NULL, expand = TRUE) Arguments
xlim: Limits for the x and y axes. ylim: Limits for the x and y axes. expand: If TRUE, the default, adds a small expansion factor to the limits to ensure that data and axes don’t overlap. If FALSE, limits are taken exactly from the data or xlim/ylim.
nz <- map_data("nz")
Error: Package `maps` required for `map_data`.
Please install and try again.
Map projections
Description:
coord_map projects a portion of the earth, which is approximately spherical, onto a flat 2D plane using any projection defined by the mapproj package. Map projections do not, in general, preserve straight lines, so this requires considerable computation. coord_quickmap is a quick approximation that does preserve straight lines. It works best for smaller areas closer to the equator.
Usage:
coord_map(projection = “mercator”, …, parameters = NULL, orientation = NULL, xlim = NULL, ylim = NULL)
coord_quickmap(xlim = NULL, ylim = NULL, expand = TRUE)
bar <- ggplot(diamonds) +
geom_bar(aes(cut, fill=cut), show.legend = FALSE, width = 1) + theme(aspect.ratio = 1) + labs(x = NULL, y = NULL)
bar + coord_flip()

#
bar + coord_polar(direction = 1)

Polar coordinates
Description:
The polar coordinate system is most commonly used for pie charts, which are a stacked bar chart in polar coordinates.
Usage:
coord_polar(theta = “x”, start = 0, direction = 1) Arguments
theta:
variable to map angle to (x or y) start:
offset of starting point from 12 o’clock in radians direction:
1, clockwise; -1, anticlockwise
# exercise
# 1 from stacked chart to polar chart
ggplot(diamonds) + geom_bar(aes(cut, fill = clarity),position = "identity", alpha = 0.2)

ggplot(diamonds) + geom_bar(aes(cut, fill = clarity),position = "identity", alpha = 0.2) + coord_polar()

Modify axis, legend, and plot labels
Description:
Good labels are critical for making your plots accessible to a wider audience. Ensure the axis and legend labels display the full variable name. Use the plot title and subtitle to explain the main findings. It’s common to use the caption to provide information about the data source.
Usage:
labs(…)
xlab(label)
ylab(label)
ggtitle(label, subtitle = NULL)
ggplot( data = mpg, mapping = aes( x = cty, y = hwy)) + geom_point() + geom_abline() + coord_fixed()

Reference lines: horizontal, vertical, and diagonal
Description:
These geoms add reference lines (sometimes called rules) to a plot, either horizontal, vertical, or diagonal (specified by slope and intercept). These are useful for annotating plots.
Usage:
geom_abline(mapping = NULL, data = NULL, …, slope, intercept, na.rm = FALSE, show.legend = NA)
geom_hline(mapping = NULL, data = NULL, …, yintercept, na.rm = FALSE, show.legend = NA)
geom_vline(mapping = NULL, data = NULL, …, xintercept, na.rm = FALSE, show.legend = NA)
Cartesian coordinates with fixed “aspect ratio”
Description:
A fixed scale coordinate system forces a specified ratio between the physical representation of data units on the axes. The ratio represents the number of units on the y-axis equivalent to one unit on the x-axis. The default, ratio = 1, ensures that one unit on the x-axis is the same length as one unit on the y-axis. Ratios higher than one make units on the y axis longer than units on the x-axis, and vice versa. This is similar to eqscplot, but it works for all types of graphics.
Usage:
coord_fixed(ratio = 1, xlim = NULL, ylim = NULL, expand = TRUE)
Final Template for ggplot
ggplot( data = < DATA >) +
< GEOM_FUNCTION >( mapping = aes( < MAPPINGS >), stat = < STAT >, position = < POSITION > ) +
< COORDINATE_FUNCTION > +
< FACET_FUNCTION >
LS0tDQp0aXRsZTogInRpZHl2ZXJzZSBnZ3Bsb3QyIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCkFzIFRyYWluaW5nIFdvcmtib29rIGZvciBnZ3Bsb3QyDQoNCkF1dGhvcjogTmFraSBDYW0sIG15QkksIERQREhMIElUIFNlcnZpY2VzIEdtYkgNClJlZmVyZW5jZTogSGFkbGV5IFdpY2toYW0sIFIgZm9yIERhdGEgU2NpZW5jZQ0KDQpub3RpY2U6IGZlZWwgZnJlZSB0byBzaGFyZSAhDQoNCg0KIyBQcmVyZXF1aXNpdGVzOg0KRG93bmxvYWQgJiBJbnN0YWxsIFJTdHVkaW8NCkRvd25sb2FkICYgSW5zdGFsbCBNaWNyb3NvZnQgUiBPcGVuIG9yIG90aGVyIFIgRGlzdHJpYnV0aW9uDQppbnN0YWxsIHBhY2thZ2VzIHdpdGggdXNpbmcgZm9sbG93aW5nIGNvZGU6DQppbnN0YWxsLnBhY2thZ2VzKHRpZHl2ZXJzZSkNCg0KDQpgYGB7cn0NCiMgbG9hZGluZyB0aWR5dmVyc2UgZnJhbWV3b3JrDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQpgYGB7cn0NCmhlYWQobXBnKQ0KYGBgDQoNCmBgYHtyfQ0KIyBjcmVhdGluZyBnZ3Bsb3QgY2hhcnQNCmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKGRpc3BsLCBod3kpKSArIGdlb21fcG9pbnQoKQ0KYGBgDQoNCmBgYHtyfQ0KIyBleGVyY2lzZQ0KDQojIHdpbGwgc3RpbGwgYmUgZW1wdHkNCmdncGxvdChkYXRhID0gbXBnKQ0KDQojIGhvdyBtYW55IHJvd3MgYW5kIGNvbHVtbnMNCmRpbShtcGcpDQoNCiMgYXNraW5nIGZvciBoZWxwIHRvIG1wZyBkYXRhc2V0DQo/bXBnDQoNCiMgc2FtcGxlDQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyBnZW9tX3BvaW50KGFlcyhod3ksIGN5bCkpDQoNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnKSArIGdlb21fcG9pbnQoYWVzKHggPSBkaXNwbCwgeSA9IGh3eSwgY29sb3IgPSBjbGFzcykpDQpgYGANCg0KYGBge3J9DQojIHVzaW5nIHBhcmFtZXRlciBzdHJva2Ugd2l0aG91dCBjb25kaXRpb25zDQo/Z2VvbV9wb2ludA0KZ2dwbG90KGRhdGEgPSBtcGcpICsgZ2VvbV9wb2ludChhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBjb2xvciA9IGNsYXNzKSwgc3Ryb2tlID0gMykNCmBgYA0KYGBge3J9DQojIHVzaW5nIHBhcmFtZXRlciBzdHJva2Ugd2l0aCBjb25kaXRpb24NCmdncGxvdChkYXRhID0gbXBnKSArIGdlb21fcG9pbnQoYWVzKGRpc3BsLCBod3ksIGNvbG9yID0gY2xhc3MsIHN0cm9rZSA9IGRpc3BsID4gNSkpDQpgYGANCg0KDQpgYGB7cn0NCiMgc2FtcGxlcyB3aXRoIHVzaW5nIGZhY2V0X3dyYXAgZnVuY3Rpb24NCmdncGxvdChkYXRhID0gbXBnKSArIGdlb21fcG9pbnQoYWVzKGRpc3BsLCBod3ksIGNvbG9yID0gaHd5ID4gMzAgJiBkaXNwbCA+IDIpKSArIGZhY2V0X3dyYXAofmNsYXNzKQ0KDQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyBnZW9tX3BvaW50KGFlcyhkaXNwbCwgaHd5LCBjb2xvciA9IGh3eSA+IDMwICYgZGlzcGwgPiAyKSkgKyBmYWNldF93cmFwKH5jbGFzcywgbnJvdyA9IDIpDQpgYGANCg0KYGBge3J9DQojIHNhbXBsZXMgd2l0aCB1c2luZyBmYWNldF93cmFwIGZ1bmN0aW9uDQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyBnZW9tX3BvaW50KGFlcyhkaXNwbCwgaHd5KSwgY29sb3IgPSAiYmx1ZSIpICsgZmFjZXRfd3JhcChkcnZ+Y3lsLCBucm93ID0gMikgDQpgYGANCg0KYGBge3J9DQojIHNhbXBsZXMgd2l0aCB1c2luZyBmYWNldF9ncmlkIGZ1bmN0aW9uDQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyANCiAgZ2VvbV9wb2ludChhZXMoZGlzcGwsIGh3eSwgY29sb3IgPSBjbGFzcykpICsgDQogIGZhY2V0X2dyaWQoZHJ2fmN5bCkgDQpgYGANCg0KYGBge3J9DQojIHNhbXBsZXMgd2l0aCB1c2luZyBmYWNldF9ncmlkIGZ1bmN0aW9uDQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyANCiAgZ2VvbV9wb2ludChhZXMoZGlzcGwsIGh3eSwgY29sb3IgPSBjbGFzcykpICsgDQogIGZhY2V0X2dyaWQoLn5jeWwpIA0KYGBgDQoNCmBgYHtyfQ0KIyBleGVyY2lzZQ0KDQojIDEgZmFjZXRzIHdpdGggYWVzIHRvIGNvbnRpbm91cyB2YXJpYWJsZSAtPiBub3QgYSBnb29kIGlkZWEuDQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyANCiAgZ2VvbV9wb2ludChhZXMoZGlzcGwsIGh3eSwgY29sb3IgPSBjbGFzcykpICsgDQogIGZhY2V0X2dyaWQoLn5od3kpIA0KDQojIDINCg0KZ2dwbG90KGRhdGEgPSBtcGcpICsgDQogIGdlb21fcG9pbnQoYWVzKGRydiwgY3lsKSkgKyANCiAgZmFjZXRfZ3JpZChkcnZ+Y3lsKSANCg0KIyAzDQoNCmdncGxvdChkYXRhID0gbXBnKSArIA0KICBnZW9tX3BvaW50KGFlcyhkaXNwbCwgaHd5KSkgKyANCiAgZmFjZXRfZ3JpZChkcnZ+LikgDQoNCmdncGxvdChkYXRhID0gbXBnKSArIA0KICBnZW9tX3BvaW50KGFlcyhkaXNwbCwgaHd5KSkgKyANCiAgZmFjZXRfZ3JpZCgufmRydikgDQoNCmBgYA0KDQpgYGB7cn0NCiMgYWx0aG91Z2ggdG8gMw0KZ2dwbG90KGRhdGEgPSBtcGcpICsgDQogIGdlb21fcG9pbnQoYWVzKGRpc3BsLCBod3kpKSArIA0KICBmYWNldF9ncmlkKC5+Y3lsKSANCg0KZ2dwbG90KGRhdGEgPSBtcGcpICsgDQogIGdlb21fcG9pbnQoYWVzKGRpc3BsLCBod3kpKSArIA0KICBmYWNldF9ncmlkKGN5bH4uKSANCmBgYA0KDQpgYGB7cn0NCiMgNA0KZ2dwbG90KGRhdGEgPSBtcGcpICsgDQogIGdlb21fcG9pbnQoYWVzKGRpc3BsLCBod3kpKSArIA0KICBmYWNldF93cmFwKH5jbGFzcywgbnJvdyA9IDIpIA0KDQpgYGANCg0KYGBge3J9DQo/ZmFjZXRfd3JhcA0KIyBucm93DQojIG5jb2wNCiMgd2hpY2ggb3RoZXIgcGFyYW1ldGVycyBhcmUgYXZhaWxhYmxlPw0KDQpgYGANCiMgRmFjZXQgV3JhcA0KZmFjZXRfd3JhcCB3cmFwcyBhIDFkIHNlcXVlbmNlIG9mIHBhbmVscyBpbnRvIDJkLiBUaGlzIGlzIGdlbmVyYWxseSBhIGJldHRlciB1c2Ugb2Ygc2NyZWVuIHNwYWNlIHRoYW4gZmFjZXRfZ3JpZCBiZWNhdXNlIG1vc3QgZGlzcGxheXMgYXJlIHJvdWdobHkgcmVjdGFuZ3VsYXIuDQoNCg0KIyMjIFRlbXBsYXRlDQpmYWNldF93cmFwKGZhY2V0cywgbnJvdyA9IE5VTEwsIG5jb2wgPSBOVUxMLCBzY2FsZXMgPSAiZml4ZWQiLHNocmluayA9IFRSVUUsIGxhYmVsbGVyID0gImxhYmVsX3ZhbHVlIiwgYXMudGFibGUgPSBUUlVFLHN3aXRjaCA9IE5VTEwsIGRyb3AgPSBUUlVFLCBkaXIgPSAiaCIsIHN0cmlwLnBvc2l0aW9uID0gInRvcCIpDQoNCg0KYGBge3J9DQo/ZmFjZXRfZ3JpZA0KIyB3aHkgYXJlIG5vIG5yb3cgYW5kIG5jb2wgcGFyYW1ldGVycyBhdmFpbGFibGU/DQoNCmBgYA0KIyBGYWNldCBHcmlkDQpmYWNldF9ncmlkIGZvcm1zIGEgbWF0cml4IG9mIHBhbmVscyBkZWZpbmVkIGJ5IHJvdyBhbmQgY29sdW1uIGZhY2V0dGluZyB2YXJpYWJsZXMuIEl0IGlzIG1vc3QgdXNlZnVsIHdoZW4geW91IGhhdmUgdHdvIGRpc2NyZXRlIHZhcmlhYmxlcywgYW5kIGFsbCBjb21iaW5hdGlvbnMgb2YgdGhlIHZhcmlhYmxlcyBleGlzdCBpbiB0aGUgZGF0YS4NCg0KIyMjIFRlbXBsYXRlDQpmYWNldF9ncmlkKGZhY2V0cywgbWFyZ2lucyA9IEZBTFNFLCBzY2FsZXMgPSAiZml4ZWQiLCBzcGFjZSA9ICJmaXhlZCIsc2hyaW5rID0gVFJVRSwgbGFiZWxsZXIgPSAibGFiZWxfdmFsdWUiLCBhcy50YWJsZSA9IFRSVUUsc3dpdGNoID0gTlVMTCwgZHJvcCA9IFRSVUUpDQoNCg0KYGBge3J9DQojIFNhbXBsZSBnZW9tX3BvaW50DQpnZ3Bsb3QoZGF0YSA9IG1wZykgKw0KICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkNCg0KIyBTYW1wbGUgZ2VvbV9zbW9vdGgNCmdncGxvdChkYXRhID0gbXBnKSArDQogIGdlb21fc21vb3RoKG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkNCiAgDQpgYGANCg0KYGBge3J9DQojIGRpZmZlcmVudCBsaW5lcyBpbiBnZW9tX3Ntb290aCBieSBwYXJhbWV0ZXIgImxpbmV0eXBlID0gdmFsdWUiIg0KZ2dwbG90KGRhdGEgPSBtcGcpICsNCiAgZ2VvbV9zbW9vdGgobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3ksIGxpbmV0eXBlPWRydikpDQoNCiMgIyAwID0gYmxhbmssIDEgPSBzb2xpZCwgMiA9IGRhc2hlZCwgMyA9IGRvdHRlZCwgNCA9IGRvdGRhc2gsIDUgPSBsb25nZGFzaCwgNiA9IHR3b2Rhc2gNCmdncGxvdChkYXRhID0gbXBnKSArDQogIGdlb21fc21vb3RoKG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSwgbGluZXR5cGU9MikNCg0KYGBgDQoNCiMgRGVzY3JpcHRpb24NCg0KQWlkcyB0aGUgZXllIGluIHNlZWluZyBwYXR0ZXJucyBpbiB0aGUgcHJlc2VuY2Ugb2Ygb3ZlcnBsb3R0aW5nLiBnZW9tX3Ntb290aCBhbmQgc3RhdF9zbW9vdGggYXJlIGVmZmVjdGl2ZWx5IGFsaWFzZXM6IHRoZXkgYm90aCB1c2UgdGhlIHNhbWUgYXJndW1lbnRzLiBVc2UgZ2VvbV9zbW9vdGggdW5sZXNzIHlvdSB3YW50IHRvIGRpc3BsYXkgdGhlIHJlc3VsdHMgd2l0aCBhIG5vbi1zdGFuZGFyZCBnZW9tLg0KDQojIFVzYWdlDQoNCmdlb21fc21vb3RoKG1hcHBpbmcgPSBOVUxMLCBkYXRhID0gTlVMTCwgc3RhdCA9ICJzbW9vdGgiLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIsIC4uLiwgbWV0aG9kID0gImF1dG8iLCBmb3JtdWxhID0geSB+IHgsIHNlID0gVFJVRSwgbmEucm0gPSBGQUxTRSwgc2hvdy5sZWdlbmQgPSBOQSwgaW5oZXJpdC5hZXMgPSBUUlVFKQ0KDQpzdGF0X3Ntb290aChtYXBwaW5nID0gTlVMTCwgZGF0YSA9IE5VTEwsIGdlb20gPSAic21vb3RoIiwgcG9zaXRpb24gPSAiaWRlbnRpdHkiLCAuLi4sIG1ldGhvZCA9ICJhdXRvIiwgZm9ybXVsYSA9IHkgfiB4LCBzZSA9IFRSVUUsIG4gPSA4MCwgc3BhbiA9IDAuNzUsIGZ1bGxyYW5nZSA9IEZBTFNFLCBsZXZlbCA9IDAuOTUsIG1ldGhvZC5hcmdzID0gbGlzdCgpLCBuYS5ybSA9IEZBTFNFLCBzaG93LmxlZ2VuZCA9IE5BLCBpbmhlcml0LmFlcyA9IFRSVUUpDQoNCg0KYGBge3J9DQojIHVzaW5nIHN0YXRfc21vdGgNCmdncGxvdChkYXRhID0gbXBnKSArIHN0YXRfc21vb3RoKGFlcyhkaXNwbCwgaHd5LCBsaW5ldHlwZT1kcnYpLA0KICBzaG93LmxlZ2VuZCA9IEZBTFNFKQ0KYGBgDQoNCg0KYGBge3J9DQojIHVzaW5nIG11bHRpcGxlIGdlb20gZnVuY3Rpb25zIGluIG9uZSBjaGFydA0KZ2dwbG90KGRhdGEgPSBtcGcpICsgDQogIGdlb21fcG9pbnQoYWVzKGRpc3BsLCBod3kpKSArDQogIGdlb21fc21vb3RoKGFlcyhkaXNwbCwgaHd5KSkNCg0KDQojIHNhbWUgYnV0IGdsb2JhbCBhbmQgbGVzcyBjb2RlDQpnZ3Bsb3QoZGF0YSA9IG1wZywgbWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHk9IGh3eSkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKQ0KYGBgDQoNCmBgYHtyfQ0KIyBhbm90aGVyIGV4YW1wbGUgYnV0IGFkZGVkIHBhcmFtZXRlcnMNCg0KZ2dwbG90KGRhdGEgPSBtcGcsIG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5PSBod3kpKSArIA0KICAgICMjIGNoYW5nZWQgY29sb3JzDQogIGdlb21fcG9pbnQoIG1hcHBpbmcgPSBhZXMoY29sb3I9Y2xhc3MpKSArIA0KICANCiAgIyMgY2hhbmdlcyB0byBnZW9tIGxpbmUgb25seSBmb3Igc3Vic2V0IQ0KICBnZW9tX3Ntb290aChkYXRhID0gZmlsdGVyKG1wZywgY2xhc3MgPT0gInN1YmNvbXBhY3QiKSxzZSA9IEZBTFNFKQ0KDQpgYGANCg0KDQpgYGB7cn0NCiNleGVyY2lzZQ0KDQojIGJyYWluc3Rvcm1pbmcNCmcgPC0gZ2dwbG90KGRhdGEgPSBtcGcsIG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5PSBod3ksIGNvbG9yID0gZHJ2KSkNCmcgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChzZT1GQUxTRSkNCmBgYA0KDQpgYGB7cn0NCiMgQ29tcGFyaXNvbiBiZXR3ZWVuIHByb2dyYW1taW5nIGxvZ2ljIC0+IGFsbW9zdCB0aGUgc2FtZSBvdXRwdXQgY2hhcnQNCmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeT0gaHd5LCBjb2xvciA9IGRydikpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKQ0KDQpnZ3Bsb3QoZGF0YSA9IG1wZywgbWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHk9IGh3eSwgY29sb3IgPSBkcnYpKSArDQogIGdlb21fcG9pbnQoIGRhdGEgPSBtcGcsIG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKw0KICBnZW9tX3Ntb290aChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpDQoNCmBgYA0KDQpgYGB7cn0NCiMgZXhlcmNpc2U6IFByb2dyYW0gQ29kZSBmb3Igc2hvd24gcGxvdHMNCg0KIyB3aXRob3V0IHNwbGl0dGluZyBpbnRvIGxlZ2VuZHMNCmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeT0gaHd5KSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChzZT1GQUxTRSkNCg0KIyB3aXRoIHNwbGl0IGdlb20gbGluZXMNCmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeT0gaHd5KSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChkYXRhID0gZmlsdGVyKG1wZywgZHJ2ID09ICI0IiksIHNlPUZBTFNFKSArDQpnZW9tX3Ntb290aChkYXRhID0gZmlsdGVyKG1wZywgZHJ2ID09ICJmIiksc2U9RkFMU0UpICsNCmdlb21fc21vb3RoKGRhdGEgPSBmaWx0ZXIobXBnLCBkcnYgPT0gInIiKSxzZT1GQUxTRSkNCg0KYGBgDQoNCg0KDQojIFN0YXRpc3RpY2FsIFRyYW5zZm9ybWF0aW9ucw0KDQpgYGB7cn0NCmhlYWQoZGlhbW9uZHMpDQpgYGANCg0KDQojIEJhciBjaGFydHMNCg0KRGVzY3JpcHRpb246DQoNClRoZXJlIGFyZSB0d28gdHlwZXMgb2YgYmFyIGNoYXJ0czogZ2VvbV9iYXIgbWFrZXMgdGhlIGhlaWdodCBvZiB0aGUgYmFyIHByb3BvcnRpb25hbCB0byB0aGUgbnVtYmVyIG9mIGNhc2VzIGluIGVhY2ggZ3JvdXAgKG9yIGlmIHRoZSB3ZWlnaHQgYWV0aGV0aWMgaXMgc3VwcGxpZWQsIHRoZSBzdW0gb2YgdGhlIHdlaWdodHMpLiBJZiB5b3Ugd2FudCB0aGUgaGVpZ2h0cyBvZiB0aGUgYmFycyB0byByZXByZXNlbnQgdmFsdWVzIGluIHRoZSBkYXRhLCB1c2UgZ2VvbV9jb2wgaW5zdGVhZC4gZ2VvbV9iYXIgdXNlcyBzdGF0X2NvdW50IGJ5IGRlZmF1bHQ6IGl0IGNvdW50cyB0aGUgbnVtYmVyIG9mIGNhc2VzIGF0IGVhY2ggeCBwb3NpdGlvbi4gZ2VvbV9jb2wgdXNlcyBzdGF0X2lkZW50aXR5OiBpdCBsZWF2ZXMgdGhlIGRhdGEgYXMgaXMuDQoNClVzYWdlOg0KDQpnZW9tX2JhcihtYXBwaW5nID0gTlVMTCwgZGF0YSA9IE5VTEwsIHN0YXQgPSAiY291bnQiLA0KICBwb3NpdGlvbiA9ICJzdGFjayIsIC4uLiwgd2lkdGggPSBOVUxMLCBiaW53aWR0aCA9IE5VTEwsIG5hLnJtID0gRkFMU0UsDQogIHNob3cubGVnZW5kID0gTkEsIGluaGVyaXQuYWVzID0gVFJVRSkNCg0KZ2VvbV9jb2wobWFwcGluZyA9IE5VTEwsIGRhdGEgPSBOVUxMLCBwb3NpdGlvbiA9ICJzdGFjayIsIC4uLiwNCiAgd2lkdGggPSBOVUxMLCBuYS5ybSA9IEZBTFNFLCBzaG93LmxlZ2VuZCA9IE5BLCBpbmhlcml0LmFlcyA9IFRSVUUpDQoNCnN0YXRfY291bnQobWFwcGluZyA9IE5VTEwsIGRhdGEgPSBOVUxMLCBnZW9tID0gImJhciIsDQogIHBvc2l0aW9uID0gInN0YWNrIiwgLi4uLCB3aWR0aCA9IE5VTEwsIG5hLnJtID0gRkFMU0UsDQogIHNob3cubGVnZW5kID0gTkEsIGluaGVyaXQuYWVzID0gVFJVRSkNCg0KDQpgYGB7cn0NCiMgdXNpbmcgZ2VvbV9iYXIgZnVuY3Rpb24NCmdncGxvdChkYXRhID0gZGlhbW9uZHMpICsgZ2VvbV9iYXIobWFwcGluZyA9IGFlcyh4ID0gY3V0KSkNCg0KYGBgDQoNCmBgYHtyfQ0KIyBjaGFuZ2luZyByZWxhdGVkIGNvbXB1dGVkIHZhcmlhYmxlcw0KDQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzKSArDQogIGdlb21fYmFyKGFlcyhjdXQsIC4ucHJvcC4uLCBncm91cCA9IDEpKQ0KDQpgYGANCg0KYGBge3J9DQojIHVzaW5nIHN0YXRfc3VtbWFyeSBmb3IgZGVzY3JpcHRpdmUgc3RhdGlzdGljcw0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcykgKyBzdGF0X3N1bW1hcnkoYWVzKGN1dCwgZGVwdGgpLCBmdW4ueW1pbiA9IG1pbiwgZnVuLnltYXggPSBtYXgsIGZ1bi55ID0gbWVkaWFuKQ0KDQpgYGANCg0KYGBge3J9DQojIGxpc3Qgb2Ygb3RoZXIgdHJhbnNmb3JtYXRpb25zIGF2YWlsYWJsZSBpbiBnZ3Bsb3QyDQojIGYuZS4gDQo/c3RhdF9iaW4NCmBgYA0KIyBTdW1tYXJpc2UgeSB2YWx1ZXMgYXQgdW5pcXVlL2Jpbm5lZCB4DQoNCkRlc2NyaXB0aW9uOg0KDQpzdGF0X3N1bW1hcnkgb3BlcmF0ZXMgb24gdW5pcXVlIHg7IHN0YXRfc3VtbWFyeV9iaW4gb3BlcmF0b3JzIG9uIGJpbm5lZCB4LiBUaGV5IGFyZSBtb3JlIGZsZXhpYmxlIHZlcnNpb25zIG9mIHN0YXRfYmluOiBpbnN0ZWFkIG9mIGp1c3QgY291bnRpbmcsIHRoZXkgY2FuIGNvbXB1dGUgYW55IGFnZ3JlZ2F0ZS4NCg0KVXNhZ2U6DQoNCnN0YXRfc3VtbWFyeV9iaW4obWFwcGluZyA9IE5VTEwsIGRhdGEgPSBOVUxMLCBnZW9tID0gInBvaW50cmFuZ2UiLA0KICBwb3NpdGlvbiA9ICJpZGVudGl0eSIsIC4uLiwgZnVuLmRhdGEgPSBOVUxMLCBmdW4ueSA9IE5VTEwsDQogIGZ1bi55bWF4ID0gTlVMTCwgZnVuLnltaW4gPSBOVUxMLCBmdW4uYXJncyA9IGxpc3QoKSwgbmEucm0gPSBGQUxTRSwNCiAgc2hvdy5sZWdlbmQgPSBOQSwgaW5oZXJpdC5hZXMgPSBUUlVFKQ0KDQpzdGF0X3N1bW1hcnkobWFwcGluZyA9IE5VTEwsIGRhdGEgPSBOVUxMLCBnZW9tID0gInBvaW50cmFuZ2UiLA0KICBwb3NpdGlvbiA9ICJpZGVudGl0eSIsIC4uLiwgZnVuLmRhdGEgPSBOVUxMLCBmdW4ueSA9IE5VTEwsDQogIGZ1bi55bWF4ID0gTlVMTCwgZnVuLnltaW4gPSBOVUxMLCBmdW4uYXJncyA9IGxpc3QoKSwgbmEucm0gPSBGQUxTRSwNCiAgc2hvdy5sZWdlbmQgPSBOQSwgaW5oZXJpdC5hZXMgPSBUUlVFKQ0KDQpgYGB7cn0NCiMgZXhlcnNpY2UNCg0KIyB1c2luZyBnZW9tX3BvaW50cmFuZ2UgZm9yIHNhbWUgcGxvdCBsaWtlIGJlZm9yZQ0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcykgKw0KICBnZW9tX3BvaW50cmFuZ2UobWFwcGluZyA9IGFlcyh4ID0gY3V0LCB5ID0gZGVwdGgpLA0KICAgICAgICAgICAgICAgICAgc3RhdCA9ICJzdW1tYXJ5IiwNCiAgICAgICAgICAgICAgICAgIGZ1bi55bWluID0gbWluLA0KICAgICAgICAgICAgICAgICAgZnVuLnltYXggPSBtYXgsDQogICAgICAgICAgICAgICAgICBmdW4ueSA9IG1lZGlhbikNCmBgYA0KDQpgYGB7cn0NCiMgdXNpbmcgZ2VvbV9jb2wgdnMgZ2VvbV9iYXINCmdncGxvdChkYXRhID0gZGlhbW9uZHMpICsgZ2VvbV9jb2woYWVzKGN1dCwgZGVwdGgpKSArIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKQ0KDQoNCmdncGxvdChkYXRhID0gZGlhbW9uZHMpICsgZ2VvbV9iYXIoYWVzKGN1dCkpDQpgYGANCg0KDQpgYGB7cn0NCj9zdGF0X3Ntb290aCAgDQpgYGANCg0KYGBge3J9DQojIHdoYXQgaXMgd3Jvbmcgd2l0aCB0aGlzIHBsb3RzDQpnZ3Bsb3QoZGlhbW9uZHMpICsgZ2VvbV9iYXIoYWVzKGN1dCx5ID0gIC4ucHJvcC4uKSkNCg0KZ2dwbG90KGRpYW1vbmRzKSArIGdlb21fYmFyKGFlcyhjdXQsIGZpbGwgPSBjb2xvcix5ID0gLi5wcm9wLi4pKQ0KDQpnZ3Bsb3QoZGlhbW9uZHMpICsgZ2VvbV9iYXIoYWVzKGN1dCx5ID0gIC4ucHJvcC4uLCBncm91cCA9IFRSVUUpKQ0KDQpnZ3Bsb3QoZGlhbW9uZHMpICsgZ2VvbV9iYXIoYWVzKGN1dCwgZmlsbCA9IGNvbG9yLHkgPSAuLnByb3AuLixncm91cCA9IDEpKQ0KDQpnZ3Bsb3QoZGlhbW9uZHMpICsgZ2VvbV9iYXIoYWVzKGN1dCwgZmlsbCA9IGNvbG9yKSkNCmBgYA0KDQoNCiMgNCBQb3NpdGlvbiBGdW5jdGlvbnMNCg0KYGBge3J9DQojIGZ1cnRoZXIgY29sb3IgZnVuY3Rpb25zIG9uIGdlb21fYmFyDQoNCiMgc3RhY2tlZCBiYXIgY2hhcnQNCmdncGxvdChkaWFtb25kcykgKyBnZW9tX2JhcihhZXMoY3V0LCBmaWxsID0gY2xhcml0eSkscG9zaXRpb24gPSAgImlkZW50aXR5IikNCg0KIyBjbHVzdGVyZWQgYmFyIGNoYXJ0DQpnZ3Bsb3QoZGlhbW9uZHMpICsgZ2VvbV9iYXIoYWVzKGN1dCwgZmlsbCA9IGNsYXJpdHkpLHBvc2l0aW9uID0gICJkb2RnZSIpDQoNCiMgMTAwJSBzdGFja2VkIGJhciBjaGFydA0KZ2dwbG90KGRpYW1vbmRzKSArIGdlb21fYmFyKGFlcyhjdXQsIGZpbGwgPSBjbGFyaXR5KSxwb3NpdGlvbiA9ICAiZmlsbCIpDQoNCg0KIyBKaXR0ZXIgQ2hhcnQgd2l0aCAicG9zaXRpb24gPSBqaXR0ZXIiICYgZ2VvbV9wb2ludA0KZ2dwbG90KGRpYW1vbmRzKSArIGdlb21fcG9pbnQoYWVzKGN1dCxkZXB0aCwgY29sb3IgPSBjbGFyaXR5KSxwb3NpdGlvbiA9ICAiaml0dGVyIikNCg0KIyBPciBzaG9ydCB2ZXJzaW9uDQojIEppdHRlciBDaGFydCB3aXRoIGdlb21faml0dGVyDQpnZ3Bsb3QoZGlhbW9uZHMpICsgZ2VvbV9qaXR0ZXIoYWVzKGN1dCxkZXB0aCwgY29sb3IgPSBjbGFyaXR5KSkNCg0KYGBgDQoNCmBgYHtyfQ0KIyBzdGFja2VkIGJhciBjaGFydCB3aXRoIHRyYW5zcGFyZW5jZSBvZiAyMCUNCmdncGxvdChkaWFtb25kcykgKyBnZW9tX2JhcihhZXMoY3V0LCBmaWxsID0gY2xhcml0eSkscG9zaXRpb24gPSAgImlkZW50aXR5IiwgYWxwaGEgPSAwLjIpDQpgYGANCg0KYGBge3J9DQojZXhlcmNpc2UNCg0KIyAxDQpnZ3Bsb3QobXBnLCBtYXBwaW5nID0gYWVzKGN0eSwgaHd5KSkgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNsYXNzKSkgKyBnZW9tX3Ntb290aChtZXRob2QgPSBsbSwgc2U9RikNCmdncGxvdChtcGcsIG1hcHBpbmcgPSBhZXMoY3R5LCBod3kpKSArIGdlb21faml0dGVyKGFlcyhjdHksaHd5LCBjb2xvciA9IGNsYXNzKSwgYWxwaGEgPSAwLjYsIHNpemUgPSA0KSArIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCBzZT1GKQ0KDQojIDMgDQojIHN0YW5kYXJkIGdlb21faml0dGVyIGNoYXJ0DQpnZ3Bsb3QobXBnLCBtYXBwaW5nID0gYWVzKGN0eSwgaHd5KSkgKyBnZW9tX2ppdHRlcihhZXMoY29sb3IgPSBjbGFzcykpDQoNCiMgYnViYmxlIGNoYXJ0IHdpdGggZ2VvbV9qaXR0ZXINCmdncGxvdChtcGcsIG1hcHBpbmcgPSBhZXMoY3R5LCBod3kpKSArIGdlb21fY291bnQoYWVzKGNvbG9yID0gY2xhc3MpKQ0KDQojIDQNCiMgZGVmYXVsdCBwb3NpdGlvbiA9ICJkb2RnZSINCmdncGxvdChtcGcsIGFlcyhjdHksIGh3eSkpICsgZ2VvbV9ib3hwbG90KGFlcyhkcnYsIGNvbG9yID0gZHJ2KSkNCg0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KG1wZywgYWVzKGNsYXNzLCBod3kpKSArIGdlb21fYm94cGxvdCgpDQoNCiMgdXNpbmcgY29vcmRfZmxpcA0KZ2dwbG90KG1wZywgYWVzKGNsYXNzLCBod3kpKSArIA0KICBnZW9tX2JveHBsb3QoKSArDQogIGNvb3JkX2ZsaXAoKQ0KDQoNCg0KYGBgDQojQ2FydGVzaWFuIGNvb3JkaW5hdGVzIHdpdGggeCBhbmQgeSBmbGlwcGVkDQoNCkRlc2NyaXB0aW9uOg0KDQpGbGlwIGNhcnRlc2lhbiBjb29yZGluYXRlcyBzbyB0aGF0IGhvcml6b250YWwgYmVjb21lcyB2ZXJ0aWNhbCwgYW5kIHZlcnRpY2FsLCBob3Jpem9udGFsLiBUaGlzIGlzIHByaW1hcmlseSB1c2VmdWwgZm9yIGNvbnZlcnRpbmcgZ2VvbXMgYW5kIHN0YXRpc3RpY3Mgd2hpY2ggZGlzcGxheSB5IGNvbmRpdGlvbmFsIG9uIHgsIHRvIHggY29uZGl0aW9uYWwgb24geS4NCg0KVXNhZ2U6DQoNCmNvb3JkX2ZsaXAoeGxpbSA9IE5VTEwsIHlsaW0gPSBOVUxMLCBleHBhbmQgPSBUUlVFKQ0KQXJndW1lbnRzDQoNCnhsaW06DQpMaW1pdHMgZm9yIHRoZSB4IGFuZCB5IGF4ZXMuDQp5bGltOg0KTGltaXRzIGZvciB0aGUgeCBhbmQgeSBheGVzLg0KZXhwYW5kOgkNCklmIFRSVUUsIHRoZSBkZWZhdWx0LCBhZGRzIGEgc21hbGwgZXhwYW5zaW9uIGZhY3RvciB0byB0aGUgbGltaXRzIHRvIGVuc3VyZSB0aGF0IGRhdGEgYW5kIGF4ZXMgZG9uJ3Qgb3ZlcmxhcC4gSWYgRkFMU0UsIGxpbWl0cyBhcmUgdGFrZW4gZXhhY3RseSBmcm9tIHRoZSBkYXRhIG9yIHhsaW0veWxpbS4NCg0KDQoNCmBgYHtyfQ0KbnogPC0gbWFwX2RhdGEoIm56IikNCg0KZ2dwbG90KG56LCBhZXMobG9uZywgbGF0LCBncm91cD1ncm91cCkpICsNCiAgZ2VvbV9wb2x5Z29uKGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJibGFjayIpDQoNCmdncGxvdChueiwgYWVzKGxvbmcsIGxhdCwgZ3JvdXA9Z3JvdXApKSArDQogIGdlb21fcG9seWdvbihmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiYmxhY2siKSArDQogIGNvb3JkX3F1aWNrbWFwKCkNCg0KDQpgYGANCiNNYXAgcHJvamVjdGlvbnMNCg0KRGVzY3JpcHRpb246DQoNCmNvb3JkX21hcCBwcm9qZWN0cyBhIHBvcnRpb24gb2YgdGhlIGVhcnRoLCB3aGljaCBpcyBhcHByb3hpbWF0ZWx5IHNwaGVyaWNhbCwgb250byBhIGZsYXQgMkQgcGxhbmUgdXNpbmcgYW55IHByb2plY3Rpb24gZGVmaW5lZCBieSB0aGUgbWFwcHJvaiBwYWNrYWdlLiBNYXAgcHJvamVjdGlvbnMgZG8gbm90LCBpbiBnZW5lcmFsLCBwcmVzZXJ2ZSBzdHJhaWdodCBsaW5lcywgc28gdGhpcyByZXF1aXJlcyBjb25zaWRlcmFibGUgY29tcHV0YXRpb24uIGNvb3JkX3F1aWNrbWFwIGlzIGEgcXVpY2sgYXBwcm94aW1hdGlvbiB0aGF0IGRvZXMgcHJlc2VydmUgc3RyYWlnaHQgbGluZXMuIEl0IHdvcmtzIGJlc3QgZm9yIHNtYWxsZXIgYXJlYXMgY2xvc2VyIHRvIHRoZSBlcXVhdG9yLg0KDQpVc2FnZToNCg0KY29vcmRfbWFwKHByb2plY3Rpb24gPSAibWVyY2F0b3IiLCAuLi4sIHBhcmFtZXRlcnMgPSBOVUxMLA0KICBvcmllbnRhdGlvbiA9IE5VTEwsIHhsaW0gPSBOVUxMLCB5bGltID0gTlVMTCkNCg0KY29vcmRfcXVpY2ttYXAoeGxpbSA9IE5VTEwsIHlsaW0gPSBOVUxMLCBleHBhbmQgPSBUUlVFKQ0KDQoNCmBgYHtyfQ0KYmFyIDwtIGdncGxvdChkaWFtb25kcykgKw0KICBnZW9tX2JhcihhZXMoY3V0LCBmaWxsPWN1dCksIHNob3cubGVnZW5kID0gRkFMU0UsIHdpZHRoID0gMSkgKyB0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArIGxhYnMoeCA9IE5VTEwsIHkgPSBOVUxMKQ0KDQpiYXIgKyBjb29yZF9mbGlwKCkNCg0KIyBQb2xhcnBsb3QNCmJhciArIGNvb3JkX3BvbGFyKGRpcmVjdGlvbiA9IDEpDQoNCg0KYGBgDQoNCiNQb2xhciBjb29yZGluYXRlcw0KDQpEZXNjcmlwdGlvbjoNCg0KVGhlIHBvbGFyIGNvb3JkaW5hdGUgc3lzdGVtIGlzIG1vc3QgY29tbW9ubHkgdXNlZCBmb3IgcGllIGNoYXJ0cywgd2hpY2ggYXJlIGEgc3RhY2tlZCBiYXIgY2hhcnQgaW4gcG9sYXIgY29vcmRpbmF0ZXMuDQoNClVzYWdlOg0KDQpjb29yZF9wb2xhcih0aGV0YSA9ICJ4Iiwgc3RhcnQgPSAwLCBkaXJlY3Rpb24gPSAxKQ0KQXJndW1lbnRzDQoNCnRoZXRhOgkNCnZhcmlhYmxlIHRvIG1hcCBhbmdsZSB0byAoeCBvciB5KQ0Kc3RhcnQ6CQ0Kb2Zmc2V0IG9mIHN0YXJ0aW5nIHBvaW50IGZyb20gMTIgbydjbG9jayBpbiByYWRpYW5zDQpkaXJlY3Rpb246CQ0KMSwgY2xvY2t3aXNlOyAtMSwgYW50aWNsb2Nrd2lzZQ0KDQpgYGB7cn0NCiMgZXhlcmNpc2UNCg0KIyAxIGZyb20gc3RhY2tlZCBjaGFydCB0byBwb2xhciBjaGFydA0KDQpnZ3Bsb3QoZGlhbW9uZHMpICsgZ2VvbV9iYXIoYWVzKGN1dCwgZmlsbCA9IGNsYXJpdHkpLHBvc2l0aW9uID0gICJpZGVudGl0eSIsIGFscGhhID0gMC4yKQ0KDQpnZ3Bsb3QoZGlhbW9uZHMpICsgZ2VvbV9iYXIoYWVzKGN1dCwgZmlsbCA9IGNsYXJpdHkpLHBvc2l0aW9uID0gICJpZGVudGl0eSIsIGFscGhhID0gMC4yKSArIGNvb3JkX3BvbGFyKCkNCg0KDQpgYGANCg0KIyBNb2RpZnkgYXhpcywgbGVnZW5kLCBhbmQgcGxvdCBsYWJlbHMNCg0KRGVzY3JpcHRpb246IA0KDQpHb29kIGxhYmVscyBhcmUgY3JpdGljYWwgZm9yIG1ha2luZyB5b3VyIHBsb3RzIGFjY2Vzc2libGUgdG8gYSB3aWRlciBhdWRpZW5jZS4gRW5zdXJlIHRoZSBheGlzIGFuZCBsZWdlbmQgbGFiZWxzIGRpc3BsYXkgdGhlIGZ1bGwgdmFyaWFibGUgbmFtZS4gVXNlIHRoZSBwbG90IHRpdGxlIGFuZCBzdWJ0aXRsZSB0byBleHBsYWluIHRoZSBtYWluIGZpbmRpbmdzLiBJdCdzIGNvbW1vbiB0byB1c2UgdGhlIGNhcHRpb24gdG8gcHJvdmlkZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZGF0YSBzb3VyY2UuDQoNClVzYWdlOg0KDQpsYWJzKC4uLikNCg0KeGxhYihsYWJlbCkNCg0KeWxhYihsYWJlbCkNCg0KZ2d0aXRsZShsYWJlbCwgc3VidGl0bGUgPSBOVUxMKQ0KDQoNCmBgYHtyfQ0KZ2dwbG90KCBkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKCB4ID0gY3R5LCB5ID0gaHd5KSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX2FibGluZSgpICsgY29vcmRfZml4ZWQoKQ0KDQpgYGANCg0KI1JlZmVyZW5jZSBsaW5lczogaG9yaXpvbnRhbCwgdmVydGljYWwsIGFuZCBkaWFnb25hbA0KDQpEZXNjcmlwdGlvbjoNCg0KVGhlc2UgZ2VvbXMgYWRkIHJlZmVyZW5jZSBsaW5lcyAoc29tZXRpbWVzIGNhbGxlZCBydWxlcykgdG8gYSBwbG90LCBlaXRoZXIgaG9yaXpvbnRhbCwgdmVydGljYWwsIG9yIGRpYWdvbmFsIChzcGVjaWZpZWQgYnkgc2xvcGUgYW5kIGludGVyY2VwdCkuIFRoZXNlIGFyZSB1c2VmdWwgZm9yIGFubm90YXRpbmcgcGxvdHMuDQoNClVzYWdlOg0KDQpnZW9tX2FibGluZShtYXBwaW5nID0gTlVMTCwgZGF0YSA9IE5VTEwsIC4uLiwgc2xvcGUsIGludGVyY2VwdCwNCiAgbmEucm0gPSBGQUxTRSwgc2hvdy5sZWdlbmQgPSBOQSkNCg0KZ2VvbV9obGluZShtYXBwaW5nID0gTlVMTCwgZGF0YSA9IE5VTEwsIC4uLiwgeWludGVyY2VwdCwgbmEucm0gPSBGQUxTRSwNCiAgc2hvdy5sZWdlbmQgPSBOQSkNCg0KZ2VvbV92bGluZShtYXBwaW5nID0gTlVMTCwgZGF0YSA9IE5VTEwsIC4uLiwgeGludGVyY2VwdCwgbmEucm0gPSBGQUxTRSwNCiAgc2hvdy5sZWdlbmQgPSBOQSkNCg0KIyBDYXJ0ZXNpYW4gY29vcmRpbmF0ZXMgd2l0aCBmaXhlZCAiYXNwZWN0IHJhdGlvIg0KDQpEZXNjcmlwdGlvbjoNCg0KQSBmaXhlZCBzY2FsZSBjb29yZGluYXRlIHN5c3RlbSBmb3JjZXMgYSBzcGVjaWZpZWQgcmF0aW8gYmV0d2VlbiB0aGUgcGh5c2ljYWwgcmVwcmVzZW50YXRpb24gb2YgZGF0YSB1bml0cyBvbiB0aGUgYXhlcy4gVGhlIHJhdGlvIHJlcHJlc2VudHMgdGhlIG51bWJlciBvZiB1bml0cyBvbiB0aGUgeS1heGlzIGVxdWl2YWxlbnQgdG8gb25lIHVuaXQgb24gdGhlIHgtYXhpcy4gVGhlIGRlZmF1bHQsIHJhdGlvID0gMSwgZW5zdXJlcyB0aGF0IG9uZSB1bml0IG9uIHRoZSB4LWF4aXMgaXMgdGhlIHNhbWUgbGVuZ3RoIGFzIG9uZSB1bml0IG9uIHRoZSB5LWF4aXMuIFJhdGlvcyBoaWdoZXIgdGhhbiBvbmUgbWFrZSB1bml0cyBvbiB0aGUgeSBheGlzIGxvbmdlciB0aGFuIHVuaXRzIG9uIHRoZSB4LWF4aXMsIGFuZCB2aWNlIHZlcnNhLiBUaGlzIGlzIHNpbWlsYXIgdG8gZXFzY3Bsb3QsIGJ1dCBpdCB3b3JrcyBmb3IgYWxsIHR5cGVzIG9mIGdyYXBoaWNzLg0KDQpVc2FnZToNCg0KY29vcmRfZml4ZWQocmF0aW8gPSAxLCB4bGltID0gTlVMTCwgeWxpbSA9IE5VTEwsIGV4cGFuZCA9IFRSVUUpDQoNCg0KIyBGaW5hbCBUZW1wbGF0ZSBmb3IgZ2dwbG90DQoNCiMjZ2dwbG90KCBkYXRhID0gPCBEQVRBID4pICsgDQojIzwgR0VPTV9GVU5DVElPTiA+KCBtYXBwaW5nID0gYWVzKCA8IE1BUFBJTkdTID4pLCBzdGF0ID0gPCBTVEFUID4sIHBvc2l0aW9uID0gPCBQT1NJVElPTiA+ICkgKw0KIyM8IENPT1JESU5BVEVfRlVOQ1RJT04gPiArIA0KIyM8IEZBQ0VUX0ZVTkNUSU9OID4NCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==