• 2. Data structures
    • R is designed to handle experimental data
    • Character, numeric and logical data types
    • Factors
    • Creating a data frame (first attempt)
    • Naming data frame variables
    • Factors in data frames
    • Removing variables
    • Adding additional columns
    • Indexing data frames and matrices
    • Advanced indexing
    • Logical Operators
    • Exercise: Exercise 2
    • (Supplementary) Matrices

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
ABCDEFGHIJ0123456789
firstName
<fctr>
secondName
<fctr>
paste.firstName..secondName.
<fctr>
sex
<fctr>
age
<dbl>
weight
<dbl>
consent
<lgl>
AdamJonesAdam JonesMale5070.8TRUE
EveParkerEve ParkerFemale2167.9TRUE
JohnEvansJohn EvansMale3575.3FALSE
MaryDavisMary DavisFemale4561.9TRUE
PeterBakerPeter BakerMale2872.4FALSE
PaulDanielsPaul DanielsMale3169.9FALSE
JoannaEdwardsJoanna EdwardsFemale4263.5FALSE
MatthewSmithMatthew SmithMale3371.5TRUE
DavidRobertsDavid RobertsMale5773.2FALSE
SallyWilsonSally WilsonFemale6264.8TRUE

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
ABCDEFGHIJ0123456789
First_Name
<chr>
Second_Name
<chr>
Full_Name
<chr>
Sex
<fctr>
Age
<dbl>
Weight
<dbl>
Consent
<lgl>
AdamJonesAdam JonesMale5070.8TRUE
EveParkerEve ParkerFemale2167.9TRUE
JohnEvansJohn EvansMale3575.3FALSE
MaryDavisMary DavisFemale4561.9TRUE
PeterBakerPeter BakerMale2872.4FALSE
PaulDanielsPaul DanielsMale3169.9FALSE
JoannaEdwardsJoanna EdwardsFemale4263.5FALSE
MatthewSmithMatthew SmithMale3371.5TRUE
DavidRobertsDavid RobertsMale5773.2FALSE
SallyWilsonSally WilsonFemale6264.8TRUE
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
ABCDEFGHIJ0123456789
First_Name
<chr>
Second_Name
<chr>
Full_Name
<chr>
Sex
<fctr>
Age
<dbl>
Weight
<dbl>
Consent
<lgl>
ID
<chr>
AdamJonesAdam JonesMale5070.8TRUEPatient 1
EveParkerEve ParkerFemale2167.9TRUEPatient 2
JohnEvansJohn EvansMale3575.3FALSEPatient 3
MaryDavisMary DavisFemale4561.9TRUEPatient 4
PeterBakerPeter BakerMale2872.4FALSEPatient 5
PaulDanielsPaul DanielsMale3169.9FALSEPatient 6
JoannaEdwardsJoanna EdwardsFemale4263.5FALSEPatient 7
MatthewSmithMatthew SmithMale3371.5TRUEPatient 8
DavidRobertsDavid RobertsMale5773.2FALSEPatient 9
SallyWilsonSally WilsonFemale6264.8TRUEPatient 10

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]
ABCDEFGHIJ0123456789
 
 
First_Name
<chr>
Second_Name
<chr>
Full_Name
<chr>
1AdamJonesAdam Jones
  • If you don’t provide an index for either rows or columns, all of the rows or columns will be returned.
patients[1,]
ABCDEFGHIJ0123456789
 
 
First_Name
<chr>
Second_Name
<chr>
Full_Name
<chr>
Sex
<fctr>
Age
<dbl>
Weight
<dbl>
Consent
<lgl>
ID
<chr>
1AdamJonesAdam JonesMale5070.8TRUEPatient 1
  • Rows or columns can be omitted by putting a - in front of the index
patients[,-1]
ABCDEFGHIJ0123456789
Second_Name
<chr>
Full_Name
<chr>
Sex
<fctr>
Age
<dbl>
Weight
<dbl>
Consent
<lgl>
ID
<chr>
JonesAdam JonesMale5070.8TRUEPatient 1
ParkerEve ParkerFemale2167.9TRUEPatient 2
EvansJohn EvansMale3575.3FALSEPatient 3
DavisMary DavisFemale4561.9TRUEPatient 4
BakerPeter BakerMale2872.4FALSEPatient 5
DanielsPaul DanielsMale3169.9FALSEPatient 6
EdwardsJoanna EdwardsFemale4263.5FALSEPatient 7
SmithMatthew SmithMale3371.5TRUEPatient 8
RobertsDavid RobertsMale5773.2FALSEPatient 9
WilsonSally WilsonFemale6264.8TRUEPatient 10
patients[-c(5,7),]
ABCDEFGHIJ0123456789
 
 
First_Name
<chr>
Second_Name
<chr>
Full_Name
<chr>
Sex
<fctr>
Age
<dbl>
Weight
<dbl>
Consent
<lgl>
ID
<chr>
1AdamJonesAdam JonesMale5070.8TRUEPatient 1
2EveParkerEve ParkerFemale2167.9TRUEPatient 2
3JohnEvansJohn EvansMale3575.3FALSEPatient 3
4MaryDavisMary DavisFemale4561.9TRUEPatient 4
6PaulDanielsPaul DanielsMale3169.9FALSEPatient 6
8MatthewSmithMatthew SmithMale3371.5TRUEPatient 8
9DavidRobertsDavid RobertsMale5773.2FALSEPatient 9
10SallyWilsonSally WilsonFemale6264.8TRUEPatient 10

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",]
ABCDEFGHIJ0123456789
 
 
First_Name
<chr>
Second_Name
<chr>
Full_Name
<chr>
Sex
<fctr>
Age
<dbl>
Weight
<dbl>
Consent
<lgl>
ID
<chr>
5PeterBakerPeter BakerMale2872.4FALSEPatient 5

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
ABCDEFGHIJ0123456789
First_Name
<chr>
Second_Name
<chr>
Full_Name
<chr>
Sex
<fctr>
Age
<dbl>
Weight
<dbl>
Consent
<lgl>
AdamJonesAdam JonesMale5070.8TRUE
EveParkerEve ParkerFemale2167.9TRUE
JohnEvansJohn EvansMale3575.3FALSE
MaryDavisMary DavisFemale4561.9TRUE
PeterBakerPeter BakerMale2872.4FALSE
PaulDanielsPaul DanielsMale3169.9FALSE
JoannaEdwardsJoanna EdwardsFemale4263.5FALSE
MatthewSmithMatthew SmithMale3371.5TRUE
DavidRobertsDavid RobertsMale5773.2FALSE
SallyWilsonSally WilsonFemale6264.8TRUE
### 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=