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 observacion .
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 is 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 .
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,-.18,-.24,-.08,-.16,-.09,-.21,-.18,-.10,-.14,-.23,-.21,-.17,-.16,***,***,-.11,-.16,-.19
1881,-.19,-.13,.04,.06,.07,-.18,.01,-.03,-.15,-.21,-.18,-.06,-.08,-.09,-.16,.05,-.07,-.18
1882,.17,.14,.05,-.15,-.13,-.22,-.16,-.07,-.14,-.23,-.16,-.36,-.11,-.08,.08,-.08,-.15,-.18
1883,-.29,-.36,-.12,-.18,-.17,-.07,-.07,-.13,-.22,-.11,-.24,-.11,-.17,-.19,-.33,-.16,-.09,-.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 19, 2023, 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 a 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.18 -0.24 -0.08 -0.16 -0.09 -0.21 -0.18 -0.1 -0.14 -0.23 -0.21 -0.17
## 2 1881 -0.19 -0.13 0.04 0.06 0.07 -0.18 0.01 -0.03 -0.15 -0.21 -0.18 -0.06
## 3 1882 0.17 0.14 0.05 -0.15 -0.13 -0.22 -0.16 -0.07 -0.14 -0.23 -0.16 -0.36
## 4 1883 -0.29 -0.36 -0.12 -0.18 -0.17 -0.07 -0.07 -0.13 -0.22 -0.11 -0.24 -0.11
## 5 1884 -0.12 -0.08 -0.36 -0.4 -0.33 -0.34 -0.3 -0.27 -0.27 -0.24 -0.33 -0.3
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 2018 0.81 0.84 0.88 0.89 0.82 0.77 0.82 0.76 0.8 1.01 0.82 0.92
## 2 2019 0.93 0.94 1.17 1.01 0.85 0.91 0.94 0.94 0.92 1.01 0.99 1.09
## 3 2020 1.16 1.24 1.17 1.13 1.02 0.92 0.9 0.88 0.99 0.89 1.1 0.81
## 4 2021 0.81 0.64 0.89 0.76 0.78 0.84 0.91 0.81 0.92 1 0.93 0.87
## 5 2022 0.91 0.89 1.05 0.84 0.84 0.92 0.93 0.95 0.9 0.97 0.72 0.8
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 2017 1.02 1.13 1.16 0.94 0.91 0.72 0.81 0.87 0.76 0.9 0.88 0.93
## 2 2018 0.81 0.84 0.88 0.89 0.82 0.77 0.82 0.76 0.8 1.01 0.82 0.92
## 3 2019 0.93 0.94 1.17 1.01 0.85 0.91 0.94 0.94 0.92 1.01 0.99 1.09
## 4 2020 1.16 1.24 1.17 1.13 1.02 0.92 0.9 0.88 0.99 0.89 1.1 0.81
## 5 2021 0.81 0.64 0.89 0.76 0.78 0.84 0.91 0.81 0.92 1 0.93 0.87
## 6 2022 0.91 0.89 1.05 0.84 0.84 0.92 0.93 0.95 0.9 0.97 0.72 0.8
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.18
## 2 1880 Feb -0.24
## 3 1880 Mar -0.08
## 4 1880 Apr -0.16
## 5 1880 May -0.09
## 6 1880 Jun -0.21
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 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
2022:
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.166
## 2 1881 -0.0792
## 3 1882 -0.105
## 4 1883 -0.172
## 5 1884 -0.278
## 6 1885 -0.328
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))
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+CgoKIyMgRGF0YSBGcmFtZXM6IE9yZ2FuaXppbmcgQ2FzZXMgYW5kIFZhcmlhYmxlcwoKVGFidWxhciBkYXRhIGluIFIgaXMgdXN1YWxseSBzdG9yZWQgYXMgYSBfZGF0YSBmcmFtZV8uCgpBIGRhdGEgZnJhbWUgaXMgYSBjb2xsZWN0aW9uIG9mIF92YXJpYWJsZXNfLCBlYWNoIHdpdGggYSB2YWx1ZSBmb3IKZXZlcnkgX2Nhc2VfIG9yIF9vYnNlcnZhY2lvbl8uCgpUaGUgYGZhaXRoZnVsYCBkYXRhIHNldCBpcyBhIGRhdGEgZnJhbWU6CgpgYGB7cn0KY2xhc3MoZmFpdGhmdWwpCm5hbWVzKGZhaXRoZnVsKQpgYGAKCk1vc3QgdG9vbHMgd2Ugd29yayB3aXRoIGluIFIgdXNlIGRhdGEgb3JnYW5pemVkIGluIGRhdGEgZnJhbWVzLgoKT3VyIGBwbG90KClgIGFuZCBgbG0oKWAgZXhwcmVzc2lvbnMgZnJvbSB0aGUKW2ludHJvZHVjdG9yeSBzZWN0aW9uXShpbnRyby5odG1sKQpjYW4gYWxzbyB3ZSB3cml0dGVuIGFzCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwbG90KHdhaXRpbmcgfiBlcnVwdGlvbnMsIGRhdGEgPSBmYWl0aGZ1bCwKICAgICB4bGFiID0gIkVydXB0aW9uIHRpbWUgKG1pbikiLAogICAgIHlsYWIgPSAiV2FpdGluZyB0aW1lIHRvIG5leHQgZXJ1cHRpb24gKG1pbikiKQpmaXQgPC0gbG0od2FpdGluZyB+IGVydXB0aW9ucywgZGF0YSA9IGZhaXRoZnVsKQpgYGAKCjxkaXYgY2xhc3MgPSAiYWxlcnQiPgpgcGxvdCgpYCBvbmx5IHVzZXMgdGhlIGBkYXRhYCBhcmd1bWVudCB3aGVuIHRoZSBwbG90IGlzIHNwZWNpZmllZCBhcyBhCl9mb3JtdWxhXywgbGlrZQoKYGBgcgp3YWl0aW5nIH4gZXJ1cHRpb25zCmBgYAo8L2Rpdj4KCgojIyBFeGFtaW5pbmcgdGhlIERhdGEgaW4gYSBEYXRhIEZyYW1lCgpgaGVhZCgpYCBwcm92aWRlcyBhbiBpZGVhIG9mIHdoYXQgdGhlIHJhdyBkYXRhIGxvb2tzIGxpa2U6CmBgYHtyfQpoZWFkKGZhaXRoZnVsKQpgYGAKCmBzdHIoKWAgaXMgYWxzbyB1c2VmdWwgZm9yIGFuIG92ZXJ2aWV3IG9mIGFuIG9iamVjdCdzIHN0cnVjdHVyZToKYGBge3J9CnN0cihmYWl0aGZ1bCkKYGBgCgpBbm90aGVyIHVzZWZ1bCBmdW5jdGlvbiBhdmFpbGFibGUgZnJvbSB0aGUgYGRwbHlyYCBvciBgdGliYmxlYApwYWNrYWdlcyBpcyBgZ2xpbXBzZSgpYDoKCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpnbGltcHNlKGZhaXRoZnVsKQpgYGAKCmBzdW1tYXJ5KClgIHNob3dzIGJhc2ljIHN0YXRpc3RpY2FsIHByb3BlcnRpZXMgb2YgdGhlIHZhcmlhYmxlczoKCmBgYHtyfQpzdW1tYXJ5KGZhaXRoZnVsKQpgYGAKCmBzdW1tYXJ5KClgIHdpdGggYSBjaGFyYWN0ZXIgdmFyaWFibGUgYW5kIGEgZmFjdG9yIHZhcmlhYmxlOgoKYGBge3J9CmZmbCA8LSBtdXRhdGUoZmFpdGhmdWwsCiAgICAgICAgICAgICAgdHlwZSA9IGlmZWxzZShlcnVwdGlvbnMgPCAzLCAic2hvcnQiLCAibG9uZyIpLAogICAgICAgICAgICAgIGZ0eXBlID0gZmFjdG9yKHR5cGUpKQpzdW1tYXJ5KGZmbCkKYGBgCgoKIyMgVmFyaWFibGVzIGluIGEgRGF0YSBGcmFtZQoKQSBEYXRhIGZyYW1lIGlzIGEgbGlzdCwgb3IgdmVjdG9yLCBvZiB2YXJpYWJsZXM6CmBgYHtyfQpsZW5ndGgoZmFpdGhmdWwpCmBgYAoKVGhlIGRvbGxhciBzaWduIGAkYCBjYW4gYmUgdXNlZCB0byBleGFtaW5lIGluZGl2aWR1YWwgdmFyaWFibGVzOgpgYGB7cn0KY2xhc3MoZmFpdGhmdWwkZXJ1cHRpb25zKQpjbGFzcyhmYWl0aGZ1bCR3YWl0aW5nKQpgYGAKClRoZSB2YXJpYWJsZXMgY2FuIGFsc28gYmUgZXh0cmFjdGVkIGJ5IG51bWVyaWNhbCBvciBjaGFyYWN0ZXIgaW5kZXggdXNpbmcgdGhlCiAgZWxlbWVudCBleHRyYWN0aW9uIG9wZXJhdGlvbiBgW1tgOgpgYGB7cn0KY2xhc3MoZmFpdGhmdWxbWzFdXSkKY2xhc3MoZmFpdGhmdWxbWyJ3YWl0aW5nIl1dKQpgYGAKCgojIyBEaW1lbnNpb25zCgpUaGUgbnVtYmVycyBvZiByb3dzIGFuZCBjb2x1bW5zIGNhbiBiZSBvYnRhaW5lZCB1c2luZyBgbnJvdygpYCBhbmQgYG5jb2woKWA6CgpgYGB7cn0KbmNvbChmYWl0aGZ1bCkKbnJvdyhmYWl0aGZ1bCkKYGBgCgpgZGltKClgIHJldHVybnMgYSB2ZWN0b3Igb2YgdGhlIGRpbWVuc2lvbnM6CgpgYGB7cn0KZGltKGZhaXRoZnVsKQpgYGAKCgojIyBTaW1wbGUgVmlzdWFsaXphdGlvbnMKCmBwbG90YCBoYXMgYSBfbWV0aG9kXyBmb3IgZGF0YSBmcmFtZXMgdGhhdCB0cmllcyB0byBwcm92aWRlIGEKcmVhc29uYWJsZSBkZWZhdWx0IHZpc3VhbGl6YXRpb24gZm9yIG51bWVyaWMgZGF0YSBmcmFtZXM6CmBgYHtyfQpwbG90KGZhaXRoZnVsKQpgYGAKCgojIyBTYW1wbGUgRGF0YSBTZXRzCgpUaGUKW2BkYXRhc2V0c2BdKGh0dHA6Ly9zdGF0LmV0aHouY2gvUi1tYW51YWwvUi1kZXZlbC9saWJyYXJ5L2RhdGFzZXRzL2h0bWwvMDBJbmRleC5odG1sKQpwYWNrYWdlIGluIHRoZSBiYXNlIFIgZGlzdHJpYnV0aW9uIGNvbnRhaW5zIGEgbnVtYmVyIG9mIGRhdGEgc2V0cyB5b3UKY2FuIGV4cGxvcmUuCgpBbm90aGVyIHBhY2thZ2Ugd2l0aCBhIHVzZWZ1bCBjb2xsZWN0aW9uIG9mIGRhdGEgc2V0cyBpcyBbYGRzbGFic2BdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3BhY2thZ2U9ZHNsYWJzKS4KCk1hbnkgb3RoZXIgZGF0YSBzZXRzIGFyZSBjb250YWluZWQgaW4gY29udHJpYnV0ZWQgcGFja2FnZXMgYXMgZXhhbXBsZXMuCgpUaGVyZSBhcmUgYWxzbyBtYW55IGNvbnRyaWJ1dGVkIHBhY2thZ2VzIGRlc2lnbmVkIHNwZWNpZmljYWxseSBmb3IKbWFraW5nIHBhcnRpY3VsYXIgZGF0YSBzZXRzIGF2YWlsYWJsZS4KCgojIyBUaWR5IERhdGEKClRoZSB1c2VmdWwgY29uY2VwdCBvZiBhIF90aWR5XyBkYXRhIGZyYW1lIHdhcyBpbnRyb2R1Y2VkIGZhaXJseQpbcmVjZW50bHldKGh0dHBzOi8vd3d3LmpzdGF0c29mdC5vcmcvYXJ0aWNsZS92aWV3L3YwNTlpMTApIGFuZCBpcwpkZXNjcmliZWQgaXMgYSBbY2hhcHRlciBpbiBfUiBmb3IgRGF0YQpTY2llbmNlX10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei90aWR5LWRhdGEuaHRtbCkuCgpUaGUgaWRlYSBpcyB0aGF0CgoqIGV2ZXJ5IG9ic2VydmF0aW9uIHNob3VsZCBjb3JyZXNwb25kIHRvIGEgc2luZ2xlIHJvdzsKKiBldmVyeSB2YXJpYWJsZSBzaG91bGQgY29ycmVzcG9uZCB0byBhIHNpbmdsZSBjb2x1bW4uCgpUaWR5IGRhdGEgaXMgY29tcHV0YXRpb25hbGx5IGNvbnZlbmllbnQsIGFuZCBtYW55IG9mIHRoZSB0b29scyB3ZSB3aWxsCnVzZSBhcmUgZGVzaWduZWQgYXJvdW5kIHRpZHkgZGF0YSBmcmFtZXMuCgpBIGxhcmdlIHJhbmdlIG9mIHRoZXNlIHRvb2xzIGNhbiBiZSBhY2Nlc3NlZCBieSBsb2FkaW5nIHRoZQpgdGlkeXZlcnNlYCBwYWNrYWdlOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKQnV0IGZvciBub3cgSSB3aWxsIGxvYWQgdGhlIG5lZWRlZCBwYWNrYWdlcyBpbmRpdmlkdWFsbHkuCgo8ZGl2IGNsYXNzID0gImFsZXJ0Ij4KVGhlIHRlcm0gX3RpZHlfIGlzIGEgbGl0dGxlIHVuZm9ydHVuYXRlLgoKKiBEYXRhIHRoYXQgaXMgbm90IF90aWR5XyBpc24ndCBuZWNlc3NhcmlseSBfYmFkXy4KKiBGb3IgaHVtYW4gcmVhZGluZywgYW5kICBmb3Igc29tZSBjb21wdXRhdGlvbnMsIGRhdGEgaW4gYSB3aWRlciBmb3JtYXQgY2FuCiAgYmUgYmV0dGVyLgoqIEZvciBvdGhlciBjb21wdXRhdGlvbnMgZGF0YSBpbiBhIGxvbmdlciwgb3IgbmFycm93ZXIsIGZvcm1hdCBjYW4gYmUgYmV0dGVyLgo8L2Rpdj4KCgojIyBUaWJibGVzCgpNYW55IHRvb2xzIGluIHRoZSBgdGlkeXZlcnNlYCBwcm9kdWNlIHNsaWdodGx5IGVuaGFuY2VkIGRhdGEgZnJhbWVzCmNhbGxlZCBfdGliYmxlc186CgpgYGB7cn0KbGlicmFyeSh0aWJibGUpCmZhaXRoZnVsX3RibCA8LSBhc190aWJibGUoZmFpdGhmdWwpCmNsYXNzKGZhaXRoZnVsX3RibCkKYGBgClRpYmJsZXMgcHJpbnQgZGlmZmVyZW50bHkgZnJvbSBzdGFuZGFyZCBkYXRhIGZyYW1lczoKCmBgYHtyLCBoaWdobGlnaHQub3V0cHV0PWMoMSwgMywgMTQpfQpmYWl0aGZ1bF90YmwKYGBgCgpGb3IgdGhlIG1vc3QgcGFydCBkYXRhIGZyYW1lcyBhbmQgdGliYmxlcyBjYW4gYmUgdXNlZCBpbnRlcmNoYW5nZWFibHkuCgoKIyMgVGlkeWluZyBEYXRhCgpNYW55IGRhdGEgc2V0cyBhcmUgaW4gdGlkeSBmb3JtIGFscmVhZHkuCgpJZiB0aGV5IGFyZSBub3QsIHRoZXkgY2FuIGJlIHB1dCBpbnRvIHRpZHkgZm9ybS4KClRoZSB0b29scyBmb3IgdGhpcyBhcmUgcGFydCBvZiBfZGF0YSB0ZWNobm9sb2dpZXNfLgoKVGhlIHRhc2tzIGludm9sdmVkIGFyZSBwYXJ0IG9mIHdoYXQgaXMgc29tZXRpbWVzIGNhbGxlZCBfZGF0YQp3cmFuZ2xpbmdfLgoKCiMjIEFuIEV4YW1wbGU6IEdsb2JhbCBBdmVyYWdlIFN1cmZhY2UgVGVtcGVyYXR1cmVzCgpBbW9uZyBtYW55IGRhdGEgc2V0cyBhdmFpbGFibGUgYXQKPGh0dHBzOi8vZGF0YS5naXNzLm5hc2EuZ292L2dpc3RlbXAvPiBpcyBkYXRhIG9uIG1vbnRobHkgZ2xvYmFsCmF2ZXJhZ2Ugc3VyZmFjZSB0ZW1wZXJhdHVyZXMgb3ZlciBhIG51bWJlciBvZiB5ZWFycy4KClRoZXNlIGRhdGEgZnJvbSAyMDE3IHdlcmUgdXNlZCBmb3IgdGhlIHdpZGVseSBjaXRlZApbQmxvb21iZXJnIGhvdHRlc3QgeWVhciB2aXN1YWxpemF0aW9uXShodHRwczovL3d3dy5ibG9vbWJlcmcuY29tL2dyYXBoaWNzL2hvdHRlc3QteWVhci1vbi1yZWNvcmQvKS4KClRoZSBjdXJyZW50IGRhdGEgYXJlIGF2YWlsYWJsZSBpbiBhIGZvcm1hdHRlZCB0ZXh0IGZpbGUgYXQKCjxodHRwczovL2RhdGEuZ2lzcy5uYXNhLmdvdi9naXN0ZW1wL3RhYmxlZGF0YV92NC9HTEIuVHMrZFNTVC50eHQ+CgpvciBhcyBhCltfQ1NWXyAoY29tbWEtc2VwYXJhdGVkIHZhbHVlcyldKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0NvbW1hLXNlcGFyYXRlZF92YWx1ZXMpIGZpbGUgYXQKCjxodHRwczovL2RhdGEuZ2lzcy5uYXNhLmdvdi9naXN0ZW1wL3RhYmxlZGF0YV92NC9HTEIuVHMrZFNTVC5jc3Y+CgpgYGB7ciBkb3dubG9hZC1HTEIsIGVjaG8gPSBGQUxTRX0KYGBgCgpUaGUgZmlyc3QgZmV3IGxpbmVzIG9mIHRoZSBDU1YgZmlsZToKCmBgYHtyfQojfCBlY2hvOiBmYWxzZQojfCBjb21tZW50OiAiICAgIgpyZWFkTGluZXMoIkdMQi5UcytkU1NULmNzdiIsIDYpIHw+IHdyaXRlTGluZXMoKQpgYGAKClRoZSBDU1YgZmlsZSBpcyBhIGxpdHRsZSBlYXNpZXIgKGZvciBhIGNvbXB1dGVyIHByb2dyYW0pIHRvIHJlYWQgaW4sCnNvIHdlIHdpbGwgd29yayB3aXRoIHRoYXQuCgpUaGUgbnVtYmVycyBpbiB0aGUgQ1NWIGZpbGUgcmVwcmVzZW50IGRldmlhdGlvbnMgaW4gZGVncmVlcyBDZWxjaXVzCmZyb20gdGhlIGF2ZXJhZ2UgdGVtcGVyYXR1cmUgZm9yIHRoZSBiYXNlIHBlcmlvZCAxOTUxLTE5ODAuCgpUaGUgZmlsZSBhdmFpbGFibGUgb24gSmFudWFyeSAxOSwgMjAyMywgaXMgbm93IGF2YWlsYWJsZQpbbG9jYWxseV0oaHR0cHM6Ly9zdGF0LnVpb3dhLmVkdS9+bHVrZS9kYXRhL0dMQi5UcytkU1NULmNzdikuCgpXZSBjYW4gbWFrZSBzdXJlIGl0IGhhcyBiZWVuIGRvd25sb2FkZWQgdG8gb3VyIHdvcmtpbmcgZGlyZWN0b3J5IHdpdGgKCmBgYHtyIGRvd25sb2FkLUdMQn0KaWYgKCEgZmlsZS5leGlzdHMoIkdMQi5UcytkU1NULmNzdiIpKQogICAgZG93bmxvYWQuZmlsZSgiaHR0cHM6Ly9zdGF0LnVpb3dhLmVkdS9+bHVrZS9kYXRhL0dMQi5UcytkU1NULmNzdiIsCiAgICAgICAgICAgICAgICAgICJHTEIuVHMrZFNTVC5jc3YiKQpgYGAKCkFzc3VtaW5nIHRoaXMgbG9jYWxseSBhdmFpbGFibGUgZmlsZSBoYXMgYmVlbiBkb3dubG9hZGVkLCB3ZSBjYW4gcmVhZAppbiB0aGUgZGF0YSBhbmQgZHJvcCBzb21lIGNvbHVtbnMgd2UgZG9uJ3QgbmVlZCB3aXRoCgpgYGB7cn0KbGlicmFyeShyZWFkcikKZ2FzdCA8LSByZWFkX2NzdigiR0xCLlRzK2RTU1QuY3N2Iiwgc2tpcCA9IDEpWzEgOiAxM10KYGBgCgo8ZGl2IGNsYXNzID0gImFsZXJ0Ij4KKiBUaGUgZnVuY3Rpb24gYHJlYWRfY3N2YCBpcyBmcm9tIHRoZSBgcmVhZHJgIHBhY2thZ2UsIHdoaWNoIGlzIHBhcnQgb2YgdGhlCiAgYHRpZHl2ZXJzZWAuCiogQW4gYWx0ZXJuYXRpdmUgaXMgdGhlIGJhc2UgUiBmdW5jdGlvbiBgcmVhZC5jc3ZgLgo8L2Rpdj4KCkEgbG9vayBhIHRoZSBmaXJzdCBmZXcgbGluZXM6CgpgYGB7cn0KaGVhZChnYXN0LCA1KQpgYGAKCkFuZCB0aGUgbGFzdCBmZXcgbGluZXM6CgpgYGB7cn0KdGFpbChnYXN0LCA1KQpgYGAKClRoZSBgcHJpbnQoKWAgbWV0aG9kIGZvciB0aWJibGVzIGFiYnJldmlhdGVzIHRoZSBvdXRwdXQuCgpJdCBpcyBuZWF0ZXIgYW5kIHByb3ZpZGVzIHNvbWUgdXNlZnVsIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gb24KdmFyaWFibGUgZGF0YSB0eXBlcy4KCkJ1dCBpdCBzaG93cyBvbmx5IHRoZSBmaXJzdCBmZXcgcm93cyBhbmQgbWF5IG5vdCBleHBsaWNpdGx5IHNob3cgc29tZQpjb2x1bW5zLgoKSWYgc29tZSBjb2x1bW5zIGFyZSBza2lwcGVkLCB5b3UgY2FuIGFzayB0byBzZWUgbW9yZSBieSBjYWxsaW5nCmBwcmludCgpYCBleHBsaWNpdGx5OgoKYGBge3J9CnByaW50KHRhaWwoZ2FzdCksIHdpZHRoID0gMTAwKQpgYGAKCjwhLS0KKiBUaGUgbGFzdCB2YWx1ZXMgaW4gdGhlIGBBdWdgIC0gYERlY2AgY29sdW1ucyBhcmUgbWlzc2luZyBhbmQgY29kZWQgYXMgYCoqKmAuCiogVGhpcyBjYXVzZXMgdGhlc2UgY29sdW1ucyB0byBiZSByZWFkIGFzIGNoYXJhY3RlciB2ZWN0b3JzIGluZGljYXRlZAogIGJ5IGA8Y2hyPmAuCiogVGhlIG90aGVycyBoYXZlIGJlZW4gcmVhZCBhcyBudW1lcmljLCBjb2RlZCBgPGRibD5gIChmb3IgCiAgX2RvdWJsZSBwcmVjaXNpb25fKS4KKiBXZSBjYW4gZml4IHRoZSB0aGVzZSBjb2x1bW5zIG5vdyBvciBkZWFsIHdpdGggdGhlbSBsYXRlci4KCk9uZSB3YXkgdG8gZml4IHRoZW0gbm93IGlzIHRvIHVzZSBgbXV0YXRlX2lmYDoKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0V9Cmdhc3QgPC0gbXV0YXRlKGdhc3QsIGFjcm9zcyh3aGVyZShpcy5jaGFyYWN0ZXIpLCBhcy5udW1lcmljKSkKaGVhZChnYXN0KQpgYGAKCkVhY2ggb2JzZXJ2YXRpb24gY29uc2lzdHMgb2YgYSB5ZWFyLCBhIG1vbnRoLCBhbmQgYSB0ZW1wZXJhdHVyZS4KLS0+CgpUaGUgZm9ybWF0IHdpdGggb25lIGNvbHVtbiBwZXIgbW9udGggaXMgY29tcGFjdCBhbmQgdXNlZnVsIGZvciB2aWV3aW5nCmFuZCBkYXRhIGVudHJ5LgoKQnV0IGl0IGlzIG5vdCBpbiBfdGlkeSBmb3JtYXRfIHNpbmNlCgoqIHRoZSBtb250aGx5IHRlbXBlcmF0dXJlICB2YXJpYWJsZSBpcyBzcHJlYWQgb3ZlciAxMiBjb2x1bW5zOwoqIHRoZSBtb250aCB2YXJpYWJsZSBpcyBlbmNvZGVkIGluIHRoZSBjb2x1bW4gaGVhZGluZ3MuCgpGb3Igb2J2aW91cyByZWFzb25zIHRoaXMgZGF0YSBmb3JtYXQgaXMgb2Z0ZW4gcmVmZXJyZWQgdG8gYXMgX3dpZGUKZm9ybWF0Xy4KClRoZSB0aWR5LCBvciBfbG9uZ18sIGZvcm1hdCB3b3VsZCBoYXZlIHRocmVlIHZhcmlhYmxlczogYFllYXJgLApgTW9udGhgLCBhbmQgYFRlbXBgLgoKT25lIHdheSB0byBwdXQgdGhpcyBkYXRhIGZyYW1lIGluIHRpZHksIG9yIGxvbmcsIGZvcm1hdCB1c2VzIGBwaXZvdF9sb25nZXJgCmZyb20gdGhlIGB0aWR5cmAgcGFja2FnZToKCmBgYHtyfQpsaWJyYXJ5KHRpZHlyKQpsZ2FzdCA8LSBwaXZvdF9sb25nZXIoZ2FzdCwKICAgICAgICAgICAgICAgICAgICAgIC1ZZWFyLCAgIyMgc3BlY2lmaWVzIHRoZSBjb2x1bW5zIHRvIHVzZSAtLSBhbGwgYnV0IFllYXIKICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIk1vbnRoIiwKICAgICAgICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJUZW1wIikKYGBgCgpUaGUgZmlyc3QgZmV3IHJvd3Mgb2YgdGhlIHJlc3VsdDoKCmBgYHtyfQpoZWFkKGxnYXN0KQpgYGAKCkR1cmluZyBwbG90dGluZyBpdCBpcyBsaWtlbHkgdGhhdCB0aGUgYE1vbnRoYCB2YXJpYWJsZSB3aWxsIGJlCmNvbnZlcnRlZCB0byBhIF9mYWN0b3JfLgoKQnkgZGVmYXVsdCwgdGhpcyB3aWxsIG9yZGVyIGxldmVscyBhbHBoYWJldGljYWxseSwgd2hpY2ggaXMgbm90IHdoYXQgd2Ugd2FudDoKCmBgYHtyfQpsZXZlbHMoYXMuZmFjdG9yKGxnYXN0JE1vbnRoKSkKYGBgCgpXZSBjYW4gZ3VhcmQgYWdhaW5zdCB0aGlzIGJ5IGNvbnZlcnRpbmcgYE1vbnRoYCB0byBhIGZhY3RvciB3aXRoIHRoZQpyaWdodCBsZXZlbHMgbm93OgoKYGBge3J9CmxnYXN0IDwtIG11dGF0ZShsZ2FzdCwgTW9udGggPSBmYWN0b3IoTW9udGgsIGxldmVscyA9IG1vbnRoLmFiYikpCmxldmVscyhsZ2FzdCRNb250aCkKYGBgCgpXZSBjYW4gbm93IHVzZSB0aGlzIHRpZHkgdmVyc2lvbiBvZiB0aGUgZGF0YSB0byBjcmVhdGUgYSBzdGF0aWMKdmVyc2lvbiBvZiB0aGUgW0Jsb29tYmVyZyBob3R0ZXN0IHllYXIKdmlzdWFsaXphdGlvbl0oaHR0cHM6Ly93d3cuYmxvb21iZXJnLmNvbS9ncmFwaGljcy9ob3R0ZXN0LXllYXItb24tcmVjb3JkLykuCgpUaGUgYmFzaWMgZnJhbWV3b3JrIGlzIHNldCB1cCB3aXRoCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpwIDwtIGdncGxvdChsZ2FzdCkgKwogICAgZ2d0aXRsZSgiR2xvYmFsIEF2ZXJhZ2UgU3VyZmFjZSBUZW1wZXJhdHVyZXMiKSArCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKYGBgCgo8ZGl2IGNsYXNzID0gImFsZXJ0Ij4KYGdncGxvdGAgb2JqZWN0cyBvbmx5IHByb2R1Y2Ugb3V0cHV0IHdoZW4gdGhleSBhcmUgcHJpbnRlZC4KClRvIHNlZSB0aGUgcGxvdCBpbiBgcGAgd2UgbmVlZCB0byBwcmludCBpdCwgZm9yIGV4YW1wbGUgYnkgdXNpbmcgYQpsaW5lIGxpbmUgd2l0aCBvbmx5IGBwYC4KPC9kaXY+CgpgYGB7ciBnYXN0LWJhc2UsIGV2YWwgPSBGQUxTRX0KIHAKYGBgCmBgYHtyIGdhc3QtYmFzZSwgZXZhbCA9IFRSVUUsIGVjaG8gPSBGQUxTRX0KYGBgCgpUaGVuIGFkZCBhIGxheWVyIHdpdGggbGluZXMgZm9yIGVhY2ggeWVhciAoc3BlY2lmaWVkIGJ5IHRoZSBgZ3JvdXBgCmFyZ3VtZW50IHRvIGBnZW9tX2xpbmVgKS4KCmBgYHtyIGdhc3QtbGluZXMsIGV2YWwgPSBGQUxTRX0KcCArIGdlb21fbGluZShhZXMoeCA9IE1vbnRoLAogICAgICAgICAgICAgICAgICB5ID0gVGVtcCwKICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBZZWFyKSkKYGBgCmBgYHtyIGdhc3QtbGluZXMsIGV2YWwgPSBUUlVFLCBlY2hvID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KYGBgCgpXZSBjYW4gdXNlIGNvbG9yIHRvIGRpc3Rpbmdpc2ggdGhlIHllYXJzLgoKYGBge3IgZ2FzdC1jb2xvcjEsIGV2YWwgPSBGQUxTRX0KcDEgPC0gcCArCiAgICBnZW9tX2xpbmUoYWVzKHggPSBNb250aCwKICAgICAgICAgICAgICAgICAgeSA9IFRlbXAsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gWWVhciwKICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBZZWFyKSwKICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpCnAxCmBgYApTYXZpbmcgdGhlIHBsb3Qgc3BlY2lmaWNhdGlvbiBpbiB0aGUgdmFyaWFibGUgYHAxYCB3aWxsIG1ha2UgaXQgZWFzaWVyCnRvIGV4cGVyaW1lbnQgd2l0aCBjb2xvciB2YXJpYXRpb25zOgoKYGBge3IgZ2FzdC1jb2xvcjEsIGV2YWwgPSBUUlVFLCBlY2hvID0gRkFMU0V9CmBgYAoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0KIyMgQ29tcHV0ZSB0aGUgcGFzdCB5ZWFyIGFuZCBtYWtlIHN1cmUgaXQgaXMgaW4gdGhlIGZpbGUKbGlicmFyeShsdWJyaWRhdGUpCnBhc3RfeWVhciA8LSB5ZWFyKHRvZGF5KCkpIC0gMQpwYXN0X3llYXIKc3RvcGlmbm90KHBhc3RfeWVhciAlaW4lIGxnYXN0JFllYXIpCmBgYAoKT25lIHdheSB0byBoaWdobGlnaHQgdGhlIGBwYXN0X3llYXJgIGByIHBhc3RfeWVhcmA6CgpgYGB7ciBnYXN0LWNvbG9yMiwgZXZhbCA9IEZBTFNFfQpsZ2FzdF9sYXN0IDwtIGZpbHRlcihsZ2FzdCwgWWVhciA9PSBwYXN0X3llYXIpCnAxICsgZ2VvbV9saW5lKGFlcyh4ID0gTW9udGgsCiAgICAgICAgICAgICAgICAgICB5ID0gVGVtcCwKICAgICAgICAgICAgICAgICAgIGdyb3VwID0gWWVhciksCiAgICAgICAgICAgICAgIGxpbmV3aWR0aCA9IDEsCiAgICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsCiAgICAgICAgICAgICAgIGRhdGEgPSBsZ2FzdF9sYXN0LAogICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpCmBgYApgYGB7ciBnYXN0LWNvbG9yMiwgZWNobyA9IEZBTFNFfQpgYGAKCkEgdXNlZnVsIHdheSB0byBzaG93IG1vcmUgcmVjZW50IGRhdGEgaW4gdGhlIGNvbnRleHQgb2YgdGhlIGZ1bGwgZGF0YQppcyB0byBzaG93IHRoZSBmdWxsIGRhdGEgaW4gZ3JleSBhbmQgdGhlIG1vcmUgcmVjZW50IHllYXJzIGluIGJsYWNrOgoKYGBge3IgZ2FzdC1mdWxsLWdyZXksIGV2YWwgPSBGQUxTRX0KbGdhc3QyayA8LSBmaWx0ZXIobGdhc3QsIFllYXIgPj0gMjAwMCkKZ2dwbG90KGxnYXN0LCBhZXMoeCA9IE1vbnRoLAogICAgICAgICAgICAgICAgICB5ID0gVGVtcCwKICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBZZWFyKSkgKwogICAgZ2VvbV9saW5lKGNvbG9yID0gImdyZXk4MCIpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBnZW9tX2xpbmUoZGF0YSA9IGxnYXN0MmspCmBgYApgYGB7ciBnYXN0LWZ1bGwtZ3JleSwgZWNobyA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CmBgYAoKSWYgeW91IHdhbnQgdG8gdXBkYXRlIHlvdXIgcGxvdCBsYXRlciBpbiB0aGUgeWVhciB0aGVuIHRoZSBjdXJyZW50CnllYXIncyBlbnRyeSBtYXkgY29udGFpbiBtaXNzaW5nIHZhbHVlIGluZGljYXRvcnMgdGhhdCB5b3Ugd2lsbCBoYXZlCnRvIGRlYWwgd2l0aC4KPCEtLQpUaGUgdmVyc2lvbiBvZiB0aGUgZGF0YSBbb24gdGhlCndlYl0oaHR0cHM6Ly9kYXRhLmdpc3MubmFzYS5nb3YvZ2lzdGVtcC90YWJsZWRhdGFfdjMvR0xCLlRzK2RTU1QudHh0KQptYXkgaGF2ZSBiZWVuIHVwZGF0ZWQgdG8gaW5jbHVkZSB0aGUgbWlzc2luZyB2YWx1ZXMgZm9yIGByIHBhc3RfeWVhcmAuCklmIHlvdSB3YW50IHRvIHVwZGF0ZSB5b3VyIHBsb3QgbGF0ZXIgaW4gdGhlIHllYXIgeW91IHdpbGwgc2VlCnNpbWlsYXIgbWlzc2luZyB2YWx1ZSBtYXJrZXJzIGZvciB0aGUgcmVtYWluaW5nIG1vbnRocyBvZgpgciAocGFzdF95ZWFyICsgMSlgLgotLT4KClRoZSBOZXcgWW9yayBUaW1lcyBvbiBKYW51YXJ5IDE4LCAyMDE4LCBwdWJsaXNoZWQKW2Fub3RoZXIgdmlzdWFsaXphdGlvbl0oaHR0cHM6Ly93d3cubnl0aW1lcy5jb20vaW50ZXJhY3RpdmUvMjAxOC8wMS8xOC9jbGltYXRlL2hvdHRlc3QteWVhci0yMDE3Lmh0bWwpCm9mIHRoZXNlIGRhdGEgc2hvd2luZyBhdmVyYWdlIHllYXJseSB0ZW1wZXJhdHVyZXMgKFt2aWEgR29vZ2xlIG1heSB3b3JrIGJldHRlcl0oaHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS91cmw/c2E9dCZyY3Q9aiZxPSZlc3JjPXMmc291cmNlPXdlYiZjZD0mY2FkPXJqYSZ1YWN0PTgmdmVkPTJhaFVLRXdqXzRQbWhuTTMxQWhYRmpZa0VIZlF4QVRFUUZub0VDQVlRQVEmdXJsPWh0dHBzJTNBJTJGJTJGd3d3Lm55dGltZXMuY29tJTJGaW50ZXJhY3RpdmUlMkYyMDE4JTJGMDElMkYxOCUyRmNsaW1hdGUlMkZob3R0ZXN0LXllYXItMjAxNy5odG1sJnVzZz1BT3ZWYXczcW91eEhRQUpDcXJ0X0YzZmJMRUdFKSkuCgpUbyByZWNyZWF0ZSB0aGlzIHBsb3Qgd2UgZmlyc3QgbmVlZCB0byBjb21wdXRlIHllYXJseSBhdmVyYWdlIHRlbXBlcmF0dXJlcy4KClRoaXMgaXMgZWFzeSB0byBkbyB3aXRoIHRoZSBgc3VtbWFyaXplYCBhbmQgYGdyb3VwX2J5YCBmdW5jdGlvbnMgZnJvbSBgZHB5cmA6CgpgYGB7cn0KbGlicmFyeShkcGx5cikKYXRlbXAgPC0gbGdhc3QgJT4lCiAgICBncm91cF9ieShZZWFyKSAlPiUKICAgIHN1bW1hcml6ZShBdmVUZW1wID0gbWVhbihUZW1wLCBuYS5ybSA9IFRSVUUpKQpoZWFkKGF0ZW1wKQpgYGAKClVzaW5nIGBuYS5ybSA9IFRSVUVgIGVuc3VyZXMgdGhhdCB0aGUgbWVhbiBpcyBiYXNlZCBvbiB0aGUgYXZhaWxhYmxlCm1vbnRocyBpZiBkYXRhIGZvciBzb21lIG1vbnRocyBpcyBtaXNzaW5nLgoKQSBzaW1wbGUgdmVyc2lvbiBvZiB0aGUgcGxvdCBpcyB0aGVuIHByb2R1Y2VkIGJ5CgpgYGB7ciBnYXN0LW55dCwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoYXRlbXApICsKICAgIGdlb21fcG9pbnQoYWVzKHggPSBZZWFyLCB5ID0gQXZlVGVtcCkpCmBgYApgYGB7ciBnYXN0LW55dCwgZWNobyA9IEZBTFNFfQpgYGAKCkFub3RoZXIgdmFyaWF0aW9uIG9uIHRoZSBCbG9vbWJlcmcgcGxvdCBzaG93aW5nIGp1c3QgYSBmZXcgeWVhcnMgMjAKeWVhcnMgYXBhcnQ6CgpgYGB7ciBnYXN0LXNraXAsIGV2YWwgPSBGQUxTRX0KbGdfYnlfMjAgPC0KICAgIGZpbHRlcihsZ2FzdCwKICAgICAgICAgICBZZWFyICVpbiUgc2VxKDIwMjAsIGJ5ID0gLTIwLCBsZW4gPSA1KSkgJT4lCiAgICBtdXRhdGUoWWVhciA9IGZhY3RvcihZZWFyKSkKZ2dwbG90KGxnX2J5XzIwLCBhZXMoeCA9IE1vbnRoLAogICAgICAgICAgICAgICAgICAgICB5ID0gVGVtcCwKICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBZZWFyLAogICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFllYXIpKSArCiAgICBnZW9tX2xpbmUoKQpgYGAKQ29udmVydGluZyBgWWVhcmAgdG8gYSBmYWN0b3IgcmVzdWx0cyBpbiBhIGRpc2NyZXRlIGNvbG9yIHNjYWxlIGFuZCBsZWdlbmQuCmBgYHtyIGdhc3Qtc2tpcCwgZWNobyA9IEZBTFNFfQpgYGAKCgojIyBIYW5kbGluZyBNaXNzaW5nIFZhbHVlcwoKVGhlIGRhdGEgZm9yIDIwMTkgYXZhaWxhYmxlIGluIGVhcmx5IDIwMjAgaXMgYWxzbyBhdmFpbGFibGUKW2xvY2FsbHldKGh0dHBzOi8vc3RhdC51aW93YS5lZHUvfmx1a2UvZGF0YS9HTEIuVHMrZFNTVC0yMDE5LmNzdikuCgpgYGB7ciwgZWNobyA9IEZBTFNFfQppZiAoISBmaWxlLmV4aXN0cygiR0xCLlRzK2RTU1QtMjAxOS5jc3YiKSkKICAgIGRvd25sb2FkLmZpbGUoImh0dHBzOi8vc3RhdC51aW93YS5lZHUvfmx1a2UvZGF0YS9HTEIuVHMrZFNTVC0yMDE5LmNzdiIsCiAgICAgICAgICAgICAgICAgICJHTEIuVHMrZFNTVC0yMDE5LmNzdiIpCmBgYAoKQXNzdW1pbmcgdGhpcyBsb2NhbGx5IGF2YWlsYWJsZSBmaWxlIGhhcyBiZWVuIGRvd25sb2FkZWQsIHdlIGNhbiByZWFkCmluIHRoZSBkYXRhIGFuZCBkcm9wIHNvbWUgY29sdW1ucyB3ZSBkb24ndCBuZWVkIHdpdGgKCmBgYHtyfQpnYXN0MjAxOSA8LSByZWFkX2NzdigiR0xCLlRzK2RTU1QtMjAxOS5jc3YiLCBza2lwID0gMSlbMSA6IDEzXQpgYGAKClRoZSBsYXN0IHRocmVlIGNvbHVtbnMgYXJlIHJlYWQgYXMgY2hhcmFjdGVyIHZhcmlhYmxlczoKCmBgYHtyfQpoZWFkKGdhc3QyMDE5LCA1KQpgYGAKClRoZSByZWFzb24gaXMgdGhhdCBkYXRhIGZvciBPY3RvYmVyIHRocm91Z2ggRGVjZW1iZXIgd2VyZSBub3QKYXZhaWxhYmxlOgoKYGBge3J9CnRhaWwoZ2FzdDIwMTksIDIpCmBgYAoKV2Ugd2FudCB0byBjb252ZXJ0IHRoZXNlIGNvbHVtbnMgdG8gbnVtZXJpYywgd2l0aCBtaXNzaW5nIHZhbHVlcwpyZXByZXNlbnRlZCBhcyBgTkFgLgoKT25lIG9wdGlvbiBpcyB0byBoYW5kbGUgdGhlbSBpbmRpdmlkdWFsbHk6CgpgYGB7cn0KZ2FzdDIwMTkkT2N0IDwtIGFzLm51bWVyaWMoZ2FzdDIwMTkkT2N0KQp0YWlsKGdhc3QyMDE5LCAyKQpgYGAKCkFub3RoZXIgb3B0aW9uIGlzIHRvIGNvbnZlcnQgYWxsIGNoYXJhY3RlciBjb2x1bW5zIHRvIG51bWVyaWMgd2l0aAoKYGBge3J9Cmdhc3QyMDE5IDwtIG11dGF0ZShnYXN0MjAxOSwgYWNyb3NzKHdoZXJlKGlzLmNoYXJhY3RlciksIGFzLm51bWVyaWMpKQp0YWlsKGdhc3QyMDE5LCAyKQpgYGAKClRoZSB3YXJuaW5ncyBhcmUgYmVuaWduIGFuZCBjYW4gYmUgc3VwcHJlc3NlZCB3aXRoIHRoZSBgd2FybmluZyA9CkZBTFNFYCBjaHVuayBvcHRpb24uCgpTaW5jZSB3ZSBrbm93IHRoZSBtaXNzaW5nIHZhbHVlIHBhdHRlcm4gYCoqKmAgd2UgY2FuIGFsc28gYXZvaWQgdGhlCm5lZWQgdG8gZml4IHRoZSBkYXRhIGFmdGVyIHRoZSBmYWN0IGJ5IHNwZWNpZnlpbmcgdGhpcyBhdCByZWFkIHRpbWU6CgpgYGB7cn0KZ2FzdDIwMTkgPC0gcmVhZF9jc3YoIkdMQi5UcytkU1NULTIwMTkuY3N2IiwgbmEgPSAiKioqIiwgc2tpcCA9IDEpWzEgOiAxM10KdGFpbChnYXN0MjAxOSwgMikKYGBgCgpBIHBsb3QgaGlnaGxpZ2h0aW5nIHRoZSB5ZWFyIDIwMTkgc2hvd3Mgb25seSB0aGUgbW9udGhzIHdpdGggYXZhaWxhYmxlCmRhdGE6CgpgYGB7ciBnYXN0LTIwMTksIGV2YWwgPSBGQUxTRX0KbGdhc3QyMDE5IDwtIGdhc3QyMDE5ICU+JQogICAgcGl2b3RfbG9uZ2VyKC1ZZWFyLAogICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIk1vbnRoIiwKICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiVGVtcCIpICU+JQogICAgbXV0YXRlKE1vbnRoID0gZmFjdG9yKE1vbnRoLCBsZXZlbHMgPSBtb250aC5hYmIpKQpnZ3Bsb3QobGdhc3QyMDE5LCBhZXMoeCA9IE1vbnRoLAogICAgICAgICAgICAgICAgICAgICAgeSA9IFRlbXAsCiAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IFllYXIpKSArCiAgICBnZW9tX2xpbmUoY29sb3IgPSAiZ3JleTgwIiwKICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpICsKICAgIGdlb21fbGluZShkYXRhID0gZmlsdGVyKGxnYXN0MjAxOSwgWWVhciA9PSAyMDE5KSwKICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpCmBgYAoKQWRkaW5nIGBuYS5ybSA9IFRSVUVgIGluIHRoZSBgZ2VvbV9saW5lYCBjYWxscyBzdXBwcmVzc2VzIHdhcm5pbmdzOwp0aGUgcGxvdCB3b3VsZCBiZSB0aGUgc2FtZSB3aXRob3V0IHRoZXNlLgoKYGBge3IgZ2FzdC0yMDE5LCBlY2hvID0gRkFMU0V9CmBgYAoKPGRpdiBjbGFzcyA9ICJhbGVydCI+Ck91dGxpbmUgb2YgdGhlIHRvb2xzIHVzZWQ6CgoqIERhdGEgcHJvY2Vzc2luZzoKICAgICogUmVhZGluZzogYHJlYWRfY3N2YCwgYHJlYWQuY3N2YDsKICAgICogUmVzaGFwaW5nOiBgcGl2b3RfbG9uZ2VyYDsKICAgICogQ2xlYW5pbmc6IGBpcy5jaGFyYWN0ZXJgLCBgYXMubnVtZXJpY2AsIGBtdXRhdGVgLCBgYWNyb3NzYCwgYGZhY3RvcmA7CiAgICAqIFN1bW1hcml6aW5nOiBgZ3JvdXBfYnlgLCBgc3VtbWFyaXplYC4KKiBWaXN1YWxpemF0aW9uIGdlb21ldHJpZXM6CiAgICAqIGBnZW9tX2xpbmVgIGZvciBhIGxpbmUgcGxvdDsKICAgICogYGdlb21fcG9pbnRgIGZvciBhIHNjYXR0ZXIgcGxvdC4KPC9kaXY+CgoKIyMgUmVhZGluZwoKU3RldmVucycgY2xhc3NpZmljYXRpb24gb2Ygc2NhbGVzIG9mIG1lYXN1cmVtZW50IGlzIGRlc2NyaWJlZCBpbiBhCltXaWtpcGVkaWEKYXJ0aWNsZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTGV2ZWxfb2ZfbWVhc3VyZW1lbnQpLgoKQSBnb29kIGludHJvZHVjdGlvbiB0byB0aGUgY29uY2VwdCBvZiBfdGlkeSBkYXRhXyBpcyBwcm92aWRlZCBpbiBhCltjaGFwdGVyIGluIF9SIGZvciBEYXRhClNjaWVuY2VfXShodHRwczovL3I0ZHMuaGFkLmNvLm56L3RpZHktZGF0YS5odG1sKS4KCgojIyBJbnRlcmFjdGl2ZSBUdXRvcmlhbAoKQW4gaW50ZXJhY3RpdmUgW2BsZWFybnJgXShodHRwczovL3JzdHVkaW8uZ2l0aHViLmlvL2xlYXJuci8pIHR1dG9yaWFsCmZvciB0aGVzZSBub3RlcyBpcyBbYXZhaWxhYmxlXShgciBXTE5LKCJ0dXRvcmlhbHMvZGF0YWZybS5SbWQiKWApLgoKWW91IGNhbiBydW4gdGhlIHR1dG9yaWFsIHdpdGgKCmBgYHtyLCBldmFsID0gRkFMU0V9ClNUQVQ0NTgwOjpydW5UdXRvcmlhbCgiZGF0YWZybSIpCmBgYAoKCiMjIEV4ZXJjaXNlcwoKMS4gV2hpY2ggb2YgdGhlIFN0ZXZlbnMgY2xhc3NpZmljYXRpb25zIChub21pbmFsLCBvcmRpbmFsLCBpbnRlcnZhbCwgcmF0aW8pCiAgIGJlc3QgY2hhcmFjdGVyaXplcyB0aGVzZSB2YXJpYWJsZXM6CgogICAgYS4gRGFpbHkgbWF4aW1hbCB0ZW1wZXJhdHVyZXMgaW4gSW93YSBDaXR5LgogICAgYi4gUG9wdWxhdGlvbiBjb3VudHMgZm9yIElvd2EgY291bnRpZXMuCiAgICBjLiBFZHVjYXRpb24gbGV2ZWwgb2Ygam9iIGFwcGxpY2FudHMgdXNpbmcgdGhlIFtCdXJlYXUgb2YgTGFib3IKICAgICAgIFN0YXRpc3RpY3MKICAgICAgIGNsYXNzaWZpY2F0aW9uXShodHRwczovL3d3dy5ibHMuZ292L2NhcmVlcm91dGxvb2svMjAxNC9hcnRpY2xlL2VkdWNhdGlvbi1sZXZlbC1hbmQtam9icy5odG0pLgogICAgZC4gTWFqb3Igb2YgVUkgc3R1ZGVudHMuCgo8IS0tCldoaWNoIGFuc3dlcnMgYXJlIGNvcnJlY3QgZm9yIGV4ZXJjaXNlIDE6CiogYTogaW50ZXJ2YWw7IGI6IHJhdGlvOyBjOiBvcmRpbmFsOyBkOiBub21pbmFsCiAgYTogbm9taW5hbDsgYjogaW50ZXJ2YWw7IGM6IG9yZGluYWw7IGQ6IHJhdGlvCiAgYTogbm9taW5hbDsgYjogaW50ZXJ2YWw7IGM6IG9yZGluYWw7IGQ6IHJhdGlvCiAgYTogaW50ZXJ2YWw7IGI6IHJhdGlvOyBjOiBub21pbmFsOyBkOiBvcmRpbmFsCmZtdCA8LSBmdW5jdGlvbih4KQogICAgcGFzdGUobGV0dGVyc1tzZXFfYWxvbmcoeCldLCB4LCBzZXAgPSAiOiAiLCBjb2xsYXBzZSA9ICI7ICIpCnN0ZXYgPC0gYygibm9taW5hbCIsICJvcmRpbmFsIiwgImludGVydmFsIiwgInJhdGlvIikKZm10KHNhbXBsZShzdGV2LCA0KSkKLS0+CgoyLiBXaGljaCBvZiB0aGVzZSBkYXRhIHNldHMgYXJlIGluIHRpZHkgZm9ybT8KCiAgICBhLiBUaGUgYnVpbHRpbiBkYXRhIHNldCBgY28yYAogICAgYi4gVGhlIGJ1aWx0aW4gZGF0YSBzZXQgYEJPRGAKICAgIGMuIFRoZSBgd2hvYCBkYXRhIHNldCBpbiBwYWNrYWdlIGB0aWR5cmAgKGB0aWR5cjo6d2hvYCkKICAgIGQuIFRoZSBgbXBnYCBkYXRhIHNldCBpbiBwYWNrYWdlIGBnZ3Bsb3QyYCAoYGdncGxvdDI6Om1wZ2ApCgo8IS0tCldoaWNoIGFuc3dlcnMgYXJlIGNvcnJlY3QgZm9yIGV4ZXJjaXNlIDI6CiogYTogbm90IHRpZHk7IGI6IHRpZHk7IGM6IG5vdCB0aWR5OyBkOiB0aWR5CiogYTogdGlkeTsgYjogdGlkeTsgYzogbm90IHRpZHk7IGQ6IHRpZHkKKiBhOiBub3QgdGlkeTsgYjogbm90IHRpZHk7IGM6IG5vdCB0aWR5OyBkOiB0aWR5CiogYTogbm90IHRpZHk7IGI6IHRpZHk7IGM6IHRpZHk7IGQ6IG5vdCB0aWR5Ci0tPgoKVGhlIG5leHQgZXhlcmNpc2VzIHVzZSB0aGUgZGF0YSBpbiB0aGUgdmFyaWFibGUgYGdhcG1pbmRlcmAgaW4gdGhlIHBhY2thZ2UKYGdhcG1pbmRlcmAuIFlvdSBjYW4gbWFrZSBpdCBhdmFpbGFibGUgd2l0aAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZGF0YShnYXBtaW5kZXIsIHBhY2thZ2UgPSAiZ2FwbWluZGVyIikKYGBgCjMuIFVzZSB0aGUgZnVuY3Rpb24gYHN0cmAgdG8gZXhhbWluZSB0aGUgdmFsdWUgb2YgdGhlIGdhcG1pbmRlcgogICB2YXJpYWJsZS4gIEhvdyBtYW55IGNhc2VzIGFyZSB0aGVyZSBpbiB0aGUgZGF0YSBzZXQ/IEhvdyBtYW55IG9mCiAgIHRoZSB2YXJpYWJsZXMgYXJlIGZhY3RvcnM/Cgo0LiBVc2UgdGhlIGZ1bmN0aW9ucyBgY2xhc3NgIGFuZCBgbmFtZXNgIHRvIGZpbmQgdGhlIGNsYXNzIGFuZAogICB2YXJpYWJsZSBuYW1lcyBpbiB0aGUgYGdhcG1pbmRlcmAgZGF0YS4KCjUuIFVzZSBgc3VtbWFyeWAgdG8gY29tcHV0ZSBzdW1tYXJ5IGluZm9ybWF0aW9uIGZvciB0aGUgdmFyaWFibGVzLgoKNi4gRmlsbCBpbiB0aGUgdmFsdWVzIGZvciBgLS0tYCBuZWVkZWQgdG8gcHJvZHVjZSBwbG90cyBvZiBsaWZlCiAgIGV4cGVjdGFuY3kgYWdhaW5zdCB5ZWFyIGZvciB0aGUgY291bnRyaWVzIGluIGNvbnRpbmVudCBPY2VhbmlhLgo8IS0tICMjIG5vbGludCBzdGFydCAtLT4KYGBge3IsIGV2YWwgPSBGQUxTRX0KbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpkYXRhKGdhcG1pbmRlciwgcGFja2FnZSA9ICJnYXBtaW5kZXIiKQpnZ3Bsb3QoZmlsdGVyKGdhcG1pbmRlciwgY29udGluZW50ID09ICJPY2VhbmlhIiksCiAgICAgICBhZXMoeCA9IC0tLSwgeSA9IC0tLSwgY29sb3IgPSBjb3VudHJ5KSkgKwogICAgZ2VvbV9saW5lKCkKYGBgCjwhLS0gIyMgbm9saW50IGVuZCAtLT4K