Adding Rows and Columns
Before we get to joins, let’s first cover adding rows or columns to a
data frame.
{dplyr}
includes bind_rows
and
bind_cols
, which are roughly equivalent to base R
rbind
and cbind
, but they are a bit easier to
work with.
To demonstrate, let’s create some subsets of the data to work with
first:
delaware <- filter(demo, state=="Delaware") %>%
select(1:6)
hawaii <- filter(demo, state=="Hawaii") %>%
select(1:4,7:9)
delaware
hawaii
We have some overlap in the column names, but the set of columns
doesn’t match exactly. To combine these datasets, we want to bind the
rows (each is a county) together:
# just printing, not saving...
bind_rows(delaware, hawaii)
bind_rows
includes all columns the exist in either
dataset and fills in missing values with NA
. It matches
columns by full name. If we tried this with rbind
, we’d get
an error, because the set of columns doesn’t match.
When would you really do this?
Maybe you have separate data files for each year of data, and you
need to combine them. Or for each school, or state, or experiment
iteration. Any case where the set of variables is
similar across multiple data sets that all have the
same unit of observation (rows are the same type of thing across data
sets and you have similar variables).
For adding columns, we’d need to make sure that
both data frames have the same number of rows and they
appear in the same order. Generally, you don’t want to
bind_cols
. If you have some column that identifies
observations in each data frame, you’ll want to join the datasets
together instead, which is what we’re going to do next. If you are just
trying to add a few columns of data, use mutate
.
Why use join instead of bind_cols
?
While it does happen, it’s dangerous to have multiple data sets with
the same set of rows without some ID variable – where they’re just
matched by the order of the rows; it’s too easy for the matching between
datasets to get messed up.
Joins
The terminology for joining comes from SQL, which is used to interact
with databases. And {dplyr}
can work directly with SQL
databases, translating {dplyr}
commands into SQL, running
them in the database, and then retrieving the results. That’s for a
separate workshop, but if you do want to work with a database, know that
you do not necessarily have to export the data in order to work with it
in R. And you can write {dplyr}
commands instead of SQL
commands to work with the data.
Note: the equivalent function for joining in base R
is merge()
.
Background: Keys
Joins work when you have two data frames (called tables in databases,
but same idea), and you want to merge them together. Each data set needs
to have some type of identifier, a key, that tells you how a row in
dataset 1 should be matched into dataset 2.
For example, here, we have three county datasets, all of which have
different data (different variables). In our case, each dataset includes
an ID variable for each county. These IDs are the same across datasets
because all 3 use the FIPS code from the US Census. So even though our
datasets have different numbers of rows:
names(cases)
## [1] "countyFIPS" "county" "state" "stateFIPS" "cases"
names(deaths)
## [1] "countyFIPS" "county" "state" "stateFIPS" "deaths"
names(demo)
## [1] "state" "county" "fips" "total"
## [5] "white" "black" "native" "asian"
## [9] "two" "non_white" "white_prop" "black_prop"
## [13] "native_prop" "asian_prop" "two_prop" "non_white_prop"
nrow(cases)
## [1] 2830
nrow(deaths)
## [1] 3145
nrow(demo)
## [1] 3142
We have a key that will link rows in the different datasets
together.
For example, look at a county that is in all three data sets:
filter(demo, fips == 1009)
filter(cases, countyFIPS == 1009)
filter(deaths, countyFIPS == 1009)
We want to combine the different variables from all 3 data frames
together into a single one. We can’t just bind the columns together
though, because the set of counties in each data set is different (some
counties are missing from some datasets).
Example (we’ll talk through the code shortly):
cases %>%
left_join(deaths, by="countyFIPS")
We also have a data set with state level data on COVID deaths. We
might want to add this state level info to each county. In that case,
you could use the state name to pull the correct state data from the
data set for each row in the county data set.
# We'll talk through the code more below
cases %>%
left_join(state_stats, by=c("state"="State"))
In this second example, each state in the state data set matches to
multiple rows (multiple counties) in the county data set – this is OK
and expected. You can have 1:1, 1:many, or many:many matches across data
sets (although this last one can get messy! so much so, that
{dplyr}
will warn you when it happens).
There can also be cases where the combination of two different
variables is the “key” – where both columns need to match for the data
sets to be joined correctly.
There is also a more complicated case where you don’t have clean
“key” variables but you still want to join two datasets. This requires
fuzzy or approximate matching using variables in the two data sets,
which is beyond the scope of what we can cover today. A case where this
might happen is when you have country names (not ISO codes), and
variants such as DRC vs. Democratic Republic of the Congo vs. Congo, DR
(yes, this happens regularly).
Today, we’re assuming there is some clean key to match on where
values match exactly.
EXERCISE 1
Import data about degrees awarded at Northwestern. What column(s) can
be used as a key to join the datasets together?
Note: this isn’t a code answer - read in the data and take a look at
the data frames to see what’s common in them.
bac <- read_csv("https://raw.githubusercontent.com/nuitrcs/R-intermediate-tidyverse-2023/main/data/bac_schools.csv")
masters <- read_csv("https://raw.githubusercontent.com/nuitrcs/R-intermediate-tidyverse-2023/main/data/masters_schools.csv")
doc <- read_csv("https://raw.githubusercontent.com/nuitrcs/R-intermediate-tidyverse-2023/main/data/doc_schools.csv")
Background: Join Types
There are different ways to match up rows in two datasets. They vary
based on what happens to rows that don’t match to the other data set
(where the key from one data set isn’t present in the other).
https://tavareshugo.github.io/r-intro-tidyverse-gapminder/fig/07-dplyr_joins.svg
An inner join only includes in the result rows that
matched in both datasets.
A left join includes all rows from the first (left)
data set, filling in missing values where there isn’t a match in the
second data set (right).
A right join does the opposite: keeps all rows from
the second (right) data set, and fills in missing values where there
isn’t a match in the fist.
An outer join keeps all rows from both data sets,
filling in missing values where there aren’t matches.
An anti-join helps you find rows in a dataset that
do NOT match to another.

Image Source: https://cghlewis.com/blog/joins/
Joins with {dplyr}
Each of the join types is a different function in
{dplyr}
: inner_join()
,
left_join()
, right_join()
,
full_join()
(the last one is an outer join). You use them
all in the same way.
Let’s start with the first example above. I’m going to re-write it in
a single line:
left_join(cases, deaths, by="countyFIPS")
The first input, cases, is the “left” table. The second, deaths, is
the “right.” The by="countyFIPS"
is telling it what key to
use to join – in this case, there’s a column named “countyFIPS” in both
data frames that should be used to match up rows.
In the output, it adds the suffix “.x” to any columns from the first
data set where there’s a column of the same name in the second data set,
and uses the suffix “.y” for the reverse. “countyFIPS” doesn’t get a
suffix because it’s the variable we told it to join on. “cases” and
“deaths” don’t get a suffix because there isn’t a column with those
names in the other data set.
Note above and also below, I’m just joining and printing the
resulting data frame. You could save the output (the resulting data
frame) in a new variable though:
cases_deaths <- left_join(cases, deaths, by="countyFIPS")
Variations on by
If I don’t tell {dplyr}
what the key variables are to
use to join the tables, it will default to trying to join on any columns
with the same name across data frames:
left_join(cases, deaths)
And tell me what it joined by in the output.
But it’s best to be explicit about how you want to join the tables.
Just because two columns have the same name, doesn’t mean all of the
data is the same! In the case of the cases
and
deaths
data frames, they come from the same source. So all
of these columns SHOULD match, but they don’t!
left_join(cases, deaths,
by="countyFIPS") %>%
filter(county.x != county.y)
# use select(county.x, county.y) to see differences
If we matched by countyFIPS and county, we’d miss these matches.
Something to always watch out for! It’s usually best to join using
the minimal set of columns needed to identify the correct matches.
With the cases
and deaths
data frames, they
came from the same data source, so the key column has the same name in
both data sets. In the demo
data frame, the name of the
column with the county FIPS code is different though:
names(demo)
To join with that, we use a named vector with by
, with
the form
c("Name in first data set"="Name in second data set")
:
left_join(cases, demo, by=c("countyFIPS"="fips"))
Here, where the column names are different, it just keeps one copy of
the joining key column, and uses the column name from the first (left)
data set.
We can also join on multiple columns, where each pair of columns we
specify needs to match. For example, in the cases and deaths data, there
are some of the same columns. We can require multiple columns to match
by supplying a vector of names:
left_join(cases, deaths, by=c("countyFIPS", "state"))
This gives us a cleaner result without duplicate rows. But make sure
that the values in these columns really should all match – if not,
you’ll be missing matches.
EXERCISE 2
Join (left_join
is fine) the cases
data
frame to the state_stats
data frame using the state column
in each (“state” in cases
and “State” in
state_stats
); case of the variable name matters! (“state”
is not equal to “State” as column names).
EXERCISE 3
Join the bac, masters, and doc data frames imported above into a
single data frame.
You can string together multiple join commands with %>%
Unmatched Rows
A left join keeps all of the rows in the first data set, whether or
not they match a row in the second. How do we find rows that didn’t
match?
You can use anti_join()
to find rows in one (the first)
data frame that don’t match to a row in the second:
anti_join(deaths, cases,
by=c("countyFIPS"))
This only returns the rows that don’t match.
Note that we don’t get any columns from the cases
data
frame because these are the rows from deaths
that don’t
match to cases. To see what rows in cases
don’t match,
switch the order:
anti_join(cases, deaths,
by=c("countyFIPS"))
They all match! But remember, there are still rows in deaths that
aren’t represented in the result.
If, instead of seeing the non-matching rows separately, you want to
see which ones didn’t match in the context of the joined data frame, you
can look for missing values in the joined columns.
left_join(deaths, cases,
by=c("countyFIPS")) %>%
filter(is.na(cases))
Beware though, because if there are legitimately missing values in
the column you check (here, cases), then you’ll get mixed results
here.
EXERCISE 4
Find the rows in deaths
that don’t have a match in
demo
Hint: join with deaths
(column
“countyFIPS”) and demo
(column “fips”).
Beware Missing Values
Missing values: by default, {dplyr}
matches missing
(NA
) as if it is a value. So if we have two data frames,
and there are missing values in our key id variables, then they will
match, even if there are multiple NA
s.
a <- tibble(id=c(1, 2, NA), val=c(10, 20, 30))
b <- tibble(id=c(2, 3, NA, NA), val2 = c("a", "b", "c", "d"))
a
b
left_join(a, b, by="id")
If we want this to not happen, we can set the na_matches
argument to “never”
left_join(a, b, by="id",
na_matches="never")
Sometimes, R will warn you that there there might be a problem.
c <- tibble(id=c(2, 3, NA, NA), val3 = c("w", "x", "y", "z"))
c
left_join(b, c, by="id")
This isn’t actually related to missing values, but it’s one of the
times when this is most likely to happen by mistake. What it means is
that there’s more than one row in the first data set that matches to the
same row in the second data set AND there are rows in the first data set
that each match to multiple rows in the second data set.
This can be legitimate, but it’s something you’d always want to check
to confirm that the behavior is correct.
Cleanup
So you’ve joined two data sets, and now you have a bunch of columns
with “.x” and “.y” (or other types of repeats). What do you do?
Option 1: select only the columns you really want in
the result ahead of doing the join. NOTE: this is one of the very few
times I might nest {dplyr}
function calls
cases %>%
select(countyFIPS, county, state, cases) %>%
left_join(select(demo, fips, total, white, black),
by=c("countyFIPS"="fips"))
Why nest the select
call instead of saving a subset of
the data first, and using that? Mostly for situations where the datasets
are large, and I don’t want extra copies of the data frame in my
environment. In this case, the data sets are small enough that I could
do this instead:
demo_sub <- select(demo, fips, total, white, black)
cases %>%
select(countyFIPS, county, state, cases) %>%
left_join(demo_sub,
by=c("countyFIPS"="fips"))
But I have demo_sub
hanging around in my environment
then (I like to keep my environment clean when possible!).
Option 2: use rename()
or
select()
on the result of the join:
cases %>%
left_join(demo,
by=c("countyFIPS"="fips")) %>%
select(countyFIPS, county=county.x, state=state.x,
cases, total, white, black)
EXERCISE 5
Create a data frame with only the following columns: countyFIPS,
state, deaths, non_white population
Hint: use the demo
and deaths
data
frames.
LS0tCnRpdGxlOiAnZHBseXI6IGpvaW4nCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgplZGl0b3Jfb3B0aW9uczoKICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQotLS0KCmBgYHtyLCBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGV2YWw9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVycm9yPUZBTFNFLCBtZXNzYWdlPUZBTFNFKQpgYGAKCipUaGUgb3V0cHV0IG9mIG1vc3Qgb2YgdGhlIFIgY2h1bmtzIGlzbid0IGluY2x1ZGVkIGluIHRoZSBIVE1MIHZlcnNpb24gb2YgdGhlIGZpbGUgdG8ga2VlcCBpdCB0byBhIG1vcmUgcmVhc29uYWJsZSBmaWxlIHNpemUuIFlvdSBjYW4gcnVuIHRoZSBjb2RlIGluIFIgdG8gc2VlIHRoZSBvdXRwdXQuKgoKIyBTZXR1cAoKYGBge3IsIGV2YWw9VFJVRX0KbGlicmFyeShkcGx5cikKbGlicmFyeShyZWFkcikKYGBgCgpXZSBrbm93IGB7ZHBseXJ9YCBmdW5jdGlvbnMgZm9yIHdvcmtpbmcgd2l0aCBhIHNpbmdsZSBkYXRhIGZyYW1lLiBge2RwbHlyfWAgYWxzbyBoYXMgZnVuY3Rpb25zIGZvciBqb2luaW5nIHR3byBkYXRhIGZyYW1lcyB0b2dldGhlci4KCiMgRGF0YQoKV2UncmUgZ29pbmcgdG8gdXNlIENPVklELTE5IGRhdGEgYnkgY291bnR5IGFuZCBkZW1vZ3JhcGhpY3MgYnkgY291bnR5IGZvciBqb2luaW5nOgoKYGBge3IsIGV2YWw9VFJVRX0KIyBjb3VudHkgbGV2ZWwgZGF0YQpkZW1vIDwtIHJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vbnVpdHJjcy9SLWludGVybWVkaWF0ZS10aWR5dmVyc2UtMjAyMy9tYWluL2RhdGEvY291bnR5X2RlbW9ncmFwaGljcy5jc3YiLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKQoKY2FzZXMgPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9udWl0cmNzL1ItaW50ZXJtZWRpYXRlLXRpZHl2ZXJzZS0yMDIzL21haW4vZGF0YS9jb3VudHlfY2FzZXMuY3N2Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKCmRlYXRocyA8LSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL251aXRyY3MvUi1pbnRlcm1lZGlhdGUtdGlkeXZlcnNlLTIwMjMvbWFpbi9kYXRhL2NvdW50eV9kZWF0aHMuY3N2Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKCiMgc3RhdGUgbGV2ZWwgZGF0YQpzdGF0ZV9zdGF0cyA8LSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL251aXRyY3MvUi1pbnRlcm1lZGlhdGUtdGlkeXZlcnNlLTIwMjMvbWFpbi9kYXRhL3N0YXRlX2NvdmlkLmNzdiIsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpCmBgYAoKSSd2ZSBpbnRyb2R1Y2VkIHNvbWUgbWlzc2luZyBkYXRhIGZvciB0aGUgcHVycG9zZXMgb2YgdGhpcyB3b3Jrc2hvcC4gR2V0IHRoZSBmdWxsIENPVklELTE5IGNhc2UgZGF0YSBmcm9tIDxodHRwczovL3VzYWZhY3RzLm9yZy92aXN1YWxpemF0aW9ucy9jb3JvbmF2aXJ1cy1jb3ZpZC0xOS1zcHJlYWQtbWFwLz4uIENPVklELTE5IGNhc2UgYW5kIGRlYXRoIGRhdGEgaW5jbHVkZWQgaGVyZSBpcyBhcyBvZiBlYXJseSBKdWx5IDIwMjAuIERlbW9ncmFwaGljIHZhcmlhYmxlcyBhcmUgY2Vuc3VzIGVzdGltYXRlcyBmb3IgSnVseSAyMDE5LlwKVG8gbGVhcm4gbW9yZSBhYm91dCBDT1ZJRC0xOSBhbmQgcmFjZSwgc2VlIDxodHRwczovL2NvdmlkdHJhY2tpbmcuY29tL3JhY2U+OyB0aGlzIGlzIHRoZSBzb3VyY2Ugb2YgdGhlIHN0YXRlLWxldmVsIENPVklELTE5IHN0YXRpc3RpY3MgdXNlZCBoZXJlLgoKV2hhdCdzIGluIHRoZSBkYXRhIHNldHM6CgpgYGB7cn0KZGVtbyAgICAgICAgICAgIyBkZW1vZ3JhcGhpY3MgYnkgY291bnR5CmNhc2VzICAgICAgICAgICMgY292aWQgY2FzZSBjb3VudHMgYnkgY291bnR5CmRlYXRocyAgICAgICAgICMgY292aWQgZGVhdGggY291bnRzIGJ5IGNvdW50eQpzdGF0ZV9zdGF0cy4gICAjIGNvdmlkIGNhc2UgYW5kIGRlYXRoIGNvdW50cyBieSBzdGF0ZSBhbmQgZGVtb2dyYXBoaWNzCmBgYAoKQ291bnR5IEZJUFMgQ29kZXMgYXJlIHVuaXF1ZSA1LWRpZ2l0IGNvZGVzIHRoYXQgcmVwcmVzZW50IHNwZWNpZmljIFVTIGNvdW50aWVzLiBUaGUgZmlyc3QgdHdvIGRpZ2l0cyByZXByZXNlbnQgdGhlIGNvdW50eSdzIHN0YXRlIGFuZCB0aGUgbGFzdCB0aHJlZSBkaWdpdHMgcmVwcmVzZW50IHRoZSBjb3VudHkuIEZvciBleGFtcGxlICIxNzAzMSIgaXMgdGhlIEZJUFMgQ29kZSBmb3IgQ29vayBDb3VudHksIElsbGlub2lzLgoKIyBBZGRpbmcgUm93cyBhbmQgQ29sdW1ucwoKQmVmb3JlIHdlIGdldCB0byBqb2lucywgbGV0J3MgZmlyc3QgY292ZXIgYWRkaW5nIHJvd3Mgb3IgY29sdW1ucyB0byBhIGRhdGEgZnJhbWUuCgpge2RwbHlyfWAgaW5jbHVkZXMgYGJpbmRfcm93c2AgYW5kIGBiaW5kX2NvbHNgLCB3aGljaCBhcmUgcm91Z2hseSBlcXVpdmFsZW50IHRvIGJhc2UgUiBgcmJpbmRgIGFuZCBgY2JpbmRgLCBidXQgdGhleSBhcmUgYSBiaXQgZWFzaWVyIHRvIHdvcmsgd2l0aC4KClRvIGRlbW9uc3RyYXRlLCBsZXQncyBjcmVhdGUgc29tZSBzdWJzZXRzIG9mIHRoZSBkYXRhIHRvIHdvcmsgd2l0aCBmaXJzdDoKCmBgYHtyLCBldmFsPVRSVUV9CmRlbGF3YXJlIDwtIGZpbHRlcihkZW1vLCBzdGF0ZT09IkRlbGF3YXJlIikgJT4lCiAgc2VsZWN0KDE6NikKaGF3YWlpIDwtIGZpbHRlcihkZW1vLCBzdGF0ZT09Ikhhd2FpaSIpICU+JQogIHNlbGVjdCgxOjQsNzo5KQpgYGAKCmBgYHtyfQpkZWxhd2FyZQpoYXdhaWkKYGBgCgpXZSBoYXZlIHNvbWUgb3ZlcmxhcCBpbiB0aGUgY29sdW1uIG5hbWVzLCBidXQgdGhlIHNldCBvZiBjb2x1bW5zIGRvZXNuJ3QgbWF0Y2ggZXhhY3RseS4gVG8gY29tYmluZSB0aGVzZSBkYXRhc2V0cywgd2Ugd2FudCB0byBiaW5kIHRoZSByb3dzIChlYWNoIGlzIGEgY291bnR5KSB0b2dldGhlcjoKCmBgYHtyLCBldmFsPVRSVUV9CiMganVzdCBwcmludGluZywgbm90IHNhdmluZy4uLgpiaW5kX3Jvd3MoZGVsYXdhcmUsIGhhd2FpaSkKYGBgCgpgYmluZF9yb3dzYCBpbmNsdWRlcyBhbGwgY29sdW1ucyB0aGUgZXhpc3QgaW4gZWl0aGVyIGRhdGFzZXQgYW5kIGZpbGxzIGluIG1pc3NpbmcgdmFsdWVzIHdpdGggYE5BYC4gSXQgbWF0Y2hlcyBjb2x1bW5zIGJ5IGZ1bGwgbmFtZS4gSWYgd2UgdHJpZWQgdGhpcyB3aXRoIGByYmluZGAsIHdlJ2QgZ2V0IGFuIGVycm9yLCBiZWNhdXNlIHRoZSBzZXQgb2YgY29sdW1ucyBkb2Vzbid0IG1hdGNoLgoKKipXaGVuIHdvdWxkIHlvdSByZWFsbHkgZG8gdGhpcz8qKgoKTWF5YmUgeW91IGhhdmUgc2VwYXJhdGUgZGF0YSBmaWxlcyBmb3IgZWFjaCB5ZWFyIG9mIGRhdGEsIGFuZCB5b3UgbmVlZCB0byBjb21iaW5lIHRoZW0uIE9yIGZvciBlYWNoIHNjaG9vbCwgb3Igc3RhdGUsIG9yIGV4cGVyaW1lbnQgaXRlcmF0aW9uLiBBbnkgY2FzZSB3aGVyZSB0aGUgc2V0IG9mICoqdmFyaWFibGVzKiogaXMgKipzaW1pbGFyIGFjcm9zcyBtdWx0aXBsZSBkYXRhIHNldHMqKiB0aGF0IGFsbCBoYXZlIHRoZSBzYW1lIHVuaXQgb2Ygb2JzZXJ2YXRpb24gKHJvd3MgYXJlIHRoZSBzYW1lIHR5cGUgb2YgdGhpbmcgYWNyb3NzIGRhdGEgc2V0cyBhbmQgeW91IGhhdmUgc2ltaWxhciB2YXJpYWJsZXMpLgoKRm9yICoqYWRkaW5nIGNvbHVtbnMqKiwgd2UnZCBuZWVkIHRvIG1ha2Ugc3VyZSB0aGF0ICoqYm90aCBkYXRhIGZyYW1lcyBoYXZlIHRoZSBzYW1lIG51bWJlciBvZiByb3dzKiogYW5kIHRoZXkgYXBwZWFyIGluIHRoZSBzYW1lIG9yZGVyLiBHZW5lcmFsbHksIHlvdSBkb24ndCB3YW50IHRvIGBiaW5kX2NvbHNgLiBJZiB5b3UgaGF2ZSBzb21lIGNvbHVtbiB0aGF0IGlkZW50aWZpZXMgb2JzZXJ2YXRpb25zIGluIGVhY2ggZGF0YSBmcmFtZSwgeW91J2xsIHdhbnQgdG8gam9pbiB0aGUgZGF0YXNldHMgdG9nZXRoZXIgaW5zdGVhZCwgd2hpY2ggaXMgd2hhdCB3ZSdyZSBnb2luZyB0byBkbyBuZXh0LiBJZiB5b3UgYXJlIGp1c3QgdHJ5aW5nIHRvIGFkZCBhIGZldyBjb2x1bW5zIG9mIGRhdGEsIHVzZSBgbXV0YXRlYC4KCioqV2h5IHVzZSBqb2luIGluc3RlYWQgb2YgYGJpbmRfY29sc2A/KioKCldoaWxlIGl0IGRvZXMgaGFwcGVuLCBpdCdzIGRhbmdlcm91cyB0byBoYXZlIG11bHRpcGxlIGRhdGEgc2V0cyB3aXRoIHRoZSBzYW1lIHNldCBvZiByb3dzIHdpdGhvdXQgc29tZSBJRCB2YXJpYWJsZSAtLSB3aGVyZSB0aGV5J3JlIGp1c3QgbWF0Y2hlZCBieSB0aGUgb3JkZXIgb2YgdGhlIHJvd3M7IGl0J3MgdG9vIGVhc3kgZm9yIHRoZSBtYXRjaGluZyBiZXR3ZWVuIGRhdGFzZXRzIHRvIGdldCBtZXNzZWQgdXAuCgojIEpvaW5zCgpUaGUgdGVybWlub2xvZ3kgZm9yIGpvaW5pbmcgY29tZXMgZnJvbSBTUUwsIHdoaWNoIGlzIHVzZWQgdG8gaW50ZXJhY3Qgd2l0aCBkYXRhYmFzZXMuIEFuZCBge2RwbHlyfWAgY2FuIHdvcmsgZGlyZWN0bHkgd2l0aCBTUUwgZGF0YWJhc2VzLCB0cmFuc2xhdGluZyBge2RwbHlyfWAgY29tbWFuZHMgaW50byBTUUwsIHJ1bm5pbmcgdGhlbSBpbiB0aGUgZGF0YWJhc2UsIGFuZCB0aGVuIHJldHJpZXZpbmcgdGhlIHJlc3VsdHMuIFRoYXQncyBmb3IgYSBzZXBhcmF0ZSB3b3Jrc2hvcCwgYnV0IGlmIHlvdSBkbyB3YW50IHRvIHdvcmsgd2l0aCBhIGRhdGFiYXNlLCBrbm93IHRoYXQgeW91IGRvIG5vdCBuZWNlc3NhcmlseSBoYXZlIHRvIGV4cG9ydCB0aGUgZGF0YSBpbiBvcmRlciB0byB3b3JrIHdpdGggaXQgaW4gUi4gQW5kIHlvdSBjYW4gd3JpdGUgYHtkcGx5cn1gIGNvbW1hbmRzIGluc3RlYWQgb2YgU1FMIGNvbW1hbmRzIHRvIHdvcmsgd2l0aCB0aGUgZGF0YS4KCioqTm90ZSoqOiB0aGUgZXF1aXZhbGVudCBmdW5jdGlvbiBmb3Igam9pbmluZyBpbiBiYXNlIFIgaXMgYG1lcmdlKClgLgoKIyMgQmFja2dyb3VuZDogS2V5cwoKSm9pbnMgd29yayB3aGVuIHlvdSBoYXZlIHR3byBkYXRhIGZyYW1lcyAoY2FsbGVkIHRhYmxlcyBpbiBkYXRhYmFzZXMsIGJ1dCBzYW1lIGlkZWEpLCBhbmQgeW91IHdhbnQgdG8gbWVyZ2UgdGhlbSB0b2dldGhlci4gRWFjaCBkYXRhIHNldCBuZWVkcyB0byBoYXZlIHNvbWUgdHlwZSBvZiBpZGVudGlmaWVyLCBhIGtleSwgdGhhdCB0ZWxscyB5b3UgaG93IGEgcm93IGluIGRhdGFzZXQgMSBzaG91bGQgYmUgbWF0Y2hlZCBpbnRvIGRhdGFzZXQgMi4KCkZvciBleGFtcGxlLCBoZXJlLCB3ZSBoYXZlIHRocmVlIGNvdW50eSBkYXRhc2V0cywgYWxsIG9mIHdoaWNoIGhhdmUgZGlmZmVyZW50IGRhdGEgKGRpZmZlcmVudCB2YXJpYWJsZXMpLiBJbiBvdXIgY2FzZSwgZWFjaCBkYXRhc2V0IGluY2x1ZGVzIGFuIElEIHZhcmlhYmxlIGZvciBlYWNoIGNvdW50eS4gVGhlc2UgSURzIGFyZSB0aGUgc2FtZSBhY3Jvc3MgZGF0YXNldHMgYmVjYXVzZSBhbGwgMyB1c2UgdGhlIEZJUFMgY29kZSBmcm9tIHRoZSBVUyBDZW5zdXMuIFNvIGV2ZW4gdGhvdWdoIG91ciBkYXRhc2V0cyBoYXZlIGRpZmZlcmVudCBudW1iZXJzIG9mIHJvd3M6CgpgYGB7ciwgZXZhbD1UUlVFfQpuYW1lcyhjYXNlcykKbmFtZXMoZGVhdGhzKQpuYW1lcyhkZW1vKQoKbnJvdyhjYXNlcykKbnJvdyhkZWF0aHMpCm5yb3coZGVtbykKYGBgCgpXZSBoYXZlIGEga2V5IHRoYXQgd2lsbCBsaW5rIHJvd3MgaW4gdGhlIGRpZmZlcmVudCBkYXRhc2V0cyB0b2dldGhlci4KCkZvciBleGFtcGxlLCBsb29rIGF0IGEgY291bnR5IHRoYXQgaXMgaW4gYWxsIHRocmVlIGRhdGEgc2V0czoKCmBgYHtyLCBldmFsPVRSVUV9CmZpbHRlcihkZW1vLCBmaXBzID09IDEwMDkpCmZpbHRlcihjYXNlcywgY291bnR5RklQUyA9PSAxMDA5KQpmaWx0ZXIoZGVhdGhzLCBjb3VudHlGSVBTID09IDEwMDkpCmBgYAoKV2Ugd2FudCB0byBjb21iaW5lIHRoZSBkaWZmZXJlbnQgdmFyaWFibGVzIGZyb20gYWxsIDMgZGF0YSBmcmFtZXMgdG9nZXRoZXIgaW50byBhIHNpbmdsZSBvbmUuIFdlIGNhbid0IGp1c3QgYmluZCB0aGUgY29sdW1ucyB0b2dldGhlciB0aG91Z2gsIGJlY2F1c2UgdGhlIHNldCBvZiBjb3VudGllcyBpbiBlYWNoIGRhdGEgc2V0IGlzIGRpZmZlcmVudCAoc29tZSBjb3VudGllcyBhcmUgbWlzc2luZyBmcm9tIHNvbWUgZGF0YXNldHMpLgoKRXhhbXBsZSAod2UnbGwgdGFsayB0aHJvdWdoIHRoZSBjb2RlIHNob3J0bHkpOgoKYGBge3IsIGV2YWw9VFJVRX0KY2FzZXMgJT4lCiAgbGVmdF9qb2luKGRlYXRocywgYnk9ImNvdW50eUZJUFMiKQpgYGAKCldlIGFsc28gaGF2ZSBhIGRhdGEgc2V0IHdpdGggc3RhdGUgbGV2ZWwgZGF0YSBvbiBDT1ZJRCBkZWF0aHMuIFdlIG1pZ2h0IHdhbnQgdG8gYWRkIHRoaXMgc3RhdGUgbGV2ZWwgaW5mbyB0byBlYWNoIGNvdW50eS4gSW4gdGhhdCBjYXNlLCB5b3UgY291bGQgdXNlIHRoZSBzdGF0ZSBuYW1lIHRvIHB1bGwgdGhlIGNvcnJlY3Qgc3RhdGUgZGF0YSBmcm9tIHRoZSBkYXRhIHNldCBmb3IgZWFjaCByb3cgaW4gdGhlIGNvdW50eSBkYXRhIHNldC4KCmBgYHtyLCBldmFsPVRSVUV9CiMgV2UnbGwgdGFsayB0aHJvdWdoIHRoZSBjb2RlIG1vcmUgYmVsb3cKY2FzZXMgJT4lCiAgbGVmdF9qb2luKHN0YXRlX3N0YXRzLCBieT1jKCJzdGF0ZSI9IlN0YXRlIikpCmBgYAoKSW4gdGhpcyBzZWNvbmQgZXhhbXBsZSwgZWFjaCBzdGF0ZSBpbiB0aGUgc3RhdGUgZGF0YSBzZXQgbWF0Y2hlcyB0byBtdWx0aXBsZSByb3dzIChtdWx0aXBsZSBjb3VudGllcykgaW4gdGhlIGNvdW50eSBkYXRhIHNldCAtLSB0aGlzIGlzIE9LIGFuZCBleHBlY3RlZC4gWW91IGNhbiBoYXZlIDE6MSwgMTptYW55LCBvciBtYW55Om1hbnkgbWF0Y2hlcyBhY3Jvc3MgZGF0YSBzZXRzIChhbHRob3VnaCB0aGlzIGxhc3Qgb25lIGNhbiBnZXQgbWVzc3khIHNvIG11Y2ggc28sIHRoYXQgYHtkcGx5cn1gIHdpbGwgd2FybiB5b3Ugd2hlbiBpdCBoYXBwZW5zKS4KClRoZXJlIGNhbiBhbHNvIGJlIGNhc2VzIHdoZXJlIHRoZSBjb21iaW5hdGlvbiBvZiB0d28gZGlmZmVyZW50IHZhcmlhYmxlcyBpcyB0aGUgImtleSIgLS0gd2hlcmUgYm90aCBjb2x1bW5zIG5lZWQgdG8gbWF0Y2ggZm9yIHRoZSBkYXRhIHNldHMgdG8gYmUgam9pbmVkIGNvcnJlY3RseS4KClRoZXJlIGlzIGFsc28gYSBtb3JlIGNvbXBsaWNhdGVkIGNhc2Ugd2hlcmUgeW91IGRvbid0IGhhdmUgY2xlYW4gImtleSIgdmFyaWFibGVzIGJ1dCB5b3Ugc3RpbGwgd2FudCB0byBqb2luIHR3byBkYXRhc2V0cy4gVGhpcyByZXF1aXJlcyBmdXp6eSBvciBhcHByb3hpbWF0ZSBtYXRjaGluZyB1c2luZyB2YXJpYWJsZXMgaW4gdGhlIHR3byBkYXRhIHNldHMsIHdoaWNoIGlzIGJleW9uZCB0aGUgc2NvcGUgb2Ygd2hhdCB3ZSBjYW4gY292ZXIgdG9kYXkuIEEgY2FzZSB3aGVyZSB0aGlzIG1pZ2h0IGhhcHBlbiBpcyB3aGVuIHlvdSBoYXZlIGNvdW50cnkgbmFtZXMgKG5vdCBJU08gY29kZXMpLCBhbmQgdmFyaWFudHMgc3VjaCBhcyBEUkMgdnMuIERlbW9jcmF0aWMgUmVwdWJsaWMgb2YgdGhlIENvbmdvIHZzLiBDb25nbywgRFIgKHllcywgdGhpcyBoYXBwZW5zIHJlZ3VsYXJseSkuCgpUb2RheSwgd2UncmUgYXNzdW1pbmcgdGhlcmUgaXMgc29tZSBjbGVhbiBrZXkgdG8gbWF0Y2ggb24gd2hlcmUgdmFsdWVzIG1hdGNoIGV4YWN0bHkuCgojIyMgRVhFUkNJU0UgMQoKSW1wb3J0IGRhdGEgYWJvdXQgZGVncmVlcyBhd2FyZGVkIGF0IE5vcnRod2VzdGVybi4gV2hhdCBjb2x1bW4ocykgY2FuIGJlIHVzZWQgYXMgYSBrZXkgdG8gam9pbiB0aGUgZGF0YXNldHMgdG9nZXRoZXI/CgpOb3RlOiB0aGlzIGlzbid0IGEgY29kZSBhbnN3ZXIgLSByZWFkIGluIHRoZSBkYXRhIGFuZCB0YWtlIGEgbG9vayBhdCB0aGUgZGF0YSBmcmFtZXMgdG8gc2VlIHdoYXQncyBjb21tb24gaW4gdGhlbS4KCmBgYHtyfQpiYWMgPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9udWl0cmNzL1ItaW50ZXJtZWRpYXRlLXRpZHl2ZXJzZS0yMDIzL21haW4vZGF0YS9iYWNfc2Nob29scy5jc3YiKQoKbWFzdGVycyA8LSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL251aXRyY3MvUi1pbnRlcm1lZGlhdGUtdGlkeXZlcnNlLTIwMjMvbWFpbi9kYXRhL21hc3RlcnNfc2Nob29scy5jc3YiKQoKZG9jIDwtIHJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vbnVpdHJjcy9SLWludGVybWVkaWF0ZS10aWR5dmVyc2UtMjAyMy9tYWluL2RhdGEvZG9jX3NjaG9vbHMuY3N2IikKYGBgCgojIyBCYWNrZ3JvdW5kOiBKb2luIFR5cGVzCgpUaGVyZSBhcmUgZGlmZmVyZW50IHdheXMgdG8gbWF0Y2ggdXAgcm93cyBpbiB0d28gZGF0YXNldHMuIFRoZXkgdmFyeSBiYXNlZCBvbiB3aGF0IGhhcHBlbnMgdG8gcm93cyB0aGF0IGRvbid0IG1hdGNoIHRvIHRoZSBvdGhlciBkYXRhIHNldCAod2hlcmUgdGhlIGtleSBmcm9tIG9uZSBkYXRhIHNldCBpc24ndCBwcmVzZW50IGluIHRoZSBvdGhlcikuCgo8aHR0cHM6Ly90YXZhcmVzaHVnby5naXRodWIuaW8vci1pbnRyby10aWR5dmVyc2UtZ2FwbWluZGVyL2ZpZy8wNy1kcGx5cl9qb2lucy5zdmc+CgpBbiAqKmlubmVyKiogam9pbiBvbmx5IGluY2x1ZGVzIGluIHRoZSByZXN1bHQgcm93cyB0aGF0IG1hdGNoZWQgaW4gYm90aCBkYXRhc2V0cy4KCkEgKipsZWZ0Kiogam9pbiBpbmNsdWRlcyBhbGwgcm93cyBmcm9tIHRoZSBmaXJzdCAobGVmdCkgZGF0YSBzZXQsIGZpbGxpbmcgaW4gbWlzc2luZyB2YWx1ZXMgd2hlcmUgdGhlcmUgaXNuJ3QgYSBtYXRjaCBpbiB0aGUgc2Vjb25kIGRhdGEgc2V0IChyaWdodCkuCgpBICoqcmlnaHQqKiBqb2luIGRvZXMgdGhlIG9wcG9zaXRlOiBrZWVwcyBhbGwgcm93cyBmcm9tIHRoZSBzZWNvbmQgKHJpZ2h0KSBkYXRhIHNldCwgYW5kIGZpbGxzIGluIG1pc3NpbmcgdmFsdWVzIHdoZXJlIHRoZXJlIGlzbid0IGEgbWF0Y2ggaW4gdGhlIGZpc3QuCgpBbiAqKm91dGVyKiogam9pbiBrZWVwcyBhbGwgcm93cyBmcm9tIGJvdGggZGF0YSBzZXRzLCBmaWxsaW5nIGluIG1pc3NpbmcgdmFsdWVzIHdoZXJlIHRoZXJlIGFyZW4ndCBtYXRjaGVzLgoKQW4gKiphbnRpKiotam9pbiBoZWxwcyB5b3UgZmluZCByb3dzIGluIGEgZGF0YXNldCB0aGF0IGRvIE5PVCBtYXRjaCB0byBhbm90aGVyLgoKIVtdKGltYWdlcy9qb2luX3R5cGVzLnBuZykKCkltYWdlIFNvdXJjZTogPGh0dHBzOi8vY2dobGV3aXMuY29tL2Jsb2cvam9pbnMvPgoKIyMgSm9pbnMgd2l0aCBge2RwbHlyfWAKCkVhY2ggb2YgdGhlIGpvaW4gdHlwZXMgaXMgYSBkaWZmZXJlbnQgZnVuY3Rpb24gaW4gYHtkcGx5cn1gOiBgaW5uZXJfam9pbigpYCwgYGxlZnRfam9pbigpYCwgYHJpZ2h0X2pvaW4oKWAsIGBmdWxsX2pvaW4oKWAgKHRoZSBsYXN0IG9uZSBpcyBhbiBvdXRlciBqb2luKS4gWW91IHVzZSB0aGVtIGFsbCBpbiB0aGUgc2FtZSB3YXkuCgpMZXQncyBzdGFydCB3aXRoIHRoZSBmaXJzdCBleGFtcGxlIGFib3ZlLiBJJ20gZ29pbmcgdG8gcmUtd3JpdGUgaXQgaW4gYSBzaW5nbGUgbGluZToKCmBgYHtyfQpsZWZ0X2pvaW4oY2FzZXMsIGRlYXRocywgYnk9ImNvdW50eUZJUFMiKQpgYGAKClRoZSBmaXJzdCBpbnB1dCwgY2FzZXMsIGlzIHRoZSAibGVmdCIgdGFibGUuIFRoZSBzZWNvbmQsIGRlYXRocywgaXMgdGhlICJyaWdodC4iIFRoZSBgYnk9ImNvdW50eUZJUFMiYCBpcyB0ZWxsaW5nIGl0IHdoYXQga2V5IHRvIHVzZSB0byBqb2luIC0tIGluIHRoaXMgY2FzZSwgdGhlcmUncyBhIGNvbHVtbiBuYW1lZCAiY291bnR5RklQUyIgaW4gYm90aCBkYXRhIGZyYW1lcyB0aGF0IHNob3VsZCBiZSB1c2VkIHRvIG1hdGNoIHVwIHJvd3MuCgpJbiB0aGUgb3V0cHV0LCBpdCBhZGRzIHRoZSBzdWZmaXggIi54IiB0byBhbnkgY29sdW1ucyBmcm9tIHRoZSBmaXJzdCBkYXRhIHNldCB3aGVyZSB0aGVyZSdzIGEgY29sdW1uIG9mIHRoZSBzYW1lIG5hbWUgaW4gdGhlIHNlY29uZCBkYXRhIHNldCwgYW5kIHVzZXMgdGhlIHN1ZmZpeCAiLnkiIGZvciB0aGUgcmV2ZXJzZS4gImNvdW50eUZJUFMiIGRvZXNuJ3QgZ2V0IGEgc3VmZml4IGJlY2F1c2UgaXQncyB0aGUgdmFyaWFibGUgd2UgdG9sZCBpdCB0byBqb2luIG9uLiAiY2FzZXMiIGFuZCAiZGVhdGhzIiBkb24ndCBnZXQgYSBzdWZmaXggYmVjYXVzZSB0aGVyZSBpc24ndCBhIGNvbHVtbiB3aXRoIHRob3NlIG5hbWVzIGluIHRoZSBvdGhlciBkYXRhIHNldC4KCk5vdGUgYWJvdmUgYW5kIGFsc28gYmVsb3csIEknbSBqdXN0IGpvaW5pbmcgYW5kIHByaW50aW5nIHRoZSByZXN1bHRpbmcgZGF0YSBmcmFtZS4gWW91IGNvdWxkIHNhdmUgdGhlIG91dHB1dCAodGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lKSBpbiBhIG5ldyB2YXJpYWJsZSB0aG91Z2g6CgpgYGB7cn0KY2FzZXNfZGVhdGhzIDwtIGxlZnRfam9pbihjYXNlcywgZGVhdGhzLCBieT0iY291bnR5RklQUyIpCmBgYAoKIyMjIFZhcmlhdGlvbnMgb24gYnkKCklmIEkgZG9uJ3QgdGVsbCBge2RwbHlyfWAgd2hhdCB0aGUga2V5IHZhcmlhYmxlcyBhcmUgdG8gdXNlIHRvIGpvaW4gdGhlIHRhYmxlcywgaXQgd2lsbCBkZWZhdWx0IHRvIHRyeWluZyB0byBqb2luIG9uIGFueSBjb2x1bW5zIHdpdGggdGhlIHNhbWUgbmFtZSBhY3Jvc3MgZGF0YSBmcmFtZXM6CgpgYGB7cn0KbGVmdF9qb2luKGNhc2VzLCBkZWF0aHMpCmBgYAoKQW5kIHRlbGwgbWUgd2hhdCBpdCBqb2luZWQgYnkgaW4gdGhlIG91dHB1dC4KCkJ1dCBpdCdzIGJlc3QgdG8gYmUgZXhwbGljaXQgYWJvdXQgaG93IHlvdSB3YW50IHRvIGpvaW4gdGhlIHRhYmxlcy4gSnVzdCBiZWNhdXNlIHR3byBjb2x1bW5zIGhhdmUgdGhlIHNhbWUgbmFtZSwgZG9lc24ndCBtZWFuIGFsbCBvZiB0aGUgZGF0YSBpcyB0aGUgc2FtZSEgSW4gdGhlIGNhc2Ugb2YgdGhlIGBjYXNlc2AgYW5kIGBkZWF0aHNgIGRhdGEgZnJhbWVzLCB0aGV5IGNvbWUgZnJvbSB0aGUgc2FtZSBzb3VyY2UuIFNvIGFsbCBvZiB0aGVzZSBjb2x1bW5zICpTSE9VTEQqIG1hdGNoLCBidXQgdGhleSBkb24ndCEKCmBgYHtyfQpsZWZ0X2pvaW4oY2FzZXMsIGRlYXRocywKICAgICAgICAgIGJ5PSJjb3VudHlGSVBTIikgJT4lCiAgZmlsdGVyKGNvdW50eS54ICE9IGNvdW50eS55KSAgCiMgdXNlIHNlbGVjdChjb3VudHkueCwgY291bnR5LnkpIHRvIHNlZSBkaWZmZXJlbmNlcwpgYGAKCklmIHdlIG1hdGNoZWQgYnkgY291bnR5RklQUyBhbmQgY291bnR5LCB3ZSdkIG1pc3MgdGhlc2UgbWF0Y2hlcy4KClNvbWV0aGluZyB0byBhbHdheXMgd2F0Y2ggb3V0IGZvciEgSXQncyB1c3VhbGx5IGJlc3QgdG8gam9pbiB1c2luZyB0aGUgbWluaW1hbCBzZXQgb2YgY29sdW1ucyBuZWVkZWQgdG8gaWRlbnRpZnkgdGhlIGNvcnJlY3QgbWF0Y2hlcy4KCldpdGggdGhlIGBjYXNlc2AgYW5kIGBkZWF0aHNgIGRhdGEgZnJhbWVzLCB0aGV5IGNhbWUgZnJvbSB0aGUgc2FtZSBkYXRhIHNvdXJjZSwgc28gdGhlIGtleSBjb2x1bW4gaGFzIHRoZSBzYW1lIG5hbWUgaW4gYm90aCBkYXRhIHNldHMuIEluIHRoZSBgZGVtb2AgZGF0YSBmcmFtZSwgdGhlIG5hbWUgb2YgdGhlIGNvbHVtbiB3aXRoIHRoZSBjb3VudHkgRklQUyBjb2RlIGlzIGRpZmZlcmVudCB0aG91Z2g6CgpgYGB7cn0KbmFtZXMoZGVtbykKYGBgCgpUbyBqb2luIHdpdGggdGhhdCwgd2UgdXNlIGEgbmFtZWQgdmVjdG9yIHdpdGggYGJ5YCwgd2l0aCB0aGUgZm9ybSBgYygiTmFtZSBpbiBmaXJzdCBkYXRhIHNldCI9Ik5hbWUgaW4gc2Vjb25kIGRhdGEgc2V0IilgOgoKYGBge3J9CmxlZnRfam9pbihjYXNlcywgZGVtbywgYnk9YygiY291bnR5RklQUyI9ImZpcHMiKSkKYGBgCgpIZXJlLCB3aGVyZSB0aGUgY29sdW1uIG5hbWVzIGFyZSBkaWZmZXJlbnQsIGl0IGp1c3Qga2VlcHMgb25lIGNvcHkgb2YgdGhlIGpvaW5pbmcga2V5IGNvbHVtbiwgYW5kIHVzZXMgdGhlIGNvbHVtbiBuYW1lIGZyb20gdGhlIGZpcnN0IChsZWZ0KSBkYXRhIHNldC4KCldlIGNhbiBhbHNvIGpvaW4gb24gbXVsdGlwbGUgY29sdW1ucywgd2hlcmUgZWFjaCBwYWlyIG9mIGNvbHVtbnMgd2Ugc3BlY2lmeSBuZWVkcyB0byBtYXRjaC4gRm9yIGV4YW1wbGUsIGluIHRoZSBjYXNlcyBhbmQgZGVhdGhzIGRhdGEsIHRoZXJlIGFyZSBzb21lIG9mIHRoZSBzYW1lIGNvbHVtbnMuIFdlIGNhbiByZXF1aXJlIG11bHRpcGxlIGNvbHVtbnMgdG8gbWF0Y2ggYnkgc3VwcGx5aW5nIGEgdmVjdG9yIG9mIG5hbWVzOgoKYGBge3J9CmxlZnRfam9pbihjYXNlcywgZGVhdGhzLCBieT1jKCJjb3VudHlGSVBTIiwgInN0YXRlIikpCmBgYAoKVGhpcyBnaXZlcyB1cyBhIGNsZWFuZXIgcmVzdWx0IHdpdGhvdXQgZHVwbGljYXRlIHJvd3MuIEJ1dCBtYWtlIHN1cmUgdGhhdCB0aGUgdmFsdWVzIGluIHRoZXNlIGNvbHVtbnMgcmVhbGx5IHNob3VsZCBhbGwgbWF0Y2ggLS0gaWYgbm90LCB5b3UnbGwgYmUgbWlzc2luZyBtYXRjaGVzLgoKIyMjIEVYRVJDSVNFIDIKCkpvaW4gKGBsZWZ0X2pvaW5gIGlzIGZpbmUpIHRoZSBgY2FzZXNgIGRhdGEgZnJhbWUgdG8gdGhlIGBzdGF0ZV9zdGF0c2AgZGF0YSBmcmFtZSB1c2luZyB0aGUgc3RhdGUgY29sdW1uIGluIGVhY2ggKCJzdGF0ZSIgaW4gYGNhc2VzYCBhbmQgIlN0YXRlIiBpbiBgc3RhdGVfc3RhdHNgKTsgY2FzZSBvZiB0aGUgdmFyaWFibGUgbmFtZSBtYXR0ZXJzISAoInN0YXRlIiBpcyBub3QgZXF1YWwgdG8gIlN0YXRlIiBhcyBjb2x1bW4gbmFtZXMpLgoKYGBge3J9CgoKYGBgCgojIyMgRVhFUkNJU0UgMwoKSm9pbiB0aGUgYmFjLCBtYXN0ZXJzLCBhbmQgZG9jIGRhdGEgZnJhbWVzIGltcG9ydGVkIGFib3ZlIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZS4KCllvdSBjYW4gc3RyaW5nIHRvZ2V0aGVyIG11bHRpcGxlIGpvaW4gY29tbWFuZHMgd2l0aCAlXD4lCgpgYGB7cn0KCmBgYAoKIyMgVW5tYXRjaGVkIFJvd3MKCkEgbGVmdCBqb2luIGtlZXBzIGFsbCBvZiB0aGUgcm93cyBpbiB0aGUgZmlyc3QgZGF0YSBzZXQsIHdoZXRoZXIgb3Igbm90IHRoZXkgbWF0Y2ggYSByb3cgaW4gdGhlIHNlY29uZC4gSG93IGRvIHdlIGZpbmQgcm93cyB0aGF0IGRpZG4ndCBtYXRjaD8KCllvdSBjYW4gdXNlIGBhbnRpX2pvaW4oKWAgdG8gZmluZCByb3dzIGluIG9uZSAodGhlIGZpcnN0KSBkYXRhIGZyYW1lIHRoYXQgZG9uJ3QgbWF0Y2ggdG8gYSByb3cgaW4gdGhlIHNlY29uZDoKCmBgYHtyfQphbnRpX2pvaW4oZGVhdGhzLCBjYXNlcywgCiAgICAgICAgICBieT1jKCJjb3VudHlGSVBTIikpIApgYGAKClRoaXMgb25seSByZXR1cm5zIHRoZSByb3dzIHRoYXQgZG9uJ3QgbWF0Y2guCgpOb3RlIHRoYXQgd2UgZG9uJ3QgZ2V0IGFueSBjb2x1bW5zIGZyb20gdGhlIGBjYXNlc2AgZGF0YSBmcmFtZSBiZWNhdXNlIHRoZXNlIGFyZSB0aGUgcm93cyBmcm9tIGBkZWF0aHNgIHRoYXQgZG9uJ3QgbWF0Y2ggdG8gY2FzZXMuIFRvIHNlZSB3aGF0IHJvd3MgaW4gYGNhc2VzYCBkb24ndCBtYXRjaCwgc3dpdGNoIHRoZSBvcmRlcjoKCmBgYHtyfQphbnRpX2pvaW4oY2FzZXMsIGRlYXRocywgCiAgICAgICAgICBieT1jKCJjb3VudHlGSVBTIikpIApgYGAKClRoZXkgYWxsIG1hdGNoISBCdXQgcmVtZW1iZXIsIHRoZXJlIGFyZSBzdGlsbCByb3dzIGluIGRlYXRocyB0aGF0IGFyZW4ndCByZXByZXNlbnRlZCBpbiB0aGUgcmVzdWx0LgoKSWYsIGluc3RlYWQgb2Ygc2VlaW5nIHRoZSBub24tbWF0Y2hpbmcgcm93cyBzZXBhcmF0ZWx5LCB5b3Ugd2FudCB0byBzZWUgd2hpY2ggb25lcyBkaWRuJ3QgbWF0Y2ggaW4gdGhlIGNvbnRleHQgb2YgdGhlIGpvaW5lZCBkYXRhIGZyYW1lLCB5b3UgY2FuIGxvb2sgZm9yIG1pc3NpbmcgdmFsdWVzIGluIHRoZSBqb2luZWQgY29sdW1ucy4KCmBgYHtyfQpsZWZ0X2pvaW4oZGVhdGhzLCBjYXNlcywgCiAgICAgICAgICBieT1jKCJjb3VudHlGSVBTIikpICU+JQogIGZpbHRlcihpcy5uYShjYXNlcykpCmBgYAoKQmV3YXJlIHRob3VnaCwgYmVjYXVzZSBpZiB0aGVyZSBhcmUgbGVnaXRpbWF0ZWx5IG1pc3NpbmcgdmFsdWVzIGluIHRoZSBjb2x1bW4geW91IGNoZWNrIChoZXJlLCBjYXNlcyksIHRoZW4geW91J2xsIGdldCBtaXhlZCByZXN1bHRzIGhlcmUuCgojIyMgRVhFUkNJU0UgNAoKRmluZCB0aGUgcm93cyBpbiBgZGVhdGhzYCB0aGF0IGRvbid0IGhhdmUgYSBtYXRjaCBpbiBgZGVtb2AgSGludDogam9pbiB3aXRoIGBkZWF0aHNgIChjb2x1bW4gImNvdW50eUZJUFMiKSBhbmQgYGRlbW9gIChjb2x1bW4gImZpcHMiKS4KCmBgYHtyfQoKCmBgYAoKIyMgQmV3YXJlIE1pc3NpbmcgVmFsdWVzCgpNaXNzaW5nIHZhbHVlczogYnkgZGVmYXVsdCwgYHtkcGx5cn1gIG1hdGNoZXMgbWlzc2luZyAoYE5BYCkgYXMgaWYgaXQgaXMgYSB2YWx1ZS4gU28gaWYgd2UgaGF2ZSB0d28gZGF0YSBmcmFtZXMsIGFuZCB0aGVyZSBhcmUgbWlzc2luZyB2YWx1ZXMgaW4gb3VyIGtleSBpZCB2YXJpYWJsZXMsIHRoZW4gdGhleSB3aWxsIG1hdGNoLCBldmVuIGlmIHRoZXJlIGFyZSBtdWx0aXBsZSBgTkFgcy4KCmBgYHtyfQphIDwtIHRpYmJsZShpZD1jKDEsIDIsIE5BKSwgdmFsPWMoMTAsIDIwLCAzMCkpCmIgPC0gdGliYmxlKGlkPWMoMiwgMywgTkEsIE5BKSwgdmFsMiA9IGMoImEiLCAiYiIsICJjIiwgImQiKSkKYQpiCmBgYAoKYGBge3J9CmxlZnRfam9pbihhLCBiLCBieT0iaWQiKQpgYGAKCklmIHdlIHdhbnQgdGhpcyB0byBub3QgaGFwcGVuLCB3ZSBjYW4gc2V0IHRoZSBgbmFfbWF0Y2hlc2AgYXJndW1lbnQgdG8gIm5ldmVyIgoKYGBge3J9CmxlZnRfam9pbihhLCBiLCBieT0iaWQiLCAKICAgICAgICAgIG5hX21hdGNoZXM9Im5ldmVyIikKYGBgCgpTb21ldGltZXMsIFIgd2lsbCB3YXJuIHlvdSB0aGF0IHRoZXJlIHRoZXJlIG1pZ2h0IGJlIGEgcHJvYmxlbS4KCmBgYHtyfQpjIDwtIHRpYmJsZShpZD1jKDIsIDMsIE5BLCBOQSksIHZhbDMgPSBjKCJ3IiwgIngiLCAieSIsICJ6IikpCmMKCmxlZnRfam9pbihiLCBjLCBieT0iaWQiKQpgYGAKClRoaXMgaXNuJ3QgYWN0dWFsbHkgcmVsYXRlZCB0byBtaXNzaW5nIHZhbHVlcywgYnV0IGl0J3Mgb25lIG9mIHRoZSB0aW1lcyB3aGVuIHRoaXMgaXMgbW9zdCBsaWtlbHkgdG8gaGFwcGVuIGJ5IG1pc3Rha2UuIFdoYXQgaXQgbWVhbnMgaXMgdGhhdCB0aGVyZSdzIG1vcmUgdGhhbiBvbmUgcm93IGluIHRoZSBmaXJzdCBkYXRhIHNldCB0aGF0IG1hdGNoZXMgdG8gdGhlIHNhbWUgcm93IGluIHRoZSBzZWNvbmQgZGF0YSBzZXQgQU5EIHRoZXJlIGFyZSByb3dzIGluIHRoZSBmaXJzdCBkYXRhIHNldCB0aGF0IGVhY2ggbWF0Y2ggdG8gbXVsdGlwbGUgcm93cyBpbiB0aGUgc2Vjb25kIGRhdGEgc2V0LgoKVGhpcyBjYW4gYmUgbGVnaXRpbWF0ZSwgYnV0IGl0J3Mgc29tZXRoaW5nIHlvdSdkIGFsd2F5cyB3YW50IHRvIGNoZWNrIHRvIGNvbmZpcm0gdGhhdCB0aGUgYmVoYXZpb3IgaXMgY29ycmVjdC4KCiMjIENsZWFudXAKClNvIHlvdSd2ZSBqb2luZWQgdHdvIGRhdGEgc2V0cywgYW5kIG5vdyB5b3UgaGF2ZSBhIGJ1bmNoIG9mIGNvbHVtbnMgd2l0aCAiLngiIGFuZCAiLnkiIChvciBvdGhlciB0eXBlcyBvZiByZXBlYXRzKS4gV2hhdCBkbyB5b3UgZG8/CgoqKk9wdGlvbiAxKio6IHNlbGVjdCBvbmx5IHRoZSBjb2x1bW5zIHlvdSByZWFsbHkgd2FudCBpbiB0aGUgcmVzdWx0IGFoZWFkIG9mIGRvaW5nIHRoZSBqb2luLiBOT1RFOiB0aGlzIGlzIG9uZSBvZiB0aGUgdmVyeSBmZXcgdGltZXMgSSBtaWdodCBuZXN0IGB7ZHBseXJ9YCBmdW5jdGlvbiBjYWxscwoKYGBge3J9CmNhc2VzICU+JQogIHNlbGVjdChjb3VudHlGSVBTLCBjb3VudHksIHN0YXRlLCBjYXNlcykgJT4lCiAgbGVmdF9qb2luKHNlbGVjdChkZW1vLCBmaXBzLCB0b3RhbCwgd2hpdGUsIGJsYWNrKSwKICAgICAgICAgICAgYnk9YygiY291bnR5RklQUyI9ImZpcHMiKSkKYGBgCgpXaHkgbmVzdCB0aGUgYHNlbGVjdGAgY2FsbCBpbnN0ZWFkIG9mIHNhdmluZyBhIHN1YnNldCBvZiB0aGUgZGF0YSBmaXJzdCwgYW5kIHVzaW5nIHRoYXQ/IE1vc3RseSBmb3Igc2l0dWF0aW9ucyB3aGVyZSB0aGUgZGF0YXNldHMgYXJlIGxhcmdlLCBhbmQgSSBkb24ndCB3YW50IGV4dHJhIGNvcGllcyBvZiB0aGUgZGF0YSBmcmFtZSBpbiBteSBlbnZpcm9ubWVudC4gSW4gdGhpcyBjYXNlLCB0aGUgZGF0YSBzZXRzIGFyZSBzbWFsbCBlbm91Z2ggdGhhdCBJIGNvdWxkIGRvIHRoaXMgaW5zdGVhZDoKCmBgYHtyfQpkZW1vX3N1YiA8LSBzZWxlY3QoZGVtbywgZmlwcywgdG90YWwsIHdoaXRlLCBibGFjaykKCmNhc2VzICU+JQogIHNlbGVjdChjb3VudHlGSVBTLCBjb3VudHksIHN0YXRlLCBjYXNlcykgJT4lCiAgbGVmdF9qb2luKGRlbW9fc3ViLAogICAgICAgICAgICBieT1jKCJjb3VudHlGSVBTIj0iZmlwcyIpKQpgYGAKCkJ1dCBJIGhhdmUgYGRlbW9fc3ViYCBoYW5naW5nIGFyb3VuZCBpbiBteSBlbnZpcm9ubWVudCB0aGVuIChJIGxpa2UgdG8ga2VlcCBteSBlbnZpcm9ubWVudCBjbGVhbiB3aGVuIHBvc3NpYmxlISkuCgoqKk9wdGlvbiAyKio6IHVzZSBgcmVuYW1lKClgIG9yIGBzZWxlY3QoKWAgb24gdGhlIHJlc3VsdCBvZiB0aGUgam9pbjoKCmBgYHtyfQpjYXNlcyAlPiUKICBsZWZ0X2pvaW4oZGVtbywKICAgICAgICAgICAgYnk9YygiY291bnR5RklQUyI9ImZpcHMiKSkgJT4lCiAgc2VsZWN0KGNvdW50eUZJUFMsIGNvdW50eT1jb3VudHkueCwgc3RhdGU9c3RhdGUueCwgCiAgICAgICAgIGNhc2VzLCB0b3RhbCwgd2hpdGUsIGJsYWNrKQpgYGAKCiMjIyBFWEVSQ0lTRSA1CgpDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggb25seSB0aGUgZm9sbG93aW5nIGNvbHVtbnM6IGNvdW50eUZJUFMsIHN0YXRlLCBkZWF0aHMsIG5vbl93aGl0ZSBwb3B1bGF0aW9uCgpIaW50OiB1c2UgdGhlIGBkZW1vYCBhbmQgYGRlYXRoc2AgZGF0YSBmcmFtZXMuCgpgYGB7cn0KCmBgYAoKIyBMZWFybmluZyBNb3JlCgpTZWUgdGhlIFJlbGF0aW9uYWwgRGF0YSBjaGFwdGVyIGluIHRoZSBib29rIFIgZm9yIERhdGEgU2NpZW5jZTogPGh0dHBzOi8vcjRkcy5oYWQuY28ubnovcmVsYXRpb25hbC1kYXRhLmh0bWw+CgojIFJlY2FwCgpZb3Ugbm93IGNhbiB1c2UgZGlmZmVyZW50IGB7ZHBseXJ9YCBqb2luIGNvbW1hbmRzLCBzdWNoIGFzIGBpbm5lcl9qb2luYCwgYG91dGVyX2pvaW5gLCBgbGVmdF9qb2luYCwgYHJpZ2h0X2pvaW5gLCBgYW50aV9qb2luYCwgdG8gam9pbiB0b2dldGhlciB5b3VyIGRhdGEgaW4gYSB3aWRlIHZhcmlldHkgb2Ygd2F5cy4KCk5leHQgc2Vzc2lvbjogdGhlIGB7dGlkeXJ9YCBmdW5jdGlvbnMgZm9yIHJlc2hhcGluZyB5b3VyIGRhdGEgdG8gZm9sbG93IHRoZSB0aWR5IGZvcm1hdDogYHBpdm90X2xvbmdlcmAsIGBwaXZvdF93aWRlcmAsIGBzZXBhcmF0ZWAgYW5kIGBzZXBhcmF0ZV9yb3dzYAoKIyBBbnN3ZXJzIHRvIHRoZSBleGVyY2lzZXMKCiMjIyBFWEVSQ0lTRSAxCgpgYGB7cn0KIyBWaWV3IHRoZSBkYXRhIGluIGVhY2ggZGF0YXNldApWaWV3KGJhYykKVmlldyhtYXN0ZXJzKQpWaWV3KGRvYykKCiMgV2hpY2ggY29sdW1ucyBhcmUgY29tbW9uPwpjKCJzY2hvb2wiLCAieWVhciIpICMgc2V0IG9mIGNvbW1vbiBjb2x1bW5zIHdoaWNoIHRvZ2V0aGVyIGNhbiBiZSB1c2VkIGFzIGEgS2V5CmBgYAoKIyMjIEVYRVJDSVNFIDIKCmBgYHtyfQojIGNoZWNrIGNvbHVtbiBuYW1lcwpuYW1lcyhjYXNlcykKbmFtZXMoc3RhdGVfc3RhdHMpCgojIHVzZSBhcHByb3ByaWF0ZSBqb2luIGtleSBzeW50YXgKbGVmdF9qb2luKGNhc2VzLCBzdGF0ZV9zdGF0cywgYnk9Yygic3RhdGUiPSJTdGF0ZSIpKQpgYGAKCiMjIyBFWEVSQ0lTRSAzCgpgYGB7cn0KIyBjaGVjayBjb2x1bW4gbmFtZXMKbmFtZXMoYmFjKQpuYW1lcyhtYXN0ZXJzKQpuYW1lcyhkb2MpCgojIHVzZSBhcHByb3ByaWF0ZSBqb2luIGtleSBzeW50YXgKYmFjICU+JSAKICBsZWZ0X2pvaW4obWFzdGVycywgYnk9Yygic2Nob29sIiwgInllYXIiKSkgJT4lIAogIGxlZnRfam9pbihkb2MsIGJ5PWMoInNjaG9vbCIsICJ5ZWFyIikpCmBgYAoKIyMjIEVYRVJDSVNFIDQKCmBgYHtyfQojIGNoZWNrIGNvbHVtbiBuYW1lcwpuYW1lcyhkZWF0aHMpCm5hbWVzKGRlbW8pCgojIHVzZSBhcHByb3ByaWF0ZSBqb2luIGtleSBzeW50YXgKYW50aV9qb2luKGRlYXRocywgZGVtbywgYnk9YygiY291bnR5RklQUyI9ImZpcHMiKSkKYGBgCgojIyMgRVhFUkNJU0UgNQoKYGBge3J9CiMgY2hlY2sgY29sdW1uIG5hbWVzCm5hbWVzKGRlYXRocykKbmFtZXMoZGVtbykKCiMgbGVmdCBqb2luIG9uIGRlYXRocyB0byBhdXRvbWF0aWNhbGx5IHNlbGVjdCBjb3VudHlGSVBTIGNvbHVtCmRlYXRocyAlPiUgCiAgbGVmdF9qb2luKGRlbW8sIGJ5PWMoImNvdW50eUZJUFMiPSJmaXBzIikpICU+JSAKICBzZWxlY3Qoc3RhdGU9c3RhdGUueCwgZGVhdGhzLCBub25fd2hpdGUpICMgcmVuYW1lIHN0YXRlIGNvbHVtbiB3aGlsZSBzZWxlY3RpbmcKYGBgCg==