Slides for this and other Data Science courses may be found at github https://github.com/DataScienceSpecialization/courses/. If you care to use them, they must be downloaded as a zip file and viewed locally. This lesson corresponds to 04_ExploratoryAnalysis/PlottingBase.
In another lesson, we gave you an overview of the three plotting systems in R. In this lesson we’ll focus on the base plotting system and talk more about how you can exploit all its many parameters to get the plot you want. We’ll focus on using the base plotting system to create graphics on the screen device rather than another graphics device.
The core plotting and graphics engine in R is encapsulated in two packages. The first is the graphics package which contains plotting functions for the “base” system. The functions in this package include plot
, hist
, boxplot
, barplot
, etc. The second package is grDevices which contains all the code implementing the various graphics devices, including X11, PDF, PostScript, PNG, etc.
Base graphics are often constructed piecemeal, with each aspect of the plot handled separately through a particular function call. Usually you start with a plot function (such as plot
, hist
, or boxplot
), then you use annotation functions (text
, abline
, points
) to add to or modify your plot.
Before making a plot you have to determine where the plot will appear and what it will be used for. Is there a large amount of data going into the plot? Or is it just a few points? Do you need to be able to dynamically resize the graphic?
What do you think is a disadvantage of the Base Plotting System?
- It mirrors how we think of building plots and analyzing data
- It’s intuitive and exploratory
- A complicated plot is a series of simple R commands
- You can’t go back once a plot has started
Yes! The base system is very intuitive and easy to use. You can’t go backwards, though, say, if you need to readjust margins or have misspelled a caption. A finished plot will be a series of R commands, so it’s difficult to translate a finished plot into a different system.
Calling a basic routine such as plot(x, y)
or hist(x)
launches a graphics device (if one is not already open) and draws a new plot on the device. If the arguments to plot or hist are not of some special class, then the default method is called.
As you’ll see, most of the base plotting functions have many arguments, for example, setting the title, labels of axes, plot character, etc. Some of the parameters can be set when you call the function or they can be added later in a separate function call.
Now we’ll go through some quick examples of basic plotting before we delve into gory details. We’ll use the dataset airquality
(part of the library datasets) which we’ve loaded for you. This shows ozone and other air measurements for New York City for 5 months in 1973.
Use the R command head
with airquality
as an argument to see what the data looks like.
head(airquality)
We see the dataset contains 6 columns of data. Run the command range
with two arguments. The first is the ozone column of airquality, specified by airquality$Ozone
, and the second is the boolean na.rm
set equal to TRUE
. If you don’t specify this second argument, you won’t get a meaningful result.
range(airquality$Ozone, na.rm = TRUE)
[1] 1 168
So the measurements range from 1 to 168. First we’ll do a simple histogram of this ozone column to show the distribution of measurements. Use the R command hist
with the argument airquality$Ozone
.
hist(airquality$Ozone)

Simple, right? R put a title on the histogram and labeled both axes for you.
What is the most frequent count?
- Between 60 and 75
- Over 100
- Over 150
- Under 25
Next we’ll do a boxplot. First, though, run the R command table
with the argument airquality$Month
.
table(airquality$Month)
5 6 7 8 9
31 30 31 31 30
We see that the data covers 5 months, May through September. We’ll want a boxplot of ozone as a function of the month in which the measurements were taken so we’ll use the R formula Ozone~Month
as the first argument of boxplot
. Our second argument will be airquality
, the dataset from which the variables of the first argument are taken. Try this now.
boxplot(Ozone~Month, airquality)

Note that boxplot, unlike hist, did NOT specify a title and axis labels for you automatically.
Let’s call boxplot
again to specify labels. (Use the up arrow to recover the previous command and save yourself some typing.) We’ll add more arguments to the call to specify labels for the 2 axes. Set xlab
equal to "Month"
and ylab
equal to "Ozone (ppb)"
. Specify col.axis
equal to "blue"
and col.lab
equal to "red"
. Try this now.
boxplot(Ozone~Month, airquality, xlab = "Month", ylab = "Ozone (ppb)", col.axis = "blue", col.lab = "red")

Nice colors, but still no title. Let’s add one with the R command title
. Use the argument main
set equal to the string "Ozone and Wind in New York City"
.
boxplot(Ozone~Month, airquality, xlab = "Month", ylab = "Ozone (ppb)", col.axis = "blue", col.lab = "red")
title(main="Ozone and Wind in New York City")

Now we’ll show you how to plot a simple two-dimensional scatterplot using the R function plot
. We’ll show the relationship between Wind
(x-axis) and Ozone
(y-axis). We’ll use the function plot
with those two arguments (Wind
and Ozone
, in that order). To save some typing, though, we’ll call the R command with
using 2 arguments. The first argument of with will be airquality
, the dataset containing Wind
and Ozone
; the second argument will be the call to plot
. Doing this allows us to avoid using the longer notation, e.g., airquality$Wind
. Try this now.
with(airquality, plot(Wind, Ozone))

Note that plot generated labels for the x and y axes but no title.
Add one now with the R command title
. Use the argument main set equal to the string "Ozone and Wind in New York City"
. (You can use the up arrow to recover the command if you don’t want to type it.)
with(airquality, plot(Wind, Ozone))
title(main="Ozone and Wind in New York City")

The basic plotting parameters are documented in the R help page for the function par
. You can use par
to set parameters OR to find out what values are already set. To see just how much flexibility you have, run the R command length
with the argument par()
now.
length(par())
[1] 72
So there are a boatload (72) of parameters that par()
gives you access to. Run the R function names
with par()
as its argument to see what these parameters are.
names(par())
[1] "xlog" "ylog" "adj" "ann" "ask" "bg" "bty" "cex"
[9] "cex.axis" "cex.lab" "cex.main" "cex.sub" "cin" "col" "col.axis" "col.lab"
[17] "col.main" "col.sub" "cra" "crt" "csi" "cxy" "din" "err"
[25] "family" "fg" "fig" "fin" "font" "font.axis" "font.lab" "font.main"
[33] "font.sub" "lab" "las" "lend" "lheight" "ljoin" "lmitre" "lty"
[41] "lwd" "mai" "mar" "mex" "mfcol" "mfg" "mfrow" "mgp"
[49] "mkh" "new" "oma" "omd" "omi" "page" "pch" "pin"
[57] "plt" "ps" "pty" "smo" "srt" "tck" "tcl" "usr"
[65] "xaxp" "xaxs" "xaxt" "xpd" "yaxp" "yaxs" "yaxt" "ylbias"
Variety is the spice of life. You might recognize some of these such as col
and lwd
from previous swirl lessons. You can always run ?par
to see what they do. For now, run the command par()$pin
and see what you get.
par()$pin
[1] 6.051666 3.059999
Alternatively, you could have gotten the same result by running par("pin")
or par('pin'))
.
What do you think these two numbers represent? The function par
specifies graphical parameters so the answer should deal with plots. That leaves two choices. The ‘in’ in 'pin'
specifies inches.
- Plot dimensions in inches
- A confidence interval
- Coordinates of the center of the plot window
- Random numbers
Now, run the command par("fg")
or or par('fg')
or par()$fg
and see what you get.
par("fg")
[1] "black"
It gave you a color, right? Since par()$fg
specifies foreground color, what do you think par()$bg
specifies?
- Beautiful color
- Background color
- blue-green
- Better color
Many base plotting functions share a set of parameters. We’ll go through some of the more commonly used ones now. See if you can tell what they do from their names.
What do you think the graphical parameter pch
controls?
- plot character
- point control height
- pc help
- picture characteristics
The plot character default is the open circle, but it “can either be a single character or an integer code for one of a set of graphics symbols.” Run the command par("pch")
to see the integer value of the default. When you need to, you can use R’s Documentation (?pch
) to find what the other values mean.
par("pch")
[1] 1
So 1 is the code for the open circle. What do you think the graphical parameters lty
and lwd
control respectively?
- line slope and intercept
- line width and type
- line length and width
- line type and width
Run the command par("lty")
to see the default line type.
par("lty")
[1] "solid"
So the default line type is solid, but it can be dashed, dotted, etc. Once again, R’s ?par documentation will tell you what other line types are available. The line width is a positive integer; the default value is 1.
We’ve seen a lot of examples of col
, the plotting color, specified as a number, string, or hex code; the colors()
function gives you a vector of colors by name.
What do you think the graphical parameters xlab
and ylab
control respectively?
- labels for the x- and y- axes
- labels for the y- and x- axes
The par()
function is used to specify global graphics parameters that affect all plots in an R session. (Use dev.off
or plot.new
to reset to the defaults.) These parameters can be overridden when specified as arguments to specific plotting functions. These include las
(the orientation of the axis labels on the plot), bg
(background color), mar
(margin size), oma
(outer margin size), mfrow
and mfcol
(number of plots per row, column).
The last two, mfrow
and mfcol
, both deal with multiple plots in that they specify the number of plots per row and column. The difference between them is the order in which they fill the plot matrix. The call mfrow
will fill the rows first while mfcol
fills the columns first.
So to reiterate, first call a basic plotting routine. For instance, plot
makes a scatterplot or other type of plot depending on the class of the object being plotted.
As we’ve seen, R provides several annotating functions. Which of the following is NOT one of them?
- title
- text
- points
- hist
- lines
So you can add text, title, points, and lines to an existing plot.
To add lines, you give a vector of x values and a corresponding vector of y values (or a 2-column matrix); the function lines
just connects the dots. The function text
adds text labels to a plot using specified x, y coordinates.
The function title
adds annotations. These include x- and y- axis labels, title, subtitle, and outer margin.
Two other annotating functions are mtext
which adds arbitrary text to either the outer or inner margins of the plot and axis which adds axis ticks and labels.
Another useful function is legend
which explains to the reader what the symbols your plot uses mean.
Before we close, let’s test your ability to make a somewhat complicated scatterplot. First run plot
with 3 arguments. airquality$Wind
, airquality$Ozone
, and type
set equal to "n"
. This tells R to set up the plot but not to put the data in it.
plot(airquality$Wind, airquality$Ozone, type = "n")

Now for the test. (You might need to check R’s documentation for some of these.) Add a title
with the argument main
set equal to the string "Wind and Ozone in NYC"
plot(airquality$Wind, airquality$Ozone, type = "n")
title(main = "Wind and Ozone in NYC")

Now create a variable called may
by subsetting airquality
appropriately. (Recall that the data specifies months by number and May is the fifth month of the year.)
may <- subset(airquality, Month == 5)
Now use the R command points
to plot May’s wind and ozone (in that order) as solid blue triangles. You have to set the color and plot character with two separate arguments. Note we use points
because we’re adding to an existing plot.
plot(airquality$Wind, airquality$Ozone, type = "n")
title(main = "Wind and Ozone in NYC")
points(may$Wind, may$Ozone, col="blue",pch=17)

Now create the variable notmay
by subsetting airquality
appropriately.
notmay <- subset(airquality, Month != 5)
Now use the R command points
to plot these notmay’s wind and ozone (in that order) as red snowflakes.
plot(airquality$Wind, airquality$Ozone, type = "n")
title(main = "Wind and Ozone in NYC")
points(may$Wind, may$Ozone, col="blue",pch=17)
notmay <- subset(airquality, Month != 5)
points(notmay$Wind, notmay$Ozone, col = "red", pch = 8)

Now we’ll use the R command legend
to clarify the plot and explain what it means. The function has a lot of arguments, but we’ll only use 4. The first will be the string "topright"
to tell R where to put the legend. The remaining 3 arguments will each be 2-long vectors created by R’s concatenate function, e.g., c(). These arguments are pch
, col
, and legend
. The first is the vector (17,8)
, the second ("blue","red")
, and the third ("May","Other Months")
. Try it now.
plot(airquality$Wind, airquality$Ozone, type = "n")
title(main = "Wind and Ozone in NYC")
points(may$Wind, may$Ozone, col="blue",pch=17)
notmay <- subset(airquality, Month != 5)
points(notmay$Wind, notmay$Ozone, col = "red", pch = 8)
legend("topright", pch = c(17, 8), col = c("blue", "red"), legend = c("May", "Other Months"))

Now add a vertical line at the median of airquality$Wind
. Make it dashed (lty=2
) with a width
of 2
.
plot(airquality$Wind, airquality$Ozone, type = "n")
title(main = "Wind and Ozone in NYC")
points(may$Wind, may$Ozone, col="blue",pch=17)
notmay <- subset(airquality, Month != 5)
points(notmay$Wind, notmay$Ozone, col = "red", pch = 8)
legend("topright", pch = c(17, 8), col = c("blue", "red"), legend = c("May", "Other Months"))
abline(v = median(airquality$Wind), lty=2, lwd=2)

Use par
with the parameter mfrow
set equal to the vector (1,2)
to set up the plot window for two plots side by side. You won’t see a result.
par(mfrow = c(1, 2))
Now plot airquality$Wind
and airquality$Ozone
and use main
to specify the title "Ozone and Wind"
.
par(mfrow = c(1, 2))
plot(airquality$Wind, airquality$Ozone, main = "Ozone and Wind")

Now for the second plot.
Plot airquality$Ozone
and airquality$Solar.R
and use main
to specify the title "Ozone and Solar Radiation"
.
par(mfrow = c(1, 2))
plot(airquality$Wind, airquality$Ozone, main = "Ozone and Wind")
plot(airquality$Ozone, airquality$Solar.R, main = "Ozone and Solar Radiation")

Now for something more challenging.
This one with 3 plots, to illustrate inner and outer margins. First, set up the plot window by typing par(mfrow = c(1, 3), mar = c(4, 4, 2, 1), oma = c(0, 0, 2, 0))
par(mfrow = c(1, 3), mar = c(4, 4, 2, 1), oma = c(0, 0, 2, 0))
Margins are specified as 4-long vectors of integers. Each number tells how many lines of text to leave at each side. The numbers are assigned clockwise starting at the bottom. The default for the inner margin is c(5.1, 4.1, 4.1, 2.1) so you can see we reduced each of these so we’ll have room for some outer text.
The first plot should be familiar. Plot airquality$Wind
and airquality$Ozone
with the title (argument main
) as "Ozone and Wind"
.
par(mfrow = c(1, 3), mar = c(4, 4, 2, 1), oma = c(0, 0, 2, 0))
plot(airquality$Wind, airquality$Ozone, main = "Ozone and Wind")

The second plot is similar.
Plot airquality$Solar.R
and airquality$Ozone
with the title (argument main
) as "Ozone and Solar Radiation"
.
par(mfrow = c(1, 3), mar = c(4, 4, 2, 1), oma = c(0, 0, 2, 0))
plot(airquality$Wind, airquality$Ozone, main = "Ozone and Wind")
plot(airquality$Solar.R, airquality$Ozone, main = "Ozone and Solar Radiation")

Now for the final panel.
Plot airquality$Temp
and airquality$Ozone
with the title (argument main
) as "Ozone and Temperature"
.
par(mfrow = c(1, 3), mar = c(4, 4, 2, 1), oma = c(0, 0, 2, 0))
plot(airquality$Wind, airquality$Ozone, main = "Ozone and Wind")
plot(airquality$Solar.R, airquality$Ozone, main = "Ozone and Solar Radiation")
plot(airquality$Temp, airquality$Ozone, main = "Ozone and Temperature")

Now we’ll put in a title.
Since this is the main title, we specify it with the R command mtext
. Call mtext
with the string "Ozone and Weather in New York City"
and the argument outer
set equal to TRUE
.
par(mfrow = c(1, 3), mar = c(4, 4, 2, 1), oma = c(0, 0, 2, 0))
plot(airquality$Wind, airquality$Ozone, main = "Ozone and Wind")
plot(airquality$Solar.R, airquality$Ozone, main = "Ozone and Solar Radiation")
plot(airquality$Temp, airquality$Ozone, main = "Ozone and Temperature")
mtext("Ozone and Weather in New York City", outer = TRUE)

Voila! Beautiful, right?
Congrats! You’ve weathered this lesson nicely and passed out of the No!zone.
END
LS0tDQp0aXRsZTogIkJhc2UgUGxvdHRpbmcgU3lzdGVtIg0Kc3VidGl0bGU6ICJTd2lybCBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIExlc3NvbiA1Ig0KYXV0aG9yOiAiUm9nZXIgRC4gUGVuZyAoY29tcGlsZWQgYW5kIGVkaXRlZCBieTogTnVubm8gTnVncm9obykiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQo8L2JyPg0KDQoqU2xpZGVzIGZvciB0aGlzIGFuZCBvdGhlciBEYXRhIFNjaWVuY2UgY291cnNlcyBtYXkgYmUgZm91bmQgYXQgZ2l0aHViIGh0dHBzOi8vZ2l0aHViLmNvbS9EYXRhU2NpZW5jZVNwZWNpYWxpemF0aW9uL2NvdXJzZXMvLiBJZiB5b3UgY2FyZSB0byB1c2UgdGhlbSwgdGhleSBtdXN0IGJlIGRvd25sb2FkZWQgYXMgYSB6aXAgZmlsZSBhbmQgdmlld2VkIGxvY2FsbHkuIFRoaXMgbGVzc29uIGNvcnJlc3BvbmRzIHRvIDA0X0V4cGxvcmF0b3J5QW5hbHlzaXMvUGxvdHRpbmdCYXNlLioNCg0KPC9icj4NCg0KSW4gYW5vdGhlciBsZXNzb24sIHdlIGdhdmUgeW91IGFuIG92ZXJ2aWV3IG9mIHRoZSB0aHJlZSBwbG90dGluZyBzeXN0ZW1zIGluIFIuIEluIHRoaXMgbGVzc29uIHdlJ2xsIGZvY3VzIG9uIHRoZSBiYXNlIHBsb3R0aW5nIHN5c3RlbSBhbmQgdGFsayBtb3JlIGFib3V0IGhvdyB5b3UgY2FuIGV4cGxvaXQgYWxsIGl0cyBtYW55IHBhcmFtZXRlcnMgdG8gZ2V0IHRoZSBwbG90IHlvdSB3YW50LiAgV2UnbGwgZm9jdXMgb24gdXNpbmcgdGhlIGJhc2UgcGxvdHRpbmcgc3lzdGVtIHRvIGNyZWF0ZSBncmFwaGljcyBvbiB0aGUgc2NyZWVuIGRldmljZSByYXRoZXIgdGhhbiBhbm90aGVyIGdyYXBoaWNzIGRldmljZS4NCg0KVGhlIGNvcmUgcGxvdHRpbmcgYW5kIGdyYXBoaWNzIGVuZ2luZSBpbiBSIGlzIGVuY2Fwc3VsYXRlZCBpbiB0d28gcGFja2FnZXMuIFRoZSBmaXJzdCBpcyB0aGUgZ3JhcGhpY3MgcGFja2FnZSB3aGljaCBjb250YWlucyBwbG90dGluZyBmdW5jdGlvbnMgZm9yIHRoZSAiYmFzZSIgc3lzdGVtLiBUaGUgZnVuY3Rpb25zIGluIHRoaXMgcGFja2FnZSBpbmNsdWRlIGBwbG90YCwgYGhpc3RgLCBgYm94cGxvdGAsIGBiYXJwbG90YCwgZXRjLiBUaGUgc2Vjb25kIHBhY2thZ2UgaXMgZ3JEZXZpY2VzIHdoaWNoIGNvbnRhaW5zIGFsbCB0aGUgY29kZSBpbXBsZW1lbnRpbmcgdGhlIHZhcmlvdXMgZ3JhcGhpY3MgZGV2aWNlcywgaW5jbHVkaW5nIFgxMSwgUERGLCBQb3N0U2NyaXB0LCBQTkcsIGV0Yy4NCg0KQmFzZSBncmFwaGljcyBhcmUgb2Z0ZW4gY29uc3RydWN0ZWQgcGllY2VtZWFsLCB3aXRoIGVhY2ggYXNwZWN0IG9mIHRoZSBwbG90IGhhbmRsZWQgc2VwYXJhdGVseSB0aHJvdWdoIGEgcGFydGljdWxhciBmdW5jdGlvbiBjYWxsLiBVc3VhbGx5IHlvdSBzdGFydCB3aXRoIGEgcGxvdCBmdW5jdGlvbiAoc3VjaCBhcyBgcGxvdGAsIGBoaXN0YCwgb3IgYGJveHBsb3RgKSwgdGhlbiB5b3UgdXNlIGFubm90YXRpb24gZnVuY3Rpb25zIChgdGV4dGAsIGBhYmxpbmVgLCBgcG9pbnRzYCkgdG8gYWRkIHRvIG9yIG1vZGlmeSB5b3VyIHBsb3QuDQoNCkJlZm9yZSBtYWtpbmcgYSBwbG90IHlvdSBoYXZlIHRvIGRldGVybWluZSB3aGVyZSB0aGUgcGxvdCB3aWxsIGFwcGVhciBhbmQgd2hhdCBpdCB3aWxsIGJlIHVzZWQgZm9yLiAgSXMgdGhlcmUgYSBsYXJnZSBhbW91bnQgb2YgZGF0YSBnb2luZyBpbnRvIHRoZSBwbG90PyBPciBpcyBpdCBqdXN0IGEgZmV3IHBvaW50cz8gRG8geW91IG5lZWQgdG8gYmUgYWJsZSB0byBkeW5hbWljYWxseSByZXNpemUgdGhlIGdyYXBoaWM/DQoNCldoYXQgZG8geW91IHRoaW5rIGlzIGEgZGlzYWR2YW50YWdlIG9mIHRoZSBCYXNlIFBsb3R0aW5nIFN5c3RlbT8NCg0KMS4gSXQgbWlycm9ycyBob3cgd2UgdGhpbmsgb2YgYnVpbGRpbmcgcGxvdHMgYW5kIGFuYWx5emluZyBkYXRhDQoyLiBJdCdzIGludHVpdGl2ZSBhbmQgZXhwbG9yYXRvcnkNCjMuIEEgY29tcGxpY2F0ZWQgcGxvdCBpcyBhIHNlcmllcyBvZiBzaW1wbGUgUiBjb21tYW5kcw0KNC4gKipZb3UgY2FuJ3QgZ28gYmFjayBvbmNlIGEgcGxvdCBoYXMgc3RhcnRlZCoqDQoNClllcyEgVGhlIGJhc2Ugc3lzdGVtIGlzIHZlcnkgaW50dWl0aXZlIGFuZCBlYXN5IHRvIHVzZS4gWW91IGNhbid0IGdvIGJhY2t3YXJkcywgdGhvdWdoLCBzYXksIGlmIHlvdSBuZWVkIHRvIHJlYWRqdXN0IG1hcmdpbnMgb3IgaGF2ZSBtaXNzcGVsbGVkIGEgY2FwdGlvbi4gQSBmaW5pc2hlZCBwbG90IHdpbGwgYmUgYSBzZXJpZXMgb2YgUiBjb21tYW5kcywgc28gaXQncyBkaWZmaWN1bHQgdG8gdHJhbnNsYXRlIGEgZmluaXNoZWQgcGxvdCBpbnRvIGEgZGlmZmVyZW50IHN5c3RlbS4NCg0KQ2FsbGluZyBhIGJhc2ljIHJvdXRpbmUgc3VjaCBhcyBgcGxvdCh4LCB5KWAgb3IgYGhpc3QoeClgIGxhdW5jaGVzIGEgZ3JhcGhpY3MgZGV2aWNlIChpZiBvbmUgaXMgbm90IGFscmVhZHkgb3BlbikgYW5kIGRyYXdzIGEgbmV3IHBsb3Qgb24gdGhlIGRldmljZS4gSWYgdGhlIGFyZ3VtZW50cyB0byBwbG90IG9yIGhpc3QgYXJlIG5vdCBvZiBzb21lIHNwZWNpYWwgY2xhc3MsIHRoZW4gdGhlIGRlZmF1bHQgbWV0aG9kIGlzIGNhbGxlZC4NCg0KQXMgeW91J2xsIHNlZSwgbW9zdCBvZiB0aGUgYmFzZSBwbG90dGluZyBmdW5jdGlvbnMgaGF2ZSBtYW55IGFyZ3VtZW50cywgZm9yIGV4YW1wbGUsIHNldHRpbmcgdGhlIHRpdGxlLCBsYWJlbHMgb2YgYXhlcywgcGxvdCBjaGFyYWN0ZXIsIGV0Yy4gU29tZSBvZiB0aGUgcGFyYW1ldGVycyBjYW4gYmUgc2V0IHdoZW4geW91IGNhbGwgdGhlIGZ1bmN0aW9uIG9yIHRoZXkgY2FuIGJlIGFkZGVkIGxhdGVyIGluIGEgc2VwYXJhdGUgZnVuY3Rpb24gY2FsbC4NCg0KTm93IHdlJ2xsIGdvIHRocm91Z2ggc29tZSBxdWljayBleGFtcGxlcyBvZiBiYXNpYyBwbG90dGluZyBiZWZvcmUgd2UgZGVsdmUgaW50byBnb3J5IGRldGFpbHMuIFdlJ2xsIHVzZSB0aGUgZGF0YXNldCBgYWlycXVhbGl0eWAgKHBhcnQgb2YgdGhlIGxpYnJhcnkgZGF0YXNldHMpIHdoaWNoIHdlJ3ZlIGxvYWRlZCBmb3IgeW91LiBUaGlzIHNob3dzIG96b25lIGFuZCBvdGhlciBhaXIgbWVhc3VyZW1lbnRzIGZvciBOZXcgWW9yayBDaXR5IGZvciA1IG1vbnRocyBpbiAxOTczLg0KDQpVc2UgdGhlIFIgY29tbWFuZCBgaGVhZGAgd2l0aCBgYWlycXVhbGl0eWAgYXMgYW4gYXJndW1lbnQgdG8gc2VlIHdoYXQgdGhlIGRhdGEgbG9va3MgbGlrZS4NCg0KYGBge3J9DQpoZWFkKGFpcnF1YWxpdHkpDQpgYGANCg0KV2Ugc2VlIHRoZSBkYXRhc2V0IGNvbnRhaW5zIDYgY29sdW1ucyBvZiBkYXRhLiBSdW4gdGhlIGNvbW1hbmQgYHJhbmdlYCB3aXRoIHR3byBhcmd1bWVudHMuIFRoZSBmaXJzdCBpcyB0aGUgb3pvbmUgY29sdW1uIG9mIGFpcnF1YWxpdHksIHNwZWNpZmllZCBieSBgYWlycXVhbGl0eSRPem9uZWAsIGFuZCB0aGUgc2Vjb25kIGlzIHRoZSBib29sZWFuIGBuYS5ybWAgc2V0IGVxdWFsIHRvIGBUUlVFYC4gSWYgeW91IGRvbid0IHNwZWNpZnkgdGhpcyBzZWNvbmQgYXJndW1lbnQsIHlvdSB3b24ndCBnZXQgYSBtZWFuaW5nZnVsIHJlc3VsdC4NCg0KYGBge3J9DQpyYW5nZShhaXJxdWFsaXR5JE96b25lLCBuYS5ybSA9IFRSVUUpDQpgYGANCg0KU28gdGhlIG1lYXN1cmVtZW50cyByYW5nZSBmcm9tIDEgdG8gMTY4LiBGaXJzdCB3ZSdsbCBkbyBhIHNpbXBsZSBoaXN0b2dyYW0gb2YgdGhpcyBvem9uZSBjb2x1bW4gdG8gc2hvdyB0aGUgZGlzdHJpYnV0aW9uIG9mIG1lYXN1cmVtZW50cy4gVXNlIHRoZSBSIGNvbW1hbmQgYGhpc3RgIHdpdGggdGhlIGFyZ3VtZW50IGBhaXJxdWFsaXR5JE96b25lYC4NCg0KYGBge3J9DQpoaXN0KGFpcnF1YWxpdHkkT3pvbmUpDQpgYGANCg0KU2ltcGxlLCByaWdodD8gUiBwdXQgYSB0aXRsZSBvbiB0aGUgaGlzdG9ncmFtIGFuZCBsYWJlbGVkIGJvdGggYXhlcyBmb3IgeW91Lg0KDQoNCldoYXQgaXMgdGhlIG1vc3QgZnJlcXVlbnQgY291bnQ/DQoNCjEuIEJldHdlZW4gNjAgYW5kIDc1DQoyLiBPdmVyIDEwMA0KMy4gT3ZlciAxNTANCjQuICoqVW5kZXIgMjUqKg0KDQpOZXh0IHdlJ2xsIGRvIGEgYm94cGxvdC4gRmlyc3QsIHRob3VnaCwgcnVuIHRoZSBSIGNvbW1hbmQgYHRhYmxlYCB3aXRoIHRoZSBhcmd1bWVudCBgYWlycXVhbGl0eSRNb250aGAuDQoNCmBgYHtyfQ0KdGFibGUoYWlycXVhbGl0eSRNb250aCkNCmBgYA0KDQpXZSBzZWUgdGhhdCB0aGUgZGF0YSBjb3ZlcnMgNSBtb250aHMsIE1heSB0aHJvdWdoIFNlcHRlbWJlci4gV2UnbGwgd2FudCBhIGJveHBsb3Qgb2Ygb3pvbmUgYXMgYSBmdW5jdGlvbiBvZiB0aGUgbW9udGggaW4gd2hpY2ggdGhlIG1lYXN1cmVtZW50cyB3ZXJlIHRha2VuIHNvIHdlJ2xsIHVzZSB0aGUgUiBmb3JtdWxhIGBPem9uZX5Nb250aGAgYXMgdGhlIGZpcnN0IGFyZ3VtZW50IG9mIGBib3hwbG90YC4gT3VyIHNlY29uZCBhcmd1bWVudCB3aWxsIGJlIGBhaXJxdWFsaXR5YCwgdGhlIGRhdGFzZXQgZnJvbSB3aGljaCB0aGUgdmFyaWFibGVzIG9mIHRoZSBmaXJzdCBhcmd1bWVudCBhcmUgdGFrZW4uICBUcnkgdGhpcyBub3cuDQoNCmBgYHtyfQ0KYm94cGxvdChPem9uZX5Nb250aCwgYWlycXVhbGl0eSkNCmBgYA0KDQpOb3RlIHRoYXQgYm94cGxvdCwgdW5saWtlIGhpc3QsIGRpZCBOT1Qgc3BlY2lmeSBhIHRpdGxlIGFuZCBheGlzIGxhYmVscyBmb3IgeW91IGF1dG9tYXRpY2FsbHkuDQoNCkxldCdzIGNhbGwgYGJveHBsb3RgIGFnYWluIHRvIHNwZWNpZnkgbGFiZWxzLiAoVXNlIHRoZSB1cCBhcnJvdyB0byByZWNvdmVyIHRoZSBwcmV2aW91cyBjb21tYW5kIGFuZCBzYXZlIHlvdXJzZWxmIHNvbWUgdHlwaW5nLikgV2UnbGwgYWRkIG1vcmUgYXJndW1lbnRzIHRvIHRoZSBjYWxsIHRvIHNwZWNpZnkgbGFiZWxzIGZvciB0aGUgMiBheGVzLiBTZXQgYHhsYWJgIGVxdWFsIHRvIGAiTW9udGgiYCBhbmQgYHlsYWJgIGVxdWFsIHRvIGAiT3pvbmUgKHBwYikiYC4gU3BlY2lmeSBgY29sLmF4aXNgIGVxdWFsIHRvIGAiYmx1ZSJgIGFuZCBgY29sLmxhYmAgZXF1YWwgdG8gYCJyZWQiYC4gVHJ5IHRoaXMgbm93Lg0KDQpgYGB7cn0NCmJveHBsb3QoT3pvbmV+TW9udGgsIGFpcnF1YWxpdHksIHhsYWIgPSAiTW9udGgiLCB5bGFiID0gIk96b25lIChwcGIpIiwgY29sLmF4aXMgPSAiYmx1ZSIsIGNvbC5sYWIgPSAicmVkIikNCmBgYA0KTmljZSBjb2xvcnMsIGJ1dCBzdGlsbCBubyB0aXRsZS4gTGV0J3MgYWRkIG9uZSB3aXRoIHRoZSBSIGNvbW1hbmQgYHRpdGxlYC4gVXNlIHRoZSBhcmd1bWVudCBgbWFpbmAgc2V0IGVxdWFsIHRvIHRoZSBzdHJpbmcgYCJPem9uZSBhbmQgV2luZCBpbiBOZXcgWW9yayBDaXR5ImAuDQoNCmBgYHtyfQ0KYm94cGxvdChPem9uZX5Nb250aCwgYWlycXVhbGl0eSwgeGxhYiA9ICJNb250aCIsIHlsYWIgPSAiT3pvbmUgKHBwYikiLCBjb2wuYXhpcyA9ICJibHVlIiwgY29sLmxhYiA9ICJyZWQiKQ0KdGl0bGUobWFpbj0iT3pvbmUgYW5kIFdpbmQgaW4gTmV3IFlvcmsgQ2l0eSIpDQpgYGANCk5vdyB3ZSdsbCBzaG93IHlvdSBob3cgdG8gcGxvdCBhIHNpbXBsZSB0d28tZGltZW5zaW9uYWwgc2NhdHRlcnBsb3QgdXNpbmcgdGhlIFIgZnVuY3Rpb24gYHBsb3RgLiBXZSdsbCBzaG93IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBgV2luZGAgKHgtYXhpcykgYW5kIGBPem9uZWAgKHktYXhpcykuIFdlJ2xsIHVzZSB0aGUgZnVuY3Rpb24gYHBsb3RgIHdpdGggdGhvc2UgdHdvIGFyZ3VtZW50cyAoYFdpbmRgIGFuZCBgT3pvbmVgLCBpbiB0aGF0IG9yZGVyKS4gVG8gc2F2ZSBzb21lIHR5cGluZywgdGhvdWdoLCB3ZSdsbCBjYWxsIHRoZSBSIGNvbW1hbmQgYHdpdGhgIHVzaW5nIDIgYXJndW1lbnRzLiBUaGUgZmlyc3QgYXJndW1lbnQgb2Ygd2l0aCB3aWxsIGJlIGBhaXJxdWFsaXR5YCwgdGhlIGRhdGFzZXQgY29udGFpbmluZyBgV2luZGAgYW5kIGBPem9uZWA7IHRoZSBzZWNvbmQgYXJndW1lbnQgd2lsbCBiZSB0aGUgY2FsbCB0byBgcGxvdGAuIERvaW5nIHRoaXMgYWxsb3dzIHVzIHRvIGF2b2lkIHVzaW5nIHRoZSBsb25nZXIgbm90YXRpb24sIGUuZy4sIGBhaXJxdWFsaXR5JFdpbmRgLiBUcnkgdGhpcyBub3cuDQoNCmBgYHtyfQ0Kd2l0aChhaXJxdWFsaXR5LCBwbG90KFdpbmQsIE96b25lKSkNCmBgYA0KDQpOb3RlIHRoYXQgcGxvdCBnZW5lcmF0ZWQgbGFiZWxzIGZvciB0aGUgeCBhbmQgeSBheGVzIGJ1dCBubyB0aXRsZS4NCg0KQWRkIG9uZSBub3cgd2l0aCB0aGUgUiBjb21tYW5kIGB0aXRsZWAuIFVzZSB0aGUgYXJndW1lbnQgbWFpbiBzZXQgZXF1YWwgdG8gdGhlIHN0cmluZyBgIk96b25lIGFuZCBXaW5kIGluIE5ldyBZb3JrIENpdHkiYC4gKFlvdSBjYW4gdXNlIHRoZSB1cCBhcnJvdyB0byByZWNvdmVyIHRoZSBjb21tYW5kIGlmIHlvdSBkb24ndCB3YW50IHRvIHR5cGUgaXQuKQ0KDQpgYGB7cn0NCndpdGgoYWlycXVhbGl0eSwgcGxvdChXaW5kLCBPem9uZSkpDQp0aXRsZShtYWluPSJPem9uZSBhbmQgV2luZCBpbiBOZXcgWW9yayBDaXR5IikNCmBgYA0KDQpUaGUgYmFzaWMgcGxvdHRpbmcgcGFyYW1ldGVycyBhcmUgZG9jdW1lbnRlZCBpbiB0aGUgUiBoZWxwIHBhZ2UgZm9yIHRoZSBmdW5jdGlvbiBgcGFyYC4gWW91IGNhbiB1c2UgYHBhcmAgdG8gc2V0IHBhcmFtZXRlcnMgT1IgdG8gZmluZCBvdXQgd2hhdCB2YWx1ZXMgYXJlIGFscmVhZHkgc2V0LiBUbyBzZWUganVzdCBob3cgbXVjaCBmbGV4aWJpbGl0eSB5b3UgaGF2ZSwgcnVuIHRoZSBSIGNvbW1hbmQgYGxlbmd0aGAgd2l0aCB0aGUgYXJndW1lbnQgYHBhcigpYCBub3cuDQoNCmBgYHtyfQ0KbGVuZ3RoKHBhcigpKQ0KYGBgDQoNClNvIHRoZXJlIGFyZSBhIGJvYXRsb2FkICg3Mikgb2YgcGFyYW1ldGVycyB0aGF0IGBwYXIoKWAgZ2l2ZXMgeW91IGFjY2VzcyB0by4gUnVuIHRoZSBSIGZ1bmN0aW9uIGBuYW1lc2Agd2l0aCBgcGFyKClgIGFzIGl0cyBhcmd1bWVudCB0byBzZWUgd2hhdCB0aGVzZSBwYXJhbWV0ZXJzIGFyZS4NCg0KYGBge3J9DQpuYW1lcyhwYXIoKSkNCmBgYA0KDQpWYXJpZXR5IGlzIHRoZSBzcGljZSBvZiBsaWZlLiBZb3UgbWlnaHQgcmVjb2duaXplIHNvbWUgb2YgdGhlc2Ugc3VjaCBhcyBgY29sYCBhbmQgYGx3ZGAgZnJvbSBwcmV2aW91cyBzd2lybCBsZXNzb25zLiBZb3UgY2FuIGFsd2F5cyBydW4gYD9wYXJgIHRvIHNlZSB3aGF0IHRoZXkgZG8uIEZvciBub3csIHJ1biB0aGUgY29tbWFuZCBgcGFyKCkkcGluYCBhbmQgc2VlIHdoYXQgeW91IGdldC4NCg0KYGBge3J9DQpwYXIoKSRwaW4NCmBgYA0KDQpBbHRlcm5hdGl2ZWx5LCB5b3UgY291bGQgaGF2ZSBnb3R0ZW4gdGhlIHNhbWUgcmVzdWx0IGJ5IHJ1bm5pbmcgYHBhcigicGluIilgIG9yIGBwYXIoJ3BpbicpKWAuDQoNCldoYXQgZG8geW91IHRoaW5rIHRoZXNlIHR3byBudW1iZXJzIHJlcHJlc2VudD8gVGhlIGZ1bmN0aW9uIGBwYXJgIHNwZWNpZmllcyBncmFwaGljYWwgcGFyYW1ldGVycyBzbyB0aGUgYW5zd2VyIHNob3VsZCBkZWFsIHdpdGggcGxvdHMuIFRoYXQgbGVhdmVzIHR3byBjaG9pY2VzLiBUaGUgJ2luJyBpbiBgJ3BpbidgIHNwZWNpZmllcyBpbmNoZXMuDQoNCjEuICoqUGxvdCBkaW1lbnNpb25zIGluIGluY2hlcyoqDQoyLiBBIGNvbmZpZGVuY2UgaW50ZXJ2YWwNCjMuIENvb3JkaW5hdGVzIG9mIHRoZSBjZW50ZXIgb2YgdGhlIHBsb3Qgd2luZG93DQo0LiBSYW5kb20gbnVtYmVycw0KDQpOb3csIHJ1biB0aGUgY29tbWFuZCBgcGFyKCJmZyIpYCBvciBvciBgcGFyKCdmZycpYCBvciBwYXJgKCkkZmdgIGFuZCBzZWUgd2hhdCB5b3UgZ2V0Lg0KDQpgYGB7cn0NCnBhcigiZmciKQ0KYGBgDQoNCkl0IGdhdmUgeW91IGEgY29sb3IsIHJpZ2h0PyBTaW5jZSBgcGFyKCkkZmdgIHNwZWNpZmllcyBmb3JlZ3JvdW5kIGNvbG9yLCB3aGF0IGRvIHlvdSB0aGluayBgcGFyKCkkYmdgIHNwZWNpZmllcz8NCg0KMS4gQmVhdXRpZnVsIGNvbG9yDQoyLiAqKkJhY2tncm91bmQgY29sb3IqKg0KMy4gYmx1ZS1ncmVlbg0KNC4gQmV0dGVyIGNvbG9yDQoNCk1hbnkgYmFzZSBwbG90dGluZyBmdW5jdGlvbnMgc2hhcmUgYSBzZXQgb2YgcGFyYW1ldGVycy4gV2UnbGwgZ28gdGhyb3VnaCBzb21lIG9mIHRoZSBtb3JlIGNvbW1vbmx5IHVzZWQgb25lcyBub3cuIFNlZSBpZiB5b3UgY2FuIHRlbGwgd2hhdCB0aGV5IGRvIGZyb20gdGhlaXIgbmFtZXMuDQoNCldoYXQgZG8geW91IHRoaW5rIHRoZSBncmFwaGljYWwgcGFyYW1ldGVyIGBwY2hgIGNvbnRyb2xzPw0KDQoxLiAqKnBsb3QgY2hhcmFjdGVyKioNCjIuIHBvaW50IGNvbnRyb2wgaGVpZ2h0DQozLiBwYyBoZWxwDQo0LiBwaWN0dXJlIGNoYXJhY3RlcmlzdGljcw0KDQpUaGUgcGxvdCBjaGFyYWN0ZXIgZGVmYXVsdCBpcyB0aGUgb3BlbiBjaXJjbGUsIGJ1dCBpdCAiY2FuIGVpdGhlciBiZSBhIHNpbmdsZSBjaGFyYWN0ZXIgb3IgYW4gaW50ZWdlciBjb2RlIGZvciBvbmUgb2YgYSBzZXQgb2YgZ3JhcGhpY3Mgc3ltYm9scy4iIFJ1biB0aGUgY29tbWFuZCBgcGFyKCJwY2giKWAgdG8gc2VlIHRoZSBpbnRlZ2VyIHZhbHVlIG9mIHRoZSBkZWZhdWx0LiBXaGVuIHlvdSBuZWVkIHRvLCB5b3UgY2FuIHVzZSBSJ3MgRG9jdW1lbnRhdGlvbiAoYD9wY2hgKSB0byBmaW5kIHdoYXQgdGhlIG90aGVyIHZhbHVlcyBtZWFuLg0KDQpgYGB7cn0NCnBhcigicGNoIikNCmBgYA0KDQoNClNvIDEgaXMgdGhlIGNvZGUgZm9yIHRoZSBvcGVuIGNpcmNsZS4gV2hhdCBkbyB5b3UgdGhpbmsgdGhlIGdyYXBoaWNhbCBwYXJhbWV0ZXJzIGBsdHlgIGFuZCBgbHdkYCBjb250cm9sIHJlc3BlY3RpdmVseT8NCg0KMS4gbGluZSBzbG9wZSBhbmQgaW50ZXJjZXB0DQoyLiBsaW5lIHdpZHRoIGFuZCB0eXBlDQozLiBsaW5lIGxlbmd0aCBhbmQgd2lkdGgNCjQuICoqbGluZSB0eXBlIGFuZCB3aWR0aCoqDQoNClJ1biB0aGUgY29tbWFuZCBgcGFyKCJsdHkiKWAgdG8gc2VlIHRoZSBkZWZhdWx0IGxpbmUgdHlwZS4NCg0KYGBge3J9DQpwYXIoImx0eSIpDQpgYGANCg0KU28gdGhlIGRlZmF1bHQgbGluZSB0eXBlIGlzICpzb2xpZCosIGJ1dCBpdCBjYW4gYmUgZGFzaGVkLCBkb3R0ZWQsIGV0Yy4gT25jZSBhZ2FpbiwgUidzID9wYXIgZG9jdW1lbnRhdGlvbiB3aWxsIHRlbGwgeW91IHdoYXQgb3RoZXIgbGluZSB0eXBlcyBhcmUgYXZhaWxhYmxlLiBUaGUgbGluZSB3aWR0aCBpcyBhIHBvc2l0aXZlIGludGVnZXI7IHRoZSBkZWZhdWx0IHZhbHVlIGlzIDEuDQoNCldlJ3ZlIHNlZW4gYSBsb3Qgb2YgZXhhbXBsZXMgb2YgYGNvbGAsIHRoZSBwbG90dGluZyBjb2xvciwgc3BlY2lmaWVkIGFzIGEgbnVtYmVyLCBzdHJpbmcsIG9yIGhleCBjb2RlOyB0aGUgYGNvbG9ycygpYCBmdW5jdGlvbiBnaXZlcyB5b3UgYSB2ZWN0b3Igb2YgY29sb3JzIGJ5IG5hbWUuDQoNCldoYXQgZG8geW91IHRoaW5rIHRoZSBncmFwaGljYWwgcGFyYW1ldGVycyBgeGxhYmAgYW5kIGB5bGFiYCBjb250cm9sIHJlc3BlY3RpdmVseT8NCg0KMS4gKipsYWJlbHMgZm9yIHRoZSB4LSBhbmQgeS0gYXhlcyoqDQoyLiBsYWJlbHMgZm9yIHRoZSB5LSBhbmQgeC0gYXhlcw0KDQpUaGUgYHBhcigpYCBmdW5jdGlvbiBpcyB1c2VkIHRvIHNwZWNpZnkgZ2xvYmFsIGdyYXBoaWNzIHBhcmFtZXRlcnMgdGhhdCBhZmZlY3QgYWxsIHBsb3RzIGluIGFuIFIgc2Vzc2lvbi4gKFVzZSBgZGV2Lm9mZmAgb3IgYHBsb3QubmV3YCB0byByZXNldCB0byB0aGUgZGVmYXVsdHMuKSBUaGVzZSBwYXJhbWV0ZXJzIGNhbiBiZSBvdmVycmlkZGVuIHdoZW4gc3BlY2lmaWVkIGFzIGFyZ3VtZW50cyB0byBzcGVjaWZpYyBwbG90dGluZyBmdW5jdGlvbnMuIFRoZXNlIGluY2x1ZGUgYGxhc2AgKHRoZSBvcmllbnRhdGlvbiBvZiB0aGUgYXhpcyBsYWJlbHMgb24gdGhlIHBsb3QpLCBgYmdgIChiYWNrZ3JvdW5kIGNvbG9yKSwgYG1hcmAgKG1hcmdpbiBzaXplKSwgYG9tYWAgKG91dGVyIG1hcmdpbiBzaXplKSwgYG1mcm93YCBhbmQgYG1mY29sYCAobnVtYmVyIG9mIHBsb3RzIHBlciByb3csIGNvbHVtbikuDQoNClRoZSBsYXN0IHR3bywgYG1mcm93YCBhbmQgYG1mY29sYCwgYm90aCBkZWFsIHdpdGggbXVsdGlwbGUgcGxvdHMgaW4gdGhhdCB0aGV5IHNwZWNpZnkgdGhlIG51bWJlciBvZiBwbG90cyBwZXIgcm93IGFuZCBjb2x1bW4uIFRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlbSBpcyB0aGUgb3JkZXIgaW4gd2hpY2ggdGhleSBmaWxsIHRoZSBwbG90IG1hdHJpeC4gVGhlIGNhbGwgYG1mcm93YCB3aWxsIGZpbGwgdGhlIHJvd3MgZmlyc3Qgd2hpbGUgYG1mY29sYCBmaWxscyB0aGUgY29sdW1ucyBmaXJzdC4NCg0KU28gdG8gcmVpdGVyYXRlLCBmaXJzdCBjYWxsIGEgYmFzaWMgcGxvdHRpbmcgcm91dGluZS4gRm9yIGluc3RhbmNlLCBgcGxvdGAgbWFrZXMgYSBzY2F0dGVycGxvdCBvciBvdGhlciB0eXBlIG9mIHBsb3QgZGVwZW5kaW5nIG9uIHRoZSBjbGFzcyBvZiB0aGUgb2JqZWN0IGJlaW5nIHBsb3R0ZWQuDQoNCjwvYnI+DQoNCkFzIHdlJ3ZlIHNlZW4sIFIgcHJvdmlkZXMgc2V2ZXJhbCBhbm5vdGF0aW5nIGZ1bmN0aW9ucy4gV2hpY2ggb2YgdGhlIGZvbGxvd2luZyBpcyBOT1Qgb25lIG9mIHRoZW0/DQoNCjEuIHRpdGxlDQoyLiB0ZXh0DQozLiBwb2ludHMNCjQuICoqaGlzdCoqDQo1LiBsaW5lcw0KDQpTbyB5b3UgY2FuIGFkZCAqdGV4dCosICp0aXRsZSosICpwb2ludHMqLCBhbmQgKmxpbmVzKiB0byBhbiBleGlzdGluZyBwbG90Lg0KDQpUbyBhZGQgKipsaW5lcyoqLCB5b3UgZ2l2ZSBhIHZlY3RvciBvZiB4IHZhbHVlcyBhbmQgYSBjb3JyZXNwb25kaW5nIHZlY3RvciBvZiB5IHZhbHVlcyAob3IgYSAyLWNvbHVtbiBtYXRyaXgpOyB0aGUgZnVuY3Rpb24gYGxpbmVzYCBqdXN0IGNvbm5lY3RzIHRoZSBkb3RzLiBUaGUgZnVuY3Rpb24gYHRleHRgIGFkZHMgdGV4dCBsYWJlbHMgdG8gYSBwbG90IHVzaW5nIHNwZWNpZmllZCB4LCB5IGNvb3JkaW5hdGVzLg0KDQpUaGUgZnVuY3Rpb24gKipgdGl0bGVgKiogYWRkcyBhbm5vdGF0aW9ucy4gVGhlc2UgaW5jbHVkZSB4LSBhbmQgeS0gYXhpcyBsYWJlbHMsIHRpdGxlLCBzdWJ0aXRsZSwgYW5kIG91dGVyIG1hcmdpbi4NCg0KVHdvIG90aGVyIGFubm90YXRpbmcgZnVuY3Rpb25zIGFyZSAqKmBtdGV4dGAqKiB3aGljaCBhZGRzIGFyYml0cmFyeSB0ZXh0IHRvIGVpdGhlciB0aGUgb3V0ZXIgb3IgaW5uZXIgbWFyZ2lucyBvZiB0aGUgcGxvdCBhbmQgYXhpcyB3aGljaCBhZGRzIGF4aXMgdGlja3MgYW5kIGxhYmVscy4NCg0KQW5vdGhlciB1c2VmdWwgZnVuY3Rpb24gaXMgKipgbGVnZW5kYCoqIHdoaWNoIGV4cGxhaW5zIHRvIHRoZSByZWFkZXIgd2hhdCB0aGUgc3ltYm9scyB5b3VyIHBsb3QgdXNlcyBtZWFuLg0KDQpCZWZvcmUgd2UgY2xvc2UsIGxldCdzIHRlc3QgeW91ciBhYmlsaXR5IHRvIG1ha2UgYSBzb21ld2hhdCBjb21wbGljYXRlZCBzY2F0dGVycGxvdC4gRmlyc3QgcnVuIGBwbG90YCB3aXRoIDMgYXJndW1lbnRzLiBgYWlycXVhbGl0eSRXaW5kYCwgYGFpcnF1YWxpdHkkT3pvbmVgLCBhbmQgYHR5cGVgIHNldCBlcXVhbCB0byBgIm4iYC4gVGhpcyB0ZWxscyBSIHRvIHNldCB1cCB0aGUgcGxvdCBidXQgbm90IHRvIHB1dCB0aGUgZGF0YSBpbiBpdC4NCg0KYGBge3J9DQpwbG90KGFpcnF1YWxpdHkkV2luZCwgYWlycXVhbGl0eSRPem9uZSwgdHlwZSA9ICJuIikNCmBgYA0KDQpOb3cgZm9yIHRoZSB0ZXN0LiAoWW91IG1pZ2h0IG5lZWQgdG8gY2hlY2sgUidzIGRvY3VtZW50YXRpb24gZm9yIHNvbWUgb2YgdGhlc2UuKSBBZGQgYSBgdGl0bGVgIHdpdGggdGhlIGFyZ3VtZW50IGBtYWluYCBzZXQgZXF1YWwgdG8gdGhlIHN0cmluZyBgIldpbmQgYW5kIE96b25lIGluIE5ZQyJgDQoNCmBgYHtyfQ0KcGxvdChhaXJxdWFsaXR5JFdpbmQsIGFpcnF1YWxpdHkkT3pvbmUsIHR5cGUgPSAibiIpDQp0aXRsZShtYWluID0gIldpbmQgYW5kIE96b25lIGluIE5ZQyIpDQpgYGANCg0KTm93IGNyZWF0ZSBhIHZhcmlhYmxlIGNhbGxlZCBgbWF5YCBieSBzdWJzZXR0aW5nIGBhaXJxdWFsaXR5YCBhcHByb3ByaWF0ZWx5LiAoUmVjYWxsIHRoYXQgdGhlIGRhdGEgc3BlY2lmaWVzIG1vbnRocyBieSBudW1iZXIgYW5kIE1heSBpcyB0aGUgZmlmdGggbW9udGggb2YgdGhlIHllYXIuKQ0KDQpgYGB7cn0NCm1heSA8LSBzdWJzZXQoYWlycXVhbGl0eSwgTW9udGggPT0gNSkNCmBgYA0KDQpOb3cgdXNlIHRoZSBSIGNvbW1hbmQgYHBvaW50c2AgdG8gcGxvdCBNYXkncyB3aW5kIGFuZCBvem9uZSAoaW4gdGhhdCBvcmRlcikgYXMgc29saWQgYmx1ZSB0cmlhbmdsZXMuIFlvdSBoYXZlIHRvIHNldCB0aGUgY29sb3IgYW5kIHBsb3QgY2hhcmFjdGVyIHdpdGggdHdvIHNlcGFyYXRlIGFyZ3VtZW50cy4gTm90ZSB3ZSB1c2UgYHBvaW50c2AgYmVjYXVzZSB3ZSdyZSBhZGRpbmcgdG8gYW4gZXhpc3RpbmcgcGxvdC4NCg0KYGBge3J9DQpwbG90KGFpcnF1YWxpdHkkV2luZCwgYWlycXVhbGl0eSRPem9uZSwgdHlwZSA9ICJuIikNCnRpdGxlKG1haW4gPSAiV2luZCBhbmQgT3pvbmUgaW4gTllDIikNCnBvaW50cyhtYXkkV2luZCwgbWF5JE96b25lLCBjb2w9ImJsdWUiLHBjaD0xNykNCmBgYA0KDQpOb3cgY3JlYXRlIHRoZSB2YXJpYWJsZSBgbm90bWF5YCBieSBzdWJzZXR0aW5nIGBhaXJxdWFsaXR5YCBhcHByb3ByaWF0ZWx5Lg0KDQpgYGB7cn0NCm5vdG1heSA8LSBzdWJzZXQoYWlycXVhbGl0eSwgTW9udGggIT0gNSkNCmBgYA0KDQpOb3cgdXNlIHRoZSBSIGNvbW1hbmQgYHBvaW50c2AgdG8gcGxvdCB0aGVzZSBub3RtYXkncyB3aW5kIGFuZCBvem9uZSAoaW4gdGhhdCBvcmRlcikgYXMgcmVkIHNub3dmbGFrZXMuDQoNCmBgYHtyfQ0KcGxvdChhaXJxdWFsaXR5JFdpbmQsIGFpcnF1YWxpdHkkT3pvbmUsIHR5cGUgPSAibiIpDQp0aXRsZShtYWluID0gIldpbmQgYW5kIE96b25lIGluIE5ZQyIpDQpwb2ludHMobWF5JFdpbmQsIG1heSRPem9uZSwgY29sPSJibHVlIixwY2g9MTcpDQpub3RtYXkgPC0gc3Vic2V0KGFpcnF1YWxpdHksIE1vbnRoICE9IDUpDQpwb2ludHMobm90bWF5JFdpbmQsIG5vdG1heSRPem9uZSwgY29sID0gInJlZCIsIHBjaCA9IDgpDQpgYGANCg0KTm93IHdlJ2xsIHVzZSB0aGUgUiBjb21tYW5kIGBsZWdlbmRgIHRvIGNsYXJpZnkgdGhlIHBsb3QgYW5kIGV4cGxhaW4gd2hhdCBpdCBtZWFucy4gVGhlIGZ1bmN0aW9uIGhhcyBhIGxvdCBvZiBhcmd1bWVudHMsIGJ1dCB3ZSdsbCBvbmx5IHVzZSA0LiBUaGUgZmlyc3Qgd2lsbCBiZSB0aGUgc3RyaW5nIGAidG9wcmlnaHQiYCB0byB0ZWxsIFIgd2hlcmUgdG8gcHV0IHRoZSBsZWdlbmQuIFRoZSByZW1haW5pbmcgMyBhcmd1bWVudHMgd2lsbCBlYWNoIGJlIDItbG9uZyB2ZWN0b3JzIGNyZWF0ZWQgYnkgUidzIGNvbmNhdGVuYXRlIGZ1bmN0aW9uLCBlLmcuLCBjKCkuIFRoZXNlIGFyZ3VtZW50cyBhcmUgYHBjaGAsIGBjb2xgLCBhbmQgYGxlZ2VuZGAuIFRoZSBmaXJzdCBpcyB0aGUgdmVjdG9yIGAoMTcsOClgLCB0aGUgc2Vjb25kIGAoImJsdWUiLCJyZWQiKWAsIGFuZCB0aGUgdGhpcmQgYCgiTWF5IiwiT3RoZXIgTW9udGhzIilgLiBUcnkgaXQgbm93Lg0KDQpgYGB7cn0NCnBsb3QoYWlycXVhbGl0eSRXaW5kLCBhaXJxdWFsaXR5JE96b25lLCB0eXBlID0gIm4iKQ0KdGl0bGUobWFpbiA9ICJXaW5kIGFuZCBPem9uZSBpbiBOWUMiKQ0KcG9pbnRzKG1heSRXaW5kLCBtYXkkT3pvbmUsIGNvbD0iYmx1ZSIscGNoPTE3KQ0Kbm90bWF5IDwtIHN1YnNldChhaXJxdWFsaXR5LCBNb250aCAhPSA1KQ0KcG9pbnRzKG5vdG1heSRXaW5kLCBub3RtYXkkT3pvbmUsIGNvbCA9ICJyZWQiLCBwY2ggPSA4KQ0KbGVnZW5kKCJ0b3ByaWdodCIsIHBjaCA9IGMoMTcsIDgpLCBjb2wgPSBjKCJibHVlIiwgInJlZCIpLCBsZWdlbmQgPSBjKCJNYXkiLCAiT3RoZXIgTW9udGhzIikpDQpgYGANCg0KTm93IGFkZCBhIHZlcnRpY2FsIGxpbmUgYXQgdGhlIG1lZGlhbiBvZiBgYWlycXVhbGl0eSRXaW5kYC4gTWFrZSBpdCBkYXNoZWQgKGBsdHk9MmApIHdpdGggYSBgd2lkdGhgIG9mIGAyYC4NCg0KYGBge3J9DQpwbG90KGFpcnF1YWxpdHkkV2luZCwgYWlycXVhbGl0eSRPem9uZSwgdHlwZSA9ICJuIikNCnRpdGxlKG1haW4gPSAiV2luZCBhbmQgT3pvbmUgaW4gTllDIikNCnBvaW50cyhtYXkkV2luZCwgbWF5JE96b25lLCBjb2w9ImJsdWUiLHBjaD0xNykNCm5vdG1heSA8LSBzdWJzZXQoYWlycXVhbGl0eSwgTW9udGggIT0gNSkNCnBvaW50cyhub3RtYXkkV2luZCwgbm90bWF5JE96b25lLCBjb2wgPSAicmVkIiwgcGNoID0gOCkNCmxlZ2VuZCgidG9wcmlnaHQiLCBwY2ggPSBjKDE3LCA4KSwgY29sID0gYygiYmx1ZSIsICJyZWQiKSwgbGVnZW5kID0gYygiTWF5IiwgIk90aGVyIE1vbnRocyIpKQ0KYWJsaW5lKHYgPSBtZWRpYW4oYWlycXVhbGl0eSRXaW5kKSwgbHR5PTIsIGx3ZD0yKQ0KYGBgDQoNClVzZSBgcGFyYCB3aXRoIHRoZSBwYXJhbWV0ZXIgYG1mcm93YCBzZXQgZXF1YWwgdG8gdGhlIHZlY3RvciBgKDEsMilgIHRvIHNldCB1cCB0aGUgcGxvdCB3aW5kb3cgZm9yIHR3byBwbG90cyBzaWRlIGJ5IHNpZGUuIFlvdSB3b24ndCBzZWUgYSByZXN1bHQuDQoNCmBgYHtyfQ0KcGFyKG1mcm93ID0gYygxLCAyKSkNCmBgYA0KDQpOb3cgcGxvdCBgYWlycXVhbGl0eSRXaW5kYCBhbmQgYGFpcnF1YWxpdHkkT3pvbmVgIGFuZCB1c2UgYG1haW5gIHRvIHNwZWNpZnkgdGhlIHRpdGxlIGAiT3pvbmUgYW5kIFdpbmQiYC4NCg0KYGBge3J9DQpwYXIobWZyb3cgPSBjKDEsIDIpKQ0KcGxvdChhaXJxdWFsaXR5JFdpbmQsIGFpcnF1YWxpdHkkT3pvbmUsIG1haW4gPSAiT3pvbmUgYW5kIFdpbmQiKQ0KYGBgDQoNCk5vdyBmb3IgdGhlIHNlY29uZCBwbG90Lg0KDQpQbG90IGBhaXJxdWFsaXR5JE96b25lYCBhbmQgYGFpcnF1YWxpdHkkU29sYXIuUmAgYW5kIHVzZSBgbWFpbmAgdG8gc3BlY2lmeSB0aGUgdGl0bGUgYCJPem9uZSBhbmQgU29sYXIgUmFkaWF0aW9uImAuDQoNCmBgYHtyfQ0KcGFyKG1mcm93ID0gYygxLCAyKSkNCnBsb3QoYWlycXVhbGl0eSRXaW5kLCBhaXJxdWFsaXR5JE96b25lLCBtYWluID0gIk96b25lIGFuZCBXaW5kIikNCnBsb3QoYWlycXVhbGl0eSRPem9uZSwgYWlycXVhbGl0eSRTb2xhci5SLCBtYWluID0gIk96b25lIGFuZCBTb2xhciBSYWRpYXRpb24iKQ0KYGBgDQoNCjwvYnI+DQoNCk5vdyBmb3Igc29tZXRoaW5nIG1vcmUgY2hhbGxlbmdpbmcuDQoNClRoaXMgb25lIHdpdGggMyBwbG90cywgdG8gaWxsdXN0cmF0ZSBpbm5lciBhbmQgb3V0ZXIgbWFyZ2lucy4gRmlyc3QsIHNldCB1cCB0aGUgcGxvdCB3aW5kb3cgYnkgdHlwaW5nIGBwYXIobWZyb3cgPSBjKDEsIDMpLCBtYXIgPSBjKDQsIDQsIDIsIDEpLCBvbWEgPSBjKDAsIDAsIDIsIDApKWANCg0KYGBge3J9DQpwYXIobWZyb3cgPSBjKDEsIDMpLCBtYXIgPSBjKDQsIDQsIDIsIDEpLCBvbWEgPSBjKDAsIDAsIDIsIDApKQ0KYGBgDQoNCk1hcmdpbnMgYXJlIHNwZWNpZmllZCBhcyA0LWxvbmcgdmVjdG9ycyBvZiBpbnRlZ2Vycy4gRWFjaCBudW1iZXIgdGVsbHMgaG93IG1hbnkgbGluZXMgb2YgdGV4dCB0byBsZWF2ZSBhdCBlYWNoIHNpZGUuIFRoZSBudW1iZXJzIGFyZSBhc3NpZ25lZCBjbG9ja3dpc2Ugc3RhcnRpbmcgYXQgdGhlIGJvdHRvbS4gVGhlIGRlZmF1bHQgZm9yIHRoZSBpbm5lciBtYXJnaW4gaXMgYyg1LjEsIDQuMSwgNC4xLCAyLjEpIHNvIHlvdSBjYW4gc2VlIHdlIHJlZHVjZWQgZWFjaCBvZiB0aGVzZSBzbyB3ZSdsbCBoYXZlIHJvb20gZm9yIHNvbWUgb3V0ZXIgdGV4dC4NCg0KVGhlIGZpcnN0IHBsb3Qgc2hvdWxkIGJlIGZhbWlsaWFyLiBQbG90IGBhaXJxdWFsaXR5JFdpbmRgIGFuZCBgYWlycXVhbGl0eSRPem9uZWAgd2l0aCB0aGUgdGl0bGUgKGFyZ3VtZW50IGBtYWluYCkgYXMgYCJPem9uZSBhbmQgV2luZCJgLg0KDQpgYGB7cn0NCnBhcihtZnJvdyA9IGMoMSwgMyksIG1hciA9IGMoNCwgNCwgMiwgMSksIG9tYSA9IGMoMCwgMCwgMiwgMCkpDQpwbG90KGFpcnF1YWxpdHkkV2luZCwgYWlycXVhbGl0eSRPem9uZSwgbWFpbiA9ICJPem9uZSBhbmQgV2luZCIpDQpgYGANCg0KVGhlIHNlY29uZCBwbG90IGlzIHNpbWlsYXIuDQoNClBsb3QgYGFpcnF1YWxpdHkkU29sYXIuUmAgYW5kIGBhaXJxdWFsaXR5JE96b25lYCB3aXRoIHRoZSB0aXRsZSAoYXJndW1lbnQgYG1haW5gKSBhcyBgIk96b25lIGFuZCBTb2xhciBSYWRpYXRpb24iYC4NCg0KYGBge3J9DQpwYXIobWZyb3cgPSBjKDEsIDMpLCBtYXIgPSBjKDQsIDQsIDIsIDEpLCBvbWEgPSBjKDAsIDAsIDIsIDApKQ0KcGxvdChhaXJxdWFsaXR5JFdpbmQsIGFpcnF1YWxpdHkkT3pvbmUsIG1haW4gPSAiT3pvbmUgYW5kIFdpbmQiKQ0KcGxvdChhaXJxdWFsaXR5JFNvbGFyLlIsIGFpcnF1YWxpdHkkT3pvbmUsIG1haW4gPSAiT3pvbmUgYW5kIFNvbGFyIFJhZGlhdGlvbiIpDQpgYGANCg0KTm93IGZvciB0aGUgZmluYWwgcGFuZWwuDQoNClBsb3QgYGFpcnF1YWxpdHkkVGVtcGAgYW5kIGBhaXJxdWFsaXR5JE96b25lYCB3aXRoIHRoZSB0aXRsZSAoYXJndW1lbnQgYG1haW5gKSBhcyBgIk96b25lIGFuZCBUZW1wZXJhdHVyZSJgLg0KDQpgYGB7cn0NCnBhcihtZnJvdyA9IGMoMSwgMyksIG1hciA9IGMoNCwgNCwgMiwgMSksIG9tYSA9IGMoMCwgMCwgMiwgMCkpDQpwbG90KGFpcnF1YWxpdHkkV2luZCwgYWlycXVhbGl0eSRPem9uZSwgbWFpbiA9ICJPem9uZSBhbmQgV2luZCIpDQpwbG90KGFpcnF1YWxpdHkkU29sYXIuUiwgYWlycXVhbGl0eSRPem9uZSwgbWFpbiA9ICJPem9uZSBhbmQgU29sYXIgUmFkaWF0aW9uIikNCnBsb3QoYWlycXVhbGl0eSRUZW1wLCBhaXJxdWFsaXR5JE96b25lLCBtYWluID0gIk96b25lIGFuZCBUZW1wZXJhdHVyZSIpDQpgYGANCg0KTm93IHdlJ2xsIHB1dCBpbiBhIHRpdGxlLg0KDQpTaW5jZSB0aGlzIGlzIHRoZSBtYWluIHRpdGxlLCB3ZSBzcGVjaWZ5IGl0IHdpdGggdGhlIFIgY29tbWFuZCBgbXRleHRgLiBDYWxsIGBtdGV4dGAgd2l0aCB0aGUgc3RyaW5nIGAiT3pvbmUgYW5kIFdlYXRoZXIgaW4gTmV3IFlvcmsgQ2l0eSJgIGFuZCB0aGUgYXJndW1lbnQgYG91dGVyYCBzZXQgZXF1YWwgdG8gYFRSVUVgLg0KDQpgYGB7cn0NCnBhcihtZnJvdyA9IGMoMSwgMyksIG1hciA9IGMoNCwgNCwgMiwgMSksIG9tYSA9IGMoMCwgMCwgMiwgMCkpDQpwbG90KGFpcnF1YWxpdHkkV2luZCwgYWlycXVhbGl0eSRPem9uZSwgbWFpbiA9ICJPem9uZSBhbmQgV2luZCIpDQpwbG90KGFpcnF1YWxpdHkkU29sYXIuUiwgYWlycXVhbGl0eSRPem9uZSwgbWFpbiA9ICJPem9uZSBhbmQgU29sYXIgUmFkaWF0aW9uIikNCnBsb3QoYWlycXVhbGl0eSRUZW1wLCBhaXJxdWFsaXR5JE96b25lLCBtYWluID0gIk96b25lIGFuZCBUZW1wZXJhdHVyZSIpDQptdGV4dCgiT3pvbmUgYW5kIFdlYXRoZXIgaW4gTmV3IFlvcmsgQ2l0eSIsIG91dGVyID0gVFJVRSkNCmBgYA0KDQpWb2lsYSEgQmVhdXRpZnVsLCByaWdodD8NCg0KQ29uZ3JhdHMhIFlvdSd2ZSB3ZWF0aGVyZWQgdGhpcyBsZXNzb24gbmljZWx5IGFuZCBwYXNzZWQgb3V0IG9mIHRoZSBObyF6b25lLg0KDQoNCjwvYnI+DQoNCi0tLQ0KDQo8L2JyPg0KDQo8Y2VudGVyPkVORDwvY2VudGVyPg0KDQo8L2JyPg0KDQotLS0=