Basic principle: STOP whenever you have an error message: it’s useless to continue!
A large majority of commands use an arrow “<-”. The following line

a <- b

means that the software will put value b inside variable a.

1 - BASICS

Packages

You need to install a package only once, but you need to activate it each time you start a new R session. The hashtag is use to append comments to the code.

if (!require("tidyverse")) install.packages('tidyverse') # This line to install, if it has not already been done.
library(tidyverse)                                       # This line to activate. Note: quotes are unnecessary here.

Working directory

R works in one particular folder. You can fix it in the Files pane in RStudio. Or you can use the setwd() function. To see what is the current working directory, type getwd().

Variables vs functions

Two major items in R: the functions that you are going to use (like in Excel: sum(), min(), etc.) and the variables that you will manipulate. There is a MAJOR difference between the two! In terms of code, there is only one small (but important!) difference: functions work with round brackets () and data variables work with square brackets [].
For a function, for instance the square root function sqrt(), there is always an argument inside the round brackets: it is the element on which the function will work. sqrt(5) will produce the square root of five. For a variable, the numbers inside the square brackets will relate to indexing (more on that below).

Importing data

This is usually done directly in the user interface, or with packages like openxlsx or readxl (to import Excel files) with the function read.xlsx() or read_excel(). The basic case:test_data <- read.xlsx(“MyFile.xlsx”) or test_data <- read_excel(“MyFile.xlsx”).
This stores your data into the test_data variable. This assumes that the Excel file “MyFile.xlsx” exists in your working directory.

2 - CREATING DATA

Simple sequences

You can create data from scratch, using the colon operator for instance.

1:10
 [1]  1  2  3  4  5  6  7  8  9 10
3:17
 [1]  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17

More generally, the c() function concatenates and encapsulates numbers (or text):

c(2,5,7)
[1] 2 5 7
c(1:6,12:20)
 [1]  1  2  3  4  5  6 12 13 14 15 16 17 18 19 20
c("R", " is ", "awesome")
[1] "R"       " is "    "awesome"

Another way to replicate data is to use row-bind and column-bind functions rbind() and cbind().

rbind(c(2,5,7),c(3,1,8)) 
     [,1] [,2] [,3]
[1,]    2    5    7
[2,]    3    1    8
cbind(c(2,5,7),c(3,1,8)) 
     [,1] [,2]
[1,]    2    3
[2,]    5    1
[3,]    7    8

You can also fill in matrices:

m <- matrix(1:20, nrow = 4) 
m2 <- matrix(1:20, nrow = 4, byrow = T) # Two ways to fill: by row or by column
m
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    5    9   13   17
[2,]    2    6   10   14   18
[3,]    3    7   11   15   19
[4,]    4    8   12   16   20
m2
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    2    3    4    5
[2,]    6    7    8    9   10
[3,]   11   12   13   14   15
[4,]   16   17   18   19   20

R is great to generate random data.

runif(10) # uniform distribution: 10 samples
 [1] 0.06688124 0.82541667 0.28137797 0.11063758 0.53751033 0.38189383 0.08916188 0.86453049 0.61392825 0.68673409
rnorm(20) # Gaussian distribution (parameters could be specified, see online manual): 20 data points
 [1]  1.58870173  0.86578773  0.13029713 -0.97993171 -1.64267374 -0.40144518  0.43771547 -0.16043997 -1.27166377  1.09629147
[11]  0.51176613  1.41954010 -0.06755030 -0.14813283  0.40070418  0.36982084  0.70984827  0.49217370  0.69688766  0.02829686

Dataframes

Datasets often mix text and numbers. R can do that too, with data frames. Let’s create one with the data.frame() function. We use the round() function which rounds up numbers.

nb_gender <- 7                                              # Number of people of each gender
Gender <- rep(c("Male"),nb_gender)                          # nb_gender men in total
Weight <- rnorm(nb_gender, mean = 70, sd = 8) %>% round()   # in kilos
Height <- rnorm(nb_gender, mean = 178, sd = 10) %>% round() # in cm
Age <- rnorm(nb_gender, mean = 40, sd = 7)  %>% round()  
data <- data.frame(Gender,Weight,Height,Age)                # data with only men
Gender <- rep(c("Female"),nb_gender)                        # nb_gender women in total
Weight <-  rnorm(nb_gender, 60, sd = 8)  %>% round()        # in kilos
Height <-  rnorm(nb_gender, 167, sd = 10)  %>% round()      # in cm
Age <- rnorm(nb_gender, mean = 40, sd = 7)  %>% round()  
data <- rbind(data, data.frame(Gender,Weight,Height,Age))   # grouping women with men
data

You can use rownames() or colnames() to get or set the names of rows or columns: colnames(data).

Dimensions

You can obtain the dimension of a matrix or data frame with the dim() function: dim(data). (Nb rows and nb columns). Each dimension can be obtained separately with nrow() and ncol() For vectors, the number of elements can be found with the length() function.

dim(data)  # Be careful with this one
[1] 14  4
nrow(data) # Number of rows
[1] 14
ncol(data) # Number of columns
[1] 4
length(3:35) # Number of elements (best used for a vector)
[1] 33

Boolean (TRUE/FALSE) data

In R, it is usefulto perform tests. For instance, given the sequence 1:12, we want to know which values are strictly greater than 6. The simple command 1:12>6 will provide the answer: the statement is false for the first six elements (1 to 6) and true for the last six (7 to 12).

1:12>6
 [1] FALSE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE

3 - HANDLING DATA IN PURE R

Extracting data

Accessing the values of a variable can be done with the square brackets [] thanks to indexing. For instance, the value in the third row and second column of data is data[3,2].
When columns have names, it is possible to use it to isolate a particular column with the dollar $ operator:

data$Age
 [1] 42 29 34 37 45 38 45 40 54 46 36 35 44 28

Another way to proceed is to omit to specify the row numbers: since Height is the third column of data, then the result is the same with data[,3]. This give you all of the third column. Likewise, data[3,] will return all of the third row.

data[,3] # Third column
 [1] 165 184 188 177 158 180 174 168 154 169 151 171 173 163
data[3,] # Third row

You can extract data with boolean vectors! For instance, if we want to select the people who are older than 42 years old: simple!

data$Age>42
 [1] FALSE FALSE FALSE FALSE  TRUE FALSE  TRUE FALSE  TRUE  TRUE FALSE FALSE  TRUE FALSE

will provide the corresponding indices. To extract the data, you just need to select the right rows and all columns:

data[data$Age>42,]

Only the TRUE rows are kept. As we will see, the filter() function of the tidyverse does just that.

Writing / Replacing values

Writing on data frames, vectors, or matrices can be done with the arrow operator:

data[3,2] <- 99
data[c(7,9),3] <- 166        # Replace 2 cells at a time! Seventh and ninth row on the third column.
data[c(6,8),3] <- c(199,177) # Same, but with 2 different values. 
data                         # CHECK where the new values are!

Seeing data

Unlike in Excel, the data is not directly shown in R. You have to ask for it! To see the content of a variable, you have to type its name and press ENTER.
The head() function shows the first 6 lines and the tail() function shows the last 6 lines.

head(data, 8) # First n lines, with n = 6 by default

The summary() function very often gives useful (statistical) information

summary(data) # Descriptive statistics
    Gender      Weight          Height           Age       
 Male  :7   Min.   :48.00   Min.   :151.0   Min.   :28.00  
 Female:7   1st Qu.:59.25   1st Qu.:165.2   1st Qu.:35.25  
            Median :65.50   Median :170.0   Median :39.00  
            Mean   :68.07   Mean   :171.9   Mean   :39.50  
            3rd Qu.:71.75   3rd Qu.:177.0   3rd Qu.:44.75  
            Max.   :99.00   Max.   :199.0   Max.   :54.00  

Date management

The best package for date management is lubridate. Dates can be converted using the as.Date(), and years, months and days can be retrieved using the year(), month() and day() functions.

if(!require(lubridate)){install.packages("lubridate")}
library(lubridate)
d <- as.Date("2000-04-08")
year(d)  # Gives the year
[1] 2000
month(d) # Gives the month
[1] 4
day(d)   # Gives the day
[1] 8
make_date(year = 2017, month = 6, day = 12) # Creates a date with specified YMD
[1] "2017-06-12"

4 - HANDLING DATA WITH THE TIDYVERSE

Manipulation

Filtering items is incredibly easy via filter(). The %in% operator can be useful when testing for several values.

filter(data, Age > 42)                        # All people older than 42
filter(data, Gender == "Male", Weight > 70)   # All guys heavier than 70 
filter(diamonds, cut %in% c("Fair", "Good"))  # Diamonds with Fair or Good cut
filter(diamonds, color %in% c("E", "F"))      # Diamonds with E or F color

Ordering according to a particular variable is performed with top_n(). In the same vein, arrange() orders the whole dataset according to particular numerical variables

top_n(data, 3, Height)   # Tallest 3 individuals
top_n(data, -4, Weight)  # Lightest 4 individuals
arrange(data, Gender, Height)       # First selects individuals by gender, and then ranks them by height
arrange(data, Gender, desc(Weight)) # Same, but with descending weight.

Selecting a few columns with select().

select(data, Gender, Age) # Keeping only these 3 columns.

Adding new columns can be performed with the mutate() function. Below, we compute the price/carat ratio of diamonds. The mutate function() is THE best choice to add columns that are easily calculated.

diamonds %>% 
    select(-x, -y, -z, - depth, -table) %>% # Getting rid of less useful variables
    mutate(P_C_ratio = price/carat)         # Create a new column: the price/carat ratio

Piping

Very often, several operations are required before the desired output is obtained. There is an elegant way to successively combine functions. It is called piping and works with the operator %>%, which we used just above.

data %>% select(Gender, Height, Age) %>% filter(Age > 40) # The two functions select() and filter() are applied successively

Pivot tables

Pivot tables (PT) are very simply obtained via the combination of group_by() summarise(). group_by() determines along which variables the PT will be computed and summarise() specifies the metric/indicator of interest.

diamonds %>% 
    group_by(cut, color, clarity) %>% # Grouping by cut, then color and the clarity
    summarise(med_price = median(price), med_carat = median(price)) # For each subgroup, computing 2 indicators: median price and median carat

Graphs

In R, there is one major function for graphical representation: ggplot(). In ggplot, aes() describe the aesthetics of the plot. Usually, we need to define the x-axis variable and often, the y-axis variable (for scatter plots and lines). Also, representations can allow for color, shape and size variations, especially for scatter plots. The type of the plot is defined by a ‘geom’. More details can be found here: https://ggplot2.tidyverse.org/articles/ggplot2-specs.html

ggplot(data) + geom_point(aes(x = Height, y = Weight, color = Age, shape = Gender, size = Age)) # geom_point = scatter plot

ggplot(diamonds) + geom_bar(aes(x = clarity, fill = cut)) # geom_bar = barplot

diamonds %>% 
    group_by(clarity) %>% 
    summarise(med_carat = median(price)) %>%
    ggplot(aes(x = clarity, y = med_carat)) + geom_bar(stat = "identity")

 # When providing a "y" for a barplot, you must specify the stat="identity". This graph shows that large diamonds are much less "pure" than smaller ones. Which makes sense.

5 - LOOPS

There are usually several types of loops, but we will focus on the for loop. Its structure is simple: the idea is to repeat a task a finite number of times. This allows to automate the changes in a variable. For instance, the Fibonacci sequence:

nb <- 20  # Number of desired numbers
Fib <- 1  # Initiate the output (it will be incrementally augmented): first value
Fib[2] <- 1 # Initialisation: second value
for(k in 3:nb){
   Fib[k] <- Fib[k-1] + Fib[k-2] # the kth value is the sum of the 2 previous ones
}
Fib # Show the sequence
 [1]    1    1    2    3    5    8   13   21   34   55   89  144  233  377  610  987 1597 2584 4181 6765

6 - MISC. FUNCTIONS

Statistics

Below, we present a few useful functions.

rbind(c(2,5,7),c(3,1,8)) 
     [,1] [,2] [,3]
[1,]    2    5    7
[2,]    3    1    8
rbind(c(2,5,7),c(3,1,8)) %>% t()    # Transpose (a vector, a matrix, a dataframe)
     [,1] [,2]
[1,]    2    3
[2,]    5    1
[3,]    7    8
sqrt(5)                             # Square root
[1] 2.236068
mean(c(2:6, 8:43))                  # Average value
[1] 22.87805
sd(c(2:6, 8:43))                    # Standard deviation, use var() for the variance
[1] 12.17004
v <- rnorm(18)                      # We generate a random vector
min(v)                              # Minimum
[1] -1.223596
max(v)                              # Maximum
[1] 1.775057
v                                   # A look at the whole vector
 [1]  0.62139521  0.75263857  0.54648395 -1.22359589  0.10757450  0.42116621  1.77505745  1.39276542  0.48365785 -0.04642858
[11]  0.78928712 -0.39740584 -0.34888729 -1.13734942  0.09880032 -0.07831536  0.67318915 -0.70351280
lag(1:10)                           # The lag() function: shifts data to the right
 [1] NA  1  2  3  4  5  6  7  8  9
mean(c(NA,1:10), na.rm = T)         # When computing means, if values are missing, NA will be returned. na.rm = T solves this.
[1] 5.5

Changing modes

Usual modes for variables are:
- logical (Boolean, TRUE or FALE),
- numeric (numbers),
- character (text),
- factor (unordered category) and
- ordered factor (ordered category)
It is sometimes possible to switch from one to another. One counter-example is: translating a charater into a number.
Some examples below.

c("3", "8", "7")                    # Numbers viewed as text 
[1] "3" "8" "7"
c("3", "8", "7") %>% as.numeric()   # Change the above into *true* numbers
[1] 3 8 7
c(3,4,6) %>% as.character()         # The opposite: change fields into characters
[1] "3" "4" "6"
data$Age %>% as.factor() %>% summary() # as.factor() transforms the fields into catagories, the final step computes the number of elements in each catergory
28 29 34 35 36 37 38 40 42 44 45 46 54 
 1  1  1  1  1  1  1  1  1  1  2  1  1 
LS0tCnRpdGxlOiAiUiBNZW1vIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgI3RvYzogdHJ1ZQogICN0b2NfZmxvYXQ6IHRydWUKICAgICNnaXRodWJfZG9jdW1lbnQ6IGRlZmF1bHQKLS0tCgpCYXNpYyBwcmluY2lwbGU6IFNUT1Agd2hlbmV2ZXIgeW91IGhhdmUgYW4gKiplcnJvciBtZXNzYWdlKio6IGl0J3MgdXNlbGVzcyB0byBjb250aW51ZSEgIApBIGxhcmdlIG1ham9yaXR5IG9mIGNvbW1hbmRzIHVzZSBhbiBhcnJvdyAiPC0iLiBUaGUgZm9sbG93aW5nIGxpbmUgIAogIAphIDwtIGIgIAogIAptZWFucyB0aGF0IHRoZSBzb2Z0d2FyZSB3aWxsIHB1dCB2YWx1ZSBiIGluc2lkZSB2YXJpYWJsZSBhLiAgIAoKIyMgMSAtIEJBU0lDUwojIyMgUGFja2FnZXMKWW91IG5lZWQgdG8gaW5zdGFsbCBhIHBhY2thZ2Ugb25seSBvbmNlLCBidXQgeW91IG5lZWQgdG8gYWN0aXZhdGUgaXQgZWFjaCB0aW1lIHlvdSBzdGFydCBhIG5ldyBSIHNlc3Npb24uIFRoZSBoYXNodGFnIGlzIHVzZSB0byBhcHBlbmQgY29tbWVudHMgdG8gdGhlIGNvZGUuCmBgYHtyIFBhY2thZ2VzLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KaWYgKCFyZXF1aXJlKCJ0aWR5dmVyc2UiKSkgaW5zdGFsbC5wYWNrYWdlcygndGlkeXZlcnNlJykgIyBUaGlzIGxpbmUgdG8gaW5zdGFsbCwgaWYgaXQgaGFzIG5vdCBhbHJlYWR5IGJlZW4gZG9uZS4KbGlicmFyeSh0aWR5dmVyc2UpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUaGlzIGxpbmUgdG8gYWN0aXZhdGUuIE5vdGU6IHF1b3RlcyBhcmUgdW5uZWNlc3NhcnkgaGVyZS4KYGBgCgojIyMgV29ya2luZyBkaXJlY3RvcnkKUiB3b3JrcyBpbiBvbmUgcGFydGljdWxhciBmb2xkZXIuIFlvdSBjYW4gZml4IGl0IGluIHRoZSBGaWxlcyBwYW5lIGluIFJTdHVkaW8uIE9yIHlvdSBjYW4gdXNlIHRoZSBzZXR3ZCgpIGZ1bmN0aW9uLiBUbyBzZWUgd2hhdCBpcyB0aGUgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeSwgdHlwZSBnZXR3ZCgpLgoKIyMjIFZhcmlhYmxlcyB2cyBmdW5jdGlvbnMKVHdvIG1ham9yIGl0ZW1zIGluIFI6IHRoZSBmdW5jdGlvbnMgdGhhdCB5b3UgYXJlIGdvaW5nIHRvIHVzZSAobGlrZSBpbiBFeGNlbDogc3VtKCksIG1pbigpLCBldGMuKSBhbmQgdGhlIHZhcmlhYmxlcyB0aGF0IHlvdSB3aWxsIG1hbmlwdWxhdGUuIFRoZXJlIGlzIGEgKipNQUpPUioqIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgdHdvISBJbiB0ZXJtcyBvZiBjb2RlLCB0aGVyZSBpcyBvbmx5IG9uZSBzbWFsbCAoYnV0IGltcG9ydGFudCEpIGRpZmZlcmVuY2U6IGZ1bmN0aW9ucyB3b3JrIHdpdGggKnJvdW5kKiBicmFja2V0cyAoKSBhbmQgZGF0YSB2YXJpYWJsZXMgd29yayB3aXRoICpzcXVhcmUqIGJyYWNrZXRzIFtdLiAgCkZvciBhIGZ1bmN0aW9uLCBmb3IgaW5zdGFuY2UgdGhlIHNxdWFyZSByb290IGZ1bmN0aW9uIHNxcnQoKSwgdGhlcmUgaXMgYWx3YXlzIGFuIGFyZ3VtZW50IGluc2lkZSB0aGUgcm91bmQgYnJhY2tldHM6IGl0IGlzIHRoZSBlbGVtZW50IG9uIHdoaWNoIHRoZSBmdW5jdGlvbiB3aWxsIHdvcmsuIHNxcnQoNSkgd2lsbCBwcm9kdWNlIHRoZSBzcXVhcmUgcm9vdCBvZiBmaXZlLgpGb3IgYSB2YXJpYWJsZSwgdGhlIG51bWJlcnMgaW5zaWRlIHRoZSBzcXVhcmUgYnJhY2tldHMgd2lsbCByZWxhdGUgdG8gaW5kZXhpbmcgKG1vcmUgb24gdGhhdCBiZWxvdykuCgojIyMgSW1wb3J0aW5nIGRhdGEKVGhpcyBpcyB1c3VhbGx5IGRvbmUgZGlyZWN0bHkgaW4gdGhlIHVzZXIgaW50ZXJmYWNlLCBvciB3aXRoIHBhY2thZ2VzIGxpa2UgKm9wZW54bHN4KiBvciAqcmVhZHhsKiAodG8gaW1wb3J0IEV4Y2VsIGZpbGVzKSB3aXRoIHRoZSBmdW5jdGlvbiByZWFkLnhsc3goKSBvciByZWFkX2V4Y2VsKCkuIFRoZSBiYXNpYyBjYXNlOnRlc3RfZGF0YSA8LSByZWFkLnhsc3goIk15RmlsZS54bHN4Iikgb3IgdGVzdF9kYXRhIDwtIHJlYWRfZXhjZWwoIk15RmlsZS54bHN4IikuICAKVGhpcyBzdG9yZXMgeW91ciBkYXRhIGludG8gdGhlIHRlc3RfZGF0YSB2YXJpYWJsZS4gVGhpcyBhc3N1bWVzIHRoYXQgdGhlIEV4Y2VsIGZpbGUgIk15RmlsZS54bHN4IiBleGlzdHMgaW4geW91ciB3b3JraW5nIGRpcmVjdG9yeS4KCgojIyAyIC0gQ1JFQVRJTkcgREFUQQojIyMgU2ltcGxlIHNlcXVlbmNlcwpZb3UgY2FuIGNyZWF0ZSBkYXRhIGZyb20gc2NyYXRjaCwgdXNpbmcgdGhlIGNvbG9uIG9wZXJhdG9yIGZvciBpbnN0YW5jZS4gCmBgYHtyIENvbG9ufQoxOjEwCjM6MTcKYGBgCiAgCiAgCk1vcmUgZ2VuZXJhbGx5LCB0aGUgYygpIGZ1bmN0aW9uIGNvbmNhdGVuYXRlcyBhbmQgZW5jYXBzdWxhdGVzIG51bWJlcnMgKG9yIHRleHQpOgpgYGB7ciBjKCl9CmMoMiw1LDcpCmMoMTo2LDEyOjIwKQpjKCJSIiwgIiBpcyAiLCAiYXdlc29tZSIpCmBgYAogIAogIApBbm90aGVyIHdheSB0byByZXBsaWNhdGUgZGF0YSBpcyB0byB1c2Ugcm93LWJpbmQgYW5kIGNvbHVtbi1iaW5kIGZ1bmN0aW9ucyByYmluZCgpIGFuZCBjYmluZCgpLiAKYGBge3IgYmluZH0KcmJpbmQoYygyLDUsNyksYygzLDEsOCkpIApjYmluZChjKDIsNSw3KSxjKDMsMSw4KSkgCmBgYAogIApZb3UgY2FuIGFsc28gZmlsbCBpbiBtYXRyaWNlczogCmBgYHtyIG1hdHJpY2VzfQptIDwtIG1hdHJpeCgxOjIwLCBucm93ID0gNCkgCm0yIDwtIG1hdHJpeCgxOjIwLCBucm93ID0gNCwgYnlyb3cgPSBUKSAjIFR3byB3YXlzIHRvIGZpbGw6IGJ5IHJvdyBvciBieSBjb2x1bW4KbQptMgpgYGAKCiAgClIgaXMgZ3JlYXQgdG8gZ2VuZXJhdGUgcmFuZG9tIGRhdGEuCmBgYHtyfQpydW5pZigxMCkgIyB1bmlmb3JtIGRpc3RyaWJ1dGlvbjogMTAgc2FtcGxlcwpybm9ybSgyMCkgIyBHYXVzc2lhbiBkaXN0cmlidXRpb24gKHBhcmFtZXRlcnMgY291bGQgYmUgc3BlY2lmaWVkLCBzZWUgb25saW5lIG1hbnVhbCk6IDIwIGRhdGEgcG9pbnRzCmBgYAoKICAgIAojIyMgRGF0YWZyYW1lcwpEYXRhc2V0cyBvZnRlbiBtaXggdGV4dCBhbmQgbnVtYmVycy4gUiBjYW4gZG8gdGhhdCB0b28sIHdpdGggZGF0YSBmcmFtZXMuIExldCdzIGNyZWF0ZSBvbmUgd2l0aCB0aGUgZGF0YS5mcmFtZSgpIGZ1bmN0aW9uLiBXZSB1c2UgdGhlIHJvdW5kKCkgZnVuY3Rpb24gd2hpY2ggcm91bmRzIHVwIG51bWJlcnMuCmBgYHtyIGRmfQpuYl9nZW5kZXIgPC0gNyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIE51bWJlciBvZiBwZW9wbGUgb2YgZWFjaCBnZW5kZXIKR2VuZGVyIDwtIHJlcChjKCJNYWxlIiksbmJfZ2VuZGVyKSAgICAgICAgICAgICAgICAgICAgICAgICAgIyBuYl9nZW5kZXIgbWVuIGluIHRvdGFsCldlaWdodCA8LSBybm9ybShuYl9nZW5kZXIsIG1lYW4gPSA3MCwgc2QgPSA4KSAlPiUgcm91bmQoKSAgICMgaW4ga2lsb3MKSGVpZ2h0IDwtIHJub3JtKG5iX2dlbmRlciwgbWVhbiA9IDE3OCwgc2QgPSAxMCkgJT4lIHJvdW5kKCkgIyBpbiBjbQpBZ2UgPC0gcm5vcm0obmJfZ2VuZGVyLCBtZWFuID0gNDAsIHNkID0gNykgICU+JSByb3VuZCgpICAKZGF0YSA8LSBkYXRhLmZyYW1lKEdlbmRlcixXZWlnaHQsSGVpZ2h0LEFnZSkgICAgICAgICAgICAgICAgIyBkYXRhIHdpdGggb25seSBtZW4KR2VuZGVyIDwtIHJlcChjKCJGZW1hbGUiKSxuYl9nZW5kZXIpICAgICAgICAgICAgICAgICAgICAgICAgIyBuYl9nZW5kZXIgd29tZW4gaW4gdG90YWwKV2VpZ2h0IDwtICBybm9ybShuYl9nZW5kZXIsIDYwLCBzZCA9IDgpICAlPiUgcm91bmQoKSAgICAgICAgIyBpbiBraWxvcwpIZWlnaHQgPC0gIHJub3JtKG5iX2dlbmRlciwgMTY3LCBzZCA9IDEwKSAgJT4lIHJvdW5kKCkgICAgICAjIGluIGNtCkFnZSA8LSBybm9ybShuYl9nZW5kZXIsIG1lYW4gPSA0MCwgc2QgPSA3KSAgJT4lIHJvdW5kKCkgIApkYXRhIDwtIHJiaW5kKGRhdGEsIGRhdGEuZnJhbWUoR2VuZGVyLFdlaWdodCxIZWlnaHQsQWdlKSkgICAjIGdyb3VwaW5nIHdvbWVuIHdpdGggbWVuCmRhdGEKYGBgCllvdSBjYW4gdXNlIHJvd25hbWVzKCkgb3IgY29sbmFtZXMoKSB0byBnZXQgb3Igc2V0IHRoZSBuYW1lcyBvZiByb3dzIG9yIGNvbHVtbnM6IGNvbG5hbWVzKGRhdGEpLgoKIyMjIERpbWVuc2lvbnMKWW91IGNhbiBvYnRhaW4gdGhlIGRpbWVuc2lvbiBvZiBhIG1hdHJpeCBvciBkYXRhIGZyYW1lIHdpdGggdGhlIGRpbSgpIGZ1bmN0aW9uOiBkaW0oZGF0YSkuIChOYiByb3dzIGFuZCBuYiBjb2x1bW5zKS4gRWFjaCBkaW1lbnNpb24gY2FuIGJlIG9idGFpbmVkIHNlcGFyYXRlbHkgd2l0aCBucm93KCkgYW5kIG5jb2woKQpGb3IgdmVjdG9ycywgdGhlIG51bWJlciBvZiBlbGVtZW50cyBjYW4gYmUgZm91bmQgd2l0aCB0aGUgbGVuZ3RoKCkgZnVuY3Rpb24uCgpgYGB7ciBkaW19CmRpbShkYXRhKSAgIyBCZSBjYXJlZnVsIHdpdGggdGhpcyBvbmUKbnJvdyhkYXRhKSAjIE51bWJlciBvZiByb3dzCm5jb2woZGF0YSkgIyBOdW1iZXIgb2YgY29sdW1ucwpsZW5ndGgoMzozNSkgIyBOdW1iZXIgb2YgZWxlbWVudHMgKGJlc3QgdXNlZCBmb3IgYSB2ZWN0b3IpCmBgYAoKIyMjIEJvb2xlYW4gKFRSVUUvRkFMU0UpIGRhdGEKSW4gUiwgaXQgaXMgdXNlZnVsdG8gcGVyZm9ybSB0ZXN0cy4gRm9yIGluc3RhbmNlLCBnaXZlbiB0aGUgc2VxdWVuY2UgMToxMiwgd2Ugd2FudCB0byBrbm93IHdoaWNoIHZhbHVlcyBhcmUgc3RyaWN0bHkgZ3JlYXRlciB0aGFuIDYuIFRoZSBzaW1wbGUgY29tbWFuZCAxOjEyPjYgd2lsbCBwcm92aWRlIHRoZSBhbnN3ZXI6IHRoZSBzdGF0ZW1lbnQgaXMgZmFsc2UgZm9yIHRoZSBmaXJzdCBzaXggZWxlbWVudHMgKDEgdG8gNikgYW5kIHRydWUgZm9yIHRoZSBsYXN0IHNpeCAoNyB0byAxMikuCmBgYHtyIEJvb2x9CjE6MTI+NgpgYGAKCgoKIyMgMyAtIEhBTkRMSU5HIERBVEEgSU4gUFVSRSBSCiMjIyBFeHRyYWN0aW5nIGRhdGEKQWNjZXNzaW5nIHRoZSB2YWx1ZXMgb2YgYSB2YXJpYWJsZSBjYW4gYmUgZG9uZSB3aXRoIHRoZSBzcXVhcmUgYnJhY2tldHMgW10gdGhhbmtzIHRvIGluZGV4aW5nLiAKRm9yIGluc3RhbmNlLCB0aGUgdmFsdWUgaW4gdGhlIHRoaXJkIHJvdyBhbmQgc2Vjb25kIGNvbHVtbiBvZiBkYXRhIGlzIGRhdGFbMywyXS4gIApXaGVuIGNvbHVtbnMgaGF2ZSBuYW1lcywgaXQgaXMgcG9zc2libGUgdG8gdXNlIGl0IHRvIGlzb2xhdGUgYSBwYXJ0aWN1bGFyIGNvbHVtbiB3aXRoIHRoZSBkb2xsYXIgJCBvcGVyYXRvcjogCgpgYGB7ciBkb2xsYXJ9CmRhdGEkQWdlCmBgYApBbm90aGVyIHdheSB0byBwcm9jZWVkIGlzIHRvIG9taXQgdG8gc3BlY2lmeSB0aGUgcm93IG51bWJlcnM6IHNpbmNlIEhlaWdodCBpcyB0aGUgdGhpcmQgY29sdW1uIG9mIGRhdGEsIHRoZW4gdGhlIHJlc3VsdCBpcyB0aGUgc2FtZSB3aXRoIGRhdGFbLDNdLiBUaGlzIGdpdmUgeW91IGFsbCBvZiB0aGUgdGhpcmQgY29sdW1uLiBMaWtld2lzZSwgZGF0YVszLF0gd2lsbCByZXR1cm4gYWxsIG9mIHRoZSB0aGlyZCByb3cuCgpgYGB7ciBjb2x9CmRhdGFbLDNdICMgVGhpcmQgY29sdW1uCmRhdGFbMyxdICMgVGhpcmQgcm93CmBgYAoKWW91IGNhbiBleHRyYWN0IGRhdGEgd2l0aCBib29sZWFuIHZlY3RvcnMhIEZvciBpbnN0YW5jZSwgaWYgd2Ugd2FudCB0byBzZWxlY3QgdGhlIHBlb3BsZSB3aG8gYXJlIG9sZGVyIHRoYW4gNDIgeWVhcnMgb2xkOiBzaW1wbGUhIApgYGB7ciBCb29sMn0KZGF0YSRBZ2U+NDIKYGBgCgp3aWxsIHByb3ZpZGUgdGhlIGNvcnJlc3BvbmRpbmcgaW5kaWNlcy4gVG8gZXh0cmFjdCB0aGUgZGF0YSwgeW91IGp1c3QgbmVlZCB0byBzZWxlY3QgdGhlIHJpZ2h0IHJvd3MgYW5kIGFsbCBjb2x1bW5zOiAKYGBge3IgQm9vbDN9CmRhdGFbZGF0YSRBZ2U+NDIsXQpgYGAKT25seSB0aGUgVFJVRSByb3dzIGFyZSBrZXB0LiBBcyB3ZSB3aWxsIHNlZSwgdGhlIGZpbHRlcigpIGZ1bmN0aW9uIG9mIHRoZSB0aWR5dmVyc2UgZG9lcyBqdXN0IHRoYXQuCgoKIyMjIFdyaXRpbmcgLyBSZXBsYWNpbmcgdmFsdWVzIApXcml0aW5nIG9uIGRhdGEgZnJhbWVzLCB2ZWN0b3JzLCBvciBtYXRyaWNlcyBjYW4gYmUgZG9uZSB3aXRoIHRoZSBhcnJvdyBvcGVyYXRvcjoKYGBge3Igd3JpdGUgfQpkYXRhWzMsMl0gPC0gOTkKZGF0YVtjKDcsOSksM10gPC0gMTY2ICAgICAgICAjIFJlcGxhY2UgMiBjZWxscyBhdCBhIHRpbWUhIFNldmVudGggYW5kIG5pbnRoIHJvdyBvbiB0aGUgdGhpcmQgY29sdW1uLgpkYXRhW2MoNiw4KSwzXSA8LSBjKDE5OSwxNzcpICMgU2FtZSwgYnV0IHdpdGggMiBkaWZmZXJlbnQgdmFsdWVzLiAKZGF0YSAgICAgICAgICAgICAgICAgICAgICAgICAjIENIRUNLIHdoZXJlIHRoZSBuZXcgdmFsdWVzIGFyZSEKYGBgCgojIyMgU2VlaW5nIGRhdGEKVW5saWtlIGluIEV4Y2VsLCB0aGUgZGF0YSBpcyBub3QgZGlyZWN0bHkgc2hvd24gaW4gUi4gWW91IGhhdmUgdG8gYXNrIGZvciBpdCEgVG8gc2VlIHRoZSBjb250ZW50IG9mIGEgdmFyaWFibGUsIHlvdSBoYXZlIHRvIHR5cGUgaXRzIG5hbWUgYW5kIHByZXNzIEVOVEVSLiAgClRoZSBoZWFkKCkgZnVuY3Rpb24gc2hvd3MgdGhlIGZpcnN0IDYgbGluZXMgYW5kIHRoZSB0YWlsKCkgZnVuY3Rpb24gc2hvd3MgdGhlIGxhc3QgNiBsaW5lcy4gCmBgYHtyIHNlZX0KaGVhZChkYXRhLCA4KSAjIEZpcnN0IG4gbGluZXMsIHdpdGggbiA9IDYgYnkgZGVmYXVsdApgYGAKCgpUaGUgc3VtbWFyeSgpIGZ1bmN0aW9uIHZlcnkgb2Z0ZW4gZ2l2ZXMgdXNlZnVsIChzdGF0aXN0aWNhbCkgaW5mb3JtYXRpb24KYGBge3Igc3VtbWFyeX0Kc3VtbWFyeShkYXRhKSAjIERlc2NyaXB0aXZlIHN0YXRpc3RpY3MKYGBgCgojIyMgRGF0ZSBtYW5hZ2VtZW50ClRoZSBiZXN0IHBhY2thZ2UgZm9yIGRhdGUgbWFuYWdlbWVudCBpcyBsdWJyaWRhdGUuIERhdGVzIGNhbiBiZSBjb252ZXJ0ZWQgdXNpbmcgdGhlIGFzLkRhdGUoKSwgYW5kIHllYXJzLCBtb250aHMgYW5kIGRheXMgY2FuIGJlIHJldHJpZXZlZCB1c2luZyB0aGUgeWVhcigpLCBtb250aCgpIGFuZCBkYXkoKSBmdW5jdGlvbnMuCmBgYHtyIGRhdGV9CmlmKCFyZXF1aXJlKGx1YnJpZGF0ZSkpe2luc3RhbGwucGFja2FnZXMoImx1YnJpZGF0ZSIpfQpsaWJyYXJ5KGx1YnJpZGF0ZSkKZCA8LSBhcy5EYXRlKCIyMDAwLTA0LTA4IikKeWVhcihkKSAgIyBHaXZlcyB0aGUgeWVhcgptb250aChkKSAjIEdpdmVzIHRoZSBtb250aApkYXkoZCkgICAjIEdpdmVzIHRoZSBkYXkKbWFrZV9kYXRlKHllYXIgPSAyMDE3LCBtb250aCA9IDYsIGRheSA9IDEyKSAjIENyZWF0ZXMgYSBkYXRlIHdpdGggc3BlY2lmaWVkIFlNRApgYGAKCiMjIDQgLSBIQU5ETElORyBEQVRBIFdJVEggVEhFIFRJRFlWRVJTRQojIyMgTWFuaXB1bGF0aW9uCkZpbHRlcmluZyBpdGVtcyBpcyBpbmNyZWRpYmx5IGVhc3kgdmlhIGZpbHRlcigpLiBUaGUgJWluJSBvcGVyYXRvciBjYW4gYmUgdXNlZnVsIHdoZW4gdGVzdGluZyBmb3Igc2V2ZXJhbCB2YWx1ZXMuCmBgYHtyIGZpbHRlcn0KZmlsdGVyKGRhdGEsIEFnZSA+IDQyKSAgICAgICAgICAgICAgICAgICAgICAgICMgQWxsIHBlb3BsZSBvbGRlciB0aGFuIDQyCmZpbHRlcihkYXRhLCBHZW5kZXIgPT0gIk1hbGUiLCBXZWlnaHQgPiA3MCkgICAjIEFsbCBndXlzIGhlYXZpZXIgdGhhbiA3MCAKZmlsdGVyKGRpYW1vbmRzLCBjdXQgJWluJSBjKCJGYWlyIiwgIkdvb2QiKSkgICMgRGlhbW9uZHMgd2l0aCBGYWlyIG9yIEdvb2QgY3V0CmZpbHRlcihkaWFtb25kcywgY29sb3IgJWluJSBjKCJFIiwgIkYiKSkgICAgICAjIERpYW1vbmRzIHdpdGggRSBvciBGIGNvbG9yCmBgYAoKT3JkZXJpbmcgYWNjb3JkaW5nIHRvIGEgcGFydGljdWxhciB2YXJpYWJsZSBpcyBwZXJmb3JtZWQgd2l0aCB0b3BfbigpLiBJbiB0aGUgc2FtZSB2ZWluLCBhcnJhbmdlKCkgb3JkZXJzIHRoZSB3aG9sZSBkYXRhc2V0IGFjY29yZGluZyB0byBwYXJ0aWN1bGFyIG51bWVyaWNhbCB2YXJpYWJsZXMKYGBge3IgdG9wfQp0b3BfbihkYXRhLCAzLCBIZWlnaHQpICAgIyBUYWxsZXN0IDMgaW5kaXZpZHVhbHMKdG9wX24oZGF0YSwgLTQsIFdlaWdodCkgICMgTGlnaHRlc3QgNCBpbmRpdmlkdWFscwphcnJhbmdlKGRhdGEsIEdlbmRlciwgSGVpZ2h0KSAgICAgICAjIEZpcnN0IHNlbGVjdHMgaW5kaXZpZHVhbHMgYnkgZ2VuZGVyLCBhbmQgdGhlbiByYW5rcyB0aGVtIGJ5IGhlaWdodAphcnJhbmdlKGRhdGEsIEdlbmRlciwgZGVzYyhXZWlnaHQpKSAjIFNhbWUsIGJ1dCB3aXRoIGRlc2NlbmRpbmcgd2VpZ2h0LgpgYGAKClNlbGVjdGluZyBhIGZldyBjb2x1bW5zIHdpdGggc2VsZWN0KCkuCmBgYHtyIHNlbGVjdH0Kc2VsZWN0KGRhdGEsIEdlbmRlciwgQWdlKSAjIEtlZXBpbmcgb25seSB0aGVzZSAzIGNvbHVtbnMuCmBgYAoKQWRkaW5nIG5ldyBjb2x1bW5zIGNhbiBiZSBwZXJmb3JtZWQgd2l0aCB0aGUgbXV0YXRlKCkgZnVuY3Rpb24uIEJlbG93LCB3ZSBjb21wdXRlIHRoZSBwcmljZS9jYXJhdCByYXRpbyBvZiBkaWFtb25kcy4gVGhlIG11dGF0ZSBmdW5jdGlvbigpIGlzIFRIRSBiZXN0IGNob2ljZSB0byBhZGQgY29sdW1ucyB0aGF0IGFyZSBlYXNpbHkgY2FsY3VsYXRlZC4KYGBge3IgbXV0YXRlfQpkaWFtb25kcyAlPiUgCiAgICBzZWxlY3QoLXgsIC15LCAteiwgLSBkZXB0aCwgLXRhYmxlKSAlPiUgIyBHZXR0aW5nIHJpZCBvZiBsZXNzIHVzZWZ1bCB2YXJpYWJsZXMKICAgIG11dGF0ZShQX0NfcmF0aW8gPSBwcmljZS9jYXJhdCkgICAgICAgICAjIENyZWF0ZSBhIG5ldyBjb2x1bW46IHRoZSBwcmljZS9jYXJhdCByYXRpbwpgYGAKCgojIyMgUGlwaW5nClZlcnkgb2Z0ZW4sIHNldmVyYWwgb3BlcmF0aW9ucyBhcmUgcmVxdWlyZWQgYmVmb3JlIHRoZSBkZXNpcmVkIG91dHB1dCBpcyBvYnRhaW5lZC4gVGhlcmUgaXMgYW4gZWxlZ2FudCB3YXkgdG8gc3VjY2Vzc2l2ZWx5IGNvbWJpbmUgZnVuY3Rpb25zLiBJdCBpcyBjYWxsZWQgcGlwaW5nIGFuZCB3b3JrcyB3aXRoIHRoZSBvcGVyYXRvciAlPiUsIHdoaWNoIHdlIHVzZWQganVzdCBhYm92ZS4KCmBgYHtyIHBpcGV9CmRhdGEgJT4lIHNlbGVjdChHZW5kZXIsIEhlaWdodCwgQWdlKSAlPiUgZmlsdGVyKEFnZSA+IDQwKSAjIFRoZSB0d28gZnVuY3Rpb25zIHNlbGVjdCgpIGFuZCBmaWx0ZXIoKSBhcmUgYXBwbGllZCBzdWNjZXNzaXZlbHkKYGBgCgojIyMgUGl2b3QgdGFibGVzClBpdm90IHRhYmxlcyAoUFQpIGFyZSB2ZXJ5IHNpbXBseSBvYnRhaW5lZCB2aWEgdGhlIGNvbWJpbmF0aW9uIG9mIGdyb3VwX2J5KCkgc3VtbWFyaXNlKCkuIGdyb3VwX2J5KCkgZGV0ZXJtaW5lcyBhbG9uZyB3aGljaCB2YXJpYWJsZXMgdGhlIFBUIHdpbGwgYmUgY29tcHV0ZWQgYW5kIHN1bW1hcmlzZSgpIHNwZWNpZmllcyB0aGUgbWV0cmljL2luZGljYXRvciBvZiBpbnRlcmVzdC4KYGBge3IgUFR9CmRpYW1vbmRzICU+JSAKICAgIGdyb3VwX2J5KGN1dCwgY29sb3IsIGNsYXJpdHkpICU+JSAjIEdyb3VwaW5nIGJ5IGN1dCwgdGhlbiBjb2xvciBhbmQgdGhlIGNsYXJpdHkKICAgIHN1bW1hcmlzZShtZWRfcHJpY2UgPSBtZWRpYW4ocHJpY2UpLCBtZWRfY2FyYXQgPSBtZWRpYW4ocHJpY2UpKSAjIEZvciBlYWNoIHN1Ymdyb3VwLCBjb21wdXRpbmcgMiBpbmRpY2F0b3JzOiBtZWRpYW4gcHJpY2UgYW5kIG1lZGlhbiBjYXJhdApgYGAKCgoKIyMjIEdyYXBocwpJbiBSLCB0aGVyZSBpcyBvbmUgbWFqb3IgZnVuY3Rpb24gZm9yIGdyYXBoaWNhbCByZXByZXNlbnRhdGlvbjogZ2dwbG90KCkuIEluIGdncGxvdCwgYWVzKCkgZGVzY3JpYmUgdGhlIGFlc3RoZXRpY3Mgb2YgdGhlIHBsb3QuIFVzdWFsbHksIHdlIG5lZWQgdG8gZGVmaW5lIHRoZSB4LWF4aXMgdmFyaWFibGUgYW5kIG9mdGVuLCB0aGUgeS1heGlzIHZhcmlhYmxlIChmb3Igc2NhdHRlciBwbG90cyBhbmQgbGluZXMpLiBBbHNvLCByZXByZXNlbnRhdGlvbnMgY2FuIGFsbG93IGZvciBjb2xvciwgc2hhcGUgYW5kIHNpemUgdmFyaWF0aW9ucywgZXNwZWNpYWxseSBmb3Igc2NhdHRlciBwbG90cy4gVGhlIHR5cGUgb2YgdGhlIHBsb3QgaXMgZGVmaW5lZCBieSBhICdnZW9tJy4gTW9yZSBkZXRhaWxzIGNhbiBiZSBmb3VuZCBoZXJlOiAKaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvYXJ0aWNsZXMvZ2dwbG90Mi1zcGVjcy5odG1sCgpgYGB7ciBnZ3Bsb3R9CmdncGxvdChkYXRhKSArIGdlb21fcG9pbnQoYWVzKHggPSBIZWlnaHQsIHkgPSBXZWlnaHQsIGNvbG9yID0gQWdlLCBzaGFwZSA9IEdlbmRlciwgc2l6ZSA9IEFnZSkpICMgZ2VvbV9wb2ludCA9IHNjYXR0ZXIgcGxvdApnZ3Bsb3QoZGlhbW9uZHMpICsgZ2VvbV9iYXIoYWVzKHggPSBjbGFyaXR5LCBmaWxsID0gY3V0KSkgIyBnZW9tX2JhciA9IGJhcnBsb3QKCmRpYW1vbmRzICU+JSAKICAgIGdyb3VwX2J5KGNsYXJpdHkpICU+JSAKICAgIHN1bW1hcmlzZShtZWRfY2FyYXQgPSBtZWRpYW4ocHJpY2UpKSAlPiUKICAgIGdncGxvdChhZXMoeCA9IGNsYXJpdHksIHkgPSBtZWRfY2FyYXQpKSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKQoKICMgV2hlbiBwcm92aWRpbmcgYSAieSIgZm9yIGEgYmFycGxvdCwgeW91IG11c3Qgc3BlY2lmeSB0aGUgc3RhdD0iaWRlbnRpdHkiLiBUaGlzIGdyYXBoIHNob3dzIHRoYXQgbGFyZ2UgZGlhbW9uZHMgYXJlIG11Y2ggbGVzcyAicHVyZSIgdGhhbiBzbWFsbGVyIG9uZXMuIFdoaWNoIG1ha2VzIHNlbnNlLgpgYGAKCgoKIyMgNSAtIExPT1BTClRoZXJlIGFyZSB1c3VhbGx5IHNldmVyYWwgdHlwZXMgb2YgbG9vcHMsIGJ1dCB3ZSB3aWxsIGZvY3VzIG9uIHRoZSAqZm9yKiBsb29wLgpJdHMgc3RydWN0dXJlIGlzIHNpbXBsZTogdGhlIGlkZWEgaXMgdG8gcmVwZWF0IGEgdGFzayBhIGZpbml0ZSBudW1iZXIgb2YgdGltZXMuIFRoaXMgYWxsb3dzIHRvIGF1dG9tYXRlIHRoZSBjaGFuZ2VzIGluIGEgdmFyaWFibGUuIEZvciBpbnN0YW5jZSwgdGhlIEZpYm9uYWNjaSBzZXF1ZW5jZToKYGBge3IgbG9vcH0KbmIgPC0gMjAgICMgTnVtYmVyIG9mIGRlc2lyZWQgbnVtYmVycwpGaWIgPC0gMSAgIyBJbml0aWF0ZSB0aGUgb3V0cHV0IChpdCB3aWxsIGJlIGluY3JlbWVudGFsbHkgYXVnbWVudGVkKTogZmlyc3QgdmFsdWUKRmliWzJdIDwtIDEgIyBJbml0aWFsaXNhdGlvbjogc2Vjb25kIHZhbHVlCmZvcihrIGluIDM6bmIpewogICBGaWJba10gPC0gRmliW2stMV0gKyBGaWJbay0yXSAjIHRoZSBrdGggdmFsdWUgaXMgdGhlIHN1bSBvZiB0aGUgMiBwcmV2aW91cyBvbmVzCn0KRmliICMgU2hvdyB0aGUgc2VxdWVuY2UKYGBgCgojIyA2IC0gTUlTQy4gRlVOQ1RJT05TCiMjIyBTdGF0aXN0aWNzCkJlbG93LCB3ZSBwcmVzZW50IGEgZmV3IHVzZWZ1bCBmdW5jdGlvbnMuCmBgYHtyIHN0YXR9CnJiaW5kKGMoMiw1LDcpLGMoMywxLDgpKSAKcmJpbmQoYygyLDUsNyksYygzLDEsOCkpICU+JSB0KCkgICAgIyBUcmFuc3Bvc2UgKGEgdmVjdG9yLCBhIG1hdHJpeCwgYSBkYXRhZnJhbWUpCnNxcnQoNSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU3F1YXJlIHJvb3QKbWVhbihjKDI6NiwgODo0MykpICAgICAgICAgICAgICAgICAgIyBBdmVyYWdlIHZhbHVlCnNkKGMoMjo2LCA4OjQzKSkgICAgICAgICAgICAgICAgICAgICMgU3RhbmRhcmQgZGV2aWF0aW9uLCB1c2UgdmFyKCkgZm9yIHRoZSB2YXJpYW5jZQp2IDwtIHJub3JtKDE4KSAgICAgICAgICAgICAgICAgICAgICAjIFdlIGdlbmVyYXRlIGEgcmFuZG9tIHZlY3RvcgptaW4odikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIE1pbmltdW0KbWF4KHYpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBNYXhpbXVtCnYgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgQSBsb29rIGF0IHRoZSB3aG9sZSB2ZWN0b3IKbGFnKDE6MTApICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUaGUgbGFnKCkgZnVuY3Rpb246IHNoaWZ0cyBkYXRhIHRvIHRoZSByaWdodAptZWFuKGMoTkEsMToxMCksIG5hLnJtID0gVCkgICAgICAgICAjIFdoZW4gY29tcHV0aW5nIG1lYW5zLCBpZiB2YWx1ZXMgYXJlIG1pc3NpbmcsIE5BIHdpbGwgYmUgcmV0dXJuZWQuIG5hLnJtID0gVCBzb2x2ZXMgdGhpcy4KYGBgCgojIyMgQ2hhbmdpbmcgbW9kZXMKVXN1YWwgbW9kZXMgZm9yIHZhcmlhYmxlcyBhcmU6ICAKLSBsb2dpY2FsIChCb29sZWFuLCBUUlVFIG9yIEZBTEUpLCAgCi0gbnVtZXJpYyAobnVtYmVycyksICAKLSBjaGFyYWN0ZXIgKHRleHQpLCAgCi0gZmFjdG9yICh1bm9yZGVyZWQgY2F0ZWdvcnkpIGFuZCAgCi0gb3JkZXJlZCBmYWN0b3IgKG9yZGVyZWQgY2F0ZWdvcnkpICAKSXQgaXMgc29tZXRpbWVzIHBvc3NpYmxlIHRvIHN3aXRjaCBmcm9tIG9uZSB0byBhbm90aGVyLiBPbmUgY291bnRlci1leGFtcGxlIGlzOiB0cmFuc2xhdGluZyBhIGNoYXJhdGVyIGludG8gYSBudW1iZXIuICAKU29tZSBleGFtcGxlcyBiZWxvdy4KCmBgYHtyIG1vZGVzfQpjKCIzIiwgIjgiLCAiNyIpICAgICAgICAgICAgICAgICAgICAjIE51bWJlcnMgdmlld2VkIGFzIHRleHQgCmMoIjMiLCAiOCIsICI3IikgJT4lIGFzLm51bWVyaWMoKSAgICMgQ2hhbmdlIHRoZSBhYm92ZSBpbnRvICp0cnVlKiBudW1iZXJzCmMoMyw0LDYpICU+JSBhcy5jaGFyYWN0ZXIoKSAgICAgICAgICMgVGhlIG9wcG9zaXRlOiBjaGFuZ2UgZmllbGRzIGludG8gY2hhcmFjdGVycwpkYXRhJEFnZSAlPiUgYXMuZmFjdG9yKCkgJT4lIHN1bW1hcnkoKSAjIGFzLmZhY3RvcigpIHRyYW5zZm9ybXMgdGhlIGZpZWxkcyBpbnRvIGNhdGFnb3JpZXMsIHRoZSBmaW5hbCBzdGVwIGNvbXB1dGVzIHRoZSBudW1iZXIgb2YgZWxlbWVudHMgaW4gZWFjaCBjYXRlcmdvcnkKYGBgCgoK