library(tidyverse)

Watch the video for Session 4

Watch the video lecture associated to this session.

You can find additional information on tidyverse and the piping information at the following link.

Once you’re done:

  1. Install the tidyverse package on your local machine.
# install.packages('tidyverse')

set.seed(123)
rnorm(1)

Tidyverse is a collection of R packages for better data cleaning, wrangling and visualization. In particular, it includes:

  • The dplyr and tidyr packages presented in the video, which make data handling and tidying easier.

  • The ggplot2 package for data visualization which we will get more into next week

  • Additional packages to handle strings, etc.

  1. go through the following exercises.

Exercise 0: Swirl

Similar to last week, we’ll use the swirl package to introduce you the piping, chaining, and dplyr syntax.

Walk through the Getting and Cleaning Data in the swirl package.

library(swirl)
swirl::install_course("Getting and Cleaning Data")
swirl()

Go through modules 1 through 3. Once you’re done, prepare the following exercises which we will use in our discussion session on Monday, July 6th.

Exercise 1: Movies Dataset

Part 0: Loading the data

Load in the dataset movies.csv at the following http address:

library(tidyverse)
url <- "https://raw.githubusercontent.com/Juanets/movie-stats/master/movies.csv"
movies <- read_csv(url)

Since this is the first real dataset that you are handling, let’s try to go through the usual inspection that you’d carry through whenever you will be presented with a dataset

What is the size of your dataset? What are the types of the different variables?

print(dim(movies))
print(names(movies))

Type in names(movies). This should print out the names of the different columns in your dataset.

Part 1: Easing into dplyr verbs

  1. Using the tally() function, count the number of entries in your dataset.
movies %>% tally()
## or
tally(movies)
  1. Using the count function, count the number of distinct movies in the dataset. count() is similar but calls group_by() before and ungroup() after. It will allow you to group the entries by movie name, and thus, to count the number of unique movies.

Your code should look something like below.

dim(movies %>% 
  count(name))
### or

length(unique(movies$name))
  1. Now, we will focus on building a summary table for the movie stars in this dataset.
    1. Using the select function, keep only the columns “budget”, “actor”, “runtime”, “gross”, “votes”, “score”, and “year”. Name this subsetted dataframe movies_sub.
    Your code should look something like:
movies_sub = movies %>% 
  select(col1, col2)
movies_sub = movies %>% 
  select(budget, star, runtime, gross, votes, year, score)

###or
select(movies_sub, budget, runtime, gross,  votes, year, score)
  1. Add a column that counts how many movies each actor appears in and name it “actor_n” Your code should look something like:
movies_sub = add_count(movies_sub, stars, name="actor_n")
movies_sub = add_count(movies_sub, star, name="actor_n")

#### Alternative with pipes
movies_sub %>% 
  add_count(star, name="actor_n2")
  1. Using the group_by and the summarize_all function, group the movies by actor and give the mean of each column. Keep the result in a table called actors.

Your code should look something like:

actors = movies_sub %>%
           group_by(star) %>%
           summarise_all(mean)

### alternative

actors2 = movies%>%
           group_by(star) %>%
           summarise_if(is.numeric, mean)
  1. Using the filter function, keep only the actors that have a mean score above 7. Your code should look something like:
actors = actors %>%
          filter(filtering_criterion)
actors = actors %>%
          filter(score>=7)
  1. Finally, using the top_n function, display the top 10 actors with the highest average votes. Are these the same as the top 10 actors in terms of the highest budget film?

print(actors %>%
  top_n(10, votes))

print(actors %>%
  top_n(10, budget))

Part 2: Using dplyr verbs (with less guiding!)

  1. Select the movies produced after 2010. Save the subset in ‘movies.sub’ variable.
movies.sub <- filter(movies, year > 2010)
movies.sub
  1. Keep only the columns ‘name’, ‘director’, ‘year’, ‘country’, ‘genre’, ‘budget’, ‘gross’, ‘score’ in the ‘movies.sub’.
movies.sub <- select(movies.sub, name, director, year, country, genre, budget, gross, score)
movies.sub
  1. Find the profit for each movie in ‘movies.sub’ as a fraction of its budget. Convert ‘budget’ and ‘gross’ columns million dollar units founded to the first decimal point. Use round() to round numbers.
movies.sub <- mutate(movies.sub, 
                     frac_profit = (gross - budget)/budget,
                     budget = round(budget/10^6, digits = 1),
                     gross = round(gross/10^6, digits = 1))
movies.sub
  1. Count the number of movies in ‘movies.sub’ produced by each genre, and order them in the descending count order.
by_genre <- group_by(movies.sub, genre)
arrange(summarise(by_genre, count = n()), desc(count))
  1. Now group movies in ‘movies.sub’ by countries and genre. Then, count the number of movies in each group and the corresponding median fractional profit, the mean and standard deviation of the movie score for each group.
movies.summary <- movies.sub %>%
  group_by(genre, country) %>%
  summarise(count = n(), 
            median_profit = median(frac_profit),
            mean_score = mean(score), 
            sd_score = sd(score)) %>%
  filter(count > 10) %>%
  arrange(desc(mean_score))
movies.summary

Part 3: Chaining

Using chaining and pipes, for each genre find the three directors the top mean movie scores received for the movies produced after 2000, after filtering out the directors with fewer than 5 movies in total. Hint: Use top_n() function to select top n from each group.

Find the movies in your favourite genre by the 5 directors you just found. These could serve as suggestions for your next movie night!

top5_dir <- movies %>% 
  filter(year > 2000) %>%
  group_by(genre, director) %>%
  summarise(
    mean_score = mean(score),
    count = n()) %>%
  filter(count >= 3) %>%
  group_by(genre) %>%
  top_n(5, wt = mean_score)
top5_dir
class(top5_dir)

See that top5_dir has a class ‘grouped_df’, so we convert it to a data frame first.

topDramaDir <- top5_dir %>% 
  as_tibble() %>%
  filter(genre == "Drama") %>% 
  select(director)
movies %>% 
  filter(genre == "Drama", 
         director %in% topDramaDir[["director"]]) %>%
  select(name, director, year, score, genre, gross, budget) %>%
  arrange(desc(score))

Part 4: Summaries

Using chain commands and the summarise function:

  1. Find the average and median number of movies that a director has been involved in.
movies %>%
  count(director, name="n_movies") %>%
  summarise_if(is.numeric, list(mean, median))
  1. Find the maximum number of action movies that a director is involved in.

movies %>%
  filter(genre == "Action") %>%
  count(director)%>%
  top_n(1, n)
  1. Compute 5th and 95th percentile of the gross amount, per movie genre.

movies %>%
  group_by(genre)%>%
  summarise(fifthQ = quantile(gross,0.05),ninetyfifthQ = quantile(gross,0.95))
  1. Is budget associated to success? To answer this question, create an additional column to your dataset using the mutate and rank function, corresponding to the rank of each movie in terms of buget. Call it rk. The rank should be descending, so that the rank 1 is the movie with highest budget. Select the top 100 movies with highest vote, and compute their mean and median rank in terms of budget.

rk_budget = movies %>%
  mutate(rk = rank(budget, ties.method = "first")) %>%
  arrange(desc(budget)) %>%
  top_n(100, budget) %>%
  summarise(mean_bdg= mean(budget),median_bdg= median(budget) )

rk_vote = movies %>%
  mutate(rk = rank(votes, ties.method = "first")) %>%
  arrange(desc(votes)) %>%
  top_n(100, votes) %>%
  summarise(mean_bdg= mean(budget),median_bdg= median(budget) )

print(rk_budget)
print(rk_vote)
LS0tCnRpdGxlOiAiRGF0YSBtYW5pcHVsYXRpb24gd2l0aCB0aWR5dmVyc2UiCnN1YnRpdGxlOiAiV2VlayAyIC0gU2Vzc2lvbiA0IgpkYXRlOiAiSnVseSA2dGgsIDIwMjAiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBkZl9wcmludDogcGFnZWQKLS0tCgpgYGB7ciBnbG9iYWxfb3B0aW9ucywgZWNobyA9IEZBTFNFLCBpbmNsdWRlID0gRkFMU0V9Cm9wdGlvbnMod2lkdGggPSA4MCkKa25pdHI6Om9wdHNfY2h1bmskc2V0KAogIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLAogIGNhY2hlID0gRkFMU0UsIHRpZHkgPSBGQUxTRSwgc2l6ZSA9ICJzbWFsbCIpCmBgYAoKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlY2hvPVRSVUV9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKIyBXYXRjaCB0aGUgdmlkZW8gZm9yIFNlc3Npb24gNAoKCldhdGNoIHRoZSB2aWRlbyBsZWN0dXJlIGFzc29jaWF0ZWQgdG8gdGhpcyBbc2Vzc2lvbl0oaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj00YU5CZGxfcWNCYykuCgpZb3UgY2FuIGZpbmQgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBvbiB0aWR5dmVyc2UgYW5kIHRoZSBwaXBpbmcgaW5mb3JtYXRpb24gYXQgdGhlIGZvbGxvd2luZyBbbGlua10oaHR0cHM6Ly9yYWZhbGFiLmdpdGh1Yi5pby9kc2Jvb2svdGlkeXZlcnNlLmh0bWwpLgoKCk9uY2UgeW91J3JlIGRvbmU6CgooYSkgSW5zdGFsbCB0aGUgdGlkeXZlcnNlIHBhY2thZ2Ugb24geW91ciBsb2NhbCBtYWNoaW5lLgpgYGB7ciwgZXZhbCA9RkFMU0V9CiMgaW5zdGFsbC5wYWNrYWdlcygndGlkeXZlcnNlJykKCnNldC5zZWVkKDEyMykKcm5vcm0oMSkKYGBgClRpZHl2ZXJzZSBpcyBhIGNvbGxlY3Rpb24gb2YgUiBwYWNrYWdlcyBmb3IgYmV0dGVyIGRhdGEgY2xlYW5pbmcsIHdyYW5nbGluZyBhbmQgdmlzdWFsaXphdGlvbi4gSW4gcGFydGljdWxhciwgaXQgaW5jbHVkZXM6CgotIFRoZSBkcGx5ciBhbmQgdGlkeXIgcGFja2FnZXMgcHJlc2VudGVkIGluIHRoZSB2aWRlbywgd2hpY2ggbWFrZSBkYXRhIGhhbmRsaW5nIGFuZCB0aWR5aW5nIGVhc2llci4KICAKLSAgVGhlIGdncGxvdDIgcGFja2FnZSBmb3IgZGF0YSB2aXN1YWxpemF0aW9uIHdoaWNoIHdlIHdpbGwgZ2V0IG1vcmUgaW50byBuZXh0IHdlZWsKCi0gQWRkaXRpb25hbCBwYWNrYWdlcyB0byBoYW5kbGUgc3RyaW5ncywgZXRjLgoKKGIpIGdvIHRocm91Z2ggdGhlIGZvbGxvd2luZyBleGVyY2lzZXMuCgoKIyBFeGVyY2lzZSAwOiBTd2lybAoKU2ltaWxhciB0byBsYXN0IHdlZWssIHdlJ2xsIHVzZSB0aGUgc3dpcmwgcGFja2FnZSB0byBpbnRyb2R1Y2UgeW91IHRoZSBwaXBpbmcsIGNoYWluaW5nLCBhbmQgZHBseXIgc3ludGF4LgoKV2FsayB0aHJvdWdoIHRoZSBHZXR0aW5nIGFuZCBDbGVhbmluZyBEYXRhIGluIHRoZSBzd2lybCBwYWNrYWdlLiAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBldmFsPUZBTFNFfQpsaWJyYXJ5KHN3aXJsKQpzd2lybDo6aW5zdGFsbF9jb3Vyc2UoIkdldHRpbmcgYW5kIENsZWFuaW5nIERhdGEiKQpzd2lybCgpCgpgYGAKCkdvIHRocm91Z2ggbW9kdWxlcyAxIHRocm91Z2ggMy4KT25jZSB5b3UncmUgZG9uZSwgcHJlcGFyZSB0aGUgZm9sbG93aW5nIGV4ZXJjaXNlcyB3aGljaCB3ZSB3aWxsIHVzZSBpbiBvdXIgZGlzY3Vzc2lvbiBzZXNzaW9uIG9uIE1vbmRheSwgSnVseSA2dGguCgoKIyBFeGVyY2lzZSAxOiBNb3ZpZXMgRGF0YXNldAoKIyMgUGFydCAwOiBMb2FkaW5nIHRoZSBkYXRhCgpMb2FkIGluIHRoZSBkYXRhc2V0IGBtb3ZpZXMuY3N2YCBhdCB0aGUgZm9sbG93aW5nIGh0dHAgYWRkcmVzczoKCmBgYHtyLCBlY2hvPVRSVUV9CmxpYnJhcnkodGlkeXZlcnNlKQp1cmwgPC0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9KdWFuZXRzL21vdmllLXN0YXRzL21hc3Rlci9tb3ZpZXMuY3N2Igptb3ZpZXMgPC0gcmVhZF9jc3YodXJsKQpgYGAKClNpbmNlIHRoaXMgaXMgdGhlIGZpcnN0IHJlYWwgZGF0YXNldCB0aGF0IHlvdSBhcmUgaGFuZGxpbmcsIGxldCdzIHRyeSB0byBnbyB0aHJvdWdoIHRoZSB1c3VhbCBpbnNwZWN0aW9uIHRoYXQgeW91J2QgY2FycnkgdGhyb3VnaCB3aGVuZXZlciB5b3Ugd2lsbCBiZSBwcmVzZW50ZWQgd2l0aCBhIGRhdGFzZXQKCldoYXQgaXMgdGhlIHNpemUgb2YgeW91ciBkYXRhc2V0PyBXaGF0IGFyZSB0aGUgdHlwZXMgb2YgdGhlIGRpZmZlcmVudCB2YXJpYWJsZXM/CgpgYGB7cn0KcHJpbnQoZGltKG1vdmllcykpCnByaW50KG5hbWVzKG1vdmllcykpCmBgYAoKVHlwZSBpbiBgbmFtZXMobW92aWVzKWAuIFRoaXMgc2hvdWxkIHByaW50IG91dCB0aGUgbmFtZXMgb2YgdGhlIGRpZmZlcmVudCBjb2x1bW5zIGluIHlvdXIgZGF0YXNldC4KCiMjIFBhcnQgMTogRWFzaW5nIGludG8gYGRwbHlyYCB2ZXJicwoKYS4gVXNpbmcgdGhlIGB0YWxseSgpYCBmdW5jdGlvbiwgY291bnQgdGhlIG51bWJlciBvZiBlbnRyaWVzIGluIHlvdXIgZGF0YXNldC4KCmBgYHtyfQptb3ZpZXMgJT4lIHRhbGx5KCkKIyMgb3IKdGFsbHkobW92aWVzKQoKYGBgCgoKYi4gVXNpbmcgdGhlIGBjb3VudGAgZnVuY3Rpb24sIGNvdW50IHRoZSBudW1iZXIgb2YgZGlzdGluY3QgbW92aWVzIGluIHRoZSBkYXRhc2V0LgpgY291bnQoKWAgaXMgc2ltaWxhciBidXQgY2FsbHMgZ3JvdXBfYnkoKSBiZWZvcmUgYW5kIHVuZ3JvdXAoKSBhZnRlci4gSXQgd2lsbCBhbGxvdyB5b3UgdG8gZ3JvdXAgdGhlIGVudHJpZXMgYnkgbW92aWUgbmFtZSwgYW5kIHRodXMsIHRvIGNvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIG1vdmllcy4KCllvdXIgY29kZSBzaG91bGQgbG9vayBzb21ldGhpbmcgbGlrZSBiZWxvdy4KCmBgYHtyLCBldmFsPVRSVUV9CmRpbShtb3ZpZXMgJT4lIAogIGNvdW50KG5hbWUpKQojIyMgb3IKCmxlbmd0aCh1bmlxdWUobW92aWVzJG5hbWUpKQoKYGBgCgpjLiBOb3csIHdlIHdpbGwgZm9jdXMgb24gYnVpbGRpbmcgYSBzdW1tYXJ5IHRhYmxlIGZvciB0aGUgbW92aWUgc3RhcnMgaW4gdGhpcyBkYXRhc2V0LgogICBpLiBVc2luZyB0aGUgYHNlbGVjdGAgZnVuY3Rpb24sIGtlZXAgb25seSB0aGUgY29sdW1ucyAiYnVkZ2V0IiwgImFjdG9yIiwgInJ1bnRpbWUiLCAiZ3Jvc3MiLCAidm90ZXMiLCAic2NvcmUiLCBhbmQgInllYXIiLiBOYW1lIHRoaXMgc3Vic2V0dGVkIGRhdGFmcmFtZSBtb3ZpZXNfc3ViLgogICAKICAgWW91ciBjb2RlIHNob3VsZCBsb29rIHNvbWV0aGluZyBsaWtlOgoKYGBge3IsIGV2YWw9RkFMU0V9Cm1vdmllc19zdWIgPSBtb3ZpZXMgJT4lIAogIHNlbGVjdChjb2wxLCBjb2wyKQpgYGAKCgpgYGB7ciwgZXZhbD1UUlVFfQptb3ZpZXNfc3ViID0gbW92aWVzICU+JSAKICBzZWxlY3QoYnVkZ2V0LCBzdGFyLCBydW50aW1lLCBncm9zcywgdm90ZXMsIHllYXIsIHNjb3JlKQoKIyMjb3IKc2VsZWN0KG1vdmllc19zdWIsIGJ1ZGdldCwgcnVudGltZSwgZ3Jvc3MsICB2b3RlcywgeWVhciwgc2NvcmUpCmBgYAogCiAKICBpaS4gQWRkIGEgY29sdW1uIHRoYXQgY291bnRzIGhvdyBtYW55IG1vdmllcyBlYWNoIGFjdG9yIGFwcGVhcnMgaW4gYW5kIG5hbWUgaXQgImFjdG9yX24iCiAgICBZb3VyIGNvZGUgc2hvdWxkIGxvb2sgc29tZXRoaW5nIGxpa2U6CgpgYGB7ciwgZXZhbD1GQUxTRX0KbW92aWVzX3N1YiA9IGFkZF9jb3VudChtb3ZpZXNfc3ViLCBzdGFycywgbmFtZT0iYWN0b3JfbiIpCmBgYAoKYGBge3IsIGV2YWw9VFJVRX0KbW92aWVzX3N1YiA9IGFkZF9jb3VudChtb3ZpZXNfc3ViLCBzdGFyLCBuYW1lPSJhY3Rvcl9uIikKCiMjIyMgQWx0ZXJuYXRpdmUgd2l0aCBwaXBlcwptb3ZpZXNfc3ViICU+JSAKICBhZGRfY291bnQoc3RhciwgbmFtZT0iYWN0b3JfbjIiKQoKYGBgCiAgCiAgIGlpaS4gVXNpbmcgdGhlIGBncm91cF9ieWAgYW5kIHRoZSBgc3VtbWFyaXplX2FsbGAgZnVuY3Rpb24sIGdyb3VwIHRoZSBtb3ZpZXMgYnkgYWN0b3IgYW5kIGdpdmUgdGhlIG1lYW4gb2YgZWFjaCBjb2x1bW4uIEtlZXAgdGhlIHJlc3VsdCBpbiBhIHRhYmxlIGNhbGxlZCBhY3RvcnMuCiAgIAogICBZb3VyIGNvZGUgc2hvdWxkIGxvb2sgc29tZXRoaW5nIGxpa2U6CgpgYGB7ciwgZXZhbD1UUlVFfQphY3RvcnMgPSBtb3ZpZXNfc3ViICU+JQogICAgICAgICAgIGdyb3VwX2J5KHN0YXIpICU+JQogICAgICAgICAgIHN1bW1hcmlzZV9hbGwobWVhbikKCiMjIyBhbHRlcm5hdGl2ZQoKYWN0b3JzMiA9IG1vdmllcyU+JQogICAgICAgICAgIGdyb3VwX2J5KHN0YXIpICU+JQogICAgICAgICAgIHN1bW1hcmlzZV9pZihpcy5udW1lcmljLCBtZWFuKQpgYGAKIAogaXYuIFVzaW5nIHRoZSBgZmlsdGVyYCBmdW5jdGlvbiwga2VlcCBvbmx5IHRoZSBhY3RvcnMgdGhhdCBoYXZlIGEgbWVhbiBzY29yZSBhYm92ZSA3LiBZb3VyIGNvZGUgc2hvdWxkIGxvb2sgc29tZXRoaW5nIGxpa2U6CgpgYGB7ciwgZXZhbD1GQUxTRX0KYWN0b3JzID0gYWN0b3JzICU+JQogICAgICAgICAgZmlsdGVyKGZpbHRlcmluZ19jcml0ZXJpb24pCmBgYAogIApgYGB7ciwgZXZhbD1UUlVFfQphY3RvcnMgPSBhY3RvcnMgJT4lCiAgICAgICAgICBmaWx0ZXIoc2NvcmU+PTcpCmBgYAogIAp2LiBGaW5hbGx5LCB1c2luZyB0aGUgYHRvcF9uYCBmdW5jdGlvbiwgZGlzcGxheSB0aGUgdG9wIDEwIGFjdG9ycyB3aXRoIHRoZSBoaWdoZXN0IGF2ZXJhZ2Ugdm90ZXMuIEFyZSB0aGVzZSB0aGUgc2FtZSBhcyB0aGUgdG9wIDEwIGFjdG9ycyBpbiB0ZXJtcyBvZiB0aGUgaGlnaGVzdCBidWRnZXQgZmlsbT8KCmBgYHtyLCBldmFsPVRSVUV9CgpwcmludChhY3RvcnMgJT4lCiAgdG9wX24oMTAsIHZvdGVzKSkKCnByaW50KGFjdG9ycyAlPiUKICB0b3BfbigxMCwgYnVkZ2V0KSkKYGBgCiAgIAoKIyMgUGFydCAyOiBVc2luZyBgZHBseXJgIHZlcmJzICh3aXRoIGxlc3MgZ3VpZGluZyEpCgphLiBTZWxlY3QgdGhlIG1vdmllcyBwcm9kdWNlZCBhZnRlciAyMDEwLiAKU2F2ZSB0aGUgc3Vic2V0IGluICdtb3ZpZXMuc3ViJyB2YXJpYWJsZS4KCgpgYGB7cn0KbW92aWVzLnN1YiA8LSBmaWx0ZXIobW92aWVzLCB5ZWFyID4gMjAxMCkKbW92aWVzLnN1YgpgYGAKCgpiLiBLZWVwIG9ubHkgdGhlIGNvbHVtbnMgJ25hbWUnLCAnZGlyZWN0b3InLCAneWVhcicsICdjb3VudHJ5JywgJ2dlbnJlJywgJ2J1ZGdldCcsICdncm9zcycsICdzY29yZScKaW4gdGhlICdtb3ZpZXMuc3ViJy4KCgoKYGBge3J9Cm1vdmllcy5zdWIgPC0gc2VsZWN0KG1vdmllcy5zdWIsIG5hbWUsIGRpcmVjdG9yLCB5ZWFyLCBjb3VudHJ5LCBnZW5yZSwgYnVkZ2V0LCBncm9zcywgc2NvcmUpCm1vdmllcy5zdWIKYGBgCgpjLiBGaW5kIHRoZSBwcm9maXQgZm9yIGVhY2ggbW92aWUgaW4gJ21vdmllcy5zdWInIGFzIGEgZnJhY3Rpb24gb2YgaXRzIGJ1ZGdldC4KQ29udmVydCAnYnVkZ2V0JyBhbmQgJ2dyb3NzJyBjb2x1bW5zIG1pbGxpb24gZG9sbGFyIHVuaXRzIGZvdW5kZWQgdG8gdGhlIGZpcnN0IGRlY2ltYWwgcG9pbnQuClVzZSBgcm91bmQoKWAgdG8gcm91bmQgbnVtYmVycy4KCmBgYHtyfQptb3ZpZXMuc3ViIDwtIG11dGF0ZShtb3ZpZXMuc3ViLCAKICAgICAgICAgICAgICAgICAgICAgZnJhY19wcm9maXQgPSAoZ3Jvc3MgLSBidWRnZXQpL2J1ZGdldCwKICAgICAgICAgICAgICAgICAgICAgYnVkZ2V0ID0gcm91bmQoYnVkZ2V0LzEwXjYsIGRpZ2l0cyA9IDEpLAogICAgICAgICAgICAgICAgICAgICBncm9zcyA9IHJvdW5kKGdyb3NzLzEwXjYsIGRpZ2l0cyA9IDEpKQptb3ZpZXMuc3ViCmBgYAoKCmQuIENvdW50IHRoZSBudW1iZXIgb2YgbW92aWVzIGluICdtb3ZpZXMuc3ViJyBwcm9kdWNlZCBieSBlYWNoIGdlbnJlLAphbmQgb3JkZXIgdGhlbSBpbiB0aGUgZGVzY2VuZGluZyBjb3VudCBvcmRlci4KCmBgYHtyfQpieV9nZW5yZSA8LSBncm91cF9ieShtb3ZpZXMuc3ViLCBnZW5yZSkKYXJyYW5nZShzdW1tYXJpc2UoYnlfZ2VucmUsIGNvdW50ID0gbigpKSwgZGVzYyhjb3VudCkpCmBgYAoKCgplLiBOb3cgZ3JvdXAgbW92aWVzIGluICdtb3ZpZXMuc3ViJyBieSBjb3VudHJpZXMgYW5kIGdlbnJlLgpUaGVuLCBjb3VudCB0aGUgbnVtYmVyIG9mIG1vdmllcyBpbiBlYWNoIGdyb3VwIGFuZCB0aGUgY29ycmVzcG9uZGluZyAKbWVkaWFuIGZyYWN0aW9uYWwgcHJvZml0LCB0aGUgbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIAp0aGUgbW92aWUgc2NvcmUgZm9yIGVhY2ggZ3JvdXAuCgpgYGB7cn0KbW92aWVzLnN1bW1hcnkgPC0gbW92aWVzLnN1YiAlPiUKICBncm91cF9ieShnZW5yZSwgY291bnRyeSkgJT4lCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpLCAKICAgICAgICAgICAgbWVkaWFuX3Byb2ZpdCA9IG1lZGlhbihmcmFjX3Byb2ZpdCksCiAgICAgICAgICAgIG1lYW5fc2NvcmUgPSBtZWFuKHNjb3JlKSwgCiAgICAgICAgICAgIHNkX3Njb3JlID0gc2Qoc2NvcmUpKSAlPiUKICBmaWx0ZXIoY291bnQgPiAxMCkgJT4lCiAgYXJyYW5nZShkZXNjKG1lYW5fc2NvcmUpKQptb3ZpZXMuc3VtbWFyeQpgYGAKCgojIyBQYXJ0IDM6IENoYWluaW5nCgpVc2luZyBjaGFpbmluZyBhbmQgcGlwZXMsIGZvciBlYWNoIGdlbnJlIGZpbmQgdGhlIHRocmVlIGRpcmVjdG9ycyB0aGUKdG9wIG1lYW4gbW92aWUgc2NvcmVzIHJlY2VpdmVkIGZvciB0aGUgbW92aWVzIHByb2R1Y2VkIGFmdGVyIDIwMDAsCmFmdGVyIGZpbHRlcmluZyBvdXQgdGhlIGRpcmVjdG9ycyB3aXRoIGZld2VyIHRoYW4gNSBtb3ZpZXMgaW4gdG90YWwuCkhpbnQ6IFVzZSBgdG9wX24oKWAgZnVuY3Rpb24gdG8gc2VsZWN0IHRvcCBuIGZyb20gZWFjaCBncm91cC4KCkZpbmQgdGhlIG1vdmllcyBpbiB5b3VyIGZhdm91cml0ZSBnZW5yZSBieSB0aGUgNSBkaXJlY3RvcnMKeW91IGp1c3QgZm91bmQuIFRoZXNlIGNvdWxkIHNlcnZlIGFzIHN1Z2dlc3Rpb25zIGZvciB5b3VyIG5leHQgbW92aWUgbmlnaHQhCgpgYGB7cn0KdG9wNV9kaXIgPC0gbW92aWVzICU+JSAKICBmaWx0ZXIoeWVhciA+IDIwMDApICU+JQogIGdyb3VwX2J5KGdlbnJlLCBkaXJlY3RvcikgJT4lCiAgc3VtbWFyaXNlKAogICAgbWVhbl9zY29yZSA9IG1lYW4oc2NvcmUpLAogICAgY291bnQgPSBuKCkpICU+JQogIGZpbHRlcihjb3VudCA+PSAzKSAlPiUKICBncm91cF9ieShnZW5yZSkgJT4lCiAgdG9wX24oNSwgd3QgPSBtZWFuX3Njb3JlKQp0b3A1X2RpcgpgYGAKCgpgYGB7cn0KY2xhc3ModG9wNV9kaXIpCmBgYAoKU2VlIHRoYXQgYHRvcDVfZGlyYCBoYXMgYSBjbGFzcyAnZ3JvdXBlZF9kZicsIHNvIHdlIGNvbnZlcnQgaXQgdG8gCmEgZGF0YSBmcmFtZSBmaXJzdC4KYGBge3J9CnRvcERyYW1hRGlyIDwtIHRvcDVfZGlyICU+JSAKICBhc190aWJibGUoKSAlPiUKICBmaWx0ZXIoZ2VucmUgPT0gIkRyYW1hIikgJT4lIAogIHNlbGVjdChkaXJlY3RvcikKCmBgYAoKYGBge3J9Cm1vdmllcyAlPiUgCiAgZmlsdGVyKGdlbnJlID09ICJEcmFtYSIsIAogICAgICAgICBkaXJlY3RvciAlaW4lIHRvcERyYW1hRGlyW1siZGlyZWN0b3IiXV0pICU+JQogIHNlbGVjdChuYW1lLCBkaXJlY3RvciwgeWVhciwgc2NvcmUsIGdlbnJlLCBncm9zcywgYnVkZ2V0KSAlPiUKICBhcnJhbmdlKGRlc2Moc2NvcmUpKQpgYGAKCiMjIFBhcnQgNDogU3VtbWFyaWVzCgpVc2luZyBjaGFpbiBjb21tYW5kcyBhbmQgdGhlIGBzdW1tYXJpc2VgIGZ1bmN0aW9uOgoKYS4gRmluZCB0aGUgYXZlcmFnZSBhbmQgbWVkaWFuIG51bWJlciBvZiBtb3ZpZXMgdGhhdCBhIGRpcmVjdG9yIGhhcyBiZWVuIGludm9sdmVkIGluLgoKCmBgYHtyfQptb3ZpZXMgJT4lCiAgY291bnQoZGlyZWN0b3IsIG5hbWU9Im5fbW92aWVzIikgJT4lCiAgc3VtbWFyaXNlX2lmKGlzLm51bWVyaWMsIGxpc3QobWVhbiwgbWVkaWFuKSkKCmBgYAoKYi4gRmluZCB0aGUgbWF4aW11bSBudW1iZXIgb2YgX19hY3Rpb25fXyBtb3ZpZXMgdGhhdCBhIGRpcmVjdG9yIGlzIGludm9sdmVkIGluLgoKYGBge3J9Cgptb3ZpZXMgJT4lCiAgZmlsdGVyKGdlbnJlID09ICJBY3Rpb24iKSAlPiUKICBjb3VudChkaXJlY3RvciklPiUKICB0b3BfbigxLCBuKQoKYGBgCgoKCmMuIENvbXB1dGUgNXRoIGFuZCA5NXRoIHBlcmNlbnRpbGUgb2YgdGhlIGBncm9zc2AgYW1vdW50LCBwZXIgbW92aWUgZ2VucmUuCgoKYGBge3J9Cgptb3ZpZXMgJT4lCiAgZ3JvdXBfYnkoZ2VucmUpJT4lCiAgc3VtbWFyaXNlKGZpZnRoUSA9IHF1YW50aWxlKGdyb3NzLDAuMDUpLG5pbmV0eWZpZnRoUSA9IHF1YW50aWxlKGdyb3NzLDAuOTUpKQoKYGBgCgpkLiBJcyBidWRnZXQgYXNzb2NpYXRlZCB0byBzdWNjZXNzPyBUbyBhbnN3ZXIgdGhpcyBxdWVzdGlvbiwgY3JlYXRlIGFuIGFkZGl0aW9uYWwgY29sdW1uICB0byB5b3VyIGRhdGFzZXQgdXNpbmcgdGhlIGBtdXRhdGVgIGFuZCBgcmFua2AgZnVuY3Rpb24sIGNvcnJlc3BvbmRpbmcgdG8gdGhlIHJhbmsgb2YgZWFjaCBtb3ZpZSBpbiB0ZXJtcyBvZiBidWdldC4gQ2FsbCBpdCBgcmtgLiBUaGUgcmFuayBzaG91bGQgYmUgZGVzY2VuZGluZywgc28gdGhhdCB0aGUgcmFuayAxIGlzIHRoZSBtb3ZpZSB3aXRoIGhpZ2hlc3QgYnVkZ2V0LiBTZWxlY3QgdGhlIHRvcCAxMDAgbW92aWVzIHdpdGggaGlnaGVzdCB2b3RlLCBhbmQgY29tcHV0ZSB0aGVpciBtZWFuIGFuZCBtZWRpYW4gcmFuayBpbiB0ZXJtcyBvZiBidWRnZXQuCgpgYGB7cn0KCnJrX2J1ZGdldCA9IG1vdmllcyAlPiUKICBtdXRhdGUocmsgPSByYW5rKGJ1ZGdldCwgdGllcy5tZXRob2QgPSAiZmlyc3QiKSkgJT4lCiAgYXJyYW5nZShkZXNjKGJ1ZGdldCkpICU+JQogIHRvcF9uKDEwMCwgYnVkZ2V0KSAlPiUKICBzdW1tYXJpc2UobWVhbl9iZGc9IG1lYW4oYnVkZ2V0KSxtZWRpYW5fYmRnPSBtZWRpYW4oYnVkZ2V0KSApCgpya192b3RlID0gbW92aWVzICU+JQogIG11dGF0ZShyayA9IHJhbmsodm90ZXMsIHRpZXMubWV0aG9kID0gImZpcnN0IikpICU+JQogIGFycmFuZ2UoZGVzYyh2b3RlcykpICU+JQogIHRvcF9uKDEwMCwgdm90ZXMpICU+JQogIHN1bW1hcmlzZShtZWFuX2JkZz0gbWVhbihidWRnZXQpLG1lZGlhbl9iZGc9IG1lZGlhbihidWRnZXQpICkKCnByaW50KHJrX2J1ZGdldCkKcHJpbnQocmtfdm90ZSkKCgpgYGAKCgoKCgoKCgo=