Data Structures and Data Attribute Types
Data comes in many different forms.
Some of the most common data structures are
rectangular tables
networks and trees
geometries, regions
Other forms include
collections of text
video and audio recordings
…
We will work mostly with tables.
Many other forms can be reduced to tables.
Stevens
(1945) classifies scales of
measurement for attributes, or variables, as
nominal (e.g. hair color)
ordinal (e.g. dislike, neutral, like)
interval (e.g. temperature) — compare by difference
ratio (e.g. counts) — compare by ratio
These are sometimes grouped as
qualitative: nominal
quantitative: ordinal, interval, ratio
These can be viewed as semantic classifications
Computational considerations often classify variables as
categorical
integer, discrete
real, continuous
Another consideration is that some scales may be cyclic:
hours of the day
angles, longitude
These distinctions can be important in choosing visual
representations.
Other typologies include one proposed by Mosteller
and Tukey (1977) :
Names
Grades (ordered labels like beginner, intermediate, advanced)
Ranks (orders with 1 being the smallest or largest, 2 the next
smallest or largest, and so on)
Counted fractions (bound by 0 and 1)
Counts (non-negative integers)
Amounts (non-negative real numbers)
Balances (any real number)
Data Types in R
R variables can be of different types.
The most common types are
numeric for real numbers
integer
character for text data or nominal data
factor for nominal or ordinal data
Factors can be
unordered, for nominal data
ordered, for ordinal data
factors are more efficient and powerful for representing
nominal or ordinal data than character data but can take a
bit more getting used to.
Membership predicates and coercion functions are
is.numeric
as.numeric
is.integer
as.integer
is.character
as.character
is.factor
as.factor
is.ordered
as.ordered
Conversion of factors with numeric-looking labels to numeric data
should always go through as.character first.
Data Frames: Organizing Cases and Variables
Tabular data in R is usually stored as a data frame .
A data frame is a collection of variables , each with a value
for every case or observation .
The faithful data set is a data frame:
class(faithful)
## [1] "data.frame"
names(faithful)
## [1] "eruptions" "waiting"
Most tools we work with in R use data organized in data frames.
Our plot() and lm() expressions from the introductory section can also we written as
plot(waiting ~ eruptions, data = faithful,
xlab = "Eruption time (min)",
ylab = "Waiting time to next eruption (min)")
fit <- lm(waiting ~ eruptions, data = faithful)
plot() only uses the data argument when the
plot is specified as a formula , like
waiting ~ eruptions
Examining the Data in a Data Frame
head() provides an idea of what the raw data looks
like:
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
str() is also useful for an overview of an object’s
structure:
str(faithful)
## 'data.frame': 272 obs. of 2 variables:
## $ eruptions: num 3.6 1.8 3.33 2.28 4.53 ...
## $ waiting : num 79 54 74 62 85 55 88 85 51 85 ...
Another useful function available from the dplyr or
tibble packages is glimpse():
library(dplyr)
glimpse(faithful)
## Rows: 272
## Columns: 2
## $ eruptions <dbl> 3.600, 1.800, 3.333, 2.283, 4.533, 2.883, 4.700, 3.600, 1.95…
## $ waiting <dbl> 79, 54, 74, 62, 85, 55, 88, 85, 51, 85, 54, 84, 78, 47, 83, …
summary() shows basic statistical properties of the
variables:
summary(faithful)
## eruptions waiting
## Min. :1.600 Min. :43.0
## 1st Qu.:2.163 1st Qu.:58.0
## Median :4.000 Median :76.0
## Mean :3.488 Mean :70.9
## 3rd Qu.:4.454 3rd Qu.:82.0
## Max. :5.100 Max. :96.0
summary() with a character variable and a factor
variable:
ffl <- mutate(faithful,
type = ifelse(eruptions < 3, "short", "long"),
ftype = factor(type))
summary(ffl)
## eruptions waiting type ftype
## Min. :1.600 Min. :43.0 Length:272 long :175
## 1st Qu.:2.163 1st Qu.:58.0 Class :character short: 97
## Median :4.000 Median :76.0 Mode :character
## Mean :3.488 Mean :70.9
## 3rd Qu.:4.454 3rd Qu.:82.0
## Max. :5.100 Max. :96.0
Variables in a Data Frame
A Data frame is a list, or vector, of variables:
length(faithful)
## [1] 2
The dollar sign $ can be used to examine individual
variables:
class(faithful$eruptions)
## [1] "numeric"
class(faithful$waiting)
## [1] "numeric"
The variables can also be extracted by numerical or character index
using the element extraction operation [[:
class(faithful[[1]])
## [1] "numeric"
class(faithful[["waiting"]])
## [1] "numeric"
Dimensions
The numbers of rows and columns can be obtained using
nrow() and ncol():
ncol(faithful)
## [1] 2
nrow(faithful)
## [1] 272
dim() returns a vector of the dimensions:
dim(faithful)
## [1] 272 2
Simple Visualizations
plot has a method for data frames that tries to
provide a reasonable default visualization for numeric data frames:
plot(faithful)
Sample Data Sets
The datasets
package in the base R distribution contains a number of data sets you
can explore.
Another package with a useful collection of data sets is dslabs .
Many other data sets are contained in contributed packages as
examples.
There are also many contributed packages designed specifically for
making particular data sets available.
Tidy Data
The useful concept of a tidy data frame was introduced
fairly recently and
is described in a chapter in R for Data
Science .
The idea is that
every observation should correspond to a single row;
every variable should correspond to a single column.
Tidy data is computationally convenient, and many of the tools we
will use are designed around tidy data frames.
A large range of these tools can be accessed by loading the
tidyverse package:
library(tidyverse)
But for now I will load the needed packages individually.
The term tidy is a little unfortunate.
Data that is not tidy isn’t necessarily bad .
For human reading, and for some computations, data in a wider format
can be better.
For other computations data in a longer, or narrower, format can be
better.
Tibbles
Many tools in the tidyverse produce slightly enhanced
data frames called tibbles :
library(tibble)
faithful_tbl <- as_tibble(faithful)
class(faithful_tbl)
## [1] "tbl_df" "tbl" "data.frame"
Tibbles print differently from standard data frames:
faithful_tbl
## # A tibble: 272 × 2
## eruptions waiting
## <dbl> <dbl>
## 1 3.6 79
## 2 1.8 54
## 3 3.33 74
## 4 2.28 62
## 5 4.53 85
## 6 2.88 55
## 7 4.7 88
## 8 3.6 85
## 9 1.95 51
## 10 4.35 85
## # ℹ 262 more rows
For the most part data frames and tibbles can be used
interchangeably.
Tidying Data
Many data sets are in tidy form already.
If they are not, they can be put into tidy form.
The tools for this are part of data technologies .
The tasks involved are part of what is sometimes called data
wrangling .
An Example: Global Average Surface Temperatures
Among many data sets available at https://data.giss.nasa.gov/gistemp/ is data on monthly
global average surface temperatures over a number of years.
These data from 2017 were used for the widely cited Bloomberg
hottest year visualization .
A
version from 2015 may work a little better.
The current data are available in a formatted text file at
https://data.giss.nasa.gov/gistemp/tabledata_v4/GLB.Ts+dSST.txt
or as a CSV
(comma-separated values) file at
https://data.giss.nasa.gov/gistemp/tabledata_v4/GLB.Ts+dSST.csv
The first few lines of the CSV file:
Land-Ocean: Global Means
Year,Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec,J-D,D-N,DJF,MAM,JJA,SON
1880,-.19,-.25,-.10,-.17,-.11,-.22,-.19,-.11,-.15,-.24,-.23,-.18,-.18,***,***,-.13,-.17,-.21
1881,-.20,-.15,.03,.04,.05,-.19,.00,-.04,-.16,-.22,-.19,-.07,-.09,-.10,-.18,.04,-.08,-.19
1882,.15,.13,.04,-.17,-.15,-.23,-.17,-.08,-.15,-.24,-.17,-.36,-.12,-.09,.07,-.09,-.16,-.19
1883,-.30,-.37,-.13,-.18,-.17,-.08,-.07,-.14,-.21,-.11,-.23,-.11,-.18,-.20,-.34,-.16,-.10,-.19
The CSV file is a little easier (for a computer program) to read in,
so we will work with that.
The numbers in the CSV file represent deviations in degrees Celcius
from the average temperature for the base period 1951-1980.
The file available on January 16, 2025, is now available locally .
We can make sure it has been downloaded to our working directory
with
if (! file.exists("GLB.Ts+dSST.csv"))
download.file("https://stat.uiowa.edu/~luke/data/GLB.Ts+dSST.csv",
"GLB.Ts+dSST.csv")
Assuming this locally available file has been downloaded, we can read
in the data and drop some columns we don’t need with
library(readr)
gast <- read_csv("GLB.Ts+dSST.csv", skip = 1)[1 : 13]
The function read_csv is from the readr
package, which is part of the tidyverse.
An alternative is the base R function read.csv.
A look at the first few lines:
head(gast, 5)
## # A tibble: 5 × 13
## Year Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1880 -0.19 -0.25 -0.1 -0.17 -0.11 -0.22 -0.19 -0.11 -0.15 -0.24 -0.23 -0.18
## 2 1881 -0.2 -0.15 0.03 0.04 0.05 -0.19 0 -0.04 -0.16 -0.22 -0.19 -0.07
## 3 1882 0.15 0.13 0.04 -0.17 -0.15 -0.23 -0.17 -0.08 -0.15 -0.24 -0.17 -0.36
## 4 1883 -0.3 -0.37 -0.13 -0.18 -0.17 -0.08 -0.07 -0.14 -0.21 -0.11 -0.23 -0.11
## 5 1884 -0.13 -0.08 -0.36 -0.41 -0.34 -0.36 -0.3 -0.27 -0.27 -0.25 -0.34 -0.31
And the last few lines:
tail(gast, 5)
## # A tibble: 5 × 13
## Year Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2021 0.81 0.64 0.89 0.76 0.79 0.85 0.92 0.81 0.93 0.99 0.93 0.87
## 2 2022 0.91 0.89 1.05 0.84 0.84 0.92 0.94 0.95 0.89 0.97 0.73 0.8
## 3 2023 0.88 0.97 1.23 0.99 0.94 1.09 1.2 1.19 1.48 1.34 1.4 1.37
## 4 2024 1.25 1.44 1.39 1.31 1.15 1.2 1.2 1.29 1.24 1.33 1.3 1.27
## 5 2025 1.38 1.26 1.36 1.23 1.08 1.05 1.02 1.16 1.25 1.19 1.21 1.05
The print() method for tibbles abbreviates the
output.
It is neater and provides some useful additional information on
variable data types.
But it shows only the first few rows and may not explicitly show some
columns.
If some columns are skipped, you can ask to see more by calling
print() explicitly:
print(tail(gast), width = 100)
## # A tibble: 6 × 13
## Year Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2020 1.18 1.24 1.18 1.12 0.99 0.91 0.89 0.86 0.97 0.87 1.09 0.8
## 2 2021 0.81 0.64 0.89 0.76 0.79 0.85 0.92 0.81 0.93 0.99 0.93 0.87
## 3 2022 0.91 0.89 1.05 0.84 0.84 0.92 0.94 0.95 0.89 0.97 0.73 0.8
## 4 2023 0.88 0.97 1.23 0.99 0.94 1.09 1.2 1.19 1.48 1.34 1.4 1.37
## 5 2024 1.25 1.44 1.39 1.31 1.15 1.2 1.2 1.29 1.24 1.33 1.3 1.27
## 6 2025 1.38 1.26 1.36 1.23 1.08 1.05 1.02 1.16 1.25 1.19 1.21 1.05
The format with one column per month is compact and useful for
viewing and data entry.
But it is not in tidy format since
the monthly temperature variable is spread over 12 columns;
the month variable is encoded in the column headings.
For obvious reasons this data format is often referred to as wide
format .
The tidy, or long , format would have three variables:
Year, Month, and Temp.
One way to put this data frame in tidy, or long, format uses
pivot_longer() from the tidyr package:
library(tidyr)
lgast <- pivot_longer(gast,
-Year, ## specifies the columns to use -- all but Year
names_to = "Month",
values_to = "Temp")
The first few rows of the result:
head(lgast)
## # A tibble: 6 × 3
## Year Month Temp
## <dbl> <chr> <dbl>
## 1 1880 Jan -0.19
## 2 1880 Feb -0.25
## 3 1880 Mar -0.1
## 4 1880 Apr -0.17
## 5 1880 May -0.11
## 6 1880 Jun -0.22
During plotting it is likely that the Month variable
will be converted to a factor .
By default, this will order levels alphabetically, which is not what
we want:
levels(as.factor(lgast$Month))
## [1] "Apr" "Aug" "Dec" "Feb" "Jan" "Jul" "Jun" "Mar" "May" "Nov" "Oct" "Sep"
We can guard against this by converting Month to a
factor with the right levels now:
lgast <- mutate(lgast, Month = factor(Month, levels = month.abb))
levels(lgast$Month)
## [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"
We can now use this tidy version of the data to create a static
version of the Bloomberg
hottest year visualization .
The basic framework is set up with
library(ggplot2)
p <- ggplot(lgast) +
ggtitle("Global Average Surface Temperatures") +
theme(plot.title = element_text(hjust = 0.5))
ggplot objects only produce output when they are
printed.
To see the plot in p we need to print it, for example by
using a line with only p.
p
Then add a layer with lines for each year (specified by the
group argument to geom_line).
p + geom_line(aes(x = Month,
y = Temp,
group = Year))
We can use color to distingish the years.
p1 <- p +
geom_line(aes(x = Month,
y = Temp,
color = Year,
group = Year),
na.rm = TRUE)
p1
Saving the plot specification in the variable p1 will
make it easier to experiment with color variations:
One way to highlight the past_year 2025:
lgast_last <- filter(lgast, Year == past_year)
p1 + geom_line(aes(x = Month,
y = Temp,
group = Year),
linewidth = 1,
color = "red",
data = lgast_last,
na.rm = TRUE)
A useful way to show more recent data in the context of the full data
is to show the full data in grey and the more recent years in black:
lgast2k <- filter(lgast, Year >= 2000)
ggplot(lgast, aes(x = Month,
y = Temp,
group = Year)) +
geom_line(color = "grey80") +
theme_minimal() +
geom_line(data = lgast2k)
If you want to update your plot later in the year then the current
year’s entry may contain missing value indicators that you will have to
deal with.
The New York Times on January 18, 2018, published another
visualization of these data showing average yearly temperatures (via
Google may work better ).
To recreate this plot we first need to compute yearly average
temperatures.
This is easy to do with the summarize() and
group_by() functions from dpyr:
library(dplyr)
atemp <- lgast |>
group_by(Year) |>
summarize(AveTemp = mean(Temp, na.rm = TRUE))
head(atemp)
## # A tibble: 6 × 2
## Year AveTemp
## <dbl> <dbl>
## 1 1880 -0.178
## 2 1881 -0.0917
## 3 1882 -0.117
## 4 1883 -0.175
## 5 1884 -0.285
## 6 1885 -0.338
Using na.rm = TRUE ensures that the mean is based on the
available months if data for some months is missing.
A simple version of the plot is then produced by
ggplot(atemp) +
geom_point(aes(x = Year, y = AveTemp))
A variation showing record years:
library(ggrepel)
atemp_rec <- filter(atemp, cummax(AveTemp) == AveTemp)
ggplot(atemp, aes(x = Year, y = AveTemp)) +
geom_point() +
geom_point(data = atemp_rec, color = "red") +
geom_text_repel(aes(label = Year),
data = atemp_rec,
color = "blue")
Matching the representation of record years in the Bloomberg
plot:
lgast_rec <- filter(lgast, Year %in% atemp_rec$Year)
ggplot(lgast, aes(x = Month, y = Temp, group = Year)) +
geom_line(color = "grey80") +
theme_minimal() +
geom_line(data = lgast_rec, aes(color = Temp)) +
scale_color_gradient(low = "blue", high = "red")
Another variation on the Bloomberg plot showing just a few years 20
years apart:
lg_by_20 <-
filter(lgast,
Year %in% seq(2020, by = -20, len = 5)) |>
mutate(Year = factor(Year))
ggplot(lg_by_20, aes(x = Month,
y = Temp,
group = Year,
color = Year)) +
geom_line()
Converting Year to a factor results in a discrete color
scale and legend.
Handling Missing Values
The data for 2019 available in early 2020 is also available locally .
Assuming this locally available file has been downloaded, we can read
in the data and drop some columns we don’t need with
gast2019 <- read_csv("GLB.Ts+dSST-2019.csv", skip = 1)[1 : 13]
The last three columns are read as character variables:
head(gast2019, 5)
## # A tibble: 5 × 13
## Year Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr> <chr>
## 1 1880 -0.17 -0.23 -0.08 -0.15 -0.08 -0.2 -0.17 -0.09 -0.13 -.22 -.20 -.16
## 2 1881 -0.18 -0.13 0.04 0.06 0.08 -0.17 0.02 -0.02 -0.14 -.20 -.17 -.05
## 3 1882 0.18 0.15 0.06 -0.15 -0.13 -0.21 -0.15 -0.06 -0.13 -.23 -.15 -.34
## 4 1883 -0.28 -0.36 -0.11 -0.17 -0.16 -0.07 -0.05 -0.12 -0.2 -.10 -.22 -.10
## 5 1884 -0.12 -0.07 -0.36 -0.39 -0.33 -0.34 -0.32 -0.27 -0.26 -.24 -.32 -.30
The reason is that data for October through December were not
available:
tail(gast2019, 2)
## # A tibble: 2 × 13
## Year Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr> <chr>
## 1 2018 0.82 0.85 0.9 0.89 0.83 0.78 0.83 0.76 0.81 1.02 .83 .92
## 2 2019 0.94 0.96 1.18 1.02 0.86 0.93 0.95 0.94 0.93 *** *** ***
We want to convert these columns to numeric, with missing values
represented as NA.
One option is to handle them individually:
gast2019$Oct <- as.numeric(gast2019$Oct)
## Warning: NAs introduced by coercion
tail(gast2019, 2)
## # A tibble: 2 × 13
## Year Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
## 1 2018 0.82 0.85 0.9 0.89 0.83 0.78 0.83 0.76 0.81 1.02 .83 .92
## 2 2019 0.94 0.96 1.18 1.02 0.86 0.93 0.95 0.94 0.93 NA *** ***
Another option is to convert all character columns to numeric
with
gast2019 <- mutate(gast2019, across(where(is.character), as.numeric))
## Warning: There were 2 warnings in `mutate()`.
## The first warning was:
## ℹ In argument: `across(where(is.character), as.numeric)`.
## Caused by warning:
## ! NAs introduced by coercion
## ℹ Run `dplyr::last_dplyr_warnings()` to see the 1 remaining warning.
tail(gast2019, 2)
## # A tibble: 2 × 13
## Year Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2018 0.82 0.85 0.9 0.89 0.83 0.78 0.83 0.76 0.81 1.02 0.83 0.92
## 2 2019 0.94 0.96 1.18 1.02 0.86 0.93 0.95 0.94 0.93 NA NA NA
The warnings are benign and can be suppressed with the
warning = FALSE chunk option.
Since we know the missing value pattern *** we can also
avoid the need to fix the data after the fact by specifying this at read
time:
gast2019 <- read_csv("GLB.Ts+dSST-2019.csv", na = "***", skip = 1)[1 : 13]
tail(gast2019, 2)
## # A tibble: 2 × 13
## Year Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2018 0.82 0.85 0.9 0.89 0.83 0.78 0.83 0.76 0.81 1.02 0.83 0.92
## 2 2019 0.94 0.96 1.18 1.02 0.86 0.93 0.95 0.94 0.93 NA NA NA
A plot highlighting the year 2019 shows only the months with
available data:
lgast2019 <- gast2019 |>
pivot_longer(-Year,
names_to = "Month",
values_to = "Temp") |>
mutate(Month = factor(Month, levels = month.abb))
ggplot(lgast2019, aes(x = Month,
y = Temp,
group = Year)) +
geom_line(color = "grey80",
na.rm = TRUE) +
geom_line(data = filter(lgast2019, Year == 2019),
na.rm = TRUE)
Adding na.rm = TRUE in the geom_line calls
suppresses warnings; the plot would be the same without these.
Outline of the tools used:
Data processing:
Reading: read_csv, read.csv;
Reshaping: pivot_longer;
Cleaning: is.character, as.numeric,
mutate, across, factor;
Summarizing: group_by, summarize.
Visualization geometries:
geom_line for a line plot;
geom_point for a scatter plot.
Interactive Tutorial
An interactive learnr
tutorial for these notes is available .
You can run the tutorial with
STAT4580::runTutorial("datafrm")
Exercises
Which of the Stevens classifications (nominal, ordinal, interval,
ratio) best characterizes these variables:
Daily maximal temperatures in Iowa City.
Population counts for Iowa counties.
Education level of job applicants using the Bureau
of Labor Statistics classification .
Major of UI students.
Which of these data sets are in tidy form?
The builtin data set co2
The builtin data set BOD
The who data set in package tidyr
(tidyr::who)
The mpg data set in package ggplot2
(ggplot2::mpg)
The next exercises use the data in the variable
gapminder in the package gapminder. You can
make it available with
data(gapminder, package = "gapminder")
Use the function str to examine the value of the
gapminder variable. How many cases are there in the data set? How many
of the variables are factors?
Use the functions class and names to
find the class and variable names in the gapminder
data.
Use summary to compute summary information for the
variables.
Fill in the values for --- needed to produce plots
of life expectancy against year for the countries in continent Oceania.
library(dplyr)
library(ggplot2)
data(gapminder, package = "gapminder")
ggplot(filter(gapminder, continent == "Oceania"),
aes(x = ---, y = ---, color = country)) +
geom_line()
LS0tCnRpdGxlOiAiRGF0YSBhbmQgRGF0YSBGcmFtZXMiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgo8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9InN0YXQ0NTgwLmNzcyIgdHlwZT0idGV4dC9jc3MiIC8+CgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Kc291cmNlKGhlcmU6OmhlcmUoInNldHVwLlIiKSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGNvbGxhcHNlID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgZmlnLmhlaWdodCA9IDUsIGZpZy53aWR0aCA9IDYsIGZpZy5hbGlnbiA9ICJjZW50ZXIiKQpvcHRpb25zKHdpZHRoID0gODApCmBgYAoKCiMjIERhdGEgU3RydWN0dXJlcyBhbmQgRGF0YSBBdHRyaWJ1dGUgVHlwZXMKCkRhdGEgY29tZXMgaW4gbWFueSBkaWZmZXJlbnQgZm9ybXMuCgpTb21lIG9mIHRoZSBtb3N0IGNvbW1vbiBkYXRhIHN0cnVjdHVyZXMgYXJlCgoqIHJlY3Rhbmd1bGFyIHRhYmxlcwoqIG5ldHdvcmtzIGFuZCB0cmVlcwoqIGdlb21ldHJpZXMsIHJlZ2lvbnMKCk90aGVyIGZvcm1zIGluY2x1ZGUKCiogY29sbGVjdGlvbnMgb2YgdGV4dAoqIHZpZGVvIGFuZCBhdWRpbyByZWNvcmRpbmdzCiogLi4uCgpXZSB3aWxsIHdvcmsgbW9zdGx5IHdpdGggdGFibGVzLgoKTWFueSBvdGhlciBmb3JtcyBjYW4gYmUgcmVkdWNlZCB0byB0YWJsZXMuCgpbU3RldmVuc10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU3RhbmxleV9TbWl0aF9TdGV2ZW5zKSAoMTk0NSkKIGNsYXNzaWZpZXMgW3NjYWxlcyBvZgogbWVhc3VyZW1lbnRdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xldmVsX29mX21lYXN1cmVtZW50KSBmb3IKIGF0dHJpYnV0ZXMsIG9yIHZhcmlhYmxlcywgYXMKPCEtLSBwZXJtYW5lbnQgbGluazogaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3cvaW5kZXgucGhwP3RpdGxlPUxldmVsX29mX21lYXN1cmVtZW50Jm9sZGlkPTEwNjA3NzI4NDcgLS0+IAoKKiBub21pbmFsIChlLmcuIGhhaXIgY29sb3IpCiogb3JkaW5hbCAoZS5nLiBkaXNsaWtlLCBuZXV0cmFsLCBsaWtlKQoqIGludGVydmFsIChlLmcuIHRlbXBlcmF0dXJlKSAtLS0gY29tcGFyZSBieSBkaWZmZXJlbmNlCiogcmF0aW8gKGUuZy4gY291bnRzKSAtLS0gY29tcGFyZSBieSByYXRpbwoKVGhlc2UgYXJlIHNvbWV0aW1lcyBncm91cGVkIGFzCgoqIHF1YWxpdGF0aXZlOiBub21pbmFsCiogcXVhbnRpdGF0aXZlOiBvcmRpbmFsLCBpbnRlcnZhbCwgcmF0aW8KClRoZXNlIGNhbiBiZSB2aWV3ZWQgYXMgX3NlbWFudGljIGNsYXNzaWZpY2F0aW9uc18KCl9Db21wdXRhdGlvbmFsIGNvbnNpZGVyYXRpb25zXyBvZnRlbiBjbGFzc2lmeSB2YXJpYWJsZXMgYXMKCiogY2F0ZWdvcmljYWwKKiBpbnRlZ2VyLCBkaXNjcmV0ZQoqIHJlYWwsIGNvbnRpbnVvdXMKCkFub3RoZXIgY29uc2lkZXJhdGlvbiBpcyB0aGF0IHNvbWUgc2NhbGVzIG1heSBiZSBjeWNsaWM6CgoqIGhvdXJzIG9mIHRoZSBkYXkKKiBhbmdsZXMsIGxvbmdpdHVkZQoKVGhlc2UgZGlzdGluY3Rpb25zIGNhbiBiZSBpbXBvcnRhbnQgaW4gY2hvb3NpbmcgdmlzdWFsIHJlcHJlc2VudGF0aW9ucy4KCk90aGVyIHR5cG9sb2dpZXMgaW5jbHVkZSBvbmUgcHJvcG9zZWQgYnkgW01vc3RlbGxlciBhbmQgVHVrZXkKKDE5NzcpXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9MZXZlbF9vZl9tZWFzdXJlbWVudCNNb3N0ZWxsZXJfYW5kX1R1a2V5J3NfdHlwb2xvZ3lfKDE5NzcpKToKCjEuIE5hbWVzCjIuIEdyYWRlcyAob3JkZXJlZCBsYWJlbHMgbGlrZSBiZWdpbm5lciwgaW50ZXJtZWRpYXRlLCBhZHZhbmNlZCkKMy4gUmFua3MgKG9yZGVycyB3aXRoIDEgYmVpbmcgdGhlIHNtYWxsZXN0IG9yIGxhcmdlc3QsIDIgdGhlIG5leHQKICAgc21hbGxlc3Qgb3IgbGFyZ2VzdCwgYW5kIHNvIG9uKQo0LiBDb3VudGVkIGZyYWN0aW9ucyAoYm91bmQgYnkgMCBhbmQgMSkKNS4gQ291bnRzIChub24tbmVnYXRpdmUgaW50ZWdlcnMpCjYuIEFtb3VudHMgKG5vbi1uZWdhdGl2ZSByZWFsIG51bWJlcnMpCjcuIEJhbGFuY2VzIChhbnkgcmVhbCBudW1iZXIpCgoKIyMgRGF0YSBUeXBlcyBpbiBSCgpSIHZhcmlhYmxlcyBjYW4gYmUgb2YgZGlmZmVyZW50IHR5cGVzLgoKVGhlIG1vc3QgY29tbW9uIHR5cGVzIGFyZQoKKiBgbnVtZXJpY2AgZm9yIHJlYWwgbnVtYmVycwoqIGBpbnRlZ2VyYAoqIGBjaGFyYWN0ZXJgIGZvciB0ZXh0IGRhdGEgb3Igbm9taW5hbCBkYXRhCiogYGZhY3RvcmAgZm9yIG5vbWluYWwgb3Igb3JkaW5hbCBkYXRhCgpGYWN0b3JzIGNhbiBiZQoKKiB1bm9yZGVyZWQsIGZvciBub21pbmFsIGRhdGEKKiBvcmRlcmVkLCBmb3Igb3JkaW5hbCBkYXRhCgo8ZGl2IGNsYXNzID0gImFsZXJ0Ij4KYGZhY3RvcnNgIGFyZSBtb3JlIGVmZmljaWVudCBhbmQgcG93ZXJmdWwgZm9yIHJlcHJlc2VudGluZyBub21pbmFsIG9yCm9yZGluYWwgZGF0YSB0aGFuIGBjaGFyYWN0ZXJgIGRhdGEgYnV0IGNhbiB0YWtlIGEgYml0IG1vcmUgZ2V0dGluZwp1c2VkIHRvLgo8L2Rpdj4KCk1lbWJlcnNoaXAgcHJlZGljYXRlcyBhbmQgY29lcmNpb24gZnVuY3Rpb25zIGFyZQoKfCBQcmVkaWNhdGUgICAgICAgfCBDb2Vyc2lvbnwKfDotLS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLXwKfCBgaXMubnVtZXJpY2AgICB8IGBhcy5udW1lcmljYCAgICB8CnwgYGlzLmludGVnZXJgICAgfCBgYXMuaW50ZWdlcmAgICAgfAp8IGBpcy5jaGFyYWN0ZXJgIHwgYGFzLmNoYXJhY3RlcmAgIHwKfCBgaXMuZmFjdG9yYCAgICB8IGBhcy5mYWN0b3JgICAgICB8CnwgYGlzLm9yZGVyZWRgICAgfCBgYXMub3JkZXJlZGAgICAgfAoKPGRpdiBjbGFzcyA9ICJhbGVydCI+CkNvbnZlcnNpb24gb2YgZmFjdG9ycyB3aXRoIG51bWVyaWMtbG9va2luZyBsYWJlbHMgdG8gbnVtZXJpYyBkYXRhCnNob3VsZCBhbHdheXMgZ28gdGhyb3VnaCBgYXMuY2hhcmFjdGVyYCBmaXJzdC4KPC9kaXY+CgoKIyMgRGF0YSBGcmFtZXM6IE9yZ2FuaXppbmcgQ2FzZXMgYW5kIFZhcmlhYmxlcwoKVGFidWxhciBkYXRhIGluIFIgaXMgdXN1YWxseSBzdG9yZWQgYXMgYSBfZGF0YSBmcmFtZV8uCgpBIGRhdGEgZnJhbWUgaXMgYSBjb2xsZWN0aW9uIG9mIF92YXJpYWJsZXNfLCBlYWNoIHdpdGggYSB2YWx1ZSBmb3IKZXZlcnkgX2Nhc2VfIG9yIF9vYnNlcnZhdGlvbl8uCgpUaGUgYGZhaXRoZnVsYCBkYXRhIHNldCBpcyBhIGRhdGEgZnJhbWU6CgpgYGB7cn0KY2xhc3MoZmFpdGhmdWwpCm5hbWVzKGZhaXRoZnVsKQpgYGAKCk1vc3QgdG9vbHMgd2Ugd29yayB3aXRoIGluIFIgdXNlIGRhdGEgb3JnYW5pemVkIGluIGRhdGEgZnJhbWVzLgoKT3VyIGBwbG90KClgIGFuZCBgbG0oKWAgZXhwcmVzc2lvbnMgZnJvbSB0aGUKW2ludHJvZHVjdG9yeSBzZWN0aW9uXShpbnRyby5odG1sKQpjYW4gYWxzbyB3ZSB3cml0dGVuIGFzCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwbG90KHdhaXRpbmcgfiBlcnVwdGlvbnMsIGRhdGEgPSBmYWl0aGZ1bCwKICAgICB4bGFiID0gIkVydXB0aW9uIHRpbWUgKG1pbikiLAogICAgIHlsYWIgPSAiV2FpdGluZyB0aW1lIHRvIG5leHQgZXJ1cHRpb24gKG1pbikiKQpmaXQgPC0gbG0od2FpdGluZyB+IGVydXB0aW9ucywgZGF0YSA9IGZhaXRoZnVsKQpgYGAKCjxkaXYgY2xhc3MgPSAiYWxlcnQiPgpgcGxvdCgpYCBvbmx5IHVzZXMgdGhlIGBkYXRhYCBhcmd1bWVudCB3aGVuIHRoZSBwbG90IGlzIHNwZWNpZmllZCBhcyBhCl9mb3JtdWxhXywgbGlrZQoKYGBgcgp3YWl0aW5nIH4gZXJ1cHRpb25zCmBgYAo8L2Rpdj4KCgojIyBFeGFtaW5pbmcgdGhlIERhdGEgaW4gYSBEYXRhIEZyYW1lCgpgaGVhZCgpYCBwcm92aWRlcyBhbiBpZGVhIG9mIHdoYXQgdGhlIHJhdyBkYXRhIGxvb2tzIGxpa2U6CmBgYHtyfQpoZWFkKGZhaXRoZnVsKQpgYGAKCmBzdHIoKWAgaXMgYWxzbyB1c2VmdWwgZm9yIGFuIG92ZXJ2aWV3IG9mIGFuIG9iamVjdCdzIHN0cnVjdHVyZToKYGBge3J9CnN0cihmYWl0aGZ1bCkKYGBgCgpBbm90aGVyIHVzZWZ1bCBmdW5jdGlvbiBhdmFpbGFibGUgZnJvbSB0aGUgYGRwbHlyYCBvciBgdGliYmxlYApwYWNrYWdlcyBpcyBgZ2xpbXBzZSgpYDoKCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpnbGltcHNlKGZhaXRoZnVsKQpgYGAKCmBzdW1tYXJ5KClgIHNob3dzIGJhc2ljIHN0YXRpc3RpY2FsIHByb3BlcnRpZXMgb2YgdGhlIHZhcmlhYmxlczoKCmBgYHtyfQpzdW1tYXJ5KGZhaXRoZnVsKQpgYGAKCmBzdW1tYXJ5KClgIHdpdGggYSBjaGFyYWN0ZXIgdmFyaWFibGUgYW5kIGEgZmFjdG9yIHZhcmlhYmxlOgoKYGBge3J9CmZmbCA8LSBtdXRhdGUoZmFpdGhmdWwsCiAgICAgICAgICAgICAgdHlwZSA9IGlmZWxzZShlcnVwdGlvbnMgPCAzLCAic2hvcnQiLCAibG9uZyIpLAogICAgICAgICAgICAgIGZ0eXBlID0gZmFjdG9yKHR5cGUpKQpzdW1tYXJ5KGZmbCkKYGBgCgoKIyMgVmFyaWFibGVzIGluIGEgRGF0YSBGcmFtZQoKQSBEYXRhIGZyYW1lIGlzIGEgbGlzdCwgb3IgdmVjdG9yLCBvZiB2YXJpYWJsZXM6CmBgYHtyfQpsZW5ndGgoZmFpdGhmdWwpCmBgYAoKVGhlIGRvbGxhciBzaWduIGAkYCBjYW4gYmUgdXNlZCB0byBleGFtaW5lIGluZGl2aWR1YWwgdmFyaWFibGVzOgpgYGB7cn0KY2xhc3MoZmFpdGhmdWwkZXJ1cHRpb25zKQpjbGFzcyhmYWl0aGZ1bCR3YWl0aW5nKQpgYGAKClRoZSB2YXJpYWJsZXMgY2FuIGFsc28gYmUgZXh0cmFjdGVkIGJ5IG51bWVyaWNhbCBvciBjaGFyYWN0ZXIgaW5kZXggdXNpbmcgdGhlCiAgZWxlbWVudCBleHRyYWN0aW9uIG9wZXJhdGlvbiBgW1tgOgpgYGB7cn0KY2xhc3MoZmFpdGhmdWxbWzFdXSkKY2xhc3MoZmFpdGhmdWxbWyJ3YWl0aW5nIl1dKQpgYGAKCgojIyBEaW1lbnNpb25zCgpUaGUgbnVtYmVycyBvZiByb3dzIGFuZCBjb2x1bW5zIGNhbiBiZSBvYnRhaW5lZCB1c2luZyBgbnJvdygpYCBhbmQgYG5jb2woKWA6CgpgYGB7cn0KbmNvbChmYWl0aGZ1bCkKbnJvdyhmYWl0aGZ1bCkKYGBgCgpgZGltKClgIHJldHVybnMgYSB2ZWN0b3Igb2YgdGhlIGRpbWVuc2lvbnM6CgpgYGB7cn0KZGltKGZhaXRoZnVsKQpgYGAKCgojIyBTaW1wbGUgVmlzdWFsaXphdGlvbnMKCmBwbG90YCBoYXMgYSBfbWV0aG9kXyBmb3IgZGF0YSBmcmFtZXMgdGhhdCB0cmllcyB0byBwcm92aWRlIGEKcmVhc29uYWJsZSBkZWZhdWx0IHZpc3VhbGl6YXRpb24gZm9yIG51bWVyaWMgZGF0YSBmcmFtZXM6CmBgYHtyfQpwbG90KGZhaXRoZnVsKQpgYGAKCgojIyBTYW1wbGUgRGF0YSBTZXRzCgpUaGUKW2BkYXRhc2V0c2BdKGh0dHA6Ly9zdGF0LmV0aHouY2gvUi1tYW51YWwvUi1kZXZlbC9saWJyYXJ5L2RhdGFzZXRzL2h0bWwvMDBJbmRleC5odG1sKQpwYWNrYWdlIGluIHRoZSBiYXNlIFIgZGlzdHJpYnV0aW9uIGNvbnRhaW5zIGEgbnVtYmVyIG9mIGRhdGEgc2V0cyB5b3UKY2FuIGV4cGxvcmUuCgpBbm90aGVyIHBhY2thZ2Ugd2l0aCBhIHVzZWZ1bCBjb2xsZWN0aW9uIG9mIGRhdGEgc2V0cyBpcyBbYGRzbGFic2BdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3BhY2thZ2U9ZHNsYWJzKS4KCk1hbnkgb3RoZXIgZGF0YSBzZXRzIGFyZSBjb250YWluZWQgaW4gY29udHJpYnV0ZWQgcGFja2FnZXMgYXMgZXhhbXBsZXMuCgpUaGVyZSBhcmUgYWxzbyBtYW55IGNvbnRyaWJ1dGVkIHBhY2thZ2VzIGRlc2lnbmVkIHNwZWNpZmljYWxseSBmb3IKbWFraW5nIHBhcnRpY3VsYXIgZGF0YSBzZXRzIGF2YWlsYWJsZS4KCgojIyBUaWR5IERhdGEKClRoZSB1c2VmdWwgY29uY2VwdCBvZiBhIF90aWR5XyBkYXRhIGZyYW1lIHdhcyBpbnRyb2R1Y2VkIGZhaXJseQpbcmVjZW50bHldKGh0dHBzOi8vd3d3LmpzdGF0c29mdC5vcmcvYXJ0aWNsZS92aWV3L3YwNTlpMTApIGFuZCBpcwpkZXNjcmliZWQgaW4gYSBbY2hhcHRlciBpbiBfUiBmb3IgRGF0YQpTY2llbmNlX10oaHR0cHM6Ly9yNGRzLmhhZGxleS5uei9kYXRhLXRpZHkuaHRtbCkuCgpUaGUgaWRlYSBpcyB0aGF0CgoqIGV2ZXJ5IG9ic2VydmF0aW9uIHNob3VsZCBjb3JyZXNwb25kIHRvIGEgc2luZ2xlIHJvdzsKKiBldmVyeSB2YXJpYWJsZSBzaG91bGQgY29ycmVzcG9uZCB0byBhIHNpbmdsZSBjb2x1bW4uCgpUaWR5IGRhdGEgaXMgY29tcHV0YXRpb25hbGx5IGNvbnZlbmllbnQsIGFuZCBtYW55IG9mIHRoZSB0b29scyB3ZSB3aWxsCnVzZSBhcmUgZGVzaWduZWQgYXJvdW5kIHRpZHkgZGF0YSBmcmFtZXMuCgpBIGxhcmdlIHJhbmdlIG9mIHRoZXNlIHRvb2xzIGNhbiBiZSBhY2Nlc3NlZCBieSBsb2FkaW5nIHRoZQpgdGlkeXZlcnNlYCBwYWNrYWdlOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKQnV0IGZvciBub3cgSSB3aWxsIGxvYWQgdGhlIG5lZWRlZCBwYWNrYWdlcyBpbmRpdmlkdWFsbHkuCgo8ZGl2IGNsYXNzID0gImFsZXJ0Ij4KVGhlIHRlcm0gX3RpZHlfIGlzIGEgbGl0dGxlIHVuZm9ydHVuYXRlLgoKKiBEYXRhIHRoYXQgaXMgbm90IF90aWR5XyBpc24ndCBuZWNlc3NhcmlseSBfYmFkXy4KKiBGb3IgaHVtYW4gcmVhZGluZywgYW5kICBmb3Igc29tZSBjb21wdXRhdGlvbnMsIGRhdGEgaW4gYSB3aWRlciBmb3JtYXQgY2FuCiAgYmUgYmV0dGVyLgoqIEZvciBvdGhlciBjb21wdXRhdGlvbnMgZGF0YSBpbiBhIGxvbmdlciwgb3IgbmFycm93ZXIsIGZvcm1hdCBjYW4gYmUgYmV0dGVyLgo8L2Rpdj4KCgojIyBUaWJibGVzCgpNYW55IHRvb2xzIGluIHRoZSBgdGlkeXZlcnNlYCBwcm9kdWNlIHNsaWdodGx5IGVuaGFuY2VkIGRhdGEgZnJhbWVzCmNhbGxlZCBfdGliYmxlc186CgpgYGB7cn0KbGlicmFyeSh0aWJibGUpCmZhaXRoZnVsX3RibCA8LSBhc190aWJibGUoZmFpdGhmdWwpCmNsYXNzKGZhaXRoZnVsX3RibCkKYGBgClRpYmJsZXMgcHJpbnQgZGlmZmVyZW50bHkgZnJvbSBzdGFuZGFyZCBkYXRhIGZyYW1lczoKCmBgYHtyLCBoaWdobGlnaHQub3V0cHV0PWMoMSwgMywgMTQpfQpmYWl0aGZ1bF90YmwKYGBgCgpGb3IgdGhlIG1vc3QgcGFydCBkYXRhIGZyYW1lcyBhbmQgdGliYmxlcyBjYW4gYmUgdXNlZCBpbnRlcmNoYW5nZWFibHkuCgoKIyMgVGlkeWluZyBEYXRhCgpNYW55IGRhdGEgc2V0cyBhcmUgaW4gdGlkeSBmb3JtIGFscmVhZHkuCgpJZiB0aGV5IGFyZSBub3QsIHRoZXkgY2FuIGJlIHB1dCBpbnRvIHRpZHkgZm9ybS4KClRoZSB0b29scyBmb3IgdGhpcyBhcmUgcGFydCBvZiBfZGF0YSB0ZWNobm9sb2dpZXNfLgoKVGhlIHRhc2tzIGludm9sdmVkIGFyZSBwYXJ0IG9mIHdoYXQgaXMgc29tZXRpbWVzIGNhbGxlZCBfZGF0YQp3cmFuZ2xpbmdfLgoKCiMjIEFuIEV4YW1wbGU6IEdsb2JhbCBBdmVyYWdlIFN1cmZhY2UgVGVtcGVyYXR1cmVzCgpBbW9uZyBtYW55IGRhdGEgc2V0cyBhdmFpbGFibGUgYXQKPGh0dHBzOi8vZGF0YS5naXNzLm5hc2EuZ292L2dpc3RlbXAvPiBpcyBkYXRhIG9uIG1vbnRobHkgZ2xvYmFsCmF2ZXJhZ2Ugc3VyZmFjZSB0ZW1wZXJhdHVyZXMgb3ZlciBhIG51bWJlciBvZiB5ZWFycy4KCjwhLS0gcGF5d2FsbGVkIG5vdywgc28gdXNlIHd3dy9hcmNoaXZlLm9yZyAtLT4KClRoZXNlIGRhdGEgZnJvbSAyMDE3IHdlcmUgdXNlZCBmb3IgdGhlIHdpZGVseSBjaXRlZApbQmxvb21iZXJnIGhvdHRlc3QgeWVhciB2aXN1YWxpemF0aW9uXShodHRwczovL3d3dy5ibG9vbWJlcmcuY29tL2dyYXBoaWNzL2hvdHRlc3QteWVhci1vbi1yZWNvcmQvMjAxNi8pLgoKW0EgdmVyc2lvbiBmcm9tIDIwMTVdKGh0dHBzOi8vd3d3LmJsb29tYmVyZy5jb20vZ3JhcGhpY3MvaG90dGVzdC15ZWFyLW9uLXJlY29yZC8yMDE1L2VtYmVkLykgbWF5IHdvcmsgYSBsaXR0bGUgYmV0dGVyLgoKVGhlIGN1cnJlbnQgZGF0YSBhcmUgYXZhaWxhYmxlIGluIGEgZm9ybWF0dGVkIHRleHQgZmlsZSBhdAoKPGh0dHBzOi8vZGF0YS5naXNzLm5hc2EuZ292L2dpc3RlbXAvdGFibGVkYXRhX3Y0L0dMQi5UcytkU1NULnR4dD4KCm9yIGFzIGEKW19DU1ZfIChjb21tYS1zZXBhcmF0ZWQgdmFsdWVzKV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ29tbWEtc2VwYXJhdGVkX3ZhbHVlcykgZmlsZSBhdAoKPGh0dHBzOi8vZGF0YS5naXNzLm5hc2EuZ292L2dpc3RlbXAvdGFibGVkYXRhX3Y0L0dMQi5UcytkU1NULmNzdj4KCmBgYHtyIGRvd25sb2FkLUdMQiwgZWNobyA9IEZBTFNFfQpgYGAKClRoZSBmaXJzdCBmZXcgbGluZXMgb2YgdGhlIENTViBmaWxlOgoKYGBge3J9CiN8IGVjaG86IGZhbHNlCiN8IGNvbW1lbnQ6ICIgICAiCnJlYWRMaW5lcygiR0xCLlRzK2RTU1QuY3N2IiwgNikgfD4gd3JpdGVMaW5lcygpCmBgYAoKVGhlIENTViBmaWxlIGlzIGEgbGl0dGxlIGVhc2llciAoZm9yIGEgY29tcHV0ZXIgcHJvZ3JhbSkgdG8gcmVhZCBpbiwKc28gd2Ugd2lsbCB3b3JrIHdpdGggdGhhdC4KClRoZSBudW1iZXJzIGluIHRoZSBDU1YgZmlsZSByZXByZXNlbnQgZGV2aWF0aW9ucyBpbiBkZWdyZWVzIENlbGNpdXMKZnJvbSB0aGUgYXZlcmFnZSB0ZW1wZXJhdHVyZSBmb3IgdGhlIGJhc2UgcGVyaW9kIDE5NTEtMTk4MC4KClRoZSBmaWxlIGF2YWlsYWJsZSBvbiBKYW51YXJ5IDE2LCAyMDI1LCBpcyBub3cgYXZhaWxhYmxlCltsb2NhbGx5XShodHRwczovL3N0YXQudWlvd2EuZWR1L35sdWtlL2RhdGEvR0xCLlRzK2RTU1QuY3N2KS4KCldlIGNhbiBtYWtlIHN1cmUgaXQgaGFzIGJlZW4gZG93bmxvYWRlZCB0byBvdXIgd29ya2luZyBkaXJlY3Rvcnkgd2l0aAoKYGBge3IgZG93bmxvYWQtR0xCfQppZiAoISBmaWxlLmV4aXN0cygiR0xCLlRzK2RTU1QuY3N2IikpCiAgICBkb3dubG9hZC5maWxlKCJodHRwczovL3N0YXQudWlvd2EuZWR1L35sdWtlL2RhdGEvR0xCLlRzK2RTU1QuY3N2IiwKICAgICAgICAgICAgICAgICAgIkdMQi5UcytkU1NULmNzdiIpCmBgYAoKQXNzdW1pbmcgdGhpcyBsb2NhbGx5IGF2YWlsYWJsZSBmaWxlIGhhcyBiZWVuIGRvd25sb2FkZWQsIHdlIGNhbiByZWFkCmluIHRoZSBkYXRhIGFuZCBkcm9wIHNvbWUgY29sdW1ucyB3ZSBkb24ndCBuZWVkIHdpdGgKCmBgYHtyfQpsaWJyYXJ5KHJlYWRyKQpnYXN0IDwtIHJlYWRfY3N2KCJHTEIuVHMrZFNTVC5jc3YiLCBza2lwID0gMSlbMSA6IDEzXQpgYGAKCjxkaXYgY2xhc3MgPSAiYWxlcnQiPgoqIFRoZSBmdW5jdGlvbiBgcmVhZF9jc3ZgIGlzIGZyb20gdGhlIGByZWFkcmAgcGFja2FnZSwgd2hpY2ggaXMgcGFydCBvZiB0aGUKICBgdGlkeXZlcnNlYC4KKiBBbiBhbHRlcm5hdGl2ZSBpcyB0aGUgYmFzZSBSIGZ1bmN0aW9uIGByZWFkLmNzdmAuCjwvZGl2PgoKQSBsb29rIGF0IHRoZSBmaXJzdCBmZXcgbGluZXM6CgpgYGB7cn0KaGVhZChnYXN0LCA1KQpgYGAKCkFuZCB0aGUgbGFzdCBmZXcgbGluZXM6CgpgYGB7cn0KdGFpbChnYXN0LCA1KQpgYGAKClRoZSBgcHJpbnQoKWAgbWV0aG9kIGZvciB0aWJibGVzIGFiYnJldmlhdGVzIHRoZSBvdXRwdXQuCgpJdCBpcyBuZWF0ZXIgYW5kIHByb3ZpZGVzIHNvbWUgdXNlZnVsIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gb24KdmFyaWFibGUgZGF0YSB0eXBlcy4KCkJ1dCBpdCBzaG93cyBvbmx5IHRoZSBmaXJzdCBmZXcgcm93cyBhbmQgbWF5IG5vdCBleHBsaWNpdGx5IHNob3cgc29tZQpjb2x1bW5zLgoKSWYgc29tZSBjb2x1bW5zIGFyZSBza2lwcGVkLCB5b3UgY2FuIGFzayB0byBzZWUgbW9yZSBieSBjYWxsaW5nCmBwcmludCgpYCBleHBsaWNpdGx5OgoKYGBge3J9CnByaW50KHRhaWwoZ2FzdCksIHdpZHRoID0gMTAwKQpgYGAKCjwhLS0KKiBUaGUgbGFzdCB2YWx1ZXMgaW4gdGhlIGBBdWdgIC0gYERlY2AgY29sdW1ucyBhcmUgbWlzc2luZyBhbmQgY29kZWQgYXMgYCoqKmAuCiogVGhpcyBjYXVzZXMgdGhlc2UgY29sdW1ucyB0byBiZSByZWFkIGFzIGNoYXJhY3RlciB2ZWN0b3JzIGluZGljYXRlZAogIGJ5IGA8Y2hyPmAuCiogVGhlIG90aGVycyBoYXZlIGJlZW4gcmVhZCBhcyBudW1lcmljLCBjb2RlZCBgPGRibD5gIChmb3IgCiAgX2RvdWJsZSBwcmVjaXNpb25fKS4KKiBXZSBjYW4gZml4IHRoZSB0aGVzZSBjb2x1bW5zIG5vdyBvciBkZWFsIHdpdGggdGhlbSBsYXRlci4KCk9uZSB3YXkgdG8gZml4IHRoZW0gbm93IGlzIHRvIHVzZSBgbXV0YXRlX2lmYDoKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0V9Cmdhc3QgPC0gbXV0YXRlKGdhc3QsIGFjcm9zcyh3aGVyZShpcy5jaGFyYWN0ZXIpLCBhcy5udW1lcmljKSkKaGVhZChnYXN0KQpgYGAKCkVhY2ggb2JzZXJ2YXRpb24gY29uc2lzdHMgb2YgYSB5ZWFyLCBhIG1vbnRoLCBhbmQgYSB0ZW1wZXJhdHVyZS4KLS0+CgpUaGUgZm9ybWF0IHdpdGggb25lIGNvbHVtbiBwZXIgbW9udGggaXMgY29tcGFjdCBhbmQgdXNlZnVsIGZvciB2aWV3aW5nCmFuZCBkYXRhIGVudHJ5LgoKQnV0IGl0IGlzIG5vdCBpbiBfdGlkeSBmb3JtYXRfIHNpbmNlCgoqIHRoZSBtb250aGx5IHRlbXBlcmF0dXJlICB2YXJpYWJsZSBpcyBzcHJlYWQgb3ZlciAxMiBjb2x1bW5zOwoqIHRoZSBtb250aCB2YXJpYWJsZSBpcyBlbmNvZGVkIGluIHRoZSBjb2x1bW4gaGVhZGluZ3MuCgpGb3Igb2J2aW91cyByZWFzb25zIHRoaXMgZGF0YSBmb3JtYXQgaXMgb2Z0ZW4gcmVmZXJyZWQgdG8gYXMgX3dpZGUKZm9ybWF0Xy4KClRoZSB0aWR5LCBvciBfbG9uZ18sIGZvcm1hdCB3b3VsZCBoYXZlIHRocmVlIHZhcmlhYmxlczogYFllYXJgLApgTW9udGhgLCBhbmQgYFRlbXBgLgoKT25lIHdheSB0byBwdXQgdGhpcyBkYXRhIGZyYW1lIGluIHRpZHksIG9yIGxvbmcsIGZvcm1hdCB1c2VzCmBwaXZvdF9sb25nZXIoKWAgZnJvbSB0aGUgYHRpZHlyYCBwYWNrYWdlOgoKYGBge3J9CmxpYnJhcnkodGlkeXIpCmxnYXN0IDwtIHBpdm90X2xvbmdlcihnYXN0LAogICAgICAgICAgICAgICAgICAgICAgLVllYXIsICAjIyBzcGVjaWZpZXMgdGhlIGNvbHVtbnMgdG8gdXNlIC0tIGFsbCBidXQgWWVhcgogICAgICAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiTW9udGgiLAogICAgICAgICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIlRlbXAiKQpgYGAKClRoZSBmaXJzdCBmZXcgcm93cyBvZiB0aGUgcmVzdWx0OgoKYGBge3J9CmhlYWQobGdhc3QpCmBgYAoKRHVyaW5nIHBsb3R0aW5nIGl0IGlzIGxpa2VseSB0aGF0IHRoZSBgTW9udGhgIHZhcmlhYmxlIHdpbGwgYmUKY29udmVydGVkIHRvIGEgX2ZhY3Rvcl8uCgpCeSBkZWZhdWx0LCB0aGlzIHdpbGwgb3JkZXIgbGV2ZWxzIGFscGhhYmV0aWNhbGx5LCB3aGljaCBpcyBub3Qgd2hhdCB3ZSB3YW50OgoKYGBge3J9CmxldmVscyhhcy5mYWN0b3IobGdhc3QkTW9udGgpKQpgYGAKCldlIGNhbiBndWFyZCBhZ2FpbnN0IHRoaXMgYnkgY29udmVydGluZyBgTW9udGhgIHRvIGEgZmFjdG9yIHdpdGggdGhlCnJpZ2h0IGxldmVscyBub3c6CgpgYGB7cn0KbGdhc3QgPC0gbXV0YXRlKGxnYXN0LCBNb250aCA9IGZhY3RvcihNb250aCwgbGV2ZWxzID0gbW9udGguYWJiKSkKbGV2ZWxzKGxnYXN0JE1vbnRoKQpgYGAKCjwhLS0gcGF5d2FsbGVkIG5vdywgc28gdXNlIHd3dy9hcmNoaXZlLm9yZyAtLT4KCldlIGNhbiBub3cgdXNlIHRoaXMgdGlkeSB2ZXJzaW9uIG9mIHRoZSBkYXRhIHRvIGNyZWF0ZSBhIHN0YXRpYwp2ZXJzaW9uIG9mIHRoZSBbQmxvb21iZXJnIGhvdHRlc3QgeWVhcgp2aXN1YWxpemF0aW9uXShodHRwczovL3dlYi5hcmNoaXZlLm9yZy93ZWIvMjAxOTAyMDIxOTQ0MzIvaHR0cHM6Ly93d3cuYmxvb21iZXJnLmNvbS9ncmFwaGljcy9ob3R0ZXN0LXllYXItb24tcmVjb3JkLykuCgpUaGUgYmFzaWMgZnJhbWV3b3JrIGlzIHNldCB1cCB3aXRoCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpwIDwtIGdncGxvdChsZ2FzdCkgKwogICAgZ2d0aXRsZSgiR2xvYmFsIEF2ZXJhZ2UgU3VyZmFjZSBUZW1wZXJhdHVyZXMiKSArCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKYGBgCgo8ZGl2IGNsYXNzID0gImFsZXJ0Ij4KYGdncGxvdGAgb2JqZWN0cyBvbmx5IHByb2R1Y2Ugb3V0cHV0IHdoZW4gdGhleSBhcmUgcHJpbnRlZC4KClRvIHNlZSB0aGUgcGxvdCBpbiBgcGAgd2UgbmVlZCB0byBwcmludCBpdCwgZm9yIGV4YW1wbGUgYnkgdXNpbmcgYQpsaW5lIHdpdGggb25seSBgcGAuCjwvZGl2PgoKYGBge3IgZ2FzdC1iYXNlLCBldmFsID0gRkFMU0V9CiBwCmBgYApgYGB7ciBnYXN0LWJhc2UsIGV2YWwgPSBUUlVFLCBlY2hvID0gRkFMU0V9CmBgYAoKVGhlbiBhZGQgYSBsYXllciB3aXRoIGxpbmVzIGZvciBlYWNoIHllYXIgKHNwZWNpZmllZCBieSB0aGUgYGdyb3VwYAphcmd1bWVudCB0byBgZ2VvbV9saW5lYCkuCgpgYGB7ciBnYXN0LWxpbmVzLCBldmFsID0gRkFMU0V9CnAgKyBnZW9tX2xpbmUoYWVzKHggPSBNb250aCwKICAgICAgICAgICAgICAgICAgeSA9IFRlbXAsCiAgICAgICAgICAgICAgICAgIGdyb3VwID0gWWVhcikpCmBgYApgYGB7ciBnYXN0LWxpbmVzLCBldmFsID0gVFJVRSwgZWNobyA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CmBgYAoKV2UgY2FuIHVzZSBjb2xvciB0byBkaXN0aW5naXNoIHRoZSB5ZWFycy4KCmBgYHtyIGdhc3QtY29sb3IxLCBldmFsID0gRkFMU0V9CnAxIDwtIHAgKwogICAgZ2VvbV9saW5lKGFlcyh4ID0gTW9udGgsCiAgICAgICAgICAgICAgICAgIHkgPSBUZW1wLAogICAgICAgICAgICAgICAgICBjb2xvciA9IFllYXIsCiAgICAgICAgICAgICAgICAgIGdyb3VwID0gWWVhciksCiAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKQpwMQpgYGAKU2F2aW5nIHRoZSBwbG90IHNwZWNpZmljYXRpb24gaW4gdGhlIHZhcmlhYmxlIGBwMWAgd2lsbCBtYWtlIGl0IGVhc2llcgp0byBleHBlcmltZW50IHdpdGggY29sb3IgdmFyaWF0aW9uczoKCmBgYHtyIGdhc3QtY29sb3IxLCBldmFsID0gVFJVRSwgZWNobyA9IEZBTFNFfQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CiMjIENvbXB1dGUgdGhlIHBhc3QgeWVhciBhbmQgbWFrZSBzdXJlIGl0IGlzIGluIHRoZSBmaWxlCmxpYnJhcnkobHVicmlkYXRlKQpwYXN0X3llYXIgPC0geWVhcih0b2RheSgpKSAtIDEKcGFzdF95ZWFyCnN0b3BpZm5vdChwYXN0X3llYXIgJWluJSBsZ2FzdCRZZWFyKQpgYGAKCk9uZSB3YXkgdG8gaGlnaGxpZ2h0IHRoZSBgcGFzdF95ZWFyYCBgciBwYXN0X3llYXJgOgoKYGBge3IgZ2FzdC1jb2xvcjIsIGV2YWwgPSBGQUxTRX0KbGdhc3RfbGFzdCA8LSBmaWx0ZXIobGdhc3QsIFllYXIgPT0gcGFzdF95ZWFyKQpwMSArIGdlb21fbGluZShhZXMoeCA9IE1vbnRoLAogICAgICAgICAgICAgICAgICAgeSA9IFRlbXAsCiAgICAgICAgICAgICAgICAgICBncm91cCA9IFllYXIpLAogICAgICAgICAgICAgICBsaW5ld2lkdGggPSAxLAogICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLAogICAgICAgICAgICAgICBkYXRhID0gbGdhc3RfbGFzdCwKICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKQpgYGAKYGBge3IgZ2FzdC1jb2xvcjIsIGVjaG8gPSBGQUxTRX0KYGBgCgpBIHVzZWZ1bCB3YXkgdG8gc2hvdyBtb3JlIHJlY2VudCBkYXRhIGluIHRoZSBjb250ZXh0IG9mIHRoZSBmdWxsIGRhdGEKaXMgdG8gc2hvdyB0aGUgZnVsbCBkYXRhIGluIGdyZXkgYW5kIHRoZSBtb3JlIHJlY2VudCB5ZWFycyBpbiBibGFjazoKCmBgYHtyIGdhc3QtZnVsbC1ncmV5LCBldmFsID0gRkFMU0V9CmxnYXN0MmsgPC0gZmlsdGVyKGxnYXN0LCBZZWFyID49IDIwMDApCmdncGxvdChsZ2FzdCwgYWVzKHggPSBNb250aCwKICAgICAgICAgICAgICAgICAgeSA9IFRlbXAsCiAgICAgICAgICAgICAgICAgIGdyb3VwID0gWWVhcikpICsKICAgIGdlb21fbGluZShjb2xvciA9ICJncmV5ODAiKSArCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgZ2VvbV9saW5lKGRhdGEgPSBsZ2FzdDJrKQpgYGAKYGBge3IgZ2FzdC1mdWxsLWdyZXksIGVjaG8gPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQpgYGAKCklmIHlvdSB3YW50IHRvIHVwZGF0ZSB5b3VyIHBsb3QgbGF0ZXIgaW4gdGhlIHllYXIgdGhlbiB0aGUgY3VycmVudAp5ZWFyJ3MgZW50cnkgbWF5IGNvbnRhaW4gbWlzc2luZyB2YWx1ZSBpbmRpY2F0b3JzIHRoYXQgeW91IHdpbGwgaGF2ZQp0byBkZWFsIHdpdGguCjwhLS0KVGhlIHZlcnNpb24gb2YgdGhlIGRhdGEgW29uIHRoZQp3ZWJdKGh0dHBzOi8vZGF0YS5naXNzLm5hc2EuZ292L2dpc3RlbXAvdGFibGVkYXRhX3YzL0dMQi5UcytkU1NULnR4dCkKbWF5IGhhdmUgYmVlbiB1cGRhdGVkIHRvIGluY2x1ZGUgdGhlIG1pc3NpbmcgdmFsdWVzIGZvciBgciBwYXN0X3llYXJgLgpJZiB5b3Ugd2FudCB0byB1cGRhdGUgeW91ciBwbG90IGxhdGVyIGluIHRoZSB5ZWFyIHlvdSB3aWxsIHNlZQpzaW1pbGFyIG1pc3NpbmcgdmFsdWUgbWFya2VycyBmb3IgdGhlIHJlbWFpbmluZyBtb250aHMgb2YKYHIgKHBhc3RfeWVhciArIDEpYC4KLS0+CgpUaGUgTmV3IFlvcmsgVGltZXMgb24gSmFudWFyeSAxOCwgMjAxOCwgcHVibGlzaGVkClthbm90aGVyIHZpc3VhbGl6YXRpb25dKGh0dHBzOi8vd3d3Lm55dGltZXMuY29tL2ludGVyYWN0aXZlLzIwMTgvMDEvMTgvY2xpbWF0ZS9ob3R0ZXN0LXllYXItMjAxNy5odG1sKQpvZiB0aGVzZSBkYXRhIHNob3dpbmcgYXZlcmFnZSB5ZWFybHkgdGVtcGVyYXR1cmVzIChbdmlhIEdvb2dsZSBtYXkgd29yayBiZXR0ZXJdKGh0dHBzOi8vd3d3Lmdvb2dsZS5jb20vdXJsP3NhPXQmcmN0PWomcT0mZXNyYz1zJnNvdXJjZT13ZWImY2Q9JmNhZD1yamEmdWFjdD04JnZlZD0yYWhVS0V3al80UG1obk0zMUFoWEZqWWtFSGZReEFURVFGbm9FQ0FZUUFRJnVybD1odHRwcyUzQSUyRiUyRnd3dy5ueXRpbWVzLmNvbSUyRmludGVyYWN0aXZlJTJGMjAxOCUyRjAxJTJGMTglMkZjbGltYXRlJTJGaG90dGVzdC15ZWFyLTIwMTcuaHRtbCZ1c2c9QU92VmF3M3FvdXhIUUFKQ3FydF9GM2ZiTEVHRSkpLgoKVG8gcmVjcmVhdGUgdGhpcyBwbG90IHdlIGZpcnN0IG5lZWQgdG8gY29tcHV0ZSB5ZWFybHkgYXZlcmFnZSB0ZW1wZXJhdHVyZXMuCgpUaGlzIGlzIGVhc3kgdG8gZG8gd2l0aCB0aGUgYHN1bW1hcml6ZSgpYCBhbmQgYGdyb3VwX2J5KClgIGZ1bmN0aW9ucwpmcm9tIGBkcHlyYDoKCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQphdGVtcCA8LSBsZ2FzdCB8PgogICAgZ3JvdXBfYnkoWWVhcikgfD4KICAgIHN1bW1hcml6ZShBdmVUZW1wID0gbWVhbihUZW1wLCBuYS5ybSA9IFRSVUUpKQpoZWFkKGF0ZW1wKQpgYGAKClVzaW5nIGBuYS5ybSA9IFRSVUVgIGVuc3VyZXMgdGhhdCB0aGUgbWVhbiBpcyBiYXNlZCBvbiB0aGUgYXZhaWxhYmxlCm1vbnRocyBpZiBkYXRhIGZvciBzb21lIG1vbnRocyBpcyBtaXNzaW5nLgoKQSBzaW1wbGUgdmVyc2lvbiBvZiB0aGUgcGxvdCBpcyB0aGVuIHByb2R1Y2VkIGJ5CgpgYGB7ciBnYXN0LW55dCwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoYXRlbXApICsKICAgIGdlb21fcG9pbnQoYWVzKHggPSBZZWFyLCB5ID0gQXZlVGVtcCkpCmBgYApgYGB7ciBnYXN0LW55dCwgZWNobyA9IEZBTFNFfQpgYGAKCkEgdmFyaWF0aW9uIHNob3dpbmcgcmVjb3JkIHllYXJzOgoKYGBge3IgIGdhc3Qtbnl0LXJlYywgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KGdncmVwZWwpCmF0ZW1wX3JlYyA8LSBmaWx0ZXIoYXRlbXAsIGN1bW1heChBdmVUZW1wKSA9PSBBdmVUZW1wKQpnZ3Bsb3QoYXRlbXAsIGFlcyh4ID0gWWVhciwgeSA9IEF2ZVRlbXApKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZ2VvbV9wb2ludChkYXRhID0gYXRlbXBfcmVjLCBjb2xvciA9ICJyZWQiKSArCiAgICBnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gWWVhciksCiAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGF0ZW1wX3JlYywKICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibHVlIikKYGBgCmBgYHtyIGdhc3Qtbnl0LXJlYywgZWNobyA9IEZBTFNFfQpgYGAKCk1hdGNoaW5nIHRoZSByZXByZXNlbnRhdGlvbiBvZiByZWNvcmQgeWVhcnMgaW4gdGhlIEJsb29tYmVyZyBwbG90OgoKYGBge3IgZ2FzdC1iYi1yZWMsIGV2YWwgPSBGQUxTRX0KbGdhc3RfcmVjIDwtIGZpbHRlcihsZ2FzdCwgWWVhciAlaW4lIGF0ZW1wX3JlYyRZZWFyKQpnZ3Bsb3QobGdhc3QsIGFlcyh4ID0gTW9udGgsIHkgPSBUZW1wLCBncm91cCA9IFllYXIpKSArCiAgICBnZW9tX2xpbmUoY29sb3IgPSAiZ3JleTgwIikgKwogICAgdGhlbWVfbWluaW1hbCgpICsKICAgIGdlb21fbGluZShkYXRhID0gbGdhc3RfcmVjLCBhZXMoY29sb3IgPSBUZW1wKSkgKwogICAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93ID0gImJsdWUiLCBoaWdoID0gInJlZCIpCmBgYApgYGB7ciBnYXN0LWJiLXJlYywgZWNobyA9IEZBTFNFfQpgYGAKCkFub3RoZXIgdmFyaWF0aW9uIG9uIHRoZSBCbG9vbWJlcmcgcGxvdCBzaG93aW5nIGp1c3QgYSBmZXcgeWVhcnMgMjAKeWVhcnMgYXBhcnQ6CgpgYGB7ciBnYXN0LXNraXAsIGV2YWwgPSBGQUxTRX0KbGdfYnlfMjAgPC0KICAgIGZpbHRlcihsZ2FzdCwKICAgICAgICAgICBZZWFyICVpbiUgc2VxKDIwMjAsIGJ5ID0gLTIwLCBsZW4gPSA1KSkgfD4KICAgIG11dGF0ZShZZWFyID0gZmFjdG9yKFllYXIpKQpnZ3Bsb3QobGdfYnlfMjAsIGFlcyh4ID0gTW9udGgsCiAgICAgICAgICAgICAgICAgICAgIHkgPSBUZW1wLAogICAgICAgICAgICAgICAgICAgICBncm91cCA9IFllYXIsCiAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gWWVhcikpICsKICAgIGdlb21fbGluZSgpCmBgYApDb252ZXJ0aW5nIGBZZWFyYCB0byBhIGZhY3RvciByZXN1bHRzIGluIGEgZGlzY3JldGUgY29sb3Igc2NhbGUgYW5kIGxlZ2VuZC4KYGBge3IgZ2FzdC1za2lwLCBlY2hvID0gRkFMU0V9CmBgYAoKCiMjIEhhbmRsaW5nIE1pc3NpbmcgVmFsdWVzCgpUaGUgZGF0YSBmb3IgMjAxOSBhdmFpbGFibGUgaW4gZWFybHkgMjAyMCBpcyBhbHNvIGF2YWlsYWJsZQpbbG9jYWxseV0oaHR0cHM6Ly9zdGF0LnVpb3dhLmVkdS9+bHVrZS9kYXRhL0dMQi5UcytkU1NULTIwMTkuY3N2KS4KCmBgYHtyLCBlY2hvID0gRkFMU0V9CmlmICghIGZpbGUuZXhpc3RzKCJHTEIuVHMrZFNTVC0yMDE5LmNzdiIpKQogICAgZG93bmxvYWQuZmlsZSgiaHR0cHM6Ly9zdGF0LnVpb3dhLmVkdS9+bHVrZS9kYXRhL0dMQi5UcytkU1NULTIwMTkuY3N2IiwKICAgICAgICAgICAgICAgICAgIkdMQi5UcytkU1NULTIwMTkuY3N2IikKYGBgCgpBc3N1bWluZyB0aGlzIGxvY2FsbHkgYXZhaWxhYmxlIGZpbGUgaGFzIGJlZW4gZG93bmxvYWRlZCwgd2UgY2FuIHJlYWQKaW4gdGhlIGRhdGEgYW5kIGRyb3Agc29tZSBjb2x1bW5zIHdlIGRvbid0IG5lZWQgd2l0aAoKYGBge3J9Cmdhc3QyMDE5IDwtIHJlYWRfY3N2KCJHTEIuVHMrZFNTVC0yMDE5LmNzdiIsIHNraXAgPSAxKVsxIDogMTNdCmBgYAoKVGhlIGxhc3QgdGhyZWUgY29sdW1ucyBhcmUgcmVhZCBhcyBjaGFyYWN0ZXIgdmFyaWFibGVzOgoKYGBge3J9CmhlYWQoZ2FzdDIwMTksIDUpCmBgYAoKVGhlIHJlYXNvbiBpcyB0aGF0IGRhdGEgZm9yIE9jdG9iZXIgdGhyb3VnaCBEZWNlbWJlciB3ZXJlIG5vdAphdmFpbGFibGU6CgpgYGB7cn0KdGFpbChnYXN0MjAxOSwgMikKYGBgCgpXZSB3YW50IHRvIGNvbnZlcnQgdGhlc2UgY29sdW1ucyB0byBudW1lcmljLCB3aXRoIG1pc3NpbmcgdmFsdWVzCnJlcHJlc2VudGVkIGFzIGBOQWAuCgpPbmUgb3B0aW9uIGlzIHRvIGhhbmRsZSB0aGVtIGluZGl2aWR1YWxseToKCmBgYHtyfQpnYXN0MjAxOSRPY3QgPC0gYXMubnVtZXJpYyhnYXN0MjAxOSRPY3QpCnRhaWwoZ2FzdDIwMTksIDIpCmBgYAoKQW5vdGhlciBvcHRpb24gaXMgdG8gY29udmVydCBhbGwgY2hhcmFjdGVyIGNvbHVtbnMgdG8gbnVtZXJpYyB3aXRoCgpgYGB7cn0KZ2FzdDIwMTkgPC0gbXV0YXRlKGdhc3QyMDE5LCBhY3Jvc3Mod2hlcmUoaXMuY2hhcmFjdGVyKSwgYXMubnVtZXJpYykpCnRhaWwoZ2FzdDIwMTksIDIpCmBgYAoKVGhlIHdhcm5pbmdzIGFyZSBiZW5pZ24gYW5kIGNhbiBiZSBzdXBwcmVzc2VkIHdpdGggdGhlIGB3YXJuaW5nID0KRkFMU0VgIGNodW5rIG9wdGlvbi4KClNpbmNlIHdlIGtub3cgdGhlIG1pc3NpbmcgdmFsdWUgcGF0dGVybiBgKioqYCB3ZSBjYW4gYWxzbyBhdm9pZCB0aGUKbmVlZCB0byBmaXggdGhlIGRhdGEgYWZ0ZXIgdGhlIGZhY3QgYnkgc3BlY2lmeWluZyB0aGlzIGF0IHJlYWQgdGltZToKCmBgYHtyfQpnYXN0MjAxOSA8LSByZWFkX2NzdigiR0xCLlRzK2RTU1QtMjAxOS5jc3YiLCBuYSA9ICIqKioiLCBza2lwID0gMSlbMSA6IDEzXQp0YWlsKGdhc3QyMDE5LCAyKQpgYGAKCkEgcGxvdCBoaWdobGlnaHRpbmcgdGhlIHllYXIgMjAxOSBzaG93cyBvbmx5IHRoZSBtb250aHMgd2l0aCBhdmFpbGFibGUKZGF0YToKCmBgYHtyIGdhc3QtMjAxOSwgZXZhbCA9IEZBTFNFfQpsZ2FzdDIwMTkgPC0gZ2FzdDIwMTkgfD4KICAgIHBpdm90X2xvbmdlcigtWWVhciwKICAgICAgICAgICAgICAgICBuYW1lc190byA9ICJNb250aCIsCiAgICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIlRlbXAiKSB8PgogICAgbXV0YXRlKE1vbnRoID0gZmFjdG9yKE1vbnRoLCBsZXZlbHMgPSBtb250aC5hYmIpKQpnZ3Bsb3QobGdhc3QyMDE5LCBhZXMoeCA9IE1vbnRoLAogICAgICAgICAgICAgICAgICAgICAgeSA9IFRlbXAsCiAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IFllYXIpKSArCiAgICBnZW9tX2xpbmUoY29sb3IgPSAiZ3JleTgwIiwKICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpICsKICAgIGdlb21fbGluZShkYXRhID0gZmlsdGVyKGxnYXN0MjAxOSwgWWVhciA9PSAyMDE5KSwKICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpCmBgYAoKQWRkaW5nIGBuYS5ybSA9IFRSVUVgIGluIHRoZSBgZ2VvbV9saW5lYCBjYWxscyBzdXBwcmVzc2VzIHdhcm5pbmdzOwp0aGUgcGxvdCB3b3VsZCBiZSB0aGUgc2FtZSB3aXRob3V0IHRoZXNlLgoKYGBge3IgZ2FzdC0yMDE5LCBlY2hvID0gRkFMU0V9CmBgYAoKPGRpdiBjbGFzcyA9ICJhbGVydCI+Ck91dGxpbmUgb2YgdGhlIHRvb2xzIHVzZWQ6CgoqIERhdGEgcHJvY2Vzc2luZzoKICAgICogUmVhZGluZzogYHJlYWRfY3N2YCwgYHJlYWQuY3N2YDsKICAgICogUmVzaGFwaW5nOiBgcGl2b3RfbG9uZ2VyYDsKICAgICogQ2xlYW5pbmc6IGBpcy5jaGFyYWN0ZXJgLCBgYXMubnVtZXJpY2AsIGBtdXRhdGVgLCBgYWNyb3NzYCwgYGZhY3RvcmA7CiAgICAqIFN1bW1hcml6aW5nOiBgZ3JvdXBfYnlgLCBgc3VtbWFyaXplYC4KKiBWaXN1YWxpemF0aW9uIGdlb21ldHJpZXM6CiAgICAqIGBnZW9tX2xpbmVgIGZvciBhIGxpbmUgcGxvdDsKICAgICogYGdlb21fcG9pbnRgIGZvciBhIHNjYXR0ZXIgcGxvdC4KPC9kaXY+CgoKIyMgUmVhZGluZwoKU3RldmVucycgY2xhc3NpZmljYXRpb24gb2Ygc2NhbGVzIG9mIG1lYXN1cmVtZW50IGlzIGRlc2NyaWJlZCBpbiBhCltXaWtpcGVkaWEKYXJ0aWNsZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTGV2ZWxfb2ZfbWVhc3VyZW1lbnQpLgoKQSBnb29kIGludHJvZHVjdGlvbiB0byB0aGUgY29uY2VwdCBvZiBfdGlkeSBkYXRhXyBpcyBwcm92aWRlZCBpbiBhCltjaGFwdGVyIGluIF9SIGZvciBEYXRhClNjaWVuY2VfXShodHRwczovL3I0ZHMuaGFkbGV5Lm56L2RhdGEtdGlkeS5odG1sKS4KCgojIyBJbnRlcmFjdGl2ZSBUdXRvcmlhbAoKQW4gaW50ZXJhY3RpdmUgW2BsZWFybnJgXShodHRwczovL3JzdHVkaW8uZ2l0aHViLmlvL2xlYXJuci8pIHR1dG9yaWFsCmZvciB0aGVzZSBub3RlcyBpcyBbYXZhaWxhYmxlXShgciBXTE5LKCJ0dXRvcmlhbHMvZGF0YWZybS5SbWQiKWApLgoKWW91IGNhbiBydW4gdGhlIHR1dG9yaWFsIHdpdGgKCmBgYHtyLCBldmFsID0gRkFMU0V9ClNUQVQ0NTgwOjpydW5UdXRvcmlhbCgiZGF0YWZybSIpCmBgYAoKCiMjIEV4ZXJjaXNlcwoKMS4gV2hpY2ggb2YgdGhlIFN0ZXZlbnMgY2xhc3NpZmljYXRpb25zIChub21pbmFsLCBvcmRpbmFsLCBpbnRlcnZhbCwgcmF0aW8pCiAgIGJlc3QgY2hhcmFjdGVyaXplcyB0aGVzZSB2YXJpYWJsZXM6CgogICAgYS4gRGFpbHkgbWF4aW1hbCB0ZW1wZXJhdHVyZXMgaW4gSW93YSBDaXR5LgogICAgYi4gUG9wdWxhdGlvbiBjb3VudHMgZm9yIElvd2EgY291bnRpZXMuCiAgICBjLiBFZHVjYXRpb24gbGV2ZWwgb2Ygam9iIGFwcGxpY2FudHMgdXNpbmcgdGhlIFtCdXJlYXUgb2YgTGFib3IKICAgICAgIFN0YXRpc3RpY3MKICAgICAgIGNsYXNzaWZpY2F0aW9uXShodHRwczovL3d3dy5ibHMuZ292L2NhcmVlcm91dGxvb2svMjAxNC9hcnRpY2xlL2VkdWNhdGlvbi1sZXZlbC1hbmQtam9icy5odG0pLgogICAgZC4gTWFqb3Igb2YgVUkgc3R1ZGVudHMuCgo8IS0tCldoaWNoIGFuc3dlcnMgYXJlIGNvcnJlY3QgZm9yIGV4ZXJjaXNlIDE6CiogYTogaW50ZXJ2YWw7IGI6IHJhdGlvOyBjOiBvcmRpbmFsOyBkOiBub21pbmFsCiAgYTogbm9taW5hbDsgYjogaW50ZXJ2YWw7IGM6IG9yZGluYWw7IGQ6IHJhdGlvCiAgYTogbm9taW5hbDsgYjogaW50ZXJ2YWw7IGM6IG9yZGluYWw7IGQ6IHJhdGlvCiAgYTogaW50ZXJ2YWw7IGI6IHJhdGlvOyBjOiBub21pbmFsOyBkOiBvcmRpbmFsCmZtdCA8LSBmdW5jdGlvbih4KQogICAgcGFzdGUobGV0dGVyc1tzZXFfYWxvbmcoeCldLCB4LCBzZXAgPSAiOiAiLCBjb2xsYXBzZSA9ICI7ICIpCnN0ZXYgPC0gYygibm9taW5hbCIsICJvcmRpbmFsIiwgImludGVydmFsIiwgInJhdGlvIikKZm10KHNhbXBsZShzdGV2LCA0KSkKLS0+CgoyLiBXaGljaCBvZiB0aGVzZSBkYXRhIHNldHMgYXJlIGluIHRpZHkgZm9ybT8KCiAgICBhLiBUaGUgYnVpbHRpbiBkYXRhIHNldCBgY28yYAogICAgYi4gVGhlIGJ1aWx0aW4gZGF0YSBzZXQgYEJPRGAKICAgIGMuIFRoZSBgd2hvYCBkYXRhIHNldCBpbiBwYWNrYWdlIGB0aWR5cmAgKGB0aWR5cjo6d2hvYCkKICAgIGQuIFRoZSBgbXBnYCBkYXRhIHNldCBpbiBwYWNrYWdlIGBnZ3Bsb3QyYCAoYGdncGxvdDI6Om1wZ2ApCgo8IS0tCldoaWNoIGFuc3dlcnMgYXJlIGNvcnJlY3QgZm9yIGV4ZXJjaXNlIDI6CiogYTogbm90IHRpZHk7IGI6IHRpZHk7IGM6IG5vdCB0aWR5OyBkOiB0aWR5CiogYTogdGlkeTsgYjogdGlkeTsgYzogbm90IHRpZHk7IGQ6IHRpZHkKKiBhOiBub3QgdGlkeTsgYjogbm90IHRpZHk7IGM6IG5vdCB0aWR5OyBkOiB0aWR5CiogYTogbm90IHRpZHk7IGI6IHRpZHk7IGM6IHRpZHk7IGQ6IG5vdCB0aWR5Ci0tPgoKVGhlIG5leHQgZXhlcmNpc2VzIHVzZSB0aGUgZGF0YSBpbiB0aGUgdmFyaWFibGUgYGdhcG1pbmRlcmAgaW4gdGhlIHBhY2thZ2UKYGdhcG1pbmRlcmAuIFlvdSBjYW4gbWFrZSBpdCBhdmFpbGFibGUgd2l0aAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZGF0YShnYXBtaW5kZXIsIHBhY2thZ2UgPSAiZ2FwbWluZGVyIikKYGBgCjMuIFVzZSB0aGUgZnVuY3Rpb24gYHN0cmAgdG8gZXhhbWluZSB0aGUgdmFsdWUgb2YgdGhlIGdhcG1pbmRlcgogICB2YXJpYWJsZS4gIEhvdyBtYW55IGNhc2VzIGFyZSB0aGVyZSBpbiB0aGUgZGF0YSBzZXQ/IEhvdyBtYW55IG9mCiAgIHRoZSB2YXJpYWJsZXMgYXJlIGZhY3RvcnM/Cgo0LiBVc2UgdGhlIGZ1bmN0aW9ucyBgY2xhc3NgIGFuZCBgbmFtZXNgIHRvIGZpbmQgdGhlIGNsYXNzIGFuZAogICB2YXJpYWJsZSBuYW1lcyBpbiB0aGUgYGdhcG1pbmRlcmAgZGF0YS4KCjUuIFVzZSBgc3VtbWFyeWAgdG8gY29tcHV0ZSBzdW1tYXJ5IGluZm9ybWF0aW9uIGZvciB0aGUgdmFyaWFibGVzLgoKNi4gRmlsbCBpbiB0aGUgdmFsdWVzIGZvciBgLS0tYCBuZWVkZWQgdG8gcHJvZHVjZSBwbG90cyBvZiBsaWZlCiAgIGV4cGVjdGFuY3kgYWdhaW5zdCB5ZWFyIGZvciB0aGUgY291bnRyaWVzIGluIGNvbnRpbmVudCBPY2VhbmlhLgo8IS0tICMjIG5vbGludCBzdGFydCAtLT4KYGBge3IsIGV2YWwgPSBGQUxTRX0KbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpkYXRhKGdhcG1pbmRlciwgcGFja2FnZSA9ICJnYXBtaW5kZXIiKQpnZ3Bsb3QoZmlsdGVyKGdhcG1pbmRlciwgY29udGluZW50ID09ICJPY2VhbmlhIiksCiAgICAgICBhZXMoeCA9IC0tLSwgeSA9IC0tLSwgY29sb3IgPSBjb3VudHJ5KSkgKwogICAgZ2VvbV9saW5lKCkKYGBgCjwhLS0gIyMgbm9saW50IGVuZCAtLT4K