The output of most of the R chunks isn’t included in the HTML version of the file to keep it to a more reasonable file size. You can run the code in R to see the output.

Setup

library(dplyr)
library(readr)

We know {dplyr} functions for working with a single data frame. {dplyr} also has functions for joining two data frames together.

Data

We’re going to use COVID-19 data by county and demographics by county for joining:

# county level data
demo <- read_csv("https://raw.githubusercontent.com/nuitrcs/R-intermediate-tidyverse-2023/main/data/county_demographics.csv", show_col_types = FALSE)

cases <- read_csv("https://raw.githubusercontent.com/nuitrcs/R-intermediate-tidyverse-2023/main/data/county_cases.csv", show_col_types = FALSE)

deaths <- read_csv("https://raw.githubusercontent.com/nuitrcs/R-intermediate-tidyverse-2023/main/data/county_deaths.csv", show_col_types = FALSE)

# state level data
state_stats <- read_csv("https://raw.githubusercontent.com/nuitrcs/R-intermediate-tidyverse-2023/main/data/state_covid.csv", show_col_types = FALSE)

I’ve introduced some missing data for the purposes of this workshop. Get the full COVID-19 case data from https://usafacts.org/visualizations/coronavirus-covid-19-spread-map/. COVID-19 case and death data included here is as of early July 2020. Demographic variables are census estimates for July 2019.
To learn more about COVID-19 and race, see https://covidtracking.com/race; this is the source of the state-level COVID-19 statistics used here.

What’s in the data sets:

demo           # demographics by county
cases          # covid case counts by county
deaths         # covid death counts by county
state_stats.   # covid case and death counts by state and demographics

County FIPS Codes are unique 5-digit codes that represent specific US counties. The first two digits represent the county’s state and the last three digits represent the county. For example “17031” is the FIPS Code for Cook County, Illinois.

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 NAs.

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.

Learning More

See the Relational Data chapter in the book R for Data Science: https://r4ds.had.co.nz/relational-data.html

Recap

You now can use different {dplyr} join commands, such as inner_join, outer_join, left_join, right_join, anti_join, to join together your data in a wide variety of ways.

Next session: the {tidyr} functions for reshaping your data to follow the tidy format: pivot_longer, pivot_wider, separate and separate_rows

Answers to the exercises

EXERCISE 1

# View the data in each dataset
View(bac)
View(masters)
View(doc)

# Which columns are common?
c("school", "year") # set of common columns which together can be used as a Key

EXERCISE 2

# check column names
names(cases)
names(state_stats)

# use appropriate join key syntax
left_join(cases, state_stats, by=c("state"="State"))

EXERCISE 3

# check column names
names(bac)
names(masters)
names(doc)

# use appropriate join key syntax
bac %>% 
  left_join(masters, by=c("school", "year")) %>% 
  left_join(doc, by=c("school", "year"))

EXERCISE 4

# check column names
names(deaths)
names(demo)

# use appropriate join key syntax
anti_join(deaths, demo, by=c("countyFIPS"="fips"))

EXERCISE 5

# check column names
names(deaths)
names(demo)

# left join on deaths to automatically select countyFIPS colum
deaths %>% 
  left_join(demo, by=c("countyFIPS"="fips")) %>% 
  select(state=state.x, deaths, non_white) # rename state column while selecting
LS0tCnRpdGxlOiAnZHBseXI6IGpvaW4nCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgplZGl0b3Jfb3B0aW9uczoKICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQotLS0KCmBgYHtyLCBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGV2YWw9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVycm9yPUZBTFNFLCBtZXNzYWdlPUZBTFNFKQpgYGAKCipUaGUgb3V0cHV0IG9mIG1vc3Qgb2YgdGhlIFIgY2h1bmtzIGlzbid0IGluY2x1ZGVkIGluIHRoZSBIVE1MIHZlcnNpb24gb2YgdGhlIGZpbGUgdG8ga2VlcCBpdCB0byBhIG1vcmUgcmVhc29uYWJsZSBmaWxlIHNpemUuIFlvdSBjYW4gcnVuIHRoZSBjb2RlIGluIFIgdG8gc2VlIHRoZSBvdXRwdXQuKgoKIyBTZXR1cAoKYGBge3IsIGV2YWw9VFJVRX0KbGlicmFyeShkcGx5cikKbGlicmFyeShyZWFkcikKYGBgCgpXZSBrbm93IGB7ZHBseXJ9YCBmdW5jdGlvbnMgZm9yIHdvcmtpbmcgd2l0aCBhIHNpbmdsZSBkYXRhIGZyYW1lLiBge2RwbHlyfWAgYWxzbyBoYXMgZnVuY3Rpb25zIGZvciBqb2luaW5nIHR3byBkYXRhIGZyYW1lcyB0b2dldGhlci4KCiMgRGF0YQoKV2UncmUgZ29pbmcgdG8gdXNlIENPVklELTE5IGRhdGEgYnkgY291bnR5IGFuZCBkZW1vZ3JhcGhpY3MgYnkgY291bnR5IGZvciBqb2luaW5nOgoKYGBge3IsIGV2YWw9VFJVRX0KIyBjb3VudHkgbGV2ZWwgZGF0YQpkZW1vIDwtIHJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vbnVpdHJjcy9SLWludGVybWVkaWF0ZS10aWR5dmVyc2UtMjAyMy9tYWluL2RhdGEvY291bnR5X2RlbW9ncmFwaGljcy5jc3YiLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKQoKY2FzZXMgPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9udWl0cmNzL1ItaW50ZXJtZWRpYXRlLXRpZHl2ZXJzZS0yMDIzL21haW4vZGF0YS9jb3VudHlfY2FzZXMuY3N2Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKCmRlYXRocyA8LSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL251aXRyY3MvUi1pbnRlcm1lZGlhdGUtdGlkeXZlcnNlLTIwMjMvbWFpbi9kYXRhL2NvdW50eV9kZWF0aHMuY3N2Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKCiMgc3RhdGUgbGV2ZWwgZGF0YQpzdGF0ZV9zdGF0cyA8LSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL251aXRyY3MvUi1pbnRlcm1lZGlhdGUtdGlkeXZlcnNlLTIwMjMvbWFpbi9kYXRhL3N0YXRlX2NvdmlkLmNzdiIsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpCmBgYAoKSSd2ZSBpbnRyb2R1Y2VkIHNvbWUgbWlzc2luZyBkYXRhIGZvciB0aGUgcHVycG9zZXMgb2YgdGhpcyB3b3Jrc2hvcC4gR2V0IHRoZSBmdWxsIENPVklELTE5IGNhc2UgZGF0YSBmcm9tIDxodHRwczovL3VzYWZhY3RzLm9yZy92aXN1YWxpemF0aW9ucy9jb3JvbmF2aXJ1cy1jb3ZpZC0xOS1zcHJlYWQtbWFwLz4uIENPVklELTE5IGNhc2UgYW5kIGRlYXRoIGRhdGEgaW5jbHVkZWQgaGVyZSBpcyBhcyBvZiBlYXJseSBKdWx5IDIwMjAuIERlbW9ncmFwaGljIHZhcmlhYmxlcyBhcmUgY2Vuc3VzIGVzdGltYXRlcyBmb3IgSnVseSAyMDE5LlwKVG8gbGVhcm4gbW9yZSBhYm91dCBDT1ZJRC0xOSBhbmQgcmFjZSwgc2VlIDxodHRwczovL2NvdmlkdHJhY2tpbmcuY29tL3JhY2U+OyB0aGlzIGlzIHRoZSBzb3VyY2Ugb2YgdGhlIHN0YXRlLWxldmVsIENPVklELTE5IHN0YXRpc3RpY3MgdXNlZCBoZXJlLgoKV2hhdCdzIGluIHRoZSBkYXRhIHNldHM6CgpgYGB7cn0KZGVtbyAgICAgICAgICAgIyBkZW1vZ3JhcGhpY3MgYnkgY291bnR5CmNhc2VzICAgICAgICAgICMgY292aWQgY2FzZSBjb3VudHMgYnkgY291bnR5CmRlYXRocyAgICAgICAgICMgY292aWQgZGVhdGggY291bnRzIGJ5IGNvdW50eQpzdGF0ZV9zdGF0cy4gICAjIGNvdmlkIGNhc2UgYW5kIGRlYXRoIGNvdW50cyBieSBzdGF0ZSBhbmQgZGVtb2dyYXBoaWNzCmBgYAoKQ291bnR5IEZJUFMgQ29kZXMgYXJlIHVuaXF1ZSA1LWRpZ2l0IGNvZGVzIHRoYXQgcmVwcmVzZW50IHNwZWNpZmljIFVTIGNvdW50aWVzLiBUaGUgZmlyc3QgdHdvIGRpZ2l0cyByZXByZXNlbnQgdGhlIGNvdW50eSdzIHN0YXRlIGFuZCB0aGUgbGFzdCB0aHJlZSBkaWdpdHMgcmVwcmVzZW50IHRoZSBjb3VudHkuIEZvciBleGFtcGxlICIxNzAzMSIgaXMgdGhlIEZJUFMgQ29kZSBmb3IgQ29vayBDb3VudHksIElsbGlub2lzLgoKIyBBZGRpbmcgUm93cyBhbmQgQ29sdW1ucwoKQmVmb3JlIHdlIGdldCB0byBqb2lucywgbGV0J3MgZmlyc3QgY292ZXIgYWRkaW5nIHJvd3Mgb3IgY29sdW1ucyB0byBhIGRhdGEgZnJhbWUuCgpge2RwbHlyfWAgaW5jbHVkZXMgYGJpbmRfcm93c2AgYW5kIGBiaW5kX2NvbHNgLCB3aGljaCBhcmUgcm91Z2hseSBlcXVpdmFsZW50IHRvIGJhc2UgUiBgcmJpbmRgIGFuZCBgY2JpbmRgLCBidXQgdGhleSBhcmUgYSBiaXQgZWFzaWVyIHRvIHdvcmsgd2l0aC4KClRvIGRlbW9uc3RyYXRlLCBsZXQncyBjcmVhdGUgc29tZSBzdWJzZXRzIG9mIHRoZSBkYXRhIHRvIHdvcmsgd2l0aCBmaXJzdDoKCmBgYHtyLCBldmFsPVRSVUV9CmRlbGF3YXJlIDwtIGZpbHRlcihkZW1vLCBzdGF0ZT09IkRlbGF3YXJlIikgJT4lCiAgc2VsZWN0KDE6NikKaGF3YWlpIDwtIGZpbHRlcihkZW1vLCBzdGF0ZT09Ikhhd2FpaSIpICU+JQogIHNlbGVjdCgxOjQsNzo5KQpgYGAKCmBgYHtyfQpkZWxhd2FyZQpoYXdhaWkKYGBgCgpXZSBoYXZlIHNvbWUgb3ZlcmxhcCBpbiB0aGUgY29sdW1uIG5hbWVzLCBidXQgdGhlIHNldCBvZiBjb2x1bW5zIGRvZXNuJ3QgbWF0Y2ggZXhhY3RseS4gVG8gY29tYmluZSB0aGVzZSBkYXRhc2V0cywgd2Ugd2FudCB0byBiaW5kIHRoZSByb3dzIChlYWNoIGlzIGEgY291bnR5KSB0b2dldGhlcjoKCmBgYHtyLCBldmFsPVRSVUV9CiMganVzdCBwcmludGluZywgbm90IHNhdmluZy4uLgpiaW5kX3Jvd3MoZGVsYXdhcmUsIGhhd2FpaSkKYGBgCgpgYmluZF9yb3dzYCBpbmNsdWRlcyBhbGwgY29sdW1ucyB0aGUgZXhpc3QgaW4gZWl0aGVyIGRhdGFzZXQgYW5kIGZpbGxzIGluIG1pc3NpbmcgdmFsdWVzIHdpdGggYE5BYC4gSXQgbWF0Y2hlcyBjb2x1bW5zIGJ5IGZ1bGwgbmFtZS4gSWYgd2UgdHJpZWQgdGhpcyB3aXRoIGByYmluZGAsIHdlJ2QgZ2V0IGFuIGVycm9yLCBiZWNhdXNlIHRoZSBzZXQgb2YgY29sdW1ucyBkb2Vzbid0IG1hdGNoLgoKKipXaGVuIHdvdWxkIHlvdSByZWFsbHkgZG8gdGhpcz8qKgoKTWF5YmUgeW91IGhhdmUgc2VwYXJhdGUgZGF0YSBmaWxlcyBmb3IgZWFjaCB5ZWFyIG9mIGRhdGEsIGFuZCB5b3UgbmVlZCB0byBjb21iaW5lIHRoZW0uIE9yIGZvciBlYWNoIHNjaG9vbCwgb3Igc3RhdGUsIG9yIGV4cGVyaW1lbnQgaXRlcmF0aW9uLiBBbnkgY2FzZSB3aGVyZSB0aGUgc2V0IG9mICoqdmFyaWFibGVzKiogaXMgKipzaW1pbGFyIGFjcm9zcyBtdWx0aXBsZSBkYXRhIHNldHMqKiB0aGF0IGFsbCBoYXZlIHRoZSBzYW1lIHVuaXQgb2Ygb2JzZXJ2YXRpb24gKHJvd3MgYXJlIHRoZSBzYW1lIHR5cGUgb2YgdGhpbmcgYWNyb3NzIGRhdGEgc2V0cyBhbmQgeW91IGhhdmUgc2ltaWxhciB2YXJpYWJsZXMpLgoKRm9yICoqYWRkaW5nIGNvbHVtbnMqKiwgd2UnZCBuZWVkIHRvIG1ha2Ugc3VyZSB0aGF0ICoqYm90aCBkYXRhIGZyYW1lcyBoYXZlIHRoZSBzYW1lIG51bWJlciBvZiByb3dzKiogYW5kIHRoZXkgYXBwZWFyIGluIHRoZSBzYW1lIG9yZGVyLiBHZW5lcmFsbHksIHlvdSBkb24ndCB3YW50IHRvIGBiaW5kX2NvbHNgLiBJZiB5b3UgaGF2ZSBzb21lIGNvbHVtbiB0aGF0IGlkZW50aWZpZXMgb2JzZXJ2YXRpb25zIGluIGVhY2ggZGF0YSBmcmFtZSwgeW91J2xsIHdhbnQgdG8gam9pbiB0aGUgZGF0YXNldHMgdG9nZXRoZXIgaW5zdGVhZCwgd2hpY2ggaXMgd2hhdCB3ZSdyZSBnb2luZyB0byBkbyBuZXh0LiBJZiB5b3UgYXJlIGp1c3QgdHJ5aW5nIHRvIGFkZCBhIGZldyBjb2x1bW5zIG9mIGRhdGEsIHVzZSBgbXV0YXRlYC4KCioqV2h5IHVzZSBqb2luIGluc3RlYWQgb2YgYGJpbmRfY29sc2A/KioKCldoaWxlIGl0IGRvZXMgaGFwcGVuLCBpdCdzIGRhbmdlcm91cyB0byBoYXZlIG11bHRpcGxlIGRhdGEgc2V0cyB3aXRoIHRoZSBzYW1lIHNldCBvZiByb3dzIHdpdGhvdXQgc29tZSBJRCB2YXJpYWJsZSAtLSB3aGVyZSB0aGV5J3JlIGp1c3QgbWF0Y2hlZCBieSB0aGUgb3JkZXIgb2YgdGhlIHJvd3M7IGl0J3MgdG9vIGVhc3kgZm9yIHRoZSBtYXRjaGluZyBiZXR3ZWVuIGRhdGFzZXRzIHRvIGdldCBtZXNzZWQgdXAuCgojIEpvaW5zCgpUaGUgdGVybWlub2xvZ3kgZm9yIGpvaW5pbmcgY29tZXMgZnJvbSBTUUwsIHdoaWNoIGlzIHVzZWQgdG8gaW50ZXJhY3Qgd2l0aCBkYXRhYmFzZXMuIEFuZCBge2RwbHlyfWAgY2FuIHdvcmsgZGlyZWN0bHkgd2l0aCBTUUwgZGF0YWJhc2VzLCB0cmFuc2xhdGluZyBge2RwbHlyfWAgY29tbWFuZHMgaW50byBTUUwsIHJ1bm5pbmcgdGhlbSBpbiB0aGUgZGF0YWJhc2UsIGFuZCB0aGVuIHJldHJpZXZpbmcgdGhlIHJlc3VsdHMuIFRoYXQncyBmb3IgYSBzZXBhcmF0ZSB3b3Jrc2hvcCwgYnV0IGlmIHlvdSBkbyB3YW50IHRvIHdvcmsgd2l0aCBhIGRhdGFiYXNlLCBrbm93IHRoYXQgeW91IGRvIG5vdCBuZWNlc3NhcmlseSBoYXZlIHRvIGV4cG9ydCB0aGUgZGF0YSBpbiBvcmRlciB0byB3b3JrIHdpdGggaXQgaW4gUi4gQW5kIHlvdSBjYW4gd3JpdGUgYHtkcGx5cn1gIGNvbW1hbmRzIGluc3RlYWQgb2YgU1FMIGNvbW1hbmRzIHRvIHdvcmsgd2l0aCB0aGUgZGF0YS4KCioqTm90ZSoqOiB0aGUgZXF1aXZhbGVudCBmdW5jdGlvbiBmb3Igam9pbmluZyBpbiBiYXNlIFIgaXMgYG1lcmdlKClgLgoKIyMgQmFja2dyb3VuZDogS2V5cwoKSm9pbnMgd29yayB3aGVuIHlvdSBoYXZlIHR3byBkYXRhIGZyYW1lcyAoY2FsbGVkIHRhYmxlcyBpbiBkYXRhYmFzZXMsIGJ1dCBzYW1lIGlkZWEpLCBhbmQgeW91IHdhbnQgdG8gbWVyZ2UgdGhlbSB0b2dldGhlci4gRWFjaCBkYXRhIHNldCBuZWVkcyB0byBoYXZlIHNvbWUgdHlwZSBvZiBpZGVudGlmaWVyLCBhIGtleSwgdGhhdCB0ZWxscyB5b3UgaG93IGEgcm93IGluIGRhdGFzZXQgMSBzaG91bGQgYmUgbWF0Y2hlZCBpbnRvIGRhdGFzZXQgMi4KCkZvciBleGFtcGxlLCBoZXJlLCB3ZSBoYXZlIHRocmVlIGNvdW50eSBkYXRhc2V0cywgYWxsIG9mIHdoaWNoIGhhdmUgZGlmZmVyZW50IGRhdGEgKGRpZmZlcmVudCB2YXJpYWJsZXMpLiBJbiBvdXIgY2FzZSwgZWFjaCBkYXRhc2V0IGluY2x1ZGVzIGFuIElEIHZhcmlhYmxlIGZvciBlYWNoIGNvdW50eS4gVGhlc2UgSURzIGFyZSB0aGUgc2FtZSBhY3Jvc3MgZGF0YXNldHMgYmVjYXVzZSBhbGwgMyB1c2UgdGhlIEZJUFMgY29kZSBmcm9tIHRoZSBVUyBDZW5zdXMuIFNvIGV2ZW4gdGhvdWdoIG91ciBkYXRhc2V0cyBoYXZlIGRpZmZlcmVudCBudW1iZXJzIG9mIHJvd3M6CgpgYGB7ciwgZXZhbD1UUlVFfQpuYW1lcyhjYXNlcykKbmFtZXMoZGVhdGhzKQpuYW1lcyhkZW1vKQoKbnJvdyhjYXNlcykKbnJvdyhkZWF0aHMpCm5yb3coZGVtbykKYGBgCgpXZSBoYXZlIGEga2V5IHRoYXQgd2lsbCBsaW5rIHJvd3MgaW4gdGhlIGRpZmZlcmVudCBkYXRhc2V0cyB0b2dldGhlci4KCkZvciBleGFtcGxlLCBsb29rIGF0IGEgY291bnR5IHRoYXQgaXMgaW4gYWxsIHRocmVlIGRhdGEgc2V0czoKCmBgYHtyLCBldmFsPVRSVUV9CmZpbHRlcihkZW1vLCBmaXBzID09IDEwMDkpCmZpbHRlcihjYXNlcywgY291bnR5RklQUyA9PSAxMDA5KQpmaWx0ZXIoZGVhdGhzLCBjb3VudHlGSVBTID09IDEwMDkpCmBgYAoKV2Ugd2FudCB0byBjb21iaW5lIHRoZSBkaWZmZXJlbnQgdmFyaWFibGVzIGZyb20gYWxsIDMgZGF0YSBmcmFtZXMgdG9nZXRoZXIgaW50byBhIHNpbmdsZSBvbmUuIFdlIGNhbid0IGp1c3QgYmluZCB0aGUgY29sdW1ucyB0b2dldGhlciB0aG91Z2gsIGJlY2F1c2UgdGhlIHNldCBvZiBjb3VudGllcyBpbiBlYWNoIGRhdGEgc2V0IGlzIGRpZmZlcmVudCAoc29tZSBjb3VudGllcyBhcmUgbWlzc2luZyBmcm9tIHNvbWUgZGF0YXNldHMpLgoKRXhhbXBsZSAod2UnbGwgdGFsayB0aHJvdWdoIHRoZSBjb2RlIHNob3J0bHkpOgoKYGBge3IsIGV2YWw9VFJVRX0KY2FzZXMgJT4lCiAgbGVmdF9qb2luKGRlYXRocywgYnk9ImNvdW50eUZJUFMiKQpgYGAKCldlIGFsc28gaGF2ZSBhIGRhdGEgc2V0IHdpdGggc3RhdGUgbGV2ZWwgZGF0YSBvbiBDT1ZJRCBkZWF0aHMuIFdlIG1pZ2h0IHdhbnQgdG8gYWRkIHRoaXMgc3RhdGUgbGV2ZWwgaW5mbyB0byBlYWNoIGNvdW50eS4gSW4gdGhhdCBjYXNlLCB5b3UgY291bGQgdXNlIHRoZSBzdGF0ZSBuYW1lIHRvIHB1bGwgdGhlIGNvcnJlY3Qgc3RhdGUgZGF0YSBmcm9tIHRoZSBkYXRhIHNldCBmb3IgZWFjaCByb3cgaW4gdGhlIGNvdW50eSBkYXRhIHNldC4KCmBgYHtyLCBldmFsPVRSVUV9CiMgV2UnbGwgdGFsayB0aHJvdWdoIHRoZSBjb2RlIG1vcmUgYmVsb3cKY2FzZXMgJT4lCiAgbGVmdF9qb2luKHN0YXRlX3N0YXRzLCBieT1jKCJzdGF0ZSI9IlN0YXRlIikpCmBgYAoKSW4gdGhpcyBzZWNvbmQgZXhhbXBsZSwgZWFjaCBzdGF0ZSBpbiB0aGUgc3RhdGUgZGF0YSBzZXQgbWF0Y2hlcyB0byBtdWx0aXBsZSByb3dzIChtdWx0aXBsZSBjb3VudGllcykgaW4gdGhlIGNvdW50eSBkYXRhIHNldCAtLSB0aGlzIGlzIE9LIGFuZCBleHBlY3RlZC4gWW91IGNhbiBoYXZlIDE6MSwgMTptYW55LCBvciBtYW55Om1hbnkgbWF0Y2hlcyBhY3Jvc3MgZGF0YSBzZXRzIChhbHRob3VnaCB0aGlzIGxhc3Qgb25lIGNhbiBnZXQgbWVzc3khIHNvIG11Y2ggc28sIHRoYXQgYHtkcGx5cn1gIHdpbGwgd2FybiB5b3Ugd2hlbiBpdCBoYXBwZW5zKS4KClRoZXJlIGNhbiBhbHNvIGJlIGNhc2VzIHdoZXJlIHRoZSBjb21iaW5hdGlvbiBvZiB0d28gZGlmZmVyZW50IHZhcmlhYmxlcyBpcyB0aGUgImtleSIgLS0gd2hlcmUgYm90aCBjb2x1bW5zIG5lZWQgdG8gbWF0Y2ggZm9yIHRoZSBkYXRhIHNldHMgdG8gYmUgam9pbmVkIGNvcnJlY3RseS4KClRoZXJlIGlzIGFsc28gYSBtb3JlIGNvbXBsaWNhdGVkIGNhc2Ugd2hlcmUgeW91IGRvbid0IGhhdmUgY2xlYW4gImtleSIgdmFyaWFibGVzIGJ1dCB5b3Ugc3RpbGwgd2FudCB0byBqb2luIHR3byBkYXRhc2V0cy4gVGhpcyByZXF1aXJlcyBmdXp6eSBvciBhcHByb3hpbWF0ZSBtYXRjaGluZyB1c2luZyB2YXJpYWJsZXMgaW4gdGhlIHR3byBkYXRhIHNldHMsIHdoaWNoIGlzIGJleW9uZCB0aGUgc2NvcGUgb2Ygd2hhdCB3ZSBjYW4gY292ZXIgdG9kYXkuIEEgY2FzZSB3aGVyZSB0aGlzIG1pZ2h0IGhhcHBlbiBpcyB3aGVuIHlvdSBoYXZlIGNvdW50cnkgbmFtZXMgKG5vdCBJU08gY29kZXMpLCBhbmQgdmFyaWFudHMgc3VjaCBhcyBEUkMgdnMuIERlbW9jcmF0aWMgUmVwdWJsaWMgb2YgdGhlIENvbmdvIHZzLiBDb25nbywgRFIgKHllcywgdGhpcyBoYXBwZW5zIHJlZ3VsYXJseSkuCgpUb2RheSwgd2UncmUgYXNzdW1pbmcgdGhlcmUgaXMgc29tZSBjbGVhbiBrZXkgdG8gbWF0Y2ggb24gd2hlcmUgdmFsdWVzIG1hdGNoIGV4YWN0bHkuCgojIyMgRVhFUkNJU0UgMQoKSW1wb3J0IGRhdGEgYWJvdXQgZGVncmVlcyBhd2FyZGVkIGF0IE5vcnRod2VzdGVybi4gV2hhdCBjb2x1bW4ocykgY2FuIGJlIHVzZWQgYXMgYSBrZXkgdG8gam9pbiB0aGUgZGF0YXNldHMgdG9nZXRoZXI/CgpOb3RlOiB0aGlzIGlzbid0IGEgY29kZSBhbnN3ZXIgLSByZWFkIGluIHRoZSBkYXRhIGFuZCB0YWtlIGEgbG9vayBhdCB0aGUgZGF0YSBmcmFtZXMgdG8gc2VlIHdoYXQncyBjb21tb24gaW4gdGhlbS4KCmBgYHtyfQpiYWMgPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9udWl0cmNzL1ItaW50ZXJtZWRpYXRlLXRpZHl2ZXJzZS0yMDIzL21haW4vZGF0YS9iYWNfc2Nob29scy5jc3YiKQoKbWFzdGVycyA8LSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL251aXRyY3MvUi1pbnRlcm1lZGlhdGUtdGlkeXZlcnNlLTIwMjMvbWFpbi9kYXRhL21hc3RlcnNfc2Nob29scy5jc3YiKQoKZG9jIDwtIHJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vbnVpdHJjcy9SLWludGVybWVkaWF0ZS10aWR5dmVyc2UtMjAyMy9tYWluL2RhdGEvZG9jX3NjaG9vbHMuY3N2IikKYGBgCgojIyBCYWNrZ3JvdW5kOiBKb2luIFR5cGVzCgpUaGVyZSBhcmUgZGlmZmVyZW50IHdheXMgdG8gbWF0Y2ggdXAgcm93cyBpbiB0d28gZGF0YXNldHMuIFRoZXkgdmFyeSBiYXNlZCBvbiB3aGF0IGhhcHBlbnMgdG8gcm93cyB0aGF0IGRvbid0IG1hdGNoIHRvIHRoZSBvdGhlciBkYXRhIHNldCAod2hlcmUgdGhlIGtleSBmcm9tIG9uZSBkYXRhIHNldCBpc24ndCBwcmVzZW50IGluIHRoZSBvdGhlcikuCgo8aHR0cHM6Ly90YXZhcmVzaHVnby5naXRodWIuaW8vci1pbnRyby10aWR5dmVyc2UtZ2FwbWluZGVyL2ZpZy8wNy1kcGx5cl9qb2lucy5zdmc+CgpBbiAqKmlubmVyKiogam9pbiBvbmx5IGluY2x1ZGVzIGluIHRoZSByZXN1bHQgcm93cyB0aGF0IG1hdGNoZWQgaW4gYm90aCBkYXRhc2V0cy4KCkEgKipsZWZ0Kiogam9pbiBpbmNsdWRlcyBhbGwgcm93cyBmcm9tIHRoZSBmaXJzdCAobGVmdCkgZGF0YSBzZXQsIGZpbGxpbmcgaW4gbWlzc2luZyB2YWx1ZXMgd2hlcmUgdGhlcmUgaXNuJ3QgYSBtYXRjaCBpbiB0aGUgc2Vjb25kIGRhdGEgc2V0IChyaWdodCkuCgpBICoqcmlnaHQqKiBqb2luIGRvZXMgdGhlIG9wcG9zaXRlOiBrZWVwcyBhbGwgcm93cyBmcm9tIHRoZSBzZWNvbmQgKHJpZ2h0KSBkYXRhIHNldCwgYW5kIGZpbGxzIGluIG1pc3NpbmcgdmFsdWVzIHdoZXJlIHRoZXJlIGlzbid0IGEgbWF0Y2ggaW4gdGhlIGZpc3QuCgpBbiAqKm91dGVyKiogam9pbiBrZWVwcyBhbGwgcm93cyBmcm9tIGJvdGggZGF0YSBzZXRzLCBmaWxsaW5nIGluIG1pc3NpbmcgdmFsdWVzIHdoZXJlIHRoZXJlIGFyZW4ndCBtYXRjaGVzLgoKQW4gKiphbnRpKiotam9pbiBoZWxwcyB5b3UgZmluZCByb3dzIGluIGEgZGF0YXNldCB0aGF0IGRvIE5PVCBtYXRjaCB0byBhbm90aGVyLgoKIVtdKGltYWdlcy9qb2luX3R5cGVzLnBuZykKCkltYWdlIFNvdXJjZTogPGh0dHBzOi8vY2dobGV3aXMuY29tL2Jsb2cvam9pbnMvPgoKIyMgSm9pbnMgd2l0aCBge2RwbHlyfWAKCkVhY2ggb2YgdGhlIGpvaW4gdHlwZXMgaXMgYSBkaWZmZXJlbnQgZnVuY3Rpb24gaW4gYHtkcGx5cn1gOiBgaW5uZXJfam9pbigpYCwgYGxlZnRfam9pbigpYCwgYHJpZ2h0X2pvaW4oKWAsIGBmdWxsX2pvaW4oKWAgKHRoZSBsYXN0IG9uZSBpcyBhbiBvdXRlciBqb2luKS4gWW91IHVzZSB0aGVtIGFsbCBpbiB0aGUgc2FtZSB3YXkuCgpMZXQncyBzdGFydCB3aXRoIHRoZSBmaXJzdCBleGFtcGxlIGFib3ZlLiBJJ20gZ29pbmcgdG8gcmUtd3JpdGUgaXQgaW4gYSBzaW5nbGUgbGluZToKCmBgYHtyfQpsZWZ0X2pvaW4oY2FzZXMsIGRlYXRocywgYnk9ImNvdW50eUZJUFMiKQpgYGAKClRoZSBmaXJzdCBpbnB1dCwgY2FzZXMsIGlzIHRoZSAibGVmdCIgdGFibGUuIFRoZSBzZWNvbmQsIGRlYXRocywgaXMgdGhlICJyaWdodC4iIFRoZSBgYnk9ImNvdW50eUZJUFMiYCBpcyB0ZWxsaW5nIGl0IHdoYXQga2V5IHRvIHVzZSB0byBqb2luIC0tIGluIHRoaXMgY2FzZSwgdGhlcmUncyBhIGNvbHVtbiBuYW1lZCAiY291bnR5RklQUyIgaW4gYm90aCBkYXRhIGZyYW1lcyB0aGF0IHNob3VsZCBiZSB1c2VkIHRvIG1hdGNoIHVwIHJvd3MuCgpJbiB0aGUgb3V0cHV0LCBpdCBhZGRzIHRoZSBzdWZmaXggIi54IiB0byBhbnkgY29sdW1ucyBmcm9tIHRoZSBmaXJzdCBkYXRhIHNldCB3aGVyZSB0aGVyZSdzIGEgY29sdW1uIG9mIHRoZSBzYW1lIG5hbWUgaW4gdGhlIHNlY29uZCBkYXRhIHNldCwgYW5kIHVzZXMgdGhlIHN1ZmZpeCAiLnkiIGZvciB0aGUgcmV2ZXJzZS4gImNvdW50eUZJUFMiIGRvZXNuJ3QgZ2V0IGEgc3VmZml4IGJlY2F1c2UgaXQncyB0aGUgdmFyaWFibGUgd2UgdG9sZCBpdCB0byBqb2luIG9uLiAiY2FzZXMiIGFuZCAiZGVhdGhzIiBkb24ndCBnZXQgYSBzdWZmaXggYmVjYXVzZSB0aGVyZSBpc24ndCBhIGNvbHVtbiB3aXRoIHRob3NlIG5hbWVzIGluIHRoZSBvdGhlciBkYXRhIHNldC4KCk5vdGUgYWJvdmUgYW5kIGFsc28gYmVsb3csIEknbSBqdXN0IGpvaW5pbmcgYW5kIHByaW50aW5nIHRoZSByZXN1bHRpbmcgZGF0YSBmcmFtZS4gWW91IGNvdWxkIHNhdmUgdGhlIG91dHB1dCAodGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lKSBpbiBhIG5ldyB2YXJpYWJsZSB0aG91Z2g6CgpgYGB7cn0KY2FzZXNfZGVhdGhzIDwtIGxlZnRfam9pbihjYXNlcywgZGVhdGhzLCBieT0iY291bnR5RklQUyIpCmBgYAoKIyMjIFZhcmlhdGlvbnMgb24gYnkKCklmIEkgZG9uJ3QgdGVsbCBge2RwbHlyfWAgd2hhdCB0aGUga2V5IHZhcmlhYmxlcyBhcmUgdG8gdXNlIHRvIGpvaW4gdGhlIHRhYmxlcywgaXQgd2lsbCBkZWZhdWx0IHRvIHRyeWluZyB0byBqb2luIG9uIGFueSBjb2x1bW5zIHdpdGggdGhlIHNhbWUgbmFtZSBhY3Jvc3MgZGF0YSBmcmFtZXM6CgpgYGB7cn0KbGVmdF9qb2luKGNhc2VzLCBkZWF0aHMpCmBgYAoKQW5kIHRlbGwgbWUgd2hhdCBpdCBqb2luZWQgYnkgaW4gdGhlIG91dHB1dC4KCkJ1dCBpdCdzIGJlc3QgdG8gYmUgZXhwbGljaXQgYWJvdXQgaG93IHlvdSB3YW50IHRvIGpvaW4gdGhlIHRhYmxlcy4gSnVzdCBiZWNhdXNlIHR3byBjb2x1bW5zIGhhdmUgdGhlIHNhbWUgbmFtZSwgZG9lc24ndCBtZWFuIGFsbCBvZiB0aGUgZGF0YSBpcyB0aGUgc2FtZSEgSW4gdGhlIGNhc2Ugb2YgdGhlIGBjYXNlc2AgYW5kIGBkZWF0aHNgIGRhdGEgZnJhbWVzLCB0aGV5IGNvbWUgZnJvbSB0aGUgc2FtZSBzb3VyY2UuIFNvIGFsbCBvZiB0aGVzZSBjb2x1bW5zICpTSE9VTEQqIG1hdGNoLCBidXQgdGhleSBkb24ndCEKCmBgYHtyfQpsZWZ0X2pvaW4oY2FzZXMsIGRlYXRocywKICAgICAgICAgIGJ5PSJjb3VudHlGSVBTIikgJT4lCiAgZmlsdGVyKGNvdW50eS54ICE9IGNvdW50eS55KSAgCiMgdXNlIHNlbGVjdChjb3VudHkueCwgY291bnR5LnkpIHRvIHNlZSBkaWZmZXJlbmNlcwpgYGAKCklmIHdlIG1hdGNoZWQgYnkgY291bnR5RklQUyBhbmQgY291bnR5LCB3ZSdkIG1pc3MgdGhlc2UgbWF0Y2hlcy4KClNvbWV0aGluZyB0byBhbHdheXMgd2F0Y2ggb3V0IGZvciEgSXQncyB1c3VhbGx5IGJlc3QgdG8gam9pbiB1c2luZyB0aGUgbWluaW1hbCBzZXQgb2YgY29sdW1ucyBuZWVkZWQgdG8gaWRlbnRpZnkgdGhlIGNvcnJlY3QgbWF0Y2hlcy4KCldpdGggdGhlIGBjYXNlc2AgYW5kIGBkZWF0aHNgIGRhdGEgZnJhbWVzLCB0aGV5IGNhbWUgZnJvbSB0aGUgc2FtZSBkYXRhIHNvdXJjZSwgc28gdGhlIGtleSBjb2x1bW4gaGFzIHRoZSBzYW1lIG5hbWUgaW4gYm90aCBkYXRhIHNldHMuIEluIHRoZSBgZGVtb2AgZGF0YSBmcmFtZSwgdGhlIG5hbWUgb2YgdGhlIGNvbHVtbiB3aXRoIHRoZSBjb3VudHkgRklQUyBjb2RlIGlzIGRpZmZlcmVudCB0aG91Z2g6CgpgYGB7cn0KbmFtZXMoZGVtbykKYGBgCgpUbyBqb2luIHdpdGggdGhhdCwgd2UgdXNlIGEgbmFtZWQgdmVjdG9yIHdpdGggYGJ5YCwgd2l0aCB0aGUgZm9ybSBgYygiTmFtZSBpbiBmaXJzdCBkYXRhIHNldCI9Ik5hbWUgaW4gc2Vjb25kIGRhdGEgc2V0IilgOgoKYGBge3J9CmxlZnRfam9pbihjYXNlcywgZGVtbywgYnk9YygiY291bnR5RklQUyI9ImZpcHMiKSkKYGBgCgpIZXJlLCB3aGVyZSB0aGUgY29sdW1uIG5hbWVzIGFyZSBkaWZmZXJlbnQsIGl0IGp1c3Qga2VlcHMgb25lIGNvcHkgb2YgdGhlIGpvaW5pbmcga2V5IGNvbHVtbiwgYW5kIHVzZXMgdGhlIGNvbHVtbiBuYW1lIGZyb20gdGhlIGZpcnN0IChsZWZ0KSBkYXRhIHNldC4KCldlIGNhbiBhbHNvIGpvaW4gb24gbXVsdGlwbGUgY29sdW1ucywgd2hlcmUgZWFjaCBwYWlyIG9mIGNvbHVtbnMgd2Ugc3BlY2lmeSBuZWVkcyB0byBtYXRjaC4gRm9yIGV4YW1wbGUsIGluIHRoZSBjYXNlcyBhbmQgZGVhdGhzIGRhdGEsIHRoZXJlIGFyZSBzb21lIG9mIHRoZSBzYW1lIGNvbHVtbnMuIFdlIGNhbiByZXF1aXJlIG11bHRpcGxlIGNvbHVtbnMgdG8gbWF0Y2ggYnkgc3VwcGx5aW5nIGEgdmVjdG9yIG9mIG5hbWVzOgoKYGBge3J9CmxlZnRfam9pbihjYXNlcywgZGVhdGhzLCBieT1jKCJjb3VudHlGSVBTIiwgInN0YXRlIikpCmBgYAoKVGhpcyBnaXZlcyB1cyBhIGNsZWFuZXIgcmVzdWx0IHdpdGhvdXQgZHVwbGljYXRlIHJvd3MuIEJ1dCBtYWtlIHN1cmUgdGhhdCB0aGUgdmFsdWVzIGluIHRoZXNlIGNvbHVtbnMgcmVhbGx5IHNob3VsZCBhbGwgbWF0Y2ggLS0gaWYgbm90LCB5b3UnbGwgYmUgbWlzc2luZyBtYXRjaGVzLgoKIyMjIEVYRVJDSVNFIDIKCkpvaW4gKGBsZWZ0X2pvaW5gIGlzIGZpbmUpIHRoZSBgY2FzZXNgIGRhdGEgZnJhbWUgdG8gdGhlIGBzdGF0ZV9zdGF0c2AgZGF0YSBmcmFtZSB1c2luZyB0aGUgc3RhdGUgY29sdW1uIGluIGVhY2ggKCJzdGF0ZSIgaW4gYGNhc2VzYCBhbmQgIlN0YXRlIiBpbiBgc3RhdGVfc3RhdHNgKTsgY2FzZSBvZiB0aGUgdmFyaWFibGUgbmFtZSBtYXR0ZXJzISAoInN0YXRlIiBpcyBub3QgZXF1YWwgdG8gIlN0YXRlIiBhcyBjb2x1bW4gbmFtZXMpLgoKYGBge3J9CgoKYGBgCgojIyMgRVhFUkNJU0UgMwoKSm9pbiB0aGUgYmFjLCBtYXN0ZXJzLCBhbmQgZG9jIGRhdGEgZnJhbWVzIGltcG9ydGVkIGFib3ZlIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZS4KCllvdSBjYW4gc3RyaW5nIHRvZ2V0aGVyIG11bHRpcGxlIGpvaW4gY29tbWFuZHMgd2l0aCAlXD4lCgpgYGB7cn0KCmBgYAoKIyMgVW5tYXRjaGVkIFJvd3MKCkEgbGVmdCBqb2luIGtlZXBzIGFsbCBvZiB0aGUgcm93cyBpbiB0aGUgZmlyc3QgZGF0YSBzZXQsIHdoZXRoZXIgb3Igbm90IHRoZXkgbWF0Y2ggYSByb3cgaW4gdGhlIHNlY29uZC4gSG93IGRvIHdlIGZpbmQgcm93cyB0aGF0IGRpZG4ndCBtYXRjaD8KCllvdSBjYW4gdXNlIGBhbnRpX2pvaW4oKWAgdG8gZmluZCByb3dzIGluIG9uZSAodGhlIGZpcnN0KSBkYXRhIGZyYW1lIHRoYXQgZG9uJ3QgbWF0Y2ggdG8gYSByb3cgaW4gdGhlIHNlY29uZDoKCmBgYHtyfQphbnRpX2pvaW4oZGVhdGhzLCBjYXNlcywgCiAgICAgICAgICBieT1jKCJjb3VudHlGSVBTIikpIApgYGAKClRoaXMgb25seSByZXR1cm5zIHRoZSByb3dzIHRoYXQgZG9uJ3QgbWF0Y2guCgpOb3RlIHRoYXQgd2UgZG9uJ3QgZ2V0IGFueSBjb2x1bW5zIGZyb20gdGhlIGBjYXNlc2AgZGF0YSBmcmFtZSBiZWNhdXNlIHRoZXNlIGFyZSB0aGUgcm93cyBmcm9tIGBkZWF0aHNgIHRoYXQgZG9uJ3QgbWF0Y2ggdG8gY2FzZXMuIFRvIHNlZSB3aGF0IHJvd3MgaW4gYGNhc2VzYCBkb24ndCBtYXRjaCwgc3dpdGNoIHRoZSBvcmRlcjoKCmBgYHtyfQphbnRpX2pvaW4oY2FzZXMsIGRlYXRocywgCiAgICAgICAgICBieT1jKCJjb3VudHlGSVBTIikpIApgYGAKClRoZXkgYWxsIG1hdGNoISBCdXQgcmVtZW1iZXIsIHRoZXJlIGFyZSBzdGlsbCByb3dzIGluIGRlYXRocyB0aGF0IGFyZW4ndCByZXByZXNlbnRlZCBpbiB0aGUgcmVzdWx0LgoKSWYsIGluc3RlYWQgb2Ygc2VlaW5nIHRoZSBub24tbWF0Y2hpbmcgcm93cyBzZXBhcmF0ZWx5LCB5b3Ugd2FudCB0byBzZWUgd2hpY2ggb25lcyBkaWRuJ3QgbWF0Y2ggaW4gdGhlIGNvbnRleHQgb2YgdGhlIGpvaW5lZCBkYXRhIGZyYW1lLCB5b3UgY2FuIGxvb2sgZm9yIG1pc3NpbmcgdmFsdWVzIGluIHRoZSBqb2luZWQgY29sdW1ucy4KCmBgYHtyfQpsZWZ0X2pvaW4oZGVhdGhzLCBjYXNlcywgCiAgICAgICAgICBieT1jKCJjb3VudHlGSVBTIikpICU+JQogIGZpbHRlcihpcy5uYShjYXNlcykpCmBgYAoKQmV3YXJlIHRob3VnaCwgYmVjYXVzZSBpZiB0aGVyZSBhcmUgbGVnaXRpbWF0ZWx5IG1pc3NpbmcgdmFsdWVzIGluIHRoZSBjb2x1bW4geW91IGNoZWNrIChoZXJlLCBjYXNlcyksIHRoZW4geW91J2xsIGdldCBtaXhlZCByZXN1bHRzIGhlcmUuCgojIyMgRVhFUkNJU0UgNAoKRmluZCB0aGUgcm93cyBpbiBgZGVhdGhzYCB0aGF0IGRvbid0IGhhdmUgYSBtYXRjaCBpbiBgZGVtb2AgSGludDogam9pbiB3aXRoIGBkZWF0aHNgIChjb2x1bW4gImNvdW50eUZJUFMiKSBhbmQgYGRlbW9gIChjb2x1bW4gImZpcHMiKS4KCmBgYHtyfQoKCmBgYAoKIyMgQmV3YXJlIE1pc3NpbmcgVmFsdWVzCgpNaXNzaW5nIHZhbHVlczogYnkgZGVmYXVsdCwgYHtkcGx5cn1gIG1hdGNoZXMgbWlzc2luZyAoYE5BYCkgYXMgaWYgaXQgaXMgYSB2YWx1ZS4gU28gaWYgd2UgaGF2ZSB0d28gZGF0YSBmcmFtZXMsIGFuZCB0aGVyZSBhcmUgbWlzc2luZyB2YWx1ZXMgaW4gb3VyIGtleSBpZCB2YXJpYWJsZXMsIHRoZW4gdGhleSB3aWxsIG1hdGNoLCBldmVuIGlmIHRoZXJlIGFyZSBtdWx0aXBsZSBgTkFgcy4KCmBgYHtyfQphIDwtIHRpYmJsZShpZD1jKDEsIDIsIE5BKSwgdmFsPWMoMTAsIDIwLCAzMCkpCmIgPC0gdGliYmxlKGlkPWMoMiwgMywgTkEsIE5BKSwgdmFsMiA9IGMoImEiLCAiYiIsICJjIiwgImQiKSkKYQpiCmBgYAoKYGBge3J9CmxlZnRfam9pbihhLCBiLCBieT0iaWQiKQpgYGAKCklmIHdlIHdhbnQgdGhpcyB0byBub3QgaGFwcGVuLCB3ZSBjYW4gc2V0IHRoZSBgbmFfbWF0Y2hlc2AgYXJndW1lbnQgdG8gIm5ldmVyIgoKYGBge3J9CmxlZnRfam9pbihhLCBiLCBieT0iaWQiLCAKICAgICAgICAgIG5hX21hdGNoZXM9Im5ldmVyIikKYGBgCgpTb21ldGltZXMsIFIgd2lsbCB3YXJuIHlvdSB0aGF0IHRoZXJlIHRoZXJlIG1pZ2h0IGJlIGEgcHJvYmxlbS4KCmBgYHtyfQpjIDwtIHRpYmJsZShpZD1jKDIsIDMsIE5BLCBOQSksIHZhbDMgPSBjKCJ3IiwgIngiLCAieSIsICJ6IikpCmMKCmxlZnRfam9pbihiLCBjLCBieT0iaWQiKQpgYGAKClRoaXMgaXNuJ3QgYWN0dWFsbHkgcmVsYXRlZCB0byBtaXNzaW5nIHZhbHVlcywgYnV0IGl0J3Mgb25lIG9mIHRoZSB0aW1lcyB3aGVuIHRoaXMgaXMgbW9zdCBsaWtlbHkgdG8gaGFwcGVuIGJ5IG1pc3Rha2UuIFdoYXQgaXQgbWVhbnMgaXMgdGhhdCB0aGVyZSdzIG1vcmUgdGhhbiBvbmUgcm93IGluIHRoZSBmaXJzdCBkYXRhIHNldCB0aGF0IG1hdGNoZXMgdG8gdGhlIHNhbWUgcm93IGluIHRoZSBzZWNvbmQgZGF0YSBzZXQgQU5EIHRoZXJlIGFyZSByb3dzIGluIHRoZSBmaXJzdCBkYXRhIHNldCB0aGF0IGVhY2ggbWF0Y2ggdG8gbXVsdGlwbGUgcm93cyBpbiB0aGUgc2Vjb25kIGRhdGEgc2V0LgoKVGhpcyBjYW4gYmUgbGVnaXRpbWF0ZSwgYnV0IGl0J3Mgc29tZXRoaW5nIHlvdSdkIGFsd2F5cyB3YW50IHRvIGNoZWNrIHRvIGNvbmZpcm0gdGhhdCB0aGUgYmVoYXZpb3IgaXMgY29ycmVjdC4KCiMjIENsZWFudXAKClNvIHlvdSd2ZSBqb2luZWQgdHdvIGRhdGEgc2V0cywgYW5kIG5vdyB5b3UgaGF2ZSBhIGJ1bmNoIG9mIGNvbHVtbnMgd2l0aCAiLngiIGFuZCAiLnkiIChvciBvdGhlciB0eXBlcyBvZiByZXBlYXRzKS4gV2hhdCBkbyB5b3UgZG8/CgoqKk9wdGlvbiAxKio6IHNlbGVjdCBvbmx5IHRoZSBjb2x1bW5zIHlvdSByZWFsbHkgd2FudCBpbiB0aGUgcmVzdWx0IGFoZWFkIG9mIGRvaW5nIHRoZSBqb2luLiBOT1RFOiB0aGlzIGlzIG9uZSBvZiB0aGUgdmVyeSBmZXcgdGltZXMgSSBtaWdodCBuZXN0IGB7ZHBseXJ9YCBmdW5jdGlvbiBjYWxscwoKYGBge3J9CmNhc2VzICU+JQogIHNlbGVjdChjb3VudHlGSVBTLCBjb3VudHksIHN0YXRlLCBjYXNlcykgJT4lCiAgbGVmdF9qb2luKHNlbGVjdChkZW1vLCBmaXBzLCB0b3RhbCwgd2hpdGUsIGJsYWNrKSwKICAgICAgICAgICAgYnk9YygiY291bnR5RklQUyI9ImZpcHMiKSkKYGBgCgpXaHkgbmVzdCB0aGUgYHNlbGVjdGAgY2FsbCBpbnN0ZWFkIG9mIHNhdmluZyBhIHN1YnNldCBvZiB0aGUgZGF0YSBmaXJzdCwgYW5kIHVzaW5nIHRoYXQ/IE1vc3RseSBmb3Igc2l0dWF0aW9ucyB3aGVyZSB0aGUgZGF0YXNldHMgYXJlIGxhcmdlLCBhbmQgSSBkb24ndCB3YW50IGV4dHJhIGNvcGllcyBvZiB0aGUgZGF0YSBmcmFtZSBpbiBteSBlbnZpcm9ubWVudC4gSW4gdGhpcyBjYXNlLCB0aGUgZGF0YSBzZXRzIGFyZSBzbWFsbCBlbm91Z2ggdGhhdCBJIGNvdWxkIGRvIHRoaXMgaW5zdGVhZDoKCmBgYHtyfQpkZW1vX3N1YiA8LSBzZWxlY3QoZGVtbywgZmlwcywgdG90YWwsIHdoaXRlLCBibGFjaykKCmNhc2VzICU+JQogIHNlbGVjdChjb3VudHlGSVBTLCBjb3VudHksIHN0YXRlLCBjYXNlcykgJT4lCiAgbGVmdF9qb2luKGRlbW9fc3ViLAogICAgICAgICAgICBieT1jKCJjb3VudHlGSVBTIj0iZmlwcyIpKQpgYGAKCkJ1dCBJIGhhdmUgYGRlbW9fc3ViYCBoYW5naW5nIGFyb3VuZCBpbiBteSBlbnZpcm9ubWVudCB0aGVuIChJIGxpa2UgdG8ga2VlcCBteSBlbnZpcm9ubWVudCBjbGVhbiB3aGVuIHBvc3NpYmxlISkuCgoqKk9wdGlvbiAyKio6IHVzZSBgcmVuYW1lKClgIG9yIGBzZWxlY3QoKWAgb24gdGhlIHJlc3VsdCBvZiB0aGUgam9pbjoKCmBgYHtyfQpjYXNlcyAlPiUKICBsZWZ0X2pvaW4oZGVtbywKICAgICAgICAgICAgYnk9YygiY291bnR5RklQUyI9ImZpcHMiKSkgJT4lCiAgc2VsZWN0KGNvdW50eUZJUFMsIGNvdW50eT1jb3VudHkueCwgc3RhdGU9c3RhdGUueCwgCiAgICAgICAgIGNhc2VzLCB0b3RhbCwgd2hpdGUsIGJsYWNrKQpgYGAKCiMjIyBFWEVSQ0lTRSA1CgpDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggb25seSB0aGUgZm9sbG93aW5nIGNvbHVtbnM6IGNvdW50eUZJUFMsIHN0YXRlLCBkZWF0aHMsIG5vbl93aGl0ZSBwb3B1bGF0aW9uCgpIaW50OiB1c2UgdGhlIGBkZW1vYCBhbmQgYGRlYXRoc2AgZGF0YSBmcmFtZXMuCgpgYGB7cn0KCmBgYAoKIyBMZWFybmluZyBNb3JlCgpTZWUgdGhlIFJlbGF0aW9uYWwgRGF0YSBjaGFwdGVyIGluIHRoZSBib29rIFIgZm9yIERhdGEgU2NpZW5jZTogPGh0dHBzOi8vcjRkcy5oYWQuY28ubnovcmVsYXRpb25hbC1kYXRhLmh0bWw+CgojIFJlY2FwCgpZb3Ugbm93IGNhbiB1c2UgZGlmZmVyZW50IGB7ZHBseXJ9YCBqb2luIGNvbW1hbmRzLCBzdWNoIGFzIGBpbm5lcl9qb2luYCwgYG91dGVyX2pvaW5gLCBgbGVmdF9qb2luYCwgYHJpZ2h0X2pvaW5gLCBgYW50aV9qb2luYCwgdG8gam9pbiB0b2dldGhlciB5b3VyIGRhdGEgaW4gYSB3aWRlIHZhcmlldHkgb2Ygd2F5cy4KCk5leHQgc2Vzc2lvbjogdGhlIGB7dGlkeXJ9YCBmdW5jdGlvbnMgZm9yIHJlc2hhcGluZyB5b3VyIGRhdGEgdG8gZm9sbG93IHRoZSB0aWR5IGZvcm1hdDogYHBpdm90X2xvbmdlcmAsIGBwaXZvdF93aWRlcmAsIGBzZXBhcmF0ZWAgYW5kIGBzZXBhcmF0ZV9yb3dzYAoKIyBBbnN3ZXJzIHRvIHRoZSBleGVyY2lzZXMKCiMjIyBFWEVSQ0lTRSAxCgpgYGB7cn0KIyBWaWV3IHRoZSBkYXRhIGluIGVhY2ggZGF0YXNldApWaWV3KGJhYykKVmlldyhtYXN0ZXJzKQpWaWV3KGRvYykKCiMgV2hpY2ggY29sdW1ucyBhcmUgY29tbW9uPwpjKCJzY2hvb2wiLCAieWVhciIpICMgc2V0IG9mIGNvbW1vbiBjb2x1bW5zIHdoaWNoIHRvZ2V0aGVyIGNhbiBiZSB1c2VkIGFzIGEgS2V5CmBgYAoKIyMjIEVYRVJDSVNFIDIKCmBgYHtyfQojIGNoZWNrIGNvbHVtbiBuYW1lcwpuYW1lcyhjYXNlcykKbmFtZXMoc3RhdGVfc3RhdHMpCgojIHVzZSBhcHByb3ByaWF0ZSBqb2luIGtleSBzeW50YXgKbGVmdF9qb2luKGNhc2VzLCBzdGF0ZV9zdGF0cywgYnk9Yygic3RhdGUiPSJTdGF0ZSIpKQpgYGAKCiMjIyBFWEVSQ0lTRSAzCgpgYGB7cn0KIyBjaGVjayBjb2x1bW4gbmFtZXMKbmFtZXMoYmFjKQpuYW1lcyhtYXN0ZXJzKQpuYW1lcyhkb2MpCgojIHVzZSBhcHByb3ByaWF0ZSBqb2luIGtleSBzeW50YXgKYmFjICU+JSAKICBsZWZ0X2pvaW4obWFzdGVycywgYnk9Yygic2Nob29sIiwgInllYXIiKSkgJT4lIAogIGxlZnRfam9pbihkb2MsIGJ5PWMoInNjaG9vbCIsICJ5ZWFyIikpCmBgYAoKIyMjIEVYRVJDSVNFIDQKCmBgYHtyfQojIGNoZWNrIGNvbHVtbiBuYW1lcwpuYW1lcyhkZWF0aHMpCm5hbWVzKGRlbW8pCgojIHVzZSBhcHByb3ByaWF0ZSBqb2luIGtleSBzeW50YXgKYW50aV9qb2luKGRlYXRocywgZGVtbywgYnk9YygiY291bnR5RklQUyI9ImZpcHMiKSkKYGBgCgojIyMgRVhFUkNJU0UgNQoKYGBge3J9CiMgY2hlY2sgY29sdW1uIG5hbWVzCm5hbWVzKGRlYXRocykKbmFtZXMoZGVtbykKCiMgbGVmdCBqb2luIG9uIGRlYXRocyB0byBhdXRvbWF0aWNhbGx5IHNlbGVjdCBjb3VudHlGSVBTIGNvbHVtCmRlYXRocyAlPiUgCiAgbGVmdF9qb2luKGRlbW8sIGJ5PWMoImNvdW50eUZJUFMiPSJmaXBzIikpICU+JSAKICBzZWxlY3Qoc3RhdGU9c3RhdGUueCwgZGVhdGhzLCBub25fd2hpdGUpICMgcmVuYW1lIHN0YXRlIGNvbHVtbiB3aGlsZSBzZWxlY3RpbmcKYGBgCg==