1 Introduction

Introduction of SF Bay Area Bike Share: http://www.bayareabikeshare.com/

Data used: Year 1 Data, from Mar 2014 - Aug 2014, trips data ONLY.

One of the most desirable wishes of the riders who uses this bike-sharing program is that they are able to get a bike at the nearest station from their current location and then able to dock at their destination bike station upon arriving. However, this is unlikely the case for some because of the irregularities in people movement across the days, timings of the day (think peak-hours traffic) and the starting and end location (most people would be heading towards central area).

This is a headache for the managers in this bike-program as they seek to keep their riders happy. What are some of the actions that they could take to ensure this problem is mitigated? One of the current practices is to assign employees to move the bikes from areas with least demand at certain timings to other areas with much higher demand. However, how would a manager know which station has highest demand, least demand, what time to move? To add on, the days of the week and who are using it also would matter. In fact, there are also limited employees to shift the bikes around on a certain day.

Hence, this submission seeks to reveal some insights related to this problem such as - what is the distribution of the number of rides across the dates, days and time. What are the most common to and fro paths? Eventually, from these exploratory data analysis, we will be able to understand the data better and then create an appropriate prediction model and tool to help manager understand the situtation much better! This tool will help to inform the manager in advance, which day would have the highest increment and fall in number of bikes. Hence, the manager will then be able to station his employees across those stations and transport the bikes to balance the situation.

After conducting a Google search on the stations, I realized that the data consists of rides and stations across different cities. Hence, for this analysis, I would focus on the trips that start or end stations within San Francisco only (i.e. includes trips that start from SF stations and end at non-SF stations, trips that start from non-SF stations and end at SF stations)

Several hypothesis that could be uncovered:

2 Exploratory Data Analysis

To start off, let’s load the necessary packages, the CSV data file provided and the stations within San Francisco.

** Please place the tripdata file in the working directory for the code to run **

library(ggplot2)
library(lubridate)
library(plyr)
library(dplyr)
library(forecast)
tripdata <- read.csv("./tripdata.csv")
sf <- c("South Van Ness at Market", "Market at 10th", "San Francisco City Hall", 
        "Golden Gate at Polk", 
        "Civic Center BART (7th at Market)", "Powell Street BART", 
        "Powell at Post (Union Square)", "5th at Howard", "Market at 4th", "Post at Kearny", 
        "Commercial at Montgomery", "Washington at Kearny", "Grant Avenue at Columbus Avenue", 
        "Embarcadero at Sansome", "Embarcadero at Vallejo", "Broadway St at Battery St", 
        "Davis at Jackson", "Clay at Battery", "Harry Bridges Plaza (Ferry Building)", 
        "Steuart at Market", "Beale at Market", "Mechanics Plaza (Market at Battery)", 
        "Embarcadero at Folsom", "Temporary Transbay Terminal (Howard at Beale)", 
        "Market at Sansome", "Spear at Folsom", "Howard at 2nd", "Embarcadero at Bryant", 
        "Yerba Buena Center of the Arts (3rd @ Howard)", "2nd at Folsom", "2nd at South Park", 
        "2nd at Townsend", "San Francisco Caltrain (Townsend at 4th)", "Townsend at 7th",
        "San Francisco Caltrain 2 (330 Townsend)")

2.1 Removal of trips within non-SF cities

### Create 2 columns that tag the start and end stations on whether they are SF stations ###
tripdata$go <- ifelse(tripdata$Start.Station %in% sf, "SF", "NotSF")
tripdata$back <- ifelse(tripdata$End.Station %in% sf, "SF", "NotSF")
tripdata$goback <- paste(tripdata$go, tripdata$back, sep = " to ")

Having done so, let’s have a look at the distributions of the trips of inter-cities and intra-cities stations within this data.

### Group up by the *goback* variable, before tallying the results up and ordering
tripdata %>% group_by(goback) %>% tally() %>% arrange(desc(n))

Interesting! There are 19031 trips made in non-SF cities within the data. We have also found people who have in fact travelled across cities (14 such cases), must have took them some time which I wonder how long, but that will be for another time :) In addition, this works out well in our favour because 14 is a negligible number, thus we can remove them without affecting the numbers much!

Now, let’s retain only those trips that are within “SF to SF”.

tripdata <- filter(tripdata, goback == 'SF to SF')
### Also to remove the tagged columns that were added as they are no longer required ###
tripdata <- tripdata[,-c(12,13,14)]

Right now, our dataset contains of trips that are only within SF city, great! Now we can move on to perform some simple exploratory analysis to see what’s been going on.

2.2 Further cleaning of dataset

### Convert the Start.Date and End.Date columns into Date-time class ###
StartDate <- strptime(tripdata$Start.Date, "%m/%d/%Y %H:%M")
tripdata$Start.Date <- as.POSIXct(StartDate) 
EndDate <- strptime(tripdata$End.Date, "%m/%d/%Y %H:%M")
tripdata$End.Date <- as.POSIXct(EndDate) 
### Remove the unnecessary stored values ###
remove(StartDate,EndDate)
### Creates a copy of the original dataset ###
original <- tripdata
### Creates several useful columns (breakdown start/end date by Month, Day, Day of Week for analysis ###
tripdata <- mutate(tripdata, sdate = date(Start.Date), smonth = month(Start.Date,label = TRUE), sday = day(Start.Date), swday = wday(Start.Date,label = TRUE), shr = hour(Start.Date), edate = date(Start.Date), emonth = month(End.Date,label = TRUE), eday = day(End.Date), ewday = wday(End.Date,label = TRUE), ehr = hour(End.Date))
### To turn the Start.Date and End.Date into character format to avoid conflicting issues in tallying ###
tripdata$Start.Date <- as.character(tripdata$Start.Date)
tripdata$End.Date <- as.character(tripdata$End.Date)

2.3 Distribution of rides by days of week, time, dates and subscriber type

With the cleaning up done, let’s now tally up the number of rides across the weekdays and see what’s the distribution like to answer our hypothesis!

### Tally up the number of trips for certain weekday ###
cwd <- ddply(tripdata, .(swday), tally)
cwd$wkday <- ifelse(cwd$swday %in% c("Mon","Tues","Wed","Thurs","Fri"), "Weekday","Weekend")
ggplot(cwd, aes(x = swday, y = n, fill = wkday)) + geom_bar(stat='identity') + labs(title = "Ridership over days of week", x = "Days of Week", y = "Count")

The result is as expected, the number of rides occurred mainly during weekdays and fell to a lower number during weekends. Hence, we would expect the availability of bikes problem to occur more during weekdays.

This brings me to the point, could the number of non-subscribers drive up the usage on weekdays? As much as we want to cater to both groups of customers, however, if there is a large number of non-subscribers using the bikes, this would cause much displeasure to our subscribers. Hence, we may want to increase the prices for customers usage such that we can allow more subscribers to use instead.

Let’s find out how does the proportion of customers v.s. subscribers look like in the distribution.

cwdrider <- ddply(tripdata, .(swday,Subscriber.Type), tally)
ggplot(cwdrider, aes(x = swday, y = n, fill = Subscriber.Type)) + geom_bar(stat='identity') + labs(title = "Ridership over days of week by Subscriber Type", x = "Days of Week", y = "Count")

Looks like the customers didn’t create high demand during the weekdays… However, it would be too quick to simply dismiss them as not a cause. Could they utilize the service mostly during the peak hours? If so, this could cause a problem as well!

Now let’s move on to examining the distribution of ridership over the hour of the day. We shall construct similar code but in the hours context.

### Just some cleaning up to do ###
remove(cwd,cwdrider)
###
hrrider <- ddply(tripdata, .(shr,Subscriber.Type), tally)
ggplot(hrrider, aes(x = shr, y = n, fill = Subscriber.Type)) + geom_bar(stat='identity') + labs(title = "Ridership over time of day by Subscriber Type", x = "Time of day (hr)", y = "Count")

Whew! Looks like customers are not causing too much of a problem then it seems… In addition, we are also able to see the distribution of trips over the time of the day. For curiosity’s sake, let’s combine all these factors together and see how does ridership vary over the days of week over hour of the day, broken down by subscriber type!

whrider <- ddply(tripdata, .(shr,swday,Subscriber.Type), tally)
ggplot(whrider, aes(x = shr, y = n, fill = Subscriber.Type)) + facet_grid(. ~ swday) + geom_bar(stat='identity') + labs(title = "Ridership over WDay over Time by Subscriber Type", x = "Time", y = "Count")

3 quick points that we can infer from the graphs:

  • The distribution of trips over time during weekdays is approximately the same across all 5 days, having peaks at 7 - 9 a.m. and 4 - 6 p.m.

  • The distribution of trips over the weekend is, different from weekdays but similar across both days!

  • The number of trips taken by customers are roughly more than the number of trips taken by subscribers on weekends, which is a good sign :)

It is also intuitive to have a look at how does the ridership change over time within the given period of March to August, and the effects of weekends and holidays have on them!

Extracted several holiday dates celebrated in the U.S. in 2014

  • 11 May 2014 (Sun) - Mother’s Day

  • 26 May 2014 (Mon) - Memorial Day

  • 15 Jun 2014 (Sun) - Father’s Day

  • 04 Jul 2014 (Fri) - Independence Day

### Some cleaning up as usual ###
remove(hrrider,whrider)
### Convert the initial start date back to POSICXlt class, and renamed it ###
tripdata$Start.DatewTime <- strptime(tripdata$Start.Date, "%Y-%m-%d %H:%M:%S")
tripdata$End.DatewTime <- strptime(tripdata$End.Date, "%Y-%m-%d %H:%M:%S")
### Extracted the date out and turned into character class ###
tripdata$Start.Date <- as.character(date(tripdata$Start.DatewTime))
tripdata$End.Date <- as.character(date(tripdata$End.DatewTime))
### Convert the DateWTime back to character so that we can tally w/o issues ###
tripdata$Start.DatewTime <- as.character(tripdata$Start.DatewTime)
tripdata$End.DatewTime <- as.character(tripdata$End.DatewTime)
### To add columns for holidays and weekends ###
tripdata <- mutate(tripdata, wkend = (swday %in% c("Sat","Sun")),
                   hol = (Start.Date %in% c("2014-05-11","2014-05-26","2014-06-15",
                                                  "2014-07-04")))
### Tally up the number of trips by start date ###
tripdate <- ddply(tripdata, .(Start.Date, wkend, hol), tally)
### Convert into date format
tripdate$Start.Date <- date(tripdate$Start.Date)
### Plot the graph with red lines for weekends, blue lines for holidays
ggplot(tripdate, aes(Start.Date, n)) + geom_line() + geom_area(aes(y = wkend*max(n)), fill ="red", alpha = 0.30) + geom_area(aes(y = hol*max(n)), fill = "blue", alpha = 0.30) +
    labs(title = "No. of trips from Mar - Aug", x = "Date", y = "Count") + theme_minimal()

When plotted across the dates from 1st Mar 14 - 31st Aug 14, there is a fall in ridership on every weekend, this finding is consistent with our previous findings. Yet, there also seems to be a slight drop for the days with holidays, although it seems minor/negligible.

Also, we note that there is a general increment trend as well which is a good sign! This could very well be due to the transition of season, from spring to summer, thus the increase in number of trips?

In addition, it seems like the distribution of the trips follow a general trend with seasonality effect, which we could actually predict using the Holts-Winter model..

After knowing how does the ridership number change over several variables, now we wonder if the ridership displays a homogenous distribution across the paths taken? This is most unlikely to be the case because we should expect several stations that are nearer to the centre to be more crowded/congested during peakhours!

We shall now investigate by looking at the top 5 starting and ending locations

### General cleaning ###
remove(tripdate)
### 
startpaths <- ddply(tripdata, .(Start.Station), tally) %>% arrange(desc(n))
head(startpaths)
endpaths <- ddply(tripdata, .(End.Station), tally) %>% arrange(desc(n))
head(endpaths)

The top 6 locations that appear in the Start Station also appear in the top 6 ending locations. This is not surprising because it is very likely that these 6 stations are places where people usually head to work and return from. This is in fact a good sign, because it would be worrying if these 2 results do not match. This would imply that riders are heading to a location, which they would very likely not return from, or vice versa. This would result in a huge imblanace in the number of bikes at each location.

However, the hypothesis still remains! If everyone is travelling to, for example, San Francisco Caltrain (Townsend at 4th), then we will be expecting that station to be full quickly!

Let’s see if this is the case for the top 6 stations and bottom 6 stations across time.

startdata <- tripdata[tripdata$Start.Station %in% startpaths$Start.Station[1:6],] %>%
    ddply(.(Start.Station,shr, wkend), tally) 
startdata$wkend <- ifelse(startdata$wkend == TRUE, 'Weekend', 'Weekday')
ggplot(startdata, aes(x = shr, y = n, colour = wkend)) + facet_wrap( ~ Start.Station, ncol = 2) + geom_line(aes(group = wkend)) + geom_point(aes(group = wkend)) + labs(title = "Distribution of trips starting from each station across time by weekday/weekend", x = "Time (hr)", y = "Count")

We notice that each of the 6 stations have very different distributions across time and different magnitude as well.

Let’s examine the case for the top 6 ending stations with highest number of trips.

enddata <- tripdata[tripdata$End.Station %in% endpaths$End.Station[1:6],] %>%
    ddply(.(End.Station,shr, wkend), tally) 
enddata$wkend <- ifelse(enddata$wkend == TRUE, 'Weekend', 'Weekday')
ggplot(enddata, aes(x = shr, y = n, colour = wkend)) + facet_wrap( ~ End.Station, ncol = 2) + geom_line(aes(group = wkend)) + geom_point(aes(group = wkend)) + labs(title = "Distribution of trips starting from each station across time by weekday/weekend", x = "Time (hr)", y = "Count")

Based on the graphs displayed, this does confirm our hypothesis that these tend to be places when people would usually go/leave in the morning and then go/leave at night! This is evident from the reflection of the first graph about the y axis, which resembles the distribution of the second graph.

From here, we could in fact see that San Francisco Caltrain (Townsend at 4th), San Francisco Caltrain 2 (330 Townsend) and Harry Bridges Plaza are places where people either go home to or to take on another form of transport (most likely the commuter train) in the evening to some where else (high number of departures in the evening)! On the other end, the other 3 stations are places where many people are most likely to work at, hence the high number of arrivals in the morning and high number of departures in the evening.

Hence, we can then see how several factors such as time of the day, day of the week, date, subscriber type can then affect the general demand and supply across the stations.

3 Creation of the prediction tool

Next step here, we will attempt to model the predicted net change of number of bikes at the San Francisco Caltrain (Townsend at 4th) station using the data provided. This will be done by several steps.

  1. Extracts out the trips that either start or end at the station.

  2. Separate this dataset into 2 - one for trips starting at that station, one for trips ending at that station.

  3. Extracts only the date information out of the Start.DatewTime and End.DatewTime columns.

  4. Aggregate the number of trips by day.

  5. Create a single column of all the dates running from 1st Mar 14 to 31st Aug 14.

  6. Merge these datasets together by date.

  7. Trips that start at that station signifies number of bikes leaving the station, trips that end at that station signifies the number of bikes leaving the station. Thus, the number of bikes ending at station deducted by trips starting at station, would give us the net change (i.e. a number of positive 8 at a particular date, would represent that there is a net increase of 8 bikes at that date)

  8. Plot the net change of bikes at the trip over date.

  9. Fit into the Holt-Winter’s additive model (found to be the best)

### General cleaning up as usual ###
remove(startdata,enddata,startpaths,endpaths)
### Extracts out the data that start or end at San Francisco Caltrain (Townsend at 4th) ###
sfct <- tripdata[tripdata$Start.Station %in% "San Francisco Caltrain (Townsend at 4th)" | tripdata$End.Station %in% "San Francisco Caltrain (Townsend at 4th)",]
### Splits the data set into 2, one that starts at that station, another one that ends at the station ###
startdt <- select(sfct, Start.Station, Start.DatewTime) %>% filter(Start.Station == "San Francisco Caltrain (Townsend at 4th)")
enddt <- select(sfct, End.Station, End.DatewTime) %>% filter(End.Station == "San Francisco Caltrain (Townsend at 4th)")
### Extracts only the date and hour out ###
startdt$Start.DatewTime <- as.POSIXct(format(as.POSIXct(startdt$Start.DatewTime), "%Y-%m-%d"))
enddt$End.DatewTime <- as.POSIXct(format(as.POSIXct(enddt$End.DatewTime), "%Y-%m-%d"))
### Aggregate the number of trips by date and hour ###
startdt <- aggregate(Start.Station ~ Start.DatewTime, data = startdt, FUN = length)
enddt <- aggregate(End.Station ~ End.DatewTime, data = enddt, FUN = length)
### Create a column of sequential date and time from 1st Mar 14 to 31st Aug 14 ###
datetime <- seq(from = as.POSIXct("2014-03-01"), to = as.POSIXct("2014-08-31"), by = "day")
datetime <- data.frame(datetime)
### Merge all the subsequent columns together, replace NA values with 0 ###
combdt <- merge(datetime, startdt, by.x = "datetime", by.y = "Start.DatewTime", all.x = TRUE)
combdt$Start.Station[is.na(combdt$Start.Station)] <- 0
combdt <- merge(combdt, enddt, by.x = "datetime", by.y = "End.DatewTime", all.x = TRUE)
combdt$End.Station[is.na(combdt$End.Station)] <- 0
### The start.station column signifies number of bikes leaving the station, the end.station column signifies the number of bikes entering the station. To get the net change, we form a new column of Net = End.Station - Start.Station ###
combdt <- mutate(combdt, Net = End.Station - Start.Station)
### How the data looks like for head and tail ###
head(combdt)
tail(combdt)
### Plot the data to see the distribution of net change in number of bikes across date and hour ###
ggplot(combdt, aes(x = datetime, y = Net)) + geom_line() + labs(title = "Net change of bikes over date", x = "Net change of bikes", y = "Date")

What we have noticed here is that the number of trips definitely follows a pattern, due to the change in usage from weekdays to weekend. Hence, we tend to notice 4 peak periods in each month (the 4 set of weekdays in each month). In addition, it also seems that there is a larger variation of net change as time progresses.

With these findings in mind, we can now definitely attempt to plot Holt-Winters model to predict what could the net change be in the upcoming month!

combdt <- select(combdt, datetime, Net)
combdts <- ts(combdt$Net, frequency = 7, start = c(1,3))
### Fit an additive Holt Winter's Additive model ###
fitHW <- HoltWinters(combdts, seasonal = "additive")
plot(fitHW, lwd = 2, col = "blue", main = "Holt Winter Additive")

The original plot is in blue, the plotted graph is in red. Looks like the fitted model seems to be doing decent, and much better at the later points! Now let’s attempt to predict and plot the next 2 weeks net change in bikes.

### Fitted line is in red ###
fa <- forecast(fitHW, 14)
plot(fa, lwd = 2, col = "blue", main = "HW Additive Prediction")

accuracy(fa)
                     ME     RMSE      MAE  MPE MAPE      MASE       ACF1
Training set -0.6762827 10.93165 8.317779 -Inf  Inf 0.8831715 0.09600309
Box.test(residuals(fitHW), lag = 7, type = "Ljung")

    Box-Ljung test

data:  residuals(fitHW)
X-squared = 5.6411, df = 7, p-value = 0.5822

The predicted model seems to work well because the observed p-value is higher than 0.05 which is a good indicator!

Now let’s attempt to then use the Holt-Winter’s additive model on all the other stations to predict the net change in bikes for the next 2 weeks (i.e. 1st Sep 14 - 14th Sep 14). After which, the results will be stored into a table and then used for the tool.

remove(startdt, enddt, sfct)
### Create an empty data frame to store all the values ###
final <- data.frame()
for (i in 1:length(sf)) {
    station <- sf[i]
    dt <- tripdata[tripdata$Start.Station == station | tripdata$End.Station == station,]
    startdt <- select(dt, Start.Station, Start.DatewTime) %>% filter(Start.Station == station)
    enddt <- select(dt, End.Station, End.DatewTime) %>% filter(End.Station == station)
    ### Extracts only the date and hour out ###
    startdt$Start.DatewTime <- as.POSIXct(format(as.POSIXct(startdt$Start.DatewTime),"%Y-%m-%d"))
    enddt$End.DatewTime <- as.POSIXct(format(as.POSIXct(enddt$End.DatewTime),"%Y-%m-%d"))
    ### Aggregate the number of trips by date and hour ###
    startdt <- aggregate(Start.Station ~ Start.DatewTime, data = startdt, FUN = length)
    enddt <- aggregate(End.Station ~ End.DatewTime, data = enddt, FUN = length)
    ### Create a column of sequential date and time from 1st Mar 14 to 31st Aug 14 ###
    datetime <- seq(from = as.POSIXct("2014-03-01"), to = as.POSIXct("2014-08-31"), by = "day")
    datetime <- data.frame(datetime)
    ### Merge all the subsequent columns together, replace NA values with 0 ###
    combdt <- merge(datetime, startdt, by.x = "datetime", by.y = "Start.DatewTime", all.x = TRUE)
    combdt$Start.Station[is.na(combdt$Start.Station)] <- 0
    combdt <- merge(combdt, enddt, by.x = "datetime", by.y = "End.DatewTime", all.x = TRUE)
    combdt$End.Station[is.na(combdt$End.Station)] <- 0
    ### The start.station column signifies number of bikes leaving the station, the end.station column signifies the number of bikes entering the station. To get the net change, we form a new column of Net = End.Station - Start.Station ###
    combdt <- mutate(combdt, Net = End.Station - Start.Station)
    
    combdt <- select(combdt, datetime, Net)
    combdts <- ts(combdt$Net, frequency = 7, start = c(1,3))
    ### Fit an additive Holt Winter's Additive model ###
    fitHW <- HoltWinters(combdts, seasonal = "additive")
    
    ### Predict the values for the next 14 days ###
    fa <- forecast(fitHW, 14)
    
    predicted <- as.character(as.numeric(fa$mean))
    datetime <- seq(from = as.POSIXct("2014-09-01"), to = as.POSIXct("2014-09-14"), by = "day")
    datetime <- as.character(datetime)
    station_name <- rep(station, 14)
    
    overall <- data.frame(cbind(station_name, datetime, predicted))
    final <- rbind(final,overall)
}
final$station_name <- as.character(final$station_name)
final$datetime <- as.character(final$datetime)
final$predicted <- as.numeric(as.character(final$predicted))
write.csv(final, file = "./final.csv")

Now that we have the final table, that consists of the station name, the date and the predicted change in number of bikes at that particular station and date, we are ready to use this CSV file for our tool!

What this tool would then help to achieve, is giving the manager the ability to know in advance, for a particular day, which station would have the highest number of incoming and highest number of outgoing bikes. The manager will then be able to assign and allocate employees to these stations and balance the bikes in a more efficient manner!

Numbers in positive will represent a net increase in number of bikes. Numbers in negative will represent a net decrease in number of bikes.

E.g. On September 12th 2014, We can expect a huge number of bikes entering San Francisco Caltrain (Townsend at 4th) at 46.81, on the other hand, we notice that there will be a large number of bikes leaving Embarcadero at Bryant. Hence, we should have most number of employees at these 2 stations to facilitate the balance of number of bikes.

4 Limitations of tool

This tool would have been better enhanced if the number of bikes was first available at 1st March 14, 00:00:00 and the number of slots available at each station. This would then be able to give a more accurate indicator of whether this particular bike station would get filled up soon.

E.g. if there is 25 bikes in the station on this day, and it is predicted to have a net change of 25 bikes the next day. This would certainly be a problem if the number of bike slots is 50, because this would indicate that the bike station would be filled up by the end of next day if the prediction is right! However, this wouldn’t be the case if the number of bike slots is 75 instead.

In addition, the prediction model could have been better optimized to fit the data. This would then result in a more accurate number.

Lastly, this tool could also be more accurate if we were given additional inputs such as the temperature, humidity level, etc. because weather conditions can definitely affect the number of trips as well.

LS0tCnRpdGxlOiAiQW5hbHlzaXMgb2YgU2FuIEZyYW5jaXNjbyBCYXkgQXJlYSBCaWtlIFNoYXJpbmcgUHJvZ3JhbSIKYXV0aG9yOiAiQWxzb24gWWFwIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKY29udGFjdDogYWxzb255YXAxODVAZ21haWwuY29tCi0tLQoKIyMgMSBJbnRyb2R1Y3Rpb24KCioqSW50cm9kdWN0aW9uIG9mIFNGIEJheSBBcmVhIEJpa2UgU2hhcmU6IGh0dHA6Ly93d3cuYmF5YXJlYWJpa2VzaGFyZS5jb20vICoqCgoqKkRhdGEgdXNlZDogWWVhciAxIERhdGEsIGZyb20gTWFyIDIwMTQgLSBBdWcgMjAxNCwgdHJpcHMgZGF0YSBPTkxZLioqCgpPbmUgb2YgdGhlIG1vc3QgZGVzaXJhYmxlIHdpc2hlcyBvZiB0aGUgcmlkZXJzIHdobyB1c2VzIHRoaXMgYmlrZS1zaGFyaW5nIHByb2dyYW0gaXMgdGhhdCB0aGV5IGFyZSBhYmxlIHRvIGdldCBhIGJpa2UgYXQgdGhlIG5lYXJlc3Qgc3RhdGlvbiBmcm9tIHRoZWlyIGN1cnJlbnQgbG9jYXRpb24gYW5kIHRoZW4gYWJsZSB0byBkb2NrIGF0IHRoZWlyIGRlc3RpbmF0aW9uIGJpa2Ugc3RhdGlvbiB1cG9uIGFycml2aW5nLgpIb3dldmVyLCB0aGlzIGlzIHVubGlrZWx5IHRoZSBjYXNlIGZvciBzb21lIGJlY2F1c2Ugb2YgdGhlIGlycmVndWxhcml0aWVzIGluIHBlb3BsZSBtb3ZlbWVudCBhY3Jvc3MgdGhlIGRheXMsIHRpbWluZ3Mgb2YgdGhlIGRheSAodGhpbmsgcGVhay1ob3VycyB0cmFmZmljKSBhbmQgdGhlIHN0YXJ0aW5nIGFuZCBlbmQgbG9jYXRpb24gKG1vc3QgcGVvcGxlIHdvdWxkIGJlIGhlYWRpbmcgdG93YXJkcyBjZW50cmFsIGFyZWEpLgoKVGhpcyBpcyBhIGhlYWRhY2hlIGZvciB0aGUgbWFuYWdlcnMgaW4gdGhpcyBiaWtlLXByb2dyYW0gYXMgdGhleSBzZWVrIHRvIGtlZXAgdGhlaXIgcmlkZXJzIGhhcHB5LiBXaGF0IGFyZSBzb21lIG9mIHRoZSBhY3Rpb25zIHRoYXQgdGhleSBjb3VsZCB0YWtlIHRvIGVuc3VyZSB0aGlzIHByb2JsZW0gaXMgbWl0aWdhdGVkPyBPbmUgb2YgdGhlIGN1cnJlbnQgcHJhY3RpY2VzIGlzIHRvIGFzc2lnbiBlbXBsb3llZXMgdG8gbW92ZSB0aGUgYmlrZXMgZnJvbSBhcmVhcyB3aXRoIGxlYXN0IGRlbWFuZCBhdCBjZXJ0YWluIHRpbWluZ3MgdG8gb3RoZXIgYXJlYXMgd2l0aCBtdWNoIGhpZ2hlciBkZW1hbmQuIEhvd2V2ZXIsIGhvdyB3b3VsZCBhIG1hbmFnZXIga25vdyB3aGljaCBzdGF0aW9uIGhhcyBoaWdoZXN0IGRlbWFuZCwgbGVhc3QgZGVtYW5kLCB3aGF0IHRpbWUgdG8gbW92ZT8gVG8gYWRkIG9uLCB0aGUgZGF5cyBvZiB0aGUgd2VlayBhbmQgd2hvIGFyZSB1c2luZyBpdCBhbHNvIHdvdWxkIG1hdHRlci4gSW4gZmFjdCwgdGhlcmUgYXJlIGFsc28gbGltaXRlZCBlbXBsb3llZXMgdG8gc2hpZnQgdGhlIGJpa2VzIGFyb3VuZCBvbiBhIGNlcnRhaW4gZGF5LiAKCkhlbmNlLCB0aGlzIHN1Ym1pc3Npb24gc2Vla3MgdG8gcmV2ZWFsIHNvbWUgaW5zaWdodHMgcmVsYXRlZCB0byB0aGlzIHByb2JsZW0gc3VjaCBhcyAtIHdoYXQgaXMgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgbnVtYmVyIG9mIHJpZGVzIGFjcm9zcyB0aGUgZGF0ZXMsIGRheXMgYW5kIHRpbWUuIFdoYXQgYXJlIHRoZSBtb3N0IGNvbW1vbiB0byBhbmQgZnJvIHBhdGhzPyBFdmVudHVhbGx5LCBmcm9tIHRoZXNlIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMsIHdlIHdpbGwgYmUgYWJsZSB0byB1bmRlcnN0YW5kIHRoZSBkYXRhIGJldHRlciBhbmQgdGhlbiBjcmVhdGUgYW4gYXBwcm9wcmlhdGUgcHJlZGljdGlvbiBtb2RlbCBhbmQgdG9vbCB0byBoZWxwIG1hbmFnZXIgdW5kZXJzdGFuZCB0aGUgc2l0dXRhdGlvbiBtdWNoIGJldHRlciEgVGhpcyB0b29sIHdpbGwgaGVscCB0byBpbmZvcm0gdGhlIG1hbmFnZXIgaW4gYWR2YW5jZSwgd2hpY2ggZGF5IHdvdWxkIGhhdmUgdGhlIGhpZ2hlc3QgaW5jcmVtZW50IGFuZCBmYWxsIGluIG51bWJlciBvZiBiaWtlcy4gSGVuY2UsIHRoZSBtYW5hZ2VyIHdpbGwgdGhlbiBiZSBhYmxlIHRvIHN0YXRpb24gaGlzIGVtcGxveWVlcyBhY3Jvc3MgdGhvc2Ugc3RhdGlvbnMgYW5kIHRyYW5zcG9ydCB0aGUgYmlrZXMgdG8gYmFsYW5jZSB0aGUgc2l0dWF0aW9uLiAKCipBZnRlciBjb25kdWN0aW5nIGEgR29vZ2xlIHNlYXJjaCBvbiB0aGUgc3RhdGlvbnMsIEkgcmVhbGl6ZWQgdGhhdCB0aGUgZGF0YSBjb25zaXN0cyBvZiByaWRlcyBhbmQgc3RhdGlvbnMgYWNyb3NzIGRpZmZlcmVudCBjaXRpZXMuIEhlbmNlLCBmb3IgdGhpcyBhbmFseXNpcywgSSB3b3VsZCBmb2N1cyBvbiB0aGUgKip0cmlwcyB0aGF0IHN0YXJ0IG9yIGVuZCBzdGF0aW9ucyB3aXRoaW4gU2FuIEZyYW5jaXNjbyBvbmx5KiogKGkuZS4gaW5jbHVkZXMgdHJpcHMgdGhhdCBzdGFydCBmcm9tIFNGIHN0YXRpb25zIGFuZCBlbmQgYXQgbm9uLVNGIHN0YXRpb25zLCB0cmlwcyB0aGF0IHN0YXJ0IGZyb20gbm9uLVNGIHN0YXRpb25zIGFuZCBlbmQgYXQgU0Ygc3RhdGlvbnMpKgoKU2V2ZXJhbCBoeXBvdGhlc2lzIHRoYXQgY291bGQgYmUgdW5jb3ZlcmVkOgoKKiBBcmUgdGhlcmUgbW9yZSByaWRlcnMgb24gdGhlIHdlZWtkYXlzIG9yIHdlZWtlbmRzPwoKKiBBcmUgdGhlcmUgbW9yZSBjdXN0b21lcnMgb3Igc3Vic2NyaWJlcnMgdXNpbmcgdGhlIHNlcnZpY2U/CgoqIEhvdyBkb2VzIHRoZSBkaXN0cmlidXRpb24gb2YgdHJpcHMgbG9vayBsaWtlIChhY3Jvc3MgdGltZSwgZGF5cyBvZiB3ZWVrLCBkYXRlcyBhbmQgY2VydGFpbiBzdGF0aW9ucyk/CgoqIFdoaWNoIGFyZSB0aGUgc3RhdGlvbnMgdGhhdCB0ZW5kIHRvIGJlIHRoZSBtb3N0ICdhY3RpdmUnPwoKIyMgMiBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzCgpUbyBzdGFydCBvZmYsIGxldCdzIGxvYWQgdGhlIG5lY2Vzc2FyeSBwYWNrYWdlcywgdGhlIENTViBkYXRhIGZpbGUgcHJvdmlkZWQgYW5kIHRoZSBzdGF0aW9ucyB3aXRoaW4gU2FuIEZyYW5jaXNjby4KCioqIFBsZWFzZSBwbGFjZSB0aGUgdHJpcGRhdGEgZmlsZSBpbiB0aGUgd29ya2luZyBkaXJlY3RvcnkgZm9yIHRoZSBjb2RlIHRvIHJ1biAqKgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShwbHlyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGZvcmVjYXN0KQp0cmlwZGF0YSA8LSByZWFkLmNzdigiLi90cmlwZGF0YS5jc3YiKQpzZiA8LSBjKCJTb3V0aCBWYW4gTmVzcyBhdCBNYXJrZXQiLCAiTWFya2V0IGF0IDEwdGgiLCAiU2FuIEZyYW5jaXNjbyBDaXR5IEhhbGwiLCAKICAgICAgICAiR29sZGVuIEdhdGUgYXQgUG9sayIsIAogICAgICAgICJDaXZpYyBDZW50ZXIgQkFSVCAoN3RoIGF0IE1hcmtldCkiLCAiUG93ZWxsIFN0cmVldCBCQVJUIiwgCiAgICAgICAgIlBvd2VsbCBhdCBQb3N0IChVbmlvbiBTcXVhcmUpIiwgIjV0aCBhdCBIb3dhcmQiLCAiTWFya2V0IGF0IDR0aCIsICJQb3N0IGF0IEtlYXJueSIsIAogICAgICAgICJDb21tZXJjaWFsIGF0IE1vbnRnb21lcnkiLCAiV2FzaGluZ3RvbiBhdCBLZWFybnkiLCAiR3JhbnQgQXZlbnVlIGF0IENvbHVtYnVzIEF2ZW51ZSIsIAogICAgICAgICJFbWJhcmNhZGVybyBhdCBTYW5zb21lIiwgIkVtYmFyY2FkZXJvIGF0IFZhbGxlam8iLCAiQnJvYWR3YXkgU3QgYXQgQmF0dGVyeSBTdCIsIAogICAgICAgICJEYXZpcyBhdCBKYWNrc29uIiwgIkNsYXkgYXQgQmF0dGVyeSIsICJIYXJyeSBCcmlkZ2VzIFBsYXphIChGZXJyeSBCdWlsZGluZykiLCAKICAgICAgICAiU3RldWFydCBhdCBNYXJrZXQiLCAiQmVhbGUgYXQgTWFya2V0IiwgIk1lY2hhbmljcyBQbGF6YSAoTWFya2V0IGF0IEJhdHRlcnkpIiwgCiAgICAgICAgIkVtYmFyY2FkZXJvIGF0IEZvbHNvbSIsICJUZW1wb3JhcnkgVHJhbnNiYXkgVGVybWluYWwgKEhvd2FyZCBhdCBCZWFsZSkiLCAKICAgICAgICAiTWFya2V0IGF0IFNhbnNvbWUiLCAiU3BlYXIgYXQgRm9sc29tIiwgIkhvd2FyZCBhdCAybmQiLCAiRW1iYXJjYWRlcm8gYXQgQnJ5YW50IiwgCiAgICAgICAgIlllcmJhIEJ1ZW5hIENlbnRlciBvZiB0aGUgQXJ0cyAoM3JkIEAgSG93YXJkKSIsICIybmQgYXQgRm9sc29tIiwgIjJuZCBhdCBTb3V0aCBQYXJrIiwgCiAgICAgICAgIjJuZCBhdCBUb3duc2VuZCIsICJTYW4gRnJhbmNpc2NvIENhbHRyYWluIChUb3duc2VuZCBhdCA0dGgpIiwgIlRvd25zZW5kIGF0IDd0aCIsCiAgICAgICAgIlNhbiBGcmFuY2lzY28gQ2FsdHJhaW4gMiAoMzMwIFRvd25zZW5kKSIpCmBgYAoKIyMjIDIuMSBSZW1vdmFsIG9mIHRyaXBzIHdpdGhpbiBub24tU0YgY2l0aWVzCmBgYHtyfQojIyMgQ3JlYXRlIDIgY29sdW1ucyB0aGF0IHRhZyB0aGUgc3RhcnQgYW5kIGVuZCBzdGF0aW9ucyBvbiB3aGV0aGVyIHRoZXkgYXJlIFNGIHN0YXRpb25zICMjIwp0cmlwZGF0YSRnbyA8LSBpZmVsc2UodHJpcGRhdGEkU3RhcnQuU3RhdGlvbiAlaW4lIHNmLCAiU0YiLCAiTm90U0YiKQp0cmlwZGF0YSRiYWNrIDwtIGlmZWxzZSh0cmlwZGF0YSRFbmQuU3RhdGlvbiAlaW4lIHNmLCAiU0YiLCAiTm90U0YiKQp0cmlwZGF0YSRnb2JhY2sgPC0gcGFzdGUodHJpcGRhdGEkZ28sIHRyaXBkYXRhJGJhY2ssIHNlcCA9ICIgdG8gIikKYGBgCgpIYXZpbmcgZG9uZSBzbywgbGV0J3MgaGF2ZSBhIGxvb2sgYXQgdGhlIGRpc3RyaWJ1dGlvbnMgb2YgdGhlIHRyaXBzIG9mIGludGVyLWNpdGllcyBhbmQgaW50cmEtY2l0aWVzIHN0YXRpb25zIHdpdGhpbiB0aGlzIGRhdGEuCmBgYHtyfQojIyMgR3JvdXAgdXAgYnkgdGhlICpnb2JhY2sqIHZhcmlhYmxlLCBiZWZvcmUgdGFsbHlpbmcgdGhlIHJlc3VsdHMgdXAgYW5kIG9yZGVyaW5nCnRyaXBkYXRhICU+JSBncm91cF9ieShnb2JhY2spICU+JSB0YWxseSgpICU+JSBhcnJhbmdlKGRlc2MobikpCmBgYApJbnRlcmVzdGluZyEgVGhlcmUgYXJlIDE5MDMxIHRyaXBzIG1hZGUgaW4gbm9uLVNGIGNpdGllcyB3aXRoaW4gdGhlIGRhdGEuIFdlIGhhdmUgYWxzbyBmb3VuZCBwZW9wbGUgd2hvIGhhdmUgaW4gZmFjdCB0cmF2ZWxsZWQgYWNyb3NzIGNpdGllcyAoMTQgc3VjaCBjYXNlcyksIG11c3QgaGF2ZSB0b29rIHRoZW0gc29tZSB0aW1lIHdoaWNoIEkgd29uZGVyIGhvdyBsb25nLCBidXQgdGhhdCB3aWxsIGJlIGZvciBhbm90aGVyIHRpbWUgOikgSW4gYWRkaXRpb24sIHRoaXMgd29ya3Mgb3V0IHdlbGwgaW4gb3VyIGZhdm91ciBiZWNhdXNlIDE0IGlzIGEgbmVnbGlnaWJsZSBudW1iZXIsIHRodXMgd2UgY2FuIHJlbW92ZSB0aGVtIHdpdGhvdXQgYWZmZWN0aW5nIHRoZSBudW1iZXJzIG11Y2ghCgpOb3csIGxldCdzIHJldGFpbiBvbmx5IHRob3NlIHRyaXBzIHRoYXQgYXJlIHdpdGhpbiAiU0YgdG8gU0YiLgpgYGB7cn0KdHJpcGRhdGEgPC0gZmlsdGVyKHRyaXBkYXRhLCBnb2JhY2sgPT0gJ1NGIHRvIFNGJykKIyMjIEFsc28gdG8gcmVtb3ZlIHRoZSB0YWdnZWQgY29sdW1ucyB0aGF0IHdlcmUgYWRkZWQgYXMgdGhleSBhcmUgbm8gbG9uZ2VyIHJlcXVpcmVkICMjIwp0cmlwZGF0YSA8LSB0cmlwZGF0YVssLWMoMTIsMTMsMTQpXQpgYGAKUmlnaHQgbm93LCBvdXIgZGF0YXNldCBjb250YWlucyBvZiB0cmlwcyB0aGF0IGFyZSBvbmx5IHdpdGhpbiBTRiBjaXR5LCBncmVhdCEgTm93IHdlIGNhbiBtb3ZlIG9uIHRvIHBlcmZvcm0gc29tZSBzaW1wbGUgZXhwbG9yYXRvcnkgYW5hbHlzaXMgdG8gc2VlIHdoYXQncyBiZWVuIGdvaW5nIG9uLgoKIyMjIDIuMiBGdXJ0aGVyIGNsZWFuaW5nIG9mIGRhdGFzZXQKYGBge3J9CiMjIyBDb252ZXJ0IHRoZSBTdGFydC5EYXRlIGFuZCBFbmQuRGF0ZSBjb2x1bW5zIGludG8gRGF0ZS10aW1lIGNsYXNzICMjIwpTdGFydERhdGUgPC0gc3RycHRpbWUodHJpcGRhdGEkU3RhcnQuRGF0ZSwgIiVtLyVkLyVZICVIOiVNIikKdHJpcGRhdGEkU3RhcnQuRGF0ZSA8LSBhcy5QT1NJWGN0KFN0YXJ0RGF0ZSkgCkVuZERhdGUgPC0gc3RycHRpbWUodHJpcGRhdGEkRW5kLkRhdGUsICIlbS8lZC8lWSAlSDolTSIpCnRyaXBkYXRhJEVuZC5EYXRlIDwtIGFzLlBPU0lYY3QoRW5kRGF0ZSkgCiMjIyBSZW1vdmUgdGhlIHVubmVjZXNzYXJ5IHN0b3JlZCB2YWx1ZXMgIyMjCnJlbW92ZShTdGFydERhdGUsRW5kRGF0ZSkKCiMjIyBDcmVhdGVzIGEgY29weSBvZiB0aGUgb3JpZ2luYWwgZGF0YXNldCAjIyMKb3JpZ2luYWwgPC0gdHJpcGRhdGEKCiMjIyBDcmVhdGVzIHNldmVyYWwgdXNlZnVsIGNvbHVtbnMgKGJyZWFrZG93biBzdGFydC9lbmQgZGF0ZSBieSBNb250aCwgRGF5LCBEYXkgb2YgV2VlayBmb3IgYW5hbHlzaXMgIyMjCnRyaXBkYXRhIDwtIG11dGF0ZSh0cmlwZGF0YSwgc2RhdGUgPSBkYXRlKFN0YXJ0LkRhdGUpLCBzbW9udGggPSBtb250aChTdGFydC5EYXRlLGxhYmVsID0gVFJVRSksIHNkYXkgPSBkYXkoU3RhcnQuRGF0ZSksIHN3ZGF5ID0gd2RheShTdGFydC5EYXRlLGxhYmVsID0gVFJVRSksIHNociA9IGhvdXIoU3RhcnQuRGF0ZSksIGVkYXRlID0gZGF0ZShTdGFydC5EYXRlKSwgZW1vbnRoID0gbW9udGgoRW5kLkRhdGUsbGFiZWwgPSBUUlVFKSwgZWRheSA9IGRheShFbmQuRGF0ZSksIGV3ZGF5ID0gd2RheShFbmQuRGF0ZSxsYWJlbCA9IFRSVUUpLCBlaHIgPSBob3VyKEVuZC5EYXRlKSkKCiMjIyBUbyB0dXJuIHRoZSBTdGFydC5EYXRlIGFuZCBFbmQuRGF0ZSBpbnRvIGNoYXJhY3RlciBmb3JtYXQgdG8gYXZvaWQgY29uZmxpY3RpbmcgaXNzdWVzIGluIHRhbGx5aW5nICMjIwp0cmlwZGF0YSRTdGFydC5EYXRlIDwtIGFzLmNoYXJhY3Rlcih0cmlwZGF0YSRTdGFydC5EYXRlKQp0cmlwZGF0YSRFbmQuRGF0ZSA8LSBhcy5jaGFyYWN0ZXIodHJpcGRhdGEkRW5kLkRhdGUpCmBgYAoKIyMjIDIuMyBEaXN0cmlidXRpb24gb2YgcmlkZXMgYnkgZGF5cyBvZiB3ZWVrLCB0aW1lLCBkYXRlcyBhbmQgc3Vic2NyaWJlciB0eXBlCgpXaXRoIHRoZSBjbGVhbmluZyB1cCBkb25lLCBsZXQncyBub3cgdGFsbHkgdXAgdGhlIG51bWJlciBvZiByaWRlcyBhY3Jvc3MgdGhlIHdlZWtkYXlzIGFuZCBzZWUgd2hhdCdzIHRoZSBkaXN0cmlidXRpb24gbGlrZSB0byBhbnN3ZXIgb3VyIGh5cG90aGVzaXMhCmBgYHtyfQojIyMgVGFsbHkgdXAgdGhlIG51bWJlciBvZiB0cmlwcyBmb3IgY2VydGFpbiB3ZWVrZGF5ICMjIwpjd2QgPC0gZGRwbHkodHJpcGRhdGEsIC4oc3dkYXkpLCB0YWxseSkKY3dkJHdrZGF5IDwtIGlmZWxzZShjd2Qkc3dkYXkgJWluJSBjKCJNb24iLCJUdWVzIiwiV2VkIiwiVGh1cnMiLCJGcmkiKSwgIldlZWtkYXkiLCJXZWVrZW5kIikKZ2dwbG90KGN3ZCwgYWVzKHggPSBzd2RheSwgeSA9IG4sIGZpbGwgPSB3a2RheSkpICsgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknKSArIGxhYnModGl0bGUgPSAiUmlkZXJzaGlwIG92ZXIgZGF5cyBvZiB3ZWVrIiwgeCA9ICJEYXlzIG9mIFdlZWsiLCB5ID0gIkNvdW50IikKYGBgClRoZSByZXN1bHQgaXMgYXMgZXhwZWN0ZWQsIHRoZSBudW1iZXIgb2YgcmlkZXMgb2NjdXJyZWQgbWFpbmx5IGR1cmluZyB3ZWVrZGF5cyBhbmQgZmVsbCB0byBhIGxvd2VyIG51bWJlciBkdXJpbmcgd2Vla2VuZHMuIEhlbmNlLCB3ZSB3b3VsZCBleHBlY3QgdGhlIGF2YWlsYWJpbGl0eSBvZiBiaWtlcyBwcm9ibGVtIHRvIG9jY3VyIG1vcmUgZHVyaW5nIHdlZWtkYXlzLgoKVGhpcyBicmluZ3MgbWUgdG8gdGhlIHBvaW50LCBjb3VsZCB0aGUgbnVtYmVyIG9mIG5vbi1zdWJzY3JpYmVycyBkcml2ZSB1cCB0aGUgdXNhZ2Ugb24gd2Vla2RheXM/IEFzIG11Y2ggYXMgd2Ugd2FudCB0byBjYXRlciB0byBib3RoIGdyb3VwcyBvZiBjdXN0b21lcnMsIGhvd2V2ZXIsIGlmIHRoZXJlIGlzIGEgbGFyZ2UgbnVtYmVyIG9mIG5vbi1zdWJzY3JpYmVycyB1c2luZyB0aGUgYmlrZXMsIHRoaXMgd291bGQgY2F1c2UgbXVjaCBkaXNwbGVhc3VyZSB0byBvdXIgc3Vic2NyaWJlcnMuIEhlbmNlLCB3ZSBtYXkgd2FudCB0byBpbmNyZWFzZSB0aGUgcHJpY2VzIGZvciBjdXN0b21lcnMgdXNhZ2Ugc3VjaCB0aGF0IHdlIGNhbiBhbGxvdyBtb3JlIHN1YnNjcmliZXJzIHRvIHVzZSBpbnN0ZWFkLgoKTGV0J3MgZmluZCBvdXQgaG93IGRvZXMgdGhlIHByb3BvcnRpb24gb2YgY3VzdG9tZXJzIHYucy4gc3Vic2NyaWJlcnMgbG9vayBsaWtlIGluIHRoZSBkaXN0cmlidXRpb24uCmBgYHtyfQpjd2RyaWRlciA8LSBkZHBseSh0cmlwZGF0YSwgLihzd2RheSxTdWJzY3JpYmVyLlR5cGUpLCB0YWxseSkKZ2dwbG90KGN3ZHJpZGVyLCBhZXMoeCA9IHN3ZGF5LCB5ID0gbiwgZmlsbCA9IFN1YnNjcmliZXIuVHlwZSkpICsgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknKSArIGxhYnModGl0bGUgPSAiUmlkZXJzaGlwIG92ZXIgZGF5cyBvZiB3ZWVrIGJ5IFN1YnNjcmliZXIgVHlwZSIsIHggPSAiRGF5cyBvZiBXZWVrIiwgeSA9ICJDb3VudCIpCmBgYAoKTG9va3MgbGlrZSB0aGUgY3VzdG9tZXJzIGRpZG4ndCBjcmVhdGUgaGlnaCBkZW1hbmQgZHVyaW5nIHRoZSB3ZWVrZGF5cy4uLiBIb3dldmVyLCBpdCB3b3VsZCBiZSB0b28gcXVpY2sgdG8gc2ltcGx5IGRpc21pc3MgdGhlbSBhcyBub3QgYSBjYXVzZS4gQ291bGQgdGhleSB1dGlsaXplIHRoZSBzZXJ2aWNlIG1vc3RseSBkdXJpbmcgdGhlIHBlYWsgaG91cnM/IElmIHNvLCB0aGlzIGNvdWxkIGNhdXNlIGEgcHJvYmxlbSBhcyB3ZWxsIQoKTm93IGxldCdzIG1vdmUgb24gdG8gZXhhbWluaW5nIHRoZSBkaXN0cmlidXRpb24gb2YgcmlkZXJzaGlwIG92ZXIgdGhlIGhvdXIgb2YgdGhlIGRheS4gV2Ugc2hhbGwgY29uc3RydWN0IHNpbWlsYXIgY29kZSBidXQgaW4gdGhlIGhvdXJzIGNvbnRleHQuCgpgYGB7cn0KIyMjIEp1c3Qgc29tZSBjbGVhbmluZyB1cCB0byBkbyAjIyMKcmVtb3ZlKGN3ZCxjd2RyaWRlcikKIyMjCmhycmlkZXIgPC0gZGRwbHkodHJpcGRhdGEsIC4oc2hyLFN1YnNjcmliZXIuVHlwZSksIHRhbGx5KQpnZ3Bsb3QoaHJyaWRlciwgYWVzKHggPSBzaHIsIHkgPSBuLCBmaWxsID0gU3Vic2NyaWJlci5UeXBlKSkgKyBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsgbGFicyh0aXRsZSA9ICJSaWRlcnNoaXAgb3ZlciB0aW1lIG9mIGRheSBieSBTdWJzY3JpYmVyIFR5cGUiLCB4ID0gIlRpbWUgb2YgZGF5IChocikiLCB5ID0gIkNvdW50IikKYGBgCgpXaGV3ISBMb29rcyBsaWtlIGN1c3RvbWVycyBhcmUgbm90IGNhdXNpbmcgdG9vIG11Y2ggb2YgYSBwcm9ibGVtIHRoZW4gaXQgc2VlbXMuLi4gSW4gYWRkaXRpb24sIHdlIGFyZSBhbHNvIGFibGUgdG8gc2VlIHRoZSBkaXN0cmlidXRpb24gb2YgdHJpcHMgb3ZlciB0aGUgdGltZSBvZiB0aGUgZGF5LiBGb3IgY3VyaW9zaXR5J3Mgc2FrZSwgbGV0J3MgY29tYmluZSBhbGwgdGhlc2UgZmFjdG9ycyB0b2dldGhlciBhbmQgc2VlIGhvdyBkb2VzIHJpZGVyc2hpcCB2YXJ5IG92ZXIgdGhlIGRheXMgb2Ygd2VlayBvdmVyIGhvdXIgb2YgdGhlIGRheSwgYnJva2VuIGRvd24gYnkgc3Vic2NyaWJlciB0eXBlIQoKYGBge3J9CndocmlkZXIgPC0gZGRwbHkodHJpcGRhdGEsIC4oc2hyLHN3ZGF5LFN1YnNjcmliZXIuVHlwZSksIHRhbGx5KQpnZ3Bsb3Qod2hyaWRlciwgYWVzKHggPSBzaHIsIHkgPSBuLCBmaWxsID0gU3Vic2NyaWJlci5UeXBlKSkgKyBmYWNldF9ncmlkKC4gfiBzd2RheSkgKyBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsgbGFicyh0aXRsZSA9ICJSaWRlcnNoaXAgb3ZlciBXRGF5IG92ZXIgVGltZSBieSBTdWJzY3JpYmVyIFR5cGUiLCB4ID0gIlRpbWUiLCB5ID0gIkNvdW50IikKYGBgCgoqKjMgcXVpY2sgcG9pbnRzIHRoYXQgd2UgY2FuIGluZmVyIGZyb20gdGhlIGdyYXBocyoqOgoKKiBUaGUgZGlzdHJpYnV0aW9uIG9mIHRyaXBzIG92ZXIgdGltZSBkdXJpbmcgd2Vla2RheXMgaXMgYXBwcm94aW1hdGVseSB0aGUgc2FtZSBhY3Jvc3MgYWxsIDUgZGF5cywgaGF2aW5nIHBlYWtzIGF0IDcgLSA5IGEubS4gYW5kIDQgLSA2IHAubS4KCiogVGhlIGRpc3RyaWJ1dGlvbiBvZiB0cmlwcyBvdmVyIHRoZSB3ZWVrZW5kIGlzLCBkaWZmZXJlbnQgZnJvbSB3ZWVrZGF5cyBidXQgc2ltaWxhciBhY3Jvc3MgYm90aCBkYXlzIQoKKiBUaGUgbnVtYmVyIG9mIHRyaXBzIHRha2VuIGJ5IGN1c3RvbWVycyBhcmUgcm91Z2hseSBtb3JlIHRoYW4gdGhlIG51bWJlciBvZiB0cmlwcyB0YWtlbiBieSBzdWJzY3JpYmVycyBvbiB3ZWVrZW5kcywgd2hpY2ggaXMgYSBnb29kIHNpZ24gOikKCkl0IGlzIGFsc28gaW50dWl0aXZlIHRvIGhhdmUgYSBsb29rIGF0IGhvdyBkb2VzIHRoZSByaWRlcnNoaXAgY2hhbmdlIG92ZXIgdGltZSB3aXRoaW4gdGhlIGdpdmVuIHBlcmlvZCBvZiBNYXJjaCB0byBBdWd1c3QsIGFuZCB0aGUgZWZmZWN0cyBvZiB3ZWVrZW5kcyBhbmQgaG9saWRheXMgaGF2ZSBvbiB0aGVtIQoKKipFeHRyYWN0ZWQgc2V2ZXJhbCBob2xpZGF5IGRhdGVzIGNlbGVicmF0ZWQgaW4gdGhlIFUuUy4gaW4gMjAxNCoqCgoqICoxMSBNYXkgMjAxNCAoU3VuKSAtIE1vdGhlcidzIERheSAqCgoqICoyNiBNYXkgMjAxNCAoTW9uKSAtIE1lbW9yaWFsIERheSoKCiogKjE1IEp1biAyMDE0IChTdW4pIC0gRmF0aGVyJ3MgRGF5KgoKKiAqMDQgSnVsIDIwMTQgKEZyaSkgLSBJbmRlcGVuZGVuY2UgRGF5KgoKYGBge3J9CiMjIyBTb21lIGNsZWFuaW5nIHVwIGFzIHVzdWFsICMjIwpyZW1vdmUoaHJyaWRlcix3aHJpZGVyKQojIyMgQ29udmVydCB0aGUgaW5pdGlhbCBzdGFydCBkYXRlIGJhY2sgdG8gUE9TSUNYbHQgY2xhc3MsIGFuZCByZW5hbWVkIGl0ICMjIwp0cmlwZGF0YSRTdGFydC5EYXRld1RpbWUgPC0gc3RycHRpbWUodHJpcGRhdGEkU3RhcnQuRGF0ZSwgIiVZLSVtLSVkICVIOiVNOiVTIikKdHJpcGRhdGEkRW5kLkRhdGV3VGltZSA8LSBzdHJwdGltZSh0cmlwZGF0YSRFbmQuRGF0ZSwgIiVZLSVtLSVkICVIOiVNOiVTIikKIyMjIEV4dHJhY3RlZCB0aGUgZGF0ZSBvdXQgYW5kIHR1cm5lZCBpbnRvIGNoYXJhY3RlciBjbGFzcyAjIyMKdHJpcGRhdGEkU3RhcnQuRGF0ZSA8LSBhcy5jaGFyYWN0ZXIoZGF0ZSh0cmlwZGF0YSRTdGFydC5EYXRld1RpbWUpKQp0cmlwZGF0YSRFbmQuRGF0ZSA8LSBhcy5jaGFyYWN0ZXIoZGF0ZSh0cmlwZGF0YSRFbmQuRGF0ZXdUaW1lKSkKIyMjIENvbnZlcnQgdGhlIERhdGVXVGltZSBiYWNrIHRvIGNoYXJhY3RlciBzbyB0aGF0IHdlIGNhbiB0YWxseSB3L28gaXNzdWVzICMjIwp0cmlwZGF0YSRTdGFydC5EYXRld1RpbWUgPC0gYXMuY2hhcmFjdGVyKHRyaXBkYXRhJFN0YXJ0LkRhdGV3VGltZSkKdHJpcGRhdGEkRW5kLkRhdGV3VGltZSA8LSBhcy5jaGFyYWN0ZXIodHJpcGRhdGEkRW5kLkRhdGV3VGltZSkKCiMjIyBUbyBhZGQgY29sdW1ucyBmb3IgaG9saWRheXMgYW5kIHdlZWtlbmRzICMjIwp0cmlwZGF0YSA8LSBtdXRhdGUodHJpcGRhdGEsIHdrZW5kID0gKHN3ZGF5ICVpbiUgYygiU2F0IiwiU3VuIikpLAogICAgICAgICAgICAgICAgICAgaG9sID0gKFN0YXJ0LkRhdGUgJWluJSBjKCIyMDE0LTA1LTExIiwiMjAxNC0wNS0yNiIsIjIwMTQtMDYtMTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIyMDE0LTA3LTA0IikpKQoKIyMjIFRhbGx5IHVwIHRoZSBudW1iZXIgb2YgdHJpcHMgYnkgc3RhcnQgZGF0ZSAjIyMKdHJpcGRhdGUgPC0gZGRwbHkodHJpcGRhdGEsIC4oU3RhcnQuRGF0ZSwgd2tlbmQsIGhvbCksIHRhbGx5KQojIyMgQ29udmVydCBpbnRvIGRhdGUgZm9ybWF0CnRyaXBkYXRlJFN0YXJ0LkRhdGUgPC0gZGF0ZSh0cmlwZGF0ZSRTdGFydC5EYXRlKQoKIyMjIFBsb3QgdGhlIGdyYXBoIHdpdGggcmVkIGxpbmVzIGZvciB3ZWVrZW5kcywgYmx1ZSBsaW5lcyBmb3IgaG9saWRheXMKZ2dwbG90KHRyaXBkYXRlLCBhZXMoU3RhcnQuRGF0ZSwgbikpICsgZ2VvbV9saW5lKCkgKyBnZW9tX2FyZWEoYWVzKHkgPSB3a2VuZCptYXgobikpLCBmaWxsID0icmVkIiwgYWxwaGEgPSAwLjMwKSArIGdlb21fYXJlYShhZXMoeSA9IGhvbCptYXgobikpLCBmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuMzApICsKICAgIGxhYnModGl0bGUgPSAiTm8uIG9mIHRyaXBzIGZyb20gTWFyIC0gQXVnIiwgeCA9ICJEYXRlIiwgeSA9ICJDb3VudCIpICsgdGhlbWVfbWluaW1hbCgpCmBgYApXaGVuIHBsb3R0ZWQgYWNyb3NzIHRoZSBkYXRlcyBmcm9tIDFzdCBNYXIgMTQgLSAzMXN0IEF1ZyAxNCwgdGhlcmUgaXMgYSBmYWxsIGluIHJpZGVyc2hpcCBvbiBldmVyeSB3ZWVrZW5kLCB0aGlzIGZpbmRpbmcgaXMgY29uc2lzdGVudCB3aXRoIG91ciBwcmV2aW91cyBmaW5kaW5ncy4gWWV0LCB0aGVyZSBhbHNvIHNlZW1zIHRvIGJlIGEgc2xpZ2h0IGRyb3AgZm9yIHRoZSBkYXlzIHdpdGggaG9saWRheXMsIGFsdGhvdWdoIGl0IHNlZW1zIG1pbm9yL25lZ2xpZ2libGUuCgpBbHNvLCB3ZSBub3RlIHRoYXQgdGhlcmUgaXMgYSBnZW5lcmFsIGluY3JlbWVudCB0cmVuZCBhcyB3ZWxsIHdoaWNoIGlzIGEgZ29vZCBzaWduISBUaGlzIGNvdWxkIHZlcnkgd2VsbCBiZSBkdWUgdG8gdGhlIHRyYW5zaXRpb24gb2Ygc2Vhc29uLCBmcm9tIHNwcmluZyB0byBzdW1tZXIsIHRodXMgdGhlIGluY3JlYXNlIGluIG51bWJlciBvZiB0cmlwcz8KCkluIGFkZGl0aW9uLCBpdCBzZWVtcyBsaWtlIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHRyaXBzIGZvbGxvdyBhIGdlbmVyYWwgdHJlbmQgd2l0aCBzZWFzb25hbGl0eSBlZmZlY3QsIHdoaWNoIHdlIGNvdWxkIGFjdHVhbGx5IHByZWRpY3QgdXNpbmcgdGhlIEhvbHRzLVdpbnRlciBtb2RlbC4uCgpBZnRlciBrbm93aW5nIGhvdyBkb2VzIHRoZSByaWRlcnNoaXAgbnVtYmVyIGNoYW5nZSBvdmVyIHNldmVyYWwgdmFyaWFibGVzLCBub3cgd2Ugd29uZGVyIGlmIHRoZSByaWRlcnNoaXAgZGlzcGxheXMgYSBob21vZ2Vub3VzIGRpc3RyaWJ1dGlvbiBhY3Jvc3MgdGhlIHBhdGhzIHRha2VuPyBUaGlzIGlzIG1vc3QgdW5saWtlbHkgdG8gYmUgdGhlIGNhc2UgYmVjYXVzZSB3ZSBzaG91bGQgZXhwZWN0IHNldmVyYWwgc3RhdGlvbnMgdGhhdCBhcmUgbmVhcmVyIHRvIHRoZSBjZW50cmUgdG8gYmUgbW9yZSBjcm93ZGVkL2Nvbmdlc3RlZCBkdXJpbmcgcGVha2hvdXJzIQoKV2Ugc2hhbGwgbm93IGludmVzdGlnYXRlIGJ5IGxvb2tpbmcgYXQgdGhlIHRvcCA1IHN0YXJ0aW5nIGFuZCBlbmRpbmcgbG9jYXRpb25zCmBgYHtyfQojIyMgR2VuZXJhbCBjbGVhbmluZyAjIyMKcmVtb3ZlKHRyaXBkYXRlKQojIyMgCnN0YXJ0cGF0aHMgPC0gZGRwbHkodHJpcGRhdGEsIC4oU3RhcnQuU3RhdGlvbiksIHRhbGx5KSAlPiUgYXJyYW5nZShkZXNjKG4pKQpoZWFkKHN0YXJ0cGF0aHMpCmVuZHBhdGhzIDwtIGRkcGx5KHRyaXBkYXRhLCAuKEVuZC5TdGF0aW9uKSwgdGFsbHkpICU+JSBhcnJhbmdlKGRlc2MobikpCmhlYWQoZW5kcGF0aHMpCmBgYAoKVGhlIHRvcCA2IGxvY2F0aW9ucyB0aGF0IGFwcGVhciBpbiB0aGUgU3RhcnQgU3RhdGlvbiBhbHNvIGFwcGVhciBpbiB0aGUgdG9wIDYgZW5kaW5nIGxvY2F0aW9ucy4gVGhpcyBpcyBub3Qgc3VycHJpc2luZyBiZWNhdXNlIGl0IGlzIHZlcnkgbGlrZWx5IHRoYXQgdGhlc2UgNiBzdGF0aW9ucyBhcmUgcGxhY2VzIHdoZXJlIHBlb3BsZSB1c3VhbGx5IGhlYWQgdG8gd29yayBhbmQgcmV0dXJuIGZyb20uIFRoaXMgaXMgaW4gZmFjdCBhIGdvb2Qgc2lnbiwgYmVjYXVzZSBpdCB3b3VsZCBiZSB3b3JyeWluZyBpZiB0aGVzZSAyIHJlc3VsdHMgZG8gbm90IG1hdGNoLiBUaGlzIHdvdWxkIGltcGx5IHRoYXQgcmlkZXJzIGFyZSBoZWFkaW5nIHRvIGEgbG9jYXRpb24sIHdoaWNoIHRoZXkgd291bGQgdmVyeSBsaWtlbHkgbm90IHJldHVybiBmcm9tLCBvciB2aWNlIHZlcnNhLiBUaGlzIHdvdWxkIHJlc3VsdCBpbiBhIGh1Z2UgaW1ibGFuYWNlIGluIHRoZSBudW1iZXIgb2YgYmlrZXMgYXQgZWFjaCBsb2NhdGlvbi4KCkhvd2V2ZXIsIHRoZSBoeXBvdGhlc2lzIHN0aWxsIHJlbWFpbnMhIElmIGV2ZXJ5b25lIGlzIHRyYXZlbGxpbmcgdG8sIGZvciBleGFtcGxlLCBTYW4gRnJhbmNpc2NvIENhbHRyYWluIChUb3duc2VuZCBhdCA0dGgpLCB0aGVuIHdlIHdpbGwgYmUgZXhwZWN0aW5nIHRoYXQgc3RhdGlvbiB0byBiZSBmdWxsIHF1aWNrbHkhCgpMZXQncyBzZWUgaWYgdGhpcyBpcyB0aGUgY2FzZSBmb3IgdGhlIHRvcCA2IHN0YXRpb25zIGFuZCBib3R0b20gNiBzdGF0aW9ucyBhY3Jvc3MgdGltZS4KCmBgYHtyfQpzdGFydGRhdGEgPC0gdHJpcGRhdGFbdHJpcGRhdGEkU3RhcnQuU3RhdGlvbiAlaW4lIHN0YXJ0cGF0aHMkU3RhcnQuU3RhdGlvblsxOjZdLF0gJT4lCiAgICBkZHBseSguKFN0YXJ0LlN0YXRpb24sc2hyLCB3a2VuZCksIHRhbGx5KSAKc3RhcnRkYXRhJHdrZW5kIDwtIGlmZWxzZShzdGFydGRhdGEkd2tlbmQgPT0gVFJVRSwgJ1dlZWtlbmQnLCAnV2Vla2RheScpCmdncGxvdChzdGFydGRhdGEsIGFlcyh4ID0gc2hyLCB5ID0gbiwgY29sb3VyID0gd2tlbmQpKSArIGZhY2V0X3dyYXAoIH4gU3RhcnQuU3RhdGlvbiwgbmNvbCA9IDIpICsgZ2VvbV9saW5lKGFlcyhncm91cCA9IHdrZW5kKSkgKyBnZW9tX3BvaW50KGFlcyhncm91cCA9IHdrZW5kKSkgKyBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiB0cmlwcyBzdGFydGluZyBmcm9tIGVhY2ggc3RhdGlvbiBhY3Jvc3MgdGltZSBieSB3ZWVrZGF5L3dlZWtlbmQiLCB4ID0gIlRpbWUgKGhyKSIsIHkgPSAiQ291bnQiKQpgYGAKV2Ugbm90aWNlIHRoYXQgZWFjaCBvZiB0aGUgNiBzdGF0aW9ucyBoYXZlIHZlcnkgZGlmZmVyZW50IGRpc3RyaWJ1dGlvbnMgYWNyb3NzIHRpbWUgYW5kIGRpZmZlcmVudCBtYWduaXR1ZGUgYXMgd2VsbC4gCgpMZXQncyBleGFtaW5lIHRoZSBjYXNlIGZvciB0aGUgdG9wIDYgZW5kaW5nIHN0YXRpb25zIHdpdGggaGlnaGVzdCBudW1iZXIgb2YgdHJpcHMuCgpgYGB7cn0KZW5kZGF0YSA8LSB0cmlwZGF0YVt0cmlwZGF0YSRFbmQuU3RhdGlvbiAlaW4lIGVuZHBhdGhzJEVuZC5TdGF0aW9uWzE6Nl0sXSAlPiUKICAgIGRkcGx5KC4oRW5kLlN0YXRpb24sc2hyLCB3a2VuZCksIHRhbGx5KSAKZW5kZGF0YSR3a2VuZCA8LSBpZmVsc2UoZW5kZGF0YSR3a2VuZCA9PSBUUlVFLCAnV2Vla2VuZCcsICdXZWVrZGF5JykKZ2dwbG90KGVuZGRhdGEsIGFlcyh4ID0gc2hyLCB5ID0gbiwgY29sb3VyID0gd2tlbmQpKSArIGZhY2V0X3dyYXAoIH4gRW5kLlN0YXRpb24sIG5jb2wgPSAyKSArIGdlb21fbGluZShhZXMoZ3JvdXAgPSB3a2VuZCkpICsgZ2VvbV9wb2ludChhZXMoZ3JvdXAgPSB3a2VuZCkpICsgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgdHJpcHMgc3RhcnRpbmcgZnJvbSBlYWNoIHN0YXRpb24gYWNyb3NzIHRpbWUgYnkgd2Vla2RheS93ZWVrZW5kIiwgeCA9ICJUaW1lIChocikiLCB5ID0gIkNvdW50IikKYGBgCgpCYXNlZCBvbiB0aGUgZ3JhcGhzIGRpc3BsYXllZCwgdGhpcyBkb2VzIGNvbmZpcm0gb3VyIGh5cG90aGVzaXMgdGhhdCB0aGVzZSB0ZW5kIHRvIGJlIHBsYWNlcyB3aGVuIHBlb3BsZSB3b3VsZCB1c3VhbGx5IGdvL2xlYXZlIGluIHRoZSBtb3JuaW5nIGFuZCB0aGVuIGdvL2xlYXZlIGF0IG5pZ2h0ISBUaGlzIGlzIGV2aWRlbnQgZnJvbSB0aGUgcmVmbGVjdGlvbiBvZiB0aGUgZmlyc3QgZ3JhcGggYWJvdXQgdGhlIHkgYXhpcywgd2hpY2ggcmVzZW1ibGVzIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHNlY29uZCBncmFwaC4KCkZyb20gaGVyZSwgd2UgY291bGQgaW4gZmFjdCBzZWUgdGhhdCBTYW4gRnJhbmNpc2NvIENhbHRyYWluIChUb3duc2VuZCBhdCA0dGgpLCBTYW4gRnJhbmNpc2NvIENhbHRyYWluIDIgKDMzMCBUb3duc2VuZCkgYW5kIEhhcnJ5IEJyaWRnZXMgUGxhemEgYXJlIHBsYWNlcyB3aGVyZSBwZW9wbGUgZWl0aGVyIGdvIGhvbWUgdG8gb3IgdG8gdGFrZSBvbiBhbm90aGVyIGZvcm0gb2YgdHJhbnNwb3J0IChtb3N0IGxpa2VseSB0aGUgY29tbXV0ZXIgdHJhaW4pIGluIHRoZSBldmVuaW5nIHRvIHNvbWUgd2hlcmUgZWxzZSAoaGlnaCBudW1iZXIgb2YgZGVwYXJ0dXJlcyBpbiB0aGUgZXZlbmluZykhIE9uIHRoZSBvdGhlciBlbmQsIHRoZSBvdGhlciAzIHN0YXRpb25zIGFyZSBwbGFjZXMgd2hlcmUgbWFueSBwZW9wbGUgYXJlIG1vc3QgbGlrZWx5IHRvIHdvcmsgYXQsIGhlbmNlIHRoZSBoaWdoIG51bWJlciBvZiBhcnJpdmFscyBpbiB0aGUgbW9ybmluZyBhbmQgaGlnaCBudW1iZXIgb2YgZGVwYXJ0dXJlcyBpbiB0aGUgZXZlbmluZy4KCkhlbmNlLCB3ZSBjYW4gdGhlbiBzZWUgaG93IHNldmVyYWwgZmFjdG9ycyBzdWNoIGFzIHRpbWUgb2YgdGhlIGRheSwgZGF5IG9mIHRoZSB3ZWVrLCBkYXRlLCBzdWJzY3JpYmVyIHR5cGUgY2FuIHRoZW4gYWZmZWN0IHRoZSBnZW5lcmFsIGRlbWFuZCBhbmQgc3VwcGx5IGFjcm9zcyB0aGUgc3RhdGlvbnMuIAoKIyMgMyBDcmVhdGlvbiBvZiB0aGUgcHJlZGljdGlvbiB0b29sCgpOZXh0IHN0ZXAgaGVyZSwgd2Ugd2lsbCBhdHRlbXB0IHRvIG1vZGVsIHRoZSBwcmVkaWN0ZWQgbmV0IGNoYW5nZSBvZiBudW1iZXIgb2YgYmlrZXMgYXQgdGhlIFNhbiBGcmFuY2lzY28gQ2FsdHJhaW4gKFRvd25zZW5kIGF0IDR0aCkgc3RhdGlvbiB1c2luZyB0aGUgZGF0YSBwcm92aWRlZC4gVGhpcyB3aWxsIGJlIGRvbmUgYnkgc2V2ZXJhbCBzdGVwcy4KCjEpIEV4dHJhY3RzIG91dCB0aGUgdHJpcHMgdGhhdCBlaXRoZXIgc3RhcnQgb3IgZW5kIGF0IHRoZSBzdGF0aW9uLgoKMikgU2VwYXJhdGUgdGhpcyBkYXRhc2V0IGludG8gMiAtIG9uZSBmb3IgdHJpcHMgc3RhcnRpbmcgYXQgdGhhdCBzdGF0aW9uLCBvbmUgZm9yIHRyaXBzIGVuZGluZyBhdCB0aGF0IHN0YXRpb24uCgozKSBFeHRyYWN0cyBvbmx5IHRoZSBkYXRlIGluZm9ybWF0aW9uIG91dCBvZiB0aGUgU3RhcnQuRGF0ZXdUaW1lIGFuZCBFbmQuRGF0ZXdUaW1lIGNvbHVtbnMuCgo0KSBBZ2dyZWdhdGUgdGhlIG51bWJlciBvZiB0cmlwcyBieSBkYXkuCgo1KSBDcmVhdGUgYSBzaW5nbGUgY29sdW1uIG9mIGFsbCB0aGUgZGF0ZXMgcnVubmluZyBmcm9tIDFzdCBNYXIgMTQgdG8gMzFzdCBBdWcgMTQuCgo2KSBNZXJnZSB0aGVzZSBkYXRhc2V0cyB0b2dldGhlciBieSBkYXRlLiAKCjcpIFRyaXBzIHRoYXQgc3RhcnQgYXQgdGhhdCBzdGF0aW9uIHNpZ25pZmllcyBudW1iZXIgb2YgYmlrZXMgbGVhdmluZyB0aGUgc3RhdGlvbiwgdHJpcHMgdGhhdCBlbmQgYXQgdGhhdCBzdGF0aW9uIHNpZ25pZmllcyB0aGUgbnVtYmVyIG9mIGJpa2VzIGxlYXZpbmcgdGhlIHN0YXRpb24uIFRodXMsIHRoZSBudW1iZXIgb2YgYmlrZXMgZW5kaW5nIGF0IHN0YXRpb24gZGVkdWN0ZWQgYnkgdHJpcHMgc3RhcnRpbmcgYXQgc3RhdGlvbiwgd291bGQgZ2l2ZSB1cyB0aGUgbmV0IGNoYW5nZSAoaS5lLiBhIG51bWJlciBvZiBwb3NpdGl2ZSA4IGF0IGEgcGFydGljdWxhciBkYXRlLCB3b3VsZCByZXByZXNlbnQgdGhhdCB0aGVyZSBpcyBhIG5ldCBpbmNyZWFzZSBvZiA4IGJpa2VzIGF0IHRoYXQgZGF0ZSkKCjgpIFBsb3QgdGhlIG5ldCBjaGFuZ2Ugb2YgYmlrZXMgYXQgdGhlIHRyaXAgb3ZlciBkYXRlLgoKOSkgRml0IGludG8gdGhlIEhvbHQtV2ludGVyJ3MgYWRkaXRpdmUgbW9kZWwgKGZvdW5kIHRvIGJlIHRoZSBiZXN0KQoKKiBUaGUgdG9vbCB3aWxsIHNob3cgdGhlIHRvcCAzIGhpZ2hlc3QgYW5kIHRvcCAzIGxvd2VzdCBudW1iZXIgb2YgaW5jb21pbmcgYmlrZXMgZm9yIGVhY2ggZGF5LiBJdCBpc24ndCBmZWFzaWJsZSB0byBzaG93IGJ5IGRheSBieSBob3VyLCBiZWNhdXNlIGl0IGlzIGFmdGVyYWxsIGFuIGVzdGltYXRlLiBJZiB3ZSB3ZXJlIHRvIGZvbGxvdyB0aGUgdG9vbCBzdHJpY3RseSwgdGhlbiB3ZSBjb3VsZCBoYXZlIGJlZW4gZWFzaWx5IGNhdWdodCBvZmZndWFyZCBpZiB0aGUgcHJlZGljdGlvbiBpc24ndCByaWdodC4gKgpgYGB7cn0KCiMjIyBHZW5lcmFsIGNsZWFuaW5nIHVwIGFzIHVzdWFsICMjIwpyZW1vdmUoc3RhcnRkYXRhLGVuZGRhdGEsc3RhcnRwYXRocyxlbmRwYXRocykKCiMjIyBFeHRyYWN0cyBvdXQgdGhlIGRhdGEgdGhhdCBzdGFydCBvciBlbmQgYXQgU2FuIEZyYW5jaXNjbyBDYWx0cmFpbiAoVG93bnNlbmQgYXQgNHRoKSAjIyMKc2ZjdCA8LSB0cmlwZGF0YVt0cmlwZGF0YSRTdGFydC5TdGF0aW9uICVpbiUgIlNhbiBGcmFuY2lzY28gQ2FsdHJhaW4gKFRvd25zZW5kIGF0IDR0aCkiIHwgdHJpcGRhdGEkRW5kLlN0YXRpb24gJWluJSAiU2FuIEZyYW5jaXNjbyBDYWx0cmFpbiAoVG93bnNlbmQgYXQgNHRoKSIsXQoKIyMjIFNwbGl0cyB0aGUgZGF0YSBzZXQgaW50byAyLCBvbmUgdGhhdCBzdGFydHMgYXQgdGhhdCBzdGF0aW9uLCBhbm90aGVyIG9uZSB0aGF0IGVuZHMgYXQgdGhlIHN0YXRpb24gIyMjCnN0YXJ0ZHQgPC0gc2VsZWN0KHNmY3QsIFN0YXJ0LlN0YXRpb24sIFN0YXJ0LkRhdGV3VGltZSkgJT4lIGZpbHRlcihTdGFydC5TdGF0aW9uID09ICJTYW4gRnJhbmNpc2NvIENhbHRyYWluIChUb3duc2VuZCBhdCA0dGgpIikKCmVuZGR0IDwtIHNlbGVjdChzZmN0LCBFbmQuU3RhdGlvbiwgRW5kLkRhdGV3VGltZSkgJT4lIGZpbHRlcihFbmQuU3RhdGlvbiA9PSAiU2FuIEZyYW5jaXNjbyBDYWx0cmFpbiAoVG93bnNlbmQgYXQgNHRoKSIpCgojIyMgRXh0cmFjdHMgb25seSB0aGUgZGF0ZSBhbmQgaG91ciBvdXQgIyMjCnN0YXJ0ZHQkU3RhcnQuRGF0ZXdUaW1lIDwtIGFzLlBPU0lYY3QoZm9ybWF0KGFzLlBPU0lYY3Qoc3RhcnRkdCRTdGFydC5EYXRld1RpbWUpLCAiJVktJW0tJWQiKSkKZW5kZHQkRW5kLkRhdGV3VGltZSA8LSBhcy5QT1NJWGN0KGZvcm1hdChhcy5QT1NJWGN0KGVuZGR0JEVuZC5EYXRld1RpbWUpLCAiJVktJW0tJWQiKSkKCiMjIyBBZ2dyZWdhdGUgdGhlIG51bWJlciBvZiB0cmlwcyBieSBkYXRlIGFuZCBob3VyICMjIwpzdGFydGR0IDwtIGFnZ3JlZ2F0ZShTdGFydC5TdGF0aW9uIH4gU3RhcnQuRGF0ZXdUaW1lLCBkYXRhID0gc3RhcnRkdCwgRlVOID0gbGVuZ3RoKQplbmRkdCA8LSBhZ2dyZWdhdGUoRW5kLlN0YXRpb24gfiBFbmQuRGF0ZXdUaW1lLCBkYXRhID0gZW5kZHQsIEZVTiA9IGxlbmd0aCkKCiMjIyBDcmVhdGUgYSBjb2x1bW4gb2Ygc2VxdWVudGlhbCBkYXRlIGFuZCB0aW1lIGZyb20gMXN0IE1hciAxNCB0byAzMXN0IEF1ZyAxNCAjIyMKZGF0ZXRpbWUgPC0gc2VxKGZyb20gPSBhcy5QT1NJWGN0KCIyMDE0LTAzLTAxIiksIHRvID0gYXMuUE9TSVhjdCgiMjAxNC0wOC0zMSIpLCBieSA9ICJkYXkiKQpkYXRldGltZSA8LSBkYXRhLmZyYW1lKGRhdGV0aW1lKQoKIyMjIE1lcmdlIGFsbCB0aGUgc3Vic2VxdWVudCBjb2x1bW5zIHRvZ2V0aGVyLCByZXBsYWNlIE5BIHZhbHVlcyB3aXRoIDAgIyMjCmNvbWJkdCA8LSBtZXJnZShkYXRldGltZSwgc3RhcnRkdCwgYnkueCA9ICJkYXRldGltZSIsIGJ5LnkgPSAiU3RhcnQuRGF0ZXdUaW1lIiwgYWxsLnggPSBUUlVFKQpjb21iZHQkU3RhcnQuU3RhdGlvbltpcy5uYShjb21iZHQkU3RhcnQuU3RhdGlvbildIDwtIDAKY29tYmR0IDwtIG1lcmdlKGNvbWJkdCwgZW5kZHQsIGJ5LnggPSAiZGF0ZXRpbWUiLCBieS55ID0gIkVuZC5EYXRld1RpbWUiLCBhbGwueCA9IFRSVUUpCmNvbWJkdCRFbmQuU3RhdGlvbltpcy5uYShjb21iZHQkRW5kLlN0YXRpb24pXSA8LSAwCgojIyMgVGhlIHN0YXJ0LnN0YXRpb24gY29sdW1uIHNpZ25pZmllcyBudW1iZXIgb2YgYmlrZXMgbGVhdmluZyB0aGUgc3RhdGlvbiwgdGhlIGVuZC5zdGF0aW9uIGNvbHVtbiBzaWduaWZpZXMgdGhlIG51bWJlciBvZiBiaWtlcyBlbnRlcmluZyB0aGUgc3RhdGlvbi4gVG8gZ2V0IHRoZSBuZXQgY2hhbmdlLCB3ZSBmb3JtIGEgbmV3IGNvbHVtbiBvZiBOZXQgPSBFbmQuU3RhdGlvbiAtIFN0YXJ0LlN0YXRpb24gIyMjCmNvbWJkdCA8LSBtdXRhdGUoY29tYmR0LCBOZXQgPSBFbmQuU3RhdGlvbiAtIFN0YXJ0LlN0YXRpb24pCgojIyMgSG93IHRoZSBkYXRhIGxvb2tzIGxpa2UgZm9yIGhlYWQgYW5kIHRhaWwgIyMjCmhlYWQoY29tYmR0KQp0YWlsKGNvbWJkdCkKCiMjIyBQbG90IHRoZSBkYXRhIHRvIHNlZSB0aGUgZGlzdHJpYnV0aW9uIG9mIG5ldCBjaGFuZ2UgaW4gbnVtYmVyIG9mIGJpa2VzIGFjcm9zcyBkYXRlIGFuZCBob3VyICMjIwpnZ3Bsb3QoY29tYmR0LCBhZXMoeCA9IGRhdGV0aW1lLCB5ID0gTmV0KSkgKyBnZW9tX2xpbmUoKSArIGxhYnModGl0bGUgPSAiTmV0IGNoYW5nZSBvZiBiaWtlcyBvdmVyIGRhdGUiLCB4ID0gIk5ldCBjaGFuZ2Ugb2YgYmlrZXMiLCB5ID0gIkRhdGUiKQoKYGBgCldoYXQgd2UgaGF2ZSBub3RpY2VkIGhlcmUgaXMgdGhhdCB0aGUgbnVtYmVyIG9mIHRyaXBzIGRlZmluaXRlbHkgZm9sbG93cyBhIHBhdHRlcm4sIGR1ZSB0byB0aGUgY2hhbmdlIGluIHVzYWdlIGZyb20gd2Vla2RheXMgdG8gd2Vla2VuZC4gSGVuY2UsIHdlIHRlbmQgdG8gbm90aWNlIDQgcGVhayBwZXJpb2RzIGluIGVhY2ggbW9udGggKHRoZSA0IHNldCBvZiB3ZWVrZGF5cyBpbiBlYWNoIG1vbnRoKS4gSW4gYWRkaXRpb24sIGl0IGFsc28gc2VlbXMgdGhhdCB0aGVyZSBpcyBhIGxhcmdlciB2YXJpYXRpb24gb2YgbmV0IGNoYW5nZSBhcyB0aW1lIHByb2dyZXNzZXMuCgpXaXRoIHRoZXNlIGZpbmRpbmdzIGluIG1pbmQsIHdlIGNhbiBub3cgZGVmaW5pdGVseSBhdHRlbXB0IHRvIHBsb3QgSG9sdC1XaW50ZXJzIG1vZGVsIHRvIHByZWRpY3Qgd2hhdCBjb3VsZCB0aGUgbmV0IGNoYW5nZSBiZSBpbiB0aGUgdXBjb21pbmcgbW9udGghCgpgYGB7cn0KY29tYmR0IDwtIHNlbGVjdChjb21iZHQsIGRhdGV0aW1lLCBOZXQpCmNvbWJkdHMgPC0gdHMoY29tYmR0JE5ldCwgZnJlcXVlbmN5ID0gNywgc3RhcnQgPSBjKDEsMykpCgojIyMgRml0IGFuIGFkZGl0aXZlIEhvbHQgV2ludGVyJ3MgQWRkaXRpdmUgbW9kZWwgIyMjCmZpdEhXIDwtIEhvbHRXaW50ZXJzKGNvbWJkdHMsIHNlYXNvbmFsID0gImFkZGl0aXZlIikKcGxvdChmaXRIVywgbHdkID0gMiwgY29sID0gImJsdWUiLCBtYWluID0gIkhvbHQgV2ludGVyIEFkZGl0aXZlIikKYGBgClRoZSBvcmlnaW5hbCBwbG90IGlzIGluIGJsdWUsIHRoZSBwbG90dGVkIGdyYXBoIGlzIGluIHJlZC4gTG9va3MgbGlrZSB0aGUgZml0dGVkIG1vZGVsIHNlZW1zIHRvIGJlIGRvaW5nIGRlY2VudCwgYW5kIG11Y2ggYmV0dGVyIGF0IHRoZSBsYXRlciBwb2ludHMhIE5vdyBsZXQncyBhdHRlbXB0IHRvIHByZWRpY3QgYW5kIHBsb3QgdGhlIG5leHQgMiB3ZWVrcyBuZXQgY2hhbmdlIGluIGJpa2VzLgpgYGB7cn0KIyMjIEZpdHRlZCBsaW5lIGlzIGluIHJlZCAjIyMKZmEgPC0gZm9yZWNhc3QoZml0SFcsIDE0KQpwbG90KGZhLCBsd2QgPSAyLCBjb2wgPSAiYmx1ZSIsIG1haW4gPSAiSFcgQWRkaXRpdmUgUHJlZGljdGlvbiIpCgphY2N1cmFjeShmYSkKQm94LnRlc3QocmVzaWR1YWxzKGZpdEhXKSwgbGFnID0gNywgdHlwZSA9ICJManVuZyIpCmBgYApUaGUgcHJlZGljdGVkIG1vZGVsIHNlZW1zIHRvIHdvcmsgd2VsbCBiZWNhdXNlIHRoZSBvYnNlcnZlZCBwLXZhbHVlIGlzIGhpZ2hlciB0aGFuIDAuMDUgd2hpY2ggaXMgYSBnb29kIGluZGljYXRvciEKCk5vdyBsZXQncyBhdHRlbXB0IHRvIHRoZW4gdXNlIHRoZSBIb2x0LVdpbnRlcidzIGFkZGl0aXZlIG1vZGVsIG9uIGFsbCB0aGUgb3RoZXIgc3RhdGlvbnMgdG8gcHJlZGljdCB0aGUgbmV0IGNoYW5nZSBpbiBiaWtlcyBmb3IgdGhlIG5leHQgMiB3ZWVrcyAoaS5lLiAxc3QgU2VwIDE0IC0gMTR0aCBTZXAgMTQpLiBBZnRlciB3aGljaCwgdGhlIHJlc3VsdHMgd2lsbCBiZSBzdG9yZWQgaW50byBhIHRhYmxlIGFuZCB0aGVuIHVzZWQgZm9yIHRoZSB0b29sLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnJlbW92ZShzdGFydGR0LCBlbmRkdCwgc2ZjdCkKCiMjIyBDcmVhdGUgYW4gZW1wdHkgZGF0YSBmcmFtZSB0byBzdG9yZSBhbGwgdGhlIHZhbHVlcyAjIyMKCmZpbmFsIDwtIGRhdGEuZnJhbWUoKQoKZm9yIChpIGluIDE6bGVuZ3RoKHNmKSkgewogICAgc3RhdGlvbiA8LSBzZltpXQogICAgZHQgPC0gdHJpcGRhdGFbdHJpcGRhdGEkU3RhcnQuU3RhdGlvbiA9PSBzdGF0aW9uIHwgdHJpcGRhdGEkRW5kLlN0YXRpb24gPT0gc3RhdGlvbixdCiAgICBzdGFydGR0IDwtIHNlbGVjdChkdCwgU3RhcnQuU3RhdGlvbiwgU3RhcnQuRGF0ZXdUaW1lKSAlPiUgZmlsdGVyKFN0YXJ0LlN0YXRpb24gPT0gc3RhdGlvbikKICAgIGVuZGR0IDwtIHNlbGVjdChkdCwgRW5kLlN0YXRpb24sIEVuZC5EYXRld1RpbWUpICU+JSBmaWx0ZXIoRW5kLlN0YXRpb24gPT0gc3RhdGlvbikKICAgICMjIyBFeHRyYWN0cyBvbmx5IHRoZSBkYXRlIGFuZCBob3VyIG91dCAjIyMKICAgIHN0YXJ0ZHQkU3RhcnQuRGF0ZXdUaW1lIDwtIGFzLlBPU0lYY3QoZm9ybWF0KGFzLlBPU0lYY3Qoc3RhcnRkdCRTdGFydC5EYXRld1RpbWUpLCIlWS0lbS0lZCIpKQogICAgZW5kZHQkRW5kLkRhdGV3VGltZSA8LSBhcy5QT1NJWGN0KGZvcm1hdChhcy5QT1NJWGN0KGVuZGR0JEVuZC5EYXRld1RpbWUpLCIlWS0lbS0lZCIpKQoKICAgICMjIyBBZ2dyZWdhdGUgdGhlIG51bWJlciBvZiB0cmlwcyBieSBkYXRlIGFuZCBob3VyICMjIwogICAgc3RhcnRkdCA8LSBhZ2dyZWdhdGUoU3RhcnQuU3RhdGlvbiB+IFN0YXJ0LkRhdGV3VGltZSwgZGF0YSA9IHN0YXJ0ZHQsIEZVTiA9IGxlbmd0aCkKICAgIGVuZGR0IDwtIGFnZ3JlZ2F0ZShFbmQuU3RhdGlvbiB+IEVuZC5EYXRld1RpbWUsIGRhdGEgPSBlbmRkdCwgRlVOID0gbGVuZ3RoKQoKICAgICMjIyBDcmVhdGUgYSBjb2x1bW4gb2Ygc2VxdWVudGlhbCBkYXRlIGFuZCB0aW1lIGZyb20gMXN0IE1hciAxNCB0byAzMXN0IEF1ZyAxNCAjIyMKICAgIGRhdGV0aW1lIDwtIHNlcShmcm9tID0gYXMuUE9TSVhjdCgiMjAxNC0wMy0wMSIpLCB0byA9IGFzLlBPU0lYY3QoIjIwMTQtMDgtMzEiKSwgYnkgPSAiZGF5IikKICAgIGRhdGV0aW1lIDwtIGRhdGEuZnJhbWUoZGF0ZXRpbWUpCgogICAgIyMjIE1lcmdlIGFsbCB0aGUgc3Vic2VxdWVudCBjb2x1bW5zIHRvZ2V0aGVyLCByZXBsYWNlIE5BIHZhbHVlcyB3aXRoIDAgIyMjCiAgICBjb21iZHQgPC0gbWVyZ2UoZGF0ZXRpbWUsIHN0YXJ0ZHQsIGJ5LnggPSAiZGF0ZXRpbWUiLCBieS55ID0gIlN0YXJ0LkRhdGV3VGltZSIsIGFsbC54ID0gVFJVRSkKICAgIGNvbWJkdCRTdGFydC5TdGF0aW9uW2lzLm5hKGNvbWJkdCRTdGFydC5TdGF0aW9uKV0gPC0gMAogICAgY29tYmR0IDwtIG1lcmdlKGNvbWJkdCwgZW5kZHQsIGJ5LnggPSAiZGF0ZXRpbWUiLCBieS55ID0gIkVuZC5EYXRld1RpbWUiLCBhbGwueCA9IFRSVUUpCiAgICBjb21iZHQkRW5kLlN0YXRpb25baXMubmEoY29tYmR0JEVuZC5TdGF0aW9uKV0gPC0gMAoKICAgICMjIyBUaGUgc3RhcnQuc3RhdGlvbiBjb2x1bW4gc2lnbmlmaWVzIG51bWJlciBvZiBiaWtlcyBsZWF2aW5nIHRoZSBzdGF0aW9uLCB0aGUgZW5kLnN0YXRpb24gY29sdW1uIHNpZ25pZmllcyB0aGUgbnVtYmVyIG9mIGJpa2VzIGVudGVyaW5nIHRoZSBzdGF0aW9uLiBUbyBnZXQgdGhlIG5ldCBjaGFuZ2UsIHdlIGZvcm0gYSBuZXcgY29sdW1uIG9mIE5ldCA9IEVuZC5TdGF0aW9uIC0gU3RhcnQuU3RhdGlvbiAjIyMKICAgIGNvbWJkdCA8LSBtdXRhdGUoY29tYmR0LCBOZXQgPSBFbmQuU3RhdGlvbiAtIFN0YXJ0LlN0YXRpb24pCiAgICAKICAgIGNvbWJkdCA8LSBzZWxlY3QoY29tYmR0LCBkYXRldGltZSwgTmV0KQogICAgY29tYmR0cyA8LSB0cyhjb21iZHQkTmV0LCBmcmVxdWVuY3kgPSA3LCBzdGFydCA9IGMoMSwzKSkKCiAgICAjIyMgRml0IGFuIGFkZGl0aXZlIEhvbHQgV2ludGVyJ3MgQWRkaXRpdmUgbW9kZWwgIyMjCiAgICBmaXRIVyA8LSBIb2x0V2ludGVycyhjb21iZHRzLCBzZWFzb25hbCA9ICJhZGRpdGl2ZSIpCiAgICAKICAgICMjIyBQcmVkaWN0IHRoZSB2YWx1ZXMgZm9yIHRoZSBuZXh0IDE0IGRheXMgIyMjCiAgICBmYSA8LSBmb3JlY2FzdChmaXRIVywgMTQpCiAgICAKICAgIHByZWRpY3RlZCA8LSBhcy5jaGFyYWN0ZXIoYXMubnVtZXJpYyhmYSRtZWFuKSkKICAgIGRhdGV0aW1lIDwtIHNlcShmcm9tID0gYXMuUE9TSVhjdCgiMjAxNC0wOS0wMSIpLCB0byA9IGFzLlBPU0lYY3QoIjIwMTQtMDktMTQiKSwgYnkgPSAiZGF5IikKICAgIGRhdGV0aW1lIDwtIGFzLmNoYXJhY3RlcihkYXRldGltZSkKICAgIHN0YXRpb25fbmFtZSA8LSByZXAoc3RhdGlvbiwgMTQpCiAgICAKICAgIG92ZXJhbGwgPC0gZGF0YS5mcmFtZShjYmluZChzdGF0aW9uX25hbWUsIGRhdGV0aW1lLCBwcmVkaWN0ZWQpKQogICAgZmluYWwgPC0gcmJpbmQoZmluYWwsb3ZlcmFsbCkKfQoKZmluYWwkc3RhdGlvbl9uYW1lIDwtIGFzLmNoYXJhY3RlcihmaW5hbCRzdGF0aW9uX25hbWUpCmZpbmFsJGRhdGV0aW1lIDwtIGFzLmNoYXJhY3RlcihmaW5hbCRkYXRldGltZSkKZmluYWwkcHJlZGljdGVkIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGZpbmFsJHByZWRpY3RlZCkpCndyaXRlLmNzdihmaW5hbCwgZmlsZSA9ICIuL2ZpbmFsLmNzdiIpCmBgYApOb3cgdGhhdCB3ZSBoYXZlIHRoZSBmaW5hbCB0YWJsZSwgdGhhdCBjb25zaXN0cyBvZiB0aGUgc3RhdGlvbiBuYW1lLCB0aGUgZGF0ZSBhbmQgdGhlIHByZWRpY3RlZCBjaGFuZ2UgaW4gbnVtYmVyIG9mIGJpa2VzIGF0IHRoYXQgcGFydGljdWxhciBzdGF0aW9uIGFuZCBkYXRlLCB3ZSBhcmUgcmVhZHkgdG8gdXNlIHRoaXMgQ1NWIGZpbGUgZm9yIG91ciB0b29sIQoKKipXaGF0IHRoaXMgdG9vbCB3b3VsZCB0aGVuIGhlbHAgdG8gYWNoaWV2ZSwgaXMgZ2l2aW5nIHRoZSBtYW5hZ2VyIHRoZSBhYmlsaXR5IHRvIGtub3cgaW4gYWR2YW5jZSwgZm9yIGEgcGFydGljdWxhciBkYXksIHdoaWNoIHN0YXRpb24gd291bGQgaGF2ZSB0aGUgaGlnaGVzdCBudW1iZXIgb2YgaW5jb21pbmcgYW5kIGhpZ2hlc3QgbnVtYmVyIG9mIG91dGdvaW5nIGJpa2VzLiBUaGUgbWFuYWdlciB3aWxsIHRoZW4gYmUgYWJsZSB0byBhc3NpZ24gYW5kIGFsbG9jYXRlIGVtcGxveWVlcyB0byB0aGVzZSBzdGF0aW9ucyBhbmQgYmFsYW5jZSB0aGUgYmlrZXMgaW4gYSBtb3JlIGVmZmljaWVudCBtYW5uZXIhKioKCioqTnVtYmVycyBpbiBwb3NpdGl2ZSB3aWxsIHJlcHJlc2VudCBhIG5ldCBpbmNyZWFzZSBpbiBudW1iZXIgb2YgYmlrZXMuKioKKipOdW1iZXJzIGluIG5lZ2F0aXZlIHdpbGwgcmVwcmVzZW50IGEgbmV0IGRlY3JlYXNlIGluIG51bWJlciBvZiBiaWtlcy4qKgoKKkUuZy4gT24gU2VwdGVtYmVyIDEydGggMjAxNCwgV2UgY2FuIGV4cGVjdCBhIGh1Z2UgbnVtYmVyIG9mIGJpa2VzIGVudGVyaW5nIFNhbiBGcmFuY2lzY28gQ2FsdHJhaW4gKFRvd25zZW5kIGF0IDR0aCkgYXQgNDYuODEsIG9uIHRoZSBvdGhlciBoYW5kLCB3ZSBub3RpY2UgdGhhdCB0aGVyZSB3aWxsIGJlIGEgbGFyZ2UgbnVtYmVyIG9mIGJpa2VzIGxlYXZpbmcgRW1iYXJjYWRlcm8gYXQgQnJ5YW50LiBIZW5jZSwgd2Ugc2hvdWxkIGhhdmUgbW9zdCBudW1iZXIgb2YgZW1wbG95ZWVzIGF0IHRoZXNlIDIgc3RhdGlvbnMgdG8gZmFjaWxpdGF0ZSB0aGUgYmFsYW5jZSBvZiBudW1iZXIgb2YgYmlrZXMuKgoKIyMgNCBMaW1pdGF0aW9ucyBvZiB0b29sClRoaXMgdG9vbCB3b3VsZCBoYXZlIGJlZW4gYmV0dGVyIGVuaGFuY2VkIGlmIHRoZSBudW1iZXIgb2YgYmlrZXMgd2FzIGZpcnN0IGF2YWlsYWJsZSBhdCAxc3QgTWFyY2ggMTQsIDAwOjAwOjAwIGFuZCB0aGUgbnVtYmVyIG9mIHNsb3RzIGF2YWlsYWJsZSBhdCBlYWNoIHN0YXRpb24uIFRoaXMgd291bGQgdGhlbiBiZSBhYmxlIHRvIGdpdmUgYSBtb3JlIGFjY3VyYXRlIGluZGljYXRvciBvZiB3aGV0aGVyIHRoaXMgcGFydGljdWxhciBiaWtlIHN0YXRpb24gd291bGQgZ2V0IGZpbGxlZCB1cCBzb29uLgoKRS5nLiBpZiB0aGVyZSBpcyAyNSBiaWtlcyBpbiB0aGUgc3RhdGlvbiBvbiB0aGlzIGRheSwgYW5kIGl0IGlzIHByZWRpY3RlZCB0byBoYXZlIGEgbmV0IGNoYW5nZSBvZiAyNSBiaWtlcyB0aGUgbmV4dCBkYXkuIFRoaXMgd291bGQgY2VydGFpbmx5IGJlIGEgcHJvYmxlbSBpZiB0aGUgbnVtYmVyIG9mIGJpa2Ugc2xvdHMgaXMgNTAsIGJlY2F1c2UgdGhpcyB3b3VsZCBpbmRpY2F0ZSB0aGF0IHRoZSBiaWtlIHN0YXRpb24gd291bGQgYmUgZmlsbGVkIHVwIGJ5IHRoZSBlbmQgb2YgbmV4dCBkYXkgaWYgdGhlIHByZWRpY3Rpb24gaXMgcmlnaHQhIEhvd2V2ZXIsIHRoaXMgd291bGRuJ3QgYmUgdGhlIGNhc2UgaWYgdGhlIG51bWJlciBvZiBiaWtlIHNsb3RzIGlzIDc1IGluc3RlYWQuCgpJbiBhZGRpdGlvbiwgdGhlIHByZWRpY3Rpb24gbW9kZWwgY291bGQgaGF2ZSBiZWVuIGJldHRlciBvcHRpbWl6ZWQgdG8gZml0IHRoZSBkYXRhLiBUaGlzIHdvdWxkIHRoZW4gcmVzdWx0IGluIGEgbW9yZSBhY2N1cmF0ZSBudW1iZXIuCgpMYXN0bHksIHRoaXMgdG9vbCBjb3VsZCBhbHNvIGJlIG1vcmUgYWNjdXJhdGUgaWYgd2Ugd2VyZSBnaXZlbiBhZGRpdGlvbmFsIGlucHV0cyBzdWNoIGFzIHRoZSB0ZW1wZXJhdHVyZSwgaHVtaWRpdHkgbGV2ZWwsIGV0Yy4gYmVjYXVzZSB3ZWF0aGVyIGNvbmRpdGlvbnMgY2FuIGRlZmluaXRlbHkgYWZmZWN0IHRoZSBudW1iZXIgb2YgdHJpcHMgYXMgd2VsbC4=