1 - GETTING STARTED

Working directory

The very first step after launching RStudio is to specify the working directory. You can do it directly in the Files pane (using the blue wheel), or via the setwd() function. The getwd() yields the location of the current working directory.

getwd()
[1] "/Users/coqueret/Documents/IT/Cours/RStats/Git"

Packages

Working with packages requires:
1 - an installation, only once (basically it downloads the code/files on your computer) - except if you wan to update version
2 - an activation, each time you start RStudio

if(!require(openxlsx)) {install.packages("openxlsx") } # Only installs if missing
if(!require(readxl)) {install.packages("readxl") }     # Only installs if missing
# The hashtag is used for comments: the program does not read the line
library(openxlsx)
library(readxl)
library(tidyverse)

Importing data

This is usually done directly in the user interface (Files pane), or with packages like openxlsx or readxl (to import Excel files) with the function read.xlsx() or read_excel(). The basic case looks like that: 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.

anes <- read.xlsx("anes.xlsx")
anes <- read_excel("anes.xlsx") # Same, with another packages

2 - CREATING DATA

Simple sequences

You can create data from scratch, using the colon operator for instance. Or the seq() function for sequences. You can replicate items (e.g., sequences) using the rep() function.

1:10 # All integers from 1 to 10
 [1]  1  2  3  4  5  6  7  8  9 10
3:17 # All integers from 3 to 17
 [1]  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17
seq(1,2,0.1) # The syntax is: seq(begin, end, step size)
 [1] 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0
rep("HA", 6)
[1] "HA" "HA" "HA" "HA" "HA" "HA"
rep(c(1,2,3,4), 3) # Syntax: rep(what_you_want_to_replicate, nb_replications)
 [1] 1 2 3 4 1 2 3 4 1 2 3 4

A very important function: 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 concatenate data is to use row-bind and column-bind functions rbind() and cbind().

rbind(c(2,5,7),c(3,1,8)) # Binding rows
     [,1] [,2] [,3]
[1,]    2    5    7
[2,]    3    1    8
cbind(c(2,5,7),c(3,1,8)) # Binding columns
     [,1] [,2]
[1,]    2    3
[2,]    5    1
[3,]    7    8

Matrices

You can also fill in matrices: the syntax is matrix(DATA, nrow = r, ncol = c) where the length of DATA must be equal to r*c! If one dimension is omitted, R will do the division (assuming the format is correct).

m <- matrix(1:20, nrow = 4) 
m2 <- matrix(1:20, nrow = 4, byrow = T) # A matrix can be filled by rows or by columns
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

Simple matrix operations in R: transposing, multiplying.

t(m)        # t() is used for transposing; it works for vectors too! By default, vectors are columns
     [,1] [,2] [,3] [,4]
[1,]    1    2    3    4
[2,]    5    6    7    8
[3,]    9   10   11   12
[4,]   13   14   15   16
[5,]   17   18   19   20
m*m2        # This is term-by-term multiplication
     [,1] [,2] [,3] [,4] [,5]
[1,]    1   10   27   52   85
[2,]   12   42   80  126  180
[3,]   33   84  143  210  285
[4,]   64  136  216  304  400
m%*%t(m2)   # This is matrix multiplication
     [,1] [,2] [,3] [,4]
[1,]  175  400  625  850
[2,]  190  440  690  940
[3,]  205  480  755 1030
[4,]  220  520  820 1120

R is great to generate random data.

runif(15) # uniform distribution: 15 samples
 [1] 0.29740595 0.43197770 0.99838659 0.79067341 0.99508619 0.08679626 0.74249126 0.99556576
 [9] 0.43868022 0.08526857 0.05788177 0.68824065 0.13493421 0.04587817 0.91213121
rnorm(10) # Gaussian distribution (parameters could be specified), 10 samples 
 [1]  0.70532545  0.26341877 -0.27780366  0.40497219 -0.10642416 -0.66211115  0.38518040  0.06023922
 [9]  0.20728269  1.15380725

An overview of all distributions available across R packages can be found here: https://cran.r-project.org/web/views/Distributions.html

Dataframes

Datasets often mix text and numbers. R can do that too, with data frames (the modern version of dataframe is the tibble). 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).

colnames(data)[4] <- "Years"    # Change last column name
head(data)                      # Show the impact
colnames(data)[4] <- "Age"      # Coming back to original name

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) 
[1] 14  4
nrow(data)   # Number of rows
[1] 14
ncol(data)   # Number of columns
[1] 4
length(3:35) # Length of 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:

rr data[data$Age > 42, ] # Mind the comma: we keep all columns! r data[data\(Age > 42 & data\)Weight >70, ] # the & operator allows to add sorting criteria

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] 174 171 192 173 182 170 188 159 181 166 169 171 167 181
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]  TRUE FALSE FALSE  TRUE FALSE  TRUE FALSE FALSE FALSE  TRUE FALSE  TRUE FALSE FALSE

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

rr head(data, 8) # First 8 rows (default number of rows is 6) r tail(data) # Last 6 rows

Only the TRUE rows are kept. As we 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,176) # 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. Sometimes, the content of the variable is huge and cannot properly be displayed. You can also double-click on the variable name in the environment pane.
The head() function shows the first 6 lines and the tail() function shows the last 6 lines.

head(data, 8) # First 8 rows (default number of rows is 6)
tail(data)    # Last 6 rows

If you want to see the different possible values of a factor (categorical variables), you can use the levels() function.

levels(diamonds$clarity)
[1] "I1"   "SI2"  "SI1"  "VS2"  "VS1"  "VVS2" "VVS1" "IF"  

Though honestly, you would get the information using the summary() function as well - because there aren’t too many of them.

4 - LOOPS: 1st step towards programming

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    # First value
Fib[2] <- 1 # Second value
for(k in 3:nb){
   Fib[k] <- Fib[k-1] + Fib[k-2] # New value equals 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
[20] 6765

GOING FURTHER: loops can take time. To speed things up, have a look at the map() family of functions. The apply functions are also cool (see below).

5 - MISC. FUNCTIONS

Statistics

Below, we present a few useful functions.

rr c(, , ) # Numbers viewed as text

[1] \3\ \8\ \7\

rr c(, , ) %>% as.numeric() # Change the above into true numbers

[1] 3 8 7

rr c(3,4,6) %>% as.character() # The opposite: change fields into characters

[1] \3\ \4\ \6\

rr data$Age %>% as.factor() %>% summary()

29 34 36 37 39 40 42 44 45 46 
 1  1  1  1  1  2  3  1  1  2 

rr # as.factor() transforms the fields into catagories, the final step computes the number of elements in each catergory fac <- rep(c(1,2,3),2) %>% as.factor() # We create a simple factor and change its values below fac <- fac %>% recode_factor(1 = , 2= , 3= , .ordered = TRUE)

Changing modes

Usual modes for variables are:
- logical (Boolean, TRUE or FALE), NOTE: they are also converted into number: FALSE = 0, TRUE = 1
- 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. For a factor, the levels is the values that can be taken by the variable. See levels(). 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()  
21 27 30 33 34 37 38 42 43 46 49 52 
 1  1  1  1  1  1  1  2  1  2  1  1 
# as.factor() transforms the fields into catagories, the final step computes the number of elements in each catergory
fac <- rep(c(1,2,3),2) %>% as.factor()  # We create a simple factor and change its values below
fac <- fac %>% recode_factor(`1` = "Low", `2`= "Medium", `3`= "High", .ordered = TRUE)
fac
[1] Low    Medium High   Low    Medium High  
Levels: Low < Medium < High

GOING FURTHER:
- lists in R are very flexible structures that can embed different types of modes. - arrays are like matrices, but are allowed to have higher dimensions (>2). - tibbles are the dataframes 2.0 stemming from the tidyverse.

The apply() function

When given a rectangular dataset with numbers only, it is often useful to apply the same function to all rows or all columns. Normally, this would require a for loop. Fortunately, alternative solutions exist. For simple functions, like sums and means, dedicated functions have been created.

data_num <- select(data, -Gender)  # Take out gender because it's not a number
colMeans(data_num)                 # Mean of all columns
   Weight    Height       Age 
 69.14286 175.21429  38.57143 
rowSums(data_num)                  # Sum of all rows: not very meaningful
 [1] 289 279 324 306 284 315 250 276 254 266 274 281 276 287

For more general functions, apply() is the way to go. You need to specify the dimension across which the function is computed.

apply(data_num, 2, mean) # The syntax is: apply(data, dimension, function), dimension 2 = column
   Weight    Height       Age 
 69.14286 175.21429  38.57143 
apply(data_num, 1, sum)  # Dimension 1 = row
 [1] 289 279 324 306 284 315 250 276 254 266 274 281 276 287
apply(data_num, 2, sd)   # Computing the standard deviation of each column
   Weight    Height       Age 
10.875924 10.116084  8.829272 

Note that you can use apply() on arrays with more than two dimensions.

User-specified functions

R let’s you create your own functions! Below, we create \(f(x)=(1+x^2)^{-1}\) and plot it.

dens = function(x){
  return(1/(1+x^2))
}
dens(1:3)  # Test values
[1] 0.5 0.2 0.1
ggplot(data.frame(x = c(-4,4)), aes(x = x)) + stat_function(fun = dens) # Plot

Missing data

This is a common problem in data science. Here, we only aim to locate missing data - using the is.na() function. Dealing with absent values is out of our scope.

data[3,4] <- NA         # NA is often the default for missing data points
is.na(data)             # This is the brute-force method. Ok for small datasets
      Gender Weight Height   Age
 [1,]  FALSE  FALSE  FALSE FALSE
 [2,]  FALSE  FALSE  FALSE FALSE
 [3,]  FALSE  FALSE  FALSE  TRUE
 [4,]  FALSE  FALSE  FALSE FALSE
 [5,]  FALSE  FALSE  FALSE FALSE
 [6,]  FALSE  FALSE  FALSE FALSE
 [7,]  FALSE  FALSE  FALSE FALSE
 [8,]  FALSE  FALSE  FALSE FALSE
 [9,]  FALSE  FALSE  FALSE FALSE
[10,]  FALSE  FALSE  FALSE FALSE
[11,]  FALSE  FALSE  FALSE FALSE
[12,]  FALSE  FALSE  FALSE FALSE
[13,]  FALSE  FALSE  FALSE FALSE
[14,]  FALSE  FALSE  FALSE FALSE
is.na(data) %>% rowSums # Locating rows
 [1] 0 0 1 0 0 0 0 0 0 0 0 0 0 0
is.na(data) %>% colSums # Locating columns
Gender Weight Height    Age 
     0      0      0      1 

The is.na combined with row and column sums locates the missing data very simply.

Exercises

Handling vectors & matrices

  1. Set your working directory and load the tidyverse package
  2. What are two simple ways to create the series {0.1, 0.2, 0.3, …, 9.9, 10}?
  3. Same question for {2,4,6, …, 96, 98, 100}.
  4. What is the simplest way to build a 10-by-10 matrix corresponding to the classical multiplication table? i.e. such that on the sixth column and seventh row (or vice-versa), you get 42. Replace its first value by the string “One” and see what happens.
  5. Fill in a 10-by-10 matrix with random numbers and compute its column sums

Filtering in base R is tedious!

  1. Load the ANES database (“anes.RData”)
  2. Using only base R commands (and not the filter() function), find the men in the dataset aged 87 years old.
  3. Again without resorting to filter(), determine the party affiliation of the people older than 70 who have three or more children
  4. Likewise, compute the proportion of party affiliation of respondents younger than 25; same question for those older than 60. Any comments?

The importance of modes

  1. Load the ANES database in Excel format (“anes.xlsx”) - make sure you have the file at the right place before!
  2. Look at the summary: what happened?
  3. Change the modes into factors, and, possibly, ordered factors when need be.
    CONCLUSION: data preparation can be time-consuming. But it is imperative.

Functions

The purpose of the questions below is to manipulate functions with integrated loops.
1) Create a function with one argument, n, that returns the values of j^j for j = 1…n. Test it on n = 10 and check! Anyway we could to this faster?

LS0tCnRpdGxlOiAiUiBCYXNpY3MiCm91dHB1dDogCiAgIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCiMjIDEgLSBHRVRUSU5HIFNUQVJURUQKIyMjIFdvcmtpbmcgZGlyZWN0b3J5ClRoZSB2ZXJ5IGZpcnN0IHN0ZXAgYWZ0ZXIgbGF1bmNoaW5nIFJTdHVkaW8gaXMgdG8gc3BlY2lmeSB0aGUgKip3b3JraW5nIGRpcmVjdG9yeSoqLiBZb3UgY2FuIGRvIGl0IGRpcmVjdGx5IGluIHRoZSBGaWxlcyBwYW5lICh1c2luZyB0aGUgKipibHVlIHdoZWVsKiopLCBvciB2aWEgdGhlICoqc2V0d2QqKigpIGZ1bmN0aW9uLiBUaGUgKipnZXR3ZCoqKCkgeWllbGRzIHRoZSBsb2NhdGlvbiBvZiB0aGUgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeS4KYGBge3IgV29ya2luZ19kaXJlY3Rvcnl9CmdldHdkKCkKYGBgCgojIyMgUGFja2FnZXMKV29ya2luZyB3aXRoIHBhY2thZ2VzIHJlcXVpcmVzOiAgCjEgLSBhbiAqKmluc3RhbGxhdGlvbioqLCBvbmx5IG9uY2UgKGJhc2ljYWxseSBpdCBkb3dubG9hZHMgdGhlIGNvZGUvZmlsZXMgb24geW91ciBjb21wdXRlcikgLSBleGNlcHQgaWYgeW91IHdhbiB0byB1cGRhdGUgdmVyc2lvbiAgCjIgLSBhbiAqKmFjdGl2YXRpb24qKiwgZWFjaCB0aW1lIHlvdSBzdGFydCBSU3R1ZGlvCmBgYHtyIFBhY2thZ2VzLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KaWYoIXJlcXVpcmUob3Blbnhsc3gpKSB7aW5zdGFsbC5wYWNrYWdlcygib3Blbnhsc3giKSB9ICMgT25seSBpbnN0YWxscyBpZiBtaXNzaW5nCmlmKCFyZXF1aXJlKHJlYWR4bCkpIHtpbnN0YWxsLnBhY2thZ2VzKCJyZWFkeGwiKSB9ICAgICAjIE9ubHkgaW5zdGFsbHMgaWYgbWlzc2luZwojIFRoZSBoYXNodGFnIGlzIHVzZWQgZm9yIGNvbW1lbnRzOiB0aGUgcHJvZ3JhbSBkb2VzIG5vdCByZWFkIHRoZSBsaW5lCmxpYnJhcnkob3Blbnhsc3gpCmxpYnJhcnkocmVhZHhsKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgojIyMgSW1wb3J0aW5nIGRhdGEKVGhpcyBpcyB1c3VhbGx5IGRvbmUgZGlyZWN0bHkgaW4gdGhlIHVzZXIgaW50ZXJmYWNlIChGaWxlcyBwYW5lKSwgb3Igd2l0aCBwYWNrYWdlcyBsaWtlICoqb3Blbnhsc3gqKiBvciAqKnJlYWR4bCoqICh0byBpbXBvcnQgRXhjZWwgZmlsZXMpIHdpdGggdGhlIGZ1bmN0aW9uICoqcmVhZC54bHN4KiooKSBvciAqKnJlYWRfZXhjZWwqKigpLiBUaGUgYmFzaWMgY2FzZSBsb29rcyBsaWtlIHRoYXQ6IAp0ZXN0X2RhdGEgPC0gcmVhZC54bHN4KCJNeUZpbGUueGxzeCIpICAgCk9SICAKdGVzdF9kYXRhIDwtIHJlYWRfZXhjZWwoIk15RmlsZS54bHN4IikuICAgIApUaGlzIHN0b3JlcyB5b3VyIGRhdGEgaW50byB0aGUgdGVzdF9kYXRhIHZhcmlhYmxlLiBUaGlzIGFzc3VtZXMgdGhhdCB0aGUgRXhjZWwgZmlsZSAiTXlGaWxlLnhsc3giIGV4aXN0cyBpbiB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5LgoKYGBge3IgSW1wb3J0IGV4Y2VsfQphbmVzIDwtIHJlYWQueGxzeCgiYW5lcy54bHN4IikKYW5lcyA8LSByZWFkX2V4Y2VsKCJhbmVzLnhsc3giKSAjIFNhbWUsIHdpdGggYW5vdGhlciBwYWNrYWdlcwpgYGAKCgojIyAyIC0gQ1JFQVRJTkcgREFUQQojIyMgU2ltcGxlIHNlcXVlbmNlcwpZb3UgY2FuIGNyZWF0ZSBkYXRhIGZyb20gc2NyYXRjaCwgdXNpbmcgdGhlIGNvbG9uIG9wZXJhdG9yIGZvciBpbnN0YW5jZS4gT3IgdGhlICoqc2VxKiooKSBmdW5jdGlvbiBmb3Igc2VxdWVuY2VzLiBZb3UgY2FuIHJlcGxpY2F0ZSBpdGVtcyAoZS5nLiwgc2VxdWVuY2VzKSB1c2luZyB0aGUgKipyZXAqKigpIGZ1bmN0aW9uLgoKYGBge3IgQ29sb259CjE6MTAgIyBBbGwgaW50ZWdlcnMgZnJvbSAxIHRvIDEwCjM6MTcgIyBBbGwgaW50ZWdlcnMgZnJvbSAzIHRvIDE3CnNlcSgxLDIsMC4xKSAjIFRoZSBzeW50YXggaXM6IHNlcShiZWdpbiwgZW5kLCBzdGVwIHNpemUpCnJlcCgiSEEiLCA2KQpyZXAoYygxLDIsMyw0KSwgMykgIyBTeW50YXg6IHJlcCh3aGF0X3lvdV93YW50X3RvX3JlcGxpY2F0ZSwgbmJfcmVwbGljYXRpb25zKQpgYGAKICAKICAKQSB2ZXJ5IGltcG9ydGFudCBmdW5jdGlvbjogdGhlICoqYyoqKCkgZnVuY3Rpb24gY29uY2F0ZW5hdGVzIGFuZCBlbmNhcHN1bGF0ZXMgbnVtYmVycyAob3IgdGV4dCk6CmBgYHtyIGMoKX0KYygyLDUsNykKYygxOjYsMTI6MjApCmMoIlIiLCAiIGlzICIsICJhd2Vzb21lIikKYGBgCiAgCiAgCkFub3RoZXIgd2F5IHRvIGNvbmNhdGVuYXRlIGRhdGEgaXMgdG8gdXNlIHJvdy1iaW5kIGFuZCBjb2x1bW4tYmluZCBmdW5jdGlvbnMgKipyYmluZCoqKCkgYW5kICoqY2JpbmQqKigpLiAKYGBge3IgYmluZH0KcmJpbmQoYygyLDUsNyksYygzLDEsOCkpICMgQmluZGluZyByb3dzCmNiaW5kKGMoMiw1LDcpLGMoMywxLDgpKSAjIEJpbmRpbmcgY29sdW1ucwpgYGAKICAKIAojIyMgTWF0cmljZXMgCiAgCllvdSBjYW4gYWxzbyBmaWxsIGluIG1hdHJpY2VzOiB0aGUgc3ludGF4IGlzIG1hdHJpeChEQVRBLCBucm93ID0gciwgbmNvbCA9IGMpIHdoZXJlIHRoZSBsZW5ndGggb2YgREFUQSBtdXN0IGJlIGVxdWFsIHRvIHIqYyEgSWYgb25lIGRpbWVuc2lvbiBpcyBvbWl0dGVkLCBSIHdpbGwgZG8gdGhlIGRpdmlzaW9uIChhc3N1bWluZyB0aGUgZm9ybWF0IGlzIGNvcnJlY3QpLgpgYGB7ciBtYXRyaWNlc30KbSA8LSBtYXRyaXgoMToyMCwgbnJvdyA9IDQpIAptMiA8LSBtYXRyaXgoMToyMCwgbnJvdyA9IDQsIGJ5cm93ID0gVCkgIyBBIG1hdHJpeCBjYW4gYmUgZmlsbGVkIGJ5IHJvd3Mgb3IgYnkgY29sdW1ucwptCm0yCmBgYAoKU2ltcGxlIG1hdHJpeCBvcGVyYXRpb25zIGluIFI6IHRyYW5zcG9zaW5nLCBtdWx0aXBseWluZy4KCmBgYHtyIHRyYW5zcG9zZX0KdChtKSAgICAgICAgIyB0KCkgaXMgdXNlZCBmb3IgdHJhbnNwb3Npbmc7IGl0IHdvcmtzIGZvciB2ZWN0b3JzIHRvbyEgQnkgZGVmYXVsdCwgdmVjdG9ycyBhcmUgY29sdW1ucwptKm0yICAgICAgICAjIFRoaXMgaXMgdGVybS1ieS10ZXJtIG11bHRpcGxpY2F0aW9uCm0lKiV0KG0yKSAgICMgVGhpcyBpcyBtYXRyaXggbXVsdGlwbGljYXRpb24KYGBgCgoKUiBpcyBncmVhdCB0byBnZW5lcmF0ZSByYW5kb20gZGF0YS4KYGBge3J9CnJ1bmlmKDE1KSAjIHVuaWZvcm0gZGlzdHJpYnV0aW9uOiAxNSBzYW1wbGVzCnJub3JtKDEwKSAjIEdhdXNzaWFuIGRpc3RyaWJ1dGlvbiAocGFyYW1ldGVycyBjb3VsZCBiZSBzcGVjaWZpZWQpLCAxMCBzYW1wbGVzIApgYGAKCkFuIG92ZXJ2aWV3IG9mIGFsbCBkaXN0cmlidXRpb25zIGF2YWlsYWJsZSBhY3Jvc3MgUiBwYWNrYWdlcyBjYW4gYmUgZm91bmQgaGVyZToKaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3ZpZXdzL0Rpc3RyaWJ1dGlvbnMuaHRtbAoKCgojIyMgRGF0YWZyYW1lcwpEYXRhc2V0cyBvZnRlbiBtaXggdGV4dCBhbmQgbnVtYmVycy4gUiBjYW4gZG8gdGhhdCB0b28sIHdpdGggZGF0YSBmcmFtZXMgKHRoZSBtb2Rlcm4gdmVyc2lvbiBvZiBkYXRhZnJhbWUgaXMgdGhlIHRpYmJsZSkuIExldCdzIGNyZWF0ZSBvbmUgd2l0aCB0aGUgKipkYXRhLmZyYW1lKiooKSBmdW5jdGlvbi4gV2UgdXNlIHRoZSAqKnJvdW5kKiooKSBmdW5jdGlvbiB3aGljaCByb3VuZHMgdXAgbnVtYmVycy4KYGBge3IgZGZ9Cm5iX2dlbmRlciA8LSA3ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTnVtYmVyIG9mIHBlb3BsZSBvZiBlYWNoIGdlbmRlcgpHZW5kZXIgPC0gcmVwKGMoIk1hbGUiKSxuYl9nZW5kZXIpICAgICAgICAgICAgICAgICAgICAgICAgICAjIG5iX2dlbmRlciBtZW4gaW4gdG90YWwKV2VpZ2h0IDwtIHJub3JtKG5iX2dlbmRlciwgbWVhbiA9IDcwLCBzZCA9IDgpICU+JSByb3VuZCgpICAgIyBpbiBraWxvcwpIZWlnaHQgPC0gcm5vcm0obmJfZ2VuZGVyLCBtZWFuID0gMTc4LCBzZCA9IDEwKSAlPiUgcm91bmQoKSAjIGluIGNtCkFnZSA8LSBybm9ybShuYl9nZW5kZXIsIG1lYW4gPSA0MCwgc2QgPSA3KSAgJT4lIHJvdW5kKCkgIApkYXRhIDwtIGRhdGEuZnJhbWUoR2VuZGVyLFdlaWdodCxIZWlnaHQsQWdlKSAgICAgICAgICAgICAgICAjIGRhdGEgd2l0aCBvbmx5IG1lbgpHZW5kZXIgPC0gcmVwKGMoIkZlbWFsZSIpLG5iX2dlbmRlcikgICAgICAgICAgICAgICAgICAgICAgICAjIG5iX2dlbmRlciB3b21lbiBpbiB0b3RhbApXZWlnaHQgPC0gIHJub3JtKG5iX2dlbmRlciwgNjAsIHNkID0gOCkgICU+JSByb3VuZCgpICAgICAgICAjIGluIGtpbG9zCkhlaWdodCA8LSAgcm5vcm0obmJfZ2VuZGVyLCAxNjcsIHNkID0gMTApICAlPiUgcm91bmQoKSAgICAgICMgaW4gY20KQWdlIDwtIHJub3JtKG5iX2dlbmRlciwgbWVhbiA9IDQwLCBzZCA9IDcpICAlPiUgcm91bmQoKSAgCmRhdGEgPC0gcmJpbmQoZGF0YSwgZGF0YS5mcmFtZShHZW5kZXIsV2VpZ2h0LEhlaWdodCxBZ2UpKSAgICMgZ3JvdXBpbmcgd29tZW4gd2l0aCBtZW4KZGF0YQpgYGAKCllvdSBjYW4gdXNlICoqcm93bmFtZXMqKigpIG9yICoqY29sbmFtZXMqKigpIHRvIGdldCBvciBzZXQgdGhlIG5hbWVzIG9mIHJvd3Mgb3IgY29sdW1uczogY29sbmFtZXMoZGF0YSkuCgpgYGB7ciBjb2xuYW1lc30KY29sbmFtZXMoZGF0YSlbNF0gPC0gIlllYXJzIiAgICAjIENoYW5nZSBsYXN0IGNvbHVtbiBuYW1lCmhlYWQoZGF0YSkgICAgICAgICAgICAgICAgICAgICAgIyBTaG93IHRoZSBpbXBhY3QKY29sbmFtZXMoZGF0YSlbNF0gPC0gIkFnZSIgICAgICAjIENvbWluZyBiYWNrIHRvIG9yaWdpbmFsIG5hbWUKYGBgCgoKIyMjIERpbWVuc2lvbnMKWW91IGNhbiBvYnRhaW4gdGhlIGRpbWVuc2lvbiBvZiBhIG1hdHJpeCBvciBkYXRhIGZyYW1lIHdpdGggdGhlICoqZGltKiooKSBmdW5jdGlvbjogZGltKGRhdGEpLiAoTmIgcm93cyBhbmQgbmIgY29sdW1ucykuIEVhY2ggZGltZW5zaW9uIGNhbiBiZSBvYnRhaW5lZCBzZXBhcmF0ZWx5IHdpdGggKipucm93KiooKSBhbmQgKipuY29sKiooKQpGb3IgdmVjdG9ycywgdGhlIG51bWJlciBvZiBlbGVtZW50cyBjYW4gYmUgZm91bmQgd2l0aCB0aGUgKipsZW5ndGgqKigpIGZ1bmN0aW9uLgoKYGBge3IgZGltfQpkaW0oZGF0YSkgCm5yb3coZGF0YSkgICAjIE51bWJlciBvZiByb3dzCm5jb2woZGF0YSkgICAjIE51bWJlciBvZiBjb2x1bW5zCmxlbmd0aCgzOjM1KSAjIExlbmd0aCBvZiBhIHZlY3RvcgpgYGAKCiMjIyBCb29sZWFuIChUUlVFL0ZBTFNFKSBkYXRhCkluIFIsIGl0IGlzIHVzZWZ1bHRvIHBlcmZvcm0gdGVzdHMuIEZvciBpbnN0YW5jZSwgZ2l2ZW4gdGhlIHNlcXVlbmNlIDE6MTIsIHdlIHdhbnQgdG8ga25vdyB3aGljaCB2YWx1ZXMgYXJlIHN0cmljdGx5IGdyZWF0ZXIgdGhhbiA2LiBUaGUgc2ltcGxlIGNvbW1hbmQgMToxMj42IHdpbGwgcHJvdmlkZSB0aGUgYW5zd2VyOiB0aGUgc3RhdGVtZW50IGlzIGZhbHNlIGZvciB0aGUgZmlyc3Qgc2l4IGVsZW1lbnRzICgxIHRvIDYpIGFuZCB0cnVlIGZvciB0aGUgbGFzdCBzaXggKDcgdG8gMTIpLgpgYGB7ciBCb29sfQooMToxMikgPiA2CmBgYAoKCgojIyAzIC0gSEFORExJTkcgREFUQSBJTiBQVVJFIFIKIyMjIEV4dHJhY3RpbmcgZGF0YQpBY2Nlc3NpbmcgdGhlIHZhbHVlcyBvZiBhIHZhcmlhYmxlIGNhbiBiZSBkb25lIHdpdGggdGhlIHNxdWFyZSBicmFja2V0cyBbIF0gdGhhbmtzIHRvIGluZGV4aW5nLiAKRm9yIGluc3RhbmNlLCB0aGUgdmFsdWUgaW4gdGhlIHRoaXJkIHJvdyBhbmQgc2Vjb25kIGNvbHVtbiBvZiBkYXRhIGlzIGRhdGFbMywyXS4gIApXaGVuIGNvbHVtbnMgaGF2ZSBuYW1lcywgaXQgaXMgcG9zc2libGUgdG8gdXNlIGl0IHRvIGlzb2xhdGUgYSBwYXJ0aWN1bGFyIGNvbHVtbiB3aXRoIHRoZSBkb2xsYXIgKCAqKiQqKiApIG9wZXJhdG9yOiAKCmBgYHtyIGRvbGxhcn0KZGF0YSRBZ2UgIyBUaGUgQWdlIGNvbHVtbiBvZiBkYXRhCmBgYApBbm90aGVyIHdheSB0byBwcm9jZWVkIGlzIHRvIG9taXQgdG8gc3BlY2lmeSB0aGUgcm93IG51bWJlcnM6IHNpbmNlIEhlaWdodCBpcyB0aGUgdGhpcmQgY29sdW1uIG9mIGRhdGEsIHRoZW4gdGhlIHJlc3VsdCBpcyB0aGUgc2FtZSB3aXRoIGRhdGFbLDNdLiBUaGlzIGdpdmUgeW91IGFsbCBvZiB0aGUgdGhpcmQgY29sdW1uLiBMaWtld2lzZSwgZGF0YVszLF0gd2lsbCByZXR1cm4gYWxsIG9mIHRoZSB0aGlyZCByb3cuCgpgYGB7ciBjb2x9CmRhdGFbLDNdICMgVGhpcmQgY29sdW1uCmRhdGFbMyxdICMgVGhpcmQgcm93CmBgYAoKWW91IGNhbiBleHRyYWN0IGRhdGEgd2l0aCBib29sZWFuIHZlY3RvcnMhIEZvciBpbnN0YW5jZSwgaWYgd2Ugd2FudCB0byBzZWxlY3QgdGhlIHBlb3BsZSB3aG8gYXJlIG9sZGVyIHRoYW4gNDIgeWVhcnMgb2xkOiBzaW1wbGUhIApgYGB7ciBCb29sMn0KZGF0YSRBZ2U+NDIKYGBgCgp3aWxsIHByb3ZpZGUgdGhlIGNvcnJlc3BvbmRpbmcgaW5kaWNlcy4gVG8gZXh0cmFjdCB0aGUgZGF0YSwgeW91IGp1c3QgbmVlZCB0byBzZWxlY3QgdGhlIHJpZ2h0IHJvd3MgYW5kIGFsbCBjb2x1bW5zOiAKYGBge3IgQm9vbDN9CmRhdGFbZGF0YSRBZ2UgPiA0MiwgXSAgICAgICAgICAgICAgICAgICAjIE1pbmQgdGhlIGNvbW1hOiB3ZSBrZWVwIGFsbCBjb2x1bW5zIQpkYXRhW2RhdGEkQWdlID4gNDIgJiBkYXRhJFdlaWdodCA+IDcwLCBdIyBUaGUgJiBvcGVyYXRvciBhbGxvd3MgdG8gYWRkIHNvcnRpbmcgY3JpdGVyaWEKZGF0YSAlPiUgZmlsdGVyKEFnZSA+IDQyKSAgICAgICAgICAgICAgICMgdGlkeXZlcnNlIHdheSEgU2ltcGxlci4KYGBgCk9ubHkgdGhlIFRSVUUgcm93cyBhcmUga2VwdC4gQXMgd2Ugc2VlLCB0aGUgKipmaWx0ZXIqKigpIGZ1bmN0aW9uIG9mIHRoZSB0aWR5dmVyc2UgZG9lcyBqdXN0IHRoYXQuCgojIyMgV3JpdGluZyAvIFJlcGxhY2luZyB2YWx1ZXMgCldyaXRpbmcgb24gZGF0YSBmcmFtZXMsIHZlY3RvcnMsIG9yIG1hdHJpY2VzIGNhbiBiZSBkb25lIHdpdGggdGhlIGFycm93IG9wZXJhdG9yOgpgYGB7ciB3cml0ZSB9CmRhdGFbMywyXSA8LSA5OQpkYXRhW2MoNyw5KSwzXSA8LSAxNjYgICAgICAgICMgUmVwbGFjZSAyIGNlbGxzIGF0IGEgdGltZSEgU2V2ZW50aCBhbmQgbmludGggcm93IG9uIHRoZSB0aGlyZCBjb2x1bW4uCmRhdGFbYyg2LDgpLDNdIDwtIGMoMTk5LDE3NikgIyBTYW1lLCBidXQgd2l0aCAyIGRpZmZlcmVudCB2YWx1ZXMuCmRhdGEgICAgICAgICAgICAgICAgICAgICAgICAgIyBDSEVDSyB3aGVyZSB0aGUgbmV3IHZhbHVlcyBhcmUhCmBgYAoKIyMjIFNlZWluZyBkYXRhClVubGlrZSBpbiBFeGNlbCwgdGhlIGRhdGEgaXMgbm90IGRpcmVjdGx5IHNob3duIGluIFIuIFlvdSBoYXZlIHRvIGFzayBmb3IgaXQhIFRvIHNlZSB0aGUgY29udGVudCBvZiBhIHZhcmlhYmxlLCB5b3UgaGF2ZSB0byB0eXBlIGl0cyBuYW1lIGFuZCBwcmVzcyBFTlRFUi4gIFNvbWV0aW1lcywgdGhlIGNvbnRlbnQgb2YgdGhlIHZhcmlhYmxlIGlzIGh1Z2UgYW5kIGNhbm5vdCBwcm9wZXJseSBiZSBkaXNwbGF5ZWQuIFlvdSBjYW4gYWxzbyBkb3VibGUtY2xpY2sgb24gdGhlIHZhcmlhYmxlIG5hbWUgaW4gdGhlIGVudmlyb25tZW50IHBhbmUuICAKVGhlICoqaGVhZCoqKCkgZnVuY3Rpb24gc2hvd3MgdGhlIGZpcnN0IDYgbGluZXMgYW5kIHRoZSAqKnRhaWwqKigpIGZ1bmN0aW9uIHNob3dzIHRoZSBsYXN0IDYgbGluZXMuIApgYGB7ciBzZWV9CmhlYWQoZGF0YSwgOCkgIyBGaXJzdCA4IHJvd3MgKGRlZmF1bHQgbnVtYmVyIG9mIHJvd3MgaXMgNikKdGFpbChkYXRhKSAgICAjIExhc3QgNiByb3dzCmBgYAoKSWYgeW91IHdhbnQgdG8gc2VlIHRoZSBkaWZmZXJlbnQgcG9zc2libGUgdmFsdWVzIG9mIGEgZmFjdG9yIChjYXRlZ29yaWNhbCB2YXJpYWJsZXMpLCB5b3UgY2FuIHVzZSB0aGUgKipsZXZlbHMqKigpIGZ1bmN0aW9uLgpgYGB7ciBsZXZlbHN9CmxldmVscyhkaWFtb25kcyRjbGFyaXR5KQpgYGAKVGhvdWdoIGhvbmVzdGx5LCB5b3Ugd291bGQgZ2V0IHRoZSBpbmZvcm1hdGlvbiB1c2luZyB0aGUgKipzdW1tYXJ5KiooKSBmdW5jdGlvbiBhcyB3ZWxsIC0gYmVjYXVzZSB0aGVyZSBhcmVuJ3QgdG9vIG1hbnkgb2YgdGhlbS4KCgojIyA0IC0gTE9PUFM6IDFzdCBzdGVwIHRvd2FyZHMgcHJvZ3JhbW1pbmcKVGhlcmUgYXJlIHVzdWFsbHkgc2V2ZXJhbCB0eXBlcyBvZiBsb29wcywgYnV0IHdlIHdpbGwgZm9jdXMgb24gdGhlICoqZm9yKiogbG9vcC4KSXRzIHN0cnVjdHVyZSBpcyBzaW1wbGU6IHRoZSBpZGVhIGlzIHRvIHJlcGVhdCBhIHRhc2sgYSBmaW5pdGUgbnVtYmVyIG9mIHRpbWVzLiBUaGlzIGFsbG93cyB0byBhdXRvbWF0ZSB0aGUgY2hhbmdlcyBpbiBhIHZhcmlhYmxlLiBGb3IgaW5zdGFuY2UsIHRoZSBGaWJvbmFjY2kgc2VxdWVuY2U6CmBgYHtyIGxvb3B9Cm5iIDwtIDIwICAgICMgTnVtYmVyIG9mIGRlc2lyZWQgbnVtYmVycwpGaWIgPC0gMSAgICAjIEZpcnN0IHZhbHVlCkZpYlsyXSA8LSAxICMgU2Vjb25kIHZhbHVlCmZvcihrIGluIDM6bmIpewogICBGaWJba10gPC0gRmliW2stMV0gKyBGaWJbay0yXSAjIE5ldyB2YWx1ZSBlcXVhbHMgdGhlIHN1bSBvZiB0aGUgMiBwcmV2aW91cyBvbmVzCn0KRmliICMgU2hvdyB0aGUgc2VxdWVuY2UKYGBgCgoqKkdPSU5HIEZVUlRIRVIqKjogbG9vcHMgY2FuIHRha2UgdGltZS4gVG8gc3BlZWQgdGhpbmdzIHVwLCBoYXZlIGEgbG9vayBhdCB0aGUgKiptYXAqKigpIGZhbWlseSBvZiBmdW5jdGlvbnMuIFRoZSAqKmFwcGx5KiogZnVuY3Rpb25zIGFyZSBhbHNvIGNvb2wgKHNlZSBiZWxvdykuCgojIyA1IC0gTUlTQy4gRlVOQ1RJT05TCiMjIyBTdGF0aXN0aWNzCkJlbG93LCB3ZSBwcmVzZW50IGEgZmV3IHVzZWZ1bCBmdW5jdGlvbnMuCmBgYHtyIHN0YXR9CnNxcnQoNSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU3F1YXJlIHJvb3QKbWVhbihjKDI6NiwgODo0MykpICAgICAgICAgICAgICAgICAgIyBBdmVyYWdlIHZhbHVlCnZhcigxOjM0KSAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVmFyaWFuY2UKc2QoYygyOjYsIDg6NDMpKSAgICAgICAgICAgICAgICAgICAgIyBTdGFuZGFyZCBkZXZpYXRpb24sIHVzZSB2YXIoKSBmb3IgdGhlIHZhcmlhbmNlCnYgPC0gcm5vcm0oMTgpICAgICAgICAgICAgICAgICAgICAgICMgV2UgZ2VuZXJhdGUgYSByYW5kb20gdmVjdG9yCm1pbih2KSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWluaW11bQptYXgodikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIE1heGltdW0KdiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBBIGxvb2sgYXQgdGhlIHdob2xlIHZlY3Rvcgp3aGljaCh2ID09IG1heCh2KSkgICAgICAgICAgICAgICAgICAKIyB3aGljaCgpIHJldHVybnMgdGhlIGluZGV4IG9mIHRoZSBpdGVtcyB0aGF0IGFyZSBUUlVFIGluc2lkZSBhIEJvb2xlYW4gZXhwcmVzc2lvbgpgYGAKCiMjIyBDaGFuZ2luZyBtb2RlcwpVc3VhbCBtb2RlcyBmb3IgdmFyaWFibGVzIGFyZTogIAotIGxvZ2ljYWwgKEJvb2xlYW4sIFRSVUUgb3IgRkFMRSksICoqTk9URSoqOiB0aGV5IGFyZSBhbHNvIGNvbnZlcnRlZCBpbnRvIG51bWJlcjogRkFMU0UgPSAwLCBUUlVFID0gMSAgIAotIG51bWVyaWMgKG51bWJlcnMpLCAgCi0gY2hhcmFjdGVyICh0ZXh0KSwgIAotIGZhY3RvciAodW5vcmRlcmVkIGNhdGVnb3J5KSBhbmQgIAotIG9yZGVyZWQgZmFjdG9yIChvcmRlcmVkIGNhdGVnb3J5KSAgCkl0IGlzIHNvbWV0aW1lcyBwb3NzaWJsZSB0byBzd2l0Y2ggZnJvbSBvbmUgdG8gYW5vdGhlci4gT25lIGNvdW50ZXItZXhhbXBsZSBpczogdHJhbnNsYXRpbmcgYSBjaGFyYXRlciBpbnRvIGEgbnVtYmVyLiBGb3IgYSBmYWN0b3IsIHRoZSBsZXZlbHMgaXMgdGhlIHZhbHVlcyB0aGF0IGNhbiBiZSB0YWtlbiBieSB0aGUgdmFyaWFibGUuIFNlZSAqKmxldmVscyoqKCkuClNvbWUgZXhhbXBsZXMgYmVsb3cuCgpgYGB7ciBtb2Rlc30KYygiMyIsICI4IiwgIjciKSAgICAgICAgICAgICAgICAgICAgICAgICMgTnVtYmVycyB2aWV3ZWQgYXMgdGV4dCAKYygiMyIsICI4IiwgIjciKSAlPiUgYXMubnVtZXJpYygpICAgICAgICMgQ2hhbmdlIHRoZSBhYm92ZSBpbnRvICp0cnVlKiBudW1iZXJzCmMoMyw0LDYpICU+JSBhcy5jaGFyYWN0ZXIoKSAgICAgICAgICAgICAjIFRoZSBvcHBvc2l0ZTogY2hhbmdlIGZpZWxkcyBpbnRvIGNoYXJhY3RlcnMKZGF0YSRBZ2UgJT4lIGFzLmZhY3RvcigpICU+JSBzdW1tYXJ5KCkgIAojIGFzLmZhY3RvcigpIHRyYW5zZm9ybXMgdGhlIGZpZWxkcyBpbnRvIGNhdGFnb3JpZXMsIHRoZSBmaW5hbCBzdGVwIGNvbXB1dGVzIHRoZSBudW1iZXIgb2YgZWxlbWVudHMgaW4gZWFjaCBjYXRlcmdvcnkKZmFjIDwtIHJlcChjKDEsMiwzKSwyKSAlPiUgYXMuZmFjdG9yKCkgICMgV2UgY3JlYXRlIGEgc2ltcGxlIGZhY3RvciBhbmQgY2hhbmdlIGl0cyB2YWx1ZXMgYmVsb3cKZmFjIDwtIGZhYyAlPiUgcmVjb2RlX2ZhY3RvcihgMWAgPSAiTG93IiwgYDJgPSAiTWVkaXVtIiwgYDNgPSAiSGlnaCIsIC5vcmRlcmVkID0gVFJVRSkKZmFjCmBgYAoKKipHT0lORyBGVVJUSEVSKio6ICAKLSAqKmxpc3RzKiogaW4gUiBhcmUgdmVyeSBmbGV4aWJsZSBzdHJ1Y3R1cmVzIHRoYXQgY2FuIGVtYmVkIGRpZmZlcmVudCB0eXBlcyBvZiBtb2Rlcy4gCi0gKiphcnJheXMqKiBhcmUgbGlrZSBtYXRyaWNlcywgYnV0IGFyZSBhbGxvd2VkIHRvIGhhdmUgaGlnaGVyIGRpbWVuc2lvbnMgKD4yKS4KLSAqKnRpYmJsZXMqKiBhcmUgdGhlIGRhdGFmcmFtZXMgMi4wIHN0ZW1taW5nIGZyb20gdGhlIHRpZHl2ZXJzZS4KCgojIyMgVGhlIGFwcGx5KCkgZnVuY3Rpb24KV2hlbiBnaXZlbiBhIHJlY3Rhbmd1bGFyIGRhdGFzZXQgd2l0aCBudW1iZXJzIG9ubHksIGl0IGlzIG9mdGVuIHVzZWZ1bCB0byAqKmFwcGx5KiogdGhlIHNhbWUgZnVuY3Rpb24gdG8gYWxsIHJvd3Mgb3IgYWxsIGNvbHVtbnMuIE5vcm1hbGx5LCB0aGlzIHdvdWxkIHJlcXVpcmUgYSAqKmZvcioqIGxvb3AuIEZvcnR1bmF0ZWx5LCBhbHRlcm5hdGl2ZSBzb2x1dGlvbnMgZXhpc3QuIEZvciBzaW1wbGUgZnVuY3Rpb25zLCBsaWtlIHN1bXMgYW5kIG1lYW5zLCBkZWRpY2F0ZWQgZnVuY3Rpb25zIGhhdmUgYmVlbiBjcmVhdGVkLgoKYGBge3Igcm93cyZjb2x1bW5zfQpkYXRhX251bSA8LSBzZWxlY3QoZGF0YSwgLUdlbmRlcikgICMgVGFrZSBvdXQgZ2VuZGVyIGJlY2F1c2UgaXQncyBub3QgYSBudW1iZXIKY29sTWVhbnMoZGF0YV9udW0pICAgICAgICAgICAgICAgICAjIE1lYW4gb2YgYWxsIGNvbHVtbnMKcm93U3VtcyhkYXRhX251bSkgICAgICAgICAgICAgICAgICAjIFN1bSBvZiBhbGwgcm93czogbm90IHZlcnkgbWVhbmluZ2Z1bApgYGAKCkZvciBtb3JlIGdlbmVyYWwgZnVuY3Rpb25zLCAqKmFwcGx5KiooKSBpcyB0aGUgd2F5IHRvIGdvLiBZb3UgbmVlZCB0byBzcGVjaWZ5IHRoZSBkaW1lbnNpb24gYWNyb3NzIHdoaWNoIHRoZSBmdW5jdGlvbiBpcyBjb21wdXRlZC4KCmBgYHtyIGFwcGx5fQphcHBseShkYXRhX251bSwgMiwgbWVhbikgIyBUaGUgc3ludGF4IGlzOiBhcHBseShkYXRhLCBkaW1lbnNpb24sIGZ1bmN0aW9uKSwgZGltZW5zaW9uIDIgPSBjb2x1bW4KYXBwbHkoZGF0YV9udW0sIDEsIHN1bSkgICMgRGltZW5zaW9uIDEgPSByb3cKYXBwbHkoZGF0YV9udW0sIDIsIHNkKSAgICMgQ29tcHV0aW5nIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgZWFjaCBjb2x1bW4KYGBgCk5vdGUgdGhhdCB5b3UgY2FuIHVzZSAqKmFwcGx5KiooKSBvbiBhcnJheXMgd2l0aCBtb3JlIHRoYW4gdHdvIGRpbWVuc2lvbnMuCgojIyMgVXNlci1zcGVjaWZpZWQgZnVuY3Rpb25zClIgbGV0J3MgeW91IGNyZWF0ZSB5b3VyIG93biBmdW5jdGlvbnMhIEJlbG93LCB3ZSBjcmVhdGUgJGYoeCk9KDEreF4yKV57LTF9JCBhbmQgcGxvdCBpdC4KYGBge3IgZnVufQpkZW5zID0gZnVuY3Rpb24oeCl7CiAgcmV0dXJuKDEvKDEreF4yKSkKfQpkZW5zKDE6MykgICMgVGVzdCB2YWx1ZXMKZ2dwbG90KGRhdGEuZnJhbWUoeCA9IGMoLTQsNCkpLCBhZXMoeCA9IHgpKSArIHN0YXRfZnVuY3Rpb24oZnVuID0gZGVucykgIyBQbG90CmBgYAoKIyMjIE1pc3NpbmcgZGF0YQpUaGlzIGlzIGEgY29tbW9uIHByb2JsZW0gaW4gZGF0YSBzY2llbmNlLiBIZXJlLCB3ZSBvbmx5IGFpbSB0byBsb2NhdGUgbWlzc2luZyBkYXRhIC0gdXNpbmcgdGhlICoqaXMubmEqKigpIGZ1bmN0aW9uLiBEZWFsaW5nIHdpdGggYWJzZW50IHZhbHVlcyBpcyBvdXQgb2Ygb3VyIHNjb3BlLgpgYGB7ciBtaXNzaW5nIGRhdGF9CmRhdGFbMyw0XSA8LSBOQSAgICAgICAgICMgTkEgaXMgb2Z0ZW4gdGhlIGRlZmF1bHQgZm9yIG1pc3NpbmcgZGF0YSBwb2ludHMKaXMubmEoZGF0YSkgICAgICAgICAgICAgIyBUaGlzIGlzIHRoZSBicnV0ZS1mb3JjZSBtZXRob2QuIE9rIGZvciBzbWFsbCBkYXRhc2V0cwppcy5uYShkYXRhKSAlPiUgcm93U3VtcyAjIExvY2F0aW5nIHJvd3MKaXMubmEoZGF0YSkgJT4lIGNvbFN1bXMgIyBMb2NhdGluZyBjb2x1bW5zCmBgYAoKVGhlIGlzLm5hIGNvbWJpbmVkIHdpdGggcm93IGFuZCBjb2x1bW4gc3VtcyBsb2NhdGVzIHRoZSBtaXNzaW5nIGRhdGEgdmVyeSBzaW1wbHkuCgojIyBFeGVyY2lzZXMKCiMjIyBIYW5kbGluZyB2ZWN0b3JzICYgbWF0cmljZXMKMSkgU2V0IHlvdXIgd29ya2luZyBkaXJlY3RvcnkgYW5kIGxvYWQgdGhlIHRpZHl2ZXJzZSBwYWNrYWdlCjIpIFdoYXQgYXJlICoqdHdvKiogc2ltcGxlIHdheXMgdG8gY3JlYXRlIHRoZSBzZXJpZXMgezAuMSwgMC4yLCAwLjMsIC4uLiwgOS45LCAxMH0/CjMpIFNhbWUgcXVlc3Rpb24gZm9yIHsyLDQsNiwgLi4uLCA5NiwgOTgsIDEwMH0uICAKNCkgV2hhdCBpcyB0aGUgc2ltcGxlc3Qgd2F5IHRvIGJ1aWxkIGEgMTAtYnktMTAgbWF0cml4IGNvcnJlc3BvbmRpbmcgdG8gdGhlIGNsYXNzaWNhbCBtdWx0aXBsaWNhdGlvbiB0YWJsZT8gaS5lLiBzdWNoIHRoYXQgb24gdGhlIHNpeHRoIGNvbHVtbiBhbmQgc2V2ZW50aCByb3cgKG9yIHZpY2UtdmVyc2EpLCB5b3UgZ2V0IDQyLiBSZXBsYWNlIGl0cyBmaXJzdCB2YWx1ZSBieSB0aGUgc3RyaW5nICJPbmUiIGFuZCBzZWUgd2hhdCBoYXBwZW5zLgo1KSBGaWxsIGluIGEgMTAtYnktMTAgbWF0cml4IHdpdGggcmFuZG9tIG51bWJlcnMgYW5kIGNvbXB1dGUgaXRzIGNvbHVtbiBzdW1zCgpgYGB7ciB5b3VyIHR1cm4hfQoKYGBgCgoKCiMjIyBGaWx0ZXJpbmcgaW4gYmFzZSBSIGlzIHRlZGlvdXMhCjEpIExvYWQgdGhlIEFORVMgZGF0YWJhc2UgKCJhbmVzLlJEYXRhIikgCjIpIFVzaW5nIG9ubHkgYmFzZSBSIGNvbW1hbmRzIChhbmQgbm90IHRoZSBmaWx0ZXIoKSBmdW5jdGlvbiksIGZpbmQgdGhlIG1lbiBpbiB0aGUgZGF0YXNldCBhZ2VkIDg3IHllYXJzIG9sZC4KMykgQWdhaW4gd2l0aG91dCByZXNvcnRpbmcgdG8gZmlsdGVyKCksIGRldGVybWluZSB0aGUgcGFydHkgYWZmaWxpYXRpb24gb2YgdGhlIHBlb3BsZSBvbGRlciB0aGFuIDcwIHdobyBoYXZlIHRocmVlIG9yIG1vcmUgY2hpbGRyZW4KNCkgTGlrZXdpc2UsIGNvbXB1dGUgdGhlIHByb3BvcnRpb24gb2YgcGFydHkgYWZmaWxpYXRpb24gb2YgcmVzcG9uZGVudHMgeW91bmdlciB0aGFuIDI1OyBzYW1lIHF1ZXN0aW9uIGZvciB0aG9zZSBvbGRlciB0aGFuIDYwLiBBbnkgY29tbWVudHM/CgoKIyMjIFRoZSBpbXBvcnRhbmNlIG9mIG1vZGVzCjEpIExvYWQgdGhlIEFORVMgZGF0YWJhc2UgaW4gRXhjZWwgZm9ybWF0ICgiYW5lcy54bHN4IikgLSBtYWtlIHN1cmUgeW91IGhhdmUgdGhlIGZpbGUgYXQgdGhlIHJpZ2h0IHBsYWNlIGJlZm9yZSEKMikgTG9vayBhdCB0aGUgc3VtbWFyeTogd2hhdCBoYXBwZW5lZD8gICAKMykgQ2hhbmdlIHRoZSBtb2RlcyBpbnRvIGZhY3RvcnMsIGFuZCwgcG9zc2libHksIG9yZGVyZWQgZmFjdG9ycyB3aGVuIG5lZWQgYmUuICAKKipDT05DTFVTSU9OKio6IGRhdGEgcHJlcGFyYXRpb24gY2FuIGJlIHRpbWUtY29uc3VtaW5nLiBCdXQgaXQgaXMgaW1wZXJhdGl2ZS4gCgoKCiMjIyBGdW5jdGlvbnMKVGhlIHB1cnBvc2Ugb2YgdGhlIHF1ZXN0aW9ucyBiZWxvdyBpcyB0byBtYW5pcHVsYXRlIGZ1bmN0aW9ucyB3aXRoIGludGVncmF0ZWQgbG9vcHMuICAKMSkgQ3JlYXRlIGEgZnVuY3Rpb24gd2l0aCBvbmUgYXJndW1lbnQsIG4sIHRoYXQgcmV0dXJucyB0aGUgdmFsdWVzIG9mIGpeaiBmb3IgaiA9IDEuLi5uLiBUZXN0IGl0IG9uIG4gPSAxMCBhbmQgY2hlY2shIEFueXdheSB3ZSBjb3VsZCB0byB0aGlzIGZhc3Rlcj8KCgoK