R operations

R as a calculator (I)

2+2
## [1] 4
2-2
## [1] 0
2*2
## [1] 4
2/2
## [1] 1

R as a calculator (II)

log(1)
## [1] 0
exp(1)
## [1] 2.718282
log(exp(1))
## [1] 1
sqrt(25)
## [1] 5

The help

?log
help(log)

Otherwise:

  • Google your error message

  • Ask for help in Stack Overflow

Packages

R comes with a number of built-in functions and datasets, but one of the main strengths of R as an open-source project is its package system.

Packages gives you access to additional functions and datasets.

If you want to do something which is not doable with the R basic functions, there is a good chance that there exist a package that will fulfill your needs.

You can install packages using the command install.packages("")

You can load packages using the command library()

You can check for installed packages using the command installed.packages()

You can check R version R.version

Exercise break (in Console: CMD -> R.exe)

Environment and math

installed.packages()
getwd()
R.version
1 + 1
2 + 3 * 4
4 * 3 + 2
(2 + 3) * 4
(4 * 3) + 2
3 ^ 2
exp(1)
pi
2*pi*6378 # Circumference of earth at the equator (in km)

Variables

Variables are assigned a value either using “=” or “<-” Re-write this calculation so that it doesn’t use variables:

a <- 4 * 20
b <- 7
a + b
## [1] 87

Re-write this calcuation over multiple lines, using a variable:

2 + 2 + 2
## [1] 6
a <- 2
b <- 3
a * b
## [1] 6

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”, )

Data structures

Visualization of data structures

Vectors (I)

You can create a vector using the command c()

x <- c(1, 3, 5, 10)
x
## [1]  1  3  5 10

Vectors must contain elements of the same data type.

c(1, "intro", TRUE)
## [1] "1"     "intro" "TRUE"

You can measure the length of a vector using the command length()

length(x)
## [1] 4

Vectors (II)

It is also possible to easily create sequences

1:10
##  [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
rep("A", times = 5)
## [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:

L$numbers[1:3] > 2
## [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.

head(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
tail(mtcars)
##                 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
dim(mtcars)
## [1] 32 11

Inspecting data frames (II)

str(mtcars)
## '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 ...
names(mtcars)
##  [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:

  • setwd() sets the working directory to the place where the data is saved;

  • read.csv() loads the csv file with the specified name.

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

Exercise break

Total lung capacity

  tlc <- read.csv("data/tlc.csv")
  str(tlc)
  summary(tlc)
  subset(tlc, sex == 1)
  subset(tlc, age < 20, tlc)
  mean(subset(tlc, sex == 1, select = tlc, drop = T))

Apply

Vectorization

  • Returns a vector or array or list

  • obtained by applying a function to margins of a matrix

  • apply(X, MARGIN, FUN, …)

  • X : data

  • MARGIN : 1 for rows, 2 for columns

  • FUN: function

    x <- cbind(x1 = 3, x2 = c(4:1, 2:5))
    col.sums <- apply(x, 2, sum)
    row.sums <- apply(x, 1, sum)
LS0tCnRpdGxlOiAgICAgICAgIkxlY3R1cmUgMTogUiBwcm9ncmFtbWluZyIKIyBzdWJ0aXRsZTogICAiQmFzZWQgb246IGh0dHBzOi8vZ2l0aHViLmNvbS9tYXR0aWFndWVyaW5pL3NsaWRlcy1pbnRyby10by1SIgojIGF1dGhvcjogICAgICJNYXJpYW0gUi4gUml6a2FsbGFoIgojIGRhdGU6ICAgICAgICIyMDIxIgojIG91dHB1dDogICAgIGJlYW1lcl9wcmVzZW50YXRpb24KIyB0aGVtZTogICAgICBib3hlcwojIGNvbG9ydGhlbWU6IGRvbHBoaW4KIyBmb250dGhlbWU6ICBzZXJpZgojIHRvYzogICAgICAgIFRSVUUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShrbml0cikKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCiMgUiBvcGVyYXRpb25zCgojIyBSIGFzIGEgY2FsY3VsYXRvciAoSSkKXGZvb3Rub3Rlc2l6ZQpgYGB7ciBjYWxjdWxhdG9yMSwgaW5jbHVkZT1ULCBlY2hvPVR9CjIrMgoyLTIKMioyCjIvMgpgYGAKXG5vcm1hbHNpemUKCiMjIFIgYXMgYSBjYWxjdWxhdG9yIChJSSkKXGZvb3Rub3Rlc2l6ZQpgYGB7ciBjYWxjdWxhdG9yMiwgaW5jbHVkZT1ULCBlY2hvPVR9CmxvZygxKQpleHAoMSkKbG9nKGV4cCgxKSkKc3FydCgyNSkKYGBgClxub3JtYWxzaXplCgojIyBUaGUgaGVscApcZm9vdG5vdGVzaXplCmBgYHtyIGhlbHAsIGluY2x1ZGU9VCwgZWNobz1ULCBldmFsID0gRkFMU0V9Cj9sb2cKaGVscChsb2cpCmBgYApcbm9ybWFsc2l6ZQoKT3RoZXJ3aXNlOgoKLSBHb29nbGUgeW91ciBlcnJvciBtZXNzYWdlCgotIEFzayBmb3IgaGVscCBpbiBTdGFjayBPdmVyZmxvdwoKIyMgUGFja2FnZXMKUiBjb21lcyB3aXRoIGEgbnVtYmVyIG9mIGJ1aWx0LWluIGZ1bmN0aW9ucyBhbmQgZGF0YXNldHMsIGJ1dCBvbmUgb2YgdGhlIG1haW4gc3RyZW5ndGhzIG9mIFIgYXMgYW4gb3Blbi1zb3VyY2UgcHJvamVjdCBpcyBpdHMgcGFja2FnZSBzeXN0ZW0uCgpQYWNrYWdlcyBnaXZlcyB5b3UgYWNjZXNzIHRvIGFkZGl0aW9uYWwgZnVuY3Rpb25zIGFuZCBkYXRhc2V0cy4KCklmIHlvdSB3YW50IHRvIGRvIHNvbWV0aGluZyB3aGljaCBpcyBub3QgZG9hYmxlIHdpdGggdGhlIFIgYmFzaWMgZnVuY3Rpb25zLCB0aGVyZSBpcyBhIGdvb2QgY2hhbmNlIHRoYXQgdGhlcmUgZXhpc3QgYSBwYWNrYWdlIHRoYXQgd2lsbCBmdWxmaWxsIHlvdXIgbmVlZHMuCgpZb3UgY2FuIGluc3RhbGwgcGFja2FnZXMgdXNpbmcgdGhlIGNvbW1hbmQgYGluc3RhbGwucGFja2FnZXMoIiIpYAoKWW91IGNhbiBsb2FkIHBhY2thZ2VzIHVzaW5nIHRoZSBjb21tYW5kIGBsaWJyYXJ5KClgCgpZb3UgY2FuIGNoZWNrIGZvciBpbnN0YWxsZWQgcGFja2FnZXMgdXNpbmcgdGhlIGNvbW1hbmQgYGluc3RhbGxlZC5wYWNrYWdlcygpYAoKWW91IGNhbiBjaGVjayBSIHZlcnNpb24gYFIudmVyc2lvbmAKCiMgRXhlcmNpc2UgYnJlYWsgKGluIENvbnNvbGU6IENNRCAtPiBSLmV4ZSkKCiMjIEVudmlyb25tZW50IGFuZCBtYXRoCmBgYHtyIGVudmlyb25tZW50X21hdGgsIGVjaG89VCwgaW5jbHVkZT1ULCBldmFsPUZ9Cmluc3RhbGxlZC5wYWNrYWdlcygpCmdldHdkKCkKUi52ZXJzaW9uCjEgKyAxCjIgKyAzICogNAo0ICogMyArIDIKKDIgKyAzKSAqIDQKKDQgKiAzKSArIDIKMyBeIDIKZXhwKDEpCnBpCjIqcGkqNjM3OCAjIENpcmN1bWZlcmVuY2Ugb2YgZWFydGggYXQgdGhlIGVxdWF0b3IgKGluIGttKQpgYGAKCiMjIFZhcmlhYmxlcwpWYXJpYWJsZXMgYXJlIGFzc2lnbmVkIGEgdmFsdWUgZWl0aGVyIHVzaW5nICI9IiBvciAiPC0iClJlLXdyaXRlIHRoaXMgY2FsY3VsYXRpb24gc28gdGhhdCBpdCBkb2VzbuKAmXQgdXNlIHZhcmlhYmxlczoKYGBge3IgZGVmaW5lX3ZhcmlhYmxlMSwgZWNobz1ULCBpbmNsdWRlPVQsIGV2YWw9VH0KYSA8LSA0ICogMjAKYiA8LSA3CmEgKyBiCmBgYApcbm9ybWFsc2l6ZQpSZS13cml0ZSB0aGlzIGNhbGN1YXRpb24gb3ZlciBtdWx0aXBsZSBsaW5lcywgdXNpbmcgYSB2YXJpYWJsZToKYGBge3IgZGVmaW5lX3ZhcmlhYmxlMiwgZWNobz1ULCBpbmNsdWRlPVR9CjIgKyAyICsgMgphIDwtIDIKYiA8LSAzCmEgKiBiCmBgYAojIERhdGEgc3RydWN0dXJlcwoKIyMgRGF0YSB0eXBlcwoKLSBOdW1lcmljL0RvdWJsZSAoZS5nLiAyLjUsIDEvNSwgMS4wLCBcZG90cykKCi0gSW50ZWdlciAoZS5nLiAxLCAyLCAzLCBcZG90cykKCi0gQ29tcGxleCAoZS5nLiAxICsgMmksIFxkb3RzKQoKLSBMb2dpY2FsIChlLmcuIFRSVUUsIEZBTFNFIG9yIE5BKQoKLSBDaGFyYWN0ZXIgKGUuZy4gImEiLCAicGFwZXIiLCAiMiBwbHVzIDIgPSA1IiwgIlRSVUUiLCBcZG90cykKCi0gRmFjdG9yL0NhdGVnb3JpY2FsICgibWFsZSIsICJmZW1hbGUiLCBcZG90cykKCiMjIERhdGEgc3RydWN0dXJlcwohW1Zpc3VhbGl6YXRpb24gb2YgZGF0YSBzdHJ1Y3R1cmVzXShmaWd1cmVzL3JfZGF0YV9zdHJ1Y3R1cmVzLnBuZykKCiMjIFZlY3RvcnMgKEkpCgpZb3UgY2FuIGNyZWF0ZSBhIHZlY3RvciB1c2luZyB0aGUgY29tbWFuZCBgYygpYApcZm9vdG5vdGVzaXplCmBgYHtyIHZlY3RvcnNfYmFzZSwgaW5jbHVkZT1ULCBlY2hvPVR9CnggPC0gYygxLCAzLCA1LCAxMCkKeApgYGAKXG5vcm1hbHNpemUKClZlY3RvcnMgbXVzdCBjb250YWluIGVsZW1lbnRzIG9mIHRoZSBzYW1lIGRhdGEgdHlwZS4KXGZvb3Rub3Rlc2l6ZQpgYGB7ciB2ZWN0b3JzX3R5cGUsIGluY2x1ZGU9VCwgZWNobz1UfQpjKDEsICJpbnRybyIsIFRSVUUpCmBgYApcbm9ybWFsc2l6ZQoKWW91IGNhbiBtZWFzdXJlIHRoZSBsZW5ndGggb2YgYSB2ZWN0b3IgdXNpbmcgdGhlIGNvbW1hbmQgYGxlbmd0aCgpYApcZm9vdG5vdGVzaXplCmBgYHtyIHZlY3RvcnNfbGVuZ3RoLCBpbmNsdWRlPVQsIGVjaG89VH0KbGVuZ3RoKHgpCmBgYApcbm9ybWFsc2l6ZQoKIyMgVmVjdG9ycyAoSUkpCkl0IGlzIGFsc28gcG9zc2libGUgdG8gZWFzaWx5IGNyZWF0ZSBzZXF1ZW5jZXMKXGZvb3Rub3Rlc2l6ZQpgYGB7ciB2ZWN0b3JzX3NlcXVlbmNlcywgaW5jbHVkZT1ULCBlY2hvPVR9CjE6MTAKc2VxKGZyb20gPSAxLCB0byA9IDIsIGJ5ID0gMC4xKQpyZXAoIkEiLCB0aW1lcyA9IDUpCmBgYApcbm9ybWFsc2l6ZQoKIyMgVmVjdG9ycyAoSUlJKQpZb3UgY2FuIGNvbWJpbmUgZGlmZmVyZW50IHZlY3RvcnMKXGZvb3Rub3Rlc2l6ZQpgYGB7ciB2ZWN0b3JzX211bHRpcGxlLCBpbmNsdWRlPVQsIGVjaG89VH0KeCA8LSAxOjMgIyBmcm9tIDEgdG8gMwp5IDwtIGMoMTAsIDE1KSAjIDEwIGFuZCAxNQp6IDwtIGMoeCx5KSAjIHggZmlyc3QgYW5kIHRoZW4geQp6CmBgYApcbm9ybWFsc2l6ZQoKQW5kIHlvdSBjYW4gcmVwZWF0IHZlY3RvcnMgKG9yIGl0cyBlbGVtZW50cykKXGZvb3Rub3Rlc2l6ZQpgYGB7ciB2ZWN0b3JzX3JlcGVhdCwgaW5jbHVkZT1ULCBlY2hvPVR9CnogPC0gcmVwKHksIGVhY2g9MykgIyByZXBlYXQgZWFjaCBlbGVtZW50IDMgdGltZXMKegp6IDwtIHJlcCh5LCB0aW1lcz0zKSAjIHJlcGVhdCB0aGUgd2hvbGUgdmVjdG9yIDMgdGltZXMKegpgYGAKXG5vcm1hbHNpemUKCiMjIFN1YnNldHRpbmcgVmVjdG9ycwpcZm9vdG5vdGVzaXplCmBgYHtyIHZlY3RvcnNfc3Vic2V0LCBpbmNsdWRlPVQsIGVjaG89VH0KeCA8LSBjKDEsIDUsIDEwLCA3KQp4IDwgNiAjIGlzIHRoZSBlbGVtZW50IGxvd2VyIHRoYW4gNj8KeCA9PSAxMCAjIGlzIHRoZSBlbGVtZW50IGVxdWFsIHRvIDEwPwp4WzJdICMgd2hpY2ggZWxlbWVudCBpcyBpbiB0aGUgc2Vjb25kIHBvc2l0aW9uPwp4WzE6Ml0gIyB3aGljaCBlbGVtZW50cyBhcmUgaW4gdGhlIGZpcnN0IDIgcG9zaXRpb25zPwp4W2MoMSwzLDQpXSAjIHdoaWNoIGVsZW1lbnRzIGFyZSBpbiBwb3NpdGlvbnMgMSwgMyBhbmQgND8KYGBgCiMjIFN1YnNldHRpbmcgVmVjdG9ycwpcZm9vdG5vdGVzaXplCmBgYHtyIHZlY3RvcnNfc3Vic2V0MiwgaW5jbHVkZT1ULCBlY2hvPVR9Cm4gPC0gYygxLCA0LCA1LCA2LCA3LCAyLCAzLCA0LCA1LCA2KSAjIGNyZWF0ZXMgYSB2ZWN0b3IsIHN0b3JlcyBpdCBpbiBuCmxlbmd0aChuKSAjIG51bWJlciBvZiBlbGVtZW50cwpuWzNdICMgZXh0cmFjdCAzIHJkIGVsZW1lbnQgaW4gbgpuWy0yXSAjIGV4dHJhY3QgYWxsIG9mIG4gYnV0IDJuZCBlbGVtZW50Cm5bYygxLDMsNCldICMgZXh0cmFjdCBmaXJzdCwgdGhpcmQsIGFuZCBmb3VydGggZWxlbWVudCBvZiBuCm5bbiA8IDRdICNleHRyYWN0IGFsbCBlbGVtZW50cyBpbiBuIHNtYWxsZXIgdGhhbiA0Cm5bbiA8IDQgJiBuICE9IDFdICMgZXh0cmFjdCBlbGVtZW50IHNtYWxsZXIgdGhhbiA0IEFORCBkaWZmZXJlbnQgZnJvbSAxCmBgYApcbm9ybWFsc2l6ZQoKIyMgRXhlcmNpc2UgYnJlYWsKXGZvb3Rub3Rlc2l6ZQpgYGB7ciB2ZWN0b3JzX3N1YnNldDQsIGluY2x1ZGU9VCwgZWNobz1UfQpuIDwtIGMoMSwgNCwgNSwgNiwgNywgMiwgMywgNCwgNSwgNikgIyBjcmVhdGVzIGEgdmVjdG9yLCBzdG9yZXMgaXQgaW4gbgpgYGAKXG5vcm1hbHNpemUKKiBudW1iZXIgb2YgZWxlbWVudHMKKiBleHRyYWN0IGZpcnN0IHRocmVlIGVsZW1lbnRzIG9mIG4KKiBleHRyYWN0IGFsbCBvZiBuIGJ1dCAzbmQgZWxlbWVudAoqIGV4dHJhY3QgZmlyc3QsIHRoaXJkLCBhbmQgZm91cnRoIGVsZW1lbnQgb2YgbgoqIGV4dHJhY3QgZWxlbWVudCBzbWFsbGVyIHRoYW4gNCBPUiBlcXVhbCB0byA1CgoKIyMgRXhlcmNpc2UgYnJlYWsKV2UgY2FuIGNyZWF0ZSBhbmQgaW5kZXggY2hhcmFjdGVyIHZlY3RvcnMgYXMgd2VsbC4gQSBjYWZlIGlzIHVzaW5nIFIgdG8gY3JlYXRlIHRoZWlyIG1lbnUuClxmb290bm90ZXNpemUKYGBge3IgdmVjdG9yc19zdWJzZXQ1LCBpbmNsdWRlPVQsIGVjaG89VH0KaXRlbXMgPC0gYygiYXBwbGVzIiwgIm9yYW5nZXMiLCAiZWdncyIsICJ0b21hdG9lcyIsICJiYW5hbmFzIikKYGBgClxub3JtYWxzaXplCiogV2hhdCBkb2VzIGl0ZW1zWy0zXSBwcm9kdWNlPwoqIEJhc2VkIG9uIHdoYXQgeW91IGZpbmQsIHVzZSBpbmRleGluZyB0byBjcmVhdGUgYSB2ZXJzaW9uIG9mIGl0ZW1zIHdpdGhvdXQgImJhbmFuYXMiLgoqIFVzZSBpbmRleGluZyB0byBjcmVhdGUgYSB2ZWN0b3IgY29udGFpbmluZyBhcHBsZXMsIGVnZ3MsIHRvbWF0b2VzLCBiYW5hbmFzLCBhbmQgYmFuYW5hcy4KKiBBZGQgYSBuZXcgaXRlbSwgImxlbW9ucyIsIHRvIGl0ZW1zLgoqIE1ha2UgaXRlbXNbM10gImJlcnJpZXMiLgoKCiMjIFZlY3RvcnMnIE9wZXJhdGlvbnMKXGZvb3Rub3Rlc2l6ZQpgYGB7ciB2ZWN0b3JzX29wZXJhdGlvbnMsIGluY2x1ZGU9VCwgZWNobz1UfQp4IDwtIGMoMSw1LDEwLDcpCngrMiAjIGFkZHMgYSBzY2FsYXIgdG8gYWxsIGVsZW1lbnRzCnheMiAjIHdoYXQncyB0aGUgc3F1YXJlIG9mIGFsbCBlbGVtZW50cz8KYGBgClxub3JtYWxzaXplCgojIyBNYXRyaWNlcyAoSSkKWW91IGNhbiBjcmVhdGUgYSBtYXRyaXggdXNpbmcgdGhlIGNvbW1hbmQgYG1hdHJpeCgpYApcZm9vdG5vdGVzaXplCmBgYHtyIG1hdHJpeCwgaW5jbHVkZT1ULCBlY2hvPVR9ClggPC0gbWF0cml4KDE6OSwgbnJvdyA9IDMsIG5jb2wgPSAzKQpYCmBgYApcbm9ybWFsc2l6ZQoKIyMgTWF0cmljZXMgKElJKQpSIGF1dG9tYXRpY2FsbHkgaW5zZXJ0cyBlbGVtZW50cyBieSBjb2x1bW5zLCBidXQgd2UgY2FuIGFzayB0byBpbmNsdWRlIGJ5IHJvd3MKXGZvb3Rub3Rlc2l6ZQpgYGB7ciBtYXRyaXhfYnlyb3csIGluY2x1ZGU9VCwgZWNobz1UfQpYIDwtIG1hdHJpeCgxOjksIG5yb3cgPSAzLCBuY29sID0gMywgYnlyb3cgPSBUUlVFKQpYCmBgYApcbm9ybWFsc2l6ZQoKWW91IGRvbid0IGV2ZW4gaGF2ZSB0byBzcGVjaWZ5IHRoZSBvcHRpb25zIG5hbWVzClxmb290bm90ZXNpemUKYGBge3IgbWF0cml4X3NpbGVudCwgaW5jbHVkZT1ULCBlY2hvPVR9ClggPC0gbWF0cml4KDE6OCwgMiwgNCwgVCkKWApgYGAKXG5vcm1hbHNpemUKCiMjIE1hdHJpY2VzIChJSUkpCk1hdHJpY2VzIGNhbiBhbHNvIGJlIGNyZWF0ZWQgYnkgY29tYmluaW5nIHZlY3RvcnMKXGZvb3Rub3Rlc2l6ZQpgYGB7ciBtYXRyaXhfZnJvbXZlY3RvcnMsIGluY2x1ZGU9VCwgZWNobz1UfQpYIDwtIGNiaW5kKDE6NCwgNjo5KSAjIGJpbmRzIHRoZW0gYXMgY29sdW1ucwpYClggPC0gcmJpbmQoMTo0LCA2OjkpICMgYmluZHMgdGhlbSBhcyByb3dzClgKYGBgClxub3JtYWxzaXplCgojIyBTdWJzZXR0aW5nIE1hdHJpY2VzClxmb290bm90ZXNpemUKYGBge3IgbWF0cml4X3N1YnNldCwgaW5jbHVkZT1ULCBlY2hvPVR9Clg+NSAjIGVsZW1lbnRzIGxhcmdlciB0aGFuIDUKWFsxLDRdICMgZWxlbWVudCBvZiBmaXJzdCByb3csIGZvdXJ0aCBjb2x1bW4/ClhbMSxdICMgZWxlbWVudCBpbiB0aGUgZmlyc3Qgcm93PwpYWywyXSAjIGVsZW1lbnRzIGluIHRoZSBzZWNvbmQgY29sdW1ucz8KYGBgClxub3JtYWxzaXplCgojIyBMaXN0cwpBIGxpc3QgaXMgYSBvbmUtZGltZW5zaW9uYWwgaGV0ZXJvZ2VuZW91cyBkYXRhIHN0cnVjdHVyZS4KCkl0IGlzIGluZGV4ZWQgbGlrZSBhIHZlY3RvciB3aXRoIGEgc2luZ2xlIGludGVnZXIgdmFsdWUgKG9yIGEgbmFtZSksIGJ1dCBlYWNoIGVsZW1lbnQgY2FuIGNvbnRhaW4gYW4gZWxlbWVudCBvZiBhbnkgZGF0YSB0eXBlLgpcZm9vdG5vdGVzaXplCmBgYHtyIGxpc3QsIGluY2x1ZGU9VCwgZWNobz1UfQp4IDwtIDE6NAp5IDwtIGMoImEiLCAiYiIsICJjIikKTCA8LSBsaXN0KG51bWJlcnMgPSB4LCBsZXR0ZXJzID0geSkKTApgYGAKXG5vcm1hbHNpemUKCiMjIFN1YnNldHRpbmcgTGlzdHMKXGZvb3Rub3Rlc2l6ZQpgYGB7ciBsaXN0X3N1YnNldHRpbmcsIGluY2x1ZGU9VCwgZWNobz1UfQpMW1sxXV0gIyBleHRyYWN0IHRoZSBmaXJzdCBlbGVtZW50CkwkbnVtYmVycyAjIGV4dHJhY3QgdGhlIGVsZW1lbnQgY2FsbGVkIG51bWJlcnMKTCRsZXR0ZXJzICMgZXh0cmFjdCB0aGUgZWxlbWVudCBjYWxsZWQgbGV0dGVycwpgYGAKXG5vcm1hbHNpemUKCllvdSBjYW4gZXZlbiAid29yayIgd2l0aCB0aGUgc3Vic2V0dGVkIGVsZW1lbnQ6Clxmb290bm90ZXNpemUKYGBge3IgbGlzdF9zdWJzZXR0aW5nMiwgaW5jbHVkZT1ULCBlY2hvPVR9CkwkbnVtYmVyc1sxOjNdID4gMgpgYGAKXG5vcm1hbHNpemUKCiMjIEV4ZXJjaXNlIGJyZWFrClxmb290bm90ZXNpemUKYGBge3IgbGV0dGVycywgaW5jbHVkZT1ULCBlY2hvPVQsIGV2YWw9Rn0KbGV0dGVycwpMRVRURVJTCm15X2xldHRlcnMgPC0gY2JpbmQoTEVUVEVSUywgbGV0dGVycykKY2xhc3MobXlfbGV0dGVycykKZGltKG15X2xldHRlcnMpCm15X2xldHRlcnMgPC0gY2JpbmQobXlfbGV0dGVycywgc2VxKDE6bGVuZ3RoKGxldHRlcnMpKSkKYGBgClxub3JtYWxzaXplCldoYXQgbnVtYmVyIGlzIHRoZSBsZXR0ZXIgRiBpbiB0aGUgRW5nbGlzaCBhbHBoYWJldD8KCgojIyBFeGVyY2lzZQpgYGB7ciBkbmFfZXgxLCBpbmNsdWRlID0gVCwgZWNobz1ULCBldmFsPUZ9CiMgdmVjdG9yIC4uLiBldGMuCm15X2RuYSA8LSAiQUFDR0FBVEdBR1RBQUFUR0FHVEFBQVRHQUFHR0FBVEdBVFRBVFRDQ1RUR0NUVFRBR0FBQ1RUQ1RHR0FBVFRBR0FHR0FDQQpBVEFUVEFBVEFBVEFDQ0FUQ0dDQUNBR1RHVFRUQ1RUVEdUVEdUVEFBVEdDVEFDQUFDQVRBQ0FBQUdBR0dBQUdDQVRHQ0FHIgpteV9kbmEKbGVuZ3RoKG15X2RuYSkKY2xhc3MobXlfZG5hKQpzdHIobXlfZG5hKQpuY2hhcihteV9kbmEpCgojIGFwcHIxCm15X2RuYV9jb21tYSA8LSBzYXBwbHkoc3Ryc3BsaXQoCiAgeCA9IG15X2RuYSwgc3BsaXQgPSAiIiwgZml4ZWQgPSBUUlVFKSwKICAgICAgIGZ1bmN0aW9uKHgpIHBhc3RlKHgsIGNvbGxhcHNlID0gIl8iKSkKbGVuZ3RoKG15X2RuYV9jb21tYSkKc3RyKG15X2RuYV9jb21tYSkKCm15X2RuYV9saXN0IDwtIHN0cnNwbGl0KHggPSBteV9kbmEsIHNwbGl0ID0gIiIsIGZpeGVkID0gVFJVRSkKbGVuZ3RoKG15X2RuYV9saXN0KQpjbGFzcyhteV9kbmFfbGlzdCkKbXlfZG5hX3ZlY3RvciA8LSB1bmxpc3QobXlfZG5hX2xpc3QpCmxlbmd0aChteV9kbmFfbGlzdFtbMV1dKQpzdHIobXlfZG5hX3ZlY3RvcikKbGVuZ3RoKG15X2RuYV92ZWN0b3IpCgojIGZpcnN0IG51Y2xlb3RpZGUKbXlfZG5hX3ZlY3RvclsxXQojIGluZGV4aW5nIDE6bmNoYXIobXlfZG5hKQpteV9kbmFfdmVjdG9yWzE6NTBdCiMgdW5pcXVlIGNoYXJhY3RlcnMKdW5pcXVlKG15X2RuYV92ZWN0b3IpCgojIG51bWJlciBvZiBBcwoobXlfZG5hX3ZlY3RvciA9PSAiQSIpCmxlbmd0aChteV9kbmFfdmVjdG9yW215X2RuYV92ZWN0b3IgPT0gIkEiXSkKCiMgcmVtb3ZlIFxuCnJlbW92ZV9udWMgPC0gbWF0Y2goIlxuIiwgbXlfZG5hX3ZlY3RvcikKd2hpY2gobXlfZG5hX3ZlY3RvciAlaW4lIGMoIlxuIiwgIlgiKSkKbXlfZG5hX3ZlY3RvcltyZW1vdmVfbnVjXSA8LSAiQSIKbXlfZG5hX3ZlY3Rvcl8yIDwtIG15X2RuYV92ZWN0b3JbLTcxXQp1bmlxdWUobXlfZG5hX3ZlY3Rvcl8yKQpgYGAKCiMjIERhdGEgRnJhbWVzIChJKQpBIGBkYXRhLmZyYW1lYCBpcyBzaW1pbGFyIHRvIGEgdHlwaWNhbCBgc3ByZWFkc2hlZXRgIGluIGV4Y2VsLgoKVGhlcmUgYXJlIHJvd3MsIGFuZCB0aGVyZSBhcmUgY29sdW1ucy4KCkEgcm93IGlzIHR5cGljYWxseSB0aG91Z2h0IG9mIGFzIGFuIFxlbXBoe29ic2VydmF0aW9ufS4KCkEgY29sdW1uIGlzIGEgY2VydGFpbiBcZW1waHt2YXJpYWJsZX0sIGNoYXJhY3RlcmlzdGljIG9yIGZlYXR1cmUgb2YgdGhhdCBvYnNlcnZhdGlvbi4KCiMjIERhdGEgRnJhbWVzIChJSSkKQSBkYXRhIGZyYW1lIGlzIGEgbGlzdCBvZiBjb2x1bW4gdmVjdG9ycyB3aGVyZToKCi0gZWFjaCBjb2x1bW4gaGFzIGEgbmFtZQoKLSBlYWNoIGNvbHVtbiBtdXN0IGNvbnRhaW4gdGhlIHNhbWUgZGF0YSB0eXBlLCBidXQgdGhlIGRpZmZlcmVudCBjb2x1bW5zIGNhbiBzdG9yZSBkaWZmZXJlbnQgZGF0YSB0eXBlcy4KCi0gZWFjaCBjb2x1bW4gbXVzdCBiZSBvZiBzYW1lIGxlbmd0aAoKIyMgRGF0YSBGcmFtZXMgKElJSSkKXHNjcmlwdHNpemUKYGBge3IgZGF0YWZyYW1lLCBpbmNsdWRlPVQsIGVjaG89VH0Kc2V0LnNlZWQoMSkKZGYgPC0gIGRhdGEuZnJhbWUoaWQgPSAxOjUsCiAgbmFtZSA9IGMoIkRpZWdvIiwgIlNhbXVlbCIsICJNYXJjbyIsICJKYXZpZXIiLCAiTGVvbmFyZG8iKSwKICBzdXJuYW1lID0gYygiTWlsaXRvIiwgIkV0bydvIiwgIk1hdGVyYXp6aSIsICJaYW5ldHRpIiwgIkJvbnVjY2kiKSwKICB3YWdlID0gcm5vcm0obj01LCBtZWFuID0gMTBeNSwgc2QgPSAxMF4zKSwgIyBub3JtYWwgcmFuZG9tIHNhbXBsZQogIG9yaWdpbiA9IGMoIkFyZ2VudGluYSIsICJDYW1lcm9vbiIsICJJdGFseSIsICJBcmdlbnRpbmEiLCAiSXRhbHkiKSwKICB0cmVibGVfd2lubmVyID0gYyhULCBULCBULCBULCBGKQogICkKZGYKYGBgClxub3JtYWxzaXplCgpZb3UgY2FuIHZlcmlmeSB0aGUgc2l6ZSBvZiB0aGUgYGRhdGEuZnJhbWVgIHVzaW5nIHRoZSBjb21tYW5kIGBkaW0oKWAKCllvdSBjYW4gZ2V0IHRoZSBgZGF0YSB0eXBlYCBpbmZvIHVzaW5nIHRoZSBjb21tYW5kIGBzdHIoKWAKCiMjIFN1YnNldHRpbmcgRGF0YSBGcmFtZXMgKEkpClxmb290bm90ZXNpemUKYGBge3IgZGF0YWZyYW1lX3N1YnNldCwgaW5jbHVkZT1ULCBlY2hvPVR9CmRmJG5hbWUgIyBzdWJzZXQgYSBjb2x1bW4KZGZbLGMoMiw1KV0gIyBjYW4gYWxzbyBzdWJzZXQgbGlrZSBhIG1hdHJpeApgYGAKXG5vcm1hbHNpemUKCiMjIFN1YnNldHRpbmcgRGF0YSBGcmFtZXMgKElJKQpcZm9vdG5vdGVzaXplCmBgYHtyIGRhdGFmcmFtZV9zdWJzZXQyLCBpbmNsdWRlPVQsIGVjaG89VH0KaGVhZChkZiwgbj0zKSAjIGZpcnN0IG4gb2JzZXJ2YXRpb25zCnRhaWwoZGYsIG49MykgIyBsYXN0IG4gb2JzZXJ2YXRpb25zCmBgYApcbm9ybWFsc2l6ZQoKIyMgSW5zcGVjdGluZyBkYXRhIGZyYW1lcyAoSSkKUiBjb21lcyB3aXRoIG1hbnkgZGF0YSBiYXNlcyBpbmNsdWRlZC4gVGhlc2UgY2FuIGJlIHVzZWQgZm9yIGxlYXJuaW5nIFIuCgpPbmUgb2YgdGhlIG1vc3QgZmFtb3VzIGlzIHRoZSBvbmUgY2FsbGVkIGBtdGNhcnNgLgpcc2NyaXB0c2l6ZQpgYGB7ciBkYXRhZnJhbWVfbXRjYXJzLCBpbmNsdWRlPVQsIGVjaG89VH0KaGVhZChtdGNhcnMpCnRhaWwobXRjYXJzKQpkaW0obXRjYXJzKQpgYGAKXG5vcm1hbHNpemUKCiMjIEluc3BlY3RpbmcgZGF0YSBmcmFtZXMgKElJKQpcc2NyaXB0c2l6ZQpgYGB7ciBkYXRhZnJhbWVfbXRjYXJzMiwgaW5jbHVkZT1ULCBlY2hvPVR9CnN0cihtdGNhcnMpCm5hbWVzKG10Y2FycykKYGBgClxub3JtYWxzaXplCgojIyBTdWJzZXR0aW5nIGRhdGEgZnJhbWVzIChJSUkpCldlIGFyZSBpbnRlcmVzdGluZyBpbiB0aGUgY3lsaW5kZXJzIGFuZCB0aGUgd2VpZ2h0cyBvZiBpbmVmZmljaWVudCBjYXJzIChsb3dlciB0aGFuIDE1IG1pbGVzIHBlciBnYWxsb24pLgpcc2NyaXB0c2l6ZQpgYGB7ciBkYXRhZnJhbWVfc3Vic2V0X210Y2FycywgaW5jbHVkZT1ULCBlY2hvPVR9CnBvbGxfY2FycyA8LSBtdGNhcnNbbXRjYXJzJG1wZzwxNSwgYygiY3lsIiwgInd0IildCnBvbGxfY2FycwpgYGAKXG5vcm1hbHNpemUKCiMjIFN1YnNldHRpbmcgZGF0YSBmcmFtZXMgKElWKQpBbHRlcm5hdGl2ZWx5Ogpcc2NyaXB0c2l6ZQpgYGB7ciBkYXRhZnJhbWVfc3Vic2V0X210Y2FyczIsIGluY2x1ZGU9VCwgZWNobz1UfQpwb2xsX2NhcnMgPC0gc3Vic2V0KG10Y2Fycywgc3Vic2V0ID0gbXBnPDE1LCBzZWxlY3QgPSBjKCJjeWwiLCAid3QiKSkKcG9sbF9jYXJzCmBgYApcbm9ybWFsc2l6ZQoKIyMgSW1wb3J0aW5nIGRvd25sb2FkZWQgZGF0YSBmcmFtZXMgKC5jc3YpCllvdSBjYW4gaW1wb3J0IGNzdiBkYXRhIHRoYXQgeW91IGhhdmUgZG93bmxvYWRlZCBmcm9tIGFueSBleHRlcm5hbCBzb3VyY2UgdXNpbmc6ClxzY3JpcHRzaXplCmBgYHtyIGltcG9ydF9kYXRhMiwgaW5jbHVkZT1ULCBlY2hvPVQsIGV2YWw9Rn0Kc2V0d2QoImRhdGEiKQpueWNfYWIgPC0gcmVhZC5jc3YoIkFCX05ZQ18yMDIwLmNzdiIpCmBgYApcbm9ybWFsc2l6ZQoKd2hlcmU6CgotIGBzZXR3ZCgpYCBzZXRzIHRoZSB3b3JraW5nIGRpcmVjdG9yeSB0byB0aGUgcGxhY2Ugd2hlcmUgdGhlIGRhdGEgaXMgc2F2ZWQ7CgotIGByZWFkLmNzdigpYCBsb2FkcyB0aGUgY3N2IGZpbGUgd2l0aCB0aGUgc3BlY2lmaWVkIG5hbWUuCgpZb3UgY2FuIHNpbWlsYXJseSBpbXBvcnQgYWxtb3N0IGFueSBraW5kIG9mIGRhdGEgZmlsZSBzdG9yZWQgaW4gb3RoZXIgZm9ybWF0cyAoLnhscywgLnR4dCwgLmNzdiwgLmR0YSwgLlJkYXRhLCAubWF0LCAuLi4pCgojIyBJbXBvcnRpbmcgZG93bmxvYWRlZCBkYXRhIGZyYW1lcyAoLnR4dCkKSW50ZXJmZXJvbiByZWd1bGF0b3J5IGZhY3RvciA2IG1vdXNlClxzY3JpcHRzaXplCmBgYHtyIGltcG9ydF9kYXRhMywgaW5jbHVkZT1ULCBlY2hvPVQsIGV2YWw9Rn0Kc2V0d2QoImRhdGEiKQppcmY2IDwtIHJlYWQudGFibGUoImlyZjYudHh0IiwgaGVhZGVyID0gVFJVRSwgcm93Lm5hbWVzID0gMSkKCiMgZXhwbG9yZQpoZWFkKGlyZjYpCm5jb2woaXJmNik7IG5yb3coaXJmNikKZGltKGlyZjYpCmBgYAojIyBJbXBvcnRpbmcgZG93bmxvYWRlZCBkYXRhIGZyYW1lcyAoLnR4dCkKXHNjcmlwdHNpemUKYGBge3IgaW1wb3J0X2RhdGE0LCBpbmNsdWRlPVQsIGVjaG89VCwgZXZhbD1GfQppcmY2IDwtIHJlYWQudGFibGUoImRhdGEvaXJmNi50eHQiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKQpjbGFzcyhpcmY2KQpzdHIoaXJmNikKY29sbmFtZXMoaXJmNikKaGVhZChyb3duYW1lcyhpcmY2KSkKCmBgYAojIyBJbXBvcnRpbmcgZG93bmxvYWRlZCBkYXRhIGZyYW1lcyAoLnR4dCkKXHNjcmlwdHNpemUKYGBge3IgaW1wb3J0X2RhdGE1LCBpbmNsdWRlPVQsIGVjaG89VCwgZXZhbD1GfQppcmY2IDwtIHJlYWQudGFibGUoImRhdGEvaXJmNi50eHQiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKQoKaGVhZChpcmY2WydFMTcuNUtPMSddKSAjIFJldHJpZXZlIG9ubHkgRTE3LjVLTzEgZGF0YQpoZWFkKGlyZjZbLCAxXSkgIyBhcyB3ZWxsCmhlYWQoaXJmNiRFMTcuNUtPMSkgIyBhcyB3ZWxsCmRpbShpcmY2WywgLTFdKTsgY29sbmFtZXMoaXJmNlssIC0xXSkgICMgRXhjbHVkZSBFMTcuNUtPMSBkYXRhCmBgYAoKIyBFeGVyY2lzZSBicmVhawoKIyMgVG90YWwgbHVuZyBjYXBhY2l0eQpcZm9vdG5vdGVzaXplCmBgYHtyIHRsYywgaW5jbHVkZT1ULCBlY2hvPVQsIGV2YWw9Rn0KICB0bGMgPC0gcmVhZC5jc3YoImRhdGEvdGxjLmNzdiIpCiAgc3RyKHRsYykKICBzdW1tYXJ5KHRsYykKICBzdWJzZXQodGxjLCBzZXggPT0gMSkKICBzdWJzZXQodGxjLCBhZ2UgPCAyMCwgdGxjKQogIG1lYW4oc3Vic2V0KHRsYywgc2V4ID09IDEsIHNlbGVjdCA9IHRsYywgZHJvcCA9IFQpKQpgYGAKCiMgQXBwbHkKIyMgVmVjdG9yaXphdGlvbgoqIFJldHVybnMJYQl2ZWN0b3Igb3IgYXJyYXkgb3IgbGlzdAoqIG9idGFpbmVkIGJ5IGFwcGx5aW5nIGEgZnVuY3Rpb24gdG8gbWFyZ2lucyBvZiBhIG1hdHJpeAoqIGFwcGx5KFgsIE1BUkdJTiwgRlVOLCAuLi4pCiogWCA6CWRhdGEKKiBNQVJHSU4gOgkxIGZvciByb3dzLCAyIGZvciBjb2x1bW5zCiogRlVOOiBmdW5jdGlvbgogIFxzY3JpcHRzaXplCiAgYGBge3IgYXBwbHksIGluY2x1ZGU9VCwgZWNobz1UfQp4IDwtIGNiaW5kKHgxID0gMywgeDIgPSBjKDQ6MSwgMjo1KSkKY29sLnN1bXMgPC0gYXBwbHkoeCwgMiwgc3VtKQpyb3cuc3VtcyA8LSBhcHBseSh4LCAxLCBzdW0pCmBgYAoKYGBge3Iga25pdF9leGl0LCBpbmNsdWRlPUYsIGVjaG89Rn0Ka25pdF9leGl0KCkKYGBgCg==