Associated Material
Zoom notes: Zoom Notes 04 -
Summarising data
Readings
Introduction
We do scientific research to test hypotheses, answer questions, or
just learn something about the world. After the often labourious process
of data collection, we may have hundreds (or even thousands) of data
points, but we haven’t actually learned anything. To squeeze the
knowledge out of our raw data, we must use statistics.
The formal topic of statistics is large and complex, and we do not
attempt to teach it here (there are papers for that, and we recommend
you take as many of them as possible). We concentrate on how to use R to
perform common statistical analyses. R is especially useful for such
tasks because of its extensive set of statistical libraries and
efficient data handling facilities.
There are two general types of statistical analyses – descriptive
statistics, which allow us to summarise and describe our raw data, and
inferential statistics, which allow us to generalise our results beyond
our observed data. We will only cover descriptive statistics in
R4SSP.
For this module, we will use two data sets – the “Palmers Penguins”
data describing penguin populations in the Palmer Archipelago
(Antarctica), and a data set containing Chlorophyll A (ChlA) readings
from three New Zealand lakes (data provided by the local Regional
Councils). ChlA levels are an indicator of phytoplankton biomass, and
provide a general measure of lake health – more ChlA indicates poorer
health. The “toxic algal blooms” that occur occasionally in New Zealand
lakes are accompanied by a dramatic spike in measured ChlA.
Downloading the ChlA NZ Lakes data:
One option for downloading the data is to use
download.file()
in RStudio.
download.file(url = "https://raw.githubusercontent.com/rtis-training/2023-s2-r4ssp/main/docs/data/NZ_lake_chla_data.csv",
destfile = "data/NZ_lake_chla_data.csv")
Remember to adjust the value of destfile
to match
your project directory structure.
The second option is to download the file from the R4SSP shared
Google Drive folder at https://drive.google.com/drive/folders/1UBp-P4wFAaQL3egIQ7dGa2RQe6RQKqgy
Loading the data
#===============================================
# The Penguin Data:
#===============================================
# Install palmerpenguins once on any computer
# install.packages("palmerspenguins")
# After loading the library, a tibble called 'penguins' will be initialised
library(palmerpenguins)
# Check the structure
str(penguins)
#> tibble [344 × 8] (S3: tbl_df/tbl/data.frame)
#> $ species : Factor w/ 3 levels "Adelie","Chinstrap",..: 1 1 1 1 1 1 1 1 1 1 ...
#> $ island : Factor w/ 3 levels "Biscoe","Dream",..: 3 3 3 3 3 3 3 3 3 3 ...
#> $ bill_length_mm : num [1:344] 39.1 39.5 40.3 NA 36.7 39.3 38.9 39.2 34.1 42 ...
#> $ bill_depth_mm : num [1:344] 18.7 17.4 18 NA 19.3 20.6 17.8 19.6 18.1 20.2 ...
#> $ flipper_length_mm: int [1:344] 181 186 195 NA 193 190 181 195 193 190 ...
#> $ body_mass_g : int [1:344] 3750 3800 3250 NA 3450 3650 3625 4675 3475 4250 ...
#> $ sex : Factor w/ 2 levels "female","male": 2 1 1 NA 1 2 1 2 NA NA ...
#> $ year : int [1:344] 2007 2007 2007 2007 2007 2007 2007 2007 2007 2007 ...
#===============================================
# The Lake Data:
#===============================================
# Read in the lakes data file
lakes_df <- read.csv("data/NZ_lake_chla_data.csv")
# Check the structure
str(lakes_df)
#> 'data.frame': 408 obs. of 4 variables:
#> $ LakeName: chr "Lake Ellesmere" "Lake Ellesmere" "Lake Ellesmere" "Lake Ellesmere" ...
#> $ Year : int 2004 2004 2004 2004 2004 2004 2004 2004 2004 2004 ...
#> $ Month : int 1 2 3 4 5 6 7 8 9 10 ...
#> $ ChlA : num 66.9 79.9 95 82.4 59.6 69 62.1 96.3 135 102 ...
In the lakes data, the LakeName, Month, and Year columns are all
categorical grouping variables. Many data analysis methods in R require
that categorical variables be of type factor
(as in
“experimental factor”). Note however, that these columns have been
imported into R as types chr
(strings) and int
(integers). We should cast these columns to type factor
to
insure that our subsequent analyses are correct. This cast does not
affect the values in the columns, it simply signals to R that they are
group identifiers, not raw string or number data values.
lakes_df$LakeName <- as.factor(lakes_df$LakeName)
lakes_df$Year <- as.factor(lakes_df$Year)
lakes_df$Month <- as.factor(lakes_df$Month)
# Confirm that the column data types have been updated
str(lakes_df)
#> 'data.frame': 408 obs. of 4 variables:
#> $ LakeName: Factor w/ 3 levels "Lake Ellesmere",..: 1 1 1 1 1 1 1 1 1 1 ...
#> $ Year : Factor w/ 13 levels "2001","2002",..: 4 4 4 4 4 4 4 4 4 4 ...
#> $ Month : Factor w/ 12 levels "1","2","3","4",..: 1 2 3 4 5 6 7 8 9 10 ...
#> $ ChlA : num 66.9 79.9 95 82.4 59.6 69 62.1 96.3 135 102 ...
Visualise the data (revision)
When faced with a new data set, my first step is invariably to start
making graphs. These “pictures” of your data provide an easy way to see
large-scale patterns that will help guide your further analysis. They
also help you to catch any problems in your data (see the skewness
exercise in the Zoom Notes for this module) that must be addressed
before proceeding to more complex analyses.
An excellent first graph for continuous (i.e. not categorical) data
is the frequency distribution, or
histogram, which has data value on the x-axis and
frequency (i.e. count or proportion) on the y-axis. This shows you, in a
single picture, how your data are distributed. We met the histogram in
Module 02.
The penguins
data set contains values for 344 different
penguins. We can begin by looking at how the penguins’ body weights are
distributed.
Histogram with base R
# The 'breaks' argument controls the number of bars drawn
hist(penguins$body_mass_g,
breaks = 100,
main="Distribution of Penguin Body Mass", xlab = "Body mass (g)", ylab = "Frequency")
Histogram with ggplot
# Load the library before your first call to ggplot
library(ggplot2)
# The values provided to 'colour' and 'fill' are hexidecimal colour codes. Note the
# hash mark prefix. It is required.
ggplot(data = penguins, mapping = aes(x = body_mass_g)) +
geom_histogram(colour = "#7133ff", fill="#bbbbff") +
labs(title = "Distribution of Penguin Body Mass",
x = "Body Mass (g)",
y = "Frequency") + theme_bw()
Illustrating Groups
Using ggplot, we can illustrate group effects in histograms by
defining a mapping from a grouping (i.e. categorical) variable to the
fill
property of function geom_histogram
. For
example, the code below will make histograms of all the ChlA values in
data frame lakes_df
, with each lake in a different
colour
By default, geom_histogram produces a stacked plot – that is, the
different groups are shown stacked up in a single bar, separated by
their colour. To make the plot with side-by-side bars, set
geom_histogram’s position
argument to dodge
.
Note that this is not a mapping, it is simply an argument to function
geom_histogram. What does this simple graph tell you about the health of
these three lakes?
For continuous data
# Stacked grouped histogram
ggplot(data = lakes_df, mapping = aes(x = ChlA)) +
geom_histogram(aes(fill=LakeName), colour = 'black')
# Side-by-side grouped histogram
ggplot(data = lakes_df, mapping = aes(x = ChlA)) +
geom_histogram(aes(fill=LakeName), colour = 'black', position="dodge")
For categorical data
The functions hist
and geom_histogram
are
appropriate for continuous (numerical) data. For
categorical variables (e.g. Species and Island in the
penguin data set) one often uses the more general geom_bar
function (geom_histogram is a special case of geom_bar). The example
code below shows how to generate a bar graph in ggplot, modifying the
default ggplot colour palette to something more accessible to viewers
with atypical colour vision:
# "Colour-blind friendly" palette from #https://personal.sron.nl/~pault/
# These are hexadecimal colour codes. The # is required.
customPalette <- c("#DDAA33", "#BB5566", "#004488")
# Generate a stacked bar plot, and use our custom colour palette
ggplot(data = penguins, mapping = aes(x = island, fill=species)) +
geom_bar() +
scale_fill_manual(values = customPalette)
As with geom_histogram above, we set the position argument of
geom_bar to change from stacked to side-by-side format.
Even a simple graph like this helps you to get to know your data.
Just by inspection we see that Biscoe island has the largest population,
Torgersen has only Adelie penguins, Dream Island has nearly equal
numbers of Chinstrap and Gentoo, etc. When first approaching a big data
set, always think about starting with some graphs.
Measures of Central Tendency
Look at the graph you made earlier showing the distributions of ChlA
for the three lakes. You might describe the Lake Ellesmere ChlA readings
as “mostly between 50 and 100” and the Lake Rotorua readings as “mostly
around 10”. Statements like this are attempts to describe a
typical score from a large data set. They allow us to
capture the fact that, for example, overall, Lake Ellesmere has
higher ChlA readings than Lake Rotorua. It is not the case that
every Ellesmere reading is higher than every Rotorua
reading, but typically this is the case.
In statistics, a precise measure of such typicality is called a
Measure of Central Tendency (MCT). The most common MCTs
are the mean, the median and the
mode. These are, respectively, the mathematical
average, the middle score, and the most frequent score (or scores) in a
data set. There are some subtle statistical issues around which of the
three MCT is appropriate for any given data analysis situation (ask your
lecturer for details), but they are all easy to compute in R (we have,
in fact, already met function mean
in earlier modules), and
we show example code below for computing these descriptive statistics on
a single column of data from the penguins data set.
Note that the penguins data has some missing some values (cf. Module
03 - Subsetting), The functions for mean and median will not work if the
input data have any NA
(missing) values. The most common
solution is to omit those scores from the computation by setting the
na.rm
argument to TRUE
as shown:
Mean
# We have seen this code before... We pass the column of interest
# to function mean
mean(penguins$body_mass_g, na.rm=TRUE)
#> [1] 4201.754
Mode
Base R has no built-in function for mode. After Module 08 you will be
able to write your own Mode function. Or you can use one of several
available in auxiliary libraries. The DescTools library is a good
one.
# Install the package once on each machine
# install.packages("DescTools")
# Load the library once each session
library(DescTools)
#Call the function
Mode(penguins$body_mass_g, na.rm=TRUE)
#> [1] 3800
#> attr(,"freq")
#> [1] 12
Note that DescTools::Mode returns the modal (i.e. most common value)
with an attached attribute called “freq” equal to the
number of occurrences.
Using function summary
When you have a very large number of data measures, you may wish to
compute MCTs for individual columns as shown above. An efficient
alternative for smaller data sets is to use function
summary
, which accepts a data frame and summarises
all its columns at once. Function summary
computes
frequencies for categorical variables, and measures of central tendency
for continuous variables. It also reports the numbers of NA values in
each column. Function summary
provides some additional
measures (minimum, 1st quartile, 3rd quartile, and maximum) that we will
discuss in more detail later.
summary(penguins)
#> species island bill_length_mm bill_depth_mm
#> Adelie :152 Biscoe :168 Min. :32.10 Min. :13.10
#> Chinstrap: 68 Dream :124 1st Qu.:39.23 1st Qu.:15.60
#> Gentoo :124 Torgersen: 52 Median :44.45 Median :17.30
#> Mean :43.92 Mean :17.15
#> 3rd Qu.:48.50 3rd Qu.:18.70
#> Max. :59.60 Max. :21.50
#> NA's :2 NA's :2
#> flipper_length_mm body_mass_g sex year
#> Min. :172.0 Min. :2700 female:165 Min. :2007
#> 1st Qu.:190.0 1st Qu.:3550 male :168 1st Qu.:2007
#> Median :197.0 Median :4050 NA's : 11 Median :2008
#> Mean :200.9 Mean :4202 Mean :2008
#> 3rd Qu.:213.0 3rd Qu.:4750 3rd Qu.:2009
#> Max. :231.0 Max. :6300 Max. :2009
#> NA's :2 NA's :2
Exercise
The results for column year
may not be what you
expected. Function summary
has computed an average
value for year
. Does this seem like the appropriate
analysis? (Answer => No.) Modify penguins
to make
summary
treat the year
data correctly, and
rerun summary
.
Measures of Variability
In the histograms for ChlA from each of three New Zealand lakes, the
three groups of scores did not overlap completely, indicating that the
typical values – the central tendencies – were different for the three
lakes. We can confirm this observation by comparing the means. We can
use function aggregate
in base R, or group_by
and summarise
, from library dplyr.
Using aggregate
# Using aggregate. compute the group means
aggregate(lakes_df$ChlA, by = list(Lake = lakes_df$LakeName), FUN=mean)
#> Lake x
#> 1 Lake Ellesmere 80.566667
#> 2 Lake Rotorua 18.419015
#> 3 Lake Taupo 1.030606
Using group_by
and summarise
# Using `group_by` and `summarise`
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
lakes_df %>%
group_by(LakeName) %>%
summarise(MeanChlA = mean(ChlA))
#> # A tibble: 3 × 2
#> LakeName MeanChlA
#> <fct> <dbl>
#> 1 Lake Ellesmere 80.6
#> 2 Lake Rotorua 18.4
#> 3 Lake Taupo 1.03
However, not only do the central points of the three lakes’
distributions differ, so do the amounts of “spread”. Lake Taupo’s
distribution is very narrow; all its readings are similar. Lake
Ellesmere is squashed and spread out; its readings vary a lot. Lake
Rotorua is intermediate. To illustrate this more clearly, we can use
ggplot to make separate graphs for each lake, using function
facet_grid
.
Note the scales
argument to facet_grid
.
This allows each graph to adjust its y-axis to its data domain, which
makes the comparison visually easier; this should be mentioned in the
discussion of the figure in a manuscript.
Using facet_grid
ggplot(data = lakes_df) +
geom_histogram(aes(x = ChlA, fill=LakeName), color="black") +
facet_grid(rows = vars(LakeName), scales="free_y")
Statistically, the “spread out” quality of a distribution reflects
its variability.
We can capture variability more precisely with measures of the
range of the data set. These are typically the smallest
and largest scores (minimum and maximum) and the scores at the 25th and
75th percentiles (also called 1st quartile and
3rd quartile). Earlier, we saw that function
summary
computes these measures of range. However, if we
simply pass the entire lakes_df
data frame to function
summary
, it will combine the data from all three lakes – to
compare the lakes we need the values from each lake separately.
In earlier modules we have seen two techniques for selecting out just
the rows from one lake (using [] or using filter
). To run
function summary
on each lake separately, we could select
the subset for each lake in turn, and pass each subset to
summary
. However, we can achieve the same result more
parsimoniously by using function aggregate
. Above we used
aggregate with FUN = mean
to get the mean ChlA for each
lake. We can use FUN = summary
to call function
summary
separately for the records of each lake.
# Apply function summary by group
aggregate(lakes_df$ChlA, by = list(Lake = lakes_df$LakeName), FUN=summary)
#> Lake x.Min. x.1st Qu. x.Median x.Mean x.3rd Qu.
#> 1 Lake Ellesmere 1.300000 44.000000 67.950000 80.566667 97.087500
#> 2 Lake Rotorua 2.500000 10.825000 15.950000 18.419015 23.625000
#> 3 Lake Taupo 0.200000 0.600000 0.900000 1.030606 1.400000
#> x.Max.
#> 1 521.300000
#> 2 77.100000
#> 3 2.900000
We can also measure the variablity in a data set with the
standard deviation. The standard deviation is the most
commonly used measure of variability, and it plays an important
mathematical role in inferential statistics (ask your stats lecturer for
details – it’s very interesting). Conceptually, the standard deviation
is almost equal to the average distance from the mean across
all the values in a data set – it doesn’t equal exactly that
value, because of how it is computed, but it is close, and it can be
helpful to think of it with this approximation. Big standard deviation
shows that scores are spread far from their mean; small standard
deviation shows that scores tend to huddle close to their mean. Compute
standard deviation with function sd
.
Using aggregate
# Using aggregate. compute the group sds
aggregate(lakes_df$ChlA, by = list(Lake = lakes_df$LakeName), FUN=sd)
#> Lake x
#> 1 Lake Ellesmere 63.5217194
#> 2 Lake Rotorua 11.6637583
#> 3 Lake Taupo 0.5627233
Using group_by
and summarise
lakes_df %>%
group_by(LakeName) %>%
summarise(StdDev = sd(ChlA))
#> # A tibble: 3 × 2
#> LakeName StdDev
#> <fct> <dbl>
#> 1 Lake Ellesmere 63.5
#> 2 Lake Rotorua 11.7
#> 3 Lake Taupo 0.563
The histograms, the measures of range, and the standard deviations
all indicate that Taupo has very stable ChlA measures, Rotorua is a
little noisier, and Ellesmere is all over the place. This phytoplankton
biomass stability is an important indicator of lake health – a stable
lake is at much lower risk of a toxic algal bloom.
Efficient code for descriptive statistics
The function describeBy
in package psych
will compute all the descriptive summaries we have seen (and a few more)
in one statement. When you are exploring a single data column and a
single grouping column (so the output doesn’t get too large), this is a
very useful function.
# Install once on any computer
#install.packages("psych")
# Call once each R session
library(psych)
#>
#> Attaching package: 'psych'
#> The following objects are masked from 'package:DescTools':
#>
#> AUC, ICC, SD
#> The following objects are masked from 'package:ggplot2':
#>
#> %+%, alpha
# Pass in the data column and the grouping column
describeBy(lakes_df$ChlA, lakes_df$LakeName)
#>
#> Descriptive statistics by group
#> group: Lake Ellesmere
#> vars n mean sd median trimmed mad min max range skew kurtosis se
#> X1 1 120 80.57 63.52 67.95 72.45 40.7 1.3 521.3 520 3.12 17.5 5.8
#> ------------------------------------------------------------
#> group: Lake Rotorua
#> vars n mean sd median trimmed mad min max range skew kurtosis se
#> X1 1 132 18.42 11.66 15.95 16.97 9.12 2.5 77.1 74.6 1.71 4.75 1.02
#> ------------------------------------------------------------
#> group: Lake Taupo
#> vars n mean sd median trimmed mad min max range skew kurtosis se
#> X1 1 156 1.03 0.56 0.9 0.97 0.56 0.2 2.9 2.7 0.97 0.5 0.05
Package psych
contains many other interesting
statistical tools, especially for multivariate data sets commonly found
in psychological and ecological research. Ask Google or your lecturer
for details.
Exploring the relationship between two variables
The preceding descriptive statistics all looked at data measures –
ChlA, bill length, body weight, etc. – individually, summarising their
distribution, central tendency and variability. Often, however, we are
interested in describing the relationship between data
measures. For example, we might want to know if heavier
penguins also tend to have longer bills. This type of relationship is
called a correlation. When we have more than one
measure for each experimental participant (or each penguin, or each
lake) we can explore correlations between pairs of measures graphically
with a scatterplot. A scatterplot has one measure on
each axis, and one point for each participant’s pair of scores.
In the code example below we make a scatterplot with ggplot
(cf. Module 02), and show how to add a linear trend
line. Conceptually, this is the line that runs through the
center of the scatterplot points, and it helps us to see the direction
of the relationship. Mathematically, trend lines are actually very
complicated things, and we generate them with the powerful function
lm
,(for linear model). You will learn
about the many fascinating things you can do with linear modeling if you
take advanced statistics papers.
Scatterplot with linear trend line
# geom_point plots the points of the scatterplot
# geom_smooth plots the linear trend line computed with function lm
# The se argument determines whether error bars are shown
# around the trend line.
ggplot(data = penguins, mapping = aes(x = body_mass_g, y = bill_length_mm)) +
geom_point() +
geom_smooth(method = "lm", se=FALSE)
The scatterplot gives us a very clear picture: those penguins with
higher body weights tend to also have longer bills, and this is
reflected in the positive slope of the trendline. Note however that this
is not an absolute rule. Is it easy to find pairs of points such that
the lighter of two penguins has the longer bill. This is typical of
correlational data.
There are various statistical measures that capture the strength of a
correlation (i.e. how close it is to being an absolute rule). For
continuous, numerical data (such as bill length and body weight) use the
R function cor
to compute the numerical correlation value.
As with means and medians, we must tell cor
how to cope
with missing data (NA scores). Unfortunately the syntax is not
consistent across the functions. For function mean
we set
argument na.rm = TRUE
. For function cor
we
must set argument use = "complete.obs"
, meaning that we
want the function to use only those rows that are complete (i.e. have
both values). These idiosyncracies occur from time to time in R; you
just have to learn them.
Correlation coefficient
# Pass the two data columns into function cor
cor(penguins$bill_length_mm, penguins$body_mass_g, use="complete.obs")
#> [1] 0.5951098
Function cor
returns a value between -1 and 1.
Correlations that trend downward (i.e. if one score is high, the other
tends to be low) will have a negative correlation value. Correlations
that trend upward (i.e. if one score is high the other also tends to be
high) will have a positive correlation value. The closer the absolute
value of cor
is to 1, the stronger the correlation (you
know who to talk to for more detail, don’t you?).
For example, consider these two scatterplots:
Exercise
For one of the scatterplots above, the computed correlation score is
0.60. For the other, it is 0.87. First, predict which is which. Second,
write the necessary R code to confirm your prediction.
LS0tCnRpdGxlOiAiU3VtbWFyaXNpbmciCmRhdGU6ICJTZW1lc3RlciAyLCAyMDIzIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KGtuaXRyKQoKa25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGNvbW1lbnQgPSAiIz4iLAogIGZpZy5wYXRoID0gImZpZ3VyZXMvMDQvIiwgIyB1c2Ugb25seSBmb3Igc2luZ2xlIFJtZCBmaWxlcwogIGNvbGxhcHNlID0gVFJVRSwKICBlY2hvID0gVFJVRQopCgoKYGBgCgo+ICMjIyMgQXNzb2NpYXRlZCBNYXRlcmlhbAo+Cj4gWm9vbSBub3RlczogW1pvb20gTm90ZXMgMDQgLSBTdW1tYXJpc2luZyBkYXRhXSh6b29tX25vdGVzXzA0X3N1bW1hcmlzZS5odG1sKQo+Cj4gUmVhZGluZ3MKPgo+IC0gW1IgZm9yIERhdGEgU2NpZW5jZSAtIENoYXB0ZXIgN10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9leHBsb3JhdG9yeS1kYXRhLWFuYWx5c2lzLmh0bWwpCgpcCgojIEludHJvZHVjdGlvbgoKV2UgZG8gc2NpZW50aWZpYyByZXNlYXJjaCB0byB0ZXN0IGh5cG90aGVzZXMsIGFuc3dlciBxdWVzdGlvbnMsIG9yIGp1c3QgbGVhcm4gc29tZXRoaW5nIGFib3V0IHRoZSB3b3JsZC4gQWZ0ZXIgdGhlIG9mdGVuIGxhYm91cmlvdXMgcHJvY2VzcyBvZiBkYXRhIGNvbGxlY3Rpb24sIHdlIG1heSBoYXZlIGh1bmRyZWRzIChvciBldmVuIHRob3VzYW5kcykgb2YgZGF0YSBwb2ludHMsIGJ1dCB3ZSBoYXZlbid0IGFjdHVhbGx5IGxlYXJuZWQgYW55dGhpbmcuIFRvIHNxdWVlemUgdGhlIGtub3dsZWRnZSBvdXQgb2Ygb3VyIHJhdyBkYXRhLCB3ZSBtdXN0IHVzZSBzdGF0aXN0aWNzLgoKVGhlIGZvcm1hbCB0b3BpYyBvZiBzdGF0aXN0aWNzIGlzIGxhcmdlIGFuZCBjb21wbGV4LCBhbmQgd2UgZG8gbm90IGF0dGVtcHQgdG8gdGVhY2ggaXQgaGVyZSAodGhlcmUgYXJlIHBhcGVycyBmb3IgdGhhdCwgYW5kIHdlIHJlY29tbWVuZCB5b3UgdGFrZSBhcyBtYW55IG9mIHRoZW0gYXMgcG9zc2libGUpLiBXZSBjb25jZW50cmF0ZSBvbiBob3cgdG8gdXNlIFIgdG8gcGVyZm9ybSBjb21tb24gc3RhdGlzdGljYWwgYW5hbHlzZXMuIFIgaXMgZXNwZWNpYWxseSB1c2VmdWwgZm9yIHN1Y2ggdGFza3MgYmVjYXVzZSBvZiBpdHMgZXh0ZW5zaXZlIHNldCBvZiBzdGF0aXN0aWNhbCBsaWJyYXJpZXMgYW5kIGVmZmljaWVudCBkYXRhIGhhbmRsaW5nIGZhY2lsaXRpZXMuIAoKVGhlcmUgYXJlIHR3byBnZW5lcmFsIHR5cGVzIG9mIHN0YXRpc3RpY2FsIGFuYWx5c2VzIC0tIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MsIHdoaWNoIGFsbG93IHVzIHRvIHN1bW1hcmlzZSBhbmQgZGVzY3JpYmUgb3VyIHJhdyBkYXRhLCBhbmQgaW5mZXJlbnRpYWwgc3RhdGlzdGljcywgd2hpY2ggYWxsb3cgdXMgdG8gZ2VuZXJhbGlzZSBvdXIgcmVzdWx0cyBiZXlvbmQgb3VyIG9ic2VydmVkIGRhdGEuIFdlIHdpbGwgb25seSBjb3ZlciBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGluIFI0U1NQLgoKRm9yIHRoaXMgbW9kdWxlLCB3ZSB3aWxsIHVzZSB0d28gZGF0YSBzZXRzIC0tIHRoZSAiUGFsbWVycyBQZW5ndWlucyIgZGF0YSBkZXNjcmliaW5nIHBlbmd1aW4gcG9wdWxhdGlvbnMgaW4gdGhlIFBhbG1lciBBcmNoaXBlbGFnbyAoQW50YXJjdGljYSksIGFuZCBhIGRhdGEgc2V0IGNvbnRhaW5pbmcgQ2hsb3JvcGh5bGwgQSAoQ2hsQSkgcmVhZGluZ3MgZnJvbSB0aHJlZSBOZXcgWmVhbGFuZCBsYWtlcyAoZGF0YSBwcm92aWRlZCBieSB0aGUgbG9jYWwgUmVnaW9uYWwgQ291bmNpbHMpLiBDaGxBIGxldmVscyBhcmUgYW4gaW5kaWNhdG9yIG9mIHBoeXRvcGxhbmt0b24gYmlvbWFzcywgYW5kIHByb3ZpZGUgYSBnZW5lcmFsIG1lYXN1cmUgb2YgbGFrZSBoZWFsdGggLS0gbW9yZSBDaGxBIGluZGljYXRlcyBwb29yZXIgaGVhbHRoLiBUaGUgInRveGljIGFsZ2FsIGJsb29tcyIgdGhhdCBvY2N1ciBvY2Nhc2lvbmFsbHkgaW4gTmV3IFplYWxhbmQgbGFrZXMgYXJlIGFjY29tcGFuaWVkIGJ5IGEgZHJhbWF0aWMgc3Bpa2UgaW4gbWVhc3VyZWQgQ2hsQS4KClwKCj4gRG93bmxvYWRpbmcgdGhlIENobEEgTlogTGFrZXMgZGF0YToKPiAKPiBPbmUgb3B0aW9uIGZvciBkb3dubG9hZGluZyB0aGUgZGF0YSBpcyB0byB1c2UgYGRvd25sb2FkLmZpbGUoKWAgaW4gUlN0dWRpby4gCj4gCj4gYGBge3IsIGV2YWwgPSBGQUxTRX0KPiBkb3dubG9hZC5maWxlKHVybCA9ICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcnRpcy10cmFpbmluZy8yMDIzLXMyLXI0c3NwL21haW4vZG9jcy9kYXRhL05aX2xha2VfY2hsYV9kYXRhLmNzdiIsIAo+ICBkZXN0ZmlsZSA9ICJkYXRhL05aX2xha2VfY2hsYV9kYXRhLmNzdiIpCj4gYGBgCj4gX1JlbWVtYmVyIHRvIGFkanVzdCB0aGUgdmFsdWUgb2YgYGRlc3RmaWxlYCB0byBtYXRjaCB5b3VyIHByb2plY3QgZGlyZWN0b3J5IHN0cnVjdHVyZS5fCj4KPiBUaGUgc2Vjb25kIG9wdGlvbiBpcyB0byBkb3dubG9hZCB0aGUgZmlsZSBmcm9tIHRoZSBSNFNTUCBzaGFyZWQgR29vZ2xlIERyaXZlIGZvbGRlciBhdCBodHRwczovL2RyaXZlLmdvb2dsZS5jb20vZHJpdmUvZm9sZGVycy8xVUJwLVA0d0ZBYVFMM2VnSVE3ZEdhMlJRZTZSUUtxZ3kKClwKCiMgTG9hZGluZyB0aGUgZGF0YQoKYGBge3IgbG9hZF90aGVfZGF0YSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgVGhlIFBlbmd1aW4gRGF0YToKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CgojIEluc3RhbGwgcGFsbWVycGVuZ3VpbnMgb25jZSBvbiBhbnkgY29tcHV0ZXIKCiMgaW5zdGFsbC5wYWNrYWdlcygicGFsbWVyc3Blbmd1aW5zIikKCiMgQWZ0ZXIgbG9hZGluZyB0aGUgbGlicmFyeSwgYSB0aWJibGUgY2FsbGVkICdwZW5ndWlucycgd2lsbCBiZSBpbml0aWFsaXNlZApsaWJyYXJ5KHBhbG1lcnBlbmd1aW5zKQoKIyBDaGVjayB0aGUgc3RydWN0dXJlCnN0cihwZW5ndWlucykKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIFRoZSBMYWtlIERhdGE6CiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKIyBSZWFkIGluIHRoZSBsYWtlcyBkYXRhIGZpbGUKbGFrZXNfZGYgPC0gcmVhZC5jc3YoImRhdGEvTlpfbGFrZV9jaGxhX2RhdGEuY3N2IikKCiMgQ2hlY2sgdGhlIHN0cnVjdHVyZQpzdHIobGFrZXNfZGYpCmBgYAoKSW4gdGhlIGxha2VzIGRhdGEsIHRoZSBMYWtlTmFtZSwgTW9udGgsIGFuZCBZZWFyIGNvbHVtbnMgYXJlIGFsbCBjYXRlZ29yaWNhbCBncm91cGluZyB2YXJpYWJsZXMuIE1hbnkgZGF0YSBhbmFseXNpcyBtZXRob2RzIGluIFIgcmVxdWlyZSB0aGF0IGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBiZSBvZiB0eXBlIGBmYWN0b3JgIChhcyBpbiAiZXhwZXJpbWVudGFsIGZhY3RvciIpLiBOb3RlIGhvd2V2ZXIsIHRoYXQgdGhlc2UgY29sdW1ucyBoYXZlIGJlZW4gaW1wb3J0ZWQgaW50byBSIGFzIHR5cGVzIGBjaHJgIChzdHJpbmdzKSBhbmQgYGludGAgKGludGVnZXJzKS4gV2Ugc2hvdWxkIGNhc3QgdGhlc2UgY29sdW1ucyB0byB0eXBlIGBmYWN0b3JgIHRvIGluc3VyZSB0aGF0IG91ciBzdWJzZXF1ZW50IGFuYWx5c2VzIGFyZSBjb3JyZWN0LiBUaGlzIGNhc3QgZG9lcyBub3QgYWZmZWN0IHRoZSB2YWx1ZXMgaW4gdGhlIGNvbHVtbnMsIGl0IHNpbXBseSBzaWduYWxzIHRvIFIgdGhhdCB0aGV5IGFyZSBncm91cCBpZGVudGlmaWVycywgbm90IHJhdyBzdHJpbmcgb3IgbnVtYmVyIGRhdGEgdmFsdWVzLgogCmBgYCB7ciBjYXN0X3RvX2ZhY3Rvcn0KbGFrZXNfZGYkTGFrZU5hbWUgPC0gYXMuZmFjdG9yKGxha2VzX2RmJExha2VOYW1lKQpsYWtlc19kZiRZZWFyIDwtIGFzLmZhY3RvcihsYWtlc19kZiRZZWFyKQpsYWtlc19kZiRNb250aCA8LSBhcy5mYWN0b3IobGFrZXNfZGYkTW9udGgpICAKCiMgQ29uZmlybSB0aGF0IHRoZSBjb2x1bW4gZGF0YSB0eXBlcyBoYXZlIGJlZW4gdXBkYXRlZApzdHIobGFrZXNfZGYpCmBgYAoKIyBWaXN1YWxpc2UgdGhlIGRhdGEgKHJldmlzaW9uKQoKV2hlbiBmYWNlZCB3aXRoIGEgbmV3IGRhdGEgc2V0LCBteSBmaXJzdCBzdGVwIGlzIGludmFyaWFibHkgdG8gc3RhcnQgbWFraW5nIGdyYXBocy4gVGhlc2UgInBpY3R1cmVzIiBvZiB5b3VyIGRhdGEgcHJvdmlkZSBhbiBlYXN5IHdheSB0byBzZWUgbGFyZ2Utc2NhbGUgcGF0dGVybnMgdGhhdCB3aWxsIGhlbHAgZ3VpZGUgeW91ciBmdXJ0aGVyIGFuYWx5c2lzLiBUaGV5IGFsc28gaGVscCB5b3UgdG8gY2F0Y2ggYW55IHByb2JsZW1zIGluIHlvdXIgZGF0YSAoc2VlIHRoZSBza2V3bmVzcyBleGVyY2lzZSBpbiB0aGUgWm9vbSBOb3RlcyBmb3IgdGhpcyBtb2R1bGUpIHRoYXQgbXVzdCBiZSBhZGRyZXNzZWQgYmVmb3JlIHByb2NlZWRpbmcgdG8gbW9yZSBjb21wbGV4IGFuYWx5c2VzLgoKQW4gZXhjZWxsZW50IGZpcnN0IGdyYXBoIGZvciBjb250aW51b3VzIChpLmUuIG5vdCBjYXRlZ29yaWNhbCkgZGF0YSBpcyB0aGUgKipmcmVxdWVuY3kgZGlzdHJpYnV0aW9uKiosICBvciAqKmhpc3RvZ3JhbSoqLCB3aGljaCBoYXMgZGF0YSB2YWx1ZSBvbiB0aGUgeC1heGlzIGFuZCBmcmVxdWVuY3kgKGkuZS4gY291bnQgb3IgcHJvcG9ydGlvbikgb24gdGhlIHktYXhpcy4gVGhpcyBzaG93cyB5b3UsIGluIGEgc2luZ2xlIHBpY3R1cmUsIGhvdyB5b3VyIGRhdGEgYXJlIGRpc3RyaWJ1dGVkLiBXZSBtZXQgdGhlIGhpc3RvZ3JhbSBpbiBNb2R1bGUgMDIuIAoKVGhlIGBwZW5ndWluc2AgZGF0YSBzZXQgY29udGFpbnMgdmFsdWVzIGZvciAzNDQgZGlmZmVyZW50IHBlbmd1aW5zLiBXZSBjYW4gYmVnaW4gYnkgbG9va2luZyBhdCBob3cgdGhlIHBlbmd1aW5zJyBib2R5IHdlaWdodHMgYXJlIGRpc3RyaWJ1dGVkLiAKCiMjIEhpc3RvZ3JhbSB3aXRoIGJhc2UgUgoKYGBge3IgMDQtaGlzdG9ncmFtcywgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyBUaGUgJ2JyZWFrcycgYXJndW1lbnQgY29udHJvbHMgdGhlIG51bWJlciBvZiBiYXJzIGRyYXduCmhpc3QocGVuZ3VpbnMkYm9keV9tYXNzX2csIAogICAgIGJyZWFrcyA9IDEwMCwKICAgICBtYWluPSJEaXN0cmlidXRpb24gb2YgUGVuZ3VpbiBCb2R5IE1hc3MiLCB4bGFiID0gIkJvZHkgbWFzcyAoZykiLCB5bGFiID0gIkZyZXF1ZW5jeSIpCmBgYAoKIyMgSGlzdG9ncmFtIHdpdGggZ2dwbG90CgpgYGB7ciBnZ3Bsb3RfaGlzdG8sICwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyBMb2FkIHRoZSBsaWJyYXJ5IGJlZm9yZSB5b3VyIGZpcnN0IGNhbGwgdG8gZ2dwbG90CmxpYnJhcnkoZ2dwbG90MikKCiMgVGhlIHZhbHVlcyBwcm92aWRlZCB0byAnY29sb3VyJyBhbmQgJ2ZpbGwnIGFyZSBoZXhpZGVjaW1hbCBjb2xvdXIgY29kZXMuIE5vdGUgdGhlIAojIGhhc2ggbWFyayBwcmVmaXguIEl0IGlzIHJlcXVpcmVkLgpnZ3Bsb3QoZGF0YSA9IHBlbmd1aW5zLCBtYXBwaW5nID0gYWVzKHggPSBib2R5X21hc3NfZykpICsKICBnZW9tX2hpc3RvZ3JhbShjb2xvdXIgPSAiIzcxMzNmZiIsIGZpbGw9IiNiYmJiZmYiKSArCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgUGVuZ3VpbiBCb2R5IE1hc3MiLCAKICAgICAgIHggPSAiQm9keSBNYXNzIChnKSIsIAogICAgICAgeSA9ICJGcmVxdWVuY3kiKSArIHRoZW1lX2J3KCkKYGBgCgojIyBJbGx1c3RyYXRpbmcgR3JvdXBzCgpVc2luZyBnZ3Bsb3QsIHdlIGNhbiBpbGx1c3RyYXRlIGdyb3VwIGVmZmVjdHMgaW4gaGlzdG9ncmFtcyBieSBkZWZpbmluZyBhIG1hcHBpbmcgZnJvbSBhIGdyb3VwaW5nIChpLmUuIGNhdGVnb3JpY2FsKSB2YXJpYWJsZSB0byB0aGUgYGZpbGxgIHByb3BlcnR5IG9mIGZ1bmN0aW9uIGBnZW9tX2hpc3RvZ3JhbWAuIEZvciBleGFtcGxlLCB0aGUgY29kZSBiZWxvdyB3aWxsIG1ha2UgaGlzdG9ncmFtcyBvZiBhbGwgdGhlIENobEEgdmFsdWVzIGluIGRhdGEgZnJhbWUgYGxha2VzX2RmYCwgd2l0aCBlYWNoIGxha2UgaW4gYSBkaWZmZXJlbnQgY29sb3VyCgpCeSBkZWZhdWx0LCBnZW9tX2hpc3RvZ3JhbSBwcm9kdWNlcyBhIHN0YWNrZWQgcGxvdCAtLSB0aGF0IGlzLCB0aGUgZGlmZmVyZW50IGdyb3VwcyBhcmUgc2hvd24gc3RhY2tlZCB1cCBpbiBhIHNpbmdsZSBiYXIsIHNlcGFyYXRlZCBieSB0aGVpciBjb2xvdXIuIFRvIG1ha2UgdGhlIHBsb3Qgd2l0aCBzaWRlLWJ5LXNpZGUgYmFycywgc2V0IGdlb21faGlzdG9ncmFtJ3MgYHBvc2l0aW9uYCBhcmd1bWVudCB0byBgZG9kZ2VgLiBOb3RlIHRoYXQgdGhpcyBpcyBub3QgYSBtYXBwaW5nLCBpdCBpcyBzaW1wbHkgYW4gYXJndW1lbnQgdG8gZnVuY3Rpb24gZ2VvbV9oaXN0b2dyYW0uIFdoYXQgZG9lcyB0aGlzIHNpbXBsZSBncmFwaCB0ZWxsIHlvdSBhYm91dCB0aGUgaGVhbHRoIG9mIHRoZXNlIHRocmVlIGxha2VzPwoKIyMjIEZvciBjb250aW51b3VzIGRhdGEKCmBgYHtyIDA0LWxha2VfaGlzdHMsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiMgU3RhY2tlZCBncm91cGVkIGhpc3RvZ3JhbQpnZ3Bsb3QoZGF0YSA9IGxha2VzX2RmLCBtYXBwaW5nID0gYWVzKHggPSBDaGxBKSkgKwogIGdlb21faGlzdG9ncmFtKGFlcyhmaWxsPUxha2VOYW1lKSwgY29sb3VyID0gJ2JsYWNrJykKCiMgU2lkZS1ieS1zaWRlIGdyb3VwZWQgaGlzdG9ncmFtCmdncGxvdChkYXRhID0gbGFrZXNfZGYsIG1hcHBpbmcgPSBhZXMoeCA9IENobEEpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKGZpbGw9TGFrZU5hbWUpLCBjb2xvdXIgPSAnYmxhY2snLCBwb3NpdGlvbj0iZG9kZ2UiKQoKYGBgCgojIyMgRm9yIGNhdGVnb3JpY2FsIGRhdGEKClRoZSBmdW5jdGlvbnMgYGhpc3RgIGFuZCBgZ2VvbV9oaXN0b2dyYW1gIGFyZSBhcHByb3ByaWF0ZSBmb3IgKipjb250aW51b3VzKiogKG51bWVyaWNhbCkgZGF0YS4gRm9yICoqY2F0ZWdvcmljYWwqKiB2YXJpYWJsZXMgKGUuZy4gU3BlY2llcyBhbmQgSXNsYW5kIGluIHRoZSBwZW5ndWluIGRhdGEgc2V0KSBvbmUgb2Z0ZW4gdXNlcyB0aGUgbW9yZSBnZW5lcmFsIGBnZW9tX2JhcmAgZnVuY3Rpb24gKGdlb21faGlzdG9ncmFtIGlzIGEgc3BlY2lhbCBjYXNlIG9mIGdlb21fYmFyKS4gVGhlIGV4YW1wbGUgY29kZSBiZWxvdyBzaG93cyBob3cgdG8gZ2VuZXJhdGUgYSBiYXIgZ3JhcGggaW4gZ2dwbG90LCBtb2RpZnlpbmcgdGhlIGRlZmF1bHQgZ2dwbG90IGNvbG91ciBwYWxldHRlIHRvIHNvbWV0aGluZyBtb3JlIGFjY2Vzc2libGUgdG8gdmlld2VycyB3aXRoIGF0eXBpY2FsIGNvbG91ciB2aXNpb246CgpgYGB7ciAwNC1jYXRlZ29yaWNhbF9kaXN0cmlidXRpb259CiMgIkNvbG91ci1ibGluZCBmcmllbmRseSIgcGFsZXR0ZSBmcm9tICNodHRwczovL3BlcnNvbmFsLnNyb24ubmwvfnBhdWx0LwojIFRoZXNlIGFyZSBoZXhhZGVjaW1hbCBjb2xvdXIgY29kZXMuIFRoZSAjIGlzIHJlcXVpcmVkLgpjdXN0b21QYWxldHRlIDwtIGMoIiNEREFBMzMiLCAiI0JCNTU2NiIsICIjMDA0NDg4IikKCiMgR2VuZXJhdGUgYSBzdGFja2VkIGJhciBwbG90LCBhbmQgdXNlIG91ciBjdXN0b20gY29sb3VyIHBhbGV0dGUKZ2dwbG90KGRhdGEgPSBwZW5ndWlucywgbWFwcGluZyA9IGFlcyh4ID0gaXNsYW5kLCBmaWxsPXNwZWNpZXMpKSArCiAgZ2VvbV9iYXIoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY3VzdG9tUGFsZXR0ZSkKYGBgCgpBcyB3aXRoIGdlb21faGlzdG9ncmFtIGFib3ZlLCB3ZSBzZXQgdGhlIHBvc2l0aW9uIGFyZ3VtZW50IG9mIGdlb21fYmFyIHRvIGNoYW5nZSBmcm9tIHN0YWNrZWQgdG8gc2lkZS1ieS1zaWRlIGZvcm1hdC4KCmBgYHtyIDA0LWNhdGVnb3JpY2FsX2Rpc3RyaWJ1dGlvbl9kb2RnZSwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KCiMgIkNvbG91ci1ibGluZCBmcmllbmRseSIgcGFsZXR0ZSBmcm9tICNodHRwczovL3BlcnNvbmFsLnNyb24ubmwvfnBhdWx0LwojIFRoZXNlIGFyZSBoZXhhZGVjaW1hbCBjb2xvdXIgY29kZXMuIFRoZSAjIGlzIHJlcXVpcmVkLgpjdXN0b21QYWxldHRlIDwtIGMoIiNEREFBMzMiLCAiI0JCNTU2NiIsICIjMDA0NDg4IikKCiMgR2VuZXJhdGUgYSBzdGFja2VkIGJhciBwbG90LCBhbmQgdXNlIG91ciBjdXN0b20gY29sb3VyIHBhbGV0dGUKZ2dwbG90KGRhdGEgPSBwZW5ndWlucywgbWFwcGluZyA9IGFlcyh4ID0gaXNsYW5kLCBmaWxsPXNwZWNpZXMpKSArCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiLCBjb2xvciA9ICJibGFjayIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21QYWxldHRlKQpgYGAKCkV2ZW4gYSBzaW1wbGUgZ3JhcGggbGlrZSB0aGlzIGhlbHBzIHlvdSB0byBnZXQgdG8ga25vdyB5b3VyIGRhdGEuIEp1c3QgYnkgaW5zcGVjdGlvbiB3ZSBzZWUgdGhhdCBCaXNjb2UgaXNsYW5kIGhhcyB0aGUgbGFyZ2VzdCBwb3B1bGF0aW9uLCBUb3JnZXJzZW4gaGFzIG9ubHkgQWRlbGllIHBlbmd1aW5zLCBEcmVhbSBJc2xhbmQgaGFzIG5lYXJseSBlcXVhbCBudW1iZXJzIG9mIENoaW5zdHJhcCBhbmQgR2VudG9vLCBldGMuIFdoZW4gZmlyc3QgYXBwcm9hY2hpbmcgYSBiaWcgZGF0YSBzZXQsIGFsd2F5cyB0aGluayBhYm91dCBzdGFydGluZyB3aXRoIHNvbWUgZ3JhcGhzLgoKCiMgTWVhc3VyZXMgb2YgQ2VudHJhbCBUZW5kZW5jeQoKTG9vayBhdCB0aGUgZ3JhcGggeW91IG1hZGUgZWFybGllciBzaG93aW5nIHRoZSBkaXN0cmlidXRpb25zIG9mIENobEEgZm9yIHRoZSB0aHJlZSBsYWtlcy4gWW91IG1pZ2h0IGRlc2NyaWJlIHRoZSBMYWtlIEVsbGVzbWVyZSBDaGxBIHJlYWRpbmdzIGFzICJtb3N0bHkgYmV0d2VlbiA1MCBhbmQgMTAwIiBhbmQgdGhlIExha2UgUm90b3J1YSByZWFkaW5ncyBhcyAibW9zdGx5IGFyb3VuZCAxMCIuICBTdGF0ZW1lbnRzIGxpa2UgdGhpcyBhcmUgYXR0ZW1wdHMgdG8gZGVzY3JpYmUgYSAqKnR5cGljYWwqKiBzY29yZSBmcm9tIGEgbGFyZ2UgZGF0YSBzZXQuIFRoZXkgYWxsb3cgdXMgdG8gY2FwdHVyZSB0aGUgZmFjdCB0aGF0LCBmb3IgZXhhbXBsZSwgKm92ZXJhbGwqLCBMYWtlIEVsbGVzbWVyZSBoYXMgaGlnaGVyIENobEEgcmVhZGluZ3MgdGhhbiBMYWtlIFJvdG9ydWEuIEl0IGlzIG5vdCB0aGUgY2FzZSB0aGF0ICpldmVyeSogRWxsZXNtZXJlIHJlYWRpbmcgaXMgaGlnaGVyIHRoYW4gKmV2ZXJ5KiBSb3RvcnVhIHJlYWRpbmcsIGJ1dCAqKnR5cGljYWxseSoqIHRoaXMgaXMgdGhlIGNhc2UuIAoKSW4gc3RhdGlzdGljcywgYSBwcmVjaXNlIG1lYXN1cmUgb2Ygc3VjaCB0eXBpY2FsaXR5IGlzIGNhbGxlZCBhICoqTWVhc3VyZSBvZiBDZW50cmFsIFRlbmRlbmN5KiogKE1DVCkuIFRoZSBtb3N0IGNvbW1vbiBNQ1RzIGFyZSB0aGUgKiptZWFuKiosIHRoZSAqKm1lZGlhbioqIGFuZCB0aGUgKiptb2RlKiouIFRoZXNlIGFyZSwgcmVzcGVjdGl2ZWx5LCB0aGUgbWF0aGVtYXRpY2FsIGF2ZXJhZ2UsIHRoZSBtaWRkbGUgc2NvcmUsIGFuZCB0aGUgbW9zdCBmcmVxdWVudCBzY29yZSAob3Igc2NvcmVzKSBpbiBhIGRhdGEgc2V0LiBUaGVyZSBhcmUgc29tZSBzdWJ0bGUgc3RhdGlzdGljYWwgaXNzdWVzIGFyb3VuZCB3aGljaCBvZiB0aGUgdGhyZWUgTUNUIGlzIGFwcHJvcHJpYXRlIGZvciBhbnkgZ2l2ZW4gZGF0YSBhbmFseXNpcyBzaXR1YXRpb24gKGFzayB5b3VyIGxlY3R1cmVyIGZvciBkZXRhaWxzKSwgYnV0IHRoZXkgYXJlIGFsbCBlYXN5IHRvIGNvbXB1dGUgaW4gUiAod2UgaGF2ZSwgaW4gZmFjdCwgYWxyZWFkeSBtZXQgZnVuY3Rpb24gYG1lYW5gIGluIGVhcmxpZXIgbW9kdWxlcyksIGFuZCB3ZSBzaG93IGV4YW1wbGUgY29kZSBiZWxvdyBmb3IgY29tcHV0aW5nIHRoZXNlIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3Mgb24gYSBzaW5nbGUgY29sdW1uIG9mIGRhdGEgZnJvbSB0aGUgcGVuZ3VpbnMgZGF0YSBzZXQuCgpOb3RlIHRoYXQgdGhlIHBlbmd1aW5zIGRhdGEgaGFzIHNvbWUgbWlzc2luZyBzb21lIHZhbHVlcyAoY2YuIE1vZHVsZSAwMyAtIFN1YnNldHRpbmcpLCBUaGUgZnVuY3Rpb25zIGZvciBtZWFuIGFuZCBtZWRpYW4gd2lsbCBub3Qgd29yayBpZiB0aGUgaW5wdXQgZGF0YSBoYXZlIGFueSBgTkFgIChtaXNzaW5nKSB2YWx1ZXMuIFRoZSBtb3N0IGNvbW1vbiBzb2x1dGlvbiBpcyB0byBvbWl0IHRob3NlIHNjb3JlcyBmcm9tIHRoZSBjb21wdXRhdGlvbiBieSBzZXR0aW5nIHRoZSBgbmEucm1gIGFyZ3VtZW50IHRvIGBUUlVFYCBhcyBzaG93bjoKCgojIyBNZWFuCgpgYGB7ciBtZWFuLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIFdlIGhhdmUgc2VlbiB0aGlzIGNvZGUgYmVmb3JlLi4uIFdlIHBhc3MgdGhlIGNvbHVtbiBvZiBpbnRlcmVzdAojIHRvIGZ1bmN0aW9uIG1lYW4KbWVhbihwZW5ndWlucyRib2R5X21hc3NfZywgbmEucm09VFJVRSkKYGBgCgojIyBNZWRpYW4KCmBgYHtyIG1lZGlhbiwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyBUaGUgZmFtaWxpYXIgcGF0dGVybi4uLi4KbWVkaWFuKHBlbmd1aW5zJGJvZHlfbWFzc19nLCBuYS5ybSA9IFRSVUUpCmBgYAoKIyMgTW9kZQoKQmFzZSBSIGhhcyBubyBidWlsdC1pbiBmdW5jdGlvbiBmb3IgbW9kZS4gQWZ0ZXIgTW9kdWxlIDA4IHlvdSB3aWxsIGJlIGFibGUgdG8gd3JpdGUgeW91ciBvd24gTW9kZSBmdW5jdGlvbi4gT3IgeW91IGNhbiB1c2Ugb25lIG9mIHNldmVyYWwgYXZhaWxhYmxlIGluIGF1eGlsaWFyeSBsaWJyYXJpZXMuIFRoZSBEZXNjVG9vbHMgbGlicmFyeSBpcyBhIGdvb2Qgb25lLgoKYGBge3IgbW9kZSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyBJbnN0YWxsIHRoZSBwYWNrYWdlIG9uY2Ugb24gZWFjaCBtYWNoaW5lCiMgaW5zdGFsbC5wYWNrYWdlcygiRGVzY1Rvb2xzIikKCiMgTG9hZCB0aGUgbGlicmFyeSBvbmNlIGVhY2ggc2Vzc2lvbgpsaWJyYXJ5KERlc2NUb29scykKCiNDYWxsIHRoZSBmdW5jdGlvbgpNb2RlKHBlbmd1aW5zJGJvZHlfbWFzc19nLCBuYS5ybT1UUlVFKQoKYGBgCgpOb3RlIHRoYXQgRGVzY1Rvb2xzOjpNb2RlIHJldHVybnMgdGhlIG1vZGFsIChpLmUuIG1vc3QgY29tbW9uIHZhbHVlKSB3aXRoIGFuIGF0dGFjaGVkICoqYXR0cmlidXRlKiogY2FsbGVkICJmcmVxIiBlcXVhbCB0byB0aGUgbnVtYmVyIG9mIG9jY3VycmVuY2VzLgoKIyMgVXNpbmcgZnVuY3Rpb24gYHN1bW1hcnlgCgpXaGVuIHlvdSBoYXZlIGEgdmVyeSBsYXJnZSBudW1iZXIgb2YgZGF0YSBtZWFzdXJlcywgeW91IG1heSB3aXNoIHRvIGNvbXB1dGUgTUNUcyBmb3IgaW5kaXZpZHVhbCBjb2x1bW5zIGFzIHNob3duIGFib3ZlLiBBbiBlZmZpY2llbnQgYWx0ZXJuYXRpdmUgZm9yIHNtYWxsZXIgZGF0YSBzZXRzIGlzIHRvIHVzZSBmdW5jdGlvbiBgc3VtbWFyeWAsIHdoaWNoIGFjY2VwdHMgYSBkYXRhIGZyYW1lIGFuZCBzdW1tYXJpc2VzICphbGwqIGl0cyBjb2x1bW5zIGF0IG9uY2UuIEZ1bmN0aW9uIGBzdW1tYXJ5YCBjb21wdXRlcyBmcmVxdWVuY2llcyBmb3IgY2F0ZWdvcmljYWwgdmFyaWFibGVzLCBhbmQgbWVhc3VyZXMgb2YgY2VudHJhbCB0ZW5kZW5jeSBmb3IgY29udGludW91cyB2YXJpYWJsZXMuIEl0IGFsc28gcmVwb3J0cyB0aGUgbnVtYmVycyBvZiBOQSB2YWx1ZXMgaW4gZWFjaCBjb2x1bW4uIEZ1bmN0aW9uIGBzdW1tYXJ5YCBwcm92aWRlcyBzb21lIGFkZGl0aW9uYWwgbWVhc3VyZXMgKG1pbmltdW0sIDFzdCBxdWFydGlsZSwgM3JkIHF1YXJ0aWxlLCBhbmQgbWF4aW11bSkgdGhhdCB3ZSB3aWxsIGRpc2N1c3MgaW4gbW9yZSBkZXRhaWwgbGF0ZXIuCgpgYGB7ciBzdW1tYXJ5fQpzdW1tYXJ5KHBlbmd1aW5zKQpgYGAKCiMjIyBFeGVyY2lzZQoKVGhlIHJlc3VsdHMgZm9yIGNvbHVtbiBgeWVhcmAgbWF5IG5vdCBiZSB3aGF0IHlvdSBleHBlY3RlZC4gRnVuY3Rpb24gYHN1bW1hcnlgIGhhcyBjb21wdXRlZCBhbiAqYXZlcmFnZSB2YWx1ZSogZm9yIGB5ZWFyYC4gRG9lcyB0aGlzIHNlZW0gbGlrZSB0aGUgYXBwcm9wcmlhdGUgYW5hbHlzaXM/IChBbnN3ZXIgPT4gTm8uKSBNb2RpZnkgYHBlbmd1aW5zYCB0byBtYWtlIGBzdW1tYXJ5YCB0cmVhdCB0aGUgYHllYXJgIGRhdGEgY29ycmVjdGx5LCBhbmQgcmVydW4gYHN1bW1hcnlgLgoKCiMgTWVhc3VyZXMgb2YgVmFyaWFiaWxpdHkKCkluIHRoZSBoaXN0b2dyYW1zIGZvciBDaGxBIGZyb20gZWFjaCBvZiB0aHJlZSBOZXcgWmVhbGFuZCBsYWtlcywgdGhlIHRocmVlIGdyb3VwcyBvZiBzY29yZXMgZGlkIG5vdCBvdmVybGFwIGNvbXBsZXRlbHksIGluZGljYXRpbmcgdGhhdCB0aGUgdHlwaWNhbCB2YWx1ZXMgLS0gdGhlIGNlbnRyYWwgdGVuZGVuY2llcyAtLSB3ZXJlIGRpZmZlcmVudCBmb3IgdGhlIHRocmVlIGxha2VzLiBXZSBjYW4gY29uZmlybSB0aGlzIG9ic2VydmF0aW9uIGJ5IGNvbXBhcmluZyB0aGUgbWVhbnMuIFdlIGNhbiB1c2UgZnVuY3Rpb24gYGFnZ3JlZ2F0ZWAgaW4gYmFzZSBSLCBvciBgZ3JvdXBfYnlgIGFuZCBgc3VtbWFyaXNlYCwgZnJvbSBsaWJyYXJ5IGRwbHlyLgoKIyMjIFVzaW5nIGBhZ2dyZWdhdGVgCgpgYGB7ciBsYWtlX21lYW5zXzAxfQojIFVzaW5nIGFnZ3JlZ2F0ZS4gY29tcHV0ZSB0aGUgZ3JvdXAgbWVhbnMKYWdncmVnYXRlKGxha2VzX2RmJENobEEsIGJ5ID0gbGlzdChMYWtlID0gbGFrZXNfZGYkTGFrZU5hbWUpLCBGVU49bWVhbikKYGBgCgojIyMgVXNpbmcgYGdyb3VwX2J5YCBhbmQgYHN1bW1hcmlzZWAKCmBgYHtyIGxha2VfbWVhbnNfMDJ9CiMgVXNpbmcgYGdyb3VwX2J5YCBhbmQgYHN1bW1hcmlzZWAKbGlicmFyeShkcGx5cikKCmxha2VzX2RmICU+JSAKICBncm91cF9ieShMYWtlTmFtZSkgJT4lCiAgc3VtbWFyaXNlKE1lYW5DaGxBID0gbWVhbihDaGxBKSkKYGBgCgpIb3dldmVyLCBub3Qgb25seSBkbyB0aGUgY2VudHJhbCBwb2ludHMgb2YgdGhlIHRocmVlIGxha2VzJyBkaXN0cmlidXRpb25zIGRpZmZlciwgc28gZG8gdGhlIGFtb3VudHMgb2YgInNwcmVhZCIuIExha2UgVGF1cG8ncyBkaXN0cmlidXRpb24gaXMgdmVyeSBuYXJyb3c7IGFsbCBpdHMgcmVhZGluZ3MgYXJlIHNpbWlsYXIuIExha2UgRWxsZXNtZXJlIGlzIHNxdWFzaGVkIGFuZCBzcHJlYWQgb3V0OyBpdHMgcmVhZGluZ3MgdmFyeSBhIGxvdC4gTGFrZSBSb3RvcnVhIGlzIGludGVybWVkaWF0ZS4gVG8gaWxsdXN0cmF0ZSB0aGlzIG1vcmUgY2xlYXJseSwgd2UgY2FuIHVzZSBnZ3Bsb3QgdG8gbWFrZSBzZXBhcmF0ZSBncmFwaHMgZm9yIGVhY2ggbGFrZSwgdXNpbmcgZnVuY3Rpb24gYGZhY2V0X2dyaWRgLiAKCk5vdGUgdGhlIGBzY2FsZXNgIGFyZ3VtZW50IHRvIGBmYWNldF9ncmlkYC4gVGhpcyBhbGxvd3MgZWFjaCBncmFwaCB0byBhZGp1c3QgaXRzIHktYXhpcyB0byBpdHMgZGF0YSBkb21haW4sIHdoaWNoIG1ha2VzIHRoZSBjb21wYXJpc29uIHZpc3VhbGx5IGVhc2llcjsgdGhpcyBzaG91bGQgYmUgbWVudGlvbmVkIGluIHRoZSBkaXNjdXNzaW9uIG9mIHRoZSBmaWd1cmUgaW4gYSBtYW51c2NyaXB0LgoKIyMgVXNpbmcgYGZhY2V0X2dyaWRgCgpgYGB7ciBmYWNldF9ncmFwaHMsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmdncGxvdChkYXRhID0gbGFrZXNfZGYpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IENobEEsIGZpbGw9TGFrZU5hbWUpLCBjb2xvcj0iYmxhY2siKSArCiAgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhMYWtlTmFtZSksIHNjYWxlcz0iZnJlZV95IikKYGBgCgpTdGF0aXN0aWNhbGx5LCB0aGUgInNwcmVhZCBvdXQiIHF1YWxpdHkgb2YgYSBkaXN0cmlidXRpb24gcmVmbGVjdHMgaXRzICoqdmFyaWFiaWxpdHkqKi4KCldlIGNhbiBjYXB0dXJlIHZhcmlhYmlsaXR5IG1vcmUgcHJlY2lzZWx5IHdpdGggbWVhc3VyZXMgb2YgdGhlICoqcmFuZ2UqKiBvZiB0aGUgZGF0YSBzZXQuIFRoZXNlIGFyZSB0eXBpY2FsbHkgdGhlIHNtYWxsZXN0IGFuZCBsYXJnZXN0IHNjb3JlcyAobWluaW11bSBhbmQgbWF4aW11bSkgYW5kIHRoZSBzY29yZXMgYXQgdGhlIDI1dGggYW5kIDc1dGggcGVyY2VudGlsZXMgKGFsc28gY2FsbGVkICoqMXN0IHF1YXJ0aWxlKiogYW5kICoqM3JkIHF1YXJ0aWxlKiopLiBFYXJsaWVyLCB3ZSBzYXcgdGhhdCBmdW5jdGlvbiBgc3VtbWFyeWAgY29tcHV0ZXMgdGhlc2UgbWVhc3VyZXMgb2YgcmFuZ2UuIEhvd2V2ZXIsIGlmIHdlIHNpbXBseSBwYXNzIHRoZSBlbnRpcmUgYGxha2VzX2RmYCBkYXRhIGZyYW1lIHRvIGZ1bmN0aW9uIGBzdW1tYXJ5YCwgaXQgd2lsbCBjb21iaW5lIHRoZSBkYXRhIGZyb20gYWxsIHRocmVlIGxha2VzIC0tIHRvIGNvbXBhcmUgdGhlIGxha2VzIHdlIG5lZWQgdGhlIHZhbHVlcyBmcm9tIGVhY2ggbGFrZSBzZXBhcmF0ZWx5LgoKSW4gZWFybGllciBtb2R1bGVzIHdlIGhhdmUgc2VlbiB0d28gdGVjaG5pcXVlcyBmb3Igc2VsZWN0aW5nIG91dCBqdXN0IHRoZSByb3dzIGZyb20gb25lIGxha2UgKHVzaW5nIFtdIG9yIHVzaW5nIGBmaWx0ZXJgKS4gVG8gcnVuIGZ1bmN0aW9uIGBzdW1tYXJ5YCBvbiBlYWNoIGxha2Ugc2VwYXJhdGVseSwgd2UgY291bGQgc2VsZWN0IHRoZSBzdWJzZXQgZm9yIGVhY2ggbGFrZSBpbiB0dXJuLCBhbmQgcGFzcyBlYWNoIHN1YnNldCB0byBgc3VtbWFyeWAuIEhvd2V2ZXIsIHdlIGNhbiBhY2hpZXZlIHRoZSBzYW1lIHJlc3VsdCBtb3JlIHBhcnNpbW9uaW91c2x5IGJ5IHVzaW5nIGZ1bmN0aW9uIGBhZ2dyZWdhdGVgLiBBYm92ZSB3ZSB1c2VkIGFnZ3JlZ2F0ZSB3aXRoIGBGVU4gPSBtZWFuYCB0byBnZXQgdGhlIG1lYW4gQ2hsQSBmb3IgZWFjaCBsYWtlLiBXZSBjYW4gdXNlIGBGVU4gPSBzdW1tYXJ5YCB0byBjYWxsIGZ1bmN0aW9uIGBzdW1tYXJ5YCBzZXBhcmF0ZWx5IGZvciB0aGUgcmVjb3JkcyBvZiBlYWNoIGxha2UuIAoKYGBge3IgYWdncmVnYXRlX3N1bW1hcnl9CiMgQXBwbHkgZnVuY3Rpb24gc3VtbWFyeSBieSBncm91cAphZ2dyZWdhdGUobGFrZXNfZGYkQ2hsQSwgYnkgPSBsaXN0KExha2UgPSBsYWtlc19kZiRMYWtlTmFtZSksIEZVTj1zdW1tYXJ5KQoKYGBgCgpXZSBjYW4gYWxzbyBtZWFzdXJlIHRoZSB2YXJpYWJsaXR5IGluIGEgZGF0YSBzZXQgd2l0aCB0aGUgKipzdGFuZGFyZCBkZXZpYXRpb24qKi4gVGhlIHN0YW5kYXJkIGRldmlhdGlvbiBpcyB0aGUgbW9zdCBjb21tb25seSB1c2VkIG1lYXN1cmUgb2YgdmFyaWFiaWxpdHksIGFuZCBpdCBwbGF5cyBhbiBpbXBvcnRhbnQgbWF0aGVtYXRpY2FsIHJvbGUgaW4gaW5mZXJlbnRpYWwgc3RhdGlzdGljcyAoYXNrIHlvdXIgc3RhdHMgbGVjdHVyZXIgZm9yIGRldGFpbHMgLS0gaXQncyB2ZXJ5IGludGVyZXN0aW5nKS4gQ29uY2VwdHVhbGx5LCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIGlzICphbG1vc3QqIGVxdWFsIHRvIHRoZSBhdmVyYWdlIGRpc3RhbmNlIGZyb20gdGhlIG1lYW4gYWNyb3NzIGFsbCB0aGUgdmFsdWVzIGluIGEgZGF0YSBzZXQgLS0gaXQgZG9lc24ndCBlcXVhbCAqZXhhY3RseSogdGhhdCB2YWx1ZSwgYmVjYXVzZSBvZiBob3cgaXQgaXMgY29tcHV0ZWQsIGJ1dCBpdCBpcyBjbG9zZSwgYW5kIGl0IGNhbiBiZSBoZWxwZnVsIHRvIHRoaW5rIG9mIGl0IHdpdGggdGhpcyBhcHByb3hpbWF0aW9uLiBCaWcgc3RhbmRhcmQgZGV2aWF0aW9uIHNob3dzIHRoYXQgc2NvcmVzIGFyZSBzcHJlYWQgZmFyIGZyb20gdGhlaXIgbWVhbjsgc21hbGwgc3RhbmRhcmQgZGV2aWF0aW9uIHNob3dzIHRoYXQgc2NvcmVzIHRlbmQgdG8gaHVkZGxlIGNsb3NlIHRvIHRoZWlyIG1lYW4uIENvbXB1dGUgc3RhbmRhcmQgZGV2aWF0aW9uIHdpdGggZnVuY3Rpb24gYHNkYC4KCiMjIyBVc2luZyBgYWdncmVnYXRlYAoKYGBge3Igc2R9CiMgVXNpbmcgYWdncmVnYXRlLiBjb21wdXRlIHRoZSBncm91cCBzZHMKYWdncmVnYXRlKGxha2VzX2RmJENobEEsIGJ5ID0gbGlzdChMYWtlID0gbGFrZXNfZGYkTGFrZU5hbWUpLCBGVU49c2QpCmBgYAoKIyMjIFVzaW5nIGBncm91cF9ieWAgYW5kIGBzdW1tYXJpc2VgCgpgYGB7ciBzZF9kcGx5cn0KbGFrZXNfZGYgJT4lIAogIGdyb3VwX2J5KExha2VOYW1lKSAlPiUKICBzdW1tYXJpc2UoU3RkRGV2ID0gc2QoQ2hsQSkpCgpgYGAKClRoZSBoaXN0b2dyYW1zLCB0aGUgbWVhc3VyZXMgb2YgcmFuZ2UsIGFuZCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9ucyBhbGwgaW5kaWNhdGUgdGhhdCBUYXVwbyBoYXMgdmVyeSBzdGFibGUgQ2hsQSBtZWFzdXJlcywgUm90b3J1YSBpcyBhIGxpdHRsZSBub2lzaWVyLCBhbmQgRWxsZXNtZXJlIGlzIGFsbCBvdmVyIHRoZSBwbGFjZS4gVGhpcyBwaHl0b3BsYW5rdG9uIGJpb21hc3Mgc3RhYmlsaXR5IGlzIGFuIGltcG9ydGFudCBpbmRpY2F0b3Igb2YgbGFrZSBoZWFsdGggLS0gYSBzdGFibGUgbGFrZSBpcyBhdCBtdWNoIGxvd2VyIHJpc2sgb2YgYSB0b3hpYyBhbGdhbCBibG9vbS4KCgojIEVmZmljaWVudCBjb2RlIGZvciBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzCgpUaGUgZnVuY3Rpb24gYGRlc2NyaWJlQnlgIGluIHBhY2thZ2UgYHBzeWNoYCB3aWxsICBjb21wdXRlIGFsbCB0aGUgZGVzY3JpcHRpdmUgc3VtbWFyaWVzIHdlIGhhdmUgc2VlbiAoYW5kIGEgZmV3IG1vcmUpIGluIG9uZSBzdGF0ZW1lbnQuIFdoZW4geW91IGFyZSBleHBsb3JpbmcgYSBzaW5nbGUgZGF0YSBjb2x1bW4gYW5kIGEgc2luZ2xlIGdyb3VwaW5nIGNvbHVtbiAoc28gdGhlIG91dHB1dCBkb2Vzbid0IGdldCB0b28gbGFyZ2UpLCB0aGlzIGlzIGEgdmVyeSB1c2VmdWwgZnVuY3Rpb24uIAoKYGBge3IgcHN5Y2h9CiMgSW5zdGFsbCBvbmNlIG9uIGFueSBjb21wdXRlcgojaW5zdGFsbC5wYWNrYWdlcygicHN5Y2giKQoKIyBDYWxsIG9uY2UgZWFjaCBSIHNlc3Npb24KbGlicmFyeShwc3ljaCkKCiMgUGFzcyBpbiB0aGUgZGF0YSBjb2x1bW4gYW5kIHRoZSBncm91cGluZyBjb2x1bW4KZGVzY3JpYmVCeShsYWtlc19kZiRDaGxBLCBsYWtlc19kZiRMYWtlTmFtZSkKYGBgCgpQYWNrYWdlIGBwc3ljaGAgY29udGFpbnMgbWFueSBvdGhlciBpbnRlcmVzdGluZyBzdGF0aXN0aWNhbCB0b29scywgZXNwZWNpYWxseSBmb3IgbXVsdGl2YXJpYXRlIGRhdGEgc2V0cyBjb21tb25seSBmb3VuZCBpbiBwc3ljaG9sb2dpY2FsIGFuZCBlY29sb2dpY2FsIHJlc2VhcmNoLiBBc2sgR29vZ2xlIG9yIHlvdXIgbGVjdHVyZXIgZm9yIGRldGFpbHMuCgojIEV4cGxvcmluZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdHdvIHZhcmlhYmxlcwoKVGhlIHByZWNlZGluZyBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGFsbCBsb29rZWQgYXQgZGF0YSBtZWFzdXJlcyAtLSBDaGxBLCBiaWxsIGxlbmd0aCwgYm9keSB3ZWlnaHQsIGV0Yy4gLS0gaW5kaXZpZHVhbGx5LCBzdW1tYXJpc2luZyB0aGVpciBkaXN0cmlidXRpb24sIGNlbnRyYWwgdGVuZGVuY3kgYW5kIHZhcmlhYmlsaXR5LiBPZnRlbiwgaG93ZXZlciwgd2UgYXJlIGludGVyZXN0ZWQgaW4gZGVzY3JpYmluZyAqKnRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBkYXRhIG1lYXN1cmVzKiouIEZvciBleGFtcGxlLCB3ZSBtaWdodCB3YW50IHRvIGtub3cgaWYgaGVhdmllciBwZW5ndWlucyBhbHNvIHRlbmQgdG8gaGF2ZSBsb25nZXIgYmlsbHMuIFRoaXMgdHlwZSBvZiByZWxhdGlvbnNoaXAgaXMgY2FsbGVkIGEgKipjb3JyZWxhdGlvbioqLiBXaGVuIHdlIGhhdmUgbW9yZSB0aGFuIG9uZSBtZWFzdXJlIGZvciBlYWNoIGV4cGVyaW1lbnRhbCBwYXJ0aWNpcGFudCAob3IgZWFjaCBwZW5ndWluLCBvciBlYWNoIGxha2UpIHdlIGNhbiBleHBsb3JlIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIHBhaXJzIG9mIG1lYXN1cmVzIGdyYXBoaWNhbGx5IHdpdGggYSAqKnNjYXR0ZXJwbG90KiouIEEgc2NhdHRlcnBsb3QgaGFzIG9uZSBtZWFzdXJlIG9uIGVhY2ggYXhpcywgYW5kIG9uZSBwb2ludCBmb3IgZWFjaCBwYXJ0aWNpcGFudCdzIHBhaXIgb2Ygc2NvcmVzLgoKSW4gdGhlIGNvZGUgZXhhbXBsZSBiZWxvdyB3ZSBtYWtlIGEgc2NhdHRlcnBsb3Qgd2l0aCBnZ3Bsb3QgKGNmLiBNb2R1bGUgMDIpLCBhbmQgc2hvdyBob3cgdG8gYWRkIGEgKipsaW5lYXIgdHJlbmQgbGluZSoqLiBDb25jZXB0dWFsbHksIHRoaXMgaXMgdGhlIGxpbmUgdGhhdCBydW5zIHRocm91Z2ggdGhlIGNlbnRlciBvZiB0aGUgc2NhdHRlcnBsb3QgcG9pbnRzLCBhbmQgaXQgaGVscHMgdXMgdG8gc2VlIHRoZSBkaXJlY3Rpb24gb2YgdGhlIHJlbGF0aW9uc2hpcC4gTWF0aGVtYXRpY2FsbHksIHRyZW5kIGxpbmVzIGFyZSBhY3R1YWxseSB2ZXJ5IGNvbXBsaWNhdGVkIHRoaW5ncywgYW5kIHdlIGdlbmVyYXRlIHRoZW0gd2l0aCB0aGUgcG93ZXJmdWwgZnVuY3Rpb24gYGxtYCwoZm9yICoqbGluZWFyIG1vZGVsKiopLiBZb3Ugd2lsbCBsZWFybiBhYm91dCB0aGUgbWFueSBmYXNjaW5hdGluZyB0aGluZ3MgeW91IGNhbiBkbyB3aXRoIGxpbmVhciBtb2RlbGluZyBpZiB5b3UgdGFrZSBhZHZhbmNlZCBzdGF0aXN0aWNzIHBhcGVycy4KCiMjIFNjYXR0ZXJwbG90IHdpdGggbGluZWFyIHRyZW5kIGxpbmUKCmBgYHtyIDA0LXNjYXR0ZXJwbG90LCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIGdlb21fcG9pbnQgcGxvdHMgdGhlIHBvaW50cyBvZiB0aGUgc2NhdHRlcnBsb3QgCgojIGdlb21fc21vb3RoIHBsb3RzIHRoZSBsaW5lYXIgdHJlbmQgbGluZSBjb21wdXRlZCB3aXRoIGZ1bmN0aW9uIGxtCgojIFRoZSBzZSBhcmd1bWVudCBkZXRlcm1pbmVzIHdoZXRoZXIgZXJyb3IgYmFycyBhcmUgc2hvd24gCiMgYXJvdW5kIHRoZSB0cmVuZCBsaW5lLgoKZ2dwbG90KGRhdGEgPSBwZW5ndWlucywgbWFwcGluZyA9IGFlcyh4ID0gYm9keV9tYXNzX2csIHkgPSBiaWxsX2xlbmd0aF9tbSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFKQoKYGBgCgpUaGUgc2NhdHRlcnBsb3QgZ2l2ZXMgdXMgYSB2ZXJ5IGNsZWFyIHBpY3R1cmU6IHRob3NlIHBlbmd1aW5zIHdpdGggaGlnaGVyIGJvZHkgd2VpZ2h0cyB0ZW5kIHRvIGFsc28gaGF2ZSBsb25nZXIgYmlsbHMsIGFuZCB0aGlzIGlzIHJlZmxlY3RlZCBpbiB0aGUgcG9zaXRpdmUgc2xvcGUgb2YgdGhlIHRyZW5kbGluZS4gTm90ZSBob3dldmVyIHRoYXQgdGhpcyBpcyBub3QgYW4gYWJzb2x1dGUgcnVsZS4gSXMgaXQgZWFzeSB0byBmaW5kIHBhaXJzIG9mIHBvaW50cyBzdWNoIHRoYXQgdGhlIGxpZ2h0ZXIgb2YgdHdvIHBlbmd1aW5zIGhhcyB0aGUgbG9uZ2VyIGJpbGwuIFRoaXMgaXMgdHlwaWNhbCBvZiBjb3JyZWxhdGlvbmFsIGRhdGEuIAoKVGhlcmUgYXJlIHZhcmlvdXMgc3RhdGlzdGljYWwgbWVhc3VyZXMgdGhhdCBjYXB0dXJlIHRoZSBzdHJlbmd0aCBvZiBhIGNvcnJlbGF0aW9uIChpLmUuIGhvdyBjbG9zZSBpdCBpcyB0byBiZWluZyBhbiBhYnNvbHV0ZSBydWxlKS4gRm9yIGNvbnRpbnVvdXMsIG51bWVyaWNhbCBkYXRhIChzdWNoIGFzIGJpbGwgbGVuZ3RoIGFuZCBib2R5IHdlaWdodCkgdXNlIHRoZSBSIGZ1bmN0aW9uIGBjb3JgIHRvIGNvbXB1dGUgdGhlIG51bWVyaWNhbCBjb3JyZWxhdGlvbiB2YWx1ZS4gQXMgd2l0aCBtZWFucyBhbmQgbWVkaWFucywgd2UgbXVzdCB0ZWxsIGBjb3JgIGhvdyB0byBjb3BlIHdpdGggbWlzc2luZyBkYXRhIChOQSBzY29yZXMpLiBVbmZvcnR1bmF0ZWx5IHRoZSBzeW50YXggaXMgbm90IGNvbnNpc3RlbnQgYWNyb3NzIHRoZSBmdW5jdGlvbnMuIEZvciBmdW5jdGlvbiBgbWVhbmAgd2Ugc2V0IGFyZ3VtZW50IGBuYS5ybSA9IFRSVUVgLiBGb3IgZnVuY3Rpb24gYGNvcmAgd2UgbXVzdCBzZXQgYXJndW1lbnQgYHVzZSA9ICJjb21wbGV0ZS5vYnMiYCwgbWVhbmluZyB0aGF0IHdlIHdhbnQgdGhlIGZ1bmN0aW9uIHRvIHVzZSBvbmx5IHRob3NlIHJvd3MgdGhhdCBhcmUgY29tcGxldGUgKGkuZS4gaGF2ZSBib3RoIHZhbHVlcykuIFRoZXNlIGlkaW9zeW5jcmFjaWVzIG9jY3VyIGZyb20gdGltZSB0byB0aW1lIGluIFI7IHlvdSBqdXN0IGhhdmUgdG8gbGVhcm4gdGhlbS4KCiMjIENvcnJlbGF0aW9uIGNvZWZmaWNpZW50CgpgYGB7ciBjb3J9CiMgUGFzcyB0aGUgdHdvIGRhdGEgY29sdW1ucyBpbnRvIGZ1bmN0aW9uIGNvcgpjb3IocGVuZ3VpbnMkYmlsbF9sZW5ndGhfbW0sIHBlbmd1aW5zJGJvZHlfbWFzc19nLCB1c2U9ImNvbXBsZXRlLm9icyIpCmBgYAoKRnVuY3Rpb24gYGNvcmAgcmV0dXJucyBhIHZhbHVlIGJldHdlZW4gLTEgYW5kIDEuIENvcnJlbGF0aW9ucyB0aGF0IHRyZW5kIGRvd253YXJkIChpLmUuIGlmIG9uZSBzY29yZSBpcyBoaWdoLCB0aGUgb3RoZXIgdGVuZHMgdG8gYmUgbG93KSB3aWxsIGhhdmUgYSBuZWdhdGl2ZSBjb3JyZWxhdGlvbiB2YWx1ZS4gQ29ycmVsYXRpb25zIHRoYXQgdHJlbmQgdXB3YXJkIChpLmUuIGlmIG9uZSBzY29yZSBpcyBoaWdoIHRoZSBvdGhlciBhbHNvIHRlbmRzIHRvIGJlIGhpZ2gpIHdpbGwgaGF2ZSBhIHBvc2l0aXZlIGNvcnJlbGF0aW9uIHZhbHVlLiBUaGUgY2xvc2VyIHRoZSBhYnNvbHV0ZSB2YWx1ZSBvZiBgY29yYCBpcyB0byAxLCB0aGUgc3Ryb25nZXIgdGhlIGNvcnJlbGF0aW9uICh5b3Uga25vdyB3aG8gdG8gdGFsayB0byBmb3IgbW9yZSBkZXRhaWwsIGRvbid0IHlvdT8pLgoKRm9yIGV4YW1wbGUsIGNvbnNpZGVyIHRoZXNlIHR3byBzY2F0dGVycGxvdHM6CgpgYGB7ciAwNC1jb21wYXJpbmcsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoZ3JpZEV4dHJhKQpwbG90MSA8LSBnZ3Bsb3QoZGF0YSA9IHBlbmd1aW5zLCBtYXBwaW5nID0gYWVzKHggPSBib2R5X21hc3NfZywgeSA9IGJpbGxfbGVuZ3RoX21tKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UpCnBsb3QyIDwtIGdncGxvdChkYXRhID0gcGVuZ3VpbnMsIG1hcHBpbmcgPSBhZXMoeCA9IGJvZHlfbWFzc19nLCB5ID0gZmxpcHBlcl9sZW5ndGhfbW0pKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSkKZ3JpZC5hcnJhbmdlKHBsb3QxLCBwbG90MiwgbmNvbD0yKQpgYGAKCmBgYHtyIGNvcl9leGVyY2lzZV9zb2x1dGlvbiwgZWNobz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KY29yKHBlbmd1aW5zJGJvZHlfbWFzc19nLCBwZW5ndWlucyRiaWxsX2xlbmd0aF9tbSwgdXNlPSJjb21wbGV0ZS5vYnMiKQoKY29yKHBlbmd1aW5zJGJvZHlfbWFzc19nLCBwZW5ndWlucyRmbGlwcGVyX2xlbmd0aF9tbSwgdXNlPSJjb21wbGV0ZS5vYnMiKQpgYGAKCiMjIEV4ZXJjaXNlCgpGb3Igb25lIG9mIHRoZSBzY2F0dGVycGxvdHMgYWJvdmUsIHRoZSBjb21wdXRlZCBjb3JyZWxhdGlvbiBzY29yZSBpcyAwLjYwLiBGb3IgdGhlIG90aGVyLCBpdCBpcyAwLjg3LiBGaXJzdCwgcHJlZGljdCB3aGljaCBpcyB3aGljaC4gU2Vjb25kLCB3cml0ZSB0aGUgbmVjZXNzYXJ5IFIgY29kZSB0byBjb25maXJtIHlvdXIgcHJlZGljdGlvbi4K