Prerequisite: R and R Studio
If you do not already have R and/or R Studio you will need to download and install them. You must first install R from R Project and then the R Studio IDE from R Studio. Alternatively, rather than installing R and R Studio locally, you can do the tutorial using R Studio Cloud.
The process to working with R and R Studio is like programming in Java. You install the JRE and the JDK to get the Java language, compiler, and run-time environment. Now you also want an IDE (Integrated Development Environment) in which you write Java program; for example, Eclipse. Same with Python; you install Python the language and then PyCharm, as an example, as your IDE. Of course, you really do not need R Studio (or any IDE) to write R programs, just like you don’t really need an IDE to program in Java, C, C++, or Python. You need a source code editor and even Notepad or TextEdit would suffice. Some programmers prefer simple IDE’s like Notepad++ or JEdit, while other like full-featured development environments like Visual Studio or Eclipse. The tutorial below assumes you will use R Studio.
Install R from R Project before installing R Studio.
Tutorial
The recorded tutorial Demonstrates how to install R, R Studio, and create projects. Shows how to build R Notebooks using R Markdown and add R code chunks. Explains how to load data into a data frame from a CSV and access the data. It summarizes the content of this tutorial.
The lesson files, including the data files can be found at the end of this page.
Basic R
R is a scripted language which means that you do not need to compile the program before running it. Statements and expressions are executed as you type them if you enter them in the R Console or are run when you execute the code in a chunk in an R Notebook.
Creating an R Notebook
R Code Chunks
This tutorial is limited to writing R “programs” using an R Notebook in R Studio. Programs in R run from start to end. Each chunk should be a step in your analysis or data project. Name your code chunk, so you can quickly navigate to them.
In the chunk below, the variable cars passed to the built-in Base R function plot
is one of the dozens of “built-in” data frames; a data frame being data arranged in rows and columns similar to a spreadsheet or CSV file.
Note that you call a function by using the function’s name followed by the arguments you wish to pass to the function. Of course, you need to follow the definition of the function. Many functions are simply “built-in” while others come from packages that you need to explicitly load into your program.
Note that there is no semicolon at the end of a line.
```{r namedChunk, eval=FALSE}
plot(x = mtcars$mpg, y = mtcars$hp)
```
Expressions
R can be directly used to solve simple or complex mathematical expressions.
# [1] in the above answer indicates the index of your results.
# R always shows the result with index for each row.
((2^3)*5)-1
## [1] 39
# sqrt and exp are built-in functions in R for finding Square root and exponential respectively.
sqrt(4)* exp(2)
## [1] 14.77811
Variables and Identifiers
Holding a value in a variable is done through assignment. Once you assign a value to a variable, the variables becomes an R object. There are two ways to do an assignment, using ‘=’ or with ‘<-’. The latter is the preferred way in R but the former might be more familiar to those programmers coming to R from Java, C++, or Python.
Note that variables are explicitly defined or declared. The first time a variable is assigned a value defines the variable and its type. The type is based on the value that is assigned. Unlike other programming languages such as C++, C#, or Java, R is not strongly typed: the type of a variable can change when a value of a different type is assigned. A variable can be used in an expression. Its value can be inspected by just using the variable by itself.
The value of a variable can be displayed either by using the variable by itself or using the print()
function.
# assignment with '=' of a number
x = 12
# inspect (print/display) the value
x
## [1] 12
# assignment a new value and change its type to "text"
x = "Hello"
x
## [1] "Hello"
# assignment with '<-'
x <- 12
print(x)
## [1] 12
The rules for naming an identifier (variable, function, or package name) for an object are as follows:
identifiers are case-sensitive and cannot contain spaces or special characters such as #, %, $, @, *, &, ^, !, ~ an identifier must start with a letter, but may contain any combination of letters and digits thereafter special characters dot (.) and underscore (_) are allowed
The dot (.) is a regular character in R and that can be confusing as other language (e.g., Java) use dot to designate property or method access, e.g, in Java x.val means that you are accessing the val property of the object x.
Some examples of legal variable names are: df, df2, df.txns, df_all2017. These are some illegal variable names: 2df (cannot start with a digit), rs$all (cannot contain a $; the $ is used to access columns in a dataframe), rs# (only . and _ are allowed in addition to digits and letters).
It is considered good programming practice to give identifiers a sensible name that hints as to what is stored in the variable rather than using random name like x, val, or i33. Identifiers should be named consistently. Many programmers use one of two styles:
- underscores, e.g., interest_rate
- camelCase, e.g., squareRoot, graphData, currentWorkingDirectory
Note that R is case sensitive which means that R treats the identifiers AP and ap as different objects. As a side note, files may also be case sensitive but that depends on the operating system. MacOS and Linux are case sensitive, while Windows is case aware but not case sensitive. For example, on MacOS and Linux there is a difference between “AirPassengers.txt” and “airpassengers.txt” while on Windows there is not. SQL is also not case sensitive. It is a best practice to assume case sensitivity.
Built-in Data Frames
There are numerous data frames built into R that are accessible without loading them first from external files. These data frames are for experimentation and learning and not for actual analytics work. One such built-in data frame is mtcars
. To get a list of all built-in data frames, run data()
.
## mpg cyl disp hp drat wt qsec vs am gear carb
## Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
## Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
## Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
## Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
## Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
## Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
## Duster 360 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4
## Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
## Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
## Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
## Merc 280C 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4
## Merc 450SE 16.4 8 275.8 180 3.07 4.070 17.40 0 0 3 3
## Merc 450SL 17.3 8 275.8 180 3.07 3.730 17.60 0 0 3 3
## Merc 450SLC 15.2 8 275.8 180 3.07 3.780 18.00 0 0 3 3
## Cadillac Fleetwood 10.4 8 472.0 205 2.93 5.250 17.98 0 0 3 4
## Lincoln Continental 10.4 8 460.0 215 3.00 5.424 17.82 0 0 3 4
## Chrysler Imperial 14.7 8 440.0 230 3.23 5.345 17.42 0 0 3 4
## Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
## Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
## Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
## Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
## Dodge Challenger 15.5 8 318.0 150 2.76 3.520 16.87 0 0 3 2
## AMC Javelin 15.2 8 304.0 150 3.15 3.435 17.30 0 0 3 2
## Camaro Z28 13.3 8 350.0 245 3.73 3.840 15.41 0 0 3 4
## Pontiac Firebird 19.2 8 400.0 175 3.08 3.845 17.05 0 0 3 2
## Fiat X1-9 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1
## Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
## Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
## Ford Pantera L 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4
## Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6
## Maserati Bora 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8
## Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2
mtcars
and mtcars
print out the first and last six rows of a data frame, respectively. You can specify the number of rows to display.
## mpg cyl disp hp drat wt qsec vs am gear carb
## Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
## Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
## Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
## Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
## Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
## Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
## mpg cyl disp hp drat wt qsec vs am gear carb
## Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.7 0 1 5 2
## Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.9 1 1 5 2
## Ford Pantera L 15.8 8 351.0 264 4.22 3.170 14.5 0 1 5 4
## Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.5 0 1 5 6
## Maserati Bora 15.0 8 301.0 335 3.54 3.570 14.6 0 1 5 8
## Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.6 1 1 4 2
## mpg cyl disp hp drat wt qsec vs am gear carb
## Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
## Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
## Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
Accessing Rows, Columns, and Elements (Cells) of a Data Frame
Data frames are very similar to tables in relational databases and spreadsheets. They have rows and columns and the intersection of a row and column is a cell (or element). The order of access is row followed by column, e.g, the third element in the fourth row of the data frame mtcars
is mtcars[4,3]
. Note that this is reversed from the way Excel and other spreadsheets work. The <-
is the operator for assignment, although =
also works. We will see and use both.
To display a value, either use the print
function or just use the variable by itself. To print multiple items, use the paste0
function.
v <- mtcars[4,3]
x = mtcars[4,3]
print(paste0("v = ",v," and x = ",x))
## [1] "v = 258 and x = 258"
Leaving out a dimension (row or column) accesses the entire row or column. The resultant is a data frame with a single row. Often the values must be converted to a vector data type. Conversions of variables from one type to another is done with the family of as.xxxx
functions, e.g., as.vector
, as.numeric
, or as.factor
. Vectors can contain numeric or character data but all elements must be of the same type. In R, a list is similar to a vector but it may contain a mix of elements. A matrix is similar to a data frame but it can only contain numbers and it can have more than two dimensions.
Some functions expect data frames, some vectors, some lists. You need to read the documentation of a function to find out. Furthermore, some functions will automatically convert (also called coerce) a variable from one type to the one it requires.
You can also access a column in data frame by its column name. For an entire column you either use the columns position or its name: df[,column]
or df$columnName
.
# all of row 4; the result is a data frame
r <- mtcars[4,]
sum(r)
## [1] 426.135
## [1] 108
mtcars[c(1,4)] # columns 1 and 4 as a new dataframe
## mpg hp
## Mazda RX4 21.0 110
## Mazda RX4 Wag 21.0 110
## Datsun 710 22.8 93
## Hornet 4 Drive 21.4 110
## Hornet Sportabout 18.7 175
## Valiant 18.1 105
## Duster 360 14.3 245
## Merc 240D 24.4 62
## Merc 230 22.8 95
## Merc 280 19.2 123
## Merc 280C 17.8 123
## Merc 450SE 16.4 180
## Merc 450SL 17.3 180
## Merc 450SLC 15.2 180
## Cadillac Fleetwood 10.4 205
## Lincoln Continental 10.4 215
## Chrysler Imperial 14.7 230
## Fiat 128 32.4 66
## Honda Civic 30.4 52
## Toyota Corolla 33.9 65
## Toyota Corona 21.5 97
## Dodge Challenger 15.5 150
## AMC Javelin 15.2 150
## Camaro Z28 13.3 245
## Pontiac Firebird 19.2 175
## Fiat X1-9 27.3 66
## Porsche 914-2 26.0 91
## Lotus Europa 30.4 113
## Ford Pantera L 15.8 264
## Ferrari Dino 19.7 175
## Maserati Bora 15.0 335
## Volvo 142E 21.4 109
mtcars[,2] # all of column 2
## [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4
mtcars[5:7,] # rows 5 to 7 as a new dataframe
## mpg cyl disp hp drat wt qsec vs am gear carb
## Hornet Sportabout 18.7 8 360 175 3.15 3.44 17.02 0 0 3 2
## Valiant 18.1 6 225 105 2.76 3.46 20.22 1 0 3 1
## Duster 360 14.3 8 360 245 3.21 3.57 15.84 0 0 3 4
mtcars$cyl # column named "cyl"
## [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4
mtcars$cyl[2] # 2nd row in the column "cyl"
## [1] 6
mtcars$cyl[3:9] # rows 3 to 9 for column "cyl" as a vector
## [1] 4 6 8 6 8 4 4
## [1] 20.09062
Aggregation and Statistical Functions
As a language with its origin in statistics and statistical data processing, R has a plethora of statistical functions. Some of the most important functions for data processing are shown below. Consult online documentation and statistics references for more information, e.g., How To Get Descriptive Statistics In R and Base R Statistical Functions.
Packages
There are thousands of functions across hundreds of packages (external libraries of functions written for specific purposes, e.g., data mining, statistical inference, machine learning, image processing, web development, visualization, XML processing, SQL, and so forth). You will learn them over time – and it’s unlikely you will ever learn all of them, so have patience. For a package to be usable in an R project it must be installed; installation is done once. Then every time you need an installed package in some R code, you must load it using the library
function.
Installing Packages
To ensure that packages are automatically installed, you can use the following code. That way your code becomes portable.
if("RSQLite" %in% rownames(installed.packages()) == FALSE) {
install.packages("RSQLite")
}
library("RSQLite")
In the above code the function installed.packages()
returns a list of the names of all installed packages. The operator %in%
is a set operator that checks if “RSQLite” is one of the returned names. If it is, the Boolean expression evaluates to \(TRUE\), otherwise \(FALSE\). If it is false, then it means the package is not installed and the optional code that installs the package is executed. That way, the loading of the package with library("RSQLite")
cannot fail.
Data Frame Dimensions and Structure
Data frames are one of the most fundamental data structures of R, along with vectors. A data frame is a row/column arrangement of data where each column is a vector of data values of the same type, e.g., all numbers or all characters.
The example below uses one of the many built-in data frames of R: mtcars. These built-in data frames are wonderful for testing code or learning R.
nrow(mtcars) # number of rows in the data frame
## [1] 32
ncol(mtcars) # number of columns on the data frame
## [1] 11
str(mtcars) # structure of the data frame
## 'data.frame': 32 obs. of 11 variables:
## $ mpg : num 21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
## $ cyl : num 6 6 4 6 8 6 8 4 4 6 ...
## $ disp: num 160 160 108 258 360 ...
## $ hp : num 110 110 93 110 175 105 245 62 95 123 ...
## $ drat: num 3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
## $ wt : num 2.62 2.88 2.32 3.21 3.44 ...
## $ qsec: num 16.5 17 18.6 19.4 17 ...
## $ vs : num 0 0 1 1 0 1 0 1 1 1 ...
## $ am : num 1 1 1 0 0 0 0 0 0 0 ...
## $ gear: num 4 4 4 3 3 3 3 4 4 4 ...
## $ carb: num 4 4 1 1 2 1 4 2 2 4 ...
mtcars[nrow(mtcars),] # last row only of a data frame
## mpg cyl disp hp drat wt qsec vs am gear carb
## Volvo 142E 21.4 4 121 109 4.11 2.78 18.6 1 1 4 2
Adding and Removing Columns from a Data Frame
To add a new column, you simply “access” the column or use a new name for the column. Note in the example below that you can operate on entire columns (as vectors) and the operation is applied to each pair of values in the two vectors in the operation. This is much more efficient than using loops as is necessary in other programming languages.
# copy the data frame mtcars to a new data frame df
df <- mtcars
# create a new column "dispcyl" which is the displacement per cylinder
df$dispcyl <- df$disp / df$cyl
head(df)
## mpg cyl disp hp drat wt qsec vs am gear carb dispcyl
## Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4 26.66667
## Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4 26.66667
## Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1 27.00000
## Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 43.00000
## Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 45.00000
## Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1 37.50000
Create a New Data Frame
Data frames are created in various ways: use the <code<>data.frame function, load a CSV file, execute a SQL query, or as a result of many package functions.
Load a Data Frame from CSV
Quick note: Capitalization in path and file names does not matter in Windows, but does matter on MacOS and Linux. Furthermore, note that even in Windows the path delimiter is a forward slash / and not the usual backwards slash \. The \ is an “escape” character and used to inject non-printable characters into a string (text), e.g., “This string contains”quotes”.” which would be written in R as “this string contains \”quotes\“.”
Also, the parameters header = F
instructs read.csv
not to interpret the first line as header labels. Of course, if there are no labels, then you need to define your own.
Aside from CSV files, R can also load a number of other file format using various packages, including XML, Excel, SPSS, MatLab, among many others.
df <- read.csv(file = "customertxndata.csv", header = F)
head(df)
df <- read.csv(file = "customertxndata.csv",
header = F,
col.names = c("numVisits","NumTxn","OS","Gender","TotSp"))
head(df)
Note that the value of the ‘Male’ column in the first row is NA which is the way that R indicates a missing data value. It is not 0 or an empty string, it is unknown. So, statistical functions and algebraic operations would result in an NA as well.
Strings vs Factors
The factor data type encodes categorical data, e.g., the value of a variable is one of a fixed value set. Many statistical functions in R require categorical variables to be of type factor. However, often, during data processing, we need the actual text rather than having it encoded as a factor (which is actually stored in R as an integer for efficiency). So, when reading a CSV file you need to decide if you want text columns to be character strings or factors by setting the stringsAsFactors
parameter.
You may use either F
and T
or FALSE
and TRUE
.
df <- read.csv(file = "customertxndata.csv",
header = F,
stringsAsFactors = FALSE,
col.names = c("numVisits","NumTxn","OS","Gender","TotSp"))
head(df)
Create a New Data Frame
The code below creates a new data frame from column vectors. Notice how the column names are the names of the vectors. A new vector is created with the c
function, e.g., v <- c(3,5,1,9)
.
df1 <- data.frame(state = c('Arizona','Georgia', 'New York','Indiana','Washington','Texas'),
code = as.factor(c('AZ','GA','NY','IN','WA','TX')),
score = c(62,47,55,74,31,85))
head(df1)
## state code score
## 1 Arizona AZ 62
## 2 Georgia GA 47
## 3 New York NY 55
## 4 Indiana IN 74
## 5 Washington WA 31
## 6 Texas TX 85
Search Data Frames
There are two important functions for “searching” data frames: which
and any
. The code below uses the built-in Orange data frame which contains measurements of orange trees. It has three columns: the tree, the age of the tree (days since 1968/12/31), and circumference (in mm).
which
## Tree age circumference
## 1 1 118 30
## 2 1 484 58
## 3 1 664 87
## 4 1 1004 115
## 5 1 1231 120
## 6 1 1372 142
# find all rows where the circumference is more than 200mm
rs <- which(df$circumference > 200)
# display all rows where the circumference is more than 200mm
df[rs,]
## Tree age circumference
## 13 2 1372 203
## 14 2 1582 203
## 27 4 1372 209
## 28 4 1582 214
# compound conditions are possible with & (and), | (or), and ! (not)
rs2 <- which(df$circumference > 200 & df$age < 1500)
rs3 <- which(df$circumference < 200 | !(df$age < 1500))
rs4 <- which(df$circumference > 400 | df$age > 1500)
rs2
## [1] 13 27
## [1] 1 2 3 4 5 6 7 8 9 10 11 12 14 15 16 17 18 19 20 21 22 23 24 25 26
## [26] 28 29 30 31 32 33 34 35
## [1] 7 14 21 28 35
## [1] 1582
## [1] 894.8788
In the above example rs <- which(df$circumference > 200)
finds all rows in the data frame df where circumference > 200. The rows are saved in rs.
References
No references.
LS0tCnRpdGxlOiAiQmVnaW5uaW5nIFIiCnBhcmFtczoKICBjYXRlZ29yeTogNgogIG51bWJlcjogMTAwCiAgdGltZTogNjAKICBsZXZlbDogYmVnaW5uZXIKICB0YWdzOiAicixwcmltZXIiCiAgZGVzY3JpcHRpb246ICJJbnRyb2R1Y2VzIHNvbWUgYmFzaWMgY29uY2VwdCBvZiBSLCBpbmNsdWRpbmcgc3RhdGVtZW50cywgZGF0YQogICAgICAgICAgICAgICAgZnJhbWVzLCB2ZWN0b3JzLCB2YXJpYWJsZXMsIGFuZCByZWFkaW5nIGZyb20gYSBDU1YgZmlsZS4iCmRhdGU6ICI8c21hbGw+YHIgU3lzLkRhdGUoKWA8L3NtYWxsPiIKYXV0aG9yOiAiPHNtYWxsPk1hcnRpbiBTY2hlZGxiYXVlcjwvc21hbGw+IgplbWFpbDogIm0uc2NoZWRsYmF1ZXJAbmV1LmVkdSIKYWZmaWxpdGF0aW9uOiAiTm9ydGhlYXN0ZXJuIFVuaXZlcnNpdHkiCm91dHB1dDogCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRoZW1lOiBzcGFjZWxhYgogICAgaGlnaGxpZ2h0OiB0YW5nbwotLS0KCi0tLQp0aXRsZTogIjxzbWFsbD5gciBwYXJhbXMkY2F0ZWdvcnlgLmByIHBhcmFtcyRudW1iZXJgPC9zbWFsbD48YnIvPjxzcGFuIHN0eWxlPSdjb2xvcjogIzJFNDA1MzsgZm9udC1zaXplOiAwLjllbSc+YHIgcm1hcmtkb3duOjptZXRhZGF0YSR0aXRsZWA8L3NwYW4+IgotLS0KCmBgYHtyIGNvZGU9eGZ1bjo6cmVhZF91dGY4KHBhc3RlMChoZXJlOjpoZXJlKCksJy9SL19pbnNlcnQyREIuUicpKSwgaW5jbHVkZSA9IEZBTFNFfQpgYGAKCiMjIFByZXJlcXVpc2l0ZTogUiBhbmQgUiBTdHVkaW8KCklmIHlvdSBkbyBub3QgYWxyZWFkeSBoYXZlIFIgYW5kL29yIFIgU3R1ZGlvIHlvdSB3aWxsIG5lZWQgdG8gZG93bmxvYWQgYW5kIGluc3RhbGwgdGhlbS4gWW91IG11c3QgZmlyc3QgaW5zdGFsbCBSIGZyb20gW1IgUHJvamVjdF0oaHR0cHM6Ly9jbG91ZC5yLXByb2plY3Qub3JnLykgYW5kIHRoZW4gdGhlIFIgU3R1ZGlvIElERSBmcm9tIFtSIFN0dWRpb10oaHR0cHM6Ly9yc3R1ZGlvLmNvbS9wcm9kdWN0cy9yc3R1ZGlvL2Rvd25sb2FkLykuIEFsdGVybmF0aXZlbHksIHJhdGhlciB0aGFuIGluc3RhbGxpbmcgUiBhbmQgUiBTdHVkaW8gbG9jYWxseSwgeW91IGNhbiBkbyB0aGUgdHV0b3JpYWwgdXNpbmcgW1IgU3R1ZGlvIENsb3VkXShodHRwczovL3JzdHVkaW8uY2xvdWQvKS4KClRoZSBwcm9jZXNzIHRvIHdvcmtpbmcgd2l0aCBSIGFuZCBSIFN0dWRpbyBpcyBsaWtlIHByb2dyYW1taW5nIGluIEphdmEuIFlvdSBpbnN0YWxsIHRoZSBKUkUgYW5kIHRoZSBKREsgdG8gZ2V0IHRoZSBKYXZhIGxhbmd1YWdlLCBjb21waWxlciwgYW5kIHJ1bi10aW1lIGVudmlyb25tZW50LiBOb3cgeW91IGFsc28gd2FudCBhbiBJREUgKEludGVncmF0ZWQgRGV2ZWxvcG1lbnQgRW52aXJvbm1lbnQpIGluIHdoaWNoIHlvdSB3cml0ZSBKYXZhIHByb2dyYW07IGZvciBleGFtcGxlLCAqRWNsaXBzZS4qIFNhbWUgd2l0aCBQeXRob247IHlvdSBpbnN0YWxsIFB5dGhvbiB0aGUgbGFuZ3VhZ2UgYW5kIHRoZW4gKlB5Q2hhcm0qLCBhcyBhbiBleGFtcGxlLCBhcyB5b3VyIElERS4gT2YgY291cnNlLCB5b3UgcmVhbGx5IGRvIG5vdCBuZWVkIFIgU3R1ZGlvIChvciBhbnkgSURFKSB0byB3cml0ZSBSIHByb2dyYW1zLCBqdXN0IGxpa2UgeW91IGRvbid0IHJlYWxseSBuZWVkIGFuIElERSB0byBwcm9ncmFtIGluIEphdmEsIEMsIEMrKywgb3IgUHl0aG9uLiBZb3UgbmVlZCBhIHNvdXJjZSBjb2RlIGVkaXRvciBhbmQgZXZlbiBOb3RlcGFkIG9yIFRleHRFZGl0IHdvdWxkIHN1ZmZpY2UuIFNvbWUgcHJvZ3JhbW1lcnMgcHJlZmVyIHNpbXBsZSBJREUncyBsaWtlIE5vdGVwYWQrKyBvciBKRWRpdCwgd2hpbGUgb3RoZXIgbGlrZSBmdWxsLWZlYXR1cmVkIGRldmVsb3BtZW50IGVudmlyb25tZW50cyBsaWtlIFZpc3VhbCBTdHVkaW8gb3IgRWNsaXBzZS4gVGhlIHR1dG9yaWFsIGJlbG93IGFzc3VtZXMgeW91IHdpbGwgdXNlIFIgU3R1ZGlvLgoKPiBJbnN0YWxsIFIgZnJvbSBbUiBQcm9qZWN0XShodHRwczovL2Nsb3VkLnItcHJvamVjdC5vcmcvKSAqKmJlZm9yZSoqIGluc3RhbGxpbmcgW1IgU3R1ZGlvXShodHRwczovL3JzdHVkaW8uY29tL3Byb2R1Y3RzL3JzdHVkaW8vZG93bmxvYWQvKS4KCiMjIFR1dG9yaWFsCgpUaGUgcmVjb3JkZWQgdHV0b3JpYWwgRGVtb25zdHJhdGVzIGhvdyB0byBpbnN0YWxsIFIsIFIgU3R1ZGlvLCBhbmQgY3JlYXRlIHByb2plY3RzLiBTaG93cyBob3cgdG8gYnVpbGQgUiBOb3RlYm9va3MgdXNpbmcgUiBNYXJrZG93biBhbmQgYWRkIFIgY29kZSBjaHVua3MuIEV4cGxhaW5zIGhvdyB0byBsb2FkIGRhdGEgaW50byBhIGRhdGEgZnJhbWUgZnJvbSBhIENTViBhbmQgYWNjZXNzIHRoZSBkYXRhLiBJdCBzdW1tYXJpemVzIHRoZSBjb250ZW50IG9mIHRoaXMgdHV0b3JpYWwuCgpUaGUgbGVzc29uIGZpbGVzLCBpbmNsdWRpbmcgdGhlIGRhdGEgZmlsZXMgY2FuIGJlIGZvdW5kIGF0IHRoZSBlbmQgb2YgdGhpcyBwYWdlLgoKYGBgez1odG1sfQo8aWZyYW1lIHNyYz0iaHR0cHM6Ly9wbGF5ZXIudmltZW8uY29tL3ZpZGVvLzY2OTc5Njk0ND9oPWM1NmY2ZmVlMzkiIHdpZHRoPSI0ODAiIGhlaWdodD0iMjcwIiBmcmFtZWJvcmRlcj0iMSIgYWxsb3c9ImF1dG9wbGF5OyBmdWxsc2NyZWVuOyBwaWN0dXJlLWluLXBpY3R1cmUiIGFsbG93ZnVsbHNjcmVlbiBkYXRhLWV4dGVybmFsPSIxIj48L2lmcmFtZT4KYGBgCgojIyBCYXNpYyBSCgpSIGlzIGEgc2NyaXB0ZWQgbGFuZ3VhZ2Ugd2hpY2ggbWVhbnMgdGhhdCB5b3UgZG8gbm90IG5lZWQgdG8gY29tcGlsZSB0aGUgcHJvZ3JhbSBiZWZvcmUgcnVubmluZyBpdC4gU3RhdGVtZW50cyBhbmQgZXhwcmVzc2lvbnMgYXJlIGV4ZWN1dGVkIGFzIHlvdSB0eXBlIHRoZW0gaWYgeW91IGVudGVyIHRoZW0gaW4gdGhlIFIgQ29uc29sZSBvciBhcmUgcnVuIHdoZW4geW91IGV4ZWN1dGUgdGhlIGNvZGUgaW4gYSBjaHVuayBpbiBhbiBSIE5vdGVib29rLgoKIyMgQ3JlYXRpbmcgYW4gUiBOb3RlYm9vawoKIVtdKHItcHJvamVjdC1wb3NpdC5qcGcpCgojIyBSIENvZGUgQ2h1bmtzCgpUaGlzIHR1dG9yaWFsIGlzIGxpbWl0ZWQgdG8gd3JpdGluZyBSICJwcm9ncmFtcyIgdXNpbmcgYW4gUiBOb3RlYm9vayBpbiBSIFN0dWRpby4gUHJvZ3JhbXMgaW4gUiBydW4gZnJvbSBzdGFydCB0byBlbmQuIEVhY2ggY2h1bmsgc2hvdWxkIGJlIGEgc3RlcCBpbiB5b3VyIGFuYWx5c2lzIG9yIGRhdGEgcHJvamVjdC4gTmFtZSB5b3VyIGNvZGUgY2h1bmssIHNvIHlvdSBjYW4gcXVpY2tseSBuYXZpZ2F0ZSB0byB0aGVtLgoKSW4gdGhlIGNodW5rIGJlbG93LCB0aGUgdmFyaWFibGUgKmNhcnMqIHBhc3NlZCB0byB0aGUgYnVpbHQtaW4gQmFzZSBSIGZ1bmN0aW9uIDxjb2RlPnBsb3Q8L2NvZGU+IGlzIG9uZSBvZiB0aGUgZG96ZW5zIG9mICJidWlsdC1pbiIgZGF0YSBmcmFtZXM7IGEgZGF0YSBmcmFtZSBiZWluZyBkYXRhIGFycmFuZ2VkIGluIHJvd3MgYW5kIGNvbHVtbnMgc2ltaWxhciB0byBhIHNwcmVhZHNoZWV0IG9yIENTViBmaWxlLgoKTm90ZSB0aGF0IHlvdSBjYWxsIGEgZnVuY3Rpb24gYnkgdXNpbmcgdGhlIGZ1bmN0aW9uJ3MgbmFtZSBmb2xsb3dlZCBieSB0aGUgYXJndW1lbnRzIHlvdSB3aXNoIHRvIHBhc3MgdG8gdGhlIGZ1bmN0aW9uLiBPZiBjb3Vyc2UsIHlvdSBuZWVkIHRvIGZvbGxvdyB0aGUgZGVmaW5pdGlvbiBvZiB0aGUgZnVuY3Rpb24uIE1hbnkgZnVuY3Rpb25zIGFyZSBzaW1wbHkgImJ1aWx0LWluIiB3aGlsZSBvdGhlcnMgY29tZSBmcm9tIHBhY2thZ2VzIHRoYXQgeW91IG5lZWQgdG8gZXhwbGljaXRseSBsb2FkIGludG8geW91ciBwcm9ncmFtLgoKTm90ZSB0aGF0IHRoZXJlIGlzIG5vIHNlbWljb2xvbiBhdCB0aGUgZW5kIG9mIGEgbGluZS4KCjxjb2RlPlxgXGBcYHtyIG5hbWVkQ2h1bmssIGV2YWw9RkFMU0V9PGJyLz4gcGxvdCh4ID0gbXRjYXJzXCRtcGcsIHkgPSBtdGNhcnNcJGhwKTxici8+IFxgXGBcYCA8L2NvZGU+CgojIyMgRXhwcmVzc2lvbnMKClIgY2FuIGJlIGRpcmVjdGx5IHVzZWQgdG8gc29sdmUgc2ltcGxlIG9yIGNvbXBsZXggbWF0aGVtYXRpY2FsIGV4cHJlc3Npb25zLgoKYGBge3J9CiMgWzFdIGluIHRoZSBhYm92ZSBhbnN3ZXIgaW5kaWNhdGVzIHRoZSBpbmRleCBvZiB5b3VyIHJlc3VsdHMuCiMgUiBhbHdheXMgc2hvd3MgdGhlIHJlc3VsdCB3aXRoIGluZGV4IGZvciBlYWNoIHJvdy4KCigoMl4zKSo1KS0xCmBgYAoKYGBge3J9CiMgc3FydCBhbmQgZXhwIGFyZSBidWlsdC1pbiBmdW5jdGlvbnMgaW4gUiBmb3IgZmluZGluZyBTcXVhcmUgcm9vdCBhbmQgZXhwb25lbnRpYWwgcmVzcGVjdGl2ZWx5LgoKc3FydCg0KSogZXhwKDIpCmBgYAoKIyMjIFZhcmlhYmxlcyBhbmQgSWRlbnRpZmllcnMKCkhvbGRpbmcgYSB2YWx1ZSBpbiBhIHZhcmlhYmxlIGlzIGRvbmUgdGhyb3VnaCBhc3NpZ25tZW50LiBPbmNlIHlvdSBhc3NpZ24gYSB2YWx1ZSB0byBhIHZhcmlhYmxlLCB0aGUgdmFyaWFibGVzIGJlY29tZXMgYW4gUiBvYmplY3QuIFRoZXJlIGFyZSB0d28gd2F5cyB0byBkbyBhbiBhc3NpZ25tZW50LCB1c2luZyAnPScgb3Igd2l0aCAnXDwtJy4gVGhlIGxhdHRlciBpcyB0aGUgcHJlZmVycmVkIHdheSBpbiBSIGJ1dCB0aGUgZm9ybWVyIG1pZ2h0IGJlIG1vcmUgZmFtaWxpYXIgdG8gdGhvc2UgcHJvZ3JhbW1lcnMgY29taW5nIHRvIFIgZnJvbSBKYXZhLCBDKyssIG9yIFB5dGhvbi4KCk5vdGUgdGhhdCB2YXJpYWJsZXMgYXJlIGV4cGxpY2l0bHkgZGVmaW5lZCBvciBkZWNsYXJlZC4gVGhlIGZpcnN0IHRpbWUgYSB2YXJpYWJsZSBpcyBhc3NpZ25lZCBhIHZhbHVlIGRlZmluZXMgdGhlIHZhcmlhYmxlIGFuZCBpdHMgdHlwZS4gVGhlIHR5cGUgaXMgYmFzZWQgb24gdGhlIHZhbHVlIHRoYXQgaXMgYXNzaWduZWQuIFVubGlrZSBvdGhlciBwcm9ncmFtbWluZyBsYW5ndWFnZXMgc3VjaCBhcyBDKyssIEMjLCBvciBKYXZhLCBSIGlzIG5vdCBzdHJvbmdseSB0eXBlZDogdGhlIHR5cGUgb2YgYSB2YXJpYWJsZSBjYW4gY2hhbmdlIHdoZW4gYSB2YWx1ZSBvZiBhIGRpZmZlcmVudCB0eXBlIGlzIGFzc2lnbmVkLiBBIHZhcmlhYmxlIGNhbiBiZSB1c2VkIGluIGFuIGV4cHJlc3Npb24uIEl0cyB2YWx1ZSBjYW4gYmUgaW5zcGVjdGVkIGJ5IGp1c3QgdXNpbmcgdGhlIHZhcmlhYmxlIGJ5IGl0c2VsZi4KClRoZSB2YWx1ZSBvZiBhIHZhcmlhYmxlIGNhbiBiZSBkaXNwbGF5ZWQgZWl0aGVyIGJ5IHVzaW5nIHRoZSB2YXJpYWJsZSBieSBpdHNlbGYgb3IgdXNpbmcgdGhlIDxjb2RlPnByaW50KCk8L2NvZGU+IGZ1bmN0aW9uLgoKYGBge3J9CiMgYXNzaWdubWVudCB3aXRoICc9JyBvZiBhIG51bWJlcgp4ID0gMTIKIyBpbnNwZWN0IChwcmludC9kaXNwbGF5KSB0aGUgdmFsdWUKeAoKIyBhc3NpZ25tZW50IGEgbmV3IHZhbHVlIGFuZCBjaGFuZ2UgaXRzIHR5cGUgdG8gInRleHQiCnggPSAiSGVsbG8iCngKCiMgYXNzaWdubWVudCB3aXRoICc8LScKeCA8LSAxMgpwcmludCh4KQpgYGAKClRoZSBydWxlcyBmb3IgbmFtaW5nIGFuIGlkZW50aWZpZXIgKHZhcmlhYmxlLCBmdW5jdGlvbiwgb3IgcGFja2FnZSBuYW1lKSBmb3IgYW4gb2JqZWN0IGFyZSBhcyBmb2xsb3dzOgoKaWRlbnRpZmllcnMgYXJlIGNhc2Utc2Vuc2l0aXZlIGFuZCBjYW5ub3QgY29udGFpbiBzcGFjZXMgb3Igc3BlY2lhbCBjaGFyYWN0ZXJzIHN1Y2ggYXMgIywgJSwgXCQsIFxALCBcKiwgJiwgXF4sICEsIFx+IGFuIGlkZW50aWZpZXIgbXVzdCBzdGFydCB3aXRoIGEgbGV0dGVyLCBidXQgbWF5IGNvbnRhaW4gYW55IGNvbWJpbmF0aW9uIG9mIGxldHRlcnMgYW5kIGRpZ2l0cyB0aGVyZWFmdGVyIHNwZWNpYWwgY2hhcmFjdGVycyBkb3QgKC4pIGFuZCB1bmRlcnNjb3JlIChcXykgYXJlIGFsbG93ZWQKClRoZSBkb3QgKC4pIGlzIGEgcmVndWxhciBjaGFyYWN0ZXIgaW4gUiBhbmQgdGhhdCBjYW4gYmUgY29uZnVzaW5nIGFzIG90aGVyIGxhbmd1YWdlICgqZS5nLiosIEphdmEpIHVzZSBkb3QgdG8gZGVzaWduYXRlIHByb3BlcnR5IG9yIG1ldGhvZCBhY2Nlc3MsICplLmcqLCBpbiBKYXZhICp4LnZhbCogbWVhbnMgdGhhdCB5b3UgYXJlIGFjY2Vzc2luZyB0aGUgKnZhbCogcHJvcGVydHkgb2YgdGhlIG9iamVjdCAqeCouCgpTb21lIGV4YW1wbGVzIG9mIGxlZ2FsIHZhcmlhYmxlIG5hbWVzIGFyZTogZGYsIGRmMiwgZGYudHhucywgZGZfYWxsMjAxNy4gVGhlc2UgYXJlIHNvbWUgaWxsZWdhbCB2YXJpYWJsZSBuYW1lczogKjJkZiogKGNhbm5vdCBzdGFydCB3aXRoIGEgZGlnaXQpLCAqcnNcJGFsbCogKGNhbm5vdCBjb250YWluIGEgXCQ7IHRoZSBcJCBpcyB1c2VkIHRvIGFjY2VzcyBjb2x1bW5zIGluIGEgZGF0YWZyYW1lKSwgKnJzIyogKG9ubHkgLiBhbmQgXF8gYXJlIGFsbG93ZWQgaW4gYWRkaXRpb24gdG8gZGlnaXRzIGFuZCBsZXR0ZXJzKS4KCkl0IGlzIGNvbnNpZGVyZWQgZ29vZCBwcm9ncmFtbWluZyBwcmFjdGljZSB0byBnaXZlIGlkZW50aWZpZXJzIGEgc2Vuc2libGUgbmFtZSB0aGF0IGhpbnRzIGFzIHRvIHdoYXQgaXMgc3RvcmVkIGluIHRoZSB2YXJpYWJsZSByYXRoZXIgdGhhbiB1c2luZyByYW5kb20gbmFtZSBsaWtlIHgsIHZhbCwgb3IgaTMzLiBJZGVudGlmaWVycyBzaG91bGQgYmUgbmFtZWQgY29uc2lzdGVudGx5LiBNYW55IHByb2dyYW1tZXJzIHVzZSBvbmUgb2YgdHdvIHN0eWxlczoKCi0gICB1bmRlcnNjb3JlcywgKmUuZy4qLCAqaW50ZXJlc3RfcmF0ZSoKLSAgIGNhbWVsQ2FzZSwgKmUuZy4qLCAqc3F1YXJlUm9vdCosICpncmFwaERhdGEqLCAqY3VycmVudFdvcmtpbmdEaXJlY3RvcnkqCgpOb3RlIHRoYXQgUiBpcyBjYXNlIHNlbnNpdGl2ZSB3aGljaCBtZWFucyB0aGF0IFIgdHJlYXRzIHRoZSBpZGVudGlmaWVycyAqQVAqIGFuZCAqYXAqIGFzIGRpZmZlcmVudCBvYmplY3RzLiBBcyBhIHNpZGUgbm90ZSwgZmlsZXMgbWF5IGFsc28gYmUgY2FzZSBzZW5zaXRpdmUgYnV0IHRoYXQgZGVwZW5kcyBvbiB0aGUgb3BlcmF0aW5nIHN5c3RlbS4gTWFjT1MgYW5kIExpbnV4IGFyZSBjYXNlIHNlbnNpdGl2ZSwgd2hpbGUgV2luZG93cyBpcyBjYXNlIGF3YXJlIGJ1dCBub3QgY2FzZSBzZW5zaXRpdmUuIEZvciBleGFtcGxlLCBvbiBNYWNPUyBhbmQgTGludXggdGhlcmUgaXMgYSBkaWZmZXJlbmNlIGJldHdlZW4gIkFpclBhc3NlbmdlcnMudHh0IiBhbmQgImFpcnBhc3NlbmdlcnMudHh0IiB3aGlsZSBvbiBXaW5kb3dzIHRoZXJlIGlzIG5vdC4gU1FMIGlzIGFsc28gbm90IGNhc2Ugc2Vuc2l0aXZlLiBJdCBpcyBhIGJlc3QgcHJhY3RpY2UgdG8gYXNzdW1lIGNhc2Ugc2Vuc2l0aXZpdHkuCgojIyBCdWlsdC1pbiBEYXRhIEZyYW1lcwoKVGhlcmUgYXJlIG51bWVyb3VzIGRhdGEgZnJhbWVzIGJ1aWx0IGludG8gUiB0aGF0IGFyZSBhY2Nlc3NpYmxlIHdpdGhvdXQgbG9hZGluZyB0aGVtIGZpcnN0IGZyb20gZXh0ZXJuYWwgZmlsZXMuIFRoZXNlIGRhdGEgZnJhbWVzIGFyZSBmb3IgZXhwZXJpbWVudGF0aW9uIGFuZCBsZWFybmluZyBhbmQgbm90IGZvciBhY3R1YWwgYW5hbHl0aWNzIHdvcmsuIE9uZSBzdWNoIGJ1aWx0LWluIGRhdGEgZnJhbWUgaXMgPGNvZGU+bXRjYXJzPC9jb2RlPi4gVG8gZ2V0IGEgbGlzdCBvZiBhbGwgYnVpbHQtaW4gZGF0YSBmcmFtZXMsIHJ1biA8Y29kZT5kYXRhKCk8L2NvZGU+LgoKYGBge3J9Cm10Y2FycwpgYGAKCjxjb2RlPm10Y2FyczwvY29kZT4gYW5kIDxjb2RlPm10Y2FyczwvY29kZT4gcHJpbnQgb3V0IHRoZSBmaXJzdCBhbmQgbGFzdCBzaXggcm93cyBvZiBhIGRhdGEgZnJhbWUsIHJlc3BlY3RpdmVseS4gWW91IGNhbiBzcGVjaWZ5IHRoZSBudW1iZXIgb2Ygcm93cyB0byBkaXNwbGF5LgoKYGBge3J9CmhlYWQobXRjYXJzKQp0YWlsKG10Y2FycykKCmhlYWQobXRjYXJzLCAzKQpgYGAKCiMjIEFjY2Vzc2luZyBSb3dzLCBDb2x1bW5zLCBhbmQgRWxlbWVudHMgKENlbGxzKSBvZiBhIERhdGEgRnJhbWUKCkRhdGEgZnJhbWVzIGFyZSB2ZXJ5IHNpbWlsYXIgdG8gdGFibGVzIGluIHJlbGF0aW9uYWwgZGF0YWJhc2VzIGFuZCBzcHJlYWRzaGVldHMuIFRoZXkgaGF2ZSByb3dzIGFuZCBjb2x1bW5zIGFuZCB0aGUgaW50ZXJzZWN0aW9uIG9mIGEgcm93IGFuZCBjb2x1bW4gaXMgYSBjZWxsIChvciBlbGVtZW50KS4gVGhlIG9yZGVyIG9mIGFjY2VzcyBpcyByb3cgZm9sbG93ZWQgYnkgY29sdW1uLCBlLmcsIHRoZSB0aGlyZCBlbGVtZW50IGluIHRoZSBmb3VydGggcm93IG9mIHRoZSBkYXRhIGZyYW1lIDxjb2RlPm10Y2FyczwvY29kZT4gaXMgPGNvZGU+bXRjYXJzWzQsM108L2NvZGU+LiBOb3RlIHRoYXQgdGhpcyBpcyByZXZlcnNlZCBmcm9tIHRoZSB3YXkgRXhjZWwgYW5kIG90aGVyIHNwcmVhZHNoZWV0cyB3b3JrLiBUaGUgPGNvZGU+XDwtPC9jb2RlPiBpcyB0aGUgb3BlcmF0b3IgZm9yIGFzc2lnbm1lbnQsIGFsdGhvdWdoIDxjb2RlPj08L2NvZGU+IGFsc28gd29ya3MuIFdlIHdpbGwgc2VlIGFuZCB1c2UgYm90aC4KClRvIGRpc3BsYXkgYSB2YWx1ZSwgZWl0aGVyIHVzZSB0aGUgPGNvZGU+cHJpbnQ8L2NvZGU+IGZ1bmN0aW9uIG9yIGp1c3QgdXNlIHRoZSB2YXJpYWJsZSBieSBpdHNlbGYuIFRvIHByaW50IG11bHRpcGxlIGl0ZW1zLCB1c2UgdGhlIDxjb2RlPnBhc3RlMDwvY29kZT4gZnVuY3Rpb24uCgpgYGB7cn0KdiA8LSBtdGNhcnNbNCwzXQp4ID0gbXRjYXJzWzQsM10KCnByaW50KHBhc3RlMCgidiA9ICIsdiwiIGFuZCB4ID0gIix4KSkKYGBgCgpMZWF2aW5nIG91dCBhIGRpbWVuc2lvbiAocm93IG9yIGNvbHVtbikgYWNjZXNzZXMgdGhlIGVudGlyZSByb3cgb3IgY29sdW1uLiBUaGUgcmVzdWx0YW50IGlzIGEgZGF0YSBmcmFtZSB3aXRoIGEgc2luZ2xlIHJvdy4gT2Z0ZW4gdGhlIHZhbHVlcyBtdXN0IGJlIGNvbnZlcnRlZCB0byBhIHZlY3RvciBkYXRhIHR5cGUuIENvbnZlcnNpb25zIG9mIHZhcmlhYmxlcyBmcm9tIG9uZSB0eXBlIHRvIGFub3RoZXIgaXMgZG9uZSB3aXRoIHRoZSBmYW1pbHkgb2YgPGNvZGU+YXMueHh4eDwvY29kZT4gZnVuY3Rpb25zLCAqZS5nLiosIDxjb2RlPmFzLnZlY3RvcjwvY29kZT4sIDxjb2RlPmFzLm51bWVyaWM8L2NvZGU+LCBvciA8Y29kZT5hcy5mYWN0b3I8L2NvZGU+LiBWZWN0b3JzIGNhbiBjb250YWluIG51bWVyaWMgb3IgY2hhcmFjdGVyIGRhdGEgYnV0IGFsbCBlbGVtZW50cyBtdXN0IGJlIG9mIHRoZSBzYW1lIHR5cGUuIEluIFIsIGEgbGlzdCBpcyBzaW1pbGFyIHRvIGEgdmVjdG9yIGJ1dCBpdCBtYXkgY29udGFpbiBhIG1peCBvZiBlbGVtZW50cy4gQSBtYXRyaXggaXMgc2ltaWxhciB0byBhIGRhdGEgZnJhbWUgYnV0IGl0IGNhbiBvbmx5IGNvbnRhaW4gbnVtYmVycyBhbmQgaXQgY2FuIGhhdmUgbW9yZSB0aGFuIHR3byBkaW1lbnNpb25zLgoKU29tZSBmdW5jdGlvbnMgZXhwZWN0IGRhdGEgZnJhbWVzLCBzb21lIHZlY3RvcnMsIHNvbWUgbGlzdHMuIFlvdSBuZWVkIHRvIHJlYWQgdGhlIGRvY3VtZW50YXRpb24gb2YgYSBmdW5jdGlvbiB0byBmaW5kIG91dC4gRnVydGhlcm1vcmUsIHNvbWUgZnVuY3Rpb25zIHdpbGwgYXV0b21hdGljYWxseSBjb252ZXJ0IChhbHNvIGNhbGxlZCBjb2VyY2UpIGEgdmFyaWFibGUgZnJvbSBvbmUgdHlwZSB0byB0aGUgb25lIGl0IHJlcXVpcmVzLgoKWW91IGNhbiBhbHNvIGFjY2VzcyBhIGNvbHVtbiBpbiBkYXRhIGZyYW1lIGJ5IGl0cyBjb2x1bW4gbmFtZS4gRm9yIGFuIGVudGlyZSBjb2x1bW4geW91IGVpdGhlciB1c2UgdGhlIGNvbHVtbnMgcG9zaXRpb24gb3IgaXRzIG5hbWU6IDxjb2RlPmRmWyxjb2x1bW5dPC9jb2RlPiBvciA8Y29kZT5kZlwkY29sdW1uTmFtZTwvY29kZT4uCgpgYGB7cn0KIyBhbGwgb2Ygcm93IDQ7IHRoZSByZXN1bHQgaXMgYSBkYXRhIGZyYW1lCnIgPC0gbXRjYXJzWzQsXQpzdW0ocikKCmMgPC0gbXRjYXJzWzMsXQpjWzEsM10KCm10Y2Fyc1tjKDEsNCldICAgIyBjb2x1bW5zIDEgYW5kIDQgYXMgYSBuZXcgZGF0YWZyYW1lCgptdGNhcnNbLDJdICAgICAgICMgYWxsIG9mIGNvbHVtbiAyCm10Y2Fyc1s1OjcsXSAgICAgIyByb3dzIDUgdG8gNyBhcyBhIG5ldyBkYXRhZnJhbWUKbXRjYXJzJGN5bCAgICAgICAjIGNvbHVtbiBuYW1lZCAiY3lsIgptdGNhcnMkY3lsWzJdICAgICMgMm5kIHJvdyBpbiB0aGUgY29sdW1uICJjeWwiCgptdGNhcnMkY3lsWzM6OV0gICMgcm93cyAzIHRvIDkgZm9yIGNvbHVtbiAiY3lsIiBhcyBhIHZlY3RvcgoKdyA8LSBtdGNhcnMkbXBnCm1lYW4odykKYGBgCgojIyBBZ2dyZWdhdGlvbiBhbmQgU3RhdGlzdGljYWwgRnVuY3Rpb25zCgpBcyBhIGxhbmd1YWdlIHdpdGggaXRzIG9yaWdpbiBpbiBzdGF0aXN0aWNzIGFuZCBzdGF0aXN0aWNhbCBkYXRhIHByb2Nlc3NpbmcsIFIgaGFzIGEgcGxldGhvcmEgb2Ygc3RhdGlzdGljYWwgZnVuY3Rpb25zLiBTb21lIG9mIHRoZSBtb3N0IGltcG9ydGFudCBmdW5jdGlvbnMgZm9yIGRhdGEgcHJvY2Vzc2luZyBhcmUgc2hvd24gYmVsb3cuIENvbnN1bHQgb25saW5lIGRvY3VtZW50YXRpb24gYW5kIHN0YXRpc3RpY3MgcmVmZXJlbmNlcyBmb3IgbW9yZSBpbmZvcm1hdGlvbiwgKmUuZy4qLCBbSG93IFRvIEdldCBEZXNjcmlwdGl2ZSBTdGF0aXN0aWNzIEluIFJdKGh0dHBzOi8vd3d3LnByb2dyYW1taW5nci5jb20vc3RhdGlzdGljcy9kZXNjcmlwdGl2ZS1zdGF0aXN0aWNzLWluLXIvIzp+OnRleHQ9VGhlJTIwc3VtbWFyeSUyMGZ1bmN0aW9uJTIwaW4lMjBSJTIwaXMlMjBvbmUlMjBvZixzdWNoJTIwYXMlMjByYW5nZSUyQyUyMG1lYW4lMkMlMjBtZWRpYW4lMjBhbmQlMjBpbnRlcnBlcmNlbnRpbGUlMjByYW5nZXMpIGFuZCBbQmFzZSBSIFN0YXRpc3RpY2FsIEZ1bmN0aW9uc10oaHR0cHM6Ly93d3cuZHVtbWllcy5jb20vZWR1Y2F0aW9uL21hdGgvc3RhdGlzdGljcy9iYXNlLXItc3RhdGlzdGljYWwtZnVuY3Rpb25zLykuCgojIyBQYWNrYWdlcwoKVGhlcmUgYXJlIHRob3VzYW5kcyBvZiBmdW5jdGlvbnMgYWNyb3NzIGh1bmRyZWRzIG9mIHBhY2thZ2VzIChleHRlcm5hbCBsaWJyYXJpZXMgb2YgZnVuY3Rpb25zIHdyaXR0ZW4gZm9yIHNwZWNpZmljIHB1cnBvc2VzLCAqZS5nLiosIGRhdGEgbWluaW5nLCBzdGF0aXN0aWNhbCBpbmZlcmVuY2UsIG1hY2hpbmUgbGVhcm5pbmcsIGltYWdlIHByb2Nlc3NpbmcsIHdlYiBkZXZlbG9wbWVudCwgdmlzdWFsaXphdGlvbiwgWE1MIHByb2Nlc3NpbmcsIFNRTCwgYW5kIHNvIGZvcnRoKS4gWW91IHdpbGwgbGVhcm4gdGhlbSBvdmVyIHRpbWUgLS0gYW5kIGl0J3MgdW5saWtlbHkgeW91IHdpbGwgZXZlciBsZWFybiBhbGwgb2YgdGhlbSwgc28gaGF2ZSBwYXRpZW5jZS4gRm9yIGEgcGFja2FnZSB0byBiZSB1c2FibGUgaW4gYW4gUiBwcm9qZWN0IGl0IG11c3QgYmUgaW5zdGFsbGVkOyBpbnN0YWxsYXRpb24gaXMgZG9uZSBvbmNlLiBUaGVuIGV2ZXJ5IHRpbWUgeW91IG5lZWQgYW4gaW5zdGFsbGVkIHBhY2thZ2UgaW4gc29tZSBSIGNvZGUsIHlvdSBtdXN0IGxvYWQgaXQgdXNpbmcgdGhlIDxjb2RlPmxpYnJhcnk8L2NvZGU+IGZ1bmN0aW9uLgoKIyMjIEluc3RhbGxpbmcgUGFja2FnZXMKClRvIGVuc3VyZSB0aGF0IHBhY2thZ2VzIGFyZSBhdXRvbWF0aWNhbGx5IGluc3RhbGxlZCwgeW91IGNhbiB1c2UgdGhlIGZvbGxvd2luZyBjb2RlLiBUaGF0IHdheSB5b3VyIGNvZGUgYmVjb21lcyBwb3J0YWJsZS4KCmBgYHtyfQppZigiUlNRTGl0ZSIgJWluJSByb3duYW1lcyhpbnN0YWxsZWQucGFja2FnZXMoKSkgPT0gRkFMU0UpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJSU1FMaXRlIikKfQoKbGlicmFyeSgiUlNRTGl0ZSIpCmBgYAoKSW4gdGhlIGFib3ZlIGNvZGUgdGhlIGZ1bmN0aW9uIGBpbnN0YWxsZWQucGFja2FnZXMoKWAgcmV0dXJucyBhIGxpc3Qgb2YgdGhlIG5hbWVzIG9mIGFsbCBpbnN0YWxsZWQgcGFja2FnZXMuIFRoZSBvcGVyYXRvciBgJWluJWAgaXMgYSBzZXQgb3BlcmF0b3IgdGhhdCBjaGVja3MgaWYgKiJSU1FMaXRlIiogaXMgb25lIG9mIHRoZSByZXR1cm5lZCBuYW1lcy4gSWYgaXQgaXMsIHRoZSBCb29sZWFuIGV4cHJlc3Npb24gZXZhbHVhdGVzIHRvICRUUlVFJCwgb3RoZXJ3aXNlICRGQUxTRSQuIElmIGl0IGlzIGZhbHNlLCB0aGVuIGl0IG1lYW5zIHRoZSBwYWNrYWdlIGlzIG5vdCBpbnN0YWxsZWQgYW5kIHRoZSBvcHRpb25hbCBjb2RlIHRoYXQgaW5zdGFsbHMgdGhlIHBhY2thZ2UgaXMgZXhlY3V0ZWQuIFRoYXQgd2F5LCB0aGUgbG9hZGluZyBvZiB0aGUgcGFja2FnZSB3aXRoIGBsaWJyYXJ5KCJSU1FMaXRlIilgIGNhbm5vdCBmYWlsLgoKIyMgRGF0YSBGcmFtZSBEaW1lbnNpb25zIGFuZCBTdHJ1Y3R1cmUKCkRhdGEgZnJhbWVzIGFyZSBvbmUgb2YgdGhlIG1vc3QgZnVuZGFtZW50YWwgZGF0YSBzdHJ1Y3R1cmVzIG9mIFIsIGFsb25nIHdpdGggdmVjdG9ycy4gQSBkYXRhIGZyYW1lIGlzIGEgcm93L2NvbHVtbiBhcnJhbmdlbWVudCBvZiBkYXRhIHdoZXJlIGVhY2ggY29sdW1uIGlzIGEgdmVjdG9yIG9mIGRhdGEgdmFsdWVzIG9mIHRoZSBzYW1lIHR5cGUsICplLmcuKiwgYWxsIG51bWJlcnMgb3IgYWxsIGNoYXJhY3RlcnMuCgpUaGUgZXhhbXBsZSBiZWxvdyB1c2VzIG9uZSBvZiB0aGUgbWFueSBidWlsdC1pbiBkYXRhIGZyYW1lcyBvZiBSOiAqbXRjYXJzKi4gVGhlc2UgYnVpbHQtaW4gZGF0YSBmcmFtZXMgYXJlIHdvbmRlcmZ1bCBmb3IgdGVzdGluZyBjb2RlIG9yIGxlYXJuaW5nIFIuCgpgYGB7cn0KbnJvdyhtdGNhcnMpICAgICAgICAgICAgIyBudW1iZXIgb2Ygcm93cyBpbiB0aGUgZGF0YSBmcmFtZQpuY29sKG10Y2FycykgICAgICAgICAgICAjIG51bWJlciBvZiBjb2x1bW5zIG9uIHRoZSBkYXRhIGZyYW1lCgpzdHIobXRjYXJzKSAgICAgICAgICAgICAjIHN0cnVjdHVyZSBvZiB0aGUgZGF0YSBmcmFtZQoKbXRjYXJzW25yb3cobXRjYXJzKSxdICAgIyBsYXN0IHJvdyBvbmx5IG9mIGEgZGF0YSBmcmFtZSAgIApgYGAKCiMjIEFkZGluZyBhbmQgUmVtb3ZpbmcgQ29sdW1ucyBmcm9tIGEgRGF0YSBGcmFtZQoKVG8gYWRkIGEgbmV3IGNvbHVtbiwgeW91IHNpbXBseSAiYWNjZXNzIiB0aGUgY29sdW1uIG9yIHVzZSBhIG5ldyBuYW1lIGZvciB0aGUgY29sdW1uLiBOb3RlIGluIHRoZSBleGFtcGxlIGJlbG93IHRoYXQgeW91IGNhbiBvcGVyYXRlIG9uIGVudGlyZSBjb2x1bW5zIChhcyB2ZWN0b3JzKSBhbmQgdGhlIG9wZXJhdGlvbiBpcyBhcHBsaWVkIHRvIGVhY2ggcGFpciBvZiB2YWx1ZXMgaW4gdGhlIHR3byB2ZWN0b3JzIGluIHRoZSBvcGVyYXRpb24uIFRoaXMgaXMgbXVjaCBtb3JlIGVmZmljaWVudCB0aGFuIHVzaW5nIGxvb3BzIGFzIGlzIG5lY2Vzc2FyeSBpbiBvdGhlciBwcm9ncmFtbWluZyBsYW5ndWFnZXMuCgpgYGB7cn0KIyBjb3B5IHRoZSBkYXRhIGZyYW1lIG10Y2FycyB0byBhIG5ldyBkYXRhIGZyYW1lIGRmCmRmIDwtIG10Y2FycwoKIyBjcmVhdGUgYSBuZXcgY29sdW1uICJkaXNwY3lsIiB3aGljaCBpcyB0aGUgZGlzcGxhY2VtZW50IHBlciBjeWxpbmRlcgpkZiRkaXNwY3lsIDwtIGRmJGRpc3AgLyBkZiRjeWwKCmhlYWQoZGYpCmBgYAoKIyMgQ3JlYXRlIGEgTmV3IERhdGEgRnJhbWUKCkRhdGEgZnJhbWVzIGFyZSBjcmVhdGVkIGluIHZhcmlvdXMgd2F5czogdXNlIHRoZSBcPGNvZGVcPFw+ZGF0YS5mcmFtZTwvY29kZT4gZnVuY3Rpb24sIGxvYWQgYSBDU1YgZmlsZSwgZXhlY3V0ZSBhIFNRTCBxdWVyeSwgb3IgYXMgYSByZXN1bHQgb2YgbWFueSBwYWNrYWdlIGZ1bmN0aW9ucy4KCiMjIyBMb2FkIGEgRGF0YSBGcmFtZSBmcm9tIENTVgoKUXVpY2sgbm90ZTogQ2FwaXRhbGl6YXRpb24gaW4gcGF0aCBhbmQgZmlsZSBuYW1lcyBkb2VzIG5vdCBtYXR0ZXIgaW4gV2luZG93cywgYnV0ICoqZG9lcyBtYXR0ZXIqKiBvbiBNYWNPUyBhbmQgTGludXguIEZ1cnRoZXJtb3JlLCBub3RlIHRoYXQgZXZlbiBpbiBXaW5kb3dzIHRoZSBwYXRoIGRlbGltaXRlciBpcyBhIGZvcndhcmQgc2xhc2ggLyBhbmQgbm90IHRoZSB1c3VhbCBiYWNrd2FyZHMgc2xhc2ggXFwuIFRoZSBcXCBpcyBhbiAiZXNjYXBlIiBjaGFyYWN0ZXIgYW5kIHVzZWQgdG8gaW5qZWN0IG5vbi1wcmludGFibGUgY2hhcmFjdGVycyBpbnRvIGEgc3RyaW5nICh0ZXh0KSwgKmUuZy4qLCAiVGhpcyBzdHJpbmcgY29udGFpbnMgInF1b3RlcyIuIiB3aGljaCB3b3VsZCBiZSB3cml0dGVuIGluIFIgYXMgInRoaXMgc3RyaW5nIGNvbnRhaW5zIFxcInF1b3Rlc1xcIi4iCgpBbHNvLCB0aGUgcGFyYW1ldGVycyA8Y29kZT5oZWFkZXIgPSBGPC9jb2RlPiBpbnN0cnVjdHMgPGNvZGU+cmVhZC5jc3Y8L2NvZGU+IG5vdCB0byBpbnRlcnByZXQgdGhlIGZpcnN0IGxpbmUgYXMgaGVhZGVyIGxhYmVscy4gT2YgY291cnNlLCBpZiB0aGVyZSBhcmUgbm8gbGFiZWxzLCB0aGVuIHlvdSBuZWVkIHRvIGRlZmluZSB5b3VyIG93bi4KCkFzaWRlIGZyb20gQ1NWIGZpbGVzLCBSIGNhbiBhbHNvIGxvYWQgYSBudW1iZXIgb2Ygb3RoZXIgZmlsZSBmb3JtYXQgdXNpbmcgdmFyaW91cyBwYWNrYWdlcywgaW5jbHVkaW5nIFhNTCwgRXhjZWwsIFNQU1MsIE1hdExhYiwgYW1vbmcgbWFueSBvdGhlcnMuCgpgYGB7ciBldmFsPUZBTFNFfQpkZiA8LSByZWFkLmNzdihmaWxlID0gImN1c3RvbWVydHhuZGF0YS5jc3YiLCBoZWFkZXIgPSBGKQpoZWFkKGRmKQoKZGYgPC0gcmVhZC5jc3YoZmlsZSA9ICJjdXN0b21lcnR4bmRhdGEuY3N2IiwgCiAgICAgICAgICAgICAgIGhlYWRlciA9IEYsCiAgICAgICAgICAgICAgIGNvbC5uYW1lcyA9IGMoIm51bVZpc2l0cyIsIk51bVR4biIsIk9TIiwiR2VuZGVyIiwiVG90U3AiKSkKaGVhZChkZikKYGBgCgo+IE5vdGUgdGhhdCB0aGUgdmFsdWUgb2YgdGhlICdNYWxlJyBjb2x1bW4gaW4gdGhlIGZpcnN0IHJvdyBpcyAqTkEqIHdoaWNoIGlzIHRoZSB3YXkgdGhhdCBSIGluZGljYXRlcyBhIG1pc3NpbmcgZGF0YSB2YWx1ZS4gSXQgaXMgbm90IDAgb3IgYW4gZW1wdHkgc3RyaW5nLCBpdCBpcyB1bmtub3duLiBTbywgc3RhdGlzdGljYWwgZnVuY3Rpb25zIGFuZCBhbGdlYnJhaWMgb3BlcmF0aW9ucyB3b3VsZCByZXN1bHQgaW4gYW4gKk5BKiBhcyB3ZWxsLgoKIyMjIyBTdHJpbmdzIHZzIEZhY3RvcnMKClRoZSAqZmFjdG9yKiBkYXRhIHR5cGUgZW5jb2RlcyBjYXRlZ29yaWNhbCBkYXRhLCAqZS5nLiosIHRoZSB2YWx1ZSBvZiBhIHZhcmlhYmxlIGlzIG9uZSBvZiBhIGZpeGVkIHZhbHVlIHNldC4gTWFueSBzdGF0aXN0aWNhbCBmdW5jdGlvbnMgaW4gUiByZXF1aXJlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyB0byBiZSBvZiB0eXBlICpmYWN0b3IqLiBIb3dldmVyLCBvZnRlbiwgZHVyaW5nIGRhdGEgcHJvY2Vzc2luZywgd2UgbmVlZCB0aGUgYWN0dWFsIHRleHQgcmF0aGVyIHRoYW4gaGF2aW5nIGl0IGVuY29kZWQgYXMgYSAqZmFjdG9yKiAod2hpY2ggaXMgYWN0dWFsbHkgc3RvcmVkIGluIFIgYXMgYW4gaW50ZWdlciBmb3IgZWZmaWNpZW5jeSkuIFNvLCB3aGVuIHJlYWRpbmcgYSBDU1YgZmlsZSB5b3UgbmVlZCB0byBkZWNpZGUgaWYgeW91IHdhbnQgdGV4dCBjb2x1bW5zIHRvIGJlIGNoYXJhY3RlciBzdHJpbmdzIG9yIGZhY3RvcnMgYnkgc2V0dGluZyB0aGUgPGNvZGU+c3RyaW5nc0FzRmFjdG9yczwvY29kZT4gcGFyYW1ldGVyLgoKWW91IG1heSB1c2UgZWl0aGVyIDxjb2RlPkY8L2NvZGU+IGFuZCA8Y29kZT5UPC9jb2RlPiBvciA8Y29kZT5GQUxTRTwvY29kZT4gYW5kIDxjb2RlPlRSVUU8L2NvZGU+LgoKYGBge3IgZXZhbD1GQUxTRX0KZGYgPC0gcmVhZC5jc3YoZmlsZSA9ICJjdXN0b21lcnR4bmRhdGEuY3N2IiwgCiAgICAgICAgICAgICAgIGhlYWRlciA9IEYsCiAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgY29sLm5hbWVzID0gYygibnVtVmlzaXRzIiwiTnVtVHhuIiwiT1MiLCJHZW5kZXIiLCJUb3RTcCIpKQpoZWFkKGRmKQpgYGAKCiMjIyBDcmVhdGUgYSBOZXcgRGF0YSBGcmFtZQoKVGhlIGNvZGUgYmVsb3cgY3JlYXRlcyBhIG5ldyBkYXRhIGZyYW1lIGZyb20gY29sdW1uIHZlY3RvcnMuIE5vdGljZSBob3cgdGhlIGNvbHVtbiBuYW1lcyBhcmUgdGhlIG5hbWVzIG9mIHRoZSB2ZWN0b3JzLiBBIG5ldyB2ZWN0b3IgaXMgY3JlYXRlZCB3aXRoIHRoZSA8Y29kZT5jPC9jb2RlPiBmdW5jdGlvbiwgZS5nLiwgPGNvZGU+diBcPC0gYygzLDUsMSw5KTwvY29kZT4uCgpgYGB7cn0KZGYxIDwtIGRhdGEuZnJhbWUoc3RhdGUgPSBjKCdBcml6b25hJywnR2VvcmdpYScsICdOZXcgWW9yaycsJ0luZGlhbmEnLCdXYXNoaW5ndG9uJywnVGV4YXMnKSwKICAgICAgICAgICAgICAgICAgY29kZSA9IGFzLmZhY3RvcihjKCdBWicsJ0dBJywnTlknLCdJTicsJ1dBJywnVFgnKSksCiAgICAgICAgICAgICAgICAgIHNjb3JlID0gYyg2Miw0Nyw1NSw3NCwzMSw4NSkpCgpoZWFkKGRmMSkKCmBgYAoKIyMgU2VhcmNoIERhdGEgRnJhbWVzCgpUaGVyZSBhcmUgdHdvIGltcG9ydGFudCBmdW5jdGlvbnMgZm9yICJzZWFyY2hpbmciIGRhdGEgZnJhbWVzOiA8Y29kZT53aGljaDwvY29kZT4gYW5kIDxjb2RlPmFueTwvY29kZT4uIFRoZSBjb2RlIGJlbG93IHVzZXMgdGhlIGJ1aWx0LWluIFsqKk9yYW5nZSoqIGRhdGEgZnJhbWVdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9kYXRhc2V0cy92ZXJzaW9ucy8zLjYuMi90b3BpY3MvT3JhbmdlKSB3aGljaCBjb250YWlucyBtZWFzdXJlbWVudHMgb2Ygb3JhbmdlIHRyZWVzLiBJdCBoYXMgdGhyZWUgY29sdW1uczogdGhlIHRyZWUsIHRoZSAqYWdlKiBvZiB0aGUgdHJlZSAoZGF5cyBzaW5jZSAxOTY4LzEyLzMxKSwgYW5kICpjaXJjdW1mZXJlbmNlKiAoaW4gKm1tKikuCgojIyMgd2hpY2gKCmBgYHtyfQpkZiA8LSBPcmFuZ2UKCmhlYWQoZGYpCgojIGZpbmQgYWxsIHJvd3Mgd2hlcmUgdGhlIGNpcmN1bWZlcmVuY2UgaXMgbW9yZSB0aGFuIDIwMG1tCnJzIDwtIHdoaWNoKGRmJGNpcmN1bWZlcmVuY2UgPiAyMDApCgojIGRpc3BsYXkgYWxsIHJvd3Mgd2hlcmUgdGhlIGNpcmN1bWZlcmVuY2UgaXMgbW9yZSB0aGFuIDIwMG1tCmRmW3JzLF0KCiMgY29tcG91bmQgY29uZGl0aW9ucyBhcmUgcG9zc2libGUgd2l0aCAmIChhbmQpLCB8IChvciksIGFuZCAhIChub3QpCnJzMiA8LSB3aGljaChkZiRjaXJjdW1mZXJlbmNlID4gMjAwICYgZGYkYWdlIDwgMTUwMCkKcnMzIDwtIHdoaWNoKGRmJGNpcmN1bWZlcmVuY2UgPCAyMDAgfCAhKGRmJGFnZSA8IDE1MDApKQpyczQgPC0gd2hpY2goZGYkY2lyY3VtZmVyZW5jZSA+IDQwMCB8IGRmJGFnZSA+IDE1MDApCgpyczIKcnMzCnJzNAoKbWVhbihkZltyczQsMl0pCm1lYW4oZGYkYWdlW3JzM10pCgpgYGAKCkluIHRoZSBhYm92ZSBleGFtcGxlIDxjb2RlPnJzIFw8LSB3aGljaChkZlwkY2lyY3VtZmVyZW5jZSBcPiAyMDApPC9jb2RlPiBmaW5kcyBhbGwgcm93cyBpbiB0aGUgZGF0YSBmcmFtZSAqZGYqIHdoZXJlICpjaXJjdW1mZXJlbmNlIFw+IDIwMCouIFRoZSByb3dzIGFyZSBzYXZlZCBpbiAqcnMqLgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBGaWxlcyAmIFJlc291cmNlcwoKYGBge3IgemlwRmlsZXMsIGVjaG89RkFMU0V9CnppcE5hbWUgPSBzcHJpbnRmKCJMZXNzb25GaWxlcy0lcy0lcy56aXAiLCAKICAgICAgICAgICAgICAgICBwYXJhbXMkY2F0ZWdvcnksCiAgICAgICAgICAgICAgICAgcGFyYW1zJG51bWJlcikKCnRleHRBTGluayA9IHBhc3RlMCgiQWxsIEZpbGVzIGZvciBMZXNzb24gIiwgCiAgICAgICAgICAgICAgIHBhcmFtcyRjYXRlZ29yeSwiLiIscGFyYW1zJG51bWJlcikKCiMgZG93bmxvYWRGaWxlc0xpbmsoKSBpcyBpbmNsdWRlZCBmcm9tIF9pbnNlcnQyREIuUgprbml0cjo6cmF3X2h0bWwoZG93bmxvYWRGaWxlc0xpbmsoIi4iLCB6aXBOYW1lLCB0ZXh0QUxpbmspKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgUmVmZXJlbmNlcwoKTm8gcmVmZXJlbmNlcy4KCiMjIEVycmF0YQoKW0xldCB1cyBrbm93XShodHRwczovL2Zvcm0uam90Zm9ybS5jb20vMjEyMTg3MDcyNzg0MTU3KXt0YXJnZXQ9Il9ibGFuayJ9Lgo=