Author : Muhamad Ilyas
Step 1: Install & Load the packages.
install.packages("tidyverse")
install.packages("skimr")
install.packages("ggplot")
install.packages("readr")
install.packages("dplyr")
install.packages("janitor")
library(tidyverse)
library(skimr)
library(lubridate)
library(ggplot2)
library(readr)
library(dplyr)
library(janitor)
Step 2: Prepare the data and combine them in one data frame.
Step 3: Examine the data frame.
head(all_trips_2022)
dim(all_trips_2022)
colnames(all_trips_2022)
summary(all_trips_2022)
Step 4: Check for NA values.
sum(is.na(all_trips_2022))
Step 5 : Identify unnecessary data and remove columns we don’t need :
start_lat, start_lng, end_lat, end_lng
all_trips_2022 <- all_trips_2022 %>% select(-c(start_lat, start_lng, end_lat, end_lng))
colnames(all_trips_2022)
Step 6 : Add columns that list the date, month, day and year of each
ride
# This will allow us to aggregate ride data for each month and day. Before completing these operations we could only aggregate at the ride level
all_trips_2022$date <- as.Date(all_trips_2022$started_at) # Default format is yyyy-mm-dd
all_trips_2022$month <- format(as.Date(all_trips_2022$date), "%m")
all_trips_2022$day <- format(as.Date(all_trips_2022$date), "%d")
all_trips_2022$year <- format(as.Date(all_trips_2022$date), "%Y")
all_trips_2022$day_of_week <- format(as.Date(all_trips_2022$date), "%A")
Step 7 : Add column “ride_length”, which is the length of each ride
from ended_at minus started_at, and format as HH:MM:SS.
#Add column ride_length
all_trips_2022 <- all_trips_2022 %>% mutate(ride_length = ended_at - started_at)
#Convert from difftime to numeric values
all_trips_2022$ride_length <- as.numeric(as.character(all_trips_2022$ride_length))
is.numeric(all_trips_2022$ride_length)
#Convert seconds to minutes
all_trips_2022$ride_length <- as.numeric(all_trips_2022$ride_length/60)
Step 8 : Removing the bad data and do analysis on the ride
length.
The dataframe includes a few hundred entries when bikes were taken
out of docks and checked for quality by Divvy or ride_length was
negative
#we will create a new version of the dataframe (v2) since data is being removed
all_trips_2022_v2 <- all_trips_2022[!(all_trips_2022$start_station_name == "HQ QR" | all_trips_2022$ride_length<0),]
#check if there is any null values in data
sum(is.na(all_trips_2022_v2))
Remove NA values for clean data
#erase the null values
all_trips_2022_v2 <- all_trips_2022_v2 %>%
drop_na()
Analysis on ride length
#check for data with ride length more than 1 day (86400 seconds or 1440 mins). There is 3484 rides that the rider use the bike for more than 1 day
sum(all_trips_2022_v2$ride_length > 1440)
#Using summary to check min, max, median and mean
summary(all_trips_2022_v2$ride_length)
Step 9 : Aggregate to analyze the data based on user type: member vs
casual
#Compare members and casual users
aggregate(all_trips_2022_v2$ride_length ~ all_trips_2022_v2$member_casual, FUN=mean)
aggregate(all_trips_2022_v2$ride_length ~ all_trips_2022_v2$member_casual, FUN=median)
aggregate(all_trips_2022_v2$ride_length ~ all_trips_2022_v2$member_casual, FUN=max)
aggregate(all_trips_2022_v2$ride_length ~ all_trips_2022_v2$member_casual, FUN=min)
#See the average ride time by each day for members vs casual users
aggregate(all_trips_2022_v2$ride_length ~ all_trips_2022_v2$member_casual +
all_trips_2022_v2$day_of_week, FUN=mean)
Notice that the days of the week are out of order.
# Let's fix order
all_trips_2022_v2$day_of_week <- ordered(all_trips_2022_v2$day_of_week,
levels=c("Sunday", "Monday",
"Tuesday", "Wednesday",
"Thursday", "Friday",
"Saturday"))
#Let's check again the average ride by time by each day for members vs casual users
aggregate(all_trips_2022_v2$ride_length ~ all_trips_2022_v2$member_casual + all_trips_2022_v2$day_of_week, FUN = mean)
Step 10 : Further analysis into the stations
head(count(all_trips_2022_v2, start_station_name, member_casual, rideable_type, sort= TRUE))
head(count(all_trips_2022_v2, end_station_name, member_casual, rideable_type, sort= TRUE))
Step 11: Analyze ridership data by user types and day of the week
all_trips_2022_v2 %>%
mutate(weekday = wday(started_at, label = TRUE)) %>% #creates weekday field using wday()
group_by(member_casual, weekday) %>% #groups by usertype and weekday
summarise(number_of_rides = n() #calculates the number of rides and average duration
,average_duration = mean(ride_length)) %>% # calculates the average duration
arrange(member_casual, weekday) # sorts by member_casual and weekday
Step 12 : Visualize the number of rides by rider types and average
duration
par(mfrow=c(2,2))
# Number of rides by rider types
all_trips_2022_v2 %>%
mutate(weekday = wday(started_at, label=TRUE)) %>%
group_by(member_casual, weekday) %>%
summarise(number_of_rides = n(), average_duration=mean(ride_length)) %>%
arrange(member_casual, weekday) %>%
ggplot(aes(x=weekday, y=number_of_rides, fill=member_casual))+
geom_col(position="dodge")
# Number of rides by average duration
all_trips_2022_v2 %>%
mutate(weekday = wday(started_at, label = TRUE)) %>%
group_by(member_casual, weekday) %>%
summarise(number_of_rides = n(),average_duration = mean(ride_length)) %>%
arrange(member_casual, weekday) %>%
ggplot(aes(x = weekday, y = average_duration, fill = member_casual)) +
geom_col(position = "dodge")
Step 13: Visualize Member vs Casual on short ride (less and one
hour)
one_hour_ride <- all_trips_2022_v2 %>% filter(ride_length < 60)
one_hour_ride$ride_length_by_mins <- cut(one_hour_ride$ride_length,breaks = 20)
ggplot(data = one_hour_ride) +
geom_bar(mapping = aes(x = ride_length_by_mins, fill = member_casual)) +
labs(title = "One hour ride length") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
scale_x_discrete(labels=c("3", "6", "9", "12", "15", "18", "21", "24", "27", "30", "33", "36", "39", "42", "45", "48", "51", "54", "57", "60"))
Step 14 : Visualize day of the week riding choices between member vs
causal
ggplot(data = all_trips_2022_v2) +
geom_bar(mapping = aes(x = factor(day_of_week), fill = rideable_type))+
facet_wrap(~member_casual) +
labs(title='riding choice during day of the week', x= 'day of the week' )+
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
Step 15 : Check for peak time for bike usage between member vs
casual
hour_data <- all_trips_2022_v2
hour_data$start_hour <- as.numeric(format(strptime(all_trips_2022_v2$started_at,"%Y-%m-%d %H:%M:%OS"),'%H'))
ggplot(data = hour_data) +
geom_bar(mapping = aes(x = start_hour, fill = member_casual), stat = 'count') +
facet_wrap(~factor(day_of_week)) +
labs(title = "bike usage by starting hour", x = "starting hour") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
Step 16 : Save as csv for further analysis and visualization in
Tableau
#the data frame
write_csv(all_trips_2022_v2, "all_trips.csv")
write_csv(hour_data, "hour_data_ride.csv")
write_csv(one_hour_ride, "one_hour_ride.csv")
#total and average weekly rides by rider type
summary_ride_weekly <- all_trips_2022_v2 %>%
mutate(weekday = wday(started_at, label = TRUE)) %>%
group_by(member_casual, weekday) %>%
summarise(number_of_rides = n()
,average_duration = mean(ride_length)) %>%
arrange(member_casual, weekday)
write_csv(summary_ride_weekly, "summary_ride_weekly.csv")
#total and average weekly rides by rider type
summary_ride_weekly_type <- all_trips_2022_v2 %>%
mutate(weekday = wday(started_at, label = TRUE)) %>%
group_by(member_casual, weekday, rideable_type) %>%
summarise(number_of_rides = n()
,average_duration = mean(ride_length)) %>%
arrange(member_casual, weekday)
write_csv(summary_ride_weekly_type, "summary_ride_weekly_type.csv")
#total and avg monthly rides by rider type
summary_month <- all_trips_2022_v2 %>%
mutate(month = month(started_at, label = TRUE)) %>%
group_by(month,member_casual) %>%
summarise(number_of_rides = n()
,average_duration = mean(ride_length)) %>%
arrange(month, member_casual)
write_csv(summary_month, "summary_ride_monthly.csv")
#most popular stations
popular_stations <- all_trips_2022_v2 %>%
mutate(station = start_station_name) %>%
drop_na(start_station_name) %>%
group_by(start_station_name, member_casual) %>%
summarise(number_of_rides=n())
write_csv(popular_stations, "popular_stations.csv")
#total membership types and rideable types
total_riders <- data.frame(table(all_trips_2022_v2$member_casual))
total_types <- data.frame(table(all_trips_2022_v2$rideable_type))
write_csv(total_riders, "total_riders.csv")
write_csv(total_types, "total_types.csv")
LS0tDQp0aXRsZTogIkdvb2dsZSBEYXRhIEFuYWx5dGljcyA6IEN5Y2xpc3RpYyBCaWtlIFNoYXJlIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KQXV0aG9yIDogTXVoYW1hZCBJbHlhcw0KDQpTdGVwIDE6IEluc3RhbGwgJiBMb2FkIHRoZSBwYWNrYWdlcy4NCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikNCmluc3RhbGwucGFja2FnZXMoInNraW1yIikNCmluc3RhbGwucGFja2FnZXMoImdncGxvdCIpDQppbnN0YWxsLnBhY2thZ2VzKCJyZWFkciIpDQppbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpDQppbnN0YWxsLnBhY2thZ2VzKCJqYW5pdG9yIikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShza2ltcikNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGphbml0b3IpDQpgYGANCg0KU3RlcCAyOiBQcmVwYXJlIHRoZSBkYXRhIGFuZCBjb21iaW5lIHRoZW0gaW4gb25lIGRhdGEgZnJhbWUuDQpgYGB7ciBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0NCiNDb21iaW5lIHRoZSBkYXRhIGZyb20gSmFuIDIwMjIgdG8gRGVjIDIwMjIgaW50byBvbmUgZGF0YSBmcmFtZSAiYWxsX3RyaXBzXzIwMjIuDQoNCmFsbF90cmlwc18yMDIyIDwtIHJiaW5kKA0KIHJlYWRfY3N2KCJDOi9Vc2Vycy9TaGFxdWlsbGUvRG9jdW1lbnRzL0N5Y2xpc3RpYy9CaWtlX2RhdGEvMjAyMjAxLWRpdnZ5LXRyaXBkYXRhLmNzdiIpLA0KIHJlYWRfY3N2KCJDOi9Vc2Vycy9TaGFxdWlsbGUvRG9jdW1lbnRzL0N5Y2xpc3RpYy9CaWtlX2RhdGEvMjAyMjAyLWRpdnZ5LXRyaXBkYXRhLmNzdiIpLA0KIHJlYWRfY3N2KCJDOi9Vc2Vycy9TaGFxdWlsbGUvRG9jdW1lbnRzL0N5Y2xpc3RpYy9CaWtlX2RhdGEvMjAyMjAzLWRpdnZ5LXRyaXBkYXRhLmNzdiIpLA0KIHJlYWRfY3N2KCJDOi9Vc2Vycy9TaGFxdWlsbGUvRG9jdW1lbnRzL0N5Y2xpc3RpYy9CaWtlX2RhdGEvMjAyMjA0LWRpdnZ5LXRyaXBkYXRhLmNzdiIpLA0KIHJlYWRfY3N2KCJDOi9Vc2Vycy9TaGFxdWlsbGUvRG9jdW1lbnRzL0N5Y2xpc3RpYy9CaWtlX2RhdGEvMjAyMjA1LWRpdnZ5LXRyaXBkYXRhLmNzdiIpLA0KIHJlYWRfY3N2KCJDOi9Vc2Vycy9TaGFxdWlsbGUvRG9jdW1lbnRzL0N5Y2xpc3RpYy9CaWtlX2RhdGEvMjAyMjA2LWRpdnZ5LXRyaXBkYXRhLmNzdiIpLA0KIHJlYWRfY3N2KCJDOi9Vc2Vycy9TaGFxdWlsbGUvRG9jdW1lbnRzL0N5Y2xpc3RpYy9CaWtlX2RhdGEvMjAyMjA3LWRpdnZ5LXRyaXBkYXRhLmNzdiIpLA0KIHJlYWRfY3N2KCJDOi9Vc2Vycy9TaGFxdWlsbGUvRG9jdW1lbnRzL0N5Y2xpc3RpYy9CaWtlX2RhdGEvMjAyMjA4LWRpdnZ5LXRyaXBkYXRhLmNzdiIpLA0KIHJlYWRfY3N2KCJDOi9Vc2Vycy9TaGFxdWlsbGUvRG9jdW1lbnRzL0N5Y2xpc3RpYy9CaWtlX2RhdGEvMjAyMjA5LWRpdnZ5LXRyaXBkYXRhLmNzdiIpLA0KIHJlYWRfY3N2KCJDOi9Vc2Vycy9TaGFxdWlsbGUvRG9jdW1lbnRzL0N5Y2xpc3RpYy9CaWtlX2RhdGEvMjAyMjEwLWRpdnZ5LXRyaXBkYXRhLmNzdiIpLA0KIHJlYWRfY3N2KCJDOi9Vc2Vycy9TaGFxdWlsbGUvRG9jdW1lbnRzL0N5Y2xpc3RpYy9CaWtlX2RhdGEvMjAyMjExLWRpdnZ5LXRyaXBkYXRhLmNzdiIpLA0KIHJlYWRfY3N2KCJDOi9Vc2Vycy9TaGFxdWlsbGUvRG9jdW1lbnRzL0N5Y2xpc3RpYy9CaWtlX2RhdGEvMjAyMjEyLWRpdnZ5LXRyaXBkYXRhLmNzdiIpKQ0KYGBgDQoNClN0ZXAgMzogRXhhbWluZSB0aGUgZGF0YSBmcmFtZS4NCmBgYHtyIGVjaG89VFJVRX0NCmhlYWQoYWxsX3RyaXBzXzIwMjIpDQpkaW0oYWxsX3RyaXBzXzIwMjIpDQpjb2xuYW1lcyhhbGxfdHJpcHNfMjAyMikNCnN1bW1hcnkoYWxsX3RyaXBzXzIwMjIpDQpgYGANCg0KU3RlcCA0OiBDaGVjayBmb3IgTkEgdmFsdWVzLg0KYGBge3J9DQpzdW0oaXMubmEoYWxsX3RyaXBzXzIwMjIpKSANCmBgYA0KDQpTdGVwIDUgOiBJZGVudGlmeSB1bm5lY2Vzc2FyeSBkYXRhIGFuZCByZW1vdmUgY29sdW1ucyB3ZSBkb24ndCBuZWVkIDogc3RhcnRfbGF0LCBzdGFydF9sbmcsIGVuZF9sYXQsIGVuZF9sbmcNCmBgYHtyfQ0KYWxsX3RyaXBzXzIwMjIgPC0gYWxsX3RyaXBzXzIwMjIgJT4lIHNlbGVjdCgtYyhzdGFydF9sYXQsIHN0YXJ0X2xuZywgZW5kX2xhdCwgZW5kX2xuZykpDQpjb2xuYW1lcyhhbGxfdHJpcHNfMjAyMikNCmBgYA0KDQoNClN0ZXAgNiA6IEFkZCBjb2x1bW5zIHRoYXQgbGlzdCB0aGUgZGF0ZSwgbW9udGgsIGRheSBhbmQgeWVhciBvZiBlYWNoIHJpZGUNCmBgYHtyfQ0KIyBUaGlzIHdpbGwgYWxsb3cgdXMgdG8gYWdncmVnYXRlIHJpZGUgZGF0YSBmb3IgZWFjaCBtb250aCBhbmQgZGF5LiBCZWZvcmUgY29tcGxldGluZyB0aGVzZSBvcGVyYXRpb25zIHdlIGNvdWxkIG9ubHkgYWdncmVnYXRlIGF0IHRoZSByaWRlIGxldmVsDQphbGxfdHJpcHNfMjAyMiRkYXRlIDwtIGFzLkRhdGUoYWxsX3RyaXBzXzIwMjIkc3RhcnRlZF9hdCkgIyBEZWZhdWx0IGZvcm1hdCBpcyB5eXl5LW1tLWRkDQphbGxfdHJpcHNfMjAyMiRtb250aCA8LSBmb3JtYXQoYXMuRGF0ZShhbGxfdHJpcHNfMjAyMiRkYXRlKSwgIiVtIikNCmFsbF90cmlwc18yMDIyJGRheSA8LSBmb3JtYXQoYXMuRGF0ZShhbGxfdHJpcHNfMjAyMiRkYXRlKSwgIiVkIikNCmFsbF90cmlwc18yMDIyJHllYXIgPC0gZm9ybWF0KGFzLkRhdGUoYWxsX3RyaXBzXzIwMjIkZGF0ZSksICIlWSIpDQphbGxfdHJpcHNfMjAyMiRkYXlfb2Zfd2VlayA8LSBmb3JtYXQoYXMuRGF0ZShhbGxfdHJpcHNfMjAyMiRkYXRlKSwgIiVBIikNCmBgYA0KDQpTdGVwIDcgOiBBZGQgY29sdW1uICJyaWRlX2xlbmd0aCIsIHdoaWNoIGlzIHRoZSBsZW5ndGggb2YgZWFjaCByaWRlIGZyb20gZW5kZWRfYXQgbWludXMgc3RhcnRlZF9hdCwgYW5kIGZvcm1hdCBhcyBISDpNTTpTUy4NCmBgYHtyfQ0KI0FkZCBjb2x1bW4gcmlkZV9sZW5ndGgNCmFsbF90cmlwc18yMDIyIDwtIGFsbF90cmlwc18yMDIyICU+JSBtdXRhdGUocmlkZV9sZW5ndGggPSBlbmRlZF9hdCAtIHN0YXJ0ZWRfYXQpDQoNCiNDb252ZXJ0IGZyb20gZGlmZnRpbWUgdG8gbnVtZXJpYyB2YWx1ZXMNCmFsbF90cmlwc18yMDIyJHJpZGVfbGVuZ3RoIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGFsbF90cmlwc18yMDIyJHJpZGVfbGVuZ3RoKSkNCmlzLm51bWVyaWMoYWxsX3RyaXBzXzIwMjIkcmlkZV9sZW5ndGgpDQoNCiNDb252ZXJ0IHNlY29uZHMgdG8gbWludXRlcw0KYWxsX3RyaXBzXzIwMjIkcmlkZV9sZW5ndGggPC0gYXMubnVtZXJpYyhhbGxfdHJpcHNfMjAyMiRyaWRlX2xlbmd0aC82MCkNCmBgYA0KDQpTdGVwIDggOiBSZW1vdmluZyB0aGUgYmFkIGRhdGEgYW5kIGRvIGFuYWx5c2lzIG9uIHRoZSByaWRlIGxlbmd0aC4NCg0KVGhlIGRhdGFmcmFtZSBpbmNsdWRlcyBhIGZldyBodW5kcmVkIGVudHJpZXMgd2hlbiBiaWtlcyB3ZXJlIHRha2VuIG91dCBvZiBkb2NrcyBhbmQgY2hlY2tlZCBmb3IgcXVhbGl0eSBieSBEaXZ2eSBvciByaWRlX2xlbmd0aCB3YXMgbmVnYXRpdmUNCmBgYHtyfQ0KI3dlIHdpbGwgY3JlYXRlIGEgbmV3IHZlcnNpb24gb2YgdGhlIGRhdGFmcmFtZSAodjIpIHNpbmNlIGRhdGEgaXMgYmVpbmcgcmVtb3ZlZCANCmFsbF90cmlwc18yMDIyX3YyIDwtIGFsbF90cmlwc18yMDIyWyEoYWxsX3RyaXBzXzIwMjIkc3RhcnRfc3RhdGlvbl9uYW1lID09ICJIUSBRUiIgfCBhbGxfdHJpcHNfMjAyMiRyaWRlX2xlbmd0aDwwKSxdDQpgYGANCg0KYGBge3J9DQojY2hlY2sgaWYgdGhlcmUgaXMgYW55IG51bGwgdmFsdWVzIGluIGRhdGENCnN1bShpcy5uYShhbGxfdHJpcHNfMjAyMl92MikpDQpgYGANCg0KUmVtb3ZlIE5BIHZhbHVlcyBmb3IgY2xlYW4gZGF0YQ0KYGBge3J9DQojZXJhc2UgdGhlIG51bGwgdmFsdWVzIA0KYWxsX3RyaXBzXzIwMjJfdjIgPC0gYWxsX3RyaXBzXzIwMjJfdjIgJT4lIA0KICBkcm9wX25hKCkNCmBgYA0KDQpBbmFseXNpcyBvbiByaWRlIGxlbmd0aA0KYGBge3J9DQojY2hlY2sgZm9yIGRhdGEgd2l0aCByaWRlIGxlbmd0aCAgbW9yZSB0aGFuIDEgZGF5ICg4NjQwMCBzZWNvbmRzIG9yIDE0NDAgbWlucykuIFRoZXJlIGlzIDM0ODQgcmlkZXMgdGhhdCB0aGUgcmlkZXIgdXNlIHRoZSBiaWtlIGZvciBtb3JlIHRoYW4gMSBkYXkNCnN1bShhbGxfdHJpcHNfMjAyMl92MiRyaWRlX2xlbmd0aCA+IDE0NDApDQoNCiNVc2luZyBzdW1tYXJ5IHRvIGNoZWNrIG1pbiwgbWF4LCBtZWRpYW4gYW5kIG1lYW4NCnN1bW1hcnkoYWxsX3RyaXBzXzIwMjJfdjIkcmlkZV9sZW5ndGgpDQpgYGANCg0KU3RlcCA5IDogQWdncmVnYXRlIHRvIGFuYWx5emUgdGhlIGRhdGEgYmFzZWQgb24gdXNlciB0eXBlOiBtZW1iZXIgdnMgY2FzdWFsDQpgYGB7cn0NCiNDb21wYXJlIG1lbWJlcnMgYW5kIGNhc3VhbCB1c2Vycw0KYWdncmVnYXRlKGFsbF90cmlwc18yMDIyX3YyJHJpZGVfbGVuZ3RoIH4gYWxsX3RyaXBzXzIwMjJfdjIkbWVtYmVyX2Nhc3VhbCwgRlVOPW1lYW4pDQphZ2dyZWdhdGUoYWxsX3RyaXBzXzIwMjJfdjIkcmlkZV9sZW5ndGggfiBhbGxfdHJpcHNfMjAyMl92MiRtZW1iZXJfY2FzdWFsLCBGVU49bWVkaWFuKQ0KYWdncmVnYXRlKGFsbF90cmlwc18yMDIyX3YyJHJpZGVfbGVuZ3RoIH4gYWxsX3RyaXBzXzIwMjJfdjIkbWVtYmVyX2Nhc3VhbCwgRlVOPW1heCkNCmFnZ3JlZ2F0ZShhbGxfdHJpcHNfMjAyMl92MiRyaWRlX2xlbmd0aCB+IGFsbF90cmlwc18yMDIyX3YyJG1lbWJlcl9jYXN1YWwsIEZVTj1taW4pDQoNCiNTZWUgdGhlIGF2ZXJhZ2UgcmlkZSB0aW1lIGJ5IGVhY2ggZGF5IGZvciBtZW1iZXJzIHZzIGNhc3VhbCB1c2Vycw0KYWdncmVnYXRlKGFsbF90cmlwc18yMDIyX3YyJHJpZGVfbGVuZ3RoIH4gYWxsX3RyaXBzXzIwMjJfdjIkbWVtYmVyX2Nhc3VhbCArIA0KICAgICAgICAgICAgYWxsX3RyaXBzXzIwMjJfdjIkZGF5X29mX3dlZWssIEZVTj1tZWFuKQ0KYGBgDQoNCk5vdGljZSB0aGF0IHRoZSBkYXlzIG9mIHRoZSB3ZWVrIGFyZSBvdXQgb2Ygb3JkZXIuDQoNCmBgYHtyfQ0KIyBMZXQncyBmaXggb3JkZXIgDQphbGxfdHJpcHNfMjAyMl92MiRkYXlfb2Zfd2VlayA8LSBvcmRlcmVkKGFsbF90cmlwc18yMDIyX3YyJGRheV9vZl93ZWVrLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPWMoIlN1bmRheSIsICJNb25kYXkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHVlc2RheSIsICJXZWRuZXNkYXkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVGh1cnNkYXkiLCAiRnJpZGF5IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTYXR1cmRheSIpKQ0KYGBgDQoNCmBgYHtyfQ0KI0xldCdzIGNoZWNrIGFnYWluIHRoZSBhdmVyYWdlIHJpZGUgYnkgdGltZSBieSBlYWNoIGRheSBmb3IgbWVtYmVycyB2cyBjYXN1YWwgdXNlcnMNCmFnZ3JlZ2F0ZShhbGxfdHJpcHNfMjAyMl92MiRyaWRlX2xlbmd0aCB+IGFsbF90cmlwc18yMDIyX3YyJG1lbWJlcl9jYXN1YWwgKyBhbGxfdHJpcHNfMjAyMl92MiRkYXlfb2Zfd2VlaywgRlVOID0gbWVhbikNCmBgYA0KDQpTdGVwIDEwIDogRnVydGhlciBhbmFseXNpcyBpbnRvIHRoZSBzdGF0aW9ucw0KYGBge3J9DQpoZWFkKGNvdW50KGFsbF90cmlwc18yMDIyX3YyLCBzdGFydF9zdGF0aW9uX25hbWUsIG1lbWJlcl9jYXN1YWwsICByaWRlYWJsZV90eXBlLCBzb3J0PSBUUlVFKSkNCmhlYWQoY291bnQoYWxsX3RyaXBzXzIwMjJfdjIsIGVuZF9zdGF0aW9uX25hbWUsIG1lbWJlcl9jYXN1YWwsICByaWRlYWJsZV90eXBlLCBzb3J0PSBUUlVFKSkNCmBgYA0KDQpTdGVwIDExOiBBbmFseXplIHJpZGVyc2hpcCBkYXRhIGJ5IHVzZXIgdHlwZXMgYW5kIGRheSBvZiB0aGUgd2Vlaw0KYGBge3J9DQphbGxfdHJpcHNfMjAyMl92MiAlPiUgDQogIG11dGF0ZSh3ZWVrZGF5ID0gd2RheShzdGFydGVkX2F0LCBsYWJlbCA9IFRSVUUpKSAlPiUgICNjcmVhdGVzIHdlZWtkYXkgZmllbGQgdXNpbmcgd2RheSgpDQogIGdyb3VwX2J5KG1lbWJlcl9jYXN1YWwsIHdlZWtkYXkpICU+JSAgICAgICAgICAgICAgICAgICNncm91cHMgYnkgdXNlcnR5cGUgYW5kIHdlZWtkYXkNCiAgc3VtbWFyaXNlKG51bWJlcl9vZl9yaWRlcyA9IG4oKQkJCQkjY2FsY3VsYXRlcyB0aGUgbnVtYmVyIG9mIHJpZGVzIGFuZCBhdmVyYWdlIGR1cmF0aW9uIA0KICAsYXZlcmFnZV9kdXJhdGlvbiA9IG1lYW4ocmlkZV9sZW5ndGgpKSAlPiUgCSAgICAgICAgIAkjIGNhbGN1bGF0ZXMgdGhlIGF2ZXJhZ2UgZHVyYXRpb24NCiAgYXJyYW5nZShtZW1iZXJfY2FzdWFsLCB3ZWVrZGF5KQkJCQkJCQkJICAgICAgICAjIHNvcnRzIGJ5IG1lbWJlcl9jYXN1YWwgYW5kIHdlZWtkYXkNCmBgYA0KDQpTdGVwIDEyIDogVmlzdWFsaXplIHRoZSBudW1iZXIgb2YgcmlkZXMgYnkgcmlkZXIgdHlwZXMgYW5kIGF2ZXJhZ2UgZHVyYXRpb24NCmBgYHtyfQ0KcGFyKG1mcm93PWMoMiwyKSkNCg0KIyBOdW1iZXIgb2YgcmlkZXMgYnkgcmlkZXIgdHlwZXMNCmFsbF90cmlwc18yMDIyX3YyICU+JQ0KICBtdXRhdGUod2Vla2RheSA9IHdkYXkoc3RhcnRlZF9hdCwgbGFiZWw9VFJVRSkpICU+JSANCiAgZ3JvdXBfYnkobWVtYmVyX2Nhc3VhbCwgd2Vla2RheSkgJT4lDQogIHN1bW1hcmlzZShudW1iZXJfb2ZfcmlkZXMgPSBuKCksIGF2ZXJhZ2VfZHVyYXRpb249bWVhbihyaWRlX2xlbmd0aCkpICU+JQ0KICBhcnJhbmdlKG1lbWJlcl9jYXN1YWwsIHdlZWtkYXkpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9d2Vla2RheSwgeT1udW1iZXJfb2ZfcmlkZXMsIGZpbGw9bWVtYmVyX2Nhc3VhbCkpKw0KICBnZW9tX2NvbChwb3NpdGlvbj0iZG9kZ2UiKQ0KYGBgDQoNCmBgYHtyfQ0KIyBOdW1iZXIgb2YgcmlkZXMgYnkgYXZlcmFnZSBkdXJhdGlvbg0KYWxsX3RyaXBzXzIwMjJfdjIgJT4lIA0KICBtdXRhdGUod2Vla2RheSA9IHdkYXkoc3RhcnRlZF9hdCwgbGFiZWwgPSBUUlVFKSkgJT4lIA0KICBncm91cF9ieShtZW1iZXJfY2FzdWFsLCB3ZWVrZGF5KSAlPiUgDQogIHN1bW1hcmlzZShudW1iZXJfb2ZfcmlkZXMgPSBuKCksYXZlcmFnZV9kdXJhdGlvbiA9IG1lYW4ocmlkZV9sZW5ndGgpKSAlPiUgDQogIGFycmFuZ2UobWVtYmVyX2Nhc3VhbCwgd2Vla2RheSkgICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gd2Vla2RheSwgeSA9IGF2ZXJhZ2VfZHVyYXRpb24sIGZpbGwgPSBtZW1iZXJfY2FzdWFsKSkgKw0KICBnZW9tX2NvbChwb3NpdGlvbiA9ICJkb2RnZSIpDQpgYGANCg0KU3RlcCAxMzogVmlzdWFsaXplIE1lbWJlciB2cyBDYXN1YWwgb24gc2hvcnQgcmlkZSAobGVzcyBhbmQgb25lIGhvdXIpDQpgYGB7cn0NCm9uZV9ob3VyX3JpZGUgPC0gYWxsX3RyaXBzXzIwMjJfdjIgJT4lIGZpbHRlcihyaWRlX2xlbmd0aCA8IDYwKQ0Kb25lX2hvdXJfcmlkZSRyaWRlX2xlbmd0aF9ieV9taW5zIDwtIGN1dChvbmVfaG91cl9yaWRlJHJpZGVfbGVuZ3RoLGJyZWFrcyA9IDIwKQ0KDQpnZ3Bsb3QoZGF0YSA9IG9uZV9ob3VyX3JpZGUpICsgDQogIGdlb21fYmFyKG1hcHBpbmcgPSBhZXMoeCA9IHJpZGVfbGVuZ3RoX2J5X21pbnMsIGZpbGwgPSBtZW1iZXJfY2FzdWFsKSkgKw0KICBsYWJzKHRpdGxlID0gIk9uZSBob3VyIHJpZGUgbGVuZ3RoIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9YygiMyIsICI2IiwgIjkiLCAiMTIiLCAiMTUiLCAiMTgiLCAiMjEiLCAiMjQiLCAiMjciLCAiMzAiLCAiMzMiLCAiMzYiLCAiMzkiLCAiNDIiLCAiNDUiLCAiNDgiLCAiNTEiLCAiNTQiLCAiNTciLCAiNjAiKSkNCmBgYA0KDQpTdGVwIDE0IDogVmlzdWFsaXplIGRheSBvZiB0aGUgd2VlayByaWRpbmcgY2hvaWNlcyBiZXR3ZWVuIG1lbWJlciB2cyBjYXVzYWwNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBhbGxfdHJpcHNfMjAyMl92MikgKw0KICBnZW9tX2JhcihtYXBwaW5nID0gYWVzKHggPSBmYWN0b3IoZGF5X29mX3dlZWspLCBmaWxsID0gcmlkZWFibGVfdHlwZSkpKw0KICBmYWNldF93cmFwKH5tZW1iZXJfY2FzdWFsKSArDQogIGxhYnModGl0bGU9J3JpZGluZyBjaG9pY2UgZHVyaW5nIGRheSBvZiB0aGUgd2VlaycsIHg9ICdkYXkgb2YgdGhlIHdlZWsnICkrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkNCmBgYA0KDQpTdGVwIDE1IDogQ2hlY2sgZm9yIHBlYWsgdGltZSBmb3IgYmlrZSB1c2FnZSBiZXR3ZWVuIG1lbWJlciB2cyBjYXN1YWwNCmBgYHtyfQ0KaG91cl9kYXRhIDwtIGFsbF90cmlwc18yMDIyX3YyDQpob3VyX2RhdGEkc3RhcnRfaG91ciA8LSBhcy5udW1lcmljKGZvcm1hdChzdHJwdGltZShhbGxfdHJpcHNfMjAyMl92MiRzdGFydGVkX2F0LCIlWS0lbS0lZCAlSDolTTolT1MiKSwnJUgnKSkNCg0KZ2dwbG90KGRhdGEgPSBob3VyX2RhdGEpICsgDQogIGdlb21fYmFyKG1hcHBpbmcgPSBhZXMoeCA9IHN0YXJ0X2hvdXIsIGZpbGwgPSBtZW1iZXJfY2FzdWFsKSwgc3RhdCA9ICdjb3VudCcpICsgDQogIGZhY2V0X3dyYXAofmZhY3RvcihkYXlfb2Zfd2VlaykpICsNCiAgbGFicyh0aXRsZSA9ICJiaWtlIHVzYWdlIGJ5IHN0YXJ0aW5nIGhvdXIiLCB4ID0gInN0YXJ0aW5nIGhvdXIiKSArIA0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpIA0KYGBgDQoNClN0ZXAgMTYgOiBTYXZlIGFzIGNzdiBmb3IgZnVydGhlciBhbmFseXNpcyBhbmQgdmlzdWFsaXphdGlvbiBpbiBUYWJsZWF1DQpgYGB7cn0NCiN0aGUgZGF0YSBmcmFtZQ0Kd3JpdGVfY3N2KGFsbF90cmlwc18yMDIyX3YyLCAiYWxsX3RyaXBzLmNzdiIpDQp3cml0ZV9jc3YoaG91cl9kYXRhLCAiaG91cl9kYXRhX3JpZGUuY3N2IikNCndyaXRlX2NzdihvbmVfaG91cl9yaWRlLCAib25lX2hvdXJfcmlkZS5jc3YiKQ0KDQojdG90YWwgYW5kIGF2ZXJhZ2Ugd2Vla2x5IHJpZGVzIGJ5IHJpZGVyIHR5cGUNCnN1bW1hcnlfcmlkZV93ZWVrbHkgPC0gYWxsX3RyaXBzXzIwMjJfdjIgJT4lIA0KICBtdXRhdGUod2Vla2RheSA9IHdkYXkoc3RhcnRlZF9hdCwgbGFiZWwgPSBUUlVFKSkgJT4lIA0KICBncm91cF9ieShtZW1iZXJfY2FzdWFsLCB3ZWVrZGF5KSAlPiUgDQogIHN1bW1hcmlzZShudW1iZXJfb2ZfcmlkZXMgPSBuKCkNCiAgICAgICAgICAgICxhdmVyYWdlX2R1cmF0aW9uID0gbWVhbihyaWRlX2xlbmd0aCkpICU+JSANCiAgYXJyYW5nZShtZW1iZXJfY2FzdWFsLCB3ZWVrZGF5KSAgDQogIA0Kd3JpdGVfY3N2KHN1bW1hcnlfcmlkZV93ZWVrbHksICJzdW1tYXJ5X3JpZGVfd2Vla2x5LmNzdiIpDQoNCiN0b3RhbCBhbmQgYXZlcmFnZSB3ZWVrbHkgcmlkZXMgYnkgcmlkZXIgdHlwZQ0Kc3VtbWFyeV9yaWRlX3dlZWtseV90eXBlIDwtIGFsbF90cmlwc18yMDIyX3YyICU+JSANCiAgbXV0YXRlKHdlZWtkYXkgPSB3ZGF5KHN0YXJ0ZWRfYXQsIGxhYmVsID0gVFJVRSkpICU+JSANCiAgZ3JvdXBfYnkobWVtYmVyX2Nhc3VhbCwgd2Vla2RheSwgcmlkZWFibGVfdHlwZSkgJT4lIA0KICBzdW1tYXJpc2UobnVtYmVyX29mX3JpZGVzID0gbigpDQogICAgICAgICAgICAsYXZlcmFnZV9kdXJhdGlvbiA9IG1lYW4ocmlkZV9sZW5ndGgpKSAlPiUgDQogIGFycmFuZ2UobWVtYmVyX2Nhc3VhbCwgd2Vla2RheSkgIA0KICANCndyaXRlX2NzdihzdW1tYXJ5X3JpZGVfd2Vla2x5X3R5cGUsICJzdW1tYXJ5X3JpZGVfd2Vla2x5X3R5cGUuY3N2IikNCiAgDQojdG90YWwgYW5kIGF2ZyBtb250aGx5IHJpZGVzIGJ5IHJpZGVyIHR5cGUNCnN1bW1hcnlfbW9udGggPC0gYWxsX3RyaXBzXzIwMjJfdjIgJT4lIA0KICBtdXRhdGUobW9udGggPSBtb250aChzdGFydGVkX2F0LCBsYWJlbCA9IFRSVUUpKSAlPiUgIA0KICBncm91cF9ieShtb250aCxtZW1iZXJfY2FzdWFsKSAlPiUgIA0KICBzdW1tYXJpc2UobnVtYmVyX29mX3JpZGVzID0gbigpDQogICAgICAgICAgICAsYXZlcmFnZV9kdXJhdGlvbiA9IG1lYW4ocmlkZV9sZW5ndGgpKSAlPiUgICAgDQogIGFycmFuZ2UobW9udGgsIG1lbWJlcl9jYXN1YWwpDQp3cml0ZV9jc3Yoc3VtbWFyeV9tb250aCwgInN1bW1hcnlfcmlkZV9tb250aGx5LmNzdiIpDQoNCiNtb3N0IHBvcHVsYXIgc3RhdGlvbnMNCnBvcHVsYXJfc3RhdGlvbnMgPC0gYWxsX3RyaXBzXzIwMjJfdjIgJT4lDQogIG11dGF0ZShzdGF0aW9uID0gc3RhcnRfc3RhdGlvbl9uYW1lKSAlPiUNCiAgZHJvcF9uYShzdGFydF9zdGF0aW9uX25hbWUpICU+JQ0KICBncm91cF9ieShzdGFydF9zdGF0aW9uX25hbWUsIG1lbWJlcl9jYXN1YWwpICU+JQ0KICBzdW1tYXJpc2UobnVtYmVyX29mX3JpZGVzPW4oKSkNCg0Kd3JpdGVfY3N2KHBvcHVsYXJfc3RhdGlvbnMsICJwb3B1bGFyX3N0YXRpb25zLmNzdiIpDQoNCiN0b3RhbCBtZW1iZXJzaGlwIHR5cGVzIGFuZCByaWRlYWJsZSB0eXBlcw0KdG90YWxfcmlkZXJzIDwtIGRhdGEuZnJhbWUodGFibGUoYWxsX3RyaXBzXzIwMjJfdjIkbWVtYmVyX2Nhc3VhbCkpDQp0b3RhbF90eXBlcyA8LSBkYXRhLmZyYW1lKHRhYmxlKGFsbF90cmlwc18yMDIyX3YyJHJpZGVhYmxlX3R5cGUpKQ0KDQp3cml0ZV9jc3YodG90YWxfcmlkZXJzLCAidG90YWxfcmlkZXJzLmNzdiIpDQp3cml0ZV9jc3YodG90YWxfdHlwZXMsICJ0b3RhbF90eXBlcy5jc3YiKQ0KYGBgDQoNCg==