Introduction

This notebook illustrates basic data cleaning operations using a number of core tools for data wrangling.

Dependencies

This notebook requires a number of packages for working with data:

# tidyverse packages
library(dplyr)       # data wrangling
library(readr)       # read csv files
library(stringr)     # string tools

# other packages
library(here)        # file path management
library(janitor)     # data wrangling
library(naniar)      # missing data analyses

Load Data

This notebook requires data from the University of Missouri on lakes listed under the Clean Water Act.

lakes <- read_csv(here("data", "example-data", "MO_HYDRO_ImpairedLakes.csv"))

When the data are read in, they are read in as a special type object called a tibble - these are data frame objects that have particular, useful properties, like clean printing. readr will always create tibble objects for you. If you want to create one from an object that is not a tibble, you can use the following syntax:

obj <- dplyr::as_tibble(obj)

Manage Variable Names

We have a couple choices when it comes to cleaning-up variable names. When you have many columns that require cleaning, the janitor package’s clean_names() function is particularly useful. It will reformat your variable names all at once, using a case you optionally change:

The default is "snake", which we’ll clearly specify here:

clean_names(lakes, case = "snake")

Notice how underscores (i.e. _) have been introduced in variable names. The following code chunk illustrates the "lower_camel" option for comparison.

clean_names(lakes, case = "lower_camel")

The underscores used in snake case are gone, and replaced by a capital letter in the second word. With "uower_camel", the first character of each variable name will also be capitalized:

clean_names(lakes, case = "upper_camel")

Once we have done some initial cleaning, there may be other variable names we wish to change. It is useful to pipe these functions together, cleaning variable names with clean_names() and then using rename() afterwards to clean-up specific variable names. You can rename multiple variables with rename() all at once as well, which this example does.

lakes %>%
  clean_names(case = "snake") %>%
  rename(
    year = yr,
    water_body_id = wbid) -> lakes_names

lakes_names

I like to rename each individual variable on a separate line in the code to make the changes I’m making crystal clear. Once I have names that I want to keep, I’ll assign the results of my pipeline into a new object (or an existing one) so that I can refer to those new names down the road.

Missing Data

Once we’ve got column names that are predictable and easy to work with, we want to check for missing data. This can be done with the naniar package’s miss_var_summary() function, which will provide us with a tibble where each row is a variable. The count and percentage of missing observations per variable is provided in the columns:

lakes_names %>%
  miss_var_summary()

When we have missing data, we have two choices. We can either try to “recover it” (perhaps we can find another data source with the same information), or we simply ignore it and move on. Sometimes, these are superfluous columns (like feat_url) that are not necessary for the work we are doing.

We can subset out missing data, if we wish, by using the following approach:

lakes_names %>%
  filter(is.na(water_body) == FALSE)

This code would eliminate all observations where is.na(water_body) was equal to TRUE (i.e. was missing), and keep only observations with valid data.

Duplicates

Before we start removing columns, however, we want to look for duplicate observations. We can do this with the janitor package’s get_dupes() function:

lakes_names %>%
  get_dupes()

Output like this indicates that there are no true duplicates that have identical information in every variable. This is a good start, but it does not mean there are not duplicated observations. For instance, we can check and see if the same body of water appears in more than one row:

lakes_names %>%
  get_dupes(water_body)

It does! When we identify this, we need to revisit what we think the observational unit for these data are. We understandably may have believed that each observation was for a single body of water, and that is true (i.e. no single observation is for multiple bodies of water), but we have bodies of water listed multiple times. We can test this easily by creating a list of all of the unique bodies of water:

lakes_names %>%
  distinct(water_body)

We get a tibble that has 55 rows, which is shorter than our original data frame with 86 observations. This tells us that there are duplicate bodies of water.

In these sorts of situations, we want to explore our data further and identify what differs between these columns. In this case, we have data that are fundamentally observations of one pollutant per body of water. So a body of water with multiple pollutants will have multiple rows. This is complicated by data that differs in some ancillary variables (like perm_id), and some data that appear to be for multiple features (Mark Twin Lake, for example, appears to be constituted by two polygons in these data). Getting to the bottom this can be complicated and can take time.

Once we are ready to get rid of duplicated data, we can use the dplyr distinct() function. The .keep_all argument is important, as it ensures that we get all of the other columns in addition to the one we specify:

lakes_names %>%
  distinct(water_body, .keep_all = TRUE) -> lakes_unique

lakes_unique

Subsetting Columns

Once we have a data set with a predictable number of observations, we can start to get rid of columns. This practice of whittling down our data is known as “subsetting.” We always use the select() function from dplyr for this. We can list one or more variables preceded by a negative sign (i.e. -) to remove a column from our data:

lakes_unique %>%
  select(-year)

Alternatively, we can list variables without negative signs that we want to retain:

lakes_unique %>%
  select(water_body_id, water_body, pollutant) -> lakes_subset_cols

lakes_subset_cols

As before, we write our data to a new object when we are happy with the changes we have made.

Our data will be created in whatever order we list the columns in, so re-ordering our list to put pollutant first will mean that pollutant is now the first column in the data we have created.

lakes_subset_cols %>%
  select(pollutant, water_body_id, water_body)

Subsetting Observations

We can also subset our data by observation. For instance, we can use the filter() function from dplyr to return only observations that are for lakes at or over 100 acres in size:

lakes_names %>% 
  filter(size >= 100)

Our filter() statement uses a relational operator (>=, i.e. “greater than or equal to”), to identify observations to keep. For each observation, it tests whether the size variable is greater than or equal to 100. This creates a series of implicit TRUE or FALSE results - the lake is either greater than or equal to 100 or it is not. The lakes that are implicitly identified as TRUE are then selected and returned.

This is known as “boolean logic,” and is a core principle within computer science. The relational operators that we can use in R are:

The == (i.e. “exactly equal to”) operator is particularly useful for selecting data that are character:

lakes_names %>% 
  filter(pollutant == "Mercury in Fish Tissue (T)")

We can combine our two filter statements into one if we wanted to select large lakes with mercury in fish tissue by using a logical operator:

lakes_names %>% 
  filter(size >= 100 & pollutant == "Mercury in Fish Tissue (T)")

We use the & operator to create a condition where there are two boolean tests (one for size and one for pollutant) that both must be TRUE. We can also use | as well to create a situation where one test or the other must be true.

Putting Things Together

If we wanted to get a list of all the large lakes that have mercury as a pollutant, we could combine this with our distinct function:

lakes_names %>%
  filter(size >= 100 & pollutant == "Mercury in Fish Tissue (T)") %>%
  distinct(water_body)

We get a tibble with the 19 lake names that are both 100 acres or larger and have mercury as a pollutant.

We can extend this logic further, by combining all of our renaming and select() statements into a single pipeline. Remember that we can read the pipeline like so:

  1. First we take the lakes data, then
  2. we clean all of the variable names to snake_case, then
  3. we rename two of the variables further, then
  4. we subset columns to clarify the characteristics we are interested in, then
  5. we subset observations to retain only the lakes that are 100 acres or greater in size, and
  6. we assign those changes to a new object called lakes_large.
lakes %>% 
  clean_names(case = "snake") %>%
  rename(
    year = yr,
    water_body_id = wbid
    ) %>%
  select(water_body_id, water_body, size, unit, pollutant) %>%
  filter(size >= 100) -> lakes_large

lakes_large

Creating and Modifying Variables

Often we want to create new variables as well. The mutate() function from dplyr can be used to both create new variables and edit existing ones.

Modifying Existing Variables

For instance, we often want to convert variables to character from numeric using as.character():

lakes_large %>%
  mutate(size = as.character(size)) -> lakes_chr

lakes_chr

The size variable is now character instead of numeric, which can be useful when writing data to shapefiles (which do not like numbers over a certain size, an issue that crops up with large areas measured in square meters or feet).

We can convert our data back with as.numeric():

lakes_chr %>%
  mutate(size = as.numeric(size))

Now we have numeric data again if we were to write this object to our global environment.

Creating New Variables

One way common way we create variables is to recode them into a binary outcome. For instance, we may want to make two maps - one of lakes that are between 100 acres and 999 acres, and one of lakes that are 1,000 acres or greater. We can identify these features with a logical variable that is TRUE if the lake is 1000 acres or greater and FALSE otherwise. We use the ifelse() function to create an expression we test in a boolean fashion along with what we return if the test is TRUE (in this case, TRUE), and what we return if the test is FALSE (in this case, FALSE):

lakes_large %>%
  mutate(vlarge = ifelse(size >= 1000, TRUE, FALSE)) %>%
  select(water_body, size, vlarge)

I’ve subset the columns afterwards just to illustrate how this looks clearly - you would only want to emulate this if you really only wanted those three variables!

We can do the same thing, but return characters instead:

lakes_large %>%
  mutate(vlarge = ifelse(size >= 1000, "Over 1000 acres", "Under 1000 acres")) %>%
  select(water_body, size, vlarge)

In both cases, the expression size >= 1000 is our boolean test. If the observation is TRUE with this test, either TRUE or "Over 1000 acres" is returned. Likewise, if the observation is FALSE with this test, either FALSE or "Under 1000 acres".

We can do the same with our test for mercury in fish:

lakes_large %>%
  mutate(mercury = ifelse(pollutant == "Mercury in Fish Tissue (T)", TRUE, FALSE)) %>%
  select(water_body, mercury)

If we have a lot of string data, it can be useful to look for specific words, like “Mercury”. We can do this with the str_detect() function from stringr, which again creates a boolean test to identify whether patterns exist in a given string:

lakes_large %>%
  mutate(mercury = ifelse(str_detect(pollutant, pattern = "Mercury"), TRUE, FALSE)) %>%
  select(water_body, pollutant, mercury)

We can make this even more complicated if there are two words we want to search for. For instance, we could look for both “Phosphorus” or “Nitrogen” to be present:

lakes_large %>%
  mutate(phosNitro = ifelse(str_detect(pollutant, pattern = "Phosphorus|Nitrogen"), TRUE, FALSE)) %>%
  select(water_body, pollutant, phosNitro)

Just like before, we use the | as a logical operator for the word “or”. Any time either of these words are found, we return a value of TRUE.

Another way to recode variables is to take long strings and shorten them. We’ll use distinct() to get a list of the pollutants in our data:

lakes_large %>%
  distinct(pollutant)

If we want to keep all of these data but create shorthand references for them, we can use case_when() combined with mutate() to specify how each pollutant should be simplified:

lakes_large %>% 
  mutate(pollutant_simple = case_when(
    pollutant == "Chlorophyll-a (W)" ~ "Chlorophyll",
    pollutant == "Mercury in Fish Tissue (T)" ~ "Mercury",
    pollutant == "Phosphorus, Total (W)" ~ "Phosphorus",
    pollutant == "Nitrogen, Total (W)"  ~ "Nitrogen",
    pollutant == "Nutrient/Eutrophication Biol. Indicators (W)"  ~ "Eutrophication"
    ))

The strings after the tilde (~) are what will be returned as new values any time an old value is detected. Notice again how the exactly equal to relational operator (i.e. ==) is used here!

Grouping and Summarizing Observations

Since we have bodies of water in multiple observations, we may want to summarize them. For instance, we could ask how many distinct pollutants are listed per body of water, and whether or not mercury is one of them. The following pipeline groups our data and then summarizes it to answer these two questions.

We:

  1. Take the lakes_large data, then
  2. we create a list of distinct combinations of lake names and pollutants, then
  3. create our binary indicator for the presence of mercury, then
  4. group our observations by body of water, then
  5. create new summary variables that count the number of pollutants per body of water and test whether any of the values for mercury for each body of water are TRUE, then
  6. re-order the output in descending order (the role of desc()) so the lake with the largest number of pollutants is listed first.
lakes_large %>%
  distinct(water_body, pollutant) %>%
  mutate(mercury = ifelse(pollutant == "Mercury in Fish Tissue (T)", TRUE, FALSE)) %>%
  group_by(water_body) %>%
  summarize(
    pollutant_count = n(),
    mercury = any(mercury)) %>%
  arrange(desc(pollutant_count))
LS0tCnRpdGxlOiAiTWVldGluZyAyLTEgRXhhbXBsZXMgLSBDb21wbGV0ZSIKYXV0aG9yOiAiQ2hyaXN0b3BoZXIgUHJlbmVyLCBQaC5ELiIKZGF0ZTogJyhgciBmb3JtYXQoU3lzLnRpbWUoKSwgIiVCICVkLCAlWSIpYCknCm91dHB1dDogCiAgZ2l0aHViX2RvY3VtZW50OiBkZWZhdWx0CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdCAKLS0tCgojIyBJbnRyb2R1Y3Rpb24KVGhpcyBub3RlYm9vayBpbGx1c3RyYXRlcyBiYXNpYyBkYXRhIGNsZWFuaW5nIG9wZXJhdGlvbnMgdXNpbmcgYSBudW1iZXIgb2YgY29yZSB0b29scyBmb3IgZGF0YSB3cmFuZ2xpbmcuCgojIyBEZXBlbmRlbmNpZXMKVGhpcyBub3RlYm9vayByZXF1aXJlcyBhIG51bWJlciBvZiBwYWNrYWdlcyBmb3Igd29ya2luZyB3aXRoIGRhdGE6CgpgYGB7ciBsb2FkLXBhY2thZ2VzfQojIHRpZHl2ZXJzZSBwYWNrYWdlcwpsaWJyYXJ5KGRwbHlyKSAgICAgICAjIGRhdGEgd3JhbmdsaW5nCmxpYnJhcnkocmVhZHIpICAgICAgICMgcmVhZCBjc3YgZmlsZXMKbGlicmFyeShzdHJpbmdyKSAgICAgIyBzdHJpbmcgdG9vbHMKCiMgb3RoZXIgcGFja2FnZXMKbGlicmFyeShoZXJlKSAgICAgICAgIyBmaWxlIHBhdGggbWFuYWdlbWVudApsaWJyYXJ5KGphbml0b3IpICAgICAjIGRhdGEgd3JhbmdsaW5nCmxpYnJhcnkobmFuaWFyKSAgICAgICMgbWlzc2luZyBkYXRhIGFuYWx5c2VzCmBgYAoKIyMgTG9hZCBEYXRhClRoaXMgbm90ZWJvb2sgcmVxdWlyZXMgZGF0YSBmcm9tIHRoZSBVbml2ZXJzaXR5IG9mIE1pc3NvdXJpIG9uIGxha2VzIGxpc3RlZCB1bmRlciB0aGUgQ2xlYW4gV2F0ZXIgQWN0LiAKCmBgYHtyIGxvYWQtZGF0YX0KbGFrZXMgPC0gcmVhZF9jc3YoaGVyZSgiZGF0YSIsICJleGFtcGxlLWRhdGEiLCAiTU9fSFlEUk9fSW1wYWlyZWRMYWtlcy5jc3YiKSkKYGBgCgpXaGVuIHRoZSBkYXRhIGFyZSByZWFkIGluLCB0aGV5IGFyZSByZWFkIGluIGFzIGEgc3BlY2lhbCB0eXBlIG9iamVjdCBjYWxsZWQgYSBgdGliYmxlYCAtIHRoZXNlIGFyZSBkYXRhIGZyYW1lIG9iamVjdHMgdGhhdCBoYXZlIHBhcnRpY3VsYXIsIHVzZWZ1bCBwcm9wZXJ0aWVzLCBsaWtlIGNsZWFuIHByaW50aW5nLiBgcmVhZHJgIHdpbGwgYWx3YXlzIGNyZWF0ZSBgdGliYmxlYCBvYmplY3RzIGZvciB5b3UuIElmIHlvdSB3YW50IHRvIGNyZWF0ZSBvbmUgZnJvbSBhbiBvYmplY3QgdGhhdCBpcyBub3QgYSB0aWJibGUsIHlvdSBjYW4gdXNlIHRoZSBmb2xsb3dpbmcgc3ludGF4OgoKYGBgcgpvYmogPC0gZHBseXI6OmFzX3RpYmJsZShvYmopCmBgYAoKIyMgTWFuYWdlIFZhcmlhYmxlIE5hbWVzCldlIGhhdmUgYSBjb3VwbGUgY2hvaWNlcyB3aGVuIGl0IGNvbWVzIHRvIGNsZWFuaW5nLXVwIHZhcmlhYmxlIG5hbWVzLiBXaGVuIHlvdSBoYXZlIG1hbnkgY29sdW1ucyB0aGF0IHJlcXVpcmUgY2xlYW5pbmcsIHRoZSBgamFuaXRvcmAgcGFja2FnZSdzIGBjbGVhbl9uYW1lcygpYCBmdW5jdGlvbiBpcyBwYXJ0aWN1bGFybHkgdXNlZnVsLiBJdCB3aWxsIHJlZm9ybWF0IHlvdXIgdmFyaWFibGUgbmFtZXMgYWxsIGF0IG9uY2UsIHVzaW5nIGEgY2FzZSB5b3Ugb3B0aW9uYWxseSBjaGFuZ2U6CgoqIGBzbmFrZV9jYXNlYAoqIGBsb3dlckNhbWVsYAoqIGBVcHBlckNhbWVsYAoKVGhlIGRlZmF1bHQgaXMgYCJzbmFrZSJgLCB3aGljaCB3ZSdsbCBjbGVhcmx5IHNwZWNpZnkgaGVyZToKCmBgYHtyIGNsZWFuLW5hbWVzLXNuYWtlfQpjbGVhbl9uYW1lcyhsYWtlcywgY2FzZSA9ICJzbmFrZSIpCmBgYAoKTm90aWNlIGhvdyB1bmRlcnNjb3JlcyAoaS5lLiBgX2ApIGhhdmUgYmVlbiBpbnRyb2R1Y2VkIGluIHZhcmlhYmxlIG5hbWVzLiBUaGUgZm9sbG93aW5nIGNvZGUgY2h1bmsgaWxsdXN0cmF0ZXMgdGhlIGAibG93ZXJfY2FtZWwiYCBvcHRpb24gZm9yIGNvbXBhcmlzb24uCgpgYGB7ciBjbGVhbi1uYW1lcy1sb3dlci1jYW1lbH0KY2xlYW5fbmFtZXMobGFrZXMsIGNhc2UgPSAibG93ZXJfY2FtZWwiKQpgYGAKClRoZSB1bmRlcnNjb3JlcyB1c2VkIGluIHNuYWtlIGNhc2UgYXJlIGdvbmUsIGFuZCByZXBsYWNlZCBieSBhIGNhcGl0YWwgbGV0dGVyIGluIHRoZSBzZWNvbmQgd29yZC4gV2l0aCBgInVvd2VyX2NhbWVsImAsIHRoZSBmaXJzdCBjaGFyYWN0ZXIgb2YgZWFjaCB2YXJpYWJsZSBuYW1lIHdpbGwgYWxzbyBiZSBjYXBpdGFsaXplZDoKCmBgYHtyIGNsZWFuLW5hbWVzLXVwcGVyLWNhbWVsfQpjbGVhbl9uYW1lcyhsYWtlcywgY2FzZSA9ICJ1cHBlcl9jYW1lbCIpCmBgYAoKT25jZSB3ZSBoYXZlIGRvbmUgc29tZSBpbml0aWFsIGNsZWFuaW5nLCB0aGVyZSBtYXkgYmUgb3RoZXIgdmFyaWFibGUgbmFtZXMgd2Ugd2lzaCB0byBjaGFuZ2UuIEl0IGlzIHVzZWZ1bCB0byBwaXBlIHRoZXNlIGZ1bmN0aW9ucyB0b2dldGhlciwgY2xlYW5pbmcgdmFyaWFibGUgbmFtZXMgd2l0aCBgY2xlYW5fbmFtZXMoKWAgYW5kIHRoZW4gdXNpbmcgYHJlbmFtZSgpYCBhZnRlcndhcmRzIHRvIGNsZWFuLXVwIHNwZWNpZmljIHZhcmlhYmxlIG5hbWVzLiBZb3UgY2FuIHJlbmFtZSBtdWx0aXBsZSB2YXJpYWJsZXMgd2l0aCBgcmVuYW1lKClgIGFsbCBhdCBvbmNlIGFzIHdlbGwsIHdoaWNoIHRoaXMgZXhhbXBsZSBkb2VzLiAgCgpgYGB7ciByZW5hbWUtcGlwZX0KbGFrZXMgJT4lCiAgY2xlYW5fbmFtZXMoY2FzZSA9ICJzbmFrZSIpICU+JQogIHJlbmFtZSgKICAgIHllYXIgPSB5ciwKICAgIHdhdGVyX2JvZHlfaWQgPSB3YmlkKSAtPiBsYWtlc19uYW1lcwoKbGFrZXNfbmFtZXMKYGBgCgpJIGxpa2UgdG8gcmVuYW1lIGVhY2ggaW5kaXZpZHVhbCB2YXJpYWJsZSBvbiBhIHNlcGFyYXRlIGxpbmUgaW4gdGhlIGNvZGUgdG8gbWFrZSB0aGUgY2hhbmdlcyBJJ20gbWFraW5nIGNyeXN0YWwgY2xlYXIuIE9uY2UgSSBoYXZlIG5hbWVzIHRoYXQgSSB3YW50IHRvIGtlZXAsIEknbGwgYXNzaWduIHRoZSByZXN1bHRzIG9mIG15IHBpcGVsaW5lIGludG8gYSBuZXcgb2JqZWN0IChvciBhbiBleGlzdGluZyBvbmUpIHNvIHRoYXQgSSBjYW4gcmVmZXIgdG8gdGhvc2UgbmV3IG5hbWVzIGRvd24gdGhlIHJvYWQuCgojIyBNaXNzaW5nIERhdGEKT25jZSB3ZSd2ZSBnb3QgY29sdW1uIG5hbWVzIHRoYXQgYXJlIHByZWRpY3RhYmxlIGFuZCBlYXN5IHRvIHdvcmsgd2l0aCwgd2Ugd2FudCB0byBjaGVjayBmb3IgbWlzc2luZyBkYXRhLiBUaGlzIGNhbiBiZSBkb25lIHdpdGggdGhlIGBuYW5pYXJgIHBhY2thZ2UncyBgbWlzc192YXJfc3VtbWFyeSgpYCBmdW5jdGlvbiwgd2hpY2ggd2lsbCBwcm92aWRlIHVzIHdpdGggYSB0aWJibGUgd2hlcmUgZWFjaCByb3cgaXMgYSB2YXJpYWJsZS4gVGhlIGNvdW50IGFuZCBwZXJjZW50YWdlIG9mIG1pc3Npbmcgb2JzZXJ2YXRpb25zIHBlciB2YXJpYWJsZSBpcyBwcm92aWRlZCBpbiB0aGUgY29sdW1uczoKCmBgYHtyIG1pc3MtdmFyfQpsYWtlc19uYW1lcyAlPiUKICBtaXNzX3Zhcl9zdW1tYXJ5KCkKYGBgCgpXaGVuIHdlIGhhdmUgbWlzc2luZyBkYXRhLCB3ZSBoYXZlIHR3byBjaG9pY2VzLiBXZSBjYW4gZWl0aGVyIHRyeSB0byAicmVjb3ZlciBpdCIgKHBlcmhhcHMgd2UgY2FuIGZpbmQgYW5vdGhlciBkYXRhIHNvdXJjZSB3aXRoIHRoZSBzYW1lIGluZm9ybWF0aW9uKSwgb3Igd2Ugc2ltcGx5IGlnbm9yZSBpdCBhbmQgbW92ZSBvbi4gU29tZXRpbWVzLCB0aGVzZSBhcmUgc3VwZXJmbHVvdXMgY29sdW1ucyAobGlrZSBgZmVhdF91cmxgKSB0aGF0IGFyZSBub3QgbmVjZXNzYXJ5IGZvciB0aGUgd29yayB3ZSBhcmUgZG9pbmcuCgpXZSBjYW4gc3Vic2V0IG91dCBtaXNzaW5nIGRhdGEsIGlmIHdlIHdpc2gsIGJ5IHVzaW5nIHRoZSBmb2xsb3dpbmcgYXBwcm9hY2g6CgpgYGByCmxha2VzX25hbWVzICU+JQogIGZpbHRlcihpcy5uYSh3YXRlcl9ib2R5KSA9PSBGQUxTRSkKYGBgCgpUaGlzIGNvZGUgd291bGQgZWxpbWluYXRlIGFsbCBvYnNlcnZhdGlvbnMgd2hlcmUgYGlzLm5hKHdhdGVyX2JvZHkpYCB3YXMgZXF1YWwgdG8gYFRSVUVgIChpLmUuIHdhcyBtaXNzaW5nKSwgYW5kIGtlZXAgb25seSBvYnNlcnZhdGlvbnMgd2l0aCB2YWxpZCBkYXRhLgoKIyMgRHVwbGljYXRlcwpCZWZvcmUgd2Ugc3RhcnQgcmVtb3ZpbmcgY29sdW1ucywgaG93ZXZlciwgd2Ugd2FudCB0byBsb29rIGZvciBkdXBsaWNhdGUgb2JzZXJ2YXRpb25zLiBXZSBjYW4gZG8gdGhpcyB3aXRoIHRoZSBgamFuaXRvcmAgcGFja2FnZSdzIGBnZXRfZHVwZXMoKWAgZnVuY3Rpb246CgpgYGB7ciBkdXBlc30KbGFrZXNfbmFtZXMgJT4lCiAgZ2V0X2R1cGVzKCkKYGBgCgpPdXRwdXQgbGlrZSB0aGlzIGluZGljYXRlcyB0aGF0IHRoZXJlIGFyZSBubyB0cnVlIGR1cGxpY2F0ZXMgdGhhdCBoYXZlIGlkZW50aWNhbCBpbmZvcm1hdGlvbiBpbiBldmVyeSB2YXJpYWJsZS4gVGhpcyBpcyBhIGdvb2Qgc3RhcnQsIGJ1dCBpdCBkb2VzIG5vdCBtZWFuIHRoZXJlIGFyZSBub3QgZHVwbGljYXRlZCBvYnNlcnZhdGlvbnMuIEZvciBpbnN0YW5jZSwgd2UgY2FuIGNoZWNrIGFuZCBzZWUgaWYgdGhlIHNhbWUgYm9keSBvZiB3YXRlciBhcHBlYXJzIGluIG1vcmUgdGhhbiBvbmUgcm93OgoKYGBge3IgZHVwZXMtd2F0ZXJ9Cmxha2VzX25hbWVzICU+JQogIGdldF9kdXBlcyh3YXRlcl9ib2R5KQpgYGAKCkl0IGRvZXMhIFdoZW4gd2UgaWRlbnRpZnkgdGhpcywgd2UgbmVlZCB0byByZXZpc2l0IHdoYXQgd2UgdGhpbmsgdGhlIG9ic2VydmF0aW9uYWwgdW5pdCBmb3IgdGhlc2UgZGF0YSBhcmUuIFdlIHVuZGVyc3RhbmRhYmx5IG1heSBoYXZlIGJlbGlldmVkIHRoYXQgZWFjaCBvYnNlcnZhdGlvbiB3YXMgZm9yIGEgc2luZ2xlIGJvZHkgb2Ygd2F0ZXIsIGFuZCB0aGF0IGlzIHRydWUgKGkuZS4gbm8gKnNpbmdsZSogb2JzZXJ2YXRpb24gaXMgZm9yICptdWx0aXBsZSogYm9kaWVzIG9mIHdhdGVyKSwgYnV0IHdlIGhhdmUgYm9kaWVzIG9mIHdhdGVyIGxpc3RlZCBtdWx0aXBsZSB0aW1lcy4gV2UgY2FuIHRlc3QgdGhpcyBlYXNpbHkgYnkgY3JlYXRpbmcgYSBsaXN0IG9mIGFsbCBvZiB0aGUgdW5pcXVlIGJvZGllcyBvZiB3YXRlcjoKCmBgYHtyIGRpc3RpbmN0LXdhdGVyfQpsYWtlc19uYW1lcyAlPiUKICBkaXN0aW5jdCh3YXRlcl9ib2R5KQpgYGAKCldlIGdldCBhIHRpYmJsZSB0aGF0IGhhcyA1NSByb3dzLCB3aGljaCBpcyBzaG9ydGVyIHRoYW4gb3VyIG9yaWdpbmFsIGRhdGEgZnJhbWUgd2l0aCA4NiBvYnNlcnZhdGlvbnMuIFRoaXMgdGVsbHMgdXMgdGhhdCB0aGVyZSBhcmUgZHVwbGljYXRlIGJvZGllcyBvZiB3YXRlci4KCkluIHRoZXNlIHNvcnRzIG9mIHNpdHVhdGlvbnMsIHdlIHdhbnQgdG8gZXhwbG9yZSBvdXIgZGF0YSBmdXJ0aGVyIGFuZCBpZGVudGlmeSB3aGF0IGRpZmZlcnMgYmV0d2VlbiB0aGVzZSBjb2x1bW5zLiBJbiB0aGlzIGNhc2UsIHdlIGhhdmUgZGF0YSB0aGF0IGFyZSBmdW5kYW1lbnRhbGx5IG9ic2VydmF0aW9ucyBvZiBvbmUgcG9sbHV0YW50IHBlciBib2R5IG9mIHdhdGVyLiBTbyBhIGJvZHkgb2Ygd2F0ZXIgd2l0aCBtdWx0aXBsZSBwb2xsdXRhbnRzIHdpbGwgaGF2ZSBtdWx0aXBsZSByb3dzLiBUaGlzIGlzIGNvbXBsaWNhdGVkIGJ5IGRhdGEgdGhhdCBkaWZmZXJzIGluIHNvbWUgYW5jaWxsYXJ5IHZhcmlhYmxlcyAobGlrZSBgcGVybV9pZGApLCBhbmQgc29tZSBkYXRhIHRoYXQgYXBwZWFyIHRvIGJlIGZvciBtdWx0aXBsZSBmZWF0dXJlcyAoTWFyayBUd2luIExha2UsIGZvciBleGFtcGxlLCBhcHBlYXJzIHRvIGJlIGNvbnN0aXR1dGVkIGJ5IHR3byBwb2x5Z29ucyBpbiB0aGVzZSBkYXRhKS4gR2V0dGluZyB0byB0aGUgYm90dG9tIHRoaXMgY2FuIGJlIGNvbXBsaWNhdGVkIGFuZCBjYW4gdGFrZSB0aW1lLgoKT25jZSB3ZSBhcmUgcmVhZHkgdG8gZ2V0IHJpZCBvZiBkdXBsaWNhdGVkIGRhdGEsIHdlIGNhbiB1c2UgdGhlIGBkcGx5cmAgYGRpc3RpbmN0KClgIGZ1bmN0aW9uLiBUaGUgYC5rZWVwX2FsbGAgYXJndW1lbnQgaXMgaW1wb3J0YW50LCBhcyBpdCBlbnN1cmVzIHRoYXQgd2UgZ2V0IGFsbCBvZiB0aGUgb3RoZXIgY29sdW1ucyBpbiBhZGRpdGlvbiB0byB0aGUgb25lIHdlIHNwZWNpZnk6CgpgYGB7ciBkaXN0aW5jdC1rZWVwfQpsYWtlc19uYW1lcyAlPiUKICBkaXN0aW5jdCh3YXRlcl9ib2R5LCAua2VlcF9hbGwgPSBUUlVFKSAtPiBsYWtlc191bmlxdWUKCmxha2VzX3VuaXF1ZQpgYGAKCiMjIFN1YnNldHRpbmcgQ29sdW1ucwpPbmNlIHdlIGhhdmUgYSBkYXRhIHNldCB3aXRoIGEgcHJlZGljdGFibGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucywgd2UgY2FuIHN0YXJ0IHRvIGdldCByaWQgb2YgY29sdW1ucy4gVGhpcyBwcmFjdGljZSBvZiB3aGl0dGxpbmcgZG93biBvdXIgZGF0YSBpcyBrbm93biBhcyAic3Vic2V0dGluZy4iIFdlIGFsd2F5cyB1c2UgdGhlIGBzZWxlY3QoKWAgZnVuY3Rpb24gZnJvbSBgZHBseXJgIGZvciB0aGlzLiBXZSBjYW4gbGlzdCBvbmUgb3IgbW9yZSB2YXJpYWJsZXMgcHJlY2VkZWQgYnkgYSBuZWdhdGl2ZSBzaWduIChpLmUuIGAtYCkgdG8gcmVtb3ZlIGEgY29sdW1uIGZyb20gb3VyIGRhdGE6CgpgYGB7ciByZW1vdmUtdmFyfQpsYWtlc191bmlxdWUgJT4lCiAgc2VsZWN0KC15ZWFyKQpgYGAKCkFsdGVybmF0aXZlbHksIHdlIGNhbiBsaXN0IHZhcmlhYmxlcyB3aXRob3V0IG5lZ2F0aXZlIHNpZ25zIHRoYXQgd2Ugd2FudCB0byByZXRhaW46CgpgYGB7ciBrZWUtdmFyc30KbGFrZXNfdW5pcXVlICU+JQogIHNlbGVjdCh3YXRlcl9ib2R5X2lkLCB3YXRlcl9ib2R5LCBwb2xsdXRhbnQpIC0+IGxha2VzX3N1YnNldF9jb2xzCgpsYWtlc19zdWJzZXRfY29scwpgYGAKCkFzIGJlZm9yZSwgd2Ugd3JpdGUgb3VyIGRhdGEgdG8gYSBuZXcgb2JqZWN0IHdoZW4gd2UgYXJlIGhhcHB5IHdpdGggdGhlIGNoYW5nZXMgd2UgaGF2ZSBtYWRlLgoKT3VyIGRhdGEgd2lsbCBiZSBjcmVhdGVkIGluIHdoYXRldmVyIG9yZGVyIHdlIGxpc3QgdGhlIGNvbHVtbnMgaW4sIHNvIHJlLW9yZGVyaW5nIG91ciBsaXN0IHRvIHB1dCBgcG9sbHV0YW50YCBmaXJzdCB3aWxsIG1lYW4gdGhhdCBwb2xsdXRhbnQgaXMgbm93IHRoZSBmaXJzdCBjb2x1bW4gaW4gdGhlIGRhdGEgd2UgaGF2ZSBjcmVhdGVkLgoKYGBge3IgcmVvcmRlci12YXJzfQpsYWtlc19zdWJzZXRfY29scyAlPiUKICBzZWxlY3QocG9sbHV0YW50LCB3YXRlcl9ib2R5X2lkLCB3YXRlcl9ib2R5KQpgYGAKCiMjIFN1YnNldHRpbmcgT2JzZXJ2YXRpb25zCldlIGNhbiBhbHNvIHN1YnNldCBvdXIgZGF0YSBieSBvYnNlcnZhdGlvbi4gRm9yIGluc3RhbmNlLCB3ZSBjYW4gdXNlIHRoZSBgZmlsdGVyKClgIGZ1bmN0aW9uIGZyb20gYGRwbHlyYCB0byByZXR1cm4gb25seSBvYnNlcnZhdGlvbnMgdGhhdCBhcmUgZm9yIGxha2VzIGF0IG9yIG92ZXIgMTAwIGFjcmVzIGluIHNpemU6CgpgYGB7ciBmaWx0ZXItc2l6ZX0KbGFrZXNfbmFtZXMgJT4lIAogIGZpbHRlcihzaXplID49IDEwMCkKYGBgCgpPdXIgYGZpbHRlcigpYCBzdGF0ZW1lbnQgdXNlcyBhICoqcmVsYXRpb25hbCBvcGVyYXRvcioqIChgPj1gLCBpLmUuICJncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8iKSwgdG8gaWRlbnRpZnkgb2JzZXJ2YXRpb25zIHRvIGtlZXAuIEZvciBlYWNoIG9ic2VydmF0aW9uLCBpdCB0ZXN0cyB3aGV0aGVyIHRoZSBgc2l6ZWAgdmFyaWFibGUgaXMgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIGAxMDBgLiBUaGlzIGNyZWF0ZXMgYSBzZXJpZXMgb2YgaW1wbGljaXQgYFRSVUVgIG9yIGBGQUxTRWAgcmVzdWx0cyAtIHRoZSBsYWtlIGlzIGVpdGhlciBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gYDEwMGAgb3IgaXQgaXMgbm90LiBUaGUgbGFrZXMgdGhhdCBhcmUgaW1wbGljaXRseSBpZGVudGlmaWVkIGFzIGBUUlVFYCBhcmUgdGhlbiBzZWxlY3RlZCBhbmQgcmV0dXJuZWQuIAoKVGhpcyBpcyBrbm93biBhcyAiYm9vbGVhbiBsb2dpYywiIGFuZCBpcyBhIGNvcmUgcHJpbmNpcGxlIHdpdGhpbiBjb21wdXRlciBzY2llbmNlLiBUaGUgcmVsYXRpb25hbCBvcGVyYXRvcnMgdGhhdCB3ZSBjYW4gdXNlIGluIGBSYCBhcmU6CgoqIGA+YCAtIGdyZWF0ZXIgdGhhbgoqIGA+PWAgLSBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8KKiBgPGAgLSBsZXNzIHRoYW4KKiBgPD1gIC0gbGVzcyB0aGFuIG9yIGVxdWFsIHRvCiogYD09YCAtIGV4YWN0bHkgZXF1YWwgdG8KKiBgIS1gIC0gbm90IGVxdWFsIHRvCgpUaGUgYD09YCAoaS5lLiAiZXhhY3RseSBlcXVhbCB0byIpIG9wZXJhdG9yIGlzIHBhcnRpY3VsYXJseSB1c2VmdWwgZm9yIHNlbGVjdGluZyBkYXRhIHRoYXQgYXJlIGNoYXJhY3RlcjoKCmBgYHtyIGZpbHRlci1wb2xsdXRhbnR9Cmxha2VzX25hbWVzICU+JSAKICBmaWx0ZXIocG9sbHV0YW50ID09ICJNZXJjdXJ5IGluIEZpc2ggVGlzc3VlIChUKSIpCmBgYAoKV2UgY2FuIGNvbWJpbmUgb3VyIHR3byBmaWx0ZXIgc3RhdGVtZW50cyBpbnRvIG9uZSBpZiB3ZSB3YW50ZWQgdG8gc2VsZWN0IGxhcmdlIGxha2VzIHdpdGggbWVyY3VyeSBpbiBmaXNoIHRpc3N1ZSBieSB1c2luZyBhICoqbG9naWNhbCBvcGVyYXRvcioqOgoKYGBge3IgZmlsdGVyLWJvdGh9Cmxha2VzX25hbWVzICU+JSAKICBmaWx0ZXIoc2l6ZSA+PSAxMDAgJiBwb2xsdXRhbnQgPT0gIk1lcmN1cnkgaW4gRmlzaCBUaXNzdWUgKFQpIikKYGBgCgpXZSB1c2UgdGhlIGAmYCBvcGVyYXRvciB0byBjcmVhdGUgYSBjb25kaXRpb24gd2hlcmUgdGhlcmUgYXJlIHR3byBib29sZWFuIHRlc3RzIChvbmUgZm9yIGBzaXplYCBhbmQgb25lIGZvciBgcG9sbHV0YW50YCkgdGhhdCAqKmJvdGgqKiBtdXN0IGJlIGBUUlVFYC4gV2UgY2FuIGFsc28gdXNlIGB8YCBhcyB3ZWxsIHRvIGNyZWF0ZSBhIHNpdHVhdGlvbiB3aGVyZSBvbmUgdGVzdCBvciB0aGUgb3RoZXIgbXVzdCBiZSB0cnVlLgoKIyMgUHV0dGluZyBUaGluZ3MgVG9nZXRoZXIKSWYgd2Ugd2FudGVkIHRvIGdldCBhIGxpc3Qgb2YgYWxsIHRoZSBsYXJnZSBsYWtlcyB0aGF0IGhhdmUgbWVyY3VyeSBhcyBhIHBvbGx1dGFudCwgd2UgY291bGQgY29tYmluZSB0aGlzIHdpdGggb3VyIGBkaXN0aW5jdGAgZnVuY3Rpb246CgpgYGB7ciBkaXN0aW5jdC1maWx0ZXJ9Cmxha2VzX25hbWVzICU+JQogIGZpbHRlcihzaXplID49IDEwMCAmIHBvbGx1dGFudCA9PSAiTWVyY3VyeSBpbiBGaXNoIFRpc3N1ZSAoVCkiKSAlPiUKICBkaXN0aW5jdCh3YXRlcl9ib2R5KQpgYGAKCldlIGdldCBhIGB0aWJibGVgIHdpdGggdGhlIDE5IGxha2UgbmFtZXMgdGhhdCBhcmUgYm90aCAxMDAgYWNyZXMgb3IgbGFyZ2VyIGFuZCBoYXZlIG1lcmN1cnkgYXMgYSBwb2xsdXRhbnQuCgpXZSBjYW4gZXh0ZW5kIHRoaXMgbG9naWMgZnVydGhlciwgYnkgY29tYmluaW5nIGFsbCBvZiBvdXIgcmVuYW1pbmcgYW5kIGBzZWxlY3QoKWAgc3RhdGVtZW50cyBpbnRvIGEgc2luZ2xlIHBpcGVsaW5lLiBSZW1lbWJlciB0aGF0IHdlIGNhbiByZWFkIHRoZSBwaXBlbGluZSBsaWtlIHNvOgoKMS4gRmlyc3Qgd2UgdGFrZSB0aGUgYGxha2VzYCBkYXRhLCAqKnRoZW4qKgoyLiB3ZSBjbGVhbiBhbGwgb2YgdGhlIHZhcmlhYmxlIG5hbWVzIHRvIHNuYWtlX2Nhc2UsICoqdGhlbioqCjMuIHdlIHJlbmFtZSB0d28gb2YgdGhlIHZhcmlhYmxlcyBmdXJ0aGVyLCAqKnRoZW4qKgo0LiB3ZSBzdWJzZXQgY29sdW1ucyB0byBjbGFyaWZ5IHRoZSBjaGFyYWN0ZXJpc3RpY3Mgd2UgYXJlIGludGVyZXN0ZWQgaW4sICoqdGhlbioqCjUuIHdlIHN1YnNldCBvYnNlcnZhdGlvbnMgdG8gcmV0YWluIG9ubHkgdGhlIGxha2VzIHRoYXQgYXJlIDEwMCBhY3JlcyBvciBncmVhdGVyIGluIHNpemUsICoqYW5kKioKNi4gd2UgYXNzaWduIHRob3NlIGNoYW5nZXMgdG8gYSBuZXcgb2JqZWN0IGNhbGxlZCBgbGFrZXNfbGFyZ2VgLgoKYGBge3IgZnVsbC1waXBlfQpsYWtlcyAlPiUgCiAgY2xlYW5fbmFtZXMoY2FzZSA9ICJzbmFrZSIpICU+JQogIHJlbmFtZSgKICAgIHllYXIgPSB5ciwKICAgIHdhdGVyX2JvZHlfaWQgPSB3YmlkCiAgICApICU+JQogIHNlbGVjdCh3YXRlcl9ib2R5X2lkLCB3YXRlcl9ib2R5LCBzaXplLCB1bml0LCBwb2xsdXRhbnQpICU+JQogIGZpbHRlcihzaXplID49IDEwMCkgLT4gbGFrZXNfbGFyZ2UKCmxha2VzX2xhcmdlCmBgYAoKIyMgQ3JlYXRpbmcgYW5kIE1vZGlmeWluZyBWYXJpYWJsZXMKT2Z0ZW4gd2Ugd2FudCB0byBjcmVhdGUgbmV3IHZhcmlhYmxlcyBhcyB3ZWxsLiBUaGUgYG11dGF0ZSgpYCBmdW5jdGlvbiBmcm9tIGBkcGx5cmAgY2FuIGJlIHVzZWQgdG8gYm90aCBjcmVhdGUgbmV3IHZhcmlhYmxlcyBhbmQgZWRpdCBleGlzdGluZyBvbmVzLiAKCiMjIyBNb2RpZnlpbmcgRXhpc3RpbmcgVmFyaWFibGVzCkZvciBpbnN0YW5jZSwgd2Ugb2Z0ZW4gd2FudCB0byBjb252ZXJ0IHZhcmlhYmxlcyB0byBjaGFyYWN0ZXIgZnJvbSBudW1lcmljIHVzaW5nIGBhcy5jaGFyYWN0ZXIoKWA6CgpgYGB7ciB0by1jaGFyYWN0ZXJ9Cmxha2VzX2xhcmdlICU+JQogIG11dGF0ZShzaXplID0gYXMuY2hhcmFjdGVyKHNpemUpKSAtPiBsYWtlc19jaHIKCmxha2VzX2NocgpgYGAKClRoZSBzaXplIHZhcmlhYmxlIGlzIG5vdyBjaGFyYWN0ZXIgaW5zdGVhZCBvZiBudW1lcmljLCB3aGljaCBjYW4gYmUgdXNlZnVsIHdoZW4gd3JpdGluZyBkYXRhIHRvIHNoYXBlZmlsZXMgKHdoaWNoIGRvIG5vdCBsaWtlIG51bWJlcnMgb3ZlciBhIGNlcnRhaW4gc2l6ZSwgYW4gaXNzdWUgdGhhdCBjcm9wcyB1cCB3aXRoIGxhcmdlIGFyZWFzIG1lYXN1cmVkIGluIHNxdWFyZSBtZXRlcnMgb3IgZmVldCkuCgpXZSBjYW4gY29udmVydCBvdXIgZGF0YSBiYWNrIHdpdGggYGFzLm51bWVyaWMoKWA6CgpgYGB7ciB0by1udW1lcmljfQpsYWtlc19jaHIgJT4lCiAgbXV0YXRlKHNpemUgPSBhcy5udW1lcmljKHNpemUpKQpgYGAKCk5vdyB3ZSBoYXZlIG51bWVyaWMgZGF0YSBhZ2FpbiBpZiB3ZSB3ZXJlIHRvIHdyaXRlIHRoaXMgb2JqZWN0IHRvIG91ciBnbG9iYWwgZW52aXJvbm1lbnQuCgojIyMgQ3JlYXRpbmcgTmV3IFZhcmlhYmxlcwpPbmUgd2F5IGNvbW1vbiB3YXkgd2UgY3JlYXRlIHZhcmlhYmxlcyBpcyB0byByZWNvZGUgdGhlbSBpbnRvIGEgYmluYXJ5IG91dGNvbWUuIEZvciBpbnN0YW5jZSwgd2UgbWF5IHdhbnQgdG8gbWFrZSB0d28gbWFwcyAtIG9uZSBvZiBsYWtlcyB0aGF0IGFyZSBiZXR3ZWVuIDEwMCBhY3JlcyBhbmQgOTk5IGFjcmVzLCBhbmQgb25lIG9mIGxha2VzIHRoYXQgYXJlIDEsMDAwIGFjcmVzIG9yIGdyZWF0ZXIuIFdlIGNhbiBpZGVudGlmeSB0aGVzZSBmZWF0dXJlcyB3aXRoIGEgbG9naWNhbCB2YXJpYWJsZSB0aGF0IGlzIGBUUlVFYCBpZiB0aGUgbGFrZSBpcyBgMTAwMGAgYWNyZXMgb3IgZ3JlYXRlciBhbmQgYEZBTFNFYCBvdGhlcndpc2UuIFdlIHVzZSB0aGUgYGlmZWxzZSgpYCBmdW5jdGlvbiB0byBjcmVhdGUgYW4gZXhwcmVzc2lvbiB3ZSB0ZXN0IGluIGEgYm9vbGVhbiBmYXNoaW9uIGFsb25nIHdpdGggd2hhdCB3ZSByZXR1cm4gaWYgdGhlIHRlc3QgaXMgYFRSVUVgIChpbiB0aGlzIGNhc2UsIGBUUlVFYCksIGFuZCB3aGF0IHdlIHJldHVybiBpZiB0aGUgdGVzdCBpcyBgRkFMU0VgIChpbiB0aGlzIGNhc2UsIGBGQUxTRWApOgoKYGBge3J9Cmxha2VzX2xhcmdlICU+JQogIG11dGF0ZSh2bGFyZ2UgPSBpZmVsc2Uoc2l6ZSA+PSAxMDAwLCBUUlVFLCBGQUxTRSkpICU+JQogIHNlbGVjdCh3YXRlcl9ib2R5LCBzaXplLCB2bGFyZ2UpCmBgYAoKSSd2ZSBzdWJzZXQgdGhlIGNvbHVtbnMgYWZ0ZXJ3YXJkcyBqdXN0IHRvIGlsbHVzdHJhdGUgaG93IHRoaXMgbG9va3MgY2xlYXJseSAtIHlvdSB3b3VsZCBvbmx5IHdhbnQgdG8gZW11bGF0ZSB0aGlzIGlmIHlvdSByZWFsbHkgb25seSB3YW50ZWQgdGhvc2UgdGhyZWUgdmFyaWFibGVzIQoKV2UgY2FuIGRvIHRoZSBzYW1lIHRoaW5nLCBidXQgcmV0dXJuIGNoYXJhY3RlcnMgaW5zdGVhZDoKCmBgYHtyfQpsYWtlc19sYXJnZSAlPiUKICBtdXRhdGUodmxhcmdlID0gaWZlbHNlKHNpemUgPj0gMTAwMCwgIk92ZXIgMTAwMCBhY3JlcyIsICJVbmRlciAxMDAwIGFjcmVzIikpICU+JQogIHNlbGVjdCh3YXRlcl9ib2R5LCBzaXplLCB2bGFyZ2UpCmBgYAoKSW4gYm90aCBjYXNlcywgdGhlIGV4cHJlc3Npb24gYHNpemUgPj0gMTAwMGAgaXMgb3VyIGJvb2xlYW4gdGVzdC4gSWYgdGhlIG9ic2VydmF0aW9uIGlzIGBUUlVFYCB3aXRoIHRoaXMgdGVzdCwgZWl0aGVyIGBUUlVFYCBvciBgIk92ZXIgMTAwMCBhY3JlcyJgIGlzIHJldHVybmVkLiBMaWtld2lzZSwgaWYgdGhlIG9ic2VydmF0aW9uIGlzIGBGQUxTRWAgd2l0aCB0aGlzIHRlc3QsIGVpdGhlciBgRkFMU0VgIG9yIGAiVW5kZXIgMTAwMCBhY3JlcyJgLgoKV2UgY2FuIGRvIHRoZSBzYW1lIHdpdGggb3VyIHRlc3QgZm9yIG1lcmN1cnkgaW4gZmlzaDoKCmBgYHtyfQpsYWtlc19sYXJnZSAlPiUKICBtdXRhdGUobWVyY3VyeSA9IGlmZWxzZShwb2xsdXRhbnQgPT0gIk1lcmN1cnkgaW4gRmlzaCBUaXNzdWUgKFQpIiwgVFJVRSwgRkFMU0UpKSAlPiUKICBzZWxlY3Qod2F0ZXJfYm9keSwgbWVyY3VyeSkKYGBgCgpJZiB3ZSBoYXZlIGEgbG90IG9mIHN0cmluZyBkYXRhLCBpdCBjYW4gYmUgdXNlZnVsIHRvIGxvb2sgZm9yIHNwZWNpZmljIHdvcmRzLCBsaWtlICJNZXJjdXJ5Ii4gV2UgY2FuIGRvIHRoaXMgd2l0aCB0aGUgYHN0cl9kZXRlY3QoKWAgZnVuY3Rpb24gZnJvbSBgc3RyaW5ncmAsIHdoaWNoIGFnYWluIGNyZWF0ZXMgYSBib29sZWFuIHRlc3QgdG8gaWRlbnRpZnkgd2hldGhlciBwYXR0ZXJucyBleGlzdCBpbiBhIGdpdmVuIHN0cmluZzoKCmBgYHtyfQpsYWtlc19sYXJnZSAlPiUKICBtdXRhdGUobWVyY3VyeSA9IGlmZWxzZShzdHJfZGV0ZWN0KHBvbGx1dGFudCwgcGF0dGVybiA9ICJNZXJjdXJ5IiksIFRSVUUsIEZBTFNFKSkgJT4lCiAgc2VsZWN0KHdhdGVyX2JvZHksIHBvbGx1dGFudCwgbWVyY3VyeSkKYGBgCgpXZSBjYW4gbWFrZSB0aGlzIGV2ZW4gbW9yZSBjb21wbGljYXRlZCBpZiB0aGVyZSBhcmUgdHdvIHdvcmRzIHdlIHdhbnQgdG8gc2VhcmNoIGZvci4gRm9yIGluc3RhbmNlLCB3ZSBjb3VsZCBsb29rIGZvciBib3RoICJQaG9zcGhvcnVzIiBvciAiTml0cm9nZW4iIHRvIGJlIHByZXNlbnQ6CgpgYGB7cn0KbGFrZXNfbGFyZ2UgJT4lCiAgbXV0YXRlKHBob3NOaXRybyA9IGlmZWxzZShzdHJfZGV0ZWN0KHBvbGx1dGFudCwgcGF0dGVybiA9ICJQaG9zcGhvcnVzfE5pdHJvZ2VuIiksIFRSVUUsIEZBTFNFKSkgJT4lCiAgc2VsZWN0KHdhdGVyX2JvZHksIHBvbGx1dGFudCwgcGhvc05pdHJvKQpgYGAKCkp1c3QgbGlrZSBiZWZvcmUsIHdlIHVzZSB0aGUgYHxgIGFzIGEgbG9naWNhbCBvcGVyYXRvciBmb3IgdGhlIHdvcmQgIm9yIi4gQW55IHRpbWUgZWl0aGVyIG9mIHRoZXNlIHdvcmRzIGFyZSBmb3VuZCwgd2UgcmV0dXJuIGEgdmFsdWUgb2YgYFRSVUVgLgoKQW5vdGhlciB3YXkgdG8gcmVjb2RlIHZhcmlhYmxlcyBpcyB0byB0YWtlIGxvbmcgc3RyaW5ncyBhbmQgc2hvcnRlbiB0aGVtLiBXZSdsbCB1c2UgYGRpc3RpbmN0KClgIHRvIGdldCBhIGxpc3Qgb2YgdGhlIHBvbGx1dGFudHMgaW4gb3VyIGRhdGE6CgpgYGB7cn0KbGFrZXNfbGFyZ2UgJT4lCiAgZGlzdGluY3QocG9sbHV0YW50KQpgYGAKCklmIHdlIHdhbnQgdG8ga2VlcCBhbGwgb2YgdGhlc2UgZGF0YSBidXQgY3JlYXRlIHNob3J0aGFuZCByZWZlcmVuY2VzIGZvciB0aGVtLCB3ZSBjYW4gdXNlIGBjYXNlX3doZW4oKWAgY29tYmluZWQgd2l0aCBgbXV0YXRlKClgIHRvIHNwZWNpZnkgaG93IGVhY2ggcG9sbHV0YW50IHNob3VsZCBiZSBzaW1wbGlmaWVkOgoKYGBge3J9Cmxha2VzX2xhcmdlICU+JSAKICBtdXRhdGUocG9sbHV0YW50X3NpbXBsZSA9IGNhc2Vfd2hlbigKICAgIHBvbGx1dGFudCA9PSAiQ2hsb3JvcGh5bGwtYSAoVykiIH4gIkNobG9yb3BoeWxsIiwKICAgIHBvbGx1dGFudCA9PSAiTWVyY3VyeSBpbiBGaXNoIFRpc3N1ZSAoVCkiIH4gIk1lcmN1cnkiLAogICAgcG9sbHV0YW50ID09ICJQaG9zcGhvcnVzLCBUb3RhbCAoVykiIH4gIlBob3NwaG9ydXMiLAogICAgcG9sbHV0YW50ID09ICJOaXRyb2dlbiwgVG90YWwgKFcpIiAgfiAiTml0cm9nZW4iLAogICAgcG9sbHV0YW50ID09ICJOdXRyaWVudC9FdXRyb3BoaWNhdGlvbiBCaW9sLiBJbmRpY2F0b3JzIChXKSIgIH4gIkV1dHJvcGhpY2F0aW9uIgogICAgKSkKYGBgCgpUaGUgc3RyaW5ncyBhZnRlciB0aGUgdGlsZGUgKGB+YCkgYXJlIHdoYXQgd2lsbCBiZSByZXR1cm5lZCBhcyBuZXcgdmFsdWVzIGFueSB0aW1lIGFuIG9sZCB2YWx1ZSBpcyBkZXRlY3RlZC4gTm90aWNlIGFnYWluIGhvdyB0aGUgZXhhY3RseSBlcXVhbCB0byByZWxhdGlvbmFsIG9wZXJhdG9yIChpLmUuIGA9PWApIGlzIHVzZWQgaGVyZSEKCiMjIEdyb3VwaW5nIGFuZCBTdW1tYXJpemluZyBPYnNlcnZhdGlvbnMKU2luY2Ugd2UgaGF2ZSBib2RpZXMgb2Ygd2F0ZXIgaW4gbXVsdGlwbGUgb2JzZXJ2YXRpb25zLCB3ZSBtYXkgd2FudCB0byBzdW1tYXJpemUgdGhlbS4gRm9yIGluc3RhbmNlLCB3ZSBjb3VsZCBhc2sgaG93IG1hbnkgZGlzdGluY3QgcG9sbHV0YW50cyBhcmUgbGlzdGVkIHBlciBib2R5IG9mIHdhdGVyLCBhbmQgd2hldGhlciBvciBub3QgbWVyY3VyeSBpcyBvbmUgb2YgdGhlbS4gVGhlIGZvbGxvd2luZyBwaXBlbGluZSBncm91cHMgb3VyIGRhdGEgYW5kIHRoZW4gc3VtbWFyaXplcyBpdCB0byBhbnN3ZXIgdGhlc2UgdHdvIHF1ZXN0aW9ucy4KCldlOgoKMS4gVGFrZSB0aGUgYGxha2VzX2xhcmdlYCBkYXRhLCAqKnRoZW4qKgoyLiB3ZSBjcmVhdGUgYSBsaXN0IG9mIGRpc3RpbmN0IGNvbWJpbmF0aW9ucyBvZiBsYWtlIG5hbWVzIGFuZCBwb2xsdXRhbnRzLCAqKnRoZW4qKgozLiBjcmVhdGUgb3VyIGJpbmFyeSBpbmRpY2F0b3IgZm9yIHRoZSBwcmVzZW5jZSBvZiBtZXJjdXJ5LCAqKnRoZW4qKgo0LiBncm91cCBvdXIgb2JzZXJ2YXRpb25zIGJ5IGJvZHkgb2Ygd2F0ZXIsICoqdGhlbioqCjUuIGNyZWF0ZSBuZXcgc3VtbWFyeSB2YXJpYWJsZXMgdGhhdCBjb3VudCB0aGUgbnVtYmVyIG9mIHBvbGx1dGFudHMgcGVyIGJvZHkgb2Ygd2F0ZXIgYW5kIHRlc3Qgd2hldGhlciBhbnkgb2YgdGhlIHZhbHVlcyBmb3IgYG1lcmN1cnlgIGZvciBlYWNoIGJvZHkgb2Ygd2F0ZXIgYXJlIGBUUlVFYCwgKip0aGVuKioKNi4gcmUtb3JkZXIgdGhlIG91dHB1dCBpbiBkZXNjZW5kaW5nIG9yZGVyICh0aGUgcm9sZSBvZiBgZGVzYygpYCkgc28gdGhlIGxha2Ugd2l0aCB0aGUgbGFyZ2VzdCBudW1iZXIgb2YgcG9sbHV0YW50cyBpcyBsaXN0ZWQgZmlyc3QuCgpgYGB7cn0KbGFrZXNfbGFyZ2UgJT4lCiAgZGlzdGluY3Qod2F0ZXJfYm9keSwgcG9sbHV0YW50KSAlPiUKICBtdXRhdGUobWVyY3VyeSA9IGlmZWxzZShwb2xsdXRhbnQgPT0gIk1lcmN1cnkgaW4gRmlzaCBUaXNzdWUgKFQpIiwgVFJVRSwgRkFMU0UpKSAlPiUKICBncm91cF9ieSh3YXRlcl9ib2R5KSAlPiUKICBzdW1tYXJpemUoCiAgICBwb2xsdXRhbnRfY291bnQgPSBuKCksCiAgICBtZXJjdXJ5ID0gYW55KG1lcmN1cnkpKSAlPiUKICBhcnJhbmdlKGRlc2MocG9sbHV0YW50X2NvdW50KSkKYGBgCgoKYGBge3IgbW92ZS10by1kb2NzLCBpbmNsdWRlPUZBTFNFfQojIHlvdSBkbyBuZWVkIHRvIGluY2x1ZGUgdGhpcyBpbiBhbnkgbm90ZWJvb2sgeW91IGNyZWF0ZSBmb3IgdGhpcyBjbGFzcwpmczo6ZmlsZV9jb3B5KGhlcmU6OmhlcmUoImV4YW1wbGVzIiwgIm1lZXRpbmctMi0xLWNvbXBsZXRlLm5iLmh0bWwiKSwgCiAgICAgICAgICAgICAgaGVyZTo6aGVyZSgiZG9jcyIsICJpbmRleC5uYi5odG1sIiksIAogICAgICAgICAgICAgIG92ZXJ3cml0ZSA9IFRSVUUpCmBgYAo=