1 Introduction

The two core plotting and graphics packages in the base R are:

  • graphics: contains plotting functions for the “base” graphing systems, including plot, hist, boxplot, and many others.

  • grDevices: contains all the code implementing the various graphics devices, including X11, PDF, PostScript, PNG, etc.

The grDevices package contains the functionality for sending plots to various output devices. The graphics package contains the code for actually constructing and annotating plots.

In this note, we focus on using the base plotting system to create graphics on the screen device.

2 Working Data -iris

Objectives: Identify meaningful patterns and make visual representations

  • What information does the data set have?
  • How to find the information of interest?
    • descriptive?
    • algorithm-based?
    • model-based?
  • Too many variables or too many data points?
  • What form of visualization to use?
  • What visual design? Single pattern or multiple patterns?

3 Base Graphics

Base graphics are used most commonly and are a very powerful system for creating data graphics. There are two phases to creating a base plot:

  • Initializing a new plot.

  • Annotating (adding to) an existing plot.

For example, calling the base plot functions plot(x, y) or hist(x) will launch a graphics device (if one is not already open) and draw a new plot on the device. The based plot function has many arguments, letting us set the title, x-axis label, y-axis label, etc.

The base graphics system has many global parameters that can be set and tweaked. These parameters are documented in ?par and are used to control the global behavior of plots, such as the margins, axis orientation, and other details.

4 Simple Base Graphics

This section explains how to use the base plotting functions to make basic statistical graphics.

4.1 Histogram

Here is an example of a simple histogram made using the hist() function in the graphics package. If we run this code and our graphics window is not already open, it should open once you call the hist() function.

## Draw a new plot on the screen device
hist(iris$Sepal.Length,
     xlab = "Sepal Length",
     ylab = "Counts",
     main = "Frequency distribution of sepal length") 
Figure 1. Sepal width of iris

Figure 1. Sepal width of iris

4.2 Boxplot

Boxplots can be made in R using the boxplot() function, which takes as its first argument a formula. The formula has a form of y-axis ~ x-axis. Anytime you see a ~ in R, it’s a formula. Here, we are plotting the sepal length of iris flowers and the right-hand side of ~ the Species.

boxplot(Sepal.Length ~ Species, data = iris, xlab = "Species", ylab = "Sepal Length")
Box plot of sepal length by species

Box plot of sepal length by species

Each boxplot shows the median, 25th, and 75th percentiles of the data (the “box”), as well as +/- 1.5 times the interquartile range (IQR) of the data (the “whiskers”). Any data points beyond 1.5 times the IQR of the data are indicated separately with circles.

We can see that Virginica has an outlier.

4.3 Scatterplot

Here is a simple scatter-plot made with the plot() function.

plot(iris$Sepal.Length, iris$Sepal.Width, 
     xlab = "sepal length",
     ylab = "sepal width",
     main = " ")
Scatter plot of sepal length and sepal width

Scatter plot of sepal length and sepal width

Generally, the plot() function takes two vectors of numbers: one for the x-axis coordinates and one for the y-axis coordinates. However, plot() is a generic function in R, which means its behavior can change depending on what kinds of data are passed to the function.

One thing to note here is that we have provided labels for the x- and the y-axis and title as well. If they were not specified, the plot function will provide this information automatically using the names of the variables.

5 Some Important Base Graphics Parameters

Many base plotting functions share a set of global parameters. Here are a few key ones.

  • pch: the plotting symbol (default is an open circle).
  • lty: the line type (default is a solid line), can be dashed, dotted, etc.
  • lwd: the line width, specified as an integer multiple.
  • col: the plotting color, specified as a number, string, or hex code; the colors() function gives you a vector of colors by name.
  • xlab: character string for the x-axis label.
  • ylab: character string for the y-axis label.

The par() function is used to specify the global graphics parameters that affect all plots in an R session. These parameters can be overridden when they are specified as arguments to specific plotting functions.

  • las: the orientation of the axis labels on the plot.
  • bg: the background color.
  • mar: the margin size.
  • oma: the outer margin size (default is 0 for all sides).
  • mfrow: number of plots per row, column (plots are filled row-wise).
  • mfcol: number of plots per row, column (plots are filled column-wise).

We can see the default values for global graphics parameters by calling the par() function and passing the name of the parameter in quotes.

par("lty")
[1] "solid"
par("col")
[1] "black"
par("pch")
[1] 1

Here are some more default values for global graphics parameters.

par("bg")
[1] "white"
par("mar")
[1] 5.1 4.1 4.1 2.1
par("mfrow")
[1] 1 1

For the most part, we usually don’t have to modify these when making quick plots. However, we might need to tweak them for finalizing finished plots.

6 Base Plotting Functions

The most basic base plotting function is plot(). The plot() function makes a scatter-plot, or other types of plot depending on the class of the object being plotted. Calling plot() will draw a plot on the screen device (and open the screen device if not already open). After that, annotation functions can be called to add to the already-made plot.

Some key annotation functions are

  • lines(): add lines to a plot, given a vector of x values and a corresponding vector of y values (or a 2-column matrix); this function just connects the dots
  • points(): add points to a plot
  • text(): add text labels to a plot using specified x, y coordinates
  • title(): add annotations to x, y-axis labels, title, subtitle, outer margin
  • mtext(): add arbitrary text to the margins (inner or outer) of the plot
  • axis(): adding axis ticks/labels

Here’s an example of creating a base plot and adding some annotation. First we make the plot with the plot() function and then add a title to the top of the plot with the title() function.

## Make the initial plot
plot(iris$Sepal.Length, iris$Sepal.Width)
## Add a title
title(main = "Sepal length and width of iris")  
Base plot with annotation: Iris

Base plot with annotation: Iris

Here, I start with the same plot as above (although I add the title right away using the main argument to plot()) and then annotate it by coloring blue the data points corresponding.

sepal.length = iris$Sepal.Length
sepal.width = iris$Sepal.Width
species = iris$Species
## identifying the ID of Virginica
virginca.id = which(species=="virginica")  # value are case sensitive!
## making scatter plot
plot(sepal.length, sepal.width, main = "Sepal legnth vs sepal width")
##
points(sepal.length[virginca.id], sepal.width[virginca.id], pch = 19, col = "red")

The following plot colors the data points with different colors based on species. legend() function explains the meaning of the different colors in the plot.

sepal.length = iris$Sepal.Length
sepal.width = iris$Sepal.Width
species = iris$Species
## identifying the ID of Virginica
virginca.id = which(species=="virginica")  # value are case sensitive!
setosa.id = which(species=="setosa")
versicolor.id = which(species=="versicolor")
## making an empty plot: type = "n" ==> no point
plot(sepal.length, sepal.width, main = "Sepal legnth vs sepal width", type = "n")
##
points(sepal.length[virginca.id], sepal.width[virginca.id], pch = 18, col = "red")
points(sepal.length[setosa.id], sepal.width[setosa.id], pch = 19, col = "blue")
points(sepal.length[versicolor.id], sepal.width[versicolor.id], pch = 20, col = "cyan")
legend("topleft", c("virginica", "setosa", "versicolor"), 
                  col=c("red", "blue", "cyan"),
                  pch=c(18, 19, 20))

7 Base Plot with Regression Line

It’s fairly common to make a scatterplot and then want to draw a simple linear regression line through the data. This can be done with the abline() function.

Below, we first make the plot (as above). Then we fit a simple linear regression model using the lm() function. Here, we try to model Ozone as a function of Wind. Then we take the output of lm() and pass it to the abline() function which automatically takes the information from the model object and calculates the corresponding regression line.

Note that in the call to plot() below, we set pch = 20 to change the plotting symbol to a filled circle.

sepal.length = iris$Sepal.Length
sepal.width = iris$Sepal.Width
## making a plot 
plot(sepal.length, sepal.width, main = "Sepal legnth vs sepal width", pch = 21)
##
 model <- lm( sepal.width ~ sepal.length)
 ## Draw regression line on plot
 abline(model, lwd = 2, col = "red")

8 Multiple Base Plots

Making multiple plots side by side is a useful way to visualize many relationships between variables with static 2-D plots. Often the repetition of data across a single plot window can be a useful way to identify patterns in the data. In order to do this, the mfrow and mfcol parameters set by the par() function are critical.

Both the mfrow and mfcol parameters take two numbers: the number of rows of plots followed by the number of columns. The multiple plots will be arranged in a matrix-like pattern. The only difference between the two parameters is that if mfrow is set, then the plots will be drawn row-wise; if mfcol is set, the plots will be drawn column-wise.

In the example below, we make two plots: sepal length vs sepal width and petal length vs petal width. We set par(mfrow = c(1, 2)), which indicates that we have one row of plots and two columns of plots.

par(mfrow = c(1, 2))
sepal.length = iris$Sepal.Length
sepal.width = iris$Sepal.Width
petal.length = iris$Petal.Length
petal.width = iris$Petal.Width
## making a plot 
plot(sepal.length, sepal.width, main = "Sepal legnth vs sepal width", pch = 20)
plot(petal.length, petal.width, main = "Petal legnth vs petal width", pch = 21)

The example below creates three plots in a row by setting par(mfrow = c(1, 3)). Here we also change the plot margins with the mar parameter. The various margin parameters, like mar, are specified by setting a value for each side of the plot. Side 1 is the bottom of the plot, side 2 is the left-hand side, side 3 is the top, and side 4 is the right-hand side.

In the example below we also modify the outer margin via the oma parameter to create a little more space for the plots and to place them closer together.

# layout of the plot
par(mfrow = c(1, 3), 
    mar = c(4, 4, 2, 1), 
    oma = c(0, 0, 2, 0))
# extract variables from the data frame
sepal.length = iris$Sepal.Length
sepal.width = iris$Sepal.Width
species = iris$Species
## identifying the ID of Virginica
virginca.id = which(species=="virginica")  # value are case sensitive!
setosa.id = which(species=="setosa")
versicolor.id = which(species=="versicolor")
## making three plots
plot(sepal.length[virginca.id], sepal.width[virginca.id], main = "virginca")
plot(sepal.length[setosa.id], sepal.width[setosa.id], main = "setosa")
plot(sepal.length[versicolor.id], sepal.width[versicolor.id], main = "versicolor")
##
mtext("Sepal length vs width: Iris", outer = TRUE)

In the above example, the mtext() function was used to create an overall title for the panel of plots. Hence, each individual plot has a title, while the overall set of plots also has a summary title. The mtext() function is important for adding text annotations that aren’t specific to a single plot.

Notice that the scales of the vertical axes of the three plots are not the same. To misleading visual comparison, we should make the scales of both axes the same.

# layout of the plot
par(mfrow = c(1, 3), 
    mar = c(4, 4, 2, 1), 
    oma = c(0, 0, 2, 0))
# extract variables from the data frame
sepal.length = iris$Sepal.Length
sepal.width = iris$Sepal.Width
species = iris$Species
## identifying the ID of Virginica
virginca.id = which(species=="virginica")  # value are case sensitive!
setosa.id = which(species=="setosa")
versicolor.id = which(species=="versicolor")
## making three plots
plot(sepal.length[virginca.id], sepal.width[virginca.id], xlim=c(4,8), ylim=c(2, 4.5), main = "virginca")
plot(sepal.length[setosa.id], sepal.width[setosa.id], xlim=c(4,8), ylim=c(2, 4.5),main = "setosa")
plot(sepal.length[versicolor.id], sepal.width[versicolor.id], xlim=c(4,8), ylim=c(2, 4.5),main = "versicolor")
##
mtext("Sepal length vs width: Iris", outer = TRUE)

We now can visualize the mean sepal width and sepal length among the species.

9 Controlling Point Size and Transparency

we use cex= and alpha= to control the point size according to the value of a variable and the level of transparency of the point.

sepal.length = iris$Sepal.Length
sepal.width = iris$Sepal.Width
size.petal = iris$Petal.Width
species = iris$Species
## identifying the ID of Virginica
virginca.id = which(species=="virginica")  # value are case sensitive!
setosa.id = which(species=="setosa")
versicolor.id = which(species=="versicolor")
## color code
col.code = c(alpha("red",0.5),alpha("blue",0.5),alpha("cyan",0.5))
## making an empty plot: type = "n" ==> no point
plot(sepal.length, sepal.width, main = "Sepal legnth vs sepal width", type = "n")
## change the point size based on their average of sepal length and width
points(sepal.length[virginca.id], sepal.width[virginca.id], 
       pch = 19, col = col.code[1], cex = size.petal[virginca.id])
points(sepal.length[setosa.id], sepal.width[setosa.id], 
       pch = 19, col = col.code[2], cex = size.petal[setosa.id])
points(sepal.length[versicolor.id], sepal.width[versicolor.id], 
       pch = 19, col = col.code[3], cex = size.petal[versicolor.id])
legend("topleft", c("virginica", "setosa", "versicolor"), 
                  col=col.code,
                  pch=c(19, 19, 19))

10 Annotations

We add annotations to the base R plot. The annotations could be plain texts, images, and mathematical expressions.

iris.img <- "https://pengdsci.github.io/STA553VIZ/w03/img/iris.jpeg"
my.iris <- readJPEG(getURLContent(iris.img))
raster.iris <- as.raster(my.iris) 
# Use the code in the precious section
sepal.length = iris$Sepal.Length
sepal.width = iris$Sepal.Width
size.petal = iris$Petal.Width
species = iris$Species
## identifying the ID of Virginica
virginca.id = which(species=="virginica")  # value are case sensitive!
setosa.id = which(species=="setosa")
versicolor.id = which(species=="versicolor")
## color code
#  col.code = c(alpha("red",0.5),alpha("blue",0.5),alpha("cyan",0.5))
## making an empty plot: type = "n" ==> no point
plot(sepal.length, sepal.width, main = "Sepal legnth vs sepal width", type = "n")
## change the point size based on their average of sepal length and width
points(sepal.length[virginca.id], sepal.width[virginca.id], 
       pch = 19, col = "purple", cex = size.petal[virginca.id], alpha = 0.6)
points(sepal.length[setosa.id], sepal.width[setosa.id], 
       pch = 19, col = "navy", cex = size.petal[setosa.id], alpha = 0.6)
points(sepal.length[versicolor.id], sepal.width[versicolor.id], 
       pch = 19, col = "cyan", cex = size.petal[versicolor.id], alpha = 0.6)
legend("topleft", c("virginica", "setosa", "versicolor"), 
                  col=c("purple", "navy", "cyan"),
                  pch=c(19, 19, 19))
## various annotations
#specify the position of the image through bottom-left and top-right coords
rasterImage(raster.iris,6,3.5,7,4.4)
text(7.25, 2.25, "The size is proportional to petal length", col = "purple", cex = 0.5)

11 What Story To Tell?

We have discussed various graphic functions and techniques in base R to create graphic representations of data. To conclude this note, let’s look at what information general audience might be interested in.

11.1 Relationship between variables?

pairs(iris[, -5])
Pair-wise Scatter Plot of Sizes of Iris

Pair-wise Scatter Plot of Sizes of Iris

11.2 Distribution of variables?

## Data Partition
Sepal.L.set = iris$Sepal.Length[which(iris$Species=="setosa")]
Sepal.L.ver = iris$Sepal.Length[which(iris$Species=="versicolor")]
Sepal.L.vir = iris$Sepal.Length[which(iris$Species=="virginica")]
#
Sepal.W.set = iris$Sepal.Width[which(iris$Species=="setosa")]
Sepal.W.ver = iris$Sepal.Width[which(iris$Species=="versicolor")]
Sepal.W.vir = iris$Sepal.Width[which(iris$Species=="virginica")]
#
Petal.L.set = iris$Petal.Length[which(iris$Species=="setosa")]
Petal.L.ver = iris$Petal.Length[which(iris$Species=="versicolor")]
Petal.L.vir = iris$Petal.Length[which(iris$Species=="virginica")]
#
Petal.W.set = iris$Petal.Width[which(iris$Species=="setosa")]
Petal.W.ver = iris$Petal.Width[which(iris$Species=="versicolor")]
Petal.W.vir = iris$Petal.Width[which(iris$Species=="virginica")]
###
par(mfrow=c(2,2))
###
##########################   plot #1: 
plot(density(Sepal.L.set), col="brown4", lty=1, lwd=2,  xlim=c(3,9), ylim=c(0,1.8), xlab="Sepal Length", main="Distributions Sepal Length")
lines(density(Sepal.L.ver), col="blue", lty=1, lwd=2)
lines(density(Sepal.L.vir), col="darkcyan", lty=1, lwd=2)
legend("topright", c("setosa", "versicolor","virginica"),
       col=c("brown4","blue","darkcyan"), lty=c(1,1,1),
       lwd=c(1,1,1), bty="n", cex=0.7)
##
##########################   plot #1: 
plot(density(Sepal.W.set), col="brown4", lty=1, lwd=2,  xlim=c(1,5), ylim=c(0,2), xlab="Sepal Width", main="Distributions Sepal Width")
lines(density(Sepal.W.ver), col="blue", lty=1, lwd=2)
lines(density(Sepal.W.vir), col="darkcyan", lty=1, lwd=2)
legend("topright", c("setosa", "versicolor","virginica"),
       col=c("brown4","blue","darkcyan"), lty=c(1,1,1),
       lwd=c(1,1,1), bty="n", cex=0.7)
##
##########################   plot #1: 
plot(density(Petal.L.set), col="brown4", lty=1, lwd=2,  xlim=c(1,8), ylim=c(0,2.8), xlab="Petal Length", main="Distributions Petal Length")
lines(density(Petal.L.ver), col="blue", lty=1, lwd=2)
lines(density(Petal.L.vir), col="darkcyan", lty=1, lwd=2)
legend("topright", c("setosa", "versicolor","virginica"),
       col=c("brown4","blue","darkcyan"), lty=c(1,1,1),
       lwd=c(1,1,1), bty="n", cex=0.7)
##
##########################   plot #1: 
plot(density(Petal.W.set), col="brown4", lty=1, lwd=2,  xlim=c(0,3), ylim=c(0,8), xlab="Petal Width", main="Distributions Petal Width")
lines(density(Petal.W.ver), col="blue", lty=1, lwd=2)
lines(density(Petal.W.vir), col="darkcyan", lty=1, lwd=2)
legend("topright", c("setosa", "versicolor","virginica"),
       col=c("brown4","blue","darkcyan"), lty=c(1,1,1),
       lwd=c(1,1,1), bty="n", cex=0.7)
Comparisons of distributions

Comparisons of distributions

11.3 Modeling - Classification and Regression?

When building regression models, one of the issues is the potential collinearity.

\(\pi_1 = Pr[Y = \text{setosa}]\), \(\pi_2 = Pr[Y = \text{versicolor}]\), and \(\pi_1 = Pr[Y = \text{virginica}]\). The well known baseline logit model is defined to be the following system of two odds regression models

\[ \log(\pi_2/ \pi_1) = \alpha_0 + \alpha_1x_1 + \cdots + \alpha_kx_k \] \[ \log(\pi_3/ \pi_1) = \beta_0 + \beta_1x_1 + \cdots + \beta_kx_k \] Note that \(\pi_1 + \pi_2 + \pi_3 = 1\). Solve for \(\pi_1, \pi_2\), and \(\pi_3\), we have

\[ \pi_1 = \frac{1}{1+\exp(\alpha_0 + \alpha_1x_1 + \cdots + \alpha_kx_k) + \exp(\beta_0 + \beta_1x_1 + \cdots + \beta_kx_k)} \]

\[ \pi_2 = \frac{\exp(\alpha_0 + \alpha_1x_1 + \cdots + \alpha_kx_k)}{1+\exp(\alpha_0 + \alpha_1x_1 + \cdots + \alpha_kx_k) + \exp(\beta_0 + \beta_1x_1 + \cdots + \beta_kx_k)} \]

\[ \pi_3 = \frac{\exp(\beta_0 + \beta_1x_1 + \cdots + \beta_kx_k)}{1+\exp(\alpha_0 + \alpha_1x_1 + \cdots + \alpha_kx_k) + \exp(\beta_0 + \beta_1x_1 + \cdots + \beta_kx_k)} \]

(Intercept) Sepal.Length Sepal.Width Petal.Length Petal.Width
versicolor 18.69037 -5.458424 -8.707401 14.24477 -3.097684
virginica -23.83628 -7.923634 -15.370769 23.65978 15.135300

How to interpret the results? Can we make a visual representation of the resulting model? How?

The following is one example of representation of how individual features impact membership prediction (classification).

setosa = summary(iris[which(iris$Species=="setosa"),])
versicolor = summary(iris[which(iris$Species=="versicolor"),])
virginica = summary(iris[which(iris$Species=="virginica"),])
list(setosa = setosa, versicolor = versicolor, virginica = virginica)
$setosa
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width   
 Min.   :4.300   Min.   :2.300   Min.   :1.000   Min.   :0.100  
 1st Qu.:4.800   1st Qu.:3.200   1st Qu.:1.400   1st Qu.:0.200  
 Median :5.000   Median :3.400   Median :1.500   Median :0.200  
 Mean   :5.006   Mean   :3.428   Mean   :1.462   Mean   :0.246  
 3rd Qu.:5.200   3rd Qu.:3.675   3rd Qu.:1.575   3rd Qu.:0.300  
 Max.   :5.800   Max.   :4.400   Max.   :1.900   Max.   :0.600  
       Species  
 setosa    :50  
 versicolor: 0  
 virginica : 0  
                
                
                

$versicolor
  Sepal.Length    Sepal.Width     Petal.Length   Petal.Width          Species  
 Min.   :4.900   Min.   :2.000   Min.   :3.00   Min.   :1.000   setosa    : 0  
 1st Qu.:5.600   1st Qu.:2.525   1st Qu.:4.00   1st Qu.:1.200   versicolor:50  
 Median :5.900   Median :2.800   Median :4.35   Median :1.300   virginica : 0  
 Mean   :5.936   Mean   :2.770   Mean   :4.26   Mean   :1.326                  
 3rd Qu.:6.300   3rd Qu.:3.000   3rd Qu.:4.60   3rd Qu.:1.500                  
 Max.   :7.000   Max.   :3.400   Max.   :5.10   Max.   :1.800                  

$virginica
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width   
 Min.   :4.900   Min.   :2.200   Min.   :4.500   Min.   :1.400  
 1st Qu.:6.225   1st Qu.:2.800   1st Qu.:5.100   1st Qu.:1.800  
 Median :6.500   Median :3.000   Median :5.550   Median :2.000  
 Mean   :6.588   Mean   :2.974   Mean   :5.552   Mean   :2.026  
 3rd Qu.:6.900   3rd Qu.:3.175   3rd Qu.:5.875   3rd Qu.:2.300  
 Max.   :7.900   Max.   :3.800   Max.   :6.900   Max.   :2.500  
       Species  
 setosa    : 0  
 versicolor: 0  
 virginica :50  
                
                
                

Next, we show the relationship between Sepal.Length and the classification probabilities \(\pi_1, \pi_2\) and \(\pi_3\) by fixing the values of other variables at their corresponding means.

SepalLength = seq(4, 9, length=50)
pi01 = 1/(1+exp(18.69 - 5.46*SepalLength - 8.71*3 + 14.25*4 - 3.10*1)+ exp(-23.84  - 7.92**SepalLength   - 15.37*3 + 23.66*4  + 15.14*1 ))
##
pi02 = exp(18.69 - 5.46*SepalLength - 8.71*3 + 14.25*4 - 3.10*1 )/(1+exp(18.69 - 5.46*SepalLength - 8.71*3 + 14.25*4 - 3.10*1 )+ exp(-23.84 - 7.92**SepalLength  - 15.37*3 +23.66*4 + 15.14*1  ))
##
##
pi03 = exp(-23.84  - 7.92**SepalLength - 15.37*3 +23.66*4 + 15.14*1 )/(1+exp(18.69 - 5.46*SepalLength - 8.71*3 + 14.25*4 - 3.10*1)+ exp(-23.84  - 7.92**SepalLength - 15.37*3 +23.66*4 + 15.14*1 ))
##
#par(mfrow=c(1,3))
plot(SepalLength, pi01, main="Setosa",
     type = "l",
     ylim = c(0,1),
     xlab = "Sepal Length",
     ylab = "Classification Probability",
     col = "blue",
     lwd = 2)
lines(SepalLength, pi02, main = "Versicolor", ylab="Classification Probability",
     type = "l", col = "purple", lwd = 2)
lines(SepalLength, pi03, main = "Virginica",  ylab="Classification Probability",
     type="l", col = "brown4", lwd = 2)
abline(v=8.51, lty = 2)
points(8.51, 0.495, pch=19, col = "darkred", dex = 1.5)
text(8, 0.495, "(8.51, 0.495)", cex = 0.6)
legend("left", c("Setosa", "Versicolor", "Virginica"), lwd=rep(2,3),
       col=c("blue", "purple", "brown"), lty = rep(1,3), cex=0.9, bty="n")

The above three figures show how sepal length is associated with the three classification probabilities. For example, among setosa, under the same conditions (i.e., sepal width, petal length, and petal width are the same), as sepal length increases, the classification probability also increases. The opposite pattern is observed among versicolor iris flowers. The sepal length and the classification probability are not associated.

For all iris flowers with Sepal Width 3, Petal Length 4, and Petal Width 1.5, when Sepal Length < 8.51, \(P(\text{Versicolor}) > P(\text{Setosa})\) meaning that it is more likely to be Versicolor iris. Otherwise, it is more likely to be setosa. Sepal length does not have predictive power for virginica.

We can also look at the relationship between the classification probability and other numerical variables in the data set in a similar way.

11.4 Clustering?

Another interesting problem we may explore is the see whether there are some clusters based on the four numerical variables. There are many clustering algorithms for practitioners. For illustrative purposes, we use the popular k-means algorithm in the following example.

tot.withinss <- vector(mode="character", length=10)
for (i in 1:10){
  irisCluster <- kmeans(iris[,1:4], center=i, nstart=20)
  tot.withinss[i] <- irisCluster$tot.withinss
}
plot(1:10, tot.withinss, main="Elbow Plot for Optimal Cluster Determination",
     type="b", pch=19)

irisCluster <- kmeans(iris[,1:4], center=3, nstart=20)
irisCluster
K-means clustering with 3 clusters of sizes 62, 38, 50

Cluster means:
  Sepal.Length Sepal.Width Petal.Length Petal.Width
1     5.901613    2.748387     4.393548    1.433871
2     6.850000    3.073684     5.742105    2.071053
3     5.006000    3.428000     1.462000    0.246000

Clustering vector:
  [1] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
 [38] 3 3 3 3 3 3 3 3 3 3 3 3 3 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [75] 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 2 2 2 2 1 2 2 2 2
[112] 2 2 1 1 2 2 2 2 1 2 1 2 1 2 2 1 1 2 2 2 2 2 1 2 2 2 2 1 2 2 2 1 2 2 2 1 2
[149] 2 1

Within cluster sum of squares by cluster:
[1] 39.82097 23.87947 15.15100
 (between_SS / total_SS =  88.4 %)

Available components:

[1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
[6] "betweenss"    "size"         "iter"         "ifault"      
clusplot(iris, irisCluster$cluster, color=T, shade=T, labels=0, lines=0)

LS0tDQp0aXRsZTogIkdldHRpbmcgU3RhcnRlZCB3aXRoIEJhc2UgUiBHcmFwaGljcyINCmF1dGhvcjogIkNoZW5nIFBlbmciDQpkYXRlOiAiV2VzdCBDaGVzdGVyIFVuaXZlcnNpdHkiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jX2NvbGxhcHNlZDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgc21vb3RoX3Njcm9sbDogeWVzDQogICAgdGhlbWU6IGx1bWVuDQogIHBkZl9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KZWRpdG9yX29wdGlvbnM6DQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCi0tLQ0KDQoNCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCi8qIFRhYmxlIG9mIGNvbnRlbnQgLSBuYXZpZ2F0aW9uICovDQpkaXYjVE9DIGxpIHsNCiAgICBsaXN0LXN0eWxlOm5vbmU7DQogICAgYmFja2dyb3VuZC1jb2xvcjpsaWdodGdyYXk7DQogICAgYmFja2dyb3VuZC1pbWFnZTpub25lOw0KICAgIGJhY2tncm91bmQtcmVwZWF0Om5vbmU7DQogICAgYmFja2dyb3VuZC1wb3NpdGlvbjowOw0KICAgIGZvbnQtZmFtaWx5OiBBcmlhbCwgSGVsdmV0aWNhLCBzYW5zLXNlcmlmOw0KICAgIGNvbG9yOiAjNzgwYzBjOw0KfQ0KDQoNCi8qIFRpdGxlIGZvbnRzICovDQpoMS50aXRsZSB7DQogIGZvbnQtc2l6ZTogMjRweDsNCiAgY29sb3I6IGRhcmtibHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtZmFtaWx5OiBBcmlhbCwgSGVsdmV0aWNhLCBzYW5zLXNlcmlmOw0KICBmb250LXZhcmlhbnQtY2Fwczogbm9ybWFsOw0KfQ0KaDQuYXV0aG9yIHsgDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC1mYW1pbHk6IEFyaWFsLCBIZWx2ZXRpY2EsIHNhbnMtc2VyaWY7DQogIGNvbG9yOiBuYXZ5Ow0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoNC5kYXRlIHsgDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC1mYW1pbHk6IEFyaWFsLCBIZWx2ZXRpY2EsIHNhbnMtc2VyaWY7DQogIGNvbG9yOiBkYXJrYmx1ZTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KDQovKiBTZWN0aW9uIGhlYWRlcnMgKi8NCmgxIHsNCiAgICBmb250LXNpemU6IDIycHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDIgew0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoMyB7IA0KICAgIGZvbnQtc2l6ZTogMTVweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoNCB7DQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBkYXJrcmVkOw0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCi8qIERlY29yYXRpb24gb2YgaHlwZXJsaW5rcyAgKi8NCg0KLyogdW52aXNpdGVkIGxpbmsgKi8NCmE6bGluayB7DQogIGNvbG9yOiBncmVlbjsNCn0NCg0KLyogdmlzaXRlZCBsaW5rICovDQphOnZpc2l0ZWQgew0KICBjb2xvcjogcHVycGxlOw0KfQ0KDQovKiBtb3VzZSBvdmVyIGxpbmsgKi8NCmE6aG92ZXIgew0KICBjb2xvcjogcmVkOw0KfQ0KDQovKiBzZWxlY3RlZCBsaW5rICovDQphOmFjdGl2ZSB7DQogIGNvbG9yOiB5ZWxsb3c7DQp9DQo8L3N0eWxlPg0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCm9wdGlvbnMocmVwb3MgPSBsaXN0KENSQU49Imh0dHA6Ly9jcmFuLnJzdHVkaW8uY29tLyIpKQ0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IA0KIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuDQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KICAgbGlicmFyeSh0aWR5dmVyc2UpDQp9DQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoImNvd3Bsb3QiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiY293cGxvdCIpDQogICBsaWJyYXJ5KGNvd3Bsb3QpDQp9DQppZiAoIXJlcXVpcmUoImxhdGV4MmV4cCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJsYXRleDJleHAiKQ0KICAgbGlicmFyeShsYXRleDJleHApDQp9DQppZiAoIXJlcXVpcmUoInBsb3RseSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KICAgbGlicmFyeShwbG90bHkpDQp9DQppZiAoIXJlcXVpcmUoImdhcG1pbmRlciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJnYXBtaW5kZXIiKQ0KICAgbGlicmFyeShnYXBtaW5kZXIpDQp9DQppZiAoIXJlcXVpcmUoInBuZyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicG5nIikgICAgICAgICAgICAgIyBJbnN0YWxsIHBuZyBwYWNrYWdlDQogICAgbGlicmFyeSgicG5nIikNCn0NCmlmICghcmVxdWlyZSgiUkN1cmwiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoIlJDdXJsIikgICAgICAgICAgICAgIyBJbnN0YWxsIFJDdXJsIHBhY2thZ2UNCiAgICBsaWJyYXJ5KCJSQ3VybCIpDQp9DQppZiAoIXJlcXVpcmUoImNvbG91cnBpY2tlciIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiY29sb3VycGlja2VyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImNvbG91cnBpY2tlciIpDQp9DQppZiAoIXJlcXVpcmUoImdnYW5pbWF0ZSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dhbmltYXRlIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImdnYW5pbWF0ZSIpDQp9DQppZiAoIXJlcXVpcmUoImdpZnNraSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ2lmc2tpIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImdpZnNraSIpDQp9DQppZiAoIXJlcXVpcmUoIm1hZ2ljayIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygibWFnaWNrIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoIm1hZ2ljayIpDQp9DQppZiAoIXJlcXVpcmUoImdyRGV2aWNlcyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ3JEZXZpY2VzIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImdyRGV2aWNlcyIpDQp9DQppZiAoIXJlcXVpcmUoImpwZWciKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImpwZWciKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgianBlZyIpDQp9DQppZiAoIXJlcXVpcmUoIlZHQU0iKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoIlZHQU0iKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiVkdBTSIpDQp9DQppZiAoIXJlcXVpcmUoIk1BU1MiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoIk1BU1MiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiTUFTUyIpDQp9DQppZiAoIXJlcXVpcmUoIm5uZXQiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoIm5uZXQiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgibm5ldCIpDQp9DQppZiAoIXJlcXVpcmUoImNsdXN0ZXIiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImNsdXN0ZXIiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiY2x1c3RlciIpDQp9DQoNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCAgIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9IFRSVUUsICAgDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQSkNCmBgYA0KDQoNCiMgSW50cm9kdWN0aW9uIA0KDQpUaGUgdHdvIGNvcmUgcGxvdHRpbmcgYW5kIGdyYXBoaWNzIHBhY2thZ2VzIGluIHRoZSBiYXNlIFIgYXJlOg0KDQoqICoqZ3JhcGhpY3MqKjogY29udGFpbnMgcGxvdHRpbmcgZnVuY3Rpb25zIGZvciB0aGUg4oCcYmFzZeKAnSBncmFwaGluZyBzeXN0ZW1zLCBpbmNsdWRpbmcgcGxvdCwgaGlzdCwgYm94cGxvdCwgYW5kIG1hbnkgb3RoZXJzLg0KDQoqICoqZ3JEZXZpY2VzKio6IGNvbnRhaW5zIGFsbCB0aGUgY29kZSBpbXBsZW1lbnRpbmcgdGhlIHZhcmlvdXMgZ3JhcGhpY3MgZGV2aWNlcywgaW5jbHVkaW5nIFgxMSwgUERGLCBQb3N0U2NyaXB0LCBQTkcsIGV0Yy4NCg0KVGhlIGBnckRldmljZXNgIHBhY2thZ2UgY29udGFpbnMgdGhlIGZ1bmN0aW9uYWxpdHkgZm9yIHNlbmRpbmcgcGxvdHMgdG8gdmFyaW91cyBvdXRwdXQgZGV2aWNlcy4gVGhlIGBncmFwaGljc2AgcGFja2FnZSBjb250YWlucyB0aGUgY29kZSBmb3IgYWN0dWFsbHkgY29uc3RydWN0aW5nIGFuZCBhbm5vdGF0aW5nIHBsb3RzLg0KDQpJbiB0aGlzIG5vdGUsIHdlIGZvY3VzIG9uIHVzaW5nIHRoZSBiYXNlIHBsb3R0aW5nIHN5c3RlbSB0byBjcmVhdGUgZ3JhcGhpY3Mgb24gdGhlIHNjcmVlbiBkZXZpY2UuDQoNCg0KIyBXb3JraW5nIERhdGEgLWlyaXMNCg0KDQpgYGB7ciBlY2hvPUZBTFNFfQ0KRFQ6OmRhdGF0YWJsZShpcmlzLCBmaWxsQ29udGFpbmVyID0gRkFMU0UsIG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSAxMCkpDQpgYGANCg0KPGZvbnQgY29sb3IgPSAiZGFya3JlZCI+PGI+T2JqZWN0aXZlczwvYj48L2ZvbnQ+OiBJZGVudGlmeSBtZWFuaW5nZnVsIHBhdHRlcm5zIGFuZCBtYWtlIHZpc3VhbCByZXByZXNlbnRhdGlvbnMNCg0KKiBXaGF0IGluZm9ybWF0aW9uIGRvZXMgdGhlIGRhdGEgc2V0IGhhdmU/DQoqIEhvdyB0byBmaW5kIHRoZSBpbmZvcm1hdGlvbiBvZiBpbnRlcmVzdD8gDQogIC0gZGVzY3JpcHRpdmU/IA0KICAtIGFsZ29yaXRobS1iYXNlZD8gDQogIC0gbW9kZWwtYmFzZWQ/DQoqIFRvbyBtYW55IHZhcmlhYmxlcyBvciB0b28gbWFueSBkYXRhIHBvaW50cz8NCiogV2hhdCBmb3JtIG9mIHZpc3VhbGl6YXRpb24gdG8gdXNlPw0KKiBXaGF0IHZpc3VhbCBkZXNpZ24/IFNpbmdsZSBwYXR0ZXJuIG9yIG11bHRpcGxlIHBhdHRlcm5zPw0KDQoNCiMgQmFzZSBHcmFwaGljcw0KDQpCYXNlIGdyYXBoaWNzIGFyZSB1c2VkIG1vc3QgY29tbW9ubHkgYW5kIGFyZSBhIHZlcnkgcG93ZXJmdWwgc3lzdGVtIGZvciBjcmVhdGluZyBkYXRhIGdyYXBoaWNzLiBUaGVyZSBhcmUgdHdvIHBoYXNlcyB0byBjcmVhdGluZyBhIGJhc2UgcGxvdDoNCg0KKiBJbml0aWFsaXppbmcgYSBuZXcgcGxvdC4NCg0KKiBBbm5vdGF0aW5nIChhZGRpbmcgdG8pIGFuIGV4aXN0aW5nIHBsb3QuDQoNCkZvciBleGFtcGxlLCBjYWxsaW5nIHRoZSBiYXNlIHBsb3QgZnVuY3Rpb25zIGBwbG90KHgsIHkpYCBvciBgaGlzdCh4KWAgd2lsbCBsYXVuY2ggYSBncmFwaGljcyBkZXZpY2UgKGlmIG9uZSBpcyBub3QgYWxyZWFkeSBvcGVuKSBhbmQgZHJhdyBhIG5ldyBwbG90IG9uIHRoZSBkZXZpY2UuIFRoZSBiYXNlZCBwbG90IGZ1bmN0aW9uIGhhcyBtYW55IGFyZ3VtZW50cywgbGV0dGluZyB1cyBzZXQgdGhlIHRpdGxlLCB4LWF4aXMgbGFiZWwsIHktYXhpcyBsYWJlbCwgZXRjLg0KDQpUaGUgYmFzZSBncmFwaGljcyBzeXN0ZW0gaGFzIG1hbnkgZ2xvYmFsIHBhcmFtZXRlcnMgdGhhdCBjYW4gYmUgc2V0IGFuZCB0d2Vha2VkLiBUaGVzZSBwYXJhbWV0ZXJzIGFyZSBkb2N1bWVudGVkIGluIGA/cGFyYCBhbmQgYXJlIHVzZWQgdG8gY29udHJvbCB0aGUgZ2xvYmFsIGJlaGF2aW9yIG9mIHBsb3RzLCBzdWNoIGFzIHRoZSBtYXJnaW5zLCBheGlzIG9yaWVudGF0aW9uLCBhbmQgb3RoZXIgZGV0YWlscy4gDQoNCiMgU2ltcGxlIEJhc2UgR3JhcGhpY3MNCg0KVGhpcyBzZWN0aW9uIGV4cGxhaW5zIGhvdyB0byB1c2UgdGhlIGJhc2UgcGxvdHRpbmcgZnVuY3Rpb25zIHRvIG1ha2UgYmFzaWMgc3RhdGlzdGljYWwgZ3JhcGhpY3MuDQoNCiMjIEhpc3RvZ3JhbQ0KDQpIZXJlIGlzIGFuIGV4YW1wbGUgb2YgYSBzaW1wbGUgaGlzdG9ncmFtIG1hZGUgdXNpbmcgdGhlIGBoaXN0KClgIGZ1bmN0aW9uIGluIHRoZSBncmFwaGljcyBwYWNrYWdlLiBJZiB3ZSBydW4gdGhpcyBjb2RlIGFuZCBvdXIgZ3JhcGhpY3Mgd2luZG93IGlzIG5vdCBhbHJlYWR5IG9wZW4sIGl0IHNob3VsZCBvcGVuIG9uY2UgeW91IGNhbGwgdGhlIGBoaXN0KClgIGZ1bmN0aW9uLg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcuY2FwPSJGaWd1cmUgMS4gU2VwYWwgd2lkdGggb2YgaXJpcyJ9DQojIyBEcmF3IGEgbmV3IHBsb3Qgb24gdGhlIHNjcmVlbiBkZXZpY2UNCmhpc3QoaXJpcyRTZXBhbC5MZW5ndGgsDQogICAgIHhsYWIgPSAiU2VwYWwgTGVuZ3RoIiwNCiAgICAgeWxhYiA9ICJDb3VudHMiLA0KICAgICBtYWluID0gIkZyZXF1ZW5jeSBkaXN0cmlidXRpb24gb2Ygc2VwYWwgbGVuZ3RoIikgDQpgYGANCg0KDQojIyBCb3hwbG90DQoNCkJveHBsb3RzIGNhbiBiZSBtYWRlIGluIFIgdXNpbmcgdGhlIGBib3hwbG90KClgIGZ1bmN0aW9uLCB3aGljaCB0YWtlcyBhcyBpdHMgZmlyc3QgYXJndW1lbnQgYSBmb3JtdWxhLiBUaGUgZm9ybXVsYSBoYXMgYSBmb3JtIG9mIGB5LWF4aXMgfiB4LWF4aXNgLiBBbnl0aW1lIHlvdSBzZWUgYSBgfmAgaW4gUiwgaXTigJlzIGEgZm9ybXVsYS4gSGVyZSwgd2UgYXJlIHBsb3R0aW5nIHRoZSBzZXBhbCBsZW5ndGggb2YgaXJpcyBmbG93ZXJzIGFuZCB0aGUgcmlnaHQtaGFuZCBzaWRlIG9mIGB+YCB0aGUgU3BlY2llcy4NCg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcuY2FwPSJCb3ggcGxvdCBvZiBzZXBhbCBsZW5ndGggYnkgc3BlY2llcyJ9DQpib3hwbG90KFNlcGFsLkxlbmd0aCB+IFNwZWNpZXMsIGRhdGEgPSBpcmlzLCB4bGFiID0gIlNwZWNpZXMiLCB5bGFiID0gIlNlcGFsIExlbmd0aCIpDQpgYGANCg0KRWFjaCBib3hwbG90IHNob3dzIHRoZSBtZWRpYW4sIDI1dGgsIGFuZCA3NXRoIHBlcmNlbnRpbGVzIG9mIHRoZSBkYXRhICh0aGUg4oCcYm944oCdKSwgYXMgd2VsbCBhcyArLy0gMS41IHRpbWVzIHRoZSBpbnRlcnF1YXJ0aWxlIHJhbmdlIChJUVIpIG9mIHRoZSBkYXRhICh0aGUg4oCcd2hpc2tlcnPigJ0pLiBBbnkgZGF0YSBwb2ludHMgYmV5b25kIDEuNSB0aW1lcyB0aGUgSVFSIG9mIHRoZSBkYXRhIGFyZSBpbmRpY2F0ZWQgc2VwYXJhdGVseSB3aXRoIGNpcmNsZXMuDQoNCldlIGNhbiBzZWUgdGhhdCBWaXJnaW5pY2EgaGFzIGFuIG91dGxpZXIuDQoNCiMjIFNjYXR0ZXJwbG90DQoNCkhlcmUgaXMgYSBzaW1wbGUgc2NhdHRlci1wbG90IG1hZGUgd2l0aCB0aGUgYHBsb3QoKWAgZnVuY3Rpb24uDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy5jYXA9IlNjYXR0ZXIgcGxvdCBvZiBzZXBhbCBsZW5ndGggYW5kIHNlcGFsIHdpZHRoIn0NCnBsb3QoaXJpcyRTZXBhbC5MZW5ndGgsIGlyaXMkU2VwYWwuV2lkdGgsIA0KICAgICB4bGFiID0gInNlcGFsIGxlbmd0aCIsDQogICAgIHlsYWIgPSAic2VwYWwgd2lkdGgiLA0KICAgICBtYWluID0gIiAiKQ0KYGBgDQoNCkdlbmVyYWxseSwgdGhlIGBwbG90KClgIGZ1bmN0aW9uIHRha2VzIHR3byB2ZWN0b3JzIG9mIG51bWJlcnM6IG9uZSBmb3IgdGhlIHgtYXhpcyBjb29yZGluYXRlcyBhbmQgb25lIGZvciB0aGUgeS1heGlzIGNvb3JkaW5hdGVzLiBIb3dldmVyLCBgcGxvdCgpYCBpcyBhIGdlbmVyaWMgZnVuY3Rpb24gaW4gUiwgd2hpY2ggbWVhbnMgaXRzIGJlaGF2aW9yIGNhbiBjaGFuZ2UgZGVwZW5kaW5nIG9uIHdoYXQga2luZHMgb2YgZGF0YSBhcmUgcGFzc2VkIHRvIHRoZSBmdW5jdGlvbi4gDQoNCk9uZSB0aGluZyB0byBub3RlIGhlcmUgaXMgdGhhdCB3ZSBoYXZlIHByb3ZpZGVkIGxhYmVscyBmb3IgdGhlIHgtIGFuZCB0aGUgeS1heGlzIGFuZCB0aXRsZSBhcyB3ZWxsLiBJZiB0aGV5IHdlcmUgbm90IHNwZWNpZmllZCwgdGhlIHBsb3QgZnVuY3Rpb24gd2lsbCBwcm92aWRlIHRoaXMgaW5mb3JtYXRpb24gYXV0b21hdGljYWxseSB1c2luZyB0aGUgbmFtZXMgb2YgdGhlIHZhcmlhYmxlcy4NCg0KIyBTb21lIEltcG9ydGFudCBCYXNlIEdyYXBoaWNzIFBhcmFtZXRlcnMNCg0KTWFueSBiYXNlIHBsb3R0aW5nIGZ1bmN0aW9ucyBzaGFyZSBhIHNldCBvZiBnbG9iYWwgcGFyYW1ldGVycy4gSGVyZSBhcmUgYSBmZXcga2V5IG9uZXMuDQoNCiogYHBjaGA6IHRoZSBwbG90dGluZyBzeW1ib2wgKGRlZmF1bHQgaXMgYW4gb3BlbiBjaXJjbGUpLg0KKiBgbHR5YDogdGhlIGxpbmUgdHlwZSAoZGVmYXVsdCBpcyBhIHNvbGlkIGxpbmUpLCBjYW4gYmUgZGFzaGVkLCBkb3R0ZWQsIGV0Yy4NCiogYGx3ZGA6IHRoZSBsaW5lIHdpZHRoLCBzcGVjaWZpZWQgYXMgYW4gaW50ZWdlciBtdWx0aXBsZS4NCiogYGNvbGA6IHRoZSBwbG90dGluZyBjb2xvciwgc3BlY2lmaWVkIGFzIGEgbnVtYmVyLCBzdHJpbmcsIG9yIGhleCBjb2RlOyB0aGUgY29sb3JzKCkgZnVuY3Rpb24gZ2l2ZXMgeW91IGEgdmVjdG9yIG9mIGNvbG9ycyBieSBuYW1lLg0KKiBgeGxhYmA6IGNoYXJhY3RlciBzdHJpbmcgZm9yIHRoZSB4LWF4aXMgbGFiZWwuDQoqIGB5bGFiYDogY2hhcmFjdGVyIHN0cmluZyBmb3IgdGhlIHktYXhpcyBsYWJlbC4NCg0KVGhlIGBwYXIoKWAgZnVuY3Rpb24gaXMgdXNlZCB0byBzcGVjaWZ5IHRoZSBnbG9iYWwgZ3JhcGhpY3MgcGFyYW1ldGVycyB0aGF0IGFmZmVjdCBhbGwgcGxvdHMgaW4gYW4gUiBzZXNzaW9uLiBUaGVzZSBwYXJhbWV0ZXJzIGNhbiBiZSBvdmVycmlkZGVuIHdoZW4gdGhleSBhcmUgc3BlY2lmaWVkIGFzIGFyZ3VtZW50cyB0byBzcGVjaWZpYyBwbG90dGluZyBmdW5jdGlvbnMuDQoNCiogYGxhc2A6IHRoZSBvcmllbnRhdGlvbiBvZiB0aGUgYXhpcyBsYWJlbHMgb24gdGhlIHBsb3QuDQoqIGBiZ2A6IHRoZSBiYWNrZ3JvdW5kIGNvbG9yLg0KKiBgbWFyYDogdGhlIG1hcmdpbiBzaXplLg0KKiBgb21hYDogdGhlIG91dGVyIG1hcmdpbiBzaXplIChkZWZhdWx0IGlzIDAgZm9yIGFsbCBzaWRlcykuDQoqIGBtZnJvd2A6IG51bWJlciBvZiBwbG90cyBwZXIgcm93LCBjb2x1bW4gKHBsb3RzIGFyZSBmaWxsZWQgcm93LXdpc2UpLg0KKiBgbWZjb2xgOiBudW1iZXIgb2YgcGxvdHMgcGVyIHJvdywgY29sdW1uIChwbG90cyBhcmUgZmlsbGVkIGNvbHVtbi13aXNlKS4NCg0KDQpXZSBjYW4gc2VlIHRoZSBkZWZhdWx0IHZhbHVlcyBmb3IgZ2xvYmFsIGdyYXBoaWNzIHBhcmFtZXRlcnMgYnkgY2FsbGluZyB0aGUgYHBhcigpYCBmdW5jdGlvbiBhbmQgcGFzc2luZyB0aGUgbmFtZSBvZiB0aGUgcGFyYW1ldGVyIGluIHF1b3Rlcy4NCg0KYGBge3J9DQpwYXIoImx0eSIpDQpwYXIoImNvbCIpDQpwYXIoInBjaCIpDQpgYGANCg0KSGVyZSBhcmUgc29tZSBtb3JlIGRlZmF1bHQgdmFsdWVzIGZvciBnbG9iYWwgZ3JhcGhpY3MgcGFyYW1ldGVycy4NCg0KYGBge3J9DQpwYXIoImJnIikNCnBhcigibWFyIikNCnBhcigibWZyb3ciKQ0KYGBgDQoNCkZvciB0aGUgbW9zdCBwYXJ0LCB3ZSB1c3VhbGx5IGRvbuKAmXQgaGF2ZSB0byBtb2RpZnkgdGhlc2Ugd2hlbiBtYWtpbmcgcXVpY2sgcGxvdHMuIEhvd2V2ZXIsIHdlIG1pZ2h0IG5lZWQgdG8gdHdlYWsgdGhlbSBmb3IgZmluYWxpemluZyBmaW5pc2hlZCBwbG90cy4NCg0KDQojIEJhc2UgUGxvdHRpbmcgRnVuY3Rpb25zDQoNClRoZSBtb3N0IGJhc2ljIGJhc2UgcGxvdHRpbmcgZnVuY3Rpb24gaXMgYHBsb3QoKWAuIFRoZSBgcGxvdCgpYCBmdW5jdGlvbiBtYWtlcyBhIHNjYXR0ZXItcGxvdCwgb3Igb3RoZXIgdHlwZXMgb2YgcGxvdCBkZXBlbmRpbmcgb24gdGhlIGNsYXNzIG9mIHRoZSBvYmplY3QgYmVpbmcgcGxvdHRlZC4gQ2FsbGluZyBgcGxvdCgpYCB3aWxsIGRyYXcgYSBwbG90IG9uIHRoZSBzY3JlZW4gZGV2aWNlIChhbmQgb3BlbiB0aGUgc2NyZWVuIGRldmljZSBpZiBub3QgYWxyZWFkeSBvcGVuKS4gQWZ0ZXIgdGhhdCwgYW5ub3RhdGlvbiBmdW5jdGlvbnMgY2FuIGJlIGNhbGxlZCB0byBhZGQgdG8gdGhlIGFscmVhZHktbWFkZSBwbG90Lg0KDQpTb21lIGtleSBhbm5vdGF0aW9uIGZ1bmN0aW9ucyBhcmUNCg0KKiBgbGluZXMoKWA6IGFkZCBsaW5lcyB0byBhIHBsb3QsIGdpdmVuIGEgdmVjdG9yIG9mIHggdmFsdWVzIGFuZCBhIGNvcnJlc3BvbmRpbmcgdmVjdG9yIG9mIHkgdmFsdWVzIChvciBhIDItY29sdW1uIG1hdHJpeCk7IHRoaXMgZnVuY3Rpb24ganVzdCBjb25uZWN0cyB0aGUgZG90cw0KKiBgcG9pbnRzKClgOiBhZGQgcG9pbnRzIHRvIGEgcGxvdA0KKiBgdGV4dCgpYDogYWRkIHRleHQgbGFiZWxzIHRvIGEgcGxvdCB1c2luZyBzcGVjaWZpZWQgeCwgeSBjb29yZGluYXRlcw0KKiBgdGl0bGUoKWA6IGFkZCBhbm5vdGF0aW9ucyB0byB4LCB5LWF4aXMgbGFiZWxzLCB0aXRsZSwgc3VidGl0bGUsIG91dGVyIG1hcmdpbg0KKiBgbXRleHQoKWA6IGFkZCBhcmJpdHJhcnkgdGV4dCB0byB0aGUgbWFyZ2lucyAoaW5uZXIgb3Igb3V0ZXIpIG9mIHRoZSBwbG90DQoqIGBheGlzKClgOiBhZGRpbmcgYXhpcyB0aWNrcy9sYWJlbHMNCg0KSGVyZeKAmXMgYW4gZXhhbXBsZSBvZiBjcmVhdGluZyBhIGJhc2UgcGxvdCBhbmQgYWRkaW5nIHNvbWUgYW5ub3RhdGlvbi4gRmlyc3Qgd2UgbWFrZSB0aGUgcGxvdCB3aXRoIHRoZSBgcGxvdCgpYCBmdW5jdGlvbiBhbmQgdGhlbiBhZGQgYSB0aXRsZSB0byB0aGUgdG9wIG9mIHRoZSBwbG90IHdpdGggdGhlIGB0aXRsZSgpYCBmdW5jdGlvbi4NCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLmNhcD0iQmFzZSBwbG90IHdpdGggYW5ub3RhdGlvbjogSXJpcyJ9DQojIyBNYWtlIHRoZSBpbml0aWFsIHBsb3QNCnBsb3QoaXJpcyRTZXBhbC5MZW5ndGgsIGlyaXMkU2VwYWwuV2lkdGgpDQojIyBBZGQgYSB0aXRsZQ0KdGl0bGUobWFpbiA9ICJTZXBhbCBsZW5ndGggYW5kIHdpZHRoIG9mIGlyaXMiKSAgDQpgYGANCg0KDQoNCkhlcmUsIEkgc3RhcnQgd2l0aCB0aGUgc2FtZSBwbG90IGFzIGFib3ZlIChhbHRob3VnaCBJIGFkZCB0aGUgdGl0bGUgcmlnaHQgYXdheSB1c2luZyB0aGUgbWFpbiBhcmd1bWVudCB0byBgcGxvdCgpYCkgYW5kIHRoZW4gYW5ub3RhdGUgaXQgYnkgY29sb3JpbmcgYmx1ZSB0aGUgZGF0YSBwb2ludHMgY29ycmVzcG9uZGluZy4NCg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0Kc2VwYWwubGVuZ3RoID0gaXJpcyRTZXBhbC5MZW5ndGgNCnNlcGFsLndpZHRoID0gaXJpcyRTZXBhbC5XaWR0aA0Kc3BlY2llcyA9IGlyaXMkU3BlY2llcw0KIyMgaWRlbnRpZnlpbmcgdGhlIElEIG9mIFZpcmdpbmljYQ0KdmlyZ2luY2EuaWQgPSB3aGljaChzcGVjaWVzPT0idmlyZ2luaWNhIikgICMgdmFsdWUgYXJlIGNhc2Ugc2Vuc2l0aXZlIQ0KIyMgbWFraW5nIHNjYXR0ZXIgcGxvdA0KcGxvdChzZXBhbC5sZW5ndGgsIHNlcGFsLndpZHRoLCBtYWluID0gIlNlcGFsIGxlZ250aCB2cyBzZXBhbCB3aWR0aCIpDQojIw0KcG9pbnRzKHNlcGFsLmxlbmd0aFt2aXJnaW5jYS5pZF0sIHNlcGFsLndpZHRoW3ZpcmdpbmNhLmlkXSwgcGNoID0gMTksIGNvbCA9ICJyZWQiKQ0KYGBgDQoNClRoZSBmb2xsb3dpbmcgcGxvdCBjb2xvcnMgdGhlIGRhdGEgcG9pbnRzIHdpdGggZGlmZmVyZW50IGNvbG9ycyBiYXNlZCBvbiBzcGVjaWVzLiBgbGVnZW5kKClgIGZ1bmN0aW9uIGV4cGxhaW5zIHRoZSBtZWFuaW5nIG9mIHRoZSBkaWZmZXJlbnQgY29sb3JzIGluIHRoZSBwbG90Lg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0Kc2VwYWwubGVuZ3RoID0gaXJpcyRTZXBhbC5MZW5ndGgNCnNlcGFsLndpZHRoID0gaXJpcyRTZXBhbC5XaWR0aA0Kc3BlY2llcyA9IGlyaXMkU3BlY2llcw0KIyMgaWRlbnRpZnlpbmcgdGhlIElEIG9mIFZpcmdpbmljYQ0KdmlyZ2luY2EuaWQgPSB3aGljaChzcGVjaWVzPT0idmlyZ2luaWNhIikgICMgdmFsdWUgYXJlIGNhc2Ugc2Vuc2l0aXZlIQ0Kc2V0b3NhLmlkID0gd2hpY2goc3BlY2llcz09InNldG9zYSIpDQp2ZXJzaWNvbG9yLmlkID0gd2hpY2goc3BlY2llcz09InZlcnNpY29sb3IiKQ0KIyMgbWFraW5nIGFuIGVtcHR5IHBsb3Q6IHR5cGUgPSAibiIgPT0+IG5vIHBvaW50DQpwbG90KHNlcGFsLmxlbmd0aCwgc2VwYWwud2lkdGgsIG1haW4gPSAiU2VwYWwgbGVnbnRoIHZzIHNlcGFsIHdpZHRoIiwgdHlwZSA9ICJuIikNCiMjDQpwb2ludHMoc2VwYWwubGVuZ3RoW3ZpcmdpbmNhLmlkXSwgc2VwYWwud2lkdGhbdmlyZ2luY2EuaWRdLCBwY2ggPSAxOCwgY29sID0gInJlZCIpDQpwb2ludHMoc2VwYWwubGVuZ3RoW3NldG9zYS5pZF0sIHNlcGFsLndpZHRoW3NldG9zYS5pZF0sIHBjaCA9IDE5LCBjb2wgPSAiYmx1ZSIpDQpwb2ludHMoc2VwYWwubGVuZ3RoW3ZlcnNpY29sb3IuaWRdLCBzZXBhbC53aWR0aFt2ZXJzaWNvbG9yLmlkXSwgcGNoID0gMjAsIGNvbCA9ICJjeWFuIikNCmxlZ2VuZCgidG9wbGVmdCIsIGMoInZpcmdpbmljYSIsICJzZXRvc2EiLCAidmVyc2ljb2xvciIpLCANCiAgICAgICAgICAgICAgICAgIGNvbD1jKCJyZWQiLCAiYmx1ZSIsICJjeWFuIiksDQogICAgICAgICAgICAgICAgICBwY2g9YygxOCwgMTksIDIwKSkNCmBgYA0KDQoNCiMgIEJhc2UgUGxvdCB3aXRoIFJlZ3Jlc3Npb24gTGluZQ0KDQpJdOKAmXMgZmFpcmx5IGNvbW1vbiB0byBtYWtlIGEgc2NhdHRlcnBsb3QgYW5kIHRoZW4gd2FudCB0byBkcmF3IGEgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIGxpbmUgdGhyb3VnaCB0aGUgZGF0YS4gVGhpcyBjYW4gYmUgZG9uZSB3aXRoIHRoZSBgYWJsaW5lKClgIGZ1bmN0aW9uLg0KDQpCZWxvdywgd2UgZmlyc3QgbWFrZSB0aGUgcGxvdCAoYXMgYWJvdmUpLiBUaGVuIHdlIGZpdCBhIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB1c2luZyB0aGUgYGxtKClgIGZ1bmN0aW9uLiBIZXJlLCB3ZSB0cnkgdG8gbW9kZWwgT3pvbmUgYXMgYSBmdW5jdGlvbiBvZiBXaW5kLiBUaGVuIHdlIHRha2UgdGhlIG91dHB1dCBvZiBgbG0oKWAgYW5kIHBhc3MgaXQgdG8gdGhlIGBhYmxpbmUoKWAgZnVuY3Rpb24gd2hpY2ggYXV0b21hdGljYWxseSB0YWtlcyB0aGUgaW5mb3JtYXRpb24gZnJvbSB0aGUgbW9kZWwgb2JqZWN0IGFuZCBjYWxjdWxhdGVzIHRoZSBjb3JyZXNwb25kaW5nIHJlZ3Jlc3Npb24gbGluZS4NCg0KTm90ZSB0aGF0IGluIHRoZSBjYWxsIHRvIGBwbG90KClgIGJlbG93LCB3ZSBzZXQgYHBjaCA9IDIwYCB0byBjaGFuZ2UgdGhlIHBsb3R0aW5nIHN5bWJvbCB0byBhIGZpbGxlZCBjaXJjbGUuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQpzZXBhbC5sZW5ndGggPSBpcmlzJFNlcGFsLkxlbmd0aA0Kc2VwYWwud2lkdGggPSBpcmlzJFNlcGFsLldpZHRoDQojIyBtYWtpbmcgYSBwbG90IA0KcGxvdChzZXBhbC5sZW5ndGgsIHNlcGFsLndpZHRoLCBtYWluID0gIlNlcGFsIGxlZ250aCB2cyBzZXBhbCB3aWR0aCIsIHBjaCA9IDIxKQ0KIyMNCiBtb2RlbCA8LSBsbSggc2VwYWwud2lkdGggfiBzZXBhbC5sZW5ndGgpDQogIyMgRHJhdyByZWdyZXNzaW9uIGxpbmUgb24gcGxvdA0KIGFibGluZShtb2RlbCwgbHdkID0gMiwgY29sID0gInJlZCIpDQpgYGANCg0KIyBNdWx0aXBsZSBCYXNlIFBsb3RzDQoNCk1ha2luZyBtdWx0aXBsZSBwbG90cyBzaWRlIGJ5IHNpZGUgaXMgYSB1c2VmdWwgd2F5IHRvIHZpc3VhbGl6ZSBtYW55IHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB2YXJpYWJsZXMgd2l0aCBzdGF0aWMgMi1EIHBsb3RzLiBPZnRlbiB0aGUgcmVwZXRpdGlvbiBvZiBkYXRhIGFjcm9zcyBhIHNpbmdsZSBwbG90IHdpbmRvdyBjYW4gYmUgYSB1c2VmdWwgd2F5IHRvIGlkZW50aWZ5IHBhdHRlcm5zIGluIHRoZSBkYXRhLiBJbiBvcmRlciB0byBkbyB0aGlzLCB0aGUgYG1mcm93YCBhbmQgYG1mY29sYCBwYXJhbWV0ZXJzIHNldCBieSB0aGUgYHBhcigpYCBmdW5jdGlvbiBhcmUgY3JpdGljYWwuDQoNCkJvdGggdGhlIGBtZnJvd2AgYW5kIGBtZmNvbGAgcGFyYW1ldGVycyB0YWtlIHR3byBudW1iZXJzOiB0aGUgbnVtYmVyIG9mIHJvd3Mgb2YgcGxvdHMgZm9sbG93ZWQgYnkgdGhlIG51bWJlciBvZiBjb2x1bW5zLiBUaGUgbXVsdGlwbGUgcGxvdHMgd2lsbCBiZSBhcnJhbmdlZCBpbiBhIG1hdHJpeC1saWtlIHBhdHRlcm4uIFRoZSBvbmx5IGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgdHdvIHBhcmFtZXRlcnMgaXMgdGhhdCBpZiBgbWZyb3dgIGlzIHNldCwgdGhlbiB0aGUgcGxvdHMgd2lsbCBiZSBkcmF3biByb3ctd2lzZTsgaWYgYG1mY29sYCBpcyBzZXQsIHRoZSBwbG90cyB3aWxsIGJlIGRyYXduIGNvbHVtbi13aXNlLg0KDQpJbiB0aGUgZXhhbXBsZSBiZWxvdywgd2UgbWFrZSB0d28gcGxvdHM6IHNlcGFsIGxlbmd0aCB2cyBzZXBhbCB3aWR0aCBhbmQgcGV0YWwgbGVuZ3RoIHZzIHBldGFsIHdpZHRoLiBXZSBzZXQgYHBhcihtZnJvdyA9IGMoMSwgMikpYCwgd2hpY2ggaW5kaWNhdGVzIHRoYXQgd2UgaGF2ZSBvbmUgcm93IG9mIHBsb3RzIGFuZCB0d28gY29sdW1ucyBvZiBwbG90cy4NCg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KcGFyKG1mcm93ID0gYygxLCAyKSkNCnNlcGFsLmxlbmd0aCA9IGlyaXMkU2VwYWwuTGVuZ3RoDQpzZXBhbC53aWR0aCA9IGlyaXMkU2VwYWwuV2lkdGgNCnBldGFsLmxlbmd0aCA9IGlyaXMkUGV0YWwuTGVuZ3RoDQpwZXRhbC53aWR0aCA9IGlyaXMkUGV0YWwuV2lkdGgNCiMjIG1ha2luZyBhIHBsb3QgDQpwbG90KHNlcGFsLmxlbmd0aCwgc2VwYWwud2lkdGgsIG1haW4gPSAiU2VwYWwgbGVnbnRoIHZzIHNlcGFsIHdpZHRoIiwgcGNoID0gMjApDQpwbG90KHBldGFsLmxlbmd0aCwgcGV0YWwud2lkdGgsIG1haW4gPSAiUGV0YWwgbGVnbnRoIHZzIHBldGFsIHdpZHRoIiwgcGNoID0gMjEpDQpgYGANCg0KVGhlIGV4YW1wbGUgYmVsb3cgY3JlYXRlcyB0aHJlZSBwbG90cyBpbiBhIHJvdyBieSBzZXR0aW5nIGBwYXIobWZyb3cgPSBjKDEsIDMpKWAuIEhlcmUgd2UgYWxzbyBjaGFuZ2UgdGhlIHBsb3QgbWFyZ2lucyB3aXRoIHRoZSBtYXIgcGFyYW1ldGVyLiBUaGUgdmFyaW91cyBtYXJnaW4gcGFyYW1ldGVycywgbGlrZSBgbWFyYCwgYXJlIHNwZWNpZmllZCBieSBzZXR0aW5nIGEgdmFsdWUgZm9yIGVhY2ggc2lkZSBvZiB0aGUgcGxvdC4gYFNpZGUgMWAgaXMgdGhlIGJvdHRvbSBvZiB0aGUgcGxvdCwgYHNpZGUgMmAgaXMgdGhlIGxlZnQtaGFuZCBzaWRlLCBgc2lkZSAzYCBpcyB0aGUgdG9wLCBhbmQgYHNpZGUgNGAgaXMgdGhlIHJpZ2h0LWhhbmQgc2lkZS4gDQoNCkluIHRoZSBleGFtcGxlIGJlbG93IHdlIGFsc28gbW9kaWZ5IHRoZSBvdXRlciBtYXJnaW4gdmlhIHRoZSBgb21hYCBwYXJhbWV0ZXIgdG8gY3JlYXRlIGEgbGl0dGxlIG1vcmUgc3BhY2UgZm9yIHRoZSBwbG90cyBhbmQgdG8gcGxhY2UgdGhlbSBjbG9zZXIgdG9nZXRoZXIuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQojIGxheW91dCBvZiB0aGUgcGxvdA0KcGFyKG1mcm93ID0gYygxLCAzKSwgDQogICAgbWFyID0gYyg0LCA0LCAyLCAxKSwgDQogICAgb21hID0gYygwLCAwLCAyLCAwKSkNCiMgZXh0cmFjdCB2YXJpYWJsZXMgZnJvbSB0aGUgZGF0YSBmcmFtZQ0Kc2VwYWwubGVuZ3RoID0gaXJpcyRTZXBhbC5MZW5ndGgNCnNlcGFsLndpZHRoID0gaXJpcyRTZXBhbC5XaWR0aA0Kc3BlY2llcyA9IGlyaXMkU3BlY2llcw0KIyMgaWRlbnRpZnlpbmcgdGhlIElEIG9mIFZpcmdpbmljYQ0KdmlyZ2luY2EuaWQgPSB3aGljaChzcGVjaWVzPT0idmlyZ2luaWNhIikgICMgdmFsdWUgYXJlIGNhc2Ugc2Vuc2l0aXZlIQ0Kc2V0b3NhLmlkID0gd2hpY2goc3BlY2llcz09InNldG9zYSIpDQp2ZXJzaWNvbG9yLmlkID0gd2hpY2goc3BlY2llcz09InZlcnNpY29sb3IiKQ0KIyMgbWFraW5nIHRocmVlIHBsb3RzDQpwbG90KHNlcGFsLmxlbmd0aFt2aXJnaW5jYS5pZF0sIHNlcGFsLndpZHRoW3ZpcmdpbmNhLmlkXSwgbWFpbiA9ICJ2aXJnaW5jYSIpDQpwbG90KHNlcGFsLmxlbmd0aFtzZXRvc2EuaWRdLCBzZXBhbC53aWR0aFtzZXRvc2EuaWRdLCBtYWluID0gInNldG9zYSIpDQpwbG90KHNlcGFsLmxlbmd0aFt2ZXJzaWNvbG9yLmlkXSwgc2VwYWwud2lkdGhbdmVyc2ljb2xvci5pZF0sIG1haW4gPSAidmVyc2ljb2xvciIpDQojIw0KbXRleHQoIlNlcGFsIGxlbmd0aCB2cyB3aWR0aDogSXJpcyIsIG91dGVyID0gVFJVRSkNCmBgYA0KDQpJbiB0aGUgYWJvdmUgZXhhbXBsZSwgdGhlIGBtdGV4dCgpYCBmdW5jdGlvbiB3YXMgdXNlZCB0byBjcmVhdGUgYW4gb3ZlcmFsbCB0aXRsZSBmb3IgdGhlIHBhbmVsIG9mIHBsb3RzLiBIZW5jZSwgZWFjaCBpbmRpdmlkdWFsIHBsb3QgaGFzIGEgdGl0bGUsIHdoaWxlIHRoZSBvdmVyYWxsIHNldCBvZiBwbG90cyBhbHNvIGhhcyBhIHN1bW1hcnkgdGl0bGUuIFRoZSBgbXRleHQoKWAgZnVuY3Rpb24gaXMgaW1wb3J0YW50IGZvciBhZGRpbmcgdGV4dCBhbm5vdGF0aW9ucyB0aGF0IGFyZW7igJl0IHNwZWNpZmljIHRvIGEgc2luZ2xlIHBsb3QuDQoNCk5vdGljZSB0aGF0IHRoZSBzY2FsZXMgb2YgdGhlIHZlcnRpY2FsIGF4ZXMgb2YgdGhlIHRocmVlIHBsb3RzIGFyZSBub3QgdGhlIHNhbWUuIFRvIG1pc2xlYWRpbmcgdmlzdWFsIGNvbXBhcmlzb24sIHdlIHNob3VsZCBtYWtlIHRoZSBzY2FsZXMgb2YgYm90aCBheGVzIHRoZSBzYW1lLg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KIyBsYXlvdXQgb2YgdGhlIHBsb3QNCnBhcihtZnJvdyA9IGMoMSwgMyksIA0KICAgIG1hciA9IGMoNCwgNCwgMiwgMSksIA0KICAgIG9tYSA9IGMoMCwgMCwgMiwgMCkpDQojIGV4dHJhY3QgdmFyaWFibGVzIGZyb20gdGhlIGRhdGEgZnJhbWUNCnNlcGFsLmxlbmd0aCA9IGlyaXMkU2VwYWwuTGVuZ3RoDQpzZXBhbC53aWR0aCA9IGlyaXMkU2VwYWwuV2lkdGgNCnNwZWNpZXMgPSBpcmlzJFNwZWNpZXMNCiMjIGlkZW50aWZ5aW5nIHRoZSBJRCBvZiBWaXJnaW5pY2ENCnZpcmdpbmNhLmlkID0gd2hpY2goc3BlY2llcz09InZpcmdpbmljYSIpICAjIHZhbHVlIGFyZSBjYXNlIHNlbnNpdGl2ZSENCnNldG9zYS5pZCA9IHdoaWNoKHNwZWNpZXM9PSJzZXRvc2EiKQ0KdmVyc2ljb2xvci5pZCA9IHdoaWNoKHNwZWNpZXM9PSJ2ZXJzaWNvbG9yIikNCiMjIG1ha2luZyB0aHJlZSBwbG90cw0KcGxvdChzZXBhbC5sZW5ndGhbdmlyZ2luY2EuaWRdLCBzZXBhbC53aWR0aFt2aXJnaW5jYS5pZF0sIHhsaW09Yyg0LDgpLCB5bGltPWMoMiwgNC41KSwgbWFpbiA9ICJ2aXJnaW5jYSIpDQpwbG90KHNlcGFsLmxlbmd0aFtzZXRvc2EuaWRdLCBzZXBhbC53aWR0aFtzZXRvc2EuaWRdLCB4bGltPWMoNCw4KSwgeWxpbT1jKDIsIDQuNSksbWFpbiA9ICJzZXRvc2EiKQ0KcGxvdChzZXBhbC5sZW5ndGhbdmVyc2ljb2xvci5pZF0sIHNlcGFsLndpZHRoW3ZlcnNpY29sb3IuaWRdLCB4bGltPWMoNCw4KSwgeWxpbT1jKDIsIDQuNSksbWFpbiA9ICJ2ZXJzaWNvbG9yIikNCiMjDQptdGV4dCgiU2VwYWwgbGVuZ3RoIHZzIHdpZHRoOiBJcmlzIiwgb3V0ZXIgPSBUUlVFKQ0KYGBgDQoNCldlIG5vdyBjYW4gdmlzdWFsaXplIHRoZSBtZWFuIHNlcGFsIHdpZHRoIGFuZCBzZXBhbCBsZW5ndGggYW1vbmcgdGhlIHNwZWNpZXMuDQoNCiMgQ29udHJvbGxpbmcgUG9pbnQgU2l6ZSBhbmQgVHJhbnNwYXJlbmN5DQoNCndlIHVzZSBgY2V4PWAgYW5kIGBhbHBoYT1gIHRvIGNvbnRyb2wgdGhlIHBvaW50IHNpemUgYWNjb3JkaW5nIHRvIHRoZSB2YWx1ZSBvZiBhIHZhcmlhYmxlIGFuZCB0aGUgbGV2ZWwgb2YgdHJhbnNwYXJlbmN5IG9mIHRoZSBwb2ludC4NCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJ30NCnNlcGFsLmxlbmd0aCA9IGlyaXMkU2VwYWwuTGVuZ3RoDQpzZXBhbC53aWR0aCA9IGlyaXMkU2VwYWwuV2lkdGgNCnNpemUucGV0YWwgPSBpcmlzJFBldGFsLldpZHRoDQpzcGVjaWVzID0gaXJpcyRTcGVjaWVzDQojIyBpZGVudGlmeWluZyB0aGUgSUQgb2YgVmlyZ2luaWNhDQp2aXJnaW5jYS5pZCA9IHdoaWNoKHNwZWNpZXM9PSJ2aXJnaW5pY2EiKSAgIyB2YWx1ZSBhcmUgY2FzZSBzZW5zaXRpdmUhDQpzZXRvc2EuaWQgPSB3aGljaChzcGVjaWVzPT0ic2V0b3NhIikNCnZlcnNpY29sb3IuaWQgPSB3aGljaChzcGVjaWVzPT0idmVyc2ljb2xvciIpDQojIyBjb2xvciBjb2RlDQpjb2wuY29kZSA9IGMoYWxwaGEoInJlZCIsMC41KSxhbHBoYSgiYmx1ZSIsMC41KSxhbHBoYSgiY3lhbiIsMC41KSkNCiMjIG1ha2luZyBhbiBlbXB0eSBwbG90OiB0eXBlID0gIm4iID09PiBubyBwb2ludA0KcGxvdChzZXBhbC5sZW5ndGgsIHNlcGFsLndpZHRoLCBtYWluID0gIlNlcGFsIGxlZ250aCB2cyBzZXBhbCB3aWR0aCIsIHR5cGUgPSAibiIpDQojIyBjaGFuZ2UgdGhlIHBvaW50IHNpemUgYmFzZWQgb24gdGhlaXIgYXZlcmFnZSBvZiBzZXBhbCBsZW5ndGggYW5kIHdpZHRoDQpwb2ludHMoc2VwYWwubGVuZ3RoW3ZpcmdpbmNhLmlkXSwgc2VwYWwud2lkdGhbdmlyZ2luY2EuaWRdLCANCiAgICAgICBwY2ggPSAxOSwgY29sID0gY29sLmNvZGVbMV0sIGNleCA9IHNpemUucGV0YWxbdmlyZ2luY2EuaWRdKQ0KcG9pbnRzKHNlcGFsLmxlbmd0aFtzZXRvc2EuaWRdLCBzZXBhbC53aWR0aFtzZXRvc2EuaWRdLCANCiAgICAgICBwY2ggPSAxOSwgY29sID0gY29sLmNvZGVbMl0sIGNleCA9IHNpemUucGV0YWxbc2V0b3NhLmlkXSkNCnBvaW50cyhzZXBhbC5sZW5ndGhbdmVyc2ljb2xvci5pZF0sIHNlcGFsLndpZHRoW3ZlcnNpY29sb3IuaWRdLCANCiAgICAgICBwY2ggPSAxOSwgY29sID0gY29sLmNvZGVbM10sIGNleCA9IHNpemUucGV0YWxbdmVyc2ljb2xvci5pZF0pDQpsZWdlbmQoInRvcGxlZnQiLCBjKCJ2aXJnaW5pY2EiLCAic2V0b3NhIiwgInZlcnNpY29sb3IiKSwgDQogICAgICAgICAgICAgICAgICBjb2w9Y29sLmNvZGUsDQogICAgICAgICAgICAgICAgICBwY2g9YygxOSwgMTksIDE5KSkNCmBgYA0KDQojIEFubm90YXRpb25zDQoNCldlIGFkZCBhbm5vdGF0aW9ucyB0byB0aGUgYmFzZSBSIHBsb3QuIFRoZSBhbm5vdGF0aW9ucyBjb3VsZCBiZSBwbGFpbiB0ZXh0cywgaW1hZ2VzLCBhbmQgbWF0aGVtYXRpY2FsIGV4cHJlc3Npb25zLg0KDQpgYGB7cn0NCg0KaXJpcy5pbWcgPC0gImh0dHBzOi8vcGVuZ2RzY2kuZ2l0aHViLmlvL1NUQTU1M1ZJWi93MDMvaW1nL2lyaXMuanBlZyINCm15LmlyaXMgPC0gcmVhZEpQRUcoZ2V0VVJMQ29udGVudChpcmlzLmltZykpDQpyYXN0ZXIuaXJpcyA8LSBhcy5yYXN0ZXIobXkuaXJpcykgDQojIFVzZSB0aGUgY29kZSBpbiB0aGUgcHJlY2lvdXMgc2VjdGlvbg0Kc2VwYWwubGVuZ3RoID0gaXJpcyRTZXBhbC5MZW5ndGgNCnNlcGFsLndpZHRoID0gaXJpcyRTZXBhbC5XaWR0aA0Kc2l6ZS5wZXRhbCA9IGlyaXMkUGV0YWwuV2lkdGgNCnNwZWNpZXMgPSBpcmlzJFNwZWNpZXMNCiMjIGlkZW50aWZ5aW5nIHRoZSBJRCBvZiBWaXJnaW5pY2ENCnZpcmdpbmNhLmlkID0gd2hpY2goc3BlY2llcz09InZpcmdpbmljYSIpICAjIHZhbHVlIGFyZSBjYXNlIHNlbnNpdGl2ZSENCnNldG9zYS5pZCA9IHdoaWNoKHNwZWNpZXM9PSJzZXRvc2EiKQ0KdmVyc2ljb2xvci5pZCA9IHdoaWNoKHNwZWNpZXM9PSJ2ZXJzaWNvbG9yIikNCiMjIGNvbG9yIGNvZGUNCiMgIGNvbC5jb2RlID0gYyhhbHBoYSgicmVkIiwwLjUpLGFscGhhKCJibHVlIiwwLjUpLGFscGhhKCJjeWFuIiwwLjUpKQ0KIyMgbWFraW5nIGFuIGVtcHR5IHBsb3Q6IHR5cGUgPSAibiIgPT0+IG5vIHBvaW50DQpwbG90KHNlcGFsLmxlbmd0aCwgc2VwYWwud2lkdGgsIG1haW4gPSAiU2VwYWwgbGVnbnRoIHZzIHNlcGFsIHdpZHRoIiwgdHlwZSA9ICJuIikNCiMjIGNoYW5nZSB0aGUgcG9pbnQgc2l6ZSBiYXNlZCBvbiB0aGVpciBhdmVyYWdlIG9mIHNlcGFsIGxlbmd0aCBhbmQgd2lkdGgNCnBvaW50cyhzZXBhbC5sZW5ndGhbdmlyZ2luY2EuaWRdLCBzZXBhbC53aWR0aFt2aXJnaW5jYS5pZF0sIA0KICAgICAgIHBjaCA9IDE5LCBjb2wgPSAicHVycGxlIiwgY2V4ID0gc2l6ZS5wZXRhbFt2aXJnaW5jYS5pZF0sIGFscGhhID0gMC42KQ0KcG9pbnRzKHNlcGFsLmxlbmd0aFtzZXRvc2EuaWRdLCBzZXBhbC53aWR0aFtzZXRvc2EuaWRdLCANCiAgICAgICBwY2ggPSAxOSwgY29sID0gIm5hdnkiLCBjZXggPSBzaXplLnBldGFsW3NldG9zYS5pZF0sIGFscGhhID0gMC42KQ0KcG9pbnRzKHNlcGFsLmxlbmd0aFt2ZXJzaWNvbG9yLmlkXSwgc2VwYWwud2lkdGhbdmVyc2ljb2xvci5pZF0sIA0KICAgICAgIHBjaCA9IDE5LCBjb2wgPSAiY3lhbiIsIGNleCA9IHNpemUucGV0YWxbdmVyc2ljb2xvci5pZF0sIGFscGhhID0gMC42KQ0KbGVnZW5kKCJ0b3BsZWZ0IiwgYygidmlyZ2luaWNhIiwgInNldG9zYSIsICJ2ZXJzaWNvbG9yIiksIA0KICAgICAgICAgICAgICAgICAgY29sPWMoInB1cnBsZSIsICJuYXZ5IiwgImN5YW4iKSwNCiAgICAgICAgICAgICAgICAgIHBjaD1jKDE5LCAxOSwgMTkpKQ0KIyMgdmFyaW91cyBhbm5vdGF0aW9ucw0KI3NwZWNpZnkgdGhlIHBvc2l0aW9uIG9mIHRoZSBpbWFnZSB0aHJvdWdoIGJvdHRvbS1sZWZ0IGFuZCB0b3AtcmlnaHQgY29vcmRzDQpyYXN0ZXJJbWFnZShyYXN0ZXIuaXJpcyw2LDMuNSw3LDQuNCkNCnRleHQoNy4yNSwgMi4yNSwgIlRoZSBzaXplIGlzIHByb3BvcnRpb25hbCB0byBwZXRhbCBsZW5ndGgiLCBjb2wgPSAicHVycGxlIiwgY2V4ID0gMC41KQ0KYGBgDQoNCg0KIyBXaGF0IFN0b3J5IFRvIFRlbGw/DQoNCldlIGhhdmUgZGlzY3Vzc2VkIHZhcmlvdXMgZ3JhcGhpYyBmdW5jdGlvbnMgYW5kIHRlY2huaXF1ZXMgaW4gYmFzZSBSIHRvIGNyZWF0ZSBncmFwaGljIHJlcHJlc2VudGF0aW9ucyBvZiBkYXRhLiBUbyBjb25jbHVkZSB0aGlzIG5vdGUsIGxldCdzIGxvb2sgYXQgd2hhdCBpbmZvcm1hdGlvbiBnZW5lcmFsIGF1ZGllbmNlIG1pZ2h0IGJlIGludGVyZXN0ZWQgaW4uDQoNCiMjIFJlbGF0aW9uc2hpcCBiZXR3ZWVuIHZhcmlhYmxlcz8NCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD03LCBmaWcuY2FwPSJQYWlyLXdpc2UgU2NhdHRlciBQbG90IG9mIFNpemVzIG9mIElyaXMifQ0KcGFpcnMoaXJpc1ssIC01XSkNCmBgYA0KDQojIyBEaXN0cmlidXRpb24gb2YgdmFyaWFibGVzPw0KDQoNCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD03LCBmaWcuY2FwPSJDb21wYXJpc29ucyBvZiBkaXN0cmlidXRpb25zIn0NCiMjIERhdGEgUGFydGl0aW9uDQpTZXBhbC5MLnNldCA9IGlyaXMkU2VwYWwuTGVuZ3RoW3doaWNoKGlyaXMkU3BlY2llcz09InNldG9zYSIpXQ0KU2VwYWwuTC52ZXIgPSBpcmlzJFNlcGFsLkxlbmd0aFt3aGljaChpcmlzJFNwZWNpZXM9PSJ2ZXJzaWNvbG9yIildDQpTZXBhbC5MLnZpciA9IGlyaXMkU2VwYWwuTGVuZ3RoW3doaWNoKGlyaXMkU3BlY2llcz09InZpcmdpbmljYSIpXQ0KIw0KU2VwYWwuVy5zZXQgPSBpcmlzJFNlcGFsLldpZHRoW3doaWNoKGlyaXMkU3BlY2llcz09InNldG9zYSIpXQ0KU2VwYWwuVy52ZXIgPSBpcmlzJFNlcGFsLldpZHRoW3doaWNoKGlyaXMkU3BlY2llcz09InZlcnNpY29sb3IiKV0NClNlcGFsLlcudmlyID0gaXJpcyRTZXBhbC5XaWR0aFt3aGljaChpcmlzJFNwZWNpZXM9PSJ2aXJnaW5pY2EiKV0NCiMNClBldGFsLkwuc2V0ID0gaXJpcyRQZXRhbC5MZW5ndGhbd2hpY2goaXJpcyRTcGVjaWVzPT0ic2V0b3NhIildDQpQZXRhbC5MLnZlciA9IGlyaXMkUGV0YWwuTGVuZ3RoW3doaWNoKGlyaXMkU3BlY2llcz09InZlcnNpY29sb3IiKV0NClBldGFsLkwudmlyID0gaXJpcyRQZXRhbC5MZW5ndGhbd2hpY2goaXJpcyRTcGVjaWVzPT0idmlyZ2luaWNhIildDQojDQpQZXRhbC5XLnNldCA9IGlyaXMkUGV0YWwuV2lkdGhbd2hpY2goaXJpcyRTcGVjaWVzPT0ic2V0b3NhIildDQpQZXRhbC5XLnZlciA9IGlyaXMkUGV0YWwuV2lkdGhbd2hpY2goaXJpcyRTcGVjaWVzPT0idmVyc2ljb2xvciIpXQ0KUGV0YWwuVy52aXIgPSBpcmlzJFBldGFsLldpZHRoW3doaWNoKGlyaXMkU3BlY2llcz09InZpcmdpbmljYSIpXQ0KIyMjDQpwYXIobWZyb3c9YygyLDIpKQ0KIyMjDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAgIHBsb3QgIzE6IA0KcGxvdChkZW5zaXR5KFNlcGFsLkwuc2V0KSwgY29sPSJicm93bjQiLCBsdHk9MSwgbHdkPTIsICB4bGltPWMoMyw5KSwgeWxpbT1jKDAsMS44KSwgeGxhYj0iU2VwYWwgTGVuZ3RoIiwgbWFpbj0iRGlzdHJpYnV0aW9ucyBTZXBhbCBMZW5ndGgiKQ0KbGluZXMoZGVuc2l0eShTZXBhbC5MLnZlciksIGNvbD0iYmx1ZSIsIGx0eT0xLCBsd2Q9MikNCmxpbmVzKGRlbnNpdHkoU2VwYWwuTC52aXIpLCBjb2w9ImRhcmtjeWFuIiwgbHR5PTEsIGx3ZD0yKQ0KbGVnZW5kKCJ0b3ByaWdodCIsIGMoInNldG9zYSIsICJ2ZXJzaWNvbG9yIiwidmlyZ2luaWNhIiksDQogICAgICAgY29sPWMoImJyb3duNCIsImJsdWUiLCJkYXJrY3lhbiIpLCBsdHk9YygxLDEsMSksDQogICAgICAgbHdkPWMoMSwxLDEpLCBidHk9Im4iLCBjZXg9MC43KQ0KIyMNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjICAgcGxvdCAjMTogDQpwbG90KGRlbnNpdHkoU2VwYWwuVy5zZXQpLCBjb2w9ImJyb3duNCIsIGx0eT0xLCBsd2Q9MiwgIHhsaW09YygxLDUpLCB5bGltPWMoMCwyKSwgeGxhYj0iU2VwYWwgV2lkdGgiLCBtYWluPSJEaXN0cmlidXRpb25zIFNlcGFsIFdpZHRoIikNCmxpbmVzKGRlbnNpdHkoU2VwYWwuVy52ZXIpLCBjb2w9ImJsdWUiLCBsdHk9MSwgbHdkPTIpDQpsaW5lcyhkZW5zaXR5KFNlcGFsLlcudmlyKSwgY29sPSJkYXJrY3lhbiIsIGx0eT0xLCBsd2Q9MikNCmxlZ2VuZCgidG9wcmlnaHQiLCBjKCJzZXRvc2EiLCAidmVyc2ljb2xvciIsInZpcmdpbmljYSIpLA0KICAgICAgIGNvbD1jKCJicm93bjQiLCJibHVlIiwiZGFya2N5YW4iKSwgbHR5PWMoMSwxLDEpLA0KICAgICAgIGx3ZD1jKDEsMSwxKSwgYnR5PSJuIiwgY2V4PTAuNykNCiMjDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAgIHBsb3QgIzE6IA0KcGxvdChkZW5zaXR5KFBldGFsLkwuc2V0KSwgY29sPSJicm93bjQiLCBsdHk9MSwgbHdkPTIsICB4bGltPWMoMSw4KSwgeWxpbT1jKDAsMi44KSwgeGxhYj0iUGV0YWwgTGVuZ3RoIiwgbWFpbj0iRGlzdHJpYnV0aW9ucyBQZXRhbCBMZW5ndGgiKQ0KbGluZXMoZGVuc2l0eShQZXRhbC5MLnZlciksIGNvbD0iYmx1ZSIsIGx0eT0xLCBsd2Q9MikNCmxpbmVzKGRlbnNpdHkoUGV0YWwuTC52aXIpLCBjb2w9ImRhcmtjeWFuIiwgbHR5PTEsIGx3ZD0yKQ0KbGVnZW5kKCJ0b3ByaWdodCIsIGMoInNldG9zYSIsICJ2ZXJzaWNvbG9yIiwidmlyZ2luaWNhIiksDQogICAgICAgY29sPWMoImJyb3duNCIsImJsdWUiLCJkYXJrY3lhbiIpLCBsdHk9YygxLDEsMSksDQogICAgICAgbHdkPWMoMSwxLDEpLCBidHk9Im4iLCBjZXg9MC43KQ0KIyMNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjICAgcGxvdCAjMTogDQpwbG90KGRlbnNpdHkoUGV0YWwuVy5zZXQpLCBjb2w9ImJyb3duNCIsIGx0eT0xLCBsd2Q9MiwgIHhsaW09YygwLDMpLCB5bGltPWMoMCw4KSwgeGxhYj0iUGV0YWwgV2lkdGgiLCBtYWluPSJEaXN0cmlidXRpb25zIFBldGFsIFdpZHRoIikNCmxpbmVzKGRlbnNpdHkoUGV0YWwuVy52ZXIpLCBjb2w9ImJsdWUiLCBsdHk9MSwgbHdkPTIpDQpsaW5lcyhkZW5zaXR5KFBldGFsLlcudmlyKSwgY29sPSJkYXJrY3lhbiIsIGx0eT0xLCBsd2Q9MikNCmxlZ2VuZCgidG9wcmlnaHQiLCBjKCJzZXRvc2EiLCAidmVyc2ljb2xvciIsInZpcmdpbmljYSIpLA0KICAgICAgIGNvbD1jKCJicm93bjQiLCJibHVlIiwiZGFya2N5YW4iKSwgbHR5PWMoMSwxLDEpLA0KICAgICAgIGx3ZD1jKDEsMSwxKSwgYnR5PSJuIiwgY2V4PTAuNykNCmBgYA0KDQoNCg0KIyMgTW9kZWxpbmcgLSBDbGFzc2lmaWNhdGlvbiBhbmQgUmVncmVzc2lvbj8NCg0KV2hlbiBidWlsZGluZyByZWdyZXNzaW9uIG1vZGVscywgb25lIG9mIHRoZSBpc3N1ZXMgaXMgdGhlIHBvdGVudGlhbCBjb2xsaW5lYXJpdHkuIA0KDQokXHBpXzEgPSBQcltZID0gXHRleHR7c2V0b3NhfV0kLCAkXHBpXzIgPSBQcltZID0gXHRleHR7dmVyc2ljb2xvcn1dJCwgYW5kICRccGlfMSA9IFByW1kgPSBcdGV4dHt2aXJnaW5pY2F9XSQuIFRoZSB3ZWxsIGtub3duIGJhc2VsaW5lIGxvZ2l0IG1vZGVsIGlzIGRlZmluZWQgdG8gYmUgdGhlIGZvbGxvd2luZyBzeXN0ZW0gb2YgdHdvIG9kZHMgcmVncmVzc2lvbiBtb2RlbHMNCg0KJCQNClxsb2coXHBpXzIvIFxwaV8xKSA9IFxhbHBoYV8wICsgXGFscGhhXzF4XzEgKyBcY2RvdHMgKyBcYWxwaGFfa3hfaw0KJCQNCiQkDQpcbG9nKFxwaV8zLyBccGlfMSkgPSBcYmV0YV8wICsgXGJldGFfMXhfMSArIFxjZG90cyArIFxiZXRhX2t4X2sNCiQkDQpOb3RlIHRoYXQgJFxwaV8xICsgXHBpXzIgKyBccGlfMyA9IDEkLiBTb2x2ZSBmb3IgJFxwaV8xLCBccGlfMiQsIGFuZCAkXHBpXzMkLCB3ZSBoYXZlIA0KDQokJA0KXHBpXzEgPSBcZnJhY3sxfXsxK1xleHAoXGFscGhhXzAgKyBcYWxwaGFfMXhfMSArIFxjZG90cyArIFxhbHBoYV9reF9rKSArIFxleHAoXGJldGFfMCArIFxiZXRhXzF4XzEgKyBcY2RvdHMgKyBcYmV0YV9reF9rKX0NCiQkDQoNCiQkDQpccGlfMiA9IFxmcmFje1xleHAoXGFscGhhXzAgKyBcYWxwaGFfMXhfMSArIFxjZG90cyArIFxhbHBoYV9reF9rKX17MStcZXhwKFxhbHBoYV8wICsgXGFscGhhXzF4XzEgKyBcY2RvdHMgKyBcYWxwaGFfa3hfaykgKyBcZXhwKFxiZXRhXzAgKyBcYmV0YV8xeF8xICsgXGNkb3RzICsgXGJldGFfa3hfayl9DQokJA0KDQokJA0KXHBpXzMgPSBcZnJhY3tcZXhwKFxiZXRhXzAgKyBcYmV0YV8xeF8xICsgXGNkb3RzICsgXGJldGFfa3hfayl9ezErXGV4cChcYWxwaGFfMCArIFxhbHBoYV8xeF8xICsgXGNkb3RzICsgXGFscGhhX2t4X2spICsgXGV4cChcYmV0YV8wICsgXGJldGFfMXhfMSArIFxjZG90cyArIFxiZXRhX2t4X2spfQ0KJCQNCg0KDQoNCmBgYHtyIGVjaG89RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCiMgRml0IG11bHRpbm9taWFsIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgDQpmaXQgPC0gbXVsdGlub20oU3BlY2llcyB+ICBTZXBhbC5MZW5ndGggDQogICAgICAgICAgICAgICAgICArIFNlcGFsLldpZHRoDQogICAgICAgICAgICAgICAgICArIFBldGFsLkxlbmd0aCANCiAgICAgICAgICAgICAgICAgICsgUGV0YWwuV2lkdGggLCANCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBpcmlzLCB0cmFjZSA9IEZBTFNFKQ0Ka2FibGUoY29lZmZpY2llbnRzKGZpdCkpDQoNCmBgYA0KDQoNCkhvdyB0byBpbnRlcnByZXQgdGhlIHJlc3VsdHM/IENhbiB3ZSBtYWtlIGEgdmlzdWFsIHJlcHJlc2VudGF0aW9uIG9mIHRoZSByZXN1bHRpbmcgbW9kZWw/IEhvdz8NCg0KVGhlIGZvbGxvd2luZyBpcyBvbmUgZXhhbXBsZSBvZiByZXByZXNlbnRhdGlvbiBvZiBob3cgaW5kaXZpZHVhbCBmZWF0dXJlcyBpbXBhY3QgbWVtYmVyc2hpcCBwcmVkaWN0aW9uIChjbGFzc2lmaWNhdGlvbikuDQoNCmBgYHtyfQ0Kc2V0b3NhID0gc3VtbWFyeShpcmlzW3doaWNoKGlyaXMkU3BlY2llcz09InNldG9zYSIpLF0pDQp2ZXJzaWNvbG9yID0gc3VtbWFyeShpcmlzW3doaWNoKGlyaXMkU3BlY2llcz09InZlcnNpY29sb3IiKSxdKQ0KdmlyZ2luaWNhID0gc3VtbWFyeShpcmlzW3doaWNoKGlyaXMkU3BlY2llcz09InZpcmdpbmljYSIpLF0pDQpsaXN0KHNldG9zYSA9IHNldG9zYSwgdmVyc2ljb2xvciA9IHZlcnNpY29sb3IsIHZpcmdpbmljYSA9IHZpcmdpbmljYSkNCmBgYA0KDQpOZXh0LCB3ZSBzaG93IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBgU2VwYWwuTGVuZ3RoYCBhbmQgdGhlIGNsYXNzaWZpY2F0aW9uIHByb2JhYmlsaXRpZXMgJFxwaV8xLCBccGlfMiQgYW5kICRccGlfMyQgYnkgZml4aW5nIHRoZSB2YWx1ZXMgb2Ygb3RoZXIgdmFyaWFibGVzIGF0IHRoZWlyIGNvcnJlc3BvbmRpbmcgbWVhbnMuDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NX0NClNlcGFsTGVuZ3RoID0gc2VxKDQsIDksIGxlbmd0aD01MCkNCnBpMDEgPSAxLygxK2V4cCgxOC42OSAtIDUuNDYqU2VwYWxMZW5ndGggLSA4LjcxKjMgKyAxNC4yNSo0IC0gMy4xMCoxKSsgZXhwKC0yMy44NCAgLSA3LjkyKipTZXBhbExlbmd0aCAgIC0gMTUuMzcqMyArIDIzLjY2KjQgICsgMTUuMTQqMSApKQ0KIyMNCnBpMDIgPSBleHAoMTguNjkgLSA1LjQ2KlNlcGFsTGVuZ3RoIC0gOC43MSozICsgMTQuMjUqNCAtIDMuMTAqMSApLygxK2V4cCgxOC42OSAtIDUuNDYqU2VwYWxMZW5ndGggLSA4LjcxKjMgKyAxNC4yNSo0IC0gMy4xMCoxICkrIGV4cCgtMjMuODQgLSA3LjkyKipTZXBhbExlbmd0aCAgLSAxNS4zNyozICsyMy42Nio0ICsgMTUuMTQqMSAgKSkNCiMjDQojIw0KcGkwMyA9IGV4cCgtMjMuODQgIC0gNy45MioqU2VwYWxMZW5ndGggLSAxNS4zNyozICsyMy42Nio0ICsgMTUuMTQqMSApLygxK2V4cCgxOC42OSAtIDUuNDYqU2VwYWxMZW5ndGggLSA4LjcxKjMgKyAxNC4yNSo0IC0gMy4xMCoxKSsgZXhwKC0yMy44NCAgLSA3LjkyKipTZXBhbExlbmd0aCAtIDE1LjM3KjMgKzIzLjY2KjQgKyAxNS4xNCoxICkpDQojIw0KI3BhcihtZnJvdz1jKDEsMykpDQpwbG90KFNlcGFsTGVuZ3RoLCBwaTAxLCBtYWluPSJTZXRvc2EiLA0KICAgICB0eXBlID0gImwiLA0KICAgICB5bGltID0gYygwLDEpLA0KICAgICB4bGFiID0gIlNlcGFsIExlbmd0aCIsDQogICAgIHlsYWIgPSAiQ2xhc3NpZmljYXRpb24gUHJvYmFiaWxpdHkiLA0KICAgICBjb2wgPSAiYmx1ZSIsDQogICAgIGx3ZCA9IDIpDQpsaW5lcyhTZXBhbExlbmd0aCwgcGkwMiwgbWFpbiA9ICJWZXJzaWNvbG9yIiwgeWxhYj0iQ2xhc3NpZmljYXRpb24gUHJvYmFiaWxpdHkiLA0KICAgICB0eXBlID0gImwiLCBjb2wgPSAicHVycGxlIiwgbHdkID0gMikNCmxpbmVzKFNlcGFsTGVuZ3RoLCBwaTAzLCBtYWluID0gIlZpcmdpbmljYSIsICB5bGFiPSJDbGFzc2lmaWNhdGlvbiBQcm9iYWJpbGl0eSIsDQogICAgIHR5cGU9ImwiLCBjb2wgPSAiYnJvd240IiwgbHdkID0gMikNCmFibGluZSh2PTguNTEsIGx0eSA9IDIpDQpwb2ludHMoOC41MSwgMC40OTUsIHBjaD0xOSwgY29sID0gImRhcmtyZWQiLCBkZXggPSAxLjUpDQp0ZXh0KDgsIDAuNDk1LCAiKDguNTEsIDAuNDk1KSIsIGNleCA9IDAuNikNCmxlZ2VuZCgibGVmdCIsIGMoIlNldG9zYSIsICJWZXJzaWNvbG9yIiwgIlZpcmdpbmljYSIpLCBsd2Q9cmVwKDIsMyksDQogICAgICAgY29sPWMoImJsdWUiLCAicHVycGxlIiwgImJyb3duIiksIGx0eSA9IHJlcCgxLDMpLCBjZXg9MC45LCBidHk9Im4iKQ0KYGBgDQpUaGUgYWJvdmUgdGhyZWUgZmlndXJlcyBzaG93IGhvdyBgc2VwYWwgbGVuZ3RoYCBpcyBhc3NvY2lhdGVkIHdpdGggdGhlIHRocmVlIGNsYXNzaWZpY2F0aW9uIHByb2JhYmlsaXRpZXMuIEZvciBleGFtcGxlLCBhbW9uZyBzZXRvc2EsIHVuZGVyIHRoZSBzYW1lIGNvbmRpdGlvbnMgKGkuZS4sIHNlcGFsIHdpZHRoLCBwZXRhbCBsZW5ndGgsIGFuZCBwZXRhbCB3aWR0aCBhcmUgdGhlIHNhbWUpLCBhcyBzZXBhbCBsZW5ndGggaW5jcmVhc2VzLCB0aGUgY2xhc3NpZmljYXRpb24gcHJvYmFiaWxpdHkgYWxzbyBpbmNyZWFzZXMuIFRoZSBvcHBvc2l0ZSBwYXR0ZXJuIGlzIG9ic2VydmVkIGFtb25nIHZlcnNpY29sb3IgaXJpcyBmbG93ZXJzLiBUaGUgc2VwYWwgbGVuZ3RoIGFuZCB0aGUgY2xhc3NpZmljYXRpb24gcHJvYmFiaWxpdHkgYXJlIG5vdCBhc3NvY2lhdGVkLg0KDQpGb3IgYWxsIGlyaXMgZmxvd2VycyB3aXRoIFNlcGFsIFdpZHRoIDMsIFBldGFsIExlbmd0aCA0LCBhbmQgUGV0YWwgV2lkdGggMS41LCB3aGVuIFNlcGFsIExlbmd0aCA8IDguNTEsICRQKFx0ZXh0e1ZlcnNpY29sb3J9KSA+IFAoXHRleHR7U2V0b3NhfSkkIG1lYW5pbmcgdGhhdCBpdCBpcyBtb3JlIGxpa2VseSB0byBiZSBgVmVyc2ljb2xvciBpcmlzYC4gT3RoZXJ3aXNlLCBpdCBpcyBtb3JlIGxpa2VseSB0byBiZSBgc2V0b3NhYC4gU2VwYWwgbGVuZ3RoIGRvZXMgbm90IGhhdmUgKnByZWRpY3RpdmUgcG93ZXIqIGZvciBgdmlyZ2luaWNhYC4NCg0KV2UgY2FuIGFsc28gbG9vayBhdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIGNsYXNzaWZpY2F0aW9uIHByb2JhYmlsaXR5IGFuZCBvdGhlciBudW1lcmljYWwgdmFyaWFibGVzIGluIHRoZSBkYXRhIHNldCBpbiBhIHNpbWlsYXIgd2F5Lg0KDQoNCiMjIENsdXN0ZXJpbmc/DQoNCkFub3RoZXIgaW50ZXJlc3RpbmcgcHJvYmxlbSB3ZSBtYXkgZXhwbG9yZSBpcyB0aGUgc2VlIHdoZXRoZXIgdGhlcmUgYXJlIHNvbWUgY2x1c3RlcnMgYmFzZWQgb24gdGhlIGZvdXIgbnVtZXJpY2FsIHZhcmlhYmxlcy4gVGhlcmUgYXJlIG1hbnkgY2x1c3RlcmluZyBhbGdvcml0aG1zIGZvciBwcmFjdGl0aW9uZXJzLiBGb3IgaWxsdXN0cmF0aXZlIHB1cnBvc2VzLCB3ZSB1c2UgdGhlIHBvcHVsYXIgay1tZWFucyBhbGdvcml0aG0gaW4gdGhlIGZvbGxvd2luZyBleGFtcGxlLg0KDQoNCmBgYHtyfQ0KdG90LndpdGhpbnNzIDwtIHZlY3Rvcihtb2RlPSJjaGFyYWN0ZXIiLCBsZW5ndGg9MTApDQpmb3IgKGkgaW4gMToxMCl7DQogIGlyaXNDbHVzdGVyIDwtIGttZWFucyhpcmlzWywxOjRdLCBjZW50ZXI9aSwgbnN0YXJ0PTIwKQ0KICB0b3Qud2l0aGluc3NbaV0gPC0gaXJpc0NsdXN0ZXIkdG90LndpdGhpbnNzDQp9DQpwbG90KDE6MTAsIHRvdC53aXRoaW5zcywgbWFpbj0iRWxib3cgUGxvdCBmb3IgT3B0aW1hbCBDbHVzdGVyIERldGVybWluYXRpb24iLA0KICAgICB0eXBlPSJiIiwgcGNoPTE5KQ0KYGBgDQoNCg0KYGBge3J9DQppcmlzQ2x1c3RlciA8LSBrbWVhbnMoaXJpc1ssMTo0XSwgY2VudGVyPTMsIG5zdGFydD0yMCkNCmlyaXNDbHVzdGVyDQpgYGANCg0KYGBge3J9DQpjbHVzcGxvdChpcmlzLCBpcmlzQ2x1c3RlciRjbHVzdGVyLCBjb2xvcj1ULCBzaGFkZT1ULCBsYWJlbHM9MCwgbGluZXM9MCkNCmBgYA0KDQoNCg0KDQoNCg==