** Please click all the tabs (in sequence) to get the entire set of information in these pages. **
** You can download all the code by clicking “Code” as shown in this picture. **

First we’ll declare some useful configuration settings. Don’t worry if you don’t understand why.
knitr::opts_chunk$set(echo = TRUE, warning=FALSE, message=FALSE)
options(scipen=10000000)
options(digits=3)
Next add a few packages that are handy. These packages need to first be installed. If they’re not already in your system, you should see a note/yellow banner above from RStudio asking if it should install these packages for you. Silently mutter “Oh thanks RStudio” and click Yes.
# install.packages("knitr")
library(knitr)
library(dplyr)
library(tidyverse)
library(ggplot2)
library(gridExtra)
library(ggrepel)
library(boxoffice)
Now there is one special package that is not installed in the usual way. “boxoffice” (through which we’ll get movie sales data). To install it, run this next chunk once (by ensuring it says “eval=TRUE”) then switch to eval=FALSE.
install.packages("devtools")
devtools::install_github("jacobkap/boxoffice")
Session 2: Let’s get our hands dirty
Our first text (this) and code chunk (below).
2+2 #
[1] 4
1:5 # generate a sequence of integers
[1] 1 2 3 4 5
vector.1 <- 1:5 # assign the name vector.1 to this sequence
vector.1 + 5 # add 5 to every element of the vector
[1] 6 7 8 9 10
vector.2 <- vector.1 + 5:6 # add 5 to the 1st element, then 6 to second,
vector.2 # print vector.2
[1] 6 8 8 10 10
mean(vector.1)
[1] 3
c(sum(vector.1), sum(vector.2), mean(vector.2), min(vector.2), max(vector.2))
[1] 15.0 42.0 8.4 6.0 10.0
The movies data
Ok, now that we’ve done some basic things let’s move on to something useful. We’ll pull data about movie sales from the “boxoffice” data source (for which we installed the boxoffice package above). First we have to decide what time frame we want the data for.
# Let's define time periods for which to collect data
date.seq <- paste(2000:2009,"-12-31",sep="")
# date.seq <- c(as.Date("2013-12-31"),as.Date("2014-12-31"),as.Date("2015-12-31"), as.Date("2016-12-31"),as.Date("2017-12-31"),as.Date("2018-12-31"),as.Date("2019-12-31"))
# Fetch the data
movies <- boxoffice(date = as.Date(date.seq), top_n = 50)
dim(movies) # what is the size of the data frame
[1] 189 9
names(movies) # or, movies %>% names # names of the columns of the data frame
[1] "movie" "distributor" "gross" "percent_change"
[5] "theaters" "per_theater" "total_gross" "days"
[9] "date"
kable(head(movies))
Cast Away |
20th Century |
7938594 |
-32 |
2927 |
2712 |
100628594 |
10 |
2000-12-31 |
What Women Want |
Paramount Pi |
4955561 |
-40 |
3046 |
1627 |
110187561 |
17 |
2000-12-31 |
The Family Man |
Universal |
3010330 |
-40 |
2395 |
1257 |
39170330 |
10 |
2000-12-31 |
The Emperor’s New Groove |
Walt Disney |
2814336 |
-29 |
2887 |
975 |
47465336 |
17 |
2000-12-31 |
Miss Congeniality |
Warner Bros. |
2142573 |
-64 |
2668 |
803 |
40784573 |
10 |
2000-12-31 |
How the Grinch Stole Chri |
Universal |
1524105 |
-42 |
3170 |
481 |
251629105 |
45 |
2000-12-31 |
A few commands featured above include 1) assignment to an object, 2) selection of a subset of data from a data frame,
We can modify or extend the data. For instance, we’ll want to isolate the Year (from the date field). Also, it will be useful to rank movies by sales (within each year), and create a new rank variable.
movies <- movies %>% na.omit() %>% mutate(Year = as.numeric(format(as.Date(date), "%Y"))) # na.omit() omits the rows with NA values; create new column Year. which extracts the Y (year) from the date
# Extract the Year, then Rank by Sales
movies <- movies %>% group_by(Year) %>% arrange(desc(total_gross)) %>% mutate(rank=row_number())
Visualizations of box office sales
Now let’s take a look at the data. You can look at the data in tabular form (let’s do that in the RStudio interface). But it will be more insightful to construct visualizations of the data. Let’s start by looking at total_gross revenues for each rank within each year.
p1 <- ggplot(data=movies, aes(x=rank,y=total_gross)) + geom_line(aes(color=as.factor(Year))) + theme_classic()
p2 <- p1 + coord_trans(y = "log10") # convert y axis to log scale
grid.arrange(p1, p2, ncol=2) # arrange both plots side by side, in two columns

What are the top movies of the year, and how much are they total_grossing? To make the question (or answers) more meaningful let’s limit the analysis to the top 10 movies each year. To get a sense of the differences in sales, let’s take a quick look at the #1 and #10 ranked movies each year.
movies.top10 <- movies %>% filter(rank %in% c(1,10)) %>% group_by(Year) %>% arrange(rank)
kable(movies.top10 %>% select(movie, Year, rank, total_gross) %>% arrange(Year))
How the Grinch Stole Chri |
2000 |
1 |
251629105 |
All the Pretty Horses |
2000 |
10 |
7640564 |
Harry Potter and the Sorc |
2001 |
1 |
288493000 |
A Beautiful Mind |
2001 |
10 |
15949000 |
Harry Potter and the Cham |
2002 |
1 |
243855000 |
The Hot Chick |
2002 |
10 |
24021000 |
The Lord of the Rings: Th |
2003 |
1 |
249400000 |
Peter Pan |
2003 |
10 |
22000000 |
The Polar Express |
2004 |
1 |
151623383 |
Harry Potter and the Gobl |
2005 |
1 |
273281180 |
The Ringer |
2005 |
10 |
17265628 |
The Polar Express |
2006 |
1 |
176454984 |
Rocky Balboa |
2006 |
10 |
47940632 |
I am Legend |
2007 |
1 |
199345154 |
Mr. Magorium’s Wonder Emp |
2007 |
10 |
31049456 |
The Dark Knight |
2008 |
1 |
530924926 |
Yes Man |
2008 |
10 |
60029690 |
The Twilight Saga: New Moon |
2009 |
1 |
284512392 |
Paranormal Activity |
2009 |
10 |
107792845 |
Looking at the numbers it seems that the top-1 and top-10 have hugely different sales numbers. Putting all of them (and all between these ranks) into the same chart will make it very hard to see the differences. In such cases it is useful to use a log transformation, which brings the numbers closer together and easier to see.
The graph we’ll produce has the rank as the x (horizontal) axis and gross revenues as the y (vertical axis). We’ll identify the movie itself by placing a dot (bullet) based on its (x,y) value, and write the name of the movie as close to the bullet as possible.
ggplot(data=movies %>% filter(rank < 11), aes(x=rank, y=total_gross, color=factor(Year))) + geom_point() + theme_classic() + theme(axis.text.x = element_text(size=12), axis.text.y = element_text(size=12), axis.title.x = element_text(size = rel(1.5)), axis.title.y = element_text(size = rel(1.5), margin = margin(t = 0, r = 20, b = 0, l = 10))) + geom_text_repel(aes(label = movie), nudge_y=1, force=6, box.padding = unit(0.75, "lines"), segment.color="gray") + coord_trans(y = 'log10')

Who’s making the winning movies? This is identified by the “distributor” column. So, this time we’ll write the distributor’s name rather than the movie name.
ggplot(data=movies %>% filter(rank < 11), aes(x=rank, y=total_gross, color=factor(Year))) + geom_point() + theme_classic() + theme(axis.text.x = element_text(size=12), axis.text.y = element_text(size=12), axis.title.x = element_text(size = rel(1.5)), axis.title.y = element_text(size = rel(1.5), margin = margin(t = 0, r = 20, b = 0, l = 10))) + geom_text_repel(aes(label = distributor), nudge_y=1, force=6, box.padding = unit(0.75, "lines"), segment.color="gray") + coord_trans(y = 'log10')

# ggsave()
LS0tCnRpdGxlOiAiU2Vzc2lvbiAyIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoqKiBQbGVhc2UgY2xpY2sgYWxsIHRoZSB0YWJzIChpbiBzZXF1ZW5jZSkgdG8gZ2V0IHRoZSBlbnRpcmUgc2V0IG9mIGluZm9ybWF0aW9uIGluIHRoZXNlIHBhZ2VzLiAqKgoKKiogWW91IGNhbiBkb3dubG9hZCBhbGwgdGhlIGNvZGUgYnkgY2xpY2tpbmcgIkNvZGUiIGFzIHNob3duIGluIHRoaXMgcGljdHVyZS4gKioKCmBgYHtyIGZpZy5hbGlnbj0iY2VudGVyIiwgb3V0LndpZHRoPSI1MCUiLCBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiSW1hZ2VzL3Nlc3Npb24yLWNvZGUucG5nIikKYGBgCgpGaXJzdCB3ZSdsbCBkZWNsYXJlIHNvbWUgdXNlZnVsIGNvbmZpZ3VyYXRpb24gc2V0dGluZ3MuIERvbid0IHdvcnJ5IGlmIHlvdSBkb24ndCB1bmRlcnN0YW5kIHdoeS4gCgpgYGB7ciBzZXR1cH0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFKQpvcHRpb25zKHNjaXBlbj0xMDAwMDAwMCkKb3B0aW9ucyhkaWdpdHM9MykKYGBgCgpOZXh0IGFkZCBhIGZldyBwYWNrYWdlcyB0aGF0IGFyZSBoYW5keS4gVGhlc2UgcGFja2FnZXMgbmVlZCB0byBmaXJzdCBiZSBpbnN0YWxsZWQuIElmIHRoZXkncmUgbm90IGFscmVhZHkgaW4geW91ciBzeXN0ZW0sIHlvdSBzaG91bGQgc2VlIGEgbm90ZS95ZWxsb3cgYmFubmVyIGFib3ZlIGZyb20gUlN0dWRpbyBhc2tpbmcgaWYgaXQgc2hvdWxkIGluc3RhbGwgdGhlc2UgcGFja2FnZXMgZm9yIHlvdS4gU2lsZW50bHkgbXV0dGVyICJPaCB0aGFua3MgUlN0dWRpbyIgYW5kIGNsaWNrIFllcy4gCgpgYGB7ciBwYWNrYWdlc30KIyBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpCmxpYnJhcnkoa25pdHIpCgpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KGJveG9mZmljZSkKYGBgCgpOb3cgdGhlcmUgaXMgb25lIHNwZWNpYWwgcGFja2FnZSB0aGF0IGlzIG5vdCBpbnN0YWxsZWQgaW4gdGhlIHVzdWFsIHdheS4gImJveG9mZmljZSIgKHRocm91Z2ggd2hpY2ggd2UnbGwgZ2V0IG1vdmllIHNhbGVzIGRhdGEpLiBUbyBpbnN0YWxsIGl0LCBydW4gdGhpcyBuZXh0IGNodW5rIG9uY2UgKGJ5IGVuc3VyaW5nIGl0IHNheXMgImV2YWw9VFJVRSIpIHRoZW4gc3dpdGNoIHRvIGV2YWw9RkFMU0UuIAoKYGBge3Igb25ldGltZSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXZhbD1GQUxTRX0gCmluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikKZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJqYWNvYmthcC9ib3hvZmZpY2UiKQpgYGAKCgojIyBTZXNzaW9uIDI6IExldCdzIGdldCBvdXIgaGFuZHMgZGlydHkgey50YWJzZXR9CgpPdXIgZmlyc3QgdGV4dCAodGhpcykgYW5kIGNvZGUgY2h1bmsgKGJlbG93KS4gCgpgYGB7ciBleGFtcGxlfQoyKzIgIyAKMTo1ICMgZ2VuZXJhdGUgYSBzZXF1ZW5jZSBvZiBpbnRlZ2Vycwp2ZWN0b3IuMSA8LSAxOjUgIyBhc3NpZ24gdGhlIG5hbWUgdmVjdG9yLjEgdG8gdGhpcyBzZXF1ZW5jZQp2ZWN0b3IuMSArIDUgIyBhZGQgNSB0byBldmVyeSBlbGVtZW50IG9mIHRoZSB2ZWN0b3IKdmVjdG9yLjIgPC0gdmVjdG9yLjEgKyA1OjYgIyBhZGQgNSB0byB0aGUgMXN0IGVsZW1lbnQsIHRoZW4gNiB0byBzZWNvbmQsIAp2ZWN0b3IuMiAjIHByaW50IHZlY3Rvci4yCm1lYW4odmVjdG9yLjEpCmMoc3VtKHZlY3Rvci4xKSwgc3VtKHZlY3Rvci4yKSwgbWVhbih2ZWN0b3IuMiksIG1pbih2ZWN0b3IuMiksIG1heCh2ZWN0b3IuMikpCmBgYAogCiMjIFRoZSBtb3ZpZXMgZGF0YSAKIApPaywgbm93IHRoYXQgd2UndmUgZG9uZSBzb21lIGJhc2ljIHRoaW5ncyBsZXQncyBtb3ZlIG9uIHRvIHNvbWV0aGluZyB1c2VmdWwuIFdlJ2xsIHB1bGwgZGF0YSBhYm91dCBtb3ZpZSBzYWxlcyBmcm9tIHRoZSAiYm94b2ZmaWNlIiBkYXRhIHNvdXJjZSAoZm9yIHdoaWNoIHdlIGluc3RhbGxlZCB0aGUgYm94b2ZmaWNlIHBhY2thZ2UgYWJvdmUpLiBGaXJzdCB3ZSBoYXZlIHRvIGRlY2lkZSB3aGF0IHRpbWUgZnJhbWUgd2Ugd2FudCB0aGUgZGF0YSBmb3IuICAKIApgYGB7ciBib3hvZmZpY2V9CiMgTGV0J3MgZGVmaW5lIHRpbWUgcGVyaW9kcyBmb3Igd2hpY2ggdG8gY29sbGVjdCBkYXRhCmRhdGUuc2VxIDwtIHBhc3RlKDIwMDA6MjAwOSwiLTEyLTMxIixzZXA9IiIpCgojIGRhdGUuc2VxIDwtIGMoYXMuRGF0ZSgiMjAxMy0xMi0zMSIpLGFzLkRhdGUoIjIwMTQtMTItMzEiKSxhcy5EYXRlKCIyMDE1LTEyLTMxIiksIGFzLkRhdGUoIjIwMTYtMTItMzEiKSxhcy5EYXRlKCIyMDE3LTEyLTMxIiksYXMuRGF0ZSgiMjAxOC0xMi0zMSIpLGFzLkRhdGUoIjIwMTktMTItMzEiKSkKCiMgRmV0Y2ggdGhlIGRhdGEgCm1vdmllcyA8LSBib3hvZmZpY2UoZGF0ZSA9IGFzLkRhdGUoZGF0ZS5zZXEpLCB0b3BfbiA9IDUwKQogCmRpbShtb3ZpZXMpICMgd2hhdCBpcyB0aGUgc2l6ZSBvZiB0aGUgZGF0YSBmcmFtZQpuYW1lcyhtb3ZpZXMpICMgb3IsIG1vdmllcyAlPiUgbmFtZXMgIyBuYW1lcyBvZiB0aGUgY29sdW1ucyBvZiB0aGUgZGF0YSBmcmFtZQprYWJsZShoZWFkKG1vdmllcykpCmBgYAoKCkEgZmV3IGNvbW1hbmRzIGZlYXR1cmVkIGFib3ZlIGluY2x1ZGUgMSkgYXNzaWdubWVudCB0byBhbiBvYmplY3QsIDIpIHNlbGVjdGlvbiBvZiBhIHN1YnNldCBvZiBkYXRhIGZyb20gYSBkYXRhIGZyYW1lLCAKCldlIGNhbiBtb2RpZnkgb3IgZXh0ZW5kIHRoZSBkYXRhLiBGb3IgaW5zdGFuY2UsIHdlJ2xsIHdhbnQgdG8gaXNvbGF0ZSB0aGUgWWVhciAoZnJvbSB0aGUgZGF0ZSBmaWVsZCkuIEFsc28sIGl0IHdpbGwgYmUgdXNlZnVsIHRvIHJhbmsgbW92aWVzIGJ5IHNhbGVzICh3aXRoaW4gZWFjaCB5ZWFyKSwgYW5kIGNyZWF0ZSBhIG5ldyByYW5rIHZhcmlhYmxlLiAKCmBgYHtyIG1vdmllcy5leHRlbmR9Cgptb3ZpZXMgPC0gbW92aWVzICU+JSBuYS5vbWl0KCkgJT4lIG11dGF0ZShZZWFyID0gIGFzLm51bWVyaWMoZm9ybWF0KGFzLkRhdGUoZGF0ZSksICIlWSIpKSkgIyBuYS5vbWl0KCkgb21pdHMgdGhlIHJvd3Mgd2l0aCBOQSB2YWx1ZXM7IGNyZWF0ZSBuZXcgY29sdW1uIFllYXIuIHdoaWNoIGV4dHJhY3RzIHRoZSBZICh5ZWFyKSBmcm9tIHRoZSBkYXRlCgojIEV4dHJhY3QgdGhlIFllYXIsIHRoZW4gUmFuayBieSBTYWxlcwoKbW92aWVzIDwtIG1vdmllcyAlPiUgZ3JvdXBfYnkoWWVhcikgJT4lIGFycmFuZ2UoZGVzYyh0b3RhbF9ncm9zcykpICU+JSAgbXV0YXRlKHJhbms9cm93X251bWJlcigpKQoKYGBgCgojIyMgVmlzdWFsaXphdGlvbnMgb2YgYm94IG9mZmljZSBzYWxlcwoKTm93IGxldCdzIHRha2UgYSBsb29rIGF0IHRoZSBkYXRhLiBZb3UgY2FuIGxvb2sgYXQgdGhlIGRhdGEgaW4gdGFidWxhciBmb3JtIChsZXQncyBkbyB0aGF0IGluIHRoZSBSU3R1ZGlvIGludGVyZmFjZSkuIEJ1dCBpdCB3aWxsIGJlIG1vcmUgaW5zaWdodGZ1bCB0byBjb25zdHJ1Y3QgdmlzdWFsaXphdGlvbnMgb2YgdGhlIGRhdGEuIExldCdzIHN0YXJ0IGJ5IGxvb2tpbmcgYXQgdG90YWxfZ3Jvc3MgcmV2ZW51ZXMgZm9yIGVhY2ggcmFuayB3aXRoaW4gZWFjaCB5ZWFyLiAKCmBgYHtyIG1vdmllcy5yYW5rLnBsb3QsZmlnLndpZHRoPTE0LGZpZy5oZWlnaHQ9M30KcDEgPC0gZ2dwbG90KGRhdGE9bW92aWVzLCBhZXMoeD1yYW5rLHk9dG90YWxfZ3Jvc3MpKSArIGdlb21fbGluZShhZXMoY29sb3I9YXMuZmFjdG9yKFllYXIpKSkgKyB0aGVtZV9jbGFzc2ljKCkKCnAyIDwtIHAxICsgY29vcmRfdHJhbnMoeSA9ICJsb2cxMCIpICMgY29udmVydCB5IGF4aXMgdG8gbG9nIHNjYWxlCgpncmlkLmFycmFuZ2UocDEsIHAyLCBuY29sPTIpICMgYXJyYW5nZSBib3RoIHBsb3RzIHNpZGUgYnkgc2lkZSwgaW4gdHdvIGNvbHVtbnMKYGBgCgoKV2hhdCBhcmUgdGhlIHRvcCBtb3ZpZXMgb2YgdGhlIHllYXIsIGFuZCBob3cgbXVjaCBhcmUgdGhleSB0b3RhbF9ncm9zc2luZz8gVG8gbWFrZSB0aGUgcXVlc3Rpb24gKG9yIGFuc3dlcnMpIG1vcmUgbWVhbmluZ2Z1bCBsZXQncyBsaW1pdCB0aGUgYW5hbHlzaXMgdG8gdGhlIHRvcCAxMCBtb3ZpZXMgZWFjaCB5ZWFyLiBUbyBnZXQgYSBzZW5zZSBvZiB0aGUgZGlmZmVyZW5jZXMgaW4gc2FsZXMsIGxldCdzIHRha2UgYSBxdWljayBsb29rIGF0IHRoZSAjMSBhbmQgIzEwIHJhbmtlZCBtb3ZpZXMgZWFjaCB5ZWFyLiAKCmBgYHtyIHRvcDEwfQptb3ZpZXMudG9wMTAgPC0gbW92aWVzICU+JSBmaWx0ZXIocmFuayAlaW4lIGMoMSwxMCkpICU+JSBncm91cF9ieShZZWFyKSAlPiUgYXJyYW5nZShyYW5rKQprYWJsZShtb3ZpZXMudG9wMTAgJT4lIHNlbGVjdChtb3ZpZSwgWWVhciwgcmFuaywgdG90YWxfZ3Jvc3MpICU+JSBhcnJhbmdlKFllYXIpKQpgYGAKCkxvb2tpbmcgYXQgdGhlIG51bWJlcnMgaXQgc2VlbXMgdGhhdCB0aGUgdG9wLTEgYW5kIHRvcC0xMCBoYXZlIGh1Z2VseSBkaWZmZXJlbnQgc2FsZXMgbnVtYmVycy4gUHV0dGluZyBhbGwgb2YgdGhlbSAoYW5kIGFsbCBiZXR3ZWVuIHRoZXNlIHJhbmtzKSBpbnRvIHRoZSBzYW1lIGNoYXJ0IHdpbGwgbWFrZSBpdCB2ZXJ5IGhhcmQgdG8gc2VlIHRoZSBkaWZmZXJlbmNlcy4gSW4gc3VjaCBjYXNlcyBpdCBpcyB1c2VmdWwgdG8gdXNlIGEgbG9nIHRyYW5zZm9ybWF0aW9uLCB3aGljaCBicmluZ3MgdGhlIG51bWJlcnMgY2xvc2VyIHRvZ2V0aGVyIGFuZCBlYXNpZXIgdG8gc2VlLiAKClRoZSBncmFwaCB3ZSdsbCBwcm9kdWNlIGhhcyB0aGUgcmFuayBhcyB0aGUgeCAoaG9yaXpvbnRhbCkgYXhpcyBhbmQgZ3Jvc3MgcmV2ZW51ZXMgYXMgdGhlIHkgKHZlcnRpY2FsIGF4aXMpLiBXZSdsbCBpZGVudGlmeSB0aGUgbW92aWUgaXRzZWxmIGJ5IHBsYWNpbmcgYSBkb3QgKGJ1bGxldCkgYmFzZWQgb24gaXRzICh4LHkpIHZhbHVlLCBhbmQgd3JpdGUgdGhlIG5hbWUgb2YgdGhlIG1vdmllIGFzIGNsb3NlIHRvIHRoZSBidWxsZXQgYXMgcG9zc2libGUuIAoKYGBge3IgbW92aWVzLnJhbmtzLCBmaWcud2lkdGg9MTUsZmlnLmhlaWdodD03fQpnZ3Bsb3QoZGF0YT1tb3ZpZXMgJT4lIGZpbHRlcihyYW5rIDwgMTEpLCBhZXMoeD1yYW5rLCB5PXRvdGFsX2dyb3NzLCBjb2xvcj1mYWN0b3IoWWVhcikpKSArIGdlb21fcG9pbnQoKSArIHRoZW1lX2NsYXNzaWMoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9MTIpLCBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTEyKSwgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMS41KSksIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDEuNSksIG1hcmdpbiA9IG1hcmdpbih0ID0gMCwgciA9IDIwLCBiID0gMCwgbCA9IDEwKSkpICsgZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IG1vdmllKSwgbnVkZ2VfeT0xLCBmb3JjZT02LCBib3gucGFkZGluZyA9IHVuaXQoMC43NSwgImxpbmVzIiksIHNlZ21lbnQuY29sb3I9ImdyYXkiKSArIGNvb3JkX3RyYW5zKHkgPSAnbG9nMTAnKQpgYGAKCldobydzIG1ha2luZyB0aGUgd2lubmluZyBtb3ZpZXM/IFRoaXMgaXMgaWRlbnRpZmllZCBieSB0aGUgImRpc3RyaWJ1dG9yIiBjb2x1bW4uIFNvLCB0aGlzIHRpbWUgd2UnbGwgd3JpdGUgdGhlIGRpc3RyaWJ1dG9yJ3MgbmFtZSByYXRoZXIgdGhhbiB0aGUgbW92aWUgbmFtZS4gCgpgYGB7ciBtb3ZpZXMucmFua3MuZGlzdHJpYnV0b3IsICBmaWcud2lkdGg9MTUsZmlnLmhlaWdodD03fQpnZ3Bsb3QoZGF0YT1tb3ZpZXMgJT4lIGZpbHRlcihyYW5rIDwgMTEpLCBhZXMoeD1yYW5rLCB5PXRvdGFsX2dyb3NzLCBjb2xvcj1mYWN0b3IoWWVhcikpKSArIGdlb21fcG9pbnQoKSArIHRoZW1lX2NsYXNzaWMoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9MTIpLCBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTEyKSwgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMS41KSksIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDEuNSksIG1hcmdpbiA9IG1hcmdpbih0ID0gMCwgciA9IDIwLCBiID0gMCwgbCA9IDEwKSkpICsgZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IGRpc3RyaWJ1dG9yKSwgbnVkZ2VfeT0xLCBmb3JjZT02LCBib3gucGFkZGluZyA9IHVuaXQoMC43NSwgImxpbmVzIiksIHNlZ21lbnQuY29sb3I9ImdyYXkiKSArIGNvb3JkX3RyYW5zKHkgPSAnbG9nMTAnKQoKIyBnZ3NhdmUoKQpgYGAKCgoKCgo=