Overview
“Faceting” is ggplot
’s ability to produce many different plots with a single command. I’m going to illustrate it here by producing multiple scatter plots, but you can facet any kind of plot.
There are four types of faceting:
You want to create many plots {x,y}
one for each value of a categorical variable z
. You do this by adding a facet_wrap(~z)
layer to the ggplot()
.
You want to create many plots {x,y}
one for each combination of values of the categorical variables {a,b}
. You do this by adding a facet_grid(a ~ b)
layer to the ggplot()
.
You want to create many plots {x1, y}
, {x2, y}
, …, {xn, y}
of many different variables against a single given variable. This is done in two steps.
- First, you gather the
{x1, x2, ..., xn}
variables in a long format dataframe, using gather(key = "variable", value = "val", -c(y,z))
.
- Second, you create the plots with regular faceting, either with
facet_wrap(~variable)
or with facet_grid(variable ~ z)
(where z
is some additional categorical variable). The ggplot
aesthetic wil now be aes(x = val, y = y)
.
You want to create plots of many different pairs of variables {x1, y1}
, {x2, y2}
, …, {xn, yn}
. This is also done in two steps.
- First you gather all the
x
s and y
s into a long format dataframe, but this time along two dimensions. This is done with cdata::rowrecs_to_blocks(controlTable)
, where controlTable
is a dataframe identifying all the pairs of variables and the label you want to use for each pair.
- You then create the plots with regular
facet_wrap(~id)
or facet_grid(id ~ z)
, where id
is the label you created for each pair.
To illustrate these methods, let us use the Quality of Government Institute basic cross-section database. Here are all the variables in this dataset:
library(tidyverse)
# import data
qog <- rio::import("http://www.qogdata.pol.gu.se/data/qog_bas_cs_jan19.dta")
# build summary of the dataset to check the available variables
qog_ <- data.frame(
Variable = names(qog),
Description = Hmisc::label(qog),
t(pastecs::stat.desc(qog))
) %>%
select(Variable, Description,
Obs. = nbr.val, Missing = nbr.na,
min, max, median, mean, std.dev)
qog_ %>% DT::datatable(rownames = FALSE) %>%
DT::formatRound(columns = 3:9, digits = 2)
Basic faceting
Here’s the plot of the relationship between incomes and life expectancy (each point is a different country):
# set the theme for all the plots
theme_set(ggthemes::theme_few(base_size = 14))
qog %>%
ggplot() +
aes(x = wdi_gdpcapcon2010,
y = wdi_lifexpf,
label = cname) +
geom_point() +
scale_x_log10(label = scales::comma) +
labs(x = "GDP per capita [2010 US dollars]",
y = "Life expectancy at birth,\nboth sexes [years]",
caption = "Source of data: World Bank")

Life expectancy is influenced by a variety of other factors apart from income. For instance, we can separate the data based on whether the country has a tropical climate:
qog %>%
ggplot() +
aes(x = wdi_gdpcapcon2010,
y = wdi_lifexpf,
label = cname,
color = nunn_tropical) +
geom_point() +
scale_x_log10(label = scales::comma) +
scale_color_viridis_c() +
labs(x = "GDP per capita [2010 US dollars]",
y = "Life expectancy at birth,\nboth sexes [years]",
color = "% Tropical\nclimate",
caption = "Source of data: World Bank, Nunn")

Facetting based on one variable
But instead of using colors, we could use faceting:
qog %>%
mutate(tropical = ifelse(nunn_tropical < mean(nunn_tropical, na.rm = TRUE),
"Temperate", "Tropical")) %>%
drop_na(tropical) %>%
ggplot() +
aes(x = wdi_gdpcapcon2010,
y = wdi_lifexpf,
label = cname) +
geom_point() +
scale_x_log10(label = scales::comma) +
labs(x = "GDP per capita [2010 US dollars]",
y = "Life expectancy at birth,\nboth sexes [years]",
caption = "Source of data: World Bank, Nunn") +
facet_wrap(~tropical)

We see that the positive relation between income and life expectancy occurs in both categories. In other words, it is not an artifact of the fact that tropical countries happen to be poorer on average.
I’ve done two things in the code above:
I created a binary variable called tropical
out of the continuous variable nunn_tropical
, using mutate()
and the function ifelse()
. Notice that I haven’t stored this variable in the qog
dataset, but I’ve only created it temporarily for the purpose of this plot. Also notice the use of drop_na(tropical)
. This is so we don’t get an extra sub-plot for the cases with missing data. (You can acheive a similar result adding the drop = TRUE
option inside the facet function.)
I used facet_wrap(~tropical)
to tell ggplot
to create several plots, one for each value of variable tropical
(in this case, there are only 2). The instructions about what to plot (in this case, the scatter plot between income and life expectancy) are applied within each of these sub-plots. Each sub-plot contains only a sub-set of the data, conditional on the different values of variable tropical
. Notice the syntax, in particular the use of the tilde (~
).
Facetting based on two variables
Suppose that I want to create a facet based on both how tropical the country is and based on some other variable, e.g. the level of democracy. The healthcare system is influenced to some extent by the government policies, and maybe a more responsive democratic government will provide better healthcare, which, in turn, might have an impact on life expectancy. In many countries, low life expectancy is due to child mortality and contagious diseases – which are affected by government actions. I’m using here Freedom House’s index to assess the level of democracy.
qog %>%
mutate(tropical = ifelse(nunn_tropical < mean(nunn_tropical, na.rm = TRUE),
"Temperate", "Tropical")) %>%
mutate(democracy = ifelse(fh_ipolity2 < mean(fh_ipolity2, na.rm = TRUE),
"Authoritarian", "Democratic")) %>%
drop_na(tropical, democracy) %>%
group_by(tropical, democracy) %>%
mutate(mean_lifexp = mean(wdi_lifexp, na.rm = TRUE)) %>%
ggplot() +
aes(x = wdi_gdpcapcon2010,
y = wdi_lifexpf,
label = cname) +
geom_hline(aes(yintercept = mean_lifexp), color = "gray") +
geom_point() +
scale_x_log10(label = scales::comma) +
labs(x = "GDP per capita [2010 US dollars]",
y = "Life expectancy at birth,\nboth sexes [years]",
caption = "Source of data: World Bank, Nunn, Freedom House") +
facet_grid(democracy ~ tropical)

I’ve done several things here:
Defined another binary variable democracy
in the same way as I’ve created the tropical
variable.
Used facet_grid(democracy ~ tropical)
instead of the previous facet_wrap(~tropical)
. This is what created the grid based on two conditions. If I had used facet_grid(tropical ~ democracy)
the grid would’ve been arranged with Democracy/Autocracy as columns and Tropical/Temperate as rows.
Before calling ggplot
, I’ve calculated the mean life expectancy for each category using group_by(tropical, democracy)
and mutate(mean_lifexp = ...)
. These means are then drawn with the geom_hline()
. Notice that I put the geom_hline()
before the geom_point()
, such that the points are on top.
You can observe that, indeed, the mean life expectancy is lower for both tropical climates and authoritarian regimes. Moreover, the relationship between income and life expectancy persists in all categories, although it is weakest in the authoritarian tropical countries.
Ploting multiple variables
The sub-plots using the basic faceting option described above use the same variables. What if instead of plotting x
vs y
for different categories defined by z
, you want to plot y
vs x1
, y
vs x2
, …, y
vs xn
(or the other way around, x
vs y1
, y2
, …, yn
)?
We can do this very easily by first reshaping the dataset from its wide format to a long format, and then using regular faceting on this long format dataset.
The wide format has each variable on its own column, and each row is an observation. Something like this:
y1 |
a1 |
b1 |
c1 |
y2 |
a1 |
b1 |
c1 |
… |
… |
… |
… |
ym |
am |
bm |
cm |
We can use the function gather(key = "variable", value = "val", -y)
to transform it into a long format, which looks like the table below, while keeping the variable y
out of it (if you want keep out of the gathering process more than one variable use -c(x, y, ...)
):
y1 |
A |
a1 |
y2 |
A |
a2 |
… |
… |
… |
ym |
A |
am |
y1 |
B |
b1 |
y2 |
B |
b2 |
… |
… |
… |
ym |
B |
bm |
y1 |
C |
c1 |
y2 |
C |
c2 |
… |
.. |
… |
ym |
C |
cm |
Here’s an example:
[A somewhat more intuitive and feature-rich version of gather
will soon be available: pivot_longer(names_to = "variable", values_to = "val")
. See this link. But the gather
function will continue to work indefinitely.]
Once we’ve gathered the data into the long format, we can use val
as the variable that changes from one sub-plot to another, and facet on variable
. Here’s an example:
qog %>%
mutate(log_income = log(wdi_gdpcapcon2010)) %>%
select(wdi_lifexp,
log_income,
`tropical percent` = nunn_tropical,
environment = epi_eh) %>%
gather(key = "variable", value = "val", -wdi_lifexp) %>%
ggplot() +
aes(x = val, y = wdi_lifexp) +
geom_smooth(method = "lm") +
geom_point() +
facet_wrap(~variable, scales = "free_x") +
labs(x = "",
y = "Life expectancy at birth,\nboth sexes [years]",
caption = "Source of data: World Bank, Nunn, Environmental Performance Index")

I only selected three variables to plot here, but you could easily select many more. The only difference would be that the select
condition coming before gather
would contain a lot more variables. The ggplot
code itself will remain unchanged no matter how many variables you add. Several things to note here:
If you want to do some change to the variable, like I changed the income to log_income, you need to do it with a mutate
function before the select. You cannot write something like select(log_income = log(wdi_gdpcapcon2010))
.
You can use the select
function to also change the names of the variables. These names will appear as labels above the facets, so it’s a good idea to change them to something easy to understand. If you want to include spaces in the variable name you can, by enclosing the variable name between back ticks `variable name with spaces in it`. Considering that gather
will pick up all those variable names for you, this is the one place where it doesn’t matter if you have names with spaces in them (generally speaking, it is frowned upon to have such variable names).
Here’s another example, but using facet_grid()
:
qog %>%
mutate(log_income = log(wdi_gdpcapcon2010)) %>%
mutate(democracy = ifelse(fh_ipolity2 < mean(fh_ipolity2, na.rm = TRUE),
"Autocracy", "Democracy")) %>%
select(wdi_lifexp, log_income, democracy,
environment = epi_eh,
`tropical percent` = nunn_tropical) %>%
gather(key = "variable", value = "val", -c(wdi_lifexp, democracy)) %>%
ggplot() +
aes(x = val, y = wdi_lifexp) +
geom_smooth(method = "lm") +
geom_point() +
facet_grid(democracy ~ variable, scales = "free") +
labs(x = "",
y = "Life expectancy at birth,\nboth sexes [years]",
caption = "Source of data: World Bank, Nunn, Environmental Performance Index, Freedom House")

Notice that I had to keep out of the gathering process both the y
variable (life expectancy) and the additional faceting variable (democracy
).
Ploting multiple pairs of variables
The above trick is extremely useful, and you rarely need anything beyond it – as you usually want to see how a given variable is related to many others, rather than see the relationships between many different random pairs of variables.
Nonetheless, sometimes you might need to plot numerous pairs of different variables. Instead of pairs like {x1, y}
, {x2, y}
, …, {xn, y}
, like above, you might need to plot pairs like {x1, y1}
, {x2, y2}
, …, {xn, yn}
. The gather
trick doesn’t help you here. In principle you would need a nested gather.
Setting up the control table manually
Fortunately, there’s an easier way using the function df %>% cdata::rowrecs_to_blocks(vars)
. Pretty much the most complicated thing about using this is remembering its name! The dataframe df
is your wide format dataset. In our example, it’s the qog
dataset. The vars
dataset specifies all the pairs that you want. It has three columns and looks something like this:
“pair 1: A vs B” |
“A” |
“B” |
“pair 2: B vs C” |
“B” |
“C” |
“pair 3: D vs E” |
“D” |
“E” |
“pair 4: F vs B” |
“F” |
“B” |
“pair 5: B vs A” |
“B” |
“A” |
The column names (id
, x
, y
) could be anything you want, but note that all values in this dataframe need to be strings. The values of the id
can also be anything you want, as long as they are unique. These labels under id
will appear as the labels of each sub-plot (we’re going to facet by id
), so you want to make them descriptive. The columns x
and y
contain the names of the variables from the df
dataframe that are going to be paired together in each sub-plot. This vars
dataframe is thus a quick way of setting up the list of all the plots you want to make and the labels for each plot. In the terminology of the cdata
package, the vars
dataframe is called the “control table”.
The function cdata::rowrecs_to_blocks(df, vars)
will generate the long format dataframe of the pairs. This is just like gather
but with two variables instead of just one. Unlike gather
, you don’t need to bother with the select
before calling it, because it automatically transforms into long format only the variables specified in the control table.
If you want to bring in some additional variables from df
, without getting them mixed up in this transformation to the long format, you can add the option columnsToCopy = c("P", "Q", "R", ...)
and variables “P”, “Q”, “R”, etc. will be copied separately as they are. This is similar to using -c(P, Q, R, ...)
in gather()
.
Here’s an example:
vars <- tribble(
~id, ~x, ~y,
"Fig. 1: \n x=Income \n y=Life expectancy", "log_income", "wdi_lifexp",
"Fig. 2: \n x=Economic freedom \n y=Income", "fi_index", "log_income",
"Fig. 3: \n x=Corruption \n y=Income", "bci_bci", "log_income",
"Fig. 4: \n x=Environment \n y=Life expectancy", "epi_eh", "wdi_lifexp",
"Fig. 5: \n x=Corruption \n y=Environment", "bci_bci", "epi_eh",
"Fig. 6: \n x=Education \n y=Income", "sgi_soed", "log_income",
"Fig. 7: \n x=Economic freeedom \n y=Inequality", "fi_index", "wdi_gini",
"Fig. 8: \n x=Corruption \n y=Inequality", "bci_bci", "wdi_gini",
"Fig. 9: \n x=Democracy \n y=Inequality", "fh_ipolity2", "wdi_gini"
)
qog %>%
mutate(log_income = log(wdi_gdpcapcon2010)) %>%
cdata::rowrecs_to_blocks(vars) %>%
ggplot() +
aes(x = x, y = y) +
geom_smooth(method = "lm") +
geom_point() +
facet_wrap(~id, scales = "free") +
labs(caption = "Data sources: World Bank, Fraser Institute, Bayesian Corruption Index,\n Environmental Performance Index, Sustainable Governance Indicators, Freedom House")

One thing to notice here is that you can make a label span over several rows by using the \n
code. I’ve used this several times above.
When you do this kind of thing in a rmarkdown document, you will need to specify the chunk options, fig.height
and fig.width
. For instance, the chuck above has the options: {r, fig.height=10, fig.width=10}
. Obviously, the more such plots you are generating, the greater the height of the produced figure should be. Similarly, if you’re using ggsave()
to save the plot in a file, make sure the size of the image is large enough (using the height
and width
options of ggsave
).
How do you create the control table manually?
You can write it down inside a tribble()
function (as I’ve done above). This allows you to write it row-by-row, rather than column-by-column (as in a data.frame()
). Notice that the variable names in the first row are preceded by a tilde.
Even better, you can create the table in Excel or Google Sheets and than just copy-paste it into your R code with the datapasta
add-on.
Or you can just have the control table stored in a file, and import it with rio::import()
.
Setting up the control table programatically
You can use this trick for generating a large number of plots with a single ggplot()
call even when the set of pairs of variables is generated by some automated process – rather than defined manually like above.
For example, suppose that I want to find the most highly correlated variables in the qog
dataset, and plot their scatter plots.
First, build the correlation matrix, and flatten it into a long-format table. We’re going to create the control table by simply taking a subset of this correlations dataframe.
qog_cor <- qog %>%
select_if(is.numeric) %>%
# creates matrix of all corrlations
cor(use="pairwise.complete.obs") %>%
as.data.frame() %>%
rownames_to_column("v1") %>%
# turn it into long format
gather(key = "v2", value = "correlation", -v1) %>%
# eliminate correlations to self
filter(correlation != 1)
Second, take a sample out of the set of highly correlated pairs, and plot them using the same procedure as before:
vars_highcor <- qog_cor %>%
# select highly correlated
filter(correlation > 0.8) %>%
# select a random sample of 9 pairs
sample_n(size = 9, replace = TRUE) %>%
# build the control table
rownames_to_column("id") %>%
select(id, v1, v2) %>%
mutate(id = paste("Figure", id, ": \n x=", v1, "\n y=", v2))
qog %>% cdata::rowrecs_to_blocks(vars_highcor) %>%
ggplot() +
aes(x = v1, y = v2) +
geom_smooth(method = "lm") +
geom_point() +
facet_wrap(~id, scales = "free") +
labs(x = "x", y = "y")

To see the meaning of these variables, you need to either check the codebook from the Quality of Government Institute, or search for the variables in the summary table at the beginning of this document and see their description. You can try to improve the plot by defining the id
variable using the variable labels instead of their names.
Here’s also a sample of the highly anti-correlated, and now using facet_grid()
:
vars_anticor <- qog_cor %>%
# select highly anti-correlated
filter(correlation < -0.8) %>%
# select a random sample of 9 pairs
sample_n(size = 9, replace = TRUE) %>%
# build the control table
rownames_to_column("id") %>%
select(id, v1, v2) %>%
mutate(id = paste("Figure", id, ": \n x=", v1, "\n y=", v2))
qog %>%
mutate(democracy = ifelse(fh_ipolity2 < mean(fh_ipolity2, na.rm = TRUE),
"Autocracy", "Democracy")) %>%
cdata::rowrecs_to_blocks(vars_anticor, columnsToCopy = c("democracy")) %>%
ggplot() +
aes(x = v1, y = v2) +
geom_smooth(method = "lm") +
geom_point() +
facet_grid(id ~ democracy, scales = "free", switch = "y") +
labs(x = "x", y = "y")

Combining plots in general
Apart from faceting, the most general, and least concise, way of combining many ggplots into one is by using the gridExtra::grid.arrange
or the patchwork
package. Under this approach you will create several different ggplot objects separately and then put them all together:
p1 <- df1 %>% ggplot() + ...
p2 <- df2 %>% ggplot() + ...
p3 <- df3 %>% ggplot() + ...
# using grid.arrange
gridExtra::grid.arrange(p1, p2, p3, ...)
# using patchwork
# (p1 and p2 are on first row, and p3 occupies the entire second row):
(p1 + p2) / p3
patchwork
is by far the easiest to use but it’s not yet on CRAN, so you’d have to install it from github.
LS0tDQp0aXRsZTogImdncGxvdCBmYWNldGluZyB0dXRvcmlhbCINCmF1dGhvcjogIlZsYWQgVGFya28iDQpvdXRwdXQ6IA0KICBodG1sX2RvY3VtZW50Og0KICAgIHRoZW1lOiB5ZXRpDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KCWVjaG8gPSBUUlVFLA0KCW1lc3NhZ2UgPSBGQUxTRSwNCgl3YXJuaW5nID0gRkFMU0UNCikNCm9wdGlvbnMoc2NpcGVuID0gOTk5KQ0KYGBgDQoNCiMgT3ZlcnZpZXcNCg0KIkZhY2V0aW5nIiBpcyBgZ2dwbG90YCdzIGFiaWxpdHkgdG8gcHJvZHVjZSBtYW55IGRpZmZlcmVudCBwbG90cyB3aXRoIGEgc2luZ2xlIGNvbW1hbmQuIEknbSBnb2luZyB0byBpbGx1c3RyYXRlIGl0IGhlcmUgYnkgcHJvZHVjaW5nIG11bHRpcGxlIHNjYXR0ZXIgcGxvdHMsIGJ1dCB5b3UgY2FuIGZhY2V0IGFueSBraW5kIG9mIHBsb3QuDQoNClRoZXJlIGFyZSBmb3VyIHR5cGVzIG9mIGZhY2V0aW5nOg0KDQoxLiBZb3Ugd2FudCB0byBjcmVhdGUgbWFueSBwbG90cyBge3gseX1gIG9uZSBmb3IgZWFjaCB2YWx1ZSBvZiBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlIGB6YC4gWW91IGRvIHRoaXMgYnkgYWRkaW5nIGEgYGZhY2V0X3dyYXAofnopYCBsYXllciB0byB0aGUgYGdncGxvdCgpYC4NCg0KMi4gWW91IHdhbnQgdG8gY3JlYXRlIG1hbnkgcGxvdHMgYHt4LHl9YCBvbmUgZm9yIGVhY2ggY29tYmluYXRpb24gb2YgdmFsdWVzIG9mIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgYHthLGJ9YC4gWW91IGRvIHRoaXMgYnkgYWRkaW5nIGEgYGZhY2V0X2dyaWQoYSB+IGIpYCBsYXllciB0byB0aGUgYGdncGxvdCgpYC4NCg0KMy4gWW91IHdhbnQgdG8gY3JlYXRlIG1hbnkgcGxvdHMgYHt4MSwgeX1gLCBge3gyLCB5fWAsIC4uLiwgYHt4biwgeX1gIG9mIG1hbnkgZGlmZmVyZW50IHZhcmlhYmxlcyBhZ2FpbnN0IGEgc2luZ2xlIGdpdmVuIHZhcmlhYmxlLiBUaGlzIGlzIGRvbmUgaW4gdHdvIHN0ZXBzLiANCg0KICAgIDEuIEZpcnN0LCB5b3UgZ2F0aGVyIHRoZSBge3gxLCB4MiwgLi4uLCB4bn1gIHZhcmlhYmxlcyBpbiBhIGxvbmcgZm9ybWF0IGRhdGFmcmFtZSwgdXNpbmcgYGdhdGhlcihrZXkgPSAidmFyaWFibGUiLCB2YWx1ZSA9ICJ2YWwiLCAtYyh5LHopKWAuIA0KICAgIDIuIFNlY29uZCwgeW91IGNyZWF0ZSB0aGUgcGxvdHMgd2l0aCByZWd1bGFyIGZhY2V0aW5nLCBlaXRoZXIgd2l0aCBgZmFjZXRfd3JhcCh+dmFyaWFibGUpYCBvciB3aXRoIGBmYWNldF9ncmlkKHZhcmlhYmxlIH4geilgICh3aGVyZSBgemAgaXMgc29tZSBhZGRpdGlvbmFsIGNhdGVnb3JpY2FsIHZhcmlhYmxlKS4gVGhlIGBnZ3Bsb3RgIGFlc3RoZXRpYyB3aWwgbm93IGJlIGBhZXMoeCA9IHZhbCwgeSA9IHkpYC4NCg0KNC4gWW91IHdhbnQgdG8gY3JlYXRlIHBsb3RzIG9mIG1hbnkgZGlmZmVyZW50IHBhaXJzIG9mIHZhcmlhYmxlcyBge3gxLCB5MX1gLCBge3gyLCB5Mn1gLCAuLi4sIGB7eG4sIHlufWAuIFRoaXMgaXMgYWxzbyBkb25lIGluIHR3byBzdGVwcy4gDQoNCiAgICAxLiBGaXJzdCB5b3UgZ2F0aGVyIGFsbCB0aGUgYHhgcyBhbmQgYHlgcyBpbnRvIGEgbG9uZyBmb3JtYXQgZGF0YWZyYW1lLCBidXQgdGhpcyB0aW1lIGFsb25nIHR3byBkaW1lbnNpb25zLiBUaGlzIGlzIGRvbmUgd2l0aCBgY2RhdGE6OnJvd3JlY3NfdG9fYmxvY2tzKGNvbnRyb2xUYWJsZSlgLCB3aGVyZSBgY29udHJvbFRhYmxlYCBpcyBhIGRhdGFmcmFtZSBpZGVudGlmeWluZyBhbGwgdGhlIHBhaXJzIG9mIHZhcmlhYmxlcyBhbmQgdGhlIGxhYmVsIHlvdSB3YW50IHRvIHVzZSBmb3IgZWFjaCBwYWlyLiANCiAgICAyLiBZb3UgdGhlbiBjcmVhdGUgdGhlIHBsb3RzIHdpdGggcmVndWxhciBgZmFjZXRfd3JhcCh+aWQpYCBvciBgZmFjZXRfZ3JpZChpZCB+IHopYCwgd2hlcmUgYGlkYCBpcyB0aGUgbGFiZWwgeW91IGNyZWF0ZWQgZm9yIGVhY2ggcGFpci4NCg0KVG8gaWxsdXN0cmF0ZSB0aGVzZSBtZXRob2RzLCBsZXQgdXMgdXNlIHRoZSBbX1F1YWxpdHkgb2YgR292ZXJubWVudCBJbnN0aXR1dGVfIGJhc2ljIGNyb3NzLXNlY3Rpb24gZGF0YWJhc2VdKGh0dHBzOi8vcW9nLnBvbC5ndS5zZS9kYXRhL2RhdGFkb3dubG9hZHMvcW9nYmFzaWNkYXRhKS4gSGVyZSBhcmUgYWxsIHRoZSB2YXJpYWJsZXMgaW4gdGhpcyBkYXRhc2V0Og0KDQo8YSBuYW1lPSJzdW10YWJsZSI+PC9hPg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQojIGltcG9ydCBkYXRhDQpxb2cgPC0gcmlvOjppbXBvcnQoImh0dHA6Ly93d3cucW9nZGF0YS5wb2wuZ3Uuc2UvZGF0YS9xb2dfYmFzX2NzX2phbjE5LmR0YSIpDQoNCiMgYnVpbGQgc3VtbWFyeSBvZiB0aGUgZGF0YXNldCB0byBjaGVjayB0aGUgYXZhaWxhYmxlIHZhcmlhYmxlcw0KcW9nXyA8LSBkYXRhLmZyYW1lKA0KICAgIFZhcmlhYmxlICAgID0gbmFtZXMocW9nKSwgDQogICAgRGVzY3JpcHRpb24gPSBIbWlzYzo6bGFiZWwocW9nKSwgDQogICAgdChwYXN0ZWNzOjpzdGF0LmRlc2MocW9nKSkgDQogICkgJT4lIA0KICBzZWxlY3QoVmFyaWFibGUsIERlc2NyaXB0aW9uLCANCiAgICAgICAgIE9icy4gPSBuYnIudmFsLCBNaXNzaW5nID0gbmJyLm5hLCANCiAgICAgICAgIG1pbiwgbWF4LCBtZWRpYW4sIG1lYW4sIHN0ZC5kZXYpDQoNCnFvZ18gJT4lIERUOjpkYXRhdGFibGUocm93bmFtZXMgPSBGQUxTRSkgJT4lIA0KICBEVDo6Zm9ybWF0Um91bmQoY29sdW1ucyA9IDM6OSwgZGlnaXRzID0gMikNCmBgYA0KDQojIEJhc2ljIGZhY2V0aW5nDQoNCkhlcmUncyB0aGUgcGxvdCBvZiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gaW5jb21lcyBhbmQgbGlmZSBleHBlY3RhbmN5IChlYWNoIHBvaW50IGlzIGEgZGlmZmVyZW50IGNvdW50cnkpOg0KDQpgYGB7cn0NCiMgc2V0IHRoZSB0aGVtZSBmb3IgYWxsIHRoZSBwbG90cw0KdGhlbWVfc2V0KGdndGhlbWVzOjp0aGVtZV9mZXcoYmFzZV9zaXplID0gMTQpKQ0KDQpxb2cgJT4lIA0KICBnZ3Bsb3QoKSArIA0KICAgIGFlcyh4ID0gd2RpX2dkcGNhcGNvbjIwMTAsIA0KICAgICAgICB5ID0gd2RpX2xpZmV4cGYsIA0KICAgICAgICBsYWJlbCA9IGNuYW1lKSArDQogICAgZ2VvbV9wb2ludCgpICsNCiAgICBzY2FsZV94X2xvZzEwKGxhYmVsID0gc2NhbGVzOjpjb21tYSkgKw0KICAgIGxhYnMoeCA9ICJHRFAgcGVyIGNhcGl0YSBbMjAxMCBVUyBkb2xsYXJzXSIsDQogICAgICAgICB5ID0gIkxpZmUgZXhwZWN0YW5jeSBhdCBiaXJ0aCxcbmJvdGggc2V4ZXMgW3llYXJzXSIsDQogICAgICAgICBjYXB0aW9uID0gIlNvdXJjZSBvZiBkYXRhOiBXb3JsZCBCYW5rIikNCmBgYA0KDQpMaWZlIGV4cGVjdGFuY3kgaXMgaW5mbHVlbmNlZCBieSBhIHZhcmlldHkgb2Ygb3RoZXIgZmFjdG9ycyBhcGFydCBmcm9tIGluY29tZS4gRm9yIGluc3RhbmNlLCB3ZSBjYW4gc2VwYXJhdGUgdGhlIGRhdGEgYmFzZWQgb24gd2hldGhlciB0aGUgY291bnRyeSBoYXMgYSB0cm9waWNhbCBjbGltYXRlOg0KDQpgYGB7cn0NCnFvZyAlPiUgDQogIGdncGxvdCgpICsgDQogICAgYWVzKHggPSB3ZGlfZ2RwY2FwY29uMjAxMCwgDQogICAgICAgIHkgPSB3ZGlfbGlmZXhwZiwgDQogICAgICAgIGxhYmVsID0gY25hbWUsDQogICAgICAgIGNvbG9yID0gbnVubl90cm9waWNhbCkgKw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgc2NhbGVfeF9sb2cxMChsYWJlbCA9IHNjYWxlczo6Y29tbWEpICsNCiAgICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKSArDQogICAgbGFicyh4ID0gIkdEUCBwZXIgY2FwaXRhIFsyMDEwIFVTIGRvbGxhcnNdIiwNCiAgICAgICAgIHkgPSAiTGlmZSBleHBlY3RhbmN5IGF0IGJpcnRoLFxuYm90aCBzZXhlcyBbeWVhcnNdIiwNCiAgICAgICAgIGNvbG9yID0gIiUgVHJvcGljYWxcbmNsaW1hdGUiLA0KICAgICAgICAgY2FwdGlvbiA9ICJTb3VyY2Ugb2YgZGF0YTogV29ybGQgQmFuaywgTnVubiIpDQpgYGANCg0KIyMgRmFjZXR0aW5nIGJhc2VkIG9uIG9uZSB2YXJpYWJsZQ0KDQpCdXQgaW5zdGVhZCBvZiB1c2luZyBjb2xvcnMsIHdlIGNvdWxkIHVzZSBmYWNldGluZzoNCg0KYGBge3J9DQpxb2cgJT4lIA0KICBtdXRhdGUodHJvcGljYWwgPSBpZmVsc2UobnVubl90cm9waWNhbCA8IG1lYW4obnVubl90cm9waWNhbCwgbmEucm0gPSBUUlVFKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAiVGVtcGVyYXRlIiwgIlRyb3BpY2FsIikpICU+JSANCiAgZHJvcF9uYSh0cm9waWNhbCkgJT4lIA0KICANCiAgZ2dwbG90KCkgKyANCiAgICBhZXMoeCA9IHdkaV9nZHBjYXBjb24yMDEwLCANCiAgICAgICAgeSA9IHdkaV9saWZleHBmLCANCiAgICAgICAgbGFiZWwgPSBjbmFtZSkgKw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgc2NhbGVfeF9sb2cxMChsYWJlbCA9IHNjYWxlczo6Y29tbWEpICsNCiAgICBsYWJzKHggPSAiR0RQIHBlciBjYXBpdGEgWzIwMTAgVVMgZG9sbGFyc10iLA0KICAgICAgICAgeSA9ICJMaWZlIGV4cGVjdGFuY3kgYXQgYmlydGgsXG5ib3RoIHNleGVzIFt5ZWFyc10iLA0KICAgICAgICAgY2FwdGlvbiA9ICJTb3VyY2Ugb2YgZGF0YTogV29ybGQgQmFuaywgTnVubiIpICsNCiAgICBmYWNldF93cmFwKH50cm9waWNhbCkNCmBgYA0KDQpXZSBzZWUgdGhhdCB0aGUgcG9zaXRpdmUgcmVsYXRpb24gYmV0d2VlbiBpbmNvbWUgYW5kIGxpZmUgZXhwZWN0YW5jeSBvY2N1cnMgaW4gYm90aCBjYXRlZ29yaWVzLiBJbiBvdGhlciB3b3JkcywgaXQgaXMgbm90IGFuIGFydGlmYWN0IG9mIHRoZSBmYWN0IHRoYXQgdHJvcGljYWwgY291bnRyaWVzIGhhcHBlbiB0byBiZSBwb29yZXIgb24gYXZlcmFnZS4NCg0KSSd2ZSBkb25lIHR3byB0aGluZ3MgaW4gdGhlIGNvZGUgYWJvdmU6DQoNCjEuIEkgY3JlYXRlZCBhIGJpbmFyeSB2YXJpYWJsZSBjYWxsZWQgYHRyb3BpY2FsYCBvdXQgb2YgdGhlIGNvbnRpbnVvdXMgdmFyaWFibGUgYG51bm5fdHJvcGljYWxgLCB1c2luZyBgbXV0YXRlKClgIGFuZCB0aGUgZnVuY3Rpb24gYGlmZWxzZSgpYC4gTm90aWNlIHRoYXQgSSBoYXZlbid0IHN0b3JlZCB0aGlzIHZhcmlhYmxlIGluIHRoZSBgcW9nYCBkYXRhc2V0LCBidXQgSSd2ZSBvbmx5IGNyZWF0ZWQgaXQgdGVtcG9yYXJpbHkgZm9yIHRoZSBwdXJwb3NlIG9mIHRoaXMgcGxvdC4gQWxzbyBub3RpY2UgdGhlIHVzZSBvZiBgZHJvcF9uYSh0cm9waWNhbClgLiBUaGlzIGlzIHNvIHdlIGRvbid0IGdldCBhbiBleHRyYSBzdWItcGxvdCBmb3IgdGhlIGNhc2VzIHdpdGggbWlzc2luZyBkYXRhLiAoWW91IGNhbiBhY2hlaXZlIGEgc2ltaWxhciByZXN1bHQgYWRkaW5nIHRoZSBgZHJvcCA9IFRSVUVgIG9wdGlvbiBpbnNpZGUgdGhlIGZhY2V0IGZ1bmN0aW9uLikNCg0KMi4gSSB1c2VkIGBmYWNldF93cmFwKH50cm9waWNhbClgIHRvIHRlbGwgYGdncGxvdGAgdG8gY3JlYXRlIHNldmVyYWwgcGxvdHMsIG9uZSBmb3IgZWFjaCB2YWx1ZSBvZiB2YXJpYWJsZSBgdHJvcGljYWxgIChpbiB0aGlzIGNhc2UsIHRoZXJlIGFyZSBvbmx5IDIpLiBUaGUgaW5zdHJ1Y3Rpb25zIGFib3V0IHdoYXQgdG8gcGxvdCAoaW4gdGhpcyBjYXNlLCB0aGUgc2NhdHRlciBwbG90IGJldHdlZW4gaW5jb21lIGFuZCBsaWZlIGV4cGVjdGFuY3kpIGFyZSBhcHBsaWVkIHdpdGhpbiBlYWNoIG9mIHRoZXNlIHN1Yi1wbG90cy4gRWFjaCBzdWItcGxvdCBjb250YWlucyBvbmx5IGEgc3ViLXNldCBvZiB0aGUgZGF0YSwgY29uZGl0aW9uYWwgb24gdGhlIGRpZmZlcmVudCB2YWx1ZXMgb2YgdmFyaWFibGUgYHRyb3BpY2FsYC4gTm90aWNlIHRoZSBzeW50YXgsIGluIHBhcnRpY3VsYXIgdGhlIHVzZSBvZiB0aGUgdGlsZGUgKGB+YCkuDQoNCiMjIEZhY2V0dGluZyBiYXNlZCBvbiB0d28gdmFyaWFibGVzDQoNClN1cHBvc2UgdGhhdCBJIHdhbnQgdG8gY3JlYXRlIGEgZmFjZXQgYmFzZWQgb24gX2JvdGhfIGhvdyB0cm9waWNhbCB0aGUgY291bnRyeSBpcyBfYW5kXyBiYXNlZCBvbiBzb21lIG90aGVyIHZhcmlhYmxlLCBlLmcuIHRoZSBsZXZlbCBvZiBkZW1vY3JhY3kuIFRoZSBoZWFsdGhjYXJlIHN5c3RlbSBpcyBpbmZsdWVuY2VkIHRvIHNvbWUgZXh0ZW50IGJ5IHRoZSBnb3Zlcm5tZW50IHBvbGljaWVzLCBhbmQgbWF5YmUgYSBtb3JlIHJlc3BvbnNpdmUgZGVtb2NyYXRpYyBnb3Zlcm5tZW50IHdpbGwgcHJvdmlkZSBiZXR0ZXIgaGVhbHRoY2FyZSwgd2hpY2gsIGluIHR1cm4sIG1pZ2h0IGhhdmUgYW4gaW1wYWN0IG9uIGxpZmUgZXhwZWN0YW5jeS4gSW4gbWFueSBjb3VudHJpZXMsIGxvdyBsaWZlIGV4cGVjdGFuY3kgaXMgZHVlIHRvIGNoaWxkIG1vcnRhbGl0eSBhbmQgY29udGFnaW91cyBkaXNlYXNlcyAtLSB3aGljaCBhcmUgYWZmZWN0ZWQgYnkgZ292ZXJubWVudCBhY3Rpb25zLiBJJ20gdXNpbmcgaGVyZSBGcmVlZG9tIEhvdXNlJ3MgaW5kZXggdG8gYXNzZXNzIHRoZSBsZXZlbCBvZiBkZW1vY3JhY3kuDQoNCmBgYHtyfQ0KcW9nICU+JSANCiAgbXV0YXRlKHRyb3BpY2FsID0gaWZlbHNlKG51bm5fdHJvcGljYWwgPCBtZWFuKG51bm5fdHJvcGljYWwsIG5hLnJtID0gVFJVRSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRlbXBlcmF0ZSIsICJUcm9waWNhbCIpKSAlPiUgDQogIG11dGF0ZShkZW1vY3JhY3kgPSBpZmVsc2UoZmhfaXBvbGl0eTIgPCBtZWFuKGZoX2lwb2xpdHkyLCBuYS5ybSA9IFRSVUUpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJBdXRob3JpdGFyaWFuIiwgIkRlbW9jcmF0aWMiKSkgJT4lIA0KICBkcm9wX25hKHRyb3BpY2FsLCBkZW1vY3JhY3kpICU+JSANCiAgZ3JvdXBfYnkodHJvcGljYWwsIGRlbW9jcmFjeSkgJT4lIA0KICAgIG11dGF0ZShtZWFuX2xpZmV4cCA9IG1lYW4od2RpX2xpZmV4cCwgbmEucm0gPSBUUlVFKSkgJT4lIA0KICANCiAgZ2dwbG90KCkgKyANCiAgICBhZXMoeCA9IHdkaV9nZHBjYXBjb24yMDEwLCANCiAgICAgICAgeSA9IHdkaV9saWZleHBmLCANCiAgICAgICAgbGFiZWwgPSBjbmFtZSkgKw0KICAgIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtZWFuX2xpZmV4cCksIGNvbG9yID0gImdyYXkiKSArDQogICAgZ2VvbV9wb2ludCgpICsNCiAgICBzY2FsZV94X2xvZzEwKGxhYmVsID0gc2NhbGVzOjpjb21tYSkgKw0KICAgIGxhYnMoeCA9ICJHRFAgcGVyIGNhcGl0YSBbMjAxMCBVUyBkb2xsYXJzXSIsDQogICAgICAgICB5ID0gIkxpZmUgZXhwZWN0YW5jeSBhdCBiaXJ0aCxcbmJvdGggc2V4ZXMgW3llYXJzXSIsDQogICAgICAgICBjYXB0aW9uID0gIlNvdXJjZSBvZiBkYXRhOiBXb3JsZCBCYW5rLCBOdW5uLCBGcmVlZG9tIEhvdXNlIikgKw0KICAgIGZhY2V0X2dyaWQoZGVtb2NyYWN5IH4gdHJvcGljYWwpDQpgYGANCg0KSSd2ZSBkb25lIHNldmVyYWwgdGhpbmdzIGhlcmU6DQoNCjEuIERlZmluZWQgYW5vdGhlciBiaW5hcnkgdmFyaWFibGUgYGRlbW9jcmFjeWAgaW4gdGhlIHNhbWUgd2F5IGFzIEkndmUgY3JlYXRlZCB0aGUgYHRyb3BpY2FsYCB2YXJpYWJsZS4NCg0KMi4gVXNlZCBgZmFjZXRfZ3JpZChkZW1vY3JhY3kgfiB0cm9waWNhbClgIGluc3RlYWQgb2YgdGhlIHByZXZpb3VzIGBmYWNldF93cmFwKH50cm9waWNhbClgLiBUaGlzIGlzIHdoYXQgY3JlYXRlZCB0aGUgZ3JpZCBiYXNlZCBvbiB0d28gY29uZGl0aW9ucy4gSWYgSSBoYWQgdXNlZCAgYGZhY2V0X2dyaWQodHJvcGljYWwgfiBkZW1vY3JhY3kpYCB0aGUgZ3JpZCB3b3VsZCd2ZSBiZWVuIGFycmFuZ2VkIHdpdGggRGVtb2NyYWN5L0F1dG9jcmFjeSBhcyBjb2x1bW5zIGFuZCBUcm9waWNhbC9UZW1wZXJhdGUgYXMgcm93cy4NCg0KMy4gQmVmb3JlIGNhbGxpbmcgYGdncGxvdGAsIEkndmUgY2FsY3VsYXRlZCB0aGUgbWVhbiBsaWZlIGV4cGVjdGFuY3kgZm9yIGVhY2ggY2F0ZWdvcnkgdXNpbmcgYGdyb3VwX2J5KHRyb3BpY2FsLCBkZW1vY3JhY3kpYCBhbmQgYG11dGF0ZShtZWFuX2xpZmV4cCA9IC4uLilgLiBUaGVzZSBtZWFucyBhcmUgdGhlbiBkcmF3biB3aXRoIHRoZSBgZ2VvbV9obGluZSgpYC4gTm90aWNlIHRoYXQgSSBwdXQgdGhlIGBnZW9tX2hsaW5lKClgIGJlZm9yZSB0aGUgYGdlb21fcG9pbnQoKWAsIHN1Y2ggdGhhdCB0aGUgcG9pbnRzIGFyZSBvbiB0b3AuDQoNCllvdSBjYW4gb2JzZXJ2ZSB0aGF0LCBpbmRlZWQsIHRoZSBtZWFuIGxpZmUgZXhwZWN0YW5jeSBpcyBsb3dlciBmb3IgYm90aCB0cm9waWNhbCBjbGltYXRlcyBhbmQgYXV0aG9yaXRhcmlhbiByZWdpbWVzLiBNb3Jlb3ZlciwgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGluY29tZSBhbmQgbGlmZSBleHBlY3RhbmN5IHBlcnNpc3RzIGluIGFsbCBjYXRlZ29yaWVzLCBhbHRob3VnaCBpdCBpcyB3ZWFrZXN0IGluIHRoZSBhdXRob3JpdGFyaWFuIHRyb3BpY2FsIGNvdW50cmllcy4NCg0KIyBQbG90aW5nIG11bHRpcGxlIHZhcmlhYmxlcw0KDQpUaGUgc3ViLXBsb3RzIHVzaW5nIHRoZSBiYXNpYyBmYWNldGluZyBvcHRpb24gZGVzY3JpYmVkIGFib3ZlIHVzZSBfdGhlIHNhbWUgdmFyaWFibGVzXy4gV2hhdCBpZiBpbnN0ZWFkIG9mIHBsb3R0aW5nIGB4YCB2cyBgeWAgZm9yIGRpZmZlcmVudCBjYXRlZ29yaWVzIGRlZmluZWQgYnkgYHpgLCB5b3Ugd2FudCB0byBwbG90IGB5YCB2cyBgeDFgLCBgeWAgdnMgYHgyYCwgLi4uLCBgeWAgdnMgYHhuYCAob3IgdGhlIG90aGVyIHdheSBhcm91bmQsIGB4YCB2cyBgeTFgLCBgeTJgLCAuLi4sIGB5bmApPw0KDQpXZSBjYW4gZG8gdGhpcyB2ZXJ5IGVhc2lseSBieSBmaXJzdCByZXNoYXBpbmcgdGhlIGRhdGFzZXQgZnJvbSBpdHMgX3dpZGVfIGZvcm1hdCB0byBhIF9sb25nXyBmb3JtYXQsIGFuZCB0aGVuIHVzaW5nIHJlZ3VsYXIgZmFjZXRpbmcgb24gdGhpcyBsb25nIGZvcm1hdCBkYXRhc2V0Lg0KDQpUaGUgd2lkZSBmb3JtYXQgaGFzIGVhY2ggdmFyaWFibGUgb24gaXRzIG93biBjb2x1bW4sIGFuZCBlYWNoIHJvdyBpcyBhbiBvYnNlcnZhdGlvbi4gU29tZXRoaW5nIGxpa2UgdGhpczoNCg0KfCB5ICB8IEEgIHwgQiAgfCBDICB8DQp8LS0tLXwtLS0tfC0tLS18LS0tLXwNCnwgeTEgfCBhMSB8IGIxIHwgYzEgfA0KfCB5MiB8IGExIHwgYjEgfCBjMSB8DQp8Li4uIHwgLi4ufC4uLiB8IC4uLnwNCnwgeW0gfCBhbSB8IGJtIHwgY20gfA0KDQpXZSBjYW4gdXNlIHRoZSBmdW5jdGlvbiBgZ2F0aGVyKGtleSA9ICJ2YXJpYWJsZSIsIHZhbHVlID0gInZhbCIsIC15KWAgdG8gdHJhbnNmb3JtIGl0IGludG8gYSBsb25nIGZvcm1hdCwgd2hpY2ggbG9va3MgbGlrZSB0aGUgdGFibGUgYmVsb3csIHdoaWxlIGtlZXBpbmcgdGhlIHZhcmlhYmxlIGB5YCBvdXQgb2YgaXQgKGlmIHlvdSB3YW50IGtlZXAgb3V0IG9mIHRoZSBnYXRoZXJpbmcgcHJvY2VzcyBtb3JlIHRoYW4gb25lIHZhcmlhYmxlIHVzZSBgLWMoeCwgeSwgLi4uKWApOg0KDQp8IHkgIHwgdmFyaWFibGUgfCB2YWwgfA0KfC0tLS18LS0tLS0tLS0tfC0tLS0tfA0KfCB5MSB8IEEgICAgICAgfCBhMSAgfA0KfCB5MiB8IEEgICAgICAgfCBhMiAgfA0KfCAuLi58IC4uLiAgICAgfCAuLi4gfA0KfCB5bSB8IEEgICAgICAgfCBhbSAgfA0KfCB5MSB8IEIgICAgICAgfCBiMSAgfA0KfCB5MiB8IEIgICAgICAgfCBiMiAgfA0KfCAuLi58IC4uLiAgICAgfCAuLi4gfA0KfCB5bSB8IEIgICAgICAgfCBibSAgfA0KfCB5MSB8IEMgICAgICAgfCBjMSAgfA0KfCB5MiB8IEMgICAgICAgfCBjMiAgfA0KfCAuLi58IC4uICAgICAgfCAuLi4gfA0KfCB5bSB8IEMgICAgICAgfCBjbSAgfA0KDQpIZXJlJ3MgYW4gZXhhbXBsZToNCg0KIVtHaWYgYnkgQWxpc29uIFByZXNtYW5lcyBIaWxsXShodHRwczovL3VzZXItaW1hZ2VzLmdpdGh1YnVzZXJjb250ZW50LmNvbS8yMDk3MTQvNDgwMjY3MzgtZTlhMDZhODAtZTExNC0xMWU4LTlhMjQtZWNjOGIzN2I4YTUzLmdpZikNCg0KW0Egc29tZXdoYXQgbW9yZSBpbnR1aXRpdmUgYW5kIGZlYXR1cmUtcmljaCB2ZXJzaW9uIG9mIGBnYXRoZXJgIHdpbGwgc29vbiBiZSBhdmFpbGFibGU6IGBwaXZvdF9sb25nZXIobmFtZXNfdG8gPSAidmFyaWFibGUiLCB2YWx1ZXNfdG8gPSAidmFsIilgLiBTZWUgW3RoaXMgbGlua10oaHR0cHM6Ly90aWR5ci50aWR5dmVyc2Uub3JnL2Rldi9hcnRpY2xlcy9waXZvdC5odG1sKS4gQnV0IHRoZSBgZ2F0aGVyYCBmdW5jdGlvbiB3aWxsIGNvbnRpbnVlIHRvIHdvcmsgaW5kZWZpbml0ZWx5Ll0NCg0KT25jZSB3ZSd2ZSBnYXRoZXJlZCB0aGUgZGF0YSBpbnRvIHRoZSBsb25nIGZvcm1hdCwgd2UgY2FuIHVzZSBgdmFsYCBhcyB0aGUgdmFyaWFibGUgdGhhdCBjaGFuZ2VzIGZyb20gb25lIHN1Yi1wbG90IHRvIGFub3RoZXIsIGFuZCBmYWNldCBvbiBgdmFyaWFibGVgLiBIZXJlJ3MgYW4gZXhhbXBsZToNCg0KYGBge3IsIGZpZy5oZWlnaHQ9IDUsIGZpZy53aWR0aD0xMH0NCnFvZyAlPiUgDQogIG11dGF0ZShsb2dfaW5jb21lID0gbG9nKHdkaV9nZHBjYXBjb24yMDEwKSkgJT4lIA0KICBzZWxlY3Qod2RpX2xpZmV4cCwgDQogICAgICAgICBsb2dfaW5jb21lLCANCiAgICAgICAgIGB0cm9waWNhbCBwZXJjZW50YCA9IG51bm5fdHJvcGljYWwsIA0KICAgICAgICAgZW52aXJvbm1lbnQgPSBlcGlfZWgpICU+JSANCiAgZ2F0aGVyKGtleSA9ICJ2YXJpYWJsZSIsIHZhbHVlID0gInZhbCIsIC13ZGlfbGlmZXhwKSAlPiUgDQogIA0KICBnZ3Bsb3QoKSArDQogICAgYWVzKHggPSB2YWwsIHkgPSB3ZGlfbGlmZXhwKSArDQogICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgKw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgZmFjZXRfd3JhcCh+dmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlX3giKSArDQogICAgbGFicyh4ID0gIiIsDQogICAgICAgICB5ID0gIkxpZmUgZXhwZWN0YW5jeSBhdCBiaXJ0aCxcbmJvdGggc2V4ZXMgW3llYXJzXSIsDQogICAgICAgICBjYXB0aW9uID0gIlNvdXJjZSBvZiBkYXRhOiBXb3JsZCBCYW5rLCBOdW5uLCBFbnZpcm9ubWVudGFsIFBlcmZvcm1hbmNlIEluZGV4IikNCmBgYA0KDQpJIG9ubHkgc2VsZWN0ZWQgdGhyZWUgdmFyaWFibGVzIHRvIHBsb3QgaGVyZSwgYnV0IHlvdSBjb3VsZCBlYXNpbHkgc2VsZWN0IG1hbnkgbW9yZS4gVGhlIG9ubHkgZGlmZmVyZW5jZSB3b3VsZCBiZSB0aGF0IHRoZSBgc2VsZWN0YCBjb25kaXRpb24gY29taW5nIGJlZm9yZSBgZ2F0aGVyYCB3b3VsZCBjb250YWluIGEgbG90IG1vcmUgdmFyaWFibGVzLiBUaGUgYGdncGxvdGAgY29kZSBpdHNlbGYgd2lsbCByZW1haW4gdW5jaGFuZ2VkIG5vIG1hdHRlciBob3cgbWFueSB2YXJpYWJsZXMgeW91IGFkZC4gU2V2ZXJhbCB0aGluZ3MgdG8gbm90ZSBoZXJlOg0KDQoxLiBJZiB5b3Ugd2FudCB0byBkbyBzb21lIGNoYW5nZSB0byB0aGUgdmFyaWFibGUsIGxpa2UgSSBjaGFuZ2VkIHRoZSBpbmNvbWUgdG8gbG9nX2luY29tZSwgeW91IG5lZWQgdG8gZG8gaXQgd2l0aCBhIGBtdXRhdGVgIGZ1bmN0aW9uIF9iZWZvcmVfIHRoZSBzZWxlY3QuIFlvdSBjYW5ub3Qgd3JpdGUgc29tZXRoaW5nIGxpa2UgYHNlbGVjdChsb2dfaW5jb21lID0gbG9nKHdkaV9nZHBjYXBjb24yMDEwKSlgLg0KDQoyLiBZb3UgY2FuIHVzZSB0aGUgYHNlbGVjdGAgZnVuY3Rpb24gdG8gYWxzbyBjaGFuZ2UgdGhlIG5hbWVzIG9mIHRoZSB2YXJpYWJsZXMuIFRoZXNlIG5hbWVzIHdpbGwgYXBwZWFyIGFzIGxhYmVscyBhYm92ZSB0aGUgZmFjZXRzLCBzbyBpdCdzIGEgZ29vZCBpZGVhIHRvIGNoYW5nZSB0aGVtIHRvIHNvbWV0aGluZyBlYXN5IHRvIHVuZGVyc3RhbmQuIElmIHlvdSB3YW50IHRvIGluY2x1ZGUgc3BhY2VzIGluIHRoZSB2YXJpYWJsZSBuYW1lIHlvdSBjYW4sIGJ5IGVuY2xvc2luZyB0aGUgdmFyaWFibGUgbmFtZSBiZXR3ZWVuIGJhY2sgdGlja3MgXGB2YXJpYWJsZSBuYW1lIHdpdGggc3BhY2VzIGluIGl0XGAuIENvbnNpZGVyaW5nIHRoYXQgYGdhdGhlcmAgd2lsbCBwaWNrIHVwIGFsbCB0aG9zZSB2YXJpYWJsZSBuYW1lcyBmb3IgeW91LCB0aGlzIGlzIHRoZSBvbmUgcGxhY2Ugd2hlcmUgaXQgZG9lc24ndCBtYXR0ZXIgaWYgeW91IGhhdmUgbmFtZXMgd2l0aCBzcGFjZXMgaW4gdGhlbSAoZ2VuZXJhbGx5IHNwZWFraW5nLCBpdCBpcyBmcm93bmVkIHVwb24gdG8gaGF2ZSBzdWNoIHZhcmlhYmxlIG5hbWVzKS4gDQoNCkhlcmUncyBhbm90aGVyIGV4YW1wbGUsIGJ1dCB1c2luZyBgZmFjZXRfZ3JpZCgpYDoNCg0KYGBge3IsIGZpZy53aWR0aD0xMH0NCnFvZyAlPiUgDQogIG11dGF0ZShsb2dfaW5jb21lID0gbG9nKHdkaV9nZHBjYXBjb24yMDEwKSkgJT4lIA0KICBtdXRhdGUoZGVtb2NyYWN5ID0gaWZlbHNlKGZoX2lwb2xpdHkyIDwgbWVhbihmaF9pcG9saXR5MiwgbmEucm0gPSBUUlVFKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQXV0b2NyYWN5IiwgIkRlbW9jcmFjeSIpKSAlPiUgDQogIHNlbGVjdCh3ZGlfbGlmZXhwLCBsb2dfaW5jb21lLCBkZW1vY3JhY3ksDQogICAgICAgICBlbnZpcm9ubWVudCA9IGVwaV9laCwgDQogICAgICAgICBgdHJvcGljYWwgcGVyY2VudGAgPSBudW5uX3Ryb3BpY2FsKSAlPiUgDQogIGdhdGhlcihrZXkgPSAidmFyaWFibGUiLCB2YWx1ZSA9ICJ2YWwiLCAtYyh3ZGlfbGlmZXhwLCBkZW1vY3JhY3kpKSAlPiUgDQogIA0KICBnZ3Bsb3QoKSArDQogICAgYWVzKHggPSB2YWwsIHkgPSB3ZGlfbGlmZXhwKSArDQogICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgKw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgZmFjZXRfZ3JpZChkZW1vY3JhY3kgfiB2YXJpYWJsZSwgc2NhbGVzID0gImZyZWUiKSArDQogICAgbGFicyh4ID0gIiIsDQogICAgICAgICB5ID0gIkxpZmUgZXhwZWN0YW5jeSBhdCBiaXJ0aCxcbmJvdGggc2V4ZXMgW3llYXJzXSIsDQogICAgICAgICBjYXB0aW9uID0gIlNvdXJjZSBvZiBkYXRhOiBXb3JsZCBCYW5rLCBOdW5uLCBFbnZpcm9ubWVudGFsIFBlcmZvcm1hbmNlIEluZGV4LCBGcmVlZG9tIEhvdXNlIikNCmBgYA0KDQpOb3RpY2UgdGhhdCBJIGhhZCB0byBrZWVwIG91dCBvZiB0aGUgZ2F0aGVyaW5nIHByb2Nlc3MgYm90aCB0aGUgYHlgIHZhcmlhYmxlIChsaWZlIGV4cGVjdGFuY3kpIGFuZCB0aGUgYWRkaXRpb25hbCBmYWNldGluZyB2YXJpYWJsZSAoYGRlbW9jcmFjeWApLg0KDQojIFBsb3RpbmcgbXVsdGlwbGUgcGFpcnMgb2YgdmFyaWFibGVzDQoNClRoZSBhYm92ZSB0cmljayBpcyBleHRyZW1lbHkgdXNlZnVsLCBhbmQgeW91IHJhcmVseSBuZWVkIGFueXRoaW5nIGJleW9uZCBpdCAtLSBhcyB5b3UgdXN1YWxseSB3YW50IHRvIHNlZSBob3cgX2EgZ2l2ZW4gdmFyaWFibGVfIGlzIHJlbGF0ZWQgdG8gbWFueSBvdGhlcnMsIHJhdGhlciB0aGFuIHNlZSB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIG1hbnkgZGlmZmVyZW50IHJhbmRvbSBwYWlycyBvZiB2YXJpYWJsZXMuIA0KDQpOb25ldGhlbGVzcywgc29tZXRpbWVzIHlvdSBtaWdodCBuZWVkIHRvIHBsb3QgbnVtZXJvdXMgX3BhaXJzXyBvZiBfZGlmZmVyZW50XyB2YXJpYWJsZXMuIEluc3RlYWQgb2YgcGFpcnMgbGlrZSBge3gxLCB5fWAsIGB7eDIsIHl9YCwgLi4uLCBge3huLCB5fWAsIGxpa2UgYWJvdmUsIHlvdSBtaWdodCBuZWVkIHRvIHBsb3QgcGFpcnMgbGlrZSBge3gxLCB5MX1gLCBge3gyLCB5Mn1gLCAuLi4sIGB7eG4sIHlufWAuIFRoZSBgZ2F0aGVyYCB0cmljayBkb2Vzbid0IGhlbHAgeW91IGhlcmUuIEluIHByaW5jaXBsZSB5b3Ugd291bGQgbmVlZCBhIG5lc3RlZCBnYXRoZXIuIA0KDQojIyBTZXR0aW5nIHVwIHRoZSBjb250cm9sIHRhYmxlIG1hbnVhbGx5DQoNCkZvcnR1bmF0ZWx5LCB0aGVyZSdzIGFuIGVhc2llciB3YXkgdXNpbmcgdGhlIGZ1bmN0aW9uIGBkZiAlPiUgY2RhdGE6OnJvd3JlY3NfdG9fYmxvY2tzKHZhcnMpYC4gUHJldHR5IG11Y2ggdGhlIG1vc3QgY29tcGxpY2F0ZWQgdGhpbmcgYWJvdXQgdXNpbmcgdGhpcyBpcyByZW1lbWJlcmluZyBpdHMgbmFtZSEgVGhlIGRhdGFmcmFtZSBgZGZgIGlzIHlvdXIgd2lkZSBmb3JtYXQgZGF0YXNldC4gSW4gb3VyIGV4YW1wbGUsIGl0J3MgdGhlIGBxb2dgIGRhdGFzZXQuIFRoZSBgdmFyc2AgZGF0YXNldCBzcGVjaWZpZXMgYWxsIHRoZSBwYWlycyB0aGF0IHlvdSB3YW50LiBJdCBoYXMgdGhyZWUgY29sdW1ucyBhbmQgbG9va3Mgc29tZXRoaW5nIGxpa2UgdGhpczoNCg0KfCBpZCAgICAgICAgICAgICAgIHwgeCAgIHwgeSAgIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS18LS0tLS18LS0tLS18DQp8ICJwYWlyIDE6IEEgdnMgQiIgfCAiQSIgfCAiQiIgfA0KfCAicGFpciAyOiBCIHZzIEMiIHwgIkIiIHwgIkMiIHwNCnwgInBhaXIgMzogRCB2cyBFIiB8ICJEIiB8ICJFIiB8DQp8ICJwYWlyIDQ6IEYgdnMgQiIgfCAiRiIgfCAiQiIgfA0KfCAicGFpciA1OiBCIHZzIEEiIHwgIkIiIHwgIkEiIHwNCg0KVGhlIGNvbHVtbiBuYW1lcyAoYGlkYCwgYHhgLCBgeWApIGNvdWxkIGJlIGFueXRoaW5nIHlvdSB3YW50LCBidXQgbm90ZSB0aGF0IGFsbCB2YWx1ZXMgaW4gdGhpcyBkYXRhZnJhbWUgbmVlZCB0byBiZSBzdHJpbmdzLiBUaGUgdmFsdWVzIG9mIHRoZSBgaWRgIGNhbiBhbHNvIGJlIGFueXRoaW5nIHlvdSB3YW50LCBhcyBsb25nIGFzIHRoZXkgYXJlIHVuaXF1ZS4gVGhlc2UgbGFiZWxzIHVuZGVyIGBpZGAgd2lsbCBhcHBlYXIgYXMgdGhlIGxhYmVscyBvZiBlYWNoIHN1Yi1wbG90ICh3ZSdyZSBnb2luZyB0byBmYWNldCBieSBgaWRgKSwgc28geW91IHdhbnQgdG8gbWFrZSB0aGVtIGRlc2NyaXB0aXZlLiBUaGUgY29sdW1ucyBgeGAgYW5kIGB5YCBjb250YWluIHRoZSBuYW1lcyBvZiB0aGUgdmFyaWFibGVzIGZyb20gdGhlIGBkZmAgZGF0YWZyYW1lIHRoYXQgYXJlIGdvaW5nIHRvIGJlIHBhaXJlZCB0b2dldGhlciBpbiBlYWNoIHN1Yi1wbG90LiBUaGlzIGB2YXJzYCBkYXRhZnJhbWUgaXMgdGh1cyBhIHF1aWNrIHdheSBvZiBzZXR0aW5nIHVwIHRoZSBsaXN0IG9mIGFsbCB0aGUgcGxvdHMgeW91IHdhbnQgdG8gbWFrZSBhbmQgdGhlIGxhYmVscyBmb3IgZWFjaCBwbG90LiBJbiB0aGUgdGVybWlub2xvZ3kgb2YgdGhlIGBjZGF0YWAgcGFja2FnZSwgdGhlIGB2YXJzYCBkYXRhZnJhbWUgaXMgY2FsbGVkIHRoZSAiY29udHJvbCB0YWJsZSIuDQoNClRoZSBmdW5jdGlvbiBgY2RhdGE6OnJvd3JlY3NfdG9fYmxvY2tzKGRmLCB2YXJzKWAgd2lsbCBnZW5lcmF0ZSB0aGUgbG9uZyBmb3JtYXQgZGF0YWZyYW1lIG9mIHRoZSBfcGFpcnNfLiBUaGlzIGlzIGp1c3QgbGlrZSBgZ2F0aGVyYCBidXQgd2l0aCB0d28gdmFyaWFibGVzIGluc3RlYWQgb2YganVzdCBvbmUuIFVubGlrZSBgZ2F0aGVyYCwgeW91IGRvbid0IG5lZWQgdG8gYm90aGVyIHdpdGggdGhlIGBzZWxlY3RgIGJlZm9yZSBjYWxsaW5nIGl0LCBiZWNhdXNlIGl0IGF1dG9tYXRpY2FsbHkgdHJhbnNmb3JtcyBpbnRvIGxvbmcgZm9ybWF0IF9vbmx5XyB0aGUgdmFyaWFibGVzIHNwZWNpZmllZCBpbiB0aGUgY29udHJvbCB0YWJsZS4NCg0KSWYgeW91IHdhbnQgdG8gYnJpbmcgaW4gc29tZSBhZGRpdGlvbmFsIHZhcmlhYmxlcyBmcm9tIGBkZmAsIHdpdGhvdXQgZ2V0dGluZyB0aGVtIG1peGVkIHVwIGluIHRoaXMgdHJhbnNmb3JtYXRpb24gdG8gdGhlIGxvbmcgZm9ybWF0LCB5b3UgY2FuIGFkZCB0aGUgb3B0aW9uIGBjb2x1bW5zVG9Db3B5ID0gYygiUCIsICJRIiwgIlIiLCAuLi4pYCBhbmQgdmFyaWFibGVzICJQIiwgIlEiLCAiUiIsIGV0Yy4gd2lsbCBiZSBjb3BpZWQgc2VwYXJhdGVseSBhcyB0aGV5IGFyZS4gVGhpcyBpcyBzaW1pbGFyIHRvIHVzaW5nIGAtYyhQLCBRLCBSLCAuLi4pYCBpbiBgZ2F0aGVyKClgLg0KDQpIZXJlJ3MgYW4gZXhhbXBsZToNCg0KYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0NCg0KdmFycyA8LSB0cmliYmxlKA0KICB+aWQsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH54LCAgICAgICAgICAgICAgICAgIH55LA0KICAiRmlnLiAxOiBcbiB4PUluY29tZSBcbiB5PUxpZmUgZXhwZWN0YW5jeSIsICAgICAgICJsb2dfaW5jb21lIiwgIndkaV9saWZleHAiLA0KICAiRmlnLiAyOiBcbiB4PUVjb25vbWljIGZyZWVkb20gXG4geT1JbmNvbWUiLCAgICAgICJmaV9pbmRleCIsICAgImxvZ19pbmNvbWUiLA0KICAiRmlnLiAzOiBcbiB4PUNvcnJ1cHRpb24gXG4geT1JbmNvbWUiLCAgICAgICAgICAgICJiY2lfYmNpIiwgICAgImxvZ19pbmNvbWUiLA0KICAiRmlnLiA0OiBcbiB4PUVudmlyb25tZW50IFxuIHk9TGlmZSBleHBlY3RhbmN5IiwgICJlcGlfZWgiLCAgICAgIndkaV9saWZleHAiLA0KICAiRmlnLiA1OiBcbiB4PUNvcnJ1cHRpb24gXG4geT1FbnZpcm9ubWVudCIsICAgICAgICJiY2lfYmNpIiwgICAgImVwaV9laCIsDQogICJGaWcuIDY6IFxuIHg9RWR1Y2F0aW9uIFxuIHk9SW5jb21lIiwgICAgICAgICAgICAgInNnaV9zb2VkIiwgICAibG9nX2luY29tZSIsDQogICJGaWcuIDc6IFxuIHg9RWNvbm9taWMgZnJlZWVkb20gXG4geT1JbmVxdWFsaXR5IiwgImZpX2luZGV4IiwgICAid2RpX2dpbmkiLA0KICAiRmlnLiA4OiBcbiB4PUNvcnJ1cHRpb24gXG4geT1JbmVxdWFsaXR5IiwgICAgICAgICJiY2lfYmNpIiwgICAgIndkaV9naW5pIiwNCiAgIkZpZy4gOTogXG4geD1EZW1vY3JhY3kgXG4geT1JbmVxdWFsaXR5IiwgICAgICAgICAiZmhfaXBvbGl0eTIiLCAid2RpX2dpbmkiDQogICkNCg0KcW9nICU+JSANCiAgbXV0YXRlKGxvZ19pbmNvbWUgPSBsb2cod2RpX2dkcGNhcGNvbjIwMTApKSAlPiUgDQogIGNkYXRhOjpyb3dyZWNzX3RvX2Jsb2Nrcyh2YXJzKSAlPiUgDQogIA0KICBnZ3Bsb3QoKSArDQogICAgYWVzKHggPSB4LCB5ID0geSkgKyANCiAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArDQogICAgZ2VvbV9wb2ludCgpICsNCiAgICBmYWNldF93cmFwKH5pZCwgc2NhbGVzID0gImZyZWUiKSArDQogICAgbGFicyhjYXB0aW9uID0gIkRhdGEgc291cmNlczogV29ybGQgQmFuaywgRnJhc2VyIEluc3RpdHV0ZSwgIEJheWVzaWFuIENvcnJ1cHRpb24gSW5kZXgsXG4gRW52aXJvbm1lbnRhbCBQZXJmb3JtYW5jZSBJbmRleCwgU3VzdGFpbmFibGUgR292ZXJuYW5jZSBJbmRpY2F0b3JzLCBGcmVlZG9tIEhvdXNlIikNCmBgYA0KDQpPbmUgdGhpbmcgdG8gbm90aWNlIGhlcmUgaXMgdGhhdCB5b3UgY2FuIG1ha2UgYSBsYWJlbCBzcGFuIG92ZXIgc2V2ZXJhbCByb3dzIGJ5IHVzaW5nIHRoZSBgXG5gIGNvZGUuIEkndmUgdXNlZCB0aGlzIHNldmVyYWwgdGltZXMgYWJvdmUuDQoNCldoZW4geW91IGRvIHRoaXMga2luZCBvZiB0aGluZyBpbiBhIHJtYXJrZG93biBkb2N1bWVudCwgeW91IHdpbGwgbmVlZCB0byBzcGVjaWZ5IHRoZSBjaHVuayBvcHRpb25zLCBgZmlnLmhlaWdodGAgYW5kIGBmaWcud2lkdGhgLiBGb3IgaW5zdGFuY2UsIHRoZSBjaHVjayBhYm92ZSBoYXMgdGhlIG9wdGlvbnM6IGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfWAuIE9idmlvdXNseSwgdGhlIG1vcmUgc3VjaCBwbG90cyB5b3UgYXJlIGdlbmVyYXRpbmcsIHRoZSBncmVhdGVyIHRoZSBoZWlnaHQgb2YgdGhlIHByb2R1Y2VkIGZpZ3VyZSBzaG91bGQgYmUuIFNpbWlsYXJseSwgaWYgeW91J3JlIHVzaW5nIGBnZ3NhdmUoKWAgdG8gc2F2ZSB0aGUgcGxvdCBpbiBhIGZpbGUsIG1ha2Ugc3VyZSB0aGUgc2l6ZSBvZiB0aGUgaW1hZ2UgaXMgbGFyZ2UgZW5vdWdoICh1c2luZyB0aGUgYGhlaWdodGAgYW5kIGB3aWR0aGAgb3B0aW9ucyBvZiBgZ2dzYXZlYCkuDQoNCkhvdyBkbyB5b3UgY3JlYXRlIHRoZSBjb250cm9sIHRhYmxlIG1hbnVhbGx5PyANCg0KMS4gWW91IGNhbiB3cml0ZSBpdCBkb3duIGluc2lkZSBhIGB0cmliYmxlKClgIGZ1bmN0aW9uIChhcyBJJ3ZlIGRvbmUgYWJvdmUpLiBUaGlzIGFsbG93cyB5b3UgdG8gd3JpdGUgaXQgcm93LWJ5LXJvdywgcmF0aGVyIHRoYW4gY29sdW1uLWJ5LWNvbHVtbiAoYXMgaW4gYSBgZGF0YS5mcmFtZSgpYCkuIE5vdGljZSB0aGF0IHRoZSB2YXJpYWJsZSBuYW1lcyBpbiB0aGUgZmlyc3Qgcm93IGFyZSBwcmVjZWRlZCBieSBhIHRpbGRlLiANCg0KMi4gRXZlbiBiZXR0ZXIsIHlvdSBjYW4gY3JlYXRlIHRoZSB0YWJsZSBpbiBFeGNlbCBvciBHb29nbGUgU2hlZXRzIGFuZCB0aGFuIGp1c3QgY29weS1wYXN0ZSBpdCBpbnRvIHlvdXIgUiBjb2RlIHdpdGggdGhlIFtgZGF0YXBhc3RhYCBhZGQtb25dKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9kYXRhcGFzdGEvUkVBRE1FLmh0bWwpLiANCg0KMy4gT3IgeW91IGNhbiBqdXN0IGhhdmUgdGhlIGNvbnRyb2wgdGFibGUgc3RvcmVkIGluIGEgZmlsZSwgYW5kIGltcG9ydCBpdCB3aXRoIGByaW86OmltcG9ydCgpYC4NCg0KIyMgU2V0dGluZyB1cCB0aGUgY29udHJvbCB0YWJsZSBwcm9ncmFtYXRpY2FsbHkNCg0KWW91IGNhbiB1c2UgdGhpcyB0cmljayBmb3IgZ2VuZXJhdGluZyBhIGxhcmdlIG51bWJlciBvZiBwbG90cyB3aXRoIGEgc2luZ2xlIGBnZ3Bsb3QoKWAgY2FsbCBldmVuIHdoZW4gdGhlIHNldCBvZiBwYWlycyBvZiB2YXJpYWJsZXMgaXMgZ2VuZXJhdGVkIGJ5IHNvbWUgYXV0b21hdGVkIHByb2Nlc3MgLS0gcmF0aGVyIHRoYW4gZGVmaW5lZCBtYW51YWxseSBsaWtlIGFib3ZlLg0KDQpGb3IgZXhhbXBsZSwgc3VwcG9zZSB0aGF0IEkgd2FudCB0byBmaW5kIHRoZSBtb3N0IGhpZ2hseSBjb3JyZWxhdGVkIHZhcmlhYmxlcyBpbiB0aGUgYHFvZ2AgZGF0YXNldCwgYW5kIHBsb3QgdGhlaXIgc2NhdHRlciBwbG90cy4NCg0KRmlyc3QsIGJ1aWxkIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXgsIGFuZCBmbGF0dGVuIGl0IGludG8gYSBsb25nLWZvcm1hdCB0YWJsZS4gV2UncmUgZ29pbmcgdG8gY3JlYXRlIHRoZSBjb250cm9sIHRhYmxlIGJ5IHNpbXBseSB0YWtpbmcgYSBzdWJzZXQgb2YgdGhpcyBjb3JyZWxhdGlvbnMgZGF0YWZyYW1lLg0KDQpgYGB7cn0NCnFvZ19jb3IgPC0gcW9nICU+JSANCiAgc2VsZWN0X2lmKGlzLm51bWVyaWMpICU+JSANCiAgDQogICMgY3JlYXRlcyBtYXRyaXggb2YgYWxsIGNvcnJsYXRpb25zIA0KICBjb3IodXNlPSJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiKSAlPiUgICAgICAgICAgICAgICAgDQogIGFzLmRhdGEuZnJhbWUoKSAlPiUgDQogIHJvd25hbWVzX3RvX2NvbHVtbigidjEiKSAlPiUgDQogIA0KICAjIHR1cm4gaXQgaW50byBsb25nIGZvcm1hdA0KICBnYXRoZXIoa2V5ID0gInYyIiwgdmFsdWUgPSAiY29ycmVsYXRpb24iLCAtdjEpICU+JSAgDQogIA0KICAjIGVsaW1pbmF0ZSBjb3JyZWxhdGlvbnMgdG8gc2VsZg0KICBmaWx0ZXIoY29ycmVsYXRpb24gIT0gMSkNCmBgYA0KDQpTZWNvbmQsIHRha2UgYSBzYW1wbGUgb3V0IG9mIHRoZSBzZXQgb2YgaGlnaGx5IGNvcnJlbGF0ZWQgcGFpcnMsIGFuZCBwbG90IHRoZW0gdXNpbmcgdGhlIHNhbWUgcHJvY2VkdXJlIGFzIGJlZm9yZToNCg0KYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0NCnZhcnNfaGlnaGNvciA8LSBxb2dfY29yICU+JSANCiAgDQogICMgc2VsZWN0IGhpZ2hseSBjb3JyZWxhdGVkDQogIGZpbHRlcihjb3JyZWxhdGlvbiA+IDAuOCkgJT4lIA0KICANCiAgIyBzZWxlY3QgYSByYW5kb20gc2FtcGxlIG9mIDkgcGFpcnMNCiAgc2FtcGxlX24oc2l6ZSA9IDksIHJlcGxhY2UgPSBUUlVFKSAlPiUgDQogIA0KICAjIGJ1aWxkIHRoZSBjb250cm9sIHRhYmxlDQogIHJvd25hbWVzX3RvX2NvbHVtbigiaWQiKSAlPiUgDQogIHNlbGVjdChpZCwgdjEsIHYyKSAlPiUgDQogIG11dGF0ZShpZCA9IHBhc3RlKCJGaWd1cmUiLCBpZCwgIjogXG4geD0iLCB2MSwgIlxuIHk9IiwgdjIpKQ0KDQoNCnFvZyAlPiUgY2RhdGE6OnJvd3JlY3NfdG9fYmxvY2tzKHZhcnNfaGlnaGNvcikgJT4lIA0KICBnZ3Bsb3QoKSArDQogICAgYWVzKHggPSB2MSwgeSA9IHYyKSArIA0KICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIGZhY2V0X3dyYXAofmlkLCBzY2FsZXMgPSAiZnJlZSIpICsNCiAgICBsYWJzKHggPSAieCIsIHkgPSAieSIpDQpgYGANCg0KVG8gc2VlIHRoZSBtZWFuaW5nIG9mIHRoZXNlIHZhcmlhYmxlcywgeW91IG5lZWQgdG8gZWl0aGVyIGNoZWNrIHRoZSBbY29kZWJvb2sgZnJvbSB0aGUgX1F1YWxpdHkgb2YgR292ZXJubWVudCBJbnN0aXR1dGVfXShodHRwczovL3FvZy5wb2wuZ3Uuc2UvZGF0YS9kYXRhZG93bmxvYWRzL3FvZ2Jhc2ljZGF0YSksIG9yIHNlYXJjaCBmb3IgdGhlIHZhcmlhYmxlcyBpbiB0aGUgW3N1bW1hcnkgdGFibGUgYXQgdGhlIGJlZ2lubmluZyBvZiB0aGlzIGRvY3VtZW50XSgjc3VtdGFibGUpIGFuZCBzZWUgdGhlaXIgZGVzY3JpcHRpb24uIFlvdSBjYW4gdHJ5IHRvIGltcHJvdmUgdGhlIHBsb3QgYnkgZGVmaW5pbmcgdGhlIGBpZGAgdmFyaWFibGUgdXNpbmcgdGhlIHZhcmlhYmxlIGxhYmVscyBpbnN0ZWFkIG9mIHRoZWlyIG5hbWVzLg0KDQpIZXJlJ3MgYWxzbyBhIHNhbXBsZSBvZiB0aGUgaGlnaGx5IGFudGktY29ycmVsYXRlZCwgYW5kIG5vdyB1c2luZyBgZmFjZXRfZ3JpZCgpYDoNCg0KYGBge3IsIGZpZy5oZWlnaHQ9MjAsIGZpZy53aWR0aD0xMH0NCnZhcnNfYW50aWNvciA8LSBxb2dfY29yICU+JSANCiAgDQogICMgc2VsZWN0IGhpZ2hseSBhbnRpLWNvcnJlbGF0ZWQNCiAgZmlsdGVyKGNvcnJlbGF0aW9uIDwgLTAuOCkgJT4lIA0KICANCiAgIyBzZWxlY3QgYSByYW5kb20gc2FtcGxlIG9mIDkgcGFpcnMNCiAgc2FtcGxlX24oc2l6ZSA9IDksIHJlcGxhY2UgPSBUUlVFKSAlPiUgDQogIA0KICAjIGJ1aWxkIHRoZSBjb250cm9sIHRhYmxlDQogIHJvd25hbWVzX3RvX2NvbHVtbigiaWQiKSAlPiUgDQogIHNlbGVjdChpZCwgdjEsIHYyKSAlPiUgDQogIG11dGF0ZShpZCA9IHBhc3RlKCJGaWd1cmUiLCBpZCwgIjogXG4geD0iLCB2MSwgIlxuIHk9IiwgdjIpKQ0KDQoNCnFvZyAlPiUgDQogIG11dGF0ZShkZW1vY3JhY3kgPSBpZmVsc2UoZmhfaXBvbGl0eTIgPCBtZWFuKGZoX2lwb2xpdHkyLCBuYS5ybSA9IFRSVUUpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBdXRvY3JhY3kiLCAiRGVtb2NyYWN5IikpICU+JSANCiAgY2RhdGE6OnJvd3JlY3NfdG9fYmxvY2tzKHZhcnNfYW50aWNvciwgY29sdW1uc1RvQ29weSA9IGMoImRlbW9jcmFjeSIpKSAlPiUgDQogIA0KICBnZ3Bsb3QoKSArDQogICAgYWVzKHggPSB2MSwgeSA9IHYyKSArIA0KICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIGZhY2V0X2dyaWQoaWQgfiBkZW1vY3JhY3ksIHNjYWxlcyA9ICJmcmVlIiwgc3dpdGNoID0gInkiKSArDQogICAgbGFicyh4ID0gIngiLCB5ID0gInkiKQ0KYGBgDQoNCg0KIyBDb21iaW5pbmcgcGxvdHMgaW4gZ2VuZXJhbA0KDQpBcGFydCBmcm9tIGZhY2V0aW5nLCB0aGUgbW9zdCBnZW5lcmFsLCBhbmQgbGVhc3QgY29uY2lzZSwgd2F5IG9mIGNvbWJpbmluZyBtYW55IGdncGxvdHMgaW50byBvbmUgaXMgYnkgdXNpbmcgdGhlIGBncmlkRXh0cmE6OmdyaWQuYXJyYW5nZWAgb3IgdGhlIFtgcGF0Y2h3b3JrYCBwYWNrYWdlXShodHRwczovL2dpdGh1Yi5jb20vdGhvbWFzcDg1L3BhdGNod29yaykuIFVuZGVyIHRoaXMgYXBwcm9hY2ggeW91IHdpbGwgY3JlYXRlIHNldmVyYWwgZGlmZmVyZW50IGdncGxvdCBvYmplY3RzIHNlcGFyYXRlbHkgYW5kIHRoZW4gcHV0IHRoZW0gYWxsIHRvZ2V0aGVyOg0KDQpgYGByDQpwMSA8LSBkZjEgJT4lIGdncGxvdCgpICsgLi4uDQpwMiA8LSBkZjIgJT4lIGdncGxvdCgpICsgLi4uDQpwMyA8LSBkZjMgJT4lIGdncGxvdCgpICsgLi4uDQoNCiMgdXNpbmcgZ3JpZC5hcnJhbmdlDQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShwMSwgcDIsIHAzLCAuLi4pDQoNCiMgdXNpbmcgcGF0Y2h3b3JrIA0KIyAocDEgYW5kIHAyIGFyZSBvbiBmaXJzdCByb3csIGFuZCBwMyBvY2N1cGllcyB0aGUgZW50aXJlIHNlY29uZCByb3cpOg0KKHAxICsgcDIpIC8gcDMNCg0KYGBgDQoNCmBwYXRjaHdvcmtgIGlzIGJ5IGZhciB0aGUgZWFzaWVzdCB0byB1c2UgYnV0IGl0J3Mgbm90IHlldCBvbiBDUkFOLCBzbyB5b3UnZCBoYXZlIHRvIGluc3RhbGwgaXQgZnJvbSBnaXRodWIu