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.

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

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

  1. 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"
df[, "xyz"]
## [1] "a"
df[, c("abc", "xyz")]
##   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.

names(Batting)
##  [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=