Citi Bike Data

Welcome to the off platform project focused on visualization. In this project, we will be exploring data associated with the New York City bike share program, Citi Bike! Remember, it may be easiest to read these instructions by clicking on the “Preview” button in RStudio.

There are over 850 Citi Bike stations in New York City — users check a bike out from a starting station and then dock that bike at a different station when they reach their destination. Citi Bike offers a variety of memberships, but most memberships allow for trips between 30 and 45 minutes — this will be relevant once we start digging into the dataset.

Citi Bike publically releases a variety of datasets. We’ve included a dataset containing information about individual trips from January of 2020. If you’d like to download a more recent dataset, or investigate other data that Citi Bike provides, take a look at their System Data page. There are so many interesting questions that you can investigate with this data — we’re about to walk you through a few, but we’d love to see what else you can discover!

Investigate the Data

We’ve included a file named january_trips.csv. Load this data into a dataframe using the read.csv() function. Note that this dataset is big. It may take a few minutes to load — if you’d like to use a subset of this data that will take less time to load, we’ve included a file named january_trips_subset.csv as well. We strongly recommend using this subset of the data. When loading these datasets, make sure that the .csv files are in the same directory as this .Rmd file.

Note that because this dataset is so large, it may take several seconds to load the data or preview your R Notebook.

# Load the data set into a data frame

all_data <- read.csv("january_trips_subset.csv")

Now that we’ve loaded the dataset, the easiest way to investigate the data is to click on the variable name in the “Global Environment” tab in RStudio. This will let you scroll through the data as if it were a spreadsheet. If you want to display some of the data in this document, call the head() function using your data frame as a parameter. Make sure to scroll through all of the columns!

# Investigate the data

head(all_data)

Since we have information about the starting and ending location for each trip, let’s quickly make a heat map of the starting locations. Make a heat map using ggplot() and geom_bin2d(). If you make the bin width for each axis very small (we used 0.001), you should see the shape of Manhattan, Brooklyn, and Queens! Check out the rectangle in Manhattan with no stations — that’s Central Park!

Make sure to install and load ggplot2 and dplyr!

# Install and load ggplot2 and dplyr
library(ggplot2)
package ‘ggplot2’ was built under R version 4.0.2
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
# Create a heatmap

heatmap <- ggplot(all_data, aes(start.station.longitude, end.station.latitude)) + geom_bin2d(binwidth = 0.001)
heatmap

We also have the duration of each trip. Using these features, we can calculate the average speed of each trip.

Finally, since we also have the date of birth for each rider, we can calculate their age.

Let’s work towards building a line graph where age is on the x axis and average speed is on the y axis. This graph could help Citi Bike understand how their users are using their bikes. For example, if younger riders tend to bike much faster than older riders, Citi Bike may want to think about ways to encourage younger riders to bike more cautiously.

Before we begin to work on the visualization, we’ll have to work a bit with our dataset to get all of the relevant columns.

Modifying the Data Frame: Subset and Age

Since this dataset is so big, we recommend using the filter() function to grab a subset of the data. For example, you could grab only the rows where the duration was under 900 seconds (15 minutes). We stored these rows in a new data frame named short_trips. Note that we did this only to speed up the runtime of upcoming operations. This is completely optional — if you’re happy to wait a bit for each operation, feel free to use the complete data set.

# Create a subset of the data
short_trips <- all_data %>% filter(tripduration < 900)
head(short_trips)

Next, let’s add a column called age to the data frame. age should be 2020 minus birth.year (this data was collected in 2020). Use the mutate() function to do this. After calling mutate, make sure to save the result in a variable. We should save the result back to short_trips.

# Add the age column
short_trips <- short_trips %>% mutate(age = 2020 - birth.year)
head(short_trips)

Modifying the Data Frame: Distance

In order to calculate the speed of each biker, we need to find the total distance they traveled. Luckily, we have information about the starting and ending latitudes and longitudes. Let’s use those four columns to create a new column named distance.

There are many different ways to calculate distance. We’ll walk you through the strategy we used. However, before following along with us, challenge yourself to solve this problem on your own — one of the goals of these off platform projects is to get comfortable problem solving on your own. Try to use Google to find the packages you might need to calculate the distance between latitude and longitude coordinates. Use the code block below to try solving this problem on your own. We’ll walk you through our solution in the following section.

As you write your code that edits the data frame, consider printing the head of the data frame to validate the work you are doing!

# Try creating a distance column in your data frame here:
library(geosphere)

starting_stations <- short_trips %>% select(start.station.latitude, start.station.longitude)
ending_stations <- short_trips %>% select(end.station.latitude, end.station.longitude)

short_trips <- short_trips %>% mutate(distance = distHaversine(starting_stations, ending_stations))

head(short_trips)

There are many different strategies to calculate the distance between two points. The simplest way to do this would be to find the length of the straight line between the two points. This is a massive assumption to make — it would be remarkable if any of these bike trips traveled in a straight line between the two points without making any turns or curves.

That being said, finding the straight line distance is a good starting point. The distHaversine() function found in the geosphere library can calculate this distance.

First, install and load the geosphere library.

Next, use dplyr’s select() function to create two new data frames that contain only the latitudes and longitudes of the starting and ending points. We called these data frames starting_stations and ending_stations.

Finally, use dplyr’s mutate() function to add a column named distance to your data. distance should be calculated by calling distHaversine() using starting_stations and ending_stations as parameters.

If you get stuck, use ?distHaversine to check the documentation to see more examples! You can also use the documentation to find the units of the result of distHaversine()!

# Use the geosphere library to create a distance column

Modifying the Data Frame: Speed

Now that we’ve made a column containing the distance of each trip, let’s make another column containing the average speed of each trip. This column should be easier to create than the previous — speed can be calculated by dividing the distance column by the tripduration column. This will give us the average speed in meters per second. Use the mutate() function to make the speed column!

# Create the speed column
short_trips <- short_trips %>% mutate(avg_speed = distance / tripduration)
head(short_trips)
NA

Modifying the Data Frame: Average Speed by Age

We’re almost there! Now that we have the speed of every bike trip, we want to group those trips by age and find the average speed of each age.

Do this by piping your data frame into the group_by() function using age as a parameter.

Then pipe the result of that function into the summarize() function. summarize() works similarly to mutate() — pass mean_speed = mean(speed) to the summarize() function to create a new column named mean_speed. Save this new data frame in a variable called average_speed_by_age.

# Use group_by() and summarize() to get the mean speed of each age
average_speed_by_age <- short_trips %>% group_by(age) %>% summarize(mean_speed = mean(avg_speed))
`summarise()` ungrouping output (override with `.groups` argument)
head(average_speed_by_age)
NA

Visualization!

We made it! We now have the average speed of every age in our dataset. Let’s use ggplot2 to make a line graph to see if younger people really do bike faster. Make sure to install and load ggplot2 if you haven’t done so already. Pass your data frame to ggplot() and add a geom_line(). geom_line() should contain an aesthetic where x = age and y = mean_speed.

# Install and load ggplot2 to create a line graph of age and mean speed
line_graph <- ggplot(average_speed_by_age) + geom_line(aes(age, mean_speed)) 
line_graph

Nice work! Our intuition seems to be right — there’s a steady drop in speed until we hit some outliers. It would be pretty surprising to see someone over the age of 100 using a bike share program! Let’s filter the data to only show ages less than 80 and redraw our visualization.

# Filter the data frame to only contain rows where the age is less than 80
under_80 <- average_speed_by_age %>% filter(age < 80)
line_graph_2 <- ggplot(under_80) + geom_line(aes(age, mean_speed)) 
line_graph_2

That looks a bit better! Let’s do some work to make our graph look a bit more professional. Add a title and axis labels. We also centered our title!

# Add a title and label the axes
line_graph_2 + labs(title = "Average Speed based on Age for Janauary", x = "Age", y = "Average Speed (m/s)") + theme(plot.title = element_text(hjust = 0.5))

Filtering By Gender

Great work! This visualization gives us some great insights on how Citi Bike users are using their bikes. Let’s dive even deeper! We can group our data by more than one feature.

Find your line of code that grouped our data by age. Copy it, but add gender as a parameter to the group_by() function. Save the result in a data frame named average_speed_by_age_and_gender. Inspect this data frame to see what it contains.

# Use group_by() again to group by both age and gender
average_speed_by_age <- short_trips %>% group_by(age, gender) %>%  summarize(mean_speed = mean(avg_speed))
`summarise()` regrouping output by 'age' (override with `.groups` argument)
head(average_speed_by_age)
NA

Let’s now visualize the difference in average speed by age and gender. Note that if you look in the documentation for the data, a 0 represents a user that didn’t specify their gender as male or female, a 1 represents a user identifying as male, and a 2 represents a user identifying as female.

The previous call to ggplot() and geom_line() should be close to what we want. Add the parameter color = gender to the aesthetic in geom_line(). Make sure you use the new data containing the gender information! You once again may want to filter out the ages greater than 80.

Note that this graph won’t quite be what we want yet, but we’re getting close!

# Make a line graph of your new filtered data frame
new_line_graph <- ggplot(average_speed_by_age) + geom_line(aes(age, mean_speed, color = gender)) + labs(title = "Average Speed based on Age for Janauary by Gender", x = "Age", y = "Average Speed (m/s)") + theme(plot.title = element_text(hjust = 0.5))
new_line_graph

It’s a bit hard to tell what is happening in that graph — but one oddity that sticks out is the scale used for gender. We know that our gender data is represented as three distinct values — 0, 1, and 2. However, ggplot() is using gender as a continuous variable.

We can turn this column into a factor by using the as.factor() and mutate() functions. Pipe your data frame into the mutate() function and use gender = as.factor(gender) as the parameter.

Note that when you make this column a factor, you will see a number of warnings. This warning is telling you that the type of the values in the gender column have been changed from integers to characters.

Then redraw your graph.

# Use mutate() and as.factor() to change the gender column into a factor.
average_speed_by_age <- average_speed_by_age %>% mutate(gender = as.factor(gender))
head(average_speed_by_age)
ggplot(average_speed_by_age) + geom_line(aes(age, mean_speed, color = gender))

Nice work! We can now see the average speeds by age broken into the 3 genders Citi Bike accounts for. You can see that male-identifying users typically bike faster than female-identifying users. Users with an unknown gender don’t follow a specific pattern — it is likely that there isn’t enough data to properly visualize those users.

For our final version of this graph, we filtered out the users with a gender of 0, and we labeled the lines as “Male Identifying” and “Female Identifying” using the scale_color_discrete() function. Take a look at the documentation for this function using ?scale_color_discrete to change the label of each line.

# scale_color_discrete(name = "Gender", labels = c("Male Identifying", "Female Identifying"))
# Filter the data frame to only include genders 1 and 2. Set appropriate labels for the legend
average_speed_by_age <- average_speed_by_age %>% filter(gender == 1 | gender == 2, age < 80)
head(average_speed_by_age)
ggplot(average_speed_by_age) + geom_line(aes(age, mean_speed, color = gender)) + labs(title = "Average Speed based on Age for Janauary by Gender", x = "Age", y = "Average Speed (m/s)") + theme(plot.title = element_text(hjust = 0.5)) + scale_color_discrete(name = "Gender", labels = c("Male Identifying", "Female Identifying"))

Making a Stacked Bar Plot Of Ages

Let’s make one final graph. For this graph, we’re interested in seeing the distribution of Citi Bike users’ age and gender. Let’s use a stacked bar plot to do this. We’ll want to create a bar plot where age is on the x axis, count is on the y axis, and each bar is split into genders.

Let’s start by using our short_trips dataset. We’ll want to call group_by() using this dataset and pipe the result to tally(). This will let us get a count of the number of bikers for each age and gender. We saved this new data frame in a variable called age_counts.

# Create the age_counts data frame
age_counts <- short_trips %>% group_by(age, gender) %>% tally 
age_counts

If you look at the head of this new data frame, you’ll see the counts are stored in a column named n. Let’s now use ggplot() and geom_col() to create a stacked bar plot. ggplot() should have an aes() where x = age, y = n and fill = gender.

# Create the stacked bar plot
stacked_plot <- age_counts %>% ggplot(aes(age, n, fill = gender)) + geom_col()
stacked_plot

Great! There are some tweaks that we might want to make to this graph. First, gender right now is represented as an integer. It will make more sense if that column is represented as a factor. To do this, we can pass as.factor(gender) as the value for the x axis.

Next, it looks like we have some unusual data around the age of 50. It looks like there are a ton of bikers with an unknown gender at that age. This might be something we want to dig into a bit more, but for now, let’s filter out the bikers with a gender of 0. We also filtered out bikers with an age over 100 — that seems like an error in data collection as well.

Finally, we labeled and titled our graph using labs() and scale_fill_discrete()

# Filter and label your graph
stacked_plot_filtered <- age_counts %>% filter(gender != 0) %>% ggplot(aes(age, n, fill = as.factor(gender))) + geom_col() + labs(title = "Bikers by Gender and Age", x = "Age", y = "Count") + scale_fill_discrete(name = "Gender", labels = c("Male", "Female")) + theme(plot.title = element_text(hjust = 0.5))
stacked_plot_filtered

Further Work

Great work! You’ve made several graphs that show a real difference in the way different groups of Citi Bike users bike. This could be a valuable asset in helping Citi Bike understand how to make bike riding safer in New York. However, there is so much more you can do with this data!

To begin, there are some major flaws in the way we calculated the speed. Specifically, we made some huge assumptions when calculating the distance of each bike ride. Instead of calculating the straight line distance using the geosphere library, we could take advantage of a service like Google Map’s API to get a more accurate measurement of distance. If you’re interested in look more into this problem, investigate getting a Google Maps API key and using a library like gmapsdistance.

Another great way to extend this project is to investigate other data that Citi Bike makes available. We used data found in the Citi Bike Trip Histories section on the System Data page. On the System Data page, you can find different dataset, including information about membership data and real time station data. You could use this real time data to track how the flow of bikes changes over the course of the day. You could investigate how the weather impacts membership. We would love to see any graphs or insights you produce!

LS0tCnRpdGxlOiAiRXhwbG9yZSBDaXRpIEJpa2UgRGF0YSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMjIENpdGkgQmlrZSBEYXRhCgpXZWxjb21lIHRvIHRoZSBvZmYgcGxhdGZvcm0gcHJvamVjdCBmb2N1c2VkIG9uIHZpc3VhbGl6YXRpb24uIEluIHRoaXMgcHJvamVjdCwgd2Ugd2lsbCBiZSBleHBsb3JpbmcgZGF0YSBhc3NvY2lhdGVkIHdpdGggdGhlIE5ldyBZb3JrIENpdHkgYmlrZSBzaGFyZSBwcm9ncmFtLCBDaXRpIEJpa2UhIFJlbWVtYmVyLCBpdCBtYXkgYmUgZWFzaWVzdCB0byByZWFkIHRoZXNlIGluc3RydWN0aW9ucyBieSBjbGlja2luZyBvbiB0aGUgIlByZXZpZXciIGJ1dHRvbiBpbiBSU3R1ZGlvLgoKVGhlcmUgYXJlIG92ZXIgODUwIENpdGkgQmlrZSBzdGF0aW9ucyBpbiBOZXcgWW9yayBDaXR5ICZtZGFzaDsgdXNlcnMgY2hlY2sgYSBiaWtlIG91dCBmcm9tIGEgc3RhcnRpbmcgc3RhdGlvbiBhbmQgdGhlbiBkb2NrIHRoYXQgYmlrZSBhdCBhIGRpZmZlcmVudCBzdGF0aW9uIHdoZW4gdGhleSByZWFjaCB0aGVpciBkZXN0aW5hdGlvbi4gQ2l0aSBCaWtlIG9mZmVycyBhIHZhcmlldHkgb2YgbWVtYmVyc2hpcHMsIGJ1dCBtb3N0IG1lbWJlcnNoaXBzIGFsbG93IGZvciB0cmlwcyBiZXR3ZWVuIDMwIGFuZCA0NSBtaW51dGVzICZtZGFzaDsgdGhpcyB3aWxsIGJlIHJlbGV2YW50IG9uY2Ugd2Ugc3RhcnQgZGlnZ2luZyBpbnRvIHRoZSBkYXRhc2V0LgoKQ2l0aSBCaWtlIHB1YmxpY2FsbHkgcmVsZWFzZXMgYSB2YXJpZXR5IG9mIGRhdGFzZXRzLiBXZSd2ZSBpbmNsdWRlZCBhIGRhdGFzZXQgY29udGFpbmluZyBpbmZvcm1hdGlvbiBhYm91dCBpbmRpdmlkdWFsIHRyaXBzIGZyb20gSmFudWFyeSBvZiAyMDIwLiBJZiB5b3UnZCBsaWtlIHRvIGRvd25sb2FkIGEgbW9yZSByZWNlbnQgZGF0YXNldCwgb3IgaW52ZXN0aWdhdGUgb3RoZXIgZGF0YSB0aGF0IENpdGkgQmlrZSBwcm92aWRlcywgdGFrZSBhIGxvb2sgYXQgdGhlaXIgW1N5c3RlbSBEYXRhXShodHRwczovL3d3dy5jaXRpYmlrZW55Yy5jb20vc3lzdGVtLWRhdGEpIHBhZ2UuIFRoZXJlIGFyZSBzbyBtYW55IGludGVyZXN0aW5nIHF1ZXN0aW9ucyB0aGF0IHlvdSBjYW4gaW52ZXN0aWdhdGUgd2l0aCB0aGlzIGRhdGEgJm1kYXNoOyB3ZSdyZSBhYm91dCB0byB3YWxrIHlvdSB0aHJvdWdoIGEgZmV3LCBidXQgd2UnZCBsb3ZlIHRvIHNlZSB3aGF0IGVsc2UgeW91IGNhbiBkaXNjb3ZlciEKCiMjIyBJbnZlc3RpZ2F0ZSB0aGUgRGF0YQoKV2UndmUgaW5jbHVkZWQgYSBmaWxlIG5hbWVkIGBqYW51YXJ5X3RyaXBzLmNzdmAuIExvYWQgdGhpcyBkYXRhIGludG8gYSBkYXRhZnJhbWUgdXNpbmcgdGhlIGByZWFkLmNzdigpYCBmdW5jdGlvbi4gTm90ZSB0aGF0IHRoaXMgZGF0YXNldCBpcyBfYmlnXy4gSXQgbWF5IHRha2UgYSBmZXcgbWludXRlcyB0byBsb2FkICZtZGFzaDsgaWYgeW91J2QgbGlrZSB0byB1c2UgYSBzdWJzZXQgb2YgdGhpcyBkYXRhIHRoYXQgd2lsbCB0YWtlIGxlc3MgdGltZSB0byBsb2FkLCB3ZSd2ZSBpbmNsdWRlZCBhIGZpbGUgbmFtZWQgYGphbnVhcnlfdHJpcHNfc3Vic2V0LmNzdmAgYXMgd2VsbC4gV2Ugc3Ryb25nbHkgcmVjb21tZW5kIHVzaW5nIHRoaXMgc3Vic2V0IG9mIHRoZSBkYXRhLiBXaGVuIGxvYWRpbmcgdGhlc2UgZGF0YXNldHMsIG1ha2Ugc3VyZSB0aGF0IHRoZSAuY3N2IGZpbGVzIGFyZSBpbiB0aGUgc2FtZSBkaXJlY3RvcnkgYXMgdGhpcyAuUm1kIGZpbGUuCgpOb3RlIHRoYXQgYmVjYXVzZSB0aGlzIGRhdGFzZXQgaXMgc28gbGFyZ2UsIGl0IG1heSB0YWtlIHNldmVyYWwgc2Vjb25kcyB0byBsb2FkIHRoZSBkYXRhIG9yIHByZXZpZXcgeW91ciBSIE5vdGVib29rLgoKYGBge3J9CiMgTG9hZCB0aGUgZGF0YSBzZXQgaW50byBhIGRhdGEgZnJhbWUKCmFsbF9kYXRhIDwtIHJlYWQuY3N2KCJqYW51YXJ5X3RyaXBzX3N1YnNldC5jc3YiKQpgYGAKCk5vdyB0aGF0IHdlJ3ZlIGxvYWRlZCB0aGUgZGF0YXNldCwgdGhlIGVhc2llc3Qgd2F5IHRvIGludmVzdGlnYXRlIHRoZSBkYXRhIGlzIHRvIGNsaWNrIG9uIHRoZSB2YXJpYWJsZSBuYW1lIGluIHRoZSAiR2xvYmFsIEVudmlyb25tZW50IiB0YWIgaW4gUlN0dWRpby4gVGhpcyB3aWxsIGxldCB5b3Ugc2Nyb2xsIHRocm91Z2ggdGhlIGRhdGEgYXMgaWYgaXQgd2VyZSBhIHNwcmVhZHNoZWV0LiBJZiB5b3Ugd2FudCB0byBkaXNwbGF5IHNvbWUgb2YgdGhlIGRhdGEgaW4gdGhpcyBkb2N1bWVudCwgY2FsbCB0aGUgYGhlYWQoKWAgZnVuY3Rpb24gdXNpbmcgeW91ciBkYXRhIGZyYW1lIGFzIGEgcGFyYW1ldGVyLiBNYWtlIHN1cmUgdG8gc2Nyb2xsIHRocm91Z2ggYWxsIG9mIHRoZSBjb2x1bW5zIQoKYGBge3J9CiMgSW52ZXN0aWdhdGUgdGhlIGRhdGEKCmhlYWQoYWxsX2RhdGEpCmBgYAoKCgpTaW5jZSB3ZSBoYXZlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBzdGFydGluZyBhbmQgZW5kaW5nIGxvY2F0aW9uIGZvciBlYWNoIHRyaXAsIGxldCdzIHF1aWNrbHkgbWFrZSBhIGhlYXQgbWFwIG9mIHRoZSBzdGFydGluZyBsb2NhdGlvbnMuIE1ha2UgYSBoZWF0IG1hcCB1c2luZyBgZ2dwbG90KClgIGFuZCBgZ2VvbV9iaW4yZCgpYC4gSWYgeW91IG1ha2UgdGhlIGJpbiB3aWR0aCBmb3IgZWFjaCBheGlzIHZlcnkgc21hbGwgKHdlIHVzZWQgYDAuMDAxYCksIHlvdSBzaG91bGQgc2VlIHRoZSBzaGFwZSBvZiBNYW5oYXR0YW4sIEJyb29rbHluLCBhbmQgUXVlZW5zISBDaGVjayBvdXQgdGhlIHJlY3RhbmdsZSBpbiBNYW5oYXR0YW4gd2l0aCBubyBzdGF0aW9ucyAmbWRhc2g7IHRoYXQncyBDZW50cmFsIFBhcmshCgpNYWtlIHN1cmUgdG8gaW5zdGFsbCBhbmQgbG9hZCBgZ2dwbG90MmAgYW5kIGBkcGx5cmAhCgpgYGB7cn0KIyBJbnN0YWxsIGFuZCBsb2FkIGdncGxvdDIgYW5kIGRwbHlyCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKIyBDcmVhdGUgYSBoZWF0bWFwCgpoZWF0bWFwIDwtIGdncGxvdChhbGxfZGF0YSwgYWVzKHN0YXJ0LnN0YXRpb24ubG9uZ2l0dWRlLCBlbmQuc3RhdGlvbi5sYXRpdHVkZSkpICsgZ2VvbV9iaW4yZChiaW53aWR0aCA9IDAuMDAxKQpoZWF0bWFwCmBgYAoKCldlIGFsc28gaGF2ZSB0aGUgZHVyYXRpb24gb2YgZWFjaCB0cmlwLiBVc2luZyB0aGVzZSBmZWF0dXJlcywgd2UgY2FuIGNhbGN1bGF0ZSB0aGUgYXZlcmFnZSBzcGVlZCBvZiBlYWNoIHRyaXAuCgpGaW5hbGx5LCBzaW5jZSB3ZSBhbHNvIGhhdmUgdGhlIGRhdGUgb2YgYmlydGggZm9yIGVhY2ggcmlkZXIsIHdlIGNhbiBjYWxjdWxhdGUgdGhlaXIgYWdlLgoKTGV0J3Mgd29yayB0b3dhcmRzIGJ1aWxkaW5nIGEgbGluZSBncmFwaCB3aGVyZSBhZ2UgaXMgb24gdGhlIHggYXhpcyBhbmQgYXZlcmFnZSBzcGVlZCBpcyBvbiB0aGUgeSBheGlzLiBUaGlzIGdyYXBoIGNvdWxkIGhlbHAgQ2l0aSBCaWtlIHVuZGVyc3RhbmQgaG93IHRoZWlyIHVzZXJzIGFyZSB1c2luZyB0aGVpciBiaWtlcy4gRm9yIGV4YW1wbGUsIGlmIHlvdW5nZXIgcmlkZXJzIHRlbmQgdG8gYmlrZSBtdWNoIGZhc3RlciB0aGFuIG9sZGVyIHJpZGVycywgQ2l0aSBCaWtlIG1heSB3YW50IHRvIHRoaW5rIGFib3V0IHdheXMgdG8gZW5jb3VyYWdlIHlvdW5nZXIgcmlkZXJzIHRvIGJpa2UgbW9yZSBjYXV0aW91c2x5LgoKQmVmb3JlIHdlIGJlZ2luIHRvIHdvcmsgb24gdGhlIHZpc3VhbGl6YXRpb24sIHdlJ2xsIGhhdmUgdG8gd29yayBhIGJpdCB3aXRoIG91ciBkYXRhc2V0IHRvIGdldCBhbGwgb2YgdGhlIHJlbGV2YW50IGNvbHVtbnMuCgojIyMgTW9kaWZ5aW5nIHRoZSBEYXRhIEZyYW1lOiBTdWJzZXQgYW5kIEFnZQoKU2luY2UgdGhpcyBkYXRhc2V0IGlzIHNvIGJpZywgd2UgcmVjb21tZW5kIHVzaW5nIHRoZSBgZmlsdGVyKClgIGZ1bmN0aW9uIHRvIGdyYWIgYSBzdWJzZXQgb2YgdGhlIGRhdGEuIEZvciBleGFtcGxlLCB5b3UgY291bGQgZ3JhYiBvbmx5IHRoZSByb3dzIHdoZXJlIHRoZSBkdXJhdGlvbiB3YXMgdW5kZXIgOTAwIHNlY29uZHMgKDE1IG1pbnV0ZXMpLiBXZSBzdG9yZWQgdGhlc2Ugcm93cyBpbiBhIG5ldyBkYXRhIGZyYW1lIG5hbWVkIGBzaG9ydF90cmlwc2AuIE5vdGUgdGhhdCB3ZSBkaWQgdGhpcyBvbmx5IHRvIHNwZWVkIHVwIHRoZSBydW50aW1lIG9mIHVwY29taW5nIG9wZXJhdGlvbnMuIFRoaXMgaXMgY29tcGxldGVseSBvcHRpb25hbCAmbWRhc2g7IGlmIHlvdSdyZSBoYXBweSB0byB3YWl0IGEgYml0IGZvciBlYWNoIG9wZXJhdGlvbiwgZmVlbCBmcmVlIHRvIHVzZSB0aGUgY29tcGxldGUgZGF0YSBzZXQuCgpgYGB7cn0KIyBDcmVhdGUgYSBzdWJzZXQgb2YgdGhlIGRhdGEKc2hvcnRfdHJpcHMgPC0gYWxsX2RhdGEgJT4lIGZpbHRlcih0cmlwZHVyYXRpb24gPCA5MDApCmhlYWQoc2hvcnRfdHJpcHMpCmBgYAoKTmV4dCwgbGV0J3MgYWRkIGEgY29sdW1uIGNhbGxlZCBgYWdlYCB0byB0aGUgZGF0YSBmcmFtZS4gYGFnZWAgc2hvdWxkIGJlIDIwMjAgbWludXMgYGJpcnRoLnllYXJgICh0aGlzIGRhdGEgd2FzIGNvbGxlY3RlZCBpbiAyMDIwKS4gVXNlIHRoZSBgbXV0YXRlKClgIGZ1bmN0aW9uIHRvIGRvIHRoaXMuIEFmdGVyIGNhbGxpbmcgbXV0YXRlLCBtYWtlIHN1cmUgdG8gc2F2ZSB0aGUgcmVzdWx0IGluIGEgdmFyaWFibGUuIFdlIHNob3VsZCBzYXZlIHRoZSByZXN1bHQgYmFjayB0byBgc2hvcnRfdHJpcHNgLgoKYGBge3J9CiMgQWRkIHRoZSBhZ2UgY29sdW1uCnNob3J0X3RyaXBzIDwtIHNob3J0X3RyaXBzICU+JSBtdXRhdGUoYWdlID0gMjAyMCAtIGJpcnRoLnllYXIpCmhlYWQoc2hvcnRfdHJpcHMpCmBgYAoKIyMjIE1vZGlmeWluZyB0aGUgRGF0YSBGcmFtZTogRGlzdGFuY2UKCkluIG9yZGVyIHRvIGNhbGN1bGF0ZSB0aGUgc3BlZWQgb2YgZWFjaCBiaWtlciwgd2UgbmVlZCB0byBmaW5kIHRoZSB0b3RhbCBkaXN0YW5jZSB0aGV5IHRyYXZlbGVkLiBMdWNraWx5LCB3ZSBoYXZlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBzdGFydGluZyBhbmQgZW5kaW5nIGxhdGl0dWRlcyBhbmQgbG9uZ2l0dWRlcy4gTGV0J3MgdXNlIHRob3NlIGZvdXIgY29sdW1ucyB0byBjcmVhdGUgYSBuZXcgY29sdW1uIG5hbWVkIGBkaXN0YW5jZWAuIAoKVGhlcmUgYXJlIG1hbnkgZGlmZmVyZW50IHdheXMgdG8gY2FsY3VsYXRlIGRpc3RhbmNlLiBXZSdsbCB3YWxrIHlvdSB0aHJvdWdoIHRoZSBzdHJhdGVneSB3ZSB1c2VkLiBIb3dldmVyLCBiZWZvcmUgZm9sbG93aW5nIGFsb25nIHdpdGggdXMsIGNoYWxsZW5nZSB5b3Vyc2VsZiB0byBzb2x2ZSB0aGlzIHByb2JsZW0gb24geW91ciBvd24gJm1kYXNoOyBvbmUgb2YgdGhlIGdvYWxzIG9mIHRoZXNlIG9mZiBwbGF0Zm9ybSBwcm9qZWN0cyBpcyB0byBnZXQgY29tZm9ydGFibGUgcHJvYmxlbSBzb2x2aW5nIG9uIHlvdXIgb3duLiBUcnkgdG8gdXNlIEdvb2dsZSB0byBmaW5kIHRoZSBwYWNrYWdlcyB5b3UgbWlnaHQgbmVlZCB0byBjYWxjdWxhdGUgdGhlIGRpc3RhbmNlIGJldHdlZW4gbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSBjb29yZGluYXRlcy4gVXNlIHRoZSBjb2RlIGJsb2NrIGJlbG93IHRvIHRyeSBzb2x2aW5nIHRoaXMgcHJvYmxlbSBvbiB5b3VyIG93bi4gV2UnbGwgd2FsayB5b3UgdGhyb3VnaCBvdXIgc29sdXRpb24gaW4gdGhlIGZvbGxvd2luZyBzZWN0aW9uLgoKQXMgeW91IHdyaXRlIHlvdXIgY29kZSB0aGF0IGVkaXRzIHRoZSBkYXRhIGZyYW1lLCBjb25zaWRlciBwcmludGluZyB0aGUgaGVhZCBvZiB0aGUgZGF0YSBmcmFtZSB0byB2YWxpZGF0ZSB0aGUgd29yayB5b3UgYXJlIGRvaW5nIQoKYGBge3J9CiMgVHJ5IGNyZWF0aW5nIGEgZGlzdGFuY2UgY29sdW1uIGluIHlvdXIgZGF0YSBmcmFtZSBoZXJlOgpsaWJyYXJ5KGdlb3NwaGVyZSkKCnN0YXJ0aW5nX3N0YXRpb25zIDwtIHNob3J0X3RyaXBzICU+JSBzZWxlY3Qoc3RhcnQuc3RhdGlvbi5sYXRpdHVkZSwgc3RhcnQuc3RhdGlvbi5sb25naXR1ZGUpCmVuZGluZ19zdGF0aW9ucyA8LSBzaG9ydF90cmlwcyAlPiUgc2VsZWN0KGVuZC5zdGF0aW9uLmxhdGl0dWRlLCBlbmQuc3RhdGlvbi5sb25naXR1ZGUpCgpzaG9ydF90cmlwcyA8LSBzaG9ydF90cmlwcyAlPiUgbXV0YXRlKGRpc3RhbmNlID0gZGlzdEhhdmVyc2luZShzdGFydGluZ19zdGF0aW9ucywgZW5kaW5nX3N0YXRpb25zKSkKCmhlYWQoc2hvcnRfdHJpcHMpCmBgYAoKClRoZXJlIGFyZSBtYW55IGRpZmZlcmVudCBzdHJhdGVnaWVzIHRvIGNhbGN1bGF0ZSB0aGUgZGlzdGFuY2UgYmV0d2VlbiB0d28gcG9pbnRzLiBUaGUgc2ltcGxlc3Qgd2F5IHRvIGRvIHRoaXMgd291bGQgYmUgdG8gZmluZCB0aGUgbGVuZ3RoIG9mIHRoZSBzdHJhaWdodCBsaW5lIGJldHdlZW4gdGhlIHR3byBwb2ludHMuIFRoaXMgaXMgYSBtYXNzaXZlIGFzc3VtcHRpb24gdG8gbWFrZSAmbWRhc2g7IGl0IHdvdWxkIGJlIHJlbWFya2FibGUgaWYgYW55IG9mIHRoZXNlIGJpa2UgdHJpcHMgdHJhdmVsZWQgaW4gYSBzdHJhaWdodCBsaW5lIGJldHdlZW4gdGhlIHR3byBwb2ludHMgd2l0aG91dCBtYWtpbmcgYW55IHR1cm5zIG9yIGN1cnZlcy4KClRoYXQgYmVpbmcgc2FpZCwgZmluZGluZyB0aGUgc3RyYWlnaHQgbGluZSBkaXN0YW5jZSBpcyBhIGdvb2Qgc3RhcnRpbmcgcG9pbnQuIFRoZSBgZGlzdEhhdmVyc2luZSgpYCBmdW5jdGlvbiBmb3VuZCBpbiB0aGUgYGdlb3NwaGVyZWAgbGlicmFyeSBjYW4gY2FsY3VsYXRlIHRoaXMgZGlzdGFuY2UuCgpGaXJzdCwgaW5zdGFsbCBhbmQgbG9hZCB0aGUgYGdlb3NwaGVyZWAgbGlicmFyeS4KCk5leHQsIHVzZSBgZHBseXJgJ3MgYHNlbGVjdCgpYCBmdW5jdGlvbiB0byBjcmVhdGUgdHdvIG5ldyBkYXRhIGZyYW1lcyB0aGF0IGNvbnRhaW4gb25seSB0aGUgbGF0aXR1ZGVzIGFuZCBsb25naXR1ZGVzIG9mIHRoZSBzdGFydGluZyBhbmQgZW5kaW5nIHBvaW50cy4gV2UgY2FsbGVkIHRoZXNlIGRhdGEgZnJhbWVzIGBzdGFydGluZ19zdGF0aW9uc2AgYW5kIGBlbmRpbmdfc3RhdGlvbnNgLgoKRmluYWxseSwgdXNlIGBkcGx5cmAncyBgbXV0YXRlKClgIGZ1bmN0aW9uIHRvIGFkZCBhIGNvbHVtbiBuYW1lZCBgZGlzdGFuY2VgIHRvIHlvdXIgZGF0YS4gYGRpc3RhbmNlYCBzaG91bGQgYmUgY2FsY3VsYXRlZCBieSBjYWxsaW5nIGBkaXN0SGF2ZXJzaW5lKClgIHVzaW5nIGBzdGFydGluZ19zdGF0aW9uc2AgYW5kIGBlbmRpbmdfc3RhdGlvbnNgIGFzIHBhcmFtZXRlcnMuCgpJZiB5b3UgZ2V0IHN0dWNrLCB1c2UgYD9kaXN0SGF2ZXJzaW5lYCB0byBjaGVjayB0aGUgZG9jdW1lbnRhdGlvbiB0byBzZWUgbW9yZSBleGFtcGxlcyEgWW91IGNhbiBhbHNvIHVzZSB0aGUgZG9jdW1lbnRhdGlvbiB0byBmaW5kIHRoZSB1bml0cyBvZiB0aGUgcmVzdWx0IG9mIGBkaXN0SGF2ZXJzaW5lKClgIQoKYGBge3J9CiMgVXNlIHRoZSBnZW9zcGhlcmUgbGlicmFyeSB0byBjcmVhdGUgYSBkaXN0YW5jZSBjb2x1bW4KCmBgYAoKIyMjIE1vZGlmeWluZyB0aGUgRGF0YSBGcmFtZTogU3BlZWQKCk5vdyB0aGF0IHdlJ3ZlIG1hZGUgYSBjb2x1bW4gY29udGFpbmluZyB0aGUgZGlzdGFuY2Ugb2YgZWFjaCB0cmlwLCBsZXQncyBtYWtlIGFub3RoZXIgY29sdW1uIGNvbnRhaW5pbmcgdGhlIGF2ZXJhZ2Ugc3BlZWQgb2YgZWFjaCB0cmlwLiBUaGlzIGNvbHVtbiBzaG91bGQgYmUgZWFzaWVyIHRvIGNyZWF0ZSB0aGFuIHRoZSBwcmV2aW91cyAmbWRhc2g7IHNwZWVkIGNhbiBiZSBjYWxjdWxhdGVkIGJ5IGRpdmlkaW5nIHRoZSBgZGlzdGFuY2VgIGNvbHVtbiBieSB0aGUgYHRyaXBkdXJhdGlvbmAgY29sdW1uLiBUaGlzIHdpbGwgZ2l2ZSB1cyB0aGUgYXZlcmFnZSBzcGVlZCBpbiBtZXRlcnMgcGVyIHNlY29uZC4gVXNlIHRoZSBgbXV0YXRlKClgIGZ1bmN0aW9uIHRvIG1ha2UgdGhlIGBzcGVlZGAgY29sdW1uIQoKYGBge3J9CiMgQ3JlYXRlIHRoZSBzcGVlZCBjb2x1bW4Kc2hvcnRfdHJpcHMgPC0gc2hvcnRfdHJpcHMgJT4lIG11dGF0ZShhdmdfc3BlZWQgPSBkaXN0YW5jZSAvIHRyaXBkdXJhdGlvbikKaGVhZChzaG9ydF90cmlwcykKCmBgYAoKIyMjIE1vZGlmeWluZyB0aGUgRGF0YSBGcmFtZTogQXZlcmFnZSBTcGVlZCBieSBBZ2UKCldlJ3JlIGFsbW9zdCB0aGVyZSEgTm93IHRoYXQgd2UgaGF2ZSB0aGUgc3BlZWQgb2YgZXZlcnkgYmlrZSB0cmlwLCB3ZSB3YW50IHRvIGdyb3VwIHRob3NlIHRyaXBzIGJ5IGBhZ2VgIGFuZCBmaW5kIHRoZSBhdmVyYWdlIHNwZWVkIG9mIGVhY2ggYWdlLiAKCkRvIHRoaXMgYnkgcGlwaW5nIHlvdXIgZGF0YSBmcmFtZSBpbnRvIHRoZSBgZ3JvdXBfYnkoKWAgZnVuY3Rpb24gdXNpbmcgYGFnZWAgYXMgYSBwYXJhbWV0ZXIuIAoKVGhlbiBwaXBlIHRoZSByZXN1bHQgb2YgdGhhdCBmdW5jdGlvbiBpbnRvIHRoZSBgc3VtbWFyaXplKClgIGZ1bmN0aW9uLiBgc3VtbWFyaXplKClgIHdvcmtzIHNpbWlsYXJseSB0byBgbXV0YXRlKClgICZtZGFzaDsgcGFzcyBgbWVhbl9zcGVlZCA9IG1lYW4oc3BlZWQpYCB0byB0aGUgYHN1bW1hcml6ZSgpYCBmdW5jdGlvbiB0byBjcmVhdGUgYSBuZXcgY29sdW1uIG5hbWVkIGBtZWFuX3NwZWVkYC4gU2F2ZSB0aGlzIG5ldyBkYXRhIGZyYW1lIGluIGEgdmFyaWFibGUgY2FsbGVkIGBhdmVyYWdlX3NwZWVkX2J5X2FnZWAuCgoKYGBge3J9CiMgVXNlIGdyb3VwX2J5KCkgYW5kIHN1bW1hcml6ZSgpIHRvIGdldCB0aGUgbWVhbiBzcGVlZCBvZiBlYWNoIGFnZQphdmVyYWdlX3NwZWVkX2J5X2FnZSA8LSBzaG9ydF90cmlwcyAlPiUgZ3JvdXBfYnkoYWdlKSAlPiUgc3VtbWFyaXplKG1lYW5fc3BlZWQgPSBtZWFuKGF2Z19zcGVlZCkpCmhlYWQoYXZlcmFnZV9zcGVlZF9ieV9hZ2UpCgpgYGAKCiMjIyBWaXN1YWxpemF0aW9uIQoKV2UgbWFkZSBpdCEgV2Ugbm93IGhhdmUgdGhlIGF2ZXJhZ2Ugc3BlZWQgb2YgZXZlcnkgYWdlIGluIG91ciBkYXRhc2V0LiBMZXQncyB1c2UgYGdncGxvdDJgIHRvIG1ha2UgYSBsaW5lIGdyYXBoIHRvIHNlZSBpZiB5b3VuZ2VyIHBlb3BsZSByZWFsbHkgZG8gYmlrZSBmYXN0ZXIuIE1ha2Ugc3VyZSB0byBpbnN0YWxsIGFuZCBsb2FkIGBnZ3Bsb3QyYCBpZiB5b3UgaGF2ZW4ndCBkb25lIHNvIGFscmVhZHkuIFBhc3MgeW91ciBkYXRhIGZyYW1lIHRvIGBnZ3Bsb3QoKWAgYW5kIGFkZCBhIGBnZW9tX2xpbmUoKWAuIGBnZW9tX2xpbmUoKWAgc2hvdWxkIGNvbnRhaW4gYW4gYWVzdGhldGljIHdoZXJlIGB4ID0gYWdlYCBhbmQgYHkgPSBtZWFuX3NwZWVkYC4KCmBgYHtyfQojIEluc3RhbGwgYW5kIGxvYWQgZ2dwbG90MiB0byBjcmVhdGUgYSBsaW5lIGdyYXBoIG9mIGFnZSBhbmQgbWVhbiBzcGVlZApsaW5lX2dyYXBoIDwtIGdncGxvdChhdmVyYWdlX3NwZWVkX2J5X2FnZSkgKyBnZW9tX2xpbmUoYWVzKGFnZSwgbWVhbl9zcGVlZCkpIApsaW5lX2dyYXBoCmBgYAoKTmljZSB3b3JrISBPdXIgaW50dWl0aW9uIHNlZW1zIHRvIGJlIHJpZ2h0ICZtZGFzaDsgdGhlcmUncyBhIHN0ZWFkeSBkcm9wIGluIHNwZWVkIHVudGlsIHdlIGhpdCBzb21lIG91dGxpZXJzLiBJdCB3b3VsZCBiZSBwcmV0dHkgc3VycHJpc2luZyB0byBzZWUgc29tZW9uZSBvdmVyIHRoZSBhZ2Ugb2YgMTAwIHVzaW5nIGEgYmlrZSBzaGFyZSBwcm9ncmFtISBMZXQncyBmaWx0ZXIgdGhlIGRhdGEgdG8gb25seSBzaG93IGFnZXMgbGVzcyB0aGFuIDgwIGFuZCByZWRyYXcgb3VyIHZpc3VhbGl6YXRpb24uCgpgYGB7cn0KIyBGaWx0ZXIgdGhlIGRhdGEgZnJhbWUgdG8gb25seSBjb250YWluIHJvd3Mgd2hlcmUgdGhlIGFnZSBpcyBsZXNzIHRoYW4gODAKdW5kZXJfODAgPC0gYXZlcmFnZV9zcGVlZF9ieV9hZ2UgJT4lIGZpbHRlcihhZ2UgPCA4MCkKbGluZV9ncmFwaF8yIDwtIGdncGxvdCh1bmRlcl84MCkgKyBnZW9tX2xpbmUoYWVzKGFnZSwgbWVhbl9zcGVlZCkpIApsaW5lX2dyYXBoXzIKYGBgCgpUaGF0IGxvb2tzIGEgYml0IGJldHRlciEgTGV0J3MgZG8gc29tZSB3b3JrIHRvIG1ha2Ugb3VyIGdyYXBoIGxvb2sgYSBiaXQgbW9yZSBwcm9mZXNzaW9uYWwuIEFkZCBhIHRpdGxlIGFuZCBheGlzIGxhYmVscy4gV2UgYWxzbyBjZW50ZXJlZCBvdXIgdGl0bGUhCgpgYGB7cn0KIyBBZGQgYSB0aXRsZSBhbmQgbGFiZWwgdGhlIGF4ZXMKbGluZV9ncmFwaF8yICsgbGFicyh0aXRsZSA9ICJBdmVyYWdlIFNwZWVkIGJhc2VkIG9uIEFnZSBmb3IgSmFuYXVhcnkiLCB4ID0gIkFnZSIsIHkgPSAiQXZlcmFnZSBTcGVlZCAobS9zKSIpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCmBgYAoKIyMjIEZpbHRlcmluZyBCeSBHZW5kZXIKCkdyZWF0IHdvcmshIFRoaXMgdmlzdWFsaXphdGlvbiBnaXZlcyB1cyBzb21lIGdyZWF0IGluc2lnaHRzIG9uIGhvdyBDaXRpIEJpa2UgdXNlcnMgYXJlIHVzaW5nIHRoZWlyIGJpa2VzLiBMZXQncyBkaXZlIGV2ZW4gZGVlcGVyISBXZSBjYW4gZ3JvdXAgb3VyIGRhdGEgYnkgbW9yZSB0aGFuIG9uZSBmZWF0dXJlLiAKCkZpbmQgeW91ciBsaW5lIG9mIGNvZGUgdGhhdCBncm91cGVkIG91ciBkYXRhIGJ5IGBhZ2VgLiBDb3B5IGl0LCBidXQgYWRkIGBnZW5kZXJgIGFzIGEgcGFyYW1ldGVyIHRvIHRoZSBgZ3JvdXBfYnkoKWAgZnVuY3Rpb24uIFNhdmUgdGhlIHJlc3VsdCBpbiBhIGRhdGEgZnJhbWUgbmFtZWQgYGF2ZXJhZ2Vfc3BlZWRfYnlfYWdlX2FuZF9nZW5kZXJgLiBJbnNwZWN0IHRoaXMgZGF0YSBmcmFtZSB0byBzZWUgd2hhdCBpdCBjb250YWlucy4KCgpgYGB7cn0KIyBVc2UgZ3JvdXBfYnkoKSBhZ2FpbiB0byBncm91cCBieSBib3RoIGFnZSBhbmQgZ2VuZGVyCmF2ZXJhZ2Vfc3BlZWRfYnlfYWdlIDwtIHNob3J0X3RyaXBzICU+JSBncm91cF9ieShhZ2UsIGdlbmRlcikgJT4lICBzdW1tYXJpemUobWVhbl9zcGVlZCA9IG1lYW4oYXZnX3NwZWVkKSkKaGVhZChhdmVyYWdlX3NwZWVkX2J5X2FnZSkKCmBgYAoKTGV0J3Mgbm93IHZpc3VhbGl6ZSB0aGUgZGlmZmVyZW5jZSBpbiBhdmVyYWdlIHNwZWVkIGJ5IGFnZSBfYW5kXyBnZW5kZXIuIE5vdGUgdGhhdCBpZiB5b3UgbG9vayBpbiB0aGUgZG9jdW1lbnRhdGlvbiBmb3IgdGhlIGRhdGEsIGEgYDBgIHJlcHJlc2VudHMgYSB1c2VyIHRoYXQgZGlkbid0IHNwZWNpZnkgdGhlaXIgZ2VuZGVyIGFzIG1hbGUgb3IgZmVtYWxlLCBhIGAxYCByZXByZXNlbnRzIGEgdXNlciBpZGVudGlmeWluZyBhcyBtYWxlLCBhbmQgYSBgMmAgcmVwcmVzZW50cyBhIHVzZXIgaWRlbnRpZnlpbmcgYXMgZmVtYWxlLgoKVGhlIHByZXZpb3VzIGNhbGwgdG8gYGdncGxvdCgpYCBhbmQgYGdlb21fbGluZSgpYCBzaG91bGQgYmUgY2xvc2UgdG8gd2hhdCB3ZSB3YW50LiBBZGQgdGhlIHBhcmFtZXRlciBgY29sb3IgPSBnZW5kZXJgIHRvIHRoZSBhZXN0aGV0aWMgaW4gYGdlb21fbGluZSgpYC4gTWFrZSBzdXJlIHlvdSB1c2UgdGhlIG5ldyBkYXRhIGNvbnRhaW5pbmcgdGhlIGdlbmRlciBpbmZvcm1hdGlvbiEgWW91IG9uY2UgYWdhaW4gbWF5IHdhbnQgdG8gZmlsdGVyIG91dCB0aGUgYWdlcyBncmVhdGVyIHRoYW4gODAuCgpOb3RlIHRoYXQgdGhpcyBncmFwaCB3b24ndCBxdWl0ZSBiZSB3aGF0IHdlIHdhbnQgeWV0LCBidXQgd2UncmUgZ2V0dGluZyBjbG9zZSEKCmBgYHtyfQojIE1ha2UgYSBsaW5lIGdyYXBoIG9mIHlvdXIgbmV3IGZpbHRlcmVkIGRhdGEgZnJhbWUKbmV3X2xpbmVfZ3JhcGggPC0gZ2dwbG90KGF2ZXJhZ2Vfc3BlZWRfYnlfYWdlKSArIGdlb21fbGluZShhZXMoYWdlLCBtZWFuX3NwZWVkLCBjb2xvciA9IGdlbmRlcikpICsgbGFicyh0aXRsZSA9ICJBdmVyYWdlIFNwZWVkIGJhc2VkIG9uIEFnZSBmb3IgSmFuYXVhcnkgYnkgR2VuZGVyIiwgeCA9ICJBZ2UiLCB5ID0gIkF2ZXJhZ2UgU3BlZWQgKG0vcykiKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpuZXdfbGluZV9ncmFwaAoKYGBgCgpJdCdzIGEgYml0IGhhcmQgdG8gdGVsbCB3aGF0IGlzIGhhcHBlbmluZyBpbiB0aGF0IGdyYXBoICZtZGFzaDsgYnV0IG9uZSBvZGRpdHkgdGhhdCBzdGlja3Mgb3V0IGlzIHRoZSBzY2FsZSB1c2VkIGZvciBnZW5kZXIuIFdlIGtub3cgdGhhdCBvdXIgZ2VuZGVyIGRhdGEgaXMgcmVwcmVzZW50ZWQgYXMgdGhyZWUgZGlzdGluY3QgdmFsdWVzICZtZGFzaDsgYDBgLCBgMWAsIGFuZCBgMmAuIEhvd2V2ZXIsIGBnZ3Bsb3QoKWAgaXMgdXNpbmcgZ2VuZGVyIGFzIGEgY29udGludW91cyB2YXJpYWJsZS4gCgpXZSBjYW4gdHVybiB0aGlzIGNvbHVtbiBpbnRvIGEgZmFjdG9yIGJ5IHVzaW5nIHRoZSBgYXMuZmFjdG9yKClgIGFuZCBgbXV0YXRlKClgIGZ1bmN0aW9ucy4gUGlwZSB5b3VyIGRhdGEgZnJhbWUgaW50byB0aGUgYG11dGF0ZSgpYCBmdW5jdGlvbiBhbmQgdXNlICBgZ2VuZGVyID0gYXMuZmFjdG9yKGdlbmRlcilgIGFzIHRoZSBwYXJhbWV0ZXIuCgpOb3RlIHRoYXQgd2hlbiB5b3UgbWFrZSB0aGlzIGNvbHVtbiBhIGZhY3RvciwgeW91IHdpbGwgc2VlIGEgbnVtYmVyIG9mIHdhcm5pbmdzLiBUaGlzIHdhcm5pbmcgaXMgdGVsbGluZyB5b3UgdGhhdCB0aGUgdHlwZSBvZiB0aGUgdmFsdWVzIGluIHRoZSBnZW5kZXIgY29sdW1uIGhhdmUgYmVlbiBjaGFuZ2VkIGZyb20gaW50ZWdlcnMgdG8gY2hhcmFjdGVycy4KClRoZW4gcmVkcmF3IHlvdXIgZ3JhcGguCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQojIFVzZSBtdXRhdGUoKSBhbmQgYXMuZmFjdG9yKCkgdG8gY2hhbmdlIHRoZSBnZW5kZXIgY29sdW1uIGludG8gYSBmYWN0b3IuCmF2ZXJhZ2Vfc3BlZWRfYnlfYWdlIDwtIGF2ZXJhZ2Vfc3BlZWRfYnlfYWdlICU+JSBtdXRhdGUoZ2VuZGVyID0gYXMuZmFjdG9yKGdlbmRlcikpICsgCmhlYWQoYXZlcmFnZV9zcGVlZF9ieV9hZ2UpCmdncGxvdChhdmVyYWdlX3NwZWVkX2J5X2FnZSkgKyBnZW9tX2xpbmUoYWVzKGFnZSwgbWVhbl9zcGVlZCwgY29sb3IgPSBnZW5kZXIpKQpgYGAKCk5pY2Ugd29yayEgV2UgY2FuIG5vdyBzZWUgdGhlIGF2ZXJhZ2Ugc3BlZWRzIGJ5IGFnZSBicm9rZW4gaW50byB0aGUgMyBnZW5kZXJzIENpdGkgQmlrZSBhY2NvdW50cyBmb3IuIFlvdSBjYW4gc2VlIHRoYXQgbWFsZS1pZGVudGlmeWluZyB1c2VycyB0eXBpY2FsbHkgYmlrZSBmYXN0ZXIgdGhhbiBmZW1hbGUtaWRlbnRpZnlpbmcgdXNlcnMuIFVzZXJzIHdpdGggYW4gdW5rbm93biBnZW5kZXIgZG9uJ3QgZm9sbG93IGEgc3BlY2lmaWMgcGF0dGVybiAmbWRhc2g7IGl0IGlzIGxpa2VseSB0aGF0IHRoZXJlIGlzbid0IGVub3VnaCBkYXRhIHRvIHByb3Blcmx5IHZpc3VhbGl6ZSB0aG9zZSB1c2Vycy4KCkZvciBvdXIgZmluYWwgdmVyc2lvbiBvZiB0aGlzIGdyYXBoLCB3ZSBmaWx0ZXJlZCBvdXQgdGhlIHVzZXJzIHdpdGggYSBnZW5kZXIgb2YgYDBgLCBhbmQgd2UgbGFiZWxlZCB0aGUgbGluZXMgYXMgIk1hbGUgSWRlbnRpZnlpbmciIGFuZCAiRmVtYWxlIElkZW50aWZ5aW5nIiB1c2luZyB0aGUgYHNjYWxlX2NvbG9yX2Rpc2NyZXRlKClgIGZ1bmN0aW9uLiBUYWtlIGEgbG9vayBhdCB0aGUgZG9jdW1lbnRhdGlvbiBmb3IgdGhpcyBmdW5jdGlvbiB1c2luZyBgP3NjYWxlX2NvbG9yX2Rpc2NyZXRlYCB0byBjaGFuZ2UgdGhlIGxhYmVsIG9mIGVhY2ggbGluZS4KCmBgYHtyfQojIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWUgPSAiR2VuZGVyIiwgbGFiZWxzID0gYygiTWFsZSBJZGVudGlmeWluZyIsICJGZW1hbGUgSWRlbnRpZnlpbmciKSkKIyBGaWx0ZXIgdGhlIGRhdGEgZnJhbWUgdG8gb25seSBpbmNsdWRlIGdlbmRlcnMgMSBhbmQgMi4gU2V0IGFwcHJvcHJpYXRlIGxhYmVscyBmb3IgdGhlIGxlZ2VuZAphdmVyYWdlX3NwZWVkX2J5X2FnZSA8LSBhdmVyYWdlX3NwZWVkX2J5X2FnZSAlPiUgZmlsdGVyKGdlbmRlciA9PSAxIHwgZ2VuZGVyID09IDIsIGFnZSA8IDgwKQpoZWFkKGF2ZXJhZ2Vfc3BlZWRfYnlfYWdlKQpnZ3Bsb3QoYXZlcmFnZV9zcGVlZF9ieV9hZ2UpICsgZ2VvbV9saW5lKGFlcyhhZ2UsIG1lYW5fc3BlZWQsIGNvbG9yID0gZ2VuZGVyKSkgKyBsYWJzKHRpdGxlID0gIkF2ZXJhZ2UgU3BlZWQgYmFzZWQgb24gQWdlIGZvciBKYW5hdWFyeSBieSBHZW5kZXIiLCB4ID0gIkFnZSIsIHkgPSAiQXZlcmFnZSBTcGVlZCAobS9zKSIpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsgc2NhbGVfY29sb3JfZGlzY3JldGUobmFtZSA9ICJHZW5kZXIiLCBsYWJlbHMgPSBjKCJNYWxlIElkZW50aWZ5aW5nIiwgIkZlbWFsZSBJZGVudGlmeWluZyIpKQpgYGAKCiMjIyBNYWtpbmcgYSBTdGFja2VkIEJhciBQbG90IE9mIEFnZXMKCkxldCdzIG1ha2Ugb25lIGZpbmFsIGdyYXBoLiBGb3IgdGhpcyBncmFwaCwgd2UncmUgaW50ZXJlc3RlZCBpbiBzZWVpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBDaXRpIEJpa2UgdXNlcnMnIGFnZSBhbmQgZ2VuZGVyLiBMZXQncyB1c2UgYSBzdGFja2VkIGJhciBwbG90IHRvIGRvIHRoaXMuIFdlJ2xsIHdhbnQgdG8gY3JlYXRlIGEgYmFyIHBsb3Qgd2hlcmUgYWdlIGlzIG9uIHRoZSB4IGF4aXMsIGNvdW50IGlzIG9uIHRoZSB5IGF4aXMsIGFuZCBlYWNoIGJhciBpcyBzcGxpdCBpbnRvIGdlbmRlcnMuCgpMZXQncyBzdGFydCBieSB1c2luZyBvdXIgYHNob3J0X3RyaXBzYCBkYXRhc2V0LiBXZSdsbCB3YW50IHRvIGNhbGwgYGdyb3VwX2J5KClgIHVzaW5nIHRoaXMgZGF0YXNldCBhbmQgcGlwZSB0aGUgcmVzdWx0IHRvIGB0YWxseSgpYC4gVGhpcyB3aWxsIGxldCB1cyBnZXQgYSBjb3VudCBvZiB0aGUgbnVtYmVyIG9mIGJpa2VycyBmb3IgZWFjaCBhZ2UgYW5kIGdlbmRlci4gV2Ugc2F2ZWQgdGhpcyBuZXcgZGF0YSBmcmFtZSBpbiBhIHZhcmlhYmxlIGNhbGxlZCBgYWdlX2NvdW50c2AuCgpgYGB7cn0KIyBDcmVhdGUgdGhlIGFnZV9jb3VudHMgZGF0YSBmcmFtZQphZ2VfY291bnRzIDwtIHNob3J0X3RyaXBzICU+JSBncm91cF9ieShhZ2UsIGdlbmRlcikgJT4lIHRhbGx5IAphZ2VfY291bnRzCmBgYAoKSWYgeW91IGxvb2sgYXQgdGhlIGhlYWQgb2YgdGhpcyBuZXcgZGF0YSBmcmFtZSwgeW91J2xsIHNlZSB0aGUgY291bnRzIGFyZSBzdG9yZWQgaW4gYSBjb2x1bW4gbmFtZWQgYG5gLiBMZXQncyBub3cgdXNlIGBnZ3Bsb3QoKWAgYW5kIGBnZW9tX2NvbCgpYCB0byBjcmVhdGUgYSBzdGFja2VkIGJhciBwbG90LiBgZ2dwbG90KClgIHNob3VsZCBoYXZlIGFuIGBhZXMoKWAgd2hlcmUgYHggPSBhZ2VgLCBgeSA9IG5gIGFuZCBgZmlsbCA9IGdlbmRlcmAuCgpgYGB7cn0KIyBDcmVhdGUgdGhlIHN0YWNrZWQgYmFyIHBsb3QKc3RhY2tlZF9wbG90IDwtIGFnZV9jb3VudHMgJT4lIGdncGxvdChhZXMoYWdlLCBuLCBmaWxsID0gZ2VuZGVyKSkgKyBnZW9tX2NvbCgpCnN0YWNrZWRfcGxvdApgYGAKCkdyZWF0ISBUaGVyZSBhcmUgc29tZSB0d2Vha3MgdGhhdCB3ZSBtaWdodCB3YW50IHRvIG1ha2UgdG8gdGhpcyBncmFwaC4gRmlyc3QsIGdlbmRlciByaWdodCBub3cgaXMgcmVwcmVzZW50ZWQgYXMgYW4gaW50ZWdlci4gSXQgd2lsbCBtYWtlIG1vcmUgc2Vuc2UgaWYgdGhhdCBjb2x1bW4gaXMgcmVwcmVzZW50ZWQgYXMgYSBmYWN0b3IuIFRvIGRvIHRoaXMsIHdlIGNhbiBwYXNzIGBhcy5mYWN0b3IoZ2VuZGVyKWAgYXMgdGhlIHZhbHVlIGZvciB0aGUgeCBheGlzLgoKTmV4dCwgaXQgbG9va3MgbGlrZSB3ZSBoYXZlIHNvbWUgdW51c3VhbCBkYXRhIGFyb3VuZCB0aGUgYWdlIG9mIDUwLiBJdCBsb29rcyBsaWtlIHRoZXJlIGFyZSBhIHRvbiBvZiBiaWtlcnMgd2l0aCBhbiB1bmtub3duIGdlbmRlciBhdCB0aGF0IGFnZS4gVGhpcyBtaWdodCBiZSBzb21ldGhpbmcgd2Ugd2FudCB0byBkaWcgaW50byBhIGJpdCBtb3JlLCBidXQgZm9yIG5vdywgbGV0J3MgZmlsdGVyIG91dCB0aGUgYmlrZXJzIHdpdGggYSBnZW5kZXIgb2YgYDBgLiBXZSBhbHNvIGZpbHRlcmVkIG91dCBiaWtlcnMgd2l0aCBhbiBhZ2Ugb3ZlciBgMTAwYCAmbWRhc2g7IHRoYXQgc2VlbXMgbGlrZSBhbiBlcnJvciBpbiBkYXRhIGNvbGxlY3Rpb24gYXMgd2VsbC4KCkZpbmFsbHksIHdlIGxhYmVsZWQgYW5kIHRpdGxlZCBvdXIgZ3JhcGggdXNpbmcgYGxhYnMoKWAgYW5kIGBzY2FsZV9maWxsX2Rpc2NyZXRlKClgCgpgYGB7cn0KIyBGaWx0ZXIgYW5kIGxhYmVsIHlvdXIgZ3JhcGgKc3RhY2tlZF9wbG90X2ZpbHRlcmVkIDwtIGFnZV9jb3VudHMgJT4lIGZpbHRlcihnZW5kZXIgIT0gMCkgJT4lIGdncGxvdChhZXMoYWdlLCBuLCBmaWxsID0gYXMuZmFjdG9yKGdlbmRlcikpKSArIGdlb21fY29sKCkgKyBsYWJzKHRpdGxlID0gIkJpa2VycyBieSBHZW5kZXIgYW5kIEFnZSIsIHggPSAiQWdlIiwgeSA9ICJDb3VudCIpICsgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lID0gIkdlbmRlciIsIGxhYmVscyA9IGMoIk1hbGUiLCAiRmVtYWxlIikpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCnN0YWNrZWRfcGxvdF9maWx0ZXJlZApgYGAKCiMjIyBGdXJ0aGVyIFdvcmsKCkdyZWF0IHdvcmshIFlvdSd2ZSBtYWRlIHNldmVyYWwgZ3JhcGhzIHRoYXQgc2hvdyBhIHJlYWwgZGlmZmVyZW5jZSBpbiB0aGUgd2F5IGRpZmZlcmVudCBncm91cHMgb2YgQ2l0aSBCaWtlIHVzZXJzIGJpa2UuIFRoaXMgY291bGQgYmUgYSB2YWx1YWJsZSBhc3NldCBpbiBoZWxwaW5nIENpdGkgQmlrZSB1bmRlcnN0YW5kIGhvdyB0byBtYWtlIGJpa2UgcmlkaW5nIHNhZmVyIGluIE5ldyBZb3JrLiBIb3dldmVyLCB0aGVyZSBpcyBzbyBtdWNoIG1vcmUgeW91IGNhbiBkbyB3aXRoIHRoaXMgZGF0YSEKClRvIGJlZ2luLCB0aGVyZSBhcmUgc29tZSBtYWpvciBmbGF3cyBpbiB0aGUgd2F5IHdlIGNhbGN1bGF0ZWQgdGhlIHNwZWVkLiBTcGVjaWZpY2FsbHksIHdlIG1hZGUgc29tZSBfaHVnZV8gYXNzdW1wdGlvbnMgd2hlbiBjYWxjdWxhdGluZyB0aGUgZGlzdGFuY2Ugb2YgZWFjaCBiaWtlIHJpZGUuIEluc3RlYWQgb2YgY2FsY3VsYXRpbmcgdGhlIHN0cmFpZ2h0IGxpbmUgZGlzdGFuY2UgdXNpbmcgdGhlIGdlb3NwaGVyZSBsaWJyYXJ5LCB3ZSBjb3VsZCB0YWtlIGFkdmFudGFnZSBvZiBhIHNlcnZpY2UgbGlrZSBHb29nbGUgTWFwJ3MgQVBJIHRvIGdldCBhIG1vcmUgYWNjdXJhdGUgbWVhc3VyZW1lbnQgb2YgZGlzdGFuY2UuIElmIHlvdSdyZSBpbnRlcmVzdGVkIGluIGxvb2sgbW9yZSBpbnRvIHRoaXMgcHJvYmxlbSwgaW52ZXN0aWdhdGUgZ2V0dGluZyBhIFtHb29nbGUgTWFwcyBBUEkga2V5XShodHRwczovL2RldmVsb3BlcnMuZ29vZ2xlLmNvbS9tYXBzL2RvY3VtZW50YXRpb24vZ2VvY29kaW5nL2dldC1hcGkta2V5KSBhbmQgdXNpbmcgYSBsaWJyYXJ5IGxpa2UgW2dtYXBzZGlzdGFuY2VdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9nbWFwc2Rpc3RhbmNlL2dtYXBzZGlzdGFuY2UucGRmKS4KCkFub3RoZXIgZ3JlYXQgd2F5IHRvIGV4dGVuZCB0aGlzIHByb2plY3QgaXMgdG8gaW52ZXN0aWdhdGUgb3RoZXIgZGF0YSB0aGF0IENpdGkgQmlrZSBtYWtlcyBhdmFpbGFibGUuIFdlIHVzZWQgZGF0YSBmb3VuZCBpbiB0aGUgX0NpdGkgQmlrZSBUcmlwIEhpc3Rvcmllc18gc2VjdGlvbiBvbiB0aGUgW1N5c3RlbSBEYXRhXShodHRwczovL3d3dy5jaXRpYmlrZW55Yy5jb20vc3lzdGVtLWRhdGEpIHBhZ2UuIE9uIHRoZSBTeXN0ZW0gRGF0YSBwYWdlLCB5b3UgY2FuIGZpbmQgZGlmZmVyZW50IGRhdGFzZXQsIGluY2x1ZGluZyBpbmZvcm1hdGlvbiBhYm91dCBtZW1iZXJzaGlwIGRhdGEgYW5kIHJlYWwgdGltZSBzdGF0aW9uIGRhdGEuIFlvdSBjb3VsZCB1c2UgdGhpcyByZWFsIHRpbWUgZGF0YSB0byB0cmFjayBob3cgdGhlIGZsb3cgb2YgYmlrZXMgY2hhbmdlcyBvdmVyIHRoZSBjb3Vyc2Ugb2YgdGhlIGRheS4gWW91IGNvdWxkIGludmVzdGlnYXRlIGhvdyB0aGUgd2VhdGhlciBpbXBhY3RzIG1lbWJlcnNoaXAuIFdlIHdvdWxkIGxvdmUgdG8gc2VlIGFueSBncmFwaHMgb3IgaW5zaWdodHMgeW91IHByb2R1Y2Uh