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.

This is an R Markdown document. Follow the link to learn more about R Markdown and the notebook format used during the workshop.

Setup

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.4
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.4.4     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.0
## ✔ purrr     1.0.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors

This gives you info on which packages it actually loaded, because when you install tidyverse, it installs ~25 packages, but it only loads the ones listed. Tidyverse packages also tend to be verbose in warning you when there are functions with the same name in multiple packages.

Background

Tidyverse packages do a few things:

Tidyverse packages are particularly useful for:

Data

Let’s import the data we’ll be using. The data is from the Stanford Open Policing Project and includes vehicle stops by the Evanston police in 2017. We’re reading the data in from a URL directly.

We’re going to use the read_csv function from the readr package, which is part of the tidyverse. The read_csv function works like read.csv except is has some different defaults, guesses data types a bit differently, and produces a tibble instead of a normal data frame (details coming).

police <- read_csv("https://raw.githubusercontent.com/nuitrcs/r-tidyverse/main/data/ev_police.csv")
## Rows: 14792 Columns: 29
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr  (10): beat, subject_race, subject_sex, department_name, type, violation...
## dbl   (8): raw_row_number, location, department_id, vehicle_year, raw_Driver...
## lgl   (9): subject_age, citation_issued, warning_issued, contraband_found, c...
## date  (1): date
## time  (1): time
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

The output message that you get tells you what data type it guessed for each column based on the format of the information. “chr” is character or text data, “dbl” is numeric (stands for double, which is technical term for a type of number), “lgl” is logical/Boolean (TRUE/FALSE). Note that it also automatically read and identified date and time values and converted them to date and time objects – not just string/character data.

We can also manually specify column types for cases where the assumption that read_csv makes is wrong. We use the col_types argument (similar to colClasses for read.csv). Let’s make the location to be character data, since it is zip codes – zip codes should not be treated as numbers.

police <- read_csv("https://raw.githubusercontent.com/nuitrcs/r-tidyverse/main/data/ev_police.csv",
                   col_types=c("location"="c"))

EXERCISE 1

Remember: you need to have loaded tidyverse, so execute the cells above.

We have a dataset that includes ISO two-letter country codes. The country code for Namibia is NA, so we don’t want to read “NA” in as missing, which it does by default (see how “NA” is grayed out in the output below for the Namibia country code?).

Look at the documentation (help) page for read_csv. You can open it by typing ?read_csv in the console. The na argument determines what values are imported as missing NA.

Change the code below so that only empty strings “” and “N/A” values are imported as missing (not “NA”). Look at fix_data after importing so you can check the values.

fix_data <- read_csv("https://raw.githubusercontent.com/nuitrcs/r-tidyverse/main/data/missing.csv")
## Rows: 8 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): country_name, country_code, continent
## dbl (1): id
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
fix_data

You can write your code here:

Tibbles

You may have noticed above that read_csv imported the data as something called a Tibble. Tibbles are the tidyverse version of a data frame. You can use them as you would a data frame (they are one), but they behave in slightly different ways.

police

The most observable difference is that tibbles will only print 10 rows and the columns that will fit in your console. When they print, they print a list of column names and the types of the columns that are shown.

To view the dataset, use View():

View(police)

When using [] notation to subset them, they will always return a tibble. In contrast, data frames sometimes return a data frame and sometimes return just a vector.

police[, 1]
as.data.frame(police)[, 1]

dplyr

dplyr is the core package of the tidyverse. It includes functions for working with tibbles (or any data frames). While you can still use base R operations on tibbles/data frames, such as using $ and [] subsetting like we did above, dplyr provides alternatives to all of the common data manipulation tasks.

Here, we’re just going to look at the basics of subsetting data to get a feel for how tidyverse functions typically work. Next session, we’ll get into variations on subsetting data and some other dplyr functions.

Before we start, let’s remember what columns are in our data:

names(police)

select

The select() function lets us choose which columns (or variables) we want to keep in our data.

The data frame is the first input, and the name of the column is the second. We do not have to put quotes around the column name.

select(police, subject_race)

If we want to select additional columns, we can just list the column names as additional inputs, each column name separated by commas:

select(police, subject_race, outcome)

As with [] indexing, columns will be returned in the order specified:

select(police, subject_sex, subject_race, date)

We could also use the column index number if we wanted to instead. We don’t need to put the values in c() like we would with [] (but we could).

select(police, 1, 4, 10)

Yes, there are other ways to specify which columns you want. We’ll cover those next session.

EXERCISE 2

Remember: you need to have loaded tidyverse, and the police data, so execute the cells above.

Convert this base R expression: police[,c("violation", "citation_issued", "warning_issued")] to use select() instead to do the same thing. You can write your code here:

filter

To choose which rows should remain in our data, we use filter(). As with [], we write expressions that evaluate to TRUE or FALSE for each row. Like select(), we can use the column names without quotes.

filter(police, location == "60202")

Note that we use == to test for equality and get TRUE/FALSE output. You can also write more complicated expressions – anything that will evaluate to a vector of TRUE/FALSE values.

filter(police, is.na(beat))

Variables (columns) that are already logical (TRUE/FALSE values), can be used to filter:

filter(police, contraband_found)

EXERCISE 3

Use filter() to choose the rows where subject_race is “white”.

The equivalent base R expression would be police[police$subject_race == "white",]. You can write your code here:

slice

Unlike select(), we can’t use row numbers to index which rows we want with filter. This gives an error:

filter(police, 10)

If we did need to use the row index (row number) to select which rows we want, we can use the slice() function.

slice(police, 10)
slice(police, 10:15)

We don’t usually use slice() in this way when working with dplyr. This is because we ideally want to be working with well-structured data, where we can reorder the rows without losing information. If reordering the rows in the dataset would result in a loss of information (it would mess up your data), then the dataset is missing an important variable – maybe just a sequence index. You should always be able to use a variable to order the data if needed.

Pipe: Chaining Commands Together

So, we can choose rows and choose columns separately; how do we combine these operations? dplyr, and other tidyverse, commands can be strung together is a series with a %>% (say/read: pipe) operator. The pipe takes the output of the command on the left and makes that the first input to the command on the right. (If you are familiar with working in a terminal/at the command line, it works like a bash pipe character |.)

This works because the functions all take a data frame as the first input, and they return a data frame as the output.

We can rewrite

select(police, date, time)

as

police %>% select(date, time)

and you’ll often see code formatted, so %>% is at the end of each line, and the following line that are still part of the same expression are indented:

police %>%
  select(date, time)

The pipe comes from a package called magrittr, which has additional special operators in it that you can use. The keyboard shortcut for %>% is command-shift-M (Mac) or control-shift-M (Windows).

We can use the pipe to string together multiple commands operating on the same data frame:

police %>%
  select(subject_race, subject_sex) %>%
  filter(subject_race == "white")

We would read the %>% in the command above as “then” if reading the code outloud: from police, select subject_race and subject_sex, then filter where subject_race is white.

This works because the dplyr functions take a tibble/data frame as the first argument (input) and return a tibble/data frame as the output. This makes it easy to pass a data frame through multiple operations, changing it one step at a time.

Order does matter, as the commands are executed in order. So this would give us an error:

police %>%
  select(subject_sex, outcome) %>%
  filter(subject_race == "white")

Because subject_race is no longer in the data frame once we try to filter with it. We’d have to reverse the order:

police %>%
  filter(subject_race == "white") %>%
  select(subject_sex, outcome)

You can use the pipe operator to string together commands outside of the tidyverse as well, and it works with any input and output, not just data frames:

# sum(is.na(police$beat))
is.na(police$beat) %>% sum()

EXERCISE 4

Select the date, time, and outcome (columns) of stops that occur in beat “71” (rows). Make use of the %>% operator.

The equivalent base R expression would be: police[police$beat == "71", c("date", "time", "outcome")]

Hint: remember that a column needs to still be in the data frame if you’re going to use the column to filter.

Note that so far, we haven’t actually changed the police data frame at all. We’ve written expressions to give us output, but we haven’t saved it.

Sometimes we may still want to save the result of some expression, such as after performing a bunch of data cleaning steps. We can assign the output of piped commands as we would with any other expression.

police60201 <- police %>%
  filter(location == "60201") %>%
  select(date, time, beat, type, outcome) 

EXERCISE 5

Select only vehicle_year and vehicle_make columns for observations where there were contraband_weapons. You can write your code here:

Recap

We learned what tibbles are, the dplyr equivalents of indexing and subsetting a data frame, and the pipe %>% operator.

Next time we’re going to look at some more complicated use cases for select, filter, and slice, as well as learn mutate to create or change variables in our datasets.

Answers to the exercises

Exercise 1

fix_data <- read_csv("https://raw.githubusercontent.com/nuitrcs/r-tidyverse/main/data/missing.csv", na = c("", "N/A"))
fix_data

Exercise 2

select(police, violation, citation_issued, warning_issued)

Exercise 3

filter(police, subject_race == "white")

Exercise 4

police %>% 
  filter(beat == "71") %>% 
  select(date, time, outcome)

Exercise 5

police %>% 
  filter(contraband_weapons == TRUE) %>% 
  select(vehicle_year, vehicle_make)

You can also write:

police %>% 
  filter(contraband_weapons) %>% 
  select(vehicle_year, vehicle_make)
LS0tCnRpdGxlOiAiVGlkeXZlcnNlIGJhc2ljcyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCmVkaXRvcl9vcHRpb25zOgogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgpgYGB7ciwgc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgeW91IGRvbid0IG5lZWQgdG8gcnVuIHRoaXMgd2hlbiB3b3JraW5nIGluIFJTdHVkaW8Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGV2YWw9RkFMU0UpICAjIHdoZW4gbWFraW5nIHRoZSBodG1sIHZlcnNpb24gb2YgdGhpcyBmaWxlLCBkb24ndCBleGVjdXRlIHRoZSBjb2RlCmBgYAoKKlRoZSBvdXRwdXQgb2YgbW9zdCBvZiB0aGUgUiBjaHVua3MgaXNuJ3QgaW5jbHVkZWQgaW4gdGhlIEhUTUwgdmVyc2lvbiBvZiB0aGUgZmlsZSB0byBrZWVwIGl0IHRvIGEgbW9yZSByZWFzb25hYmxlIGZpbGUgc2l6ZS4gIFlvdSBjYW4gcnVuIHRoZSBjb2RlIGluIFIgdG8gc2VlIHRoZSBvdXRwdXQuKgoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vKSBkb2N1bWVudC4gIEZvbGxvdyB0aGUgbGluayB0byBsZWFybiBtb3JlIGFib3V0IFIgTWFya2Rvd24gYW5kIHRoZSBub3RlYm9vayBmb3JtYXQgdXNlZCBkdXJpbmcgdGhlIHdvcmtzaG9wLgoKIyBTZXR1cAoKYGBge3IsIGV2YWw9VFJVRX0KbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKVGhpcyBnaXZlcyB5b3UgaW5mbyBvbiB3aGljaCBwYWNrYWdlcyBpdCBhY3R1YWxseSBsb2FkZWQsIGJlY2F1c2Ugd2hlbiB5b3UgaW5zdGFsbCB0aWR5dmVyc2UsIGl0IGluc3RhbGxzIH4yNSBwYWNrYWdlcywgYnV0IGl0IG9ubHkgbG9hZHMgdGhlIG9uZXMgbGlzdGVkLiAgVGlkeXZlcnNlIHBhY2thZ2VzIGFsc28gdGVuZCB0byBiZSB2ZXJib3NlIGluIHdhcm5pbmcgeW91IHdoZW4gdGhlcmUgYXJlIGZ1bmN0aW9ucyB3aXRoIHRoZSBzYW1lIG5hbWUgaW4gbXVsdGlwbGUgcGFja2FnZXMuCgojIEJhY2tncm91bmQKClRpZHl2ZXJzZSBwYWNrYWdlcyBkbyBhIGZldyB0aGluZ3M6CgoqIGZpeCBzb21lIG9mIHRoZSBhbm5veWluZyBwYXJ0cyBvZiB1c2luZyBSLCBzdWNoIGFzIGNoYW5naW5nIGRlZmF1bHQgb3B0aW9ucyB3aGVuIGltcG9ydGluZyBkYXRhIGZpbGVzIGFuZCBwcmV2ZW50aW5nIGxhcmdlIGRhdGEgZnJhbWVzIGZyb20gcHJpbnRpbmcgdG8gdGhlIGNvbnNvbGUKKiBhcmUgZm9jdXNlZCBvbiB3b3JraW5nIHdpdGggZGF0YSBmcmFtZXMgKGFuZCB0aGVpciBjb2x1bW5zKSwgcmF0aGVyIHRoYW4gaW5kaXZpZHVhbCB2ZWN0b3JzCiogdXN1YWxseSB0YWtlIGEgZGF0YSBmcmFtZSBhcyB0aGUgZmlyc3QgaW5wdXQgdG8gYSBmdW5jdGlvbiwgYW5kIHJldHVybiBhIGRhdGEgZnJhbWUgYXMgdGhlIG91dHB1dCBvZiBhIGZ1bmN0aW9uLCBzbyB0aGF0IGZ1bmN0aW9uIGNhbGxzIGNhbiBiZSBtb3JlIGVhc2lseSBzdHJ1bmcgdG9nZXRoZXIgaW4gYSBzZXF1ZW5jZQoqIHNoYXJlIHNvbWUgY29tbW9uIG5hbWluZyBjb252ZW50aW9ucyBmb3IgZnVuY3Rpb25zIGFuZCBhcmd1bWVudHMgdGhhdCBoYXZlIGEgZ29hbCBvZiBtYWtpbmcgY29kZSBtb3JlIHJlYWRhYmxlCiogdGVuZCB0byBiZSB2ZXJib3NlLCBvcGluaW9uYXRlZCwgYW5kIGFyZSBhY3RpdmVseSB3b3JraW5nIHRvIHByb3ZpZGUgbW9yZSB1c2VmdWwgZXJyb3IgbWVzc2FnZXMKClRpZHl2ZXJzZSBwYWNrYWdlcyBhcmUgcGFydGljdWxhcmx5IHVzZWZ1bCBmb3I6CgoqIGRhdGEgZXhwbG9yYXRpb24KKiByZXNoYXBpbmcgZGF0YSBzZXRzCiogY29tcHV0aW5nIHN1bW1hcnkgbWVhc3VyZXMgb3ZlciBncm91cHMKKiBjbGVhbmluZyB1cCBkaWZmZXJlbnQgdHlwZXMgb2YgZGF0YQoqIHJlYWRpbmcgYW5kIHdyaXRpbmcgZGF0YQoKIyBEYXRhCgpMZXQncyBpbXBvcnQgdGhlIGRhdGEgd2UnbGwgYmUgdXNpbmcuICBUaGUgZGF0YSBpcyBmcm9tIHRoZSBbU3RhbmZvcmQgT3BlbiBQb2xpY2luZyBQcm9qZWN0XShodHRwczovL29wZW5wb2xpY2luZy5zdGFuZm9yZC5lZHUvZGF0YS8pIGFuZCBpbmNsdWRlcyB2ZWhpY2xlIHN0b3BzIGJ5IHRoZSBFdmFuc3RvbiBwb2xpY2UgaW4gMjAxNy4gIFdlJ3JlIHJlYWRpbmcgdGhlIGRhdGEgaW4gZnJvbSBhIFVSTCBkaXJlY3RseS4gIAoKV2UncmUgZ29pbmcgdG8gdXNlIHRoZSBgcmVhZF9jc3ZgIGZ1bmN0aW9uIGZyb20gdGhlIGByZWFkcmAgcGFja2FnZSwgd2hpY2ggaXMgcGFydCBvZiB0aGUgdGlkeXZlcnNlLiAgVGhlIGByZWFkX2NzdmAgZnVuY3Rpb24gd29ya3MgbGlrZSBgcmVhZC5jc3ZgIGV4Y2VwdCBpcyBoYXMgc29tZSBkaWZmZXJlbnQgZGVmYXVsdHMsIGd1ZXNzZXMgZGF0YSB0eXBlcyBhIGJpdCBkaWZmZXJlbnRseSwgYW5kIHByb2R1Y2VzIGEgdGliYmxlIGluc3RlYWQgb2YgYSBub3JtYWwgZGF0YSBmcmFtZSAoZGV0YWlscyBjb21pbmcpLiAgCgpgYGB7ciwgZXZhbD1UUlVFfQpwb2xpY2UgPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9udWl0cmNzL3ItdGlkeXZlcnNlL21haW4vZGF0YS9ldl9wb2xpY2UuY3N2IikKYGBgCgpUaGUgb3V0cHV0IG1lc3NhZ2UgdGhhdCB5b3UgZ2V0IHRlbGxzIHlvdSB3aGF0IGRhdGEgdHlwZSBpdCBndWVzc2VkIGZvciBlYWNoIGNvbHVtbiBiYXNlZCBvbiB0aGUgZm9ybWF0IG9mIHRoZSBpbmZvcm1hdGlvbi4gICJjaHIiIGlzIGNoYXJhY3RlciBvciB0ZXh0IGRhdGEsICJkYmwiIGlzIG51bWVyaWMgKHN0YW5kcyBmb3IgZG91YmxlLCB3aGljaCBpcyB0ZWNobmljYWwgdGVybSBmb3IgYSB0eXBlIG9mIG51bWJlciksICJsZ2wiIGlzIGxvZ2ljYWwvQm9vbGVhbiAoVFJVRS9GQUxTRSkuICBOb3RlIHRoYXQgaXQgYWxzbyBhdXRvbWF0aWNhbGx5IHJlYWQgYW5kIGlkZW50aWZpZWQgZGF0ZSBhbmQgdGltZSB2YWx1ZXMgYW5kIGNvbnZlcnRlZCB0aGVtIHRvIGRhdGUgYW5kIHRpbWUgb2JqZWN0cyAtLSBub3QganVzdCBzdHJpbmcvY2hhcmFjdGVyIGRhdGEuICAgIAoKV2UgY2FuIGFsc28gbWFudWFsbHkgc3BlY2lmeSBjb2x1bW4gdHlwZXMgZm9yIGNhc2VzIHdoZXJlIHRoZSBhc3N1bXB0aW9uIHRoYXQgYHJlYWRfY3N2YCBtYWtlcyBpcyB3cm9uZy4gIFdlIHVzZSB0aGUgYGNvbF90eXBlc2AgYXJndW1lbnQgKHNpbWlsYXIgdG8gY29sQ2xhc3NlcyBmb3IgYHJlYWQuY3N2YCkuICBMZXQncyBtYWtlIHRoZSBsb2NhdGlvbiB0byBiZSBjaGFyYWN0ZXIgZGF0YSwgc2luY2UgaXQgaXMgemlwIGNvZGVzIC0tIHppcCBjb2RlcyBzaG91bGQgbm90IGJlIHRyZWF0ZWQgYXMgbnVtYmVycy4KCgpgYGB7ciwgZXZhbD1UUlVFfQpwb2xpY2UgPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9udWl0cmNzL3ItdGlkeXZlcnNlL21haW4vZGF0YS9ldl9wb2xpY2UuY3N2IiwKICAgICAgICAgICAgICAgICAgIGNvbF90eXBlcz1jKCJsb2NhdGlvbiI9ImMiKSkKYGBgCgojIyMgRVhFUkNJU0UgMQoKUmVtZW1iZXI6IHlvdSBuZWVkIHRvIGhhdmUgbG9hZGVkIHRpZHl2ZXJzZSwgc28gZXhlY3V0ZSB0aGUgY2VsbHMgYWJvdmUuCgpXZSBoYXZlIGEgZGF0YXNldCB0aGF0IGluY2x1ZGVzIFtJU08gdHdvLWxldHRlciBjb3VudHJ5IGNvZGVzXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9JU09fMzE2Ni0xX2FscGhhLTIpLiAgVGhlIGNvdW50cnkgY29kZSBmb3IgTmFtaWJpYSBpcyBOQSwgc28gd2UgZG9uJ3Qgd2FudCB0byByZWFkICJOQSIgaW4gYXMgbWlzc2luZywgd2hpY2ggaXQgZG9lcyBieSBkZWZhdWx0IChzZWUgaG93ICJOQSIgaXMgZ3JheWVkIG91dCBpbiB0aGUgb3V0cHV0IGJlbG93IGZvciB0aGUgTmFtaWJpYSBjb3VudHJ5IGNvZGU/KS4KCkxvb2sgYXQgdGhlIGRvY3VtZW50YXRpb24gKGhlbHApIHBhZ2UgZm9yIGByZWFkX2NzdmAuICBZb3UgY2FuIG9wZW4gaXQgYnkgdHlwaW5nIGA/cmVhZF9jc3ZgIGluIHRoZSBjb25zb2xlLiAgVGhlIGBuYWAgYXJndW1lbnQgZGV0ZXJtaW5lcyB3aGF0IHZhbHVlcyBhcmUgaW1wb3J0ZWQgYXMgbWlzc2luZyBgTkFgLiAgCgpDaGFuZ2UgdGhlIGNvZGUgYmVsb3cgc28gdGhhdCAqKm9ubHkqKiBlbXB0eSBzdHJpbmdzICIiIGFuZCAiTi9BIiB2YWx1ZXMgYXJlIGltcG9ydGVkIGFzIG1pc3NpbmcgKG5vdCAiTkEiKS4gIExvb2sgYXQgYGZpeF9kYXRhYCBhZnRlciBpbXBvcnRpbmcgc28geW91IGNhbiBjaGVjayB0aGUgdmFsdWVzLgoKYGBge3IsIGV2YWw9VFJVRX0KZml4X2RhdGEgPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9udWl0cmNzL3ItdGlkeXZlcnNlL21haW4vZGF0YS9taXNzaW5nLmNzdiIpCmZpeF9kYXRhCmBgYAoKWW91IGNhbiB3cml0ZSB5b3VyIGNvZGUgaGVyZToKCmBgYHtyfQoKYGBgCgojIFRpYmJsZXMKCllvdSBtYXkgaGF2ZSBub3RpY2VkIGFib3ZlIHRoYXQgYHJlYWRfY3N2YCBpbXBvcnRlZCB0aGUgZGF0YSBhcyBzb21ldGhpbmcgY2FsbGVkIGEgVGliYmxlLiAgVGliYmxlcyBhcmUgdGhlIHRpZHl2ZXJzZSB2ZXJzaW9uIG9mIGEgZGF0YSBmcmFtZS4gIFlvdSBjYW4gdXNlIHRoZW0gYXMgeW91IHdvdWxkIGEgZGF0YSBmcmFtZSAodGhleSBhcmUgb25lKSwgYnV0IHRoZXkgYmVoYXZlIGluIHNsaWdodGx5IGRpZmZlcmVudCB3YXlzLgoKYGBge3IsIGV2YWw9VFJVRX0KcG9saWNlCmBgYAoKVGhlIG1vc3Qgb2JzZXJ2YWJsZSBkaWZmZXJlbmNlIGlzIHRoYXQgdGliYmxlcyB3aWxsIG9ubHkgcHJpbnQgMTAgcm93cyBhbmQgdGhlIGNvbHVtbnMgdGhhdCB3aWxsIGZpdCBpbiB5b3VyIGNvbnNvbGUuICBXaGVuIHRoZXkgcHJpbnQsIHRoZXkgcHJpbnQgYSBsaXN0IG9mIGNvbHVtbiBuYW1lcyBhbmQgdGhlIHR5cGVzIG9mIHRoZSBjb2x1bW5zIHRoYXQgYXJlIHNob3duLiAgCgpUbyB2aWV3IHRoZSBkYXRhc2V0LCB1c2UgYFZpZXcoKWA6CgpgYGB7cn0KVmlldyhwb2xpY2UpCmBgYAoKV2hlbiB1c2luZyBbXSBub3RhdGlvbiB0byBzdWJzZXQgdGhlbSwgdGhleSB3aWxsIGFsd2F5cyByZXR1cm4gYSB0aWJibGUuICBJbiBjb250cmFzdCwgZGF0YSBmcmFtZXMgc29tZXRpbWVzIHJldHVybiBhIGRhdGEgZnJhbWUgYW5kIHNvbWV0aW1lcyByZXR1cm4ganVzdCBhIHZlY3Rvci4KCmBgYHtyfQpwb2xpY2VbLCAxXQphcy5kYXRhLmZyYW1lKHBvbGljZSlbLCAxXQpgYGAKCiMgZHBseXIKCmRwbHlyIGlzIHRoZSBjb3JlIHBhY2thZ2Ugb2YgdGhlIHRpZHl2ZXJzZS4gIEl0IGluY2x1ZGVzIGZ1bmN0aW9ucyBmb3Igd29ya2luZyB3aXRoIHRpYmJsZXMgKG9yIGFueSBkYXRhIGZyYW1lcykuICBXaGlsZSB5b3UgY2FuIHN0aWxsIHVzZSBiYXNlIFIgb3BlcmF0aW9ucyBvbiB0aWJibGVzL2RhdGEgZnJhbWVzLCBzdWNoIGFzIHVzaW5nIGAkYCBhbmQgYFtdYCBzdWJzZXR0aW5nIGxpa2Ugd2UgZGlkIGFib3ZlLCBkcGx5ciBwcm92aWRlcyBhbHRlcm5hdGl2ZXMgdG8gYWxsIG9mIHRoZSBjb21tb24gZGF0YSBtYW5pcHVsYXRpb24gdGFza3MuCgpIZXJlLCB3ZSdyZSBqdXN0IGdvaW5nIHRvIGxvb2sgYXQgdGhlIGJhc2ljcyBvZiBzdWJzZXR0aW5nIGRhdGEgdG8gZ2V0IGEgZmVlbCBmb3IgaG93IHRpZHl2ZXJzZSBmdW5jdGlvbnMgdHlwaWNhbGx5IHdvcmsuICAgTmV4dCBzZXNzaW9uLCB3ZSdsbCBnZXQgaW50byB2YXJpYXRpb25zIG9uIHN1YnNldHRpbmcgZGF0YSBhbmQgc29tZSBvdGhlciBkcGx5ciBmdW5jdGlvbnMuCgpCZWZvcmUgd2Ugc3RhcnQsIGxldCdzIHJlbWVtYmVyIHdoYXQgY29sdW1ucyBhcmUgaW4gb3VyIGRhdGE6CgpgYGB7cn0KbmFtZXMocG9saWNlKQpgYGAKCgojIyBzZWxlY3QKClRoZSBgc2VsZWN0KClgIGZ1bmN0aW9uIGxldHMgdXMgY2hvb3NlIHdoaWNoIGNvbHVtbnMgKG9yIHZhcmlhYmxlcykgd2Ugd2FudCB0byBrZWVwIGluIG91ciBkYXRhLgoKVGhlIGRhdGEgZnJhbWUgaXMgdGhlIGZpcnN0IGlucHV0LCBhbmQgdGhlIG5hbWUgb2YgdGhlIGNvbHVtbiBpcyB0aGUgc2Vjb25kLiAgV2UgZG8gbm90IGhhdmUgdG8gcHV0IHF1b3RlcyBhcm91bmQgdGhlIGNvbHVtbiBuYW1lLiAgCgpgYGB7cn0Kc2VsZWN0KHBvbGljZSwgc3ViamVjdF9yYWNlKQpgYGAKCklmIHdlIHdhbnQgdG8gc2VsZWN0IGFkZGl0aW9uYWwgY29sdW1ucywgd2UgY2FuIGp1c3QgbGlzdCB0aGUgY29sdW1uIG5hbWVzIGFzIGFkZGl0aW9uYWwgaW5wdXRzLCBlYWNoIGNvbHVtbiBuYW1lIHNlcGFyYXRlZCBieSBjb21tYXM6CgpgYGB7cn0Kc2VsZWN0KHBvbGljZSwgc3ViamVjdF9yYWNlLCBvdXRjb21lKQpgYGAKCkFzIHdpdGggYFtdYCBpbmRleGluZywgY29sdW1ucyB3aWxsIGJlIHJldHVybmVkIGluIHRoZSBvcmRlciBzcGVjaWZpZWQ6CgpgYGB7cn0Kc2VsZWN0KHBvbGljZSwgc3ViamVjdF9zZXgsIHN1YmplY3RfcmFjZSwgZGF0ZSkKYGBgCgoKV2UgY291bGQgYWxzbyB1c2UgdGhlIGNvbHVtbiBpbmRleCBudW1iZXIgaWYgd2Ugd2FudGVkIHRvIGluc3RlYWQuICBXZSBkb24ndCBuZWVkIHRvIHB1dCB0aGUgdmFsdWVzIGluIGBjKClgIGxpa2Ugd2Ugd291bGQgd2l0aCBgW11gIChidXQgd2UgY291bGQpLgoKYGBge3J9CnNlbGVjdChwb2xpY2UsIDEsIDQsIDEwKQpgYGAKClllcywgdGhlcmUgYXJlIG90aGVyIHdheXMgdG8gc3BlY2lmeSB3aGljaCBjb2x1bW5zIHlvdSB3YW50LiAgV2UnbGwgY292ZXIgdGhvc2UgbmV4dCBzZXNzaW9uLiAgCgojIyMgRVhFUkNJU0UgMgoKUmVtZW1iZXI6IHlvdSBuZWVkIHRvIGhhdmUgbG9hZGVkIHRpZHl2ZXJzZSwgYW5kIHRoZSBwb2xpY2UgZGF0YSwgc28gZXhlY3V0ZSB0aGUgY2VsbHMgYWJvdmUuCgpDb252ZXJ0IHRoaXMgYmFzZSBSIGV4cHJlc3Npb246IGBwb2xpY2VbLGMoInZpb2xhdGlvbiIsICJjaXRhdGlvbl9pc3N1ZWQiLCAid2FybmluZ19pc3N1ZWQiKV1gIHRvIHVzZSBgc2VsZWN0KClgIGluc3RlYWQgdG8gZG8gdGhlIHNhbWUgdGhpbmcuIFlvdSBjYW4gd3JpdGUgeW91ciBjb2RlIGhlcmU6CgpgYGB7cn0KCmBgYAoKIyMgZmlsdGVyCgpUbyBjaG9vc2Ugd2hpY2ggcm93cyBzaG91bGQgcmVtYWluIGluIG91ciBkYXRhLCB3ZSB1c2UgYGZpbHRlcigpYC4gIEFzIHdpdGggYFtdYCwgd2Ugd3JpdGUgZXhwcmVzc2lvbnMgdGhhdCBldmFsdWF0ZSB0byBUUlVFIG9yIEZBTFNFIGZvciBlYWNoIHJvdy4gIExpa2UgYHNlbGVjdCgpYCwgd2UgY2FuIHVzZSB0aGUgY29sdW1uIG5hbWVzIHdpdGhvdXQgcXVvdGVzLgoKCmBgYHtyfQpmaWx0ZXIocG9saWNlLCBsb2NhdGlvbiA9PSAiNjAyMDIiKQpgYGAKCk5vdGUgdGhhdCB3ZSB1c2UgYD09YCB0byB0ZXN0IGZvciBlcXVhbGl0eSBhbmQgZ2V0IFRSVUUvRkFMU0Ugb3V0cHV0LiAgWW91IGNhbiBhbHNvIHdyaXRlIG1vcmUgY29tcGxpY2F0ZWQgZXhwcmVzc2lvbnMgLS0gYW55dGhpbmcgdGhhdCB3aWxsIGV2YWx1YXRlIHRvIGEgdmVjdG9yIG9mIFRSVUUvRkFMU0UgdmFsdWVzLgoKYGBge3J9CmZpbHRlcihwb2xpY2UsIGlzLm5hKGJlYXQpKQpgYGAKClZhcmlhYmxlcyAoY29sdW1ucykgdGhhdCBhcmUgYWxyZWFkeSBsb2dpY2FsIChUUlVFL0ZBTFNFIHZhbHVlcyksIGNhbiBiZSB1c2VkIHRvIGZpbHRlcjoKCmBgYHtyfQpmaWx0ZXIocG9saWNlLCBjb250cmFiYW5kX2ZvdW5kKQpgYGAKCgojIyMgRVhFUkNJU0UgMwoKVXNlIGBmaWx0ZXIoKWAgdG8gY2hvb3NlIHRoZSByb3dzIHdoZXJlIHN1YmplY3RfcmFjZSBpcyAid2hpdGUiLiAgCgpUaGUgZXF1aXZhbGVudCBiYXNlIFIgZXhwcmVzc2lvbiB3b3VsZCBiZSBgcG9saWNlW3BvbGljZSRzdWJqZWN0X3JhY2UgPT0gIndoaXRlIixdYC4gWW91IGNhbiB3cml0ZSB5b3VyIGNvZGUgaGVyZTogIAoKYGBge3J9CgpgYGAKCiMjIHNsaWNlCgpVbmxpa2UgYHNlbGVjdCgpYCwgd2UgY2FuJ3QgdXNlIHJvdyBudW1iZXJzIHRvIGluZGV4IHdoaWNoIHJvd3Mgd2Ugd2FudCB3aXRoIGZpbHRlci4gIFRoaXMgZ2l2ZXMgYW4gZXJyb3I6CgpgYGB7cn0KZmlsdGVyKHBvbGljZSwgMTApCmBgYAoKSWYgd2UgZGlkIG5lZWQgdG8gdXNlIHRoZSByb3cgaW5kZXggKHJvdyBudW1iZXIpIHRvIHNlbGVjdCB3aGljaCByb3dzIHdlIHdhbnQsIHdlIGNhbiB1c2UgdGhlIGBzbGljZSgpYCBmdW5jdGlvbi4gIAoKYGBge3J9CnNsaWNlKHBvbGljZSwgMTApCmBgYAoKYGBge3J9CnNsaWNlKHBvbGljZSwgMTA6MTUpCmBgYAoKV2UgZG9uJ3QgdXN1YWxseSB1c2UgYHNsaWNlKClgIGluIHRoaXMgd2F5IHdoZW4gd29ya2luZyB3aXRoIGRwbHlyLiAgVGhpcyBpcyBiZWNhdXNlIHdlIGlkZWFsbHkgd2FudCB0byBiZSB3b3JraW5nIHdpdGggd2VsbC1zdHJ1Y3R1cmVkIGRhdGEsIHdoZXJlIHdlIGNhbiByZW9yZGVyIHRoZSByb3dzIHdpdGhvdXQgbG9zaW5nIGluZm9ybWF0aW9uLiAgSWYgcmVvcmRlcmluZyB0aGUgcm93cyBpbiB0aGUgZGF0YXNldCB3b3VsZCByZXN1bHQgaW4gYSBsb3NzIG9mIGluZm9ybWF0aW9uIChpdCB3b3VsZCBtZXNzIHVwIHlvdXIgZGF0YSksIHRoZW4gdGhlIGRhdGFzZXQgaXMgbWlzc2luZyBhbiBpbXBvcnRhbnQgdmFyaWFibGUgLS0gbWF5YmUganVzdCBhIHNlcXVlbmNlIGluZGV4LiAgWW91IHNob3VsZCBhbHdheXMgYmUgYWJsZSB0byB1c2UgYSB2YXJpYWJsZSB0byBvcmRlciB0aGUgZGF0YSBpZiBuZWVkZWQuCgojIyBQaXBlOiBDaGFpbmluZyBDb21tYW5kcyBUb2dldGhlcgoKU28sIHdlIGNhbiBjaG9vc2Ugcm93cyBhbmQgY2hvb3NlIGNvbHVtbnMgc2VwYXJhdGVseTsgaG93IGRvIHdlIGNvbWJpbmUgdGhlc2Ugb3BlcmF0aW9ucz8gIGBkcGx5cmAsIGFuZCBvdGhlciB0aWR5dmVyc2UsIGNvbW1hbmRzIGNhbiBiZSBzdHJ1bmcgdG9nZXRoZXIgaXMgYSBzZXJpZXMgd2l0aCBhIGAlPiVgIChzYXkvcmVhZDogcGlwZSkgb3BlcmF0b3IuIFRoZSBwaXBlIHRha2VzIHRoZSBvdXRwdXQgb2YgdGhlIGNvbW1hbmQgb24gdGhlIGxlZnQgYW5kIG1ha2VzIHRoYXQgdGhlIGZpcnN0IGlucHV0IHRvIHRoZSBjb21tYW5kIG9uIHRoZSByaWdodC4gKElmIHlvdSBhcmUgZmFtaWxpYXIgd2l0aCB3b3JraW5nIGluIGEgdGVybWluYWwvYXQgdGhlIGNvbW1hbmQgbGluZSwgaXQgd29ya3MgbGlrZSBhIGJhc2ggcGlwZSBjaGFyYWN0ZXIgYHxgLikKClRoaXMgd29ya3MgYmVjYXVzZSB0aGUgZnVuY3Rpb25zIGFsbCB0YWtlIGEgZGF0YSBmcmFtZSBhcyB0aGUgZmlyc3QgaW5wdXQsIGFuZCB0aGV5IHJldHVybiBhIGRhdGEgZnJhbWUgYXMgdGhlIG91dHB1dC4gIAoKV2UgY2FuIHJld3JpdGUgCgpgYGB7cn0Kc2VsZWN0KHBvbGljZSwgZGF0ZSwgdGltZSkKYGBgCgphcwoKYGBge3J9CnBvbGljZSAlPiUgc2VsZWN0KGRhdGUsIHRpbWUpCmBgYAoKYW5kIHlvdSdsbCBvZnRlbiBzZWUgY29kZSBmb3JtYXR0ZWQsIHNvIGAlPiVgIGlzIGF0IHRoZSBlbmQgb2YgZWFjaCBsaW5lLCBhbmQgdGhlIGZvbGxvd2luZyBsaW5lIHRoYXQgYXJlIHN0aWxsIHBhcnQgb2YgdGhlIHNhbWUgZXhwcmVzc2lvbiBhcmUgaW5kZW50ZWQ6CgpgYGB7cn0KcG9saWNlICU+JQogIHNlbGVjdChkYXRlLCB0aW1lKQpgYGAKClRoZSBwaXBlIGNvbWVzIGZyb20gYSBwYWNrYWdlIGNhbGxlZCBgbWFncml0dHJgLCB3aGljaCBoYXMgYWRkaXRpb25hbCBzcGVjaWFsIG9wZXJhdG9ycyBpbiBpdCB0aGF0IHlvdSBjYW4gdXNlLiAgVGhlIGtleWJvYXJkIHNob3J0Y3V0IGZvciBgJT4lYCBpcyBjb21tYW5kLXNoaWZ0LU0gKE1hYykgb3IgY29udHJvbC1zaGlmdC1NIChXaW5kb3dzKS4KCldlIGNhbiB1c2UgdGhlIHBpcGUgdG8gc3RyaW5nIHRvZ2V0aGVyIG11bHRpcGxlIGNvbW1hbmRzIG9wZXJhdGluZyBvbiB0aGUgc2FtZSBkYXRhIGZyYW1lOgoKYGBge3J9CnBvbGljZSAlPiUKICBzZWxlY3Qoc3ViamVjdF9yYWNlLCBzdWJqZWN0X3NleCkgJT4lCiAgZmlsdGVyKHN1YmplY3RfcmFjZSA9PSAid2hpdGUiKQpgYGAKCldlIHdvdWxkIHJlYWQgdGhlIGAlPiVgIGluIHRoZSBjb21tYW5kIGFib3ZlIGFzICJ0aGVuIiBpZiByZWFkaW5nIHRoZSBjb2RlIG91dGxvdWQ6IGZyb20gcG9saWNlLCBzZWxlY3Qgc3ViamVjdF9yYWNlIGFuZCBzdWJqZWN0X3NleCwgdGhlbiBmaWx0ZXIgd2hlcmUgc3ViamVjdF9yYWNlIGlzIHdoaXRlLgoKVGhpcyB3b3JrcyBiZWNhdXNlIHRoZSBkcGx5ciBmdW5jdGlvbnMgdGFrZSBhIHRpYmJsZS9kYXRhIGZyYW1lIGFzIHRoZSBmaXJzdCBhcmd1bWVudCAoaW5wdXQpIGFuZCByZXR1cm4gYSB0aWJibGUvZGF0YSBmcmFtZSBhcyB0aGUgb3V0cHV0LiAgVGhpcyBtYWtlcyBpdCBlYXN5IHRvIHBhc3MgYSBkYXRhIGZyYW1lIHRocm91Z2ggbXVsdGlwbGUgb3BlcmF0aW9ucywgY2hhbmdpbmcgaXQgb25lIHN0ZXAgYXQgYSB0aW1lLiAgCgpPcmRlciBkb2VzIG1hdHRlciwgYXMgdGhlIGNvbW1hbmRzIGFyZSBleGVjdXRlZCBpbiBvcmRlci4gIFNvIHRoaXMgd291bGQgZ2l2ZSB1cyBhbiBlcnJvcjoKCmBgYHtyfQpwb2xpY2UgJT4lCiAgc2VsZWN0KHN1YmplY3Rfc2V4LCBvdXRjb21lKSAlPiUKICBmaWx0ZXIoc3ViamVjdF9yYWNlID09ICJ3aGl0ZSIpCmBgYAoKQmVjYXVzZSBgc3ViamVjdF9yYWNlYCBpcyBubyBsb25nZXIgaW4gdGhlIGRhdGEgZnJhbWUgb25jZSB3ZSB0cnkgdG8gZmlsdGVyIHdpdGggaXQuICBXZSdkIGhhdmUgdG8gcmV2ZXJzZSB0aGUgb3JkZXI6CgpgYGB7cn0KcG9saWNlICU+JQogIGZpbHRlcihzdWJqZWN0X3JhY2UgPT0gIndoaXRlIikgJT4lCiAgc2VsZWN0KHN1YmplY3Rfc2V4LCBvdXRjb21lKQpgYGAKCllvdSBjYW4gdXNlIHRoZSBwaXBlIG9wZXJhdG9yIHRvIHN0cmluZyB0b2dldGhlciBjb21tYW5kcyBvdXRzaWRlIG9mIHRoZSB0aWR5dmVyc2UgYXMgd2VsbCwgYW5kIGl0IHdvcmtzIHdpdGggYW55IGlucHV0IGFuZCBvdXRwdXQsIG5vdCBqdXN0IGRhdGEgZnJhbWVzOgoKYGBge3J9CiMgc3VtKGlzLm5hKHBvbGljZSRiZWF0KSkKaXMubmEocG9saWNlJGJlYXQpICU+JSBzdW0oKQpgYGAKCgojIyMgRVhFUkNJU0UgNAoKU2VsZWN0IHRoZSBkYXRlLCB0aW1lLCBhbmQgb3V0Y29tZSAoY29sdW1ucykgb2Ygc3RvcHMgdGhhdCBvY2N1ciBpbiBiZWF0ICI3MSIgKHJvd3MpLiAgTWFrZSB1c2Ugb2YgdGhlIGAlPiVgIG9wZXJhdG9yLiAgCgpUaGUgZXF1aXZhbGVudCBiYXNlIFIgZXhwcmVzc2lvbiB3b3VsZCBiZTogYHBvbGljZVtwb2xpY2UkYmVhdCA9PSAiNzEiLCBjKCJkYXRlIiwgInRpbWUiLCAib3V0Y29tZSIpXWAKCkhpbnQ6IHJlbWVtYmVyIHRoYXQgYSBjb2x1bW4gbmVlZHMgdG8gc3RpbGwgYmUgaW4gdGhlIGRhdGEgZnJhbWUgaWYgeW91J3JlIGdvaW5nIHRvIHVzZSB0aGUgY29sdW1uIHRvIGZpbHRlci4KCmBgYHtyfQoKYGBgCgoKTm90ZSB0aGF0IHNvIGZhciwgd2UgaGF2ZW4ndCBhY3R1YWxseSBjaGFuZ2VkIHRoZSBgcG9saWNlYCBkYXRhIGZyYW1lIGF0IGFsbC4gIFdlJ3ZlIHdyaXR0ZW4gZXhwcmVzc2lvbnMgdG8gZ2l2ZSB1cyBvdXRwdXQsIGJ1dCB3ZSBoYXZlbid0IHNhdmVkIGl0LiAgCgpTb21ldGltZXMgd2UgbWF5IHN0aWxsIHdhbnQgdG8gc2F2ZSB0aGUgcmVzdWx0IG9mIHNvbWUgZXhwcmVzc2lvbiwgc3VjaCBhcyBhZnRlciBwZXJmb3JtaW5nIGEgYnVuY2ggb2YgZGF0YSBjbGVhbmluZyBzdGVwcy4gV2UgY2FuIGFzc2lnbiB0aGUgb3V0cHV0IG9mIHBpcGVkIGNvbW1hbmRzIGFzIHdlIHdvdWxkIHdpdGggYW55IG90aGVyIGV4cHJlc3Npb24uCgpgYGB7cn0KcG9saWNlNjAyMDEgPC0gcG9saWNlICU+JQogIGZpbHRlcihsb2NhdGlvbiA9PSAiNjAyMDEiKSAlPiUKICBzZWxlY3QoZGF0ZSwgdGltZSwgYmVhdCwgdHlwZSwgb3V0Y29tZSkgCmBgYAoKCgojIyMgRVhFUkNJU0UgNQoKU2VsZWN0IG9ubHkgdmVoaWNsZV95ZWFyIGFuZCB2ZWhpY2xlX21ha2UgY29sdW1ucyBmb3Igb2JzZXJ2YXRpb25zIHdoZXJlIHRoZXJlIHdlcmUgY29udHJhYmFuZF93ZWFwb25zLiBZb3UgY2FuIHdyaXRlIHlvdXIgY29kZSBoZXJlOgoKYGBge3J9CgpgYGAKCiMgUmVjYXAKCldlIGxlYXJuZWQgd2hhdCB0aWJibGVzIGFyZSwgdGhlIGRwbHlyIGVxdWl2YWxlbnRzIG9mIGluZGV4aW5nIGFuZCBzdWJzZXR0aW5nIGEgZGF0YSBmcmFtZSwgYW5kIHRoZSBwaXBlIGAlPiVgIG9wZXJhdG9yLgoKTmV4dCB0aW1lIHdlJ3JlIGdvaW5nIHRvIGxvb2sgYXQgc29tZSBtb3JlIGNvbXBsaWNhdGVkIHVzZSBjYXNlcyBmb3IgYHNlbGVjdGAsIGBmaWx0ZXJgLCBhbmQgYHNsaWNlYCwgYXMgd2VsbCBhcyBsZWFybiBgbXV0YXRlYCB0byBjcmVhdGUgb3IgY2hhbmdlIHZhcmlhYmxlcyBpbiBvdXIgZGF0YXNldHMuICAKCiMgQW5zd2VycyB0byB0aGUgZXhlcmNpc2VzCgojIyMgRXhlcmNpc2UgMQoKYGBge3J9CmZpeF9kYXRhIDwtIHJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vbnVpdHJjcy9yLXRpZHl2ZXJzZS9tYWluL2RhdGEvbWlzc2luZy5jc3YiLCBuYSA9IGMoIiIsICJOL0EiKSkKZml4X2RhdGEKYGBgCgojIyMgRXhlcmNpc2UgMgoKYGBge3J9CnNlbGVjdChwb2xpY2UsIHZpb2xhdGlvbiwgY2l0YXRpb25faXNzdWVkLCB3YXJuaW5nX2lzc3VlZCkKYGBgCgojIyMgRXhlcmNpc2UgMwoKYGBge3J9CmZpbHRlcihwb2xpY2UsIHN1YmplY3RfcmFjZSA9PSAid2hpdGUiKQpgYGAKCiMjIyBFeGVyY2lzZSA0CgpgYGB7cn0KcG9saWNlICU+JSAKICBmaWx0ZXIoYmVhdCA9PSAiNzEiKSAlPiUgCiAgc2VsZWN0KGRhdGUsIHRpbWUsIG91dGNvbWUpCmBgYAoKIyMjIEV4ZXJjaXNlIDUKCmBgYHtyfQpwb2xpY2UgJT4lIAogIGZpbHRlcihjb250cmFiYW5kX3dlYXBvbnMgPT0gVFJVRSkgJT4lIAogIHNlbGVjdCh2ZWhpY2xlX3llYXIsIHZlaGljbGVfbWFrZSkKYGBgCgpZb3UgY2FuIGFsc28gd3JpdGU6CgpgYGB7cn0KcG9saWNlICU+JSAKICBmaWx0ZXIoY29udHJhYmFuZF93ZWFwb25zKSAlPiUgCiAgc2VsZWN0KHZlaGljbGVfeWVhciwgdmVoaWNsZV9tYWtlKQpgYGAK