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

Median

# The familiar pattern....
median(penguins$body_mass_g, na.rm = TRUE)
#> [1] 4050

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