Background

R is a language, or an environment, for data analysis and visualization.

R is derived form the [S language](https://en.wikipedia.org/wiki/S_(programming_language) developed at ATT Bell Laboratories.

R was originally developed for teaching at the University of Auckland, New Zealand, by Ross Ihaka and Robert Gentleman.

R is now maintained by an international group of about 20 statisticians and computer scientists.

A great strength of R is the large number of extension packages that have been developed.

The number available on CRAN is now over 19,000.

Basic Usage

Interactive R uses a command line interface (CLI).

The interface runs a read-evaluate-print loop (REPL).

A simple interaction with the R interpreter:

> 1 + 2
[1] 3

Values can be assigned to variables using a left arrow <- combination:

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

The = sign can also be used for assignment, but <- is recommended.

Basic arithmetic operations work element-wise on vectors:

> x + x
[1]  2  6 10

Scalars are recycled to the length of the longer operand:

> x + 1
[1] 2 4 6
> 2 * x
[1]  2  6 10

Some ways to create new vectors:

> c(1, 2, 3)
[1] 1 2 3
> c("a", "b", "c")
[1] "a" "b" "c"
> 1 : 3
[1] 1 2 3

These examples show a prompt as you would see in the interpreter.

Usually Rmarkdown documents show code and results like this:

2 * x
## [1]  2  6 10

This makes it easier to copy code for pasting it into another document or the R console.

Data Frames

Data sets in R are often organized in named lists of variables called data frames.

The value of the variable faithful is a data frame with two variables recorded for eruptions of the Old Faithful geyser in Yellowstone National Park:

head() shows the first 6 rows:

head(faithful)
##   eruptions waiting
## 1     3.600      79
## 2     1.800      54
## 3     3.333      74
## 4     2.283      62
## 5     4.533      85
## 6     2.883      55

A Simple Scatter Plot

with(faithful,
     plot(eruptions, waiting,
          xlab = "Eruption time (min)",
          ylab = "Waiting time to next eruption (min)"))

Several graphics systems are available for R.

plot() is part of base graphics.

Fitting a Linear Regression

fit <- with(faithful, lm(waiting ~ eruptions))
fit
## 
## Call:
## lm(formula = waiting ~ eruptions)
## 
## Coefficients:
## (Intercept)    eruptions  
##       33.47        10.73

You can also use the data argument to lm():

fit <- lm(waiting ~ eruptions, data = faithful)

coef() extracts the coefficients:

coef(fit)
## (Intercept)   eruptions 
##    33.47440    10.72964

summary(fit) provides more details:

summary(fit)
## 
## Call:
## lm(formula = waiting ~ eruptions, data = faithful)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -12.0796  -4.4831   0.2122   3.9246  15.9719 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  33.4744     1.1549   28.98   <2e-16 ***
## eruptions    10.7296     0.3148   34.09   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 5.914 on 270 degrees of freedom
## Multiple R-squared:  0.8115, Adjusted R-squared:  0.8108 
## F-statistic:  1162 on 1 and 270 DF,  p-value: < 2.2e-16

Adding the Regression Line to the Plot

Original plot:

with(faithful,
     plot(eruptions, waiting,
          xlab = "Eruption time (min)",
          ylab = "Waiting time to next eruption (min)"))

With a regression line:

with(faithful,
     plot(eruptions, waiting,
          xlab = "Eruption time (min)",
          ylab = "Waiting time to next eruption (min)"))
abline(coef(fit), col = "red", lwd = 3)

Packages and Package Libraries

Extension code and data sets are often made available in packages.

Packages are stored in folders or directories as collections called libraries.

.libPaths() will show you the libraries your R process will search.

search() shows what packages are attached to the global search path.

The library() function is used to find packages in the libraries and attach them to the search path.

The expression pkg::var gets the value of variable var from package pkg without attaching pkg.

You can install packages using the install.packages function or the Install Packages item in the RStudio Tools menu.

By default packages are installed from CRAN.

It is also possible to use functions in the remotes package to install packages hosted on GitHub or GitLab.

A Useful Package: ggplot2

The ggplot2 package provides a powerful alternative to the base graphics system.

The geyser example can be done in ggplot2 like this:

library(ggplot2)
ggplot(data = faithful) +
    geom_point(mapping = aes(x = eruptions, y = waiting)) +
    geom_smooth(mapping = aes(x = eruptions, y = waiting),
                method = "lm", se = FALSE)

ggplot2 is part of a useful collection of packages called the tidyverse.

ggplot is based on the Grammar of Graphics.

  • Plots are composed of geometric objects (geoms).

  • Variables are mapped to aesthetic features of geometric objects.

A basic template for creating a plot with ggplot:

ggplot(data = <DATA>) + <GEOM>(mapping = aes(<MAPPINGS>))

Subsetting and Extracting Components

The subset operator [ can be used to extract element by index:

month.abb
##  [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"
month.abb[1 : 3]
## [1] "Jan" "Feb" "Mar"

Subsetting can also be based on a logical expression that returns TRUE or FALSE for each element:

(starts_with_J <- substr(month.abb, 1, 1) == "J")
##  [1]  TRUE FALSE FALSE FALSE FALSE  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE
month.abb[starts_with_J]
## [1] "Jan" "Jun" "Jul"

The value of an assignment operation is the right hand side value.

  • Ordinarily this value is not printed.
  • Placing the assignment expression in parentheses causes it to be printed.

Individual elements can be extracted using the element operator [[:

month.abb[[3]]
## [1] "Mar"

Components of named list, like data frames, can be extracted with the $ operator:

names(faithful)
## [1] "eruptions" "waiting"
head(faithful, 4)
##   eruptions waiting
## 1     3.600      79
## 2     1.800      54
## 3     3.333      74
## 4     2.283      62
head(faithful$eruptions, 4)
## [1] 3.600 1.800 3.333 2.283

The element operator can be used as well:

head(faithful[["eruptions"]], 4)
## [1] 3.600 1.800 3.333 2.283

Functions

Simple Functions

All computations in R are carried out by functions.

Defining a function allows you to avoid cutting and pasting code.

A simple function:

ms <- function(x) list(mean = mean(x), sd = sd(x))
ms(faithful$eruptions)
## $mean
## [1] 3.487783
## 
## $sd
## [1] 1.141371

Generic Functions and Object-Oriented Programming

R supports several mechanisms for object-oriented programming based on generic functions.

The most commonly used mechanism, called S3, allows a function to dispatch to a method based on the class of its first argument.

plot is a very simple generic function.

plot
## function (x, y, ...) 
## UseMethod("plot")
## <bytecode: 0x557d1ad19b50>
## <environment: namespace:base>

For example, the plot method for linear model fit objects produces a set of 4 plots commonly used to assess regression fits.

plot(fit)

Lazy and Non-Standard Evaluation

An unusual but useful feature of R is that function arguments are not evaluated until their value is needed, so they may not be evaluated at all.

This is called lazy evaluation.

log("A")
## Error in log("A"): non-numeric argument to mathematical function
f <- function(x) NULL
f(log("A"))
## NULL

Functions can also capture the expression of the arguments they were called with:

f <- function(x) deparse(substitute(x))
f(a + b)
## [1] "a + b"

Together these features allow functions to evaluate their arguments in non-standard ways.

This is most commonly used to allow values for variables in arguments to be found in a provided data frame.

The with() function is a simple example:

mean(eruptions)
## Error in eval(expr, envir, enclos): object 'eruptions' not found
with(faithful, mean(eruptions))
## [1] 3.487783

Non-standard evaluation of this type is used extensively in the tidyverse.

The Tidyverse

Tidyverse functions are designed to perform operations on data frames.

The dplyr package provides a grammar for data manipulation.

A simple example: computing means and standard deviations for the waiting times after the short (less than 3 minutes) and the long (3 minutes or more) eruptions:

library(dplyr)
tmp <- mutate(faithful,
              type = ifelse(eruptions < 3,
                            "short",
                            "long"))
head(tmp)
##   eruptions waiting  type
## 1     3.600      79  long
## 2     1.800      54 short
## 3     3.333      74  long
## 4     2.283      62 short
## 5     4.533      85  long
## 6     2.883      55 short
summarize(group_by(tmp, type),
          mean = mean(waiting),
          sd = sd(waiting))
## # A tibble: 2 × 3
##   type   mean    sd
##   <chr> <dbl> <dbl>
## 1 long   80.0  5.99
## 2 short  54.5  5.84

Tidyverse functions like to work with an enhanced form of data frame called a tibble.

A computation like this can be viewed as a transformation pipeline consisting of three stages:

Tidyverse code often uses the forward pipe operator %>% provided by the magrittr package to express such a pipeline.

R 4.1.0 and later also provides a native pipe operator |>.

The pipe operator allows a call f(x) to be written as

x %>% f()

The left hand value is passed implicitly as the first argument to the function called on the right.

Using the pipe operator, the code for computing means and standard deviations can be written as

faithful %>%
    mutate(type = ifelse(eruptions < 3, "short", "long")) %>%
    group_by(type) %>%
    summarize(mean = mean(waiting),
              sd = sd(waiting))
## # A tibble: 2 × 3
##   type   mean    sd
##   <chr> <dbl> <dbl>
## 1 long   80.0  5.99
## 2 short  54.5  5.84

There are trade-offs:

Contrast to Point-and-Click Interfaces

R and Reproducibility

Analyses in R are carried out by running code describing the tasks to perform.

This code can be

Literate data analysis tools like Rmarkdown provide support for this.

Finding Out More

Getting Help on Functions

  • help(mean) will show help for the function mean.
  • This can be abbreviated as ?mean

Some R Introductions and Tutorials

Introductions to the Tidiverse

R Markdown Tutorials

Interactive Tutorial

An interactive learnr tutorial for these notes is available.

You can run the tutorial with

STAT4580::runTutorial("Rintro")

You can install the current version of the STAT4580 package with

remotes::install_gitlab("luke-tierney/STAT4580")

You may need to install the remotes package from CRAN first.

Exercises

  1. Compute the mean of the numbers 1, 3, 5, 8.
  1. What is the mean of the eruptions variable in the faithful data frame?
  1. Find the average of the first 50 eruption durations in the faithful data frame.
  1. Use the median function to modify the pipe example in the tidyverse section to include medians.
LS0tCnRpdGxlOiAiQSBCcmllZiBPdmVydmlldyBvZiBSIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0Kc291cmNlKGhlcmU6OmhlcmUoInNldHVwLlIiKSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGNvbGxhcHNlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIGZpZy5oZWlnaHQgPSA1LCBmaWcud2lkdGggPSA2LCBmaWcuYWxpZ24gPSAiY2VudGVyIikKYGBgCjxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0ic3RhdDQ1ODAuY3NzIiB0eXBlPSJ0ZXh0L2NzcyIgLz4KCgojIyBCYWNrZ3JvdW5kCgpSIGlzIGEgbGFuZ3VhZ2UsIG9yIGFuIGVudmlyb25tZW50LCBmb3IgZGF0YSBhbmFseXNpcyBhbmQgdmlzdWFsaXphdGlvbi4KClIgaXMgZGVyaXZlZCBmb3JtIHRoZSBbX1NfCmxhbmd1YWdlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TXyhwcm9ncmFtbWluZ19sYW5ndWFnZSkKZGV2ZWxvcGVkIGF0IEFUVCBCZWxsIExhYm9yYXRvcmllcy4KClIgd2FzIG9yaWdpbmFsbHkgZGV2ZWxvcGVkIGZvciB0ZWFjaGluZyBhdCB0aGUgVW5pdmVyc2l0eSBvZiBBdWNrbGFuZCwKTmV3IFplYWxhbmQsIGJ5IFJvc3MgSWhha2EgYW5kIFJvYmVydCBHZW50bGVtYW4uCgpSIGlzIG5vdyBtYWludGFpbmVkIGJ5IGFuIGludGVybmF0aW9uYWwgZ3JvdXAgb2YgYWJvdXQgMjAKc3RhdGlzdGljaWFucyBhbmQgY29tcHV0ZXIgc2NpZW50aXN0cy4KCkEgZ3JlYXQgc3RyZW5ndGggb2YgUiBpcyB0aGUgbGFyZ2UgbnVtYmVyIG9mIGV4dGVuc2lvbiBwYWNrYWdlcyB0aGF0CmhhdmUgYmVlbiBkZXZlbG9wZWQuCgpUaGUgbnVtYmVyIGF2YWlsYWJsZSBvbiBbQ1JBTl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcpIGlzIG5vdyBvdmVyCjE5LDAwMC4KCgojIyBCYXNpYyBVc2FnZQoKSW50ZXJhY3RpdmUgUiB1c2VzIGEgX2NvbW1hbmQgbGluZSBpbnRlcmZhY2VfIChDTEkpLgoKVGhlIGludGVyZmFjZSBydW5zIGEgX3JlYWQtZXZhbHVhdGUtcHJpbnQgbG9vcF8gKFJFUEwpLgoKQSBzaW1wbGUgaW50ZXJhY3Rpb24gd2l0aCB0aGUgUiBpbnRlcnByZXRlcjoKCmBgYHtyLCBwcm9tcHQgPSBUUlVFLCBjb21tZW50ID0gIiJ9CjEgKyAyCmBgYAoKVmFsdWVzIGNhbiBiZSBhc3NpZ25lZCB0byB2YXJpYWJsZXMgdXNpbmcgYSBsZWZ0IGFycm93IGA8LWAgY29tYmluYXRpb246CgpgYGB7ciwgcHJvbXB0ID0gVFJVRSwgY29tbWVudCA9ICIifQp4IDwtIGMoMSwgMywgNSkKeApgYGAKCjxkaXYgY2xhc3MgPSAiYWxlcnQiPgpUaGUgYD1gIHNpZ24gY2FuIGFsc28gYmUgdXNlZCBmb3IgYXNzaWdubWVudCwgYnV0IGA8LWAgaXMgcmVjb21tZW5kZWQuCjwvZGl2PgoKQmFzaWMgYXJpdGhtZXRpYyBvcGVyYXRpb25zIHdvcmsgZWxlbWVudC13aXNlIG9uIHZlY3RvcnM6CgpgYGB7ciwgcHJvbXB0ID0gVFJVRSwgY29tbWVudCA9ICIifQp4ICsgeApgYGAKClNjYWxhcnMgYXJlIF9yZWN5Y2xlZF8gdG8gdGhlIGxlbmd0aCBvZiB0aGUgbG9uZ2VyIG9wZXJhbmQ6CgpgYGB7ciwgcHJvbXB0ID0gVFJVRSwgY29tbWVudCA9ICIifQp4ICsgMQpgYGAKCmBgYHtyLCBwcm9tcHQgPSBUUlVFLCBjb21tZW50ID0gIiJ9CjIgKiB4CmBgYAoKU29tZSB3YXlzIHRvIGNyZWF0ZSBuZXcgdmVjdG9yczoKCmBgYHtyLCBwcm9tcHQgPSBUUlVFLCBjb21tZW50ID0gIiJ9CmMoMSwgMiwgMykKYGBgCgpgYGB7ciwgcHJvbXB0ID0gVFJVRSwgY29tbWVudCA9ICIifQpjKCJhIiwgImIiLCAiYyIpCmBgYAoKYGBge3IsIHByb21wdCA9IFRSVUUsIGNvbW1lbnQgPSAiIn0KMSA6IDMKYGBgCgpUaGVzZSBleGFtcGxlcyBzaG93IGEgX3Byb21wdF8gYXMgeW91IHdvdWxkIHNlZSBpbiB0aGUgaW50ZXJwcmV0ZXIuCgpVc3VhbGx5IFJtYXJrZG93biBkb2N1bWVudHMgc2hvdyBjb2RlIGFuZCByZXN1bHRzIGxpa2UgdGhpczoKCmBgYHtyfQoyICogeApgYGAKClRoaXMgbWFrZXMgaXQgZWFzaWVyIHRvIGNvcHkgY29kZSBmb3IgcGFzdGluZyBpdCBpbnRvIGFub3RoZXIgZG9jdW1lbnQKb3IgdGhlIFIgY29uc29sZS4KCgojIyBEYXRhIEZyYW1lcwoKRGF0YSBzZXRzIGluIFIgYXJlIG9mdGVuIG9yZ2FuaXplZCBpbiBfbmFtZWQgbGlzdHNfIG9mIHZhcmlhYmxlcwpjYWxsZWQgX2RhdGEgZnJhbWVzXy4KClRoZSB2YWx1ZSBvZiB0aGUgdmFyaWFibGUgYGZhaXRoZnVsYCBpcyBhIGRhdGEgZnJhbWUgd2l0aCB0d28KdmFyaWFibGVzIHJlY29yZGVkIGZvciBlcnVwdGlvbnMgb2YgdGhlIF9PbGQgRmFpdGhmdWxfIGdleXNlciBpbiBZZWxsb3dzdG9uZQpOYXRpb25hbCBQYXJrOgoKKiBgZXJ1cHRpb25zYDogIEVydXB0aW9uIGR1cmF0aW9uIChtaW51dGVzKQoqIGB3YWl0aW5nYDogIFdhaXRpbmcgdGltZSB0byBuZXh0IGVydXB0aW9uIChtaW51dGVzKQoKYGhlYWQoKWAgc2hvd3MgdGhlIGZpcnN0IDYgcm93czoKCmBgYHtyfQpoZWFkKGZhaXRoZnVsKQpgYGAKCgojIyBBIFNpbXBsZSBTY2F0dGVyIFBsb3QKCmBgYHtyIGdleXNlciwgZXZhbCA9IEZBTFNFfQp3aXRoKGZhaXRoZnVsLAogICAgIHBsb3QoZXJ1cHRpb25zLCB3YWl0aW5nLAogICAgICAgICAgeGxhYiA9ICJFcnVwdGlvbiB0aW1lIChtaW4pIiwKICAgICAgICAgIHlsYWIgPSAiV2FpdGluZyB0aW1lIHRvIG5leHQgZXJ1cHRpb24gKG1pbikiKSkKYGBgCgo8IS0tICMjIG5vbGludCBzdGFydCAtLT4KYGBge3IgZXZhbCA9IFRSVUUsIGVjaG8gPSBGQUxTRSwgZmlnLmhlaWdodCA9IDR9Cm9wIDwtIHBhcihtYXIgPSBjKDQsIDQsIDAuMSwgMC4xKSkKPDxnZXlzZXI+PgpwYXIob3ApCmBgYAo8IS0tICMjIG5vbGludCBlbmQgLS0+CgpTZXZlcmFsIGdyYXBoaWNzIHN5c3RlbXMgYXJlIGF2YWlsYWJsZSBmb3IgUi4KCmBwbG90KClgIGlzIHBhcnQgb2YgX2Jhc2UgZ3JhcGhpY3NfLgoKCiMjIEZpdHRpbmcgYSBMaW5lYXIgUmVncmVzc2lvbgpgYGB7cn0KZml0IDwtIHdpdGgoZmFpdGhmdWwsIGxtKHdhaXRpbmcgfiBlcnVwdGlvbnMpKQpmaXQKYGBgCgpZb3UgY2FuIGFsc28gdXNlIHRoZSBgZGF0YWAgYXJndW1lbnQgdG8gYGxtKClgOgoKYGBge3J9CmZpdCA8LSBsbSh3YWl0aW5nIH4gZXJ1cHRpb25zLCBkYXRhID0gZmFpdGhmdWwpCmBgYAoKYGNvZWYoKWAgZXh0cmFjdHMgdGhlIGNvZWZmaWNpZW50czoKCmBgYHtyfQpjb2VmKGZpdCkKYGBgCgpgc3VtbWFyeShmaXQpYCBwcm92aWRlcyBtb3JlIGRldGFpbHM6CgpgYGB7cn0Kc3VtbWFyeShmaXQpCmBgYAoKCiMjIEFkZGluZyB0aGUgUmVncmVzc2lvbiBMaW5lIHRvIHRoZSBQbG90CgpPcmlnaW5hbCBwbG90OgoKPCEtLSAjIyBub2xpbnQgc3RhcnQgLS0+CmBgYHtyLCBldmFsID0gRkFMU0V9Cjw8Z2V5c2VyPj4KYGBgCjwhLS0gIyMgbm9saW50IGVuZCAtLT4KPCEtLSAjIyBub2xpbnQgc3RhcnQgLS0+CmBgYHtyLCBldmFsID0gVFJVRSwgZWNobyA9IEZBTFNFfQo8PGdleXNlcj4+CmBgYAo8IS0tICMjIG5vbGludCBlbmQgLS0+CgpXaXRoIGEgcmVncmVzc2lvbiBsaW5lOgoKPCEtLSAjIyBub2xpbnQgc3RhcnQgLS0+CmBgYHtyIGdleXNlci13aXRoLWxpbmUsIGV2YWwgPSBGQUxTRX0KPDxnZXlzZXI+PgphYmxpbmUoY29lZihmaXQpLCBjb2wgPSAicmVkIiwgbHdkID0gMykKYGBgCjwhLS0gIyMgbm9saW50IGVuZCAtLT4KCmBgYHtyIGdleXNlci13aXRoLWxpbmUsIGV2YWwgPSBUUlVFLCBlY2hvID0gRkFMU0V9CmBgYAoKCiMjIFBhY2thZ2VzIGFuZCBQYWNrYWdlIExpYnJhcmllcwoKRXh0ZW5zaW9uIGNvZGUgYW5kIGRhdGEgc2V0cyBhcmUgb2Z0ZW4gbWFkZSBhdmFpbGFibGUgaW4gX3BhY2thZ2VzXy4KClBhY2thZ2VzIGFyZSBzdG9yZWQgaW4gZm9sZGVycyBvciBkaXJlY3RvcmllcyBhcyBjb2xsZWN0aW9ucyBjYWxsZWQgX2xpYnJhcmllc18uCgpgLmxpYlBhdGhzKClgIHdpbGwgc2hvdyB5b3UgdGhlIGxpYnJhcmllcyB5b3VyIFIgcHJvY2VzcyB3aWxsIHNlYXJjaC4KCmBzZWFyY2goKWAgc2hvd3Mgd2hhdCBwYWNrYWdlcyBhcmUgYXR0YWNoZWQgdG8gdGhlIGdsb2JhbCBzZWFyY2ggcGF0aC4KClRoZSBgbGlicmFyeSgpYCBmdW5jdGlvbiBpcyB1c2VkIHRvIGZpbmQgcGFja2FnZXMgaW4gdGhlIGxpYnJhcmllcyBhbmQKYXR0YWNoIHRoZW0gdG8gdGhlIHNlYXJjaCBwYXRoLgoKVGhlIGV4cHJlc3Npb24gYHBrZzo6dmFyYCBnZXRzIHRoZSB2YWx1ZSBvZiB2YXJpYWJsZSBgdmFyYCBmcm9tCnBhY2thZ2UgYHBrZ2Agd2l0aG91dCBhdHRhY2hpbmcgYHBrZ2AuCgpZb3UgY2FuIGluc3RhbGwgcGFja2FnZXMgdXNpbmcgdGhlIGBpbnN0YWxsLnBhY2thZ2VzYCBmdW5jdGlvbiBvcgp0aGUgKipJbnN0YWxsIFBhY2thZ2VzKiogaXRlbSBpbiB0aGUgUlN0dWRpbyAqKlRvb2xzKiogbWVudS4KCkJ5IGRlZmF1bHQgcGFja2FnZXMgYXJlIGluc3RhbGxlZCBmcm9tIFtDUkFOXShjcmFuLnItcHJvamVjdC5vcmcpLgoKSXQgaXMgYWxzbyBwb3NzaWJsZSB0byB1c2UgZnVuY3Rpb25zIGluIHRoZSBgcmVtb3Rlc2AgcGFja2FnZSB0bwppbnN0YWxsIHBhY2thZ2VzIGhvc3RlZCBvbiBbR2l0SHViXShodHRwczovL2dpdGh1Yi5jb20vKSBvcgpbR2l0TGFiXShodHRwczovL2Fib3V0LmdpdGxhYi5jb20vKS4KCgojIyBBIFVzZWZ1bCBQYWNrYWdlOiBgZ2dwbG90MmAKClRoZSBgZ2dwbG90MmAgcGFja2FnZSBwcm92aWRlcyBhIHBvd2VyZnVsIGFsdGVybmF0aXZlIHRvIHRoZSBiYXNlCmdyYXBoaWNzIHN5c3RlbS4KClRoZSBnZXlzZXIgZXhhbXBsZSBjYW4gYmUgZG9uZSBpbiBgZ2dwbG90MmAgbGlrZSB0aGlzOgoKYGBge3IgZ2V5c2VyLWdncGxvdCwgZXZhbCA9IEZBTFNFLCBlY2hvID0gVFJVRX0KbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QoZGF0YSA9IGZhaXRoZnVsKSArCiAgICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGVydXB0aW9ucywgeSA9IHdhaXRpbmcpKSArCiAgICBnZW9tX3Ntb290aChtYXBwaW5nID0gYWVzKHggPSBlcnVwdGlvbnMsIHkgPSB3YWl0aW5nKSwKICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpCmBgYApgYGB7ciBnZXlzZXItZ2dwbG90LCBldmFsID0gVFJVRSwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmBgYAoKYGdncGxvdDJgIGlzIHBhcnQgb2YgYSB1c2VmdWwgY29sbGVjdGlvbiBvZiBwYWNrYWdlcyBjYWxsZWQgdGhlCltfdGlkeXZlcnNlX10oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy8pLgoKPGRpdiBjbGFzcz0iYWxlcnQiPgpgZ2dwbG90YCBpcyBiYXNlZCBvbiB0aGUgX0dyYW1tYXIgb2YgR3JhcGhpY3NfLgoKKiBQbG90cyBhcmUgY29tcG9zZWQgb2YgX2dlb21ldHJpYyBvYmplY3RzXyAoYGdlb21zYCkuCgoqIFZhcmlhYmxlcyBhcmUgX21hcHBlZF8gdG8gX2Flc3RoZXRpYyBmZWF0dXJlc18gb2YgZ2VvbWV0cmljIG9iamVjdHMuCgpBIGJhc2ljIHRlbXBsYXRlIGZvciBjcmVhdGluZyBhIHBsb3Qgd2l0aCBgZ2dwbG90YDoKCjwhLS0gIyBub2xpbnQgc3RhcnQgLS0+CmBgYHtyLCBldmFsID0gRkFMU0V9CmdncGxvdChkYXRhID0gPERBVEE+KSArIDxHRU9NPihtYXBwaW5nID0gYWVzKDxNQVBQSU5HUz4pKQpgYGAKPCEtLSAjIG5vbGludCBlbmQgLS0+CjwvZGl2PgoKCiMjIFN1YnNldHRpbmcgYW5kIEV4dHJhY3RpbmcgQ29tcG9uZW50cwoKVGhlIF9zdWJzZXQgb3BlcmF0b3JfIGBbYCBjYW4gYmUgdXNlZCB0byBleHRyYWN0IGVsZW1lbnQgYnkgaW5kZXg6CgpgYGB7cn0KbW9udGguYWJiCm1vbnRoLmFiYlsxIDogM10KYGBgCgpTdWJzZXR0aW5nIGNhbiBhbHNvIGJlIGJhc2VkIG9uIGEgbG9naWNhbCBleHByZXNzaW9uIHRoYXQgcmV0dXJucwpgVFJVRWAgb3IgYEZBTFNFYCBmb3IgZWFjaCBlbGVtZW50OgoKYGBge3J9CihzdGFydHNfd2l0aF9KIDwtIHN1YnN0cihtb250aC5hYmIsIDEsIDEpID09ICJKIikKbW9udGguYWJiW3N0YXJ0c193aXRoX0pdCmBgYAoKPGRpdiBjbGFzcyA9ICJhbGVydCI+ClRoZSB2YWx1ZSBvZiBhbiBhc3NpZ25tZW50IG9wZXJhdGlvbiBpcyB0aGUgcmlnaHQgaGFuZCBzaWRlIHZhbHVlLgoKKiBPcmRpbmFyaWx5IHRoaXMgdmFsdWUgaXMgbm90IHByaW50ZWQuCiogUGxhY2luZyB0aGUgYXNzaWdubWVudCBleHByZXNzaW9uIGluIHBhcmVudGhlc2VzIGNhdXNlcyBpdCB0byBiZQogIHByaW50ZWQuCjwvZGl2PgoKSW5kaXZpZHVhbCBlbGVtZW50cyBjYW4gYmUgZXh0cmFjdGVkIHVzaW5nIHRoZSBfZWxlbWVudCBvcGVyYXRvcl8gYFtbYDoKCmBgYHtyfQptb250aC5hYmJbWzNdXQpgYGAKCkNvbXBvbmVudHMgb2YgbmFtZWQgbGlzdCwgbGlrZSBfZGF0YSBmcmFtZXNfLCBjYW4gYmUgZXh0cmFjdGVkIHdpdGgKdGhlIGAkYCBvcGVyYXRvcjoKCmBgYHtyfQpuYW1lcyhmYWl0aGZ1bCkKaGVhZChmYWl0aGZ1bCwgNCkKaGVhZChmYWl0aGZ1bCRlcnVwdGlvbnMsIDQpCmBgYAoKVGhlIGVsZW1lbnQgb3BlcmF0b3IgY2FuIGJlIHVzZWQgYXMgd2VsbDoKCmBgYHtyfQpoZWFkKGZhaXRoZnVsW1siZXJ1cHRpb25zIl1dLCA0KQpgYGAKCgojIyBGdW5jdGlvbnMKCgojIyMgU2ltcGxlIEZ1bmN0aW9ucwoKQWxsIGNvbXB1dGF0aW9ucyBpbiBSIGFyZSBjYXJyaWVkIG91dCBieSBmdW5jdGlvbnMuCgpEZWZpbmluZyBhIGZ1bmN0aW9uIGFsbG93cyB5b3UgdG8gYXZvaWQgY3V0dGluZyBhbmQgcGFzdGluZyBjb2RlLgoKQSBzaW1wbGUgZnVuY3Rpb246CgpgYGB7cn0KbXMgPC0gZnVuY3Rpb24oeCkgbGlzdChtZWFuID0gbWVhbih4KSwgc2QgPSBzZCh4KSkKbXMoZmFpdGhmdWwkZXJ1cHRpb25zKQpgYGAKCgojIyMgR2VuZXJpYyBGdW5jdGlvbnMgYW5kIE9iamVjdC1PcmllbnRlZCBQcm9ncmFtbWluZwoKUiBzdXBwb3J0cyBzZXZlcmFsIG1lY2hhbmlzbXMgZm9yIG9iamVjdC1vcmllbnRlZCBwcm9ncmFtbWluZyBiYXNlZCBvbgpfZ2VuZXJpYyBmdW5jdGlvbnNfLgoKVGhlIG1vc3QgY29tbW9ubHkgdXNlZCBtZWNoYW5pc20sIGNhbGxlZCBTMywgYWxsb3dzIGEgZnVuY3Rpb24gdG8KX2Rpc3BhdGNoXyB0byBhIF9tZXRob2RfIGJhc2VkIG9uIHRoZSBfY2xhc3NfIG9mIGl0cyBmaXJzdCBhcmd1bWVudC4KCmBwbG90YCBpcyBhIHZlcnkgc2ltcGxlIGdlbmVyaWMgZnVuY3Rpb24uCgpgYGB7cn0KcGxvdApgYGAKCkZvciBleGFtcGxlLCB0aGUgYHBsb3RgIG1ldGhvZCBmb3IgbGluZWFyIG1vZGVsIGZpdCBvYmplY3RzIHByb2R1Y2VzIGEKc2V0IG9mIDQgcGxvdHMgY29tbW9ubHkgdXNlZCB0byBhc3Nlc3MgcmVncmVzc2lvbiBmaXRzLgoKYGBge3IgZ2V5c2VyX2xtX2ZpdCwgZXZhbCA9IEZBTFNFfQpwbG90KGZpdCkKYGBgCmBgYHtyLCBlY2hvID0gRkFMU0UsIGZpZy5oZWlnaHQgPSA2LjUsIGZpZy53aWR0aCA9IDd9Cm9wIDwtIHBhcihtZnJvdyA9IGMoMiwgMikpCjw8Z2V5c2VyX2xtX2ZpdD4+CnBhcihvcCkKYGBgCgoKIyMjIExhenkgYW5kIE5vbi1TdGFuZGFyZCBFdmFsdWF0aW9uCgpBbiB1bnVzdWFsIGJ1dCB1c2VmdWwgZmVhdHVyZSBvZiBSIGlzIHRoYXQgZnVuY3Rpb24gYXJndW1lbnRzIGFyZSBub3QKZXZhbHVhdGVkIHVudGlsIHRoZWlyIHZhbHVlIGlzIG5lZWRlZCwgc28gdGhleSBtYXkgbm90IGJlIGV2YWx1YXRlZCBhdAphbGwuCgpUaGlzIGlzIGNhbGxlZCBfbGF6eSBldmFsdWF0aW9uXy4KCmBgYHtyLCBlcnJvciA9IFRSVUV9CmxvZygiQSIpCmYgPC0gZnVuY3Rpb24oeCkgTlVMTApmKGxvZygiQSIpKQpgYGAKCkZ1bmN0aW9ucyBjYW4gYWxzbyBjYXB0dXJlIHRoZSBleHByZXNzaW9uIG9mIHRoZSBhcmd1bWVudHMgdGhleSB3ZXJlCmNhbGxlZCB3aXRoOgoKYGBge3J9CmYgPC0gZnVuY3Rpb24oeCkgZGVwYXJzZShzdWJzdGl0dXRlKHgpKQpmKGEgKyBiKQpgYGAKClRvZ2V0aGVyIHRoZXNlIGZlYXR1cmVzIGFsbG93IGZ1bmN0aW9ucyB0byBldmFsdWF0ZSB0aGVpciBhcmd1bWVudHMgaW4KX25vbi1zdGFuZGFyZF8gd2F5cy4KClRoaXMgaXMgbW9zdCBjb21tb25seSB1c2VkIHRvIGFsbG93IHZhbHVlcyBmb3IgdmFyaWFibGVzIGluIGFyZ3VtZW50cwp0byBiZSBmb3VuZCBpbiBhIHByb3ZpZGVkIGRhdGEgZnJhbWUuCgpUaGUgYHdpdGgoKWAgZnVuY3Rpb24gaXMgYSBzaW1wbGUgZXhhbXBsZToKCmBgYHtyLCBlcnJvciA9IFRSVUV9Cm1lYW4oZXJ1cHRpb25zKQpgYGAKCmBgYHtyfQp3aXRoKGZhaXRoZnVsLCBtZWFuKGVydXB0aW9ucykpCmBgYAoKTm9uLXN0YW5kYXJkIGV2YWx1YXRpb24gb2YgdGhpcyB0eXBlIGlzIHVzZWQgZXh0ZW5zaXZlbHkgaW4gdGhlIF90aWR5dmVyc2VfLgoKCiMjIFRoZSBUaWR5dmVyc2UKCltfVGlkeXZlcnNlX10oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy8pIGZ1bmN0aW9ucyBhcmUgZGVzaWduZWQgdG8KcGVyZm9ybSBvcGVyYXRpb25zIG9uIGRhdGEgZnJhbWVzLgoKVGhlIGBkcGx5cmAgcGFja2FnZSBwcm92aWRlcyBhIF9ncmFtbWFyIGZvciBkYXRhIG1hbmlwdWxhdGlvbl8uCgpBIHNpbXBsZSBleGFtcGxlOiBjb21wdXRpbmcgbWVhbnMgYW5kIHN0YW5kYXJkIGRldmlhdGlvbnMgZm9yIHRoZQp3YWl0aW5nIHRpbWVzIGFmdGVyIHRoZSBzaG9ydCAobGVzcyB0aGFuIDMgbWludXRlcykgYW5kIHRoZSBsb25nICgzCm1pbnV0ZXMgb3IgbW9yZSkgZXJ1cHRpb25zOgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRX0KbGlicmFyeShkcGx5cikKdG1wIDwtIG11dGF0ZShmYWl0aGZ1bCwKICAgICAgICAgICAgICB0eXBlID0gaWZlbHNlKGVydXB0aW9ucyA8IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2hvcnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvbmciKSkKaGVhZCh0bXApCmBgYApgYGB7cn0Kc3VtbWFyaXplKGdyb3VwX2J5KHRtcCwgdHlwZSksCiAgICAgICAgICBtZWFuID0gbWVhbih3YWl0aW5nKSwKICAgICAgICAgIHNkID0gc2Qod2FpdGluZykpCmBgYAo8ZGl2IGNsYXNzID0gImFsZXJ0Ij4KVGlkeXZlcnNlIGZ1bmN0aW9ucyBsaWtlIHRvIHdvcmsgd2l0aCBhbiBlbmhhbmNlZCBmb3JtIG9mIGRhdGEgZnJhbWUKY2FsbGVkIGEgX3RpYmJsZV8uCjwvZGl2PgoKQSBjb21wdXRhdGlvbiBsaWtlIHRoaXMgY2FuIGJlIHZpZXdlZCBhcyBhIF90cmFuc2Zvcm1hdGlvbiBwaXBlbGluZV8KY29uc2lzdGluZyBvZiB0aHJlZSBzdGFnZXM6CgoqIG11dGF0aW9uIChhZGRpbmcgYSBuZXcgdmFyaWFibGUpCiogZ3JvdXBpbmcgKHNwbGl0dGluZyBieSBgdHlwZWApCiogc3VtbWFyaXppbmcgd2l0aGluIGdyb3Vwcy4KICAKClRpZHl2ZXJzZSBjb2RlIG9mdGVuIHVzZXMgdGhlIF9mb3J3YXJkIHBpcGUgb3BlcmF0b3JfIGAlPiVgIHByb3ZpZGVkIGJ5CnRoZSBgbWFncml0dHJgIHBhY2thZ2UgdG8gZXhwcmVzcyBzdWNoIGEgcGlwZWxpbmUuCgpSIDQuMS4wIGFuZCBsYXRlciBhbHNvIHByb3ZpZGVzIGEgX25hdGl2ZSBwaXBlIG9wZXJhdG9yXyBgfD5gLgoKVGhlIHBpcGUgb3BlcmF0b3IgYWxsb3dzIGEgY2FsbCBgZih4KWAgdG8gYmUgd3JpdHRlbiBhcwoKYGBge3IsIGV2YWwgPSBGQUxTRX0KeCAlPiUgZigpCmBgYAoKVGhlIGxlZnQgaGFuZCB2YWx1ZSBpcyBwYXNzZWQgaW1wbGljaXRseSBhcyB0aGUgZmlyc3QgYXJndW1lbnQgdG8gdGhlCmZ1bmN0aW9uIGNhbGxlZCBvbiB0aGUgcmlnaHQuCgpVc2luZyB0aGUgcGlwZSBvcGVyYXRvciwgdGhlIGNvZGUgZm9yIGNvbXB1dGluZyBtZWFucyBhbmQgc3RhbmRhcmQKZGV2aWF0aW9ucyBjYW4gYmUgd3JpdHRlbiBhcwoKYGBge3J9CmZhaXRoZnVsICU+JQogICAgbXV0YXRlKHR5cGUgPSBpZmVsc2UoZXJ1cHRpb25zIDwgMywgInNob3J0IiwgImxvbmciKSkgJT4lCiAgICBncm91cF9ieSh0eXBlKSAlPiUKICAgIHN1bW1hcml6ZShtZWFuID0gbWVhbih3YWl0aW5nKSwKICAgICAgICAgICAgICBzZCA9IHNkKHdhaXRpbmcpKQpgYGAKClRoZXJlIGFyZSB0cmFkZS1vZmZzOgoKKiBNYW5pcHVsYXRpb24gcGlwZWxpbmVzIGV4cHJlc3NlZCB0aGlzIHdheSBhcmUgb2Z0ZW4gbW9yZSBjb21wYWN0CiAgdGhhbiBvbmVzIHVzaW5nIGludGVybWVkaWF0ZSB2YXJpYWJsZXMgYW5kL29yIG5lc3RlZCBjYWxscy4KCiogV2l0aCBwaXBlIG5vdGF0aW9uIHRoZXJlIGlzIG5vIG5lZWQgdG8gY29tZSB1cCB3aXRoIGludGVybWVkaWF0ZQogIHZhcmlhYmxlIG5hbWVzLgoKKiBQaXBlIG5vdGF0aW9uIG9ic2N1cmVzIHRoZSBmdW5jdGlvbiBjYWxscyB0aGF0IGFyZSBhY3R1YWxseQogIGhhcHBlbmluZyBhbmQgdGhpcyBjYW4gbWFrZSBkZWJ1Z2dpbmcgaGFyZGVyLgoKCiMjIENvbnRyYXN0IHRvIFBvaW50LWFuZC1DbGljayBJbnRlcmZhY2VzCgoqIEV2ZW4gc2ltcGxlIHRhc2tzIHJlcXVpcmUgbGVhcm5pbmcgc29tZSBvZiB0aGUgUiBsYW5ndWFnZS4KCiogT25jZSB5b3UgY2FuIGRvIHNpbXBsZSB0YXNrcywgeW91IGhhdmUgbGVhcm5lZCBzb21lIG9mIHRoZSBSIGxhbmd1YWdlLgoKKiBNb3JlIGNvbXBsaWNhdGVkIHRhc2tzIGJlY29tZSBlYXNpZXIuCgoqIEV2ZW4gdmVyeSBjb21wbGljYXRlZCB0YXNrcyBiZWNvbWUgcG9zc2libGUuCgoKIyMgUiBhbmQgUmVwcm9kdWNpYmlsaXR5CgpBbmFseXNlcyBpbiBSIGFyZSBjYXJyaWVkIG91dCBieSBydW5uaW5nIGNvZGUgZGVzY3JpYmluZyB0aGUgdGFza3MgdG8KcGVyZm9ybS4KClRoaXMgY29kZSBjYW4gYmUKCiogYXVkaXRlZCB0byBtYWtlIHN1cmUgdGhlIGFuYWx5c2lzIGlzIHJpZ2h0OwoKKiByZXBsYXllZCB0byBtYWtlIHN1cmUgdGhlIHJlc3VsdHMgYXJlIHJlcG9kdWNpYmxlOwoKKiByZXVzZWQgYWZ0ZXIgY2hhbmdlcyBpbiB0aGUgZGF0YSBvciBvbiBuZXcgZGF0YS4KCl9MaXRlcmF0ZSBkYXRhIGFuYWx5c2lzXyB0b29scyBsaWtlIFJtYXJrZG93biBwcm92aWRlIHN1cHBvcnQgZm9yCnRoaXMuCgoKIyMgRmluZGluZyBPdXQgTW9yZQoKCiMjIyBHZXR0aW5nIEhlbHAgb24gRnVuY3Rpb25zCgoqIGBoZWxwKG1lYW4pYCB3aWxsIHNob3cgaGVscCBmb3IgdGhlIGZ1bmN0aW9uIGBtZWFuYC4KKiBUaGlzIGNhbiBiZSBhYmJyZXZpYXRlZCBhcyBgP21lYW5gCgoKIyMjIFNvbWUgUiBJbnRyb2R1Y3Rpb25zIGFuZCBUdXRvcmlhbHMKCiogW0FuIEludHJvZHVjdGlvbiB0byBSXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9kb2MvbWFudWFscy9SLWludHJvLmh0bWwpCiAgaW50cm9kdWNlcyB0aGUgbGFuZ3VhZ2UgYW5kIHNob3dzIGhvdyB0byB1c2UgUiBmb3IKICBzdGF0aXN0aWNhbCBhbmFseXNpcyBhbmQgZ3JhcGhpY3MuCiogQW5vdGhlcgogIFtpbnRyb2R1Y3Rpb24gdG8gUl0oaHR0cDovL3pvb25lazIuZnJlZS5mci9VTklYLzQ4X1IvYWxsLmh0bWwpIGJ5CiAgVmluY2VudCBab29uZWt5bmQuCiogW1F1aWNrLVJdKGh0dHBzOi8vd3d3LnN0YXRtZXRob2RzLm5ldC8pIHdlYiBzaXRlIHJlbGF0ZWQgdG8gKlIKICBpbiBBY3Rpb24qIGJvb2suCiogW1IgRm9yCiAgQmVnaW5uZXJzXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9kb2MvY29udHJpYi9QYXJhZGlzLXJkZWJ1dHNfZW4ucGRmKS4KKiBbVHJ5Ul0oaHR0cHM6Ly93d3cucGx1cmFsc2lnaHQuY29tL3NlYXJjaD9xPVIpIGF0IENvZGVzY2hvb2wuCiogW3N3aXJsOiBMZWFybiBSIGluIFJdKGh0dHBzOi8vc3dpcmxzdGF0cy5jb20vKS4KKiBbX0hhbmRzLU9uIFByb2dyYW1taW5nIHdpdGggUl9dKGh0dHBzOi8vcnN0dWRpby1lZHVjYXRpb24uZ2l0aHViLmlvL2hvcHIvKS4KKiBbX1IgZm9yIERhdGEgU2NpZW5jZV9dKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovKS4KKiBbRGF0YSBTY2llbmNlIERvam8gWW91VHViZQogIHR1dG9yaWFsc10oaHR0cHM6Ly93d3cueW91dHViZS5jb20vYy9EYXRhc2NpZW5jZWRvam8vcGxheWxpc3RzP3ZpZXc9NTAmc29ydD1kZCZzaGVsZl9pZD0yKS4KKiBbVHV0b3JpYWxzIGFkIFJTdHVkaW9dKGh0dHBzOi8vZWR1Y2F0aW9uLnJzdHVkaW8uY29tL2xlYXJuLykuCiogW1IgZm9yIHRoZSBSZXN0IG9mIFVzXShodHRwczovL3Jmb3J0aGVyZXN0b2Z1cy5jb20vKS4KKiBUaGVyZSBhcmUgX21hbnlfIG90aGVycy4KCgojIyMgSW50cm9kdWN0aW9ucyB0byB0aGUgVGlkaXZlcnNlCgogICogSGFkbGV5IFdpY2toYW0gYW5kIEdhcnJldHQgR3JvbGVtdW5kICgyMDE2KSwgW19SIGZvciBEYXRhCiAgICBTY2llbmNlX10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei8pLCBPJ1JlaWxseS4gKEJvb2sgc291cmNlIG9uCiAgICBbR2l0SHViXShodHRwczovL2dpdGh1Yi5jb20vaGFkbGV5L3I0ZHMpKQoKICAqIFtSIEJhc2ljcyBjaGFwdGVyXShodHRwczovL3JhZmFsYWIuZGZjaS5oYXJ2YXJkLmVkdS9kc2Jvb2svci1iYXNpY3MuaHRtbCkKICAgIGluIFJhZmFlbCBBLiBJcml6YXJyeSAoMjAxOSksIFtJbnRyb2R1Y3Rpb24gdG8gRGF0YSBTY2llbmNlOiBfRGF0YQogICAgQW5hbHlzaXMgYW5kIFByZWRpY3Rpb24gQWxnb3JpdGhtcyB3aXRoCiAgICBSX10oaHR0cHM6Ly9yYWZhbGFiLmRmY2kuaGFydmFyZC5lZHUvZHNib29rLyksIENoYXBtYW4gJiBIYWxsL0NSQy4gKFtCb29rCiAgICBzb3VyY2Ugb24gR2l0SHViXShodHRwczovL2dpdGh1Yi5jb20vcmFmYWxhYi9kc2Jvb2spKQoKCiMjIyBSIE1hcmtkb3duIFR1dG9yaWFscwoKKiBbUiBNYXJrZG93bjogVGhlIERlZmluaXRpdmUgR3VpZGVdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3lpaHVpL3JtYXJrZG93bi8pCiAgYnkgWWlodWkgWGllIGlzIGEgYm9vay1sZW5ndGggcHJlc2VudGF0aW9uLgoqIFRoZSBbUiBNYXJrZG93biBIb21lIFBhZ2VdKGh0dHBzOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBoYXMgYSBsaW5rCiAgdG8gYSBbdHV0b3JpYWxdKGh0dHBzOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL2xlc3Nvbi0xLmh0bWwpLgoKCiMjIEludGVyYWN0aXZlIFR1dG9yaWFsCgpBbiBpbnRlcmFjdGl2ZSBbYGxlYXJucmBdKGh0dHBzOi8vcnN0dWRpby5naXRodWIuaW8vbGVhcm5yLykgdHV0b3JpYWwKZm9yIHRoZXNlIG5vdGVzIGlzIFthdmFpbGFibGVdKGByIFdMTksoInR1dG9yaWFscy9SaW50cm8uUm1kIilgKS4KCllvdSBjYW4gcnVuIHRoZSB0dXRvcmlhbCB3aXRoCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpTVEFUNDU4MDo6cnVuVHV0b3JpYWwoIlJpbnRybyIpCmBgYAoKWW91IGNhbiBpbnN0YWxsIHRoZSBjdXJyZW50IHZlcnNpb24gb2YgdGhlIGBTVEFUNDU4MGAgcGFja2FnZSB3aXRoCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpyZW1vdGVzOjppbnN0YWxsX2dpdGxhYigibHVrZS10aWVybmV5L1NUQVQ0NTgwIikKYGBgCgpZb3UgbWF5IG5lZWQgdG8gaW5zdGFsbCB0aGUgYHJlbW90ZXNgIHBhY2thZ2UgZnJvbSBDUkFOIGZpcnN0LgoKCiMjIEV4ZXJjaXNlcwoKMS4gQ29tcHV0ZSB0aGUgbWVhbiBvZiB0aGUgbnVtYmVycyAxLCAzLCA1LCA4LgoKPCEtLQpUaGUgYW5zd2VyIHRvIEV4ZXJjaXNlIDEgaXMgY2xvc2VzdCB0bwoqIDQuMjUKICA1Ljc1CiAgMy43NQogIDUuMjUKLS0+CgoyLiBXaGF0IGlzIHRoZSBtZWFuIG9mIHRoZSBgZXJ1cHRpb25zYCB2YXJpYWJsZSBpbiB0aGUgYGZhaXRoZnVsYCBkYXRhCiAgIGZyYW1lPwoKPCEtLQpUaGUgYW5zd2VyIHRvIEV4ZXJjaXNlIDIgaXMgY2xvc2VzdCB0bwoqIDMuNDkKICAzLjM1CiAgMy44NwogIDMuMTYKLS0+CgozLiBGaW5kIHRoZSBhdmVyYWdlIG9mIHRoZSBmaXJzdCA1MCBlcnVwdGlvbiBkdXJhdGlvbnMgaW4gdGhlIGBmYWl0aGZ1bGAKICAgZGF0YSBmcmFtZS4KCjwhLS0KVGhlIGFuc3dlciB0byBFeGVyY2lzZSAzIGlzIGNsb3Nlc3QgdG8KKiAzLjMwCiAgMi41MAogIDMuMTMKICA0LjMzCi0tPgoKNC4gVXNlIHRoZSBgbWVkaWFuYCBmdW5jdGlvbiB0byBtb2RpZnkgdGhlIHBpcGUgZXhhbXBsZSBpbiB0aGUKICAgW3RpZHl2ZXJzZSBzZWN0aW9uXSgjdGhlLXRpZHl2ZXJzZSkgdG8gaW5jbHVkZSBtZWRpYW5zLgo=