Application: the BIXI Bikeshare Data
dataset
Lets take a step back and zoom a bit into different forms of visualization. We will now take a look at the BIXI Bikeshare Data
, covering 500k bike-rides in the BIXI bike-sharing system in Montreal.
bike <- readRDS(url("https://github.com/SDS-AAU/SDS-master/raw/master/00_data/bikes_montreal.rds?dl=1"))
Lets take a look:
bike %>% glimpse()
Rows: 500,000
Columns: 12
$ start_date <dttm> 2017-08-16 12:10:00, 2017-06-25 23:22:00, 2017-08-10 17:26:00, 2017-08-17 15:25:00, 2017-10-12 10:39:00, 2017-07-03 08:53:00, 2…
$ start_station_code <int> 6213, 6393, 6114, 6044, 6389, 6411, 6738, 6425, 7042, 6034, 6213, 6184, 6008, 6202, 6048, 6087, 6195, 6013, 6168, 6154, 6901, 62…
$ end_date <dttm> 2017-08-16 12:30:00, 2017-06-25 23:27:00, 2017-08-10 17:29:00, 2017-08-17 15:32:00, 2017-10-12 10:49:00, 2017-07-03 08:55:00, 2…
$ end_station_code <int> 6391, 6394, 6113, 6015, 6262, 6206, 6090, 6406, 6185, 6039, 6188, 6142, 6012, 6038, 6020, 6032, 7080, 6078, 6302, 6164, 6011, 62…
$ duration_sec <int> 1237, 294, 156, 419, 601, 108, 438, 757, 1144, 578, 366, 326, 154, 835, 863, 565, 929, 262, 633, 559, 1071, 120, 847, 1223, 287,…
$ start_day <date> 2017-08-16, 2017-06-25, 2017-08-10, 2017-08-17, 2017-10-12, 2017-07-03, 2017-07-04, 2017-06-27, 2017-11-09, 2017-07-03, 2017-05…
$ start_dow <fct> Wed, Sun, Thu, Thu, Thu, Mon, Tue, Tue, Thu, Mon, Wed, Thu, Wed, Thu, Wed, Wed, Mon, Sun, Thu, Wed, Thu, Wed, Sun, Sat, Sun, Thu…
$ weekday <fct> workweek, weekend, workweek, workweek, workweek, workweek, workweek, workweek, workweek, workweek, workweek, workweek, workweek,…
$ start_hod <dbl> 12, 23, 17, 15, 10, 8, 16, 17, 9, 17, 16, 8, 10, 18, 7, 11, 8, 15, 23, 16, 8, 22, 9, 22, 19, 17, 14, 18, 15, 14, 21, 20, 14, 17,…
$ start_mon <dbl> 8, 6, 8, 8, 10, 7, 7, 6, 11, 7, 5, 6, 8, 8, 10, 7, 8, 10, 6, 5, 8, 5, 4, 8, 6, 5, 9, 4, 10, 8, 9, 10, 8, 5, 5, 6, 7, 6, 6, 6, 10…
$ start_wk <dbl> 33, 26, 32, 33, 41, 27, 27, 26, 45, 27, 22, 26, 33, 32, 40, 30, 33, 41, 26, 19, 35, 22, 17, 33, 23, 19, 39, 16, 42, 33, 37, 43, …
$ membership <fct> member, non-member, member, member, member, member, member, member, member, member, member, member, member, member, member, memb…
bike %>% head()
We see here a number of different variable types present, namely:
- Continuous variables
- Categorical variables
- Temporal variables
First of all: Lets remember, the first thing we do is defining the aestetics, first of all the dimensions (x, y) of the visualization.
bike %>% ggplot(aes(x = weekday, y = start_hod))
The result will be an empty plane with the dimensions we defined. Note that there are more aestetic dimensions which can be used to convey informations visualy, such as for instance:
- Position (x, y)
- Color
- Shape
- Alpha (Transparency)
We will explore them later.
Basic visualization of variable types
Summaries of One Variable: Continuous
When attempting to summarize a single variable, histograms and density distributions are often the visualization of choice. We can do that easily by using the geom_histogram()
layer. Notice that we only define a x
aestetic, since we only summarize one variable
bike %>% ggplot(aes(x = duration_sec)) +
geom_histogram()
To plot a probability density function (PDF) instead, we can use the geom_density()
layer.
bike %>% ggplot(aes(x = duration_sec)) +
geom_density()
Note the distribution appears right-skewed, since we have some outliers of very long bike rides. Adding a log-scale on the x-axis might help to reduce their impact on the visualization.
bike %>% ggplot(aes(x = duration_sec)) +
geom_histogram() +
scale_x_log10()
In case we would already like to start looking at conditional distributions, we could add an adittional fill
aestetic.
bike %>% ggplot(aes(x = duration_sec, fill = weekday)) +
geom_histogram() +
scale_x_log10()
Summaries of One Variable: Discrete
To do the same for a discrete variable, we would start with a simple barplot via geom_bar()
. Notice again that we only define a x aestetic. ggplot
per default will use the count on the y-axis.
bike %>% ggplot(aes(x = start_dow)) +
geom_bar()
We could also use the membership as fill aestetic to map further information in the plot.
bike %>% ggplot(aes(x = start_dow, fill = membership)) +
geom_bar()
Summaries of One Variable: Temporal
A temporal variable can also be visualized as a line-plot with geom_line()
.
bike %>%
count(start_wk) %>%
ggplot(aes(x = start_wk, y = n)) +
geom_line()
To instead (or in addition) add a trendline, we can use geom_smooth()
bike %>%
count(start_wk) %>%
ggplot(aes(x = start_wk, y = n)) +
geom_smooth()
Summarizing multiple variablea jointly
Ok, that was pretty easy. However, the insights gained so far are pretty little. To tease out interesting pattern in our data, it might not be enough to only look at one variable at a time. To display relationships between multiple variables, we mainly can:
- Use aestetics such as
color
, fill
, size
, shape
(alter the aestetics within one plot)
- Use
facet_wrap()
(produce multiple plots)
Lets look at some examples:
First, we could take a look at the number of daily rides with workweek / weekend days colored differently.
# Compute daily counts & plot
bike %>%
count(start_day, weekday) %>%
ggplot(aes(start_day, n, color = weekday)) +
geom_point()
Now let’s look at how rides are distributed according to the time of day. Let’s make a summary plot of weekly ride counts faceted by start hour of day and broken down by workweek/weekend. Here, we will use the facet_grid
# Compute week_hod & plot
bike %>%
count(start_wk, start_hod, weekday) %>%
ggplot(aes(start_wk, n, color = weekday)) +
geom_point() +
facet_grid(~ start_hod) +
scale_y_sqrt()
Expanding on the previous plot, let’s add one more variable into our summary, adding a facet dimension for whether or not the rider is a member of BIXI.
# Compute wk_memb_hod & plot
bike %>%
count(start_wk, start_hod, weekday, membership) %>%
ggplot(aes(start_wk, n, color = weekday)) +
geom_point() +
facet_grid(membership ~ start_hod) +
scale_y_sqrt()
Let’s now look at the number of rides vs. hour for each day. To start, we’ll create a summary dataset for the first full month in the dataset (May) and look at it.
# Compute daily_may &
bike %>%
filter(start_mon == 5) %>%
count(start_day, start_hod, membership) %>%
ggplot(aes(start_hod, n, color = membership)) +
geom_point() +
facet_wrap(~ start_day, ncol = 7)
LS0tCnRpdGxlOiAnQXBwbGljYXRpb246IERhdGEgVmlzdWFsaXphdGlvbiBpbiBSJwphdXRob3I6ICJEYW5pZWwgUy4gSGFpbiAoZHNoQGJ1c2luZXNzLmFhdS5kaykiCmRhdGU6ICJVcGRhdGVkIGByIGZvcm1hdChTeXMudGltZSgpLCAnJUIgJWQsICVZJylgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICB0aGVtZTogZmxhdGx5Ci0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgS25pdHIgb3B0aW9ucwojIyMgR2VuZXJpYyBwcmVhbWJsZQpTeXMuc2V0ZW52KExBTkcgPSAiZW4iKSAjIEZvciBlbmdsaXNoIGxhbmd1YWdlCm9wdGlvbnMoc2NpcGVuID0gNSkgIyBUbyBkZWFjdGl2YXRlIGFubm95aW5nIHNjaWVudGlmaWMgbnVtYmVyIG5vdGF0aW9uCgojIHJtKGxpc3Q9bHMoKSk7IGdyYXBoaWNzLm9mZigpICMgZ2V0IHJpZCBvZiBldmVyeXRoaW5nIGluIHRoZSB3b3Jrc3BhY2UKaWYgKCFyZXF1aXJlKCJrbml0ciIpKSBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpOyBsaWJyYXJ5KGtuaXRyKSAjIEZvciBkaXNwbGF5IG9mIHRoZSBtYXJrZG93bgoKIyMjIEtuaXRyIG9wdGlvbnMKa25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmc9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbj0iY2VudGVyIgogICAgICAgICAgICAgICAgICAgICApCmBgYAoKYGBge3J9CiMjIyBMb2FkIHBhY2thZ2VzCmxpYnJhcnkodGlkeXZlcnNlKSAjIENvbGxlY3Rpb24gb2YgYWxsIHRoZSBnb29kIHN0dWZmIGxpa2UgZHBseXIsIGdncGxvdDIgZWN0LgpsaWJyYXJ5KG1hZ3JpdHRyKSAjIEZvciBleHRyYS1waXBpbmcgb3BlcmF0b3JzIChlZy4gJTw+JSkKYGBgCgoKIyBJbnRyb2R1Y3Rpb24gCgpXZWxjb21lIHRvIHRoZSBhcHBsaWVkIHNlc3Npb24gaW4gZGF0YSB2aXN1YWxpemF0aW9uIGZvciBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIChFREEpIGluIGBSYC4KCiMgSW50cm9kdWN0aW9uIHRvIGBnZ2xvdDJgCiAKW2BnZ3Bsb3QyYF0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvKSBjYW4gYmUgdGhvdWdodCBvZiBhcyBhIG1pbmktbGFuZ3VhZ2UgKGRvbWFpbi1zcGVjaWZpYyBsYW5ndWFnZSkgd2l0aGluIHRoZSBgUmAgbGFuZ3VhZ2UuIEl0IGlzIGFuIFIgaW1wbGVtZW50YXRpb24gb2YgW1dpbGtpbnNvbidzIEdyYW1tYXIgb2YgR3JhcGhpY3MgYm9va10oaHR0cHM6Ly93d3cuc3ByaW5nZXIuY29tL2dwL2Jvb2svOTc4MDM4NzI0NTQ0NykuIFtBIExheWVyZWQgR3JhbW1hciBvZiBHcmFwaGljc10oaHR0cDovL3ZpdGEuaGFkLmNvLm56L3BhcGVycy9sYXllcmVkLWdyYW1tYXIucGRmKSBkZXNjcmliZXMgSGFkbGV5J3MgaW1wbGVtZW50YXRpb24gb2YgdGhlc2UgdGhvdWdodHMgaW4gdGhlIGdncGxvdDIncyBkZXNpZ24uIER1ZSB0byBpdHMgY29uY2VwdGlvbmFsIHJpY2huZXNzIGFzIHdlbGwgYXMgdGhlIHJpY2ggZnVuY3Rpb25hbGl0eSBwcm92aWRlZCwgYGdncGxvdDJgIGhhcyBvdmVyIHRpbWUgYmVjb21lIHRoZSBtYWluIHN1Yi1lY29zdXN0ZW0gZm9yIHJnYXBoaWMgdmlzdWFsaXphdGlvbi4gTW9zdCBwYWNrYWdlcyBkZWRpY2F0ZWQgdG8gc3BlY2lhbGl6ZWQgZm9ybXMgb2YgdmlzdWFsaXphdGlvbiAobmV0d29ya3MsIGludGVyYWN0aW9ucywgZXRjLikgd2lsbCB1c2UgdGhlIGBnZ3Bsb3RgIHBhY2thZ2UgYXMgdW5kZXJseWluZyBwbGF0dGZvcm0uIFNvLCBpdCBtYWtlcyBzZW5zZSB0byBkaXZlIGEgYml0IGRlZXBlciBpbnRvIGl0IGZ1bmN0aW9uYWxpdHkKCkNvbmNlcHR1YWxseSwgdGhlIG1haW4gaWRlYSBiZWhpbmQgdGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MgaXMgdGhhdCBhIHN0YXRpc3RpY2FsIGdyYXBoaWMgaXMgYSBtYXBwaW5nIGZyb20gdmFyaWFibGVzIHRvIGFlc3RoZXRpYyBhdHRyaWJ1dGVzICh4IGF4aXMgdmFsdWUsIHkgYXhpcyB2YWx1ZSwgY29sb3IsIHNoYXBlLCBzaXplKSBvZiBnZW9tZXRyaWMgb2JqZWN0cyAocG9pbnRzLCBsaW5lLCBiYXJzKS4gCgogV2hpbGUgdGhlIEdyYW1tYXIgb2YgR3JhcGhpYyBjb250YWlucyBtb3JlIGVsZW1lbnRzLCB3ZSB3aWxsIGZvY3VzIGluIHRoaXMgYnJpZWYgaW50cm8gaW4gdGhlIHR3byBtYWluIG9uZXMsIGFlc3RldGljcyBhbmQgZ2VvbWV0cmllcy4gCgoqICoqQWVzdGV0aWNzOioqIERldmluZSB0aGUgInN1cmZhY2UiIG9mIHlvdXIgcGxvdCwgaW4gdGVybXMgb2Ygd2hhdCBoYXMgdG8gYmUgbWFwcGVkIChzaXplLCBjb3Bsb3IpIG9uIHRoZSB4IGFuZCB5IChhbmQgcG90ZW50aWFsbHkgYWRpdHRpb25hbCkgYXhlcy4gQWVzdGV0aWN0cyBhcmUgZGVmaW5lZCB3aXRoaW4gdGhlIGBhZXMoKWAgZnVuY3Rpb24uCiogKipHZW9tZXRyaWVzOioqIFZpc3VhbCBlbGVtZW50cyB5b3UgY2FuIHNlZSBpbiB0aGUgcGxvdCBpdHNlbGYsIHN1Y2ggYXMgYmFycywgbGluZXMsIGFuZCBwb2ludHMuIFRoZXkgYXJlIGRlZmluZWQgd2l0aGluIHZhcmlvdXMgYGdlb21fWFlaKClgIGZ1bmN0aW9ucy4KCiFbXShodHRwczovL2dpdGh1Yi5jb20vU0RTLUFBVS9TRFMtbWFzdGVyL3Jhdy9tYXN0ZXIvMDBfbWVkaWEvZ2dwbG90X3N0cnVjdHVyZS5wbmcpCgpCYXNpY2FsbHksIHlvdSBkZWZpbmUgYSBzdXJmYWNlIGdyaWQgYW5kIHRoZW4gcGxvdCBzb21ldGhpbmcgb24gdG9wLiBXZSB3aWxsIHRhbGsgYWJvdXQgYWxsIG9mIHRoYXQgaW4gZGVwdGggaW4gbGF0ZXIgc2Vzc2lvbnMsIGZvciBub3cgdGhhdCdzIGFsbCB5b3UgbmVlZCB0byBrbm93IHRvIHVuZGVyc3RhbmQgdGhlIGZvbGxvd2luZyBzaW1wbGUgZXhhbXBsZXMuCgojIEFwcGxpY2F0aW9uOiB0aGUgYEJJWEkgQmlrZXNoYXJlIERhdGFgIGRhdGFzZXQKCkxldHMgdGFrZSBhIHN0ZXAgYmFjayBhbmQgem9vbSBhIGJpdCBpbnRvIGRpZmZlcmVudCBmb3JtcyBvZiB2aXN1YWxpemF0aW9uLiBXZSB3aWxsIG5vdyB0YWtlIGEgbG9vayBhdCB0aGUgYEJJWEkgQmlrZXNoYXJlIERhdGFgLCBjb3ZlcmluZyA1MDBrIGJpa2UtcmlkZXMgaW4gdGhlIEJJWEkgYmlrZS1zaGFyaW5nIHN5c3RlbSBpbiBNb250cmVhbC4KCmBgYHtyfQpiaWtlIDwtIHJlYWRSRFModXJsKCJodHRwczovL2dpdGh1Yi5jb20vU0RTLUFBVS9TRFMtbWFzdGVyL3Jhdy9tYXN0ZXIvMDBfZGF0YS9iaWtlc19tb250cmVhbC5yZHMiKSkKYGBgCgpMZXRzIHRha2UgYSBsb29rOgoKYGBge3J9CmJpa2UgJT4lIGdsaW1wc2UoKQpgYGAKCmBgYHtyfQpiaWtlICU+JSBoZWFkKCkKYGBgCgpXZSBzZWUgaGVyZSBhIG51bWJlciBvZiBkaWZmZXJlbnQgdmFyaWFibGUgdHlwZXMgcHJlc2VudCwgbmFtZWx5OgoKKiBDb250aW51b3VzIHZhcmlhYmxlcwoqIENhdGVnb3JpY2FsIHZhcmlhYmxlcwoqIFRlbXBvcmFsIHZhcmlhYmxlcwoKRmlyc3Qgb2YgYWxsOiBMZXRzIHJlbWVtYmVyLCB0aGUgZmlyc3QgdGhpbmcgd2UgZG8gaXMgZGVmaW5pbmcgdGhlIGFlc3RldGljcywgZmlyc3Qgb2YgYWxsIHRoZSBkaW1lbnNpb25zICh4LCB5KSBvZiB0aGUgdmlzdWFsaXphdGlvbi4KCmBgYHtyfQpiaWtlICU+JSBnZ3Bsb3QoYWVzKHggPSB3ZWVrZGF5LCB5ID0gc3RhcnRfaG9kKSkgCmBgYAoKVGhlIHJlc3VsdCB3aWxsIGJlIGFuIGVtcHR5IHBsYW5lIHdpdGggdGhlIGRpbWVuc2lvbnMgd2UgZGVmaW5lZC4gTm90ZSB0aGF0IHRoZXJlIGFyZSBtb3JlIGFlc3RldGljIGRpbWVuc2lvbnMgd2hpY2ggY2FuIGJlIHVzZWQgdG8gY29udmV5IGluZm9ybWF0aW9ucyB2aXN1YWx5LCBzdWNoIGFzIGZvciBpbnN0YW5jZToKCiogUG9zaXRpb24gKHgsIHkpCiogQ29sb3IKKiBTaGFwZQoqIEFscGhhIChUcmFuc3BhcmVuY3kpCgpXZSB3aWxsIGV4cGxvcmUgdGhlbSBsYXRlci4KCiMjIEJhc2ljIHZpc3VhbGl6YXRpb24gb2YgdmFyaWFibGUgdHlwZXMKCiMjIyBTdW1tYXJpZXMgb2YgT25lIFZhcmlhYmxlOiBDb250aW51b3VzCgpXaGVuIGF0dGVtcHRpbmcgdG8gc3VtbWFyaXplIGEgc2luZ2xlIHZhcmlhYmxlLCBoaXN0b2dyYW1zIGFuZCBkZW5zaXR5IGRpc3RyaWJ1dGlvbnMgYXJlIG9mdGVuIHRoZSB2aXN1YWxpemF0aW9uIG9mIGNob2ljZS4gV2UgY2FuIGRvIHRoYXQgZWFzaWx5IGJ5IHVzaW5nIHRoZSBgZ2VvbV9oaXN0b2dyYW0oKWAgbGF5ZXIuIE5vdGljZSB0aGF0IHdlIG9ubHkgZGVmaW5lIGEgYHhgIGFlc3RldGljLCBzaW5jZSB3ZSBvbmx5IHN1bW1hcml6ZSBvbmUgdmFyaWFibGUKCmBgYHtyfQpiaWtlICU+JSBnZ3Bsb3QoYWVzKHggPSBkdXJhdGlvbl9zZWMpKSArCiAgZ2VvbV9oaXN0b2dyYW0oKQpgYGAKClRvIHBsb3QgYSBwcm9iYWJpbGl0eSBkZW5zaXR5IGZ1bmN0aW9uIChQREYpIGluc3RlYWQsIHdlIGNhbiB1c2UgdGhlIGBnZW9tX2RlbnNpdHkoKWAgbGF5ZXIuCgpgYGB7cn0KYmlrZSAlPiUgZ2dwbG90KGFlcyh4ID0gZHVyYXRpb25fc2VjKSkgKwogIGdlb21fZGVuc2l0eSgpCmBgYAoKTm90ZSB0aGUgZGlzdHJpYnV0aW9uIGFwcGVhcnMgcmlnaHQtc2tld2VkLCBzaW5jZSB3ZSBoYXZlIHNvbWUgb3V0bGllcnMgb2YgdmVyeSBsb25nIGJpa2UgcmlkZXMuIEFkZGluZyBhIGxvZy1zY2FsZSBvbiB0aGUgeC1heGlzIG1pZ2h0IGhlbHAgdG8gcmVkdWNlIHRoZWlyIGltcGFjdCBvbiB0aGUgdmlzdWFsaXphdGlvbi4KCgpgYGB7cn0KYmlrZSAlPiUgZ2dwbG90KGFlcyh4ID0gZHVyYXRpb25fc2VjKSkgKwogIGdlb21faGlzdG9ncmFtKCkgKwogIHNjYWxlX3hfbG9nMTAoKSAKYGBgCgpJbiBjYXNlIHdlIHdvdWxkIGFscmVhZHkgbGlrZSB0byBzdGFydCBsb29raW5nIGF0IGNvbmRpdGlvbmFsIGRpc3RyaWJ1dGlvbnMsIHdlIGNvdWxkIGFkZCBhbiBhZGl0dGlvbmFsIGBmaWxsYCBhZXN0ZXRpYy4KCmBgYHtyfQpiaWtlICU+JSBnZ3Bsb3QoYWVzKHggPSBkdXJhdGlvbl9zZWMsIGZpbGwgPSB3ZWVrZGF5KSkgKwogIGdlb21faGlzdG9ncmFtKCkgKwogIHNjYWxlX3hfbG9nMTAoKSAKYGBgCgojIyMgU3VtbWFyaWVzIG9mIE9uZSBWYXJpYWJsZTogRGlzY3JldGUKClRvIGRvIHRoZSBzYW1lIGZvciBhIGRpc2NyZXRlIHZhcmlhYmxlLCB3ZSB3b3VsZCBzdGFydCB3aXRoIGEgc2ltcGxlIGJhcnBsb3QgdmlhIGBnZW9tX2JhcigpYC4gTm90aWNlIGFnYWluIHRoYXQgd2Ugb25seSBkZWZpbmUgYSB4IGFlc3RldGljLiBgZ2dwbG90YCBwZXIgZGVmYXVsdCB3aWxsIHVzZSB0aGUgY291bnQgb24gdGhlIHktYXhpcy4KCmBgYHtyfQpiaWtlICU+JSBnZ3Bsb3QoYWVzKHggPSBzdGFydF9kb3cpKSArCiAgZ2VvbV9iYXIoKQpgYGAKCldlIGNvdWxkIGFsc28gdXNlIHRoZSBtZW1iZXJzaGlwIGFzIGZpbGwgYWVzdGV0aWMgdG8gbWFwIGZ1cnRoZXIgaW5mb3JtYXRpb24gaW4gdGhlIHBsb3QuCgpgYGB7cn0KYmlrZSAlPiUgZ2dwbG90KGFlcyh4ID0gc3RhcnRfZG93LCBmaWxsID0gbWVtYmVyc2hpcCkpICsKICBnZW9tX2JhcigpCmBgYAoKIyMjIFN1bW1hcmllcyBvZiBPbmUgVmFyaWFibGU6IFRlbXBvcmFsCgpBIHRlbXBvcmFsIHZhcmlhYmxlIGNhbiBhbHNvIGJlIHZpc3VhbGl6ZWQgYXMgYSBsaW5lLXBsb3Qgd2l0aCBgZ2VvbV9saW5lKClgLgoKYGBge3J9CmJpa2UgJT4lCiAgY291bnQoc3RhcnRfd2spICU+JQogIGdncGxvdChhZXMoeCA9IHN0YXJ0X3drLCB5ID0gbikpICsKICBnZW9tX2xpbmUoKQpgYGAKClRvIGluc3RlYWQgKG9yIGluIGFkZGl0aW9uKSBhZGQgYSB0cmVuZGxpbmUsIHdlIGNhbiB1c2UgYGdlb21fc21vb3RoKClgCgpgYGB7cn0KYmlrZSAlPiUKICBjb3VudChzdGFydF93aykgJT4lCiAgZ2dwbG90KGFlcyh4ID0gc3RhcnRfd2ssIHkgPSBuKSkgKwogIGdlb21fc21vb3RoKCkKYGBgCgoKIyMgU3VtbWFyaXppbmcgbXVsdGlwbGUgdmFyaWFibGVhIGpvaW50bHkKCk9rLCB0aGF0IHdhcyBwcmV0dHkgZWFzeS4gSG93ZXZlciwgdGhlIGluc2lnaHRzIGdhaW5lZCBzbyBmYXIgYXJlIHByZXR0eSBsaXR0bGUuIFRvIHRlYXNlIG91dCBpbnRlcmVzdGluZyBwYXR0ZXJuIGluIG91ciBkYXRhLCBpdCBtaWdodCBub3QgYmUgZW5vdWdoIHRvIG9ubHkgbG9vayBhdCBvbmUgdmFyaWFibGUgYXQgYSB0aW1lLiBUbyBkaXNwbGF5IHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBtdWx0aXBsZSB2YXJpYWJsZXMsIHdlIG1haW5seSBjYW46CgoqIFVzZSBhZXN0ZXRpY3Mgc3VjaCBhcyBgY29sb3JgLCBgZmlsbGAsIGBzaXplYCwgYHNoYXBlYCAoYWx0ZXIgdGhlIGFlc3RldGljcyB3aXRoaW4gb25lIHBsb3QpCiogVXNlIGBmYWNldF93cmFwKClgKHByb2R1Y2UgIG11bHRpcGxlIHBsb3RzKQoKTGV0cyBsb29rIGF0IHNvbWUgZXhhbXBsZXM6CgpGaXJzdCwgd2UgY291bGQgdGFrZSBhIGxvb2sgYXQgdGhlIG51bWJlciBvZiBkYWlseSByaWRlcyB3aXRoICB3b3Jrd2VlayAvIHdlZWtlbmQgZGF5cyBjb2xvcmVkIGRpZmZlcmVudGx5LgoKYGBge3J9CiMgQ29tcHV0ZSBkYWlseSBjb3VudHMgJiBwbG90CmJpa2UgJT4lCiAgY291bnQoc3RhcnRfZGF5LCB3ZWVrZGF5KSAlPiUKICBnZ3Bsb3QoYWVzKHN0YXJ0X2RheSwgbiwgY29sb3IgPSB3ZWVrZGF5KSkgKwogIGdlb21fcG9pbnQoKQpgYGAKCk5vdyBsZXQncyBsb29rIGF0IGhvdyByaWRlcyBhcmUgZGlzdHJpYnV0ZWQgYWNjb3JkaW5nIHRvIHRoZSB0aW1lIG9mIGRheS4gTGV0J3MgbWFrZSBhIHN1bW1hcnkgcGxvdCBvZiB3ZWVrbHkgcmlkZSBjb3VudHMgZmFjZXRlZCBieSBzdGFydCBob3VyIG9mIGRheSBhbmQgYnJva2VuIGRvd24gYnkgd29ya3dlZWsvd2Vla2VuZC4gSGVyZSwgd2Ugd2lsbCB1c2UgdGhlIGBmYWNldF9ncmlkYAoKCmBgYHtyLGZpZy5oZWlnaHQ9NSAsIGZpZy53aWR0aD0xNX0KIyBDb21wdXRlIHdlZWtfaG9kICYgcGxvdApiaWtlICU+JQogIGNvdW50KHN0YXJ0X3drLCBzdGFydF9ob2QsIHdlZWtkYXkpICU+JQogIGdncGxvdChhZXMoc3RhcnRfd2ssIG4sIGNvbG9yID0gd2Vla2RheSkpICsKICBnZW9tX3BvaW50KCkgKwogIGZhY2V0X2dyaWQofiBzdGFydF9ob2QpICsKICBzY2FsZV95X3NxcnQoKQpgYGAKCkV4cGFuZGluZyBvbiB0aGUgcHJldmlvdXMgcGxvdCwgbGV0J3MgYWRkIG9uZSBtb3JlIHZhcmlhYmxlIGludG8gb3VyIHN1bW1hcnksIGFkZGluZyBhIGZhY2V0IGRpbWVuc2lvbiBmb3Igd2hldGhlciBvciBub3QgdGhlIHJpZGVyIGlzIGEgbWVtYmVyIG9mIEJJWEkuCgpgYGB7cixmaWcuaGVpZ2h0PTcuNSwgZmlnLndpZHRoPTE1fQojIENvbXB1dGUgd2tfbWVtYl9ob2QgJiBwbG90CmJpa2UgJT4lCiAgY291bnQoc3RhcnRfd2ssIHN0YXJ0X2hvZCwgd2Vla2RheSwgbWVtYmVyc2hpcCkgJT4lCiAgZ2dwbG90KGFlcyhzdGFydF93aywgbiwgY29sb3IgPSB3ZWVrZGF5KSkgKwogIGdlb21fcG9pbnQoKSArCiAgZmFjZXRfZ3JpZChtZW1iZXJzaGlwIH4gc3RhcnRfaG9kKSArCiAgc2NhbGVfeV9zcXJ0KCkKYGBgCgpMZXQncyBub3cgbG9vayBhdCB0aGUgbnVtYmVyIG9mIHJpZGVzIHZzLiBob3VyIGZvciBlYWNoIGRheS4gVG8gc3RhcnQsIHdlJ2xsIGNyZWF0ZSBhIHN1bW1hcnkgZGF0YXNldCBmb3IgdGhlIGZpcnN0IGZ1bGwgbW9udGggaW4gdGhlIGRhdGFzZXQgKE1heSkgYW5kIGxvb2sgYXQgaXQuCgpgYGB7cixmaWcuaGVpZ2h0PTcuNSwgZmlnLndpZHRoPTE1fQojIENvbXB1dGUgZGFpbHlfbWF5ICYgCmJpa2UgJT4lCiAgZmlsdGVyKHN0YXJ0X21vbiA9PSA1KSAlPiUKICBjb3VudChzdGFydF9kYXksIHN0YXJ0X2hvZCwgbWVtYmVyc2hpcCkgJT4lCiAgZ2dwbG90KGFlcyhzdGFydF9ob2QsIG4sIGNvbG9yID0gbWVtYmVyc2hpcCkpICsKICBnZW9tX3BvaW50KCkgKwogIGZhY2V0X3dyYXAofiBzdGFydF9kYXksIG5jb2wgPSA3KQpgYGAKCiMgRW5kbm90ZXMKCiMjIyBSZWZlcmVuY2VzCgojIyMgU3VnZ2VzdGlvbnMgZm9yIGZ1cnRoZXIgc3R1ZHkKCiMjIyMgT3duIGV4cGxvcmF0aW9uClRoZXJlIGlzIHNvIG11Y2ggbW9yZSB0byBleHBsb3JlLiBIb3dldmVyLCBzaW5jZSB0aW1lIGlzIGxpbWl0ZWQsIEkgd2lsbCBsZWF2ZSBpdCB1cCB0byB5b3UgdG8gZXhwbG9yZSBtb3JlLiAKCiogVGFrZSBhIG1vbWVudCB0byByZXZpZXcgdGhlIGRpZmZlcmVudCBnZW9tcyBvZmZlcmVkIGJ5IGBnZ3Bsb3RgIFtoZXJlXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvaW5kZXguaHRtbCkuIAoqIEZvciBpbnNwaXJhdGlvbiB3aGF0IGNhbiBiZSBkb25lLCBjaGVjayBbaGVyZV0oaHR0cDovL3Itc3RhdGlzdGljcy5jby9Ub3A1MC1HZ3Bsb3QyLVZpc3VhbGl6YXRpb25zLU1hc3Rlckxpc3QtUi1Db2RlLmh0bWwpLgoqIENoZWNrIGBnZ3Bsb3QyYCBhZGRvbnMgIFtoZXJlXShodHRwczovL2V4dHMuZ2dwbG90Mi50aWR5dmVyc2Uub3JnLykuIFNvbWUgb2YgbXkgZmF2b3JpdGUgYXJlOgogICAqIGBnZ2ZvcmNlYDogRm9yIGEgY29sbGVjdGlvbiBvZiBhZGl0dGlvbmFsIGZlYXR1cmVzCiAgICogYHBhdGNod29ya2A6IEZvciBlYXN5IGluZWdyYXRpb24gb2YgbXVsdGlwbGUgcGxvdHMgam9pbnRseQogICAqIGBHR2FsbHlgOiBDb2xsZWN0aW9uIGZvIG1hbnkgY29vbCBwbG90dGluZyBmZWF0dXJlcywgaW5jbHVkaW5nIG1hbnkgc3RhbmRhcmQgc3RhdHMgcGxvdCBmb3IgY29ycmVsYXRpb24sIGRpc3RyaWJ1dGlvbiBldGMuCiAgICogYGdnbWFwYDogRm9yIGdlb3Bsb3R0aW5nCiAgICogYGdncmFwaGA6IEZvciBuZXR3b3JrIHBsb3RzICh3aWxsIGJlIGhhbmRsZWQgbGF0ZXIpCiAgICogYGdncmlkZ2VzYDogUmlkZ2UgZmVhdHVyZXMsIGZvciBleGFtcGxlIHRvIGNyZWF0ZSBqb3ktcGxvdHMKICAgKiBgZ2dhbGx1dmlhbGA6IEZvciBhbGx1dmlhbCBwbG90cwogICAKIyMjIyBEYXRhY2FtcAoKKiBbSW50cm9kdWN0aW9uIHRvIERhdGEgVmlzdWFsaXphdGlvbiB3aXRoIGdncGxvdDJdKGh0dHBzOi8vbGVhcm4uZGF0YWNhbXAuY29tL2NvdXJzZXMvaW50cm9kdWN0aW9uLXRvLWRhdGEtdmlzdWFsaXphdGlvbi13aXRoLWdncGxvdDIpOiBSZWFsbHkgZ29vZCBhbmQgdGhyb3Jyb3VnaCBnZ3Bsb3QyIGludHJvZHVjdGlvbi4gUmljayBoYXMgYWxzbyBtb3JlIGFkdmFuY2VkIGdncGxvdCBjb3Vyc2VzIGZvciB0aGUgb25lcyB3aG8gd2FudCB0byBnbyBkZWVwZXIuCiogW2h0dHBzOi8vbGVhcm4uZGF0YWNhbXAuY29tL2NvdXJzZXMvY29tbXVuaWNhdGluZy13aXRoLWRhdGEtaW4tdGhlLXRpZHl2ZXJzZV0oQ29tbXVuaWNhdGluZyB3aXRoIERhdGEgaW4gdGhlIFRpZHl2ZXJzZSk6IEFsc28gaW5jbHVkZXMgbW9yZSB3b3JrZmxvd3MgZm9yIHVzaW5nIGRhdGF2aXogZm9yIHJlcG9ydGluZyAmIGNvbW11bmljYXRpbmcgdG8gZGlmZmVyZW50IGF1ZGllbmNlcywgZWcuIHVzaW5nIFJtYXJrZG93bi4KKiBbSW50ZXJhY3RpdmUgRGF0YSBWaXN1YWxpemF0aW9uIHdpdGggcGxvdGx5IGluIFJdKGh0dHBzOi8vbGVhcm4uZGF0YWNhbXAuY29tL2NvdXJzZXMvaW50ZXJhY3RpdmUtZGF0YS12aXN1YWxpemF0aW9uLXdpdGgtcGxvdGx5LWluLXIpOiBGb3IgdGhlIG9uZXMgd2hvIHdhbnQgdG8gZ28gZGVlcGVyIGludG8gaW50ZXJhY3RpdmUgcGxvdHRpbmcuCgojIyMjIE90aGVyIG9ubGluZSBjb3Vyc2VzCgoqIFtEYXRhIFZpc3VhbGl6YXRpb25dKGh0dHBzOi8vZGF0YXZpem0yMC5jbGFzc2VzLmFuZHJld2hlaXNzLmNvbS8pOiBBbWF6aW5nIGZyZWUgb25saW5lIG1hdGVyaWFsLCBpbnRyb2R1Y2luZyB5b3UgdG8gbWFueSBkZXNpZ24gY29uY2VwdHMgYW5kIHJlZmxlY3Rpb25zIG9uIGRhdGF2aXosIGNvbWJpbmVkIHdpdGggaGFuZHMtb24gZXhhbXBsZXMuCiogW0RhdGFDYXJwZW50cnk6IERhdGEgVmlzdWFsaXphdGlvbiB3aXRoIGdncGxvdDJdKGh0dHBzOi8vZGF0YWNhcnBlbnRyeS5vcmcvUi1lY29sb2d5LWxlc3Nvbi8wNC12aXN1YWxpemF0aW9uLWdncGxvdDIuaHRtbCNQbG90dGluZ193aXRoX2dncGxvdDIpOiBHZW5lcmFsbHkgZ29vZCBjb3Vyc2UuIFlvdSBtaWdodCBoZXJlIGVuam95IHRoZSBhdHRlbnRpb24gcGFpZCB0byBzdGF0aXN0aWNhbCBwbG90cy4KCiMjIyMgUGFwZXJzLCBFYm9va3MgJiBjaGFwdGVycwoqIFIgZm9yIERhdGEgU2NpZW5jZSAoR3JvbGVtdW5kICYgV2lja2hhbSkKICAgKiBbQ2hhcHRlciAzXShodHRwczovL3I0ZHMuaGFkLmNvLm56L2RhdGEtdmlzdWFsaXNhdGlvbi5odG1sKTogQmFzaWNzIG9mIERhdGFWaXoKICAgKiBbQ2hhcHRlciA3XShodHRwczovL3I0ZHMuaGFkLmNvLm56L2V4cGxvcmF0b3J5LWRhdGEtYW5hbHlzaXMuaHRtbCk6IERhdGFWaXogZm9yIEVEQQogICAqIFtDaGFwdGVyIDI4XShodHRwczovL3I0ZHMuaGFkLmNvLm56L2dyYXBoaWNzLWZvci1jb21tdW5pY2F0aW9uLmh0bWwpOiBGaW5ldHVuaW5nIERhdGFWaXoKKiBbV2lja2hhbSwgSGFkbGV5LiAiQSBsYXllcmVkIGdyYW1tYXIgb2YgZ3JhcGhpY3MuIiBKb3VybmFsIG9mIENvbXB1dGF0aW9uYWwgYW5kIEdyYXBoaWNhbCBTdGF0aXN0aWNzIDE5LjEgKDIwMTApOiAzLTI4Ll0oaHR0cDovL3ZpdGEuaGFkLmNvLm56L3BhcGVycy9sYXllcmVkLWdyYW1tYXIucGRmKTogR2VuZXJhbCBjb25jZXB0IG9mIHRoZSBncmFtbWFyIG9mIGdyYXBoaWNzCiogSGVhbHksIEtpZXJhbi4gRGF0YSB2aXN1YWxpemF0aW9uOiBhIHByYWN0aWNhbCBpbnRyb2R1Y3Rpb24uIFByaW5jZXRvbiBVbml2ZXJzaXR5IFByZXNzLCAyMDE4LiBbb25saW5lIGF2YWlsYWJsZSBoZXJlXShodHRwczovL3NvY3Zpei5jby9pbmRleC5odG1sI3ByZWZhY2UpOiBHb29kIGlucnRvIHdpdGggbWFueSBiZXN0LXByYWN0aWNlIGFkdmljZXMuCgoKIyMjIFNlc3Npb24gSW5mbwpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGBgCg==