Packages
These packages are used in this lesson:
library(ggplot2)
library(dplyr)
library(ggthemes) # install.packages("ggthemes")
library(hflights) # install.packages("hflights")
Base Graphics
R bills itself as a language and environment for statistical computing and graphics, and base R has some pretty decent capabilities. However, the ggplot2
package is ultimately more versatile and easier to use. If you should end up using ggplot2
anyway, then we will spend more time on this. There are some very useful base R plots, and I’ll cover those, but we will not spend much time customizing them. (I admit, I have actually forgotten most of what I did at one time know about base R graphics).
As I mentioned, when your data grows very large (think: 30 columns and millions of rows), taking a look at the data is not going to be very informative. This is why we want functions like mean()
or summary()
. Sometimes though, a picture is worth 1000 words and a simple plot can help us understand what we are looking at. Most useful among these are histograms, density plots, and scatterplots. For example a histogram or a density plot can tell us if the data is normally distributed, which is an assumption of many statistical tests.
hist(iris$Sepal.Length[iris$Species == "setosa"])
Naturally there are many options for hist()
but it is most useful for just letting the analyst visualize the data.
hist(iris$Sepal.Length[iris$Species == "setosa"], breaks = 10, col = "blue", xlab = "Sepal Length",
main = "Histogram for Setosa")
plot(density(iris$Petal.Length[iris$Species == "virginica"]))
As we saw, scatterplots can give us a quick visualization of relationships.
plot(MTcars$disp, MTcars$hp)
We can even produce a scatterplot matrix to look at multiple relationships.
We can even create a scatterplot matrix to see multiple relationships in the data.
pairs(~Sepal.Length + Sepal.Width + Petal.Length + Petal.Width,data=iris, main = "Simple Scatterplot Matrix")
We can see that there is a more clear relationship between petal length and width than there is between sepal length and width.
plot(iris$Sepal.Length, iris$Sepal.Width, col = iris$Species, pch = 2)
legend(6.8, 4.4, unique(iris$Species), col = 1:length(iris$Species), pch = 2)
Adding some colour helps to see what is going on here. In short, you can do a lot with ‘base’ graphics.
GGplot2
The ‘GG’ in ggplot2
stands for ‘grammar of graphics’. The general idea is that there should be rules for hierarchically organizing information into a data visualization. This is actually quite complex and mostly beyond the scope of what we want to do today. The essential idea is that in a simple plot there are x
and y
coordinates and then additional information layers are added on (like the mapping of colour onto iris$Species
, above). I will argue that this creates a simple, transparent, and powerful approach to creating visualizations. You can still end up with chunks of code like below to create a single plot, but the important thing is that it will (eventually - likely not today) be clear what the code is doing. And normally, you can get what you want with much less code.
theme_tufteNL <- function (base_size = 20, base_family = "serif", ticks = TRUE)
{
ret <- theme_bw(base_family = base_family, base_size = base_size) +
theme(legend.position="right", legend.background = element_blank(), legend.key = element_blank(),
panel.background = element_blank(), panel.border = element_blank(),
strip.background = element_blank(), plot.background = element_blank(),
axis.line = element_blank(), panel.grid = element_blank())
if (!ticks) {
ret <- ret + theme(axis.ticks = element_blank())
}
ret
}
AllSta <- ggplot(AllIA1GrandAvgS, aes(x = (IDX*20), y = mean, color = IA)) +
geom_line() +
geom_errorbar(aes(ymin = mean - se, ymax = mean + se), alpha = .4, width = 0.3) +
geom_vline(xintercept = vlineStcueMeans, alpha = 0.5) +
geom_vline(xintercept = vlineStcueSD, linetype = "longdash", alpha = 0.5) +
scale_x_continuous(breaks = seq(0, 6000, 1000)) + scale_y_continuous(breaks = seq(0, .8, .2)) +
labs(title = "Statements", y = NULL, x = "Time") +
facet_grid(ModGroup ~.) +
theme_tufteNL2()
Simple Plot (Scatter)
aa <- ggplot(MTcars, aes(x = hp, y = qsec)) + geom_scatter()
Error in geom_scatter() : could not find function "geom_scatter"
We have the data that we are going to plot. aes()
means ‘aesthetic’.
Now to our data layer, we can add a ‘geom’ that maps the numerical data onto points.
aa + geom_point()
bb <- aa + geom_point()
We called the plot in the first line, and then assigned the plot to bb
with the next. Let’s add a line that shows the relationship.
Constructing the plot this way is the same as writing it into one call like below.
ggplot(MTcars, aes(hp, qsec)) +
geom_point() +
geom_smooth()
We can also add a colour' layer into the
aes()` function.
ggplot(MTcars, aes(hp, qsec, colour = cyl)) +
geom_point() +
geom_smooth()
Is this the way we want our plot to look? How can we fix it? Think about what I said is often a source of problems. (We’ll get to labels later.)
MTcars$cyl <- as.factor(MTcars$cyl)
ggplot(MTcars, aes(hp, qsec, colour = cyl)) +
geom_point() +
geom_smooth()
This is not a great plot, but that has most to do with how hp
is in explaining qsec
times. Creating a new column that is a ratio of hp
in relation to wt
(weight) would be better. We have (at least) two ways of creating this column.
MTcars <- MTcars %>%
mutate(hp2wt = hp/wt)
Error in MTcars %>% mutate(hp2wt = hp/wt) : could not find function "%>%"
or
identical(MTcars$hp2wt, MTcars$hp2wtB)
[1] TRUE
As we can see, these did exactly the same thing. Let’s get rid of MTcars$hp2wtB
.
MTcars$hp2wtB <- NULL
Now we will use the new column hp2wt
as the new x
variable.
ggplot(MTcars, aes(hp2wt, qsec, colour = cyl)) +
geom_point() +
geom_smooth()
Our confidence intervals are not very informative because of the overlap. We can fix this by setting the fill
(not colour
) argument in the aes()
of geom_smooth()
.
ggplot(MTcars, aes(hp2wt, qsec, colour = cyl)) +
geom_point() +
geom_smooth(aes(fill = cyl))
Barplots
To make some barplots, we will first use the hflights
dataset from the package of the same name. This dataset shows all the flights that left Houston, Texas’s two airports in 2011. Let’s take a look at the dataset.
head(hflights)
glimpse(hflights)
summary(hflights)
Running the line…
sort(table(hflights$Dest))
… we can see that the most frequent destination for these flights was a place called ‘DAL’, which means Dallas, Texas, but these were not the only flights to Dallas, as ‘DFW’ means Dallas/Fort Worth International Airport. If we wanted to know which carriers operated the most flights to the Dallas area, what would we do?
# Let's use 'filter()' and the pipe operator.
What does the plot below show us? How would we recreate it?
DALhfli <- hflights %>%
...
DALhfli <- within(DALhfli, UniqueCarrier <- factor(UniqueCarrier, levels = names(sort(table(UniqueCarrier), decreasing = TRUE)))) # Let's not worry about what this does at the moment.
ggplot(DALhfli, ...
Obviously, we want to make visualizations of our data so that we can communicate relevant insights with our audiences. However, making visualizations can tell us a lot about the data that we work with. Previously, I showed you a barplot of the LeisureAtt
data that looked like this.
The plots are a similar format, but there is something drastically different about the format of the datasets. What is it? The code below should create (roughly) the plot above, but it does not. We will work around this in the next section.
ggplot(LeisureAttend, aes(REPORT_PERIOD, MONTHLY_ATTENDANCE)) + geom_bar()
Setting Labels, etc.
Let’s use the LeisureAttend
dataset to make a polished barplot.
This rough plot has multiple problems. First of all, there appears to be a lot of variation by both month, and year. Maybe it is good that the dataset reflects the timeseries nature of the data, but this layout makes it hard to make comparisons directly. It should be no surprise that there is always a big drop in attendance between August and September. Maybe it would be better to group the data by months rather than the sequentially arranged variable, REPORT_PERIOD
. We can do this like this.
Let’s re-arrange the bars for better comparison.
How did we change the ’LeisureAttdataset so that
MONTHwas arranged by time rather than alphabetically? Let's do that to
LeisureAttend`, as well.
LeisureAttend$MONTH <- factor(LeisureAttend$MONTH, levels = toupper(month.name))
# Now go back and recreate 'aa' and 'bb'
Yes, we will finally deal with the text on the x-axis now.
Better, but how readable is that y-axis?
How about the legend title?
And I would be lying to say I like the colours in this plot. Let’s try something else.
This produces the same result as below (which is how I would normally do it).
To see some other ways we could change our plot we can use the ggthemes
package in two ways. First, we can check out some of the themes. Have a look at the vignette by running: vignette("ggthemes")
.
Naturally, they change the theme of the plot, but even more valuable than that is running them without the ‘()’. Running them this way, we can see some additional options that we can alter.
theme_tufte
Exercise
Try changing one aspect of the plot ff
(this will be easier if you use the large code block above). Try doing a google search for adding a title, (add title ggplot2), changing the y-axis label (change y axis label ggplot2), or making the legen title bold… or whatever you want.
Bad Barplots
Read in the dataset and take a look.
dat <- read_csv("dat.csv")
datSum <- dat %>%
group_by(Key) %>%
summarise(mean = mean(Value), upper = mean(Value) - sd(Value), lower = mean(Value) + sd(Value))
datSum
Let’s make a plot from the summary data.
ggplot(data = datSum, aes(x = Key, y = mean)) + geom_bar(stat = "identity") + geom_errorbar(data = datSum, aes(x = Key, ymin = upper, ymax = lower), width = 0.2, size = 1, color = "blue")
But, does this plot tell the truth?
ggplot(dat, aes(Value, fill = Key)) + geom_density(alpha = .5)
This is what the data distributions actually look like, even though they have the same mean and same standard deviation.
And, in fact, there’s a statistically significant difference between the two distributions.
wilcox.test(dat$Value[dat$Key == "A"], dat$Value[dat$Key == "B"])
# t.test(dat$Value[dat$Key == "A"], dat$Value[dat$Key == "B"])
If you are ever tempted to make a bar plot with error bars, use a boxplot instead.
ggplot(dat, aes(Key, Value)) + geom_boxplot()
LS0tCnRpdGxlOiAnREMgMzogUGxvdHRpbmcgRGF0YScKYXV0aG9yOiAiQnJpYW4gUnVzayIKZGF0ZTogIkp1bHkgMTJ0aCAmIDEzdGgsIDIwMTciCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiBkZWZhdWx0CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAotLS0KCiMjIFBhY2thZ2VzCgpUaGVzZSBwYWNrYWdlcyBhcmUgdXNlZCBpbiB0aGlzIGxlc3NvbjoKYGBge3IsIGV2YWwgPSBGQUxTRX0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdndGhlbWVzKSAjIGluc3RhbGwucGFja2FnZXMoImdndGhlbWVzIikKbGlicmFyeShoZmxpZ2h0cykgIyBpbnN0YWxsLnBhY2thZ2VzKCJoZmxpZ2h0cyIpCmBgYAoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdndGhlbWVzKSAjIGluc3RhbGwucGFja2FnZXMoImdndGhlbWVzIikKbGlicmFyeShoZmxpZ2h0cykgIyBpbnN0YWxsLnBhY2thZ2VzKCJoZmxpZ2h0cyIpCmBgYAoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0KbGlicmFyeShyZWFkcikKTVRjYXJzIDwtIHJlYWRfY3N2KCJNVGNhcnMudHh0IiwgY29sX25hbWVzID0gRkFMU0UsIHNraXAgPSAxKQpNVGNhcnNOYW1lcyA8LSBhcy5jaGFyYWN0ZXIocmVhZF9jc3YoIk1UY2Fycy50eHQiLCBjb2xfbmFtZXMgPSBGQUxTRSwgbl9tYXggPSAxKSkKTVRjYXJzTmFtZXMgPC0gZ3N1YihwYXR0ZXJuID0gIlwiIiwgcmVwbGFjZW1lbnQgPSAiIiwgTVRjYXJzTmFtZXMpICMgTm90ZSB0aGUgb3JkZXIgb2YgdGhlIGFyZ3VtZW50cwpNVGNhcnNOYW1lcyA8LSBjKCJtb2RlbCIsIE1UY2Fyc05hbWVzKQpuYW1lcyhNVGNhcnMpIDwtIE1UY2Fyc05hbWVzCkxlaXN1cmVBdHRlbmQgPC0gcmVhZC5jc3YodXJsKCJodHRwczovL2Rhc2hib2FyZC5lZG1vbnRvbi5jYS9hcGkvdmlld3MvaWFhNy14OGtrL3Jvd3MuY3N2IikpCkxlaXN1cmVBdHRlbmQgPC0gZmlsdGVyKExlaXN1cmVBdHRlbmQsIE1PTlRITFlfQVRURU5EQU5DRSAhPSAwKQpgYGAKCiMjIEJhc2UgR3JhcGhpY3MKClIgYmlsbHMgaXRzZWxmIGFzIGEgbGFuZ3VhZ2UgYW5kIGVudmlyb25tZW50IGZvciBzdGF0aXN0aWNhbCBjb21wdXRpbmcgX2FuZF8gZ3JhcGhpY3MsIGFuZCBiYXNlIFIgaGFzIHNvbWUgcHJldHR5IGRlY2VudCBjYXBhYmlsaXRpZXMuIEhvd2V2ZXIsIHRoZSBgZ2dwbG90MmAgcGFja2FnZSBpcyB1bHRpbWF0ZWx5IG1vcmUgdmVyc2F0aWxlIGFuZCBlYXNpZXIgdG8gdXNlLiBJZiB5b3Ugc2hvdWxkIGVuZCB1cCB1c2luZyBgZ2dwbG90MmAgYW55d2F5LCB0aGVuIHdlIHdpbGwgc3BlbmQgbW9yZSB0aW1lIG9uIHRoaXMuIFRoZXJlIGFyZSBzb21lIHZlcnkgdXNlZnVsIGJhc2UgUiBwbG90cywgYW5kIEknbGwgY292ZXIgdGhvc2UsIGJ1dCB3ZSB3aWxsIG5vdCBzcGVuZCBtdWNoIHRpbWUgY3VzdG9taXppbmcgdGhlbS4gKEkgYWRtaXQsIEkgaGF2ZSBhY3R1YWxseSBmb3Jnb3R0ZW4gbW9zdCBvZiB3aGF0IEkgZGlkIGF0IG9uZSB0aW1lIGtub3cgYWJvdXQgYmFzZSBSIGdyYXBoaWNzKS4KCkFzIEkgbWVudGlvbmVkLCB3aGVuIHlvdXIgZGF0YSBncm93cyB2ZXJ5IGxhcmdlICh0aGluazogMzAgY29sdW1ucyBhbmQgbWlsbGlvbnMgb2Ygcm93cyksIHRha2luZyBhIGxvb2sgYXQgdGhlIGRhdGEgaXMgbm90IGdvaW5nIHRvIGJlIHZlcnkgaW5mb3JtYXRpdmUuIFRoaXMgaXMgd2h5IHdlIHdhbnQgZnVuY3Rpb25zIGxpa2UgYG1lYW4oKWAgb3IgYHN1bW1hcnkoKWAuIFNvbWV0aW1lcyB0aG91Z2gsIGEgcGljdHVyZSBpcyB3b3J0aCAxMDAwIHdvcmRzIGFuZCBhIHNpbXBsZSBwbG90IGNhbiBoZWxwIHVzIHVuZGVyc3RhbmQgd2hhdCB3ZSBhcmUgbG9va2luZyBhdC4gTW9zdCB1c2VmdWwgYW1vbmcgdGhlc2UgYXJlIGhpc3RvZ3JhbXMsIGRlbnNpdHkgcGxvdHMsIGFuZCBzY2F0dGVycGxvdHMuIEZvciBleGFtcGxlIGEgaGlzdG9ncmFtIG9yIGEgZGVuc2l0eSBwbG90IGNhbiB0ZWxsIHVzIGlmIHRoZSBkYXRhIGlzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLCB3aGljaCBpcyBhbiBhc3N1bXB0aW9uIG9mIG1hbnkgc3RhdGlzdGljYWwgdGVzdHMuCgpgYGB7cn0KaGlzdChpcmlzJFNlcGFsLkxlbmd0aFtpcmlzJFNwZWNpZXMgPT0gInNldG9zYSJdKQpgYGAKCk5hdHVyYWxseSB0aGVyZSBhcmUgbWFueSBvcHRpb25zIGZvciBgaGlzdCgpYCBidXQgaXQgaXMgbW9zdCB1c2VmdWwgZm9yIGp1c3QgbGV0dGluZyB0aGUgYW5hbHlzdCB2aXN1YWxpemUgdGhlIGRhdGEuIApgYGB7cn0KaGlzdChpcmlzJFNlcGFsLkxlbmd0aFtpcmlzJFNwZWNpZXMgPT0gInNldG9zYSJdLCBicmVha3MgPSAxMCwgY29sID0gImJsdWUiLCB4bGFiID0gIlNlcGFsIExlbmd0aCIsIAogIAltYWluID0gIkhpc3RvZ3JhbSBmb3IgU2V0b3NhIikKYGBgCgoKYGBge3J9CnBsb3QoZGVuc2l0eShpcmlzJFBldGFsLkxlbmd0aFtpcmlzJFNwZWNpZXMgPT0gInZpcmdpbmljYSJdKSkKYGBgCgpBcyB3ZSBzYXcsIHNjYXR0ZXJwbG90cyBjYW4gZ2l2ZSB1cyBhIHF1aWNrIHZpc3VhbGl6YXRpb24gb2YgcmVsYXRpb25zaGlwcy4KYGBge3J9CnBsb3QoTVRjYXJzJGRpc3AsIE1UY2FycyRocCkKYGBgCgpXZSBjYW4gZXZlbiBwcm9kdWNlIGEgc2NhdHRlcnBsb3QgbWF0cml4IHRvIGxvb2sgYXQgbXVsdGlwbGUgcmVsYXRpb25zaGlwcy4KCgpXZSBjYW4gZXZlbiBjcmVhdGUgYSBzY2F0dGVycGxvdCBtYXRyaXggdG8gc2VlIG11bHRpcGxlIHJlbGF0aW9uc2hpcHMgaW4gdGhlIGRhdGEuCmBgYHtyfQpwYWlycyh+U2VwYWwuTGVuZ3RoICsgU2VwYWwuV2lkdGggKyBQZXRhbC5MZW5ndGggKyBQZXRhbC5XaWR0aCxkYXRhPWlyaXMsIG1haW4gPSAiU2ltcGxlIFNjYXR0ZXJwbG90IE1hdHJpeCIpCmBgYAoKV2UgY2FuIHNlZSB0aGF0IHRoZXJlIGlzIGEgbW9yZSBjbGVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiBwZXRhbCBsZW5ndGggYW5kIHdpZHRoIHRoYW4gdGhlcmUgaXMgYmV0d2VlbiBzZXBhbCBsZW5ndGggYW5kIHdpZHRoLiAKCmBgYHtyfQpwbG90KGlyaXMkU2VwYWwuTGVuZ3RoLCBpcmlzJFNlcGFsLldpZHRoLCBjb2wgPSBpcmlzJFNwZWNpZXMsIHBjaCA9IDIpCmxlZ2VuZCg2LjgsIDQuNCwgdW5pcXVlKGlyaXMkU3BlY2llcyksIGNvbCA9IDE6bGVuZ3RoKGlyaXMkU3BlY2llcyksIHBjaCA9IDIpCmBgYAoKQWRkaW5nIHNvbWUgY29sb3VyIGhlbHBzIHRvIHNlZSB3aGF0IGlzIGdvaW5nIG9uIGhlcmUuIEluIHNob3J0LCB5b3UgY2FuIGRvIGEgbG90IHdpdGggJ2Jhc2UnIGdyYXBoaWNzLgoKCiMjIEdHcGxvdDIKClRoZSAnR0cnIGluIGBnZ3Bsb3QyYCBzdGFuZHMgZm9yICdncmFtbWFyIG9mIGdyYXBoaWNzJy4gVGhlIGdlbmVyYWwgaWRlYSBpcyB0aGF0IHRoZXJlIHNob3VsZCBiZSBydWxlcyBmb3IgaGllcmFyY2hpY2FsbHkgb3JnYW5pemluZyBpbmZvcm1hdGlvbiBpbnRvIGEgZGF0YSB2aXN1YWxpemF0aW9uLiBUaGlzIGlzIGFjdHVhbGx5IHF1aXRlIGNvbXBsZXggYW5kIG1vc3RseSBiZXlvbmQgdGhlIHNjb3BlIG9mIHdoYXQgd2Ugd2FudCB0byBkbyB0b2RheS4gVGhlIGVzc2VudGlhbCBpZGVhIGlzIHRoYXQgaW4gYSBzaW1wbGUgcGxvdCB0aGVyZSBhcmUgYHhgIGFuZCBgeWAgY29vcmRpbmF0ZXMgYW5kIHRoZW4gYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBsYXllcnMgYXJlIGFkZGVkIG9uIChsaWtlIHRoZSBtYXBwaW5nIG9mIGNvbG91ciBvbnRvIGBpcmlzJFNwZWNpZXNgLCBhYm92ZSkuIEkgd2lsbCBhcmd1ZSB0aGF0IHRoaXMgY3JlYXRlcyBhIHNpbXBsZSwgdHJhbnNwYXJlbnQsIGFuZCBwb3dlcmZ1bCBhcHByb2FjaCB0byBjcmVhdGluZyB2aXN1YWxpemF0aW9ucy4gWW91IGNhbiBzdGlsbCBlbmQgdXAgd2l0aCBjaHVua3Mgb2YgY29kZSBsaWtlIGJlbG93IHRvIGNyZWF0ZSBhIHNpbmdsZSBwbG90LCBidXQgdGhlIGltcG9ydGFudCB0aGluZyBpcyB0aGF0IGl0IHdpbGwgKGV2ZW50dWFsbHkgLSBsaWtlbHkgbm90IHRvZGF5KSBiZSBjbGVhciB3aGF0IHRoZSBjb2RlIGlzIGRvaW5nLiBBbmQgbm9ybWFsbHksIHlvdSBjYW4gZ2V0IHdoYXQgeW91IHdhbnQgd2l0aCBtdWNoIGxlc3MgY29kZS4KCmBgYHtyLCBldmFsID0gRkFMU0V9CnRoZW1lX3R1ZnRlTkwgPC0gZnVuY3Rpb24gKGJhc2Vfc2l6ZSA9IDIwLCBiYXNlX2ZhbWlseSA9ICJzZXJpZiIsIHRpY2tzID0gVFJVRSkgCnsKCXJldCA8LSB0aGVtZV9idyhiYXNlX2ZhbWlseSA9IGJhc2VfZmFtaWx5LCBiYXNlX3NpemUgPSBiYXNlX3NpemUpICsgCgkJdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJyaWdodCIsIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCBsZWdlbmQua2V5ID0gZWxlbWVudF9ibGFuaygpLAoJCQlwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksIAoJCQlzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksIAoJCQlheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCkpCglpZiAoIXRpY2tzKSB7CgkJcmV0IDwtIHJldCArIHRoZW1lKGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkpCgl9CglyZXQKfQoKCkFsbFN0YSA8LSBnZ3Bsb3QoQWxsSUExR3JhbmRBdmdTLCBhZXMoeCA9IChJRFgqMjApLCB5ID0gbWVhbiwgY29sb3IgPSBJQSkpICsgCglnZW9tX2xpbmUoKSArIAoJZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IG1lYW4gLSBzZSwgeW1heCA9IG1lYW4gKyBzZSksIGFscGhhID0gLjQsIHdpZHRoID0gMC4zKSArIAoJZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gdmxpbmVTdGN1ZU1lYW5zLCBhbHBoYSA9IDAuNSkgKyAKCWdlb21fdmxpbmUoeGludGVyY2VwdCA9IHZsaW5lU3RjdWVTRCwgbGluZXR5cGUgPSAibG9uZ2Rhc2giLCBhbHBoYSA9IDAuNSkgKyAKCXNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgNjAwMCwgMTAwMCkpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAuOCwgLjIpKSArIAoJbGFicyh0aXRsZSA9ICJTdGF0ZW1lbnRzIiwgeSA9IE5VTEwsIHggPSAiVGltZSIpICsgCglmYWNldF9ncmlkKE1vZEdyb3VwIH4uKSArIAoJdGhlbWVfdHVmdGVOTDIoKQpgYGAKCiFbVGhlIGNvZGUgYWJvdmUgY3JlYXRlcyB0aGlzIHBsb3QuXShQbG90RXhhbXBsZURDLnBuZykKCiMjIFNpbXBsZSBQbG90IChTY2F0dGVyKQoKYGBge3J9CiMgbGlicmFyeShnZ3Bsb3QyKQphYSA8LSBnZ3Bsb3QoTVRjYXJzLCBhZXMoeCA9IGhwLCB5ID0gcXNlYykpICMgVGhlIGRhdGEgd2Ugd2FudCB0byBwbG90LiAiZ2dwbG90KE1UY2FycywgYWVzKGhwLCBxc2VjKSkiIHdvdWxkIGFsc28gd29yay4KYGBgCldlIGhhdmUgdGhlIGRhdGEgdGhhdCB3ZSBhcmUgZ29pbmcgdG8gcGxvdC4gYGFlcygpYCBtZWFucyAnYWVzdGhldGljJy4KCk5vdyB0byBvdXIgZGF0YSBsYXllciwgd2UgY2FuIGFkZCBhICdnZW9tJyB0aGF0IG1hcHMgdGhlIG51bWVyaWNhbCBkYXRhIG9udG8gcG9pbnRzLgpgYGB7cn0KYWEgKyBnZW9tX3BvaW50KCkKYmIgPC0gYWEgKyBnZW9tX3BvaW50KCkKYGBgCldlIGNhbGxlZCB0aGUgcGxvdCBpbiB0aGUgZmlyc3QgbGluZSwgYW5kIHRoZW4gYXNzaWduZWQgdGhlIHBsb3QgdG8gYGJiYCB3aXRoIHRoZSBuZXh0LiBMZXQncyBhZGQgYSBsaW5lIHRoYXQgc2hvd3MgdGhlIHJlbGF0aW9uc2hpcC4KCmBgYHtyfQpiYiArIGdlb21fc21vb3RoKCkKYGBgCgpDb25zdHJ1Y3RpbmcgdGhlIHBsb3QgdGhpcyB3YXkgaXMgdGhlIHNhbWUgYXMgd3JpdGluZyBpdCBpbnRvIG9uZSBjYWxsIGxpa2UgYmVsb3cuCmBgYHtyLCBldmFsID0gRkFMU0V9CmdncGxvdChNVGNhcnMsIGFlcyhocCwgcXNlYykpICsKCWdlb21fcG9pbnQoKSArCglnZW9tX3Ntb290aCgpCmBgYAoKV2UgY2FuIGFsc28gYWRkIGEgYGNvbG91cicgbGF5ZXIgaW50byB0aGUgYGFlcygpYCBmdW5jdGlvbi4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0V9CmdncGxvdChNVGNhcnMsIGFlcyhocCwgcXNlYywgY29sb3VyID0gY3lsKSkgKwoJZ2VvbV9wb2ludCgpICsKCWdlb21fc21vb3RoKCkKYGBgCgpJcyB0aGlzIHRoZSB3YXkgd2Ugd2FudCBvdXIgcGxvdCB0byBsb29rPyBIb3cgY2FuIHdlIGZpeCBpdD8gVGhpbmsgYWJvdXQgd2hhdCBJIHNhaWQgaXMgb2Z0ZW4gYSBzb3VyY2Ugb2YgcHJvYmxlbXMuIChXZSdsbCBnZXQgdG8gbGFiZWxzIGxhdGVyLikKCmBgYHtyfQpNVGNhcnMkY3lsIDwtIGFzLmZhY3RvcihNVGNhcnMkY3lsKQpnZ3Bsb3QoTVRjYXJzLCBhZXMoaHAsIHFzZWMsIGNvbG91ciA9IGN5bCkpICsKCWdlb21fcG9pbnQoKSArCglnZW9tX3Ntb290aCgpCmBgYAoKVGhpcyBpcyBub3QgYSBncmVhdCBwbG90LCBidXQgdGhhdCBoYXMgbW9zdCB0byBkbyB3aXRoIGhvdyBgaHBgIGlzIGluIGV4cGxhaW5pbmcgYHFzZWNgIHRpbWVzLiBDcmVhdGluZyBhIG5ldyBjb2x1bW4gdGhhdCBpcyBhIHJhdGlvIG9mIGBocGAgaW4gcmVsYXRpb24gdG8gYHd0YCAod2VpZ2h0KSB3b3VsZCBiZSBiZXR0ZXIuIFdlIGhhdmUgKGF0IGxlYXN0KSB0d28gd2F5cyBvZiBjcmVhdGluZyB0aGlzIGNvbHVtbi4KCmBgYHtyfQpNVGNhcnMgPC0gTVRjYXJzICU+JSAjIFRoaXMgdXNlcyB0aGUgZHBseXIgcGFja2FnZQoJbXV0YXRlKGhwMnd0ID0gaHAvd3QpCmBgYApvcgpgYGB7cn0KTVRjYXJzJGhwMnd0QiA8LSBNVGNhcnMkaHAvTVRjYXJzJHd0CmlkZW50aWNhbChNVGNhcnMkaHAyd3QsIE1UY2FycyRocDJ3dEIpCmBgYApBcyB3ZSBjYW4gc2VlLCB0aGVzZSBkaWQgZXhhY3RseSB0aGUgc2FtZSB0aGluZy4gTGV0J3MgZ2V0IHJpZCBvZiBgTVRjYXJzJGhwMnd0QmAuCmBgYHtyfQpNVGNhcnMkaHAyd3RCIDwtIE5VTEwKYGBgCgpOb3cgd2Ugd2lsbCB1c2UgdGhlIG5ldyBjb2x1bW4gYGhwMnd0YCBhcyB0aGUgbmV3IGB4YCB2YXJpYWJsZS4KYGBge3J9CmdncGxvdChNVGNhcnMsIGFlcyhocDJ3dCwgcXNlYywgY29sb3VyID0gY3lsKSkgKwoJZ2VvbV9wb2ludCgpICsKCWdlb21fc21vb3RoKCkKYGBgCgpPdXIgY29uZmlkZW5jZSBpbnRlcnZhbHMgYXJlIG5vdCB2ZXJ5IGluZm9ybWF0aXZlIGJlY2F1c2Ugb2YgdGhlIG92ZXJsYXAuIFdlIGNhbiBmaXggdGhpcyBieSBzZXR0aW5nIHRoZSBgZmlsbGAgKG5vdCBgY29sb3VyYCkgYXJndW1lbnQgaW4gdGhlIGBhZXMoKWAgb2YgYGdlb21fc21vb3RoKClgLgoKYGBge3J9CmdncGxvdChNVGNhcnMsIGFlcyhocDJ3dCwgcXNlYywgY29sb3VyID0gY3lsKSkgKwoJZ2VvbV9wb2ludCgpICsKCWdlb21fc21vb3RoKGFlcyhmaWxsID0gY3lsKSkKYGBgCgojIyBCYXJwbG90cwoKVG8gbWFrZSBzb21lIGJhcnBsb3RzLCB3ZSB3aWxsIGZpcnN0IHVzZSB0aGUgYGhmbGlnaHRzYCBkYXRhc2V0IGZyb20gdGhlIHBhY2thZ2Ugb2YgdGhlIHNhbWUgbmFtZS4gVGhpcyBkYXRhc2V0IHNob3dzIGFsbCB0aGUgZmxpZ2h0cyB0aGF0IGxlZnQgSG91c3RvbiwgVGV4YXMncyB0d28gYWlycG9ydHMgaW4gMjAxMS4gTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIGRhdGFzZXQuCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpoZWFkKGhmbGlnaHRzKQpnbGltcHNlKGhmbGlnaHRzKQpzdW1tYXJ5KGhmbGlnaHRzKQpgYGAKClJ1bm5pbmcgdGhlIGxpbmUuLi4KCmBgYHtyfQpzb3J0KHRhYmxlKGhmbGlnaHRzJERlc3QpKQpgYGAKLi4uIHdlIGNhbiBzZWUgdGhhdCB0aGUgbW9zdCBmcmVxdWVudCBkZXN0aW5hdGlvbiBmb3IgdGhlc2UgZmxpZ2h0cyB3YXMgYSBwbGFjZSBjYWxsZWQgJ0RBTCcsIHdoaWNoIG1lYW5zIERhbGxhcywgVGV4YXMsIGJ1dCB0aGVzZSB3ZXJlIG5vdCB0aGUgb25seSBmbGlnaHRzIHRvIERhbGxhcywgYXMgJ0RGVycgbWVhbnMgRGFsbGFzL0ZvcnQgV29ydGggSW50ZXJuYXRpb25hbCBBaXJwb3J0LiBJZiB3ZSB3YW50ZWQgdG8ga25vdyB3aGljaCBjYXJyaWVycyBvcGVyYXRlZCB0aGUgbW9zdCBmbGlnaHRzIHRvIHRoZSBEYWxsYXMgYXJlYSwgd2hhdCB3b3VsZCB3ZSBkbz8KCmBgYHtyfQojIExldCdzIHVzZSAnZmlsdGVyKCknIGFuZCB0aGUgcGlwZSBvcGVyYXRvci4KYGBgCgoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0KREFMaGZsaSA8LSBoZmxpZ2h0cyAlPiUKCWZpbHRlcihEZXN0ICVpbiUgYygiREFMIiwgIkRGVyIpKSAKREFMaGZsaSA8LSB3aXRoaW4oREFMaGZsaSwgVW5pcXVlQ2FycmllciA8LSBmYWN0b3IoVW5pcXVlQ2FycmllciwgbGV2ZWxzID0gbmFtZXMoc29ydCh0YWJsZShVbmlxdWVDYXJyaWVyKSwgZGVjcmVhc2luZyA9IFRSVUUpKSkpCmdncGxvdChEQUxoZmxpLCBhZXMoVW5pcXVlQ2FycmllcikpICsgZ2VvbV9iYXIoKQphYSA8LSBnZ3Bsb3QoREFMaGZsaSwgYWVzKFVuaXF1ZUNhcnJpZXIsIGZpbGwgPSBEZXN0KSkgKyBnZW9tX2JhcigpCmBgYAoKV2hhdCBkb2VzIHRoZSBwbG90IGJlbG93IHNob3cgdXM/IEhvdyB3b3VsZCB3ZSByZWNyZWF0ZSBpdD8KYGBge3IsIGVjaG8gPSBGQUxTRX0KYWEKYGBgCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpEQUxoZmxpIDwtIGhmbGlnaHRzICU+JQoJLi4uCkRBTGhmbGkgPC0gd2l0aGluKERBTGhmbGksIFVuaXF1ZUNhcnJpZXIgPC0gZmFjdG9yKFVuaXF1ZUNhcnJpZXIsIGxldmVscyA9IG5hbWVzKHNvcnQodGFibGUoVW5pcXVlQ2FycmllciksIGRlY3JlYXNpbmcgPSBUUlVFKSkpKSAjIExldCdzIG5vdCB3b3JyeSBhYm91dCB3aGF0IHRoaXMgZG9lcyBhdCB0aGUgbW9tZW50LgpnZ3Bsb3QoREFMaGZsaSwgLi4uCmBgYAoKT2J2aW91c2x5LCB3ZSB3YW50IHRvIG1ha2UgdmlzdWFsaXphdGlvbnMgb2Ygb3VyIGRhdGEgc28gdGhhdCB3ZSBjYW4gY29tbXVuaWNhdGUgcmVsZXZhbnQgaW5zaWdodHMgd2l0aCBvdXIgYXVkaWVuY2VzLiBIb3dldmVyLCBtYWtpbmcgdmlzdWFsaXphdGlvbnMgY2FuIHRlbGwgdXMgYSBsb3QgYWJvdXQgdGhlIGRhdGEgdGhhdCB3ZSB3b3JrIHdpdGguIFByZXZpb3VzbHksIEkgc2hvd2VkIHlvdSBhIGJhcnBsb3Qgb2YgdGhlIGBMZWlzdXJlQXR0YCBkYXRhIHRoYXQgbG9va2VkIGxpa2UgdGhpcy4KCmBgYHtyLCBlY2hvID0gRkFMU0V9CmdncGxvdChMZWlzdXJlQXR0ZW5kLCBhZXMoeCA9IFJFUE9SVF9QRVJJT0QsIHkgPSBNT05USExZX0FUVEVOREFOQ0UpKSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIHRoZW1lKGF4aXMudGV4dC54ICA9IGVsZW1lbnRfdGV4dChhbmdsZT02MCwgaGp1c3QgPSAxLCB2anVzdD0xLCBzaXplPTgpKQpgYGAKClRoZSBwbG90cyBhcmUgYSBzaW1pbGFyIGZvcm1hdCwgYnV0IHRoZXJlIGlzIHNvbWV0aGluZyBkcmFzdGljYWxseSBkaWZmZXJlbnQgYWJvdXQgdGhlIGZvcm1hdCBvZiB0aGUgZGF0YXNldHMuIFdoYXQgaXMgaXQ/IFRoZSBjb2RlIGJlbG93IF9zaG91bGRfIGNyZWF0ZSAocm91Z2hseSkgdGhlIHBsb3QgYWJvdmUsIGJ1dCBpdCBkb2VzIG5vdC4gV2Ugd2lsbCB3b3JrIGFyb3VuZCB0aGlzIGluIHRoZSBuZXh0IHNlY3Rpb24uCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoTGVpc3VyZUF0dGVuZCwgYWVzKFJFUE9SVF9QRVJJT0QsIE1PTlRITFlfQVRURU5EQU5DRSkpICsgZ2VvbV9iYXIoKQpgYGAKCiMjIyMgU2V0dGluZyBMYWJlbHMsIGV0Yy4KTGV0J3MgdXNlIHRoZSBgTGVpc3VyZUF0dGVuZGAgZGF0YXNldCB0byBtYWtlIGEgcG9saXNoZWQgYmFycGxvdC4gCgpgYGB7cn0KYWEgPC0gZ2dwbG90KExlaXN1cmVBdHRlbmQsIGFlcyhSRVBPUlRfUEVSSU9ELCBNT05USExZX0FUVEVOREFOQ0UpKSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKQphYQpgYGAKClRoaXMgcm91Z2ggcGxvdCBoYXMgbXVsdGlwbGUgcHJvYmxlbXMuIEZpcnN0IG9mIGFsbCwgdGhlcmUgYXBwZWFycyB0byBiZSBhIGxvdCBvZiB2YXJpYXRpb24gYnkgYm90aCBtb250aCwgYW5kIHllYXIuIE1heWJlIGl0IGlzIGdvb2QgdGhhdCB0aGUgZGF0YXNldCByZWZsZWN0cyB0aGUgdGltZXNlcmllcyBuYXR1cmUgb2YgdGhlIGRhdGEsIGJ1dCB0aGlzIGxheW91dCBtYWtlcyBpdCBoYXJkIHRvIG1ha2UgY29tcGFyaXNvbnMgZGlyZWN0bHkuIEl0IHNob3VsZCBiZSBubyBzdXJwcmlzZSB0aGF0IHRoZXJlIGlzIGFsd2F5cyBhIGJpZyBkcm9wIGluIGF0dGVuZGFuY2UgYmV0d2VlbiBBdWd1c3QgYW5kIFNlcHRlbWJlci4gTWF5YmUgaXQgd291bGQgYmUgYmV0dGVyIHRvIGdyb3VwIHRoZSBkYXRhIGJ5IG1vbnRocyByYXRoZXIgdGhhbiB0aGUgc2VxdWVudGlhbGx5IGFycmFuZ2VkIHZhcmlhYmxlLCBgUkVQT1JUX1BFUklPRGAuIFdlIGNhbiBkbyB0aGlzIGxpa2UgdGhpcy4KCmBgYHtyfQphYSA8LSBnZ3Bsb3QoTGVpc3VyZUF0dGVuZCwgYWVzKHggPSBNT05USCwgeSA9IE1PTlRITFlfQVRURU5EQU5DRSwgZmlsbCA9IGFzLmZhY3RvcihZRUFSKSkpCmFhICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpCmBgYAoKTGV0J3MgcmUtYXJyYW5nZSB0aGUgYmFycyBmb3IgYmV0dGVyIGNvbXBhcmlzb24uCmBgYHtyfQpiYiA8LSBhYSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpCmJiCmBgYAoKSG93IGRpZCB3ZSBjaGFuZ2UgdGhlICdMZWlzdXJlQXR0YCBkYXRhc2V0IHNvIHRoYXQgYE1PTlRIYCB3YXMgYXJyYW5nZWQgYnkgdGltZSByYXRoZXIgdGhhbiBhbHBoYWJldGljYWxseT8gTGV0J3MgZG8gdGhhdCB0byBgTGVpc3VyZUF0dGVuZGAsIGFzIHdlbGwuCmBgYHtyfQpMZWlzdXJlQXR0ZW5kJE1PTlRIIDwtIGZhY3RvcihMZWlzdXJlQXR0ZW5kJE1PTlRILCBsZXZlbHMgPSB0b3VwcGVyKG1vbnRoLm5hbWUpKQojIE5vdyBnbyBiYWNrIGFuZCByZWNyZWF0ZSAnYWEnIGFuZCAnYmInCmBgYApZZXMsIHdlIHdpbGwgZmluYWxseSBkZWFsIHdpdGggdGhlIHRleHQgb24gdGhlIHgtYXhpcyBub3cuCgpgYGB7cn0KY2MgPC0gYmIgKyB0aGVtZShheGlzLnRleHQueCAgPSBlbGVtZW50X3RleHQoYW5nbGUgPSA2MCwgaGp1c3QgPSAxLCB2anVzdCA9IDEsIHNpemUgPSA4KSkKY2MKYGBgCgpCZXR0ZXIsIGJ1dCBob3cgcmVhZGFibGUgaXMgdGhhdCB5LWF4aXM/CgpgYGB7cn0KZGQgPC0gY2MgKyBzY2FsZV95X2NvbnRpbnVvdXMoIkFUVEVOREFOQ0UiLCBsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKQpkZApgYGAKCkhvdyBhYm91dCB0aGUgbGVnZW5kIHRpdGxlPwoKYGBge3J9CmVlIDwtIGRkICsgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiWWVhciIpKSAjIFRoZXJlIGFyZSBhdCBsZWFzdCBmaXZlIGRpZmZlcmVudCB3YXlzIHRvIGRvIHRoaXMuIFRoaXMgaXNuJ3QgbXkgZmF2b3VyaXRlLgplZQpgYGAKCkFuZCBJIHdvdWxkIGJlIGx5aW5nIHRvIHNheSBJIGxpa2UgdGhlIGNvbG91cnMgaW4gdGhpcyBwbG90LiBMZXQncyB0cnkgc29tZXRoaW5nIGVsc2UuCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFfQpmZiA8LSBlZSArIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiWWxHbkJ1IikKZmYKYGBgCgpUaGlzIHByb2R1Y2VzIHRoZSBzYW1lIHJlc3VsdCBhcyBiZWxvdyAod2hpY2ggaXMgaG93IEkgd291bGQgbm9ybWFsbHkgZG8gaXQpLgoKYGBge3J9CmdncGxvdChMZWlzdXJlQXR0ZW5kLCBhZXMoeCA9IE1PTlRILCB5ID0gTU9OVEhMWV9BVFRFTkRBTkNFLCBmaWxsID0gYXMuZmFjdG9yKFlFQVIpKSkgKyAKCWdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsgCgl0aGVtZShheGlzLnRleHQueCAgPSBlbGVtZW50X3RleHQoYW5nbGU9NjAsIGhqdXN0ID0gMSwgdmp1c3Q9MSwgc2l6ZT04KSkgKyAKCXNjYWxlX3lfY29udGludW91cygiQVRURU5EQU5DRSIsIGxhYmVscyA9IHNjYWxlczo6Y29tbWEpICsKCWd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIlllYXIiKSkgKwoJc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iWWxHbkJ1IikKYGBgCgpUbyBzZWUgc29tZSBvdGhlciB3YXlzIHdlIGNvdWxkIGNoYW5nZSBvdXIgcGxvdCB3ZSBjYW4gdXNlIHRoZSBgZ2d0aGVtZXNgIHBhY2thZ2UgaW4gdHdvIHdheXMuIEZpcnN0LCB3ZSBjYW4gY2hlY2sgb3V0IHNvbWUgb2YgdGhlIHRoZW1lcy4gSGF2ZSBhIGxvb2sgYXQgdGhlIHZpZ25ldHRlIGJ5IHJ1bm5pbmc6IGB2aWduZXR0ZSgiZ2d0aGVtZXMiKWAuCgpgYGB7cn0KZmYgKyB0aGVtZV90dWZ0ZSgpCmZmICsgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkKZmYgKyB0aGVtZV9jbGFzc2ljKCkgIyAqIGZyb20gZ2dwbG90MgpgYGAKCk5hdHVyYWxseSwgdGhleSBjaGFuZ2UgdGhlIHRoZW1lIG9mIHRoZSBwbG90LCBidXQgZXZlbiBtb3JlIHZhbHVhYmxlIHRoYW4gdGhhdCBpcyBydW5uaW5nIHRoZW0gd2l0aG91dCB0aGUgJygpJy4gUnVubmluZyB0aGVtIHRoaXMgd2F5LCB3ZSBjYW4gc2VlIHNvbWUgYWRkaXRpb25hbCBvcHRpb25zIHRoYXQgd2UgY2FuIGFsdGVyLiAKCmBgYHtyLCBldmFsID0gRkFMU0V9CnRoZW1lX3R1ZnRlCmBgYAoKIyMjIyBFeGVyY2lzZQoKVHJ5IGNoYW5naW5nIG9uZSBhc3BlY3Qgb2YgdGhlIHBsb3QgYGZmYCAodGhpcyB3aWxsIGJlIGVhc2llciBpZiB5b3UgdXNlIHRoZSBsYXJnZSBjb2RlIGJsb2NrIGFib3ZlKS4gVHJ5IGRvaW5nIGEgZ29vZ2xlIHNlYXJjaCBmb3IgYWRkaW5nIGEgdGl0bGUsIChhZGQgdGl0bGUgZ2dwbG90MiksIGNoYW5naW5nIHRoZSB5LWF4aXMgbGFiZWwgKGNoYW5nZSB5IGF4aXMgbGFiZWwgZ2dwbG90MiksIG9yIG1ha2luZyB0aGUgbGVnZW4gdGl0bGUgYm9sZC4uLiBvciB3aGF0ZXZlciB5b3Ugd2FudC4KCgojIyBCYWQgQmFycGxvdHMKClJlYWQgaW4gdGhlIGRhdGFzZXQgYW5kIHRha2UgYSBsb29rLgpgYGB7cn0KZGF0IDwtIHJlYWRfY3N2KCJkYXQuY3N2IikKZGF0U3VtIDwtIGRhdCAlPiUKCWdyb3VwX2J5KEtleSkgJT4lIAoJc3VtbWFyaXNlKG1lYW4gPSBtZWFuKFZhbHVlKSwgdXBwZXIgPSBtZWFuKFZhbHVlKSAtIHNkKFZhbHVlKSwgbG93ZXIgPSBtZWFuKFZhbHVlKSArIHNkKFZhbHVlKSkKZGF0U3VtCmBgYAoKTGV0J3MgbWFrZSBhIHBsb3QgZnJvbSB0aGUgc3VtbWFyeSBkYXRhLgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkYXRTdW0sIGFlcyh4ID0gS2V5LCB5ID0gbWVhbikpICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgZ2VvbV9lcnJvcmJhcihkYXRhID0gZGF0U3VtLCBhZXMoeCA9IEtleSwgeW1pbiA9IHVwcGVyLCB5bWF4ID0gbG93ZXIpLCB3aWR0aCA9IDAuMiwgc2l6ZSA9IDEsIGNvbG9yID0gImJsdWUiKSAKYGBgCgpCdXQsIGRvZXMgdGhpcyBwbG90IHRlbGwgdGhlIHRydXRoPwoKYGBge3J9CmdncGxvdChkYXQsIGFlcyhWYWx1ZSwgZmlsbCA9IEtleSkpICsgZ2VvbV9kZW5zaXR5KGFscGhhID0gLjUpCmBgYAoKVGhpcyBpcyB3aGF0IHRoZSBkYXRhIGRpc3RyaWJ1dGlvbnMgYWN0dWFsbHkgbG9vayBsaWtlLCBldmVuIHRob3VnaCB0aGV5IGhhdmUgdGhlIHNhbWUgbWVhbiBhbmQgc2FtZSBzdGFuZGFyZCBkZXZpYXRpb24uCgpBbmQsIGluIGZhY3QsIHRoZXJlJ3MgYSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgdHdvIGRpc3RyaWJ1dGlvbnMuCmBgYHtyfQp3aWxjb3gudGVzdChkYXQkVmFsdWVbZGF0JEtleSA9PSAiQSJdLCBkYXQkVmFsdWVbZGF0JEtleSA9PSAiQiJdKQojIHQudGVzdChkYXQkVmFsdWVbZGF0JEtleSA9PSAiQSJdLCBkYXQkVmFsdWVbZGF0JEtleSA9PSAiQiJdKQpgYGAKCklmIHlvdSBhcmUgZXZlciB0ZW1wdGVkIHRvIG1ha2UgYSBiYXIgcGxvdCB3aXRoIGVycm9yIGJhcnMsIHVzZSBhIGJveHBsb3QgaW5zdGVhZC4KYGBge3J9CmdncGxvdChkYXQsIGFlcyhLZXksIFZhbHVlKSkgKyBnZW9tX2JveHBsb3QoKQpgYGAKCgoK