library(tidyverse)
library(openintro)
library(tibble)
library(readr)
library(Lahman)
library(babynames)
library(nasaweather)
library(fueleconomy)
library(nycflights13)
10.5 Practice Problem 2
This exercise highlights key differences between base R
data.frame behavior and tibbles when extracting
columns.
df$x
In a base R data.frame, the $ operator allows partial
matching. Even though there is no column named x, R matches x to xyz and
returns: 1 “a” In contrast, a tibble does not
allow partial matching. Attempting to access a non-existent column like
tbl$x would not match xyz, making the behavior stricter and safer.
df[, "xyz"]
In a base R data.frame, selecting a single column with
[, ] returns a vector: 1 “a”
In a tibble, this same operation returns a one-column tibble, not a
vector. Tibbles preserve structure rather than simplifying output.
df[, c("abc", "xyz")]
When selecting multiple columns, a base R data.frame
returns a data.frame, and a tibble returns a tibble. This
behavior is consistent across both structures.
The default behavior of R data.frame can lead to
confusion and bugs:
- Partial matching (e.g., df$x returning xyz) can produce unexpected
results.
- Selecting one column returns a vector, but selecting multiple
returns a data frame.
- The inconsistency in output types makes code harder to debug and
predict.
Tibbles improve usability by enforcing stricter rules and returning
consistent data types, which makes analysis workflows more reliable.
df <- data.frame(abc = 1, xyz = "a")
df$x
## [1] "a"
## [1] "a"
## abc xyz
## 1 1 a
11.2.2 Question 1
To read a file where fields are separated by the pipe character (|),
I would use the read_delim() function from the readr package and specify
the delimiter using delim = “|”. This function is designed for reading
files with non-standard separators and provides flexibility compared to
functions like read_csv().
data <- read_delim("file.txt", delim = "|")
11.3.5 Question 7
The correct format strings are:
- d1: “%B %d, %Y”
- d2: “%Y-%b-%d”
- d3: “%d-%b-%Y”
- d4: “%B %d (%Y)”
- d5: “%m/%d/%y”
- t1: “%H%M”
- t2: “%I:%M:%OS %p”
These strings work because: - %B = full month name (e.g., January,
August) - %b = abbreviated month name (e.g., Mar, Jun) - %d = day of the
month - %Y = four-digit year - %y = two-digit year - %m = two-digit
month number - %H = hour in 24-hour time - %M = minutes - %OS = seconds
with fractional seconds - %I = hour in 12-hour time - %p = AM/PM
marker
This formatting allows R to correctly interpret each date and time
value.
d1 <- "January 1, 2010"
d2 <- "2015-Mar-07"
d3 <- "06-Jun-2017"
d4 <- c("August 19 (2015)", "July 1 (2015)")
d5 <- "12/30/14" # Dec 30, 2014
t1 <- "1705"
t2 <- "11:15:10.12 PM"
12.2.1 Exercises
1
The sample tables organize variables and observations in different
ways:
table1 is a tidy dataset. Each variable
(country, year, cases, population) has its own column, and each
observation (a country-year combination) has its own row.
table2 is not tidy. The cases and population
values are stored in a single column called count, and
another column (type) indicates whether the value
represents cases or population. This means one variable is spread across
rows instead of having separate columns.
table3 is not tidy because cases and population
are combined into a single column as a ratio (e.g., “745/19987071”),
instead of being separated into two variables.
table4a contains only case counts, with each
year as a separate column. This spreads a variable (year) across
multiple columns.
table4b contains population data, also with each
year as a separate column. Like table4a, it is not tidy
because a variable is stored in the column names.
2
Table2
table2_rates <- table2 %>%
pivot_wider(names_from = type, values_from = count) %>%
mutate(rate = (cases / population) * 10000)
table2_rates
## # A tibble: 6 × 5
## country year cases population rate
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 Afghanistan 1999 745 19987071 0.373
## 2 Afghanistan 2000 2666 20595360 1.29
## 3 Brazil 1999 37737 172006362 2.19
## 4 Brazil 2000 80488 174504898 4.61
## 5 China 1999 212258 1272915272 1.67
## 6 China 2000 213766 1280428583 1.67
To compute the rate for table2, I first reshaped the data using
pivot_wider() so that cases and population became separate columns. Then
I divided cases by population and multiplied by 10,000 to create a new
variable called rate.
Table4a and Table4b
table4a_long <- table4a %>%
pivot_longer(cols = c(`1999`, `2000`),
names_to = "year",
values_to = "cases")
table4b_long <- table4b %>%
pivot_longer(cols = c(`1999`, `2000`),
names_to = "year",
values_to = "population")
table4_rates <- table4a_long %>%
left_join(table4b_long, by = c("country", "year")) %>%
mutate(rate = (cases / population) * 10000)
table4_rates
## # A tibble: 6 × 5
## country year cases population rate
## <chr> <chr> <dbl> <dbl> <dbl>
## 1 Afghanistan 1999 745 19987071 0.373
## 2 Afghanistan 2000 2666 20595360 1.29
## 3 Brazil 1999 37737 172006362 2.19
## 4 Brazil 2000 80488 174504898 4.61
## 5 China 1999 212258 1272915272 1.67
## 6 China 2000 213766 1280428583 1.67
To compute the rate for table4a and table4b, I first converted both
tables from wide format to long format using pivot_longer(). This moved
the year values into a single year column. I then joined the two tables
by country and year so that each row contained both the number of cases
and the population for the same country and year. Finally, I calculated
the rate by dividing cases by population and multiplying by 10,000.
3
The easiest representation to work with is table1 because it is
already tidy. Each variable has its own column, each observation has its
own row, and calculations can be done directly without reshaping the
data. The hardest representation to work with is table3 because cases
and population are combined into a single column. Before any analysis
can be done, the combined values would need to be split into separate
variables. This adds extra steps and makes the data less convenient to
use. table2, table4a, and table4b are somewhat harder to use than table1
because they require reshaping before analysis, but they are still
easier to work with than table3.
4
To recreate the plot showing change in cases over time using
table2, I first needed to reshape the data. In
table2, the values for cases and population are stored
together in a single column called count, while another
column called type identifies whether the value is for
cases or population. Because cases is not already its own
variable, I had to use pivot_wider() to convert the data
into a tidy format with separate cases and
population columns. After that, I used
ggplot() to plot the number of cases over time by
country.
table2_tidy <- table2 %>%
pivot_wider(names_from = type, values_from = count)
ggplot(table2_tidy, aes(x = year, y = cases, color = country, group = country)) +
geom_line() +
geom_point()

12.3.3 Question 1
pivot_longer() and pivot_wider() are not
perfectly symmetrical because information can be lost or changed during
the transformation process, particularly when column names are created
or modified.
In the example, when pivot_wider() is applied, the
values from the year column become column
names (e.g., "2015" and "2016").
These column names are stored as character strings,
even though the original year variable was numeric.
When the data is then transformed back using
pivot_longer(), the year values are
reconstructed from the column names, but they remain character
type, not numeric. As a result, the dataset is not identical to
the original because the variable type has changed.
Additionally, when reshaping data, there is a risk of losing
information if the data structure is not uniquely identified (e.g., if
there are duplicate combinations of keys). This also contributes to the
lack of perfect symmetry.
stocks <- tibble(
year = c(2015, 2015, 2016, 2016),
half = c( 1, 2, 1, 2),
return = c(1.88, 0.59, 0.92, 0.17)
)
stocks %>%
pivot_wider(names_from = year, values_from = return) %>%
pivot_longer(`2015`:`2016`, names_to = "year", values_to = "return")
## # A tibble: 4 × 3
## half year return
## <dbl> <chr> <dbl>
## 1 1 2015 1.88
## 2 1 2016 0.92
## 3 2 2015 0.59
## 4 2 2016 0.17
The names_ptypes argument in pivot_longer()
is used to enforce the data type of the new column created from column
names. However, it requires that the values can already be interpreted
as that type. If they cannot be safely converted, it will produce an
error.
In contrast, names_transform can be used to explicitly
convert the column to a new type (for example, numeric) after it is
created. This is often more flexible when working with column names like
years that are initially stored as character values.
stocks %>%
pivot_wider(names_from = year, values_from = return) %>%
pivot_longer(`2015`:`2016`,
names_to = "year",
values_to = "return",
names_transform = list(year = as.numeric))
12.5.1 Exercises
1
The fill argument in pivot_wider() and
complete() serves a similar purpose but is used in slightly
different contexts.
In pivot_wider(), the values_fill argument
is used to replace missing values that arise when reshaping data from
long format to wide format. If a combination of variables does not exist
in the original data, pivot_wider() will create an
NA value, and values_fill allows you to
specify what value should be used instead (such as 0).
In contrast, complete() is used to generate missing
combinations of variables in a dataset. The fill argument
in complete() is then used to assign values to those newly
created rows. This means that complete() actively adds
missing rows to the dataset, whereas pivot_wider() only
fills in missing cells created during reshaping.
In summary: - pivot_wider() uses
values_fill to replace missing values in an existing
structure after transformation. - complete() uses
fill to assign values to newly created rows that represent
missing combinations of variables.
2
The direction argument in the fill()
function controls how missing values (NAs) are filled
within a column.
"down" fills missing values using the last observed
non-missing value moving downward.
"up" fills missing values using the next observed
non-missing value moving upward.
"downup" first fills downward, then upward.
"updown" first fills upward, then downward.
This argument allows you to control the direction in which values are
carried forward or backward, which is especially useful when working
with grouped or sequential data.
12.6.1 Question 1
In this case study, using values_drop_na = TRUE is
reasonable for simplifying the dataset and making it easier to check
whether the values have been correctly transformed. By removing rows
with missing values, it becomes easier to focus only on the observed
data and verify accuracy.
However, whether this is appropriate depends on how missing values
are represented in the dataset. In this case, there are implicit
missing values, meaning that some combinations of variables are
absent from the data rather than explicitly recorded as NA.
When values_drop_na = TRUE is used, these implicit missing
values are effectively ignored, which may lead to loss of potentially
meaningful information.
An important distinction is between missing values
(NA) and zeros:
- An
NA represents a value that is unknown, not recorded,
or not applicable. It indicates missing data.
- A value of zero represents a known quantity, meaning that the
measured value truly is zero.
This distinction matters because removing NA values does
not imply that the value is zero. Treating missing values as zero could
lead to incorrect conclusions, especially in analyses where absence of
data is not the same as absence of events.
13.2.1 Practice Problem
1
To draw the route each plane flies from its origin to its
destination, I would need variables that describe both the departure and
arrival locations. Specifically, I would need:
- Origin airport code (
origin)
- Destination airport code (
dest)
- Latitude and longitude for each airport
To obtain this information, I would need to combine:
- The
flights table (for origin and destination airport
codes)
- The
airports table (for latitude and longitude)
I would join the flights data to the
airports table twice: - Once to get the coordinates of the
origin airport - Once to get the coordinates of the destination
airport
This would allow me to map the flight paths between airports.
2
The relationship between weather and
airports is based on the airport location. Specifically,
each weather observation corresponds to a particular airport, identified
by the origin code.
This is a many-to-one relationship: - Many rows in
the weather table correspond to one airport in the
airports table
In a relational diagram, this relationship should be shown as a link
between weather.origin and airports.faa,
indicating that multiple weather records are associated with each
airport.
3
The weather table currently only contains information
for origin (NYC) airports. If it instead contained weather data for all
airports in the United States, it would define an additional
relationship with the flights table.
In this case, there would be two relationships
between weather and flights: - One linking
weather to the origin airport - One linking weather to
the destination airport
Both relationships would likely also involve time variables (such as
date and hour), since weather conditions change over time. This would
create a more complex relationship where each flight could be associated
with weather conditions at both departure and arrival locations.
4
To represent “special” days when fewer people fly, I would create a
new data frame with a variable indicating whether a day is special.
For example, the table might include: - year -
month - day - special_day (e.g.,
TRUE/FALSE or a category such as holiday or event)
The primary key for this table would be the
combination of year, month, and
day, since each date should be unique.
This table would connect to the flights table through
these same variables (year, month, and
day). This creates a one-to-many
relationship, where each day in the new table corresponds to
many flights in the flights table.
This structure would allow analysis of how flight patterns differ on
special days compared to regular days.
13.3.1 Question 2
The keys in each dataset are:
Lahman::Batting:
playerID, yearID, and stint
The documentation describes playerID as the player
identifier, yearID as the year, and stint as
the player’s stint within a season, which together identify each batting
record.
babynames::babynames:
year, sex, and name
The dataset contains one row for each baby name by year and sex, with
n and prop describing that combination, so the
identifying variables are year, sex, and
name.
nasaweather::atmos:
lat, long, year, and
month
The documentation says the dataset records measurements by location
(lat, long) and time (year,
month), so those four variables together identify each
observation.
fueleconomy::vehicles:
id
The dataset documentation explicitly states that id is the
unique EPA identifier, so this is the key.
ggplot2::diamonds: no obvious
natural key
The documentation lists 10 descriptive variables (such as
carat, cut, color,
clarity, price, and dimensions), but it does
not include any variable described as a unique identifier. In practice,
this means diamonds does not have a simple natural key, and
if a unique key were needed, an artificial row ID would likely need to
be added.
## [1] "playerID" "yearID" "stint" "teamID" "lgID" "G"
## [7] "AB" "R" "H" "X2B" "X3B" "HR"
## [13] "RBI" "SB" "CS" "BB" "SO" "IBB"
## [19] "HBP" "SH" "SF" "GIDP"
13.4.6 Question 1
To compute the average delay by destination and visualize it
spatially, I first summarized the average arrival delay for each
destination airport. Then I joined this summary to the
airports dataset to obtain latitude and longitude for
mapping.
# Step 1: Compute average delay by destination
avg_delay <- flights %>%
group_by(dest) %>%
summarise(avg_delay = mean(arr_delay, na.rm = TRUE))
# Step 2: Join with airports data
delay_map <- airports %>%
semi_join(flights, c("faa" = "dest")) %>%
left_join(avg_delay, by = c("faa" = "dest"))
# Step 3: Plot the map
ggplot(delay_map, aes(x = lon, y = lat)) +
borders("state") +
geom_point(aes(color = avg_delay, size = avg_delay), alpha = 0.7) +
coord_quickmap() +
labs(
title = "Average Arrival Delay by Destination Airport",
x = "Longitude",
y = "Latitude",
color = "Avg Delay",
size = "Avg Delay"
)
## Warning: `borders()` was deprecated in ggplot2 4.0.0.
## ℹ Please use `annotation_borders()` instead.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_point()`).

13.5.1 Exercises
1
A flight has a missing tailnum when the aircraft
registration number was not recorded for that flight. This means the
flight cannot be matched to a specific plane in the planes
table.
Tail numbers in flights that do not have a matching
record in planes are likely missing from the FAA aircraft
registry data used in the planes dataset. A major reason
for this is that many unmatched or missing tail numbers are associated
with regional carriers. In the nycflights13 data, one
variable that explains most of the issue is carrier,
because certain airlines account for most of the unmatched plane
records.
To investigate this, I would compare missing and unmatched tail
numbers across carriers.
# Flights with missing tail numbers
flights %>%
filter(is.na(tailnum)) %>%
count(carrier, sort = TRUE)
## # A tibble: 7 × 2
## carrier n
## <chr> <int>
## 1 9E 1044
## 2 UA 686
## 3 US 663
## 4 AA 84
## 5 WN 30
## 6 F9 3
## 7 MQ 2
# Flights with tail numbers that do not match planes
flights %>%
filter(!is.na(tailnum)) %>%
anti_join(planes, by = "tailnum") %>%
count(carrier, sort = TRUE)
## # A tibble: 9 × 2
## carrier n
## <chr> <int>
## 1 MQ 25395
## 2 AA 22474
## 3 UA 1007
## 4 B6 830
## 5 FL 187
## 6 DL 110
## 7 F9 47
## 8 US 36
## 9 WN 8
2
To show only flights operated by planes that flew at least 100 times,
I first counted the number of flights for each tailnum,
filtered to keep only those with at least 100 flights, and then used
semi_join() to retain only matching rows in the
flights dataset.
planes_100 <- flights %>%
count(tailnum) %>%
filter(!is.na(tailnum), n >= 100)
flights_100 <- flights %>%
semi_join(planes_100, by = "tailnum")
flights_100
## # A tibble: 228,390 × 19
## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
## <int> <int> <int> <int> <int> <dbl> <int> <int>
## 1 2013 1 1 517 515 2 830 819
## 2 2013 1 1 533 529 4 850 830
## 3 2013 1 1 544 545 -1 1004 1022
## 4 2013 1 1 554 558 -4 740 728
## 5 2013 1 1 555 600 -5 913 854
## 6 2013 1 1 557 600 -3 709 723
## 7 2013 1 1 557 600 -3 838 846
## 8 2013 1 1 558 600 -2 849 851
## 9 2013 1 1 558 600 -2 853 856
## 10 2013 1 1 558 600 -2 923 937
## # ℹ 228,380 more rows
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
## # tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
## # hour <dbl>, minute <dbl>, time_hour <dttm>
``
LS0tCnRpdGxlOiAiSG9tZXdvcmsgIyA1IgphdXRob3I6ICJDYWl0bGluIEtlbm5lZHkiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OiBvcGVuaW50cm86OmxhYl9yZXBvcnQKLS0tCgpgYGB7ciBsb2FkLXBhY2thZ2VzLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShvcGVuaW50cm8pCmxpYnJhcnkodGliYmxlKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KExhaG1hbikKbGlicmFyeShiYWJ5bmFtZXMpCmxpYnJhcnkobmFzYXdlYXRoZXIpCmxpYnJhcnkoZnVlbGVjb25vbXkpCmxpYnJhcnkobnljZmxpZ2h0czEzKQpgYGAKCiMjIyAxMC41IFByYWN0aWNlIFByb2JsZW0gMgoKVGhpcyBleGVyY2lzZSBoaWdobGlnaHRzIGtleSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGJhc2UgUiBgZGF0YS5mcmFtZWAgYmVoYXZpb3IgYW5kIHRpYmJsZXMgd2hlbiBleHRyYWN0aW5nIGNvbHVtbnMuCgoxLiBgZGYkeGAKCkluIGEgYmFzZSBSIGBkYXRhLmZyYW1lYCwgdGhlICQgb3BlcmF0b3IgYWxsb3dzIHBhcnRpYWwgbWF0Y2hpbmcuCkV2ZW4gdGhvdWdoIHRoZXJlIGlzIG5vIGNvbHVtbiBuYW1lZCB4LCBSIG1hdGNoZXMgeCB0byB4eXogYW5kIHJldHVybnM6ClsxXSAiYSIKSW4gY29udHJhc3QsIGEgdGliYmxlIGRvZXMgbm90IGFsbG93IHBhcnRpYWwgbWF0Y2hpbmcuIEF0dGVtcHRpbmcgdG8gYWNjZXNzIGEgbm9uLWV4aXN0ZW50IGNvbHVtbiBsaWtlIHRibCR4IHdvdWxkIG5vdCBtYXRjaCB4eXosIG1ha2luZyB0aGUgYmVoYXZpb3Igc3RyaWN0ZXIgYW5kIHNhZmVyLgoKMi4gYGRmWywgInh5eiJdYAoKSW4gYSBiYXNlIFIgYGRhdGEuZnJhbWVgLCBzZWxlY3RpbmcgYSBzaW5nbGUgY29sdW1uIHdpdGggWywgXSByZXR1cm5zIGEgdmVjdG9yOgpbMV0gImEiCgpJbiBhIHRpYmJsZSwgdGhpcyBzYW1lIG9wZXJhdGlvbiByZXR1cm5zIGEgb25lLWNvbHVtbiB0aWJibGUsIG5vdCBhIHZlY3Rvci4gVGliYmxlcyBwcmVzZXJ2ZSBzdHJ1Y3R1cmUgcmF0aGVyIHRoYW4gc2ltcGxpZnlpbmcgb3V0cHV0LgoKMy4gYGRmWywgYygiYWJjIiwgInh5eiIpXWAKCldoZW4gc2VsZWN0aW5nIG11bHRpcGxlIGNvbHVtbnMsIGEgYmFzZSBSIGBkYXRhLmZyYW1lYCByZXR1cm5zIGEgYGRhdGEuZnJhbWVgLCBhbmQgYSB0aWJibGUgcmV0dXJucyBhIHRpYmJsZS4gVGhpcyBiZWhhdmlvciBpcyBjb25zaXN0ZW50IGFjcm9zcyBib3RoIHN0cnVjdHVyZXMuCgpUaGUgZGVmYXVsdCBiZWhhdmlvciBvZiBSIGBkYXRhLmZyYW1lYCBjYW4gbGVhZCB0byBjb25mdXNpb24gYW5kIGJ1Z3M6CgotIFBhcnRpYWwgbWF0Y2hpbmcgKGUuZy4sIGRmJHggcmV0dXJuaW5nIHh5eikgY2FuIHByb2R1Y2UgdW5leHBlY3RlZCByZXN1bHRzLgotIFNlbGVjdGluZyBvbmUgY29sdW1uIHJldHVybnMgYSB2ZWN0b3IsIGJ1dCBzZWxlY3RpbmcgbXVsdGlwbGUgcmV0dXJucyBhIGRhdGEgZnJhbWUuCi0gVGhlIGluY29uc2lzdGVuY3kgaW4gb3V0cHV0IHR5cGVzIG1ha2VzIGNvZGUgaGFyZGVyIHRvIGRlYnVnIGFuZCBwcmVkaWN0LgoKVGliYmxlcyBpbXByb3ZlIHVzYWJpbGl0eSBieSBlbmZvcmNpbmcgc3RyaWN0ZXIgcnVsZXMgYW5kIHJldHVybmluZyBjb25zaXN0ZW50IGRhdGEgdHlwZXMsIHdoaWNoIG1ha2VzIGFuYWx5c2lzIHdvcmtmbG93cyBtb3JlIHJlbGlhYmxlLgoKYGBge3J9CmRmIDwtIGRhdGEuZnJhbWUoYWJjID0gMSwgeHl6ID0gImEiKQoKZGYkeApkZlssICJ4eXoiXQpkZlssIGMoImFiYyIsICJ4eXoiKV0KYGBgCgojIyMgMTEuMi4yIFF1ZXN0aW9uIDEKClRvIHJlYWQgYSBmaWxlIHdoZXJlIGZpZWxkcyBhcmUgc2VwYXJhdGVkIGJ5IHRoZSBwaXBlIGNoYXJhY3RlciAofCksIEkgd291bGQgdXNlIHRoZSByZWFkX2RlbGltKCkgZnVuY3Rpb24gZnJvbSB0aGUgcmVhZHIgcGFja2FnZSBhbmQgc3BlY2lmeSB0aGUgZGVsaW1pdGVyIHVzaW5nIGRlbGltID0gInwiLiBUaGlzIGZ1bmN0aW9uIGlzIGRlc2lnbmVkIGZvciByZWFkaW5nIGZpbGVzIHdpdGggbm9uLXN0YW5kYXJkIHNlcGFyYXRvcnMgYW5kIHByb3ZpZGVzIGZsZXhpYmlsaXR5IGNvbXBhcmVkIHRvIGZ1bmN0aW9ucyBsaWtlIHJlYWRfY3N2KCkuCgpgYGB7ciBldmFsPUZBTFNFfQpkYXRhIDwtIHJlYWRfZGVsaW0oImZpbGUudHh0IiwgZGVsaW0gPSAifCIpCmBgYAoKIyMjIDExLjMuNSBRdWVzdGlvbiA3CgpUaGUgY29ycmVjdCBmb3JtYXQgc3RyaW5ncyBhcmU6CgotIGQxOiAiJUIgJWQsICVZIgotIGQyOiAiJVktJWItJWQiCi0gZDM6ICIlZC0lYi0lWSIKLSBkNDogIiVCICVkICglWSkiCi0gZDU6ICIlbS8lZC8leSIKLSB0MTogIiVIJU0iCi0gdDI6ICIlSTolTTolT1MgJXAiCgpUaGVzZSBzdHJpbmdzIHdvcmsgYmVjYXVzZToKLSAlQiA9IGZ1bGwgbW9udGggbmFtZSAoZS5nLiwgSmFudWFyeSwgQXVndXN0KQotICViID0gYWJicmV2aWF0ZWQgbW9udGggbmFtZSAoZS5nLiwgTWFyLCBKdW4pCi0gJWQgPSBkYXkgb2YgdGhlIG1vbnRoCi0gJVkgPSBmb3VyLWRpZ2l0IHllYXIKLSAleSA9IHR3by1kaWdpdCB5ZWFyCi0gJW0gPSB0d28tZGlnaXQgbW9udGggbnVtYmVyCi0gJUggPSBob3VyIGluIDI0LWhvdXIgdGltZQotICVNID0gbWludXRlcwotICVPUyA9IHNlY29uZHMgd2l0aCBmcmFjdGlvbmFsIHNlY29uZHMKLSAlSSA9IGhvdXIgaW4gMTItaG91ciB0aW1lCi0gJXAgPSBBTS9QTSBtYXJrZXIKClRoaXMgZm9ybWF0dGluZyBhbGxvd3MgUiB0byBjb3JyZWN0bHkgaW50ZXJwcmV0IGVhY2ggZGF0ZSBhbmQgdGltZSB2YWx1ZS4KCmBgYHtyIGV2YWw9RkFMU0V9CmQxIDwtICJKYW51YXJ5IDEsIDIwMTAiCmQyIDwtICIyMDE1LU1hci0wNyIKZDMgPC0gIjA2LUp1bi0yMDE3IgpkNCA8LSBjKCJBdWd1c3QgMTkgKDIwMTUpIiwgIkp1bHkgMSAoMjAxNSkiKQpkNSA8LSAiMTIvMzAvMTQiICMgRGVjIDMwLCAyMDE0CnQxIDwtICIxNzA1Igp0MiA8LSAiMTE6MTU6MTAuMTIgUE0iCmBgYAoKIyMjIDEyLjIuMSBFeGVyY2lzZXMKCiMjIyMgMQoKVGhlIHNhbXBsZSB0YWJsZXMgb3JnYW5pemUgdmFyaWFibGVzIGFuZCBvYnNlcnZhdGlvbnMgaW4gZGlmZmVyZW50IHdheXM6CgotICoqdGFibGUxKiogaXMgYSB0aWR5IGRhdGFzZXQuIEVhY2ggdmFyaWFibGUgKGNvdW50cnksIHllYXIsIGNhc2VzLCBwb3B1bGF0aW9uKSBoYXMgaXRzIG93biBjb2x1bW4sIGFuZCBlYWNoIG9ic2VydmF0aW9uIChhIGNvdW50cnkteWVhciBjb21iaW5hdGlvbikgaGFzIGl0cyBvd24gcm93LgoKLSAqKnRhYmxlMioqIGlzIG5vdCB0aWR5LiBUaGUgY2FzZXMgYW5kIHBvcHVsYXRpb24gdmFsdWVzIGFyZSBzdG9yZWQgaW4gYSBzaW5nbGUgY29sdW1uIGNhbGxlZCBgY291bnRgLCBhbmQgYW5vdGhlciBjb2x1bW4gKGB0eXBlYCkgaW5kaWNhdGVzIHdoZXRoZXIgdGhlIHZhbHVlIHJlcHJlc2VudHMgY2FzZXMgb3IgcG9wdWxhdGlvbi4gVGhpcyBtZWFucyBvbmUgdmFyaWFibGUgaXMgc3ByZWFkIGFjcm9zcyByb3dzIGluc3RlYWQgb2YgaGF2aW5nIHNlcGFyYXRlIGNvbHVtbnMuCgotICoqdGFibGUzKiogaXMgbm90IHRpZHkgYmVjYXVzZSBjYXNlcyBhbmQgcG9wdWxhdGlvbiBhcmUgY29tYmluZWQgaW50byBhIHNpbmdsZSBjb2x1bW4gYXMgYSByYXRpbyAoZS5nLiwgIjc0NS8xOTk4NzA3MSIpLCBpbnN0ZWFkIG9mIGJlaW5nIHNlcGFyYXRlZCBpbnRvIHR3byB2YXJpYWJsZXMuCgotICoqdGFibGU0YSoqIGNvbnRhaW5zIG9ubHkgY2FzZSBjb3VudHMsIHdpdGggZWFjaCB5ZWFyIGFzIGEgc2VwYXJhdGUgY29sdW1uLiBUaGlzIHNwcmVhZHMgYSB2YXJpYWJsZSAoeWVhcikgYWNyb3NzIG11bHRpcGxlIGNvbHVtbnMuCgotICoqdGFibGU0YioqIGNvbnRhaW5zIHBvcHVsYXRpb24gZGF0YSwgYWxzbyB3aXRoIGVhY2ggeWVhciBhcyBhIHNlcGFyYXRlIGNvbHVtbi4gTGlrZSBgdGFibGU0YWAsIGl0IGlzIG5vdCB0aWR5IGJlY2F1c2UgYSB2YXJpYWJsZSBpcyBzdG9yZWQgaW4gdGhlIGNvbHVtbiBuYW1lcy4KCiMjIyMgMgoKIyMjIyMgVGFibGUyCgpgYGB7cn0KdGFibGUyX3JhdGVzIDwtIHRhYmxlMiAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gdHlwZSwgdmFsdWVzX2Zyb20gPSBjb3VudCkgJT4lCiAgbXV0YXRlKHJhdGUgPSAoY2FzZXMgLyBwb3B1bGF0aW9uKSAqIDEwMDAwKQoKdGFibGUyX3JhdGVzCgpgYGAKVG8gY29tcHV0ZSB0aGUgcmF0ZSBmb3IgdGFibGUyLCBJIGZpcnN0IHJlc2hhcGVkIHRoZSBkYXRhIHVzaW5nIHBpdm90X3dpZGVyKCkgc28gdGhhdCBjYXNlcyBhbmQgcG9wdWxhdGlvbiBiZWNhbWUgc2VwYXJhdGUgY29sdW1ucy4gVGhlbiBJIGRpdmlkZWQgY2FzZXMgYnkgcG9wdWxhdGlvbiBhbmQgbXVsdGlwbGllZCBieSAxMCwwMDAgdG8gY3JlYXRlIGEgbmV3IHZhcmlhYmxlIGNhbGxlZCByYXRlLgoKIyMjIyMgVGFibGU0YSBhbmQgVGFibGU0YgoKYGBge3J9CnRhYmxlNGFfbG9uZyA8LSB0YWJsZTRhICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gYyhgMTk5OWAsIGAyMDAwYCksCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInllYXIiLAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiY2FzZXMiKQoKdGFibGU0Yl9sb25nIDwtIHRhYmxlNGIgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKGAxOTk5YCwgYDIwMDBgKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAieWVhciIsCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJwb3B1bGF0aW9uIikKCnRhYmxlNF9yYXRlcyA8LSB0YWJsZTRhX2xvbmcgJT4lCiAgbGVmdF9qb2luKHRhYmxlNGJfbG9uZywgYnkgPSBjKCJjb3VudHJ5IiwgInllYXIiKSkgJT4lCiAgbXV0YXRlKHJhdGUgPSAoY2FzZXMgLyBwb3B1bGF0aW9uKSAqIDEwMDAwKQoKdGFibGU0X3JhdGVzCmBgYApUbyBjb21wdXRlIHRoZSByYXRlIGZvciB0YWJsZTRhIGFuZCB0YWJsZTRiLCBJIGZpcnN0IGNvbnZlcnRlZCBib3RoIHRhYmxlcyBmcm9tIHdpZGUgZm9ybWF0IHRvIGxvbmcgZm9ybWF0IHVzaW5nIHBpdm90X2xvbmdlcigpLiBUaGlzIG1vdmVkIHRoZSB5ZWFyIHZhbHVlcyBpbnRvIGEgc2luZ2xlIHllYXIgY29sdW1uLiBJIHRoZW4gam9pbmVkIHRoZSB0d28gdGFibGVzIGJ5IGNvdW50cnkgYW5kIHllYXIgc28gdGhhdCBlYWNoIHJvdyBjb250YWluZWQgYm90aCB0aGUgbnVtYmVyIG9mIGNhc2VzIGFuZCB0aGUgcG9wdWxhdGlvbiBmb3IgdGhlIHNhbWUgY291bnRyeSBhbmQgeWVhci4gRmluYWxseSwgSSBjYWxjdWxhdGVkIHRoZSByYXRlIGJ5IGRpdmlkaW5nIGNhc2VzIGJ5IHBvcHVsYXRpb24gYW5kIG11bHRpcGx5aW5nIGJ5IDEwLDAwMC4KCiMjIyMgMwoKVGhlIGVhc2llc3QgcmVwcmVzZW50YXRpb24gdG8gd29yayB3aXRoIGlzIHRhYmxlMSBiZWNhdXNlIGl0IGlzIGFscmVhZHkgdGlkeS4gRWFjaCB2YXJpYWJsZSBoYXMgaXRzIG93biBjb2x1bW4sIGVhY2ggb2JzZXJ2YXRpb24gaGFzIGl0cyBvd24gcm93LCBhbmQgY2FsY3VsYXRpb25zIGNhbiBiZSBkb25lIGRpcmVjdGx5IHdpdGhvdXQgcmVzaGFwaW5nIHRoZSBkYXRhLgpUaGUgaGFyZGVzdCByZXByZXNlbnRhdGlvbiB0byB3b3JrIHdpdGggaXMgdGFibGUzIGJlY2F1c2UgY2FzZXMgYW5kIHBvcHVsYXRpb24gYXJlIGNvbWJpbmVkIGludG8gYSBzaW5nbGUgY29sdW1uLiBCZWZvcmUgYW55IGFuYWx5c2lzIGNhbiBiZSBkb25lLCB0aGUgY29tYmluZWQgdmFsdWVzIHdvdWxkIG5lZWQgdG8gYmUgc3BsaXQgaW50byBzZXBhcmF0ZSB2YXJpYWJsZXMuIFRoaXMgYWRkcyBleHRyYSBzdGVwcyBhbmQgbWFrZXMgdGhlIGRhdGEgbGVzcyBjb252ZW5pZW50IHRvIHVzZS4KdGFibGUyLCB0YWJsZTRhLCBhbmQgdGFibGU0YiBhcmUgc29tZXdoYXQgaGFyZGVyIHRvIHVzZSB0aGFuIHRhYmxlMSBiZWNhdXNlIHRoZXkgcmVxdWlyZSByZXNoYXBpbmcgYmVmb3JlIGFuYWx5c2lzLCBidXQgdGhleSBhcmUgc3RpbGwgZWFzaWVyIHRvIHdvcmsgd2l0aCB0aGFuIHRhYmxlMy4KCiMjIyMgNAoKVG8gcmVjcmVhdGUgdGhlIHBsb3Qgc2hvd2luZyBjaGFuZ2UgaW4gY2FzZXMgb3ZlciB0aW1lIHVzaW5nIGB0YWJsZTJgLCBJIGZpcnN0IG5lZWRlZCB0byByZXNoYXBlIHRoZSBkYXRhLiBJbiBgdGFibGUyYCwgdGhlIHZhbHVlcyBmb3IgY2FzZXMgYW5kIHBvcHVsYXRpb24gYXJlIHN0b3JlZCB0b2dldGhlciBpbiBhIHNpbmdsZSBjb2x1bW4gY2FsbGVkIGBjb3VudGAsIHdoaWxlIGFub3RoZXIgY29sdW1uIGNhbGxlZCBgdHlwZWAgaWRlbnRpZmllcyB3aGV0aGVyIHRoZSB2YWx1ZSBpcyBmb3IgY2FzZXMgb3IgcG9wdWxhdGlvbi4gQmVjYXVzZSBgY2FzZXNgIGlzIG5vdCBhbHJlYWR5IGl0cyBvd24gdmFyaWFibGUsIEkgaGFkIHRvIHVzZSBgcGl2b3Rfd2lkZXIoKWAgdG8gY29udmVydCB0aGUgZGF0YSBpbnRvIGEgdGlkeSBmb3JtYXQgd2l0aCBzZXBhcmF0ZSBgY2FzZXNgIGFuZCBgcG9wdWxhdGlvbmAgY29sdW1ucy4gQWZ0ZXIgdGhhdCwgSSB1c2VkIGBnZ3Bsb3QoKWAgdG8gcGxvdCB0aGUgbnVtYmVyIG9mIGNhc2VzIG92ZXIgdGltZSBieSBjb3VudHJ5LgoKYGBge3J9CnRhYmxlMl90aWR5IDwtIHRhYmxlMiAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gdHlwZSwgdmFsdWVzX2Zyb20gPSBjb3VudCkKCmdncGxvdCh0YWJsZTJfdGlkeSwgYWVzKHggPSB5ZWFyLCB5ID0gY2FzZXMsIGNvbG9yID0gY291bnRyeSwgZ3JvdXAgPSBjb3VudHJ5KSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkKYGBgCgojIyMgMTIuMy4zIFF1ZXN0aW9uIDEKCmBwaXZvdF9sb25nZXIoKWAgYW5kIGBwaXZvdF93aWRlcigpYCBhcmUgbm90IHBlcmZlY3RseSBzeW1tZXRyaWNhbCBiZWNhdXNlIGluZm9ybWF0aW9uIGNhbiBiZSBsb3N0IG9yIGNoYW5nZWQgZHVyaW5nIHRoZSB0cmFuc2Zvcm1hdGlvbiBwcm9jZXNzLCBwYXJ0aWN1bGFybHkgd2hlbiBjb2x1bW4gbmFtZXMgYXJlIGNyZWF0ZWQgb3IgbW9kaWZpZWQuCgpJbiB0aGUgZXhhbXBsZSwgd2hlbiBgcGl2b3Rfd2lkZXIoKWAgaXMgYXBwbGllZCwgdGhlIHZhbHVlcyBmcm9tIHRoZSBgeWVhcmAgY29sdW1uIGJlY29tZSAqKmNvbHVtbiBuYW1lcyoqIChlLmcuLCBgIjIwMTUiYCBhbmQgYCIyMDE2ImApLiBUaGVzZSBjb2x1bW4gbmFtZXMgYXJlIHN0b3JlZCBhcyAqKmNoYXJhY3RlciBzdHJpbmdzKiosIGV2ZW4gdGhvdWdoIHRoZSBvcmlnaW5hbCBgeWVhcmAgdmFyaWFibGUgd2FzIG51bWVyaWMuCgpXaGVuIHRoZSBkYXRhIGlzIHRoZW4gdHJhbnNmb3JtZWQgYmFjayB1c2luZyBgcGl2b3RfbG9uZ2VyKClgLCB0aGUgYHllYXJgIHZhbHVlcyBhcmUgcmVjb25zdHJ1Y3RlZCBmcm9tIHRoZSBjb2x1bW4gbmFtZXMsIGJ1dCB0aGV5IHJlbWFpbiAqKmNoYXJhY3RlciB0eXBlKiosIG5vdCBudW1lcmljLiBBcyBhIHJlc3VsdCwgdGhlIGRhdGFzZXQgaXMgbm90IGlkZW50aWNhbCB0byB0aGUgb3JpZ2luYWwgYmVjYXVzZSB0aGUgdmFyaWFibGUgdHlwZSBoYXMgY2hhbmdlZC4KCkFkZGl0aW9uYWxseSwgd2hlbiByZXNoYXBpbmcgZGF0YSwgdGhlcmUgaXMgYSByaXNrIG9mIGxvc2luZyBpbmZvcm1hdGlvbiBpZiB0aGUgZGF0YSBzdHJ1Y3R1cmUgaXMgbm90IHVuaXF1ZWx5IGlkZW50aWZpZWQgKGUuZy4sIGlmIHRoZXJlIGFyZSBkdXBsaWNhdGUgY29tYmluYXRpb25zIG9mIGtleXMpLiBUaGlzIGFsc28gY29udHJpYnV0ZXMgdG8gdGhlIGxhY2sgb2YgcGVyZmVjdCBzeW1tZXRyeS4KCmBgYHtyfQpzdG9ja3MgPC0gdGliYmxlKAogIHllYXIgICA9IGMoMjAxNSwgMjAxNSwgMjAxNiwgMjAxNiksCiAgaGFsZiAgPSBjKCAgIDEsICAgIDIsICAgICAxLCAgICAyKSwKICByZXR1cm4gPSBjKDEuODgsIDAuNTksIDAuOTIsIDAuMTcpCikKc3RvY2tzICU+JSAKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0geWVhciwgdmFsdWVzX2Zyb20gPSByZXR1cm4pICU+JSAKICBwaXZvdF9sb25nZXIoYDIwMTVgOmAyMDE2YCwgbmFtZXNfdG8gPSAieWVhciIsIHZhbHVlc190byA9ICJyZXR1cm4iKQpgYGAKClRoZSBgbmFtZXNfcHR5cGVzYCBhcmd1bWVudCBpbiBgcGl2b3RfbG9uZ2VyKClgIGlzIHVzZWQgdG8gZW5mb3JjZSB0aGUgZGF0YSB0eXBlIG9mIHRoZSBuZXcgY29sdW1uIGNyZWF0ZWQgZnJvbSBjb2x1bW4gbmFtZXMuIEhvd2V2ZXIsIGl0IHJlcXVpcmVzIHRoYXQgdGhlIHZhbHVlcyBjYW4gYWxyZWFkeSBiZSBpbnRlcnByZXRlZCBhcyB0aGF0IHR5cGUuIElmIHRoZXkgY2Fubm90IGJlIHNhZmVseSBjb252ZXJ0ZWQsIGl0IHdpbGwgcHJvZHVjZSBhbiBlcnJvci4KCkluIGNvbnRyYXN0LCBgbmFtZXNfdHJhbnNmb3JtYCBjYW4gYmUgdXNlZCB0byBleHBsaWNpdGx5IGNvbnZlcnQgdGhlIGNvbHVtbiB0byBhIG5ldyB0eXBlIChmb3IgZXhhbXBsZSwgbnVtZXJpYykgYWZ0ZXIgaXQgaXMgY3JlYXRlZC4gVGhpcyBpcyBvZnRlbiBtb3JlIGZsZXhpYmxlIHdoZW4gd29ya2luZyB3aXRoIGNvbHVtbiBuYW1lcyBsaWtlIHllYXJzIHRoYXQgYXJlIGluaXRpYWxseSBzdG9yZWQgYXMgY2hhcmFjdGVyIHZhbHVlcy4KCmBgYHtyIGV2YWw9RkFMU0V9CnN0b2NrcyAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0geWVhciwgdmFsdWVzX2Zyb20gPSByZXR1cm4pICU+JQogIHBpdm90X2xvbmdlcihgMjAxNWA6YDIwMTZgLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJ5ZWFyIiwKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInJldHVybiIsCiAgICAgICAgICAgICAgIG5hbWVzX3RyYW5zZm9ybSA9IGxpc3QoeWVhciA9IGFzLm51bWVyaWMpKQpgYGAKCiMjIyAxMi41LjEgRXhlcmNpc2VzCgojIyMjIDEKClRoZSBgZmlsbGAgYXJndW1lbnQgaW4gYHBpdm90X3dpZGVyKClgIGFuZCBgY29tcGxldGUoKWAgc2VydmVzIGEgc2ltaWxhciBwdXJwb3NlIGJ1dCBpcyB1c2VkIGluIHNsaWdodGx5IGRpZmZlcmVudCBjb250ZXh0cy4KCkluIGBwaXZvdF93aWRlcigpYCwgdGhlIGB2YWx1ZXNfZmlsbGAgYXJndW1lbnQgaXMgdXNlZCB0byByZXBsYWNlIG1pc3NpbmcgdmFsdWVzIHRoYXQgYXJpc2Ugd2hlbiByZXNoYXBpbmcgZGF0YSBmcm9tIGxvbmcgZm9ybWF0IHRvIHdpZGUgZm9ybWF0LiBJZiBhIGNvbWJpbmF0aW9uIG9mIHZhcmlhYmxlcyBkb2VzIG5vdCBleGlzdCBpbiB0aGUgb3JpZ2luYWwgZGF0YSwgYHBpdm90X3dpZGVyKClgIHdpbGwgY3JlYXRlIGFuIGBOQWAgdmFsdWUsIGFuZCBgdmFsdWVzX2ZpbGxgIGFsbG93cyB5b3UgdG8gc3BlY2lmeSB3aGF0IHZhbHVlIHNob3VsZCBiZSB1c2VkIGluc3RlYWQgKHN1Y2ggYXMgMCkuCgpJbiBjb250cmFzdCwgYGNvbXBsZXRlKClgIGlzIHVzZWQgdG8gZ2VuZXJhdGUgbWlzc2luZyBjb21iaW5hdGlvbnMgb2YgdmFyaWFibGVzIGluIGEgZGF0YXNldC4gVGhlIGBmaWxsYCBhcmd1bWVudCBpbiBgY29tcGxldGUoKWAgaXMgdGhlbiB1c2VkIHRvIGFzc2lnbiB2YWx1ZXMgdG8gdGhvc2UgbmV3bHkgY3JlYXRlZCByb3dzLiBUaGlzIG1lYW5zIHRoYXQgYGNvbXBsZXRlKClgIGFjdGl2ZWx5IGFkZHMgbWlzc2luZyByb3dzIHRvIHRoZSBkYXRhc2V0LCB3aGVyZWFzIGBwaXZvdF93aWRlcigpYCBvbmx5IGZpbGxzIGluIG1pc3NpbmcgY2VsbHMgY3JlYXRlZCBkdXJpbmcgcmVzaGFwaW5nLgoKSW4gc3VtbWFyeToKLSBgcGl2b3Rfd2lkZXIoKWAgdXNlcyBgdmFsdWVzX2ZpbGxgIHRvIHJlcGxhY2UgbWlzc2luZyB2YWx1ZXMgaW4gYW4gZXhpc3Rpbmcgc3RydWN0dXJlIGFmdGVyIHRyYW5zZm9ybWF0aW9uLgotIGBjb21wbGV0ZSgpYCB1c2VzIGBmaWxsYCB0byBhc3NpZ24gdmFsdWVzIHRvIG5ld2x5IGNyZWF0ZWQgcm93cyB0aGF0IHJlcHJlc2VudCBtaXNzaW5nIGNvbWJpbmF0aW9ucyBvZiB2YXJpYWJsZXMuCgojIyMjIDIKClRoZSBgZGlyZWN0aW9uYCBhcmd1bWVudCBpbiB0aGUgYGZpbGwoKWAgZnVuY3Rpb24gY29udHJvbHMgaG93IG1pc3NpbmcgdmFsdWVzIChgTkFgcykgYXJlIGZpbGxlZCB3aXRoaW4gYSBjb2x1bW4uCgotIGAiZG93biJgIGZpbGxzIG1pc3NpbmcgdmFsdWVzIHVzaW5nIHRoZSBsYXN0IG9ic2VydmVkIG5vbi1taXNzaW5nIHZhbHVlIG1vdmluZyBkb3dud2FyZC4KLSBgInVwImAgZmlsbHMgbWlzc2luZyB2YWx1ZXMgdXNpbmcgdGhlIG5leHQgb2JzZXJ2ZWQgbm9uLW1pc3NpbmcgdmFsdWUgbW92aW5nIHVwd2FyZC4KLSBgImRvd251cCJgIGZpcnN0IGZpbGxzIGRvd253YXJkLCB0aGVuIHVwd2FyZC4KLSBgInVwZG93biJgIGZpcnN0IGZpbGxzIHVwd2FyZCwgdGhlbiBkb3dud2FyZC4KClRoaXMgYXJndW1lbnQgYWxsb3dzIHlvdSB0byBjb250cm9sIHRoZSBkaXJlY3Rpb24gaW4gd2hpY2ggdmFsdWVzIGFyZSBjYXJyaWVkIGZvcndhcmQgb3IgYmFja3dhcmQsIHdoaWNoIGlzIGVzcGVjaWFsbHkgdXNlZnVsIHdoZW4gd29ya2luZyB3aXRoIGdyb3VwZWQgb3Igc2VxdWVudGlhbCBkYXRhLgoKIyMjIDEyLjYuMSBRdWVzdGlvbiAxCgpJbiB0aGlzIGNhc2Ugc3R1ZHksIHVzaW5nIGB2YWx1ZXNfZHJvcF9uYSA9IFRSVUVgIGlzIHJlYXNvbmFibGUgZm9yIHNpbXBsaWZ5aW5nIHRoZSBkYXRhc2V0IGFuZCBtYWtpbmcgaXQgZWFzaWVyIHRvIGNoZWNrIHdoZXRoZXIgdGhlIHZhbHVlcyBoYXZlIGJlZW4gY29ycmVjdGx5IHRyYW5zZm9ybWVkLiBCeSByZW1vdmluZyByb3dzIHdpdGggbWlzc2luZyB2YWx1ZXMsIGl0IGJlY29tZXMgZWFzaWVyIHRvIGZvY3VzIG9ubHkgb24gdGhlIG9ic2VydmVkIGRhdGEgYW5kIHZlcmlmeSBhY2N1cmFjeS4KCkhvd2V2ZXIsIHdoZXRoZXIgdGhpcyBpcyBhcHByb3ByaWF0ZSBkZXBlbmRzIG9uIGhvdyBtaXNzaW5nIHZhbHVlcyBhcmUgcmVwcmVzZW50ZWQgaW4gdGhlIGRhdGFzZXQuIEluIHRoaXMgY2FzZSwgdGhlcmUgYXJlICoqaW1wbGljaXQgbWlzc2luZyB2YWx1ZXMqKiwgbWVhbmluZyB0aGF0IHNvbWUgY29tYmluYXRpb25zIG9mIHZhcmlhYmxlcyBhcmUgYWJzZW50IGZyb20gdGhlIGRhdGEgcmF0aGVyIHRoYW4gZXhwbGljaXRseSByZWNvcmRlZCBhcyBgTkFgLiBXaGVuIGB2YWx1ZXNfZHJvcF9uYSA9IFRSVUVgIGlzIHVzZWQsIHRoZXNlIGltcGxpY2l0IG1pc3NpbmcgdmFsdWVzIGFyZSBlZmZlY3RpdmVseSBpZ25vcmVkLCB3aGljaCBtYXkgbGVhZCB0byBsb3NzIG9mIHBvdGVudGlhbGx5IG1lYW5pbmdmdWwgaW5mb3JtYXRpb24uCgpBbiBpbXBvcnRhbnQgZGlzdGluY3Rpb24gaXMgYmV0d2VlbiAqKm1pc3NpbmcgdmFsdWVzIChgTkFgKSBhbmQgemVyb3MqKjoKCi0gQW4gYE5BYCByZXByZXNlbnRzIGEgdmFsdWUgdGhhdCBpcyB1bmtub3duLCBub3QgcmVjb3JkZWQsIG9yIG5vdCBhcHBsaWNhYmxlLiBJdCBpbmRpY2F0ZXMgbWlzc2luZyBkYXRhLgotIEEgdmFsdWUgb2YgemVybyByZXByZXNlbnRzIGEga25vd24gcXVhbnRpdHksIG1lYW5pbmcgdGhhdCB0aGUgbWVhc3VyZWQgdmFsdWUgdHJ1bHkgaXMgemVyby4KClRoaXMgZGlzdGluY3Rpb24gbWF0dGVycyBiZWNhdXNlIHJlbW92aW5nIGBOQWAgdmFsdWVzIGRvZXMgbm90IGltcGx5IHRoYXQgdGhlIHZhbHVlIGlzIHplcm8uIFRyZWF0aW5nIG1pc3NpbmcgdmFsdWVzIGFzIHplcm8gY291bGQgbGVhZCB0byBpbmNvcnJlY3QgY29uY2x1c2lvbnMsIGVzcGVjaWFsbHkgaW4gYW5hbHlzZXMgd2hlcmUgYWJzZW5jZSBvZiBkYXRhIGlzIG5vdCB0aGUgc2FtZSBhcyBhYnNlbmNlIG9mIGV2ZW50cy4KCiMjIyAxMy4yLjEgUHJhY3RpY2UgUHJvYmxlbQoKIyMjIyAxCgpUbyBkcmF3IHRoZSByb3V0ZSBlYWNoIHBsYW5lIGZsaWVzIGZyb20gaXRzIG9yaWdpbiB0byBpdHMgZGVzdGluYXRpb24sIEkgd291bGQgbmVlZCB2YXJpYWJsZXMgdGhhdCBkZXNjcmliZSBib3RoIHRoZSBkZXBhcnR1cmUgYW5kIGFycml2YWwgbG9jYXRpb25zLiBTcGVjaWZpY2FsbHksIEkgd291bGQgbmVlZDoKCi0gT3JpZ2luIGFpcnBvcnQgY29kZSAoYG9yaWdpbmApCi0gRGVzdGluYXRpb24gYWlycG9ydCBjb2RlIChgZGVzdGApCi0gTGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSBmb3IgZWFjaCBhaXJwb3J0CgpUbyBvYnRhaW4gdGhpcyBpbmZvcm1hdGlvbiwgSSB3b3VsZCBuZWVkIHRvIGNvbWJpbmU6CgotIFRoZSBgZmxpZ2h0c2AgdGFibGUgKGZvciBvcmlnaW4gYW5kIGRlc3RpbmF0aW9uIGFpcnBvcnQgY29kZXMpCi0gVGhlIGBhaXJwb3J0c2AgdGFibGUgKGZvciBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlKQoKSSB3b3VsZCBqb2luIHRoZSBgZmxpZ2h0c2AgZGF0YSB0byB0aGUgYGFpcnBvcnRzYCB0YWJsZSB0d2ljZToKLSBPbmNlIHRvIGdldCB0aGUgY29vcmRpbmF0ZXMgb2YgdGhlIG9yaWdpbiBhaXJwb3J0Ci0gT25jZSB0byBnZXQgdGhlIGNvb3JkaW5hdGVzIG9mIHRoZSBkZXN0aW5hdGlvbiBhaXJwb3J0CgpUaGlzIHdvdWxkIGFsbG93IG1lIHRvIG1hcCB0aGUgZmxpZ2h0IHBhdGhzIGJldHdlZW4gYWlycG9ydHMuCgojIyMjIDIKClRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBgd2VhdGhlcmAgYW5kIGBhaXJwb3J0c2AgaXMgYmFzZWQgb24gdGhlIGFpcnBvcnQgbG9jYXRpb24uIFNwZWNpZmljYWxseSwgZWFjaCB3ZWF0aGVyIG9ic2VydmF0aW9uIGNvcnJlc3BvbmRzIHRvIGEgcGFydGljdWxhciBhaXJwb3J0LCBpZGVudGlmaWVkIGJ5IHRoZSBgb3JpZ2luYCBjb2RlLgoKVGhpcyBpcyBhICoqbWFueS10by1vbmUgcmVsYXRpb25zaGlwKio6Ci0gTWFueSByb3dzIGluIHRoZSBgd2VhdGhlcmAgdGFibGUgY29ycmVzcG9uZCB0byBvbmUgYWlycG9ydCBpbiB0aGUgYGFpcnBvcnRzYCB0YWJsZQoKSW4gYSByZWxhdGlvbmFsIGRpYWdyYW0sIHRoaXMgcmVsYXRpb25zaGlwIHNob3VsZCBiZSBzaG93biBhcyBhIGxpbmsgYmV0d2VlbiBgd2VhdGhlci5vcmlnaW5gIGFuZCBgYWlycG9ydHMuZmFhYCwgaW5kaWNhdGluZyB0aGF0IG11bHRpcGxlIHdlYXRoZXIgcmVjb3JkcyBhcmUgYXNzb2NpYXRlZCB3aXRoIGVhY2ggYWlycG9ydC4KCiMjIyMgMwoKVGhlIGB3ZWF0aGVyYCB0YWJsZSBjdXJyZW50bHkgb25seSBjb250YWlucyBpbmZvcm1hdGlvbiBmb3Igb3JpZ2luIChOWUMpIGFpcnBvcnRzLiBJZiBpdCBpbnN0ZWFkIGNvbnRhaW5lZCB3ZWF0aGVyIGRhdGEgZm9yIGFsbCBhaXJwb3J0cyBpbiB0aGUgVW5pdGVkIFN0YXRlcywgaXQgd291bGQgZGVmaW5lIGFuIGFkZGl0aW9uYWwgcmVsYXRpb25zaGlwIHdpdGggdGhlIGBmbGlnaHRzYCB0YWJsZS4KCkluIHRoaXMgY2FzZSwgdGhlcmUgd291bGQgYmUgKip0d28gcmVsYXRpb25zaGlwcyoqIGJldHdlZW4gYHdlYXRoZXJgIGFuZCBgZmxpZ2h0c2A6Ci0gT25lIGxpbmtpbmcgd2VhdGhlciB0byB0aGUgKipvcmlnaW4gYWlycG9ydCoqCi0gT25lIGxpbmtpbmcgd2VhdGhlciB0byB0aGUgKipkZXN0aW5hdGlvbiBhaXJwb3J0KioKCkJvdGggcmVsYXRpb25zaGlwcyB3b3VsZCBsaWtlbHkgYWxzbyBpbnZvbHZlIHRpbWUgdmFyaWFibGVzIChzdWNoIGFzIGRhdGUgYW5kIGhvdXIpLCBzaW5jZSB3ZWF0aGVyIGNvbmRpdGlvbnMgY2hhbmdlIG92ZXIgdGltZS4gVGhpcyB3b3VsZCBjcmVhdGUgYSBtb3JlIGNvbXBsZXggcmVsYXRpb25zaGlwIHdoZXJlIGVhY2ggZmxpZ2h0IGNvdWxkIGJlIGFzc29jaWF0ZWQgd2l0aCB3ZWF0aGVyIGNvbmRpdGlvbnMgYXQgYm90aCBkZXBhcnR1cmUgYW5kIGFycml2YWwgbG9jYXRpb25zLiAKCiMjIyA0CgpUbyByZXByZXNlbnQg4oCcc3BlY2lhbOKAnSBkYXlzIHdoZW4gZmV3ZXIgcGVvcGxlIGZseSwgSSB3b3VsZCBjcmVhdGUgYSBuZXcgZGF0YSBmcmFtZSB3aXRoIGEgdmFyaWFibGUgaW5kaWNhdGluZyB3aGV0aGVyIGEgZGF5IGlzIHNwZWNpYWwuCgpGb3IgZXhhbXBsZSwgdGhlIHRhYmxlIG1pZ2h0IGluY2x1ZGU6Ci0gYHllYXJgCi0gYG1vbnRoYAotIGBkYXlgCi0gYHNwZWNpYWxfZGF5YCAoZS5nLiwgVFJVRS9GQUxTRSBvciBhIGNhdGVnb3J5IHN1Y2ggYXMgaG9saWRheSBvciBldmVudCkKClRoZSAqKnByaW1hcnkga2V5KiogZm9yIHRoaXMgdGFibGUgd291bGQgYmUgdGhlIGNvbWJpbmF0aW9uIG9mIGB5ZWFyYCwgYG1vbnRoYCwgYW5kIGBkYXlgLCBzaW5jZSBlYWNoIGRhdGUgc2hvdWxkIGJlIHVuaXF1ZS4KClRoaXMgdGFibGUgd291bGQgY29ubmVjdCB0byB0aGUgYGZsaWdodHNgIHRhYmxlIHRocm91Z2ggdGhlc2Ugc2FtZSB2YXJpYWJsZXMgKGB5ZWFyYCwgYG1vbnRoYCwgYW5kIGBkYXlgKS4gVGhpcyBjcmVhdGVzIGEgKipvbmUtdG8tbWFueSByZWxhdGlvbnNoaXAqKiwgd2hlcmUgZWFjaCBkYXkgaW4gdGhlIG5ldyB0YWJsZSBjb3JyZXNwb25kcyB0byBtYW55IGZsaWdodHMgaW4gdGhlIGBmbGlnaHRzYCB0YWJsZS4KClRoaXMgc3RydWN0dXJlIHdvdWxkIGFsbG93IGFuYWx5c2lzIG9mIGhvdyBmbGlnaHQgcGF0dGVybnMgZGlmZmVyIG9uIHNwZWNpYWwgZGF5cyBjb21wYXJlZCB0byByZWd1bGFyIGRheXMuCgojIyMgMTMuMy4xIFF1ZXN0aW9uIDIKClRoZSBrZXlzIGluIGVhY2ggZGF0YXNldCBhcmU6CgotICoqYExhaG1hbjo6QmF0dGluZ2AqKjogYHBsYXllcklEYCwgYHllYXJJRGAsIGFuZCBgc3RpbnRgICAKICBUaGUgZG9jdW1lbnRhdGlvbiBkZXNjcmliZXMgYHBsYXllcklEYCBhcyB0aGUgcGxheWVyIGlkZW50aWZpZXIsIGB5ZWFySURgIGFzIHRoZSB5ZWFyLCBhbmQgYHN0aW50YCBhcyB0aGUgcGxheWVy4oCZcyBzdGludCB3aXRoaW4gYSBzZWFzb24sIHdoaWNoIHRvZ2V0aGVyIGlkZW50aWZ5IGVhY2ggYmF0dGluZyByZWNvcmQuCgotICoqYGJhYnluYW1lczo6YmFieW5hbWVzYCoqOiBgeWVhcmAsIGBzZXhgLCBhbmQgYG5hbWVgICAKICBUaGUgZGF0YXNldCBjb250YWlucyBvbmUgcm93IGZvciBlYWNoIGJhYnkgbmFtZSBieSB5ZWFyIGFuZCBzZXgsIHdpdGggYG5gIGFuZCBgcHJvcGAgZGVzY3JpYmluZyB0aGF0IGNvbWJpbmF0aW9uLCBzbyB0aGUgaWRlbnRpZnlpbmcgdmFyaWFibGVzIGFyZSBgeWVhcmAsIGBzZXhgLCBhbmQgYG5hbWVgLgoKLSAqKmBuYXNhd2VhdGhlcjo6YXRtb3NgKio6IGBsYXRgLCBgbG9uZ2AsIGB5ZWFyYCwgYW5kIGBtb250aGAgIAogIFRoZSBkb2N1bWVudGF0aW9uIHNheXMgdGhlIGRhdGFzZXQgcmVjb3JkcyBtZWFzdXJlbWVudHMgYnkgbG9jYXRpb24gKGBsYXRgLCBgbG9uZ2ApIGFuZCB0aW1lIChgeWVhcmAsIGBtb250aGApLCBzbyB0aG9zZSBmb3VyIHZhcmlhYmxlcyB0b2dldGhlciBpZGVudGlmeSBlYWNoIG9ic2VydmF0aW9uLgoKLSAqKmBmdWVsZWNvbm9teTo6dmVoaWNsZXNgKio6IGBpZGAgIAogIFRoZSBkYXRhc2V0IGRvY3VtZW50YXRpb24gZXhwbGljaXRseSBzdGF0ZXMgdGhhdCBgaWRgIGlzIHRoZSAqKnVuaXF1ZSBFUEEgaWRlbnRpZmllcioqLCBzbyB0aGlzIGlzIHRoZSBrZXkuIAoKLSAqKmBnZ3Bsb3QyOjpkaWFtb25kc2AqKjogbm8gb2J2aW91cyBuYXR1cmFsIGtleSAgCiAgVGhlIGRvY3VtZW50YXRpb24gbGlzdHMgMTAgZGVzY3JpcHRpdmUgdmFyaWFibGVzIChzdWNoIGFzIGBjYXJhdGAsIGBjdXRgLCBgY29sb3JgLCBgY2xhcml0eWAsIGBwcmljZWAsIGFuZCBkaW1lbnNpb25zKSwgYnV0IGl0IGRvZXMgbm90IGluY2x1ZGUgYW55IHZhcmlhYmxlIGRlc2NyaWJlZCBhcyBhIHVuaXF1ZSBpZGVudGlmaWVyLiBJbiBwcmFjdGljZSwgdGhpcyBtZWFucyBgZGlhbW9uZHNgIGRvZXMgbm90IGhhdmUgYSBzaW1wbGUgbmF0dXJhbCBrZXksIGFuZCBpZiBhIHVuaXF1ZSBrZXkgd2VyZSBuZWVkZWQsIGFuIGFydGlmaWNpYWwgcm93IElEIHdvdWxkIGxpa2VseSBuZWVkIHRvIGJlIGFkZGVkLiAKCmBgYHtyfQpuYW1lcyhCYXR0aW5nKQpgYGAKCiMjIyAxMy40LjYgUXVlc3Rpb24gMQoKVG8gY29tcHV0ZSB0aGUgYXZlcmFnZSBkZWxheSBieSBkZXN0aW5hdGlvbiBhbmQgdmlzdWFsaXplIGl0IHNwYXRpYWxseSwgSSBmaXJzdCBzdW1tYXJpemVkIHRoZSBhdmVyYWdlIGFycml2YWwgZGVsYXkgZm9yIGVhY2ggZGVzdGluYXRpb24gYWlycG9ydC4gVGhlbiBJIGpvaW5lZCB0aGlzIHN1bW1hcnkgdG8gdGhlIGBhaXJwb3J0c2AgZGF0YXNldCB0byBvYnRhaW4gbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSBmb3IgbWFwcGluZy4KCmBgYHtyfQojIFN0ZXAgMTogQ29tcHV0ZSBhdmVyYWdlIGRlbGF5IGJ5IGRlc3RpbmF0aW9uCmF2Z19kZWxheSA8LSBmbGlnaHRzICU+JQogIGdyb3VwX2J5KGRlc3QpICU+JQogIHN1bW1hcmlzZShhdmdfZGVsYXkgPSBtZWFuKGFycl9kZWxheSwgbmEucm0gPSBUUlVFKSkKCiMgU3RlcCAyOiBKb2luIHdpdGggYWlycG9ydHMgZGF0YQpkZWxheV9tYXAgPC0gYWlycG9ydHMgJT4lCiAgc2VtaV9qb2luKGZsaWdodHMsIGMoImZhYSIgPSAiZGVzdCIpKSAlPiUKICBsZWZ0X2pvaW4oYXZnX2RlbGF5LCBieSA9IGMoImZhYSIgPSAiZGVzdCIpKQoKIyBTdGVwIDM6IFBsb3QgdGhlIG1hcApnZ3Bsb3QoZGVsYXlfbWFwLCBhZXMoeCA9IGxvbiwgeSA9IGxhdCkpICsKICBib3JkZXJzKCJzdGF0ZSIpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGF2Z19kZWxheSwgc2l6ZSA9IGF2Z19kZWxheSksIGFscGhhID0gMC43KSArCiAgY29vcmRfcXVpY2ttYXAoKSArCiAgbGFicygKICAgIHRpdGxlID0gIkF2ZXJhZ2UgQXJyaXZhbCBEZWxheSBieSBEZXN0aW5hdGlvbiBBaXJwb3J0IiwKICAgIHggPSAiTG9uZ2l0dWRlIiwKICAgIHkgPSAiTGF0aXR1ZGUiLAogICAgY29sb3IgPSAiQXZnIERlbGF5IiwKICAgIHNpemUgPSAiQXZnIERlbGF5IgogICkKYGBgCgojIyMgMTMuNS4xIEV4ZXJjaXNlcwoKIyMjIyAxCgpBIGZsaWdodCBoYXMgYSBtaXNzaW5nIGB0YWlsbnVtYCB3aGVuIHRoZSBhaXJjcmFmdCByZWdpc3RyYXRpb24gbnVtYmVyIHdhcyBub3QgcmVjb3JkZWQgZm9yIHRoYXQgZmxpZ2h0LiBUaGlzIG1lYW5zIHRoZSBmbGlnaHQgY2Fubm90IGJlIG1hdGNoZWQgdG8gYSBzcGVjaWZpYyBwbGFuZSBpbiB0aGUgYHBsYW5lc2AgdGFibGUuCgpUYWlsIG51bWJlcnMgaW4gYGZsaWdodHNgIHRoYXQgZG8gbm90IGhhdmUgYSBtYXRjaGluZyByZWNvcmQgaW4gYHBsYW5lc2AgYXJlIGxpa2VseSBtaXNzaW5nIGZyb20gdGhlIEZBQSBhaXJjcmFmdCByZWdpc3RyeSBkYXRhIHVzZWQgaW4gdGhlIGBwbGFuZXNgIGRhdGFzZXQuIEEgbWFqb3IgcmVhc29uIGZvciB0aGlzIGlzIHRoYXQgbWFueSB1bm1hdGNoZWQgb3IgbWlzc2luZyB0YWlsIG51bWJlcnMgYXJlIGFzc29jaWF0ZWQgd2l0aCByZWdpb25hbCBjYXJyaWVycy4gSW4gdGhlIGBueWNmbGlnaHRzMTNgIGRhdGEsIG9uZSB2YXJpYWJsZSB0aGF0IGV4cGxhaW5zIG1vc3Qgb2YgdGhlIGlzc3VlIGlzIGBjYXJyaWVyYCwgYmVjYXVzZSBjZXJ0YWluIGFpcmxpbmVzIGFjY291bnQgZm9yIG1vc3Qgb2YgdGhlIHVubWF0Y2hlZCBwbGFuZSByZWNvcmRzLgoKVG8gaW52ZXN0aWdhdGUgdGhpcywgSSB3b3VsZCBjb21wYXJlIG1pc3NpbmcgYW5kIHVubWF0Y2hlZCB0YWlsIG51bWJlcnMgYWNyb3NzIGNhcnJpZXJzLgoKYGBge3J9CiMgRmxpZ2h0cyB3aXRoIG1pc3NpbmcgdGFpbCBudW1iZXJzCmZsaWdodHMgJT4lCiAgZmlsdGVyKGlzLm5hKHRhaWxudW0pKSAlPiUKICBjb3VudChjYXJyaWVyLCBzb3J0ID0gVFJVRSkKCiMgRmxpZ2h0cyB3aXRoIHRhaWwgbnVtYmVycyB0aGF0IGRvIG5vdCBtYXRjaCBwbGFuZXMKZmxpZ2h0cyAlPiUKICBmaWx0ZXIoIWlzLm5hKHRhaWxudW0pKSAlPiUKICBhbnRpX2pvaW4ocGxhbmVzLCBieSA9ICJ0YWlsbnVtIikgJT4lCiAgY291bnQoY2Fycmllciwgc29ydCA9IFRSVUUpCmBgYAoKIyMjIyAyCgpUbyBzaG93IG9ubHkgZmxpZ2h0cyBvcGVyYXRlZCBieSBwbGFuZXMgdGhhdCBmbGV3IGF0IGxlYXN0IDEwMCB0aW1lcywgSSBmaXJzdCBjb3VudGVkIHRoZSBudW1iZXIgb2YgZmxpZ2h0cyBmb3IgZWFjaCBgdGFpbG51bWAsIGZpbHRlcmVkIHRvIGtlZXAgb25seSB0aG9zZSB3aXRoIGF0IGxlYXN0IDEwMCBmbGlnaHRzLCBhbmQgdGhlbiB1c2VkIGBzZW1pX2pvaW4oKWAgdG8gcmV0YWluIG9ubHkgbWF0Y2hpbmcgcm93cyBpbiB0aGUgYGZsaWdodHNgIGRhdGFzZXQuCgpgYGB7cn0KcGxhbmVzXzEwMCA8LSBmbGlnaHRzICU+JQogIGNvdW50KHRhaWxudW0pICU+JQogIGZpbHRlcighaXMubmEodGFpbG51bSksIG4gPj0gMTAwKQoKZmxpZ2h0c18xMDAgPC0gZmxpZ2h0cyAlPiUKICBzZW1pX2pvaW4ocGxhbmVzXzEwMCwgYnkgPSAidGFpbG51bSIpCgpmbGlnaHRzXzEwMApgYGAKYGA=