• Setup
  • Learning Goals
  • Changing the data layout with pivot functions
    • pivot_longer()
    • pivot_wider()
    • Demo video
    • Resources
    • Your turn!
  • Joining datasets
    • Mutating joins
    • Filtering joins
    • Demo video
    • Resources
    • Your turn!
  • Using forcats functions with factors
    • Demo video
    • Resources
    • Your turn!
  • Helpful functions to work with strings
    • Demo
    • Resources
    • Your turn!
  • Hints to exercises

Setup

To find other tutorials for this class, go to the main website, https://ds112-lendway.netlify.app/.

Welcome to your second tutorial for this class, COMP/STAT 112: Introduction to Data Science! It will be similar to the first, although in this one I opted for introducing material right in the tutorial rather than creating separate videos and slide decks. There are still demo videos and files embedded in this document.

As most of our files do, we start this one with three R code chunks: 1. options, 2. libraries and settings, 3. data.

knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE)
library(tidyverse)     # for data cleaning and plotting
library(gardenR)       # for Lisa's garden data
library(lubridate)     # for date manipulation
library(palmerpenguins)# for Palmer penguin data
# Lisa's garden data
data("garden_harvest")

# Seeds/plants (and other garden supply) costs
data("garden_spending")

# Planting dates and locations
data("garden_planting")

# Palmer penguins
data("penguins")

# US tuition by state data
us_avg_tuition <- read_csv("https://www.dropbox.com/s/9e0paradcwvuzll/us_avg_tuition.csv?dl=1") %>% 
   mutate(across(starts_with("20"), parse_number))

Learning Goals

After this tutorial, you should be able to do the following:

  • Use pivot_longer() and pivot_wider()to change the way the data are laid out.
  • Join tables of data together using the dplyr join functions and understand the differences among the different types of joins.
  • Use various forcats functions, including ones not covered in the tutorial, to change the order or values of levels of categorical variables.
  • Use the stringr functions covered in this tutorial (plus separate()) and know where to find information about other stringr functions (HINT: the cheatsheet is a great start).

Changing the data layout with pivot functions

This part of the tutorial will introduce you to two functions: pivot_longer() and pivot_wider(). These functions are used to change the way the data are laid out. The GIF below illustrates what the functions do. I encourage you to revisit this illustration after reading through the more detailed descriptions.

Image credit: Mara Averick (tweet from 2019-10-04)

pivot_longer()

pivot_longer(): makes the dataset longer, reducing the number of columns and increasing the number of rows. Often used when column names should be values of a variable.

The data below shows average college tuition costs in the US by state. Notice that years are column names.

ABCDEFGHIJ0123456789
State
<chr>
2004-05
<dbl>
2005-06
<dbl>
2006-07
<dbl>
2007-08
<dbl>
2008-09
<dbl>
2009-10
<dbl>
2010-11
<dbl>
2011-12
<dbl>
2012-13
<dbl>
Alabama568358415753600864757189807184529098
Alaska432846334919507050755455575957626026
Arizona5138541654815682605872638840996710134
Arkansas577260826232641564176627690170297287
California528655285335567258987259819494369361
Colorado470454075596622762846948774883168793
Connecticut7984824983688678872193719827973610037
Delaware835386118682894689959987105341102611363
Florida384839243888387941504783551159416495
Georgia429844924584479048315550642877097853

Now, we would like to change this so there is a variable called year that would indicate the year and the tuition values would be a sinle variable rather than spread across multiple variables - pivot_longer() to the rescue!

The generic code for pivot_longer() is shown here:

data %>% 
  pivot_longer(cols = ___________,
               names_to = "name_of_cols_variable",
               values_to = "name_of_values_variable")

Let’s try it with the tuition data:

us_avg_tuition %>% 
  pivot_longer(cols = starts_with("20"),
               names_to = "year",
               values_to = "avg_tuition")
ABCDEFGHIJ0123456789
State
<chr>
year
<chr>
avg_tuition
<dbl>
Alabama2004-055683
Alabama2005-065841
Alabama2006-075753
Alabama2007-086008
Alabama2008-096475
Alabama2009-107189
Alabama2010-118071
Alabama2011-128452
Alabama2012-139098
Alabama2013-149359

Now there is a row for each unique state and year combination and year and avg_tuition are variables. This dataset has more rows and fewer columns than the original dataset.

Let’s go over each argument in the function in more detail.

us_avg_tuition %>% 
  pivot_longer(cols = starts_with("20"),
               names_to = "year",
               values_to = "avg_tuition")
ABCDEFGHIJ0123456789
State
<chr>
year
<chr>
avg_tuition
<dbl>
Alabama2004-055683
Alabama2005-065841
Alabama2006-075753
Alabama2007-086008
Alabama2008-096475
Alabama2009-107189
Alabama2010-118071
Alabama2011-128452
Alabama2012-139098
Alabama2013-149359

The cols argument indicates which columns should be pivoted so that these column names become values of a new variable. You can make a list of column names or use helper functions to select columns (see the select() function on the dplyr cheatsheet for more detail or search for tidy-select in the Help tab). I used the starts_with() helper function in this example.

us_avg_tuition %>% 
  pivot_longer(cols = starts_with("20"),
               names_to = "year",
               values_to = "avg_tuition")
ABCDEFGHIJ0123456789
State
<chr>
year
<chr>
avg_tuition
<dbl>
Alabama2004-055683
Alabama2005-065841
Alabama2006-075753
Alabama2007-086008
Alabama2008-096475
Alabama2009-107189
Alabama2010-118071
Alabama2011-128452
Alabama2012-139098
Alabama2013-149359

The names_to argument is what you want to name the new variable where the column names will be stored. This needs to be in quotes.

us_avg_tuition %>% 
  pivot_longer(cols = starts_with("20"),
               names_to = "year",
               values_to = "avg_tuition")
ABCDEFGHIJ0123456789
State
<chr>
year
<chr>
avg_tuition
<dbl>
Alabama2004-055683
Alabama2005-065841
Alabama2006-075753
Alabama2007-086008
Alabama2008-096475
Alabama2009-107189
Alabama2010-118071
Alabama2011-128452
Alabama2012-139098
Alabama2013-149359

The values_to argument is what you want to name the new variable where the values that used to be spread across multiple columns will now be stored in one variable.

pivot_wider()

pivot_wider(): makes the dataset wider, reducing the number of rows and increasing the number of columns. Often used when observations are spread over multiple rows and the values for one variable should actually be their own variables.

Here is an example where the values of the third column (Population annual rate of increase (percent), Total fertility rate (children per women), etc.) should each be their own variable.

Now, let’s look at a similar example. I created a new dataset called penguins_fake which is a reorganization of the penguins data.

penguins_fake
ABCDEFGHIJ0123456789
species
<fct>
island
<fct>
sex
<fct>
year
<int>
obs
<int>
measurement
<chr>
value
<dbl>
AdelieTorgersenmale20071bill_length_mm39.1
AdelieTorgersenmale20071bill_depth_mm18.7
AdelieTorgersenmale20071flipper_length_mm181.0
AdelieTorgersenmale20071body_mass_g3750.0
AdelieTorgersenfemale20072bill_length_mm39.5
AdelieTorgersenfemale20072bill_depth_mm17.4
AdelieTorgersenfemale20072flipper_length_mm186.0
AdelieTorgersenfemale20072body_mass_g3800.0
AdelieTorgersenfemale20073bill_length_mm40.3
AdelieTorgersenfemale20073bill_depth_mm18.0

In penguins_fake, the column called measurement has all the names of the measurements. We would like to return those to column names so there is once again only one row for each penguin. We will use pivot_wider() to do that! Notice there is also a column called obs that identifies each unique observation from the original data - this is very important!

The generic code for pivot_wider() is shown here:

data %>% 
  pivot_wider(id_cols = ___________,
              names_from = variable_with_names,
              values_from = variable_with_values)

Let’s do this with the penguins_fake data:

penguins_fake %>% 
  pivot_wider(id_cols = species:obs,
              names_from = measurement,
              values_from = value)
ABCDEFGHIJ0123456789
species
<fct>
island
<fct>
sex
<fct>
year
<int>
obs
<int>
bill_length_mm
<dbl>
bill_depth_mm
<dbl>
flipper_length_mm
<dbl>
AdelieTorgersenmale2007139.118.7181
AdelieTorgersenfemale2007239.517.4186
AdelieTorgersenfemale2007340.318.0195
AdelieTorgersenNA20074NANANA
AdelieTorgersenfemale2007536.719.3193
AdelieTorgersenmale2007639.320.6190
AdelieTorgersenfemale2007738.917.8181
AdelieTorgersenmale2007839.219.6195
AdelieTorgersenNA2007934.118.1193
AdelieTorgersenNA20071042.020.2190

Now the four measurement variables each have their own column again. This dataset has more columns and fewer rows than the penguins_fake dataset.

Let’s go over each argument in the function in more detail.

penguins_fake %>% 
  pivot_wider(id_cols = species:obs,
              names_from = measurement,
              values_from = value)
ABCDEFGHIJ0123456789
species
<fct>
island
<fct>
sex
<fct>
year
<int>
obs
<int>
bill_length_mm
<dbl>
bill_depth_mm
<dbl>
flipper_length_mm
<dbl>
AdelieTorgersenmale2007139.118.7181
AdelieTorgersenfemale2007239.517.4186
AdelieTorgersenfemale2007340.318.0195
AdelieTorgersenNA20074NANANA
AdelieTorgersenfemale2007536.719.3193
AdelieTorgersenmale2007639.320.6190
AdelieTorgersenfemale2007738.917.8181
AdelieTorgersenmale2007839.219.6195
AdelieTorgersenNA2007934.118.1193
AdelieTorgersenNA20071042.020.2190

The id_cols argument is the set of columns that uniquely identifies each observation. By default it will be all columns that are not in the names_from and values_from arguments. Like cols from the pivot_longer() function, you can make a list of column names or use helper functions to select columns (see the select() function on the dplyr cheatsheet for more detail or search for tidy-select in the Help tab).

!!CAUTION!!: It is easy to make a mistake on the id_cols argument. For example, in the code below, I forgot to include obs. The result is something weird and unexpected with only 35 rows.

penguins_fake %>% 
  pivot_wider(id_cols = species:year,
              names_from = measurement,
              values_from = value)
ABCDEFGHIJ0123456789
species
<fct>
island
<fct>
sex
<fct>
year
<int>
bill_length_mm
<list>
bill_depth_mm
<list>
flipper_length_mm
<list>
AdelieTorgersenmale2007<dbl [7]><dbl [7]><dbl [7]>
AdelieTorgersenfemale2007<dbl [8]><dbl [8]><dbl [8]>
AdelieTorgersenNA2007<dbl [5]><dbl [5]><dbl [5]>
AdelieBiscoefemale2007<dbl [5]><dbl [5]><dbl [5]>
AdelieBiscoemale2007<dbl [5]><dbl [5]><dbl [5]>
AdelieDreamfemale2007<dbl [9]><dbl [9]><dbl [9]>
AdelieDreammale2007<dbl [10]><dbl [10]><dbl [10]>
AdelieDreamNA2007<dbl [1]><dbl [1]><dbl [1]>
AdelieBiscoefemale2008<dbl [9]><dbl [9]><dbl [9]>
AdelieBiscoemale2008<dbl [9]><dbl [9]><dbl [9]>
penguins_fake %>% 
  pivot_wider(id_cols = species:obs,
              names_from = measurement,
              values_from = value)
ABCDEFGHIJ0123456789
species
<fct>
island
<fct>
sex
<fct>
year
<int>
obs
<int>
bill_length_mm
<dbl>
bill_depth_mm
<dbl>
flipper_length_mm
<dbl>
AdelieTorgersenmale2007139.118.7181
AdelieTorgersenfemale2007239.517.4186
AdelieTorgersenfemale2007340.318.0195
AdelieTorgersenNA20074NANANA
AdelieTorgersenfemale2007536.719.3193
AdelieTorgersenmale2007639.320.6190
AdelieTorgersenfemale2007738.917.8181
AdelieTorgersenmale2007839.219.6195
AdelieTorgersenNA2007934.118.1193
AdelieTorgersenNA20071042.020.2190

The names_from argument is the variable (or variables) that contain values that you want to be turned into their own columns. This is not in quotes.

penguins_fake %>% 
  pivot_wider(id_cols = species:obs,
              names_from = measurement,
              values_from = value)
ABCDEFGHIJ0123456789
species
<fct>
island
<fct>
sex
<fct>
year
<int>
obs
<int>
bill_length_mm
<dbl>
bill_depth_mm
<dbl>
flipper_length_mm
<dbl>
AdelieTorgersenmale2007139.118.7181
AdelieTorgersenfemale2007239.517.4186
AdelieTorgersenfemale2007340.318.0195
AdelieTorgersenNA20074NANANA
AdelieTorgersenfemale2007536.719.3193
AdelieTorgersenmale2007639.320.6190
AdelieTorgersenfemale2007738.917.8181
AdelieTorgersenmale2007839.219.6195
AdelieTorgersenNA2007934.118.1193
AdelieTorgersenNA20071042.020.2190

The values_from argument is the variable (or variables) that should be values of the new variables.

Demo video

Now that you’ve learned the basics of pivoting, watch the video below that will walk you through some coding examples and download the R Markdown files to follow along. This is the same file you will use for the other topics.

Voicethread: pivoting demo

Resources

Your turn!

Exercise 1: pivot_wider()

Summarize the garden_harvest data to find the total harvest weight in pounds for each vegetable and day of week. Display the results so that the vegetables are rows but the days of the week are columns.

Exercise 2: pivot_longer()

Use the billboard dataset (search for it in help or type ?billboard in the console). It has rankings of songs for each week they entered the Billboard Top 100. The weeks are column names. Use pivot_longer() to make weeks a single column and remove rows with missing values for rank (HINT: use values_drop_na argument in pivot_longer()).

Joining datasets

When analyzing data, it is common to need to combine together datasets that are related. The join verbs will give us a way to do this. For all joins we must establish a correspondance or match between each case in the left table and zero or more cases in the right table.

A match between a case in the left table and a case in the right table is made based on the values in pairs of corresponding variables.

  • You specify which pairs to use.
  • A pair is a variable from the left table and a variable from the right table or a set of variables from the left and right table.
  • Cases must have exactly equal values in the pair for a match to be made.

When we join datasets, the general format is

left_dataset %>% 
  <JOIN>(right_dataset, 
         by=<HOW TO JOIN>)

where left_dataset and right_dataset are datasets, <JOIN> is the specific type of join, and <HOW TO JOIN> gives detailed information for how to do it.

The by argument tells it how to join the two datasets together, specifically which variables it should match. If the variables have the same names, we only need to write the name of that variable, in quotes: by = "variable_name".

If the two variables to match have different names in the two datasets, we can write by=c("name1"="name2"), where name1 is the variable in the left dataset to be matched to the name2 variable in the right dataset.

We can also match on multiple variables using by=c("name1"="name2", "name1a" = "name2a"), where the names to the left of the equals are variables from the left dataset and those on the right of the equals are from the right dataset.

If the by= is omitted from a join, then R will perform a natural join, which matches the two datasets by all variables they have in common. It is good practice to always include the by=.

Let’s discuss the different types of joins.

Mutating joins

The first class of joins are mutating joins, which add new variables (columns) to the left data table from matching observations in the right table.

The main difference in the three mutating join options in this class is how they answer the following questions:

  1. What happens when a case in the right table has no matches in the left table?
  2. What happens when a case in the left table has no matches in the right table?

Three mutating join functions:

left_join(): the output has all cases from the left, regardless if there is a match in the right, but discards any cases in the right that do not have a match in the left. (There is also a right_join() function which which does the opposite.)

Image credit: Wickham, R for Data Science

Credit: Garrick Aden-Buie – @grrrck

inner_join(): the output has only the cases from the left with a match in the right.

Image credit: Wickham, R for Data Science

Credit: Garrick Aden-Buie – @grrrck

full_join(): the output has all cases from the left and the right. This is less common than the first two join operators.

Image credit: Wickham, R for Data Science

Credit: Garrick Aden-Buie – @grrrck

When there are multiple matches in the right table for a particular case in the left table, all three of these mutating join operators produce a separate case in the new table for each of the matches from the right.

Examples

First, create two small datasets:

general_info <- tibble(
  person_id = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10),
  age = c(34, 54, 67, 92, 21, 32, 18, 45, 34, 55),
  rent_or_own = c("rent", "rent", "own", "rent", "rent", "own", "rent", "own", "own", "own")
)

general_info
ABCDEFGHIJ0123456789
person_id
<dbl>
age
<dbl>
rent_or_own
<chr>
134rent
254rent
367own
492rent
521rent
632own
718rent
845own
934own
1055own
pet_info <- tibble(
  person_id = c(2,3,5,7,8,10,11,12,13,14,15),
  pet_owner = c("yes", "no", "no", "yes", "yes", "no", "no", "no", "yes", "no", "no")
)

pet_info
ABCDEFGHIJ0123456789
person_id
<dbl>
pet_owner
<chr>
2yes
3no
5no
7yes
8yes
10no
11no
12no
13yes
14no
  1. Start with general_info and left_join() the pet_info by person_id:
general_info %>% 
  left_join(pet_info, 
            by = "person_id")
ABCDEFGHIJ0123456789
person_id
<dbl>
age
<dbl>
rent_or_own
<chr>
pet_owner
<chr>
134rentNA
254rentyes
367ownno
492rentNA
521rentno
632ownNA
718rentyes
845ownyes
934ownNA
1055ownno

The resulting table has 10 rows of data - the 10 observations from general_info. There are missing values for pet_owner for person_id’s that were in the general_info table and not the pet_info table.

??? How would the results change if a right_join() was used in the code above rather than a left_join()?

  1. Start with general_info and inner_join() the pet_info by person_id:
general_info %>% 
  inner_join(pet_info, 
             by = "person_id")
ABCDEFGHIJ0123456789
person_id
<dbl>
age
<dbl>
rent_or_own
<chr>
pet_owner
<chr>
254rentyes
367ownno
521rentno
718rentyes
845ownyes
1055ownno

The resulting table is only 6 rows with the observations that are in both general_info and pet_info.

  1. Start with general_info and full_join() the pet_info by person_id:
general_info %>% 
  full_join(pet_info, 
            by = "person_id")
ABCDEFGHIJ0123456789
person_id
<dbl>
age
<dbl>
rent_or_own
<chr>
pet_owner
<chr>
134rentNA
254rentyes
367ownno
492rentNA
521rentno
632ownNA
718rentyes
845ownyes
934ownNA
1055ownno

The resulting table has 15 rows. There are missing values for pet_owner for person_id’s that were in the general_info table and not the pet_info table, and there are missing values for age and rent for for person_id’s that were in the pet_info table and not the general_info table.

Filtering joins

The second class of joins are filtering joins, which select specific cases from the left table based on whether they match an observation in the right table.

semi_join(): discards any cases in the left table that do not have a match in the right table. If there are multiple matches of right cases to a left case, it keeps just one copy of the left case.

Image credit: Wickham, R for Data Science

Credit: Garrick Aden-Buie – @grrrck

anti_join(): discards any cases in the left table that have a match in the right table.

Image credit: Wickham, R for Data Science

Credit: Garrick Aden-Buie – @grrrck

Example

These use the example data from the previous section

A semi_join() is used to find the age and rental status (information in the general_info table) for people who are pet owners:

general_info %>% 
  semi_join(pet_info %>% filter(pet_owner == "yes"), 
            by = "person_id") 
ABCDEFGHIJ0123456789
person_id
<dbl>
age
<dbl>
rent_or_own
<chr>
254rent
718rent
845own

This returns a table with 3 rows. Since these are small tables, you should go verify this by hand. Also notice I did not press enter after the %>% inside the semi_join(). This is one case where we leave it on the same line to make it more readable.

Use an anti_join() to find the age and rental status (information in the general_info table) for people who are not confirmed pet owners (notice this includes unknowns):

general_info %>% 
  anti_join(pet_info %>% filter(pet_owner == "yes"),
            by = "person_id")
ABCDEFGHIJ0123456789
person_id
<dbl>
age
<dbl>
rent_or_own
<chr>
134rent
367own
492rent
521rent
632own
934own
1055own

Demo video

Now watch the video below that will walk you through some more advanced coding examples (plus a cameo by my daughter, Hadley). The downloadable R Markdown files to follow along are found below the pivoting video.

Voicethread: joining demo

Resources

Your turn!

Exercise 1: mutating join

Summarize the garden_harvest data to find the total harvest in pound for each vegetable variety and then try adding the plot from the garden_planting table. This will not turn out perfectly. What is the problem? How might you fix it?

Exercise 2: mutating join

I would like to understand how much money I “saved” by gardening, for each vegetable type. Describe how I could use the garden_harvest and garden_spending datasets, along with data from somewhere like this to answer this question. You can answer this in words, referencing various join functions. You don’t need R code but could provide some if it’s helpful.

Exercise 3: filtering join

Exclude the vegetable varieties from garden_harvest that are in plots M and H.

Using forcats functions with factors

R calls categorical variables factors. They are slightly different from character variables, but I will skip talking about this detail right now. The unique values that factor variables take are called levels.

There are many times we might want to modify factors. Below I list the functions I will demonstrate in the video. These are the ones I use most often, but there are many other useful functions. Check out the forcats cheatsheet (see link below) to see all of them. I highly recommend having it open when you work through the “Your turn!” exercises.

Changing the order of levels
fct_relevel(): manually reorder levels
fct_infreq(): order levels from highest to lowest frequency
fct_reorder(): reorder levels by values of another variable
fct_rev(): reverse the current order

Changing the values of levels
fct_recode(): manually change levels
fct_lump(): group together least common levels

Demo video

Watch the video below that illustrates these functions. The downloadable R Markdown files to follow along are found below the pivoting video.

Voicethread: working with factors

Resources

Your turn!

Exercise 1: changing order of factors

Subset the data to tomatoes. Reorder the tomato varieties from smallest to largest first harvest date. Create a barplot of total harvest in pounds for each variety, in the new order.

Exercise 2: changing order of factors

Reverse the order of the varieties in the previous plot.

Exercise 3: changing the values of levels

Combine the tomato varieties of volunteers and grape to a new level: “small tomatoes”.

Helpful functions to work with strings

Strings are found in the cells of character variables. For example, in the dataset I created below, each of the names in the name column is a string.

ABCDEFGHIJ0123456789
name
<chr>
adult
<lgl>
Lisa LendwayTRUE
Chris FischerTRUE
Adeline LendwayFALSE
Hadley LendwayFALSE

Here are the functions I will discuss in the video. This is just a small sample of the functions you could use to work with strings. Most of them are from the stringr package and start with str_. These functions all rely on something called regular expressions: regex or regexp, for short.

separate(): separates a character variable into multiple variables
str_length(): gives the number of characters in the string (includes white space, punctuation, etc.)
str_to_lower(): makes the characters lowercase
str_sub(): extract part of a string
str_detect(): returns TRUE/FALSE if a pattern is in the string

Demo

Watch the video below that illustrates these functions. The downloadable R Markdown files to follow along are found below the pivoting video.

Voicethread: working with strings

Your turn!

Exercise 1: working with strings

In the garden_harvest data, create two new variables: one that makes the varieties lowercase and another that finds the length of the variety name.

Exercise 2: working with strings

Find all the varieties that have “er” or “ar” in their name.

Hints to exercises

Exercise 1: pivot_wider()

Summarize the garden_harvest data to find the total harvest weight in pounds for each vegetable and day of week. Display the results so that the vegetables are rows but the days of the week are columns.

garden_harvest %>% 
  mutate(day_of_week = ) %>% 
  group_by(vegetable, day_of_week) %>% 
  summarize() %>% 
  pivot_wider()

Exercise 2: pivot_longer()

Use the billboard dataset (search for it in help or type ?billboard in the console). It has rankings of songs for each week they entered the Billboard Top 100. The weeks are column names. Use pivot_longer() to make weeks a single column and remove rows with missing values for rank (HINT: use values_drop_na argument in pivot_longer()).

billboard %>% 
  pivot_longer()

Exercise 1: mutating join

Summarize the garden_harvest data to find the total harvest in pound for each vegetable variety and then try adding the plot from the plant_date_loc table. This will not turn out perfectly. What is the problem? How might you fix it?

garden_harvest %>% 
  group_by(vegetable, variety) %>% 
  summarize() %>% 
  left_join(plant_date_loc,
            by = )

Exercise 2: mutating join

I would like to understand how much money I “saved” by gardening, for each vegetable type. Describe how I could use the garden_harvest and garden_spending datasets, along with data from somewhere like this to answer this question. You can answer this in words, referencing various join functions. You don’t need R code but could provide some if it’s helpful.

Exercise 3: filtering join

Exclude the vegetable varieties from garden_harvest that are in plots M and H.

garden_harvest %>% 
  anti_join(garden_planting,
            by = )

Exercise 1: changing order of factors

Subset the data to tomatoes. Reorder the tomato varieties from smallest to largest first harvest date. Create a barplot of total harvest in pounds for each variety, in the new order.

garden_harvest %>% 
  filter(vegetable == "tomatoes") %>% 
  mutate(variety = fct_reorder(___))
  group_by(variety) %>% 
  summarize(___) %>% 
  ggplot() +
  ___

Exercise 2: changing order of factors

Reverse the order of the varieties in the previous plot.

Exercise 3: changing the values of levels

Combine the tomato varieties of volunteers and grape to a new level: “small tomatoes”.

HINT: fct_relevel()

Exercise 1: working with strings

In the garden_harvest data, create two new variables: one that makes the varieties lowercase and another that finds the length of the variety name.

HINT: str_to_lower(), str_length()

Exercise 2: working with strings

Find all the varieties that have “er” or “ar” in their name.

HINT: str_detect() and use or, “|”

LS0tCnRpdGxlOiAiRXhwYW5kaW5nIHRoZSBkYXRhIHdyYW5nbGluZyB0b29sa2l0IgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKIyMgU2V0dXAKClRvIGZpbmQgb3RoZXIgdHV0b3JpYWxzIGZvciB0aGlzIGNsYXNzLCBnbyB0byB0aGUgbWFpbiB3ZWJzaXRlLCBbaHR0cHM6Ly9kczExMi1sZW5kd2F5Lm5ldGxpZnkuYXBwL10oaHR0cHM6Ly9kczExMi1sZW5kd2F5Lm5ldGxpZnkuYXBwLykuCgpXZWxjb21lIHRvIHlvdXIgc2Vjb25kIHR1dG9yaWFsIGZvciB0aGlzIGNsYXNzLCBDT01QL1NUQVQgMTEyOiAqSW50cm9kdWN0aW9uIHRvIERhdGEgU2NpZW5jZSohIEl0IHdpbGwgYmUgc2ltaWxhciB0byB0aGUgZmlyc3QsIGFsdGhvdWdoIGluIHRoaXMgb25lIEkgb3B0ZWQgZm9yIGludHJvZHVjaW5nIG1hdGVyaWFsIHJpZ2h0IGluIHRoZSB0dXRvcmlhbCByYXRoZXIgdGhhbiBjcmVhdGluZyBzZXBhcmF0ZSB2aWRlb3MgYW5kIHNsaWRlIGRlY2tzLiBUaGVyZSBhcmUgc3RpbGwgZGVtbyB2aWRlb3MgYW5kIGZpbGVzIGVtYmVkZGVkIGluIHRoaXMgZG9jdW1lbnQuCgpBcyBtb3N0IG9mIG91ciBmaWxlcyBkbywgd2Ugc3RhcnQgdGhpcyBvbmUgd2l0aCB0aHJlZSBSIGNvZGUgY2h1bmtzOiAxLiBvcHRpb25zLCAyLiBsaWJyYXJpZXMgYW5kIHNldHRpbmdzLCAzLiBkYXRhLiAKCmBgYHtyIHNldHVwfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFKQpgYGAKCmBgYHtyIGxpYnJhcmllc30KbGlicmFyeSh0aWR5dmVyc2UpICAgICAjIGZvciBkYXRhIGNsZWFuaW5nIGFuZCBwbG90dGluZwpsaWJyYXJ5KGdhcmRlblIpICAgICAgICMgZm9yIExpc2EncyBnYXJkZW4gZGF0YQpsaWJyYXJ5KGx1YnJpZGF0ZSkgICAgICMgZm9yIGRhdGUgbWFuaXB1bGF0aW9uCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpIyBmb3IgUGFsbWVyIHBlbmd1aW4gZGF0YQpgYGAKCmBgYHtyIG15X2xpYnJhcmllcywgaW5jbHVkZT1GQUxTRX0KIyBMaXNhIG5lZWRzIHRoaXMsIHN0dWRlbnRzIGRvbid0CmxpYnJhcnkoZG93bmxvYWR0aGlzKSAjIGZvciBpbmNsdWRpbmcgZG93bmxvYWQgYnV0dG9ucyBmb3IgZmlsZXMKbGlicmFyeShmbGFpcikgIyBmb3IgaGlnaGxpZ2h0aW5nIGNvZGUKYGBgCgpgYGB7ciBkYXRhfQojIExpc2EncyBnYXJkZW4gZGF0YQpkYXRhKCJnYXJkZW5faGFydmVzdCIpCgojIFNlZWRzL3BsYW50cyAoYW5kIG90aGVyIGdhcmRlbiBzdXBwbHkpIGNvc3RzCmRhdGEoImdhcmRlbl9zcGVuZGluZyIpCgojIFBsYW50aW5nIGRhdGVzIGFuZCBsb2NhdGlvbnMKZGF0YSgiZ2FyZGVuX3BsYW50aW5nIikKCiMgUGFsbWVyIHBlbmd1aW5zCmRhdGEoInBlbmd1aW5zIikKCiMgVVMgdHVpdGlvbiBieSBzdGF0ZSBkYXRhCnVzX2F2Z190dWl0aW9uIDwtIHJlYWRfY3N2KCJodHRwczovL3d3dy5kcm9wYm94LmNvbS9zLzllMHBhcmFkY3d2dXpsbC91c19hdmdfdHVpdGlvbi5jc3Y/ZGw9MSIpICU+JSAKICAgbXV0YXRlKGFjcm9zcyhzdGFydHNfd2l0aCgiMjAiKSwgcGFyc2VfbnVtYmVyKSkKYGBgCgojIyBMZWFybmluZyBHb2FscwoKQWZ0ZXIgdGhpcyB0dXRvcmlhbCwgeW91IHNob3VsZCBiZSBhYmxlIHRvIGRvIHRoZSBmb2xsb3dpbmc6CgoqIFVzZSBgcGl2b3RfbG9uZ2VyKClgIGFuZCBgcGl2b3Rfd2lkZXIoKWB0byBjaGFuZ2UgdGhlIHdheSB0aGUgZGF0YSBhcmUgbGFpZCBvdXQuICAKKiBKb2luIHRhYmxlcyBvZiBkYXRhIHRvZ2V0aGVyIHVzaW5nIHRoZSBgZHBseXJgIGpvaW4gZnVuY3Rpb25zIGFuZCB1bmRlcnN0YW5kIHRoZSBkaWZmZXJlbmNlcyBhbW9uZyB0aGUgZGlmZmVyZW50IHR5cGVzIG9mIGpvaW5zLiAgCiogVXNlIHZhcmlvdXMgYGZvcmNhdHNgIGZ1bmN0aW9ucywgaW5jbHVkaW5nIG9uZXMgbm90IGNvdmVyZWQgaW4gdGhlIHR1dG9yaWFsLCB0byBjaGFuZ2UgdGhlIG9yZGVyIG9yIHZhbHVlcyBvZiBsZXZlbHMgb2YgY2F0ZWdvcmljYWwgdmFyaWFibGVzLiAgCiogVXNlIHRoZSBgc3RyaW5ncmAgZnVuY3Rpb25zIGNvdmVyZWQgaW4gdGhpcyB0dXRvcmlhbCAocGx1cyBgc2VwYXJhdGUoKWApIGFuZCBrbm93IHdoZXJlIHRvIGZpbmQgaW5mb3JtYXRpb24gYWJvdXQgb3RoZXIgYHN0cmluZ3JgIGZ1bmN0aW9ucyAoSElOVDogdGhlIGNoZWF0c2hlZXQgaXMgYSBncmVhdCBzdGFydCkuCgojIyBDaGFuZ2luZyB0aGUgZGF0YSBsYXlvdXQgd2l0aCBwaXZvdCBmdW5jdGlvbnMKClRoaXMgcGFydCBvZiB0aGUgdHV0b3JpYWwgd2lsbCBpbnRyb2R1Y2UgeW91IHRvIHR3byBmdW5jdGlvbnM6IGBwaXZvdF9sb25nZXIoKWAgYW5kIGBwaXZvdF93aWRlcigpYC4gVGhlc2UgZnVuY3Rpb25zIGFyZSB1c2VkIHRvIGNoYW5nZSB0aGUgd2F5IHRoZSBkYXRhIGFyZSBsYWlkIG91dC4gVGhlIEdJRiBiZWxvdyBpbGx1c3RyYXRlcyB3aGF0IHRoZSBmdW5jdGlvbnMgZG8uIEkgZW5jb3VyYWdlIHlvdSB0byByZXZpc2l0IHRoaXMgaWxsdXN0cmF0aW9uIGFmdGVyIHJlYWRpbmcgdGhyb3VnaCB0aGUgbW9yZSBkZXRhaWxlZCBkZXNjcmlwdGlvbnMuCgo8Y2VudGVyPgoKIVtJbWFnZSBjcmVkaXQ6IE1hcmEgQXZlcmljayAodHdlZXQgZnJvbSAyMDE5LTEwLTA0KV0oaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vcy9hNm83NXpqNDQzYjJydjMvdGlkeXItbG9uZ2VyLXdpZGVyLW1vZGlmaWVkLmdpZj9kbD0xKQoKPC9jZW50ZXI+CgojIyMgYHBpdm90X2xvbmdlcigpYAoKKipgcGl2b3RfbG9uZ2VyKClgKio6IG1ha2VzIHRoZSBkYXRhc2V0IGxvbmdlciwgcmVkdWNpbmcgdGhlIG51bWJlciBvZiBjb2x1bW5zIGFuZCBpbmNyZWFzaW5nIHRoZSBudW1iZXIgb2Ygcm93cy4gT2Z0ZW4gdXNlZCB3aGVuIGNvbHVtbiBuYW1lcyBzaG91bGQgYmUgdmFsdWVzIG9mIGEgdmFyaWFibGUuCgpUaGUgZGF0YSBiZWxvdyBzaG93cyBhdmVyYWdlIGNvbGxlZ2UgdHVpdGlvbiBjb3N0cyBpbiB0aGUgVVMgYnkgc3RhdGUuIE5vdGljZSB0aGF0IHllYXJzIGFyZSBjb2x1bW4gbmFtZXMuCgpgYGB7ciwgZWNobz1GQUxTRX0KdXNfYXZnX3R1aXRpb24gCmBgYAoKTm93LCB3ZSB3b3VsZCBsaWtlIHRvIGNoYW5nZSB0aGlzIHNvIHRoZXJlIGlzIGEgdmFyaWFibGUgY2FsbGVkIGB5ZWFyYCB0aGF0IHdvdWxkIGluZGljYXRlIHRoZSB5ZWFyIGFuZCB0aGUgdHVpdGlvbiB2YWx1ZXMgd291bGQgYmUgYSBzaW5sZSB2YXJpYWJsZSByYXRoZXIgdGhhbiBzcHJlYWQgYWNyb3NzIG11bHRpcGxlIHZhcmlhYmxlcyAtIGBwaXZvdF9sb25nZXIoKWAgdG8gdGhlIHJlc2N1ZSEKClRoZSBnZW5lcmljIGNvZGUgZm9yIGBwaXZvdF9sb25nZXIoKWAgaXMgc2hvd24gaGVyZToKCmBgYHtyLCBldmFsPUZBTFNFfQpkYXRhICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9IF9fX19fX19fX19fLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJuYW1lX29mX2NvbHNfdmFyaWFibGUiLAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAibmFtZV9vZl92YWx1ZXNfdmFyaWFibGUiKQpgYGAKCkxldCdzIHRyeSBpdCB3aXRoIHRoZSB0dWl0aW9uIGRhdGE6CgpgYGB7ciBwaXZvdC1sb25nZXItZXgxfQp1c19hdmdfdHVpdGlvbiAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgiMjAiKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAieWVhciIsCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJhdmdfdHVpdGlvbiIpCmBgYAoKTm93IHRoZXJlIGlzIGEgcm93IGZvciBlYWNoIHVuaXF1ZSBzdGF0ZSBhbmQgeWVhciBjb21iaW5hdGlvbiBhbmQgYHllYXJgIGFuZCBgYXZnX3R1aXRpb25gIGFyZSB2YXJpYWJsZXMuIFRoaXMgZGF0YXNldCBoYXMgbW9yZSByb3dzIGFuZCBmZXdlciBjb2x1bW5zIHRoYW4gdGhlIG9yaWdpbmFsIGRhdGFzZXQuCgpMZXQncyBnbyBvdmVyIGVhY2ggYXJndW1lbnQgaW4gdGhlIGZ1bmN0aW9uIGluIG1vcmUgZGV0YWlsLgoKYGBge3IsIGVjaG89RkFMU0V9CmRlY29yYXRlX2NodW5rKCJwaXZvdC1sb25nZXItZXgxIikgJT4lIAogIGZsYWlyKCJjb2xzID0gIikgCmBgYAoKVGhlIGBjb2xzYCBhcmd1bWVudCBpbmRpY2F0ZXMgd2hpY2ggY29sdW1ucyBzaG91bGQgYmUgcGl2b3RlZCBzbyB0aGF0IHRoZXNlIGNvbHVtbiBuYW1lcyBiZWNvbWUgdmFsdWVzIG9mIGEgbmV3IHZhcmlhYmxlLiBZb3UgY2FuIG1ha2UgYSBsaXN0IG9mIGNvbHVtbiBuYW1lcyBvciB1c2UgaGVscGVyIGZ1bmN0aW9ucyB0byBzZWxlY3QgY29sdW1ucyAoc2VlIHRoZSBgc2VsZWN0KClgIGZ1bmN0aW9uIG9uIHRoZSBgZHBseXJgIGNoZWF0c2hlZXQgZm9yIG1vcmUgZGV0YWlsIG9yIHNlYXJjaCBmb3IgYHRpZHktc2VsZWN0YCBpbiAgdGhlIEhlbHAgdGFiKS4gSSB1c2VkIHRoZSBgc3RhcnRzX3dpdGgoKWAgaGVscGVyIGZ1bmN0aW9uIGluIHRoaXMgZXhhbXBsZS4gCgpgYGB7ciwgZWNobz1GQUxTRX0KZGVjb3JhdGVfY2h1bmsoInBpdm90LWxvbmdlci1leDEiKSAlPiUgCiAgZmxhaXIoIm5hbWVzX3RvID0gIikgCmBgYAoKVGhlIGBuYW1lc190b2AgYXJndW1lbnQgaXMgd2hhdCB5b3Ugd2FudCB0byBuYW1lIHRoZSBuZXcgdmFyaWFibGUgd2hlcmUgdGhlIGNvbHVtbiBuYW1lcyB3aWxsIGJlIHN0b3JlZC4gVGhpcyBuZWVkcyB0byBiZSBpbiBxdW90ZXMuCgpgYGB7ciwgZWNobz1GQUxTRX0KZGVjb3JhdGVfY2h1bmsoInBpdm90LWxvbmdlci1leDEiKSAlPiUgCiAgZmxhaXIoInZhbHVlc190byA9ICIpIApgYGAKClRoZSBgdmFsdWVzX3RvYCBhcmd1bWVudCBpcyB3aGF0IHlvdSB3YW50IHRvIG5hbWUgdGhlIG5ldyB2YXJpYWJsZSB3aGVyZSB0aGUgdmFsdWVzIHRoYXQgdXNlZCB0byBiZSBzcHJlYWQgYWNyb3NzIG11bHRpcGxlIGNvbHVtbnMgd2lsbCBub3cgYmUgc3RvcmVkIGluIG9uZSB2YXJpYWJsZS4gIAoKIyMjIGBwaXZvdF93aWRlcigpYAoKYHBpdm90X3dpZGVyKClgOiBtYWtlcyB0aGUgZGF0YXNldCB3aWRlciwgcmVkdWNpbmcgdGhlIG51bWJlciBvZiByb3dzIGFuZCBpbmNyZWFzaW5nIHRoZSBudW1iZXIgb2YgY29sdW1ucy4gT2Z0ZW4gdXNlZCB3aGVuIG9ic2VydmF0aW9ucyBhcmUgc3ByZWFkIG92ZXIgbXVsdGlwbGUgcm93cyBhbmQgdGhlIHZhbHVlcyBmb3Igb25lIHZhcmlhYmxlIHNob3VsZCBhY3R1YWxseSBiZSB0aGVpciBvd24gdmFyaWFibGVzLgoKSGVyZSBpcyBhbiBleGFtcGxlIHdoZXJlIHRoZSB2YWx1ZXMgb2YgdGhlIHRoaXJkIGNvbHVtbiAoUG9wdWxhdGlvbiBhbm51YWwgcmF0ZSBvZiBpbmNyZWFzZSAocGVyY2VudCksIFRvdGFsIGZlcnRpbGl0eSByYXRlIChjaGlsZHJlbiBwZXIgd29tZW4pLCBldGMuKSBzaG91bGQgZWFjaCBiZSB0aGVpciBvd24gdmFyaWFibGUuCgo8Y2VudGVyPgoKIVtEYXRhIGZyb206IGh0dHBzOi8vZGF0YS51bi5vcmcvXSguLi8uLi9pbWFnZXMvcGl2b3Rfd2lkZXJfZXhhbXBsZS5wbmcpCgo8L2NlbnRlcj4KCk5vdywgbGV0J3MgbG9vayBhdCBhIHNpbWlsYXIgZXhhbXBsZS4gSSBjcmVhdGVkIGEgbmV3IGRhdGFzZXQgY2FsbGVkIGBwZW5ndWluc19mYWtlYCB3aGljaCBpcyBhIHJlb3JnYW5pemF0aW9uIG9mIHRoZSAgYHBlbmd1aW5zYCBkYXRhLiAKCmBgYHtyLCBlY2hvPUZBTFNFfQpwZW5ndWluc19mYWtlIDwtIHBlbmd1aW5zICU+JSAKICBtdXRhdGUob2JzID0gcm93X251bWJlcigpKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBiaWxsX2xlbmd0aF9tbTpib2R5X21hc3NfZywKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAibWVhc3VyZW1lbnQiLAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWUiKQpgYGAKCmBgYHtyfQpwZW5ndWluc19mYWtlCmBgYAoKSW4gYHBlbmd1aW5zX2Zha2VgLCB0aGUgY29sdW1uIGNhbGxlZCBgbWVhc3VyZW1lbnRgIGhhcyBhbGwgdGhlIG5hbWVzIG9mIHRoZSBtZWFzdXJlbWVudHMuIFdlIHdvdWxkIGxpa2UgdG8gcmV0dXJuIHRob3NlIHRvIGNvbHVtbiBuYW1lcyBzbyB0aGVyZSBpcyBvbmNlIGFnYWluIG9ubHkgb25lIHJvdyBmb3IgZWFjaCBwZW5ndWluLiBXZSB3aWxsIHVzZSBgcGl2b3Rfd2lkZXIoKWAgdG8gZG8gdGhhdCEgTm90aWNlIHRoZXJlIGlzIGFsc28gYSBjb2x1bW4gY2FsbGVkIGBvYnNgIHRoYXQgaWRlbnRpZmllcyBlYWNoIHVuaXF1ZSBvYnNlcnZhdGlvbiBmcm9tIHRoZSBvcmlnaW5hbCBkYXRhIC0gdGhpcyBpcyB2ZXJ5IGltcG9ydGFudCEgCgpUaGUgZ2VuZXJpYyBjb2RlIGZvciBgcGl2b3Rfd2lkZXIoKWAgaXMgc2hvd24gaGVyZToKCmBgYHtyLCBldmFsPUZBTFNFfQpkYXRhICU+JSAKICBwaXZvdF93aWRlcihpZF9jb2xzID0gX19fX19fX19fX18sCiAgICAgICAgICAgICAgbmFtZXNfZnJvbSA9IHZhcmlhYmxlX3dpdGhfbmFtZXMsCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSB2YXJpYWJsZV93aXRoX3ZhbHVlcykKYGBgCgpMZXQncyBkbyB0aGlzIHdpdGggdGhlIGBwZW5ndWluc19mYWtlYCBkYXRhOgoKYGBge3IgcGl2b3Qtd2lkZXItZXgxfQpwZW5ndWluc19mYWtlICU+JSAKICBwaXZvdF93aWRlcihpZF9jb2xzID0gc3BlY2llczpvYnMsCiAgICAgICAgICAgICAgbmFtZXNfZnJvbSA9IG1lYXN1cmVtZW50LAogICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gdmFsdWUpCmBgYAoKTm93IHRoZSBmb3VyIG1lYXN1cmVtZW50IHZhcmlhYmxlcyBlYWNoIGhhdmUgdGhlaXIgb3duIGNvbHVtbiBhZ2Fpbi4gVGhpcyBkYXRhc2V0IGhhcyBtb3JlIGNvbHVtbnMgYW5kIGZld2VyIHJvd3MgdGhhbiB0aGUgYHBlbmd1aW5zX2Zha2VgIGRhdGFzZXQuCgpMZXQncyBnbyBvdmVyIGVhY2ggYXJndW1lbnQgaW4gdGhlIGZ1bmN0aW9uIGluIG1vcmUgZGV0YWlsLgoKYGBge3IsIGVjaG89RkFMU0V9CmRlY29yYXRlX2NodW5rKCJwaXZvdC13aWRlci1leDEiKSAlPiUgCiAgZmxhaXIoImlkX2NvbHMgPSAiKSAKYGBgCgpUaGUgYGlkX2NvbHNgIGFyZ3VtZW50IGlzIHRoZSBzZXQgb2YgY29sdW1ucyB0aGF0IHVuaXF1ZWx5IGlkZW50aWZpZXMgZWFjaCBvYnNlcnZhdGlvbi4gQnkgZGVmYXVsdCBpdCB3aWxsIGJlIGFsbCBjb2x1bW5zIHRoYXQgYXJlIG5vdCBpbiB0aGUgYG5hbWVzX2Zyb21gIGFuZCBgdmFsdWVzX2Zyb21gIGFyZ3VtZW50cy4gTGlrZSBgY29sc2AgZnJvbSB0aGUgYHBpdm90X2xvbmdlcigpYCBmdW5jdGlvbiwgeW91IGNhbiBtYWtlIGEgbGlzdCBvZiBjb2x1bW4gbmFtZXMgb3IgdXNlIGhlbHBlciBmdW5jdGlvbnMgdG8gc2VsZWN0IGNvbHVtbnMgKHNlZSB0aGUgYHNlbGVjdCgpYCBmdW5jdGlvbiBvbiB0aGUgYGRwbHlyYCBjaGVhdHNoZWV0IGZvciBtb3JlIGRldGFpbCBvciBzZWFyY2ggZm9yIGB0aWR5LXNlbGVjdGAgaW4gIHRoZSBIZWxwIHRhYikuCgoqKiEhQ0FVVElPTiEhOioqIEl0IGlzIGVhc3kgdG8gbWFrZSBhIG1pc3Rha2Ugb24gdGhlIGBpZF9jb2xzYCBhcmd1bWVudC4gRm9yIGV4YW1wbGUsIGluIHRoZSBjb2RlIGJlbG93LCBJIGZvcmdvdCB0byBpbmNsdWRlIGBvYnNgLiBUaGUgcmVzdWx0IGlzIHNvbWV0aGluZyB3ZWlyZCBhbmQgdW5leHBlY3RlZCB3aXRoIG9ubHkgMzUgcm93cy4KCmBgYHtyfQpwZW5ndWluc19mYWtlICU+JSAKICBwaXZvdF93aWRlcihpZF9jb2xzID0gc3BlY2llczp5ZWFyLAogICAgICAgICAgICAgIG5hbWVzX2Zyb20gPSBtZWFzdXJlbWVudCwKICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IHZhbHVlKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQpkZWNvcmF0ZV9jaHVuaygicGl2b3Qtd2lkZXItZXgxIikgJT4lIAogIGZsYWlyKCJuYW1lc19mcm9tID0iKSAKYGBgCgpUaGUgYG5hbWVzX2Zyb21gIGFyZ3VtZW50IGlzIHRoZSB2YXJpYWJsZSAob3IgdmFyaWFibGVzKSB0aGF0IGNvbnRhaW4gdmFsdWVzIHRoYXQgeW91IHdhbnQgdG8gYmUgdHVybmVkIGludG8gdGhlaXIgb3duIGNvbHVtbnMuIFRoaXMgaXMgKm5vdCogaW4gcXVvdGVzLgoKYGBge3IsIGVjaG89RkFMU0V9CmRlY29yYXRlX2NodW5rKCJwaXZvdC13aWRlci1leDEiKSAlPiUgCiAgZmxhaXIoInZhbHVlc19mcm9tID0iKSAKYGBgCgpUaGUgYHZhbHVlc19mcm9tYCBhcmd1bWVudCBpcyB0aGUgdmFyaWFibGUgKG9yIHZhcmlhYmxlcykgdGhhdCBzaG91bGQgYmUgdmFsdWVzIG9mIHRoZSBuZXcgdmFyaWFibGVzLgoKIyMjIERlbW8gdmlkZW8KCk5vdyB0aGF0IHlvdSd2ZSBsZWFybmVkIHRoZSBiYXNpY3Mgb2YgcGl2b3RpbmcsIHdhdGNoIHRoZSB2aWRlbyBiZWxvdyB0aGF0IHdpbGwgd2FsayB5b3UgdGhyb3VnaCBzb21lIGNvZGluZyBleGFtcGxlcyBhbmQgZG93bmxvYWQgdGhlIFIgTWFya2Rvd24gZmlsZXMgdG8gZm9sbG93IGFsb25nLiBUaGlzIGlzIHRoZSBzYW1lIGZpbGUgeW91IHdpbGwgdXNlIGZvciB0aGUgb3RoZXIgdG9waWNzLgoKPGlmcmFtZSB3aWR0aD0iNTYwIiBoZWlnaHQ9IjMxNSIgc3JjPSJodHRwczovL3d3dy55b3V0dWJlLmNvbS9lbWJlZC9rM1NaOGtlaWJ1USIgZnJhbWVib3JkZXI9IjAiIGFsbG93PSJhY2NlbGVyb21ldGVyOyBhdXRvcGxheTsgZW5jcnlwdGVkLW1lZGlhOyBneXJvc2NvcGU7IHBpY3R1cmUtaW4tcGljdHVyZSIgYWxsb3dmdWxsc2NyZWVuPjwvaWZyYW1lPgoKW1ZvaWNldGhyZWFkOiBwaXZvdGluZyBkZW1vXShodHRwczovL3ZvaWNldGhyZWFkLmNvbS9zaGFyZS8xNTM3NTI4Ny8pCgpgYGB7ciwgZWNobz1GQUxTRX0KZG93bmxvYWRfZmlsZSgKICBwYXRoID0gIjAzX3dyYW5nbGluZ19kZW1vX25vX2NvZGUuUm1kIiwKICBidXR0b25fbGFiZWwgPSAiRG93bmxvYWQgd3JhbmdsaW5nIGRlbW8gZmlsZSAod2l0aG91dCBjb2RlKSIsCiAgYnV0dG9uX3R5cGUgPSAid2FybmluZyIsCiAgaGFzX2ljb24gPSBUUlVFLAogIGljb24gPSAiZmEgZmEtc2F2ZSIsCiAgc2VsZl9jb250YWluZWQgPSBGQUxTRQopCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmRvd25sb2FkX2ZpbGUoCiAgcGF0aCA9ICIwM193cmFuZ2xpbmdfZGVtby5SbWQiLAogIGJ1dHRvbl9sYWJlbCA9ICJEb3dubG9hZCB3cmFuZ2xpbmcgZGVtbyBmaWxlICh3aXRoIGNvZGUpIiwKICBidXR0b25fdHlwZSA9ICJpbmZvIiwKICBoYXNfaWNvbiA9IFRSVUUsCiAgaWNvbiA9ICJmYSBmYS1zYXZlIiwKICBzZWxmX2NvbnRhaW5lZCA9IEZBTFNFCikKYGBgCgojIyMgUmVzb3VyY2VzCgoqIFtTbGlkZXNdKGh0dHBzOi8vc3BlYWtlcmRlY2suY29tL3l1dGFubmloaWxhdGlvbi9hLWdyYXBoaWNhbC1pbnRyb2R1Y3Rpb24tdG8tdGlkeXJzLXBpdm90LXN0YXIpIGZyb20gSGlyb2FraSBZdXRhbmkgIAoqIFtSNERTIENoYXB0ZXIgMTIuM10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei90aWR5LWRhdGEuaHRtbCkKCiMjIyBZb3VyIHR1cm4hCgojIyMjIEV4ZXJjaXNlIDE6IGBwaXZvdF93aWRlcigpYAoKU3VtbWFyaXplIHRoZSBgZ2FyZGVuX2hhcnZlc3RgIGRhdGEgdG8gZmluZCB0aGUgdG90YWwgaGFydmVzdCB3ZWlnaHQgaW4gcG91bmRzIGZvciBlYWNoIHZlZ2V0YWJsZSBhbmQgZGF5IG9mIHdlZWsuIERpc3BsYXkgdGhlIHJlc3VsdHMgc28gdGhhdCB0aGUgdmVnZXRhYmxlcyBhcmUgcm93cyBidXQgdGhlIGRheXMgb2YgdGhlIHdlZWsgYXJlIGNvbHVtbnMuCgpgYGB7cn0KCmBgYAoKIyMjIyBFeGVyY2lzZSAyOiBgcGl2b3RfbG9uZ2VyKClgCgpVc2UgdGhlIGBiaWxsYm9hcmRgIGRhdGFzZXQgKHNlYXJjaCBmb3IgaXQgaW4gaGVscCBvciB0eXBlIGA/YmlsbGJvYXJkYCBpbiB0aGUgY29uc29sZSkuIEl0IGhhcyByYW5raW5ncyBvZiBzb25ncyBmb3IgZWFjaCB3ZWVrIHRoZXkgZW50ZXJlZCB0aGUgQmlsbGJvYXJkIFRvcCAxMDAuIFRoZSB3ZWVrcyBhcmUgY29sdW1uIG5hbWVzLiBVc2UgYHBpdm90X2xvbmdlcigpYCB0byBtYWtlIHdlZWtzIGEgc2luZ2xlIGNvbHVtbiBhbmQgcmVtb3ZlIHJvd3Mgd2l0aCBtaXNzaW5nIHZhbHVlcyBmb3IgcmFuayAoSElOVDogdXNlIGB2YWx1ZXNfZHJvcF9uYWAgYXJndW1lbnQgaW4gYHBpdm90X2xvbmdlcigpYCkuCgpgYGB7cn0KCmBgYAoKCiMjIEpvaW5pbmcgZGF0YXNldHMKCldoZW4gYW5hbHl6aW5nIGRhdGEsIGl0IGlzIGNvbW1vbiB0byBuZWVkIHRvIGNvbWJpbmUgdG9nZXRoZXIgZGF0YXNldHMgdGhhdCBhcmUgcmVsYXRlZC4gVGhlIGBqb2luYCB2ZXJicyB3aWxsIGdpdmUgdXMgYSB3YXkgdG8gZG8gdGhpcy4gRm9yIGFsbCBqb2lucyB3ZSBtdXN0IGVzdGFibGlzaCBhIGNvcnJlc3BvbmRhbmNlIG9yIG1hdGNoIGJldHdlZW4gZWFjaCBjYXNlIGluIHRoZSBsZWZ0IHRhYmxlIGFuZCB6ZXJvIG9yIG1vcmUgY2FzZXMgaW4gdGhlIHJpZ2h0IHRhYmxlLgoKQSBtYXRjaCBiZXR3ZWVuIGEgY2FzZSBpbiB0aGUgKmxlZnQqIHRhYmxlIGFuZCBhIGNhc2UgaW4gdGhlICpyaWdodCogdGFibGUgaXMgbWFkZSBiYXNlZCBvbiB0aGUgdmFsdWVzIGluIHBhaXJzIG9mIGNvcnJlc3BvbmRpbmcgdmFyaWFibGVzLgoKKiAqKllvdSoqIHNwZWNpZnkgd2hpY2ggcGFpcnMgdG8gdXNlLgoqIEEgcGFpciBpcyBhIHZhcmlhYmxlIGZyb20gdGhlIGxlZnQgdGFibGUgYW5kIGEgdmFyaWFibGUgZnJvbSB0aGUgcmlnaHQgdGFibGUgb3IgYSBzZXQgb2YgdmFyaWFibGVzIGZyb20gdGhlIGxlZnQgYW5kIHJpZ2h0IHRhYmxlLiAKKiBDYXNlcyBtdXN0IGhhdmUgKmV4YWN0bHkgZXF1YWwqIHZhbHVlcyBpbiB0aGUgcGFpciBmb3IgYSBtYXRjaCB0byBiZSBtYWRlLgoKV2hlbiB3ZSBqb2luIGRhdGFzZXRzLCB0aGUgZ2VuZXJhbCBmb3JtYXQgaXMgCgpgYGB7ciwgZXZhbD1GQUxTRX0KbGVmdF9kYXRhc2V0ICU+JSAKICA8Sk9JTj4ocmlnaHRfZGF0YXNldCwgCiAgICAgICAgIGJ5PTxIT1cgVE8gSk9JTj4pCmBgYAoKd2hlcmUgYGxlZnRfZGF0YXNldGAgYW5kIGByaWdodF9kYXRhc2V0YCBhcmUgZGF0YXNldHMsIGA8Sk9JTj5gIGlzIHRoZSBzcGVjaWZpYyB0eXBlIG9mIGpvaW4sIGFuZCBgPEhPVyBUTyBKT0lOPmAgZ2l2ZXMgZGV0YWlsZWQgaW5mb3JtYXRpb24gZm9yIGhvdyB0byBkbyBpdC4gCgpUaGUgYGJ5YCBhcmd1bWVudCB0ZWxscyBpdCBob3cgdG8gam9pbiB0aGUgdHdvIGRhdGFzZXRzIHRvZ2V0aGVyLCBzcGVjaWZpY2FsbHkgd2hpY2ggdmFyaWFibGVzIGl0IHNob3VsZCBtYXRjaC4gSWYgdGhlIHZhcmlhYmxlcyBoYXZlIHRoZSBzYW1lIG5hbWVzLCB3ZSBvbmx5IG5lZWQgdG8gd3JpdGUgdGhlIG5hbWUgb2YgdGhhdCB2YXJpYWJsZSwgaW4gcXVvdGVzOiBgYnkgPSAidmFyaWFibGVfbmFtZSJgLiAKCklmIHRoZSB0d28gdmFyaWFibGVzIHRvIG1hdGNoIGhhdmUgZGlmZmVyZW50IG5hbWVzIGluIHRoZSB0d28gZGF0YXNldHMsIHdlIGNhbiB3cml0ZSBgYnk9YygibmFtZTEiPSJuYW1lMiIpYCwgd2hlcmUgYG5hbWUxYCBpcyB0aGUgdmFyaWFibGUgaW4gdGhlIGxlZnQgZGF0YXNldCB0byBiZSBtYXRjaGVkIHRvIHRoZSBgbmFtZTJgIHZhcmlhYmxlIGluIHRoZSByaWdodCBkYXRhc2V0LiAKCldlIGNhbiBhbHNvIG1hdGNoIG9uIG11bHRpcGxlIHZhcmlhYmxlcyB1c2luZyBgYnk9YygibmFtZTEiPSJuYW1lMiIsICJuYW1lMWEiID0gIm5hbWUyYSIpYCwgd2hlcmUgdGhlIG5hbWVzIHRvIHRoZSBsZWZ0IG9mIHRoZSBlcXVhbHMgYXJlIHZhcmlhYmxlcyBmcm9tIHRoZSBsZWZ0IGRhdGFzZXQgYW5kIHRob3NlIG9uIHRoZSByaWdodCBvZiB0aGUgZXF1YWxzIGFyZSBmcm9tIHRoZSByaWdodCBkYXRhc2V0LiAgCgpJZiB0aGUgYGJ5PWAgaXMgb21pdHRlZCBmcm9tIGEgam9pbiwgdGhlbiBgUmAgd2lsbCBwZXJmb3JtIGEgKm5hdHVyYWwgam9pbiosIHdoaWNoIG1hdGNoZXMgdGhlIHR3byBkYXRhc2V0cyBieSBhbGwgdmFyaWFibGVzIHRoZXkgaGF2ZSBpbiBjb21tb24uIEl0IGlzIGdvb2QgcHJhY3RpY2UgdG8gYWx3YXlzIGluY2x1ZGUgdGhlIGBieT1gLgoKTGV0J3MgZGlzY3VzcyB0aGUgZGlmZmVyZW50IHR5cGVzIG9mIGpvaW5zLgoKIyMjIE11dGF0aW5nIGpvaW5zCgpUaGUgZmlyc3QgY2xhc3Mgb2Ygam9pbnMgYXJlIG11dGF0aW5nIGpvaW5zLCB3aGljaCBhZGQgbmV3IHZhcmlhYmxlcyAoY29sdW1ucykgdG8gdGhlIGxlZnQgZGF0YSB0YWJsZSBmcm9tIG1hdGNoaW5nIG9ic2VydmF0aW9ucyBpbiB0aGUgcmlnaHQgdGFibGUuCgpUaGUgbWFpbiBkaWZmZXJlbmNlIGluIHRoZSB0aHJlZSBtdXRhdGluZyBqb2luIG9wdGlvbnMgaW4gdGhpcyBjbGFzcyBpcyBob3cgdGhleSBhbnN3ZXIgdGhlIGZvbGxvd2luZyBxdWVzdGlvbnM6CgoxLiBXaGF0IGhhcHBlbnMgd2hlbiBhIGNhc2UgaW4gdGhlIHJpZ2h0IHRhYmxlIGhhcyBubyBtYXRjaGVzIGluIHRoZSBsZWZ0IHRhYmxlPwoyLiBXaGF0IGhhcHBlbnMgd2hlbiBhIGNhc2UgaW4gdGhlIGxlZnQgdGFibGUgaGFzIG5vIG1hdGNoZXMgaW4gdGhlIHJpZ2h0IHRhYmxlPwoKVGhyZWUgbXV0YXRpbmcgam9pbiBmdW5jdGlvbnM6CgoqKmBsZWZ0X2pvaW4oKWAqKjogdGhlIG91dHB1dCBoYXMgYWxsIGNhc2VzIGZyb20gdGhlIGxlZnQsIHJlZ2FyZGxlc3MgaWYgdGhlcmUgaXMgYSBtYXRjaCBpbiB0aGUgcmlnaHQsIGJ1dCBkaXNjYXJkcyBhbnkgY2FzZXMgaW4gdGhlIHJpZ2h0IHRoYXQgZG8gbm90IGhhdmUgYSBtYXRjaCBpbiB0aGUgbGVmdC4gKFRoZXJlIGlzIGFsc28gYSAqKmByaWdodF9qb2luKClgKiogZnVuY3Rpb24gd2hpY2ggd2hpY2ggZG9lcyB0aGUgb3Bwb3NpdGUuKQoKIVtJbWFnZSBjcmVkaXQ6IFdpY2toYW0sIFIgZm9yIERhdGEgU2NpZW5jZV0oLi4vLi4vaW1hZ2VzL2xlZnRfcmlnaHRfam9pbi5wbmcpCgohW0NyZWRpdDogR2FycmljayBBZGVuLUJ1aWUg4oCTIEBncnJyY2tdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9nYWRlbmJ1aWUvdGlkeWV4cGxhaW4vbWFzdGVyL2ltYWdlcy9sZWZ0LWpvaW4uZ2lmKXt3aWR0aD0zMDB9CgoqKmBpbm5lcl9qb2luKClgKio6IHRoZSBvdXRwdXQgaGFzIG9ubHkgdGhlIGNhc2VzIGZyb20gdGhlIGxlZnQgd2l0aCBhIG1hdGNoIGluIHRoZSByaWdodC4KCiFbSW1hZ2UgY3JlZGl0OiBXaWNraGFtLCBSIGZvciBEYXRhIFNjaWVuY2VdKC4uLy4uL2ltYWdlcy9pbm5lcl9qb2luLnBuZykKCiFbQ3JlZGl0OiBHYXJyaWNrIEFkZW4tQnVpZSDigJMgQGdycnJja10oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2dhZGVuYnVpZS90aWR5ZXhwbGFpbi9tYXN0ZXIvaW1hZ2VzL2lubmVyLWpvaW4uZ2lmKXt3aWR0aD0zMDB9CgoqKmBmdWxsX2pvaW4oKWAqKjogdGhlIG91dHB1dCBoYXMgYWxsIGNhc2VzIGZyb20gdGhlIGxlZnQgYW5kIHRoZSByaWdodC4gVGhpcyBpcyBsZXNzIGNvbW1vbiB0aGFuIHRoZSBmaXJzdCB0d28gam9pbiBvcGVyYXRvcnMuCgohW0ltYWdlIGNyZWRpdDogV2lja2hhbSwgUiBmb3IgRGF0YSBTY2llbmNlXSguLi8uLi9pbWFnZXMvZnVsbF9qb2luLnBuZykKCiFbQ3JlZGl0OiBHYXJyaWNrIEFkZW4tQnVpZSDigJMgQGdycnJja10oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2dhZGVuYnVpZS90aWR5ZXhwbGFpbi9tYXN0ZXIvaW1hZ2VzL2Z1bGwtam9pbi5naWYpe3dpZHRoPTMwMH0KCldoZW4gdGhlcmUgYXJlIG11bHRpcGxlIG1hdGNoZXMgaW4gdGhlIHJpZ2h0IHRhYmxlIGZvciBhIHBhcnRpY3VsYXIgY2FzZSBpbiB0aGUgbGVmdCB0YWJsZSwgYWxsIHRocmVlIG9mIHRoZXNlIG11dGF0aW5nIGpvaW4gb3BlcmF0b3JzIHByb2R1Y2UgYSBzZXBhcmF0ZSBjYXNlIGluIHRoZSBuZXcgdGFibGUgZm9yIGVhY2ggb2YgdGhlIG1hdGNoZXMgZnJvbSB0aGUgcmlnaHQuCgojIyMjIEV4YW1wbGVzCgpGaXJzdCwgY3JlYXRlIHR3byBzbWFsbCBkYXRhc2V0czoKCmBgYHtyfQpnZW5lcmFsX2luZm8gPC0gdGliYmxlKAogIHBlcnNvbl9pZCA9IGMoMSwgMiwgMywgNCwgNSwgNiwgNywgOCwgOSwgMTApLAogIGFnZSA9IGMoMzQsIDU0LCA2NywgOTIsIDIxLCAzMiwgMTgsIDQ1LCAzNCwgNTUpLAogIHJlbnRfb3Jfb3duID0gYygicmVudCIsICJyZW50IiwgIm93biIsICJyZW50IiwgInJlbnQiLCAib3duIiwgInJlbnQiLCAib3duIiwgIm93biIsICJvd24iKQopCgpnZW5lcmFsX2luZm8KCnBldF9pbmZvIDwtIHRpYmJsZSgKICBwZXJzb25faWQgPSBjKDIsMyw1LDcsOCwxMCwxMSwxMiwxMywxNCwxNSksCiAgcGV0X293bmVyID0gYygieWVzIiwgIm5vIiwgIm5vIiwgInllcyIsICJ5ZXMiLCAibm8iLCAibm8iLCAibm8iLCAieWVzIiwgIm5vIiwgIm5vIikKKQoKcGV0X2luZm8KYGBgCgoxLiBTdGFydCB3aXRoIGBnZW5lcmFsX2luZm9gIGFuZCBgbGVmdF9qb2luKClgIHRoZSBgcGV0X2luZm9gIGJ5IGBwZXJzb25faWRgOgoKYGBge3J9CmdlbmVyYWxfaW5mbyAlPiUgCiAgbGVmdF9qb2luKHBldF9pbmZvLCAKICAgICAgICAgICAgYnkgPSAicGVyc29uX2lkIikKYGBgCgpUaGUgcmVzdWx0aW5nIHRhYmxlIGhhcyAxMCByb3dzIG9mIGRhdGEgLSB0aGUgMTAgb2JzZXJ2YXRpb25zIGZyb20gYGdlbmVyYWxfaW5mb2AuIFRoZXJlIGFyZSBtaXNzaW5nIHZhbHVlcyBmb3IgYHBldF9vd25lcmAgZm9yIGBwZXJzb25faWRgJ3MgdGhhdCB3ZXJlIGluIHRoZSBgZ2VuZXJhbF9pbmZvYCB0YWJsZSBhbmQgbm90IHRoZSBgcGV0X2luZm9gIHRhYmxlLgoKKio/Pz8qKiBIb3cgd291bGQgdGhlIHJlc3VsdHMgY2hhbmdlIGlmIGEgYHJpZ2h0X2pvaW4oKWAgd2FzIHVzZWQgaW4gdGhlIGNvZGUgYWJvdmUgcmF0aGVyIHRoYW4gYSBgbGVmdF9qb2luKClgPwoKMi4gU3RhcnQgd2l0aCBgZ2VuZXJhbF9pbmZvYCBhbmQgYGlubmVyX2pvaW4oKWAgdGhlIGBwZXRfaW5mb2AgYnkgYHBlcnNvbl9pZGA6CgpgYGB7cn0KZ2VuZXJhbF9pbmZvICU+JSAKICBpbm5lcl9qb2luKHBldF9pbmZvLCAKICAgICAgICAgICAgIGJ5ID0gInBlcnNvbl9pZCIpCmBgYAoKVGhlIHJlc3VsdGluZyB0YWJsZSBpcyBvbmx5IDYgcm93cyB3aXRoIHRoZSBvYnNlcnZhdGlvbnMgdGhhdCBhcmUgaW4gYm90aCBgZ2VuZXJhbF9pbmZvYCBhbmQgYHBldF9pbmZvYC4KCjMuIFN0YXJ0IHdpdGggYGdlbmVyYWxfaW5mb2AgYW5kIGBmdWxsX2pvaW4oKWAgdGhlIGBwZXRfaW5mb2AgYnkgYHBlcnNvbl9pZGA6CgpgYGB7cn0KZ2VuZXJhbF9pbmZvICU+JSAKICBmdWxsX2pvaW4ocGV0X2luZm8sIAogICAgICAgICAgICBieSA9ICJwZXJzb25faWQiKQpgYGAKClRoZSByZXN1bHRpbmcgdGFibGUgaGFzIDE1IHJvd3MuIFRoZXJlIGFyZSBtaXNzaW5nIHZhbHVlcyBmb3IgYHBldF9vd25lcmAgZm9yIGBwZXJzb25faWRgJ3MgdGhhdCB3ZXJlIGluIHRoZSBgZ2VuZXJhbF9pbmZvYCB0YWJsZSBhbmQgbm90IHRoZSBgcGV0X2luZm9gIHRhYmxlLCBhbmQgdGhlcmUgYXJlIG1pc3NpbmcgdmFsdWVzIGZvciBgYWdlYCBhbmQgYHJlbnRgIGZvciBmb3IgYHBlcnNvbl9pZGAncyB0aGF0IHdlcmUgaW4gdGhlIGBwZXRfaW5mb2AgdGFibGUgYW5kIG5vdCB0aGUgYGdlbmVyYWxfaW5mb2AgdGFibGUuCgojIyMgRmlsdGVyaW5nIGpvaW5zCgpUaGUgc2Vjb25kIGNsYXNzIG9mIGpvaW5zIGFyZSBmaWx0ZXJpbmcgam9pbnMsIHdoaWNoIHNlbGVjdCBzcGVjaWZpYyBjYXNlcyBmcm9tIHRoZSBsZWZ0IHRhYmxlIGJhc2VkIG9uIHdoZXRoZXIgdGhleSBtYXRjaCBhbiBvYnNlcnZhdGlvbiBpbiB0aGUgcmlnaHQgdGFibGUuCgoqKmBzZW1pX2pvaW4oKWAqKjogZGlzY2FyZHMgYW55IGNhc2VzIGluIHRoZSBsZWZ0IHRhYmxlIHRoYXQgZG8gbm90IGhhdmUgYSBtYXRjaCBpbiB0aGUgcmlnaHQgdGFibGUuIElmIHRoZXJlIGFyZSBtdWx0aXBsZSBtYXRjaGVzIG9mIHJpZ2h0IGNhc2VzIHRvIGEgbGVmdCBjYXNlLCBpdCBrZWVwcyBqdXN0IG9uZSBjb3B5IG9mIHRoZSBsZWZ0IGNhc2UuCgohW0ltYWdlIGNyZWRpdDogV2lja2hhbSwgUiBmb3IgRGF0YSBTY2llbmNlXSguLi8uLi9pbWFnZXMvc2VtaV9qb2luLnBuZykKCiFbQ3JlZGl0OiBHYXJyaWNrIEFkZW4tQnVpZSDigJMgQGdycnJja10oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2dhZGVuYnVpZS90aWR5ZXhwbGFpbi9tYXN0ZXIvaW1hZ2VzL3NlbWktam9pbi5naWYpe3dpZHRoPTMwMH0KCioqYGFudGlfam9pbigpYCoqOiBkaXNjYXJkcyBhbnkgY2FzZXMgaW4gdGhlIGxlZnQgdGFibGUgdGhhdCBoYXZlIGEgbWF0Y2ggaW4gdGhlIHJpZ2h0IHRhYmxlLgoKIVtJbWFnZSBjcmVkaXQ6IFdpY2toYW0sIFIgZm9yIERhdGEgU2NpZW5jZV0oLi4vLi4vaW1hZ2VzL2FudGlfam9pbi5wbmcpCgohW0NyZWRpdDogR2FycmljayBBZGVuLUJ1aWUg4oCTIEBncnJyY2tdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9nYWRlbmJ1aWUvdGlkeWV4cGxhaW4vbWFzdGVyL2ltYWdlcy9hbnRpLWpvaW4uZ2lmKXt3aWR0aD0zMDB9CgojIyMjIEV4YW1wbGUKClRoZXNlIHVzZSB0aGUgZXhhbXBsZSBkYXRhIGZyb20gdGhlIHByZXZpb3VzIHNlY3Rpb24KCkEgYHNlbWlfam9pbigpYCBpcyB1c2VkIHRvIGZpbmQgdGhlIGFnZSBhbmQgcmVudGFsIHN0YXR1cyAoaW5mb3JtYXRpb24gaW4gdGhlIGBnZW5lcmFsX2luZm9gIHRhYmxlKSBmb3IgcGVvcGxlIHdobyBhcmUgcGV0IG93bmVyczoKCmBgYHtyfQpnZW5lcmFsX2luZm8gJT4lIAogIHNlbWlfam9pbihwZXRfaW5mbyAlPiUgZmlsdGVyKHBldF9vd25lciA9PSAieWVzIiksIAogICAgICAgICAgICBieSA9ICJwZXJzb25faWQiKSAKYGBgCgpUaGlzIHJldHVybnMgYSB0YWJsZSB3aXRoIDMgcm93cy4gU2luY2UgdGhlc2UgYXJlIHNtYWxsIHRhYmxlcywgeW91IHNob3VsZCBnbyB2ZXJpZnkgdGhpcyBieSBoYW5kLiBBbHNvIG5vdGljZSBJIGRpZCBub3QgcHJlc3MgZW50ZXIgYWZ0ZXIgdGhlIGAlPiVgIGluc2lkZSB0aGUgYHNlbWlfam9pbigpYC4gVGhpcyBpcyBvbmUgY2FzZSB3aGVyZSB3ZSBsZWF2ZSBpdCBvbiB0aGUgc2FtZSBsaW5lIHRvIG1ha2UgaXQgbW9yZSByZWFkYWJsZS4KClVzZSBhbiBgYW50aV9qb2luKClgIHRvIGZpbmQgdGhlIGFnZSBhbmQgcmVudGFsIHN0YXR1cyAoaW5mb3JtYXRpb24gaW4gdGhlIGBnZW5lcmFsX2luZm9gIHRhYmxlKSBmb3IgcGVvcGxlIHdobyBhcmUgbm90IGNvbmZpcm1lZCBwZXQgb3duZXJzIChub3RpY2UgdGhpcyBpbmNsdWRlcyB1bmtub3ducyk6CgpgYGB7cn0KZ2VuZXJhbF9pbmZvICU+JSAKICBhbnRpX2pvaW4ocGV0X2luZm8gJT4lIGZpbHRlcihwZXRfb3duZXIgPT0gInllcyIpLAogICAgICAgICAgICBieSA9ICJwZXJzb25faWQiKQpgYGAKCiMjIyBEZW1vIHZpZGVvCgpOb3cgd2F0Y2ggdGhlIHZpZGVvIGJlbG93IHRoYXQgd2lsbCB3YWxrIHlvdSB0aHJvdWdoIHNvbWUgbW9yZSBhZHZhbmNlZCBjb2RpbmcgZXhhbXBsZXMgKHBsdXMgYSBjYW1lbyBieSBteSBkYXVnaHRlciwgSGFkbGV5KS4gVGhlIGRvd25sb2FkYWJsZSBSIE1hcmtkb3duIGZpbGVzIHRvIGZvbGxvdyBhbG9uZyBhcmUgZm91bmQgYmVsb3cgdGhlIHBpdm90aW5nIHZpZGVvLgoKPGlmcmFtZSB3aWR0aD0iNTYwIiBoZWlnaHQ9IjMxNSIgc3JjPSJodHRwczovL3d3dy55b3V0dWJlLmNvbS9lbWJlZC9NSkRIUnR3WmhvTSIgZnJhbWVib3JkZXI9IjAiIGFsbG93PSJhY2NlbGVyb21ldGVyOyBhdXRvcGxheTsgZW5jcnlwdGVkLW1lZGlhOyBneXJvc2NvcGU7IHBpY3R1cmUtaW4tcGljdHVyZSIgYWxsb3dmdWxsc2NyZWVuPjwvaWZyYW1lPgoKW1ZvaWNldGhyZWFkOiBqb2luaW5nIGRlbW9dKGh0dHBzOi8vdm9pY2V0aHJlYWQuY29tL3NoYXJlLzE1MzkyMzUxLykKCgojIyMgUmVzb3VyY2VzCgoqIFtBbmltYXRlZCBHSUZzXShodHRwczovL2dpdGh1Yi5jb20vZ2FkZW5idWllL3RpZHlleHBsYWluKSAgCiogW1I0RFMgQ2hhcHRlciAxM10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9yZWxhdGlvbmFsLWRhdGEuaHRtbCkgIAoqIFtKb2luIENoZWF0c2hlZXRdKGh0dHBzOi8vc3RhdDU0NS5jb20vam9pbi1jaGVhdHNoZWV0Lmh0bWwpIGJ5IEplbm55IEJyeWFuCgoKIyMjIFlvdXIgdHVybiEKCiMjIyMgRXhlcmNpc2UgMTogbXV0YXRpbmcgam9pbgoKU3VtbWFyaXplIHRoZSBgZ2FyZGVuX2hhcnZlc3RgIGRhdGEgdG8gZmluZCB0aGUgdG90YWwgaGFydmVzdCBpbiBwb3VuZCBmb3IgZWFjaCB2ZWdldGFibGUgdmFyaWV0eSBhbmQgdGhlbiB0cnkgYWRkaW5nIHRoZSBwbG90IGZyb20gdGhlIGBnYXJkZW5fcGxhbnRpbmdgIHRhYmxlLiBUaGlzIHdpbGwgbm90IHR1cm4gb3V0IHBlcmZlY3RseS4gV2hhdCBpcyB0aGUgcHJvYmxlbT8gSG93IG1pZ2h0IHlvdSBmaXggaXQ/CgpgYGB7cn0KCmBgYAoKIyMjIyBFeGVyY2lzZSAyOiBtdXRhdGluZyBqb2luCgpJIHdvdWxkIGxpa2UgdG8gdW5kZXJzdGFuZCBob3cgbXVjaCBtb25leSBJICJzYXZlZCIgYnkgZ2FyZGVuaW5nLCBmb3IgZWFjaCB2ZWdldGFibGUgdHlwZS4gRGVzY3JpYmUgaG93IEkgY291bGQgdXNlIHRoZSBgZ2FyZGVuX2hhcnZlc3RgIGFuZCBgZ2FyZGVuX3NwZW5kaW5nYCBkYXRhc2V0cywgYWxvbmcgd2l0aCBkYXRhIGZyb20gc29tZXdoZXJlIGxpa2UgW3RoaXNdKGh0dHBzOi8vcHJvZHVjdHMud2hvbGVmb29kc21hcmtldC5jb20vc2VhcmNoP3NvcnQ9cmVsZXZhbmNlJnN0b3JlPTEwNTQyKSB0byBhbnN3ZXIgdGhpcyBxdWVzdGlvbi4gWW91IGNhbiBhbnN3ZXIgdGhpcyBpbiB3b3JkcywgcmVmZXJlbmNpbmcgdmFyaW91cyBqb2luIGZ1bmN0aW9ucy4gWW91IGRvbid0IG5lZWQgUiBjb2RlIGJ1dCBjb3VsZCBwcm92aWRlIHNvbWUgaWYgaXQncyBoZWxwZnVsLgoKIyMjIyBFeGVyY2lzZSAzOiBmaWx0ZXJpbmcgam9pbgoKRXhjbHVkZSB0aGUgdmVnZXRhYmxlIHZhcmlldGllcyBmcm9tIGBnYXJkZW5faGFydmVzdGAgdGhhdCBhcmUgaW4gcGxvdHMgTSBhbmQgSC4gCgpgYGB7cn0KCmBgYAoKCiMjIFVzaW5nIGBmb3JjYXRzYCBmdW5jdGlvbnMgd2l0aCBmYWN0b3JzCgpSIGNhbGxzIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBmYWN0b3JzLiBUaGV5IGFyZSBzbGlnaHRseSBkaWZmZXJlbnQgZnJvbSBjaGFyYWN0ZXIgdmFyaWFibGVzLCBidXQgSSB3aWxsIHNraXAgdGFsa2luZyBhYm91dCB0aGlzIGRldGFpbCByaWdodCBub3cuIFRoZSB1bmlxdWUgdmFsdWVzIHRoYXQgZmFjdG9yIHZhcmlhYmxlcyB0YWtlIGFyZSBjYWxsZWQgbGV2ZWxzLiAKClRoZXJlIGFyZSBtYW55IHRpbWVzIHdlIG1pZ2h0IHdhbnQgdG8gbW9kaWZ5IGZhY3RvcnMuIEJlbG93IEkgbGlzdCB0aGUgZnVuY3Rpb25zIEkgd2lsbCBkZW1vbnN0cmF0ZSBpbiB0aGUgdmlkZW8uIFRoZXNlIGFyZSB0aGUgb25lcyBJIHVzZSBtb3N0IG9mdGVuLCBidXQgdGhlcmUgYXJlIG1hbnkgb3RoZXIgdXNlZnVsIGZ1bmN0aW9ucy4gQ2hlY2sgb3V0IHRoZSBgZm9yY2F0c2AgY2hlYXRzaGVldCAoc2VlIGxpbmsgYmVsb3cpIHRvIHNlZSBhbGwgb2YgdGhlbS4gIEkgaGlnaGx5IHJlY29tbWVuZCBoYXZpbmcgaXQgb3BlbiB3aGVuIHlvdSB3b3JrIHRocm91Z2ggdGhlICJZb3VyIHR1cm4hIiBleGVyY2lzZXMuIAoKKipDaGFuZ2luZyB0aGUgb3JkZXIgb2YgbGV2ZWxzKiogIApgZmN0X3JlbGV2ZWwoKWA6IG1hbnVhbGx5IHJlb3JkZXIgbGV2ZWxzICAKYGZjdF9pbmZyZXEoKWA6IG9yZGVyIGxldmVscyBmcm9tIGhpZ2hlc3QgdG8gbG93ZXN0IGZyZXF1ZW5jeSAgCmBmY3RfcmVvcmRlcigpYDogcmVvcmRlciBsZXZlbHMgYnkgdmFsdWVzIG9mIGFub3RoZXIgdmFyaWFibGUgIApgZmN0X3JldigpYDogcmV2ZXJzZSB0aGUgY3VycmVudCBvcmRlcgoKKipDaGFuZ2luZyB0aGUgdmFsdWVzIG9mIGxldmVscyoqICAKYGZjdF9yZWNvZGUoKWA6IG1hbnVhbGx5IGNoYW5nZSBsZXZlbHMgIApgZmN0X2x1bXAoKWA6IGdyb3VwIHRvZ2V0aGVyIGxlYXN0IGNvbW1vbiBsZXZlbHMKCiMjIyBEZW1vIHZpZGVvCgpXYXRjaCB0aGUgdmlkZW8gYmVsb3cgdGhhdCBpbGx1c3RyYXRlcyB0aGVzZSBmdW5jdGlvbnMuIFRoZSBkb3dubG9hZGFibGUgUiBNYXJrZG93biBmaWxlcyB0byBmb2xsb3cgYWxvbmcgYXJlIGZvdW5kIGJlbG93IHRoZSBwaXZvdGluZyB2aWRlby4KCjxpZnJhbWUgd2lkdGg9IjU2MCIgaGVpZ2h0PSIzMTUiIHNyYz0iaHR0cHM6Ly93d3cueW91dHViZS5jb20vZW1iZWQvcnY0SXduTGNyOTgiIGZyYW1lYm9yZGVyPSIwIiBhbGxvdz0iYWNjZWxlcm9tZXRlcjsgYXV0b3BsYXk7IGVuY3J5cHRlZC1tZWRpYTsgZ3lyb3Njb3BlOyBwaWN0dXJlLWluLXBpY3R1cmUiIGFsbG93ZnVsbHNjcmVlbj48L2lmcmFtZT4KCltWb2ljZXRocmVhZDogd29ya2luZyB3aXRoIGZhY3RvcnNdKGh0dHBzOi8vdm9pY2V0aHJlYWQuY29tL3NoYXJlLzE1Mzk0MjYwLykKCiMjIyBSZXNvdXJjZXMKCiogW1I0RFMgQ2hhcHRlciAxNV0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9mYWN0b3JzLmh0bWwpICAKKiBbYGZvcmNhdHNgIENoZWF0c2hlZXRdKGh0dHBzOi8vcnN0dWRpby5jb20vcmVzb3VyY2VzL2NoZWF0c2hlZXRzLykgKHNlYXJjaCBmb3IgYGZvcmNhdHNgKQoKIyMjIFlvdXIgdHVybiEKCiMjIyMgRXhlcmNpc2UgMTogY2hhbmdpbmcgb3JkZXIgb2YgZmFjdG9ycwoKU3Vic2V0IHRoZSBkYXRhIHRvIHRvbWF0b2VzLiBSZW9yZGVyIHRoZSB0b21hdG8gdmFyaWV0aWVzIGZyb20gc21hbGxlc3QgdG8gbGFyZ2VzdCBmaXJzdCBoYXJ2ZXN0IGRhdGUuIENyZWF0ZSBhIGJhcnBsb3Qgb2YgdG90YWwgaGFydmVzdCBpbiBwb3VuZHMgZm9yIGVhY2ggdmFyaWV0eSwgaW4gdGhlIG5ldyBvcmRlci4KCmBgYHtyfQoKYGBgCgojIyMjIEV4ZXJjaXNlIDI6IGNoYW5naW5nIG9yZGVyIG9mIGZhY3RvcnMKClJldmVyc2UgdGhlIG9yZGVyIG9mIHRoZSB2YXJpZXRpZXMgaW4gdGhlIHByZXZpb3VzIHBsb3QuCgpgYGB7cn0KCmBgYAoKIyMjIyBFeGVyY2lzZSAzOiBjaGFuZ2luZyB0aGUgdmFsdWVzIG9mIGxldmVscwoKQ29tYmluZSB0aGUgdG9tYXRvIHZhcmlldGllcyBvZiB2b2x1bnRlZXJzIGFuZCBncmFwZSB0byBhIG5ldyBsZXZlbDogInNtYWxsIHRvbWF0b2VzIi4KCmBgYHtyfQoKYGBgCgojIyBIZWxwZnVsIGZ1bmN0aW9ucyB0byB3b3JrIHdpdGggc3RyaW5ncwoKU3RyaW5ncyBhcmUgZm91bmQgaW4gdGhlIGNlbGxzIG9mIGNoYXJhY3RlciB2YXJpYWJsZXMuIEZvciBleGFtcGxlLCBpbiB0aGUgZGF0YXNldCBJIGNyZWF0ZWQgYmVsb3csIGVhY2ggb2YgdGhlIG5hbWVzIGluIHRoZSBuYW1lIGNvbHVtbiBpcyBhIHN0cmluZy4KCmBgYHtyIGVjaG89RkFMU0V9CmZhbWlseSA8LSB0aWJibGUobmFtZSA9IGMoIkxpc2EgTGVuZHdheSIsICJDaHJpcyBGaXNjaGVyIiwgIkFkZWxpbmUgTGVuZHdheSIsICJIYWRsZXkgTGVuZHdheSIpLAogICAgICAgICAgICAgICAgIGFkdWx0ID0gYyhUUlVFLCBUUlVFLCBGQUxTRSwgRkFMU0UpKQpmYW1pbHkKYGBgCgpIZXJlIGFyZSB0aGUgZnVuY3Rpb25zIEkgd2lsbCBkaXNjdXNzIGluIHRoZSB2aWRlby4gVGhpcyBpcyBqdXN0IGEgc21hbGwgc2FtcGxlIG9mIHRoZSBmdW5jdGlvbnMgeW91IGNvdWxkIHVzZSB0byB3b3JrIHdpdGggc3RyaW5ncy4gTW9zdCBvZiB0aGVtIGFyZSBmcm9tIHRoZSBgc3RyaW5ncmAgcGFja2FnZSBhbmQgc3RhcnQgd2l0aCBgc3RyX2AuIFRoZXNlIGZ1bmN0aW9ucyBhbGwgcmVseSBvbiBzb21ldGhpbmcgY2FsbGVkIHJlZ3VsYXIgZXhwcmVzc2lvbnM6IHJlZ2V4IG9yIHJlZ2V4cCwgZm9yIHNob3J0LiAKCmBzZXBhcmF0ZSgpYDogc2VwYXJhdGVzIGEgY2hhcmFjdGVyIHZhcmlhYmxlIGludG8gbXVsdGlwbGUgdmFyaWFibGVzICAKYHN0cl9sZW5ndGgoKWA6IGdpdmVzIHRoZSBudW1iZXIgb2YgY2hhcmFjdGVycyBpbiB0aGUgc3RyaW5nIChpbmNsdWRlcyB3aGl0ZSBzcGFjZSwgcHVuY3R1YXRpb24sIGV0Yy4pICAKYHN0cl90b19sb3dlcigpYDogbWFrZXMgdGhlIGNoYXJhY3RlcnMgbG93ZXJjYXNlICAKYHN0cl9zdWIoKWA6IGV4dHJhY3QgcGFydCBvZiBhIHN0cmluZyAgCmBzdHJfZGV0ZWN0KClgOiByZXR1cm5zIFRSVUUvRkFMU0UgaWYgYSBwYXR0ZXJuIGlzIGluIHRoZSBzdHJpbmcKCiMjIyBEZW1vCgpXYXRjaCB0aGUgdmlkZW8gYmVsb3cgdGhhdCBpbGx1c3RyYXRlcyB0aGVzZSBmdW5jdGlvbnMuIFRoZSBkb3dubG9hZGFibGUgUiBNYXJrZG93biBmaWxlcyB0byBmb2xsb3cgYWxvbmcgYXJlIGZvdW5kIGJlbG93IHRoZSBwaXZvdGluZyB2aWRlby4KCjxpZnJhbWUgd2lkdGg9IjU2MCIgaGVpZ2h0PSIzMTUiIHNyYz0iaHR0cHM6Ly93d3cueW91dHViZS5jb20vZW1iZWQvX19wSl91OTRMWmciIGZyYW1lYm9yZGVyPSIwIiBhbGxvdz0iYWNjZWxlcm9tZXRlcjsgYXV0b3BsYXk7IGVuY3J5cHRlZC1tZWRpYTsgZ3lyb3Njb3BlOyBwaWN0dXJlLWluLXBpY3R1cmUiIGFsbG93ZnVsbHNjcmVlbj48L2lmcmFtZT4KCltWb2ljZXRocmVhZDogd29ya2luZyB3aXRoIHN0cmluZ3NdKGh0dHBzOi8vdm9pY2V0aHJlYWQuY29tL3NoYXJlLzE1Mzk4Mzg0LykKCiMjIyBSZXNvdXJjZXMKCiogW1I0RFMgQ2hhcHRlciAxNF0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9zdHJpbmdzLmh0bWwpICAKKiBbYHN0cmluZ3JgIENoZWF0c2hlZXRdKGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL2Jsb2IvbWFzdGVyL3N0cmluZ3MucGRmKQoKCiMjIyBZb3VyIHR1cm4hCgojIyMjIEV4ZXJjaXNlIDE6IHdvcmtpbmcgd2l0aCBzdHJpbmdzCgpJbiB0aGUgYGdhcmRlbl9oYXJ2ZXN0YCBkYXRhLCBjcmVhdGUgdHdvIG5ldyB2YXJpYWJsZXM6IG9uZSB0aGF0IG1ha2VzIHRoZSB2YXJpZXRpZXMgbG93ZXJjYXNlIGFuZCBhbm90aGVyIHRoYXQgZmluZHMgdGhlIGxlbmd0aCBvZiB0aGUgdmFyaWV0eSBuYW1lLgoKIyMjIyBFeGVyY2lzZSAyOiB3b3JraW5nIHdpdGggc3RyaW5ncwoKRmluZCBhbGwgdGhlIHZhcmlldGllcyB0aGF0IGhhdmUgImVyIiBvciAiYXIiIGluIHRoZWlyIG5hbWUuCgojIyBIaW50cyB0byBleGVyY2lzZXMKCiMjIyMgRXhlcmNpc2UgMTogYHBpdm90X3dpZGVyKClgCgpTdW1tYXJpemUgdGhlIGBnYXJkZW5faGFydmVzdGAgZGF0YSB0byBmaW5kIHRoZSB0b3RhbCBoYXJ2ZXN0IHdlaWdodCBpbiBwb3VuZHMgZm9yIGVhY2ggdmVnZXRhYmxlIGFuZCBkYXkgb2Ygd2Vlay4gRGlzcGxheSB0aGUgcmVzdWx0cyBzbyB0aGF0IHRoZSB2ZWdldGFibGVzIGFyZSByb3dzIGJ1dCB0aGUgZGF5cyBvZiB0aGUgd2VlayBhcmUgY29sdW1ucy4KCmBgYHtyLCBldmFsPUZBTFNFfQpnYXJkZW5faGFydmVzdCAlPiUgCiAgbXV0YXRlKGRheV9vZl93ZWVrID0gKSAlPiUgCiAgZ3JvdXBfYnkodmVnZXRhYmxlLCBkYXlfb2Zfd2VlaykgJT4lIAogIHN1bW1hcml6ZSgpICU+JSAKICBwaXZvdF93aWRlcigpCmBgYAoKIyMjIyBFeGVyY2lzZSAyOiBgcGl2b3RfbG9uZ2VyKClgCgpVc2UgdGhlIGBiaWxsYm9hcmRgIGRhdGFzZXQgKHNlYXJjaCBmb3IgaXQgaW4gaGVscCBvciB0eXBlIGA/YmlsbGJvYXJkYCBpbiB0aGUgY29uc29sZSkuIEl0IGhhcyByYW5raW5ncyBvZiBzb25ncyBmb3IgZWFjaCB3ZWVrIHRoZXkgZW50ZXJlZCB0aGUgQmlsbGJvYXJkIFRvcCAxMDAuIFRoZSB3ZWVrcyBhcmUgY29sdW1uIG5hbWVzLiBVc2UgYHBpdm90X2xvbmdlcigpYCB0byBtYWtlIHdlZWtzIGEgc2luZ2xlIGNvbHVtbiBhbmQgcmVtb3ZlIHJvd3Mgd2l0aCBtaXNzaW5nIHZhbHVlcyBmb3IgcmFuayAoSElOVDogdXNlIGB2YWx1ZXNfZHJvcF9uYWAgYXJndW1lbnQgaW4gYHBpdm90X2xvbmdlcigpYCkuCgpgYGB7ciwgZXZhbD1GQUxTRX0KYmlsbGJvYXJkICU+JSAKICBwaXZvdF9sb25nZXIoKQpgYGAKCiMjIyMgRXhlcmNpc2UgMTogbXV0YXRpbmcgam9pbgoKU3VtbWFyaXplIHRoZSBgZ2FyZGVuX2hhcnZlc3RgIGRhdGEgdG8gZmluZCB0aGUgdG90YWwgaGFydmVzdCBpbiBwb3VuZCBmb3IgZWFjaCB2ZWdldGFibGUgdmFyaWV0eSBhbmQgdGhlbiB0cnkgYWRkaW5nIHRoZSBwbG90IGZyb20gdGhlIGBwbGFudF9kYXRlX2xvY2AgdGFibGUuIFRoaXMgd2lsbCBub3QgdHVybiBvdXQgcGVyZmVjdGx5LiBXaGF0IGlzIHRoZSBwcm9ibGVtPyBIb3cgbWlnaHQgeW91IGZpeCBpdD8KCmBgYHtyLCBldmFsPUZBTFNFfQpnYXJkZW5faGFydmVzdCAlPiUgCiAgZ3JvdXBfYnkodmVnZXRhYmxlLCB2YXJpZXR5KSAlPiUgCiAgc3VtbWFyaXplKCkgJT4lIAogIGxlZnRfam9pbihwbGFudF9kYXRlX2xvYywKICAgICAgICAgICAgYnkgPSApCmBgYAoKIyMjIyBFeGVyY2lzZSAyOiBtdXRhdGluZyBqb2luCgpJIHdvdWxkIGxpa2UgdG8gdW5kZXJzdGFuZCBob3cgbXVjaCBtb25leSBJICJzYXZlZCIgYnkgZ2FyZGVuaW5nLCBmb3IgZWFjaCB2ZWdldGFibGUgdHlwZS4gRGVzY3JpYmUgaG93IEkgY291bGQgdXNlIHRoZSBgZ2FyZGVuX2hhcnZlc3RgIGFuZCBgZ2FyZGVuX3NwZW5kaW5nYCBkYXRhc2V0cywgYWxvbmcgd2l0aCBkYXRhIGZyb20gc29tZXdoZXJlIGxpa2UgW3RoaXNdKGh0dHBzOi8vcHJvZHVjdHMud2hvbGVmb29kc21hcmtldC5jb20vc2VhcmNoP3NvcnQ9cmVsZXZhbmNlJnN0b3JlPTEwNTQyKSB0byBhbnN3ZXIgdGhpcyBxdWVzdGlvbi4gWW91IGNhbiBhbnN3ZXIgdGhpcyBpbiB3b3JkcywgcmVmZXJlbmNpbmcgdmFyaW91cyBqb2luIGZ1bmN0aW9ucy4gWW91IGRvbid0IG5lZWQgUiBjb2RlIGJ1dCBjb3VsZCBwcm92aWRlIHNvbWUgaWYgaXQncyBoZWxwZnVsLgoKIyMjIyBFeGVyY2lzZSAzOiBmaWx0ZXJpbmcgam9pbgoKRXhjbHVkZSB0aGUgdmVnZXRhYmxlIHZhcmlldGllcyBmcm9tIGBnYXJkZW5faGFydmVzdGAgdGhhdCBhcmUgaW4gcGxvdHMgTSBhbmQgSC4gCgpgYGB7ciwgZXZhbD1GQUxTRX0KZ2FyZGVuX2hhcnZlc3QgJT4lIAogIGFudGlfam9pbihnYXJkZW5fcGxhbnRpbmcsCiAgICAgICAgICAgIGJ5ID0gKQpgYGAKCiMjIyMgRXhlcmNpc2UgMTogY2hhbmdpbmcgb3JkZXIgb2YgZmFjdG9ycwoKU3Vic2V0IHRoZSBkYXRhIHRvIHRvbWF0b2VzLiBSZW9yZGVyIHRoZSB0b21hdG8gdmFyaWV0aWVzIGZyb20gc21hbGxlc3QgdG8gbGFyZ2VzdCBmaXJzdCBoYXJ2ZXN0IGRhdGUuIENyZWF0ZSBhIGJhcnBsb3Qgb2YgdG90YWwgaGFydmVzdCBpbiBwb3VuZHMgZm9yIGVhY2ggdmFyaWV0eSwgaW4gdGhlIG5ldyBvcmRlci4KCmBgYHtyLCBldmFsPUZBTFNFfQpnYXJkZW5faGFydmVzdCAlPiUgCiAgZmlsdGVyKHZlZ2V0YWJsZSA9PSAidG9tYXRvZXMiKSAlPiUgCiAgbXV0YXRlKHZhcmlldHkgPSBmY3RfcmVvcmRlcihfX18pKQogIGdyb3VwX2J5KHZhcmlldHkpICU+JSAKICBzdW1tYXJpemUoX19fKSAlPiUgCiAgZ2dwbG90KCkgKwogIF9fXwpgYGAKCiMjIyMgRXhlcmNpc2UgMjogY2hhbmdpbmcgb3JkZXIgb2YgZmFjdG9ycwoKUmV2ZXJzZSB0aGUgb3JkZXIgb2YgdGhlIHZhcmlldGllcyBpbiB0aGUgcHJldmlvdXMgcGxvdC4KCmBgYHtyfQoKYGBgCgojIyMjIEV4ZXJjaXNlIDM6IGNoYW5naW5nIHRoZSB2YWx1ZXMgb2YgbGV2ZWxzCgpDb21iaW5lIHRoZSB0b21hdG8gdmFyaWV0aWVzIG9mIHZvbHVudGVlcnMgYW5kIGdyYXBlIHRvIGEgbmV3IGxldmVsOiAic21hbGwgdG9tYXRvZXMiLgoKSElOVDogYGZjdF9yZWxldmVsKClgCgojIyMjIEV4ZXJjaXNlIDE6IHdvcmtpbmcgd2l0aCBzdHJpbmdzCgpJbiB0aGUgYGdhcmRlbl9oYXJ2ZXN0YCBkYXRhLCBjcmVhdGUgdHdvIG5ldyB2YXJpYWJsZXM6IG9uZSB0aGF0IG1ha2VzIHRoZSB2YXJpZXRpZXMgbG93ZXJjYXNlIGFuZCBhbm90aGVyIHRoYXQgZmluZHMgdGhlIGxlbmd0aCBvZiB0aGUgdmFyaWV0eSBuYW1lLgoKSElOVDogYHN0cl90b19sb3dlcigpYCwgYHN0cl9sZW5ndGgoKWAKCiMjIyMgRXhlcmNpc2UgMjogd29ya2luZyB3aXRoIHN0cmluZ3MKCkZpbmQgYWxsIHRoZSB2YXJpZXRpZXMgdGhhdCBoYXZlICJlciIgb3IgImFyIiBpbiB0aGVpciBuYW1lLgoKSElOVDogYHN0cl9kZXRlY3QoKWAgYW5kIHVzZSBvciwgInwiCg==