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:
eruptions
: Eruption duration (minutes)
waiting
: Waiting time to next eruption (minutes)
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 .
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:
mutation (adding a new variable)
grouping (splitting by type
)
summarizing within groups.
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:
Manipulation pipelines expressed this way are often more compact than ones using intermediate variables and/or nested calls.
With pipe notation there is no need to come up with intermediate variable names.
Pipe notation obscures the function calls that are actually happening and this can make debugging harder.
Contrast to Point-and-Click Interfaces
Even simple tasks require learning some of the R language.
Once you can do simple tasks, you have learned some of the R language.
More complicated tasks become easier.
Even very complicated tasks become possible.
R and Reproducibility
Analyses in R are carried out by running code describing the tasks to perform.
This code can be
audited to make sure the analysis is right;
replayed to make sure the results are repoducible;
reused after changes in the data or on new data.
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
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
Compute the mean of the numbers 1, 3, 5, 8.
What is the mean of the eruptions
variable in the faithful
data frame?
Find the average of the first 50 eruption durations in the faithful
data frame.
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=