Associated Material
Zoom notes: Zoom Notes 03 -
Selecting and Filtering Data
Reading:
Introduction
When working with a complex data set, researchers rarely summarise or
analyse all the values as a single unit. More commonly, we select out
specific subsets of interest, and analyse those. We saw this pattern in
the Visualisation Module, where we wanted to isolate the data for a
single country (Denmark) from a data frame that had records for 142
different countries.
R has a rich and powerful syntax for selecting subsets from a large
data frame, based on specific conditions (for example, select only those
records where the value in column country
is “Denmark”).
Because this kind of operation is so essential, researchers have also
developed additional R packages that supplement and extend the base R
facilities. Our favourite among these is a library called
dplyr. Library dplyr
is widely used, and
you will see many examples of it in R code you find in the wild.
The functions in dplyr
(and in all the other R libraries
you install) are technically wrappers around base R
code. That is, they are themselves written using base R commands. Thus
it is possible to perform all the same transformations by using
dplyr
, or by using only base R. Many programmers and
researchers (including some of your lecturers) prefer to use base R for
these operations. Therefore, in this module, we will look at both
approaches.
People make the choice between dplyr
and base R for
several reasons. Many people find dplyr
syntax easier to
use, because it is more uniform. That is, all the
dplyr
transformation functions use approximately the same
syntax. In base R, there is more variation. Scientists who work with
very large data sets are often concerned about how fast their code can
be executed. In some cases, dplyr
executes more slowly than
base R (because of the extra code required for the wrapping), leading
these researchers to prefer the base R approach. Because
dplyr
is a relatively new addition to R, some people prefer
base R because they learned it first, and are happy to continue using
it.
Unless you are required to use a particular approach (check with your
lecturer if you are unsure), you can choose whichever set of commands
you like using. You can even mix and match them – they give the same
results, and R doesn’t care. However, it is very important that you can
understand both styles. One of the great benefits of the R
ecosystem is the wide sharing of code, and you can’t fully participate
in this unless you are comfortable with all the major dialects.
Subsetting
The most direct way to select out a subset of a vector or data frame
is using the extraction operator [], which is part of
base R. With this operator we specify which elements we want in one of
three ways:
- By position (e.g. the 8th and 9th elements in a vector)
- By name (e.g. all of column Country)
- By some condition (e.g. all values less than 1)
The square bracket syntax works on both one and two dimensional data
(i.e. vectors or data frames) and we will look at both cases, starting
with vectors.
Subsetting by position
Each item in a vector has a position, starting from 1. Colloquially, this is
called its index. We pass the index as an argument to
[]
to select the specific item.
# A numeric vector
some_numbers <- c(2, 45, -9, 6)
# Pull out the second item
some_numbers[2]
#> [1] 45
To select multiple items, pass a vector of indices into []. The
result is itself a vector of the items at the specified positions.
# A vector of strings
some_words <- c("Ant", "Bear", "Cat", "Dino")
# Pull out items 1 and 3
some_words[c(1,3)]
#> [1] "Ant" "Cat"
Items do not have to be selected in the same order in which they
appear in the original vector.
# Return items in different order
some_words[c(4, 2, 1)]
#> [1] "Dino" "Bear" "Ant"
Items can be duplicated in the result by using an index multiple
times inside [].
# Duplicate items
some_words[c(3,1,4, 4, 4,2)]
#> [1] "Cat" "Ant" "Dino" "Dino" "Dino" "Bear"
Although vector indices start from 1, [] will accept negative values.
A negative values excludes the item at that
position.
# Remove the first item
some_words[-1]
#> [1] "Bear" "Cat" "Dino"
Exercise:
The expression shown above does not remove element Ant from vector
some_words
.
- Confirm this by executing an appropriate R command.
- Modify the expression so that Ant is deleted from vector
some_words
- Consider the possible downside of the command you just wrote.
Subsetting by condition
Commonly, we wish to select elements from a vector (or as we will see
later, rows from a data frame) based on some condition. For example, we
might want to select all items greater than some minimum value, or all
items not equal to some value we wish to exclude. The [] operator offers
a syntactic option that we use in these cases. Rather than passing in
specific item indices, we can pass in a vector of booleans (i.e. TRUE or
FALSE) having the same length as the vector from which we are selecting.
The operation returns all elements for which the corresponding value of
the boolean vector is TRUE.
Here is a toy example to illustrate the pattern:
# A vector of string
some_words <- c("Antelope", "Bear", "Cat", "Dino", "Eagle", "Ferret")
# Passing a boolean vector to []
some_words[c(TRUE, FALSE, FALSE, FALSE, TRUE, TRUE)]
#> [1] "Antelope" "Eagle" "Ferret"
In practice, we don’t really type out all the TRUE and FALSE values.
We let R generate the boolean vector for us, by defining a condition.
For example, imagine that I am interested specifically in words having
more than 4 characters. I can test this condition on my vector of string
as follows:
# Generating a booolean vector
nchar(some_words) > 4
#> [1] TRUE FALSE FALSE FALSE TRUE TRUE
We have seen before that R is a vectorised language,
so applying any operation or expression to a vector returns the result
of applying the operation to each element in turn. The syntax in the
preceding command is consistent with our earlier examples of vectorised
operations – it computes the result of evaluating “nchar > 4” for
each element of some_words
, bundles them all up into a
vector, and returns it.
Note that the boolean vector produced is exactly the same one passed
to [] in the “Passing a boolean vector” code example earlier. Therefore,
we can simply replace the manually typed out boolean vector with the
conditional expression to achieve the same result:
# Using a conditional expression inside []
some_words[nchar(some_words) > 4]
#> [1] "Antelope" "Eagle" "Ferret"
This syntactic structure, where you apply the [] operator to
some_words
and also use some_words
in the
argument you pass to [] is very idiomatic R. Many programming languages
do not allow such things; if you have programmed a lot in more
traditional languages, it will take you a while to get used to this.
Just remember that R always evaluates the input argument first (in this
case, generating a boolean vector) and then passes the result of that
evaluation to []. Many people find it helps to think through those two
steps separately, when trying to figure out what a command like this
produces.
Exercise:
- Write an R command that selects from
some_words
all the
four-character words, but no others.
- Check to see if executing this command has changed the value of
some_words
. Explain why or why not.
- Modify the command so that it does change
some_words
, with that vector now holding only words with
exactly four characters.
- Explain what the command you just wrote illustrates about the order
in which assignment statements are evaluated.
Conditional Operators in R
Less than |
< |
Less than or equal to |
<= |
Greater than |
> |
Greater than or equal to |
>= |
Not equal to |
!= |
Conditional Selection with %in%
We have used ==
to test if one value is exactly equal to
another value. Often, we wish to test if a value is equal to one of a
set of values (i.e. is value A equal to either value B or value C or
value D). In R, this specific complex conditional logic can be performed
with the %in%
operator:
# %in% with a scalar
# Given a vector...
clean_vehicles <- c("bicycle", "scooter", "skateboard")
# Test if a scalar is contained in the vector
"scooter" %in% clean_vehicles
#> [1] TRUE
"jet ski" %in% clean_vehicles
#> [1] FALSE
If we use %in
with a vector as the left operand, R
performs in a vectorised way, as expected, testing each element in
turn:
# Using %in% with a vector
my_vehicles <- c("skateboard", "tractor", "car", "scooter")
my_vehicles %in% clean_vehicles
#> [1] TRUE FALSE FALSE TRUE
And the resulting boolean vector can, of course, be passed to []:
# Select out my clean vehicles
my_vehicles[my_vehicles %in% clean_vehicles]
#> [1] "skateboard" "scooter"
Missing data
One extremely common subsetting task is dealing with missing data.
Missing data arises frequently during real research. For example, an
ecologist studying penguin populations may be measuring the weight of
each penguin in a local colony. If a particular penguin runs away before
it can be weighed, that penguin’s weight is missing. It would be a
mistake to code the penguin’s weight as 0 – this would corrupt any
numerical analysis of weight. Therefore R provides a special data entity
for ‘missing’ or ‘undefined’. Missing data in R is represented by
NA
.
The conditional operators we used above cannot be applied to NA. Any
operation for which one or more operands is NA will return NA (even
NA == NA
will return NA
). To test for NA,
therefore, R provides function is.na
which returns
TRUE
its input is NA
and FALSE
in
all other instances.
# Penguin #3 got away....
penguin_weights_g <- c(3475, 2975, NA, 3420)
# Use is.na to generate a boolean vector
is.na(penguin_weights_g)
#> [1] FALSE FALSE TRUE FALSE
# Use the boolean vector to select everyone
# who is not NA. Note the use of !
penguin_weights_g[!is.na(penguin_weights_g)]
#> [1] 3475 2975 3420
Subsetting in two dimensions
The [] operator is also used for selecting subsets from data frames.
In this usage, one supplies two arguments to [], separated by commas.
The first argument specifies which row(s) to select and the second
specifies which column(s). The general syntax is:
name_of_data_frame[row_information, column_information]. There
are, however, many complex options for the format of the row and column
information. We will cover the most useful here.
# Create a data frame to practice two-dimensional selection
countries <- c("Austria", "Brazil", "Canada", "Denmark")
capitals <- c("Vienna", "Brasilia", "Ottawa", "Copenhagen")
population_in_millions <- c(9, 211, 38, 6)
geography_df <- data.frame(Country = countries,
Capital = capitals,
PopulationMillions = population_in_millions)
geography_df
#> Country Capital PopulationMillions
#> 1 Austria Vienna 9
#> 2 Brazil Brasilia 211
#> 3 Canada Ottawa 38
#> 4 Denmark Copenhagen 6
In the simplest case, we wish to select a single value from a data
frame, so we provide the row index and column index of the cell of
interest. For example, imagine we want the population of Vienna. We know
that Vienna is in row 1 and the population is in column 3. To select
that cell we provide 1 for the row information and 3 for the column
information in the square brackets:
geography_df[1,3]
#> [1] 9
Don’t worry about how you would know the specific row and column of
the cell you are interested in. This particular selection operation is
typically used in situations where your code is computing those values
based on complex criteria. This example is merely illustrative.
There are two very useful extensions to this pattern:
- Either the row or column index (or both) may specify a
range using the
:
operator. For example,
1:3
or 6:12
(these are “1 to 3” and “6 to 12”
respectively).
# For rows 2 to 4 (Brazil, Canada, Denmark), select the population (column 3)
geography_df[2:4, 3]
#> [1] 211 38 6
# For Canada (row 3), select both the capital name and population (cols 2 and 3)
geography_df[3, 2:3]
#> Capital PopulationMillions
#> 3 Ottawa 38
- Either the row or column index may be omitted. That
is, we can say
geography_df[3 , ]
or
geography_df[ , 2]
. The missing element is interpreted as
all. Omit the row number and you want all
rows in the supplied column(s). Omit the column number and you
want all columns in the supplied row(s).
# For Denmark (row 4), select all the columns
geography_df[4, ]
#> Country Capital PopulationMillions
#> 4 Denmark Copenhagen 6
# For all rows, select the capital city name (column 2)
geography_df[ , 2]
#> [1] "Vienna" "Brasilia" "Ottawa" "Copenhagen"
You may have been surprised by the output generated by that last
example. Although you have selected a single column, the output is
printed horizontally, as though it were a row. This is a peculiarity of
R. Any collection that has a single dimension (i.e. doesn’t have both
columns and rows) is treated as a plain vector. And vectors are always
printed horizontally. By extension, since a selected column of a data
frame is a vector, you can apply everything you have learned about
vectors to selected data frame columns, which is exactly what we want to
be able to do.
You can combine ranges and the missing index = all
technique:
# For the first three rows, select all the columns
geography_df[1:3 , ]
#> Country Capital PopulationMillions
#> 1 Austria Vienna 9
#> 2 Brazil Brasilia 211
#> 3 Canada Ottawa 38
Exercise
- What do you think
geography_df[ , ]
(i.e. where both
row and column information are omitted) will do?
- Try it. Were you right?
Instead of using column numbers, you can provide column names as the
column information (and row names as the row information if your data
frame has named rows). Use the combine function c()
to
provide multiple column names, and be sure to surround each column name
with quotes, because R considers them to be strings in this
situation.
geography_df[2:4, "Capital"]
#> [1] "Brasilia" "Ottawa" "Copenhagen"
geography_df[3:4, c("Country", "Capital")]
#> Country Capital
#> 3 Canada Ottawa
#> 4 Denmark Copenhagen
Function subset
An alternative for selecting rows and columns is function
subset
, with structure:
subset(x = dataframe, subset = conditional_statement_to_apply_to_rows, select = columns_to_keep)
countries_over_30million <- subset(geography_df,
subset = PopulationMillions > 30,
select = c("Country", "PopulationMillions") )
countries_over_30million
#> Country PopulationMillions
#> 2 Brazil 211
#> 3 Canada 38
Omitting values for the subset
argument or
select
argument will extract all rows or columns,
respectively.
Selecting columns
Data frames are commonly organised with a column for each data
variable and a row for each instance. To analyse a particular data
variable, we wish to select all rows in a column. This particular
subsetting operation is so common that there are additional, efficient
ways to perform it in R.
To select a single column from a data frame, use the $
operator, followed by the column name (double quotes are not used in
this context).
geography_df$Country
#> [1] "Austria" "Brazil" "Canada" "Denmark"
This operation is commonly used when performing summary operations on
entire columns:
# Mean of a the PopulationMillions column
mean(geography_df$PopulationMillions)
#> [1] 66
The $
operator can also be used to create new
columns. Simply give a name to the new column on the left hand of
an assignment statement, and the contents of the new column on the right
hand side. (NB: This type of operation is not allowed in more
traditional languages, but is very common in R.)
# Create a new column en passant
geography_df$PopulationThousands <- geography_df$PopulationMillions * 1000
# Check that the column has been created
str(geography_df)
#> 'data.frame': 4 obs. of 4 variables:
#> $ Country : chr "Austria" "Brazil" "Canada" "Denmark"
#> $ Capital : chr "Vienna" "Brasilia" "Ottawa" "Copenhagen"
#> $ PopulationMillions : num 9 211 38 6
#> $ PopulationThousands: num 9000 211000 38000 6000
# Print the contents of the new column
geography_df$PopulationThousands
#> [1] 9000 211000 38000 6000
Subsetting with dplyr
We will now switch to a larger data set, and see how the types of
selection operations described above are performed with the
dplyr
library. There are five main functions in
dplyr
: filter, select, mutate (not covered in this
session), group_by, and summarise. We will see how they can be used to
perform fundamental processing of selected portions of data sets.
To use dplyr
, you must first install it on your machine
with install.packages
. Alternatively, if you have installed
package tidyverse
, dplyr
will have been
installed as part of that operation. To load dplyr
for use
during an RStudio session, use the library
command, as we
have done previously.
For this section, we will use data about forest fires in Portugal in
2022. Portugal has a high incidence of forest fire, and these data can
reveal patterns about occurrence and severity.
You can download the data file forest_fires_portugal.csv
from this link:
https://drive.google.com/drive/folders/1UBp-P4wFAaQL3egIQ7dGa2RQe6RQKqgy
or using:
download.file(url = "https://raw.githubusercontent.com/rtis-training/2023-s2-r4ssp/main/data/forest_fires_portugal.csv",
destfile = "data/forest_fires_portugal.csv")
Remember that R is case sensitive so if you don’t have a
directory exactly called data/
modify the command to match
your directory.
The file contains one row for each recorded forest fire in Portugal
in 2022. Each record has a number of measures (columns) including region
(x and y values on a grid), month, day, ground condition measures,
meteorological measures, and the extent of the fire in heactares. Note
that any fire that consumed less than one hectare has a value of 0 in
this last column.
In the code that follows, we will assume that you are working in a
project, that the project folder has a sub-folder called
data and that the .csv file has been placed in that
folder.
# Load dplyr
library(dplyr)
# Import the csv file into a data frame
fires_df <- fires_df <- read.csv("data/forest_fires_portugal.csv")
# Inspect the data
str(fires_df)
#> 'data.frame': 517 obs. of 13 variables:
#> $ XRegion : int 7 7 7 8 8 8 8 8 8 7 ...
#> $ YRegion : int 5 4 4 6 6 6 6 6 6 5 ...
#> $ Month : chr "mar" "oct" "oct" "mar" ...
#> $ Day : chr "fri" "tue" "sat" "fri" ...
#> $ ForestLitterFuel : num 86.2 90.6 90.6 91.7 89.3 92.3 92.3 91.5 91 92.5 ...
#> $ OrganicMaterialFuel: num 26.2 35.4 43.7 33.3 51.3 ...
#> $ DroughtLevel : num 94.3 669.1 686.9 77.5 102.2 ...
#> $ SpreadPotential : num 5.1 6.7 6.7 9 9.6 14.7 8.5 10.7 7 7.1 ...
#> $ Temperature : num 8.2 18 14.6 8.3 11.4 22.2 24.1 8 13.1 22.8 ...
#> $ Humidity : int 51 33 33 97 99 29 27 86 63 40 ...
#> $ WindSpeed : num 6.7 0.9 1.3 4 1.8 5.4 3.1 2.2 5.4 4 ...
#> $ Rainfall : num 0 0 0 0.2 0 0 0 0 0 0 ...
#> $ FireAreaHa : num 0 0 0 0 0 0 0 0 0 0 ...
Selecting columns
To extract individual columns, use function select
,
passing in the dataframe, and the names of the columns you want. There
is no need to format the column names into a vector, or to use double
quotes.
# Select the FireAreaHa column, which shows area damaged by the fire in hectares
select(fires_df, FireAreaHa)
# Select the month and fire area columns, and store them in a new vector
month_area_df <- select(fires_df, Month, FireAreaHa)
# Inspect
month_area_df[350:355,] # You can combine base R [] with dplyr
#> Month FireAreaHa
#> 350 sep 1.64
#> 351 sep 3.71
#> 352 sep 7.31
#> 353 sep 2.03
#> 354 sep 1.72
#> 355 sep 5.97
# For efficiency, you can define a vector of column names to use whenever
# needed. Use double quotes as per usual
selected_cols <- c("Humidity", "FireAreaHa")
humidity_area_df <- select(fires_df, all_of(selected_cols))
# Example analysis using two columns
cor(humidity_area_df)
#> Humidity FireAreaHa
#> Humidity 1.00000000 -0.07551856
#> FireAreaHa -0.07551856 1.00000000
Selecting rows
In computing, the process of selecting rows from a data table based
on particular criteria is called filtering. With
dplyr
we use function filter
, passing the name
of the data frame and the filter condition. The result is a new data
frame that contains all rows from the original for which the filter
condition evaluated to TRUE.
# Extract all fires that occurred in October (i.e. where the Month column contains
# 'oct')
october_fires <- filter(fires_df, Month == 'oct')
# Compute the mean drought level for all fires in october, using base R
# column selection with $
mean(october_fires$DroughtLevel)
#> [1] 681.6733
Exercise
- How would you generate data frame october_fires using [] in base
R?
- Referring to the Visualisation Module, how would you select the
Denmark data using dplyr?
Filtering with complex conditionals
Often we wish to filter on criteria that involve multiple
comparisons. For example, if we wish to extract all the rows for fires
during the winter season (December through February in Portugal) we want
rows for which Month is ‘dec’ or ‘jan’ or ‘feb’. Earlier we saw how to
do this with%in%
. More generally, we can combine
conditional statements using the logical AND and OR operators,
&
and |
respectively.
# Select the winter fires
winter_df <- filter(fires_df, Month == "dec" | Month == "jan" | Month == "feb")
Exercise:
The XRegion and YRegion fields are coordinates in a pre-defined 9 by
9 grid that covers all of Portugal. You want to find all fires that
occurred on the east and west borders of the country – that is, in
XRegions 1, 2, 8, or 9. Filter for these records. Make your expression
as succinct as you can. (You only need one |
.)
x_edge_fires <- filter(fires_df, XRegion <= 2 | XRegion >= 8)
Exercise:
Frequently, we want extract a subset of rows and a subset of
columns from a large data set – that is, we will want to perform both a
select and a filter operation. Using an intermediate variable, write
code to extract month, temperature, and fire area for only the summer
months (June, July, and August). Note that later in today’s session we
will see an alternative “dplyr syntax” for this combined operation, but
it good to master both approaches.
# Exercise Solution (there are many possible solutions)
# Filter to get the rows we want
summer_fires_df <- filter(fires_df, Month %in% c('jun', 'jul','aug'))
# Pass the result of the filtering operation to select
summer_temperature_area_df <- select(summer_fires_df, Month, Temperature, FireAreaHa)
Sorting
Sorting is an extremely common operation on large data sets, as it
makes it easier to inspect our data efficiently. To sort with
dplyr
, use function arrange
. Pass in the name
of the data frame, and the columns on which you wish to sort, from
slowest to fastest moving.
# Sort all records in order of area consumed (column FireAreaHa)
sort_by_area_df <- arrange(fires_df, FireAreaHa)
# To sort in descending order, apply function desc to the column
sort_by_area_df <- arrange(fires_df, desc(FireAreaHa))
# Sorting on multiple columns
sort_by_month_area_df <- arrange(fires_df, Month, desc(FireAreaHa))
You will note that this last result is sorted by month
alphabetically. This makes sense to R, but we probably would
prefer to have it sorted logically. To ensure this, we can cast
column Month from type “chr” (just strings) to type “Ordered Factor”, an
ordered categorical variable. The code for this operation is shown
below. We will discuss the management of categorical vs. numerical
variables in more depth in the Summarising Module, later in the
semester.
# Define the logical order of our month codes
months_in_order = c("jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep",
"oct", "nov", "dec")
# Modify the Month column so it uses that ordering
fires_df$Month <- ordered(fires_df$Month, levels = months_in_order)
# Repeat the sort operation to confirm
sort_by_month_area_df <- arrange(fires_df, Month, desc(FireAreaHa))
# Check
head(sort_by_month_area_df)
#> XRegion YRegion Month Day ForestLitterFuel OrganicMaterialFuel DroughtLevel
#> 1 2 4 jan sat 82.1 3.7 9.3
#> 2 4 5 jan sun 18.7 1.1 171.4
#> 3 4 5 feb sun 85.0 9.0 56.9
#> 4 5 4 feb fri 85.2 4.9 15.8
#> 5 7 4 feb sun 83.9 8.7 32.1
#> 6 7 4 feb mon 84.7 9.5 58.3
#> SpreadPotential Temperature Humidity WindSpeed Rainfall FireAreaHa
#> 1 2.9 5.3 78 3.1 0 0.00
#> 2 0.0 5.2 100 0.9 0 0.00
#> 3 3.5 10.1 62 1.8 0 51.78
#> 4 6.3 7.5 46 8.0 0 24.24
#> 5 2.1 8.8 68 2.2 0 13.05
#> 6 4.1 7.5 71 6.3 0 9.96
Summarising by group
We typically extract subsets from a data frame to serve as input to
methods that summarise, perform statistical analyses, or produce graphs
of those data. Library dplyr
provides a pair of methods –
group_by
and summarise
– which, when used
together, make this workflow especially succinct for simple
post-selection processing.
Assume that you would like to know the mean fire area consumed for
each month. You will first apply group_by
to
fires_df
to define your grouping variable as column
Month
. This has no major visible impact on
fires_df
. However, any subsequent summaries you perform on
that data frame with function summarise
are applied
by group. For example, if you use summarise
to
compute the mean of FireAreaHa (syntax shown below), it computes that
mean for each month separately. If you use summarise
to
compute the standard deviation of Temperature, it computes that standard
deviation for each month separately. And so on.
# To compare area consumed by month
# Apply group_by. In View the data frame looks unchanged
fires_gp_month_df <- group_by(fires_df, Month)
# When printed to console, the data frame also looks the same except for Groups
# header
fires_gp_month_df
#> # A tibble: 517 × 13
#> # Groups: Month [12]
#> XRegion YRegion Month Day ForestLitterFuel OrganicMaterialFuel DroughtLevel
#> <int> <int> <ord> <chr> <dbl> <dbl> <dbl>
#> 1 7 5 mar fri 86.2 26.2 94.3
#> 2 7 4 oct tue 90.6 35.4 669.
#> 3 7 4 oct sat 90.6 43.7 687.
#> 4 8 6 mar fri 91.7 33.3 77.5
#> 5 8 6 mar sun 89.3 51.3 102.
#> 6 8 6 aug sun 92.3 85.3 488
#> 7 8 6 aug mon 92.3 88.9 496.
#> 8 8 6 aug mon 91.5 145. 608.
#> 9 8 6 sep tue 91 130. 693.
#> 10 7 5 sep sat 92.5 88 699.
#> # ℹ 507 more rows
#> # ℹ 6 more variables: SpreadPotential <dbl>, Temperature <dbl>, Humidity <int>,
#> # WindSpeed <dbl>, Rainfall <dbl>, FireAreaHa <dbl>
The magic happens when we take a grouped data frame and pass it to
summarise
. Function summarise
returns a new
data frame with a row for each level of the grouping variable, and a
column for each summary function we specify.
# T second arg to summarise is "col header = expression". Expression is applied by group
summarise(fires_gp_month_df, MeanAreaLn = mean(FireAreaHa, na.rm = TRUE))
#> # A tibble: 12 × 2
#> Month MeanAreaLn
#> <ord> <dbl>
#> 1 jan 0
#> 2 feb 6.28
#> 3 mar 4.36
#> 4 apr 8.89
#> 5 may 19.2
#> 6 jun 5.84
#> 7 jul 14.4
#> 8 aug 12.5
#> 9 sep 17.9
#> 10 oct 6.64
#> 11 nov 0
#> 12 dec 13.3
Note that you can provide multiple expressions to
summarise
. It produces a column for each.
You can also provide multiple column names to group_by
.
Function summarise
is then applied to the groups defined by
each combination of values on the grouping variables.
# Grouping on two variables
fires_month_day <- group_by(fires_df, Month, Day)
summarise(fires_month_day, MeanAreaForMonthAndDayOfWeek = mean(FireAreaHa, na.rm = TRUE))
#> `summarise()` has grouped output by 'Month'. You can override using the
#> `.groups` argument.
#> # A tibble: 64 × 3
#> # Groups: Month [12]
#> Month Day MeanAreaForMonthAndDayOfWeek
#> <ord> <chr> <dbl>
#> 1 jan sat 0
#> 2 jan sun 0
#> 3 feb fri 5.77
#> 4 feb mon 3.32
#> 5 feb sat 1.71
#> 6 feb sun 17.8
#> 7 feb thu 0
#> 8 feb tue 3.76
#> 9 feb wed 1.1
#> 10 mar fri 0.985
#> # ℹ 54 more rows
Exercise
- Use ‘group_by’ and ‘summarise’ to find the largest fire
(i.e. maximum area burnt) in each month.
- A useful worker function in library
dplyr
is
n()
. This function, when used in summarise
,
counts the number of records in each group (it takes no column header;
it just counts the number of rows in the group). Generate a table that
shows the number of fires that occurred on each day of the week. How
would you explain the pattern in these results?
# Exercise Solution
fires_gp_day_df <- group_by(fires_df, Day)
summarise(fires_gp_day_df, NFires = n())
#> # A tibble: 7 × 2
#> Day NFires
#> <chr> <int>
#> 1 fri 85
#> 2 mon 74
#> 3 sat 84
#> 4 sun 95
#> 5 thu 61
#> 6 tue 64
#> 7 wed 54
- Modify
fires_df
so that summarise
outputs
the days in their correct logical order (e.g. Wednesday before
Thursday).
Pipes
When you install dplyr (either alone or as part of meta-package
tidyverse), you also get library magrittr
, which contains a
special operator called the pipe. (NB: The character |, used
for OR, is also called the pipe. Just one of those things.)
With the pipe, we can perform a series of operations, for example
filter and select, without intermediate variables. We think of starting
with our complete data frame and pushing it through a series, or
pipeline of operations. At each step, the output from
one operation is passed along as the input of the next.
When using the pipe, we no longer have to provide the name of data
frame as the first argument to all the dplyr functions – they always
take the output of the previous step in the pipe.
Previously, we combined filter and select by using an intermediate
variable, like this:
# Filter to get the rows we want
summer_fires_df <- filter(fires_df, Month %in% c('jun', 'jul','aug'))
# Pass the result of the filtering operation to select
summer_temperature_area_df <- select(summer_fires_df, Month, Temperature, FireAreaHa)
Using pipe syntax, the same result would be obtained with:
summer_temperature_area_df <- fires_df %>% filter(Month %in% c("jun", "jul", "aug")) %>% select(Month, Temperature, FireAreaHa)
head(summer_temperature_area_df)
#> Month Temperature FireAreaHa
#> 1 aug 22.2 0
#> 2 aug 24.1 0
#> 3 aug 8.0 0
#> 4 aug 17.0 0
#> 5 jun 21.0 0
#> 6 aug 19.5 0
Like the + symbol in ggplot, %>%
signals to RStudio
that more code is coming, allowing you to make the code readable by
placing each operation in the pipeline on a new line:
summer_temperature_area_df <- fires_df %>%
filter(Month %in% c("jun", "jul", "aug")) %>%
select(Month, Temperature, FireAreaHa)
head(summer_temperature_area_df)
#> Month Temperature FireAreaHa
#> 1 aug 22.2 0
#> 2 aug 24.1 0
#> 3 aug 8.0 0
#> 4 aug 17.0 0
#> 5 jun 21.0 0
#> 6 aug 19.5 0
Exercise
- Using group_by, summarise, and pipe notation, compute the mean
temperature for each month
# Exercise solution
fires_df %>% group_by(Month) %>% summarise(MeanTemperature = mean(Temperature, na.rm = TRUE))
#> # A tibble: 12 × 2
#> Month MeanTemperature
#> <ord> <dbl>
#> 1 jan 5.25
#> 2 feb 9.64
#> 3 mar 13.1
#> 4 apr 12.0
#> 5 may 14.6
#> 6 jun 20.5
#> 7 jul 22.1
#> 8 aug 21.6
#> 9 sep 19.6
#> 10 oct 17.1
#> 11 nov 11.8
#> 12 dec 4.52
- You can use ggplot at the end of a pipeline. Write a single command
to make a boxplot that illustrates the distribution of drought level
during each of the summer months in Portugal. Plan: First filter out the
months you want, then make a boxplot with x = Month and y =
DroughtLevel
library(ggplot2)
fires_df %>% filter(Month %in% c("jun", "jul", "aug")) %>%
ggplot(mapping = aes(x = Month, y = DroughtLevel)) +
geom_boxplot()
- Write a single command that produces a bar graph of the mean
temperature for each month. (Plan: The pipeline starts with
group_by
and summarise
to compute the means,
then pours that output into ggplot. Make the graph using
geom_bar
. Set the x axis to Month and the y axis to the
column name you assign for the mean temperature in
summarise
. Pass into geom_bar the argument
stat = "identity"
so it plots the data values as provided.
Note the double quotes around the identity; they are
required.)
# Exercise solution
library(ggplot2)
fires_df %>%
group_by(Month) %>%
summarise(MeanTemperature = mean(Temperature, na.rm = TRUE)) %>%
ggplot(mapping = aes(x = Month, y = MeanTemperature)) + geom_bar(stat = "identity")
A Word of Caution
Dplyr pipeline syntax is popular because it eliminates the need for
intermediate variables and, therefore, requires a lot less typing. It is
common, when first encountering this option, to get slightly carried
away, and chain together dozens of operations, reveling in how many
keystrokes you are saving. This is risky, for several reasons:
- If a long pipeline fails, you will not know in which operation
the error occurred. This can make it extremely difficult to
debug.
- Because you are not saving any of the intermediate outputs, you do
not have them available for different processing in other contexts – you
will have to repeat the whole portion of the pipeline which generated
them.
- A long pipeline will be difficult for other people to “read”
(i.e. to figure out what it’s doing). When you have multiple options for
performing a computation, the goal is to strike a balance between
parsimony (not too much typing) and
readability (your code is easy for other
people to understand). When working on group projects, or in a
professional software development context, readability is considered the
more critical of the two features. My personal style is never to build
pipelines of more than three or four operations. The outputs of these
short sequences can be saved into intermediate variables which can be
checked and reused, which are then passed along into the next
pipeline.
Another Word of Caution
R users are constantly adding new libraries to base R, meaning that
you will probably have several options for doing any job in R. The
various options sometimes have subtle technical differences that will
generate a lot of argument between professional programmers, but are
unlikely to matter much to research scientists. In general, you should
explore the R ecosystem freely and use whatever you like.
However on assignments, it is wise to check with the
lecturer before using something that is really different from what is
presented in class. Your lecturer may, for educational reasons, want you
to use specific R tools.
What’s Next
You may recall that way back in the first module of R4SSP we said we
were going to analyse data. We haven’t really done much of that yet. So
far we have been getting ready to analyse data. In the next
module, we will start really digging into our data with exploratory
analysis and descriptive statistics. Because this is not a statistics
course per se we will only be using common general analyses in
the handouts and readings. If, for a project or assignment, you need to
do something more esoteric, just let us know – someone has probably
written an R library for it.
LS0tCnRpdGxlOiAiU3Vic2V0dGluZyIKZGF0ZTogIlNlbWVzdGVyIDIsIDIwMjMiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19kZXB0aDogMwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93Ci0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkoa25pdHIpCgprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgY29tbWVudCA9ICIjPiIsCiAgZmlnLnBhdGggPSAiZmlndXJlcy8wMy8iLCAjIHVzZSBvbmx5IGZvciBzaW5nbGUgUm1kIGZpbGVzCiAgY29sbGFwc2UgPSBUUlVFLAogIGVjaG8gPSBUUlVFCikKYGBgCgoKCj4gIyMjIyBBc3NvY2lhdGVkIE1hdGVyaWFsCj4KPiBab29tIG5vdGVzOiBbWm9vbSBOb3RlcyAwMyAtIFNlbGVjdGluZyBhbmQgRmlsdGVyaW5nIERhdGFdKHpvb21fbm90ZXNfMDNfdmlzdWFsaXNlLmh0bWwpCj4KPiBSZWFkaW5nOgo+Cj4gLSBbUiBmb3IgRGF0YSBTY2llbmNlIC0gQ2hhcHRlciA1XShodHRwczovL3I0ZHMuaGFkLmNvLm56L3RyYW5zZm9ybS5odG1sKQoKXAoKIyMgSW50cm9kdWN0aW9uCgpXaGVuIHdvcmtpbmcgd2l0aCBhIGNvbXBsZXggZGF0YSBzZXQsIHJlc2VhcmNoZXJzIHJhcmVseSBzdW1tYXJpc2Ugb3IgYW5hbHlzZSBhbGwgdGhlIHZhbHVlcyBhcyBhIHNpbmdsZSB1bml0LiBNb3JlIGNvbW1vbmx5LCB3ZSBzZWxlY3Qgb3V0IHNwZWNpZmljIHN1YnNldHMgb2YgaW50ZXJlc3QsIGFuZCBhbmFseXNlIHRob3NlLiBXZSBzYXcgdGhpcyBwYXR0ZXJuIGluIHRoZSBWaXN1YWxpc2F0aW9uIE1vZHVsZSwgd2hlcmUgd2Ugd2FudGVkIHRvIGlzb2xhdGUgdGhlIGRhdGEgZm9yIGEgc2luZ2xlIGNvdW50cnkgKERlbm1hcmspIGZyb20gYSBkYXRhIGZyYW1lIHRoYXQgaGFkIHJlY29yZHMgZm9yIDE0MiBkaWZmZXJlbnQgY291bnRyaWVzLgoKUiBoYXMgYSByaWNoIGFuZCBwb3dlcmZ1bCBzeW50YXggZm9yIHNlbGVjdGluZyBzdWJzZXRzIGZyb20gYSBsYXJnZSBkYXRhIGZyYW1lLCBiYXNlZCBvbiBzcGVjaWZpYyBjb25kaXRpb25zIChmb3IgZXhhbXBsZSwgc2VsZWN0IG9ubHkgdGhvc2UgcmVjb3JkcyB3aGVyZSB0aGUgdmFsdWUgaW4gY29sdW1uIGBjb3VudHJ5YCBpcyAiRGVubWFyayIpLiBCZWNhdXNlIHRoaXMga2luZCBvZiBvcGVyYXRpb24gaXMgc28gZXNzZW50aWFsLCByZXNlYXJjaGVycyBoYXZlIGFsc28gZGV2ZWxvcGVkIGFkZGl0aW9uYWwgUiBwYWNrYWdlcyB0aGF0IHN1cHBsZW1lbnQgYW5kIGV4dGVuZCB0aGUgYmFzZSBSIGZhY2lsaXRpZXMuIE91ciBmYXZvdXJpdGUgYW1vbmcgdGhlc2UgaXMgYSBsaWJyYXJ5IGNhbGxlZCAqKmRwbHlyKiouICBMaWJyYXJ5IGBkcGx5cmAgaXMgd2lkZWx5IHVzZWQsIGFuZCB5b3Ugd2lsbCBzZWUgbWFueSBleGFtcGxlcyBvZiBpdCBpbiBSIGNvZGUgeW91IGZpbmQgaW4gdGhlIHdpbGQuCgpUaGUgZnVuY3Rpb25zIGluIGBkcGx5cmAgKGFuZCBpbiBhbGwgdGhlIG90aGVyIFIgbGlicmFyaWVzIHlvdSBpbnN0YWxsKSBhcmUgdGVjaG5pY2FsbHkgKip3cmFwcGVycyoqIGFyb3VuZCBiYXNlIFIgY29kZS4gVGhhdCBpcywgdGhleSBhcmUgdGhlbXNlbHZlcyB3cml0dGVuIHVzaW5nIGJhc2UgUiBjb21tYW5kcy4gVGh1cyBpdCBpcyBwb3NzaWJsZSB0byBwZXJmb3JtIGFsbCB0aGUgc2FtZSB0cmFuc2Zvcm1hdGlvbnMgYnkgdXNpbmcgYGRwbHlyYCwgb3IgYnkgdXNpbmcgb25seSBiYXNlIFIuIE1hbnkgcHJvZ3JhbW1lcnMgYW5kIHJlc2VhcmNoZXJzIChpbmNsdWRpbmcgc29tZSBvZiB5b3VyIGxlY3R1cmVycykgcHJlZmVyIHRvIHVzZSBiYXNlIFIgZm9yIHRoZXNlIG9wZXJhdGlvbnMuIFRoZXJlZm9yZSwgaW4gdGhpcyBtb2R1bGUsIHdlIHdpbGwgbG9vayBhdCBib3RoIGFwcHJvYWNoZXMuCgpQZW9wbGUgbWFrZSB0aGUgY2hvaWNlIGJldHdlZW4gYGRwbHlyYCBhbmQgYmFzZSBSIGZvciBzZXZlcmFsIHJlYXNvbnMuIE1hbnkgcGVvcGxlIGZpbmQgYGRwbHlyYCBzeW50YXggZWFzaWVyIHRvIHVzZSwgYmVjYXVzZSBpdCBpcyBtb3JlICoqdW5pZm9ybSoqLiBUaGF0IGlzLCBhbGwgdGhlIGBkcGx5cmAgdHJhbnNmb3JtYXRpb24gZnVuY3Rpb25zIHVzZSBhcHByb3hpbWF0ZWx5IHRoZSBzYW1lIHN5bnRheC4gSW4gYmFzZSBSLCB0aGVyZSBpcyBtb3JlIHZhcmlhdGlvbi4gU2NpZW50aXN0cyB3aG8gd29yayB3aXRoIHZlcnkgbGFyZ2UgZGF0YSBzZXRzIGFyZSBvZnRlbiBjb25jZXJuZWQgYWJvdXQgaG93IGZhc3QgdGhlaXIgY29kZSBjYW4gYmUgZXhlY3V0ZWQuIEluIHNvbWUgY2FzZXMsIGBkcGx5cmAgZXhlY3V0ZXMgbW9yZSBzbG93bHkgdGhhbiBiYXNlIFIgKGJlY2F1c2Ugb2YgdGhlIGV4dHJhIGNvZGUgcmVxdWlyZWQgZm9yIHRoZSB3cmFwcGluZyksIGxlYWRpbmcgdGhlc2UgcmVzZWFyY2hlcnMgdG8gcHJlZmVyIHRoZSBiYXNlIFIgYXBwcm9hY2guIEJlY2F1c2UgYGRwbHlyYCBpcyBhIHJlbGF0aXZlbHkgbmV3IGFkZGl0aW9uIHRvIFIsIHNvbWUgcGVvcGxlIHByZWZlciBiYXNlIFIgYmVjYXVzZSB0aGV5IGxlYXJuZWQgaXQgZmlyc3QsIGFuZCBhcmUgaGFwcHkgdG8gY29udGludWUgdXNpbmcgaXQuCgpVbmxlc3MgeW91IGFyZSByZXF1aXJlZCB0byB1c2UgYSBwYXJ0aWN1bGFyIGFwcHJvYWNoIChjaGVjayB3aXRoIHlvdXIgbGVjdHVyZXIgaWYgeW91IGFyZSB1bnN1cmUpLCB5b3UgY2FuIGNob29zZSB3aGljaGV2ZXIgc2V0IG9mIGNvbW1hbmRzIHlvdSBsaWtlIHVzaW5nLiBZb3UgY2FuIGV2ZW4gbWl4IGFuZCBtYXRjaCB0aGVtIC0tIHRoZXkgZ2l2ZSB0aGUgc2FtZSByZXN1bHRzLCBhbmQgUiBkb2Vzbid0IGNhcmUuIEhvd2V2ZXIsIGl0IGlzIHZlcnkgaW1wb3J0YW50IHRoYXQgeW91IGNhbiAqdW5kZXJzdGFuZCogYm90aCBzdHlsZXMuIE9uZSBvZiB0aGUgZ3JlYXQgYmVuZWZpdHMgb2YgdGhlIFIgZWNvc3lzdGVtIGlzIHRoZSB3aWRlIHNoYXJpbmcgb2YgY29kZSwgYW5kIHlvdSBjYW4ndCBmdWxseSBwYXJ0aWNpcGF0ZSBpbiB0aGlzIHVubGVzcyB5b3UgYXJlIGNvbWZvcnRhYmxlIHdpdGggYWxsIHRoZSBtYWpvciBkaWFsZWN0cy4KClwKCiMjIFN1YnNldHRpbmcKClRoZSBtb3N0IGRpcmVjdCB3YXkgdG8gc2VsZWN0IG91dCBhIHN1YnNldCBvZiBhIHZlY3RvciBvciBkYXRhIGZyYW1lIGlzIHVzaW5nIHRoZSAqKmV4dHJhY3Rpb24gb3BlcmF0b3IqKiBbXSwgd2hpY2ggaXMgcGFydCBvZiBiYXNlIFIuIFdpdGggdGhpcyBvcGVyYXRvciB3ZSBzcGVjaWZ5IHdoaWNoIGVsZW1lbnRzIHdlIHdhbnQgaW4gb25lIG9mIHRocmVlIHdheXM6CgotIEJ5IHBvc2l0aW9uIChlLmcuIHRoZSA4dGggYW5kIDl0aCBlbGVtZW50cyBpbiBhIHZlY3RvcikKLSBCeSBuYW1lIChlLmcuIGFsbCBvZiBjb2x1bW4gQ291bnRyeSkKLSBCeSBzb21lIGNvbmRpdGlvbiAoZS5nLiBhbGwgdmFsdWVzIGxlc3MgdGhhbiAxKQoKVGhlIHNxdWFyZSBicmFja2V0IHN5bnRheCB3b3JrcyBvbiBib3RoIG9uZSBhbmQgdHdvIGRpbWVuc2lvbmFsIGRhdGEgKGkuZS4gdmVjdG9ycyBvciBkYXRhIGZyYW1lcykgYW5kIHdlIHdpbGwgbG9vayBhdCBib3RoIGNhc2VzLCBzdGFydGluZyB3aXRoIHZlY3RvcnMuCgojIyMgU3Vic2V0dGluZyBieSBwb3NpdGlvbgoKRWFjaCBpdGVtIGluIGEgdmVjdG9yIGhhcyBhIHBvc2l0aW9uLCBzdGFydGluZyBmcm9tIDEuXltOQjogTW9zdCBwcm9ncmFtbWluZyBsYW5ndWFnZXMgc3RhcnQgZnJvbSAwLiBJZiB5b3UgYXJlIGFuIGV4cGVyaWVuY2VkIEphdmEsIEMsIGV0Yy4sIHByb2dyYW1tZXIsIGJlIGFsZXJ0IGZvciB0aGlzLl0gQ29sbG9xdWlhbGx5LCB0aGlzIGlzIGNhbGxlZCBpdHMgKippbmRleCoqLiBXZSBwYXNzIHRoZSBpbmRleCBhcyBhbiBhcmd1bWVudCB0byBgW11gIHRvIHNlbGVjdCB0aGUgc3BlY2lmaWMgaXRlbS4gCmBgYHtyfQojIEEgbnVtZXJpYyB2ZWN0b3IKc29tZV9udW1iZXJzIDwtIGMoMiwgNDUsIC05LCA2KQoKIyBQdWxsIG91dCB0aGUgc2Vjb25kIGl0ZW0Kc29tZV9udW1iZXJzWzJdCmBgYAoKVG8gc2VsZWN0IG11bHRpcGxlIGl0ZW1zLCBwYXNzIGEgdmVjdG9yIG9mIGluZGljZXMgaW50byBbXS4gVGhlIHJlc3VsdCBpcyBpdHNlbGYgYSB2ZWN0b3Igb2YgdGhlIGl0ZW1zIGF0IHRoZSBzcGVjaWZpZWQgcG9zaXRpb25zLiAKCgpgYGB7cn0KIyBBIHZlY3RvciBvZiBzdHJpbmdzCnNvbWVfd29yZHMgPC0gYygiQW50IiwgIkJlYXIiLCAiQ2F0IiwgIkRpbm8iKQoKIyBQdWxsIG91dCBpdGVtcyAxIGFuZCAzCnNvbWVfd29yZHNbYygxLDMpXQpgYGAKSXRlbXMgZG8gbm90IGhhdmUgdG8gYmUgc2VsZWN0ZWQgaW4gdGhlIHNhbWUgb3JkZXIgaW4gd2hpY2ggdGhleSBhcHBlYXIgaW4gdGhlIG9yaWdpbmFsIHZlY3Rvci4KCmBgYHtyfQojIFJldHVybiBpdGVtcyBpbiBkaWZmZXJlbnQgb3JkZXIKc29tZV93b3Jkc1tjKDQsIDIsIDEpXQpgYGAKSXRlbXMgY2FuIGJlIGR1cGxpY2F0ZWQgaW4gdGhlIHJlc3VsdCBieSB1c2luZyBhbiBpbmRleCBtdWx0aXBsZSB0aW1lcyBpbnNpZGUgW10uCgoKYGBge3J9CiMgRHVwbGljYXRlIGl0ZW1zCnNvbWVfd29yZHNbYygzLDEsNCwgNCwgNCwyKV0KYGBgCgpBbHRob3VnaCB2ZWN0b3IgaW5kaWNlcyBzdGFydCBmcm9tIDEsIFtdIHdpbGwgYWNjZXB0IG5lZ2F0aXZlIHZhbHVlcy4gQSBuZWdhdGl2ZSB2YWx1ZXMgKipleGNsdWRlcyoqIHRoZSBpdGVtIGF0IHRoYXQgcG9zaXRpb24uCgpgYGAge3J9CiMgUmVtb3ZlIHRoZSBmaXJzdCBpdGVtCnNvbWVfd29yZHNbLTFdCmBgYAoKIyMjIEV4ZXJjaXNlOgoKVGhlIGV4cHJlc3Npb24gc2hvd24gYWJvdmUgZG9lcyBub3QgcmVtb3ZlIGVsZW1lbnQgQW50IGZyb20gdmVjdG9yIGBzb21lX3dvcmRzYC4gCgoxLiBDb25maXJtIHRoaXMgYnkgZXhlY3V0aW5nIGFuIGFwcHJvcHJpYXRlIFIgY29tbWFuZC4KMi4gTW9kaWZ5IHRoZSBleHByZXNzaW9uIHNvIHRoYXQgQW50IGlzIGRlbGV0ZWQgZnJvbSB2ZWN0b3IgYHNvbWVfd29yZHNgCjMuIENvbnNpZGVyIHRoZSBwb3NzaWJsZSBkb3duc2lkZSBvZiB0aGUgY29tbWFuZCB5b3UganVzdCB3cm90ZS4KCiMjIyBTdWJzZXR0aW5nIGJ5IGNvbmRpdGlvbgoKQ29tbW9ubHksIHdlIHdpc2ggdG8gc2VsZWN0IGVsZW1lbnRzIGZyb20gYSB2ZWN0b3IgKG9yIGFzIHdlIHdpbGwgc2VlIGxhdGVyLCByb3dzIGZyb20gYSBkYXRhIGZyYW1lKSBiYXNlZCBvbiBzb21lIGNvbmRpdGlvbi4gRm9yIGV4YW1wbGUsIHdlIG1pZ2h0IHdhbnQgdG8gc2VsZWN0IGFsbCBpdGVtcyBncmVhdGVyIHRoYW4gc29tZSBtaW5pbXVtIHZhbHVlLCBvciBhbGwgaXRlbXMgbm90IGVxdWFsIHRvIHNvbWUgdmFsdWUgd2Ugd2lzaCB0byBleGNsdWRlLiBUaGUgW10gb3BlcmF0b3Igb2ZmZXJzIGEgc3ludGFjdGljIG9wdGlvbiB0aGF0IHdlIHVzZSBpbiB0aGVzZSBjYXNlcy4gUmF0aGVyIHRoYW4gcGFzc2luZyBpbiBzcGVjaWZpYyBpdGVtIGluZGljZXMsIHdlIGNhbiBwYXNzIGluIGEgdmVjdG9yIG9mIGJvb2xlYW5zIChpLmUuIFRSVUUgb3IgRkFMU0UpIGhhdmluZyB0aGUgc2FtZSBsZW5ndGggYXMgdGhlIHZlY3RvciBmcm9tIHdoaWNoIHdlIGFyZSBzZWxlY3RpbmcuIFRoZSBvcGVyYXRpb24gcmV0dXJucyBhbGwgZWxlbWVudHMgZm9yIHdoaWNoIHRoZSBjb3JyZXNwb25kaW5nIHZhbHVlIG9mIHRoZSBib29sZWFuIHZlY3RvciBpcyBUUlVFLiAKCkhlcmUgaXMgYSB0b3kgZXhhbXBsZSB0byBpbGx1c3RyYXRlIHRoZSBwYXR0ZXJuOgoKYGBge3J9CiMgQSB2ZWN0b3Igb2Ygc3RyaW5nCnNvbWVfd29yZHMgPC0gYygiQW50ZWxvcGUiLCAiQmVhciIsICJDYXQiLCAiRGlubyIsICJFYWdsZSIsICJGZXJyZXQiKQoKIyBQYXNzaW5nIGEgYm9vbGVhbiB2ZWN0b3IgdG8gW10Kc29tZV93b3Jkc1tjKFRSVUUsIEZBTFNFLCBGQUxTRSwgRkFMU0UsIFRSVUUsIFRSVUUpXQpgYGAKSW4gcHJhY3RpY2UsIHdlIGRvbid0IHJlYWxseSB0eXBlIG91dCBhbGwgdGhlIFRSVUUgYW5kIEZBTFNFIHZhbHVlcy4gV2UgbGV0IFIgZ2VuZXJhdGUgdGhlIGJvb2xlYW4gdmVjdG9yIGZvciB1cywgYnkgZGVmaW5pbmcgYSBjb25kaXRpb24uIEZvciBleGFtcGxlLCBpbWFnaW5lIHRoYXQgSSBhbSBpbnRlcmVzdGVkIHNwZWNpZmljYWxseSBpbiB3b3JkcyBoYXZpbmcgbW9yZSB0aGFuIDQgY2hhcmFjdGVycy4gSSBjYW4gdGVzdCB0aGlzIGNvbmRpdGlvbiBvbiBteSB2ZWN0b3Igb2Ygc3RyaW5nIGFzIGZvbGxvd3M6CgpgYGAge3J9CiMgR2VuZXJhdGluZyBhIGJvb29sZWFuIHZlY3RvcgpuY2hhcihzb21lX3dvcmRzKSA+IDQKYGBgCgpXZSBoYXZlIHNlZW4gYmVmb3JlIHRoYXQgUiBpcyBhICoqdmVjdG9yaXNlZCoqIGxhbmd1YWdlLCBzbyBhcHBseWluZyBhbnkgb3BlcmF0aW9uIG9yIGV4cHJlc3Npb24gdG8gYSB2ZWN0b3IgcmV0dXJucyB0aGUgcmVzdWx0IG9mIGFwcGx5aW5nIHRoZSBvcGVyYXRpb24gdG8gZWFjaCBlbGVtZW50IGluIHR1cm4uIFRoZSBzeW50YXggaW4gdGhlIHByZWNlZGluZyBjb21tYW5kIGlzIGNvbnNpc3RlbnQgd2l0aCBvdXIgZWFybGllciBleGFtcGxlcyBvZiB2ZWN0b3Jpc2VkIG9wZXJhdGlvbnMgLS0gaXQgY29tcHV0ZXMgdGhlIHJlc3VsdCBvZiBldmFsdWF0aW5nICJuY2hhciA+IDQiIGZvciBlYWNoIGVsZW1lbnQgb2YgYHNvbWVfd29yZHNgLCBidW5kbGVzIHRoZW0gYWxsIHVwIGludG8gYSB2ZWN0b3IsIGFuZCByZXR1cm5zIGl0LgoKTm90ZSB0aGF0IHRoZSBib29sZWFuIHZlY3RvciBwcm9kdWNlZCBpcyBleGFjdGx5IHRoZSBzYW1lIG9uZSBwYXNzZWQgdG8gW10gaW4gdGhlICJQYXNzaW5nIGEgYm9vbGVhbiB2ZWN0b3IiIGNvZGUgZXhhbXBsZSBlYXJsaWVyLiBUaGVyZWZvcmUsIHdlIGNhbiBzaW1wbHkgcmVwbGFjZSB0aGUgbWFudWFsbHkgdHlwZWQgb3V0IGJvb2xlYW4gdmVjdG9yIHdpdGggdGhlIGNvbmRpdGlvbmFsIGV4cHJlc3Npb24gdG8gYWNoaWV2ZSB0aGUgc2FtZSByZXN1bHQ6CgpgYGAge3J9CiMgVXNpbmcgYSBjb25kaXRpb25hbCBleHByZXNzaW9uIGluc2lkZSBbXQpzb21lX3dvcmRzW25jaGFyKHNvbWVfd29yZHMpID4gNF0KCmBgYApUaGlzIHN5bnRhY3RpYyBzdHJ1Y3R1cmUsIHdoZXJlIHlvdSBhcHBseSB0aGUgW10gb3BlcmF0b3IgdG8gYHNvbWVfd29yZHNgIGFuZCBhbHNvIHVzZSBgc29tZV93b3Jkc2AgaW4gdGhlIGFyZ3VtZW50IHlvdSBwYXNzIHRvIFtdIGlzIHZlcnkgaWRpb21hdGljIFIuIE1hbnkgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzIGRvIG5vdCBhbGxvdyBzdWNoIHRoaW5nczsgaWYgeW91IGhhdmUgcHJvZ3JhbW1lZCBhIGxvdCBpbiBtb3JlIHRyYWRpdGlvbmFsIGxhbmd1YWdlcywgaXQgd2lsbCB0YWtlIHlvdSBhIHdoaWxlIHRvIGdldCB1c2VkIHRvIHRoaXMuIEp1c3QgcmVtZW1iZXIgdGhhdCBSIGFsd2F5cyBldmFsdWF0ZXMgdGhlIGlucHV0IGFyZ3VtZW50IGZpcnN0IChpbiB0aGlzIGNhc2UsIGdlbmVyYXRpbmcgYSBib29sZWFuIHZlY3RvcikgYW5kIHRoZW4gcGFzc2VzIHRoZSByZXN1bHQgb2YgdGhhdCBldmFsdWF0aW9uIHRvIFtdLiBNYW55IHBlb3BsZSBmaW5kIGl0IGhlbHBzIHRvIHRoaW5rIHRocm91Z2ggdGhvc2UgdHdvIHN0ZXBzIHNlcGFyYXRlbHksIHdoZW4gdHJ5aW5nIHRvIGZpZ3VyZSBvdXQgd2hhdCBhIGNvbW1hbmQgbGlrZSB0aGlzIHByb2R1Y2VzLgoKCiMjIyBFeGVyY2lzZToKCjEuIFdyaXRlIGFuIFIgY29tbWFuZCB0aGF0IHNlbGVjdHMgZnJvbSBgc29tZV93b3Jkc2AgYWxsIHRoZSBmb3VyLWNoYXJhY3RlciB3b3JkcywgYnV0IG5vIG90aGVycy4KMi4gQ2hlY2sgdG8gc2VlIGlmIGV4ZWN1dGluZyB0aGlzIGNvbW1hbmQgaGFzIGNoYW5nZWQgdGhlIHZhbHVlIG9mIGBzb21lX3dvcmRzYC4gRXhwbGFpbiB3aHkgb3Igd2h5IG5vdC4KMy4gTW9kaWZ5IHRoZSBjb21tYW5kIHNvIHRoYXQgaXQgKipkb2VzKiogY2hhbmdlIGBzb21lX3dvcmRzYCwgd2l0aCB0aGF0IHZlY3RvciBub3cgaG9sZGluZyBvbmx5IHdvcmRzIHdpdGggZXhhY3RseSBmb3VyIGNoYXJhY3RlcnMuCjQuIEV4cGxhaW4gd2hhdCB0aGUgY29tbWFuZCB5b3UganVzdCB3cm90ZSBpbGx1c3RyYXRlcyBhYm91dCB0aGUgb3JkZXIgaW4gd2hpY2ggYXNzaWdubWVudCBzdGF0ZW1lbnRzIGFyZSBldmFsdWF0ZWQuCgoKXAoKIyMjIENvbmRpdGlvbmFsIE9wZXJhdG9ycyBpbiBSCgoKfCBPcGVyYXRpb24gICAgICAgICAgICAgICAgfCBSIFN5bWJvbCB8Cnw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS06fAp8IExlc3MgdGhhbiAgICAgICAgICAgICAgICB8IFw8ICAgICAgIHwKfCBMZXNzIHRoYW4gb3IgZXF1YWwgdG8gICAgfCBcPD0gICAgICB8CnwgR3JlYXRlciB0aGFuICAgICAgICAgICAgIHwgXD4gICAgICAgfAp8IEdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byB8IFw+PSAgICAgIHwKfCBOb3QgZXF1YWwgdG8gICAgICAgICAgICAgfCAhPSAgICAgICB8CgoKIyMjIENvbmRpdGlvbmFsIFNlbGVjdGlvbiB3aXRoIGAlaW4lYAoKV2UgaGF2ZSB1c2VkIGA9PWAgdG8gdGVzdCBpZiBvbmUgdmFsdWUgaXMgZXhhY3RseSBlcXVhbCB0byBhbm90aGVyIHZhbHVlLiBPZnRlbiwgd2Ugd2lzaCB0byB0ZXN0IGlmIGEgdmFsdWUgaXMgZXF1YWwgdG8gb25lIG9mIGEgc2V0IG9mIHZhbHVlcyAoaS5lLiBpcyB2YWx1ZSBBIGVxdWFsIHRvIGVpdGhlciB2YWx1ZSBCIG9yIHZhbHVlIEMgb3IgdmFsdWUgRCkuIEluIFIsIHRoaXMgc3BlY2lmaWMgY29tcGxleCBjb25kaXRpb25hbCBsb2dpYyBjYW4gYmUgcGVyZm9ybWVkIHdpdGggdGhlIGAlaW4lYCBvcGVyYXRvcjoKCmBgYHtyfQojICVpbiUgd2l0aCBhIHNjYWxhcgoKIyBHaXZlbiBhIHZlY3Rvci4uLgpjbGVhbl92ZWhpY2xlcyA8LSBjKCJiaWN5Y2xlIiwgInNjb290ZXIiLCAic2thdGVib2FyZCIpCgojIFRlc3QgaWYgYSBzY2FsYXIgaXMgY29udGFpbmVkIGluIHRoZSB2ZWN0b3IKInNjb290ZXIiICVpbiUgY2xlYW5fdmVoaWNsZXMKCiJqZXQgc2tpIiAlaW4lIGNsZWFuX3ZlaGljbGVzCmBgYApJZiB3ZSB1c2UgYCVpbmAgd2l0aCBhIHZlY3RvciBhcyB0aGUgbGVmdCBvcGVyYW5kLCBSIHBlcmZvcm1zIGluIGEgdmVjdG9yaXNlZCB3YXksIGFzIGV4cGVjdGVkLCB0ZXN0aW5nIGVhY2ggZWxlbWVudCBpbiB0dXJuOgoKYGBge3J9CiMgVXNpbmcgJWluJSB3aXRoIGEgdmVjdG9yCm15X3ZlaGljbGVzIDwtIGMoInNrYXRlYm9hcmQiLCAidHJhY3RvciIsICJjYXIiLCAic2Nvb3RlciIpCgpteV92ZWhpY2xlcyAlaW4lIGNsZWFuX3ZlaGljbGVzCgpgYGAKCkFuZCB0aGUgcmVzdWx0aW5nIGJvb2xlYW4gdmVjdG9yIGNhbiwgb2YgY291cnNlLCBiZSBwYXNzZWQgdG8gW106CgpgYGAge3J9CiMgU2VsZWN0IG91dCBteSBjbGVhbiB2ZWhpY2xlcwpteV92ZWhpY2xlc1tteV92ZWhpY2xlcyAlaW4lIGNsZWFuX3ZlaGljbGVzXQoKYGBgClwKCiMjIE1pc3NpbmcgZGF0YQoKT25lIGV4dHJlbWVseSBjb21tb24gc3Vic2V0dGluZyB0YXNrIGlzIGRlYWxpbmcgd2l0aCBtaXNzaW5nIGRhdGEuIE1pc3NpbmcgZGF0YSBhcmlzZXMgZnJlcXVlbnRseSBkdXJpbmcgcmVhbCByZXNlYXJjaC4gRm9yIGV4YW1wbGUsIGFuIGVjb2xvZ2lzdCBzdHVkeWluZyBwZW5ndWluIHBvcHVsYXRpb25zIG1heSBiZSBtZWFzdXJpbmcgdGhlIHdlaWdodCBvZiBlYWNoIHBlbmd1aW4gaW4gYSBsb2NhbCBjb2xvbnkuIElmIGEgcGFydGljdWxhciBwZW5ndWluIHJ1bnMgYXdheSBiZWZvcmUgaXQgY2FuIGJlIHdlaWdoZWQsIHRoYXQgcGVuZ3VpbidzIHdlaWdodCBpcyBtaXNzaW5nLiBJdCB3b3VsZCBiZSBhIG1pc3Rha2UgdG8gY29kZSB0aGUgcGVuZ3VpbidzIHdlaWdodCBhcyAwIC0tIHRoaXMgd291bGQgY29ycnVwdCBhbnkgbnVtZXJpY2FsIGFuYWx5c2lzIG9mIHdlaWdodC4gVGhlcmVmb3JlIFIgcHJvdmlkZXMgYSBzcGVjaWFsIGRhdGEgZW50aXR5IGZvciAnbWlzc2luZycgb3IgJ3VuZGVmaW5lZCcuIE1pc3NpbmcgZGF0YSBpbiBSIGlzIHJlcHJlc2VudGVkIGJ5IGBOQWAuIAoKVGhlIGNvbmRpdGlvbmFsIG9wZXJhdG9ycyB3ZSB1c2VkIGFib3ZlIGNhbm5vdCBiZSBhcHBsaWVkIHRvIE5BLiBBbnkgb3BlcmF0aW9uIGZvciB3aGljaCBvbmUgb3IgbW9yZSBvcGVyYW5kcyBpcyBOQSB3aWxsIHJldHVybiBOQSAoZXZlbiBgTkEgPT0gTkFgIHdpbGwgcmV0dXJuIGBOQWApLiBUbyB0ZXN0IGZvciBOQSwgdGhlcmVmb3JlLCBSIHByb3ZpZGVzIGZ1bmN0aW9uIGBpcy5uYWAgd2hpY2ggcmV0dXJucyBgVFJVRWAgaXRzIGlucHV0IGlzIGBOQWAgYW5kIGBGQUxTRWAgaW4gYWxsIG90aGVyIGluc3RhbmNlcy4KCmBgYHtyfQojIFBlbmd1aW4gIzMgZ290IGF3YXkuLi4uCnBlbmd1aW5fd2VpZ2h0c19nIDwtIGMoMzQ3NSwgMjk3NSwgTkEsIDM0MjApCgojIFVzZSBpcy5uYSB0byBnZW5lcmF0ZSBhIGJvb2xlYW4gdmVjdG9yCmlzLm5hKHBlbmd1aW5fd2VpZ2h0c19nKQoKIyBVc2UgdGhlIGJvb2xlYW4gdmVjdG9yIHRvIHNlbGVjdCBldmVyeW9uZSAKIyB3aG8gaXMgbm90IE5BLiBOb3RlIHRoZSB1c2Ugb2YgIQpwZW5ndWluX3dlaWdodHNfZ1shaXMubmEocGVuZ3Vpbl93ZWlnaHRzX2cpXQoKYGBgCgpcCgojIyBTdWJzZXR0aW5nIGluIHR3byBkaW1lbnNpb25zCgpUaGUgW10gb3BlcmF0b3IgaXMgYWxzbyB1c2VkIGZvciBzZWxlY3Rpbmcgc3Vic2V0cyBmcm9tIGRhdGEgZnJhbWVzLiBJbiB0aGlzIHVzYWdlLCBvbmUgc3VwcGxpZXMgdHdvIGFyZ3VtZW50cyB0byBbXSwgc2VwYXJhdGVkIGJ5IGNvbW1hcy4gVGhlIGZpcnN0IGFyZ3VtZW50IHNwZWNpZmllcyB3aGljaCByb3cocykgdG8gc2VsZWN0IGFuZCB0aGUgc2Vjb25kIHNwZWNpZmllcyB3aGljaCBjb2x1bW4ocykuIFRoZSBnZW5lcmFsIHN5bnRheCBpczogKm5hbWVfb2ZfZGF0YV9mcmFtZVtyb3dfaW5mb3JtYXRpb24sIGNvbHVtbl9pbmZvcm1hdGlvbl0qLiBUaGVyZSBhcmUsIGhvd2V2ZXIsIG1hbnkgY29tcGxleCBvcHRpb25zIGZvciB0aGUgZm9ybWF0IG9mIHRoZSByb3cgYW5kIGNvbHVtbiBpbmZvcm1hdGlvbi4gV2Ugd2lsbCBjb3ZlciB0aGUgbW9zdCB1c2VmdWwgaGVyZS4KCmBgYHtyfQojIENyZWF0ZSBhIGRhdGEgZnJhbWUgdG8gcHJhY3RpY2UgdHdvLWRpbWVuc2lvbmFsIHNlbGVjdGlvbgpjb3VudHJpZXMgPC0gYygiQXVzdHJpYSIsICJCcmF6aWwiLCAiQ2FuYWRhIiwgIkRlbm1hcmsiKQpjYXBpdGFscyA8LSBjKCJWaWVubmEiLCAiQnJhc2lsaWEiLCAiT3R0YXdhIiwgIkNvcGVuaGFnZW4iKQpwb3B1bGF0aW9uX2luX21pbGxpb25zIDwtIGMoOSwgMjExLCAzOCwgNikKCmdlb2dyYXBoeV9kZiA8LSBkYXRhLmZyYW1lKENvdW50cnkgPSBjb3VudHJpZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIENhcGl0YWwgPSBjYXBpdGFscywKICAgICAgICAgICAgICAgICAgICAgICAgICAgUG9wdWxhdGlvbk1pbGxpb25zID0gcG9wdWxhdGlvbl9pbl9taWxsaW9ucykKCmdlb2dyYXBoeV9kZgoKYGBgCgoKSW4gdGhlIHNpbXBsZXN0IGNhc2UsIHdlIHdpc2ggdG8gc2VsZWN0IGEgc2luZ2xlIHZhbHVlIGZyb20gYSBkYXRhIGZyYW1lLCBzbyB3ZSBwcm92aWRlIHRoZSByb3cgaW5kZXggYW5kIGNvbHVtbiBpbmRleCBvZiB0aGUgY2VsbCBvZiBpbnRlcmVzdC4gRm9yIGV4YW1wbGUsIGltYWdpbmUgd2Ugd2FudCB0aGUgcG9wdWxhdGlvbiBvZiBWaWVubmEuIFdlIGtub3cgdGhhdCBWaWVubmEgaXMgaW4gcm93IDEgYW5kIHRoZSBwb3B1bGF0aW9uIGlzIGluIGNvbHVtbiAzLiBUbyBzZWxlY3QgdGhhdCBjZWxsIHdlIHByb3ZpZGUgMSBmb3IgdGhlIHJvdyBpbmZvcm1hdGlvbiBhbmQgMyBmb3IgdGhlIGNvbHVtbiBpbmZvcm1hdGlvbiBpbiB0aGUgc3F1YXJlIGJyYWNrZXRzOgoKYGBge3Igc2luZ2xlIHNlbGVjdGlvbn0KZ2VvZ3JhcGh5X2RmWzEsM10KYGBgCgpEb24ndCB3b3JyeSBhYm91dCBob3cgeW91IHdvdWxkIGtub3cgdGhlIHNwZWNpZmljIHJvdyBhbmQgY29sdW1uIG9mIHRoZSBjZWxsIHlvdSBhcmUgaW50ZXJlc3RlZCBpbi4gVGhpcyBwYXJ0aWN1bGFyIHNlbGVjdGlvbiBvcGVyYXRpb24gaXMgdHlwaWNhbGx5IHVzZWQgaW4gc2l0dWF0aW9ucyB3aGVyZSB5b3VyIGNvZGUgaXMgY29tcHV0aW5nIHRob3NlIHZhbHVlcyBiYXNlZCBvbiBjb21wbGV4IGNyaXRlcmlhLiBUaGlzIGV4YW1wbGUgaXMgbWVyZWx5IGlsbHVzdHJhdGl2ZS4KClRoZXJlIGFyZSB0d28gdmVyeSB1c2VmdWwgZXh0ZW5zaW9ucyB0byB0aGlzIHBhdHRlcm46CgoxLiAgRWl0aGVyIHRoZSByb3cgb3IgY29sdW1uIGluZGV4IChvciBib3RoKSBtYXkgc3BlY2lmeSBhICoqcmFuZ2UqKiB1c2luZyB0aGUgYDpgIG9wZXJhdG9yLiBGb3IgZXhhbXBsZSwgYDE6M2Agb3IgYDY6MTJgICh0aGVzZSBhcmUgIjEgdG8gMyIgYW5kICI2IHRvIDEyIiByZXNwZWN0aXZlbHkpLgoKYGBge3IgcmFuZ2V9CiMgRm9yIHJvd3MgMiB0byA0IChCcmF6aWwsIENhbmFkYSwgRGVubWFyayksIHNlbGVjdCB0aGUgcG9wdWxhdGlvbiAoY29sdW1uIDMpCmdlb2dyYXBoeV9kZlsyOjQsIDNdCgojIEZvciBDYW5hZGEgKHJvdyAzKSwgc2VsZWN0IGJvdGggdGhlIGNhcGl0YWwgbmFtZSBhbmQgcG9wdWxhdGlvbiAoY29scyAyIGFuZCAzKQpnZW9ncmFwaHlfZGZbMywgMjozXQoKYGBgCgoyLiAgRWl0aGVyIHRoZSByb3cgb3IgY29sdW1uIGluZGV4ICoqbWF5IGJlIG9taXR0ZWQqKi4gVGhhdCBpcywgd2UgY2FuIHNheSBgZ2VvZ3JhcGh5X2RmWzMgLCBdYCBvciBgZ2VvZ3JhcGh5X2RmWyAsIDJdYC4gVGhlIG1pc3NpbmcgZWxlbWVudCBpcyBpbnRlcnByZXRlZCBhcyAqKmFsbCoqLiBPbWl0IHRoZSByb3cgbnVtYmVyIGFuZCB5b3Ugd2FudCAqKmFsbCByb3dzKiogaW4gdGhlIHN1cHBsaWVkIGNvbHVtbihzKS4gT21pdCB0aGUgY29sdW1uIG51bWJlciBhbmQgeW91IHdhbnQgKiphbGwgY29sdW1ucyoqIGluIHRoZSBzdXBwbGllZCByb3cocykuCgpgYGB7ciBvbWl0dGVkIGluZGV4fQojIEZvciBEZW5tYXJrIChyb3cgNCksIHNlbGVjdCBhbGwgdGhlIGNvbHVtbnMKZ2VvZ3JhcGh5X2RmWzQsIF0KCiMgRm9yIGFsbCByb3dzLCBzZWxlY3QgdGhlIGNhcGl0YWwgY2l0eSBuYW1lIChjb2x1bW4gMikKZ2VvZ3JhcGh5X2RmWyAsIDJdCgpgYGAKCllvdSBtYXkgaGF2ZSBiZWVuIHN1cnByaXNlZCBieSB0aGUgb3V0cHV0IGdlbmVyYXRlZCBieSB0aGF0IGxhc3QgZXhhbXBsZS4gQWx0aG91Z2ggeW91IGhhdmUgc2VsZWN0ZWQgYSBzaW5nbGUgY29sdW1uLCB0aGUgb3V0cHV0IGlzIHByaW50ZWQgaG9yaXpvbnRhbGx5LCBhcyB0aG91Z2ggaXQgd2VyZSBhIHJvdy4gVGhpcyBpcyBhIHBlY3VsaWFyaXR5IG9mIFIuIEFueSBjb2xsZWN0aW9uIHRoYXQgaGFzIGEgc2luZ2xlIGRpbWVuc2lvbiAoaS5lLiBkb2Vzbid0IGhhdmUgYm90aCBjb2x1bW5zIGFuZCByb3dzKSBpcyB0cmVhdGVkIGFzIGEgcGxhaW4gdmVjdG9yLiBBbmQgdmVjdG9ycyBhcmUgYWx3YXlzIHByaW50ZWQgaG9yaXpvbnRhbGx5LiBCeSBleHRlbnNpb24sIHNpbmNlIGEgc2VsZWN0ZWQgY29sdW1uIG9mIGEgZGF0YSBmcmFtZSBpcyBhIHZlY3RvciwgeW91IGNhbiBhcHBseSBldmVyeXRoaW5nIHlvdSBoYXZlIGxlYXJuZWQgYWJvdXQgdmVjdG9ycyB0byBzZWxlY3RlZCBkYXRhIGZyYW1lIGNvbHVtbnMsIHdoaWNoIGlzIGV4YWN0bHkgd2hhdCB3ZSB3YW50IHRvIGJlIGFibGUgdG8gZG8uCgpZb3UgY2FuIGNvbWJpbmUgcmFuZ2VzIGFuZCB0aGUgKm1pc3NpbmcgaW5kZXggPSBhbGwqIHRlY2huaXF1ZToKCmBgYHtyIHRvZ2V0aGVyfQojIEZvciB0aGUgZmlyc3QgdGhyZWUgcm93cywgc2VsZWN0IGFsbCB0aGUgY29sdW1ucwpnZW9ncmFwaHlfZGZbMTozICwgXQoKYGBgCgojIyMgRXhlcmNpc2UKCjEuIFdoYXQgZG8geW91IHRoaW5rIGBnZW9ncmFwaHlfZGZbICwgXWAgKGkuZS4gd2hlcmUgYm90aCByb3cgYW5kIGNvbHVtbiBpbmZvcm1hdGlvbiBhcmUgb21pdHRlZCkgd2lsbCBkbz8gCjIuIFRyeSBpdC4gV2VyZSB5b3UgcmlnaHQ/CgpcCgpJbnN0ZWFkIG9mIHVzaW5nIGNvbHVtbiBudW1iZXJzLCB5b3UgY2FuIHByb3ZpZGUgY29sdW1uIG5hbWVzIGFzIHRoZSBjb2x1bW4gaW5mb3JtYXRpb24gKGFuZCByb3cgbmFtZXMgYXMgdGhlIHJvdyBpbmZvcm1hdGlvbiBpZiB5b3VyIGRhdGEgZnJhbWUgaGFzIG5hbWVkIHJvd3MpLiBVc2UgdGhlIGNvbWJpbmUgZnVuY3Rpb24gYGMoKWAgdG8gcHJvdmlkZSBtdWx0aXBsZSBjb2x1bW4gbmFtZXMsIGFuZCBiZSBzdXJlIHRvIHN1cnJvdW5kIGVhY2ggY29sdW1uIG5hbWUgd2l0aCBxdW90ZXMsIGJlY2F1c2UgUiBjb25zaWRlcnMgdGhlbSB0byBiZSBzdHJpbmdzIGluIHRoaXMgc2l0dWF0aW9uLgoKYGBge3IgbmFtZWQgY29sdW1uc30KZ2VvZ3JhcGh5X2RmWzI6NCwgIkNhcGl0YWwiXQoKZ2VvZ3JhcGh5X2RmWzM6NCwgYygiQ291bnRyeSIsICJDYXBpdGFsIildCmBgYAoKXAoKIyMgRnVuY3Rpb24gYHN1YnNldGAKCkFuIGFsdGVybmF0aXZlIGZvciBzZWxlY3Rpbmcgcm93cyBhbmQgY29sdW1ucyBpcyBmdW5jdGlvbiBgc3Vic2V0YCwgd2l0aCBzdHJ1Y3R1cmU6Cgpgc3Vic2V0KHggPSBkYXRhZnJhbWUsIHN1YnNldCA9IGNvbmRpdGlvbmFsX3N0YXRlbWVudF90b19hcHBseV90b19yb3dzLCBzZWxlY3QgPSBjb2x1bW5zX3RvX2tlZXApYAoKCmBgYHtyfQpjb3VudHJpZXNfb3Zlcl8zMG1pbGxpb24gPC0gc3Vic2V0KGdlb2dyYXBoeV9kZiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0ID0gUG9wdWxhdGlvbk1pbGxpb25zID4gMzAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCA9IGMoIkNvdW50cnkiLCAiUG9wdWxhdGlvbk1pbGxpb25zIikgKQpjb3VudHJpZXNfb3Zlcl8zMG1pbGxpb24KYGBgCgpPbWl0dGluZyB2YWx1ZXMgZm9yIHRoZSBgc3Vic2V0YCBhcmd1bWVudCBvciBgc2VsZWN0YCBhcmd1bWVudCB3aWxsIGV4dHJhY3QgYWxsIHJvd3Mgb3IgY29sdW1ucywgcmVzcGVjdGl2ZWx5LgoKXAoKIyMgU2VsZWN0aW5nIGNvbHVtbnMKCkRhdGEgZnJhbWVzIGFyZSBjb21tb25seSBvcmdhbmlzZWQgd2l0aCBhIGNvbHVtbiBmb3IgZWFjaCBkYXRhIHZhcmlhYmxlIGFuZCBhIHJvdyBmb3IgZWFjaCBpbnN0YW5jZS4gVG8gYW5hbHlzZSBhIHBhcnRpY3VsYXIgZGF0YSB2YXJpYWJsZSwgd2Ugd2lzaCB0byBzZWxlY3QgYWxsIHJvd3MgaW4gYSBjb2x1bW4uIFRoaXMgcGFydGljdWxhciBzdWJzZXR0aW5nIG9wZXJhdGlvbiBpcyBzbyBjb21tb24gdGhhdCB0aGVyZSBhcmUgYWRkaXRpb25hbCwgZWZmaWNpZW50IHdheXMgdG8gcGVyZm9ybSBpdCBpbiBSLgoKVG8gc2VsZWN0IGEgc2luZ2xlIGNvbHVtbiBmcm9tIGEgZGF0YSBmcmFtZSwgdXNlIHRoZSBgJGAgb3BlcmF0b3IsIGZvbGxvd2VkIGJ5IHRoZSBjb2x1bW4gbmFtZSAoZG91YmxlIHF1b3RlcyBhcmUgbm90IHVzZWQgaW4gdGhpcyBjb250ZXh0KS4KCmBgYHtyfQpnZW9ncmFwaHlfZGYkQ291bnRyeQpgYGAKClRoaXMgb3BlcmF0aW9uIGlzIGNvbW1vbmx5IHVzZWQgd2hlbiBwZXJmb3JtaW5nIHN1bW1hcnkgb3BlcmF0aW9ucyBvbiBlbnRpcmUgY29sdW1uczoKCmBgYHtyfQojIE1lYW4gb2YgYSB0aGUgUG9wdWxhdGlvbk1pbGxpb25zIGNvbHVtbgptZWFuKGdlb2dyYXBoeV9kZiRQb3B1bGF0aW9uTWlsbGlvbnMpCmBgYAoKClRoZSBgJGAgb3BlcmF0b3IgY2FuIGFsc28gYmUgdXNlZCAqdG8gY3JlYXRlIG5ldyBjb2x1bW5zKi4gU2ltcGx5IGdpdmUgYSBuYW1lIHRvIHRoZSBuZXcgY29sdW1uIG9uIHRoZSBsZWZ0IGhhbmQgb2YgYW4gYXNzaWdubWVudCBzdGF0ZW1lbnQsIGFuZCB0aGUgY29udGVudHMgb2YgdGhlIG5ldyBjb2x1bW4gb24gdGhlIHJpZ2h0IGhhbmQgc2lkZS4gKE5COiBUaGlzIHR5cGUgb2Ygb3BlcmF0aW9uIGlzIG5vdCBhbGxvd2VkIGluIG1vcmUgdHJhZGl0aW9uYWwgbGFuZ3VhZ2VzLCBidXQgaXMgdmVyeSBjb21tb24gaW4gUi4pCgpgYGB7cn0KIyBDcmVhdGUgYSBuZXcgY29sdW1uIGVuIHBhc3NhbnQKZ2VvZ3JhcGh5X2RmJFBvcHVsYXRpb25UaG91c2FuZHMgPC0gZ2VvZ3JhcGh5X2RmJFBvcHVsYXRpb25NaWxsaW9ucyAqIDEwMDAKCiMgQ2hlY2sgdGhhdCB0aGUgY29sdW1uIGhhcyBiZWVuIGNyZWF0ZWQKc3RyKGdlb2dyYXBoeV9kZikKCiMgUHJpbnQgdGhlIGNvbnRlbnRzIG9mIHRoZSBuZXcgY29sdW1uCmdlb2dyYXBoeV9kZiRQb3B1bGF0aW9uVGhvdXNhbmRzCmBgYAoKXAoKIyMgU3Vic2V0dGluZyB3aXRoIGBkcGx5cmAKCldlIHdpbGwgbm93IHN3aXRjaCB0byBhIGxhcmdlciBkYXRhIHNldCwgYW5kIHNlZSBob3cgdGhlIHR5cGVzIG9mIHNlbGVjdGlvbiBvcGVyYXRpb25zIGRlc2NyaWJlZCBhYm92ZSBhcmUgcGVyZm9ybWVkIHdpdGggdGhlIGBkcGx5cmAgbGlicmFyeS4gVGhlcmUgYXJlIGZpdmUgbWFpbiBmdW5jdGlvbnMgaW4gYGRwbHlyYDogZmlsdGVyLCBzZWxlY3QsIG11dGF0ZSAobm90IGNvdmVyZWQgaW4gdGhpcyBzZXNzaW9uKSwgZ3JvdXBfYnksIGFuZCBzdW1tYXJpc2UuIFdlIHdpbGwgc2VlIGhvdyB0aGV5IGNhbiBiZSB1c2VkIHRvIHBlcmZvcm0gZnVuZGFtZW50YWwgcHJvY2Vzc2luZyBvZiBzZWxlY3RlZCBwb3J0aW9ucyBvZiBkYXRhIHNldHMuCgpUbyB1c2UgYGRwbHlyYCwgeW91IG11c3QgZmlyc3QgaW5zdGFsbCBpdCBvbiB5b3VyIG1hY2hpbmUgd2l0aCBgaW5zdGFsbC5wYWNrYWdlc2AuIEFsdGVybmF0aXZlbHksIGlmIHlvdSBoYXZlIGluc3RhbGxlZCBwYWNrYWdlIGB0aWR5dmVyc2VgLCBgZHBseXJgIHdpbGwgaGF2ZSBiZWVuIGluc3RhbGxlZCBhcyBwYXJ0IG9mIHRoYXQgb3BlcmF0aW9uLiBUbyBsb2FkIGBkcGx5cmAgZm9yIHVzZSBkdXJpbmcgYW4gUlN0dWRpbyBzZXNzaW9uLCB1c2UgdGhlIGBsaWJyYXJ5YCBjb21tYW5kLCBhcyB3ZSBoYXZlIGRvbmUgcHJldmlvdXNseS4KCkZvciB0aGlzIHNlY3Rpb24sIHdlIHdpbGwgdXNlIGRhdGEgYWJvdXQgZm9yZXN0IGZpcmVzIGluIFBvcnR1Z2FsIGluIDIwMjIuIFBvcnR1Z2FsIGhhcyBhIGhpZ2ggaW5jaWRlbmNlIG9mIGZvcmVzdCBmaXJlLCBhbmQgdGhlc2UgZGF0YSBjYW4gcmV2ZWFsIHBhdHRlcm5zIGFib3V0IG9jY3VycmVuY2UgYW5kIHNldmVyaXR5LgoKWW91IGNhbiBkb3dubG9hZCB0aGUgZGF0YSBmaWxlICpmb3Jlc3RfZmlyZXNfcG9ydHVnYWwuY3N2KiBmcm9tIHRoaXMgbGluazoKCmh0dHBzOi8vZHJpdmUuZ29vZ2xlLmNvbS9kcml2ZS9mb2xkZXJzLzFVQnAtUDR3RkFhUUwzZWdJUTdkR2EyUlFlNlJRS3FneSBvciB1c2luZzoKCj4KPmBgYHtyLCBldmFsPUZBTFNFfQo+IGRvd25sb2FkLmZpbGUodXJsID0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9ydGlzLXRyYWluaW5nLzIwMjMtczItcjRzc3AvbWFpbi9kYXRhL2ZvcmVzdF9maXJlc19wb3J0dWdhbC5jc3YiLCAKPiAgICAgICAgICAgICAgZGVzdGZpbGUgPSAiZGF0YS9mb3Jlc3RfZmlyZXNfcG9ydHVnYWwuY3N2IikKPmBgYAo+IF9SZW1lbWJlciB0aGF0IFIgaXMgY2FzZSBzZW5zaXRpdmUgc28gaWYgeW91IGRvbid0IGhhdmUgYSBkaXJlY3RvcnkgZXhhY3RseSBjYWxsZWQgYGRhdGEvYCBtb2RpZnkgdGhlIGNvbW1hbmQgdG8gbWF0Y2ggeW91ciBkaXJlY3RvcnkuXwoKVGhlIGZpbGUgY29udGFpbnMgb25lIHJvdyBmb3IgZWFjaCByZWNvcmRlZCBmb3Jlc3QgZmlyZSBpbiBQb3J0dWdhbCBpbiAyMDIyLiBFYWNoIHJlY29yZCBoYXMgYSBudW1iZXIgb2YgbWVhc3VyZXMgKGNvbHVtbnMpIGluY2x1ZGluZyByZWdpb24gKHggYW5kIHkgdmFsdWVzIG9uIGEgZ3JpZCksIG1vbnRoLCBkYXksIGdyb3VuZCBjb25kaXRpb24gbWVhc3VyZXMsIG1ldGVvcm9sb2dpY2FsIG1lYXN1cmVzLCBhbmQgdGhlIGV4dGVudCBvZiB0aGUgZmlyZSBpbiBoZWFjdGFyZXMuIE5vdGUgdGhhdCBhbnkgZmlyZSB0aGF0IGNvbnN1bWVkIGxlc3MgdGhhbiBvbmUgaGVjdGFyZSBoYXMgYSB2YWx1ZSBvZiAwIGluIHRoaXMgbGFzdCBjb2x1bW4uCgpJbiB0aGUgY29kZSB0aGF0IGZvbGxvd3MsIHdlIHdpbGwgYXNzdW1lIHRoYXQgeW91IGFyZSB3b3JraW5nIGluIGEgcHJvamVjdCwgdGhhdCB0aGUgcHJvamVjdCBmb2xkZXIgaGFzIGEgc3ViLWZvbGRlciBjYWxsZWQgKipkYXRhKiogYW5kIHRoYXQgdGhlIC5jc3YgZmlsZSBoYXMgYmVlbiBwbGFjZWQgaW4gdGhhdCBmb2xkZXIuCgpgYGB7ciBsb2FkIGRhdGEsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KIyBMb2FkIGRwbHlyCmxpYnJhcnkoZHBseXIpCgoKIyBJbXBvcnQgdGhlIGNzdiBmaWxlIGludG8gYSBkYXRhIGZyYW1lCmZpcmVzX2RmIDwtIGZpcmVzX2RmIDwtIHJlYWQuY3N2KCJkYXRhL2ZvcmVzdF9maXJlc19wb3J0dWdhbC5jc3YiKQoKIyBJbnNwZWN0IHRoZSBkYXRhCnN0cihmaXJlc19kZikKCmBgYAoKIyMjIFNlbGVjdGluZyBjb2x1bW5zCgpUbyBleHRyYWN0IGluZGl2aWR1YWwgY29sdW1ucywgdXNlIGZ1bmN0aW9uIGBzZWxlY3RgLCBwYXNzaW5nIGluIHRoZSBkYXRhZnJhbWUsIGFuZCB0aGUgbmFtZXMgb2YgdGhlIGNvbHVtbnMgeW91IHdhbnQuIFRoZXJlIGlzIG5vIG5lZWQgdG8gZm9ybWF0IHRoZSBjb2x1bW4gbmFtZXMgaW50byBhIHZlY3Rvciwgb3IgdG8gdXNlIGRvdWJsZSBxdW90ZXMuCgpgYGB7ciwgcmVzdWx0cz0naGlkZSd9CiMgU2VsZWN0IHRoZSBGaXJlQXJlYUhhIGNvbHVtbiwgd2hpY2ggc2hvd3MgYXJlYSBkYW1hZ2VkIGJ5IHRoZSBmaXJlIGluIGhlY3RhcmVzCnNlbGVjdChmaXJlc19kZiwgRmlyZUFyZWFIYSkKYGBgCgpgYGB7cn0KIyBTZWxlY3QgdGhlIG1vbnRoIGFuZCBmaXJlIGFyZWEgY29sdW1ucywgYW5kIHN0b3JlIHRoZW0gaW4gYSBuZXcgdmVjdG9yCm1vbnRoX2FyZWFfZGYgPC0gc2VsZWN0KGZpcmVzX2RmLCBNb250aCwgRmlyZUFyZWFIYSkKCiMgSW5zcGVjdAptb250aF9hcmVhX2RmWzM1MDozNTUsXSAjIFlvdSBjYW4gY29tYmluZSBiYXNlIFIgW10gd2l0aCBkcGx5cgoKIyBGb3IgZWZmaWNpZW5jeSwgeW91IGNhbiBkZWZpbmUgYSB2ZWN0b3Igb2YgY29sdW1uIG5hbWVzIHRvIHVzZSB3aGVuZXZlcgojIG5lZWRlZC4gVXNlIGRvdWJsZSBxdW90ZXMgYXMgcGVyIHVzdWFsCnNlbGVjdGVkX2NvbHMgPC0gYygiSHVtaWRpdHkiLCAiRmlyZUFyZWFIYSIpCmh1bWlkaXR5X2FyZWFfZGYgPC0gc2VsZWN0KGZpcmVzX2RmLCBhbGxfb2Yoc2VsZWN0ZWRfY29scykpCgojIEV4YW1wbGUgYW5hbHlzaXMgdXNpbmcgdHdvIGNvbHVtbnMKY29yKGh1bWlkaXR5X2FyZWFfZGYpCgpgYGAKCjwhLS0KLSB1c2luZyBoZWxwZXIgZnVuY3Rpb25zCiAgLSBzdGFydHNfd2l0aAogIC0gZW5kc193aXRoCiAgLSBjb250YWlucwogIC0gYW55b2YKLS0+CgoKXAoKIyMjIFNlbGVjdGluZyByb3dzCgpJbiBjb21wdXRpbmcsIHRoZSBwcm9jZXNzIG9mIHNlbGVjdGluZyByb3dzIGZyb20gYSBkYXRhIHRhYmxlIGJhc2VkIG9uIHBhcnRpY3VsYXIgY3JpdGVyaWEgaXMgY2FsbGVkICoqZmlsdGVyaW5nKiouIFdpdGggYGRwbHlyYCB3ZSB1c2UgZnVuY3Rpb24gYGZpbHRlcmAsIHBhc3NpbmcgdGhlIG5hbWUgb2YgdGhlIGRhdGEgZnJhbWUgYW5kIHRoZSBmaWx0ZXIgY29uZGl0aW9uLiBUaGUgcmVzdWx0IGlzIGEgbmV3IGRhdGEgZnJhbWUgdGhhdCBjb250YWlucyBhbGwgcm93cyBmcm9tIHRoZSBvcmlnaW5hbCBmb3Igd2hpY2ggdGhlIGZpbHRlciBjb25kaXRpb24gZXZhbHVhdGVkIHRvIFRSVUUuCgpgYGB7cn0KIyBFeHRyYWN0IGFsbCBmaXJlcyB0aGF0IG9jY3VycmVkIGluIE9jdG9iZXIgKGkuZS4gd2hlcmUgdGhlIE1vbnRoIGNvbHVtbiBjb250YWlucwojICdvY3QnKQpvY3RvYmVyX2ZpcmVzIDwtIGZpbHRlcihmaXJlc19kZiwgTW9udGggPT0gJ29jdCcpCgojIENvbXB1dGUgdGhlIG1lYW4gZHJvdWdodCBsZXZlbCBmb3IgYWxsIGZpcmVzIGluIG9jdG9iZXIsIHVzaW5nIGJhc2UgUgojIGNvbHVtbiBzZWxlY3Rpb24gd2l0aCAkCm1lYW4ob2N0b2Jlcl9maXJlcyREcm91Z2h0TGV2ZWwpCgpgYGAKCiMjIyBFeGVyY2lzZQoKMS4gSG93IHdvdWxkIHlvdSBnZW5lcmF0ZSBkYXRhIGZyYW1lIG9jdG9iZXJfZmlyZXMgdXNpbmcgW10gaW4gYmFzZSBSPwoyLiBSZWZlcnJpbmcgdG8gdGhlIFZpc3VhbGlzYXRpb24gTW9kdWxlLCBob3cgd291bGQgeW91IHNlbGVjdCB0aGUgRGVubWFyayBkYXRhIHVzaW5nIGRwbHlyPwoKCiMjIyBGaWx0ZXJpbmcgd2l0aCBjb21wbGV4IGNvbmRpdGlvbmFscwoKT2Z0ZW4gd2Ugd2lzaCB0byBmaWx0ZXIgb24gY3JpdGVyaWEgdGhhdCBpbnZvbHZlIG11bHRpcGxlIGNvbXBhcmlzb25zLiBGb3IgZXhhbXBsZSwgaWYgd2Ugd2lzaCB0byBleHRyYWN0IGFsbCB0aGUgcm93cyBmb3IgZmlyZXMgZHVyaW5nIHRoZSB3aW50ZXIgc2Vhc29uIChEZWNlbWJlciB0aHJvdWdoIEZlYnJ1YXJ5IGluIFBvcnR1Z2FsKSB3ZSB3YW50IHJvd3MgZm9yIHdoaWNoIE1vbnRoIGlzICdkZWMnIG9yICdqYW4nIG9yICdmZWInLiBFYXJsaWVyIHdlIHNhdyBob3cgdG8gZG8gdGhpcyB3aXRoYCVpbiVgLiBNb3JlIGdlbmVyYWxseSwgd2UgY2FuIGNvbWJpbmUgY29uZGl0aW9uYWwgc3RhdGVtZW50cyB1c2luZyB0aGUgbG9naWNhbCBBTkQgYW5kIE9SIG9wZXJhdG9ycywgYCZgIGFuZCBgfGAgcmVzcGVjdGl2ZWx5LgoKYGBge3J9CiMgU2VsZWN0IHRoZSB3aW50ZXIgZmlyZXMKd2ludGVyX2RmIDwtIGZpbHRlcihmaXJlc19kZiwgTW9udGggPT0gImRlYyIgfCBNb250aCA9PSAiamFuIiB8IE1vbnRoID09ICJmZWIiKQpgYGAKCgojIyMgRXhlcmNpc2U6CgpUaGUgWFJlZ2lvbiBhbmQgWVJlZ2lvbiBmaWVsZHMgYXJlIGNvb3JkaW5hdGVzIGluIGEgcHJlLWRlZmluZWQgOSBieSA5IGdyaWQgdGhhdCBjb3ZlcnMgYWxsIG9mIFBvcnR1Z2FsLiBZb3Ugd2FudCB0byBmaW5kIGFsbCBmaXJlcyB0aGF0IG9jY3VycmVkIG9uIHRoZSBlYXN0IGFuZCB3ZXN0IGJvcmRlcnMgb2YgdGhlIGNvdW50cnkgLS0gdGhhdCBpcywgaW4gWFJlZ2lvbnMgMSwgMiwgOCwgb3IgOS4gRmlsdGVyIGZvciB0aGVzZSByZWNvcmRzLiBNYWtlIHlvdXIgZXhwcmVzc2lvbiBhcyBzdWNjaW5jdCBhcyB5b3UgY2FuLiAoWW91IG9ubHkgbmVlZCBvbmUgYHxgLikKCmBgYHtyfQp4X2VkZ2VfZmlyZXMgPC0gZmlsdGVyKGZpcmVzX2RmLCBYUmVnaW9uIDw9IDIgfCBYUmVnaW9uID49IDgpCmBgYAoKCgojIyMgRXhlcmNpc2U6CgpGcmVxdWVudGx5LCB3ZSB3YW50IGV4dHJhY3QgYSBzdWJzZXQgb2Ygcm93cyAqYW5kKiBhIHN1YnNldCBvZiBjb2x1bW5zIGZyb20gYSBsYXJnZSBkYXRhIHNldCAtLSB0aGF0IGlzLCB3ZSB3aWxsIHdhbnQgdG8gcGVyZm9ybSBib3RoIGEgc2VsZWN0IGFuZCBhIGZpbHRlciBvcGVyYXRpb24uIFVzaW5nIGFuIGludGVybWVkaWF0ZSB2YXJpYWJsZSwgd3JpdGUgY29kZSB0byBleHRyYWN0IG1vbnRoLCB0ZW1wZXJhdHVyZSwgYW5kIGZpcmUgYXJlYSBmb3Igb25seSB0aGUgc3VtbWVyIG1vbnRocyAoSnVuZSwgSnVseSwgYW5kIEF1Z3VzdCkuIE5vdGUgdGhhdCBsYXRlciBpbiB0b2RheSdzIHNlc3Npb24gd2Ugd2lsbCBzZWUgYW4gYWx0ZXJuYXRpdmUgImRwbHlyIHN5bnRheCIgZm9yIHRoaXMgY29tYmluZWQgb3BlcmF0aW9uLCBidXQgaXQgZ29vZCB0byBtYXN0ZXIgYm90aCBhcHByb2FjaGVzLgoKYGBge3J9CiMgRXhlcmNpc2UgU29sdXRpb24gKHRoZXJlIGFyZSBtYW55IHBvc3NpYmxlIHNvbHV0aW9ucykKCiMgRmlsdGVyIHRvIGdldCB0aGUgcm93cyB3ZSB3YW50CnN1bW1lcl9maXJlc19kZiA8LSBmaWx0ZXIoZmlyZXNfZGYsIE1vbnRoICVpbiUgYygnanVuJywgJ2p1bCcsJ2F1ZycpKQoKIyBQYXNzIHRoZSByZXN1bHQgb2YgdGhlIGZpbHRlcmluZyBvcGVyYXRpb24gdG8gc2VsZWN0CnN1bW1lcl90ZW1wZXJhdHVyZV9hcmVhX2RmIDwtIHNlbGVjdChzdW1tZXJfZmlyZXNfZGYsIE1vbnRoLCBUZW1wZXJhdHVyZSwgRmlyZUFyZWFIYSkKYGBgCgoKCgpcCgojIyBTb3J0aW5nCgpTb3J0aW5nIGlzIGFuIGV4dHJlbWVseSBjb21tb24gb3BlcmF0aW9uIG9uIGxhcmdlIGRhdGEgc2V0cywgYXMgaXQgbWFrZXMgaXQgZWFzaWVyIHRvIGluc3BlY3Qgb3VyIGRhdGEgZWZmaWNpZW50bHkuIFRvIHNvcnQgd2l0aCBgZHBseXJgLCB1c2UgZnVuY3Rpb24gYGFycmFuZ2VgLiBQYXNzIGluIHRoZSBuYW1lIG9mIHRoZSBkYXRhIGZyYW1lLCBhbmQgdGhlIGNvbHVtbnMgb24gd2hpY2ggeW91IHdpc2ggdG8gc29ydCwgZnJvbSBzbG93ZXN0IHRvIGZhc3Rlc3QgbW92aW5nLgoKYGBge3J9CiMgU29ydCBhbGwgcmVjb3JkcyBpbiBvcmRlciBvZiBhcmVhIGNvbnN1bWVkIChjb2x1bW4gRmlyZUFyZWFIYSkKc29ydF9ieV9hcmVhX2RmIDwtIGFycmFuZ2UoZmlyZXNfZGYsIEZpcmVBcmVhSGEpCgoKIyBUbyBzb3J0IGluIGRlc2NlbmRpbmcgb3JkZXIsIGFwcGx5IGZ1bmN0aW9uIGRlc2MgdG8gdGhlIGNvbHVtbgpzb3J0X2J5X2FyZWFfZGYgPC0gYXJyYW5nZShmaXJlc19kZiwgZGVzYyhGaXJlQXJlYUhhKSkKCgojIFNvcnRpbmcgb24gbXVsdGlwbGUgY29sdW1ucwpzb3J0X2J5X21vbnRoX2FyZWFfZGYgPC0gYXJyYW5nZShmaXJlc19kZiwgTW9udGgsIGRlc2MoRmlyZUFyZWFIYSkpCmBgYAoKWW91IHdpbGwgbm90ZSB0aGF0IHRoaXMgbGFzdCByZXN1bHQgaXMgc29ydGVkIGJ5IG1vbnRoICphbHBoYWJldGljYWxseSouIFRoaXMgbWFrZXMgc2Vuc2UgdG8gUiwgYnV0IHdlIHByb2JhYmx5IHdvdWxkIHByZWZlciB0byBoYXZlIGl0IHNvcnRlZCAqbG9naWNhbGx5Ki4gVG8gZW5zdXJlIHRoaXMsIHdlIGNhbiBjYXN0IGNvbHVtbiBNb250aCBmcm9tIHR5cGUgImNociIgKGp1c3Qgc3RyaW5ncykgdG8gdHlwZSAiT3JkZXJlZCBGYWN0b3IiLCBhbiBvcmRlcmVkIGNhdGVnb3JpY2FsIHZhcmlhYmxlLiBUaGUgY29kZSBmb3IgdGhpcyBvcGVyYXRpb24gaXMgc2hvd24gYmVsb3cuIFdlIHdpbGwgZGlzY3VzcyB0aGUgbWFuYWdlbWVudCBvZiBjYXRlZ29yaWNhbCB2cy4gbnVtZXJpY2FsIHZhcmlhYmxlcyBpbiBtb3JlIGRlcHRoIGluIHRoZSBTdW1tYXJpc2luZyBNb2R1bGUsIGxhdGVyIGluIHRoZSBzZW1lc3Rlci4KCmBgYCB7cn0KIyBEZWZpbmUgdGhlIGxvZ2ljYWwgb3JkZXIgb2Ygb3VyIG1vbnRoIGNvZGVzCm1vbnRoc19pbl9vcmRlciA9IGMoImphbiIsICJmZWIiLCAibWFyIiwgImFwciIsICJtYXkiLCAianVuIiwgImp1bCIsICJhdWciLCAic2VwIiwKICAgICAgICAgICAgICAgICAgICAib2N0IiwgIm5vdiIsICJkZWMiKQoKIyBNb2RpZnkgdGhlIE1vbnRoIGNvbHVtbiBzbyBpdCB1c2VzIHRoYXQgb3JkZXJpbmcgCmZpcmVzX2RmJE1vbnRoIDwtIG9yZGVyZWQoZmlyZXNfZGYkTW9udGgsIGxldmVscyA9IG1vbnRoc19pbl9vcmRlcikKCiMgUmVwZWF0IHRoZSBzb3J0IG9wZXJhdGlvbiB0byBjb25maXJtCnNvcnRfYnlfbW9udGhfYXJlYV9kZiA8LSBhcnJhbmdlKGZpcmVzX2RmLCBNb250aCwgZGVzYyhGaXJlQXJlYUhhKSkKCiMgQ2hlY2sKaGVhZChzb3J0X2J5X21vbnRoX2FyZWFfZGYpCmBgYAoKCiMjIFN1bW1hcmlzaW5nIGJ5IGdyb3VwCgpXZSB0eXBpY2FsbHkgZXh0cmFjdCBzdWJzZXRzIGZyb20gYSBkYXRhIGZyYW1lIHRvIHNlcnZlIGFzIGlucHV0IHRvIG1ldGhvZHMgdGhhdCBzdW1tYXJpc2UsIHBlcmZvcm0gc3RhdGlzdGljYWwgYW5hbHlzZXMsIG9yIHByb2R1Y2UgZ3JhcGhzIG9mIHRob3NlIGRhdGEuIExpYnJhcnkgYGRwbHlyYCBwcm92aWRlcyBhIHBhaXIgb2YgbWV0aG9kcyAtLSBgZ3JvdXBfYnlgIGFuZCBgc3VtbWFyaXNlYCAtLSB3aGljaCwgd2hlbiB1c2VkIHRvZ2V0aGVyLCBtYWtlIHRoaXMgd29ya2Zsb3cgZXNwZWNpYWxseSBzdWNjaW5jdCBmb3Igc2ltcGxlIHBvc3Qtc2VsZWN0aW9uIHByb2Nlc3NpbmcuCgpBc3N1bWUgdGhhdCB5b3Ugd291bGQgbGlrZSB0byBrbm93IHRoZSBtZWFuIGZpcmUgYXJlYSBjb25zdW1lZCBmb3IgZWFjaCBtb250aC4gWW91IHdpbGwgZmlyc3QgYXBwbHkgYGdyb3VwX2J5YCB0byBgZmlyZXNfZGZgIHRvIGRlZmluZSB5b3VyIGdyb3VwaW5nIHZhcmlhYmxlIGFzIGNvbHVtbiBgTW9udGhgLiBUaGlzIGhhcyBubyBtYWpvciB2aXNpYmxlIGltcGFjdCBvbiBgZmlyZXNfZGZgLiBIb3dldmVyLCBhbnkgc3Vic2VxdWVudCBzdW1tYXJpZXMgeW91IHBlcmZvcm0gb24gdGhhdCBkYXRhIGZyYW1lIHdpdGggZnVuY3Rpb24gYHN1bW1hcmlzZWAgKiphcmUgYXBwbGllZCBieSBncm91cCoqLiBGb3IgZXhhbXBsZSwgaWYgeW91IHVzZSBgc3VtbWFyaXNlYCB0byBjb21wdXRlIHRoZSBtZWFuIG9mIEZpcmVBcmVhSGEgKHN5bnRheCBzaG93biBiZWxvdyksIGl0IGNvbXB1dGVzIHRoYXQgbWVhbiBmb3IgZWFjaCBtb250aCBzZXBhcmF0ZWx5LiBJZiB5b3UgdXNlIGBzdW1tYXJpc2VgIHRvIGNvbXB1dGUgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiBUZW1wZXJhdHVyZSwgaXQgY29tcHV0ZXMgdGhhdCBzdGFuZGFyZCBkZXZpYXRpb24gZm9yIGVhY2ggbW9udGggc2VwYXJhdGVseS4gQW5kIHNvIG9uLgoKYGBge3J9CiMgVG8gY29tcGFyZSBhcmVhIGNvbnN1bWVkIGJ5IG1vbnRoCgojIEFwcGx5IGdyb3VwX2J5LiBJbiBWaWV3IHRoZSBkYXRhIGZyYW1lIGxvb2tzIHVuY2hhbmdlZApmaXJlc19ncF9tb250aF9kZiA8LSBncm91cF9ieShmaXJlc19kZiwgTW9udGgpCgojIFdoZW4gcHJpbnRlZCB0byBjb25zb2xlLCB0aGUgZGF0YSBmcmFtZSBhbHNvIGxvb2tzIHRoZSBzYW1lIGV4Y2VwdCBmb3IgR3JvdXBzCiMgaGVhZGVyCmZpcmVzX2dwX21vbnRoX2RmCmBgYAoKClRoZSBtYWdpYyBoYXBwZW5zIHdoZW4gd2UgdGFrZSBhIGdyb3VwZWQgZGF0YSBmcmFtZSBhbmQgcGFzcyBpdCB0byBgc3VtbWFyaXNlYC4gRnVuY3Rpb24gYHN1bW1hcmlzZWAgcmV0dXJucyBhIG5ldyBkYXRhIGZyYW1lIHdpdGggYSByb3cgZm9yIGVhY2ggbGV2ZWwgb2YgdGhlIGdyb3VwaW5nIHZhcmlhYmxlLCBhbmQgYSBjb2x1bW4gZm9yIGVhY2ggc3VtbWFyeSBmdW5jdGlvbiB3ZSBzcGVjaWZ5LgoKYGBge3J9CiMgVCBzZWNvbmQgYXJnIHRvIHN1bW1hcmlzZSBpcyAiY29sIGhlYWRlciA9IGV4cHJlc3Npb24iLiBFeHByZXNzaW9uIGlzIGFwcGxpZWQgYnkgZ3JvdXAKCnN1bW1hcmlzZShmaXJlc19ncF9tb250aF9kZiwgTWVhbkFyZWFMbiA9IG1lYW4oRmlyZUFyZWFIYSwgbmEucm0gPSBUUlVFKSkKYGBgCgpOb3RlIHRoYXQgeW91IGNhbiBwcm92aWRlIG11bHRpcGxlIGV4cHJlc3Npb25zIHRvIGBzdW1tYXJpc2VgLiBJdCBwcm9kdWNlcyBhIGNvbHVtbiBmb3IgZWFjaC4gCgpZb3UgY2FuIGFsc28gcHJvdmlkZSBtdWx0aXBsZSBjb2x1bW4gbmFtZXMgdG8gYGdyb3VwX2J5YC4gRnVuY3Rpb24gYHN1bW1hcmlzZWAgaXMgdGhlbiBhcHBsaWVkIHRvIHRoZSBncm91cHMgZGVmaW5lZCBieSBlYWNoIGNvbWJpbmF0aW9uIG9mIHZhbHVlcyBvbiB0aGUgZ3JvdXBpbmcgdmFyaWFibGVzLiAKCmBgYHtyfQojIEdyb3VwaW5nIG9uIHR3byB2YXJpYWJsZXMKZmlyZXNfbW9udGhfZGF5IDwtIGdyb3VwX2J5KGZpcmVzX2RmLCBNb250aCwgRGF5KQpzdW1tYXJpc2UoZmlyZXNfbW9udGhfZGF5LCBNZWFuQXJlYUZvck1vbnRoQW5kRGF5T2ZXZWVrID0gbWVhbihGaXJlQXJlYUhhLCBuYS5ybSA9IFRSVUUpKQoKYGBgCgoKIyMjIEV4ZXJjaXNlCgoxLiBVc2UgJ2dyb3VwX2J5JyBhbmQgJ3N1bW1hcmlzZScgdG8gZmluZCB0aGUgbGFyZ2VzdCBmaXJlIChpLmUuIG1heGltdW0gYXJlYSBidXJudCkgaW4gZWFjaCBtb250aC4KMi4gQSB1c2VmdWwgd29ya2VyIGZ1bmN0aW9uIGluIGxpYnJhcnkgYGRwbHlyYCBpcyBgbigpYC4gVGhpcyBmdW5jdGlvbiwgd2hlbiB1c2VkIGluIGBzdW1tYXJpc2VgLCBjb3VudHMgdGhlIG51bWJlciBvZiByZWNvcmRzIGluIGVhY2ggZ3JvdXAgKGl0IHRha2VzIG5vIGNvbHVtbiBoZWFkZXI7IGl0IGp1c3QgY291bnRzIHRoZSBudW1iZXIgb2Ygcm93cyBpbiB0aGUgZ3JvdXApLiBHZW5lcmF0ZSBhIHRhYmxlIHRoYXQgc2hvd3MgdGhlIG51bWJlciBvZiBmaXJlcyB0aGF0IG9jY3VycmVkIG9uIGVhY2ggZGF5IG9mIHRoZSB3ZWVrLiBIb3cgd291bGQgeW91IGV4cGxhaW4gdGhlIHBhdHRlcm4gaW4gdGhlc2UgcmVzdWx0cz8KCgpgYGB7cn0KIyBFeGVyY2lzZSBTb2x1dGlvbgpmaXJlc19ncF9kYXlfZGYgPC0gZ3JvdXBfYnkoZmlyZXNfZGYsIERheSkKc3VtbWFyaXNlKGZpcmVzX2dwX2RheV9kZiwgTkZpcmVzID0gbigpKSAKYGBgCgozLiBNb2RpZnkgYGZpcmVzX2RmYCBzbyB0aGF0IGBzdW1tYXJpc2VgIG91dHB1dHMgdGhlIGRheXMgaW4gdGhlaXIgY29ycmVjdCBsb2dpY2FsIG9yZGVyIChlLmcuIFdlZG5lc2RheSBiZWZvcmUgVGh1cnNkYXkpLgoKXAoKIyMgUGlwZXMKCldoZW4geW91IGluc3RhbGwgZHBseXIgKGVpdGhlciBhbG9uZSBvciBhcyBwYXJ0IG9mIG1ldGEtcGFja2FnZSB0aWR5dmVyc2UpLCB5b3UgYWxzbyBnZXQgbGlicmFyeSBgbWFncml0dHJgLCB3aGljaCBjb250YWlucyBhIHNwZWNpYWwgb3BlcmF0b3IgY2FsbGVkICp0aGUgcGlwZSouIChOQjogVGhlIGNoYXJhY3RlciB8LCB1c2VkIGZvciBPUiwgaXMgYWxzbyBjYWxsZWQgdGhlIHBpcGUuIEp1c3Qgb25lIG9mIHRob3NlIHRoaW5ncy4pCgpXaXRoIHRoZSBwaXBlLCB3ZSBjYW4gcGVyZm9ybSBhIHNlcmllcyBvZiBvcGVyYXRpb25zLCBmb3IgZXhhbXBsZSBmaWx0ZXIgYW5kIHNlbGVjdCwgd2l0aG91dCBpbnRlcm1lZGlhdGUgdmFyaWFibGVzLiBXZSB0aGluayBvZiBzdGFydGluZyB3aXRoIG91ciBjb21wbGV0ZSBkYXRhIGZyYW1lIGFuZCBwdXNoaW5nIGl0IHRocm91Z2ggYSBzZXJpZXMsIG9yICoqcGlwZWxpbmUqKiBvZiBvcGVyYXRpb25zLiBBdCBlYWNoIHN0ZXAsIHRoZSBvdXRwdXQgZnJvbSBvbmUgb3BlcmF0aW9uIGlzIHBhc3NlZCBhbG9uZyBhcyB0aGUgaW5wdXQgb2YgdGhlIG5leHQuCgpXaGVuIHVzaW5nIHRoZSBwaXBlLCB3ZSBubyBsb25nZXIgaGF2ZSB0byBwcm92aWRlIHRoZSBuYW1lIG9mIGRhdGEgZnJhbWUgYXMgdGhlIGZpcnN0IGFyZ3VtZW50IHRvIGFsbCB0aGUgZHBseXIgZnVuY3Rpb25zIC0tIHRoZXkgYWx3YXlzIHRha2UgdGhlIG91dHB1dCBvZiB0aGUgcHJldmlvdXMgc3RlcCBpbiB0aGUgcGlwZS4KClByZXZpb3VzbHksIHdlIGNvbWJpbmVkIGZpbHRlciBhbmQgc2VsZWN0IGJ5IHVzaW5nIGFuIGludGVybWVkaWF0ZSB2YXJpYWJsZSwgbGlrZSB0aGlzOgoKYGBge3J9CiMgRmlsdGVyIHRvIGdldCB0aGUgcm93cyB3ZSB3YW50CnN1bW1lcl9maXJlc19kZiA8LSBmaWx0ZXIoZmlyZXNfZGYsIE1vbnRoICVpbiUgYygnanVuJywgJ2p1bCcsJ2F1ZycpKQoKIyBQYXNzIHRoZSByZXN1bHQgb2YgdGhlIGZpbHRlcmluZyBvcGVyYXRpb24gdG8gc2VsZWN0CnN1bW1lcl90ZW1wZXJhdHVyZV9hcmVhX2RmIDwtIHNlbGVjdChzdW1tZXJfZmlyZXNfZGYsIE1vbnRoLCBUZW1wZXJhdHVyZSwgRmlyZUFyZWFIYSkKYGBgCgpVc2luZyBwaXBlIHN5bnRheCwgdGhlIHNhbWUgcmVzdWx0IHdvdWxkIGJlIG9idGFpbmVkIHdpdGg6CgpgYGB7cn0Kc3VtbWVyX3RlbXBlcmF0dXJlX2FyZWFfZGYgPC0gZmlyZXNfZGYgJT4lIGZpbHRlcihNb250aCAlaW4lIGMoImp1biIsICJqdWwiLCAiYXVnIikpICU+JSBzZWxlY3QoTW9udGgsIFRlbXBlcmF0dXJlLCBGaXJlQXJlYUhhKQoKaGVhZChzdW1tZXJfdGVtcGVyYXR1cmVfYXJlYV9kZikKYGBgCgpMaWtlIHRoZSArIHN5bWJvbCBpbiBnZ3Bsb3QsIGAlPiVgIHNpZ25hbHMgdG8gUlN0dWRpbyB0aGF0IG1vcmUgY29kZSBpcyBjb21pbmcsIGFsbG93aW5nIHlvdSB0byBtYWtlIHRoZSBjb2RlIHJlYWRhYmxlIGJ5IHBsYWNpbmcgZWFjaCBvcGVyYXRpb24gaW4gdGhlIHBpcGVsaW5lIG9uIGEgbmV3IGxpbmU6CgpgYGB7cn0Kc3VtbWVyX3RlbXBlcmF0dXJlX2FyZWFfZGYgPC0gZmlyZXNfZGYgJT4lIAogIGZpbHRlcihNb250aCAlaW4lIGMoImp1biIsICJqdWwiLCAiYXVnIikpICU+JSAKICBzZWxlY3QoTW9udGgsIFRlbXBlcmF0dXJlLCBGaXJlQXJlYUhhKQoKaGVhZChzdW1tZXJfdGVtcGVyYXR1cmVfYXJlYV9kZikKYGBgCgojIyMgRXhlcmNpc2UKCjEuIFVzaW5nIGdyb3VwX2J5LCBzdW1tYXJpc2UsIGFuZCBwaXBlIG5vdGF0aW9uLCBjb21wdXRlIHRoZSBtZWFuIHRlbXBlcmF0dXJlIGZvciBlYWNoIG1vbnRoCgpgYGB7cn0KIyBFeGVyY2lzZSBzb2x1dGlvbgpmaXJlc19kZiAlPiUgZ3JvdXBfYnkoTW9udGgpICU+JSBzdW1tYXJpc2UoTWVhblRlbXBlcmF0dXJlID0gbWVhbihUZW1wZXJhdHVyZSwgbmEucm0gPSBUUlVFKSkKYGBgCgpcCgoyLiBZb3UgY2FuIHVzZSBnZ3Bsb3QgYXQgdGhlIGVuZCBvZiBhIHBpcGVsaW5lLiBXcml0ZSBhIHNpbmdsZSBjb21tYW5kIHRvIG1ha2UgYSBib3hwbG90IHRoYXQgaWxsdXN0cmF0ZXMgdGhlIGRpc3RyaWJ1dGlvbiBvZiBkcm91Z2h0IGxldmVsIGR1cmluZyBlYWNoIG9mIHRoZSBzdW1tZXIgbW9udGhzIGluIFBvcnR1Z2FsLiBQbGFuOiBGaXJzdCBmaWx0ZXIgb3V0IHRoZSBtb250aHMgeW91IHdhbnQsIHRoZW4gbWFrZSBhIGJveHBsb3Qgd2l0aCB4ID0gTW9udGggYW5kIHkgPSBEcm91Z2h0TGV2ZWwKCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCgpmaXJlc19kZiAlPiUgZmlsdGVyKE1vbnRoICVpbiUgYygianVuIiwgImp1bCIsICJhdWciKSkgJT4lCiAgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IE1vbnRoLCB5ID0gRHJvdWdodExldmVsKSkgKyAKICBnZW9tX2JveHBsb3QoKQpgYGAKClwKCjMuIFdyaXRlIGEgc2luZ2xlIGNvbW1hbmQgdGhhdCBwcm9kdWNlcyBhIGJhciBncmFwaCBvZiB0aGUgbWVhbiB0ZW1wZXJhdHVyZSBmb3IgZWFjaCBtb250aC4gKFBsYW46IFRoZSBwaXBlbGluZSBzdGFydHMgd2l0aCBgZ3JvdXBfYnlgIGFuZCBgc3VtbWFyaXNlYCB0byBjb21wdXRlIHRoZSBtZWFucywgdGhlbiBwb3VycyB0aGF0IG91dHB1dCBpbnRvIGdncGxvdC4gTWFrZSB0aGUgZ3JhcGggdXNpbmcgYGdlb21fYmFyYC4gU2V0IHRoZSB4IGF4aXMgdG8gTW9udGggYW5kIHRoZSB5IGF4aXMgdG8gdGhlIGNvbHVtbiBuYW1lIHlvdSBhc3NpZ24gZm9yIHRoZSBtZWFuIHRlbXBlcmF0dXJlIGluIGBzdW1tYXJpc2VgLiBQYXNzIGludG8gZ2VvbV9iYXIgdGhlIGFyZ3VtZW50IGBzdGF0ID0gImlkZW50aXR5ImAgc28gaXQgcGxvdHMgdGhlIGRhdGEgdmFsdWVzIGFzIHByb3ZpZGVkLiBOb3RlIHRoZSBkb3VibGUgcXVvdGVzIGFyb3VuZCB0aGUgKmlkZW50aXR5KjsgdGhleSBhcmUgcmVxdWlyZWQuKQoKYGBge3J9CiMgRXhlcmNpc2Ugc29sdXRpb24KCmxpYnJhcnkoZ2dwbG90MikKCmZpcmVzX2RmICU+JSAKICBncm91cF9ieShNb250aCkgJT4lIAogIHN1bW1hcmlzZShNZWFuVGVtcGVyYXR1cmUgPSBtZWFuKFRlbXBlcmF0dXJlLCBuYS5ybSA9IFRSVUUpKSAlPiUKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gTW9udGgsIHkgPSBNZWFuVGVtcGVyYXR1cmUpKSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKQpgYGAKCgoKIyMgQSBXb3JkIG9mIENhdXRpb24KCkRwbHlyIHBpcGVsaW5lIHN5bnRheCBpcyBwb3B1bGFyIGJlY2F1c2UgaXQgZWxpbWluYXRlcyB0aGUgbmVlZCBmb3IgaW50ZXJtZWRpYXRlIHZhcmlhYmxlcyBhbmQsIHRoZXJlZm9yZSwgcmVxdWlyZXMgYSBsb3QgbGVzcyB0eXBpbmcuIEl0IGlzIGNvbW1vbiwgd2hlbiBmaXJzdCBlbmNvdW50ZXJpbmcgdGhpcyBvcHRpb24sIHRvIGdldCBzbGlnaHRseSBjYXJyaWVkIGF3YXksIGFuZCBjaGFpbiB0b2dldGhlciBkb3plbnMgb2Ygb3BlcmF0aW9ucywgcmV2ZWxpbmcgaW4gaG93IG1hbnkga2V5c3Ryb2tlcyB5b3UgYXJlIHNhdmluZy4gVGhpcyBpcyByaXNreSwgZm9yIHNldmVyYWwgcmVhc29uczoKCjEuIElmIGEgbG9uZyBwaXBlbGluZSBmYWlscywgeW91IHdpbGwgbm90IGtub3cgKmluIHdoaWNoIG9wZXJhdGlvbiB0aGUgZXJyb3Igb2NjdXJyZWQqLiBUaGlzIGNhbiBtYWtlIGl0IGV4dHJlbWVseSBkaWZmaWN1bHQgdG8gZGVidWcuIAoyLiBCZWNhdXNlIHlvdSBhcmUgbm90IHNhdmluZyBhbnkgb2YgdGhlIGludGVybWVkaWF0ZSBvdXRwdXRzLCB5b3UgZG8gbm90IGhhdmUgdGhlbSBhdmFpbGFibGUgZm9yIGRpZmZlcmVudCBwcm9jZXNzaW5nIGluIG90aGVyIGNvbnRleHRzIC0tIHlvdSB3aWxsIGhhdmUgdG8gcmVwZWF0IHRoZSB3aG9sZSBwb3J0aW9uIG9mIHRoZSBwaXBlbGluZSB3aGljaCBnZW5lcmF0ZWQgdGhlbS4gCjMuIEEgbG9uZyBwaXBlbGluZSB3aWxsIGJlIGRpZmZpY3VsdCBmb3Igb3RoZXIgcGVvcGxlIHRvICJyZWFkIiAoaS5lLiB0byBmaWd1cmUgb3V0IHdoYXQgaXQncyBkb2luZykuIFdoZW4geW91IGhhdmUgbXVsdGlwbGUgb3B0aW9ucyBmb3IgcGVyZm9ybWluZyBhIGNvbXB1dGF0aW9uLCB0aGUgIGdvYWwgaXMgdG8gc3RyaWtlIGEgYmFsYW5jZSBiZXR3ZWVuICoqcGFyc2ltb255KiogKG5vdCB0b28gbXVjaCB0eXBpbmcpIGFuZCAqKnJlYWRhYmlsaXR5KiogKHlvdXIgY29kZSBpcyBlYXN5ICpmb3Igb3RoZXIgcGVvcGxlKiB0byB1bmRlcnN0YW5kKS4gV2hlbiB3b3JraW5nIG9uIGdyb3VwIHByb2plY3RzLCBvciBpbiBhIHByb2Zlc3Npb25hbCBzb2Z0d2FyZSBkZXZlbG9wbWVudCBjb250ZXh0LCByZWFkYWJpbGl0eSBpcyBjb25zaWRlcmVkIHRoZSBtb3JlIGNyaXRpY2FsIG9mIHRoZSB0d28gZmVhdHVyZXMuIE15IHBlcnNvbmFsIHN0eWxlIGlzIG5ldmVyIHRvIGJ1aWxkIHBpcGVsaW5lcyBvZiBtb3JlIHRoYW4gdGhyZWUgb3IgZm91ciBvcGVyYXRpb25zLiBUaGUgb3V0cHV0cyBvZiB0aGVzZSBzaG9ydCBzZXF1ZW5jZXMgY2FuIGJlIHNhdmVkIGludG8gaW50ZXJtZWRpYXRlIHZhcmlhYmxlcyB3aGljaCBjYW4gYmUgY2hlY2tlZCBhbmQgcmV1c2VkLCB3aGljaCBhcmUgdGhlbiBwYXNzZWQgYWxvbmcgaW50byB0aGUgbmV4dCBwaXBlbGluZS4KXAoKIyMgQW5vdGhlciBXb3JkIG9mIENhdXRpb24KClIgdXNlcnMgYXJlIGNvbnN0YW50bHkgYWRkaW5nIG5ldyBsaWJyYXJpZXMgdG8gYmFzZSBSLCBtZWFuaW5nIHRoYXQgeW91IHdpbGwgcHJvYmFibHkgaGF2ZSBzZXZlcmFsIG9wdGlvbnMgZm9yIGRvaW5nIGFueSBqb2IgaW4gUi4gVGhlIHZhcmlvdXMgb3B0aW9ucyBzb21ldGltZXMgaGF2ZSBzdWJ0bGUgdGVjaG5pY2FsIGRpZmZlcmVuY2VzIHRoYXQgd2lsbCBnZW5lcmF0ZSBhIGxvdCBvZiBhcmd1bWVudCBiZXR3ZWVuIHByb2Zlc3Npb25hbCBwcm9ncmFtbWVycywgYnV0IGFyZSB1bmxpa2VseSB0byBtYXR0ZXIgbXVjaCB0byByZXNlYXJjaCBzY2llbnRpc3RzLiBJbiBnZW5lcmFsLCB5b3Ugc2hvdWxkIGV4cGxvcmUgdGhlIFIgZWNvc3lzdGVtIGZyZWVseSBhbmQgdXNlIHdoYXRldmVyIHlvdSBsaWtlLiAqKkhvd2V2ZXIqKiBvbiBhc3NpZ25tZW50cywgaXQgaXMgd2lzZSB0byBjaGVjayB3aXRoIHRoZSBsZWN0dXJlciBiZWZvcmUgdXNpbmcgc29tZXRoaW5nIHRoYXQgaXMgcmVhbGx5IGRpZmZlcmVudCBmcm9tIHdoYXQgaXMgcHJlc2VudGVkIGluIGNsYXNzLiBZb3VyIGxlY3R1cmVyIG1heSwgZm9yIGVkdWNhdGlvbmFsIHJlYXNvbnMsIHdhbnQgeW91IHRvIHVzZSBzcGVjaWZpYyBSIHRvb2xzLgoKIyMgV2hhdCdzIE5leHQKCllvdSBtYXkgcmVjYWxsIHRoYXQgd2F5IGJhY2sgaW4gdGhlIGZpcnN0IG1vZHVsZSBvZiBSNFNTUCB3ZSBzYWlkIHdlIHdlcmUgZ29pbmcgdG8gYW5hbHlzZSBkYXRhLiBXZSBoYXZlbid0IHJlYWxseSBkb25lIG11Y2ggb2YgdGhhdCB5ZXQuIFNvIGZhciB3ZSBoYXZlIGJlZW4gKmdldHRpbmcgcmVhZHkqIHRvIGFuYWx5c2UgZGF0YS4gSW4gdGhlIG5leHQgbW9kdWxlLCB3ZSB3aWxsIHN0YXJ0IHJlYWxseSBkaWdnaW5nIGludG8gb3VyIGRhdGEgd2l0aCBleHBsb3JhdG9yeSBhbmFseXNpcyBhbmQgZGVzY3JpcHRpdmUgc3RhdGlzdGljcy4gQmVjYXVzZSB0aGlzIGlzIG5vdCBhIHN0YXRpc3RpY3MgY291cnNlICpwZXIgc2UqIHdlIHdpbGwgb25seSBiZSB1c2luZyBjb21tb24gZ2VuZXJhbCBhbmFseXNlcyBpbiB0aGUgaGFuZG91dHMgYW5kIHJlYWRpbmdzLiBJZiwgZm9yIGEgcHJvamVjdCBvciBhc3NpZ25tZW50LCB5b3UgbmVlZCB0byBkbyBzb21ldGhpbmcgbW9yZSBlc290ZXJpYywganVzdCBsZXQgdXMga25vdyAtLSBzb21lb25lIGhhcyBwcm9iYWJseSB3cml0dGVuIGFuIFIgbGlicmFyeSBmb3IgaXQuCg==