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:

Analysis Format

This analysis has been conducted using the R statistical programming language along with a variety of other tools. In order to create a reproducable analysis, the code that produced the particular output (such as a graph or statistic) is included before that output. If you are only interested in conclusions drawn from this data and not in the source code, please scroll to the top, click Code in the upper right corner, and then select Hide All Code.

ts <- read.csv("log-timestamps.csv")
ts <- ts[-c(28994),]  # remove malformed row at the end
# ts <- as.data.table(ts)
names(ts)[names(ts) == "log_value"] <- "times_read"
# Add a column with the time rounded to the nearest hour and no date
times_only <- as.POSIXct(ts$log_timestamp, format="%m/%d/%Y %H:%M")
times_only <- round(times_only, units=c("hours"))
ts <- mutate(ts, hour_rounded = format(times_only, "%H:%M"))
# Convert column to Date objects
ts$date_read <- as.Date(strptime(ts$date_read, "%m/%d/%Y"))

Introducing the Data

Let’s begin by examining what data we have. Here are the first few entries:

head(ts)

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.

For normally distributed data (think of a bell curve), 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.

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:

That being said, there is one main challenge with this data:

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=