Introduction

In this session, you will learn the basic R syntax, including:

  1. General syntax
  2. basic operations
  3. Object & data types
  4. Flow controls
  5. Introduction to the tidyverse and piping.

Brief Introduction: Base-R

The general base-R syntax is, in contrast to other more general-purpose programming language, already very geared towards working with data. MAny slicing/dicing ect. functionalities which for instance in Python require additional libraries (eg. Pandas in Python) are in R (as a dedicated statistical pogramming language) come with R out-of-the-box. However, the traditional R syntax has during the last years been mainly replaced by the tidy-principles pushed by R.Studio and implemented in their ´tidyverse´ ecosystem.

We will not use too much base-R syntax in our time together. However, it is still important to know the basics, since there are always here and there situation where you just cannot avoid it

Basics

Assignments

You can assign a value to an object using assign(), <-, or =.

z <- x + 17*y  # Assignment
z   
[1] 71

Value comparisons

Comparisons return boolean values: TRUE or FALSE (often abbreviated to T and F)

x <= y 
[1] TRUE

Special constants: NA, NULL, Inf, -Inf, NaN

NA indicates missing or undefined data

mean(c(1, 2, NA, 4, 5), na.rm = TRUE)
[1] 3

NULL indicates an empty object, e.g. a null/empty list

10 + NULL     # use returns an empty object (length zero)
numeric(0)
is.null(NULL) # check if NULL
[1] TRUE

Inf and -Inf represent positive and negative infinity. They can be returned by mathematical operations like division of a number by zero.

5/0
[1] Inf
is.finite(5/0) # Check if a number is finite
[1] FALSE
is.infinite(5/0) # Check if a number is infinite
[1] TRUE

NaN (Not a Number) - the result of an operation that cannot be reasonably defined

is.nan(0/0)
[1] TRUE

Object classes

Vectors

v1 <- c(1, 5, 11, 33)       # Numeric vector, length 4
v1
[1]  1  5 11 33
v2 <- c("hello","world")    # Character vector, length 2 (a vector of strings)
v2
[1] "hello" "world"
v3 <- c(TRUE, TRUE, FALSE)  # Logical vector, same as c(T, T, F)
v3
[1]  TRUE  TRUE FALSE

Combining different types of elements in one vector will coerce the elements to the least restrictive type:

v4 <- c(v1,v2,v3,"boo")     # All elements turn into strings
v4
 [1] "1"     "5"     "11"    "33"    "hello" "world" "TRUE"  "TRUE"  "FALSE" "boo"  

Element-wise operations:

v1 + c(1,7) 
[1]  2 12 12 40

Mathematical operations:

cor(v1,v1*5) 
[1] 1

Logical operations:

v1 > 2       # Each element is compared to 2, returns logical vector
[1] FALSE  TRUE  TRUE  TRUE
v1==v2       # Are corresponding elements equivalent, returns logical vector.
[1] FALSE FALSE FALSE FALSE
v1!=v2       # Are corresponding elements *not* equivalent? Same as !(v1==v2)
[1] TRUE TRUE TRUE TRUE
(v1>2) | (v2>0)   # | is the boolean OR, returns a vector.
[1] TRUE TRUE TRUE TRUE
(v1>2) & (v2>0)   # & is the boolean AND, returns a vector.
[1] FALSE  TRUE  TRUE  TRUE
(v1>2) || (v2>0)  # || is the boolean OR, returns a single value (if it is true at least once)
[1] TRUE
(v1>2) && (v2>0)  # && is the boolean AND, returns a single value (if it is true at least once)
[1] FALSE

Adressing vector elements:

v1[v1>3] 
[1]  5 11 33

NOTE: If you are used to languages indexing from 0 (eg. Python), R will surprise you by indexing from 1.

To add more elements to a vector, simply assign them values.

v1[6:10] <- 6:10
v1
 [1]  1  5 11 33 NA  6  7  8  9 10

We can also directly assign the vector a length:

length(v1) <- 15 # the last 5 elements are added as missing data: NA
v1
 [1]  1  5 11 33 NA  6  7  8  9 10 NA NA NA NA NA

Factors

Factors are used to store categorical data.

eye.col.v <- c("brown", "green", "brown", "blue", "blue", "blue")         #vector
eye.col.f <- factor(c("brown", "green", "brown", "blue", "blue", "blue")) #factor
eye.col.v
[1] "brown" "green" "brown" "blue"  "blue"  "blue" 
eye.col.f
[1] brown green brown blue  blue  blue 
Levels: blue brown green

R will identify the different levels of the factor - e.g. all distinct values. The data is stored internally as integers - each number corresponding to a factor level.

levels(eye.col.f)  # The levels (distinct values) of the factor (categorical variable)
[1] "blue"  "brown" "green"
as.numeric(eye.col.f)  # The factor as numeric values: 1 is  blue, 2 is brown, 3 is green
[1] 2 3 2 1 1 1
as.numeric(eye.col.v)  # The character vector, however, can not be coerced to numeric
[1] NA NA NA NA NA NA
as.character(eye.col.f)  
[1] "brown" "green" "brown" "blue"  "blue"  "blue" 
as.character(eye.col.v) 
[1] "brown" "green" "brown" "blue"  "blue"  "blue" 

Matrces & Arrays

A matrix is a vector with dimensions:

m
     [,1] [,2] [,3] [,4]
[1,]    1    1    1    1
[2,]    1    1    1    1
[3,]    1    1    1    1
[4,]    1    1    1    1
[5,]    1    1    1    1

Create a matrix using matrix():

m <- matrix(data=1, nrow=5, ncol=4)  # same matrix as above, 5x4, full of 1s
m <- matrix(1,5,4)                       # same matrix as above (lazy style)
dim(m)                                # What are the dimensions of m?
[1] 5 4
m
     [,1] [,2] [,3] [,4]
[1,]    1    1    1    1
[2,]    1    1    1    1
[3,]    1    1    1    1
[4,]    1    1    1    1
[5,]    1    1    1    1

Create a matrix by combining vectors:

m <- cbind(1:5, 5:1, 5:9)  # Bind 3 vectors as columns, 5x3 matrix
m <- rbind(1:5, 5:1, 5:9)  # Bind 3 vectors as rows, 3x5 matrix
m <- matrix(1:10,10,10)
m
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    1    1    1    1    1    1    1    1    1     1
 [2,]    2    2    2    2    2    2    2    2    2     2
 [3,]    3    3    3    3    3    3    3    3    3     3
 [4,]    4    4    4    4    4    4    4    4    4     4
 [5,]    5    5    5    5    5    5    5    5    5     5
 [6,]    6    6    6    6    6    6    6    6    6     6
 [7,]    7    7    7    7    7    7    7    7    7     7
 [8,]    8    8    8    8    8    8    8    8    8     8
 [9,]    9    9    9    9    9    9    9    9    9     9
[10,]   10   10   10   10   10   10   10   10   10    10

Select matrix elements:

m[-1,]     # all rows *except* the first one
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    2    2    2    2    2    2    2    2    2     2
 [2,]    3    3    3    3    3    3    3    3    3     3
 [3,]    4    4    4    4    4    4    4    4    4     4
 [4,]    5    5    5    5    5    5    5    5    5     5
 [5,]    6    6    6    6    6    6    6    6    6     6
 [6,]    7    7    7    7    7    7    7    7    7     7
 [7,]    8    8    8    8    8    8    8    8    8     8
 [8,]    9    9    9    9    9    9    9    9    9     9
 [9,]   10   10   10   10   10   10   10   10   10    10

Conditional operations

m[m > 3] 
 [1]  4  5  6  7  8  9 10  4  5  6  7  8  9 10  4  5  6  7  8  9 10  4  5  6  7  8  9 10  4  5  6  7  8  9 10  4  5
[38]  6  7  8  9 10  4  5  6  7  8  9 10  4  5  6  7  8  9 10  4  5  6  7  8  9 10  4  5  6  7  8  9 10

Other matrix manipulation

m * m  
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    1    1    1    1    1    1    1    1    1     1
 [2,]    4    4    4    4    4    4    4    4    4     4
 [3,]    9    9    9    9    9    9    9    9    9     9
 [4,]   16   16   16   16   16   16   16   16   16    16
 [5,]   25   25   25   25   25   25   25   25   25    25
 [6,]   36   36   36   36   36   36   36   36   36    36
 [7,]   49   49   49   49   49   49   49   49   49    49
 [8,]   64   64   64   64   64   64   64   64   64    64
 [9,]   81   81   81   81   81   81   81   81   81    81
[10,]  100  100  100  100  100  100  100  100  100   100

Arrays: more than 2 dimensions

Created with the array() function:

a <- array(data=1:18,dim=c(3,3,2)) # 3d with dimensions 3x3x2
a <- array(1:18,c(3,3,2))          # the same array
a
, , 1

     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9

, , 2

     [,1] [,2] [,3]
[1,]   10   13   16
[2,]   11   14   17
[3,]   12   15   18

Since arrays have 3 dimensions, also a 3rd element can be used for slicing&dicinhg.

a[1,3,2]
[1] 16

Lists

Lists are collections of objects (e.g. of strings, vectors, matrices, other lists, etc.)

l1$boo 
 [1]  1  5 11 33 NA  6  7  8  9 10 NA NA NA NA NA

Add more elements to a list:

l3[[1]] <- 11 # add an element to the empty list l3
l4[[3]] <- c(22, 23) # add a vector as element 3 in the empty list l4. 
                     # Since we added element 3, elements 1 & 2 will be generated and empty (NULL)
l1[[5]] <- "More elements!" # The list l1 had 4 elements, we're adding a 5th here.
l1[[8]] <- 1:11 # We added an 8th element, but not 6th or 7th. Those will be created empty (NULL)
l1$Something <- "A thing"  # Adds a ninth element - "A thing", named "Something"

Data Frames

The data frame is a special kind of list used for storing dataset tables. Think of rows as cases, columns as variables. Each column is a vector or factor.

Note: While base R uses the data.frame, we later when working with tidyverse use the tibble instead, which is the same but modifies some annoying behaviors of the original data type (eg. no default interpretations of strings as factors, no rownames. More on that later).

Creating a dataframe:

dfr1$FirstName
[1] "Jesper"   "Jonas"    "Pernille" "Helle"   

Notice that R thinks this is a categorical variable and so it’s treating it like a factor, not a character vector. You can tell R you don’t like factors from the start using stringsAsFactors=FALSE. I find that annoying. The tibble (introduced later) does not do that.

dfr2 <- data.frame(FirstName=c("John","Jim","Jane","Jill"), stringsAsFactors=FALSE)
dfr2$FirstName   # Success: not a factor.
[1] "John" "Jim"  "Jane" "Jill"

Access elements of the data frame. Notation is dfr[row, column] Rows can be acessed by number or condition, columns by number or name. Alternatively, columns can be acessed by dfr$column

dfr1[1,]   # First row, all columns
dfr1[,1]   # First column, all rows
[1] 1 2 3 4
dfr1$Age   # Age column, all rows
[1] 22 33 44 55
dfr1[1:2,3:4] # Rows 1 and 2, columns 3 and 4 - the gender and age of John & Jim
dfr1[c(1,3),] # Rows 1 and 3, all columns

Find the names of everyone over the age of 30 in the data

dfr1[dfr1$Age>30,2]
[1] "Jonas"    "Pernille" "Helle"   

Find the average age of all females in the data:

mean (dfr1[dfr1$Female==TRUE,4])
[1] 49.5

Flow Control (loops & friends)

Loops are powerful little helpers to do the same operation iterating over a number of items.

If statements: if (condition) expr1 else expr2

x <- 5; y <- 10
if (x==0) y <- 0 else y <- y/x  
y
[1] 2

for loops: for (variable in sequence) expr

for (i in 1:x)  { print(paste("OMG, i just counted to", i)) }
[1] "OMG, i just counted to 1"
[1] "OMG, i just counted to 2"
[1] "OMG, i just counted to 3"
[1] "OMG, i just counted to 4"
[1] "OMG, i just counted to 5"

While loop: while (condintion) expr

while (x > 0) {print(x); x <- x-1;}
[1] 5
[1] 4
[1] 3
[1] 2
[1] 1

Repeat loop: repeat expr, use break to exit the loop

repeat { print(x); x <- x+1; if (x>7) break}
[1] 0
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7

R troubleshooting

While I generate many (and often very creative) errors in R, there are three simple things that will most often go wrong for me. Those include:

  • Capitalization. R is case sensitive - a graph vertex named “Jack” is not the same as one named “jack”. The function rowSums won’t work as “rowsums” or “RowSums”.
  • Object class. While many functions are willing to take anything you throw at them, some will still surprisingly require character vector or a factor instead of a numeric vector, or a matrix instead of a data frame. Functions will also occasionally return results in an unexpected format.
  • Package namespaces. Occasionally problems will arise when different packages contain functions with the same name. R may warn you about this by saying something like “The following object(s) are masked from ‘package:igraph’” as you load a package. One way to deal with this is to call functions from a package explicitly using ‘::’. For instance, if function ‘blah’ is present in packages A and B, you can call A::blah
# install.packages('dplyr')
# library(dplyr)          # load a package
# detach(package:dplyr)   # detach a package

For more advanced troubleshooting, check out try(), tryCatch(), and debug().

?tryCatch

Generally, just using ?functionyouwonderabout often solves problems. There you can review the functions arguments, inputs, outputs, syntax etc.

R 2.0: The Tidyverse

What is it all about?

Base R comes with quite some functionality for slicing and dicing data, there also exists a myriad specialized packages for more tricky data manipulation. To read others’ code and example as well as to perform some special operations, you all should be able to use standard R syntax.

However, the factors, the [row, column] syntax anhd so forth are not very comfortable and intuitive. Further, for more tricky operation such as certain aggregations etc., one has to rely on a variety of packages, which often come with an own syntax.

The good news is: The efforts of a small set of key-developers (foremost Hadley Wickham) has let to the development of the tidyverse, an ecosystem of R packages particularly designed for data science applications. All packages share an underlying design philosophy, common API, grammar, and data structures.

Among the most amazing contributions here is dplyr, a grammar of data manipulation, providing a consistent set of verbs that help you solve the most common data manipulation challenges. I use dplyr for 90% of my data-manipulation tasks for the following reasons:

  • All the underlying code is runs optimized in C++, making it faster than most base R
  • It consistently unifies the grammar of data manipulation to a small set of operations, which can be flexibly combined to master almost every task
  • It is designed to work neathly with the %>% pipe-operator of magrittr (more on that later)
  • its syntax is very similar to the logic of SQL and other data-management languages
  • It expanded far beoyond its original 5 verbs, and now replaces most base R commands with optimized, clever, and high-performance alternatives
  • It works neathly with many databases, such as SQL (with addon packages DBI and dbplyr)

I will not touch on all packages there, but the complete tidyverse covers almost all issues of data manipulation. They all operate under the same logic, are fast, and usually your best choice for almost any given problem. Particularly dplyr is enourmeously powerfull, and has a lot more functions than the basics I cover here. So, for every given problem, your first question (to yourself or stackoverflow) should be:

1: Is there a way to solve my problem in dplyr? 2: If not, is there another tidyverse package dedicated to this problem?

For the sake of illustration, I will load every package of the tidyverse one-by-one when we need it. However, normally I just load library(tidyverse) all at once, since I need a lot of these packages often anyhow

library(tidyverse) # Collection of all the good stuff like dplyr, ggplot2 ect.
library(magrittr) # For extra-piping operators (eg. %<>%)

Tibbles

Tibbles are the tidyverse version of the traditional dataframe. They work in exactly the same way, only with some small differences, which are usually from a data science perspctive seen as an improvement:

  1. Strings ae by default not recoded as factors
  2. Rownames are dropped
  3. Default print delivers more convenient overview.

They can be created in 3 different ways.

  1. Creating them from scratch with tibble()
  2. Using explicitly the as_tibble() function on a table
  3. When applying and dplyr function on a dataframe, it will automatically be converted to a tibble.
head(iris) # a dataframe
head(as_tibble(iris))

It is usually the prefered format for data science projects in R.

Piping

in traditional R syntax, data-manipulations are carried out one by one. For example, one would first assign a new variable x$numbers <- 1:5, then maybe manipulate it x$numbers <- x$numbers * 2, and subset it x <- x[x$numbers > 4]. dplyr makes use of margrittr’s pipes, written like %>%.

A pipe means take the output of it’s left-hand side and insert it as first input in the function on the right-hand side. Accordingly, all dplyr functions follow the syntax that their first input is always the data to be manipulated. Therefore, they can all be “piped”.

x <- tibble(numbers = 1:5) 

Lets say we want to multiply all number with 2, and THEN subset the data for observations with a number larger than 4. We could do the following

y <- x
y[,'numbers'] <- y[,'numbers'] * 2
y <- y[y['numbers'] > 4, ]
y

For example, we could pipe as follows (don’t worry about the other syntax yet):

x %>%
  mutate(numbers = numbers * 2) %>%
  filter(numbers > 4)

It basically reads like:

  • Create a dataframe (to be precise, a tibble) with the variable “numbers” and assign the values 1:5.
  • THEN multiply them with 2.
    THEN subset the dataframe to only rows with a nuimber value higher than 4.

It first looks not so intuitive, but it will become your second nature. Using pipes facilitates fast, reproducible and easily readable coding practices, and all of you are encouraged to go on with that.

Note: %>% pipes do not autometically assign their output to the left-hand side object, meaning the original dataset will not per se be overwritten. To do that, there are two ways:

1: Initially, assign the output to the original data with <- 2: Initially, use margrittr’s %<>% command, meaning: Assign and pipe.

# This will create an output, but not change x
x %>%
  filter(numbers > 5)

# This will re-assign x
x <- x %>%
  filter(numbers > 5)
# is equivalent to
x %<>%
  filter(numbers > 5) 

In conclusion: The pipe basically passes on dataframe between functions in the following way:

# Only pseudo code here, does not run
x %>% fun(na.rm = TRUE) %>%
  filter() %>%

# Is equivalent to
fun(x, na.rm = TRUE)

# While
x %<>% fun()
# Is equivalent to
x <- fun(x)

Piping also provides better overview over the flow of actions as compared to nested functions

# Nested functions
went_to_bed(had_dinner(programmed_some_r(had_lunch(programmed_some_r(had_brekfast(got_up(day)))))))

# vs pipes
day %>%
  got_up() %>%
  had_breakfast() %>%
  programmed_some_r() %>%
  had_lunch() %>%
  programmed_some_r() %>%
  had_dinner() %>%
  went_to_bed()

Handling special data formats

It is not part of this introductory lecture, but you soon might encounter that you have to deal with 2 common formats in some way, which are date-times (time-codes) and strings (text). When that point comes, just check the following to get started (and if necessary branch out to further sources suggested):

  • Strings: R for Data Science (Grolemund & Wickham) Chapter 14
  • DateTimes: R for Data Science (Grolemund & Wickham) Chapter 16

Adittional Infos

R, notebooks & markdown

While many people prefer to work with R scripts, computional notebooks are especially in the data science community more popular for R users. This is mainly don in the Rmarkdown format, which combines markdown markup an notation with executable code and result outputs. All the petty html notebooks i create fo you are also done in that way. For further information and to get started, check:

R, google colab & co.

Google colab does not officially support R kernels. However, there is a little trick how to make R run with colab.

If you want to start from scratch, do the following:

  • You can simply run the demo.ipynb from IRkernel Github
  • Make changes and then save a copy to your Google Drive.
  • You can also see all 3 example notebooks here.

If you already have an R-Markdown notebook:

  • use the IRkernel to create a .ipynb out of your .rmd
  • If you dont want to do it locally, use this colab notebook instead. Just upload the .rmd, run the code (alter the filename), and download the resulting .ipynb. This now can be uploaded to colab.

Endnotes

References

Further infos

Session Info

sessionInfo()
LS0tCnRpdGxlOiAnRGF0YSBTY2llbmNlIEJhc2ljczogUiBiYXNpY3MnCmF1dGhvcjogIkRhbmllbCBTLiBIYWluIChkc2hAYnVzaW5lc3MuYWF1LmRrKSIKZGF0ZTogIlVwZGF0ZWQgYHIgZm9ybWF0KFN5cy50aW1lKCksICclQiAlZCwgJVknKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBkZl9wcmludDogcGFnZWQKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIHRoZW1lOiBmbGF0bHkKICAgIAojIGtuaXQ6IG1hcmtkb3dudGVtcGxhdGVzOjp0b19qdXB5dGVyCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMjIyBHZW5lcmljIHByZWFtYmxlClN5cy5zZXRlbnYoTEFORyA9ICJlbiIpICMgRm9yIGVuZ2xpc2ggbGFuZ3VhZ2UKb3B0aW9ucyhzY2lwZW4gPSA1KSAjIFRvIGRlYWN0aXZhdGUgYW5ub3lpbmcgc2NpZW50aWZpYyBudW1iZXIgbm90YXRpb24KCiMjIyBLbml0ciBvcHRpb25zCmtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nPUZBTFNFLAogICAgICAgICAgICAgICAgICAgICBtZXNzYWdlPUZBTFNFLAogICAgICAgICAgICAgICAgICAgICBmaWcuYWxpZ249ImNlbnRlciIKICAgICAgICAgICAgICAgICAgICAgKQpgYGAKCiMgSW50cm9kdWN0aW9uCkluIHRoaXMgc2Vzc2lvbiwgeW91IHdpbGwgbGVhcm4gdGhlIGJhc2ljIFIgc3ludGF4LCBpbmNsdWRpbmc6CgoxLiBHZW5lcmFsIHN5bnRheAoyLiBiYXNpYyBvcGVyYXRpb25zCjMuIE9iamVjdCAmIGRhdGEgdHlwZXMKNC4gRmxvdyBjb250cm9scwo1LiBJbnRyb2R1Y3Rpb24gdG8gdGhlIGB0aWR5dmVyc2VgIGFuZCBwaXBpbmcuCgojIEJyaWVmIEludHJvZHVjdGlvbjogQmFzZS1SCgpUaGUgZ2VuZXJhbCBiYXNlLVIgc3ludGF4IGlzLCBpbiBjb250cmFzdCB0byBvdGhlciBtb3JlIGdlbmVyYWwtcHVycG9zZSBwcm9ncmFtbWluZyBsYW5ndWFnZSwgYWxyZWFkeSB2ZXJ5IGdlYXJlZCB0b3dhcmRzIHdvcmtpbmcgd2l0aCBkYXRhLiBNQW55IHNsaWNpbmcvZGljaW5nIGVjdC4gZnVuY3Rpb25hbGl0aWVzIHdoaWNoIGZvciBpbnN0YW5jZSBpbiBQeXRob24gcmVxdWlyZSBhZGRpdGlvbmFsIGxpYnJhcmllcyAoZWcuIFBhbmRhcyBpbiBQeXRob24pIGFyZSBpbiBSIChhcyBhIGRlZGljYXRlZCBzdGF0aXN0aWNhbCBwb2dyYW1taW5nIGxhbmd1YWdlKSBjb21lIHdpdGggUiBvdXQtb2YtdGhlLWJveC4gSG93ZXZlciwgdGhlIHRyYWRpdGlvbmFsIFIgc3ludGF4IGhhcyBkdXJpbmcgdGhlIGxhc3QgeWVhcnMgYmVlbiBtYWlubHkgcmVwbGFjZWQgYnkgdGhlIHRpZHktcHJpbmNpcGxlcyBwdXNoZWQgYnkgUi5TdHVkaW8gYW5kIGltcGxlbWVudGVkIGluIHRoZWlyIMK0dGlkeXZlcnNlwrQgZWNvc3lzdGVtLgoKV2Ugd2lsbCBub3QgdXNlIHRvbyBtdWNoIGJhc2UtUiBzeW50YXggaW4gb3VyIHRpbWUgdG9nZXRoZXIuIEhvd2V2ZXIsIGl0IGlzIHN0aWxsIGltcG9ydGFudCB0byBrbm93IHRoZSBiYXNpY3MsIHNpbmNlIHRoZXJlIGFyZSBhbHdheXMgaGVyZSBhbmQgdGhlcmUgc2l0dWF0aW9uIHdoZXJlIHlvdSBqdXN0IGNhbm5vdCBhdm9pZCBpdAoKIyMgQmFzaWNzIAoKIyMjIEFzc2lnbm1lbnRzIAogCllvdSBjYW4gYXNzaWduIGEgdmFsdWUgdG8gYW4gb2JqZWN0IHVzaW5nIGBhc3NpZ24oKWAsICBgPC1gLCBvciBgPWAuCmBgYHtyfQp4IDwtIDMgICAgICAgICAjIEFzc2lnbm1lbnQKeCAgICAgICAgICAgICAgIyBFdmFsdWF0ZSB0aGUgZXhwcmVzc2lvbiBhbmQgcHJpbnQgcmVzdWx0Cgp5IDwtIDQgICAgICAgICAjIEFzc2lnbm1lbnQKeSArIDUgICAgICAgICAgIyBFdmFsdWF0aW9uLCB5IHJlbWFpbnMgNAoKeiA8LSB4ICsgMTcqeSAgIyBBc3NpZ25tZW50CnogICAgICAgICAgICAgICMgRXZhbHVhdGlvbgoKcm0oeikgICAgICAgICAgIyBSZW1vdmUgejogZGVsZXRlcyB0aGUgb2JqZWN0LgojIHogICAgICAgICAgICAgIyBFcnJvciEKYGBgCgoKIyMjIFZhbHVlIGNvbXBhcmlzb25zIAoKQ29tcGFyaXNvbnMgcmV0dXJuIGJvb2xlYW4gdmFsdWVzOiBUUlVFIG9yIEZBTFNFIChvZnRlbiBhYmJyZXZpYXRlZCB0byBgVGAgYW5kIGBGYCkKYGBge3J9CjI9PTIgICMgRXF1YWxpdHkKMiE9MiAgIyBJbmVxdWFsaXR5CnggPD0geSAjIGxlc3MgdGhhbiBvciBlcXVhbDogIjwiLCAiPiIsIGFuZCAiPj0iIGFsc28gd29yawpgYGAKCgojIyMgU3BlY2lhbCBjb25zdGFudHM6IGBOQWAsIGBOVUxMYCwgYEluZmAsIGAtSW5mYCwgYE5hTmAKCmBOQWAgaW5kaWNhdGVzIG1pc3Npbmcgb3IgdW5kZWZpbmVkIGRhdGEKYGBge3J9CjUgKyBOQSAgICAgICMgV2hlbiB1c2VkIGluIGFuIGV4cHJlc3Npb24sIHRoZSByZXN1bHQgaXMgZ2VuZXJhbGx5IE5BCmlzLm5hKDUrTkEpICMgQ2hlY2sgaWYgbWlzc2luZwoKbWVhbihjKDEsIDIsIE5BLCA0LCA1KSkgIyBtYW55IGZ1bmN0aW9ucyByZXF1aXJpbmcgbnVtZXJpYyB2ZWN0b3JzIG91dHB1dCAgd2hlbiBvbmUgaXMgaW5jbHVkZWQKbWVhbihjKDEsIDIsIE5BLCA0LCA1KSwgbmEucm0gPSBUUlVFKSAjIE5vdCBpZiB3ZSBwcm92aWRlIHRoZSByaWdodCBhcmd1bWVudCB0byBpZ25vcmUgdGhlbQpgYGAKCmBOVUxMYCBpbmRpY2F0ZXMgYW4gZW1wdHkgb2JqZWN0LCBlLmcuIGEgbnVsbC9lbXB0eSBsaXN0CmBgYHtyfQoxMCArIE5VTEwgICAgICMgdXNlIHJldHVybnMgYW4gZW1wdHkgb2JqZWN0IChsZW5ndGggemVybykKaXMubnVsbChOVUxMKSAjIGNoZWNrIGlmIE5VTEwKYGBgCgpgSW5mYCBhbmQgYC1JbmZgIHJlcHJlc2VudCBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgaW5maW5pdHkuIFRoZXkgY2FuIGJlIHJldHVybmVkIGJ5ICBtYXRoZW1hdGljYWwgb3BlcmF0aW9ucyBsaWtlIGRpdmlzaW9uIG9mIGEgbnVtYmVyIGJ5IHplcm8uCmBgYHtyfQo1LzAKaXMuZmluaXRlKDUvMCkgIyBDaGVjayBpZiBhIG51bWJlciBpcyBmaW5pdGUKaXMuaW5maW5pdGUoNS8wKSAjIENoZWNrIGlmIGEgbnVtYmVyIGlzIGluZmluaXRlCmBgYAoKYE5hTmAgKE5vdCBhIE51bWJlcikgLSB0aGUgcmVzdWx0IG9mIGFuIG9wZXJhdGlvbiB0aGF0IGNhbm5vdCBiZSByZWFzb25hYmx5IGRlZmluZWQgCmBgYHtyfQowLzAKaXMubmFuKDAvMCkKYGBgCgojIyBPYmplY3QgY2xhc3NlcwoKIyMjIFZlY3RvcnMKCmBgYHtyfQp2MSA8LSBjKDEsIDUsIDExLCAzMykgICAgICAgIyBOdW1lcmljIHZlY3RvciwgbGVuZ3RoIDQKdjEKCnYyIDwtIGMoImhlbGxvIiwid29ybGQiKSAgICAjIENoYXJhY3RlciB2ZWN0b3IsIGxlbmd0aCAyIChhIHZlY3RvciBvZiBzdHJpbmdzKQp2MgoKdjMgPC0gYyhUUlVFLCBUUlVFLCBGQUxTRSkgICMgTG9naWNhbCB2ZWN0b3IsIHNhbWUgYXMgYyhULCBULCBGKQp2MwpgYGAKCkNvbWJpbmluZyBkaWZmZXJlbnQgdHlwZXMgb2YgZWxlbWVudHMgaW4gb25lIHZlY3RvciB3aWxsIGNvZXJjZSB0aGUgZWxlbWVudHMgIHRvIHRoZSBsZWFzdCByZXN0cmljdGl2ZSB0eXBlOgpgYGB7cn0KdjQgPC0gYyh2MSx2Mix2MywiYm9vIikgCSMgQWxsIGVsZW1lbnRzIHR1cm4gaW50byBzdHJpbmdzCnY0CmBgYAoKRWxlbWVudC13aXNlIG9wZXJhdGlvbnM6CmBgYHtyfQp2MSArIHYzICAgICAgIyBFbGVtZW50LXdpc2UgYWRkaXRpb24gKHJlbWluZGVyOiBUUlVFIGNvdW50cyBhcyAxLCBGQUxTRSBhcyAwKQp2MSArIDEgICAgICAgIyBBZGQgMSB0byBlYWNoIGVsZW1lbnQKdjEgKiAyICAgICAgICMgTXVsdGlwbHkgRUFDSCBlbGVtZW50IGJ5IDIgKG5vdCBsaWtlIHJlYWwgdmVjdG9yIG11bHRpcGxpY2F0aW9uKQp2MSArIGMoMSw3KSAgIyAoMSw3KSBpcyBhIHZlY3RvciBvZiBkaWZmZXJlbnQgbGVuZ3RoLi4uIHdoYXQgaGFwcGVucyBoZXJlPwpgYGAKCk1hdGhlbWF0aWNhbCBvcGVyYXRpb25zOgpgYGB7cn0Kc3VtKHYxKSAgICAgICMgVGhlIHN1bSBvZiBhbGwgZWxlbWVudHMKbWVhbih2MSkgICAgICMgVGhlIGF2ZXJhZ2Ugb2YgYWxsIGVsZW1lbnRzCnNkKHYxKSAgICAgICAjIFRoZSBzdGFuZGFyZCBkZXZpYXRpb24KY29yKHYxLHYxKjUpICMgQ29ycmVsYXRpb24gYmV0d2VlbiB2MSBhbmQgdjEqNSAKYGBgCgpMb2dpY2FsIG9wZXJhdGlvbnM6CmBgYHtyfQp2MSA+IDIgICAgICAgIyBFYWNoIGVsZW1lbnQgaXMgY29tcGFyZWQgdG8gMiwgcmV0dXJucyBsb2dpY2FsIHZlY3Rvcgp2MT09djIgICAgICAgIyBBcmUgY29ycmVzcG9uZGluZyBlbGVtZW50cyBlcXVpdmFsZW50LCByZXR1cm5zIGxvZ2ljYWwgdmVjdG9yLgp2MSE9djIgICAgICAgIyBBcmUgY29ycmVzcG9uZGluZyBlbGVtZW50cyAqbm90KiBlcXVpdmFsZW50PyBTYW1lIGFzICEodjE9PXYyKQoodjE+MikgfCAodjI+MCkgICAjIHwgaXMgdGhlIGJvb2xlYW4gT1IsIHJldHVybnMgYSB2ZWN0b3IuCih2MT4yKSAmICh2Mj4wKSAgICMgJiBpcyB0aGUgYm9vbGVhbiBBTkQsIHJldHVybnMgYSB2ZWN0b3IuCih2MT4yKSB8fCAodjI+MCkgICMgfHwgaXMgdGhlIGJvb2xlYW4gT1IsIHJldHVybnMgYSBzaW5nbGUgdmFsdWUgKGlmIGl0IGlzIHRydWUgYXQgbGVhc3Qgb25jZSkKKHYxPjIpICYmICh2Mj4wKSAgIyAmJiBpcyB0aGUgYm9vbGVhbiBBTkQsIHJldHVybnMgYSBzaW5nbGUgdmFsdWUgKGlmIGl0IGlzIHRydWUgYXQgbGVhc3Qgb25jZSkKYGBgCgpBZHJlc3NpbmcgdmVjdG9yIGVsZW1lbnRzOgpgYGB7cn0KdjFbM10gICAgICAgICAgICAgIyB0aGlyZCBlbGVtZW50IG9mIHYxCnYxWzI6NF0gICAgICAgICAgICMgZWxlbWVudHMgMiwgdG8gNCBvZiB2MQp2MVtjKDEsMyldICAgICAgICAjIGVsZW1lbnRzIDEgYW5kIDMgLSBub3RlIHRoYXQgeW91ciBpbmRleGVzIGFyZSBhIHZlY3Rvcgp2MVtjKFQsVCxGLEYsRildICAjIGVsZW1lbnRzIDEgYW5kIDIgLSBvbmx5IHRoZSBvbmVzIHRoYXQgYXJlIFRSVUUKdjFbdjE+M10gICAgICAgICAgIyB2MT4zIGlzIGEgbG9naWNhbCB2ZWN0b3IgVFJVRSBmb3IgZWxlbWVudHMgPjMKYGBgCgoqKk5PVEU6KiogSWYgeW91IGFyZSB1c2VkIHRvIGxhbmd1YWdlcyBpbmRleGluZyBmcm9tIDAgKGVnLiBQeXRob24pLCBgUmAgd2lsbCBzdXJwcmlzZSB5b3UgYnkgaW5kZXhpbmcgZnJvbSAxLgoKVG8gYWRkIG1vcmUgZWxlbWVudHMgdG8gYSB2ZWN0b3IsIHNpbXBseSBhc3NpZ24gdGhlbSB2YWx1ZXMuCmBgYHtyfQp2MVs2OjEwXSA8LSA2OjEwCnYxCmBgYAoKV2UgY2FuIGFsc28gZGlyZWN0bHkgYXNzaWduIHRoZSB2ZWN0b3IgYSBsZW5ndGg6CmBgYHtyfQpsZW5ndGgodjEpIDwtIDE1ICMgdGhlIGxhc3QgNSBlbGVtZW50cyBhcmUgYWRkZWQgYXMgbWlzc2luZyBkYXRhOiBOQQp2MQpgYGAKCiMjIyBGYWN0b3JzCgpGYWN0b3JzIGFyZSB1c2VkIHRvIHN0b3JlIGNhdGVnb3JpY2FsIGRhdGEuCmBgYHtyfQpleWUuY29sLnYgPC0gYygiYnJvd24iLCAiZ3JlZW4iLCAiYnJvd24iLCAiYmx1ZSIsICJibHVlIiwgImJsdWUiKSAgICAgICAgICN2ZWN0b3IKZXllLmNvbC5mIDwtIGZhY3RvcihjKCJicm93biIsICJncmVlbiIsICJicm93biIsICJibHVlIiwgImJsdWUiLCAiYmx1ZSIpKSAjZmFjdG9yCmV5ZS5jb2wudgpleWUuY29sLmYKYGBgCgpgUmAgd2lsbCBpZGVudGlmeSB0aGUgZGlmZmVyZW50IGxldmVscyBvZiB0aGUgZmFjdG9yIC0gZS5nLiBhbGwgZGlzdGluY3QgdmFsdWVzLiBUaGUgZGF0YSBpcyBzdG9yZWQgaW50ZXJuYWxseSBhcyBpbnRlZ2VycyAtIGVhY2ggbnVtYmVyIGNvcnJlc3BvbmRpbmcgdG8gYSBmYWN0b3IgbGV2ZWwuCgpgYGB7cn0KbGV2ZWxzKGV5ZS5jb2wuZikgICMgVGhlIGxldmVscyAoZGlzdGluY3QgdmFsdWVzKSBvZiB0aGUgZmFjdG9yIChjYXRlZ29yaWNhbCB2YXJpYWJsZSkKCmFzLm51bWVyaWMoZXllLmNvbC5mKSAgIyBUaGUgZmFjdG9yIGFzIG51bWVyaWMgdmFsdWVzOiAxIGlzICBibHVlLCAyIGlzIGJyb3duLCAzIGlzIGdyZWVuCmFzLm51bWVyaWMoZXllLmNvbC52KSAgIyBUaGUgY2hhcmFjdGVyIHZlY3RvciwgaG93ZXZlciwgY2FuIG5vdCBiZSBjb2VyY2VkIHRvIG51bWVyaWMKCmFzLmNoYXJhY3RlcihleWUuY29sLmYpICAKYXMuY2hhcmFjdGVyKGV5ZS5jb2wudikgCmBgYAoKCiMjIyBNYXRyY2VzICYgQXJyYXlzIAoKQSBtYXRyaXggaXMgYSB2ZWN0b3Igd2l0aCBkaW1lbnNpb25zOgpgYGB7cn0KbSA8LSByZXAoMSwgMjApICAgIyBBIHZlY3RvciBvZiAyMCBlbGVtZW50cywgYWxsIDEKZGltKG0pIDwtIGMoNSw0KSAgIyBEaW1lbnNpb25zIHNldCB0byA1ICYgNCwgc28gbSBpcyBub3cgYSA1eDQgbWF0cml4Cm0KYGBgCgpDcmVhdGUgYSBtYXRyaXggdXNpbmcgYG1hdHJpeCgpYDoKYGBge3J9Cm0gPC0gbWF0cml4KGRhdGE9MSwgbnJvdz01LCBuY29sPTQpICAjIHNhbWUgbWF0cml4IGFzIGFib3ZlLCA1eDQsIGZ1bGwgb2YgMXMKbSA8LSBtYXRyaXgoMSw1LDQpIAkJCSAgICAgICAgICAgICAjIHNhbWUgbWF0cml4IGFzIGFib3ZlIChsYXp5IHN0eWxlKQpkaW0obSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgV2hhdCBhcmUgdGhlIGRpbWVuc2lvbnMgb2YgbT8KbQpgYGAKCkNyZWF0ZSBhIG1hdHJpeCBieSBjb21iaW5pbmcgdmVjdG9yczoKYGBge3J9Cm0gPC0gY2JpbmQoMTo1LCA1OjEsIDU6OSkgICMgQmluZCAzIHZlY3RvcnMgYXMgY29sdW1ucywgNXgzIG1hdHJpeAptIDwtIHJiaW5kKDE6NSwgNToxLCA1OjkpICAjIEJpbmQgMyB2ZWN0b3JzIGFzIHJvd3MsIDN4NSBtYXRyaXgKbSA8LSBtYXRyaXgoMToxMCwxMCwxMCkKbQpgYGAKClNlbGVjdCBtYXRyaXggZWxlbWVudHM6IApgYGB7cn0KbVsyLDNdICAjIE1hdHJpeCBtLCByb3cgMiwgY29sdW1uIDMgLSBhIHNpbmdsZSBjZWxsCm1bMixdICAgIyBUaGUgd2hvbGUgc2Vjb25kIHJvdyBvZiBtIGFzIGEgdmVjdG9yCm1bLDJdICAgIyBUaGUgd2hvbGUgc2Vjb25kIGNvbHVtbiBvZiBtIGFzIGEgdmVjdG9yCm1bMToyLDQ6Nl0gIyBzdWJtYXRyaXg6IHJvd3MgMSBhbmQgMiwgY29sdW1ucyA0LCA1IGFuZCA2Cm1bLTEsXSAgICAgIyBhbGwgcm93cyAqZXhjZXB0KiB0aGUgZmlyc3Qgb25lCmBgYAoKQ29uZGl0aW9uYWwgb3BlcmF0aW9ucwpgYGB7cn0KbVsxLF0gPT0gbVssMV0gICMgQXJlIGVsZW1lbnRzIGluIHJvdyAxIGVxdWl2YWxlbnQgdG8gY29ycmVzcG9uZGluZyBlbGVtZW50cyBmcm9tIGNvbHVtbiAxPyAKbSA+IDMgICAgICAgICAgICMgQSBsb2dpY2FsIG1hdHJpeDogVFJVRSBmb3IgbSBlbGVtZW50cyA+MywgRkFMU0Ugb3RoZXJ3aXNlCm1bbSA+IDNdICAgICAgICAjIFNlbGVjdHMgb25seSBUUlVFIGVsZW1lbnRzIC0gdGhhdCBpcyBvbmVzIGdyZWF0ZXIgdGhhbiAzCmBgYAoKT3RoZXIgbWF0cml4IG1hbmlwdWxhdGlvbgpgYGB7cn0KdChtKSAgICAgICAgICAjIFRyYW5zcG9zZSBtICAgICAKbSAlKiUgdChtKSAgICAjICUqJSBkb2VzIG1hdHJpeCBtdWx0aXBsaWNhdGlvbgptICogbSAgICAgICAgICMgKiBkb2VzIGVsZW1lbnQtd2lzZSBtdWx0aXBsaWNhdGlvbgpgYGAKCgojIyMgQXJyYXlzOiBtb3JlIHRoYW4gMiBkaW1lbnNpb25zCgpDcmVhdGVkIHdpdGggdGhlIGBhcnJheSgpYCBmdW5jdGlvbjoKYGBge3J9CmEgPC0gYXJyYXkoZGF0YT0xOjE4LGRpbT1jKDMsMywyKSkgIyAzZCB3aXRoIGRpbWVuc2lvbnMgM3gzeDIKYSA8LSBhcnJheSgxOjE4LGMoMywzLDIpKSAgICAgICAgICAjIHRoZSBzYW1lIGFycmF5CmEKYGBgCgpTaW5jZSBhcnJheXMgaGF2ZSAzIGRpbWVuc2lvbnMsIGFsc28gYSAzcmQgZWxlbWVudCBjYW4gYmUgdXNlZCBmb3Igc2xpY2luZyZkaWNpbmhnLgpgYGB7cn0KYVsxLDMsMl0KYGBgCgoKIyMjIExpc3RzICAKCkxpc3RzIGFyZSBjb2xsZWN0aW9ucyBvZiBvYmplY3RzIChlLmcuIG9mIHN0cmluZ3MsIHZlY3RvcnMsIG1hdHJpY2VzLCBvdGhlciBsaXN0cywgZXRjLikKYGBge3J9CmwxIDwtIGxpc3QoYm9vPXYxLGZvbz12Mixtb289djMsem9vPSJBbmltYWxzISIpICAjIEEgbGlzdCB3aXRoIGZvdXIgY29tcG9uZW50cwpsMiA8LSBsaXN0KHYxLHYyLHYzLCJBbmltYWxzISIpCgpsMyA8LSBsaXN0KCkKbDQgPC0gTlVMTAoKYGBgCgpgYGB7cn0KbDFbImJvbyJdICAgICAgIyBBY2Nlc3MgYm9vOiB0aGlzIHJldHVybnMgYSBsaXN0LgpsMVtbImJvbyJdXSAgICAjIEFjY2VzcyBib286IHRoaXMgcmV0dXJucyB0aGUgbnVtZXJpYyB2ZWN0b3IKbDFbWzFdXSAgICAgICAgIyBSZXR1cm5zIHRoZSBmaXJzdCBjb21wb25lbnQgb2YgdGhlIGxpc3QsIGVxdWl2YWxlbnQgdG8gYWJvdmUuCmwxW1sxXV1bMl0gICAgICMgQWNlc3MgdGhlIHNlY29uZCBlbGVtZW50IGluIHRoZSBmaXJzdCBsaXN0CmwxJGJvbyAgICAgICAgICMgTmFtZWQgZWxlbWVudHMgY2FuIGJlIGFjY2Vzc2VkIHVzaW5nIHRoZSAkIG9wZXJhdG9yIC0gZXF1aXZhbGVudCB0byBbW11dCmBgYAoKQWRkIG1vcmUgZWxlbWVudHMgdG8gYSBsaXN0OgpgYGB7cn0KbDNbWzFdXSA8LSAxMSAjIGFkZCBhbiBlbGVtZW50IHRvIHRoZSBlbXB0eSBsaXN0IGwzCmw0W1szXV0gPC0gYygyMiwgMjMpICMgYWRkIGEgdmVjdG9yIGFzIGVsZW1lbnQgMyBpbiB0aGUgZW1wdHkgbGlzdCBsNC4gCiAgICAgICAgICAgICAgICAgICAgICMgU2luY2Ugd2UgYWRkZWQgZWxlbWVudCAzLCBlbGVtZW50cyAxICYgMiB3aWxsIGJlIGdlbmVyYXRlZCBhbmQgZW1wdHkgKE5VTEwpCmwxW1s1XV0gPC0gIk1vcmUgZWxlbWVudHMhIiAjIFRoZSBsaXN0IGwxIGhhZCA0IGVsZW1lbnRzLCB3ZSdyZSBhZGRpbmcgYSA1dGggaGVyZS4KbDFbWzhdXSA8LSAxOjExICMgV2UgYWRkZWQgYW4gOHRoIGVsZW1lbnQsIGJ1dCBub3QgNnRoIG9yIDd0aC4gVGhvc2Ugd2lsbCBiZSBjcmVhdGVkIGVtcHR5IChOVUxMKQpsMSRTb21ldGhpbmcgPC0gIkEgdGhpbmciICAjIEFkZHMgYSBuaW50aCBlbGVtZW50IC0gIkEgdGhpbmciLCBuYW1lZCAiU29tZXRoaW5nIgpgYGAKCgojIyMgRGF0YSBGcmFtZXMgIApUaGUgZGF0YSBmcmFtZSBpcyBhIHNwZWNpYWwga2luZCBvZiBsaXN0IHVzZWQgZm9yIHN0b3JpbmcgZGF0YXNldCB0YWJsZXMuIFRoaW5rIG9mIHJvd3MgYXMgY2FzZXMsIGNvbHVtbnMgYXMgdmFyaWFibGVzLiBFYWNoIGNvbHVtbiBpcyBhIHZlY3RvciBvciBmYWN0b3IuCgpOb3RlOiBXaGlsZSBiYXNlIGBSYCB1c2VzIHRoZSBgZGF0YS5mcmFtZWAsIHdlIGxhdGVyIHdoZW4gd29ya2luZyB3aXRoIGB0aWR5dmVyc2VgIHVzZSB0aGUgYHRpYmJsZWAgaW5zdGVhZCwgd2hpY2ggaXMgdGhlIHNhbWUgYnV0IG1vZGlmaWVzIHNvbWUgYW5ub3lpbmcgYmVoYXZpb3JzIG9mIHRoZSBvcmlnaW5hbCBkYXRhIHR5cGUgKGVnLiBubyBkZWZhdWx0IGludGVycHJldGF0aW9ucyBvZiBzdHJpbmdzIGFzIGZhY3RvcnMsIG5vIGByb3duYW1lc2AuIE1vcmUgb24gdGhhdCBsYXRlcikuICAKCkNyZWF0aW5nIGEgZGF0YWZyYW1lOgpgYGB7cn0KZGZyMSA8LSBkYXRhLmZyYW1lKCBJRD0xOjQsCiAgICAgICAgICAgICAgICAgICAgRmlyc3ROYW1lPWMoIkplc3BlciIsIkpvbmFzIiwiUGVybmlsbGUiLCJIZWxsZSIpLAogICAgICAgICAgICAgICAgICAgIEZlbWFsZT1jKEYsRixULFQpLCAKICAgICAgICAgICAgICAgICAgICBBZ2U9YygyMiwzMyw0NCw1NSkgKQoKZGZyMSRGaXJzdE5hbWUgICAjIEFjY2VzcyB0aGUgc2Vjb25kIGNvbHVtbiBvZiBkZnIxLiAKYGBgCgoqKk5vdGljZSoqIHRoYXQgYFJgIHRoaW5rcyB0aGlzIGlzIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgIGFuZCBzbyBpdCdzIHRyZWF0aW5nIGl0IGxpa2UgYSBmYWN0b3IsIG5vdCBhIGNoYXJhY3RlciB2ZWN0b3IuIFlvdSBjYW4gdGVsbCBgUmAgeW91IGRvbid0IGxpa2UgZmFjdG9ycyBmcm9tIHRoZSBzdGFydCB1c2luZyBgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRWAuIEkgZmluZCB0aGF0IGFubm95aW5nLiBUaGUgYHRpYmJsZWAgKGludHJvZHVjZWQgbGF0ZXIpIGRvZXMgbm90IGRvIHRoYXQuCgpgYGB7cn0KZGZyMiA8LSBkYXRhLmZyYW1lKEZpcnN0TmFtZT1jKCJKb2huIiwiSmltIiwiSmFuZSIsIkppbGwiKSwgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkKZGZyMiRGaXJzdE5hbWUgICAjIFN1Y2Nlc3M6IG5vdCBhIGZhY3Rvci4KYGBgCgpBY2Nlc3MgZWxlbWVudHMgb2YgdGhlIGRhdGEgZnJhbWUuIE5vdGF0aW9uIGlzIGBkZnJbcm93LCBjb2x1bW5dYCBSb3dzIGNhbiBiZSBhY2Vzc2VkIGJ5IG51bWJlciBvciBjb25kaXRpb24sIGNvbHVtbnMgYnkgbnVtYmVyIG9yIG5hbWUuIEFsdGVybmF0aXZlbHksIGNvbHVtbnMgY2FuIGJlIGFjZXNzZWQgYnkgYGRmciRjb2x1bW5gCmBgYHtyfQpkZnIxWzEsXSAgICMgRmlyc3Qgcm93LCBhbGwgY29sdW1ucwpkZnIxWywxXSAgICMgRmlyc3QgY29sdW1uLCBhbGwgcm93cwpkZnIxJEFnZSAgICMgQWdlIGNvbHVtbiwgYWxsIHJvd3MKZGZyMVsxOjIsMzo0XSAjIFJvd3MgMSBhbmQgMiwgY29sdW1ucyAzIGFuZCA0IC0gdGhlIGdlbmRlciBhbmQgYWdlIG9mIEpvaG4gJiBKaW0KZGZyMVtjKDEsMyksXSAjIFJvd3MgMSBhbmQgMywgYWxsIGNvbHVtbnMKYGBgCgpGaW5kIHRoZSBuYW1lcyBvZiBldmVyeW9uZSBvdmVyIHRoZSBhZ2Ugb2YgMzAgaW4gdGhlIGRhdGEKYGBge3J9CmRmcjFbZGZyMSRBZ2U+MzAsMl0KYGBgCgpGaW5kIHRoZSBhdmVyYWdlIGFnZSBvZiBhbGwgZmVtYWxlcyBpbiB0aGUgZGF0YToKYGBge3J9Cm1lYW4gKGRmcjFbZGZyMSRGZW1hbGU9PVRSVUUsNF0pCmBgYAoKCiMjIEZsb3cgQ29udHJvbCAobG9vcHMgJiBmcmllbmRzKQpMb29wcyBhcmUgcG93ZXJmdWwgbGl0dGxlIGhlbHBlcnMgdG8gZG8gdGhlIHNhbWUgb3BlcmF0aW9uIGl0ZXJhdGluZyBvdmVyIGEgbnVtYmVyIG9mIGl0ZW1zLgoKSWYgc3RhdGVtZW50czogYGlmIChjb25kaXRpb24pIGV4cHIxIGVsc2UgZXhwcjJgCmBgYHtyfQp4IDwtIDU7IHkgPC0gMTAKaWYgKHg9PTApIHkgPC0gMCBlbHNlIHkgPC0geS94ICAKeQpgYGAKCmZvciBsb29wczogYGZvciAodmFyaWFibGUgaW4gc2VxdWVuY2UpIGV4cHJgCmBgYHtyfQpmb3IgKGkgaW4gMTp4KSAgeyBwcmludChwYXN0ZSgiT01HLCBpIGp1c3QgY291bnRlZCB0byIsIGkpKSB9CmBgYAoKV2hpbGUgbG9vcDogYHdoaWxlIChjb25kaW50aW9uKSBleHByYApgYGB7cn0Kd2hpbGUgKHggPiAwKSB7cHJpbnQoeCk7IHggPC0geC0xO30KYGBgCgpSZXBlYXQgbG9vcDogYHJlcGVhdCBleHByLCB1c2UgYnJlYWsgdG8gZXhpdCB0aGUgbG9vcGAKYGBge3J9CnJlcGVhdCB7IHByaW50KHgpOyB4IDwtIHgrMTsgaWYgKHg+NykgYnJlYWt9CmBgYAoKIyMgUiB0cm91Ymxlc2hvb3RpbmcgCgpXaGlsZSBJIGdlbmVyYXRlIG1hbnkgKGFuZCBvZnRlbiB2ZXJ5IGNyZWF0aXZlKSBlcnJvcnMgaW4gUiwgdGhlcmUgYXJlIHRocmVlIHNpbXBsZSB0aGluZ3MgdGhhdCB3aWxsIG1vc3Qgb2Z0ZW4gZ28gd3JvbmcgZm9yIG1lLiBUaG9zZSBpbmNsdWRlOiAKCiogQ2FwaXRhbGl6YXRpb24uIFIgaXMgY2FzZSBzZW5zaXRpdmUgLSBhIGdyYXBoIHZlcnRleCBuYW1lZCAiSmFjayIgaXMgbm90IHRoZSBzYW1lIGFzIG9uZSBuYW1lZCAiamFjayIuIFRoZSBmdW5jdGlvbiBgcm93U3Vtc2Agd29uJ3Qgd29yayBhcyAicm93c3VtcyIgb3IgIlJvd1N1bXMiLgoqIE9iamVjdCBjbGFzcy4gV2hpbGUgbWFueSBmdW5jdGlvbnMgYXJlIHdpbGxpbmcgdG8gdGFrZSBhbnl0aGluZyB5b3UgdGhyb3cgYXQgdGhlbSwgc29tZSB3aWxsIHN0aWxsIHN1cnByaXNpbmdseSByZXF1aXJlIGNoYXJhY3RlciB2ZWN0b3Igb3IgYSBmYWN0b3IgaW5zdGVhZCBvZiBhIG51bWVyaWMgdmVjdG9yLCBvciBhIG1hdHJpeCBpbnN0ZWFkIG9mIGEgZGF0YSBmcmFtZS4gRnVuY3Rpb25zIHdpbGwgYWxzbyBvY2Nhc2lvbmFsbHkgcmV0dXJuIHJlc3VsdHMgaW4gYW4gdW5leHBlY3RlZCBmb3JtYXQuCiogUGFja2FnZSBuYW1lc3BhY2VzLiBPY2Nhc2lvbmFsbHkgcHJvYmxlbXMgd2lsbCBhcmlzZSB3aGVuIGRpZmZlcmVudCBwYWNrYWdlcyBjb250YWluIGZ1bmN0aW9ucyB3aXRoIHRoZSBzYW1lIG5hbWUuIFIgbWF5IHdhcm4geW91IGFib3V0IHRoaXMgYnkgc2F5aW5nIHNvbWV0aGluZyBsaWtlICJUaGUgZm9sbG93aW5nIG9iamVjdChzKSBhcmUgbWFza2VkIGZyb20gJ3BhY2thZ2U6aWdyYXBoJyIgYXMgeW91IGxvYWQgYSBwYWNrYWdlLiBPbmUgd2F5IHRvIGRlYWwgd2l0aCB0aGlzIGlzIHRvIGNhbGwgZnVuY3Rpb25zIGZyb20gYSBwYWNrYWdlIGV4cGxpY2l0bHkgdXNpbmcgJzo6Jy4gRm9yIGluc3RhbmNlLCBpZiBmdW5jdGlvbiAnYmxhaCcgaXMgcHJlc2VudCBpbiBwYWNrYWdlcyBBIGFuZCBCLCB5b3UgY2FuIGNhbGwgQTo6YmxhaAoKYGBge3J9CiMgaW5zdGFsbC5wYWNrYWdlcygnZHBseXInKQojIGxpYnJhcnkoZHBseXIpICAgICAgICAgICMgbG9hZCBhIHBhY2thZ2UKIyBkZXRhY2gocGFja2FnZTpkcGx5cikgICAjIGRldGFjaCBhIHBhY2thZ2UKYGBgCgpgYGB7cn0KZHBseXI6OnNlbGVjdChkZnIxLCBGaXJzdE5hbWUpICMgcGFja2FnZW5hbWU6OmZ1bmN0aW9ubmFtZSAgIGxldHMgeW91IGFjY2VzcyB0aGUgbmFtZXNwYWNlIGZyb20gcGFja2FnZXMgbm90IGxvYWRlZCAoYnV0IGluc3RhbGxlZCkKYGBgCgpGb3IgbW9yZSBhZHZhbmNlZCB0cm91Ymxlc2hvb3RpbmcsIGNoZWNrIG91dCBgdHJ5KClgLCBgdHJ5Q2F0Y2goKWAsIGFuZCBgZGVidWcoKWAuIApgYGB7cn0KP3RyeUNhdGNoCmBgYAoKR2VuZXJhbGx5LCBqdXN0IHVzaW5nIGA/ZnVuY3Rpb255b3V3b25kZXJhYm91dGAgb2Z0ZW4gc29sdmVzIHByb2JsZW1zLiBUaGVyZSB5b3UgY2FuIHJldmlldyB0aGUgZnVuY3Rpb25zIGFyZ3VtZW50cywgaW5wdXRzLCBvdXRwdXRzLCBzeW50YXggZXRjLgoKIyBSIDIuMDogVGhlIFRpZHl2ZXJzZQoKIyMgV2hhdCBpcyBpdCBhbGwgYWJvdXQ/CkJhc2UgYFJgIGNvbWVzIHdpdGggcXVpdGUgc29tZSBmdW5jdGlvbmFsaXR5IGZvciBzbGljaW5nIGFuZCBkaWNpbmcgZGF0YSwgdGhlcmUgYWxzbyBleGlzdHMgYSBteXJpYWQgc3BlY2lhbGl6ZWQgcGFja2FnZXMgZm9yIG1vcmUgdHJpY2t5IGRhdGEgbWFuaXB1bGF0aW9uLiBUbyByZWFkIG90aGVycycgY29kZSBhbmQgZXhhbXBsZSBhcyB3ZWxsIGFzIHRvIHBlcmZvcm0gc29tZSBzcGVjaWFsIG9wZXJhdGlvbnMsIHlvdSBhbGwgc2hvdWxkIGJlIGFibGUgdG8gdXNlIHN0YW5kYXJkIGBSYCBzeW50YXguIAoKSG93ZXZlciwgdGhlIGZhY3RvcnMsIHRoZSBgW3JvdywgY29sdW1uXWAgc3ludGF4IGFuaGQgc28gZm9ydGggYXJlIG5vdCB2ZXJ5IGNvbWZvcnRhYmxlIGFuZCBpbnR1aXRpdmUuIEZ1cnRoZXIsIGZvciBtb3JlIHRyaWNreSBvcGVyYXRpb24gc3VjaCBhcyBjZXJ0YWluIGFnZ3JlZ2F0aW9ucyBldGMuLCBvbmUgaGFzIHRvIHJlbHkgb24gYSB2YXJpZXR5IG9mIHBhY2thZ2VzLCB3aGljaCBvZnRlbiBjb21lIHdpdGggYW4gb3duIHN5bnRheC4KClRoZSBnb29kIG5ld3MgaXM6IFRoZSBlZmZvcnRzIG9mIGEgc21hbGwgc2V0IG9mIGtleS1kZXZlbG9wZXJzIChmb3JlbW9zdCBbSGFkbGV5IFdpY2toYW1dKGh0dHA6Ly9oYWRsZXkubnovKSkgaGFzIGxldCB0byB0aGUgZGV2ZWxvcG1lbnQgb2YgdGhlICBbYHRpZHl2ZXJzZWBdKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcvKSwgYW4gZWNvc3lzdGVtIG9mIGBSYCBwYWNrYWdlcyBwYXJ0aWN1bGFybHkgZGVzaWduZWQgZm9yIGRhdGEgc2NpZW5jZSBhcHBsaWNhdGlvbnMuIEFsbCBwYWNrYWdlcyBzaGFyZSBhbiB1bmRlcmx5aW5nIGRlc2lnbiBwaGlsb3NvcGh5LCBjb21tb24gQVBJLCBncmFtbWFyLCBhbmQgZGF0YSBzdHJ1Y3R1cmVzLiAKCkFtb25nIHRoZSBtb3N0IGFtYXppbmcgY29udHJpYnV0aW9ucyBoZXJlIGlzIFtgZHBseXJgXShodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvKSwgYSBncmFtbWFyIG9mIGRhdGEgbWFuaXB1bGF0aW9uLCBwcm92aWRpbmcgYSBjb25zaXN0ZW50IHNldCBvZiB2ZXJicyB0aGF0IGhlbHAgeW91IHNvbHZlIHRoZSBtb3N0IGNvbW1vbiBkYXRhIG1hbmlwdWxhdGlvbiBjaGFsbGVuZ2VzLiBJIHVzZSBgZHBseXJgIGZvciA5MCUgb2YgbXkgZGF0YS1tYW5pcHVsYXRpb24gdGFza3MgZm9yIHRoZSBmb2xsb3dpbmcgcmVhc29uczoKCiogQWxsIHRoZSB1bmRlcmx5aW5nIGNvZGUgaXMgcnVucyBvcHRpbWl6ZWQgaW4gYEMrK2AsIG1ha2luZyBpdCBmYXN0ZXIgdGhhbiBtb3N0IGJhc2UgYFJgCiogSXQgY29uc2lzdGVudGx5IHVuaWZpZXMgdGhlIGdyYW1tYXIgb2YgZGF0YSBtYW5pcHVsYXRpb24gdG8gYSBzbWFsbCBzZXQgb2Ygb3BlcmF0aW9ucywgd2hpY2ggY2FuIGJlIGZsZXhpYmx5IGNvbWJpbmVkIHRvIG1hc3RlciBhbG1vc3QgZXZlcnkgdGFzawoqIEl0IGlzIGRlc2lnbmVkIHRvIHdvcmsgbmVhdGhseSB3aXRoIHRoZSBgJT4lYCBwaXBlLW9wZXJhdG9yIG9mIFttYWdyaXR0cl0oaGh0dHBzOi8vbWFncml0dHIudGlkeXZlcnNlLm9yZy8pIChtb3JlIG9uIHRoYXQgbGF0ZXIpCiogaXRzIHN5bnRheCBpcyB2ZXJ5IHNpbWlsYXIgdG8gdGhlIGxvZ2ljIG9mIGBTUUxgIGFuZCBvdGhlciBkYXRhLW1hbmFnZW1lbnQgbGFuZ3VhZ2VzCiogSXQgZXhwYW5kZWQgZmFyIGJlb3lvbmQgaXRzIG9yaWdpbmFsIDUgdmVyYnMsIGFuZCBub3cgcmVwbGFjZXMgbW9zdCBiYXNlIFIgY29tbWFuZHMgd2l0aCBvcHRpbWl6ZWQsIGNsZXZlciwgYW5kIGhpZ2gtcGVyZm9ybWFuY2UgYWx0ZXJuYXRpdmVzCiogSXQgd29ya3MgbmVhdGhseSB3aXRoIG1hbnkgZGF0YWJhc2VzLCBzdWNoIGFzIGBTUUxgICh3aXRoIGFkZG9uIHBhY2thZ2VzIGBEQklgIGFuZCBgZGJwbHlyYCkKCkkgd2lsbCBub3QgdG91Y2ggb24gYWxsIHBhY2thZ2VzIHRoZXJlLCBidXQgdGhlIGNvbXBsZXRlIGB0aWR5dmVyc2VgIGNvdmVycyBhbG1vc3QgYWxsIGlzc3VlcyBvZiBkYXRhIG1hbmlwdWxhdGlvbi4gVGhleSBhbGwgb3BlcmF0ZSB1bmRlciB0aGUgc2FtZSBsb2dpYywgYXJlIGZhc3QsIGFuZCB1c3VhbGx5IHlvdXIgYmVzdCBjaG9pY2UgZm9yIGFsbW9zdCBhbnkgZ2l2ZW4gcHJvYmxlbS4gUGFydGljdWxhcmx5IGBkcGx5cmAgaXMgZW5vdXJtZW91c2x5IHBvd2VyZnVsbCwgYW5kIGhhcyBhIGxvdCBtb3JlIGZ1bmN0aW9ucyB0aGFuIHRoZSBiYXNpY3MgSSBjb3ZlciBoZXJlLiBTbywgZm9yIGV2ZXJ5IGdpdmVuIHByb2JsZW0sIHlvdXIgZmlyc3QgcXVlc3Rpb24gKHRvIHlvdXJzZWxmIG9yIFtzdGFja292ZXJmbG93XShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tLykpIHNob3VsZCBiZToKCjE6IElzIHRoZXJlIGEgd2F5IHRvIHNvbHZlIG15IHByb2JsZW0gaW4gYGRwbHlyYD8KMjogSWYgbm90LCBpcyB0aGVyZSBhbm90aGVyIGB0aWR5dmVyc2VgIHBhY2thZ2UgZGVkaWNhdGVkIHRvIHRoaXMgcHJvYmxlbT8KCkZvciB0aGUgc2FrZSBvZiBpbGx1c3RyYXRpb24sIEkgd2lsbCBsb2FkIGV2ZXJ5IHBhY2thZ2Ugb2YgdGhlIGB0aWR5dmVyc2VgIG9uZS1ieS1vbmUgd2hlbiB3ZSBuZWVkIGl0LiBIb3dldmVyLCBub3JtYWxseSBJIGp1c3QgbG9hZCBgbGlicmFyeSh0aWR5dmVyc2UpYCBhbGwgYXQgb25jZSwgc2luY2UgSSBuZWVkIGEgbG90IG9mIHRoZXNlIHBhY2thZ2VzIG9mdGVuIGFueWhvdwoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKSAjIENvbGxlY3Rpb24gb2YgYWxsIHRoZSBnb29kIHN0dWZmIGxpa2UgZHBseXIsIGdncGxvdDIgZWN0LgpsaWJyYXJ5KG1hZ3JpdHRyKSAjIEZvciBleHRyYS1waXBpbmcgb3BlcmF0b3JzIChlZy4gJTw+JSkKYGBgCgojIyBUaWJibGVzCgpUaWJibGVzIGFyZSB0aGUgYHRpZHl2ZXJzZWAgdmVyc2lvbiBvZiB0aGUgdHJhZGl0aW9uYWwgZGF0YWZyYW1lLiBUaGV5IHdvcmsgaW4gZXhhY3RseSB0aGUgc2FtZSB3YXksIG9ubHkgd2l0aCBzb21lIHNtYWxsIGRpZmZlcmVuY2VzLCB3aGljaCBhcmUgdXN1YWxseSBmcm9tIGEgZGF0YSBzY2llbmNlIHBlcnNwY3RpdmUgc2VlbiBhcyBhbiBpbXByb3ZlbWVudDoKCjEuIFN0cmluZ3MgYWUgYnkgZGVmYXVsdCBub3QgcmVjb2RlZCBhcyBmYWN0b3JzCjIuIFJvd25hbWVzIGFyZSBkcm9wcGVkCjMuIERlZmF1bHQgcHJpbnQgZGVsaXZlcnMgIG1vcmUgY29udmVuaWVudCBvdmVydmlldy4KClRoZXkgY2FuIGJlIGNyZWF0ZWQgaW4gMyBkaWZmZXJlbnQgd2F5cy4KCjEuIENyZWF0aW5nIHRoZW0gZnJvbSBzY3JhdGNoIHdpdGggYHRpYmJsZSgpYAozLiBVc2luZyBleHBsaWNpdGx5IHRoZSBgYXNfdGliYmxlKClgIGZ1bmN0aW9uIG9uIGEgdGFibGUKMy4gV2hlbiBhcHBseWluZyBhbmQgYGRwbHlyYCBmdW5jdGlvbiBvbiBhIGRhdGFmcmFtZSwgaXQgd2lsbCBhdXRvbWF0aWNhbGx5IGJlIGNvbnZlcnRlZCB0byBhIHRpYmJsZS4KCmBgYHtyfQpoZWFkKGlyaXMpICMgYSBkYXRhZnJhbWUKYGBgCgpgYGB7cn0KaGVhZChhc190aWJibGUoaXJpcykpCmBgYAoKSXQgaXMgdXN1YWxseSB0aGUgcHJlZmVyZWQgZm9ybWF0IGZvciBkYXRhIHNjaWVuY2UgcHJvamVjdHMgaW4gUi4KCiMjIFBpcGluZyAKCmluIHRyYWRpdGlvbmFsIGBSYCBzeW50YXgsIGRhdGEtbWFuaXB1bGF0aW9ucyBhcmUgY2FycmllZCBvdXQgb25lIGJ5IG9uZS4gRm9yIGV4YW1wbGUsIG9uZSB3b3VsZCBmaXJzdCBhc3NpZ24gYSBuZXcgdmFyaWFibGUgYHgkbnVtYmVycyA8LSAxOjVgLCB0aGVuIG1heWJlIG1hbmlwdWxhdGUgaXQgYHgkbnVtYmVycyA8LSB4JG51bWJlcnMgKiAyYCwgYW5kIHN1YnNldCBpdCBgeCA8LSB4W3gkbnVtYmVycyA+IDRdYC4gYGRwbHlyYCBtYWtlcyB1c2Ugb2YgYG1hcmdyaXR0cmAncyBwaXBlcywgd3JpdHRlbiBsaWtlIGAlPiVgLiAKCkEgcGlwZSBtZWFucyB0YWtlIHRoZSBvdXRwdXQgb2YgaXQncyBsZWZ0LWhhbmQgc2lkZSBhbmQgaW5zZXJ0IGl0IGFzIGZpcnN0IGlucHV0IGluIHRoZSBmdW5jdGlvbiBvbiB0aGUgcmlnaHQtaGFuZCBzaWRlLiBBY2NvcmRpbmdseSwgYWxsIGBkcGx5cmAgZnVuY3Rpb25zIGZvbGxvdyB0aGUgc3ludGF4IHRoYXQgdGhlaXIgZmlyc3QgaW5wdXQgaXMgYWx3YXlzIHRoZSBkYXRhIHRvIGJlIG1hbmlwdWxhdGVkLiBUaGVyZWZvcmUsIHRoZXkgY2FuIGFsbCBiZSAicGlwZWQiLgoKYGBge3J9CnggPC0gdGliYmxlKG51bWJlcnMgPSAxOjUpIApgYGAKCkxldHMgc2F5IHdlIHdhbnQgdG8gbXVsdGlwbHkgYWxsIG51bWJlciB3aXRoIDIsIGFuZCBUSEVOIHN1YnNldCB0aGUgZGF0YSBmb3Igb2JzZXJ2YXRpb25zIHdpdGggYSBudW1iZXIgbGFyZ2VyIHRoYW4gNC4gV2UgY291bGQgZG8gdGhlIGZvbGxvd2luZwoKYGBge3J9CnkgPC0geAp5WywnbnVtYmVycyddIDwtIHlbLCdudW1iZXJzJ10gKiAyCnkgPC0geVt5WydudW1iZXJzJ10gPiA0LCBdCnkKYGBgCgpGb3IgZXhhbXBsZSwgd2UgY291bGQgcGlwZSBhcyBmb2xsb3dzIChkb24ndCB3b3JyeSBhYm91dCB0aGUgb3RoZXIgc3ludGF4IHlldCk6CgpgYGB7cn0KeCAlPiUKICBtdXRhdGUobnVtYmVycyA9IG51bWJlcnMgKiAyKSAlPiUKICBmaWx0ZXIobnVtYmVycyA+IDQpCmBgYAoKSXQgYmFzaWNhbGx5IHJlYWRzIGxpa2U6IAoKKiBDcmVhdGUgYSBkYXRhZnJhbWUgKHRvIGJlIHByZWNpc2UsIGEgYHRpYmJsZWApIHdpdGggdGhlIHZhcmlhYmxlICJudW1iZXJzIiBhbmQgYXNzaWduIHRoZSB2YWx1ZXMgMTo1LgoqIFRIRU4gbXVsdGlwbHkgdGhlbSB3aXRoIDIuCjogVEhFTiBzdWJzZXQgdGhlIGRhdGFmcmFtZSB0byBvbmx5IHJvd3Mgd2l0aCBhIG51aW1iZXIgdmFsdWUgaGlnaGVyIHRoYW4gNC4KCkl0IGZpcnN0IGxvb2tzIG5vdCBzbyBpbnR1aXRpdmUsIGJ1dCBpdCB3aWxsIGJlY29tZSB5b3VyIHNlY29uZCBuYXR1cmUuIFVzaW5nIHBpcGVzIGZhY2lsaXRhdGVzIGZhc3QsIHJlcHJvZHVjaWJsZSBhbmQgZWFzaWx5IHJlYWRhYmxlIGNvZGluZyBwcmFjdGljZXMsIGFuZCBhbGwgb2YgeW91IGFyZSBlbmNvdXJhZ2VkIHRvIGdvIG9uIHdpdGggdGhhdC4KCioqTm90ZToqKiBgJT4lYCBwaXBlcyBkbyBub3QgYXV0b21ldGljYWxseSBhc3NpZ24gdGhlaXIgb3V0cHV0IHRvIHRoZSBsZWZ0LWhhbmQgc2lkZSBvYmplY3QsIG1lYW5pbmcgdGhlIG9yaWdpbmFsIGRhdGFzZXQgd2lsbCBub3QgcGVyIHNlIGJlIG92ZXJ3cml0dGVuLiBUbyBkbyB0aGF0LCB0aGVyZSBhcmUgdHdvIHdheXM6CgoxOiBJbml0aWFsbHksIGFzc2lnbiB0aGUgb3V0cHV0IHRvIHRoZSBvcmlnaW5hbCBkYXRhIHdpdGggYDwtYAoyOiBJbml0aWFsbHksIHVzZSBgbWFyZ3JpdHRyYCdzIGAlPD4lYCBjb21tYW5kLCBtZWFuaW5nOiBBc3NpZ24gYW5kIHBpcGUuCgpgYGB7cn0KIyBUaGlzIHdpbGwgY3JlYXRlIGFuIG91dHB1dCwgYnV0IG5vdCBjaGFuZ2UgeAp4ICU+JQogIGZpbHRlcihudW1iZXJzID4gNSkKCiMgVGhpcyB3aWxsIHJlLWFzc2lnbiB4CnggPC0geCAlPiUKICBmaWx0ZXIobnVtYmVycyA+IDUpCiMgaXMgZXF1aXZhbGVudCB0bwp4ICU8PiUKICBmaWx0ZXIobnVtYmVycyA+IDUpIApgYGAKCkluIGNvbmNsdXNpb246IFRoZSBwaXBlIGJhc2ljYWxseSBwYXNzZXMgb24gZGF0YWZyYW1lIGJldHdlZW4gZnVuY3Rpb25zIGluIHRoZSBmb2xsb3dpbmcgd2F5OgpgYGB7ciwgZXZhbCA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMgT25seSBwc2V1ZG8gY29kZSBoZXJlLCBkb2VzIG5vdCBydW4KeCAlPiUgZnVuKG5hLnJtID0gVFJVRSkgJT4lCiAgZmlsdGVyKCkgJT4lCgojIElzIGVxdWl2YWxlbnQgdG8KZnVuKHgsIG5hLnJtID0gVFJVRSkKCiMgV2hpbGUKeCAlPD4lIGZ1bigpCiMgSXMgZXF1aXZhbGVudCB0bwp4IDwtIGZ1bih4KQpgYGAKClBpcGluZyBhbHNvIHByb3ZpZGVzIGJldHRlciBvdmVydmlldyBvdmVyIHRoZSBmbG93IG9mIGFjdGlvbnMgYXMgY29tcGFyZWQgdG8gbmVzdGVkIGZ1bmN0aW9ucwoKYGBge3IsIGV2YWwgPSBGQUxTRX0KIyBOZXN0ZWQgZnVuY3Rpb25zCndlbnRfdG9fYmVkKGhhZF9kaW5uZXIocHJvZ3JhbW1lZF9zb21lX3IoaGFkX2x1bmNoKHByb2dyYW1tZWRfc29tZV9yKGhhZF9icmVrZmFzdChnb3RfdXAoZGF5KSkpKSkpKQoKIyB2cyBwaXBlcwpkYXkgJT4lCiAgZ290X3VwKCkgJT4lCiAgaGFkX2JyZWFrZmFzdCgpICU+JQogIHByb2dyYW1tZWRfc29tZV9yKCkgJT4lCiAgaGFkX2x1bmNoKCkgJT4lCiAgcHJvZ3JhbW1lZF9zb21lX3IoKSAlPiUKICBoYWRfZGlubmVyKCkgJT4lCiAgd2VudF90b19iZWQoKQpgYGAKCgojIyBIYW5kbGluZyBzcGVjaWFsIGRhdGEgZm9ybWF0cwoKSXQgaXMgbm90IHBhcnQgb2YgdGhpcyBpbnRyb2R1Y3RvcnkgbGVjdHVyZSwgYnV0IHlvdSBzb29uIG1pZ2h0IGVuY291bnRlciB0aGF0IHlvdSBoYXZlIHRvIGRlYWwgd2l0aCAyIGNvbW1vbiBmb3JtYXRzIGluIHNvbWUgd2F5LCB3aGljaCBhcmUgZGF0ZS10aW1lcyAodGltZS1jb2RlcykgYW5kIHN0cmluZ3MgKHRleHQpLiBXaGVuIHRoYXQgcG9pbnQgY29tZXMsIGp1c3QgY2hlY2sgdGhlIGZvbGxvd2luZyB0byBnZXQgc3RhcnRlZCAoYW5kIGlmIG5lY2Vzc2FyeSBicmFuY2ggb3V0IHRvIGZ1cnRoZXIgc291cmNlcyBzdWdnZXN0ZWQpOgoKKiAqKlN0cmluZ3M6KiogUiBmb3IgRGF0YSBTY2llbmNlIChHcm9sZW11bmQgJiBXaWNraGFtKSBbQ2hhcHRlciAxNF0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9zdHJpbmdzLmh0bWwpCiogKipEYXRlVGltZXM6KiogUiBmb3IgRGF0YSBTY2llbmNlIChHcm9sZW11bmQgJiBXaWNraGFtKSBbQ2hhcHRlciAxNl0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9kYXRlcy1hbmQtdGltZXMuaHRtbCkKCgojIEFkaXR0aW9uYWwgSW5mb3MKCiMjIFIsIG5vdGVib29rcyAmIG1hcmtkb3duCgpXaGlsZSBtYW55IHBlb3BsZSBwcmVmZXIgdG8gd29yayB3aXRoIGBSYCBzY3JpcHRzLCBjb21wdXRpb25hbCBub3RlYm9va3MgYXJlIGVzcGVjaWFsbHkgaW4gdGhlIGRhdGEgc2NpZW5jZSBjb21tdW5pdHkgbW9yZSBwb3B1bGFyIGZvciBgUmAgdXNlcnMuIFRoaXMgaXMgbWFpbmx5IGRvbiBpbiB0aGUgYFJtYXJrZG93bmAgZm9ybWF0LCB3aGljaCBjb21iaW5lcyBtYXJrZG93biBtYXJrdXAgYW4gbm90YXRpb24gd2l0aCBleGVjdXRhYmxlIGNvZGUgYW5kIHJlc3VsdCBvdXRwdXRzLiBBbGwgdGhlIHBldHR5IGh0bWwgbm90ZWJvb2tzIGkgY3JlYXRlIGZvIHlvdSBhcmUgYWxzbyBkb25lIGluIHRoYXQgd2F5LiBGb3IgZnVydGhlciBpbmZvcm1hdGlvbiBhbmQgdG8gZ2V0IHN0YXJ0ZWQsIGNoZWNrOgoKKiBCYXNpY3M6IFIgZm9yIERhdGEgU2NpZW5jZSAoR3JvbGVtdW5kICYgV2lja2hhbSkgW0NoYXB0ZXIgMjddKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovci1tYXJrZG93bi5odG1sKQoqIERldGFpbHM6IFtSIE1hcmtkb3duOiBUaGUgRGVmaW5pdGl2ZSBHdWlkZSAoWGllLCBBbGxhaXJlICYgR3JvbGVtdW5kKV0oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duLykKCgojIyBSLCBnb29nbGUgY29sYWIgJiBjby4KCkdvb2dsZSBjb2xhYiBkb2VzIG5vdCBvZmZpY2lhbGx5IHN1cHBvcnQgYFJgIGtlcm5lbHMuIEhvd2V2ZXIsIHRoZXJlIGlzIGEgbGl0dGxlIHRyaWNrIGhvdyB0byBtYWtlIGBSYCBydW4gd2l0aCBjb2xhYi4gCgpJZiB5b3Ugd2FudCB0byBzdGFydCBmcm9tIHNjcmF0Y2gsIGRvIHRoZSBmb2xsb3dpbmc6CgoqIFlvdSBjYW4gc2ltcGx5IHJ1biB0aGUgYGRlbW8uaXB5bmJgIGZyb20gW0lSa2VybmVsIEdpdGh1Yl0oaHR0cHM6Ly9jb2xhYi5yZXNlYXJjaC5nb29nbGUuY29tL2dpdGh1Yi9JUmtlcm5lbC9JUmtlcm5lbC9ibG9iL21hc3Rlci9leGFtcGxlLW5vdGVib29rcy9EZW1vLmlweW5iKQoqIE1ha2UgY2hhbmdlcyBhbmQgdGhlbiBzYXZlIGEgY29weSB0byB5b3VyIEdvb2dsZSBEcml2ZS4KKiBZb3UgY2FuIGFsc28gc2VlIGFsbCAzIGV4YW1wbGUgbm90ZWJvb2tzIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vSVJrZXJuZWwvSVJrZXJuZWwvdHJlZS9tYXN0ZXIvZXhhbXBsZS1ub3RlYm9va3MpLgoKSWYgeW91IGFscmVhZHkgaGF2ZSBhbiBSLU1hcmtkb3duIG5vdGVib29rOgoKKiB1c2UgdGhlIFtJUmtlcm5lbF0oaHR0cHM6Ly9naXRodWIuY29tL0lSa2VybmVsL0lSa2VybmVsKSB0byBjcmVhdGUgYSBgLmlweW5iYCBvdXQgb2YgeW91ciBgLnJtZGAKKiBJZiB5b3UgZG9udCB3YW50IHRvIGRvIGl0IGxvY2FsbHksIHVzZSBbdGhpcyBjb2xhYiBub3RlYm9va10oaHR0cHM6Ly9jb2xhYi5yZXNlYXJjaC5nb29nbGUuY29tL2dpdGh1Yi9TRFMtQUFVL1NEUy1tYXN0ZXIvYmxvYi9tYXN0ZXIvMDBfbm90ZWJvb2tzL2NvbnZlcnRlcl9ybWRfdG9fanVweXRlci5pcHluYikgaW5zdGVhZC4gSnVzdCB1cGxvYWQgdGhlIGAucm1kYCwgcnVuIHRoZSBjb2RlIChhbHRlciB0aGUgZmlsZW5hbWUpLCBhbmQgZG93bmxvYWQgdGhlIHJlc3VsdGluZyBgLmlweW5iYC4gVGhpcyBub3cgY2FuIGJlIHVwbG9hZGVkIHRvIGNvbGFiLgoKIyBFbmRub3RlcwoKIyMjIFJlZmVyZW5jZXMKCiMjIyBGdXJ0aGVyIGluZm9zCgojIyMgU2Vzc2lvbiBJbmZvCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYGA=