Goals for Lab 01
- Get your feet wet!
- Innoculate you against
ggplot2
errors- we all get
them!
- Get exposed to the range of things you can do, before we go
deep…
- Develop your own personal preferences for
data visualizations!
- Do you like or hate gridlines?
- What fonts do you find pleasant to read?
- What kinds of colors do you like?
- Are you team
theme_gray
or theme_bw
(or
theme_minimal
)?
These are important questions, and I want you to develop
(well-informed) opinions on these matters!
Most of the things we cover today, we will be re-visiting later in
the term; you are not expected to be familiar with all of the R
functions or patterns that we are using.
Other things to think about for this lab:
- Try not to copy-and-paste code:
- Becoming efficient/proficient with R depends on building muscle
memory, and the only way to do that is to type
- By typing, you will introduce errors, and this is a good
chance to get practice at interpreting and fixing them.
- Take note of steps or functions that involve bits of R that you are
unfamiliar with- this lab will help you identify particular areas that
you are comfortable with as well as areas you will want to focus
on.
Nathan’s Hot Dog Eating
Contest
This includes a reconstruction of Nathan
Yau’s hot dog contest example, as interpreted by Jackie Wirz, ported
into R and ggplot2
by Steven Bedrick for a workshop for the
OHSU Data
Science Institute, and finally adapted, made idiomatic, and improved
by Alison Hill for all you intrepid Data-Viz-onauts!
First, we load our packages:
library(tidyverse)
library(extrafont)
library(here)
Read in and wrangle
data
Next, we load some data. In the Posit Cloud project for this lab, you
will see a data
directory with the necessary files. We can
read it in using read_csv
, and along the way use
col_factor
to tell it how to handle the gender column.
hot_dogs <- read_csv(here::here("data", "hot_dog_contest.csv"),
col_types = cols(
gender = col_factor(levels = NULL)
))
Validation
Check it out, once it is read in, and make sure it looks like
this!
glimpse(hot_dogs)
Rows: 59
Columns: 4
$ year <dbl> 2022, 2022, 2021, 2021, 2020, 2020, 2019, 2019, 2018, 2018, …
$ gender <fct> male, female, male, female, male, female, male, female, male…
$ name <chr> "Joey Chestnut", "Miki Sudo", "Joey Chestnut", "Michelle Les…
$ num_eaten <dbl> 63.00, 40.00, 76.00, 30.75, 75.00, 48.50, 71.00, 31.00, 74.0…
hot_dogs
# A tibble: 59 × 4
year gender name num_eaten
<dbl> <fct> <chr> <dbl>
1 2022 male Joey Chestnut 63
2 2022 female Miki Sudo 40
3 2021 male Joey Chestnut 76
4 2021 female Michelle Lesco 30.8
5 2020 male Joey Chestnut 75
6 2020 female Miki Sudo 48.5
7 2019 male Joey Chestnut 71
8 2019 female Miki Sudo 31
9 2018 male Joey Chestnut 74
10 2018 female Miki Sudo 37
# … with 49 more rows
At this point, follow the HLO process and familiarize yourself with
the columns and their contents. Questions to ask:
- Are the types of the columns what you would expect? Note that this
requires that you have some notion of what to expect! Possible things to
think about:
- What type makes sense for a column representing a year?
- What about the
num_eaten
column?
- What about the
gender
column? What assumptions
- What are the minimum and maximum values in the numeric columns? Are
the values in the range you would expect?
- Are there any missing values?
In addition to glimpse()
, try loading (not installing)
the skimr
package and using its skim()
function on the hot_dogs
data frame.
Adding a
variable
In addition to the information that is already in the
dataset itself, we know that we will also be wanting to somehow include
information about whether a given year was before or after the
incorporation of the competitive eating league. Let’s add an
indicator variable to the data using mutate()
.
Also, the data’s a little sketchy pre-1981, and for our purposes today
we’ll be focusing on males only, so let’s do some
filter
ing, as well:
hot_dogs <- hot_dogs %>%
mutate(post_ifoce = year >= 1997) %>%
filter(year >= 1981 & gender == 'male')
hot_dogs
# A tibble: 42 × 5
year gender name num_eaten post_ifoce
<dbl> <fct> <chr> <dbl> <lgl>
1 2022 male Joey Chestnut 63 TRUE
2 2021 male Joey Chestnut 76 TRUE
3 2020 male Joey Chestnut 75 TRUE
4 2019 male Joey Chestnut 71 TRUE
5 2018 male Joey Chestnut 74 TRUE
6 2017 male Joey Chestnut 72 TRUE
7 2016 male Joey Chestnut 70 TRUE
8 2015 male Matthew Stonie 62 TRUE
9 2014 male Joey Chestnut 61 TRUE
10 2013 male Joey Chestnut 69 TRUE
# … with 32 more rows
Plot The Data
Now let’s try making a first crack at a plot:
ggplot(hot_dogs, aes(x = year, y = num_eaten)) +
geom_col()
Note that our data is already in “counted” form, so we’re using
geom_col()
instead of geom_bar()
.
We will now progressively improve this visualization, one step at a
time.
Add Axis Labels And
Title
ggplot(hot_dogs, aes(x = year, y = num_eaten)) +
geom_col() +
labs(x = "Year", y = "Hot Dogs and Buns Consumed") +
ggtitle("Nathan's Hot Dog Eating Contest Results, 1981-2022", subtitle = "(Male contestants only)")
Play With Colors
Challenge #1:
Make 3 versions of the last plot we just made:
- In the first, make all the columns outlined in
“white”.
- In the second, make all the columns outlined in
“white” and filled in “navyblue”.
- In the third, make all the columns outlined in
“white” and filled in according to whether or not
post_ifoce
is TRUE or FALSE (use default colors for
now).
HINT: color
and fill
are two of
ggplot
’s aesthetic mapping variables (i.e., “things about
how the plot looks that we get to specify”)
ggplot(hot_dogs, aes(x = year, y = num_eaten)) +
geom_col(colour = "white") +
labs(x = "Year", y = "Hot Dogs and Buns Consumed") +
ggtitle("Nathan's Hot Dog Eating Contest Results, 1981-2022", subtitle = "(Male contestants only)")
ggplot(hot_dogs, aes(x = year, y = num_eaten)) +
geom_col(colour = "white", fill = "navyblue") +
labs(x = "Year", y = "Hot Dogs and Buns Consumed") +
ggtitle("Nathan's Hot Dog Eating Contest Results, 1981-2022", subtitle = "(Male contestants only)")
ggplot(hot_dogs, aes(x = year, y = num_eaten)) +
geom_col(aes(fill = post_ifoce), colour = "white") +
labs(x = "Year", y = "Hot Dogs and Buns Consumed") +
ggtitle("Nathan's Hot Dog Eating Contest Results, 1981-2022", subtitle = "(Male contestants only)")
Challenge #2:
What if you want to change the legend in the last plot you made? Use
google to figure out how to do the following:
- Delete the legend title
- Make the legend text either “Post-IFOCE” or “Pre-IFOCE”.
HINT: in ggplot
, legends are controlled by the relevant
scale (color, fill, etc.) that they are mapped to.
ggplot(hot_dogs, aes(x = year, y = num_eaten)) +
geom_col(aes(fill = post_ifoce), colour = "white") +
labs(x = "Year", y = "Hot Dogs and Buns Consumed") +
ggtitle("Nathan's Hot Dog Eating Contest Results, 1981-2022", subtitle = "(Male contestants only)") +
scale_fill_discrete(name = "",
labels=c("Pre-IFOCE", "Post-IFOCE"))
Change The Dataset
Now, let’s change the question a little bit. Up to this point, we
have looked at HDB performance relative to the creation of the
IFOCE. What if what matters is the affiliation of the
contestants (i.e., whether or not the contestants are members of the
IFOCE or not)? We’ll need some different data for this. Through the
Magic Of Data Science™, we have dug that information up and put
it into an expanded version of our CSV file, which you can find in the
data directory.
Challenge #3:
Let’s work with this new dataset! Do the following:
Read in the “hot_dog_contest_with_affiliation.csv” data file,
using col_types
to read in affiliated
and
gender
as factors.
Within a mutate
, create a new variable called
post_ifoce
that is TRUE if year
is greater
than or equal to 1997.
Also filter
the new data for only years 1981 and
after, and only for male competitors.
hdm_affil <- read_csv(here::here("data", "hot_dog_contest_with_affiliation.csv"),
col_types = cols(
affiliated = col_factor(levels = NULL),
gender = col_factor(levels = NULL)
)) %>%
mutate(post_ifoce = year >= 1997) %>%
filter(year >= 1981 & gender == "male")
hdm_affil <- read_csv(here::here("data", "hot_dog_contest_with_affiliation.csv"),
col_types = cols(
affiliated = col_factor(levels = NULL),
gender = col_factor(levels = NULL)
)) %>%
mutate(post_ifoce = year >= 1997) %>%
filter(year >= 1981 & gender == "male")
glimpse(hdm_affil)
Rows: 42
Columns: 6
$ year <dbl> 2022, 2021, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 2013,…
$ gender <fct> male, male, male, male, male, male, male, male, male, male,…
$ name <chr> "Joey Chestnut", "Joey Chestnut", "Joey Chestnut", "Joey Ch…
$ num_eaten <dbl> 63.000, 76.000, 75.000, 71.000, 74.000, 72.000, 70.000, 62.…
$ affiliated <fct> current, current, current, current, current, current, curre…
$ post_ifoce <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,…
Challenge #4:
Let’s do some basic EDA with this new dataset! Do the following:
Use dplyr::distinct
to figure out how many unique
values there are of affiliated
.
Use dplyr::count
to count the number of rows for
each unique value of affiliated
; use ?count
to
figure out how to sort the counts in descending order.
hdm_affil %>%
distinct(affiliated)
# A tibble: 3 × 1
affiliated
<fct>
1 current
2 former
3 not affiliated
hdm_affil %>%
count(affiliated, sort = TRUE)
# A tibble: 3 × 2
affiliated n
<fct> <int>
1 not affiliated 20
2 current 16
3 former 6
Now let’s plot this new data, and fill the columns according to our
new affiliated
column.
ggplot(hdm_affil, aes(x = year, y = num_eaten)) +
geom_col(aes(fill = affiliated)) +
labs(x = "Year", y = "Hot Dogs and Buns Consumed") +
ggtitle("Nathan's Hot Dog Eating Contest Results, 1981-2022", subtitle = "(Male contestants only)")
Challenge #5:
Do the following updates to the last plot we just made:
Update the colors using hex colors:
c('#E9602B','#2277A0','#CCB683')
.
Change the legend title to “IFOCE-affiliation”.
Save this plot object as “affil_plot”.
affil_plot <- ggplot(hdm_affil, aes(x = year, y = num_eaten)) +
geom_col(aes(fill = affiliated)) +
labs(x = "Year", y = "Hot Dogs and Buns Consumed") +
ggtitle("Nathan's Hot Dog Eating Contest Results, 1981-2022", subtitle = "(Male contestants only)") +
scale_fill_manual(values = c('#E9602B','#2277A0','#CCB683'),
name = "IFOCE-affiliation")
affil_plot
Play With Scales &
Coordinates
Now that the bones of the plot are in place, it’s time to tweak the
details.
The spacing’s a little funky down near the origin of the plot. The documentation
tells us that the defaults are c(0.05, 0)
for continuous
variables. The first number is multiplicative and the second is
additive.
The default was that 2.05 ((2022-1981)*.05+0) was added to the right
and left sides of the x-axis as padding, so the effective default limits
were c(1979, 2024)
.
Let’s tighten that up with the expand
property for the
scale_y_continuous
(we’ll also change the breaks for y-axis
tick marks here) and scale_x_continuous
settings:
affil_plot <- affil_plot +
scale_y_continuous(expand = c(0, 0),
breaks = seq(0, 70, 10)) +
scale_x_continuous(expand = c(0, 0))
affil_plot
That is perhaps too tight; note the lack of any space
between the bars and the y-axis on hte left.
Let’s loosen things up a bit by updating the plot coordinates.
Using coord_cartesian
is the preferred layer here
because (from the coord_cartesian
documentation): “setting
limits on the coordinate system will zoom the plot (like you’re looking
at it with a magnifying glass), and will not change the underlying data
like setting limits
on a scale will.”
In other words, setting limits
will actually result in
individual data points being included or excluded from the plot based on
whether they fall within the specified limits, which could have
unanticipated effects (for example, if your plot includes a line fit,
that line fit will be done using only the included data rather than all
of your data).
Lesson:
Don’t directly change limits
unless you really know what
you are doing! Most of the time, you want to change the coordinates
instead, and do any data point filtering outside of your plotting.
affil_plot <- affil_plot +
coord_cartesian(xlim = c(1980, 2023), ylim = c(0, 80))
affil_plot
Play With Theme
Settings
We will talk a lot more about themes and ggplot
later in
the term, but for now, the important thing to know is that most visual
aspects of the plot have a name (e.g. plot.title
),
and the theme()
function lets us tell ggplot
what any named part
of the plot should look like.
Let’s change some key theme settings:
affil_plot +
theme(plot.title = element_text(hjust = 0.5)) +
theme(axis.text = element_text(size = 12)) +
theme(panel.background = element_blank()) +
theme(axis.line.x = element_line(color = "gray80", size = 0.5)) +
theme(axis.ticks = element_line(color = "gray80", size = 0.5))
Warning: The `size` argument of `element_line()` is deprecated as of ggplot2 3.4.0.
ℹ Please use the `linewidth` argument instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
generated.
Lesson:
You can change almost anything that your heart desires to
change!
By default, plot titles in ggplot2
are left-aligned. For
hjust
:
0
== left
0.5
== centered
1
== right
We could also save all these as a custom theme. We are not fans of
the default font, so we are also going to change this. To do this, you
need to install the (extrafont
package)[https://github.com/wch/extrafont] and follow its setup
instructions before doing this next step.
hot_diggity <- theme(plot.title = element_text(hjust = 0.5),
axis.text = element_text(size = 12),
panel.background = element_blank(),
axis.line.x = element_line(color = "gray80", size = 0.5),
axis.ticks = element_line(color = "gray80", size = 0.5),
text = element_text(family = "Lato") # need extrafont for this
)
affil_plot + hot_diggity
We could also use someone else’s theme:
library(ggthemes)
affil_plot + theme_fivethirtyeight(base_family = "Lato")
affil_plot + theme_tufte( base_family = "Palatino")
The final thing we have to mess with is the x-axis ticks and labels.
We’ll do this in two steps, then override our previous layer
scale_x_continuous
.
# manually compute a list of years that we want labeled...
years_to_label <- seq(from = 1981, to = 2022, by = 4)
years_to_label
[1] 1981 1985 1989 1993 1997 2001 2005 2009 2013 2017 2021
# add a column to the dataframe containing what we want each year's label to be
hd_years <- hdm_affil %>%
distinct(year) %>%
mutate(year_lab = ifelse(year %in% years_to_label, year, ""))
# manually tell ggplot what to use for breaks and labels
affil_plot +
hot_diggity +
scale_x_continuous(expand = c(0, 0),
breaks = hd_years$year,
labels = hd_years$year_lab)
Scale for x is already present.
Adding another scale for x, which will replace the existing scale.
Final (final, final)
version
Don’t name your files “final” :)
All together in one chunk, here is our final (for now) plot! I’m also
adding some additional elements here to show you options:
nathan_plot <- ggplot(hdm_affil, aes(x = year, y = num_eaten)) +
geom_col(aes(fill = affiliated)) +
labs(x = "Year", y = "Hot Dogs and Buns Consumed") +
ggtitle("Nathan's Hot Dog Eating Contest Results, 1981-2022", subtitle = "(Male contestants only)") +
scale_fill_manual(values = c('#E9602B','#2277A0','#CCB683'),
name = "IFOCE-affiliation") +
hot_diggity +
scale_y_continuous(expand = c(0, 0),
breaks = seq(0, 70, 10)) +
scale_x_continuous(expand = c(0, 0),
breaks = hd_years$year,
labels = hd_years$year_lab) +
coord_cartesian(xlim = c(1980, 2023), ylim = c(0, 80))
nathan_plot
The fill legend is doing its job, here, but we might instead want to
use direct annotations on the plot itself, to make it easier and faster
to read.
ggplot
will let us add annotatations to the
plot- i.e., extra text, lines , etc. that are not derived from the data
in the plot, but are manually specified - using the
annotate()
function, which adds additional layers to the
plot. The way we are doing it below is a bit tedious, but demonstrates
how it works.
nathan_ann <- nathan_plot +
guides(fill="none") + # turn off the legend/guide for the "fill" aesthetic
coord_cartesian(xlim = c(1980, 2023), ylim = c(0, 90)) +
annotate('segment', x=1980.75, xend=2000.25, y= 30, yend=30, linewidth=0.5, color="#CCB683")+
annotate('segment', x=1980.75, xend=1980.75, y= 30, yend=28, linewidth=0.5, color="#CCB683") +
annotate('segment', x=2000.25, xend=2000.25, y= 30, yend=28, linewidth=0.5, color="#CCB683") +
annotate('segment', x=1990, xend=1990, y= 33, yend=30, linewidth=0.5, color="#CCB683") +
annotate('text', x=1990, y=36, label="No MLE/IFOCE Affiliation", color="#CCB683", family="Lato", hjust=0.5, size = 3) +
annotate('segment', x=2000.75, xend=2006.25, y= 58, yend=58, linewidth=0.5, color="#2277A0") +
annotate('segment', x=2000.75, xend=2000.75, y= 58, yend=56, linewidth=0.5, color="#2277A0") +
annotate('segment', x=2006.25, xend=2006.25, y= 58, yend=56, linewidth=0.5, color="#2277A0") +
annotate('segment', x=2003.5, xend=2003.5, y= 61, yend=58, linewidth=0.5, color="#2277A0") +
annotate('text', x=2003.5, y=65, label="MLE/IFOCE\nFormer Member", color="#2277A0", family="Lato", hjust=0.5, size = 3) +
annotate('segment', x=2006.75, xend=2022.25, y= 79, yend=79, linewidth=0.5, color="#E9602B") +
annotate('segment', x=2006.75, xend=2006.75, y= 79, yend=77, linewidth=0.5, color="#E9602B") +
annotate('segment', x=2022.25, xend=2022.25, y= 79, yend=77, linewidth=0.5, color="#E9602B") +
annotate('segment', x=2015, xend=2015, y= 82, yend=79, linewidth=0.5, color="#E9602B") +
annotate('text', x=2015, y=86, label="MLE/IFOCE Current Member", color="#E9602B", family="Lato", hjust=0.5, size = 3)
Coordinate system already present. Adding new coordinate system, which will
replace the existing one.
nathan_ann
Finally, adding in another layer of data, including information about
female contestants:
hdm_females <- read_csv(here::here("data", "hot_dog_contest_with_affiliation.csv"),
col_types = cols(
affiliated = col_factor(levels = NULL),
gender = col_factor(levels = NULL)
)) %>%
mutate(post_ifoce = year >= 1997) %>%
filter(year >= 1981 & gender == "female")
glimpse(hdm_females)
Rows: 12
Columns: 6
$ year <dbl> 2022, 2021, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 2013,…
$ gender <fct> female, female, female, female, female, female, female, fem…
$ name <chr> "Miki Sudo", "Michelle Lesco", "Miki Sudo", "Miki Sudo", "M…
$ num_eaten <dbl> 40.00, 30.75, 48.50, 31.00, 37.00, 41.00, 38.00, 38.00, 34.…
$ affiliated <fct> current, current, current, current, current, current, curre…
$ post_ifoce <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,…
nathan_w_females <- nathan_ann +
# add in the female data, and manually set a fill color
geom_col(data = hdm_females,
width = 0.75,
fill = "#F68A39") +
labs(subtitle = NULL) # no longer need the subtitle warning about male-only data!
nathan_w_females
And adding a final caption:
caption <- paste(strwrap("* From 2011 on, separate Men's and Women's prizes have been awarded. All female champions to date have been MLE/IFOCE-affiliated.", 70), collapse="\n")
nathan_w_females +
# now an asterisk to set off the female scores, and a caption
annotate('text', x = 2011, y = 36, label="*", family = "Lato", size = 8) +
labs(caption = caption) +
theme(plot.caption = element_text(family = "Lato", size=8, hjust=0, margin=margin(t=15)))
LS0tCnRpdGxlOiAiTGFiIDAxOiBOYXRoYW4ncyBIb3QtRG9nIEVhdGluZyBDb250ZXN0IgpzdWJ0aXRsZTogIkJNSSA1LzYyNSIKYXV0aG9yOiAiQWxpc29uIEhpbGwgKHdpdGggbWlub3IgbW9kaWZpY2F0aW9ucyBieSBTdGV2ZW4gQmVkcmljaykiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IGZsYXRseQogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKICAgIHRvY19kZXB0aDogMgogICAgbnVtYmVyX3NlY3Rpb25zOiBUUlVFCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFLCBjYWNoZSA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZXJyb3IgPSBUUlVFLCBjb21tZW50ID0gTkEsIHdhcm5pbmdzID0gRkFMU0UsIGVycm9ycyA9IEZBTFNFLCBtZXNzYWdlcyA9IEZBTFNFLCB0aWR5ID0gRkFMU0UsIGNhY2hlID0gVFJVRSkKYGBgCgpgYGB7ciBsb2FkLXBhY2thZ2VzLCBpbmNsdWRlID0gRkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGV4dHJhZm9udCkKYGBgCgojIEdvYWxzIGZvciBMYWIgMDEKCi0gR2V0IHlvdXIgZmVldCB3ZXQhCi0gSW5ub2N1bGF0ZSB5b3UgYWdhaW5zdCBgZ2dwbG90MmAgZXJyb3JzLSB3ZSBhbGwgZ2V0IHRoZW0hCi0gR2V0IGV4cG9zZWQgdG8gdGhlICpyYW5nZSogb2YgdGhpbmdzIHlvdSBjYW4gZG8sIGJlZm9yZSB3ZSBnbyAqKmRlZXAqKi4uLgotIERldmVsb3AgeW91ciAqb3duKiAqKnBlcnNvbmFsKiogcHJlZmVyZW5jZXMgZm9yIGRhdGEgdmlzdWFsaXphdGlvbnMhCiAgICAtIERvIHlvdSBsaWtlIG9yIGhhdGUgZ3JpZGxpbmVzPwogICAgLSBXaGF0IGZvbnRzIGRvIHlvdSBmaW5kIHBsZWFzYW50IHRvIHJlYWQ/CiAgICAtIFdoYXQga2luZHMgb2YgY29sb3JzIGRvIHlvdSBsaWtlPwogICAgLSBBcmUgeW91IHRlYW0gYHRoZW1lX2dyYXlgIG9yIGB0aGVtZV9id2AgKG9yIGB0aGVtZV9taW5pbWFsYCk/CiAgICAKVGhlc2UgYXJlIGltcG9ydGFudCBxdWVzdGlvbnMsIGFuZCBJIHdhbnQgeW91IHRvIGRldmVsb3AgKHdlbGwtaW5mb3JtZWQpIG9waW5pb25zIG9uIHRoZXNlIG1hdHRlcnMhCiFbXShpbWFnZXMvdGhlbWUtdGVhbS10d2VldHMucG5nKQoKTW9zdCBvZiB0aGUgdGhpbmdzIHdlIGNvdmVyIHRvZGF5LCB3ZSB3aWxsIGJlIHJlLXZpc2l0aW5nIGxhdGVyIGluIHRoZSB0ZXJtOyB5b3UgYXJlIG5vdCBleHBlY3RlZCB0byBiZSBmYW1pbGlhciB3aXRoIGFsbCBvZiB0aGUgUiBmdW5jdGlvbnMgb3IgcGF0dGVybnMgdGhhdCB3ZSBhcmUgdXNpbmcuCgpPdGhlciB0aGluZ3MgdG8gdGhpbmsgYWJvdXQgZm9yIHRoaXMgbGFiOgoKLSBUcnkgbm90IHRvIGNvcHktYW5kLXBhc3RlIGNvZGU6CiAgLSBCZWNvbWluZyBlZmZpY2llbnQvcHJvZmljaWVudCB3aXRoIFIgZGVwZW5kcyBvbiBidWlsZGluZyBtdXNjbGUgbWVtb3J5LCBhbmQgdGhlIG9ubHkgd2F5IHRvIGRvIHRoYXQgaXMgdG8gdHlwZQogIC0gQnkgdHlwaW5nLCB5b3UgX3dpbGxfIGludHJvZHVjZSBlcnJvcnMsIGFuZCB0aGlzIGlzIGEgZ29vZCBjaGFuY2UgdG8gZ2V0IHByYWN0aWNlIGF0IGludGVycHJldGluZyBhbmQgZml4aW5nIHRoZW0uCi0gVGFrZSBub3RlIG9mIHN0ZXBzIG9yIGZ1bmN0aW9ucyB0aGF0IGludm9sdmUgYml0cyBvZiBSIHRoYXQgeW91IGFyZSB1bmZhbWlsaWFyIHdpdGgtIHRoaXMgbGFiIHdpbGwgaGVscCB5b3UgaWRlbnRpZnkgcGFydGljdWxhciBhcmVhcyB0aGF0IHlvdSBhcmUgY29tZm9ydGFibGUgd2l0aCBhcyB3ZWxsIGFzIGFyZWFzIHlvdSB3aWxsIHdhbnQgdG8gZm9jdXMgb24uCgojIE5hdGhhbidzIEhvdCBEb2cgRWF0aW5nIENvbnRlc3QKCiFbXShodHRwczovL2kwLndwLmNvbS9mbG93aW5nZGF0YS5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMDkvMDYvaG90LWRvZ3MxLmdpZj96b29tPTImZml0PTkwMCUyQzQyMykKClRoaXMgaW5jbHVkZXMgYSByZWNvbnN0cnVjdGlvbiBvZiBbTmF0aGFuIFlhdSdzIGhvdCBkb2cgY29udGVzdCBleGFtcGxlXShodHRwOi8vZmxvd2luZ2RhdGEuY29tLzIwMDkvMDcvMDIvd2hvcy1nb2luZy10by13aW4tbmF0aGFucy1ob3QtZG9nLWVhdGluZy1jb250ZXN0L2hvdC1kb2dzLTIvKSwgYXMgaW50ZXJwcmV0ZWQgYnkgSmFja2llIFdpcnosIHBvcnRlZCBpbnRvIFIgYW5kIGBnZ3Bsb3QyYCBieSBTdGV2ZW4gQmVkcmljayBmb3IgYSB3b3Jrc2hvcCBmb3IgdGhlIFtPSFNVIERhdGEgU2NpZW5jZSBJbnN0aXR1dGVdKGh0dHBzOi8vb2hzdWxpYnJhcnktZGF0YXNjaWVuY2VpbnN0aXR1dGUuZ2l0aHViLmlvKSwgYW5kIGZpbmFsbHkgYWRhcHRlZCwgbWFkZSBpZGlvbWF0aWMsIGFuZCBpbXByb3ZlZCBieSBBbGlzb24gSGlsbCBmb3IgYWxsIHlvdSBpbnRyZXBpZCBEYXRhLVZpei1vbmF1dHMhCgoKRmlyc3QsIHdlIGxvYWQgb3VyIHBhY2thZ2VzOiAKCgpgYGB7ciBldmFsPUZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZXh0cmFmb250KQpsaWJyYXJ5KGhlcmUpCmBgYAoKCiMgUmVhZCBpbiBhbmQgd3JhbmdsZSBkYXRhCgpOZXh0LCB3ZSBsb2FkIHNvbWUgZGF0YS4gSW4gdGhlIFBvc2l0IENsb3VkIHByb2plY3QgZm9yIHRoaXMgbGFiLCB5b3Ugd2lsbCBzZWUgYSBgZGF0YWAgZGlyZWN0b3J5IHdpdGggdGhlIG5lY2Vzc2FyeSBmaWxlcy4gV2UgY2FuIHJlYWQgaXQgaW4gdXNpbmcgYHJlYWRfY3N2YCwgYW5kIGFsb25nIHRoZSB3YXkgdXNlIGBjb2xfZmFjdG9yYCB0byB0ZWxsIGl0IGhvdyB0byBoYW5kbGUgdGhlIGdlbmRlciBjb2x1bW4uCgpgYGB7cn0KaG90X2RvZ3MgPC0gcmVhZF9jc3YoaGVyZTo6aGVyZSgiZGF0YSIsICJob3RfZG9nX2NvbnRlc3QuY3N2IiksIAogICAgY29sX3R5cGVzID0gY29scygKICAgICAgZ2VuZGVyID0gY29sX2ZhY3RvcihsZXZlbHMgPSBOVUxMKQogICAgKSkKYGBgCgojIyBWYWxpZGF0aW9uCgpDaGVjayBpdCBvdXQsIG9uY2UgaXQgaXMgcmVhZCBpbiwgYW5kIG1ha2Ugc3VyZSBpdCBsb29rcyBsaWtlIHRoaXMhCgpgYGB7cn0KZ2xpbXBzZShob3RfZG9ncykKaG90X2RvZ3MKYGBgCgpBdCB0aGlzIHBvaW50LCBmb2xsb3cgdGhlIEhMTyBwcm9jZXNzIGFuZCBmYW1pbGlhcml6ZSB5b3Vyc2VsZiB3aXRoIHRoZSBjb2x1bW5zIGFuZCB0aGVpciBjb250ZW50cy4gUXVlc3Rpb25zIHRvIGFzazoKCjEuIEFyZSB0aGUgdHlwZXMgb2YgdGhlIGNvbHVtbnMgd2hhdCB5b3Ugd291bGQgZXhwZWN0PyBOb3RlIHRoYXQgdGhpcyByZXF1aXJlcyB0aGF0IHlvdSBoYXZlIHNvbWUgbm90aW9uIG9mIHdoYXQgdG8gZXhwZWN0ISBQb3NzaWJsZSB0aGluZ3MgdG8gdGhpbmsgYWJvdXQ6CiAgMS4gV2hhdCB0eXBlIG1ha2VzIHNlbnNlIGZvciBhIGNvbHVtbiByZXByZXNlbnRpbmcgYSB5ZWFyPwogIDIuIFdoYXQgYWJvdXQgdGhlIGBudW1fZWF0ZW5gIGNvbHVtbj8KICAzLiBXaGF0IGFib3V0IHRoZSBgZ2VuZGVyYCBjb2x1bW4/IFdoYXQgYXNzdW1wdGlvbnMKMi4gV2hhdCBhcmUgdGhlIG1pbmltdW0gYW5kIG1heGltdW0gdmFsdWVzIGluIHRoZSBudW1lcmljIGNvbHVtbnM/IEFyZSB0aGUgdmFsdWVzIGluIHRoZSByYW5nZSB5b3Ugd291bGQgZXhwZWN0PwozLiBBcmUgdGhlcmUgYW55IG1pc3NpbmcgdmFsdWVzPwoKSW4gYWRkaXRpb24gdG8gYGdsaW1wc2UoKWAsIHRyeSBsb2FkaW5nIChub3QgaW5zdGFsbGluZykgdGhlIGBza2ltcmAgcGFja2FnZSBhbmQgdXNpbmcgaXRzIGBza2ltKClgIGZ1bmN0aW9uIG9uIHRoZSBgaG90X2RvZ3NgIGRhdGEgZnJhbWUuCgojIyBBZGRpbmcgYSB2YXJpYWJsZQoKSW4gYWRkaXRpb24gdG8gdGhlIGluZm9ybWF0aW9uIHRoYXQgaXMgYWxyZWFkeSBfaW5fIHRoZSBkYXRhc2V0IGl0c2VsZiwgd2Uga25vdyB0aGF0IHdlIHdpbGwgYWxzbyBiZSB3YW50aW5nIHRvIHNvbWVob3cgaW5jbHVkZSBpbmZvcm1hdGlvbiBhYm91dCB3aGV0aGVyIGEgZ2l2ZW4geWVhciB3YXMgYmVmb3JlIG9yIGFmdGVyIHRoZSBpbmNvcnBvcmF0aW9uIG9mIHRoZSBjb21wZXRpdGl2ZSBlYXRpbmcgbGVhZ3VlLiBMZXQncyBhZGQgYW4gX2luZGljYXRvciB2YXJpYWJsZV8gdG8gdGhlIGRhdGEgdXNpbmcgYG11dGF0ZSgpYC4gQWxzbywgdGhlIGRhdGEncyBhIGxpdHRsZSBza2V0Y2h5IHByZS0xOTgxLCBhbmQgZm9yIG91ciBwdXJwb3NlcyB0b2RheSB3ZSdsbCBiZSBmb2N1c2luZyBvbiBtYWxlcyBvbmx5LCBzbyBsZXQncyBkbyBzb21lIGBmaWx0ZXJgaW5nLCBhcyB3ZWxsOgoKYGBge3J9CmhvdF9kb2dzIDwtIGhvdF9kb2dzICU+JSAKICBtdXRhdGUocG9zdF9pZm9jZSA9IHllYXIgPj0gMTk5NykgJT4lIAogIGZpbHRlcih5ZWFyID49IDE5ODEgJiBnZW5kZXIgPT0gJ21hbGUnKQpob3RfZG9ncwpgYGAKCgoKIyBQbG90IFRoZSBEYXRhCgpOb3cgbGV0J3MgdHJ5IG1ha2luZyBhIGZpcnN0IGNyYWNrIGF0IGEgcGxvdDoKCmBgYHtyfQpnZ3Bsb3QoaG90X2RvZ3MsIGFlcyh4ID0geWVhciwgeSA9IG51bV9lYXRlbikpICsgCiAgZ2VvbV9jb2woKQpgYGAKCk5vdGUgdGhhdCBvdXIgZGF0YSBpcyBhbHJlYWR5IGluICJjb3VudGVkIiBmb3JtLCBzbyB3ZSdyZSB1c2luZyBgZ2VvbV9jb2woKWAgaW5zdGVhZCBvZiBgZ2VvbV9iYXIoKWAuCgpXZSB3aWxsIG5vdyBwcm9ncmVzc2l2ZWx5IGltcHJvdmUgdGhpcyB2aXN1YWxpemF0aW9uLCBvbmUgc3RlcCBhdCBhIHRpbWUuCgojIEFkZCBBeGlzIExhYmVscyBBbmQgVGl0bGUKCgpgYGB7cn0KZ2dwbG90KGhvdF9kb2dzLCBhZXMoeCA9IHllYXIsIHkgPSBudW1fZWF0ZW4pKSArIAogIGdlb21fY29sKCkgKwogIGxhYnMoeCA9ICJZZWFyIiwgeSA9ICJIb3QgRG9ncyBhbmQgQnVucyBDb25zdW1lZCIpICsKICBnZ3RpdGxlKCJOYXRoYW4ncyBIb3QgRG9nIEVhdGluZyBDb250ZXN0IFJlc3VsdHMsIDE5ODEtMjAyMiIsIHN1YnRpdGxlID0gIihNYWxlIGNvbnRlc3RhbnRzIG9ubHkpIikKYGBgCgojIFBsYXkgV2l0aCBDb2xvcnMKCjxkaXYgY2xhc3M9InBhbmVsIHBhbmVsLXN1Y2Nlc3MiPgogIDxkaXYgY2xhc3M9InBhbmVsLWhlYWRpbmciPkNoYWxsZW5nZSAjMTo8L2Rpdj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1ib2R5Ij4KTWFrZSAzIHZlcnNpb25zIG9mIHRoZSBsYXN0IHBsb3Qgd2UganVzdCBtYWRlOgoKKiBfX0luIHRoZSBmaXJzdCxfXyBtYWtlIGFsbCB0aGUgY29sdW1ucyBvdXRsaW5lZCBpbiAid2hpdGUiLgoqIF9fSW4gdGhlIHNlY29uZCxfXyBtYWtlIGFsbCB0aGUgY29sdW1ucyBvdXRsaW5lZCBpbiAid2hpdGUiIGFuZCBmaWxsZWQgaW4gIm5hdnlibHVlIi4KKiBfX0luIHRoZSB0aGlyZCxfXyBtYWtlIGFsbCB0aGUgY29sdW1ucyBvdXRsaW5lZCBpbiAid2hpdGUiIGFuZCBmaWxsZWQgaW4gYWNjb3JkaW5nIHRvIHdoZXRoZXIgb3Igbm90IGBwb3N0X2lmb2NlYCBpcyBUUlVFIG9yIEZBTFNFICh1c2UgZGVmYXVsdCBjb2xvcnMgZm9yIG5vdykuCgpfSElOVDpfIGBjb2xvcmAgYW5kIGBmaWxsYCBhcmUgdHdvIG9mIGBnZ3Bsb3RgJ3MgYWVzdGhldGljIG1hcHBpbmcgdmFyaWFibGVzIChpLmUuLCAidGhpbmdzIGFib3V0IGhvdyB0aGUgcGxvdCBsb29rcyB0aGF0IHdlIGdldCB0byBzcGVjaWZ5IikKCiAgPC9kaXY+CjwvZGl2PgoKCgpgYGB7cn0KZ2dwbG90KGhvdF9kb2dzLCBhZXMoeCA9IHllYXIsIHkgPSBudW1fZWF0ZW4pKSArIAogIGdlb21fY29sKGNvbG91ciA9ICJ3aGl0ZSIpICsgCiAgbGFicyh4ID0gIlllYXIiLCB5ID0gIkhvdCBEb2dzIGFuZCBCdW5zIENvbnN1bWVkIikgKwogIGdndGl0bGUoIk5hdGhhbidzIEhvdCBEb2cgRWF0aW5nIENvbnRlc3QgUmVzdWx0cywgMTk4MS0yMDIyIiwgc3VidGl0bGUgPSAiKE1hbGUgY29udGVzdGFudHMgb25seSkiKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoaG90X2RvZ3MsIGFlcyh4ID0geWVhciwgeSA9IG51bV9lYXRlbikpICsgCiAgZ2VvbV9jb2woY29sb3VyID0gIndoaXRlIiwgZmlsbCA9ICJuYXZ5Ymx1ZSIpICsgCiAgbGFicyh4ID0gIlllYXIiLCB5ID0gIkhvdCBEb2dzIGFuZCBCdW5zIENvbnN1bWVkIikgKwogIGdndGl0bGUoIk5hdGhhbidzIEhvdCBEb2cgRWF0aW5nIENvbnRlc3QgUmVzdWx0cywgMTk4MS0yMDIyIiwgc3VidGl0bGUgPSAiKE1hbGUgY29udGVzdGFudHMgb25seSkiKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoaG90X2RvZ3MsIGFlcyh4ID0geWVhciwgeSA9IG51bV9lYXRlbikpICsgCiAgZ2VvbV9jb2woYWVzKGZpbGwgPSBwb3N0X2lmb2NlKSwgY29sb3VyID0gIndoaXRlIikgKyAKICBsYWJzKHggPSAiWWVhciIsIHkgPSAiSG90IERvZ3MgYW5kIEJ1bnMgQ29uc3VtZWQiKSArCiAgZ2d0aXRsZSgiTmF0aGFuJ3MgSG90IERvZyBFYXRpbmcgQ29udGVzdCBSZXN1bHRzLCAxOTgxLTIwMjIiLCBzdWJ0aXRsZSA9ICIoTWFsZSBjb250ZXN0YW50cyBvbmx5KSIpCmBgYAoKCgo8ZGl2IGNsYXNzPSJwYW5lbCBwYW5lbC1zdWNjZXNzIj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj5DaGFsbGVuZ2UgIzI6PC9kaXY+CiAgPGRpdiBjbGFzcz0icGFuZWwtYm9keSI+CldoYXQgaWYgeW91IHdhbnQgdG8gY2hhbmdlIHRoZSBsZWdlbmQgaW4gdGhlIGxhc3QgcGxvdCB5b3UgbWFkZT8gVXNlIGdvb2dsZSB0byBmaWd1cmUgb3V0IGhvdyB0byBkbyB0aGUgZm9sbG93aW5nOgoKKiBEZWxldGUgdGhlIGxlZ2VuZCB0aXRsZQoqIE1ha2UgdGhlIGxlZ2VuZCB0ZXh0IGVpdGhlciAiUG9zdC1JRk9DRSIgb3IgIlByZS1JRk9DRSIuCgpISU5UOiBpbiBgZ2dwbG90YCwgbGVnZW5kcyBhcmUgY29udHJvbGxlZCBieSB0aGUgcmVsZXZhbnQgc2NhbGUgKGNvbG9yLCBmaWxsLCBldGMuKSB0aGF0IHRoZXkgYXJlIG1hcHBlZCB0by4KCiAgPC9kaXY+CjwvZGl2PgoKYGBge3J9CmdncGxvdChob3RfZG9ncywgYWVzKHggPSB5ZWFyLCB5ID0gbnVtX2VhdGVuKSkgKyAKICBnZW9tX2NvbChhZXMoZmlsbCA9IHBvc3RfaWZvY2UpLCBjb2xvdXIgPSAid2hpdGUiKSArIAogIGxhYnMoeCA9ICJZZWFyIiwgeSA9ICJIb3QgRG9ncyBhbmQgQnVucyBDb25zdW1lZCIpICsKICBnZ3RpdGxlKCJOYXRoYW4ncyBIb3QgRG9nIEVhdGluZyBDb250ZXN0IFJlc3VsdHMsIDE5ODEtMjAyMiIsIHN1YnRpdGxlID0gIihNYWxlIGNvbnRlc3RhbnRzIG9ubHkpIikgKwogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9ICIiLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIlByZS1JRk9DRSIsICJQb3N0LUlGT0NFIikpCmBgYAoKIyBDaGFuZ2UgVGhlIERhdGFzZXQKCk5vdywgbGV0J3MgY2hhbmdlIHRoZSBxdWVzdGlvbiBhIGxpdHRsZSBiaXQuIFVwIHRvIHRoaXMgcG9pbnQsIHdlIGhhdmUgbG9va2VkIGF0IEhEQiBwZXJmb3JtYW5jZSByZWxhdGl2ZSB0byAgdGhlIF9jcmVhdGlvbl8gb2YgdGhlIElGT0NFLiBXaGF0IGlmIHdoYXQgbWF0dGVycyBpcyB0aGUgX2FmZmlsaWF0aW9uXyBvZiB0aGUgY29udGVzdGFudHMgKGkuZS4sIHdoZXRoZXIgb3Igbm90IHRoZSBjb250ZXN0YW50cyBhcmUgbWVtYmVycyBvZiB0aGUgSUZPQ0Ugb3Igbm90KT8gV2UnbGwgbmVlZCBzb21lIGRpZmZlcmVudCBkYXRhIGZvciB0aGlzLiBUaHJvdWdoIHRoZSBfTWFnaWMgT2YgRGF0YSBTY2llbmNl4oSiXywgd2UgaGF2ZSBkdWcgdGhhdCBpbmZvcm1hdGlvbiB1cCBhbmQgcHV0IGl0IGludG8gYW4gZXhwYW5kZWQgdmVyc2lvbiBvZiBvdXIgQ1NWIGZpbGUsIHdoaWNoIHlvdSBjYW4gZmluZCBpbiB0aGUgZGF0YSBkaXJlY3RvcnkuCgoKPGRpdiBjbGFzcz0icGFuZWwgcGFuZWwtc3VjY2VzcyI+CiAgPGRpdiBjbGFzcz0icGFuZWwtaGVhZGluZyI+Q2hhbGxlbmdlICMzOjwvZGl2PgogIDxkaXYgY2xhc3M9InBhbmVsLWJvZHkiPgpMZXQncyB3b3JrIHdpdGggdGhpcyBuZXcgZGF0YXNldCEgRG8gdGhlIGZvbGxvd2luZzoKCiogUmVhZCBpbiB0aGUgImhvdF9kb2dfY29udGVzdF93aXRoX2FmZmlsaWF0aW9uLmNzdiIgZGF0YSBmaWxlLCB1c2luZyBgY29sX3R5cGVzYCB0byByZWFkIGluIGBhZmZpbGlhdGVkYCBhbmQgYGdlbmRlcmAgYXMgZmFjdG9ycy4gCiogV2l0aGluIGEgYG11dGF0ZWAsIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSBjYWxsZWQgYHBvc3RfaWZvY2VgIHRoYXQgaXMgVFJVRSBpZiBgeWVhcmAgaXMgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIDE5OTcuIAoqIEFsc28gYGZpbHRlcmAgdGhlIG5ldyBkYXRhIGZvciBvbmx5IHllYXJzIDE5ODEgYW5kIGFmdGVyLCBhbmQgb25seSBmb3IgbWFsZSBjb21wZXRpdG9ycy4KICA8L2Rpdj4KPC9kaXY+CgpgYGB7ciBldmFsID0gRkFMU0V9CmhkbV9hZmZpbCA8LSByZWFkX2NzdihoZXJlOjpoZXJlKCJkYXRhIiwgImhvdF9kb2dfY29udGVzdF93aXRoX2FmZmlsaWF0aW9uLmNzdiIpLCAKICAgIGNvbF90eXBlcyA9IGNvbHMoCiAgICAgIGFmZmlsaWF0ZWQgPSBjb2xfZmFjdG9yKGxldmVscyA9IE5VTEwpLCAKICAgICAgZ2VuZGVyID0gY29sX2ZhY3RvcihsZXZlbHMgPSBOVUxMKQogICAgICApKSAlPiUgCiAgbXV0YXRlKHBvc3RfaWZvY2UgPSB5ZWFyID49IDE5OTcpICU+JSAKICBmaWx0ZXIoeWVhciA+PSAxOTgxICYgZ2VuZGVyID09ICJtYWxlIikgCmBgYAoKCmBgYHtyfQpoZG1fYWZmaWwgPC0gcmVhZF9jc3YoaGVyZTo6aGVyZSgiZGF0YSIsICJob3RfZG9nX2NvbnRlc3Rfd2l0aF9hZmZpbGlhdGlvbi5jc3YiKSwgCiAgICBjb2xfdHlwZXMgPSBjb2xzKAogICAgICBhZmZpbGlhdGVkID0gY29sX2ZhY3RvcihsZXZlbHMgPSBOVUxMKSwgCiAgICAgIGdlbmRlciA9IGNvbF9mYWN0b3IobGV2ZWxzID0gTlVMTCkKICAgICAgKSkgJT4lIAogIG11dGF0ZShwb3N0X2lmb2NlID0geWVhciA+PSAxOTk3KSAlPiUgCiAgZmlsdGVyKHllYXIgPj0gMTk4MSAmIGdlbmRlciA9PSAibWFsZSIpIApnbGltcHNlKGhkbV9hZmZpbCkKYGBgCgo8ZGl2IGNsYXNzPSJwYW5lbCBwYW5lbC1zdWNjZXNzIj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj5DaGFsbGVuZ2UgIzQ6PC9kaXY+CiAgPGRpdiBjbGFzcz0icGFuZWwtYm9keSI+CkxldCdzIGRvIHNvbWUgYmFzaWMgRURBIHdpdGggdGhpcyBuZXcgZGF0YXNldCEgRG8gdGhlIGZvbGxvd2luZzoKCiogVXNlIGBkcGx5cjo6ZGlzdGluY3RgIHRvIGZpZ3VyZSBvdXQgaG93IG1hbnkgdW5pcXVlIHZhbHVlcyB0aGVyZSBhcmUgb2YgYGFmZmlsaWF0ZWRgLgoqIFVzZSBgZHBseXI6OmNvdW50YCB0byBjb3VudCB0aGUgbnVtYmVyIG9mIHJvd3MgZm9yIGVhY2ggdW5pcXVlIHZhbHVlIG9mIGBhZmZpbGlhdGVkYDsgdXNlIGA/Y291bnRgIHRvIGZpZ3VyZSBvdXQgaG93IHRvIHNvcnQgdGhlIGNvdW50cyBpbiBkZXNjZW5kaW5nIG9yZGVyLgogIDwvZGl2Pgo8L2Rpdj4KCmBgYHtyfQpoZG1fYWZmaWwgJT4lIAogIGRpc3RpbmN0KGFmZmlsaWF0ZWQpCmhkbV9hZmZpbCAlPiUgCiAgY291bnQoYWZmaWxpYXRlZCwgc29ydCA9IFRSVUUpCmBgYAoKCk5vdyBsZXQncyBwbG90IHRoaXMgbmV3IGRhdGEsIGFuZCBmaWxsIHRoZSBjb2x1bW5zIGFjY29yZGluZyB0byBvdXIgbmV3IGBhZmZpbGlhdGVkYCBjb2x1bW4uCgpgYGB7cn0KZ2dwbG90KGhkbV9hZmZpbCwgYWVzKHggPSB5ZWFyLCB5ID0gbnVtX2VhdGVuKSkgKyAKICBnZW9tX2NvbChhZXMoZmlsbCA9IGFmZmlsaWF0ZWQpKSArIAogIGxhYnMoeCA9ICJZZWFyIiwgeSA9ICJIb3QgRG9ncyBhbmQgQnVucyBDb25zdW1lZCIpICsKICBnZ3RpdGxlKCJOYXRoYW4ncyBIb3QgRG9nIEVhdGluZyBDb250ZXN0IFJlc3VsdHMsIDE5ODEtMjAyMiIsIHN1YnRpdGxlID0gIihNYWxlIGNvbnRlc3RhbnRzIG9ubHkpIikKYGBgCgo8ZGl2IGNsYXNzPSJwYW5lbCBwYW5lbC1zdWNjZXNzIj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj5DaGFsbGVuZ2UgIzU6PC9kaXY+CiAgPGRpdiBjbGFzcz0icGFuZWwtYm9keSI+CkRvIHRoZSBmb2xsb3dpbmcgdXBkYXRlcyB0byB0aGUgbGFzdCBwbG90IHdlIGp1c3QgbWFkZToKCiogVXBkYXRlIHRoZSBjb2xvcnMgdXNpbmcgaGV4IGNvbG9yczogYGMoJyNFOTYwMkInLCcjMjI3N0EwJywnI0NDQjY4MycpYC4gCiogQ2hhbmdlIHRoZSBsZWdlbmQgdGl0bGUgdG8gIklGT0NFLWFmZmlsaWF0aW9uIi4gCiogU2F2ZSB0aGlzIHBsb3Qgb2JqZWN0IGFzICJhZmZpbF9wbG90Ii4KICA8L2Rpdj4KPC9kaXY+CgpgYGB7cn0KYWZmaWxfcGxvdCA8LSBnZ3Bsb3QoaGRtX2FmZmlsLCBhZXMoeCA9IHllYXIsIHkgPSBudW1fZWF0ZW4pKSArIAogIGdlb21fY29sKGFlcyhmaWxsID0gYWZmaWxpYXRlZCkpICsgCiAgbGFicyh4ID0gIlllYXIiLCB5ID0gIkhvdCBEb2dzIGFuZCBCdW5zIENvbnN1bWVkIikgKwogIGdndGl0bGUoIk5hdGhhbidzIEhvdCBEb2cgRWF0aW5nIENvbnRlc3QgUmVzdWx0cywgMTk4MS0yMDIyIiwgc3VidGl0bGUgPSAiKE1hbGUgY29udGVzdGFudHMgb25seSkiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygnI0U5NjAyQicsJyMyMjc3QTAnLCcjQ0NCNjgzJyksCiAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJJRk9DRS1hZmZpbGlhdGlvbiIpCmFmZmlsX3Bsb3QKYGBgCgojIFBsYXkgV2l0aCBTY2FsZXMgJiBDb29yZGluYXRlcwoKTm93IHRoYXQgdGhlIGJvbmVzIG9mIHRoZSBwbG90IGFyZSBpbiBwbGFjZSwgaXQncyB0aW1lIHRvIHR3ZWFrIHRoZSBkZXRhaWxzLgoKVGhlIHNwYWNpbmcncyBhIGxpdHRsZSBmdW5reSBkb3duIG5lYXIgdGhlIG9yaWdpbiBvZiB0aGUgcGxvdC4gVGhlIFtkb2N1bWVudGF0aW9uXShodHRwOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9zY2FsZV9jb250aW51b3VzLmh0bWwpIHRlbGxzIHVzIHRoYXQgdGhlIGRlZmF1bHRzIGFyZSBgYygwLjA1LCAwKWAgZm9yIGNvbnRpbnVvdXMgdmFyaWFibGVzLiBUaGUgZmlyc3QgbnVtYmVyIGlzIG11bHRpcGxpY2F0aXZlIGFuZCB0aGUgc2Vjb25kIGlzIGFkZGl0aXZlLiAKClRoZSBkZWZhdWx0IHdhcyB0aGF0IDIuMDUgKCgyMDIyLTE5ODEpKi4wNSswKSB3YXMgYWRkZWQgdG8gdGhlIHJpZ2h0IGFuZCBsZWZ0IHNpZGVzIG9mIHRoZSB4LWF4aXMgYXMgcGFkZGluZywgc28gdGhlIGVmZmVjdGl2ZSBkZWZhdWx0IGxpbWl0cyB3ZXJlIGBjKDE5NzksIDIwMjQpYC4KCkxldCdzIHRpZ2h0ZW4gdGhhdCB1cCB3aXRoIHRoZSBgZXhwYW5kYCBwcm9wZXJ0eSBmb3IgdGhlIGBzY2FsZV95X2NvbnRpbnVvdXNgICh3ZSdsbCBhbHNvIGNoYW5nZSB0aGUgYnJlYWtzIGZvciB5LWF4aXMgdGljayBtYXJrcyBoZXJlKSBhbmQgYHNjYWxlX3hfY29udGludW91c2Agc2V0dGluZ3M6CgpgYGB7cn0KYWZmaWxfcGxvdCA8LSBhZmZpbF9wbG90ICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLCA3MCwgMTApKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCkpCmFmZmlsX3Bsb3QKYGBgCgoKClRoYXQgaXMgcGVyaGFwcyBfdG9vXyB0aWdodDsgbm90ZSB0aGUgbGFjayBvZiBhbnkgc3BhY2UgYmV0d2VlbiB0aGUgYmFycyBhbmQgdGhlIHktYXhpcyBvbiBodGUgbGVmdC4KCkxldCdzIGxvb3NlbiB0aGluZ3MgdXAgYSBiaXQgYnkgdXBkYXRpbmcgdGhlIHBsb3QgY29vcmRpbmF0ZXMuIAoKPGRpdiBjbGFzcz0icGFuZWwgcGFuZWwtc3VjY2VzcyI+CiAgPGRpdiBjbGFzcz0icGFuZWwtaGVhZGluZyI+Q2hhbGxlbmdlICM2OjwvZGl2PgogIDxkaXYgY2xhc3M9InBhbmVsLWJvZHkiPgpVc2UgYGNvb3JkX2NhcnRlc2lhbmAgdG86CgoqIFNldCB0aGUgeC1heGlzIHJhbmdlIHRvIDE5ODAtMjAyMwoqIFNldCB0aGUgeS1heGlzIHJhbmdlIHRvIDAtODAKICA8L2Rpdj4KPC9kaXY+CgpVc2luZyBgY29vcmRfY2FydGVzaWFuYCBpcyB0aGUgcHJlZmVycmVkIGxheWVyIGhlcmUgYmVjYXVzZSAoZnJvbSB0aGUgYGNvb3JkX2NhcnRlc2lhbmAgZG9jdW1lbnRhdGlvbik6ICJzZXR0aW5nIGxpbWl0cyBvbiB0aGUgY29vcmRpbmF0ZSBzeXN0ZW0gd2lsbCB6b29tIHRoZSBwbG90IChsaWtlIHlvdSdyZSBsb29raW5nIGF0IGl0IHdpdGggYSBtYWduaWZ5aW5nIGdsYXNzKSwgYW5kIHdpbGwgbm90IGNoYW5nZSB0aGUgdW5kZXJseWluZyBkYXRhIGxpa2Ugc2V0dGluZyBgbGltaXRzYCBvbiBhIHNjYWxlIHdpbGwuIiAgCgpJbiBvdGhlciB3b3Jkcywgc2V0dGluZyBgbGltaXRzYCB3aWxsIGFjdHVhbGx5IHJlc3VsdCBpbiBpbmRpdmlkdWFsIGRhdGEgcG9pbnRzIGJlaW5nIGluY2x1ZGVkIG9yIGV4Y2x1ZGVkIGZyb20gdGhlIHBsb3QgYmFzZWQgb24gd2hldGhlciB0aGV5IGZhbGwgd2l0aGluIHRoZSBzcGVjaWZpZWQgbGltaXRzLCB3aGljaCBjb3VsZCBoYXZlIHVuYW50aWNpcGF0ZWQgZWZmZWN0cyAoZm9yIGV4YW1wbGUsIGlmIHlvdXIgcGxvdCBpbmNsdWRlcyBhIGxpbmUgZml0LCB0aGF0IGxpbmUgZml0IHdpbGwgYmUgZG9uZSB1c2luZyBvbmx5IHRoZSBpbmNsdWRlZCBkYXRhIHJhdGhlciB0aGFuIGFsbCBvZiB5b3VyIGRhdGEpLgoKPGRpdiBjbGFzcz0icGFuZWwgcGFuZWwtaW5mbyI+CiAgPGRpdiBjbGFzcz0icGFuZWwtaGVhZGluZyI+TGVzc29uOjwvZGl2PgogIDxkaXYgY2xhc3M9InBhbmVsLWJvZHkiPgpEb24ndCBkaXJlY3RseSBjaGFuZ2UgYGxpbWl0c2AgdW5sZXNzIHlvdSByZWFsbHkga25vdyB3aGF0IHlvdSBhcmUgZG9pbmchIE1vc3Qgb2YgdGhlIHRpbWUsIHlvdSB3YW50IHRvIGNoYW5nZSB0aGUgY29vcmRpbmF0ZXMgaW5zdGVhZCwgYW5kIGRvIGFueSBkYXRhIHBvaW50IGZpbHRlcmluZyBvdXRzaWRlIG9mIHlvdXIgcGxvdHRpbmcuCiAgPC9kaXY+CjwvZGl2PgoKYGBge3J9CmFmZmlsX3Bsb3QgPC0gYWZmaWxfcGxvdCArIAogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygxOTgwLCAyMDIzKSwgeWxpbSA9IGMoMCwgODApKSAKYWZmaWxfcGxvdApgYGAKCgojIFBsYXkgV2l0aCBUaGVtZSBTZXR0aW5ncwoKV2Ugd2lsbCB0YWxrIGEgbG90IG1vcmUgYWJvdXQgdGhlbWVzIGFuZCBgZ2dwbG90YCBsYXRlciBpbiB0aGUgdGVybSwgYnV0IGZvciBub3csIHRoZSBpbXBvcnRhbnQgdGhpbmcgdG8ga25vdyBpcyB0aGF0IG1vc3QgdmlzdWFsIGFzcGVjdHMgb2YgdGhlIHBsb3QgaGF2ZSBhIF9uYW1lXyAoZS5nLiBgcGxvdC50aXRsZWApLCBhbmQgdGhlIGB0aGVtZSgpYCBmdW5jdGlvbiBsZXRzIHVzIHRlbGwgYGdncGxvdGAgd2hhdCBhbnkgW25hbWVkIHBhcnRdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS90aGVtZS5odG1sKSBvZiB0aGUgcGxvdCBzaG91bGQgbG9vayBsaWtlLgoKTGV0J3MgY2hhbmdlIHNvbWUga2V5IHRoZW1lIHNldHRpbmdzOgoKYGBge3J9CmFmZmlsX3Bsb3QgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikpICsKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUoYXhpcy5saW5lLnggPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JheTgwIiwgc2l6ZSA9IDAuNSkpICsKICB0aGVtZShheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyYXk4MCIsIHNpemUgPSAwLjUpKQpgYGAKCjxkaXYgY2xhc3M9InBhbmVsIHBhbmVsLWluZm8iPgogIDxkaXYgY2xhc3M9InBhbmVsLWhlYWRpbmciPkxlc3Nvbjo8L2Rpdj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1ib2R5Ij4KWW91IGNhbiBjaGFuZ2UgKmFsbW9zdCBhbnl0aGluZyogdGhhdCB5b3VyIGhlYXJ0IGRlc2lyZXMgdG8gY2hhbmdlISAKICA8L2Rpdj4KPC9kaXY+CgoKQnkgZGVmYXVsdCwgcGxvdCB0aXRsZXMgaW4gYGdncGxvdDJgIGFyZSBsZWZ0LWFsaWduZWQuIEZvciBgaGp1c3RgOgoKLSBgMGAgPT0gbGVmdAotIGAwLjVgID09IGNlbnRlcmVkCi0gYDFgID09IHJpZ2h0CgpXZSBjb3VsZCBhbHNvIHNhdmUgYWxsIHRoZXNlIGFzIGEgY3VzdG9tIHRoZW1lLiBXZSBhcmUgbm90IGZhbnMgb2YgdGhlIGRlZmF1bHQgZm9udCwgc28gd2UgYXJlIGFsc28gZ29pbmcgdG8gY2hhbmdlIHRoaXMuIFRvIGRvIHRoaXMsIHlvdSBuZWVkIHRvIGluc3RhbGwgdGhlIChgZXh0cmFmb250YCBwYWNrYWdlKVtodHRwczovL2dpdGh1Yi5jb20vd2NoL2V4dHJhZm9udF0gYW5kIGZvbGxvdyBpdHMgc2V0dXAgaW5zdHJ1Y3Rpb25zIGJlZm9yZSBkb2luZyB0aGlzIG5leHQgc3RlcC4KCmBgYHtyfQpob3RfZGlnZ2l0eSA8LSB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgICAgICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgIGF4aXMubGluZS54ID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyYXk4MCIsIHNpemUgPSAwLjUpLAogICAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyYXk4MCIsIHNpemUgPSAwLjUpLAogICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJMYXRvIikgIyBuZWVkIGV4dHJhZm9udCBmb3IgdGhpcwogICAgICAgICAgICAgICAgICAgICApCmBgYAoKCgpgYGB7cn0KYWZmaWxfcGxvdCArIGhvdF9kaWdnaXR5IApgYGAKCldlIGNvdWxkIGFsc28gdXNlIHNvbWVvbmUgZWxzZSdzIHRoZW1lOgoKYGBge3J9CmxpYnJhcnkoZ2d0aGVtZXMpCmFmZmlsX3Bsb3QgKyB0aGVtZV9maXZldGhpcnR5ZWlnaHQoYmFzZV9mYW1pbHkgPSAiTGF0byIpCmFmZmlsX3Bsb3QgKyB0aGVtZV90dWZ0ZSggYmFzZV9mYW1pbHkgPSAiUGFsYXRpbm8iKQpgYGAKCgoKClRoZSBmaW5hbCB0aGluZyB3ZSBoYXZlIHRvIG1lc3Mgd2l0aCBpcyB0aGUgeC1heGlzIHRpY2tzIGFuZCBsYWJlbHMuIFdlJ2xsIGRvIHRoaXMgaW4gdHdvIHN0ZXBzLCB0aGVuIG92ZXJyaWRlIG91ciBwcmV2aW91cyBsYXllciBgc2NhbGVfeF9jb250aW51b3VzYC4KCmBgYHtyfQojIG1hbnVhbGx5IGNvbXB1dGUgYSBsaXN0IG9mIHllYXJzIHRoYXQgd2Ugd2FudCBsYWJlbGVkLi4uCnllYXJzX3RvX2xhYmVsIDwtIHNlcShmcm9tID0gMTk4MSwgdG8gPSAyMDIyLCBieSA9IDQpIAp5ZWFyc190b19sYWJlbAoKIyBhZGQgYSBjb2x1bW4gdG8gdGhlIGRhdGFmcmFtZSBjb250YWluaW5nIHdoYXQgd2Ugd2FudCBlYWNoIHllYXIncyBsYWJlbCB0byBiZQpoZF95ZWFycyA8LSBoZG1fYWZmaWwgJT4lCiAgZGlzdGluY3QoeWVhcikgJT4lIAogIG11dGF0ZSh5ZWFyX2xhYiA9IGlmZWxzZSh5ZWFyICVpbiUgeWVhcnNfdG9fbGFiZWwsIHllYXIsICIiKSkKYGBgCgpgYGB7cn0KIyBtYW51YWxseSB0ZWxsIGdncGxvdCB3aGF0IHRvIHVzZSBmb3IgYnJlYWtzIGFuZCBsYWJlbHMKYWZmaWxfcGxvdCArIAogIGhvdF9kaWdnaXR5ICsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwgCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGhkX3llYXJzJHllYXIsCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGhkX3llYXJzJHllYXJfbGFiKQpgYGAKCiMgRmluYWwgKGZpbmFsLCBmaW5hbCkgdmVyc2lvbgoKRG9uJ3QgbmFtZSB5b3VyIGZpbGVzICJmaW5hbCIgOikKCiFbXShodHRwOi8vd3d3LnBoZGNvbWljcy5jb20vY29taWNzL2FyY2hpdmUvcGhkMTAxMjEycy5naWYpCgpBbGwgdG9nZXRoZXIgaW4gb25lIGNodW5rLCBoZXJlIGlzIG91ciBmaW5hbCAoZm9yIG5vdykgcGxvdCEgSSdtIGFsc28gYWRkaW5nIHNvbWUgYWRkaXRpb25hbCBlbGVtZW50cyBoZXJlIHRvIHNob3cgeW91IG9wdGlvbnM6CgpgYGB7cn0KbmF0aGFuX3Bsb3QgPC0gZ2dwbG90KGhkbV9hZmZpbCwgYWVzKHggPSB5ZWFyLCB5ID0gbnVtX2VhdGVuKSkgKyAKICBnZW9tX2NvbChhZXMoZmlsbCA9IGFmZmlsaWF0ZWQpKSArIAogIGxhYnMoeCA9ICJZZWFyIiwgeSA9ICJIb3QgRG9ncyBhbmQgQnVucyBDb25zdW1lZCIpICsKICBnZ3RpdGxlKCJOYXRoYW4ncyBIb3QgRG9nIEVhdGluZyBDb250ZXN0IFJlc3VsdHMsIDE5ODEtMjAyMiIsIHN1YnRpdGxlID0gIihNYWxlIGNvbnRlc3RhbnRzIG9ubHkpIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoJyNFOTYwMkInLCcjMjI3N0EwJywnI0NDQjY4MycpLAogICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiSUZPQ0UtYWZmaWxpYXRpb24iKSArIAogIGhvdF9kaWdnaXR5ICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKDAsIDcwLCAxMCkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwgCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGhkX3llYXJzJHllYXIsCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGhkX3llYXJzJHllYXJfbGFiKSArIAogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygxOTgwLCAyMDIzKSwgeWxpbSA9IGMoMCwgODApKSAKbmF0aGFuX3Bsb3QKYGBgCgoKClRoZSBmaWxsIGxlZ2VuZCBpcyBkb2luZyBpdHMgam9iLCBoZXJlLCBidXQgd2UgbWlnaHQgaW5zdGVhZCB3YW50IHRvIHVzZSBkaXJlY3QgYW5ub3RhdGlvbnMgb24gdGhlIHBsb3QgaXRzZWxmLCB0byBtYWtlIGl0IGVhc2llciBhbmQgZmFzdGVyIHRvIHJlYWQuIAoKYGdncGxvdGAgd2lsbCBsZXQgdXMgYWRkIF9hbm5vdGF0YXRpb25zXyB0byB0aGUgcGxvdC0gaS5lLiwgZXh0cmEgdGV4dCwgbGluZXMgLCBldGMuIHRoYXQgYXJlIG5vdCBkZXJpdmVkIGZyb20gdGhlIGRhdGEgaW4gdGhlIHBsb3QsIGJ1dCBhcmUgbWFudWFsbHkgc3BlY2lmaWVkIC0gdXNpbmcgdGhlIGBhbm5vdGF0ZSgpYCBmdW5jdGlvbiwgd2hpY2ggYWRkcyBhZGRpdGlvbmFsIGxheWVycyB0byB0aGUgcGxvdC4gVGhlIHdheSB3ZSBhcmUgZG9pbmcgaXQgYmVsb3cgaXMgYSBiaXQgdGVkaW91cywgYnV0IGRlbW9uc3RyYXRlcyBob3cgaXQgd29ya3MuCgpgYGB7cn0KbmF0aGFuX2FubiA8LSBuYXRoYW5fcGxvdCArCiAgZ3VpZGVzKGZpbGw9Im5vbmUiKSArICMgdHVybiBvZmYgdGhlIGxlZ2VuZC9ndWlkZSBmb3IgdGhlICJmaWxsIiBhZXN0aGV0aWMKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMTk4MCwgMjAyMyksIHlsaW0gPSBjKDAsIDkwKSkgKwogIGFubm90YXRlKCdzZWdtZW50JywgeD0xOTgwLjc1LCB4ZW5kPTIwMDAuMjUsIHk9IDMwLCB5ZW5kPTMwLCBsaW5ld2lkdGg9MC41LCBjb2xvcj0iI0NDQjY4MyIpKwogIGFubm90YXRlKCdzZWdtZW50JywgeD0xOTgwLjc1LCB4ZW5kPTE5ODAuNzUsIHk9IDMwLCB5ZW5kPTI4LCBsaW5ld2lkdGg9MC41LCBjb2xvcj0iI0NDQjY4MyIpICsKICBhbm5vdGF0ZSgnc2VnbWVudCcsIHg9MjAwMC4yNSwgeGVuZD0yMDAwLjI1LCB5PSAzMCwgeWVuZD0yOCwgbGluZXdpZHRoPTAuNSwgY29sb3I9IiNDQ0I2ODMiKSArCiAgYW5ub3RhdGUoJ3NlZ21lbnQnLCB4PTE5OTAsIHhlbmQ9MTk5MCwgeT0gMzMsIHllbmQ9MzAsIGxpbmV3aWR0aD0wLjUsIGNvbG9yPSIjQ0NCNjgzIikgKwogIGFubm90YXRlKCd0ZXh0JywgeD0xOTkwLCB5PTM2LCBsYWJlbD0iTm8gTUxFL0lGT0NFIEFmZmlsaWF0aW9uIiwgY29sb3I9IiNDQ0I2ODMiLCBmYW1pbHk9IkxhdG8iLCBoanVzdD0wLjUsIHNpemUgPSAzKSArCgoKCiAgYW5ub3RhdGUoJ3NlZ21lbnQnLCB4PTIwMDAuNzUsIHhlbmQ9MjAwNi4yNSwgeT0gNTgsIHllbmQ9NTgsIGxpbmV3aWR0aD0wLjUsIGNvbG9yPSIjMjI3N0EwIikgKwogIGFubm90YXRlKCdzZWdtZW50JywgeD0yMDAwLjc1LCB4ZW5kPTIwMDAuNzUsIHk9IDU4LCB5ZW5kPTU2LCBsaW5ld2lkdGg9MC41LCBjb2xvcj0iIzIyNzdBMCIpICsKICBhbm5vdGF0ZSgnc2VnbWVudCcsIHg9MjAwNi4yNSwgeGVuZD0yMDA2LjI1LCB5PSA1OCwgeWVuZD01NiwgbGluZXdpZHRoPTAuNSwgY29sb3I9IiMyMjc3QTAiKSArCiAgYW5ub3RhdGUoJ3NlZ21lbnQnLCB4PTIwMDMuNSwgeGVuZD0yMDAzLjUsIHk9IDYxLCB5ZW5kPTU4LCBsaW5ld2lkdGg9MC41LCBjb2xvcj0iIzIyNzdBMCIpICsKICBhbm5vdGF0ZSgndGV4dCcsIHg9MjAwMy41LCB5PTY1LCBsYWJlbD0iTUxFL0lGT0NFXG5Gb3JtZXIgTWVtYmVyIiwgY29sb3I9IiMyMjc3QTAiLCBmYW1pbHk9IkxhdG8iLCBoanVzdD0wLjUsIHNpemUgPSAzKSArCgoKICBhbm5vdGF0ZSgnc2VnbWVudCcsIHg9MjAwNi43NSwgeGVuZD0yMDIyLjI1LCB5PSA3OSwgeWVuZD03OSwgbGluZXdpZHRoPTAuNSwgY29sb3I9IiNFOTYwMkIiKSArCiAgYW5ub3RhdGUoJ3NlZ21lbnQnLCB4PTIwMDYuNzUsIHhlbmQ9MjAwNi43NSwgeT0gNzksIHllbmQ9NzcsIGxpbmV3aWR0aD0wLjUsIGNvbG9yPSIjRTk2MDJCIikgKwogIGFubm90YXRlKCdzZWdtZW50JywgeD0yMDIyLjI1LCB4ZW5kPTIwMjIuMjUsIHk9IDc5LCB5ZW5kPTc3LCBsaW5ld2lkdGg9MC41LCBjb2xvcj0iI0U5NjAyQiIpICsKICBhbm5vdGF0ZSgnc2VnbWVudCcsIHg9MjAxNSwgeGVuZD0yMDE1LCB5PSA4MiwgeWVuZD03OSwgbGluZXdpZHRoPTAuNSwgY29sb3I9IiNFOTYwMkIiKSArCiAgYW5ub3RhdGUoJ3RleHQnLCB4PTIwMTUsIHk9ODYsIGxhYmVsPSJNTEUvSUZPQ0UgQ3VycmVudCBNZW1iZXIiLCBjb2xvcj0iI0U5NjAyQiIsIGZhbWlseT0iTGF0byIsIGhqdXN0PTAuNSwgc2l6ZSA9IDMpCm5hdGhhbl9hbm4KYGBgCgpGaW5hbGx5LCBhZGRpbmcgaW4gYW5vdGhlciBsYXllciBvZiBkYXRhLCBpbmNsdWRpbmcgaW5mb3JtYXRpb24gYWJvdXQgZmVtYWxlIGNvbnRlc3RhbnRzOgoKYGBge3J9CmhkbV9mZW1hbGVzIDwtIHJlYWRfY3N2KGhlcmU6OmhlcmUoImRhdGEiLCAiaG90X2RvZ19jb250ZXN0X3dpdGhfYWZmaWxpYXRpb24uY3N2IiksIAogICAgY29sX3R5cGVzID0gY29scygKICAgICAgYWZmaWxpYXRlZCA9IGNvbF9mYWN0b3IobGV2ZWxzID0gTlVMTCksIAogICAgICBnZW5kZXIgPSBjb2xfZmFjdG9yKGxldmVscyA9IE5VTEwpCiAgICAgICkpICU+JSAKICBtdXRhdGUocG9zdF9pZm9jZSA9IHllYXIgPj0gMTk5NykgJT4lIAogIGZpbHRlcih5ZWFyID49IDE5ODEgJiBnZW5kZXIgPT0gImZlbWFsZSIpIApnbGltcHNlKGhkbV9mZW1hbGVzKQpgYGAKCmBgYHtyfQpuYXRoYW5fd19mZW1hbGVzIDwtIG5hdGhhbl9hbm4gKwogICMgYWRkIGluIHRoZSBmZW1hbGUgZGF0YSwgYW5kIG1hbnVhbGx5IHNldCBhIGZpbGwgY29sb3IKICBnZW9tX2NvbChkYXRhID0gaGRtX2ZlbWFsZXMsIAogICAgICAgICAgIHdpZHRoID0gMC43NSwgCiAgICAgICAgICAgZmlsbCA9ICIjRjY4QTM5IikgKwogIGxhYnMoc3VidGl0bGUgPSBOVUxMKSAjIG5vIGxvbmdlciBuZWVkIHRoZSBzdWJ0aXRsZSB3YXJuaW5nIGFib3V0IG1hbGUtb25seSBkYXRhIQpuYXRoYW5fd19mZW1hbGVzCmBgYAoKQW5kIGFkZGluZyBhIGZpbmFsIGNhcHRpb246CgpgYGB7cn0KY2FwdGlvbiA8LSBwYXN0ZShzdHJ3cmFwKCIqIEZyb20gMjAxMSBvbiwgc2VwYXJhdGUgTWVuJ3MgYW5kIFdvbWVuJ3MgcHJpemVzIGhhdmUgYmVlbiBhd2FyZGVkLiBBbGwgZmVtYWxlIGNoYW1waW9ucyB0byBkYXRlIGhhdmUgYmVlbiBNTEUvSUZPQ0UtYWZmaWxpYXRlZC4iLCA3MCksIGNvbGxhcHNlPSJcbiIpCgpuYXRoYW5fd19mZW1hbGVzICsKICAjIG5vdyBhbiBhc3RlcmlzayB0byBzZXQgb2ZmIHRoZSBmZW1hbGUgc2NvcmVzLCBhbmQgYSBjYXB0aW9uCiAgYW5ub3RhdGUoJ3RleHQnLCB4ID0gMjAxMSwgeSA9IDM2LCBsYWJlbD0iKiIsIGZhbWlseSA9ICJMYXRvIiwgc2l6ZSA9IDgpICsKICBsYWJzKGNhcHRpb24gPSBjYXB0aW9uKSArCiAgdGhlbWUocGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJMYXRvIiwgc2l6ZT04LCBoanVzdD0wLCBtYXJnaW49bWFyZ2luKHQ9MTUpKSkKYGBgCgo=