Introduction

Bike-share is a bike-sharing system that allows people to borrow and ride bikes without owning a bike. It’s comprised of a network of bike-sharing stations with docks for bikes to be checked in, stored and checked out.

Stations in different geographical locations face different demands that vary with time. One paradox is that popular stations usually have higher demand for bikes and will sooner run out of bikes if not replenished properly. For the bike-share companies, this deficiency can lead to increasing dissatisfaction and churning rate among users, thereby lowering their profits. For bike-share users, this can seriously cut down their utility to use bike-sharing service and cause inefficiency and inconvenience for their work and life. For the city/region, bike-share services would be useless in raising active travel if they can’t provide enough supply where and when needed. With some of the users turning to car travel again, the congestion and environmental issues will only be worsen in the city/region. Given all these perspectives, bike-share re-balancing, which means re-distributing bikes with certain strategies so that the shortage of supply in a station can be narrowed as much as possible, is much needed as a critical element to make the bike-share system a success.

The strategy for re-balancing here will be relying on trucks to collect and move bikes to certain station. This is because the bike stations in Philadelphia are quite dispersed, therefore a relatively high incentive should be provided if we want to purely rely on users to re-balance the bikes, adding to be a nonnegligible cost with possibly very limited effects. Compared with this method, small trucks may be more competent in guaranteeing the outcomes. At any given time, it would be ideal if we can predict the demand in the next 1 hour, which should be sufficient for the trucks to relocate bikes to hot-spot areas.

Data collection and feature engineering

I use the bike-sharing data in Philadelphia from 10/29/2018 to 12/02/2018 here to conduct a 5-week panel experiment in predicting demands. Features that are included are weather (temperature, precipitation and wind speed) as shown in Figure 2.1, amenity elements (closeness to school, shops, parks, tourism sites, cuisine places, offices and bus, trolly, transit stations), some other spatial features (median income, median age, percent of white population, mean commute time and percent of taking public transportation, number of commuters), and time lag features (Table 1). A fishnet is also created for spatial inputs.

weather.Panel <- 
  riem_measures(station = "PHL", date_start = "2018-10-29", date_end = "2018-12-02") %>%
  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, label=TRUE)) %>%
    group_by(interval60) %>%
    summarize(Temperature = max(tmpf),
              Precipitation = sum(p01i),
              Wind_Speed = max(sknt)) %>%
    mutate(Temperature = ifelse(Temperature == 0, 42, Temperature))
library(ggplot2)
library(gridExtra)
grid.arrange(
  ggplot(weather.Panel, aes(interval60,Precipitation)) + geom_line() + 
  labs(title="Percipitation", x="Hour", y="Perecipitation") + plotTheme,
  ggplot(weather.Panel, aes(interval60,Wind_Speed)) + geom_line() + 
    labs(title="Wind Speed", x="Hour", y="Wind Speed") + plotTheme,
  ggplot(weather.Panel, aes(interval60,Temperature)) + geom_line() + 
    labs(title="Temperature", x="Hour", y="Temperature") + plotTheme,
  top="Figure 2.1Weather Data - Philadelphia PHL - Nov, 2018")

library(sf)
library(dplyr)
dat_net <- 
  dplyr::select(dat_sf%>%
  st_transform(st_crs(fishnet))) %>% 
  mutate(countRIDE = 1) %>% 
  aggregate(., fishnet, sum) %>%
  mutate(countRIDE = replace_na(countRIDE, 0),
         uniqueID = rownames(.),
         cvID = sample(round(nrow(fishnet) / 24), size=nrow(fishnet), replace = TRUE)) %>%
  st_join(., neighborhoods) %>%
  mutate(nbname=ifelse(!is.na(name),name,"unknown"))

final_net <-
  left_join(dat_net, st_drop_geometry(vars_net), by="uniqueID") 

final_net_weather <- left_join(final_net, st_drop_geometry(vars_net), by="uniqueID") 

##Prepare for moran's
library(spdep)
final_net.nb <- poly2nb(as_Spatial(final_net), queen=TRUE)
final_net.weights <- nb2listw(final_net.nb, style="W", zero.policy=TRUE)


final_net.localMorans <- 
  cbind(
    as.data.frame(localmoran(final_net$countRIDE, final_net.weights)),
    as.data.frame(final_net)) %>% 
    st_sf() %>%
      dplyr::select(RIDE_Count = countRIDE, 
                    Local_Morans_I = Ii, 
                    P_Value = `Pr(z > 0)`) %>%
      mutate(Significant_Hotspots = ifelse(P_Value <= 0.0000001, 1, 0)) %>%
      gather(Variable, Value, -geometry)

##distance to hotspots## 
final_net <-
  final_net %>% 
  mutate(RIDE.isSig = 
           ifelse(localmoran(final_net$countRIDE, 
                             final_net.weights)[,5] <= 0.05, 1, 0)) %>%
  mutate(RIDE.isSig.dist = 
           nn_function(st_coordinates(st_centroid(final_net)),
                       st_coordinates(st_centroid(
                         filter(final_net, RIDE.isSig == 1))), 1))
dat_census <- st_join(dat_ymd %>% 
          filter(is.na(start_lon) == FALSE &
                   is.na(start_lat) == FALSE &
                   is.na(end_lat) == FALSE &
                   is.na(end_lon) == FALSE) %>%
          st_as_sf(., coords = c("start_lon", "start_lat"), crs = 4326),
        PhillyTracts %>%
          st_transform(crs=4326),
        join=st_intersects,
              left = TRUE) %>%
  rename(Origin.Tract = GEOID) %>%
  mutate(start_lon = unlist(map(geometry, 1)),
         start_lat = unlist(map(geometry, 2)))%>%
  as.data.frame() %>%
  dplyr::select(-geometry)%>%
  st_as_sf(., coords = c("end_lon", "end_lat"), crs = 4326) %>%
  st_join(., PhillyTracts %>%
            st_transform(crs=4326),
          join=st_intersects,
          left = TRUE) %>%
  rename(Destination.Tract = GEOID)  %>%
  mutate(end_lon = unlist(map(geometry, 1)),
         end_lat = unlist(map(geometry, 2)))%>%
  as.data.frame() %>%
  dplyr::select(-geometry)
study.panel <- 
  expand.grid(interval60=unique(dat_census$interval60), 
              start_station = unique(dat_census$start_station)) %>%
  left_join(., dat_census %>%
              dplyr::select(start_station, Origin.Tract, start_lon, start_lat )%>% #add station name
              distinct() %>%
              group_by(start_station) %>%
              slice(1))


study.panel <- 
  dat_census %>%
  mutate(Trip_Counter = 1) %>%
  right_join(study.panel) %>% 
  group_by(interval60, start_station, Origin.Tract, start_lon, start_lat) %>% #add station name
  summarize(Trip_Count = sum(Trip_Counter, na.rm=T)) %>%
  left_join(weather.Panel) %>%
  ungroup() %>%
  filter(is.na(start_station) == FALSE) %>%
  mutate(week = week(interval60),
         dotw = wday(interval60, label = TRUE)) %>%
  filter(is.na(Origin.Tract) == FALSE)
study.panel <- 
  study.panel %>% 
  arrange(start_station, 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),
         holiday = ifelse(yday(interval60) == c(315,326),1,0)) %>% ##??
   mutate(day = yday(interval60)) %>%
   mutate(holidayLag = case_when(dplyr::lag(holiday, 1) == 1 ~ "PlusOneDay",
                                 dplyr::lag(holiday, 2) == 1 ~ "PlustTwoDays",
                                 dplyr::lag(holiday, 3) == 1 ~ "PlustThreeDays",
                                 dplyr::lead(holiday, 1) == 1 ~ "MinusOneDay",
                                 dplyr::lead(holiday, 2) == 1 ~ "MinusTwoDays",
                                 dplyr::lead(holiday, 3) == 1 ~ "MinusThreeDays"),
         holidayLag = replace_na(holidayLag, 0))
as.data.frame(study.panel) %>%
    group_by(interval60) %>% 
    summarise_at(vars(starts_with("lag"), "Trip_Count"), mean, na.rm = TRUE) %>%
    gather(Variable, Value, -interval60, -Trip_Count) %>%
    mutate(Variable = factor(Variable, levels=c("lagHour","lag2Hours","lag3Hours","lag4Hours",
                                                "lag12Hours","lag1day")))%>%
    group_by(Variable) %>%  
    summarize(correlation = round(cor(Value, Trip_Count),2)) %>%
    kable (caption="Table 1: Time lag features and corelation", col.names = c('Time lag', 'Corelation')) %>%
    kable_styling()
Table 1: Time lag features and corelation
Time lag Corelation
lagHour 0.82
lag2Hours 0.59
lag3Hours 0.39
lag4Hours 0.23
lag12Hours -0.22
lag1day 0.72

Explorary analysis

More frequent re-balancing is required in places and at times that have the highest bike-share ridership demands.

According to Figure 4.1, there is a similar weekly trend in bike-share data. Weekdays tend to have higher ridership than weekends. There is a major decrease in ridership around Thanksgiving and a decline around the Veteran’s day, too.

ggplot(dat_ymd %>%
         group_by(interval60) %>%
         tally())+
  geom_line(aes(x = interval60, y = n))+
  labs(title="Figure 4.1: Bike share trips per hr. Philadelphia, Nov, 2018",
       x="Date", 
       y="Number of trips")+
  plotTheme

Figure 4.2 reveals that peak hours have the highest demand and need more frequent re-balancing.

dat_ymd %>%
        mutate(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(interval60, start_station, time_of_day) %>%
         tally()%>%
  group_by(start_station, time_of_day)%>%
  summarize(mean_trips = mean(n))%>%
  ggplot()+
  geom_histogram(aes(mean_trips), binwidth = 1)+
  labs(title="Figure 4.2: Mean Number of Hourly Trips Per Station. Philadelphia, Nov, 2018",
       x="Number of trips", 
       y="Frequency")+
  facet_wrap(~time_of_day)+
  plotTheme

Figure 4.3 and 4.4 indicate that on average Wednesdays have the highest peak demands, and Fridays have the lowest demands for weekdays. Weekends have lower and more evenly distributed demands, and peaks on Sundays are typically around 1 hour later than the peaks on Saturdays.

ggplot(dat_ymd %>% mutate(hour = hour(start_time)))+
     geom_freqpoly(aes(hour, color = dotw), binwidth = 1)+
  labs(title="Figure 4.3: Bike share trips in Philadelphia, by day of the week, Nov, 2018",
       x="Hour", 
       y="Trip Counts")+
     plotTheme

ggplot(dat_ymd %>% 
         mutate(hour = hour(start_time),
                weekend = ifelse(dotw %in% c("Sun", "Sat"), "Weekend", "Weekday")))+
     geom_freqpoly(aes(hour, color = weekend), binwidth = 1)+
  labs(title="Figure 4.4: Bike share trips in Philadelphia - weekend vs weekday, Nov, 2018",
       x="Hour", 
       y="Trip Counts")+
     plotTheme

Figure 4.5 shows that 1) Weekdays tend to have higher demands than weekends; 2) On weekdays in PM rush hours, there are distinctly clustering high demands in center city and the bridge linking center city and eastern fringe of west Philly. Mid-days and overnight hours have very similar spatial distribution of demands, with nearly all hot spots in center city. Compared with other times on weekdays, AM rush hours have high demands extending into the south of center city; 3) On weekends, AM rush hours have the lowest overall demand. Hot spots appear mostly on mid-days and PM rush hours in center city. Figure 4.6 is an animation of a typical Monday ridership in early November in Philadelphia.

ggplot()+
  geom_sf(data = PhillyTracts %>%
          st_transform(crs=4326), fill = "white")+
  geom_point(data = dat_census %>% 
            mutate(hour = hour(start_time),
                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, start_lat, start_lon, weekend, time_of_day) %>%
              tally(),
            aes(x=start_lon, y = start_lat, color = n), 
            fill = "transparent", alpha = 0.4, size = 1)+
  scale_colour_viridis(direction = -1,
  discrete = FALSE, option = "D")+
  ylim(min(dat_census$start_lat), max(dat_census$start_lat))+
  xlim(min(dat_census$start_lon), max(dat_census$start_lon))+
  facet_grid(weekend ~ time_of_day)+
  labs(title="Figure 4.5: Bike share trips per hr by station. Philadelphia, Nov, 2018")+
  mapTheme

PhillyCensus <- PhillyCensus %>%
    st_transform(st_crs(fishnet))
bs_tract <- st_join(dat_sf, PhillyCensus, join=st_within)

week44 <-
  filter(bs_tract , week == 44 & dotw == "Mon")

week44.panel <-
  expand.grid(
    interval15 = unique(week44$interval15),
    Pickup.Census.Tract = unique(bs_tract$GEOID))

ride.animation.data <-
  mutate(week44, Trip_Counter = 1) %>%
    right_join(week44.panel) %>% 
    group_by(interval15, Pickup.Census.Tract) %>%
    summarize(Trip_Count = sum(Trip_Counter, na.rm=T)) %>% 
    ungroup() %>% 
    st_sf() %>%
    mutate(Trips = case_when(Trip_Count == 0 ~ "0 trips",
                             Trip_Count > 0 & Trip_Count <= 3 ~ "1-3 trips",
                             Trip_Count > 3 & Trip_Count <= 6 ~ "4-6 trips",
                             Trip_Count > 6 & Trip_Count <= 10 ~ "7-10 trips",
                             Trip_Count > 10 ~ "11+ trips")) %>%
    mutate(Trips  = fct_relevel(Trips, "0 trips","1-3 trips","4-6 trips",
                                       "7-10 trips","10+ trips"))

#  left_join(dat_net, st_drop_geometry(vars_net), by="uniqueID")
rideshare_animation <-
  ggplot() +
    geom_sf(data = ride.animation.data, aes(col = Trips, size = Trips), show.legend = "point")+
    geom_sf(data = PhillyTracts, color = "grey", fill = "transparent")+
    scale_fill_manual(values = c("green", "yellow", "orange","red")) +
    labs(title = "Figure 4.6: Rideshare pickups for one day in November 2018",
         subtitle = "15 minute intervals: {current_frame}") +
    transition_manual(interval15) +
    mapTheme

library(gganimate)
library(gifski)
animate(rideshare_animation, duration=20, renderer = gifski_renderer())

anim_save("rideshare_local", rideshare_animation, duration=20, renderer = gifski_renderer())

Comparison of model performance

We can learn from Figure 5.1 and 5.2 that DTime_Space_FE_timeLags model has the least overall errors, and adding holiday feature doesn’t have a significant accuracy improvement to the time-lag models.

ride.Train <- filter(study.panel, week <= 46) 
ride.Test <- filter(study.panel, week > 46)
study_panel.net <- merge(x=study.panel, y=st_drop_geometry(vars_net), by.x = "start_station", by.y = "id", all.x = TRUE) %>%
    filter(is.na(uniqueID) == FALSE)
  
ride.Train.net <- filter(study_panel.net, week <= 46) 
ride.Test.net <- filter(study_panel.net, week > 46)
ride.Test.weekNest <- 
  ride.Test %>%
  nest(-week) 

ride.Test.weekNest.net <- 
  ride.Test.net %>%
  nest(-week) 
model_pred <- function(dat, fit){
   pred <- predict(fit, newdata = dat)}
week_predictions <- 
  ride.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_timeLags = map(.x = data, fit = reg4, .f = model_pred),
           ETime_Space_FE_timeLags_holidayLags = map(.x = data, fit = reg5, .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))
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 = palette5) +
    labs(title = "Figure 5.1: Mean Absolute Errors by model specification and week") +
  plotTheme

week_predictions %>% 
    mutate(interval60 = map(data, pull, interval60),
           start_station = map(data, pull, start_station)) %>%
    dplyr::select(interval60, start_station, Observed, Prediction, Regression) %>%
    unnest() %>%
    gather(Variable, Value, -Regression, -interval60, -start_station) %>%
    group_by(Regression, Variable, interval60) %>%
    summarize(Value = sum(Value)) %>%
    ggplot(aes(interval60, Value, colour=Variable)) + 
      geom_line(size = 1.1) + 
      facet_wrap(~Regression, ncol=1) +
      labs(title = "Figure 5.2: Predicted/Observed bike share time series", subtitle = "Philadelphia; A test set of 2 weeks",  x = "Hour", y= "Station Trips") +
      plotTheme

week_predictions %>% 
    mutate(interval60 = map(data, pull, interval60),
           start_station = map(data, pull, start_station), 
           start_lat = map(data, pull, start_lat), 
           start_lon = map(data, pull, start_lon)) %>%
    dplyr::select(interval60, start_station, start_lon, start_lat, Observed, Prediction, Regression) %>%
    unnest() %>%
  filter(Regression == "DTime_Space_FE_timeLags") %>% ##regresstion
  group_by(start_station, start_lon, start_lat) %>%
  summarize(MAE = mean(abs(Observed-Prediction), na.rm = TRUE))%>%
ggplot(.)+
  geom_sf(data = (PhillyTracts%>%
    st_transform(st_crs(4326))), color = "grey", fill = "transparent")+
  geom_point(aes(x = start_lon, y = start_lat, color = MAE), 
             fill = "transparent", alpha = 0.4)+
  scale_colour_viridis(direction = -1,
  discrete = FALSE, option = "D")+
  ylim(min(dat_census$start_lat), max(dat_census$start_lat))+
  xlim(min(dat_census$start_lon), max(dat_census$start_lon))+
  labs(title="Figure 5.3: Mean Abs Error, Test Set, Model 4")+
  mapTheme

Using our best model to map the error (Figure 5.3), it’s clear that higher errors concentrate in the center city and share a similar pattern with the overall peaks of demands. That’s to say, this model may not be competent enough to generalize in the center city to predict the demands.

week_predictions %>% 
    mutate(interval60 = map(data, pull, interval60),
           start_station = map(data, pull, start_station), 
           start_lat = map(data, pull, start_lat), 
           start_lon = map(data, pull, start_lon),
           dotw = map(data, pull, dotw)) %>%
    dplyr::select(interval60, start_station, start_lon, 
           start_lat, Observed, Prediction, Regression,
           dotw) %>%
    unnest() %>%
  filter(Regression == "DTime_Space_FE_timeLags")%>%
  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"))%>%
  ggplot()+
  geom_point(aes(x= Observed, y = Prediction))+
    geom_smooth(aes(x= Observed, y= Prediction), method = "lm", se = FALSE, color = "red")+
    geom_abline(slope = 1, intercept = 0)+
  facet_grid(time_of_day~weekend)+
  labs(title="Figure 5.4: Observed vs Predicted",
       x="Observed trips", 
       y="Predicted trips")+
  plotTheme

According to Figure 5.4, there is a serious under-prediction of ridership at all time of a day and a week, which suggests that our model is not accurate enough because the actual riderships are typically higher.

week_predictions %>% 
    mutate(interval60 = map(data, pull, interval60),
           start_station = map(data, pull, start_station), 
           start_lat = map(data, pull, start_lat), 
           start_lon = map(data, pull, start_lon),
           dotw = map(data, pull, dotw)) %>%
    dplyr::select(interval60, start_station, start_lon, 
           start_lat, Observed, Prediction, Regression,
           dotw) %>%
    unnest() %>%
  filter(Regression == "DTime_Space_FE_timeLags")%>%
  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, weekend, time_of_day, start_lon, start_lat) %>%
  summarize(MAE = mean(abs(Observed-Prediction), na.rm = TRUE))%>%
  ggplot(.)+
  geom_sf(data = PhillyTracts%>%
    st_transform(st_crs(4326)), color = "grey", fill = "transparent")+
  geom_point(aes(x = start_lon, y = start_lat, color = MAE), 
             fill = "transparent", size = 0.5, alpha = 1)+
  scale_colour_viridis(direction = -1,
  discrete = FALSE, option = "D")+
  ylim(min(dat_census$start_lat), max(dat_census$start_lat))+
  xlim(min(dat_census$start_lon), max(dat_census$start_lon))+
  facet_grid(weekend~time_of_day)+
  labs(title="Figure 5.5: Mean Absolute Errors, Test Set")+
  mapTheme

Further examining the spatial distribution of errors by geography and time, we can see that the biggest errors are on AM Rush hours in Lower North Philadelphia and northern fringe of the South Philadelphia. The errors are also high on PM Rush hours in center city and the bridge connecting West Philadelphia. It’s not necessary that high errors will accompany high demands given that AM Rush hours on weekdays tend to have lower demands than PM Rush hours (Figure 4.5), and AM hours on weekends have higher overall errors than overnight hours while demands are otherwise. Bigger mean absolute error means higher inaccuracy, while also casting a concern to expect this model to generalize in certain places and at certain time.

Cross validation

Temporal and spatial cross-validation are conducted to examine models’ generalizibility on new data.

bikenetsample <- sample_n(study.panel, 10000)%>%
  na.omit()

study_panel.net <- study_panel.net[,colSums(is.na(study_panel.net)) < nrow(study_panel.net)]
cp_stu_pa.net <- na.omit(study_panel.net) 

bikenetsample.net <- sample_n(cp_stu_pa.net, 10000)%>%
  na.omit()

library(caret)
fitControl <- trainControl(method = "cv", 
                           number = 100,
                           savePredictions = TRUE)

set.seed(1000)
# for k-folds CV

reg.cv.k <-  
  train(Trip_Count ~ start_station +  hour(interval60) + dotw + Temperature + Precipitation +
          lagHour + lag2Hours +lag3Hours + lag12Hours + lag1day, 
        data = bikenetsample,  
        method = "lm",  
        trControl = fitControl,  
        na.action = na.pass)

reg.cv.k
## Linear Regression 
## 
## 9712 samples
##   10 predictor
## 
## No pre-processing
## Resampling: Cross-Validated (100 fold) 
## Summary of sample sizes: 9614, 9615, 9615, 9615, 9615, 9615, ... 
## Resampling results:
## 
##   RMSE       Rsquared   MAE      
##   0.8660462  0.2714473  0.5275034
## 
## Tuning parameter 'intercept' was held constant at a value of TRUE
reg.cv.k.net <-
   train(Trip_Count ~ start_station +  hour(interval60) + dotw + Temperature + Precipitation +
          lagHour + lag2Hours +lag3Hours + lag12Hours + lag1day + college.nn + national_park.nn + tourism.nn + shop.nn + cuisine.nn + office.nn + bus_station.nn + trolly_stops.nn + rail_station.nn + septaStops.nn + uniqueID + Total_Pop + Med_Inc + White_Pop + Travel_Time + Means_of_Transport + Total_Public_Trans + Med_Age + Percent_White + Mean_Commute_Time + Percent_Taking_Public_Trans, 
        data = bikenetsample.net,  
        method = "lm",  
        trControl = fitControl,  
        na.action = na.pass)

reg.cv.k.net
## Linear Regression 
## 
## 10000 samples
##    31 predictor
## 
## No pre-processing
## Resampling: Cross-Validated (100 fold) 
## Summary of sample sizes: 9900, 9900, 9900, 9900, 9901, 9901, ... 
## Resampling results:
## 
##   RMSE       Rsquared   MAE      
##   0.9356777  0.2825749  0.5723044
## 
## Tuning parameter 'intercept' was held constant at a value of TRUE
ggplot(reg.cv.k$resample, aes(x=MAE)) +
  geom_histogram(bins = 30, colour="black", fill = "#FDE725FF") +
  labs(title = "Figure 6.1: Mean Average Error in Cross Validation Tests with model 4") 

ggplot(reg.cv.k.net$resample, aes(x=MAE)) +
  geom_histogram(bins = 30, colour="black", fill = "#FDE725FF") +
  labs(title = "Figure 6.2: Mean Average Error in Cross Validation Tests with model 4 plus Addtional Features") 

K-fold method is used here for the temporal cross-validation. The errors of our best time-lag model DTime_Space_FE_timeLags cluster around 0.5-0.55 (Figure 6.1), which is a fair performance given the average ridership per station per hour. However, adding amenity and spatial features to the model doesn’t improve its temporal performance in this case (Figure 6.2).

K-fold and LOGO-CV methods are both used here to examine the improvements of the model generalizibility by adding spatial and amenity features. The accumulated mean absolute error for each station is significantly reduced by adding spatial process to the model (Fgure 6.3). The errors in center city are much lower now with higher error in the southeastern Philly (Figure 6.4). The predictions by all four models (Figure 6.5) are very similar to the actual observation (Figure 6.6).

Conclusions

Overall, I think my algorithm is more useful to predict the overall spatial distribution of the ridership hot spots than predicting the next 1 hour bike-share demand in a certain station. Based on my current algorithm, it’s advised that bikes be roughly divided according to the predicted spatial pattern and attributed to the hot spots first and then do minor adjustments among near hot spots.

To improve the current algorithm, longer range of time data should be applied but here the data is only 5-week long due to the computer processing power restrictions. Also, since the demand of next time period is most affected by the closest last time period, a smaller time lag in temporal prediction should also help to improve the performance of the current algorithm.

LS0tDQp0aXRsZTogIlBoaWxhZGVscGhpYSBCaWtlLXNoYXJlIFByb2plY3QiDQphdXRob3I6ICJCaW5nY2h1IENoZW4iDQpkYXRlOiAiMTEvNy8yMDIwIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6ICJoaWRlIg0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KDQojIEludHJvZHVjdGlvbg0KDQpCaWtlLXNoYXJlIGlzIGEgYmlrZS1zaGFyaW5nIHN5c3RlbSB0aGF0IGFsbG93cyBwZW9wbGUgdG8gYm9ycm93IGFuZCByaWRlIGJpa2VzIHdpdGhvdXQgb3duaW5nIGEgYmlrZS4gSXQncyBjb21wcmlzZWQgb2YgYSBuZXR3b3JrIG9mIGJpa2Utc2hhcmluZyBzdGF0aW9ucyB3aXRoIGRvY2tzIGZvciBiaWtlcyB0byBiZSBjaGVja2VkIGluLCBzdG9yZWQgYW5kIGNoZWNrZWQgb3V0Lg0KDQpTdGF0aW9ucyBpbiBkaWZmZXJlbnQgZ2VvZ3JhcGhpY2FsIGxvY2F0aW9ucyBmYWNlIGRpZmZlcmVudCBkZW1hbmRzIHRoYXQgdmFyeSB3aXRoIHRpbWUuIE9uZSBwYXJhZG94IGlzIHRoYXQgcG9wdWxhciBzdGF0aW9ucyB1c3VhbGx5IGhhdmUgaGlnaGVyIGRlbWFuZCBmb3IgYmlrZXMgYW5kIHdpbGwgc29vbmVyIHJ1biBvdXQgb2YgYmlrZXMgaWYgbm90IHJlcGxlbmlzaGVkIHByb3Blcmx5LiBGb3IgdGhlIGJpa2Utc2hhcmUgY29tcGFuaWVzLCB0aGlzIGRlZmljaWVuY3kgY2FuIGxlYWQgdG8gaW5jcmVhc2luZyBkaXNzYXRpc2ZhY3Rpb24gYW5kIGNodXJuaW5nIHJhdGUgYW1vbmcgdXNlcnMsIHRoZXJlYnkgbG93ZXJpbmcgdGhlaXIgcHJvZml0cy4gRm9yIGJpa2Utc2hhcmUgdXNlcnMsIHRoaXMgY2FuIHNlcmlvdXNseSBjdXQgZG93biB0aGVpciB1dGlsaXR5IHRvIHVzZSBiaWtlLXNoYXJpbmcgc2VydmljZSBhbmQgY2F1c2UgaW5lZmZpY2llbmN5IGFuZCBpbmNvbnZlbmllbmNlIGZvciB0aGVpciB3b3JrIGFuZCBsaWZlLiBGb3IgdGhlIGNpdHkvcmVnaW9uLCBiaWtlLXNoYXJlIHNlcnZpY2VzIHdvdWxkIGJlIHVzZWxlc3MgaW4gcmFpc2luZyBhY3RpdmUgdHJhdmVsIGlmIHRoZXkgY2FuJ3QgcHJvdmlkZSBlbm91Z2ggc3VwcGx5IHdoZXJlIGFuZCB3aGVuIG5lZWRlZC4gV2l0aCBzb21lIG9mIHRoZSB1c2VycyB0dXJuaW5nIHRvIGNhciB0cmF2ZWwgYWdhaW4sIHRoZSBjb25nZXN0aW9uIGFuZCBlbnZpcm9ubWVudGFsIGlzc3VlcyB3aWxsIG9ubHkgYmUgd29yc2VuIGluIHRoZSBjaXR5L3JlZ2lvbi4gR2l2ZW4gYWxsIHRoZXNlIHBlcnNwZWN0aXZlcywgYmlrZS1zaGFyZSByZS1iYWxhbmNpbmcsIHdoaWNoIG1lYW5zIHJlLWRpc3RyaWJ1dGluZyBiaWtlcyB3aXRoIGNlcnRhaW4gc3RyYXRlZ2llcyBzbyB0aGF0IHRoZSBzaG9ydGFnZSBvZiBzdXBwbHkgaW4gYSBzdGF0aW9uIGNhbiBiZSBuYXJyb3dlZCBhcyBtdWNoIGFzIHBvc3NpYmxlLCBpcyBtdWNoIG5lZWRlZCBhcyBhIGNyaXRpY2FsIGVsZW1lbnQgdG8gbWFrZSB0aGUgYmlrZS1zaGFyZSBzeXN0ZW0gYSBzdWNjZXNzLiANCg0KVGhlIHN0cmF0ZWd5IGZvciByZS1iYWxhbmNpbmcgaGVyZSB3aWxsIGJlIHJlbHlpbmcgb24gdHJ1Y2tzIHRvIGNvbGxlY3QgYW5kIG1vdmUgYmlrZXMgdG8gY2VydGFpbiBzdGF0aW9uLiBUaGlzIGlzIGJlY2F1c2UgdGhlIGJpa2Ugc3RhdGlvbnMgaW4gUGhpbGFkZWxwaGlhIGFyZSBxdWl0ZSBkaXNwZXJzZWQsIHRoZXJlZm9yZSBhIHJlbGF0aXZlbHkgaGlnaCBpbmNlbnRpdmUgc2hvdWxkIGJlIHByb3ZpZGVkIGlmIHdlIHdhbnQgdG8gcHVyZWx5IHJlbHkgb24gdXNlcnMgIHRvIHJlLWJhbGFuY2UgdGhlIGJpa2VzLCBhZGRpbmcgdG8gYmUgYSBub25uZWdsaWdpYmxlIGNvc3Qgd2l0aCBwb3NzaWJseSB2ZXJ5IGxpbWl0ZWQgZWZmZWN0cy4gQ29tcGFyZWQgd2l0aCB0aGlzIG1ldGhvZCwgc21hbGwgdHJ1Y2tzIG1heSBiZSBtb3JlIGNvbXBldGVudCBpbiBndWFyYW50ZWVpbmcgdGhlIG91dGNvbWVzLiBBdCBhbnkgZ2l2ZW4gdGltZSwgaXQgd291bGQgYmUgaWRlYWwgaWYgd2UgY2FuIHByZWRpY3QgdGhlIGRlbWFuZCBpbiB0aGUgbmV4dCAxIGhvdXIsIHdoaWNoIHNob3VsZCBiZSBzdWZmaWNpZW50IGZvciB0aGUgdHJ1Y2tzIHRvIHJlbG9jYXRlIGJpa2VzIHRvIGhvdC1zcG90IGFyZWFzLiANCg0KYGBge3Igc2V0dXAxLCBjYWNoZT1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGVjaG89RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KbGlicmFyeShzZikNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeSh0aWdyaXMpDQpsaWJyYXJ5KHRpZHljZW5zdXMpDQpsaWJyYXJ5KHZpcmlkaXMpDQpsaWJyYXJ5KHJpZW0pDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpsaWJyYXJ5KHJlYWR4bCkNCmxpYnJhcnkoZ2dhbmltYXRlKQ0KbGlicmFyeShGTk4pDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQoNCnBsb3RUaGVtZSA8LSB0aGVtZSgNCiAgcGxvdC50aXRsZSA9ZWxlbWVudF90ZXh0KHNpemU9MTIpLA0KICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksDQogIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksDQogIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwNCiAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwNCiAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksDQogICMgU2V0IHRoZSBlbnRpcmUgY2hhcnQgcmVnaW9uIHRvIGJsYW5rDQogIHBhbmVsLmJhY2tncm91bmQ9ZWxlbWVudF9ibGFuaygpLA0KICBwbG90LmJhY2tncm91bmQ9ZWxlbWVudF9ibGFuaygpLA0KICAjcGFuZWwuYm9yZGVyPWVsZW1lbnRfcmVjdChjb2xvdXI9IiNGMEYwRjAiKSwNCiAgIyBGb3JtYXQgdGhlIGdyaWQNCiAgcGFuZWwuZ3JpZC5tYWpvcj1lbGVtZW50X2xpbmUoY29sb3VyPSIjRDBEMEQwIixzaXplPS4yKSwNCiAgYXhpcy50aWNrcz1lbGVtZW50X2JsYW5rKCkpDQoNCm1hcFRoZW1lIDwtIHRoZW1lKHBsb3QudGl0bGUgPWVsZW1lbnRfdGV4dChzaXplPTEyKSwNCiAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwNCiAgICAgICAgICAgICAgICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksDQogICAgICAgICAgICAgICAgICBheGlzLmxpbmU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgYXhpcy50aWNrcz1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQ9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgcGFuZWwuYm9yZGVyPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3I9ZWxlbWVudF9saW5lKGNvbG91ciA9ICd0cmFuc3BhcmVudCcpLA0KICAgICAgICAgICAgICAgICAgcGFuZWwuZ3JpZC5taW5vcj1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gInZlcnRpY2FsIiwgDQogICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLA0KICAgICAgICAgICAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMSwgMSwgMSwgMSwgJ2NtJyksDQogICAgICAgICAgICAgICAgICBsZWdlbmQua2V5LmhlaWdodCA9IHVuaXQoMSwgImNtIiksIGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KDAuMiwgImNtIikpDQoNCg0KcGFsZXR0ZTUgPC0gYygiI2VmZjNmZiIsIiNiZGQ3ZTciLCIjNmJhZWQ2IiwiIzMxODJiZCIsIiMwODUxOWMiKQ0KcGFsZXR0ZTQgPC0gYygiI0QyRkJENCIsIiM5MkJDQUIiLCIjNTI3RDgyIiwiIzEyM0Y1QSIpDQpwYWxldHRlMiA8LSBjKCIjNmJhZWQ2IiwiIzA4NTE5YyIpDQoNCiNuZWFyZXN0IG5laWdoYm9yIGZ1bmN0aW9uDQpubl9mdW5jdGlvbiA8LSBmdW5jdGlvbihtZWFzdXJlRnJvbSxtZWFzdXJlVG8saykgew0KICBtZWFzdXJlRnJvbV9NYXRyaXggPC0gYXMubWF0cml4KG1lYXN1cmVGcm9tKQ0KICBtZWFzdXJlVG9fTWF0cml4IDwtIGFzLm1hdHJpeChtZWFzdXJlVG8pDQogIG5uIDwtICAgDQogICAgZ2V0LmtubngobWVhc3VyZVRvLCBtZWFzdXJlRnJvbSwgaykkbm4uZGlzdA0KICBvdXRwdXQgPC0NCiAgICBhcy5kYXRhLmZyYW1lKG5uKSAlPiUNCiAgICByb3duYW1lc190b19jb2x1bW4odmFyID0gInRoaXNQb2ludCIpICU+JQ0KICAgIGdhdGhlcihwb2ludHMsIHBvaW50X2Rpc3RhbmNlLCBWMTpuY29sKC4pKSAlPiUNCiAgICBhcnJhbmdlKGFzLm51bWVyaWModGhpc1BvaW50KSkgJT4lDQogICAgZ3JvdXBfYnkodGhpc1BvaW50KSAlPiUNCiAgICBzdW1tYXJpemUocG9pbnREaXN0YW5jZSA9IG1lYW4ocG9pbnRfZGlzdGFuY2UpKSAlPiUNCiAgICBhcnJhbmdlKGFzLm51bWVyaWModGhpc1BvaW50KSkgJT4lDQogICAgZHBseXI6OnNlbGVjdCgtdGhpc1BvaW50KSAlPiUNCiAgICBwdWxsKCkNCg0KICByZXR1cm4ob3V0cHV0KSAgDQp9DQoNCmRhdHE0IDwtIHJlYWQuY3N2KCJFOi9VcGVubi9DUExONTA4L2Jpa2VzaGFyZV9uZXcvRGF0YS9QaGlsbHkyMDE4cTQuY3N2IikNCmJzX3N0YXRpb24gPC0gc3RfcmVhZCgiaHR0cHM6Ly9hcGkucGhpbGEuZ292L2Jpa2Utc2hhcmUtc3RhdGlvbnMvdjEiKSANCg0KZGF0X3ltZCA8LSBkYXRxNCAlPiUgI2VkaXQNCiAgbXV0YXRlKGludGVydmFsNjAgPSBmbG9vcl9kYXRlKHltZF9obXMoc3RhcnRfdGltZSksIHVuaXQgPSAiaG91ciIpLA0KICAgICAgICAgaW50ZXJ2YWwxNSA9IGZsb29yX2RhdGUoeW1kX2htcyhzdGFydF90aW1lKSwgdW5pdCA9ICIxNSBtaW5zIiksDQogICAgICAgICB3ZWVrID0gd2VlayhpbnRlcnZhbDYwKSwNCiAgICAgICAgIGRvdHcgPSB3ZGF5KGludGVydmFsNjAsIGxhYmVsPVRSVUUpKSAlPiUNCiAgZmlsdGVyKHdlZWsgPj00NCwgd2VlayA8PSA0OCkNCg0KcTEgPC0gbWVyZ2UoZGF0X3ltZCwgYnNfc3RhdGlvbiwgYnkueCA9ICJzdGFydF9zdGF0aW9uIiwgYnkueSA9ICJpZCIpDQoNCmNvbG5hbWVzKHExKVt3aGljaChuYW1lcyhxMSkgPT0gIm5hbWUiKV0gPC0gInN0YXJ0X3N0YXRpb25fbmFtZSINCg0KZGF0X3NmIDwtIHN0X2FzX3NmKHExLCBjb29yZHMgPSBjKCJzdGFydF9sb24iLCAic3RhcnRfbGF0IiksIGNycyA9IDQzMjYpDQpgYGANCg0KDQojIERhdGEgY29sbGVjdGlvbiBhbmQgZmVhdHVyZSBlbmdpbmVlcmluZw0KDQpJIHVzZSB0aGUgYmlrZS1zaGFyaW5nIGRhdGEgaW4gUGhpbGFkZWxwaGlhIGZyb20gMTAvMjkvMjAxOCB0byAxMi8wMi8yMDE4IGhlcmUgdG8gY29uZHVjdCBhIDUtd2VlayBwYW5lbCBleHBlcmltZW50IGluIHByZWRpY3RpbmcgZGVtYW5kcy4gRmVhdHVyZXMgdGhhdCBhcmUgaW5jbHVkZWQgYXJlIHdlYXRoZXIgKHRlbXBlcmF0dXJlLCBwcmVjaXBpdGF0aW9uIGFuZCB3aW5kIHNwZWVkKSBhcyBzaG93biBpbiBGaWd1cmUgMi4xLCBhbWVuaXR5IGVsZW1lbnRzIChjbG9zZW5lc3MgdG8gc2Nob29sLCBzaG9wcywgcGFya3MsIHRvdXJpc20gc2l0ZXMsIGN1aXNpbmUgcGxhY2VzLCBvZmZpY2VzIGFuZCBidXMsIHRyb2xseSwgdHJhbnNpdCBzdGF0aW9ucyksIHNvbWUgb3RoZXIgc3BhdGlhbCBmZWF0dXJlcyAobWVkaWFuIGluY29tZSwgbWVkaWFuIGFnZSwgcGVyY2VudCBvZiB3aGl0ZSBwb3B1bGF0aW9uLCBtZWFuIGNvbW11dGUgdGltZSBhbmQgcGVyY2VudCBvZiB0YWtpbmcgcHVibGljIHRyYW5zcG9ydGF0aW9uLCBudW1iZXIgb2YgY29tbXV0ZXJzKSwgYW5kIHRpbWUgbGFnIGZlYXR1cmVzIChUYWJsZSAxKS4gQSBmaXNobmV0IGlzIGFsc28gY3JlYXRlZCBmb3Igc3BhdGlhbCBpbnB1dHMuIA0KDQpgYGB7ciBpbXBvcnRfd2VhdGhlciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGNhY2hlID0gVFJVRX0NCndlYXRoZXIuUGFuZWwgPC0gDQogIHJpZW1fbWVhc3VyZXMoc3RhdGlvbiA9ICJQSEwiLCBkYXRlX3N0YXJ0ID0gIjIwMTgtMTAtMjkiLCBkYXRlX2VuZCA9ICIyMDE4LTEyLTAyIikgJT4lDQogIGRwbHlyOjpzZWxlY3QodmFsaWQsIHRtcGYsIHAwMWksIHNrbnQpJT4lDQogIHJlcGxhY2UoaXMubmEoLiksIDApICU+JQ0KICAgIG11dGF0ZShpbnRlcnZhbDYwID0geW1kX2goc3Vic3RyKHZhbGlkLDEsMTMpKSkgJT4lDQogICAgbXV0YXRlKHdlZWsgPSB3ZWVrKGludGVydmFsNjApLA0KICAgICAgICAgICBkb3R3ID0gd2RheShpbnRlcnZhbDYwLCBsYWJlbD1UUlVFKSkgJT4lDQogICAgZ3JvdXBfYnkoaW50ZXJ2YWw2MCkgJT4lDQogICAgc3VtbWFyaXplKFRlbXBlcmF0dXJlID0gbWF4KHRtcGYpLA0KICAgICAgICAgICAgICBQcmVjaXBpdGF0aW9uID0gc3VtKHAwMWkpLA0KICAgICAgICAgICAgICBXaW5kX1NwZWVkID0gbWF4KHNrbnQpKSAlPiUNCiAgICBtdXRhdGUoVGVtcGVyYXR1cmUgPSBpZmVsc2UoVGVtcGVyYXR1cmUgPT0gMCwgNDIsIFRlbXBlcmF0dXJlKSkNCmBgYA0KDQpgYGB7ciBwbG90X3dlYXRoZXIsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShncmlkRXh0cmEpDQpncmlkLmFycmFuZ2UoDQogIGdncGxvdCh3ZWF0aGVyLlBhbmVsLCBhZXMoaW50ZXJ2YWw2MCxQcmVjaXBpdGF0aW9uKSkgKyBnZW9tX2xpbmUoKSArIA0KICBsYWJzKHRpdGxlPSJQZXJjaXBpdGF0aW9uIiwgeD0iSG91ciIsIHk9IlBlcmVjaXBpdGF0aW9uIikgKyBwbG90VGhlbWUsDQogIGdncGxvdCh3ZWF0aGVyLlBhbmVsLCBhZXMoaW50ZXJ2YWw2MCxXaW5kX1NwZWVkKSkgKyBnZW9tX2xpbmUoKSArIA0KICAgIGxhYnModGl0bGU9IldpbmQgU3BlZWQiLCB4PSJIb3VyIiwgeT0iV2luZCBTcGVlZCIpICsgcGxvdFRoZW1lLA0KICBnZ3Bsb3Qod2VhdGhlci5QYW5lbCwgYWVzKGludGVydmFsNjAsVGVtcGVyYXR1cmUpKSArIGdlb21fbGluZSgpICsgDQogICAgbGFicyh0aXRsZT0iVGVtcGVyYXR1cmUiLCB4PSJIb3VyIiwgeT0iVGVtcGVyYXR1cmUiKSArIHBsb3RUaGVtZSwNCiAgdG9wPSJGaWd1cmUgMi4xV2VhdGhlciBEYXRhIC0gUGhpbGFkZWxwaGlhIFBITCAtIE5vdiwgMjAxOCIpDQpgYGANCg0KYGBge3IgaW5zdGFsbF9jZW5zdXNfQVBJX2tleV9mYWxzZWNvZGUsIGV2YWwgPSBGQUxTRSwgbWVzc2FnZT1GQUxTRSwgaW5jbHVkZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmNlbnN1c19hcGlfa2V5KCIwZTNjYzM5MTA3MjM0MzQ2ODVmMWU0YzVkZjJhYzUxOWMwNzkwYmE1Iiwgb3ZlcndyaXRlID0gVFJVRSkNCmBgYA0KDQpgYGB7ciBnZXRfY2Vuc3VzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjYWNoZT1UUlVFLCBlY2hvPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NClBoaWxseUNlbnN1cyA8LSANCiAgZ2V0X2FjcyhnZW9ncmFwaHkgPSAidHJhY3QiLCANCiAgICAgICAgICB2YXJpYWJsZXMgPSBjKCJCMDEwMDNfMDAxIiwgIkIxOTAxM18wMDEiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICJCMDIwMDFfMDAyIiwgIkIwODAxM18wMDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIkIwODAxMl8wMDEiLCAiQjA4MzAxXzAwMSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIkIwODMwMV8wMTAiLCAiQjAxMDAyXzAwMSIpLCANCiAgICAgICAgICB5ZWFyID0gMjAxOCwgDQogICAgICAgICAgc3RhdGUgPSA0MiwNCiAgICAgICAgICBjb3VudHk9IDEwMSwNCiAgICAgICAgICBnZW9tZXRyeSA9IFRSVUUsIA0KICAgICAgICAgIG91dHB1dCA9ICJ3aWRlIikgJT4lDQogIHJlbmFtZShUb3RhbF9Qb3AgPSAgQjAxMDAzXzAwMUUsDQogICAgICAgICBNZWRfSW5jID0gQjE5MDEzXzAwMUUsDQogICAgICAgICBNZWRfQWdlID0gQjAxMDAyXzAwMUUsDQogICAgICAgICBXaGl0ZV9Qb3AgPSBCMDIwMDFfMDAyRSwNCiAgICAgICAgIFRyYXZlbF9UaW1lID0gQjA4MDEzXzAwMUUsDQogICAgICAgICBOdW1fQ29tbXV0ZXJzID0gQjA4MDEyXzAwMUUsDQogICAgICAgICBNZWFuc19vZl9UcmFuc3BvcnQgPSBCMDgzMDFfMDAxRSwNCiAgICAgICAgIFRvdGFsX1B1YmxpY19UcmFucyA9IEIwODMwMV8wMTBFKSAlPiUNCiAgZHBseXI6OnNlbGVjdChUb3RhbF9Qb3AsIE1lZF9JbmMsIFdoaXRlX1BvcCwgVHJhdmVsX1RpbWUsIA0KICAgICAgICAgTWVhbnNfb2ZfVHJhbnNwb3J0LCBUb3RhbF9QdWJsaWNfVHJhbnMsDQogICAgICAgICBNZWRfQWdlLA0KICAgICAgICAgR0VPSUQsIGdlb21ldHJ5KSAlPiUNCiAgbXV0YXRlKFBlcmNlbnRfV2hpdGUgPSBXaGl0ZV9Qb3AgLyBUb3RhbF9Qb3AsDQogICAgICAgICBNZWFuX0NvbW11dGVfVGltZSA9IFRyYXZlbF9UaW1lIC8gVG90YWxfUHVibGljX1RyYW5zLA0KICAgICAgICAgUGVyY2VudF9UYWtpbmdfUHVibGljX1RyYW5zID0gVG90YWxfUHVibGljX1RyYW5zIC8gTWVhbnNfb2ZfVHJhbnNwb3J0KQ0KDQpQaGlsbHlib3VuZGFyeSA8LSBzdF9yZWFkKCJodHRwOi8vZGF0YS5waGwub3BlbmRhdGEuYXJjZ2lzLmNvbS9kYXRhc2V0cy80MDVlYzNkYTk0MmQ0ZTIwODY5ZDRlMTQ0OWEyYmU0OF8wLmdlb2pzb24iKSAlPiUNCiAgc3RfdHJhbnNmb3JtKHN0X2NycygyMjcyKSkNCg0KUGhpbGx5VHJhY3RzIDwtIA0KICBQaGlsbHlDZW5zdXMgJT4lDQogIGFzLmRhdGEuZnJhbWUoKSAlPiUNCiAgZGlzdGluY3QoR0VPSUQsIC5rZWVwX2FsbCA9IFRSVUUpICU+JQ0KICBkcGx5cjo6c2VsZWN0KEdFT0lELCBnZW9tZXRyeSkgJT4lIA0KICBzdF9zZiAlPiUNCiAgc3RfdHJhbnNmb3JtKHN0X2NycygyMjcyKSkNCg0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoc2YpDQpmaXNobmV0IDwtIA0KICBzdF9tYWtlX2dyaWQoUGhpbGx5Ym91bmRhcnksIGNlbGxzaXplID0gODAwKSAlPiUgIzQzMjYgYmVmb3JlIG1hcHBpbmcNCiAgc3Rfc2YoKSAlPiUNCiAgbXV0YXRlKHVuaXF1ZUlEID0gcm93bmFtZXMoLikpDQoNCmRhdF9zZiA8LSBkYXRfc2YgJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnMoZmlzaG5ldCkpDQpic19zdGF0aW9uIDwtIGJzX3N0YXRpb24gJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnMoZmlzaG5ldCkpDQpgYGANCg0KYGBge3IgdmFyZmlzaG5ldCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZyA9IEZBTFNFLCBlY2hvPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCg0KY29sbGVnZSA8LSBzdF9yZWFkKCJFOi9VcGVubi9DUExONTA4L2Jpa2VzaGFyZV9uZXcvRGF0YS9Vbml2ZXJzaXRpZXNfQ29sbGVnZXMuZ2VvanNvbiIpICU+JQ0KICAgIHN0X3RyYW5zZm9ybShzdF9jcnMoZmlzaG5ldCkpICU+JQ0KICAgIG11dGF0ZShMZWdlbmQgPSAiY29sbGVnZSIpIA0KDQpuYXRpb25hbF9wYXJrIDwtIHN0X3JlYWQoIkU6L1VwZW5uL0NQTE41MDgvYmlrZXNoYXJlX25ldy9EYXRhL3BoaWxseV9uYXRpb25hbF9wYXJrX3BvbHkuZ2VvanNvbiIpICU+JQ0KICAgIHN0X3RyYW5zZm9ybShzdF9jcnMoZmlzaG5ldCkpICU+JQ0KICAgIG11dGF0ZShMZWdlbmQgPSAibmF0aW9uYWxfcGFyayIpDQoNCg0KdG91cmlzbSA8LSBzdF9yZWFkKCJFOi9VcGVubi9DUExONTA4L2Jpa2VzaGFyZV9uZXcvRGF0YS9waGlsbHlfdG91cmlzbS5nZW9qc29uIikgJT4lDQogICAgc3RfdHJhbnNmb3JtKHN0X2NycyhmaXNobmV0KSkgJT4lDQogICAgbXV0YXRlKExlZ2VuZCA9ICJ0b3VyaXNtIikgDQoNCnNob3AgPC0gc3RfcmVhZCgiRTovVXBlbm4vQ1BMTjUwOC9iaWtlc2hhcmVfbmV3L0RhdGEvcGhpbGx5X3Nob3AuZ2VvanNvbiIpICU+JQ0KICAgIHN0X3RyYW5zZm9ybShzdF9jcnMoZmlzaG5ldCkpICU+JQ0KICAgIG11dGF0ZShMZWdlbmQgPSAic2hvcCIpDQoNCmN1aXNpbmUgPC0gc3RfcmVhZCgiRTovVXBlbm4vQ1BMTjUwOC9iaWtlc2hhcmVfbmV3L0RhdGEvcGhpbGx5X2N1aXNpbmUuZ2VvanNvbiIpICU+JQ0KICAgIHN0X3RyYW5zZm9ybShzdF9jcnMoZmlzaG5ldCkpICU+JQ0KICAgIG11dGF0ZShMZWdlbmQgPSAiY3Vpc2luZSIpDQoNCm9mZmljZSA8LSBzdF9yZWFkKCJFOi9VcGVubi9DUExONTA4L2Jpa2VzaGFyZV9uZXcvRGF0YS9waGlsbHlfb2ZmaWNlLmdlb2pzb24iKSAlPiUNCiAgICBzdF90cmFuc2Zvcm0oc3RfY3JzKGZpc2huZXQpKSAlPiUNCiAgICBtdXRhdGUoTGVnZW5kID0gIm9mZmljZSIpDQoNCmJ1c19zdGF0aW9uIDwtIHN0X3JlYWQoImh0dHBzOi8vb3BlbmRhdGEuYXJjZ2lzLmNvbS9kYXRhc2V0cy9lMDllOWY5OGJkZjA0ZWFkYTIxNGQyMjE3ZjNhZGJmMV8wLmdlb2pzb24iKSAlPiUNCiAgICBzdF90cmFuc2Zvcm0oc3RfY3JzKGZpc2huZXQpKSAlPiUNCiAgICBtdXRhdGUoTGVnZW5kID0gImJ1c19zdGF0aW9uIikgICAjZGlzdGluY3QNCiAgDQp0cm9sbHlfc3RvcHMgPC0gc3RfcmVhZCgiaHR0cHM6Ly9vcGVuZGF0YS5hcmNnaXMuY29tL2RhdGFzZXRzLzhhZWU0ZWE5OWQ1NjRlNTBiOTg2ZTk5YTQ2Njk0MThhXzAuZ2VvanNvbiIpICU+JQ0KICAgIHN0X3RyYW5zZm9ybShzdF9jcnMoZmlzaG5ldCkpICU+JQ0KICAgIG11dGF0ZShMZWdlbmQgPSAidHJvbGx5X3N0b3BzIikNCg0KcmFpbF9zdGF0aW9uIDwtIHN0X3JlYWQoImh0dHBzOi8vb3BlbmRhdGEuYXJjZ2lzLmNvbS9kYXRhc2V0cy82NGVhYTQ1MzljZjQ0MjkwOTVjMmM3YmYyNWM2MjlhMl8wLmdlb2pzb24iKSAlPiUNCiAgICBzdF90cmFuc2Zvcm0oc3RfY3JzKGZpc2huZXQpKSAlPiUNCiAgICBtdXRhdGUoTGVnZW5kID0gInJhaWxfc3RhdGlvbiIpDQoNCnNlcHRhU3RvcHMgPC0gDQogIHJiaW5kKA0KICAgIHN0X3JlYWQoImh0dHBzOi8vb3BlbmRhdGEuYXJjZ2lzLmNvbS9kYXRhc2V0cy84YzZlMjU3NWM4YWQ0NmViODg3ZTZiYjM1ODI1ZTFhNl8wLmdlb2pzb24iKSAlPiUgDQogICAgbXV0YXRlKExpbmUgPSAiRWwiKSAlPiUNCiAgICBkcGx5cjo6c2VsZWN0KFN0YXRpb24sIExpbmUpLA0KICAgICAgc3RfcmVhZCgiaHR0cHM6Ly9vcGVuZGF0YS5hcmNnaXMuY29tL2RhdGFzZXRzLzJlOTAzN2ZkNWJlZjQwNjQ4OGZmZTViYjY3ZDIxMzEyXzAuZ2VvanNvbiIpICU+JQ0KICAgICAgbXV0YXRlKExpbmUgPSJCcm9hZF9TdCIpICU+JQ0KICAgICAgZHBseXI6OnNlbGVjdChTdGF0aW9uLCBMaW5lKSkgJT4lDQogICAgc3RfdHJhbnNmb3JtKHN0X2NycyhmaXNobmV0KSkgJT4lDQogICAgbXV0YXRlKExlZ2VuZCA9ICJzZXB0YVN0b3BzIikNCg0Kc3RfYyA8LSBzdF9jb29yZGluYXRlcw0Kc3RfY29pZCA8LSBzdF9jZW50cm9pZA0KDQp2YXJzX25ldCA8LQ0KICBic19zdGF0aW9uICU+JQ0KICAgIG11dGF0ZSgNCiAgICAgIGNvbGxlZ2Uubm4gPQ0KICAgICAgICBubl9mdW5jdGlvbihzdF9jKGJzX3N0YXRpb24pLCBzdF9jKHN0X2NvaWQoY29sbGVnZSkpLDMpLA0KICAgICAgbmF0aW9uYWxfcGFyay5ubiA9DQogICAgICAgIG5uX2Z1bmN0aW9uKHN0X2MoYnNfc3RhdGlvbiksIHN0X2Moc3RfY29pZChuYXRpb25hbF9wYXJrKSksMyksDQogICAgICB0b3VyaXNtLm5uID0NCiAgICAgICAgbm5fZnVuY3Rpb24oc3RfYyhic19zdGF0aW9uKSwgc3RfYyh0b3VyaXNtKSwzKSwNCiAgICAgIHNob3Aubm4gPQ0KICAgICAgICBubl9mdW5jdGlvbihzdF9jKGJzX3N0YXRpb24pLCBzdF9jKHNob3ApLDMpLA0KICAgICAgY3Vpc2luZS5ubiA9DQogICAgICAgIG5uX2Z1bmN0aW9uKHN0X2MoYnNfc3RhdGlvbiksIHN0X2MoY3Vpc2luZSksMyksDQogICAgICBvZmZpY2Uubm4gPQ0KICAgICAgICBubl9mdW5jdGlvbihzdF9jKGJzX3N0YXRpb24pLCBzdF9jKG9mZmljZSksMyksDQogICAgICBidXNfc3RhdGlvbi5ubiA9DQogICAgICAgIG5uX2Z1bmN0aW9uKHN0X2MoYnNfc3RhdGlvbiksIHN0X2MoYnVzX3N0YXRpb24pLDEpLA0KICAgICAgdHJvbGx5X3N0b3BzLm5uID0NCiAgICAgICAgbm5fZnVuY3Rpb24oc3RfYyhic19zdGF0aW9uKSwgc3RfYyh0cm9sbHlfc3RvcHMpLDEpLA0KICAgICAgcmFpbF9zdGF0aW9uLm5uID0NCiAgICAgICAgbm5fZnVuY3Rpb24oc3RfYyhic19zdGF0aW9uKSwgc3RfYyhyYWlsX3N0YXRpb24pLDEpLA0KICAgICAgc2VwdGFTdG9wcy5ubiA9DQogICAgICAgIG5uX2Z1bmN0aW9uKHN0X2MoYnNfc3RhdGlvbiksIHN0X2Moc2VwdGFTdG9wcyksMSkNCiAgICAgICkNCg0KDQp2YXJzX25ldCA8LSANCiAgc3Rfam9pbih2YXJzX25ldCwgZmlzaG5ldCwgam9pbj1zdF93aXRoaW4pDQoNClBoaWxseUNlbnN1cyA8LSBQaGlsbHlDZW5zdXMgJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnMoZmlzaG5ldCkpDQp2YXJzX25ldCA8LSB2YXJzX25ldCAlPiUNCiAgc3Rfam9pbiguLCBQaGlsbHlDZW5zdXMpDQoNCg0KbmVpZ2hib3Job29kcyA8LSANCiAgc3RfcmVhZCgiRTovVXBlbm4vQ1BMTjUwOC9iaWtlc2hhcmVfbmV3L0RhdGEvTmVpZ2hib3Job29kc19QaGlsYWRlbHBoaWEuZ2VvanNvbiIpICU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKGZpc2huZXQpKSANCmBgYA0KDQpgYGB7ciByaWRlZmlzaG5ldCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZyA9IEZBTFNFfQ0KbGlicmFyeShzZikNCmxpYnJhcnkoZHBseXIpDQpkYXRfbmV0IDwtIA0KICBkcGx5cjo6c2VsZWN0KGRhdF9zZiU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKGZpc2huZXQpKSkgJT4lIA0KICBtdXRhdGUoY291bnRSSURFID0gMSkgJT4lIA0KICBhZ2dyZWdhdGUoLiwgZmlzaG5ldCwgc3VtKSAlPiUNCiAgbXV0YXRlKGNvdW50UklERSA9IHJlcGxhY2VfbmEoY291bnRSSURFLCAwKSwNCiAgICAgICAgIHVuaXF1ZUlEID0gcm93bmFtZXMoLiksDQogICAgICAgICBjdklEID0gc2FtcGxlKHJvdW5kKG5yb3coZmlzaG5ldCkgLyAyNCksIHNpemU9bnJvdyhmaXNobmV0KSwgcmVwbGFjZSA9IFRSVUUpKSAlPiUNCiAgc3Rfam9pbiguLCBuZWlnaGJvcmhvb2RzKSAlPiUNCiAgbXV0YXRlKG5ibmFtZT1pZmVsc2UoIWlzLm5hKG5hbWUpLG5hbWUsInVua25vd24iKSkNCg0KZmluYWxfbmV0IDwtDQogIGxlZnRfam9pbihkYXRfbmV0LCBzdF9kcm9wX2dlb21ldHJ5KHZhcnNfbmV0KSwgYnk9InVuaXF1ZUlEIikgDQoNCmZpbmFsX25ldF93ZWF0aGVyIDwtIGxlZnRfam9pbihmaW5hbF9uZXQsIHN0X2Ryb3BfZ2VvbWV0cnkodmFyc19uZXQpLCBieT0idW5pcXVlSUQiKSANCg0KIyNQcmVwYXJlIGZvciBtb3JhbidzDQpsaWJyYXJ5KHNwZGVwKQ0KZmluYWxfbmV0Lm5iIDwtIHBvbHkybmIoYXNfU3BhdGlhbChmaW5hbF9uZXQpLCBxdWVlbj1UUlVFKQ0KZmluYWxfbmV0LndlaWdodHMgPC0gbmIybGlzdHcoZmluYWxfbmV0Lm5iLCBzdHlsZT0iVyIsIHplcm8ucG9saWN5PVRSVUUpDQoNCg0KZmluYWxfbmV0LmxvY2FsTW9yYW5zIDwtIA0KICBjYmluZCgNCiAgICBhcy5kYXRhLmZyYW1lKGxvY2FsbW9yYW4oZmluYWxfbmV0JGNvdW50UklERSwgZmluYWxfbmV0LndlaWdodHMpKSwNCiAgICBhcy5kYXRhLmZyYW1lKGZpbmFsX25ldCkpICU+JSANCiAgICBzdF9zZigpICU+JQ0KICAgICAgZHBseXI6OnNlbGVjdChSSURFX0NvdW50ID0gY291bnRSSURFLCANCiAgICAgICAgICAgICAgICAgICAgTG9jYWxfTW9yYW5zX0kgPSBJaSwgDQogICAgICAgICAgICAgICAgICAgIFBfVmFsdWUgPSBgUHIoeiA+IDApYCkgJT4lDQogICAgICBtdXRhdGUoU2lnbmlmaWNhbnRfSG90c3BvdHMgPSBpZmVsc2UoUF9WYWx1ZSA8PSAwLjAwMDAwMDEsIDEsIDApKSAlPiUNCiAgICAgIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1nZW9tZXRyeSkNCg0KIyNkaXN0YW5jZSB0byBob3RzcG90cyMjIA0KZmluYWxfbmV0IDwtDQogIGZpbmFsX25ldCAlPiUgDQogIG11dGF0ZShSSURFLmlzU2lnID0gDQogICAgICAgICAgIGlmZWxzZShsb2NhbG1vcmFuKGZpbmFsX25ldCRjb3VudFJJREUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaW5hbF9uZXQud2VpZ2h0cylbLDVdIDw9IDAuMDUsIDEsIDApKSAlPiUNCiAgbXV0YXRlKFJJREUuaXNTaWcuZGlzdCA9IA0KICAgICAgICAgICBubl9mdW5jdGlvbihzdF9jb29yZGluYXRlcyhzdF9jZW50cm9pZChmaW5hbF9uZXQpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgc3RfY29vcmRpbmF0ZXMoc3RfY2VudHJvaWQoDQogICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKGZpbmFsX25ldCwgUklERS5pc1NpZyA9PSAxKSkpLCAxKSkNCmBgYA0KDQpgYGB7ciBhZGRfY2Vuc3VzX3RyYWN0cywgY2FjaGUgPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0NCmRhdF9jZW5zdXMgPC0gc3Rfam9pbihkYXRfeW1kICU+JSANCiAgICAgICAgICBmaWx0ZXIoaXMubmEoc3RhcnRfbG9uKSA9PSBGQUxTRSAmDQogICAgICAgICAgICAgICAgICAgaXMubmEoc3RhcnRfbGF0KSA9PSBGQUxTRSAmDQogICAgICAgICAgICAgICAgICAgaXMubmEoZW5kX2xhdCkgPT0gRkFMU0UgJg0KICAgICAgICAgICAgICAgICAgIGlzLm5hKGVuZF9sb24pID09IEZBTFNFKSAlPiUNCiAgICAgICAgICBzdF9hc19zZiguLCBjb29yZHMgPSBjKCJzdGFydF9sb24iLCAic3RhcnRfbGF0IiksIGNycyA9IDQzMjYpLA0KICAgICAgICBQaGlsbHlUcmFjdHMgJT4lDQogICAgICAgICAgc3RfdHJhbnNmb3JtKGNycz00MzI2KSwNCiAgICAgICAgam9pbj1zdF9pbnRlcnNlY3RzLA0KICAgICAgICAgICAgICBsZWZ0ID0gVFJVRSkgJT4lDQogIHJlbmFtZShPcmlnaW4uVHJhY3QgPSBHRU9JRCkgJT4lDQogIG11dGF0ZShzdGFydF9sb24gPSB1bmxpc3QobWFwKGdlb21ldHJ5LCAxKSksDQogICAgICAgICBzdGFydF9sYXQgPSB1bmxpc3QobWFwKGdlb21ldHJ5LCAyKSkpJT4lDQogIGFzLmRhdGEuZnJhbWUoKSAlPiUNCiAgZHBseXI6OnNlbGVjdCgtZ2VvbWV0cnkpJT4lDQogIHN0X2FzX3NmKC4sIGNvb3JkcyA9IGMoImVuZF9sb24iLCAiZW5kX2xhdCIpLCBjcnMgPSA0MzI2KSAlPiUNCiAgc3Rfam9pbiguLCBQaGlsbHlUcmFjdHMgJT4lDQogICAgICAgICAgICBzdF90cmFuc2Zvcm0oY3JzPTQzMjYpLA0KICAgICAgICAgIGpvaW49c3RfaW50ZXJzZWN0cywNCiAgICAgICAgICBsZWZ0ID0gVFJVRSkgJT4lDQogIHJlbmFtZShEZXN0aW5hdGlvbi5UcmFjdCA9IEdFT0lEKSAgJT4lDQogIG11dGF0ZShlbmRfbG9uID0gdW5saXN0KG1hcChnZW9tZXRyeSwgMSkpLA0KICAgICAgICAgZW5kX2xhdCA9IHVubGlzdChtYXAoZ2VvbWV0cnksIDIpKSklPiUNCiAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICBkcGx5cjo6c2VsZWN0KC1nZW9tZXRyeSkNCmBgYA0KDQpgYGB7ciBwYW5lbF9sZW5ndGhfY2hlY2ssIGNhY2hlID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9DQoNCnN0dWR5LnBhbmVsIDwtIA0KICBleHBhbmQuZ3JpZChpbnRlcnZhbDYwPXVuaXF1ZShkYXRfY2Vuc3VzJGludGVydmFsNjApLCANCiAgICAgICAgICAgICAgc3RhcnRfc3RhdGlvbiA9IHVuaXF1ZShkYXRfY2Vuc3VzJHN0YXJ0X3N0YXRpb24pKSAlPiUNCiAgbGVmdF9qb2luKC4sIGRhdF9jZW5zdXMgJT4lDQogICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3Qoc3RhcnRfc3RhdGlvbiwgT3JpZ2luLlRyYWN0LCBzdGFydF9sb24sIHN0YXJ0X2xhdCApJT4lICNhZGQgc3RhdGlvbiBuYW1lDQogICAgICAgICAgICAgIGRpc3RpbmN0KCkgJT4lDQogICAgICAgICAgICAgIGdyb3VwX2J5KHN0YXJ0X3N0YXRpb24pICU+JQ0KICAgICAgICAgICAgICBzbGljZSgxKSkNCg0KDQpzdHVkeS5wYW5lbCA8LSANCiAgZGF0X2NlbnN1cyAlPiUNCiAgbXV0YXRlKFRyaXBfQ291bnRlciA9IDEpICU+JQ0KICByaWdodF9qb2luKHN0dWR5LnBhbmVsKSAlPiUgDQogIGdyb3VwX2J5KGludGVydmFsNjAsIHN0YXJ0X3N0YXRpb24sIE9yaWdpbi5UcmFjdCwgc3RhcnRfbG9uLCBzdGFydF9sYXQpICU+JSAjYWRkIHN0YXRpb24gbmFtZQ0KICBzdW1tYXJpemUoVHJpcF9Db3VudCA9IHN1bShUcmlwX0NvdW50ZXIsIG5hLnJtPVQpKSAlPiUNCiAgbGVmdF9qb2luKHdlYXRoZXIuUGFuZWwpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIGZpbHRlcihpcy5uYShzdGFydF9zdGF0aW9uKSA9PSBGQUxTRSkgJT4lDQogIG11dGF0ZSh3ZWVrID0gd2VlayhpbnRlcnZhbDYwKSwNCiAgICAgICAgIGRvdHcgPSB3ZGF5KGludGVydmFsNjAsIGxhYmVsID0gVFJVRSkpICU+JQ0KICBmaWx0ZXIoaXMubmEoT3JpZ2luLlRyYWN0KSA9PSBGQUxTRSkNCg0KYGBgDQoNCmBgYHtyIHRpbWVfbGFncywgY2FjaGUgPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0NCnN0dWR5LnBhbmVsIDwtIA0KICBzdHVkeS5wYW5lbCAlPiUgDQogIGFycmFuZ2Uoc3RhcnRfc3RhdGlvbiwgaW50ZXJ2YWw2MCkgJT4lIA0KICBtdXRhdGUobGFnSG91ciA9IGRwbHlyOjpsYWcoVHJpcF9Db3VudCwxKSwNCiAgICAgICAgIGxhZzJIb3VycyA9IGRwbHlyOjpsYWcoVHJpcF9Db3VudCwyKSwNCiAgICAgICAgIGxhZzNIb3VycyA9IGRwbHlyOjpsYWcoVHJpcF9Db3VudCwzKSwNCiAgICAgICAgIGxhZzRIb3VycyA9IGRwbHlyOjpsYWcoVHJpcF9Db3VudCw0KSwNCiAgICAgICAgIGxhZzEySG91cnMgPSBkcGx5cjo6bGFnKFRyaXBfQ291bnQsMTIpLA0KICAgICAgICAgbGFnMWRheSA9IGRwbHlyOjpsYWcoVHJpcF9Db3VudCwyNCksDQogICAgICAgICBob2xpZGF5ID0gaWZlbHNlKHlkYXkoaW50ZXJ2YWw2MCkgPT0gYygzMTUsMzI2KSwxLDApKSAlPiUgIyM/Pw0KICAgbXV0YXRlKGRheSA9IHlkYXkoaW50ZXJ2YWw2MCkpICU+JQ0KICAgbXV0YXRlKGhvbGlkYXlMYWcgPSBjYXNlX3doZW4oZHBseXI6OmxhZyhob2xpZGF5LCAxKSA9PSAxIH4gIlBsdXNPbmVEYXkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OmxhZyhob2xpZGF5LCAyKSA9PSAxIH4gIlBsdXN0VHdvRGF5cyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6bGFnKGhvbGlkYXksIDMpID09IDEgfiAiUGx1c3RUaHJlZURheXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OmxlYWQoaG9saWRheSwgMSkgPT0gMSB+ICJNaW51c09uZURheSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6bGVhZChob2xpZGF5LCAyKSA9PSAxIH4gIk1pbnVzVHdvRGF5cyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6bGVhZChob2xpZGF5LCAzKSA9PSAxIH4gIk1pbnVzVGhyZWVEYXlzIiksDQogICAgICAgICBob2xpZGF5TGFnID0gcmVwbGFjZV9uYShob2xpZGF5TGFnLCAwKSkNCg0KYGBgDQoNCmBgYHtyIGV2YWx1YXRlX2xhZ3MsIGNhY2hlID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQphcy5kYXRhLmZyYW1lKHN0dWR5LnBhbmVsKSAlPiUNCiAgICBncm91cF9ieShpbnRlcnZhbDYwKSAlPiUgDQogICAgc3VtbWFyaXNlX2F0KHZhcnMoc3RhcnRzX3dpdGgoImxhZyIpLCAiVHJpcF9Db3VudCIpLCBtZWFuLCBuYS5ybSA9IFRSVUUpICU+JQ0KICAgIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1pbnRlcnZhbDYwLCAtVHJpcF9Db3VudCkgJT4lDQogICAgbXV0YXRlKFZhcmlhYmxlID0gZmFjdG9yKFZhcmlhYmxlLCBsZXZlbHM9YygibGFnSG91ciIsImxhZzJIb3VycyIsImxhZzNIb3VycyIsImxhZzRIb3VycyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGFnMTJIb3VycyIsImxhZzFkYXkiKSkpJT4lDQogICAgZ3JvdXBfYnkoVmFyaWFibGUpICU+JSAgDQogICAgc3VtbWFyaXplKGNvcnJlbGF0aW9uID0gcm91bmQoY29yKFZhbHVlLCBUcmlwX0NvdW50KSwyKSkgJT4lDQogICAga2FibGUgKGNhcHRpb249IlRhYmxlIDE6IFRpbWUgbGFnIGZlYXR1cmVzIGFuZCBjb3JlbGF0aW9uIiwgY29sLm5hbWVzID0gYygnVGltZSBsYWcnLCAnQ29yZWxhdGlvbicpKSAlPiUNCiAgICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KDQojIEV4cGxvcmFyeSBhbmFseXNpcw0KDQpNb3JlIGZyZXF1ZW50IHJlLWJhbGFuY2luZyBpcyByZXF1aXJlZCBpbiBwbGFjZXMgYW5kIGF0IHRpbWVzIHRoYXQgaGF2ZSB0aGUgaGlnaGVzdCBiaWtlLXNoYXJlIHJpZGVyc2hpcCBkZW1hbmRzLiANCg0KQWNjb3JkaW5nIHRvIEZpZ3VyZSA0LjEsIHRoZXJlIGlzIGEgc2ltaWxhciB3ZWVrbHkgdHJlbmQgaW4gYmlrZS1zaGFyZSBkYXRhLiBXZWVrZGF5cyB0ZW5kIHRvIGhhdmUgaGlnaGVyIHJpZGVyc2hpcCB0aGFuIHdlZWtlbmRzLiBUaGVyZSBpcyBhIG1ham9yIGRlY3JlYXNlIGluIHJpZGVyc2hpcCBhcm91bmQgVGhhbmtzZ2l2aW5nIGFuZCBhIGRlY2xpbmUgYXJvdW5kIHRoZSBWZXRlcmFuJ3MgZGF5LCB0b28uIA0KDQpgYGB7ciB0cmlwX3RpbWVzZXJpZXMsIGNhY2hlID0gVFJVRX0NCmdncGxvdChkYXRfeW1kICU+JQ0KICAgICAgICAgZ3JvdXBfYnkoaW50ZXJ2YWw2MCkgJT4lDQogICAgICAgICB0YWxseSgpKSsNCiAgZ2VvbV9saW5lKGFlcyh4ID0gaW50ZXJ2YWw2MCwgeSA9IG4pKSsNCiAgbGFicyh0aXRsZT0iRmlndXJlIDQuMTogQmlrZSBzaGFyZSB0cmlwcyBwZXIgaHIuIFBoaWxhZGVscGhpYSwgTm92LCAyMDE4IiwNCiAgICAgICB4PSJEYXRlIiwgDQogICAgICAgeT0iTnVtYmVyIG9mIHRyaXBzIikrDQogIHBsb3RUaGVtZQ0KYGBgDQoNCkZpZ3VyZSA0LjIgcmV2ZWFscyB0aGF0IHBlYWsgaG91cnMgaGF2ZSB0aGUgaGlnaGVzdCBkZW1hbmQgYW5kIG5lZWQgbW9yZSBmcmVxdWVudCByZS1iYWxhbmNpbmcuIA0KDQpgYGB7ciBtZWFuX3RyaXBzX2hpc3QsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBjYWNoZSA9IFRSVUV9DQpkYXRfeW1kICU+JQ0KICAgICAgICBtdXRhdGUodGltZV9vZl9kYXkgPSBjYXNlX3doZW4oaG91cihpbnRlcnZhbDYwKSA8IDcgfCBob3VyKGludGVydmFsNjApID4gMTggfiAiT3Zlcm5pZ2h0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIoaW50ZXJ2YWw2MCkgPj0gNyAmIGhvdXIoaW50ZXJ2YWw2MCkgPCAxMCB+ICJBTSBSdXNoIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIoaW50ZXJ2YWw2MCkgPj0gMTAgJiBob3VyKGludGVydmFsNjApIDwgMTUgfiAiTWlkLURheSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3VyKGludGVydmFsNjApID49IDE1ICYgaG91cihpbnRlcnZhbDYwKSA8PSAxOCB+ICJQTSBSdXNoIikpJT4lDQogICAgICAgICBncm91cF9ieShpbnRlcnZhbDYwLCBzdGFydF9zdGF0aW9uLCB0aW1lX29mX2RheSkgJT4lDQogICAgICAgICB0YWxseSgpJT4lDQogIGdyb3VwX2J5KHN0YXJ0X3N0YXRpb24sIHRpbWVfb2ZfZGF5KSU+JQ0KICBzdW1tYXJpemUobWVhbl90cmlwcyA9IG1lYW4obikpJT4lDQogIGdncGxvdCgpKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMobWVhbl90cmlwcyksIGJpbndpZHRoID0gMSkrDQogIGxhYnModGl0bGU9IkZpZ3VyZSA0LjI6IE1lYW4gTnVtYmVyIG9mIEhvdXJseSBUcmlwcyBQZXIgU3RhdGlvbi4gUGhpbGFkZWxwaGlhLCBOb3YsIDIwMTgiLA0KICAgICAgIHg9Ik51bWJlciBvZiB0cmlwcyIsIA0KICAgICAgIHk9IkZyZXF1ZW5jeSIpKw0KICBmYWNldF93cmFwKH50aW1lX29mX2RheSkrDQogIHBsb3RUaGVtZQ0KYGBgDQoNCkZpZ3VyZSA0LjMgYW5kIDQuNCBpbmRpY2F0ZSB0aGF0IG9uIGF2ZXJhZ2UgV2VkbmVzZGF5cyBoYXZlIHRoZSBoaWdoZXN0IHBlYWsgZGVtYW5kcywgYW5kIEZyaWRheXMgaGF2ZSB0aGUgbG93ZXN0IGRlbWFuZHMgZm9yIHdlZWtkYXlzLiBXZWVrZW5kcyBoYXZlIGxvd2VyIGFuZCBtb3JlIGV2ZW5seSBkaXN0cmlidXRlZCBkZW1hbmRzLCBhbmQgcGVha3Mgb24gU3VuZGF5cyBhcmUgdHlwaWNhbGx5IGFyb3VuZCAxIGhvdXIgbGF0ZXIgdGhhbiB0aGUgcGVha3Mgb24gU2F0dXJkYXlzLiANCg0KYGBge3IgdHJpcHNfaG91cl9kb3R3LCBjYWNoZSA9IFRSVUV9DQpnZ3Bsb3QoZGF0X3ltZCAlPiUgbXV0YXRlKGhvdXIgPSBob3VyKHN0YXJ0X3RpbWUpKSkrDQogICAgIGdlb21fZnJlcXBvbHkoYWVzKGhvdXIsIGNvbG9yID0gZG90dyksIGJpbndpZHRoID0gMSkrDQogIGxhYnModGl0bGU9IkZpZ3VyZSA0LjM6IEJpa2Ugc2hhcmUgdHJpcHMgaW4gUGhpbGFkZWxwaGlhLCBieSBkYXkgb2YgdGhlIHdlZWssIE5vdiwgMjAxOCIsDQogICAgICAgeD0iSG91ciIsIA0KICAgICAgIHk9IlRyaXAgQ291bnRzIikrDQogICAgIHBsb3RUaGVtZQ0KDQoNCmdncGxvdChkYXRfeW1kICU+JSANCiAgICAgICAgIG11dGF0ZShob3VyID0gaG91cihzdGFydF90aW1lKSwNCiAgICAgICAgICAgICAgICB3ZWVrZW5kID0gaWZlbHNlKGRvdHcgJWluJSBjKCJTdW4iLCAiU2F0IiksICJXZWVrZW5kIiwgIldlZWtkYXkiKSkpKw0KICAgICBnZW9tX2ZyZXFwb2x5KGFlcyhob3VyLCBjb2xvciA9IHdlZWtlbmQpLCBiaW53aWR0aCA9IDEpKw0KICBsYWJzKHRpdGxlPSJGaWd1cmUgNC40OiBCaWtlIHNoYXJlIHRyaXBzIGluIFBoaWxhZGVscGhpYSAtIHdlZWtlbmQgdnMgd2Vla2RheSwgTm92LCAyMDE4IiwNCiAgICAgICB4PSJIb3VyIiwgDQogICAgICAgeT0iVHJpcCBDb3VudHMiKSsNCiAgICAgcGxvdFRoZW1lDQpgYGANCg0KRmlndXJlIDQuNSBzaG93cyB0aGF0IDEpIFdlZWtkYXlzIHRlbmQgdG8gaGF2ZSBoaWdoZXIgZGVtYW5kcyB0aGFuIHdlZWtlbmRzOyAyKSBPbiB3ZWVrZGF5cyBpbiBQTSBydXNoIGhvdXJzLCB0aGVyZSBhcmUgZGlzdGluY3RseSBjbHVzdGVyaW5nIGhpZ2ggZGVtYW5kcyBpbiBjZW50ZXIgY2l0eSBhbmQgdGhlIGJyaWRnZSBsaW5raW5nIGNlbnRlciBjaXR5IGFuZCBlYXN0ZXJuIGZyaW5nZSBvZiB3ZXN0IFBoaWxseS4gTWlkLWRheXMgYW5kIG92ZXJuaWdodCBob3VycyBoYXZlIHZlcnkgc2ltaWxhciBzcGF0aWFsIGRpc3RyaWJ1dGlvbiBvZiBkZW1hbmRzLCB3aXRoIG5lYXJseSBhbGwgaG90IHNwb3RzIGluIGNlbnRlciBjaXR5LiBDb21wYXJlZCB3aXRoIG90aGVyIHRpbWVzIG9uIHdlZWtkYXlzLCBBTSBydXNoIGhvdXJzIGhhdmUgaGlnaCBkZW1hbmRzIGV4dGVuZGluZyBpbnRvIHRoZSBzb3V0aCBvZiBjZW50ZXIgY2l0eTsgMykgT24gd2Vla2VuZHMsIEFNIHJ1c2ggaG91cnMgaGF2ZSB0aGUgbG93ZXN0IG92ZXJhbGwgZGVtYW5kLiBIb3Qgc3BvdHMgYXBwZWFyIG1vc3RseSBvbiBtaWQtZGF5cyBhbmQgUE0gcnVzaCBob3VycyBpbiBjZW50ZXIgY2l0eS4gRmlndXJlIDQuNiBpcyBhbiBhbmltYXRpb24gb2YgYSB0eXBpY2FsIE1vbmRheSByaWRlcnNoaXAgaW4gZWFybHkgTm92ZW1iZXIgaW4gUGhpbGFkZWxwaGlhLiANCg0KYGBge3Igb3JpZ2luX21hcCwgY2FjaGUgPSBUUlVFLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD03fQ0KDQpnZ3Bsb3QoKSsNCiAgZ2VvbV9zZihkYXRhID0gUGhpbGx5VHJhY3RzICU+JQ0KICAgICAgICAgIHN0X3RyYW5zZm9ybShjcnM9NDMyNiksIGZpbGwgPSAid2hpdGUiKSsNCiAgZ2VvbV9wb2ludChkYXRhID0gZGF0X2NlbnN1cyAlPiUgDQogICAgICAgICAgICBtdXRhdGUoaG91ciA9IGhvdXIoc3RhcnRfdGltZSksDQogICAgICAgICAgICAgICAgd2Vla2VuZCA9IGlmZWxzZShkb3R3ICVpbiUgYygiU3VuIiwgIlNhdCIpLCAiV2Vla2VuZCIsICJXZWVrZGF5IiksDQogICAgICAgICAgICAgICAgdGltZV9vZl9kYXkgPSBjYXNlX3doZW4oaG91cihpbnRlcnZhbDYwKSA8IDcgfCBob3VyKGludGVydmFsNjApID4gMTggfiAiT3Zlcm5pZ2h0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIoaW50ZXJ2YWw2MCkgPj0gNyAmIGhvdXIoaW50ZXJ2YWw2MCkgPCAxMCB+ICJBTSBSdXNoIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIoaW50ZXJ2YWw2MCkgPj0gMTAgJiBob3VyKGludGVydmFsNjApIDwgMTUgfiAiTWlkLURheSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3VyKGludGVydmFsNjApID49IDE1ICYgaG91cihpbnRlcnZhbDYwKSA8PSAxOCB+ICJQTSBSdXNoIikpJT4lDQogICAgICAgICAgICAgIGdyb3VwX2J5KHN0YXJ0X3N0YXRpb24sIHN0YXJ0X2xhdCwgc3RhcnRfbG9uLCB3ZWVrZW5kLCB0aW1lX29mX2RheSkgJT4lDQogICAgICAgICAgICAgIHRhbGx5KCksDQogICAgICAgICAgICBhZXMoeD1zdGFydF9sb24sIHkgPSBzdGFydF9sYXQsIGNvbG9yID0gbiksIA0KICAgICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGFscGhhID0gMC40LCBzaXplID0gMSkrDQogIHNjYWxlX2NvbG91cl92aXJpZGlzKGRpcmVjdGlvbiA9IC0xLA0KICBkaXNjcmV0ZSA9IEZBTFNFLCBvcHRpb24gPSAiRCIpKw0KICB5bGltKG1pbihkYXRfY2Vuc3VzJHN0YXJ0X2xhdCksIG1heChkYXRfY2Vuc3VzJHN0YXJ0X2xhdCkpKw0KICB4bGltKG1pbihkYXRfY2Vuc3VzJHN0YXJ0X2xvbiksIG1heChkYXRfY2Vuc3VzJHN0YXJ0X2xvbikpKw0KICBmYWNldF9ncmlkKHdlZWtlbmQgfiB0aW1lX29mX2RheSkrDQogIGxhYnModGl0bGU9IkZpZ3VyZSA0LjU6IEJpa2Ugc2hhcmUgdHJpcHMgcGVyIGhyIGJ5IHN0YXRpb24uIFBoaWxhZGVscGhpYSwgTm92LCAyMDE4IikrDQogIG1hcFRoZW1lDQoNCg0KYGBgDQoNCmBgYHtyIGFuaW1hdGUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBjYWNoZSA9IFRSVUUsICBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD03fQ0KDQpQaGlsbHlDZW5zdXMgPC0gUGhpbGx5Q2Vuc3VzICU+JQ0KICAgIHN0X3RyYW5zZm9ybShzdF9jcnMoZmlzaG5ldCkpDQpic190cmFjdCA8LSBzdF9qb2luKGRhdF9zZiwgUGhpbGx5Q2Vuc3VzLCBqb2luPXN0X3dpdGhpbikNCg0Kd2VlazQ0IDwtDQogIGZpbHRlcihic190cmFjdCAsIHdlZWsgPT0gNDQgJiBkb3R3ID09ICJNb24iKQ0KDQp3ZWVrNDQucGFuZWwgPC0NCiAgZXhwYW5kLmdyaWQoDQogICAgaW50ZXJ2YWwxNSA9IHVuaXF1ZSh3ZWVrNDQkaW50ZXJ2YWwxNSksDQogICAgUGlja3VwLkNlbnN1cy5UcmFjdCA9IHVuaXF1ZShic190cmFjdCRHRU9JRCkpDQoNCnJpZGUuYW5pbWF0aW9uLmRhdGEgPC0NCiAgbXV0YXRlKHdlZWs0NCwgVHJpcF9Db3VudGVyID0gMSkgJT4lDQogICAgcmlnaHRfam9pbih3ZWVrNDQucGFuZWwpICU+JSANCiAgICBncm91cF9ieShpbnRlcnZhbDE1LCBQaWNrdXAuQ2Vuc3VzLlRyYWN0KSAlPiUNCiAgICBzdW1tYXJpemUoVHJpcF9Db3VudCA9IHN1bShUcmlwX0NvdW50ZXIsIG5hLnJtPVQpKSAlPiUgDQogICAgdW5ncm91cCgpICU+JSANCiAgICBzdF9zZigpICU+JQ0KICAgIG11dGF0ZShUcmlwcyA9IGNhc2Vfd2hlbihUcmlwX0NvdW50ID09IDAgfiAiMCB0cmlwcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyaXBfQ291bnQgPiAwICYgVHJpcF9Db3VudCA8PSAzIH4gIjEtMyB0cmlwcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyaXBfQ291bnQgPiAzICYgVHJpcF9Db3VudCA8PSA2IH4gIjQtNiB0cmlwcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyaXBfQ291bnQgPiA2ICYgVHJpcF9Db3VudCA8PSAxMCB+ICI3LTEwIHRyaXBzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJpcF9Db3VudCA+IDEwIH4gIjExKyB0cmlwcyIpKSAlPiUNCiAgICBtdXRhdGUoVHJpcHMgID0gZmN0X3JlbGV2ZWwoVHJpcHMsICIwIHRyaXBzIiwiMS0zIHRyaXBzIiwiNC02IHRyaXBzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI3LTEwIHRyaXBzIiwiMTArIHRyaXBzIikpDQoNCiMgIGxlZnRfam9pbihkYXRfbmV0LCBzdF9kcm9wX2dlb21ldHJ5KHZhcnNfbmV0KSwgYnk9InVuaXF1ZUlEIikNCnJpZGVzaGFyZV9hbmltYXRpb24gPC0NCiAgZ2dwbG90KCkgKw0KICAgIGdlb21fc2YoZGF0YSA9IHJpZGUuYW5pbWF0aW9uLmRhdGEsIGFlcyhjb2wgPSBUcmlwcywgc2l6ZSA9IFRyaXBzKSwgc2hvdy5sZWdlbmQgPSAicG9pbnQiKSsNCiAgICBnZW9tX3NmKGRhdGEgPSBQaGlsbHlUcmFjdHMsIGNvbG9yID0gImdyZXkiLCBmaWxsID0gInRyYW5zcGFyZW50IikrDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiZ3JlZW4iLCAieWVsbG93IiwgIm9yYW5nZSIsInJlZCIpKSArDQogICAgbGFicyh0aXRsZSA9ICJGaWd1cmUgNC42OiBSaWRlc2hhcmUgcGlja3VwcyBmb3Igb25lIGRheSBpbiBOb3ZlbWJlciAyMDE4IiwNCiAgICAgICAgIHN1YnRpdGxlID0gIjE1IG1pbnV0ZSBpbnRlcnZhbHM6IHtjdXJyZW50X2ZyYW1lfSIpICsNCiAgICB0cmFuc2l0aW9uX21hbnVhbChpbnRlcnZhbDE1KSArDQogICAgbWFwVGhlbWUNCg0KbGlicmFyeShnZ2FuaW1hdGUpDQpsaWJyYXJ5KGdpZnNraSkNCmFuaW1hdGUocmlkZXNoYXJlX2FuaW1hdGlvbiwgZHVyYXRpb249MjAsIHJlbmRlcmVyID0gZ2lmc2tpX3JlbmRlcmVyKCkpDQoNCmFuaW1fc2F2ZSgicmlkZXNoYXJlX2xvY2FsIiwgcmlkZXNoYXJlX2FuaW1hdGlvbiwgZHVyYXRpb249MjAsIHJlbmRlcmVyID0gZ2lmc2tpX3JlbmRlcmVyKCkpDQoNCmBgYA0KDQoNCiMgQ29tcGFyaXNvbiBvZiBtb2RlbCBwZXJmb3JtYW5jZSANCg0KV2UgY2FuIGxlYXJuIGZyb20gRmlndXJlIDUuMSBhbmQgNS4yIHRoYXQgRFRpbWVfU3BhY2VfRkVfdGltZUxhZ3MgbW9kZWwgaGFzIHRoZSBsZWFzdCBvdmVyYWxsIGVycm9ycywgYW5kIGFkZGluZyBob2xpZGF5IGZlYXR1cmUgZG9lc24ndCBoYXZlIGEgc2lnbmlmaWNhbnQgYWNjdXJhY3kgaW1wcm92ZW1lbnQgdG8gdGhlIHRpbWUtbGFnIG1vZGVscy4gDQoNCmBgYHtyIHRyYWluX3Rlc3QsIGNhY2hlID0gVFJVRX0NCnJpZGUuVHJhaW4gPC0gZmlsdGVyKHN0dWR5LnBhbmVsLCB3ZWVrIDw9IDQ2KSANCnJpZGUuVGVzdCA8LSBmaWx0ZXIoc3R1ZHkucGFuZWwsIHdlZWsgPiA0NikNCmBgYA0KDQpgYGB7ciB0cmFpbl90ZXN0X25ldCwgY2FjaGUgPSBUUlVFfQ0Kc3R1ZHlfcGFuZWwubmV0IDwtIG1lcmdlKHg9c3R1ZHkucGFuZWwsIHk9c3RfZHJvcF9nZW9tZXRyeSh2YXJzX25ldCksIGJ5LnggPSAic3RhcnRfc3RhdGlvbiIsIGJ5LnkgPSAiaWQiLCBhbGwueCA9IFRSVUUpICU+JQ0KICAgIGZpbHRlcihpcy5uYSh1bmlxdWVJRCkgPT0gRkFMU0UpDQogIA0KcmlkZS5UcmFpbi5uZXQgPC0gZmlsdGVyKHN0dWR5X3BhbmVsLm5ldCwgd2VlayA8PSA0NikgDQpyaWRlLlRlc3QubmV0IDwtIGZpbHRlcihzdHVkeV9wYW5lbC5uZXQsIHdlZWsgPiA0NikNCmBgYA0KDQoNCmBgYHtyIGZpdmVfbW9kZWxzLCBjYWNoZSA9IFRSVUUsIHJlc3VsdHM9J2hpZGUnLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZWNobz1GQUxTRX0NCnJlZzEgPC0gDQogIGxtKFRyaXBfQ291bnQgfiAgaG91cihpbnRlcnZhbDYwKSArIGRvdHcgKyBUZW1wZXJhdHVyZSwgIGRhdGE9cmlkZS5UcmFpbikNCg0KcmVnMiA8LSANCiAgbG0oVHJpcF9Db3VudCB+ICBzdGFydF9zdGF0aW9uICsgZG90dyArIFRlbXBlcmF0dXJlLCAgZGF0YT1yaWRlLlRyYWluKQ0KDQpyZWczIDwtIA0KICBsbShUcmlwX0NvdW50IH4gIHN0YXJ0X3N0YXRpb24gKyBob3VyKGludGVydmFsNjApICsgZG90dyArIFRlbXBlcmF0dXJlICsgUHJlY2lwaXRhdGlvbiwgDQogICAgIGRhdGE9cmlkZS5UcmFpbikNCg0KcmVnNCA8LSANCiAgbG0oVHJpcF9Db3VudCB+ICBzdGFydF9zdGF0aW9uICsgIGhvdXIoaW50ZXJ2YWw2MCkgKyBkb3R3ICsgVGVtcGVyYXR1cmUgKyBQcmVjaXBpdGF0aW9uICsNCiAgICAgICAgICAgICAgICAgICBsYWdIb3VyICsgbGFnMkhvdXJzICtsYWczSG91cnMgKyBsYWcxMkhvdXJzICsgbGFnMWRheSwgDQogICAgIGRhdGE9cmlkZS5UcmFpbikNCg0KcmVnNSA8LSANCiAgbG0oVHJpcF9Db3VudCB+ICBzdGFydF9zdGF0aW9uICsgaG91cihpbnRlcnZhbDYwKSArIGRvdHcgKyBUZW1wZXJhdHVyZSArIFByZWNpcGl0YXRpb24gKw0KICAgICAgICAgICAgICAgICAgIGxhZ0hvdXIgKyBsYWcySG91cnMgK2xhZzNIb3VycyArbGFnMTJIb3VycyArIGxhZzFkYXkgKyBob2xpZGF5TGFnICsgaG9saWRheSwgDQogICAgIGRhdGE9cmlkZS5UcmFpbikNCg0KcmVnNiA8LSANCiAgbG0oVHJpcF9Db3VudCB+ICBzdGFydF9zdGF0aW9uICsgaG91cihpbnRlcnZhbDYwKSArIGRvdHcgKyBUZW1wZXJhdHVyZSArIFByZWNpcGl0YXRpb24gKw0KICAgICAgICAgICAgICAgICAgIGxhZ0hvdXIgKyBsYWcySG91cnMgK2xhZzNIb3VycyArbGFnMTJIb3VycyArIGxhZzFkYXkgKyBob2xpZGF5TGFnICsgY29sbGVnZS5ubiArIG5hdGlvbmFsX3Bhcmsubm4gKyB0b3VyaXNtLm5uICsgc2hvcC5ubiArIGN1aXNpbmUubm4gKyBvZmZpY2Uubm4gKyBidXNfc3RhdGlvbi5ubiArIHRyb2xseV9zdG9wcy5ubiArIHJhaWxfc3RhdGlvbi5ubiArIHNlcHRhU3RvcHMubm4gKyB1bmlxdWVJRCArIFRvdGFsX1BvcCArIE1lZF9JbmMgKyBXaGl0ZV9Qb3AgKyBUcmF2ZWxfVGltZSArIE1lYW5zX29mX1RyYW5zcG9ydCArIFRvdGFsX1B1YmxpY19UcmFucyArIE1lZF9BZ2UgKyBQZXJjZW50X1doaXRlICsgTWVhbl9Db21tdXRlX1RpbWUgKyBQZXJjZW50X1Rha2luZ19QdWJsaWNfVHJhbnMsIA0KICAgICBkYXRhPXJpZGUuVHJhaW4ubmV0KQ0KDQpyZWc3IDwtIA0KICBsbShUcmlwX0NvdW50IH4gIHN0YXJ0X3N0YXRpb24gKyAgaG91cihpbnRlcnZhbDYwKSArIGRvdHcgKyBUZW1wZXJhdHVyZSArIFByZWNpcGl0YXRpb24gKw0KICAgICAgICAgICAgICAgICAgIGxhZ0hvdXIgKyBsYWcySG91cnMgK2xhZzNIb3VycyArIGxhZzEySG91cnMgKyBsYWcxZGF5LCANCiAgICAgZGF0YT1yaWRlLlRyYWluLm5ldCkNCmBgYA0KDQpgYGB7ciBuZXN0X2RhdGEsIGNhY2hlID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQpyaWRlLlRlc3Qud2Vla05lc3QgPC0gDQogIHJpZGUuVGVzdCAlPiUNCiAgbmVzdCgtd2VlaykgDQoNCnJpZGUuVGVzdC53ZWVrTmVzdC5uZXQgPC0gDQogIHJpZGUuVGVzdC5uZXQgJT4lDQogIG5lc3QoLXdlZWspIA0KDQpgYGANCg0KYGBge3IgcHJlZGljdF9mdW5jdGlvbiwgY2FjaGUgPSBUUlVFfQ0KbW9kZWxfcHJlZCA8LSBmdW5jdGlvbihkYXQsIGZpdCl7DQogICBwcmVkIDwtIHByZWRpY3QoZml0LCBuZXdkYXRhID0gZGF0KX0NCmBgYA0KDQpgYGB7ciBkb19wcmVkaWNpdG9ucywgY2FjaGUgPSBUUlVFfQ0Kd2Vla19wcmVkaWN0aW9ucyA8LSANCiAgcmlkZS5UZXN0LndlZWtOZXN0ICU+JSANCiAgICBtdXRhdGUoQVRpbWVfRkUgPSBtYXAoLnggPSBkYXRhLCBmaXQgPSByZWcxLCAuZiA9IG1vZGVsX3ByZWQpLA0KICAgICAgICAgICBCU3BhY2VfRkUgPSBtYXAoLnggPSBkYXRhLCBmaXQgPSByZWcyLCAuZiA9IG1vZGVsX3ByZWQpLA0KICAgICAgICAgICBDVGltZV9TcGFjZV9GRSA9IG1hcCgueCA9IGRhdGEsIGZpdCA9IHJlZzMsIC5mID0gbW9kZWxfcHJlZCksDQogICAgICAgICAgIERUaW1lX1NwYWNlX0ZFX3RpbWVMYWdzID0gbWFwKC54ID0gZGF0YSwgZml0ID0gcmVnNCwgLmYgPSBtb2RlbF9wcmVkKSwNCiAgICAgICAgICAgRVRpbWVfU3BhY2VfRkVfdGltZUxhZ3NfaG9saWRheUxhZ3MgPSBtYXAoLnggPSBkYXRhLCBmaXQgPSByZWc1LCAuZiA9IG1vZGVsX3ByZWQpKSAlPiUgDQogICAgZ2F0aGVyKFJlZ3Jlc3Npb24sIFByZWRpY3Rpb24sIC1kYXRhLCAtd2VlaykgJT4lDQogICAgbXV0YXRlKE9ic2VydmVkID0gbWFwKGRhdGEsIHB1bGwsIFRyaXBfQ291bnQpLA0KICAgICAgICAgICBBYnNvbHV0ZV9FcnJvciA9IG1hcDIoT2JzZXJ2ZWQsIFByZWRpY3Rpb24sICB+IGFicygueCAtIC55KSksDQogICAgICAgICAgIE1BRSA9IG1hcF9kYmwoQWJzb2x1dGVfRXJyb3IsIG1lYW4sIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgIHNkX0FFID0gbWFwX2RibChBYnNvbHV0ZV9FcnJvciwgc2QsIG5hLnJtID0gVFJVRSkpDQoNCmBgYA0KDQpgYGB7ciBwbG90X2Vycm9yc19ieV9tb2RlbCwgY2FjaGUgPSBUUlVFfQ0Kd2Vla19wcmVkaWN0aW9ucyAlPiUNCiAgZHBseXI6OnNlbGVjdCh3ZWVrLCBSZWdyZXNzaW9uLCBNQUUpICU+JQ0KICBnYXRoZXIoVmFyaWFibGUsIE1BRSwgLVJlZ3Jlc3Npb24sIC13ZWVrKSAlPiUNCiAgZ2dwbG90KGFlcyh3ZWVrLCBNQUUpKSArIA0KICAgIGdlb21fYmFyKGFlcyhmaWxsID0gUmVncmVzc2lvbiksIHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUpICsNCiAgICBsYWJzKHRpdGxlID0gIkZpZ3VyZSA1LjE6IE1lYW4gQWJzb2x1dGUgRXJyb3JzIGJ5IG1vZGVsIHNwZWNpZmljYXRpb24gYW5kIHdlZWsiKSArDQogIHBsb3RUaGVtZQ0KYGBgDQoNCmBgYHtyIGVycm9yX3ZzX2FjdHVhbF90aW1lc2VyaWVzLCBjYWNoZSA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0Kd2Vla19wcmVkaWN0aW9ucyAlPiUgDQogICAgbXV0YXRlKGludGVydmFsNjAgPSBtYXAoZGF0YSwgcHVsbCwgaW50ZXJ2YWw2MCksDQogICAgICAgICAgIHN0YXJ0X3N0YXRpb24gPSBtYXAoZGF0YSwgcHVsbCwgc3RhcnRfc3RhdGlvbikpICU+JQ0KICAgIGRwbHlyOjpzZWxlY3QoaW50ZXJ2YWw2MCwgc3RhcnRfc3RhdGlvbiwgT2JzZXJ2ZWQsIFByZWRpY3Rpb24sIFJlZ3Jlc3Npb24pICU+JQ0KICAgIHVubmVzdCgpICU+JQ0KICAgIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1SZWdyZXNzaW9uLCAtaW50ZXJ2YWw2MCwgLXN0YXJ0X3N0YXRpb24pICU+JQ0KICAgIGdyb3VwX2J5KFJlZ3Jlc3Npb24sIFZhcmlhYmxlLCBpbnRlcnZhbDYwKSAlPiUNCiAgICBzdW1tYXJpemUoVmFsdWUgPSBzdW0oVmFsdWUpKSAlPiUNCiAgICBnZ3Bsb3QoYWVzKGludGVydmFsNjAsIFZhbHVlLCBjb2xvdXI9VmFyaWFibGUpKSArIA0KICAgICAgZ2VvbV9saW5lKHNpemUgPSAxLjEpICsgDQogICAgICBmYWNldF93cmFwKH5SZWdyZXNzaW9uLCBuY29sPTEpICsNCiAgICAgIGxhYnModGl0bGUgPSAiRmlndXJlIDUuMjogUHJlZGljdGVkL09ic2VydmVkIGJpa2Ugc2hhcmUgdGltZSBzZXJpZXMiLCBzdWJ0aXRsZSA9ICJQaGlsYWRlbHBoaWE7IEEgdGVzdCBzZXQgb2YgMiB3ZWVrcyIsICB4ID0gIkhvdXIiLCB5PSAiU3RhdGlvbiBUcmlwcyIpICsNCiAgICAgIHBsb3RUaGVtZQ0KYGBgDQoNCg0KYGBge3IgZXJyb3JzX2J5X3N0YXRpb24sIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBjYWNoZSA9IFRSVUV9DQp3ZWVrX3ByZWRpY3Rpb25zICU+JSANCiAgICBtdXRhdGUoaW50ZXJ2YWw2MCA9IG1hcChkYXRhLCBwdWxsLCBpbnRlcnZhbDYwKSwNCiAgICAgICAgICAgc3RhcnRfc3RhdGlvbiA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9zdGF0aW9uKSwgDQogICAgICAgICAgIHN0YXJ0X2xhdCA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9sYXQpLCANCiAgICAgICAgICAgc3RhcnRfbG9uID0gbWFwKGRhdGEsIHB1bGwsIHN0YXJ0X2xvbikpICU+JQ0KICAgIGRwbHlyOjpzZWxlY3QoaW50ZXJ2YWw2MCwgc3RhcnRfc3RhdGlvbiwgc3RhcnRfbG9uLCBzdGFydF9sYXQsIE9ic2VydmVkLCBQcmVkaWN0aW9uLCBSZWdyZXNzaW9uKSAlPiUNCiAgICB1bm5lc3QoKSAlPiUNCiAgZmlsdGVyKFJlZ3Jlc3Npb24gPT0gIkRUaW1lX1NwYWNlX0ZFX3RpbWVMYWdzIikgJT4lICMjcmVncmVzc3Rpb24NCiAgZ3JvdXBfYnkoc3RhcnRfc3RhdGlvbiwgc3RhcnRfbG9uLCBzdGFydF9sYXQpICU+JQ0KICBzdW1tYXJpemUoTUFFID0gbWVhbihhYnMoT2JzZXJ2ZWQtUHJlZGljdGlvbiksIG5hLnJtID0gVFJVRSkpJT4lDQpnZ3Bsb3QoLikrDQogIGdlb21fc2YoZGF0YSA9IChQaGlsbHlUcmFjdHMlPiUNCiAgICBzdF90cmFuc2Zvcm0oc3RfY3JzKDQzMjYpKSksIGNvbG9yID0gImdyZXkiLCBmaWxsID0gInRyYW5zcGFyZW50IikrDQogIGdlb21fcG9pbnQoYWVzKHggPSBzdGFydF9sb24sIHkgPSBzdGFydF9sYXQsIGNvbG9yID0gTUFFKSwgDQogICAgICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGFscGhhID0gMC40KSsNCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXMoZGlyZWN0aW9uID0gLTEsDQogIGRpc2NyZXRlID0gRkFMU0UsIG9wdGlvbiA9ICJEIikrDQogIHlsaW0obWluKGRhdF9jZW5zdXMkc3RhcnRfbGF0KSwgbWF4KGRhdF9jZW5zdXMkc3RhcnRfbGF0KSkrDQogIHhsaW0obWluKGRhdF9jZW5zdXMkc3RhcnRfbG9uKSwgbWF4KGRhdF9jZW5zdXMkc3RhcnRfbG9uKSkrDQogIGxhYnModGl0bGU9IkZpZ3VyZSA1LjM6IE1lYW4gQWJzIEVycm9yLCBUZXN0IFNldCwgTW9kZWwgNCIpKw0KICBtYXBUaGVtZQ0KYGBgDQoNClVzaW5nIG91ciBiZXN0IG1vZGVsIHRvIG1hcCB0aGUgZXJyb3IgKEZpZ3VyZSA1LjMpLCBpdCdzIGNsZWFyIHRoYXQgaGlnaGVyIGVycm9ycyBjb25jZW50cmF0ZSBpbiB0aGUgY2VudGVyIGNpdHkgYW5kIHNoYXJlIGEgc2ltaWxhciBwYXR0ZXJuIHdpdGggdGhlIG92ZXJhbGwgcGVha3Mgb2YgZGVtYW5kcy4gVGhhdCdzIHRvIHNheSwgdGhpcyBtb2RlbCBtYXkgbm90IGJlIGNvbXBldGVudCBlbm91Z2ggdG8gZ2VuZXJhbGl6ZSBpbiB0aGUgY2VudGVyIGNpdHkgdG8gcHJlZGljdCB0aGUgZGVtYW5kcy4NCg0KYGBge3Igb2JzX3ByZWRfYWxsLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQp3ZWVrX3ByZWRpY3Rpb25zICU+JSANCiAgICBtdXRhdGUoaW50ZXJ2YWw2MCA9IG1hcChkYXRhLCBwdWxsLCBpbnRlcnZhbDYwKSwNCiAgICAgICAgICAgc3RhcnRfc3RhdGlvbiA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9zdGF0aW9uKSwgDQogICAgICAgICAgIHN0YXJ0X2xhdCA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9sYXQpLCANCiAgICAgICAgICAgc3RhcnRfbG9uID0gbWFwKGRhdGEsIHB1bGwsIHN0YXJ0X2xvbiksDQogICAgICAgICAgIGRvdHcgPSBtYXAoZGF0YSwgcHVsbCwgZG90dykpICU+JQ0KICAgIGRwbHlyOjpzZWxlY3QoaW50ZXJ2YWw2MCwgc3RhcnRfc3RhdGlvbiwgc3RhcnRfbG9uLCANCiAgICAgICAgICAgc3RhcnRfbGF0LCBPYnNlcnZlZCwgUHJlZGljdGlvbiwgUmVncmVzc2lvbiwNCiAgICAgICAgICAgZG90dykgJT4lDQogICAgdW5uZXN0KCkgJT4lDQogIGZpbHRlcihSZWdyZXNzaW9uID09ICJEVGltZV9TcGFjZV9GRV90aW1lTGFncyIpJT4lDQogIG11dGF0ZSh3ZWVrZW5kID0gaWZlbHNlKGRvdHcgJWluJSBjKCJTdW4iLCAiU2F0IiksICJXZWVrZW5kIiwgIldlZWtkYXkiKSwNCiAgICAgICAgIHRpbWVfb2ZfZGF5ID0gY2FzZV93aGVuKGhvdXIoaW50ZXJ2YWw2MCkgPCA3IHwgaG91cihpbnRlcnZhbDYwKSA+IDE4IH4gIk92ZXJuaWdodCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3VyKGludGVydmFsNjApID49IDcgJiBob3VyKGludGVydmFsNjApIDwgMTAgfiAiQU0gUnVzaCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3VyKGludGVydmFsNjApID49IDEwICYgaG91cihpbnRlcnZhbDYwKSA8IDE1IH4gIk1pZC1EYXkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSAxNSAmIGhvdXIoaW50ZXJ2YWw2MCkgPD0gMTggfiAiUE0gUnVzaCIpKSU+JQ0KICBnZ3Bsb3QoKSsNCiAgZ2VvbV9wb2ludChhZXMoeD0gT2JzZXJ2ZWQsIHkgPSBQcmVkaWN0aW9uKSkrDQogICAgZ2VvbV9zbW9vdGgoYWVzKHg9IE9ic2VydmVkLCB5PSBQcmVkaWN0aW9uKSwgbWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgY29sb3IgPSAicmVkIikrDQogICAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxLCBpbnRlcmNlcHQgPSAwKSsNCiAgZmFjZXRfZ3JpZCh0aW1lX29mX2RheX53ZWVrZW5kKSsNCiAgbGFicyh0aXRsZT0iRmlndXJlIDUuNDogT2JzZXJ2ZWQgdnMgUHJlZGljdGVkIiwNCiAgICAgICB4PSJPYnNlcnZlZCB0cmlwcyIsIA0KICAgICAgIHk9IlByZWRpY3RlZCB0cmlwcyIpKw0KICBwbG90VGhlbWUNCmBgYA0KDQpBY2NvcmRpbmcgdG8gRmlndXJlIDUuNCwgdGhlcmUgaXMgYSBzZXJpb3VzIHVuZGVyLXByZWRpY3Rpb24gb2YgcmlkZXJzaGlwIGF0IGFsbCB0aW1lIG9mIGEgZGF5IGFuZCBhIHdlZWssIHdoaWNoIHN1Z2dlc3RzIHRoYXQgb3VyIG1vZGVsIGlzIG5vdCBhY2N1cmF0ZSBlbm91Z2ggYmVjYXVzZSB0aGUgYWN0dWFsIHJpZGVyc2hpcHMgYXJlIHR5cGljYWxseSBoaWdoZXIuDQoNCmBgYHtyIHN0YXRpb25fc3VtbWFyeSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBjYWNoZSA9IFRSVUUsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTd9DQp3ZWVrX3ByZWRpY3Rpb25zICU+JSANCiAgICBtdXRhdGUoaW50ZXJ2YWw2MCA9IG1hcChkYXRhLCBwdWxsLCBpbnRlcnZhbDYwKSwNCiAgICAgICAgICAgc3RhcnRfc3RhdGlvbiA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9zdGF0aW9uKSwgDQogICAgICAgICAgIHN0YXJ0X2xhdCA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9sYXQpLCANCiAgICAgICAgICAgc3RhcnRfbG9uID0gbWFwKGRhdGEsIHB1bGwsIHN0YXJ0X2xvbiksDQogICAgICAgICAgIGRvdHcgPSBtYXAoZGF0YSwgcHVsbCwgZG90dykpICU+JQ0KICAgIGRwbHlyOjpzZWxlY3QoaW50ZXJ2YWw2MCwgc3RhcnRfc3RhdGlvbiwgc3RhcnRfbG9uLCANCiAgICAgICAgICAgc3RhcnRfbGF0LCBPYnNlcnZlZCwgUHJlZGljdGlvbiwgUmVncmVzc2lvbiwNCiAgICAgICAgICAgZG90dykgJT4lDQogICAgdW5uZXN0KCkgJT4lDQogIGZpbHRlcihSZWdyZXNzaW9uID09ICJEVGltZV9TcGFjZV9GRV90aW1lTGFncyIpJT4lDQogIG11dGF0ZSh3ZWVrZW5kID0gaWZlbHNlKGRvdHcgJWluJSBjKCJTdW4iLCAiU2F0IiksICJXZWVrZW5kIiwgIldlZWtkYXkiKSwNCiAgICAgICAgIHRpbWVfb2ZfZGF5ID0gY2FzZV93aGVuKGhvdXIoaW50ZXJ2YWw2MCkgPCA3IHwgaG91cihpbnRlcnZhbDYwKSA+IDE4IH4gIk92ZXJuaWdodCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3VyKGludGVydmFsNjApID49IDcgJiBob3VyKGludGVydmFsNjApIDwgMTAgfiAiQU0gUnVzaCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3VyKGludGVydmFsNjApID49IDEwICYgaG91cihpbnRlcnZhbDYwKSA8IDE1IH4gIk1pZC1EYXkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSAxNSAmIGhvdXIoaW50ZXJ2YWw2MCkgPD0gMTggfiAiUE0gUnVzaCIpKSAlPiUNCiAgZ3JvdXBfYnkoc3RhcnRfc3RhdGlvbiwgd2Vla2VuZCwgdGltZV9vZl9kYXksIHN0YXJ0X2xvbiwgc3RhcnRfbGF0KSAlPiUNCiAgc3VtbWFyaXplKE1BRSA9IG1lYW4oYWJzKE9ic2VydmVkLVByZWRpY3Rpb24pLCBuYS5ybSA9IFRSVUUpKSU+JQ0KICBnZ3Bsb3QoLikrDQogIGdlb21fc2YoZGF0YSA9IFBoaWxseVRyYWN0cyU+JQ0KICAgIHN0X3RyYW5zZm9ybShzdF9jcnMoNDMyNikpLCBjb2xvciA9ICJncmV5IiwgZmlsbCA9ICJ0cmFuc3BhcmVudCIpKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gc3RhcnRfbG9uLCB5ID0gc3RhcnRfbGF0LCBjb2xvciA9IE1BRSksIA0KICAgICAgICAgICAgIGZpbGwgPSAidHJhbnNwYXJlbnQiLCBzaXplID0gMC41LCBhbHBoYSA9IDEpKw0KICBzY2FsZV9jb2xvdXJfdmlyaWRpcyhkaXJlY3Rpb24gPSAtMSwNCiAgZGlzY3JldGUgPSBGQUxTRSwgb3B0aW9uID0gIkQiKSsNCiAgeWxpbShtaW4oZGF0X2NlbnN1cyRzdGFydF9sYXQpLCBtYXgoZGF0X2NlbnN1cyRzdGFydF9sYXQpKSsNCiAgeGxpbShtaW4oZGF0X2NlbnN1cyRzdGFydF9sb24pLCBtYXgoZGF0X2NlbnN1cyRzdGFydF9sb24pKSsNCiAgZmFjZXRfZ3JpZCh3ZWVrZW5kfnRpbWVfb2ZfZGF5KSsNCiAgbGFicyh0aXRsZT0iRmlndXJlIDUuNTogTWVhbiBBYnNvbHV0ZSBFcnJvcnMsIFRlc3QgU2V0IikrDQogIG1hcFRoZW1lDQogIA0KYGBgDQoNCkZ1cnRoZXIgZXhhbWluaW5nIHRoZSBzcGF0aWFsIGRpc3RyaWJ1dGlvbiBvZiBlcnJvcnMgYnkgZ2VvZ3JhcGh5IGFuZCB0aW1lLCB3ZSBjYW4gc2VlIHRoYXQgdGhlIGJpZ2dlc3QgZXJyb3JzIGFyZSBvbiBBTSBSdXNoIGhvdXJzIGluIExvd2VyIE5vcnRoIFBoaWxhZGVscGhpYSBhbmQgbm9ydGhlcm4gZnJpbmdlIG9mIHRoZSBTb3V0aCBQaGlsYWRlbHBoaWEuIFRoZSBlcnJvcnMgYXJlIGFsc28gaGlnaCBvbiBQTSBSdXNoIGhvdXJzIGluIGNlbnRlciBjaXR5IGFuZCB0aGUgYnJpZGdlIGNvbm5lY3RpbmcgV2VzdCBQaGlsYWRlbHBoaWEuIEl0J3Mgbm90IG5lY2Vzc2FyeSB0aGF0IGhpZ2ggZXJyb3JzIHdpbGwgYWNjb21wYW55IGhpZ2ggZGVtYW5kcyBnaXZlbiB0aGF0IEFNIFJ1c2ggaG91cnMgb24gd2Vla2RheXMgdGVuZCB0byBoYXZlIGxvd2VyIGRlbWFuZHMgdGhhbiBQTSBSdXNoIGhvdXJzIChGaWd1cmUgNC41KSwgYW5kIEFNIGhvdXJzIG9uIHdlZWtlbmRzIGhhdmUgaGlnaGVyIG92ZXJhbGwgZXJyb3JzIHRoYW4gb3Zlcm5pZ2h0IGhvdXJzIHdoaWxlIGRlbWFuZHMgYXJlIG90aGVyd2lzZS4gQmlnZ2VyIG1lYW4gYWJzb2x1dGUgZXJyb3IgbWVhbnMgaGlnaGVyIGluYWNjdXJhY3ksIHdoaWxlIGFsc28gY2FzdGluZyBhIGNvbmNlcm4gdG8gZXhwZWN0IHRoaXMgbW9kZWwgdG8gZ2VuZXJhbGl6ZSBpbiBjZXJ0YWluIHBsYWNlcyBhbmQgYXQgY2VydGFpbiB0aW1lLg0KDQojIENyb3NzIHZhbGlkYXRpb24gDQoNClRlbXBvcmFsIGFuZCBzcGF0aWFsIGNyb3NzLXZhbGlkYXRpb24gYXJlIGNvbmR1Y3RlZCB0byBleGFtaW5lIG1vZGVscycgZ2VuZXJhbGl6aWJpbGl0eSBvbiBuZXcgZGF0YS4NCg0KYGBge3IgdGVtcG9yYWwgY3Jvc3MgdmFsaWRhdGlvbiwgd2FybmluZz1GQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0KYmlrZW5ldHNhbXBsZSA8LSBzYW1wbGVfbihzdHVkeS5wYW5lbCwgMTAwMDApJT4lDQogIG5hLm9taXQoKQ0KDQpzdHVkeV9wYW5lbC5uZXQgPC0gc3R1ZHlfcGFuZWwubmV0Wyxjb2xTdW1zKGlzLm5hKHN0dWR5X3BhbmVsLm5ldCkpIDwgbnJvdyhzdHVkeV9wYW5lbC5uZXQpXQ0KY3Bfc3R1X3BhLm5ldCA8LSBuYS5vbWl0KHN0dWR5X3BhbmVsLm5ldCkgDQoNCmJpa2VuZXRzYW1wbGUubmV0IDwtIHNhbXBsZV9uKGNwX3N0dV9wYS5uZXQsIDEwMDAwKSU+JQ0KICBuYS5vbWl0KCkNCg0KbGlicmFyeShjYXJldCkNCmZpdENvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyID0gMTAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc2F2ZVByZWRpY3Rpb25zID0gVFJVRSkNCg0Kc2V0LnNlZWQoMTAwMCkNCiMgZm9yIGstZm9sZHMgQ1YNCg0KcmVnLmN2LmsgPC0gIA0KICB0cmFpbihUcmlwX0NvdW50IH4gc3RhcnRfc3RhdGlvbiArICBob3VyKGludGVydmFsNjApICsgZG90dyArIFRlbXBlcmF0dXJlICsgUHJlY2lwaXRhdGlvbiArDQogICAgICAgICAgbGFnSG91ciArIGxhZzJIb3VycyArbGFnM0hvdXJzICsgbGFnMTJIb3VycyArIGxhZzFkYXksIA0KICAgICAgICBkYXRhID0gYmlrZW5ldHNhbXBsZSwgIA0KICAgICAgICBtZXRob2QgPSAibG0iLCAgDQogICAgICAgIHRyQ29udHJvbCA9IGZpdENvbnRyb2wsICANCiAgICAgICAgbmEuYWN0aW9uID0gbmEucGFzcykNCg0KcmVnLmN2LmsNCg0KcmVnLmN2LmsubmV0IDwtDQogICB0cmFpbihUcmlwX0NvdW50IH4gc3RhcnRfc3RhdGlvbiArICBob3VyKGludGVydmFsNjApICsgZG90dyArIFRlbXBlcmF0dXJlICsgUHJlY2lwaXRhdGlvbiArDQogICAgICAgICAgbGFnSG91ciArIGxhZzJIb3VycyArbGFnM0hvdXJzICsgbGFnMTJIb3VycyArIGxhZzFkYXkgKyBjb2xsZWdlLm5uICsgbmF0aW9uYWxfcGFyay5ubiArIHRvdXJpc20ubm4gKyBzaG9wLm5uICsgY3Vpc2luZS5ubiArIG9mZmljZS5ubiArIGJ1c19zdGF0aW9uLm5uICsgdHJvbGx5X3N0b3BzLm5uICsgcmFpbF9zdGF0aW9uLm5uICsgc2VwdGFTdG9wcy5ubiArIHVuaXF1ZUlEICsgVG90YWxfUG9wICsgTWVkX0luYyArIFdoaXRlX1BvcCArIFRyYXZlbF9UaW1lICsgTWVhbnNfb2ZfVHJhbnNwb3J0ICsgVG90YWxfUHVibGljX1RyYW5zICsgTWVkX0FnZSArIFBlcmNlbnRfV2hpdGUgKyBNZWFuX0NvbW11dGVfVGltZSArIFBlcmNlbnRfVGFraW5nX1B1YmxpY19UcmFucywgDQogICAgICAgIGRhdGEgPSBiaWtlbmV0c2FtcGxlLm5ldCwgIA0KICAgICAgICBtZXRob2QgPSAibG0iLCAgDQogICAgICAgIHRyQ29udHJvbCA9IGZpdENvbnRyb2wsICANCiAgICAgICAgbmEuYWN0aW9uID0gbmEucGFzcykNCg0KcmVnLmN2LmsubmV0DQoNCmdncGxvdChyZWcuY3YuayRyZXNhbXBsZSwgYWVzKHg9TUFFKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzAsIGNvbG91cj0iYmxhY2siLCBmaWxsID0gIiNGREU3MjVGRiIpICsNCiAgbGFicyh0aXRsZSA9ICJGaWd1cmUgNi4xOiBNZWFuIEF2ZXJhZ2UgRXJyb3IgaW4gQ3Jvc3MgVmFsaWRhdGlvbiBUZXN0cyB3aXRoIG1vZGVsIDQiKSANCg0KZ2dwbG90KHJlZy5jdi5rLm5ldCRyZXNhbXBsZSwgYWVzKHg9TUFFKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzAsIGNvbG91cj0iYmxhY2siLCBmaWxsID0gIiNGREU3MjVGRiIpICsNCiAgbGFicyh0aXRsZSA9ICJGaWd1cmUgNi4yOiBNZWFuIEF2ZXJhZ2UgRXJyb3IgaW4gQ3Jvc3MgVmFsaWRhdGlvbiBUZXN0cyB3aXRoIG1vZGVsIDQgcGx1cyBBZGR0aW9uYWwgRmVhdHVyZXMiKSANCg0KYGBgDQoNCkstZm9sZCBtZXRob2QgaXMgdXNlZCBoZXJlIGZvciB0aGUgdGVtcG9yYWwgY3Jvc3MtdmFsaWRhdGlvbi4gVGhlIGVycm9ycyBvZiBvdXIgYmVzdCB0aW1lLWxhZyBtb2RlbCBEVGltZV9TcGFjZV9GRV90aW1lTGFncyBjbHVzdGVyIGFyb3VuZCAwLjUtMC41NSAoRmlndXJlIDYuMSksIHdoaWNoIGlzIGEgZmFpciBwZXJmb3JtYW5jZSBnaXZlbiB0aGUgYXZlcmFnZSByaWRlcnNoaXAgcGVyIHN0YXRpb24gcGVyIGhvdXIuIEhvd2V2ZXIsIGFkZGluZyBhbWVuaXR5IGFuZCBzcGF0aWFsIGZlYXR1cmVzIHRvIHRoZSBtb2RlbCBkb2Vzbid0IGltcHJvdmUgaXRzIHRlbXBvcmFsIHBlcmZvcm1hbmNlIGluIHRoaXMgY2FzZSAoRmlndXJlIDYuMikuDQoNCmBgYHtyIHNwYXRpYWwgY3Jvc3MgdmFsaWRhdGlvbiwgd2FybmluZz1GQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBlY2hvPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCg0KcmVnLnZhcnMgPC0gYygiY29sbGVnZS5ubiIsIm5hdGlvbmFsX3Bhcmsubm4iLCJ0b3VyaXNtLm5uIiwic2hvcC5ubiIsImN1aXNpbmUubm4iLCJvZmZpY2Uubm4iLCJidXNfc3RhdGlvbi5ubiIsInRyb2xseV9zdG9wcy5ubiIsInJhaWxfc3RhdGlvbi5ubiIsInNlcHRhU3RvcHMubm4iLCJNZWRfSW5jIiwiVHJhdmVsX1RpbWUiLCJNZWFuc19vZl9UcmFuc3BvcnQiLCJUb3RhbF9QdWJsaWNfVHJhbnMiLCJQZXJjZW50X1doaXRlIiwiTWVkX0FnZSIsIk1lYW5fQ29tbXV0ZV9UaW1lIiwiUGVyY2VudF9UYWtpbmdfUHVibGljX1RyYW5zIikNCg0KcmVnLnNzLnZhcnMgPC0gYygiY29sbGVnZS5ubiIsIm5hdGlvbmFsX3Bhcmsubm4iLCJ0b3VyaXNtLm5uIiwic2hvcC5ubiIsImN1aXNpbmUubm4iLCJvZmZpY2Uubm4iLCJidXNfc3RhdGlvbi5ubiIsInRyb2xseV9zdG9wcy5ubiIsInJhaWxfc3RhdGlvbi5ubiIsInNlcHRhU3RvcHMubm4iLCJSSURFLmlzU2lnIiwiUklERS5pc1NpZy5kaXN0IikNCg0KY3Jvc3NWYWxpZGF0ZSA8LSBmdW5jdGlvbihkYXRhc2V0LCBpZCwgZGVwZW5kZW50VmFyaWFibGUsIGluZFZhcmlhYmxlcykgew0KDQphbGxQcmVkaWN0aW9ucyA8LSBkYXRhLmZyYW1lKCkNCmN2SURfbGlzdCA8LSB1bmlxdWUoZGF0YXNldFtbaWRdXSkNCg0KDQogIGZvciAoaSBpbiBjdklEX2xpc3QpIHsNCg0KICB0aGlzRm9sZCA8LSBpDQogIGNhdCgiVGhpcyBob2xkIG91dCBmb2xkIGlzIiwgdGhpc0ZvbGQsICJcbiIpDQoNCiAgZm9sZC50cmFpbiA8LSBmaWx0ZXIoZGF0YXNldCwgZGF0YXNldFtbaWRdXSAhPSB0aGlzRm9sZCkgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgDQogICAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdChpZCwgZ2VvbWV0cnksIGluZFZhcmlhYmxlcywgZGVwZW5kZW50VmFyaWFibGUpDQogIGZvbGQudGVzdCAgPC0gZmlsdGVyKGRhdGFzZXQsIGRhdGFzZXRbW2lkXV0gPT0gdGhpc0ZvbGQpICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIA0KICAgICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QoaWQsIGdlb21ldHJ5LCBpbmRWYXJpYWJsZXMsIGRlcGVuZGVudFZhcmlhYmxlKQ0KICANCiAgcmVncmVzc2lvbiA8LQ0KICAgIGxtKGNvdW50UklERSB+IC4sIA0KICAgICAgZGF0YSA9IGZvbGQudHJhaW4gJT4lIA0KICAgICAgZHBseXI6OnNlbGVjdCgtZ2VvbWV0cnksIC1pZCkpDQogIA0KICB0aGlzUHJlZGljdGlvbiA8LSANCiAgICBtdXRhdGUoZm9sZC50ZXN0LCBQcmVkaWN0aW9uID0gcHJlZGljdChyZWdyZXNzaW9uLCBmb2xkLnRlc3QsIHR5cGUgPSAicmVzcG9uc2UiKSkNCiAgICANCiAgYWxsUHJlZGljdGlvbnMgPC0NCiAgICByYmluZChhbGxQcmVkaWN0aW9ucywgdGhpc1ByZWRpY3Rpb24pDQogICAgDQogIH0NCiAgcmV0dXJuKHN0X3NmKGFsbFByZWRpY3Rpb25zKSkNCn0NCg0Kc3ViZmluYWxfbmV0IDwtIGZpbmFsX25ldFssYygiY29sbGVnZS5ubiIsIm5hdGlvbmFsX3Bhcmsubm4iLCJ0b3VyaXNtLm5uIiwic2hvcC5ubiIsImN1aXNpbmUubm4iLCJvZmZpY2Uubm4iLCJidXNfc3RhdGlvbi5ubiIsInRyb2xseV9zdG9wcy5ubiIsInJhaWxfc3RhdGlvbi5ubiIsInNlcHRhU3RvcHMubm4iLCJNZWRfSW5jIiwiVHJhdmVsX1RpbWUiLCJNZWFuc19vZl9UcmFuc3BvcnQiLCAiVG90YWxfUHVibGljX1RyYW5zIiwiUGVyY2VudF9XaGl0ZSIsIk1lZF9BZ2UiLCJNZWFuX0NvbW11dGVfVGltZSIsIlBlcmNlbnRfVGFraW5nX1B1YmxpY19UcmFucyIsIlJJREUuaXNTaWciLCJSSURFLmlzU2lnLmRpc3QiLCJjdklEIiwibmJuYW1lIiwiY291bnRSSURFIildDQoNCiNSZWdyZXNzaW9ucw0KcmVnLmN2IDwtIGNyb3NzVmFsaWRhdGUoDQogIGRhdGFzZXQgPSBzdWJmaW5hbF9uZXQsDQogIGlkID0gImN2SUQiLA0KICBkZXBlbmRlbnRWYXJpYWJsZSA9ICJjb3VudFJJREUiLA0KICBpbmRWYXJpYWJsZXMgPSByZWcudmFycykgJT4lDQogICAgZHBseXI6OnNlbGVjdChjdklEID0gY3ZJRCwgY291bnRSSURFLCBQcmVkaWN0aW9uLCBnZW9tZXRyeSkNCg0KcmVnLnNzLmN2IDwtIGNyb3NzVmFsaWRhdGUoDQogIGRhdGFzZXQgPSBzdWJmaW5hbF9uZXQsDQogIGlkID0gImN2SUQiLA0KICBkZXBlbmRlbnRWYXJpYWJsZSA9ICJjb3VudFJJREUiLA0KICBpbmRWYXJpYWJsZXMgPSByZWcuc3MudmFycykgJT4lDQogICAgZHBseXI6OnNlbGVjdChjdklEID0gY3ZJRCwgY291bnRSSURFLCBQcmVkaWN0aW9uLCBnZW9tZXRyeSkNCg0KcmVnLnNwYXRpYWxDViA8LSBjcm9zc1ZhbGlkYXRlKA0KICBkYXRhc2V0ID0gc3ViZmluYWxfbmV0LA0KICBpZCA9ICJuYm5hbWUiLA0KICBkZXBlbmRlbnRWYXJpYWJsZSA9ICJjb3VudFJJREUiLA0KICBpbmRWYXJpYWJsZXMgPSByZWcudmFycykgJT4lDQogICAgZHBseXI6OnNlbGVjdChjdklEID0gbmJuYW1lLCBjb3VudFJJREUsIFByZWRpY3Rpb24sIGdlb21ldHJ5KQ0KDQpyZWcuc3Muc3BhdGlhbENWIDwtIGNyb3NzVmFsaWRhdGUoDQogIGRhdGFzZXQgPSBzdWJmaW5hbF9uZXQsDQogIGlkID0gIm5ibmFtZSIsDQogIGRlcGVuZGVudFZhcmlhYmxlID0gImNvdW50UklERSIsDQogIGluZFZhcmlhYmxlcyA9IHJlZy5zcy52YXJzKSAlPiUNCiAgICBkcGx5cjo6c2VsZWN0KGN2SUQgPSBuYm5hbWUsIGNvdW50UklERSwgUHJlZGljdGlvbiwgZ2VvbWV0cnkpDQoNCiNzdW1tYXJ5IG9mIHJlZ3Jlc3Npb24gcmVzdWx0cw0KcmVnLnN1bW1hcnkgPC0gDQogIHJiaW5kKA0KICAgIG11dGF0ZShyZWcuY3YsICAgICAgICAgICBFcnJvciA9IFByZWRpY3Rpb24gLSBjb3VudFJJREUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJlZ3Jlc3Npb24gPSAiUmFuZG9tIGstZm9sZCBDVjogSnVzdCBSaXNrIEZhY3RvcnMiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgbXV0YXRlKHJlZy5zcy5jdiwgICAgICAgIEVycm9yID0gUHJlZGljdGlvbiAtIGNvdW50UklERSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUmVncmVzc2lvbiA9ICJSYW5kb20gay1mb2xkIENWOiBTcGF0aWFsIFByb2Nlc3MiKSwNCiAgICANCiAgICBtdXRhdGUocmVnLnNwYXRpYWxDViwgICAgRXJyb3IgPSBQcmVkaWN0aW9uIC0gY291bnRSSURFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZWdyZXNzaW9uID0gIlNwYXRpYWwgTE9HTy1DVjogSnVzdCBSaXNrIEZhY3RvcnMiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgbXV0YXRlKHJlZy5zcy5zcGF0aWFsQ1YsIEVycm9yID0gUHJlZGljdGlvbiAtIGNvdW50UklERSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUmVncmVzc2lvbiA9ICJTcGF0aWFsIExPR08tQ1Y6IFNwYXRpYWwgUHJvY2VzcyIpKSAlPiUNCiAgICBzdF9zZigpIA0KDQplcnJvcl9ieV9yZWdfYW5kX2ZvbGQgPC0gDQogIHJlZy5zdW1tYXJ5ICU+JQ0KICAgIGdyb3VwX2J5KFJlZ3Jlc3Npb24sIGN2SUQpICU+JSANCiAgICBzdW1tYXJpemUoTWVhbl9FcnJvciA9IG1lYW4oUHJlZGljdGlvbiAtIGNvdW50UklERSwgbmEucm0gPSBUKSwNCiAgICAgICAgICAgICAgTUFFID0gbWVhbihhYnMoTWVhbl9FcnJvciksIG5hLnJtID0gVCksDQogICAgICAgICAgICAgIFNEX01BRSA9IG1lYW4oYWJzKE1lYW5fRXJyb3IpLCBuYS5ybSA9IFQpKSAlPiUNCiAgdW5ncm91cCgpDQpgYGANCg0KYGBge3IgTUFQIDc6IHNtYWxsIG11bHRpcGxlIG1hcCBvZiBtb2RlbCBlcnJvcnMgYnkgcmFuZG9tIGstZm9sZCBhbmQgc3BhdGlhbCBjcm9zcyB2YWxpZGF0aW9uLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD02LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlY2hvPUZBTFNFfQ0KDQplcnJvcl9ieV9yZWdfYW5kX2ZvbGQgJT4lDQogIGdncGxvdChhZXMoTUFFKSkgKyANCiAgICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzAsIGNvbG91cj0iYmxhY2siLCBmaWxsID0gIiNGREU3MjVGRiIpICsNCiAgICBmYWNldF93cmFwKH5SZWdyZXNzaW9uKSArICANCiAgICBsYWJzKHRpdGxlPSJGaWd1cmUgNi4zOiBEaXN0cmlidXRpb24gb2YgTUFFIiwgc3VidGl0bGUgPSAiay1mb2xkIGNyb3NzIHZhbGlkYXRpb24gdnMuIExPR08tQ1YiLA0KICAgICAgICAgeD0iTWVhbiBBYnNvbHV0ZSBFcnJvciIsIHk9IkNvdW50IikgDQoNCm1hcF9vZl9rX2FuZF92YWxpZGF0aW9uIDwtDQogIGdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gZXJyb3JfYnlfcmVnX2FuZF9mb2xkLCBhZXMoZmlsbCA9IE1BRSwgY29sb3VyID0gTUFFKSkgKw0KICAgICAgICBzY2FsZV9maWxsX3ZpcmlkaXMoKSArDQogICAgICAgIGZhY2V0X3dyYXAoflJlZ3Jlc3Npb24pICsgDQogICAgICAgIHNjYWxlX2NvbG91cl92aXJpZGlzKCkrDQogICAgICAgIGxhYnModGl0bGU9IkZpZ3VyZSA2LjQ6IERpc3RyaWJ1dGlvbiBvZiBNQUUiLCBzdWJ0aXRsZSA9ICJrLWZvbGQgY3Jvc3MgdmFsaWRhdGlvbiB2cy4gTE9HTy1DViIsDQogICAgICAgIHg9Ik1lYW4gQWJzb2x1dGUgRXJyb3IiLCB5PSJDb3VudCIpKw0KICAgICAgICBtYXBUaGVtZQ0KDQptYXBfb2Zfa19hbmRfdmFsaWRhdGlvbg0KDQoNCiNwcmVkaWN0aW9ucw0KcmVnLnN1bW1hcnkgJT4lDQogIGdncGxvdCgpICsNCiAgZ2VvbV9zZihhZXMoZmlsbCA9IFByZWRpY3Rpb24sIGNvbG91ciA9IFByZWRpY3Rpb24pKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpcygpICsNCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXMoKSArDQogIGZhY2V0X3dyYXAoflJlZ3Jlc3Npb24pICsgIA0KICBtYXBUaGVtZSsNCiAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTUpKSsNCiAgbGFicyh0aXRsZSA9ICJGaWd1cmUgNi41OiBQcmVkaWN0aW9ucyBXaXRoIERpZmZlcmVudCBNZXRob2RzICIpDQoNCiNhY3R1YWwgY291bnRzLCBmb3IgY29tcGFyaXNvbg0KcmVnLnNzLmN2ICU+JQ0KICBnZ3Bsb3QoKSArDQogIGdlb21fc2YoYWVzKGZpbGwgPSBjb3VudFJJREUsIGNvbG91ciA9IGNvdW50UklERSkpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzKCkgKw0KICBzY2FsZV9jb2xvdXJfdmlyaWRpcygpKw0KICBtYXBUaGVtZSsNCiAgbGFicyh0aXRsZSA9ICJGaWd1cmUgNi42OiBBY3R1YWwgUmlkZS1zaGFyZSBDb3VudHMgZnJvbSAxMC8yOS8yMDE4IHRvIDExLzIvMjAxOCIpDQoNCmBgYA0KDQpLLWZvbGQgYW5kIExPR08tQ1YgbWV0aG9kcyBhcmUgYm90aCB1c2VkIGhlcmUgdG8gZXhhbWluZSB0aGUgaW1wcm92ZW1lbnRzIG9mIHRoZSBtb2RlbCBnZW5lcmFsaXppYmlsaXR5IGJ5IGFkZGluZyBzcGF0aWFsIGFuZCBhbWVuaXR5IGZlYXR1cmVzLiBUaGUgYWNjdW11bGF0ZWQgbWVhbiBhYnNvbHV0ZSBlcnJvciBmb3IgZWFjaCBzdGF0aW9uIGlzIHNpZ25pZmljYW50bHkgcmVkdWNlZCBieSBhZGRpbmcgc3BhdGlhbCBwcm9jZXNzIHRvIHRoZSBtb2RlbCAoRmd1cmUgNi4zKS4gVGhlIGVycm9ycyBpbiBjZW50ZXIgY2l0eSBhcmUgbXVjaCBsb3dlciBub3cgd2l0aCBoaWdoZXIgZXJyb3IgaW4gdGhlIHNvdXRoZWFzdGVybiBQaGlsbHkgKEZpZ3VyZSA2LjQpLiBUaGUgcHJlZGljdGlvbnMgYnkgYWxsIGZvdXIgbW9kZWxzIChGaWd1cmUgNi41KSBhcmUgdmVyeSBzaW1pbGFyIHRvIHRoZSBhY3R1YWwgb2JzZXJ2YXRpb24gKEZpZ3VyZSA2LjYpLiANCg0KIyBDb25jbHVzaW9ucw0KDQpPdmVyYWxsLCBJIHRoaW5rIG15IGFsZ29yaXRobSBpcyBtb3JlIHVzZWZ1bCB0byBwcmVkaWN0IHRoZSBvdmVyYWxsIHNwYXRpYWwgZGlzdHJpYnV0aW9uIG9mIHRoZSByaWRlcnNoaXAgaG90IHNwb3RzIHRoYW4gcHJlZGljdGluZyB0aGUgbmV4dCAxIGhvdXIgYmlrZS1zaGFyZSBkZW1hbmQgaW4gYSBjZXJ0YWluIHN0YXRpb24uIEJhc2VkIG9uIG15IGN1cnJlbnQgYWxnb3JpdGhtLCBpdCdzIGFkdmlzZWQgdGhhdCBiaWtlcyBiZSByb3VnaGx5IGRpdmlkZWQgYWNjb3JkaW5nIHRvIHRoZSBwcmVkaWN0ZWQgc3BhdGlhbCBwYXR0ZXJuIGFuZCBhdHRyaWJ1dGVkIHRvIHRoZSBob3Qgc3BvdHMgZmlyc3QgYW5kIHRoZW4gZG8gbWlub3IgYWRqdXN0bWVudHMgYW1vbmcgbmVhciBob3Qgc3BvdHMuIA0KDQpUbyBpbXByb3ZlIHRoZSBjdXJyZW50IGFsZ29yaXRobSwgbG9uZ2VyIHJhbmdlIG9mIHRpbWUgZGF0YSBzaG91bGQgYmUgYXBwbGllZCBidXQgaGVyZSB0aGUgZGF0YSBpcyBvbmx5IDUtd2VlayBsb25nIGR1ZSB0byB0aGUgY29tcHV0ZXIgcHJvY2Vzc2luZyBwb3dlciByZXN0cmljdGlvbnMuIEFsc28sIHNpbmNlIHRoZSBkZW1hbmQgb2YgbmV4dCB0aW1lIHBlcmlvZCBpcyBtb3N0IGFmZmVjdGVkIGJ5IHRoZSBjbG9zZXN0IGxhc3QgdGltZSBwZXJpb2QsIGEgc21hbGxlciB0aW1lIGxhZyBpbiB0ZW1wb3JhbCBwcmVkaWN0aW9uIHNob3VsZCBhbHNvIGhlbHAgdG8gaW1wcm92ZSB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhlIGN1cnJlbnQgYWxnb3JpdGhtLiANCg==