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
- Using the
tally()
function, count the number of entries in your dataset.
movies %>% tally()
## or
tally(movies)
- 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))
- Now, we will focus on building a summary table for the movie stars in this dataset.
- 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)
- 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")
- 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)
- 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)
- 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!)
- Select the movies produced after 2010. Save the subset in ‘movies.sub’ variable.
movies.sub <- filter(movies, year > 2010)
movies.sub
- 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
- 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
- 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))
- 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:
- 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))
- Find the maximum number of action movies that a director is involved in.
movies %>%
filter(genre == "Action") %>%
count(director)%>%
top_n(1, n)
- 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))
- 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=