Vectors
In R, a vector is similar to a list or array in other programming languages. It is a collection of elements of the same basic type: numeric, character, or logical (Boolean). As an aside, a list in R is a collection of mixed data types. This section applies to vectors only.
Vectors emerge in different ways in R:
- result of manually creating them
- result of a call to a function
- extraction of elements from a vector
- column in a data frame
There are no specific packages required to manipulate, use, create, or access vectors.
Creating a Vector
A simple vector can be created by creating a collection using the c()
function. Note that all elements in a vector must be of the same basic type.
v.numeric <- c(23, 77, 12, 98, -23, 0)
v.char <- c('R', 'Java', 'Python', 'C++', 'LISP')
v.logical <- c(TRUE, F, FALSE, T, T, T)
When elements are not of the same type, R attempts to coerce (cast or convert) them to the same “higher” data type. For example, in the code below, all elements are coerced to character (i.e., text/string):
v.mixed <- c(23, 0, 'R', TRUE)
print(v.mixed)
## [1] "23" "0" "R" "TRUE"
The code below shows a more complex example. It creates an artificial vector of random integers for use in the remainder of the tutorial.
# vector of 50 random integers between 0 and 10
# set the seed for the random number generator to ensure same
# sequence of random numbers every time the code is run
set.seed(98788)
v <- round(runif(50, min = 0, max = 10),0)
# arguments do not have to be passed in the order that they are
# declared in the function definition as long as the names of the
# arguments are specified
v <- round(runif(n = 50, max = 10, min = 1),0)
v <- round(runif(max = 10, min = 1, n = 50),0)
print(v)
## [1] 9 6 9 1 9 8 10 2 6 9 8 7 6 1 7 10 4 3 5 9 7 8 4 9 7 4 6 5 1 4 4 2 7 9 9 6 3 2 3 7 8 6
## [43] 3 9 4 10 7 1 4 2
Size of Vectors
To find the size (length) of a vector, i.e, the number of elements in a vector, use the function length()
.
v <- round(runif(50, min = 0, max = 10),0)
print(length(v))
## [1] 50
Vector Operations
Vectors can be used in algebraic operations without having to write loops like in other programming languages. For example, in the example below, every element of the vector v.a is multiplied by 2.5
v.a <- c(23, 33, 10, 8, 7)
r <- v.a * 2.5
print(r)
## [1] 57.5 82.5 25.0 20.0 17.5
If two vectors are used in an operation then the operand is applied to each corresponding pair of values.
v.a <- c(23, 33, 10, 8, 7)
v.b <- c(0.3, 0.5, 0.1, 0.9, 0.8)
r <- v.a * v.b
print(r)
## [1] 6.9 16.5 1.0 7.2 5.6
The vectors, naturally, need to be of the same size. So, the code below does not work correctly as the vectors do not have the same number of elements.
v.a <- c(23, 33, 10, 8, 7)
v.b <- c(23, 33, 10, 8, 7, 99)
r <- v.a * v.b
## Warning in v.a * v.b: longer object length is not a multiple of shorter object length
Of course, we could also write the code explicitly using a loop. Note that this runs significantly slower and thus should be avoided.
Example: Dot Product
The code below calculates the sum of the products of the two vectors (their dot product), first using a loop, and then using vector operations, and finally using the %*% operator.
# Approach 1: Using Loops
v.a <- c(23, 33, 10, 8, 7)
v.b <- c(0.3, 0.5, 0.1, 0.9, 0.8)
n <- length(v.a)
r <- 0
for (i in 1:n) {
r <- r + (v.a[i] * v.b[i])
}
print(paste0("Approach 1 (loop): ", r))
## [1] "Approach 1 (loop): 37.2"
# Approach 2: Vector Operation
v.prod <- v.a * v.b
v.sum <- sum(v.prod)
print(paste0("Approach 2 (vector operation and function): ", v.sum))
## [1] "Approach 2 (vector operation and function): 37.2"
# Approach 3: Operator
dp <- v.a %*% v.b
print(paste0("Approach 3 (dot product operand): ", dp))
## [1] "Approach 3 (dot product operand): 37.2"
The above example illustrates that there is often more than one way to achieve some task in R. Of course, some approaches are more elegant, require less code, and are faster. There is another way, the above dot product could have been calculated and that is to use the dot()
function from the pracma library. We are certain that you can come up with yet another way.
Functions on Vectors
As seen in previous examples, many functions take vectors as arguments. For example, the function mean()
takes a numeric or logical vector as input and returns the average value. For a logical vector, the values of TRUE and FALSE are converted to numbers where TRUE = 1 and FALSE = 0 and then the arithmetic average is calculated.
The code below illustrates some common functions. The function round()
can be useful for output.
v <- c(2, 6, 8.2, 1.3, 9.4, 45.7, 32, 99, 104,55, 0.05)
n <- length(v)
print(paste0("n = ", n))
## [1] "n = 11"
m <- mean(v)
print(paste0("Mean = ", round(m,2)))
## [1] "Mean = 32.97"
s <- sd(v)
print(paste0("StdDev = ", round(s,2)))
## [1] "StdDev = 38.72"
d <- median(v)
print(paste0("Median = ", round(d,2)))
## [1] "Median = 9.4"
r <- max(v) - min(v)
print(paste0("Range = ", round(r,2)))
## [1] "Range = 103.95"
tm <- mean(v, trim = 0.1)
print(paste0("10% Trimmed Mean = ", round(tm,2)))
## [1] "10% Trimmed Mean = 28.73"
Accessing Elements in a Vector
Elements are accessed positionally, although in R, the access index can be a vector of integers in which case all elements at those positions are retrieved. Positions are numbered from 1 to the number of elements in a vector. The number of elements (or length) of a vector can be obtained using the length()
function.
In the example below, note that n:m
generates a vector of integers from n to m, inclusive. The seq()
generates a vector that is a sequence of integers at an interval.
## [1] 2.00 6.00 8.20 1.30 9.40 45.70 32.00 99.00 104.00 55.00 0.05
# access a single element at position 3
v[3]
## [1] 8.2
# access element 3 through 5
v[3:5]
## [1] 8.2 1.3 9.4
# access the first element
v[1]
## [1] 2
# access the last element
v[length(v)]
## [1] 0.05
# access the next to last element
v[length(v)-1]
## [1] 55
# access every other element
v[seq(from = 1, to = length(v), by = 2)]
## [1] 2.00 8.20 9.40 32.00 104.00 0.05
# access specific elements at positions 1, 5, and 7
i <- c(1,5,7)
v[i]
## [1] 2.0 9.4 32.0
Testing Predicate Expressions
It is possible in R to apply a predicate expression to every element in a vector. This generates a “Boolean vector” of TRUE/FALSE values that indicate which element matches the predicate expression (TRUE) and which doesn’t (FALSE).
Predicate expressions are built with logical operators (<, >, <=, >=, ==, !=)
## [1] TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE
## [1] FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [1] TRUE TRUE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE
## [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
Finding Matches
The which()
function returns the positions that are TRUE in a Boolean vector.
# returns positions of vector that matches predicate expression
which(v != 5)
## [1] 1 2 3 4 5 6 7 8 9 10 11
# count the number of matches
length(which(v != 0))
## [1] 11
p <- which(v < 5)
print (v[p])
## [1] 2.00 1.30 0.05
# or combine
x <- v[which(v < 5)]
print (x)
## [1] 2.00 1.30 0.05
Determining Any Matches
To determine if there are any matches, i.e., at least one element in a vector matches the predicate expression, use the any()
function. The function any()
returns TRUE if there’s at least one match, FALSE otherwise.
## [1] TRUE
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]
.
Every column in a data frame is a vector but every row is a data frame of a single row.
Note that this is reversed from the way Excel and other spreadsheets work.
The example code below uses the built-in data frame mtcars. You can find out more about its structure using str(mtcars)
or displaying the first few rows with head(mtcars)
. It is also often useful to restrict the columns in the output.
The function str()
should not be included in any R program or R Notebook, but rather is best used to explore the data interactively in the R console; it’s output is lengthy and not useful, generally, to those looking at the output of an R program or a knitted R Notebook.
## 'data.frame': 32 obs. of 12 variables:
## $ miPerGa: 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 ...
## $ foo : num 99 99 99 99 99 99 99 99 99 99 ...
## miPerGa cyl disp hp
## Mazda RX4 21.0 6 160 110
## Mazda RX4 Wag 21.0 6 160 110
## Datsun 710 22.8 4 108 93
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 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] 525.135
## [1] 108
mtcars[c(1,4)] # columns 1 and 4 as a new dataframe
## miPerGa 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
## miPerGa cyl disp hp drat wt qsec vs am gear carb foo
## Hornet Sportabout 18.7 8 360 175 3.15 3.44 17.02 0 0 3 2 99
## Valiant 18.1 6 225 105 2.76 3.46 20.22 1 0 3 1 99
## Duster 360 14.3 8 360 245 3.21 3.57 15.84 0 0 3 4 99
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
## Warning in mean.default(w): argument is not numeric or logical: returning NA
## [1] NA
Adding Columns to 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)
## miPerGa cyl disp hp drat wt qsec vs am gear carb foo dispcyl
## Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4 99 26.66667
## Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4 99 26.66667
## Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1 99 27.00000
## Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 99 43.00000
## Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 99 45.00000
## Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1 99 37.50000
Change Column Names
To change the names of columns you can either create a new dataframe and copy selected columns from an existing dataframe to the new dataframe, or you can use the colnames()
function to update columns names without copying.
The function colnames()
returns the names of the columns, but it can also be used on the left side of an assignment and so we can change the column names that way. This is illustrated below.
# copy columns 1 through 3 to new dataframe
df <- mtcars[,1:3]
# rename all columns
colnames(df) <- c("mpg.all", "numCylinders", "Displacement.ltr")
# rename a single column
colnames(df)[1] <- "miPerGa"
head(df,3)
## miPerGa numCylinders Displacement.ltr
## Mazda RX4 21.0 6 160
## Mazda RX4 Wag 21.0 6 160
## Datsun 710 22.8 4 108
Adding Rows to a Data Frame
Simply assign a new value to a column in a row that does not (yet) exist, will cause R to allocate additional memory. The code below illustrates this:
Creating a New Data Frame
Data frames are created in various ways:
- use the
data.frame
function
- load a CSV, TSV, or other value separated file
- load a simple XML file
- execute a SQL query
- result of running a function
Load a Data Frame from CSV
Loading data into data frames from files, particularly CSV and TSV files, is covered in detail in Lesson 6.106 – Import Data into R from CSV, TSV, and Excel Files, so consult that lesson for more details. This section is a quick overview and summary.
The most commonly used function to load data from a CSV files is read.csv()
and it is part of Base R, so no additional packages are required. This function can load a CSV from a local file or from a URL.
The parameter 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.
df <- read.csv(file = "customertxndata.csv", header = F)
head(df)
## V1 V2 V3 V4 V5
## 1 7 0 Android Male 0.0000
## 2 20 1 iOS <NA> 576.8668
## 3 22 1 iOS Female 850.0000
## 4 24 2 iOS Female 1050.0000
## 5 1 0 Android Male 0.0000
## 6 13 1 Android Male 460.0000
df <- read.csv(file = "customertxndata.csv",
header = F,
col.names = c("numVisits","NumTxn","OS","Gender","TotSp"))
head(df)
## numVisits NumTxn OS Gender TotSp
## 1 7 0 Android Male 0.0000
## 2 20 1 iOS <NA> 576.8668
## 3 22 1 iOS Female 850.0000
## 4 24 2 iOS Female 1050.0000
## 5 1 0 Android Male 0.0000
## 6 13 1 Android Male 460.0000
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.
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.
Nota Bene: 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\“.”
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)
## numVisits NumTxn OS Gender TotSp
## 1 7 0 Android Male 0.0000
## 2 20 1 iOS <NA> 576.8668
## 3 22 1 iOS Female 850.0000
## 4 24 2 iOS Female 1050.0000
## 5 1 0 Android Male 0.0000
## 6 13 1 Android Male 460.0000
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
## Grouped Data: circumference ~ age | Tree
## 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,]
## Grouped Data: circumference ~ age | Tree
## 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 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.
any
The any
function returns TRUE or FALSE depending on whether any column (or row) in the dataframe satisfies a Boolean expression.
# is there any tree with age > 2000?
any(df$age > 25)
## [1] TRUE
Memory Management
R is similar to Python and other interpreted languages in terms of memory management. Objects and variables remain in memory until you restart R or explicitly delete them. This can sometimes cause conflicts during development. Adding this to the start of an R script or an R Notebook ensures that the program runs with an empty memory environment. This is critical for languages like R and Python, but is not needed for programming languages that run in separate processes such as Java and C++ programs.
Use the code below to find and then delete all objects, and reclaim memory. The function ls()
lists all objects (variables) by name, while the rm()
removes one or more objects from memory. Finally, the function gc()
runs the garbage collector and returns freed memory to the usable memory pool for the process in which R is running.
rm(list = ls(all.names = TRUE))
gc()
Of course, rather than deleting all objects as in the code chunk above, you may wish to release large objects or unused objects selectively by their name, e.g., rm(“objName”)
.
Conclusion
As you saw, R is not a difficult language to learn as it is similar to other languages and for most language constructs that you are familiar with, there is an equivalent. But it is important that you go beyond this tutorial and learn the “R way” of programming using vectorized operations.
Tutorial
The video tutorial demonstrates the constructs introduced in this lesson.
References
No references.
LS0tCnRpdGxlOiAiV29ya2luZyB3aXRoIFZlY3RvcnMgYW5kIERhdGEgRnJhbWVzIGluIFIiCnBhcmFtczoKICBjYXRlZ29yeTogNgogIG51bWJlcjogMTAzCiAgdGltZTogIjc1IG1pbiIKICBsZXZlbDogYmVnaW5uZXIKICB0YWdzOiAicixwcmltZXIsdmVjdG9ycyxkYXRhIGZyYW1lcyIKICBkZXNjcmlwdGlvbjogIkRlbW9uc3RyYXRlcyBob3cgdG8gY3JlYXRlLCBhY2Nlc3MsIGFuZCBtYW5pcHVsYXRlIG51bWVyaWMsCiAgICAgICAgICAgICAgICBjaGFyYWN0ZXIgKHRleHQpLCBhbmQgbG9naWNhbCBkYXRhIGluIHZlY3RvcnMgYW5kIGRhdGEgZnJhbWVzLiIKZGF0ZTogIjxzbWFsbD5gciBTeXMuRGF0ZSgpYDwvc21hbGw+IgphdXRob3I6ICI8c21hbGw+TWFydGluIFNjaGVkbGJhdWVyPC9zbWFsbD4iCmVtYWlsOiAibS5zY2hlZGxiYXVlckBuZXUuZWR1IgphZmZpbGl0YXRpb246ICJOb3J0aGVhc3Rlcm4gVW5pdmVyc2l0eSIKb3V0cHV0OiAKICBib29rZG93bjo6aHRtbF9kb2N1bWVudDI6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29sbGFwc2VkOiBmYWxzZQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdGhlbWU6IHNhbmRzdG9uZQogICAgaGlnaGxpZ2h0OiB0YW5nbwotLS0KCi0tLQp0aXRsZTogIjxzbWFsbD5gciBwYXJhbXMkY2F0ZWdvcnlgLmByIHBhcmFtcyRudW1iZXJgPC9zbWFsbD48YnIvPjxzcGFuIHN0eWxlPSdjb2xvcjogIzJFNDA1MzsgZm9udC1zaXplOiAwLjllbSc+YHIgcm1hcmtkb3duOjptZXRhZGF0YSR0aXRsZWA8L3NwYW4+IgotLS0KCmBgYHtyIGNvZGU9eGZ1bjo6cmVhZF91dGY4KHBhc3RlMChoZXJlOjpoZXJlKCksJy9SL19pbnNlcnQyREIuUicpKSwgaW5jbHVkZSA9IEZBTFNFfQpgYGAKCiMjIEludHJvZHVjdGlvbgoKIyMgVmVjdG9ycwoKSW4gUiwgYSB2ZWN0b3IgaXMgc2ltaWxhciB0byBhIGxpc3Qgb3IgYXJyYXkgaW4gb3RoZXIgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzLiBJdCBpcyBhIGNvbGxlY3Rpb24gb2YgZWxlbWVudHMgb2YgdGhlIHNhbWUgYmFzaWMgdHlwZTogbnVtZXJpYywgY2hhcmFjdGVyLCBvciBsb2dpY2FsIChCb29sZWFuKS4gQXMgYW4gYXNpZGUsIGEgKmxpc3QqIGluIFIgaXMgYSBjb2xsZWN0aW9uIG9mIG1peGVkIGRhdGEgdHlwZXMuIFRoaXMgc2VjdGlvbiBhcHBsaWVzIHRvIHZlY3RvcnMgb25seS4KClZlY3RvcnMgZW1lcmdlIGluIGRpZmZlcmVudCB3YXlzIGluIFI6CgotICAgcmVzdWx0IG9mIG1hbnVhbGx5IGNyZWF0aW5nIHRoZW0KLSAgIHJlc3VsdCBvZiBhIGNhbGwgdG8gYSBmdW5jdGlvbgotICAgZXh0cmFjdGlvbiBvZiBlbGVtZW50cyBmcm9tIGEgdmVjdG9yCi0gICBjb2x1bW4gaW4gYSBkYXRhIGZyYW1lCgpUaGVyZSBhcmUgbm8gc3BlY2lmaWMgcGFja2FnZXMgcmVxdWlyZWQgdG8gbWFuaXB1bGF0ZSwgdXNlLCBjcmVhdGUsIG9yIGFjY2VzcyB2ZWN0b3JzLgoKIyMgQ3JlYXRpbmcgYSBWZWN0b3IKCkEgc2ltcGxlIHZlY3RvciBjYW4gYmUgY3JlYXRlZCBieSBjcmVhdGluZyBhIGNvbGxlY3Rpb24gdXNpbmcgdGhlIDxjb2RlPmMoKTwvY29kZT4gZnVuY3Rpb24uIE5vdGUgdGhhdCBhbGwgZWxlbWVudHMgaW4gYSB2ZWN0b3IgbXVzdCBiZSBvZiB0aGUgc2FtZSBiYXNpYyB0eXBlLgoKYGBge3IgY3JlYXRlU2ltcGxlVmVjdG9yfQp2Lm51bWVyaWMgPC0gYygyMywgNzcsIDEyLCA5OCwgLTIzLCAwKQp2LmNoYXIgPC0gYygnUicsICdKYXZhJywgJ1B5dGhvbicsICdDKysnLCAnTElTUCcpCnYubG9naWNhbCA8LSBjKFRSVUUsIEYsIEZBTFNFLCBULCBULCBUKQpgYGAKCldoZW4gZWxlbWVudHMgYXJlIG5vdCBvZiB0aGUgc2FtZSB0eXBlLCBSIGF0dGVtcHRzIHRvIGNvZXJjZSAoY2FzdCBvciBjb252ZXJ0KSB0aGVtIHRvIHRoZSBzYW1lICJoaWdoZXIiIGRhdGEgdHlwZS4gRm9yIGV4YW1wbGUsIGluIHRoZSBjb2RlIGJlbG93LCBhbGwgZWxlbWVudHMgYXJlIGNvZXJjZWQgdG8gY2hhcmFjdGVyICgqaS5lLiosIHRleHQvc3RyaW5nKToKCmBgYHtyIGNvZXJjaW9uVmVjdG9yc30Kdi5taXhlZCA8LSBjKDIzLCAwLCAnUicsIFRSVUUpCgpwcmludCh2Lm1peGVkKQpgYGAKClRoZSBjb2RlIGJlbG93IHNob3dzIGEgbW9yZSBjb21wbGV4IGV4YW1wbGUuIEl0IGNyZWF0ZXMgYW4gYXJ0aWZpY2lhbCB2ZWN0b3Igb2YgcmFuZG9tIGludGVnZXJzIGZvciB1c2UgaW4gdGhlIHJlbWFpbmRlciBvZiB0aGUgdHV0b3JpYWwuCgpgYGB7ciBjcmVhdGVTYW1wbGVWZWN0b3J9CiMgdmVjdG9yIG9mIDUwIHJhbmRvbSBpbnRlZ2VycyBiZXR3ZWVuIDAgYW5kIDEwCgojIHNldCB0aGUgc2VlZCBmb3IgdGhlIHJhbmRvbSBudW1iZXIgZ2VuZXJhdG9yIHRvIGVuc3VyZSBzYW1lCiMgc2VxdWVuY2Ugb2YgcmFuZG9tIG51bWJlcnMgZXZlcnkgdGltZSB0aGUgY29kZSBpcyBydW4Kc2V0LnNlZWQoOTg3ODgpCnYgPC0gcm91bmQocnVuaWYoNTAsIG1pbiA9IDAsIG1heCA9IDEwKSwwKQoKIyBhcmd1bWVudHMgZG8gbm90IGhhdmUgdG8gYmUgcGFzc2VkIGluIHRoZSBvcmRlciB0aGF0IHRoZXkgYXJlCiMgZGVjbGFyZWQgaW4gdGhlIGZ1bmN0aW9uIGRlZmluaXRpb24gYXMgbG9uZyBhcyB0aGUgbmFtZXMgb2YgdGhlCiMgYXJndW1lbnRzIGFyZSBzcGVjaWZpZWQKdiA8LSByb3VuZChydW5pZihuID0gNTAsIG1heCA9IDEwLCBtaW4gPSAxKSwwKQp2IDwtIHJvdW5kKHJ1bmlmKG1heCA9IDEwLCBtaW4gPSAxLCBuID0gNTApLDApCgpwcmludCh2KQpgYGAKCiMjIFNpemUgb2YgVmVjdG9ycwoKVG8gZmluZCB0aGUgc2l6ZSAobGVuZ3RoKSBvZiBhIHZlY3RvciwgKmkuZSosIHRoZSBudW1iZXIgb2YgZWxlbWVudHMgaW4gYSB2ZWN0b3IsIHVzZSB0aGUgZnVuY3Rpb24gPGNvZGU+bGVuZ3RoKCk8L2NvZGU+LgoKYGBge3J9CnYgPC0gcm91bmQocnVuaWYoNTAsIG1pbiA9IDAsIG1heCA9IDEwKSwwKQoKcHJpbnQobGVuZ3RoKHYpKQpgYGAKCiMjIFZlY3RvciBPcGVyYXRpb25zCgpWZWN0b3JzIGNhbiBiZSB1c2VkIGluIGFsZ2VicmFpYyBvcGVyYXRpb25zIHdpdGhvdXQgaGF2aW5nIHRvIHdyaXRlIGxvb3BzIGxpa2UgaW4gb3RoZXIgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzLiBGb3IgZXhhbXBsZSwgaW4gdGhlIGV4YW1wbGUgYmVsb3csIGV2ZXJ5IGVsZW1lbnQgb2YgdGhlIHZlY3RvciAqdi5hKiBpcyBtdWx0aXBsaWVkIGJ5IDIuNQoKYGBge3J9CnYuYSA8LSBjKDIzLCAzMywgMTAsIDgsIDcpCgpyIDwtIHYuYSAqIDIuNQoKcHJpbnQocikKYGBgCgpJZiB0d28gdmVjdG9ycyBhcmUgdXNlZCBpbiBhbiBvcGVyYXRpb24gdGhlbiB0aGUgb3BlcmFuZCBpcyBhcHBsaWVkIHRvIGVhY2ggY29ycmVzcG9uZGluZyBwYWlyIG9mIHZhbHVlcy4KCmBgYHtyfQp2LmEgPC0gYygyMywgMzMsIDEwLCA4LCA3KQp2LmIgPC0gYygwLjMsIDAuNSwgMC4xLCAwLjksIDAuOCkKCnIgPC0gdi5hICogdi5iCgpwcmludChyKQpgYGAKClRoZSB2ZWN0b3JzLCBuYXR1cmFsbHksIG5lZWQgdG8gYmUgb2YgdGhlIHNhbWUgc2l6ZS4gU28sIHRoZSBjb2RlIGJlbG93IGRvZXMgbm90IHdvcmsgY29ycmVjdGx5IGFzIHRoZSB2ZWN0b3JzIGRvIG5vdCBoYXZlIHRoZSBzYW1lIG51bWJlciBvZiBlbGVtZW50cy4KCmBgYHtyfQp2LmEgPC0gYygyMywgMzMsIDEwLCA4LCA3KQp2LmIgPC0gYygyMywgMzMsIDEwLCA4LCA3LCA5OSkKCnIgPC0gdi5hICogdi5iCmBgYAoKT2YgY291cnNlLCB3ZSBjb3VsZCBhbHNvIHdyaXRlIHRoZSBjb2RlIGV4cGxpY2l0bHkgdXNpbmcgYSBsb29wLiBOb3RlIHRoYXQgdGhpcyBydW5zIHNpZ25pZmljYW50bHkgc2xvd2VyIGFuZCB0aHVzIHNob3VsZCBiZSBhdm9pZGVkLgoKIyMjIEV4YW1wbGU6IERvdCBQcm9kdWN0CgpUaGUgY29kZSBiZWxvdyBjYWxjdWxhdGVzIHRoZSBzdW0gb2YgdGhlIHByb2R1Y3RzIG9mIHRoZSB0d28gdmVjdG9ycyAodGhlaXIgKmRvdCBwcm9kdWN0KiksIGZpcnN0IHVzaW5nIGEgbG9vcCwgYW5kIHRoZW4gdXNpbmcgdmVjdG9yIG9wZXJhdGlvbnMsIGFuZCBmaW5hbGx5IHVzaW5nIHRoZSAqJVwqJSogb3BlcmF0b3IuCgpgYGB7cn0KCiMgQXBwcm9hY2ggMTogVXNpbmcgTG9vcHMKdi5hIDwtIGMoMjMsIDMzLCAxMCwgOCwgNykKdi5iIDwtIGMoMC4zLCAwLjUsIDAuMSwgMC45LCAwLjgpCgpuIDwtIGxlbmd0aCh2LmEpCgpyIDwtIDAKZm9yIChpIGluIDE6bikgewogIHIgPC0gciArICh2LmFbaV0gKiB2LmJbaV0pCn0KCnByaW50KHBhc3RlMCgiQXBwcm9hY2ggMSAobG9vcCk6ICIsIHIpKQoKIyBBcHByb2FjaCAyOiBWZWN0b3IgT3BlcmF0aW9uCnYucHJvZCA8LSB2LmEgKiB2LmIKdi5zdW0gPC0gc3VtKHYucHJvZCkKCnByaW50KHBhc3RlMCgiQXBwcm9hY2ggMiAodmVjdG9yIG9wZXJhdGlvbiBhbmQgZnVuY3Rpb24pOiAiLCB2LnN1bSkpCgojIEFwcHJvYWNoIDM6IE9wZXJhdG9yCgpkcCA8LSB2LmEgJSolIHYuYgpwcmludChwYXN0ZTAoIkFwcHJvYWNoIDMgKGRvdCBwcm9kdWN0IG9wZXJhbmQpOiAiLCBkcCkpCmBgYAoKVGhlIGFib3ZlIGV4YW1wbGUgaWxsdXN0cmF0ZXMgdGhhdCB0aGVyZSBpcyBvZnRlbiBtb3JlIHRoYW4gb25lIHdheSB0byBhY2hpZXZlIHNvbWUgdGFzayBpbiBSLiBPZiBjb3Vyc2UsIHNvbWUgYXBwcm9hY2hlcyBhcmUgbW9yZSBlbGVnYW50LCByZXF1aXJlIGxlc3MgY29kZSwgYW5kIGFyZSBmYXN0ZXIuIFRoZXJlIGlzIGFub3RoZXIgd2F5LCB0aGUgYWJvdmUgZG90IHByb2R1Y3QgY291bGQgaGF2ZSBiZWVuIGNhbGN1bGF0ZWQgYW5kIHRoYXQgaXMgdG8gdXNlIHRoZSA8Y29kZT5kb3QoKTwvY29kZT4gZnVuY3Rpb24gZnJvbSB0aGUgKipwcmFjbWEqKiBsaWJyYXJ5LiBXZSBhcmUgY2VydGFpbiB0aGF0IHlvdSBjYW4gY29tZSB1cCB3aXRoIHlldCBhbm90aGVyIHdheS4KCiMjIEZ1bmN0aW9ucyBvbiBWZWN0b3JzCgpBcyBzZWVuIGluIHByZXZpb3VzIGV4YW1wbGVzLCBtYW55IGZ1bmN0aW9ucyB0YWtlIHZlY3RvcnMgYXMgYXJndW1lbnRzLiBGb3IgZXhhbXBsZSwgdGhlIGZ1bmN0aW9uIDxjb2RlPm1lYW4oKTwvY29kZT4gdGFrZXMgYSBudW1lcmljIG9yIGxvZ2ljYWwgdmVjdG9yIGFzIGlucHV0IGFuZCByZXR1cm5zIHRoZSBhdmVyYWdlIHZhbHVlLiBGb3IgYSBsb2dpY2FsIHZlY3RvciwgdGhlIHZhbHVlcyBvZiAqVFJVRSogYW5kICpGQUxTRSogYXJlIGNvbnZlcnRlZCB0byBudW1iZXJzIHdoZXJlICpUUlVFID0gMSogYW5kICpGQUxTRSA9IDAqIGFuZCB0aGVuIHRoZSBhcml0aG1ldGljIGF2ZXJhZ2UgaXMgY2FsY3VsYXRlZC4KClRoZSBjb2RlIGJlbG93IGlsbHVzdHJhdGVzIHNvbWUgY29tbW9uIGZ1bmN0aW9ucy4gVGhlIGZ1bmN0aW9uIDxjb2RlPnJvdW5kKCk8L2NvZGU+IGNhbiBiZSB1c2VmdWwgZm9yIG91dHB1dC4KCmBgYHtyIGZ1bmNzT25WZWNzfQp2IDwtIGMoMiwgNiwgOC4yLCAxLjMsIDkuNCwgNDUuNywgMzIsIDk5LCAxMDQsNTUsIDAuMDUpCgpuIDwtIGxlbmd0aCh2KQpwcmludChwYXN0ZTAoIm4gPSAiLCBuKSkKCm0gPC0gbWVhbih2KQpwcmludChwYXN0ZTAoIk1lYW4gPSAiLCByb3VuZChtLDIpKSkKCnMgPC0gc2QodikKcHJpbnQocGFzdGUwKCJTdGREZXYgPSAiLCByb3VuZChzLDIpKSkKCmQgPC0gbWVkaWFuKHYpCnByaW50KHBhc3RlMCgiTWVkaWFuID0gIiwgcm91bmQoZCwyKSkpCgpyIDwtIG1heCh2KSAtIG1pbih2KQpwcmludChwYXN0ZTAoIlJhbmdlID0gIiwgcm91bmQociwyKSkpCgp0bSA8LSBtZWFuKHYsIHRyaW0gPSAwLjEpCnByaW50KHBhc3RlMCgiMTAlIFRyaW1tZWQgTWVhbiA9ICIsIHJvdW5kKHRtLDIpKSkKYGBgCgojIyBBY2Nlc3NpbmcgRWxlbWVudHMgaW4gYSBWZWN0b3IKCkVsZW1lbnRzIGFyZSBhY2Nlc3NlZCBwb3NpdGlvbmFsbHksIGFsdGhvdWdoIGluIFIsIHRoZSBhY2Nlc3MgaW5kZXggY2FuIGJlIGEgdmVjdG9yIG9mIGludGVnZXJzIGluIHdoaWNoIGNhc2UgYWxsIGVsZW1lbnRzIGF0IHRob3NlIHBvc2l0aW9ucyBhcmUgcmV0cmlldmVkLiBQb3NpdGlvbnMgYXJlIG51bWJlcmVkIGZyb20gMSB0byB0aGUgbnVtYmVyIG9mIGVsZW1lbnRzIGluIGEgdmVjdG9yLiBUaGUgbnVtYmVyIG9mIGVsZW1lbnRzIChvciBsZW5ndGgpIG9mIGEgdmVjdG9yIGNhbiBiZSBvYnRhaW5lZCB1c2luZyB0aGUgPGNvZGU+bGVuZ3RoKCk8L2NvZGU+IGZ1bmN0aW9uLgoKSW4gdGhlIGV4YW1wbGUgYmVsb3csIG5vdGUgdGhhdCA8Y29kZT5uOm08L2NvZGU+IGdlbmVyYXRlcyBhIHZlY3RvciBvZiBpbnRlZ2VycyBmcm9tICpuKiB0byAqbSosIGluY2x1c2l2ZS4gVGhlIDxjb2RlPnNlcSgpPC9jb2RlPiBnZW5lcmF0ZXMgYSB2ZWN0b3IgdGhhdCBpcyBhIHNlcXVlbmNlIG9mIGludGVnZXJzIGF0IGFuIGludGVydmFsLgoKYGBge3Igc2ltcGxlVmVjdG9yQWNjZXNzLCBlY2hvPVR9CgpwcmludCh2KQoKIyBhY2Nlc3MgYSBzaW5nbGUgZWxlbWVudCBhdCBwb3NpdGlvbiAzCnZbM10KCiMgYWNjZXNzIGVsZW1lbnQgMyB0aHJvdWdoIDUKdlszOjVdCgojIGFjY2VzcyB0aGUgZmlyc3QgZWxlbWVudAp2WzFdCgojIGFjY2VzcyB0aGUgbGFzdCBlbGVtZW50CnZbbGVuZ3RoKHYpXQoKIyBhY2Nlc3MgdGhlIG5leHQgdG8gbGFzdCBlbGVtZW50CnZbbGVuZ3RoKHYpLTFdCgojIGFjY2VzcyBldmVyeSBvdGhlciBlbGVtZW50CnZbc2VxKGZyb20gPSAxLCB0byA9IGxlbmd0aCh2KSwgYnkgPSAyKV0KCiMgYWNjZXNzIHNwZWNpZmljIGVsZW1lbnRzIGF0IHBvc2l0aW9ucyAxLCA1LCBhbmQgNwppIDwtIGMoMSw1LDcpCnZbaV0KYGBgCgojIyBUZXN0aW5nIFByZWRpY2F0ZSBFeHByZXNzaW9ucwoKSXQgaXMgcG9zc2libGUgaW4gUiB0byBhcHBseSBhIHByZWRpY2F0ZSBleHByZXNzaW9uIHRvIGV2ZXJ5IGVsZW1lbnQgaW4gYSB2ZWN0b3IuIFRoaXMgZ2VuZXJhdGVzIGEgIkJvb2xlYW4gdmVjdG9yIiBvZiAqVFJVRS9GQUxTRSogdmFsdWVzIHRoYXQgaW5kaWNhdGUgd2hpY2ggZWxlbWVudCBtYXRjaGVzIHRoZSBwcmVkaWNhdGUgZXhwcmVzc2lvbiAoKlRSVUUqKSBhbmQgd2hpY2ggZG9lc24ndCAoKkZBTFNFKikuCgpQcmVkaWNhdGUgZXhwcmVzc2lvbnMgYXJlIGJ1aWx0IHdpdGggbG9naWNhbCBvcGVyYXRvcnMgKFw8LCBcPiwgXDw9LCBcPj0sID09LCAhPSkKCmBgYHtyfQp2IDwgNQoKKHYgPCAxIHwgdiA+IDkpCgoodiA8PSA3ICYgdiAhPSAzKQoKdiAhPSA1CgpsIDwtICh2ID09IDUpCnByaW50KGwpCmBgYAoKIyMgRmluZGluZyBNYXRjaGVzCgpUaGUgPGNvZGU+d2hpY2goKTwvY29kZT4gZnVuY3Rpb24gcmV0dXJucyB0aGUgcG9zaXRpb25zIHRoYXQgYXJlICpUUlVFKiBpbiBhIEJvb2xlYW4gdmVjdG9yLgoKYGBge3J9CiMgcmV0dXJucyBwb3NpdGlvbnMgb2YgdmVjdG9yIHRoYXQgbWF0Y2hlcyBwcmVkaWNhdGUgZXhwcmVzc2lvbgp3aGljaCh2ICE9IDUpCgojIGNvdW50IHRoZSBudW1iZXIgb2YgbWF0Y2hlcwpsZW5ndGgod2hpY2godiAhPSAwKSkKYGBgCgpgYGB7cn0KcCA8LSB3aGljaCh2IDwgNSkKcHJpbnQgKHZbcF0pCgojIG9yIGNvbWJpbmUKeCA8LSB2W3doaWNoKHYgPCA1KV0KcHJpbnQgKHgpCmBgYAoKIyMgRGV0ZXJtaW5pbmcgQW55IE1hdGNoZXMKClRvIGRldGVybWluZSBpZiB0aGVyZSBhcmUgYW55IG1hdGNoZXMsICppLmUuKiwgYXQgbGVhc3Qgb25lIGVsZW1lbnQgaW4gYSB2ZWN0b3IgbWF0Y2hlcyB0aGUgcHJlZGljYXRlIGV4cHJlc3Npb24sIHVzZSB0aGUgPGNvZGU+YW55KCk8L2NvZGU+IGZ1bmN0aW9uLiBUaGUgZnVuY3Rpb24gPGNvZGU+YW55KCk8L2NvZGU+IHJldHVybnMgKlRSVUUqIGlmIHRoZXJlJ3MgYXQgbGVhc3Qgb25lIG1hdGNoLCAqRkFMU0UqIG90aGVyd2lzZS4KCmBgYHtyfQphbnkodiA8IDUpCmBgYAoKIyMgQWNjZXNzaW5nIFJvd3MsIENvbHVtbnMsIGFuZCBFbGVtZW50cyAoQ2VsbHMpIG9mIGEgRGF0YSBGcmFtZQoKRGF0YSBmcmFtZXMgYXJlIHZlcnkgc2ltaWxhciB0byB0YWJsZXMgaW4gcmVsYXRpb25hbCBkYXRhYmFzZXMgYW5kIHNwcmVhZHNoZWV0cy4gVGhleSBoYXZlIHJvd3MgYW5kIGNvbHVtbnMgYW5kIHRoZSBpbnRlcnNlY3Rpb24gb2YgYSByb3cgYW5kIGNvbHVtbiBpcyBhIGNlbGwgKG9yIGVsZW1lbnQpLiBUaGUgb3JkZXIgb2YgYWNjZXNzIGlzIHJvdyBmb2xsb3dlZCBieSBjb2x1bW4sICplLmcuKiwgdGhlIHRoaXJkIGVsZW1lbnQgaW4gdGhlIGZvdXJ0aCByb3cgb2YgdGhlIGRhdGEgZnJhbWUgPGNvZGU+bXRjYXJzPC9jb2RlPiBpcyA8Y29kZT5tdGNhcnNbNCwzXTwvY29kZT4uCgpFdmVyeSBjb2x1bW4gaW4gYSBkYXRhIGZyYW1lICoqaXMgYSB2ZWN0b3IqKiBidXQgZXZlcnkgcm93IGlzIGEgZGF0YSBmcmFtZSBvZiBhIHNpbmdsZSByb3cuCgpOb3RlIHRoYXQgdGhpcyBpcyByZXZlcnNlZCBmcm9tIHRoZSB3YXkgRXhjZWwgYW5kIG90aGVyIHNwcmVhZHNoZWV0cyB3b3JrLgoKVGhlIGV4YW1wbGUgY29kZSBiZWxvdyB1c2VzIHRoZSBidWlsdC1pbiBkYXRhIGZyYW1lICptdGNhcnMqLiBZb3UgY2FuIGZpbmQgb3V0IG1vcmUgYWJvdXQgaXRzIHN0cnVjdHVyZSB1c2luZyA8Y29kZT5zdHIobXRjYXJzKTwvY29kZT4gb3IgZGlzcGxheWluZyB0aGUgZmlyc3QgZmV3IHJvd3Mgd2l0aCA8Y29kZT5oZWFkKG10Y2Fycyk8L2NvZGU+LiBJdCBpcyBhbHNvIG9mdGVuIHVzZWZ1bCB0byByZXN0cmljdCB0aGUgY29sdW1ucyBpbiB0aGUgb3V0cHV0LgoKVGhlIGZ1bmN0aW9uIGBzdHIoKWAgc2hvdWxkIG5vdCBiZSBpbmNsdWRlZCBpbiBhbnkgUiBwcm9ncmFtIG9yIFIgTm90ZWJvb2ssIGJ1dCByYXRoZXIgaXMgYmVzdCB1c2VkIHRvIGV4cGxvcmUgdGhlIGRhdGEgaW50ZXJhY3RpdmVseSBpbiB0aGUgUiBjb25zb2xlOyBpdCdzIG91dHB1dCBpcyBsZW5ndGh5IGFuZCBub3QgdXNlZnVsLCBnZW5lcmFsbHksIHRvIHRob3NlIGxvb2tpbmcgYXQgdGhlIG91dHB1dCBvZiBhbiBSIHByb2dyYW0gb3IgYSBrbml0dGVkIFIgTm90ZWJvb2suCgpgYGB7cn0Kc3RyKG10Y2FycykKaGVhZChtdGNhcnNbMTo0XSwgMykKYGBgCgpgYGB7cn0KdiA8LSBtdGNhcnNbNCwzXQp4ID0gbXRjYXJzWzQsM10KCnByaW50KHBhc3RlMCgidiA9ICIsdiwiIGFuZCB4ID0gIix4KSkKYGBgCgpMZWF2aW5nIG91dCBhIGRpbWVuc2lvbiAocm93IG9yIGNvbHVtbikgYWNjZXNzZXMgdGhlIGVudGlyZSByb3cgb3IgY29sdW1uLiBUaGUgcmVzdWx0YW50IGlzIGEgZGF0YSBmcmFtZSB3aXRoIGEgc2luZ2xlIHJvdy4KCk9mdGVuIHRoZSB2YWx1ZXMgbXVzdCBiZSBjb252ZXJ0ZWQgdG8gYSB2ZWN0b3IgZGF0YSB0eXBlLiBDb252ZXJzaW9ucyBvZiB2YXJpYWJsZXMgZnJvbSBvbmUgdHlwZSB0byBhbm90aGVyIGlzIGRvbmUgd2l0aCB0aGUgZmFtaWx5IG9mIDxjb2RlPmFzLnh4eHg8L2NvZGU+IGZ1bmN0aW9ucywgKmUuZy4qLCA8Y29kZT5hcy52ZWN0b3I8L2NvZGU+LCA8Y29kZT5hcy5udW1lcmljPC9jb2RlPiwgb3IgPGNvZGU+YXMuZmFjdG9yPC9jb2RlPi4gVmVjdG9ycyBjYW4gY29udGFpbiBudW1lcmljIG9yIGNoYXJhY3RlciBkYXRhIGJ1dCBhbGwgZWxlbWVudHMgbXVzdCBiZSBvZiB0aGUgc2FtZSB0eXBlLiBJbiBSLCBhIGxpc3QgaXMgc2ltaWxhciB0byBhIHZlY3RvciBidXQgaXQgbWF5IGNvbnRhaW4gYSBtaXggb2YgZWxlbWVudHMuIEEgbWF0cml4IGlzIHNpbWlsYXIgdG8gYSBkYXRhIGZyYW1lIGJ1dCBpdCBjYW4gb25seSBjb250YWluIG51bWJlcnMgYW5kIGl0IGNhbiBoYXZlIG1vcmUgdGhhbiB0d28gZGltZW5zaW9ucy4KClNvbWUgZnVuY3Rpb25zIGV4cGVjdCBkYXRhIGZyYW1lcywgc29tZSB2ZWN0b3JzLCBzb21lIGxpc3RzLiBZb3UgbmVlZCB0byByZWFkIHRoZSBkb2N1bWVudGF0aW9uIG9mIGEgZnVuY3Rpb24gdG8gZmluZCBvdXQuIEZ1cnRoZXJtb3JlLCBzb21lIGZ1bmN0aW9ucyB3aWxsIGF1dG9tYXRpY2FsbHkgY29udmVydCBhIHZhcmlhYmxlIGZyb20gb25lIHR5cGUgdG8gdGhlIG9uZSBpdCByZXF1aXJlcy4KCllvdSBjYW4gYWxzbyBhY2Nlc3MgYSBjb2x1bW4gaW4gZGF0YSBmcmFtZSBieSBpdHMgY29sdW1uIG5hbWUuIEZvciBhbiBlbnRpcmUgY29sdW1uIHlvdSBlaXRoZXIgdXNlIHRoZSBjb2x1bW5zIHBvc2l0aW9uIG9yIGl0cyBuYW1lOiA8Y29kZT5kZlssY29sdW1uXTwvY29kZT4gb3IgPGNvZGU+ZGZcJGNvbHVtbk5hbWU8L2NvZGU+LgoKYGBge3J9CiMgYWxsIG9mIHJvdyA0OyB0aGUgcmVzdWx0IGlzIGEgZGF0YSBmcmFtZQpyIDwtIG10Y2Fyc1s0LF0Kc3VtKHIpCgpjIDwtIG10Y2Fyc1szLF0KY1sxLDNdCgptdGNhcnNbYygxLDQpXSAgICMgY29sdW1ucyAxIGFuZCA0IGFzIGEgbmV3IGRhdGFmcmFtZQoKbXRjYXJzWywyXSAgICAgICAjIGFsbCBvZiBjb2x1bW4gMgptdGNhcnNbNTo3LF0gICAgICMgcm93cyA1IHRvIDcgYXMgYSBuZXcgZGF0YWZyYW1lCm10Y2FycyRjeWwgICAgICAgIyBjb2x1bW4gbmFtZWQgImN5bCIKbXRjYXJzJGN5bFsyXSAgICAjIDJuZCByb3cgaW4gdGhlIGNvbHVtbiAiY3lsIgoKbXRjYXJzJGN5bFszOjldICAjIHJvd3MgMyB0byA5IGZvciBjb2x1bW4gImN5bCIgYXMgYSB2ZWN0b3IKCncgPC0gbXRjYXJzJG1wZwptZWFuKHcpCmBgYAoKIyMgQWRkaW5nIENvbHVtbnMgdG8gYSBEYXRhIEZyYW1lCgpUbyBhZGQgYSBuZXcgY29sdW1uLCB5b3Ugc2ltcGx5ICJhY2Nlc3MiIHRoZSBjb2x1bW4gb3IgdXNlIGEgbmV3IG5hbWUgZm9yIHRoZSBjb2x1bW4uIE5vdGUgaW4gdGhlIGV4YW1wbGUgYmVsb3cgdGhhdCB5b3UgY2FuIG9wZXJhdGUgb24gZW50aXJlIGNvbHVtbnMgKGFzIHZlY3RvcnMpIGFuZCB0aGUgb3BlcmF0aW9uIGlzIGFwcGxpZWQgdG8gZWFjaCBwYWlyIG9mIHZhbHVlcyBpbiB0aGUgdHdvIHZlY3RvcnMgaW4gdGhlIG9wZXJhdGlvbi4gVGhpcyBpcyBtdWNoIG1vcmUgZWZmaWNpZW50IHRoYW4gdXNpbmcgbG9vcHMgYXMgaXMgbmVjZXNzYXJ5IGluIG90aGVyIHByb2dyYW1taW5nIGxhbmd1YWdlcy4KCmBgYHtyfQojIGNvcHkgdGhlIGRhdGEgZnJhbWUgbXRjYXJzIHRvIGEgbmV3IGRhdGEgZnJhbWUgZGYKZGYgPC0gbXRjYXJzCgojIGNyZWF0ZSBhIG5ldyBjb2x1bW4gImRpc3BjeWwiIHdoaWNoIGlzIHRoZSBkaXNwbGFjZW1lbnQgcGVyIGN5bGluZGVyCmRmJGRpc3BjeWwgPC0gZGYkZGlzcCAvIGRmJGN5bAoKaGVhZChkZikKYGBgCgojIyBDaGFuZ2UgQ29sdW1uIE5hbWVzCgpUbyBjaGFuZ2UgdGhlIG5hbWVzIG9mIGNvbHVtbnMgeW91IGNhbiBlaXRoZXIgY3JlYXRlIGEgbmV3IGRhdGFmcmFtZSBhbmQgY29weSBzZWxlY3RlZCBjb2x1bW5zIGZyb20gYW4gZXhpc3RpbmcgZGF0YWZyYW1lIHRvIHRoZSBuZXcgZGF0YWZyYW1lLCBvciB5b3UgY2FuIHVzZSB0aGUgYGNvbG5hbWVzKClgIGZ1bmN0aW9uIHRvIHVwZGF0ZSBjb2x1bW5zIG5hbWVzIHdpdGhvdXQgY29weWluZy4KClRoZSBmdW5jdGlvbiBgY29sbmFtZXMoKWAgcmV0dXJucyB0aGUgbmFtZXMgb2YgdGhlIGNvbHVtbnMsIGJ1dCBpdCBjYW4gYWxzbyBiZSB1c2VkIG9uIHRoZSBsZWZ0IHNpZGUgb2YgYW4gYXNzaWdubWVudCBhbmQgc28gd2UgY2FuIGNoYW5nZSB0aGUgY29sdW1uIG5hbWVzIHRoYXQgd2F5LiBUaGlzIGlzIGlsbHVzdHJhdGVkIGJlbG93LgoKYGBge3J9CiMgY29weSBjb2x1bW5zIDEgdGhyb3VnaCAzIHRvIG5ldyBkYXRhZnJhbWUKZGYgPC0gbXRjYXJzWywxOjNdCgojIHJlbmFtZSBhbGwgY29sdW1ucwpjb2xuYW1lcyhkZikgPC0gYygibXBnLmFsbCIsICJudW1DeWxpbmRlcnMiLCAiRGlzcGxhY2VtZW50Lmx0ciIpCgojIHJlbmFtZSBhIHNpbmdsZSBjb2x1bW4KY29sbmFtZXMoZGYpWzFdIDwtICJtaVBlckdhIgoKaGVhZChkZiwzKQpgYGAKCiMjIEFkZGluZyBSb3dzIHRvIGEgRGF0YSBGcmFtZQoKU2ltcGx5IGFzc2lnbiBhIG5ldyB2YWx1ZSB0byBhIGNvbHVtbiBpbiBhIHJvdyB0aGF0IGRvZXMgbm90ICh5ZXQpIGV4aXN0LCB3aWxsIGNhdXNlIFIgdG8gYWxsb2NhdGUgYWRkaXRpb25hbCBtZW1vcnkuIFRoZSBjb2RlIGJlbG93IGlsbHVzdHJhdGVzIHRoaXM6CgpgYGB7ciBhZGRSb3cyREZ9CmRmW25yb3coZGYpKzEsM10gPC0gMzQKYGBgCgojIyBDcmVhdGluZyBhIE5ldyBEYXRhIEZyYW1lCgpEYXRhIGZyYW1lcyBhcmUgY3JlYXRlZCBpbiB2YXJpb3VzIHdheXM6CgoxLiAgdXNlIHRoZSBgZGF0YS5mcmFtZWAgZnVuY3Rpb24KMi4gIGxvYWQgYSBDU1YsIFRTViwgb3Igb3RoZXIgdmFsdWUgc2VwYXJhdGVkIGZpbGUKMy4gIGxvYWQgYSBzaW1wbGUgWE1MIGZpbGUKNC4gIGV4ZWN1dGUgYSBTUUwgcXVlcnkKNS4gIHJlc3VsdCBvZiBydW5uaW5nIGEgZnVuY3Rpb24KCiMjIyBMb2FkIGEgRGF0YSBGcmFtZSBmcm9tIENTVgoKTG9hZGluZyBkYXRhIGludG8gZGF0YSBmcmFtZXMgZnJvbSBmaWxlcywgcGFydGljdWxhcmx5IENTViBhbmQgVFNWIGZpbGVzLCBpcyBjb3ZlcmVkIGluIGRldGFpbCBpbiBMZXNzb24gWzYuMTA2IC0tIEltcG9ydCBEYXRhIGludG8gUiBmcm9tIENTViwgVFNWLCBhbmQgRXhjZWwgRmlsZXNdKGh0dHA6Ly9hcnRpZmljaXVtLnVzL2xlc3NvbnMvMDYuci9sLTYtMTA2LWxvYWQtY3N2LXRzdi1leGNlbC1maWxlcy9sLTYtMTA2Lmh0bWwpLCBzbyBjb25zdWx0IHRoYXQgbGVzc29uIGZvciBtb3JlIGRldGFpbHMuIFRoaXMgc2VjdGlvbiBpcyBhIHF1aWNrIG92ZXJ2aWV3IGFuZCBzdW1tYXJ5LgoKVGhlIG1vc3QgY29tbW9ubHkgdXNlZCBmdW5jdGlvbiB0byBsb2FkIGRhdGEgZnJvbSBhIENTViBmaWxlcyBpcyBgcmVhZC5jc3YoKWAgYW5kIGl0IGlzIHBhcnQgb2YgQmFzZSBSLCBzbyBubyBhZGRpdGlvbmFsIHBhY2thZ2VzIGFyZSByZXF1aXJlZC4gVGhpcyBmdW5jdGlvbiBjYW4gbG9hZCBhIENTViBmcm9tIGEgbG9jYWwgZmlsZSBvciBmcm9tIGEgVVJMLgoKVGhlIHBhcmFtZXRlciAqaGVhZGVyID0gRiogaW5zdHJ1Y3RzIDxjb2RlPnJlYWQuY3N2KCk8L2NvZGU+IG5vdCB0byBpbnRlcnByZXQgdGhlIGZpcnN0IGxpbmUgYXMgaGVhZGVyIGxhYmVscy4gT2YgY291cnNlLCBpZiB0aGVyZSBhcmUgbm8gbGFiZWxzLCB0aGVuIHlvdSBuZWVkIHRvIGRlZmluZSB5b3VyIG93bi4KCmBgYHtyfQpkZiA8LSByZWFkLmNzdihmaWxlID0gImN1c3RvbWVydHhuZGF0YS5jc3YiLCBoZWFkZXIgPSBGKQpoZWFkKGRmKQoKZGYgPC0gcmVhZC5jc3YoZmlsZSA9ICJjdXN0b21lcnR4bmRhdGEuY3N2IiwgCiAgICAgICAgICAgICAgIGhlYWRlciA9IEYsCiAgICAgICAgICAgICAgIGNvbC5uYW1lcyA9IGMoIm51bVZpc2l0cyIsIk51bVR4biIsIk9TIiwiR2VuZGVyIiwiVG90U3AiKSkKaGVhZChkZikKYGBgCgo+IE5vdGUgdGhhdCB0aGUgdmFsdWUgb2YgdGhlICdNYWxlJyBjb2x1bW4gaW4gdGhlIGZpcnN0IHJvdyBpcyAqTkEqIHdoaWNoIGlzIHRoZSB3YXkgdGhhdCBSIGluZGljYXRlcyBhIG1pc3NpbmcgZGF0YSB2YWx1ZS4gSXQgaXMgbm90IDAgb3IgYW4gZW1wdHkgc3RyaW5nLCBpdCBpcyB1bmtub3duLiBTbywgc3RhdGlzdGljYWwgZnVuY3Rpb25zIGFuZCBhbGdlYnJhaWMgb3BlcmF0aW9ucyB3b3VsZCByZXN1bHQgaW4gYW4gKk5BKiBhcyB3ZWxsLgoKQXNpZGUgZnJvbSBDU1YgZmlsZXMsIFIgY2FuIGFsc28gbG9hZCBhIG51bWJlciBvZiBvdGhlciBmaWxlIGZvcm1hdCB1c2luZyB2YXJpb3VzIHBhY2thZ2VzLCBpbmNsdWRpbmcgWE1MLCBFeGNlbCwgU1BTUywgTWF0TGFiLCBhbW9uZyBtYW55IG90aGVycy4KCioqTm90YSBCZW5lKio6IENhcGl0YWxpemF0aW9uIGluIHBhdGggYW5kIGZpbGUgbmFtZXMgZG9lcyBub3QgbWF0dGVyIGluIFdpbmRvd3MsIGJ1dCAqKmRvZXMgbWF0dGVyKiogb24gTWFjT1MgYW5kIExpbnV4LiBGdXJ0aGVybW9yZSwgbm90ZSB0aGF0IGV2ZW4gaW4gV2luZG93cyB0aGUgcGF0aCBkZWxpbWl0ZXIgaXMgYSBmb3J3YXJkIHNsYXNoIC8gYW5kIG5vdCB0aGUgdXN1YWwgYmFja3dhcmRzIHNsYXNoIFxcLiBUaGUgXFwgaXMgYW4gImVzY2FwZSIgY2hhcmFjdGVyIGFuZCB1c2VkIHRvIGluamVjdCBub24tcHJpbnRhYmxlIGNoYXJhY3RlcnMgaW50byBhIHN0cmluZyAodGV4dCksICplLmcuKiwgIlRoaXMgc3RyaW5nIGNvbnRhaW5zInF1b3RlcyIuIiB3aGljaCB3b3VsZCBiZSB3cml0dGVuIGluIFIgYXMgInRoaXMgc3RyaW5nIGNvbnRhaW5zIFxcInF1b3Rlc1xcIi4iCgojIyMjIFN0cmluZ3MgdnMgRmFjdG9ycwoKVGhlICpmYWN0b3IqIGRhdGEgdHlwZSBlbmNvZGVzIGNhdGVnb3JpY2FsIGRhdGEsICplLmcuKiwgdGhlIHZhbHVlIG9mIGEgdmFyaWFibGUgaXMgb25lIG9mIGEgZml4ZWQgdmFsdWUgc2V0LiBNYW55IHN0YXRpc3RpY2FsIGZ1bmN0aW9ucyBpbiBSIHJlcXVpcmUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHRvIGJlIG9mIHR5cGUgKmZhY3RvciouIEhvd2V2ZXIsIG9mdGVuLCBkdXJpbmcgZGF0YSBwcm9jZXNzaW5nLCB3ZSBuZWVkIHRoZSBhY3R1YWwgdGV4dCByYXRoZXIgdGhhbiBoYXZpbmcgaXQgZW5jb2RlZCBhcyBhICpmYWN0b3IqICh3aGljaCBpcyBhY3R1YWxseSBzdG9yZWQgaW4gUiBhcyBhbiBpbnRlZ2VyIGZvciBlZmZpY2llbmN5KS4gU28sIHdoZW4gcmVhZGluZyBhIENTViBmaWxlIHlvdSBuZWVkIHRvIGRlY2lkZSBpZiB5b3Ugd2FudCB0ZXh0IGNvbHVtbnMgdG8gYmUgY2hhcmFjdGVyIHN0cmluZ3Mgb3IgZmFjdG9ycyBieSBzZXR0aW5nIHRoZSA8Y29kZT5zdHJpbmdzQXNGYWN0b3JzPC9jb2RlPiBwYXJhbWV0ZXIuCgpZb3UgbWF5IHVzZSBlaXRoZXIgPGNvZGU+RjwvY29kZT4gYW5kIDxjb2RlPlQ8L2NvZGU+IG9yIDxjb2RlPkZBTFNFPC9jb2RlPiBhbmQgPGNvZGU+VFJVRTwvY29kZT4uCgpgYGB7cn0KZGYgPC0gcmVhZC5jc3YoZmlsZSA9ICJjdXN0b21lcnR4bmRhdGEuY3N2IiwgCiAgICAgICAgICAgICAgIGhlYWRlciA9IEYsCiAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgY29sLm5hbWVzID0gYygibnVtVmlzaXRzIiwiTnVtVHhuIiwiT1MiLCJHZW5kZXIiLCJUb3RTcCIpKQpoZWFkKGRmKQpgYGAKCiMjIyBDcmVhdGUgYSBuZXcgRGF0YSBGcmFtZQoKVGhlIGNvZGUgYmVsb3cgY3JlYXRlcyBhIG5ldyBkYXRhIGZyYW1lIGZyb20gY29sdW1uIHZlY3RvcnMuIE5vdGljZSBob3cgdGhlIGNvbHVtbiBuYW1lcyBhcmUgdGhlIG5hbWVzIG9mIHRoZSB2ZWN0b3JzLiBBIG5ldyB2ZWN0b3IgaXMgY3JlYXRlZCB3aXRoIHRoZSA8Y29kZT5jPC9jb2RlPiBmdW5jdGlvbiwgZS5nLiwgPGNvZGU+diBcPC0gYygzLDUsMSw5KTwvY29kZT4uCgpgYGB7cn0KZGYxIDwtIGRhdGEuZnJhbWUoc3RhdGUgPSBjKCdBcml6b25hJywnR2VvcmdpYScsICdOZXcgWW9yaycsJ0luZGlhbmEnLCdXYXNoaW5ndG9uJywnVGV4YXMnKSwKICAgICAgICAgICAgICAgICAgY29kZSA9IGFzLmZhY3RvcihjKCdBWicsJ0dBJywnTlknLCdJTicsJ1dBJywnVFgnKSksCiAgICAgICAgICAgICAgICAgIHNjb3JlID0gYyg2Miw0Nyw1NSw3NCwzMSw4NSkpCgpoZWFkKGRmMSkKCmBgYAoKIyMgU2VhcmNoIERhdGEgRnJhbWVzCgpUaGVyZSBhcmUgdHdvIGltcG9ydGFudCBmdW5jdGlvbnMgZm9yICJzZWFyY2hpbmciIGRhdGEgZnJhbWVzOiA8Y29kZT53aGljaDwvY29kZT4gYW5kIDxjb2RlPmFueTwvY29kZT4uIFRoZSBjb2RlIGJlbG93IHVzZXMgdGhlIGJ1aWx0LWluIFsqKk9yYW5nZSoqIGRhdGEgZnJhbWVdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9kYXRhc2V0cy92ZXJzaW9ucy8zLjYuMi90b3BpY3MvT3JhbmdlKSB3aGljaCBjb250YWlucyBtZWFzdXJlbWVudHMgb2Ygb3JhbmdlIHRyZWVzLiBJdCBoYXMgdGhyZWUgY29sdW1uczogdGhlIHRyZWUsIHRoZSAqYWdlKiBvZiB0aGUgdHJlZSAoZGF5cyBzaW5jZSAxOTY4LzEyLzMxKSwgYW5kICpjaXJjdW1mZXJlbmNlKiAoaW4gKm1tKikuCgojIyMgd2hpY2gKCmBgYHtyfQpkZiA8LSBPcmFuZ2UKCmhlYWQoZGYpCgojIGZpbmQgYWxsIHJvd3Mgd2hlcmUgdGhlIGNpcmN1bWZlcmVuY2UgaXMgbW9yZSB0aGFuIDIwMG1tCnJzIDwtIHdoaWNoKGRmJGNpcmN1bWZlcmVuY2UgPiAyMDApCgojIGRpc3BsYXkgYWxsIHJvd3Mgd2hlcmUgdGhlIGNpcmN1bWZlcmVuY2UgaXMgbW9yZSB0aGFuIDIwMG1tCmRmW3JzLF0KCiMgY29tcG91bmQgY29uZGl0aW9ucyBhcmUgcG9zc2libGUgd2l0aCAmIChhbmQpLCB8IChvciksIGFuZCAhIChub3QpCnJzMiA8LSB3aGljaChkZiRjaXJjdW1mZXJlbmNlID4gMjAwICYgZGYkYWdlIDwgMTUwMCkKcnMzIDwtIHdoaWNoKGRmJGNpcmN1bWZlcmVuY2UgPCAyMDAgfCAhKGRmJGFnZSA8IDE1MDApKQpyczQgPC0gd2hpY2goZGYkY2lyY3VtZmVyZW5jZSA+IDQwMCB8IGRmJGFnZSA+IDE1MDApCgpyczIKcnMzCnJzNAoKbWVhbihkZltyczQsMl0pCm1lYW4oZGYkYWdlW3JzM10pCgpgYGAKCkluIHRoZSBhYm92ZSBleGFtcGxlIDxjb2RlPnJzIFw8LSB3aGljaChkZlwkY2lyY3VtZmVyZW5jZSBcPiAyMDApPC9jb2RlPiBmaW5kcyBhbGwgcm93cyBpbiB0aGUgZGF0YSBmcmFtZSAqZGYqIHdoZXJlICpjaXJjdW1mZXJlbmNlIFw+IDIwMCouIFRoZSByb3dzIGFyZSBzYXZlZCBpbiAqcnMqLgoKIyMjIGFueQoKVGhlIDxjb2RlPmFueTwvY29kZT4gZnVuY3Rpb24gcmV0dXJucyAkVFJVRSQgb3IgJEZBTFNFJCBkZXBlbmRpbmcgb24gd2hldGhlciBhbnkgY29sdW1uIChvciByb3cpIGluIHRoZSBkYXRhZnJhbWUgc2F0aXNmaWVzIGEgQm9vbGVhbiBleHByZXNzaW9uLgoKYGBge3J9CiMgaXMgdGhlcmUgYW55IHRyZWUgd2l0aCBhZ2UgPiAyMDAwPwphbnkoZGYkYWdlID4gMjUpCmBgYAoKIyMgTWVtb3J5IE1hbmFnZW1lbnQKClIgaXMgc2ltaWxhciB0byBQeXRob24gYW5kIG90aGVyIGludGVycHJldGVkIGxhbmd1YWdlcyBpbiB0ZXJtcyBvZiBtZW1vcnkgbWFuYWdlbWVudC4gT2JqZWN0cyBhbmQgdmFyaWFibGVzIHJlbWFpbiBpbiBtZW1vcnkgdW50aWwgeW91IHJlc3RhcnQgUiBvciBleHBsaWNpdGx5IGRlbGV0ZSB0aGVtLiBUaGlzIGNhbiBzb21ldGltZXMgY2F1c2UgY29uZmxpY3RzIGR1cmluZyBkZXZlbG9wbWVudC4gQWRkaW5nIHRoaXMgdG8gdGhlIHN0YXJ0IG9mIGFuIFIgc2NyaXB0IG9yIGFuIFIgTm90ZWJvb2sgZW5zdXJlcyB0aGF0IHRoZSBwcm9ncmFtIHJ1bnMgd2l0aCBhbiBlbXB0eSBtZW1vcnkgZW52aXJvbm1lbnQuIFRoaXMgaXMgY3JpdGljYWwgZm9yIGxhbmd1YWdlcyBsaWtlIFIgYW5kIFB5dGhvbiwgYnV0IGlzIG5vdCBuZWVkZWQgZm9yIHByb2dyYW1taW5nIGxhbmd1YWdlcyB0aGF0IHJ1biBpbiBzZXBhcmF0ZSBwcm9jZXNzZXMgc3VjaCBhcyBKYXZhIGFuZCBDKysgcHJvZ3JhbXMuCgpVc2UgdGhlIGNvZGUgYmVsb3cgdG8gZmluZCBhbmQgdGhlbiBkZWxldGUgYWxsIG9iamVjdHMsIGFuZCByZWNsYWltIG1lbW9yeS4gVGhlIGZ1bmN0aW9uIDxjb2RlPmxzKCk8L2NvZGU+IGxpc3RzIGFsbCBvYmplY3RzICh2YXJpYWJsZXMpIGJ5IG5hbWUsIHdoaWxlIHRoZSA8Y29kZT5ybSgpPC9jb2RlPiByZW1vdmVzIG9uZSBvciBtb3JlIG9iamVjdHMgZnJvbSBtZW1vcnkuIEZpbmFsbHksIHRoZSBmdW5jdGlvbiA8Y29kZT5nYygpPC9jb2RlPiBydW5zIHRoZSBnYXJiYWdlIGNvbGxlY3RvciBhbmQgcmV0dXJucyBmcmVlZCBtZW1vcnkgdG8gdGhlIHVzYWJsZSBtZW1vcnkgcG9vbCBmb3IgdGhlIHByb2Nlc3MgaW4gd2hpY2ggUiBpcyBydW5uaW5nLgoKYGBge3IgY2xlYW5NZW0sIGVjaG89VCwgZXZhbD1GfQpybShsaXN0ID0gbHMoYWxsLm5hbWVzID0gVFJVRSkpCmdjKCkKYGBgCgpPZiBjb3Vyc2UsIHJhdGhlciB0aGFuIGRlbGV0aW5nIGFsbCBvYmplY3RzIGFzIGluIHRoZSBjb2RlIGNodW5rIGFib3ZlLCB5b3UgbWF5IHdpc2ggdG8gcmVsZWFzZSBsYXJnZSBvYmplY3RzIG9yIHVudXNlZCBvYmplY3RzIHNlbGVjdGl2ZWx5IGJ5IHRoZWlyIG5hbWUsICplLmcuKiwgPGNvZGU+cm0oIm9iak5hbWUiKTwvY29kZT4uCgojIyBDb25jbHVzaW9uCgpBcyB5b3Ugc2F3LCBSIGlzIG5vdCBhIGRpZmZpY3VsdCBsYW5ndWFnZSB0byBsZWFybiBhcyBpdCBpcyBzaW1pbGFyIHRvIG90aGVyIGxhbmd1YWdlcyBhbmQgZm9yIG1vc3QgbGFuZ3VhZ2UgY29uc3RydWN0cyB0aGF0IHlvdSBhcmUgZmFtaWxpYXIgd2l0aCwgdGhlcmUgaXMgYW4gZXF1aXZhbGVudC4gQnV0IGl0IGlzIGltcG9ydGFudCB0aGF0IHlvdSBnbyBiZXlvbmQgdGhpcyB0dXRvcmlhbCBhbmQgbGVhcm4gdGhlICJSIHdheSIgb2YgcHJvZ3JhbW1pbmcgdXNpbmcgdmVjdG9yaXplZCBvcGVyYXRpb25zLgoKIyMgVHV0b3JpYWwKClRoZSB2aWRlbyB0dXRvcmlhbCBkZW1vbnN0cmF0ZXMgdGhlIGNvbnN0cnVjdHMgaW50cm9kdWNlZCBpbiB0aGlzIGxlc3Nvbi4KCmBgYHs9aHRtbH0KCjxpZnJhbWUgc3JjPSJodHRwczovL3BsYXllci52aW1lby5jb20vdmlkZW8vOTA0MjAyODQ0P3RpdGxlPTAmYW1wO2J5bGluZT0wJmFtcDtwb3J0cmFpdD0wJmFtcDtiYWRnZT0wJmFtcDthdXRvcGF1c2U9MCZhbXA7cGxheWVyX2lkPTAmYW1wO2FwcF9pZD01ODQ3OSIgd2lkdGg9IjY0MCIgaGVpZ2h0PSIzNjAiIGZyYW1lYm9yZGVyPSIwIiBhbGxvdz0iYXV0b3BsYXk7IGZ1bGxzY3JlZW47IHBpY3R1cmUtaW4tcGljdHVyZSIgdGl0bGU9Ildvcmtpbmcgd2l0aCBWZWN0b3JzIGFuZCBEYXRhIEZyYW1lcyBpbiBSIiBkYXRhLWV4dGVybmFsPSIxIj48L2lmcmFtZT4KYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIEZpbGVzICYgUmVzb3VyY2VzCgpgYGB7ciB6aXBGaWxlcywgZWNobz1GQUxTRX0KemlwTmFtZSA9IHNwcmludGYoIkxlc3NvbkZpbGVzLSVzLSVzLnppcCIsIAogICAgICAgICAgICAgICAgIHBhcmFtcyRjYXRlZ29yeSwKICAgICAgICAgICAgICAgICBwYXJhbXMkbnVtYmVyKQoKdGV4dEFMaW5rID0gcGFzdGUwKCJBbGwgRmlsZXMgZm9yIExlc3NvbiAiLCAKICAgICAgICAgICAgICAgcGFyYW1zJGNhdGVnb3J5LCIuIixwYXJhbXMkbnVtYmVyKQoKIyBkb3dubG9hZEZpbGVzTGluaygpIGlzIGluY2x1ZGVkIGZyb20gX2luc2VydDJEQi5SCmtuaXRyOjpyYXdfaHRtbChkb3dubG9hZEZpbGVzTGluaygiLiIsIHppcE5hbWUsIHRleHRBTGluaykpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBSZWZlcmVuY2VzCgpObyByZWZlcmVuY2VzLgoKIyMgRXJyYXRhCgpbTGV0IHVzIGtub3ddKGh0dHBzOi8vZm9ybS5qb3Rmb3JtLmNvbS8yMTIxODcwNzI3ODQxNTcpe3RhcmdldD0iX2JsYW5rIn0uCg==