Graphs are often the starting point for statistical analysis. One of the main advantages of R is how easy it is for the user to create many different kinds of graphs. We begin this chapter by studying conventional graphs, followed by an examination of some more complex representations. This final part uses the ggplot2 package.

1. Conventional Graphical Functions

To begin with, it may be interesting to examine a few example of graphical representations which can be constructed with R. We use the demo function:

demo(graphics)

The plot function

The plot function is a generic function used to represent all kinds of data. Classical use of the plot function consists of representing a scatterplot for a variable y according to another variable x. For example, to represent the graph of the function \(x\mapsto \sin(2\pi x)\) on \([0,1]\), at regular steps we use the following commands:

x <- seq(-2*pi,2*pi,by=0.05)
y <- sin(x)
plot(x,y) #dot representation (default)
plot(x,y,type="l") #line representation

We provide examples of representations for quantitative and qualitative variables. We use the data file ozone.txt, imported using

setwd("~/Dropbox/LAURENT/COURS/EDHEC/R/FICHES")
path <- file.path("../DATA", "ozone.txt") 
ozone <- read.table(path)
summary(ozone)

Let us start by representing two quantitative variables: maximum ozone maxO3 according to temperature T12:

plot(ozone[,"T12"],ozone[,"maxO3"])

As the two variables are contained and named within the same table, a simpler syntax can be used, which automatically inserts the variables as labels for the axes:

plot(maxO3~T12,data=ozone)

We can also use (more complicated)

plot(ozone[,"T12"],ozone[,"maxO3"],xlab="T12",ylab="maxO3")

Functions histogram, barplot and boxplot allow to draw classical graphs:

hist(ozone$maxO3,main="Histogram")
barplot(table(ozone$wind)/nrow(ozone),col="blue")
boxplot(maxO3~wind,data=ozone)

Interactive graphs with rAmCharts

We can use this package to obtain dynamic graphs. It is easy, we just have to use the prefix am beforme the name of the function:

library(rAmCharts)
amHist(ozone$maxO3)
amPlot(ozone,col=c("T9","T12"))
amBoxplot(maxO3~wind,data=ozone)

Exercise 1

  • Draw the sine function between 0 and \(2\pi\).
  • Add the following title: plot of sine function.

Exercise 2

  • Draw the pdf (probability distribution function) of the standard Gaussian distribution between \(-4\) and 4 (use dnorm).
  • Add a vertical dashed line of equation \(x=0\) (use abline)
  • On the same graph, draw Student’s \(t\)-distribution to 5 and 30 degrees of freedom (use dt). Use the lines function and a different colour for each line.
  • Add a legend at the top left to differentiate between each distribution (use legend).

Exercise 3 (Law of Large Numbers) - optional

  • Having set the seed of the random generator set.seed, simulate a sample \((x_1,...,x_{1000})\) from Bernoulli’s distribution with parameter \(p=0.6\) (use rbinom).
  • Calculate the successive means \(M_l=S_l/l\) where \(S_l=\sum_{i=1}^l X_i\). Draw \(M_l\) according to \(l\), then add the horizontal line with equation \(y=0.6\) (use cumsum).

Exercise 4 (Central Limit Theorem) - optional

  • Let us denote \(X_1,X_2,\cdots,X_N\) i.i.d. random variables following Bernoulli’s distribution with parameter \(p\). Recall the distribution of \(S_N=X_1+\ldots + X_N\). Specify the mean and standard deviation.
  • Set \(p=0.5\). For \(N=10\), using the rbinom function, simulate \(n=1000\) occurrences \(S_1,\cdots,S_{1000}\) of a binomial distribution with parameters \(N\) and \(p\). Organise the quantities \(\frac{S_i-N\times p}{\sqrt{N\times p\times (1-p)}}\) into a vector U10. * Do the same with \(N=30\) and \(N=1000\) to obtain two new vectors U30 and U1000.
  • In one window (use par(mfrow=c(1,3))) represent histograms for U10, U30 and U1000, each time overlapping the density of the standard Gaussian distribution (obtained using dnorm).

2. Ggplot2

ggplot2 is a plotting system for R based on the grammar of graphics (as dplyr to manipulate data). We can find documentation here. We consider a subsample of the diamond dataset from the package ggplot2:

library(ggplot2)
set.seed(1234)
diamonds2 <- diamonds[sample(nrow(diamonds),5000),] 
summary(diamonds2)
help(diamonds)

Given a dataset, a graph is defined from many layers. We have to specify:

Ggplot graphs are defined from these layers. We indicate

The scatterplot carat vs price is obtained with the plot function with

plot(price~carat,data=diamonds2)

With ggplot, we use

ggplot(diamonds2) #nothing
ggplot(diamonds2)+aes(x=carat,y=price) #nothing
ggplot(diamonds2)+aes(x=carat,y=price)+geom_point() #good

Exercise 5

  • Draw the histogram of carat (use geom_histogram)
  • Draw the histogram of carat with 10 bins (help(geom_histogram))
  • Draw the barplot for the variable cut (use geom_bar)

2.1 ggplot grammar

In ggplot, the syntax is defined from independent elements. These elements define the grammar of ggplot. Main elements of the grammar include:

  • Data (ggplot): the dataset, it should be a dataframe
  • Aesthetics (aes): to describe the way that variables in the data are mapped. All the variables used in the graph should be precise in aes
  • Geometrics (geom_…): to control the type of plot
  • Statistics (stat_…): to describe transformation of the data
  • Scales (scale_…): to control the mapping from data to aesthetic attributes (change of colors…)

All these elements are conbined with a +.

Data and aesthetics

These two elements specify the data and the variables we want to represent. For a scaterplot price vs carat we enter the command

ggplot(diamonds2)+aes(x=carat,y=price)

aes also use arguments such as color, size, fill. We use these arguments as soon as a color or a size is defined from a variable of the dataset:

ggplot(diamonds2)+aes(x=carat,y=price,color=cut)

Geometrics

To obtain the graph, we need to precise the type of representation. We use geometrics to do that. For a scatter plot, we use geom_point:

ggplot(diamonds2)+aes(x=carat,y=price,color=cut)+geom_point()

Observe that ggplot adds the lengend automatically. Exemples of geometrics are described here:

Geom Description Aesthetics
geom_point() Scatter plot x, y, shape, fill
geom_line() Line (ordered according to x) x, y, linetype
geom_abline() Line slope, intercept
geom_path() Line (ordered according to the index) x, y, linetype
geom_text() Text x, y, label, hjust, vjust
geom_rect() Rectangle xmin, xmax, ymin, ymax, fill, linetype
geom_polygon() Polygone x, y, fill, linetype
geom_segment() Segment x, y, fill, linetype
geom_bar() Barplot x, fill, linetype, weight
geom_histogram() Histogram x, fill, linetype, weight
geom_boxplot() Boxplots x, y, fill, weight
geom_density() Density x, y, fill, linetype
geom_contour() Contour lines x, y, fill, linetype
geom_smooth() Smoothers (linear or non linear) x, y, fill, linetype
All color, size, group

Exercise 6

  • Draw the barplot of cut (with blue bars)
  • Draw the barplot of cut with one color for each modality of cut.

Statistics (this part can be omitted for beginners)

Many graphs need to transform the data to make the representation (barplot, histogram). Simple transformations can be obtained quickly. For instance we can draw the sine function with

D <- data.frame(X=seq(-2*pi,2*pi,by=0.01))
ggplot(D)+aes(x=X,y=sin(X))+geom_line()

The sine transformation is precised in aes. For more complex transformations, we have to used statistics. A stat function takes a dataset as input and returns a dataset as output, and so a stat can add new variables to the original dataset. It is possible to map aesthetics to these new variables. For example, stat_bin, the statistic used to make histograms, produces the following variables:

  • count, the number of observations in each bin
  • density, the density of observations in each bin (percentage of total / bar width)
  • x, the center of the bin

By default geom_histogram represents on the \(y\)-axis the number of observations in each bin (the outuput count).

ggplot(diamonds)+aes(x=price)+geom_histogram(bins=40)

For the density, we use

ggplot(diamonds)+aes(x=price,y=..density..)+geom_histogram(bins=40)

ggplot propose another way to make the representations: we can use stat_ instead of geom_. Formally, each stat function has a geom and each geom has a stat. For instance,

ggplot(diamonds2)+aes(x=carat,y=price)+geom_smooth(method="loess")
ggplot(diamonds2)+aes(x=carat,y=price)+stat_smooth(method="loess")

lead to the same graph. We can change the type of representation in the stat_ with the argument geom:

ggplot(diamonds2)+aes(x=carat,y=price)+stat_smooth(method="loess",geom="point")

Here are some examples of stat functions

Stat Description Parameters
stat_identity() No transformation
stat_bin() Count binwidth, origin
stat_density() Density adjust, kernel
stat_smooth() Smoother method, se
stat_boxplot() Boxplot coef

stat and geom are not always easy to combine. For beginners, we recommand to only use geom.

Exercise 7

We consider a color variable \(X\) with probability distribution \[P(X=red)=0.3,\ P(X=blue)=0.2,\ P(X=green)=0.4,\ P(X=black)=0.1\] Draw the barplot of this distribution.

Scales

Scales control the mapping from data to aesthetic attributes (change of colors, sizes…). We generally use this element at the end of the process to refine the graph. Scales are defined as follows:

  • begin with scale_
  • add the aesthetics we want to modify (color, fill, x_)
  • end with the name of the scale (manual, identity…)

For instance,

ggplot(diamonds2)+aes(x=carat,y=price,color=cut)+geom_point()+
scale_color_manual(values=c("Fair"="black","Good"="yellow",
"Very Good"="blue","Premium"="red","Ideal"="green"))

Here are the main scales:

aes Discrete Continuous
Couleur (color et fill) brewer gradient
- grey gradient2
- hue gradientn
- identity
- manual
Position (x et y) discrete continous
- date
Forme shape
- identity
- manual
Taille identity size
- manual

Some examples:

  • color of a barplot
p1 <- ggplot(diamonds2)+aes(x=cut)+geom_bar(aes(fill=cut))
p1

We change colors by using the palette Purples :

p1+scale_fill_brewer(palette="Purples")
  • Gradient color for a scatter plot :
p2 <- ggplot(diamonds2)+aes(x=carat,y=price)+geom_point(aes(color=depth))
p2

We change the gradient color

p2+scale_color_gradient(low="red",high="yellow")
  • Change on the axis
p2+scale_x_continuous(breaks=seq(0.5,3,by=0.5))+scale_y_continuous(name="prix")+scale_color_gradient("Profondeur")

Group and facets

ggplot allows to make representations for subgroup of individuals. We can proceed in two ways:

  • to represent subgroup on the same graph, we use group in aes
  • to represent subgroup on the different graphs, we use facets

We can represent (on the same graph) the smoother price vs carat for each modality of cut with

ggplot(diamonds2)+aes(x=carat,y=price,group=cut,color=cut)+geom_smooth(method="loess")

To obtain the representation on many graphs, we use

ggplot(diamonds2)+aes(x=carat,y=price)+geom_smooth(method="loess")+facet_wrap(~cut)
ggplot(diamonds2)+aes(x=carat,y=price)+geom_smooth(method="loess")+facet_wrap(~cut,nrow=1)

facet_grid and facet_wrap do the same job but split the screen in different ways:

ggplot(diamonds2)+aes(x=carat,y=price)+geom_point()+geom_smooth(method="lm")+facet_grid(color~cut)
ggplot(diamonds2)+aes(x=carat,y=price)+geom_point()+geom_smooth(method="lm")+facet_wrap(color~cut)

2.2 Complements

Syntax for ggplot is defined according to the following scheme:

ggplot()+aes()+geom_()+scale_()

It is more flexible: for instance aes could be specified in ggplot or in geom_

ggplot(diamonds2)+aes(x=carat,y=price)+geom_point()
ggplot(diamonds2,aes(x=carat,y=price))+geom_point()
ggplot(diamonds2)+geom_point(aes(x=carat,y=price))

We can also built our graph with many datasets:

X <- seq(-2*pi,2*pi,by=0.001)
Y1 <- cos(X)
Y2 <- sin(X)
donnees1 <- data.frame(X,Y1)
donnees2 <- data.frame(X,Y2)
ggplot(donnees1)+geom_line(aes(x=X,y=Y1))+
geom_line(data=donnees2,aes(x=X,y=Y2),color="red")

Many other functions are proposed by ggplot:

  • ggtitle to add a title
  • ggsave ta save a graph
  • theme_ to change the theme of the graph
p <- ggplot(diamonds2)+aes(x=carat,y=price,color=cut)+geom_point()
p+theme_bw()
p+theme_classic()
p+theme_grey()
p+theme_bw()

Exercise 8

  1. Draw the sine and cosine funtions on the same graph. You first use two datasets (one for the sine function, the other for the cosine function).

  2. Do the same with one dataset. Hint: use the melt function of the package reshape2.

  3. Draw the two functions on two different graphs (use facet_wrap).

  4. Do the same with the function grid.arrange from the package gridExtra.

Exercise 9

We consider the dataset mtcars

data(mtcars)
summary(mtcars)
  1. Draw the histogram of mpg (use many number of bins)

  2. Represent the density on the \(y\)-axis.

  3. Draw the barplot of cyl.

  4. Draw the scatter plot disp vs mpg for each value of cyl (one color for each value of cyl).

  5. Add the linear smoother on each graph.

Exercise 10

  1. Draw the sine function on \([-2\pi,2\pi]\)
  2. Add the lines (in blue) of equation \(y=1\) and \(y=-1\). Use size=2.

Exercise 11

  1. Simulate a sample \((x_i,y_i),i=1,\dots,100\) according to the linear model \[Y_i=3+X_i+\varepsilon_i\] where \(X_i\) are i.i.d. and uniform on \([0,1]\) and \(\varepsilon_i\) are gaussian \(N(0,0.2^2)\) (use runif and rnorm)

  2. Draw the scatter plot y vs x and add the linear smoother.

  3. Draw the residuals: add a vertical line from each point to the linear smoother (use geom_segment).

Exercise 12 (optional)

File exo3_ggplot.dta contains \(n=100\) observations. We want to explain \(Y\) by the other variables with a linear model.

  1. Compute the linear model (lm function). What can we say about it?
D1 <- read.table("../DATA/exo3_ggplot.dta",header=T,sep=";")
m1 <- lm(Y~.,data=D1)
summary(m1)
  1. Calculate the partial residuals (residuals(…,type=“partial”)):
res <- data.frame(residuals(m1,type="partial"))
  1. Draw for each explanatory variable, the scatter plot of the partial residuals versus the variable. Add the smoother loess.

  2. Improve the linear model.

m2 <- lm(Y~X1+X2+X3+X4+I(X4^2),data=D1)
summary(m2)

Challenge (optional)

We consider the mtcars dataset (exercise 9).

  1. Obtain the following graphs (use coord_flip for the second graph).

  1. Add on the third graph a line for the quartiles of the variable carat (for each value of cut)

  2. Draw the following graph (use the ggstance package).

LS0tCnRpdGxlOiAiVmlzdWFsaXplIGRhdGEiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCkdyYXBocyBhcmUgb2Z0ZW4gdGhlIHN0YXJ0aW5nIHBvaW50IGZvciBzdGF0aXN0aWNhbCBhbmFseXNpcy4gIE9uZSBvZiB0aGUgbWFpbiBhZHZhbnRhZ2VzIG9mICoqUioqIGlzIGhvdyBlYXN5IGl0IGlzIGZvciB0aGUgdXNlciB0byBjcmVhdGUgbWFueSBkaWZmZXJlbnQga2luZHMgb2YgZ3JhcGhzLiBXZSBiZWdpbiB0aGlzIGNoYXB0ZXIgYnkgc3R1ZHlpbmcgY29udmVudGlvbmFsIGdyYXBocywgZm9sbG93ZWQgYnkgYW4gZXhhbWluYXRpb24gb2Ygc29tZSBtb3JlIGNvbXBsZXggcmVwcmVzZW50YXRpb25zLiBUaGlzIGZpbmFsIHBhcnQgdXNlcyB0aGUgKipnZ3Bsb3QyKiogcGFja2FnZS4KCiMgMS4gQ29udmVudGlvbmFsIEdyYXBoaWNhbCBGdW5jdGlvbnMKClRvIGJlZ2luIHdpdGgsIGl0IG1heSBiZSBpbnRlcmVzdGluZyB0byBleGFtaW5lIGEgZmV3IGV4YW1wbGUgb2YgZ3JhcGhpY2FsIHJlcHJlc2VudGF0aW9ucwp3aGljaCBjYW4gYmUgY29uc3RydWN0ZWQgd2l0aCAqKlIqKi4gV2UgdXNlIHRoZSAqKmRlbW8qKiBmdW5jdGlvbjoKYGBge3IsIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0KZGVtbyhncmFwaGljcykKYGBgCgojIyBUaGUgcGxvdCBmdW5jdGlvbgoKVGhlICoqcGxvdCoqIGZ1bmN0aW9uIGlzIGEgZ2VuZXJpYyBmdW5jdGlvbiB1c2VkIHRvIHJlcHJlc2VudCBhbGwga2luZHMgb2YgZGF0YS4gQ2xhc3NpY2FsIHVzZSBvZiB0aGUgKipwbG90KiogZnVuY3Rpb24gY29uc2lzdHMgb2YgcmVwcmVzZW50aW5nIGEgc2NhdHRlcnBsb3QgZm9yIGEgdmFyaWFibGUgKnkqIGFjY29yZGluZyB0byBhbm90aGVyIHZhcmlhYmxlICp4Ki4gRm9yIGV4YW1wbGUsIHRvIHJlcHJlc2VudCB0aGUgZ3JhcGggb2YgdGhlIGZ1bmN0aW9uCiR4XG1hcHN0byBcc2luKDJccGkgeCkkIG9uICRbMCwxXSQsIGF0IHJlZ3VsYXIgc3RlcHMgd2UgdXNlIHRoZQpmb2xsb3dpbmcgY29tbWFuZHM6CgpgYGB7cn0KeCA8LSBzZXEoLTIqcGksMipwaSxieT0wLjA1KQp5IDwtIHNpbih4KQpwbG90KHgseSkgI2RvdCByZXByZXNlbnRhdGlvbiAoZGVmYXVsdCkKcGxvdCh4LHksdHlwZT0ibCIpICNsaW5lIHJlcHJlc2VudGF0aW9uCmBgYAoKV2UgcHJvdmlkZSBleGFtcGxlcyBvZiByZXByZXNlbnRhdGlvbnMgZm9yIHF1YW50aXRhdGl2ZSBhbmQgcXVhbGl0YXRpdmUgdmFyaWFibGVzLiBXZSB1c2UgdGhlIGRhdGEgZmlsZSAqKm96b25lLnR4dCoqLCBpbXBvcnRlZCB1c2luZwoKYGBge3J9CnNldHdkKCJ+L0Ryb3Bib3gvTEFVUkVOVC9DT1VSUy9FREhFQy9SL0ZJQ0hFUyIpCnBhdGggPC0gZmlsZS5wYXRoKCIuLi9EQVRBIiwgIm96b25lLnR4dCIpIApvem9uZSA8LSByZWFkLnRhYmxlKHBhdGgpCnN1bW1hcnkob3pvbmUpCmBgYAoKTGV0IHVzIHN0YXJ0IGJ5IHJlcHJlc2VudGluZyB0d28gcXVhbnRpdGF0aXZlIHZhcmlhYmxlczogbWF4aW11bSBvem9uZSAqKm1heE8zKiogYWNjb3JkaW5nIHRvIHRlbXBlcmF0dXJlICoqVDEyKio6CmBgYHtyfQpwbG90KG96b25lWywiVDEyIl0sb3pvbmVbLCJtYXhPMyJdKQpgYGAKCkFzIHRoZSB0d28gdmFyaWFibGVzIGFyZSBjb250YWluZWQgYW5kIG5hbWVkIHdpdGhpbiB0aGUgc2FtZSB0YWJsZSwgYSBzaW1wbGVyIHN5bnRheCBjYW4gYmUgdXNlZCwgd2hpY2ggYXV0b21hdGljYWxseSBpbnNlcnRzIHRoZSB2YXJpYWJsZXMgYXMgbGFiZWxzIGZvciB0aGUgYXhlczoKYGBge3J9CnBsb3QobWF4TzN+VDEyLGRhdGE9b3pvbmUpCmBgYApXZSBjYW4gYWxzbyB1c2UgKG1vcmUgY29tcGxpY2F0ZWQpCmBgYHtyfQpwbG90KG96b25lWywiVDEyIl0sb3pvbmVbLCJtYXhPMyJdLHhsYWI9IlQxMiIseWxhYj0ibWF4TzMiKQpgYGAKRnVuY3Rpb25zICoqaGlzdG9ncmFtKiosICoqYmFycGxvdCoqIGFuZCAqKmJveHBsb3QqKiBhbGxvdyB0byBkcmF3IGNsYXNzaWNhbCBncmFwaHM6CmBgYHtyfQpoaXN0KG96b25lJG1heE8zLG1haW49Ikhpc3RvZ3JhbSIpCmJhcnBsb3QodGFibGUob3pvbmUkd2luZCkvbnJvdyhvem9uZSksY29sPSJibHVlIikKYm94cGxvdChtYXhPM353aW5kLGRhdGE9b3pvbmUpCmBgYAoKIyMgSW50ZXJhY3RpdmUgZ3JhcGhzIHdpdGggckFtQ2hhcnRzCgpXZSBjYW4gdXNlIHRoaXMgcGFja2FnZSB0byBvYnRhaW4gZHluYW1pYyBncmFwaHMuIEl0IGlzIGVhc3ksIHdlIGp1c3QgaGF2ZSB0byB1c2UgdGhlIHByZWZpeCAqKmFtKiogYmVmb3JtZSB0aGUgbmFtZSBvZiB0aGUgZnVuY3Rpb246CgpgYGB7cn0KbGlicmFyeShyQW1DaGFydHMpCmFtSGlzdChvem9uZSRtYXhPMykKYW1QbG90KG96b25lLGNvbD1jKCJUOSIsIlQxMiIpKQphbUJveHBsb3QobWF4TzN+d2luZCxkYXRhPW96b25lKQpgYGAKCgojIyMgRXhlcmNpc2UgMQoKKiBEcmF3IHRoZSBzaW5lIGZ1bmN0aW9uIGJldHdlZW4gMCBhbmQgJDJccGkkLgoqIEFkZCB0aGUgZm9sbG93aW5nIHRpdGxlOiAqKnBsb3Qgb2Ygc2luZSBmdW5jdGlvbioqLgoKCiMjIyBFeGVyY2lzZSAyCgoqIERyYXcgdGhlIHBkZiAocHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uKSBvZiB0aGUgc3RhbmRhcmQgR2F1c3NpYW4gZGlzdHJpYnV0aW9uIGJldHdlZW4gJC00JCBhbmQgNCAodXNlICoqZG5vcm0qKikuCiogQWRkIGEgdmVydGljYWwgZGFzaGVkIGxpbmUgb2YgZXF1YXRpb24gJHg9MCQgKHVzZSAqKmFibGluZSoqKQoqIE9uIHRoZSBzYW1lIGdyYXBoLCBkcmF3IFN0dWRlbnQncyAkdCQtZGlzdHJpYnV0aW9uIHRvIDUgYW5kIDMwIGRlZ3JlZXMgb2YgZnJlZWRvbSAodXNlICoqZHQqKikuIFVzZSB0aGUgKipsaW5lcyoqIGZ1bmN0aW9uCmFuZCBhIGRpZmZlcmVudCBjb2xvdXIgZm9yIGVhY2ggbGluZS4KKiBBZGQgYSBsZWdlbmQgYXQgdGhlIHRvcCBsZWZ0IHRvIGRpZmZlcmVudGlhdGUgYmV0d2VlbiBlYWNoIGRpc3RyaWJ1dGlvbiAodXNlICoqbGVnZW5kKiopLgoKCiMjIyBFeGVyY2lzZSAzIChMYXcgb2YgTGFyZ2UgTnVtYmVycykgLSBvcHRpb25hbAoKKiBIYXZpbmcgc2V0IHRoZSBzZWVkIG9mIHRoZSByYW5kb20gZ2VuZXJhdG9yICoqc2V0LnNlZWQqKiwgc2ltdWxhdGUgYSBzYW1wbGUgJCh4XzEsLi4uLHhfezEwMDB9KSQgZnJvbSBCZXJub3VsbGkncyBkaXN0cmlidXRpb24gd2l0aCBwYXJhbWV0ZXIgJHA9MC42JCAodXNlICoqcmJpbm9tKiopLgoqIENhbGN1bGF0ZSB0aGUgc3VjY2Vzc2l2ZSBtZWFucyAkTV9sPVNfbC9sJCB3aGVyZSAkU19sPVxzdW1fe2k9MX1ebCBYX2kkLiBEcmF3ICRNX2wkIGFjY29yZGluZyB0byAkbCQsIHRoZW4gYWRkIHRoZSBob3Jpem9udGFsIGxpbmUgd2l0aCBlcXVhdGlvbiAkeT0wLjYkICh1c2UgKipjdW1zdW0qKikuCgoKIyMjIEV4ZXJjaXNlIDQgKENlbnRyYWwgTGltaXQgVGhlb3JlbSkgLSBvcHRpb25hbAoKKiBMZXQgdXMgZGVub3RlICRYXzEsWF8yLFxjZG90cyxYX04kIGkuaS5kLiByYW5kb20gdmFyaWFibGVzIGZvbGxvd2luZyBCZXJub3VsbGkncyBkaXN0cmlidXRpb24gd2l0aCBwYXJhbWV0ZXIgJHAkLiBSZWNhbGwgdGhlIGRpc3RyaWJ1dGlvbiBvZiAkU19OPVhfMStcbGRvdHMgKyBYX04kLiBTcGVjaWZ5IHRoZSBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24uCiogU2V0ICRwPTAuNSQuIEZvciAkTj0xMCQsIHVzaW5nIHRoZSAqKnJiaW5vbSoqIGZ1bmN0aW9uLCBzaW11bGF0ZSAkbj0xMDAwJCBvY2N1cnJlbmNlcyAkU18xLFxjZG90cyxTX3sxMDAwfSQgb2YgYSBiaW5vbWlhbCBkaXN0cmlidXRpb24gd2l0aCBwYXJhbWV0ZXJzICROJCBhbmQgJHAkLiBPcmdhbmlzZSB0aGUgcXVhbnRpdGllcyAkXGZyYWN7U19pLU5cdGltZXMgcH17XHNxcnR7Tlx0aW1lcyBwXHRpbWVzICgxLXApfX0kIGludG8gYSB2ZWN0b3IgVTEwLiAqIERvIHRoZSBzYW1lIHdpdGggICROPTMwJCBhbmQgJE49MTAwMCQgdG8gb2J0YWluIHR3byBuZXcgdmVjdG9ycyBVMzAgYW5kIFUxMDAwLgoqIEluIG9uZSB3aW5kb3cgKHVzZSAqKnBhcihtZnJvdz1jKDEsMykpKiopIHJlcHJlc2VudCBoaXN0b2dyYW1zIGZvciBVMTAsIFUzMCBhbmQgVTEwMDAsIGVhY2ggdGltZSBvdmVybGFwcGluZyB0aGUgZGVuc2l0eSBvZiB0aGUgc3RhbmRhcmQgR2F1c3NpYW4gZGlzdHJpYnV0aW9uIChvYnRhaW5lZCB1c2luZyAqKmRub3JtKiopLgoKIyAyLiBHZ3Bsb3QyCgpnZ3Bsb3QyIGlzIGEgcGxvdHRpbmcgc3lzdGVtIGZvciBSIGJhc2VkIG9uIHRoZSBncmFtbWFyIG9mIGdyYXBoaWNzIChhcyAqKmRwbHlyKiogdG8gbWFuaXB1bGF0ZSBkYXRhKS4gV2UgY2FuIGZpbmQgZG9jdW1lbnRhdGlvbiBbaGVyZV0oaHR0cDovL2dncGxvdDIub3JnKS4gV2UgY29uc2lkZXIgYSBzdWJzYW1wbGUgb2YgdGhlIGRpYW1vbmQgZGF0YXNldCBmcm9tIHRoZSBwYWNrYWdlICoqZ2dwbG90MioqOgoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKc2V0LnNlZWQoMTIzNCkKZGlhbW9uZHMyIDwtIGRpYW1vbmRzW3NhbXBsZShucm93KGRpYW1vbmRzKSw1MDAwKSxdIApzdW1tYXJ5KGRpYW1vbmRzMikKaGVscChkaWFtb25kcykKYGBgCgpHaXZlbiBhIGRhdGFzZXQsIGEgZ3JhcGggaXMgZGVmaW5lZCBmcm9tIG1hbnkgKipsYXllcnMqKi4gV2UgaGF2ZSB0byBzcGVjaWZ5OgoKKiB0aGUgZGF0YQoqIHRoZSB2YXJpYWJsZXMgd2Ugd2FudCB0byBwbG90CiogdGhlIHR5cGUgb2YgcmVwcmVzZW50YXRpb24gKHNjYXR0ZXJwbG90LCBib3hwbG90Li4uKS4KCkdncGxvdCBncmFwaHMgYXJlIGRlZmluZWQgZnJvbSB0aGVzZSBsYXllcnMuIFdlIGluZGljYXRlCgoqIHRoZSBkYXRhIHdpdGggKipnZ3Bsb3QqKgoqIHRoZSB2YXJpYWJsZXMgd2l0aCAqKmFlcyoqIChhZXN0aGV0aWNzKQoqIHRoZSB0eXBlIG9mIHJlcHJlc2VudGF0aW9uIHdpdGggKipnZW9tXyoqCgpUaGUgc2NhdHRlcnBsb3QgKipjYXJhdCB2cyBwcmljZSoqIGlzIG9idGFpbmVkIHdpdGggdGhlICoqcGxvdCoqIGZ1bmN0aW9uIHdpdGgKYGBge3J9CnBsb3QocHJpY2V+Y2FyYXQsZGF0YT1kaWFtb25kczIpCmBgYAoKV2l0aCAqKmdncGxvdCoqLCB3ZSB1c2UKYGBge3J9CmdncGxvdChkaWFtb25kczIpICNub3RoaW5nCmdncGxvdChkaWFtb25kczIpK2Flcyh4PWNhcmF0LHk9cHJpY2UpICNub3RoaW5nCmdncGxvdChkaWFtb25kczIpK2Flcyh4PWNhcmF0LHk9cHJpY2UpK2dlb21fcG9pbnQoKSAjZ29vZApgYGAKCgojIyMgRXhlcmNpc2UgNQoKKiBEcmF3IHRoZSBoaXN0b2dyYW0gb2YgKipjYXJhdCoqICh1c2UgKipnZW9tX2hpc3RvZ3JhbSoqKQoqIERyYXcgdGhlIGhpc3RvZ3JhbSBvZiAqKmNhcmF0Kiogd2l0aCAxMCBiaW5zICgqKmhlbHAoZ2VvbV9oaXN0b2dyYW0pKiopCiogRHJhdyB0aGUgYmFycGxvdCBmb3IgdGhlIHZhcmlhYmxlICoqY3V0KiogKHVzZSAqKmdlb21fYmFyKiopCgojIyAyLjEgZ2dwbG90IGdyYW1tYXIKCkluIGdncGxvdCwgdGhlIHN5bnRheCBpcyBkZWZpbmVkIGZyb20gaW5kZXBlbmRlbnQgZWxlbWVudHMuIFRoZXNlIGVsZW1lbnRzIGRlZmluZSB0aGUgKipncmFtbWFyKiogb2YgKipnZ3Bsb3QqKi4gTWFpbiBlbGVtZW50cyBvZiB0aGUgZ3JhbW1hciBpbmNsdWRlOgoKKiAqKkRhdGEgKGdncGxvdCkqKjogdGhlIGRhdGFzZXQsIGl0IHNob3VsZCBiZSBhIGRhdGFmcmFtZQoqICoqQWVzdGhldGljcyAoYWVzKSoqOiB0byBkZXNjcmliZSB0aGUgd2F5IHRoYXQgdmFyaWFibGVzIGluIHRoZSBkYXRhIGFyZSBtYXBwZWQuIEFsbCB0aGUgdmFyaWFibGVzIHVzZWQgaW4gdGhlIGdyYXBoIHNob3VsZCBiZSBwcmVjaXNlIGluICoqYWVzKioKKiAqKkdlb21ldHJpY3MgKGdlb21fLi4uKSoqOiB0byBjb250cm9sIHRoZSB0eXBlIG9mIHBsb3QKKiAqKlN0YXRpc3RpY3MgKHN0YXRfLi4uKSoqOiB0byBkZXNjcmliZSB0cmFuc2Zvcm1hdGlvbiBvZiB0aGUgZGF0YQoqICoqU2NhbGVzIChzY2FsZV8uLi4pKio6IHRvIGNvbnRyb2wgdGhlIG1hcHBpbmcgZnJvbSBkYXRhIHRvIGFlc3RoZXRpYyBhdHRyaWJ1dGVzIChjaGFuZ2Ugb2YgY29sb3JzLi4uKQoKQWxsIHRoZXNlIGVsZW1lbnRzIGFyZSBjb25iaW5lZCB3aXRoIGEgKiorKiouCgojIyMgYERhdGEgYW5kIGFlc3RoZXRpY3NgCgpUaGVzZSB0d28gZWxlbWVudHMgc3BlY2lmeSB0aGUgZGF0YSBhbmQgdGhlIHZhcmlhYmxlcyB3ZSB3YW50IHRvIHJlcHJlc2VudC4gRm9yIGEgc2NhdGVycGxvdCAqKnByaWNlIHZzIGNhcmF0Kiogd2UgZW50ZXIgdGhlIGNvbW1hbmQKCmBgYHtyfQpnZ3Bsb3QoZGlhbW9uZHMyKSthZXMoeD1jYXJhdCx5PXByaWNlKQpgYGAKCioqYWVzKiogYWxzbyB1c2UgYXJndW1lbnRzIHN1Y2ggYXMgKipjb2xvcioqLCAqKnNpemUqKiwgKipmaWxsKiouIFdlIHVzZSB0aGVzZSBhcmd1bWVudHMgYXMgc29vbiBhcyBhIGNvbG9yIG9yIGEgc2l6ZSBpcyBkZWZpbmVkIGZyb20gYSAqKnZhcmlhYmxlIG9mIHRoZSBkYXRhc2V0Kio6CgpgYGB7ciBldmFsPVR9CmdncGxvdChkaWFtb25kczIpK2Flcyh4PWNhcmF0LHk9cHJpY2UsY29sb3I9Y3V0KQpgYGAKCiMjIyBgR2VvbWV0cmljc2AKClRvIG9idGFpbiB0aGUgZ3JhcGgsIHdlIG5lZWQgdG8gcHJlY2lzZSB0aGUgdHlwZSBvZiByZXByZXNlbnRhdGlvbi4gV2UgdXNlICoqZ2VvbWV0cmljcyoqIHRvIGRvIHRoYXQuIEZvciBhIHNjYXR0ZXIgcGxvdCwgd2UgdXNlICoqZ2VvbV9wb2ludCoqOgoKYGBge3J9CmdncGxvdChkaWFtb25kczIpK2Flcyh4PWNhcmF0LHk9cHJpY2UsY29sb3I9Y3V0KStnZW9tX3BvaW50KCkKYGBgCgpPYnNlcnZlIHRoYXQgKipnZ3Bsb3QqKiBhZGRzIHRoZSBsZW5nZW5kIGF1dG9tYXRpY2FsbHkuIEV4ZW1wbGVzIG9mICoqZ2VvbWV0cmljcyoqIGFyZSBkZXNjcmliZWQgaGVyZToKCkdlb20gICB8IERlc2NyaXB0aW9ufCBBZXN0aGV0aWNzIAotLS0tLS0tfC0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLQpnZW9tX3BvaW50KCl8IFNjYXR0ZXIgcGxvdCB8IHgsIHksIHNoYXBlLCBmaWxsIApnZW9tX2xpbmUoKXwgIExpbmUgKG9yZGVyZWQgYWNjb3JkaW5nIHRvIHgpIHwgeCwgeSwgbGluZXR5cGUKZ2VvbV9hYmxpbmUoKXwgIExpbmUgfCBzbG9wZSwgaW50ZXJjZXB0IApnZW9tX3BhdGgoKSB8IExpbmUgKG9yZGVyZWQgYWNjb3JkaW5nIHRvIHRoZSBpbmRleCkgfCB4LCB5LCBsaW5ldHlwZSAKZ2VvbV90ZXh0KCkgfCBUZXh0IHwgeCwgeSwgbGFiZWwsIGhqdXN0LCB2anVzdCAKZ2VvbV9yZWN0KCkgfCBSZWN0YW5nbGUgfCB4bWluLCB4bWF4LCB5bWluLCB5bWF4LCBmaWxsLCBsaW5ldHlwZQpnZW9tX3BvbHlnb24oKSB8IFBvbHlnb25lIHwgeCwgeSwgZmlsbCwgbGluZXR5cGUKZ2VvbV9zZWdtZW50KCkgfCBTZWdtZW50IHwgeCwgeSwgZmlsbCwgbGluZXR5cGUgCmdlb21fYmFyKCkgfCBCYXJwbG90IHwgeCwgZmlsbCwgbGluZXR5cGUsIHdlaWdodCAKZ2VvbV9oaXN0b2dyYW0oKSB8IEhpc3RvZ3JhbSB8IHgsIGZpbGwsIGxpbmV0eXBlLCB3ZWlnaHQgCmdlb21fYm94cGxvdCgpIHwgQm94cGxvdHMgfCB4LCB5LCBmaWxsLCB3ZWlnaHQgCmdlb21fZGVuc2l0eSgpIHwgRGVuc2l0eSB8IHgsIHksIGZpbGwsIGxpbmV0eXBlIApnZW9tX2NvbnRvdXIoKSB8IENvbnRvdXIgbGluZXMgfCB4LCB5LCBmaWxsLCBsaW5ldHlwZSAKZ2VvbV9zbW9vdGgoKSB8IFNtb290aGVycyAobGluZWFyIG9yIG5vbiBsaW5lYXIpIHwgeCwgeSwgZmlsbCwgbGluZXR5cGUgCkFsbCB8IHwgY29sb3IsIHNpemUsIGdyb3VwCgoKIyMjIEV4ZXJjaXNlIDYKCiogRHJhdyB0aGUgYmFycGxvdCBvZiAqKmN1dCoqICh3aXRoIGJsdWUgYmFycykKKiBEcmF3IHRoZSBiYXJwbG90IG9mICoqY3V0Kiogd2l0aCBvbmUgY29sb3IgZm9yIGVhY2ggbW9kYWxpdHkgb2YgY3V0LgoKCiMjIyBgU3RhdGlzdGljc2AgKHRoaXMgcGFydCBjYW4gYmUgb21pdHRlZCBmb3IgYmVnaW5uZXJzKQoKTWFueSBncmFwaHMgbmVlZCB0byB0cmFuc2Zvcm0gdGhlIGRhdGEgdG8gbWFrZSB0aGUgcmVwcmVzZW50YXRpb24gKGJhcnBsb3QsIGhpc3RvZ3JhbSkuIFNpbXBsZSB0cmFuc2Zvcm1hdGlvbnMgY2FuIGJlIG9idGFpbmVkIHF1aWNrbHkuIEZvciBpbnN0YW5jZSB3ZSBjYW4gZHJhdyB0aGUgc2luZSBmdW5jdGlvbiB3aXRoCgpgYGB7cn0KRCA8LSBkYXRhLmZyYW1lKFg9c2VxKC0yKnBpLDIqcGksYnk9MC4wMSkpCmdncGxvdChEKSthZXMoeD1YLHk9c2luKFgpKStnZW9tX2xpbmUoKQpgYGAKClRoZSBzaW5lIHRyYW5zZm9ybWF0aW9uIGlzIHByZWNpc2VkIGluICoqYWVzKiouIEZvciBtb3JlIGNvbXBsZXggdHJhbnNmb3JtYXRpb25zLCB3ZSBoYXZlIHRvIHVzZWQgKipzdGF0aXN0aWNzKiouIEEgKipzdGF0KiogZnVuY3Rpb24gdGFrZXMgYSBkYXRhc2V0IGFzIGlucHV0IGFuZCByZXR1cm5zIGEgZGF0YXNldCBhcyBvdXRwdXQsIGFuZCBzbyBhIHN0YXQgY2FuIGFkZCBuZXcgdmFyaWFibGVzIHRvIHRoZSBvcmlnaW5hbCBkYXRhc2V0LiBJdCBpcyBwb3NzaWJsZSB0byBtYXAKYWVzdGhldGljcyB0byB0aGVzZSBuZXcgdmFyaWFibGVzLiBGb3IgZXhhbXBsZSwgc3RhdF9iaW4sIHRoZSBzdGF0aXN0aWMgdXNlZCB0byBtYWtlIGhpc3RvZ3JhbXMsIHByb2R1Y2VzIHRoZSBmb2xsb3dpbmcgdmFyaWFibGVzOgoKKiBgY291bnRgLCB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiBlYWNoIGJpbgoqIGBkZW5zaXR5YCwgdGhlIGRlbnNpdHkgb2Ygb2JzZXJ2YXRpb25zIGluIGVhY2ggYmluIChwZXJjZW50YWdlIG9mIHRvdGFsIC8gYmFyCndpZHRoKQoqIGB4YCwgdGhlIGNlbnRlciBvZiB0aGUgYmluCgpCeSBkZWZhdWx0ICpnZW9tX2hpc3RvZ3JhbSogcmVwcmVzZW50cyBvbiB0aGUgJHkkLWF4aXMgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaW4gZWFjaCBiaW4gKHRoZSBvdXR1cHV0ICoqY291bnQqKikuIAoKYGBge3J9CmdncGxvdChkaWFtb25kcykrYWVzKHg9cHJpY2UpK2dlb21faGlzdG9ncmFtKGJpbnM9NDApCmBgYAoKRm9yIHRoZSBkZW5zaXR5LCB3ZSB1c2UKYGBge3J9CmdncGxvdChkaWFtb25kcykrYWVzKHg9cHJpY2UseT0uLmRlbnNpdHkuLikrZ2VvbV9oaXN0b2dyYW0oYmlucz00MCkKYGBgCgoqZ2dwbG90KiBwcm9wb3NlIGFub3RoZXIgd2F5IHRvIG1ha2UgdGhlIHJlcHJlc2VudGF0aW9uczogd2UgY2FuIHVzZSAqc3RhdF8qIGluc3RlYWQgb2YgKmdlb21fKi4gRm9ybWFsbHksIGVhY2ggc3RhdCBmdW5jdGlvbiBoYXMgYSBnZW9tIGFuZCBlYWNoIGdlb20gaGFzIGEgc3RhdC4gRm9yIGluc3RhbmNlLCAKYGBge3J9CmdncGxvdChkaWFtb25kczIpK2Flcyh4PWNhcmF0LHk9cHJpY2UpK2dlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiKQpnZ3Bsb3QoZGlhbW9uZHMyKSthZXMoeD1jYXJhdCx5PXByaWNlKStzdGF0X3Ntb290aChtZXRob2Q9ImxvZXNzIikKYGBgCmxlYWQgdG8gdGhlIHNhbWUgZ3JhcGguIFdlIGNhbiBjaGFuZ2UgdGhlIHR5cGUgb2YgcmVwcmVzZW50YXRpb24gaW4gdGhlICoqc3RhdF8qKiB3aXRoIHRoZSBhcmd1bWVudCAqKmdlb20qKjoKCmBgYHtyfQpnZ3Bsb3QoZGlhbW9uZHMyKSthZXMoeD1jYXJhdCx5PXByaWNlKStzdGF0X3Ntb290aChtZXRob2Q9ImxvZXNzIixnZW9tPSJwb2ludCIpCmBgYAoKSGVyZSBhcmUgc29tZSBleGFtcGxlcyBvZiAqKnN0YXQgZnVuY3Rpb25zKioKCiBTdGF0ICAgfCAgRGVzY3JpcHRpb24gfCAgUGFyYW1ldGVycwotLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLQpzdGF0X2lkZW50aXR5KCkgfCBObyB0cmFuc2Zvcm1hdGlvbiB8ICAKc3RhdF9iaW4oKSB8IENvdW50IHwgYmlud2lkdGgsIG9yaWdpbiAKc3RhdF9kZW5zaXR5KCkgfCBEZW5zaXR5IHwgYWRqdXN0LCBrZXJuZWwgCnN0YXRfc21vb3RoKCkgfCBTbW9vdGhlciB8IG1ldGhvZCwgc2UgCnN0YXRfYm94cGxvdCgpIHwgQm94cGxvdCB8IGNvZWYgCgoqc3RhdCogYW5kICpnZW9tKiBhcmUgbm90IGFsd2F5cyBlYXN5IHRvIGNvbWJpbmUuIEZvciBiZWdpbm5lcnMsIHdlIHJlY29tbWFuZCB0byBvbmx5IHVzZSAqZ2VvbSouCgojIyMgRXhlcmNpc2UgNwoKV2UgY29uc2lkZXIgYSBjb2xvciB2YXJpYWJsZSAkWCQgd2l0aCBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb24KJCRQKFg9cmVkKT0wLjMsXCBQKFg9Ymx1ZSk9MC4yLFwgUChYPWdyZWVuKT0wLjQsXCBQKFg9YmxhY2spPTAuMSQkCkRyYXcgdGhlIGJhcnBsb3Qgb2YgdGhpcyBkaXN0cmlidXRpb24uCgoKIyMjIGBTY2FsZXNgCgpTY2FsZXMgY29udHJvbCB0aGUgbWFwcGluZyBmcm9tIGRhdGEgdG8gYWVzdGhldGljIGF0dHJpYnV0ZXMgKGNoYW5nZSBvZiBjb2xvcnMsIHNpemVzLi4uKS4gV2UgZ2VuZXJhbGx5IHVzZSB0aGlzIGVsZW1lbnQgYXQgdGhlIGVuZCBvZiB0aGUgcHJvY2VzcyB0byByZWZpbmUgdGhlIGdyYXBoLiBTY2FsZXMgYXJlIGRlZmluZWQgYXMgZm9sbG93czoKCiogYmVnaW4gd2l0aCAqKnNjYWxlXyoqCiogYWRkIHRoZSBhZXN0aGV0aWNzIHdlIHdhbnQgdG8gbW9kaWZ5ICgqKmNvbG9yKiosICoqZmlsbCoqLCAqKnhfKiopCiogZW5kIHdpdGggdGhlIG5hbWUgb2YgdGhlIHNjYWxlICgqKm1hbnVhbCoqLCAqKmlkZW50aXR5KiouLi4pCgpGb3IgaW5zdGFuY2UsCgpgYGB7cn0KZ2dwbG90KGRpYW1vbmRzMikrYWVzKHg9Y2FyYXQseT1wcmljZSxjb2xvcj1jdXQpK2dlb21fcG9pbnQoKSsKc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJGYWlyIj0iYmxhY2siLCJHb29kIj0ieWVsbG93IiwKIlZlcnkgR29vZCI9ImJsdWUiLCJQcmVtaXVtIj0icmVkIiwiSWRlYWwiPSJncmVlbiIpKQpgYGAKCkhlcmUgYXJlIHRoZSBtYWluIHNjYWxlczoKCmFlcyB8IERpc2NyZXRlIHwgQ29udGludW91cwotLS0tfC0tLS0tLS0tLS0tfC0tLS0tLS0tLS0KQ291bGV1ciAoY29sb3IgZXQgZmlsbCkgfCBicmV3ZXIgfCBncmFkaWVudCAKICAtICB8IGdyZXkgfCBncmFkaWVudDIgCiAgLSAgfCBodWUgfCBncmFkaWVudG4gCiAtIHwgaWRlbnRpdHkgfCAKIC0gfCBtYW51YWwgfApQb3NpdGlvbiAoeCBldCB5KSB8IGRpc2NyZXRlIHwgY29udGlub3VzIAotIHwgfCBkYXRlIApGb3JtZSB8IHNoYXBlIHwgCi0gfCBpZGVudGl0eSB8IAotIHwgbWFudWFsIHwgClRhaWxsZSAgfCBpZGVudGl0eSB8IHNpemUgCi0gfCBtYW51YWwgfCAKClNvbWUgZXhhbXBsZXM6CgoqIGBjb2xvciBvZiBhIGJhcnBsb3RgCgpgYGB7cn0KcDEgPC0gZ2dwbG90KGRpYW1vbmRzMikrYWVzKHg9Y3V0KStnZW9tX2JhcihhZXMoZmlsbD1jdXQpKQpwMQpgYGAKCgpXZSBjaGFuZ2UgY29sb3JzIGJ5IHVzaW5nIHRoZSBwYWxldHRlICoqUHVycGxlcyoqIDoKCmBgYHtyfQpwMStzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSJQdXJwbGVzIikKYGBgCgoKKiBgR3JhZGllbnQgY29sb3IgZm9yIGEgc2NhdHRlciBwbG90YCA6CmBgYHtyfQpwMiA8LSBnZ3Bsb3QoZGlhbW9uZHMyKSthZXMoeD1jYXJhdCx5PXByaWNlKStnZW9tX3BvaW50KGFlcyhjb2xvcj1kZXB0aCkpCnAyCmBgYAoKV2UgY2hhbmdlIHRoZSBncmFkaWVudCBjb2xvciAKYGBge3J9CnAyK3NjYWxlX2NvbG9yX2dyYWRpZW50KGxvdz0icmVkIixoaWdoPSJ5ZWxsb3ciKQpgYGAKCiogIGBDaGFuZ2Ugb24gdGhlIGF4aXNgCmBgYHtyfQpwMitzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLjUsMyxieT0wLjUpKStzY2FsZV95X2NvbnRpbnVvdXMobmFtZT0icHJpeCIpK3NjYWxlX2NvbG9yX2dyYWRpZW50KCJQcm9mb25kZXVyIikKYGBgCgoKIyMjIGBHcm91cCBhbmQgZmFjZXRzYAoKKipnZ3Bsb3QqKiBhbGxvd3MgdG8gbWFrZSByZXByZXNlbnRhdGlvbnMgZm9yIHN1Ymdyb3VwIG9mIGluZGl2aWR1YWxzLiBXZSBjYW4gcHJvY2VlZCBpbiB0d28gd2F5czoKCiogdG8gcmVwcmVzZW50IHN1Ymdyb3VwIG9uIHRoZSBzYW1lIGdyYXBoLCB3ZSB1c2UgKmdyb3VwKiBpbiAqKmFlcyoqCiogdG8gcmVwcmVzZW50IHN1Ymdyb3VwIG9uIHRoZSBkaWZmZXJlbnQgZ3JhcGhzLCB3ZSB1c2UgKipmYWNldHMqKgoKV2UgY2FuIHJlcHJlc2VudCAob24gdGhlIHNhbWUgZ3JhcGgpIHRoZSBzbW9vdGhlciAqKnByaWNlIHZzIGNhcmF0KiogZm9yIGVhY2ggbW9kYWxpdHkgb2YgKmN1dCogd2l0aCAKYGBge3J9CmdncGxvdChkaWFtb25kczIpK2Flcyh4PWNhcmF0LHk9cHJpY2UsZ3JvdXA9Y3V0LGNvbG9yPWN1dCkrZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIpCmBgYAoKVG8gb2J0YWluIHRoZSByZXByZXNlbnRhdGlvbiBvbiBtYW55IGdyYXBocywgd2UgdXNlCgpgYGB7cn0KZ2dwbG90KGRpYW1vbmRzMikrYWVzKHg9Y2FyYXQseT1wcmljZSkrZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIpK2ZhY2V0X3dyYXAofmN1dCkKZ2dwbG90KGRpYW1vbmRzMikrYWVzKHg9Y2FyYXQseT1wcmljZSkrZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIpK2ZhY2V0X3dyYXAofmN1dCxucm93PTEpCmBgYAoKKmZhY2V0X2dyaWQqIGFuZCAqZmFjZXRfd3JhcCogZG8gdGhlIHNhbWUgam9iIGJ1dCBzcGxpdCB0aGUgc2NyZWVuIGluIGRpZmZlcmVudCB3YXlzOgoKYGBge3J9CmdncGxvdChkaWFtb25kczIpK2Flcyh4PWNhcmF0LHk9cHJpY2UpK2dlb21fcG9pbnQoKStnZW9tX3Ntb290aChtZXRob2Q9ImxtIikrZmFjZXRfZ3JpZChjb2xvcn5jdXQpCmdncGxvdChkaWFtb25kczIpK2Flcyh4PWNhcmF0LHk9cHJpY2UpK2dlb21fcG9pbnQoKStnZW9tX3Ntb290aChtZXRob2Q9ImxtIikrZmFjZXRfd3JhcChjb2xvcn5jdXQpCmBgYAoKCiMjIDIuMiBDb21wbGVtZW50cwoKU3ludGF4IGZvciAqKmdncGxvdCoqIGlzIGRlZmluZWQgYWNjb3JkaW5nIHRvIHRoZSBmb2xsb3dpbmcgc2NoZW1lOgoKZ2dwbG90KCkrYWVzKCkrZ2VvbV8oKStzY2FsZV8oKQoKSXQgaXMgbW9yZSBmbGV4aWJsZTogZm9yIGluc3RhbmNlICoqYWVzKiogY291bGQgYmUgc3BlY2lmaWVkIGluICoqZ2dwbG90Kiogb3IgaW4gKipnZW9tXyoqCgpgYGB7cn0KZ2dwbG90KGRpYW1vbmRzMikrYWVzKHg9Y2FyYXQseT1wcmljZSkrZ2VvbV9wb2ludCgpCmdncGxvdChkaWFtb25kczIsYWVzKHg9Y2FyYXQseT1wcmljZSkpK2dlb21fcG9pbnQoKQpnZ3Bsb3QoZGlhbW9uZHMyKStnZW9tX3BvaW50KGFlcyh4PWNhcmF0LHk9cHJpY2UpKQpgYGAKCldlIGNhbiBhbHNvIGJ1aWx0IG91ciBncmFwaCB3aXRoIG1hbnkgZGF0YXNldHM6CmBgYHtyfQpYIDwtIHNlcSgtMipwaSwyKnBpLGJ5PTAuMDAxKQpZMSA8LSBjb3MoWCkKWTIgPC0gc2luKFgpCmRvbm5lZXMxIDwtIGRhdGEuZnJhbWUoWCxZMSkKZG9ubmVlczIgPC0gZGF0YS5mcmFtZShYLFkyKQpnZ3Bsb3QoZG9ubmVlczEpK2dlb21fbGluZShhZXMoeD1YLHk9WTEpKSsKZ2VvbV9saW5lKGRhdGE9ZG9ubmVlczIsYWVzKHg9WCx5PVkyKSxjb2xvcj0icmVkIikKYGBgCgpNYW55IG90aGVyIGZ1bmN0aW9ucyBhcmUgcHJvcG9zZWQgYnkgKipnZ3Bsb3QqKjoKCiogKipnZ3RpdGxlKiogdG8gYWRkIGEgdGl0bGUKKiAqKmdnc2F2ZSoqIHRhIHNhdmUgYSBncmFwaAoqICoqdGhlbWVfKiogdG8gY2hhbmdlIHRoZSB0aGVtZSBvZiB0aGUgZ3JhcGgKCmBgYHtyfQpwIDwtIGdncGxvdChkaWFtb25kczIpK2Flcyh4PWNhcmF0LHk9cHJpY2UsY29sb3I9Y3V0KStnZW9tX3BvaW50KCkKcCt0aGVtZV9idygpCnArdGhlbWVfY2xhc3NpYygpCnArdGhlbWVfZ3JleSgpCnArdGhlbWVfYncoKQpgYGAKCgojIyMgRXhlcmNpc2UgOAoKMS4gRHJhdyB0aGUgc2luZSBhbmQgY29zaW5lIGZ1bnRpb25zIG9uIHRoZSBzYW1lIGdyYXBoLiBZb3UgZmlyc3QgdXNlIHR3byBkYXRhc2V0cyAob25lIGZvciB0aGUgc2luZSBmdW5jdGlvbiwgdGhlIG90aGVyIGZvciB0aGUgY29zaW5lIGZ1bmN0aW9uKS4KCgoyLiBEbyB0aGUgc2FtZSB3aXRoIG9uZSBkYXRhc2V0LiBIaW50OiB1c2UgdGhlICoqbWVsdCoqIGZ1bmN0aW9uIG9mIHRoZSBwYWNrYWdlICoqcmVzaGFwZTIqKi4KCjMuIERyYXcgdGhlIHR3byBmdW5jdGlvbnMgb24gdHdvIGRpZmZlcmVudCBncmFwaHMgKHVzZSAqKmZhY2V0X3dyYXAqKikuCgo0LiBEbyB0aGUgc2FtZSB3aXRoIHRoZSBmdW5jdGlvbiAqKmdyaWQuYXJyYW5nZSoqIGZyb20gdGhlIHBhY2thZ2UgKipncmlkRXh0cmEqKi4KCgojIyMgRXhlcmNpc2UgOQoKV2UgY29uc2lkZXIgdGhlIGRhdGFzZXQgKiptdGNhcnMqKgpgYGB7cn0KZGF0YShtdGNhcnMpCnN1bW1hcnkobXRjYXJzKQpgYGAKCjEuIERyYXcgdGhlIGhpc3RvZ3JhbSBvZiAqKm1wZyoqICh1c2UgbWFueSBudW1iZXIgb2YgYmlucykKCjIuIFJlcHJlc2VudCB0aGUgZGVuc2l0eSBvbiB0aGUgJHkkLWF4aXMuCgozLiBEcmF3IHRoZSBiYXJwbG90IG9mICoqY3lsKiouCgo0LiBEcmF3IHRoZSBzY2F0dGVyIHBsb3QgKipkaXNwIHZzIG1wZyoqIGZvciBlYWNoIHZhbHVlIG9mICoqY3lsKiogKG9uZSBjb2xvciBmb3IgZWFjaCB2YWx1ZSBvZiAqKmN5bCoqKS4KCjUuIEFkZCB0aGUgbGluZWFyIHNtb290aGVyIG9uIGVhY2ggZ3JhcGguCgoKIyMjIEV4ZXJjaXNlIDEwCgoxLiBEcmF3IHRoZSBzaW5lIGZ1bmN0aW9uIG9uICRbLTJccGksMlxwaV0kCjIuIEFkZCB0aGUgbGluZXMgKGluIGJsdWUpIG9mIGVxdWF0aW9uICR5PTEkIGFuZCAkeT0tMSQuIFVzZSAqc2l6ZT0yKi4KCiMjIyBFeGVyY2lzZSAxMQoKMS4gU2ltdWxhdGUgYSBzYW1wbGUgJCh4X2kseV9pKSxpPTEsXGRvdHMsMTAwJCBhY2NvcmRpbmcgdG8gdGhlIGxpbmVhciBtb2RlbAokJFlfaT0zK1hfaStcdmFyZXBzaWxvbl9pJCQKd2hlcmUgJFhfaSQgYXJlIGkuaS5kLiBhbmQgdW5pZm9ybSBvbiAkWzAsMV0kIGFuZCAkXHZhcmVwc2lsb25faSQgYXJlIGdhdXNzaWFuICROKDAsMC4yXjIpJCAodXNlICoqcnVuaWYqKiBhbmQgKipybm9ybSoqKQoKCjIuIERyYXcgdGhlIHNjYXR0ZXIgcGxvdCAqKnkgdnMgeCoqIGFuZCBhZGQgdGhlIGxpbmVhciBzbW9vdGhlci4KCjMuIERyYXcgdGhlIHJlc2lkdWFsczogYWRkIGEgdmVydGljYWwgbGluZSBmcm9tIGVhY2ggcG9pbnQgdG8gdGhlIGxpbmVhciBzbW9vdGhlciAodXNlICoqZ2VvbV9zZWdtZW50KiopLgoKIyMjIEV4ZXJjaXNlIDEyIChvcHRpb25hbCkKCkZpbGUgKipleG8zX2dncGxvdC5kdGEqKiBjb250YWlucyAkbj0xMDAkIG9ic2VydmF0aW9ucy4gV2Ugd2FudCB0byBleHBsYWluICRZJCBieSB0aGUgb3RoZXIgdmFyaWFibGVzIHdpdGggYSBsaW5lYXIgbW9kZWwuCgoxLiBDb21wdXRlIHRoZSBsaW5lYXIgbW9kZWwgKCoqbG0qKiBmdW5jdGlvbikuIFdoYXQgY2FuIHdlIHNheSBhYm91dCBpdD8KYGBge3J9CkQxIDwtIHJlYWQudGFibGUoIi4uL0RBVEEvZXhvM19nZ3Bsb3QuZHRhIixoZWFkZXI9VCxzZXA9IjsiKQptMSA8LSBsbShZfi4sZGF0YT1EMSkKc3VtbWFyeShtMSkKYGBgCgoyLiBDYWxjdWxhdGUgdGhlIHBhcnRpYWwgcmVzaWR1YWxzICgqKnJlc2lkdWFscyguLi4sdHlwZT0icGFydGlhbCIpKiopOgpgYGB7cn0KcmVzIDwtIGRhdGEuZnJhbWUocmVzaWR1YWxzKG0xLHR5cGU9InBhcnRpYWwiKSkKYGBgCgozLiBEcmF3IGZvciBlYWNoIGV4cGxhbmF0b3J5IHZhcmlhYmxlLCB0aGUgc2NhdHRlciBwbG90IG9mIHRoZSBwYXJ0aWFsIHJlc2lkdWFscyB2ZXJzdXMgdGhlIHZhcmlhYmxlLiBBZGQgdGhlIHNtb290aGVyICoqbG9lc3MqKi4KCjQuIEltcHJvdmUgdGhlIGxpbmVhciBtb2RlbC4KCmBgYHtyfQptMiA8LSBsbShZflgxK1gyK1gzK1g0K0koWDReMiksZGF0YT1EMSkKc3VtbWFyeShtMikKYGBgCgojIyMgQ2hhbGxlbmdlIChvcHRpb25hbCkKCldlIGNvbnNpZGVyIHRoZSAqKm10Y2FycyoqIGRhdGFzZXQgKGV4ZXJjaXNlIDkpLgoKMS4gT2J0YWluIHRoZSBmb2xsb3dpbmcgZ3JhcGhzICh1c2UgKipjb29yZF9mbGlwKiogZm9yIHRoZSBzZWNvbmQgZ3JhcGgpLgoKIVtdKGNoYWxsZW5nZTEucGRmKQohW10oY2hhbGxlbmdlMi5wZGYpCiFbXShjaGFsbGVuZ2UzLnBkZikKCjIuIEFkZCBvbiB0aGUgdGhpcmQgZ3JhcGggYSBsaW5lIGZvciB0aGUgcXVhcnRpbGVzIG9mIHRoZSB2YXJpYWJsZSAqKmNhcmF0KiogKGZvciBlYWNoIHZhbHVlIG9mICoqY3V0KiopCgozLiBEcmF3IHRoZSBmb2xsb3dpbmcgZ3JhcGggKHVzZSB0aGUgKipnZ3N0YW5jZSoqIHBhY2thZ2UpLgoKIVtdKGNoYWxsZW5nZTQucGRmKQoKCg==