• Introduction
  • Vectors
  • Creating a Vector
  • Size of Vectors
  • Vector Operations
    • Example: Dot Product
  • Functions on Vectors
  • Accessing Elements in a Vector
  • Testing Predicate Expressions
  • Finding Matches
  • Determining Any Matches
  • Accessing Rows, Columns, and Elements (Cells) of a Data Frame
  • Adding Columns to a Data Frame
  • Change Column Names
  • Adding Rows to a Data Frame
  • Creating a New Data Frame
    • Load a Data Frame from CSV
    • Create a new Data Frame
  • Search Data Frames
    • which
    • any
  • Memory Management
  • Conclusion
  • Tutorial
  • Files & Resources
  • References
  • Errata

Introduction

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.

print(v)
##  [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 (<, >, <=, >=, ==, !=)

v < 5
##  [1]  TRUE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE
(v < 1 | v > 9)
##  [1] FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
(v <= 7 & v != 3)
##  [1]  TRUE  TRUE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE
v != 5
##  [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
l <- (v == 5)
print(l)
##  [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.

any(v < 5)
## [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.

str(mtcars)
## '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 ...
head(mtcars[1:4], 3)
##               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
c <- mtcars[3,]
c[1,3]
## [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
w <- mtcars$mpg
mean(w)
## 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:

df[nrow(df)+1,3] <- 34

Creating a New Data Frame

Data frames are created in various ways:

  1. use the data.frame function
  2. load a CSV, TSV, or other value separated file
  3. load a simple XML file
  4. execute a SQL query
  5. 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

df <- Orange

head(df)
## 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
rs3
##  [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
rs4
## [1]  7 14 21 28 35
mean(df[rs4,2])
## [1] 1582
mean(df$age[rs3])
## [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.


Files & Resources

All Files for Lesson 6.103

References

No references.

Errata

Let us know.

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==