Notes: The purpose of this script is to introduce the ggplot package. This introduction will go through creating the base ggplot, attaching data to your plot, setting aesthetics, working with basic color schemes and palettes, facetting, facet grids, plotting multiple variables, and the geometries for line plots, smoothers, scatterplots, boxplots, bar plots, histograms, jittering, heatmaps. Culminating in plotting a regression model with confidence intervals

Clear environment and plotting window ———————————–

knitr::opts_chunk$set(fig.pos = "!h")
rm(list = ls())
# set working directory to the Data Days output folder, or wherever your data is stored
setwd("C:/Users/keess/Box/ASA Share/ASA 2020/ASA Meetings/Data Days Spring 2021/Data Days output")
The working directory was changed to C:/Users/keess/Box/ASA Share/ASA 2020/ASA Meetings/Data Days Spring 2021/Data Days output inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
library(tidyverse)

Starting with the Diamonds dataset in tidyverse ———————————

df <- diamonds
head(df)
Error in gregexpr(calltext, singleline, fixed = TRUE) : 
  regular expression is invalid UTF-8

How does ggplot work?

ggplot stands for “grammar of graphics” plot, and the package breaks down plotting into a layered system consisting of global settings using the function ggplot(), one or more geometries called geoms (geom_boxplot, geom_point, geom_line), and finishing with themes, layouts, text, color schemes, and other design features that make ggplots extremely flexible. We’ll try a simple scatterplot to start with.

# simple scatterplots of diamond price by carat
df %>%
  ggplot(aes(x = carat, y = price)) +
  geom_point()

Here we see a simple scatterplot of the relationship of diamond price by carat. The aes() stands for aesthetic, which means that we are mapping data to the ggplot’s x or y aesthetic. There are a lot of points here, so we can use one of geom_point’s settings alpha to make our points more transparent. This is one of the ways to check on the density of points in a scatterplot when we have overplotted.

p <- df %>%
  ggplot(aes(x = carat, y = price)) +
  geom_point(alpha = 0.1)
print(p)

like I said before, ggplot is a layered graphics system, so we can add geometries on top of our already present layers

p_smooth <- p + geom_smooth(method = 'loess', se = F)
p_smooth

The smoother fits pretty well where there are a lot of data points, but the ending piece is a bit nonsensical because of the lack of data points. We might want to consider reducing our x limit so that we are focusing on areas with a lot of data

we can also split our data up by faceting, which is splitting up our plots by category

p_smooth + facet_wrap(~cut)

here we split our plot up by different cuts. I’ve also shown the layered nature of ggplot, where graphing elements are added one on top of the other. If we were to write that whole plot out using code (and adding labels) it would look like this:

df %>%
  ggplot(aes(x = carat, y = price)) +
  geom_point(alpha = 0.1) +
  geom_smooth(method = 'loess', se = F) +
  facet_wrap(~cut) +
  labs(title = "Price vs. Carat, Faceted by Diamond Cut",
       x = 'Carat', y = 'Price', subtitle = "ggplot tutorial for EDA",
       caption = "By Kees Schipper")

Now let’s do the same layered process for a boxplot ———————

start by simply looking at the distribution of price in our entire dataset

df %>%
  ggplot(aes(y = price)) +
  geom_boxplot()

clearly not very informative. Let’s split price into cut

df %>%
  ggplot(aes(x = cut, y = price)) +
  geom_boxplot()

this gives us a little more information, but we can improve on our boxplot aesthetics with notches and outlier colors

df %>%
  ggplot(aes(x = cut, y = price)) +
  geom_boxplot(notch = TRUE, outlier.color = 'blue', fill = 'grey40')

we can increase the dimensions accross which we visualize our data by adding facets again. Let’s look at the relationship between cut and price, split by clarity

df %>%
  ggplot(aes(x = cut, y = price)) +
  geom_boxplot(outlier.color = 'blue') +
  facet_wrap(~clarity)

here our notches aren’t especially useful, and we also see that labels tend to overlap we could try flipping our axes, which usually gives more room for each x axis label

df %>%
  ggplot(aes(x = cut, y = price)) +
  geom_boxplot(outlier.color = 'blue') +
  facet_wrap(~clarity) +
  coord_flip()

this is still a little confusing but at least the labels don’t overlap! Finally, we can add labels. Also, let’s keep the coordinates as they were originally, but we can use the ggplot theme to tilt our x labels

df %>%
  ggplot(aes(x = cut, y = price)) +
  geom_boxplot(outlier.color = 'blue') +
  facet_wrap(~clarity) +
  theme(axis.text.x = element_text(hjust = 1, angle = 45)) +
  labs(title = "Price of Different Diamond Cuts, Stratified by Clarity")

I almost forgot! We can also add summary statistics to our boxplot with stat_summary() we can also look at adding jitter so that we can see individual data points in our plots, and get a better visual of the distribution of points

df %>%
  ggplot(aes(x = cut, y = price)) +
  geom_boxplot(outlier.color = 'blue') +
  stat_summary(fun = 'mean', col = 'red', size = 0.2) +
  geom_jitter(color = 'brown', alpha = 0.02) +
  facet_wrap(~clarity) +
  theme(axis.text.x = element_text(hjust = 1, angle = 45)) +
  labs(title = "Price of Different Diamond Cuts, Stratified by Clarity")

Now we can look at histograms and density plots ————————-

df %>%
  ggplot(aes(x = price)) +
  geom_histogram(bins = 200) # specify number of bins to increase detail in distribution

Like with other plots, we can color, facet, group, etc… but now we can also stack different categories using a position argument

df %>%
  ggplot(aes(x = price, fill = cut)) +
  geom_histogram(bins = 200, position = 'stack')

other position arguments are dodge and fill. I’ll leave it to the reader to test these out and see what these do. dodge is better used with bar plots (not histograms) and fill is good for examining proportions, as we shall soon see

The above visual isn’t super useful if we’re trying to see the proportions of each cut that make up the distribution, let’s try changing the fill argument

df %>%
  ggplot(aes(x = price, fill = cut)) +
  geom_histogram(bins = 200, position = 'fill')

now we can see the relative proportions of what diamonds make up which price however, the jitteriness of this plot still isn’t fantastic. This is where density plots come in

df %>%
  ggplot(aes(x = price, fill = cut)) +
  geom_density(alpha = 0.25, color = "black")

Here we can visualize the smoothed shape of multiple distributions. However, it’s hard to see the different distributions when they’re one on top of the other. Let’s try stacking instead

df %>%
  ggplot(aes(x = price, fill = cut)) +
  geom_density(position = 'stack')

slightly better, but we still run into the same problem where we get a good picture of the entire distribution, but proportions of cut for each price are still difficult to distinguish. We can now see what fill does.

df %>%
  ggplot(aes(x = price, fill = cut)) +
  geom_density(position = 'fill', color = "black")

Interestingly, all diamond cuts except for fair have a dip in the price range of around 4000-5000. Maybe this is because jewlers tend to start charging at around 5000, and don’t bother with mid-range prices.

A geom halfway between histograms and the densit plot is the ‘geom_freqpoly’ which makes a frequency polygon with jagged edges matching a given number of bins. However, I don’t believe you can give freqpoly a fill

df %>%
  ggplot(aes(x = price, color = cut)) +
  geom_freqpoly(bins = 50, size = 1) +
  scale_color_hue()

some other geoms that may be worth trying ——————————-

# geom hex
df %>%
  ggplot(aes(x = carat, y = price)) +
  geom_hex() +
  scale_fill_viridis_c()


# geom_density_2d
df %>%
  ggplot(aes(x = carat, y = price)) +
  geom_density_2d()


# geom_density_2d_fill
df %>%
  ggplot(aes(x = carat, y = price)) +
  geom_density_2d_filled() +
  geom_density_2d(color = "black") +
  ylim(0, 6000) +
  xlim(0, 1)


# violin plots
df %>%
  ggplot(aes(x = cut, y = price, fill = cut)) +
  geom_violin() +
  facet_wrap(~clarity)

These two geoms are useful for setting vertical and horizontal reference lines in time series, when checking model residuals… ?geom_hline ?geom_vline

for all of the geoms, you can type geom_ and scroll through the autocomplete to see what is available. There are also a bunch of other ggplot-based packages that have some cool premade graphs for individuals to take advantage of. ggpairs creates a panel plot matrix to compare relationships between variables within a dataset, calculating correlation coefficients and histograms along the diagonal. sjPlot has a lot of really cool plots for comparing model parameters (which I’ve used extensively). There’s also ggridges, which creates a ridgeline plot to compare multiple distributions, time series curves, or other curves along the same x axis.

The below example was taken from the following weblink: https://www.r-graph-gallery.com/294-basic-ridgeline-plot.html

# library
library(ggridges)
library(ggplot2)
 
# Diamonds dataset is provided by R natively
#head(diamonds)
 
# basic example
ggplot(diamonds, aes(x = price, y = cut, fill = cut)) +
  geom_density_ridges() +
  theme_ridges() + 
  theme(legend.position = "none")

Here’s some code for making visualizations with COVID data

I didn’t get to annotating, so feel free to take a look and see what you like! I’m probably not going to go through this section in the workshop, as it involves a lot of data cleaning, which isn’t the point of introduction to ggplot. Take a look at the pivot sections, as having your data in long format is key to having versatile data for ggplot.

# read in data ------------------------------------------------------------
# load in COVID data for all counties across the united states
load('../Data Days output/COVID_master_20200218.RData')

# select a county or state that you're interested in:
MACovid <- COVID_master %>%
            filter(state == "Massachusetts" & Population > 0) %>%
            group_by(date) %>%
            summarise(across(where(is.numeric), ~sum(.x, na.rm = T))) %>%
  select(date, cases, deaths:daily_deaths_100k)
# we now have data for the entire state of Massachusetts. If you want to work with
# a different state, change Massachusetts to whatever state that interests you!


# simple visualizations of the data ---------------------------------------
# let's examine a scatterplot of cases in Massachusetts compared to deaths

# this is about as simple a ggplot as you can get. 
ggplot(data = MACovid, aes(x = daily_cases, y = daily_deaths)) +
  geom_point()


# your ggplot specifies global options, so you don't have to specify aesthetics
# in your added geometries. This has benefits and drawbacks...

# We can also store this as a ggplot object and add to it
p <- ggplot(data = MACovid, aes(x = daily_cases, y = daily_deaths))
# p for plot
p # gives us an empty plot with scales corresponding to our data



# adding to a ggplot object -----------------------------------------------

# let's add some points, and a line showing the relationship of our points:
p +
  geom_point() +
  geom_smooth(se = T, span = 0.2, method = "loess", color = 'red') +
  labs(title = "COVID Deaths vs. Cases")


# geom_smooth does a simple loess smoother by default, over our data. Note:
# you don't actually need to plot your points to get a smoother, R knows what
# data you are using, so the smoother is only dependent on your data, not the
# plots that you have in ggplot beforehand

# methods of geom_smooth include linear models (lm) loess, and generalized 
# additve models (gam)


# boxplots ----------------------------------------------------------------
# I'm interested in differences of case and death counts by weekday. Let's get a
# variable for that

MAWeekday <- MACovid %>%
  mutate(weekday = factor(weekdays(date)))

summary(MAWeekday$weekday) # now each observation corresponds to a weekday
   Friday    Monday  Saturday    Sunday  Thursday   Tuesday Wednesday 
       56        56        56        56        56        56        57 
# let's plot case counts by weekday
MAWeekday %>%
  ggplot(aes(x = weekday, y = daily_cases)) +
  geom_boxplot() +
  stat_summary(fun = 'mean', color = 'red', geom = 'point')

# now we have boxplots of cases by weekday. We can also add a mean value indicator
# with stat_summary()

# If we like color, we could color weekdays differently
MAWeekday %>%
  ggplot(aes(x = weekday, y = daily_cases, fill = weekday)) +
  geom_boxplot(outlier.alpha = 0) +
  geom_jitter(color = "blue", alpha = 0.2) +
  stat_summary(fun = 'mean', color = 'red', geom = 'point')


# note the difference between specifying color in the aes() function vs in the general
# gemetry. aes() maps a data value to an inherent aspect of ggplot. Therefore, each
# different value of weekday returns a different color. If you specify color or fill
# outside of aes, that singular color is applied to all of the fills and/or colors for 
# that geometry.


# some other interesting geometries ---------------------------------------
# let's accumulate data by week first
week <- 1
daycount <- 0
MAWeekday$week <- 0
for (i in 1:nrow(MAWeekday)){
  MAWeekday$week[i] <- week
  daycount <- daycount + 1
  
  if (daycount == 7){
    
    daycount <- 1
    week <- week + 1
    
  }
}
# create a count of weeks

MAWeek <- MAWeekday %>%
  select(cases, deaths, daily_cases:week) %>%
  group_by(week) %>%
  summarise(across(is.numeric, sum))

# line graphs
MAWeek %>%
  ggplot(aes(x = week)) +
  geom_line(aes(y = daily_cases), size = 1, col = "blue") +
  geom_line(aes(y = daily_deaths), size = 1, col = "red") +
  labs(title = "Comparing Cases and Deaths in Massachusetts")


# geom histogram
MACovid %>%
  ggplot(aes(x = daily_cases)) +
  geom_histogram(bins = 70)


# can even stack histograms 
MACovid %>%
  pivot_longer(cols = c('daily_cases', 'daily_deaths'),
               names_to = 'outcome',
               values_to = 'values') %>%
  ggplot() +
  geom_histogram(aes(x = values, fill = outcome), position = "stack", bins = 30, col = "blue") 


# not the greatest visualization. Let's use a frequency polygon to compare distributions
MACovid %>%
  pivot_longer(cols = c('daily_cases', 'daily_deaths'),
               names_to = 'outcome',
               values_to = 'values') %>%
  ggplot() +
  geom_freqpoly(aes(x = values, color = outcome), size = 1, bins = 50) +
  labs(title = "Now we can see both distributions")



# can even use a density plot as an overlay -------------------------------

MAWeekday %>%
  ggplot() +
  geom_density(aes(x = daily_cases, group = weekday, color = weekday), size = 1) +
  labs(title = "Daily case count density functions by weekday--Hard to see")



# Let's facet! ------------------------------------------------------------
# faceting lets us separate our plots by a factor or character value
MAWeekday %>%
  ggplot() +
  geom_density(aes(x = daily_cases, group = weekday, color = weekday), size = 1) +
  facet_wrap(~weekday) +
  labs(title = "Faceted case counts by weekday--much clearer!!")


# let's look at other variables
MAWeekday %>%
  ggplot() +
  geom_density(aes(x = parks, group = weekday, color = weekday), size = 1) +
  facet_wrap(~weekday) +
  labs(title = "Faceted case counts by weekday--much clearer!!") +
  theme(legend.position = "none") # after faceting, we don't need a legend anymore!



# Let's look at a heatmap/density map -------------------------------------

MAWeekday %>%
  ggplot(aes(x = daily_cases, y = daily_deaths)) +
  geom_density_2d_filled() +
  geom_density_2d(color = 'black') +
  geom_point(color = 'black', alpha = 0.35) +
  scale_fill_brewer(palette = "Reds") +
  ylim(0, 100) +
  xlim(0, 3000) +
  facet_wrap(~weekday)


# Final note on faceting --------------------------------------------------
# if you want to facet your data by a category, your data needs to be in the
# following format (but not in this order):
# x column | y column | faceting column
# because of this, often times you will have to pivot your data for faceting.
# Let's do this to look at the relationships between deaths, cases, and mobility
# over time

facet_data <- MAWeekday %>%
  pivot_longer(cols = c('retail_rec':'daily_deaths'),
               names_to = 'vars',
               values_to = 'values') %>%
  select(date, vars, values, weekday)

facet_data %>%
  ggplot(aes(x = date, y = values)) +
  geom_line(aes(color = vars))


# see...not the most visible data. Much easier to facet

facet_data %>%
  ggplot(aes(x = date, y = values)) +
  geom_line(aes(color = vars)) +
  facet_wrap(~vars)


# you can also facet by multiple variables. You just need to use `facet_grid` and
# specify the column facet, and the row facet
facet_data %>%
  ggplot(aes(x = date, y = values)) +
  geom_line(aes(color = vars)) +
  facet_grid(cols = vars(vars), rows = vars(weekday), scales = 'free_y') +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))





# Further resources -------------------------------------------------------

# for more on visualizations in R, I always point people to the R graph gallery:
# https://www.r-graph-gallery.com/
# if you have an idea for a visualization, there is likely some code already in the
# gallery that can get you started on making your final visualization. If you're stuck
# I recommend checking it out.
LS0tDQp0aXRsZTogIkRhdGEgRGF5cyBnZ3Bsb3QgSW50cm8gZm9yIEVEQSINCnN1YnRpdGxlOiBCeSBLZWVzIFNjaGlwcGVyDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpOb3RlczogVGhlIHB1cnBvc2Ugb2YgdGhpcyBzY3JpcHQgaXMgdG8gaW50cm9kdWNlIHRoZSBnZ3Bsb3QgcGFja2FnZS4gVGhpcyANCmludHJvZHVjdGlvbiB3aWxsIGdvIHRocm91Z2ggY3JlYXRpbmcgdGhlIGJhc2UgZ2dwbG90LCBhdHRhY2hpbmcgZGF0YSB0byB5b3VyDQpwbG90LCBzZXR0aW5nIGFlc3RoZXRpY3MsIHdvcmtpbmcgd2l0aCBiYXNpYyBjb2xvciBzY2hlbWVzIGFuZCBwYWxldHRlcywNCmZhY2V0dGluZywgZmFjZXQgZ3JpZHMsIHBsb3R0aW5nIG11bHRpcGxlIHZhcmlhYmxlcywgYW5kIHRoZSBnZW9tZXRyaWVzDQpmb3IgbGluZSBwbG90cywgc21vb3RoZXJzLCBzY2F0dGVycGxvdHMsIGJveHBsb3RzLCBiYXIgcGxvdHMsIGhpc3RvZ3JhbXMsDQpqaXR0ZXJpbmcsIGhlYXRtYXBzLiBDdWxtaW5hdGluZyBpbiBwbG90dGluZyBhIA0KcmVncmVzc2lvbiBtb2RlbCB3aXRoIGNvbmZpZGVuY2UgaW50ZXJ2YWxzDQoNCg0KDQojIENsZWFyIGVudmlyb25tZW50IGFuZCBwbG90dGluZyB3aW5kb3cgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KYGBge3Igc2V0dXAsIGVjaG8gPSBUUlVFLCBtZXNzYWdlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy5wb3MgPSAiIWgiKQ0Kcm0obGlzdCA9IGxzKCkpDQojIHNldCB3b3JraW5nIGRpcmVjdG9yeSB0byB0aGUgRGF0YSBEYXlzIG91dHB1dCBmb2xkZXIsIG9yIHdoZXJldmVyIHlvdXIgZGF0YSBpcyBzdG9yZWQNCnNldHdkKCJDOi9Vc2Vycy9rZWVzcy9Cb3gvQVNBIFNoYXJlL0FTQSAyMDIwL0FTQSBNZWV0aW5ncy9EYXRhIERheXMgU3ByaW5nIDIwMjEvRGF0YSBEYXlzIG91dHB1dCIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQoNCg0KIyMgU3RhcnRpbmcgd2l0aCB0aGUgRGlhbW9uZHMgZGF0YXNldCBpbiB0aWR5dmVyc2UgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCmBgYHtyIGxvYWRpbmcgZGF0YX0NCmRmIDwtIGRpYW1vbmRzDQpoZWFkKGRmKQ0KYGBgDQoNCiMjIyBIb3cgZG9lcyBnZ3Bsb3Qgd29yaz8NCmdncGxvdCBzdGFuZHMgZm9yICJncmFtbWFyIG9mIGdyYXBoaWNzIiBwbG90LCBhbmQgdGhlIHBhY2thZ2UgYnJlYWtzIGRvd24gcGxvdHRpbmcgaW50byBhIGxheWVyZWQgc3lzdGVtIGNvbnNpc3Rpbmcgb2YgZ2xvYmFsIHNldHRpbmdzIHVzaW5nIHRoZSBmdW5jdGlvbiBgZ2dwbG90KClgLCBvbmUgb3IgbW9yZSBnZW9tZXRyaWVzIGNhbGxlZCBnZW9tcyAoYGdlb21fYm94cGxvdGAsIGBnZW9tX3BvaW50YCwgYGdlb21fbGluZWApLCBhbmQgZmluaXNoaW5nIHdpdGggdGhlbWVzLCBsYXlvdXRzLCB0ZXh0LCBjb2xvciBzY2hlbWVzLCBhbmQgb3RoZXIgZGVzaWduIGZlYXR1cmVzIHRoYXQgbWFrZSBnZ3Bsb3RzIGV4dHJlbWVseSBmbGV4aWJsZS4gV2UnbGwgdHJ5IGEgc2ltcGxlIHNjYXR0ZXJwbG90IHRvIHN0YXJ0IHdpdGguDQoNCg0KYGBge3IgZmlyc3QgZ2dwbG90fQ0KIyBzaW1wbGUgc2NhdHRlcnBsb3RzIG9mIGRpYW1vbmQgcHJpY2UgYnkgY2FyYXQNCmRmICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSkgKw0KICBnZW9tX3BvaW50KCkNCmBgYA0KSGVyZSB3ZSBzZWUgYSBzaW1wbGUgc2NhdHRlcnBsb3Qgb2YgdGhlIHJlbGF0aW9uc2hpcCBvZiBkaWFtb25kIHByaWNlIGJ5IGNhcmF0LiBUaGUgYGFlcygpYCBzdGFuZHMgZm9yIGFlc3RoZXRpYywgd2hpY2ggbWVhbnMgdGhhdCB3ZSBhcmUgbWFwcGluZyBkYXRhIHRvIHRoZSBnZ3Bsb3QncyB4IG9yIHkgYWVzdGhldGljLiBUaGVyZSBhcmUgYSBsb3Qgb2YgcG9pbnRzIGhlcmUsIHNvIHdlIGNhbiB1c2Ugb25lIG9mIGdlb21fcG9pbnQncyBzZXR0aW5ncyBgYWxwaGFgIHRvIG1ha2Ugb3VyIHBvaW50cyBtb3JlIHRyYW5zcGFyZW50LiBUaGlzIGlzIG9uZSBvZiB0aGUgd2F5cyB0byBjaGVjayBvbiB0aGUgZGVuc2l0eSBvZiBwb2ludHMgaW4gYSBzY2F0dGVycGxvdCB3aGVuIHdlIGhhdmUgb3ZlcnBsb3R0ZWQuDQoNCmBgYHtyIG92ZXJwbG90dGluZyBzb2x1dGlvbn0NCmRmICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC4xKQ0KYGBgDQoNCg0KbGlrZSBJIHNhaWQgYmVmb3JlLCBnZ3Bsb3QgaXMgYSBsYXllcmVkIGdyYXBoaWNzIHN5c3RlbSwgc28gd2UgY2FuIGFkZCBnZW9tZXRyaWVzIG9uIHRvcCBvZiBvdXIgYWxyZWFkeSBwcmVzZW50IGxheWVycw0KDQpgYGB7ciBzbW9vdGhlcn0NCmRmICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC4xKSArIA0KICBnZW9tX3Ntb290aChtZXRob2QgPSAnbG9lc3MnLCBzZSA9IEYpDQpgYGANClRoZSBzbW9vdGhlciBmaXRzIHByZXR0eSB3ZWxsIHdoZXJlIHRoZXJlIGFyZSBhIGxvdCBvZiBkYXRhIHBvaW50cywgYnV0IHRoZSBlbmRpbmcgcGllY2UgaXMgYSBiaXQgbm9uc2Vuc2ljYWwgYmVjYXVzZSBvZiB0aGUgbGFjayBvZiBkYXRhIHBvaW50cy4gV2UgbWlnaHQgd2FudCB0byBjb25zaWRlciByZWR1Y2luZyBvdXIgeCBsaW1pdCBzbyB0aGF0IHdlIGFyZSBmb2N1c2luZyBvbiBhcmVhcyB3aXRoIGEgbG90IG9mIGRhdGENCg0KDQp3ZSBjYW4gYWxzbyBzcGxpdCBvdXIgZGF0YSB1cCBieSBmYWNldGluZywgd2hpY2ggaXMgc3BsaXR0aW5nIHVwIG91ciBwbG90cyBieSBjYXRlZ29yeQ0KYGBge3IgZmFjZXRpbmcgc21vb3RoZXJzfQ0KcF9zbW9vdGggKyBmYWNldF93cmFwKH5jdXQpDQpgYGANCg0KDQoNCmhlcmUgd2Ugc3BsaXQgb3VyIHBsb3QgdXAgYnkgZGlmZmVyZW50IGN1dHMuIEkndmUgYWxzbyBzaG93biB0aGUgbGF5ZXJlZCBuYXR1cmUgb2YgZ2dwbG90LCB3aGVyZSBncmFwaGluZyBlbGVtZW50cyBhcmUgYWRkZWQgb25lIG9uIHRvcCBvZiB0aGUgb3RoZXIuIElmIHdlIHdlcmUgdG8gd3JpdGUgdGhhdCB3aG9sZSBwbG90IG91dCB1c2luZyBjb2RlIChhbmQgYWRkaW5nIGxhYmVscykgaXQgd291bGQgbG9vayBsaWtlIHRoaXM6DQoNCmBgYHtyfQ0KZGYgJT4lDQogIGdncGxvdChhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjEpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xvZXNzJywgc2UgPSBGKSArDQogIGZhY2V0X3dyYXAofmN1dCkgKw0KICBsYWJzKHRpdGxlID0gIlByaWNlIHZzLiBDYXJhdCwgRmFjZXRlZCBieSBEaWFtb25kIEN1dCIsDQogICAgICAgeCA9ICdDYXJhdCcsIHkgPSAnUHJpY2UnLCBzdWJ0aXRsZSA9ICJnZ3Bsb3QgdHV0b3JpYWwgZm9yIEVEQSIsDQogICAgICAgY2FwdGlvbiA9ICJCeSBLZWVzIFNjaGlwcGVyIikNCmBgYA0KDQoNCiMgTm93IGxldCdzIGRvIHRoZSBzYW1lIGxheWVyZWQgcHJvY2VzcyBmb3IgYSBib3hwbG90IC0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgc3RhcnQgYnkgc2ltcGx5IGxvb2tpbmcgYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBwcmljZSBpbiBvdXIgZW50aXJlIGRhdGFzZXQNCmBgYHtyfQ0KZGYgJT4lDQogIGdncGxvdChhZXMoeSA9IHByaWNlKSkgKw0KICBnZW9tX2JveHBsb3QoKQ0KYGBgDQoNCiMjIyBjbGVhcmx5IG5vdCB2ZXJ5IGluZm9ybWF0aXZlLiBMZXQncyBzcGxpdCBwcmljZSBpbnRvIGN1dA0KYGBge3J9DQpkZiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gY3V0LCB5ID0gcHJpY2UpKSArDQogIGdlb21fYm94cGxvdCgpDQpgYGANCg0KDQoNCnRoaXMgZ2l2ZXMgdXMgYSBsaXR0bGUgbW9yZSBpbmZvcm1hdGlvbiwgYnV0IHdlIGNhbiBpbXByb3ZlIG9uIG91ciBib3hwbG90IGFlc3RoZXRpY3Mgd2l0aCBub3RjaGVzIGFuZCBvdXRsaWVyIGNvbG9ycw0KYGBge3J9DQpkZiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gY3V0LCB5ID0gcHJpY2UpKSArDQogIGdlb21fYm94cGxvdChub3RjaCA9IFRSVUUsIG91dGxpZXIuY29sb3IgPSAnYmx1ZScsIGZpbGwgPSAnZ3JleTQwJykNCmBgYA0KDQp3ZSBjYW4gaW5jcmVhc2UgdGhlIGRpbWVuc2lvbnMgYWNjcm9zcyB3aGljaCB3ZSB2aXN1YWxpemUgb3VyIGRhdGEgYnkgYWRkaW5nIGZhY2V0cyBhZ2Fpbi4gTGV0J3MgbG9vayBhdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gY3V0IGFuZCBwcmljZSwgc3BsaXQgYnkgY2xhcml0eQ0KYGBge3J9DQpkZiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gY3V0LCB5ID0gcHJpY2UpKSArDQogIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG9yID0gJ2JsdWUnKSArDQogIGZhY2V0X3dyYXAofmNsYXJpdHkpDQpgYGANCg0KDQoNCmhlcmUgb3VyIG5vdGNoZXMgYXJlbid0IGVzcGVjaWFsbHkgdXNlZnVsLCBhbmQgd2UgYWxzbyBzZWUgdGhhdCBsYWJlbHMgdGVuZCB0byBvdmVybGFwIHdlIGNvdWxkIHRyeSBmbGlwcGluZyBvdXIgYXhlcywgd2hpY2ggdXN1YWxseSBnaXZlcyBtb3JlIHJvb20gZm9yIGVhY2ggeCBheGlzIGxhYmVsDQpgYGB7cn0NCmRmICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBjdXQsIHkgPSBwcmljZSkpICsNCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3IgPSAnYmx1ZScpICsNCiAgZmFjZXRfd3JhcCh+Y2xhcml0eSkgKw0KICBjb29yZF9mbGlwKCkNCmBgYA0KDQoNCnRoaXMgaXMgc3RpbGwgYSBsaXR0bGUgY29uZnVzaW5nIGJ1dCBhdCBsZWFzdCB0aGUgbGFiZWxzIGRvbid0IG92ZXJsYXAhIEZpbmFsbHksIHdlIGNhbiBhZGQgbGFiZWxzLiBBbHNvLCBsZXQncyBrZWVwIHRoZSBjb29yZGluYXRlcyBhcyB0aGV5IHdlcmUgb3JpZ2luYWxseSwgYnV0IHdlIGNhbiB1c2UgdGhlIGdncGxvdCBgdGhlbWVgIHRvIHRpbHQgb3VyIHggbGFiZWxzDQpgYGB7cn0NCmRmICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBjdXQsIHkgPSBwcmljZSkpICsNCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3IgPSAnYmx1ZScpICsNCiAgZmFjZXRfd3JhcCh+Y2xhcml0eSkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEsIGFuZ2xlID0gNDUpKSArDQogIGxhYnModGl0bGUgPSAiUHJpY2Ugb2YgRGlmZmVyZW50IERpYW1vbmQgQ3V0cywgU3RyYXRpZmllZCBieSBDbGFyaXR5IikNCmBgYA0KDQoNCg0KSSBhbG1vc3QgZm9yZ290ISBXZSBjYW4gYWxzbyBhZGQgc3VtbWFyeSBzdGF0aXN0aWNzIHRvIG91ciBib3hwbG90IHdpdGggc3RhdF9zdW1tYXJ5KCkgd2UgY2FuIGFsc28gbG9vayBhdCBhZGRpbmcgaml0dGVyIHNvIHRoYXQgd2UgY2FuIHNlZSBpbmRpdmlkdWFsIGRhdGEgcG9pbnRzIGluIG91ciBwbG90cywgYW5kIGdldCBhIGJldHRlciB2aXN1YWwgb2YgdGhlIGRpc3RyaWJ1dGlvbiBvZiBwb2ludHMNCmBgYHtyIGJveHBsb3RXaml0dGVyLCB3YXJuaW5nID0gRkFMU0V9DQpkZiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gY3V0LCB5ID0gcHJpY2UpKSArDQogIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG9yID0gJ2JsdWUnKSArDQogIHN0YXRfc3VtbWFyeShmdW4gPSAnbWVhbicsIGNvbCA9ICdyZWQnLCBzaXplID0gMC4yKSArDQogIGdlb21faml0dGVyKGNvbG9yID0gJ2Jyb3duJywgYWxwaGEgPSAwLjAyKSArDQogIGZhY2V0X3dyYXAofmNsYXJpdHkpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoaGp1c3QgPSAxLCBhbmdsZSA9IDQ1KSkgKw0KICBsYWJzKHRpdGxlID0gIlByaWNlIG9mIERpZmZlcmVudCBEaWFtb25kIEN1dHMsIFN0cmF0aWZpZWQgYnkgQ2xhcml0eSIpDQpgYGANCg0KDQoNCiMgTm93IHdlIGNhbiBsb29rIGF0IGhpc3RvZ3JhbXMgYW5kIGRlbnNpdHkgcGxvdHMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoNCmBgYHtyfQ0KZGYgJT4lDQogIGdncGxvdChhZXMoeCA9IHByaWNlKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjAwKSAjIHNwZWNpZnkgbnVtYmVyIG9mIGJpbnMgdG8gaW5jcmVhc2UgZGV0YWlsIGluIGRpc3RyaWJ1dGlvbg0KYGBgDQpMaWtlIHdpdGggb3RoZXIgcGxvdHMsIHdlIGNhbiBjb2xvciwgZmFjZXQsIGdyb3VwLCBldGMuLi4gYnV0IG5vdyB3ZSBjYW4gYWxzbyBzdGFjayBkaWZmZXJlbnQgY2F0ZWdvcmllcyB1c2luZyBhIGBwb3NpdGlvbmAgYXJndW1lbnQNCmBgYHtyfQ0KZGYgJT4lDQogIGdncGxvdChhZXMoeCA9IHByaWNlLCBmaWxsID0gY3V0KSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjAwLCBwb3NpdGlvbiA9ICdzdGFjaycpDQpgYGANCm90aGVyIHBvc2l0aW9uIGFyZ3VtZW50cyBhcmUgYGRvZGdlYCBhbmQgYGZpbGxgLiBJJ2xsIGxlYXZlIGl0IHRvIHRoZSByZWFkZXIgdG8gdGVzdCB0aGVzZSBvdXQgYW5kIHNlZSB3aGF0IHRoZXNlIGRvLiBgZG9kZ2VgIGlzIGJldHRlciB1c2VkIHdpdGggYmFyIHBsb3RzIChub3QgaGlzdG9ncmFtcykgYW5kIGZpbGwgaXMgZ29vZCBmb3IgZXhhbWluaW5nIHByb3BvcnRpb25zLCBhcyB3ZSBzaGFsbCBzb29uIHNlZQ0KDQoNCg0KVGhlIGFib3ZlIHZpc3VhbCBpc24ndCBzdXBlciB1c2VmdWwgaWYgd2UncmUgdHJ5aW5nIHRvIHNlZSB0aGUgcHJvcG9ydGlvbnMgb2YgZWFjaCBjdXQgdGhhdCBtYWtlIHVwIHRoZSBkaXN0cmlidXRpb24sIGxldCdzIHRyeSBjaGFuZ2luZyB0aGUgYGZpbGxgIGFyZ3VtZW50DQpgYGB7cn0NCmRmICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBwcmljZSwgZmlsbCA9IGN1dCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDIwMCwgcG9zaXRpb24gPSAnZmlsbCcpDQpgYGANCg0Kbm93IHdlIGNhbiBzZWUgdGhlIHJlbGF0aXZlIHByb3BvcnRpb25zIG9mIHdoYXQgZGlhbW9uZHMgbWFrZSB1cCB3aGljaCBwcmljZQ0KaG93ZXZlciwgdGhlIGppdHRlcmluZXNzIG9mIHRoaXMgcGxvdCBzdGlsbCBpc24ndCBmYW50YXN0aWMuIFRoaXMgaXMgd2hlcmUNCmRlbnNpdHkgcGxvdHMgY29tZSBpbg0KYGBge3J9DQpkZiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gcHJpY2UsIGZpbGwgPSBjdXQpKSArDQogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMjUsIGNvbG9yID0gImJsYWNrIikNCmBgYA0KDQoNCg0KSGVyZSB3ZSBjYW4gdmlzdWFsaXplIHRoZSBzbW9vdGhlZCBzaGFwZSBvZiBtdWx0aXBsZSBkaXN0cmlidXRpb25zLiBIb3dldmVyLCBpdCdzIGhhcmQgdG8gc2VlIHRoZSBkaWZmZXJlbnQgZGlzdHJpYnV0aW9ucyB3aGVuIHRoZXkncmUgb25lIG9uIHRvcCBvZiB0aGUgb3RoZXIuIExldCdzIHRyeSBzdGFja2luZyBpbnN0ZWFkDQpgYGB7cn0NCmRmICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBwcmljZSwgZmlsbCA9IGN1dCkpICsNCiAgZ2VvbV9kZW5zaXR5KHBvc2l0aW9uID0gJ3N0YWNrJykNCmBgYA0KDQpzbGlnaHRseSBiZXR0ZXIsIGJ1dCB3ZSBzdGlsbCBydW4gaW50byB0aGUgc2FtZSBwcm9ibGVtIHdoZXJlIHdlIGdldCBhIGdvb2QgcGljdHVyZSBvZiB0aGUgZW50aXJlIGRpc3RyaWJ1dGlvbiwgYnV0IHByb3BvcnRpb25zIG9mIGN1dCBmb3IgZWFjaCBwcmljZSBhcmUgc3RpbGwgZGlmZmljdWx0IHRvIGRpc3Rpbmd1aXNoLiBXZSBjYW4gbm93IHNlZSB3aGF0IGBmaWxsYCBkb2VzLg0KYGBge3J9DQpkZiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gcHJpY2UsIGZpbGwgPSBjdXQpKSArDQogIGdlb21fZGVuc2l0eShwb3NpdGlvbiA9ICdmaWxsJywgY29sb3IgPSAiYmxhY2siKQ0KYGBgDQoNCg0KDQpJbnRlcmVzdGluZ2x5LCBhbGwgZGlhbW9uZCBjdXRzIGV4Y2VwdCBmb3IgZmFpciBoYXZlIGEgZGlwIGluIHRoZSBwcmljZSByYW5nZSBvZiBhcm91bmQgNDAwMC01MDAwLiBNYXliZSB0aGlzIGlzIGJlY2F1c2UgamV3bGVycyB0ZW5kIHRvIHN0YXJ0IGNoYXJnaW5nIGF0IGFyb3VuZCA1MDAwLCBhbmQgZG9uJ3QgYm90aGVyIHdpdGggbWlkLXJhbmdlIHByaWNlcy4NCg0KQSBnZW9tIGhhbGZ3YXkgYmV0d2VlbiBoaXN0b2dyYW1zIGFuZCB0aGUgZGVuc2l0IHBsb3QgaXMgdGhlICdnZW9tX2ZyZXFwb2x5JyB3aGljaCBtYWtlcyBhIGZyZXF1ZW5jeSBwb2x5Z29uIHdpdGggamFnZ2VkIGVkZ2VzIG1hdGNoaW5nIGEgZ2l2ZW4gbnVtYmVyIG9mIGJpbnMuIEhvd2V2ZXIsIEkgZG9uJ3QgYmVsaWV2ZSB5b3UgY2FuIGdpdmUgZnJlcXBvbHkgYSBmaWxsDQpgYGB7cn0NCmRmICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBwcmljZSwgY29sb3IgPSBjdXQpKSArDQogIGdlb21fZnJlcXBvbHkoYmlucyA9IDUwLCBzaXplID0gMSkgKw0KICBzY2FsZV9jb2xvcl9odWUoKQ0KYGBgDQoNCiMgc29tZSBvdGhlciBnZW9tcyB0aGF0IG1heSBiZSB3b3J0aCB0cnlpbmcgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KYGBge3J9DQojIGdlb20gaGV4DQpkZiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSkpICsNCiAgZ2VvbV9oZXgoKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkNCg0KIyBnZW9tX2RlbnNpdHlfMmQNCmRmICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSkgKw0KICBnZW9tX2RlbnNpdHlfMmQoKQ0KDQojIGdlb21fZGVuc2l0eV8yZF9maWxsDQpkZiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSkpICsNCiAgZ2VvbV9kZW5zaXR5XzJkX2ZpbGxlZCgpICsNCiAgZ2VvbV9kZW5zaXR5XzJkKGNvbG9yID0gImJsYWNrIikgKw0KICB5bGltKDAsIDYwMDApICsNCiAgeGxpbSgwLCAxKQ0KDQojIHZpb2xpbiBwbG90cw0KZGYgJT4lDQogIGdncGxvdChhZXMoeCA9IGN1dCwgeSA9IHByaWNlLCBmaWxsID0gY3V0KSkgKw0KICBnZW9tX3Zpb2xpbigpICsNCiAgZmFjZXRfd3JhcCh+Y2xhcml0eSkNCmBgYA0KDQpUaGVzZSB0d28gZ2VvbXMgYXJlIHVzZWZ1bCBmb3Igc2V0dGluZyB2ZXJ0aWNhbCBhbmQgaG9yaXpvbnRhbCByZWZlcmVuY2UgbGluZXMgaW4gdGltZSBzZXJpZXMsIHdoZW4gY2hlY2tpbmcgbW9kZWwgcmVzaWR1YWxzLi4uDQpgP2dlb21faGxpbmVgDQpgP2dlb21fdmxpbmVgDQoNCmZvciBhbGwgb2YgdGhlIGdlb21zLCB5b3UgY2FuIHR5cGUgZ2VvbV88VEFCPiBhbmQgc2Nyb2xsIHRocm91Z2ggdGhlIGF1dG9jb21wbGV0ZSB0byBzZWUgd2hhdCBpcyBhdmFpbGFibGUuIFRoZXJlIGFyZSBhbHNvIGEgYnVuY2ggb2Ygb3RoZXIgZ2dwbG90LWJhc2VkIHBhY2thZ2VzIHRoYXQgaGF2ZSBzb21lIGNvb2wgcHJlbWFkZSBncmFwaHMgZm9yIGluZGl2aWR1YWxzIHRvIHRha2UgYWR2YW50YWdlIG9mLiBgZ2dwYWlyc2AgY3JlYXRlcyBhIHBhbmVsIHBsb3QgbWF0cml4IHRvIGNvbXBhcmUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHZhcmlhYmxlcyB3aXRoaW4gYSBkYXRhc2V0LCBjYWxjdWxhdGluZyBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgYW5kIGhpc3RvZ3JhbXMgYWxvbmcgdGhlIGRpYWdvbmFsLiBgc2pQbG90YCBoYXMgYSBsb3Qgb2YgcmVhbGx5IGNvb2wgcGxvdHMgZm9yIGNvbXBhcmluZyBtb2RlbCBwYXJhbWV0ZXJzICh3aGljaCBJJ3ZlIHVzZWQgZXh0ZW5zaXZlbHkpLiBUaGVyZSdzIGFsc28gYGdncmlkZ2VzYCwgd2hpY2ggY3JlYXRlcyBhIHJpZGdlbGluZSBwbG90IHRvIGNvbXBhcmUgbXVsdGlwbGUgZGlzdHJpYnV0aW9ucywgdGltZSBzZXJpZXMgY3VydmVzLCBvciBvdGhlciBjdXJ2ZXMgYWxvbmcgdGhlIHNhbWUgeCBheGlzLg0KDQpUaGUgYmVsb3cgZXhhbXBsZSB3YXMgdGFrZW4gZnJvbSB0aGUgZm9sbG93aW5nIHdlYmxpbms6IGh0dHBzOi8vd3d3LnItZ3JhcGgtZ2FsbGVyeS5jb20vMjk0LWJhc2ljLXJpZGdlbGluZS1wbG90Lmh0bWwNCmBgYHtyfQ0KIyBsaWJyYXJ5DQpsaWJyYXJ5KGdncmlkZ2VzKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KIA0KIyBEaWFtb25kcyBkYXRhc2V0IGlzIHByb3ZpZGVkIGJ5IFIgbmF0aXZlbHkNCiNoZWFkKGRpYW1vbmRzKQ0KIA0KIyBiYXNpYyBleGFtcGxlDQpnZ3Bsb3QoZGlhbW9uZHMsIGFlcyh4ID0gcHJpY2UsIHkgPSBjdXQsIGZpbGwgPSBjdXQpKSArDQogIGdlb21fZGVuc2l0eV9yaWRnZXMoKSArDQogIHRoZW1lX3JpZGdlcygpICsgDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KIyBIZXJlJ3Mgc29tZSBjb2RlIGZvciBtYWtpbmcgdmlzdWFsaXphdGlvbnMgd2l0aCBDT1ZJRCBkYXRhDQpJIGRpZG4ndCBnZXQgdG8gYW5ub3RhdGluZywgc28gZmVlbCBmcmVlIHRvIHRha2UgYSBsb29rIGFuZCBzZWUgd2hhdCB5b3UgbGlrZSEgSSdtIHByb2JhYmx5IG5vdCBnb2luZyB0byBnbyB0aHJvdWdoIHRoaXMgc2VjdGlvbiBpbiB0aGUgd29ya3Nob3AsIGFzIGl0IGludm9sdmVzIGEgbG90IG9mIGRhdGEgY2xlYW5pbmcsIHdoaWNoIGlzbid0IHRoZSBwb2ludCBvZiBpbnRyb2R1Y3Rpb24gdG8gZ2dwbG90LiBUYWtlIGEgbG9vayBhdCB0aGUgcGl2b3Qgc2VjdGlvbnMsIGFzIGhhdmluZyB5b3VyIGRhdGEgaW4gbG9uZyBmb3JtYXQgaXMga2V5IHRvIGhhdmluZyB2ZXJzYXRpbGUgZGF0YSBmb3IgZ2dwbG90Lg0KYGBge3Igd29ya2luZ1dDT1ZJRERhdGEsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCiMgcmVhZCBpbiBkYXRhIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBsb2FkIGluIENPVklEIGRhdGEgZm9yIGFsbCBjb3VudGllcyBhY3Jvc3MgdGhlIHVuaXRlZCBzdGF0ZXMNCmxvYWQoJy4uL0RhdGEgRGF5cyBvdXRwdXQvQ09WSURfbWFzdGVyXzIwMjAwMjE4LlJEYXRhJykNCg0KIyBzZWxlY3QgYSBjb3VudHkgb3Igc3RhdGUgdGhhdCB5b3UncmUgaW50ZXJlc3RlZCBpbjoNCk1BQ292aWQgPC0gQ09WSURfbWFzdGVyICU+JQ0KICAgICAgICAgICAgZmlsdGVyKHN0YXRlID09ICJNYXNzYWNodXNldHRzIiAmIFBvcHVsYXRpb24gPiAwKSAlPiUNCiAgICAgICAgICAgIGdyb3VwX2J5KGRhdGUpICU+JQ0KICAgICAgICAgICAgc3VtbWFyaXNlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSwgfnN1bSgueCwgbmEucm0gPSBUKSkpICU+JQ0KICBzZWxlY3QoZGF0ZSwgY2FzZXMsIGRlYXRoczpkYWlseV9kZWF0aHNfMTAwaykNCiMgd2Ugbm93IGhhdmUgZGF0YSBmb3IgdGhlIGVudGlyZSBzdGF0ZSBvZiBNYXNzYWNodXNldHRzLiBJZiB5b3Ugd2FudCB0byB3b3JrIHdpdGgNCiMgYSBkaWZmZXJlbnQgc3RhdGUsIGNoYW5nZSBNYXNzYWNodXNldHRzIHRvIHdoYXRldmVyIHN0YXRlIHRoYXQgaW50ZXJlc3RzIHlvdSENCg0KDQojIHNpbXBsZSB2aXN1YWxpemF0aW9ucyBvZiB0aGUgZGF0YSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgbGV0J3MgZXhhbWluZSBhIHNjYXR0ZXJwbG90IG9mIGNhc2VzIGluIE1hc3NhY2h1c2V0dHMgY29tcGFyZWQgdG8gZGVhdGhzDQoNCiMgdGhpcyBpcyBhYm91dCBhcyBzaW1wbGUgYSBnZ3Bsb3QgYXMgeW91IGNhbiBnZXQuIA0KZ2dwbG90KGRhdGEgPSBNQUNvdmlkLCBhZXMoeCA9IGRhaWx5X2Nhc2VzLCB5ID0gZGFpbHlfZGVhdGhzKSkgKw0KICBnZW9tX3BvaW50KCkNCg0KIyB5b3VyIGdncGxvdCBzcGVjaWZpZXMgZ2xvYmFsIG9wdGlvbnMsIHNvIHlvdSBkb24ndCBoYXZlIHRvIHNwZWNpZnkgYWVzdGhldGljcw0KIyBpbiB5b3VyIGFkZGVkIGdlb21ldHJpZXMuIFRoaXMgaGFzIGJlbmVmaXRzIGFuZCBkcmF3YmFja3MuLi4NCg0KIyBXZSBjYW4gYWxzbyBzdG9yZSB0aGlzIGFzIGEgZ2dwbG90IG9iamVjdCBhbmQgYWRkIHRvIGl0DQpwIDwtIGdncGxvdChkYXRhID0gTUFDb3ZpZCwgYWVzKHggPSBkYWlseV9jYXNlcywgeSA9IGRhaWx5X2RlYXRocykpDQojIHAgZm9yIHBsb3QNCnAgIyBnaXZlcyB1cyBhbiBlbXB0eSBwbG90IHdpdGggc2NhbGVzIGNvcnJlc3BvbmRpbmcgdG8gb3VyIGRhdGENCg0KDQojIGFkZGluZyB0byBhIGdncGxvdCBvYmplY3QgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBsZXQncyBhZGQgc29tZSBwb2ludHMsIGFuZCBhIGxpbmUgc2hvd2luZyB0aGUgcmVsYXRpb25zaGlwIG9mIG91ciBwb2ludHM6DQpwICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgoc2UgPSBULCBzcGFuID0gMC4yLCBtZXRob2QgPSAibG9lc3MiLCBjb2xvciA9ICdyZWQnKSArDQogIGxhYnModGl0bGUgPSAiQ09WSUQgRGVhdGhzIHZzLiBDYXNlcyIpDQoNCiMgZ2VvbV9zbW9vdGggZG9lcyBhIHNpbXBsZSBsb2VzcyBzbW9vdGhlciBieSBkZWZhdWx0LCBvdmVyIG91ciBkYXRhLiBOb3RlOg0KIyB5b3UgZG9uJ3QgYWN0dWFsbHkgbmVlZCB0byBwbG90IHlvdXIgcG9pbnRzIHRvIGdldCBhIHNtb290aGVyLCBSIGtub3dzIHdoYXQNCiMgZGF0YSB5b3UgYXJlIHVzaW5nLCBzbyB0aGUgc21vb3RoZXIgaXMgb25seSBkZXBlbmRlbnQgb24geW91ciBkYXRhLCBub3QgdGhlDQojIHBsb3RzIHRoYXQgeW91IGhhdmUgaW4gZ2dwbG90IGJlZm9yZWhhbmQNCg0KIyBtZXRob2RzIG9mIGdlb21fc21vb3RoIGluY2x1ZGUgbGluZWFyIG1vZGVscyAobG0pIGxvZXNzLCBhbmQgZ2VuZXJhbGl6ZWQgDQojIGFkZGl0dmUgbW9kZWxzIChnYW0pDQoNCg0KIyBib3hwbG90cyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIEknbSBpbnRlcmVzdGVkIGluIGRpZmZlcmVuY2VzIG9mIGNhc2UgYW5kIGRlYXRoIGNvdW50cyBieSB3ZWVrZGF5LiBMZXQncyBnZXQgYQ0KIyB2YXJpYWJsZSBmb3IgdGhhdA0KDQpNQVdlZWtkYXkgPC0gTUFDb3ZpZCAlPiUNCiAgbXV0YXRlKHdlZWtkYXkgPSBmYWN0b3Iod2Vla2RheXMoZGF0ZSkpKQ0KDQpzdW1tYXJ5KE1BV2Vla2RheSR3ZWVrZGF5KSAjIG5vdyBlYWNoIG9ic2VydmF0aW9uIGNvcnJlc3BvbmRzIHRvIGEgd2Vla2RheQ0KDQojIGxldCdzIHBsb3QgY2FzZSBjb3VudHMgYnkgd2Vla2RheQ0KTUFXZWVrZGF5ICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB3ZWVrZGF5LCB5ID0gZGFpbHlfY2FzZXMpKSArDQogIGdlb21fYm94cGxvdCgpICsNCiAgc3RhdF9zdW1tYXJ5KGZ1biA9ICdtZWFuJywgY29sb3IgPSAncmVkJywgZ2VvbSA9ICdwb2ludCcpDQojIG5vdyB3ZSBoYXZlIGJveHBsb3RzIG9mIGNhc2VzIGJ5IHdlZWtkYXkuIFdlIGNhbiBhbHNvIGFkZCBhIG1lYW4gdmFsdWUgaW5kaWNhdG9yDQojIHdpdGggc3RhdF9zdW1tYXJ5KCkNCg0KIyBJZiB3ZSBsaWtlIGNvbG9yLCB3ZSBjb3VsZCBjb2xvciB3ZWVrZGF5cyBkaWZmZXJlbnRseQ0KTUFXZWVrZGF5ICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB3ZWVrZGF5LCB5ID0gZGFpbHlfY2FzZXMsIGZpbGwgPSB3ZWVrZGF5KSkgKw0KICBnZW9tX2JveHBsb3Qob3V0bGllci5hbHBoYSA9IDApICsNCiAgZ2VvbV9qaXR0ZXIoY29sb3IgPSAiYmx1ZSIsIGFscGhhID0gMC4yKSArDQogIHN0YXRfc3VtbWFyeShmdW4gPSAnbWVhbicsIGNvbG9yID0gJ3JlZCcsIGdlb20gPSAncG9pbnQnKQ0KDQojIG5vdGUgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBzcGVjaWZ5aW5nIGNvbG9yIGluIHRoZSBhZXMoKSBmdW5jdGlvbiB2cyBpbiB0aGUgZ2VuZXJhbA0KIyBnZW1ldHJ5LiBhZXMoKSBtYXBzIGEgZGF0YSB2YWx1ZSB0byBhbiBpbmhlcmVudCBhc3BlY3Qgb2YgZ2dwbG90LiBUaGVyZWZvcmUsIGVhY2gNCiMgZGlmZmVyZW50IHZhbHVlIG9mIHdlZWtkYXkgcmV0dXJucyBhIGRpZmZlcmVudCBjb2xvci4gSWYgeW91IHNwZWNpZnkgY29sb3Igb3IgZmlsbA0KIyBvdXRzaWRlIG9mIGFlcywgdGhhdCBzaW5ndWxhciBjb2xvciBpcyBhcHBsaWVkIHRvIGFsbCBvZiB0aGUgZmlsbHMgYW5kL29yIGNvbG9ycyBmb3IgDQojIHRoYXQgZ2VvbWV0cnkuDQoNCg0KIyBzb21lIG90aGVyIGludGVyZXN0aW5nIGdlb21ldHJpZXMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIGxldCdzIGFjY3VtdWxhdGUgZGF0YSBieSB3ZWVrIGZpcnN0DQp3ZWVrIDwtIDENCmRheWNvdW50IDwtIDANCk1BV2Vla2RheSR3ZWVrIDwtIDANCmZvciAoaSBpbiAxOm5yb3coTUFXZWVrZGF5KSl7DQogIE1BV2Vla2RheSR3ZWVrW2ldIDwtIHdlZWsNCiAgZGF5Y291bnQgPC0gZGF5Y291bnQgKyAxDQogIA0KICBpZiAoZGF5Y291bnQgPT0gNyl7DQogICAgDQogICAgZGF5Y291bnQgPC0gMQ0KICAgIHdlZWsgPC0gd2VlayArIDENCiAgICANCiAgfQ0KfQ0KIyBjcmVhdGUgYSBjb3VudCBvZiB3ZWVrcw0KDQpNQVdlZWsgPC0gTUFXZWVrZGF5ICU+JQ0KICBzZWxlY3QoY2FzZXMsIGRlYXRocywgZGFpbHlfY2FzZXM6d2VlaykgJT4lDQogIGdyb3VwX2J5KHdlZWspICU+JQ0KICBzdW1tYXJpc2UoYWNyb3NzKGlzLm51bWVyaWMsIHN1bSkpDQoNCiMgbGluZSBncmFwaHMNCk1BV2VlayAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gd2VlaykpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gZGFpbHlfY2FzZXMpLCBzaXplID0gMSwgY29sID0gImJsdWUiKSArDQogIGdlb21fbGluZShhZXMoeSA9IGRhaWx5X2RlYXRocyksIHNpemUgPSAxLCBjb2wgPSAicmVkIikgKw0KICBsYWJzKHRpdGxlID0gIkNvbXBhcmluZyBDYXNlcyBhbmQgRGVhdGhzIGluIE1hc3NhY2h1c2V0dHMiKQ0KDQojIGdlb20gaGlzdG9ncmFtDQpNQUNvdmlkICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBkYWlseV9jYXNlcykpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDcwKQ0KDQojIGNhbiBldmVuIHN0YWNrIGhpc3RvZ3JhbXMgDQpNQUNvdmlkICU+JQ0KICBwaXZvdF9sb25nZXIoY29scyA9IGMoJ2RhaWx5X2Nhc2VzJywgJ2RhaWx5X2RlYXRocycpLA0KICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAnb3V0Y29tZScsDQogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAndmFsdWVzJykgJT4lDQogIGdncGxvdCgpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSB2YWx1ZXMsIGZpbGwgPSBvdXRjb21lKSwgcG9zaXRpb24gPSAic3RhY2siLCBiaW5zID0gMzAsIGNvbCA9ICJibHVlIikgDQoNCiMgbm90IHRoZSBncmVhdGVzdCB2aXN1YWxpemF0aW9uLiBMZXQncyB1c2UgYSBmcmVxdWVuY3kgcG9seWdvbiB0byBjb21wYXJlIGRpc3RyaWJ1dGlvbnMNCk1BQ292aWQgJT4lDQogIHBpdm90X2xvbmdlcihjb2xzID0gYygnZGFpbHlfY2FzZXMnLCAnZGFpbHlfZGVhdGhzJyksDQogICAgICAgICAgICAgICBuYW1lc190byA9ICdvdXRjb21lJywNCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICd2YWx1ZXMnKSAlPiUNCiAgZ2dwbG90KCkgKw0KICBnZW9tX2ZyZXFwb2x5KGFlcyh4ID0gdmFsdWVzLCBjb2xvciA9IG91dGNvbWUpLCBzaXplID0gMSwgYmlucyA9IDUwKSArDQogIGxhYnModGl0bGUgPSAiTm93IHdlIGNhbiBzZWUgYm90aCBkaXN0cmlidXRpb25zIikNCg0KDQojIGNhbiBldmVuIHVzZSBhIGRlbnNpdHkgcGxvdCBhcyBhbiBvdmVybGF5IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KTUFXZWVrZGF5ICU+JQ0KICBnZ3Bsb3QoKSArDQogIGdlb21fZGVuc2l0eShhZXMoeCA9IGRhaWx5X2Nhc2VzLCBncm91cCA9IHdlZWtkYXksIGNvbG9yID0gd2Vla2RheSksIHNpemUgPSAxKSArDQogIGxhYnModGl0bGUgPSAiRGFpbHkgY2FzZSBjb3VudCBkZW5zaXR5IGZ1bmN0aW9ucyBieSB3ZWVrZGF5LS1IYXJkIHRvIHNlZSIpDQoNCg0KIyBMZXQncyBmYWNldCEgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIGZhY2V0aW5nIGxldHMgdXMgc2VwYXJhdGUgb3VyIHBsb3RzIGJ5IGEgZmFjdG9yIG9yIGNoYXJhY3RlciB2YWx1ZQ0KTUFXZWVrZGF5ICU+JQ0KICBnZ3Bsb3QoKSArDQogIGdlb21fZGVuc2l0eShhZXMoeCA9IGRhaWx5X2Nhc2VzLCBncm91cCA9IHdlZWtkYXksIGNvbG9yID0gd2Vla2RheSksIHNpemUgPSAxKSArDQogIGZhY2V0X3dyYXAofndlZWtkYXkpICsNCiAgbGFicyh0aXRsZSA9ICJGYWNldGVkIGNhc2UgY291bnRzIGJ5IHdlZWtkYXktLW11Y2ggY2xlYXJlciEhIikNCg0KIyBsZXQncyBsb29rIGF0IG90aGVyIHZhcmlhYmxlcw0KTUFXZWVrZGF5ICU+JQ0KICBnZ3Bsb3QoKSArDQogIGdlb21fZGVuc2l0eShhZXMoeCA9IHBhcmtzLCBncm91cCA9IHdlZWtkYXksIGNvbG9yID0gd2Vla2RheSksIHNpemUgPSAxKSArDQogIGZhY2V0X3dyYXAofndlZWtkYXkpICsNCiAgbGFicyh0aXRsZSA9ICJGYWNldGVkIHBhcmsgbW9iaWxpdHkgYnkgd2Vla2RheSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSAjIGFmdGVyIGZhY2V0aW5nLCB3ZSBkb24ndCBuZWVkIGEgbGVnZW5kIGFueW1vcmUhDQoNCg0KIyBMZXQncyBsb29rIGF0IGEgaGVhdG1hcC9kZW5zaXR5IG1hcCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCk1BV2Vla2RheSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gZGFpbHlfY2FzZXMsIHkgPSBkYWlseV9kZWF0aHMpKSArDQogIGdlb21fZGVuc2l0eV8yZF9maWxsZWQoKSArDQogIGdlb21fZGVuc2l0eV8yZChjb2xvciA9ICdibGFjaycpICsNCiAgZ2VvbV9wb2ludChjb2xvciA9ICdibGFjaycsIGFscGhhID0gMC4zNSkgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlJlZHMiKSArDQogIHlsaW0oMCwgMTAwKSArDQogIHhsaW0oMCwgMzAwMCkgKw0KICBmYWNldF93cmFwKH53ZWVrZGF5KQ0KDQojIEZpbmFsIG5vdGUgb24gZmFjZXRpbmcgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgaWYgeW91IHdhbnQgdG8gZmFjZXQgeW91ciBkYXRhIGJ5IGEgY2F0ZWdvcnksIHlvdXIgZGF0YSBuZWVkcyB0byBiZSBpbiB0aGUNCiMgZm9sbG93aW5nIGZvcm1hdCAoYnV0IG5vdCBpbiB0aGlzIG9yZGVyKToNCiMgeCBjb2x1bW4gfCB5IGNvbHVtbiB8IGZhY2V0aW5nIGNvbHVtbg0KIyBiZWNhdXNlIG9mIHRoaXMsIG9mdGVuIHRpbWVzIHlvdSB3aWxsIGhhdmUgdG8gcGl2b3QgeW91ciBkYXRhIGZvciBmYWNldGluZy4NCiMgTGV0J3MgZG8gdGhpcyB0byBsb29rIGF0IHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gZGVhdGhzLCBjYXNlcywgYW5kIG1vYmlsaXR5DQojIG92ZXIgdGltZQ0KDQpmYWNldF9kYXRhIDwtIE1BV2Vla2RheSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKCdyZXRhaWxfcmVjJzonZGFpbHlfZGVhdGhzJyksDQogICAgICAgICAgICAgICBuYW1lc190byA9ICd2YXJzJywNCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICd2YWx1ZXMnKSAlPiUNCiAgc2VsZWN0KGRhdGUsIHZhcnMsIHZhbHVlcywgd2Vla2RheSkNCg0KZmFjZXRfZGF0YSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSwgeSA9IHZhbHVlcykpICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvciA9IHZhcnMpKQ0KDQojIHNlZS4uLm5vdCB0aGUgbW9zdCB2aXNpYmxlIGRhdGEuIE11Y2ggZWFzaWVyIHRvIGZhY2V0DQoNCmZhY2V0X2RhdGEgJT4lDQogIGdncGxvdChhZXMoeCA9IGRhdGUsIHkgPSB2YWx1ZXMpKSArDQogIGdlb21fbGluZShhZXMoY29sb3IgPSB2YXJzKSkgKw0KICBmYWNldF93cmFwKH52YXJzKQ0KDQojIHlvdSBjYW4gYWxzbyBmYWNldCBieSBtdWx0aXBsZSB2YXJpYWJsZXMuIFlvdSBqdXN0IG5lZWQgdG8gdXNlIGBmYWNldF9ncmlkYCBhbmQNCiMgc3BlY2lmeSB0aGUgY29sdW1uIGZhY2V0LCBhbmQgdGhlIHJvdyBmYWNldA0KZmFjZXRfZGF0YSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSwgeSA9IHZhbHVlcykpICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvciA9IHZhcnMpKSArDQogIGZhY2V0X2dyaWQoY29scyA9IHZhcnModmFycyksIHJvd3MgPSB2YXJzKHdlZWtkYXkpLCBzY2FsZXMgPSAnZnJlZV95JykgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KDQoNCg0KDQojIEZ1cnRoZXIgcmVzb3VyY2VzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBmb3IgbW9yZSBvbiB2aXN1YWxpemF0aW9ucyBpbiBSLCBJIGFsd2F5cyBwb2ludCBwZW9wbGUgdG8gdGhlIFIgZ3JhcGggZ2FsbGVyeToNCiMgaHR0cHM6Ly93d3cuci1ncmFwaC1nYWxsZXJ5LmNvbS8NCiMgaWYgeW91IGhhdmUgYW4gaWRlYSBmb3IgYSB2aXN1YWxpemF0aW9uLCB0aGVyZSBpcyBsaWtlbHkgc29tZSBjb2RlIGFscmVhZHkgaW4gdGhlDQojIGdhbGxlcnkgdGhhdCBjYW4gZ2V0IHlvdSBzdGFydGVkIG9uIG1ha2luZyB5b3VyIGZpbmFsIHZpc3VhbGl6YXRpb24uIElmIHlvdSdyZSBzdHVjaw0KIyBJIHJlY29tbWVuZCBjaGVja2luZyBpdCBvdXQuDQpgYGA=