Set Up
The data used in this analysis comes from NYC OpenData, which
provides monthly records of Citi Bike usage. Even though Jersey City is
in New Jersey, Citi Bike’s operations are largely centered in New York
City, and thus the data is managed through NYC’s system. For this study,
we focused on bikeshare trips in Jersey City during August 2024,
capturing information about the starting and ending stations for each
ride. Additionally, we acquired the census tract geometries for all of
Hudson County, which encompasses Jersey City, using the tidycensus
package.
nycbike <- read.csv("/Users/bin/Documents/MUSA/MUSA 5080/JC-202308-citibike-tripdata.csv")
njCensus <- get_acs(geography = "tract",
variables = c("B01003_001", "B19013_001",
"B02001_002", "B08013_001",
"B08012_001", "B08301_001",
"B08301_010", "B01002_001"),
year = 2021,
state = "NJ",
geometry = TRUE,
county=c("Hudson"),
output = "wide") %>%
rename(Total_Pop = B01003_001E,
Med_Inc = B19013_001E,
Med_Age = B01002_001E,
White_Pop = B02001_002E,
Travel_Time = B08013_001E,
Num_Commuters = B08012_001E,
Means_of_Transport = B08301_001E,
Total_Public_Trans = B08301_010E) %>%
select(Total_Pop, Med_Inc, White_Pop, Travel_Time,
Means_of_Transport, Total_Public_Trans,
Med_Age,
GEOID, geometry) %>%
mutate(Percent_White = White_Pop / Total_Pop,
Mean_Commute_Time = Travel_Time / Total_Public_Trans,
Percent_Taking_Public_Trans = Total_Public_Trans / Means_of_Transport)
jcTracts <- njCensus %>%
as.data.frame() %>%
distinct(GEOID, .keep_all = TRUE) %>%
select(GEOID, geometry) %>%
st_sf()
The bikeshare dataset we’re working with contains spatial details,
but it isn’t inherently formatted as spatial data. To conduct a spatial
analysis, we first need to integrate it with the census geometry. Our
focus is on the starting locations of rides, as our aim is to predict
bike availability at stations when users initiate a trip. To prepare the
data, we filter out records lacking latitude or longitude information
and address errors where certain coordinates incorrectly place stations
in the Hudson River. These inaccurate entries are removed to ensure data
quality.
bike_census <- st_join(nycbike %>%
filter(is.na(start_lat) == FALSE &
is.na(start_lng) == FALSE &
is.na(end_lat) == FALSE &
is.na(end_lng) == FALSE) %>%
st_as_sf(., coords = c("start_lng", "start_lat"), crs = 4326),
jcTracts %>% st_transform(crs=4326),
join=st_intersects, left = TRUE) %>%
rename(Origin.Tract = GEOID) %>%
mutate(start_lng = unlist(map(geometry, 1)),
start_lat = unlist(map(geometry, 2)))%>%
filter(!(ride_id %in% c("BEC9CC4D1007C753", "86087F431785EDE7", "A1AAB92631439883", "71938DA085242DCC", "4203C77668C47C75", "C92410CA2AEF3947", "6E02CBF36C90F244", "0259885253FD238E", "B39136B37AFB5FE9", "1EC9376D1559C51C", "CDDF16D3A37BE370"))) %>%
as.data.frame()
The following map shows the Locartion of ride starting points in
Jersey City
ggplot()+
geom_sf(data = jcTracts %>%
st_transform(crs=4326), fill="white")+
geom_point(data = bike_census, aes(x=start_lng, y = start_lat),
color = "blue", alpha = 1, size = 0.3) +
ylim(min(bike_census$start_lat), max(bike_census$start_lat))+
xlim(min(bike_census$start_lng), max(bike_census$start_lng)) +
labs(title = "Location of Rides Starting Points in Jersey City") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, linewidth=0.8)
)

We carry out essential feature engineering to derive temporal
insights from the ride data. Specifically, we’ll create time intervals
at both 60-minute and 15-minute increments based on each ride’s start
time. Additionally, we’ll categorize these intervals into broader time
periods, such as “Overnight,” “AM Rush,” “Mid-Day,” and “PM Rush,” and
classify each day as either a weekday or weekend.
bike_census <- bike_census %>%
mutate(
interval60 = floor_date(ymd_hms(started_at), unit = "hour"),
interval15 = floor_date(ymd_hms(started_at), unit = "15 mins"),
week = week(interval60),
dotw = wday(interval60), # Removed label = TRUE
time_of_day = case_when(
hour(interval60) < 7 | hour(interval60) > 18 ~ "Overnight",
hour(interval60) >= 7 & hour(interval60) < 10 ~ "AM Rush",
hour(interval60) >= 10 & hour(interval60) < 15 ~ "Mid-Day",
hour(interval60) >= 15 & hour(interval60) <= 18 ~ "PM Rush"
),
hour = hour(started_at),
weekend = ifelse(dotw %in% c(1, 7), "Weekend", "Weekday") # Use numeric values for days
)
To better understand commuting patterns and rider behavior throughout
the day, we generated line strings to represent the routes between the
start and end points of rides. To simplify the visualization, we limited
it to 200 sample rides. The visualization reveals that the majority of
trips are confined to Jersey City, though some riders use Citi Bike to
travel from Jersey City to Manhattan.
lines <- st_sfc(
lapply(1:nrow(bike_census), function(i) {
st_linestring(matrix(c(bike_census[i, "start_lng"], bike_census[i, "start_lat"],
bike_census[i, "end_lng"], bike_census[i, "end_lat"]), ncol = 2, byrow = TRUE))
}))
lines_sf <- st_sf(geometry = lines)
lines_sf <- st_set_crs(lines_sf, 4326)
Route <- lines_sf %>% head(200)
mapview(Route, zcol = NULL, color = "#6E0E0A", linewidth = 0.001, alpha = 0.3) # only show 200
Exploratory Analysis
In this section, we delve into some further exploratory analysis of
our data set.
Temporal Analysis
We began our analysis by examining the temporal patterns of bikeshare
usage in Jersey City throughout August 2023, focusing on the
distribution of trips by the hour. Our visualization reveals distinct
fluctuations, with demand rising and falling in a predictable cycle each
day. During weekdays, we observe pronounced peaks that align with
typical rush hours—mornings and late afternoons—when people are
commuting to and from work. These busy periods are followed by
noticeable lulls during the night and early morning hours, as well as
during weekends, when overall usage tends to decrease. The pattern is
characterized by a series of five consecutive high-demand peaks that
correspond to weekdays, consistently followed by two lower peaks over
the weekend, highlighting the influence of workweek routines on
bikeshare activity. This cyclical behavior underscores the strong link
between bikeshare demand and daily commuting schedules.
ggplot(bike_census %>%
group_by(interval60) %>%
tally())+
geom_line(aes(x = interval60, y = n), color= "blue")+
labs(title="Bike Share Trips Per Hour in Jersey City, Aug 2023",
x="Date",
y="Number of trips") +
theme(plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
axis.text.x=element_text(size=8),
axis.text.y=element_text(size=8),
axis.title=element_text(size=10),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, linewidth =0.8))

Next, we analyzed how trips were distributed across different
stations on an hourly basis throughout August. The resulting
visualization revealed a highly skewed distribution: the vast majority
of stations recorded either no rides or just a few trips per hour.
However, a select few stations stood out, experiencing significantly
higher demand. These high-traffic stations sometimes saw as many as 40
to 50 rides within a single hour on certain days. This uneven
distribution highlights the varying levels of bikeshare usage across
Jersey City, with certain locations acting as major hubs of activity
while many others experience minimal ridership.
ggplot(bike_census %>%
group_by(interval60, start_station_name) %>%
tally())+
geom_histogram(aes(n), binwidth = 5, fill="lightblue")+
labs(title="Bike Share Trips Per Hour by Station in Jersey City, Aug 2023",
x="Trip Counts",
y="Number of Stations") +
theme(plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
axis.text.x=element_text(size=8),
axis.text.y=element_text(size=8),
axis.title=element_text(size=10),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, linewidth =0.8))

We further explored the average number of trips per station at
different times of the day. The analysis showed that PM rush hours
consistently had the highest average trip counts, with several stations
averaging more than 10 trips, indicating substantial demand at these
locations. Midday came next in terms of activity levels, where some
stations recorded averages of over 5 trips. Surprisingly, there was also
considerable demand overnight, as a few stations averaged more than 5
rides even during late-night hours. This unexpected overnight activity
suggests that bikeshare usage extends beyond traditional commuting
patterns, potentially serving other purposes such as late-night leisure
or early-morning travel.
bike_census %>%
group_by(interval60, start_station_name, time_of_day) %>%
tally()%>%
group_by(start_station_name, time_of_day)%>%
summarize(mean_trips = mean(n))%>%
ggplot()+
geom_histogram(aes(mean_trips, fill=time_of_day), binwidth = 1)+
scale_fill_manual(values = palette4, name="Time of Day") +
labs(title="Mean Number of Hourly Trips Per Station",
x="Number of trips",
y="Frequency")+
facet_wrap(~time_of_day) +
theme(plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
axis.text.x=element_text(size=8),
axis.text.y=element_text(size=8),
axis.title=element_text(size=10),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8))

We then examined the total number of trips throughout August,
breaking them down by day of the week. Two key patterns emerged from
this visualization. First, the number of trips fluctuates significantly
over the course of a day, with the lowest activity observed in the early
morning hours and the highest in the late afternoon, corresponding to
evening commute times. Second, the data shows that trip volumes are
generally higher on Tuesdays, Wednesdays, and Thursdays, suggesting that
midweek days experience the greatest demand for bikeshare services,
likely driven by regular work-related travel.
ggplot(bike_census %>% mutate(hour = hour(started_at)))+
geom_freqpoly(aes(hour, color = dotw), binwidth = 3, lwd = 0.8)+
scale_color_manual(values = c("#124E78", "#819FA1", "#F0F0C9", "#F2BB05", "#E58507", "#D74E09", "#6E0E0A"), name = "Day") +
labs(title="Bike Share Trips in Jersey City by Day of the Week, Aug 2023",
x="Hour",
y="Trip Counts") +
theme(plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
axis.text.x=element_text(size=8),
axis.text.y=element_text(size=8),
axis.title=element_text(size=10),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8))
## Warning: The following aesthetics were dropped during statistical transformation:
## colour.
## ℹ This can happen when ggplot fails to infer the correct grouping structure in
## the data.
## ℹ Did you forget to specify a `group` aesthetic or to convert a numerical
## variable into a factor?

We created a similar visualization but this time compared trip
volumes between weekdays and weekends. The differences are striking:
bikeshare usage is noticeably higher on weekdays compared to weekends.
Additionally, on weekends, trips start to pick up much later in the day,
reflecting the absence of the typical weekday morning rush. This delay
suggests that without the pressure of early work commutes, people are
more likely to use bikeshare services later in the day for leisure or
non-work-related activities, highlighting the influence of daily
routines on bikeshare demand.
ggplot(bike_census)+
geom_freqpoly(aes(hour, color = weekend), binwidth = 3, lwd = 1.2)+
scale_color_manual(values=palette2) +
labs(color = "Time of Week") +
labs(title="Bike Share Trips in Jersey City - Weekend vs Weekday, Aug 2023",
x="Hour",
y="Trip Counts") +
theme(plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
axis.text.x=element_text(size=8),
axis.text.y=element_text(size=8),
axis.title=element_text(size=10),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8))

# Assuming `bike_census` has a column 'Origin.Tract' that maps each bike trip to a census tract
# and `jcTracts` is already loaded and transformed as necessary
# Aggregate bike trip data by census tract
bike_trips_by_tract <- bike_census %>%
group_by(Origin.Tract) %>%
summarise(Total_Trips = n(), .groups = 'drop') %>%
ungroup()
# Join this aggregated data to the jcTracts GeoDataFrame
jcTracts <- st_transform(jcTracts, crs = 4326) # Make sure CRS matches, if not already
jcTracts_data <- jcTracts %>%
left_join(bike_trips_by_tract, by = c("GEOID" = "Origin.Tract"))
# Calculate the bounding box for centering
bbox <- st_bbox(jcTracts_data)
# Plot the choropleth map with adjusted limits
ggplot(data = jcTracts_data) +
geom_sf(aes(fill = Total_Trips), color = "white", size = 0.1) +
scale_fill_viridis_c(option = "D", name = "Total Bike Trips", na.value = "white") +
coord_sf(xlim = c(bbox["xmin"], bbox["xmax"]), ylim = c(bbox["ymin"], bbox["ymax"]), expand = FALSE) + # Set limits
labs(title = "Bike Share Trips per Census Tract in Jersey City") +
theme_minimal() +
theme(legend.position = "bottom",
axis.text.x = element_blank(),
axis.text.y = element_blank(),
axis.ticks = element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.title = element_text(size = 14, face = "bold"),
plot.subtitle = element_text(size = 12))

Expanding on our analysis, we grouped the data by station and time of
day to explore trip volumes during different periods on weekdays versus
weekends. To keep the visualization clear, we focused on the top 50
station-time occurrences. Each bar in the chart represents a specific
combination of station, time of day, and whether it’s a weekday or
weekend. The results clearly show that PM rush hours generate the
highest bikeshare demand, particularly at a few key stations. Notably,
weekend rides are scarce in the top 50 list, appearing only three times.
A closer look reveals that Grove St PATH station had a total of 1,117
rides during PM rush on a weekday and 801 rides overnight on a weekday
in August. Hoboken Terminal - River St & Hudson Pl recorded 864
rides during weekday PM rush. South Waterfront Walkway - Sinatra Dr
& 1 St had 319 rides overnight on a weekend and 731 rides during PM
rush on a weekday. These details underscore how certain stations
experience significant spikes in demand, especially during weekday
commute hours.
to_plot <- bike_census%>% # only show 100 stations
group_by(start_station_id, start_lat, start_lng, weekend, time_of_day, start_station_name) %>%
tally() %>%
arrange(-n) %>%
head(50)
to_plot$ID <- seq_along(to_plot$start_station_id)
to_plot %>%
arrange(-n) %>%
head(50) %>%
ggplot(aes(x = reorder(ID, -n), n, fill = time_of_day, color = weekend)) +
scale_fill_manual(values = palette4, name="Time of Day") +
guides(color="none") +
geom_bar(stat = "identity", position="stack") +
scale_color_manual(values = c("transparent", "black")) +
labs(title="Top 50 Occurences of Rides By Time")+
ylab("Number of Rides") +
xlab("") +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
theme(plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.text.y=element_text(size=8),
axis.title=element_text(size=10),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8))

Spatial Analysis
Beyond our temporal analysis, we created maps to visualize the
locations of stations with the highest bikeshare demand, broken down by
time of day and day of the week. These proportional maps reveal that the
greatest demand occurs during PM rush hours and overnight on weekdays,
while AM rush hours see the lowest activity. The stations with the
highest usage are predominantly clustered along the Hudson River. This
pattern could be due to two factors: a higher concentration of stations
in this area or strategic placement by Citi Bike to accommodate
commuters traveling from Jersey City to Manhattan for work or school.
The proximity to major transit connections likely drives this increased
demand.
ggplot()+
geom_sf(data = jcTracts %>%
st_transform(crs=4326), fill = "white")+
geom_point(data = to_plot,
aes(x=start_lng, y = start_lat, size=n), color = alpha("#6E0E0A", 0.7)) +
ylim(min(bike_census$start_lat), max(bike_census$start_lat))+
xlim(min(bike_census$start_lng), max(bike_census$start_lng)) +
scale_size(range = c(0.5, 8)) +
labs(size = "Number of Rides") +
facet_grid(weekend ~ time_of_day) +
labs(title="Locations of Stations with Greatest Bikeshare Demand by Time")+
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, linewidth=0.8)
)

We analyzed all the starting stations collectively to assess spatial
autocorrelation, aiming to determine if stations with high demand tend
to be located near other high-demand stations. If such a pattern exists,
it would allow us to predict demand at a specific station based on the
activity levels at neighboring stations. To investigate this, we
selected the top 80 stations with the highest number of rides in August
2023 and calculated Moran’s I using 999 random Monte Carlo simulations.
The results indicated a highly significant Moran’s I, confirming that
there is indeed spatial autocorrelation among nearby bikeshare stations,
meaning demand is not randomly distributed but clustered in specific
areas.
moran_data <- bike_census %>%
group_by(start_station_id, start_lat, start_lng, start_station_name) %>%
tally() %>%
arrange(-n) %>%
head(80) %>%
st_as_sf(., coords = c("start_lng", "start_lat"), crs = 4326)
coords <- st_coordinates(moran_data)
neighborList <- knn2nb(knearneigh(coords, 5))
spatialWeights <- nb2listw(neighborList, style="W")
moranTest <- moran.mc(moran_data$n,
spatialWeights, nsim = 999)
ggplot(as.data.frame(moranTest$res[c(1:999)]), aes(moranTest$res[c(1:999)])) +
geom_histogram(binwidth = 0.01, fill = "#124E78") +
geom_vline(aes(xintercept = moranTest$statistic), colour = "#6E0E0A" ,lwd=1) +
scale_x_continuous(limits = c(-0.5, 0.5)) +
labs(title = "Observed and Permuted Moran's I for Stations with Most Rides",
subtitle = "Observed Moran's I in Red",
x = "Moran's I",
y = "Count") +
theme_light() +
theme(plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
axis.text.x=element_text(size=8),
axis.text.y=element_text(size=8),
axis.title=element_text(size=10))

The map below illustrates the clustering of stations with higher
bikeshare demand. Specifically, two main clusters emerge, and within
each of these clusters, a few stations experience exceptionally high
numbers of rides. These high-demand stations are typically surrounded by
others with slightly lower, but still significant, levels of activity,
highlighting a clear pattern of concentrated bikeshare usage.
ggplot() +
geom_sf(data = jcTracts, fill = "white") +
geom_sf(data = moran_data,
aes(size = n, color = n)) +
ylim(min(bike_census$start_lat), max(bike_census$start_lat))+
xlim(min(bike_census$start_lng), max(bike_census$start_lng)) +
scale_size(
range = c(1, 6),
guide = guide_legend(
direction = "horizontal",
nrow = 1,
label.position = "bottom")) +
scale_color_gradientn(colours = hcl.colors(5, "RdBu",rev = TRUE, alpha = 0.9),
guide = guide_legend(direction = "horizontal", nrow = 1)) +
labs(title = "Stations with Most Rides",
subtitle = "Dot size proportional to rides") +
theme(legend.position = "bottom") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, linewidth=0.8)
)

This map below combines the spatial and temporal characteristics of
bike rides at 15min interval into a choropleth map.
bike_aggregated <- bike_census %>%
group_by(interval15, Origin.Tract) %>%
summarize(Trip_Count = n(), .groups = 'drop')
# Joining with spatial data
choropleth_data <- bike_aggregated %>%
left_join(jcTracts, by = c("Origin.Tract" = "GEOID")) %>%
st_sf()
# Creating the choropleth map with the same scale
choropleth_bike_map <- ggplot() +
geom_sf(data = jcTracts, fill = "white", color = "grey", alpha = 0.5) + # Base map
geom_sf(data = choropleth_data, aes(fill = Trip_Count), color = NA) + # Choropleth layer
scale_fill_gradientn(
colours = hcl.colors(5, "YlOrRd", rev = TRUE),
name = "Number of Trips",
na.value = "transparent"
) +
# Set the same scale using xlim and ylim
xlim(min(bike_census$start_lng), max(bike_census$start_lng)) +
ylim(min(bike_census$start_lat), max(bike_census$start_lat)) +
labs(
title = "Bikeshare Trip Density by 15-Minute Intervals",
subtitle = "Number of bike rides aggregated by origin tract and time interval"
) +
theme(
legend.position = "bottom",
axis.text.x = element_blank(),
axis.text.y = element_blank(),
axis.ticks = element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9, face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill = NA, linewidth = 0.8)
)
# Display the updated map
print(choropleth_bike_map)

Weather Analysis
In addition to space and time, weather plays a crucial role in
influencing bikeshare demand. We expect that demand typically drops on
rainy, windy, or extremely hot days. To incorporate this factor into our
analysis, we collected hourly weather data from Newark Airport using the
riem_measures function, covering the period from August 1 to September
1, 2023. Since Newark Airport is close enough to Jersey City, it serves
as a reliable source for local weather conditions. We then created a
weather panel summarizing key variables such as temperature,
precipitation, and wind speed for each hour.
weather.Panel <-
riem_measures(station = "EWR", date_start = "2023-08-01", date_end = "2023-09-01") %>%
dplyr::select(valid, tmpf, p01i, sknt) %>%
replace(is.na(.), 0) %>%
mutate(interval60 = ymd_h(substr(valid, 1, 13))) %>%
mutate(
week = week(interval60),
dotw = wday(interval60) # Remove label = TRUE
) %>%
group_by(interval60) %>%
summarize(
Temperature = max(tmpf),
Precipitation = sum(p01i),
Wind_Speed = max(sknt)
) %>%
mutate(Temperature = ifelse(Temperature == 0, 42, Temperature))
Examining the temperature, precipitation, and wind data collected for
August, we observe a few notable weather patterns. There were several
rainy days scattered throughout the month, while wind speeds remained
generally stable, with only a few instances of particularly strong
winds. Temperature showed daily fluctuations, with higher readings
during the daytime and cooler temperatures at night, reflecting a
typical summer cycle.
grid.arrange(
ggplot(weather.Panel, aes(interval60,Precipitation)) + geom_line(color="#124E78") +
labs(title="Percipitation", x="Hour", y="Perecipitation") +
theme(plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
axis.text.x=element_text(size=8),
axis.text.y=element_text(size=8),
axis.title=element_text(size=10),
panel.background = element_blank()),
ggplot(weather.Panel, aes(interval60,Wind_Speed)) + geom_line(color="#124E78") +
labs(title="Wind Speed", x="Hour", y="Wind Speed") +
theme(plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
axis.text.x=element_text(size=8),
axis.text.y=element_text(size=8),
axis.title=element_text(size=10),
panel.background = element_blank()),
ggplot(weather.Panel, aes(interval60,Temperature)) + geom_line(color="#124E78") +
labs(title="Temperature", x="Hour", y="Temperature") +theme(plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
axis.text.x=element_text(size=8),
axis.text.y=element_text(size=8),
axis.title=element_text(size=10),
panel.background = element_blank()),
top="Weather Data ")

Space-Time Panel
We proceeded by constructing a study panel that captures unique
combinations of space and time. This means each row in the panel
corresponds to a ride occurring at a specific station during a specific
hour, allowing us to analyze bikeshare activity across both temporal and
spatial dimensions.
# Check if 'interval60' exists and is not entirely NULL
if("interval60" %in% names(bike_census) && !all(is.na(bike_census$interval60))) {
print("Column 'interval60' exists and has data.")
print(head(bike_census$interval60))
} else {
print("Column 'interval60' does not exist or is all NA.")
}
## [1] "Column 'interval60' exists and has data."
## [1] "2023-08-07 19:00:00 UTC" "2023-08-01 13:00:00 UTC"
## [3] "2023-08-15 17:00:00 UTC" "2023-08-01 12:00:00 UTC"
## [5] "2023-08-08 12:00:00 UTC" "2023-08-02 07:00:00 UTC"
# If 'interval60' needs to be created or recalculated:
if(!"interval60" %in% names(bike_census) || all(is.na(bike_census$interval60))) {
print("Recalculating 'interval60' from existing datetime column, assuming 'started_at' exists.")
# Assuming 'started_at' is the column from which 'interval60' should be derived:
if("started_at" %in% names(bike_census)) {
bike_census$interval60 <- as.POSIXct(bike_census$started_at, format = "%Y-%m-%d %H:%M:%S") # Customize format as necessary
bike_census$interval60 <- floor_date(bike_census$interval60, "hour") # Using lubridate to round down to the nearest hour
print("New 'interval60' calculated and added to 'bike_census'.")
} else {
print("'started_at' column also missing; please check data source.")
}
}
# Check and recreate the data frame for joining
if("interval60" %in% names(bike_census) && "start_station_id" %in% names(bike_census)) {
combined_df <- expand.grid(interval60 = unique(bike_census$interval60),
start_station_id = unique(bike_census$start_station_id))
print("Combined DataFrame created successfully. Displaying structure:")
print(str(combined_df))
} else {
print("One or both key columns are still missing or invalid.")
}
## [1] "Combined DataFrame created successfully. Displaying structure:"
## 'data.frame': 78120 obs. of 2 variables:
## $ interval60 : POSIXct, format: "2023-08-07 19:00:00" "2023-08-01 13:00:00" ...
## $ start_station_id: Factor w/ 105 levels "HB302","JC059",..: 1 1 1 1 1 1 1 1 1 1 ...
## - attr(*, "out.attrs")=List of 2
## ..$ dim : Named int [1:2] 744 105
## .. ..- attr(*, "names")= chr [1:2] "interval60" "start_station_id"
## ..$ dimnames:List of 2
## .. ..$ interval60 : chr [1:744] "interval60=2023-08-07 19:00:00" "interval60=2023-08-01 13:00:00" "interval60=2023-08-15 17:00:00" "interval60=2023-08-01 12:00:00" ...
## .. ..$ start_station_id: chr [1:105] "start_station_id=HB302" "start_station_id=JC059" "start_station_id=JC105" "start_station_id=JC063" ...
## NULL
study.panel <-
expand.grid(interval60=unique(bike_census$interval60),
start_station_id = unique(bike_census$start_station_id)) %>%
left_join(., bike_census %>%
select(start_station_id, start_station_name, Origin.Tract, start_lng, start_lat)%>%
distinct() %>%
group_by(start_station_id) %>%
slice(1))
We then enhanced this panel by adding more detailed information. This
involved counting the number of rides at each station for each hour,
incorporating weather data, merging in census information, and using the
lubridate package to derive time-related variables such as the day of
the week and hour of the day.
bike.panel <-
bike_census %>%
mutate(Trip_Counter = 1) %>%
right_join(study.panel) %>%
group_by(interval60, start_station_id, start_station_name, Origin.Tract, start_lng, start_lat) %>%
summarize(Trip_Count = sum(Trip_Counter, na.rm = TRUE), .groups = 'drop') %>%
left_join(weather.Panel) %>%
ungroup() %>%
filter(!is.na(start_station_id)) %>%
mutate(
week = week(interval60),
dotw = wday(interval60) # Removed label = TRUE
) %>%
filter(!is.na(Origin.Tract)) %>%
left_join(
njCensus %>%
as.data.frame() %>%
select(-geometry),
by = c("Origin.Tract" = "GEOID")
)
Weather Correlation
With our panel data ready, we conducted further exploratory analysis
to prepare for our regression modeling. Our first focus was on
understanding the relationship between temperature and the number of
bike rides in August. The analysis revealed a strong, positive, and
linear correlation, indicating that bikeshare demand tends to increase
as temperatures rise throughout the month.
bike.panel %>%
group_by(interval60) %>%
summarize(Trip_Count = mean(Trip_Count),
Temperature = first(Temperature)) %>%
mutate(week = week(interval60)) %>%
ggplot(aes(Temperature, Trip_Count)) +
geom_point(size = .5, color = "black") + geom_smooth(method = "lm", se= FALSE, color="darkred") +
facet_wrap(~week, ncol=8) +
labs(title="Trip Count As a Fuction of Temperature by Week",
x="Temperature", y="Mean Trip Count") +
theme(plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.text.y=element_text(size=8),
axis.title=element_text(size=10),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8))

Next, we examined the correlation between wind speed and the number
of bike rides in August. We observed a generally positive linear
relationship, though the strength of this correlation varied across
different weeks. This suggests that bikeshare demand tends to increase
on windy days, but the impact of wind speed on ride activity is not
uniform throughout the month.
bike.panel %>%
group_by(interval60) %>%
summarize(Trip_Count = mean(Trip_Count),
Wind = first(Wind_Speed)) %>%
mutate(week = week(interval60)) %>%
ggplot(aes(Wind, Trip_Count)) +
geom_point(size = .5, color = "black") + geom_smooth(method = "lm", se= FALSE, color="darkred") +
facet_wrap(~week, ncol=8) +
labs(title="Trip Count As a Fuction of Wind by Week",
x="Wind Speed", y="Mean Trip Count") +
theme(plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.text.y=element_text(size=8),
axis.title=element_text(size=10),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)) +
theme(plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.text.y=element_text(size=8),
axis.title=element_text(size=10),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8))

Serial Correlation
If the number of bike trips shows serial (temporal) correlation,
incorporating time lag features into our model could significantly
enhance its predictive power. The underlying concept is that bikeshare
demand at any given hour is likely to be influenced by demand patterns
immediately preceding or following that hour. For example, if we know
there was a high number of trips during one hour, we can reasonably
predict that nearby hours will also have elevated demand. To capture
these temporal dependencies, we computed six different lagged variables,
each representing the number of trips one, two, three, four, twelve
hours earlier, and one day earlier. These lag features allow us to
account for short-term fluctuations as well as daily patterns, providing
a more comprehensive framework for modeling bikeshare activity. By
leveraging these time-based correlations, our model can better
anticipate shifts in demand, improving accuracy and reliability.
bike.panel <-
bike.panel %>%
arrange(start_station_id, interval60) %>%
mutate(lagHour = dplyr::lag(Trip_Count,1),
lag2Hours = dplyr::lag(Trip_Count,2),
lag3Hours = dplyr::lag(Trip_Count,3),
lag4Hours = dplyr::lag(Trip_Count,4),
lag12Hours = dplyr::lag(Trip_Count,12),
lag1day = dplyr::lag(Trip_Count,24)) %>%
mutate(day = yday(interval60))
We visualized the relationships between the number of trips and the
various lagged hours to better understand these temporal correlations.
The analysis revealed some interesting patterns. There is a strong,
linear, and negative relationship between the number of trips and the
lagged variable representing 12 hours prior. For instance, the number of
rides recorded at 5 PM on a given day tends to be negatively correlated
with the number of rides at 5 AM on the same day, likely reflecting
contrasting peak and off-peak usage times.
Conversely, we observed a strong, positive, and linear correlation
between the number of trips at a given hour and both the immediate next
hour (laghour) and the same hour on the following day (lag1day). This
indicates that bikeshare demand is highly predictable from one hour to
the next and tends to follow consistent daily patterns. However, the
strength of the correlation decreases for the lag variables representing
two and three hours prior. For example, the number of rides at 4 PM is
more strongly correlated with the number of rides at 5 PM than with
rides at 6 or 7 PM. This gradual weakening of correlation over time
highlights how bikeshare demand is most heavily influenced by recent
past activity, making these time lag features valuable for modeling
purposes.
as.data.frame(bike.panel) %>%
group_by(interval60) %>%
summarise_at(vars(starts_with("lag"), "Trip_Count"), mean, na.rm = TRUE) %>%
gather(Variable, Value, -interval60, -Trip_Count) %>%
ggplot(aes(x = Value , y = Trip_Count)) +
geom_point(size = .5, color = "black") + geom_smooth(method = "lm", se= FALSE, color="darkred") +
facet_wrap(~Variable, ncol = 6)+
labs(x = "Variable", y = "Correlation", title = "Correlation Between Serial Lag Trips and Trip Count") +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
theme(plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.text.y=element_text(size=8),
axis.title=element_text(size=10),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8))

Spatial Correlation
We previously established that spatial autocorrelation is present in
our data, indicating that the number of trips at one station during a
specific time is related to the number of trips at nearby stations. In
other words, knowing the demand at one station can help us estimate
demand at neighboring stations. To leverage this spatial relationship,
we created a new dataframe called lag_result.
We then implemented a nested loop to systematically calculate spatial
lags for each hour in August 2023. For each hour, we identified the five
nearest neighboring stations for every station and computed the average
number of trips across these neighbors. This provided a measure of
spatial influence, capturing how demand at one station is affected by
activity at surrounding stations. Finally, we integrated these spatial
lag values back into our panel dataset, enhancing our model’s ability to
predict bikeshare demand by incorporating both spatial and temporal
dependencies.
library(sf)
library(spdep)
library(dplyr)
library(parallel)
# Step 1: Filter data once and convert to sf object
bike.panel_sf <- bike.panel %>%
st_as_sf(coords = c("start_lng", "start_lat"), crs = 4326)
# Step 2: Define a function to calculate spatial lag for a given day and hour
calculate_spatial_lag <- function(d, h, bike_data) {
# Load required libraries within the function
library(dplyr)
library(sf)
library(spdep)
library(lubridate)
# Filter data for the specified day and hour
spacelag <- bike_data %>%
mutate(hour = hour(interval60), day = yday(interval60)) %>%
filter(day == d & hour == h)
if (nrow(spacelag) == 0) return(NULL) # Skip if no data
# Compute spatial neighbors and weights
coords <- st_coordinates(spacelag)
neighborList <- knn2nb(knearneigh(coords, k = 5))
spatialWeights <- nb2listw(neighborList, style = "W")
# Calculate spatial lag
spacelag <- spacelag %>%
mutate(lagTrip = lag.listw(spatialWeights, Trip_Count))
return(spacelag)
}
# Step 3: Use parallel processing to speed up the loop
cl <- makeCluster(detectCores() - 1) # Create a cluster using available cores
clusterExport(cl, c("bike.panel_sf", "calculate_spatial_lag")) # Export data and function
clusterEvalQ(cl, { # Load required libraries in each worker
library(dplyr)
library(sf)
library(spdep)
library(lubridate)
})
## [[1]]
## [1] "lubridate" "spdep" "spData" "sf" "dplyr" "stats"
## [7] "graphics" "grDevices" "utils" "datasets" "methods" "base"
##
## [[2]]
## [1] "lubridate" "spdep" "spData" "sf" "dplyr" "stats"
## [7] "graphics" "grDevices" "utils" "datasets" "methods" "base"
##
## [[3]]
## [1] "lubridate" "spdep" "spData" "sf" "dplyr" "stats"
## [7] "graphics" "grDevices" "utils" "datasets" "methods" "base"
# Run the loop in parallel
lag_result_list <- parLapply(cl, 213:243, function(d) {
lapply(0:23, function(h) calculate_spatial_lag(d, h, bike.panel_sf))
})
stopCluster(cl) # Stop the cluster
# Combine the results into a single data frame
lag_result <- do.call(rbind, unlist(lag_result_list, recursive = FALSE)) %>%
mutate(start_lng = unlist(map(geometry, 1)),
start_lat = unlist(map(geometry, 2))) %>%
st_drop_geometry()
# Final result
bike.panel <- lag_result
The figure below illustrates a consistent, strong, and positive
linear relationship between the number of trips at a station and the
number of trips at nearby stations throughout all weeks in August. This
confirms the presence of significant spatial dependence, suggesting that
stations in close proximity tend to experience similar levels of demand.
Given this finding, we will incorporate this spatial lag variable into
our regression analysis to improve the model’s predictive accuracy by
accounting for the influence of nearby station activity.
bike.panel %>%
group_by(interval60) %>%
summarize(Trip_Count = mean(Trip_Count),
Lag_Count = mean(lagTrip)) %>%
mutate(week = week(interval60)) %>%
ggplot(aes(Lag_Count, Trip_Count)) +
geom_point(size = 1, color = "#6E0E0A") + geom_smooth(method = "lm", se= FALSE, color="#124E78") +
facet_wrap(~week, ncol=8) +
labs(title="Correlation Between Spatial Lag Trips and Trip Count",
x="Lag Trip Count", y="Mean Trip Count") +
theme(plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.text.y=element_text(size=8),
axis.title=element_text(size=10),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8))

OLS Regression
We divided our five weeks of data into a training set consisting of
three weeks and a testing set comprising the remaining two weeks. Using
the training data, we estimated four distinct linear regression models,
each incorporating different fixed effects:
- reg1 captures only temporal factors, including hour-of-day fixed
effects, day of the week, wind speed, and temperature.
- reg2 emphasizes spatial components, using station-specific effects
and weather conditions.
- reg3 combines both spatial and temporal elements to account for
interactions between space and time.
- reg4 extends the third model by adding both time and spatial lag
features, capturing temporal and spatial dependencies to enhance
predictive accuracy.
bike.Train <- filter(bike.panel, week <= 33)
bike.Test <- filter(bike.panel, week > 33)
# Time
reg1 <-
lm(Trip_Count ~ hour(interval60) + dotw + Temperature + Wind_Speed, data=bike.Train)
# Space
reg2 <-
lm(Trip_Count ~ start_station_name + Temperature + Wind_Speed, data=bike.Train)
# Time and Space
reg3 <-
lm(Trip_Count ~ start_station_name + hour(interval60) + dotw + Temperature + Wind_Speed,
data=bike.Train)
# Time and Space and TimeLag and SpaceLag
reg4 <-
lm(Trip_Count ~ start_station_name + hour(interval60) + dotw + Temperature + Wind_Speed +
lagHour + lag2Hours + lag12Hours + lag1day + lagTrip,
data=bike.Train)
Making Predictions
We utilized the purrr package to efficiently iterate over a set of
nested data frames, allowing us to compare different model
specifications. To prepare for testing, we organized our data by week,
nesting it into separate tibbles. We then created a simple prediction
function that takes a tibble (dat) and a regression model (fit) as
inputs and generates predictions, returning them as pred. This approach
streamlines the process of applying our models across multiple subsets
of data, making it easy to evaluate and compare their performance.
bike.Test.weekNest <-
bike.Test %>%
nest(-week)
model_pred <- function(dat, fit){
pred <- predict(fit, newdata = dat)}
The nested data format enables us to iterate through each model for
every week and compute summary statistics efficiently. In the code block
that follows, we generate week_predictions for each week within the
testing set. Using the map function from purrr, we apply the model_pred
function to each nested tibble. For instance, in the first line of the
mutate operation, a new column called Time_FE is created, which contains
predictions from reg1, the model that includes time fixed effects. These
predictions are produced by mapping model_pred to each data subset, with
fit set to the reg1 model. This method allows us to easily apply
multiple models and gather prediction results for comparison.
week_predictions <-
bike.Test.weekNest %>%
mutate(ATime_FE = map(.x = data, fit = reg1, .f = model_pred),
BSpace_FE = map(.x = data, fit = reg2, .f = model_pred),
CTime_Space_FE = map(.x = data, fit = reg3, .f = model_pred),
DTime_Space_FE_Lags = map(.x = data, fit = reg4, .f = model_pred)) %>%
gather(Regression, Prediction, -data, -week) %>%
mutate(Observed = map(data, pull, Trip_Count),
Absolute_Error = map2(Observed, Prediction, ~ abs(.x - .y)),
MAE = map_dbl(Absolute_Error, mean, na.rm = TRUE),
sd_AE = map_dbl(Absolute_Error, sd, na.rm = TRUE))
Error Analysis
When comparing the mean absolute errors (MAE) across our four models,
we observe that Model 1, which only incorporates time effects, has the
highest error rate. Models 2 and 3, which account for spatial effects
and the combination of space and time, show improved accuracy. However,
the greatest enhancement in predictive performance comes from Model 4,
which includes both space and time lag features, significantly reducing
the error. To ensure the reliability of Model 4, we also performed a
variance inflation factor (VIF) analysis to confirm that
multicollinearity was not an issue among the predictors.
week_predictions %>%
dplyr::select(week, Regression, MAE) %>%
gather(Variable, MAE, -Regression, -week) %>%
ggplot(aes(week, MAE)) +
geom_bar(aes(fill = Regression), position = "dodge", stat="identity") +
scale_fill_manual(values = palette4) +
labs(title = "Mean Absolute Errors by Model Specification and Week") +
theme(plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.text.y=element_text(size=8),
axis.title=element_text(size=10),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8))

The figure below provides a visual comparison of the accuracy of each
model by plotting the predicted versus observed number of trips. Among
the four models, Model 4 clearly demonstrates superior predictive
performance, closely aligning with the actual bikeshare demand. This
highlights the effectiveness of incorporating both spatial and temporal
lag features, making Model 4 the most robust and reliable in forecasting
trip counts.
week_predictions %>%
mutate(interval60 = map(data, pull, interval60),
start_station_id = map(data, pull, start_station_id)) %>%
dplyr::select(interval60, start_station_id, Observed, Prediction, Regression) %>%
unnest() %>%
gather(Variable, Value, -Regression, -interval60, -start_station_id) %>%
group_by(Regression, Variable, interval60) %>%
summarize(Value = sum(Value)) %>%
ggplot(aes(interval60, Value, colour=Variable)) +
geom_line(size = 1.1) +
scale_color_manual(values=palette2)+
facet_wrap(~Regression, ncol=1) +
labs(title = "Predicted/Observed Bike Share Time Series", subtitle = "Jersey City: a test set of 2 weeks", x = "Hour", y= "Station Trips") +
theme(plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.text.y=element_text(size=8),
axis.title=element_text(size=10),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8))

To evaluate whether our predictions generalize well across different
locations and times, we mapped the mean absolute error (MAE) of Model 4
across various stations. The results show that the model struggles to
accurately predict the number of rides at stations located along the
Hudson River, which tend to experience high bikeshare demand on both
weekdays and weekends. However, for stations with lower ridership
throughout August, the model performs significantly better,
demonstrating reasonable predictive accuracy in those areas.
week_predictions %>%
mutate(interval60 = map(data, pull, interval60),
start_station_id = map(data, pull, start_station_id),
start_latitude = map(data, pull, start_lat),
start_longitude = map(data, pull, start_lng)) %>%
select(interval60, start_station_id, start_longitude, start_latitude, Observed, Prediction, Regression) %>%
unnest() %>%
filter(Regression == "DTime_Space_FE_Lags") %>%
group_by(start_station_id, start_longitude, start_latitude) %>%
summarize(MAE = mean(abs(Observed-Prediction), na.rm = TRUE))%>%
ggplot(.)+
geom_sf(data = jcTracts, fill = "white")+
geom_point(aes(x = start_longitude, y = start_latitude, color = MAE),
fill = "transparent")+
scale_color_continuous(low = "#124E78", high = "#6E0E0A", name= "MAE")+
ylim(min(bike_census$start_lat), max(bike_census$start_lat))+
xlim(min(bike_census$start_lng), max(bike_census$start_lng)) +
labs(title="Mean Abs Error, Test Set, Model 4") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, linewidth=0.8)
)

When examining Model 4’s performance across both space and time, the
figure demonstrates that the model maintains consistent predictions
throughout different periods. Stations identified as having high demand
on weekends are similarly predicted to have high demand during weekdays.
However, the errors persist over time, indicating that stations with
historically high demand also exhibit greater variability in ride
counts, making them more challenging to predict with our current set of
features. In fact, our model only explains 50.12% of the variability,
which is a limitation. The concern is that if these larger errors remain
unaddressed for high-demand stations, we risk failing to adequately meet
bikeshare users’ needs at these crucial locations.
week_predictions %>%
mutate(interval60 = map(data, pull, interval60),
start_station_id = map(data, pull, start_station_id),
start_latitude = map(data, pull, start_lat),
start_longitude = map(data, pull, start_lng),
dotw = map(data, pull, dotw) ) %>%
select(interval60, start_station_id, start_longitude,
start_latitude, Observed, Prediction, Regression,
dotw) %>%
unnest() %>%
filter(Regression == "DTime_Space_FE_Lags")%>%
mutate(weekend = ifelse(dotw %in% c("Sun", "Sat"), "Weekend", "Weekday"),
time_of_day = case_when(hour(interval60) < 7 | hour(interval60) > 18 ~ "Overnight",
hour(interval60) >= 7 & hour(interval60) < 10 ~ "AM Rush",
hour(interval60) >= 10 & hour(interval60) < 15 ~ "Mid-Day",
hour(interval60) >= 15 & hour(interval60) <= 18 ~ "PM Rush")) %>%
group_by(start_station_id, weekend, time_of_day, start_longitude, start_latitude) %>%
summarize(MAE = mean(abs(Observed-Prediction), na.rm = TRUE))%>%
ggplot(.)+
geom_sf(data = jcTracts, color = "grey", fill = "transparent")+
geom_point(aes(x = start_longitude, y = start_latitude, color = MAE),
fill = "transparent", size = 0.5)+
scale_color_continuous(low = "#124E78", high = "#6E0E0A", name= "MAE")+
ylim(min(bike_census$start_lat), max(bike_census$start_lat))+
xlim(min(bike_census$start_lng), max(bike_census$start_lng)) +
facet_grid(weekend~time_of_day)+
labs(title="Mean Absolute Errors by Time, Test Set, Model 4") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, linewidth=0.8)
)

Cross-Validation
Lastly, we conducted 100 cross-validation tests using Model 4 on data
spanning all five weeks. The mean absolute error (MAE) was 0.44,
indicating that while this model is our most effective solution so far,
there is still a notable level of prediction error. To improve the model
further, it would be valuable to cross-validate against socio-economic
indicators. This approach could reveal whether bike demand at stations
in specific neighborhoods is being disproportionately under- or
over-predicted. However, our current model does not incorporate
socioeconomic variables as predictors, and the dynamics in Jersey
City—such as the continued development of new stations—may also impact
demand patterns.
fitControl <- trainControl(method = "cv", number = 100)
reg.cv <- train(Trip_Count ~ start_station_name + hour(interval60) + dotw + Temperature + Wind_Speed + lagHour + lag2Hours + lag12Hours + lag1day + lagTrip, data=bike.panel, method = "lm", trControl = fitControl, na.action = na.pass)
reg.cv$resample %>%
summarise(MAE = mean(reg.cv$resample[,3]),
sd(reg.cv$resample[,3])
) %>%
kbl(col.name=c('Mean Absolute Error','Standard Deviation of MAE')) %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"))
Mean Absolute Error
|
Standard Deviation of MAE
|
0.4418984
|
0.029647
|
LS0tCnRpdGxlOiAiUHJlZGljdGluZyBCaWtlc2hhcmUgRGVtYW5kIGluIEplcnNleSBDaXR5IgphdXRob3I6ICJZdXRvbmcgSmlhbmciCmRhdGU6ICJOb3YsMTUsMjAyNCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogY29zbW8gCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogeWVzCmVkaXRvcl9vcHRpb25zOgogIG1hcmtkb3duOgogICAgd3JhcDogc2VudGVuY2UKLS0tCgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgoKIyBJbnRyb2R1Y3Rpb24KCkJpa2VzaGFyZSBwcm9ncmFtcyBoYXZlIGJlY29tZSBhbiBlc3NlbnRpYWwgcGFydCBvZiB1cmJhbiB0cmFuc3BvcnRhdGlvbiwgb2ZmZXJpbmcgYSBzdXN0YWluYWJsZSBhbmQgZmxleGlibGUgY29tbXV0aW5nIG9wdGlvbi4gQ2l0aSBCaWtlLCBsYXVuY2hlZCBieSBMeWZ0IGluIDIwMDggaW4gTmV3IFlvcmsgQ2l0eSwgaGFzIGV4cGFuZGVkIHRvIEplcnNleSBDaXR5IHRvIG1lZXQgdGhlIG5lZWRzIG9mIGNvbW11dGVycyB0cmF2ZWxpbmcgdG8gTWFuaGF0dGFuLiBBcyBDaXRpIEJpa2UncyBwb3B1bGFyaXR5IGdyb3dzLCBtYW5hZ2luZyBiaWtlIGF2YWlsYWJpbGl0eSBlZmZlY3RpdmVseSBiZWNvbWVzIGNydWNpYWwsIGVzcGVjaWFsbHkgZ2l2ZW4gdGhlIGZsdWN0dWF0aW9ucyBpbiBkZW1hbmQgdGhyb3VnaG91dCB0aGUgZGF5IGFuZCBhY3Jvc3MgZGlmZmVyZW50IGxvY2F0aW9ucy4KCk1haW50YWluaW5nIGEgYmFsYW5jZWQgZGlzdHJpYnV0aW9uIG9mIGJpa2VzLCBrbm93biBhcyAicmUtYmFsYW5jaW5nLCIgaXMgYSBjb21wbGV4IGNoYWxsZW5nZSBpbmZsdWVuY2VkIGJ5IHRoZXNlIHNwYXRpYWwgYW5kIHRlbXBvcmFsIGRlbWFuZCBwYXR0ZXJucy4gQ29tcGFuaWVzIGxpa2UgVWJlciBhbmQgTHlmdCB1c2UgcHJvcHJpZXRhcnkgYWxnb3JpdGhtcyB0byBhZGRyZXNzIHRoZXNlIGlzc3VlcywgYnV0IHRoZSBkZXRhaWxzIG9mIHRoZWlyIG1vZGVscyBhcmUgbm90IHB1YmxpY2x5IGF2YWlsYWJsZS4gVGhpcyByZXBvcnQgYWltcyB0byBkZXZlbG9wIGEgdHJhbnNwYXJlbnQsIG9wZW4tc291cmNlIGFwcHJvYWNoIHRvIGZvcmVjYXN0aW5nIGJpa2UgZGVtYW5kIGluIEplcnNleSBDaXR5IHVzaW5nIE9yZGluYXJ5IExlYXN0IFNxdWFyZXMgKE9MUykgcmVncmVzc2lvbi4gQnkgYW5hbHl6aW5nIGhpc3RvcmljYWwgZGF0YSwgaW5jbHVkaW5nIHdlYXRoZXIgYW5kIGJpa2UgdXNhZ2UgcGF0dGVybnMsIG91ciBnb2FsIGlzIHRvIGNyZWF0ZSBhIHNpbXBsZSB5ZXQgZWZmZWN0aXZlIG1vZGVsIHRvIGFudGljaXBhdGUgZGVtYW5kIHNwaWtlcyBhbmQgZ3VpZGUgZWZmaWNpZW50IGJpa2UgcmVhbGxvY2F0aW9uLgoKV2Ugd2lsbCBiZWdpbiBieSBsb2FkaW5nIGFuZCBwcm9jZXNzaW5nIG91ciBkYXRhc2V0cywgZm9sbG93ZWQgYnkgYW4gZXhwbG9yYXRvcnkgYW5hbHlzaXMgb2YgdXNhZ2UgdHJlbmRzIGFuZCB3ZWF0aGVyIGltcGFjdHMuIE5leHQsIHdl4oCZbGwgY3JlYXRlIGEgc3BhY2UtdGltZSBwYW5lbCB0byBleGFtaW5lIGNvcnJlbGF0aW9ucyBhbmQgZmluYWxseSBkZXZlbG9wIGFuZCBldmFsdWF0ZSBmb3VyIHByZWRpY3RpdmUgbW9kZWxzLCB0ZXN0aW5nIHRoZWlyIHBlcmZvcm1hbmNlIGFuZCByZWxpYWJpbGl0eS4KCgojIFNldCBVcAoKYGBge3IgbGlicmFyeSBhbmQgZGF0YSwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoc2YpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KHRpZ3JpcykKbGlicmFyeShnZ2FuaW1hdGUpCmxpYnJhcnkocmllbSkKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeSh0aWR5Y2Vuc3VzKQpsaWJyYXJ5KG1hcHZpZXcpCmxpYnJhcnkoc2ZkZXApCmxpYnJhcnkoc3BkZXApCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkgKHJlYWRyKQoKb3B0aW9ucyh0aWdyaXNfY2xhc3MgPSAic2YiKQpzb3VyY2UoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS91cmJhblNwYXRpYWwvUHVibGljLVBvbGljeS1BbmFseXRpY3MtTGFuZGluZy9tYXN0ZXIvZnVuY3Rpb25zLnIiKQoKcGFsZXR0ZTUgPC0gYygiIzFGNzhCNCIsICIjMzNBMDJDIiwgIiNFMzFBMUMiLCAiI0ZGN0YwMCIsICIjNkEzRDlBIikgICMgQmx1ZSwgR3JlZW4sIFJlZCwgT3JhbmdlLCBQdXJwbGUKcGFsZXR0ZTQgPC0gYygiIzFGNzhCNCIsICIjMzNBMDJDIiwgIiNFMzFBMUMiLCAiIzZBM0Q5QSIpICAgICAgICAgICAgIyBCbHVlLCBHcmVlbiwgUmVkLCBQdXJwbGUKcGFsZXR0ZTIgPC0gYygiIzFGNzhCNCIsICIjRTMxQTFDIikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBCbHVlLCBSZWQKCgp0aWR5Y2Vuc3VzOjpjZW5zdXNfYXBpX2tleSgiZTc5ZjM3MDZiNmQ2MTI0OTk2OGM2Y2U4ODc5NGY2ZjU1NmU1YmYzZCIsIG92ZXJ3cml0ZSA9IFRSVUUpCmBgYAoKVGhlIGRhdGEgdXNlZCBpbiB0aGlzIGFuYWx5c2lzIGNvbWVzIGZyb20gTllDIE9wZW5EYXRhLCB3aGljaCBwcm92aWRlcyBtb250aGx5IHJlY29yZHMgb2YgQ2l0aSBCaWtlIHVzYWdlLiBFdmVuIHRob3VnaCBKZXJzZXkgQ2l0eSBpcyBpbiBOZXcgSmVyc2V5LCBDaXRpIEJpa2UncyBvcGVyYXRpb25zIGFyZSBsYXJnZWx5IGNlbnRlcmVkIGluIE5ldyBZb3JrIENpdHksIGFuZCB0aHVzIHRoZSBkYXRhIGlzIG1hbmFnZWQgdGhyb3VnaCBOWUMncyBzeXN0ZW0uIEZvciB0aGlzIHN0dWR5LCB3ZSBmb2N1c2VkIG9uIGJpa2VzaGFyZSB0cmlwcyBpbiBKZXJzZXkgQ2l0eSBkdXJpbmcgQXVndXN0IDIwMjQsIGNhcHR1cmluZyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc3RhcnRpbmcgYW5kIGVuZGluZyBzdGF0aW9ucyBmb3IgZWFjaCByaWRlLiBBZGRpdGlvbmFsbHksIHdlIGFjcXVpcmVkIHRoZSBjZW5zdXMgdHJhY3QgZ2VvbWV0cmllcyBmb3IgYWxsIG9mIEh1ZHNvbiBDb3VudHksIHdoaWNoIGVuY29tcGFzc2VzIEplcnNleSBDaXR5LCB1c2luZyB0aGUgdGlkeWNlbnN1cyBwYWNrYWdlLgoKYGBge3Igb2J0YWluIGNlbnN1cyBnZW9tIGFuZCBiaWtlIGRhdGEsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHJlc3VsdHM9J2hpZGUnLCBjYWNoZT1UUlVFfQoKbnljYmlrZSA8LSByZWFkLmNzdigiL1VzZXJzL2Jpbi9Eb2N1bWVudHMvTVVTQS9NVVNBIDUwODAvSkMtMjAyMzA4LWNpdGliaWtlLXRyaXBkYXRhLmNzdiIpCgpuakNlbnN1cyA8LSBnZXRfYWNzKGdlb2dyYXBoeSA9ICJ0cmFjdCIsIAogICAgICAgICAgdmFyaWFibGVzID0gYygiQjAxMDAzXzAwMSIsICJCMTkwMTNfMDAxIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICJCMDIwMDFfMDAyIiwgIkIwODAxM18wMDEiLAogICAgICAgICAgICAgICAgICAgICAgICAiQjA4MDEyXzAwMSIsICJCMDgzMDFfMDAxIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICJCMDgzMDFfMDEwIiwgIkIwMTAwMl8wMDEiKSwgCiAgICAgICAgICB5ZWFyID0gMjAyMSwgCiAgICAgICAgICBzdGF0ZSA9ICJOSiIsIAogICAgICAgICAgZ2VvbWV0cnkgPSBUUlVFLCAKICAgICAgICAgIGNvdW50eT1jKCJIdWRzb24iKSwKICAgICAgICAgIG91dHB1dCA9ICJ3aWRlIikgJT4lCiAgcmVuYW1lKFRvdGFsX1BvcCA9ICBCMDEwMDNfMDAxRSwKICAgICAgICAgTWVkX0luYyA9IEIxOTAxM18wMDFFLAogICAgICAgICBNZWRfQWdlID0gQjAxMDAyXzAwMUUsCiAgICAgICAgIFdoaXRlX1BvcCA9IEIwMjAwMV8wMDJFLAogICAgICAgICBUcmF2ZWxfVGltZSA9IEIwODAxM18wMDFFLAogICAgICAgICBOdW1fQ29tbXV0ZXJzID0gQjA4MDEyXzAwMUUsCiAgICAgICAgIE1lYW5zX29mX1RyYW5zcG9ydCA9IEIwODMwMV8wMDFFLAogICAgICAgICBUb3RhbF9QdWJsaWNfVHJhbnMgPSBCMDgzMDFfMDEwRSkgJT4lCiAgc2VsZWN0KFRvdGFsX1BvcCwgTWVkX0luYywgV2hpdGVfUG9wLCBUcmF2ZWxfVGltZSwKICAgICAgICAgTWVhbnNfb2ZfVHJhbnNwb3J0LCBUb3RhbF9QdWJsaWNfVHJhbnMsCiAgICAgICAgIE1lZF9BZ2UsCiAgICAgICAgIEdFT0lELCBnZW9tZXRyeSkgJT4lCiAgbXV0YXRlKFBlcmNlbnRfV2hpdGUgPSBXaGl0ZV9Qb3AgLyBUb3RhbF9Qb3AsCiAgICAgICAgIE1lYW5fQ29tbXV0ZV9UaW1lID0gVHJhdmVsX1RpbWUgLyBUb3RhbF9QdWJsaWNfVHJhbnMsCiAgICAgICAgIFBlcmNlbnRfVGFraW5nX1B1YmxpY19UcmFucyA9IFRvdGFsX1B1YmxpY19UcmFucyAvIE1lYW5zX29mX1RyYW5zcG9ydCkKCmpjVHJhY3RzIDwtIG5qQ2Vuc3VzICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBkaXN0aW5jdChHRU9JRCwgLmtlZXBfYWxsID0gVFJVRSkgJT4lCiAgc2VsZWN0KEdFT0lELCBnZW9tZXRyeSkgJT4lIAogIHN0X3NmKCkKYGBgCgpUaGUgYmlrZXNoYXJlIGRhdGFzZXQgd2XigJlyZSB3b3JraW5nIHdpdGggY29udGFpbnMgc3BhdGlhbCBkZXRhaWxzLCBidXQgaXQgaXNuJ3QgaW5oZXJlbnRseSBmb3JtYXR0ZWQgYXMgc3BhdGlhbCBkYXRhLiBUbyBjb25kdWN0IGEgc3BhdGlhbCBhbmFseXNpcywgd2UgZmlyc3QgbmVlZCB0byBpbnRlZ3JhdGUgaXQgd2l0aCB0aGUgY2Vuc3VzIGdlb21ldHJ5LiBPdXIgZm9jdXMgaXMgb24gdGhlIHN0YXJ0aW5nIGxvY2F0aW9ucyBvZiByaWRlcywgYXMgb3VyIGFpbSBpcyB0byBwcmVkaWN0IGJpa2UgYXZhaWxhYmlsaXR5IGF0IHN0YXRpb25zIHdoZW4gdXNlcnMgaW5pdGlhdGUgYSB0cmlwLiBUbyBwcmVwYXJlIHRoZSBkYXRhLCB3ZSBmaWx0ZXIgb3V0IHJlY29yZHMgbGFja2luZyBsYXRpdHVkZSBvciBsb25naXR1ZGUgaW5mb3JtYXRpb24gYW5kIGFkZHJlc3MgZXJyb3JzIHdoZXJlIGNlcnRhaW4gY29vcmRpbmF0ZXMgaW5jb3JyZWN0bHkgcGxhY2Ugc3RhdGlvbnMgaW4gdGhlIEh1ZHNvbiBSaXZlci4gVGhlc2UgaW5hY2N1cmF0ZSBlbnRyaWVzIGFyZSByZW1vdmVkIHRvIGVuc3VyZSBkYXRhIHF1YWxpdHkuCgoKCmBgYHtyIGFkZCBjZW5zdXMgdHJhY3R9CgpiaWtlX2NlbnN1cyA8LSBzdF9qb2luKG55Y2Jpa2UgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKGlzLm5hKHN0YXJ0X2xhdCkgPT0gRkFMU0UgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlzLm5hKHN0YXJ0X2xuZykgPT0gRkFMU0UgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlzLm5hKGVuZF9sYXQpID09IEZBTFNFICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpcy5uYShlbmRfbG5nKSA9PSBGQUxTRSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICBzdF9hc19zZiguLCBjb29yZHMgPSBjKCJzdGFydF9sbmciLCAic3RhcnRfbGF0IiksIGNycyA9IDQzMjYpLAogICAgICAgICAgICAgICAgICAgICAgIGpjVHJhY3RzICU+JSAgc3RfdHJhbnNmb3JtKGNycz00MzI2KSwKICAgICAgICAgICAgICAgICAgICAgICBqb2luPXN0X2ludGVyc2VjdHMsIGxlZnQgPSBUUlVFKSAlPiUKICByZW5hbWUoT3JpZ2luLlRyYWN0ID0gR0VPSUQpICU+JQogIG11dGF0ZShzdGFydF9sbmcgPSB1bmxpc3QobWFwKGdlb21ldHJ5LCAxKSksCiAgICAgICAgIHN0YXJ0X2xhdCA9IHVubGlzdChtYXAoZ2VvbWV0cnksIDIpKSklPiUKICBmaWx0ZXIoIShyaWRlX2lkICVpbiUgYygiQkVDOUNDNEQxMDA3Qzc1MyIsICI4NjA4N0Y0MzE3ODVFREU3IiwgIkExQUFCOTI2MzE0Mzk4ODMiLCAiNzE5MzhEQTA4NTI0MkRDQyIsICI0MjAzQzc3NjY4QzQ3Qzc1IiwgIkM5MjQxMENBMkFFRjM5NDciLCAiNkUwMkNCRjM2QzkwRjI0NCIsICIwMjU5ODg1MjUzRkQyMzhFIiwgIkIzOTEzNkIzN0FGQjVGRTkiLCAiMUVDOTM3NkQxNTU5QzUxQyIsICJDRERGMTZEM0EzN0JFMzcwIikpKSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpCiAgCmBgYAoKVGhlIGZvbGxvd2luZyBtYXAgc2hvd3MgdGhlIExvY2FydGlvbiBvZiByaWRlIHN0YXJ0aW5nIHBvaW50cyBpbiBKZXJzZXkgQ2l0eQoKYGBge3IgZGlzdHJpYnV0aW9uIG9mIHN0YXRpb25zfQoKZ2dwbG90KCkrCiAgZ2VvbV9zZihkYXRhID0gamNUcmFjdHMgJT4lCiAgICAgICAgICBzdF90cmFuc2Zvcm0oY3JzPTQzMjYpLCBmaWxsPSJ3aGl0ZSIpKwogIGdlb21fcG9pbnQoZGF0YSA9IGJpa2VfY2Vuc3VzLCBhZXMoeD1zdGFydF9sbmcsIHkgPSBzdGFydF9sYXQpLCAKICAgICAgICAgICAgY29sb3IgPSAiYmx1ZSIsIGFscGhhID0gMSwgc2l6ZSA9IDAuMykgKyAKICB5bGltKG1pbihiaWtlX2NlbnN1cyRzdGFydF9sYXQpLCBtYXgoYmlrZV9jZW5zdXMkc3RhcnRfbGF0KSkrCiAgeGxpbShtaW4oYmlrZV9jZW5zdXMkc3RhcnRfbG5nKSwgbWF4KGJpa2VfY2Vuc3VzJHN0YXJ0X2xuZykpICsKICBsYWJzKHRpdGxlID0gIkxvY2F0aW9uIG9mIFJpZGVzIFN0YXJ0aW5nIFBvaW50cyBpbiBKZXJzZXkgQ2l0eSIpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MgPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBsaW5ld2lkdGg9MC44KQogICAgICAgICkKCmBgYAoKV2UgY2Fycnkgb3V0IGVzc2VudGlhbCBmZWF0dXJlIGVuZ2luZWVyaW5nIHRvIGRlcml2ZSB0ZW1wb3JhbCBpbnNpZ2h0cyBmcm9tIHRoZSByaWRlIGRhdGEuIFNwZWNpZmljYWxseSwgd2UnbGwgY3JlYXRlIHRpbWUgaW50ZXJ2YWxzIGF0IGJvdGggNjAtbWludXRlIGFuZCAxNS1taW51dGUgaW5jcmVtZW50cyBiYXNlZCBvbiBlYWNoIHJpZGUncyBzdGFydCB0aW1lLiBBZGRpdGlvbmFsbHksIHdlJ2xsIGNhdGVnb3JpemUgdGhlc2UgaW50ZXJ2YWxzIGludG8gYnJvYWRlciB0aW1lIHBlcmlvZHMsIHN1Y2ggYXMgIk92ZXJuaWdodCwiICJBTSBSdXNoLCIgIk1pZC1EYXksIiBhbmQgIlBNIFJ1c2gsIiBhbmQgY2xhc3NpZnkgZWFjaCBkYXkgYXMgZWl0aGVyIGEgd2Vla2RheSBvciB3ZWVrZW5kLgoKYGBge3IgdGVtcG9yYWwgZmVhdHVyZSBlbmdpbmVlcmluZ30KYmlrZV9jZW5zdXMgPC0gYmlrZV9jZW5zdXMgJT4lCiAgbXV0YXRlKAogICAgaW50ZXJ2YWw2MCA9IGZsb29yX2RhdGUoeW1kX2htcyhzdGFydGVkX2F0KSwgdW5pdCA9ICJob3VyIiksCiAgICBpbnRlcnZhbDE1ID0gZmxvb3JfZGF0ZSh5bWRfaG1zKHN0YXJ0ZWRfYXQpLCB1bml0ID0gIjE1IG1pbnMiKSwKICAgIHdlZWsgPSB3ZWVrKGludGVydmFsNjApLAogICAgZG90dyA9IHdkYXkoaW50ZXJ2YWw2MCksICAjIFJlbW92ZWQgbGFiZWwgPSBUUlVFCiAgICB0aW1lX29mX2RheSA9IGNhc2Vfd2hlbigKICAgICAgaG91cihpbnRlcnZhbDYwKSA8IDcgfCBob3VyKGludGVydmFsNjApID4gMTggfiAiT3Zlcm5pZ2h0IiwKICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSA3ICYgaG91cihpbnRlcnZhbDYwKSA8IDEwIH4gIkFNIFJ1c2giLAogICAgICBob3VyKGludGVydmFsNjApID49IDEwICYgaG91cihpbnRlcnZhbDYwKSA8IDE1IH4gIk1pZC1EYXkiLAogICAgICBob3VyKGludGVydmFsNjApID49IDE1ICYgaG91cihpbnRlcnZhbDYwKSA8PSAxOCB+ICJQTSBSdXNoIgogICAgKSwKICAgIGhvdXIgPSBob3VyKHN0YXJ0ZWRfYXQpLAogICAgd2Vla2VuZCA9IGlmZWxzZShkb3R3ICVpbiUgYygxLCA3KSwgIldlZWtlbmQiLCAiV2Vla2RheSIpICAjIFVzZSBudW1lcmljIHZhbHVlcyBmb3IgZGF5cwogICkKCmBgYAoKVG8gYmV0dGVyIHVuZGVyc3RhbmQgY29tbXV0aW5nIHBhdHRlcm5zIGFuZCByaWRlciBiZWhhdmlvciB0aHJvdWdob3V0IHRoZSBkYXksIHdlIGdlbmVyYXRlZCBsaW5lIHN0cmluZ3MgdG8gcmVwcmVzZW50IHRoZSByb3V0ZXMgYmV0d2VlbiB0aGUgc3RhcnQgYW5kIGVuZCBwb2ludHMgb2YgcmlkZXMuIFRvIHNpbXBsaWZ5IHRoZSB2aXN1YWxpemF0aW9uLCB3ZSBsaW1pdGVkIGl0IHRvIDIwMCBzYW1wbGUgcmlkZXMuIFRoZSB2aXN1YWxpemF0aW9uIHJldmVhbHMgdGhhdCB0aGUgbWFqb3JpdHkgb2YgdHJpcHMgYXJlIGNvbmZpbmVkIHRvIEplcnNleSBDaXR5LCB0aG91Z2ggc29tZSByaWRlcnMgdXNlIENpdGkgQmlrZSB0byB0cmF2ZWwgZnJvbSBKZXJzZXkgQ2l0eSB0byBNYW5oYXR0YW4uCgpgYGB7ciBvcmlnaW4tZGVzaW50YXRpb24gbWF0cml4LCB3YXJuaW5nPUZBTFNFfQoKbGluZXMgPC0gc3Rfc2ZjKAogIGxhcHBseSgxOm5yb3coYmlrZV9jZW5zdXMpLCBmdW5jdGlvbihpKSB7CiAgICBzdF9saW5lc3RyaW5nKG1hdHJpeChjKGJpa2VfY2Vuc3VzW2ksICJzdGFydF9sbmciXSwgYmlrZV9jZW5zdXNbaSwgInN0YXJ0X2xhdCJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYmlrZV9jZW5zdXNbaSwgImVuZF9sbmciXSwgYmlrZV9jZW5zdXNbaSwgImVuZF9sYXQiXSksIG5jb2wgPSAyLCBieXJvdyA9IFRSVUUpKQogIH0pKQoKbGluZXNfc2YgPC0gc3Rfc2YoZ2VvbWV0cnkgPSBsaW5lcykKbGluZXNfc2YgPC0gc3Rfc2V0X2NycyhsaW5lc19zZiwgNDMyNikKClJvdXRlIDwtIGxpbmVzX3NmICU+JSBoZWFkKDIwMCkKbWFwdmlldyhSb3V0ZSwgemNvbCA9IE5VTEwsIGNvbG9yID0gIiM2RTBFMEEiLCBsaW5ld2lkdGggPSAwLjAwMSwgYWxwaGEgPSAwLjMpICMgb25seSBzaG93IDIwMApgYGAKCgojIEV4cGxvcmF0b3J5IEFuYWx5c2lzCgpJbiB0aGlzIHNlY3Rpb24sIHdlIGRlbHZlIGludG8gc29tZSBmdXJ0aGVyIGV4cGxvcmF0b3J5IGFuYWx5c2lzIG9mIG91ciBkYXRhIHNldC4gCgojIyBUZW1wb3JhbCBBbmFseXNpcwoKV2UgYmVnYW4gb3VyIGFuYWx5c2lzIGJ5IGV4YW1pbmluZyB0aGUgdGVtcG9yYWwgcGF0dGVybnMgb2YgYmlrZXNoYXJlIHVzYWdlIGluIEplcnNleSBDaXR5IHRocm91Z2hvdXQgQXVndXN0IDIwMjMsIGZvY3VzaW5nIG9uIHRoZSBkaXN0cmlidXRpb24gb2YgdHJpcHMgYnkgdGhlIGhvdXIuIE91ciB2aXN1YWxpemF0aW9uIHJldmVhbHMgZGlzdGluY3QgZmx1Y3R1YXRpb25zLCB3aXRoIGRlbWFuZCByaXNpbmcgYW5kIGZhbGxpbmcgaW4gYSBwcmVkaWN0YWJsZSBjeWNsZSBlYWNoIGRheS4gRHVyaW5nIHdlZWtkYXlzLCB3ZSBvYnNlcnZlIHByb25vdW5jZWQgcGVha3MgdGhhdCBhbGlnbiB3aXRoIHR5cGljYWwgcnVzaCBob3Vyc+KAlG1vcm5pbmdzIGFuZCBsYXRlIGFmdGVybm9vbnPigJR3aGVuIHBlb3BsZSBhcmUgY29tbXV0aW5nIHRvIGFuZCBmcm9tIHdvcmsuIFRoZXNlIGJ1c3kgcGVyaW9kcyBhcmUgZm9sbG93ZWQgYnkgbm90aWNlYWJsZSBsdWxscyBkdXJpbmcgdGhlIG5pZ2h0IGFuZCBlYXJseSBtb3JuaW5nIGhvdXJzLCBhcyB3ZWxsIGFzIGR1cmluZyB3ZWVrZW5kcywgd2hlbiBvdmVyYWxsIHVzYWdlIHRlbmRzIHRvIGRlY3JlYXNlLiBUaGUgcGF0dGVybiBpcyBjaGFyYWN0ZXJpemVkIGJ5IGEgc2VyaWVzIG9mIGZpdmUgY29uc2VjdXRpdmUgaGlnaC1kZW1hbmQgcGVha3MgdGhhdCBjb3JyZXNwb25kIHRvIHdlZWtkYXlzLCBjb25zaXN0ZW50bHkgZm9sbG93ZWQgYnkgdHdvIGxvd2VyIHBlYWtzIG92ZXIgdGhlIHdlZWtlbmQsIGhpZ2hsaWdodGluZyB0aGUgaW5mbHVlbmNlIG9mIHdvcmt3ZWVrIHJvdXRpbmVzIG9uIGJpa2VzaGFyZSBhY3Rpdml0eS4gVGhpcyBjeWNsaWNhbCBiZWhhdmlvciB1bmRlcnNjb3JlcyB0aGUgc3Ryb25nIGxpbmsgYmV0d2VlbiBiaWtlc2hhcmUgZGVtYW5kIGFuZCBkYWlseSBjb21tdXRpbmcgc2NoZWR1bGVzLgoKYGBge3IgYmlrZXNoYXJlIHZpejF9CmdncGxvdChiaWtlX2NlbnN1cyAlPiUKICAgICAgICAgZ3JvdXBfYnkoaW50ZXJ2YWw2MCkgJT4lCiAgICAgICAgIHRhbGx5KCkpKwogIGdlb21fbGluZShhZXMoeCA9IGludGVydmFsNjAsIHkgPSBuKSwgY29sb3I9ICJibHVlIikrCiAgbGFicyh0aXRsZT0iQmlrZSBTaGFyZSBUcmlwcyBQZXIgSG91ciBpbiBKZXJzZXkgQ2l0eSwgQXVnIDIwMjMiLAogICAgICAgeD0iRGF0ZSIsIAogICAgICAgeT0iTnVtYmVyIG9mIHRyaXBzIikgKyAKdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9OCksIAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTApLCAKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIGxpbmV3aWR0aCA9MC44KSkKICAKYGBgCgpOZXh0LCB3ZSBhbmFseXplZCBob3cgdHJpcHMgd2VyZSBkaXN0cmlidXRlZCBhY3Jvc3MgZGlmZmVyZW50IHN0YXRpb25zIG9uIGFuIGhvdXJseSBiYXNpcyB0aHJvdWdob3V0IEF1Z3VzdC4gVGhlIHJlc3VsdGluZyB2aXN1YWxpemF0aW9uIHJldmVhbGVkIGEgaGlnaGx5IHNrZXdlZCBkaXN0cmlidXRpb246IHRoZSB2YXN0IG1ham9yaXR5IG9mIHN0YXRpb25zIHJlY29yZGVkIGVpdGhlciBubyByaWRlcyBvciBqdXN0IGEgZmV3IHRyaXBzIHBlciBob3VyLiBIb3dldmVyLCBhIHNlbGVjdCBmZXcgc3RhdGlvbnMgc3Rvb2Qgb3V0LCBleHBlcmllbmNpbmcgc2lnbmlmaWNhbnRseSBoaWdoZXIgZGVtYW5kLiBUaGVzZSBoaWdoLXRyYWZmaWMgc3RhdGlvbnMgc29tZXRpbWVzIHNhdyBhcyBtYW55IGFzIDQwIHRvIDUwIHJpZGVzIHdpdGhpbiBhIHNpbmdsZSBob3VyIG9uIGNlcnRhaW4gZGF5cy4gVGhpcyB1bmV2ZW4gZGlzdHJpYnV0aW9uIGhpZ2hsaWdodHMgdGhlIHZhcnlpbmcgbGV2ZWxzIG9mIGJpa2VzaGFyZSB1c2FnZSBhY3Jvc3MgSmVyc2V5IENpdHksIHdpdGggY2VydGFpbiBsb2NhdGlvbnMgYWN0aW5nIGFzIG1ham9yIGh1YnMgb2YgYWN0aXZpdHkgd2hpbGUgbWFueSBvdGhlcnMgZXhwZXJpZW5jZSBtaW5pbWFsIHJpZGVyc2hpcC4KCmBgYHtyIGJpa2VzaGFyZSB2aXozLCB3YXJuaW5nPUZBTFNFfQoKZ2dwbG90KGJpa2VfY2Vuc3VzICU+JQogICAgICAgICBncm91cF9ieShpbnRlcnZhbDYwLCBzdGFydF9zdGF0aW9uX25hbWUpICU+JQogICAgICAgICB0YWxseSgpKSsKICBnZW9tX2hpc3RvZ3JhbShhZXMobiksIGJpbndpZHRoID0gNSwgZmlsbD0ibGlnaHRibHVlIikrCiAgbGFicyh0aXRsZT0iQmlrZSBTaGFyZSBUcmlwcyBQZXIgSG91ciBieSBTdGF0aW9uIGluIEplcnNleSBDaXR5LCBBdWcgMjAyMyIsCiAgICAgICB4PSJUcmlwIENvdW50cyIsIAogICAgICAgeT0iTnVtYmVyIG9mIFN0YXRpb25zIikgKyAKICAgIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksIAogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTgpLCAKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEwKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBsaW5ld2lkdGggPTAuOCkpCgpgYGAKCldlIGZ1cnRoZXIgZXhwbG9yZWQgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIHRyaXBzIHBlciBzdGF0aW9uIGF0IGRpZmZlcmVudCB0aW1lcyBvZiB0aGUgZGF5LiBUaGUgYW5hbHlzaXMgc2hvd2VkIHRoYXQgUE0gcnVzaCBob3VycyBjb25zaXN0ZW50bHkgaGFkIHRoZSBoaWdoZXN0IGF2ZXJhZ2UgdHJpcCBjb3VudHMsIHdpdGggc2V2ZXJhbCBzdGF0aW9ucyBhdmVyYWdpbmcgbW9yZSB0aGFuIDEwIHRyaXBzLCBpbmRpY2F0aW5nIHN1YnN0YW50aWFsIGRlbWFuZCBhdCB0aGVzZSBsb2NhdGlvbnMuIE1pZGRheSBjYW1lIG5leHQgaW4gdGVybXMgb2YgYWN0aXZpdHkgbGV2ZWxzLCB3aGVyZSBzb21lIHN0YXRpb25zIHJlY29yZGVkIGF2ZXJhZ2VzIG9mIG92ZXIgNSB0cmlwcy4gU3VycHJpc2luZ2x5LCB0aGVyZSB3YXMgYWxzbyBjb25zaWRlcmFibGUgZGVtYW5kIG92ZXJuaWdodCwgYXMgYSBmZXcgc3RhdGlvbnMgYXZlcmFnZWQgbW9yZSB0aGFuIDUgcmlkZXMgZXZlbiBkdXJpbmcgbGF0ZS1uaWdodCBob3Vycy4gVGhpcyB1bmV4cGVjdGVkIG92ZXJuaWdodCBhY3Rpdml0eSBzdWdnZXN0cyB0aGF0IGJpa2VzaGFyZSB1c2FnZSBleHRlbmRzIGJleW9uZCB0cmFkaXRpb25hbCBjb21tdXRpbmcgcGF0dGVybnMsIHBvdGVudGlhbGx5IHNlcnZpbmcgb3RoZXIgcHVycG9zZXMgc3VjaCBhcyBsYXRlLW5pZ2h0IGxlaXN1cmUgb3IgZWFybHktbW9ybmluZyB0cmF2ZWwuCgoKCmBgYHtyIGJpa2VzaGFyZSB2aXoyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQoKYmlrZV9jZW5zdXMgJT4lCiAgICAgICAgZ3JvdXBfYnkoaW50ZXJ2YWw2MCwgc3RhcnRfc3RhdGlvbl9uYW1lLCB0aW1lX29mX2RheSkgJT4lCiAgICAgICAgIHRhbGx5KCklPiUKICBncm91cF9ieShzdGFydF9zdGF0aW9uX25hbWUsIHRpbWVfb2ZfZGF5KSU+JQogIHN1bW1hcml6ZShtZWFuX3RyaXBzID0gbWVhbihuKSklPiUKICBnZ3Bsb3QoKSsKICBnZW9tX2hpc3RvZ3JhbShhZXMobWVhbl90cmlwcywgZmlsbD10aW1lX29mX2RheSksIGJpbndpZHRoID0gMSkrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTQsIG5hbWU9IlRpbWUgb2YgRGF5IikgKyAKICBsYWJzKHRpdGxlPSJNZWFuIE51bWJlciBvZiBIb3VybHkgVHJpcHMgUGVyIFN0YXRpb24iLAogICAgICAgeD0iTnVtYmVyIG9mIHRyaXBzIiwgCiAgICAgICB5PSJGcmVxdWVuY3kiKSsKICBmYWNldF93cmFwKH50aW1lX29mX2RheSkgKwp0aGVtZShwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT04KSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksIAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpKQoKYGBgCgpXZSB0aGVuIGV4YW1pbmVkIHRoZSB0b3RhbCBudW1iZXIgb2YgdHJpcHMgdGhyb3VnaG91dCBBdWd1c3QsIGJyZWFraW5nIHRoZW0gZG93biBieSBkYXkgb2YgdGhlIHdlZWsuIFR3byBrZXkgcGF0dGVybnMgZW1lcmdlZCBmcm9tIHRoaXMgdmlzdWFsaXphdGlvbi4gRmlyc3QsIHRoZSBudW1iZXIgb2YgdHJpcHMgZmx1Y3R1YXRlcyBzaWduaWZpY2FudGx5IG92ZXIgdGhlIGNvdXJzZSBvZiBhIGRheSwgd2l0aCB0aGUgbG93ZXN0IGFjdGl2aXR5IG9ic2VydmVkIGluIHRoZSBlYXJseSBtb3JuaW5nIGhvdXJzIGFuZCB0aGUgaGlnaGVzdCBpbiB0aGUgbGF0ZSBhZnRlcm5vb24sIGNvcnJlc3BvbmRpbmcgdG8gZXZlbmluZyBjb21tdXRlIHRpbWVzLiBTZWNvbmQsIHRoZSBkYXRhIHNob3dzIHRoYXQgdHJpcCB2b2x1bWVzIGFyZSBnZW5lcmFsbHkgaGlnaGVyIG9uIFR1ZXNkYXlzLCBXZWRuZXNkYXlzLCBhbmQgVGh1cnNkYXlzLCBzdWdnZXN0aW5nIHRoYXQgbWlkd2VlayBkYXlzIGV4cGVyaWVuY2UgdGhlIGdyZWF0ZXN0IGRlbWFuZCBmb3IgYmlrZXNoYXJlIHNlcnZpY2VzLCBsaWtlbHkgZHJpdmVuIGJ5IHJlZ3VsYXIgd29yay1yZWxhdGVkIHRyYXZlbC4KCmBgYHtyIGJpa2VzaGFyZSB2aXo0fQoKZ2dwbG90KGJpa2VfY2Vuc3VzICU+JSBtdXRhdGUoaG91ciA9IGhvdXIoc3RhcnRlZF9hdCkpKSsKICAgICBnZW9tX2ZyZXFwb2x5KGFlcyhob3VyLCBjb2xvciA9IGRvdHcpLCBiaW53aWR0aCA9IDMsIGx3ZCA9IDAuOCkrCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiMxMjRFNzgiLCAiIzgxOUZBMSIsICIjRjBGMEM5IiwgIiNGMkJCMDUiLCAiI0U1ODUwNyIsICIjRDc0RTA5IiwgIiM2RTBFMEEiKSwgbmFtZSA9ICJEYXkiKSArIAogIGxhYnModGl0bGU9IkJpa2UgU2hhcmUgVHJpcHMgaW4gSmVyc2V5IENpdHkgYnkgRGF5IG9mIHRoZSBXZWVrLCBBdWcgMjAyMyIsCiAgICAgICB4PSJIb3VyIiwgCiAgICAgICB5PSJUcmlwIENvdW50cyIpICsgCiAgdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9OCksIAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTApLCAKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIHNpemU9MC44KSkKCmBgYAoKV2UgY3JlYXRlZCBhIHNpbWlsYXIgdmlzdWFsaXphdGlvbiBidXQgdGhpcyB0aW1lIGNvbXBhcmVkIHRyaXAgdm9sdW1lcyBiZXR3ZWVuIHdlZWtkYXlzIGFuZCB3ZWVrZW5kcy4gVGhlIGRpZmZlcmVuY2VzIGFyZSBzdHJpa2luZzogYmlrZXNoYXJlIHVzYWdlIGlzIG5vdGljZWFibHkgaGlnaGVyIG9uIHdlZWtkYXlzIGNvbXBhcmVkIHRvIHdlZWtlbmRzLiBBZGRpdGlvbmFsbHksIG9uIHdlZWtlbmRzLCB0cmlwcyBzdGFydCB0byBwaWNrIHVwIG11Y2ggbGF0ZXIgaW4gdGhlIGRheSwgcmVmbGVjdGluZyB0aGUgYWJzZW5jZSBvZiB0aGUgdHlwaWNhbCB3ZWVrZGF5IG1vcm5pbmcgcnVzaC4gVGhpcyBkZWxheSBzdWdnZXN0cyB0aGF0IHdpdGhvdXQgdGhlIHByZXNzdXJlIG9mIGVhcmx5IHdvcmsgY29tbXV0ZXMsIHBlb3BsZSBhcmUgbW9yZSBsaWtlbHkgdG8gdXNlIGJpa2VzaGFyZSBzZXJ2aWNlcyBsYXRlciBpbiB0aGUgZGF5IGZvciBsZWlzdXJlIG9yIG5vbi13b3JrLXJlbGF0ZWQgYWN0aXZpdGllcywgaGlnaGxpZ2h0aW5nIHRoZSBpbmZsdWVuY2Ugb2YgZGFpbHkgcm91dGluZXMgb24gYmlrZXNoYXJlIGRlbWFuZC4KCmBgYHtyIGJpa2VzaGFyZSB2aXogNX0KCmdncGxvdChiaWtlX2NlbnN1cykrCiAgICAgZ2VvbV9mcmVxcG9seShhZXMoaG91ciwgY29sb3IgPSB3ZWVrZW5kKSwgYmlud2lkdGggPSAzLCBsd2QgPSAxLjIpKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9cGFsZXR0ZTIpICsgCiAgbGFicyhjb2xvciA9ICJUaW1lIG9mIFdlZWsiKSArCiAgbGFicyh0aXRsZT0iQmlrZSBTaGFyZSBUcmlwcyBpbiBKZXJzZXkgQ2l0eSAtIFdlZWtlbmQgdnMgV2Vla2RheSwgQXVnIDIwMjMiLAogICAgICAgeD0iSG91ciIsIAogICAgICAgeT0iVHJpcCBDb3VudHMiKSArIAogIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksIAogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTgpLCAKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEwKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkpCgpgYGAKCmBgYHtyIG9yaWdpbl9tYXAgfQojIEFzc3VtaW5nIGBiaWtlX2NlbnN1c2AgaGFzIGEgY29sdW1uICdPcmlnaW4uVHJhY3QnIHRoYXQgbWFwcyBlYWNoIGJpa2UgdHJpcCB0byBhIGNlbnN1cyB0cmFjdAojIGFuZCBgamNUcmFjdHNgIGlzIGFscmVhZHkgbG9hZGVkIGFuZCB0cmFuc2Zvcm1lZCBhcyBuZWNlc3NhcnkKCiMgQWdncmVnYXRlIGJpa2UgdHJpcCBkYXRhIGJ5IGNlbnN1cyB0cmFjdApiaWtlX3RyaXBzX2J5X3RyYWN0IDwtIGJpa2VfY2Vuc3VzICU+JQogIGdyb3VwX2J5KE9yaWdpbi5UcmFjdCkgJT4lCiAgc3VtbWFyaXNlKFRvdGFsX1RyaXBzID0gbigpLCAuZ3JvdXBzID0gJ2Ryb3AnKSAlPiUKICB1bmdyb3VwKCkKCiMgSm9pbiB0aGlzIGFnZ3JlZ2F0ZWQgZGF0YSB0byB0aGUgamNUcmFjdHMgR2VvRGF0YUZyYW1lCmpjVHJhY3RzIDwtIHN0X3RyYW5zZm9ybShqY1RyYWN0cywgY3JzID0gNDMyNikgICMgTWFrZSBzdXJlIENSUyBtYXRjaGVzLCBpZiBub3QgYWxyZWFkeQpqY1RyYWN0c19kYXRhIDwtIGpjVHJhY3RzICU+JQogIGxlZnRfam9pbihiaWtlX3RyaXBzX2J5X3RyYWN0LCBieSA9IGMoIkdFT0lEIiA9ICJPcmlnaW4uVHJhY3QiKSkKCiMgQ2FsY3VsYXRlIHRoZSBib3VuZGluZyBib3ggZm9yIGNlbnRlcmluZwpiYm94IDwtIHN0X2Jib3goamNUcmFjdHNfZGF0YSkKCiMgUGxvdCB0aGUgY2hvcm9wbGV0aCBtYXAgd2l0aCBhZGp1c3RlZCBsaW1pdHMKZ2dwbG90KGRhdGEgPSBqY1RyYWN0c19kYXRhKSArCiAgZ2VvbV9zZihhZXMoZmlsbCA9IFRvdGFsX1RyaXBzKSwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMC4xKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gIkQiLCBuYW1lID0gIlRvdGFsIEJpa2UgVHJpcHMiLCBuYS52YWx1ZSA9ICJ3aGl0ZSIpICsKICBjb29yZF9zZih4bGltID0gYyhiYm94WyJ4bWluIl0sIGJib3hbInhtYXgiXSksIHlsaW0gPSBjKGJib3hbInltaW4iXSwgYmJveFsieW1heCJdKSwgZXhwYW5kID0gRkFMU0UpICsgICMgU2V0IGxpbWl0cwogIGxhYnModGl0bGUgPSAiQmlrZSBTaGFyZSBUcmlwcyBwZXIgQ2Vuc3VzIFRyYWN0IGluIEplcnNleSBDaXR5IikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikpCgpgYGAKCkV4cGFuZGluZyBvbiBvdXIgYW5hbHlzaXMsIHdlIGdyb3VwZWQgdGhlIGRhdGEgYnkgc3RhdGlvbiBhbmQgdGltZSBvZiBkYXkgdG8gZXhwbG9yZSB0cmlwIHZvbHVtZXMgZHVyaW5nIGRpZmZlcmVudCBwZXJpb2RzIG9uIHdlZWtkYXlzIHZlcnN1cyB3ZWVrZW5kcy4gVG8ga2VlcCB0aGUgdmlzdWFsaXphdGlvbiBjbGVhciwgd2UgZm9jdXNlZCBvbiB0aGUgdG9wIDUwIHN0YXRpb24tdGltZSBvY2N1cnJlbmNlcy4gRWFjaCBiYXIgaW4gdGhlIGNoYXJ0IHJlcHJlc2VudHMgYSBzcGVjaWZpYyBjb21iaW5hdGlvbiBvZiBzdGF0aW9uLCB0aW1lIG9mIGRheSwgYW5kIHdoZXRoZXIgaXQncyBhIHdlZWtkYXkgb3Igd2Vla2VuZC4gVGhlIHJlc3VsdHMgY2xlYXJseSBzaG93IHRoYXQgUE0gcnVzaCBob3VycyBnZW5lcmF0ZSB0aGUgaGlnaGVzdCBiaWtlc2hhcmUgZGVtYW5kLCBwYXJ0aWN1bGFybHkgYXQgYSBmZXcga2V5IHN0YXRpb25zLiBOb3RhYmx5LCB3ZWVrZW5kIHJpZGVzIGFyZSBzY2FyY2UgaW4gdGhlIHRvcCA1MCBsaXN0LCBhcHBlYXJpbmcgb25seSB0aHJlZSB0aW1lcy4gQSBjbG9zZXIgbG9vayByZXZlYWxzIHRoYXQgR3JvdmUgU3QgUEFUSCBzdGF0aW9uIGhhZCBhIHRvdGFsIG9mIDEsMTE3IHJpZGVzIGR1cmluZyBQTSBydXNoIG9uIGEgd2Vla2RheSBhbmQgODAxIHJpZGVzIG92ZXJuaWdodCBvbiBhIHdlZWtkYXkgaW4gQXVndXN0LiBIb2Jva2VuIFRlcm1pbmFsIC0gUml2ZXIgU3QgJiBIdWRzb24gUGwgcmVjb3JkZWQgODY0IHJpZGVzIGR1cmluZyB3ZWVrZGF5IFBNIHJ1c2guIFNvdXRoIFdhdGVyZnJvbnQgV2Fsa3dheSAtIFNpbmF0cmEgRHIgJiAxIFN0IGhhZCAzMTkgcmlkZXMgb3Zlcm5pZ2h0IG9uIGEgd2Vla2VuZCBhbmQgNzMxIHJpZGVzIGR1cmluZyBQTSBydXNoIG9uIGEgd2Vla2RheS4gVGhlc2UgZGV0YWlscyB1bmRlcnNjb3JlIGhvdyBjZXJ0YWluIHN0YXRpb25zIGV4cGVyaWVuY2Ugc2lnbmlmaWNhbnQgc3Bpa2VzIGluIGRlbWFuZCwgZXNwZWNpYWxseSBkdXJpbmcgd2Vla2RheSBjb21tdXRlIGhvdXJzLgoKYGBge3IgYmlrZXNoYXJlIHZpeiA3LCBmaWcud2lkdGg9MTB9Cgp0b19wbG90IDwtIGJpa2VfY2Vuc3VzJT4lICMgb25seSBzaG93IDEwMCBzdGF0aW9ucwogIGdyb3VwX2J5KHN0YXJ0X3N0YXRpb25faWQsIHN0YXJ0X2xhdCwgc3RhcnRfbG5nLCB3ZWVrZW5kLCB0aW1lX29mX2RheSwgc3RhcnRfc3RhdGlvbl9uYW1lKSAlPiUKICB0YWxseSgpICU+JSAKICBhcnJhbmdlKC1uKSAlPiUgCiAgaGVhZCg1MCkgCnRvX3Bsb3QkSUQgPC0gc2VxX2Fsb25nKHRvX3Bsb3Qkc3RhcnRfc3RhdGlvbl9pZCkKCnRvX3Bsb3QgJT4lIAogIGFycmFuZ2UoLW4pICU+JQogIGhlYWQoNTApICU+JSAKICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyKElELCAtbiksIG4sIGZpbGwgPSB0aW1lX29mX2RheSwgY29sb3IgPSB3ZWVrZW5kKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGU0LCBuYW1lPSJUaW1lIG9mIERheSIpICsgCiAgZ3VpZGVzKGNvbG9yPSJub25lIikgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb249InN0YWNrIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJ0cmFuc3BhcmVudCIsICJibGFjayIpKSArCiAgbGFicyh0aXRsZT0iVG9wIDUwIE9jY3VyZW5jZXMgb2YgUmlkZXMgQnkgVGltZSIpKwogIHlsYWIoIk51bWJlciBvZiBSaWRlcyIpICsKICB4bGFiKCIiKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsgCiAgdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTgpLCAKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEwKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkpCmBgYAoKCiMjIFNwYXRpYWwgQW5hbHlzaXMKCkJleW9uZCBvdXIgdGVtcG9yYWwgYW5hbHlzaXMsIHdlIGNyZWF0ZWQgbWFwcyB0byB2aXN1YWxpemUgdGhlIGxvY2F0aW9ucyBvZiBzdGF0aW9ucyB3aXRoIHRoZSBoaWdoZXN0IGJpa2VzaGFyZSBkZW1hbmQsIGJyb2tlbiBkb3duIGJ5IHRpbWUgb2YgZGF5IGFuZCBkYXkgb2YgdGhlIHdlZWsuIFRoZXNlIHByb3BvcnRpb25hbCBtYXBzIHJldmVhbCB0aGF0IHRoZSBncmVhdGVzdCBkZW1hbmQgb2NjdXJzIGR1cmluZyBQTSBydXNoIGhvdXJzIGFuZCBvdmVybmlnaHQgb24gd2Vla2RheXMsIHdoaWxlIEFNIHJ1c2ggaG91cnMgc2VlIHRoZSBsb3dlc3QgYWN0aXZpdHkuIFRoZSBzdGF0aW9ucyB3aXRoIHRoZSBoaWdoZXN0IHVzYWdlIGFyZSBwcmVkb21pbmFudGx5IGNsdXN0ZXJlZCBhbG9uZyB0aGUgSHVkc29uIFJpdmVyLiBUaGlzIHBhdHRlcm4gY291bGQgYmUgZHVlIHRvIHR3byBmYWN0b3JzOiBhIGhpZ2hlciBjb25jZW50cmF0aW9uIG9mIHN0YXRpb25zIGluIHRoaXMgYXJlYSBvciBzdHJhdGVnaWMgcGxhY2VtZW50IGJ5IENpdGkgQmlrZSB0byBhY2NvbW1vZGF0ZSBjb21tdXRlcnMgdHJhdmVsaW5nIGZyb20gSmVyc2V5IENpdHkgdG8gTWFuaGF0dGFuIGZvciB3b3JrIG9yIHNjaG9vbC4gVGhlIHByb3hpbWl0eSB0byBtYWpvciB0cmFuc2l0IGNvbm5lY3Rpb25zIGxpa2VseSBkcml2ZXMgdGhpcyBpbmNyZWFzZWQgZGVtYW5kLgoKYGBge3IgYmlrZXNoYXJlIHZpeiA2fQoKZ2dwbG90KCkrCiAgZ2VvbV9zZihkYXRhID0gamNUcmFjdHMgJT4lCiAgICAgICAgICBzdF90cmFuc2Zvcm0oY3JzPTQzMjYpLCBmaWxsID0gIndoaXRlIikrCiAgZ2VvbV9wb2ludChkYXRhID0gdG9fcGxvdCwKICAgICAgICAgICAgIGFlcyh4PXN0YXJ0X2xuZywgeSA9IHN0YXJ0X2xhdCwgc2l6ZT1uKSwgY29sb3IgPSBhbHBoYSgiIzZFMEUwQSIsIDAuNykpICsgCiAgeWxpbShtaW4oYmlrZV9jZW5zdXMkc3RhcnRfbGF0KSwgbWF4KGJpa2VfY2Vuc3VzJHN0YXJ0X2xhdCkpKwogIHhsaW0obWluKGJpa2VfY2Vuc3VzJHN0YXJ0X2xuZyksIG1heChiaWtlX2NlbnN1cyRzdGFydF9sbmcpKSArCiAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMC41LCA4KSkgKyAKICBsYWJzKHNpemUgPSAiTnVtYmVyIG9mIFJpZGVzIikgKwogIGZhY2V0X2dyaWQod2Vla2VuZCB+IHRpbWVfb2ZfZGF5KSArIAogICAgbGFicyh0aXRsZT0iTG9jYXRpb25zIG9mIFN0YXRpb25zIHdpdGggR3JlYXRlc3QgQmlrZXNoYXJlIERlbWFuZCBieSBUaW1lIikrIAogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcyA9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIGxpbmV3aWR0aD0wLjgpCiAgICAgICAgKQpgYGAKCldlIGFuYWx5emVkIGFsbCB0aGUgc3RhcnRpbmcgc3RhdGlvbnMgY29sbGVjdGl2ZWx5IHRvIGFzc2VzcyBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiwgYWltaW5nIHRvIGRldGVybWluZSBpZiBzdGF0aW9ucyB3aXRoIGhpZ2ggZGVtYW5kIHRlbmQgdG8gYmUgbG9jYXRlZCBuZWFyIG90aGVyIGhpZ2gtZGVtYW5kIHN0YXRpb25zLiBJZiBzdWNoIGEgcGF0dGVybiBleGlzdHMsIGl0IHdvdWxkIGFsbG93IHVzIHRvIHByZWRpY3QgZGVtYW5kIGF0IGEgc3BlY2lmaWMgc3RhdGlvbiBiYXNlZCBvbiB0aGUgYWN0aXZpdHkgbGV2ZWxzIGF0IG5laWdoYm9yaW5nIHN0YXRpb25zLiBUbyBpbnZlc3RpZ2F0ZSB0aGlzLCB3ZSBzZWxlY3RlZCB0aGUgdG9wIDgwIHN0YXRpb25zIHdpdGggdGhlIGhpZ2hlc3QgbnVtYmVyIG9mIHJpZGVzIGluIEF1Z3VzdCAyMDIzIGFuZCBjYWxjdWxhdGVkIE1vcmFuJ3MgSSB1c2luZyA5OTkgcmFuZG9tIE1vbnRlIENhcmxvIHNpbXVsYXRpb25zLiBUaGUgcmVzdWx0cyBpbmRpY2F0ZWQgYSBoaWdobHkgc2lnbmlmaWNhbnQgTW9yYW4ncyBJLCBjb25maXJtaW5nIHRoYXQgdGhlcmUgaXMgaW5kZWVkIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uIGFtb25nIG5lYXJieSBiaWtlc2hhcmUgc3RhdGlvbnMsIG1lYW5pbmcgZGVtYW5kIGlzIG5vdCByYW5kb21seSBkaXN0cmlidXRlZCBidXQgY2x1c3RlcmVkIGluIHNwZWNpZmljIGFyZWFzLgoKCgoKYGBge3Igc3BhdGlhbCBhdXRvIGNvciwgd2FybmluZz1GQUxTRX0KCm1vcmFuX2RhdGEgPC0gYmlrZV9jZW5zdXMgJT4lCiAgZ3JvdXBfYnkoc3RhcnRfc3RhdGlvbl9pZCwgc3RhcnRfbGF0LCBzdGFydF9sbmcsIHN0YXJ0X3N0YXRpb25fbmFtZSkgJT4lCiAgdGFsbHkoKSAlPiUgCiAgYXJyYW5nZSgtbikgJT4lIAogIGhlYWQoODApICU+JSAKICBzdF9hc19zZiguLCBjb29yZHMgPSBjKCJzdGFydF9sbmciLCAic3RhcnRfbGF0IiksIGNycyA9IDQzMjYpCgoKY29vcmRzIDwtICBzdF9jb29yZGluYXRlcyhtb3Jhbl9kYXRhKSAKbmVpZ2hib3JMaXN0IDwtIGtubjJuYihrbmVhcm5laWdoKGNvb3JkcywgNSkpCnNwYXRpYWxXZWlnaHRzIDwtIG5iMmxpc3R3KG5laWdoYm9yTGlzdCwgc3R5bGU9IlciKQoKbW9yYW5UZXN0IDwtIG1vcmFuLm1jKG1vcmFuX2RhdGEkbiwgCiAgICAgICAgICAgICAgICAgICAgICBzcGF0aWFsV2VpZ2h0cywgbnNpbSA9IDk5OSkKCmdncGxvdChhcy5kYXRhLmZyYW1lKG1vcmFuVGVzdCRyZXNbYygxOjk5OSldKSwgYWVzKG1vcmFuVGVzdCRyZXNbYygxOjk5OSldKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4wMSwgZmlsbCA9ICIjMTI0RTc4IikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtb3JhblRlc3Qkc3RhdGlzdGljKSwgY29sb3VyID0gIiM2RTBFMEEiICxsd2Q9MSkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKC0wLjUsIDAuNSkpICsKICBsYWJzKHRpdGxlID0gIk9ic2VydmVkIGFuZCBQZXJtdXRlZCBNb3JhbidzIEkgZm9yIFN0YXRpb25zIHdpdGggTW9zdCBSaWRlcyIsCiAgICAgICBzdWJ0aXRsZSA9ICJPYnNlcnZlZCBNb3JhbidzIEkgaW4gUmVkIiwKICAgICAgIHggPSAiTW9yYW4ncyBJIiwKICAgICAgIHkgPSAiQ291bnQiKSArCiAgdGhlbWVfbGlnaHQoKSArICAgCnRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksIAogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTgpLCAKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEwKSkKYGBgCgpUaGUgbWFwIGJlbG93IGlsbHVzdHJhdGVzIHRoZSBjbHVzdGVyaW5nIG9mIHN0YXRpb25zIHdpdGggaGlnaGVyIGJpa2VzaGFyZSBkZW1hbmQuIFNwZWNpZmljYWxseSwgdHdvIG1haW4gY2x1c3RlcnMgZW1lcmdlLCBhbmQgd2l0aGluIGVhY2ggb2YgdGhlc2UgY2x1c3RlcnMsIGEgZmV3IHN0YXRpb25zIGV4cGVyaWVuY2UgZXhjZXB0aW9uYWxseSBoaWdoIG51bWJlcnMgb2YgcmlkZXMuIFRoZXNlIGhpZ2gtZGVtYW5kIHN0YXRpb25zIGFyZSB0eXBpY2FsbHkgc3Vycm91bmRlZCBieSBvdGhlcnMgd2l0aCBzbGlnaHRseSBsb3dlciwgYnV0IHN0aWxsIHNpZ25pZmljYW50LCBsZXZlbHMgb2YgYWN0aXZpdHksIGhpZ2hsaWdodGluZyBhIGNsZWFyIHBhdHRlcm4gb2YgY29uY2VudHJhdGVkIGJpa2VzaGFyZSB1c2FnZS4KCmBgYHtyIHNwYXRpYWwgY29yIHZpeiwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTF9CgogIGdncGxvdCgpICsKICBnZW9tX3NmKGRhdGEgPSBqY1RyYWN0cywgZmlsbCA9ICJ3aGl0ZSIpICsKICBnZW9tX3NmKGRhdGEgPSBtb3Jhbl9kYXRhLAogICAgICAgICAgYWVzKHNpemUgPSBuLCBjb2xvciA9IG4pKSArIAogIHlsaW0obWluKGJpa2VfY2Vuc3VzJHN0YXJ0X2xhdCksIG1heChiaWtlX2NlbnN1cyRzdGFydF9sYXQpKSsKICB4bGltKG1pbihiaWtlX2NlbnN1cyRzdGFydF9sbmcpLCBtYXgoYmlrZV9jZW5zdXMkc3RhcnRfbG5nKSkgKyAKICBzY2FsZV9zaXplKAogICAgcmFuZ2UgPSBjKDEsIDYpLAogICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQoCiAgICAgIGRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwKICAgICAgbnJvdyA9IDEsCiAgICAgIGxhYmVsLnBvc2l0aW9uID0gImJvdHRvbSIpKSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG91cnMgPSBoY2wuY29sb3JzKDUsICJSZEJ1IixyZXYgPSBUUlVFLCBhbHBoYSA9IDAuOSksIAogICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChkaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIG5yb3cgPSAxKSkgKwogIGxhYnModGl0bGUgPSAiU3RhdGlvbnMgd2l0aCBNb3N0IFJpZGVzIiwKICAgICAgIHN1YnRpdGxlID0gICJEb3Qgc2l6ZSBwcm9wb3J0aW9uYWwgdG8gcmlkZXMiKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArIAogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcyA9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIGxpbmV3aWR0aD0wLjgpCiAgICAgICAgKQoKYGBgCgpUaGlzIG1hcCBiZWxvdyBjb21iaW5lcyB0aGUgc3BhdGlhbCBhbmQgdGVtcG9yYWwgY2hhcmFjdGVyaXN0aWNzIG9mIGJpa2UgcmlkZXMgYXQgMTVtaW4gaW50ZXJ2YWwgaW50byBhIGNob3JvcGxldGggbWFwLiAKCmBgYHtyIGFuaW1hdGUgbWFwIHRlbXBvcmFsIHZpeiwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY2FjaGU9VFJVRSwgIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTExfQpiaWtlX2FnZ3JlZ2F0ZWQgPC0gYmlrZV9jZW5zdXMgJT4lCiAgZ3JvdXBfYnkoaW50ZXJ2YWwxNSwgT3JpZ2luLlRyYWN0KSAlPiUKICBzdW1tYXJpemUoVHJpcF9Db3VudCA9IG4oKSwgLmdyb3VwcyA9ICdkcm9wJykKCiMgSm9pbmluZyB3aXRoIHNwYXRpYWwgZGF0YQpjaG9yb3BsZXRoX2RhdGEgPC0gYmlrZV9hZ2dyZWdhdGVkICU+JQogIGxlZnRfam9pbihqY1RyYWN0cywgYnkgPSBjKCJPcmlnaW4uVHJhY3QiID0gIkdFT0lEIikpICU+JQogIHN0X3NmKCkKCiMgQ3JlYXRpbmcgdGhlIGNob3JvcGxldGggbWFwIHdpdGggdGhlIHNhbWUgc2NhbGUKY2hvcm9wbGV0aF9iaWtlX21hcCA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhID0gamNUcmFjdHMsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJncmV5IiwgYWxwaGEgPSAwLjUpICsgIyBCYXNlIG1hcAogIGdlb21fc2YoZGF0YSA9IGNob3JvcGxldGhfZGF0YSwgYWVzKGZpbGwgPSBUcmlwX0NvdW50KSwgY29sb3IgPSBOQSkgKyAjIENob3JvcGxldGggbGF5ZXIKICBzY2FsZV9maWxsX2dyYWRpZW50bigKICAgIGNvbG91cnMgPSBoY2wuY29sb3JzKDUsICJZbE9yUmQiLCByZXYgPSBUUlVFKSwKICAgIG5hbWUgPSAiTnVtYmVyIG9mIFRyaXBzIiwKICAgIG5hLnZhbHVlID0gInRyYW5zcGFyZW50IgogICkgKwogICMgU2V0IHRoZSBzYW1lIHNjYWxlIHVzaW5nIHhsaW0gYW5kIHlsaW0KICB4bGltKG1pbihiaWtlX2NlbnN1cyRzdGFydF9sbmcpLCBtYXgoYmlrZV9jZW5zdXMkc3RhcnRfbG5nKSkgKwogIHlsaW0obWluKGJpa2VfY2Vuc3VzJHN0YXJ0X2xhdCksIG1heChiaWtlX2NlbnN1cyRzdGFydF9sYXQpKSArCiAgbGFicygKICAgIHRpdGxlID0gIkJpa2VzaGFyZSBUcmlwIERlbnNpdHkgYnkgMTUtTWludXRlIEludGVydmFscyIsCiAgICBzdWJ0aXRsZSA9ICJOdW1iZXIgb2YgYmlrZSByaWRlcyBhZ2dyZWdhdGVkIGJ5IG9yaWdpbiB0cmFjdCBhbmQgdGltZSBpbnRlcnZhbCIKICApICsKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LCBmYWNlID0gIml0YWxpYyIpLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGwgPSBOQSwgbGluZXdpZHRoID0gMC44KQogICkKCiMgRGlzcGxheSB0aGUgdXBkYXRlZCBtYXAKcHJpbnQoY2hvcm9wbGV0aF9iaWtlX21hcCkKCmBgYAoKCgojIyBXZWF0aGVyIEFuYWx5c2lzCgpJbiBhZGRpdGlvbiB0byBzcGFjZSBhbmQgdGltZSwgd2VhdGhlciBwbGF5cyBhIGNydWNpYWwgcm9sZSBpbiBpbmZsdWVuY2luZyBiaWtlc2hhcmUgZGVtYW5kLiBXZSBleHBlY3QgdGhhdCBkZW1hbmQgdHlwaWNhbGx5IGRyb3BzIG9uIHJhaW55LCB3aW5keSwgb3IgZXh0cmVtZWx5IGhvdCBkYXlzLiBUbyBpbmNvcnBvcmF0ZSB0aGlzIGZhY3RvciBpbnRvIG91ciBhbmFseXNpcywgd2UgY29sbGVjdGVkIGhvdXJseSB3ZWF0aGVyIGRhdGEgZnJvbSBOZXdhcmsgQWlycG9ydCB1c2luZyB0aGUgcmllbV9tZWFzdXJlcyBmdW5jdGlvbiwgY292ZXJpbmcgdGhlIHBlcmlvZCBmcm9tIEF1Z3VzdCAxIHRvIFNlcHRlbWJlciAxLCAyMDIzLiBTaW5jZSBOZXdhcmsgQWlycG9ydCBpcyBjbG9zZSBlbm91Z2ggdG8gSmVyc2V5IENpdHksIGl0IHNlcnZlcyBhcyBhIHJlbGlhYmxlIHNvdXJjZSBmb3IgbG9jYWwgd2VhdGhlciBjb25kaXRpb25zLiBXZSB0aGVuIGNyZWF0ZWQgYSB3ZWF0aGVyIHBhbmVsIHN1bW1hcml6aW5nIGtleSB2YXJpYWJsZXMgc3VjaCBhcyB0ZW1wZXJhdHVyZSwgcHJlY2lwaXRhdGlvbiwgYW5kIHdpbmQgc3BlZWQgZm9yIGVhY2ggaG91ci4KCmBgYHtyIGRvd25sb2FkIGFuZCB3cmFuZ2xlIHdlYXRoZXIgZGF0YSwgd2FybmluZz1GQUxTRX0Kd2VhdGhlci5QYW5lbCA8LSAKICByaWVtX21lYXN1cmVzKHN0YXRpb24gPSAiRVdSIiwgZGF0ZV9zdGFydCA9ICIyMDIzLTA4LTAxIiwgZGF0ZV9lbmQgPSAiMjAyMy0wOS0wMSIpICU+JQogIGRwbHlyOjpzZWxlY3QodmFsaWQsIHRtcGYsIHAwMWksIHNrbnQpICU+JQogIHJlcGxhY2UoaXMubmEoLiksIDApICU+JQogIG11dGF0ZShpbnRlcnZhbDYwID0geW1kX2goc3Vic3RyKHZhbGlkLCAxLCAxMykpKSAlPiUKICBtdXRhdGUoCiAgICB3ZWVrID0gd2VlayhpbnRlcnZhbDYwKSwKICAgIGRvdHcgPSB3ZGF5KGludGVydmFsNjApICAjIFJlbW92ZSBsYWJlbCA9IFRSVUUKICApICU+JQogIGdyb3VwX2J5KGludGVydmFsNjApICU+JQogIHN1bW1hcml6ZSgKICAgIFRlbXBlcmF0dXJlID0gbWF4KHRtcGYpLAogICAgUHJlY2lwaXRhdGlvbiA9IHN1bShwMDFpKSwKICAgIFdpbmRfU3BlZWQgPSBtYXgoc2tudCkKICApICU+JQogIG11dGF0ZShUZW1wZXJhdHVyZSA9IGlmZWxzZShUZW1wZXJhdHVyZSA9PSAwLCA0MiwgVGVtcGVyYXR1cmUpKQoKYGBgCgpFeGFtaW5pbmcgdGhlIHRlbXBlcmF0dXJlLCBwcmVjaXBpdGF0aW9uLCBhbmQgd2luZCBkYXRhIGNvbGxlY3RlZCBmb3IgQXVndXN0LCB3ZSBvYnNlcnZlIGEgZmV3IG5vdGFibGUgd2VhdGhlciBwYXR0ZXJucy4gVGhlcmUgd2VyZSBzZXZlcmFsIHJhaW55IGRheXMgc2NhdHRlcmVkIHRocm91Z2hvdXQgdGhlIG1vbnRoLCB3aGlsZSB3aW5kIHNwZWVkcyByZW1haW5lZCBnZW5lcmFsbHkgc3RhYmxlLCB3aXRoIG9ubHkgYSBmZXcgaW5zdGFuY2VzIG9mIHBhcnRpY3VsYXJseSBzdHJvbmcgd2luZHMuIFRlbXBlcmF0dXJlIHNob3dlZCBkYWlseSBmbHVjdHVhdGlvbnMsIHdpdGggaGlnaGVyIHJlYWRpbmdzIGR1cmluZyB0aGUgZGF5dGltZSBhbmQgY29vbGVyIHRlbXBlcmF0dXJlcyBhdCBuaWdodCwgcmVmbGVjdGluZyBhIHR5cGljYWwgc3VtbWVyIGN5Y2xlLgoKYGBge3IgdmlzdWFsaXplIHdlYXRoZXIgZGF0YSwgZmlnLmhlaWdodD0xMSwgZmlnLndpZHRoPTgsIHdhcm5pbmc9RkFMU0V9CgpncmlkLmFycmFuZ2UoCiAgZ2dwbG90KHdlYXRoZXIuUGFuZWwsIGFlcyhpbnRlcnZhbDYwLFByZWNpcGl0YXRpb24pKSArIGdlb21fbGluZShjb2xvcj0iIzEyNEU3OCIpICsgCiAgICBsYWJzKHRpdGxlPSJQZXJjaXBpdGF0aW9uIiwgeD0iSG91ciIsIHk9IlBlcmVjaXBpdGF0aW9uIikgKyAKICAgIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksIAogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTgpLCAKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEwKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSksIAogIGdncGxvdCh3ZWF0aGVyLlBhbmVsLCBhZXMoaW50ZXJ2YWw2MCxXaW5kX1NwZWVkKSkgKyBnZW9tX2xpbmUoY29sb3I9IiMxMjRFNzgiKSArIAogICAgbGFicyh0aXRsZT0iV2luZCBTcGVlZCIsIHg9IkhvdXIiLCB5PSJXaW5kIFNwZWVkIikgKwogICAgdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9OCksIAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTApLCAKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSwgIAogIGdncGxvdCh3ZWF0aGVyLlBhbmVsLCBhZXMoaW50ZXJ2YWw2MCxUZW1wZXJhdHVyZSkpICsgZ2VvbV9saW5lKGNvbG9yPSIjMTI0RTc4IikgKyAKICAgIGxhYnModGl0bGU9IlRlbXBlcmF0dXJlIiwgeD0iSG91ciIsIHk9IlRlbXBlcmF0dXJlIikgK3RoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksIAogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTgpLCAKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEwKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSksIAogIHRvcD0iV2VhdGhlciBEYXRhICIpCgpgYGAKCgojIFNwYWNlLVRpbWUgUGFuZWwKCldlIHByb2NlZWRlZCBieSBjb25zdHJ1Y3RpbmcgYSBzdHVkeSBwYW5lbCB0aGF0IGNhcHR1cmVzIHVuaXF1ZSBjb21iaW5hdGlvbnMgb2Ygc3BhY2UgYW5kIHRpbWUuIFRoaXMgbWVhbnMgZWFjaCByb3cgaW4gdGhlIHBhbmVsIGNvcnJlc3BvbmRzIHRvIGEgcmlkZSBvY2N1cnJpbmcgYXQgYSBzcGVjaWZpYyBzdGF0aW9uIGR1cmluZyBhIHNwZWNpZmljIGhvdXIsIGFsbG93aW5nIHVzIHRvIGFuYWx5emUgYmlrZXNoYXJlIGFjdGl2aXR5IGFjcm9zcyBib3RoIHRlbXBvcmFsIGFuZCBzcGF0aWFsIGRpbWVuc2lvbnMuCgpgYGB7ciBjcmVhdGUgc3R1ZHkgcGFuZWwsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiMgQ2hlY2sgaWYgJ2ludGVydmFsNjAnIGV4aXN0cyBhbmQgaXMgbm90IGVudGlyZWx5IE5VTEwKaWYoImludGVydmFsNjAiICVpbiUgbmFtZXMoYmlrZV9jZW5zdXMpICYmICFhbGwoaXMubmEoYmlrZV9jZW5zdXMkaW50ZXJ2YWw2MCkpKSB7CiAgcHJpbnQoIkNvbHVtbiAnaW50ZXJ2YWw2MCcgZXhpc3RzIGFuZCBoYXMgZGF0YS4iKQogIHByaW50KGhlYWQoYmlrZV9jZW5zdXMkaW50ZXJ2YWw2MCkpCn0gZWxzZSB7CiAgcHJpbnQoIkNvbHVtbiAnaW50ZXJ2YWw2MCcgZG9lcyBub3QgZXhpc3Qgb3IgaXMgYWxsIE5BLiIpCn0KCiMgSWYgJ2ludGVydmFsNjAnIG5lZWRzIHRvIGJlIGNyZWF0ZWQgb3IgcmVjYWxjdWxhdGVkOgppZighImludGVydmFsNjAiICVpbiUgbmFtZXMoYmlrZV9jZW5zdXMpIHx8IGFsbChpcy5uYShiaWtlX2NlbnN1cyRpbnRlcnZhbDYwKSkpIHsKICBwcmludCgiUmVjYWxjdWxhdGluZyAnaW50ZXJ2YWw2MCcgZnJvbSBleGlzdGluZyBkYXRldGltZSBjb2x1bW4sIGFzc3VtaW5nICdzdGFydGVkX2F0JyBleGlzdHMuIikKICAKICAjIEFzc3VtaW5nICdzdGFydGVkX2F0JyBpcyB0aGUgY29sdW1uIGZyb20gd2hpY2ggJ2ludGVydmFsNjAnIHNob3VsZCBiZSBkZXJpdmVkOgogIGlmKCJzdGFydGVkX2F0IiAlaW4lIG5hbWVzKGJpa2VfY2Vuc3VzKSkgewogICAgYmlrZV9jZW5zdXMkaW50ZXJ2YWw2MCA8LSBhcy5QT1NJWGN0KGJpa2VfY2Vuc3VzJHN0YXJ0ZWRfYXQsIGZvcm1hdCA9ICIlWS0lbS0lZCAlSDolTTolUyIpICMgQ3VzdG9taXplIGZvcm1hdCBhcyBuZWNlc3NhcnkKICAgIGJpa2VfY2Vuc3VzJGludGVydmFsNjAgPC0gZmxvb3JfZGF0ZShiaWtlX2NlbnN1cyRpbnRlcnZhbDYwLCAiaG91ciIpICMgVXNpbmcgbHVicmlkYXRlIHRvIHJvdW5kIGRvd24gdG8gdGhlIG5lYXJlc3QgaG91cgogICAgcHJpbnQoIk5ldyAnaW50ZXJ2YWw2MCcgY2FsY3VsYXRlZCBhbmQgYWRkZWQgdG8gJ2Jpa2VfY2Vuc3VzJy4iKQogIH0gZWxzZSB7CiAgICBwcmludCgiJ3N0YXJ0ZWRfYXQnIGNvbHVtbiBhbHNvIG1pc3Npbmc7IHBsZWFzZSBjaGVjayBkYXRhIHNvdXJjZS4iKQogIH0KfQoKIyBDaGVjayBhbmQgcmVjcmVhdGUgdGhlIGRhdGEgZnJhbWUgZm9yIGpvaW5pbmcKaWYoImludGVydmFsNjAiICVpbiUgbmFtZXMoYmlrZV9jZW5zdXMpICYmICJzdGFydF9zdGF0aW9uX2lkIiAlaW4lIG5hbWVzKGJpa2VfY2Vuc3VzKSkgewogIGNvbWJpbmVkX2RmIDwtIGV4cGFuZC5ncmlkKGludGVydmFsNjAgPSB1bmlxdWUoYmlrZV9jZW5zdXMkaW50ZXJ2YWw2MCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGFydF9zdGF0aW9uX2lkID0gdW5pcXVlKGJpa2VfY2Vuc3VzJHN0YXJ0X3N0YXRpb25faWQpKQogIHByaW50KCJDb21iaW5lZCBEYXRhRnJhbWUgY3JlYXRlZCBzdWNjZXNzZnVsbHkuIERpc3BsYXlpbmcgc3RydWN0dXJlOiIpCiAgcHJpbnQoc3RyKGNvbWJpbmVkX2RmKSkKfSBlbHNlIHsKICBwcmludCgiT25lIG9yIGJvdGgga2V5IGNvbHVtbnMgYXJlIHN0aWxsIG1pc3Npbmcgb3IgaW52YWxpZC4iKQp9CnN0dWR5LnBhbmVsIDwtIAogIGV4cGFuZC5ncmlkKGludGVydmFsNjA9dW5pcXVlKGJpa2VfY2Vuc3VzJGludGVydmFsNjApLCAKICAgICAgICAgICAgICBzdGFydF9zdGF0aW9uX2lkID0gdW5pcXVlKGJpa2VfY2Vuc3VzJHN0YXJ0X3N0YXRpb25faWQpKSAlPiUKICBsZWZ0X2pvaW4oLiwgYmlrZV9jZW5zdXMgJT4lCiAgICAgICAgICAgICAgc2VsZWN0KHN0YXJ0X3N0YXRpb25faWQsIHN0YXJ0X3N0YXRpb25fbmFtZSwgT3JpZ2luLlRyYWN0LCBzdGFydF9sbmcsIHN0YXJ0X2xhdCklPiUKICAgICAgICAgICAgICBkaXN0aW5jdCgpICU+JQogICAgICAgICAgICAgIGdyb3VwX2J5KHN0YXJ0X3N0YXRpb25faWQpICU+JQogICAgICAgICAgICAgIHNsaWNlKDEpKQpgYGAKCgpXZSB0aGVuIGVuaGFuY2VkIHRoaXMgcGFuZWwgYnkgYWRkaW5nIG1vcmUgZGV0YWlsZWQgaW5mb3JtYXRpb24uIFRoaXMgaW52b2x2ZWQgY291bnRpbmcgdGhlIG51bWJlciBvZiByaWRlcyBhdCBlYWNoIHN0YXRpb24gZm9yIGVhY2ggaG91ciwgaW5jb3Jwb3JhdGluZyB3ZWF0aGVyIGRhdGEsIG1lcmdpbmcgaW4gY2Vuc3VzIGluZm9ybWF0aW9uLCBhbmQgdXNpbmcgdGhlIGx1YnJpZGF0ZSBwYWNrYWdlIHRvIGRlcml2ZSB0aW1lLXJlbGF0ZWQgdmFyaWFibGVzIHN1Y2ggYXMgdGhlIGRheSBvZiB0aGUgd2VlayBhbmQgaG91ciBvZiB0aGUgZGF5LgoKYGBge3IgYWRkIGluZm8gdG8gc3R1ZHkgcGFuZWwsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CgpiaWtlLnBhbmVsIDwtIAogIGJpa2VfY2Vuc3VzICU+JQogIG11dGF0ZShUcmlwX0NvdW50ZXIgPSAxKSAlPiUKICByaWdodF9qb2luKHN0dWR5LnBhbmVsKSAlPiUgCiAgZ3JvdXBfYnkoaW50ZXJ2YWw2MCwgc3RhcnRfc3RhdGlvbl9pZCwgc3RhcnRfc3RhdGlvbl9uYW1lLCBPcmlnaW4uVHJhY3QsIHN0YXJ0X2xuZywgc3RhcnRfbGF0KSAlPiUKICBzdW1tYXJpemUoVHJpcF9Db3VudCA9IHN1bShUcmlwX0NvdW50ZXIsIG5hLnJtID0gVFJVRSksIC5ncm91cHMgPSAnZHJvcCcpICU+JQogIGxlZnRfam9pbih3ZWF0aGVyLlBhbmVsKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZmlsdGVyKCFpcy5uYShzdGFydF9zdGF0aW9uX2lkKSkgJT4lCiAgbXV0YXRlKAogICAgd2VlayA9IHdlZWsoaW50ZXJ2YWw2MCksCiAgICBkb3R3ID0gd2RheShpbnRlcnZhbDYwKSAgIyBSZW1vdmVkIGxhYmVsID0gVFJVRQogICkgJT4lCiAgZmlsdGVyKCFpcy5uYShPcmlnaW4uVHJhY3QpKSAlPiUgCiAgbGVmdF9qb2luKAogICAgbmpDZW5zdXMgJT4lCiAgICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUKICAgICAgc2VsZWN0KC1nZW9tZXRyeSksCiAgICBieSA9IGMoIk9yaWdpbi5UcmFjdCIgPSAiR0VPSUQiKQogICkKCmBgYAoKIyMgV2VhdGhlciBDb3JyZWxhdGlvbgoKV2l0aCBvdXIgcGFuZWwgZGF0YSByZWFkeSwgd2UgY29uZHVjdGVkIGZ1cnRoZXIgZXhwbG9yYXRvcnkgYW5hbHlzaXMgdG8gcHJlcGFyZSBmb3Igb3VyIHJlZ3Jlc3Npb24gbW9kZWxpbmcuIE91ciBmaXJzdCBmb2N1cyB3YXMgb24gdW5kZXJzdGFuZGluZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGVtcGVyYXR1cmUgYW5kIHRoZSBudW1iZXIgb2YgYmlrZSByaWRlcyBpbiBBdWd1c3QuIFRoZSBhbmFseXNpcyByZXZlYWxlZCBhIHN0cm9uZywgcG9zaXRpdmUsIGFuZCBsaW5lYXIgY29ycmVsYXRpb24sIGluZGljYXRpbmcgdGhhdCBiaWtlc2hhcmUgZGVtYW5kIHRlbmRzIHRvIGluY3JlYXNlIGFzIHRlbXBlcmF0dXJlcyByaXNlIHRocm91Z2hvdXQgdGhlIG1vbnRoLgoKCmBgYHtyIHZpc3VhbGl6ZSB0ZW1wLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQoKYmlrZS5wYW5lbCAlPiUKICBncm91cF9ieShpbnRlcnZhbDYwKSAlPiUgCiAgc3VtbWFyaXplKFRyaXBfQ291bnQgPSBtZWFuKFRyaXBfQ291bnQpLAogICAgICAgICAgICBUZW1wZXJhdHVyZSA9IGZpcnN0KFRlbXBlcmF0dXJlKSkgJT4lCiAgbXV0YXRlKHdlZWsgPSB3ZWVrKGludGVydmFsNjApKSAlPiUKICBnZ3Bsb3QoYWVzKFRlbXBlcmF0dXJlLCBUcmlwX0NvdW50KSkgKyAKICAgIGdlb21fcG9pbnQoc2l6ZSA9IC41LCBjb2xvciA9ICJibGFjayIpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9IEZBTFNFLCBjb2xvcj0iZGFya3JlZCIpICsKICAgIGZhY2V0X3dyYXAofndlZWssIG5jb2w9OCkgKyAKICAgIGxhYnModGl0bGU9IlRyaXAgQ291bnQgQXMgYSBGdWN0aW9uIG9mIFRlbXBlcmF0dXJlIGJ5IFdlZWsiLAogICAgICAgICB4PSJUZW1wZXJhdHVyZSIsIHk9Ik1lYW4gVHJpcCBDb3VudCIpICsgCiAgdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTgpLCAKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEwKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkpCiAgCgpgYGAKCk5leHQsIHdlIGV4YW1pbmVkIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHdpbmQgc3BlZWQgYW5kIHRoZSBudW1iZXIgb2YgYmlrZSByaWRlcyBpbiBBdWd1c3QuIFdlIG9ic2VydmVkIGEgZ2VuZXJhbGx5IHBvc2l0aXZlIGxpbmVhciByZWxhdGlvbnNoaXAsIHRob3VnaCB0aGUgc3RyZW5ndGggb2YgdGhpcyBjb3JyZWxhdGlvbiB2YXJpZWQgYWNyb3NzIGRpZmZlcmVudCB3ZWVrcy4gVGhpcyBzdWdnZXN0cyB0aGF0IGJpa2VzaGFyZSBkZW1hbmQgdGVuZHMgdG8gaW5jcmVhc2Ugb24gd2luZHkgZGF5cywgYnV0IHRoZSBpbXBhY3Qgb2Ygd2luZCBzcGVlZCBvbiByaWRlIGFjdGl2aXR5IGlzIG5vdCB1bmlmb3JtIHRocm91Z2hvdXQgdGhlIG1vbnRoLgoKYGBge3IgdmlzdWFsaXplIHdpbmQsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmJpa2UucGFuZWwgJT4lCiAgZ3JvdXBfYnkoaW50ZXJ2YWw2MCkgJT4lIAogIHN1bW1hcml6ZShUcmlwX0NvdW50ID0gbWVhbihUcmlwX0NvdW50KSwKICAgICAgICAgICAgV2luZCA9IGZpcnN0KFdpbmRfU3BlZWQpKSAlPiUKICBtdXRhdGUod2VlayA9IHdlZWsoaW50ZXJ2YWw2MCkpICU+JQogIGdncGxvdChhZXMoV2luZCwgVHJpcF9Db3VudCkpICsgCiAgICBnZW9tX3BvaW50KHNpemUgPSAuNSwgY29sb3IgPSAiYmxhY2siKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPSBGQUxTRSwgY29sb3I9ImRhcmtyZWQiKSArCiAgICBmYWNldF93cmFwKH53ZWVrLCBuY29sPTgpICsgCiAgICBsYWJzKHRpdGxlPSJUcmlwIENvdW50IEFzIGEgRnVjdGlvbiBvZiBXaW5kIGJ5IFdlZWsiLAogICAgICAgICB4PSJXaW5kIFNwZWVkIiwgeT0iTWVhbiBUcmlwIENvdW50IikgKyAKICB0aGVtZShwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9OCksIAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTApLCAKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIHNpemU9MC44KSkgKyAKICAgIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksIAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT04KSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksIAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpKQpgYGAKCgojIyBTZXJpYWwgQ29ycmVsYXRpb24KCklmIHRoZSBudW1iZXIgb2YgYmlrZSB0cmlwcyBzaG93cyBzZXJpYWwgKHRlbXBvcmFsKSBjb3JyZWxhdGlvbiwgaW5jb3Jwb3JhdGluZyB0aW1lIGxhZyBmZWF0dXJlcyBpbnRvIG91ciBtb2RlbCBjb3VsZCBzaWduaWZpY2FudGx5IGVuaGFuY2UgaXRzIHByZWRpY3RpdmUgcG93ZXIuIFRoZSB1bmRlcmx5aW5nIGNvbmNlcHQgaXMgdGhhdCBiaWtlc2hhcmUgZGVtYW5kIGF0IGFueSBnaXZlbiBob3VyIGlzIGxpa2VseSB0byBiZSBpbmZsdWVuY2VkIGJ5IGRlbWFuZCBwYXR0ZXJucyBpbW1lZGlhdGVseSBwcmVjZWRpbmcgb3IgZm9sbG93aW5nIHRoYXQgaG91ci4gRm9yIGV4YW1wbGUsIGlmIHdlIGtub3cgdGhlcmUgd2FzIGEgaGlnaCBudW1iZXIgb2YgdHJpcHMgZHVyaW5nIG9uZSBob3VyLCB3ZSBjYW4gcmVhc29uYWJseSBwcmVkaWN0IHRoYXQgbmVhcmJ5IGhvdXJzIHdpbGwgYWxzbyBoYXZlIGVsZXZhdGVkIGRlbWFuZC4gVG8gY2FwdHVyZSB0aGVzZSB0ZW1wb3JhbCBkZXBlbmRlbmNpZXMsIHdlIGNvbXB1dGVkIHNpeCBkaWZmZXJlbnQgbGFnZ2VkIHZhcmlhYmxlcywgZWFjaCByZXByZXNlbnRpbmcgdGhlIG51bWJlciBvZiB0cmlwcyBvbmUsIHR3bywgdGhyZWUsIGZvdXIsIHR3ZWx2ZSBob3VycyBlYXJsaWVyLCBhbmQgb25lIGRheSBlYXJsaWVyLiBUaGVzZSBsYWcgZmVhdHVyZXMgYWxsb3cgdXMgdG8gYWNjb3VudCBmb3Igc2hvcnQtdGVybSBmbHVjdHVhdGlvbnMgYXMgd2VsbCBhcyBkYWlseSBwYXR0ZXJucywgcHJvdmlkaW5nIGEgbW9yZSBjb21wcmVoZW5zaXZlIGZyYW1ld29yayBmb3IgbW9kZWxpbmcgYmlrZXNoYXJlIGFjdGl2aXR5LiBCeSBsZXZlcmFnaW5nIHRoZXNlIHRpbWUtYmFzZWQgY29ycmVsYXRpb25zLCBvdXIgbW9kZWwgY2FuIGJldHRlciBhbnRpY2lwYXRlIHNoaWZ0cyBpbiBkZW1hbmQsIGltcHJvdmluZyBhY2N1cmFjeSBhbmQgcmVsaWFiaWxpdHkuCgoKYGBge3IgY2FsY3VsYXRlIHNlcmlhbCBsYWd9CgpiaWtlLnBhbmVsIDwtIAogIGJpa2UucGFuZWwgJT4lIAogIGFycmFuZ2Uoc3RhcnRfc3RhdGlvbl9pZCwgaW50ZXJ2YWw2MCkgJT4lIAogIG11dGF0ZShsYWdIb3VyID0gZHBseXI6OmxhZyhUcmlwX0NvdW50LDEpLAogICAgICAgICBsYWcySG91cnMgPSBkcGx5cjo6bGFnKFRyaXBfQ291bnQsMiksCiAgICAgICAgIGxhZzNIb3VycyA9IGRwbHlyOjpsYWcoVHJpcF9Db3VudCwzKSwKICAgICAgICAgbGFnNEhvdXJzID0gZHBseXI6OmxhZyhUcmlwX0NvdW50LDQpLAogICAgICAgICBsYWcxMkhvdXJzID0gZHBseXI6OmxhZyhUcmlwX0NvdW50LDEyKSwKICAgICAgICAgbGFnMWRheSA9IGRwbHlyOjpsYWcoVHJpcF9Db3VudCwyNCkpICU+JQogICBtdXRhdGUoZGF5ID0geWRheShpbnRlcnZhbDYwKSkKCmBgYAoKV2UgdmlzdWFsaXplZCB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRoZSBudW1iZXIgb2YgdHJpcHMgYW5kIHRoZSB2YXJpb3VzIGxhZ2dlZCBob3VycyB0byBiZXR0ZXIgdW5kZXJzdGFuZCB0aGVzZSB0ZW1wb3JhbCBjb3JyZWxhdGlvbnMuIFRoZSBhbmFseXNpcyByZXZlYWxlZCBzb21lIGludGVyZXN0aW5nIHBhdHRlcm5zLiBUaGVyZSBpcyBhIHN0cm9uZywgbGluZWFyLCBhbmQgbmVnYXRpdmUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIG51bWJlciBvZiB0cmlwcyBhbmQgdGhlIGxhZ2dlZCB2YXJpYWJsZSByZXByZXNlbnRpbmcgMTIgaG91cnMgcHJpb3IuIEZvciBpbnN0YW5jZSwgdGhlIG51bWJlciBvZiByaWRlcyByZWNvcmRlZCBhdCA1IFBNIG9uIGEgZ2l2ZW4gZGF5IHRlbmRzIHRvIGJlIG5lZ2F0aXZlbHkgY29ycmVsYXRlZCB3aXRoIHRoZSBudW1iZXIgb2YgcmlkZXMgYXQgNSBBTSBvbiB0aGUgc2FtZSBkYXksIGxpa2VseSByZWZsZWN0aW5nIGNvbnRyYXN0aW5nIHBlYWsgYW5kIG9mZi1wZWFrIHVzYWdlIHRpbWVzLgoKQ29udmVyc2VseSwgd2Ugb2JzZXJ2ZWQgYSBzdHJvbmcsIHBvc2l0aXZlLCBhbmQgbGluZWFyIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIG51bWJlciBvZiB0cmlwcyBhdCBhIGdpdmVuIGhvdXIgYW5kIGJvdGggdGhlIGltbWVkaWF0ZSBuZXh0IGhvdXIgKGxhZ2hvdXIpIGFuZCB0aGUgc2FtZSBob3VyIG9uIHRoZSBmb2xsb3dpbmcgZGF5IChsYWcxZGF5KS4gVGhpcyBpbmRpY2F0ZXMgdGhhdCBiaWtlc2hhcmUgZGVtYW5kIGlzIGhpZ2hseSBwcmVkaWN0YWJsZSBmcm9tIG9uZSBob3VyIHRvIHRoZSBuZXh0IGFuZCB0ZW5kcyB0byBmb2xsb3cgY29uc2lzdGVudCBkYWlseSBwYXR0ZXJucy4gSG93ZXZlciwgdGhlIHN0cmVuZ3RoIG9mIHRoZSBjb3JyZWxhdGlvbiBkZWNyZWFzZXMgZm9yIHRoZSBsYWcgdmFyaWFibGVzIHJlcHJlc2VudGluZyB0d28gYW5kIHRocmVlIGhvdXJzIHByaW9yLiBGb3IgZXhhbXBsZSwgdGhlIG51bWJlciBvZiByaWRlcyBhdCA0IFBNIGlzIG1vcmUgc3Ryb25nbHkgY29ycmVsYXRlZCB3aXRoIHRoZSBudW1iZXIgb2YgcmlkZXMgYXQgNSBQTSB0aGFuIHdpdGggcmlkZXMgYXQgNiBvciA3IFBNLiBUaGlzIGdyYWR1YWwgd2Vha2VuaW5nIG9mIGNvcnJlbGF0aW9uIG92ZXIgdGltZSBoaWdobGlnaHRzIGhvdyBiaWtlc2hhcmUgZGVtYW5kIGlzIG1vc3QgaGVhdmlseSBpbmZsdWVuY2VkIGJ5IHJlY2VudCBwYXN0IGFjdGl2aXR5LCBtYWtpbmcgdGhlc2UgdGltZSBsYWcgZmVhdHVyZXMgdmFsdWFibGUgZm9yIG1vZGVsaW5nIHB1cnBvc2VzLgoKYGBge3IgdmlzdWFsaXplIHNlcmlhbCBjb3JyZWxhdGlvbnMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cgphcy5kYXRhLmZyYW1lKGJpa2UucGFuZWwpICU+JQogICAgZ3JvdXBfYnkoaW50ZXJ2YWw2MCkgJT4lIAogICAgc3VtbWFyaXNlX2F0KHZhcnMoc3RhcnRzX3dpdGgoImxhZyIpLCAiVHJpcF9Db3VudCIpLCBtZWFuLCBuYS5ybSA9IFRSVUUpICU+JQogICAgZ2F0aGVyKFZhcmlhYmxlLCBWYWx1ZSwgLWludGVydmFsNjAsIC1UcmlwX0NvdW50KSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBWYWx1ZSAsIHkgPSBUcmlwX0NvdW50KSkgKwogICAgZ2VvbV9wb2ludChzaXplID0gLjUsIGNvbG9yID0gImJsYWNrIikgKyBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT0gRkFMU0UsIGNvbG9yPSJkYXJrcmVkIikgKwogIGZhY2V0X3dyYXAoflZhcmlhYmxlLCBuY29sID0gNikrCiAgbGFicyh4ID0gIlZhcmlhYmxlIiwgeSA9ICJDb3JyZWxhdGlvbiIsIHRpdGxlID0gIkNvcnJlbGF0aW9uIEJldHdlZW4gU2VyaWFsIExhZyBUcmlwcyBhbmQgVHJpcCBDb3VudCIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArIAogIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksIAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT04KSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksIAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpKQoKYGBgCgojIyBTcGF0aWFsIENvcnJlbGF0aW9uCgpXZSBwcmV2aW91c2x5IGVzdGFibGlzaGVkIHRoYXQgc3BhdGlhbCBhdXRvY29ycmVsYXRpb24gaXMgcHJlc2VudCBpbiBvdXIgZGF0YSwgaW5kaWNhdGluZyB0aGF0IHRoZSBudW1iZXIgb2YgdHJpcHMgYXQgb25lIHN0YXRpb24gZHVyaW5nIGEgc3BlY2lmaWMgdGltZSBpcyByZWxhdGVkIHRvIHRoZSBudW1iZXIgb2YgdHJpcHMgYXQgbmVhcmJ5IHN0YXRpb25zLiBJbiBvdGhlciB3b3Jkcywga25vd2luZyB0aGUgZGVtYW5kIGF0IG9uZSBzdGF0aW9uIGNhbiBoZWxwIHVzIGVzdGltYXRlIGRlbWFuZCBhdCBuZWlnaGJvcmluZyBzdGF0aW9ucy4gVG8gbGV2ZXJhZ2UgdGhpcyBzcGF0aWFsIHJlbGF0aW9uc2hpcCwgd2UgY3JlYXRlZCBhIG5ldyBkYXRhZnJhbWUgY2FsbGVkIGxhZ19yZXN1bHQuCgpXZSB0aGVuIGltcGxlbWVudGVkIGEgbmVzdGVkIGxvb3AgdG8gc3lzdGVtYXRpY2FsbHkgY2FsY3VsYXRlIHNwYXRpYWwgbGFncyBmb3IgZWFjaCBob3VyIGluIEF1Z3VzdCAyMDIzLiBGb3IgZWFjaCBob3VyLCB3ZSBpZGVudGlmaWVkIHRoZSBmaXZlIG5lYXJlc3QgbmVpZ2hib3Jpbmcgc3RhdGlvbnMgZm9yIGV2ZXJ5IHN0YXRpb24gYW5kIGNvbXB1dGVkIHRoZSBhdmVyYWdlIG51bWJlciBvZiB0cmlwcyBhY3Jvc3MgdGhlc2UgbmVpZ2hib3JzLiBUaGlzIHByb3ZpZGVkIGEgbWVhc3VyZSBvZiBzcGF0aWFsIGluZmx1ZW5jZSwgY2FwdHVyaW5nIGhvdyBkZW1hbmQgYXQgb25lIHN0YXRpb24gaXMgYWZmZWN0ZWQgYnkgYWN0aXZpdHkgYXQgc3Vycm91bmRpbmcgc3RhdGlvbnMuIEZpbmFsbHksIHdlIGludGVncmF0ZWQgdGhlc2Ugc3BhdGlhbCBsYWcgdmFsdWVzIGJhY2sgaW50byBvdXIgcGFuZWwgZGF0YXNldCwgZW5oYW5jaW5nIG91ciBtb2RlbCdzIGFiaWxpdHkgdG8gcHJlZGljdCBiaWtlc2hhcmUgZGVtYW5kIGJ5IGluY29ycG9yYXRpbmcgYm90aCBzcGF0aWFsIGFuZCB0ZW1wb3JhbCBkZXBlbmRlbmNpZXMuCgoKCgpgYGB7ciBjYWxjdWxhdGUgc3BhdGlhbCBsYWcsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoc2YpCmxpYnJhcnkoc3BkZXApCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocGFyYWxsZWwpCgojIFN0ZXAgMTogRmlsdGVyIGRhdGEgb25jZSBhbmQgY29udmVydCB0byBzZiBvYmplY3QKYmlrZS5wYW5lbF9zZiA8LSBiaWtlLnBhbmVsICU+JQogIHN0X2FzX3NmKGNvb3JkcyA9IGMoInN0YXJ0X2xuZyIsICJzdGFydF9sYXQiKSwgY3JzID0gNDMyNikKCiMgU3RlcCAyOiBEZWZpbmUgYSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgc3BhdGlhbCBsYWcgZm9yIGEgZ2l2ZW4gZGF5IGFuZCBob3VyCmNhbGN1bGF0ZV9zcGF0aWFsX2xhZyA8LSBmdW5jdGlvbihkLCBoLCBiaWtlX2RhdGEpIHsKICAjIExvYWQgcmVxdWlyZWQgbGlicmFyaWVzIHdpdGhpbiB0aGUgZnVuY3Rpb24KICBsaWJyYXJ5KGRwbHlyKQogIGxpYnJhcnkoc2YpCiAgbGlicmFyeShzcGRlcCkKICBsaWJyYXJ5KGx1YnJpZGF0ZSkKICAKICAjIEZpbHRlciBkYXRhIGZvciB0aGUgc3BlY2lmaWVkIGRheSBhbmQgaG91cgogIHNwYWNlbGFnIDwtIGJpa2VfZGF0YSAlPiUKICAgIG11dGF0ZShob3VyID0gaG91cihpbnRlcnZhbDYwKSwgZGF5ID0geWRheShpbnRlcnZhbDYwKSkgJT4lCiAgICBmaWx0ZXIoZGF5ID09IGQgJiBob3VyID09IGgpCiAgCiAgaWYgKG5yb3coc3BhY2VsYWcpID09IDApIHJldHVybihOVUxMKSAgIyBTa2lwIGlmIG5vIGRhdGEKICAKICAjIENvbXB1dGUgc3BhdGlhbCBuZWlnaGJvcnMgYW5kIHdlaWdodHMKICBjb29yZHMgPC0gc3RfY29vcmRpbmF0ZXMoc3BhY2VsYWcpCiAgbmVpZ2hib3JMaXN0IDwtIGtubjJuYihrbmVhcm5laWdoKGNvb3JkcywgayA9IDUpKQogIHNwYXRpYWxXZWlnaHRzIDwtIG5iMmxpc3R3KG5laWdoYm9yTGlzdCwgc3R5bGUgPSAiVyIpCiAgCiAgIyBDYWxjdWxhdGUgc3BhdGlhbCBsYWcKICBzcGFjZWxhZyA8LSBzcGFjZWxhZyAlPiUKICAgIG11dGF0ZShsYWdUcmlwID0gbGFnLmxpc3R3KHNwYXRpYWxXZWlnaHRzLCBUcmlwX0NvdW50KSkKICAKICByZXR1cm4oc3BhY2VsYWcpCn0KCiMgU3RlcCAzOiBVc2UgcGFyYWxsZWwgcHJvY2Vzc2luZyB0byBzcGVlZCB1cCB0aGUgbG9vcApjbCA8LSBtYWtlQ2x1c3RlcihkZXRlY3RDb3JlcygpIC0gMSkgICMgQ3JlYXRlIGEgY2x1c3RlciB1c2luZyBhdmFpbGFibGUgY29yZXMKY2x1c3RlckV4cG9ydChjbCwgYygiYmlrZS5wYW5lbF9zZiIsICJjYWxjdWxhdGVfc3BhdGlhbF9sYWciKSkgICMgRXhwb3J0IGRhdGEgYW5kIGZ1bmN0aW9uCmNsdXN0ZXJFdmFsUShjbCwgeyAgIyBMb2FkIHJlcXVpcmVkIGxpYnJhcmllcyBpbiBlYWNoIHdvcmtlcgogIGxpYnJhcnkoZHBseXIpCiAgbGlicmFyeShzZikKICBsaWJyYXJ5KHNwZGVwKQogIGxpYnJhcnkobHVicmlkYXRlKQp9KQoKIyBSdW4gdGhlIGxvb3AgaW4gcGFyYWxsZWwKbGFnX3Jlc3VsdF9saXN0IDwtIHBhckxhcHBseShjbCwgMjEzOjI0MywgZnVuY3Rpb24oZCkgewogIGxhcHBseSgwOjIzLCBmdW5jdGlvbihoKSBjYWxjdWxhdGVfc3BhdGlhbF9sYWcoZCwgaCwgYmlrZS5wYW5lbF9zZikpCn0pCgpzdG9wQ2x1c3RlcihjbCkgICMgU3RvcCB0aGUgY2x1c3RlcgoKIyBDb21iaW5lIHRoZSByZXN1bHRzIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZQpsYWdfcmVzdWx0IDwtIGRvLmNhbGwocmJpbmQsIHVubGlzdChsYWdfcmVzdWx0X2xpc3QsIHJlY3Vyc2l2ZSA9IEZBTFNFKSkgJT4lCiAgbXV0YXRlKHN0YXJ0X2xuZyA9IHVubGlzdChtYXAoZ2VvbWV0cnksIDEpKSwKICAgICAgICAgc3RhcnRfbGF0ID0gdW5saXN0KG1hcChnZW9tZXRyeSwgMikpKSAlPiUKICBzdF9kcm9wX2dlb21ldHJ5KCkKCiMgRmluYWwgcmVzdWx0CmJpa2UucGFuZWwgPC0gbGFnX3Jlc3VsdAoKICAKYGBgCgpUaGUgZmlndXJlIGJlbG93IGlsbHVzdHJhdGVzIGEgY29uc2lzdGVudCwgc3Ryb25nLCBhbmQgcG9zaXRpdmUgbGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBudW1iZXIgb2YgdHJpcHMgYXQgYSBzdGF0aW9uIGFuZCB0aGUgbnVtYmVyIG9mIHRyaXBzIGF0IG5lYXJieSBzdGF0aW9ucyB0aHJvdWdob3V0IGFsbCB3ZWVrcyBpbiBBdWd1c3QuIFRoaXMgY29uZmlybXMgdGhlIHByZXNlbmNlIG9mIHNpZ25pZmljYW50IHNwYXRpYWwgZGVwZW5kZW5jZSwgc3VnZ2VzdGluZyB0aGF0IHN0YXRpb25zIGluIGNsb3NlIHByb3hpbWl0eSB0ZW5kIHRvIGV4cGVyaWVuY2Ugc2ltaWxhciBsZXZlbHMgb2YgZGVtYW5kLiBHaXZlbiB0aGlzIGZpbmRpbmcsIHdlIHdpbGwgaW5jb3Jwb3JhdGUgdGhpcyBzcGF0aWFsIGxhZyB2YXJpYWJsZSBpbnRvIG91ciByZWdyZXNzaW9uIGFuYWx5c2lzIHRvIGltcHJvdmUgdGhlIG1vZGVsJ3MgcHJlZGljdGl2ZSBhY2N1cmFjeSBieSBhY2NvdW50aW5nIGZvciB0aGUgaW5mbHVlbmNlIG9mIG5lYXJieSBzdGF0aW9uIGFjdGl2aXR5LgoKCgpgYGB7ciB2aXN1YWxpemUgc3BhdGlhbCBjb3JyZWxhdGlvbiwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCmJpa2UucGFuZWwgJT4lCiAgZ3JvdXBfYnkoaW50ZXJ2YWw2MCkgJT4lIAogIHN1bW1hcml6ZShUcmlwX0NvdW50ID0gbWVhbihUcmlwX0NvdW50KSwKICAgICAgICAgICAgTGFnX0NvdW50ID0gbWVhbihsYWdUcmlwKSkgJT4lCiAgbXV0YXRlKHdlZWsgPSB3ZWVrKGludGVydmFsNjApKSAlPiUKICBnZ3Bsb3QoYWVzKExhZ19Db3VudCwgVHJpcF9Db3VudCkpICsgCiAgICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICIjNkUwRTBBIikgKyBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT0gRkFMU0UsIGNvbG9yPSIjMTI0RTc4IikgKwogICAgZmFjZXRfd3JhcCh+d2VlaywgbmNvbD04KSArIAogICAgbGFicyh0aXRsZT0iQ29ycmVsYXRpb24gQmV0d2VlbiBTcGF0aWFsIExhZyBUcmlwcyBhbmQgVHJpcCBDb3VudCIsCiAgICAgICAgIHg9IkxhZyBUcmlwIENvdW50IiwgeT0iTWVhbiBUcmlwIENvdW50IikgKyAKICB0aGVtZShwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9OCksIAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTApLCAKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIHNpemU9MC44KSkKCmBgYAoKCiMgT0xTIFJlZ3Jlc3Npb24KCldlIGRpdmlkZWQgb3VyIGZpdmUgd2Vla3Mgb2YgZGF0YSBpbnRvIGEgdHJhaW5pbmcgc2V0IGNvbnNpc3Rpbmcgb2YgdGhyZWUgd2Vla3MgYW5kIGEgdGVzdGluZyBzZXQgY29tcHJpc2luZyB0aGUgcmVtYWluaW5nIHR3byB3ZWVrcy4gVXNpbmcgdGhlIHRyYWluaW5nIGRhdGEsIHdlIGVzdGltYXRlZCBmb3VyIGRpc3RpbmN0IGxpbmVhciByZWdyZXNzaW9uIG1vZGVscywgZWFjaCBpbmNvcnBvcmF0aW5nIGRpZmZlcmVudCBmaXhlZCBlZmZlY3RzOgoKLSByZWcxIGNhcHR1cmVzIG9ubHkgdGVtcG9yYWwgZmFjdG9ycywgaW5jbHVkaW5nIGhvdXItb2YtZGF5IGZpeGVkIGVmZmVjdHMsIGRheSBvZiB0aGUgd2Vlaywgd2luZCBzcGVlZCwgYW5kIHRlbXBlcmF0dXJlLgotIHJlZzIgZW1waGFzaXplcyBzcGF0aWFsIGNvbXBvbmVudHMsIHVzaW5nIHN0YXRpb24tc3BlY2lmaWMgZWZmZWN0cyBhbmQgd2VhdGhlciBjb25kaXRpb25zLgotIHJlZzMgY29tYmluZXMgYm90aCBzcGF0aWFsIGFuZCB0ZW1wb3JhbCBlbGVtZW50cyB0byBhY2NvdW50IGZvciBpbnRlcmFjdGlvbnMgYmV0d2VlbiBzcGFjZSBhbmQgdGltZS4KLSByZWc0IGV4dGVuZHMgdGhlIHRoaXJkIG1vZGVsIGJ5IGFkZGluZyBib3RoIHRpbWUgYW5kIHNwYXRpYWwgbGFnIGZlYXR1cmVzLCBjYXB0dXJpbmcgdGVtcG9yYWwgYW5kIHNwYXRpYWwgZGVwZW5kZW5jaWVzIHRvIGVuaGFuY2UgcHJlZGljdGl2ZSBhY2N1cmFjeS4KCgoKCmBgYHtyIGZpdHRpbmcgcmVncmVzc2lvbn0KCmJpa2UuVHJhaW4gPC0gZmlsdGVyKGJpa2UucGFuZWwsIHdlZWsgPD0gMzMpCmJpa2UuVGVzdCA8LSBmaWx0ZXIoYmlrZS5wYW5lbCwgd2VlayA+IDMzKQoKIyBUaW1lCnJlZzEgPC0gCiAgbG0oVHJpcF9Db3VudCB+ICBob3VyKGludGVydmFsNjApICsgZG90dyArIFRlbXBlcmF0dXJlICsgV2luZF9TcGVlZCwgZGF0YT1iaWtlLlRyYWluKQoKIyBTcGFjZQpyZWcyIDwtIAogIGxtKFRyaXBfQ291bnQgfiAgc3RhcnRfc3RhdGlvbl9uYW1lICsgVGVtcGVyYXR1cmUgKyBXaW5kX1NwZWVkLCAgZGF0YT1iaWtlLlRyYWluKQoKIyBUaW1lIGFuZCBTcGFjZQpyZWczIDwtIAogIGxtKFRyaXBfQ291bnQgfiAgc3RhcnRfc3RhdGlvbl9uYW1lICsgaG91cihpbnRlcnZhbDYwKSArIGRvdHcgKyBUZW1wZXJhdHVyZSArIFdpbmRfU3BlZWQsIAogICAgIGRhdGE9YmlrZS5UcmFpbikKCiMgVGltZSBhbmQgU3BhY2UgYW5kIFRpbWVMYWcgYW5kIFNwYWNlTGFnCnJlZzQgPC0gCiAgbG0oVHJpcF9Db3VudCB+ICBzdGFydF9zdGF0aW9uX25hbWUgKyAgaG91cihpbnRlcnZhbDYwKSArIGRvdHcgKyBUZW1wZXJhdHVyZSArIFdpbmRfU3BlZWQgKwogICAgICAgICAgICAgICAgICAgbGFnSG91ciArIGxhZzJIb3VycyArIGxhZzEySG91cnMgKyBsYWcxZGF5ICsgbGFnVHJpcCwgCiAgICAgZGF0YT1iaWtlLlRyYWluKQpgYGAKCiMjIE1ha2luZyBQcmVkaWN0aW9ucwoKV2UgdXRpbGl6ZWQgdGhlIHB1cnJyIHBhY2thZ2UgdG8gZWZmaWNpZW50bHkgaXRlcmF0ZSBvdmVyIGEgc2V0IG9mIG5lc3RlZCBkYXRhIGZyYW1lcywgYWxsb3dpbmcgdXMgdG8gY29tcGFyZSBkaWZmZXJlbnQgbW9kZWwgc3BlY2lmaWNhdGlvbnMuIFRvIHByZXBhcmUgZm9yIHRlc3RpbmcsIHdlIG9yZ2FuaXplZCBvdXIgZGF0YSBieSB3ZWVrLCBuZXN0aW5nIGl0IGludG8gc2VwYXJhdGUgdGliYmxlcy4gV2UgdGhlbiBjcmVhdGVkIGEgc2ltcGxlIHByZWRpY3Rpb24gZnVuY3Rpb24gdGhhdCB0YWtlcyBhIHRpYmJsZSAoZGF0KSBhbmQgYSByZWdyZXNzaW9uIG1vZGVsIChmaXQpIGFzIGlucHV0cyBhbmQgZ2VuZXJhdGVzIHByZWRpY3Rpb25zLCByZXR1cm5pbmcgdGhlbSBhcyBwcmVkLiBUaGlzIGFwcHJvYWNoIHN0cmVhbWxpbmVzIHRoZSBwcm9jZXNzIG9mIGFwcGx5aW5nIG91ciBtb2RlbHMgYWNyb3NzIG11bHRpcGxlIHN1YnNldHMgb2YgZGF0YSwgbWFraW5nIGl0IGVhc3kgdG8gZXZhbHVhdGUgYW5kIGNvbXBhcmUgdGhlaXIgcGVyZm9ybWFuY2UuCgpgYGB7ciBuZXN0IGRhdGEgYW5kIHByZWRpY3QgZnVuY3Rpb24sIHdhcm5pbmc9RkFMU0V9CgpiaWtlLlRlc3Qud2Vla05lc3QgPC0gCiAgYmlrZS5UZXN0ICU+JQogIG5lc3QoLXdlZWspIAoKbW9kZWxfcHJlZCA8LSBmdW5jdGlvbihkYXQsIGZpdCl7CiAgIHByZWQgPC0gcHJlZGljdChmaXQsIG5ld2RhdGEgPSBkYXQpfQoKYGBgCgoKVGhlIG5lc3RlZCBkYXRhIGZvcm1hdCBlbmFibGVzIHVzIHRvIGl0ZXJhdGUgdGhyb3VnaCBlYWNoIG1vZGVsIGZvciBldmVyeSB3ZWVrIGFuZCBjb21wdXRlIHN1bW1hcnkgc3RhdGlzdGljcyBlZmZpY2llbnRseS4gSW4gdGhlIGNvZGUgYmxvY2sgdGhhdCBmb2xsb3dzLCB3ZSBnZW5lcmF0ZSB3ZWVrX3ByZWRpY3Rpb25zIGZvciBlYWNoIHdlZWsgd2l0aGluIHRoZSB0ZXN0aW5nIHNldC4gVXNpbmcgdGhlIG1hcCBmdW5jdGlvbiBmcm9tIHB1cnJyLCB3ZSBhcHBseSB0aGUgbW9kZWxfcHJlZCBmdW5jdGlvbiB0byBlYWNoIG5lc3RlZCB0aWJibGUuIEZvciBpbnN0YW5jZSwgaW4gdGhlIGZpcnN0IGxpbmUgb2YgdGhlIG11dGF0ZSBvcGVyYXRpb24sIGEgbmV3IGNvbHVtbiBjYWxsZWQgVGltZV9GRSBpcyBjcmVhdGVkLCB3aGljaCBjb250YWlucyBwcmVkaWN0aW9ucyBmcm9tIHJlZzEsIHRoZSBtb2RlbCB0aGF0IGluY2x1ZGVzIHRpbWUgZml4ZWQgZWZmZWN0cy4gVGhlc2UgcHJlZGljdGlvbnMgYXJlIHByb2R1Y2VkIGJ5IG1hcHBpbmcgbW9kZWxfcHJlZCB0byBlYWNoIGRhdGEgc3Vic2V0LCB3aXRoIGZpdCBzZXQgdG8gdGhlIHJlZzEgbW9kZWwuIFRoaXMgbWV0aG9kIGFsbG93cyB1cyB0byBlYXNpbHkgYXBwbHkgbXVsdGlwbGUgbW9kZWxzIGFuZCBnYXRoZXIgcHJlZGljdGlvbiByZXN1bHRzIGZvciBjb21wYXJpc29uLgoKYGBge3IgbWFraW5nIHByZWRpY3Rpb25zLCB3YXJuaW5nPUZBTFNFfQoKd2Vla19wcmVkaWN0aW9ucyA8LSAKICBiaWtlLlRlc3Qud2Vla05lc3QgJT4lIAogICAgbXV0YXRlKEFUaW1lX0ZFID0gbWFwKC54ID0gZGF0YSwgZml0ID0gcmVnMSwgLmYgPSBtb2RlbF9wcmVkKSwKICAgICAgICAgICBCU3BhY2VfRkUgPSBtYXAoLnggPSBkYXRhLCBmaXQgPSByZWcyLCAuZiA9IG1vZGVsX3ByZWQpLAogICAgICAgICAgIENUaW1lX1NwYWNlX0ZFID0gbWFwKC54ID0gZGF0YSwgZml0ID0gcmVnMywgLmYgPSBtb2RlbF9wcmVkKSwKICAgICAgICAgICBEVGltZV9TcGFjZV9GRV9MYWdzID0gbWFwKC54ID0gZGF0YSwgZml0ID0gcmVnNCwgLmYgPSBtb2RlbF9wcmVkKSkgJT4lIAogICAgZ2F0aGVyKFJlZ3Jlc3Npb24sIFByZWRpY3Rpb24sIC1kYXRhLCAtd2VlaykgJT4lCiAgICBtdXRhdGUoT2JzZXJ2ZWQgPSBtYXAoZGF0YSwgcHVsbCwgVHJpcF9Db3VudCksCiAgICAgICAgICAgQWJzb2x1dGVfRXJyb3IgPSBtYXAyKE9ic2VydmVkLCBQcmVkaWN0aW9uLCAgfiBhYnMoLnggLSAueSkpLAogICAgICAgICAgIE1BRSA9IG1hcF9kYmwoQWJzb2x1dGVfRXJyb3IsIG1lYW4sIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgc2RfQUUgPSBtYXBfZGJsKEFic29sdXRlX0Vycm9yLCBzZCwgbmEucm0gPSBUUlVFKSkKCmBgYAoKIyMgRXJyb3IgQW5hbHlzaXMKCldoZW4gY29tcGFyaW5nIHRoZSBtZWFuIGFic29sdXRlIGVycm9ycyAoTUFFKSBhY3Jvc3Mgb3VyIGZvdXIgbW9kZWxzLCB3ZSBvYnNlcnZlIHRoYXQgTW9kZWwgMSwgd2hpY2ggb25seSBpbmNvcnBvcmF0ZXMgdGltZSBlZmZlY3RzLCBoYXMgdGhlIGhpZ2hlc3QgZXJyb3IgcmF0ZS4gTW9kZWxzIDIgYW5kIDMsIHdoaWNoIGFjY291bnQgZm9yIHNwYXRpYWwgZWZmZWN0cyBhbmQgdGhlIGNvbWJpbmF0aW9uIG9mIHNwYWNlIGFuZCB0aW1lLCBzaG93IGltcHJvdmVkIGFjY3VyYWN5LiBIb3dldmVyLCB0aGUgZ3JlYXRlc3QgZW5oYW5jZW1lbnQgaW4gcHJlZGljdGl2ZSBwZXJmb3JtYW5jZSBjb21lcyBmcm9tIE1vZGVsIDQsIHdoaWNoIGluY2x1ZGVzIGJvdGggc3BhY2UgYW5kIHRpbWUgbGFnIGZlYXR1cmVzLCBzaWduaWZpY2FudGx5IHJlZHVjaW5nIHRoZSBlcnJvci4gVG8gZW5zdXJlIHRoZSByZWxpYWJpbGl0eSBvZiBNb2RlbCA0LCB3ZSBhbHNvIHBlcmZvcm1lZCBhIHZhcmlhbmNlIGluZmxhdGlvbiBmYWN0b3IgKFZJRikgYW5hbHlzaXMgdG8gY29uZmlybSB0aGF0IG11bHRpY29sbGluZWFyaXR5IHdhcyBub3QgYW4gaXNzdWUgYW1vbmcgdGhlIHByZWRpY3RvcnMuCgpgYGB7ciBwbG90IGVycm9yc30KCndlZWtfcHJlZGljdGlvbnMgJT4lCiAgZHBseXI6OnNlbGVjdCh3ZWVrLCBSZWdyZXNzaW9uLCBNQUUpICU+JQogIGdhdGhlcihWYXJpYWJsZSwgTUFFLCAtUmVncmVzc2lvbiwgLXdlZWspICU+JQogIGdncGxvdChhZXMod2VlaywgTUFFKSkgKyAKICAgIGdlb21fYmFyKGFlcyhmaWxsID0gUmVncmVzc2lvbiksIHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNCkgKwogICAgbGFicyh0aXRsZSA9ICJNZWFuIEFic29sdXRlIEVycm9ycyBieSBNb2RlbCBTcGVjaWZpY2F0aW9uIGFuZCBXZWVrIikgKwogIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksIAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT04KSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksIAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpKQoKYGBgCgpUaGUgZmlndXJlIGJlbG93IHByb3ZpZGVzIGEgdmlzdWFsIGNvbXBhcmlzb24gb2YgdGhlIGFjY3VyYWN5IG9mIGVhY2ggbW9kZWwgYnkgcGxvdHRpbmcgdGhlIHByZWRpY3RlZCB2ZXJzdXMgb2JzZXJ2ZWQgbnVtYmVyIG9mIHRyaXBzLiBBbW9uZyB0aGUgZm91ciBtb2RlbHMsIE1vZGVsIDQgY2xlYXJseSBkZW1vbnN0cmF0ZXMgc3VwZXJpb3IgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZSwgY2xvc2VseSBhbGlnbmluZyB3aXRoIHRoZSBhY3R1YWwgYmlrZXNoYXJlIGRlbWFuZC4gVGhpcyBoaWdobGlnaHRzIHRoZSBlZmZlY3RpdmVuZXNzIG9mIGluY29ycG9yYXRpbmcgYm90aCBzcGF0aWFsIGFuZCB0ZW1wb3JhbCBsYWcgZmVhdHVyZXMsIG1ha2luZyBNb2RlbCA0IHRoZSBtb3N0IHJvYnVzdCBhbmQgcmVsaWFibGUgaW4gZm9yZWNhc3RpbmcgdHJpcCBjb3VudHMuCgoKCgpgYGB7ciBjb21wYXJpbmcgcHJlZGljdGVkIGFuZCBvYnNlcnZlZCwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTEsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNhY2hlPVRSVUV9Cgp3ZWVrX3ByZWRpY3Rpb25zICU+JSAKICAgIG11dGF0ZShpbnRlcnZhbDYwID0gbWFwKGRhdGEsIHB1bGwsIGludGVydmFsNjApLAogICAgICAgICAgIHN0YXJ0X3N0YXRpb25faWQgPSBtYXAoZGF0YSwgcHVsbCwgc3RhcnRfc3RhdGlvbl9pZCkpICU+JQogICAgZHBseXI6OnNlbGVjdChpbnRlcnZhbDYwLCBzdGFydF9zdGF0aW9uX2lkLCBPYnNlcnZlZCwgUHJlZGljdGlvbiwgUmVncmVzc2lvbikgJT4lCiAgICB1bm5lc3QoKSAlPiUKICAgIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1SZWdyZXNzaW9uLCAtaW50ZXJ2YWw2MCwgLXN0YXJ0X3N0YXRpb25faWQpICU+JQogICAgZ3JvdXBfYnkoUmVncmVzc2lvbiwgVmFyaWFibGUsIGludGVydmFsNjApICU+JQogICAgc3VtbWFyaXplKFZhbHVlID0gc3VtKFZhbHVlKSkgJT4lCiAgICBnZ3Bsb3QoYWVzKGludGVydmFsNjAsIFZhbHVlLCBjb2xvdXI9VmFyaWFibGUpKSArIAogICAgICBnZW9tX2xpbmUoc2l6ZSA9IDEuMSkgKyAKICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1wYWxldHRlMikrCiAgICAgIGZhY2V0X3dyYXAoflJlZ3Jlc3Npb24sIG5jb2w9MSkgKwogICAgICBsYWJzKHRpdGxlID0gIlByZWRpY3RlZC9PYnNlcnZlZCBCaWtlIFNoYXJlIFRpbWUgU2VyaWVzIiwgc3VidGl0bGUgPSAiSmVyc2V5IENpdHk6IGEgdGVzdCBzZXQgb2YgMiB3ZWVrcyIsICB4ID0gIkhvdXIiLCB5PSAiU3RhdGlvbiBUcmlwcyIpICsgCiAgIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksIAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT04KSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksIAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpKQoKYGBgCgpUbyBldmFsdWF0ZSB3aGV0aGVyIG91ciBwcmVkaWN0aW9ucyBnZW5lcmFsaXplIHdlbGwgYWNyb3NzIGRpZmZlcmVudCBsb2NhdGlvbnMgYW5kIHRpbWVzLCB3ZSBtYXBwZWQgdGhlIG1lYW4gYWJzb2x1dGUgZXJyb3IgKE1BRSkgb2YgTW9kZWwgNCBhY3Jvc3MgdmFyaW91cyBzdGF0aW9ucy4gVGhlIHJlc3VsdHMgc2hvdyB0aGF0IHRoZSBtb2RlbCBzdHJ1Z2dsZXMgdG8gYWNjdXJhdGVseSBwcmVkaWN0IHRoZSBudW1iZXIgb2YgcmlkZXMgYXQgc3RhdGlvbnMgbG9jYXRlZCBhbG9uZyB0aGUgSHVkc29uIFJpdmVyLCB3aGljaCB0ZW5kIHRvIGV4cGVyaWVuY2UgaGlnaCBiaWtlc2hhcmUgZGVtYW5kIG9uIGJvdGggd2Vla2RheXMgYW5kIHdlZWtlbmRzLiBIb3dldmVyLCBmb3Igc3RhdGlvbnMgd2l0aCBsb3dlciByaWRlcnNoaXAgdGhyb3VnaG91dCBBdWd1c3QsIHRoZSBtb2RlbCBwZXJmb3JtcyBzaWduaWZpY2FudGx5IGJldHRlciwgZGVtb25zdHJhdGluZyByZWFzb25hYmxlIHByZWRpY3RpdmUgYWNjdXJhY3kgaW4gdGhvc2UgYXJlYXMuCgoKCgoKYGBge3IgZXJyb3IgYnkgc3RhdGlvbiwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgY2FjaGU9VFJVRX0KCndlZWtfcHJlZGljdGlvbnMgJT4lIAogICAgbXV0YXRlKGludGVydmFsNjAgPSBtYXAoZGF0YSwgcHVsbCwgaW50ZXJ2YWw2MCksCiAgICAgICAgICAgc3RhcnRfc3RhdGlvbl9pZCA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9zdGF0aW9uX2lkKSwgCiAgICAgICAgICAgc3RhcnRfbGF0aXR1ZGUgPSBtYXAoZGF0YSwgcHVsbCwgc3RhcnRfbGF0KSwgCiAgICAgICAgICAgc3RhcnRfbG9uZ2l0dWRlID0gbWFwKGRhdGEsIHB1bGwsIHN0YXJ0X2xuZykpICU+JQogICAgc2VsZWN0KGludGVydmFsNjAsIHN0YXJ0X3N0YXRpb25faWQsIHN0YXJ0X2xvbmdpdHVkZSwgc3RhcnRfbGF0aXR1ZGUsIE9ic2VydmVkLCBQcmVkaWN0aW9uLCBSZWdyZXNzaW9uKSAlPiUKICAgIHVubmVzdCgpICU+JQogIGZpbHRlcihSZWdyZXNzaW9uID09ICJEVGltZV9TcGFjZV9GRV9MYWdzIikgJT4lCiAgZ3JvdXBfYnkoc3RhcnRfc3RhdGlvbl9pZCwgc3RhcnRfbG9uZ2l0dWRlLCBzdGFydF9sYXRpdHVkZSkgJT4lCiAgc3VtbWFyaXplKE1BRSA9IG1lYW4oYWJzKE9ic2VydmVkLVByZWRpY3Rpb24pLCBuYS5ybSA9IFRSVUUpKSU+JQpnZ3Bsb3QoLikrCiAgZ2VvbV9zZihkYXRhID0gamNUcmFjdHMsICBmaWxsID0gIndoaXRlIikrCiAgZ2VvbV9wb2ludChhZXMoeCA9IHN0YXJ0X2xvbmdpdHVkZSwgeSA9IHN0YXJ0X2xhdGl0dWRlLCBjb2xvciA9IE1BRSksIAogICAgICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIpKwogIHNjYWxlX2NvbG9yX2NvbnRpbnVvdXMobG93ID0gIiMxMjRFNzgiLCBoaWdoID0gIiM2RTBFMEEiLCBuYW1lPSAiTUFFIikrCiB5bGltKG1pbihiaWtlX2NlbnN1cyRzdGFydF9sYXQpLCBtYXgoYmlrZV9jZW5zdXMkc3RhcnRfbGF0KSkrCiAgeGxpbShtaW4oYmlrZV9jZW5zdXMkc3RhcnRfbG5nKSwgbWF4KGJpa2VfY2Vuc3VzJHN0YXJ0X2xuZykpICsKICBsYWJzKHRpdGxlPSJNZWFuIEFicyBFcnJvciwgVGVzdCBTZXQsIE1vZGVsIDQiKSArIAogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcyA9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIGxpbmV3aWR0aD0wLjgpCiAgICAgICAgKQoKYGBgCgpXaGVuIGV4YW1pbmluZyBNb2RlbCA0J3MgcGVyZm9ybWFuY2UgYWNyb3NzIGJvdGggc3BhY2UgYW5kIHRpbWUsIHRoZSBmaWd1cmUgZGVtb25zdHJhdGVzIHRoYXQgdGhlIG1vZGVsIG1haW50YWlucyBjb25zaXN0ZW50IHByZWRpY3Rpb25zIHRocm91Z2hvdXQgZGlmZmVyZW50IHBlcmlvZHMuIFN0YXRpb25zIGlkZW50aWZpZWQgYXMgaGF2aW5nIGhpZ2ggZGVtYW5kIG9uIHdlZWtlbmRzIGFyZSBzaW1pbGFybHkgcHJlZGljdGVkIHRvIGhhdmUgaGlnaCBkZW1hbmQgZHVyaW5nIHdlZWtkYXlzLiBIb3dldmVyLCB0aGUgZXJyb3JzIHBlcnNpc3Qgb3ZlciB0aW1lLCBpbmRpY2F0aW5nIHRoYXQgc3RhdGlvbnMgd2l0aCBoaXN0b3JpY2FsbHkgaGlnaCBkZW1hbmQgYWxzbyBleGhpYml0IGdyZWF0ZXIgdmFyaWFiaWxpdHkgaW4gcmlkZSBjb3VudHMsIG1ha2luZyB0aGVtIG1vcmUgY2hhbGxlbmdpbmcgdG8gcHJlZGljdCB3aXRoIG91ciBjdXJyZW50IHNldCBvZiBmZWF0dXJlcy4gSW4gZmFjdCwgb3VyIG1vZGVsIG9ubHkgZXhwbGFpbnMgNTAuMTIlIG9mIHRoZSB2YXJpYWJpbGl0eSwgd2hpY2ggaXMgYSBsaW1pdGF0aW9uLiBUaGUgY29uY2VybiBpcyB0aGF0IGlmIHRoZXNlIGxhcmdlciBlcnJvcnMgcmVtYWluIHVuYWRkcmVzc2VkIGZvciBoaWdoLWRlbWFuZCBzdGF0aW9ucywgd2UgcmlzayBmYWlsaW5nIHRvIGFkZXF1YXRlbHkgbWVldCBiaWtlc2hhcmUgdXNlcnMnIG5lZWRzIGF0IHRoZXNlIGNydWNpYWwgbG9jYXRpb25zLgoKCgoKCmBgYHtyIGNvbXBhcmluZyBlcnJvcnMgYWNyb3NzIHNwYWNlIGFuZCB0aW1lLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBjYWNoZT1UUlVFfQoKd2Vla19wcmVkaWN0aW9ucyAlPiUgCiAgICBtdXRhdGUoaW50ZXJ2YWw2MCA9IG1hcChkYXRhLCBwdWxsLCBpbnRlcnZhbDYwKSwKICAgICAgICAgICBzdGFydF9zdGF0aW9uX2lkID0gbWFwKGRhdGEsIHB1bGwsIHN0YXJ0X3N0YXRpb25faWQpLCAKICAgICAgICAgICBzdGFydF9sYXRpdHVkZSA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9sYXQpLCAKICAgICAgICAgICBzdGFydF9sb25naXR1ZGUgPSBtYXAoZGF0YSwgcHVsbCwgc3RhcnRfbG5nKSwKICAgICAgICAgICBkb3R3ID0gbWFwKGRhdGEsIHB1bGwsIGRvdHcpICkgJT4lCiAgICBzZWxlY3QoaW50ZXJ2YWw2MCwgc3RhcnRfc3RhdGlvbl9pZCwgc3RhcnRfbG9uZ2l0dWRlLCAKICAgICAgICAgICBzdGFydF9sYXRpdHVkZSwgT2JzZXJ2ZWQsIFByZWRpY3Rpb24sIFJlZ3Jlc3Npb24sCiAgICAgICAgICAgZG90dykgJT4lCiAgICB1bm5lc3QoKSAlPiUKICBmaWx0ZXIoUmVncmVzc2lvbiA9PSAiRFRpbWVfU3BhY2VfRkVfTGFncyIpJT4lCiAgbXV0YXRlKHdlZWtlbmQgPSBpZmVsc2UoZG90dyAlaW4lIGMoIlN1biIsICJTYXQiKSwgIldlZWtlbmQiLCAiV2Vla2RheSIpLAogICAgICAgICB0aW1lX29mX2RheSA9IGNhc2Vfd2hlbihob3VyKGludGVydmFsNjApIDwgNyB8IGhvdXIoaW50ZXJ2YWw2MCkgPiAxOCB+ICJPdmVybmlnaHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3VyKGludGVydmFsNjApID49IDcgJiBob3VyKGludGVydmFsNjApIDwgMTAgfiAiQU0gUnVzaCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIoaW50ZXJ2YWw2MCkgPj0gMTAgJiBob3VyKGludGVydmFsNjApIDwgMTUgfiAiTWlkLURheSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIoaW50ZXJ2YWw2MCkgPj0gMTUgJiBob3VyKGludGVydmFsNjApIDw9IDE4IH4gIlBNIFJ1c2giKSkgJT4lCiAgZ3JvdXBfYnkoc3RhcnRfc3RhdGlvbl9pZCwgd2Vla2VuZCwgdGltZV9vZl9kYXksIHN0YXJ0X2xvbmdpdHVkZSwgc3RhcnRfbGF0aXR1ZGUpICU+JQogIHN1bW1hcml6ZShNQUUgPSBtZWFuKGFicyhPYnNlcnZlZC1QcmVkaWN0aW9uKSwgbmEucm0gPSBUUlVFKSklPiUKICBnZ3Bsb3QoLikrCiAgZ2VvbV9zZihkYXRhID0gamNUcmFjdHMsIGNvbG9yID0gImdyZXkiLCBmaWxsID0gInRyYW5zcGFyZW50IikrCiAgZ2VvbV9wb2ludChhZXMoeCA9IHN0YXJ0X2xvbmdpdHVkZSwgeSA9IHN0YXJ0X2xhdGl0dWRlLCBjb2xvciA9IE1BRSksIAogICAgICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIHNpemUgPSAwLjUpKwogIHNjYWxlX2NvbG9yX2NvbnRpbnVvdXMobG93ID0gIiMxMjRFNzgiLCBoaWdoID0gIiM2RTBFMEEiLCBuYW1lPSAiTUFFIikrCiB5bGltKG1pbihiaWtlX2NlbnN1cyRzdGFydF9sYXQpLCBtYXgoYmlrZV9jZW5zdXMkc3RhcnRfbGF0KSkrCiAgeGxpbShtaW4oYmlrZV9jZW5zdXMkc3RhcnRfbG5nKSwgbWF4KGJpa2VfY2Vuc3VzJHN0YXJ0X2xuZykpICsKICBmYWNldF9ncmlkKHdlZWtlbmR+dGltZV9vZl9kYXkpKwogIGxhYnModGl0bGU9Ik1lYW4gQWJzb2x1dGUgRXJyb3JzIGJ5IFRpbWUsIFRlc3QgU2V0LCBNb2RlbCA0IikgKyAKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MgPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBsaW5ld2lkdGg9MC44KQogICAgICAgICkKYGBgCgoKIyMgQ3Jvc3MtVmFsaWRhdGlvbgoKTGFzdGx5LCB3ZSBjb25kdWN0ZWQgMTAwIGNyb3NzLXZhbGlkYXRpb24gdGVzdHMgdXNpbmcgTW9kZWwgNCBvbiBkYXRhIHNwYW5uaW5nIGFsbCBmaXZlIHdlZWtzLiBUaGUgbWVhbiBhYnNvbHV0ZSBlcnJvciAoTUFFKSB3YXMgMC40NCwgaW5kaWNhdGluZyB0aGF0IHdoaWxlIHRoaXMgbW9kZWwgaXMgb3VyIG1vc3QgZWZmZWN0aXZlIHNvbHV0aW9uIHNvIGZhciwgdGhlcmUgaXMgc3RpbGwgYSBub3RhYmxlIGxldmVsIG9mIHByZWRpY3Rpb24gZXJyb3IuIFRvIGltcHJvdmUgdGhlIG1vZGVsIGZ1cnRoZXIsIGl0IHdvdWxkIGJlIHZhbHVhYmxlIHRvIGNyb3NzLXZhbGlkYXRlIGFnYWluc3Qgc29jaW8tZWNvbm9taWMgaW5kaWNhdG9ycy4gVGhpcyBhcHByb2FjaCBjb3VsZCByZXZlYWwgd2hldGhlciBiaWtlIGRlbWFuZCBhdCBzdGF0aW9ucyBpbiBzcGVjaWZpYyBuZWlnaGJvcmhvb2RzIGlzIGJlaW5nIGRpc3Byb3BvcnRpb25hdGVseSB1bmRlci0gb3Igb3Zlci1wcmVkaWN0ZWQuIEhvd2V2ZXIsIG91ciBjdXJyZW50IG1vZGVsIGRvZXMgbm90IGluY29ycG9yYXRlIHNvY2lvZWNvbm9taWMgdmFyaWFibGVzIGFzIHByZWRpY3RvcnMsIGFuZCB0aGUgZHluYW1pY3MgaW4gSmVyc2V5IENpdHnigJRzdWNoIGFzIHRoZSBjb250aW51ZWQgZGV2ZWxvcG1lbnQgb2YgbmV3IHN0YXRpb25z4oCUbWF5IGFsc28gaW1wYWN0IGRlbWFuZCBwYXR0ZXJucy4KCmBgYHtyIGNyb3NzIHZhbGlkYXRpb259CgpmaXRDb250cm9sIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAxMDApCgpyZWcuY3YgPC0gdHJhaW4oVHJpcF9Db3VudCB+IHN0YXJ0X3N0YXRpb25fbmFtZSArICBob3VyKGludGVydmFsNjApICsgZG90dyArIFRlbXBlcmF0dXJlICsgV2luZF9TcGVlZCArIGxhZ0hvdXIgKyBsYWcySG91cnMgKyBsYWcxMkhvdXJzICsgbGFnMWRheSArIGxhZ1RyaXAsIGRhdGE9YmlrZS5wYW5lbCwgbWV0aG9kID0gImxtIiwgdHJDb250cm9sID0gZml0Q29udHJvbCwgbmEuYWN0aW9uID0gbmEucGFzcykKCnJlZy5jdiRyZXNhbXBsZSAlPiUgCiAgc3VtbWFyaXNlKE1BRSA9IG1lYW4ocmVnLmN2JHJlc2FtcGxlWywzXSksCiAgICAgICAgICAgIHNkKHJlZy5jdiRyZXNhbXBsZVssM10pCikgJT4lCiAga2JsKGNvbC5uYW1lPWMoJ01lYW4gQWJzb2x1dGUgRXJyb3InLCdTdGFuZGFyZCBEZXZpYXRpb24gb2YgTUFFJykpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpKQpgYGAKCgojIENvbmNsdXNpb24KCkluIHN1bW1hcnksIHRoaXMgcmVwb3J0IGhpZ2hsaWdodHMgdGhlIHVyZ2VudCBuZWVkIGZvciBlZmZpY2llbnQgYmlrZXNoYXJlIG1hbmFnZW1lbnQgc3RyYXRlZ2llcyBpbiBKZXJzZXkgQ2l0eSBhbmQgcHJlc2VudHMgYSBtYWNoaW5lIGxlYXJuaW5nIGFwcHJvYWNoIHRvIGFkZHJlc3MgdGhpcyBjaGFsbGVuZ2UuIE91ciBhbmFseXNpcyBkZW1vbnN0cmF0ZXMgdGhhdCBiaWtlc2hhcmUgZGVtYW5kIGlzIGluZmx1ZW5jZWQgYnkgYSBjb21iaW5hdGlvbiBvZiBzcGF0aWFsLCB0ZW1wb3JhbCwgYW5kIHdlYXRoZXItcmVsYXRlZCBmYWN0b3JzLiBTcGVjaWZpY2FsbHksIHRoZSBtb2RlbCBzaG93cyB0aGF0IGZvcmVjYXN0aW5nIGRlbWFuZCBpcyBtb3JlIGFjY3VyYXRlIHdoZW4gY29uc2lkZXJpbmcgdGhlIG51bWJlciBvZiB0cmlwcyBpbiB0aGUgcHJlY2VkaW5nIGFuZCBmb2xsb3dpbmcgaG91cnMsIHRoZSBzYW1lIGhvdXIgb24gdGhlIHByZXZpb3VzIGRheSwgYW5kIG5lYXJieSBzdGF0aW9uIGFjdGl2aXR5LiBXZSBhbHNvIGRpc2NvdmVyZWQgdGhhdCBkZW1hbmQgcGVha3MgZHVyaW5nIHdpbmR5IGFuZCBob3QgZGF5cyBpbiBBdWd1c3QsIHdpdGggdGhlIGhpZ2hlc3QgdXNhZ2Ugb2NjdXJyaW5nIGR1cmluZyB3ZWVrZGF5IFBNIHJ1c2ggaG91cnMgYW5kLCB0byBhIGxlc3NlciBleHRlbnQsIG92ZXJuaWdodC4gV2Vla2VuZHMgc2VlIGEgc2lnbmlmaWNhbnQgc3Bpa2UgaW4gZGVtYW5kIGR1cmluZyB0aGUgUE0gcnVzaCBhcyB3ZWxsLiBOb3RhYmx5LCBDaXRpIEJpa2UgaXMgaGVhdmlseSB1c2VkIGZvciBjb21tdXRpbmcgdG8gTmV3IFlvcmsgQ2l0eSwgbWFraW5nIHN0YXRpb25zIGFsb25nIHRoZSBIdWRzb24gUml2ZXIsIGVzcGVjaWFsbHkgbmVhciBmZXJyeSB0ZXJtaW5hbHMsIHRoZSBtb3N0IGFjdGl2ZS4KCldlIGZ1cnRoZXIgZXhwbG9yZWQgc3BlY2lmaWMgaGlnaC1kZW1hbmQgc3RhdGlvbnMgaW4gZGV0YWlsLiBHcm92ZSBTdCBQQVRIIHN0YXRpb24gcmVjb3JkZWQgYW4gaW1wcmVzc2l2ZSAxLDExNyByaWRlcyBkdXJpbmcgdGhlIHdlZWtkYXkgUE0gcnVzaCBhbmQgODAxIHJpZGVzIG92ZXJuaWdodCBpbiBBdWd1c3QsIHJlZmxlY3RpbmcgaXRzIHN0cmF0ZWdpYyBsb2NhdGlvbiBpbiBkb3dudG93biBKZXJzZXkgQ2l0eS4gSG9ib2tlbiBUZXJtaW5hbCAtIFJpdmVyIFN0ICYgSHVkc29uIFBsIGhhZCA4NjQgcmlkZXMgZHVyaW5nIHdlZWtkYXkgUE0gcnVzaCBob3VycywgbGlrZWx5IGR1ZSB0byBpdHMgcHJveGltaXR5IHRvIG1ham9yIHRyYW5zaXQgY29ubmVjdGlvbnMuIFNvdXRoIFdhdGVyZnJvbnQgV2Fsa3dheSAtIFNpbmF0cmEgRHIgJiAxIFN0IHN0YXRpb24gc2F3IDMxOSByaWRlcyBvdmVybmlnaHQgb24gYSB3ZWVrZW5kIGFuZCA3MzEgZHVyaW5nIHRoZSB3ZWVrZGF5IFBNIHJ1c2gsIGJlbmVmaXRpbmcgZnJvbSBpdHMgbG9jYXRpb24gbmVhciB0aGUgTkogVHJhbnNpdCBUZXJtaW5hbCBhbmQgdGhlIHBvcHVsYXIgUGllciBBIFBhcmsuIFRoZXNlIGZpbmRpbmdzIHN1Z2dlc3QgdGhhdCBzdGF0aW9ucyBleHBlcmllbmNlIGhpZ2hlciBkZW1hbmQgYmVjYXVzZSB0aGV5IHNlcnZlIGtleSBhcmVhcyBzdWNoIGFzIGVtcGxveW1lbnQgaHVicywgdHJhbnNpdCBub2RlcyBmb3IgY29tbXV0aW5nLCBhbmQgcmVjcmVhdGlvbmFsIHNwYWNlcyBhbG9uZyB0aGUgd2F0ZXJmcm9udC4gVG8gYWRkcmVzcyB0aGlzLCBsb2NhbCBhdXRob3JpdGllcyBzaG91bGQgY29uc2lkZXIgcmViYWxhbmNpbmcgYmlrZSBzdXBwbGllcyBtb3JlIHN0cmF0ZWdpY2FsbHkgYXQgdGhlc2UgY3JpdGljYWwgbG9jYXRpb25zLgoKQWx0aG91Z2ggb3VyIG1vZGVsIHN1Y2Nlc3NmdWxseSBjYXB0dXJlcyBvdmVyIGhhbGYgb2YgdGhlIGRlbWFuZCB2YXJpYXRpb25zLCBpdCBzdGlsbCByZXF1aXJlcyBmdXJ0aGVyIHJlZmluZW1lbnQgdG8gaW5jcmVhc2UgaXRzIHByZWRpY3RpdmUgYWNjdXJhY3kuIFdpdGhvdXQgdGhlc2UgaW1wcm92ZW1lbnRzLCB0aGUgbW9kZWwgcmlza3MgZmFpbGluZyB0byBtZWV0IHJpZGVyIG5lZWRzIGFuZCBjb21wbGljYXRpbmcgYmlrZSByZWRpc3RyaWJ1dGlvbiBlZmZvcnRzLiBOZXZlcnRoZWxlc3MsIHRoZSBzdHJhaWdodGZvcndhcmQgYW5kIG9wZW4tc291cmNlIG5hdHVyZSBvZiBvdXIgYXBwcm9hY2ggbWFrZXMgaXQgYSB2YWx1YWJsZSB0b29sIGZvciBiaWtlc2hhcmUgb3BlcmF0b3JzIGxvb2tpbmcgdG8gaW1wcm92ZSBzZXJ2aWNlIHF1YWxpdHkgYW5kIG9wdGltaXplIGJpa2UgYXZhaWxhYmlsaXR5LiBBcyB1cmJhbiB0cmFuc3BvcnRhdGlvbiBjb250aW51ZXMgdG8gZXZvbHZlLCB0aGUgdXNlIG9mIHByZWRpY3RpdmUgbW9kZWxzIGxpa2Ugb3VycyB3aWxsIGJlIHZpdGFsIGZvciBjcmVhdGluZyBzdXN0YWluYWJsZSwgd2VsbC1iYWxhbmNlZCBtb2JpbGl0eSBzeXN0ZW1zLgo=