Colour vs fill
aesthetic
Fill and colour scales in ggplot2 can use the same palettes. Some
shapes such as lines only accept the colour
aesthetic,
while others, such as polygons, accept both colour
and
fill
aesthetics. In the latter case, the
colour
refers to the border of the shape, and the
fill
to the interior.
All symbols have a foreground colour, so if we add
color = "navy"
, they all are affected.
s + geom_point(aes(shape = z), size = 4, colour = "navy")
While all symbols have a foreground colour, symbols 21-25 also take a
background colour (fill). So if we add fill = "orchid"
,
only the last row of symbols are affected.
s + geom_point(aes(shape = z), size = 4, colour = "navy", fill = "orchid")
This is why it is so common to have issues with color
and fill
and geom_point()
, by the way!
Data
For the rest of today, we’ll play with the sounds
dataset. This data was derived from the R package wordbankr
,
an R interface to access Wordbank- an open source
database of children’s vocabulary development. The tool used to measure
children’s language and communicative development in this database is
the MacArthur-Bates Communicative
Development Inventories (MB-CDI). The MD-CDI is a parent-reported
questionnaire.
Here is a glimpse of the data:
glimpse(sounds)
Rows: 33
Columns: 7
$ age <dbl> 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, …
$ sound <chr> "cockadoodledoo", "meow", "woof woof", "cockadoodledoo…
$ kids_produce <dbl> 1, 0, 3, 0, 2, 2, 0, 5, 4, 0, 5, 12, 0, 12, 28, 9, 125…
$ kids_understand <dbl> 3, 10, 12, 2, 21, 22, 9, 41, 40, 4, 36, 32, 16, 59, 59…
$ kids_respond <dbl> 35, 35, 35, 91, 93, 93, 139, 145, 143, 94, 94, 94, 141…
$ prop_produce <dbl> 0.02857143, 0.00000000, 0.08571429, 0.00000000, 0.0215…
$ prop_understand <dbl> 0.08571429, 0.28571429, 0.34285714, 0.02197802, 0.2258…
Note that the unit of observation here is
one-row-per-age-group/animal sound.
Variables you need for this lab:
age
: child age in months
sound
: a string describing a type of animal sound
kids_produce
: the number of parents who answered “yes,
my child produces this animal sound” (note that if the child produces a
sound it is assumed that they understand it as well)
kids_respond
: the number of parents who responded to
this question at all
prop_produce
: the proportion of kids whose parents
endorsed that their child produces this animal sound, out of all
questionnaires administered (i.e.,
kids_produce / kids_respond
)
Other variables in this dataset:
kids_understand
: the number of parents who answered
“yes, my child understands what this animal sound means” (note that a
child can understand the sound but not produce it)
prop_understand
: the proportion of kids whose parents
endorsed that their child understands this animal sound, out of all
questionnaires administered (i.e.,
kids_understand / kids_respond
)
Know your data
Challenge #1:
How many variables?
- Which variables are continuous?
- Which ones are categorical or ordinal?
How many total kids do we have data for?
How many ages (in months)?
How many types of animal sounds? What are they?
Let’s start just by getting a feel for how many kids produce each
kind of sound, across the full age range. We could make a table:
sounds %>%
group_by(sound) %>%
summarize(total_produce = sum(kids_produce)) %>%
knitr::kable()
cockadoodledoo |
148 |
meow |
681 |
woof woof |
940 |
Or we could make a simple bar plot:
ggplot(sounds, aes(x = sound, y = kids_produce)) +
geom_col() +
labs(x = "Sound", y = "Total Children Producing")
For this kind of plot, we don’t really need color. What if we want to
see how the number of kids who produce each sound varies by age? We’ll
change the x-axis to age and instead facet_wrap
by
sound
, and make the y-axis a proportion instead of
counts.
ggplot(sounds, aes(x = age, y = prop_produce)) +
geom_col() +
labs(x = "Age (mos)", y = "Proportion of Children Producing") +
facet_wrap(~sound)
The bar geom makes this a little hard to read and compare across
facets though. Let’s try points instead.
ggplot(sounds, aes(x = age, y = prop_produce)) +
geom_point() +
labs(x = "Age (mos)", y = "Proportion of Children Producing") +
facet_wrap(~sound)
That is a little better! Facets allow us to parse the relationship
between two quantitative variables (here, age and proportion of kids
producing) by a qualitative variable (here, type of sound). Another way
we could do this, instead of faceting, is to use color. This would make
it easier to compare proportions at each age.
Discrete colors
Let’s start with a base plot with age (in months) along the x-axis
and the proportion of children producing each word along the y-axis,
using points as the geometric object. Set the size of the points to 2
and change the x- and y-axis labels to “Age (months)” and “Proportion of
Children Producing”, respectively.
ggplot(sounds, aes(x = age, y = prop_produce)) +
geom_point(size = 2) +
labs(x = "Age (months)", y = "Proportion of Children Producing")
Default discrete
palette
Challenge #2:
Take the plot we just made, and edit the code to map the color of the
points to the type of sound produced at the geom level. The
colors that show up are the default discrete palette in
ggplot2
.
ggplot(sounds, aes(x = age, y = prop_produce)) +
geom_point(aes(color = sound), size = 2) +
labs(x = "Age (months)", y = "Proportion of Children Producing")
Challenge #3:
Try adding geom_line()
to this plot to connect the dots.
Does this look right? Use ?geom_line
to figure out how this
geom connects the dots by default, and which aesthetic can be used to
connect cases together. Try editing your code to draw 3 black lines- one
for each sound.
# Does this look right? no!
ggplot(sounds, aes(x = age, y = prop_produce)) +
geom_line() +
geom_point(aes(color = sound), size = 2) +
labs(x = "Age (months)", y = "Proportion of Children Producing")
# A possible solution
ggplot(sounds, aes(x = age, y = prop_produce)) +
geom_line(aes(group = sound)) +
geom_point(aes(color = sound), size = 2) +
labs(x = "Age (months)", y = "Proportion of Children Producing")
Challenge #4:
Make two plots:
Recreate the plot above, but this time map color to the type of
sound produced for both the point and line geoms. Pay attention to the
order of the layers you are adding- you may wish to place
geom_line
before geom_point
so the
lines are always “painted” underneath the points.
Instead of geom_line
, add a loess line using
geom_smooth
. Use ?geom_smooth
to figure out
how to get rid of the grey standard error ribbon. You may also want to
increase the line width.
# Does this look right? yes!
ggplot(sounds, aes(x = age, y = prop_produce, color = sound)) +
geom_line() +
geom_point(size = 2) +
labs(x = "Age (months)", y = "Proportion of Children Producing")
ggplot(sounds, aes(x = age,
y = prop_produce,
color = sound)) +
geom_smooth(se = FALSE, lwd = .5) +
geom_point(size = 2) +
labs(x = "Age (months)", y = "Proportion of Children Producing")
Why does this work? To tell geom_line
how to connect
your dots, you can either:
- Map the
group
aesthetic (so
aes(group = sound)
), or
- Map the
color
aesthetic globally
(aes(color = sound)
.
Because geom_line
understands the color
aesthetic, it will try to draw separate lines for each color. Here that
translates to three lines, one for each sound, which is what we
want!
Brief aside:
factors
At this point, our plot is looking pretty good. But you may have
noticed that the legend order doesn’t match the order of the lines in
the plot. Question: why is this an issue?
What determines the order of levels in the legend? The order of
levels in the underlying factor:
levels(as.factor(sounds$sound))
[1] "cockadoodledoo" "meow" "woof woof"
In this case, since we haven’t set them, R will pick an order for
us.
We could manually re-order the levels of the factor, but
different plots might necessitate different factor ordering, and if we
have more than two or three levels, typing them repeatedly gets tedious
fast. Instead, let’s have R do it!
The forcats
package, is for
cat
egorical variables and
has lots of useful functions, including some for re-ordering levels.
There are lots of functions in forcats
, and you can install
& load it separately, although forcats
is loaded with
the tidyverse
.
install.packages("forcats")
library(forcats)
We’ll use the fct_reorder2
function, which by default
will re-order the levels of a factor based on the order of occurrence of
one variable (y
in the docs) when the dataframe is
sorted by another variable (x
in the docs):
# "Sort the dataframe by age, find the last occurrence of each level of sounds$sound in order of prop_produce
fct_reorder2(
as.factor(sounds$sound),
sounds$age, # variable "x"
sounds$prop_produce # varible "y"
) %>% levels
[1] "woof woof" "meow" "cockadoodledoo"
This (somehwat convoluted) procedure is very useful for when you have
a line chart of two quantitative variables, colored by a factor
variable. Let’s see the difference:
sounds <- sounds %>%
mutate(sound = as.factor(sound))
sound_traj <- ggplot(sounds, aes(x = age,
y = prop_produce,
color = fct_reorder2(sound, age, prop_produce))) +
geom_smooth(se = FALSE, lwd = .5) +
geom_point(size = 2) +
labs(x = "Age (months)",
y = "Proportion of Children Producing",
color = "sound")
sound_traj
MUCH BETTER! Save your plot object as sound_traj
. Now we
can start playing with the actual colors.
Set luminance and
saturation (chromaticity)
The default qualitative palette works fine here. The addition of scale_color_hue
changes nothing.
sound_traj +
scale_color_hue()
We can also change these settings within the default color palette,
where the arguments are:
h
= range of hues to use, in [0, 360]
l
= luminance (lightness)
c
= chroma (intensity of color)
# Change hue (l and c are defaults)
sound_traj +
scale_color_hue(h = c(0, 90), l = 65, c = 100)
# Use luminance=45, instead of default 65
sound_traj +
scale_color_hue(l = 45)
# Reduce saturation (chroma) from 100 to 50, and increase luminance
sound_traj +
scale_color_hue(l = 75, c = 50)
Set discrete
colors
We can change the actual colors used by adding the layer
scale_color_manual
or scale_fill_manual
.
Confusion between which to use when is often the cause of much
frustration!
To name more than one color, which you often want to do, use
c()
. In the parentheses, named colors and hex colors are
always in quotes.
sound_traj +
scale_color_manual(values = c("cornflowerblue",
"seagreen", "coral"))
There are many named
colors available in R!
Challenge #5:
View the code blocks below. Copy and paste the code to run them in
your own file. Why do neither of the following code blocks change the
colors of the points and lines? Use your words :) (the answer is
below the challenge, but try to trouble-shoot on your own
first)
ggplot(sounds, aes(x = age,
y = prop_produce,
color = fct_reorder2(sound, age, prop_produce))) +
geom_smooth(se = FALSE, lwd = .5) +
geom_point(size = 2) +
labs(x = "Age (months)",
y = "Proportion of Children Producing",
color = "sound") +
scale_fill_manual(values = c("cornflowerblue",
"seagreen", "coral"))
ggplot(sounds, aes(x = age,
y = prop_produce,
fill = fct_reorder2(sound, age, prop_produce))) +
geom_smooth(se = FALSE, lwd = .5) +
geom_point(size = 2) +
labs(x = "Age (months)",
y = "Proportion of Children Producing",
fill = "sound") +
scale_fill_manual(values = c("cornflowerblue",
"seagreen", "coral"))
Answers:
- In the first, we used
scale_fill_manual
, but the in the
global aesthetics, we mapped the color
, not
fill
, aesthetic onto the sound
variable.
- In the second, we did define the
fill
aesthetic and
used scale_fill_manual
, so that is good. But
geom_line
only understands the color
aesthetic, not fill
. And for geom_point
, the
default shape for is 19, which does not understand the fill
aesthetic.
Challenge #6:
Start with this plot:
sound_traj
Add a black outline to the points, and color the inside of the points
and the lines by sound
using the default discrete color
palette. You may also wish to edit the legends on this plot:
geom_smooth
has an argument called
show.legend = FALSE
. See if you prefer the plot with this
change.
If this was easy, try applying the same custom color palette to the
inside of the points and to the lines.
ggplot(sounds, aes(x = age,
y = prop_produce,
fill = fct_reorder2(sound, age, prop_produce))) +
geom_smooth(aes(color = fct_reorder2(sound, age, prop_produce)),
se = FALSE, lwd = .5, show.legend = FALSE) +
geom_point(size = 2, shape = 21) +
labs(x = "Age (months)",
y = "Proportion of Children Producing",
fill = "sound")
ggplot(sounds, aes(x = age,
y = prop_produce,
fill = fct_reorder2(sound, age, prop_produce))) +
geom_smooth(aes(color = fct_reorder2(sound, age, prop_produce)),
se = FALSE, lwd = .5, show.legend = FALSE) +
geom_point(size = 2, shape = 21) +
labs(x = "Age (months)",
y = "Proportion of Children Producing",
fill = "sound") +
scale_fill_manual(values = c("cornflowerblue",
"seagreen", "coral")) +
scale_color_manual(values = c("cornflowerblue",
"seagreen", "coral"))
You can also define your color palette as a vector outside of
ggplot2
. Below, I made an object called
my_colors
outside of ggplot2
. To use it, we
call that object within the scale_colour_manual
function.
my_colors <- c("cadetblue", "steelblue", "salmon") # quote color names
sound_traj +
scale_color_manual(values = my_colors) # note: not in quotes
Challenge #7:
Define a custom color palette using hexadecimal colors (#rrggbb), and
apply it using scale_color_manual
to your
sound_traj
plot. Some basic ones are here:
https://sashat.me/2017/01/11/list-of-20-simple-distinct-colors/
Parse the hexadecimal string like so: #rrggbb, where rr, gg, and bb
refer to color intensity in the red, green, and blue channels,
respectively.
# from https://github.com/mwaskom/seaborn/blob/master/seaborn/palettes.py
sb_colorblind <- c("#0072B2", "#009E73", "#D55E00",
"#CC79A7", "#F0E442", "#56B4E9")
sound_traj +
scale_colour_manual(values = sb_colorblind)
Built-in discrete
palettes
Colorbrewer
As we discussed on Monday, Colorbrewer is a useful tool for
designing color palettes, which can be used directly in R.
To use Colorbrewer palettes, you’ll need to install the
RColorBrewer
package from CRAN. This chunk of code tells
you how:
install.packages("RColorBrewer")
library(RColorBrewer)
Colorbrewer has a few named (i.e., pre-set) qualitative
palettes: Accent, Dark2, Paired, Pastel1, Pastel2, Set1, Set2, Set3.
Here is how to view them:
brewer.pal(5, "Dark2") # list 5 hex colors
[1] "#1B9E77" "#D95F02" "#7570B3" "#E7298A" "#66A61E"
display.brewer.pal(5, "Dark2") # view 5 hex colors
And here is how you use them:
sound_traj +
scale_color_brewer(palette = "Dark2")
Viridis
palettes
ggplot
comes built-in with the “Viridis” color palette,
the point of which is to be a set of colors that “are pretty, better
represent your data, easier to read by those with colorblindness, and
print well in grey scale.”
Read more here in the viridis
vignette. Note that this vignette is for an R package that is
generally no longer needed with recent versions of ggplot- the Viridis
color palette didn’t used to be part of ggplot
by default,
but it does now! 🎉 There are Viridis four colormap options
available:
- “magma” (or “A”),
- “inferno” (or “B”),
- “plasma” (or “C”),
- “viridis” (or “D”, the default option).
sound_traj +
scale_color_viridis_d() +
theme_minimal()
sound_traj +
scale_color_viridis_d(option = "plasma") +
theme_minimal()
Challenge #8:
Use the Viridis palettes to color the points by and the lines by
sound
; make the outline of the points “midnightblue”. Pick
any colormap option, and play with theme_bw
or
theme_minimal
to see what you like.
ggplot(sounds, aes(x = age,
y = prop_produce,
fill = fct_reorder2(sound, age, prop_produce))) +
geom_smooth(aes(color = fct_reorder2(sound, age, prop_produce)),
se = FALSE, lwd = .5, show.legend = FALSE) +
geom_point(size = 2, shape = 21, colour = "midnightblue") +
labs(x = "Age (months)",
y = "Proportion of Children Producing",
fill = "sound") +
scale_fill_viridis_d() +
scale_color_viridis_d() +
theme_minimal()
A note: the default Viridis discrete palette ends up in a pretty
gnarly yellow color that I personally feel like is not ideal for all
situations. In practice, I have been known to artificially clamp the
range of the Viridis discrete palette to avoid that last color.
Color Palettes from
Packages
As with everything else in R, there are numerous homebrew
packages with different color palettes. Here, we will meet a few of my
favs.
Wes Anderson
palettes
My favorite! To use Wes Anderson palettes, you’ll need to install the
wesanderson
package from CRAN. This chunk of code tells you
how:
install.packages("wesanderson")
library(wesanderson)
names(wes_palettes) # all the palette names
[1] "BottleRocket1" "BottleRocket2" "Rushmore1"
[4] "Rushmore" "Royal1" "Royal2"
[7] "Zissou1" "Zissou1Continuous" "Darjeeling1"
[10] "Darjeeling2" "Chevalier1" "FantasticFox1"
[13] "Moonrise1" "Moonrise2" "Moonrise3"
[16] "Cavalcanti1" "GrandBudapest1" "GrandBudapest2"
[19] "IsleofDogs1" "IsleofDogs2" "FrenchDispatch"
[22] "AsteroidCity1" "AsteroidCity2" "AsteroidCity3"
wes_palette("GrandBudapest2") # view named palette
wes_palette("GrandBudapest2")[1:4] # list first 4 hex colors
[1] "#E6A0C4" "#C6CDF7" "#D8A499" "#7294D4"
wes_palette("GrandBudapest2")[c(1,4)] # list colors 1 and 4
[1] "#E6A0C4" "#7294D4"
To use these palettes, use scale_color_manual
where
values
is set to wes_palette("name")
. For
example:
sound_traj +
scale_color_manual(values = wes_palette("Darjeeling1"))
sound_traj +
scale_color_manual(values = wes_palette("FantasticFox1"))
Challenge #8:
What if you just don’t want to use the colors in the order they are
in? Use a wes_palette
of your choice. Using our code from
above, try picking the last 3 colors of a palette. Add it to your
sound_traj
plot.
If this was easy, try using colors 2, 3, and 5 instead.
sound_traj +
scale_color_manual(values = wes_palette("Darjeeling1")[3:5])
sound_traj +
scale_color_manual(values = wes_palette("FantasticFox1")[c(2, 3, 5)])
In The Wild
I recently used one of the Wes Anderson palettes (“AsteroidCity1”) in
one of my own figures!
Asteroid City In The Wild
Also great: Studio
Ghibli!
Another fun choice: The Studio Ghibli color
palettes!
sound_traj +
scale_colour_ghibli_d("YesterdayMedium", direction = -1)
Why do we need that direction = -1
business? To answer,
let’s try running this with the default color ordering:
sound_traj +
scale_colour_ghibli_d("YesterdayMedium", direction = 1)
Those all look indistinguishable; why is this happening? Let’s look
at the colors in the palette:
ghibli_palette("YesterdayMedium")
We see that the first few colors are very dark; on a grey background,
with small lines, those colors look very similar to one another. So
similar that, at first, it looks like they are all the same! To prove
that we are actually pulling in those colors, let’s artificially (and
temporarily!) increase our point size:
sound_traj +
geom_point(size = 20) +
scale_colour_ghibli_d("YesterdayMedium", direction = 1)
We can see that indeed, the colors from the palette are being used,
but that they are too similar to be a good choice for this plot. Hence,
swapping the normal order of the colors using the direction
argument.
ggthemes
palettes
To use these palettes, you’ll need to install the
ggthemes
package from CRAN. This chunk of code tells you
how:
install.packages("ggthemes")
library(ggthemes)
sound_traj +
scale_color_fivethirtyeight()
sound_traj +
scale_color_economist()
ggsci
Palettes
ggsci
provides
color palettes designed to match with the aesthetics of a wide variety
of scientific publishers:
library(ggsci)
sound_traj + scale_color_nejm()
Palettes from the
Queen Bee
To use Beyonce
palettes, you’ll need to install the beyonce
package
from GitHub using devtools::install_github()
. This chunk of
code tells you how:
# install.packages("devtools")
# devtools::install_github("dill/beyonce")
library(beyonce)
Note that a number of students had installation problems with this
package! Move on if you do.
beyonce_palette(18)
sound_traj +
scale_color_manual(values = beyonce_palette(18)[3:5])
Here we’ll only use the first, fourth, and fifth colors in the
palette.
sound_traj +
scale_color_manual(values = beyonce_palette(18)[c(1, 4, 5)])
Going Greyscale for
Discrete
Use scale_color_grey
or scale_fill_grey
, or
sometimes both depending on your geoms and the aesthetics they
understand.
sound_traj +
scale_color_grey() +
theme_minimal()
Set start and end
sound_traj +
scale_color_grey(start = 0.2, end = .8)
Make the same plot but make points outlined in black
ggplot(sounds, aes(x = age,
y = prop_produce,
fill = fct_reorder2(sound, age, prop_produce))) +
geom_smooth(aes(color = fct_reorder2(sound, age, prop_produce)),
se = FALSE, lwd = .5, show.legend = FALSE) +
geom_point(size = 2, shape = 21) +
labs(x = "Age (months)",
y = "Proportion of Children Producing",
fill = "sound") +
scale_fill_grey(start = 0.3, end = 1) +
scale_color_grey(start = 0.3, end = 1)
Suggest redundancy in greyscale- try changing line type instead of
line (or in addition to) line color.
Change line type by sound
, set color to black.
ggplot(sounds, aes(x = age,
y = prop_produce,
fill = fct_reorder2(sound, age, prop_produce))) +
geom_smooth(aes(lty = fct_reorder2(sound, age, prop_produce)), color = "black",
se = FALSE, lwd = .5, show.legend = FALSE) +
geom_point(size = 2, shape = 21) +
labs(x = "Age (months)",
y = "Proportion of Children Producing",
fill = "sound") +
scale_fill_grey(start = 0.3, end = 1)
Change both!
ggplot(sounds, aes(x = age,
y = prop_produce,
fill = fct_reorder2(sound, age, prop_produce))) +
geom_smooth(aes(color = fct_reorder2(sound, age, prop_produce),
lty = fct_reorder2(sound, age, prop_produce)),
se = FALSE, lwd = .5, show.legend = FALSE) +
geom_point(size = 2, shape = 21) +
labs(x = "Age (months)",
y = "Proportion of Children Producing",
fill = "sound") +
scale_fill_grey(start = 0.3, end = .8) +
scale_color_grey(start = 0.3, end = .8)
Colorblind-friendly
palettes
The colorblindr
package can be used to “simulate colorblindness in production-ready
R figures.” To use this package, you’ll need to first install the
cowplot
package from GitHub using
devtools::install_github()
. You’ll also need to install the
colorspace
package from CRAN. Finally, you can then use
devtools::install_github()
again to install the
colorblindr
package. This code chunk shows you how to do
all 3 installs to use the colorblindr
package:
devtools::install_github("wilkelab/cowplot")
install.packages("colorspace", repos = "http://R-Forge.R-project.org")
devtools::install_github("clauswilke/colorblindr")
To use:
# save a ggplot object
my_sound_traj <- sound_traj +
scale_color_manual(values = beyonce_palette(18)[c(1, 4, 5)])
View that figure after color-vision-deficiency simulation:
library(colorblindr)
cvd_grid(my_sound_traj)
You can also use the colorblind-friendly palette in this package
using scale_color_OkabeIto
and
scale_fill_OkabeIto
:
cb_sound_traj <- sound_traj +
scale_color_OkabeIto()
cb_sound_traj
cvd_grid(cb_sound_traj)
You can still use this colorblind-friendly palette without the
colorblindr
package though. Here are the colors!
The Cookbook
for R provided the matching hex colors too to make life easier:
cbbPalette <- c("#000000", "#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7")
# To use for line and point colors, add
sound_traj +
scale_colour_manual(values = cbbPalette[c(3, 7, 8)])
Labels
When working with colors, the default pattern, enforced by
ggplot
, is to use a figure legend to indicate which color
goes with which level of a factor. However, this is not always the best
way to go! Another option is to directly label your data within the plot
itself. When should you consider this option?
- When you have a very small number of levels
- When the design of the rest of the plot is amenable: there aren’t
too many other annotations or visual elements taking up space.
There are several ways to do this, but a good place to start is
ggrepel
:
library(ggrepel)
sounds <- sounds %>%
mutate(label = case_when(
age == max(age) ~ sound))
ggplot(sounds, aes(x = age,
y = prop_produce,
color = fct_reorder2(sound, age, prop_produce))) +
geom_smooth(se = FALSE, lwd = .5) +
geom_point(size = 2) +
labs(x = "Age (months)",
y = "Proportion of Children Producing") +
geom_text_repel(aes(label = label),
nudge_x = 1,
direction = "y",
na.rm = TRUE) +
guides(color = FALSE)
Continuous colors
N.B. All of the example plots below are great examples of how
not to use continuous colors. I’m showing these so you
can see how to work with continuous color palettes, and to make this
topic flow easier for you I’m sticking with original dataset.
Default continuous
palette
Let’s map color to a continuous variable. For this, we are returning
to geom_line
instead of geom_smooth
, because
the latter doesn’t respond to continuous color palettes.
sound_by_age <- ggplot(sounds, aes(x = age,
y = prop_produce,
color = age)) +
geom_line(aes(group = sound), lwd = .5) +
geom_point(size = 2) +
labs(x = "Age (months)",
y = "Proportion of Children Producing")
sound_by_age
Color choice with
continuous variables
With discrete colors, we used either scale_color_manual
or scale_fill_manual
(and sometimes both were needed!). For
continuous colors, we use either scale_color_gradient
or
scale_fill_gradient
.
sound_by_age +
scale_color_gradient()
You can reverse the gradient scale…
sound_by_age +
scale_color_gradient(trans = "reverse")
sound_by_age +
scale_color_gradient(low = "white", high = "red")
We can make this same plot using a custom greyscale gradient.
sound_by_age +
scale_color_gradient(low = "grey90", high = "black")
So scale_color_gradient
gives you a sequential gradient,
but you may want a diverging color scheme instead. For that, you can use
scale_color_gradient2
# Diverging color scheme
med_age <- sounds %>%
summarize(mos = median(age)) %>%
pull()
sound_by_age +
scale_color_gradient2(midpoint = med_age,
low="blue", mid="white", high="red" )
Built-in continuous
palettes
Use
RColorBrewer
Again, to use you need to install and load the
RColorBrewer
palette.
library(RColorBrewer)
Then use scale_color_gradientn
.
sound_by_age +
scale_color_gradientn(colours = brewer.pal(n=5, name="PuBuGn"))
Reverse the colors…
sound_by_age +
scale_color_gradientn(colours = rev(brewer.pal(n=5, name="PuBuGn")))
Viridis
Note! For discrete == FALSE (the default) all other arguments are as
to scale_fill_gradientn
or
scale_color_gradientn
. (Also note that
_gradient_n_
is not a typo- the n versions of
those functions allow multi-color gradients).
sound_by_age +
scale_color_viridis_c()
sound_by_age +
scale_color_viridis_c(option = "magma")
Read the help function for ?scale_color_viridis_c
. As
before, we can use the direction
parameter to reverse the
order of the colors, or, atlernatively, we can use the
begin
and end
parameters to accomplish the
same thing. Using the “inferno” palette in reverse:
sound_by_age +
scale_color_viridis_c(option = "inferno", begin = 1, end = 0)
This begin
/end
trick is also how we can
adjust the color scale to not get quite so… yellow at the very
end>
sound_by_age +
scale_color_viridis_c(option = "inferno", begin = 0, end = 0.9) # if we don't want it to go all the way to 1.0...
LS0tCnRpdGxlOiAiTGFiIDAzOiBDb2xvcnMgd2l0aCBBbmltYWwgU291bmRzIgpzdWJ0aXRsZTogIkJNSSA1LzYyNSIKYXV0aG9yOiAiQWxpc29uIEhpbGwgdy8gdHdlYWtzIGJ5IFN0ZXZlbiBCZWRyaWNrIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRoZW1lOiBmbGF0bHkKICAgIHRvYzogVFJVRQogICAgdG9jX2Zsb2F0OiBUUlVFCiAgICB0b2NfZGVwdGg6IDIKICAgIG51bWJlcl9zZWN0aW9uczogVFJVRQogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQpgYGB7ciBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFLCBjYWNoZSA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZXJyb3IgPSBUUlVFLCBjb21tZW50ID0gTkEsIHdhcm5pbmcgPSBGQUxTRSwgZXJyb3JzID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgdGlkeSA9IEZBTFNFLCBjYWNoZSA9IEZBTFNFKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeSh3ZXNhbmRlcnNvbikKbGlicmFyeShnaGlibGkpCmxpYnJhcnkoZ2d0aGVtZXMpCmxpYnJhcnkoYmV5b25jZSkKIyBsaWJyYXJ5KHZpcmlkaXMpCmBgYAoKIyBPdmVydmlldwoKVGhlcmUgYXJlIDEwIGNoYWxsZW5nZXMgdG90YWwtIG5vbmUgYXJlIGluIHRoZSAiY29udGludW91cyBjb2xvcnMiIHNlY3Rpb24sIGJ1dCB5b3UgY2FuIHVzZSB0aGF0IHNlY3Rpb24gdG8gY29tcGxldGUgdGhlIHRlbnRoIGNoYWxsZW5nZSBvbiB5b3VyIG93bi4gVXBsb2FkIHlvdXIga25pdHRlZCBodG1sIGRvY3VtZW50IGJ5IG5leHQgV2VkbmVzZGF5IHRvIFNha2FpIQoKTm90ZSB0aGF0IHRoaXMgbGFiIGRlcGVuZHMgb24gX21hbnlfIHBhY2thZ2VzOyBvbiB0aGUgUG9zaXQgQ2xvdWQgcHJvamVjdCBmb3IgdGhlIGxhYiBkZWxpdmVyYWJsZSwgSSBoYXZlIHByZS1pbnN0YWxsZWQgdGhlbSBhbGwgKEkgdGhpbmspLiBXZSd2ZSBsZWZ0IHRoZSBpbnN0YWxsYXRpb24gaW5zdHJ1Y3Rpb25zIGhlcmUgaW4gdGhlIGxhYiBkb2N1bWVudCBmb3IgZGVtb25zdHJhdGlvbiBwdXJwb3Nlcy4KCiMgU2xpZGVzIGZvciB0b2RheQoKYGBge3J9CmtuaXRyOjppbmNsdWRlX3VybCgic2xpZGVzLzAzLXNsaWRlcy5odG1sIikKYGBgCgojIFBhY2thZ2VzCgpPdGhlciBwYWNrYWdlcyB3aWxsIGJlIG5lZWRlZCB0byBiZSBpbnN0YWxsZWQgYXMgeW91IGdvLSByZXZlYWwgdGhlIGZpcnN0IGNvZGUgY2h1bmtzIHdoZW4gaW4gZG91YnQhCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKIyBSZWFkIGluIHRoZSBkYXRhCgpgYGB7cn0Kc291bmRzIDwtIHJlYWRfY3N2KGhlcmU6OmhlcmUoImRhdGEiLCAiYW5pbWFsX3NvdW5kc19zdW1tYXJ5LmNzdiIpKQpgYGAKCiMgQ29sb3VyIHZzIGZpbGwgYWVzdGhldGljCgpGaWxsIGFuZCBjb2xvdXIgc2NhbGVzIGluIGdncGxvdDIgY2FuIHVzZSB0aGUgc2FtZSBwYWxldHRlcy4gU29tZSBzaGFwZXMgc3VjaCBhcyBsaW5lcyBvbmx5IGFjY2VwdCB0aGUgYGNvbG91cmAgYWVzdGhldGljLCB3aGlsZSBvdGhlcnMsIHN1Y2ggYXMgcG9seWdvbnMsIGFjY2VwdCBib3RoIGBjb2xvdXJgIGFuZCBgZmlsbGAgYWVzdGhldGljcy4gSW4gdGhlIGxhdHRlciBjYXNlLCB0aGUgYGNvbG91cmAgcmVmZXJzIHRvIHRoZSBib3JkZXIgb2YgdGhlIHNoYXBlLCBhbmQgdGhlIGBmaWxsYCB0byB0aGUgaW50ZXJpb3IuCgoKCmBgYHtyIGVjaG8gPSBGQUxTRX0KIyMgQSBsb29rIGF0IGFsbCAyNSBzeW1ib2xzCmRmIDwtIGRhdGEuZnJhbWUoeCA9IDE6NSwgCiAgICAgICAgICAgICAgICAgIHkgPSByZXAocmV2KHNlcSgwLCAyNCwgYnkgPSA1KSksIGVhY2ggPSA1KSwgCiAgICAgICAgICAgICAgICAgIHogPSAxOjI1KQpzIDwtIGdncGxvdChkZiwgYWVzKHggPSB4LCB5ID0geSkpICsgCiAgc2NhbGVfc2hhcGVfaWRlbnRpdHkoKSArIAogIGdlb21fdGV4dChhZXMobGFiZWwgPSB6LCB5ID0geSAtIDEpKSArIAogIHRoZW1lX3ZvaWQoKQpzICsgZ2VvbV9wb2ludChhZXMoc2hhcGUgPSB6KSwgc2l6ZSA9IDQpIApgYGAKCi0tLQoKQWxsIHN5bWJvbHMgaGF2ZSBhIGZvcmVncm91bmQgY29sb3VyLCBzbyBpZiB3ZSBhZGQgYGNvbG9yID0gIm5hdnkiYCwgdGhleSBhbGwgYXJlIGFmZmVjdGVkLgoKYGBge3J9CnMgKyBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IHopLCBzaXplID0gNCwgY29sb3VyID0gIm5hdnkiKSAKYGBgCgotLS0KCldoaWxlIGFsbCBzeW1ib2xzIGhhdmUgYSBmb3JlZ3JvdW5kIGNvbG91ciwgc3ltYm9scyAyMS0yNSBhbHNvIHRha2UgYSBiYWNrZ3JvdW5kIGNvbG91ciAoZmlsbCkuIFNvIGlmIHdlIGFkZCBgZmlsbCA9ICJvcmNoaWQiYCwgb25seSB0aGUgbGFzdCByb3cgb2Ygc3ltYm9scyBhcmUgYWZmZWN0ZWQuCgpgYGB7cn0KcyArIGdlb21fcG9pbnQoYWVzKHNoYXBlID0geiksIHNpemUgPSA0LCBjb2xvdXIgPSAibmF2eSIsIGZpbGwgPSAib3JjaGlkIikgCmBgYAoKVGhpcyBpcyB3aHkgaXQgaXMgc28gY29tbW9uIHRvIGhhdmUgaXNzdWVzIHdpdGggYGNvbG9yYCBhbmQgYGZpbGxgIGFuZCBgZ2VvbV9wb2ludCgpYCwgYnkgdGhlIHdheSEKCiMgRGF0YQoKRm9yIHRoZSByZXN0IG9mIHRvZGF5LCB3ZSdsbCBwbGF5IHdpdGggdGhlIGBzb3VuZHNgIGRhdGFzZXQuIFRoaXMgZGF0YSB3YXMgZGVyaXZlZCBmcm9tIHRoZSBSIHBhY2thZ2UgW2B3b3JkYmFua3JgXShodHRwOi8vbGFuZ2NvZy5naXRodWIuaW8vd29yZGJhbmtyLyksIGFuIFIgaW50ZXJmYWNlIHRvIGFjY2VzcyBbV29yZGJhbmtdKGh0dHA6Ly93b3JkYmFuay5zdGFuZm9yZC5lZHUpLSBhbiBvcGVuIHNvdXJjZSBkYXRhYmFzZSBvZiBjaGlsZHJlbidzIHZvY2FidWxhcnkgZGV2ZWxvcG1lbnQuIFRoZSB0b29sIHVzZWQgdG8gbWVhc3VyZSBjaGlsZHJlbidzIGxhbmd1YWdlIGFuZCBjb21tdW5pY2F0aXZlIGRldmVsb3BtZW50IGluIHRoaXMgZGF0YWJhc2UgaXMgdGhlIFtNYWNBcnRodXItQmF0ZXMgQ29tbXVuaWNhdGl2ZSBEZXZlbG9wbWVudCBJbnZlbnRvcmllcyAoTUItQ0RJKV0oaHR0cDovL21iLWNkaS5zdGFuZm9yZC5lZHUpLiBUaGUgTUQtQ0RJIGlzIGEgcGFyZW50LXJlcG9ydGVkIHF1ZXN0aW9ubmFpcmUuCgpIZXJlIGlzIGEgZ2xpbXBzZSBvZiB0aGUgZGF0YToKCmBgYHtyfQpnbGltcHNlKHNvdW5kcykKYGBgCgoKTm90ZSB0aGF0IHRoZSB1bml0IG9mIG9ic2VydmF0aW9uIGhlcmUgaXMgb25lLXJvdy1wZXItYWdlLWdyb3VwL2FuaW1hbCBzb3VuZC4KCgpWYXJpYWJsZXMgeW91IG5lZWQgZm9yIHRoaXMgbGFiOgoKLSBgYWdlYDogY2hpbGQgYWdlIGluIG1vbnRocwotIGBzb3VuZGA6IGEgc3RyaW5nIGRlc2NyaWJpbmcgYSB0eXBlIG9mIGFuaW1hbCBzb3VuZAotIGBraWRzX3Byb2R1Y2VgOiB0aGUgbnVtYmVyIG9mIHBhcmVudHMgd2hvIGFuc3dlcmVkICJ5ZXMsIG15IGNoaWxkIHByb2R1Y2VzIHRoaXMgYW5pbWFsIHNvdW5kIiAobm90ZSB0aGF0IGlmIHRoZSBjaGlsZCBwcm9kdWNlcyBhIHNvdW5kIGl0IGlzIGFzc3VtZWQgdGhhdCB0aGV5IHVuZGVyc3RhbmQgaXQgYXMgd2VsbCkKLSBga2lkc19yZXNwb25kYDogdGhlIG51bWJlciBvZiBwYXJlbnRzIHdobyByZXNwb25kZWQgdG8gdGhpcyBxdWVzdGlvbiBhdCBhbGwKLSBgcHJvcF9wcm9kdWNlYDogdGhlIHByb3BvcnRpb24gb2Yga2lkcyB3aG9zZSBwYXJlbnRzIGVuZG9yc2VkIHRoYXQgdGhlaXIgY2hpbGQgcHJvZHVjZXMgdGhpcyBhbmltYWwgc291bmQsIG91dCBvZiBhbGwgcXVlc3Rpb25uYWlyZXMgYWRtaW5pc3RlcmVkIChpLmUuLCBga2lkc19wcm9kdWNlIC8ga2lkc19yZXNwb25kYCkKCk90aGVyIHZhcmlhYmxlcyBpbiB0aGlzIGRhdGFzZXQ6CgotIGBraWRzX3VuZGVyc3RhbmRgOiB0aGUgbnVtYmVyIG9mIHBhcmVudHMgd2hvIGFuc3dlcmVkICJ5ZXMsIG15IGNoaWxkIHVuZGVyc3RhbmRzIHdoYXQgdGhpcyBhbmltYWwgc291bmQgbWVhbnMiIChub3RlIHRoYXQgYSBjaGlsZCBjYW4gdW5kZXJzdGFuZCB0aGUgc291bmQgYnV0IG5vdCBwcm9kdWNlIGl0KQotIGBwcm9wX3VuZGVyc3RhbmRgOiB0aGUgcHJvcG9ydGlvbiBvZiBraWRzIHdob3NlIHBhcmVudHMgZW5kb3JzZWQgdGhhdCB0aGVpciBjaGlsZCB1bmRlcnN0YW5kcyB0aGlzIGFuaW1hbCBzb3VuZCwgb3V0IG9mIGFsbCBxdWVzdGlvbm5haXJlcyBhZG1pbmlzdGVyZWQgKGkuZS4sIGBraWRzX3VuZGVyc3RhbmQgLyBraWRzX3Jlc3BvbmRgKQoKCgojIERpc2NyZXRlIHZzIGNvbnRpbnVvdXMgdmFyaWFibGVzCgo8ZGl2IGNsYXNzPSJwYW5lbCBwYW5lbC1wcmltYXJ5Ij4KICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj5SZWZyZXNoZXIgQ29udGVudDo8L2Rpdj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1ib2R5Ij4KRm9yIGEgcmVmcmVzaGVyIChhbmQgbW9yZSBkZXRhaWxlZCBkZWVwLWRpdmUpLCBjaGVjayBvdXQ6IFsiV0hBVCBJUyBUSEUgRElGRkVSRU5DRSBCRVRXRUVOIENBVEVHT1JJQ0FMLCBPUkRJTkFMIEFORCBOVU1FUklDQUwgVkFSSUFCTEVTPyJdKGh0dHBzOi8vc3RhdHMub2FyYy51Y2xhLmVkdS9vdGhlci9tdWx0LXBrZy93aGF0c3RhdC93aGF0LWlzLXRoZS1kaWZmZXJlbmNlLWJldHdlZW4tY2F0ZWdvcmljYWwtb3JkaW5hbC1hbmQtaW50ZXJ2YWwtdmFyaWFibGVzLykKPC9kaXY+CjwvZGl2PgoKSW4gb3JkZXIgdG8gdXNlIGNvbG9yIHdpdGggeW91ciBkYXRhLCBtb3N0IGltcG9ydGFudGx5LCB5b3UgbmVlZCB0byBrbm93IGlmIHlvdeKAmXJlIGRlYWxpbmcgd2l0aCBkaXNjcmV0ZSBvciBjb250aW51b3VzIHZhcmlhYmxlcy4gCgojIyBEaXNjcmV0ZSBjb2xvciBwYWxldHRlcwoKRGlzY3JldGUgY29sb3IgcGFsZXR0ZXMgd29yayBiZXN0IHdoZW4geW91IHdhbnQgdG8gY29sb3IgYnkgYSBxdWFsaXRhdGl2ZSB2YXJpYWJsZS4gUXVhbGl0YXRpdmUgdmFyaWFibGVzIHRlbmQgdG8gYmUgZWl0aGVyIGNhdGVnb3JpY2FsIG9yIG9yZGluYWwuIERpZmZlcmVudCB2YXJpYWJsZXMgY2FuIGJlIHF1YWxpdGF0aXZlIG9yIHF1YW50aXRhdGl2ZSBkZXBlbmRpbmcgb24gY29udGV4dC4gCgpJbiB0aGlzIGRhdGFzZXQsIGBzb3VuZGAgaXMgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSB3aXRoIDMgcG9zc2libGUgdmFsdWVzOgpgYGB7cn0Kc291bmRzICU+JSAKICBkaXN0aW5jdChzb3VuZCkgJT4lIAogIGtuaXRyOjprYWJsZSgpCmBgYAoKV2UgY291bGQgbWFwIGFyYml0cmFyeSBudW1iZXJzIG9udG8gZWFjaCBvZiB0aGVzZSBzb3VuZHMsIGxpa2UgMSwgMiwgYW5kIDMtIGJ1dCB0aGUgbnVtYmVycyBzdGlsbCB3b3VsZCBub3QgbWVhbiBhbnl0aGluZy4gVGhhdCBpcywgdGhlcmUgaXMgbm8gaW50cmluc2ljIG9yZGVyaW5nIHRvIHRoZXNlIGNhdGVnb3JpZXMuIEV4YW1wbGVzIG9mIGNvbW1vbiBwdXJlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBhcmUgcmFjZSBvciBldGhuaWNpdHksIGdlbmRlciwgaGFpciBjb2xvciwgZXllIGNvbG9yLCBldGMuIENvbG9yaW5nIGJ5IHNvdW5kIGlzIHVzZWQgYXMgYSB3YXkgdG8gKmRpc3Rpbmd1aXNoKiB0aGUgZGF0YSBmb3IgZGlmZmVyZW50IHNvdW5kcyBmcm9tIGVhY2ggb3RoZXIgKHJlYWQgbW9yZSBoZXJlOiBodHRwOi8vc2VyaWFsbWVudG9yLmNvbS9kYXRhdml6L2NvbG9yLWJhc2ljcy5odG1sI2NvbG9yLWFzLWEtdG9vbC10by1kaXN0aW5ndWlzaCkKCiMjIENvbnRpbnVvdXMgY29sb3IgcGFsZXR0ZXMKCkNvbnRpbnVvdXMgY29sb3IgcGFsZXR0ZXMgd29yayBiZXN0IHdoZW4geW91IHdhbnQgdG8gY29sb3IgYnkgYSBxdWFudGl0YXRpdmUgdmFyaWFibGUuIFF1YW50aXRhdGl2ZSB2YXJpYWJsZXMgdGVuZCB0byBiZSBlaXRoZXIgb3JkaW5hbCBvciBjb250aW51b3VzLiBJbiB0aGlzIGRhdGFzZXQsIGBhZ2VgIChpbiBtb250aHMpIGNhbiBvbmx5IHRha2Ugb24gYSBsaW1pdGVkIHNldCBvZiB2YWx1ZXM6CgpgYGB7cn0Kc291bmRzICU+JSAKICBkaXN0aW5jdChhZ2UpICU+JSAKICBwdWxsCmBgYAoKSG93ZXZlciwgaW4gdGhlIGZvbGxvd2luZyBwbG90cywgd2UnbGwgdHJlYXQgYWdlIGFzIGEgY29udGludW91cyB2YXJpYWJsZSBwbG90dGVkIGFjcm9zcyB0aGUgeC1heGlzLiBJbiBzb21lIGNvbnRleHRzLCB0aGlzIGtpbmQgb2YgdmFyaWFibGUgY291bGQgYmUgdHJlYXRlZCBhcyBhIG9yZGluYWwgdmFyaWFibGUuIEhvd2V2ZXIsIGZvciBjb2xvciBwdXJwb3NlcywgdGhpcyB3b3VsZCBub3QgaWRlYWwgaGVyZSBzaW5jZSB0aGVyZSBhcmUgMTEgImNhdGVnb3JpZXMiIChzZWUgaHR0cDovL3NlcmlhbG1lbnRvci5jb20vZGF0YXZpei9jb2xvci1waXRmYWxscy5odG1sKS4gQWdlIGhhcyBhIG5hdHVyYWwgYW5kIG1lYW5pbmdmdWwgb3JkZXI6IGEgY2hpbGQgd2hvIGlzIDkgbW9udGhzIG9sZCBpcyAxIG1vbnRoIG9sZGVyIHRoYW4gb25lIHdobyBpcyA4IG1vbnRocyBvbGQuIFNvLCB3ZSdsbCB1c2UgdGhhdCBuYXR1cmFsIG9yZGVyaW5nIHRvIG91ciBhZHZhbnRhZ2UgYW5kIG5vdCB1c2UgY29sb3IgdG8gcmVwcmVzZW50IGFnZSBhcyBhIHZhcmlhYmxlLiBXaGVuIHlvdSAqZG8qIGFwcGx5IGEgY29udGludW91cyBjb2xvciBwYWxldHRlLCB5b3UnbGwgd2FudCB0byB1c2UgY29sb3IgdG8geW91ciBhZHZhbnRhZ2UgdG8gW3JlcHJlc2VudCBkYXRhIHZhbHVlc10oaHR0cDovL3NlcmlhbG1lbnRvci5jb20vZGF0YXZpei9jb2xvci1iYXNpY3MuaHRtbCNjb2xvci10by1yZXByZXNlbnQtZGF0YS12YWx1ZXMpLgoKIyBLbm93IHlvdXIgZGF0YQoKPGRpdiBjbGFzcz0icGFuZWwgcGFuZWwtc3VjY2VzcyI+CiAgPGRpdiBjbGFzcz0icGFuZWwtaGVhZGluZyI+Q2hhbGxlbmdlICMxOjwvZGl2PgogIDxkaXYgY2xhc3M9InBhbmVsLWJvZHkiPgotIEhvdyBtYW55IHZhcmlhYmxlcz8KICAgIC0gV2hpY2ggdmFyaWFibGVzIGFyZSBjb250aW51b3VzPwogICAgLSBXaGljaCBvbmVzIGFyZSBjYXRlZ29yaWNhbCBvciBvcmRpbmFsPwotIEhvdyBtYW55IHRvdGFsIGtpZHMgZG8gd2UgaGF2ZSBkYXRhIGZvcj8KLSBIb3cgbWFueSBhZ2VzIChpbiBtb250aHMpPwogIC0gSG93IG1hbnkga2lkcyBwZXIgYWdlPwotIEhvdyBtYW55IHR5cGVzIG9mIGFuaW1hbCBzb3VuZHM/IFdoYXQgYXJlIHRoZXk/CiAgPC9kaXY+CjwvZGl2PgoKTGV0J3Mgc3RhcnQganVzdCBieSBnZXR0aW5nIGEgZmVlbCBmb3IgaG93IG1hbnkga2lkcyBwcm9kdWNlIGVhY2gga2luZCBvZiBzb3VuZCwgYWNyb3NzIHRoZSBmdWxsIGFnZSByYW5nZS4gV2UgY291bGQgbWFrZSBhIHRhYmxlOgoKYGBge3J9CnNvdW5kcyAlPiUgCiAgZ3JvdXBfYnkoc291bmQpICU+JSAKICBzdW1tYXJpemUodG90YWxfcHJvZHVjZSA9IHN1bShraWRzX3Byb2R1Y2UpKSAlPiUgCiAga25pdHI6OmthYmxlKCkKYGBgCgpPciB3ZSBjb3VsZCBtYWtlIGEgc2ltcGxlIGJhciBwbG90OgoKYGBge3J9CmdncGxvdChzb3VuZHMsIGFlcyh4ID0gc291bmQsIHkgPSBraWRzX3Byb2R1Y2UpKSArIAogIGdlb21fY29sKCkgKwogIGxhYnMoeCA9ICJTb3VuZCIsIHkgPSAiVG90YWwgQ2hpbGRyZW4gUHJvZHVjaW5nIikKYGBgCgpGb3IgdGhpcyBraW5kIG9mIHBsb3QsIHdlIGRvbid0IHJlYWxseSBuZWVkIGNvbG9yLiBXaGF0IGlmIHdlIHdhbnQgdG8gc2VlIGhvdyB0aGUgbnVtYmVyIG9mIGtpZHMgd2hvIHByb2R1Y2UgZWFjaCBzb3VuZCB2YXJpZXMgYnkgYWdlPyBXZSdsbCBjaGFuZ2UgdGhlIHgtYXhpcyB0byBhZ2UgYW5kIGluc3RlYWQgYGZhY2V0X3dyYXBgIGJ5IGBzb3VuZGAsIGFuZCBtYWtlIHRoZSB5LWF4aXMgYSBwcm9wb3J0aW9uIGluc3RlYWQgb2YgY291bnRzLgoKYGBge3J9CmdncGxvdChzb3VuZHMsIGFlcyh4ID0gYWdlLCB5ID0gcHJvcF9wcm9kdWNlKSkgKyAKICBnZW9tX2NvbCgpICsKICBsYWJzKHggPSAiQWdlIChtb3MpIiwgeSA9ICJQcm9wb3J0aW9uIG9mIENoaWxkcmVuIFByb2R1Y2luZyIpICsKICBmYWNldF93cmFwKH5zb3VuZCkKYGBgCgpUaGUgYmFyIGdlb20gbWFrZXMgdGhpcyBhIGxpdHRsZSBoYXJkIHRvIHJlYWQgYW5kIGNvbXBhcmUgYWNyb3NzIGZhY2V0cyB0aG91Z2guIExldCdzIHRyeSBwb2ludHMgaW5zdGVhZC4KCmBgYHtyfQpnZ3Bsb3Qoc291bmRzLCBhZXMoeCA9IGFnZSwgeSA9IHByb3BfcHJvZHVjZSkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBsYWJzKHggPSAiQWdlIChtb3MpIiwgeSA9ICJQcm9wb3J0aW9uIG9mIENoaWxkcmVuIFByb2R1Y2luZyIpICsKICBmYWNldF93cmFwKH5zb3VuZCkKYGBgCgpUaGF0IGlzIGEgbGl0dGxlIGJldHRlciEgRmFjZXRzIGFsbG93IHVzIHRvIHBhcnNlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0d28gcXVhbnRpdGF0aXZlIHZhcmlhYmxlcyAoaGVyZSwgYWdlIGFuZCBwcm9wb3J0aW9uIG9mIGtpZHMgcHJvZHVjaW5nKSBieSBhIHF1YWxpdGF0aXZlIHZhcmlhYmxlIChoZXJlLCB0eXBlIG9mIHNvdW5kKS4gQW5vdGhlciB3YXkgd2UgY291bGQgZG8gdGhpcywgaW5zdGVhZCBvZiBmYWNldGluZywgaXMgdG8gdXNlIGNvbG9yLiBUaGlzIHdvdWxkIG1ha2UgaXQgZWFzaWVyIHRvIGNvbXBhcmUgcHJvcG9ydGlvbnMgYXQgZWFjaCBhZ2UuCgoKCgojIERpc2NyZXRlIGNvbG9ycwoKTGV0J3Mgc3RhcnQgd2l0aCBhIGJhc2UgcGxvdCB3aXRoIGFnZSAoaW4gbW9udGhzKSBhbG9uZyB0aGUgeC1heGlzIGFuZCB0aGUgcHJvcG9ydGlvbiBvZiBjaGlsZHJlbiBwcm9kdWNpbmcgZWFjaCB3b3JkIGFsb25nIHRoZSB5LWF4aXMsIHVzaW5nIHBvaW50cyBhcyB0aGUgZ2VvbWV0cmljIG9iamVjdC4gU2V0IHRoZSBzaXplIG9mIHRoZSBwb2ludHMgdG8gMiBhbmQgY2hhbmdlIHRoZSB4LSBhbmQgeS1heGlzIGxhYmVscyB0byAiQWdlIChtb250aHMpIiBhbmQgIlByb3BvcnRpb24gb2YgQ2hpbGRyZW4gUHJvZHVjaW5nIiwgcmVzcGVjdGl2ZWx5LiAKCmBgYHtyfQpnZ3Bsb3Qoc291bmRzLCBhZXMoeCA9IGFnZSwgeSA9IHByb3BfcHJvZHVjZSkpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMikgKwogIGxhYnMoeCA9ICJBZ2UgKG1vbnRocykiLCB5ID0gIlByb3BvcnRpb24gb2YgQ2hpbGRyZW4gUHJvZHVjaW5nIikKYGBgCgoKCiMjIERlZmF1bHQgZGlzY3JldGUgcGFsZXR0ZQoKPGRpdiBjbGFzcz0icGFuZWwgcGFuZWwtc3VjY2VzcyI+CiAgPGRpdiBjbGFzcz0icGFuZWwtaGVhZGluZyI+Q2hhbGxlbmdlICMyOjwvZGl2PgogIDxkaXYgY2xhc3M9InBhbmVsLWJvZHkiPgpUYWtlIHRoZSBwbG90IHdlIGp1c3QgbWFkZSwgYW5kIGVkaXQgdGhlIGNvZGUgdG8gbWFwIHRoZSBjb2xvciBvZiB0aGUgcG9pbnRzIHRvIHRoZSB0eXBlIG9mIHNvdW5kIHByb2R1Y2VkICphdCB0aGUgZ2VvbSBsZXZlbCouIFRoZSBjb2xvcnMgdGhhdCBzaG93IHVwIGFyZSB0aGUgZGVmYXVsdCBkaXNjcmV0ZSBwYWxldHRlIGluIGBnZ3Bsb3QyYC4KCmBgYHtyfQpnZ3Bsb3Qoc291bmRzLCBhZXMoeCA9IGFnZSwgeSA9IHByb3BfcHJvZHVjZSkpICsgCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBzb3VuZCksIHNpemUgPSAyKSArCiAgbGFicyh4ID0gIkFnZSAobW9udGhzKSIsIHkgPSAiUHJvcG9ydGlvbiBvZiBDaGlsZHJlbiBQcm9kdWNpbmciKQpgYGAKICA8L2Rpdj4KPC9kaXY+CgoKPGRpdiBjbGFzcz0icGFuZWwgcGFuZWwtc3VjY2VzcyI+CiAgPGRpdiBjbGFzcz0icGFuZWwtaGVhZGluZyI+Q2hhbGxlbmdlICMzOjwvZGl2PgogIDxkaXYgY2xhc3M9InBhbmVsLWJvZHkiPgpUcnkgYWRkaW5nIGBnZW9tX2xpbmUoKWAgdG8gdGhpcyBwbG90IHRvIGNvbm5lY3QgdGhlIGRvdHMuIERvZXMgdGhpcyBsb29rIHJpZ2h0PyBVc2UgYD9nZW9tX2xpbmVgIHRvIGZpZ3VyZSBvdXQgaG93IHRoaXMgZ2VvbSBjb25uZWN0cyB0aGUgZG90cyBieSBkZWZhdWx0LCBhbmQgd2hpY2ggYWVzdGhldGljIGNhbiBiZSB1c2VkIHRvIGNvbm5lY3QgY2FzZXMgdG9nZXRoZXIuIFRyeSBlZGl0aW5nIHlvdXIgY29kZSB0byBkcmF3IDMgYmxhY2sgbGluZXMtIG9uZSBmb3IgZWFjaCBzb3VuZC4KCgpgYGB7cn0KIyBEb2VzIHRoaXMgbG9vayByaWdodD8gbm8hCmdncGxvdChzb3VuZHMsIGFlcyh4ID0gYWdlLCB5ID0gcHJvcF9wcm9kdWNlKSkgKyAKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBzb3VuZCksIHNpemUgPSAyKSArCiAgbGFicyh4ID0gIkFnZSAobW9udGhzKSIsIHkgPSAiUHJvcG9ydGlvbiBvZiBDaGlsZHJlbiBQcm9kdWNpbmciKSAKYGBgCgoKYGBge3J9CiMgQSBwb3NzaWJsZSBzb2x1dGlvbgpnZ3Bsb3Qoc291bmRzLCBhZXMoeCA9IGFnZSwgeSA9IHByb3BfcHJvZHVjZSkpICsgCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IHNvdW5kKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gc291bmQpLCBzaXplID0gMikgKwogIGxhYnMoeCA9ICJBZ2UgKG1vbnRocykiLCB5ID0gIlByb3BvcnRpb24gb2YgQ2hpbGRyZW4gUHJvZHVjaW5nIikgCmBgYAoKICA8L2Rpdj4KPC9kaXY+Cgo8ZGl2IGNsYXNzPSJwYW5lbCBwYW5lbC1zdWNjZXNzIj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj5DaGFsbGVuZ2UgIzQ6PC9kaXY+CiAgPGRpdiBjbGFzcz0icGFuZWwtYm9keSI+Ck1ha2UgdHdvIHBsb3RzOgoKMS4gUmVjcmVhdGUgdGhlIHBsb3QgYWJvdmUsIGJ1dCB0aGlzIHRpbWUgbWFwIGNvbG9yIHRvIHRoZSB0eXBlIG9mIHNvdW5kIHByb2R1Y2VkIGZvciBib3RoIHRoZSBwb2ludCBhbmQgbGluZSBnZW9tcy4gUGF5IGF0dGVudGlvbiB0byB0aGUgb3JkZXIgb2YgdGhlIGxheWVycyB5b3UgYXJlIGFkZGluZy0geW91IG1heSB3aXNoIHRvIHBsYWNlIGBnZW9tX2xpbmVgICpiZWZvcmUqIGBnZW9tX3BvaW50YCBzbyB0aGUgbGluZXMgYXJlIGFsd2F5cyAicGFpbnRlZCIgdW5kZXJuZWF0aCB0aGUgcG9pbnRzLgoKMi4gSW5zdGVhZCBvZiBgZ2VvbV9saW5lYCwgYWRkIGEgbG9lc3MgbGluZSB1c2luZyBgZ2VvbV9zbW9vdGhgLiBVc2UgYD9nZW9tX3Ntb290aGAgdG8gZmlndXJlIG91dCBob3cgdG8gZ2V0IHJpZCBvZiB0aGUgZ3JleSBzdGFuZGFyZCBlcnJvciByaWJib24uIFlvdSBtYXkgYWxzbyB3YW50IHRvIGluY3JlYXNlIHRoZSBsaW5lIHdpZHRoLiAKCmBgYHtyfQojIERvZXMgdGhpcyBsb29rIHJpZ2h0PyB5ZXMhCmdncGxvdChzb3VuZHMsIGFlcyh4ID0gYWdlLCB5ID0gcHJvcF9wcm9kdWNlLCBjb2xvciA9IHNvdW5kKSkgKyAKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludChzaXplID0gMikgKwogIGxhYnMoeCA9ICJBZ2UgKG1vbnRocykiLCB5ID0gIlByb3BvcnRpb24gb2YgQ2hpbGRyZW4gUHJvZHVjaW5nIikgCgpnZ3Bsb3Qoc291bmRzLCBhZXMoeCA9IGFnZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcHJvcF9wcm9kdWNlLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gc291bmQpKSArIAogIGdlb21fc21vb3RoKHNlID0gRkFMU0UsIGx3ZCA9IC41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMikgICsKICBsYWJzKHggPSAiQWdlIChtb250aHMpIiwgeSA9ICJQcm9wb3J0aW9uIG9mIENoaWxkcmVuIFByb2R1Y2luZyIpIApgYGAKCiAgPC9kaXY+CjwvZGl2PgoKV2h5IGRvZXMgdGhpcyB3b3JrPyBUbyB0ZWxsIGBnZW9tX2xpbmVgIGhvdyB0byBjb25uZWN0IHlvdXIgZG90cywgeW91IGNhbiBlaXRoZXI6CgotIE1hcCB0aGUgYGdyb3VwYCBhZXN0aGV0aWMgKHNvIGBhZXMoZ3JvdXAgPSBzb3VuZClgKSwgb3IgCi0gTWFwIHRoZSBgY29sb3JgIGFlc3RoZXRpYyBnbG9iYWxseSAoYGFlcyhjb2xvciA9IHNvdW5kKWAuIAoKQmVjYXVzZSBgZ2VvbV9saW5lYCB1bmRlcnN0YW5kcyB0aGUgYGNvbG9yYCBhZXN0aGV0aWMsIGl0IHdpbGwgdHJ5IHRvIGRyYXcgc2VwYXJhdGUgbGluZXMgZm9yIGVhY2ggY29sb3IuIEhlcmUgdGhhdCB0cmFuc2xhdGVzIHRvIHRocmVlIGxpbmVzLCBvbmUgZm9yIGVhY2ggc291bmQsIHdoaWNoIGlzIHdoYXQgd2Ugd2FudCEKCiMjIEJyaWVmIGFzaWRlOiBmYWN0b3JzCgpBdCB0aGlzIHBvaW50LCBvdXIgcGxvdCBpcyBsb29raW5nIHByZXR0eSBnb29kLiBCdXQgeW91IG1heSBoYXZlIG5vdGljZWQgdGhhdCB0aGUgbGVnZW5kIG9yZGVyIGRvZXNuJ3QgbWF0Y2ggdGhlIG9yZGVyIG9mIHRoZSBsaW5lcyBpbiB0aGUgcGxvdC4gKipRdWVzdGlvbjoqKiB3aHkgaXMgdGhpcyBhbiBpc3N1ZT8KCldoYXQgZGV0ZXJtaW5lcyB0aGUgb3JkZXIgb2YgbGV2ZWxzIGluIHRoZSBsZWdlbmQ/IFRoZSBvcmRlciBvZiBsZXZlbHMgaW4gdGhlIHVuZGVybHlpbmcgZmFjdG9yOgoKYGBge3J9CmxldmVscyhhcy5mYWN0b3Ioc291bmRzJHNvdW5kKSkKYGBgCgpJbiB0aGlzIGNhc2UsIHNpbmNlIHdlIGhhdmVuJ3Qgc2V0IHRoZW0sIFIgd2lsbCBwaWNrIGFuIG9yZGVyIGZvciB1cy4KCldlIF9jb3VsZF8gbWFudWFsbHkgcmUtb3JkZXIgdGhlIGxldmVscyBvZiB0aGUgZmFjdG9yLCBidXQgZGlmZmVyZW50IHBsb3RzIG1pZ2h0IG5lY2Vzc2l0YXRlIGRpZmZlcmVudCBmYWN0b3Igb3JkZXJpbmcsIGFuZCBpZiB3ZSBoYXZlIG1vcmUgdGhhbiB0d28gb3IgdGhyZWUgbGV2ZWxzLCB0eXBpbmcgdGhlbSByZXBlYXRlZGx5IGdldHMgdGVkaW91cyBmYXN0LiBJbnN0ZWFkLCBsZXQncyBoYXZlIFIgZG8gaXQhCgpUaGUgW2Bmb3JjYXRzYCBwYWNrYWdlXShodHRwOi8vZm9yY2F0cy50aWR5dmVyc2Uub3JnKSwgaXMgYGZvcmAgYGNhdGBlZ29yaWNhbCB2YXJpYWJsZXMgYW5kIGhhcyBsb3RzIG9mIHVzZWZ1bCBmdW5jdGlvbnMsIGluY2x1ZGluZyBzb21lIGZvciByZS1vcmRlcmluZyBsZXZlbHMuIFRoZXJlIGFyZSBsb3RzIG9mIGZ1bmN0aW9ucyBpbiBgZm9yY2F0c2AsIGFuZCB5b3UgY2FuIGluc3RhbGwgJiBsb2FkIGl0IHNlcGFyYXRlbHksIGFsdGhvdWdoIGBmb3JjYXRzYCBpcyBsb2FkZWQgd2l0aCB0aGUgYHRpZHl2ZXJzZWAuCgpgYGB7ciBldmFsID0gRkFMU0V9Cmluc3RhbGwucGFja2FnZXMoImZvcmNhdHMiKQpsaWJyYXJ5KGZvcmNhdHMpCmBgYAoKV2UnbGwgdXNlIHRoZSBgZmN0X3Jlb3JkZXIyYCBmdW5jdGlvbiwgd2hpY2ggYnkgZGVmYXVsdCB3aWxsIHJlLW9yZGVyIHRoZSBsZXZlbHMgb2YgYSBmYWN0b3IgYmFzZWQgb24gdGhlIG9yZGVyIG9mIG9jY3VycmVuY2Ugb2Ygb25lIHZhcmlhYmxlIChgeWAgaW4gdGhlIGRvY3MpIHdoZW4gdGhlIGRhdGFmcmFtZSBpcyBfc29ydGVkXyBieSBhbm90aGVyIHZhcmlhYmxlIChgeGAgaW4gdGhlIGRvY3MpOgoKYGBge3J9CiMgIlNvcnQgdGhlIGRhdGFmcmFtZSBieSBhZ2UsIGZpbmQgdGhlIGxhc3Qgb2NjdXJyZW5jZSBvZiBlYWNoIGxldmVsIG9mIHNvdW5kcyRzb3VuZCBpbiBvcmRlciBvZiBwcm9wX3Byb2R1Y2UKZmN0X3Jlb3JkZXIyKAogIGFzLmZhY3Rvcihzb3VuZHMkc291bmQpLCAKICBzb3VuZHMkYWdlLCAjIHZhcmlhYmxlICJ4IgogIHNvdW5kcyRwcm9wX3Byb2R1Y2UgIyB2YXJpYmxlICJ5IgopICU+JSBsZXZlbHMKYGBgCgoKClRoaXMgKHNvbWVod2F0IGNvbnZvbHV0ZWQpIHByb2NlZHVyZSBpcyB2ZXJ5IHVzZWZ1bCBmb3Igd2hlbiB5b3UgaGF2ZSBhIGxpbmUgY2hhcnQgb2YgdHdvIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMsIGNvbG9yZWQgYnkgYSBmYWN0b3IgdmFyaWFibGUuIExldCdzIHNlZSB0aGUgZGlmZmVyZW5jZToKCmBgYHtyfQpzb3VuZHMgPC0gc291bmRzICU+JSAKICBtdXRhdGUoc291bmQgPSBhcy5mYWN0b3Ioc291bmQpKQoKc291bmRfdHJhaiA8LSBnZ3Bsb3Qoc291bmRzLCBhZXMoeCA9IGFnZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcHJvcF9wcm9kdWNlLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gZmN0X3Jlb3JkZXIyKHNvdW5kLCBhZ2UsIHByb3BfcHJvZHVjZSkpKSArCiAgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSwgbHdkID0gLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAyKSArCiAgbGFicyh4ID0gIkFnZSAobW9udGhzKSIsIAogICAgICAgeSA9ICJQcm9wb3J0aW9uIG9mIENoaWxkcmVuIFByb2R1Y2luZyIsIAogICAgICAgY29sb3IgPSAic291bmQiKQpzb3VuZF90cmFqCmBgYAoKTVVDSCBCRVRURVIhIFNhdmUgeW91ciBwbG90IG9iamVjdCBhcyBgc291bmRfdHJhamAuIE5vdyB3ZSBjYW4gc3RhcnQgcGxheWluZyB3aXRoIHRoZSBhY3R1YWwgY29sb3JzLgoKIyMgU2V0IGx1bWluYW5jZSBhbmQgc2F0dXJhdGlvbiAoY2hyb21hdGljaXR5KQoKVGhlIGRlZmF1bHQgcXVhbGl0YXRpdmUgcGFsZXR0ZSB3b3JrcyBmaW5lIGhlcmUuIFRoZSBhZGRpdGlvbiBvZiBbYHNjYWxlX2NvbG9yX2h1ZWBdKGh0dHA6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3NjYWxlX2h1ZS5odG1sKSBjaGFuZ2VzIG5vdGhpbmcuCgpgYGB7cn0Kc291bmRfdHJhaiArCiAgc2NhbGVfY29sb3JfaHVlKCkKYGBgCgpXZSBjYW4gYWxzbyBjaGFuZ2UgdGhlc2Ugc2V0dGluZ3Mgd2l0aGluIHRoZSBkZWZhdWx0IGNvbG9yIHBhbGV0dGUsIHdoZXJlIHRoZSBhcmd1bWVudHMgYXJlOgoKLSBgaGAgPSByYW5nZSBvZiBodWVzIHRvIHVzZSwgaW4gWzAsIDM2MF0KLSBgbGAgPSBsdW1pbmFuY2UgKGxpZ2h0bmVzcykKLSBgY2AgPSBjaHJvbWEgKGludGVuc2l0eSBvZiBjb2xvcikKCmBgYHtyfQojIENoYW5nZSBodWUgKGwgYW5kIGMgYXJlIGRlZmF1bHRzKQpzb3VuZF90cmFqICsKICBzY2FsZV9jb2xvcl9odWUoaCA9IGMoMCwgOTApLCBsID0gNjUsIGMgPSAxMDApCgojIFVzZSBsdW1pbmFuY2U9NDUsIGluc3RlYWQgb2YgZGVmYXVsdCA2NQpzb3VuZF90cmFqICsKICBzY2FsZV9jb2xvcl9odWUobCA9IDQ1KQoKIyBSZWR1Y2Ugc2F0dXJhdGlvbiAoY2hyb21hKSBmcm9tIDEwMCB0byA1MCwgYW5kIGluY3JlYXNlIGx1bWluYW5jZQpzb3VuZF90cmFqICsKICBzY2FsZV9jb2xvcl9odWUobCA9IDc1LCBjID0gNTApCmBgYAoKIyMgU2V0IGRpc2NyZXRlIGNvbG9ycwoKV2UgY2FuIGNoYW5nZSB0aGUgYWN0dWFsIGNvbG9ycyB1c2VkIGJ5IGFkZGluZyB0aGUgbGF5ZXIgYHNjYWxlX2NvbG9yX21hbnVhbGAgb3IgYHNjYWxlX2ZpbGxfbWFudWFsYC4gQ29uZnVzaW9uIGJldHdlZW4gd2hpY2ggdG8gdXNlIHdoZW4gaXMgb2Z0ZW4gdGhlIGNhdXNlIG9mIG11Y2ggZnJ1c3RyYXRpb24hCgpUbyBuYW1lIG1vcmUgdGhhbiBvbmUgY29sb3IsIHdoaWNoIHlvdSBvZnRlbiB3YW50IHRvIGRvLCB1c2UgYGMoKWAuIEluIHRoZSBwYXJlbnRoZXNlcywgbmFtZWQgY29sb3JzIGFuZCBoZXggY29sb3JzIGFyZSBhbHdheXMgaW4gcXVvdGVzLgoKYGBge3J9CnNvdW5kX3RyYWogKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJjb3JuZmxvd2VyYmx1ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzZWFncmVlbiIsICJjb3JhbCIpKQpgYGAKClRoZXJlIGFyZSBtYW55IFtuYW1lZCBjb2xvcnNdKGh0dHA6Ly93d3cuc3RhdC5jb2x1bWJpYS5lZHUvfnR6aGVuZy9maWxlcy9SY29sb3IucGRmKSBhdmFpbGFibGUgaW4gUiEKCjxkaXYgY2xhc3M9InBhbmVsIHBhbmVsLXN1Y2Nlc3MiPgogIDxkaXYgY2xhc3M9InBhbmVsLWhlYWRpbmciPkNoYWxsZW5nZSAjNTo8L2Rpdj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1ib2R5Ij4KVmlldyB0aGUgY29kZSBibG9ja3MgYmVsb3cuIENvcHkgYW5kIHBhc3RlIHRoZSBjb2RlIHRvIHJ1biB0aGVtIGluIHlvdXIgb3duIGZpbGUuIFdoeSBkbyBuZWl0aGVyIG9mIHRoZSBmb2xsb3dpbmcgY29kZSBibG9ja3MgY2hhbmdlIHRoZSBjb2xvcnMgb2YgdGhlIHBvaW50cyBhbmQgbGluZXM/IFVzZSB5b3VyIHdvcmRzIDopICoodGhlIGFuc3dlciBpcyBiZWxvdyB0aGUgY2hhbGxlbmdlLCBidXQgdHJ5IHRvIHRyb3VibGUtc2hvb3Qgb24geW91ciBvd24gZmlyc3QpKgoKYGBge3J9CmdncGxvdChzb3VuZHMsIGFlcyh4ID0gYWdlLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBwcm9wX3Byb2R1Y2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBmY3RfcmVvcmRlcjIoc291bmQsIGFnZSwgcHJvcF9wcm9kdWNlKSkpICsgCiAgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSwgbHdkID0gLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAyKSArCiAgbGFicyh4ID0gIkFnZSAobW9udGhzKSIsIAogICAgICAgeSA9ICJQcm9wb3J0aW9uIG9mIENoaWxkcmVuIFByb2R1Y2luZyIsIAogICAgICAgY29sb3IgPSAic291bmQiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiY29ybmZsb3dlcmJsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNlYWdyZWVuIiwgImNvcmFsIikpCmBgYAoKCgpgYGB7cn0KZ2dwbG90KHNvdW5kcywgYWVzKHggPSBhZ2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IHByb3BfcHJvZHVjZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gZmN0X3Jlb3JkZXIyKHNvdW5kLCBhZ2UsIHByb3BfcHJvZHVjZSkpKSArIAogIGdlb21fc21vb3RoKHNlID0gRkFMU0UsIGx3ZCA9IC41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMikgKwogIGxhYnMoeCA9ICJBZ2UgKG1vbnRocykiLCAKICAgICAgIHkgPSAiUHJvcG9ydGlvbiBvZiBDaGlsZHJlbiBQcm9kdWNpbmciLCAKICAgICAgIGZpbGwgPSAic291bmQiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiY29ybmZsb3dlcmJsdWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzZWFncmVlbiIsICJjb3JhbCIpKQpgYGAKICA8L2Rpdj4KPC9kaXY+CgpBbnN3ZXJzOiAKCi0gSW4gdGhlIGZpcnN0LCB3ZSB1c2VkIGBzY2FsZV9maWxsX21hbnVhbGAsIGJ1dCB0aGUgaW4gdGhlIGdsb2JhbCBhZXN0aGV0aWNzLCB3ZSBtYXBwZWQgdGhlIGBjb2xvcmAsIG5vdCBgZmlsbGAsIGFlc3RoZXRpYyBvbnRvIHRoZSBgc291bmRgIHZhcmlhYmxlLgotIEluIHRoZSBzZWNvbmQsIHdlIGRpZCBkZWZpbmUgdGhlIGBmaWxsYCBhZXN0aGV0aWMgYW5kIHVzZWQgYHNjYWxlX2ZpbGxfbWFudWFsYCwgc28gdGhhdCBpcyBnb29kLiBCdXQgYGdlb21fbGluZWAgb25seSB1bmRlcnN0YW5kcyB0aGUgYGNvbG9yYCBhZXN0aGV0aWMsIG5vdCBgZmlsbGAuIEFuZCBmb3IgYGdlb21fcG9pbnRgLCB0aGUgZGVmYXVsdCBzaGFwZSBmb3IgaXMgMTksIHdoaWNoIGRvZXMgbm90IHVuZGVyc3RhbmQgdGhlIGBmaWxsYCBhZXN0aGV0aWMuCgo8ZGl2IGNsYXNzPSJwYW5lbCBwYW5lbC1zdWNjZXNzIj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj5DaGFsbGVuZ2UgIzY6PC9kaXY+CiAgPGRpdiBjbGFzcz0icGFuZWwtYm9keSI+ClN0YXJ0IHdpdGggdGhpcyBwbG90OgoKYGBge3J9CnNvdW5kX3RyYWoKYGBgCgpBZGQgYSBibGFjayBvdXRsaW5lIHRvIHRoZSBwb2ludHMsIGFuZCBjb2xvciB0aGUgaW5zaWRlIG9mIHRoZSBwb2ludHMgYW5kIHRoZSBsaW5lcyBieSBgc291bmRgIHVzaW5nIHRoZSBkZWZhdWx0IGRpc2NyZXRlIGNvbG9yIHBhbGV0dGUuIFlvdSBtYXkgYWxzbyB3aXNoIHRvIGVkaXQgdGhlIGxlZ2VuZHMgb24gdGhpcyBwbG90OiBgZ2VvbV9zbW9vdGhgIGhhcyBhbiBhcmd1bWVudCBjYWxsZWQgYHNob3cubGVnZW5kID0gRkFMU0VgLiBTZWUgaWYgeW91IHByZWZlciB0aGUgcGxvdCB3aXRoIHRoaXMgY2hhbmdlLiAKCklmIHRoaXMgd2FzIGVhc3ksIHRyeSBhcHBseWluZyB0aGUgc2FtZSBjdXN0b20gY29sb3IgcGFsZXR0ZSB0byB0aGUgaW5zaWRlIG9mIHRoZSBwb2ludHMgYW5kIHRvIHRoZSBsaW5lcy4gCgpgYGB7cn0KZ2dwbG90KHNvdW5kcywgYWVzKHggPSBhZ2UsIAogICAgICAgICAgICAgICAgICAgeSA9IHByb3BfcHJvZHVjZSwgCiAgICAgICAgICAgICAgICAgICBmaWxsID0gZmN0X3Jlb3JkZXIyKHNvdW5kLCBhZ2UsIHByb3BfcHJvZHVjZSkpKSArIAogIGdlb21fc21vb3RoKGFlcyhjb2xvciA9IGZjdF9yZW9yZGVyMihzb3VuZCwgYWdlLCBwcm9wX3Byb2R1Y2UpKSwKICAgICAgICAgICAgICBzZSA9IEZBTFNFLCBsd2QgPSAuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIsIHNoYXBlID0gMjEpICsKICBsYWJzKHggPSAiQWdlIChtb250aHMpIiwgCiAgICAgICB5ID0gIlByb3BvcnRpb24gb2YgQ2hpbGRyZW4gUHJvZHVjaW5nIiwgCiAgICAgICBmaWxsID0gInNvdW5kIikKYGBgCgoKCmBgYHtyfQpnZ3Bsb3Qoc291bmRzLCBhZXMoeCA9IGFnZSwgCiAgICAgICAgICAgICAgICAgICB5ID0gcHJvcF9wcm9kdWNlLCAKICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmY3RfcmVvcmRlcjIoc291bmQsIGFnZSwgcHJvcF9wcm9kdWNlKSkpICsgCiAgZ2VvbV9zbW9vdGgoYWVzKGNvbG9yID0gZmN0X3Jlb3JkZXIyKHNvdW5kLCBhZ2UsIHByb3BfcHJvZHVjZSkpLAogICAgICAgICAgICAgIHNlID0gRkFMU0UsIGx3ZCA9IC41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZ2VvbV9wb2ludChzaXplID0gMiwgc2hhcGUgPSAyMSkgKwogIGxhYnMoeCA9ICJBZ2UgKG1vbnRocykiLCAKICAgICAgIHkgPSAiUHJvcG9ydGlvbiBvZiBDaGlsZHJlbiBQcm9kdWNpbmciLCAKICAgICAgIGZpbGwgPSAic291bmQiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiY29ybmZsb3dlcmJsdWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzZWFncmVlbiIsICJjb3JhbCIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImNvcm5mbG93ZXJibHVlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2VhZ3JlZW4iLCAiY29yYWwiKSkKYGBgCgoKICA8L2Rpdj4KPC9kaXY+CgpZb3UgY2FuIGFsc28gZGVmaW5lIHlvdXIgY29sb3IgcGFsZXR0ZSBhcyBhIHZlY3RvciBvdXRzaWRlIG9mIGBnZ3Bsb3QyYC4gQmVsb3csIEkgbWFkZSBhbiBvYmplY3QgY2FsbGVkIGBteV9jb2xvcnNgIG91dHNpZGUgb2YgYGdncGxvdDJgLiBUbyB1c2UgaXQsIHdlIGNhbGwgdGhhdCBvYmplY3Qgd2l0aGluIHRoZSBgc2NhbGVfY29sb3VyX21hbnVhbGAgZnVuY3Rpb24uCgpgYGB7cn0KbXlfY29sb3JzIDwtIGMoImNhZGV0Ymx1ZSIsICJzdGVlbGJsdWUiLCAic2FsbW9uIikgIyBxdW90ZSBjb2xvciBuYW1lcwpzb3VuZF90cmFqICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gbXlfY29sb3JzKSAjIG5vdGU6IG5vdCBpbiBxdW90ZXMKYGBgCgoKPGRpdiBjbGFzcz0icGFuZWwgcGFuZWwtc3VjY2VzcyI+CiAgPGRpdiBjbGFzcz0icGFuZWwtaGVhZGluZyI+Q2hhbGxlbmdlICM3OjwvZGl2PgogIDxkaXYgY2xhc3M9InBhbmVsLWJvZHkiPgpEZWZpbmUgYSBjdXN0b20gY29sb3IgcGFsZXR0ZSB1c2luZyBoZXhhZGVjaW1hbCBjb2xvcnMgKCNycmdnYmIpLCBhbmQgYXBwbHkgaXQgdXNpbmcgYHNjYWxlX2NvbG9yX21hbnVhbGAgdG8geW91ciBgc291bmRfdHJhamAgcGxvdC4gU29tZSBiYXNpYyBvbmVzIGFyZSBoZXJlOiAKCmh0dHBzOi8vc2FzaGF0Lm1lLzIwMTcvMDEvMTEvbGlzdC1vZi0yMC1zaW1wbGUtZGlzdGluY3QtY29sb3JzLwoKUGFyc2UgdGhlIGhleGFkZWNpbWFsIHN0cmluZyBsaWtlIHNvOiAjcnJnZ2JiLCB3aGVyZSByciwgZ2csIGFuZCBiYiByZWZlciB0byBjb2xvciBpbnRlbnNpdHkgaW4gdGhlIHJlZCwgZ3JlZW4sIGFuZCBibHVlIGNoYW5uZWxzLCByZXNwZWN0aXZlbHkuIAoKCmBgYHtyfQojIGZyb20gaHR0cHM6Ly9naXRodWIuY29tL213YXNrb20vc2VhYm9ybi9ibG9iL21hc3Rlci9zZWFib3JuL3BhbGV0dGVzLnB5CnNiX2NvbG9yYmxpbmQgPC0gYygiIzAwNzJCMiIsICIjMDA5RTczIiwgIiNENTVFMDAiLAogICAgICAgICAgICAgICAgICAgICAgICAiI0NDNzlBNyIsICIjRjBFNDQyIiwgIiM1NkI0RTkiKQpzb3VuZF90cmFqICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHNiX2NvbG9yYmxpbmQpCmBgYAoKICA8L2Rpdj4KPC9kaXY+CgojIyBCdWlsdC1pbiBkaXNjcmV0ZSBwYWxldHRlcwoKIyMjIENvbG9yYnJld2VyCgpBcyB3ZSBkaXNjdXNzZWQgb24gTW9uZGF5LCBbQ29sb3JicmV3ZXJdKGh0dHBzOi8vY29sb3JicmV3ZXIyLm9yZy8pIGlzIGEgdXNlZnVsIHRvb2wgZm9yIGRlc2lnbmluZyBjb2xvciBwYWxldHRlcywgd2hpY2ggY2FuIGJlIHVzZWQgZGlyZWN0bHkgaW4gUi4KClRvIHVzZSBDb2xvcmJyZXdlciBwYWxldHRlcywgeW91J2xsIG5lZWQgdG8gaW5zdGFsbCB0aGUgYFJDb2xvckJyZXdlcmAgcGFja2FnZSBmcm9tIENSQU4uIFRoaXMgY2h1bmsgb2YgY29kZSB0ZWxscyB5b3UgaG93OgoKYGBge3IgZXZhbCA9IEZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJSQ29sb3JCcmV3ZXIiKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKYGBgCgpDb2xvcmJyZXdlciBoYXMgYSBmZXcgbmFtZWQgKGkuZS4sIHByZS1zZXQpIF9xdWFsaXRhdGl2ZV8gcGFsZXR0ZXM6IEFjY2VudCwgRGFyazIsIFBhaXJlZCwgUGFzdGVsMSwgUGFzdGVsMiwgU2V0MSwgU2V0MiwgU2V0My4gSGVyZSBpcyBob3cgdG8gdmlldyB0aGVtOgoKYGBge3J9CmJyZXdlci5wYWwoNSwgIkRhcmsyIikgIyBsaXN0IDUgaGV4IGNvbG9ycwpkaXNwbGF5LmJyZXdlci5wYWwoNSwgIkRhcmsyIikgIyB2aWV3IDUgaGV4IGNvbG9ycwpgYGAKCkFuZCBoZXJlIGlzIGhvdyB5b3UgdXNlIHRoZW06CgpgYGB7cn0Kc291bmRfdHJhaiArCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKQpgYGAKCgojIyMgVmlyaWRpcyBwYWxldHRlcwoKYGdncGxvdGAgY29tZXMgYnVpbHQtaW4gd2l0aCB0aGUgIlZpcmlkaXMiIGNvbG9yIHBhbGV0dGUsIHRoZSBwb2ludCBvZiB3aGljaCBpcyB0byBiZSBhIHNldCBvZiBjb2xvcnMgdGhhdCAiYXJlIHByZXR0eSwgYmV0dGVyIHJlcHJlc2VudCB5b3VyIGRhdGEsIGVhc2llciB0byByZWFkIGJ5IHRob3NlIHdpdGggY29sb3JibGluZG5lc3MsIGFuZCBwcmludCB3ZWxsIGluIGdyZXkgc2NhbGUuIgoKUmVhZCBtb3JlIGhlcmUgaW4gdGhlIFt2aXJpZGlzIHZpZ25ldHRlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvdmlyaWRpcy92aWduZXR0ZXMvaW50cm8tdG8tdmlyaWRpcy5odG1sKS4gTm90ZSB0aGF0IHRoaXMgdmlnbmV0dGUgaXMgZm9yIGFuIFIgcGFja2FnZSB0aGF0IGlzIGdlbmVyYWxseSBubyBsb25nZXIgbmVlZGVkIHdpdGggcmVjZW50IHZlcnNpb25zIG9mIGdncGxvdC0gdGhlIFZpcmlkaXMgY29sb3IgcGFsZXR0ZSBkaWRuJ3QgdXNlZCB0byBiZSBwYXJ0IG9mIGBnZ3Bsb3RgIGJ5IGRlZmF1bHQsIGJ1dCBpdCBkb2VzIG5vdyEg8J+OiSBUaGVyZSBhcmUgVmlyaWRpcyBmb3VyIGNvbG9ybWFwIG9wdGlvbnMgYXZhaWxhYmxlOgoKLSAibWFnbWEiIChvciAiQSIpLAotICJpbmZlcm5vIiAob3IgIkIiKSwKLSAicGxhc21hIiAob3IgIkMiKSwKLSAidmlyaWRpcyIgKG9yICJEIiwgdGhlIGRlZmF1bHQgb3B0aW9uKS4KCmBgYHtyfQpzb3VuZF90cmFqICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QoKSArCiAgdGhlbWVfbWluaW1hbCgpCgpzb3VuZF90cmFqICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2Qob3B0aW9uID0gInBsYXNtYSIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgo8ZGl2IGNsYXNzPSJwYW5lbCBwYW5lbC1zdWNjZXNzIj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj5DaGFsbGVuZ2UgIzg6PC9kaXY+CiAgPGRpdiBjbGFzcz0icGFuZWwtYm9keSI+ClVzZSB0aGUgVmlyaWRpcyBwYWxldHRlcyB0byBjb2xvciB0aGUgcG9pbnRzIGJ5IGFuZCB0aGUgbGluZXMgYnkgYHNvdW5kYDsgbWFrZSB0aGUgb3V0bGluZSBvZiB0aGUgcG9pbnRzICJtaWRuaWdodGJsdWUiLiBQaWNrIGFueSBjb2xvcm1hcCBvcHRpb24sIGFuZCBwbGF5IHdpdGggYHRoZW1lX2J3YCBvciBgdGhlbWVfbWluaW1hbGAgdG8gc2VlIHdoYXQgeW91IGxpa2UuCgpgYGB7cn0KZ2dwbG90KHNvdW5kcywgYWVzKHggPSBhZ2UsIAogICAgICAgICAgICAgICAgICAgeSA9IHByb3BfcHJvZHVjZSwgCiAgICAgICAgICAgICAgICAgICBmaWxsID0gZmN0X3Jlb3JkZXIyKHNvdW5kLCBhZ2UsIHByb3BfcHJvZHVjZSkpKSArIAogIGdlb21fc21vb3RoKGFlcyhjb2xvciA9IGZjdF9yZW9yZGVyMihzb3VuZCwgYWdlLCBwcm9wX3Byb2R1Y2UpKSwKICAgICAgICAgICAgICBzZSA9IEZBTFNFLCBsd2QgPSAuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIsIHNoYXBlID0gMjEsIGNvbG91ciA9ICJtaWRuaWdodGJsdWUiKSArCiAgbGFicyh4ID0gIkFnZSAobW9udGhzKSIsIAogICAgICAgeSA9ICJQcm9wb3J0aW9uIG9mIENoaWxkcmVuIFByb2R1Y2luZyIsIAogICAgICAgZmlsbCA9ICJzb3VuZCIpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QoKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAogIDwvZGl2Pgo8L2Rpdj4KCkEgbm90ZTogdGhlIGRlZmF1bHQgVmlyaWRpcyBkaXNjcmV0ZSBwYWxldHRlIGVuZHMgdXAgaW4gYSBwcmV0dHkgZ25hcmx5IHllbGxvdyBjb2xvciB0aGF0IEkgcGVyc29uYWxseSBmZWVsIGxpa2UgaXMgbm90IGlkZWFsIGZvciBhbGwgc2l0dWF0aW9ucy4KSW4gcHJhY3RpY2UsIEkgaGF2ZSBiZWVuIGtub3duIHRvIGFydGlmaWNpYWxseSBjbGFtcCB0aGUgcmFuZ2Ugb2YgdGhlIFZpcmlkaXMgZGlzY3JldGUgcGFsZXR0ZSB0byBhdm9pZCB0aGF0IGxhc3QgY29sb3IuCgojIyBDb2xvciBQYWxldHRlcyBmcm9tIFBhY2thZ2VzCgpBcyB3aXRoIGV2ZXJ5dGhpbmcgZWxzZSBpbiBSLCB0aGVyZSBhcmUgX251bWVyb3VzXyBob21lYnJldyBwYWNrYWdlcyB3aXRoIGRpZmZlcmVudCBjb2xvciBwYWxldHRlcy4gSGVyZSwgd2Ugd2lsbCBtZWV0IGEgZmV3IG9mIG15IGZhdnMuCgojIyMgV2VzIEFuZGVyc29uIHBhbGV0dGVzIAoKTXkgZmF2b3JpdGUhIFRvIHVzZSBXZXMgQW5kZXJzb24gcGFsZXR0ZXMsIHlvdSdsbCBuZWVkIHRvIGluc3RhbGwgdGhlIGB3ZXNhbmRlcnNvbmAgcGFja2FnZSBmcm9tIENSQU4uIFRoaXMgY2h1bmsgb2YgY29kZSB0ZWxscyB5b3UgaG93OgoKYGBge3IgZXZhbCA9IEZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJ3ZXNhbmRlcnNvbiIpCmxpYnJhcnkod2VzYW5kZXJzb24pCmBgYAoKCgpgYGB7cn0KbmFtZXMod2VzX3BhbGV0dGVzKSAjIGFsbCB0aGUgcGFsZXR0ZSBuYW1lcwp3ZXNfcGFsZXR0ZSgiR3JhbmRCdWRhcGVzdDIiKSAjIHZpZXcgbmFtZWQgcGFsZXR0ZQp3ZXNfcGFsZXR0ZSgiR3JhbmRCdWRhcGVzdDIiKVsxOjRdICMgbGlzdCBmaXJzdCA0IGhleCBjb2xvcnMKd2VzX3BhbGV0dGUoIkdyYW5kQnVkYXBlc3QyIilbYygxLDQpXSAjIGxpc3QgY29sb3JzIDEgYW5kIDQKYGBgCgpUbyB1c2UgdGhlc2UgcGFsZXR0ZXMsIHVzZSBgc2NhbGVfY29sb3JfbWFudWFsYCB3aGVyZSBgdmFsdWVzYCBpcyBzZXQgdG8gYHdlc19wYWxldHRlKCJuYW1lIilgLiBGb3IgZXhhbXBsZToKCmBgYHtyfQpzb3VuZF90cmFqICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gd2VzX3BhbGV0dGUoIkRhcmplZWxpbmcxIikpCgpzb3VuZF90cmFqICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gd2VzX3BhbGV0dGUoIkZhbnRhc3RpY0ZveDEiKSkKYGBgCgoKPGRpdiBjbGFzcz0icGFuZWwgcGFuZWwtc3VjY2VzcyI+CiAgPGRpdiBjbGFzcz0icGFuZWwtaGVhZGluZyI+Q2hhbGxlbmdlICM4OjwvZGl2PgogIDxkaXYgY2xhc3M9InBhbmVsLWJvZHkiPgpXaGF0IGlmIHlvdSBqdXN0IGRvbid0IHdhbnQgdG8gdXNlIHRoZSBjb2xvcnMgaW4gdGhlIG9yZGVyIHRoZXkgYXJlIGluPyBVc2UgYSBgd2VzX3BhbGV0dGVgIG9mIHlvdXIgY2hvaWNlLiBVc2luZyBvdXIgY29kZSBmcm9tIGFib3ZlLCB0cnkgcGlja2luZyB0aGUgbGFzdCAzIGNvbG9ycyBvZiBhIHBhbGV0dGUuIEFkZCBpdCB0byB5b3VyIGBzb3VuZF90cmFqYCBwbG90LgoKSWYgdGhpcyB3YXMgZWFzeSwgdHJ5IHVzaW5nIGNvbG9ycyAyLCAzLCBhbmQgNSBpbnN0ZWFkLgoKYGBge3J9CnNvdW5kX3RyYWogKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSB3ZXNfcGFsZXR0ZSgiRGFyamVlbGluZzEiKVszOjVdKQoKc291bmRfdHJhaiArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHdlc19wYWxldHRlKCJGYW50YXN0aWNGb3gxIilbYygyLCAzLCA1KV0pCmBgYAogIDwvZGl2Pgo8L2Rpdj4KCgojIyMjIEluIFRoZSBXaWxkCgpJIHJlY2VudGx5IHVzZWQgb25lIG9mIHRoZSBXZXMgQW5kZXJzb24gcGFsZXR0ZXMgKCJBc3Rlcm9pZENpdHkxIikgaW4gb25lIG9mIG15IG93biBmaWd1cmVzISAKCiFbQXN0ZXJvaWQgQ2l0eSBJbiBUaGUgV2lsZF0oZmlndXJlLzJfa3JpcHBlbmRvcmZfYnlfZGltZW5zaW9uLnBuZykKCiMjIyBBbHNvIGdyZWF0OiBTdHVkaW8gR2hpYmxpIQoKQW5vdGhlciBmdW4gY2hvaWNlOiBUaGUgW1N0dWRpbyBHaGlibGldKGh0dHBzOi8vZXdlbm1lLmdpdGh1Yi5pby9naGlibGkvKSBjb2xvciBwYWxldHRlcyEKCmBgYHtyfQpzb3VuZF90cmFqICsKIHNjYWxlX2NvbG91cl9naGlibGlfZCgiWWVzdGVyZGF5TWVkaXVtIiwgZGlyZWN0aW9uID0gLTEpCmBgYAoKV2h5IGRvIHdlIG5lZWQgdGhhdCBgZGlyZWN0aW9uID0gLTFgIGJ1c2luZXNzPyBUbyBhbnN3ZXIsIGxldCdzIHRyeSBydW5uaW5nIHRoaXMgd2l0aCB0aGUgZGVmYXVsdCBjb2xvciBvcmRlcmluZzoKCmBgYHtyfQpzb3VuZF90cmFqICsKIHNjYWxlX2NvbG91cl9naGlibGlfZCgiWWVzdGVyZGF5TWVkaXVtIiwgZGlyZWN0aW9uID0gMSkKYGBgCgpUaG9zZSBhbGwgbG9vayBpbmRpc3Rpbmd1aXNoYWJsZTsgd2h5IGlzIHRoaXMgaGFwcGVuaW5nPyBMZXQncyBsb29rIGF0IHRoZSBjb2xvcnMgaW4gdGhlIHBhbGV0dGU6CgpgYGB7cn0KZ2hpYmxpX3BhbGV0dGUoIlllc3RlcmRheU1lZGl1bSIpCmBgYAoKV2Ugc2VlIHRoYXQgdGhlIGZpcnN0IGZldyBjb2xvcnMgYXJlIHZlcnkgZGFyazsgb24gYSBncmV5IGJhY2tncm91bmQsIHdpdGggc21hbGwgbGluZXMsIHRob3NlIGNvbG9ycyBsb29rIHZlcnkgc2ltaWxhciB0byBvbmUgYW5vdGhlci4gU28gc2ltaWxhciB0aGF0LCBhdCBmaXJzdCwgaXQgbG9va3MgbGlrZSB0aGV5IGFyZSBhbGwgdGhlIHNhbWUhIFRvIHByb3ZlIHRoYXQgd2UgYXJlIGFjdHVhbGx5IHB1bGxpbmcgaW4gdGhvc2UgY29sb3JzLCBsZXQncyBhcnRpZmljaWFsbHkgKGFuZCB0ZW1wb3JhcmlseSEpIGluY3JlYXNlIG91ciBwb2ludCBzaXplOgoKYGBge3J9CiAgc291bmRfdHJhaiArCiAgZ2VvbV9wb2ludChzaXplID0gMjApICsKICBzY2FsZV9jb2xvdXJfZ2hpYmxpX2QoIlllc3RlcmRheU1lZGl1bSIsIGRpcmVjdGlvbiA9IDEpCmBgYAoKV2UgY2FuIHNlZSB0aGF0IGluZGVlZCwgdGhlIGNvbG9ycyBmcm9tIHRoZSBwYWxldHRlIGFyZSBiZWluZyB1c2VkLCBidXQgdGhhdCB0aGV5IGFyZSB0b28gc2ltaWxhciB0byBiZSBhIGdvb2QgY2hvaWNlIGZvciB0aGlzIHBsb3QuIEhlbmNlLCBzd2FwcGluZyB0aGUgbm9ybWFsIG9yZGVyIG9mIHRoZSBjb2xvcnMgdXNpbmcgdGhlIGBkaXJlY3Rpb25gIGFyZ3VtZW50LgoKIyMjIGBnZ3RoZW1lc2AgcGFsZXR0ZXMKClRvIHVzZSB0aGVzZSBwYWxldHRlcywgeW91J2xsIG5lZWQgdG8gaW5zdGFsbCB0aGUgYGdndGhlbWVzYCBwYWNrYWdlIGZyb20gQ1JBTi4gVGhpcyBjaHVuayBvZiBjb2RlIHRlbGxzIHlvdSBob3c6CgpgYGB7ciBldmFsID0gRkFMU0V9Cmluc3RhbGwucGFja2FnZXMoImdndGhlbWVzIikKbGlicmFyeShnZ3RoZW1lcykKYGBgCgoKCmBgYHtyfQpzb3VuZF90cmFqICsKICBzY2FsZV9jb2xvcl9maXZldGhpcnR5ZWlnaHQoKQoKc291bmRfdHJhaiArCiAgc2NhbGVfY29sb3JfZWNvbm9taXN0KCkKYGBgCgojIyMgYGdnc2NpYCBQYWxldHRlcwoKW2BnZ3NjaWBdKGh0dHBzOi8vbmFueC5tZS9nZ3NjaS8pIHByb3ZpZGVzIGNvbG9yIHBhbGV0dGVzIGRlc2lnbmVkIHRvIG1hdGNoIHdpdGggdGhlIGFlc3RoZXRpY3Mgb2YgYSB3aWRlIHZhcmlldHkgb2Ygc2NpZW50aWZpYyBwdWJsaXNoZXJzOgoKYGBge3J9CmxpYnJhcnkoZ2dzY2kpCgpzb3VuZF90cmFqICsgc2NhbGVfY29sb3JfbmVqbSgpCmBgYAoKCiMjIyBQYWxldHRlcyBmcm9tIHRoZSBRdWVlbiBCZWUKClRvIHVzZSBbQmV5b25jZSBwYWxldHRlc10oaHR0cHM6Ly9naXRodWIuY29tL2RpbGwvYmV5b25jZSksIHlvdSdsbCBuZWVkIHRvIGluc3RhbGwgdGhlIGBiZXlvbmNlYCBwYWNrYWdlIGZyb20gR2l0SHViIHVzaW5nIGBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoKWAuIFRoaXMgY2h1bmsgb2YgY29kZSB0ZWxscyB5b3UgaG93OgoKYGBge3IgZXZhbCA9IEZBTFNFfQojIGluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikKIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImRpbGwvYmV5b25jZSIpCmxpYnJhcnkoYmV5b25jZSkKYGBgCgpOb3RlIHRoYXQgYSBudW1iZXIgb2Ygc3R1ZGVudHMgaGFkIGluc3RhbGxhdGlvbiBwcm9ibGVtcyB3aXRoIHRoaXMgcGFja2FnZSEgTW92ZSBvbiBpZiB5b3UgZG8uCgpgYGB7cn0KYmV5b25jZV9wYWxldHRlKDE4KQpgYGAKCmBgYHtyfQpzb3VuZF90cmFqICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYmV5b25jZV9wYWxldHRlKDE4KVszOjVdKQpgYGAKCkhlcmUgd2UnbGwgb25seSB1c2UgdGhlIGZpcnN0LCBmb3VydGgsIGFuZCBmaWZ0aCBjb2xvcnMgaW4gdGhlIHBhbGV0dGUuCgpgYGB7cn0Kc291bmRfdHJhaiArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGJleW9uY2VfcGFsZXR0ZSgxOClbYygxLCA0LCA1KV0pCmBgYAoKCiMjIEdvaW5nIEdyZXlzY2FsZSBmb3IgRGlzY3JldGUKClVzZSBgc2NhbGVfY29sb3JfZ3JleWAgb3IgYHNjYWxlX2ZpbGxfZ3JleWAsIG9yIHNvbWV0aW1lcyBib3RoIGRlcGVuZGluZyBvbiB5b3VyIGdlb21zIGFuZCB0aGUgYWVzdGhldGljcyB0aGV5IHVuZGVyc3RhbmQuCgpgYGB7cn0Kc291bmRfdHJhaiArCiAgc2NhbGVfY29sb3JfZ3JleSgpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpTZXQgc3RhcnQgYW5kIGVuZAoKYGBge3J9CnNvdW5kX3RyYWogKwogIHNjYWxlX2NvbG9yX2dyZXkoc3RhcnQgPSAwLjIsIGVuZCA9IC44KSAKYGBgCgoKTWFrZSB0aGUgc2FtZSBwbG90IGJ1dCBtYWtlIHBvaW50cyBvdXRsaW5lZCBpbiBibGFjawoKYGBge3J9CmdncGxvdChzb3VuZHMsIGFlcyh4ID0gYWdlLCAKICAgICAgICAgICAgICAgICAgIHkgPSBwcm9wX3Byb2R1Y2UsIAogICAgICAgICAgICAgICAgICAgZmlsbCA9IGZjdF9yZW9yZGVyMihzb3VuZCwgYWdlLCBwcm9wX3Byb2R1Y2UpKSkgKyAKICBnZW9tX3Ntb290aChhZXMoY29sb3IgPSBmY3RfcmVvcmRlcjIoc291bmQsIGFnZSwgcHJvcF9wcm9kdWNlKSksCiAgICAgICAgICAgICAgc2UgPSBGQUxTRSwgbHdkID0gLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBnZW9tX3BvaW50KHNpemUgPSAyLCBzaGFwZSA9IDIxKSArCiAgbGFicyh4ID0gIkFnZSAobW9udGhzKSIsIAogICAgICAgeSA9ICJQcm9wb3J0aW9uIG9mIENoaWxkcmVuIFByb2R1Y2luZyIsIAogICAgICAgZmlsbCA9ICJzb3VuZCIpICsKICBzY2FsZV9maWxsX2dyZXkoc3RhcnQgPSAwLjMsIGVuZCA9IDEpICsKICBzY2FsZV9jb2xvcl9ncmV5KHN0YXJ0ID0gMC4zLCBlbmQgPSAxKSAKYGBgCgpTdWdnZXN0IHJlZHVuZGFuY3kgaW4gZ3JleXNjYWxlLSB0cnkgY2hhbmdpbmcgbGluZSB0eXBlIGluc3RlYWQgb2YgbGluZSAob3IgaW4gYWRkaXRpb24gdG8pIGxpbmUgY29sb3IuCgoKQ2hhbmdlIGxpbmUgdHlwZSBieSBgc291bmRgLCBzZXQgY29sb3IgdG8gYmxhY2suCgpgYGB7cn0KZ2dwbG90KHNvdW5kcywgYWVzKHggPSBhZ2UsIAogICAgICAgICAgICAgICAgICAgeSA9IHByb3BfcHJvZHVjZSwgCiAgICAgICAgICAgICAgICAgICBmaWxsID0gZmN0X3Jlb3JkZXIyKHNvdW5kLCBhZ2UsIHByb3BfcHJvZHVjZSkpKSArIAogIGdlb21fc21vb3RoKGFlcyhsdHkgPSBmY3RfcmVvcmRlcjIoc291bmQsIGFnZSwgcHJvcF9wcm9kdWNlKSksIGNvbG9yID0gImJsYWNrIiwKICAgICAgICAgICAgICBzZSA9IEZBTFNFLCBsd2QgPSAuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIsIHNoYXBlID0gMjEpICsKICBsYWJzKHggPSAiQWdlIChtb250aHMpIiwgCiAgICAgICB5ID0gIlByb3BvcnRpb24gb2YgQ2hpbGRyZW4gUHJvZHVjaW5nIiwgCiAgICAgICBmaWxsID0gInNvdW5kIikgKwogIHNjYWxlX2ZpbGxfZ3JleShzdGFydCA9IDAuMywgZW5kID0gMSkgCmBgYAoKQ2hhbmdlIGJvdGghCgpgYGB7cn0KZ2dwbG90KHNvdW5kcywgYWVzKHggPSBhZ2UsIAogICAgICAgICAgICAgICAgICAgeSA9IHByb3BfcHJvZHVjZSwgCiAgICAgICAgICAgICAgICAgICBmaWxsID0gZmN0X3Jlb3JkZXIyKHNvdW5kLCBhZ2UsIHByb3BfcHJvZHVjZSkpKSArIAogIGdlb21fc21vb3RoKGFlcyhjb2xvciA9IGZjdF9yZW9yZGVyMihzb3VuZCwgYWdlLCBwcm9wX3Byb2R1Y2UpLAogICAgICAgICAgICAgICAgICBsdHkgPSBmY3RfcmVvcmRlcjIoc291bmQsIGFnZSwgcHJvcF9wcm9kdWNlKSksCiAgICAgICAgICAgICAgc2UgPSBGQUxTRSwgbHdkID0gLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBnZW9tX3BvaW50KHNpemUgPSAyLCBzaGFwZSA9IDIxKSArCiAgbGFicyh4ID0gIkFnZSAobW9udGhzKSIsIAogICAgICAgeSA9ICJQcm9wb3J0aW9uIG9mIENoaWxkcmVuIFByb2R1Y2luZyIsIAogICAgICAgZmlsbCA9ICJzb3VuZCIpICsKICBzY2FsZV9maWxsX2dyZXkoc3RhcnQgPSAwLjMsIGVuZCA9IC44KSArCiAgc2NhbGVfY29sb3JfZ3JleShzdGFydCA9IDAuMywgZW5kID0gLjgpIApgYGAKCiMjIENvbG9yYmxpbmQtZnJpZW5kbHkgcGFsZXR0ZXMKClRoZSBbYGNvbG9yYmxpbmRyYCBwYWNrYWdlXShodHRwczovL2dpdGh1Yi5jb20vY2xhdXN3aWxrZS9jb2xvcmJsaW5kcikgY2FuIGJlIHVzZWQgdG8gInNpbXVsYXRlIGNvbG9yYmxpbmRuZXNzIGluIHByb2R1Y3Rpb24tcmVhZHkgUiBmaWd1cmVzLiIgVG8gdXNlIHRoaXMgcGFja2FnZSwgeW91J2xsIG5lZWQgdG8gZmlyc3QgaW5zdGFsbCB0aGUgYGNvd3Bsb3RgIHBhY2thZ2UgZnJvbSBHaXRIdWIgdXNpbmcgYGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigpYC4gWW91J2xsIGFsc28gbmVlZCB0byBpbnN0YWxsIHRoZSBgY29sb3JzcGFjZWAgcGFja2FnZSBmcm9tIENSQU4uIEZpbmFsbHksIHlvdSBjYW4gdGhlbiB1c2UgYGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigpYCBhZ2FpbiB0byBpbnN0YWxsIHRoZSBgY29sb3JibGluZHJgIHBhY2thZ2UuIFRoaXMgY29kZSBjaHVuayBzaG93cyB5b3UgaG93IHRvIGRvIGFsbCAzIGluc3RhbGxzIHRvIHVzZSB0aGUgYGNvbG9yYmxpbmRyYCBwYWNrYWdlOgoKYGBge3IgZXZhbCA9IEZBTFNFfQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIndpbGtlbGFiL2Nvd3Bsb3QiKQppbnN0YWxsLnBhY2thZ2VzKCJjb2xvcnNwYWNlIiwgcmVwb3MgPSAiaHR0cDovL1ItRm9yZ2UuUi1wcm9qZWN0Lm9yZyIpCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiY2xhdXN3aWxrZS9jb2xvcmJsaW5kciIpCmBgYAoKVG8gdXNlOgpgYGB7cn0KIyBzYXZlIGEgZ2dwbG90IG9iamVjdApteV9zb3VuZF90cmFqIDwtIHNvdW5kX3RyYWogKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBiZXlvbmNlX3BhbGV0dGUoMTgpW2MoMSwgNCwgNSldKQpgYGAKClZpZXcgdGhhdCBmaWd1cmUgYWZ0ZXIgY29sb3ItdmlzaW9uLWRlZmljaWVuY3kgc2ltdWxhdGlvbjoKCmBgYHtyfQpsaWJyYXJ5KGNvbG9yYmxpbmRyKQpjdmRfZ3JpZChteV9zb3VuZF90cmFqKQpgYGAKCllvdSBjYW4gYWxzbyB1c2UgdGhlIGNvbG9yYmxpbmQtZnJpZW5kbHkgcGFsZXR0ZSBpbiB0aGlzIHBhY2thZ2UgdXNpbmcgYHNjYWxlX2NvbG9yX09rYWJlSXRvYCBhbmQgYHNjYWxlX2ZpbGxfT2thYmVJdG9gOgoKYGBge3J9CmNiX3NvdW5kX3RyYWogPC0gc291bmRfdHJhaiArCiAgc2NhbGVfY29sb3JfT2thYmVJdG8oKQoKY2Jfc291bmRfdHJhagpjdmRfZ3JpZChjYl9zb3VuZF90cmFqKQpgYGAKCllvdSBjYW4gc3RpbGwgdXNlIHRoaXMgY29sb3JibGluZC1mcmllbmRseSBwYWxldHRlIHdpdGhvdXQgdGhlIGBjb2xvcmJsaW5kcmAgcGFja2FnZSB0aG91Z2guIFtIZXJlXShodHRwOi8vamZseS5pYW0udS10b2t5by5hYy5qcC9jb2xvci8pIGFyZSB0aGUgY29sb3JzIQoKIVtdKGh0dHA6Ly9qZmx5LmlhbS51LXRva3lvLmFjLmpwL2NvbG9yL2ltYWdlL3BhbGxldGUuanBnKQoKVGhlIFtDb29rYm9vayBmb3IgUl0oaHR0cDovL3d3dy5jb29rYm9vay1yLmNvbS9HcmFwaHMvQ29sb3JzXyhnZ3Bsb3QyKS8jYS1jb2xvcmJsaW5kLWZyaWVuZGx5LXBhbGV0dGUpIHByb3ZpZGVkIHRoZSBtYXRjaGluZyBoZXggY29sb3JzIHRvbyB0byBtYWtlIGxpZmUgZWFzaWVyOgpgYGB7cn0KY2JiUGFsZXR0ZSA8LSBjKCIjMDAwMDAwIiwgIiNFNjlGMDAiLCAiIzU2QjRFOSIsICIjMDA5RTczIiwgIiNGMEU0NDIiLCAiIzAwNzJCMiIsICIjRDU1RTAwIiwgIiNDQzc5QTciKQoKIyBUbyB1c2UgZm9yIGxpbmUgYW5kIHBvaW50IGNvbG9ycywgYWRkCnNvdW5kX3RyYWogKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gY2JiUGFsZXR0ZVtjKDMsIDcsIDgpXSkKYGBgCgojIyBMYWJlbHMKCldoZW4gd29ya2luZyB3aXRoIGNvbG9ycywgdGhlIF9kZWZhdWx0XyBwYXR0ZXJuLCBlbmZvcmNlZCBieSBgZ2dwbG90YCwgaXMgdG8gdXNlIGEgZmlndXJlIGxlZ2VuZCB0byBpbmRpY2F0ZSB3aGljaCBjb2xvciBnb2VzIHdpdGggd2hpY2ggbGV2ZWwgb2YgYSBmYWN0b3IuIApIb3dldmVyLCB0aGlzIGlzIG5vdCBhbHdheXMgdGhlIGJlc3Qgd2F5IHRvIGdvISAKQW5vdGhlciBvcHRpb24gaXMgdG8gZGlyZWN0bHkgbGFiZWwgeW91ciBkYXRhIHdpdGhpbiB0aGUgcGxvdCBpdHNlbGYuCldoZW4gc2hvdWxkIHlvdSBjb25zaWRlciB0aGlzIG9wdGlvbj8KCjEuIFdoZW4geW91IGhhdmUgYSB2ZXJ5IHNtYWxsIG51bWJlciBvZiBsZXZlbHMKMi4gV2hlbiB0aGUgZGVzaWduIG9mIHRoZSByZXN0IG9mIHRoZSBwbG90IGlzIGFtZW5hYmxlOiB0aGVyZSBhcmVuJ3QgdG9vIG1hbnkgb3RoZXIgYW5ub3RhdGlvbnMgb3IgdmlzdWFsIGVsZW1lbnRzIHRha2luZyB1cCBzcGFjZS4KClRoZXJlIGFyZSBzZXZlcmFsIHdheXMgdG8gZG8gdGhpcywgYnV0IGEgZ29vZCBwbGFjZSB0byBzdGFydCBpcyBgZ2dyZXBlbGA6CgpgYGB7cn0KbGlicmFyeShnZ3JlcGVsKQoKc291bmRzIDwtIHNvdW5kcyAlPiUKICBtdXRhdGUobGFiZWwgPSBjYXNlX3doZW4oCiAgICBhZ2UgPT0gbWF4KGFnZSkgfiBzb3VuZCkpCgpnZ3Bsb3Qoc291bmRzLCBhZXMoeCA9IGFnZSwgCiAgICAgICAgICAgICAgICAgICB5ID0gcHJvcF9wcm9kdWNlLCAKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gZmN0X3Jlb3JkZXIyKHNvdW5kLCBhZ2UsIHByb3BfcHJvZHVjZSkpKSArCiAgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSwgbHdkID0gLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAyKSArCiAgbGFicyh4ID0gIkFnZSAobW9udGhzKSIsIAogICAgICAgeSA9ICJQcm9wb3J0aW9uIG9mIENoaWxkcmVuIFByb2R1Y2luZyIpICsKICBnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gbGFiZWwpLAogICAgICAgICAgICAgICAgICBudWRnZV94ID0gMSwKICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gInkiLAogICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpICsKICBndWlkZXMoY29sb3IgPSBGQUxTRSkKYGBgCgoKCiMgQ29udGludW91cyBjb2xvcnMKCipOLkIuIEFsbCBvZiB0aGUgZXhhbXBsZSBwbG90cyBiZWxvdyBhcmUgZ3JlYXQgZXhhbXBsZXMgb2YgaG93ICoqbm90KiogdG8gdXNlIGNvbnRpbnVvdXMgY29sb3JzLiBJJ20gc2hvd2luZyB0aGVzZSBzbyB5b3UgY2FuIHNlZSBob3cgdG8gd29yayB3aXRoIGNvbnRpbnVvdXMgY29sb3IgcGFsZXR0ZXMsIGFuZCB0byBtYWtlIHRoaXMgdG9waWMgZmxvdyBlYXNpZXIgZm9yIHlvdSBJJ20gc3RpY2tpbmcgd2l0aCBvcmlnaW5hbCBkYXRhc2V0LioKCiMjIERlZmF1bHQgY29udGludW91cyBwYWxldHRlCgpMZXTigJlzIG1hcCBjb2xvciB0byBhIGNvbnRpbnVvdXMgdmFyaWFibGUuIEZvciB0aGlzLCB3ZSBhcmUgcmV0dXJuaW5nIHRvIGBnZW9tX2xpbmVgIGluc3RlYWQgb2YgYGdlb21fc21vb3RoYCwgYmVjYXVzZSB0aGUgbGF0dGVyIGRvZXNuJ3QgcmVzcG9uZCB0byBjb250aW51b3VzIGNvbG9yIHBhbGV0dGVzLgoKYGBge3J9CnNvdW5kX2J5X2FnZSA8LSBnZ3Bsb3Qoc291bmRzLCBhZXMoeCA9IGFnZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IHByb3BfcHJvZHVjZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBhZ2UpKSArCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IHNvdW5kKSwgbHdkID0gLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAyKSArCiAgbGFicyh4ID0gIkFnZSAobW9udGhzKSIsIAogICAgICAgeSA9ICJQcm9wb3J0aW9uIG9mIENoaWxkcmVuIFByb2R1Y2luZyIpCnNvdW5kX2J5X2FnZQpgYGAKCgoKIyMgQ29sb3IgY2hvaWNlIHdpdGggY29udGludW91cyB2YXJpYWJsZXMgCgpXaXRoIGRpc2NyZXRlIGNvbG9ycywgd2UgdXNlZCBlaXRoZXIgYHNjYWxlX2NvbG9yX21hbnVhbGAgb3IgYHNjYWxlX2ZpbGxfbWFudWFsYCAoYW5kIHNvbWV0aW1lcyBib3RoIHdlcmUgbmVlZGVkISkuIEZvciBjb250aW51b3VzIGNvbG9ycywgd2UgdXNlIGVpdGhlciBgc2NhbGVfY29sb3JfZ3JhZGllbnRgIG9yIGBzY2FsZV9maWxsX2dyYWRpZW50YC4KCmBgYHtyfQpzb3VuZF9ieV9hZ2UgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50KCkKYGBgCgpZb3UgY2FuIHJldmVyc2UgdGhlIGdyYWRpZW50IHNjYWxlLi4uCgpgYGB7cn0Kc291bmRfYnlfYWdlICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudCh0cmFucyA9ICJyZXZlcnNlIikKYGBgCgoKYGBge3J9CnNvdW5kX2J5X2FnZSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93ID0gIndoaXRlIiwgaGlnaCA9ICJyZWQiKQpgYGAKCldlIGNhbiBtYWtlIHRoaXMgc2FtZSBwbG90IHVzaW5nIGEgY3VzdG9tIGdyZXlzY2FsZSBncmFkaWVudC4KCmBgYHtyfQpzb3VuZF9ieV9hZ2UgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdyA9ICJncmV5OTAiLCBoaWdoID0gImJsYWNrIikKYGBgCgoKClNvIGBzY2FsZV9jb2xvcl9ncmFkaWVudGAgZ2l2ZXMgeW91IGEgc2VxdWVudGlhbCBncmFkaWVudCwgYnV0IHlvdSBtYXkgd2FudCBhIGRpdmVyZ2luZyBjb2xvciBzY2hlbWUgaW5zdGVhZC4gRm9yIHRoYXQsIHlvdSBjYW4gdXNlIGBzY2FsZV9jb2xvcl9ncmFkaWVudDJgCgoKYGBge3J9CiMgRGl2ZXJnaW5nIGNvbG9yIHNjaGVtZQptZWRfYWdlIDwtIHNvdW5kcyAlPiUgCiAgc3VtbWFyaXplKG1vcyA9IG1lZGlhbihhZ2UpKSAlPiUgCiAgcHVsbCgpCnNvdW5kX2J5X2FnZSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQyKG1pZHBvaW50ID0gbWVkX2FnZSwKICAgICAgICAgICAgICAgICAgICAgIGxvdz0iYmx1ZSIsIG1pZD0id2hpdGUiLCBoaWdoPSJyZWQiICkKYGBgCgojIyBCdWlsdC1pbiBjb250aW51b3VzIHBhbGV0dGVzCgojIyMgVXNlIGBSQ29sb3JCcmV3ZXJgIAoKQWdhaW4sIHRvIHVzZSB5b3UgbmVlZCB0byBpbnN0YWxsIGFuZCBsb2FkIHRoZSBgUkNvbG9yQnJld2VyYCBwYWxldHRlLiAKCmBgYHtyIGV2YWwgPSBGQUxTRX0KbGlicmFyeShSQ29sb3JCcmV3ZXIpCmBgYAoKVGhlbiB1c2UgYHNjYWxlX2NvbG9yX2dyYWRpZW50bmAuCgpgYGB7cn0Kc291bmRfYnlfYWdlICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3VycyA9IGJyZXdlci5wYWwobj01LCBuYW1lPSJQdUJ1R24iKSkKYGBgCgpSZXZlcnNlIHRoZSBjb2xvcnMuLi4KCmBgYHtyfQpzb3VuZF9ieV9hZ2UgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvdXJzID0gcmV2KGJyZXdlci5wYWwobj01LCBuYW1lPSJQdUJ1R24iKSkpCmBgYAoKIyMjIFZpcmlkaXMKCk5vdGUhIEZvciBkaXNjcmV0ZSA9PSBGQUxTRSAodGhlIGRlZmF1bHQpIGFsbCBvdGhlciBhcmd1bWVudHMgYXJlIGFzIHRvIGBzY2FsZV9maWxsX2dyYWRpZW50bmAgb3IgYHNjYWxlX2NvbG9yX2dyYWRpZW50bmAuIChBbHNvIG5vdGUgdGhhdCBgX2dyYWRpZW50X25fYCBpcyBub3QgYSB0eXBvLSB0aGUgX25fIHZlcnNpb25zIG9mIHRob3NlIGZ1bmN0aW9ucyBhbGxvdyBtdWx0aS1jb2xvciBncmFkaWVudHMpLgoKYGBge3J9CnNvdW5kX2J5X2FnZSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkKYGBgCgpgYGB7cn0Kc291bmRfYnlfYWdlICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2Mob3B0aW9uID0gIm1hZ21hIikKYGBgCgpSZWFkIHRoZSBoZWxwIGZ1bmN0aW9uIGZvciBgP3NjYWxlX2NvbG9yX3ZpcmlkaXNfY2AuIEFzIGJlZm9yZSwgd2UgY2FuIHVzZSB0aGUgYGRpcmVjdGlvbmAgcGFyYW1ldGVyIHRvIHJldmVyc2UgdGhlIG9yZGVyIG9mIHRoZSBjb2xvcnMsIG9yLCBhdGxlcm5hdGl2ZWx5LCB3ZSBjYW4gdXNlIHRoZSBgYmVnaW5gIGFuZCBgZW5kYCBwYXJhbWV0ZXJzIHRvIGFjY29tcGxpc2ggdGhlIHNhbWUgdGhpbmcuIFVzaW5nIHRoZSAiaW5mZXJubyIgcGFsZXR0ZSBfaW4gcmV2ZXJzZV86CgpgYGB7cn0Kc291bmRfYnlfYWdlICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2Mob3B0aW9uID0gImluZmVybm8iLCBiZWdpbiA9IDEsIGVuZCA9IDApCmBgYAoKVGhpcyBgYmVnaW5gL2BlbmRgIHRyaWNrIGlzIGFsc28gaG93IHdlIGNhbiBhZGp1c3QgdGhlIGNvbG9yIHNjYWxlIHRvIG5vdCBnZXQgcXVpdGUgc28uLi4geWVsbG93IGF0IHRoZSB2ZXJ5IGVuZD4KCmBgYHtyfQpzb3VuZF9ieV9hZ2UgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYyhvcHRpb24gPSAiaW5mZXJubyIsIGJlZ2luID0gMCwgZW5kID0gMC45KSAjIGlmIHdlIGRvbid0IHdhbnQgaXQgdG8gZ28gYWxsIHRoZSB3YXkgdG8gMS4wLi4uCgpgYGAKCgojIEZpbmFsIGNoYWxsZW5nZSAoIzkpCgo8ZGl2IGNsYXNzPSJwYW5lbCBwYW5lbC1zdWNjZXNzIj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj5DaGFsbGVuZ2UgIzk6PC9kaXY+CiAgPGRpdiBjbGFzcz0icGFuZWwtYm9keSI+ClVzaW5nICoqbmV3IGRhdGEqKiBvZiB5b3VyIGNob2ljZSwgbWFrZSB0aHJlZSBuZXcgcGxvdHMuIFVzZSBhbnkgYGdlb21gIHRoYXQgbWFrZXMgc2Vuc2UuIFRoZSBwbG90cyBzaG91bGQ6CgotIEhhdmUgeC0gYW5kIHktYXhlcyB0aGF0IGFyZSBlYWNoIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMuCi0gQXBwbHkgYSBub24tZGVmYXVsdCBjb2xvciBwYWxldHRlLCBlaXRoZXIgY29sb3JpbmcgYnkgYSBxdWFsaXRhdGl2ZSB2YXJpYWJsZSAoZGlzY3JldGUgY29sb3JzKSBvciBhIHF1YW50aXRhdGl2ZSB2YXJpYWJsZSAoY29udGludW91cyBjb2xvcnMpLiBUaGlzIGxpc3Qgb2YgW1IgY29sb3IgcGFsZXR0ZXNdKGh0dHBzOi8vZ2l0aHViLmNvbS9FbWlsSHZpdGZlbGR0L3ItY29sb3ItcGFsZXR0ZXMpIGhhcyBldmVuIG1vcmUgaWRlYXMgdGhhbiB3ZSBjb3VsZCBjb3ZlciBpbiBjbGFzcy4KCgoxLiBJbiB0aGUgZmlyc3QgcGxvdCwgeW91IG11c3QgKip3aWVsZCBjb2xvciBjYXJlZnVsbHkgYW5kIGVmZmVjdGl2ZWx5KiouIFRoZSBhZGRpdGlvbiBvZiB0aGUgY29sb3IvZmlsbCBhZXN0aGV0aWNzIG11c3QgYmUgZG9uZSBpbiBhIHdheSB0aGF0IHRoZSBpbnRlcnByZXRhdGlvbiBvZiB0aGUgcGxvdCBpbXByb3Zlcy4gQWxzbywgeW91IG11c3Qgc2hvdyBob3cgeW91ciBjb2xvcnMgZmFyZSBmb3IgY29sb3JibGluZCB2aWV3ZXJzLiBJbmNsdWRlIDItMyBzZW50ZW5jZXMgYWJvdXQgd2h5IHlvdSBtYWRlIHRoZSBwbG90IHRoYXQgeW91IGRpZC4gV2hhdCBxdWVzdGlvbnMgZG9lcyB5b3VyIHBsb3QgYW5zd2VycyAob3IgcGVyaGFwcyB3aGF0IHF1ZXN0aW9ucyBkb2VzIHlvdXIgcGxvdCByYWlzZSk/CgoyLiBJbiB0aGUgc2Vjb25kIHBsb3QsIHlvdSBtdXN0ICoqbWFrZSBhIGdyZXlzY2FsZSB2ZXJzaW9uIG9mIHlvdXIgZmlyc3QgcGxvdCEqKiBBbmQgYWdhaW4sIGl0IG11c3QgbG9vayBnb29kIGFuZCBtYWtlIHNlbnNlLgoKMy4gSW4gdGhlIHRoaXJkIHBsb3QsIHlvdSBtdXN0ICoqdXNlIGNvbG9yIGJhZGx5KiouIE1ha2UgYSBwbG90IHdoZXJlIHRoZSBjb2xvcnMgYXJlIGVpdGhlciByZWR1bmRhbnQsIGNvbmZ1c2luZywgb3IganVzdCBnZW5lcmFsbHkgbm9uLXNlbnNpY2FsLiBFeHBsYWluIHdoeSB0aGlzIGxhc3QgdmlzdWFsaXphdGlvbiBmYWlscy4KCiAgICAKICAgIDwvZGl2Pgo8L2Rpdj4KClNvbWUgZGF0YSBpZGVhczoKCi0gTWFjQXJ0aHVyLUJhdGVzIENvbW11bmljYXRpdmUgRGV2ZWxvcG1lbnQgSW52ZW50b3J5IChNQi1DREkpLCBhIGZhbWlseSBvZiBwYXJlbnQtcmVwb3J0IHF1ZXN0aW9ubmFpcmVzIG1lYXN1cmluZyBjaGlsZHJlbidzIHZvY2FidWxhcnkgdW5kZXJzdGFuZGluZyBhbmQgcHJvZHVjdGlvbgogICAgLSBbUiBwYWNrYWdlIGB3b3JkYmFua3JgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvd29yZGJhbmtyL2luZGV4Lmh0bWwpCiAgICAtIFNlZSBteSBjb2RlIFtoZXJlXSgwM2EtbWVvdy1jbGVhbmluZy5odG1sKQotIFBPVFVTIEV4ZWN1dGl2ZSBPcmRlcnMKICAgIC0gW0RhdGFdKGh0dHBzOi8vd3d3LmZlZGVyYWxyZWdpc3Rlci5nb3YvZXhlY3V0aXZlLW9yZGVycykKICAgIC0gRm9sbG93IEJvYiBSdWRpcycgY29kZS10aHJvdWdoIFtoZXJlXShodHRwczovL3J1ZC5pcy9iLzIwMTgvMDQvMTgvZXhhbWluaW5nLXBvdHVzLWV4ZWN1dGl2ZS1vcmRlcnMvKQotIE5hdGlvbmFsIEVsZWN0cm9uaWMgSW5qdXJ5IFN1cnZlaWxsYW5jZSBTeXN0ZW0gKE5FSVNTKQogICAgLSBbUiBwYWNrYWdlIGBuZWlzc2BdKGh0dHBzOi8vZ2l0aHViLmNvbS9oYWRsZXkvbmVpc3MpCiAgICAtIEZvbGxvdyBKdWxpYSBTaWxnZSdzIGNvZGUtdGhyb3VnaCBbaGVyZV0oaHR0cHM6Ly9qdWxpYXNpbGdlLmNvbS9ibG9nL3lvdXItZmxvb3IvKQotIEZsaWdodHMKICAgIC0gW1IgcGFja2FnZSBgcG53ZmxpZ2h0czE0YF0oaHR0cHM6Ly9naXRodWIuY29tL2lzbWF5Yy9wbndmbGlnaHRzMTQpCiAgICAtIFtSIHBhY2thZ2UgYG55Y2ZsaWdodHMxM2BdKGh0dHBzOi8vZ2l0aHViLmNvbS9oYWRsZXkvbnljZmxpZ2h0czEzKQotIEJ1aWxkaW5nIFBlcm1pdHMKICAgIC0gU2VlIGNvZGUgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9oYWRsZXkvYnVpbGRpbmctcGVybWl0cykKICAgIC0gV2F0Y2ggWW91VHViZSBjb2RlLXRocm91Z2ggW2hlcmVdKGh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL3dhdGNoP3Y9Z281QXUwMUpydnMpCi0gQ29ja3RhaWwgQmFsYW5jZQogICAgLSBbRGF0YV0oaHR0cHM6Ly9naXRodWIuY29tL2thcm5lc2t5L2NvY2t0YWlsLWJhbGFuY2UpCi0gTkFTQSBXZWF0aGVyCiAgICAtIFtEYXRhXShodHRwczovL2dpdGh1Yi5jb20vaGFkbGV5L25hc2F3ZWF0aGVyKQotIFNvY2lhbCBTZWN1cml0eSBBZG1pbmlzdHJhdGlvbiBCYWJ5IE5hbWVzCiAgICAtIFtSIHBhY2thZ2UgYGJhYnluYW1lc2BdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9iYWJ5bmFtZXMvaW5kZXguaHRtbCkKICAgIC0gRm9sbG93IEp1bGlhIFNpbGdlIConTXkgQmFieSBCb29tZXIgTmFtZSBNaWdodCBIYXZlIEJlZW4gIkRlYmJpZSInKjogaHR0cHM6Ly9qdWxpYXNpbGdlLmNvbS9ibG9nL215LWJhYnktYm9vbWVyLW5hbWUvCiAgICAtIEZvbGxvdyBIaWxhcnkgUGFya2VyOiAqSGlsYXJ5OiBUaGUgTW9zdCBQb2lzb25lZCBCYWJ5IE5hbWUgaW4gVVMgSGlzdG9yeSo6IGh0dHBzOi8vaGlsYXJ5cGFya2VyLmNvbS8yMDEzLzAxLzMwL2hpbGFyeS10aGUtbW9zdC1wb2lzb25lZC1iYWJ5LW5hbWUtaW4tdXMtaGlzdG9yeS8KLSBZb3V0aCBCZWhhdmlvciBSaXNrIFN1cnZlaWxsYW5jZSBTeXN0ZW0KICAgIC0gW1IgcGFja2FnZSBgeXJic3NgXShodHRwczovL2dpdGh1Yi5jb20vaGFkbGV5L3lyYnNzKQogICAgLSBTb21lIFtnb29kIGlkZWFzIGhlcmVdKGh0dHBzOi8vd3d3LmNkYy5nb3YvbmNoaHN0cC9uZXdzcm9vbS8yMDEyL3lyYnMtZ3JhcGhpY3MyMDEyLmh0bWwpCi0gR3VuIHNhbGVzCiAgICAtIFtEYXRhXShodHRwczovL2dpdGh1Yi5jb20vaGFkbGV5L2d1bi1zYWxlcykKICAgIC0gU29tZSBbZXhhbXBsZSBwbG90cyBoZXJlXShodHRwczovL2dpdGh1Yi5jb20vTllUaW1lcy9ndW5zYWxlcy9ibG9iL21hc3Rlci9vdXQvcGxvdHMucGRmKQotIEdhcG1pbmRlcgogICAgLSBbUiBwYWNrYWdlIGBnYXBtaW5kZXJgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZ2FwbWluZGVyL2luZGV4Lmh0bWwpCiAgICAtIFNvbWUgW2V4YW1wbGUgcGxvdHMgaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvZ2dwbG90Mi10dXRvcmlhbC9ibG9iL21hc3Rlci9nYXBtaW5kZXItZ2dwbG90Mi1zY2F0dGVycGxvdC5tZCkKCgoKCg==