1 Data Frames

A data frame is one of the most commonly used data structure in R (see the tibble object in the Data Wrangling lab), they are just a list of equal length vectors. Each vector is treated as a column and elements of the vectors as rows.

Most often a data frame will be constructed by reading in from a file, but we can also create them from scratch as done below:

df <- data.frame(x = 1:3, y = c("a", "b", "c"))
str(df)
'data.frame':   3 obs. of  2 variables:
 $ x: int  1 2 3
 $ y: Factor w/ 3 levels "a","b","c": 1 2 3

It is extremely important to understand that data frames are simply a special form of a list:

typeof(df)
[1] "list"
attributes(df)
$`names`
[1] "x" "y"

$class
[1] "data.frame"

$row.names
[1] 1 2 3
attributes(df)
$`names`
[1] "x" "y"

$class
[1] "data.frame"

$row.names
[1] 1 2 3

2 Strings (Characters) vs Factors

By default R will convert character vectors into factors when they are included in a data frame.

Sometimes this is useful, sometimes usually it isn’t – either way it is important to know what type/class you are working with. This behavior can be changed using the stringsAsFactors argument.

df <- data.frame(x = 1:3, y = c("a", "b", "c"), stringsAsFactors = FALSE)
str(df)
'data.frame':   3 obs. of  2 variables:
 $ x: int  1 2 3
 $ y: chr  "a" "b" "c"

2.0.1 Some general stringsAsFactors advice

You almost always want stringsAsFactors = FALSE

Here is a little background


3 Length Coercion

As we have seen before, if a vector is shorter than expected, R will increase the length by repeating elements of the short vector. If the lengths are evenly divisible this will occur without any output / warning.

For data frames if the lengths are not evenly divisible then there will be an error.

data.frame(x = 1:3, y = c("a"))
data.frame(x = 1:3, y = c("a","b"))
Error in data.frame(x = 1:3, y = c("a", "b")) : 
  arguments imply differing number of rows: 3, 2

4 Growing data frames

We can add rows or columns to a data frame using rbind and cbind respectively.

df <- data.frame(x = 1:3, y = c("a", "b", "c"))
str(rbind(df, c(TRUE, FALSE)))
invalid factor level, NA generated
'data.frame':   4 obs. of  2 variables:
 $ x: int  1 2 3 1
 $ y: Factor w/ 3 levels "a","b","c": 1 2 3 NA
str(cbind(df, z = TRUE))
'data.frame':   3 obs. of  3 variables:
 $ x: int  1 2 3
 $ y: Factor w/ 3 levels "a","b","c": 1 2 3
 $ z: logi  TRUE TRUE TRUE
str(cbind(df, z = TRUE))
'data.frame':   3 obs. of  3 variables:
 $ x: int  1 2 3
 $ y: Factor w/ 3 levels "a","b","c": 1 2 3
 $ z: logi  TRUE TRUE TRUE
str(cbind(df, z = TRUE))
'data.frame':   3 obs. of  3 variables:
 $ x: int  1 2 3
 $ y: Factor w/ 3 levels "a","b","c": 1 2 3
 $ z: logi  TRUE TRUE TRUE
# Good
str(rbind(cbind(df1, df2), list(1, "a", 1, 1)))
'data.frame':   4 obs. of  4 variables:
 $ x: num  1 2 3 1
 $ y: Factor w/ 3 levels "a","b","c": 1 2 3 1
 $ m: num  3 2 1 1
 $ n: num  1 1 0 1

5 Exercise 1

1. Construct a data frame that contains the following data (in as efficient a manner as possible). *Hint:* the `rep` function should prove useful (`?rep`).
Patient Gender Treatment 1 Treatment 2 Treatment 3
1 Male Yes Yes Yes
2 Male Yes Yes No
3 Male Yes No Yes
4 Male Yes No No
5 Male No Yes Yes
6 Male No Yes No
7 Male No No Yes
8 Male No No No
9 Female Yes Yes Yes
10 Female Yes Yes No
11 Female Yes No Yes
12 Female Yes No No
13 Female No Yes Yes
14 Female No Yes No
15 Female No No Yes
16 Female No No No
2. Why does the following fail, i.e. should fail IMHO?
df <- data.frame(x = 1:3, group = factor(c("A","B","C")))
rbind(df, c(4, "D"))
invalid factor level, NA generated

6 Matrices

A matrix is a 2-dimensional equivalent of an numeric vector, in that all entries must be of the same type.

(m <- matrix(c(1, 2, 3, 4), ncol = 2, nrow = 2))
     [,1] [,2]
[1,]    1    3
[2,]    2    4
str(m)
 num [1:2, 1:2] 1 2 3 4
attributes(m)
$`dim`
[1] 2 2

7 Column major ordering

A matrix is therefore just a vector with a dim attribute where the data is stored in column major order (fill the first column starting at row one, then the next column and so on).

Data in a matrix is always stored in this format but we can fill by rows using the byrow argument

(cm <- matrix(1:4,  ncol = 2, nrow = 2))
     [,1] [,2]
[1,]    1    3
[2,]    2    4
c(cm)
[1] 1 2 3 4
(rm <- matrix(1:4,  ncol = 2, nrow = 2, byrow = TRUE))
     [,1] [,2]
[1,]    1    2
[2,]    3    4
c(rm)
[1] 1 3 2 4

8 Naming dimensions

x <- array(1:8, c(2, 2, 2))
rownames(x) <- LETTERS[1:2]
colnames(x) <- LETTERS[3:4]
dimnames(x)[[3]] <- LETTERS[5:6]
x
, , E

  C D
A 1 3
B 2 4

, , F

  C D
A 5 7
B 6 8
str(x)
 int [1:2, 1:2, 1:2] 1 2 3 4 5 6 7 8
 - attr(*, "dimnames")=List of 3
  ..$ : chr [1:2] "A" "B"
  ..$ : chr [1:2] "C" "D"
  ..$ : chr [1:2] "E" "F"

9 Subsetting

9.1 Subsetting in General

R has several different subsetting operators ([, [[, and $).

The behavior of these operators will depend on the object they are being used with.

In general there are 6 different data types that can be used to subset:

  * Positive integers
  * Negative integers
  * Logical values
  * Empty / NULL
  * Zero
  * Character values (names)

10 Subsetting Vectors

10.1 Positive Integer subsetting

Returns elements at the given location(s) (note R uses a 1-based not a 0-based indexing scheme).

x <- c(1, 4, 7)
x[c(1, 3)]
[1] 1 7
x[c(1, 1)]
[1] 1 1
x[c(1.9, 2.1)]
[1] 1 4
x[c(1.9, 2.1)]
[1] 1 4
str( y[c(1, 1)] )
List of 2
 $ : num 1
 $ : num 1
str( y[c(1.9, 2.1)] )
List of 2
 $ : num 1
 $ : num 4

10.2 Negative Integer subsetting

Excludes elements at the given location

x <- c(1, 4, 7)
x[-1]
[1] 4 7
x[-c(1, 3)]
[1] 4
x[c(-1, -1)]
[1] 4 7
y <- list(1, 4, 7)
str( y[-1] )
List of 2
 $ : num 4
 $ : num 7
str( y[-c(1, 3)] )
List of 1
 $ : num 4
x[c(-1, 2)]
Error in x[c(-1, 2)] : only 0's may be mixed with negative subscripts

11 Logical Value Subsetting

Returns elements that correspond to TRUE in the logical vector. Length of the logical vector is expected to be the same of the vector being subsetted.

x <- c(1, 4, 7, 12)
x[c(TRUE, TRUE, FALSE, TRUE)]
[1]  1  4 12
x[c(TRUE, FALSE)]
[1] 1 7
x[x %% 2 == 0]
[1]  4 12
y <- list(1, 4, 7, 12)
str( y[c(TRUE, TRUE, FALSE, TRUE)] )
List of 3
 $ : num 1
 $ : num 4
 $ : num 12
str( y[c(TRUE, FALSE)] )
List of 2
 $ : num 1
 $ : num 7
str( y[y %% 2 == 0] )
Error in y%%2 : non-numeric argument to binary operator

12 Empty Subsetting

Returns the original vector.

x <- c(1, 4, 7)
x[]
[1] 1 4 7
y <- list(1, 4, 7)
str(y[])
List of 3
 $ : num 1
 $ : num 4
 $ : num 7

13 Zero subsetting

Returns an empty vector of the same type as the vector being subseted.

x = c(1, 4, 7)
x[0]
numeric(0)
y = list(1, 4, 7)
str(y[0])
 list()
x[c(0, 1)]
[1] 1
y[c(0, 1)]
[[1]]
[1] 1

14 Character subsetting

If the vector has names, select elements whose names correspond to the character vector.

x <- c(a = 1, b = 4, c = 7)
x["a"]
a 
1 
x[c("a","a")]
a a 
1 1 
x[c("b","c")]
b c 
4 7 
y <- list(a = 1, b = 4, c = 7)
str(y["a"])
List of 1
 $ a: num 1
str(y[c("a", "a")])
List of 2
 $ a: num 1
 $ a: num 1
str(y[c("b", "c")])
List of 2
 $ b: num 4
 $ c: num 7

15 Out of bound subsetting

x <- c(1, 4, 7)
x[4]
[1] NA
x["a"]
[1] NA
x[c(1,4)]
[1]  1 NA
x[c(1,4)]
[1]  1 NA
x[c(1,4)]
[1]  1 NA
str(y[c(1,4)])
List of 2
 $ : num 1
 $ : NULL

16 Missing and NULL subsetting

x = c(1,4,7)
x[NA]
[1] NA NA NA
x[NULL]
numeric(0)
x[c(1,NA)]
[1]  1 NA
y = list(1,4,7)
str(y[NA])
List of 3
 $ : NULL
 $ : NULL
 $ : NULL
str(y[NULL])
 list()

17 Vectors: [ vs. [[

[[ subsets like [ except it can only subset a single value.

x <- c(a = 1, b = 4, c = 7)
x[[1]]
[1] 1
x[["a"]]
[1] 1
x[[1:2]]
Error in x[[1:2]] : 
  attempt to select more than one element in vectorIndex

18 Generic Vectors: [ vs. [[

Subsets a single value, but returns that value - not a list containing that value.

y <- list(a = 1, b = 4, c = 7)
y[2]
$`b`
[1] 4
y[[2]]
[1] 4
y[["b"]]
[1] 4
y[[1:2]]
Error in y[[1:2]] : subscript out of bounds

19 Hadley’s Analogy

20 Vectors: [[ vs. $

$ is equivalent to [[ but it only works for named lists, by default it uses partial matching (exact = FALSE).

x <- c("abc" = 1, "def" = 5)
x$abc
Error in x$abc : $ operator is invalid for atomic vectors
y <- list("abc" = 1, "def" = 5)
y[["abc"]]
[1] 1
y$abc
[1] 1
y$d
[1] 5

21 A Common ‘Gotcha’

Why does the following code not work?

x <- list(abc = 1:10, def = 10:1)
y <- "abc"
x$y
NULL
x$y ⇔ x[[“y”]] ≠ x[[y]]
x[[y]]
 [1]  1  2  3  4  5  6  7  8  9 10

22 Exercise 2

Below are 100 values:

x <- c(56, 3, 17, 2, 4, 9, 6, 5, 19, 5, 2, 3, 5, 0, 13, 12,
       6, 31, 10, 21, 8, 4, 1, 1, 2, 5, 16, 1, 3, 8, 1,
       3, 4, 8, 5, 2, 8, 6, 18, 40, 10, 20, 1, 27, 2, 11, 14,
       5, 7, 0, 3, 0, 7, 0, 8, 10, 10, 12, 8, 82,
       21, 3, 34, 55, 18, 2, 9, 29, 1, 4, 7, 14, 7, 1, 2, 7, 4,
       74, 5, 0, 3, 13, 2, 8, 1, 6, 13, 7, 1, 10,
       5, 2, 4, 4, 14, 15, 4, 17, 1, 9)

Write down how you would create a subset to accomplish each of the following:

  1. Select every third value starting at position 2 in x.
  2. Remove all values with an odd index (e.g. 1, 3, etc.)
  3. Select only the values that are primes. (You may assume all values are less than 100)
  4. Remove every 4th value, but only if it is odd.

23 Subsetting Matrices, Data Frames, and Arrays

23.0.1 Subsetting Matrices

(x <- matrix(1:6, nrow = 2, ncol = 3, dimnames = list(c("A", "B"), c("M", "N", "O"))))
  M N O
A 1 3 5
B 2 4 6
x[1,3]
[1] 5
x[1:2, 1:2]
  M N
A 1 3
B 2 4
x[, 1:2]
  M N
A 1 3
B 2 4
x[-1,-3]
M N 
2 4 
x["A","M"]
[1] 1
x["A", c("M","O")]
M O 
1 5 
x[, "C"]
Error in x[, "C"] : subscript out of bounds
x[1,"M"]
[1] 1
x["B",]
M N O 
2 4 6 
x["B"]
[1] NA
x[-1]
[1] 2 3 4 5 6

24 Preserving Subsetting

By default R’s [ subset operator is a preserving subset operator, in that the returned object will have the same type as the parent. Confusingly, when used with a matrix or array [ becomes a simplifying operator (does not preserve type) - this behavior can be controlled by the drop argument.

x <- matrix(1:6, nrow = 2, ncol = 3, dimnames = list(c("A", "B"), c("M", "N", "O")))
x[1, ]
M N O 
1 3 5 
x[1, , drop = TRUE]
M N O 
1 3 5 
x[1, , drop = FALSE]
  M N O
A 1 3 5
str(x[1, ])
 Named int [1:3] 1 3 5
 - attr(*, "names")= chr [1:3] "M" "N" "O"
str(x[1, , drop = TRUE])
 Named int [1:3] 1 3 5
 - attr(*, "names")= chr [1:3] "M" "N" "O"
str(x[1, , drop = FALSE])
 int [1, 1:3] 1 3 5
 - attr(*, "dimnames")=List of 2
  ..$ : chr "A"
  ..$ : chr [1:3] "M" "N" "O"

25 Preserving vs Simplifying Subsets

Type Simplifying Preserving
Vector x[[1]] x[1]
List x[[1]] x[1]
Array x[1, ] x[1, , drop = FALSE]
x[, 1] x[, 1, drop = FALSE]
Factor x[1:4, drop = TRUE] ` x[1:4]`
Data frame x[, 1] x[, 1, drop = FALSE]
x[[1]] x[1]

26 Back to Hadley’s Analogy

27 Factor Subsetting

(x <- factor(c("BS", "MS", "PhD", "MS")))
[1] BS  MS  PhD MS 
Levels: BS MS PhD
x[1:2]
[1] BS MS
Levels: BS MS PhD
x[1:2, drop = TRUE]
[1] BS MS
Levels: BS MS

28 Data Frame Subsetting

If provided with a single value, data frames assume you want to subset a column or columns - multiple values then the data frame is treated as a matrix.

df <- data.frame(a = 1:2, b = 3:4)
df[1]
df[[1]]
[1] 1 2
df[, "a"]
[1] 1 2
df["a"]
df[, "a", drop = FALSE]
df[1, ]
df[c("a", "b", "a")]

29 Subsetting and assignment

Subsets can also be used with assignment to update specific values within an object.

x <- c(1, 4, 7)
x[2] <- 2
x
[1] 1 2 7
x[x %% 2 != 0] <- x[x %% 2 != 0] + 1
x
[1] 2 2 8
x[c(1, 1)] <- c(2, 3)
x
[1] 3 2 8
x[c(1, 1)] <- c(2, 3)
x
[1] 3 2 8
x[c(1, 1)] <- c(2, 3)
x
[1] 3 2 8
x[c(-1, -3)] <- 3
x
[1] 1 3 1 3 3 3
x[] <- 6:1
x
[1] 6 5 4 3 2 1

30 Deleting list (df) elements

df <- data.frame(a = 1:2, b = TRUE, c = c("A", "B"))
df[["b"]] <- NULL
str(df)
'data.frame':   2 obs. of  2 variables:
 $ a: int  1 2
 $ c: Factor w/ 2 levels "A","B": 1 2
df[, "c"] = NULL
str(df)
'data.frame':   2 obs. of  1 variable:
 $ a: int  1 2

31 Subsets of Subsets

df <- data.frame(a = c(5, 1, NA, 3))
df$a[df$a == 5] <- 0
df[["a"]][df[["a"]] == 1] <- 0
df[1][df[1] == 3] <- 0
df

32 Exercise 3

grades <- data.frame(
            student = c("Alice", "Bob", "Carol", "Dan", "Eve", "Frank",
                        "Mallory", "Oscar", "Peggy", "Sam", "Wendy"),
            grade   = c(82, 78, 62, 98, 64, 53, 86, 73, 54, 57, 61),
            year    = c(3L, 2L, 2L, 1L, 3L, 3L, 4L, 3L, 2L, 2L, 1L),
            stringsAsFactors = FALSE
         )
1. For the above data frame use subsetting and subsetting assignment to add two new features (columns) to the data set:

  * the student's letter grade (factor vector with labels A - F)
     + A (90-100), B (80-89), C (70-79), D (60-69), F (0-59)
  * the student's passing status the class (logical vector)
     + TRUE for a grade of A, B, or C
     + FALSE for a grade of D or F

These changes should not be hard coded, i.e. if you were given a new data frame with new values, your code should still produce the correct answer.


33 Acknowledgments

Above materials are derived in part from the following sources:

Hadley Wickham: Advanced R


Created on 2019-01-24 by the Rmarkdown package (v1.10) and R version 3.5.1 (2018-07-02).

LS0tDQp0aXRsZTogIlN1YnNldHRpbmcgT2JqZWN0cyBpbiBSIg0KYXV0aG9yOiAiU2ltb24gVGF2ZW5lciBhbmRsIFN0dSBGaWVsZCINCmRhdGU6ICJgciBmb3JtYXQoU3lzLkRhdGUoKSwgJyVlICVCICVZJylgIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogZmFsc2UNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCnJhdGlvOiAnOToxNicNCnRhYmxlczogeWVzDQpmb250c2l6ZTogMTJwdA0KLS0tDQoNCg0KIyBEYXRhIEZyYW1lcw0KDQpBIGRhdGEgZnJhbWUgaXMgb25lIG9mIHRoZSBtb3N0IGNvbW1vbmx5IHVzZWQgZGF0YSBzdHJ1Y3R1cmUgaW4gUiAoc2VlIHRoZSBgdGliYmxlYCBvYmplY3QgaW4gdGhlICpEYXRhIFdyYW5nbGluZyogbGFiKSwgdGhleSBhcmUganVzdCBhIGxpc3Qgb2YgZXF1YWwgbGVuZ3RoIHZlY3RvcnMuIEVhY2ggdmVjdG9yIGlzIHRyZWF0ZWQgYXMgYSBjb2x1bW4gYW5kIGVsZW1lbnRzIG9mIHRoZSB2ZWN0b3JzIGFzIHJvd3MuDQoNCk1vc3Qgb2Z0ZW4gYSBkYXRhIGZyYW1lIHdpbGwgYmUgY29uc3RydWN0ZWQgYnkgcmVhZGluZyBpbiBmcm9tIGEgZmlsZSwgYnV0IHdlIGNhbiBhbHNvIGNyZWF0ZSB0aGVtIGZyb20gc2NyYXRjaCBhcyBkb25lIGJlbG93Og0KDQpgYGB7cn0NCmRmIDwtIGRhdGEuZnJhbWUoeCA9IDE6MywgeSA9IGMoImEiLCAiYiIsICJjIikpDQpzdHIoZGYpDQpgYGANCg0KSXQgaXMgZXh0cmVtZWx5IGltcG9ydGFudCB0byB1bmRlcnN0YW5kIHRoYXQgZGF0YSBmcmFtZXMgYXJlIHNpbXBseSBhIHNwZWNpYWwgZm9ybSBvZiBhIGxpc3Q6DQpgYGB7cn0NCnR5cGVvZihkZikNCmBgYA0KDQoNCmBgYHtyfQ0KYXR0cmlidXRlcyhkZikNCmBgYA0KDQpgYGB7cn0NCmRmMiA8LSBsaXN0KHggPSAxOjMsIHkgPSBmYWN0b3IoYygiYSIsICJiIiwgImMiKSkpDQphdHRyKGRmMiwgImNsYXNzIikgPC0gImRhdGEuZnJhbWUiDQphdHRyKGRmMiwgInJvdy5uYW1lcyIpIDwtIDE6Mw0Kc3RyKGRmMikNCmBgYA0KDQoNCg0KIyBTdHJpbmdzIChDaGFyYWN0ZXJzKSB2cyBGYWN0b3JzDQoNCkJ5IGRlZmF1bHQgUiB3aWxsIGNvbnZlcnQgY2hhcmFjdGVyIHZlY3RvcnMgaW50byBmYWN0b3JzIHdoZW4gdGhleSBhcmUgaW5jbHVkZWQgaW4gYSBkYXRhIGZyYW1lLg0KDQpTb21ldGltZXMgdGhpcyBpcyB1c2VmdWwsIHNvbWV0aW1lcyB1c3VhbGx5IGl0IGlzbid0IC0tIGVpdGhlciB3YXkgaXQgaXMgaW1wb3J0YW50IHRvIGtub3cgd2hhdCB0eXBlL2NsYXNzIHlvdSBhcmUgd29ya2luZyB3aXRoLiBUaGlzIGJlaGF2aW9yIGNhbiBiZSBjaGFuZ2VkIHVzaW5nIHRoZSBgc3RyaW5nc0FzRmFjdG9yc2AgYXJndW1lbnQuDQoNCmBgYHtyfQ0KZGYgPC0gZGF0YS5mcmFtZSh4ID0gMTozLCB5ID0gYygiYSIsICJiIiwgImMiKSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0Kc3RyKGRmKQ0KYGBgDQoNCiMjIyBTb21lIGdlbmVyYWwgYHN0cmluZ3NBc0ZhY3RvcnNgIGFkdmljZQ0KWW91IGFsbW9zdCAqYWx3YXlzKiB3YW50IGBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0VgDQoNCkhlcmUgaXMgYSBsaXR0bGUgW2JhY2tncm91bmRdKGh0dHBzOi8vc2ltcGx5c3RhdGlzdGljcy5vcmcvMjAxNS8wNy8yNC9zdHJpbmdzYXNmYWN0b3JzLWFuLXVuYXV0aG9yaXplZC1iaW9ncmFwaHkvKQ0KDQo8Y2VudGVyPg0KPGltZyBzcmM9ImltYWdlcy9zYXluby5wbmciIGNsYXNzPSJ0aXRsZS1oZXgiPg0KPC9jZW50ZXI+DQoNCg0KDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCg0KIyBMZW5ndGggQ29lcmNpb24NCg0KQXMgd2UgaGF2ZSBzZWVuIGJlZm9yZSwgaWYgYSB2ZWN0b3IgaXMgc2hvcnRlciB0aGFuIGV4cGVjdGVkLCBSIHdpbGwgaW5jcmVhc2UgdGhlIGxlbmd0aCBieSByZXBlYXRpbmcgZWxlbWVudHMgb2YgdGhlIHNob3J0IHZlY3Rvci4gSWYgdGhlIGxlbmd0aHMgYXJlIGV2ZW5seSBkaXZpc2libGUgdGhpcyB3aWxsIG9jY3VyIHdpdGhvdXQgYW55IG91dHB1dCAvIHdhcm5pbmcuDQoNCkZvciBkYXRhIGZyYW1lcyBpZiB0aGUgbGVuZ3RocyBhcmUgbm90IGV2ZW5seSBkaXZpc2libGUgdGhlbiB0aGVyZSB3aWxsIGJlIGFuIGVycm9yLg0KDQpgYGB7cn0NCmRhdGEuZnJhbWUoeCA9IDE6MywgeSA9IGMoImEiKSkNCmBgYA0KYGBge3IsIGVycm9yID0gVFJVRX0NCmRhdGEuZnJhbWUoeCA9IDE6MywgeSA9IGMoImEiLCJiIikpDQpgYGANCg0KDQojIEdyb3dpbmcgZGF0YSBmcmFtZXMNCg0KV2UgY2FuIGFkZCByb3dzIG9yIGNvbHVtbnMgdG8gYSBkYXRhIGZyYW1lIHVzaW5nIGByYmluZGAgYW5kIGBjYmluZGAgcmVzcGVjdGl2ZWx5Lg0KDQpgYGB7cn0NCmRmIDwtIGRhdGEuZnJhbWUoeCA9IDE6MywgeSA9IGMoImEiLCAiYiIsICJjIikpDQpzdHIocmJpbmQoZGYsIGMoVFJVRSwgRkFMU0UpKSkNCmBgYA0KDQpgYGB7cn0NCnN0cihjYmluZChkZiwgeiA9IFRSVUUpKQ0KYGBgDQoNCmBgYHtyfQ0KZGYxIDwtIGRhdGEuZnJhbWUoeCA9IDE6MywgeSA9IGMoImEiLCJiIiwiYyIpKQ0KZGYyIDwtIGRhdGEuZnJhbWUobSA9IDM6MSwgbiA9IGMoVFJVRSwgVFJVRSwgRkFMU0UpKQ0Kc3RyKGNiaW5kKGRmMSwgZGYyKSkNCmBgYA0KDQpgYGB7cn0NCiMgQmFkDQpzdHIocmJpbmQoY2JpbmQoZGYxLCBkZjIpLCBjKDEsICJhIiwgMSwgMSkpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBHb29kDQpzdHIocmJpbmQoY2JpbmQoZGYxLCBkZjIpLCBsaXN0KDEsICJhIiwgMSwgMSkpKQ0KYGBgDQoNCiMgRXhlcmNpc2UgMQ0KDQogICAgMS4gQ29uc3RydWN0IGEgZGF0YSBmcmFtZSB0aGF0IGNvbnRhaW5zIHRoZSBmb2xsb3dpbmcgZGF0YSAoaW4gYXMgZWZmaWNpZW50IGEgbWFubmVyIGFzIHBvc3NpYmxlKS4gKkhpbnQ6KiB0aGUgYHJlcGAgZnVuY3Rpb24gc2hvdWxkIHByb3ZlIHVzZWZ1bCAoYD9yZXBgKS4NCg0KICBQYXRpZW50ICAgIEdlbmRlciAgICAgICAgICBUcmVhdG1lbnQgMSAgICAgVHJlYXRtZW50IDIgICAgIFRyZWF0bWVudCAzDQotLS0tLS0tLS0tIC0tLS0tLS0tLS0tLS0tLSAtLS0tLS0tLS0tLS0tLS0gLS0tLS0tLS0tLS0tLS0tIC0tLS0tLS0tLS0tLS0tLQ0KICAxICAgICAgICAgIE1hbGUgICAgICAgICAgICBZZXMgICAgICAgICAgICAgWWVzICAgICAgICAgICAgIFllcw0KICAyICAgICAgICAgIE1hbGUgICAgICAgICAgICBZZXMgICAgICAgICAgICAgWWVzICAgICAgICAgICAgIE5vDQogIDMgICAgICAgICAgTWFsZSAgICAgICAgICAgIFllcyAgICAgICAgICAgICBObyAgICAgICAgICAgICAgWWVzDQogIDQgICAgICAgICAgTWFsZSAgICAgICAgICAgIFllcyAgICAgICAgICAgICBObyAgICAgICAgICAgICAgTm8NCiAgNSAgICAgICAgICBNYWxlICAgICAgICAgICAgTm8gICAgICAgICAgICAgIFllcyAgICAgICAgICAgICBZZXMNCiAgNiAgICAgICAgICBNYWxlICAgICAgICAgICAgTm8gICAgICAgICAgICAgIFllcyAgICAgICAgICAgICBObw0KICA3ICAgICAgICAgIE1hbGUgICAgICAgICAgICBObyAgICAgICAgICAgICAgTm8gICAgICAgICAgICAgIFllcw0KICA4ICAgICAgICAgIE1hbGUgICAgICAgICAgICBObyAgICAgICAgICAgICAgTm8gICAgICAgICAgICAgIE5vDQogIDkgICAgICAgICAgRmVtYWxlICAgICAgICAgIFllcyAgICAgICAgICAgICBZZXMgICAgICAgICAgICAgWWVzDQogIDEwICAgICAgICAgRmVtYWxlICAgICAgICAgIFllcyAgICAgICAgICAgICBZZXMgICAgICAgICAgICAgTm8NCiAgMTEgICAgICAgICBGZW1hbGUgICAgICAgICAgWWVzICAgICAgICAgICAgIE5vICAgICAgICAgICAgICBZZXMNCiAgMTIgICAgICAgICBGZW1hbGUgICAgICAgICAgWWVzICAgICAgICAgICAgIE5vICAgICAgICAgICAgICBObw0KICAxMyAgICAgICAgIEZlbWFsZSAgICAgICAgICBObyAgICAgICAgICAgICAgWWVzICAgICAgICAgICAgIFllcw0KICAxNCAgICAgICAgIEZlbWFsZSAgICAgICAgICBObyAgICAgICAgICAgICAgWWVzICAgICAgICAgICAgIE5vDQogIDE1ICAgICAgICAgRmVtYWxlICAgICAgICAgIE5vICAgICAgICAgICAgICBObyAgICAgICAgICAgICAgWWVzDQogIDE2ICAgICAgICAgRmVtYWxlICAgICAgICAgIE5vICAgICAgICAgICAgICBObyAgICAgICAgICAgICAgTm8NCg0KICAgIDIuIFdoeSBkb2VzIHRoZSBmb2xsb3dpbmcgZmFpbCwgaS5lLiBzaG91bGQgZmFpbCBJTUhPPw0KYGBge3IgZ3Jvd19kZiwgZXJyb3IgPSBUUlVFfQ0KZGYgPC0gZGF0YS5mcmFtZSh4ID0gMTozLCBncm91cCA9IGZhY3RvcihjKCJBIiwiQiIsIkMiKSkpDQpyYmluZChkZiwgYyg0LCAiRCIpKQ0KYGBgDQoNCg0KIyBNYXRyaWNlcw0KDQpBIG1hdHJpeCBpcyBhIDItZGltZW5zaW9uYWwgZXF1aXZhbGVudCBvZiBhbiBudW1lcmljIHZlY3RvciwgaW4gdGhhdCBhbGwgZW50cmllcyBtdXN0IGJlIG9mIHRoZSBzYW1lIHR5cGUuDQoNCmBgYHtyfQ0KKG0gPC0gbWF0cml4KGMoMSwgMiwgMywgNCksIG5jb2wgPSAyLCBucm93ID0gMikpDQpgYGANCg0KYGBge3J9DQpzdHIobSkNCmBgYA0KDQpgYGB7cn0NCmF0dHJpYnV0ZXMobSkNCmBgYA0KDQojIENvbHVtbiBtYWpvciBvcmRlcmluZw0KDQpBIG1hdHJpeCBpcyB0aGVyZWZvcmUganVzdCBhIHZlY3RvciB3aXRoIGEgYGRpbWAgYXR0cmlidXRlIHdoZXJlIHRoZSBkYXRhIGlzIHN0b3JlZCBpbiBjb2x1bW4gbWFqb3Igb3JkZXIgKGZpbGwgdGhlIGZpcnN0IGNvbHVtbiBzdGFydGluZyBhdCByb3cgb25lLCB0aGVuIHRoZSBuZXh0IGNvbHVtbiBhbmQgc28gb24pLg0KDQpEYXRhIGluIGEgbWF0cml4IGlzIGFsd2F5cyBzdG9yZWQgaW4gdGhpcyBmb3JtYXQgYnV0IHdlIGNhbiBmaWxsIGJ5IHJvd3MgdXNpbmcgdGhlIGBieXJvd2AgYXJndW1lbnQNCg0KYGBge3J9DQooY20gPC0gbWF0cml4KDE6NCwgIG5jb2wgPSAyLCBucm93ID0gMikpDQpgYGANCg0KYGBge3J9DQpjKGNtKQ0KYGBgDQoNCmBgYHtyfQ0KKHJtIDwtIG1hdHJpeCgxOjQsICBuY29sID0gMiwgbnJvdyA9IDIsIGJ5cm93ID0gVFJVRSkpDQpgYGANCg0KYGBge3J9DQpjKHJtKQ0KYGBgDQoNCg0KDQoNCg0KIyBOYW1pbmcgZGltZW5zaW9ucw0KDQpgYGB7cn0NCnggPC0gYXJyYXkoMTo4LCBjKDIsIDIsIDIpKQ0Kcm93bmFtZXMoeCkgPC0gTEVUVEVSU1sxOjJdDQpjb2xuYW1lcyh4KSA8LSBMRVRURVJTWzM6NF0NCmRpbW5hbWVzKHgpW1szXV0gPC0gTEVUVEVSU1s1OjZdDQp4DQpgYGANCg0KDQpgYGB7cn0NCnN0cih4KQ0KYGBgDQoNCg0KDQojIFN1YnNldHRpbmcNCg0KIyMgU3Vic2V0dGluZyBpbiBHZW5lcmFsDQpSIGhhcyBzZXZlcmFsIGRpZmZlcmVudCBzdWJzZXR0aW5nIG9wZXJhdG9ycyAoYFtgLCBgW1tgLCBhbmQgYCRgKS4NCg0KVGhlIGJlaGF2aW9yIG9mIHRoZXNlIG9wZXJhdG9ycyB3aWxsIGRlcGVuZCBvbiB0aGUgb2JqZWN0IHRoZXkgYXJlIGJlaW5nIHVzZWQgd2l0aC4NCg0KSW4gZ2VuZXJhbCB0aGVyZSBhcmUgNiBkaWZmZXJlbnQgZGF0YSB0eXBlcyB0aGF0IGNhbiBiZSB1c2VkIHRvIHN1YnNldDoNCg0KICAgICAgKiBQb3NpdGl2ZSBpbnRlZ2Vycw0KICAgICAgKiBOZWdhdGl2ZSBpbnRlZ2Vycw0KICAgICAgKiBMb2dpY2FsIHZhbHVlcw0KICAgICAgKiBFbXB0eSAvIE5VTEwNCiAgICAgICogWmVybw0KICAgICAgKiBDaGFyYWN0ZXIgdmFsdWVzIChuYW1lcykNCg0KDQojIFN1YnNldHRpbmcgVmVjdG9ycw0KDQojIyBQb3NpdGl2ZSBJbnRlZ2VyIHN1YnNldHRpbmcNClJldHVybnMgZWxlbWVudHMgYXQgdGhlIGdpdmVuIGxvY2F0aW9uKHMpIChub3RlIFIgdXNlcyBhIDEtYmFzZWQgbm90IGEgMC1iYXNlZCBpbmRleGluZyBzY2hlbWUpLg0KDQpgYGB7cn0NCnggPC0gYygxLCA0LCA3KQ0KeFtjKDEsIDMpXQ0KYGBgDQoNCmBgYHtyfQ0KeFtjKDEsIDEpXQ0KYGBgDQoNCmBgYHtyfQ0KeFtjKDEuOSwgMi4xKV0NCmBgYA0KDQoNCg0KYGBge3J9DQp5IDwtIGxpc3QoMSwgNCwgNykNCnN0ciggeVtjKDEsIDMpXSApDQpgYGANCg0KYGBge3J9DQpzdHIoIHlbYygxLCAxKV0gKQ0KYGBgDQoNCmBgYHtyfQ0Kc3RyKCB5W2MoMS45LCAyLjEpXSApDQpgYGANCg0KDQojIyBOZWdhdGl2ZSBJbnRlZ2VyIHN1YnNldHRpbmcNCg0KRXhjbHVkZXMgZWxlbWVudHMgYXQgdGhlIGdpdmVuIGxvY2F0aW9uDQpgYGB7cn0NCnggPC0gYygxLCA0LCA3KQ0KeFstMV0NCmBgYA0KDQpgYGB7cn0NCnhbLWMoMSwgMyldDQpgYGANCg0KYGBge3J9DQp4W2MoLTEsIC0xKV0NCmBgYA0KDQpgYGB7cn0NCnkgPC0gbGlzdCgxLCA0LCA3KQ0KYGBgDQoNCmBgYHtyfQ0Kc3RyKCB5Wy0xXSApDQpgYGANCg0KYGBge3J9DQpzdHIoIHlbLWMoMSwgMyldICkNCmBgYA0KDQpgYGB7ciwgZXJyb3IgPSBUUlVFfQ0KeFtjKC0xLCAyKV0NCmBgYA0KDQoNCg0KIyBMb2dpY2FsIFZhbHVlIFN1YnNldHRpbmcNCg0KUmV0dXJucyBlbGVtZW50cyB0aGF0IGNvcnJlc3BvbmQgdG8gYFRSVUVgIGluIHRoZSBsb2dpY2FsIHZlY3Rvci4gTGVuZ3RoIG9mIHRoZSBsb2dpY2FsIHZlY3RvciBpcyBleHBlY3RlZCB0byBiZSB0aGUgc2FtZSBvZiB0aGUgdmVjdG9yIGJlaW5nIHN1YnNldHRlZC4NCg0KYGBge3J9DQp4IDwtIGMoMSwgNCwgNywgMTIpDQpgYGANCg0KYGBge3J9DQp4W2MoVFJVRSwgVFJVRSwgRkFMU0UsIFRSVUUpXQ0KYGBgDQoNCmBgYHtyfQ0KeFtjKFRSVUUsIEZBTFNFKV0NCmBgYA0KDQpgYGB7cn0NCnhbeCAlJSAyID09IDBdDQpgYGANCg0KYGBge3J9DQp5IDwtIGxpc3QoMSwgNCwgNywgMTIpDQpzdHIoIHlbYyhUUlVFLCBUUlVFLCBGQUxTRSwgVFJVRSldICkNCmBgYA0KDQoNCmBgYHtyfQ0Kc3RyKCB5W2MoVFJVRSwgRkFMU0UpXSApDQpgYGANCg0KYGBge3IsIGVycm9yID0gVFJVRX0NCnN0ciggeVt5ICUlIDIgPT0gMF0gKQ0KYGBgDQoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCg0KDQojIEVtcHR5IFN1YnNldHRpbmcNCg0KUmV0dXJucyB0aGUgb3JpZ2luYWwgdmVjdG9yLg0KDQpgYGB7cn0NCnggPC0gYygxLCA0LCA3KQ0KeFtdDQpgYGANCg0KYGBge3J9DQp5IDwtIGxpc3QoMSwgNCwgNykNCnN0cih5W10pDQpgYGANCg0KDQojIFplcm8gc3Vic2V0dGluZw0KDQpSZXR1cm5zIGFuIGVtcHR5IHZlY3RvciBvZiB0aGUgc2FtZSB0eXBlIGFzIHRoZSB2ZWN0b3IgYmVpbmcgc3Vic2V0ZWQuDQoNCmBgYHtyfQ0KeCA9IGMoMSwgNCwgNykNCnhbMF0NCmBgYA0KDQpgYGB7cn0NCnkgPSBsaXN0KDEsIDQsIDcpDQpzdHIoeVswXSkNCmBgYA0KDQpgYGB7cn0NCnhbYygwLCAxKV0NCmBgYA0KDQpgYGB7cn0NCnlbYygwLCAxKV0NCmBgYA0KDQoNCg0KIyBDaGFyYWN0ZXIgc3Vic2V0dGluZw0KDQpJZiB0aGUgdmVjdG9yIGhhcyBuYW1lcywgc2VsZWN0IGVsZW1lbnRzIHdob3NlIG5hbWVzIGNvcnJlc3BvbmQgdG8gdGhlIGNoYXJhY3RlciB2ZWN0b3IuDQpgYGB7cn0NCnggPC0gYyhhID0gMSwgYiA9IDQsIGMgPSA3KQ0KeFsiYSJdDQpgYGANCg0KYGBge3J9DQp4W2MoImEiLCJhIildDQpgYGANCg0KYGBge3J9DQp4W2MoImIiLCJjIildDQpgYGANCg0KYGBge3J9DQp5IDwtIGxpc3QoYSA9IDEsIGIgPSA0LCBjID0gNykNCnN0cih5WyJhIl0pDQpgYGANCg0KYGBge3J9DQpzdHIoeVtjKCJhIiwgImEiKV0pDQpgYGANCg0KYGBge3J9DQpzdHIoeVtjKCJiIiwgImMiKV0pDQpgYGANCg0KDQoNCiMgT3V0IG9mIGJvdW5kIHN1YnNldHRpbmcNCg0KYGBge3J9DQp4IDwtIGMoMSwgNCwgNykNCnhbNF0NCmBgYA0KDQpgYGB7cn0NCnhbImEiXQ0KYGBgDQoNCmBgYHtyfQ0KeFtjKDEsNCldDQpgYGANCg0KDQpgYGB7cn0NCnkgPSBsaXN0KDEsNCw3KQ0Kc3RyKHlbNF0pDQpgYGANCg0KDQpgYGB7cn0NCnN0cih5WyJhIl0pDQpgYGANCg0KYGBge3J9DQpzdHIoeVtjKDEsNCldKQ0KYGBgDQoNCg0KDQojIE1pc3NpbmcgYW5kIE5VTEwgc3Vic2V0dGluZw0KDQpgYGB7cn0NCnggPSBjKDEsNCw3KQ0KeFtOQV0NCmBgYA0KDQpgYGB7cn0NCnhbTlVMTF0NCmBgYA0KDQpgYGB7cn0NCnhbYygxLE5BKV0NCmBgYA0KDQpgYGB7cn0NCnkgPSBsaXN0KDEsNCw3KQ0Kc3RyKHlbTkFdKQ0KYGBgDQoNCg0KYGBge3J9DQpzdHIoeVtOVUxMXSkNCmBgYA0KDQoNCiMgVmVjdG9yczogYFtgIHZzLiBgW1tgDQoNCmBbW2Agc3Vic2V0cyBsaWtlIGBbYCBleGNlcHQgaXQgY2FuIG9ubHkgc3Vic2V0IGEgc2luZ2xlIHZhbHVlLg0KDQpgYGB7cn0NCnggPC0gYyhhID0gMSwgYiA9IDQsIGMgPSA3KQ0KeFtbMV1dDQpgYGANCg0KYGBge3J9DQp4W1siYSJdXQ0KYGBgDQoNCmBgYHtyLCBlcnJvciA9IFRSVUV9DQp4W1sxOjJdXQ0KYGBgDQoNCg0KDQojIEdlbmVyaWMgVmVjdG9yczogYFtgIHZzLiBgW1tgDQoNClN1YnNldHMgYSBzaW5nbGUgdmFsdWUsIGJ1dCByZXR1cm5zIHRoYXQgdmFsdWUgLSBub3QgYSBsaXN0IGNvbnRhaW5pbmcgdGhhdCB2YWx1ZS4NCg0KYGBge3J9DQp5IDwtIGxpc3QoYSA9IDEsIGIgPSA0LCBjID0gNykNCnlbMl0NCmBgYA0KDQpgYGB7cn0NCnlbWzJdXQ0KYGBgDQoNCmBgYHtyfQ0KeVtbImIiXV0NCmBgYA0KDQpgYGB7ciwgZXJyb3IgPSBUUlVFfQ0KeVtbMToyXV0NCmBgYA0KDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KDQoNCiMgSGFkbGV5J3MgQW5hbG9neQ0KDQohW10oaW1hZ2VzL3BlcHBlci5wbmcpDQoNCg0KIyBWZWN0b3JzOiBgW1tgIHZzLiBgJGANCg0KYCRgIGlzIGVxdWl2YWxlbnQgdG8gYFtbYCBidXQgaXQgb25seSB3b3JrcyBmb3IgbmFtZWQgbGlzdHMsIGJ5IGRlZmF1bHQgaXQgdXNlcyBwYXJ0aWFsIG1hdGNoaW5nIChgZXhhY3QgPSBGQUxTRWApLg0KDQpgYGB7ciwgZXJyb3IgPSBUUlVFfQ0KeCA8LSBjKCJhYmMiID0gMSwgImRlZiIgPSA1KQ0KeCRhYmMNCmBgYA0KDQpgYGB7cn0NCnkgPC0gbGlzdCgiYWJjIiA9IDEsICJkZWYiID0gNSkNCnlbWyJhYmMiXV0NCmBgYA0KDQpgYGB7cn0NCnkkYWJjDQpgYGANCg0KYGBge3J9DQp5JGQNCmBgYA0KDQoNCg0KIyBBIENvbW1vbiAnR290Y2hhJw0KDQpXaHkgZG9lcyB0aGUgZm9sbG93aW5nIGNvZGUgbm90IHdvcms/DQoNCmBgYHtyfQ0KeCA8LSBsaXN0KGFiYyA9IDE6MTAsIGRlZiA9IDEwOjEpDQp5IDwtICJhYmMiDQpgYGANCg0KYGBge3J9DQp4JHkNCmBgYA0KDQoNCjxjZW50ZXI+DQp4JHkg4oeUIHhbWyJ5Il1dIOKJoCB4W1t5XV0NCjwvY2VudGVyPg0KDQoNCmBgYHtyfQ0KeFtbeV1dDQpgYGANCg0KDQoNCiMgRXhlcmNpc2UgMg0KDQpCZWxvdyBhcmUgMTAwIHZhbHVlczoNCmBgYHtyfQ0KeCA8LSBjKDU2LCAzLCAxNywgMiwgNCwgOSwgNiwgNSwgMTksIDUsIDIsIDMsIDUsIDAsIDEzLCAxMiwNCiAgICAgICA2LCAzMSwgMTAsIDIxLCA4LCA0LCAxLCAxLCAyLCA1LCAxNiwgMSwgMywgOCwgMSwNCiAgICAgICAzLCA0LCA4LCA1LCAyLCA4LCA2LCAxOCwgNDAsIDEwLCAyMCwgMSwgMjcsIDIsIDExLCAxNCwNCiAgICAgICA1LCA3LCAwLCAzLCAwLCA3LCAwLCA4LCAxMCwgMTAsIDEyLCA4LCA4MiwNCiAgICAgICAyMSwgMywgMzQsIDU1LCAxOCwgMiwgOSwgMjksIDEsIDQsIDcsIDE0LCA3LCAxLCAyLCA3LCA0LA0KICAgICAgIDc0LCA1LCAwLCAzLCAxMywgMiwgOCwgMSwgNiwgMTMsIDcsIDEsIDEwLA0KICAgICAgIDUsIDIsIDQsIDQsIDE0LCAxNSwgNCwgMTcsIDEsIDkpDQpgYGANCg0KV3JpdGUgZG93biBob3cgeW91IHdvdWxkIGNyZWF0ZSBhIHN1YnNldCB0byBhY2NvbXBsaXNoIGVhY2ggb2YgdGhlIGZvbGxvd2luZzoNCg0KICAgICAgMS4gU2VsZWN0IGV2ZXJ5IHRoaXJkIHZhbHVlIHN0YXJ0aW5nIGF0IHBvc2l0aW9uIDIgaW4geC4NCiAgICAgIDIuIFJlbW92ZSBhbGwgdmFsdWVzIHdpdGggYW4gb2RkIGluZGV4IChlLmcuIDEsIDMsIGV0Yy4pDQogICAgICAzLiBTZWxlY3Qgb25seSB0aGUgdmFsdWVzIHRoYXQgYXJlIHByaW1lcy4gKFlvdSBtYXkgYXNzdW1lIGFsbCB2YWx1ZXMgYXJlIGxlc3MgdGhhbiAxMDApDQogICAgICA0LiBSZW1vdmUgZXZlcnkgNHRoIHZhbHVlLCBidXQgb25seSBpZiBpdCBpcyBvZGQuDQoNCg0KDQoNCiMgU3Vic2V0dGluZyBNYXRyaWNlcywgRGF0YSBGcmFtZXMsIGFuZCBBcnJheXMNCg0KIyMjIFN1YnNldHRpbmcgTWF0cmljZXMNCg0KYGBge3J9DQooeCA8LSBtYXRyaXgoMTo2LCBucm93ID0gMiwgbmNvbCA9IDMsIGRpbW5hbWVzID0gbGlzdChjKCJBIiwgIkIiKSwgYygiTSIsICJOIiwgIk8iKSkpKQ0KYGBgDQoNCmBgYHtyfQ0KeFsxLDNdDQpgYGANCg0KYGBge3J9DQp4WzE6MiwgMToyXQ0KYGBgDQoNCmBgYHtyfQ0KeFssIDE6Ml0NCmBgYA0KDQpgYGB7cn0NCnhbLTEsLTNdDQpgYGANCg0KYGBge3J9DQp4WyJBIiwiTSJdDQpgYGANCg0KYGBge3J9DQp4WyJBIiwgYygiTSIsIk8iKV0NCmBgYA0KDQpgYGB7ciwgZXJyb3IgPSBUUlVFfQ0KeFssICJDIl0NCmBgYA0KDQpgYGB7cn0NCnhbMSwiTSJdDQpgYGANCg0KYGBge3J9DQp4WyJCIixdDQpgYGANCg0KYGBge3J9DQp4WyJCIl0NCmBgYA0KDQpgYGB7cn0NCnhbLTFdDQpgYGANCg0KDQoNCiMgUHJlc2VydmluZyBTdWJzZXR0aW5nDQoNCkJ5IGRlZmF1bHQgUidzIGBbYCBzdWJzZXQgb3BlcmF0b3IgaXMgYSBwcmVzZXJ2aW5nIHN1YnNldCBvcGVyYXRvciwgaW4gdGhhdCB0aGUgcmV0dXJuZWQgb2JqZWN0IHdpbGwgaGF2ZSB0aGUgc2FtZSB0eXBlIGFzIHRoZSBwYXJlbnQuIENvbmZ1c2luZ2x5LCB3aGVuIHVzZWQgd2l0aCBhIG1hdHJpeCBvciBhcnJheSBgW2AgYmVjb21lcyBhIHNpbXBsaWZ5aW5nIG9wZXJhdG9yIChkb2VzIG5vdCBwcmVzZXJ2ZSB0eXBlKSAtIHRoaXMgYmVoYXZpb3IgY2FuIGJlIGNvbnRyb2xsZWQgYnkgdGhlIGBkcm9wYCBhcmd1bWVudC4NCg0KYGBge3J9DQp4IDwtIG1hdHJpeCgxOjYsIG5yb3cgPSAyLCBuY29sID0gMywgZGltbmFtZXMgPSBsaXN0KGMoIkEiLCAiQiIpLCBjKCJNIiwgIk4iLCAiTyIpKSkNCnhbMSwgXQ0KYGBgDQoNCmBgYHtyfQ0KeFsxLCAsIGRyb3AgPSBUUlVFXQ0KYGBgDQoNCmBgYHtyfQ0KeFsxLCAsIGRyb3AgPSBGQUxTRV0NCmBgYA0KDQpgYGB7cn0NCnN0cih4WzEsIF0pDQpgYGANCg0KYGBge3J9DQpzdHIoeFsxLCAsIGRyb3AgPSBUUlVFXSkNCmBgYA0KDQpgYGB7cn0NCnN0cih4WzEsICwgZHJvcCA9IEZBTFNFXSkNCmBgYA0KDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KDQoNCiMgUHJlc2VydmluZyB2cyBTaW1wbGlmeWluZyBTdWJzZXRzDQoNClR5cGUgICAgICAgICBTaW1wbGlmeWluZyAgICAgICAgICAgICAgUHJlc2VydmluZw0KLS0tLS0tLS0tLS0gIC0tLS0tLS0tLS0tLS0tLSAgICAgICAgICAtLS0tLS0tLS0tLS0NClZlY3RvcgkgICAgIGB4W1sxXV1gCSAgICAgICAgICAgICAgYHhbMV1gDQpMaXN0CSAgICAgICAgYHhbWzFdXWAgICAgICAgICAgICAgICAgYHhbMV1gDQpBcnJheQkgICAgICAgIGB4WzEsIF1gICAgICAgICAgICAgICAgIGB4WzEsICwgZHJvcCA9IEZBTFNFXWANCiAgICAgICAgICAgICAgYHhbLCAxXWAJICAgICAgICAgICAgICBgeFssIDEsIGRyb3AgPSBGQUxTRV1gDQpGYWN0b3IJICAgICBgeFsxOjQsIGRyb3AgPSBUUlVFXWAgICBgeFsxOjRdYA0KRGF0YSBmcmFtZSAgICBgeFssIDFdYCAgICAgICAgICAgICAgICBgeFssIDEsIGRyb3AgPSBGQUxTRV1gDQogICAgICAgICAgICAgIGB4W1sxXV1gICAgICAgICAgICAgICAgIGB4WzFdYA0KDQoNCiMgQmFjayB0byBIYWRsZXkncyBBbmFsb2d5DQoNCiFbXShpbWFnZXMvcGVwcGVyLnBuZykNCg0KDQojIEZhY3RvciBTdWJzZXR0aW5nDQoNCmBgYHtyfQ0KKHggPC0gZmFjdG9yKGMoIkJTIiwgIk1TIiwgIlBoRCIsICJNUyIpKSkNCmBgYA0KDQpgYGB7cn0NCnhbMToyXQ0KYGBgDQoNCmBgYHtyfQ0KeFsxOjIsIGRyb3AgPSBUUlVFXQ0KYGBgDQoNCg0KDQojIERhdGEgRnJhbWUgU3Vic2V0dGluZw0KDQpJZiBwcm92aWRlZCB3aXRoIGEgc2luZ2xlIHZhbHVlLCBkYXRhIGZyYW1lcyBhc3N1bWUgeW91IHdhbnQgdG8gc3Vic2V0IGEgY29sdW1uIG9yIGNvbHVtbnMgLSBtdWx0aXBsZSB2YWx1ZXMgdGhlbiB0aGUgZGF0YSBmcmFtZSBpcyB0cmVhdGVkIGFzIGEgbWF0cml4Lg0KDQpgYGB7cn0NCmRmIDwtIGRhdGEuZnJhbWUoYSA9IDE6MiwgYiA9IDM6NCkNCmRmWzFdDQpgYGANCg0KYGBge3J9DQpkZltbMV1dDQpgYGANCg0KYGBge3J9DQpkZlssICJhIl0NCmBgYA0KDQpgYGB7cn0NCmRmWyJhIl0NCmBgYA0KDQpgYGB7cn0NCmRmWywgImEiLCBkcm9wID0gRkFMU0VdDQpgYGANCg0KYGBge3J9DQpkZlsxLCBdDQpgYGANCg0KYGBge3J9DQpkZltjKCJhIiwgImIiLCAiYSIpXQ0KYGBgDQoNCg0KDQojIFN1YnNldHRpbmcgYW5kIGFzc2lnbm1lbnQNCg0KU3Vic2V0cyBjYW4gYWxzbyBiZSB1c2VkIHdpdGggYXNzaWdubWVudCB0byB1cGRhdGUgc3BlY2lmaWMgdmFsdWVzIHdpdGhpbiBhbiBvYmplY3QuDQoNCmBgYHtyfQ0KeCA8LSBjKDEsIDQsIDcpDQp4WzJdIDwtIDINCngNCmBgYA0KDQpgYGB7cn0NCnhbeCAlJSAyICE9IDBdIDwtIHhbeCAlJSAyICE9IDBdICsgMQ0KeA0KYGBgDQoNCmBgYHtyfQ0KeFtjKDEsIDEpXSA8LSBjKDIsIDMpDQp4DQpgYGANCg0KYGBge3J9DQp4IDwtIDE6Ng0KeFtjKDIsIE5BKV0gPC0gMQ0KeA0KYGBgDQoNCmBgYHtyfQ0KeFtjKFRSVUUsIE5BKV0gPC0gMQ0KeA0KYGBgDQoNCmBgYHtyfQ0KeFtjKC0xLCAtMyldIDwtIDMNCngNCmBgYA0KDQpgYGB7cn0NCnhbXSA8LSA2OjENCngNCmBgYA0KDQoNCiMgRGVsZXRpbmcgbGlzdCAoZGYpIGVsZW1lbnRzDQoNCmBgYHtyfQ0KZGYgPC0gZGF0YS5mcmFtZShhID0gMToyLCBiID0gVFJVRSwgYyA9IGMoIkEiLCAiQiIpKQ0KZGZbWyJiIl1dIDwtIE5VTEwNCnN0cihkZikNCmBgYA0KDQpgYGB7cn0NCmRmWywgImMiXSA9IE5VTEwNCnN0cihkZikNCmBgYA0KDQoNCiMgU3Vic2V0cyBvZiBTdWJzZXRzDQoNCmBgYHtyfQ0KZGYgPC0gZGF0YS5mcmFtZShhID0gYyg1LCAxLCBOQSwgMykpDQpkZiRhW2RmJGEgPT0gNV0gPC0gMA0KZGZbWyJhIl1dW2RmW1siYSJdXSA9PSAxXSA8LSAwDQpkZlsxXVtkZlsxXSA9PSAzXSA8LSAwDQpkZg0KYGBgDQoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoNCiMgRXhlcmNpc2UgMw0KDQpgYGB7cn0NCmdyYWRlcyA8LSBkYXRhLmZyYW1lKA0KICAgICAgICAgICAgc3R1ZGVudCA9IGMoIkFsaWNlIiwgIkJvYiIsICJDYXJvbCIsICJEYW4iLCAiRXZlIiwgIkZyYW5rIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJNYWxsb3J5IiwgIk9zY2FyIiwgIlBlZ2d5IiwgIlNhbSIsICJXZW5keSIpLA0KICAgICAgICAgICAgZ3JhZGUgICA9IGMoODIsIDc4LCA2MiwgOTgsIDY0LCA1MywgODYsIDczLCA1NCwgNTcsIDYxKSwNCiAgICAgICAgICAgIHllYXIgICAgPSBjKDNMLCAyTCwgMkwsIDFMLCAzTCwgM0wsIDRMLCAzTCwgMkwsIDJMLCAxTCksDQogICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UNCiAgICAgICAgICkNCmBgYA0KDQogICAgMS4gRm9yIHRoZSBhYm92ZSBkYXRhIGZyYW1lIHVzZSBzdWJzZXR0aW5nIGFuZCBzdWJzZXR0aW5nIGFzc2lnbm1lbnQgdG8gYWRkIHR3byBuZXcgZmVhdHVyZXMgKGNvbHVtbnMpIHRvIHRoZSBkYXRhIHNldDoNCg0KICAgICAgKiB0aGUgc3R1ZGVudCdzIGxldHRlciBncmFkZSAoZmFjdG9yIHZlY3RvciB3aXRoIGxhYmVscyBBIC0gRikNCiAgICAgICAgICsgQSAoOTAtMTAwKSwgQiAoODAtODkpLCBDICg3MC03OSksIEQgKDYwLTY5KSwgRiAoMC01OSkNCiAgICAgICogdGhlIHN0dWRlbnQncyBwYXNzaW5nIHN0YXR1cyB0aGUgY2xhc3MgKGxvZ2ljYWwgdmVjdG9yKQ0KICAgICAgICAgKyBUUlVFIGZvciBhIGdyYWRlIG9mIEEsIEIsIG9yIEMNCiAgICAgICAgICsgRkFMU0UgZm9yIGEgZ3JhZGUgb2YgRCBvciBGDQoNClRoZXNlIGNoYW5nZXMgc2hvdWxkIG5vdCBiZSBoYXJkIGNvZGVkLCBpLmUuIGlmIHlvdSB3ZXJlIGdpdmVuIGEgKipuZXcqKiBkYXRhIGZyYW1lIHdpdGggbmV3IHZhbHVlcywgeW91ciBjb2RlIHNob3VsZCBzdGlsbCBwcm9kdWNlIHRoZSBjb3JyZWN0IGFuc3dlci4NCg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBBY2tub3dsZWRnbWVudHMNCg0KQWJvdmUgbWF0ZXJpYWxzIGFyZSBkZXJpdmVkIGluIHBhcnQgZnJvbSB0aGUgZm9sbG93aW5nIHNvdXJjZXM6DQoNCkhhZGxleSBXaWNraGFtOiBbQWR2YW5jZWQgUl0oaHR0cDovL2Fkdi1yLmhhZC5jby5uei9TdWJzZXR0aW5nLmh0bWwpDQoNCg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KQ3JlYXRlZCBvbiBgciBTeXMuRGF0ZSgpYCBieSB0aGUgW1JtYXJrZG93biBwYWNrYWdlXShodHRwczovL2dpdGh1Yi5jb20vcnN0dWRpby9ybWFya2Rvd24pICh2YHIgdXRpbHM6OnBhY2thZ2VWZXJzaW9uKCJybWFya2Rvd24iKWApIGFuZCBgciBSLnZlcnNpb24kdmVyc2lvbi5zdHJpbmdgLg0K