A short introduction to R

Outline

In this session, we introduce some of the fundamentals of the R language.

Topics covered include:-

  • Creating variables
  • Using Functions
  • Vectors
  • Data frame
  • Subsetting data, the base R way
  • Plotting
  • How to get help

For a more detailed introduction, we suggest the following free resources

R basics

Advantages of R

The R programming language is now recognised beyond the academic community as an effect solution for data analysis and visualisation. Notable users of R include:-

duke-scandal

duke-scandal

Two Biostatiscians (later termed ‘Forensic Bioinformaticians’) from M.D. Anderson used R extensively during their re-analysis and investigation of a Clinical Prognostication paper from Duke. The subsequent scandal put Reproducible Research at the forefront of everyone’s mind.

Keith Baggerly’s talk on the subject is highly-recommended.

Support for R

  • Online forums such as Stack Overflow regularly feature R
  • Blogs
  • Local user groups
  • Documentation via ? or help.start()
  • Documentation for packages is found via the Packages tab in the bottom-right of RStudio.

RStudio

  • Rstudio is a free environment for R
  • Convenient menus to access scripts, display plots
  • Still need to use command-line to get things done
  • Developed by some of the leading R programmers
  • Used by beginners, and experienced users alike

To get started, you will need to install the latest version of R and RStudio Desktop; both of which are free.

Once installed, you should be able to launch RStudio by clicking on its icon:-

Bioconductor

Established in 2001 as a means of distributing tools for the analysis and comprehension of high-throughput genomic data in R. Initially focused on microarrays, but now has many tools for NGS data

  • primary processing of NGS data nearly-always done in other languages
    • R is extensively-used for visualisation and interpretation once data are in manageable form

On the Bioconductor website,. you will find

Entering commands in R

  • The traditional way to enter R commands is via the Terminal, or using the console in RStudio (bottom-left panel when RStudio opens for first time).
  • However, for this course we will use a relatively new feature called R-notebooks.
  • An R-notebook mixes plain text with R code

Markdown is a very simple way of writing a template to produce a pdf, HTML or word document. The compiled version of this document is available online and is more convenient to browse https://bioinformatics-core-shared-training.github.io/cruk-summer-school-2017/Day1/Session2-Rnotes.nb.html.

  • “chunks” of R code can be added using the insert option from the toolbar, or the CTRL + ALT + I shortcut
  • Each line of R can be executed by clicking on the line and pressing CTRL and ENTER
  • Or you can press the green triangle on the right-hand side to run everything in the chunk
  • Try this now!
  • The code might have different options which dictate how the output is displayed in the compiled document (e.g. HTML)
    • e.g. you might see EVAL = FALSE or echo = FALSE
    • you don’t have to worry about this if stepping through the markdown interactively
print("Hello World")

This will be displayed in italic

This will be displayed in bold

  • this
  • is
  • a
  • list
    • this is a sub-list

You can also add hyperlinks, images and tables. More help is available through RStudio Help -> Markdown Quick Reference

At a basic level, we can use R as a calculator to compute simple sums with the +, -, * (for multiplication) and / (for division) symbols.

Try adding some of your own calculations below….

2 + 2
[1] 4
2 - 2
[1] 0
4 * 3
[1] 12
10 / 2
[1] 5

The answer is displayed at the console with a [1] in front of it. The 1 inside the square brackets is a place-holder to signify how many values were in the answer (in this case only one). We will talk about dealing with lists of numbers shortly…

In the case of expressions involving multiple operations, R respects the BODMAS system to decide the order in which operations should be performed.

2 + 2 *3
[1] 8
2 + (2 * 3)
[1] 8
(2 + 2) * 3
[1] 12

R is capable of more complicated arithmetic such as trigonometry and logarithms; like you would find on a fancy scientific calculator. Of course, R also has a plethora of statistical operations as we will see.

pi
[1] 3.141593
sin (pi/2)
[1] 1
cos(pi)
[1] -1
tan(2)
[1] -2.18504
log(1)
[1] 0

We can only go so far with performing simple calculations like this. Eventually we will need to store our results for later use. For this, we need to make use of variables.

Variables

A variable is a letter or word which takes (or contains) a value. We use the assignment ‘operator’, <- to create a variable and store some value in it.

x <- 10
x
[1] 10
myNumber <- 25
myNumber
[1] 25

We also can perform arithmetic on variables using functions:

sqrt(myNumber)
[1] 5

We can add variables together:

x + myNumber
[1] 35

We can change the value of an existing variable:

x <- 21
x
[1] 21
  • We can set one variable to equal the value of another variable:
x <- myNumber
x
[1] 25

When we are feeling lazy we might give our variables short names (x, y, i…etc), but a better practice would be to give them meaningful names. There are some restrictions on creating variable names. They cannot start with a number or contain characters such as ., _, ‘-’. Naming variables the same as in-built functions in R, such as c, T, mean should also be avoided.

Functions

Functions in R perform operations on arguments (the inputs(s) to the function). We have already used:

sin(x)
[1] -0.1323518

this returns the sine of x. In this case the function has one argument: x.

  • Arguments are always contained in parentheses – curved brackets, () – separated by commas.

  • Arguments can be named or unnamed, but if they are unnamed they must be ordered (we will see later how to find the right order). The names of the arguments are determined by the author of the function and can be found in the help page for the function. When testing code, it is easier and safer to name the arguments.

rnorm is a function that will generate a series of values from a normal distribution. In order to use the function, we need to tell R how many values we want

rnorm(n=10)
 [1]  1.44225570 -0.86118934 -1.31558685 -1.40655864 -0.33885927  2.94080404  0.43556310 -0.06240997  0.12814119  1.50239669

The normal distribution is defined by a mean (average) and standard deviation (spread). However, in the above example we didn’t tell R what mean and standard deviation we wanted. So how does R know what to do? All arguments to a function and their default values are listed in the help page

(N.B sometimes help pages can describe more than one function)

?rnorm

The help page is often the best way to find out about a function. Otherwise, google is your friend.

In this case, we see that the defaults for mean and standard deviation are 0 and 1. We can change the function to generate values from a distribution with a different mean and standard deviation using the mean and sd arguments. It is important that we get the spelling of these arguments exactly right, otherwise R will an error message, or (worse?) do something unexpected.

rnorm(n=10, mean=2,sd=3)
 [1]  3.0652574  2.5962377 -2.9751633 -1.0348971  1.0717109  4.2704699  1.1752755  1.7457067  0.5138356 -2.0764301
rnorm(10, 2, 3)
 [1]  4.0147699  2.8199592  5.4761540 -3.1681876  6.1246047 -4.7092447 -0.4482680  3.5031579 -0.1774535  3.4573333

In the example, rnorm is outputting a series of numbers, which is called a vector in R and is the most-fundamental data-type.

Packages in R

So far we have used functions that are available with the base distribution of R; the functions you get with a clean install of R. The open-source nature of R encourages others to write their own functions for their particular data-type or analyses.

Packages are distributed through repositories. The most-common ones are CRAN and Bioconductor. CRAN alone has many thousands of packages.

The Packages tab in the bottom-right panel of RStudio lists all packages that you currently have installed. Clicking on a package name will show a list of functions that available once that package has been loaded. The library function is used to load a package and make it’s functions / data available in your current R session. You need to do this every time you load a new RStudio session.

library(RColorBrewer)

There are functions for installing packages within R. If your package is part of the main CRAN repository, you can use install.packages.

  • Here, tidyr is a useful package for cleaning and reshaping data:-
install.packages("tidyr")

Bioconductor packages have their own install script, which you can download from the Bioconductor website

source("http://www.bioconductor.org/biocLite.R")
biocLite("affy")

A package may have several dependancies; other R packages from which it uses functions or data types (re-using code from other packages is strongly-encouraged). If this is the case, the other R packages will be located and installed too.

So long as you stick with the same version of R, you won’t need to repeat this install process.

Dealing with data

We are going to explore some of the basic features of R using data from the gapminder project, which have been bundled into an R package. These data give various indicator variables for different countries around the world (life expectancy, population and Gross Domestic Product). We have saved these data as a .csv file to demonstrate how to import data into R.

You can download these data here. Right-click the link and save to somewhere on your computer that you wish to work from.

The working directory

Like other software (Word, Excel, Photoshop….), R has a default location where it will save files to and import data from. This is known as the working directory in R. You can query what R currently considers its working directory by doing:-

getwd()

N.B. Here, a set of open and closed brackets () is used to call the getwd function with no arguments.

We can also list the files in this directory with:-

list.files()
 [1] "dataOrderedByWealth.csv"     "gapminder-plots.pdf"         "gapminder.csv"               "myLittlePlot.pdf"           
 [5] "Session1-intro.html"         "Session1-intro.Rmd"          "Session2-Rnotes.nb.html"     "Session2-Rnotes.Rmd"        
 [9] "Session4-seqIntro.html"      "Session5-alignedReads.html"  "Session6-IGV.html"           "Session7-seqDatainR.nb.html"
[13] "Session7-seqDatainR.Rmd"    

Any .csv file in the working directory can be imported into R by supplying the name of the file to the read.csv function and creating a new variable to store the result. A useful sanity check is the file.exists function which will print TRUE is the file can be found in the working directory.

file.exists("gapminder.csv")
[1] TRUE

If the file we want to read is not in the current working directory, we will have to write the path to the file; either relative to the current working directory (e.g. the directory “up” from the current working directory, or in a sub-folder), or the full path. In an interactive session, you can do use file.choose to open a dialogue box. The path to the the file will then be displayed in R.

myfile <- file.choose()
myfile
myfile <- "/home/participant/Course_Materials/Day1/gapminder.csv"

Assuming the file can be found, we can use read.csv to import. Other functions can be used to read tab-delimited files (read.delim) or a generic read.table function. A data frame object is created.

  • read.delim has lots of options to control how to import the data, including skipping lines in the files. See ?read.table

  • If you get an error saying Error in file(file, “rt”) : cannot open the connection, you might need to change your working directory or make sure the file name is typed correctly (R is case-sensitive)

gapminder <- read.csv("gapminder.csv")
gapminder
  • N.B. The latest version of RStudio (> 1.0.44) provides the option to import data from the File menu. Try File -> Import Dataset -> From Csv.
    • not really recommended for best practice, but it should help get you started

The data frame object in R allows us to work with “tabular” data, like we might be used to dealing with in Excel, where our data can be thought of having rows and columns. The values in each column have to all be of the same type (i.e. all numbers or all text).

Disclaimer If you are trying to read your own data, and encounter an error at this stage, you may need to consider if your data are in the correct form for analysis. Like most programming languages, R will struggle if your spreadsheet has been heavily formatted to include colours, formulas and special formatting.

These references will guide you through some of the pitfalls and common mistakes to avoid when formatting data

In RStudio , you can view the contents of the data frame we have just created. This is useful for interactive exploration of the data, but not so useful for automation and scripting and analyses.

View(gapminder)

We should always check the data frame that we have created. Sometimes R will happily read data using an inappropriate function and create an object without raising an error. However, the data might be unsuable. Consider:-

test <- read.delim("gapminder.csv")
dim(test)
[1] 1704    1

We can access the columns of a data frame by knowing the column name. TIP Use auto-complete with the TAB key to get the name of the column correct

gapminder$country

A vector (1-dimensional) is returned, the length of which is the same as the number of rows in the data frame. The vector could be stored as a variable and itself be subset or used in further calculations

countries <- gapminder$country

The summary function is a useful way of summarising the data containing in each column. It will give information about the type of data (remember, data frames can have a mixture of numeric and character columns) and also an appropriate summary. For numeric columns, it will report some stats about the distribution of the data. For categorical data, it will report the different levels.

summary(gapminder)
        country        continent        year         lifeExp           pop              gdpPercap       
 Afghanistan:  12   Africa  :624   Min.   :1952   Min.   :23.60   Min.   :6.001e+04   Min.   :   241.2  
 Albania    :  12   Americas:300   1st Qu.:1966   1st Qu.:48.20   1st Qu.:2.794e+06   1st Qu.:  1202.1  
 Algeria    :  12   Asia    :396   Median :1980   Median :60.71   Median :7.024e+06   Median :  3531.8  
 Angola     :  12   Europe  :360   Mean   :1980   Mean   :59.47   Mean   :2.960e+07   Mean   :  7215.3  
 Argentina  :  12   Oceania : 24   3rd Qu.:1993   3rd Qu.:70.85   3rd Qu.:1.959e+07   3rd Qu.:  9325.5  
 Australia  :  12                  Max.   :2007   Max.   :82.60   Max.   :1.319e+09   Max.   :113523.1  
 (Other)    :1632                                                                                       



Exercise

  • Create new variables for the life expectancy and population columns
    • round the life expectancy to the nearest whole number
    • modify the population variable so that the population size is given in millions of people
    • what is the maximum life expectancy observed?
    • what is the smallest population observed?
    • HINT:- min, max, round…..



### Your answer here ###

Subsetting rows and columns

A data frame can be subset using square brackes[] placed after the name of the data frame. As a data frame is a two-dimensional object, you need both a row and column index.

gapminder[1,2]
gapminder[2,1]
gapminder[c(1,2,3),1]
gapminder[c(1,2,3),c(1,2,3)]

Note that the data frame is not altered we are just seeing what a subset of the data looks like and not changing the underlying data. If we wanted to do this, we would need to create a new variale.

gapminder

Should we wish to see all rows, or all columns, we can neglect either the row or column index

gapminder[1,]
gapminder[,1]

The indices can be more complicated R expressions containing multiple values

gapminder[1:3,1:2]
gapminder[seq(1,1704,length.out = 10),1:4]

A common shortcut is head which prints the first six rows of a data frame.

head(gapminder)

When subsetting entire rows you need to remember the , after the row indices. If you fail to do so, R may still return a result. However, it probably won’t be what you expected. Look what happens if you wanted to the first three rows but typed the following command

gapminder[1:3]

We can also create new columns, or alter existing ones

  • Using an assignment operator <- we can assign the value of a particular column to be vector
    • if the column doesn’t exist, it will be created
    • the vector should be the same length as the number of rows in the data frame
gapminder$popInMillions <- gapminder$pop / 1000000
gapminder$lifeExp <- round(gapminder$lifeExp,1)
gapminder

Filtering rows

Rather than selecting rows based on their numeric index (as in the previous example) we can use what we call a logical test. This is a test that gives either a TRUE or FALSE result. When applied to subsetting, only rows with a TRUE result get returned.

myvec <- c("A","B","C","D")
x <- c(TRUE, TRUE,FALSE,TRUE)
myvec[x]
[1] "A" "B" "D"
x <- c(FALSE, FALSE,FALSE,TRUE)

For example we could compare the lifeExp variable to 40. The result is a vector of TRUE or FALSE; one for each row in the data frame

gapminder$lifeExp < 40

This R code can be put inside the square brackets to select rows of interest (those observations where the life expectancy variable is less than 40).

gapminder[gapminder$lifeExp < 40, ]

The , is important as this tells R to display all columns. If we wanted a subset of the columns we would put their indices after the ,

gapminder[gapminder$lifeExp < 40, 1:4]

Using the column names is also valid

gapminder[gapminder$lifeExp < 40, c("country", "continent","year")]

Testing for equality can be done using ==. This will only give TRUE for entries that are exactly the same as the test string.

gapminder[gapminder$country == "Zambia",]

N.B. For partial matches, the grep function and / or regular expressions (if you know them) can be used.

gapminder[grep("land", gapminder$country),]

There are a couple of ways of testing for more than one text value. The first uses an or | statement. i.e. testing if the value of country is Zambia or the value is Zimbabwe.

The %in% function is a convenient function for testing which items in a vector correspond to a defined set of values.

gapminder[gapminder$country == "Zambia" | gapminder$country == "Zimbabwe",]
gapminder[gapminder$country %in% c("Zambia","Zimbabwe"),]

Similar to or, we can require that both tests are TRUE by using an and & operation. e.g. which years in Zambia had a life expectancy less than 40

gapminder[gapminder$country == "Zambia" & gapminder$lifeExp < 40,]



Exercise

  • Print the contents of the row in the data frame that contains the entry with the highest population?
  • Print the contents of the row in the data frame that contains the entry with the lowest life expectancy?
  • A data frame of countries with a population less than a million in the year 2002, that are not in Africa?
    • you may need to do some googling to find out how to do “not equal” in R (Optional)
  • What country has the longest name? Print all the rows containing data for this country
  • HINT: investigate the nchar function



### Your answer here ###

Ordering and sorting

A vector can be returned in sorted form using the sort function.

sort(countries)
sort(countries,decreasing = TRUE)

However, if we want to sort an entire data frame a different approach is needed. The trick is to use order. Rather than giving a sorted set of values, it will give sorted indices. These indices can then be used for a subset operation.

leastPop <- gapminder[order(gapminder$pop),]
head(leastPop)

We can even order by more than one condition

gapminder[order(gapminder$year, gapminder$country),]

A final point on data frames is that we can export them out of R once we have done our data processing.

byWealth <- gapminder[order(gapminder$gdpPercap,decreasing = TRUE),]
head(byWealth)
write.csv(byWealth, file="dataOrderedByWealth.csv")

Plotting and stats (in brief!)

All your favourite types of plot can be created in R

Plots can be constructed from vectors of numeric data, such as the data we get from a particular column in a data frame.

Basic Plot types

  • A histogram is a common choice if we want to visualise the distribution of a numeric variables
hist(gapminder$lifeExp)

Scatter plots of two variables require two arguments; one for the x and one for the y axis.

plot(gapminder$pop,gapminder$lifeExp)

Barplots are commonly-used for counts of categorical data

  • Let’s say we want to know how many countries there are
  • table will give us the total number of times each country is observed
    • but the set of countries is repeated for each year
table(gapminder$continent)

  Africa Americas     Asia   Europe  Oceania 
     624      300      396      360       24 
table(gapminder$continent[gapminder$year==2002])

  Africa Americas     Asia   Europe  Oceania 
      52       25       33       30        2 
barplot(table(gapminder$continent[gapminder$year==2002]))

Boxplots are good for visualising and comparing distributions. Here the ~ symbol sets up a formula, the effect of which is to put the categorical variable on the x axis and continuous variable on the y axis.

boxplot(gapminder$gdpPercap ~ gapminder$continent)

Lots of customisations are possible to enhance the appaerance of our plots. Not for the faint-hearted, the help pages ?plot and ?par give the full details. In short,

  • Axis labels, and titles can be specified as character strings.

  • R recognises many preset names as colours. To get a full list use colours(), or check this online reference.
    • can also use `Red, Green, Blue values; which you might get from a paint program
  • Plotting characters can be specified using a pre-defined number:-

Putting it all together.

plot(gapminder$pop,gapminder$lifeExp,pch=16,
     col="red",ylab="Life Expectancy",
     xlab="Population",main="Life Expectancy trend with population")

The same customisations can be used for various plots:-

boxplot(gapminder$gdpPercap ~ gapminder$continent,col=c("red","orange","green","blue","purple"),
        main="GDP per-continent",
        xlab="Continent",
        ylab="GDP")




Exercise

  • First, create a subset of observations from the year 1952
  • Do countries with a higher GDP tend to have a higher life expectancy?
    • use a scatter plot to find out
  • Was there an overall difference in life expectancy between continents in this year?
    • use a boxplot to find out



## Your answer here##

Plots can be exported by the Plots tab in RStudio, which is useful in an interactive setting. However, one can also save plots to a file calling the pdf or png functions before executing the code to create the plot.

pdf("myLittlePlot.pdf")
boxplot(gapminder$gdpPercap ~ gapminder$continent,col=c("red","orange","green","blue","purple"),
        main="GDP per-continent",
        xlab="Continent",
        ylab="GDP")
dev.off()
null device 
          1 

Any plots created in-between the pdf(..) and dev.off() lines will get saved to the named file. The dev.off() line is very important; without it you will not be able to view the plot you have created. pdf files are useful because you can create documents with multiple pages. Moreover, they can be imported into tools such as Adobe Illustrator to be incorporated with other graphics.

  • Often it is quicker to save a plot as a pdf and tinker in Illustrator rather than getting R to do exactly what you want
pdf("gapminder-plots.pdf")
plot(gapminder$pop,gapminder$lifeExp,pch=16,
     col="red",ylab="Life Expectancy",
     xlab="Population",main="Life Expectancy trend with population")
boxplot(gapminder$gdpPercap ~ gapminder$continent,col=c("red","orange","green","blue","purple"),
        main="GDP per-continent",
        xlab="Continent",
        ylab="GDP")
dev.off()
null device 
          1 

The canvas model

It is important to realise that base graphics in R uses a “canvas model” to create graphics. We can only overlay extra information on-top of an exising plot and cannot “undo” what is already drawn.

Let’s suppose we want to compare the relationship between GDP and Life Expectancy in 1952 and 2002

  • A variety of functions can be used to add extra data / annotations to a plot. e.g.
    • points
    • lines
    • text
    • legend
oldData <- gapminder[gapminder$year == 1952,]
oldData
newData <- gapminder[gapminder$year == 2002,]
newData
newData

We can start by plotting the life expectancy of the 1952 observations as red dots.

plot(oldData$gdpPercap, oldData$lifeExp,col="red",
     pch=16,
     xlab="GDP",
     ylab="Life Expectancy")

The points function can be used to addt extra points corresponding to 2002 countries on the existing plot.

  • Something doesn’t look right though…
plot(oldData$gdpPercap, oldData$lifeExp,col="red",
     pch=16,
     xlab="GDP",
     ylab="Life Expectancy")
points(newData$gdpPercap, newData$lifeExp,col="blue",
     pch=16,
     xlab="GDP",
     ylab="Life Expectancy")

The problem here is that the initial limits of the y axis were defined using the life expectancy range of the 1952 data. We can only add points to the existing plotting window, so anycountries with life expectancy outside this range in 2002 will not get displayed.

range(oldData$lifeExp)
[1] 28.8 72.7
range(oldData$gdpPercap)
[1]    298.8462 108382.3529
range(newData$lifeExp)
[1] 39.2 82.0
range(newData$gdpPercap)
[1]   241.1659 44683.9753

We can define the axes when we create the plot using xlim and ylim.

plot(oldData$gdpPercap, oldData$lifeExp,col="red",
     pch=16,
     xlab="GDP",
     ylab="Life Expectancy",
     xlim=c(0,1.1e5),ylim=c(30,90))
points(newData$gdpPercap, newData$lifeExp,col="blue",pch=16)

A legend can also be added using the legend function

  • You can specify a set of coordinates where you want to display the legend, but there are also shortcuts
plot(oldData$gdpPercap, oldData$lifeExp,col="red",
     pch=16,
     xlab="GDP",
     ylab="Life Expectancy",
     xlim=c(0,1.1e5),ylim=c(30,90))
points(newData$gdpPercap, newData$lifeExp,col="blue",pch=16)
legend("topright", legend=c(1952,2002),col=c("red","blue"),pch=16)

The text function works in a similar way to points

  • You give it the set of x and y coordinates you want to plot
    • plus also a set of labels
plot(oldData$gdpPercap, oldData$lifeExp,col="red",
     pch=16,
     xlab="GDP",
     ylab="Life Expectancy",
     xlim=c(0,1.1e5),ylim=c(30,90))
points(newData$gdpPercap, newData$lifeExp,col="blue",pch=16)
text(c(2e04,4e04),c(40,40),labels = c("Hello","World"))
legend("topright", legend=c(1952,2002),col=c("red","blue"),pch=16)




Exercise

  • What is that outlier on the x axis (GDP)?
    • use a logical test to find out
  • Use the text function to annotate the plot with the corresponding country name
plot(oldData$gdpPercap, oldData$lifeExp,col="red",
     pch=16,
     xlab="GDP",
     ylab="Life Expectancy",
     xlim=c(0,1.1e5),ylim=c(30,90))
points(newData$gdpPercap, newData$lifeExp,col="blue",pch=16)

## Some code here to find the outlier here...



Specifying a vector of colours

  • So far we have used a vector of length 1 to specify the colours
    • e.g. `col=“red”
  • We needn’t have to do this. The col argument can have multiple values
    • the values will get “re-cycled” in order to complete the plot
  • In the code below we use either red or blue to colour the points
    • only one plot command is used
    • since 1952 and 2002 alernate in the data frame, the years will be plotted red or blue accordingly
subset <- gapminder[gapminder$year %in% c(1952, 2002),]
subset
plot(subset$gdpPercap, subset$lifeExp,
     xlab="GDP",
     ylab="Life Expectancy",
     xlim=c(0,1.1e5),ylim=c(30,90),
     col=c("red","blue"),
     pch=c(16,17))

A useful trick to make plots look nice is to take advantage of pre-existing colour palettes in R. The RColorBrewer package is a useful package for such palettes; many of which are friendly to those with visual impairments.

library(RColorBrewer)
display.brewer.all(colorblindFriendly = TRUE)

The brewer.pal function can return the names of n colours from one of the pre-defined palettes to be used as a col argument to a plotting function.

boxplot(gapminder$gdpPercap ~ gapminder$continent,col=brewer.pal(5,"Set1"),
        main="GDP per-continent",
        xlab="Continent",
        ylab="GDP")

Plot Layouts

A further solution to the problem of comparing different years might be to have two plots arranged side-by-side

  • One option that can be set with par is multiple figures by row
    • argument is a vector containing number of columns and rows in the figure
par(mfrow=c(1,2))
plot(oldData$gdpPercap, oldData$lifeExp,col="red",
     pch=16,
     xlab="GDP",
     ylab="Life Expectancy",
     xlim=c(0,1.1e5),ylim=c(30,90))
plot(newData$gdpPercap, newData$lifeExp,col="blue",
     pch=16,
     xlab="GDP",
     ylab="Life Expectancy",
     xlim=c(0,1.1e5),ylim=c(30,90))

Don’t need to have the same kind of plot in each cell.

par(mfrow=c(1,2))
plot(oldData$gdpPercap, oldData$lifeExp,col="red",
     pch=16,
     xlab="GDP",
     ylab="Life Expectancy",
     xlim=c(0,1.1e5),ylim=c(30,90))
boxplot(oldData$lifeExp ~ oldData$continent)

There is plenty more to know about plotting that we don’t have time to cover

  • adding lines of best fit with abline
  • adding grids with grid
  • control over axis appearance and labels with axis
  • for more inspiration, see the R graph gallery

(Optional)

Statistical Testing

We can’t really have a run-through of the R language without at least mentioning statistics! However, like plotting it is a vast field. The main challenges are putting your data in the correct format (which we have covered here), and deciding which test to use (which R will not advise you on!)

The t.test function is probably the most fundamental statistical testing function in R, and can be adapted to many different situations. Full details are given in the help page ?t.test. Lets consider we have two vectors of normally-distributed data that we can visualise using a boxplot.

x <- rnorm(20)
y <- rnorm(20, 5,1)
df <- data.frame(x,y)
boxplot(df)

The output from t.test can be used to judge if there is a statistically-significant difference in means:-

t.test(x,y)

    Welch Two Sample t-test

data:  x and y
t = -16.217, df = 37.628, p-value < 2.2e-16
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -5.672333 -4.412951
sample estimates:
 mean of x  mean of y 
-0.0410879  5.0015542 

If our data were paired we could set the argument paired=TRUE to use a different flavour of the test

t.test(x,y,paired = TRUE)

    Paired t-test

data:  x and y
t = -17.977, df = 19, p-value = 2.192e-13
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -5.629741 -4.455543
sample estimates:
mean of the differences 
              -5.042642 

Similarly, if our data have different variances we can adjust the test accordingly:-

x <- rnorm(20)
y <- rnorm(20, 5,4)
df <- data.frame(x,y)
boxplot(df)

t.test(x,y,var.equal = FALSE)

    Welch Two Sample t-test

data:  x and y
t = -6.0106, df = 22.322, p-value = 4.476e-06
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -5.672837 -2.764186
sample estimates:
  mean of x   mean of y 
-0.08056119  4.13795064 

Were our data not normally-distributed we could use wilcox.test, for example. Fortunately, most statistical tests can be accessed in a similar manner, so it is easy to switch between using different tests provided your data are in the correct format. To re-iterate, the skill is in choosing which test is appropriate.

wilcox.test(x,y,var.equal = FALSE)

    Wilcoxon rank sum test

data:  x and y
W = 35, p-value = 1.126e-06
alternative hypothesis: true location shift is not equal to 0
LS0tCnRpdGxlOiAiUiBQcmltZXIiCmF1dGhvcjogIk1hcmsgRHVubmluZyIKZGF0ZTogJ2ByIGZvcm1hdChTeXMudGltZSgpLCAiTGFzdCBtb2RpZmllZDogJWQgJWIgJVkiKWAnCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKLS0tCgoKIyBBIHNob3J0IGludHJvZHVjdGlvbiB0byBSCgojIyBPdXRsaW5lCgpJbiB0aGlzIHNlc3Npb24sIHdlIGludHJvZHVjZSBzb21lIG9mIHRoZSBmdW5kYW1lbnRhbHMgb2YgdGhlIFIgbGFuZ3VhZ2UuIAoKVG9waWNzIGNvdmVyZWQgaW5jbHVkZTotIAoKLSBDcmVhdGluZyB2YXJpYWJsZXMKLSBVc2luZyBGdW5jdGlvbnMKLSBWZWN0b3JzCi0gRGF0YSBmcmFtZQotIFN1YnNldHRpbmcgZGF0YSwgdGhlIGJhc2UgUiB3YXkKLSBQbG90dGluZyAKLSAqKipIb3cgdG8gZ2V0IGhlbHAqKioKCkZvciBhIG1vcmUgZGV0YWlsZWQgaW50cm9kdWN0aW9uLCB3ZSBzdWdnZXN0IHRoZSBmb2xsb3dpbmcgKioqZnJlZSoqKiByZXNvdXJjZXMKCi0gW1NvbHZpbmcgQmlvbG9naWNhbCBQcm9ibGVtcyB3aXRoIFJdKGh0dHA6Ly9jYW1iaW90cmFpbmluZy5naXRodWIuaW8vci1pbnRyby8pCi0gW0ludHJvZHVjdGlvbiB0byBEYXRhIFNjaWVuY2Ugd2l0aCBSXShodHRwOi8vc2hvcC5vcmVpbGx5LmNvbS9wcm9kdWN0LzA2MzY5MjAwMzQ4MzQuZG8pCi0gW0NvdXJzZXJhIGNvdXJzZSBpbiBSXShodHRwOi8vYmxvZy5yZXZvbHV0aW9uYW5hbHl0aWNzLmNvbS8yMDEyLzEyL2NvdXJzZXJhLXZpZGVvcy5odG1sKQotIFtCZWdpbm5lcnMgSW50cm9kdWN0aW9uIHRvIFIgU3RhdGlzdGljYWwgU29mdHdhcmVdKGh0dHA6Ly9iaXRlc2l6ZWJpby5jb20vd2ViaW5hci8yMDYwMC9iZWdpbm5lcnMtaW50cm9kdWN0aW9uLXRvLXItc3RhdGlzdGljYWwtc29mdHdhcmUvKQotIFtSIHByb2dyYW1taW5nIHdpa2ldKGh0dHBzOi8vZW4ud2lraWJvb2tzLm9yZy93aWtpL1JfUHJvZ3JhbW1pbmcpCi0gW1F1aWNrIFJdKGh0dHA6Ly93d3cuc3RhdG1ldGhvZHMubmV0LykKCiMgUiBiYXNpY3MKCiMjIEFkdmFudGFnZXMgb2YgUgoKIVtdKC4uL2ltYWdlcy9OWVRpbWVzX1JfQXJ0aWNsZS5wbmcpCgpUaGUgUiBwcm9ncmFtbWluZyBsYW5ndWFnZSBpcyBub3cgcmVjb2duaXNlZCBiZXlvbmQgdGhlIGFjYWRlbWljIGNvbW11bml0eSBhcyBhbiBlZmZlY3Qgc29sdXRpb24gZm9yIGRhdGEgYW5hbHlzaXMgYW5kIHZpc3VhbGlzYXRpb24uIFtOb3RhYmxlIHVzZXJzIG9mIFJdKGh0dHA6Ly93d3cucmV2b2x1dGlvbmFuYWx5dGljcy5jb20vY29tcGFuaWVzLXVzaW5nLXIpIGluY2x1ZGU6LSAKCi0gW0ZhY2Vib29rXShodHRwOi8vYmxvZy5yZXZvbHV0aW9uYW5hbHl0aWNzLmNvbS8yMDEwLzEyL2FuYWx5c2lzLW9mLWZhY2Vib29rLXN0YXR1cy11cGRhdGVzLmh0bWwpLAotIFtnb29nbGVdKGh0dHA6Ly9ibG9nLnJldm9sdXRpb25hbmFseXRpY3MuY29tLzIwMDkvMDUvZ29vZ2xlLXVzaW5nLXItdG8tYW5hbHl6ZS1lZmZlY3RpdmVuZXNzLW9mLXR2LWFkcy5odG1sKSwKLSBbTWljcm9zb2Z0XShodHRwOi8vYmxvZy5yZXZvbHV0aW9uYW5hbHl0aWNzLmNvbS8yMDE0LzA1L21pY3Jvc29mdC11c2VzLXItZm9yLXhib3gtbWF0Y2htYWtpbmcuaHRtbCkgKHdobyByZWNlbnRseSBbaW52ZXN0ZWRdKGh0dHA6Ly9ibG9ncy5taWNyb3NvZnQuY29tL2Jsb2cvMjAxNS8wMS8yMy9taWNyb3NvZnQtYWNxdWlyZS1yZXZvbHV0aW9uLWFuYWx5dGljcy1oZWxwLWN1c3RvbWVycy1maW5kLWJpZy1kYXRhLXZhbHVlLWFkdmFuY2VkLXN0YXRpc3RpY2FsLWFuYWx5c2lzLykgaW4gYSBjb21tZXJpY2FsIHByb3ZpZGVyIG9mIFIpCi0gVGhlIFtOZXcgWW9yayBUaW1lc10oaHR0cDovL2Jsb2cucmV2b2x1dGlvbmFuYWx5dGljcy5jb20vMjAxMS8wMy9ob3ctdGhlLW5ldy15b3JrLXRpbWVzLXVzZXMtci1mb3ItZGF0YS12aXN1YWxpemF0aW9uLmh0bWwpLgotIFtCdXp6ZmVlZF0oaHR0cDovL2Jsb2cucmV2b2x1dGlvbmFuYWx5dGljcy5jb20vMjAxNS8xMi9idXp6ZmVlZC11c2VzLXItZm9yLWRhdGEtam91cm5hbGlzbS5odG1sKSB1c2UgUiBmb3Igc29tZSBvZiB0aGVpciBzZXJpb3VzIGFydGljbGVzIGFuZCBoYXZlIG1hZGUgdGhlIGNvZGUgW3B1YmxpY2FsbHkgYXZhaWxhYmxlXShodHRwczovL2J1enpmZWVkbmV3cy5naXRodWIuaW8vMjAxNi0wNC1mZWRlcmFsLXN1cnZlaWxsYW5jZS1wbGFuZXMvYW5hbHlzaXMuaHRtbCkKLSBUaGUgW05ldyBaZWFsYW5kIFRvdXJpc3QgQm9hcmRdKGh0dHBzOi8vbWJpZW56LnNoaW55YXBwcy5pby90b3VyaXNtX2Rhc2hib2FyZF9wcm9kLykgaGF2ZSBSIHJ1bm5pbmcgaW4gdGhlIGJhY2tncm91bmQgb2YgdGhlaXIgd2Vic2l0ZQotIFtBaXJibmJdKGh0dHBzOi8vbWVkaXVtLmNvbS9haXJibmItZW5naW5lZXJpbmcvdXNpbmctci1wYWNrYWdlcy1hbmQtZWR1Y2F0aW9uLXRvLXNjYWxlLWRhdGEtc2NpZW5jZS1hdC1haXJibmItOTA2ZmFhNThlMTJkKQotIFtUaGUgQkJDXShodHRwczovL2dpdGh1Yi5jb20vQkJDLURhdGEtVW5pdC9tdXNpYy1mZXN0aXZhbHMpIG1ha2VzIGNvZGUgYXZhaWxhYmxlIGZvciBzb21lIG9mIHRoZWlyIHN0b3JpZXMgKGUuZy4gZ2VuZGVyIGJpYXMgaW4gbXVzaWMgZmVzdGl2YWxzKQoKIVtdKGh0dHBzOi8vY2RuLWltYWdlcy0yLm1lZGl1bS5jb20vbWF4LzEyMDAvMSpCVE1iVkZoX2h6aUpKY2FRN1RCUndnLnBuZykKCgohW2R1a2Utc2NhbmRhbF0oLi4vaW1hZ2VzL3JlcC1yZXNlYXJjaC1ueXQucG5nKQoKVHdvIEJpb3N0YXRpc2NpYW5zIChsYXRlciB0ZXJtZWQgJypGb3JlbnNpYyBCaW9pbmZvcm1hdGljaWFucyonKSBmcm9tIE0uRC4gQW5kZXJzb24gdXNlZCBSIGV4dGVuc2l2ZWx5IGR1cmluZyB0aGVpciByZS1hbmFseXNpcyBhbmQgaW52ZXN0aWdhdGlvbiBvZiBhIENsaW5pY2FsIFByb2dub3N0aWNhdGlvbiBwYXBlciBmcm9tIER1a2UuIFRoZSBzdWJzZXF1ZW50IFtzY2FuZGFsXShodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PVc1c1pUTlBNUVJNKSBwdXQgUmVwcm9kdWNpYmxlIFJlc2VhcmNoIGF0IHRoZSBmb3JlZnJvbnQgb2YgZXZlcnlvbmUncyBtaW5kLgoKS2VpdGggQmFnZ2VybHkncyB0YWxrIG9uIHRoZSBzdWJqZWN0IGlzIGhpZ2hseS1yZWNvbW1lbmRlZC4KCjxpZnJhbWUgd2lkdGg9IjQyMCIgaGVpZ2h0PSIzMTUiIHNyYz0iaHR0cHM6Ly93d3cueW91dHViZS5jb20vZW1iZWQvN2dZSXM3dVliTW8iIGZyYW1lYm9yZGVyPSIwIiBhbGxvd2Z1bGxzY3JlZW4+PC9pZnJhbWU+CgojIyBTdXBwb3J0IGZvciBSCgotIE9ubGluZSBmb3J1bXMgc3VjaCBhcyBbU3RhY2sgT3ZlcmZsb3ddKGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvdGFnZ2VkL3IpIHJlZ3VsYXJseSBmZWF0dXJlIFIKLSBbQmxvZ3NdKGh0dHA6Ly93d3cuci1ibG9nZ2Vycy5jb20vKQotIExvY2FsIFt1c2VyIGdyb3Vwc10oaHR0cDovL2Jsb2cucmV2b2x1dGlvbmFuYWx5dGljcy5jb20vbG9jYWwtci1ncm91cHMuaHRtbCkgCi0gRG9jdW1lbnRhdGlvbiB2aWEgYD9gIG9yIGBoZWxwLnN0YXJ0KClgCi0gRG9jdW1lbnRhdGlvbiBmb3IgcGFja2FnZXMgaXMgZm91bmQgdmlhIHRoZSBQYWNrYWdlcyB0YWIgaW4gdGhlIGJvdHRvbS1yaWdodCBvZiBSU3R1ZGlvLgoKCiMjIFJTdHVkaW8KCi0gUnN0dWRpbyBpcyBhIGZyZWUgZW52aXJvbm1lbnQgZm9yIFIKLSBDb252ZW5pZW50IG1lbnVzIHRvIGFjY2VzcyBzY3JpcHRzLCBkaXNwbGF5IHBsb3RzCi0gU3RpbGwgbmVlZCB0byB1c2UgKmNvbW1hbmQtbGluZSogdG8gZ2V0IHRoaW5ncyBkb25lCi0gRGV2ZWxvcGVkIGJ5IHNvbWUgb2YgdGhlIGxlYWRpbmcgUiBwcm9ncmFtbWVycwotIFVzZWQgYnkgYmVnaW5uZXJzLCBhbmQgZXhwZXJpZW5jZWQgdXNlcnMgYWxpa2UKClRvIGdldCBzdGFydGVkLCB5b3Ugd2lsbCBuZWVkIHRvIGluc3RhbGwgdGhlIFtsYXRlc3QgdmVyc2lvbiBvZiBSXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy8pIGFuZCBbUlN0dWRpbyBEZXNrdG9wXShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9wcm9kdWN0cy9yc3R1ZGlvL2Rvd25sb2FkMy8pOyBib3RoIG9mIHdoaWNoIGFyZSAqKipmcmVlKioqLiAKCk9uY2UgaW5zdGFsbGVkLCB5b3Ugc2hvdWxkIGJlIGFibGUgdG8gbGF1bmNoIFJTdHVkaW8gYnkgY2xpY2tpbmcgb24gaXRzIGljb246LQoKIVtdKGh0dHA6Ly93d3cucnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTQvMDMvYmx1ZS0xMjUucG5nKQoKIyMgQmlvY29uZHVjdG9yCgoKIVtdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL2ltYWdlcy9sb2dvX2Jpb2NvbmR1Y3Rvci5naWYpCgoKRXN0YWJsaXNoZWQgaW4gMjAwMSBhcyBhIG1lYW5zIG9mIGRpc3RyaWJ1dGluZyB0b29scyBmb3IgdGhlIGFuYWx5c2lzIGFuZCBjb21wcmVoZW5zaW9uIG9mIGhpZ2gtdGhyb3VnaHB1dCBnZW5vbWljIGRhdGEgaW4gUi4gSW5pdGlhbGx5IGZvY3VzZWQgb24gbWljcm9hcnJheXMsIGJ1dCBub3cgaGFzIG1hbnkgdG9vbHMgZm9yIE5HUyBkYXRhCgotIHByaW1hcnkgcHJvY2Vzc2luZyBvZiBOR1MgZGF0YSBuZWFybHktYWx3YXlzIGRvbmUgaW4gb3RoZXIgbGFuZ3VhZ2VzCiAgICArIFIgaXMgZXh0ZW5zaXZlbHktdXNlZCBmb3IgdmlzdWFsaXNhdGlvbiBhbmQgaW50ZXJwcmV0YXRpb24gb25jZSBkYXRhIGFyZSBpbiBtYW5hZ2VhYmxlIGZvcm0KCk9uIHRoZSBbQmlvY29uZHVjdG9yIHdlYnNpdGVdKHd3dy5iaW9jb25kdWN0b3Iub3JnKSwuIHlvdSB3aWxsIGZpbmQKCi0gSW5zdGFsbGF0aW9uIGluc3RydWN0aW9ucwotIFtDb3Vyc2UgTWF0ZXJpYWxzXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9oZWxwL2NvdXJzZS1tYXRlcmlhbHMvKQotIFtTdXBwb3J0IGZvcnVtXShodHRwczovL3N1cHBvcnQuYmlvY29uZHVjdG9yLm9yZy8pCiAgICArIGEgbWVhbnMgb2YgY29tbXVuaWNhdGluZyB3aXRoIGRldmVsb3BlcnMgYW5kIHBvd2VyLXVzZXJzCi0gW0V4YW1wbGUgZGF0YXNldHNdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvQmlvY1ZpZXdzLmh0bWwjX19fRXhwZXJpbWVudERhdGEpCi0gW0Fubm90YXRpb24gUmVzb3VyY2VzXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL0Jpb2NWaWV3cy5odG1sI19fX0Fubm90YXRpb25EYXRhKQotIENvbmZlcmVuY2VzCiAgICArIHVwY29taW5nIGNvbmZlcmVuY2UgaW4gQ2FtYnJpZGdlIC0gRGVjZW1iZXIgMjAxNwoKCiMgRW50ZXJpbmcgY29tbWFuZHMgaW4gUgoKLSBUaGUgdHJhZGl0aW9uYWwgd2F5IHRvIGVudGVyIFIgY29tbWFuZHMgaXMgdmlhIHRoZSBUZXJtaW5hbCwgb3IgdXNpbmcgdGhlIGNvbnNvbGUgaW4gUlN0dWRpbyAoYm90dG9tLWxlZnQgcGFuZWwgd2hlbiBSU3R1ZGlvIG9wZW5zIGZvciBmaXJzdCB0aW1lKS4KLSBIb3dldmVyLCBmb3IgdGhpcyBjb3Vyc2Ugd2Ugd2lsbCB1c2UgYSByZWxhdGl2ZWx5IG5ldyBmZWF0dXJlIGNhbGxlZCAqUi1ub3RlYm9va3MqLgotIEFuIFItbm90ZWJvb2sgbWl4ZXMgcGxhaW4gdGV4dCB3aXRoIFIgY29kZQoKTWFya2Rvd24gaXMgYSB2ZXJ5IHNpbXBsZSB3YXkgb2Ygd3JpdGluZyBhIHRlbXBsYXRlIHRvIHByb2R1Y2UgYSBwZGYsIEhUTUwgb3Igd29yZCBkb2N1bWVudC4gVGhlIGNvbXBpbGVkIHZlcnNpb24gb2YgdGhpcyBkb2N1bWVudCBpcyBhdmFpbGFibGUgb25saW5lIGFuZCBpcyBtb3JlIGNvbnZlbmllbnQgdG8gYnJvd3NlIFtodHRwczovL2Jpb2luZm9ybWF0aWNzLWNvcmUtc2hhcmVkLXRyYWluaW5nLmdpdGh1Yi5pby9jcnVrLXN1bW1lci1zY2hvb2wtMjAxNy9EYXkxL1Nlc3Npb24yLVJub3Rlcy5uYi5odG1sXShoZXJlKS4KCgoKLSAiY2h1bmtzIiBvZiBSIGNvZGUgY2FuIGJlIGFkZGVkIHVzaW5nIHRoZSAqaW5zZXJ0KiBvcHRpb24gZnJvbSB0aGUgdG9vbGJhciwgb3IgdGhlIENUUkwgKyBBTFQgKyBJIHNob3J0Y3V0Ci0gRWFjaCBsaW5lIG9mIFIgY2FuIGJlIGV4ZWN1dGVkIGJ5IGNsaWNraW5nIG9uIHRoZSBsaW5lIGFuZCBwcmVzc2luZyBDVFJMIGFuZCBFTlRFUgotIE9yIHlvdSBjYW4gcHJlc3MgdGhlIGdyZWVuIHRyaWFuZ2xlIG9uIHRoZSByaWdodC1oYW5kIHNpZGUgdG8gcnVuIGV2ZXJ5dGhpbmcgaW4gdGhlIGNodW5rCi0gVHJ5IHRoaXMgbm93IQotIFRoZSBjb2RlIG1pZ2h0IGhhdmUgZGlmZmVyZW50IG9wdGlvbnMgd2hpY2ggZGljdGF0ZSBob3cgdGhlIG91dHB1dCBpcyBkaXNwbGF5ZWQgaW4gdGhlIGNvbXBpbGVkIGRvY3VtZW50IChlLmcuIEhUTUwpCiAgICArIGUuZy4geW91IG1pZ2h0IHNlZSBgRVZBTCA9IEZBTFNFYCBvciBgZWNobyA9IEZBTFNFYAogICAgKyB5b3UgZG9uJ3QgaGF2ZSB0byB3b3JyeSBhYm91dCB0aGlzIGlmIHN0ZXBwaW5nIHRocm91Z2ggdGhlIG1hcmtkb3duIGludGVyYWN0aXZlbHkKCmBgYHtyfQpwcmludCgiSGVsbG8gV29ybGQiKQoKYGBgCgoqVGhpcyB3aWxsIGJlIGRpc3BsYXllZCBpbiBpdGFsaWMqCgoqKlRoaXMgd2lsbCBiZSBkaXNwbGF5ZWQgaW4gYm9sZCoqCgoKLSB0aGlzIAotIGlzIAotIGEgCi0gbGlzdAogICAgKyB0aGlzIGlzIGEgKnN1Yi1saXN0KgoKWW91IGNhbiBhbHNvIGFkZCBoeXBlcmxpbmtzLCBpbWFnZXMgYW5kIHRhYmxlcy4gTW9yZSBoZWxwIGlzIGF2YWlsYWJsZSB0aHJvdWdoIFJTdHVkaW8gKipIZWxwIC0+IE1hcmtkb3duIFF1aWNrIFJlZmVyZW5jZSoqCgpBdCBhIGJhc2ljIGxldmVsLCB3ZSBjYW4gdXNlIFIgYXMgYSBjYWxjdWxhdG9yIHRvIGNvbXB1dGUgc2ltcGxlIHN1bXMgd2l0aCB0aGUgYCtgLCBgLWAsIGAqYCAoZm9yIG11bHRpcGxpY2F0aW9uKSBhbmQgYC9gIChmb3IgZGl2aXNpb24pIHN5bWJvbHMuIAoKIVtdKC4uL2ltYWdlcy8xMjhweC1TSEFSUF9FTFNJTUFURV9FTC1XMjIxLmpwZykKCgpUcnkgYWRkaW5nIHNvbWUgb2YgeW91ciBvd24gY2FsY3VsYXRpb25zIGJlbG93Li4uLgoKYGBge3J9CjIgKyAyCjIgLSAyCjQgKiAzCjEwIC8gMgoKCmBgYAoKVGhlIGFuc3dlciBpcyBkaXNwbGF5ZWQgYXQgdGhlIGNvbnNvbGUgd2l0aCBhIGBbMV1gIGluIGZyb250IG9mIGl0LiBUaGUgYDFgIGluc2lkZSB0aGUgc3F1YXJlIGJyYWNrZXRzIGlzIGEgcGxhY2UtaG9sZGVyIHRvIHNpZ25pZnkgaG93IG1hbnkgdmFsdWVzIHdlcmUgaW4gdGhlIGFuc3dlciAoaW4gdGhpcyBjYXNlIG9ubHkgb25lKS4gV2Ugd2lsbCB0YWxrIGFib3V0IGRlYWxpbmcgd2l0aCBsaXN0cyBvZiBudW1iZXJzIHNob3J0bHkuLi4KCkluIHRoZSBjYXNlIG9mIGV4cHJlc3Npb25zIGludm9sdmluZyBtdWx0aXBsZSBvcGVyYXRpb25zLCBSIHJlc3BlY3RzIHRoZSBbQk9ETUFTXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9PcmRlcl9vZl9vcGVyYXRpb25zI01uZW1vbmljcykgc3lzdGVtIHRvIGRlY2lkZSB0aGUgb3JkZXIgaW4gd2hpY2ggb3BlcmF0aW9ucyBzaG91bGQgYmUgcGVyZm9ybWVkLgoKYGBge3J9CjIgKyAyICozCjIgKyAoMiAqIDMpCigyICsgMikgKiAzCgoKYGBgCgoKUiBpcyBjYXBhYmxlIG9mIG1vcmUgY29tcGxpY2F0ZWQgYXJpdGhtZXRpYyBzdWNoIGFzIHRyaWdvbm9tZXRyeSBhbmQgbG9nYXJpdGhtczsgbGlrZSB5b3Ugd291bGQgZmluZCBvbiBhIGZhbmN5IHNjaWVudGlmaWMgY2FsY3VsYXRvci4gT2YgY291cnNlLCBSIGFsc28gaGFzIGEgcGxldGhvcmEgb2Ygc3RhdGlzdGljYWwgb3BlcmF0aW9ucyBhcyB3ZSB3aWxsIHNlZS4KCiFbXSguLi9pbWFnZXMvMTI4cHgtQ2FzaW8tZngxMTVFUy01NTY0LmpwZykKCmBgYHtyfQpwaQpzaW4gKHBpLzIpCmNvcyhwaSkKdGFuKDIpCmxvZygxKQoKCmBgYAoKV2UgY2FuIG9ubHkgZ28gc28gZmFyIHdpdGggcGVyZm9ybWluZyBzaW1wbGUgY2FsY3VsYXRpb25zIGxpa2UgdGhpcy4gRXZlbnR1YWxseSB3ZSB3aWxsIG5lZWQgdG8gc3RvcmUgb3VyIHJlc3VsdHMgZm9yIGxhdGVyIHVzZS4gRm9yIHRoaXMsIHdlIG5lZWQgdG8gbWFrZSB1c2Ugb2YgKnZhcmlhYmxlcyouCgojIyBWYXJpYWJsZXMKCkEgdmFyaWFibGUgaXMgYSBsZXR0ZXIgb3Igd29yZCB3aGljaCB0YWtlcyAob3IgY29udGFpbnMpIGEgdmFsdWUuIFdlCnVzZSB0aGUgYXNzaWdubWVudCAnb3BlcmF0b3InLCBgPC1gIHRvIGNyZWF0ZSBhIHZhcmlhYmxlIGFuZCBzdG9yZSBzb21lIHZhbHVlIGluIGl0LiAKCmBgYHtyfQp4IDwtIDEwCngKbXlOdW1iZXIgPC0gMjUKbXlOdW1iZXIKCgpgYGAKV2UgYWxzbyBjYW4gcGVyZm9ybSBhcml0aG1ldGljIG9uIHZhcmlhYmxlcyB1c2luZyBmdW5jdGlvbnM6CgpgYGB7cn0Kc3FydChteU51bWJlcikKYGBgCgpXZSBjYW4gYWRkIHZhcmlhYmxlcyB0b2dldGhlcjoKYGBge3J9CnggKyBteU51bWJlcgpgYGAKCgpXZSBjYW4gY2hhbmdlIHRoZSB2YWx1ZSBvZiBhbiBleGlzdGluZyB2YXJpYWJsZToKCmBgYHtyfQp4IDwtIDIxCngKYGBgCgotIFdlIGNhbiBzZXQgb25lIHZhcmlhYmxlIHRvIGVxdWFsIHRoZSB2YWx1ZSBvZiBhbm90aGVyIHZhcmlhYmxlOgoKYGBge3J9CnggPC0gbXlOdW1iZXIKeApgYGAKCldoZW4gd2UgYXJlIGZlZWxpbmcgbGF6eSB3ZSBtaWdodCBnaXZlIG91ciB2YXJpYWJsZXMgc2hvcnQgbmFtZXMgKGB4YCwgYHlgLCBgaWAuLi5ldGMpLCBidXQgYSBiZXR0ZXIgcHJhY3RpY2Ugd291bGQgYmUgdG8gZ2l2ZSB0aGVtIG1lYW5pbmdmdWwgbmFtZXMuIFRoZXJlIGFyZSBzb21lIHJlc3RyaWN0aW9ucyBvbiBjcmVhdGluZyB2YXJpYWJsZSBuYW1lcy4gVGhleSBjYW5ub3Qgc3RhcnQgd2l0aCBhIG51bWJlciBvciBjb250YWluIGNoYXJhY3RlcnMgc3VjaCBhcyBgLmAsIGBfYCwgJy0nLiBOYW1pbmcgdmFyaWFibGVzIHRoZSBzYW1lIGFzIGluLWJ1aWx0IGZ1bmN0aW9ucyBpbiBSLCBzdWNoIGFzIGBjYCwgYFRgLCBgbWVhbmAgc2hvdWxkIGFsc28gYmUgYXZvaWRlZC4KCgojIyBGdW5jdGlvbnMKCioqRnVuY3Rpb25zKiogaW4gUiBwZXJmb3JtIG9wZXJhdGlvbnMgb24gKiphcmd1bWVudHMqKiAodGhlIGlucHV0cyhzKSB0byB0aGUgZnVuY3Rpb24pLiBXZSBoYXZlIGFscmVhZHkgdXNlZDoKCmBgYHtyfQpzaW4oeCkKYGBgCgp0aGlzIHJldHVybnMgdGhlIHNpbmUgb2YgeC4gSW4gdGhpcyBjYXNlIHRoZSBmdW5jdGlvbiBoYXMgb25lIGFyZ3VtZW50OiAqKngqKi4gCgotIEFyZ3VtZW50cyBhcmUgYWx3YXlzIGNvbnRhaW5lZCBpbiBwYXJlbnRoZXNlcyAtLSBjdXJ2ZWQgYnJhY2tldHMsICoqKCkqKiAtLSBzZXBhcmF0ZWQgYnkgY29tbWFzLgoKCi0gQXJndW1lbnRzIGNhbiBiZSBuYW1lZCBvciB1bm5hbWVkLCBidXQgaWYgdGhleSBhcmUgdW5uYW1lZCB0aGV5IG11c3QgYmUgb3JkZXJlZCAod2Ugd2lsbCBzZWUgbGF0ZXIgaG93IHRvIGZpbmQgdGhlIHJpZ2h0IG9yZGVyKS4gVGhlIG5hbWVzIG9mIHRoZSBhcmd1bWVudHMgYXJlIGRldGVybWluZWQgYnkgdGhlIGF1dGhvciBvZiB0aGUgZnVuY3Rpb24gYW5kIGNhbiBiZSBmb3VuZCBpbiB0aGUgaGVscCBwYWdlIGZvciB0aGUgZnVuY3Rpb24uIFdoZW4gdGVzdGluZyBjb2RlLCBpdCBpcyBlYXNpZXIgYW5kIHNhZmVyIHRvIG5hbWUgdGhlIGFyZ3VtZW50cy4gCgpgcm5vcm1gIGlzIGEgZnVuY3Rpb24gdGhhdCB3aWxsIGdlbmVyYXRlIGEgc2VyaWVzIG9mIHZhbHVlcyBmcm9tIGEgKm5vcm1hbCBkaXN0cmlidXRpb24qLiBJbiBvcmRlciB0byB1c2UgdGhlIGZ1bmN0aW9uLCB3ZSBuZWVkIHRvIHRlbGwgUiBob3cgbWFueSB2YWx1ZXMgd2Ugd2FudAoKYGBge3J9CnJub3JtKG49MTApCmBgYAoKVGhlIG5vcm1hbCBkaXN0cmlidXRpb24gaXMgZGVmaW5lZCBieSBhICptZWFuKiAoYXZlcmFnZSkgYW5kICpzdGFuZGFyZCBkZXZpYXRpb24qIChzcHJlYWQpLiBIb3dldmVyLCBpbiB0aGUgYWJvdmUgZXhhbXBsZSB3ZSBkaWRuJ3QgdGVsbCBSIHdoYXQgbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIHdlIHdhbnRlZC4gU28gaG93IGRvZXMgUiBrbm93IHdoYXQgdG8gZG8/IEFsbCBhcmd1bWVudHMgdG8gYSBmdW5jdGlvbiBhbmQgdGhlaXIgZGVmYXVsdCB2YWx1ZXMgYXJlIGxpc3RlZCBpbiB0aGUgaGVscCBwYWdlCgooKk4uQiBzb21ldGltZXMgaGVscCBwYWdlcyBjYW4gZGVzY3JpYmUgbW9yZSB0aGFuIG9uZSBmdW5jdGlvbiopCgpgYGB7ciBldmFsPUZBTFNFfQo/cm5vcm0KYGBgCgpUaGUgaGVscCBwYWdlIGlzIG9mdGVuIHRoZSBiZXN0IHdheSB0byBmaW5kIG91dCBhYm91dCBhIGZ1bmN0aW9uLiBPdGhlcndpc2UsICoqZ29vZ2xlIGlzIHlvdXIgZnJpZW5kKiouCgpJbiB0aGlzIGNhc2UsIHdlIHNlZSB0aGF0IHRoZSBkZWZhdWx0cyBmb3IgbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIGFyZSAwIGFuZCAxLiBXZSBjYW4gY2hhbmdlIHRoZSBmdW5jdGlvbiB0byBnZW5lcmF0ZSB2YWx1ZXMgZnJvbSBhIGRpc3RyaWJ1dGlvbiB3aXRoIGEgZGlmZmVyZW50IG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiB1c2luZyB0aGUgYG1lYW5gIGFuZCBgc2RgICphcmd1bWVudHMqLiBJdCBpcyBpbXBvcnRhbnQgdGhhdCB3ZSBnZXQgdGhlIHNwZWxsaW5nIG9mIHRoZXNlIGFyZ3VtZW50cyBleGFjdGx5IHJpZ2h0LCBvdGhlcndpc2UgUiB3aWxsIGFuIGVycm9yIG1lc3NhZ2UsIG9yICh3b3JzZT8pIGRvIHNvbWV0aGluZyB1bmV4cGVjdGVkLgoKYGBge3J9CnJub3JtKG49MTAsIG1lYW49MixzZD0zKQpybm9ybSgxMCwgMiwgMykKCgpgYGAKCkluIHRoZSBleGFtcGxlLCBgcm5vcm1gIGlzIG91dHB1dHRpbmcgYSBzZXJpZXMgb2YgbnVtYmVycywgd2hpY2ggaXMgY2FsbGVkIGEgKnZlY3RvciogaW4gUiBhbmQgaXMgdGhlIG1vc3QtZnVuZGFtZW50YWwgZGF0YS10eXBlLgoKCiMjIFBhY2thZ2VzIGluIFIKClNvIGZhciB3ZSBoYXZlIHVzZWQgZnVuY3Rpb25zIHRoYXQgYXJlIGF2YWlsYWJsZSB3aXRoIHRoZSAqYmFzZSogZGlzdHJpYnV0aW9uIG9mIFI7IHRoZSBmdW5jdGlvbnMgeW91IGdldCB3aXRoIGEgY2xlYW4gaW5zdGFsbCBvZiBSLiBUaGUgb3Blbi1zb3VyY2UgbmF0dXJlIG9mIFIgZW5jb3VyYWdlcyBvdGhlcnMgdG8gd3JpdGUgdGhlaXIgb3duIGZ1bmN0aW9ucyBmb3IgdGhlaXIgcGFydGljdWxhciBkYXRhLXR5cGUgb3IgYW5hbHlzZXMuCgpQYWNrYWdlcyBhcmUgZGlzdHJpYnV0ZWQgdGhyb3VnaCAqcmVwb3NpdG9yaWVzKi4gVGhlIG1vc3QtY29tbW9uIG9uZXMgYXJlIENSQU4gYW5kIEJpb2NvbmR1Y3Rvci4gQ1JBTiBhbG9uZSBoYXMgbWFueSB0aG91c2FuZHMgb2YgcGFja2FnZXMuCgpUaGUgKipQYWNrYWdlcyoqIHRhYiBpbiB0aGUgYm90dG9tLXJpZ2h0IHBhbmVsIG9mIFJTdHVkaW8gbGlzdHMgYWxsIHBhY2thZ2VzIHRoYXQgeW91IGN1cnJlbnRseSBoYXZlIGluc3RhbGxlZC4gQ2xpY2tpbmcgb24gYSBwYWNrYWdlIG5hbWUgd2lsbCBzaG93IGEgbGlzdCBvZiBmdW5jdGlvbnMgdGhhdCBhdmFpbGFibGUgb25jZSB0aGF0IHBhY2thZ2UgaGFzIGJlZW4gbG9hZGVkLiBUaGUgYGxpYnJhcnlgIGZ1bmN0aW9uIGlzIHVzZWQgdG8gbG9hZCBhIHBhY2thZ2UgYW5kIG1ha2UgaXQncyBmdW5jdGlvbnMgLyBkYXRhIGF2YWlsYWJsZSBpbiB5b3VyIGN1cnJlbnQgUiBzZXNzaW9uLiAqWW91IG5lZWQgdG8gZG8gdGhpcyBldmVyeSB0aW1lIHlvdSBsb2FkIGEgbmV3IFJTdHVkaW8gc2Vzc2lvbiouIAoKCmBgYHtyIGV2YWw9RkFMU0V9CmxpYnJhcnkoUkNvbG9yQnJld2VyKQpgYGAKClRoZXJlIGFyZSBmdW5jdGlvbnMgZm9yIGluc3RhbGxpbmcgcGFja2FnZXMgd2l0aGluIFIuIElmIHlvdXIgcGFja2FnZSBpcyBwYXJ0IG9mIHRoZSBtYWluICoqQ1JBTioqIHJlcG9zaXRvcnksIHlvdSBjYW4gdXNlIGBpbnN0YWxsLnBhY2thZ2VzYC4KCi0gSGVyZSwgYHRpZHlyYCBpcyBhIHVzZWZ1bCBwYWNrYWdlIGZvciBjbGVhbmluZyBhbmQgcmVzaGFwaW5nIGRhdGE6LQoKYGBge3IgZXZhbD1GQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygidGlkeXIiKQpgYGAKCipCaW9jb25kdWN0b3IqIHBhY2thZ2VzIGhhdmUgdGhlaXIgb3duIGluc3RhbGwgc2NyaXB0LCB3aGljaCB5b3UgY2FuIGRvd25sb2FkIGZyb20gdGhlIEJpb2NvbmR1Y3RvciB3ZWJzaXRlCgpgYGB7ciBldmFsPUZBTFNFfQpzb3VyY2UoImh0dHA6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9iaW9jTGl0ZS5SIikKYmlvY0xpdGUoImFmZnkiKQpgYGAKCkEgcGFja2FnZSBtYXkgaGF2ZSBzZXZlcmFsICpkZXBlbmRhbmNpZXMqOyBvdGhlciBSIHBhY2thZ2VzIGZyb20gd2hpY2ggaXQgdXNlcyBmdW5jdGlvbnMgb3IgZGF0YSB0eXBlcyAocmUtdXNpbmcgY29kZSBmcm9tIG90aGVyIHBhY2thZ2VzIGlzIHN0cm9uZ2x5LWVuY291cmFnZWQpLiBJZiB0aGlzIGlzIHRoZSBjYXNlLCB0aGUgb3RoZXIgUiBwYWNrYWdlcyB3aWxsIGJlIGxvY2F0ZWQgYW5kIGluc3RhbGxlZCB0b28uCgoqKlNvIGxvbmcgYXMgeW91IHN0aWNrIHdpdGggdGhlIHNhbWUgdmVyc2lvbiBvZiBSLCB5b3Ugd29uJ3QgbmVlZCB0byByZXBlYXQgdGhpcyBpbnN0YWxsIHByb2Nlc3MuKioKCiMgRGVhbGluZyB3aXRoIGRhdGEKCldlIGFyZSBnb2luZyB0byBleHBsb3JlIHNvbWUgb2YgdGhlIGJhc2ljIGZlYXR1cmVzIG9mIFIgdXNpbmcgZGF0YSBmcm9tIHRoZSBbZ2FwbWluZGVyXShodHRwczovL3d3dy5nYXBtaW5kZXIub3JnL2RhdGEvKSBwcm9qZWN0LCB3aGljaCBoYXZlIGJlZW4gYnVuZGxlZCBpbnRvIGFuIFtSIHBhY2thZ2VdKGh0dHBzOi8vZ2l0aHViLmNvbS9qZW5ueWJjL2dhcG1pbmRlcikuIFRoZXNlIGRhdGEgZ2l2ZSB2YXJpb3VzIGluZGljYXRvciB2YXJpYWJsZXMgZm9yIGRpZmZlcmVudCBjb3VudHJpZXMgYXJvdW5kIHRoZSB3b3JsZCAobGlmZSBleHBlY3RhbmN5LCBwb3B1bGF0aW9uIGFuZCBHcm9zcyBEb21lc3RpYyBQcm9kdWN0KS4gV2UgaGF2ZSBzYXZlZCB0aGVzZSBkYXRhIGFzIGEgYC5jc3ZgIGZpbGUgdG8gZGVtb25zdHJhdGUgaG93IHRvIGltcG9ydCBkYXRhIGludG8gUi4KCllvdSBjYW4gZG93bmxvYWQgdGhlc2UgZGF0YSBbaGVyZV0oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2Jpb2luZm9ybWF0aWNzLWNvcmUtc2hhcmVkLXRyYWluaW5nL3ItaW50ZXJtZWRpYXRlL21hc3Rlci9nYXBtaW5kZXIuY3N2KS4gUmlnaHQtY2xpY2sgdGhlIGxpbmsgYW5kIHNhdmUgdG8gc29tZXdoZXJlIG9uIHlvdXIgY29tcHV0ZXIgdGhhdCB5b3Ugd2lzaCB0byB3b3JrIGZyb20uCgojIyBUaGUgd29ya2luZyBkaXJlY3RvcnkKCkxpa2Ugb3RoZXIgc29mdHdhcmUgKFdvcmQsIEV4Y2VsLCBQaG90b3Nob3AuLi4uKSwgUiBoYXMgYSBkZWZhdWx0IGxvY2F0aW9uIHdoZXJlIGl0IHdpbGwgc2F2ZSBmaWxlcyB0byBhbmQgaW1wb3J0IGRhdGEgZnJvbS4gVGhpcyBpcyBrbm93biBhcyB0aGUgKndvcmtpbmcgZGlyZWN0b3J5KiBpbiBSLiBZb3UgY2FuIHF1ZXJ5IHdoYXQgUiBjdXJyZW50bHkgY29uc2lkZXJzIGl0cyB3b3JraW5nIGRpcmVjdG9yeSBieSBkb2luZzotCgpgYGB7ciBldmFsPUZBTFNFfQpnZXR3ZCgpCmBgYAoKKk4uQi4gSGVyZSwgYSBzZXQgb2Ygb3BlbiBhbmQgY2xvc2VkIGJyYWNrZXRzIGAoKWAgaXMgdXNlZCB0byBjYWxsIHRoZSBgZ2V0d2RgIGZ1bmN0aW9uIHdpdGggbm8gYXJndW1lbnRzLioKCldlIGNhbiBhbHNvIGxpc3QgdGhlIGZpbGVzIGluIHRoaXMgZGlyZWN0b3J5IHdpdGg6LQoKYGBge3J9Cmxpc3QuZmlsZXMoKQpgYGAKCkFueSBgLmNzdmAgZmlsZSBpbiB0aGUgd29ya2luZyBkaXJlY3RvcnkgY2FuIGJlIGltcG9ydGVkIGludG8gUiBieSBzdXBwbHlpbmcgdGhlIG5hbWUgb2YgdGhlIGZpbGUgdG8gdGhlIGByZWFkLmNzdmAgZnVuY3Rpb24gYW5kIGNyZWF0aW5nIGEgbmV3IHZhcmlhYmxlIHRvIHN0b3JlIHRoZSByZXN1bHQuIEEgdXNlZnVsIHNhbml0eSBjaGVjayBpcyB0aGUgYGZpbGUuZXhpc3RzYCBmdW5jdGlvbiB3aGljaCB3aWxsIHByaW50IGBUUlVFYCBpcyB0aGUgZmlsZSBjYW4gYmUgZm91bmQgaW4gdGhlIHdvcmtpbmcgZGlyZWN0b3J5LgoKYGBge3J9CmZpbGUuZXhpc3RzKCJnYXBtaW5kZXIuY3N2IikKYGBgCgpJZiB0aGUgZmlsZSB3ZSB3YW50IHRvIHJlYWQgaXMgbm90IGluIHRoZSBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5LCB3ZSB3aWxsIGhhdmUgdG8gd3JpdGUgdGhlIHBhdGggdG8gdGhlIGZpbGU7IGVpdGhlciAqcmVsYXRpdmUqIHRvIHRoZSBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5IChlLmcuIHRoZSBkaXJlY3RvcnkgInVwIiBmcm9tIHRoZSBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5LCBvciBpbiBhIHN1Yi1mb2xkZXIpLCBvciB0aGUgZnVsbCBwYXRoLiBJbiBhbiBpbnRlcmFjdGl2ZSBzZXNzaW9uLCB5b3UgY2FuIGRvIHVzZSBgZmlsZS5jaG9vc2VgIHRvIG9wZW4gYSBkaWFsb2d1ZSBib3guIFRoZSBwYXRoIHRvIHRoZSB0aGUgZmlsZSB3aWxsIHRoZW4gYmUgZGlzcGxheWVkIGluIFIuCgpgYGB7ciBldmFsPUZBTFNFfQpteWZpbGUgPC0gZmlsZS5jaG9vc2UoKQpteWZpbGUKYGBgCgoKYGBge3IgZXZhbD1GQUxTRX0KbXlmaWxlIDwtICIvaG9tZS9wYXJ0aWNpcGFudC9Db3Vyc2VfTWF0ZXJpYWxzL0RheTEvZ2FwbWluZGVyLmNzdiIKYGBgCgoKQXNzdW1pbmcgdGhlIGZpbGUgY2FuIGJlIGZvdW5kLCB3ZSBjYW4gdXNlIGByZWFkLmNzdmAgdG8gaW1wb3J0LiBPdGhlciBmdW5jdGlvbnMgY2FuIGJlIHVzZWQgdG8gcmVhZCB0YWItZGVsaW1pdGVkIGZpbGVzIChgcmVhZC5kZWxpbWApIG9yIGEgZ2VuZXJpYyBgcmVhZC50YWJsZWAgZnVuY3Rpb24uIEEgZGF0YSBmcmFtZSBvYmplY3QgaXMgY3JlYXRlZC4KCi0gYHJlYWQuZGVsaW1gIGhhcyBsb3RzIG9mIG9wdGlvbnMgdG8gY29udHJvbCBob3cgdG8gaW1wb3J0IHRoZSBkYXRhLCBpbmNsdWRpbmcgc2tpcHBpbmcgbGluZXMgaW4gdGhlIGZpbGVzLiBTZWUgYD9yZWFkLnRhYmxlYAoKLSBJZiB5b3UgZ2V0IGFuIGVycm9yIHNheWluZyAqRXJyb3IgaW4gZmlsZShmaWxlLCAicnQiKSA6IGNhbm5vdCBvcGVuIHRoZSBjb25uZWN0aW9uKiwgeW91IG1pZ2h0IG5lZWQgdG8gY2hhbmdlIHlvdXIgd29ya2luZyBkaXJlY3Rvcnkgb3IgbWFrZSBzdXJlIHRoZSBmaWxlIG5hbWUgaXMgdHlwZWQgY29ycmVjdGx5IChSIGlzICpjYXNlLXNlbnNpdGl2ZSopCgpgYGB7cn0KZ2FwbWluZGVyIDwtIHJlYWQuY3N2KCJnYXBtaW5kZXIuY3N2IikKZ2FwbWluZGVyCmBgYAoKLSBOLkIuIFRoZSBsYXRlc3QgdmVyc2lvbiBvZiBSU3R1ZGlvICg+IDEuMC40NCkgcHJvdmlkZXMgdGhlIG9wdGlvbiB0byBpbXBvcnQgZGF0YSBmcm9tIHRoZSBGaWxlIG1lbnUuIFRyeSAqKipGaWxlKioqIC0+ICoqKkltcG9ydCBEYXRhc2V0KioqIC0+ICoqKkZyb20gQ3N2KioqLgogICAgKyBub3QgcmVhbGx5IHJlY29tbWVuZGVkIGZvciBiZXN0IHByYWN0aWNlLCBidXQgaXQgc2hvdWxkIGhlbHAgZ2V0IHlvdSBzdGFydGVkCgpUaGUgZGF0YSBmcmFtZSBvYmplY3QgaW4gUiBhbGxvd3MgdXMgdG8gd29yayB3aXRoICJ0YWJ1bGFyIiBkYXRhLCBsaWtlIHdlIG1pZ2h0IGJlIHVzZWQgdG8gZGVhbGluZyB3aXRoIGluIEV4Y2VsLCB3aGVyZSBvdXIgZGF0YSBjYW4gYmUgdGhvdWdodCBvZiBoYXZpbmcgcm93cyBhbmQgY29sdW1ucy4gVGhlIHZhbHVlcyBpbiBlYWNoIGNvbHVtbiBoYXZlIHRvIGFsbCBiZSBvZiB0aGUgc2FtZSB0eXBlIChpLmUuIGFsbCBudW1iZXJzIG9yIGFsbCB0ZXh0KS4KCioqRGlzY2xhaW1lcioqIElmIHlvdSBhcmUgdHJ5aW5nIHRvIHJlYWQgeW91ciBvd24gZGF0YSwgYW5kIGVuY291bnRlciBhbiBlcnJvciBhdCB0aGlzIHN0YWdlLCB5b3UgbWF5IG5lZWQgdG8gY29uc2lkZXIgaWYgeW91ciBkYXRhIGFyZSBpbiB0aGUgKmNvcnJlY3QgZm9ybSBmb3IgYW5hbHlzaXMqLiBMaWtlIG1vc3QgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzLCBSIHdpbGwgc3RydWdnbGUgaWYgeW91ciBzcHJlYWRzaGVldCBoYXMgYmVlbiBoZWF2aWx5IGZvcm1hdHRlZCB0byBpbmNsdWRlIGNvbG91cnMsIGZvcm11bGFzIGFuZCBzcGVjaWFsIGZvcm1hdHRpbmcuIAoKVGhlc2UgcmVmZXJlbmNlcyB3aWxsIGd1aWRlIHlvdSB0aHJvdWdoIHNvbWUgb2YgdGhlIHBpdGZhbGxzIGFuZCBjb21tb24gbWlzdGFrZXMgdG8gYXZvaWQgd2hlbiBmb3JtYXR0aW5nIGRhdGEKCi0gW0Zvcm1hdHRpbmcgZGF0YSB0YWJsZXMgaW4gU3ByZWFkc2hlZXRzXShodHRwOi8vd3d3LmRhdGFjYXJwZW50cnkub3JnL3NwcmVhZHNoZWV0LWVjb2xvZ3ktbGVzc29uLzAxLWZvcm1hdC1kYXRhLmh0bWwpCi0gW0RhdGEgT3JnYW5pc2F0aW9uIHR1dG9yaWFsIGJ5IEthcmwgQnJvbWFuXShodHRwOi8va2Jyb21hbi5vcmcvZGF0YW9yZy8pCi0gW1RoZSBRdWFydHogZ3VpZGUgdG8gYmFkIGRhdGFdKGh0dHBzOi8vZ2l0aHViLmNvbS9RdWFydHovYmFkLWRhdGEtZ3VpZGUvYmxvYi9tYXN0ZXIvUkVBRE1FLm1kKQoKSW4gUlN0dWRpbyAsIHlvdSBjYW4gdmlldyB0aGUgY29udGVudHMgb2YgdGhlIGRhdGEgZnJhbWUgd2UgaGF2ZSBqdXN0IGNyZWF0ZWQuIFRoaXMgaXMgdXNlZnVsIGZvciBpbnRlcmFjdGl2ZSBleHBsb3JhdGlvbiBvZiB0aGUgZGF0YSwgYnV0IG5vdCBzbyB1c2VmdWwgZm9yIGF1dG9tYXRpb24gYW5kIHNjcmlwdGluZyBhbmQgYW5hbHlzZXMuCgpgYGB7ciBldmFsPUZBTFNFfQpWaWV3KGdhcG1pbmRlcikKYGBgCgpXZSBzaG91bGQgYWx3YXlzIGNoZWNrIHRoZSBkYXRhIGZyYW1lIHRoYXQgd2UgaGF2ZSBjcmVhdGVkLiBTb21ldGltZXMgUiB3aWxsIGhhcHBpbHkgcmVhZCBkYXRhIHVzaW5nIGFuIGluYXBwcm9wcmlhdGUgZnVuY3Rpb24gYW5kIGNyZWF0ZSBhbiBvYmplY3Qgd2l0aG91dCByYWlzaW5nIGFuIGVycm9yLiBIb3dldmVyLCB0aGUgZGF0YSBtaWdodCBiZSB1bnN1YWJsZS4gQ29uc2lkZXI6LQoKYGBge3J9CnRlc3QgPC0gcmVhZC5kZWxpbSgiZ2FwbWluZGVyLmNzdiIpCmRpbSh0ZXN0KQpgYGAKCgpXZSBjYW4gYWNjZXNzIHRoZSBjb2x1bW5zIG9mIGEgZGF0YSBmcmFtZSBieSBrbm93aW5nIHRoZSBjb2x1bW4gbmFtZS4gCioqKlRJUCoqKiBVc2UgYXV0by1jb21wbGV0ZSB3aXRoIHRoZSAqKipUQUIqKioga2V5IHRvIGdldCB0aGUgbmFtZSBvZiB0aGUgY29sdW1uIGNvcnJlY3QKCmBgYHtyIGV2YWw9RkFMU0V9CmdhcG1pbmRlciRjb3VudHJ5CmBgYAoKCkEgdmVjdG9yICgxLWRpbWVuc2lvbmFsKSBpcyByZXR1cm5lZCwgdGhlIGxlbmd0aCBvZiB3aGljaCBpcyB0aGUgc2FtZSBhcyB0aGUgbnVtYmVyIG9mIHJvd3MgaW4gdGhlIGRhdGEgZnJhbWUuIFRoZSB2ZWN0b3IgY291bGQgYmUgc3RvcmVkIGFzIGEgdmFyaWFibGUgYW5kIGl0c2VsZiBiZSBzdWJzZXQgb3IgdXNlZCBpbiBmdXJ0aGVyIGNhbGN1bGF0aW9ucwoKYGBge3J9CmNvdW50cmllcyA8LSBnYXBtaW5kZXIkY291bnRyeQoKYGBgCgoKVGhlIGBzdW1tYXJ5YCBmdW5jdGlvbiBpcyBhIHVzZWZ1bCB3YXkgb2Ygc3VtbWFyaXNpbmcgdGhlIGRhdGEgY29udGFpbmluZyBpbiBlYWNoIGNvbHVtbi4gSXQgd2lsbCBnaXZlIGluZm9ybWF0aW9uIGFib3V0IHRoZSAqdHlwZSogb2YgZGF0YSAocmVtZW1iZXIsIGRhdGEgZnJhbWVzIGNhbiBoYXZlIGEgbWl4dHVyZSBvZiBudW1lcmljIGFuZCBjaGFyYWN0ZXIgY29sdW1ucykgYW5kIGFsc28gYW4gYXBwcm9wcmlhdGUgc3VtbWFyeS4gRm9yIG51bWVyaWMgY29sdW1ucywgaXQgd2lsbCByZXBvcnQgc29tZSBzdGF0cyBhYm91dCB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBkYXRhLiBGb3IgY2F0ZWdvcmljYWwgZGF0YSwgaXQgd2lsbCByZXBvcnQgdGhlIGRpZmZlcmVudCAqbGV2ZWxzKi4KCmBgYHtyfQpzdW1tYXJ5KGdhcG1pbmRlcikKYGBgCgoKKioqKioqCioqKioqKgoqKioqKioKCiMjIyBFeGVyY2lzZQoKLSBDcmVhdGUgbmV3IHZhcmlhYmxlcyBmb3IgdGhlIGxpZmUgZXhwZWN0YW5jeSBhbmQgcG9wdWxhdGlvbiBjb2x1bW5zCiAgICArIHJvdW5kIHRoZSBsaWZlIGV4cGVjdGFuY3kgdG8gdGhlIG5lYXJlc3Qgd2hvbGUgbnVtYmVyCiAgICArIG1vZGlmeSB0aGUgcG9wdWxhdGlvbiB2YXJpYWJsZSBzbyB0aGF0IHRoZSBwb3B1bGF0aW9uIHNpemUgaXMgZ2l2ZW4gaW4gbWlsbGlvbnMgb2YgcGVvcGxlCiAgICArIHdoYXQgaXMgdGhlIG1heGltdW0gbGlmZSBleHBlY3RhbmN5IG9ic2VydmVkPwogICAgKyB3aGF0IGlzIHRoZSBzbWFsbGVzdCBwb3B1bGF0aW9uIG9ic2VydmVkPyAgCiAgICArIEhJTlQ6LSBgbWluYCwgYG1heGAsIGByb3VuZGAuLi4uLgogICAgCioqKioqKgoqKioqKioKKioqKioqCgpgYGB7cn0KCiMjIyBZb3VyIGFuc3dlciBoZXJlICMjIwoKYGBgCgoKIyMgU3Vic2V0dGluZyByb3dzIGFuZCBjb2x1bW5zCgpBIGRhdGEgZnJhbWUgY2FuIGJlIHN1YnNldCB1c2luZyBzcXVhcmUgYnJhY2tlc2BbXWAgcGxhY2VkIGFmdGVyIHRoZSBuYW1lIG9mIHRoZSBkYXRhIGZyYW1lLiBBcyBhIGRhdGEgZnJhbWUgaXMgYSB0d28tZGltZW5zaW9uYWwgb2JqZWN0LCB5b3UgbmVlZCBib3RoIGEgKnJvdyogYW5kICpjb2x1bW4qIGluZGV4LgoKYGBge3IgZXZhbD1GQUxTRX0KZ2FwbWluZGVyWzEsMl0KZ2FwbWluZGVyWzIsMV0KZ2FwbWluZGVyW2MoMSwyLDMpLDFdCmdhcG1pbmRlcltjKDEsMiwzKSxjKDEsMiwzKV0KCgpgYGAKCioqKk5vdGUgdGhhdCB0aGUgZGF0YSBmcmFtZSBpcyBub3QgYWx0ZXJlZCoqKiB3ZSBhcmUganVzdCBzZWVpbmcgd2hhdCBhIHN1YnNldCBvZiB0aGUgZGF0YSBsb29rcyBsaWtlIGFuZCBub3QgY2hhbmdpbmcgdGhlIHVuZGVybHlpbmcgZGF0YS4gSWYgd2Ugd2FudGVkIHRvIGRvIHRoaXMsIHdlIHdvdWxkIG5lZWQgdG8gY3JlYXRlIGEgbmV3IHZhcmlhbGUuCgpgYGB7ciBldmFsPUZBTFNFfQpnYXBtaW5kZXIKYGBgCgpTaG91bGQgd2Ugd2lzaCB0byBzZWUgYWxsIHJvd3MsIG9yIGFsbCBjb2x1bW5zLCB3ZSBjYW4gbmVnbGVjdCBlaXRoZXIgdGhlIHJvdyBvciBjb2x1bW4gaW5kZXgKCmBgYHtyIGV2YWw9RkFMU0V9CmdhcG1pbmRlclsxLF0KZ2FwbWluZGVyWywxXQoKCmBgYAoKVGhlIGluZGljZXMgY2FuIGJlIG1vcmUgY29tcGxpY2F0ZWQgUiBleHByZXNzaW9ucyBjb250YWluaW5nIG11bHRpcGxlIHZhbHVlcwoKYGBge3IgZXZhbD1GQUxTRX0KZ2FwbWluZGVyWzE6MywxOjJdCmdhcG1pbmRlcltzZXEoMSwxNzA0LGxlbmd0aC5vdXQgPSAxMCksMTo0XQoKCmBgYAoKCkEgY29tbW9uIHNob3J0Y3V0IGlzIGBoZWFkYCB3aGljaCBwcmludHMgdGhlIGZpcnN0IHNpeCByb3dzIG9mIGEgZGF0YSBmcmFtZS4KICAgCmBgYHtyfQpoZWFkKGdhcG1pbmRlcikKYGBgCgpXaGVuIHN1YnNldHRpbmcgZW50aXJlIHJvd3MgKioqeW91IG5lZWQgdG8gcmVtZW1iZXIgdGhlICwgYWZ0ZXIgdGhlIHJvdyBpbmRpY2VzKioqLiBJZiB5b3UgZmFpbCB0byBkbyBzbywgUiBtYXkgc3RpbGwgcmV0dXJuIGEgcmVzdWx0LiBIb3dldmVyLCBpdCBwcm9iYWJseSB3b24ndCBiZSB3aGF0IHlvdSBleHBlY3RlZC4gTG9vayB3aGF0IGhhcHBlbnMgaWYgeW91IHdhbnRlZCB0byB0aGUgZmlyc3QgdGhyZWUgcm93cyBidXQgdHlwZWQgdGhlIGZvbGxvd2luZyBjb21tYW5kCgpgYGB7ciBldmFsPUZBTFNFfQpnYXBtaW5kZXJbMTozXQpgYGAKCgpXZSBjYW4gYWxzbyBjcmVhdGUgbmV3IGNvbHVtbnMsIG9yIGFsdGVyIGV4aXN0aW5nIG9uZXMKCi0gVXNpbmcgYW4gYXNzaWdubWVudCBvcGVyYXRvciBgPC1gIHdlIGNhbiBhc3NpZ24gdGhlIHZhbHVlIG9mIGEgcGFydGljdWxhciBjb2x1bW4gdG8gYmUgdmVjdG9yCiAgICArIGlmIHRoZSBjb2x1bW4gZG9lc24ndCBleGlzdCwgaXQgd2lsbCBiZSBjcmVhdGVkCiAgICArIHRoZSB2ZWN0b3Igc2hvdWxkIGJlIHRoZSBzYW1lIGxlbmd0aCBhcyB0aGUgbnVtYmVyIG9mIHJvd3MgaW4gdGhlIGRhdGEgZnJhbWUKCmBgYHtyfQpnYXBtaW5kZXIkcG9wSW5NaWxsaW9ucyA8LSBnYXBtaW5kZXIkcG9wIC8gMTAwMDAwMApnYXBtaW5kZXIkbGlmZUV4cCA8LSByb3VuZChnYXBtaW5kZXIkbGlmZUV4cCwxKQpnYXBtaW5kZXIKCmBgYAoKIyMgRmlsdGVyaW5nIHJvd3MKClJhdGhlciB0aGFuIHNlbGVjdGluZyByb3dzIGJhc2VkIG9uIHRoZWlyICpudW1lcmljKiBpbmRleCAoYXMgaW4gdGhlIHByZXZpb3VzIGV4YW1wbGUpIHdlIGNhbiB1c2Ugd2hhdCB3ZSBjYWxsIGEgKmxvZ2ljYWwgdGVzdCouIFRoaXMgaXMgYSB0ZXN0IHRoYXQgZ2l2ZXMgZWl0aGVyIGEgYFRSVUVgIG9yIGBGQUxTRWAgcmVzdWx0LiBXaGVuIGFwcGxpZWQgdG8gc3Vic2V0dGluZywgb25seSByb3dzIHdpdGggYSBgVFJVRWAgcmVzdWx0IGdldCByZXR1cm5lZC4KCmBgYHtyfQpteXZlYyA8LSBjKCJBIiwiQiIsIkMiLCJEIikKeCA8LSBjKFRSVUUsIFRSVUUsRkFMU0UsVFJVRSkKbXl2ZWNbeF0KeCA8LSBjKEZBTFNFLCBGQUxTRSxGQUxTRSxUUlVFKQoKYGBgCgoKRm9yIGV4YW1wbGUgd2UgY291bGQgY29tcGFyZSB0aGUgYGxpZmVFeHBgIHZhcmlhYmxlIHRvIDQwLiBUaGUgcmVzdWx0IGlzIGEgKnZlY3Rvciogb2YgYFRSVUVgIG9yIGBGQUxTRWA7IG9uZSBmb3IgZWFjaCByb3cgaW4gdGhlIGRhdGEgZnJhbWUKCmBgYHtyIGV2YWw9RkFMU0V9CmdhcG1pbmRlciRsaWZlRXhwIDwgNDAKCmBgYAoKVGhpcyBSIGNvZGUgY2FuIGJlIHB1dCBpbnNpZGUgdGhlIHNxdWFyZSBicmFja2V0cyB0byBzZWxlY3Qgcm93cyBvZiBpbnRlcmVzdCAodGhvc2Ugb2JzZXJ2YXRpb25zIHdoZXJlIHRoZSBsaWZlIGV4cGVjdGFuY3kgdmFyaWFibGUgaXMgbGVzcyB0aGFuIDQwKS4gCgpgYGB7cn0KZ2FwbWluZGVyW2dhcG1pbmRlciRsaWZlRXhwIDwgNDAsIF0KYGBgCgoKVGhlIGAsYCBpcyBpbXBvcnRhbnQgYXMgdGhpcyB0ZWxscyBSIHRvIGRpc3BsYXkgYWxsIGNvbHVtbnMuIElmIHdlIHdhbnRlZCBhIHN1YnNldCBvZiB0aGUgY29sdW1ucyB3ZSB3b3VsZCBwdXQgdGhlaXIgaW5kaWNlcyBhZnRlciB0aGUgYCxgCgpgYGB7cn0KZ2FwbWluZGVyW2dhcG1pbmRlciRsaWZlRXhwIDwgNDAsIDE6NF0KYGBgCgoKVXNpbmcgdGhlIGNvbHVtbiBuYW1lcyBpcyBhbHNvIHZhbGlkCgpgYGB7ciB9CmdhcG1pbmRlcltnYXBtaW5kZXIkbGlmZUV4cCA8IDQwLCBjKCJjb3VudHJ5IiwgImNvbnRpbmVudCIsInllYXIiKV0KYGBgCgoKVGVzdGluZyBmb3IgZXF1YWxpdHkgY2FuIGJlIGRvbmUgdXNpbmcgYD09YC4gVGhpcyB3aWxsIG9ubHkgZ2l2ZSBgVFJVRWAgZm9yIGVudHJpZXMgdGhhdCBhcmUgKmV4YWN0bHkqIHRoZSBzYW1lIGFzIHRoZSB0ZXN0IHN0cmluZy4gCgpgYGB7cn0KZ2FwbWluZGVyW2dhcG1pbmRlciRjb3VudHJ5ID09ICJaYW1iaWEiLF0KCmBgYAoKTi5CLiBGb3IgcGFydGlhbCBtYXRjaGVzLCB0aGUgYGdyZXBgIGZ1bmN0aW9uIGFuZCAvIG9yICpyZWd1bGFyIGV4cHJlc3Npb25zKiAoaWYgeW91IGtub3cgdGhlbSkgY2FuIGJlIHVzZWQuCgpgYGB7cn0gCmdhcG1pbmRlcltncmVwKCJsYW5kIiwgZ2FwbWluZGVyJGNvdW50cnkpLF0KCmBgYAoKCgoKVGhlcmUgYXJlIGEgY291cGxlIG9mIHdheXMgb2YgdGVzdGluZyBmb3IgbW9yZSB0aGFuIG9uZSB0ZXh0IHZhbHVlLiBUaGUgZmlyc3QgdXNlcyBhbiAqb3IqIGB8YCBzdGF0ZW1lbnQuIGkuZS4gdGVzdGluZyBpZiB0aGUgdmFsdWUgb2YgYGNvdW50cnlgIGlzIGBaYW1iaWFgICpvciogdGhlIHZhbHVlIGlzIGBaaW1iYWJ3ZWAuCgpUaGUgYCVpbiVgIGZ1bmN0aW9uIGlzIGEgY29udmVuaWVudCBmdW5jdGlvbiBmb3IgdGVzdGluZyB3aGljaCBpdGVtcyBpbiBhIHZlY3RvciBjb3JyZXNwb25kIHRvIGEgZGVmaW5lZCBzZXQgb2YgdmFsdWVzLgoKYGBge3J9CmdhcG1pbmRlcltnYXBtaW5kZXIkY291bnRyeSA9PSAiWmFtYmlhIiB8IGdhcG1pbmRlciRjb3VudHJ5ID09ICJaaW1iYWJ3ZSIsXQpnYXBtaW5kZXJbZ2FwbWluZGVyJGNvdW50cnkgJWluJSBjKCJaYW1iaWEiLCJaaW1iYWJ3ZSIpLF0KYGBgCgoKCgpTaW1pbGFyIHRvICpvciosIHdlIGNhbiByZXF1aXJlIHRoYXQgYm90aCB0ZXN0cyBhcmUgYFRSVUVgIGJ5IHVzaW5nIGFuICphbmQqIGAmYCBvcGVyYXRpb24uIGUuZy4gd2hpY2ggeWVhcnMgaW4gWmFtYmlhIGhhZCBhIGxpZmUgZXhwZWN0YW5jeSBsZXNzIHRoYW4gNDAKCmBgYHtyfQpnYXBtaW5kZXJbZ2FwbWluZGVyJGNvdW50cnkgPT0gIlphbWJpYSIgJiBnYXBtaW5kZXIkbGlmZUV4cCA8IDQwLF0KYGBgCgoqKioqKioKKioqKioqCioqKioqKgoKIyMjIEV4ZXJjaXNlCgotIFByaW50IHRoZSBjb250ZW50cyBvZiB0aGUgcm93IGluIHRoZSBkYXRhIGZyYW1lIHRoYXQgY29udGFpbnMgdGhlIGVudHJ5IHdpdGggdGhlIGhpZ2hlc3QgcG9wdWxhdGlvbj8KLSBQcmludCB0aGUgY29udGVudHMgb2YgdGhlIHJvdyBpbiB0aGUgZGF0YSBmcmFtZSB0aGF0IGNvbnRhaW5zIHRoZSBlbnRyeSB3aXRoIHRoZSBsb3dlc3QgbGlmZSBleHBlY3RhbmN5PwotIEEgZGF0YSBmcmFtZSBvZiBjb3VudHJpZXMgd2l0aCBhIHBvcHVsYXRpb24gbGVzcyB0aGFuIGEgbWlsbGlvbiBpbiB0aGUgeWVhciAyMDAyLCB0aGF0IGFyZSBub3QgaW4gQWZyaWNhPwogICAgKyB5b3UgbWF5IG5lZWQgdG8gZG8gc29tZSBnb29nbGluZyB0byBmaW5kIG91dCBob3cgdG8gZG8gIm5vdCBlcXVhbCIgaW4gUgooT3B0aW9uYWwpCi0gV2hhdCBjb3VudHJ5IGhhcyB0aGUgbG9uZ2VzdCBuYW1lPyBQcmludCBhbGwgdGhlIHJvd3MgY29udGFpbmluZyBkYXRhIGZvciB0aGlzIGNvdW50cnkKICAgKyBISU5UOiBpbnZlc3RpZ2F0ZSB0aGUgYG5jaGFyYCBmdW5jdGlvbgoKKioqKioqCioqKioqKgoqKioqKioKCmBgYHtyfQoKIyMjIFlvdXIgYW5zd2VyIGhlcmUgIyMjCgpgYGAKCgoKIyMgT3JkZXJpbmcgYW5kIHNvcnRpbmcKCkEgdmVjdG9yIGNhbiBiZSByZXR1cm5lZCBpbiBzb3J0ZWQgZm9ybSB1c2luZyB0aGUgYHNvcnRgIGZ1bmN0aW9uLgoKYGBge3IgZXZhbD1GQUxTRX0Kc29ydChjb3VudHJpZXMpCnNvcnQoY291bnRyaWVzLGRlY3JlYXNpbmcgPSBUUlVFKQpgYGAKCkhvd2V2ZXIsIGlmIHdlIHdhbnQgdG8gc29ydCBhbiBlbnRpcmUgZGF0YSBmcmFtZSBhIGRpZmZlcmVudCBhcHByb2FjaCBpcyBuZWVkZWQuIFRoZSB0cmljayBpcyB0byB1c2UgYG9yZGVyYC4gUmF0aGVyIHRoYW4gZ2l2aW5nIGEgc29ydGVkIHNldCBvZiAqdmFsdWVzKiwgaXQgd2lsbCBnaXZlIHNvcnRlZCAqaW5kaWNlcyouIFRoZXNlIGluZGljZXMgY2FuIHRoZW4gYmUgdXNlZCBmb3IgYSBzdWJzZXQgb3BlcmF0aW9uLgoKYGBge3J9CmxlYXN0UG9wIDwtIGdhcG1pbmRlcltvcmRlcihnYXBtaW5kZXIkcG9wKSxdCmhlYWQobGVhc3RQb3ApCmBgYAoKV2UgY2FuIGV2ZW4gb3JkZXIgYnkgbW9yZSB0aGFuIG9uZSBjb25kaXRpb24KCmBgYHtyIGV2YWw9RkFMU0V9CmdhcG1pbmRlcltvcmRlcihnYXBtaW5kZXIkeWVhciwgZ2FwbWluZGVyJGNvdW50cnkpLF0KYGBgCgoKQSBmaW5hbCBwb2ludCBvbiBkYXRhIGZyYW1lcyBpcyB0aGF0IHdlIGNhbiBleHBvcnQgdGhlbSBvdXQgb2YgUiBvbmNlIHdlIGhhdmUgZG9uZSBvdXIgZGF0YSBwcm9jZXNzaW5nLiAKCmBgYHtyfQpieVdlYWx0aCA8LSBnYXBtaW5kZXJbb3JkZXIoZ2FwbWluZGVyJGdkcFBlcmNhcCxkZWNyZWFzaW5nID0gVFJVRSksXQpoZWFkKGJ5V2VhbHRoKQp3cml0ZS5jc3YoYnlXZWFsdGgsIGZpbGU9ImRhdGFPcmRlcmVkQnlXZWFsdGguY3N2IikKYGBgCgoKCgojIFBsb3R0aW5nIGFuZCBzdGF0cyAoaW4gYnJpZWYhKQoKQWxsIHlvdXIgZmF2b3VyaXRlIHR5cGVzIG9mIHBsb3QgY2FuIGJlIGNyZWF0ZWQgaW4gUgoKIVtdKC4uL2ltYWdlcy9leGFtcGxlUGxvdHMucG5nKQoKCgotIFNpbXBsZSBwbG90cyBhcmUgc3VwcG9ydGVkIGluIHRoZSAqYmFzZSogZGlzdHJpYnV0aW9uIG9mIFIgKHdoYXQgeW91IGdldCBhdXRvbWF0aWNhbGx5IHdoZW4geW91IGRvd25sb2FkIFIpLiAKICAgICsgYGJveHBsb3RgLCBgaGlzdGAsIGBiYXJwbG90YCwuLi4gYWxsIG9mIHdoaWNoIGFyZSBleHRlbnNpb25zIG9mIHRoZSBiYXNpYyBgcGxvdGAgZnVuY3Rpb24KLSBNYW55IGRpZmZlcmVudCBjdXN0b21pc2F0aW9ucyBhcmUgcG9zc2libGUKICAgICsgY29sb3VyLCBvdmVybGF5IHBvaW50cyAvIHRleHQsIGxlZ2VuZHMsIG11bHRpLXBhbmVsIGZpZ3VyZXMKLSAqKipZb3UgbmVlZCB0byB0aGluayBhYm91dCBob3cgYmVzdCB0byB2aXN1YWxpc2UgeW91ciBkYXRhKioqIAogICAgKyBodHRwOi8vd3d3LmJpb2luZm9ybWF0aWNzLmJhYnJhaGFtLmFjLnVrL3RyYWluaW5nLmh0bWwjZmlndXJlZGVzaWduCi0gUiBjYW5ub3QgcHJldmVudCB5b3UgZnJvbSBjcmVhdGluZyBhIHBsb3R0aW5nIGRpc2FzdGVyOiAKICAgICsgaHR0cDovL3d3dy5idXNpbmVzc2luc2lkZXIuY29tL3RoZS0yNy13b3JzdC1jaGFydHMtb2YtYWxsLXRpbWUtMjAxMy02P29wPTEmSVI9VAotIFJlZmVyZW5jZXMuLgogICAgKyBbSW50cm9kdWN0b3J5IFIgY291cnNlXShodHRwOi8vY2FtYmlvdHJhaW5pbmcuZ2l0aHViLmlvL3ItaW50cm8vKQogICAgKyBbUXVpY2stUl0oaHR0cDovL3d3dy5zdGF0bWV0aG9kcy5uZXQvZ3JhcGhzL2luZGV4Lmh0bWwpCiAgICArIFtNb3JlIGFkdmFuY2VkIHBsb3R0aW5nOyBnZ3Bsb3QyXShodHRwOi8vYmlvaW5mb3JtYXRpY3MtY29yZS1zaGFyZWQtdHJhaW5pbmcuZ2l0aHViLmlvL3ItaW50ZXJtZWRpYXRlLykKICAgIApQbG90cyBjYW4gYmUgY29uc3RydWN0ZWQgZnJvbSB2ZWN0b3JzIG9mIG51bWVyaWMgZGF0YSwgc3VjaCBhcyB0aGUgZGF0YSB3ZSBnZXQgZnJvbSBhIHBhcnRpY3VsYXIgY29sdW1uIGluIGEgZGF0YSBmcmFtZS4KCiMjIEJhc2ljIFBsb3QgdHlwZXMKCi0gQSBoaXN0b2dyYW0gaXMgYSBjb21tb24gY2hvaWNlIGlmIHdlIHdhbnQgdG8gdmlzdWFsaXNlIHRoZSBkaXN0cmlidXRpb24gb2YgYSBudW1lcmljIHZhcmlhYmxlcwoKYGBge3J9Cmhpc3QoZ2FwbWluZGVyJGxpZmVFeHApCmBgYAoKU2NhdHRlciBwbG90cyBvZiB0d28gdmFyaWFibGVzIHJlcXVpcmUgdHdvIGFyZ3VtZW50czsgb25lIGZvciB0aGUgYHhgIGFuZCBvbmUgZm9yIHRoZSBgeWAgYXhpcy4KCmBgYHtyfQpwbG90KGdhcG1pbmRlciRwb3AsZ2FwbWluZGVyJGxpZmVFeHApCmBgYAoKCkJhcnBsb3RzIGFyZSBjb21tb25seS11c2VkIGZvciBjb3VudHMgb2YgY2F0ZWdvcmljYWwgZGF0YQoKLSBMZXQncyBzYXkgd2Ugd2FudCB0byBrbm93IGhvdyBtYW55IGNvdW50cmllcyB0aGVyZSBhcmUgCi0gYHRhYmxlYCB3aWxsIGdpdmUgdXMgdGhlIHRvdGFsIG51bWJlciBvZiB0aW1lcyBlYWNoIGNvdW50cnkgaXMgb2JzZXJ2ZWQKICAgICsgYnV0IHRoZSBzZXQgb2YgY291bnRyaWVzIGlzIHJlcGVhdGVkIGZvciBlYWNoIHllYXIKICAgIApgYGB7cn0KdGFibGUoZ2FwbWluZGVyJGNvbnRpbmVudCkKdGFibGUoZ2FwbWluZGVyJGNvbnRpbmVudFtnYXBtaW5kZXIkeWVhcj09MjAwMl0pCmJhcnBsb3QodGFibGUoZ2FwbWluZGVyJGNvbnRpbmVudFtnYXBtaW5kZXIkeWVhcj09MjAwMl0pKQpgYGAKCkJveHBsb3RzIGFyZSBnb29kIGZvciB2aXN1YWxpc2luZyBhbmQgY29tcGFyaW5nIGRpc3RyaWJ1dGlvbnMuIEhlcmUgdGhlIGB+YCBzeW1ib2wgc2V0cyB1cCBhIGZvcm11bGEsIHRoZSBlZmZlY3Qgb2Ygd2hpY2ggaXMgdG8gcHV0IHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBvbiB0aGUgYHhgIGF4aXMgYW5kIGNvbnRpbnVvdXMgdmFyaWFibGUgb24gdGhlIGB5YCBheGlzLgoKYGBge3J9CmJveHBsb3QoZ2FwbWluZGVyJGdkcFBlcmNhcCB+IGdhcG1pbmRlciRjb250aW5lbnQpCmBgYAoKKkxvdHMqIG9mIGN1c3RvbWlzYXRpb25zIGFyZSBwb3NzaWJsZSB0byBlbmhhbmNlIHRoZSBhcHBhZXJhbmNlIG9mIG91ciBwbG90cy4gTm90IGZvciB0aGUgZmFpbnQtaGVhcnRlZCwgdGhlIGhlbHAgcGFnZXMgYD9wbG90YCBhbmQgYD9wYXJgIGdpdmUgdGhlIGZ1bGwgZGV0YWlscy4gSW4gc2hvcnQsCgotIEF4aXMgbGFiZWxzLCBhbmQgdGl0bGVzIGNhbiBiZSBzcGVjaWZpZWQgYXMgY2hhcmFjdGVyIHN0cmluZ3MuIAoKLSBSIHJlY29nbmlzZXMgbWFueSBwcmVzZXQgbmFtZXMgYXMgY29sb3Vycy4gVG8gZ2V0IGEgZnVsbCBsaXN0IHVzZSBgY29sb3VycygpYCwgb3IgY2hlY2sgdGhpcyBbb25saW5lIHJlZmVyZW5jZV0oaHR0cDovL3d3dy5zdGF0LmNvbHVtYmlhLmVkdS9+dHpoZW5nL2ZpbGVzL1Jjb2xvci5wZGYpLgogICAgKyBjYW4gYWxzbyB1c2UgYCpSKmVkLCAqRypyZWVuLCAqQipsdWUgdmFsdWVzOyB3aGljaCB5b3UgbWlnaHQgZ2V0IGZyb20gYSBwYWludCBwcm9ncmFtCi0gUGxvdHRpbmcgY2hhcmFjdGVycyBjYW4gYmUgc3BlY2lmaWVkIHVzaW5nIGEgcHJlLWRlZmluZWQgbnVtYmVyOi0KCiFbXSguLi9pbWFnZXMvcGxvdC1jaGFyLnBuZykKCgoKUHV0dGluZyBpdCBhbGwgdG9nZXRoZXIuCgpgYGB7cn0KcGxvdChnYXBtaW5kZXIkcG9wLGdhcG1pbmRlciRsaWZlRXhwLHBjaD0xNiwKICAgICBjb2w9InJlZCIseWxhYj0iTGlmZSBFeHBlY3RhbmN5IiwKICAgICB4bGFiPSJQb3B1bGF0aW9uIixtYWluPSJMaWZlIEV4cGVjdGFuY3kgdHJlbmQgd2l0aCBwb3B1bGF0aW9uIikKYGBgCgoKVGhlIHNhbWUgY3VzdG9taXNhdGlvbnMgY2FuIGJlIHVzZWQgZm9yIHZhcmlvdXMgcGxvdHM6LQoKYGBge3J9CmJveHBsb3QoZ2FwbWluZGVyJGdkcFBlcmNhcCB+IGdhcG1pbmRlciRjb250aW5lbnQsY29sPWMoInJlZCIsIm9yYW5nZSIsImdyZWVuIiwiYmx1ZSIsInB1cnBsZSIpLAogICAgICAgIG1haW49IkdEUCBwZXItY29udGluZW50IiwKICAgICAgICB4bGFiPSJDb250aW5lbnQiLAogICAgICAgIHlsYWI9IkdEUCIpCmBgYAoKKioqKioqCioqKioqKgoqKioqKioKCiMjIEV4ZXJjaXNlCgotIEZpcnN0LCBjcmVhdGUgYSBzdWJzZXQgb2Ygb2JzZXJ2YXRpb25zIGZyb20gdGhlIHllYXIgKioxOTUyKioKLSBEbyBjb3VudHJpZXMgd2l0aCBhIGhpZ2hlciBHRFAgdGVuZCB0byBoYXZlIGEgaGlnaGVyIGxpZmUgZXhwZWN0YW5jeT8KICAgICsgdXNlIGEgc2NhdHRlciBwbG90IHRvIGZpbmQgb3V0Ci0gV2FzIHRoZXJlIGFuIG92ZXJhbGwgZGlmZmVyZW5jZSBpbiBsaWZlIGV4cGVjdGFuY3kgYmV0d2VlbiBjb250aW5lbnRzIGluIHRoaXMgeWVhcj8KICAgICsgdXNlIGEgYm94cGxvdCB0byBmaW5kIG91dAoKKioqKioqCioqKioqKgoqKioqKioKCmBgYHtyfQojIyBZb3VyIGFuc3dlciBoZXJlIyMKCgpgYGAKCgpQbG90cyBjYW4gYmUgZXhwb3J0ZWQgYnkgdGhlICoqKlBsb3RzKioqIHRhYiBpbiBSU3R1ZGlvLCB3aGljaCBpcyB1c2VmdWwgaW4gYW4gaW50ZXJhY3RpdmUgc2V0dGluZy4gSG93ZXZlciwgb25lIGNhbiBhbHNvIHNhdmUgcGxvdHMgdG8gYSBmaWxlIGNhbGxpbmcgdGhlIGBwZGZgIG9yIGBwbmdgIGZ1bmN0aW9ucyBiZWZvcmUgZXhlY3V0aW5nIHRoZSBjb2RlIHRvIGNyZWF0ZSB0aGUgcGxvdC4gCiAKCiAKYGBge3J9CnBkZigibXlMaXR0bGVQbG90LnBkZiIpCmJveHBsb3QoZ2FwbWluZGVyJGdkcFBlcmNhcCB+IGdhcG1pbmRlciRjb250aW5lbnQsY29sPWMoInJlZCIsIm9yYW5nZSIsImdyZWVuIiwiYmx1ZSIsInB1cnBsZSIpLAogICAgICAgIG1haW49IkdEUCBwZXItY29udGluZW50IiwKICAgICAgICB4bGFiPSJDb250aW5lbnQiLAogICAgICAgIHlsYWI9IkdEUCIpCmRldi5vZmYoKQpgYGAKCkFueSBwbG90cyBjcmVhdGVkIGluLWJldHdlZW4gdGhlIGBwZGYoLi4pYCBhbmQgYGRldi5vZmYoKWAgbGluZXMgd2lsbCBnZXQgc2F2ZWQgdG8gdGhlIG5hbWVkIGZpbGUuIFRoZSBgZGV2Lm9mZigpYCBsaW5lIGlzIHZlcnkgaW1wb3J0YW50OyB3aXRob3V0IGl0IHlvdSB3aWxsIG5vdCBiZSBhYmxlIHRvIHZpZXcgdGhlIHBsb3QgeW91IGhhdmUgY3JlYXRlZC4gYHBkZmAgZmlsZXMgYXJlIHVzZWZ1bCBiZWNhdXNlIHlvdSBjYW4gY3JlYXRlIGRvY3VtZW50cyB3aXRoIG11bHRpcGxlIHBhZ2VzLiBNb3Jlb3ZlciwgdGhleSBjYW4gYmUgaW1wb3J0ZWQgaW50byB0b29scyBzdWNoIGFzIEFkb2JlIElsbHVzdHJhdG9yIHRvIGJlIGluY29ycG9yYXRlZCB3aXRoIG90aGVyIGdyYXBoaWNzLiAKCi0gT2Z0ZW4gaXQgaXMgcXVpY2tlciB0byBzYXZlIGEgcGxvdCBhcyBhIHBkZiBhbmQgdGlua2VyIGluIElsbHVzdHJhdG9yIHJhdGhlciB0aGFuIGdldHRpbmcgUiB0byBkbyBleGFjdGx5IHdoYXQgeW91IHdhbnQKCmBgYHtyfQpwZGYoImdhcG1pbmRlci1wbG90cy5wZGYiKQpwbG90KGdhcG1pbmRlciRwb3AsZ2FwbWluZGVyJGxpZmVFeHAscGNoPTE2LAogICAgIGNvbD0icmVkIix5bGFiPSJMaWZlIEV4cGVjdGFuY3kiLAogICAgIHhsYWI9IlBvcHVsYXRpb24iLG1haW49IkxpZmUgRXhwZWN0YW5jeSB0cmVuZCB3aXRoIHBvcHVsYXRpb24iKQpib3hwbG90KGdhcG1pbmRlciRnZHBQZXJjYXAgfiBnYXBtaW5kZXIkY29udGluZW50LGNvbD1jKCJyZWQiLCJvcmFuZ2UiLCJncmVlbiIsImJsdWUiLCJwdXJwbGUiKSwKICAgICAgICBtYWluPSJHRFAgcGVyLWNvbnRpbmVudCIsCiAgICAgICAgeGxhYj0iQ29udGluZW50IiwKICAgICAgICB5bGFiPSJHRFAiKQpkZXYub2ZmKCkKYGBgCgoKIyMgVGhlIGNhbnZhcyBtb2RlbAoKSXQgaXMgaW1wb3J0YW50IHRvIHJlYWxpc2UgdGhhdCBiYXNlIGdyYXBoaWNzIGluIFIgdXNlcyBhICoiY2FudmFzIG1vZGVsIiogdG8gY3JlYXRlIGdyYXBoaWNzLiBXZSBjYW4gb25seSBvdmVybGF5IGV4dHJhIGluZm9ybWF0aW9uIG9uLXRvcCBvZiBhbiBleGlzaW5nIHBsb3QgYW5kIGNhbm5vdCAidW5kbyIgd2hhdCBpcyBhbHJlYWR5IGRyYXduLgoKTGV0J3Mgc3VwcG9zZSB3ZSB3YW50IHRvIGNvbXBhcmUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIEdEUCBhbmQgTGlmZSBFeHBlY3RhbmN5IGluIDE5NTIgYW5kIDIwMDIKCi0gQSB2YXJpZXR5IG9mIGZ1bmN0aW9ucyBjYW4gYmUgdXNlZCB0byBhZGQgZXh0cmEgZGF0YSAvIGFubm90YXRpb25zIHRvIGEgcGxvdC4gZS5nLgogICAgLSBgcG9pbnRzYAogICAgLSBgbGluZXNgCiAgICAtIGB0ZXh0YAogICAgLSBgbGVnZW5kYAoKYGBge3J9Cm9sZERhdGEgPC0gZ2FwbWluZGVyW2dhcG1pbmRlciR5ZWFyID09IDE5NTIsXQpvbGREYXRhCmBgYAoKYGBge3J9Cm5ld0RhdGEgPC0gZ2FwbWluZGVyW2dhcG1pbmRlciR5ZWFyID09IDIwMDIsXQpuZXdEYXRhCm5ld0RhdGEKYGBgCgpXZSBjYW4gc3RhcnQgYnkgcGxvdHRpbmcgdGhlIGxpZmUgZXhwZWN0YW5jeSBvZiB0aGUgMTk1MiBvYnNlcnZhdGlvbnMgYXMgcmVkIGRvdHMuCgpgYGB7cn0KcGxvdChvbGREYXRhJGdkcFBlcmNhcCwgb2xkRGF0YSRsaWZlRXhwLGNvbD0icmVkIiwKICAgICBwY2g9MTYsCiAgICAgeGxhYj0iR0RQIiwKICAgICB5bGFiPSJMaWZlIEV4cGVjdGFuY3kiKQpgYGAKClRoZSBgcG9pbnRzYCBmdW5jdGlvbiBjYW4gYmUgdXNlZCB0byBhZGR0IGV4dHJhIHBvaW50cyBjb3JyZXNwb25kaW5nIHRvIDIwMDIgY291bnRyaWVzIG9uIHRoZSBleGlzdGluZyBwbG90LiAKCi0gU29tZXRoaW5nIGRvZXNuJ3QgbG9vayByaWdodCB0aG91Z2guLi4KCmBgYHtyfQpwbG90KG9sZERhdGEkZ2RwUGVyY2FwLCBvbGREYXRhJGxpZmVFeHAsY29sPSJyZWQiLAogICAgIHBjaD0xNiwKICAgICB4bGFiPSJHRFAiLAogICAgIHlsYWI9IkxpZmUgRXhwZWN0YW5jeSIpCgpwb2ludHMobmV3RGF0YSRnZHBQZXJjYXAsIG5ld0RhdGEkbGlmZUV4cCxjb2w9ImJsdWUiLAogICAgIHBjaD0xNiwKICAgICB4bGFiPSJHRFAiLAogICAgIHlsYWI9IkxpZmUgRXhwZWN0YW5jeSIpCmBgYAoKClRoZSBwcm9ibGVtIGhlcmUgaXMgdGhhdCB0aGUgaW5pdGlhbCBsaW1pdHMgb2YgdGhlIHkgYXhpcyB3ZXJlIGRlZmluZWQgdXNpbmcgdGhlIGxpZmUgZXhwZWN0YW5jeSByYW5nZSBvZiB0aGUgMTk1MiBkYXRhLiBXZSBjYW4gb25seSBhZGQgcG9pbnRzIHRvIHRoZSBleGlzdGluZyBwbG90dGluZyB3aW5kb3csIHNvIGFueWNvdW50cmllcyB3aXRoIGxpZmUgZXhwZWN0YW5jeSBvdXRzaWRlIHRoaXMgcmFuZ2UgaW4gMjAwMiB3aWxsIG5vdCBnZXQgZGlzcGxheWVkLgoKYGBge3J9CnJhbmdlKG9sZERhdGEkbGlmZUV4cCkKcmFuZ2Uob2xkRGF0YSRnZHBQZXJjYXApCnJhbmdlKG5ld0RhdGEkbGlmZUV4cCkKcmFuZ2UobmV3RGF0YSRnZHBQZXJjYXApCgpgYGAKCldlIGNhbiBkZWZpbmUgdGhlIGF4ZXMgd2hlbiB3ZSBjcmVhdGUgdGhlIHBsb3QgdXNpbmcgYHhsaW1gIGFuZCBgeWxpbWAuCgpgYGB7cn0KcGxvdChvbGREYXRhJGdkcFBlcmNhcCwgb2xkRGF0YSRsaWZlRXhwLGNvbD0icmVkIiwKICAgICBwY2g9MTYsCiAgICAgeGxhYj0iR0RQIiwKICAgICB5bGFiPSJMaWZlIEV4cGVjdGFuY3kiLAogICAgIHhsaW09YygwLDEuMWU1KSx5bGltPWMoMzAsOTApKQpwb2ludHMobmV3RGF0YSRnZHBQZXJjYXAsIG5ld0RhdGEkbGlmZUV4cCxjb2w9ImJsdWUiLHBjaD0xNikKYGBgCgpBIGxlZ2VuZCBjYW4gYWxzbyBiZSBhZGRlZCB1c2luZyB0aGUgYGxlZ2VuZGAgZnVuY3Rpb24KCi0gWW91IGNhbiBzcGVjaWZ5IGEgc2V0IG9mIGNvb3JkaW5hdGVzIHdoZXJlIHlvdSB3YW50IHRvIGRpc3BsYXkgdGhlIGxlZ2VuZCwgYnV0IHRoZXJlIGFyZSBhbHNvIHNob3J0Y3V0cwoKYGBge3J9CnBsb3Qob2xkRGF0YSRnZHBQZXJjYXAsIG9sZERhdGEkbGlmZUV4cCxjb2w9InJlZCIsCiAgICAgcGNoPTE2LAogICAgIHhsYWI9IkdEUCIsCiAgICAgeWxhYj0iTGlmZSBFeHBlY3RhbmN5IiwKICAgICB4bGltPWMoMCwxLjFlNSkseWxpbT1jKDMwLDkwKSkKcG9pbnRzKG5ld0RhdGEkZ2RwUGVyY2FwLCBuZXdEYXRhJGxpZmVFeHAsY29sPSJibHVlIixwY2g9MTYpCmxlZ2VuZCgidG9wcmlnaHQiLCBsZWdlbmQ9YygxOTUyLDIwMDIpLGNvbD1jKCJyZWQiLCJibHVlIikscGNoPTE2KQpgYGAKClRoZSBgdGV4dGAgZnVuY3Rpb24gd29ya3MgaW4gYSBzaW1pbGFyIHdheSB0byBgcG9pbnRzYAoKLSBZb3UgZ2l2ZSBpdCB0aGUgc2V0IG9mIHggYW5kIHkgY29vcmRpbmF0ZXMgeW91IHdhbnQgdG8gcGxvdAogICAgKyBwbHVzIGFsc28gYSBzZXQgb2YgbGFiZWxzCgpgYGB7cn0KcGxvdChvbGREYXRhJGdkcFBlcmNhcCwgb2xkRGF0YSRsaWZlRXhwLGNvbD0icmVkIiwKICAgICBwY2g9MTYsCiAgICAgeGxhYj0iR0RQIiwKICAgICB5bGFiPSJMaWZlIEV4cGVjdGFuY3kiLAogICAgIHhsaW09YygwLDEuMWU1KSx5bGltPWMoMzAsOTApKQpwb2ludHMobmV3RGF0YSRnZHBQZXJjYXAsIG5ld0RhdGEkbGlmZUV4cCxjb2w9ImJsdWUiLHBjaD0xNikKdGV4dChjKDJlMDQsNGUwNCksYyg0MCw0MCksbGFiZWxzID0gYygiSGVsbG8iLCJXb3JsZCIpKQoKbGVnZW5kKCJ0b3ByaWdodCIsIGxlZ2VuZD1jKDE5NTIsMjAwMiksY29sPWMoInJlZCIsImJsdWUiKSxwY2g9MTYpCgpgYGAKCgoqKioqKioKKioqKioqCioqKioqKgoKIyMgRXhlcmNpc2UKCi0gV2hhdCBpcyB0aGF0IG91dGxpZXIgb24gdGhlIHggYXhpcyAoR0RQKT8KICAgICsgdXNlIGEgbG9naWNhbCB0ZXN0IHRvIGZpbmQgb3V0Ci0gVXNlIHRoZSB0ZXh0IGZ1bmN0aW9uIHRvIGFubm90YXRlIHRoZSBwbG90IHdpdGggdGhlIGNvcnJlc3BvbmRpbmcgY291bnRyeSBuYW1lCgpgYGB7cn0KcGxvdChvbGREYXRhJGdkcFBlcmNhcCwgb2xkRGF0YSRsaWZlRXhwLGNvbD0icmVkIiwKICAgICBwY2g9MTYsCiAgICAgeGxhYj0iR0RQIiwKICAgICB5bGFiPSJMaWZlIEV4cGVjdGFuY3kiLAogICAgIHhsaW09YygwLDEuMWU1KSx5bGltPWMoMzAsOTApKQpwb2ludHMobmV3RGF0YSRnZHBQZXJjYXAsIG5ld0RhdGEkbGlmZUV4cCxjb2w9ImJsdWUiLHBjaD0xNikKCiMjIFNvbWUgY29kZSBoZXJlIHRvIGZpbmQgdGhlIG91dGxpZXIgaGVyZS4uLgoKYGBgCgoqKioqKioKKioqKioqCioqKioqKgoKCiAgICAKCgoKIyMjIFNwZWNpZnlpbmcgYSB2ZWN0b3Igb2YgY29sb3VycwoKLSBTbyBmYXIgd2UgaGF2ZSB1c2VkIGEgdmVjdG9yIG9mIGxlbmd0aCAxIHRvIHNwZWNpZnkgdGhlIGNvbG91cnMKICAgICsgZS5nLiBgY29sPSJyZWQiCi0gV2UgbmVlZG4ndCBoYXZlIHRvIGRvIHRoaXMuIFRoZSBgY29sYCBhcmd1bWVudCBjYW4gaGF2ZSBtdWx0aXBsZSB2YWx1ZXMKICAgICsgdGhlIHZhbHVlcyB3aWxsIGdldCAicmUtY3ljbGVkIiBpbiBvcmRlciB0byBjb21wbGV0ZSB0aGUgcGxvdAotIEluIHRoZSBjb2RlIGJlbG93IHdlIHVzZSBlaXRoZXIgYHJlZGAgb3IgYGJsdWVgIHRvIGNvbG91ciB0aGUgcG9pbnRzCiAgICArIG9ubHkgb25lIGBwbG90YCBjb21tYW5kIGlzIHVzZWQKICAgICsgc2luY2UgMTk1MiBhbmQgMjAwMiBhbGVybmF0ZSBpbiB0aGUgZGF0YSBmcmFtZSwgdGhlIHllYXJzIHdpbGwgYmUgcGxvdHRlZCBgcmVkYCBvciBgYmx1ZWAgYWNjb3JkaW5nbHkKIApgYGB7cn0Kc3Vic2V0IDwtIGdhcG1pbmRlcltnYXBtaW5kZXIkeWVhciAlaW4lIGMoMTk1MiwgMjAwMiksXQpzdWJzZXQKcGxvdChzdWJzZXQkZ2RwUGVyY2FwLCBzdWJzZXQkbGlmZUV4cCwKICAgICB4bGFiPSJHRFAiLAogICAgIHlsYWI9IkxpZmUgRXhwZWN0YW5jeSIsCiAgICAgeGxpbT1jKDAsMS4xZTUpLHlsaW09YygzMCw5MCksCiAgICAgY29sPWMoInJlZCIsImJsdWUiKSwKICAgICBwY2g9YygxNiwxNykpCgpgYGAKCgpBIHVzZWZ1bCB0cmljayB0byBtYWtlIHBsb3RzIGxvb2sgbmljZSBpcyB0byB0YWtlIGFkdmFudGFnZSBvZiBwcmUtZXhpc3RpbmcgY29sb3VyIHBhbGV0dGVzIGluIFIuIFRoZSBgUkNvbG9yQnJld2VyYCBwYWNrYWdlIGlzIGEgdXNlZnVsIHBhY2thZ2UgZm9yIHN1Y2ggcGFsZXR0ZXM7IG1hbnkgb2Ygd2hpY2ggYXJlIGZyaWVuZGx5IHRvIHRob3NlIHdpdGggdmlzdWFsIGltcGFpcm1lbnRzLgoKYGBge3J9CmxpYnJhcnkoUkNvbG9yQnJld2VyKQpkaXNwbGF5LmJyZXdlci5hbGwoY29sb3JibGluZEZyaWVuZGx5ID0gVFJVRSkKCmBgYAoKVGhlIGBicmV3ZXIucGFsYCBmdW5jdGlvbiBjYW4gcmV0dXJuIHRoZSBuYW1lcyBvZiBgbmAgY29sb3VycyBmcm9tIG9uZSBvZiB0aGUgcHJlLWRlZmluZWQgcGFsZXR0ZXMgdG8gYmUgdXNlZCBhcyBhIGBjb2xgIGFyZ3VtZW50IHRvIGEgcGxvdHRpbmcgZnVuY3Rpb24uCgpgYGB7cn0KYm94cGxvdChnYXBtaW5kZXIkZ2RwUGVyY2FwIH4gZ2FwbWluZGVyJGNvbnRpbmVudCxjb2w9YnJld2VyLnBhbCg1LCJTZXQxIiksCiAgICAgICAgbWFpbj0iR0RQIHBlci1jb250aW5lbnQiLAogICAgICAgIHhsYWI9IkNvbnRpbmVudCIsCiAgICAgICAgeWxhYj0iR0RQIikKYGBgCgojIyBQbG90IExheW91dHMKCkEgZnVydGhlciBzb2x1dGlvbiB0byB0aGUgcHJvYmxlbSBvZiBjb21wYXJpbmcgZGlmZmVyZW50IHllYXJzIG1pZ2h0IGJlIHRvIGhhdmUgdHdvIHBsb3RzIGFycmFuZ2VkIHNpZGUtYnktc2lkZQoKLSBPbmUgb3B0aW9uIHRoYXQgY2FuIGJlIHNldCB3aXRoIGBwYXJgIGlzICptKnVsdGlwbGUgKmYqaWd1cmVzIGJ5ICpyb3cqCiAgICArIGFyZ3VtZW50IGlzIGEgdmVjdG9yIGNvbnRhaW5pbmcgbnVtYmVyIG9mIGNvbHVtbnMgYW5kIHJvd3MgaW4gdGhlIGZpZ3VyZQoKYGBge3J9CnBhcihtZnJvdz1jKDEsMikpCnBsb3Qob2xkRGF0YSRnZHBQZXJjYXAsIG9sZERhdGEkbGlmZUV4cCxjb2w9InJlZCIsCiAgICAgcGNoPTE2LAogICAgIHhsYWI9IkdEUCIsCiAgICAgeWxhYj0iTGlmZSBFeHBlY3RhbmN5IiwKICAgICB4bGltPWMoMCwxLjFlNSkseWxpbT1jKDMwLDkwKSkKCnBsb3QobmV3RGF0YSRnZHBQZXJjYXAsIG5ld0RhdGEkbGlmZUV4cCxjb2w9ImJsdWUiLAogICAgIHBjaD0xNiwKICAgICB4bGFiPSJHRFAiLAogICAgIHlsYWI9IkxpZmUgRXhwZWN0YW5jeSIsCiAgICAgeGxpbT1jKDAsMS4xZTUpLHlsaW09YygzMCw5MCkpCgpgYGAKCkRvbid0IG5lZWQgdG8gaGF2ZSB0aGUgc2FtZSBraW5kIG9mIHBsb3QgaW4gZWFjaCBjZWxsLgoKCmBgYHtyfQpwYXIobWZyb3c9YygxLDIpKQpwbG90KG9sZERhdGEkZ2RwUGVyY2FwLCBvbGREYXRhJGxpZmVFeHAsY29sPSJyZWQiLAogICAgIHBjaD0xNiwKICAgICB4bGFiPSJHRFAiLAogICAgIHlsYWI9IkxpZmUgRXhwZWN0YW5jeSIsCiAgICAgeGxpbT1jKDAsMS4xZTUpLHlsaW09YygzMCw5MCkpCgpib3hwbG90KG9sZERhdGEkbGlmZUV4cCB+IG9sZERhdGEkY29udGluZW50KQoKYGBgCgpUaGVyZSBpcyBwbGVudHkgbW9yZSB0byBrbm93IGFib3V0IHBsb3R0aW5nIHRoYXQgd2UgZG9uJ3QgaGF2ZSB0aW1lIHRvIGNvdmVyCgotIGFkZGluZyBsaW5lcyBvZiBiZXN0IGZpdCB3aXRoIGBhYmxpbmVgCi0gYWRkaW5nIGdyaWRzIHdpdGggYGdyaWRgCi0gY29udHJvbCBvdmVyIGF4aXMgYXBwZWFyYW5jZSBhbmQgbGFiZWxzIHdpdGggYGF4aXNgCi0gZm9yIG1vcmUgaW5zcGlyYXRpb24sIHNlZSB0aGUgW1IgZ3JhcGggZ2FsbGVyeV0oaHR0cDovL3d3dy5yLWdyYXBoLWdhbGxlcnkuY29tLykKCiMjICgqKipPcHRpb25hbCoqKikKCiMjIFN0YXRpc3RpY2FsIFRlc3RpbmcKCldlIGNhbid0IHJlYWxseSBoYXZlIGEgcnVuLXRocm91Z2ggb2YgdGhlIFIgbGFuZ3VhZ2Ugd2l0aG91dCBhdCBsZWFzdCAqbWVudGlvbmluZyogc3RhdGlzdGljcyEgSG93ZXZlciwgbGlrZSBwbG90dGluZyBpdCBpcyBhIHZhc3QgZmllbGQuIFRoZSBtYWluIGNoYWxsZW5nZXMgYXJlIHB1dHRpbmcgeW91ciBkYXRhIGluIHRoZSBjb3JyZWN0IGZvcm1hdCAod2hpY2ggd2UgaGF2ZSBjb3ZlcmVkIGhlcmUpLCBhbmQgZGVjaWRpbmcgd2hpY2ggdGVzdCB0byB1c2UgKCoqd2hpY2ggUiB3aWxsIG5vdCBhZHZpc2UgeW91IG9uISoqKSAKCiFbXSguLi9pbWFnZXMvY2xpcHB5LmpwZykKCi0gSWYgeW91IGhhdmUgc29tZSBiYWNrZ3JvdW5kIGluIHN0YXRpc3RpY3MgeW91IGNhbiBzZWUgdGhpcyBjb3Vyc2UgZnJvbSB0aGUgW0JhYnJhaGFtIEluc3RpdHV0ZSBCaW9pbmZvcm1hdGljcyBDb3JlXShodHRwOi8vd3d3LmJpb2luZm9ybWF0aWNzLmJhYnJhaGFtLmFjLnVrL3RyYWluaW5nL1JfU3RhdGlzdGljcy9JbnRyb2R1Y3Rpb24lMjB0byUyMFN0YXRpc3RpY3MlMjB3aXRoJTIwUi5wZGYpIGFib3V0IGhvdyB0byBwZXJmb3JtIHN0YXRpc3RpY2FsIHRlc3RpbmcgaW4gUi4KLSBJZiB5b3UgbmVlZCBhIG1vcmUgYmFzaWMgZ3JvdW5kaW5nIGluIHdoaWNoIHN0YXRpc3RpY2FsIHRlc3QgdG8gdXNlLCB5b3UgY2FuIHNlZSB0aGlzIGNvdXJzZSBmcm9tIFtDUlVLIENhbWJyaWRnZSBJbnN0aXR1dGVdKGh0dHA6Ly9iaW9pbmZvcm1hdGljcy1jb3JlLXNoYXJlZC10cmFpbmluZy5naXRodWIuaW8vSW50cm9kdWN0aW9uVG9TdGF0cy8pCiAgClRoZSBgdC50ZXN0YCBmdW5jdGlvbiBpcyBwcm9iYWJseSB0aGUgbW9zdCBmdW5kYW1lbnRhbCBzdGF0aXN0aWNhbCB0ZXN0aW5nIGZ1bmN0aW9uIGluIFIsIGFuZCBjYW4gYmUgYWRhcHRlZCB0byBtYW55IGRpZmZlcmVudCBzaXR1YXRpb25zLiBGdWxsIGRldGFpbHMgYXJlIGdpdmVuIGluIHRoZSBoZWxwIHBhZ2UgYD90LnRlc3RgLiBMZXRzIGNvbnNpZGVyIHdlIGhhdmUgdHdvIHZlY3RvcnMgb2Ygbm9ybWFsbHktZGlzdHJpYnV0ZWQgZGF0YSB0aGF0IHdlIGNhbiB2aXN1YWxpc2UgdXNpbmcgYSBib3hwbG90LgoKYGBge3J9CnggPC0gcm5vcm0oMjApCnkgPC0gcm5vcm0oMjAsIDUsMSkKZGYgPC0gZGF0YS5mcmFtZSh4LHkpCmJveHBsb3QoZGYpCmBgYAoKVGhlIG91dHB1dCBmcm9tIGB0LnRlc3RgIGNhbiBiZSB1c2VkIHRvIGp1ZGdlIGlmIHRoZXJlIGlzIGEgc3RhdGlzdGljYWxseS1zaWduaWZpY2FudCBkaWZmZXJlbmNlIGluIG1lYW5zOi0KCmBgYHtyfQp0LnRlc3QoeCx5KQpgYGAKCklmIG91ciBkYXRhIHdlcmUgcGFpcmVkIHdlIGNvdWxkIHNldCB0aGUgYXJndW1lbnQgYHBhaXJlZD1UUlVFYCB0byB1c2UgYSBkaWZmZXJlbnQgZmxhdm91ciBvZiB0aGUgdGVzdAoKYGBge3J9CnQudGVzdCh4LHkscGFpcmVkID0gVFJVRSkKYGBgCgpTaW1pbGFybHksIGlmIG91ciBkYXRhIGhhdmUgZGlmZmVyZW50IHZhcmlhbmNlcyB3ZSBjYW4gYWRqdXN0IHRoZSB0ZXN0IGFjY29yZGluZ2x5Oi0KCmBgYHtyfQp4IDwtIHJub3JtKDIwKQp5IDwtIHJub3JtKDIwLCA1LDQpCmRmIDwtIGRhdGEuZnJhbWUoeCx5KQpib3hwbG90KGRmKQp0LnRlc3QoeCx5LHZhci5lcXVhbCA9IEZBTFNFKQpgYGAKCldlcmUgb3VyIGRhdGEgbm90IG5vcm1hbGx5LWRpc3RyaWJ1dGVkIHdlIGNvdWxkIHVzZSBgd2lsY294LnRlc3RgLCBmb3IgZXhhbXBsZS4gIEZvcnR1bmF0ZWx5LCBtb3N0IHN0YXRpc3RpY2FsIHRlc3RzIGNhbiBiZSBhY2Nlc3NlZCBpbiBhIHNpbWlsYXIgbWFubmVyLCBzbyBpdCBpcyBlYXN5IHRvIHN3aXRjaCBiZXR3ZWVuIHVzaW5nIGRpZmZlcmVudCB0ZXN0cyBwcm92aWRlZCB5b3VyIGRhdGEgYXJlIGluIHRoZSBjb3JyZWN0IGZvcm1hdC4gVG8gcmUtaXRlcmF0ZSwgdGhlIHNraWxsIGlzIGluIGNob29zaW5nIHdoaWNoIHRlc3QgaXMgYXBwcm9wcmlhdGUuCgpgYGB7cn0Kd2lsY294LnRlc3QoeCx5LHZhci5lcXVhbCA9IEZBTFNFKQpgYGAKCg==