1 Explore your data

1.1 Import your data

1.1.1 Read a CSV file

In this exercise, you’ll use read_csv() twice. The first time you will only specify the filename, but you’ll notice a problem with the imported data. The second time you’ll use a new argument called skip to fix the problem. Remember to use ?read_csv to read more about arguments like skip and how to use them.

The data you’ll work with is from “The Great British Bake-Off.” The file “bakeoff.csv” contains data for each episode of the show, organized by series and baker.

Tidyverse chearsheet

# Load readr
library(readr)

# Create bakeoff from "bakeoff.csv"
bakeoff <- read_csv("bakeoff.csv")
Parsed with column specification:
cols(
  series = col_double(),
  episode = col_double(),
  baker = col_character(),
  signature = col_character(),
  technical = col_double(),
  showstopper = col_character(),
  result = col_character(),
  uk_airdate = col_date(format = ""),
  us_season = col_double(),
  us_airdate = col_date(format = "")
)
# Print bakeoff
bakeoff

# Create bakeoff but skip first row in case needed
# bakeoff <- read_csv("bakeoff.csv", skip = 1)

Our original import had 3 problems: we had 1 extra observation, our first row stored the true variable names, and all column types were characters. Adding skip = 1 worked because the default argument for col_names is TRUE.

1.1.2 Assign missing values

The read_csv() function also has an na argument, which allows you to specify value(s) that represent missing values in your data. The default values for the na argument are c("“,”NA“), so both are recoded as missing (NA) in R. When you read in data, you can add additional values like the string”UNKNOWN" to a vector of missing values using the c() function to combine multiple values into a single vector.

The is.na() function is also helpful for identifying rows with missing values for a variable.

# Load dplyr
library(dplyr)

# Filter rows where showstopper is UNKNOWN 
bakeoff %>% 
    filter(showstopper == "UNKNOWN")

# Edit to add list of missing values
bakeoff <- read_csv("bakeoff.csv", na = c("", "NA", "UNKNOWN"))
Parsed with column specification:
cols(
  series = col_double(),
  episode = col_double(),
  baker = col_character(),
  signature = col_character(),
  technical = col_double(),
  showstopper = col_character(),
  result = col_character(),
  uk_airdate = col_date(format = ""),
  us_season = col_double(),
  us_airdate = col_date(format = "")
)
# Filter rows where showstopper is NA 
bakeoff %>%
  filter(is.na(showstopper))

We only had 4 missing values for the showstopper variable to start, but now all 21 are present and accounted for.

1.2 Know your data

1.2.1 Arrange and glimpse

You can combine glimpse() with other functions in a sequence using the pipe (%>%) operator. For example, you can use other dplyr functions like arrange first, then use glimpse by adding a line after the final pipe (%>%):

bakers_mini %>% 
  arrange(age) %>% 
  glimpse() # no argument needed here

Take a glimpse of the bakeoff data we imported in the first set of exercises. On which date did the first episode of the show air in the US?

# Filter and skim
bakeoff %>%
  filter(!is.na(us_season)) %>% 
  skim()
── Data Summary ────────────────────────
                           Values    
Name                       Piped data
Number of rows             302       
Number of columns          10        
_______________________              
Column type frequency:               
  character                4         
  Date                     2         
  numeric                  4         
________________________             
Group variables            None      

── Variable type: character ──────────────────────────────────────────────
  skim_variable n_missing complete_rate   min   max empty n_unique
1 baker                 0         1         3     9     0       48
2 signature             1         0.997    10   125     0      300
3 showstopper           1         0.997     5   126     0      298
4 result                0         1         2     9     0        6
  whitespace
1          0
2          0
3          0
4          0

── Variable type: Date ───────────────────────────────────────────────────
  skim_variable n_missing complete_rate min        max        median    
1 uk_airdate            0             1 2013-08-20 2016-10-26 2014-10-08
2 us_airdate            0             1 2014-12-28 2017-08-04 2015-11-08
  n_unique
1       40
2       35

── Variable type: numeric ────────────────────────────────────────────────
  skim_variable n_missing complete_rate  mean    sd    p0   p25   p50
1 series                0         1      5.49  1.13     4     4     5
2 episode               0         1      4.37  2.66     1     2     4
3 technical             1         0.997  4.85  2.98     1     2     4
4 us_season             0         1      2.49  1.13     1     1     2
    p75  p100 hist 
1     6     7 ▇▇▁▇▇
2     6    10 ▇▆▅▃▂
3     7    13 ▇▅▅▂▁
4     3     4 ▇▇▁▇▇

The first episode of the first US season aired on December 28, 2014.

1.2.2 Summarize your data

You can combine skim() with other functions in a sequence using the pipe (%>%) operator. For example, you could use other dplyr functions like group_by first, then use skim() by adding a line after the final pipe.

bakers_mini %>% 
  group_by(series) %>% 
  skim() # no argument needed here

This will produce summary statistics for each series. Let’s practice this!

# Load skimr
library(skimr)
bakeoff %>% 
  group_by(series) %>% 
  skim() # no argument needed here
no non-missing arguments to min; returning Infno non-missing arguments to min; returning Infno non-missing arguments to min; returning Infno non-missing arguments to min; returning Infno non-missing arguments to max; returning -Infno non-missing arguments to max; returning -Infno non-missing arguments to max; returning -Infno non-missing arguments to max; returning -Inf
── Data Summary ────────────────────────
                           Values    
Name                       Piped data
Number of rows             549       
Number of columns          10        
_______________________              
Column type frequency:               
  character                4         
  Date                     2         
  numeric                  3         
________________________             
Group variables            series    

── Variable type: character ──────────────────────────────────────────────
   skim_variable series n_missing complete_rate   min   max empty n_unique
 1 baker              1         0         1         3     9     0       10
 2 baker              2         0         1         3     9     0       12
 3 baker              3         0         1         4    10     0       12
 4 baker              4         0         1         3     9     0       13
 5 baker              5         0         1         4     7     0       12
 6 baker              6         0         1         3     6     0       12
 7 baker              7         0         1         3     9     0       12
 8 baker              8         0         1         3     6     0       12
 9 signature          1         0         1         8    63     0       36
10 signature          2         0         1         9    69     0       59
11 signature          3         0         1        11   119     0       76
12 signature          4         0         1        10   125     0       78
13 signature          5         1         0.986    15   107     0       73
14 signature          6         0         1        12    64     0       74
15 signature          7         0         1        12    93     0       75
16 signature          8         0         1        12    64     0       75
17 showstopper        1         3         0.917    13   197     0       33
18 showstopper        2        10         0.833    17   118     0       50
19 showstopper        3         7         0.908    12    70     0       69
20 showstopper        4         0         1         5   126     0       78
21 showstopper        5         1         0.986     8    82     0       73
22 showstopper        6         0         1        10    70     0       73
23 showstopper        7         0         1         5    86     0       74
24 showstopper        8         0         1        13    68     0       75
25 result             1         0         1         2     9     0        4
26 result             2         0         1         2     9     0        5
27 result             3         0         1         2     9     0        5
28 result             4         0         1         2     9     0        5
29 result             5         0         1         2     9     0        6
30 result             6         0         1         2     9     0        5
31 result             7         0         1         2     9     0        5
32 result             8         0         1         2     9     0        5
   whitespace
 1          0
 2          0
 3          0
 4          0
 5          0
 6          0
 7          0
 8          0
 9          0
10          0
11          0
12          0
13          0
14          0
15          0
16          0
17          0
18          0
19          0
20          0
21          0
22          0
23          0
24          0
25          0
26          0
27          0
28          0
29          0
30          0
31          0
32          0

── Variable type: Date ───────────────────────────────────────────────────
   skim_variable series n_missing complete_rate min        max       
 1 uk_airdate         1         0             1 2010-08-17 2010-09-21
 2 uk_airdate         2         0             1 2011-08-16 2011-10-04
 3 uk_airdate         3         0             1 2012-08-14 2012-10-16
 4 uk_airdate         4         0             1 2013-08-20 2013-10-22
 5 uk_airdate         5         0             1 2014-08-06 2014-10-08
 6 uk_airdate         6         0             1 2015-08-05 2015-10-07
 7 uk_airdate         7         0             1 2016-08-24 2016-10-26
 8 uk_airdate         8         0             1 2017-08-29 2017-10-31
 9 us_airdate         1        36             0 NA         NA        
10 us_airdate         2        60             0 NA         NA        
11 us_airdate         3        76             0 NA         NA        
12 us_airdate         4         0             1 2014-12-28 2015-03-01
13 us_airdate         5         0             1 2015-09-06 2015-11-08
14 us_airdate         6         0             1 2016-07-01 2016-08-12
15 us_airdate         7         0             1 2017-06-16 2017-08-04
16 us_airdate         8        75             0 NA         NA        
   median     n_unique
 1 2010-08-27        6
 2 2011-08-30        8
 3 2012-09-04       10
 4 2013-09-10       10
 5 2014-08-27       10
 6 2015-08-26       10
 7 2016-09-14       10
 8 2017-09-19       10
 9 NA                0
10 NA                0
11 NA                0
12 2015-01-18       10
13 2015-09-27       10
14 2016-07-15        7
15 2017-06-30        8
16 NA                0

── Variable type: numeric ────────────────────────────────────────────────
   skim_variable series n_missing complete_rate   mean    sd    p0   p25
 1 episode            1         0         1       2.83  1.65     1  1   
 2 episode            2         0         1       3.55  2.10     1  2   
 3 episode            3         0         1       4.43  2.67     1  2   
 4 episode            4         0         1       4.31  2.66     1  2   
 5 episode            5         0         1       4.38  2.68     1  2   
 6 episode            6         0         1       4.4   2.67     1  2   
 7 episode            7         0         1       4.4   2.67     1  2   
 8 episode            8         0         1       4.4   2.67     1  2   
 9 technical          1         7         0.806   3.97  2.56     1  2   
10 technical          2         0         1       4.9   3.03     1  2   
11 technical          3         0         1       4.82  2.92     1  2   
12 technical          4         0         1       5.08  3.19     1  2.25
13 technical          5         1         0.986   4.73  2.93     1  2   
14 technical          6         0         1       4.8   2.92     1  2   
15 technical          7         0         1       4.8   2.92     1  2   
16 technical          8         0         1       4.8   2.92     1  2   
17 us_season          1        36         0     NaN    NA       NA NA   
18 us_season          2        60         0     NaN    NA       NA NA   
19 us_season          3        76         0     NaN    NA       NA NA   
20 us_season          4         0         1       1     0        1  1   
21 us_season          5         0         1       2     0        2  2   
22 us_season          6         0         1       3     0        3  3   
23 us_season          7         0         1       4     0        4  4   
24 us_season          8        75         0     NaN    NA       NA NA   
     p50   p75  p100 hist   
 1   2.5  4        6 "▇▂▂▂▁"
 2   3    5        8 "▇▃▅▂▂"
 3   4    6.25    10 "▇▆▅▅▂"
 4   4    6       10 "▇▆▅▃▂"
 5   4    6       10 "▇▆▅▃▂"
 6   4    6       10 "▇▆▅▃▂"
 7   4    6       10 "▇▆▅▃▂"
 8   4    6       10 "▇▆▅▃▂"
 9   3    5       10 "▇▇▃▂▂"
10   4    7       12 "▇▅▃▂▂"
11   4    7       12 "▇▅▃▂▂"
12   4.5  7       13 "▇▅▅▂▂"
13   4    7       12 "▇▅▃▂▂"
14   4    7       12 "▇▅▃▂▂"
15   4    7       12 "▇▅▃▂▂"
16   4    7       12 "▇▅▃▂▂"
17  NA   NA       NA " "    
18  NA   NA       NA " "    
19  NA   NA       NA " "    
20   1    1        1 "▁▁▇▁▁"
21   2    2        2 "▁▁▇▁▁"
22   3    3        3 "▁▁▇▁▁"
23   4    4        4 "▁▁▇▁▁"
24  NA   NA       NA " "    
# Edit to filter, group by, and skim
bakeoff %>% 
  filter(!is.na(us_season)) %>% 
  group_by(us_season)  %>% 
  skim()
── Data Summary ────────────────────────
                           Values    
Name                       Piped data
Number of rows             302       
Number of columns          10        
_______________________              
Column type frequency:               
  character                4         
  Date                     2         
  numeric                  3         
________________________             
Group variables            us_season 

── Variable type: character ──────────────────────────────────────────────
   skim_variable us_season n_missing complete_rate   min   max empty
 1 baker                 1         0         1         3     9     0
 2 baker                 2         0         1         4     7     0
 3 baker                 3         0         1         3     6     0
 4 baker                 4         0         1         3     9     0
 5 signature             1         0         1        10   125     0
 6 signature             2         1         0.986    15   107     0
 7 signature             3         0         1        12    64     0
 8 signature             4         0         1        12    93     0
 9 showstopper           1         0         1         5   126     0
10 showstopper           2         1         0.986     8    82     0
11 showstopper           3         0         1        10    70     0
12 showstopper           4         0         1         5    86     0
13 result                1         0         1         2     9     0
14 result                2         0         1         2     9     0
15 result                3         0         1         2     9     0
16 result                4         0         1         2     9     0
   n_unique whitespace
 1       13          0
 2       12          0
 3       12          0
 4       12          0
 5       78          0
 6       73          0
 7       74          0
 8       75          0
 9       78          0
10       73          0
11       73          0
12       74          0
13        5          0
14        6          0
15        5          0
16        5          0

── Variable type: Date ───────────────────────────────────────────────────
  skim_variable us_season n_missing complete_rate min        max       
1 uk_airdate            1         0             1 2013-08-20 2013-10-22
2 uk_airdate            2         0             1 2014-08-06 2014-10-08
3 uk_airdate            3         0             1 2015-08-05 2015-10-07
4 uk_airdate            4         0             1 2016-08-24 2016-10-26
5 us_airdate            1         0             1 2014-12-28 2015-03-01
6 us_airdate            2         0             1 2015-09-06 2015-11-08
7 us_airdate            3         0             1 2016-07-01 2016-08-12
8 us_airdate            4         0             1 2017-06-16 2017-08-04
  median     n_unique
1 2013-09-10       10
2 2014-08-27       10
3 2015-08-26       10
4 2016-09-14       10
5 2015-01-18       10
6 2015-09-27       10
7 2016-07-15        7
8 2017-06-30        8

── Variable type: numeric ────────────────────────────────────────────────
   skim_variable us_season n_missing complete_rate  mean    sd    p0   p25
 1 series                1         0         1      4     0        4  4   
 2 series                2         0         1      5     0        5  5   
 3 series                3         0         1      6     0        6  6   
 4 series                4         0         1      7     0        7  7   
 5 episode               1         0         1      4.31  2.66     1  2   
 6 episode               2         0         1      4.38  2.68     1  2   
 7 episode               3         0         1      4.4   2.67     1  2   
 8 episode               4         0         1      4.4   2.67     1  2   
 9 technical             1         0         1      5.08  3.19     1  2.25
10 technical             2         1         0.986  4.73  2.93     1  2   
11 technical             3         0         1      4.8   2.92     1  2   
12 technical             4         0         1      4.8   2.92     1  2   
     p50   p75  p100 hist 
 1   4       4     4 ▁▁▇▁▁
 2   5       5     5 ▁▁▇▁▁
 3   6       6     6 ▁▁▇▁▁
 4   7       7     7 ▁▁▇▁▁
 5   4       6    10 ▇▆▅▃▂
 6   4       6    10 ▇▆▅▃▂
 7   4       6    10 ▇▆▅▃▂
 8   4       6    10 ▇▆▅▃▂
 9   4.5     7    13 ▇▅▅▂▂
10   4       7    12 ▇▅▃▂▂
11   4       7    12 ▇▅▃▂▂
12   4       7    12 ▇▅▃▂▂

Nice piping & skimming! Producing quick grouped summaries with the skim function is a real time (and sanity) saver.

1.2.3 Know your variable types

How many variables of each type do we have in the bakeoff data? You may also want to try piping a skimmed object to summary(), also from the skimr package:

tibble_name %>% 
  skim() %>%  # no argument needed here
  summary() # no argument needed here
bakeoff %>% 
  skim() %>%  # no argument needed here
  summary() # no argument needed here
── Data Summary ────────────────────────
                           Values    
Name                       Piped data
Number of rows             549       
Number of columns          10        
_______________________              
Column type frequency:               
  character                4         
  Date                     2         
  numeric                  4         
________________________             
Group variables            None      

1.3 Count with your data

In every episode of “The Great British Bake-Off”, bakers complete 3 challenges and the show’s judges award the title “Star Baker” to the baker who excelled in that week’s challenges (with the exception of the finale). Each baker’s result for every episode is stored in bakeoff- result is a character variable, and the value “SB” stands for star baker.

1.3.1 Distinct and count

# View distinct results
bakeoff %>%
  distinct(result)
# Count whether or not star baker
bakeoff %>% 
  count(result)
# Count whether or not star baker
bakeoff %>% 
  count(result == "SB")

There are 488 results that are not star baker.

1.3.2 Count episodes

Here’s how we can use count() back-to-back to roll up a level of detail across series:

bakers %>% 
  count(aired_us, series) %>% 
  count(aired_us)
  

Let’s practice counting the number of episodes per series.

bakeoff %>% 
  count(series, episode) %>% 
  count(series)
Using `n` as weighting variable
ℹ Quiet this message with `wt = n` or count rows with `wt = 1`

You can see that by series 3 we were treated to 10 episodes with each new series!

1.3.3 Count bakers

We’ll practice that again, focusing on counting bakers.

# Count the number of rows by series and baker
bakers_by_series <- bakeoff %>%
                      count(series, baker)


# Print to view
bakers_by_series

# Count the number of rows by series and baker
bakers_by_series <- bakeoff %>% 
  count(series, baker)
  
# Print to view
bakers_by_series
  
# Count again by series
bakers_by_series %>%
  count(series)
Using `n` as weighting variable
ℹ Quiet this message with `wt = n` or count rows with `wt = 1`
# Count again by baker
bakers_by_series %>%
  count(baker, sort = TRUE)
Using `n` as weighting variable
ℹ Quiet this message with `wt = n` or count rows with `wt = 1`

Series 4 had the most bakers with 13 total (a baker’s dozen!). Kate is the most popular name- Kates have appeared in 3 different series of the TV show.

1.3.4 Plot counts

You can learn a lot about your data by counting, but sometimes you can learn even more by plotting counts. This is especially true when you have lots of things to count! With eight series, 74 episodes, and 95 bakers, a plot can be more helpful than a table of numbers. We’ll use ggplot2 to visualize the number of bakers across episodes for each series from bakeoff.

library(ggplot2)

ggplot(bakeoff, aes(episode)) + 
    geom_bar() + 
    facet_wrap(~series)

You made it through the first chapter! We have a lot of work left to do with The Great British Bake Off data, but notice how this plot is a great sanity check: the number of bakers tends to go down with every episode (never up!), with a few exceptions where no one was eliminated. Also, you can see that there are fewer episodes in the first two series. Finally, you can see that there are always three bakers who make it to the series finale.

2 Tame your data

2.1 Cast column types

2.1.1 Cast a column to a date

A good workflow for parsing dates using readr is to, for example:

  • Use parse_date(“2012-14-08”, format = “%Y-%d-%m”) first, then
  • Use col_date(format = “%Y-%d-%m”) within cols() as the col_types argument of read_csv().

In “desserts.csv”, the variable uk_airdate is formatted like “17 August 2010”. Let’s parse, then cast this variable!

Later on in Chapter 4, we’ll showcase some functions from the lubridate package to help you extract data from dates, once they are cast properly.

# Find format to parse uk_airdate 
parse_date("17 August 2010", format = "%d %B %Y")
[1] "2010-08-17"
# Edit to cast uk_airdate
desserts <- read_csv("desserts.csv", 
                     col_types = cols(
                       uk_airdate = col_date(format = "%d %B %Y")
                     )
                    )

# Arrange by descending uk_airdate
desserts %>% 
    arrange(desc(uk_airdate))

Notice that us_airdate didn’t need to be cast - this is because the date format %Y-%m-%d is unambiguous, so it is automatically parsed as a date by readr.

2.1.2 Cast a column to a number

We saw a good workflow for parsing columns using readr:

  • Use parse_number() to practice, then
  • Use col_number() to cast.

But sometimes you’ll need to start with casting, then diagnose parsing problems using a new readr function called problems(). Using problems() on a result of read_csv() will show you the rows and columns where parsing error occurred, what the parser expected to find (for example, a number), and the actual value that caused the parsing error.

Let’s practice this with “desserts.csv”, which includes data about the judges’ ranks for each baker in the technical challenge, for every episode.

# Try to cast technical as a number
desserts <- read_csv("desserts.csv", 
                     col_types = cols(
                       uk_airdate = col_date(format = "%d %B %Y"),
                       technical = col_number()
                     )
                    )
7 parsing failures.
row       col expected actual           file
  4 technical a number    N/A 'desserts.csv'
  6 technical a number    N/A 'desserts.csv'
  8 technical a number    N/A 'desserts.csv'
 10 technical a number    N/A 'desserts.csv'
 34 technical a number    N/A 'desserts.csv'
... ......... ........ ...... ..............
See problems(...) for more details.
# View parsing problems
desserts %>%
  problems()
# Edit code to fix the parsing error 
desserts <- read_csv("desserts.csv",
                      col_types = cols(
                        uk_airdate = col_date(format = "%d %B %Y"),
                        technical = col_number()
                      ),
                        na = c("", "NA", "N/A") #solving the problem
                     )

# View parsing problems
problems(desserts)

2.1.3 Cast a column as a factor

Factors are categorical variables, where the possible values are a fixed and known set. For example, take a simple factor like bake below:

bake <- c("pie", "cake", "neither") 
parse_factor(bake, levels = NULL) 
[1] pie     cake    neither
Levels: pie cake neither

You can use parse_factor() to parse variables and col_factor() to cast columns as categorical. Both functions have a levels argument that is used to specify the possible values for the factors. When levels is set to NULL, the possible values will be inferred from the unique values in the dataset. Alternatively, you can pass a list of possible values.

This time, you will look at the result column, which contains the outcome of the competition for each baker.

# Cast result a factor
desserts <- read_csv("desserts.csv", 
                     na = c("", "NA", "N/A"),
                     col_types = cols(
                       uk_airdate = col_date(format = "%d %B %Y"),
                       technical = col_number(),                       
                       result = col_factor(levels = NULL)
                     )
                    )
                    
# Glimpse to view
glimpse(desserts)
Rows: 549
Columns: 16
$ series                <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
$ episode               <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,…
$ baker                 <chr> "Annetha", "David", "Edd", "Jasminder", "…
$ technical             <dbl> 2, 3, 1, NA, 9, NA, 8, NA, 10, NA, 8, 6, …
$ result                <fct> IN, IN, IN, IN, IN, IN, IN, IN, OUT, OUT,…
$ uk_airdate            <date> 2010-08-17, 2010-08-17, 2010-08-17, 2010…
$ us_season             <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ us_airdate            <date> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ showstopper_chocolate <chr> "chocolate", "chocolate", "no chocolate",…
$ showstopper_dessert   <chr> "other", "other", "other", "other", "othe…
$ showstopper_fruit     <chr> "no fruit", "no fruit", "no fruit", "no f…
$ showstopper_nut       <chr> "no nut", "no nut", "no nut", "no nut", "…
$ signature_chocolate   <chr> "no chocolate", "chocolate", "no chocolat…
$ signature_dessert     <chr> "cake", "cake", "cake", "cake", "cake", "…
$ signature_fruit       <chr> "no fruit", "fruit", "fruit", "fruit", "f…
$ signature_nut         <chr> "no nut", "no nut", "no nut", "no nut", "…

While it takes a lot of work up front, casting column types when you import can make your analyses easier to reproduce.

2.2 Recode values

2.2.1 Recode a character variable

In this exercise, you’ll recode() the nut variable in the desserts data. This is a character variable that tells us, for each bake, whether a nut was a key ingredient and if so, what kind of nut!

Remember that the arguments of recode() are the variable that you want to recode, and then an expression of the form old_value = new_value. recode() is most useful when used inside a mutate() to create a new variable or reassign the old one.

Often you want to recode a certain value as missing. In this case, it is important to preserve the data type of the variable. R has built-in words to indicate missing data of various types, for example NA_character_ for missing value for a character variable, or NA_integer_ for missing integer data. The NA value is of the logical type.

Remember that you can use ?function_name_here to read more about arguments and how to use them.

# Count rows grouping by nut variable
desserts %>% 
    count(signature_nut, sort = TRUE)
    
# Edit code to recode "no nut" as missing
desserts_2 <- desserts %>% 
  mutate(nut = recode(signature_nut, "filbert" = "hazelnut", 
                           "no nut" = NA_character_))

# Count rows again 
desserts_2 %>% 
    count(nut, sort = TRUE)

2.2.2 Recode a numeric variable

Dummy variables are often used in data analysis to bin a variable into one of two categories to indicate the absence or presence of something. Dummy variables take the value 0 or 1 to stand for, for example, loser or winner. Dummy variables are often factor variables as opposed to numeric - we’ll cover more about factors in the last chapter.

In desserts, the technical variable is a number representing the judges’ ranks for each baker in the technical challenge, for every episode (remember to run glimpse(desserts) to take a peek). In this exercise, you will recode the numeric technical variable to a new factor dummy variable.

## Edit to recode tech_win as factor
desserts <- desserts %>% 
  mutate(tech_win = recode_factor(technical, `1` = 1,
                           .default = 0))

# Count to compare values                      
desserts %>% 
  count(technical == 1, tech_win)

2.3 Select variables

2.3.1 Combine functions with select

The ratings data tells us about how many UK viewers tuned in to watch “The Great British Bake-Off” for each episode of each series. If you explore ratings in console, you’ll see that there is a separate variable for the number of viewers for each episode. A key question we’ll return to in Chapter 3 is: how many more viewers watched the finale than the premiere episode?

For series with 10 episodes, which showed the most growth in viewers from the premiere to the finale? Which showed the least?

ratings <- read_csv("Ratings/02.03_messy_ratings.csv")
Parsed with column specification:
cols(
  .default = col_double(),
  premiere = col_character(),
  finale = col_character(),
  winner = col_character(),
  day_of_week = col_character(),
  timeslot = col_time(format = ""),
  channel = col_character(),
  runner_up_1 = col_character(),
  runner_up_2 = col_character(),
  season_premiere = col_character(),
  season_finale = col_character(),
  e1_uk_airdate = col_character(),
  e2_uk_airdate = col_character(),
  e3_uk_airdate = col_character(),
  e4_uk_airdate = col_character(),
  e5_uk_airdate = col_character(),
  e6_uk_airdate = col_character(),
  e7_uk_airdate = col_character(),
  e8_uk_airdate = col_character(),
  e9_uk_airdate = col_character(),
  e10_uk_airdate = col_character()
)
See spec(...) for full column specifications.
head(ratings)
ratings <- ratings %>% 
  filter(episodes == 10) %>%
  mutate(viewer_growth = e10_viewers_7day - e1_viewers_7day)
ratings %>% 
  select(series, viewer_growth)

Most: series 5, Least = series 8

2.3.2 Recode factor to plot

In the last exercise, you may have used mutate() to find the difference between the number of premiere and finale episode viewers for each series. This variable, which I created and called viewer_growth, is now available in ratings, and feel free to check out it.

Now let’s practice using select() to check the result of another mutate with recode_factor().

If you have examined ratings, you might have seen that series have aired on three different channels: “Channel 4”, “BBC One”, “BBC Two”.

# Recode channel as factor: bbc (1) or not (0)
ratings <- ratings %>% 
  mutate(bbc = recode_factor(channel, 
                             "Channel 4" = 0,
                             .default = 1))
                            
# Select to look at variables to plot next
ratings %>% 
  select(series, channel, bbc, viewer_growth)
  
# Make a filled bar chart
ggplot(ratings, aes(x = series, y = viewer_growth, fill = bbc)) +
  geom_col()

Remember to look up the other select helper functions using ?select. What is going on with viewer_growth in series 8?

2.3.3 Select and reorder variables

As we have seen, selecting a subset of columns to print can help you check that a mutate() worked as expected, and rearranging columns next to each other can help you spot obvious errors in data entry.

The select() helpers are functions that you can use inside select() to allow you to select variables based on their names.

In this exercise, you’ll work with the ratings data, and you’ll use a new helper function called everything() which can be useful when reordering columns. Use ?everything to read more. You will learn more about using helper functions in the next lesson.

# Move channel to first column
ratings %>% 
  select(channel, everything())

# Drop 7- and 28-day episode ratings
ratings %>% 
  select(-ends_with("day"))

# Move channel to front and drop 7-/28-day episode ratings
ratings %>% 
  select(channel, everything(), -ends_with("day"))

everything() is a great little helper! If it had been placed at the end, it would have added back in all the columns that end with “day”. Placing it before deselecting columns, though, is a real time-saver.

2.4 Tame variable names

2.4.1 Reformat variables

We saw how to use clean_names() from the janitor package to convert all variable names to snake_case. Here is how:

library(janitor)
young_bakers3 <- young_bakers3 %>% 
   clean_names()

The function clean_names() takes an argument case that can be used to convert variable names to other cases, like “upper_camel” or “all_caps”.

Let’s practice using the messy_ratings tibble. Remember you can use ?clean_names to see the function arguments and how to use them.

messy_ratings <- read_csv("Ratings/02.03_messy_ratings.csv")
Parsed with column specification:
cols(
  .default = col_double(),
  premiere = col_character(),
  finale = col_character(),
  winner = col_character(),
  day_of_week = col_character(),
  timeslot = col_time(format = ""),
  channel = col_character(),
  runner_up_1 = col_character(),
  runner_up_2 = col_character(),
  season_premiere = col_character(),
  season_finale = col_character(),
  e1_uk_airdate = col_character(),
  e2_uk_airdate = col_character(),
  e3_uk_airdate = col_character(),
  e4_uk_airdate = col_character(),
  e5_uk_airdate = col_character(),
  e6_uk_airdate = col_character(),
  e7_uk_airdate = col_character(),
  e8_uk_airdate = col_character(),
  e9_uk_airdate = col_character(),
  e10_uk_airdate = col_character()
)
See spec(...) for full column specifications.

colnames(messy_ratings)

names(messy_ratings) <- c("SERIES", "episodes", "premiere", "finale", "winner", "Avg.uk~Viewers", 
"day", "timeslot", "channel", "runner.up~1", "runner.up~2", "season", 
"season.premiere", "season.finale", "Gbbo E1.Viewers~7Day", "Gbbo E1.Viewers~28Day", 
"Gbbo E2.Viewers~7Day", "Gbbo E2.Viewers~28Day", "Gbbo E3.Viewers~7Day", 
"Gbbo E3.Viewers~28Day", "Gbbo E4.Viewers~7Day", "Gbbo E4.Viewers~28Day", 
"Gbbo E5.Viewers~7Day", "Gbbo E5.Viewers~28Day", "Gbbo E6.Viewers~7Day", 
"Gbbo E6.Viewers~28Day", "Gbbo E7.Viewers~7Day", "Gbbo E7.Viewers~28Day", 
"Gbbo E8.Viewers~7Day", "Gbbo E8.Viewers~28Day", "Gbbo E9.Viewers~7Day", 
"Gbbo E9.Viewers~28Day", "Gbbo E10.Viewers~7Day", "Gbbo E10.Viewers~28Day")
The `value` argument of ``names<-`()` must have the same length as `x` as of tibble 3.0.0.
`names` must have length 44, not 34.
This warning is displayed once every 8 hours.
Call `lifecycle::last_warnings()` to see where this warning was generated.The `value` argument of ``names<-`()` can't be empty as of tibble 3.0.0.
Columns 35, 36, 37, 38, 39, and 5 more must be named.
This warning is displayed once every 8 hours.
Call `lifecycle::last_warnings()` to see where this warning was generated.
# Glimpse to see variable names
glimpse(messy_ratings)
Rows: 8
Columns: 44
$ SERIES                   <dbl> 1, 2, 3, 4, 5, 6, 7, 8
$ episodes                 <dbl> 6, 8, 10, 10, 10, 10, 10, 10
$ premiere                 <chr> "17-Aug-10", "14-Aug-11", "14-Aug-12",…
$ finale                   <chr> "21-Sep-10", "4-Oct-11", "16-Oct-12", …
$ winner                   <chr> "Edd Kimber", "Joanne Wheatley", "John…
$ `Avg.uk~Viewers`         <dbl> 2.77, 4.00, 5.00, 7.35, 10.04, 12.50, …
$ day                      <chr> "Tuesday", "Tuesday", "Tuesday", "Tues…
$ timeslot                 <time> 20:00:00, 20:00:00, 20:00:00, 20:00:0…
$ channel                  <chr> "BBC Two", "BBC Two", "BBC Two", "BBC …
$ `runner.up~1`            <chr> "Miranda Gore Browne", "Holly Bell", "…
$ `runner.up~2`            <chr> "Ruth Clemens", "Mary-Anne Boermans", …
$ season                   <dbl> NA, NA, NA, 1, 2, 3, 4, NA
$ season.premiere          <chr> NA, NA, NA, "12/28/14", "9/6/15", "7/1…
$ season.finale            <chr> NA, NA, NA, "3/1/15", "11/8/15", "8/12…
$ `Gbbo E1.Viewers~7Day`   <dbl> 2.24, 3.10, 3.85, 6.60, 8.51, 11.62, 1…
$ `Gbbo E1.Viewers~28Day`  <dbl> NA, NA, NA, NA, NA, 11.73, 13.86, 9.72
$ `Gbbo E2.Viewers~7Day`   <dbl> 3.00, 3.53, 4.60, 6.65, 8.79, 11.59, 1…
$ `Gbbo E2.Viewers~28Day`  <dbl> NA, NA, NA, NA, NA, 11.84, 13.74, 9.53
$ `Gbbo E3.Viewers~7Day`   <dbl> 3.00, 3.82, 4.53, 7.17, 9.28, 12.01, 1…
$ `Gbbo E3.Viewers~28Day`  <dbl> NA, NA, NA, NA, NA, NA, 13.38, 9.06
$ `Gbbo E4.Viewers~7Day`   <dbl> 2.60, 3.60, 4.71, 6.82, 10.25, 12.36, …
$ `Gbbo E4.Viewers~28Day`  <dbl> NA, NA, NA, NA, NA, 12.67, 13.88, 8.87
$ `Gbbo E5.Viewers~7Day`   <dbl> 3.03, 3.83, 4.61, 6.95, 9.95, 12.39, 1…
$ `Gbbo E5.Viewers~28Day`  <dbl> NA, NA, NA, NA, NA, 12.63, 13.33, 8.98
$ `Gbbo E6.Viewers~7Day`   <dbl> 2.75, 4.25, 4.82, 7.32, 10.13, 12.00, …
$ `Gbbo E6.Viewers~28Day`  <dbl> NA, NA, NA, NA, NA, 12.26, 13.41, 8.91
$ `Gbbo E7.Viewers~7Day`   <dbl> NA, 4.42, 5.10, 7.76, 10.28, 12.35, 13…
$ `Gbbo E7.Viewers~28Day`  <dbl> NA, NA, NA, NA, NA, 12.58, 13.72, 9.32
$ `Gbbo E8.Viewers~7Day`   <dbl> NA, 5.060, 5.350, 7.410, 9.023, 11.090…
$ `Gbbo E8.Viewers~28Day`  <dbl> NA, NA, NA, NA, NA, 11.35, 13.45, 9.19
$ `Gbbo E9.Viewers~7Day`   <dbl> NA, NA, 5.70, 7.41, 10.67, 12.65, 13.4…
$ `Gbbo E9.Viewers~28Day`  <dbl> NA, NA, NA, NA, NA, 12.78, 13.65, 9.16
$ `Gbbo E10.Viewers~7Day`  <dbl> NA, NA, 6.74, 9.45, 13.51, 15.05, 15.9…
$ `Gbbo E10.Viewers~28Day` <dbl> NA, NA, NA, NA, NA, 15.16, 16.03, 10.13
$ NA                       <chr> "8/17/10", "8/16/11", "8/14/12", "8/20…
$ NA                       <chr> "8/24/10", "8/23/11", "8/21/12", "8/27…
$ NA                       <chr> "8/31/10", "8/30/11", "8/28/12", "9/3/…
$ NA                       <chr> "9/7/10", "9/6/11", "9/4/12", "9/10/13…
$ NA                       <chr> "9/14/10", "9/13/11", "9/11/12", "9/17…
$ NA                       <chr> "9/21/10", "9/20/11", "9/18/12", "9/24…
$ NA                       <chr> NA, "9/27/11", "9/25/12", "10/1/13", "…
$ NA                       <chr> NA, "10/4/11", "10/2/12", "10/8/13", "…
$ NA                       <chr> NA, NA, "10/9/12", "10/15/13", "10/1/1…
$ NA                       <chr> NA, NA, "10/16/12", "10/22/13", "10/8/…
# Load janitor
library(janitor)

# Reformat to lower camelcase
ratings2 <- messy_ratings %>%
  clean_names("lower_camel")
    
# Glimpse new tibble
glimpse(ratings2)
Rows: 8
Columns: 44
$ series              <dbl> 1, 2, 3, 4, 5, 6, 7, 8
$ episodes            <dbl> 6, 8, 10, 10, 10, 10, 10, 10
$ premiere            <chr> "17-Aug-10", "14-Aug-11", "14-Aug-12", "20-…
$ finale              <chr> "21-Sep-10", "4-Oct-11", "16-Oct-12", "22-O…
$ winner              <chr> "Edd Kimber", "Joanne Wheatley", "John Whai…
$ avgUkViewers        <dbl> 2.77, 4.00, 5.00, 7.35, 10.04, 12.50, 13.85…
$ day                 <chr> "Tuesday", "Tuesday", "Tuesday", "Tuesday",…
$ timeslot            <time> 20:00:00, 20:00:00, 20:00:00, 20:00:00, 20…
$ channel             <chr> "BBC Two", "BBC Two", "BBC Two", "BBC Two",…
$ runnerUp1           <chr> "Miranda Gore Browne", "Holly Bell", "Brend…
$ runnerUp2           <chr> "Ruth Clemens", "Mary-Anne Boermans", "Jame…
$ season              <dbl> NA, NA, NA, 1, 2, 3, 4, NA
$ seasonPremiere      <chr> NA, NA, NA, "12/28/14", "9/6/15", "7/1/16",…
$ seasonFinale        <chr> NA, NA, NA, "3/1/15", "11/8/15", "8/12/16",…
$ gbboE1Viewers7Day   <dbl> 2.24, 3.10, 3.85, 6.60, 8.51, 11.62, 13.58,…
$ gbboE1Viewers28Day  <dbl> NA, NA, NA, NA, NA, 11.73, 13.86, 9.72
$ gbboE2Viewers7Day   <dbl> 3.00, 3.53, 4.60, 6.65, 8.79, 11.59, 13.45,…
$ gbboE2Viewers28Day  <dbl> NA, NA, NA, NA, NA, 11.84, 13.74, 9.53
$ gbboE3Viewers7Day   <dbl> 3.00, 3.82, 4.53, 7.17, 9.28, 12.01, 13.01,…
$ gbboE3Viewers28Day  <dbl> NA, NA, NA, NA, NA, NA, 13.38, 9.06
$ gbboE4Viewers7Day   <dbl> 2.60, 3.60, 4.71, 6.82, 10.25, 12.36, 13.29…
$ gbboE4Viewers28Day  <dbl> NA, NA, NA, NA, NA, 12.67, 13.88, 8.87
$ gbboE5Viewers7Day   <dbl> 3.03, 3.83, 4.61, 6.95, 9.95, 12.39, 13.12,…
$ gbboE5Viewers28Day  <dbl> NA, NA, NA, NA, NA, 12.63, 13.33, 8.98
$ gbboE6Viewers7Day   <dbl> 2.75, 4.25, 4.82, 7.32, 10.13, 12.00, 13.13…
$ gbboE6Viewers28Day  <dbl> NA, NA, NA, NA, NA, 12.26, 13.41, 8.91
$ gbboE7Viewers7Day   <dbl> NA, 4.42, 5.10, 7.76, 10.28, 12.35, 13.45, …
$ gbboE7Viewers28Day  <dbl> NA, NA, NA, NA, NA, 12.58, 13.72, 9.32
$ gbboE8Viewers7Day   <dbl> NA, 5.060, 5.350, 7.410, 9.023, 11.090, 13.…
$ gbboE8Viewers28Day  <dbl> NA, NA, NA, NA, NA, 11.35, 13.45, 9.19
$ gbboE9Viewers7Day   <dbl> NA, NA, 5.70, 7.41, 10.67, 12.65, 13.44, 9.…
$ gbboE9Viewers28Day  <dbl> NA, NA, NA, NA, NA, 12.78, 13.65, 9.16
$ gbboE10Viewers7Day  <dbl> NA, NA, 6.74, 9.45, 13.51, 15.05, 15.90, 10…
$ gbboE10Viewers28Day <dbl> NA, NA, NA, NA, NA, 15.16, 16.03, 10.13
$ na                  <chr> "8/17/10", "8/16/11", "8/14/12", "8/20/13",…
$ na_2                <chr> "8/24/10", "8/23/11", "8/21/12", "8/27/13",…
$ na_3                <chr> "8/31/10", "8/30/11", "8/28/12", "9/3/13", …
$ na_4                <chr> "9/7/10", "9/6/11", "9/4/12", "9/10/13", "8…
$ na_5                <chr> "9/14/10", "9/13/11", "9/11/12", "9/17/13",…
$ na_6                <chr> "9/21/10", "9/20/11", "9/18/12", "9/24/13",…
$ na_7                <chr> NA, "9/27/11", "9/25/12", "10/1/13", "9/17/…
$ na_8                <chr> NA, "10/4/11", "10/2/12", "10/8/13", "9/24/…
$ na_9                <chr> NA, NA, "10/9/12", "10/15/13", "10/1/14", "…
$ na_10               <chr> NA, NA, "10/16/12", "10/22/13", "10/8/14", …
# Glimpse to see variable names
glimpse(messy_ratings)
Rows: 8
Columns: 44
$ SERIES                   <dbl> 1, 2, 3, 4, 5, 6, 7, 8
$ episodes                 <dbl> 6, 8, 10, 10, 10, 10, 10, 10
$ premiere                 <chr> "17-Aug-10", "14-Aug-11", "14-Aug-12",…
$ finale                   <chr> "21-Sep-10", "4-Oct-11", "16-Oct-12", …
$ winner                   <chr> "Edd Kimber", "Joanne Wheatley", "John…
$ `Avg.uk~Viewers`         <dbl> 2.77, 4.00, 5.00, 7.35, 10.04, 12.50, …
$ day                      <chr> "Tuesday", "Tuesday", "Tuesday", "Tues…
$ timeslot                 <time> 20:00:00, 20:00:00, 20:00:00, 20:00:0…
$ channel                  <chr> "BBC Two", "BBC Two", "BBC Two", "BBC …
$ `runner.up~1`            <chr> "Miranda Gore Browne", "Holly Bell", "…
$ `runner.up~2`            <chr> "Ruth Clemens", "Mary-Anne Boermans", …
$ season                   <dbl> NA, NA, NA, 1, 2, 3, 4, NA
$ season.premiere          <chr> NA, NA, NA, "12/28/14", "9/6/15", "7/1…
$ season.finale            <chr> NA, NA, NA, "3/1/15", "11/8/15", "8/12…
$ `Gbbo E1.Viewers~7Day`   <dbl> 2.24, 3.10, 3.85, 6.60, 8.51, 11.62, 1…
$ `Gbbo E1.Viewers~28Day`  <dbl> NA, NA, NA, NA, NA, 11.73, 13.86, 9.72
$ `Gbbo E2.Viewers~7Day`   <dbl> 3.00, 3.53, 4.60, 6.65, 8.79, 11.59, 1…
$ `Gbbo E2.Viewers~28Day`  <dbl> NA, NA, NA, NA, NA, 11.84, 13.74, 9.53
$ `Gbbo E3.Viewers~7Day`   <dbl> 3.00, 3.82, 4.53, 7.17, 9.28, 12.01, 1…
$ `Gbbo E3.Viewers~28Day`  <dbl> NA, NA, NA, NA, NA, NA, 13.38, 9.06
$ `Gbbo E4.Viewers~7Day`   <dbl> 2.60, 3.60, 4.71, 6.82, 10.25, 12.36, …
$ `Gbbo E4.Viewers~28Day`  <dbl> NA, NA, NA, NA, NA, 12.67, 13.88, 8.87
$ `Gbbo E5.Viewers~7Day`   <dbl> 3.03, 3.83, 4.61, 6.95, 9.95, 12.39, 1…
$ `Gbbo E5.Viewers~28Day`  <dbl> NA, NA, NA, NA, NA, 12.63, 13.33, 8.98
$ `Gbbo E6.Viewers~7Day`   <dbl> 2.75, 4.25, 4.82, 7.32, 10.13, 12.00, …
$ `Gbbo E6.Viewers~28Day`  <dbl> NA, NA, NA, NA, NA, 12.26, 13.41, 8.91
$ `Gbbo E7.Viewers~7Day`   <dbl> NA, 4.42, 5.10, 7.76, 10.28, 12.35, 13…
$ `Gbbo E7.Viewers~28Day`  <dbl> NA, NA, NA, NA, NA, 12.58, 13.72, 9.32
$ `Gbbo E8.Viewers~7Day`   <dbl> NA, 5.060, 5.350, 7.410, 9.023, 11.090…
$ `Gbbo E8.Viewers~28Day`  <dbl> NA, NA, NA, NA, NA, 11.35, 13.45, 9.19
$ `Gbbo E9.Viewers~7Day`   <dbl> NA, NA, 5.70, 7.41, 10.67, 12.65, 13.4…
$ `Gbbo E9.Viewers~28Day`  <dbl> NA, NA, NA, NA, NA, 12.78, 13.65, 9.16
$ `Gbbo E10.Viewers~7Day`  <dbl> NA, NA, 6.74, 9.45, 13.51, 15.05, 15.9…
$ `Gbbo E10.Viewers~28Day` <dbl> NA, NA, NA, NA, NA, 15.16, 16.03, 10.13
$ NA                       <chr> "8/17/10", "8/16/11", "8/14/12", "8/20…
$ NA                       <chr> "8/24/10", "8/23/11", "8/21/12", "8/27…
$ NA                       <chr> "8/31/10", "8/30/11", "8/28/12", "9/3/…
$ NA                       <chr> "9/7/10", "9/6/11", "9/4/12", "9/10/13…
$ NA                       <chr> "9/14/10", "9/13/11", "9/11/12", "9/17…
$ NA                       <chr> "9/21/10", "9/20/11", "9/18/12", "9/24…
$ NA                       <chr> NA, "9/27/11", "9/25/12", "10/1/13", "…
$ NA                       <chr> NA, "10/4/11", "10/2/12", "10/8/13", "…
$ NA                       <chr> NA, NA, "10/9/12", "10/15/13", "10/1/1…
$ NA                       <chr> NA, NA, "10/16/12", "10/22/13", "10/8/…
# Load janitor
library(janitor)

# Reformat to snake case
ratings2 <- messy_ratings %>%  
  clean_names()

# Glimpse cleaned names
glimpse(ratings2)
Rows: 8
Columns: 44
$ series                 <dbl> 1, 2, 3, 4, 5, 6, 7, 8
$ episodes               <dbl> 6, 8, 10, 10, 10, 10, 10, 10
$ premiere               <chr> "17-Aug-10", "14-Aug-11", "14-Aug-12", "…
$ finale                 <chr> "21-Sep-10", "4-Oct-11", "16-Oct-12", "2…
$ winner                 <chr> "Edd Kimber", "Joanne Wheatley", "John W…
$ avg_uk_viewers         <dbl> 2.77, 4.00, 5.00, 7.35, 10.04, 12.50, 13…
$ day                    <chr> "Tuesday", "Tuesday", "Tuesday", "Tuesda…
$ timeslot               <time> 20:00:00, 20:00:00, 20:00:00, 20:00:00,…
$ channel                <chr> "BBC Two", "BBC Two", "BBC Two", "BBC Tw…
$ runner_up_1            <chr> "Miranda Gore Browne", "Holly Bell", "Br…
$ runner_up_2            <chr> "Ruth Clemens", "Mary-Anne Boermans", "J…
$ season                 <dbl> NA, NA, NA, 1, 2, 3, 4, NA
$ season_premiere        <chr> NA, NA, NA, "12/28/14", "9/6/15", "7/1/1…
$ season_finale          <chr> NA, NA, NA, "3/1/15", "11/8/15", "8/12/1…
$ gbbo_e1_viewers_7day   <dbl> 2.24, 3.10, 3.85, 6.60, 8.51, 11.62, 13.…
$ gbbo_e1_viewers_28day  <dbl> NA, NA, NA, NA, NA, 11.73, 13.86, 9.72
$ gbbo_e2_viewers_7day   <dbl> 3.00, 3.53, 4.60, 6.65, 8.79, 11.59, 13.…
$ gbbo_e2_viewers_28day  <dbl> NA, NA, NA, NA, NA, 11.84, 13.74, 9.53
$ gbbo_e3_viewers_7day   <dbl> 3.00, 3.82, 4.53, 7.17, 9.28, 12.01, 13.…
$ gbbo_e3_viewers_28day  <dbl> NA, NA, NA, NA, NA, NA, 13.38, 9.06
$ gbbo_e4_viewers_7day   <dbl> 2.60, 3.60, 4.71, 6.82, 10.25, 12.36, 13…
$ gbbo_e4_viewers_28day  <dbl> NA, NA, NA, NA, NA, 12.67, 13.88, 8.87
$ gbbo_e5_viewers_7day   <dbl> 3.03, 3.83, 4.61, 6.95, 9.95, 12.39, 13.…
$ gbbo_e5_viewers_28day  <dbl> NA, NA, NA, NA, NA, 12.63, 13.33, 8.98
$ gbbo_e6_viewers_7day   <dbl> 2.75, 4.25, 4.82, 7.32, 10.13, 12.00, 13…
$ gbbo_e6_viewers_28day  <dbl> NA, NA, NA, NA, NA, 12.26, 13.41, 8.91
$ gbbo_e7_viewers_7day   <dbl> NA, 4.42, 5.10, 7.76, 10.28, 12.35, 13.4…
$ gbbo_e7_viewers_28day  <dbl> NA, NA, NA, NA, NA, 12.58, 13.72, 9.32
$ gbbo_e8_viewers_7day   <dbl> NA, 5.060, 5.350, 7.410, 9.023, 11.090, …
$ gbbo_e8_viewers_28day  <dbl> NA, NA, NA, NA, NA, 11.35, 13.45, 9.19
$ gbbo_e9_viewers_7day   <dbl> NA, NA, 5.70, 7.41, 10.67, 12.65, 13.44,…
$ gbbo_e9_viewers_28day  <dbl> NA, NA, NA, NA, NA, 12.78, 13.65, 9.16
$ gbbo_e10_viewers_7day  <dbl> NA, NA, 6.74, 9.45, 13.51, 15.05, 15.90,…
$ gbbo_e10_viewers_28day <dbl> NA, NA, NA, NA, NA, 15.16, 16.03, 10.13
$ na                     <chr> "8/17/10", "8/16/11", "8/14/12", "8/20/1…
$ na_2                   <chr> "8/24/10", "8/23/11", "8/21/12", "8/27/1…
$ na_3                   <chr> "8/31/10", "8/30/11", "8/28/12", "9/3/13…
$ na_4                   <chr> "9/7/10", "9/6/11", "9/4/12", "9/10/13",…
$ na_5                   <chr> "9/14/10", "9/13/11", "9/11/12", "9/17/1…
$ na_6                   <chr> "9/21/10", "9/20/11", "9/18/12", "9/24/1…
$ na_7                   <chr> NA, "9/27/11", "9/25/12", "10/1/13", "9/…
$ na_8                   <chr> NA, "10/4/11", "10/2/12", "10/8/13", "9/…
$ na_9                   <chr> NA, NA, "10/9/12", "10/15/13", "10/1/14"…
$ na_10                  <chr> NA, NA, "10/16/12", "10/22/13", "10/8/14…

You have a ton of case options with the clean_names function- choose wisely!

2.4.2 Rename and subset variables

In the next chapter, when we reshape data to be tidy, you’ll see the importance of having tame variable names.

Now that our variable names in ratings have been converted to snake_case, you’ll practice combining renaming with select() helper functions to take advantage of the variable name structure.

To rename a group of variables, you only need to specify a prefix of the new name.

For example, my_tbl %>% select( new_name_ = starts_with(“oldname”) ) will find all variables in my_tbl whose names start with oldname, enumerate them, then rename each variable as new_name_, where N is a number. If my_tbl has variables oldname, oldname_v1, oldname3, running the statement above will replace these names with new_name_1, new_name_2, new_name_3. Notice how we only specified new_name_ in select(), and the numbers were appended automatically!

Take a second to glimpse() at ratings in your console, and take note of the variables that store data about the number of viewers. Can you see the pattern in their names? In this exercise, you will first select those variables using a helper function, and then rename them!

# Select 7-day viewer data by series
viewers_7day <- ratings %>%
    select(series, ends_with("7day"))
    

# Glimpse
glimpse(viewers_7day)
Rows: 6
Columns: 11
$ series           <dbl> 3, 4, 5, 6, 7, 8
$ e1_viewers_7day  <dbl> 3.85, 6.60, 8.51, 11.62, 13.58, 9.46
$ e2_viewers_7day  <dbl> 4.60, 6.65, 8.79, 11.59, 13.45, 9.23
$ e3_viewers_7day  <dbl> 4.53, 7.17, 9.28, 12.01, 13.01, 8.68
$ e4_viewers_7day  <dbl> 4.71, 6.82, 10.25, 12.36, 13.29, 8.55
$ e5_viewers_7day  <dbl> 4.61, 6.95, 9.95, 12.39, 13.12, 8.61
$ e6_viewers_7day  <dbl> 4.82, 7.32, 10.13, 12.00, 13.13, 8.61
$ e7_viewers_7day  <dbl> 5.10, 7.76, 10.28, 12.35, 13.45, 9.01
$ e8_viewers_7day  <dbl> 5.350, 7.410, 9.023, 11.090, 13.260, 8.950
$ e9_viewers_7day  <dbl> 5.70, 7.41, 10.67, 12.65, 13.44, 9.03
$ e10_viewers_7day <dbl> 6.74, 9.45, 13.51, 15.05, 15.90, 10.04
# Adapt code to also rename 7-day viewer data
viewers_7day <- ratings %>% 
    select(series, viewers_7day_ = ends_with("7day"))

# Glimpse
glimpse(viewers_7day)
Rows: 6
Columns: 11
$ series          <dbl> 3, 4, 5, 6, 7, 8
$ viewers_7day_1  <dbl> 3.85, 6.60, 8.51, 11.62, 13.58, 9.46
$ viewers_7day_2  <dbl> 4.60, 6.65, 8.79, 11.59, 13.45, 9.23
$ viewers_7day_3  <dbl> 4.53, 7.17, 9.28, 12.01, 13.01, 8.68
$ viewers_7day_4  <dbl> 4.71, 6.82, 10.25, 12.36, 13.29, 8.55
$ viewers_7day_5  <dbl> 4.61, 6.95, 9.95, 12.39, 13.12, 8.61
$ viewers_7day_6  <dbl> 4.82, 7.32, 10.13, 12.00, 13.13, 8.61
$ viewers_7day_7  <dbl> 5.10, 7.76, 10.28, 12.35, 13.45, 9.01
$ viewers_7day_8  <dbl> 5.350, 7.410, 9.023, 11.090, 13.260, 8.950
$ viewers_7day_9  <dbl> 5.70, 7.41, 10.67, 12.65, 13.44, 9.03
$ viewers_7day_10 <dbl> 6.74, 9.45, 13.51, 15.05, 15.90, 10.04

Using select helper functions is a powerful way to tame your variable names. Tame names make it easier to tidy your data.

2.4.3 Rename and reorder variables

In the previous exercise, you renamed all the variables in ratings that end with “7day” and kept only a subset of variables. In this exercise, you’ll adapt that code to drop some specific columns, while keeping and reordering other columns.

# Adapt code to drop 28-day columns; keep 7-day in front
viewers_7day <- ratings %>% 
    select(viewers_7day_ = ends_with("7day"),
        everything(),
        -ends_with("28day"))
        
# Glimpse
glimpse(viewers_7day)
Rows: 6
Columns: 36
$ viewers_7day_1  <dbl> 3.85, 6.60, 8.51, 11.62, 13.58, 9.46
$ viewers_7day_2  <dbl> 4.60, 6.65, 8.79, 11.59, 13.45, 9.23
$ viewers_7day_3  <dbl> 4.53, 7.17, 9.28, 12.01, 13.01, 8.68
$ viewers_7day_4  <dbl> 4.71, 6.82, 10.25, 12.36, 13.29, 8.55
$ viewers_7day_5  <dbl> 4.61, 6.95, 9.95, 12.39, 13.12, 8.61
$ viewers_7day_6  <dbl> 4.82, 7.32, 10.13, 12.00, 13.13, 8.61
$ viewers_7day_7  <dbl> 5.10, 7.76, 10.28, 12.35, 13.45, 9.01
$ viewers_7day_8  <dbl> 5.350, 7.410, 9.023, 11.090, 13.260, 8.950
$ viewers_7day_9  <dbl> 5.70, 7.41, 10.67, 12.65, 13.44, 9.03
$ viewers_7day_10 <dbl> 6.74, 9.45, 13.51, 15.05, 15.90, 10.04
$ series          <dbl> 3, 4, 5, 6, 7, 8
$ episodes        <dbl> 10, 10, 10, 10, 10, 10
$ premiere        <chr> "14-Aug-12", "20-Aug-13", "6-Aug-14", "5-Aug-15…
$ finale          <chr> "16-Oct-12", "22-Oct-13", "8-Oct-14", "7-Oct-15…
$ winner          <chr> "John Whaite", "Frances Quinn", "Nancy Birtwhis…
$ avg_uk_viewers  <dbl> 5.00, 7.35, 10.04, 12.50, 13.85, 9.29
$ day_of_week     <chr> "Tuesday", "Tuesday", "Wednesday", "Wednesday",…
$ timeslot        <time> 20:00:00, 20:00:00, 20:00:00, 20:00:00, 20:00:…
$ channel         <chr> "BBC Two", "BBC Two", "BBC One", "BBC One", "BB…
$ runner_up_1     <chr> "Brendan Lynch", "Kimberley Wilson", "Luis Troy…
$ runner_up_2     <chr> "James Morton", "Ruby Tandoh", "Richard Burr", …
$ season          <dbl> NA, 1, 2, 3, 4, NA
$ season_premiere <chr> NA, "12/28/14", "9/6/15", "7/1/16", "6/16/17", …
$ season_finale   <chr> NA, "3/1/15", "11/8/15", "8/12/16", "8/4/17", NA
$ e1_uk_airdate   <chr> "8/14/12", "8/20/13", "8/6/14", "8/5/15", "8/24…
$ e2_uk_airdate   <chr> "8/21/12", "8/27/13", "8/13/14", "8/12/15", "8/…
$ e3_uk_airdate   <chr> "8/28/12", "9/3/13", "8/20/14", "8/19/15", "9/7…
$ e4_uk_airdate   <chr> "9/4/12", "9/10/13", "8/27/14", "8/26/15", "9/1…
$ e5_uk_airdate   <chr> "9/11/12", "9/17/13", "9/3/14", "9/2/15", "9/21…
$ e6_uk_airdate   <chr> "9/18/12", "9/24/13", "9/10/14", "9/9/15", "9/2…
$ e7_uk_airdate   <chr> "9/25/12", "10/1/13", "9/17/14", "9/16/15", "10…
$ e8_uk_airdate   <chr> "10/2/12", "10/8/13", "9/24/14", "9/23/15", "10…
$ e9_uk_airdate   <chr> "10/9/12", "10/15/13", "10/1/14", "9/30/15", "1…
$ e10_uk_airdate  <chr> "10/16/12", "10/22/13", "10/8/14", "10/7/15", "…
$ viewer_growth   <dbl> 2.89, 2.85, 5.00, 3.43, 2.32, 0.58
$ bbc             <fct> 1, 1, 1, 1, 1, 0
# Adapt code to keep original order
viewers_7day <- ratings %>% 
    select(everything(),
           viewers_7day_ = ends_with("7day"), 
           -ends_with("28day"))

# Glimpse
glimpse(viewers_7day)
Rows: 6
Columns: 36
$ series          <dbl> 3, 4, 5, 6, 7, 8
$ episodes        <dbl> 10, 10, 10, 10, 10, 10
$ premiere        <chr> "14-Aug-12", "20-Aug-13", "6-Aug-14", "5-Aug-15…
$ finale          <chr> "16-Oct-12", "22-Oct-13", "8-Oct-14", "7-Oct-15…
$ winner          <chr> "John Whaite", "Frances Quinn", "Nancy Birtwhis…
$ avg_uk_viewers  <dbl> 5.00, 7.35, 10.04, 12.50, 13.85, 9.29
$ day_of_week     <chr> "Tuesday", "Tuesday", "Wednesday", "Wednesday",…
$ timeslot        <time> 20:00:00, 20:00:00, 20:00:00, 20:00:00, 20:00:…
$ channel         <chr> "BBC Two", "BBC Two", "BBC One", "BBC One", "BB…
$ runner_up_1     <chr> "Brendan Lynch", "Kimberley Wilson", "Luis Troy…
$ runner_up_2     <chr> "James Morton", "Ruby Tandoh", "Richard Burr", …
$ season          <dbl> NA, 1, 2, 3, 4, NA
$ season_premiere <chr> NA, "12/28/14", "9/6/15", "7/1/16", "6/16/17", …
$ season_finale   <chr> NA, "3/1/15", "11/8/15", "8/12/16", "8/4/17", NA
$ viewers_7day_1  <dbl> 3.85, 6.60, 8.51, 11.62, 13.58, 9.46
$ viewers_7day_2  <dbl> 4.60, 6.65, 8.79, 11.59, 13.45, 9.23
$ viewers_7day_3  <dbl> 4.53, 7.17, 9.28, 12.01, 13.01, 8.68
$ viewers_7day_4  <dbl> 4.71, 6.82, 10.25, 12.36, 13.29, 8.55
$ viewers_7day_5  <dbl> 4.61, 6.95, 9.95, 12.39, 13.12, 8.61
$ viewers_7day_6  <dbl> 4.82, 7.32, 10.13, 12.00, 13.13, 8.61
$ viewers_7day_7  <dbl> 5.10, 7.76, 10.28, 12.35, 13.45, 9.01
$ viewers_7day_8  <dbl> 5.350, 7.410, 9.023, 11.090, 13.260, 8.950
$ viewers_7day_9  <dbl> 5.70, 7.41, 10.67, 12.65, 13.44, 9.03
$ viewers_7day_10 <dbl> 6.74, 9.45, 13.51, 15.05, 15.90, 10.04
$ e1_uk_airdate   <chr> "8/14/12", "8/20/13", "8/6/14", "8/5/15", "8/24…
$ e2_uk_airdate   <chr> "8/21/12", "8/27/13", "8/13/14", "8/12/15", "8/…
$ e3_uk_airdate   <chr> "8/28/12", "9/3/13", "8/20/14", "8/19/15", "9/7…
$ e4_uk_airdate   <chr> "9/4/12", "9/10/13", "8/27/14", "8/26/15", "9/1…
$ e5_uk_airdate   <chr> "9/11/12", "9/17/13", "9/3/14", "9/2/15", "9/21…
$ e6_uk_airdate   <chr> "9/18/12", "9/24/13", "9/10/14", "9/9/15", "9/2…
$ e7_uk_airdate   <chr> "9/25/12", "10/1/13", "9/17/14", "9/16/15", "10…
$ e8_uk_airdate   <chr> "10/2/12", "10/8/13", "9/24/14", "9/23/15", "10…
$ e9_uk_airdate   <chr> "10/9/12", "10/15/13", "10/1/14", "9/30/15", "1…
$ e10_uk_airdate  <chr> "10/16/12", "10/22/13", "10/8/14", "10/7/15", "…
$ viewer_growth   <dbl> 2.89, 2.85, 5.00, 3.43, 2.32, 0.58
$ bbc             <fct> 1, 1, 1, 1, 1, 0

3 Tidy your data

3.1 Introduction to Tidy Data

3.1.1 Plot untidy data

We’ll start by visualizing the number of show viewers (in millions, cumulative across seven days) across series by episode using the ratings data

ratings <- read_csv("Ratings/messy_ratings.csv",
                     col_types = cols(
                    series = col_factor(levels = NULL)
                    ))
# Plot of episode 1 viewers by series
ggplot(ratings, aes(x = series, y = e1))+
 geom_col()

# Adapt code to plot episode 2 viewers by series
ggplot(ratings, aes(x = series, y = e2)) +
    geom_col()

Series 8 was the first with different hosts on a different channel. Do you notice a pattern? To draw any conclusions, we’d want to plot the viewers for every episode though- aren’t you glad you weren’t asked to make all 10 plots? Wouldn’t it be nice if episode was a variable we could use?

3.2 Gather

3.2.1 Gather & plot

we’ll tidy the data, then use row_number() from the dplyr package to create a continuous episode count variable, such that episode 1 from series 1 will have a value of 1, episode 1 from series 2 will have a value of 7, and so on. This new variable will be mapped onto the x-axis using ggplot2.

library(tidyr)
tidy_ratings <- ratings %>%
    # Gather and convert episode to factor
    gather(key = "episode", value = "viewers_7day", -series, 
           factor_key = TRUE, na.rm = TRUE) %>%
    # Sort in ascending order by series and episode
    arrange(series, episode) %>% 
    # Create new variable using row_number()
    mutate(episode_count = row_number())

# Plot viewers by episode and series
ggplot(tidy_ratings, aes(x = episode_count, 
                y = viewers_7day, 
                fill = series)) +
    geom_col()

Beautiful plot! In series 8, one of the judges accidentally tweeted out the winner’s name 12 hours before the finale aired. Do you think that hurt their finale viewer numbers?

3.2.2 Gather & plot non-sequential columns

In this exercise, you’ll combine tidyr with dplyr::select() to keep and gather non-sequential columns in the ratings2 data. You can review the select helper functions here. The ratings2 data includes per-episode data:

  • e*_7day: viewers within a 7-day window, and
  • e*_28day: viewers within a 28-day window.

Here, we’ll focus on the 7-day viewers.

ratings2 <- read_csv("Ratings/messy_ratings2.csv",
                     col_types = cols(
                    series = col_factor(levels = NULL)
                    ))
week_ratings <- ratings2  %>% 
    # Select 7-day viewer ratings
    select(series, ends_with("7day")) %>% 
    # Gather 7-day viewers by episode
    gather(episode, viewers_7day, ends_with("7day"), na.rm = TRUE, factor_key = TRUE)
# Plot 7-day viewers by episode and series
ggplot(week_ratings, aes(x = episode, 
                y = viewers_7day, 
                group = series)) +
    geom_line() +
    facet_wrap(~series) 

You’re done gathering! This plot tells you a lot, but the x-axis labels are a mess! We need to do more tidying to make this plot better.

3.3 Separate

3.3.1 Separate a column

To create the week_ratings data, we first selected all the columns in ratings2 that contain data about the number of 7-day viewers in millions for each series, then we gathered those 10 columns into two columns.

In this exercise, you’ll use the tidyr, dplyr, and readr packages to tidy and plot the week_ratings data so we can read the x-axis, which labels each episode!

week_ratings <- ratings2 %>% 
    select(series, ends_with("7day")) %>% 
    gather(episode, viewers_7day, ends_with("7day"), 
           na.rm = TRUE) %>% 
    separate(episode, into = "episode", extra = "drop") %>% 
    # Edit to parse episode number
    mutate(episode = parse_number(episode))

# Print to view
week_ratings
# Create week_ratings
week_ratings <- ratings2 %>% 
    select(series, ends_with("7day")) %>% 
    gather(episode, viewers_7day, ends_with("7day"), 
           na.rm = TRUE) %>% 
    separate(episode, into = "episode", extra = "drop") %>% 
    mutate(episode = parse_number(episode))
    
# Edit your code to color by series and add a theme
ggplot(week_ratings, aes(x = episode, y = viewers_7day, 
                         group = series, color = series)) +
    geom_line() +
    facet_wrap(~series) +
    guides(color = FALSE) +
    theme_minimal()

3.3.2 Unite columns

In the tidyr package, the opposite of separate() is unite(). Sometimes you need to paste values from two or more columns together to tidy. Here is an example usage for unite():

data %>%
 unite(new_var, old_var1, old_var2) 

The ratings2 dataset includes these variables:

  • series: 1-8
  • episode: 1-10
  • viewers_millions: whole number of 7-day viewers in millions
  • viewers_decimal: additional number of 7-days viewers in millions

If viewers_millions = 2 and viewers_decimal = .6, then the total number of viewers in millions is 2.6. In this exercise, you’ll practice uniting these two columns into a single new column.

ratings_2 <- readRDS("ratings_2.rds")
ratings3 <- ratings_2 %>% 
    # Unite viewers in millions and decimals together    
    unite(viewers_7day, viewers_millions, viewers_decimal)

# Print to view
ratings3
ratings3 <- ratings_2  %>% 
    # Adapt to change the separator
    unite(viewers_7day, viewers_millions, viewers_decimal, sep = "")

# Print to view
ratings3
ratings3 <- ratings_2  %>% 
    # Unite and change the separator
    unite(viewers_7day, viewers_millions, viewers_decimal, sep = "") %>%
    # Adapt to cast viewers as a number
    mutate(viewers_7day = parse_number(viewers_7day))

# Print to view
ratings3

3.4 Spread

3.4.1 Spread rows into columns

In this exercise, you’ll use the tidyr, dplyr, and readr packages to tidy the ratings2 data after using dplyr::count(). The tidy_ratings_all data should have four variables to work with:

  • series (integer: 1-8),
  • episode (integer: 1-10),
  • days (integer: 7 or 28), and
  • viewers (numeric).
# Create tidy data with 7- and 28-day viewers
tidy_ratings_all <- ratings2 %>%
    gather(key = episode, value = viewers, ends_with("day"), na.rm = TRUE) %>% 
    separate(episode, into = c("episode", "days")) %>%  
    mutate(episode = parse_number(episode),
           days = parse_number(days))

Using your new tidy data, count the number of viewers grouping by series and days. Remember: you don’t need to use group_by before count, but you do need to use the wt = argument to sum the values in the viewers column.

tidy_ratings_all %>%
    # Count viewers by series and days
    count(series, days, wt = viewers)
tidy_7day <- tidy_ratings_all %>% 
    # Count viewers by series and days
    count(series, days, wt = viewers) %>%
    # Adapt to spread counted values
      spread(key = days, value = n, sep = "_")

The sep = "_" argument is especially helpful when you are spreading by a numeric variable so that you don’t end up with variable names that start with numbers.

3.5 Tidy multiple sets of columns

3.5.1 Masterclass: Tidy

In the last three exercises, you’ll tidy the ratings data to create a scatterplot to see the relationship between the number of premiere and finale UK viewers (7-day only) by series.

# Gather viewer columns and remove NA rows
tidy_ratings <- ratings %>%
    gather(episode, viewers, -series, na.rm = TRUE) %>%
    mutate(episode = parse_number(episode)) %>% 
    group_by(series) %>% 
    filter(episode == 1 | episode == max(episode)) %>% 
    ungroup()
tidy_ratings

Now we have a tidy version of the ratings data with only premiere and finale episodes. Let’s see if that tweet spoiled the Series 8 finale.

In the previous exercise, you gathered, then did a grouped filter to make tidy_ratings. In this exercise, you’ll make two charts in ggplot2 with this tidy data to see if the judge’s tweet spoiled the Series 8 finale ratings.

The two plots you’ll make, a slope chart and a dumbbell chart, are nice ways to show change between two data points combining geom_point() and geom_line().

You’ll use these to visualize the “finale bump” in viewers between the premiere and finale episodes across series.

first_last <- tidy_ratings %>% 
  mutate(episode = recode(episode, `1` = "first", .default = "last")) 
first_last
# Fill in to make slope chart
ggplot(first_last, aes(x = episode, y = viewers, color = series)) +
  geom_point() +
  geom_line(aes(group = series))

# Switch the variables mapping x-axis and color
ggplot(first_last, aes(x = series, y = viewers, color = episode)) +
  geom_point() + # keep
  geom_line(aes(group = series)) + # keep
  coord_flip() # keep

Great work- notice how each of these two plots relied on data that is tidy! Series 8 had a pretty small ‘finale bump’, but we are still looking at raw changes in viewers. Let’s visualize percentage changes in viewers instead.

In the previous exercise, you visualized the size of the “finale bump” in viewers for each series. From those charts, we know that finale episodes always garner more viewers than premieres. We also know that the change in viewers for series 8 seemed smaller compared to some previous series.

But the question of whether the series 8 finale viewers were “spoiled” might be best answered by comparing the relative percentage changes in premiere and finale viewers across series. In this exercise, you’ll reshape first_last once more to calculate and visualize the relative finale bump.

# Calculate relative increase in viewers
bump_by_series <- first_last %>% 
  spread(episode, viewers) %>%   
  mutate(bump = (last - first) / first)
bump_by_series
# Calculate relative increase in viewers
bump_by_series <- first_last %>% 
  spread(episode, viewers) %>%   
  mutate(bump = (last - first) / first)
  
# Fill in to make bar chart of bumps by series
ggplot(bump_by_series, aes(x = series, y = bump)) +
  geom_col() +
  scale_y_continuous(labels = scales::percent) # converts to %

Series 8 definitely had the lowest ‘finale bump’ so far- only 6% more viewers watched the finale than the premiere. Notice that we had to spread here, because the tidy version of the data depends on the question you want to ask.

4 Transform your data

4.1 Complex recoding with case_when

4.1.1 Combine two variables

In this exercise, you’ll use case_when() with the bakers data to create a new variable based on the number of times each baker was crowned as star baker or the technical challenge winner:

  • If star baker is greater than technical wins, recode as super_star
  • If star baker is less than technical wins, recode as high_tech
  • Recode the rest as well_rounded
bakers <- read_csv("Bakers/baker_results.csv",
                   col_types = cols(series = col_factor(levels = NULL),
                                    series_winner = col_factor(levels = NULL))
                   )
head(bakers)
# Create skill variable with 3 levels
bakers_skill <- bakers %>% 
  mutate(skill = case_when(
    star_baker > technical_winner ~ "super_star",
    star_baker < technical_winner ~ "high_tech",
    TRUE ~ "well_rounded"
  ))
  
# Filter zeroes to examine skill variable
bakers_skill %>% 
  filter(star_baker == 0 & technical_winner == 0) %>% 
  count(skill)

Whoops- we may not want bakers who never won star baker or the technical challenge to be called ‘well-rounded’. Let’s add another level to take care of this!

4.1.2 Add another bin

In this exercise, you’ll edit your previous code so that skill has four levels:

  • If both star baker and technical wins are zero, recode as NA_character_
  • If star baker is greater than technical wins, recode as super_star
  • If star baker is less than technical wins, recode as high_tech
  • If star baker is equal to technical wins, recode as well_rounded
# Add pipe to drop skill = NA
bakers_skill <- bakers %>% 
  mutate(skill = case_when(
    star_baker > technical_winner ~ "super_star",
    star_baker < technical_winner ~ "high_tech",
    star_baker == 0 & technical_winner == 0 ~ NA_character_,
    star_baker == technical_winner  ~ "well_rounded"
  )) %>% 
  drop_na(skill)
  
# Count bakers by skill
bakers_skill %>%
  count(skill)

Very nice- now we only have 15 ‘well-rounded’ bakers, and we dropped the 41 bakers who never won star baker or a technical challenge.

4.2 Factors

4.2.1 Cast a factor and examine levels

In this exercise, you’ll start where you left off in the last series of exercises, working with the bakers data to examine your new categorical variable called skill, which has three levels: well_rounded, super_star, and high_tech.

# Cast skill as a factor
bakers <- bakers_skill %>% 
  mutate(skill = as.factor(skill))

# Examine levels
levels(bakers$skill)

Sometimes factors have a natural order to them- like beginner, intermediate, and advanced- but sometimes they don’t. Know your data, and know your factors!

4.2.2 Plot factor counts

In this exercise, you’ll use ggplot2 to make a bar chart with one per level of the skill factor we just made, where the height of the bar shows the number of bakers in each skill level.

Notice a big difference between the data here and the data we have used before to make a bar chart- here, you want the height of the bars to be represent the number of rows in the bakers data in each skill level. This is compared to bar charts we made where the height represented values in the data (when you used geom_col()).

library(forcats)
# Edit to reverse x-axis order
ggplot(bakers, aes(x = fct_rev(skill), fill = series_winner)) +
  geom_bar()

Note that when you use function like fct_rev(), forcats converts the character to a factor for your plot

4.3 Dates

4.3.1 Cast characters as dates

We can use lubridate to parse and cast a date variable within a mutate(). In this exercise, you’ll practice doing this with the baker_dates data, and then go even further to extract data from the dates like the labelled month. Remember to use ?month to read about the month() function and its arguments.

baker_dates <- readRDS("baker_dates.rds")
head(baker_dates)
library(lubridate)

Attaching package: ‘lubridate’

The following objects are masked from ‘package:base’:

    date, intersect, setdiff, union
# Add a line to extract labeled month
baker_dates_cast <- baker_dates %>% 
  mutate(last_date_appeared_us = dmy(last_date_appeared_us),
         last_month_us = month(last_date_appeared_us, label = TRUE))
         
# Make bar chart by last month
ggplot(baker_dates_cast,aes(x = last_month_us)) +
  geom_bar()

There are more functions like year for getting out other date components- you can even extract fiscal quarters or semesters! Look at the documentation to see more ways.

4.3.2 Calculate timespans

The first step to calculating a timespan in lubridate is to make an interval, then use division to convert the units to what you want (like weeks(x) or months(x)). The x refers to the number of time units to be included in the period. In this exercise, you’ll calculate timespans with the baker_time data we just made.

# Add a line to create whole months on air variable
baker_time <- baker_dates  %>% 
  mutate(time_on_air = interval(first_date_appeared_uk, last_date_appeared_uk),
         weeks_on_air = time_on_air / weeks(1),
         months_on_air = time_on_air %/% months(1))
baker_time

Here again, lubridate gives you many options for working with periods from years down to picoseconds. You may also end up needing to work with durations too, which have similar functions that always start with a d from dyears to dpicoseconds.

4.4 Strings

4.4.1 Wrangle a character variable

In this exercise, we’ll wrangle the values in the position_reached variable, which looks like this right now:

messy_baker_results <- read_csv("Bakers/messy_baker_results.csv")
Parsed with column specification:
cols(
  .default = col_character(),
  series = col_double(),
  star_baker = col_double(),
  technical_winner = col_double(),
  technical_top3 = col_double(),
  technical_bottom = col_double(),
  technical_highest = col_double(),
  technical_lowest = col_double(),
  technical_median = col_double(),
  series_winner = col_double(),
  series_runner_up = col_double(),
  total_episodes_appeared = col_double(),
  percent_episodes_appeared = col_double(),
  percent_technical_top3 = col_double(),
  first_date_appeared_uk = col_date(format = ""),
  last_date_appeared_uk = col_date(format = ""),
  first_date_us = col_date(format = ""),
  last_date_us = col_date(format = ""),
  e_1_technical = col_double(),
  e_10_technical = col_double(),
  e_2_technical = col_double()
  # ... with 7 more columns
)
See spec(...) for full column specifications.
messy_baker_results %>% 
  count(position_reached)

Let’s clean these strings up!

library(stringr)
# Add another mutate to replace "THIRD PLACE" with "RUNNER UP"and count
bakers_2 <- messy_baker_results %>% 
  mutate(position_reached = str_to_upper(position_reached),
         position_reached = str_replace(position_reached, "-", " "),
         position_reached = str_replace(position_reached, "THIRD PLACE", "RUNNER UP"))

# Count rows
bakers_2 %>%
  count(position_reached)

Great work! Each of the 8 series had one winner and two runner-ups, so now this variable is much easier to work with!

4.4.2 Detect a string pattern

we’ll wrangle each bakers’ occupation from the bakers data, which is currently a character string like this:

bakers_2 %>% 
  select(baker, occupation)

We’ll create a logical variable (TRUE or FALSE) indicating whether or not each baker is a student.

# Add a line to create new variable called student
bakers_3 <- bakers_2 %>% 
    mutate(occupation = str_to_lower(occupation), 
           student = str_detect(occupation, "student"))

# Find all students and examine occupations
bakers_3 %>%
  filter(student == TRUE) %>%
  select(baker, occupation, student)

Rav’s occupation was student support, but we classified him as a student. This shows a common problem in working with data- it helps to always check your work!

LS0tCnRpdGxlOiAiV29ya2luZyB3aXRoIERhdGEgaW4gdGhlIFRpZHl2ZXJzZSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2NvbGxhcHNlZDogZmFsc2UKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgCnRvY19kZXB0aDogMwotLS0KIyBFeHBsb3JlIHlvdXIgZGF0YQoKIyMgSW1wb3J0IHlvdXIgZGF0YQoKIyMjIFJlYWQgYSBDU1YgZmlsZQoKSW4gdGhpcyBleGVyY2lzZSwgeW91J2xsIHVzZSByZWFkX2NzdigpIHR3aWNlLiBUaGUgZmlyc3QgdGltZSB5b3Ugd2lsbCBvbmx5IHNwZWNpZnkgdGhlIGZpbGVuYW1lLCBidXQgeW91J2xsIG5vdGljZSBhIHByb2JsZW0gd2l0aCB0aGUgaW1wb3J0ZWQgZGF0YS4gVGhlIHNlY29uZCB0aW1lIHlvdSdsbCB1c2UgYSBuZXcgYXJndW1lbnQgY2FsbGVkIHNraXAgdG8gZml4IHRoZSBwcm9ibGVtLiBSZW1lbWJlciB0byB1c2UgP3JlYWRfY3N2IHRvIHJlYWQgbW9yZSBhYm91dCBhcmd1bWVudHMgbGlrZSBza2lwIGFuZCBob3cgdG8gdXNlIHRoZW0uCgpUaGUgZGF0YSB5b3UnbGwgd29yayB3aXRoIGlzIGZyb20gIlRoZSBHcmVhdCBCcml0aXNoIEJha2UtT2ZmLiIgVGhlIGZpbGUgImJha2VvZmYuY3N2IiBjb250YWlucyBkYXRhIGZvciBlYWNoIGVwaXNvZGUgb2YgdGhlIHNob3csIG9yZ2FuaXplZCBieSBzZXJpZXMgYW5kIGJha2VyLgoKW1RpZHl2ZXJzZSBjaGVhcnNoZWV0XShodHRwczovL2RhdGFjYW1wLWNvbW11bml0eS1wcm9kLnMzLmFtYXpvbmF3cy5jb20vZTYzYThmNmItMmFhMy00MDA2LTg5ZTAtYmFkYzI5NGIxNzljKQoKYGBge3J9CiMgTG9hZCByZWFkcgpsaWJyYXJ5KHJlYWRyKQoKIyBDcmVhdGUgYmFrZW9mZiBmcm9tICJiYWtlb2ZmLmNzdiIKYmFrZW9mZiA8LSByZWFkX2NzdigiYmFrZW9mZi5jc3YiKQoKIyBQcmludCBiYWtlb2ZmCmJha2VvZmYKCiMgQ3JlYXRlIGJha2VvZmYgYnV0IHNraXAgZmlyc3Qgcm93IGluIGNhc2UgbmVlZGVkCiMgYmFrZW9mZiA8LSByZWFkX2NzdigiYmFrZW9mZi5jc3YiLCBza2lwID0gMSkKYGBgCk91ciBvcmlnaW5hbCBpbXBvcnQgaGFkIDMgcHJvYmxlbXM6IHdlIGhhZCAxIGV4dHJhIG9ic2VydmF0aW9uLCBvdXIgZmlyc3Qgcm93IHN0b3JlZCB0aGUgdHJ1ZSB2YXJpYWJsZSBuYW1lcywgYW5kIGFsbCBjb2x1bW4gdHlwZXMgd2VyZSBjaGFyYWN0ZXJzLiBBZGRpbmcgc2tpcCA9IDEgd29ya2VkIGJlY2F1c2UgdGhlIGRlZmF1bHQgYXJndW1lbnQgZm9yIGNvbF9uYW1lcyBpcyBUUlVFLgoKIyMjIEFzc2lnbiBtaXNzaW5nIHZhbHVlcwoKVGhlIHJlYWRfY3N2KCkgZnVuY3Rpb24gYWxzbyBoYXMgYW4gbmEgYXJndW1lbnQsIHdoaWNoIGFsbG93cyB5b3UgdG8gc3BlY2lmeSB2YWx1ZShzKSB0aGF0IHJlcHJlc2VudCBtaXNzaW5nIHZhbHVlcyBpbiB5b3VyIGRhdGEuIFRoZSBkZWZhdWx0IHZhbHVlcyBmb3IgdGhlIG5hIGFyZ3VtZW50IGFyZSBjKCIiLCAiTkEiKSwgc28gYm90aCBhcmUgcmVjb2RlZCBhcyBtaXNzaW5nIChOQSkgaW4gUi4gV2hlbiB5b3UgcmVhZCBpbiBkYXRhLCB5b3UgY2FuIGFkZCBhZGRpdGlvbmFsIHZhbHVlcyBsaWtlIHRoZSBzdHJpbmcgIlVOS05PV04iIHRvIGEgdmVjdG9yIG9mIG1pc3NpbmcgdmFsdWVzIHVzaW5nIHRoZSBjKCkgZnVuY3Rpb24gdG8gY29tYmluZSBtdWx0aXBsZSB2YWx1ZXMgaW50byBhIHNpbmdsZSB2ZWN0b3IuCgpUaGUgaXMubmEoKSBmdW5jdGlvbiBpcyBhbHNvIGhlbHBmdWwgZm9yIGlkZW50aWZ5aW5nIHJvd3Mgd2l0aCBtaXNzaW5nIHZhbHVlcyBmb3IgYSB2YXJpYWJsZS4KCmBgYHtyfQojIExvYWQgZHBseXIKbGlicmFyeShkcGx5cikKCiMgRmlsdGVyIHJvd3Mgd2hlcmUgc2hvd3N0b3BwZXIgaXMgVU5LTk9XTiAKYmFrZW9mZiAlPiUgCiAgICBmaWx0ZXIoc2hvd3N0b3BwZXIgPT0gIlVOS05PV04iKQoKIyBFZGl0IHRvIGFkZCBsaXN0IG9mIG1pc3NpbmcgdmFsdWVzCmJha2VvZmYgPC0gcmVhZF9jc3YoImJha2VvZmYuY3N2IiwgbmEgPSBjKCIiLCAiTkEiLCAiVU5LTk9XTiIpKQoKIyBGaWx0ZXIgcm93cyB3aGVyZSBzaG93c3RvcHBlciBpcyBOQSAKYmFrZW9mZiAlPiUKICBmaWx0ZXIoaXMubmEoc2hvd3N0b3BwZXIpKQpgYGAKV2Ugb25seSBoYWQgNCBtaXNzaW5nIHZhbHVlcyBmb3IgdGhlIHNob3dzdG9wcGVyIHZhcmlhYmxlIHRvIHN0YXJ0LCBidXQgbm93IGFsbCAyMSBhcmUgcHJlc2VudCBhbmQgYWNjb3VudGVkIGZvci4KCiMjIEtub3cgeW91ciBkYXRhCgojIyMgQXJyYW5nZSBhbmQgZ2xpbXBzZQoKWW91IGNhbiBjb21iaW5lIGdsaW1wc2UoKSB3aXRoIG90aGVyIGZ1bmN0aW9ucyBpbiBhIHNlcXVlbmNlIHVzaW5nIHRoZSBwaXBlICglPiUpIG9wZXJhdG9yLiBGb3IgZXhhbXBsZSwgeW91IGNhbiB1c2Ugb3RoZXIgZHBseXIgZnVuY3Rpb25zIGxpa2UgYXJyYW5nZSBmaXJzdCwgdGhlbiB1c2UgZ2xpbXBzZSBieSBhZGRpbmcgYSBsaW5lIGFmdGVyIHRoZSBmaW5hbCBwaXBlICglPiUpOgoKICAgIGJha2Vyc19taW5pICU+JSAKICAgICAgYXJyYW5nZShhZ2UpICU+JSAKICAgICAgZ2xpbXBzZSgpICMgbm8gYXJndW1lbnQgbmVlZGVkIGhlcmUKClRha2UgYSBnbGltcHNlIG9mIHRoZSBiYWtlb2ZmIGRhdGEgd2UgaW1wb3J0ZWQgaW4gdGhlIGZpcnN0IHNldCBvZiBleGVyY2lzZXMuIE9uIHdoaWNoIGRhdGUgZGlkIHRoZSBmaXJzdCBlcGlzb2RlIG9mIHRoZSBzaG93IGFpciBpbiB0aGUgVVM/CmBgYHtyfQojIEZpbHRlciBhbmQgc2tpbQpiYWtlb2ZmICU+JQogIGZpbHRlcighaXMubmEodXNfc2Vhc29uKSkgJT4lIAogIHNraW0oKQpgYGAKVGhlIGZpcnN0IGVwaXNvZGUgb2YgdGhlIGZpcnN0IFVTIHNlYXNvbiBhaXJlZCBvbiBEZWNlbWJlciAyOCwgMjAxNC4KCiMjIyBTdW1tYXJpemUgeW91ciBkYXRhCgpZb3UgY2FuIGNvbWJpbmUgc2tpbSgpIHdpdGggb3RoZXIgZnVuY3Rpb25zIGluIGEgc2VxdWVuY2UgdXNpbmcgdGhlIHBpcGUgKCU+JSkgb3BlcmF0b3IuIEZvciBleGFtcGxlLCB5b3UgY291bGQgdXNlIG90aGVyIGRwbHlyIGZ1bmN0aW9ucyBsaWtlIGdyb3VwX2J5IGZpcnN0LCB0aGVuIHVzZSBza2ltKCkgYnkgYWRkaW5nIGEgbGluZSBhZnRlciB0aGUgZmluYWwgcGlwZS4KCiAgICBiYWtlcnNfbWluaSAlPiUgCiAgICAgIGdyb3VwX2J5KHNlcmllcykgJT4lIAogICAgICBza2ltKCkgIyBubyBhcmd1bWVudCBuZWVkZWQgaGVyZQoKVGhpcyB3aWxsIHByb2R1Y2Ugc3VtbWFyeSBzdGF0aXN0aWNzIGZvciBlYWNoIHNlcmllcy4gTGV0J3MgcHJhY3RpY2UgdGhpcyEKYGBge3J9CiMgTG9hZCBza2ltcgpsaWJyYXJ5KHNraW1yKQpgYGAKCmBgYHtyfQpiYWtlb2ZmICU+JSAKICBncm91cF9ieShzZXJpZXMpICU+JSAKICBza2ltKCkgIyBubyBhcmd1bWVudCBuZWVkZWQgaGVyZQpgYGAKYGBge3J9CiMgRWRpdCB0byBmaWx0ZXIsIGdyb3VwIGJ5LCBhbmQgc2tpbQpiYWtlb2ZmICU+JSAKICBmaWx0ZXIoIWlzLm5hKHVzX3NlYXNvbikpICU+JSAKICBncm91cF9ieSh1c19zZWFzb24pICAlPiUgCiAgc2tpbSgpCmBgYApOaWNlIHBpcGluZyAmIHNraW1taW5nISBQcm9kdWNpbmcgcXVpY2sgZ3JvdXBlZCBzdW1tYXJpZXMgd2l0aCB0aGUgc2tpbSBmdW5jdGlvbiBpcyBhIHJlYWwgdGltZSAoYW5kIHNhbml0eSkgc2F2ZXIuCgojIyMgS25vdyB5b3VyIHZhcmlhYmxlIHR5cGVzCgpIb3cgbWFueSB2YXJpYWJsZXMgb2YgZWFjaCB0eXBlIGRvIHdlIGhhdmUgaW4gdGhlIGJha2VvZmYgZGF0YT8gWW91IG1heSBhbHNvIHdhbnQgdG8gdHJ5IHBpcGluZyBhIHNraW1tZWQgb2JqZWN0IHRvIHN1bW1hcnkoKSwgYWxzbyBmcm9tIHRoZSBza2ltciBwYWNrYWdlOgoKICAgIHRpYmJsZV9uYW1lICU+JSAKICAgICAgc2tpbSgpICU+JSAgIyBubyBhcmd1bWVudCBuZWVkZWQgaGVyZQogICAgICBzdW1tYXJ5KCkgIyBubyBhcmd1bWVudCBuZWVkZWQgaGVyZQoKYGBge3J9CmJha2VvZmYgJT4lIAogIHNraW0oKSAlPiUgICMgbm8gYXJndW1lbnQgbmVlZGVkIGhlcmUKICBzdW1tYXJ5KCkgIyBubyBhcmd1bWVudCBuZWVkZWQgaGVyZQpgYGAKIyMgQ291bnQgd2l0aCB5b3VyIGRhdGEKCkluIGV2ZXJ5IGVwaXNvZGUgb2YgIlRoZSBHcmVhdCBCcml0aXNoIEJha2UtT2ZmIiwgYmFrZXJzIGNvbXBsZXRlIDMgY2hhbGxlbmdlcyBhbmQgdGhlIHNob3cncyBqdWRnZXMgYXdhcmQgdGhlIHRpdGxlICJTdGFyIEJha2VyIiB0byB0aGUgYmFrZXIgd2hvIGV4Y2VsbGVkIGluIHRoYXQgd2VlaydzIGNoYWxsZW5nZXMgKHdpdGggdGhlIGV4Y2VwdGlvbiBvZiB0aGUgZmluYWxlKS4gRWFjaCBiYWtlcidzIHJlc3VsdCBmb3IgZXZlcnkgZXBpc29kZSBpcyBzdG9yZWQgaW4gYmFrZW9mZi0gcmVzdWx0IGlzIGEgY2hhcmFjdGVyIHZhcmlhYmxlLCBhbmQgdGhlIHZhbHVlICJTQiIgc3RhbmRzIGZvciBzdGFyIGJha2VyLgoKCiMjIyBEaXN0aW5jdCBhbmQgY291bnQKCmBgYHtyfQojIFZpZXcgZGlzdGluY3QgcmVzdWx0cwpiYWtlb2ZmICU+JQogIGRpc3RpbmN0KHJlc3VsdCkKYGBgCmBgYHtyfQojIENvdW50IHdoZXRoZXIgb3Igbm90IHN0YXIgYmFrZXIKYmFrZW9mZiAlPiUgCiAgY291bnQocmVzdWx0KQpgYGAKYGBge3J9CiMgQ291bnQgd2hldGhlciBvciBub3Qgc3RhciBiYWtlcgpiYWtlb2ZmICU+JSAKICBjb3VudChyZXN1bHQgPT0gIlNCIikKYGBgClRoZXJlIGFyZSA0ODggcmVzdWx0cyB0aGF0IGFyZSBub3Qgc3RhciBiYWtlci4KCiMjIyBDb3VudCBlcGlzb2RlcwoKSGVyZSdzIGhvdyB3ZSBjYW4gdXNlIGNvdW50KCkgYmFjay10by1iYWNrIHRvIHJvbGwgdXAgYSBsZXZlbCBvZiBkZXRhaWwgYWNyb3NzIHNlcmllczoKCiAgICBiYWtlcnMgJT4lIAogICAgICBjb3VudChhaXJlZF91cywgc2VyaWVzKSAlPiUgCiAgICAgIGNvdW50KGFpcmVkX3VzKQogICAgICAKTGV0J3MgcHJhY3RpY2UgY291bnRpbmcgdGhlIG51bWJlciBvZiBlcGlzb2RlcyBwZXIgc2VyaWVzLgpgYGB7cn0KYmFrZW9mZiAlPiUgCiAgY291bnQoc2VyaWVzLCBlcGlzb2RlKSAlPiUgCiAgY291bnQoc2VyaWVzKQpgYGAKWW91IGNhbiBzZWUgdGhhdCBieSBzZXJpZXMgMyB3ZSB3ZXJlIHRyZWF0ZWQgdG8gMTAgZXBpc29kZXMgd2l0aCBlYWNoIG5ldyBzZXJpZXMhCgojIyMgQ291bnQgYmFrZXJzCgoKV2UnbGwgcHJhY3RpY2UgdGhhdCBhZ2FpbiwgZm9jdXNpbmcgb24gY291bnRpbmcgYmFrZXJzLgpgYGB7cn0KIyBDb3VudCB0aGUgbnVtYmVyIG9mIHJvd3MgYnkgc2VyaWVzIGFuZCBiYWtlcgpiYWtlcnNfYnlfc2VyaWVzIDwtIGJha2VvZmYgJT4lCiAgICAgICAgICAgICAgICAgICAgICBjb3VudChzZXJpZXMsIGJha2VyKQoKCiMgUHJpbnQgdG8gdmlldwpiYWtlcnNfYnlfc2VyaWVzCgojIENvdW50IHRoZSBudW1iZXIgb2Ygcm93cyBieSBzZXJpZXMgYW5kIGJha2VyCmJha2Vyc19ieV9zZXJpZXMgPC0gYmFrZW9mZiAlPiUgCiAgY291bnQoc2VyaWVzLCBiYWtlcikKICAKIyBQcmludCB0byB2aWV3CmJha2Vyc19ieV9zZXJpZXMKICAKIyBDb3VudCBhZ2FpbiBieSBzZXJpZXMKYmFrZXJzX2J5X3NlcmllcyAlPiUKICBjb3VudChzZXJpZXMpCgojIENvdW50IGFnYWluIGJ5IGJha2VyCmJha2Vyc19ieV9zZXJpZXMgJT4lCiAgY291bnQoYmFrZXIsIHNvcnQgPSBUUlVFKQpgYGAKU2VyaWVzIDQgaGFkIHRoZSBtb3N0IGJha2VycyB3aXRoIDEzIHRvdGFsIChhIGJha2VyJ3MgZG96ZW4hKS4gS2F0ZSBpcyB0aGUgbW9zdCBwb3B1bGFyIG5hbWUtIEthdGVzIGhhdmUgYXBwZWFyZWQgaW4gMyBkaWZmZXJlbnQgc2VyaWVzIG9mIHRoZSBUViBzaG93LgoKIyMjIFBsb3QgY291bnRzCgpZb3UgY2FuIGxlYXJuIGEgbG90IGFib3V0IHlvdXIgZGF0YSBieSBjb3VudGluZywgYnV0IHNvbWV0aW1lcyB5b3UgY2FuIGxlYXJuIGV2ZW4gbW9yZSBieSBwbG90dGluZyBjb3VudHMuIFRoaXMgaXMgZXNwZWNpYWxseSB0cnVlIHdoZW4geW91IGhhdmUgbG90cyBvZiB0aGluZ3MgdG8gY291bnQhIFdpdGggZWlnaHQgc2VyaWVzLCA3NCBlcGlzb2RlcywgYW5kIDk1IGJha2VycywgYSBwbG90IGNhbiBiZSBtb3JlIGhlbHBmdWwgdGhhbiBhIHRhYmxlIG9mIG51bWJlcnMuIFdlJ2xsIHVzZSBnZ3Bsb3QyIHRvIHZpc3VhbGl6ZSB0aGUgbnVtYmVyIG9mIGJha2VycyBhY3Jvc3MgZXBpc29kZXMgZm9yIGVhY2ggc2VyaWVzIGZyb20gYmFrZW9mZi4KYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKCmdncGxvdChiYWtlb2ZmLCBhZXMoZXBpc29kZSkpICsgCiAgICBnZW9tX2JhcigpICsgCiAgICBmYWNldF93cmFwKH5zZXJpZXMpCmBgYApZb3UgbWFkZSBpdCB0aHJvdWdoIHRoZSBmaXJzdCBjaGFwdGVyISBXZSBoYXZlIGEgbG90IG9mIHdvcmsgbGVmdCB0byBkbyB3aXRoIFRoZSBHcmVhdCBCcml0aXNoIEJha2UgT2ZmIGRhdGEsIGJ1dCBub3RpY2UgaG93IHRoaXMgcGxvdCBpcyBhIGdyZWF0IHNhbml0eSBjaGVjazogdGhlIG51bWJlciBvZiBiYWtlcnMgdGVuZHMgdG8gZ28gZG93biB3aXRoIGV2ZXJ5IGVwaXNvZGUgKG5ldmVyIHVwISksIHdpdGggYSBmZXcgZXhjZXB0aW9ucyB3aGVyZSBubyBvbmUgd2FzIGVsaW1pbmF0ZWQuIEFsc28sIHlvdSBjYW4gc2VlIHRoYXQgdGhlcmUgYXJlIGZld2VyIGVwaXNvZGVzIGluIHRoZSBmaXJzdCB0d28gc2VyaWVzLiBGaW5hbGx5LCB5b3UgY2FuIHNlZSB0aGF0IHRoZXJlIGFyZSBhbHdheXMgdGhyZWUgYmFrZXJzIHdobyBtYWtlIGl0IHRvIHRoZSBzZXJpZXMgZmluYWxlLgoKIyBUYW1lIHlvdXIgZGF0YQoKIyMgQ2FzdCBjb2x1bW4gdHlwZXMKCiMjIyBDYXN0IGEgY29sdW1uIHRvIGEgZGF0ZQoKQSBnb29kIHdvcmtmbG93IGZvciBwYXJzaW5nIGRhdGVzIHVzaW5nIHJlYWRyIGlzIHRvLCBmb3IgZXhhbXBsZToKCi0gVXNlIHBhcnNlX2RhdGUoIjIwMTItMTQtMDgiLCBmb3JtYXQgPSAiJVktJWQtJW0iKSBmaXJzdCwgdGhlbgotIFVzZSBjb2xfZGF0ZShmb3JtYXQgPSAiJVktJWQtJW0iKSB3aXRoaW4gY29scygpIGFzIHRoZSBjb2xfdHlwZXMgYXJndW1lbnQgb2YgcmVhZF9jc3YoKS4KCkluICJkZXNzZXJ0cy5jc3YiLCB0aGUgdmFyaWFibGUgdWtfYWlyZGF0ZSBpcyBmb3JtYXR0ZWQgbGlrZSAiMTcgQXVndXN0IDIwMTAiLiBMZXQncyBwYXJzZSwgdGhlbiBjYXN0IHRoaXMgdmFyaWFibGUhCgpMYXRlciBvbiBpbiBDaGFwdGVyIDQsIHdlJ2xsIHNob3djYXNlIHNvbWUgZnVuY3Rpb25zIGZyb20gdGhlIGx1YnJpZGF0ZSBwYWNrYWdlIHRvIGhlbHAgeW91IGV4dHJhY3QgZGF0YSBmcm9tIGRhdGVzLCBvbmNlIHRoZXkgYXJlIGNhc3QgcHJvcGVybHkuCmBgYHtyfQojIEZpbmQgZm9ybWF0IHRvIHBhcnNlIHVrX2FpcmRhdGUgCnBhcnNlX2RhdGUoIjE3IEF1Z3VzdCAyMDEwIiwgZm9ybWF0ID0gIiVkICVCICVZIikKCiMgRWRpdCB0byBjYXN0IHVrX2FpcmRhdGUKZGVzc2VydHMgPC0gcmVhZF9jc3YoImRlc3NlcnRzLmNzdiIsIAogICAgICAgICAgICAgICAgICAgICBjb2xfdHlwZXMgPSBjb2xzKAogICAgICAgICAgICAgICAgICAgICAgIHVrX2FpcmRhdGUgPSBjb2xfZGF0ZShmb3JtYXQgPSAiJWQgJUIgJVkiKQogICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgKQoKIyBBcnJhbmdlIGJ5IGRlc2NlbmRpbmcgdWtfYWlyZGF0ZQpkZXNzZXJ0cyAlPiUgCglhcnJhbmdlKGRlc2ModWtfYWlyZGF0ZSkpCmBgYApOb3RpY2UgdGhhdCB1c19haXJkYXRlIGRpZG4ndCBuZWVkIHRvIGJlIGNhc3QgLSB0aGlzIGlzIGJlY2F1c2UgdGhlIGRhdGUgZm9ybWF0ICVZLSVtLSVkIGlzIHVuYW1iaWd1b3VzLCBzbyBpdCBpcyBhdXRvbWF0aWNhbGx5IHBhcnNlZCBhcyBhIGRhdGUgYnkgcmVhZHIuCgojIyMgQ2FzdCBhIGNvbHVtbiB0byBhIG51bWJlcgoKV2Ugc2F3IGEgZ29vZCB3b3JrZmxvdyBmb3IgcGFyc2luZyBjb2x1bW5zIHVzaW5nIHJlYWRyOgoKLSBVc2UgcGFyc2VfbnVtYmVyKCkgdG8gcHJhY3RpY2UsIHRoZW4KLSBVc2UgY29sX251bWJlcigpIHRvIGNhc3QuCgpCdXQgc29tZXRpbWVzIHlvdSdsbCBuZWVkIHRvIHN0YXJ0IHdpdGggY2FzdGluZywgdGhlbiBkaWFnbm9zZSBwYXJzaW5nIHByb2JsZW1zIHVzaW5nIGEgbmV3IHJlYWRyIGZ1bmN0aW9uIGNhbGxlZCBwcm9ibGVtcygpLiBVc2luZyBwcm9ibGVtcygpIG9uIGEgcmVzdWx0IG9mIHJlYWRfY3N2KCkgd2lsbCBzaG93IHlvdSB0aGUgcm93cyBhbmQgY29sdW1ucyB3aGVyZSBwYXJzaW5nIGVycm9yIG9jY3VycmVkLCB3aGF0IHRoZSBwYXJzZXIgZXhwZWN0ZWQgdG8gZmluZCAoZm9yIGV4YW1wbGUsIGEgbnVtYmVyKSwgYW5kIHRoZSBhY3R1YWwgdmFsdWUgdGhhdCBjYXVzZWQgdGhlIHBhcnNpbmcgZXJyb3IuCgpMZXQncyBwcmFjdGljZSB0aGlzIHdpdGggImRlc3NlcnRzLmNzdiIsIHdoaWNoIGluY2x1ZGVzIGRhdGEgYWJvdXQgdGhlIGp1ZGdlcycgcmFua3MgZm9yIGVhY2ggYmFrZXIgaW4gdGhlIHRlY2huaWNhbCBjaGFsbGVuZ2UsIGZvciBldmVyeSBlcGlzb2RlLgoKYGBge3J9CiMgVHJ5IHRvIGNhc3QgdGVjaG5pY2FsIGFzIGEgbnVtYmVyCmRlc3NlcnRzIDwtIHJlYWRfY3N2KCJkZXNzZXJ0cy5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgY29sX3R5cGVzID0gY29scygKICAgICAgICAgICAJCQkgICB1a19haXJkYXRlID0gY29sX2RhdGUoZm9ybWF0ID0gIiVkICVCICVZIiksCiAgICAgICAgICAgICAgICAgICAgICAgdGVjaG5pY2FsID0gY29sX251bWJlcigpCiAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICApCgojIFZpZXcgcGFyc2luZyBwcm9ibGVtcwpkZXNzZXJ0cyAlPiUKICBwcm9ibGVtcygpCmBgYApgYGB7cn0KIyBFZGl0IGNvZGUgdG8gZml4IHRoZSBwYXJzaW5nIGVycm9yIApkZXNzZXJ0cyA8LSByZWFkX2NzdigiZGVzc2VydHMuY3N2IiwKICAgICAgICAgICAgICAgICAgICAgIGNvbF90eXBlcyA9IGNvbHMoCiAgICAgICAgICAgICAgICAgICAgICAgIHVrX2FpcmRhdGUgPSBjb2xfZGF0ZShmb3JtYXQgPSAiJWQgJUIgJVkiKSwKICAgICAgICAgICAgICAgICAgICAgICAgdGVjaG5pY2FsID0gY29sX251bWJlcigpCiAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICBuYSA9IGMoIiIsICJOQSIsICJOL0EiKSAjc29sdmluZyB0aGUgcHJvYmxlbQogICAgICAgICAgICAgICAgICAgICApCgojIFZpZXcgcGFyc2luZyBwcm9ibGVtcwpwcm9ibGVtcyhkZXNzZXJ0cykKYGBgCiMjIyBDYXN0IGEgY29sdW1uIGFzIGEgZmFjdG9yCgpGYWN0b3JzIGFyZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMsIHdoZXJlIHRoZSBwb3NzaWJsZSB2YWx1ZXMgYXJlIGEgZml4ZWQgYW5kIGtub3duIHNldC4gRm9yIGV4YW1wbGUsIHRha2UgYSBzaW1wbGUgZmFjdG9yIGxpa2UgYmFrZSBiZWxvdzoKCmBgYHtyfQpiYWtlIDwtIGMoInBpZSIsICJjYWtlIiwgIm5laXRoZXIiKSAKcGFyc2VfZmFjdG9yKGJha2UsIGxldmVscyA9IE5VTEwpIApgYGAKWW91IGNhbiB1c2UgcGFyc2VfZmFjdG9yKCkgdG8gcGFyc2UgdmFyaWFibGVzIGFuZCBjb2xfZmFjdG9yKCkgdG8gY2FzdCBjb2x1bW5zIGFzIGNhdGVnb3JpY2FsLiBCb3RoIGZ1bmN0aW9ucyBoYXZlIGEgbGV2ZWxzIGFyZ3VtZW50IHRoYXQgaXMgdXNlZCB0byBzcGVjaWZ5IHRoZSBwb3NzaWJsZSB2YWx1ZXMgZm9yIHRoZSBmYWN0b3JzLiBXaGVuIGxldmVscyBpcyBzZXQgdG8gTlVMTCwgdGhlIHBvc3NpYmxlIHZhbHVlcyB3aWxsIGJlIGluZmVycmVkIGZyb20gdGhlIHVuaXF1ZSB2YWx1ZXMgaW4gdGhlIGRhdGFzZXQuIEFsdGVybmF0aXZlbHksIHlvdSBjYW4gcGFzcyBhIGxpc3Qgb2YgcG9zc2libGUgdmFsdWVzLgoKVGhpcyB0aW1lLCB5b3Ugd2lsbCBsb29rIGF0IHRoZSByZXN1bHQgY29sdW1uLCB3aGljaCBjb250YWlucyB0aGUgb3V0Y29tZSBvZiB0aGUgY29tcGV0aXRpb24gZm9yIGVhY2ggYmFrZXIuCmBgYHtyfQojIENhc3QgcmVzdWx0IGEgZmFjdG9yCmRlc3NlcnRzIDwtIHJlYWRfY3N2KCJkZXNzZXJ0cy5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgbmEgPSBjKCIiLCAiTkEiLCAiTi9BIiksCiAgICAgICAgICAgICAgICAgICAgIGNvbF90eXBlcyA9IGNvbHMoCiAgICAgICAgICAgICAgICAgICAgICAgdWtfYWlyZGF0ZSA9IGNvbF9kYXRlKGZvcm1hdCA9ICIlZCAlQiAlWSIpLAogICAgICAgICAgICAgICAgICAgICAgIHRlY2huaWNhbCA9IGNvbF9udW1iZXIoKSwgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9IGNvbF9mYWN0b3IobGV2ZWxzID0gTlVMTCkKICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAKIyBHbGltcHNlIHRvIHZpZXcKZ2xpbXBzZShkZXNzZXJ0cykKYGBgCldoaWxlIGl0IHRha2VzIGEgbG90IG9mIHdvcmsgdXAgZnJvbnQsIGNhc3RpbmcgY29sdW1uIHR5cGVzIHdoZW4geW91IGltcG9ydCBjYW4gbWFrZSB5b3VyIGFuYWx5c2VzIGVhc2llciB0byByZXByb2R1Y2UuCgojIyBSZWNvZGUgdmFsdWVzCgoKIyMjIFJlY29kZSBhIGNoYXJhY3RlciB2YXJpYWJsZQoKSW4gdGhpcyBleGVyY2lzZSwgeW91J2xsIHJlY29kZSgpIHRoZSBudXQgdmFyaWFibGUgaW4gdGhlIGRlc3NlcnRzIGRhdGEuIFRoaXMgaXMgYSBjaGFyYWN0ZXIgdmFyaWFibGUgdGhhdCB0ZWxscyB1cywgZm9yIGVhY2ggYmFrZSwgd2hldGhlciBhIG51dCB3YXMgYSBrZXkgaW5ncmVkaWVudCBhbmQgaWYgc28sIHdoYXQga2luZCBvZiBudXQhCgpSZW1lbWJlciB0aGF0IHRoZSBhcmd1bWVudHMgb2YgcmVjb2RlKCkgYXJlIHRoZSB2YXJpYWJsZSB0aGF0IHlvdSB3YW50IHRvIHJlY29kZSwgYW5kIHRoZW4gYW4gZXhwcmVzc2lvbiBvZiB0aGUgZm9ybSBvbGRfdmFsdWUgPSBuZXdfdmFsdWUuIHJlY29kZSgpIGlzIG1vc3QgdXNlZnVsIHdoZW4gdXNlZCBpbnNpZGUgYSBtdXRhdGUoKSB0byBjcmVhdGUgYSBuZXcgdmFyaWFibGUgb3IgcmVhc3NpZ24gdGhlIG9sZCBvbmUuCgpPZnRlbiB5b3Ugd2FudCB0byByZWNvZGUgYSBjZXJ0YWluIHZhbHVlIGFzIG1pc3NpbmcuIEluIHRoaXMgY2FzZSwgaXQgaXMgaW1wb3J0YW50IHRvIHByZXNlcnZlIHRoZSBkYXRhIHR5cGUgb2YgdGhlIHZhcmlhYmxlLiBSIGhhcyBidWlsdC1pbiB3b3JkcyB0byBpbmRpY2F0ZSBtaXNzaW5nIGRhdGEgb2YgdmFyaW91cyB0eXBlcywgZm9yIGV4YW1wbGUgTkFfY2hhcmFjdGVyXyBmb3IgbWlzc2luZyB2YWx1ZSBmb3IgYSBjaGFyYWN0ZXIgdmFyaWFibGUsIG9yIE5BX2ludGVnZXJfIGZvciBtaXNzaW5nIGludGVnZXIgZGF0YS4gVGhlIE5BIHZhbHVlIGlzIG9mIHRoZSBsb2dpY2FsIHR5cGUuCgpSZW1lbWJlciB0aGF0IHlvdSBjYW4gdXNlID9mdW5jdGlvbl9uYW1lX2hlcmUgdG8gcmVhZCBtb3JlIGFib3V0IGFyZ3VtZW50cyBhbmQgaG93IHRvIHVzZSB0aGVtLgoKYGBge3J9CiMgQ291bnQgcm93cyBncm91cGluZyBieSBudXQgdmFyaWFibGUKZGVzc2VydHMgJT4lIAogICAgY291bnQoc2lnbmF0dXJlX251dCwgc29ydCA9IFRSVUUpCiAgICAKIyBFZGl0IGNvZGUgdG8gcmVjb2RlICJubyBudXQiIGFzIG1pc3NpbmcKZGVzc2VydHNfMiA8LSBkZXNzZXJ0cyAlPiUgCiAgbXV0YXRlKG51dCA9IHJlY29kZShzaWduYXR1cmVfbnV0LCAiZmlsYmVydCIgPSAiaGF6ZWxudXQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5vIG51dCIgPSBOQV9jaGFyYWN0ZXJfKSkKCiMgQ291bnQgcm93cyBhZ2FpbiAKZGVzc2VydHNfMiAlPiUgCiAgICBjb3VudChudXQsIHNvcnQgPSBUUlVFKQpgYGAKIyMjIFJlY29kZSBhIG51bWVyaWMgdmFyaWFibGUKCkR1bW15IHZhcmlhYmxlcyBhcmUgb2Z0ZW4gdXNlZCBpbiBkYXRhIGFuYWx5c2lzIHRvIGJpbiBhIHZhcmlhYmxlIGludG8gb25lIG9mIHR3byBjYXRlZ29yaWVzIHRvIGluZGljYXRlIHRoZSBhYnNlbmNlIG9yIHByZXNlbmNlIG9mIHNvbWV0aGluZy4gRHVtbXkgdmFyaWFibGVzIHRha2UgdGhlIHZhbHVlIDAgb3IgMSB0byBzdGFuZCBmb3IsIGZvciBleGFtcGxlLCBsb3NlciBvciB3aW5uZXIuIER1bW15IHZhcmlhYmxlcyBhcmUgb2Z0ZW4gZmFjdG9yIHZhcmlhYmxlcyBhcyBvcHBvc2VkIHRvIG51bWVyaWMgLSB3ZSdsbCBjb3ZlciBtb3JlIGFib3V0IGZhY3RvcnMgaW4gdGhlIGxhc3QgY2hhcHRlci4KCkluIGRlc3NlcnRzLCB0aGUgdGVjaG5pY2FsIHZhcmlhYmxlIGlzIGEgbnVtYmVyIHJlcHJlc2VudGluZyB0aGUganVkZ2VzJyByYW5rcyBmb3IgZWFjaCBiYWtlciBpbiB0aGUgdGVjaG5pY2FsIGNoYWxsZW5nZSwgZm9yIGV2ZXJ5IGVwaXNvZGUgKHJlbWVtYmVyIHRvIHJ1biBnbGltcHNlKGRlc3NlcnRzKSB0byB0YWtlIGEgcGVlaykuIEluIHRoaXMgZXhlcmNpc2UsIHlvdSB3aWxsIHJlY29kZSB0aGUgbnVtZXJpYyB0ZWNobmljYWwgdmFyaWFibGUgdG8gYSBuZXcgZmFjdG9yIGR1bW15IHZhcmlhYmxlLgoKYGBge3J9CiMjIEVkaXQgdG8gcmVjb2RlIHRlY2hfd2luIGFzIGZhY3RvcgpkZXNzZXJ0cyA8LSBkZXNzZXJ0cyAlPiUgCiAgbXV0YXRlKHRlY2hfd2luID0gcmVjb2RlX2ZhY3Rvcih0ZWNobmljYWwsIGAxYCA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIC5kZWZhdWx0ID0gMCkpCgojIENvdW50IHRvIGNvbXBhcmUgdmFsdWVzICAgICAgICAgICAgICAgICAgICAgIApkZXNzZXJ0cyAlPiUgCiAgY291bnQodGVjaG5pY2FsID09IDEsIHRlY2hfd2luKQpgYGAKIyMgU2VsZWN0IHZhcmlhYmxlcwoKIyMjIENvbWJpbmUgZnVuY3Rpb25zIHdpdGggc2VsZWN0CgpUaGUgcmF0aW5ncyBkYXRhIHRlbGxzIHVzIGFib3V0IGhvdyBtYW55IFVLIHZpZXdlcnMgdHVuZWQgaW4gdG8gd2F0Y2ggIlRoZSBHcmVhdCBCcml0aXNoIEJha2UtT2ZmIiBmb3IgZWFjaCBlcGlzb2RlIG9mIGVhY2ggc2VyaWVzLiBJZiB5b3UgZXhwbG9yZSByYXRpbmdzIGluIGNvbnNvbGUsIHlvdSdsbCBzZWUgdGhhdCB0aGVyZSBpcyBhIHNlcGFyYXRlIHZhcmlhYmxlIGZvciB0aGUgbnVtYmVyIG9mIHZpZXdlcnMgZm9yIGVhY2ggZXBpc29kZS4gQSBrZXkgcXVlc3Rpb24gd2UnbGwgcmV0dXJuIHRvIGluIENoYXB0ZXIgMyBpczogaG93IG1hbnkgbW9yZSB2aWV3ZXJzIHdhdGNoZWQgdGhlIGZpbmFsZSB0aGFuIHRoZSBwcmVtaWVyZSBlcGlzb2RlPwoKRm9yIHNlcmllcyB3aXRoIDEwIGVwaXNvZGVzLCB3aGljaCBzaG93ZWQgdGhlIG1vc3QgZ3Jvd3RoIGluIHZpZXdlcnMgZnJvbSB0aGUgcHJlbWllcmUgdG8gdGhlIGZpbmFsZT8gV2hpY2ggc2hvd2VkIHRoZSBsZWFzdD8KYGBge3J9CnJhdGluZ3MgPC0gcmVhZF9jc3YoIlJhdGluZ3MvMDIuMDNfbWVzc3lfcmF0aW5ncy5jc3YiKQpgYGAKCmBgYHtyfQpoZWFkKHJhdGluZ3MpCmBgYApgYGB7cn0KcmF0aW5ncyA8LSByYXRpbmdzICU+JSAKICBmaWx0ZXIoZXBpc29kZXMgPT0gMTApICU+JQogIG11dGF0ZSh2aWV3ZXJfZ3Jvd3RoID0gZTEwX3ZpZXdlcnNfN2RheSAtIGUxX3ZpZXdlcnNfN2RheSkKYGBgCmBgYHtyfQpyYXRpbmdzICU+JSAKICBzZWxlY3Qoc2VyaWVzLCB2aWV3ZXJfZ3Jvd3RoKQpgYGAKCk1vc3Q6IHNlcmllcyA1LCBMZWFzdCA9IHNlcmllcyA4CgojIyMgUmVjb2RlIGZhY3RvciB0byBwbG90CgpJbiB0aGUgbGFzdCBleGVyY2lzZSwgeW91IG1heSBoYXZlIHVzZWQgbXV0YXRlKCkgdG8gZmluZCB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBudW1iZXIgb2YgcHJlbWllcmUgYW5kIGZpbmFsZSBlcGlzb2RlIHZpZXdlcnMgZm9yIGVhY2ggc2VyaWVzLiBUaGlzIHZhcmlhYmxlLCB3aGljaCBJIGNyZWF0ZWQgYW5kIGNhbGxlZCB2aWV3ZXJfZ3Jvd3RoLCBpcyBub3cgYXZhaWxhYmxlIGluIHJhdGluZ3MsIGFuZCBmZWVsIGZyZWUgdG8gY2hlY2sgb3V0IGl0LgoKTm93IGxldCdzIHByYWN0aWNlIHVzaW5nIHNlbGVjdCgpIHRvIGNoZWNrIHRoZSByZXN1bHQgb2YgYW5vdGhlciBtdXRhdGUgd2l0aCByZWNvZGVfZmFjdG9yKCkuCgpJZiB5b3UgaGF2ZSBleGFtaW5lZCByYXRpbmdzLCB5b3UgbWlnaHQgaGF2ZSBzZWVuIHRoYXQgc2VyaWVzIGhhdmUgYWlyZWQgb24gdGhyZWUgZGlmZmVyZW50IGNoYW5uZWxzOiAiQ2hhbm5lbCA0IiwgIkJCQyBPbmUiLCAiQkJDIFR3byIuCmBgYHtyfQojIFJlY29kZSBjaGFubmVsIGFzIGZhY3RvcjogYmJjICgxKSBvciBub3QgKDApCnJhdGluZ3MgPC0gcmF0aW5ncyAlPiUgCiAgbXV0YXRlKGJiYyA9IHJlY29kZV9mYWN0b3IoY2hhbm5lbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNoYW5uZWwgNCIgPSAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIC5kZWZhdWx0ID0gMSkpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAKIyBTZWxlY3QgdG8gbG9vayBhdCB2YXJpYWJsZXMgdG8gcGxvdCBuZXh0CnJhdGluZ3MgJT4lIAogIHNlbGVjdChzZXJpZXMsIGNoYW5uZWwsIGJiYywgdmlld2VyX2dyb3d0aCkKICAKIyBNYWtlIGEgZmlsbGVkIGJhciBjaGFydApnZ3Bsb3QocmF0aW5ncywgYWVzKHggPSBzZXJpZXMsIHkgPSB2aWV3ZXJfZ3Jvd3RoLCBmaWxsID0gYmJjKSkgKwogIGdlb21fY29sKCkKYGBgClJlbWVtYmVyIHRvIGxvb2sgdXAgdGhlIG90aGVyIHNlbGVjdCBoZWxwZXIgZnVuY3Rpb25zIHVzaW5nID9zZWxlY3QuIFdoYXQgaXMgZ29pbmcgb24gd2l0aCB2aWV3ZXJfZ3Jvd3RoIGluIHNlcmllcyA4PwoKIyMjIFNlbGVjdCBhbmQgcmVvcmRlciB2YXJpYWJsZXMKCkFzIHdlIGhhdmUgc2Vlbiwgc2VsZWN0aW5nIGEgc3Vic2V0IG9mIGNvbHVtbnMgdG8gcHJpbnQgY2FuIGhlbHAgeW91IGNoZWNrIHRoYXQgYSBtdXRhdGUoKSB3b3JrZWQgYXMgZXhwZWN0ZWQsIGFuZCByZWFycmFuZ2luZyBjb2x1bW5zIG5leHQgdG8gZWFjaCBvdGhlciBjYW4gaGVscCB5b3Ugc3BvdCBvYnZpb3VzIGVycm9ycyBpbiBkYXRhIGVudHJ5LgoKVGhlIHNlbGVjdCgpIGhlbHBlcnMgYXJlIGZ1bmN0aW9ucyB0aGF0IHlvdSBjYW4gdXNlIGluc2lkZSBzZWxlY3QoKSB0byBhbGxvdyB5b3UgdG8gc2VsZWN0IHZhcmlhYmxlcyBiYXNlZCBvbiB0aGVpciBuYW1lcy4KCkluIHRoaXMgZXhlcmNpc2UsIHlvdSdsbCB3b3JrIHdpdGggdGhlIHJhdGluZ3MgZGF0YSwgYW5kIHlvdSdsbCB1c2UgYSBuZXcgaGVscGVyIGZ1bmN0aW9uIGNhbGxlZCBldmVyeXRoaW5nKCkgd2hpY2ggY2FuIGJlIHVzZWZ1bCB3aGVuIHJlb3JkZXJpbmcgY29sdW1ucy4gVXNlID9ldmVyeXRoaW5nIHRvIHJlYWQgbW9yZS4gWW91IHdpbGwgbGVhcm4gbW9yZSBhYm91dCB1c2luZyBoZWxwZXIgZnVuY3Rpb25zIGluIHRoZSBuZXh0IGxlc3Nvbi4KCmBgYHtyfQojIE1vdmUgY2hhbm5lbCB0byBmaXJzdCBjb2x1bW4KcmF0aW5ncyAlPiUgCiAgc2VsZWN0KGNoYW5uZWwsIGV2ZXJ5dGhpbmcoKSkKCiMgRHJvcCA3LSBhbmQgMjgtZGF5IGVwaXNvZGUgcmF0aW5ncwpyYXRpbmdzICU+JSAKICBzZWxlY3QoLWVuZHNfd2l0aCgiZGF5IikpCgojIE1vdmUgY2hhbm5lbCB0byBmcm9udCBhbmQgZHJvcCA3LS8yOC1kYXkgZXBpc29kZSByYXRpbmdzCnJhdGluZ3MgJT4lIAogIHNlbGVjdChjaGFubmVsLCBldmVyeXRoaW5nKCksIC1lbmRzX3dpdGgoImRheSIpKQpgYGAKZXZlcnl0aGluZygpIGlzIGEgZ3JlYXQgbGl0dGxlIGhlbHBlciEgSWYgaXQgaGFkIGJlZW4gcGxhY2VkIGF0IHRoZSBlbmQsIGl0IHdvdWxkIGhhdmUgYWRkZWQgYmFjayBpbiBhbGwgdGhlIGNvbHVtbnMgdGhhdCBlbmQgd2l0aCAiZGF5Ii4gUGxhY2luZyBpdCBiZWZvcmUgZGVzZWxlY3RpbmcgY29sdW1ucywgdGhvdWdoLCBpcyBhIHJlYWwgdGltZS1zYXZlci4KCiMjIFRhbWUgdmFyaWFibGUgbmFtZXMKCiMjIyBSZWZvcm1hdCB2YXJpYWJsZXMKCldlIHNhdyBob3cgdG8gdXNlIGNsZWFuX25hbWVzKCkgZnJvbSB0aGUgamFuaXRvciBwYWNrYWdlIHRvIGNvbnZlcnQgYWxsIHZhcmlhYmxlIG5hbWVzIHRvIHNuYWtlX2Nhc2UuIEhlcmUgaXMgaG93OgoKICAgIGxpYnJhcnkoamFuaXRvcikKICAgIHlvdW5nX2Jha2VyczMgPC0geW91bmdfYmFrZXJzMyAlPiUgCiAgICAgICBjbGVhbl9uYW1lcygpCgoKVGhlIGZ1bmN0aW9uIGNsZWFuX25hbWVzKCkgdGFrZXMgYW4gYXJndW1lbnQgY2FzZSB0aGF0IGNhbiBiZSB1c2VkIHRvIGNvbnZlcnQgdmFyaWFibGUgbmFtZXMgdG8gb3RoZXIgY2FzZXMsIGxpa2UgInVwcGVyX2NhbWVsIiBvciAiYWxsX2NhcHMiLgoKTGV0J3MgcHJhY3RpY2UgdXNpbmcgdGhlIG1lc3N5X3JhdGluZ3MgdGliYmxlLiBSZW1lbWJlciB5b3UgY2FuIHVzZSA/Y2xlYW5fbmFtZXMgdG8gc2VlIHRoZSBmdW5jdGlvbiBhcmd1bWVudHMgYW5kIGhvdyB0byB1c2UgdGhlbS4KYGBge3J9Cm1lc3N5X3JhdGluZ3MgPC0gcmVhZF9jc3YoIlJhdGluZ3MvMDIuMDNfbWVzc3lfcmF0aW5ncy5jc3YiKQpgYGAKY29sbmFtZXMobWVzc3lfcmF0aW5ncykKCmBgYHtyfQpuYW1lcyhtZXNzeV9yYXRpbmdzKSA8LSBjKCJTRVJJRVMiLCAiZXBpc29kZXMiLCAicHJlbWllcmUiLCAiZmluYWxlIiwgIndpbm5lciIsICJBdmcudWt+Vmlld2VycyIsIAoiZGF5IiwgInRpbWVzbG90IiwgImNoYW5uZWwiLCAicnVubmVyLnVwfjEiLCAicnVubmVyLnVwfjIiLCAic2Vhc29uIiwgCiJzZWFzb24ucHJlbWllcmUiLCAic2Vhc29uLmZpbmFsZSIsICJHYmJvIEUxLlZpZXdlcnN+N0RheSIsICJHYmJvIEUxLlZpZXdlcnN+MjhEYXkiLCAKIkdiYm8gRTIuVmlld2Vyc343RGF5IiwgIkdiYm8gRTIuVmlld2Vyc34yOERheSIsICJHYmJvIEUzLlZpZXdlcnN+N0RheSIsIAoiR2JibyBFMy5WaWV3ZXJzfjI4RGF5IiwgIkdiYm8gRTQuVmlld2Vyc343RGF5IiwgIkdiYm8gRTQuVmlld2Vyc34yOERheSIsIAoiR2JibyBFNS5WaWV3ZXJzfjdEYXkiLCAiR2JibyBFNS5WaWV3ZXJzfjI4RGF5IiwgIkdiYm8gRTYuVmlld2Vyc343RGF5IiwgCiJHYmJvIEU2LlZpZXdlcnN+MjhEYXkiLCAiR2JibyBFNy5WaWV3ZXJzfjdEYXkiLCAiR2JibyBFNy5WaWV3ZXJzfjI4RGF5IiwgCiJHYmJvIEU4LlZpZXdlcnN+N0RheSIsICJHYmJvIEU4LlZpZXdlcnN+MjhEYXkiLCAiR2JibyBFOS5WaWV3ZXJzfjdEYXkiLCAKIkdiYm8gRTkuVmlld2Vyc34yOERheSIsICJHYmJvIEUxMC5WaWV3ZXJzfjdEYXkiLCAiR2JibyBFMTAuVmlld2Vyc34yOERheSIpCmBgYApgYGB7cn0KIyBHbGltcHNlIHRvIHNlZSB2YXJpYWJsZSBuYW1lcwpnbGltcHNlKG1lc3N5X3JhdGluZ3MpCgojIExvYWQgamFuaXRvcgpsaWJyYXJ5KGphbml0b3IpCgojIFJlZm9ybWF0IHRvIGxvd2VyIGNhbWVsY2FzZQpyYXRpbmdzMiA8LSBtZXNzeV9yYXRpbmdzICU+JQogIGNsZWFuX25hbWVzKCJsb3dlcl9jYW1lbCIpCiAgICAKIyBHbGltcHNlIG5ldyB0aWJibGUKZ2xpbXBzZShyYXRpbmdzMikKYGBgCmBgYHtyfQojIEdsaW1wc2UgdG8gc2VlIHZhcmlhYmxlIG5hbWVzCmdsaW1wc2UobWVzc3lfcmF0aW5ncykKCiMgTG9hZCBqYW5pdG9yCmxpYnJhcnkoamFuaXRvcikKCiMgUmVmb3JtYXQgdG8gc25ha2UgY2FzZQpyYXRpbmdzMiA8LSBtZXNzeV9yYXRpbmdzICU+JSAgCiAgY2xlYW5fbmFtZXMoKQoKIyBHbGltcHNlIGNsZWFuZWQgbmFtZXMKZ2xpbXBzZShyYXRpbmdzMikKYGBgCllvdSBoYXZlIGEgdG9uIG9mIGNhc2Ugb3B0aW9ucyB3aXRoIHRoZSBjbGVhbl9uYW1lcyBmdW5jdGlvbi0gY2hvb3NlIHdpc2VseSEKCiMjIyBSZW5hbWUgYW5kIHN1YnNldCB2YXJpYWJsZXMKCkluIHRoZSBuZXh0IGNoYXB0ZXIsIHdoZW4gd2UgcmVzaGFwZSBkYXRhIHRvIGJlIHRpZHksIHlvdSdsbCBzZWUgdGhlIGltcG9ydGFuY2Ugb2YgaGF2aW5nIHRhbWUgdmFyaWFibGUgbmFtZXMuCgpOb3cgdGhhdCBvdXIgdmFyaWFibGUgbmFtZXMgaW4gcmF0aW5ncyBoYXZlIGJlZW4gY29udmVydGVkIHRvIHNuYWtlX2Nhc2UsIHlvdSdsbCBwcmFjdGljZSBjb21iaW5pbmcgcmVuYW1pbmcgd2l0aCBzZWxlY3QoKSBoZWxwZXIgZnVuY3Rpb25zIHRvIHRha2UgYWR2YW50YWdlIG9mIHRoZSB2YXJpYWJsZSBuYW1lIHN0cnVjdHVyZS4KClRvIHJlbmFtZSBhIGdyb3VwIG9mIHZhcmlhYmxlcywgeW91IG9ubHkgbmVlZCB0byBzcGVjaWZ5IGEgcHJlZml4IG9mIHRoZSBuZXcgbmFtZS4KCkZvciBleGFtcGxlLCBteV90YmwgJT4lIHNlbGVjdCggbmV3X25hbWVfID0gc3RhcnRzX3dpdGgoIm9sZG5hbWUiKSApIHdpbGwgZmluZCBhbGwgdmFyaWFibGVzIGluIG15X3RibCB3aG9zZSBuYW1lcyBzdGFydCB3aXRoIG9sZG5hbWUsIGVudW1lcmF0ZSB0aGVtLCB0aGVuIHJlbmFtZSBlYWNoIHZhcmlhYmxlIGFzIG5ld19uYW1lXzxOPiwgd2hlcmUgTiBpcyBhIG51bWJlci4gSWYgbXlfdGJsIGhhcyB2YXJpYWJsZXMgb2xkbmFtZSwgb2xkbmFtZV92MSwgb2xkbmFtZTMsIHJ1bm5pbmcgdGhlIHN0YXRlbWVudCBhYm92ZSB3aWxsIHJlcGxhY2UgdGhlc2UgbmFtZXMgd2l0aCBuZXdfbmFtZV8xLCBuZXdfbmFtZV8yLCBuZXdfbmFtZV8zLiBOb3RpY2UgaG93IHdlIG9ubHkgc3BlY2lmaWVkIG5ld19uYW1lXyBpbiBzZWxlY3QoKSwgYW5kIHRoZSBudW1iZXJzIHdlcmUgYXBwZW5kZWQgYXV0b21hdGljYWxseSEKClRha2UgYSBzZWNvbmQgdG8gZ2xpbXBzZSgpIGF0IHJhdGluZ3MgaW4geW91ciBjb25zb2xlLCBhbmQgdGFrZSBub3RlIG9mIHRoZSB2YXJpYWJsZXMgdGhhdCBzdG9yZSBkYXRhIGFib3V0IHRoZSBudW1iZXIgb2Ygdmlld2Vycy4gQ2FuIHlvdSBzZWUgdGhlIHBhdHRlcm4gaW4gdGhlaXIgbmFtZXM/IEluIHRoaXMgZXhlcmNpc2UsIHlvdSB3aWxsIGZpcnN0IHNlbGVjdCB0aG9zZSB2YXJpYWJsZXMgdXNpbmcgYSBoZWxwZXIgZnVuY3Rpb24sIGFuZCB0aGVuIHJlbmFtZSB0aGVtIQpgYGB7cn0KIyBTZWxlY3QgNy1kYXkgdmlld2VyIGRhdGEgYnkgc2VyaWVzCnZpZXdlcnNfN2RheSA8LSByYXRpbmdzICU+JQoJc2VsZWN0KHNlcmllcywgZW5kc193aXRoKCI3ZGF5IikpCgkKCiMgR2xpbXBzZQpnbGltcHNlKHZpZXdlcnNfN2RheSkKYGBgCmBgYHtyfQojIEFkYXB0IGNvZGUgdG8gYWxzbyByZW5hbWUgNy1kYXkgdmlld2VyIGRhdGEKdmlld2Vyc183ZGF5IDwtIHJhdGluZ3MgJT4lIAogICAgc2VsZWN0KHNlcmllcywgdmlld2Vyc183ZGF5XyA9IGVuZHNfd2l0aCgiN2RheSIpKQoKIyBHbGltcHNlCmdsaW1wc2Uodmlld2Vyc183ZGF5KQpgYGAKVXNpbmcgc2VsZWN0IGhlbHBlciBmdW5jdGlvbnMgaXMgYSBwb3dlcmZ1bCB3YXkgdG8gdGFtZSB5b3VyIHZhcmlhYmxlIG5hbWVzLiBUYW1lIG5hbWVzIG1ha2UgaXQgZWFzaWVyIHRvIHRpZHkgeW91ciBkYXRhLgoKIyMjIFJlbmFtZSBhbmQgcmVvcmRlciB2YXJpYWJsZXMKCkluIHRoZSBwcmV2aW91cyBleGVyY2lzZSwgeW91IHJlbmFtZWQgYWxsIHRoZSB2YXJpYWJsZXMgaW4gcmF0aW5ncyB0aGF0IGVuZCB3aXRoICI3ZGF5IiBhbmQga2VwdCBvbmx5IGEgc3Vic2V0IG9mIHZhcmlhYmxlcy4gSW4gdGhpcyBleGVyY2lzZSwgeW91J2xsIGFkYXB0IHRoYXQgY29kZSB0byBkcm9wIHNvbWUgc3BlY2lmaWMgY29sdW1ucywgd2hpbGUga2VlcGluZyBhbmQgcmVvcmRlcmluZyBvdGhlciBjb2x1bW5zLgpgYGB7cn0KIyBBZGFwdCBjb2RlIHRvIGRyb3AgMjgtZGF5IGNvbHVtbnM7IGtlZXAgNy1kYXkgaW4gZnJvbnQKdmlld2Vyc183ZGF5IDwtIHJhdGluZ3MgJT4lIAogICAgc2VsZWN0KHZpZXdlcnNfN2RheV8gPSBlbmRzX3dpdGgoIjdkYXkiKSwKICAgICAgICBldmVyeXRoaW5nKCksCiAgICAgICAgLWVuZHNfd2l0aCgiMjhkYXkiKSkKICAgICAgICAKIyBHbGltcHNlCmdsaW1wc2Uodmlld2Vyc183ZGF5KQpgYGAKYGBge3J9CiMgQWRhcHQgY29kZSB0byBrZWVwIG9yaWdpbmFsIG9yZGVyCnZpZXdlcnNfN2RheSA8LSByYXRpbmdzICU+JSAKICAgIHNlbGVjdChldmVyeXRoaW5nKCksCiAgICAgICAgICAgdmlld2Vyc183ZGF5XyA9IGVuZHNfd2l0aCgiN2RheSIpLCAKICAgICAgICAgICAtZW5kc193aXRoKCIyOGRheSIpKQoKIyBHbGltcHNlCmdsaW1wc2Uodmlld2Vyc183ZGF5KQpgYGAKIyBUaWR5IHlvdXIgZGF0YQoKIyMgSW50cm9kdWN0aW9uIHRvIFRpZHkgRGF0YQoKIyMjIFBsb3QgdW50aWR5IGRhdGEKCldlJ2xsIHN0YXJ0IGJ5IHZpc3VhbGl6aW5nIHRoZSBudW1iZXIgb2Ygc2hvdyB2aWV3ZXJzIChpbiBtaWxsaW9ucywgY3VtdWxhdGl2ZSBhY3Jvc3Mgc2V2ZW4gZGF5cykgYWNyb3NzIHNlcmllcyBieSBlcGlzb2RlIHVzaW5nIHRoZSByYXRpbmdzIGRhdGEKYGBge3J9CnJhdGluZ3MgPC0gcmVhZF9jc3YoIlJhdGluZ3MvbWVzc3lfcmF0aW5ncy5jc3YiLAogICAgICAgICAgICAgICAgICAgICBjb2xfdHlwZXMgPSBjb2xzKAogICAgICAgICAgICAgICAgICAgIHNlcmllcyA9IGNvbF9mYWN0b3IobGV2ZWxzID0gTlVMTCkKICAgICAgICAgICAgICAgICAgICApKQpgYGAKYGBge3J9CiMgUGxvdCBvZiBlcGlzb2RlIDEgdmlld2VycyBieSBzZXJpZXMKZ2dwbG90KHJhdGluZ3MsIGFlcyh4ID0gc2VyaWVzLCB5ID0gZTEpKSsKIGdlb21fY29sKCkKYGBgCmBgYHtyfQojIEFkYXB0IGNvZGUgdG8gcGxvdCBlcGlzb2RlIDIgdmlld2VycyBieSBzZXJpZXMKZ2dwbG90KHJhdGluZ3MsIGFlcyh4ID0gc2VyaWVzLCB5ID0gZTIpKSArCiAgICBnZW9tX2NvbCgpCmBgYApTZXJpZXMgOCB3YXMgdGhlIGZpcnN0IHdpdGggZGlmZmVyZW50IGhvc3RzIG9uIGEgZGlmZmVyZW50IGNoYW5uZWwuIERvIHlvdSBub3RpY2UgYSBwYXR0ZXJuPyBUbyBkcmF3IGFueSBjb25jbHVzaW9ucywgd2UnZCB3YW50IHRvIHBsb3QgdGhlIHZpZXdlcnMgZm9yIGV2ZXJ5IGVwaXNvZGUgdGhvdWdoLSBhcmVuJ3QgeW91IGdsYWQgeW91IHdlcmVuJ3QgYXNrZWQgdG8gbWFrZSBhbGwgMTAgcGxvdHM/IFdvdWxkbid0IGl0IGJlIG5pY2UgaWYgZXBpc29kZSB3YXMgYSB2YXJpYWJsZSB3ZSBjb3VsZCB1c2U/CgojIyBHYXRoZXIKCiMjIyBHYXRoZXIgJiBwbG90Cgp3ZSdsbCB0aWR5IHRoZSBkYXRhLCB0aGVuIHVzZSByb3dfbnVtYmVyKCkgZnJvbSB0aGUgZHBseXIgcGFja2FnZSB0byBjcmVhdGUgYSBjb250aW51b3VzIGVwaXNvZGUgY291bnQgdmFyaWFibGUsIHN1Y2ggdGhhdCBlcGlzb2RlIDEgZnJvbSBzZXJpZXMgMSB3aWxsIGhhdmUgYSB2YWx1ZSBvZiAxLCBlcGlzb2RlIDEgZnJvbSBzZXJpZXMgMiB3aWxsIGhhdmUgYSB2YWx1ZSBvZiA3LCBhbmQgc28gb24uIFRoaXMgbmV3IHZhcmlhYmxlIHdpbGwgYmUgbWFwcGVkIG9udG8gdGhlIHgtYXhpcyB1c2luZyBnZ3Bsb3QyLgoKYGBge3J9CmxpYnJhcnkodGlkeXIpCnRpZHlfcmF0aW5ncyA8LSByYXRpbmdzICU+JQogICAgIyBHYXRoZXIgYW5kIGNvbnZlcnQgZXBpc29kZSB0byBmYWN0b3IKCWdhdGhlcihrZXkgPSAiZXBpc29kZSIsIHZhbHVlID0gInZpZXdlcnNfN2RheSIsIC1zZXJpZXMsIAogICAgICAgICAgIGZhY3Rvcl9rZXkgPSBUUlVFLCBuYS5ybSA9IFRSVUUpICU+JQoJIyBTb3J0IGluIGFzY2VuZGluZyBvcmRlciBieSBzZXJpZXMgYW5kIGVwaXNvZGUKICAgIGFycmFuZ2Uoc2VyaWVzLCBlcGlzb2RlKSAlPiUgCgkjIENyZWF0ZSBuZXcgdmFyaWFibGUgdXNpbmcgcm93X251bWJlcigpCiAgICBtdXRhdGUoZXBpc29kZV9jb3VudCA9IHJvd19udW1iZXIoKSkKCiMgUGxvdCB2aWV3ZXJzIGJ5IGVwaXNvZGUgYW5kIHNlcmllcwpnZ3Bsb3QodGlkeV9yYXRpbmdzLCBhZXMoeCA9IGVwaXNvZGVfY291bnQsIAogICAgICAgICAgICAgICAgeSA9IHZpZXdlcnNfN2RheSwgCiAgICAgICAgICAgICAgICBmaWxsID0gc2VyaWVzKSkgKwogICAgZ2VvbV9jb2woKQpgYGAKQmVhdXRpZnVsIHBsb3QhIEluIHNlcmllcyA4LCBvbmUgb2YgdGhlIGp1ZGdlcyBhY2NpZGVudGFsbHkgdHdlZXRlZCBvdXQgdGhlIHdpbm5lcidzIG5hbWUgMTIgaG91cnMgYmVmb3JlIHRoZSBmaW5hbGUgYWlyZWQuIERvIHlvdSB0aGluayB0aGF0IGh1cnQgdGhlaXIgZmluYWxlIHZpZXdlciBudW1iZXJzPwoKIyMjIEdhdGhlciAmIHBsb3Qgbm9uLXNlcXVlbnRpYWwgY29sdW1ucwoKSW4gdGhpcyBleGVyY2lzZSwgeW91J2xsIGNvbWJpbmUgdGlkeXIgd2l0aCBkcGx5cjo6c2VsZWN0KCkgdG8ga2VlcCBhbmQgZ2F0aGVyIG5vbi1zZXF1ZW50aWFsIGNvbHVtbnMgaW4gdGhlIHJhdGluZ3MyIGRhdGEuIFlvdSBjYW4gcmV2aWV3IHRoZSBzZWxlY3QgaGVscGVyIGZ1bmN0aW9ucyBoZXJlLiBUaGUgcmF0aW5nczIgZGF0YSBpbmNsdWRlcyBwZXItZXBpc29kZSBkYXRhOgoKLSBlKl83ZGF5OiB2aWV3ZXJzIHdpdGhpbiBhIDctZGF5IHdpbmRvdywgYW5kCi0gZSpfMjhkYXk6IHZpZXdlcnMgd2l0aGluIGEgMjgtZGF5IHdpbmRvdy4KCkhlcmUsIHdlJ2xsIGZvY3VzIG9uIHRoZSA3LWRheSB2aWV3ZXJzLgoKYGBge3J9CnJhdGluZ3MyIDwtIHJlYWRfY3N2KCJSYXRpbmdzL21lc3N5X3JhdGluZ3MyLmNzdiIsCiAgICAgICAgICAgICAgICAgICAgIGNvbF90eXBlcyA9IGNvbHMoCiAgICAgICAgICAgICAgICAgICAgc2VyaWVzID0gY29sX2ZhY3RvcihsZXZlbHMgPSBOVUxMKQogICAgICAgICAgICAgICAgICAgICkpCmBgYApgYGB7cn0Kd2Vla19yYXRpbmdzIDwtIHJhdGluZ3MyICAlPiUgCgkjIFNlbGVjdCA3LWRheSB2aWV3ZXIgcmF0aW5ncwogICAgc2VsZWN0KHNlcmllcywgZW5kc193aXRoKCI3ZGF5IikpICU+JSAKCSMgR2F0aGVyIDctZGF5IHZpZXdlcnMgYnkgZXBpc29kZQogICAgZ2F0aGVyKGVwaXNvZGUsIHZpZXdlcnNfN2RheSwgZW5kc193aXRoKCI3ZGF5IiksIG5hLnJtID0gVFJVRSwgZmFjdG9yX2tleSA9IFRSVUUpCmBgYApgYGB7cn0KIyBQbG90IDctZGF5IHZpZXdlcnMgYnkgZXBpc29kZSBhbmQgc2VyaWVzCmdncGxvdCh3ZWVrX3JhdGluZ3MsIGFlcyh4ID0gZXBpc29kZSwgCiAgICAgICAgICAgICAgICB5ID0gdmlld2Vyc183ZGF5LCAKICAgICAgICAgICAgICAgIGdyb3VwID0gc2VyaWVzKSkgKwogICAgZ2VvbV9saW5lKCkgKwogICAgZmFjZXRfd3JhcCh+c2VyaWVzKSAKYGBgCllvdSdyZSBkb25lIGdhdGhlcmluZyEgVGhpcyBwbG90IHRlbGxzIHlvdSBhIGxvdCwgYnV0IHRoZSB4LWF4aXMgbGFiZWxzIGFyZSBhIG1lc3MhIFdlIG5lZWQgdG8gZG8gbW9yZSB0aWR5aW5nIHRvIG1ha2UgdGhpcyBwbG90IGJldHRlci4KCiMjIFNlcGFyYXRlCgojIyMgU2VwYXJhdGUgYSBjb2x1bW4KClRvIGNyZWF0ZSB0aGUgd2Vla19yYXRpbmdzIGRhdGEsIHdlIGZpcnN0IHNlbGVjdGVkIGFsbCB0aGUgY29sdW1ucyBpbiByYXRpbmdzMiB0aGF0IGNvbnRhaW4gZGF0YSBhYm91dCB0aGUgbnVtYmVyIG9mIDctZGF5IHZpZXdlcnMgaW4gbWlsbGlvbnMgZm9yIGVhY2ggc2VyaWVzLCB0aGVuIHdlIGdhdGhlcmVkIHRob3NlIDEwIGNvbHVtbnMgaW50byB0d28gY29sdW1ucy4KCkluIHRoaXMgZXhlcmNpc2UsIHlvdSdsbCB1c2UgdGhlIHRpZHlyLCBkcGx5ciwgYW5kIHJlYWRyIHBhY2thZ2VzIHRvIHRpZHkgYW5kIHBsb3QgdGhlIHdlZWtfcmF0aW5ncyBkYXRhIHNvIHdlIGNhbiByZWFkIHRoZSB4LWF4aXMsIHdoaWNoIGxhYmVscyBlYWNoIGVwaXNvZGUhCgpgYGB7cn0Kd2Vla19yYXRpbmdzIDwtIHJhdGluZ3MyICU+JSAKICAgIHNlbGVjdChzZXJpZXMsIGVuZHNfd2l0aCgiN2RheSIpKSAlPiUgCiAgICBnYXRoZXIoZXBpc29kZSwgdmlld2Vyc183ZGF5LCBlbmRzX3dpdGgoIjdkYXkiKSwgCiAgICAgICAgICAgbmEucm0gPSBUUlVFKSAlPiUgCiAgICBzZXBhcmF0ZShlcGlzb2RlLCBpbnRvID0gImVwaXNvZGUiLCBleHRyYSA9ICJkcm9wIikgJT4lIAogICAgIyBFZGl0IHRvIHBhcnNlIGVwaXNvZGUgbnVtYmVyCiAgICBtdXRhdGUoZXBpc29kZSA9IHBhcnNlX251bWJlcihlcGlzb2RlKSkKCiMgUHJpbnQgdG8gdmlldwp3ZWVrX3JhdGluZ3MKYGBgCgpgYGB7cn0KIyBDcmVhdGUgd2Vla19yYXRpbmdzCndlZWtfcmF0aW5ncyA8LSByYXRpbmdzMiAlPiUgCiAgICBzZWxlY3Qoc2VyaWVzLCBlbmRzX3dpdGgoIjdkYXkiKSkgJT4lIAogICAgZ2F0aGVyKGVwaXNvZGUsIHZpZXdlcnNfN2RheSwgZW5kc193aXRoKCI3ZGF5IiksIAogICAgICAgICAgIG5hLnJtID0gVFJVRSkgJT4lIAogICAgc2VwYXJhdGUoZXBpc29kZSwgaW50byA9ICJlcGlzb2RlIiwgZXh0cmEgPSAiZHJvcCIpICU+JSAKICAgIG11dGF0ZShlcGlzb2RlID0gcGFyc2VfbnVtYmVyKGVwaXNvZGUpKQogICAgCiMgRWRpdCB5b3VyIGNvZGUgdG8gY29sb3IgYnkgc2VyaWVzIGFuZCBhZGQgYSB0aGVtZQpnZ3Bsb3Qod2Vla19yYXRpbmdzLCBhZXMoeCA9IGVwaXNvZGUsIHkgPSB2aWV3ZXJzXzdkYXksIAogICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBzZXJpZXMsIGNvbG9yID0gc2VyaWVzKSkgKwogICAgZ2VvbV9saW5lKCkgKwogICAgZmFjZXRfd3JhcCh+c2VyaWVzKSArCiAgICBndWlkZXMoY29sb3IgPSBGQUxTRSkgKwogICAgdGhlbWVfbWluaW1hbCgpCmBgYAojIyMgVW5pdGUgY29sdW1ucwoKSW4gdGhlIHRpZHlyIHBhY2thZ2UsIHRoZSBvcHBvc2l0ZSBvZiBzZXBhcmF0ZSgpIGlzIHVuaXRlKCkuIFNvbWV0aW1lcyB5b3UgbmVlZCB0byBwYXN0ZSB2YWx1ZXMgZnJvbSB0d28gb3IgbW9yZSBjb2x1bW5zIHRvZ2V0aGVyIHRvIHRpZHkuIEhlcmUgaXMgYW4gZXhhbXBsZSB1c2FnZSBmb3IgdW5pdGUoKToKCiAgICBkYXRhICU+JQogICAgIHVuaXRlKG5ld192YXIsIG9sZF92YXIxLCBvbGRfdmFyMikgCgpUaGUgcmF0aW5nczIgZGF0YXNldCBpbmNsdWRlcyB0aGVzZSB2YXJpYWJsZXM6CgotIHNlcmllczogMS04Ci0gZXBpc29kZTogMS0xMAotIHZpZXdlcnNfbWlsbGlvbnM6IHdob2xlIG51bWJlciBvZiA3LWRheSB2aWV3ZXJzIGluIG1pbGxpb25zCi0gdmlld2Vyc19kZWNpbWFsOiBhZGRpdGlvbmFsIG51bWJlciBvZiA3LWRheXMgdmlld2VycyBpbiBtaWxsaW9ucwoKSWYgdmlld2Vyc19taWxsaW9ucyA9IDIgYW5kIHZpZXdlcnNfZGVjaW1hbCA9IC42LCB0aGVuIHRoZSB0b3RhbCBudW1iZXIgb2Ygdmlld2VycyBpbiBtaWxsaW9ucyBpcyAyLjYuIEluIHRoaXMgZXhlcmNpc2UsIHlvdSdsbCBwcmFjdGljZSB1bml0aW5nIHRoZXNlIHR3byBjb2x1bW5zIGludG8gYSBzaW5nbGUgbmV3IGNvbHVtbi4KYGBge3J9CnJhdGluZ3NfMiA8LSByZWFkUkRTKCJyYXRpbmdzXzIucmRzIikKYGBgCmBgYHtyfQpyYXRpbmdzMyA8LSByYXRpbmdzXzIgJT4lIAoJIyBVbml0ZSB2aWV3ZXJzIGluIG1pbGxpb25zIGFuZCBkZWNpbWFscyB0b2dldGhlciAgICAKCXVuaXRlKHZpZXdlcnNfN2RheSwgdmlld2Vyc19taWxsaW9ucywgdmlld2Vyc19kZWNpbWFsKQoKIyBQcmludCB0byB2aWV3CnJhdGluZ3MzCmBgYApgYGB7cn0KcmF0aW5nczMgPC0gcmF0aW5nc18yICAlPiUgCgkjIEFkYXB0IHRvIGNoYW5nZSB0aGUgc2VwYXJhdG9yCgl1bml0ZSh2aWV3ZXJzXzdkYXksIHZpZXdlcnNfbWlsbGlvbnMsIHZpZXdlcnNfZGVjaW1hbCwgc2VwID0gIiIpCgojIFByaW50IHRvIHZpZXcKcmF0aW5nczMKYGBgCmBgYHtyfQpyYXRpbmdzMyA8LSByYXRpbmdzXzIgICU+JSAKCSMgVW5pdGUgYW5kIGNoYW5nZSB0aGUgc2VwYXJhdG9yCgl1bml0ZSh2aWV3ZXJzXzdkYXksIHZpZXdlcnNfbWlsbGlvbnMsIHZpZXdlcnNfZGVjaW1hbCwgc2VwID0gIiIpICU+JQoJIyBBZGFwdCB0byBjYXN0IHZpZXdlcnMgYXMgYSBudW1iZXIKCW11dGF0ZSh2aWV3ZXJzXzdkYXkgPSBwYXJzZV9udW1iZXIodmlld2Vyc183ZGF5KSkKCiMgUHJpbnQgdG8gdmlldwpyYXRpbmdzMwpgYGAKCiMjIFNwcmVhZAoKIyMjIFNwcmVhZCByb3dzIGludG8gY29sdW1ucwoKSW4gdGhpcyBleGVyY2lzZSwgeW91J2xsIHVzZSB0aGUgdGlkeXIsIGRwbHlyLCBhbmQgcmVhZHIgcGFja2FnZXMgdG8gdGlkeSB0aGUgcmF0aW5nczIgZGF0YSBhZnRlciB1c2luZyBkcGx5cjo6Y291bnQoKS4gVGhlIHRpZHlfcmF0aW5nc19hbGwgZGF0YSBzaG91bGQgaGF2ZSBmb3VyIHZhcmlhYmxlcyB0byB3b3JrIHdpdGg6CgotIHNlcmllcyAoaW50ZWdlcjogMS04KSwKLSBlcGlzb2RlIChpbnRlZ2VyOiAxLTEwKSwKLSBkYXlzIChpbnRlZ2VyOiA3IG9yIDI4KSwgYW5kCi0gdmlld2VycyAobnVtZXJpYykuCgpgYGB7cn0KIyBDcmVhdGUgdGlkeSBkYXRhIHdpdGggNy0gYW5kIDI4LWRheSB2aWV3ZXJzCnRpZHlfcmF0aW5nc19hbGwgPC0gcmF0aW5nczIgJT4lCglnYXRoZXIoa2V5ID0gZXBpc29kZSwgdmFsdWUgPSB2aWV3ZXJzLCBlbmRzX3dpdGgoImRheSIpLCBuYS5ybSA9IFRSVUUpICU+JSAKICAgIHNlcGFyYXRlKGVwaXNvZGUsIGludG8gPSBjKCJlcGlzb2RlIiwgImRheXMiKSkgJT4lICAKICAgIG11dGF0ZShlcGlzb2RlID0gcGFyc2VfbnVtYmVyKGVwaXNvZGUpLAogICAgICAgICAgIGRheXMgPSBwYXJzZV9udW1iZXIoZGF5cykpCmBgYApVc2luZyB5b3VyIG5ldyB0aWR5IGRhdGEsIGNvdW50IHRoZSBudW1iZXIgb2Ygdmlld2VycyBncm91cGluZyBieSBzZXJpZXMgYW5kIGRheXMuIFJlbWVtYmVyOiB5b3UgZG9uJ3QgbmVlZCB0byB1c2UgZ3JvdXBfYnkgYmVmb3JlIGNvdW50LCBidXQgeW91IGRvIG5lZWQgdG8gdXNlIHRoZSB3dCA9IGFyZ3VtZW50IHRvIHN1bSB0aGUgdmFsdWVzIGluIHRoZSB2aWV3ZXJzIGNvbHVtbi4KYGBge3J9CnRpZHlfcmF0aW5nc19hbGwgJT4lCgkjIENvdW50IHZpZXdlcnMgYnkgc2VyaWVzIGFuZCBkYXlzCgljb3VudChzZXJpZXMsIGRheXMsIHd0ID0gdmlld2VycykKYGBgCmBgYHtyfQp0aWR5XzdkYXkgPC0gdGlkeV9yYXRpbmdzX2FsbCAlPiUgCgkjIENvdW50IHZpZXdlcnMgYnkgc2VyaWVzIGFuZCBkYXlzCiAgICBjb3VudChzZXJpZXMsIGRheXMsIHd0ID0gdmlld2VycykgJT4lCgkjIEFkYXB0IHRvIHNwcmVhZCBjb3VudGVkIHZhbHVlcwoJICBzcHJlYWQoa2V5ID0gZGF5cywgdmFsdWUgPSBuLCBzZXAgPSAiXyIpCnRpZHlfN2RheQpgYGAKVGhlIHNlcCA9ICJfIiBhcmd1bWVudCBpcyBlc3BlY2lhbGx5IGhlbHBmdWwgd2hlbiB5b3UgYXJlIHNwcmVhZGluZyBieSBhIG51bWVyaWMgdmFyaWFibGUgc28gdGhhdCB5b3UgZG9uJ3QgZW5kIHVwIHdpdGggdmFyaWFibGUgbmFtZXMgdGhhdCBzdGFydCB3aXRoIG51bWJlcnMuCgoKCgojIyBUaWR5IG11bHRpcGxlIHNldHMgb2YgY29sdW1ucwoKIyMjIE1hc3RlcmNsYXNzOiBUaWR5CgpJbiB0aGUgbGFzdCB0aHJlZSBleGVyY2lzZXMsIHlvdSdsbCB0aWR5IHRoZSByYXRpbmdzIGRhdGEgdG8gY3JlYXRlIGEgc2NhdHRlcnBsb3QgdG8gc2VlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgbnVtYmVyIG9mIHByZW1pZXJlIGFuZCBmaW5hbGUgVUsgdmlld2VycyAoNy1kYXkgb25seSkgYnkgc2VyaWVzLgoKYGBge3J9CiMgR2F0aGVyIHZpZXdlciBjb2x1bW5zIGFuZCByZW1vdmUgTkEgcm93cwp0aWR5X3JhdGluZ3MgPC0gcmF0aW5ncyAlPiUKICAgIGdhdGhlcihlcGlzb2RlLCB2aWV3ZXJzLCAtc2VyaWVzLCBuYS5ybSA9IFRSVUUpICU+JQogICAgbXV0YXRlKGVwaXNvZGUgPSBwYXJzZV9udW1iZXIoZXBpc29kZSkpICU+JSAKICAgIGdyb3VwX2J5KHNlcmllcykgJT4lIAogICAgZmlsdGVyKGVwaXNvZGUgPT0gMSB8IGVwaXNvZGUgPT0gbWF4KGVwaXNvZGUpKSAlPiUgCiAgICB1bmdyb3VwKCkKdGlkeV9yYXRpbmdzCmBgYApOb3cgd2UgaGF2ZSBhIHRpZHkgdmVyc2lvbiBvZiB0aGUgcmF0aW5ncyBkYXRhIHdpdGggb25seSBwcmVtaWVyZSBhbmQgZmluYWxlIGVwaXNvZGVzLiBMZXQncyBzZWUgaWYgdGhhdCB0d2VldCBzcG9pbGVkIHRoZSBTZXJpZXMgOCBmaW5hbGUuCgpJbiB0aGUgcHJldmlvdXMgZXhlcmNpc2UsIHlvdSBnYXRoZXJlZCwgdGhlbiBkaWQgYSBncm91cGVkIGZpbHRlciB0byBtYWtlIHRpZHlfcmF0aW5ncy4gSW4gdGhpcyBleGVyY2lzZSwgeW91J2xsIG1ha2UgdHdvIGNoYXJ0cyBpbiBnZ3Bsb3QyIHdpdGggdGhpcyB0aWR5IGRhdGEgdG8gc2VlIGlmIHRoZSBqdWRnZSdzIHR3ZWV0IHNwb2lsZWQgdGhlIFNlcmllcyA4IGZpbmFsZSByYXRpbmdzLgoKVGhlIHR3byBwbG90cyB5b3UnbGwgbWFrZSwgYSBzbG9wZSBjaGFydCBhbmQgYSBkdW1iYmVsbCBjaGFydCwgYXJlIG5pY2Ugd2F5cyB0byBzaG93IGNoYW5nZSBiZXR3ZWVuIHR3byBkYXRhIHBvaW50cyBjb21iaW5pbmcgZ2VvbV9wb2ludCgpIGFuZCBnZW9tX2xpbmUoKS4KCllvdSdsbCB1c2UgdGhlc2UgdG8gdmlzdWFsaXplIHRoZSAiZmluYWxlIGJ1bXAiIGluIHZpZXdlcnMgYmV0d2VlbiB0aGUgcHJlbWllcmUgYW5kIGZpbmFsZSBlcGlzb2RlcyBhY3Jvc3Mgc2VyaWVzLgpgYGB7cn0KZmlyc3RfbGFzdCA8LSB0aWR5X3JhdGluZ3MgJT4lIAogIG11dGF0ZShlcGlzb2RlID0gcmVjb2RlKGVwaXNvZGUsIGAxYCA9ICJmaXJzdCIsIC5kZWZhdWx0ID0gImxhc3QiKSkgCmZpcnN0X2xhc3QKYGBgCgpgYGB7cn0KIyBGaWxsIGluIHRvIG1ha2Ugc2xvcGUgY2hhcnQKZ2dwbG90KGZpcnN0X2xhc3QsIGFlcyh4ID0gZXBpc29kZSwgeSA9IHZpZXdlcnMsIGNvbG9yID0gc2VyaWVzKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IHNlcmllcykpCmBgYAoKYGBge3J9CiMgU3dpdGNoIHRoZSB2YXJpYWJsZXMgbWFwcGluZyB4LWF4aXMgYW5kIGNvbG9yCmdncGxvdChmaXJzdF9sYXN0LCBhZXMoeCA9IHNlcmllcywgeSA9IHZpZXdlcnMsIGNvbG9yID0gZXBpc29kZSkpICsKICBnZW9tX3BvaW50KCkgKyAjIGtlZXAKICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gc2VyaWVzKSkgKyAjIGtlZXAKICBjb29yZF9mbGlwKCkgIyBrZWVwCmBgYApHcmVhdCB3b3JrLSBub3RpY2UgaG93IGVhY2ggb2YgdGhlc2UgdHdvIHBsb3RzIHJlbGllZCBvbiBkYXRhIHRoYXQgaXMgdGlkeSEgU2VyaWVzIDggaGFkIGEgcHJldHR5IHNtYWxsICdmaW5hbGUgYnVtcCcsIGJ1dCB3ZSBhcmUgc3RpbGwgbG9va2luZyBhdCByYXcgY2hhbmdlcyBpbiB2aWV3ZXJzLiBMZXQncyB2aXN1YWxpemUgcGVyY2VudGFnZSBjaGFuZ2VzIGluIHZpZXdlcnMgaW5zdGVhZC4KCkluIHRoZSBwcmV2aW91cyBleGVyY2lzZSwgeW91IHZpc3VhbGl6ZWQgdGhlIHNpemUgb2YgdGhlICJmaW5hbGUgYnVtcCIgaW4gdmlld2VycyBmb3IgZWFjaCBzZXJpZXMuIEZyb20gdGhvc2UgY2hhcnRzLCB3ZSBrbm93IHRoYXQgZmluYWxlIGVwaXNvZGVzIGFsd2F5cyBnYXJuZXIgbW9yZSB2aWV3ZXJzIHRoYW4gcHJlbWllcmVzLiBXZSBhbHNvIGtub3cgdGhhdCB0aGUgY2hhbmdlIGluIHZpZXdlcnMgZm9yIHNlcmllcyA4IHNlZW1lZCBzbWFsbGVyIGNvbXBhcmVkIHRvIHNvbWUgcHJldmlvdXMgc2VyaWVzLgoKQnV0IHRoZSBxdWVzdGlvbiBvZiB3aGV0aGVyIHRoZSBzZXJpZXMgOCBmaW5hbGUgdmlld2VycyB3ZXJlICJzcG9pbGVkIiBtaWdodCBiZSBiZXN0IGFuc3dlcmVkIGJ5IGNvbXBhcmluZyB0aGUgcmVsYXRpdmUgcGVyY2VudGFnZSBjaGFuZ2VzIGluIHByZW1pZXJlIGFuZCBmaW5hbGUgdmlld2VycyBhY3Jvc3Mgc2VyaWVzLiBJbiB0aGlzIGV4ZXJjaXNlLCB5b3UnbGwgcmVzaGFwZSBmaXJzdF9sYXN0IG9uY2UgbW9yZSB0byBjYWxjdWxhdGUgYW5kIHZpc3VhbGl6ZSB0aGUgcmVsYXRpdmUgZmluYWxlIGJ1bXAuCmBgYHtyfQojIENhbGN1bGF0ZSByZWxhdGl2ZSBpbmNyZWFzZSBpbiB2aWV3ZXJzCmJ1bXBfYnlfc2VyaWVzIDwtIGZpcnN0X2xhc3QgJT4lIAogIHNwcmVhZChlcGlzb2RlLCB2aWV3ZXJzKSAlPiUgICAKICBtdXRhdGUoYnVtcCA9IChsYXN0IC0gZmlyc3QpIC8gZmlyc3QpCmJ1bXBfYnlfc2VyaWVzCmBgYApgYGB7cn0KIyBDYWxjdWxhdGUgcmVsYXRpdmUgaW5jcmVhc2UgaW4gdmlld2VycwpidW1wX2J5X3NlcmllcyA8LSBmaXJzdF9sYXN0ICU+JSAKICBzcHJlYWQoZXBpc29kZSwgdmlld2VycykgJT4lICAgCiAgbXV0YXRlKGJ1bXAgPSAobGFzdCAtIGZpcnN0KSAvIGZpcnN0KQogIAojIEZpbGwgaW4gdG8gbWFrZSBiYXIgY2hhcnQgb2YgYnVtcHMgYnkgc2VyaWVzCmdncGxvdChidW1wX2J5X3NlcmllcywgYWVzKHggPSBzZXJpZXMsIHkgPSBidW1wKSkgKwogIGdlb21fY29sKCkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpICMgY29udmVydHMgdG8gJQpgYGAKU2VyaWVzIDggZGVmaW5pdGVseSBoYWQgdGhlIGxvd2VzdCAnZmluYWxlIGJ1bXAnIHNvIGZhci0gb25seSA2JSBtb3JlIHZpZXdlcnMgd2F0Y2hlZCB0aGUgZmluYWxlIHRoYW4gdGhlIHByZW1pZXJlLiBOb3RpY2UgdGhhdCB3ZSBoYWQgdG8gc3ByZWFkIGhlcmUsIGJlY2F1c2UgdGhlIHRpZHkgdmVyc2lvbiBvZiB0aGUgZGF0YSBkZXBlbmRzIG9uIHRoZSBxdWVzdGlvbiB5b3Ugd2FudCB0byBhc2suCgojIFRyYW5zZm9ybSB5b3VyIGRhdGEKCiMjIENvbXBsZXggcmVjb2Rpbmcgd2l0aCBjYXNlX3doZW4KCiMjIyBDb21iaW5lIHR3byB2YXJpYWJsZXMKCkluIHRoaXMgZXhlcmNpc2UsIHlvdSdsbCB1c2UgY2FzZV93aGVuKCkgd2l0aCB0aGUgYmFrZXJzIGRhdGEgdG8gY3JlYXRlIGEgbmV3IHZhcmlhYmxlIGJhc2VkIG9uIHRoZSBudW1iZXIgb2YgdGltZXMgZWFjaCBiYWtlciB3YXMgY3Jvd25lZCBhcyBzdGFyIGJha2VyIG9yIHRoZSB0ZWNobmljYWwgY2hhbGxlbmdlIHdpbm5lcjoKCi0gSWYgc3RhciBiYWtlciBpcyBncmVhdGVyIHRoYW4gdGVjaG5pY2FsIHdpbnMsIHJlY29kZSBhcyBzdXBlcl9zdGFyCi0gSWYgc3RhciBiYWtlciBpcyBsZXNzIHRoYW4gdGVjaG5pY2FsIHdpbnMsIHJlY29kZSBhcyBoaWdoX3RlY2gKLSBSZWNvZGUgdGhlIHJlc3QgYXMgd2VsbF9yb3VuZGVkCmBgYHtyfQpiYWtlcnMgPC0gcmVhZF9jc3YoIkJha2Vycy9iYWtlcl9yZXN1bHRzLmNzdiIsCiAgICAgICAgICAgICAgICAgICBjb2xfdHlwZXMgPSBjb2xzKHNlcmllcyA9IGNvbF9mYWN0b3IobGV2ZWxzID0gTlVMTCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcmllc193aW5uZXIgPSBjb2xfZmFjdG9yKGxldmVscyA9IE5VTEwpKQogICAgICAgICAgICAgICAgICAgKQpgYGAKYGBge3J9CmhlYWQoYmFrZXJzKQpgYGAKYGBge3J9CiMgQ3JlYXRlIHNraWxsIHZhcmlhYmxlIHdpdGggMyBsZXZlbHMKYmFrZXJzX3NraWxsIDwtIGJha2VycyAlPiUgCiAgbXV0YXRlKHNraWxsID0gY2FzZV93aGVuKAogICAgc3Rhcl9iYWtlciA+IHRlY2huaWNhbF93aW5uZXIgfiAic3VwZXJfc3RhciIsCiAgICBzdGFyX2Jha2VyIDwgdGVjaG5pY2FsX3dpbm5lciB+ICJoaWdoX3RlY2giLAogICAgVFJVRSB+ICJ3ZWxsX3JvdW5kZWQiCiAgKSkKICAKIyBGaWx0ZXIgemVyb2VzIHRvIGV4YW1pbmUgc2tpbGwgdmFyaWFibGUKYmFrZXJzX3NraWxsICU+JSAKICBmaWx0ZXIoc3Rhcl9iYWtlciA9PSAwICYgdGVjaG5pY2FsX3dpbm5lciA9PSAwKSAlPiUgCiAgY291bnQoc2tpbGwpCmBgYApXaG9vcHMtIHdlIG1heSBub3Qgd2FudCBiYWtlcnMgd2hvIG5ldmVyIHdvbiBzdGFyIGJha2VyIG9yIHRoZSB0ZWNobmljYWwgY2hhbGxlbmdlIHRvIGJlIGNhbGxlZCAnd2VsbC1yb3VuZGVkJy4gTGV0J3MgYWRkIGFub3RoZXIgbGV2ZWwgdG8gdGFrZSBjYXJlIG9mIHRoaXMhCgojIyMgQWRkIGFub3RoZXIgYmluCgpJbiB0aGlzIGV4ZXJjaXNlLCB5b3UnbGwgZWRpdCB5b3VyIHByZXZpb3VzIGNvZGUgc28gdGhhdCBza2lsbCBoYXMgZm91ciBsZXZlbHM6CgotIElmIGJvdGggc3RhciBiYWtlciBhbmQgdGVjaG5pY2FsIHdpbnMgYXJlIHplcm8sIHJlY29kZSBhcyBOQV9jaGFyYWN0ZXJfCi0gSWYgc3RhciBiYWtlciBpcyBncmVhdGVyIHRoYW4gdGVjaG5pY2FsIHdpbnMsIHJlY29kZSBhcyBzdXBlcl9zdGFyCi0gSWYgc3RhciBiYWtlciBpcyBsZXNzIHRoYW4gdGVjaG5pY2FsIHdpbnMsIHJlY29kZSBhcyBoaWdoX3RlY2gKLSBJZiBzdGFyIGJha2VyIGlzIGVxdWFsIHRvIHRlY2huaWNhbCB3aW5zLCByZWNvZGUgYXMgd2VsbF9yb3VuZGVkCgpgYGB7cn0KIyBBZGQgcGlwZSB0byBkcm9wIHNraWxsID0gTkEKYmFrZXJzX3NraWxsIDwtIGJha2VycyAlPiUgCiAgbXV0YXRlKHNraWxsID0gY2FzZV93aGVuKAogICAgc3Rhcl9iYWtlciA+IHRlY2huaWNhbF93aW5uZXIgfiAic3VwZXJfc3RhciIsCiAgICBzdGFyX2Jha2VyIDwgdGVjaG5pY2FsX3dpbm5lciB+ICJoaWdoX3RlY2giLAogICAgc3Rhcl9iYWtlciA9PSAwICYgdGVjaG5pY2FsX3dpbm5lciA9PSAwIH4gTkFfY2hhcmFjdGVyXywKICAgIHN0YXJfYmFrZXIgPT0gdGVjaG5pY2FsX3dpbm5lciAgfiAid2VsbF9yb3VuZGVkIgogICkpICU+JSAKICBkcm9wX25hKHNraWxsKQogIAojIENvdW50IGJha2VycyBieSBza2lsbApiYWtlcnNfc2tpbGwgJT4lCiAgY291bnQoc2tpbGwpCmBgYApWZXJ5IG5pY2UtIG5vdyB3ZSBvbmx5IGhhdmUgMTUgJ3dlbGwtcm91bmRlZCcgYmFrZXJzLCBhbmQgd2UgZHJvcHBlZCB0aGUgNDEgYmFrZXJzIHdobyBuZXZlciB3b24gc3RhciBiYWtlciBvciBhIHRlY2huaWNhbCBjaGFsbGVuZ2UuCgojIyBGYWN0b3JzCgojIyMgQ2FzdCBhIGZhY3RvciBhbmQgZXhhbWluZSBsZXZlbHMKCkluIHRoaXMgZXhlcmNpc2UsIHlvdSdsbCBzdGFydCB3aGVyZSB5b3UgbGVmdCBvZmYgaW4gdGhlIGxhc3Qgc2VyaWVzIG9mIGV4ZXJjaXNlcywgd29ya2luZyB3aXRoIHRoZSBiYWtlcnMgZGF0YSB0byBleGFtaW5lIHlvdXIgbmV3IGNhdGVnb3JpY2FsIHZhcmlhYmxlIGNhbGxlZCBza2lsbCwgd2hpY2ggaGFzIHRocmVlIGxldmVsczogd2VsbF9yb3VuZGVkLCBzdXBlcl9zdGFyLCBhbmQgaGlnaF90ZWNoLgoKYGBge3J9CiMgQ2FzdCBza2lsbCBhcyBhIGZhY3RvcgpiYWtlcnMgPC0gYmFrZXJzX3NraWxsICU+JSAKICBtdXRhdGUoc2tpbGwgPSBhcy5mYWN0b3Ioc2tpbGwpKQoKIyBFeGFtaW5lIGxldmVscwpsZXZlbHMoYmFrZXJzJHNraWxsKQpgYGAKU29tZXRpbWVzIGZhY3RvcnMgaGF2ZSBhIG5hdHVyYWwgb3JkZXIgdG8gdGhlbS0gbGlrZSBiZWdpbm5lciwgaW50ZXJtZWRpYXRlLCBhbmQgYWR2YW5jZWQtIGJ1dCBzb21ldGltZXMgdGhleSBkb24ndC4gS25vdyB5b3VyIGRhdGEsIGFuZCBrbm93IHlvdXIgZmFjdG9ycyEKCiMjIyBQbG90IGZhY3RvciBjb3VudHMKCkluIHRoaXMgZXhlcmNpc2UsIHlvdSdsbCB1c2UgZ2dwbG90MiB0byBtYWtlIGEgYmFyIGNoYXJ0IHdpdGggb25lIHBlciBsZXZlbCBvZiB0aGUgc2tpbGwgZmFjdG9yIHdlIGp1c3QgbWFkZSwgd2hlcmUgdGhlIGhlaWdodCBvZiB0aGUgYmFyIHNob3dzIHRoZSBudW1iZXIgb2YgYmFrZXJzIGluIGVhY2ggc2tpbGwgbGV2ZWwuCgpOb3RpY2UgYSBiaWcgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBkYXRhIGhlcmUgYW5kIHRoZSBkYXRhIHdlIGhhdmUgdXNlZCBiZWZvcmUgdG8gbWFrZSBhIGJhciBjaGFydC0gaGVyZSwgeW91IHdhbnQgdGhlIGhlaWdodCBvZiB0aGUgYmFycyB0byBiZSByZXByZXNlbnQgdGhlIG51bWJlciBvZiByb3dzIGluIHRoZSBiYWtlcnMgZGF0YSBpbiBlYWNoIHNraWxsIGxldmVsLiBUaGlzIGlzIGNvbXBhcmVkIHRvIGJhciBjaGFydHMgd2UgbWFkZSB3aGVyZSB0aGUgaGVpZ2h0IHJlcHJlc2VudGVkIHZhbHVlcyBpbiB0aGUgZGF0YSAod2hlbiB5b3UgdXNlZCBnZW9tX2NvbCgpKS4KCmBgYHtyfQpsaWJyYXJ5KGZvcmNhdHMpCmBgYAoKYGBge3J9CiMgRWRpdCB0byByZXZlcnNlIHgtYXhpcyBvcmRlcgpnZ3Bsb3QoYmFrZXJzLCBhZXMoeCA9IGZjdF9yZXYoc2tpbGwpLCBmaWxsID0gc2VyaWVzX3dpbm5lcikpICsKICBnZW9tX2JhcigpCmBgYApOb3RlIHRoYXQgd2hlbiB5b3UgdXNlIGZ1bmN0aW9uIGxpa2UgZmN0X3JldigpLCBmb3JjYXRzIGNvbnZlcnRzIHRoZSBjaGFyYWN0ZXIgdG8gYSBmYWN0b3IgZm9yIHlvdXIgcGxvdAoKIyMgRGF0ZXMKCiMjIyBDYXN0IGNoYXJhY3RlcnMgYXMgZGF0ZXMKCldlIGNhbiB1c2UgbHVicmlkYXRlIHRvIHBhcnNlIGFuZCBjYXN0IGEgZGF0ZSB2YXJpYWJsZSB3aXRoaW4gYSBtdXRhdGUoKS4gSW4gdGhpcyBleGVyY2lzZSwgeW91J2xsIHByYWN0aWNlIGRvaW5nIHRoaXMgd2l0aCB0aGUgYmFrZXJfZGF0ZXMgZGF0YSwgYW5kIHRoZW4gZ28gZXZlbiBmdXJ0aGVyIHRvIGV4dHJhY3QgZGF0YSBmcm9tIHRoZSBkYXRlcyBsaWtlIHRoZSBsYWJlbGxlZCBtb250aC4gUmVtZW1iZXIgdG8gdXNlID9tb250aCB0byByZWFkIGFib3V0IHRoZSBtb250aCgpIGZ1bmN0aW9uIGFuZCBpdHMgYXJndW1lbnRzLgoKYGBge3J9CmJha2VyX2RhdGVzIDwtIHJlYWRSRFMoImJha2VyX2RhdGVzLnJkcyIpCmBgYApgYGB7cn0KaGVhZChiYWtlcl9kYXRlcykKYGBgCmBgYHtyfQpsaWJyYXJ5KGx1YnJpZGF0ZSkKIyBBZGQgYSBsaW5lIHRvIGV4dHJhY3QgbGFiZWxlZCBtb250aApiYWtlcl9kYXRlc19jYXN0IDwtIGJha2VyX2RhdGVzICU+JSAKICBtdXRhdGUobGFzdF9kYXRlX2FwcGVhcmVkX3VzID0gZG15KGxhc3RfZGF0ZV9hcHBlYXJlZF91cyksCiAgICAgICAgIGxhc3RfbW9udGhfdXMgPSBtb250aChsYXN0X2RhdGVfYXBwZWFyZWRfdXMsIGxhYmVsID0gVFJVRSkpCiAgICAgICAgIAojIE1ha2UgYmFyIGNoYXJ0IGJ5IGxhc3QgbW9udGgKZ2dwbG90KGJha2VyX2RhdGVzX2Nhc3QsYWVzKHggPSBsYXN0X21vbnRoX3VzKSkgKwogIGdlb21fYmFyKCkKYGBgClRoZXJlIGFyZSBtb3JlIGZ1bmN0aW9ucyBsaWtlIHllYXIgZm9yIGdldHRpbmcgb3V0IG90aGVyIGRhdGUgY29tcG9uZW50cy0geW91IGNhbiBldmVuIGV4dHJhY3QgZmlzY2FsIHF1YXJ0ZXJzIG9yIHNlbWVzdGVycyEgTG9vayBhdCB0aGUgZG9jdW1lbnRhdGlvbiB0byBzZWUgbW9yZSB3YXlzLgoKIyMjIENhbGN1bGF0ZSB0aW1lc3BhbnMKClRoZSBmaXJzdCBzdGVwIHRvIGNhbGN1bGF0aW5nIGEgdGltZXNwYW4gaW4gbHVicmlkYXRlIGlzIHRvIG1ha2UgYW4gaW50ZXJ2YWwsIHRoZW4gdXNlIGRpdmlzaW9uIHRvIGNvbnZlcnQgdGhlIHVuaXRzIHRvIHdoYXQgeW91IHdhbnQgKGxpa2Ugd2Vla3MoeCkgb3IgbW9udGhzKHgpKS4gVGhlIHggcmVmZXJzIHRvIHRoZSBudW1iZXIgb2YgdGltZSB1bml0cyB0byBiZSBpbmNsdWRlZCBpbiB0aGUgcGVyaW9kLiBJbiB0aGlzIGV4ZXJjaXNlLCB5b3UnbGwgY2FsY3VsYXRlIHRpbWVzcGFucyB3aXRoIHRoZSBiYWtlcl90aW1lIGRhdGEgd2UganVzdCBtYWRlLgpgYGB7cn0KIyBBZGQgYSBsaW5lIHRvIGNyZWF0ZSB3aG9sZSBtb250aHMgb24gYWlyIHZhcmlhYmxlCmJha2VyX3RpbWUgPC0gYmFrZXJfZGF0ZXMgICU+JSAKICBtdXRhdGUodGltZV9vbl9haXIgPSBpbnRlcnZhbChmaXJzdF9kYXRlX2FwcGVhcmVkX3VrLCBsYXN0X2RhdGVfYXBwZWFyZWRfdWspLAogICAgICAgICB3ZWVrc19vbl9haXIgPSB0aW1lX29uX2FpciAvIHdlZWtzKDEpLAogICAgICAgICBtb250aHNfb25fYWlyID0gdGltZV9vbl9haXIgJS8lIG1vbnRocygxKSkKYmFrZXJfdGltZQpgYGAKSGVyZSBhZ2FpbiwgbHVicmlkYXRlIGdpdmVzIHlvdSBtYW55IG9wdGlvbnMgZm9yIHdvcmtpbmcgd2l0aCBwZXJpb2RzIGZyb20geWVhcnMgZG93biB0byBwaWNvc2Vjb25kcy4gWW91IG1heSBhbHNvIGVuZCB1cCBuZWVkaW5nIHRvIHdvcmsgd2l0aCBkdXJhdGlvbnMgdG9vLCB3aGljaCBoYXZlIHNpbWlsYXIgZnVuY3Rpb25zIHRoYXQgYWx3YXlzIHN0YXJ0IHdpdGggYSBkIGZyb20gZHllYXJzIHRvIGRwaWNvc2Vjb25kcy4KCiMjIFN0cmluZ3MKCiMjIyBXcmFuZ2xlIGEgY2hhcmFjdGVyIHZhcmlhYmxlCgpJbiB0aGlzIGV4ZXJjaXNlLCB3ZSdsbCB3cmFuZ2xlIHRoZSB2YWx1ZXMgaW4gdGhlIHBvc2l0aW9uX3JlYWNoZWQgdmFyaWFibGUsIHdoaWNoIGxvb2tzIGxpa2UgdGhpcyByaWdodCBub3c6CgpgYGB7cn0KbWVzc3lfYmFrZXJfcmVzdWx0cyA8LSByZWFkX2NzdigiQmFrZXJzL21lc3N5X2Jha2VyX3Jlc3VsdHMuY3N2IikKbWVzc3lfYmFrZXJfcmVzdWx0cyAlPiUgCiAgY291bnQocG9zaXRpb25fcmVhY2hlZCkKYGBgCkxldCdzIGNsZWFuIHRoZXNlIHN0cmluZ3MgdXAhCgpgYGB7cn0KbGlicmFyeShzdHJpbmdyKQpgYGAKYGBge3J9CiMgQWRkIGFub3RoZXIgbXV0YXRlIHRvIHJlcGxhY2UgIlRISVJEIFBMQUNFIiB3aXRoICJSVU5ORVIgVVAiYW5kIGNvdW50CmJha2Vyc18yIDwtIG1lc3N5X2Jha2VyX3Jlc3VsdHMgJT4lIAogIG11dGF0ZShwb3NpdGlvbl9yZWFjaGVkID0gc3RyX3RvX3VwcGVyKHBvc2l0aW9uX3JlYWNoZWQpLAogICAgICAgICBwb3NpdGlvbl9yZWFjaGVkID0gc3RyX3JlcGxhY2UocG9zaXRpb25fcmVhY2hlZCwgIi0iLCAiICIpLAogICAgICAgICBwb3NpdGlvbl9yZWFjaGVkID0gc3RyX3JlcGxhY2UocG9zaXRpb25fcmVhY2hlZCwgIlRISVJEIFBMQUNFIiwgIlJVTk5FUiBVUCIpKQoKIyBDb3VudCByb3dzCmJha2Vyc18yICU+JQogIGNvdW50KHBvc2l0aW9uX3JlYWNoZWQpCmBgYApHcmVhdCB3b3JrISBFYWNoIG9mIHRoZSA4IHNlcmllcyBoYWQgb25lIHdpbm5lciBhbmQgdHdvIHJ1bm5lci11cHMsIHNvIG5vdyB0aGlzIHZhcmlhYmxlIGlzIG11Y2ggZWFzaWVyIHRvIHdvcmsgd2l0aCEKCiMjIyBEZXRlY3QgYSBzdHJpbmcgcGF0dGVybgoKd2UnbGwgd3JhbmdsZSBlYWNoIGJha2Vycycgb2NjdXBhdGlvbiBmcm9tIHRoZSBiYWtlcnMgZGF0YSwgd2hpY2ggaXMgY3VycmVudGx5IGEgY2hhcmFjdGVyIHN0cmluZyBsaWtlIHRoaXM6CmBgYHtyfQpiYWtlcnNfMiAlPiUgCiAgc2VsZWN0KGJha2VyLCBvY2N1cGF0aW9uKQpgYGAKV2UnbGwgY3JlYXRlIGEgbG9naWNhbCB2YXJpYWJsZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyB3aGV0aGVyIG9yIG5vdCBlYWNoIGJha2VyIGlzIGEgc3R1ZGVudC4KYGBge3J9CiMgQWRkIGEgbGluZSB0byBjcmVhdGUgbmV3IHZhcmlhYmxlIGNhbGxlZCBzdHVkZW50CmJha2Vyc18zIDwtIGJha2Vyc18yICU+JSAKICAgIG11dGF0ZShvY2N1cGF0aW9uID0gc3RyX3RvX2xvd2VyKG9jY3VwYXRpb24pLCAKICAgICAgICAgICBzdHVkZW50ID0gc3RyX2RldGVjdChvY2N1cGF0aW9uLCAic3R1ZGVudCIpKQoKIyBGaW5kIGFsbCBzdHVkZW50cyBhbmQgZXhhbWluZSBvY2N1cGF0aW9ucwpiYWtlcnNfMyAlPiUKICBmaWx0ZXIoc3R1ZGVudCA9PSBUUlVFKSAlPiUKICBzZWxlY3QoYmFrZXIsIG9jY3VwYXRpb24sIHN0dWRlbnQpCmBgYApSYXYncyBvY2N1cGF0aW9uIHdhcyBzdHVkZW50IHN1cHBvcnQsIGJ1dCB3ZSBjbGFzc2lmaWVkIGhpbSBhcyBhIHN0dWRlbnQuIFRoaXMgc2hvd3MgYSBjb21tb24gcHJvYmxlbSBpbiB3b3JraW5nIHdpdGggZGF0YS0gaXQgaGVscHMgdG8gYWx3YXlzIGNoZWNrIHlvdXIgd29yayEKCg==