Visualizations with ggplot2
Now that we have our data visualization imaginations going, let’s get
into how we can visually represent our data in R.
The go-to package for data visualization in R is
ggplot2, which is part of the tidyverse. You can find more
information about ggplot2 on the
tidyverse website.
This package approaches data visualization through “a grammar of
graphics.” In other words, using the same syntax, you can create an
infinite number of data visualizations. Although there are a lot of
functions and components to learn at first, once you understand the
overall structure of building graphics in ggplot2, you can
replicate and expand on this structure to visualize data in an unlimited
number of ways.
There are many ways to make data visualizations in R; however, other
approaches tend to be more automatic and consequently limit the amount
you can change and adapt your visualization to your needs.
ggplot2 works in layers, allowing for maximum control and
flexibility.
Layers in ggplot2
Here are some of the most common layers (i.e., functions) used in
ggplot2. Typically you connect these layers using the
+ symbol. There is often more than one way to build the
same plot with the ggplot package.
- ggplot(): how you will start most
plots you build in ggplot2. The rest of the information
goes within this function.
- aes(): this is the aesthetic mapping
function, in which you can control aesthetic components of the plot. You
can add colors, axis labels, font sizes and more within this function.
Color and shape can be defined both within and outside of the aesthetic
function.
- geom_point(): used for making a
scatter plot
- geom_line(): used for adding a line
to a plot
- geom_histogram(): used for making a
histogram
- geom_col(): used for making a bar
plot
- xlab, ylab, and
labs(title = ): used for adding axis labels and an
overall title to plots
- color: assigns a color to part of the
plot, such as different groups or the data points
- fill: assigns the interior color of
part of the plot, such as a confidence band or the bars in bar plots
- alpha: used to change the
transparency of a component of the plot. Useful if you lots of have
overlapping data points or distributions from multiple groups.
- size: used to set the size of part of
the plot, such as how big the data points or text should be
There are many more data visualization options in ggplot
but to get started today we are going to focus on making a bar plot
(good for categorical data) and a scatter plot (good for continuous
data).
Let’s take a look at the ggplot2
documentation
Create a New Script
Let’s start by creating a new script.

If you have installed the tidyverse, then ggplot2 is
included. Otherwise you can install it now. Let’s also load our dataset
for today.
install.packages("ggplot2")
library(ggplot2)
Let’s load our dataset. For this exercise, we are using a dataset
that is exactly the same as the one that was produced in the session
Data Types and Structures, but that has one extra
variable added called isFeelRushed. This variable is
derived from the feelRushed variable, with the number
1 indicating that somebody feels rushed, and 0
indicating they do not feel rushed. For more information on this
variable and how it was created, see the session R:
Filter and Select
load("data/block-5_visualization.RData")
Basic Bar Plot: Counts
Let’s make our first plot in R! We are going to slowly add layers,
building up to a box plot representing the number of people in each
group in the isFeelRushed variable.
When creating visualizations, it’s always good to have a sense of the
variables you’re working with. It can be helpful to use the
View() command we discussed in the session First
Steps in R:
View(js_data)
Because we know that we’re going to be starting with the
isFeelRushed variable, it’s also helpful to know what its
data type is:
class(js_data$isFeelRushed)
Starting the Plot
First we create the blank plot on which we will add our data. We
nearly always start with the function ggplot and then
telling the function what dataset to use.
ggplot(js_data)

This creates our blank canvas.
Next we tell ggplot what variable we want to use and put it in the
aes() function. If you want a bar chart representing the
number of people in each group, you can add just one variable to the
aes() function. We include the as.factor
function around the isFeelRushed variable so it is treated
as a categorical variable for the box plot instead of a numeric
variable.
ggplot(js_data, aes(x = as.factor(isFeelRushed)))

You now see that the grid represents a scale relevant to that
variable.
Next, we tell ggplot what type of data visualization we want. To
create a bar chart we use the function geom_bar(). To see
how many people are in each of the isFeelRushed groups, we
use the default geom_bar(stat = "count"). Remember that we
connect layers with a + symbol.
ggplot(js_data, aes(x = as.factor(isFeelRushed))) +
geom_bar(stat = "count")

We’ve got a bar chart!
Lastly, let’s add some labels to the x-axis and y-axis to make it
clear what is being plotted.
ggplot(js_data, aes(x = as.factor(isFeelRushed))) +
geom_bar(stat = "count") +
xlab("Feeling Rushed") +
ylab ("Number of Participants")

Basic Bar Plot: Group Means
Now, let’s see if people who feel rushed tend to work more than
people who do not feel rushed. To represent the mean for each group or
some other variable, you add both an x and a y variable to the
aes() function and use the
geom_bar(stat = "summary"). Note that the Y-axis scale has
now adjusted to a scale that matches the variable we are using (i.e.,
mean number of minutes spent working for each group).
ggplot(js_data, aes(x = as.factor(isFeelRushed), y = durWork)) +
geom_bar(stat = "summary") +
xlab("Feeling Rushed") +
ylab ("Average Minutes Working")

- Most plots will start with the
ggplot()function
- You have to include the object where
ggplot() will get
the information for the plot from. In this case, it’s our dataset
js_data
- Within the
aes() function, we identify what the x and y
variables are for this plot
- We add layers using the
+ sign
- Next we tell
ggplot() what type of plot we are making;
in this case we are creating a bar plot using the function
geom_bar()
- Then we add what type of statistic we want presented on the plot.
Here we ask for the mean of each group for the variable durWork using
the function
geom_bar(stat = "summary")
- We add a label to the x-axis with
lab("Feeling Rushed")
- We add the label to the y-axis with
ylab ("Time Working")
Improved Box Plot!
This plot gets the idea across, but we can add more layers and
functions to make more adjustments. See the walkthrough below for how we
made all these changes.
plotlabels <- c("Not Rushed", "Rushed", "Did Not Respond")
ggplot(js_data, aes(x = as.factor(isFeelRushed), y = durWork)) +
geom_bar(stat = "summary", fill = "#2D5E7F") +
xlab("Feeling Rushed") +
ylab ("Average Minutes Working") +
labs(title = "Working and Feeling Rushed") +
scale_x_discrete(labels = plotlabels) +
theme(text = element_text(size = 18),
axis.text.x = element_text(angle = 25, hjust = 1))

- We can change the color of the bars using
fill =. Here
we added the specific color using a hex code. But you can also write in
the names of colors such as “blue”.
- Using
labs(title = "") we added an overall title to the
plot
- We probably want to indicate what each category represents, rather
than the “0”, “1” and “NA” labels. To add text labels, we first create
an object with each of those labels in order
(
plotlabels <- c("Not Rushed", "Rushed", "Did Not Respond")).
Then in the scale_x_discrete(labels = plotlabels) function
we call to that object we created as the labels for the x-axis. There
are many other ways to adjust the labels for each axis, but this method
works well for a small number of groups.
- In the
theme() layer you can add many different
specifications. Here we added
text = element_text(size = 18) to make the text size bigger
than the default and made the x-axis labels angled so they fit better
using axis.text.x = element_text(angle = 25, hjust = 1).
The hjust = adjusts the vertical location of the axis
labels so they don’t overlap with the plot itself. vjust =
can be used to move the labels right and left.
Try manipulating this plot in some way. Can you change the color of
the bars? What happens if you change angle = 90?
Basic Scatter Plot
Now let’s make a scatter plot to visualize two continuous variables.
We are going to check if it looks like there is a correlation between
how much time people work (durWork) and how much they sleep
(durSleep).
ggplot(js_data, aes(durWork, durSleep)) +
geom_point() +
geom_smooth() +
xlab("Minutes Spent Working") +
ylab ("Minutes Spent Sleeping")

- Again we start with the
ggplot() function, including
telling it to use the dataset js_data
- In the
aes() function we list the x and y variables
(here durWork and durSleep)
- The
geom_point() function is what makes a scatter
plot
- The geom_smooth() is what adds the correlation line to the plot
- The
xlab() and ylab() add the x-axis and
y-axis labels to the plot
Improved Scatter Plot!
Now let’s add some layers and aesthetic adjustments to improve this
plot.
ggplot(js_data, aes(durWork, durSleep)) +
geom_point(color = "#2D5E7F", alpha = .2) +
geom_smooth(method = lm, color = "black") +
xlab("Minutes Spent Working") +
ylab ("Minutes Spent Sleeping") +
scale_x_continuous(breaks = seq(0, 1500, 250)) +
labs(title = "Association Between Working and Sleeping") +
theme(text = element_text(size = 18))

- We can change the color of the data points by adding
color = "#2D5E7F" and change the transparency of the data
points (so you can see where there are overlapping clusters) with the
addition of alpha = .2.
- In the
geom_smooth() function we changed the method for
calculating the line to be linear (instead of the ggplot default) using
method = lm and adjusted the color of the line with
color =. Remember that you can write hex code numbers or
the names of colors to adjust colors. Make sure both are in quotes to
avoid error messages.
- We adjust the x-axis tick marks with the
scale_x_continuous(breaks = seq(0, 1500, 250)) part, which
tells R to plot the x-axis on a sequence from 0 to 1500 (this captures
all of the responses in our data), with labels every 250 minutes.
- The
labs(title = "Association Between Working and Sleeping")
adds an overall title to the plot
- Lastly,
theme(text = element_text(size = 18)) adjust
the text size
Try manipulating this plot in some way. Can you make the data points
more and less transparent? Can you change the color of the line? What
happens if the x-axis has labels every 100 minutes?
Your Turn!
For the following questions, the goal is to get you thinking about
the building blocks that go into making a visual. Don’t get too focused
on the finished product right away, and try to think about the
individual steps needed to get you to there. As with any coding task, it
can be very helpful to think of or write each of the steps down in plain
English, then work through each one.
As a final note, a huge part of learning how to code
is using search engines, and there is a ton of documentation out there
to help you. This also relates back to putting things into
plain-language steps, because being able to clearly articulate your
goals is synonymous with using good search terms.
The data dictionary will also be an asset in working through this
exercise:
Good luck!
Take a look at our data set and make two new plots.
Make a plot comparing groups (i.e., a categorical variable) on
one of the duration variables (i.e., a continuous variable).
Make a plot comparing two continuous variables.
Challenge: What is one component of your plot you
would like to change? Can you look up a solution?
See if you can recreate the plot below.
Hint: This type of chart is called a “proportional
bar chart”, and search engines are your best friend!

prov_edu_plot <- ggplot(js_data, aes(x = province_fact, fill = eduLevel_fact)) +
geom_bar(position = "fill") +
labs(
title = "Proportion of Education Levels by Province",
x = "Province",
y = "Proportion",
fill = "Education Level"
) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
See if you can recreate the plot below.
Hint: First try to recreate the plot with NA
values. Then, to remove NA values from the plot, you will
need to use both the na.omit and the
na.rm = TRUE commands.

time_alone_plot <- ggplot(na.omit(js_data), aes(x = factor(timeWantAlone), y = durAlone)) +
geom_boxplot(fill = "blue", na.rm = TRUE) +
scale_x_discrete(labels = c(
"1" = "Would like more time alone",
"2" = "Do not want more time alone"
)) +
labs(
title = "Distribution of Time Spent Alone by Desire to Be Alone",
x = "Desire to Spend Time Alone",
y = "Duration Spent Alone - Minutes"
)
LS0tDQp0aXRsZTogIlZpc3VhbGl6YXRpb24gd2l0aCBnZ3Bsb3Rsb3QiDQpwYWdldGl0bGU6ICJWaXN1YWxpemF0aW9uIHdpdGggZ2dwbG90Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZm9sZGluZzogc2hvdyAjIGFsbG93cyB0b2dnbGluZyBvZiBzaG93aW5nIGFuZCBoaWRpbmcgY29kZS4gUmVtb3ZlIGlmIG5vdCB1c2luZyBjb2RlLg0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUgIyBhbGxvd3MgdGhlIHVzZXIgdG8gZG93bmxvYWQgdGhlIHNvdXJjZSAuUm1kIGZpbGUuIFJlbW92ZSBpZiBub3QgdXNpbmcgY29kZS4NCiAgICBpbmNsdWRlczoNCiAgICAgIGFmdGVyX2JvZHk6IGZvb3Rlci5odG1sICMgaW5jbHVkZSBhIGN1c3RvbSBmb290ZXIuDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiAzDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UNCi0tLQ0KDQpgYGB7ciwgbGlicmFyaWVzLCBpbmNsdWRlID0gRkFMU0V9DQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KYGBgDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmdzID0gRkFMU0UpDQpgYGANCg0KDQojIyBWaXN1YWxpemF0aW9ucyB3aXRoIGdncGxvdDINCg0KTm93IHRoYXQgd2UgaGF2ZSBvdXIgZGF0YSB2aXN1YWxpemF0aW9uIGltYWdpbmF0aW9ucyBnb2luZywgbGV0J3MgZ2V0IGludG8gaG93IHdlIGNhbiB2aXN1YWxseSByZXByZXNlbnQgb3VyIGRhdGEgaW4gUi4NCg0KVGhlIGdvLXRvIHBhY2thZ2UgZm9yIGRhdGEgdmlzdWFsaXphdGlvbiBpbiBSIGlzIGBnZ3Bsb3QyYCwgd2hpY2ggaXMgcGFydCBvZiB0aGUgdGlkeXZlcnNlLiBZb3UgY2FuIGZpbmQgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCBgZ2dwbG90MmAgb24gdGhlIDxhIGhyZWY9Imh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnLyI+dGlkeXZlcnNlIHdlYnNpdGU8L2E+Lg0KDQpUaGlzIHBhY2thZ2UgYXBwcm9hY2hlcyBkYXRhIHZpc3VhbGl6YXRpb24gdGhyb3VnaCAiYSBncmFtbWFyIG9mIGdyYXBoaWNzLiIgSW4gb3RoZXIgd29yZHMsIHVzaW5nIHRoZSBzYW1lIHN5bnRheCwgeW91IGNhbiBjcmVhdGUgYW4gaW5maW5pdGUgbnVtYmVyIG9mIGRhdGEgdmlzdWFsaXphdGlvbnMuIEFsdGhvdWdoIHRoZXJlIGFyZSBhIGxvdCBvZiBmdW5jdGlvbnMgYW5kIGNvbXBvbmVudHMgdG8gbGVhcm4gYXQgZmlyc3QsIG9uY2UgeW91IHVuZGVyc3RhbmQgdGhlIG92ZXJhbGwgc3RydWN0dXJlIG9mIGJ1aWxkaW5nIGdyYXBoaWNzIGluIGBnZ3Bsb3QyYCwgeW91IGNhbiByZXBsaWNhdGUgYW5kIGV4cGFuZCBvbiB0aGlzIHN0cnVjdHVyZSB0byB2aXN1YWxpemUgZGF0YSBpbiBhbiB1bmxpbWl0ZWQgbnVtYmVyIG9mIHdheXMuIA0KDQoNClRoZXJlIGFyZSBtYW55IHdheXMgdG8gbWFrZSBkYXRhIHZpc3VhbGl6YXRpb25zIGluIFI7IGhvd2V2ZXIsIG90aGVyIGFwcHJvYWNoZXMgdGVuZCB0byBiZSBtb3JlIGF1dG9tYXRpYyBhbmQgY29uc2VxdWVudGx5IGxpbWl0IHRoZSBhbW91bnQgeW91IGNhbiBjaGFuZ2UgYW5kIGFkYXB0IHlvdXIgdmlzdWFsaXphdGlvbiB0byB5b3VyIG5lZWRzLiBgZ2dwbG90MmAgd29ya3MgaW4gbGF5ZXJzLCBhbGxvd2luZyBmb3IgbWF4aW11bSBjb250cm9sIGFuZCBmbGV4aWJpbGl0eS4gDQoNCiMjIyBMYXllcnMgaW4gZ2dwbG90Mg0KDQpIZXJlIGFyZSBzb21lIG9mIHRoZSBtb3N0IGNvbW1vbiBsYXllcnMgKGkuZS4sIGZ1bmN0aW9ucykgdXNlZCBpbiBgZ2dwbG90MmAuIFR5cGljYWxseSB5b3UgY29ubmVjdCB0aGVzZSBsYXllcnMgdXNpbmcgdGhlIGArYCBzeW1ib2wuIFRoZXJlIGlzIG9mdGVuIG1vcmUgdGhhbiBvbmUgd2F5IHRvIGJ1aWxkIHRoZSBzYW1lIHBsb3Qgd2l0aCB0aGUgYGdncGxvdGAgcGFja2FnZS4gDQoNCioqLSBgZ2dwbG90KClgOioqIGhvdyB5b3Ugd2lsbCBzdGFydCBtb3N0IHBsb3RzIHlvdSBidWlsZCBpbiBgZ2dwbG90MmAuIFRoZSByZXN0IG9mIHRoZSBpbmZvcm1hdGlvbiBnb2VzIHdpdGhpbiB0aGlzIGZ1bmN0aW9uLg0KDQoqKi0gYGFlcygpYDoqKiB0aGlzIGlzIHRoZSBhZXN0aGV0aWMgbWFwcGluZyBmdW5jdGlvbiwgaW4gd2hpY2ggeW91IGNhbiBjb250cm9sIGFlc3RoZXRpYyBjb21wb25lbnRzIG9mIHRoZSBwbG90LiBZb3UgY2FuIGFkZCBjb2xvcnMsIGF4aXMgbGFiZWxzLCBmb250IHNpemVzIGFuZCBtb3JlIHdpdGhpbiB0aGlzIGZ1bmN0aW9uLiBDb2xvciBhbmQgc2hhcGUgY2FuIGJlIGRlZmluZWQgYm90aCB3aXRoaW4gYW5kIG91dHNpZGUgb2YgdGhlIGFlc3RoZXRpYyBmdW5jdGlvbi4gDQoNCioqLSBgZ2VvbV9wb2ludCgpYDoqKiB1c2VkIGZvciBtYWtpbmcgYSBzY2F0dGVyIHBsb3QNCg0KKiotIGBnZW9tX2xpbmUoKWA6KiogdXNlZCBmb3IgYWRkaW5nIGEgbGluZSB0byBhIHBsb3QNCg0KKiotIGBnZW9tX2hpc3RvZ3JhbSgpYDoqKiB1c2VkIGZvciBtYWtpbmcgYSBoaXN0b2dyYW0NCg0KKiotIGBnZW9tX2NvbCgpYDoqKiB1c2VkIGZvciBtYWtpbmcgYSBiYXIgcGxvdA0KDQoqKi0gYHhsYWJgLCBgeWxhYmAsIGFuZCBgbGFicyh0aXRsZSA9IClgOioqIHVzZWQgZm9yIGFkZGluZyBheGlzIGxhYmVscyBhbmQgYW4gb3ZlcmFsbCB0aXRsZSB0byBwbG90cw0KDQoqKi0gYGNvbG9yYDoqKiBhc3NpZ25zIGEgY29sb3IgdG8gcGFydCBvZiB0aGUgcGxvdCwgc3VjaCBhcyBkaWZmZXJlbnQgZ3JvdXBzIG9yIHRoZSBkYXRhIHBvaW50cw0KDQoqKi0gYGZpbGxgOioqIGFzc2lnbnMgdGhlIGludGVyaW9yIGNvbG9yIG9mIHBhcnQgb2YgdGhlIHBsb3QsIHN1Y2ggYXMgYSBjb25maWRlbmNlIGJhbmQgb3IgdGhlIGJhcnMgaW4gYmFyIHBsb3RzDQoNCioqLSBgYWxwaGFgOioqIHVzZWQgdG8gY2hhbmdlIHRoZSB0cmFuc3BhcmVuY3kgb2YgYSBjb21wb25lbnQgb2YgdGhlIHBsb3QuIFVzZWZ1bCBpZiB5b3UgbG90cyBvZiBoYXZlIG92ZXJsYXBwaW5nIGRhdGEgcG9pbnRzIG9yIGRpc3RyaWJ1dGlvbnMgZnJvbSBtdWx0aXBsZSBncm91cHMuIA0KDQoqKi0gYHNpemVgOioqIHVzZWQgdG8gc2V0IHRoZSBzaXplIG9mIHBhcnQgb2YgdGhlIHBsb3QsIHN1Y2ggYXMgaG93IGJpZyB0aGUgZGF0YSBwb2ludHMgb3IgdGV4dCBzaG91bGQgYmUNCg0KVGhlcmUgYXJlIG1hbnkgbW9yZSBkYXRhIHZpc3VhbGl6YXRpb24gb3B0aW9ucyBpbiBgZ2dwbG90YCBidXQgdG8gZ2V0IHN0YXJ0ZWQgdG9kYXkgd2UgYXJlIGdvaW5nIHRvIGZvY3VzIG9uIG1ha2luZyBhIGJhciBwbG90IChnb29kIGZvciBjYXRlZ29yaWNhbCBkYXRhKSBhbmQgYSBzY2F0dGVyIHBsb3QgKGdvb2QgZm9yIGNvbnRpbnVvdXMgZGF0YSkuIA0KDQpMZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgW2dncGxvdDIgZG9jdW1lbnRhdGlvbl0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2luZGV4Lmh0bWwpDQoNCiMjIyBDcmVhdGUgYSBOZXcgU2NyaXB0DQoNCkxldCdzIHN0YXJ0IGJ5IGNyZWF0aW5nIGEgbmV3IHNjcmlwdC4NCg0KIVtdKGltYWdlcy9ibG9jazNfY3JlYXRlLXItc2NyaXB0LmdpZikNCg0KSWYgeW91IGhhdmUgaW5zdGFsbGVkIHRoZSB0aWR5dmVyc2UsIHRoZW4gYGdncGxvdDJgIGlzIGluY2x1ZGVkLiBPdGhlcndpc2UgeW91IGNhbiBpbnN0YWxsIGl0IG5vdy4gTGV0J3MgYWxzbyBsb2FkIG91ciBkYXRhc2V0IGZvciB0b2RheS4NCg0KYGBge3IsIGV2YWwgPSBGQUxTRX0NCmluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KYGBgDQoNCg0KOjo6bm90ZQ0KTGV0J3MgbG9hZCBvdXIgZGF0YXNldC4gIEZvciB0aGlzIGV4ZXJjaXNlLCB3ZSBhcmUgdXNpbmcgYSBkYXRhc2V0IHRoYXQgaXMgZXhhY3RseSB0aGUgc2FtZSBhcyB0aGUgb25lIHRoYXQgd2FzIHByb2R1Y2VkIGluIHRoZSBzZXNzaW9uICoqRGF0YSBUeXBlcyBhbmQgU3RydWN0dXJlcyoqLCBidXQgdGhhdCBoYXMgb25lIGV4dHJhIHZhcmlhYmxlIGFkZGVkIGNhbGxlZCBgaXNGZWVsUnVzaGVkYC4gIFRoaXMgdmFyaWFibGUgaXMgZGVyaXZlZCBmcm9tIHRoZSBgZmVlbFJ1c2hlZGAgdmFyaWFibGUsIHdpdGggdGhlIG51bWJlciBgMWAgaW5kaWNhdGluZyB0aGF0IHNvbWVib2R5IGZlZWxzIHJ1c2hlZCwgYW5kIGAwYCBpbmRpY2F0aW5nIHRoZXkgZG8gbm90IGZlZWwgcnVzaGVkLiAgRm9yIG1vcmUgaW5mb3JtYXRpb24gb24gdGhpcyB2YXJpYWJsZSBhbmQgaG93IGl0IHdhcyBjcmVhdGVkLCBzZWUgdGhlIHNlc3Npb24gW1I6IEZpbHRlciBhbmQgU2VsZWN0XShodHRwczovL2FsbGlhbmNlLXJkbS1nZHIuZ2l0aHViLmlvL3JkbS1qdW1wc3RhcnQvMy1BQ1QtMy1GaWx0ZXJTZWxlY3QuaHRtbCkNCg0KOjo6DQoNCmBgYHtyfQ0KbG9hZCgiZGF0YS9ibG9jay01X3Zpc3VhbGl6YXRpb24uUkRhdGEiKQ0KYGBgDQoNCiMjIyBCYXNpYyBCYXIgUGxvdDogQ291bnRzDQpMZXQncyBtYWtlIG91ciBmaXJzdCBwbG90IGluIFIhIFdlIGFyZSBnb2luZyB0byBzbG93bHkgYWRkIGxheWVycywgYnVpbGRpbmcgdXAgdG8gYSBib3ggcGxvdCByZXByZXNlbnRpbmcgdGhlIG51bWJlciBvZiBwZW9wbGUgaW4gZWFjaCBncm91cCBpbiB0aGUgYGlzRmVlbFJ1c2hlZGAgdmFyaWFibGUuDQoNCldoZW4gY3JlYXRpbmcgdmlzdWFsaXphdGlvbnMsIGl0J3MgYWx3YXlzIGdvb2QgdG8gaGF2ZSBhIHNlbnNlIG9mIHRoZSB2YXJpYWJsZXMgeW91J3JlIHdvcmtpbmcgd2l0aC4gIEl0IGNhbiBiZSBoZWxwZnVsIHRvIHVzZSB0aGUgYFZpZXcoKWAgY29tbWFuZCB3ZSBkaXNjdXNzZWQgaW4gdGhlIHNlc3Npb24gKipGaXJzdCBTdGVwcyBpbiBSKio6DQoNCmBgYHtyLCByZXN1bHRzID0gJ2hpZGUnfQ0KVmlldyhqc19kYXRhKQ0KYGBgDQoNCkJlY2F1c2Ugd2Uga25vdyB0aGF0IHdlJ3JlIGdvaW5nIHRvIGJlIHN0YXJ0aW5nIHdpdGggdGhlIGBpc0ZlZWxSdXNoZWRgIHZhcmlhYmxlLCBpdCdzIGFsc28gaGVscGZ1bCB0byBrbm93IHdoYXQgaXRzIGRhdGEgdHlwZSBpczoNCg0KYGBge3IsIHJlc3VsdHMgPSAnaGlkZSd9DQpjbGFzcyhqc19kYXRhJGlzRmVlbFJ1c2hlZCkNCmBgYA0KDQo8YnI+DQoNCiMjIyMgU3RhcnRpbmcgdGhlIFBsb3QNCg0KRmlyc3Qgd2UgY3JlYXRlIHRoZSBibGFuayBwbG90IG9uIHdoaWNoIHdlIHdpbGwgYWRkIG91ciBkYXRhLiBXZSBuZWFybHkgYWx3YXlzIHN0YXJ0IHdpdGggdGhlIGZ1bmN0aW9uIGBnZ3Bsb3RgIGFuZCB0aGVuIHRlbGxpbmcgdGhlIGZ1bmN0aW9uIHdoYXQgZGF0YXNldCB0byB1c2UuIA0KDQpgYGB7cn0NCmdncGxvdChqc19kYXRhKQ0KYGBgDQoNClRoaXMgY3JlYXRlcyBvdXIgYmxhbmsgY2FudmFzLg0KDQpOZXh0IHdlIHRlbGwgZ2dwbG90IHdoYXQgdmFyaWFibGUgd2Ugd2FudCB0byB1c2UgYW5kIHB1dCBpdCBpbiB0aGUgYGFlcygpYCBmdW5jdGlvbi4gSWYgeW91IHdhbnQgYSBiYXIgY2hhcnQgcmVwcmVzZW50aW5nIHRoZSBudW1iZXIgb2YgcGVvcGxlIGluIGVhY2ggZ3JvdXAsIHlvdSBjYW4gYWRkIGp1c3Qgb25lIHZhcmlhYmxlIHRvIHRoZSBgYWVzKClgIGZ1bmN0aW9uLiBXZSBpbmNsdWRlIHRoZSBgYXMuZmFjdG9yYCBmdW5jdGlvbiBhcm91bmQgdGhlIGBpc0ZlZWxSdXNoZWRgIHZhcmlhYmxlIHNvIGl0IGlzIHRyZWF0ZWQgYXMgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBmb3IgdGhlIGJveCBwbG90IGluc3RlYWQgb2YgYSBudW1lcmljIHZhcmlhYmxlLiANCg0KYGBge3J9DQpnZ3Bsb3QoanNfZGF0YSwgYWVzKHggPSBhcy5mYWN0b3IoaXNGZWVsUnVzaGVkKSkpDQpgYGANCg0KWW91IG5vdyBzZWUgdGhhdCB0aGUgZ3JpZCByZXByZXNlbnRzIGEgc2NhbGUgcmVsZXZhbnQgdG8gdGhhdCB2YXJpYWJsZS4gDQoNCk5leHQsIHdlIHRlbGwgZ2dwbG90IHdoYXQgdHlwZSBvZiBkYXRhIHZpc3VhbGl6YXRpb24gd2Ugd2FudC4gVG8gY3JlYXRlIGEgYmFyIGNoYXJ0IHdlIHVzZSB0aGUgZnVuY3Rpb24gYGdlb21fYmFyKClgLiBUbyBzZWUgaG93IG1hbnkgcGVvcGxlIGFyZSBpbiBlYWNoIG9mIHRoZSBgaXNGZWVsUnVzaGVkYCBncm91cHMsIHdlIHVzZSB0aGUgZGVmYXVsdCBgZ2VvbV9iYXIoc3RhdCA9ICJjb3VudCIpYC4gUmVtZW1iZXIgdGhhdCB3ZSBjb25uZWN0IGxheWVycyB3aXRoIGEgYCtgIHN5bWJvbC4gDQoNCmBgYHtyfQ0KZ2dwbG90KGpzX2RhdGEsIGFlcyh4ID0gYXMuZmFjdG9yKGlzRmVlbFJ1c2hlZCkpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiY291bnQiKQ0KYGBgDQoNCldlJ3ZlIGdvdCBhIGJhciBjaGFydCENCg0KTGFzdGx5LCBsZXQncyBhZGQgc29tZSBsYWJlbHMgdG8gdGhlIHgtYXhpcyBhbmQgeS1heGlzIHRvIG1ha2UgaXQgY2xlYXIgd2hhdCBpcyBiZWluZyBwbG90dGVkLiANCg0KYGBge3J9DQpnZ3Bsb3QoanNfZGF0YSwgYWVzKHggPSBhcy5mYWN0b3IoaXNGZWVsUnVzaGVkKSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJjb3VudCIpICsgDQogIHhsYWIoIkZlZWxpbmcgUnVzaGVkIikgKw0KICB5bGFiICgiTnVtYmVyIG9mIFBhcnRpY2lwYW50cyIpIA0KYGBgDQoNCiMjIyBCYXNpYyBCYXIgUGxvdDogR3JvdXAgTWVhbnMNCg0KTm93LCBsZXQncyBzZWUgaWYgcGVvcGxlIHdobyBmZWVsIHJ1c2hlZCB0ZW5kIHRvIHdvcmsgbW9yZSB0aGFuIHBlb3BsZSB3aG8gZG8gbm90IGZlZWwgcnVzaGVkLiBUbyByZXByZXNlbnQgdGhlIG1lYW4gZm9yIGVhY2ggZ3JvdXAgb3Igc29tZSBvdGhlciB2YXJpYWJsZSwgeW91IGFkZCBib3RoIGFuIHggYW5kIGEgeSB2YXJpYWJsZSB0byB0aGUgYGFlcygpYCBmdW5jdGlvbiBhbmQgdXNlIHRoZSBgZ2VvbV9iYXIoc3RhdCA9ICJzdW1tYXJ5IilgLiBOb3RlIHRoYXQgdGhlIFktYXhpcyBzY2FsZSBoYXMgbm93IGFkanVzdGVkIHRvIGEgc2NhbGUgdGhhdCBtYXRjaGVzIHRoZSB2YXJpYWJsZSB3ZSBhcmUgdXNpbmcgKGkuZS4sIG1lYW4gbnVtYmVyIG9mIG1pbnV0ZXMgc3BlbnQgd29ya2luZyBmb3IgZWFjaCBncm91cCkuIA0KDQpgYGB7cn0NCmdncGxvdChqc19kYXRhLCBhZXMoeCA9IGFzLmZhY3Rvcihpc0ZlZWxSdXNoZWQpLCB5ID0gZHVyV29yaykpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJzdW1tYXJ5IikgKyANCiAgeGxhYigiRmVlbGluZyBSdXNoZWQiKSArDQogIHlsYWIgKCJBdmVyYWdlIE1pbnV0ZXMgV29ya2luZyIpIA0KYGBgDQoNCjo6OndhbGt0aHJvdWdoDQogIC0gTW9zdCBwbG90cyB3aWxsIHN0YXJ0IHdpdGggdGhlIGBnZ3Bsb3QoKWBmdW5jdGlvbg0KICAtIFlvdSBoYXZlIHRvIGluY2x1ZGUgdGhlIG9iamVjdCB3aGVyZSBgZ2dwbG90KClgIHdpbGwgZ2V0IHRoZSBpbmZvcm1hdGlvbiBmb3IgdGhlIHBsb3QgZnJvbS4gSW4gdGhpcyBjYXNlLCBpdCdzIG91ciBkYXRhc2V0IGBqc19kYXRhYA0KICAtIFdpdGhpbiB0aGUgYGFlcygpYCBmdW5jdGlvbiwgd2UgaWRlbnRpZnkgd2hhdCB0aGUgeCBhbmQgeSB2YXJpYWJsZXMgYXJlIGZvciB0aGlzIHBsb3QNCiAgLSBXZSBhZGQgbGF5ZXJzIHVzaW5nIHRoZSBgK2Agc2lnbg0KICAtIE5leHQgd2UgdGVsbCBgZ2dwbG90KClgIHdoYXQgdHlwZSBvZiBwbG90IHdlIGFyZSBtYWtpbmc7IGluIHRoaXMgY2FzZSB3ZSBhcmUgY3JlYXRpbmcgYSBiYXIgcGxvdCB1c2luZyB0aGUgZnVuY3Rpb24gYGdlb21fYmFyKClgDQogIC0gVGhlbiB3ZSBhZGQgd2hhdCB0eXBlIG9mIHN0YXRpc3RpYyB3ZSB3YW50IHByZXNlbnRlZCBvbiB0aGUgcGxvdC4gSGVyZSB3ZSBhc2sgZm9yIHRoZSBtZWFuIG9mIGVhY2ggZ3JvdXAgZm9yIHRoZSB2YXJpYWJsZSBkdXJXb3JrIHVzaW5nIHRoZSBmdW5jdGlvbiBgZ2VvbV9iYXIoc3RhdCA9ICJzdW1tYXJ5IilgDQogIC0gV2UgYWRkIGEgbGFiZWwgdG8gdGhlIHgtYXhpcyB3aXRoIGBsYWIoIkZlZWxpbmcgUnVzaGVkIilgDQogIC0gV2UgYWRkIHRoZSBsYWJlbCB0byB0aGUgeS1heGlzIHdpdGggYHlsYWIgKCJUaW1lIFdvcmtpbmciKWANCiAgDQo6OjoNCg0KIyMjIEltcHJvdmVkIEJveCBQbG90ISANClRoaXMgcGxvdCBnZXRzIHRoZSBpZGVhIGFjcm9zcywgYnV0IHdlIGNhbiBhZGQgbW9yZSBsYXllcnMgYW5kIGZ1bmN0aW9ucyB0byBtYWtlIG1vcmUgYWRqdXN0bWVudHMuIFNlZSB0aGUgd2Fsa3Rocm91Z2ggYmVsb3cgZm9yIGhvdyB3ZSBtYWRlIGFsbCB0aGVzZSBjaGFuZ2VzLg0KDQpgYGB7cn0NCg0KcGxvdGxhYmVscyA8LSBjKCJOb3QgUnVzaGVkIiwgIlJ1c2hlZCIsICJEaWQgTm90IFJlc3BvbmQiKQ0KDQpnZ3Bsb3QoanNfZGF0YSwgYWVzKHggPSBhcy5mYWN0b3IoaXNGZWVsUnVzaGVkKSwgeSA9IGR1cldvcmspKSArDQogIGdlb21fYmFyKHN0YXQgPSAic3VtbWFyeSIsIGZpbGwgPSAiIzJENUU3RiIpICsgDQogIHhsYWIoIkZlZWxpbmcgUnVzaGVkIikgKw0KICB5bGFiICgiQXZlcmFnZSBNaW51dGVzIFdvcmtpbmciKSArDQogIGxhYnModGl0bGUgPSAiV29ya2luZyBhbmQgRmVlbGluZyBSdXNoZWQiKSArDQogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gcGxvdGxhYmVscykgKw0KICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksDQogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMjUsIGhqdXN0ID0gMSkpDQogIA0KYGBgDQoNCjo6OndhbGt0aHJvdWdoDQogIC0gV2UgY2FuIGNoYW5nZSB0aGUgY29sb3Igb2YgdGhlIGJhcnMgdXNpbmcgYGZpbGwgPSBgLiBIZXJlIHdlIGFkZGVkIHRoZSBzcGVjaWZpYyBjb2xvciB1c2luZyBhIGhleCBjb2RlLiBCdXQgeW91IGNhbiBhbHNvIHdyaXRlIGluIHRoZSBuYW1lcyBvZiBjb2xvcnMgc3VjaCBhcyAiYmx1ZSIuDQogIC0gVXNpbmcgYGxhYnModGl0bGUgPSAiIilgIHdlIGFkZGVkIGFuIG92ZXJhbGwgdGl0bGUgdG8gdGhlIHBsb3QNCiAgLSBXZSBwcm9iYWJseSB3YW50IHRvIGluZGljYXRlIHdoYXQgZWFjaCBjYXRlZ29yeSByZXByZXNlbnRzLCByYXRoZXIgdGhhbiB0aGUgIjAiLCAiMSIgYW5kICJOQSIgbGFiZWxzLiBUbyBhZGQgdGV4dCBsYWJlbHMsIHdlIGZpcnN0IGNyZWF0ZSBhbiBvYmplY3Qgd2l0aCBlYWNoIG9mIHRob3NlIGxhYmVscyBpbiBvcmRlciAoYHBsb3RsYWJlbHMgPC0gYygiTm90IFJ1c2hlZCIsICJSdXNoZWQiLCAiRGlkIE5vdCBSZXNwb25kIilgKS4gVGhlbiBpbiB0aGUgYHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gcGxvdGxhYmVscylgIGZ1bmN0aW9uIHdlIGNhbGwgdG8gdGhhdCBvYmplY3Qgd2UgY3JlYXRlZCBhcyB0aGUgbGFiZWxzIGZvciB0aGUgeC1heGlzLiBUaGVyZSBhcmUgbWFueSBvdGhlciB3YXlzIHRvIGFkanVzdCB0aGUgbGFiZWxzIGZvciBlYWNoIGF4aXMsIGJ1dCB0aGlzIG1ldGhvZCB3b3JrcyB3ZWxsIGZvciBhIHNtYWxsIG51bWJlciBvZiBncm91cHMuDQogIC0gSW4gdGhlIGB0aGVtZSgpYCBsYXllciB5b3UgY2FuIGFkZCBtYW55IGRpZmZlcmVudCBzcGVjaWZpY2F0aW9ucy4gSGVyZSB3ZSBhZGRlZCBgdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpYCB0byBtYWtlIHRoZSB0ZXh0IHNpemUgYmlnZ2VyIHRoYW4gdGhlIGRlZmF1bHQgYW5kIG1hZGUgdGhlIHgtYXhpcyBsYWJlbHMgYW5nbGVkIHNvIHRoZXkgZml0IGJldHRlciB1c2luZyBgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAyNSwgaGp1c3QgPSAxKWAuIFRoZSBgaGp1c3QgPSBgIGFkanVzdHMgdGhlIHZlcnRpY2FsIGxvY2F0aW9uIG9mIHRoZSBheGlzIGxhYmVscyBzbyB0aGV5IGRvbid0IG92ZXJsYXAgd2l0aCB0aGUgcGxvdCBpdHNlbGYuIGB2anVzdCA9IGAgY2FuIGJlIHVzZWQgdG8gbW92ZSB0aGUgbGFiZWxzIHJpZ2h0IGFuZCBsZWZ0LiANCiAgDQo6OjoNCg0KOjo6cXVlc3Rpb24NClRyeSBtYW5pcHVsYXRpbmcgdGhpcyBwbG90IGluIHNvbWUgd2F5LiBDYW4geW91IGNoYW5nZSB0aGUgY29sb3Igb2YgdGhlIGJhcnM/IFdoYXQgaGFwcGVucyBpZiB5b3UgY2hhbmdlIGBhbmdsZSA9IDkwYD8NCjo6Og0KDQojIyMgQmFzaWMgU2NhdHRlciBQbG90DQpOb3cgbGV0J3MgbWFrZSBhIHNjYXR0ZXIgcGxvdCB0byB2aXN1YWxpemUgdHdvIGNvbnRpbnVvdXMgdmFyaWFibGVzLiBXZSBhcmUgZ29pbmcgdG8gY2hlY2sgaWYgaXQgbG9va3MgbGlrZSB0aGVyZSBpcyBhIGNvcnJlbGF0aW9uIGJldHdlZW4gaG93IG11Y2ggdGltZSBwZW9wbGUgd29yayAoZHVyV29yaykgYW5kIGhvdyBtdWNoIHRoZXkgc2xlZXAgKGR1clNsZWVwKS4NCg0KYGBge3J9DQpnZ3Bsb3QoanNfZGF0YSwgYWVzKGR1cldvcmssIGR1clNsZWVwKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aCgpICsNCiAgeGxhYigiTWludXRlcyBTcGVudCBXb3JraW5nIikgKw0KICB5bGFiICgiTWludXRlcyBTcGVudCBTbGVlcGluZyIpDQogICAgICANCmBgYA0KDQo6Ojp3YWxrdGhyb3VnaA0KICAtIEFnYWluIHdlIHN0YXJ0IHdpdGggdGhlIGBnZ3Bsb3QoKWAgZnVuY3Rpb24sIGluY2x1ZGluZyB0ZWxsaW5nIGl0IHRvIHVzZSB0aGUgZGF0YXNldCBganNfZGF0YWANCiAgLSBJbiB0aGUgYGFlcygpYCBmdW5jdGlvbiB3ZSBsaXN0IHRoZSB4IGFuZCB5IHZhcmlhYmxlcyAoaGVyZSBkdXJXb3JrIGFuZCBkdXJTbGVlcCkNCiAgLSBUaGUgYGdlb21fcG9pbnQoKWAgZnVuY3Rpb24gaXMgd2hhdCBtYWtlcyBhIHNjYXR0ZXIgcGxvdA0KICAtIFRoZSBnZW9tX3Ntb290aCgpIGlzIHdoYXQgYWRkcyB0aGUgY29ycmVsYXRpb24gbGluZSB0byB0aGUgcGxvdA0KICAtIFRoZSBgeGxhYigpYCBhbmQgYHlsYWIoKWAgYWRkIHRoZSB4LWF4aXMgYW5kIHktYXhpcyBsYWJlbHMgdG8gdGhlIHBsb3QNCiAgDQo6OjoNCiMjIyBJbXByb3ZlZCBTY2F0dGVyIFBsb3QhDQoNCk5vdyBsZXQncyBhZGQgc29tZSBsYXllcnMgYW5kIGFlc3RoZXRpYyBhZGp1c3RtZW50cyB0byBpbXByb3ZlIHRoaXMgcGxvdC4NCg0KYGBge3J9DQpnZ3Bsb3QoanNfZGF0YSwgYWVzKGR1cldvcmssIGR1clNsZWVwKSkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gIiMyRDVFN0YiLCBhbHBoYSA9IC4yKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCBjb2xvciA9ICJibGFjayIpICsNCiAgeGxhYigiTWludXRlcyBTcGVudCBXb3JraW5nIikgKw0KICB5bGFiICgiTWludXRlcyBTcGVudCBTbGVlcGluZyIpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxNTAwLCAyNTApKSArDQogIGxhYnModGl0bGUgPSAiQXNzb2NpYXRpb24gQmV0d2VlbiBXb3JraW5nIGFuZCBTbGVlcGluZyIpICsNCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpKQ0KYGBgDQoNCjo6OndhbGt0aHJvdWdoDQogIC0gV2UgY2FuIGNoYW5nZSB0aGUgY29sb3Igb2YgdGhlIGRhdGEgcG9pbnRzIGJ5IGFkZGluZyBgY29sb3IgPSAiIzJENUU3RiJgIGFuZCBjaGFuZ2UgdGhlIHRyYW5zcGFyZW5jeSBvZiB0aGUgZGF0YSBwb2ludHMgKHNvIHlvdSBjYW4gc2VlIHdoZXJlIHRoZXJlIGFyZSBvdmVybGFwcGluZyBjbHVzdGVycykgd2l0aCB0aGUgYWRkaXRpb24gb2YgYGFscGhhID0gLjJgLiANCiAgLSBJbiB0aGUgYGdlb21fc21vb3RoKClgIGZ1bmN0aW9uIHdlIGNoYW5nZWQgdGhlIG1ldGhvZCBmb3IgY2FsY3VsYXRpbmcgdGhlIGxpbmUgdG8gYmUgbGluZWFyIChpbnN0ZWFkIG9mIHRoZSBnZ3Bsb3QgZGVmYXVsdCkgdXNpbmcgYG1ldGhvZCA9IGxtYCBhbmQgYWRqdXN0ZWQgdGhlIGNvbG9yIG9mIHRoZSBsaW5lIHdpdGggYGNvbG9yID0gYC4gUmVtZW1iZXIgdGhhdCB5b3UgY2FuIHdyaXRlIGhleCBjb2RlIG51bWJlcnMgb3IgdGhlIG5hbWVzIG9mIGNvbG9ycyB0byBhZGp1c3QgY29sb3JzLiBNYWtlIHN1cmUgYm90aCBhcmUgaW4gcXVvdGVzIHRvIGF2b2lkIGVycm9yIG1lc3NhZ2VzLiANCiAgLSBXZSBhZGp1c3QgdGhlIHgtYXhpcyB0aWNrIG1hcmtzIHdpdGggdGhlIGBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDE1MDAsIDI1MCkpYCBwYXJ0LCB3aGljaCB0ZWxscyBSIHRvIHBsb3QgdGhlIHgtYXhpcyBvbiBhIHNlcXVlbmNlIGZyb20gMCB0byAxNTAwICh0aGlzIGNhcHR1cmVzIGFsbCBvZiB0aGUgcmVzcG9uc2VzIGluIG91ciBkYXRhKSwgd2l0aCBsYWJlbHMgZXZlcnkgMjUwIG1pbnV0ZXMuIA0KICAtIFRoZSBgbGFicyh0aXRsZSA9ICJBc3NvY2lhdGlvbiBCZXR3ZWVuIFdvcmtpbmcgYW5kIFNsZWVwaW5nIilgIGFkZHMgYW4gb3ZlcmFsbCB0aXRsZSB0byB0aGUgcGxvdA0KICAtIExhc3RseSwgYHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSlgIGFkanVzdCB0aGUgdGV4dCBzaXplDQogIA0KOjo6DQoNCjo6OnF1ZXN0aW9uDQpUcnkgbWFuaXB1bGF0aW5nIHRoaXMgcGxvdCBpbiBzb21lIHdheS4gQ2FuIHlvdSBtYWtlIHRoZSBkYXRhIHBvaW50cyBtb3JlIGFuZCBsZXNzIHRyYW5zcGFyZW50PyBDYW4geW91IGNoYW5nZSB0aGUgY29sb3Igb2YgdGhlIGxpbmU/IFdoYXQgaGFwcGVucyBpZiB0aGUgeC1heGlzIGhhcyBsYWJlbHMgZXZlcnkgMTAwIG1pbnV0ZXM/DQo6OjoNCg0KIyMjIFlvdXIgVHVybiENCg0KOjo6bm90ZQ0KDQpGb3IgdGhlIGZvbGxvd2luZyBxdWVzdGlvbnMsIHRoZSBnb2FsIGlzIHRvIGdldCB5b3UgdGhpbmtpbmcgYWJvdXQgdGhlIGJ1aWxkaW5nIGJsb2NrcyB0aGF0IGdvIGludG8gbWFraW5nIGEgdmlzdWFsLiAgRG9uJ3QgZ2V0IHRvbyBmb2N1c2VkIG9uIHRoZSBmaW5pc2hlZCBwcm9kdWN0IHJpZ2h0IGF3YXksIGFuZCB0cnkgdG8gdGhpbmsgYWJvdXQgdGhlIGluZGl2aWR1YWwgc3RlcHMgbmVlZGVkIHRvIGdldCB5b3UgdG8gdGhlcmUuICBBcyB3aXRoIGFueSBjb2RpbmcgdGFzaywgaXQgY2FuIGJlIHZlcnkgaGVscGZ1bCB0byB0aGluayBvZiBvciB3cml0ZSBlYWNoIG9mIHRoZSBzdGVwcyBkb3duIGluIHBsYWluIEVuZ2xpc2gsIHRoZW4gd29yayB0aHJvdWdoIGVhY2ggb25lLiAgDQoNCkFzIGEgZmluYWwgbm90ZSwgYSAqKmh1Z2UqKiBwYXJ0IG9mIGxlYXJuaW5nIGhvdyB0byBjb2RlIGlzIHVzaW5nIHNlYXJjaCBlbmdpbmVzLCBhbmQgdGhlcmUgaXMgYSB0b24gb2YgZG9jdW1lbnRhdGlvbiBvdXQgdGhlcmUgdG8gaGVscCB5b3UuICBUaGlzIGFsc28gcmVsYXRlcyBiYWNrIHRvIHB1dHRpbmcgdGhpbmdzIGludG8gcGxhaW4tbGFuZ3VhZ2Ugc3RlcHMsIGJlY2F1c2UgYmVpbmcgYWJsZSB0byBjbGVhcmx5IGFydGljdWxhdGUgeW91ciBnb2FscyBpcyBzeW5vbnltb3VzIHdpdGggdXNpbmcgZ29vZCBzZWFyY2ggdGVybXMuDQoNClRoZSBkYXRhIGRpY3Rpb25hcnkgd2lsbCBhbHNvIGJlIGFuIGFzc2V0IGluIHdvcmtpbmcgdGhyb3VnaCB0aGlzIGV4ZXJjaXNlOg0KDQpgYGB7ciwgZWNobz1GQUxTRSwgb3V0LndpZHRoPSI5NSUiLCBvdXQuaGVpZ2h0PSI1MDBpbiJ9DQogICAga25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImltYWdlcy9CbG9jazQtMl9EaWN0aW9uYXJ5LnBkZiIpDQpgYGANCg0KR29vZCBsdWNrIQ0KDQo6OjoNCg0KDQo6OjogcXVlc3Rpb24NClRha2UgYSBsb29rIGF0IG91ciBkYXRhIHNldCBhbmQgbWFrZSB0d28gbmV3IHBsb3RzLiANCg0KMS4gTWFrZSBhIHBsb3QgY29tcGFyaW5nIGdyb3VwcyAoaS5lLiwgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSkgb24gb25lIG9mIHRoZSBkdXJhdGlvbiB2YXJpYWJsZXMgKGkuZS4sIGEgY29udGludW91cyB2YXJpYWJsZSkuDQoNCjIuIE1ha2UgYSBwbG90IGNvbXBhcmluZyB0d28gY29udGludW91cyB2YXJpYWJsZXMuIA0KOjo6DQoNCjo6OiBxdWVzdGlvbg0KDQoqKkNoYWxsZW5nZToqKiBXaGF0IGlzIG9uZSBjb21wb25lbnQgb2YgeW91ciBwbG90IHlvdSB3b3VsZCBsaWtlIHRvIGNoYW5nZT8gQ2FuIHlvdSBsb29rIHVwIGEgc29sdXRpb24/IA0KOjo6DQoNCjo6OnF1ZXN0aW9uDQpTZWUgaWYgeW91IGNhbiByZWNyZWF0ZSB0aGUgcGxvdCBiZWxvdy4NCg0KPGJyPg0KDQoqKkhpbnQ6KiogVGhpcyB0eXBlIG9mIGNoYXJ0IGlzIGNhbGxlZCBhICJwcm9wb3J0aW9uYWwgYmFyIGNoYXJ0IiwgYW5kIHNlYXJjaCBlbmdpbmVzIGFyZSB5b3VyIGJlc3QgZnJpZW5kIQ0KDQpgYGB7ciBlY2hvPUZBTFNFLCBmaWcuYWxpZ249J2NlbnRlcid9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaW1hZ2VzL2Jsb2NrNS0yX3Byb3YtZWR1LnBuZyIpDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gJ2ZvbGQtaGlkZScsIHJlc3VsdHMgPSAnaGlkZSd9DQpwcm92X2VkdV9wbG90IDwtIGdncGxvdChqc19kYXRhLCBhZXMoeCA9IHByb3ZpbmNlX2ZhY3QsIGZpbGwgPSBlZHVMZXZlbF9mYWN0KSkgKw0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlByb3BvcnRpb24gb2YgRWR1Y2F0aW9uIExldmVscyBieSBQcm92aW5jZSIsDQogICAgeCA9ICJQcm92aW5jZSIsDQogICAgeSA9ICJQcm9wb3J0aW9uIiwNCiAgICBmaWxsID0gIkVkdWNhdGlvbiBMZXZlbCINCiAgKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KOjo6DQoNCjo6OnF1ZXN0aW9uDQpTZWUgaWYgeW91IGNhbiByZWNyZWF0ZSB0aGUgcGxvdCBiZWxvdy4gIA0KPGJyPg0KKipIaW50OioqIEZpcnN0IHRyeSB0byByZWNyZWF0ZSB0aGUgcGxvdCB3aXRoIE5BIHZhbHVlcy4gIFRoZW4sIHRvIHJlbW92ZSBgTkFgIHZhbHVlcyBmcm9tIHRoZSBwbG90LCB5b3Ugd2lsbCBuZWVkIHRvIHVzZSBib3RoIHRoZSBgbmEub21pdGAgYW5kIHRoZSBgbmEucm0gPSBUUlVFYCBjb21tYW5kcy4NCg0KDQpgYGB7ciBlY2hvPUZBTFNFLCBmaWcuYWxpZ249J2NlbnRlcid9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaW1hZ2VzL2Jsb2NrNS0yX3RpbWUtYWxvbmUucG5nIikNCmBgYA0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJywgcmVzdWx0cyA9ICdoaWRlJ30NCnRpbWVfYWxvbmVfcGxvdCA8LSBnZ3Bsb3QobmEub21pdChqc19kYXRhKSwgYWVzKHggPSBmYWN0b3IodGltZVdhbnRBbG9uZSksIHkgPSBkdXJBbG9uZSkpICsNCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAiYmx1ZSIsIG5hLnJtID0gVFJVRSkgKw0KICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGMoDQogICAgIjEiID0gIldvdWxkIGxpa2UgbW9yZSB0aW1lIGFsb25lIiwNCiAgICAiMiIgPSAiRG8gbm90IHdhbnQgbW9yZSB0aW1lIGFsb25lIg0KICApKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFRpbWUgU3BlbnQgQWxvbmUgYnkgRGVzaXJlIHRvIEJlIEFsb25lIiwNCiAgICB4ID0gIkRlc2lyZSB0byBTcGVuZCBUaW1lIEFsb25lIiwNCiAgICB5ID0gIkR1cmF0aW9uIFNwZW50IEFsb25lIC0gTWludXRlcyINCiAgKQ0KYGBgDQoNCjo6Og0KDQoNCg0KIyMgU2F2ZSBZb3VyIFdvcmsNCg0KV2UgZGlkbid0IG1ha2UgYW55IGNoYW5nZXMgdG8gdGhlIGRhdGEgaW4gdGhpcyBzZXNzaW9uLCBidXQgd2UgZGlkIGNyZWF0ZSBhIG5ldyBSIHNjcmlwdC4gIFNhdmUgdGhhdCBzY3JpcHQgYW5kIGJhY2sgaXQgdXAgdG8gT1NGLg0KDQohW10oaW1hZ2VzL29zZi9vc2ZVcGxvYWQuZ2lmKQ0KDQoNCiMjIFVwZGF0ZSBEb2N1bWVudGF0aW9uDQoNCldlJ3ZlIGFkZGVkIGEgbmV3IGZpbGUgdG8gb3VyIHByb2plY3QsIHNvIHdlJ2xsIGFsc28gbmVlZCB0byB1cGRhdGUgdGhlIFJFQURNRSBkb2N1bWVudCB0byByZWZsZWN0IHRoaXMuDQoNCg0KDQoNCg0KDQo=