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)
First_Name Second_Name Full_Name Sex Age Weight Consent
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
First_Name Second_Name
1 Adam Jones
2 Eve Parker
  • Only even-numbered rows

HINT: you can use the seq function that we saw earlier to define a vector of even numbers

First_Name Second_Name Full_Name Sex Age Weight Consent
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

First_Name Second_Name Full_Name Sex Age Weight Consent
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:
    1. Patients under 40
    2. Patients who give consent to share their data
    3. 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=