This is my documentation while going through the book Data Visualization, A practical introduction, by Kieran Healy: http://vissoc.co/index.html#preface
CHAPTERS ONE AND TWO
Let’s first load all required libraries:
library(tidyverse)
library(socviz)
library(gapminder)
library(here)
library(ggrepel)
library(maps)
Now that our functions are loaded through their respective libraries, we can start lading some dat from the gapminder library:
gapminder
Let’s make a quick plot:
p <- ggplot(data = gapminder,
mapping = aes(x = gdpPercap, y = lifeExp))
p + geom_point()
CHAPTER THREE
The central activity of visualizing data with ggplot more or less always involves the same sequence of steps. There is some structured relationship, some mapping, between the variables in your data and their representation in the plot displayed on your screen or on the page. Ggplot provides you with a set of tools to map data to visual elements on your plot, to specify the kind of plot you want, and then subsquently to control the fine details of how it will be displayed.In ggplot, the logical connections between your data and the plot elements are called aesthetic mappings or just aesthetics.
-Tell the ggplot() function what your data is -Then how the variables in this data logically map onto the plot’s aesthetics -Take the result and say what general sort of plot you want. In ggplot, the overall type of plot is called a geom. Each geom has a function that creates it. -You combine these two pieces, the ggplot() object and the geom, by literally adding them together in an expression, using the “+” symbol.
So: 1. Data (what data we want to use) 2. Mapping (what relationships we want to see) 3. Geom (how we want to see the relationships in our data) 4. Coordinates and Scales (reference) 5. Labels and Guides (explanation)
p + geom_point() + geom_smooth()
p + geom_smooth() + geom_point() # loads the points last, on top of the smooth line
p + geom_point() + geom_smooth(method = "lm")
It’s possible to give geoms separate instructions that they will follow instead, but in the absence of any other information, the geoms will look for the instructions it needs in the ggplot() function, or the object created by it.
An aesthetic mapping specifies that a variable will be expressed by one of the available visual elements, such as size, or color, or shape, and so on. Code does not give a direct instruction like “color the points purple”. Instead it says, “the property ‘color’ will represent the variable continent”, or “color will map continent”. The aes() function is for mappings only. Do not use it to change properties to a particular value. If we want to set a property, we do it in the geom_ we are using, and outside the mapping = aes(…) step.
The various geom_ functions can take many other arguments that will affect how the plot looks, but that do not involve mapping variables to aesthetic elements.
Now a more polished view:
p <- ggplot(data = gapminder, mapping = aes(x = gdpPercap, y = lifeExp, colour = continent, fill = continent))
p + geom_point(alpha = 0.3) +
geom_smooth(method = "gam") +
scale_x_log10(labels = scales::dollar) +
labs(x = "GDP per Capita", y = "Life Expectancy in Years",
title = "Economic Growth and Life Expectancy, by Continent",
subtitle = "Data points are country and years",
caption = "Source: Gapminder")
By default, geoms inherit their mappings from the ggplot() function. We can change this by mapping the aesthetics we want only to geom_ functions that we want them to apply to. We use the same mapping = aes(…) expression as in the initial call to ggplot(), but now use it in the geom_ functions as well, specifying only the mappings we want to apply to each one. Mappings specified only in the initial ggplot() function will carry through to all subsequent geoms.
p <- ggplot(data = gapminder, mapping = aes(x = gdpPercap, y = lifeExp))
p + geom_point(mapping = aes(color = continent)) +
geom_smooth(method = "loess", color = "black") +
scale_x_log10(labels = scales::dollar) +
labs(x = "GDP per Capita", y = "Life Expectancy in Years",
title = "Economic Growth and Life Expectancy, by Continent",
subtitle = "Data points are country and years",
caption = "Source: Gapminder")
You can set the default size of plots within your .Rmd document by setting an option in your first code chunk. This one tells R to make 8x5 figures:
knitr::opts_chunk$set(fig.width=12, fig.height=5) # this sets the environment's default
Like so, on a per-chart basis:
p <- ggplot(data = gapminder, mapping = aes(x = gdpPercap, y = lifeExp))
p + geom_point(mapping = aes(color = continent)) +
geom_smooth(method = "loess", color = "black") +
scale_x_log10(labels = scales::dollar) +
labs(x = "GDP per Capita", y = "Life Expectancy in Years",
title = "Economic Growth and Life Expectancy, by Continent",
subtitle = "Data points are country and years",
caption = "Source: Gapminder")
Now save an image of the chart (using the here-package)
p <- ggplot(data = gapminder, mapping = aes(x = gdpPercap, y = lifeExp))
p + geom_point(mapping = aes(color = continent)) +
geom_smooth(method = "loess", color = "black") +
scale_x_log10(labels = scales::dollar) +
labs(x = "GDP per Capita", y = "Life Expectancy in Years",
title = "Economic Growth and Life Expectancy, by Continent",
subtitle = "Data points are country and years",
caption = "Source: Gapminder") +
ggsave(here("figures", "lifeexp_vs_gdp.png"), width = 12, height = 4)
CHAPTER FOUR The ggplot library is an implementation of the “grammar” of graphics: a set of rules for producing graphics from data, taking pieces of data and mapping them to geometric objects that have aesthetic attributes, together with further rules for transforming the data if needed, adjusting scales, and projecting the results onto a different coordinate system.
p4 <- ggplot(data = gapminder, mapping = aes(x = year, y = gdpPercap))
p4 + geom_line(color = "gray70", aes(group = country)) +
geom_smooth(size = 1.1, method = "loess", se = FALSE) +
scale_y_log10(labels = scales::dollar) +
facet_wrap(~ continent, ncol = 5) +
labs(x = "Year",
y = "GDP per Capita",
title = "GDP per Capita on Five Continents")
Now let’s create a grid of more than one variable:
p45 <- ggplot(data = gss_sm, mapping = aes(x = age, y = childs))
p45 + geom_point(alpha = 0.2) +
geom_smooth() +
facet_grid(sex ~ race) +
theme_minimal()
Every geom_ function has an associated stat_ function that it uses by default. The reverse is also the case: every stat_ function has an associated geom_ function that it will plot by default if you ask it to. Below we call the prop(ortion) stat, rather than the default count stat. Or ..count..
p46 <- ggplot(data = gss_sm, mapping = aes(x = bigregion))
p46 + geom_bar(mapping = aes(y = ..prop..))
p46 <- ggplot(data = gss_sm, mapping = aes(x = bigregion))
p46 + geom_bar(mapping = aes(y = ..prop.., group = 1)) # this tells R that what we group by (biregion) should add up to 1; to see proportion of total rather than proportion per biregion
p49 <- ggplot(data = gss_sm, mapping = aes(x = religion, fill = religion))
p49 + geom_bar(mapping = aes(y = ..prop.., group = 1)) +
theme_minimal() +
guides(fill = FALSE)
p410 <- ggplot(data = gss_sm,
mapping = aes(x = bigregion, fill = religion))
p410 + geom_bar(position = "fill")
p410 + geom_bar(position = "dodge",
mapping = aes(y = ..prop.., group = religion))
p414 <- ggplot(data = gss_sm,
mapping = aes(x = religion))
p414 + geom_bar(position = "dodge",
mapping = aes(y = ..prop.., group = bigregion)) +
facet_wrap(~ bigregion, ncol = 2) +
theme_minimal()
p416 <- ggplot(data = subset(midwest, subset = state %in% c("OH", "WI", "IL")),
mapping = aes(x = percollege, fill = state))
p416 + geom_histogram(alpha = 0.4, bins = 20)
p418 <- ggplot(data = midwest,
mapping = aes(x = area, fill = state, color = state))
p418 + geom_density(alpha = 0.4)
Now some Titanic data, to show stat = ‘identity’ tells geom_bar not to do any stat calculation (like count or prop). Alternatively you can use geom_col() which does this automatically (ignore stat calcuation).
Also note how position = ‘dodge’ changes the bar chart from stacked to side-by-side
p420 <- ggplot(data = titanic, mapping = aes(x = fate, y = percent, fill = sex))
p420 + geom_bar(stat = 'identity') +
theme_minimal() +
theme(legend.position = "top")
p420 + geom_bar(position = 'dodge',stat = 'identity') +
theme_minimal() +
theme(legend.position = "top")
Now try a difference map, but show it vertical instead of the horizontal view the book provides
p421 <- ggplot(data = oecd_sum, mapping = aes(x = year, y = diff, fill = hi_lo))
p421 + geom_col() +
guides(fill = FALSE) +
theme_minimal() +
labs(x = NULL, y = "Difference in years",
title = "US vs rest Life Expectancy gap")
CHAPTER FIVE Prepping the data before visualizing it, using dplyr. Also introducing the pipe-operator: %>%. Data goes in one side of the pipe, actions are performed via functions, and results come out the other. A pipeline is typically a series of operations, like group_by(), filter() rows, select() columns, mutate(), and summarize(). As dplyr’s functions see things, summarizing actions “peel off” one grouping level at a time, so that the resulting summaries are at the next level up.
Let’s start by creating a table:
rel_by_region <- gss_sm %>%
group_by(bigregion, religion) %>%
summarize(N = n()) %>%
mutate(freq = N / sum(N),
pct = round((freq*100), 0))
Factor `religion` contains implicit NA, consider using `forcats::fct_explicit_na`
The variables specified in group_by() are retained in the new summary table; the variables created with summarize() and mutate() are added, and all the other variables in the original dataset are dropped.
p52 <- ggplot(rel_by_region, aes(x = bigregion, y = pct, fill = religion))
p52 + geom_col(position = "dodge2") +
geom_col(position = "dodge2", color = "black", fill = NA) +
labs(x = "region", y = "percent", fill = "religion") +
theme(legend.position = "top")
Two ways of drawing the same (although the book menions it cannot work this way)
p53 <- ggplot(rel_by_region, aes(x = religion, y = pct, fill = religion))
p53 + geom_col(position = "dodge2") +
labs(x = NULL, y = "%", fill = "Religions") +
guides(fill = FALSE) +
coord_flip() +
facet_grid(~ bigregion)
p53a <- ggplot(rel_by_region, aes(x = pct, y = religion, fill = religion))
p53a + geom_col(position = "dodge2") +
labs(x = "%", y = NULL) +
guides(fill = FALSE) +
facet_grid(~bigregion)
p54 <- ggplot(data = organdata, aes(x = year, y = donors))
p54 + geom_point()
p55 <- ggplot(data = organdata, aes(x = year, y = donors))
p55 + geom_line(aes(group = country)) + facet_wrap(~ country) + theme_minimal()
p56 <- ggplot(data = organdata, aes(x = country, y = donors))
p56 + geom_boxplot() + coord_flip()
The reorder() function will do this for us. It takes two required arguments. The first is the categorical variable or factor that we want to reorder. The second is the variable we want to reorder it by. The third and optional argument to reorder() is the function you want to use as a summary statistic.
p58 <- ggplot(data = organdata, aes(x = reorder(country, donors, na.rm=TRUE) ,y = donors, fill = world))
p58 + geom_boxplot() + labs(x=NULL) + coord_flip() + theme(legend.position = "top")
by_country <- organdata %>% group_by(consent_law, country) %>%
summarize(donors_mean= mean(donors, na.rm = TRUE),
donors_sd = sd(donors, na.rm = TRUE),
gdp_mean = mean(gdp, na.rm = TRUE),
health_mean = mean(health, na.rm = TRUE),
roads_mean = mean(roads, na.rm = TRUE),
cerebvas_mean = mean(cerebvas, na.rm = TRUE))
by_country <- organdata %>% group_by(consent_law, country) %>%
summarize_if(is.numeric, funs(mean, sd), na.rm = TRUE) %>%
ungroup()
funs() is soft deprecated as of dplyr 0.8.0
Please use a list of either functions or lambdas:
# Simple named list:
list(mean = mean, median = median)
# Auto named with `tibble::lst()`:
tibble::lst(mean, median)
# Using lambdas
list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))
[90mThis warning is displayed once per session.[39m
The next phase is adding some labels. And although geom_text() does the trick, it doesn’t have many clean options that work if the standard does not suffice. That’s where we load the ggrepel library. The ggrepel library provides geom_text_repel() and geom_label_repel(), two geoms that can pick out labels much more flexibly than the default geom_text().
p518 <- ggplot(elections_historic, aes(x = popular_pct, y = ec_pct, label = winner_label))
p518 + geom_hline(yintercept = 0.5, size = 1.4, color = "gray80") +
geom_vline(xintercept = 0.5, size = 1.4, color = "gray80") +
geom_point() +
geom_text_repel() +
scale_x_continuous(labels = scales::percent) +
scale_y_continuous(labels = scales::percent) +
labs(x = "Winner's share Pop. Vote", y = "Winner's share Elec. Vote", title = "Presidents", subtitle = "Elections 1824 - 2016", caption = "Caption text")
Labelling only subsets of the data, based on coditions
p518 <- ggplot(data = by_country, mapping = aes(x = gdp_mean, y = health_mean))
p518 + geom_point() +
geom_text_repel(data = subset(by_country,
gdp_mean > 25000 | health_mean < 1500 | country %in% "Belgium"),
mapping = aes(label = country))
Annotate is for adding text that is not directly related to the data. We can still use the geom_text arguments by using annotate as follows:
p + geom_point() + annotate(geom = “text”, x = 91, y = 33, label = “A surprisingly high recovery rate.”, hjust = 0)
The annotate() function can work with other geoms, too. Use it to draw rectangles, line segments, and arrows. Just remember to pass along the right arguments to the geom you use.
Consistent with ggplot’s overall approach, adjusting some visible feature of the graph means first thinking about the relationship that the feature has with the underlying data. Roughly speaking, if the change you want to make will affect the substantive interpretation of any particular geom, then most likely you will either be mapping an aesthetic to a variable using that geom’s aes() function, or you will be specifying a change via some scale_ function. If the change you want to make does not affect the interpretation of a given geom_, then most likely you will either be setting a variable inside the geom_ function, or making a cosmetic change via the theme() function.
Because we have several potential mappings, and each mapping might be to one of several different scales, we end up with a lot of individual scale_ functions. Each deals with one combination of mapping and scale. scale__()
CHAPTER SIX is mostly about working with statistical models, which I’m skipping for now
CHAPTER SEVEN - MAPS
election %>% select(state, total_vote, r_points, pct_trump, party, census) %>% sample_n(5)
party_colors <- c("#2E74C0","#CB454A")
p72 <- ggplot(data = subset(election, st %nin% "DC"),
mapping = aes(x = r_points,
y = reorder(state, r_points),
color = party))
p72a <- p72 + geom_vline(xintercept = 0, color = "gray30") +
geom_point(size = 2)
p72b <- p72a + scale_color_manual(values = party_colors)
p72c <- p72b + scale_x_continuous(breaks = c(-30, -20, -10, 0, 10, 20, 30, 40),
labels = c("20\n (Clinton)", "20", "10", "0", "10", "20", "30", "40\n(Trump)"))
p72d <- p72c + facet_wrap(~ census, ncol = 1, scales = "free_y") +
guides(color=FALSE) +
labs(x = "Pt. mrgn", y ="")+
theme(axis.text = element_text(size = 8))
p72d
Now we are going to use the maps package (added library call at top). Also add mapproj
us_states <- map_data("state")
p73 <- ggplot(data = us_states, mapping = aes(x = long, y = lat, group = group, fill = region))
p73 + geom_polygon(color = "gray90", size = 0.1) +
coord_map(projection = "albers", lat0 = 39, lat1 = 45) +
guides(fill = FALSE)
You probabble end up merging data you want to show on a state level, with the data that is used for drawing the map. Make sure every join works, or you may end up with NA data. To reiterate, it is important to know your data and variables well enough to check that they have merged properly.
election$region <- tolower(election$state)
us_states_elec <- left_join(us_states, election)
Joining, by = "region"
#head(us_states_elec)
p76 <- ggplot(data = us_states_elec,
aes(x = long, y = lat, group = group, fill = party))
p76 + geom_polygon(color = "gray90", size = 0.1) +
coord_map(projection = "albers", lat0 = 39, lat1 = 45) +
scale_fill_manual(values = party_colors) +
labs(title = "Elections 2016", fill = NULL)
p78 <- ggplot(data = us_states_elec,
aes(x = long, y = lat, group = group, fill = pct_trump))
p78 + geom_polygon(color = "gray90", size = 0.1) +
coord_map(projection = "albers", lat0 = 39, lat1 = 45) +
scale_fill_gradient(low = "white", high = "#CB454A") +
labs(title = "Trump vote", fill = "Percent")
CHAPTER EIGHT looking at opportunities to tweak or customize things. It’s only when we have some specific plot in mind that the question of polishing the results comes up.
p81 <- ggplot(data = subset(asasec, Year == 2014),
mapping = aes(x = Members, y = Revenues, label = Sname))
p81 + geom_point(mapping = aes(color = Journal)) +
geom_smooth(method = "lm", se = FALSE, color = "gray80") +
geom_text_repel(data = subset(asasec,
Year == 2014 & Revenues > 10000),
size = 3) +
labs(x = "Membership", y = "Revenues", color = "Section has own journal",
title = "ASA Sections", subtitle = "2014 calendar year", caption = "Source: ASA annual report") +
scale_y_continuous(labels = scales::dollar) +
theme_dark() +
theme(legend.position = "bottom")
NA
Themes can be turned on or off using the theme_set() function.
Internally, theme functions are a set of detailed instructions to turn on, turn off, or modify a large number of graphical elements on the plot. Once set, a theme applies to all subsequent plots and it remains active until it is replaced by a different theme.
# Democrat Blue and Republican Red
party_colors <- c("#2E74C0", "#CB454A")
p0 <- ggplot(data = subset(county_data,
flipped == "No"),
mapping = aes(x = pop,
y = black/100))
p1 <- p0 + geom_point(alpha = 0.15, color = "gray50") +
scale_x_log10(labels=scales::comma)
p1
p2 <- p1 + geom_point(data = subset(county_data,
flipped == "Yes"),
mapping = aes(x = pop, y = black/100,
color = partywinner16)) +
scale_color_manual(values = party_colors)
p2
p3 <- p2 + scale_y_continuous(labels=scales::percent) +
labs(color = "County flipped to ... ",
x = "County Population (log scale)",
y = "Percent Black Population",
title = "Flipped counties, 2016",
caption = "Counties in gray did not flip.")
p3
p4 <- p3 + geom_text_repel(data = subset(county_data,
flipped == "Yes" &
black > 25),
mapping = aes(x = pop,
y = black/100,
label = state), size = 2)
p4 + theme_minimal() +
theme(legend.position="top")
theme_set(theme_bw())
p4 + theme(legend.position="top")
theme_set(theme_dark())
p4 + theme(legend.position="top")
library(ggthemes)
theme_set(theme_economist())
p4 + theme(legend.position="top")
theme_set(theme_wsj())
p4 + theme(plot.title = element_text(size = rel(0.6)),
legend.title = element_text(size = rel(0.35)),
plot.caption = element_text(size = rel(0.35)),
legend.position = "top")
The theme() function allows you to exert very fine-grained control over the appearance of all kinds of text and graphical elements in a plot.
yrs <- c(seq(1972,1988,4),1993, seq(1996,2016,4))
mean_age <- gss_lon %>%
filter(age %nin% NA && year %in% yrs) %>%
group_by(year) %>%
summarize(xbar = round(mean(age, na.rm = TRUE),0))
mean_age$y <- 0.3
yr_labs <- data.frame(x = 85, y = 0.8, year = yrs)
p816 <- ggplot(data = subset(gss_lon, year %in% yrs),
mapping = aes(x = age))
p816a <- p816 + geom_density(fill = "gray20", color = FALSE,
alpha = 0.9, mapping = aes(y = ..scaled..)) +
geom_vline(data = subset(mean_age, year %in% yrs),
aes(xintercept = xbar), color = "white", size = 0.5) +
geom_text(data = subset(mean_age, year %in% yrs),
aes(x = xbar, y = y, label = xbar), nudge_x = 7.5,
color = "white", size = 3.5, hjust = 1) +
geom_text(data = subset(yr_labs, year %in% yrs),
aes(x = x, y = y, label = year)) +
facet_grid(year ~ ., switch = "y")
p816a
p816a + theme_minimal(base_size = 10) + #, panel_spacing = unit(0.1, "lines"))#, strip_text_size = 32)#, plot_title_size = 10)#, strip_text_size = 32)
theme(plot.title = element_text(size = 16),
axis.text.x= element_text(size = 12),
axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y = element_blank(),
strip.background = element_blank(),
strip.text.y = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank()) +
labs(x = "Age",
y = NULL,
title = "Age Distribution of\nGSS Respondents")
p822 <- ggplot(data = yahoo, mapping = aes(x = Employees, y = Revenue))
p822 + geom_path(color = "gray40", size = 2, alpha = 0.3) +
geom_text(aes(color = Mayer, label = Year),
size = 4, fontface = "bold") +
theme(legend.position = "bottom") +
labs(color = "Mayer is CEO",
x = "employees", y = "revenue (Mln)",
title = "Yahoos") +
scale_y_continuous(labels = scales::dollar) +
scale_x_continuous(labels = scales::comma)
LS0tCnRpdGxlOiAic29jdml6IGRvY3VtZW50YXRpb24iCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KVGhpcyBpcyBteSBkb2N1bWVudGF0aW9uIHdoaWxlIGdvaW5nIHRocm91Z2ggdGhlIGJvb2sgRGF0YSBWaXN1YWxpemF0aW9uLCBBIHByYWN0aWNhbCBpbnRyb2R1Y3Rpb24sIGJ5IEtpZXJhbiBIZWFseTogaHR0cDovL3Zpc3NvYy5jby9pbmRleC5odG1sI3ByZWZhY2UKCkNIQVBURVJTIE9ORSBBTkQgVFdPCgpMZXQncyBmaXJzdCBsb2FkIGFsbCByZXF1aXJlZCBsaWJyYXJpZXM6CmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShzb2N2aXopCmxpYnJhcnkoZ2FwbWluZGVyKQpsaWJyYXJ5KGhlcmUpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShtYXBzKQpsaWJyYXJ5KG1hcHByb2opCmBgYAoKTm93IHRoYXQgb3VyIGZ1bmN0aW9ucyBhcmUgbG9hZGVkIHRocm91Z2ggdGhlaXIgcmVzcGVjdGl2ZSBsaWJyYXJpZXMsIHdlIGNhbiBzdGFydCBsYWRpbmcgc29tZSBkYXQgZnJvbSB0aGUgZ2FwbWluZGVyIGxpYnJhcnk6CmBgYHtyfQpnYXBtaW5kZXIKYGBgCkxldCdzIG1ha2UgYSBxdWljayBwbG90OgpgYGB7cn0KcCA8LSBnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlciwKICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gbGlmZUV4cCkpCnAgKyBnZW9tX3BvaW50KCkKYGBgCgpDSEFQVEVSIFRIUkVFCgpUaGUgY2VudHJhbCBhY3Rpdml0eSBvZiB2aXN1YWxpemluZyBkYXRhIHdpdGggZ2dwbG90IG1vcmUgb3IgbGVzcyBhbHdheXMgaW52b2x2ZXMgdGhlIHNhbWUgc2VxdWVuY2Ugb2Ygc3RlcHMuClRoZXJlIGlzIHNvbWUgc3RydWN0dXJlZCByZWxhdGlvbnNoaXAsIHNvbWUgbWFwcGluZywgYmV0d2VlbiB0aGUgdmFyaWFibGVzIGluIHlvdXIgZGF0YSBhbmQgdGhlaXIgcmVwcmVzZW50YXRpb24gaW4gdGhlIHBsb3QgZGlzcGxheWVkIG9uIHlvdXIgc2NyZWVuIG9yIG9uIHRoZSBwYWdlLgpHZ3Bsb3QgcHJvdmlkZXMgeW91IHdpdGggYSBzZXQgb2YgdG9vbHMgdG8gbWFwIGRhdGEgdG8gdmlzdWFsIGVsZW1lbnRzIG9uIHlvdXIgcGxvdCwgdG8gc3BlY2lmeSB0aGUga2luZCBvZiBwbG90IHlvdSB3YW50LCBhbmQgdGhlbiBzdWJzcXVlbnRseSB0byBjb250cm9sIHRoZSBmaW5lIGRldGFpbHMgb2YgaG93IGl0IHdpbGwgYmUgZGlzcGxheWVkLkluIGdncGxvdCwgdGhlIGxvZ2ljYWwgY29ubmVjdGlvbnMgYmV0d2VlbiB5b3VyIGRhdGEgYW5kIHRoZSBwbG90IGVsZW1lbnRzIGFyZSBjYWxsZWQgYWVzdGhldGljIG1hcHBpbmdzIG9yIGp1c3QgYWVzdGhldGljcy4gCgotVGVsbCB0aGUgZ2dwbG90KCkgZnVuY3Rpb24gd2hhdCB5b3VyIGRhdGEgaXMKLVRoZW4gaG93IHRoZSB2YXJpYWJsZXMgaW4gdGhpcyBkYXRhIGxvZ2ljYWxseSBtYXAgb250byB0aGUgcGxvdOKAmXMgYWVzdGhldGljcwotVGFrZSB0aGUgcmVzdWx0IGFuZCBzYXkgd2hhdCBnZW5lcmFsIHNvcnQgb2YgcGxvdCB5b3Ugd2FudC4gSW4gZ2dwbG90LCB0aGUgb3ZlcmFsbCB0eXBlIG9mIHBsb3QgaXMgY2FsbGVkIGEgZ2VvbS4gRWFjaCBnZW9tIGhhcyBhIGZ1bmN0aW9uIHRoYXQgY3JlYXRlcyBpdC4gCi1Zb3UgY29tYmluZSB0aGVzZSB0d28gcGllY2VzLCB0aGUgZ2dwbG90KCkgb2JqZWN0IGFuZCB0aGUgZ2VvbSwgYnkgbGl0ZXJhbGx5IGFkZGluZyB0aGVtIHRvZ2V0aGVyIGluIGFuIGV4cHJlc3Npb24sIHVzaW5nIHRoZSDigJwr4oCdIHN5bWJvbC4KClNvOgoxLiBEYXRhICh3aGF0IGRhdGEgd2Ugd2FudCB0byB1c2UpCjIuIE1hcHBpbmcgKHdoYXQgcmVsYXRpb25zaGlwcyB3ZSB3YW50IHRvIHNlZSkKMy4gR2VvbSAoaG93IHdlIHdhbnQgdG8gc2VlIHRoZSByZWxhdGlvbnNoaXBzIGluIG91ciBkYXRhKQo0LiBDb29yZGluYXRlcyBhbmQgU2NhbGVzIChyZWZlcmVuY2UpCjUuIExhYmVscyBhbmQgR3VpZGVzIChleHBsYW5hdGlvbikKCmBgYHtyfQpwICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKQpwICArIGdlb21fc21vb3RoKCkgKyBnZW9tX3BvaW50KCkgIyBsb2FkcyB0aGUgcG9pbnRzIGxhc3QsIG9uIHRvcCBvZiB0aGUgc21vb3RoIGxpbmUKcCArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpCgpgYGAKSXTigJlzIHBvc3NpYmxlIHRvIGdpdmUgZ2VvbXMgc2VwYXJhdGUgaW5zdHJ1Y3Rpb25zIHRoYXQgdGhleSB3aWxsIGZvbGxvdyBpbnN0ZWFkLCBidXQgaW4gdGhlIGFic2VuY2Ugb2YgYW55IG90aGVyIGluZm9ybWF0aW9uLCB0aGUgZ2VvbXMgd2lsbCBsb29rIGZvciB0aGUgaW5zdHJ1Y3Rpb25zIGl0IG5lZWRzIGluIHRoZSBnZ3Bsb3QoKSBmdW5jdGlvbiwgb3IgdGhlIG9iamVjdCBjcmVhdGVkIGJ5IGl0LgoKQW4gYWVzdGhldGljIG1hcHBpbmcgc3BlY2lmaWVzIHRoYXQgYSB2YXJpYWJsZSB3aWxsIGJlIGV4cHJlc3NlZCBieSBvbmUgb2YgdGhlIGF2YWlsYWJsZSB2aXN1YWwgZWxlbWVudHMsIHN1Y2ggYXMgc2l6ZSwgb3IgY29sb3IsIG9yIHNoYXBlLCBhbmQgc28gb24uIENvZGUgZG9lcyBub3QgZ2l2ZSBhIGRpcmVjdCBpbnN0cnVjdGlvbiBsaWtlIOKAnGNvbG9yIHRoZSBwb2ludHMgcHVycGxl4oCdLiBJbnN0ZWFkIGl0IHNheXMsIOKAnHRoZSBwcm9wZXJ0eSDigJhjb2xvcuKAmSB3aWxsIHJlcHJlc2VudCB0aGUgdmFyaWFibGUgY29udGluZW504oCdLCBvciDigJxjb2xvciB3aWxsIG1hcCBjb250aW5lbnTigJ0uIFRoZSBhZXMoKSBmdW5jdGlvbiBpcyBmb3IgbWFwcGluZ3Mgb25seS4gRG8gbm90IHVzZSBpdCB0byBjaGFuZ2UgcHJvcGVydGllcyB0byBhIHBhcnRpY3VsYXIgdmFsdWUuIElmIHdlIHdhbnQgdG8gc2V0IGEgcHJvcGVydHksIHdlIGRvIGl0IGluIHRoZSBnZW9tXyB3ZSBhcmUgdXNpbmcsIGFuZCBvdXRzaWRlIHRoZSBtYXBwaW5nID0gYWVzKC4uLikgc3RlcC4gCgpUaGUgdmFyaW91cyBnZW9tXyBmdW5jdGlvbnMgY2FuIHRha2UgbWFueSBvdGhlciBhcmd1bWVudHMgdGhhdCB3aWxsIGFmZmVjdCBob3cgdGhlIHBsb3QgbG9va3MsIGJ1dCB0aGF0IGRvIG5vdCBpbnZvbHZlIG1hcHBpbmcgdmFyaWFibGVzIHRvIGFlc3RoZXRpYyBlbGVtZW50cy4gCgpOb3cgYSBtb3JlIHBvbGlzaGVkIHZpZXc6CmBgYHtyfQpwIDwtIGdncGxvdChkYXRhID0gZ2FwbWluZGVyLCBtYXBwaW5nID0gYWVzKHggPSBnZHBQZXJjYXAsIHkgPSBsaWZlRXhwLCBjb2xvdXIgPSBjb250aW5lbnQsIGZpbGwgPSBjb250aW5lbnQpKQpwICsgZ2VvbV9wb2ludChhbHBoYSA9IDAuMykgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJnYW0iKSArCiAgc2NhbGVfeF9sb2cxMChsYWJlbHMgPSBzY2FsZXM6OmRvbGxhcikgKwogIGxhYnMoeCA9ICJHRFAgcGVyIENhcGl0YSIsIHkgPSAiTGlmZSBFeHBlY3RhbmN5IGluIFllYXJzIiwKICAgICAgIHRpdGxlID0gIkVjb25vbWljIEdyb3d0aCBhbmQgTGlmZSBFeHBlY3RhbmN5LCBieSBDb250aW5lbnQiLAogICAgICAgc3VidGl0bGUgPSAiRGF0YSBwb2ludHMgYXJlIGNvdW50cnkgYW5kIHllYXJzIiwKICAgICAgIGNhcHRpb24gPSAiU291cmNlOiBHYXBtaW5kZXIiKQpgYGAKCkJ5IGRlZmF1bHQsIGdlb21zIGluaGVyaXQgdGhlaXIgbWFwcGluZ3MgZnJvbSB0aGUgZ2dwbG90KCkgZnVuY3Rpb24uIFdlIGNhbiBjaGFuZ2UgdGhpcyBieSBtYXBwaW5nIHRoZSBhZXN0aGV0aWNzIHdlIHdhbnQgb25seSB0byBnZW9tXyBmdW5jdGlvbnMgdGhhdCB3ZSB3YW50IHRoZW0gdG8gYXBwbHkgdG8uIFdlIHVzZSB0aGUgc2FtZSBtYXBwaW5nID0gYWVzKC4uLikgZXhwcmVzc2lvbiBhcyBpbiB0aGUgaW5pdGlhbCBjYWxsIHRvIGdncGxvdCgpLCBidXQgbm93IHVzZSBpdCBpbiB0aGUgZ2VvbV8gZnVuY3Rpb25zIGFzIHdlbGwsIHNwZWNpZnlpbmcgb25seSB0aGUgbWFwcGluZ3Mgd2Ugd2FudCB0byBhcHBseSB0byBlYWNoIG9uZS4gTWFwcGluZ3Mgc3BlY2lmaWVkIG9ubHkgaW4gdGhlIGluaXRpYWwgZ2dwbG90KCkgZnVuY3Rpb24gd2lsbCBjYXJyeSB0aHJvdWdoIHRvIGFsbCBzdWJzZXF1ZW50IGdlb21zLgoKYGBge3J9CnAgPC0gZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXIsIG1hcHBpbmcgPSBhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHApKQpwICsgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKGNvbG9yID0gY29udGluZW50KSkgKwogICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIiwgY29sb3IgPSAiYmxhY2siKSArCiAgICBzY2FsZV94X2xvZzEwKGxhYmVscyA9IHNjYWxlczo6ZG9sbGFyKSArCiAgICBsYWJzKHggPSAiR0RQIHBlciBDYXBpdGEiLCB5ID0gIkxpZmUgRXhwZWN0YW5jeSBpbiBZZWFycyIsCiAgICAgICB0aXRsZSA9ICJFY29ub21pYyBHcm93dGggYW5kIExpZmUgRXhwZWN0YW5jeSwgYnkgQ29udGluZW50IiwKICAgICAgIHN1YnRpdGxlID0gIkRhdGEgcG9pbnRzIGFyZSBjb3VudHJ5IGFuZCB5ZWFycyIsCiAgICAgICBjYXB0aW9uID0gIlNvdXJjZTogR2FwbWluZGVyIikKYGBgCgpZb3UgY2FuIHNldCB0aGUgZGVmYXVsdCBzaXplIG9mIHBsb3RzIHdpdGhpbiB5b3VyIC5SbWQgZG9jdW1lbnQgYnkgc2V0dGluZyBhbiBvcHRpb24gaW4geW91ciBmaXJzdCBjb2RlIGNodW5rLiBUaGlzIG9uZSB0ZWxscyBSIHRvIG1ha2UgOHg1IGZpZ3VyZXM6CgpgYGB7cn0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD01KSAjIHRoaXMgc2V0cyB0aGUgZW52aXJvbm1lbnQncyBkZWZhdWx0CmBgYAoKTGlrZSBzbywgb24gYSBwZXItY2hhcnQgYmFzaXM6CmBgYHtyICwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA0fQpwIDwtIGdncGxvdChkYXRhID0gZ2FwbWluZGVyLCBtYXBwaW5nID0gYWVzKHggPSBnZHBQZXJjYXAsIHkgPSBsaWZlRXhwKSkKcCArIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyhjb2xvciA9IGNvbnRpbmVudCkpICsKICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIGNvbG9yID0gImJsYWNrIikgKwogICAgc2NhbGVfeF9sb2cxMChsYWJlbHMgPSBzY2FsZXM6OmRvbGxhcikgKwogICAgbGFicyh4ID0gIkdEUCBwZXIgQ2FwaXRhIiwgeSA9ICJMaWZlIEV4cGVjdGFuY3kgaW4gWWVhcnMiLAogICAgICAgdGl0bGUgPSAiRWNvbm9taWMgR3Jvd3RoIGFuZCBMaWZlIEV4cGVjdGFuY3ksIGJ5IENvbnRpbmVudCIsCiAgICAgICBzdWJ0aXRsZSA9ICJEYXRhIHBvaW50cyBhcmUgY291bnRyeSBhbmQgeWVhcnMiLAogICAgICAgY2FwdGlvbiA9ICJTb3VyY2U6IEdhcG1pbmRlciIpCmBgYAoKTm93IHNhdmUgYW4gaW1hZ2Ugb2YgdGhlIGNoYXJ0ICh1c2luZyB0aGUgaGVyZS1wYWNrYWdlKQpgYGB7ciAsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNH0KcCA8LSBnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlciwgbWFwcGluZyA9IGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gbGlmZUV4cCkpCnAgKyBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoY29sb3IgPSBjb250aW5lbnQpKSArCiAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBjb2xvciA9ICJibGFjayIpICsKICAgIHNjYWxlX3hfbG9nMTAobGFiZWxzID0gc2NhbGVzOjpkb2xsYXIpICsKICAgIGxhYnMoeCA9ICJHRFAgcGVyIENhcGl0YSIsIHkgPSAiTGlmZSBFeHBlY3RhbmN5IGluIFllYXJzIiwKICAgICAgIHRpdGxlID0gIkVjb25vbWljIEdyb3d0aCBhbmQgTGlmZSBFeHBlY3RhbmN5LCBieSBDb250aW5lbnQiLAogICAgICAgc3VidGl0bGUgPSAiRGF0YSBwb2ludHMgYXJlIGNvdW50cnkgYW5kIHllYXJzIiwKICAgICAgIGNhcHRpb24gPSAiU291cmNlOiBHYXBtaW5kZXIiKSArCiAgICBnZ3NhdmUoaGVyZSgiZmlndXJlcyIsICJsaWZlZXhwX3ZzX2dkcC5wbmciKSwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNCkKYGBgCgpDSEFQVEVSIEZPVVIKVGhlIGdncGxvdCBsaWJyYXJ5IGlzIGFuIGltcGxlbWVudGF0aW9uIG9mIHRoZSDigJxncmFtbWFy4oCdIG9mIGdyYXBoaWNzOiBhIHNldCBvZiBydWxlcyBmb3IgcHJvZHVjaW5nIGdyYXBoaWNzIGZyb20gZGF0YSwgdGFraW5nIHBpZWNlcyBvZiBkYXRhIGFuZCBtYXBwaW5nIHRoZW0gdG8gZ2VvbWV0cmljIG9iamVjdHMgdGhhdCBoYXZlIGFlc3RoZXRpYyBhdHRyaWJ1dGVzLCB0b2dldGhlciB3aXRoIGZ1cnRoZXIgcnVsZXMgZm9yIHRyYW5zZm9ybWluZyB0aGUgZGF0YSBpZiBuZWVkZWQsIGFkanVzdGluZyBzY2FsZXMsIGFuZCBwcm9qZWN0aW5nIHRoZSByZXN1bHRzIG9udG8gYSBkaWZmZXJlbnQgY29vcmRpbmF0ZSBzeXN0ZW0uCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA0fQpwNCA8LSBnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlciwgbWFwcGluZyA9IGFlcyh4ID0geWVhciwgeSA9IGdkcFBlcmNhcCkpCnA0ICsgZ2VvbV9saW5lKGNvbG9yID0gImdyYXk3MCIsIGFlcyhncm91cCA9IGNvdW50cnkpKSArCiAgZ2VvbV9zbW9vdGgoc2l6ZSA9IDEuMSwgbWV0aG9kID0gImxvZXNzIiwgc2UgPSBGQUxTRSkgKwogIHNjYWxlX3lfbG9nMTAobGFiZWxzID0gc2NhbGVzOjpkb2xsYXIpICsKICBmYWNldF93cmFwKH4gY29udGluZW50LCBuY29sID0gNSkgKwogIGxhYnMoeCA9ICJZZWFyIiwKICAgICAgIHkgPSAiR0RQIHBlciBDYXBpdGEiLAogICAgICAgdGl0bGUgPSAiR0RQIHBlciBDYXBpdGEgb24gRml2ZSBDb250aW5lbnRzIikKYGBgCgpOb3cgbGV0J3MgY3JlYXRlIGEgZ3JpZCBvZiBtb3JlIHRoYW4gb25lIHZhcmlhYmxlOgpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA2fQpwNDUgPC0gZ2dwbG90KGRhdGEgPSBnc3Nfc20sIG1hcHBpbmcgPSBhZXMoeCA9IGFnZSwgeSA9IGNoaWxkcykpCnA0NSArIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpICsKICBnZW9tX3Ntb290aCgpICsKICBmYWNldF9ncmlkKHNleCB+IHJhY2UpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpFdmVyeSBnZW9tXyBmdW5jdGlvbiBoYXMgYW4gYXNzb2NpYXRlZCBzdGF0XyBmdW5jdGlvbiB0aGF0IGl0IHVzZXMgYnkgZGVmYXVsdC4gVGhlIHJldmVyc2UgaXMgYWxzbyB0aGUgY2FzZTogZXZlcnkgc3RhdF8gZnVuY3Rpb24gaGFzIGFuIGFzc29jaWF0ZWQgZ2VvbV8gZnVuY3Rpb24gdGhhdCBpdCB3aWxsIHBsb3QgYnkgZGVmYXVsdCBpZiB5b3UgYXNrIGl0IHRvLiBCZWxvdyB3ZSBjYWxsIHRoZSBwcm9wKG9ydGlvbikgc3RhdCwgcmF0aGVyIHRoYW4gdGhlIGRlZmF1bHQgY291bnQgc3RhdC4gT3IgLi5jb3VudC4uCgpgYGB7cn0KcDQ2IDwtIGdncGxvdChkYXRhID0gZ3NzX3NtLCBtYXBwaW5nID0gYWVzKHggPSBiaWdyZWdpb24pKQpwNDYgKyBnZW9tX2JhcihtYXBwaW5nID0gYWVzKHkgPSAuLnByb3AuLikpCmBgYApgYGB7cn0KcDQ2IDwtIGdncGxvdChkYXRhID0gZ3NzX3NtLCBtYXBwaW5nID0gYWVzKHggPSBiaWdyZWdpb24pKQpwNDYgKyBnZW9tX2JhcihtYXBwaW5nID0gYWVzKHkgPSAuLnByb3AuLiwgZ3JvdXAgPSAxKSkgIyB0aGlzIHRlbGxzIFIgdGhhdCB3aGF0IHdlIGdyb3VwIGJ5IChiaXJlZ2lvbikgc2hvdWxkIGFkZCB1cCB0byAxOyB0byBzZWUgcHJvcG9ydGlvbiBvZiB0b3RhbCByYXRoZXIgdGhhbiBwcm9wb3J0aW9uIHBlciBiaXJlZ2lvbgoKYGBgCgpgYGB7cn0KcDQ5IDwtIGdncGxvdChkYXRhID0gZ3NzX3NtLCBtYXBwaW5nID0gYWVzKHggPSByZWxpZ2lvbiwgZmlsbCA9IHJlbGlnaW9uKSkKcDQ5ICsgZ2VvbV9iYXIobWFwcGluZyA9IGFlcyh5ID0gLi5wcm9wLi4sIGdyb3VwID0gMSkpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGd1aWRlcyhmaWxsID0gRkFMU0UpIApgYGAKCmBgYHtyfQpwNDEwIDwtIGdncGxvdChkYXRhID0gZ3NzX3NtLAogICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBiaWdyZWdpb24sIGZpbGwgPSByZWxpZ2lvbikpCnA0MTAgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikKCnA0MTAgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIsCiAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHkgPSAuLnByb3AuLiwgZ3JvdXAgPSByZWxpZ2lvbikpCmBgYAoKYGBge3J9CnA0MTQgPC0gZ2dwbG90KGRhdGEgPSBnc3Nfc20sCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IHJlbGlnaW9uKSkKcDQxNCArIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwKICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeSA9IC4ucHJvcC4uLCBncm91cCA9IGJpZ3JlZ2lvbikpICsKICAgIGZhY2V0X3dyYXAofiBiaWdyZWdpb24sIG5jb2wgPSAyKSArCiAgICB0aGVtZV9taW5pbWFsKCkKYGBgCgpgYGB7cn0KcDQxNiA8LSBnZ3Bsb3QoZGF0YSA9IHN1YnNldChtaWR3ZXN0LCBzdWJzZXQgPSBzdGF0ZSAlaW4lIGMoIk9IIiwgIldJIiwgIklMIikpLAogICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBwZXJjb2xsZWdlLCBmaWxsID0gc3RhdGUpKQpwNDE2ICsgZ2VvbV9oaXN0b2dyYW0oYWxwaGEgPSAwLjQsIGJpbnMgPSAyMCkKCnA0MTggPC0gZ2dwbG90KGRhdGEgPSBtaWR3ZXN0LAogICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBhcmVhLCBmaWxsID0gc3RhdGUsIGNvbG9yID0gc3RhdGUpKQpwNDE4ICsgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC40KQpgYGAKCk5vdyBzb21lIFRpdGFuaWMgZGF0YSwgdG8gc2hvdyBzdGF0ID0gJ2lkZW50aXR5JyB0ZWxscyBnZW9tX2JhciBub3QgdG8gZG8gYW55IHN0YXQgY2FsY3VsYXRpb24gKGxpa2UgY291bnQgb3IgcHJvcCkuIEFsdGVybmF0aXZlbHkgeW91IGNhbiB1c2UgZ2VvbV9jb2woKSB3aGljaCBkb2VzIHRoaXMgYXV0b21hdGljYWxseSAoaWdub3JlIHN0YXQgY2FsY3VhdGlvbikuCgpBbHNvIG5vdGUgaG93IHBvc2l0aW9uID0gJ2RvZGdlJyBjaGFuZ2VzIHRoZSBiYXIgY2hhcnQgZnJvbSBzdGFja2VkIHRvIHNpZGUtYnktc2lkZQpgYGB7cn0KcDQyMCA8LSBnZ3Bsb3QoZGF0YSA9IHRpdGFuaWMsIG1hcHBpbmcgPSBhZXMoeCA9IGZhdGUsIHkgPSBwZXJjZW50LCBmaWxsID0gc2V4KSkKCnA0MjAgKyBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JykgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCgpwNDIwICsgZ2VvbV9iYXIocG9zaXRpb24gPSAnZG9kZ2UnLHN0YXQgPSAnaWRlbnRpdHknKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgpOb3cgdHJ5IGEgZGlmZmVyZW5jZSBtYXAsIGJ1dCBzaG93IGl0IHZlcnRpY2FsIGluc3RlYWQgb2YgdGhlIGhvcml6b250YWwgdmlldyB0aGUgYm9vayBwcm92aWRlcwpgYGB7ciwgZmlnLndpZHRoPTEyfQpwNDIxIDwtIGdncGxvdChkYXRhID0gb2VjZF9zdW0sIG1hcHBpbmcgPSBhZXMoeCA9IHllYXIsIHkgPSBkaWZmLCBmaWxsID0gaGlfbG8pKQpwNDIxICsgZ2VvbV9jb2woKSArCiAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh4ID0gTlVMTCwgeSA9ICJEaWZmZXJlbmNlIGluIHllYXJzIiwKICAgICAgIHRpdGxlID0gIlVTIHZzIHJlc3QgTGlmZSBFeHBlY3RhbmN5IGdhcCIpCmBgYAoKQ0hBUFRFUiBGSVZFClByZXBwaW5nIHRoZSBkYXRhIGJlZm9yZSB2aXN1YWxpemluZyBpdCwgdXNpbmcgZHBseXIuIEFsc28gaW50cm9kdWNpbmcgdGhlIHBpcGUtb3BlcmF0b3I6ICU+JS4gRGF0YSBnb2VzIGluIG9uZSBzaWRlIG9mIHRoZSBwaXBlLCBhY3Rpb25zIGFyZSBwZXJmb3JtZWQgdmlhIGZ1bmN0aW9ucywgYW5kIHJlc3VsdHMgY29tZSBvdXQgdGhlIG90aGVyLiBBIHBpcGVsaW5lIGlzIHR5cGljYWxseSBhIHNlcmllcyBvZiBvcGVyYXRpb25zLCBsaWtlIGdyb3VwX2J5KCksIGZpbHRlcigpIHJvd3MsIHNlbGVjdCgpIGNvbHVtbnMsIG11dGF0ZSgpLCBhbmQgc3VtbWFyaXplKCkuIEFzIGRwbHly4oCZcyBmdW5jdGlvbnMgc2VlIHRoaW5ncywgc3VtbWFyaXppbmcgYWN0aW9ucyDigJxwZWVsIG9mZuKAnSBvbmUgZ3JvdXBpbmcgbGV2ZWwgYXQgYSB0aW1lLCBzbyB0aGF0IHRoZSByZXN1bHRpbmcgc3VtbWFyaWVzIGFyZSBhdCB0aGUgbmV4dCBsZXZlbCB1cC4gCgpMZXQncyBzdGFydCBieSBjcmVhdGluZyBhIHRhYmxlOgpgYGB7cn0KcmVsX2J5X3JlZ2lvbiA8LSBnc3Nfc20gJT4lCiAgZ3JvdXBfYnkoYmlncmVnaW9uLCByZWxpZ2lvbikgJT4lCiAgc3VtbWFyaXplKE4gPSBuKCkpICU+JQogIG11dGF0ZShmcmVxID0gTiAvIHN1bShOKSwKICAgICAgICAgcGN0ID0gcm91bmQoKGZyZXEqMTAwKSwgMCkpCmBgYApUaGUgdmFyaWFibGVzIHNwZWNpZmllZCBpbiBncm91cF9ieSgpIGFyZSByZXRhaW5lZCBpbiB0aGUgbmV3IHN1bW1hcnkgdGFibGU7IHRoZSB2YXJpYWJsZXMgY3JlYXRlZCB3aXRoIHN1bW1hcml6ZSgpIGFuZCBtdXRhdGUoKSBhcmUgYWRkZWQsIGFuZCBhbGwgdGhlIG90aGVyIHZhcmlhYmxlcyBpbiB0aGUgb3JpZ2luYWwgZGF0YXNldCBhcmUgZHJvcHBlZC4KCmBgYHtyfQpwNTIgPC0gZ2dwbG90KHJlbF9ieV9yZWdpb24sIGFlcyh4ID0gYmlncmVnaW9uLCB5ID0gcGN0LCBmaWxsID0gcmVsaWdpb24pKQpwNTIgKyBnZW9tX2NvbChwb3NpdGlvbiA9ICJkb2RnZTIiKSArCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UyIiwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gTkEpICsKICBsYWJzKHggPSAicmVnaW9uIiwgeSA9ICJwZXJjZW50IiwgZmlsbCA9ICJyZWxpZ2lvbiIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgpUd28gd2F5cyBvZiBkcmF3aW5nIHRoZSBzYW1lIChhbHRob3VnaCB0aGUgYm9vayBtZW5pb25zIGl0IGNhbm5vdCB3b3JrIHRoaXMgd2F5KQpgYGB7cn0KcDUzIDwtIGdncGxvdChyZWxfYnlfcmVnaW9uLCBhZXMoeCA9IHJlbGlnaW9uLCB5ID0gcGN0LCBmaWxsID0gcmVsaWdpb24pKQpwNTMgKyBnZW9tX2NvbChwb3NpdGlvbiA9ICJkb2RnZTIiKSArCiAgbGFicyh4ID0gTlVMTCwgeSA9ICIlIiwgZmlsbCA9ICJSZWxpZ2lvbnMiKSArCiAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgZmFjZXRfZ3JpZCh+IGJpZ3JlZ2lvbikKCnA1M2EgPC0gZ2dwbG90KHJlbF9ieV9yZWdpb24sIGFlcyh4ID0gcGN0LCB5ID0gcmVsaWdpb24sIGZpbGwgPSByZWxpZ2lvbikpCnA1M2EgKyBnZW9tX2NvbChwb3NpdGlvbiA9ICJkb2RnZTIiKSArCiAgbGFicyh4ID0gIiUiLCB5ID0gTlVMTCkgKwogIGd1aWRlcyhmaWxsID0gRkFMU0UpICsKICBmYWNldF9ncmlkKH5iaWdyZWdpb24pCgpgYGAKYGBge3J9CnA1NCA8LSBnZ3Bsb3QoZGF0YSA9IG9yZ2FuZGF0YSwgYWVzKHggPSB5ZWFyLCB5ID0gZG9ub3JzKSkKcDU0ICsgZ2VvbV9wb2ludCgpCmBgYApgYGB7cn0KcDU1IDwtIGdncGxvdChkYXRhID0gb3JnYW5kYXRhLCBhZXMoeCA9IHllYXIsIHkgPSBkb25vcnMpKQpwNTUgKyBnZW9tX2xpbmUoYWVzKGdyb3VwID0gY291bnRyeSkpICsgZmFjZXRfd3JhcCh+IGNvdW50cnkpICsgdGhlbWVfbWluaW1hbCgpCnA1NiA8LSBnZ3Bsb3QoZGF0YSA9IG9yZ2FuZGF0YSwgYWVzKHggPSBjb3VudHJ5LCB5ID0gZG9ub3JzKSkKcDU2ICsgZ2VvbV9ib3hwbG90KCkgKyBjb29yZF9mbGlwKCkKYGBgClRoZSByZW9yZGVyKCkgZnVuY3Rpb24gd2lsbCBkbyB0aGlzIGZvciB1cy4gSXQgdGFrZXMgdHdvIHJlcXVpcmVkIGFyZ3VtZW50cy4gVGhlIGZpcnN0IGlzIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBvciBmYWN0b3IgdGhhdCB3ZSB3YW50IHRvIHJlb3JkZXIuIFRoZSBzZWNvbmQgaXMgdGhlIHZhcmlhYmxlIHdlIHdhbnQgdG8gcmVvcmRlciBpdCBieS4gVGhlIHRoaXJkIGFuZCBvcHRpb25hbCBhcmd1bWVudCB0byByZW9yZGVyKCkgaXMgdGhlIGZ1bmN0aW9uIHlvdSB3YW50IHRvIHVzZSBhcyBhIHN1bW1hcnkgc3RhdGlzdGljLgoKYGBge3J9CnA1OCA8LSBnZ3Bsb3QoZGF0YSA9IG9yZ2FuZGF0YSwgYWVzKHggPSByZW9yZGVyKGNvdW50cnksIGRvbm9ycywgbmEucm09VFJVRSkgLHkgPSBkb25vcnMsIGZpbGwgPSB3b3JsZCkpCnA1OCArIGdlb21fYm94cGxvdCgpICsgbGFicyh4PU5VTEwpICsgY29vcmRfZmxpcCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKYGBge3J9CmJ5X2NvdW50cnkgPC0gb3JnYW5kYXRhICU+JSBncm91cF9ieShjb25zZW50X2xhdywgY291bnRyeSkgJT4lCiAgICBzdW1tYXJpemUoZG9ub3JzX21lYW49IG1lYW4oZG9ub3JzLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgIGRvbm9yc19zZCA9IHNkKGRvbm9ycywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICBnZHBfbWVhbiA9IG1lYW4oZ2RwLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgIGhlYWx0aF9tZWFuID0gbWVhbihoZWFsdGgsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgcm9hZHNfbWVhbiA9IG1lYW4ocm9hZHMsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgY2VyZWJ2YXNfbWVhbiA9IG1lYW4oY2VyZWJ2YXMsIG5hLnJtID0gVFJVRSkpCgpieV9jb3VudHJ5IDwtIG9yZ2FuZGF0YSAlPiUgZ3JvdXBfYnkoY29uc2VudF9sYXcsIGNvdW50cnkpICU+JQogICAgc3VtbWFyaXplX2lmKGlzLm51bWVyaWMsIGZ1bnMobWVhbiwgc2QpLCBuYS5ybSA9IFRSVUUpICU+JQogICAgdW5ncm91cCgpCmBgYAoKVGhlIG5leHQgcGhhc2UgaXMgYWRkaW5nIHNvbWUgbGFiZWxzLiBBbmQgYWx0aG91Z2ggZ2VvbV90ZXh0KCkgZG9lcyB0aGUgdHJpY2ssIGl0IGRvZXNuJ3QgaGF2ZSBtYW55IGNsZWFuIG9wdGlvbnMgdGhhdCB3b3JrIGlmIHRoZSBzdGFuZGFyZCBkb2VzIG5vdCBzdWZmaWNlLiBUaGF0J3Mgd2hlcmUgd2UgbG9hZCB0aGUgZ2dyZXBlbCBsaWJyYXJ5LiBUaGUgZ2dyZXBlbCBsaWJyYXJ5IHByb3ZpZGVzIGdlb21fdGV4dF9yZXBlbCgpIGFuZCBnZW9tX2xhYmVsX3JlcGVsKCksIHR3byBnZW9tcyB0aGF0IGNhbiBwaWNrIG91dCBsYWJlbHMgbXVjaCBtb3JlIGZsZXhpYmx5IHRoYW4gdGhlIGRlZmF1bHQgZ2VvbV90ZXh0KCkuIApgYGB7ciwgZmlnLndpZHRoPTEyLGZpZy5oZWlnaHQ9Nn0KcDUxOCA8LSBnZ3Bsb3QoZWxlY3Rpb25zX2hpc3RvcmljLCBhZXMoeCA9IHBvcHVsYXJfcGN0LCB5ID0gZWNfcGN0LCBsYWJlbCA9IHdpbm5lcl9sYWJlbCkpCnA1MTggKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLjUsIHNpemUgPSAxLjQsIGNvbG9yID0gImdyYXk4MCIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLjUsIHNpemUgPSAxLjQsIGNvbG9yID0gImdyYXk4MCIpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fdGV4dF9yZXBlbCgpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKwogIGxhYnMoeCA9ICJXaW5uZXIncyBzaGFyZSBQb3AuIFZvdGUiLCB5ID0gIldpbm5lcidzIHNoYXJlIEVsZWMuIFZvdGUiLCB0aXRsZSA9ICJQcmVzaWRlbnRzIiwgc3VidGl0bGUgPSAiRWxlY3Rpb25zIDE4MjQgLSAyMDE2IiwgY2FwdGlvbiA9ICJDYXB0aW9uIHRleHQiKQpgYGAKCkxhYmVsbGluZyBvbmx5IHN1YnNldHMgb2YgdGhlIGRhdGEsIGJhc2VkIG9uIGNvZGl0aW9ucwpgYGB7cn0KcDUxOCA8LSBnZ3Bsb3QoZGF0YSA9IGJ5X2NvdW50cnksIG1hcHBpbmcgPSBhZXMoeCA9IGdkcF9tZWFuLCB5ID0gaGVhbHRoX21lYW4pKQpwNTE4ICsgZ2VvbV9wb2ludCgpICsKICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHN1YnNldChieV9jb3VudHJ5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZHBfbWVhbiA+IDI1MDAwIHwgaGVhbHRoX21lYW4gPCAxNTAwIHwgY291bnRyeSAlaW4lICJCZWxnaXVtIiksIAogICAgICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKGxhYmVsID0gY291bnRyeSkpCmBgYAoKQW5ub3RhdGUgaXMgZm9yIGFkZGluZyB0ZXh0IHRoYXQgaXMgbm90IGRpcmVjdGx5IHJlbGF0ZWQgdG8gdGhlIGRhdGEuIFdlIGNhbiBzdGlsbCB1c2UgdGhlIGdlb21fdGV4dCBhcmd1bWVudHMgYnkgdXNpbmcgYW5ub3RhdGUgYXMgZm9sbG93czoKCnAgKyBnZW9tX3BvaW50KCkgKyBhbm5vdGF0ZShnZW9tID0gInRleHQiLCB4ID0gOTEsIHkgPSAzMywgbGFiZWwgPSAiQSBzdXJwcmlzaW5nbHkgaGlnaCBcbiByZWNvdmVyeSByYXRlLiIsIGhqdXN0ID0gMCkKClRoZSBhbm5vdGF0ZSgpIGZ1bmN0aW9uIGNhbiB3b3JrIHdpdGggb3RoZXIgZ2VvbXMsIHRvby4gVXNlIGl0IHRvIGRyYXcgcmVjdGFuZ2xlcywgbGluZSBzZWdtZW50cywgYW5kIGFycm93cy4gSnVzdCByZW1lbWJlciB0byBwYXNzIGFsb25nIHRoZSByaWdodCBhcmd1bWVudHMgdG8gdGhlIGdlb20geW91IHVzZS4KCkNvbnNpc3RlbnQgd2l0aCBnZ3Bsb3TigJlzIG92ZXJhbGwgYXBwcm9hY2gsIGFkanVzdGluZyBzb21lIHZpc2libGUgZmVhdHVyZSBvZiB0aGUgZ3JhcGggbWVhbnMgZmlyc3QgdGhpbmtpbmcgYWJvdXQgdGhlIHJlbGF0aW9uc2hpcCB0aGF0IHRoZSBmZWF0dXJlIGhhcyB3aXRoIHRoZSB1bmRlcmx5aW5nIGRhdGEuIFJvdWdobHkgc3BlYWtpbmcsIGlmIHRoZSBjaGFuZ2UgeW91IHdhbnQgdG8gbWFrZSB3aWxsIGFmZmVjdCB0aGUgc3Vic3RhbnRpdmUgaW50ZXJwcmV0YXRpb24gb2YgYW55IHBhcnRpY3VsYXIgZ2VvbSwgdGhlbiBtb3N0IGxpa2VseSB5b3Ugd2lsbCBlaXRoZXIgYmUgbWFwcGluZyBhbiBhZXN0aGV0aWMgdG8gYSB2YXJpYWJsZSB1c2luZyB0aGF0IGdlb23igJlzIGFlcygpIGZ1bmN0aW9uLCBvciB5b3Ugd2lsbCBiZSBzcGVjaWZ5aW5nIGEgY2hhbmdlIHZpYSBzb21lIHNjYWxlXyBmdW5jdGlvbi4gSWYgdGhlIGNoYW5nZSB5b3Ugd2FudCB0byBtYWtlIGRvZXMgbm90IGFmZmVjdCB0aGUgaW50ZXJwcmV0YXRpb24gb2YgYSBnaXZlbiBnZW9tXywgdGhlbiBtb3N0IGxpa2VseSB5b3Ugd2lsbCBlaXRoZXIgYmUgc2V0dGluZyBhIHZhcmlhYmxlIGluc2lkZSB0aGUgZ2VvbV8gZnVuY3Rpb24sIG9yIG1ha2luZyBhIGNvc21ldGljIGNoYW5nZSB2aWEgdGhlIHRoZW1lKCkgZnVuY3Rpb24uCgpCZWNhdXNlIHdlIGhhdmUgc2V2ZXJhbCBwb3RlbnRpYWwgbWFwcGluZ3MsIGFuZCBlYWNoIG1hcHBpbmcgbWlnaHQgYmUgdG8gb25lIG9mIHNldmVyYWwgZGlmZmVyZW50IHNjYWxlcywgd2UgZW5kIHVwIHdpdGggYSBsb3Qgb2YgaW5kaXZpZHVhbCBzY2FsZV8gZnVuY3Rpb25zLiBFYWNoIGRlYWxzIHdpdGggb25lIGNvbWJpbmF0aW9uIG9mIG1hcHBpbmcgYW5kIHNjYWxlLiBzY2FsZV88TUFQUElORz5fPEtJTkQ+KCkKCkNIQVBURVIgU0lYIGlzIG1vc3RseSBhYm91dCB3b3JraW5nIHdpdGggc3RhdGlzdGljYWwgbW9kZWxzLCB3aGljaCBJJ20gc2tpcHBpbmcgZm9yIG5vdwoKQ0hBUFRFUiBTRVZFTiAtIE1BUFMKCmBgYHtyfQplbGVjdGlvbiAlPiUgc2VsZWN0KHN0YXRlLCB0b3RhbF92b3RlLCByX3BvaW50cywgcGN0X3RydW1wLCBwYXJ0eSwgY2Vuc3VzKSAlPiUgc2FtcGxlX24oNSkKCnBhcnR5X2NvbG9ycyA8LSBjKCIjMkU3NEMwIiwiI0NCNDU0QSIpCgpwNzIgPC0gZ2dwbG90KGRhdGEgPSBzdWJzZXQoZWxlY3Rpb24sIHN0ICVuaW4lICJEQyIpLAogICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IHJfcG9pbnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IHJlb3JkZXIoc3RhdGUsIHJfcG9pbnRzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gcGFydHkpKQpwNzJhIDwtIHA3MiArIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGNvbG9yID0gImdyYXkzMCIpICsKICBnZW9tX3BvaW50KHNpemUgPSAyKQoKcDcyYiA8LSBwNzJhICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhcnR5X2NvbG9ycykKCnA3MmMgPC0gcDcyYiArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBjKC0zMCwgLTIwLCAtMTAsIDAsIDEwLCAyMCwgMzAsIDQwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjIwXG4gKENsaW50b24pIiwgIjIwIiwgIjEwIiwgIjAiLCAiMTAiLCAiMjAiLCAiMzAiLCAiNDBcbihUcnVtcCkiKSkKCnA3MmQgPC0gcDcyYyArIGZhY2V0X3dyYXAofiBjZW5zdXMsIG5jb2wgPSAxLCBzY2FsZXMgPSAiZnJlZV95IikgKwogIGd1aWRlcyhjb2xvcj1GQUxTRSkgKwogIGxhYnMoeCA9ICJQdC4gbXJnbiIsIHkgPSIiKSsKICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKQoKcDcyZApgYGAKCk5vdyB3ZSBhcmUgZ29pbmcgdG8gdXNlIHRoZSBtYXBzIHBhY2thZ2UgKGFkZGVkIGxpYnJhcnkgY2FsbCBhdCB0b3ApLiBBbHNvIGFkZCBtYXBwcm9qCmBgYHtyfQp1c19zdGF0ZXMgPC0gbWFwX2RhdGEoInN0YXRlIikKcDczIDwtIGdncGxvdChkYXRhID0gdXNfc3RhdGVzLCBtYXBwaW5nID0gYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwLCBmaWxsID0gcmVnaW9uKSkKcDczICsgZ2VvbV9wb2x5Z29uKGNvbG9yID0gImdyYXk5MCIsIHNpemUgPSAwLjEpICsgCiAgY29vcmRfbWFwKHByb2plY3Rpb24gPSAiYWxiZXJzIiwgbGF0MCA9IDM5LCBsYXQxID0gNDUpICsKICBndWlkZXMoZmlsbCA9IEZBTFNFKQpgYGAKWW91IHByb2JhYmJsZSBlbmQgdXAgbWVyZ2luZyBkYXRhIHlvdSB3YW50IHRvIHNob3cgb24gYSBzdGF0ZSBsZXZlbCwgd2l0aCB0aGUgZGF0YSB0aGF0IGlzIHVzZWQgZm9yIGRyYXdpbmcgdGhlIG1hcC4gTWFrZSBzdXJlIGV2ZXJ5IGpvaW4gd29ya3MsIG9yIHlvdSBtYXkgZW5kIHVwIHdpdGggTkEgZGF0YS4gVG8gcmVpdGVyYXRlLCBpdCBpcyBpbXBvcnRhbnQgdG8ga25vdyB5b3VyIGRhdGEgYW5kIHZhcmlhYmxlcyB3ZWxsIGVub3VnaCB0byBjaGVjayB0aGF0IHRoZXkgaGF2ZSBtZXJnZWQgcHJvcGVybHkuIApgYGB7cn0KZWxlY3Rpb24kcmVnaW9uIDwtIHRvbG93ZXIoZWxlY3Rpb24kc3RhdGUpCnVzX3N0YXRlc19lbGVjIDwtIGxlZnRfam9pbih1c19zdGF0ZXMsIGVsZWN0aW9uKQojaGVhZCh1c19zdGF0ZXNfZWxlYykKcDc2IDwtIGdncGxvdChkYXRhID0gdXNfc3RhdGVzX2VsZWMsCiAgICAgICAgICAgICAgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwLCBmaWxsID0gcGFydHkpKQpwNzYgKyBnZW9tX3BvbHlnb24oY29sb3IgPSAiZ3JheTkwIiwgc2l6ZSA9IDAuMSkgKwogIGNvb3JkX21hcChwcm9qZWN0aW9uID0gImFsYmVycyIsIGxhdDAgPSAzOSwgbGF0MSA9IDQ1KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFydHlfY29sb3JzKSArCiAgbGFicyh0aXRsZSA9ICJFbGVjdGlvbnMgMjAxNiIsIGZpbGwgPSBOVUxMKSAKCmBgYApgYGB7cn0KcDc4IDwtIGdncGxvdChkYXRhID0gdXNfc3RhdGVzX2VsZWMsCiAgICAgICAgICAgICAgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwLCBmaWxsID0gcGN0X3RydW1wKSkKcDc4ICsgZ2VvbV9wb2x5Z29uKGNvbG9yID0gImdyYXk5MCIsIHNpemUgPSAwLjEpICsKICBjb29yZF9tYXAocHJvamVjdGlvbiA9ICJhbGJlcnMiLCBsYXQwID0gMzksIGxhdDEgPSA0NSkgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIndoaXRlIiwgaGlnaCA9ICIjQ0I0NTRBIikgKwogIGxhYnModGl0bGUgPSAiVHJ1bXAgdm90ZSIsIGZpbGwgPSAiUGVyY2VudCIpIApgYGAKCkNIQVBURVIgRUlHSFQKbG9va2luZyBhdCBvcHBvcnR1bml0aWVzIHRvIHR3ZWFrIG9yIGN1c3RvbWl6ZSB0aGluZ3MuIEl04oCZcyBvbmx5IHdoZW4gd2UgaGF2ZSBzb21lIHNwZWNpZmljIHBsb3QgaW4gbWluZCB0aGF0IHRoZSBxdWVzdGlvbiBvZiBwb2xpc2hpbmcgdGhlIHJlc3VsdHMgY29tZXMgdXAuCmBgYHtyfQpwODEgPC0gZ2dwbG90KGRhdGEgPSBzdWJzZXQoYXNhc2VjLCBZZWFyID09IDIwMTQpLAogICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IE1lbWJlcnMsIHkgPSBSZXZlbnVlcywgbGFiZWwgPSBTbmFtZSkpCnA4MSArIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyhjb2xvciA9IEpvdXJuYWwpKSArCiAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIGNvbG9yID0gImdyYXk4MCIpICsKICAgICAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBzdWJzZXQoYXNhc2VjLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgWWVhciA9PSAyMDE0ICYgUmV2ZW51ZXMgPiAxMDAwMCksCiAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMykgKwogICAgICBsYWJzKHggPSAiTWVtYmVyc2hpcCIsIHkgPSAiUmV2ZW51ZXMiLCBjb2xvciA9ICJTZWN0aW9uIGhhcyBvd24gam91cm5hbCIsCiAgICAgICAgICAgdGl0bGUgPSAiQVNBIFNlY3Rpb25zIiwgc3VidGl0bGUgPSAiMjAxNCBjYWxlbmRhciB5ZWFyIiwgY2FwdGlvbiA9ICJTb3VyY2U6IEFTQSBhbm51YWwgcmVwb3J0IikgKwogICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpkb2xsYXIpICsKICAgICAgdGhlbWVfZGFyaygpICsKICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCiAgCmBgYApUaGVtZXMgY2FuIGJlIHR1cm5lZCBvbiBvciBvZmYgdXNpbmcgdGhlIHRoZW1lX3NldCgpIGZ1bmN0aW9uLgoKSW50ZXJuYWxseSwgdGhlbWUgZnVuY3Rpb25zIGFyZSBhIHNldCBvZiBkZXRhaWxlZCBpbnN0cnVjdGlvbnMgdG8gdHVybiBvbiwgdHVybiBvZmYsIG9yIG1vZGlmeSBhIGxhcmdlIG51bWJlciBvZiBncmFwaGljYWwgZWxlbWVudHMgb24gdGhlIHBsb3QuIE9uY2Ugc2V0LCBhIHRoZW1lIGFwcGxpZXMgdG8gYWxsIHN1YnNlcXVlbnQgcGxvdHMgYW5kIGl0IHJlbWFpbnMgYWN0aXZlIHVudGlsIGl0IGlzIHJlcGxhY2VkIGJ5IGEgZGlmZmVyZW50IHRoZW1lLiAKYGBge3J9CiMgRGVtb2NyYXQgQmx1ZSBhbmQgUmVwdWJsaWNhbiBSZWQKcGFydHlfY29sb3JzIDwtIGMoIiMyRTc0QzAiLCAiI0NCNDU0QSIpCgpwMCA8LSBnZ3Bsb3QoZGF0YSA9IHN1YnNldChjb3VudHlfZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZmxpcHBlZCA9PSAiTm8iKSwKICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IHBvcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGJsYWNrLzEwMCkpCgpwMSA8LSBwMCArIGdlb21fcG9pbnQoYWxwaGEgPSAwLjE1LCBjb2xvciA9ICJncmF5NTAiKSArCiAgICBzY2FsZV94X2xvZzEwKGxhYmVscz1zY2FsZXM6OmNvbW1hKSAKCnAxCgpwMiA8LSBwMSArIGdlb21fcG9pbnQoZGF0YSA9IHN1YnNldChjb3VudHlfZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmxpcHBlZCA9PSAiWWVzIiksCiAgICAgICAgICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBwb3AsIHkgPSBibGFjay8xMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gcGFydHl3aW5uZXIxNikpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYXJ0eV9jb2xvcnMpCgpwMgoKcDMgPC0gcDIgKyBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6cGVyY2VudCkgKwogICAgbGFicyhjb2xvciA9ICJDb3VudHkgZmxpcHBlZCB0byAuLi4gIiwKICAgICAgICAgeCA9ICJDb3VudHkgUG9wdWxhdGlvbiAobG9nIHNjYWxlKSIsCiAgICAgICAgIHkgPSAiUGVyY2VudCBCbGFjayBQb3B1bGF0aW9uIiwKICAgICAgICAgdGl0bGUgPSAiRmxpcHBlZCBjb3VudGllcywgMjAxNiIsCiAgICAgICAgIGNhcHRpb24gPSAiQ291bnRpZXMgaW4gZ3JheSBkaWQgbm90IGZsaXAuIikKCnAzCgpwNCA8LSBwMyArIGdlb21fdGV4dF9yZXBlbChkYXRhID0gc3Vic2V0KGNvdW50eV9kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZsaXBwZWQgPT0gIlllcyIgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJsYWNrICA+IDI1KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gcG9wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBibGFjay8xMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBzdGF0ZSksIHNpemUgPSAyKQoKcDQgKyB0aGVtZV9taW5pbWFsKCkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiKQpgYGAKCmBgYHtyfQp0aGVtZV9zZXQodGhlbWVfYncoKSkKcDQgKyB0aGVtZShsZWdlbmQucG9zaXRpb249InRvcCIpCgp0aGVtZV9zZXQodGhlbWVfZGFyaygpKQpwNCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0idG9wIikKYGBgCgpgYGB7cn0KbGlicmFyeShnZ3RoZW1lcykKCnRoZW1lX3NldCh0aGVtZV9lY29ub21pc3QoKSkKcDQgKyB0aGVtZShsZWdlbmQucG9zaXRpb249InRvcCIpCgp0aGVtZV9zZXQodGhlbWVfd3NqKCkpCgpwNCArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjYpKSwKICAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjM1KSksCiAgICAgICAgICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC4zNSkpLAogICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKVGhlIHRoZW1lKCkgZnVuY3Rpb24gYWxsb3dzIHlvdSB0byBleGVydCB2ZXJ5IGZpbmUtZ3JhaW5lZCBjb250cm9sIG92ZXIgdGhlIGFwcGVhcmFuY2Ugb2YgYWxsIGtpbmRzIG9mIHRleHQgYW5kIGdyYXBoaWNhbCBlbGVtZW50cyBpbiBhIHBsb3QuCmBgYHtyLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD0xMn0KeXJzIDwtIGMoc2VxKDE5NzIsMTk4OCw0KSwxOTkzLCBzZXEoMTk5NiwyMDE2LDQpKQptZWFuX2FnZSA8LSBnc3NfbG9uICU+JQogIGZpbHRlcihhZ2UgJW5pbiUgTkEgJiYgeWVhciAlaW4lIHlycykgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgc3VtbWFyaXplKHhiYXIgPSByb3VuZChtZWFuKGFnZSwgbmEucm0gPSBUUlVFKSwwKSkKbWVhbl9hZ2UkeSA8LSAwLjMKCnlyX2xhYnMgPC0gZGF0YS5mcmFtZSh4ID0gODUsIHkgPSAwLjgsIHllYXIgPSB5cnMpCgpwODE2IDwtIGdncGxvdChkYXRhID0gc3Vic2V0KGdzc19sb24sIHllYXIgJWluJSB5cnMpLAogICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBhZ2UpKQpwODE2YSA8LSBwODE2ICsgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiZ3JheTIwIiwgY29sb3IgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuOSwgbWFwcGluZyA9IGFlcyh5ID0gLi5zY2FsZWQuLikpICsKICBnZW9tX3ZsaW5lKGRhdGEgPSBzdWJzZXQobWVhbl9hZ2UsIHllYXIgJWluJSB5cnMpLAogICAgICAgICAgICAgYWVzKHhpbnRlcmNlcHQgPSB4YmFyKSwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMC41KSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBzdWJzZXQobWVhbl9hZ2UsIHllYXIgJWluJSB5cnMpLAogICAgICAgICAgICBhZXMoeCA9IHhiYXIsIHkgPSB5LCBsYWJlbCA9IHhiYXIpLCBudWRnZV94ID0gNy41LAogICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAzLjUsIGhqdXN0ID0gMSkgKwogIGdlb21fdGV4dChkYXRhID0gc3Vic2V0KHlyX2xhYnMsIHllYXIgJWluJSB5cnMpLAogICAgICAgICAgICBhZXMoeCA9IHgsIHkgPSB5LCBsYWJlbCA9IHllYXIpKSArCiAgZmFjZXRfZ3JpZCh5ZWFyIH4gLiwgc3dpdGNoID0gInkiKQoKcDgxNmEKCnA4MTZhICsgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMCkgKyAjLCBwYW5lbF9zcGFjaW5nID0gdW5pdCgwLjEsICJsaW5lcyIpKSMsIHN0cmlwX3RleHRfc2l6ZSA9IDMyKSMsIHBsb3RfdGl0bGVfc2l6ZSA9IDEwKSMsIHN0cmlwX3RleHRfc2l6ZSA9IDMyKQoKICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwKICAgICAgICAgIGF4aXMudGV4dC54PSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgc3RyaXAudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpICsKICAgIGxhYnMoeCA9ICJBZ2UiLAogICAgICAgICB5ID0gTlVMTCwKICAgICAgICAgdGl0bGUgPSAiQWdlIERpc3RyaWJ1dGlvbiBvZlxuR1NTIFJlc3BvbmRlbnRzIikKYGBgCmBgYHtyfQpwODIyIDwtIGdncGxvdChkYXRhID0geWFob28sIG1hcHBpbmcgPSBhZXMoeCA9IEVtcGxveWVlcywgeSA9IFJldmVudWUpKQpwODIyICsgZ2VvbV9wYXRoKGNvbG9yID0gImdyYXk0MCIsIHNpemUgPSAyLCBhbHBoYSA9IDAuMykgKwogIGdlb21fdGV4dChhZXMoY29sb3IgPSBNYXllciwgbGFiZWwgPSBZZWFyKSwKICAgICAgICAgICAgc2l6ZSA9IDQsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICBsYWJzKGNvbG9yID0gIk1heWVyIGlzIENFTyIsCiAgICAgICB4ID0gImVtcGxveWVlcyIsIHkgPSAicmV2ZW51ZSAoTWxuKSIsCiAgICAgICB0aXRsZSA9ICJZYWhvb3MiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6ZG9sbGFyKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6Y29tbWEpCmBgYAoK