2. Data structures
R is designed to handle experimental data
- Although the basic unit of R is a vector, we usually handle data in data frames.
- A data frame is a set of observations of a set of variables – in other words, the outcome of an experiment.
- For example, we might want to analyse information about a set of patients.
- To start with, let’s say we have ten patients and for each one we know their name, sex, age, weight and whether they give consent for their data to be made public.
- We are going to create a data frame called ‘patients’, which will have ten rows (observations) and seven columns (variables). The columns must all be equal lengths.
- We will explore how to construct these data from scratch.
- (in practice, we would usually import such data from a file)
1 |
Adam |
Jones |
Adam Jones |
Male |
50 |
70.8 |
TRUE |
2 |
Eve |
Parker |
Eve Parker |
Female |
21 |
67.9 |
TRUE |
3 |
John |
Evans |
John Evans |
Male |
35 |
75.3 |
FALSE |
4 |
Mary |
Davis |
Mary Davis |
Female |
45 |
61.9 |
TRUE |
5 |
Peter |
Baker |
Peter Baker |
Male |
28 |
72.4 |
FALSE |
6 |
Paul |
Daniels |
Paul Daniels |
Male |
31 |
69.9 |
FALSE |
7 |
Joanna |
Edwards |
Joanna Edwards |
Female |
42 |
63.5 |
FALSE |
8 |
Matthew |
Smith |
Matthew Smith |
Male |
33 |
71.5 |
TRUE |
9 |
David |
Roberts |
David Roberts |
Male |
57 |
73.2 |
FALSE |
10 |
Sally |
Wilson |
Sally Wilson |
Female |
62 |
64.8 |
TRUE |
Character, numeric and logical data types
- Each column is a vector, like previous vectors we have seen, for example:
age <- c(50, 21, 35, 45, 28, 31, 42, 33, 57, 62)
weight <- c(70.8, 67.9, 75.3, 61.9, 72.4, 69.9,
63.5, 71.5, 73.2, 64.8)
- We can define the names using character vectors:
firstName <- c("Adam", "Eve", "John", "Mary",
"Peter", "Paul", "Joanna", "Matthew",
"David", "Sally")
secondName <- c("Jones", "Parker", "Evans", "Davis",
"Baker","Daniels", "Edwards", "Smith",
"Roberts", "Wilson")
Notice how a particular line of R code can be typed over multiple lines. R won’t execute the code until it sees the closing bracket )
that matches the initial bracket (
) - We often use this trick to make our code easier to read
- We also have a new type of vector, the logical vector, which only contains the values
TRUE
and FALSE
:
consent <- c(TRUE, TRUE, FALSE, TRUE, FALSE,
FALSE, FALSE, TRUE, FALSE, TRUE)
- Vectors can only contain one type of data; we cannot mix numbers, characters and logical values in the same vector.
- If we try this, R will convert everything to characters:
c(20, "a string", TRUE)
[1] "20" "a string" "TRUE"
- We can see the type of a particular vector using the
class()
function:
class(firstName)
[1] "character"
class(age)
[1] "numeric"
class(weight)
[1] "numeric"
class(consent)
[1] "logical"
Factors
- Character vectors are fine for some variables, like names. But sometimes we have categorical data and we want R to recognize this
- A factor is R’s data structure for categorical data:
sex <- c("Male", "Female", "Male", "Female", "Male",
"Male", "Female", "Male", "Male", "Female")
sex
[1] "Male" "Female" "Male" "Female" "Male" "Male" "Female" "Male" "Male"
[10] "Female"
factor(sex)
[1] Male Female Male Female Male Male Female Male Male Female
Levels: Female Male
- R has converted the strings of the sex character vector into two levels, which are the categories in the data
- Note the values of this factor are not character strings, but levels
- We can use this factor later-on to compare data for males and females
Creating a data frame (first attempt)
- We can construct a data frame from other objects (N.B. The
paste()
function joins character vectors together)
patients <- data.frame(firstName, secondName,
paste(firstName, secondName),
sex, age, weight, consent)
patients
Naming data frame variables
- We can access particular variables using the ‘
$
’ operator:
- TIP: you can use TAB-complete to select the variable you want
patients$age
[1] 50 21 35 45 28 31 42 33 57 62
- R has inferred the names of our data frame variables from the names of the vectors or the commands (e.g. the
paste()
command)
- We can name the variables after we have created a data frame using the
names()
function, and we can use the same function to see the names:
names(patients) <- c("First_Name", "Second_Name",
"Full_Name", "Sex", "Age",
"Weight", "Consent")
names(patients)
[1] "First_Name" "Second_Name" "Full_Name" "Sex" "Age"
[6] "Weight" "Consent"
- Or we can name the variables when we define the data frame
patients <- data.frame(First_Name = firstName,
Second_Name = secondName,
Full_Name = paste(firstName,
secondName),
Sex = sex,
Age = age,
Weight = weight,
Consent = consent)
names(patients)
[1] "First_Name" "Second_Name" "Full_Name" "Sex" "Age"
[6] "Weight" "Consent"
Factors in data frames
- When creating a data frame, R assumes all character vectors should be categorical variables and converts them to factors. This is not always what we want:
- e.g. we are unlikely to be interested in the hypothesis that people called Adam are taller, so it seems a bit silly to represent this as a factor
patients$First_Name
[1] Adam Eve John Mary Peter Paul Joanna Matthew David Sally
Levels: Adam David Eve Joanna John Mary Matthew Paul Peter Sally
- We can avoid this by asking R not to treat strings as factors, and then explicitly stating when we want a factor by using
factor()
:
patients <- data.frame(First_Name = firstName,
Second_Name = secondName,
Full_Name = paste(firstName,
secondName),
Sex = factor(sex),
Age = age,
Weight = weight,
Consent = consent,
stringsAsFactors = FALSE)
patients
patients$Sex
[1] Male Female Male Female Male Male Female Male Male Female
Levels: Female Male
patients$First_Name
[1] "Adam" "Eve" "John" "Mary" "Peter" "Paul" "Joanna" "Matthew"
[9] "David" "Sally"
Removing variables
Now that we are happy with our data frame, we no longer have any use for the vectors that were used to create it
- R has a function called
rm
that will allow us to remove variables
rm(age)
Once something has been removed, we can no longer use it
age
Multiple objects can be removed at the same time
rm(list = c("age","firstName","secondName","sex","weight","consent"))
Adding additional columns
Recall that we can create a new variable using an assignment operator and specifying a name that R isn’t currently using as a variable name
myNewVariable <- 42
myNewVariable
[1] 42
We use a similar trick to define new columns in the data frame - The value you assign must be the same length as the number of rows in the data frame.
patients$ID
NULL
patients$ID <- paste("Patient", 1:10)
patients
Indexing data frames and matrices
- You can index multidimensional data structures like matrices and data frames using commas:
object[rows, colums]
- Try and predict what each of the following commands will do:-
patients[2,1]
[1] "Eve"
patients[1,2]
[1] "Jones"
patients[1,1:3]
- If you don’t provide an index for either rows or columns, all of the rows or columns will be returned.
patients[1,]
- Rows or columns can be omitted by putting a
-
in front of the index
patients[,-1]
patients[-c(5,7),]
Advanced indexing
- Indices are actually vectors, and can be numeric or logical:
- We won’t always know in advance which indices we want to return
- we might want all values that exceed a particular value or satisfy some other criteria
- In this example,
letters
is a vector containing all letters in the English alphabet
letters
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t"
[21] "u" "v" "w" "x" "y" "z"
s <- letters[1:5]
s
[1] "a" "b" "c" "d" "e"
So far we have seen how to extract the first and third values in the vector
s[c(1,3)]
[1] "a" "c"
R can perform the same operation using a vector of logical values. Only indices with a TRUE
value will get returned
s[c(TRUE, FALSE, TRUE, FALSE, FALSE)]
[1] "a" "c"
- We can do the logical test and indexing in the same line of R code
- R will do the test first, and then use the vector of
TRUE
and FALSE
values to subset the vector
a <- 1:5
a < 3
[1] TRUE TRUE FALSE FALSE FALSE
s[a < 3]
[1] "a" "b"
Logical Operators
- Operators allow us to combine multiple logical tests
- comparison operators
<, >, <=, >=, ==, !=
- logical operators
!, &, |, xor
- The operators for ‘comparison’ and ‘logical’ always return logical values! i.e. (
TRUE
, FALSE
)
s[a > 1 & a <3]
[1] "b"
s[a == 2]
[1] "b"
The vector that you use to perform the logical test could be extracted from a data frame
- which could then be used to subset the data frame
patients$First_Name == "Peter"
[1] FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
patients[patients$First_Name == "Peter",]
Exercise: Exercise 2
- Write R code to print the following subsets of the patients data frame
- The first and second rows, and the first and second colums
1 |
Adam |
Jones |
2 |
Eve |
Parker |
HINT: you can use the seq
function that we saw earlier to define a vector of even numbers
2 |
Eve |
Parker |
Eve Parker |
Female |
21 |
67.9 |
TRUE |
4 |
Mary |
Davis |
Mary Davis |
Female |
45 |
61.9 |
TRUE |
6 |
Paul |
Daniels |
Paul Daniels |
Male |
31 |
69.9 |
FALSE |
8 |
Matthew |
Smith |
Matthew Smith |
Male |
33 |
71.5 |
TRUE |
10 |
Sally |
Wilson |
Sally Wilson |
Female |
62 |
64.8 |
TRUE |
- All rows except the last one, all columns
HINT: the nrow
function will give the number of rows in the data frame
1 |
Adam |
Jones |
Adam Jones |
Male |
50 |
70.8 |
TRUE |
2 |
Eve |
Parker |
Eve Parker |
Female |
21 |
67.9 |
TRUE |
3 |
John |
Evans |
John Evans |
Male |
35 |
75.3 |
FALSE |
4 |
Mary |
Davis |
Mary Davis |
Female |
45 |
61.9 |
TRUE |
5 |
Peter |
Baker |
Peter Baker |
Male |
28 |
72.4 |
FALSE |
6 |
Paul |
Daniels |
Paul Daniels |
Male |
31 |
69.9 |
FALSE |
7 |
Joanna |
Edwards |
Joanna Edwards |
Female |
42 |
63.5 |
FALSE |
8 |
Matthew |
Smith |
Matthew Smith |
Male |
33 |
71.5 |
TRUE |
9 |
David |
Roberts |
David Roberts |
Male |
57 |
73.2 |
FALSE |
- Use logical indexing to select the following patients from the data frame:
- Patients under 40
- Patients who give consent to share their data
- Men who weigh as much or more than the average European male (70.8 kg)
age <- c(50, 21, 35, 45, 28, 31, 42, 33, 57, 62)
weight <- c(70.8, 67.9, 75.3, 61.9, 72.4, 69.9,
63.5, 71.5, 73.2, 64.8)
firstName <- c("Adam", "Eve", "John", "Mary",
"Peter", "Paul", "Joanna", "Matthew",
"David", "Sally")
secondName <- c("Jones", "Parker", "Evans", "Davis",
"Baker","Daniels", "Edwards", "Smith",
"Roberts", "Wilson")
consent <- c(TRUE, TRUE, FALSE, TRUE, FALSE,
FALSE, FALSE, TRUE, FALSE, TRUE)
sex <- c("Male", "Female", "Male", "Female", "Male",
"Male", "Female", "Male", "Male", "Female")
patients <- data.frame(First_Name = firstName,
Second_Name = secondName,
Full_Name = paste(firstName,
secondName),
Sex = factor(sex),
Age = age,
Weight = weight,
Consent = consent,
stringsAsFactors = FALSE)
rm(list = c("firstName","secondName","sex","weight","consent"))
patients
### Your Answer ###
(Supplementary) Matrices
- Data frames are R’s speciality, but R also handles matrices:
- All columns are assumed to contain the same data type, e.g. numerical
- Matrices can be manipulated in the same fashion as data frame
- We can easily convert between the two object types
e <- matrix(1:10, nrow=5, ncol=2)
e
[,1] [,2]
[1,] 1 6
[2,] 2 7
[3,] 3 8
[4,] 4 9
[5,] 5 10
- Some calculations are more efficient to do on matrices, e.g.:
rowMeans(e)
[1] 3.5 4.5 5.5 6.5 7.5
Matrices (and indeed data frames) can be joined together using the functions cbind
and rbind
Let’s first create some example data
mat1 <- matrix(11:20, nrow=5,ncol=2)
mat1
[,1] [,2]
[1,] 11 16
[2,] 12 17
[3,] 13 18
[4,] 14 19
[5,] 15 20
mat2 <- matrix(21:30, nrow=5, ncol=2)
mat2
[,1] [,2]
[1,] 21 26
[2,] 22 27
[3,] 23 28
[4,] 24 29
[5,] 25 30
mat3 <- matrix(31:40,nrow=5,ncol=2)
mat3
[,1] [,2]
[1,] 31 36
[2,] 32 37
[3,] 33 38
[4,] 34 39
[5,] 35 40
and now try out these functions:-
cbind(mat1,mat2)
[,1] [,2] [,3] [,4]
[1,] 11 16 21 26
[2,] 12 17 22 27
[3,] 13 18 23 28
[4,] 14 19 24 29
[5,] 15 20 25 30
rbind(mat1,mat3)
[,1] [,2]
[1,] 11 16
[2,] 12 17
[3,] 13 18
[4,] 14 19
[5,] 15 20
[6,] 31 36
[7,] 32 37
[8,] 33 38
[9,] 34 39
[10,] 35 40
LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIFNvbHZpbmcgQmlvbG9naWNhbCBQcm9ibGVtcyBVc2luZyBSIC0gRGF5IDEiCmF1dGhvcjogTWFyayBEdW5uaW5nLCBTdXJhaiBNZW5vbiBhbmQgQWlvcmEgWmFiYWxhLiBPcmlnaW5hbCBtYXRlcmlhbCBieSBSb2JlcnQgU3Rvam5pxIcsCiAgTGF1cmVudCBHYXR0bywgUm9iIEZveSwgSm9obiBEYXZleSwgRMOhdmlkIE1vbG7DoXIgYW5kIElhbiBSb2JlcnRzCmRhdGU6ICdgciBmb3JtYXQoU3lzLnRpbWUoKSwgIkxhc3QgbW9kaWZpZWQ6ICVkICViICVZIilgJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwotLS0KCiMgMi4gRGF0YSBzdHJ1Y3R1cmVzCgojIyBSIGlzIGRlc2lnbmVkIHRvIGhhbmRsZSBleHBlcmltZW50YWwgZGF0YQoKLSBBbHRob3VnaCB0aGUgYmFzaWMgdW5pdCBvZiBSIGlzIGEgdmVjdG9yLCB3ZSB1c3VhbGx5IGhhbmRsZSBkYXRhIGluICoqZGF0YSBmcmFtZXMqKi4KLSBBIGRhdGEgZnJhbWUgaXMgYSBzZXQgb2Ygb2JzZXJ2YXRpb25zIG9mIGEgc2V0IG9mIHZhcmlhYmxlcyAtLSBpbiBvdGhlciB3b3JkcywgdGhlIG91dGNvbWUgb2YgYW4gZXhwZXJpbWVudC4KLSBGb3IgZXhhbXBsZSwgd2UgbWlnaHQgd2FudCB0byBhbmFseXNlIGluZm9ybWF0aW9uIGFib3V0IGEgc2V0IG9mIHBhdGllbnRzLiAKLSBUbyBzdGFydCB3aXRoLCBsZXQncyBzYXkgd2UgaGF2ZSB0ZW4gcGF0aWVudHMgYW5kIGZvciBlYWNoIG9uZSB3ZSBrbm93IHRoZWlyIG5hbWUsIHNleCwgYWdlLCB3ZWlnaHQgYW5kIHdoZXRoZXIgdGhleSBnaXZlIGNvbnNlbnQgZm9yIHRoZWlyIGRhdGEgdG8gYmUgbWFkZSBwdWJsaWMuCi0gV2UgYXJlIGdvaW5nIHRvIGNyZWF0ZSBhIGRhdGEgZnJhbWUgY2FsbGVkICdwYXRpZW50cycsIHdoaWNoIHdpbGwgaGF2ZSB0ZW4gcm93cyAob2JzZXJ2YXRpb25zKSBhbmQgc2V2ZW4gY29sdW1ucyAodmFyaWFibGVzKS4gVGhlIGNvbHVtbnMgbXVzdCBhbGwgYmUgZXF1YWwgbGVuZ3Rocy4gCi0gV2Ugd2lsbCBleHBsb3JlIGhvdyB0byBjb25zdHJ1Y3QgdGhlc2UgZGF0YSBmcm9tIHNjcmF0Y2guCiAgICArIChpbiBwcmFjdGljZSwgd2Ugd291bGQgdXN1YWxseSBpbXBvcnQgc3VjaCBkYXRhIGZyb20gYSBmaWxlKQogICAgCnwgIHxGaXJzdF9OYW1lfFNlY29uZF9OYW1lfEZ1bGxfTmFtZXxTZXggfEFnZXxXZWlnaHQgfENvbnNlbnR8CnwtLXwtLS0tLS0tfC0tLS0tLS18LS0tLS0tLS0tLS0tLS18Oi0tLS06fC0tOnwtLS0tLS06fDotLS0tLTp8CnwxIHxBZGFtICAgfEpvbmVzICB8QWRhbSBKb25lcyAgICB8ICBNYWxlfDUwIHwgIDcwLjggfCAgIFRSVUV8CnwyIHxFdmUgICAgfFBhcmtlciB8RXZlIFBhcmtlciAgICB8RmVtYWxlfDIxIHwgIDY3LjkgfCAgIFRSVUV8CnwzIHxKb2huICAgfEV2YW5zICB8Sm9obiBFdmFucyAgICB8ICBNYWxlfDM1IHwgIDc1LjMgfCAgRkFMU0V8Cnw0IHxNYXJ5ICAgfERhdmlzICB8TWFyeSBEYXZpcyAgICB8RmVtYWxlfDQ1IHwgIDYxLjkgfCAgIFRSVUV8Cnw1IHxQZXRlciAgfEJha2VyICB8UGV0ZXIgQmFrZXIgICB8ICBNYWxlfDI4IHwgIDcyLjQgfCAgRkFMU0V8Cnw2IHxQYXVsICAgfERhbmllbHN8UGF1bCBEYW5pZWxzICB8ICBNYWxlfDMxIHwgIDY5LjkgfCAgRkFMU0V8Cnw3IHxKb2FubmEgfEVkd2FyZHN8Sm9hbm5hIEVkd2FyZHN8RmVtYWxlfDQyIHwgIDYzLjUgfCAgRkFMU0V8Cnw4IHxNYXR0aGV3fFNtaXRoICB8TWF0dGhldyBTbWl0aCB8ICBNYWxlfDMzIHwgIDcxLjUgfCAgIFRSVUV8Cnw5IHxEYXZpZCAgfFJvYmVydHN8RGF2aWQgUm9iZXJ0cyB8ICBNYWxlfDU3IHwgIDczLjIgfCAgRkFMU0V8CnwxMHxTYWxseSAgfFdpbHNvbiB8U2FsbHkgV2lsc29uICB8RmVtYWxlfDYyIHwgIDY0LjggfCAgIFRSVUV8CgojIyBDaGFyYWN0ZXIsIG51bWVyaWMgYW5kIGxvZ2ljYWwgZGF0YSB0eXBlcwoKLSBFYWNoIGNvbHVtbiBpcyBhIHZlY3RvciwgbGlrZSBwcmV2aW91cyB2ZWN0b3JzIHdlIGhhdmUgc2VlbiwgZm9yIApleGFtcGxlOiAKCmBgYHtyfQphZ2UgICAgPC0gYyg1MCwgMjEsIDM1LCA0NSwgMjgsIDMxLCA0MiwgMzMsIDU3LCA2MikKd2VpZ2h0IDwtIGMoNzAuOCwgNjcuOSwgNzUuMywgNjEuOSwgNzIuNCwgNjkuOSwgCiAgICAgICAgICAgIDYzLjUsIDcxLjUsIDczLjIsIDY0LjgpCgpgYGAKCi0gV2UgY2FuIGRlZmluZSB0aGUgbmFtZXMgdXNpbmcgY2hhcmFjdGVyIHZlY3RvcnM6CgpgYGB7cn0KZmlyc3ROYW1lICA8LSBjKCJBZGFtIiwgIkV2ZSIsICJKb2huIiwgIk1hcnkiLAogICAgICAgICAgICAgICAgIlBldGVyIiwgIlBhdWwiLCAiSm9hbm5hIiwgIk1hdHRoZXciLAogICAgICAgICAgICAgICAgIkRhdmlkIiwgIlNhbGx5IikKc2Vjb25kTmFtZSA8LSBjKCJKb25lcyIsICJQYXJrZXIiLCAiRXZhbnMiLCAiRGF2aXMiLAogICAgICAgICAgICAgICAgIkJha2VyIiwiRGFuaWVscyIsICJFZHdhcmRzIiwgIlNtaXRoIiwgCiAgICAgICAgICAgICAgICAiUm9iZXJ0cyIsICJXaWxzb24iKQpgYGAKCk5vdGljZSBob3cgYSBwYXJ0aWN1bGFyIGxpbmUgb2YgUiBjb2RlIGNhbiBiZSB0eXBlZCBvdmVyIG11bHRpcGxlIGxpbmVzLiBSIHdvbid0IGV4ZWN1dGUgdGhlIGNvZGUgdW50aWwgaXQgc2VlcyB0aGUgY2xvc2luZyBicmFja2V0IGApYCB0aGF0IG1hdGNoZXMgdGhlIGluaXRpYWwgYnJhY2tldCBgKGApCi0gV2Ugb2Z0ZW4gdXNlIHRoaXMgdHJpY2sgdG8gbWFrZSBvdXIgY29kZSBlYXNpZXIgdG8gcmVhZAoKLSBXZSBhbHNvIGhhdmUgYSBuZXcgdHlwZSBvZiB2ZWN0b3IsIHRoZSAqKipsb2dpY2FsKioqIHZlY3Rvciwgd2hpY2ggb25seSAKY29udGFpbnMgdGhlIHZhbHVlcyBgVFJVRWAgYW5kIGBGQUxTRWA6CgpgYGB7cn0KY29uc2VudCA8LSBjKFRSVUUsIFRSVUUsIEZBTFNFLCBUUlVFLCBGQUxTRSwgCiAgICAgICAgICAgICBGQUxTRSwgRkFMU0UsIFRSVUUsIEZBTFNFLCBUUlVFKQpgYGAKCgotIFZlY3RvcnMgY2FuIG9ubHkgY29udGFpbiBvbmUgdHlwZSBvZiBkYXRhOyB3ZSBjYW5ub3QgbWl4IG51bWJlcnMsIGNoYXJhY3RlcnMgYW5kIGxvZ2ljYWwgdmFsdWVzIGluIHRoZSBzYW1lIHZlY3Rvci4gCiAgICArIElmIHdlIHRyeSB0aGlzLCBSIHdpbGwgY29udmVydCBldmVyeXRoaW5nIHRvIGNoYXJhY3RlcnM6CgpgYGB7cn0KYygyMCwgImEgc3RyaW5nIiwgVFJVRSkKCmBgYAoKLSBXZSBjYW4gc2VlIHRoZSB0eXBlIG9mIGEgcGFydGljdWxhciB2ZWN0b3IgdXNpbmcgdGhlICoqYGNsYXNzKClgKiogZnVuY3Rpb246CgpgYGB7cn0KIGNsYXNzKGZpcnN0TmFtZSkKIGNsYXNzKGFnZSkKIGNsYXNzKHdlaWdodCkKIGNsYXNzKGNvbnNlbnQpCmBgYAoKIyNGYWN0b3JzCgotIENoYXJhY3RlciB2ZWN0b3JzIGFyZSBmaW5lIGZvciBzb21lIHZhcmlhYmxlcywgbGlrZSBuYW1lcy4gQnV0IHNvbWV0aW1lcyB3ZSBoYXZlIGNhdGVnb3JpY2FsIGRhdGEgYW5kIHdlIHdhbnQgUiB0byAKcmVjb2duaXplIHRoaXMKLSBBIGZhY3RvciBpcyBSJ3MgZGF0YSBzdHJ1Y3R1cmUgZm9yIGNhdGVnb3JpY2FsIGRhdGE6CgpgYGB7cn0Kc2V4IDwtIGMoIk1hbGUiLCAiRmVtYWxlIiwgIk1hbGUiLCAiRmVtYWxlIiwgIk1hbGUiLAogICAgICAgICAiTWFsZSIsICJGZW1hbGUiLCAiTWFsZSIsICJNYWxlIiwgIkZlbWFsZSIpCnNleApgYGAKCgoKYGBge3J9CmZhY3RvcihzZXgpCmBgYAoKLSBSIGhhcyBjb252ZXJ0ZWQgdGhlIHN0cmluZ3Mgb2YgdGhlIHNleCBjaGFyYWN0ZXIgdmVjdG9yIGludG8gdHdvICoqbGV2ZWxzKiosIHdoaWNoIGFyZSB0aGUgY2F0ZWdvcmllcyBpbiB0aGUgZGF0YQotIE5vdGUgdGhlIHZhbHVlcyBvZiB0aGlzIGZhY3RvciBhcmUgbm90IGNoYXJhY3RlciBzdHJpbmdzLCBidXQgbGV2ZWxzCi0gV2UgY2FuIHVzZSB0aGlzIGZhY3RvciBsYXRlci1vbiB0byBjb21wYXJlIGRhdGEgZm9yIG1hbGVzIGFuZCBmZW1hbGVzCgojIyBDcmVhdGluZyBhIGRhdGEgZnJhbWUgKGZpcnN0IGF0dGVtcHQpCgotIFdlIGNhbiBjb25zdHJ1Y3QgYSBkYXRhIGZyYW1lIGZyb20gb3RoZXIgb2JqZWN0cyAoTi5CLiBUaGUgKipgcGFzdGUoKWAqKiBmdW5jdGlvbiBqb2lucyBjaGFyYWN0ZXIgdmVjdG9ycyB0b2dldGhlcikKCmBgYHtyfQpwYXRpZW50cyA8LSBkYXRhLmZyYW1lKGZpcnN0TmFtZSwgc2Vjb25kTmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoZmlyc3ROYW1lLCBzZWNvbmROYW1lKSwgIAogICAgICAgICAgICAgICAgICAgICAgIHNleCwgYWdlLCB3ZWlnaHQsIGNvbnNlbnQpCmBgYAoKCmBgYHtyfQpwYXRpZW50cwoKYGBgCgoKCiMjTmFtaW5nIGRhdGEgZnJhbWUgdmFyaWFibGVzCgotIFdlIGNhbiBhY2Nlc3MgcGFydGljdWxhciB2YXJpYWJsZXMgdXNpbmcgdGhlICoqJ2AkYCcqKiAqb3BlcmF0b3IqOgotIFRJUDogeW91IGNhbiB1c2UgVEFCLWNvbXBsZXRlIHRvIHNlbGVjdCB0aGUgdmFyaWFibGUgeW91IHdhbnQKCmBgYHtyfQpwYXRpZW50cyRhZ2UKCgpgYGAKCi0gUiBoYXMgaW5mZXJyZWQgdGhlIG5hbWVzIG9mIG91ciBkYXRhIGZyYW1lIHZhcmlhYmxlcyBmcm9tIHRoZSBuYW1lcyBvZiB0aGUgdmVjdG9ycyBvciB0aGUgY29tbWFuZHMgKGUuZy4gdGhlIGBwYXN0ZSgpYCBjb21tYW5kKQotIFdlIGNhbiBuYW1lIHRoZSB2YXJpYWJsZXMgYWZ0ZXIgd2UgaGF2ZSBjcmVhdGVkIGEgZGF0YSBmcmFtZSB1c2luZyB0aGUgKipgbmFtZXMoKWAqKiBmdW5jdGlvbiwgYW5kIHdlIGNhbiB1c2UgdGhlIHNhbWUgZnVuY3Rpb24gdG8gc2VlIHRoZSBuYW1lczoKCmBgYHtyfQpuYW1lcyhwYXRpZW50cykgPC0gYygiRmlyc3RfTmFtZSIsICJTZWNvbmRfTmFtZSIsCiAgICAgICAgICAgICAgICAgICAgICJGdWxsX05hbWUiLCAiU2V4IiwgIkFnZSIsIAogICAgICAgICAgICAgICAgICAgICAiV2VpZ2h0IiwgIkNvbnNlbnQiKQpgYGAKCmBgYHtyfQpuYW1lcyhwYXRpZW50cykKYGBgCgoKLSBPciB3ZSBjYW4gbmFtZSB0aGUgdmFyaWFibGVzIHdoZW4gd2UgZGVmaW5lIHRoZSBkYXRhIGZyYW1lCgpgYGB7cn0KcGF0aWVudHMgPC0gZGF0YS5mcmFtZShGaXJzdF9OYW1lID0gZmlyc3ROYW1lLCAKICAgICAgICAgICAgICAgICAgICAgICBTZWNvbmRfTmFtZSA9IHNlY29uZE5hbWUsIAogICAgICAgICAgICAgICAgICAgICAgIEZ1bGxfTmFtZSA9IHBhc3RlKGZpcnN0TmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWNvbmROYW1lKSwgCiAgICAgICAgICAgICAgICAgICAgICAgU2V4ID0gc2V4LAogICAgICAgICAgICAgICAgICAgICAgIEFnZSA9IGFnZSwKICAgICAgICAgICAgICAgICAgICAgICBXZWlnaHQgPSB3ZWlnaHQsIAogICAgICAgICAgICAgICAgICAgICAgIENvbnNlbnQgPSBjb25zZW50KQoKYGBgCgpgYGB7cn0KbmFtZXMocGF0aWVudHMpCmBgYAoKIyNGYWN0b3JzIGluIGRhdGEgZnJhbWVzCgotIFdoZW4gY3JlYXRpbmcgYSBkYXRhIGZyYW1lLCBSIGFzc3VtZXMgYWxsIGNoYXJhY3RlciB2ZWN0b3JzIHNob3VsZCBiZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgYW5kIGNvbnZlcnRzIHRoZW0gdG8gZmFjdG9ycy4gVGhpcyBpcyBub3QgCmFsd2F5cyB3aGF0IHdlIHdhbnQ6CiAgICArIGUuZy4gd2UgYXJlIHVubGlrZWx5IHRvIGJlIGludGVyZXN0ZWQgaW4gdGhlIGh5cG90aGVzaXMgdGhhdCBwZW9wbGUgY2FsbGVkIEFkYW0gYXJlIHRhbGxlciwgc28gaXQgc2VlbXMgYSBiaXQgc2lsbHkgdG8gcmVwcmVzZW50IHRoaXMgYXMgYSBmYWN0b3IKCmBgYHtyfQpwYXRpZW50cyRGaXJzdF9OYW1lCmBgYAoKCi0gV2UgY2FuIGF2b2lkIHRoaXMgYnkgYXNraW5nIFIgbm90IHRvIHRyZWF0IHN0cmluZ3MgYXMgZmFjdG9ycywgYW5kIAp0aGVuIGV4cGxpY2l0bHkgc3RhdGluZyB3aGVuIHdlIHdhbnQgYSBmYWN0b3IgYnkgdXNpbmcgKipgZmFjdG9yKClgKio6CgpgYGB7cn0KcGF0aWVudHMgPC0gZGF0YS5mcmFtZShGaXJzdF9OYW1lID0gZmlyc3ROYW1lLCAKICAgICAgICAgICAgICAgICAgICAgICBTZWNvbmRfTmFtZSA9IHNlY29uZE5hbWUsIAogICAgICAgICAgICAgICAgICAgICAgIEZ1bGxfTmFtZSA9IHBhc3RlKGZpcnN0TmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWNvbmROYW1lKSwgCiAgICAgICAgICAgICAgICAgICAgICAgU2V4ID0gZmFjdG9yKHNleCksCiAgICAgICAgICAgICAgICAgICAgICAgQWdlID0gYWdlLAogICAgICAgICAgICAgICAgICAgICAgIFdlaWdodCA9IHdlaWdodCwKICAgICAgICAgICAgICAgICAgICAgICBDb25zZW50ID0gY29uc2VudCwKICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCnBhdGllbnRzCmBgYAoKYGBge3J9CnBhdGllbnRzJFNleApwYXRpZW50cyRGaXJzdF9OYW1lCmBgYAoKIyMgUmVtb3ZpbmcgdmFyaWFibGVzCgpOb3cgdGhhdCB3ZSBhcmUgaGFwcHkgd2l0aCBvdXIgZGF0YSBmcmFtZSwgd2Ugbm8gbG9uZ2VyIGhhdmUgYW55IHVzZSBmb3IgdGhlIHZlY3RvcnMgdGhhdCB3ZXJlIHVzZWQgdG8gY3JlYXRlIGl0CgotIFIgaGFzIGEgZnVuY3Rpb24gY2FsbGVkIGBybWAgdGhhdCB3aWxsIGFsbG93IHVzIHRvIHJlbW92ZSB2YXJpYWJsZXMKCgpgYGB7ciBldmFsPUZBTFNFfQpybShhZ2UpCmBgYAoKT25jZSBzb21ldGhpbmcgaGFzIGJlZW4gcmVtb3ZlZCwgd2UgY2FuIG5vIGxvbmdlciB1c2UgaXQKCmBgYHtyIGV2YWw9RkFMU0V9CmFnZQpgYGAKCk11bHRpcGxlIG9iamVjdHMgY2FuIGJlIHJlbW92ZWQgYXQgdGhlIHNhbWUgdGltZQoKYGBge3J9CnJtKGxpc3QgPSBjKCJhZ2UiLCJmaXJzdE5hbWUiLCJzZWNvbmROYW1lIiwic2V4Iiwid2VpZ2h0IiwiY29uc2VudCIpKQoKYGBgCgojIyBBZGRpbmcgYWRkaXRpb25hbCBjb2x1bW5zCgpSZWNhbGwgdGhhdCB3ZSBjYW4gY3JlYXRlIGEgbmV3IHZhcmlhYmxlIHVzaW5nIGFuIGFzc2lnbm1lbnQgb3BlcmF0b3IgYW5kIHNwZWNpZnlpbmcgYSBuYW1lIHRoYXQgUiBpc24ndCBjdXJyZW50bHkgdXNpbmcgYXMgYSB2YXJpYWJsZSBuYW1lCgpgYGB7cn0KbXlOZXdWYXJpYWJsZSA8LSA0MgpteU5ld1ZhcmlhYmxlCmBgYAoKV2UgdXNlIGEgc2ltaWxhciB0cmljayB0byBkZWZpbmUgbmV3IGNvbHVtbnMgaW4gdGhlIGRhdGEgZnJhbWUKLSBUaGUgdmFsdWUgeW91IGFzc2lnbiBtdXN0IGJlIHRoZSBzYW1lIGxlbmd0aCBhcyB0aGUgbnVtYmVyIG9mIHJvd3MgaW4gdGhlIGRhdGEgZnJhbWUuCgpgYGB7cn0KcGF0aWVudHMkSUQKcGF0aWVudHMkSUQgPC0gcGFzdGUoIlBhdGllbnQiLCAxOjEwKQpwYXRpZW50cwpgYGAKCgojI0luZGV4aW5nIGRhdGEgZnJhbWVzIGFuZCBtYXRyaWNlcwoKLSBZb3UgY2FuIGluZGV4IG11bHRpZGltZW5zaW9uYWwgZGF0YSBzdHJ1Y3R1cmVzIGxpa2UgbWF0cmljZXMgYW5kIGRhdGEgCmZyYW1lcyB1c2luZyBjb21tYXM6Ci0gKipgb2JqZWN0W3Jvd3MsIGNvbHVtc11gKioKLSBUcnkgYW5kIHByZWRpY3Qgd2hhdCBlYWNoIG9mIHRoZSBmb2xsb3dpbmcgY29tbWFuZHMgd2lsbCBkbzotCgpgYGB7cn0KcGF0aWVudHNbMiwxXQpgYGAKCgpgYGB7cn0KcGF0aWVudHNbMSwyXQoKYGBgCgpgYGB7cn0KcGF0aWVudHNbMSwxOjNdCgpgYGAKCi0gSWYgeW91IGRvbid0IHByb3ZpZGUgYW4gaW5kZXggZm9yIGVpdGhlciByb3dzIG9yIGNvbHVtbnMsIGFsbCBvZiB0aGUgcm93cyBvciBjb2x1bW5zIHdpbGwgYmUgcmV0dXJuZWQuCgpgYGB7cn0KcGF0aWVudHNbMSxdCgpgYGAKCi0gUm93cyBvciBjb2x1bW5zIGNhbiBiZSBvbWl0dGVkIGJ5IHB1dHRpbmcgYSBgLWAgaW4gZnJvbnQgb2YgdGhlIGluZGV4CgpgYGB7cn0KcGF0aWVudHNbLC0xXQpwYXRpZW50c1stYyg1LDcpLF0KYGBgCgojI0FkdmFuY2VkIGluZGV4aW5nCgotIEluZGljZXMgYXJlIGFjdHVhbGx5IHZlY3RvcnMsIGFuZCBjYW4gYmUgKioqbnVtZXJpYyoqKiBvciAqKipsb2dpY2FsKioqOgotIFdlIHdvbid0IGFsd2F5cyBrbm93IGluIGFkdmFuY2Ugd2hpY2ggaW5kaWNlcyB3ZSB3YW50IHRvIHJldHVybgogICAgKyB3ZSBtaWdodCB3YW50IGFsbCB2YWx1ZXMgdGhhdCBleGNlZWQgYSBwYXJ0aWN1bGFyIHZhbHVlIG9yIHNhdGlzZnkgc29tZSBvdGhlciBjcml0ZXJpYQotIEluIHRoaXMgZXhhbXBsZSwgYGxldHRlcnNgIGlzIGEgdmVjdG9yIGNvbnRhaW5pbmcgYWxsIGxldHRlcnMgaW4gdGhlIEVuZ2xpc2ggYWxwaGFiZXQKCmBgYHtyfQpsZXR0ZXJzCnMgPC0gbGV0dGVyc1sxOjVdCnMKYGBgCgpTbyBmYXIgd2UgaGF2ZSBzZWVuIGhvdyB0byBleHRyYWN0IHRoZSBmaXJzdCBhbmQgdGhpcmQgdmFsdWVzIGluIHRoZSB2ZWN0b3IKCmBgYHtyfQpzW2MoMSwzKV0KYGBgCgpSIGNhbiBwZXJmb3JtIHRoZSBzYW1lIG9wZXJhdGlvbiB1c2luZyBhIHZlY3RvciBvZiBsb2dpY2FsIHZhbHVlcy4gT25seSBpbmRpY2VzIHdpdGggYSBgVFJVRWAgdmFsdWUgd2lsbCBnZXQgcmV0dXJuZWQKCmBgYHtyfQpzW2MoVFJVRSwgRkFMU0UsIFRSVUUsIEZBTFNFLCBGQUxTRSldCmBgYAoKCi0gV2UgY2FuIGRvIHRoZSBsb2dpY2FsIHRlc3QgYW5kIGluZGV4aW5nIGluIHRoZSBzYW1lIGxpbmUgb2YgUiBjb2RlCiAgICArIFIgd2lsbCBkbyB0aGUgdGVzdCBmaXJzdCwgYW5kIHRoZW4gdXNlIHRoZSB2ZWN0b3Igb2YgYFRSVUVgIGFuZCBgRkFMU0VgIHZhbHVlcyB0byBzdWJzZXQgdGhlIHZlY3RvcgpgYGB7cn0KYSA8LSAxOjUKCmEgPCAzCnNbYSA8IDNdCmBgYAoKCiMjIExvZ2ljYWwgT3BlcmF0b3JzCgotIE9wZXJhdG9ycyBhbGxvdyB1cyB0byBjb21iaW5lIG11bHRpcGxlIGxvZ2ljYWwgdGVzdHMKLSBjb21wYXJpc29uIG9wZXJhdG9ycwoqKmA8LCA+LCA8PSwgPj0sID09LCAhPWAqKgotIGxvZ2ljYWwgb3BlcmF0b3JzIAoqKmAhLCAmLCB8LCB4b3JgKioKICAgICsgVGhlIG9wZXJhdG9ycyBmb3IgJ2NvbXBhcmlzb24nIGFuZCAnbG9naWNhbCcgYWx3YXlzIHJldHVybiBsb2dpY2FsIHZhbHVlcyEgaS5lLiAgKGBUUlVFYCwgYEZBTFNFYCkKCgpgYGB7cn0KCnNbYSA+IDEgJiBhIDwzXQpzW2EgPT0gMl0KYGBgCgpUaGUgdmVjdG9yIHRoYXQgeW91IHVzZSB0byBwZXJmb3JtIHRoZSBsb2dpY2FsIHRlc3QgY291bGQgYmUgZXh0cmFjdGVkIGZyb20gYSBkYXRhIGZyYW1lCgotIHdoaWNoIGNvdWxkIHRoZW4gYmUgdXNlZCB0byBzdWJzZXQgdGhlIGRhdGEgZnJhbWUKCmBgYHtyfQpwYXRpZW50cyRGaXJzdF9OYW1lID09ICJQZXRlciIKcGF0aWVudHNbcGF0aWVudHMkRmlyc3RfTmFtZSA9PSAiUGV0ZXIiLF0KYGBgCgoKCiMjRXhlcmNpc2U6IEV4ZXJjaXNlIDIKCi0gV3JpdGUgUiBjb2RlIHRvIHByaW50IHRoZSBmb2xsb3dpbmcgc3Vic2V0cyBvZiB0aGUgcGF0aWVudHMgZGF0YSBmcmFtZQotIFRoZSBmaXJzdCBhbmQgc2Vjb25kIHJvd3MsIGFuZCB0aGUgZmlyc3QgYW5kIHNlY29uZCBjb2x1bXMKCnwgIHxGaXJzdF9OYW1lfFNlY29uZF9OYW1lCnwtLXwtLS0tLS0tfC0tLS0tLS18CnwxIHxBZGFtICAgfEpvbmVzIAp8MiB8RXZlICAgIHxQYXJrZXIgCgotIE9ubHkgZXZlbi1udW1iZXJlZCByb3dzCgpISU5UOiB5b3UgY2FuIHVzZSB0aGUgYHNlcWAgZnVuY3Rpb24gdGhhdCB3ZSBzYXcgZWFybGllciB0byBkZWZpbmUgYSB2ZWN0b3Igb2YgZXZlbiBudW1iZXJzCgp8ICB8Rmlyc3RfTmFtZXxTZWNvbmRfTmFtZXxGdWxsX05hbWV8U2V4IHxBZ2V8V2VpZ2h0IHxDb25zZW50fAp8LS18LS0tLS0tLXwtLS0tLS0tfC0tLS0tLS0tLS0tLS0tfDotLS0tOnwtLTp8LS0tLS0tOnw6LS0tLS06fAp8MiB8RXZlICAgIHxQYXJrZXIgfEV2ZSBQYXJrZXIgICAgfEZlbWFsZXwyMSB8ICA2Ny45IHwgICBUUlVFfAp8NCB8TWFyeSAgIHxEYXZpcyAgfE1hcnkgRGF2aXMgICAgfEZlbWFsZXw0NSB8ICA2MS45IHwgICBUUlVFfAp8NiB8UGF1bCAgIHxEYW5pZWxzfFBhdWwgRGFuaWVscyAgfCAgTWFsZXwzMSB8ICA2OS45IHwgIEZBTFNFfAp8OCB8TWF0dGhld3xTbWl0aCAgfE1hdHRoZXcgU21pdGggfCAgTWFsZXwzMyB8ICA3MS41IHwgICBUUlVFfAp8MTB8U2FsbHkgIHxXaWxzb24gfFNhbGx5IFdpbHNvbiAgfEZlbWFsZXw2MiB8ICA2NC44IHwgICBUUlVFfAoKLSBBbGwgcm93cyBleGNlcHQgdGhlIGxhc3Qgb25lLCBhbGwgY29sdW1ucwoKSElOVDogdGhlIGBucm93YCBmdW5jdGlvbiB3aWxsIGdpdmUgdGhlIG51bWJlciBvZiByb3dzIGluIHRoZSBkYXRhIGZyYW1lCgp8ICB8Rmlyc3RfTmFtZXxTZWNvbmRfTmFtZXxGdWxsX05hbWV8U2V4IHxBZ2V8V2VpZ2h0IHxDb25zZW50fAp8LS18LS0tLS0tLXwtLS0tLS0tfC0tLS0tLS0tLS0tLS0tfDotLS0tOnwtLTp8LS0tLS0tOnw6LS0tLS06fAp8MSB8QWRhbSAgIHxKb25lcyAgfEFkYW0gSm9uZXMgICAgfCAgTWFsZXw1MCB8ICA3MC44IHwgICBUUlVFfAp8MiB8RXZlICAgIHxQYXJrZXIgfEV2ZSBQYXJrZXIgICAgfEZlbWFsZXwyMSB8ICA2Ny45IHwgICBUUlVFfAp8MyB8Sm9obiAgIHxFdmFucyAgfEpvaG4gRXZhbnMgICAgfCAgTWFsZXwzNSB8ICA3NS4zIHwgIEZBTFNFfAp8NCB8TWFyeSAgIHxEYXZpcyAgfE1hcnkgRGF2aXMgICAgfEZlbWFsZXw0NSB8ICA2MS45IHwgICBUUlVFfAp8NSB8UGV0ZXIgIHxCYWtlciAgfFBldGVyIEJha2VyICAgfCAgTWFsZXwyOCB8ICA3Mi40IHwgIEZBTFNFfAp8NiB8UGF1bCAgIHxEYW5pZWxzfFBhdWwgRGFuaWVscyAgfCAgTWFsZXwzMSB8ICA2OS45IHwgIEZBTFNFfAp8NyB8Sm9hbm5hIHxFZHdhcmRzfEpvYW5uYSBFZHdhcmRzfEZlbWFsZXw0MiB8ICA2My41IHwgIEZBTFNFfAp8OCB8TWF0dGhld3xTbWl0aCAgfE1hdHRoZXcgU21pdGggfCAgTWFsZXwzMyB8ICA3MS41IHwgICBUUlVFfAp8OSB8RGF2aWQgIHxSb2JlcnRzfERhdmlkIFJvYmVydHMgfCAgTWFsZXw1NyB8ICA3My4yIHwgIEZBTFNFfAoKLSBVc2UgbG9naWNhbCBpbmRleGluZyB0byBzZWxlY3QgdGhlIGZvbGxvd2luZyBwYXRpZW50cyBmcm9tIHRoZSBkYXRhIGZyYW1lOgogICAgMS4gUGF0aWVudHMgdW5kZXIgNDAKICAgIDIuIFBhdGllbnRzIHdobyBnaXZlIGNvbnNlbnQgdG8gc2hhcmUgdGhlaXIgZGF0YQogICAgMy4gTWVuIHdobyB3ZWlnaCBhcyBtdWNoIG9yIG1vcmUgdGhhbiB0aGUgYXZlcmFnZSBFdXJvcGVhbiBtYWxlICg3MC44IGtnKQogICAgCiAgCmBgYHtyfQphZ2UgICAgPC0gYyg1MCwgMjEsIDM1LCA0NSwgMjgsIDMxLCA0MiwgMzMsIDU3LCA2MikKd2VpZ2h0IDwtIGMoNzAuOCwgNjcuOSwgNzUuMywgNjEuOSwgNzIuNCwgNjkuOSwgCiAgICAgICAgICAgIDYzLjUsIDcxLjUsIDczLjIsIDY0LjgpCgpmaXJzdE5hbWUgIDwtIGMoIkFkYW0iLCAiRXZlIiwgIkpvaG4iLCAiTWFyeSIsCiAgICAgICAgICAgICAgICAiUGV0ZXIiLCAiUGF1bCIsICJKb2FubmEiLCAiTWF0dGhldyIsCiAgICAgICAgICAgICAgICAiRGF2aWQiLCAiU2FsbHkiKQpzZWNvbmROYW1lIDwtIGMoIkpvbmVzIiwgIlBhcmtlciIsICJFdmFucyIsICJEYXZpcyIsCiAgICAgICAgICAgICAgICAiQmFrZXIiLCJEYW5pZWxzIiwgIkVkd2FyZHMiLCAiU21pdGgiLCAKICAgICAgICAgICAgICAgICJSb2JlcnRzIiwgIldpbHNvbiIpCgpjb25zZW50IDwtIGMoVFJVRSwgVFJVRSwgRkFMU0UsIFRSVUUsIEZBTFNFLCAKICAgICAgICAgICAgIEZBTFNFLCBGQUxTRSwgVFJVRSwgRkFMU0UsIFRSVUUpCgpzZXggPC0gYygiTWFsZSIsICJGZW1hbGUiLCAiTWFsZSIsICJGZW1hbGUiLCAiTWFsZSIsCiAgICAgICAgICJNYWxlIiwgIkZlbWFsZSIsICJNYWxlIiwgIk1hbGUiLCAiRmVtYWxlIikKcGF0aWVudHMgPC0gZGF0YS5mcmFtZShGaXJzdF9OYW1lID0gZmlyc3ROYW1lLCAKICAgICAgICAgICAgICAgICAgICAgICBTZWNvbmRfTmFtZSA9IHNlY29uZE5hbWUsIAogICAgICAgICAgICAgICAgICAgICAgIEZ1bGxfTmFtZSA9IHBhc3RlKGZpcnN0TmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWNvbmROYW1lKSwgCiAgICAgICAgICAgICAgICAgICAgICAgU2V4ID0gZmFjdG9yKHNleCksCiAgICAgICAgICAgICAgICAgICAgICAgQWdlID0gYWdlLAogICAgICAgICAgICAgICAgICAgICAgIFdlaWdodCA9IHdlaWdodCwKICAgICAgICAgICAgICAgICAgICAgICBDb25zZW50ID0gY29uc2VudCwKICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCnJtKGxpc3QgPSBjKCJmaXJzdE5hbWUiLCJzZWNvbmROYW1lIiwic2V4Iiwid2VpZ2h0IiwiY29uc2VudCIpKQpwYXRpZW50cwoKIyMjIFlvdXIgQW5zd2VyICMjIwoKCmBgYAoKCgojIyAoU3VwcGxlbWVudGFyeSkgTWF0cmljZXMKCi0gRGF0YSBmcmFtZXMgYXJlIFIncyBzcGVjaWFsaXR5LCBidXQgUiBhbHNvIGhhbmRsZXMgbWF0cmljZXM6CiAgICArIEFsbCBjb2x1bW5zIGFyZSBhc3N1bWVkIHRvIGNvbnRhaW4gdGhlIHNhbWUgZGF0YSB0eXBlLCBlLmcuIG51bWVyaWNhbAogICAgKyBNYXRyaWNlcyBjYW4gYmUgbWFuaXB1bGF0ZWQgaW4gdGhlIHNhbWUgZmFzaGlvbiBhcyBkYXRhIGZyYW1lCiAgICAgICAgKyBXZSBjYW4gZWFzaWx5IGNvbnZlcnQgYmV0d2VlbiB0aGUgdHdvIG9iamVjdCB0eXBlcwogICAgICAgIApgYGB7cn0KZSA8LSBtYXRyaXgoMToxMCwgbnJvdz01LCBuY29sPTIpCmUKYGBgCgotIFNvbWUgY2FsY3VsYXRpb25zIGFyZSBtb3JlIGVmZmljaWVudCB0byBkbyBvbiBtYXRyaWNlcywgZS5nLjoKCmBgYHtyfQpyb3dNZWFucyhlKQpgYGAKCk1hdHJpY2VzIChhbmQgaW5kZWVkIGRhdGEgZnJhbWVzKSBjYW4gYmUgam9pbmVkIHRvZ2V0aGVyIHVzaW5nIHRoZSBmdW5jdGlvbnMgYGNiaW5kYCBhbmQgYHJiaW5kYAoKTGV0J3MgZmlyc3QgY3JlYXRlIHNvbWUgZXhhbXBsZSBkYXRhCmBgYHtyfQoKbWF0MSA8LSBtYXRyaXgoMTE6MjAsIG5yb3c9NSxuY29sPTIpCm1hdDEKbWF0MiA8LSBtYXRyaXgoMjE6MzAsIG5yb3c9NSwgbmNvbD0yKQptYXQyCm1hdDMgPC0gbWF0cml4KDMxOjQwLG5yb3c9NSxuY29sPTIpCm1hdDMKCmBgYAoKYW5kIG5vdyB0cnkgb3V0IHRoZXNlIGZ1bmN0aW9uczotCgpgYGB7cn0KY2JpbmQobWF0MSxtYXQyKQpyYmluZChtYXQxLG1hdDMpCmBgYAo=