Data structures
Data types
Numeric/Double (e.g. 2.5, 1/5, 1.0, )
Integer (e.g. 1, 2, 3, )
Complex (e.g. 1 + 2i, )
Logical (e.g. TRUE, FALSE or NA)
Character (e.g. “a”, “paper”, “2 plus 2 = 5”, “TRUE”, )
Factor/Categorical (“male”, “female”, )
Vectors (I)
You can create a vector using the command c()
## [1] 1 3 5 10
Vectors must contain elements of the same data type.
## [1] "1" "intro" "TRUE"
You can measure the length of a vector using the command length()
## [1] 4
Vectors (II)
It is also possible to easily create sequences
## [1] 1 2 3 4 5 6 7 8 9 10
seq(from = 1, to = 2, by = 0.1)
## [1] 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0
## [1] "A" "A" "A" "A" "A"
Vectors (III)
You can combine different vectors
x <- 1:3 # from 1 to 3
y <- c(10, 15) # 10 and 15
z <- c(x,y) # x first and then y
z
## [1] 1 2 3 10 15
And you can repeat vectors (or its elements)
z <- rep(y, each=3) # repeat each element 3 times
z
## [1] 10 10 10 15 15 15
z <- rep(y, times=3) # repeat the whole vector 3 times
z
## [1] 10 15 10 15 10 15
Subsetting Vectors
x <- c(1, 5, 10, 7)
x < 6 # is the element lower than 6?
## [1] TRUE TRUE FALSE FALSE
x == 10 # is the element equal to 10?
## [1] FALSE FALSE TRUE FALSE
x[2] # which element is in the second position?
## [1] 5
x[1:2] # which elements are in the first 2 positions?
## [1] 1 5
x[c(1,3,4)] # which elements are in positions 1, 3 and 4?
## [1] 1 10 7
Subsetting Vectors
n <- c(1, 4, 5, 6, 7, 2, 3, 4, 5, 6) # creates a vector, stores it in n
length(n) # number of elements
## [1] 10
n[3] # extract 3 rd element in n
## [1] 5
n[-2] # extract all of n but 2nd element
## [1] 1 5 6 7 2 3 4 5 6
n[c(1,3,4)] # extract first, third, and fourth element of n
## [1] 1 5 6
n[n < 4] #extract all elements in n smaller than 4
## [1] 1 2 3
n[n < 4 & n != 1] # extract element smaller than 4 AND different from 1
## [1] 2 3
Exercise break
n <- c(1, 4, 5, 6, 7, 2, 3, 4, 5, 6) # creates a vector, stores it in n
- number of elements
- extract first three elements of n
- extract all of n but 3nd element
- extract first, third, and fourth element of n
- extract element smaller than 4 OR equal to 5
Exercise break
We can create and index character vectors as well. A cafe is using R to create their menu.
items <- c("apples", "oranges", "eggs", "tomatoes", "bananas")
- What does items[-3] produce?
- Based on what you find, use indexing to create a version of items without “bananas”.
- Use indexing to create a vector containing apples, eggs, tomatoes, bananas, and bananas.
- Add a new item, “lemons”, to items.
- Make items[3] “berries”.
Vectors’ Operations
x <- c(1,5,10,7)
x+2 # adds a scalar to all elements
## [1] 3 7 12 9
x^2 # what's the square of all elements?
## [1] 1 25 100 49
Matrices (I)
You can create a matrix using the command matrix()
X <- matrix(1:9, nrow = 3, ncol = 3)
X
## [,1] [,2] [,3]
## [1,] 1 4 7
## [2,] 2 5 8
## [3,] 3 6 9
Matrices (II)
R automatically inserts elements by columns, but we can ask to include by rows
X <- matrix(1:9, nrow = 3, ncol = 3, byrow = TRUE)
X
## [,1] [,2] [,3]
## [1,] 1 2 3
## [2,] 4 5 6
## [3,] 7 8 9
You don’t even have to specify the options names
X <- matrix(1:8, 2, 4, T)
X
## [,1] [,2] [,3] [,4]
## [1,] 1 2 3 4
## [2,] 5 6 7 8
Matrices (III)
Matrices can also be created by combining vectors
X <- cbind(1:4, 6:9) # binds them as columns
X
## [,1] [,2]
## [1,] 1 6
## [2,] 2 7
## [3,] 3 8
## [4,] 4 9
X <- rbind(1:4, 6:9) # binds them as rows
X
## [,1] [,2] [,3] [,4]
## [1,] 1 2 3 4
## [2,] 6 7 8 9
Subsetting Matrices
X>5 # elements larger than 5
## [,1] [,2] [,3] [,4]
## [1,] FALSE FALSE FALSE FALSE
## [2,] TRUE TRUE TRUE TRUE
X[1,4] # element of first row, fourth column?
## [1] 4
X[1,] # element in the first row?
## [1] 1 2 3 4
X[,2] # elements in the second columns?
## [1] 2 7
Lists
A list is a one-dimensional heterogeneous data structure.
It is indexed like a vector with a single integer value (or a name), but each element can contain an element of any data type.
x <- 1:4
y <- c("a", "b", "c")
L <- list(numbers = x, letters = y)
L
## $numbers
## [1] 1 2 3 4
##
## $letters
## [1] "a" "b" "c"
Subsetting Lists
L[[1]] # extract the first element
## [1] 1 2 3 4
L$numbers # extract the element called numbers
## [1] 1 2 3 4
L$letters # extract the element called letters
## [1] "a" "b" "c"
You can even “work” with the subsetted element:
## [1] FALSE FALSE TRUE
Exercise break
letters
LETTERS
my_letters <- cbind(LETTERS, letters)
class(my_letters)
dim(my_letters)
my_letters <- cbind(my_letters, seq(1:length(letters)))
What number is the letter F in the English alphabet?
Exercise
# vector ... etc.
my_dna <- "AACGAATGAGTAAATGAGTAAATGAAGGAATGATTATTCCTTGCTTTAGAACTTCTGGAATTAGAGGACA
ATATTAATAATACCATCGCACAGTGTTTCTTTGTTGTTAATGCTACAACATACAAAGAGGAAGCATGCAG"
my_dna
length(my_dna)
class(my_dna)
str(my_dna)
nchar(my_dna)
# appr1
my_dna_comma <- sapply(strsplit(
x = my_dna, split = "", fixed = TRUE),
function(x) paste(x, collapse = "_"))
length(my_dna_comma)
str(my_dna_comma)
my_dna_list <- strsplit(x = my_dna, split = "", fixed = TRUE)
length(my_dna_list)
class(my_dna_list)
my_dna_vector <- unlist(my_dna_list)
length(my_dna_list[[1]])
str(my_dna_vector)
length(my_dna_vector)
# first nucleotide
my_dna_vector[1]
# indexing 1:nchar(my_dna)
my_dna_vector[1:50]
# unique characters
unique(my_dna_vector)
# number of As
(my_dna_vector == "A")
length(my_dna_vector[my_dna_vector == "A"])
# remove \n
remove_nuc <- match("\n", my_dna_vector)
which(my_dna_vector %in% c("\n", "X"))
my_dna_vector[remove_nuc] <- "A"
my_dna_vector_2 <- my_dna_vector[-71]
unique(my_dna_vector_2)
Data Frames (I)
A data.frame
is similar to a typical spreadsheet
in excel.
There are rows, and there are columns.
A row is typically thought of as an .
A column is a certain , characteristic or feature of that observation.
Data Frames (II)
A data frame is a list of column vectors where:
each column has a name
each column must contain the same data type, but the different columns can store different data types.
each column must be of same length
Data Frames (III)
set.seed(1)
df <- data.frame(id = 1:5,
name = c("Diego", "Samuel", "Marco", "Javier", "Leonardo"),
surname = c("Milito", "Eto'o", "Materazzi", "Zanetti", "Bonucci"),
wage = rnorm(n=5, mean = 10^5, sd = 10^3), # normal random sample
origin = c("Argentina", "Cameroon", "Italy", "Argentina", "Italy"),
treble_winner = c(T, T, T, T, F)
)
df
## id name surname wage origin treble_winner
## 1 1 Diego Milito 99373.55 Argentina TRUE
## 2 2 Samuel Eto'o 100183.64 Cameroon TRUE
## 3 3 Marco Materazzi 99164.37 Italy TRUE
## 4 4 Javier Zanetti 101595.28 Argentina TRUE
## 5 5 Leonardo Bonucci 100329.51 Italy FALSE
You can verify the size of the data.frame
using the command dim()
You can get the data type
info using the command str()
Subsetting Data Frames (I)
df$name # subset a column
## [1] "Diego" "Samuel" "Marco" "Javier" "Leonardo"
df[,c(2,5)] # can also subset like a matrix
## name origin
## 1 Diego Argentina
## 2 Samuel Cameroon
## 3 Marco Italy
## 4 Javier Argentina
## 5 Leonardo Italy
Subsetting Data Frames (II)
head(df, n=3) # first n observations
## id name surname wage origin treble_winner
## 1 1 Diego Milito 99373.55 Argentina TRUE
## 2 2 Samuel Eto'o 100183.64 Cameroon TRUE
## 3 3 Marco Materazzi 99164.37 Italy TRUE
tail(df, n=3) # last n observations
## id name surname wage origin treble_winner
## 3 3 Marco Materazzi 99164.37 Italy TRUE
## 4 4 Javier Zanetti 101595.28 Argentina TRUE
## 5 5 Leonardo Bonucci 100329.51 Italy FALSE
Inspecting data frames (I)
R comes with many data bases included. These can be used for learning R.
One of the most famous is the one called mtcars
.
## mpg cyl disp hp drat wt qsec vs am gear carb
## Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
## Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
## Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
## Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
## Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
## Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
## mpg cyl disp hp drat wt qsec vs am gear carb
## Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.7 0 1 5 2
## Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.9 1 1 5 2
## Ford Pantera L 15.8 8 351.0 264 4.22 3.170 14.5 0 1 5 4
## Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.5 0 1 5 6
## Maserati Bora 15.0 8 301.0 335 3.54 3.570 14.6 0 1 5 8
## Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.6 1 1 4 2
## [1] 32 11
Inspecting data frames (II)
## 'data.frame': 32 obs. of 11 variables:
## $ mpg : num 21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
## $ cyl : num 6 6 4 6 8 6 8 4 4 6 ...
## $ disp: num 160 160 108 258 360 ...
## $ hp : num 110 110 93 110 175 105 245 62 95 123 ...
## $ drat: num 3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
## $ wt : num 2.62 2.88 2.32 3.21 3.44 ...
## $ qsec: num 16.5 17 18.6 19.4 17 ...
## $ vs : num 0 0 1 1 0 1 0 1 1 1 ...
## $ am : num 1 1 1 0 0 0 0 0 0 0 ...
## $ gear: num 4 4 4 3 3 3 3 4 4 4 ...
## $ carb: num 4 4 1 1 2 1 4 2 2 4 ...
## [1] "mpg" "cyl" "disp" "hp" "drat" "wt" "qsec" "vs" "am" "gear" "carb"
Subsetting data frames (III)
We are interesting in the cylinders and the weights of inefficient cars (lower than 15 miles per gallon).
poll_cars <- mtcars[mtcars$mpg<15, c("cyl", "wt")]
poll_cars
## cyl wt
## Duster 360 8 3.570
## Cadillac Fleetwood 8 5.250
## Lincoln Continental 8 5.424
## Chrysler Imperial 8 5.345
## Camaro Z28 8 3.840
Subsetting data frames (IV)
Alternatively:
poll_cars <- subset(mtcars, subset = mpg<15, select = c("cyl", "wt"))
poll_cars
## cyl wt
## Duster 360 8 3.570
## Cadillac Fleetwood 8 5.250
## Lincoln Continental 8 5.424
## Chrysler Imperial 8 5.345
## Camaro Z28 8 3.840
Importing downloaded data frames (.csv)
You can import csv data that you have downloaded from any external source using:
setwd("data")
nyc_ab <- read.csv("AB_NYC_2020.csv")
where:
You can similarly import almost any kind of data file stored in other formats (.xls, .txt, .csv, .dta, .Rdata, .mat, …)
Importing downloaded data frames (.txt)
Interferon regulatory factor 6 mouse
setwd("data")
irf6 <- read.table("irf6.txt", header = TRUE, row.names = 1)
# explore
head(irf6)
ncol(irf6); nrow(irf6)
dim(irf6)
Importing downloaded data frames (.txt)
irf6 <- read.table("data/irf6.txt", header = TRUE, row.names = 1)
class(irf6)
str(irf6)
colnames(irf6)
head(rownames(irf6))
Importing downloaded data frames (.txt)
irf6 <- read.table("data/irf6.txt", header = TRUE, row.names = 1)
head(irf6['E17.5KO1']) # Retrieve only E17.5KO1 data
head(irf6[, 1]) # as well
head(irf6$E17.5KO1) # as well
dim(irf6[, -1]); colnames(irf6[, -1]) # Exclude E17.5KO1 data
LS0tCnRpdGxlOiAgICAgICAgIkxlY3R1cmUgMTogUiBwcm9ncmFtbWluZyIKIyBzdWJ0aXRsZTogICAiQmFzZWQgb246IGh0dHBzOi8vZ2l0aHViLmNvbS9tYXR0aWFndWVyaW5pL3NsaWRlcy1pbnRyby10by1SIgojIGF1dGhvcjogICAgICJNYXJpYW0gUi4gUml6a2FsbGFoIgojIGRhdGU6ICAgICAgICIyMDIxIgojIG91dHB1dDogICAgIGJlYW1lcl9wcmVzZW50YXRpb24KIyB0aGVtZTogICAgICBib3hlcwojIGNvbG9ydGhlbWU6IGRvbHBoaW4KIyBmb250dGhlbWU6ICBzZXJpZgojIHRvYzogICAgICAgIFRSVUUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShrbml0cikKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCiMgUiBvcGVyYXRpb25zCgojIyBSIGFzIGEgY2FsY3VsYXRvciAoSSkKXGZvb3Rub3Rlc2l6ZQpgYGB7ciBjYWxjdWxhdG9yMSwgaW5jbHVkZT1ULCBlY2hvPVR9CjIrMgoyLTIKMioyCjIvMgpgYGAKXG5vcm1hbHNpemUKCiMjIFIgYXMgYSBjYWxjdWxhdG9yIChJSSkKXGZvb3Rub3Rlc2l6ZQpgYGB7ciBjYWxjdWxhdG9yMiwgaW5jbHVkZT1ULCBlY2hvPVR9CmxvZygxKQpleHAoMSkKbG9nKGV4cCgxKSkKc3FydCgyNSkKYGBgClxub3JtYWxzaXplCgojIyBUaGUgaGVscApcZm9vdG5vdGVzaXplCmBgYHtyIGhlbHAsIGluY2x1ZGU9VCwgZWNobz1ULCBldmFsID0gRkFMU0V9Cj9sb2cKaGVscChsb2cpCmBgYApcbm9ybWFsc2l6ZQoKT3RoZXJ3aXNlOgoKLSBHb29nbGUgeW91ciBlcnJvciBtZXNzYWdlCgotIEFzayBmb3IgaGVscCBpbiBTdGFjayBPdmVyZmxvdwoKIyMgUGFja2FnZXMKUiBjb21lcyB3aXRoIGEgbnVtYmVyIG9mIGJ1aWx0LWluIGZ1bmN0aW9ucyBhbmQgZGF0YXNldHMsIGJ1dCBvbmUgb2YgdGhlIG1haW4gc3RyZW5ndGhzIG9mIFIgYXMgYW4gb3Blbi1zb3VyY2UgcHJvamVjdCBpcyBpdHMgcGFja2FnZSBzeXN0ZW0uCgpQYWNrYWdlcyBnaXZlcyB5b3UgYWNjZXNzIHRvIGFkZGl0aW9uYWwgZnVuY3Rpb25zIGFuZCBkYXRhc2V0cy4KCklmIHlvdSB3YW50IHRvIGRvIHNvbWV0aGluZyB3aGljaCBpcyBub3QgZG9hYmxlIHdpdGggdGhlIFIgYmFzaWMgZnVuY3Rpb25zLCB0aGVyZSBpcyBhIGdvb2QgY2hhbmNlIHRoYXQgdGhlcmUgZXhpc3QgYSBwYWNrYWdlIHRoYXQgd2lsbCBmdWxmaWxsIHlvdXIgbmVlZHMuCgpZb3UgY2FuIGluc3RhbGwgcGFja2FnZXMgdXNpbmcgdGhlIGNvbW1hbmQgYGluc3RhbGwucGFja2FnZXMoIiIpYAoKWW91IGNhbiBsb2FkIHBhY2thZ2VzIHVzaW5nIHRoZSBjb21tYW5kIGBsaWJyYXJ5KClgCgpZb3UgY2FuIGNoZWNrIGZvciBpbnN0YWxsZWQgcGFja2FnZXMgdXNpbmcgdGhlIGNvbW1hbmQgYGluc3RhbGxlZC5wYWNrYWdlcygpYAoKWW91IGNhbiBjaGVjayBSIHZlcnNpb24gYFIudmVyc2lvbmAKCiMgRXhlcmNpc2UgYnJlYWsgKGluIENvbnNvbGU6IENNRCAtPiBSLmV4ZSkKCiMjIEVudmlyb25tZW50IGFuZCBtYXRoCmBgYHtyIGVudmlyb25tZW50X21hdGgsIGVjaG89VCwgaW5jbHVkZT1ULCBldmFsPUZ9Cmluc3RhbGxlZC5wYWNrYWdlcygpCmdldHdkKCkKUi52ZXJzaW9uCjEgKyAxCjIgKyAzICogNAo0ICogMyArIDIKKDIgKyAzKSAqIDQKKDQgKiAzKSArIDIKMyBeIDIKZXhwKDEpCnBpCjIqcGkqNjM3OCAjIENpcmN1bWZlcmVuY2Ugb2YgZWFydGggYXQgdGhlIGVxdWF0b3IgKGluIGttKQpgYGAKCiMjIFZhcmlhYmxlcwpWYXJpYWJsZXMgYXJlIGFzc2lnbmVkIGEgdmFsdWUgZWl0aGVyIHVzaW5nICI9IiBvciAiPC0iClJlLXdyaXRlIHRoaXMgY2FsY3VsYXRpb24gc28gdGhhdCBpdCBkb2VzbuKAmXQgdXNlIHZhcmlhYmxlczoKYGBge3IgZGVmaW5lX3ZhcmlhYmxlMSwgZWNobz1ULCBpbmNsdWRlPVQsIGV2YWw9VH0KYSA8LSA0ICogMjAKYiA8LSA3CmEgKyBiCmBgYApcbm9ybWFsc2l6ZQpSZS13cml0ZSB0aGlzIGNhbGN1YXRpb24gb3ZlciBtdWx0aXBsZSBsaW5lcywgdXNpbmcgYSB2YXJpYWJsZToKYGBge3IgZGVmaW5lX3ZhcmlhYmxlMiwgZWNobz1ULCBpbmNsdWRlPVR9CjIgKyAyICsgMgphIDwtIDIKYiA8LSAzCmEgKiBiCmBgYAojIERhdGEgc3RydWN0dXJlcwoKIyMgRGF0YSB0eXBlcwoKLSBOdW1lcmljL0RvdWJsZSAoZS5nLiAyLjUsIDEvNSwgMS4wLCBcZG90cykKCi0gSW50ZWdlciAoZS5nLiAxLCAyLCAzLCBcZG90cykKCi0gQ29tcGxleCAoZS5nLiAxICsgMmksIFxkb3RzKQoKLSBMb2dpY2FsIChlLmcuIFRSVUUsIEZBTFNFIG9yIE5BKQoKLSBDaGFyYWN0ZXIgKGUuZy4gImEiLCAicGFwZXIiLCAiMiBwbHVzIDIgPSA1IiwgIlRSVUUiLCBcZG90cykKCi0gRmFjdG9yL0NhdGVnb3JpY2FsICgibWFsZSIsICJmZW1hbGUiLCBcZG90cykKCiMjIERhdGEgc3RydWN0dXJlcwohW1Zpc3VhbGl6YXRpb24gb2YgZGF0YSBzdHJ1Y3R1cmVzXShmaWd1cmVzL3JfZGF0YV9zdHJ1Y3R1cmVzLnBuZykKCiMjIFZlY3RvcnMgKEkpCgpZb3UgY2FuIGNyZWF0ZSBhIHZlY3RvciB1c2luZyB0aGUgY29tbWFuZCBgYygpYApcZm9vdG5vdGVzaXplCmBgYHtyIHZlY3RvcnNfYmFzZSwgaW5jbHVkZT1ULCBlY2hvPVR9CnggPC0gYygxLCAzLCA1LCAxMCkKeApgYGAKXG5vcm1hbHNpemUKClZlY3RvcnMgbXVzdCBjb250YWluIGVsZW1lbnRzIG9mIHRoZSBzYW1lIGRhdGEgdHlwZS4KXGZvb3Rub3Rlc2l6ZQpgYGB7ciB2ZWN0b3JzX3R5cGUsIGluY2x1ZGU9VCwgZWNobz1UfQpjKDEsICJpbnRybyIsIFRSVUUpCmBgYApcbm9ybWFsc2l6ZQoKWW91IGNhbiBtZWFzdXJlIHRoZSBsZW5ndGggb2YgYSB2ZWN0b3IgdXNpbmcgdGhlIGNvbW1hbmQgYGxlbmd0aCgpYApcZm9vdG5vdGVzaXplCmBgYHtyIHZlY3RvcnNfbGVuZ3RoLCBpbmNsdWRlPVQsIGVjaG89VH0KbGVuZ3RoKHgpCmBgYApcbm9ybWFsc2l6ZQoKIyMgVmVjdG9ycyAoSUkpCkl0IGlzIGFsc28gcG9zc2libGUgdG8gZWFzaWx5IGNyZWF0ZSBzZXF1ZW5jZXMKXGZvb3Rub3Rlc2l6ZQpgYGB7ciB2ZWN0b3JzX3NlcXVlbmNlcywgaW5jbHVkZT1ULCBlY2hvPVR9CjE6MTAKc2VxKGZyb20gPSAxLCB0byA9IDIsIGJ5ID0gMC4xKQpyZXAoIkEiLCB0aW1lcyA9IDUpCmBgYApcbm9ybWFsc2l6ZQoKIyMgVmVjdG9ycyAoSUlJKQpZb3UgY2FuIGNvbWJpbmUgZGlmZmVyZW50IHZlY3RvcnMKXGZvb3Rub3Rlc2l6ZQpgYGB7ciB2ZWN0b3JzX211bHRpcGxlLCBpbmNsdWRlPVQsIGVjaG89VH0KeCA8LSAxOjMgIyBmcm9tIDEgdG8gMwp5IDwtIGMoMTAsIDE1KSAjIDEwIGFuZCAxNQp6IDwtIGMoeCx5KSAjIHggZmlyc3QgYW5kIHRoZW4geQp6CmBgYApcbm9ybWFsc2l6ZQoKQW5kIHlvdSBjYW4gcmVwZWF0IHZlY3RvcnMgKG9yIGl0cyBlbGVtZW50cykKXGZvb3Rub3Rlc2l6ZQpgYGB7ciB2ZWN0b3JzX3JlcGVhdCwgaW5jbHVkZT1ULCBlY2hvPVR9CnogPC0gcmVwKHksIGVhY2g9MykgIyByZXBlYXQgZWFjaCBlbGVtZW50IDMgdGltZXMKegp6IDwtIHJlcCh5LCB0aW1lcz0zKSAjIHJlcGVhdCB0aGUgd2hvbGUgdmVjdG9yIDMgdGltZXMKegpgYGAKXG5vcm1hbHNpemUKCiMjIFN1YnNldHRpbmcgVmVjdG9ycwpcZm9vdG5vdGVzaXplCmBgYHtyIHZlY3RvcnNfc3Vic2V0LCBpbmNsdWRlPVQsIGVjaG89VH0KeCA8LSBjKDEsIDUsIDEwLCA3KQp4IDwgNiAjIGlzIHRoZSBlbGVtZW50IGxvd2VyIHRoYW4gNj8KeCA9PSAxMCAjIGlzIHRoZSBlbGVtZW50IGVxdWFsIHRvIDEwPwp4WzJdICMgd2hpY2ggZWxlbWVudCBpcyBpbiB0aGUgc2Vjb25kIHBvc2l0aW9uPwp4WzE6Ml0gIyB3aGljaCBlbGVtZW50cyBhcmUgaW4gdGhlIGZpcnN0IDIgcG9zaXRpb25zPwp4W2MoMSwzLDQpXSAjIHdoaWNoIGVsZW1lbnRzIGFyZSBpbiBwb3NpdGlvbnMgMSwgMyBhbmQgND8KYGBgCiMjIFN1YnNldHRpbmcgVmVjdG9ycwpcZm9vdG5vdGVzaXplCmBgYHtyIHZlY3RvcnNfc3Vic2V0MiwgaW5jbHVkZT1ULCBlY2hvPVR9Cm4gPC0gYygxLCA0LCA1LCA2LCA3LCAyLCAzLCA0LCA1LCA2KSAjIGNyZWF0ZXMgYSB2ZWN0b3IsIHN0b3JlcyBpdCBpbiBuCmxlbmd0aChuKSAjIG51bWJlciBvZiBlbGVtZW50cwpuWzNdICMgZXh0cmFjdCAzIHJkIGVsZW1lbnQgaW4gbgpuWy0yXSAjIGV4dHJhY3QgYWxsIG9mIG4gYnV0IDJuZCBlbGVtZW50Cm5bYygxLDMsNCldICMgZXh0cmFjdCBmaXJzdCwgdGhpcmQsIGFuZCBmb3VydGggZWxlbWVudCBvZiBuCm5bbiA8IDRdICNleHRyYWN0IGFsbCBlbGVtZW50cyBpbiBuIHNtYWxsZXIgdGhhbiA0Cm5bbiA8IDQgJiBuICE9IDFdICMgZXh0cmFjdCBlbGVtZW50IHNtYWxsZXIgdGhhbiA0IEFORCBkaWZmZXJlbnQgZnJvbSAxCmBgYApcbm9ybWFsc2l6ZQoKIyMgRXhlcmNpc2UgYnJlYWsKXGZvb3Rub3Rlc2l6ZQpgYGB7ciB2ZWN0b3JzX3N1YnNldDQsIGluY2x1ZGU9VCwgZWNobz1UfQpuIDwtIGMoMSwgNCwgNSwgNiwgNywgMiwgMywgNCwgNSwgNikgIyBjcmVhdGVzIGEgdmVjdG9yLCBzdG9yZXMgaXQgaW4gbgpgYGAKXG5vcm1hbHNpemUKKiBudW1iZXIgb2YgZWxlbWVudHMKKiBleHRyYWN0IGZpcnN0IHRocmVlIGVsZW1lbnRzIG9mIG4KKiBleHRyYWN0IGFsbCBvZiBuIGJ1dCAzbmQgZWxlbWVudAoqIGV4dHJhY3QgZmlyc3QsIHRoaXJkLCBhbmQgZm91cnRoIGVsZW1lbnQgb2YgbgoqIGV4dHJhY3QgZWxlbWVudCBzbWFsbGVyIHRoYW4gNCBPUiBlcXVhbCB0byA1CgoKIyMgRXhlcmNpc2UgYnJlYWsKV2UgY2FuIGNyZWF0ZSBhbmQgaW5kZXggY2hhcmFjdGVyIHZlY3RvcnMgYXMgd2VsbC4gQSBjYWZlIGlzIHVzaW5nIFIgdG8gY3JlYXRlIHRoZWlyIG1lbnUuClxmb290bm90ZXNpemUKYGBge3IgdmVjdG9yc19zdWJzZXQ1LCBpbmNsdWRlPVQsIGVjaG89VH0KaXRlbXMgPC0gYygiYXBwbGVzIiwgIm9yYW5nZXMiLCAiZWdncyIsICJ0b21hdG9lcyIsICJiYW5hbmFzIikKYGBgClxub3JtYWxzaXplCiogV2hhdCBkb2VzIGl0ZW1zWy0zXSBwcm9kdWNlPwoqIEJhc2VkIG9uIHdoYXQgeW91IGZpbmQsIHVzZSBpbmRleGluZyB0byBjcmVhdGUgYSB2ZXJzaW9uIG9mIGl0ZW1zIHdpdGhvdXQgImJhbmFuYXMiLgoqIFVzZSBpbmRleGluZyB0byBjcmVhdGUgYSB2ZWN0b3IgY29udGFpbmluZyBhcHBsZXMsIGVnZ3MsIHRvbWF0b2VzLCBiYW5hbmFzLCBhbmQgYmFuYW5hcy4KKiBBZGQgYSBuZXcgaXRlbSwgImxlbW9ucyIsIHRvIGl0ZW1zLgoqIE1ha2UgaXRlbXNbM10gImJlcnJpZXMiLgoKCiMjIFZlY3RvcnMnIE9wZXJhdGlvbnMKXGZvb3Rub3Rlc2l6ZQpgYGB7ciB2ZWN0b3JzX29wZXJhdGlvbnMsIGluY2x1ZGU9VCwgZWNobz1UfQp4IDwtIGMoMSw1LDEwLDcpCngrMiAjIGFkZHMgYSBzY2FsYXIgdG8gYWxsIGVsZW1lbnRzCnheMiAjIHdoYXQncyB0aGUgc3F1YXJlIG9mIGFsbCBlbGVtZW50cz8KYGBgClxub3JtYWxzaXplCgojIyBNYXRyaWNlcyAoSSkKWW91IGNhbiBjcmVhdGUgYSBtYXRyaXggdXNpbmcgdGhlIGNvbW1hbmQgYG1hdHJpeCgpYApcZm9vdG5vdGVzaXplCmBgYHtyIG1hdHJpeCwgaW5jbHVkZT1ULCBlY2hvPVR9ClggPC0gbWF0cml4KDE6OSwgbnJvdyA9IDMsIG5jb2wgPSAzKQpYCmBgYApcbm9ybWFsc2l6ZQoKIyMgTWF0cmljZXMgKElJKQpSIGF1dG9tYXRpY2FsbHkgaW5zZXJ0cyBlbGVtZW50cyBieSBjb2x1bW5zLCBidXQgd2UgY2FuIGFzayB0byBpbmNsdWRlIGJ5IHJvd3MKXGZvb3Rub3Rlc2l6ZQpgYGB7ciBtYXRyaXhfYnlyb3csIGluY2x1ZGU9VCwgZWNobz1UfQpYIDwtIG1hdHJpeCgxOjksIG5yb3cgPSAzLCBuY29sID0gMywgYnlyb3cgPSBUUlVFKQpYCmBgYApcbm9ybWFsc2l6ZQoKWW91IGRvbid0IGV2ZW4gaGF2ZSB0byBzcGVjaWZ5IHRoZSBvcHRpb25zIG5hbWVzClxmb290bm90ZXNpemUKYGBge3IgbWF0cml4X3NpbGVudCwgaW5jbHVkZT1ULCBlY2hvPVR9ClggPC0gbWF0cml4KDE6OCwgMiwgNCwgVCkKWApgYGAKXG5vcm1hbHNpemUKCiMjIE1hdHJpY2VzIChJSUkpCk1hdHJpY2VzIGNhbiBhbHNvIGJlIGNyZWF0ZWQgYnkgY29tYmluaW5nIHZlY3RvcnMKXGZvb3Rub3Rlc2l6ZQpgYGB7ciBtYXRyaXhfZnJvbXZlY3RvcnMsIGluY2x1ZGU9VCwgZWNobz1UfQpYIDwtIGNiaW5kKDE6NCwgNjo5KSAjIGJpbmRzIHRoZW0gYXMgY29sdW1ucwpYClggPC0gcmJpbmQoMTo0LCA2OjkpICMgYmluZHMgdGhlbSBhcyByb3dzClgKYGBgClxub3JtYWxzaXplCgojIyBTdWJzZXR0aW5nIE1hdHJpY2VzClxmb290bm90ZXNpemUKYGBge3IgbWF0cml4X3N1YnNldCwgaW5jbHVkZT1ULCBlY2hvPVR9Clg+NSAjIGVsZW1lbnRzIGxhcmdlciB0aGFuIDUKWFsxLDRdICMgZWxlbWVudCBvZiBmaXJzdCByb3csIGZvdXJ0aCBjb2x1bW4/ClhbMSxdICMgZWxlbWVudCBpbiB0aGUgZmlyc3Qgcm93PwpYWywyXSAjIGVsZW1lbnRzIGluIHRoZSBzZWNvbmQgY29sdW1ucz8KYGBgClxub3JtYWxzaXplCgojIyBMaXN0cwpBIGxpc3QgaXMgYSBvbmUtZGltZW5zaW9uYWwgaGV0ZXJvZ2VuZW91cyBkYXRhIHN0cnVjdHVyZS4KCkl0IGlzIGluZGV4ZWQgbGlrZSBhIHZlY3RvciB3aXRoIGEgc2luZ2xlIGludGVnZXIgdmFsdWUgKG9yIGEgbmFtZSksIGJ1dCBlYWNoIGVsZW1lbnQgY2FuIGNvbnRhaW4gYW4gZWxlbWVudCBvZiBhbnkgZGF0YSB0eXBlLgpcZm9vdG5vdGVzaXplCmBgYHtyIGxpc3QsIGluY2x1ZGU9VCwgZWNobz1UfQp4IDwtIDE6NAp5IDwtIGMoImEiLCAiYiIsICJjIikKTCA8LSBsaXN0KG51bWJlcnMgPSB4LCBsZXR0ZXJzID0geSkKTApgYGAKXG5vcm1hbHNpemUKCiMjIFN1YnNldHRpbmcgTGlzdHMKXGZvb3Rub3Rlc2l6ZQpgYGB7ciBsaXN0X3N1YnNldHRpbmcsIGluY2x1ZGU9VCwgZWNobz1UfQpMW1sxXV0gIyBleHRyYWN0IHRoZSBmaXJzdCBlbGVtZW50CkwkbnVtYmVycyAjIGV4dHJhY3QgdGhlIGVsZW1lbnQgY2FsbGVkIG51bWJlcnMKTCRsZXR0ZXJzICMgZXh0cmFjdCB0aGUgZWxlbWVudCBjYWxsZWQgbGV0dGVycwpgYGAKXG5vcm1hbHNpemUKCllvdSBjYW4gZXZlbiAid29yayIgd2l0aCB0aGUgc3Vic2V0dGVkIGVsZW1lbnQ6Clxmb290bm90ZXNpemUKYGBge3IgbGlzdF9zdWJzZXR0aW5nMiwgaW5jbHVkZT1ULCBlY2hvPVR9CkwkbnVtYmVyc1sxOjNdID4gMgpgYGAKXG5vcm1hbHNpemUKCiMjIEV4ZXJjaXNlIGJyZWFrClxmb290bm90ZXNpemUKYGBge3IgbGV0dGVycywgaW5jbHVkZT1ULCBlY2hvPVQsIGV2YWw9Rn0KbGV0dGVycwpMRVRURVJTCm15X2xldHRlcnMgPC0gY2JpbmQoTEVUVEVSUywgbGV0dGVycykKY2xhc3MobXlfbGV0dGVycykKZGltKG15X2xldHRlcnMpCm15X2xldHRlcnMgPC0gY2JpbmQobXlfbGV0dGVycywgc2VxKDE6bGVuZ3RoKGxldHRlcnMpKSkKYGBgClxub3JtYWxzaXplCldoYXQgbnVtYmVyIGlzIHRoZSBsZXR0ZXIgRiBpbiB0aGUgRW5nbGlzaCBhbHBoYWJldD8KCgojIyBFeGVyY2lzZQpgYGB7ciBkbmFfZXgxLCBpbmNsdWRlID0gVCwgZWNobz1ULCBldmFsPUZ9CiMgdmVjdG9yIC4uLiBldGMuCm15X2RuYSA8LSAiQUFDR0FBVEdBR1RBQUFUR0FHVEFBQVRHQUFHR0FBVEdBVFRBVFRDQ1RUR0NUVFRBR0FBQ1RUQ1RHR0FBVFRBR0FHR0FDQQpBVEFUVEFBVEFBVEFDQ0FUQ0dDQUNBR1RHVFRUQ1RUVEdUVEdUVEFBVEdDVEFDQUFDQVRBQ0FBQUdBR0dBQUdDQVRHQ0FHIgpteV9kbmEKbGVuZ3RoKG15X2RuYSkKY2xhc3MobXlfZG5hKQpzdHIobXlfZG5hKQpuY2hhcihteV9kbmEpCgojIGFwcHIxCm15X2RuYV9jb21tYSA8LSBzYXBwbHkoc3Ryc3BsaXQoCiAgeCA9IG15X2RuYSwgc3BsaXQgPSAiIiwgZml4ZWQgPSBUUlVFKSwKICAgICAgIGZ1bmN0aW9uKHgpIHBhc3RlKHgsIGNvbGxhcHNlID0gIl8iKSkKbGVuZ3RoKG15X2RuYV9jb21tYSkKc3RyKG15X2RuYV9jb21tYSkKCm15X2RuYV9saXN0IDwtIHN0cnNwbGl0KHggPSBteV9kbmEsIHNwbGl0ID0gIiIsIGZpeGVkID0gVFJVRSkKbGVuZ3RoKG15X2RuYV9saXN0KQpjbGFzcyhteV9kbmFfbGlzdCkKbXlfZG5hX3ZlY3RvciA8LSB1bmxpc3QobXlfZG5hX2xpc3QpCmxlbmd0aChteV9kbmFfbGlzdFtbMV1dKQpzdHIobXlfZG5hX3ZlY3RvcikKbGVuZ3RoKG15X2RuYV92ZWN0b3IpCgojIGZpcnN0IG51Y2xlb3RpZGUKbXlfZG5hX3ZlY3RvclsxXQojIGluZGV4aW5nIDE6bmNoYXIobXlfZG5hKQpteV9kbmFfdmVjdG9yWzE6NTBdCiMgdW5pcXVlIGNoYXJhY3RlcnMKdW5pcXVlKG15X2RuYV92ZWN0b3IpCgojIG51bWJlciBvZiBBcwoobXlfZG5hX3ZlY3RvciA9PSAiQSIpCmxlbmd0aChteV9kbmFfdmVjdG9yW215X2RuYV92ZWN0b3IgPT0gIkEiXSkKCiMgcmVtb3ZlIFxuCnJlbW92ZV9udWMgPC0gbWF0Y2goIlxuIiwgbXlfZG5hX3ZlY3RvcikKd2hpY2gobXlfZG5hX3ZlY3RvciAlaW4lIGMoIlxuIiwgIlgiKSkKbXlfZG5hX3ZlY3RvcltyZW1vdmVfbnVjXSA8LSAiQSIKbXlfZG5hX3ZlY3Rvcl8yIDwtIG15X2RuYV92ZWN0b3JbLTcxXQp1bmlxdWUobXlfZG5hX3ZlY3Rvcl8yKQpgYGAKCiMjIERhdGEgRnJhbWVzIChJKQpBIGBkYXRhLmZyYW1lYCBpcyBzaW1pbGFyIHRvIGEgdHlwaWNhbCBgc3ByZWFkc2hlZXRgIGluIGV4Y2VsLgoKVGhlcmUgYXJlIHJvd3MsIGFuZCB0aGVyZSBhcmUgY29sdW1ucy4KCkEgcm93IGlzIHR5cGljYWxseSB0aG91Z2h0IG9mIGFzIGFuIFxlbXBoe29ic2VydmF0aW9ufS4KCkEgY29sdW1uIGlzIGEgY2VydGFpbiBcZW1waHt2YXJpYWJsZX0sIGNoYXJhY3RlcmlzdGljIG9yIGZlYXR1cmUgb2YgdGhhdCBvYnNlcnZhdGlvbi4KCiMjIERhdGEgRnJhbWVzIChJSSkKQSBkYXRhIGZyYW1lIGlzIGEgbGlzdCBvZiBjb2x1bW4gdmVjdG9ycyB3aGVyZToKCi0gZWFjaCBjb2x1bW4gaGFzIGEgbmFtZQoKLSBlYWNoIGNvbHVtbiBtdXN0IGNvbnRhaW4gdGhlIHNhbWUgZGF0YSB0eXBlLCBidXQgdGhlIGRpZmZlcmVudCBjb2x1bW5zIGNhbiBzdG9yZSBkaWZmZXJlbnQgZGF0YSB0eXBlcy4KCi0gZWFjaCBjb2x1bW4gbXVzdCBiZSBvZiBzYW1lIGxlbmd0aAoKIyMgRGF0YSBGcmFtZXMgKElJSSkKXHNjcmlwdHNpemUKYGBge3IgZGF0YWZyYW1lLCBpbmNsdWRlPVQsIGVjaG89VH0Kc2V0LnNlZWQoMSkKZGYgPC0gIGRhdGEuZnJhbWUoaWQgPSAxOjUsCiAgbmFtZSA9IGMoIkRpZWdvIiwgIlNhbXVlbCIsICJNYXJjbyIsICJKYXZpZXIiLCAiTGVvbmFyZG8iKSwKICBzdXJuYW1lID0gYygiTWlsaXRvIiwgIkV0bydvIiwgIk1hdGVyYXp6aSIsICJaYW5ldHRpIiwgIkJvbnVjY2kiKSwKICB3YWdlID0gcm5vcm0obj01LCBtZWFuID0gMTBeNSwgc2QgPSAxMF4zKSwgIyBub3JtYWwgcmFuZG9tIHNhbXBsZQogIG9yaWdpbiA9IGMoIkFyZ2VudGluYSIsICJDYW1lcm9vbiIsICJJdGFseSIsICJBcmdlbnRpbmEiLCAiSXRhbHkiKSwKICB0cmVibGVfd2lubmVyID0gYyhULCBULCBULCBULCBGKQogICkKZGYKYGBgClxub3JtYWxzaXplCgpZb3UgY2FuIHZlcmlmeSB0aGUgc2l6ZSBvZiB0aGUgYGRhdGEuZnJhbWVgIHVzaW5nIHRoZSBjb21tYW5kIGBkaW0oKWAKCllvdSBjYW4gZ2V0IHRoZSBgZGF0YSB0eXBlYCBpbmZvIHVzaW5nIHRoZSBjb21tYW5kIGBzdHIoKWAKCiMjIFN1YnNldHRpbmcgRGF0YSBGcmFtZXMgKEkpClxmb290bm90ZXNpemUKYGBge3IgZGF0YWZyYW1lX3N1YnNldCwgaW5jbHVkZT1ULCBlY2hvPVR9CmRmJG5hbWUgIyBzdWJzZXQgYSBjb2x1bW4KZGZbLGMoMiw1KV0gIyBjYW4gYWxzbyBzdWJzZXQgbGlrZSBhIG1hdHJpeApgYGAKXG5vcm1hbHNpemUKCiMjIFN1YnNldHRpbmcgRGF0YSBGcmFtZXMgKElJKQpcZm9vdG5vdGVzaXplCmBgYHtyIGRhdGFmcmFtZV9zdWJzZXQyLCBpbmNsdWRlPVQsIGVjaG89VH0KaGVhZChkZiwgbj0zKSAjIGZpcnN0IG4gb2JzZXJ2YXRpb25zCnRhaWwoZGYsIG49MykgIyBsYXN0IG4gb2JzZXJ2YXRpb25zCmBgYApcbm9ybWFsc2l6ZQoKIyMgSW5zcGVjdGluZyBkYXRhIGZyYW1lcyAoSSkKUiBjb21lcyB3aXRoIG1hbnkgZGF0YSBiYXNlcyBpbmNsdWRlZC4gVGhlc2UgY2FuIGJlIHVzZWQgZm9yIGxlYXJuaW5nIFIuCgpPbmUgb2YgdGhlIG1vc3QgZmFtb3VzIGlzIHRoZSBvbmUgY2FsbGVkIGBtdGNhcnNgLgpcc2NyaXB0c2l6ZQpgYGB7ciBkYXRhZnJhbWVfbXRjYXJzLCBpbmNsdWRlPVQsIGVjaG89VH0KaGVhZChtdGNhcnMpCnRhaWwobXRjYXJzKQpkaW0obXRjYXJzKQpgYGAKXG5vcm1hbHNpemUKCiMjIEluc3BlY3RpbmcgZGF0YSBmcmFtZXMgKElJKQpcc2NyaXB0c2l6ZQpgYGB7ciBkYXRhZnJhbWVfbXRjYXJzMiwgaW5jbHVkZT1ULCBlY2hvPVR9CnN0cihtdGNhcnMpCm5hbWVzKG10Y2FycykKYGBgClxub3JtYWxzaXplCgojIyBTdWJzZXR0aW5nIGRhdGEgZnJhbWVzIChJSUkpCldlIGFyZSBpbnRlcmVzdGluZyBpbiB0aGUgY3lsaW5kZXJzIGFuZCB0aGUgd2VpZ2h0cyBvZiBpbmVmZmljaWVudCBjYXJzIChsb3dlciB0aGFuIDE1IG1pbGVzIHBlciBnYWxsb24pLgpcc2NyaXB0c2l6ZQpgYGB7ciBkYXRhZnJhbWVfc3Vic2V0X210Y2FycywgaW5jbHVkZT1ULCBlY2hvPVR9CnBvbGxfY2FycyA8LSBtdGNhcnNbbXRjYXJzJG1wZzwxNSwgYygiY3lsIiwgInd0IildCnBvbGxfY2FycwpgYGAKXG5vcm1hbHNpemUKCiMjIFN1YnNldHRpbmcgZGF0YSBmcmFtZXMgKElWKQpBbHRlcm5hdGl2ZWx5Ogpcc2NyaXB0c2l6ZQpgYGB7ciBkYXRhZnJhbWVfc3Vic2V0X210Y2FyczIsIGluY2x1ZGU9VCwgZWNobz1UfQpwb2xsX2NhcnMgPC0gc3Vic2V0KG10Y2Fycywgc3Vic2V0ID0gbXBnPDE1LCBzZWxlY3QgPSBjKCJjeWwiLCAid3QiKSkKcG9sbF9jYXJzCmBgYApcbm9ybWFsc2l6ZQoKIyMgSW1wb3J0aW5nIGRvd25sb2FkZWQgZGF0YSBmcmFtZXMgKC5jc3YpCllvdSBjYW4gaW1wb3J0IGNzdiBkYXRhIHRoYXQgeW91IGhhdmUgZG93bmxvYWRlZCBmcm9tIGFueSBleHRlcm5hbCBzb3VyY2UgdXNpbmc6ClxzY3JpcHRzaXplCmBgYHtyIGltcG9ydF9kYXRhMiwgaW5jbHVkZT1ULCBlY2hvPVQsIGV2YWw9Rn0Kc2V0d2QoImRhdGEiKQpueWNfYWIgPC0gcmVhZC5jc3YoIkFCX05ZQ18yMDIwLmNzdiIpCmBgYApcbm9ybWFsc2l6ZQoKd2hlcmU6CgotIGBzZXR3ZCgpYCBzZXRzIHRoZSB3b3JraW5nIGRpcmVjdG9yeSB0byB0aGUgcGxhY2Ugd2hlcmUgdGhlIGRhdGEgaXMgc2F2ZWQ7CgotIGByZWFkLmNzdigpYCBsb2FkcyB0aGUgY3N2IGZpbGUgd2l0aCB0aGUgc3BlY2lmaWVkIG5hbWUuCgpZb3UgY2FuIHNpbWlsYXJseSBpbXBvcnQgYWxtb3N0IGFueSBraW5kIG9mIGRhdGEgZmlsZSBzdG9yZWQgaW4gb3RoZXIgZm9ybWF0cyAoLnhscywgLnR4dCwgLmNzdiwgLmR0YSwgLlJkYXRhLCAubWF0LCAuLi4pCgojIyBJbXBvcnRpbmcgZG93bmxvYWRlZCBkYXRhIGZyYW1lcyAoLnR4dCkKSW50ZXJmZXJvbiByZWd1bGF0b3J5IGZhY3RvciA2IG1vdXNlClxzY3JpcHRzaXplCmBgYHtyIGltcG9ydF9kYXRhMywgaW5jbHVkZT1ULCBlY2hvPVQsIGV2YWw9Rn0Kc2V0d2QoImRhdGEiKQppcmY2IDwtIHJlYWQudGFibGUoImlyZjYudHh0IiwgaGVhZGVyID0gVFJVRSwgcm93Lm5hbWVzID0gMSkKCiMgZXhwbG9yZQpoZWFkKGlyZjYpCm5jb2woaXJmNik7IG5yb3coaXJmNikKZGltKGlyZjYpCmBgYAojIyBJbXBvcnRpbmcgZG93bmxvYWRlZCBkYXRhIGZyYW1lcyAoLnR4dCkKXHNjcmlwdHNpemUKYGBge3IgaW1wb3J0X2RhdGE0LCBpbmNsdWRlPVQsIGVjaG89VCwgZXZhbD1GfQppcmY2IDwtIHJlYWQudGFibGUoImRhdGEvaXJmNi50eHQiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKQpjbGFzcyhpcmY2KQpzdHIoaXJmNikKY29sbmFtZXMoaXJmNikKaGVhZChyb3duYW1lcyhpcmY2KSkKCmBgYAojIyBJbXBvcnRpbmcgZG93bmxvYWRlZCBkYXRhIGZyYW1lcyAoLnR4dCkKXHNjcmlwdHNpemUKYGBge3IgaW1wb3J0X2RhdGE1LCBpbmNsdWRlPVQsIGVjaG89VCwgZXZhbD1GfQppcmY2IDwtIHJlYWQudGFibGUoImRhdGEvaXJmNi50eHQiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKQoKaGVhZChpcmY2WydFMTcuNUtPMSddKSAjIFJldHJpZXZlIG9ubHkgRTE3LjVLTzEgZGF0YQpoZWFkKGlyZjZbLCAxXSkgIyBhcyB3ZWxsCmhlYWQoaXJmNiRFMTcuNUtPMSkgIyBhcyB3ZWxsCmRpbShpcmY2WywgLTFdKTsgY29sbmFtZXMoaXJmNlssIC0xXSkgICMgRXhjbHVkZSBFMTcuNUtPMSBkYXRhCmBgYAoKIyBFeGVyY2lzZSBicmVhawoKIyMgVG90YWwgbHVuZyBjYXBhY2l0eQpcZm9vdG5vdGVzaXplCmBgYHtyIHRsYywgaW5jbHVkZT1ULCBlY2hvPVQsIGV2YWw9Rn0KICB0bGMgPC0gcmVhZC5jc3YoImRhdGEvdGxjLmNzdiIpCiAgc3RyKHRsYykKICBzdW1tYXJ5KHRsYykKICBzdWJzZXQodGxjLCBzZXggPT0gMSkKICBzdWJzZXQodGxjLCBhZ2UgPCAyMCwgdGxjKQogIG1lYW4oc3Vic2V0KHRsYywgc2V4ID09IDEsIHNlbGVjdCA9IHRsYywgZHJvcCA9IFQpKQpgYGAKCiMgQXBwbHkKIyMgVmVjdG9yaXphdGlvbgoqIFJldHVybnMJYQl2ZWN0b3Igb3IgYXJyYXkgb3IgbGlzdAoqIG9idGFpbmVkIGJ5IGFwcGx5aW5nIGEgZnVuY3Rpb24gdG8gbWFyZ2lucyBvZiBhIG1hdHJpeAoqIGFwcGx5KFgsIE1BUkdJTiwgRlVOLCAuLi4pCiogWCA6CWRhdGEKKiBNQVJHSU4gOgkxIGZvciByb3dzLCAyIGZvciBjb2x1bW5zCiogRlVOOiBmdW5jdGlvbgogIFxzY3JpcHRzaXplCiAgYGBge3IgYXBwbHksIGluY2x1ZGU9VCwgZWNobz1UfQp4IDwtIGNiaW5kKHgxID0gMywgeDIgPSBjKDQ6MSwgMjo1KSkKY29sLnN1bXMgPC0gYXBwbHkoeCwgMiwgc3VtKQpyb3cuc3VtcyA8LSBhcHBseSh4LCAxLCBzdW0pCmBgYAoKYGBge3Iga25pdF9leGl0LCBpbmNsdWRlPUYsIGVjaG89Rn0Ka25pdF9leGl0KCkKYGBgCg==