1 Learning Objectives

Today we’ll talk about a collection of plotting-related things. These notes consist of my own musings, along with ideas from Jenny’s notes here:

suppressPackageStartupMessages(library(tidyverse))
suppressPackageStartupMessages(library(gapminder))

2 1 Best Practices

2.0.1 1.1 Don’t over-assign variables

You’ve seen me make a plot like this before:

ggplot(gapminder, aes(year, lifeExp)) +
    geom_point()

and this:

p <- ggplot(gapminder, aes(year, lifeExp)) +
    geom_point()
p

Both produce the same output, but why bother with the p? General rule of thumb: don’t assign plots to variables unless you’d be repeating repeating yourself otherwise.

Some examples follow.

2.0.1.1 Multiple plots

To make the following two plots, this code is repetitive:

ggplot(gapminder, aes(year, lifeExp)) + 
    geom_jitter(alpha=0.5)
ggplot(gapminder, aes(year, lifeExp)) + 
    geom_line(aes(group=country), alpha=0.2)

The first line is repetitive. Let’s assign the first line to a variable, to avoid repeating ourselves:

p <- ggplot(gapminder, aes(year, lifeExp))
p + geom_jitter(alpha=0.5)
p + geom_line(aes(group=country), alpha=0.2)

2.0.1.2 One-time data wrangling

Let’s say you want to run an analysis on Chile’s gapminder data.

You might be tempted to make a plot:

gapminder %>% 
    filter(country == "Chile") %>% 
    arrange(year) %>% 
    ggplot(aes(gdpPercap, lifeExp)) +
    geom_path()

But then you have to re-wrangle the data for your downstream analyses!

Try this:

gapminder_chile <- gapminder %>% 
    filter(country == "Chile") %>% 
    arrange(year)
ggplot(gapminder_chile, aes(gdpPercap, lifeExp)) +
    geom_path()

Now you can run further analyses on gapminder_chile.

2.0.1.3 You want to save a figure to file

We’ll see this in the next topic, but saving to file:

  • Requires you to save the plot as a variable (ggsave or save), or
  • Requires you to print the plot “behind a wall” (with plotting devices)
    • …in which case it’s just good practice to also print the plot interactively for your own eyes first.

2.0.1.4 Avoid “variable chains”… you probably don’t need them

Feel the need to do this?

p <- ggplot(gapminder, aes(year, lifeExp))
p <- p + geom_line(aes(group=country), alpha=0.2, colour="red")
p <- p + geom_point(alpha=0.5)
p

This relies on you always executing these lines from top to bottom – and I can assure you, when programming interactively, this does not always happen.

Better is this…

p1 <- ggplot(gapminder, aes(year, lifeExp))
p2 <- p1 + geom_line(aes(group=country), alpha=0.2, colour="red")
p3 <- p2 + geom_point(alpha=0.5)
p3

But even better would be to get rid of the chain of p’s altogether. They’re just confusing and make the code hard to read.

Easy-to-read version:

ggplot(gapminder, aes(year, lifeExp)) +
    geom_line(aes(group=country), alpha=0.2, colour="red") +
    geom_point(alpha=0.5)

2.0.2 1.2 Keep stuff in data frames

Do you find yourself doing the following?

life_exp <- gapminder$lifeExp
year <- gapminder$year

Problems:

  • Breaks the “don’t repeat yourself” principle.
    • How? Two instances of the data are created (one as a variable, another in the data frame).
  • Pertinent for data frames: life_exp and year are no longer linked as they are in the data frame, and can get shuffled.
    • Maybe one gets sorted, or another loses some entries with na.omit.

It’s better practice to work directly from the data frame. Most “high-use” R functions require the specification of a data frame:

lm(lifeExp ~ year, data=gapminder)

…is better than

lm(life_exp ~ year)

But not all functions allow the input of a data frame – let’s try finding the correlation of two variables:

# This produces an error:
cor(lifeExp, gdpPercap, data=gapminder)

Instead, use the with function. Syntax: with(dataframe, function_of_df_variables).

with(gapminder, cor(lifeExp, gdpPercap))

2.0.3 1.3 Making data frames/Tibbles

Consider putting variables into a data frame instead of having them as loose variables.

  • Old-school way: data.frame
data.frame(x = 1:5, 
           y = (1:5)^2, 
           text = c("alpha", "beta", "gamma", "delta", "epsilon"))
  • Easier way: tibble::tibble(), or tribble() for smaller data frames “by hand”. Jenny’s example:
tibble(x = 1:5,
       y = x ^ 2,
       text = c("alpha", "beta", "gamma", "delta", "epsilon"))
## if you're truly "hand coding", tribble() is an alternative
tribble(
  ~ x, ~ y,    ~ text,
    1,   1,   "alpha",
    2,   4,    "beta",
    3,   9,   "gamma",
    4,  16,   "delta",
    5,  25, "epsilon"
)
  • expand.grid is also a useful function. Makes a row for every combination:
expand.grid(type=c("A", "B", "C"),
            level=1:4)

3 2 Saving Figures to File

There are a few ways you can do this, but first let’s look at two main types of image files.

3.0.1 2.0 Types of Image Files

Images are stored on your computer as either vector or raster.

  • Raster: an n by m grid of pixels, each with its own colour.
    • Digital cameras do this. jpeg, png, gif, bmp.
    • Look horrible when expanded.
  • Vector: represented as shapes and lines.
    • pdf, svg.
    • Can scale naturally when expanded. svg is actually just plain text.

There’s no gold standard for which file format is best. Perhaps use both. I use png and pdf (so does Jenny).

Want more advice? Check out 10 tips for making your R graphics look their best.

3.0.2 2.1 Method 1: Graphics Devices

Here’s how to save an image using this method.

  1. Open a specific graphics device with a function like pdf(), png(), jpeg(), etc. Put the desired filename/path in here.
  2. Print the plot.
  3. Close the graphics device with dev.off().

Here’s an example that will write a plot to a pdf called my_plot.pdf:

(p <- ggplot(gapminder, aes(year, lifeExp)) +
     geom_point())
pdf("my_plot.pdf", width=12, height=6)
p
dev.off()

Check out the devices documentation to learn all about this. Works for other plot types too.

3.0.3 2.2 Method 2: Using ggsave

You can save ggplot2 plots using the ggsave function. Here’s an example.

p <- ggplot(gapminder, aes(gdpPercap, lifeExp)) +
    geom_point(alpha=0.2) +
    scale_x_log10()
# p    # Uncomment when working interactively
ggsave("my_plot.pdf", p)
ggsave("my_plot.png", p)

ggsave guesses which arguments are best (and the file extension!), but sometimes you’ll need to specify them yourself.

3.0.4 2.3 Problems

Sometimes you’ll end up seeing an empty plot. More details can be found here, but one example is when using a for loop:

for (i in 1){
    qplot(1,1)
}

Nothing appears. This is because you need to wrap the plot with print:

for (i in 1){
    qplot(1,1) %>% 
        print
}

PS: when working interactively, print is usually run for you “behind the scenes”.

4 3 Colours

4.0.1 3.1 Using different Colour Schemes

Don’t like the default colour scheme that comes with ggplot2? Let’s say we want to change the colours in the following plots:

(Psst: for p1, can you see the difference between colour and fill?)

(p1 <- gapminder %>% 
        filter(year > 1990) %>%
        ggplot(aes(x=year)) +
        geom_bar(aes(weight=gdpPercap, fill=continent),
                 position="dodge",
                 colour="black") +
        scale_y_continuous("GDP Per Capita"))
(p2 <- ggplot(gapminder, aes(gdpPercap, lifeExp)) +
        geom_point(aes(colour=year)) +
        scale_x_log10())
(p3 <- gapminder %>% 
        filter(continent=="Europe") %>% 
        group_by(country) %>% 
        arrange(year) %>% 
        mutate(chpop=(pop-lag(pop))/lag(pop)) %>%
        filter(!is.na(chpop)) %>% 
        ggplot(aes(gdpPercap, lifeExp)) +
        geom_point(aes(colour=chpop)) +
        scale_x_log10())

Each of these plots represent different types of scales. Here are the scales, and the corresponding ggplot2 scales to modify the colours:

  1. Qualitative (p1). Coloured by categorical data.
    • Use scale_colour_manual or scale_fill_manual, with the values argument
  2. Sequential (p2). Coloured from low to high.
    • Use scale_colour_gradient or scale_fill_gradient, with the low and high arguments.
  3. Diverging (p3). Coloured from negative to zero to positive.
    • Use scale_colour_gradient2 or scale_fill_gradient2, with the low, mid, and high arguments.

Note: there’s also scale_colour_gradientn and scale_fill_gradientn for “n-colour gradients”, fading between n colours. Use the colours argument.

Let’s try the qualitative plot:

my_cols <- c("red", "blue", "green", "wheat3", "orange")
p1 + scale_fill_manual(values=my_cols)
# You can even name the colour vector according the levels:
names(my_cols) <- c("Europe", "Americas", "Africa", "Asia", "Oceania")
p1 + scale_fill_manual(values=my_cols)

The sequential plot:

p2 + scale_colour_gradient(low="black", high="red")

The diverging plot:

p3 + scale_colour_gradient2(low="blue", mid="white", high="red")

4.0.2 3.2 Identifying Colours in R

How would anyone know that R understands wheat3? R has lots of colours pre-defined – see them all by running colors():

colors() %>% head

Or, check out Jenny’s cheatsheet with white background or black background.

You could also specify the hexadecimal colour.

p1 + scale_fill_manual(values=c("#9b5123", "#5dd8a6", "#11ad52", "#dde91d", "#0113d7"))

4.0.3 3.3 Palettes

Unless you’re an expert in colours, it’s best to leave choosing colours to an expert to balance all sorts of colour dimensions based on the intended purpose.

The RColorBrewer is a nice R package, based on designs by Cynthia Brewer, a colour expert. Here are the palettes available:

library(RColorBrewer)
display.brewer.all()

Notice that they’re separated into the three types of scales.

Of note: the qualitative scales consist of colours of equal “importance” – not one “dominates” the other in prominence. My favourite is Dark2.

Let’s display and print the hexadecimal codes of the Dark2 palette – the first 5. Note that we need to specify the number of colours from the palette!

display.brewer.pal(n=5, "Dark2")
brewer.pal(n=5, "Dark2")

Go ahead and put this into the qualitative plot:

p1 + scale_fill_manual(values=brewer.pal(n=5, "Dark2"))

We can also use the functions that are built-in to ggplot2:

  • scale_colour_brewer or scale_fill_brewer, with the palette argument, for discrete data only.
  • scale_colour_distiller interpolates the colours in a given palette.
p1 + scale_fill_brewer(palette="Dark2")
p2 + scale_colour_distiller(palette="Greens", direction=1)
p3 + scale_colour_distiller(palette="Spectral")

Other useful R packages:

  • dichromat package. Converts a colour palette into various colour-blind views.
  • viridis package. Provides colour-blind friendly scales.
library(viridis)
p2 + scale_colour_viridis(option="inferno")
p3 + scale_colour_viridis()

5 4 Exercise Set 1

  1. Make histograms of Life Expectancies, facetted by continent. Paint the histogram by continent, using a colour palette of your choice from RColorBrewer.
ggplot(gapminder, aes(lifeExp)) +
    facet_wrap(~ continent) +
    geom_histogram(aes(y=..density.., 
                       fill=continent)) +
    scale_fill_brewer(palette="Dark2")
  1. Make a trend plot/spaghetti plot of life Expectancies over time for each country. Highlight Rwanda in red (ensure the legend shows this, too).
colour_layer <- scale_colour_manual("", 
                        labels=c("Other Countries", "Rwanda"),
                        values=c("black", "red"))
ggplot(gapminder, aes(year, lifeExp)) +
    geom_line(aes(group=country,
                  colour=country=="Rwanda"), alpha=0.2) +
    colour_layer

BONUS: make all countries besides Rwanda have alpha transparency of 0.2, and Rwanda be non-transparent.

ggplot(gapminder, aes(year, lifeExp)) +
    geom_line(aes(group=country,
                  colour=country=="Rwanda",
                  alpha=country=="Rwanda")) +
    colour_layer +
    scale_alpha_discrete(range=c(0.2, 1),
                         guide=FALSE)

6 5 Adding layers as a list

You might have noticed in the above solutions that we can save individual layers as R variables:

(my_layer <- geom_point(alpha=0.2))
ggplot(gapminder, aes(gdpPercap, lifeExp)) +
    my_layer

You can also save multiple layers in a single R variable – a list.

my_layer2 <- geom_line(aes(group=country),
                       alpha=0.2)
multiple_layers <- list(my_layer, my_layer2)
ggplot(gapminder, aes(year, lifeExp)) +
    multiple_layers

This becomes really useful when you want to plot multiple layers iteratively. Let’s plot f(x)=sin(k*x) where k ranges from 1 to 4:

fun_layers <- lapply(1:4, function(k) 
    list(stat_function(fun=function(x) sin(k*x),
                       colour=brewer.pal(8, "Dark2")[k])))
ggplot(data.frame(x=c(0, 2*pi)), aes(x)) +
    fun_layers +
    scale_colour_manual(labels=1:4)

Making a legend in this case requires some hard-coding. See this Stack Overflow page for advice.

7 6 Scatterplot Symbols

You can change the “points” that get plotted in a scatterplot. Anything (?) on your keyboard works with the shape argument:

p <- gapminder %>% 
    filter(country == "Canada") %>% 
    ggplot(aes(lifeExp, gdpPercap))
p + geom_point(shape="%", size=5)
p + geom_point(shape='"', size=5)

Or, you can specify a plotting character” (pch) by its symbol number. Let’s try 21.

p + geom_point(shape=21, size=5)
# Also works:
p + geom_point(pch=21, size=5)

21-pch is great when used in conjunction with a size aesthetic:

gapminder %>% 
    filter(continent=="Asia") %>% 
    ggplot(aes(gdpPercap, lifeExp, size=pop)) +
    geom_point(shape=21) +
    scale_x_log10()

7.1 Exercise Set 2

  1. For the gapminder data in 2007, for all continents except Oceania, make a scatterplot of gdpPercap vs lifeExp facetted by continent, so that the size is proportional to the pop, the circumference of each circular “point” is black, and the interior is coloured by the country colours in the vector country_colors (NOTE: this vector comes with the gapminder package). Hide the legend.
(e2_1 <- gapminder %>%
    filter(year == 2007, continent != "Oceania") %>% 
    ggplot(aes(gdpPercap, lifeExp)) +
    facet_wrap(~ continent) +
    geom_point(aes(size=pop,
                   fill=country),
               shape=21) +
    scale_x_log10() +
    scale_fill_manual(values=country_colors,
                      guide=FALSE))
  1. Save the above plot to file, as a pdf and png.
# ggsave("tmp/e2_1.png", e2_1)
# ggsave("tmp/e2_1.pdf", e2_1)
LS0tCnRpdGxlOiAiU1RBVCA1NDUgQ2xhc3MgTWVldGluZyAxMyIKb3V0cHV0OgogICAgaHRtbF9ub3RlYm9vazoKICAgICAgICB0b2M6IHRydWUKICAgICAgICB0aGVtZTogY2VydWxlYW4KICAgICAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgojIExlYXJuaW5nIE9iamVjdGl2ZXMKCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTMsIGZpZy5hbGlnbj0iY2VudGVyIikKYGBgCgpUb2RheSB3ZSdsbCB0YWxrIGFib3V0IGEgY29sbGVjdGlvbiBvZiBwbG90dGluZy1yZWxhdGVkIHRoaW5ncy4gVGhlc2Ugbm90ZXMgY29uc2lzdCBvZiBteSBvd24gbXVzaW5ncywgYWxvbmcgd2l0aCBpZGVhcyBmcm9tIEplbm55J3Mgbm90ZXMgaGVyZToKCi0gW1VzaW5nIGNvbG9ycyBpbiBSXShibG9jazAxOF9jb2xvcnMuaHRtbCkKLSBbVGFraW5nIGNvbnRyb2wgb2YgcXVhbGl0YXRpdmUgY29sb3JzIGluIGBnZ3Bsb3QyYF0oYmxvY2swMTlfZW5mb3JjZS1jb2xvci1zY2hlbWUuaHRtbCkKLSBbU2VjcmV0cyBvZiBhIGhhcHB5IGdyYXBoaW5nIGxpZmVdKGJsb2NrMDE2X3NlY3JldHMtaGFwcHktZ3JhcGhpbmcuaHRtbCkKLSBbV3JpdGluZyBmaWd1cmVzIHRvIGZpbGVdKGJsb2NrMDE3X3dyaXRlLWZpZ3VyZS10by1maWxlLmh0bWwpCiAgIApgYGB7cn0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkodGlkeXZlcnNlKSkKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoZ2FwbWluZGVyKSkKYGBgCgoKIyAxIEJlc3QgUHJhY3RpY2VzCgojIyMgMS4xIERvbid0IG92ZXItYXNzaWduIHZhcmlhYmxlcwoKWW91J3ZlIHNlZW4gbWUgbWFrZSBhIHBsb3QgbGlrZSB0aGlzIGJlZm9yZToKCmBgYApnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeWVhciwgbGlmZUV4cCkpICsKICAgIGdlb21fcG9pbnQoKQpgYGAKCl9hbmRfIHRoaXM6CgpgYGAKcCA8LSBnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeWVhciwgbGlmZUV4cCkpICsKICAgIGdlb21fcG9pbnQoKQpwCmBgYAoKQm90aCBwcm9kdWNlIHRoZSBzYW1lIG91dHB1dCwgYnV0IHdoeSBib3RoZXIgd2l0aCB0aGUgYHBgPyBHZW5lcmFsIHJ1bGUgb2YgdGh1bWI6IF9kb24ndCBhc3NpZ24gcGxvdHMgdG8gdmFyaWFibGVzIHVubGVzcyB5b3UnZCBiZSByZXBlYXRpbmcgcmVwZWF0aW5nIHlvdXJzZWxmIG90aGVyd2lzZV8uCgpTb21lIGV4YW1wbGVzIGZvbGxvdy4KCiMjIyMgTXVsdGlwbGUgcGxvdHMKClRvIG1ha2UgdGhlIGZvbGxvd2luZyB0d28gcGxvdHMsIHRoaXMgY29kZSBpcyByZXBldGl0aXZlOgoKYGBge3J9CmdncGxvdChnYXBtaW5kZXIsIGFlcyh5ZWFyLCBsaWZlRXhwKSkgKyAKICAgIGdlb21faml0dGVyKGFscGhhPTAuNSkKZ2dwbG90KGdhcG1pbmRlciwgYWVzKHllYXIsIGxpZmVFeHApKSArIAogICAgZ2VvbV9saW5lKGFlcyhncm91cD1jb3VudHJ5KSwgYWxwaGE9MC4yKQpgYGAKClRoZSBmaXJzdCBsaW5lIGlzIHJlcGV0aXRpdmUuIExldCdzIGFzc2lnbiB0aGUgZmlyc3QgbGluZSB0byBhIHZhcmlhYmxlLCB0byBhdm9pZCByZXBlYXRpbmcgb3Vyc2VsdmVzOgoKYGBge3J9CnAgPC0gZ2dwbG90KGdhcG1pbmRlciwgYWVzKHllYXIsIGxpZmVFeHApKQpwICsgZ2VvbV9qaXR0ZXIoYWxwaGE9MC41KQpwICsgZ2VvbV9saW5lKGFlcyhncm91cD1jb3VudHJ5KSwgYWxwaGE9MC4yKQpgYGAKCiMjIyMgT25lLXRpbWUgZGF0YSB3cmFuZ2xpbmcKCkxldCdzIHNheSB5b3Ugd2FudCB0byBydW4gYW4gYW5hbHlzaXMgb24gQ2hpbGUncyBgZ2FwbWluZGVyYCBkYXRhLgoKWW91IG1pZ2h0IGJlIHRlbXB0ZWQgdG8gbWFrZSBhIHBsb3Q6CgpgYGB7cn0KZ2FwbWluZGVyICU+JSAKICAgIGZpbHRlcihjb3VudHJ5ID09ICJDaGlsZSIpICU+JSAKICAgIGFycmFuZ2UoeWVhcikgJT4lIAogICAgZ2dwbG90KGFlcyhnZHBQZXJjYXAsIGxpZmVFeHApKSArCiAgICBnZW9tX3BhdGgoKQpgYGAKCkJ1dCB0aGVuIHlvdSBoYXZlIHRvIHJlLXdyYW5nbGUgdGhlIGRhdGEgZm9yIHlvdXIgZG93bnN0cmVhbSBhbmFseXNlcyEKClRyeSB0aGlzOgoKYGBge3J9CmdhcG1pbmRlcl9jaGlsZSA8LSBnYXBtaW5kZXIgJT4lIAogICAgZmlsdGVyKGNvdW50cnkgPT0gIkNoaWxlIikgJT4lIAogICAgYXJyYW5nZSh5ZWFyKQpnZ3Bsb3QoZ2FwbWluZGVyX2NoaWxlLCBhZXMoZ2RwUGVyY2FwLCBsaWZlRXhwKSkgKwogICAgZ2VvbV9wYXRoKCkKYGBgCgpOb3cgeW91IGNhbiBydW4gZnVydGhlciBhbmFseXNlcyBvbiBgZ2FwbWluZGVyX2NoaWxlYC4gCgojIyMjIFlvdSB3YW50IHRvIHNhdmUgYSBmaWd1cmUgdG8gZmlsZQoKV2UnbGwgc2VlIHRoaXMgaW4gdGhlIG5leHQgdG9waWMsIGJ1dCBzYXZpbmcgdG8gZmlsZToKCi0gUmVxdWlyZXMgeW91IHRvIHNhdmUgdGhlIHBsb3QgYXMgYSB2YXJpYWJsZSAoYGdnc2F2ZWAgb3IgYHNhdmVgKSwgb3IKLSBSZXF1aXJlcyB5b3UgdG8gcHJpbnQgdGhlIHBsb3QgImJlaGluZCBhIHdhbGwiICh3aXRoIHBsb3R0aW5nIGRldmljZXMpCiAgICAtIC4uLmluIHdoaWNoIGNhc2UgaXQncyBqdXN0IGdvb2QgcHJhY3RpY2UgdG8gX2Fsc29fIHByaW50IHRoZSBwbG90IGludGVyYWN0aXZlbHkgZm9yIHlvdXIgb3duIGV5ZXMgZmlyc3QuIAoKCiMjIyMgQXZvaWQgInZhcmlhYmxlIGNoYWlucyIuLi4geW91IHByb2JhYmx5IGRvbid0IG5lZWQgdGhlbQoKRmVlbCB0aGUgbmVlZCB0byBkbyB0aGlzPwoKYGBge3J9CnAgPC0gZ2dwbG90KGdhcG1pbmRlciwgYWVzKHllYXIsIGxpZmVFeHApKQpwIDwtIHAgKyBnZW9tX2xpbmUoYWVzKGdyb3VwPWNvdW50cnkpLCBhbHBoYT0wLjIsIGNvbG91cj0icmVkIikKcCA8LSBwICsgZ2VvbV9wb2ludChhbHBoYT0wLjUpCnAKYGBgCgpUaGlzIHJlbGllcyBvbiB5b3UgX2Fsd2F5c18gZXhlY3V0aW5nIHRoZXNlIGxpbmVzIGZyb20gdG9wIHRvIGJvdHRvbSAtLSBhbmQgSSBjYW4gYXNzdXJlIHlvdSwgd2hlbiBwcm9ncmFtbWluZyBpbnRlcmFjdGl2ZWx5LCB0aGlzIGRvZXMgbm90IGFsd2F5cyBoYXBwZW4uCgpCZXR0ZXIgaXMgdGhpcy4uLgoKYGBge3J9CnAxIDwtIGdncGxvdChnYXBtaW5kZXIsIGFlcyh5ZWFyLCBsaWZlRXhwKSkKcDIgPC0gcDEgKyBnZW9tX2xpbmUoYWVzKGdyb3VwPWNvdW50cnkpLCBhbHBoYT0wLjIsIGNvbG91cj0icmVkIikKcDMgPC0gcDIgKyBnZW9tX3BvaW50KGFscGhhPTAuNSkKcDMKYGBgCgpCdXQgZXZlbiBiZXR0ZXIgd291bGQgYmUgdG8gZ2V0IHJpZCBvZiB0aGUgY2hhaW4gb2YgYHBgJ3MgYWx0b2dldGhlci4gVGhleSdyZSBqdXN0IGNvbmZ1c2luZyBhbmQgbWFrZSB0aGUgY29kZSBoYXJkIHRvIHJlYWQuCgpFYXN5LXRvLXJlYWQgdmVyc2lvbjoKCmBgYHtyfQpnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeWVhciwgbGlmZUV4cCkpICsKICAgIGdlb21fbGluZShhZXMoZ3JvdXA9Y291bnRyeSksIGFscGhhPTAuMiwgY29sb3VyPSJyZWQiKSArCiAgICBnZW9tX3BvaW50KGFscGhhPTAuNSkKYGBgCgojIyMgMS4yIEtlZXAgc3R1ZmYgaW4gZGF0YSBmcmFtZXMKCkRvIHlvdSBmaW5kIHlvdXJzZWxmIGRvaW5nIHRoZSBmb2xsb3dpbmc/CgpgYGB7cn0KbGlmZV9leHAgPC0gZ2FwbWluZGVyJGxpZmVFeHAKeWVhciA8LSBnYXBtaW5kZXIkeWVhcgpgYGAKClByb2JsZW1zOgoKLSBCcmVha3MgdGhlICJkb24ndCByZXBlYXQgeW91cnNlbGYiIHByaW5jaXBsZS4KICAgIC0gSG93PyBUd28gaW5zdGFuY2VzIG9mIHRoZSBkYXRhIGFyZSBjcmVhdGVkIChvbmUgYXMgYSB2YXJpYWJsZSwgYW5vdGhlciBpbiB0aGUgZGF0YSBmcmFtZSkuCi0gUGVydGluZW50IGZvciBkYXRhIGZyYW1lczogYGxpZmVfZXhwYCBhbmQgYHllYXJgIGFyZSBfbm8gbG9uZ2VyIGxpbmtlZF8gYXMgdGhleSBhcmUgaW4gdGhlIGRhdGEgZnJhbWUsIGFuZCBjYW4gZ2V0IHNodWZmbGVkLgogICAgLSBNYXliZSBvbmUgZ2V0cyBgc29ydGBlZCwgb3IgYW5vdGhlciBsb3NlcyBzb21lIGVudHJpZXMgd2l0aCBgbmEub21pdGAuIAoKSXQncyBiZXR0ZXIgcHJhY3RpY2UgdG8gd29yayBkaXJlY3RseSBmcm9tIHRoZSBkYXRhIGZyYW1lLiBNb3N0ICJoaWdoLXVzZSIgUiBmdW5jdGlvbnMgcmVxdWlyZSB0aGUgc3BlY2lmaWNhdGlvbiBvZiBhIGRhdGEgZnJhbWU6CgpgYGB7cn0KbG0obGlmZUV4cCB+IHllYXIsIGRhdGE9Z2FwbWluZGVyKQpgYGAKCi4uLmlzIGJldHRlciB0aGFuCgpgYGB7cn0KbG0obGlmZV9leHAgfiB5ZWFyKQpgYGAKCkJ1dCBub3QgYWxsIGZ1bmN0aW9ucyBhbGxvdyB0aGUgaW5wdXQgb2YgYSBkYXRhIGZyYW1lIC0tIGxldCdzIHRyeSBmaW5kaW5nIHRoZSBjb3JyZWxhdGlvbiBvZiB0d28gdmFyaWFibGVzOgoKYGBgCiMgVGhpcyBwcm9kdWNlcyBhbiBlcnJvcjoKY29yKGxpZmVFeHAsIGdkcFBlcmNhcCwgZGF0YT1nYXBtaW5kZXIpCmBgYAoKSW5zdGVhZCwgdXNlIHRoZSBgd2l0aGAgZnVuY3Rpb24uIFN5bnRheDogYHdpdGgoZGF0YWZyYW1lLCBmdW5jdGlvbl9vZl9kZl92YXJpYWJsZXMpYC4KCmBgYHtyfQp3aXRoKGdhcG1pbmRlciwgY29yKGxpZmVFeHAsIGdkcFBlcmNhcCkpCmBgYAoKIyMjIDEuMyBNYWtpbmcgZGF0YSBmcmFtZXMvVGliYmxlcwoKQ29uc2lkZXIgcHV0dGluZyB2YXJpYWJsZXMgaW50byBhIGRhdGEgZnJhbWUgaW5zdGVhZCBvZiBoYXZpbmcgdGhlbSBhcyBsb29zZSB2YXJpYWJsZXMuIAoKLSBPbGQtc2Nob29sIHdheTogYGRhdGEuZnJhbWVgCgpgYGB7cn0KZGF0YS5mcmFtZSh4ID0gMTo1LCAKICAgICAgICAgICB5ID0gKDE6NSleMiwgCiAgICAgICAgICAgdGV4dCA9IGMoImFscGhhIiwgImJldGEiLCAiZ2FtbWEiLCAiZGVsdGEiLCAiZXBzaWxvbiIpKQpgYGAKCi0gRWFzaWVyIHdheTogYHRpYmJsZTo6dGliYmxlKClgLCBvciBgdHJpYmJsZSgpYCBmb3Igc21hbGxlciBkYXRhIGZyYW1lcyAiYnkgaGFuZCIuIEplbm55J3MgZXhhbXBsZToKCmBgYHtyfQp0aWJibGUoeCA9IDE6NSwKICAgICAgIHkgPSB4IF4gMiwKICAgICAgIHRleHQgPSBjKCJhbHBoYSIsICJiZXRhIiwgImdhbW1hIiwgImRlbHRhIiwgImVwc2lsb24iKSkKIyMgaWYgeW91J3JlIHRydWx5ICJoYW5kIGNvZGluZyIsIHRyaWJibGUoKSBpcyBhbiBhbHRlcm5hdGl2ZQp0cmliYmxlKAogIH4geCwgfiB5LCAgICB+IHRleHQsCiAgICAxLCAgIDEsICAgImFscGhhIiwKICAgIDIsICAgNCwgICAgImJldGEiLAogICAgMywgICA5LCAgICJnYW1tYSIsCiAgICA0LCAgMTYsICAgImRlbHRhIiwKICAgIDUsICAyNSwgImVwc2lsb24iCikKYGBgCgotIGBleHBhbmQuZ3JpZGAgaXMgYWxzbyBhIHVzZWZ1bCBmdW5jdGlvbi4gTWFrZXMgYSByb3cgZm9yIGV2ZXJ5IGNvbWJpbmF0aW9uOgoKYGBge3J9CmV4cGFuZC5ncmlkKHR5cGU9YygiQSIsICJCIiwgIkMiKSwKICAgICAgICAgICAgbGV2ZWw9MTo0KQpgYGAKCgojIDIgU2F2aW5nIEZpZ3VyZXMgdG8gRmlsZQoKVGhlcmUgYXJlIGEgZmV3IHdheXMgeW91IGNhbiBkbyB0aGlzLCBidXQgZmlyc3QgbGV0J3MgbG9vayBhdCB0d28gbWFpbiBfdHlwZXNfIG9mIGltYWdlIGZpbGVzLgoKIyMjIDIuMCBUeXBlcyBvZiBJbWFnZSBGaWxlcwoKSW1hZ2VzIGFyZSBzdG9yZWQgb24geW91ciBjb21wdXRlciBhcyBlaXRoZXIgX3ZlY3Rvcl8gb3IgX3Jhc3Rlcl8uCgotIF9fUmFzdGVyX186IGFuIGBuYCBieSBgbWAgZ3JpZCBvZiBwaXhlbHMsIGVhY2ggd2l0aCBpdHMgb3duIGNvbG91ci4KICAgIC0gRGlnaXRhbCBjYW1lcmFzIGRvIHRoaXMuIGBqcGVnYCwgYHBuZ2AsIGBnaWZgLCBgYm1wYC4KICAgIC0gTG9vayBob3JyaWJsZSB3aGVuIGV4cGFuZGVkLgotIF9fVmVjdG9yX186IHJlcHJlc2VudGVkIGFzIHNoYXBlcyBhbmQgbGluZXMuCiAgICAtIGBwZGZgLCBbYHN2Z2BdKGh0dHA6Ly9ibG9nLnJldm9sdXRpb25hbmFseXRpY3MuY29tLzIwMTEvMDcvci1zdmctZ3JhcGhpY3MuaHRtbCkuCiAgICAtIENhbiBzY2FsZSBuYXR1cmFsbHkgd2hlbiBleHBhbmRlZC4gYHN2Z2AgaXMgYWN0dWFsbHkganVzdCBbcGxhaW4gdGV4dF0oaHR0cHM6Ly93d3cudzNzY2hvb2xzLmNvbS9ncmFwaGljcy9zdmdfaW50cm8uYXNwKS4KClRoZXJlJ3Mgbm8gZ29sZCBzdGFuZGFyZCBmb3Igd2hpY2ggZmlsZSBmb3JtYXQgaXMgYmVzdC4gUGVyaGFwcyB1c2UgYm90aC4gSSB1c2UgYHBuZ2AgYW5kIGBwZGZgIChbc28gZG9lcyBKZW5ueV0oaHR0cDovL3N0YXQ1NDUuY29tL2Jsb2NrMDE3X3dyaXRlLWZpZ3VyZS10by1maWxlLmh0bWwjZ3JhcGhpY3MtZGV2aWNlcykpLgoKV2FudCBtb3JlIGFkdmljZT8gQ2hlY2sgb3V0IFsxMCB0aXBzIGZvciBtYWtpbmcgeW91ciBSIGdyYXBoaWNzIGxvb2sgdGhlaXIgYmVzdF0oaHR0cDovL2Jsb2cucmV2b2x1dGlvbmFuYWx5dGljcy5jb20vMjAwOS8wMS8xMC10aXBzLWZvci1tYWtpbmcteW91ci1yLWdyYXBoaWNzLWxvb2stdGhlaXItYmVzdC5odG1sKS4KCiMjIyAyLjEgTWV0aG9kIDE6IEdyYXBoaWNzIERldmljZXMKCkhlcmUncyBob3cgdG8gc2F2ZSBhbiBpbWFnZSB1c2luZyB0aGlzIG1ldGhvZC4gCgoxLiBPcGVuIGEgc3BlY2lmaWMgZ3JhcGhpY3MgZGV2aWNlIHdpdGggYSBmdW5jdGlvbiBsaWtlIGBwZGYoKWAsIGBwbmcoKWAsIGBqcGVnKClgLCBldGMuIFB1dCB0aGUgZGVzaXJlZCBmaWxlbmFtZS9wYXRoIGluIGhlcmUuCjIuIFByaW50IHRoZSBwbG90LgozLiBDbG9zZSB0aGUgZ3JhcGhpY3MgZGV2aWNlIHdpdGggYGRldi5vZmYoKWAuCgpIZXJlJ3MgYW4gZXhhbXBsZSB0aGF0IHdpbGwgd3JpdGUgYSBwbG90IHRvIGEgcGRmIGNhbGxlZCBgbXlfcGxvdC5wZGZgOgoKYGBgCihwIDwtIGdncGxvdChnYXBtaW5kZXIsIGFlcyh5ZWFyLCBsaWZlRXhwKSkgKwogICAgIGdlb21fcG9pbnQoKSkKcGRmKCJteV9wbG90LnBkZiIsIHdpZHRoPTEyLCBoZWlnaHQ9NikKcApkZXYub2ZmKCkKYGBgCgpDaGVjayBvdXQgdGhlIFtkZXZpY2VzIGRvY3VtZW50YXRpb25dKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9nckRldmljZXMvdmVyc2lvbnMvMy40LjEvdG9waWNzL0RldmljZXMpIHRvIGxlYXJuIGFsbCBhYm91dCB0aGlzLiBXb3JrcyBmb3Igb3RoZXIgcGxvdCB0eXBlcyB0b28uCgojIyMgMi4yIE1ldGhvZCAyOiBVc2luZyBgZ2dzYXZlYAoKWW91IGNhbiBzYXZlIGBnZ3Bsb3QyYCBwbG90cyB1c2luZyB0aGUgYGdnc2F2ZWAgZnVuY3Rpb24uIEhlcmUncyBhbiBleGFtcGxlLgoKYGBgCnAgPC0gZ2dwbG90KGdhcG1pbmRlciwgYWVzKGdkcFBlcmNhcCwgbGlmZUV4cCkpICsKICAgIGdlb21fcG9pbnQoYWxwaGE9MC4yKSArCiAgICBzY2FsZV94X2xvZzEwKCkKIyBwICAgICMgVW5jb21tZW50IHdoZW4gd29ya2luZyBpbnRlcmFjdGl2ZWx5Cmdnc2F2ZSgibXlfcGxvdC5wZGYiLCBwKQpnZ3NhdmUoIm15X3Bsb3QucG5nIiwgcCkKYGBgCgpgZ2dzYXZlYCBndWVzc2VzIHdoaWNoIGFyZ3VtZW50cyBhcmUgYmVzdCAoYW5kIHRoZSBmaWxlIGV4dGVuc2lvbiEpLCBidXQgc29tZXRpbWVzIHlvdSdsbCBuZWVkIHRvIHNwZWNpZnkgdGhlbSB5b3Vyc2VsZi4gCgojIyMgMi4zIFByb2JsZW1zCgpTb21ldGltZXMgeW91J2xsIGVuZCB1cCBzZWVpbmcgYW4gZW1wdHkgcGxvdC4gTW9yZSBkZXRhaWxzIGNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cDovL3N0YXQ1NDUuY29tL2Jsb2NrMDE3X3dyaXRlLWZpZ3VyZS10by1maWxlLmh0bWwjZGVzcGFpci1vdmVyLW5vbi1leGlzdGVudC1vci1lbXB0eS1maWd1cmVzKSwgYnV0IG9uZSBleGFtcGxlIGlzIHdoZW4gdXNpbmcgYSBgZm9yYCBsb29wOgoKYGBge3J9CmZvciAoaSBpbiAxKXsKICAgIHFwbG90KDEsMSkKfQpgYGAKCk5vdGhpbmcgYXBwZWFycy4gVGhpcyBpcyBiZWNhdXNlIHlvdSBuZWVkIHRvIHdyYXAgdGhlIHBsb3Qgd2l0aCBgcHJpbnRgOgoKYGBge3J9CmZvciAoaSBpbiAxKXsKICAgIHFwbG90KDEsMSkgJT4lIAogICAgICAgIHByaW50Cn0KYGBgCgpQUzogd2hlbiB3b3JraW5nIGludGVyYWN0aXZlbHksIGBwcmludGAgaXMgdXN1YWxseSBydW4gZm9yIHlvdSAiYmVoaW5kIHRoZSBzY2VuZXMiLgoKIyAzIENvbG91cnMKCiMjIyAzLjEgVXNpbmcgZGlmZmVyZW50IENvbG91ciBTY2hlbWVzCgpEb24ndCBsaWtlIHRoZSBkZWZhdWx0IGNvbG91ciBzY2hlbWUgdGhhdCBjb21lcyB3aXRoIGBnZ3Bsb3QyYD8gTGV0J3Mgc2F5IHdlIHdhbnQgdG8gY2hhbmdlIHRoZSBjb2xvdXJzIGluIHRoZSBmb2xsb3dpbmcgcGxvdHM6CgooUHNzdDogZm9yIGBwMWAsIGNhbiB5b3Ugc2VlIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gYGNvbG91cmAgYW5kIGBmaWxsYD8pCgpgYGB7cn0KKHAxIDwtIGdhcG1pbmRlciAlPiUgCiAgICAgICAgZmlsdGVyKHllYXIgPiAxOTkwKSAlPiUKICAgICAgICBnZ3Bsb3QoYWVzKHg9eWVhcikpICsKICAgICAgICBnZW9tX2JhcihhZXMod2VpZ2h0PWdkcFBlcmNhcCwgZmlsbD1jb250aW5lbnQpLAogICAgICAgICAgICAgICAgIHBvc2l0aW9uPSJkb2RnZSIsCiAgICAgICAgICAgICAgICAgY29sb3VyPSJibGFjayIpICsKICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoIkdEUCBQZXIgQ2FwaXRhIikpCihwMiA8LSBnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoZ2RwUGVyY2FwLCBsaWZlRXhwKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG91cj15ZWFyKSkgKwogICAgICAgIHNjYWxlX3hfbG9nMTAoKSkKKHAzIDwtIGdhcG1pbmRlciAlPiUgCiAgICAgICAgZmlsdGVyKGNvbnRpbmVudD09IkV1cm9wZSIpICU+JSAKICAgICAgICBncm91cF9ieShjb3VudHJ5KSAlPiUgCiAgICAgICAgYXJyYW5nZSh5ZWFyKSAlPiUgCiAgICAgICAgbXV0YXRlKGNocG9wPShwb3AtbGFnKHBvcCkpL2xhZyhwb3ApKSAlPiUKICAgICAgICBmaWx0ZXIoIWlzLm5hKGNocG9wKSkgJT4lIAogICAgICAgIGdncGxvdChhZXMoZ2RwUGVyY2FwLCBsaWZlRXhwKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG91cj1jaHBvcCkpICsKICAgICAgICBzY2FsZV94X2xvZzEwKCkpCmBgYAoKRWFjaCBvZiB0aGVzZSBwbG90cyByZXByZXNlbnQgZGlmZmVyZW50IHR5cGVzIG9mIHNjYWxlcy4gSGVyZSBhcmUgdGhlIHNjYWxlcywgYW5kIHRoZSBjb3JyZXNwb25kaW5nIGBnZ3Bsb3QyYCBzY2FsZXMgdG8gbW9kaWZ5IHRoZSBjb2xvdXJzOgoKMS4gX19RdWFsaXRhdGl2ZV9fIChgcDFgKS4gQ29sb3VyZWQgYnkgY2F0ZWdvcmljYWwgZGF0YS4gCiAgICAtIFVzZSBgc2NhbGVfY29sb3VyX21hbnVhbGAgb3IgYHNjYWxlX2ZpbGxfbWFudWFsYCwgd2l0aCB0aGUgYHZhbHVlc2AgYXJndW1lbnQKMi4gX19TZXF1ZW50aWFsX18gKGBwMmApLiBDb2xvdXJlZCBmcm9tIGxvdyB0byBoaWdoLgogICAgLSBVc2UgYHNjYWxlX2NvbG91cl9ncmFkaWVudGAgb3IgYHNjYWxlX2ZpbGxfZ3JhZGllbnRgLCB3aXRoIHRoZSBgbG93YCBhbmQgYGhpZ2hgIGFyZ3VtZW50cy4KMy4gX19EaXZlcmdpbmdfXyAoYHAzYCkuIENvbG91cmVkIGZyb20gbmVnYXRpdmUgdG8gemVybyB0byBwb3NpdGl2ZS4KICAgIC0gVXNlIGBzY2FsZV9jb2xvdXJfZ3JhZGllbnQyYCBvciBgc2NhbGVfZmlsbF9ncmFkaWVudDJgLCB3aXRoIHRoZSBgbG93YCwgYG1pZGAsIGFuZCBgaGlnaGAgYXJndW1lbnRzLgoKTm90ZTogdGhlcmUncyBhbHNvIGBzY2FsZV9jb2xvdXJfZ3JhZGllbnRuYCBhbmQgYHNjYWxlX2ZpbGxfZ3JhZGllbnRuYCBmb3IgImBuYC1jb2xvdXIgZ3JhZGllbnRzIiwgZmFkaW5nIGJldHdlZW4gYG5gIGNvbG91cnMuIFVzZSB0aGUgYGNvbG91cnNgIGFyZ3VtZW50LiAKCkxldCdzIHRyeSB0aGUgcXVhbGl0YXRpdmUgcGxvdDoKCmBgYHtyfQpteV9jb2xzIDwtIGMoInJlZCIsICJibHVlIiwgImdyZWVuIiwgIndoZWF0MyIsICJvcmFuZ2UiKQpwMSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1teV9jb2xzKQojIFlvdSBjYW4gZXZlbiBuYW1lIHRoZSBjb2xvdXIgdmVjdG9yIGFjY29yZGluZyB0aGUgbGV2ZWxzOgpuYW1lcyhteV9jb2xzKSA8LSBjKCJFdXJvcGUiLCAiQW1lcmljYXMiLCAiQWZyaWNhIiwgIkFzaWEiLCAiT2NlYW5pYSIpCnAxICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPW15X2NvbHMpCmBgYAoKVGhlIHNlcXVlbnRpYWwgcGxvdDoKCmBgYHtyfQpwMiArIHNjYWxlX2NvbG91cl9ncmFkaWVudChsb3c9ImJsYWNrIiwgaGlnaD0icmVkIikKYGBgCgpUaGUgZGl2ZXJnaW5nIHBsb3Q6CgpgYGB7cn0KcDMgKyBzY2FsZV9jb2xvdXJfZ3JhZGllbnQyKGxvdz0iYmx1ZSIsIG1pZD0id2hpdGUiLCBoaWdoPSJyZWQiKQpgYGAKCiMjIyAzLjIgSWRlbnRpZnlpbmcgQ29sb3VycyBpbiBSCgpIb3cgd291bGQgYW55b25lIGtub3cgdGhhdCBSIHVuZGVyc3RhbmRzIGB3aGVhdDNgPyBSIGhhcyBsb3RzIG9mIGNvbG91cnMgcHJlLWRlZmluZWQgLS0gc2VlIHRoZW0gYWxsIGJ5IHJ1bm5pbmcgYGNvbG9ycygpYDoKCmBgYHtyfQpjb2xvcnMoKSAlPiUgaGVhZApgYGAKCk9yLCBjaGVjayBvdXQgSmVubnkncyBjaGVhdHNoZWV0IHdpdGggW3doaXRlIGJhY2tncm91bmRdKGh0dHA6Ly9zdGF0NTQ1LmNvbS9pbWcvci5jb2wud2hpdGUuYmtnZC5wZGYpIG9yIFtibGFjayBiYWNrZ3JvdW5kXShodHRwOi8vc3RhdDU0NS5jb20vaW1nL3IuY29sLmJsYWNrLmJrZ2QucGRmKS4KCllvdSBjb3VsZCBhbHNvIHNwZWNpZnkgdGhlIGhleGFkZWNpbWFsIGNvbG91ci4gCgpgYGB7cn0KcDEgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiIzliNTEyMyIsICIjNWRkOGE2IiwgIiMxMWFkNTIiLCAiI2RkZTkxZCIsICIjMDExM2Q3IikpCmBgYAoKIyMjIDMuMyBQYWxldHRlcwoKVW5sZXNzIHlvdSdyZSBhbiBleHBlcnQgaW4gY29sb3VycywgaXQncyBiZXN0IHRvIGxlYXZlIGNob29zaW5nIGNvbG91cnMgdG8gYW4gZXhwZXJ0IHRvIGJhbGFuY2UgYWxsIHNvcnRzIG9mIGNvbG91ciBkaW1lbnNpb25zIGJhc2VkIG9uIHRoZSBpbnRlbmRlZCBwdXJwb3NlLgoKVGhlIGBSQ29sb3JCcmV3ZXJgIGlzIGEgbmljZSBSIHBhY2thZ2UsIGJhc2VkIG9uIGRlc2lnbnMgYnkgQ3ludGhpYSBCcmV3ZXIsIGEgY29sb3VyIGV4cGVydC4gSGVyZSBhcmUgdGhlIHBhbGV0dGVzIGF2YWlsYWJsZToKCmBgYHtyLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD0xMH0KbGlicmFyeShSQ29sb3JCcmV3ZXIpCmRpc3BsYXkuYnJld2VyLmFsbCgpCmBgYAoKTm90aWNlIHRoYXQgdGhleSdyZSBzZXBhcmF0ZWQgaW50byB0aGUgdGhyZWUgdHlwZXMgb2Ygc2NhbGVzLiAKCk9mIG5vdGU6IHRoZSBxdWFsaXRhdGl2ZSBzY2FsZXMgY29uc2lzdCBvZiBjb2xvdXJzIG9mIGVxdWFsICJpbXBvcnRhbmNlIiAtLSBub3Qgb25lICJkb21pbmF0ZXMiIHRoZSBvdGhlciBpbiBwcm9taW5lbmNlLiBNeSBmYXZvdXJpdGUgaXMgYERhcmsyYC4KCkxldCdzIGRpc3BsYXkgYW5kIHByaW50IHRoZSBoZXhhZGVjaW1hbCBjb2RlcyBvZiB0aGUgYERhcmsyYCBwYWxldHRlIC0tIHRoZSBmaXJzdCA1LiBOb3RlIHRoYXQgd2UgbmVlZCB0byBzcGVjaWZ5IHRoZSBudW1iZXIgb2YgY29sb3VycyBmcm9tIHRoZSBwYWxldHRlIQoKYGBge3J9CmRpc3BsYXkuYnJld2VyLnBhbChuPTUsICJEYXJrMiIpCmJyZXdlci5wYWwobj01LCAiRGFyazIiKQpgYGAKCkdvIGFoZWFkIGFuZCBwdXQgdGhpcyBpbnRvIHRoZSBxdWFsaXRhdGl2ZSBwbG90OgoKYGBge3J9CnAxICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWJyZXdlci5wYWwobj01LCAiRGFyazIiKSkKYGBgCgpXZSBjYW4gYWxzbyB1c2UgdGhlIGZ1bmN0aW9ucyB0aGF0IGFyZSBidWlsdC1pbiB0byBgZ2dwbG90MmA6CgotIGBzY2FsZV9jb2xvdXJfYnJld2VyYCBvciBgc2NhbGVfZmlsbF9icmV3ZXJgLCB3aXRoIHRoZSBgcGFsZXR0ZWAgYXJndW1lbnQsIGZvciBfZGlzY3JldGUgZGF0YSBvbmx5Xy4gCi0gYHNjYWxlX2NvbG91cl9kaXN0aWxsZXJgIGludGVycG9sYXRlcyB0aGUgY29sb3VycyBpbiBhIGdpdmVuIHBhbGV0dGUuIAoKYGBge3J9CnAxICsgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iRGFyazIiKQpwMiArIHNjYWxlX2NvbG91cl9kaXN0aWxsZXIocGFsZXR0ZT0iR3JlZW5zIiwgZGlyZWN0aW9uPTEpCnAzICsgc2NhbGVfY29sb3VyX2Rpc3RpbGxlcihwYWxldHRlPSJTcGVjdHJhbCIpCmBgYAoKCk90aGVyIHVzZWZ1bCBSIHBhY2thZ2VzOgoKLSBgZGljaHJvbWF0YCBwYWNrYWdlLiBDb252ZXJ0cyBhIGNvbG91ciBwYWxldHRlIGludG8gdmFyaW91cyBjb2xvdXItYmxpbmQgdmlld3MuIAotIFtgdmlyaWRpc2BdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy92aXJpZGlzL3ZpZ25ldHRlcy9pbnRyby10by12aXJpZGlzLmh0bWwpIHBhY2thZ2UuIFByb3ZpZGVzIGNvbG91ci1ibGluZCBmcmllbmRseSBzY2FsZXMuIAoKYGBge3J9CmxpYnJhcnkodmlyaWRpcykKcDIgKyBzY2FsZV9jb2xvdXJfdmlyaWRpcyhvcHRpb249ImluZmVybm8iKQpwMyArIHNjYWxlX2NvbG91cl92aXJpZGlzKCkKYGBgCgojIDQgRXhlcmNpc2UgU2V0IDEKCjEuIE1ha2UgaGlzdG9ncmFtcyBvZiBMaWZlIEV4cGVjdGFuY2llcywgZmFjZXR0ZWQgYnkgYGNvbnRpbmVudGAuIFBhaW50IHRoZSBoaXN0b2dyYW0gYnkgY29udGluZW50LCB1c2luZyBhIGNvbG91ciBwYWxldHRlIG9mIHlvdXIgY2hvaWNlIGZyb20gYFJDb2xvckJyZXdlcmAuCgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKGxpZmVFeHApKSArCiAgICBmYWNldF93cmFwKH4gY29udGluZW50KSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeT0uLmRlbnNpdHkuLiwgCiAgICAgICAgICAgICAgICAgICAgICAgZmlsbD1jb250aW5lbnQpKSArCiAgICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSJEYXJrMiIpCmBgYAoKMi4gTWFrZSBhIHRyZW5kIHBsb3Qvc3BhZ2hldHRpIHBsb3Qgb2YgbGlmZSBFeHBlY3RhbmNpZXMgb3ZlciB0aW1lIGZvciBlYWNoIGNvdW50cnkuIEhpZ2hsaWdodCBSd2FuZGEgaW4gcmVkIChlbnN1cmUgdGhlIGxlZ2VuZCBzaG93cyB0aGlzLCB0b28pLgoKYGBge3J9CmNvbG91cl9sYXllciA8LSBzY2FsZV9jb2xvdXJfbWFudWFsKCIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk90aGVyIENvdW50cmllcyIsICJSd2FuZGEiKSwKICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzPWMoImJsYWNrIiwgInJlZCIpKQpnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeWVhciwgbGlmZUV4cCkpICsKICAgIGdlb21fbGluZShhZXMoZ3JvdXA9Y291bnRyeSwKICAgICAgICAgICAgICAgICAgY29sb3VyPWNvdW50cnk9PSJSd2FuZGEiKSwgYWxwaGE9MC4yKSArCiAgICBjb2xvdXJfbGF5ZXIKYGBgCgpCT05VUzogbWFrZSBhbGwgY291bnRyaWVzIGJlc2lkZXMgUndhbmRhIGhhdmUgYWxwaGEgdHJhbnNwYXJlbmN5IG9mIDAuMiwgYW5kIFJ3YW5kYSBiZSBub24tdHJhbnNwYXJlbnQuCgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKHllYXIsIGxpZmVFeHApKSArCiAgICBnZW9tX2xpbmUoYWVzKGdyb3VwPWNvdW50cnksCiAgICAgICAgICAgICAgICAgIGNvbG91cj1jb3VudHJ5PT0iUndhbmRhIiwKICAgICAgICAgICAgICAgICAgYWxwaGE9Y291bnRyeT09IlJ3YW5kYSIpKSArCiAgICBjb2xvdXJfbGF5ZXIgKwogICAgc2NhbGVfYWxwaGFfZGlzY3JldGUocmFuZ2U9YygwLjIsIDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGU9RkFMU0UpCmBgYAoKCiMgNSBBZGRpbmcgbGF5ZXJzIGFzIGEgbGlzdAoKWW91IG1pZ2h0IGhhdmUgbm90aWNlZCBpbiB0aGUgYWJvdmUgc29sdXRpb25zIHRoYXQgd2UgY2FuIHNhdmUgaW5kaXZpZHVhbCBsYXllcnMgYXMgUiB2YXJpYWJsZXM6CgpgYGB7cn0KKG15X2xheWVyIDwtIGdlb21fcG9pbnQoYWxwaGE9MC4yKSkKZ2dwbG90KGdhcG1pbmRlciwgYWVzKGdkcFBlcmNhcCwgbGlmZUV4cCkpICsKICAgIG15X2xheWVyCmBgYAoKWW91IGNhbiBhbHNvIHNhdmUgbXVsdGlwbGUgbGF5ZXJzIGluIGEgX3NpbmdsZSBSIHZhcmlhYmxlXyAtLSBhIGxpc3QuCgpgYGB7cn0KbXlfbGF5ZXIyIDwtIGdlb21fbGluZShhZXMoZ3JvdXA9Y291bnRyeSksCiAgICAgICAgICAgICAgICAgICAgICAgYWxwaGE9MC4yKQptdWx0aXBsZV9sYXllcnMgPC0gbGlzdChteV9sYXllciwgbXlfbGF5ZXIyKQpnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeWVhciwgbGlmZUV4cCkpICsKICAgIG11bHRpcGxlX2xheWVycwpgYGAKClRoaXMgYmVjb21lcyByZWFsbHkgdXNlZnVsIHdoZW4geW91IHdhbnQgdG8gcGxvdCBtdWx0aXBsZSBsYXllcnMgaXRlcmF0aXZlbHkuIExldCdzIHBsb3QgYGYoeCk9c2luKGsqeClgIHdoZXJlIGBrYCByYW5nZXMgZnJvbSAxIHRvIDQ6CgpgYGB7cn0KZnVuX2xheWVycyA8LSBsYXBwbHkoMTo0LCBmdW5jdGlvbihrKSAKICAgIGxpc3Qoc3RhdF9mdW5jdGlvbihmdW49ZnVuY3Rpb24oeCkgc2luKGsqeCksCiAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyPWJyZXdlci5wYWwoOCwgIkRhcmsyIilba10pKSkKZ2dwbG90KGRhdGEuZnJhbWUoeD1jKDAsIDIqcGkpKSwgYWVzKHgpKSArCiAgICBmdW5fbGF5ZXJzICsKICAgIHNjYWxlX2NvbG91cl9tYW51YWwobGFiZWxzPTE6NCkKYGBgCgpNYWtpbmcgYSBsZWdlbmQgaW4gdGhpcyBjYXNlIHJlcXVpcmVzIHNvbWUgaGFyZC1jb2RpbmcuIFNlZSBbdGhpcyBTdGFjayBPdmVyZmxvdyBwYWdlXShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xOTIxOTQxMS9zdGF0LWZ1bmN0aW9uLWFuZC1sZWdlbmRzLWNyZWF0ZS1wbG90LXdpdGgtdHdvLXNlcGFyYXRlLWNvbG91ci1sZWdlbmRzLW1hcHBlZC10KSBmb3IgYWR2aWNlLiAKCiMgNiBTY2F0dGVycGxvdCBTeW1ib2xzCgpZb3UgY2FuIGNoYW5nZSB0aGUgInBvaW50cyIgdGhhdCBnZXQgcGxvdHRlZCBpbiBhIHNjYXR0ZXJwbG90LiBBbnl0aGluZyAoPykgb24geW91ciBrZXlib2FyZCB3b3JrcyB3aXRoIHRoZSBgc2hhcGVgIGFyZ3VtZW50OgoKYGBge3J9CnAgPC0gZ2FwbWluZGVyICU+JSAKICAgIGZpbHRlcihjb3VudHJ5ID09ICJDYW5hZGEiKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKGxpZmVFeHAsIGdkcFBlcmNhcCkpCnAgKyBnZW9tX3BvaW50KHNoYXBlPSIlIiwgc2l6ZT01KQpwICsgZ2VvbV9wb2ludChzaGFwZT0nIicsIHNpemU9NSkKYGBgCgpPciwgeW91IGNhbiBzcGVjaWZ5IGEgWyJgcGBsb3R0aW5nIGBjaGBhcmFjdGVyIiAocGNoKV0oaHR0cDovL3d3dy5lbmRtZW1vLmNvbS9wcm9ncmFtL1IvcGNoc3ltYm9scy5waHApIGJ5IGl0cyBzeW1ib2wgbnVtYmVyLiBMZXQncyB0cnkgYDIxYC4KCmBgYHtyfQpwICsgZ2VvbV9wb2ludChzaGFwZT0yMSwgc2l6ZT01KQojIEFsc28gd29ya3M6CnAgKyBnZW9tX3BvaW50KHBjaD0yMSwgc2l6ZT01KQpgYGAKCjIxLXBjaCBpcyBncmVhdCB3aGVuIHVzZWQgaW4gY29uanVuY3Rpb24gd2l0aCBhIGBzaXplYCBhZXN0aGV0aWM6CgpgYGB7cn0KZ2FwbWluZGVyICU+JSAKICAgIGZpbHRlcihjb250aW5lbnQ9PSJBc2lhIikgJT4lIAogICAgZ2dwbG90KGFlcyhnZHBQZXJjYXAsIGxpZmVFeHAsIHNpemU9cG9wKSkgKwogICAgZ2VvbV9wb2ludChzaGFwZT0yMSkgKwogICAgc2NhbGVfeF9sb2cxMCgpCmBgYAoKIyMgRXhlcmNpc2UgU2V0IDIKCjEuIEZvciB0aGUgYGdhcG1pbmRlcmAgZGF0YSBpbiAyMDA3LCBmb3IgYWxsIGNvbnRpbmVudHMgZXhjZXB0IE9jZWFuaWEsIG1ha2UgYSBzY2F0dGVycGxvdCBvZiBgZ2RwUGVyY2FwYCB2cyBgbGlmZUV4cGAgZmFjZXR0ZWQgYnkgYGNvbnRpbmVudGAsIHNvIHRoYXQgdGhlIHNpemUgaXMgcHJvcG9ydGlvbmFsIHRvIHRoZSBgcG9wYCwgdGhlIGNpcmN1bWZlcmVuY2Ugb2YgZWFjaCBjaXJjdWxhciAicG9pbnQiIGlzIGJsYWNrLCBhbmQgdGhlIGludGVyaW9yIGlzIGNvbG91cmVkIGJ5IHRoZSBjb3VudHJ5IGNvbG91cnMgaW4gdGhlIHZlY3RvciBgY291bnRyeV9jb2xvcnNgIChOT1RFOiB0aGlzIHZlY3RvciBjb21lcyB3aXRoIHRoZSBgZ2FwbWluZGVyYCBwYWNrYWdlKS4gSGlkZSB0aGUgbGVnZW5kLiAKCmBgYHtyfQooZTJfMSA8LSBnYXBtaW5kZXIgJT4lCiAgICBmaWx0ZXIoeWVhciA9PSAyMDA3LCBjb250aW5lbnQgIT0gIk9jZWFuaWEiKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKGdkcFBlcmNhcCwgbGlmZUV4cCkpICsKICAgIGZhY2V0X3dyYXAofiBjb250aW5lbnQpICsKICAgIGdlb21fcG9pbnQoYWVzKHNpemU9cG9wLAogICAgICAgICAgICAgICAgICAgZmlsbD1jb3VudHJ5KSwKICAgICAgICAgICAgICAgc2hhcGU9MjEpICsKICAgIHNjYWxlX3hfbG9nMTAoKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y291bnRyeV9jb2xvcnMsCiAgICAgICAgICAgICAgICAgICAgICBndWlkZT1GQUxTRSkpCmBgYAoKCjIuIFNhdmUgdGhlIGFib3ZlIHBsb3QgdG8gZmlsZSwgYXMgYSBgcGRmYCBhbmQgYHBuZ2AuCgpgYGB7cn0KIyBnZ3NhdmUoInRtcC9lMl8xLnBuZyIsIGUyXzEpCiMgZ2dzYXZlKCJ0bXAvZTJfMS5wZGYiLCBlMl8xKQpgYGAK