Data Focus

Modules and Handouts 2–7 cover topics that are part of the data analysis journey and are all interrelated. Each module will introduce new content and expand on the material covered in previous modules. For example, in Module 2 we introduce the basics of the R plotting systems. Subsequent handouts include more advanced plotting options and techniques.



Associated Material

Zoom Notes: Zoom notes 02 - Visualising Data

Readings:

Tabular Data

In this module we begin to work with complete tables of data, and learn how to make informative graphs.

We will start by getting our scientific research data into RStudio. Commonly, we first enter our data into Excel for cleaning and organising, and save it out as a csv (comma separated values) file. We then import the file into R for data analysis. We will provide the csv file to be used with this module.

When you have completed this module, you should be ready to produce the graphs and figures you will need for your in-course research projects. If you run into any problems or have any questions, email us, or drop us a message in the R4SSP class team on MS Teams.


Preparation - Do this very carefully

Document organisation is vitally important. We suggest that you keep a separate folder for each R4SSP module. To do this, you can set up a formal RStudio project (see for example https://support.rstudio.com/hc/en-us/articles/200526207-Using-Projects). But if you don’t feel quite ready for that, you can just collect your script file and data files together in a folder. Proceed as follows:

  1. Launch R Studio

  2. Following the procedure from Using a Script File in Module 1 - Intro to R and RStudio, create a script file and save it to your desktop or other location where you will be able to find it.

  3. Quit R Studio

  4. Locate the script file you just made (it will have a .R file suffix).

  5. Create a new folder (name it something sensible) and place the script file in it.

  6. The sample csv data file used in this module is called gapminder_data_2007.csv. This file contains life expectancy, population, and GDP values for 142 different countries. The data for each country were measured 12 times between 1952 and 2007. These data are sourced from the GapMinder foundation (https://www.gapminder.org/).

Downloading the GapMinder 2007 data:

One option for downloading the data is to use download.file() in R. You can use this command to download the file into your data/ directory:

download.file(url = "https://raw.githubusercontent.com/rtis-training/2023-s2-r4ssp/main/docs/data/gapminder_data_2007.csv", 
 destfile = "data/gapminder_data_2007.csv")

Remember that R is case sensitive so if you don’t have a directory exactly called data/ modify the command to match your directory.

The second option is in a web browser go to https://raw.githubusercontent.com/rtis-training/2023-s2-r4ssp/main/docs/data/gapminder_data_2007.csv and then save the page using Save As and give it the name “gapminder_data_2007.csv”. Save it to your data location.

  1. Open the folder and double-click on your script file to open it in RStudio.

If you set things up this way, RStudio will be able to find your data file for the next step – importing data.


Importing a data file

The first few lines of our data file gapminder_data_2007.csv as it appears when opened in Excel, are shown below:

GapMinder Data

GapMinder Data

We will import this file into R for some preliminary data analysis.

To import a csv file into R we can use the function read.csv. Like the R functions we used in Module 1, read.csv accepts an argument between its round brackets. The argument is the name of the input file. Because the name of the file is a string, we surround it with double quotes. The complete command is shown below.


Import and check your data

Type the following line into your script file. Execute the command as you did in the previous module by placing the cursor anywhere on the line and typing ctrl-Enter (Windows) or cmd-Enter (Mac).

# Save an imported data frame into a named variable
gapminder_data <- read.csv("data/gapminder_data_2007.csv")

N.B. stringsAsFactors=: A common argument for read.csv was stringsAsFactors=, which could be TRUE or FALSE. The default for R (v4.0+) is stringsAsFactors=FALSE as R will automatically treat character data as categorical for the purposes of visualisation. Many statistical methods or older versions of R, however, still require conversion to factors. See the appendix below for more information on creating factors.

When imported into R, the data from the csv file are translated into an R object called a data frame. Data frames are simply tables, organised into rows and columns. The columns have names taken from the first row of the csv file, and each subsequent row of the csv file becomes a row in the data frame.

We store the data frame in a named variable so that we can refer to it later (i.e., perform analyses on it). We use the assignment operator, as we did in our previous module.

After storing our data frame into a variable, you should always check that the data have been imported correctly. Data entry errors can cause R to make the wrong assumptions about your data. If you have a column of numbers that contains even one accidental alphabetic character (typos do happen) R will consider the whole column to be strings. Later, R will give the wrong results when you perform statistical analyses on these data (or it will refuse to perform them at all).

Use the following commands to inspect your imported data:

# Write the first few lines of a data frame to the console with function head
head(gapminder_data)
#>       country continent year lifeExp      pop gdpPercap
#> 1 Afghanistan      Asia 1952  28.801  8425333  779.4453
#> 2 Afghanistan      Asia 1957  30.332  9240934  820.8530
#> 3 Afghanistan      Asia 1962  31.997 10267083  853.1007
#> 4 Afghanistan      Asia 1967  34.020 11537966  836.1971
#> 5 Afghanistan      Asia 1972  36.088 13079460  739.9811
#> 6 Afghanistan      Asia 1977  38.438 14880372  786.1134

# Write the last few lines of a data frame to the console with function tail
tail(gapminder_data)
#>       country continent year lifeExp      pop gdpPercap
#> 1699 Zimbabwe    Africa 1982  60.363  7636524  788.8550
#> 1700 Zimbabwe    Africa 1987  62.351  9216418  706.1573
#> 1701 Zimbabwe    Africa 1992  60.377 10704340  693.4208
#> 1702 Zimbabwe    Africa 1997  46.809 11404948  792.4500
#> 1703 Zimbabwe    Africa 2002  39.989 11926563  672.0386
#> 1704 Zimbabwe    Africa 2007  43.487 12311143  469.7093
# Display the number of lines, the column names, and the data type of each 
# column, with function str (short for 'structure')
str(gapminder_data)
#> 'data.frame':    1704 obs. of  6 variables:
#>  $ country  : chr  "Afghanistan" "Afghanistan" "Afghanistan" "Afghanistan" ...
#>  $ continent: chr  "Asia" "Asia" "Asia" "Asia" ...
#>  $ year     : int  1952 1957 1962 1967 1972 1977 1982 1987 1992 1997 ...
#>  $ lifeExp  : num  28.8 30.3 32 34 36.1 ...
#>  $ pop      : int  8425333 9240934 10267083 11537966 13079460 14880372 12881816 13867957 16317921 22227415 ...
#>  $ gdpPercap: num  779 821 853 836 740 ...

Each column in a data frame is associated with a data type chr, int, or num. These indicate what kind of data R identified in the input file. Columns that are chr contain strings (characters), columns that are int contain integers (whole numbers) and columns that are num contain numbers with a decimal part. Always check that these properties of the imported columns are correct for your data set. If they are not, you must locate and correct any errors in your csv file.

You can see the same information about the structure of a data frame in the Environment panel of RStudio (upper-right of screen; Environment tab). When you successfully import a csv file into a variable with read.csv, the resulting data frame appears in the Environment pane. Click the blue arrow beside the object to display the details of its structure.

Data Frame in Environment Pane

Data Frame in Environment Pane


Selecting and using columns of data

In both of the above displays, each column name is prefaced with $. We use the $ operator to select individual columns of data from a data frame. For example, to select just the life expectancy values from our data, we say gapminder_data$lifeExp. (Be sure to type the column name exactly as it appears in the output of str and in the Environment pane.) We can use this expression directly as the argument to a function, or we can store the selected column in a variable (which will be a vector) for later processing.

For example:

# Print the first few values of column year
head(gapminder_data$year)
#> [1] 1952 1957 1962 1967 1972 1977

# Store column continent in a new variable
continents <- gapminder_data$continent


Creating graphs in R

There are a variety of complex analyses that we can perform on a data frame using R’s built-in statistical functions and those available in additional packages and libraries. We will explore many of these techniques in later modules. However, an effective first step in getting to know a data set is to generate plots and graphs to represent visually the patterns in the data.


Simple plots - the histogram

A histogram shows the frequency distribution of a data set. That is, it shows counts of the different values of the dependent variable (or ranges of values, for continuous variables). We generate this graph with function hist. The graph will be displayed in the Plots tab of RStudio’s lower-right pane.

# Histogram of life expectancy values from gapminder
hist(gapminder_data$lifeExp)


We see that the distribution of life expectancy is approximately bell-shaped, with many scores between 65 and 85, and a small number of extreme values greater than 80 or less than 30.


Boxplots

The boxplot allows us to compare distribution information between groups. For example, we can compare life expectancy for the different continents.

The R function boxplot accepts two arguments.

The first argument is the formula. This is a complex, yet very common, argument format for R statistical functions. The formula describes a linear model for a data set with the general structure: dependent or predicted variable ~ independent variables or predictors, using columns names from the data frame. The ~ (tilde) is read as “depends on” or “is predicted by”. For our example, we are interested in the way that life expectancy is dependent on the continent, so we specify our formula as lifeExp ~ continent. We will see more complex examples of the formula argument later in the semester.

The second argument to boxplot is the data frame.

boxplot(lifeExp ~ continent, gapminder_data)


Boxplots efficiently illustrate both the central tendency and the variability of a data set. Each grey box extends from the first quartile to the third quartile of its input values. The dark line across the box is at the median. The two thin lines outside the qrey box show the values of the minimum and maximum scores, excluding extreme outliers. If extreme outliers are present, they are shown as small circles. This figure clearly illustrates that, in the gapminder data, life expectancy – both central tendency and variablity – is not the same for all continents.


Plotting with ggplot2

The hist and boxplot functions are part of Base R. They are useful, but for more elaborate, publication-quality graphs, we can use the third-party library ggplot contained in package ggplot2. The ggplot library is a very popular data visualisation tool based on an elaborate symbolic system called the ‘Grammar of Graphics’.

The syntax of ggplot is complex, and we will concentrate on the foundations in this module. For additional detail, see the Data Visualisation chapter in the R for Data Science online text, at https://r4ds.had.co.nz/data-visualisation.html.


Semantics of ggplot

You can think of a ggplot graph as being built as a sequence of layers. On the bottom is the base of the graph, then the axes and the data are layered on, then titles and notations and other features. A ggplot command reflects this layered structure.


Building a graph

To use the ggplot library, we must install the ggplot2 package (once on a computer) and invoke the library command (for every RStudio session).

# Once on any computer
install.packages(ggplot2)

# Once for any RStudio session
library(ggplot2)

Every graph represents a data frame. The base part of any ggplot command is a call to function ggplot() passing in the data frame, assigned to function argument data.

# The ggplot base layer
ggplot(data = gapminder_data)


If you run this command from the RStudio console or an R script, the grey square shown above appears in the Plots pane. This indicates that ggplot is ready to draw a figure – this is the bottom layer of a ggplot graph.

To add x and y axes to the graph, we need to define the relationship between informational elements in the data set (the variables we want to plot) and visual elements in our graph (the axes). In ggplot this relationship is a mapping. To initialise a mapping, we identify a particular element of the graph (e.g. the x-axis) and assign a particular element of the data (e.g. a column in the data frame) to it. This assignment is called an aesthetic in the Grammar of Graphics, and in ggplot we use function aes() to specify aesthetics.

Imagine that we wish to make a graph showing the relationship between per capita GDP and life expectancy (two columns in gapminder_data). We map the first variable to the x axis (argument x) of our graph and the second to the y axis (argument y). This will add a new layer. We add this new information to the ggplot() base call as shown below. Note that we don’t need to use the $ operator here, as all column names in a ggplot command apply to the supplied data frame.

ggplot(data = gapminder_data, mapping = aes(x = gdpPercap, y = lifeExp ))


We have added a new layer to our graph with axes and grid lines. Note that the axes’ tic values are correctly formatted for the associated data and the data frame column names are used as the axis labels (we will see how to improve those labels later).

To add points to our graph, we specify a geometry (another term from the Grammar of Graphics). There are many, many available geometries in ggplot, corresponding to all the different sorts of graphs – scatterplots, bar plots, pie charts, line graphs, etc. – that you might wish to make. For our current graph, we wish to place a point at the intersection of per capita GDP (our x axis) and life expectancy (our y axis) for each row in the input data frame. To add this geometry to ggplot append geom_point() to your current ggplot command using the + operator. It is conventional to place each chunk of the ggplot command on its own line in the code.

# Add points (a 'geometry') to the graph
ggplot(data = gapminder_data, mapping = aes(x = gdpPercap, y = lifeExp )) +
  geom_point()


This type of graph (usually called a scatterplot) illustrates the relationship between two dependent variables. Even from this very simple figure we can see that there is a general tendency for higher per capita GDP to be associated with higher life expectancy in the gapminder data.

Like most functions, geom_point can accept arguments that modify its behaviour. The argument colour determines the colour of the points to be drawn, and can be assigned any of R’s built-in colour names (call function colours() to list all possible values) or a hexidecimal RGB code (see for example, https://r-charts.com/colors/).

ggplot(data = gapminder_data, mapping = aes(x = gdpPercap, y = lifeExp )) +
  geom_point(colour = 'tomato')


This livens up our plot, but it doesn’t acutally add any new information. It is better technique to use colour to represent another of our data variables. We might, for example, wish to use a different colour for each continent, to see how the relationship between GDP and life expectancy differs between continents. This requires defining a mapping between a visual feature (colour) and an element of the data set (column continent), so we initialise the mapping property with function aes, in our call to geom_point.

ggplot(data = gapminder_data, mapping = aes(x = gdpPercap, y = lifeExp )) +
  geom_point(mapping = aes(colour = continent))


This graph illustrates clearly that, in the gapminder data, life expectancy and per capita GDP vary substantially between continents.

You should carefully compare the two preceding graphs. In the first, we simply set the colour argument of functiongeom_point. In the second, we set the mapping argument of geom_point using function aes. In the former graph, all points are the same colour. In the latter graph, the colour of each point depends on its continent value. That is, we have mapped colour to continent.


Choosing geometries

It is essential to select the correct type of graph (the correct geometry in ggplot) for the data pattern you wish to illustrate.

Assume, for example, that you wish to show the change in life expectancy across years, for the country of Denmark. First, we must select out only the rows for Denmark from our data frame. (We will consider selection in detail in next week’s module. For now, just note that between the square brackets we provide row and column criteria for selection, and an empty value for column means all.)

We will then pass the selected data to ggplot as before, specifying the mapping of the data to the x and y axes.

The graph will be illustrating a trend (change in a variable across time). Trend graphs are usually drawn with a continuous line between the plotted points. In ggplot, this is geometry geom_line.

The complete code is:

# Select all rows where the country is equal to Denmark. Select all columns.
denmark_data <- gapminder_data[gapminder_data$country == "Denmark", ]

ggplot(data = denmark_data, mapping = aes(x = year, y = lifeExp)) +
  geom_line()


We can use ggplot to produce a histogram for life expectancy (as we did in Base R above) with geom_histogram. For histograms we only need to map the x axis, as the y axis represents, by default, frequency. We can enhance the plot’s appearance by initialising geom_histogram arguments colour which sets the border around the bars on the graph, and fill which sets the interior of the bars on the graph.

ggplot(data = gapminder_data, mapping = aes(x = lifeExp)) +
  geom_histogram(colour = "white", fill = "darkgreen")


Similarly, we can reproduce the boxplot above with geom_boxplot. In Base R we used a formula to identify the dependent and independent variables for the boxplot. With ggplot, we use a mapping to assign the DV to the x axis and the IV to the y axis.

ggplot(data = gapminder_data, mapping = aes(x = continent, y = lifeExp)) +
  geom_boxplot()


Exercise:

What would you predict to be the effect of swapping the values of x and y in the call to aes above? Test your prediction.


Refining the appearance of a plot

After we have built the foundation of our plot with data and geometry, we can add further layers to modify other visual features. For example, we can use function labs to set the axis, legend, and main titles of our plots. Consider the following enhancements to our figure illustrating the relationship between GDP and life expectancy by continent:

# NB: Multiple function arguments (as in labs below) can
# be placed on separate lines to improve readability

ggplot(data = gapminder_data, mapping = aes(x = gdpPercap, y = lifeExp )) +
  geom_point(mapping = aes(colour = continent)) +
  labs(x = "GDP Per Capita", 
       y = "Life Expectancy", 
       title = "Gap Minder Data 1952 to 2007", 
       colour = "Continent")


The code for ggplot formatting can get extremely complex, and the full functionality is beyond the scope of this module. In addition, there are many, many more geometries available, each with appropriate arguments and mapping options.

The formal documentation for ggplot can be found at https://ggplot2.tidyverse.org/index.html. If you prefer tutorials and galleries, there are many available online. Two good places to start are http://www.cookbook-r.com/Graphs/ and https://www.r-graph-gallery.com/.


Saving ggplots

You can save figures made with ggplot to image files, which can then be used in documents generated in MS Word or other text editors. We first save the output of our ggplot command into a named variable (to R a ggplot is a data object just like a number or a string). We then use function ggsave to export out plot as an image file. You specify the image format by supplying an outfile name with the corresponding file suffix (e.g. .jpg or .png). By default, the file is saved into the working folder (in our case, the folder containing our csv and script files).

# Save a ggplot to a variable. The syntax of the gpplot command is unaffected
gdp_lifeExp_plot <- ggplot(data = gapminder_data, mapping = aes(x = gdpPercap, y = lifeExp )) +
  geom_point(mapping = aes(colour = continent)) +
  xlab("GDP Per Capita") +
  ylab("Life Expectancy") +
  ggtitle("Gap Minder Data 1952 to 2007")

# Export the variable as an image file. Provide the file name and the ggplot object
ggsave(filename = "gdp_lifeExp_plot.png", gdp_lifeExp_plot)
#> Saving 7 x 5 in image



Conclusion

This document has presented a simple introduction to working with complete tables of data in R. We learned how to import a csv file into a data frame, and how to use Base R or library ggplot to generate graphs to illustrate important patterns in our data.


What’s Next

Ensure you have the tidyverse installed for the next module. The tidyverse is a collection of packages (a metapackage) that provide a succinct syntax for performing data manipulation and basic analysis in R. Running install.packages(tidyverse) installs all the individual packages to your machine.

The tidyverse consists of ggplot2 (plotting), dplyr (data manipulation), tidyr (data tidying), readr (data importing), purrr (functional programming), tibble (a special type of data frame), stringr (common tasks for string manipulations), and forcats (dealing with factors)

# Download and install the packages of tidyverse
install.packages("tidyverse")
# load the tidyverse packages for the current session
library(tidyverse)
#> ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
#> ✔ dplyr     1.1.2     ✔ readr     2.1.4
#> ✔ forcats   1.0.0     ✔ stringr   1.5.0
#> ✔ lubridate 1.9.2     ✔ tibble    3.2.1
#> ✔ purrr     1.0.1     ✔ tidyr     1.3.0
#> ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
#> ✖ dplyr::filter() masks stats::filter()
#> ✖ dplyr::lag()    masks stats::lag()
#> ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors

Appendix - Factors

If you are dealing with categorical data, R calls these Factors. Prior to R version 4.0 the default behaviour of read.csv was to have the parameter stringsAsFactors=TRUE as the default which would read all character data in automatically as a factor. From version 4.0 onwards the default is stringsAsFactors=FALSE.

To convert a column to a factor you can use as.factor. You can use levels on a factor to see the categories. It is best to wait until after any data tidying is performed before converting to factors. For instance if we repeated the code for pulling out the continents column from the gapminder dataset:

# Store column continent in a new variable after converting to a factor
continents_factor <- as.factor(gapminder_data$continent)

# Pass the variable to function levels which returns the values
# of a categorical variable
levels(continents_factor)
#> [1] "Africa"   "Americas" "Asia"     "Europe"   "Oceania"
LS0tCnRpdGxlOiAiVmlzdWFsaXNpbmciCmRhdGU6ICJTZW1lc3RlciAyLCAyMDIzIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KGdncGxvdDIpCgpsaWJyYXJ5KGtuaXRyKQoKa25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGNvbW1lbnQgPSAiIz4iLAogIGZpZy5wYXRoID0gImZpZ3VyZXMvMDIvIiwgIyB1c2Ugb25seSBmb3Igc2luZ2xlIFJtZCBmaWxlcwogIGNvbGxhcHNlID0gVFJVRSwKICBlY2hvID0gVFJVRQopCmBgYAoKPiAjIyMjIERhdGEgRm9jdXMKPgo+IE1vZHVsZXMgYW5kIEhhbmRvdXRzIDItLTcgY292ZXIgdG9waWNzIHRoYXQgYXJlIHBhcnQgb2YgdGhlIGRhdGEgYW5hbHlzaXMgam91cm5leSBhbmQgYXJlIGFsbCBpbnRlcnJlbGF0ZWQuIEVhY2ggbW9kdWxlIHdpbGwgaW50cm9kdWNlIG5ldyBjb250ZW50IGFuZCBleHBhbmQgb24gdGhlIG1hdGVyaWFsIGNvdmVyZWQgaW4gcHJldmlvdXMgbW9kdWxlcy4gRm9yIGV4YW1wbGUsIGluIE1vZHVsZSAyIHdlIGludHJvZHVjZSB0aGUgYmFzaWNzIG9mIHRoZSBSIHBsb3R0aW5nIHN5c3RlbXMuIFN1YnNlcXVlbnQgaGFuZG91dHMgaW5jbHVkZSBtb3JlIGFkdmFuY2VkIHBsb3R0aW5nIG9wdGlvbnMgYW5kIHRlY2huaXF1ZXMuCgpcCgpcCgo+ICMjIyMgQXNzb2NpYXRlZCBNYXRlcmlhbAo+Cj4gWm9vbSBOb3RlczogW1pvb20gbm90ZXMgMDIgLSBWaXN1YWxpc2luZyBEYXRhXSh6b29tX25vdGVzXzAyX3Zpc3VhbGlzZS5odG1sKQo+IAo+IFJlYWRpbmdzOgo+Cj4gLSBbUiBmb3IgRGF0YSBTY2llbmNlIC0gQ2hhcHRlciAxMl0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei90aWR5LWRhdGEuaHRtbCkKPiAtIFtSIGZvciBEYXRhIFNjaWVuY2UgLSBDaGFwdGVyIDExXShodHRwczovL3I0ZHMuaGFkLmNvLm56L2RhdGEtaW1wb3J0Lmh0bWwpCj4gLSBbUiBmb3IgRGF0YSBTY2llbmNlIC0gQ2hhcHRlciAzXShodHRwczovL3I0ZHMuaGFkLmNvLm56L2RhdGEtdmlzdWFsaXNhdGlvbi5odG1sKQoKCgojIyBUYWJ1bGFyIERhdGEKCkluIHRoaXMgbW9kdWxlIHdlIGJlZ2luIHRvIHdvcmsgd2l0aCBjb21wbGV0ZSB0YWJsZXMgb2YgZGF0YSwgYW5kIGxlYXJuIGhvdyB0byBtYWtlIGluZm9ybWF0aXZlIGdyYXBocy4gCgpXZSB3aWxsIHN0YXJ0IGJ5IGdldHRpbmcgb3VyIHNjaWVudGlmaWMgcmVzZWFyY2ggZGF0YSBpbnRvIFJTdHVkaW8uIENvbW1vbmx5LCB3ZSBmaXJzdCBlbnRlciBvdXIgZGF0YSBpbnRvIEV4Y2VsIGZvciBjbGVhbmluZyBhbmQgb3JnYW5pc2luZywgYW5kIHNhdmUgaXQgb3V0IGFzIGEgY3N2IChjb21tYSBzZXBhcmF0ZWQgdmFsdWVzKSBmaWxlLiBXZSB0aGVuIGltcG9ydCB0aGUgZmlsZSBpbnRvIFIgZm9yIGRhdGEgYW5hbHlzaXMuIFdlIHdpbGwgcHJvdmlkZSB0aGUgY3N2IGZpbGUgdG8gYmUgdXNlZCB3aXRoIHRoaXMgbW9kdWxlLgoKV2hlbiB5b3UgaGF2ZSBjb21wbGV0ZWQgdGhpcyBtb2R1bGUsIHlvdSBzaG91bGQgYmUgcmVhZHkgdG8gcHJvZHVjZSB0aGUgZ3JhcGhzIGFuZCBmaWd1cmVzIHlvdSB3aWxsIG5lZWQgZm9yIHlvdXIgaW4tY291cnNlIHJlc2VhcmNoIHByb2plY3RzLiBJZiB5b3UgcnVuIGludG8gYW55IHByb2JsZW1zIG9yIGhhdmUgYW55IHF1ZXN0aW9ucywgZW1haWwgdXMsIG9yIGRyb3AgdXMgYSBtZXNzYWdlIGluIHRoZSBSNFNTUCBjbGFzcyB0ZWFtIG9uIE1TIFRlYW1zLgoKXAoKIyMgUHJlcGFyYXRpb24gLSBEbyB0aGlzIHZlcnkgY2FyZWZ1bGx5CgpEb2N1bWVudCBvcmdhbmlzYXRpb24gaXMgdml0YWxseSBpbXBvcnRhbnQuIFdlIHN1Z2dlc3QgdGhhdCB5b3Uga2VlcCBhIHNlcGFyYXRlIGZvbGRlciBmb3IgZWFjaCBSNFNTUCBtb2R1bGUuIFRvIGRvIHRoaXMsIHlvdSBjYW4gc2V0IHVwIGEgZm9ybWFsIFJTdHVkaW8gcHJvamVjdCAoc2VlIGZvciBleGFtcGxlIGh0dHBzOi8vc3VwcG9ydC5yc3R1ZGlvLmNvbS9oYy9lbi11cy9hcnRpY2xlcy8yMDA1MjYyMDctVXNpbmctUHJvamVjdHMpLiBCdXQgaWYgeW91IGRvbid0IGZlZWwgcXVpdGUgcmVhZHkgZm9yIHRoYXQsIHlvdSBjYW4ganVzdCBjb2xsZWN0IHlvdXIgc2NyaXB0IGZpbGUgYW5kIGRhdGEgZmlsZXMgdG9nZXRoZXIgaW4gYSBmb2xkZXIuIFByb2NlZWQgYXMgZm9sbG93czoKCgoxLiBMYXVuY2ggUiBTdHVkaW8KCjIuIEZvbGxvd2luZyB0aGUgcHJvY2VkdXJlIGZyb20gKipVc2luZyBhIFNjcmlwdCBGaWxlKiogaW4gTW9kdWxlIDEgLSBJbnRybyB0byBSIGFuZCBSU3R1ZGlvLCBjcmVhdGUgYSBzY3JpcHQgZmlsZSBhbmQgc2F2ZSBpdCB0byB5b3VyIGRlc2t0b3Agb3Igb3RoZXIgbG9jYXRpb24gd2hlcmUgeW91IHdpbGwgYmUgYWJsZSB0byBmaW5kIGl0LgoKMy4gUXVpdCBSIFN0dWRpbwoKNC4gTG9jYXRlIHRoZSBzY3JpcHQgZmlsZSB5b3UganVzdCBtYWRlIChpdCB3aWxsIGhhdmUgYSAuUiBmaWxlIHN1ZmZpeCkuIAoKNS4gQ3JlYXRlIGEgbmV3IGZvbGRlciAobmFtZSBpdCBzb21ldGhpbmcgc2Vuc2libGUpIGFuZCBwbGFjZSB0aGUgc2NyaXB0IGZpbGUgaW4gaXQuCgo2LiBUaGUgc2FtcGxlIGNzdiBkYXRhIGZpbGUgdXNlZCBpbiB0aGlzIG1vZHVsZSBpcyBjYWxsZWQgKipnYXBtaW5kZXJfZGF0YV8yMDA3LmNzdioqLiBUaGlzIGZpbGUgY29udGFpbnMgbGlmZSBleHBlY3RhbmN5LCBwb3B1bGF0aW9uLCBhbmQgR0RQIHZhbHVlcyBmb3IgMTQyIGRpZmZlcmVudCBjb3VudHJpZXMuIFRoZSBkYXRhIGZvciBlYWNoIGNvdW50cnkgd2VyZSBtZWFzdXJlZCAxMiB0aW1lcyBiZXR3ZWVuIDE5NTIgYW5kIDIwMDcuIFRoZXNlIGRhdGEgYXJlIHNvdXJjZWQgZnJvbSB0aGUgR2FwTWluZGVyIGZvdW5kYXRpb24gKGh0dHBzOi8vd3d3LmdhcG1pbmRlci5vcmcvKS4KCj4gRG93bmxvYWRpbmcgdGhlIEdhcE1pbmRlciAyMDA3IGRhdGE6Cj4gCj4gT25lIG9wdGlvbiBmb3IgZG93bmxvYWRpbmcgdGhlIGRhdGEgaXMgdG8gdXNlIGBkb3dubG9hZC5maWxlKClgIGluIFIuIFlvdSBjYW4gdXNlIHRoaXMgY29tbWFuZCB0byBkb3dubG9hZCB0aGUgZmlsZSBpbnRvIHlvdXIgYGRhdGEvYCBkaXJlY3Rvcnk6IAo+IAo+IGBgYHtyLCBldmFsID0gRkFMU0V9Cj4gZG93bmxvYWQuZmlsZSh1cmwgPSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3J0aXMtdHJhaW5pbmcvMjAyMy1zMi1yNHNzcC9tYWluL2RvY3MvZGF0YS9nYXBtaW5kZXJfZGF0YV8yMDA3LmNzdiIsIAo+ICBkZXN0ZmlsZSA9ICJkYXRhL2dhcG1pbmRlcl9kYXRhXzIwMDcuY3N2IikKPiBgYGAKPiBfUmVtZW1iZXIgdGhhdCBSIGlzIGNhc2Ugc2Vuc2l0aXZlIHNvIGlmIHlvdSBkb24ndCBoYXZlIGEgZGlyZWN0b3J5IGV4YWN0bHkgY2FsbGVkIGBkYXRhL2AgbW9kaWZ5IHRoZSBjb21tYW5kIHRvIG1hdGNoIHlvdXIgZGlyZWN0b3J5Ll8KPgo+IFRoZSBzZWNvbmQgb3B0aW9uIGlzIGluIGEgd2ViIGJyb3dzZXIgZ28gdG8gaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3J0aXMtdHJhaW5pbmcvMjAyMy1zMi1yNHNzcC9tYWluL2RvY3MvZGF0YS9nYXBtaW5kZXJfZGF0YV8yMDA3LmNzdiBhbmQgdGhlbiBzYXZlIHRoZSBwYWdlIHVzaW5nIGBTYXZlIEFzYCBhbmQgZ2l2ZSBpdCB0aGUgbmFtZSAiZ2FwbWluZGVyX2RhdGFfMjAwNy5jc3YiLiBTYXZlIGl0IHRvIHlvdXIgZGF0YSBsb2NhdGlvbi4KCjwhLS0KUmV0cmlldmUgdGhlIHNhbXBsZSBjc3YgZGF0YSBmaWxlIGZyb20gdGhlIGNvdXJzZSBbR29vZ2xlIERyaXZlIHNoYXJlZCBmb2xkZXJdKGh0dHBzOi8vZHJpdmUuZ29vZ2xlLmNvbS9kcml2ZS9mb2xkZXJzLzF0dGYxczgtdmtKTk9sSGRwaGZpMnpGeU1xNmdHRXZDeT91c3A9c2hhcmluZykuIFBsYWNlIHRoZSBjc3YgZmlsZSBpbnRvIHRoZSBmb2xkZXIgd2l0aCB5b3VyIHNjcmlwdCBmaWxlLgotLT4KCgo3LiBPcGVuIHRoZSBmb2xkZXIgYW5kIGRvdWJsZS1jbGljayAqKm9uIHlvdXIgc2NyaXB0IGZpbGUqKiB0byBvcGVuIGl0IGluIFJTdHVkaW8uCgpJZiB5b3Ugc2V0IHRoaW5ncyB1cCB0aGlzIHdheSwgUlN0dWRpbyB3aWxsIGJlIGFibGUgdG8gZmluZCB5b3VyIGRhdGEgZmlsZSBmb3IgdGhlIG5leHQgc3RlcCAtLSBpbXBvcnRpbmcgZGF0YS4KClwKCiMjIEltcG9ydGluZyBhIGRhdGEgZmlsZQoKVGhlIGZpcnN0IGZldyBsaW5lcyBvZiBvdXIgZGF0YSBmaWxlICoqZ2FwbWluZGVyX2RhdGFfMjAwNy5jc3YqKiBhcyBpdCBhcHBlYXJzIHdoZW4gb3BlbmVkIGluIEV4Y2VsLCBhcmUgc2hvd24gYmVsb3c6CgpgYGB7ciBmaWcwMSwgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGggPSAiNTAlIiwgZmlnLmNhcCA9ICJHYXBNaW5kZXIgRGF0YSIsIGZpZy5wb3MgPSAiSCIsIGVjaG8gPSBGQUxTRX0KaW5jbHVkZV9ncmFwaGljcygiaW1hZ2VzLzAyLWdhcG1pbmRlcl9kYXRhXzIwMDcucG5nIikKYGBgCgoKCldlIHdpbGwgaW1wb3J0IHRoaXMgZmlsZSBpbnRvIFIgZm9yIHNvbWUgcHJlbGltaW5hcnkgZGF0YSBhbmFseXNpcy4KClRvIGltcG9ydCBhIGNzdiBmaWxlIGludG8gUiB3ZSBjYW4gdXNlIHRoZSBmdW5jdGlvbiBgcmVhZC5jc3ZgLiBMaWtlIHRoZSBSIGZ1bmN0aW9ucyB3ZSB1c2VkIGluIE1vZHVsZSAxLCBgcmVhZC5jc3ZgIGFjY2VwdHMgYW4gYXJndW1lbnQgYmV0d2VlbiBpdHMgcm91bmQgYnJhY2tldHMuIFRoZSBhcmd1bWVudCBpcyB0aGUgbmFtZSBvZiB0aGUgaW5wdXQgZmlsZS4gQmVjYXVzZSB0aGUgbmFtZSBvZiB0aGUgZmlsZSBpcyBhIHN0cmluZywgd2Ugc3Vycm91bmQgaXQgd2l0aCBkb3VibGUgcXVvdGVzLiBUaGUgY29tcGxldGUgY29tbWFuZCBpcyBzaG93biBiZWxvdy4KClwKCiMjIEltcG9ydCBhbmQgY2hlY2sgeW91ciBkYXRhCgpUeXBlIHRoZSBmb2xsb3dpbmcgbGluZSBpbnRvIHlvdXIgc2NyaXB0IGZpbGUuIEV4ZWN1dGUgdGhlIGNvbW1hbmQgYXMgeW91IGRpZCBpbiB0aGUgcHJldmlvdXMgbW9kdWxlIGJ5IHBsYWNpbmcgdGhlIGN1cnNvciBhbnl3aGVyZSBvbiB0aGUgbGluZSBhbmQgdHlwaW5nIGN0cmwtRW50ZXIgKFdpbmRvd3MpIG9yIGNtZC1FbnRlciAoTWFjKS4KCmBgYHtyIHJlYWQuY3N2fQojIFNhdmUgYW4gaW1wb3J0ZWQgZGF0YSBmcmFtZSBpbnRvIGEgbmFtZWQgdmFyaWFibGUKZ2FwbWluZGVyX2RhdGEgPC0gcmVhZC5jc3YoImRhdGEvZ2FwbWluZGVyX2RhdGFfMjAwNy5jc3YiKQpgYGAKCj4gTi5CLiBgc3RyaW5nc0FzRmFjdG9ycz1gOiBBIGNvbW1vbiBhcmd1bWVudCBmb3IgYHJlYWQuY3N2YCB3YXMgYHN0cmluZ3NBc0ZhY3RvcnM9YCwgd2hpY2ggY291bGQgYmUgYFRSVUVgIG9yIGBGQUxTRWAuIFRoZSBkZWZhdWx0IGZvciBSICh2NC4wKykgaXMgYHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0VgIGFzIFIgd2lsbCBhdXRvbWF0aWNhbGx5IHRyZWF0IGNoYXJhY3RlciBkYXRhIGFzIGNhdGVnb3JpY2FsIGZvciB0aGUgcHVycG9zZXMgb2YgdmlzdWFsaXNhdGlvbi4gTWFueSBzdGF0aXN0aWNhbCBtZXRob2RzIG9yIG9sZGVyIHZlcnNpb25zIG9mIFIsIGhvd2V2ZXIsIHN0aWxsIHJlcXVpcmUgY29udmVyc2lvbiB0byBmYWN0b3JzLiBTZWUgdGhlIGFwcGVuZGl4IGJlbG93IGZvciBtb3JlIGluZm9ybWF0aW9uIG9uIGNyZWF0aW5nIGZhY3RvcnMuCgpXaGVuIGltcG9ydGVkIGludG8gUiwgdGhlIGRhdGEgZnJvbSB0aGUgY3N2IGZpbGUgYXJlIHRyYW5zbGF0ZWQgaW50byBhbiBSIG9iamVjdCBjYWxsZWQgYSAqKmRhdGEgZnJhbWUqKi4gRGF0YSBmcmFtZXMgYXJlIHNpbXBseSB0YWJsZXMsIG9yZ2FuaXNlZCBpbnRvIHJvd3MgYW5kIGNvbHVtbnMuIFRoZSBjb2x1bW5zIGhhdmUgbmFtZXMgdGFrZW4gZnJvbSB0aGUgZmlyc3Qgcm93IG9mIHRoZSBjc3YgZmlsZSwgYW5kIGVhY2ggc3Vic2VxdWVudCByb3cgb2YgdGhlIGNzdiBmaWxlIGJlY29tZXMgYSByb3cgaW4gdGhlIGRhdGEgZnJhbWUuIAoKV2Ugc3RvcmUgdGhlIGRhdGEgZnJhbWUgaW4gYSBuYW1lZCB2YXJpYWJsZSBzbyB0aGF0IHdlIGNhbiByZWZlciB0byBpdCBsYXRlciAoaS5lLiwgcGVyZm9ybSBhbmFseXNlcyBvbiBpdCkuIFdlIHVzZSB0aGUgYXNzaWdubWVudCBvcGVyYXRvciwgYXMgd2UgZGlkIGluIG91ciBwcmV2aW91cyBtb2R1bGUuIAoKQWZ0ZXIgc3RvcmluZyBvdXIgZGF0YSBmcmFtZSBpbnRvIGEgdmFyaWFibGUsIHlvdSBzaG91bGQgYWx3YXlzIGNoZWNrIHRoYXQgdGhlIGRhdGEgaGF2ZSBiZWVuIGltcG9ydGVkIGNvcnJlY3RseS4gRGF0YSBlbnRyeSBlcnJvcnMgY2FuIGNhdXNlIFIgdG8gbWFrZSB0aGUgd3JvbmcgYXNzdW1wdGlvbnMgYWJvdXQgeW91ciBkYXRhLiBJZiB5b3UgaGF2ZSBhIGNvbHVtbiBvZiBudW1iZXJzIHRoYXQgY29udGFpbnMgZXZlbiBvbmUgYWNjaWRlbnRhbCBhbHBoYWJldGljIGNoYXJhY3RlciAodHlwb3MgZG8gaGFwcGVuKSBSIHdpbGwgY29uc2lkZXIgdGhlIHdob2xlIGNvbHVtbiB0byBiZSBzdHJpbmdzLiBMYXRlciwgUiB3aWxsIGdpdmUgdGhlIHdyb25nIHJlc3VsdHMgd2hlbiB5b3UgcGVyZm9ybSBzdGF0aXN0aWNhbCBhbmFseXNlcyBvbiB0aGVzZSBkYXRhIChvciBpdCB3aWxsIHJlZnVzZSB0byBwZXJmb3JtIHRoZW0gYXQgYWxsKS4gCgpVc2UgdGhlIGZvbGxvd2luZyBjb21tYW5kcyB0byBpbnNwZWN0IHlvdXIgaW1wb3J0ZWQgZGF0YToKCmBgYHtyIGNoZWNrIDF9CiMgV3JpdGUgdGhlIGZpcnN0IGZldyBsaW5lcyBvZiBhIGRhdGEgZnJhbWUgdG8gdGhlIGNvbnNvbGUgd2l0aCBmdW5jdGlvbiBoZWFkCmhlYWQoZ2FwbWluZGVyX2RhdGEpCgojIFdyaXRlIHRoZSBsYXN0IGZldyBsaW5lcyBvZiBhIGRhdGEgZnJhbWUgdG8gdGhlIGNvbnNvbGUgd2l0aCBmdW5jdGlvbiB0YWlsCnRhaWwoZ2FwbWluZGVyX2RhdGEpCmBgYAoKYGBge3IgY2hlY2sgMn0KIyBEaXNwbGF5IHRoZSBudW1iZXIgb2YgbGluZXMsIHRoZSBjb2x1bW4gbmFtZXMsIGFuZCB0aGUgZGF0YSB0eXBlIG9mIGVhY2ggCiMgY29sdW1uLCB3aXRoIGZ1bmN0aW9uIHN0ciAoc2hvcnQgZm9yICdzdHJ1Y3R1cmUnKQpzdHIoZ2FwbWluZGVyX2RhdGEpCmBgYAoKRWFjaCBjb2x1bW4gaW4gYSBkYXRhIGZyYW1lIGlzIGFzc29jaWF0ZWQgd2l0aCBhIGRhdGEgdHlwZSAqKmNocioqLCAqKmludCoqLCBvciAqKm51bSoqLiBUaGVzZSBpbmRpY2F0ZSB3aGF0IGtpbmQgb2YgZGF0YSBSIGlkZW50aWZpZWQgaW4gdGhlIGlucHV0IGZpbGUuIENvbHVtbnMgdGhhdCBhcmUgKipjaHIqKiBjb250YWluIHN0cmluZ3MgKGNoYXJhY3RlcnMpLCBjb2x1bW5zIHRoYXQgYXJlICoqaW50KiogY29udGFpbiBpbnRlZ2VycyAod2hvbGUgbnVtYmVycykgYW5kIGNvbHVtbnMgdGhhdCBhcmUgbnVtIGNvbnRhaW4gbnVtYmVycyB3aXRoIGEgZGVjaW1hbCBwYXJ0LiBBbHdheXMgY2hlY2sgdGhhdCB0aGVzZSBwcm9wZXJ0aWVzIG9mIHRoZSBpbXBvcnRlZCBjb2x1bW5zIGFyZSBjb3JyZWN0IGZvciB5b3VyIGRhdGEgc2V0LiBJZiB0aGV5IGFyZSBub3QsIHlvdSBtdXN0IGxvY2F0ZSBhbmQgY29ycmVjdCBhbnkgZXJyb3JzIGluIHlvdXIgY3N2IGZpbGUuCgpZb3UgY2FuIHNlZSB0aGUgc2FtZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc3RydWN0dXJlIG9mIGEgZGF0YSBmcmFtZSBpbiB0aGUgRW52aXJvbm1lbnQgcGFuZWwgb2YgUlN0dWRpbyAodXBwZXItcmlnaHQgb2Ygc2NyZWVuOyBFbnZpcm9ubWVudCB0YWIpLiBXaGVuIHlvdSBzdWNjZXNzZnVsbHkgaW1wb3J0IGEgY3N2IGZpbGUgaW50byBhIHZhcmlhYmxlIHdpdGggYHJlYWQuY3N2YCwgdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lIGFwcGVhcnMgaW4gdGhlIEVudmlyb25tZW50IHBhbmUuIENsaWNrIHRoZSBibHVlIGFycm93IGJlc2lkZSB0aGUgb2JqZWN0IHRvIGRpc3BsYXkgdGhlIGRldGFpbHMgb2YgaXRzIHN0cnVjdHVyZS4KCmBgYHtyIGZpZzAyLCBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aCA9ICI1MCUiLCBmaWcuY2FwID0gIkRhdGEgRnJhbWUgaW4gRW52aXJvbm1lbnQgUGFuZSIsIGZpZy5wb3MgPSAiSCIsIGVjaG8gPSBGQUxTRX0KaW5jbHVkZV9ncmFwaGljcygiaW1hZ2VzLzAyLWRmX2luX2Vudl9wYW5lLnBuZyIpCmBgYAoKXAoKCiMjIFNlbGVjdGluZyBhbmQgdXNpbmcgY29sdW1ucyBvZiBkYXRhCgpJbiBib3RoIG9mIHRoZSBhYm92ZSBkaXNwbGF5cywgZWFjaCBjb2x1bW4gbmFtZSBpcyBwcmVmYWNlZCB3aXRoIGAkYC4gV2UgdXNlIHRoZSBgJGAgb3BlcmF0b3IgdG8gc2VsZWN0IGluZGl2aWR1YWwgY29sdW1ucyBvZiBkYXRhIGZyb20gYSBkYXRhIGZyYW1lLiBGb3IgZXhhbXBsZSwgdG8gc2VsZWN0IGp1c3QgdGhlIGxpZmUgZXhwZWN0YW5jeSB2YWx1ZXMgZnJvbSBvdXIgZGF0YSwgd2Ugc2F5IGBnYXBtaW5kZXJfZGF0YSRsaWZlRXhwYC4gKEJlIHN1cmUgdG8gdHlwZSB0aGUgY29sdW1uIG5hbWUgZXhhY3RseSBhcyBpdCBhcHBlYXJzIGluIHRoZSBvdXRwdXQgb2YgYHN0cmAgYW5kIGluIHRoZSBFbnZpcm9ubWVudCBwYW5lLikgV2UgY2FuIHVzZSB0aGlzIGV4cHJlc3Npb24gZGlyZWN0bHkgYXMgdGhlIGFyZ3VtZW50IHRvIGEgZnVuY3Rpb24sIG9yIHdlIGNhbiBzdG9yZSB0aGUgc2VsZWN0ZWQgY29sdW1uIGluIGEgdmFyaWFibGUgKHdoaWNoIHdpbGwgYmUgYSB2ZWN0b3IpIGZvciBsYXRlciBwcm9jZXNzaW5nLiAKCkZvciBleGFtcGxlOgoKYGBgIHtyIHNpbmdsZSBjb2x1bW4gZXhhbXBsZXN9CiMgUHJpbnQgdGhlIGZpcnN0IGZldyB2YWx1ZXMgb2YgY29sdW1uIHllYXIKaGVhZChnYXBtaW5kZXJfZGF0YSR5ZWFyKQoKIyBTdG9yZSBjb2x1bW4gY29udGluZW50IGluIGEgbmV3IHZhcmlhYmxlCmNvbnRpbmVudHMgPC0gZ2FwbWluZGVyX2RhdGEkY29udGluZW50CmBgYAoKXAoKIyMgQ3JlYXRpbmcgZ3JhcGhzIGluIFIKClRoZXJlIGFyZSBhIHZhcmlldHkgb2YgY29tcGxleCBhbmFseXNlcyB0aGF0IHdlIGNhbiBwZXJmb3JtIG9uIGEgZGF0YSBmcmFtZSB1c2luZyBSJ3MgYnVpbHQtaW4gc3RhdGlzdGljYWwgZnVuY3Rpb25zIGFuZCB0aG9zZSBhdmFpbGFibGUgaW4gYWRkaXRpb25hbCBwYWNrYWdlcyBhbmQgbGlicmFyaWVzLiBXZSB3aWxsIGV4cGxvcmUgbWFueSBvZiB0aGVzZSB0ZWNobmlxdWVzIGluIGxhdGVyIG1vZHVsZXMuIEhvd2V2ZXIsIGFuIGVmZmVjdGl2ZSBmaXJzdCBzdGVwIGluIGdldHRpbmcgdG8ga25vdyBhIGRhdGEgc2V0IGlzIHRvIGdlbmVyYXRlIHBsb3RzIGFuZCBncmFwaHMgdG8gcmVwcmVzZW50IHZpc3VhbGx5IHRoZSBwYXR0ZXJucyBpbiB0aGUgZGF0YS4KClwKCiMjIyBTaW1wbGUgcGxvdHMgLSB0aGUgaGlzdG9ncmFtCgpBIGhpc3RvZ3JhbSBzaG93cyB0aGUgKipmcmVxdWVuY3kgZGlzdHJpYnV0aW9uKiogb2YgYSBkYXRhIHNldC4gVGhhdCBpcywgaXQgc2hvd3MgY291bnRzIG9mIHRoZSAgZGlmZmVyZW50IHZhbHVlcyBvZiB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIChvciByYW5nZXMgb2YgdmFsdWVzLCBmb3IgY29udGludW91cyB2YXJpYWJsZXMpLiBXZSBnZW5lcmF0ZSB0aGlzIGdyYXBoIHdpdGggZnVuY3Rpb24gYGhpc3RgLiBUaGUgZ3JhcGggd2lsbCBiZSBkaXNwbGF5ZWQgaW4gdGhlIFBsb3RzIHRhYiBvZiBSU3R1ZGlvJ3MgbG93ZXItcmlnaHQgcGFuZS4KCmBgYHtyIGhpc3R9CiMgSGlzdG9ncmFtIG9mIGxpZmUgZXhwZWN0YW5jeSB2YWx1ZXMgZnJvbSBnYXBtaW5kZXIKaGlzdChnYXBtaW5kZXJfZGF0YSRsaWZlRXhwKQpgYGAKClwKCldlIHNlZSB0aGF0IHRoZSBkaXN0cmlidXRpb24gb2YgbGlmZSBleHBlY3RhbmN5IGlzIGFwcHJveGltYXRlbHkgYmVsbC1zaGFwZWQsIHdpdGggbWFueSBzY29yZXMgYmV0d2VlbiA2NSBhbmQgODUsIGFuZCBhIHNtYWxsIG51bWJlciBvZiBleHRyZW1lIHZhbHVlcyBncmVhdGVyIHRoYW4gODAgb3IgbGVzcyB0aGFuIDMwLiAKClwKCiMjIyBCb3hwbG90cwpUaGUgYm94cGxvdCBhbGxvd3MgdXMgdG8gY29tcGFyZSBkaXN0cmlidXRpb24gaW5mb3JtYXRpb24gYmV0d2VlbiBncm91cHMuIEZvciBleGFtcGxlLCB3ZSBjYW4gY29tcGFyZSBsaWZlIGV4cGVjdGFuY3kgZm9yIHRoZSBkaWZmZXJlbnQgY29udGluZW50cy4gCgpUaGUgUiBmdW5jdGlvbiBgYm94cGxvdGAgYWNjZXB0cyB0d28gYXJndW1lbnRzLiAKClRoZSBmaXJzdCBhcmd1bWVudCBpcyB0aGUgKipmb3JtdWxhKiouIFRoaXMgaXMgYSBjb21wbGV4LCB5ZXQgdmVyeSBjb21tb24sIGFyZ3VtZW50IGZvcm1hdCBmb3IgUiBzdGF0aXN0aWNhbCBmdW5jdGlvbnMuIFRoZSBmb3JtdWxhIGRlc2NyaWJlcyBhIGxpbmVhciBtb2RlbCBmb3IgYSBkYXRhIHNldCB3aXRoIHRoZSBnZW5lcmFsIHN0cnVjdHVyZTogKipkZXBlbmRlbnQgb3IgcHJlZGljdGVkIHZhcmlhYmxlIH4gaW5kZXBlbmRlbnQgdmFyaWFibGVzIG9yIHByZWRpY3RvcnMqKiwgdXNpbmcgY29sdW1ucyBuYW1lcyBmcm9tIHRoZSBkYXRhIGZyYW1lLiBUaGUgfiAodGlsZGUpIGlzIHJlYWQgYXMgImRlcGVuZHMgb24iIG9yICJpcyBwcmVkaWN0ZWQgYnkiLiBGb3Igb3VyIGV4YW1wbGUsIHdlIGFyZSBpbnRlcmVzdGVkIGluIHRoZSB3YXkgdGhhdCBsaWZlIGV4cGVjdGFuY3kgaXMgZGVwZW5kZW50IG9uIHRoZSBjb250aW5lbnQsIHNvIHdlIHNwZWNpZnkgb3VyIGZvcm11bGEgYXMgKipsaWZlRXhwIH4gY29udGluZW50KiouICBXZSB3aWxsIHNlZSBtb3JlIGNvbXBsZXggZXhhbXBsZXMgb2YgdGhlIGZvcm11bGEgYXJndW1lbnQgbGF0ZXIgaW4gdGhlIHNlbWVzdGVyLgoKVGhlIHNlY29uZCBhcmd1bWVudCB0byBib3hwbG90IGlzIHRoZSBkYXRhIGZyYW1lLgoKYGBge3IgYm94cGxvdDAxfQpib3hwbG90KGxpZmVFeHAgfiBjb250aW5lbnQsIGdhcG1pbmRlcl9kYXRhKQpgYGAKClwKCkJveHBsb3RzIGVmZmljaWVudGx5IGlsbHVzdHJhdGUgYm90aCB0aGUgY2VudHJhbCB0ZW5kZW5jeSBhbmQgdGhlIHZhcmlhYmlsaXR5IG9mIGEgZGF0YSBzZXQuIEVhY2ggZ3JleSBib3ggZXh0ZW5kcyBmcm9tIHRoZSBmaXJzdCBxdWFydGlsZSB0byB0aGUgdGhpcmQgcXVhcnRpbGUgb2YgaXRzIGlucHV0IHZhbHVlcy4gVGhlIGRhcmsgbGluZSBhY3Jvc3MgdGhlIGJveCBpcyBhdCB0aGUgbWVkaWFuLiBUaGUgdHdvIHRoaW4gbGluZXMgb3V0c2lkZSB0aGUgcXJleSBib3ggc2hvdyB0aGUgdmFsdWVzIG9mIHRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIHNjb3JlcywgZXhjbHVkaW5nIGV4dHJlbWUgb3V0bGllcnMuIElmIGV4dHJlbWUgb3V0bGllcnMgYXJlIHByZXNlbnQsIHRoZXkgYXJlIHNob3duIGFzIHNtYWxsIGNpcmNsZXMuIFRoaXMgZmlndXJlIGNsZWFybHkgaWxsdXN0cmF0ZXMgdGhhdCwgaW4gdGhlIGdhcG1pbmRlciBkYXRhLCBsaWZlIGV4cGVjdGFuY3kgLS0gYm90aCBjZW50cmFsIHRlbmRlbmN5IGFuZCB2YXJpYWJsaXR5IC0tIGlzIG5vdCB0aGUgc2FtZSBmb3IgYWxsIGNvbnRpbmVudHMuCgoKXAoKIyMgUGxvdHRpbmcgd2l0aCBnZ3Bsb3QyCgpUaGUgYGhpc3RgIGFuZCBgYm94cGxvdGAgZnVuY3Rpb25zIGFyZSBwYXJ0IG9mIEJhc2UgUi4gVGhleSBhcmUgdXNlZnVsLCBidXQgZm9yIG1vcmUgZWxhYm9yYXRlLCBwdWJsaWNhdGlvbi1xdWFsaXR5IGdyYXBocywgd2UgY2FuIHVzZSB0aGUgdGhpcmQtcGFydHkgbGlicmFyeSAqKmdncGxvdCoqIGNvbnRhaW5lZCBpbiBwYWNrYWdlICoqZ2dwbG90MioqLiBUaGUgZ2dwbG90IGxpYnJhcnkgaXMgYSB2ZXJ5IHBvcHVsYXIgZGF0YSB2aXN1YWxpc2F0aW9uIHRvb2wgYmFzZWQgb24gYW4gZWxhYm9yYXRlIHN5bWJvbGljIHN5c3RlbSBjYWxsZWQgdGhlICdHcmFtbWFyIG9mIEdyYXBoaWNzJy4gCgpUaGUgc3ludGF4IG9mIGdncGxvdCBpcyBjb21wbGV4LCBhbmQgd2Ugd2lsbCBjb25jZW50cmF0ZSBvbiB0aGUgZm91bmRhdGlvbnMgaW4gdGhpcyBtb2R1bGUuIEZvciBhZGRpdGlvbmFsIGRldGFpbCwgc2VlIHRoZSBEYXRhIFZpc3VhbGlzYXRpb24gY2hhcHRlciBpbiB0aGUgUiBmb3IgRGF0YSBTY2llbmNlIG9ubGluZSB0ZXh0LCBhdCAgaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9kYXRhLXZpc3VhbGlzYXRpb24uaHRtbC4KClwKCiMjIyBTZW1hbnRpY3Mgb2YgZ2dwbG90CllvdSBjYW4gdGhpbmsgb2YgYSBnZ3Bsb3QgZ3JhcGggYXMgYmVpbmcgYnVpbHQgYXMgYSBzZXF1ZW5jZSBvZiBsYXllcnMuIE9uIHRoZSBib3R0b20gaXMgdGhlIGJhc2Ugb2YgdGhlIGdyYXBoLCB0aGVuIHRoZSBheGVzIGFuZCB0aGUgZGF0YSBhcmUgbGF5ZXJlZCBvbiwgdGhlbiB0aXRsZXMgYW5kIG5vdGF0aW9ucyBhbmQgb3RoZXIgZmVhdHVyZXMuIEEgZ2dwbG90IGNvbW1hbmQgcmVmbGVjdHMgdGhpcyBsYXllcmVkIHN0cnVjdHVyZS4KClwKCiMjIyBCdWlsZGluZyBhIGdyYXBoCgpUbyB1c2UgdGhlIGdncGxvdCBsaWJyYXJ5LCB3ZSBtdXN0IGluc3RhbGwgdGhlIGdncGxvdDIgcGFja2FnZSAob25jZSBvbiBhIGNvbXB1dGVyKSBhbmQgaW52b2tlIHRoZSBsaWJyYXJ5IGNvbW1hbmQgKGZvciBldmVyeSBSU3R1ZGlvIHNlc3Npb24pLgoKYGBge3IgaW5zdGFsbCBhbmQgbG9hZCwgZXZhbCA9IEZBTFNFfQojIE9uY2Ugb24gYW55IGNvbXB1dGVyCmluc3RhbGwucGFja2FnZXMoZ2dwbG90MikKCiMgT25jZSBmb3IgYW55IFJTdHVkaW8gc2Vzc2lvbgpsaWJyYXJ5KGdncGxvdDIpCmBgYAoKRXZlcnkgZ3JhcGggcmVwcmVzZW50cyBhIGRhdGEgZnJhbWUuIFRoZSBiYXNlIHBhcnQgb2YgYW55IGdncGxvdCBjb21tYW5kIGlzIGEgY2FsbCB0byBmdW5jdGlvbiBgZ2dwbG90KClgIHBhc3NpbmcgaW4gdGhlIGRhdGEgZnJhbWUsIGFzc2lnbmVkIHRvIGZ1bmN0aW9uIGFyZ3VtZW50IGBkYXRhYC4KCmBgYHtyIGdncGxvdF9iYXNlfQojIFRoZSBnZ3Bsb3QgYmFzZSBsYXllcgpnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlcl9kYXRhKQpgYGAKClwKCklmIHlvdSBydW4gdGhpcyBjb21tYW5kIGZyb20gdGhlIFJTdHVkaW8gY29uc29sZSBvciBhbiBSIHNjcmlwdCwgdGhlIGdyZXkgc3F1YXJlIHNob3duIGFib3ZlIGFwcGVhcnMgaW4gdGhlIFBsb3RzIHBhbmUuIFRoaXMgaW5kaWNhdGVzIHRoYXQgZ2dwbG90IGlzIHJlYWR5IHRvIGRyYXcgYSBmaWd1cmUgLS0gdGhpcyBpcyB0aGUgIGJvdHRvbSBsYXllciBvZiBhIGdncGxvdCBncmFwaC4KClRvIGFkZCB4IGFuZCB5IGF4ZXMgdG8gdGhlIGdyYXBoLCB3ZSBuZWVkIHRvIGRlZmluZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gaW5mb3JtYXRpb25hbCBlbGVtZW50cyBpbiB0aGUgZGF0YSBzZXQgKHRoZSB2YXJpYWJsZXMgd2Ugd2FudCB0byBwbG90KSBhbmQgdmlzdWFsIGVsZW1lbnRzIGluIG91ciBncmFwaCAodGhlIGF4ZXMpLiBJbiBnZ3Bsb3QgdGhpcyByZWxhdGlvbnNoaXAgaXMgYSAqKm1hcHBpbmcqKi4gVG8gaW5pdGlhbGlzZSBhIG1hcHBpbmcsIHdlIGlkZW50aWZ5IGEgcGFydGljdWxhciBlbGVtZW50IG9mIHRoZSBncmFwaCAoZS5nLiB0aGUgeC1heGlzKSBhbmQgYXNzaWduIGEgcGFydGljdWxhciBlbGVtZW50IG9mIHRoZSBkYXRhIChlLmcuIGEgY29sdW1uIGluIHRoZSBkYXRhIGZyYW1lKSB0byBpdC4gVGhpcyBhc3NpZ25tZW50IGlzIGNhbGxlZCBhbiAqKmFlc3RoZXRpYyoqIGluIHRoZSBHcmFtbWFyIG9mIEdyYXBoaWNzLCBhbmQgaW4gZ2dwbG90IHdlIHVzZSBmdW5jdGlvbiBgYWVzKClgIHRvIHNwZWNpZnkgYWVzdGhldGljcy4gCgpJbWFnaW5lIHRoYXQgd2Ugd2lzaCB0byBtYWtlIGEgZ3JhcGggc2hvd2luZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gcGVyIGNhcGl0YSBHRFAgYW5kIGxpZmUgZXhwZWN0YW5jeSAodHdvIGNvbHVtbnMgaW4gZ2FwbWluZGVyX2RhdGEpLiBXZSBtYXAgdGhlIGZpcnN0IHZhcmlhYmxlIHRvIHRoZSB4IGF4aXMgKGFyZ3VtZW50IHgpIG9mIG91ciBncmFwaCBhbmQgdGhlIHNlY29uZCB0byB0aGUgeSBheGlzIChhcmd1bWVudCB5KS4gVGhpcyB3aWxsIGFkZCBhIG5ldyBsYXllci4gV2UgYWRkIHRoaXMgbmV3IGluZm9ybWF0aW9uIHRvIHRoZSBnZ3Bsb3QoKSBiYXNlIGNhbGwgYXMgc2hvd24gYmVsb3cuIE5vdGUgdGhhdCB3ZSBkb24ndCBuZWVkIHRvIHVzZSB0aGUgYCRgIG9wZXJhdG9yIGhlcmUsIGFzIGFsbCBjb2x1bW4gbmFtZXMgaW4gYSBnZ3Bsb3QgY29tbWFuZCBhcHBseSB0byB0aGUgc3VwcGxpZWQgZGF0YSBmcmFtZS4KCmBgYHtyIG1hcHBpbmdfeF9hbmRfeX0KZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXJfZGF0YSwgbWFwcGluZyA9IGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gbGlmZUV4cCApKQoKCmBgYAoKXAoKV2UgaGF2ZSBhZGRlZCBhIG5ldyBsYXllciB0byBvdXIgZ3JhcGggd2l0aCBheGVzIGFuZCBncmlkIGxpbmVzLiBOb3RlIHRoYXQgdGhlIGF4ZXMnIHRpYyB2YWx1ZXMgYXJlIGNvcnJlY3RseSBmb3JtYXR0ZWQgZm9yIHRoZSBhc3NvY2lhdGVkIGRhdGEgYW5kIHRoZSBkYXRhIGZyYW1lIGNvbHVtbiBuYW1lcyBhcmUgdXNlZCBhcyB0aGUgYXhpcyBsYWJlbHMgKHdlIHdpbGwgc2VlIGhvdyB0byBpbXByb3ZlIHRob3NlIGxhYmVscyBsYXRlcikuCgpUbyBhZGQgcG9pbnRzIHRvIG91ciBncmFwaCwgd2Ugc3BlY2lmeSBhICoqZ2VvbWV0cnkqKiAoYW5vdGhlciB0ZXJtIGZyb20gdGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MpLiBUaGVyZSBhcmUgbWFueSwgbWFueSBhdmFpbGFibGUgZ2VvbWV0cmllcyBpbiBnZ3Bsb3QsIGNvcnJlc3BvbmRpbmcgdG8gYWxsIHRoZSBkaWZmZXJlbnQgc29ydHMgb2YgZ3JhcGhzIC0tIHNjYXR0ZXJwbG90cywgYmFyIHBsb3RzLCBwaWUgY2hhcnRzLCBsaW5lIGdyYXBocywgZXRjLiAtLSB0aGF0IHlvdSBtaWdodCB3aXNoIHRvIG1ha2UuIEZvciBvdXIgY3VycmVudCBncmFwaCwgd2Ugd2lzaCB0byBwbGFjZSBhIHBvaW50IGF0IHRoZSBpbnRlcnNlY3Rpb24gb2YgcGVyIGNhcGl0YSBHRFAgKG91ciB4IGF4aXMpIGFuZCBsaWZlIGV4cGVjdGFuY3kgKG91ciB5IGF4aXMpIGZvciBlYWNoIHJvdyBpbiB0aGUgaW5wdXQgZGF0YSBmcmFtZS4gVG8gYWRkIHRoaXMgZ2VvbWV0cnkgdG8gZ2dwbG90IGFwcGVuZCBgZ2VvbV9wb2ludCgpYCB0byB5b3VyIGN1cnJlbnQgZ2dwbG90IGNvbW1hbmQgdXNpbmcgdGhlIGArYCBvcGVyYXRvci4gSXQgaXMgY29udmVudGlvbmFsIHRvIHBsYWNlIGVhY2ggY2h1bmsgb2YgdGhlIGdncGxvdCBjb21tYW5kIG9uIGl0cyBvd24gbGluZSBpbiB0aGUgY29kZS4KCmBgYHtyIGdlb21fcG9pbnR9CiMgQWRkIHBvaW50cyAoYSAnZ2VvbWV0cnknKSB0byB0aGUgZ3JhcGgKZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXJfZGF0YSwgbWFwcGluZyA9IGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gbGlmZUV4cCApKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKXAoKVGhpcyB0eXBlIG9mIGdyYXBoICh1c3VhbGx5IGNhbGxlZCBhICoqc2NhdHRlcnBsb3QqKikgaWxsdXN0cmF0ZXMgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHR3byBkZXBlbmRlbnQgdmFyaWFibGVzLiBFdmVuIGZyb20gdGhpcyB2ZXJ5IHNpbXBsZSBmaWd1cmUgd2UgY2FuIHNlZSB0aGF0IHRoZXJlIGlzIGEgZ2VuZXJhbCB0ZW5kZW5jeSBmb3IgaGlnaGVyIHBlciBjYXBpdGEgR0RQIHRvIGJlIGFzc29jaWF0ZWQgd2l0aCBoaWdoZXIgbGlmZSBleHBlY3RhbmN5IGluIHRoZSBnYXBtaW5kZXIgZGF0YS4KCkxpa2UgbW9zdCBmdW5jdGlvbnMsIGBnZW9tX3BvaW50YCBjYW4gYWNjZXB0IGFyZ3VtZW50cyB0aGF0IG1vZGlmeSBpdHMgYmVoYXZpb3VyLiBUaGUgYXJndW1lbnQgKipjb2xvdXIqKiBkZXRlcm1pbmVzIHRoZSBjb2xvdXIgb2YgdGhlIHBvaW50cyB0byBiZSBkcmF3biwgYW5kIGNhbiBiZSBhc3NpZ25lZCBhbnkgb2YgUidzIGJ1aWx0LWluIGNvbG91ciBuYW1lcyAoY2FsbCBmdW5jdGlvbiBgY29sb3VycygpYCB0byBsaXN0IGFsbCBwb3NzaWJsZSB2YWx1ZXMpIG9yIGEgaGV4aWRlY2ltYWwgUkdCIGNvZGUgKHNlZSBmb3IgZXhhbXBsZSwgaHR0cHM6Ly9yLWNoYXJ0cy5jb20vY29sb3JzLykuCgpgYGAge3IgY29sb3VyfQpnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlcl9kYXRhLCBtYXBwaW5nID0gYWVzKHggPSBnZHBQZXJjYXAsIHkgPSBsaWZlRXhwICkpICsKICBnZW9tX3BvaW50KGNvbG91ciA9ICd0b21hdG8nKQpgYGAKClwKClRoaXMgbGl2ZW5zIHVwIG91ciBwbG90LCBidXQgaXQgZG9lc24ndCBhY3V0YWxseSBhZGQgYW55IG5ldyBpbmZvcm1hdGlvbi4gSXQgaXMgYmV0dGVyIHRlY2huaXF1ZSB0byB1c2UgY29sb3VyIHRvIHJlcHJlc2VudCBhbm90aGVyIG9mIG91ciBkYXRhIHZhcmlhYmxlcy4gV2UgbWlnaHQsIGZvciBleGFtcGxlLCB3aXNoIHRvIHVzZSBhIGRpZmZlcmVudCBjb2xvdXIgZm9yIGVhY2ggY29udGluZW50LCB0byBzZWUgaG93IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBHRFAgYW5kIGxpZmUgZXhwZWN0YW5jeSBkaWZmZXJzIGJldHdlZW4gY29udGluZW50cy4gVGhpcyByZXF1aXJlcyBkZWZpbmluZyBhIG1hcHBpbmcgYmV0d2VlbiBhIHZpc3VhbCBmZWF0dXJlIChjb2xvdXIpIGFuZCBhbiBlbGVtZW50IG9mIHRoZSBkYXRhIHNldCAoY29sdW1uIGNvbnRpbmVudCksIHNvIHdlIGluaXRpYWxpc2UgdGhlIGBtYXBwaW5nYCBwcm9wZXJ0eSB3aXRoIGZ1bmN0aW9uIGBhZXNgLCBpbiBvdXIgY2FsbCB0byBgZ2VvbV9wb2ludGAuCgpgYGB7ciBtYXBwaW5nX2NvbG91cn0KZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXJfZGF0YSwgbWFwcGluZyA9IGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gbGlmZUV4cCApKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKGNvbG91ciA9IGNvbnRpbmVudCkpCmBgYAoKXAoKVGhpcyBncmFwaCBpbGx1c3RyYXRlcyBjbGVhcmx5IHRoYXQsIGluIHRoZSBnYXBtaW5kZXIgZGF0YSwgbGlmZSBleHBlY3RhbmN5IGFuZCBwZXIgY2FwaXRhIEdEUCB2YXJ5IHN1YnN0YW50aWFsbHkgYmV0d2VlbiBjb250aW5lbnRzLgoKWW91IHNob3VsZCBjYXJlZnVsbHkgY29tcGFyZSB0aGUgdHdvIHByZWNlZGluZyBncmFwaHMuIEluIHRoZSBmaXJzdCwgd2Ugc2ltcGx5IHNldCB0aGUgKipjb2xvdXIqKiBhcmd1bWVudCBvZiBmdW5jdGlvbmBnZW9tX3BvaW50YC4gSW4gdGhlIHNlY29uZCwgd2Ugc2V0IHRoZSAqKm1hcHBpbmcqKiBhcmd1bWVudCBvZiBgZ2VvbV9wb2ludGAgdXNpbmcgZnVuY3Rpb24gYGFlc2AuIEluIHRoZSBmb3JtZXIgZ3JhcGgsIGFsbCBwb2ludHMgYXJlIHRoZSBzYW1lIGNvbG91ci4gSW4gdGhlIGxhdHRlciBncmFwaCwgdGhlIGNvbG91ciBvZiBlYWNoIHBvaW50IGRlcGVuZHMgb24gaXRzIGNvbnRpbmVudCB2YWx1ZS4gVGhhdCBpcywgd2UgaGF2ZSAqKm1hcHBlZCoqIGNvbG91ciB0byBjb250aW5lbnQuIAoKXAoKIyMjIENob29zaW5nIGdlb21ldHJpZXMKCkl0IGlzIGVzc2VudGlhbCB0byBzZWxlY3QgdGhlIGNvcnJlY3QgdHlwZSBvZiBncmFwaCAodGhlIGNvcnJlY3QgZ2VvbWV0cnkgaW4gZ2dwbG90KSBmb3IgdGhlIGRhdGEgcGF0dGVybiB5b3Ugd2lzaCB0byBpbGx1c3RyYXRlLgoKQXNzdW1lLCBmb3IgZXhhbXBsZSwgdGhhdCB5b3Ugd2lzaCB0byBzaG93IHRoZSBjaGFuZ2UgaW4gbGlmZSBleHBlY3RhbmN5IGFjcm9zcyB5ZWFycywgZm9yIHRoZSBjb3VudHJ5IG9mIERlbm1hcmsuIEZpcnN0LCB3ZSBtdXN0IHNlbGVjdCBvdXQgb25seSB0aGUgcm93cyBmb3IgRGVubWFyayBmcm9tIG91ciBkYXRhIGZyYW1lLiAoV2Ugd2lsbCBjb25zaWRlciBzZWxlY3Rpb24gaW4gZGV0YWlsIGluIG5leHQgd2VlaydzIG1vZHVsZS4gRm9yIG5vdywganVzdCBub3RlIHRoYXQgYmV0d2VlbiB0aGUgc3F1YXJlIGJyYWNrZXRzIHdlIHByb3ZpZGUgcm93IGFuZCBjb2x1bW4gY3JpdGVyaWEgZm9yIHNlbGVjdGlvbiwgYW5kIGFuIGVtcHR5IHZhbHVlIGZvciBjb2x1bW4gbWVhbnMgKiphbGwqKi4pIAoKV2Ugd2lsbCB0aGVuIHBhc3MgdGhlIHNlbGVjdGVkIGRhdGEgdG8gZ2dwbG90IGFzIGJlZm9yZSwgc3BlY2lmeWluZyB0aGUgbWFwcGluZyBvZiB0aGUgZGF0YSB0byB0aGUgeCBhbmQgeSBheGVzLgoKVGhlIGdyYXBoIHdpbGwgYmUgaWxsdXN0cmF0aW5nIGEgdHJlbmQgKGNoYW5nZSBpbiBhIHZhcmlhYmxlIGFjcm9zcyB0aW1lKS4gVHJlbmQgZ3JhcGhzIGFyZSB1c3VhbGx5IGRyYXduIHdpdGggYSBjb250aW51b3VzIGxpbmUgYmV0d2VlbiB0aGUgcGxvdHRlZCBwb2ludHMuIEluIGdncGxvdCwgdGhpcyBpcyBnZW9tZXRyeSBgZ2VvbV9saW5lYC4KClRoZSBjb21wbGV0ZSBjb2RlIGlzOgoKYGBge3IgbGluZV9ncmFwaH0KIyBTZWxlY3QgYWxsIHJvd3Mgd2hlcmUgdGhlIGNvdW50cnkgaXMgZXF1YWwgdG8gRGVubWFyay4gU2VsZWN0IGFsbCBjb2x1bW5zLgpkZW5tYXJrX2RhdGEgPC0gZ2FwbWluZGVyX2RhdGFbZ2FwbWluZGVyX2RhdGEkY291bnRyeSA9PSAiRGVubWFyayIsIF0KCmdncGxvdChkYXRhID0gZGVubWFya19kYXRhLCBtYXBwaW5nID0gYWVzKHggPSB5ZWFyLCB5ID0gbGlmZUV4cCkpICsKICBnZW9tX2xpbmUoKQpgYGAKClwKCldlIGNhbiB1c2UgZ2dwbG90IHRvIHByb2R1Y2UgYSBoaXN0b2dyYW0gZm9yIGxpZmUgZXhwZWN0YW5jeSAoYXMgd2UgZGlkIGluIEJhc2UgUiBhYm92ZSkgd2l0aCBgZ2VvbV9oaXN0b2dyYW1gLiBGb3IgaGlzdG9ncmFtcyB3ZSBvbmx5IG5lZWQgdG8gbWFwIHRoZSB4IGF4aXMsIGFzIHRoZSB5IGF4aXMgcmVwcmVzZW50cywgYnkgZGVmYXVsdCwgZnJlcXVlbmN5LiBXZSBjYW4gZW5oYW5jZSB0aGUgcGxvdCdzIGFwcGVhcmFuY2UgYnkgaW5pdGlhbGlzaW5nIGBnZW9tX2hpc3RvZ3JhbWAgYXJndW1lbnRzIGBjb2xvdXJgIHdoaWNoIHNldHMgdGhlIGJvcmRlciBhcm91bmQgdGhlIGJhcnMgb24gdGhlIGdyYXBoLCBhbmQgYGZpbGxgIHdoaWNoIHNldHMgdGhlIGludGVyaW9yIG9mIHRoZSBiYXJzIG9uIHRoZSBncmFwaC4KCmBgYHtyIGhpc3RvZ3JhbSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPUZBTFNFfQpnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlcl9kYXRhLCBtYXBwaW5nID0gYWVzKHggPSBsaWZlRXhwKSkgKwogIGdlb21faGlzdG9ncmFtKGNvbG91ciA9ICJ3aGl0ZSIsIGZpbGwgPSAiZGFya2dyZWVuIikKYGBgCgpcCgpTaW1pbGFybHksIHdlIGNhbiByZXByb2R1Y2UgdGhlIGJveHBsb3QgYWJvdmUgd2l0aCBgZ2VvbV9ib3hwbG90YC4gSW4gQmFzZSBSIHdlIHVzZWQgYSAqKmZvcm11bGEqKiB0byBpZGVudGlmeSB0aGUgZGVwZW5kZW50IGFuZCBpbmRlcGVuZGVudCB2YXJpYWJsZXMgZm9yIHRoZSBib3hwbG90LiBXaXRoIGdncGxvdCwgd2UgdXNlIGEgbWFwcGluZyB0byBhc3NpZ24gdGhlIERWIHRvIHRoZSB4IGF4aXMgYW5kIHRoZSBJViB0byB0aGUgeSBheGlzLgoKYGBge3IgYm94cGxvdH0KZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXJfZGF0YSwgbWFwcGluZyA9IGFlcyh4ID0gY29udGluZW50LCB5ID0gbGlmZUV4cCkpICsKICBnZW9tX2JveHBsb3QoKQoKCmBgYAoKXAoKIyMjIEV4ZXJjaXNlOgpXaGF0IHdvdWxkIHlvdSBwcmVkaWN0IHRvIGJlIHRoZSBlZmZlY3Qgb2Ygc3dhcHBpbmcgdGhlIHZhbHVlcyBvZiB4IGFuZCB5IGluIHRoZSBjYWxsIHRvIGBhZXNgIGFib3ZlPyBUZXN0IHlvdXIgcHJlZGljdGlvbi4KClwKCiMjIyBSZWZpbmluZyB0aGUgYXBwZWFyYW5jZSBvZiBhIHBsb3QKQWZ0ZXIgd2UgaGF2ZSBidWlsdCB0aGUgZm91bmRhdGlvbiBvZiBvdXIgcGxvdCB3aXRoIGRhdGEgYW5kIGdlb21ldHJ5LCB3ZSBjYW4gYWRkIGZ1cnRoZXIgbGF5ZXJzIHRvIG1vZGlmeSBvdGhlciB2aXN1YWwgZmVhdHVyZXMuIEZvciBleGFtcGxlLCB3ZSBjYW4gdXNlIGZ1bmN0aW9uIGBsYWJzYCB0byBzZXQgdGhlIGF4aXMsIGxlZ2VuZCwgYW5kIG1haW4gdGl0bGVzIG9mIG91ciBwbG90cy4gQ29uc2lkZXIgdGhlIGZvbGxvd2luZyBlbmhhbmNlbWVudHMgdG8gb3VyIGZpZ3VyZSBpbGx1c3RyYXRpbmcgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIEdEUCBhbmQgbGlmZSBleHBlY3RhbmN5IGJ5IGNvbnRpbmVudDoKCmBgYCB7ciBsYWJlbHN9CiMgTkI6IE11bHRpcGxlIGZ1bmN0aW9uIGFyZ3VtZW50cyAoYXMgaW4gbGFicyBiZWxvdykgY2FuCiMgYmUgcGxhY2VkIG9uIHNlcGFyYXRlIGxpbmVzIHRvIGltcHJvdmUgcmVhZGFiaWxpdHkKCmdncGxvdChkYXRhID0gZ2FwbWluZGVyX2RhdGEsIG1hcHBpbmcgPSBhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHAgKSkgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyhjb2xvdXIgPSBjb250aW5lbnQpKSArCiAgbGFicyh4ID0gIkdEUCBQZXIgQ2FwaXRhIiwgCiAgICAgICB5ID0gIkxpZmUgRXhwZWN0YW5jeSIsIAogICAgICAgdGl0bGUgPSAiR2FwIE1pbmRlciBEYXRhIDE5NTIgdG8gMjAwNyIsIAogICAgICAgY29sb3VyID0gIkNvbnRpbmVudCIpCmBgYAoKXAoKClRoZSBjb2RlIGZvciBnZ3Bsb3QgZm9ybWF0dGluZyBjYW4gZ2V0IGV4dHJlbWVseSBjb21wbGV4LCBhbmQgdGhlIGZ1bGwgZnVuY3Rpb25hbGl0eSBpcyBiZXlvbmQgdGhlIHNjb3BlIG9mIHRoaXMgbW9kdWxlLiBJbiBhZGRpdGlvbiwgdGhlcmUgYXJlIG1hbnksIG1hbnkgbW9yZSBnZW9tZXRyaWVzIGF2YWlsYWJsZSwgZWFjaCB3aXRoIGFwcHJvcHJpYXRlIGFyZ3VtZW50cyBhbmQgbWFwcGluZyBvcHRpb25zLgoKVGhlIGZvcm1hbCBkb2N1bWVudGF0aW9uIGZvciBnZ3Bsb3QgY2FuIGJlIGZvdW5kIGF0IGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL2luZGV4Lmh0bWwuIElmIHlvdSBwcmVmZXIgdHV0b3JpYWxzIGFuZCBnYWxsZXJpZXMsIHRoZXJlIGFyZSBtYW55IGF2YWlsYWJsZSBvbmxpbmUuIFR3byBnb29kIHBsYWNlcyB0byBzdGFydCBhcmUgaHR0cDovL3d3dy5jb29rYm9vay1yLmNvbS9HcmFwaHMvIGFuZCBodHRwczovL3d3dy5yLWdyYXBoLWdhbGxlcnkuY29tLy4KClwKCiMjIyBTYXZpbmcgZ2dwbG90cwpZb3UgY2FuIHNhdmUgZmlndXJlcyBtYWRlIHdpdGggZ2dwbG90IHRvIGltYWdlIGZpbGVzLCB3aGljaCBjYW4gdGhlbiBiZSB1c2VkIGluIGRvY3VtZW50cyBnZW5lcmF0ZWQgaW4gTVMgV29yZCBvciBvdGhlciB0ZXh0IGVkaXRvcnMuIFdlIGZpcnN0IHNhdmUgdGhlIG91dHB1dCBvZiBvdXIgZ2dwbG90IGNvbW1hbmQgaW50byBhIG5hbWVkIHZhcmlhYmxlICh0byBSIGEgZ2dwbG90IGlzIGEgZGF0YSBvYmplY3QganVzdCBsaWtlIGEgbnVtYmVyIG9yIGEgc3RyaW5nKS4gV2UgdGhlbiB1c2UgZnVuY3Rpb24gYGdnc2F2ZWAgdG8gZXhwb3J0IG91dCBwbG90IGFzIGFuIGltYWdlIGZpbGUuIFlvdSBzcGVjaWZ5IHRoZSBpbWFnZSBmb3JtYXQgYnkgc3VwcGx5aW5nIGFuIG91dGZpbGUgbmFtZSB3aXRoIHRoZSBjb3JyZXNwb25kaW5nIGZpbGUgc3VmZml4IChlLmcuIC5qcGcgb3IgLnBuZykuIEJ5IGRlZmF1bHQsIHRoZSBmaWxlIGlzIHNhdmVkIGludG8gdGhlIHdvcmtpbmcgZm9sZGVyIChpbiBvdXIgY2FzZSwgdGhlIGZvbGRlciBjb250YWluaW5nIG91ciBjc3YgYW5kIHNjcmlwdCBmaWxlcykuCgpgYGB7ciBnZ3NhdmV9CiMgU2F2ZSBhIGdncGxvdCB0byBhIHZhcmlhYmxlLiBUaGUgc3ludGF4IG9mIHRoZSBncHBsb3QgY29tbWFuZCBpcyB1bmFmZmVjdGVkCmdkcF9saWZlRXhwX3Bsb3QgPC0gZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXJfZGF0YSwgbWFwcGluZyA9IGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gbGlmZUV4cCApKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKGNvbG91ciA9IGNvbnRpbmVudCkpICsKICB4bGFiKCJHRFAgUGVyIENhcGl0YSIpICsKICB5bGFiKCJMaWZlIEV4cGVjdGFuY3kiKSArCiAgZ2d0aXRsZSgiR2FwIE1pbmRlciBEYXRhIDE5NTIgdG8gMjAwNyIpCgojIEV4cG9ydCB0aGUgdmFyaWFibGUgYXMgYW4gaW1hZ2UgZmlsZS4gUHJvdmlkZSB0aGUgZmlsZSBuYW1lIGFuZCB0aGUgZ2dwbG90IG9iamVjdApnZ3NhdmUoZmlsZW5hbWUgPSAiZ2RwX2xpZmVFeHBfcGxvdC5wbmciLCBnZHBfbGlmZUV4cF9wbG90KQpgYGAKClwKClwKCiMjIENvbmNsdXNpb24KClRoaXMgZG9jdW1lbnQgaGFzIHByZXNlbnRlZCBhIHNpbXBsZSBpbnRyb2R1Y3Rpb24gdG8gd29ya2luZyB3aXRoIGNvbXBsZXRlIHRhYmxlcyBvZiBkYXRhIGluIFIuIFdlIGxlYXJuZWQgaG93IHRvIGltcG9ydCBhIGNzdiBmaWxlIGludG8gYSBkYXRhIGZyYW1lLCBhbmQgaG93IHRvIHVzZSBCYXNlIFIgb3IgbGlicmFyeSBgZ2dwbG90YCB0byBnZW5lcmF0ZSBncmFwaHMgdG8gaWxsdXN0cmF0ZSBpbXBvcnRhbnQgcGF0dGVybnMgaW4gb3VyIGRhdGEuCgpcCgojIyMgV2hhdCdzIE5leHQKCkVuc3VyZSB5b3UgaGF2ZSB0aGUgYHRpZHl2ZXJzZWAgaW5zdGFsbGVkIGZvciB0aGUgbmV4dCBtb2R1bGUuIFRoZSB0aWR5dmVyc2UgaXMgYSBjb2xsZWN0aW9uIG9mIHBhY2thZ2VzIChhIG1ldGFwYWNrYWdlKSB0aGF0IHByb3ZpZGUgYSBzdWNjaW5jdCBzeW50YXggZm9yIHBlcmZvcm1pbmcgZGF0YSBtYW5pcHVsYXRpb24gYW5kIGJhc2ljIGFuYWx5c2lzIGluIFIuICBSdW5uaW5nIGBpbnN0YWxsLnBhY2thZ2VzKHRpZHl2ZXJzZSlgIGluc3RhbGxzIGFsbCB0aGUgaW5kaXZpZHVhbCBwYWNrYWdlcyB0byB5b3VyIG1hY2hpbmUuCgpUaGUgdGlkeXZlcnNlIGNvbnNpc3RzIG9mIGBnZ3Bsb3QyYCAocGxvdHRpbmcpLCBgZHBseXJgIChkYXRhIG1hbmlwdWxhdGlvbiksIGB0aWR5cmAgKGRhdGEgdGlkeWluZyksIGByZWFkcmAgKGRhdGEgaW1wb3J0aW5nKSwgYHB1cnJyYCAoZnVuY3Rpb25hbCBwcm9ncmFtbWluZyksIGB0aWJibGVgIChhIHNwZWNpYWwgdHlwZSBvZiBkYXRhIGZyYW1lKSwgYHN0cmluZ3JgIChjb21tb24gdGFza3MgZm9yIHN0cmluZyBtYW5pcHVsYXRpb25zKSwgYW5kICBgZm9yY2F0c2AgKGRlYWxpbmcgd2l0aCBmYWN0b3JzKQogCgpgYGB7ciBpbnN0YWxsIHRpZHl2ZXJzZSwgZXZhbCA9IEZBTFNFfQojIERvd25sb2FkIGFuZCBpbnN0YWxsIHRoZSBwYWNrYWdlcyBvZiB0aWR5dmVyc2UKaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKYGBgCgoKYGBge3J9CiMgbG9hZCB0aGUgdGlkeXZlcnNlIHBhY2thZ2VzIGZvciB0aGUgY3VycmVudCBzZXNzaW9uCmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCgojIyBBcHBlbmRpeCAtIEZhY3RvcnMKCklmIHlvdSBhcmUgZGVhbGluZyB3aXRoIGNhdGVnb3JpY2FsIGRhdGEsIFIgY2FsbHMgdGhlc2UgX0ZhY3RvcnNfLiBQcmlvciB0byBSIHZlcnNpb24gNC4wIHRoZSBkZWZhdWx0IGJlaGF2aW91ciBvZiBgcmVhZC5jc3ZgIHdhcyB0byBoYXZlIHRoZSBwYXJhbWV0ZXIgYHN0cmluZ3NBc0ZhY3RvcnM9VFJVRWAgYXMgdGhlIGRlZmF1bHQgd2hpY2ggd291bGQgcmVhZCBhbGwgY2hhcmFjdGVyIGRhdGEgaW4gYXV0b21hdGljYWxseSBhcyBhIF9mYWN0b3JfLiBGcm9tIHZlcnNpb24gNC4wIG9ud2FyZHMgdGhlIGRlZmF1bHQgaXMgYHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0VgLgoKVG8gY29udmVydCBhIGNvbHVtbiB0byBhIGZhY3RvciB5b3UgY2FuIHVzZSBgYXMuZmFjdG9yYC4gWW91IGNhbiB1c2UgYGxldmVsc2Agb24gYSBfZmFjdG9yXyB0byBzZWUgdGhlIGNhdGVnb3JpZXMuIEl0IGlzIGJlc3QgdG8gd2FpdCB1bnRpbCBhZnRlciBhbnkgZGF0YSB0aWR5aW5nIGlzIHBlcmZvcm1lZCBiZWZvcmUgY29udmVydGluZyB0byBmYWN0b3JzLiBGb3IgaW5zdGFuY2UgaWYgd2UgcmVwZWF0ZWQgdGhlIGNvZGUgZm9yIHB1bGxpbmcgb3V0IHRoZSBjb250aW5lbnRzIGNvbHVtbiBmcm9tIHRoZSBnYXBtaW5kZXIgZGF0YXNldDoKCmBgYHtyfQojIFN0b3JlIGNvbHVtbiBjb250aW5lbnQgaW4gYSBuZXcgdmFyaWFibGUgYWZ0ZXIgY29udmVydGluZyB0byBhIGZhY3Rvcgpjb250aW5lbnRzX2ZhY3RvciA8LSBhcy5mYWN0b3IoZ2FwbWluZGVyX2RhdGEkY29udGluZW50KQoKIyBQYXNzIHRoZSB2YXJpYWJsZSB0byBmdW5jdGlvbiBsZXZlbHMgd2hpY2ggcmV0dXJucyB0aGUgdmFsdWVzCiMgb2YgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZQpsZXZlbHMoY29udGluZW50c19mYWN0b3IpCmBgYA==