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:
- fix some of the annoying parts of using R, such as changing default
options when importing data files and preventing large data frames from
printing to the console
- are focused on working with data frames (and their columns), rather
than individual vectors
- usually take a data frame as the first input to a function, and
return a data frame as the output of a function, so that function calls
can be more easily strung together in a sequence
- share some common naming conventions for functions and arguments
that have a goal of making code more readable
- tend to be verbose, opinionated, and are actively working to provide
more useful error messages
Tidyverse packages are particularly useful for:
- data exploration
- reshaping data sets
- computing summary measures over groups
- cleaning up different types of data
- reading and writing data
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