The Carnegie Library of Pittsburgh has released a variety of data related to self-reported summer reading behavior. In the following exploratory analysis I will investigate a subset of this data.
This subset describes the number of times a given book has been read by logged time and location. For instance, a child reading a book 3 times before bed will show up as a single data point of 3 reads with a time corresponding to when they (or a parent) entered this data into the system.
For the sake of preserving patron privacy CLPGH decided not to include the actual book titles with this particular subset of data, as it could be used to deanonymize users.
My exploration of the data resulted in discovering the following topics:
- Which days people read most
- The number of times books are read
- Reading trends per library over time
- Consistency in reading trends
- Most popular library locations
- Typical logging hours
Introducing the Data
Let’s begin by examining what data we have. Here are the first few entries:
head(ts)
log_timestamp
: When the patron submitted their reading information. Note that this is not the time when they read the book.
date_read
: Patrons can optionally provide a different date that they read the book from when they submitted their information. However, it does not contain a time.
times_read
: The number of times the book was read. If a book was read for a group, each member of the group counts as one read.
library
: The patron’s home library, even if the book was obtained through another branch.
hour_rounded
: A column that I created which has the contents of log_timestap
, but without a date and rounded to the nearest hour.
Most Common Reading Days
When looking at the data it’s useful to think about columns that might be related to one another. We have a date_read
column along with times_read
indicating the number of times the book was read per log. Using R, we can convert date_read
from dates into a new column of weekdays. From there we can see which days people tend to read most often.
ts$weekday <- weekdays(ts$date_read)
# Convert to factor in order to impart a manual ordering
ts$weekday <- factor(ts$weekday, levels = c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"))
weekday_summary <- summarise(group_by(ts, weekday),
reads = sum(times_read))
ggplot(na.omit(weekday_summary), aes(weekday, reads)) +
geom_point() +
geom_line(color="blue", alpha=0.2, aes(group=1)) +
ggtitle("Reading by Weekday")

Surprisingly, it’s more common to read on Tuesday through Thursday than on the weekend. Note that the light blue line is intended only to guide your eye to the changes in each black data point; we don’t have values for the time between each weekday.
Number of Times Read
Let’s get an idea of the typical amount of times books are read per log.
ggplot(ts, aes(times_read)) +
geom_histogram(binwidth = 1) +
xlim(0, 20) +
ggtitle("Count of Times Read")

I’ve limited the range of times_read and lost a few outliers, but we can clearly see that most books are only read once per logged event.
However, we can get a better idea of the range of values per library using a boxplot. A boxplot gives us an easy way to see the median and range of values for a category.
sample_data <- data.frame(value=rnorm(100, mean=10, sd=3), type=c("sample1", "sample2"))
ggplot(sample_data, aes(type, value)) +
geom_boxplot() +
ggtitle("Sample Boxplots")

In the above sample boxplots, the bold line indicates the median of the data. There are as many values that fall between the bottom horizontal line and the median as there are that fall between the median and the top horizontal line.
The vertical line indicates the total range of values, with any unusual outliers marked by solid dots (which you’ll encounter below).
Let’s take a look at our actual data.
# Omit data that has no library specified.
with_library <- ts[ts$library != "",]
boxplot_read_library <- ggplot(with_library, aes(library, times_read)) +
geom_boxplot() +
scale_x_discrete(label=function(x) abbreviate(x, minlength=14)) +
theme(axis.text.x = element_text(angle=45, hjust=1))
boxplot_read_library + ggtitle("Boxplot of Times Read by Library")

Wow, this looks strange! That’s because some libraries have some very high outliers marked by dots. Let’s zoom in a bit.
boxplot_read_library +
coord_cartesian(ylim = c(0,15)) +
ggtitle("Boxplot of Times Read by Library (max 15)")

This is a bit better. We can see that for almost every library we don’t even have a box to plot, since all non-outlier values are simply 1. This means that these libraries almost always have patrons who only read a book once per logged event; anything else is an unusual event marked by a dot.
However, there are three libraries that have a larger range of values where we can actually see a box. For the sake of brevity, I am only including an analysis of the Woods Run location.
Woods Run Times Read
count_times_read <- function(l) {
p <- ggplot(filter(ts, library == l), aes(times_read)) +
geom_histogram(binwidth = 1) +
ggtitle(sprintf("Times Read, %s", l))
return(p)
}
count_times_read("Woods Run") +
coord_cartesian(xlim=c(0,30), ylim=c(0,75))

For this library I’ve already zoomed in since the data point for single readings is so large (about 850-900). Despite that, Woods Run has some range of values for times_read
. Let’s see how the total number of reads per day changes over time.
Woods Run Reads Over Time and Reading Consistency
wr <- summarise(group_by(filter(ts, library == "Woods Run"), date_read),
reads = sum(times_read)
)
ggplot(wr, aes(date_read, reads)) +
geom_step() +
scale_x_date(date_minor_breaks = "1 day") +
ggtitle("Daily Reads over Time, Woods Run")

Two trends can be seen. First, there’s not much data prior to June 6th. Second, once we start to have more data the number of reads per day appears to vary significantly. Let’s get some statistics for Woods Run’s data starting at June 6th.
wr_june6 = filter(wr, date_read >= "2016-06-06")
summary(wr_june6$reads)
Min. 1st Qu. Median Mean 3rd Qu. Max.
5.00 36.75 80.50 94.33 121.20 282.00
Woods Run has a mean reads/day of 94.33, but the standard deviation is 76.55. If you’re not familiar with standard deviation, this is a measurement that represents how wide of a range the data vary across.
To illustrate this, look at the following. Here I’ve plotted two sets of data that have the same average value of 0; however, the red line has a standard deviation of 1 while the blue line has a standard deviation of 10.
sample_data2 <- data.frame(o=seq(1, 100),
a=rnorm(100, mean=0, sd=1),
b=rnorm(100, mean=0, sd=10))
ggplot(sample_data2) +
geom_line(aes(o, a, color="blue")) +
geom_line(aes(o, b, color="red")) +
scale_color_discrete(name="SD",
breaks=c("blue", "red"),
labels=c("1", "10")) +
theme(axis.title.x = element_blank(), axis.title.y = element_blank()) +
ggtitle("Mean of 0, Different Standard Deviations")

From this we can see that while the mean tells us where the line is generally centered upon, the standard deviation captures the spread of values.
A rule of thumb is that 68% of data falls within one standard deviation of the mean, 95% fall within two standard deviations, and 99% fall within three standard deviations.
Now back to our Woods Run data. Since the standard deviation of 76.55 is relatively large compared to the mean of 94.33, we do indeed have quite wide variability – and thus inconsistency – in reads/day for Woods Run.
In addition, we can look at the quartiles for the data. We can visually examine them using the familiar boxplot.
ggplot(wr_june6, aes(c("Woods Run"), reads)) +
theme(axis.title.x = element_blank()) +
coord_fixed(ratio = 0.01) +
geom_boxplot()

There are as many values contained between the first quartile (36.75) and the median (94.33) as there are between the median and the third quartile (121.20). Visually this may be difficult to see, so this is a case when looking at the actual numeric ranges may be more helpful: the range between the first quartile and median is greater than the median and third quartile.
One way to interpret this is that on days with less than the median number of reads we’re more likely to see a wider variety of read amounts, but on days with above the median number of reads, we are more likely to see a closer range of read amounts. However, we can’t establish any causal connections with this limited data – this is just observing a trend.
Most Popular Locations
So what are the most popular locations? A naive approach is to simply see which locations had the highest total number of reads.
reads_by_library <- summarise(group_by(ts[ts$library != "",], library),
reads = sum(times_read)
)
popular <- arrange(reads_by_library, desc(reads))
top_3 <- popular[1:3,]
ggplot(popular, aes(library, reads)) +
geom_bar(stat="identity") +
scale_x_discrete(label=function(x) abbreviate(x, minlength=14)) +
theme(axis.text.x = element_text(angle=45, hjust=1)) +
ggtitle("Total Summer Reads per Library")

top_3
It’s clear that Squirrel Hill is the most popular library followed by Oakland. Third place is difficult to identify visually, but looking at the numbers we see that it is Woods Run.
Reads/Day Across the Most Popular Locations
Being that these three are the most popular libraries, they provide us with a lot of data points to analyze. Let’s look at how total reads per day has varied over time across these libraries.
popular_data <- group_by(ts[ts$library %in% top_3$library,],
library,
date_read) %>%
summarise(reads = sum(times_read))
popular_trends <- ggplot(popular_data[popular_data$date_read >= "2016-06-06",],
aes(date_read, reads, color=library)) +
geom_point() +
geom_line() +
scale_x_date(date_minor_breaks = "1 day")
popular_trends + ggtitle("Reading Trends Across Most Popular Libraries")

We can see that Squirrel Hill has a huge spike on July 6th; for further analysis we may want to look into whether any special events occurred on that day. However, this data point makes the rest of the chart difficult to compare. As such, let’s zoom in.
popular_trends +
coord_cartesian(ylim = c(0,750)) +
ggtitle("Reading Trends Across Most Popular Libraries (max 750)")

From here we can see that even in the most popular libraries, the total number of reads per day over time is generally erratic.
Further analysis might want to look at measurements such as libraries with the most consistent number of reads (by using standard deviation). From there an experiment can be conducted to try out programs and see if they increase consistency and/or the total number of reads.
Typical Logging Times
The dataset also includes the time when each book was logged. Note that this is different from when the book was actually read. Let’s group these by hour and see what hours are most popular overall – earlier on I amended the data with a new column of values containing the time rounded to the nearest hour.
ggplot(ts, aes(x=hour_rounded)) +
geom_bar() +
theme(axis.text.x = element_text(angle=45, hjust=1)) +
ggtitle("Logging by Hour")

It looks like most logging occurs between 3PM-9PM, but it’s interesting that there are more log events around midnight than around noon when people might be on a lunch break.
Let’s compare this data across the most popular locations:
hours_by_location <- function(location) {
ggplot(filter(ts, library == location), aes(x=hour_rounded)) +
geom_bar() +
theme(axis.text.x = element_text(angle=45, hjust=1)) +
ggtitle(sprintf("Logging Hours: %s", location))
}
for (l in top_3$library) {
print(hours_by_location(l))
}



Although the count scale varies across these three libraries, the general shape is similar.
We can derive some practical insights from this data. Times with high logging counts would be good times to ensure that the libraries’ technical support departments are available for computer help. Also, if CLPGH’s IT department needs to perform maintenance on this system, they might not want to do it at a late hour – in fact, performing it at the start of the workday or even at noon may actually impact a lesser amount of patrons.
Conclusion
This was an exploration of only a small amount of the released summer reading data. However, we’ve been able to come up with some interesting avenues for further investigation:
- Which days people read the most
- Library popularity
- Reading behavior over time
- Consistency in reading behavior
- How often books are read multiple times
- Times for IT staffing and maintenance
- A variety of bases for measuring the effectiveness of future programs
That being said, there is one main challenge with this data:
- The data are self-reported and thus biased towards those who are inclined to put forth the effort to record their activity.
It would be interesting to try alternative methods of recording reading activity that don’t involve the user having access to a computer, particularly in low income areas. In addition, it’d be nice if users could enter a time range in which they read the book separate from when they log their reading (although users can optionally enter a separate date without a time).
LS0tDQp0aXRsZTogIkNhcm5lZ2llIExpYnJhcnkgUmVhZGluZyBEYXRhIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KVGhlIENhcm5lZ2llIExpYnJhcnkgb2YgUGl0dHNidXJnaCBoYXMgW3JlbGVhc2VkXShodHRwczovL2dpdGh1Yi5jb20vY2FybmVnaWVsaWJyYXJ5L3N1bW1lci1yZWFkaW5nLWRhdGEpIGEgdmFyaWV0eSBvZiBkYXRhIHJlbGF0ZWQgdG8gc2VsZi1yZXBvcnRlZCBzdW1tZXIgcmVhZGluZyBiZWhhdmlvci4gSW4gdGhlIGZvbGxvd2luZyBleHBsb3JhdG9yeSBhbmFseXNpcyBJIHdpbGwgaW52ZXN0aWdhdGUgYSBzdWJzZXQgb2YgdGhpcyBkYXRhLg0KDQpUaGlzIHN1YnNldCBkZXNjcmliZXMgdGhlIG51bWJlciBvZiB0aW1lcyBhIGdpdmVuIGJvb2sgaGFzIGJlZW4gcmVhZCBieSBsb2dnZWQgdGltZSBhbmQgbG9jYXRpb24uIEZvciBpbnN0YW5jZSwgYSBjaGlsZCByZWFkaW5nIGEgYm9vayAzIHRpbWVzIGJlZm9yZSBiZWQgd2lsbCBzaG93IHVwIGFzIGEgc2luZ2xlIGRhdGEgcG9pbnQgb2YgMyByZWFkcyB3aXRoIGEgdGltZSBjb3JyZXNwb25kaW5nIHRvIHdoZW4gdGhleSAob3IgYSBwYXJlbnQpIGVudGVyZWQgdGhpcyBkYXRhIGludG8gdGhlIHN5c3RlbS4NCg0KRm9yIHRoZSBzYWtlIG9mIHByZXNlcnZpbmcgcGF0cm9uIHByaXZhY3kgQ0xQR0ggZGVjaWRlZCBub3QgdG8gaW5jbHVkZSB0aGUgYWN0dWFsIGJvb2sgdGl0bGVzIHdpdGggdGhpcyBwYXJ0aWN1bGFyIHN1YnNldCBvZiBkYXRhLCBhcyBpdCBjb3VsZCBiZSB1c2VkIHRvIGRlYW5vbnltaXplIHVzZXJzLg0KDQpNeSBleHBsb3JhdGlvbiBvZiB0aGUgZGF0YSByZXN1bHRlZCBpbiBkaXNjb3ZlcmluZyB0aGUgZm9sbG93aW5nIHRvcGljczoNCg0KLSBXaGljaCBkYXlzIHBlb3BsZSByZWFkIG1vc3QNCi0gVGhlIG51bWJlciBvZiB0aW1lcyBib29rcyBhcmUgcmVhZA0KLSBSZWFkaW5nIHRyZW5kcyBwZXIgbGlicmFyeSBvdmVyIHRpbWUNCi0gQ29uc2lzdGVuY3kgaW4gcmVhZGluZyB0cmVuZHMNCi0gTW9zdCBwb3B1bGFyIGxpYnJhcnkgbG9jYXRpb25zDQotIFR5cGljYWwgbG9nZ2luZyBob3Vycw0KDQojIEFuYWx5c2lzIEZvcm1hdA0KDQpUaGlzIGFuYWx5c2lzIGhhcyBiZWVuIGNvbmR1Y3RlZCB1c2luZyB0aGUgUiBzdGF0aXN0aWNhbCBwcm9ncmFtbWluZyBsYW5ndWFnZSBhbG9uZyB3aXRoIGEgdmFyaWV0eSBvZiBvdGhlciB0b29scy4gSW4gb3JkZXIgdG8gY3JlYXRlIGEgcmVwcm9kdWNhYmxlIGFuYWx5c2lzLCB0aGUgY29kZSB0aGF0IHByb2R1Y2VkIHRoZSBwYXJ0aWN1bGFyIG91dHB1dCAoc3VjaCBhcyBhIGdyYXBoIG9yIHN0YXRpc3RpYykgaXMgaW5jbHVkZWQgYmVmb3JlIHRoYXQgb3V0cHV0LiBJZiB5b3UgYXJlIG9ubHkgaW50ZXJlc3RlZCBpbiBjb25jbHVzaW9ucyBkcmF3biBmcm9tIHRoaXMgZGF0YSBhbmQgbm90IGluIHRoZSBzb3VyY2UgY29kZSwgcGxlYXNlIHNjcm9sbCB0byB0aGUgdG9wLCBjbGljayBgQ29kZWAgaW4gdGhlIHVwcGVyIHJpZ2h0IGNvcm5lciwgYW5kIHRoZW4gc2VsZWN0IGBIaWRlIEFsbCBDb2RlYC4NCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQpyZXF1aXJlKGRwbHlyKQ0KcmVxdWlyZShnZ3Bsb3QyKQ0KYGBgDQoNCmBgYHtyfQ0KdHMgPC0gcmVhZC5jc3YoImxvZy10aW1lc3RhbXBzLmNzdiIpDQp0cyA8LSB0c1stYygyODk5NCksXSAgIyByZW1vdmUgbWFsZm9ybWVkIHJvdyBhdCB0aGUgZW5kDQojIHRzIDwtIGFzLmRhdGEudGFibGUodHMpDQpuYW1lcyh0cylbbmFtZXModHMpID09ICJsb2dfdmFsdWUiXSA8LSAidGltZXNfcmVhZCINCmBgYA0KDQpgYGB7cn0NCiMgQWRkIGEgY29sdW1uIHdpdGggdGhlIHRpbWUgcm91bmRlZCB0byB0aGUgbmVhcmVzdCBob3VyIGFuZCBubyBkYXRlDQp0aW1lc19vbmx5IDwtIGFzLlBPU0lYY3QodHMkbG9nX3RpbWVzdGFtcCwgZm9ybWF0PSIlbS8lZC8lWSAlSDolTSIpDQp0aW1lc19vbmx5IDwtIHJvdW5kKHRpbWVzX29ubHksIHVuaXRzPWMoImhvdXJzIikpDQp0cyA8LSBtdXRhdGUodHMsIGhvdXJfcm91bmRlZCA9IGZvcm1hdCh0aW1lc19vbmx5LCAiJUg6JU0iKSkNCmBgYA0KDQpgYGB7cn0NCiMgQ29udmVydCBjb2x1bW4gdG8gRGF0ZSBvYmplY3RzDQp0cyRkYXRlX3JlYWQgPC0gYXMuRGF0ZShzdHJwdGltZSh0cyRkYXRlX3JlYWQsICIlbS8lZC8lWSIpKQ0KYGBgDQoNCiMgSW50cm9kdWNpbmcgdGhlIERhdGENCg0KTGV0J3MgYmVnaW4gYnkgZXhhbWluaW5nIHdoYXQgZGF0YSB3ZSBoYXZlLiBIZXJlIGFyZSB0aGUgZmlyc3QgZmV3IGVudHJpZXM6DQoNCmBgYHtyfQ0KaGVhZCh0cykNCmBgYA0KDQotIGBsb2dfdGltZXN0YW1wYDogV2hlbiB0aGUgcGF0cm9uIHN1Ym1pdHRlZCB0aGVpciByZWFkaW5nIGluZm9ybWF0aW9uLiBOb3RlIHRoYXQgdGhpcyBpcyBub3QgdGhlIHRpbWUgd2hlbiB0aGV5IHJlYWQgdGhlIGJvb2suDQotIGBkYXRlX3JlYWRgOiBQYXRyb25zIGNhbiBvcHRpb25hbGx5IHByb3ZpZGUgYSBkaWZmZXJlbnQgZGF0ZSB0aGF0IHRoZXkgcmVhZCB0aGUgYm9vayBmcm9tIHdoZW4gdGhleSBzdWJtaXR0ZWQgdGhlaXIgaW5mb3JtYXRpb24uIEhvd2V2ZXIsIGl0IGRvZXMgbm90IGNvbnRhaW4gYSB0aW1lLg0KLSBgdGltZXNfcmVhZGA6IFRoZSBudW1iZXIgb2YgdGltZXMgdGhlIGJvb2sgd2FzIHJlYWQuIElmIGEgYm9vayB3YXMgcmVhZCBmb3IgYSBncm91cCwgZWFjaCBtZW1iZXIgb2YgdGhlIGdyb3VwIGNvdW50cyBhcyBvbmUgcmVhZC4NCi0gYGxpYnJhcnlgOiBUaGUgcGF0cm9uJ3MgaG9tZSBsaWJyYXJ5LCBldmVuIGlmIHRoZSBib29rIHdhcyBvYnRhaW5lZCB0aHJvdWdoIGFub3RoZXIgYnJhbmNoLg0KLSBgaG91cl9yb3VuZGVkYDogQSBjb2x1bW4gdGhhdCBJIGNyZWF0ZWQgd2hpY2ggaGFzIHRoZSBjb250ZW50cyBvZiBgbG9nX3RpbWVzdGFwYCwgYnV0IHdpdGhvdXQgYSBkYXRlIGFuZCByb3VuZGVkIHRvIHRoZSBuZWFyZXN0IGhvdXIuDQoNCiMgTW9zdCBDb21tb24gUmVhZGluZyBEYXlzDQoNCldoZW4gbG9va2luZyBhdCB0aGUgZGF0YSBpdCdzIHVzZWZ1bCB0byB0aGluayBhYm91dCBjb2x1bW5zIHRoYXQgbWlnaHQgYmUgcmVsYXRlZCB0byBvbmUgYW5vdGhlci4gV2UgaGF2ZSBhIGBkYXRlX3JlYWRgIGNvbHVtbiBhbG9uZyB3aXRoIGB0aW1lc19yZWFkYCBpbmRpY2F0aW5nIHRoZSBudW1iZXIgb2YgdGltZXMgdGhlIGJvb2sgd2FzIHJlYWQgcGVyIGxvZy4gVXNpbmcgUiwgd2UgY2FuIGNvbnZlcnQgYGRhdGVfcmVhZGAgZnJvbSBkYXRlcyBpbnRvIGEgbmV3IGNvbHVtbiBvZiB3ZWVrZGF5cy4gRnJvbSB0aGVyZSB3ZSBjYW4gc2VlIHdoaWNoIGRheXMgcGVvcGxlIHRlbmQgdG8gcmVhZCBtb3N0IG9mdGVuLg0KDQpgYGB7cn0NCnRzJHdlZWtkYXkgPC0gd2Vla2RheXModHMkZGF0ZV9yZWFkKQ0KIyBDb252ZXJ0IHRvIGZhY3RvciBpbiBvcmRlciB0byBpbXBhcnQgYSBtYW51YWwgb3JkZXJpbmcNCnRzJHdlZWtkYXkgPC0gZmFjdG9yKHRzJHdlZWtkYXksIGxldmVscyA9IGMoIk1vbmRheSIsICJUdWVzZGF5IiwgIldlZG5lc2RheSIsICJUaHVyc2RheSIsICJGcmlkYXkiLCAiU2F0dXJkYXkiLCAiU3VuZGF5IikpDQpgYGANCg0KDQpgYGB7cn0NCndlZWtkYXlfc3VtbWFyeSA8LSBzdW1tYXJpc2UoZ3JvdXBfYnkodHMsIHdlZWtkYXkpLA0KICAgICAgICAgICAgICAgICAgIHJlYWRzID0gc3VtKHRpbWVzX3JlYWQpKQ0KDQpnZ3Bsb3QobmEub21pdCh3ZWVrZGF5X3N1bW1hcnkpLCBhZXMod2Vla2RheSwgcmVhZHMpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fbGluZShjb2xvcj0iYmx1ZSIsIGFscGhhPTAuMiwgYWVzKGdyb3VwPTEpKSArDQogIGdndGl0bGUoIlJlYWRpbmcgYnkgV2Vla2RheSIpDQpgYGANCg0KU3VycHJpc2luZ2x5LCBpdCdzIG1vcmUgY29tbW9uIHRvIHJlYWQgb24gVHVlc2RheSB0aHJvdWdoIFRodXJzZGF5IHRoYW4gb24gdGhlIHdlZWtlbmQuIE5vdGUgdGhhdCB0aGUgbGlnaHQgYmx1ZSBsaW5lIGlzIGludGVuZGVkIG9ubHkgdG8gZ3VpZGUgeW91ciBleWUgdG8gdGhlIGNoYW5nZXMgaW4gZWFjaCBibGFjayBkYXRhIHBvaW50OyB3ZSBkb24ndCBoYXZlIHZhbHVlcyBmb3IgdGhlIHRpbWUgYmV0d2VlbiBlYWNoIHdlZWtkYXkuDQoNCiMgTnVtYmVyIG9mIFRpbWVzIFJlYWQNCg0KTGV0J3MgZ2V0IGFuIGlkZWEgb2YgdGhlIHR5cGljYWwgYW1vdW50IG9mIHRpbWVzIGJvb2tzIGFyZSByZWFkIHBlciBsb2cuDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQ0KZ2dwbG90KHRzLCBhZXModGltZXNfcmVhZCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKSArDQogIHhsaW0oMCwgMjApICsNCiAgZ2d0aXRsZSgiQ291bnQgb2YgVGltZXMgUmVhZCIpDQpgYGANCg0KSSd2ZSBsaW1pdGVkIHRoZSByYW5nZSBvZiB0aW1lc19yZWFkIGFuZCBsb3N0IGEgZmV3IG91dGxpZXJzLCBidXQgd2UgY2FuIGNsZWFybHkgc2VlIHRoYXQgbW9zdCBib29rcyBhcmUgb25seSByZWFkIG9uY2UgcGVyIGxvZ2dlZCBldmVudC4NCg0KSG93ZXZlciwgd2UgY2FuIGdldCBhIGJldHRlciBpZGVhIG9mIHRoZSByYW5nZSBvZiB2YWx1ZXMgcGVyIGxpYnJhcnkgdXNpbmcgYSBbYm94cGxvdF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQm94X3Bsb3QpLiBBIGJveHBsb3QgZ2l2ZXMgdXMgYW4gZWFzeSB3YXkgdG8gc2VlIHRoZSBtZWRpYW4gYW5kIHJhbmdlIG9mIHZhbHVlcyBmb3IgYSBjYXRlZ29yeS4NCg0KYGBge3J9DQpzYW1wbGVfZGF0YSA8LSBkYXRhLmZyYW1lKHZhbHVlPXJub3JtKDEwMCwgbWVhbj0xMCwgc2Q9MyksIHR5cGU9Yygic2FtcGxlMSIsICJzYW1wbGUyIikpDQoNCmdncGxvdChzYW1wbGVfZGF0YSwgYWVzKHR5cGUsIHZhbHVlKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIGdndGl0bGUoIlNhbXBsZSBCb3hwbG90cyIpDQpgYGANCg0KSW4gdGhlIGFib3ZlIHNhbXBsZSBib3hwbG90cywgdGhlIGJvbGQgbGluZSBpbmRpY2F0ZXMgdGhlIG1lZGlhbiBvZiB0aGUgZGF0YS4gVGhlcmUgYXJlIGFzIG1hbnkgdmFsdWVzIHRoYXQgZmFsbCBiZXR3ZWVuIHRoZSBib3R0b20gaG9yaXpvbnRhbCBsaW5lIGFuZCB0aGUgbWVkaWFuIGFzIHRoZXJlIGFyZSB0aGF0IGZhbGwgYmV0d2VlbiB0aGUgbWVkaWFuIGFuZCB0aGUgdG9wIGhvcml6b250YWwgbGluZS4NCg0KVGhlIHZlcnRpY2FsIGxpbmUgaW5kaWNhdGVzIHRoZSB0b3RhbCByYW5nZSBvZiB2YWx1ZXMsIHdpdGggYW55IHVudXN1YWwgb3V0bGllcnMgbWFya2VkIGJ5IHNvbGlkIGRvdHMgKHdoaWNoIHlvdSdsbCBlbmNvdW50ZXIgYmVsb3cpLg0KDQpMZXQncyB0YWtlIGEgbG9vayBhdCBvdXIgYWN0dWFsIGRhdGEuDQoNCmBgYHtyfQ0KIyBPbWl0IGRhdGEgdGhhdCBoYXMgbm8gbGlicmFyeSBzcGVjaWZpZWQuDQp3aXRoX2xpYnJhcnkgPC0gdHNbdHMkbGlicmFyeSAhPSAiIixdDQoNCmJveHBsb3RfcmVhZF9saWJyYXJ5IDwtIGdncGxvdCh3aXRoX2xpYnJhcnksIGFlcyhsaWJyYXJ5LCB0aW1lc19yZWFkKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIHNjYWxlX3hfZGlzY3JldGUobGFiZWw9ZnVuY3Rpb24oeCkgYWJicmV2aWF0ZSh4LCBtaW5sZW5ndGg9MTQpKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCg0KYm94cGxvdF9yZWFkX2xpYnJhcnkgKyBnZ3RpdGxlKCJCb3hwbG90IG9mIFRpbWVzIFJlYWQgYnkgTGlicmFyeSIpDQpgYGANCg0KV293LCB0aGlzIGxvb2tzIHN0cmFuZ2UhIFRoYXQncyBiZWNhdXNlIHNvbWUgbGlicmFyaWVzIGhhdmUgc29tZSB2ZXJ5IGhpZ2ggb3V0bGllcnMgbWFya2VkIGJ5IGRvdHMuIExldCdzIHpvb20gaW4gYSBiaXQuDQoNCmBgYHtyfQ0KYm94cGxvdF9yZWFkX2xpYnJhcnkgKw0KICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMCwxNSkpICsNCiAgZ2d0aXRsZSgiQm94cGxvdCBvZiBUaW1lcyBSZWFkIGJ5IExpYnJhcnkgKG1heCAxNSkiKQ0KYGBgDQoNClRoaXMgaXMgYSBiaXQgYmV0dGVyLiBXZSBjYW4gc2VlIHRoYXQgZm9yIGFsbW9zdCBldmVyeSBsaWJyYXJ5IHdlIGRvbid0IGV2ZW4gaGF2ZSBhIGJveCB0byBwbG90LCBzaW5jZSBhbGwgbm9uLW91dGxpZXIgdmFsdWVzIGFyZSBzaW1wbHkgMS4gVGhpcyBtZWFucyB0aGF0IHRoZXNlIGxpYnJhcmllcyBhbG1vc3QgYWx3YXlzIGhhdmUgcGF0cm9ucyB3aG8gb25seSByZWFkIGEgYm9vayBvbmNlIHBlciBsb2dnZWQgZXZlbnQ7IGFueXRoaW5nIGVsc2UgaXMgYW4gdW51c3VhbCBldmVudCBtYXJrZWQgYnkgYSBkb3QuDQoNCkhvd2V2ZXIsIHRoZXJlIGFyZSB0aHJlZSBsaWJyYXJpZXMgdGhhdCBoYXZlIGEgbGFyZ2VyIHJhbmdlIG9mIHZhbHVlcyB3aGVyZSB3ZSBjYW4gYWN0dWFsbHkgc2VlIGEgYm94LiBGb3IgdGhlIHNha2Ugb2YgYnJldml0eSwgSSBhbSBvbmx5IGluY2x1ZGluZyBhbiBhbmFseXNpcyBvZiB0aGUgV29vZHMgUnVuIGxvY2F0aW9uLg0KDQojIyBXb29kcyBSdW4gVGltZXMgUmVhZA0KDQpgYGB7cn0NCmNvdW50X3RpbWVzX3JlYWQgPC0gZnVuY3Rpb24obCkgew0KICBwIDwtIGdncGxvdChmaWx0ZXIodHMsIGxpYnJhcnkgPT0gbCksIGFlcyh0aW1lc19yZWFkKSkgKw0KICAgIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkgKw0KICAgIGdndGl0bGUoc3ByaW50ZigiVGltZXMgUmVhZCwgJXMiLCBsKSkNCiAgcmV0dXJuKHApDQp9DQpgYGANCg0KDQpgYGB7cn0NCmNvdW50X3RpbWVzX3JlYWQoIldvb2RzIFJ1biIpICsNCiAgY29vcmRfY2FydGVzaWFuKHhsaW09YygwLDMwKSwgeWxpbT1jKDAsNzUpKQ0KYGBgDQoNCkZvciB0aGlzIGxpYnJhcnkgSSd2ZSBhbHJlYWR5IHpvb21lZCBpbiBzaW5jZSB0aGUgZGF0YSBwb2ludCBmb3Igc2luZ2xlIHJlYWRpbmdzIGlzIHNvIGxhcmdlIChhYm91dCA4NTAtOTAwKS4gRGVzcGl0ZSB0aGF0LCBXb29kcyBSdW4gaGFzIHNvbWUgcmFuZ2Ugb2YgdmFsdWVzIGZvciBgdGltZXNfcmVhZGAuIExldCdzIHNlZSBob3cgdGhlIHRvdGFsIG51bWJlciBvZiByZWFkcyBwZXIgZGF5IGNoYW5nZXMgb3ZlciB0aW1lLg0KDQojIyBXb29kcyBSdW4gUmVhZHMgT3ZlciBUaW1lIGFuZCBSZWFkaW5nIENvbnNpc3RlbmN5DQoNCmBgYHtyfQ0Kd3IgPC0gc3VtbWFyaXNlKGdyb3VwX2J5KGZpbHRlcih0cywgbGlicmFyeSA9PSAiV29vZHMgUnVuIiksIGRhdGVfcmVhZCksDQogICAgICAgICAgcmVhZHMgPSBzdW0odGltZXNfcmVhZCkNCiAgICAgICAgICApDQpgYGANCg0KDQpgYGB7cn0NCmdncGxvdCh3ciwgYWVzKGRhdGVfcmVhZCwgcmVhZHMpKSArDQogIGdlb21fc3RlcCgpICsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfbWlub3JfYnJlYWtzID0gIjEgZGF5IikgKw0KICBnZ3RpdGxlKCJEYWlseSBSZWFkcyBvdmVyIFRpbWUsIFdvb2RzIFJ1biIpDQpgYGANCg0KVHdvIHRyZW5kcyBjYW4gYmUgc2Vlbi4gRmlyc3QsIHRoZXJlJ3Mgbm90IG11Y2ggZGF0YSBwcmlvciB0byBKdW5lIDZ0aC4gU2Vjb25kLCBvbmNlIHdlIHN0YXJ0IHRvIGhhdmUgbW9yZSBkYXRhIHRoZSBudW1iZXIgb2YgcmVhZHMgcGVyIGRheSBhcHBlYXJzIHRvIHZhcnkgc2lnbmlmaWNhbnRseS4gTGV0J3MgZ2V0IHNvbWUgc3RhdGlzdGljcyBmb3IgV29vZHMgUnVuJ3MgZGF0YSBzdGFydGluZyBhdCBKdW5lIDZ0aC4NCg0KYGBge3J9DQp3cl9qdW5lNiA9IGZpbHRlcih3ciwgZGF0ZV9yZWFkID49ICIyMDE2LTA2LTA2IikNCnN1bW1hcnkod3JfanVuZTYkcmVhZHMpDQpgYGANCg0KV29vZHMgUnVuIGhhcyBhIG1lYW4gcmVhZHMvZGF5IG9mIDk0LjMzLCBidXQgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBpcyA3Ni41NS4gSWYgeW91J3JlIG5vdCBmYW1pbGlhciB3aXRoIHN0YW5kYXJkIGRldmlhdGlvbiwgdGhpcyBpcyBhIG1lYXN1cmVtZW50IHRoYXQgcmVwcmVzZW50cyBob3cgd2lkZSBvZiBhIHJhbmdlIHRoZSBkYXRhIHZhcnkgYWNyb3NzLg0KDQpUbyBpbGx1c3RyYXRlIHRoaXMsIGxvb2sgYXQgdGhlIGZvbGxvd2luZy4gSGVyZSBJJ3ZlIHBsb3R0ZWQgdHdvIHNldHMgb2YgZGF0YSB0aGF0IGhhdmUgdGhlIHNhbWUgYXZlcmFnZSB2YWx1ZSBvZiAwOyBob3dldmVyLCB0aGUgcmVkIGxpbmUgaGFzIGEgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIDEgd2hpbGUgdGhlIGJsdWUgbGluZSBoYXMgYSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgMTAuDQoNCmBgYHtyfQ0Kc2FtcGxlX2RhdGEyIDwtIGRhdGEuZnJhbWUobz1zZXEoMSwgMTAwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGE9cm5vcm0oMTAwLCBtZWFuPTAsIHNkPTEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgYj1ybm9ybSgxMDAsIG1lYW49MCwgc2Q9MTApKQ0KDQpnZ3Bsb3Qoc2FtcGxlX2RhdGEyKSArDQogIGdlb21fbGluZShhZXMobywgYSwgY29sb3I9ImJsdWUiKSkgKw0KICBnZW9tX2xpbmUoYWVzKG8sIGIsIGNvbG9yPSJyZWQiKSkgKw0KICBzY2FsZV9jb2xvcl9kaXNjcmV0ZShuYW1lPSJTRCIsDQogICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcz1jKCJibHVlIiwgInJlZCIpLA0KICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiMSIsICIxMCIpKSArDQogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpKSArDQogIGdndGl0bGUoIk1lYW4gb2YgMCwgRGlmZmVyZW50IFN0YW5kYXJkIERldmlhdGlvbnMiKQ0KYGBgDQoNCkZyb20gdGhpcyB3ZSBjYW4gc2VlIHRoYXQgd2hpbGUgdGhlIG1lYW4gdGVsbHMgdXMgd2hlcmUgdGhlIGxpbmUgaXMgZ2VuZXJhbGx5IGNlbnRlcmVkIHVwb24sIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gY2FwdHVyZXMgdGhlIHNwcmVhZCBvZiB2YWx1ZXMuDQoNCkEgcnVsZSBvZiB0aHVtYiBpcyB0aGF0IDY4JSBvZiBkYXRhIGZhbGxzIHdpdGhpbiBvbmUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBtZWFuLCA5NSUgZmFsbCB3aXRoaW4gdHdvIHN0YW5kYXJkIGRldmlhdGlvbnMsIGFuZCA5OSUgZmFsbCB3aXRoaW4gdGhyZWUgc3RhbmRhcmQgZGV2aWF0aW9ucy4NCg0KTm93IGJhY2sgdG8gb3VyIFdvb2RzIFJ1biBkYXRhLiBTaW5jZSB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIDc2LjU1IGlzIHJlbGF0aXZlbHkgbGFyZ2UgY29tcGFyZWQgdG8gdGhlIG1lYW4gb2YgOTQuMzMsIHdlIGRvIGluZGVlZCBoYXZlIHF1aXRlIHdpZGUgdmFyaWFiaWxpdHkgLS0gYW5kIHRodXMgaW5jb25zaXN0ZW5jeSAtLSBpbiByZWFkcy9kYXkgZm9yIFdvb2RzIFJ1bi4NCg0KSW4gYWRkaXRpb24sIHdlIGNhbiBsb29rIGF0IHRoZSBxdWFydGlsZXMgZm9yIHRoZSBkYXRhLiBXZSBjYW4gdmlzdWFsbHkgZXhhbWluZSB0aGVtIHVzaW5nIHRoZSBmYW1pbGlhciBib3hwbG90Lg0KDQpgYGB7cn0NCmdncGxvdCh3cl9qdW5lNiwgYWVzKGMoIldvb2RzIFJ1biIpLCByZWFkcykpICsNCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSArDQogIGNvb3JkX2ZpeGVkKHJhdGlvID0gMC4wMSkgKw0KICBnZW9tX2JveHBsb3QoKQ0KYGBgDQoNClRoZXJlIGFyZSBhcyBtYW55IHZhbHVlcyBjb250YWluZWQgYmV0d2VlbiB0aGUgZmlyc3QgcXVhcnRpbGUgKDM2Ljc1KSBhbmQgdGhlIG1lZGlhbiAoOTQuMzMpIGFzIHRoZXJlIGFyZSBiZXR3ZWVuIHRoZSBtZWRpYW4gYW5kIHRoZSB0aGlyZCBxdWFydGlsZSAoMTIxLjIwKS4gVmlzdWFsbHkgdGhpcyBtYXkgYmUgZGlmZmljdWx0IHRvIHNlZSwgc28gdGhpcyBpcyBhIGNhc2Ugd2hlbiBsb29raW5nIGF0IHRoZSBhY3R1YWwgbnVtZXJpYyByYW5nZXMgbWF5IGJlIG1vcmUgaGVscGZ1bDogdGhlIHJhbmdlIGJldHdlZW4gdGhlIGZpcnN0IHF1YXJ0aWxlIGFuZCBtZWRpYW4gaXMgZ3JlYXRlciB0aGFuIHRoZSBtZWRpYW4gYW5kIHRoaXJkIHF1YXJ0aWxlLg0KDQpPbmUgd2F5IHRvIGludGVycHJldCB0aGlzIGlzIHRoYXQgb24gZGF5cyB3aXRoIGxlc3MgdGhhbiB0aGUgbWVkaWFuIG51bWJlciBvZiByZWFkcyB3ZSdyZSBtb3JlIGxpa2VseSB0byBzZWUgYSB3aWRlciB2YXJpZXR5IG9mIHJlYWQgYW1vdW50cywgYnV0IG9uIGRheXMgd2l0aCBhYm92ZSB0aGUgbWVkaWFuIG51bWJlciBvZiByZWFkcywgd2UgYXJlIG1vcmUgbGlrZWx5IHRvIHNlZSBhIGNsb3NlciByYW5nZSBvZiByZWFkIGFtb3VudHMuIEhvd2V2ZXIsIHdlIGNhbid0IGVzdGFibGlzaCBhbnkgY2F1c2FsIGNvbm5lY3Rpb25zIHdpdGggdGhpcyBsaW1pdGVkIGRhdGEgLS0gdGhpcyBpcyBqdXN0IG9ic2VydmluZyBhIHRyZW5kLg0KDQojIE1vc3QgUG9wdWxhciBMb2NhdGlvbnMNCg0KU28gd2hhdCBhcmUgdGhlIG1vc3QgcG9wdWxhciBsb2NhdGlvbnM/IEEgbmFpdmUgYXBwcm9hY2ggaXMgdG8gc2ltcGx5IHNlZSB3aGljaCBsb2NhdGlvbnMgaGFkIHRoZSBoaWdoZXN0IHRvdGFsIG51bWJlciBvZiByZWFkcy4NCg0KYGBge3J9DQpyZWFkc19ieV9saWJyYXJ5IDwtIHN1bW1hcmlzZShncm91cF9ieSh0c1t0cyRsaWJyYXJ5ICE9ICIiLF0sIGxpYnJhcnkpLA0KICByZWFkcyA9IHN1bSh0aW1lc19yZWFkKQ0KKQ0KDQpwb3B1bGFyIDwtIGFycmFuZ2UocmVhZHNfYnlfbGlicmFyeSwgZGVzYyhyZWFkcykpDQp0b3BfMyA8LSBwb3B1bGFyWzE6MyxdDQoNCmdncGxvdChwb3B1bGFyLCBhZXMobGlicmFyeSwgcmVhZHMpKSArDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKw0KICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVsPWZ1bmN0aW9uKHgpIGFiYnJldmlhdGUoeCwgbWlubGVuZ3RoPTE0KSkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpICsNCiAgZ2d0aXRsZSgiVG90YWwgU3VtbWVyIFJlYWRzIHBlciBMaWJyYXJ5IikNCmBgYA0KDQpgYGB7cn0NCnRvcF8zDQpgYGANCg0KDQpJdCdzIGNsZWFyIHRoYXQgU3F1aXJyZWwgSGlsbCBpcyB0aGUgbW9zdCBwb3B1bGFyIGxpYnJhcnkgZm9sbG93ZWQgYnkgT2FrbGFuZC4gVGhpcmQgcGxhY2UgaXMgZGlmZmljdWx0IHRvIGlkZW50aWZ5IHZpc3VhbGx5LCBidXQgbG9va2luZyBhdCB0aGUgbnVtYmVycyB3ZSBzZWUgdGhhdCBpdCBpcyBXb29kcyBSdW4uDQoNCiMjIFJlYWRzL0RheSBBY3Jvc3MgdGhlIE1vc3QgUG9wdWxhciBMb2NhdGlvbnMNCg0KQmVpbmcgdGhhdCB0aGVzZSB0aHJlZSBhcmUgdGhlIG1vc3QgcG9wdWxhciBsaWJyYXJpZXMsIHRoZXkgcHJvdmlkZSB1cyB3aXRoIGEgbG90IG9mIGRhdGEgcG9pbnRzIHRvIGFuYWx5emUuIExldCdzIGxvb2sgYXQgaG93IHRvdGFsIHJlYWRzIHBlciBkYXkgaGFzIHZhcmllZCBvdmVyIHRpbWUgYWNyb3NzIHRoZXNlIGxpYnJhcmllcy4NCg0KYGBge3J9DQpwb3B1bGFyX2RhdGEgPC0gZ3JvdXBfYnkodHNbdHMkbGlicmFyeSAlaW4lIHRvcF8zJGxpYnJhcnksXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBsaWJyYXJ5LA0KICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGVfcmVhZCkgJT4lDQogICAgICAgICAgICAgICAgc3VtbWFyaXNlKHJlYWRzID0gc3VtKHRpbWVzX3JlYWQpKQ0KDQpwb3B1bGFyX3RyZW5kcyA8LSBnZ3Bsb3QocG9wdWxhcl9kYXRhW3BvcHVsYXJfZGF0YSRkYXRlX3JlYWQgPj0gIjIwMTYtMDYtMDYiLF0sDQogICAgICAgICBhZXMoZGF0ZV9yZWFkLCByZWFkcywgY29sb3I9bGlicmFyeSkpICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIGdlb21fbGluZSgpICsNCiAgICBzY2FsZV94X2RhdGUoZGF0ZV9taW5vcl9icmVha3MgPSAiMSBkYXkiKQ0KDQpwb3B1bGFyX3RyZW5kcyArIGdndGl0bGUoIlJlYWRpbmcgVHJlbmRzIEFjcm9zcyBNb3N0IFBvcHVsYXIgTGlicmFyaWVzIikNCmBgYA0KDQpXZSBjYW4gc2VlIHRoYXQgU3F1aXJyZWwgSGlsbCBoYXMgYSBodWdlIHNwaWtlIG9uIEp1bHkgNnRoOyBmb3IgZnVydGhlciBhbmFseXNpcyB3ZSBtYXkgd2FudCB0byBsb29rIGludG8gd2hldGhlciBhbnkgc3BlY2lhbCBldmVudHMgb2NjdXJyZWQgb24gdGhhdCBkYXkuIEhvd2V2ZXIsIHRoaXMgZGF0YSBwb2ludCBtYWtlcyB0aGUgcmVzdCBvZiB0aGUgY2hhcnQgZGlmZmljdWx0IHRvIGNvbXBhcmUuIEFzIHN1Y2gsIGxldCdzIHpvb20gaW4uDQoNCmBgYHtyfQ0KcG9wdWxhcl90cmVuZHMgKw0KICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMCw3NTApKSArDQogIGdndGl0bGUoIlJlYWRpbmcgVHJlbmRzIEFjcm9zcyBNb3N0IFBvcHVsYXIgTGlicmFyaWVzIChtYXggNzUwKSIpDQpgYGANCg0KRnJvbSBoZXJlIHdlIGNhbiBzZWUgdGhhdCBldmVuIGluIHRoZSBtb3N0IHBvcHVsYXIgbGlicmFyaWVzLCB0aGUgdG90YWwgbnVtYmVyIG9mIHJlYWRzIHBlciBkYXkgb3ZlciB0aW1lIGlzIGdlbmVyYWxseSBlcnJhdGljLg0KDQpGdXJ0aGVyIGFuYWx5c2lzIG1pZ2h0IHdhbnQgdG8gbG9vayBhdCBtZWFzdXJlbWVudHMgc3VjaCBhcyBsaWJyYXJpZXMgd2l0aCB0aGUgbW9zdCBjb25zaXN0ZW50IG51bWJlciBvZiByZWFkcyAoYnkgdXNpbmcgc3RhbmRhcmQgZGV2aWF0aW9uKS4gRnJvbSB0aGVyZSBhbiBleHBlcmltZW50IGNhbiBiZSBjb25kdWN0ZWQgdG8gdHJ5IG91dCBwcm9ncmFtcyBhbmQgc2VlIGlmIHRoZXkgaW5jcmVhc2UgY29uc2lzdGVuY3kgYW5kL29yIHRoZSB0b3RhbCBudW1iZXIgb2YgcmVhZHMuDQoNCiMgVHlwaWNhbCBMb2dnaW5nIFRpbWVzDQoNClRoZSBkYXRhc2V0IGFsc28gaW5jbHVkZXMgdGhlIHRpbWUgd2hlbiBlYWNoIGJvb2sgd2FzIGxvZ2dlZC4gTm90ZSB0aGF0IHRoaXMgaXMgZGlmZmVyZW50IGZyb20gd2hlbiB0aGUgYm9vayB3YXMgYWN0dWFsbHkgcmVhZC4gTGV0J3MgZ3JvdXAgdGhlc2UgYnkgaG91ciBhbmQgc2VlIHdoYXQgaG91cnMgYXJlIG1vc3QgcG9wdWxhciBvdmVyYWxsIC0tIGVhcmxpZXIgb24gSSBhbWVuZGVkIHRoZSBkYXRhIHdpdGggYSBuZXcgY29sdW1uIG9mIHZhbHVlcyBjb250YWluaW5nIHRoZSB0aW1lIHJvdW5kZWQgdG8gdGhlIG5lYXJlc3QgaG91ci4NCg0KYGBge3J9DQpnZ3Bsb3QodHMsIGFlcyh4PWhvdXJfcm91bmRlZCkpICsNCiAgZ2VvbV9iYXIoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkgKw0KICBnZ3RpdGxlKCJMb2dnaW5nIGJ5IEhvdXIiKQ0KYGBgDQoNCkl0IGxvb2tzIGxpa2UgbW9zdCBsb2dnaW5nIG9jY3VycyBiZXR3ZWVuIDNQTS05UE0sIGJ1dCBpdCdzIGludGVyZXN0aW5nIHRoYXQgdGhlcmUgYXJlIG1vcmUgbG9nIGV2ZW50cyBhcm91bmQgbWlkbmlnaHQgdGhhbiBhcm91bmQgbm9vbiB3aGVuIHBlb3BsZSBtaWdodCBiZSBvbiBhIGx1bmNoIGJyZWFrLg0KDQpMZXQncyBjb21wYXJlIHRoaXMgZGF0YSBhY3Jvc3MgdGhlIG1vc3QgcG9wdWxhciBsb2NhdGlvbnM6DQoNCmBgYHtyfQ0KaG91cnNfYnlfbG9jYXRpb24gPC0gZnVuY3Rpb24obG9jYXRpb24pIHsNCiAgZ2dwbG90KGZpbHRlcih0cywgbGlicmFyeSA9PSBsb2NhdGlvbiksIGFlcyh4PWhvdXJfcm91bmRlZCkpICsNCiAgICBnZW9tX2JhcigpICsNCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpICsNCiAgICBnZ3RpdGxlKHNwcmludGYoIkxvZ2dpbmcgSG91cnM6ICVzIiwgbG9jYXRpb24pKQ0KfQ0KYGBgDQoNCmBgYHtyfQ0KZm9yIChsIGluIHRvcF8zJGxpYnJhcnkpIHsNCiAgcHJpbnQoaG91cnNfYnlfbG9jYXRpb24obCkpDQp9DQpgYGANCg0KQWx0aG91Z2ggdGhlIGNvdW50IHNjYWxlIHZhcmllcyBhY3Jvc3MgdGhlc2UgdGhyZWUgbGlicmFyaWVzLCB0aGUgZ2VuZXJhbCBzaGFwZSBpcyBzaW1pbGFyLg0KDQpXZSBjYW4gZGVyaXZlIHNvbWUgcHJhY3RpY2FsIGluc2lnaHRzIGZyb20gdGhpcyBkYXRhLiBUaW1lcyB3aXRoIGhpZ2ggbG9nZ2luZyBjb3VudHMgd291bGQgYmUgZ29vZCB0aW1lcyB0byBlbnN1cmUgdGhhdCB0aGUgbGlicmFyaWVzJyB0ZWNobmljYWwgc3VwcG9ydCBkZXBhcnRtZW50cyBhcmUgYXZhaWxhYmxlIGZvciBjb21wdXRlciBoZWxwLiBBbHNvLCBpZiBDTFBHSCdzIElUIGRlcGFydG1lbnQgbmVlZHMgdG8gcGVyZm9ybSBtYWludGVuYW5jZSBvbiB0aGlzIHN5c3RlbSwgdGhleSBtaWdodCBub3Qgd2FudCB0byBkbyBpdCBhdCBhIGxhdGUgaG91ciAtLSBpbiBmYWN0LCBwZXJmb3JtaW5nIGl0IGF0IHRoZSBzdGFydCBvZiB0aGUgd29ya2RheSBvciBldmVuIGF0IG5vb24gbWF5IGFjdHVhbGx5IGltcGFjdCBhIGxlc3NlciBhbW91bnQgb2YgcGF0cm9ucy4NCg0KIyBDb25jbHVzaW9uDQoNClRoaXMgd2FzIGFuIGV4cGxvcmF0aW9uIG9mIG9ubHkgYSBzbWFsbCBhbW91bnQgb2YgdGhlIHJlbGVhc2VkIHN1bW1lciByZWFkaW5nIGRhdGEuIEhvd2V2ZXIsIHdlJ3ZlIGJlZW4gYWJsZSB0byBjb21lIHVwIHdpdGggc29tZSBpbnRlcmVzdGluZyBhdmVudWVzIGZvciBmdXJ0aGVyIGludmVzdGlnYXRpb246DQoNCi0gV2hpY2ggZGF5cyBwZW9wbGUgcmVhZCB0aGUgbW9zdA0KLSBMaWJyYXJ5IHBvcHVsYXJpdHkNCi0gUmVhZGluZyBiZWhhdmlvciBvdmVyIHRpbWUNCi0gQ29uc2lzdGVuY3kgaW4gcmVhZGluZyBiZWhhdmlvcg0KLSBIb3cgb2Z0ZW4gYm9va3MgYXJlIHJlYWQgbXVsdGlwbGUgdGltZXMNCi0gVGltZXMgZm9yIElUIHN0YWZmaW5nIGFuZCBtYWludGVuYW5jZQ0KLSBBIHZhcmlldHkgb2YgYmFzZXMgZm9yIG1lYXN1cmluZyB0aGUgZWZmZWN0aXZlbmVzcyBvZiBmdXR1cmUgcHJvZ3JhbXMNCg0KVGhhdCBiZWluZyBzYWlkLCB0aGVyZSBpcyBvbmUgbWFpbiBjaGFsbGVuZ2Ugd2l0aCB0aGlzIGRhdGE6DQoNCi0gVGhlIGRhdGEgYXJlIHNlbGYtcmVwb3J0ZWQgYW5kIHRodXMgYmlhc2VkIHRvd2FyZHMgdGhvc2Ugd2hvIGFyZSBpbmNsaW5lZCB0byBwdXQgZm9ydGggdGhlIGVmZm9ydCB0byByZWNvcmQgdGhlaXIgYWN0aXZpdHkuDQoNCkl0IHdvdWxkIGJlIGludGVyZXN0aW5nIHRvIHRyeSBhbHRlcm5hdGl2ZSBtZXRob2RzIG9mIHJlY29yZGluZyByZWFkaW5nIGFjdGl2aXR5IHRoYXQgZG9uJ3QgaW52b2x2ZSB0aGUgdXNlciBoYXZpbmcgYWNjZXNzIHRvIGEgY29tcHV0ZXIsIHBhcnRpY3VsYXJseSBpbiBsb3cgaW5jb21lIGFyZWFzLiBJbiBhZGRpdGlvbiwgaXQnZCBiZSBuaWNlIGlmIHVzZXJzIGNvdWxkIGVudGVyIGEgdGltZSByYW5nZSBpbiB3aGljaCB0aGV5IHJlYWQgdGhlIGJvb2sgc2VwYXJhdGUgZnJvbSB3aGVuIHRoZXkgbG9nIHRoZWlyIHJlYWRpbmcgKGFsdGhvdWdoIHVzZXJzIGNhbiAgb3B0aW9uYWxseSBlbnRlciBhIHNlcGFyYXRlIGRhdGUgX3dpdGhvdXRfIGEgdGltZSkuDQo=