Exam Instructions

The sample exam can be found here.

  • This exam covers material from R for Data Science (r4ds) to test your knowledge of the tidyverse. You may find the solution provided here for r4ds useful.

  • You must use tidy-style operations (e.g., the pipe operator %>%) unless instructed otherwise.

  • This file contains several deliberate errors. Please correct them as you go and point out your changes to your examiner.

  • You do not need to narrate your work.

  • You must complete the exam within 90 minutes.

  • You may use any books or digital resources you want during this examination, but you may not communicate with any person other than your examiner.

  • You are required to use the RStudio IDE for this exam. You may use either the desktop edition or rstudio.cloud as you prefer.

Load packages

This program will download from the internet and install the latest version of the tidyverse set of packages if they are not already installed in your R environment. It is necessary to have internet connection to download those packages.

if (!require(install.load)) {
  install.packages("install.load")
}

install.load::install_load("tidyverse")

# Set default ggplot2 theme to theme_bw()
theme_set(theme_bw())

To see the list of all packages in the tidyvserse use:

tidyverse_packages()
 [1] "broom"      "cli"        "crayon"     "dbplyr"     "dplyr"     
 [6] "forcats"    "ggplot2"    "haven"      "hms"        "httr"      
[11] "jsonlite"   "lubridate"  "magrittr"   "modelr"     "pillar"    
[16] "purrr"      "readr"      "readxl"     "reprex"     "rlang"     
[21] "rstudioapi" "rvest"      "stringr"    "tibble"     "tidyr"     
[26] "xml2"       "tidyverse" 

Basic Operations

Question 1

Question

Read the file person.csv and store the result in a tibble called person.

Solution

person <- read_csv("https://education.rstudio.com/blog/2020/08/more-example-exams/person.csv")

person

The function display_data() with the help of vtable package output a descriptive variable table as an HTML file that can be viewed continuously while working with data and the first 6 rows of the data is also shown on the console. This is helpful when working with the data, otherwise we will be typing glimpse(person) or View(person) to view the data every time.

# Display the structure of the dataframe

display_data <- function(df, title = "Data structure") {
  require(vtable)

  if (is.data.frame(df) == FALSE) {
    stop("Display_data() only works for dataframe object")
  } else {
    df %>%
      mutate(across(where(is.character), as.factor)) %>%
      vtable(factor.limit = 10, data.title = title)
    cat("Returns the First 6 rows\n")
    print(head(df))
  }
}

Question 2

Question

Create a tibble containing only family and personal names, in that order. You do not need to assign this tibble or any others to variables unless explicitly asked to do so. However, as noted in the introduction, you must use the pipe operator %>% and code that follows the tidyverse style guide.

Solution

person %>% select(family_name, personal_name)

Question 3

Question

Create a new tibble containing only the rows in which family names come before the letter M. Your solution should work for tables with more rows than the example, i.e., you cannot rely on row numbers or select specific names.

Solution

person %>% arrange(str_starts(family_name, pattern = "M", negate = TRUE))

Question 4

Question

Display all the rows in person sorted by family name length with the longest name first.

Solution

person %>% arrange(desc(str_length(family_name)))

Cleaning and Counting

Question 1

Question

Read the file measurements.csv to create a tibble called measurements. (The strings "rad", "sal", and "temp" in the quantity column stand for “radiation”, “salinity”, and “temperature” respectively.)

Solution

measurements <- read_csv("https://education.rstudio.com/blog/2020/08/more-example-exams/measurements.csv")

measurements

Question 2

Question

Create a tibble containing only rows where none of the values are NA and save in a tibble called cleaned.

Solution

cleaned <- measurements %>%
  drop_na()

cleaned

Question 3

Question

Count the number of measurements of each type of quantity in cleaned. Your result should have one row for each quantity "rad", "sal", and "temp".

Solution

cleaned %>% count(quantity)

Question 4

Question

Display the minimum and maximum value of reading separately for each quantity in cleaned. Your result should have one row for each quantity "rad", "sal", and "temp".

Solution

cleaned %>%
  group_by(quantity) %>%
  summarise(across(reading, list(Min = min, Max = max)))

Question 5

Question

Create a tibble in which all salinity ("sal") readings greater than 1 are divided by 100. (This is needed because some people wrote percentages as numbers from 0.0 to 1.0, but others wrote them as 0.0 to 100.0.)

Solution

cleaned %>% mutate(reading = case_when(
  quantity == "sal" & reading > 1 ~ reading / 100,
  TRUE ~ reading
))

Combining Data

Question 1

Question

Read visited.csv and drop rows containing any NAs, assigning the result to a new tibble called visited.

Solution

visited <- read_csv("https://education.rstudio.com/blog/2020/08/more-example-exams/visited.csv") %>%
  drop_na()

visited

Question 2

Question

Use an inner join to combine visited with cleaned using the visit_id column for matches.

Solution

combined_data <- visited %>% inner_join(cleaned, by = "visit_id")

combined_data

Question 3

Question

Find the highest radiation ("rad") reading at each site. (Sites are identified by values in the site_id column.)

Solution

combined_data %>%
  filter(quantity == "rad") %>%
  group_by(site_id) %>%
  summarise(Max_reading = max(reading))

Question 4

Question

Find the date of the highest radiation reading at each site.

Solution

max_rad4site <- combined_data %>%
  filter(quantity == "rad") %>%
  group_by(site_id, visit_date) %>%
  summarise(Max_reading = max(reading)) %>%
  slice_max(Max_reading)

max_rad4site

Plotting

Question 1

Question

The code below is supposed to read the file home-range-database.csv to create a tibble called hra_raw, but contains a bug. Describe and fix the problem. (There are several ways to fix it: please use whichever you prefer.)

hra_raw <- read_csv(here::here("data", "home-range-database.csv"))

Solution

There is no directory called data on my working directory and therefore, the chunk will throw an error.

hra_raw <- read_csv(here::here("data", "home-range-database.csv"))
Error: 'C:/Users/OGUNDEPO EZEKIEL .A/Desktop/R programming end-to-end/data/home-range-database.csv' does not exist.

I can resolve it by creating a directory called data and download the file home-range-database.csv in it.

fs::dir_create("data")

download.file("https://education.rstudio.com/blog/2020/08/more-example-exams/home-range-database.csv", "data/home-range-database.csv")
hra_raw <- read_csv(here::here("data", "home-range-database.csv"))

hra_raw

Question 2

Question

Convert the class column (which is text) to create a factor column class_fct and assign the result to a tibble hra. Use forcats to order the factor levels as:

  • mammalia
  • reptilia
  • aves
  • actinopterygii

Solution

hra <- hra_raw %>%
  mutate(class_fct = as_factor(class) %>% fct_relevel("mammalia", "reptilia", "aves", "actinopterygii")) %>%
  relocate(class_fct, .after = class)

hra

Question 3

Question

Create a scatterplot showing the relationship between log10.mass and log10.hra in hra.

Solution

hra %>%
  ggplot(aes(x = log10.mass, y = log10.hra)) +
  geom_point()

Question 4

Question

Colorize the points in the scatterplot by class_fct.

Solution

hra %>%
  ggplot(aes(x = log10.mass, y = log10.hra, color = class_fct)) +
  geom_point()

Question 5

Question

Display a scatterplot showing only data for birds (class aves) and fit a linear regression to that data using the lm function.

Solution

hra %>%
  filter(class == "aves") %>%
  ggplot(aes(x = log10.mass, y = log10.hra)) +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE) +
  labs(title = "Linear relationship between home range and mass for Aves")

Functional Programming

Question 1

Question

Write a function called summarize_table that takes a title string and a tibble as input and returns a string that says something like, “title has # rows and # columns”. For example, summarize_table('our table', person) should return the string "our table has 5 rows and 3 columns".

Solution

summarize_table <- function(title, tbl) {
  str_c(title, "has", nrow(tbl), "rows and", ncol(tbl), "columns", sep = " ")
}

summarize_table("our table", person)
[1] "our table has 5 rows and 3 columns"

Question 2

Question

Write another function called show_columns that takes a string and a tibble as input and returns a string that says something like, “table has columns name, name, name”. For example, show_columns('person', person) should return the string "person has columns person_id, personal_name, family_name".

Solution

show_columns <- function(title, tbl) {
  col_names <- names(tbl) %>%
    str_c(collapse = ", ")

  glue::glue("{title} has columns {col_names}")
}

show_columns("person", person)
person has columns person_id, personal_name, family_name

Question 3

Question

The function rows_from_file returns the first N rows from a table in a CSV file given the file’s name and the number of rows desired. Modify it so that if no value is specified for the number of rows, a default of 3 is used.

rows_from_file <- function(filename, num_rows) {
  readr::read_csv(filename) %>% head(n = num_rows)
}

Solution

rows_from_file <- function(filename, num_rows = NULL) {
  if (is.null(num_rows) == TRUE) {
    readr::read_csv(filename) %>% head(n = 3)
  } else {
    readr::read_csv(filename) %>% head(n = num_rows)
  }
}

# See it in action

rows_from_file("measurements.csv")
rows_from_file("measurements.csv", num_rows = 5)

Question 4

Question

The function long_name checks whether a string is longer than 4 characters. Use this function and a function from purrr to create a logical vector that contains the value TRUE where family names in the tibble person are longer than 4 characters, and FALSE where they are 4 characters or less.

long_name <- function(name) {
  stringr::str_length(name) > 4
}

Solution

long_name <- function(name) {
  stringr::str_length(name) > 4
}

# See it in action

map_lgl(person$family_name, long_name)
[1] FALSE  TRUE FALSE  TRUE  TRUE
person %>% mutate(family_name_len_greater_than_4 = map_lgl(family_name, long_name))

Wrapping Up

Question

Modify the YAML header of this file so that a table of contents is automatically created each time this document is knit, and fix any errors that are preventing the document from knitting cleanly.

---
title: "Tidyverse Exam Verson 2.0"
output:
html_document:
    theme: flatly
---

Solution

---
title: "Tidyverse Exam Verson 2.0"
output:
  html_document:
    toc: true
    theme: flatly
---


I hope you enjoy R Programming End to End Using Tidyverse Sample Exam Solution. For more updates in data science and artificial intelligence, you can follow Data Science Nigeria on Twitter and Linkedin. The Github repository for this AIBootcamp 2020 R programming session can be found here.

LS0tDQp0aXRsZTogIlIgUHJvZ3JhbW1pbmcgRW5kIHRvIEVuZCBVc2luZyBUaWR5dmVyc2UgU2FtcGxlIEV4YW0iDQpzdWJ0aXRsZTogfA0KICBbZm9yIERhdGEgU2NpZW5jZSBOaWdlcmlhIEFJIEJvb3RjYW1wIDIwMjBdKGh0dHBzOi8vd3d3LmRhdGFzY2llbmNlbmlnZXJpYS5vcmcpe3RhcmdldD0iX2JsYW5rIn0NCmF1dGhvcjogfA0KICB8IFtPZ3VuZGVwbyBFemVraWVsIEFkZWJheW9dKGh0dHBzOi8vYml0Lmx5L2diZ2FuYWx5c3Qpe3RhcmdldD0iX2JsYW5rIn0NCiAgfCBbSSdtIG9uIFR3aXR0ZXJdKGh0dHBzOi8vdHdpdHRlci5jb20vZ2JnYW5hbHlzdCl7dGFyZ2V0PSJfYmxhbmsifQ0KZGF0ZTogIlR1ZXNkYXksIE9jdG9iZXIgMjAsIDIwMjAiDQpvdXRwdXQ6IA0KICBodG1sX2RvY3VtZW50Og0KICAgIHRoZW1lOiBmbGF0bHkNCiAgICBoaWdobGlnaHQ6IGVzcHJlc3NvDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiAzDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAga2VlcF9tZDogdHJ1ZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCiAgICBkZl9wcmludDogcGFnZWQNCmVkaXRvcl9vcHRpb25zOiANCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUNCi0tLQ0KIA0KPGJyPg0KPGNlbnRlcj4NCmBgYHtyIG91dC53aWR0aCA9ICIxMDAlIiwgb3V0LmhlaWdodCA9ICAiMTAwJSIsIGZpZy5wb3MgPSAiYyIsIGVjaG8gPSBGQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJCb290Y2FtcF9zbGlkZXMvYm9vdGNhbXBfbG9nby5wbmciKQ0KYGBgDQo8L2NlbnRlcj4NCjxicj4NCiANCiANCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgICAgICB0aWR5ID0gJ3N0eWxlcicsIA0KICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQSwNCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCByb3dzLnByaW50ID0gNSkNCmBgYA0KDQojIEV4YW0gSW5zdHJ1Y3Rpb25zDQoNClRoZSBzYW1wbGUgZXhhbSBjYW4gYmUgZm91bmQgW2hlcmVdKGh0dHBzOi8vZWR1Y2F0aW9uLnJzdHVkaW8uY29tL2Jsb2cvMjAyMC8wOC9tb3JlLWV4YW1wbGUtZXhhbXMvKXt0YXJnZXQ9Il9ibGFuayJ9Lg0KDQotIFRoaXMgZXhhbSBjb3ZlcnMgbWF0ZXJpYWwgZnJvbSBSIGZvciBEYXRhIFNjaWVuY2UgKHI0ZHMpIHRvIHRlc3QgeW91ciBrbm93bGVkZ2Ugb2YgdGhlIFt0aWR5dmVyc2VdKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcvKS4gWW91IG1heSBmaW5kIHRoZSBzb2x1dGlvbiBwcm92aWRlZCBbaGVyZV0oaHR0cHM6Ly9qcm5vbGQuZ2l0aHViLmlvL3I0ZHMtZXhlcmNpc2Utc29sdXRpb25zLykgZm9yIHI0ZHMgdXNlZnVsLg0KDQotIFlvdSBtdXN0IHVzZSB0aWR5LXN0eWxlIG9wZXJhdGlvbnMgKGUuZy4sIHRoZSBwaXBlIG9wZXJhdG9yIGAlPiVgKSB1bmxlc3MgaW5zdHJ1Y3RlZCBvdGhlcndpc2UuIA0KDQotIFRoaXMgZmlsZSBjb250YWlucyBzZXZlcmFsIGRlbGliZXJhdGUgZXJyb3JzLiBQbGVhc2UgY29ycmVjdCB0aGVtIGFzIHlvdSBnbyBhbmQgcG9pbnQgb3V0IHlvdXIgY2hhbmdlcyB0byB5b3VyIGV4YW1pbmVyLg0KDQotIFlvdSBkbyBub3QgbmVlZCB0byBuYXJyYXRlIHlvdXIgd29yay4NCg0KLSBZb3UgbXVzdCBjb21wbGV0ZSB0aGUgZXhhbSB3aXRoaW4gOTAgbWludXRlcy4NCg0KLSBZb3UgbWF5IHVzZSBhbnkgYm9va3Mgb3IgZGlnaXRhbCByZXNvdXJjZXMgeW91IHdhbnQgZHVyaW5nIHRoaXMgZXhhbWluYXRpb24sIGJ1dCB5b3UgbWF5IG5vdCBjb21tdW5pY2F0ZSB3aXRoIGFueSBwZXJzb24gb3RoZXIgdGhhbiB5b3VyIGV4YW1pbmVyLg0KDQotIFlvdSBhcmUgcmVxdWlyZWQgdG8gdXNlIHRoZSBSU3R1ZGlvIElERSBmb3IgdGhpcyBleGFtLiBZb3UgbWF5IHVzZSBlaXRoZXIgdGhlIGRlc2t0b3AgZWRpdGlvbiBvciByc3R1ZGlvLmNsb3VkIGFzIHlvdSBwcmVmZXIuDQoNCiMgTG9hZCBwYWNrYWdlcw0KDQpUaGlzIHByb2dyYW0gd2lsbCBkb3dubG9hZCBmcm9tIHRoZSBpbnRlcm5ldCBhbmQgaW5zdGFsbCB0aGUgbGF0ZXN0IHZlcnNpb24gb2YgdGhlIHRpZHl2ZXJzZSBzZXQgb2YgcGFja2FnZXMgaWYgdGhleSBhcmUgbm90IGFscmVhZHkgaW5zdGFsbGVkIGluIHlvdXIgUiBlbnZpcm9ubWVudC4gSXQgaXMgbmVjZXNzYXJ5IHRvIGhhdmUgaW50ZXJuZXQgY29ubmVjdGlvbiB0byBkb3dubG9hZCB0aG9zZSBwYWNrYWdlcy4gDQoNCmBgYHtyIHBhY2thZ2VzfQ0KDQppZiAoIXJlcXVpcmUoaW5zdGFsbC5sb2FkKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJpbnN0YWxsLmxvYWQiKQ0KfQ0KDQppbnN0YWxsLmxvYWQ6Omluc3RhbGxfbG9hZCgidGlkeXZlcnNlIikNCg0KIyBTZXQgZGVmYXVsdCBnZ3Bsb3QyIHRoZW1lIHRvIHRoZW1lX2J3KCkNCnRoZW1lX3NldCh0aGVtZV9idygpKQ0KYGBgDQoNClRvIHNlZSB0aGUgbGlzdCBvZiBhbGwgcGFja2FnZXMgaW4gdGhlIHRpZHl2c2Vyc2UgdXNlOiANCg0KYGBge3IgdGlkeXZlcnNlIHBhY2thZ2VzfQ0KdGlkeXZlcnNlX3BhY2thZ2VzKCkNCmBgYA0KDQojIEJhc2ljIE9wZXJhdGlvbnMNCg0KIyMgUXVlc3Rpb24gMSB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KDQojIyMgUXVlc3Rpb24gDQoNClJlYWQgdGhlIGZpbGUgW3BlcnNvbi5jc3ZdKGh0dHBzOi8vZWR1Y2F0aW9uLnJzdHVkaW8uY29tL2Jsb2cvMjAyMC8wOC9tb3JlLWV4YW1wbGUtZXhhbXMvcGVyc29uLmNzdikgYW5kIHN0b3JlIHRoZSByZXN1bHQgaW4gYSB0aWJibGUgY2FsbGVkIGBwZXJzb25gLg0KDQojIyMgU29sdXRpb24NCg0KYGBge3J9DQpwZXJzb24gPC0gcmVhZF9jc3YoImh0dHBzOi8vZWR1Y2F0aW9uLnJzdHVkaW8uY29tL2Jsb2cvMjAyMC8wOC9tb3JlLWV4YW1wbGUtZXhhbXMvcGVyc29uLmNzdiIpDQoNCnBlcnNvbg0KYGBgDQpUaGUgZnVuY3Rpb24gYGRpc3BsYXlfZGF0YSgpYCB3aXRoIHRoZSBoZWxwIG9mIGB2dGFibGVgIHBhY2thZ2Ugb3V0cHV0IGEgZGVzY3JpcHRpdmUgdmFyaWFibGUgdGFibGUgYXMgYW4gSFRNTCBmaWxlIHRoYXQgY2FuIGJlIHZpZXdlZCBjb250aW51b3VzbHkgd2hpbGUgd29ya2luZyB3aXRoIGRhdGEgYW5kIHRoZSBmaXJzdCA2IHJvd3Mgb2YgdGhlIGRhdGEgaXMgYWxzbyBzaG93biBvbiB0aGUgY29uc29sZS4gVGhpcyBpcyBoZWxwZnVsIHdoZW4gd29ya2luZyB3aXRoIHRoZSBkYXRhLCBvdGhlcndpc2Ugd2Ugd2lsbCBiZSB0eXBpbmcgYGdsaW1wc2UocGVyc29uKWAgb3IgYFZpZXcocGVyc29uYCkgdG8gdmlldyB0aGUgZGF0YSBldmVyeSB0aW1lLg0KDQpgYGB7cn0NCiMgRGlzcGxheSB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhZnJhbWUNCg0KZGlzcGxheV9kYXRhIDwtIGZ1bmN0aW9uKGRmLCB0aXRsZSA9ICJEYXRhIHN0cnVjdHVyZSIpew0KICANCiAgcmVxdWlyZSh2dGFibGUpDQogIA0KICBpZihpcy5kYXRhLmZyYW1lKGRmKSA9PSBGQUxTRSl7DQogICAgc3RvcCgiRGlzcGxheV9kYXRhKCkgb25seSB3b3JrcyBmb3IgZGF0YWZyYW1lIG9iamVjdCIpDQogIH1lbHNlew0KICAgIGRmICU+JQ0KICAgICAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5jaGFyYWN0ZXIpLCBhcy5mYWN0b3IpKSAlPiUNCiAgICAgIHZ0YWJsZShmYWN0b3IubGltaXQgPSAxMCwgZGF0YS50aXRsZSA9IHRpdGxlKQ0KICAgIGNhdCgiUmV0dXJucyB0aGUgRmlyc3QgNiByb3dzXG4iKQ0KICAgIHByaW50KGhlYWQoZGYpKQ0KICB9DQogIA0KfQ0KYGBgDQoNCiFbXShCb290Y2FtcF9zbGlkZXMvdnRhYmxlLmdpZikNCg0KDQojIyBRdWVzdGlvbiAyIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQ0KDQoNCiMjIyBRdWVzdGlvbiANCg0KQ3JlYXRlIGEgdGliYmxlIGNvbnRhaW5pbmcgb25seSBmYW1pbHkgYW5kIHBlcnNvbmFsIG5hbWVzLCBpbiB0aGF0IG9yZGVyLiBZb3UgZG8gbm90IG5lZWQgdG8gYXNzaWduIHRoaXMgdGliYmxlIG9yIGFueSBvdGhlcnMgdG8gdmFyaWFibGVzIHVubGVzcyBleHBsaWNpdGx5IGFza2VkIHRvIGRvIHNvLiBIb3dldmVyLCBhcyBub3RlZCBpbiB0aGUgaW50cm9kdWN0aW9uLCB5b3UgbXVzdCB1c2UgdGhlIHBpcGUgb3BlcmF0b3IgYCU+JWAgYW5kIGNvZGUgdGhhdCBmb2xsb3dzIHRoZSB0aWR5dmVyc2Ugc3R5bGUgZ3VpZGUuDQoNCiMjIyBTb2x1dGlvbg0KDQpgYGB7cn0NCnBlcnNvbiAlPiUgc2VsZWN0KGZhbWlseV9uYW1lLCBwZXJzb25hbF9uYW1lKQ0KYGBgDQoNCiMjIFF1ZXN0aW9uIDMgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9DQoNCiMjIyBRdWVzdGlvbiANCg0KQ3JlYXRlIGEgbmV3IHRpYmJsZSBjb250YWluaW5nIG9ubHkgdGhlIHJvd3MgaW4gd2hpY2ggZmFtaWx5IG5hbWVzIGNvbWUgYmVmb3JlIHRoZSBsZXR0ZXIgYE1gLiBZb3VyIHNvbHV0aW9uIHNob3VsZCB3b3JrIGZvciB0YWJsZXMgd2l0aCBtb3JlIHJvd3MgdGhhbiB0aGUgZXhhbXBsZSwgaS5lLiwgeW91IGNhbm5vdCByZWx5IG9uIHJvdyBudW1iZXJzIG9yIHNlbGVjdCBzcGVjaWZpYyBuYW1lcy4NCg0KIyMjIFNvbHV0aW9uDQoNCmBgYHtyfQ0KcGVyc29uICU+JSBhcnJhbmdlKHN0cl9zdGFydHMoZmFtaWx5X25hbWUsIHBhdHRlcm4gPSAiTSIsIG5lZ2F0ZSA9IFRSVUUpKQ0KYGBgDQoNCiMjIFF1ZXN0aW9uIDQgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9DQoNCiMjIyBRdWVzdGlvbg0KDQpEaXNwbGF5IGFsbCB0aGUgcm93cyBpbiBgcGVyc29uYCBzb3J0ZWQgYnkgZmFtaWx5IG5hbWUgbGVuZ3RoIHdpdGggdGhlIGxvbmdlc3QgbmFtZSBmaXJzdC4NCg0KIyMjIFNvbHV0aW9uDQoNCmBgYHtyfQ0KcGVyc29uICU+JSBhcnJhbmdlKGRlc2Moc3RyX2xlbmd0aChmYW1pbHlfbmFtZSkpKQ0KYGBgDQoNCg0KIyBDbGVhbmluZyBhbmQgQ291bnRpbmcgDQoNCiMjIFF1ZXN0aW9uIDEgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9DQoNCiMjIyBRdWVzdGlvbg0KDQpSZWFkIHRoZSBmaWxlIFttZWFzdXJlbWVudHMuY3N2XShodHRwczovL2VkdWNhdGlvbi5yc3R1ZGlvLmNvbS9ibG9nLzIwMjAvMDgvbW9yZS1leGFtcGxlLWV4YW1zL21lYXN1cmVtZW50cy5jc3YpIHRvIGNyZWF0ZSBhIHRpYmJsZSBjYWxsZWQgYG1lYXN1cmVtZW50cy5gIChUaGUgc3RyaW5ncyBgInJhZCJgLCBgInNhbCJgLCBhbmQgYCJ0ZW1wImAgaW4gdGhlIGBxdWFudGl0eWAgY29sdW1uIHN0YW5kIGZvciDigJxyYWRpYXRpb27igJ0sIOKAnHNhbGluaXR54oCdLCBhbmQg4oCcdGVtcGVyYXR1cmXigJ0gcmVzcGVjdGl2ZWx5LikNCg0KIyMjIFNvbHV0aW9uDQoNCmBgYHtyfQ0KbWVhc3VyZW1lbnRzIDwtIHJlYWRfY3N2KCJodHRwczovL2VkdWNhdGlvbi5yc3R1ZGlvLmNvbS9ibG9nLzIwMjAvMDgvbW9yZS1leGFtcGxlLWV4YW1zL21lYXN1cmVtZW50cy5jc3YiKQ0KDQptZWFzdXJlbWVudHMgDQpgYGANCg0KDQojIyBRdWVzdGlvbiAyIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQ0KDQojIyMgUXVlc3Rpb24NCg0KQ3JlYXRlIGEgdGliYmxlIGNvbnRhaW5pbmcgb25seSByb3dzIHdoZXJlIG5vbmUgb2YgdGhlIHZhbHVlcyBhcmUgYE5BYCBhbmQgc2F2ZSBpbiBhIHRpYmJsZSBjYWxsZWQgYGNsZWFuZWRgLg0KDQojIyMgU29sdXRpb24NCg0KYGBge3J9DQpjbGVhbmVkIDwtICBtZWFzdXJlbWVudHMgJT4lIA0KICBkcm9wX25hKCkNCg0KY2xlYW5lZA0KYGBgDQoNCiMjIFF1ZXN0aW9uIDMgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9DQoNCiMjIyBRdWVzdGlvbg0KDQpDb3VudCB0aGUgbnVtYmVyIG9mIG1lYXN1cmVtZW50cyBvZiBlYWNoIHR5cGUgb2YgcXVhbnRpdHkgaW4gYGNsZWFuZWRgLiBZb3VyIHJlc3VsdCBzaG91bGQgaGF2ZSBvbmUgcm93IGZvciBlYWNoIHF1YW50aXR5IGAicmFkImAsIGAic2FsImAsIGFuZCBgInRlbXAiYC4NCg0KIyMjIFNvbHV0aW9uDQoNCmBgYHtyfQ0KY2xlYW5lZCAlPiUgY291bnQocXVhbnRpdHkpDQpgYGANCg0KDQojIyBRdWVzdGlvbiA0IHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQ0KDQojIyMgUXVlc3Rpb24NCg0KRGlzcGxheSB0aGUgbWluaW11bSBhbmQgbWF4aW11bSB2YWx1ZSBvZiBgcmVhZGluZ2Agc2VwYXJhdGVseSBmb3IgZWFjaCBxdWFudGl0eSBpbiBgY2xlYW5lZGAuIFlvdXIgcmVzdWx0IHNob3VsZCBoYXZlIG9uZSByb3cgZm9yIGVhY2ggcXVhbnRpdHkgYCJyYWQiYCwgYCJzYWwiYCwgYW5kIGAidGVtcCJgLg0KDQojIyMgU29sdXRpb24NCg0KYGBge3J9DQpjbGVhbmVkICU+JSBncm91cF9ieShxdWFudGl0eSkgJT4lIHN1bW1hcmlzZShhY3Jvc3MocmVhZGluZywgbGlzdChNaW4gPSBtaW4sIE1heCA9IG1heCkpKQ0KYGBgDQoNCiMjIFF1ZXN0aW9uIDUgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9DQoNCiMjIyBRdWVzdGlvbg0KDQpDcmVhdGUgYSB0aWJibGUgaW4gd2hpY2ggYWxsIHNhbGluaXR5IGAoInNhbCIpYCByZWFkaW5ncyBncmVhdGVyIHRoYW4gMSBhcmUgZGl2aWRlZCBieSAxMDAuIChUaGlzIGlzIG5lZWRlZCBiZWNhdXNlIHNvbWUgcGVvcGxlIHdyb3RlIHBlcmNlbnRhZ2VzIGFzIG51bWJlcnMgZnJvbSAwLjAgdG8gMS4wLCBidXQgb3RoZXJzIHdyb3RlIHRoZW0gYXMgMC4wIHRvIDEwMC4wLikNCg0KIyMjIFNvbHV0aW9uDQoNCmBgYHtyfQ0KY2xlYW5lZCAlPiUgbXV0YXRlKHJlYWRpbmcgPSBjYXNlX3doZW4oDQogIHF1YW50aXR5ID09ICJzYWwiICYgcmVhZGluZyA+IDEgfiByZWFkaW5nLzEwMCwNCiAgVFJVRSB+IHJlYWRpbmcNCikpDQpgYGANCg0KIyBDb21iaW5pbmcgRGF0YSANCg0KIyMgUXVlc3Rpb24gMSB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KIyMjIFF1ZXN0aW9uDQoNClJlYWQgW3Zpc2l0ZWQuY3N2XShodHRwczovL2VkdWNhdGlvbi5yc3R1ZGlvLmNvbS9ibG9nLzIwMjAvMDgvbW9yZS1leGFtcGxlLWV4YW1zL3Zpc2l0ZWQuY3N2KSBhbmQgZHJvcCByb3dzIGNvbnRhaW5pbmcgYW55IGBOQXNgLCBhc3NpZ25pbmcgdGhlIHJlc3VsdCB0byBhIG5ldyB0aWJibGUgY2FsbGVkIGB2aXNpdGVkYC4NCg0KIyMjIFNvbHV0aW9uDQoNCmBgYHtyfQ0KdmlzaXRlZCA8LSByZWFkX2NzdigiaHR0cHM6Ly9lZHVjYXRpb24ucnN0dWRpby5jb20vYmxvZy8yMDIwLzA4L21vcmUtZXhhbXBsZS1leGFtcy92aXNpdGVkLmNzdiIpICU+JSANCiAgZHJvcF9uYSgpDQoNCnZpc2l0ZWQgDQpgYGANCg0KIyMgUXVlc3Rpb24gMiB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KIyMjIFF1ZXN0aW9uDQoNClVzZSBhbiBpbm5lciBqb2luIHRvIGNvbWJpbmUgYHZpc2l0ZWRgIHdpdGggYGNsZWFuZWRgIHVzaW5nIHRoZSBgdmlzaXRfaWRgIGNvbHVtbiBmb3IgbWF0Y2hlcy4NCg0KIyMjIFNvbHV0aW9uDQoNCmBgYHtyfQ0KY29tYmluZWRfZGF0YSA8LSAgdmlzaXRlZCAlPiUgaW5uZXJfam9pbihjbGVhbmVkLCBieSA9ICJ2aXNpdF9pZCIpDQoNCmNvbWJpbmVkX2RhdGEgDQpgYGANCg0KIyMgUXVlc3Rpb24gMyB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KIyMjIFF1ZXN0aW9uDQoNCkZpbmQgdGhlIGhpZ2hlc3QgcmFkaWF0aW9uIGAoInJhZCIpYCByZWFkaW5nIGF0IGVhY2ggc2l0ZS4gKFNpdGVzIGFyZSBpZGVudGlmaWVkIGJ5IHZhbHVlcyBpbiB0aGUgYHNpdGVfaWRgIGNvbHVtbi4pDQoNCiMjIyBTb2x1dGlvbg0KDQpgYGB7cn0NCmNvbWJpbmVkX2RhdGEgJT4lIGZpbHRlcihxdWFudGl0eSA9PSAicmFkIikgJT4lIGdyb3VwX2J5KHNpdGVfaWQpICU+JSBzdW1tYXJpc2UoTWF4X3JlYWRpbmcgPSBtYXgocmVhZGluZykpDQpgYGANCg0KIyMgUXVlc3Rpb24gNCB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KIyMjIFF1ZXN0aW9uDQoNCkZpbmQgdGhlIGRhdGUgb2YgdGhlIGhpZ2hlc3QgcmFkaWF0aW9uIHJlYWRpbmcgYXQgZWFjaCBzaXRlLg0KDQojIyMgU29sdXRpb24NCg0KYGBge3J9DQptYXhfcmFkNHNpdGUgPC0gY29tYmluZWRfZGF0YSAlPiUgZmlsdGVyKHF1YW50aXR5ID09ICJyYWQiKSAlPiUgZ3JvdXBfYnkoc2l0ZV9pZCwgdmlzaXRfZGF0ZSkgJT4lIHN1bW1hcmlzZShNYXhfcmVhZGluZyA9IG1heChyZWFkaW5nKSkgJT4lIHNsaWNlX21heChNYXhfcmVhZGluZykNCg0KbWF4X3JhZDRzaXRlDQoNCmBgYA0KDQoNCiMgUGxvdHRpbmcgDQoNCiMjIFF1ZXN0aW9uIDEgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9DQoNCiMjIyBRdWVzdGlvbiANCg0KVGhlIGNvZGUgYmVsb3cgaXMgc3VwcG9zZWQgdG8gcmVhZCB0aGUgZmlsZSBbaG9tZS1yYW5nZS1kYXRhYmFzZS5jc3ZdKGh0dHBzOi8vZWR1Y2F0aW9uLnJzdHVkaW8uY29tL2Jsb2cvMjAyMC8wOC9tb3JlLWV4YW1wbGUtZXhhbXMvaG9tZS1yYW5nZS1kYXRhYmFzZS5jc3YpIHRvIGNyZWF0ZSBhIHRpYmJsZSBjYWxsZWQgYGhyYV9yYXdgLCBidXQgY29udGFpbnMgYSBidWcuIERlc2NyaWJlIGFuZCBmaXggdGhlIHByb2JsZW0uIChUaGVyZSBhcmUgc2V2ZXJhbCB3YXlzIHRvIGZpeCBpdDogcGxlYXNlIHVzZSB3aGljaGV2ZXIgeW91IHByZWZlci4pDQoNCmBgYHtyLCBpbmNsdWRlPSBGQUxTRSwgZWNobz0gRkFMU0V9DQojIEkgbmVlZCB0byByZW1vdmUgdGhlIGRpcmVjdG9yeSB0aGF0IEkgY3JlYXRlZCBpbiB0aGUgY2h1bmsgZGlyX2RhdGEgZm9yIHRoZSBhdXRvbWF0aW9uIG9mIHRoaXMgc2NyaXB0Lg0KDQpmczo6ZGlyX2RlbGV0ZSgiZGF0YSIpDQpgYGANCg0KDQpgYGB7ciwgZXZhbD0gRkFMU0V9DQpocmFfcmF3IDwtIHJlYWRfY3N2KGhlcmU6OmhlcmUoImRhdGEiLCAiaG9tZS1yYW5nZS1kYXRhYmFzZS5jc3YiKSkNCmBgYA0KIyMjIFNvbHV0aW9uDQoNClRoZXJlIGlzIG5vIGRpcmVjdG9yeSBjYWxsZWQgYGRhdGFgIG9uIG15IHdvcmtpbmcgZGlyZWN0b3J5IGFuZCB0aGVyZWZvcmUsIHRoZSBjaHVuayB3aWxsIHRocm93IGFuIGVycm9yLg0KDQpgYGB7ciBlcnJvciA9IFRSVUV9DQpocmFfcmF3IDwtIHJlYWRfY3N2KGhlcmU6OmhlcmUoImRhdGEiLCAiaG9tZS1yYW5nZS1kYXRhYmFzZS5jc3YiKSkNCmBgYA0KSSBjYW4gcmVzb2x2ZSBpdCBieSBjcmVhdGluZyBhIGRpcmVjdG9yeSBjYWxsZWQgYGRhdGFgIGFuZCBkb3dubG9hZCB0aGUgZmlsZSBgaG9tZS1yYW5nZS1kYXRhYmFzZS5jc3ZgIGluIGl0Lg0KDQpgYGB7ciBkaXJfZGF0YX0NCmZzOjpkaXJfY3JlYXRlKCJkYXRhIikNCg0KZG93bmxvYWQuZmlsZSgiaHR0cHM6Ly9lZHVjYXRpb24ucnN0dWRpby5jb20vYmxvZy8yMDIwLzA4L21vcmUtZXhhbXBsZS1leGFtcy9ob21lLXJhbmdlLWRhdGFiYXNlLmNzdiIsImRhdGEvaG9tZS1yYW5nZS1kYXRhYmFzZS5jc3YiKQ0KYGBgDQoNCg0KYGBge3J9DQpocmFfcmF3IDwtIHJlYWRfY3N2KGhlcmU6OmhlcmUoImRhdGEiLCAiaG9tZS1yYW5nZS1kYXRhYmFzZS5jc3YiKSkNCg0KaHJhX3JhdyANCmBgYA0KDQojIyBRdWVzdGlvbiAyIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQ0KDQojIyMgUXVlc3Rpb24NCg0KQ29udmVydCB0aGUgY2xhc3MgY29sdW1uICh3aGljaCBpcyB0ZXh0KSB0byBjcmVhdGUgYSBmYWN0b3IgY29sdW1uIGBjbGFzc19mY3RgIGFuZCBhc3NpZ24gdGhlIHJlc3VsdCB0byBhIHRpYmJsZSBgaHJhYC4gVXNlIGBmb3JjYXRzYCB0byBvcmRlciB0aGUgZmFjdG9yIGxldmVscyBhczoNCg0KLSBtYW1tYWxpYQ0KLSByZXB0aWxpYQ0KLSBhdmVzDQotIGFjdGlub3B0ZXJ5Z2lpDQoNCiMjIyBTb2x1dGlvbg0KDQpgYGB7cn0NCmhyYSA8LSBocmFfcmF3ICU+JSANCiAgbXV0YXRlKGNsYXNzX2ZjdCA9IGFzX2ZhY3RvcihjbGFzcykgJT4lIGZjdF9yZWxldmVsKCJtYW1tYWxpYSIsICJyZXB0aWxpYSIsICJhdmVzIiwgImFjdGlub3B0ZXJ5Z2lpIikpICU+JSANCiAgIHJlbG9jYXRlKGNsYXNzX2ZjdCwgLmFmdGVyID0gY2xhc3MpDQoNCmhyYSANCmBgYA0KDQojIyBRdWVzdGlvbiAzIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQ0KDQojIyMgUXVlc3Rpb24NCg0KQ3JlYXRlIGEgc2NhdHRlcnBsb3Qgc2hvd2luZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYGxvZzEwLm1hc3NgIGFuZCBgbG9nMTAuaHJhYCBpbiBgaHJhYC4NCg0KIyMjIFNvbHV0aW9uDQoNCmBgYHtyfQ0KaHJhICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gbG9nMTAubWFzcywgeSA9IGxvZzEwLmhyYSkpICsgDQogIGdlb21fcG9pbnQoKQ0KYGBgDQoNCiMjIFF1ZXN0aW9uIDQgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9DQoNCiMjIyBRdWVzdGlvbg0KDQpDb2xvcml6ZSB0aGUgcG9pbnRzIGluIHRoZSBzY2F0dGVycGxvdCBieSBgY2xhc3NfZmN0YC4NCg0KIyMjIFNvbHV0aW9uDQoNCmBgYHtyfQ0KaHJhICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gbG9nMTAubWFzcywgeSA9IGxvZzEwLmhyYSwgY29sb3IgPSBjbGFzc19mY3QpKSArIA0KICBnZW9tX3BvaW50KCkNCmBgYA0KDQojIyBRdWVzdGlvbiA1IHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQ0KDQojIyMgUXVlc3Rpb24NCg0KRGlzcGxheSBhIHNjYXR0ZXJwbG90IHNob3dpbmcgb25seSBkYXRhIGZvciBiaXJkcyAoY2xhc3MgYGF2ZXNgKSBhbmQgZml0IGEgbGluZWFyIHJlZ3Jlc3Npb24gdG8gdGhhdCBkYXRhIHVzaW5nIHRoZSBgbG1gIGZ1bmN0aW9uLg0KDQojIyMgU29sdXRpb24NCg0KYGBge3J9DQpocmEgJT4lIA0KICBmaWx0ZXIoY2xhc3MgPT0gImF2ZXMiKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IGxvZzEwLm1hc3MsIHkgPSAgbG9nMTAuaHJhKSkgKyANCiAgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpICsgDQogIGxhYnModGl0bGUgPSAiTGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGhvbWUgcmFuZ2UgYW5kIG1hc3MgZm9yIEF2ZXMiKQ0KYGBgDQoNCiMgRnVuY3Rpb25hbCBQcm9ncmFtbWluZyANCg0KIyMgUXVlc3Rpb24gMSB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KIyMjIFF1ZXN0aW9uDQoNCldyaXRlIGEgZnVuY3Rpb24gY2FsbGVkIGBzdW1tYXJpemVfdGFibGVgIHRoYXQgdGFrZXMgYSB0aXRsZSBzdHJpbmcgYW5kIGEgdGliYmxlIGFzIGlucHV0IGFuZCByZXR1cm5zIGEgc3RyaW5nIHRoYXQgc2F5cyBzb21ldGhpbmcgbGlrZSwg4oCcdGl0bGUgaGFzICMgcm93cyBhbmQgIyBjb2x1bW5z4oCdLiBGb3IgZXhhbXBsZSwgYHN1bW1hcml6ZV90YWJsZSgnb3VyIHRhYmxlJywgcGVyc29uKWAgc2hvdWxkIHJldHVybiB0aGUgc3RyaW5nIGAib3VyIHRhYmxlIGhhcyA1IHJvd3MgYW5kIDMgY29sdW1ucyJgLg0KDQojIyMgU29sdXRpb24NCg0KYGBge3J9DQpzdW1tYXJpemVfdGFibGUgPC0gZnVuY3Rpb24odGl0bGUsIHRibCkgew0KICBzdHJfYyh0aXRsZSwgImhhcyIsIG5yb3codGJsKSwgInJvd3MgYW5kIiwgbmNvbCh0YmwpLCAiY29sdW1ucyIsIHNlcCA9ICIgIikNCn0NCg0Kc3VtbWFyaXplX3RhYmxlKCJvdXIgdGFibGUiLCBwZXJzb24pDQpgYGANCg0KIyMgUXVlc3Rpb24gMiB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KIyMjIFF1ZXN0aW9uDQoNCldyaXRlIGFub3RoZXIgZnVuY3Rpb24gY2FsbGVkIGBzaG93X2NvbHVtbnNgIHRoYXQgdGFrZXMgYSBzdHJpbmcgYW5kIGEgdGliYmxlIGFzIGlucHV0IGFuZCByZXR1cm5zIGEgc3RyaW5nIHRoYXQgc2F5cyBzb21ldGhpbmcgbGlrZSwg4oCcdGFibGUgaGFzIGNvbHVtbnMgbmFtZSwgbmFtZSwgbmFtZSIuIEZvciBleGFtcGxlLCBgc2hvd19jb2x1bW5zKCdwZXJzb24nLCBwZXJzb24pYCBzaG91bGQgcmV0dXJuIHRoZSBzdHJpbmcgYCJwZXJzb24gaGFzIGNvbHVtbnMgcGVyc29uX2lkLCBwZXJzb25hbF9uYW1lLCBmYW1pbHlfbmFtZSJgLg0KDQojIyMgU29sdXRpb24NCg0KYGBge3J9DQpzaG93X2NvbHVtbnMgPC0gZnVuY3Rpb24odGl0bGUsIHRibCkgeyANCiAgY29sX25hbWVzIDwtIG5hbWVzKHRibCkgJT4lIA0KICAgIHN0cl9jKGNvbGxhcHNlID0gIiwgIikNCg0KZ2x1ZTo6Z2x1ZSgie3RpdGxlfSBoYXMgY29sdW1ucyB7Y29sX25hbWVzfSIpDQp9DQoNCnNob3dfY29sdW1ucygncGVyc29uJywgcGVyc29uKQ0KYGBgDQoNCiMjIFF1ZXN0aW9uIDMgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9DQoNCiMjIyBRdWVzdGlvbg0KDQpUaGUgZnVuY3Rpb24gYHJvd3NfZnJvbV9maWxlYCByZXR1cm5zIHRoZSBmaXJzdCBOIHJvd3MgZnJvbSBhIHRhYmxlIGluIGEgQ1NWIGZpbGUgZ2l2ZW4gdGhlIGZpbGXigJlzIG5hbWUgYW5kIHRoZSBudW1iZXIgb2Ygcm93cyBkZXNpcmVkLiBNb2RpZnkgaXQgc28gdGhhdCBpZiBubyB2YWx1ZSBpcyBzcGVjaWZpZWQgZm9yIHRoZSBudW1iZXIgb2Ygcm93cywgYSBkZWZhdWx0IG9mIDMgaXMgdXNlZC4NCg0KYGBge3J9DQpyb3dzX2Zyb21fZmlsZSA8LSBmdW5jdGlvbihmaWxlbmFtZSwgbnVtX3Jvd3MpIHsNCiAgcmVhZHI6OnJlYWRfY3N2KGZpbGVuYW1lKSAlPiUgaGVhZChuID0gbnVtX3Jvd3MpDQp9DQoNCmBgYA0KIyMjIFNvbHV0aW9uDQoNCmBgYHtyfQ0Kcm93c19mcm9tX2ZpbGUgPC0gZnVuY3Rpb24oZmlsZW5hbWUsIG51bV9yb3dzID0gTlVMTCkgew0KICANCiAgaWYoaXMubnVsbChudW1fcm93cykgPT0gVFJVRSl7DQogICAgDQogIHJlYWRyOjpyZWFkX2NzdihmaWxlbmFtZSkgJT4lIGhlYWQobiA9IDMpDQogIH1lbHNlew0KICAgICAgcmVhZHI6OnJlYWRfY3N2KGZpbGVuYW1lKSAlPiUgaGVhZChuID0gbnVtX3Jvd3MpDQogICAgfQ0KfQ0KDQojIFNlZSBpdCBpbiBhY3Rpb24NCg0Kcm93c19mcm9tX2ZpbGUoIm1lYXN1cmVtZW50cy5jc3YiKQ0KDQpyb3dzX2Zyb21fZmlsZSgibWVhc3VyZW1lbnRzLmNzdiIsIG51bV9yb3dzID0gNSkNCmBgYA0KDQojIyBRdWVzdGlvbiA0IHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQ0KDQojIyMgUXVlc3Rpb24NCg0KVGhlIGZ1bmN0aW9uIGBsb25nX25hbWVgIGNoZWNrcyB3aGV0aGVyIGEgc3RyaW5nIGlzIGxvbmdlciB0aGFuIDQgY2hhcmFjdGVycy4gVXNlIHRoaXMgZnVuY3Rpb24gYW5kIGEgZnVuY3Rpb24gZnJvbSBgcHVycnJgIHRvIGNyZWF0ZSBhIGxvZ2ljYWwgdmVjdG9yIHRoYXQgY29udGFpbnMgdGhlIHZhbHVlIGBUUlVFYCB3aGVyZSBmYW1pbHkgbmFtZXMgaW4gdGhlIHRpYmJsZSBgcGVyc29uYCBhcmUgbG9uZ2VyIHRoYW4gNCBjaGFyYWN0ZXJzLCBhbmQgYEZBTFNFYCB3aGVyZSB0aGV5IGFyZSA0IGNoYXJhY3RlcnMgb3IgbGVzcy4NCg0KYGBge3J9DQpsb25nX25hbWUgPC0gZnVuY3Rpb24obmFtZSkgew0KICBzdHJpbmdyOjpzdHJfbGVuZ3RoKG5hbWUpID4gNA0KfQ0KYGBgDQoNCiMjIyBTb2x1dGlvbg0KDQpgYGB7cn0NCmxvbmdfbmFtZSA8LSBmdW5jdGlvbihuYW1lKSB7DQogICAgICBzdHJpbmdyOjpzdHJfbGVuZ3RoKG5hbWUpID4gNA0KICAgIH0NCg0KIyBTZWUgaXQgaW4gYWN0aW9uDQoNCm1hcF9sZ2wocGVyc29uJGZhbWlseV9uYW1lLCBsb25nX25hbWUpDQoNCnBlcnNvbiAlPiUgbXV0YXRlKGZhbWlseV9uYW1lX2xlbl9ncmVhdGVyX3RoYW5fNCA9IG1hcF9sZ2woZmFtaWx5X25hbWUsIGxvbmdfbmFtZSkpDQpgYGANCg0KDQojIFdyYXBwaW5nIFVwDQoNCg0KIyMgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9DQoNCiMjIyBRdWVzdGlvbg0KDQpNb2RpZnkgdGhlIFlBTUwgaGVhZGVyIG9mIHRoaXMgZmlsZSBzbyB0aGF0IGEgdGFibGUgb2YgY29udGVudHMgaXMgYXV0b21hdGljYWxseSBjcmVhdGVkIGVhY2ggdGltZSB0aGlzIGRvY3VtZW50IGlzIGtuaXQsIGFuZCBmaXggYW55IGVycm9ycyB0aGF0IGFyZSBwcmV2ZW50aW5nIHRoZSBkb2N1bWVudCBmcm9tIGtuaXR0aW5nIGNsZWFubHkuDQogICAgDQpgYGANCi0tLQ0KdGl0bGU6ICJUaWR5dmVyc2UgRXhhbSBWZXJzb24gMi4wIg0Kb3V0cHV0Og0KaHRtbF9kb2N1bWVudDoNCiAgICB0aGVtZTogZmxhdGx5DQotLS0NCmBgYA0KIyMjIFNvbHV0aW9uDQoNCmBgYA0KLS0tDQp0aXRsZTogIlRpZHl2ZXJzZSBFeGFtIFZlcnNvbiAyLjAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdGhlbWU6IGZsYXRseQ0KLS0tDQpgYGANCg0KIw0KDQotLS0NCg0KSSBob3BlIHlvdSBlbmpveSBSIFByb2dyYW1taW5nIEVuZCB0byBFbmQgVXNpbmcgVGlkeXZlcnNlIFNhbXBsZSBFeGFtIFNvbHV0aW9uLiBGb3IgbW9yZSB1cGRhdGVzIGluIGRhdGEgc2NpZW5jZSBhbmQgYXJ0aWZpY2lhbCBpbnRlbGxpZ2VuY2UsIHlvdSBjYW4gZm9sbG93IERhdGEgU2NpZW5jZSBOaWdlcmlhIG9uICBbVHdpdHRlcl0oaHR0cHM6Ly90d2l0dGVyLmNvbS9EYXRhU2NpZW5jZU5JRyl7dGFyZ2V0PSJfYmxhbmsifSBhbmQgW0xpbmtlZGluXShodHRwczovL3d3dy5saW5rZWRpbi5jb20vY29tcGFueS9kYXRhc2NpZW5jZW5pZ2VyaWEvKXt0YXJnZXQ9Il9ibGFuayJ9LiBUaGUgR2l0aHViIHJlcG9zaXRvcnkgZm9yIHRoaXMgW0FJQm9vdGNhbXAgMjAyMF0oaHR0cHM6Ly93d3cuZGF0YXNjaWVuY2VuaWdlcmlhLm9yZy8yMDIwLWJvb3RjYW1wLyl7dGFyZ2V0PSJfYmxhbmsifSBSIHByb2dyYW1taW5nIHNlc3Npb24gIGNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL0RhdGFTY2llbmNlTmlnZXJpYS9SLXByb2dyYW1taW5nLWVuZC10by1lbmQpe3RhcmdldD0iX2JsYW5rIn0uDQoNCiFbXShodHRwczovL21lZGlhMi5naXBoeS5jb20vbWVkaWEvMTR0Q2VvU0dwWENXclF2aXhrL2dpcGh5LmdpZikgIA0K