Introduction

Bike Share Systems Role in Urban Mobilities

Bike share system are one of the critical components of urban mobility. They provide a flexible and convenient mode of transportation for short trips, reducing congestion and promoting sustainable travel. The systems are designed to be user-friendly, with easy access to bikes and docking stations, making them an attractive option for commuters and tourists alike. At the same time, bike sharing systems fill a gap in the public transportation system, providing a last-mile solution for users who need to travel short distances. By integrating bike sharing with other modes of transportation, such as buses and trains, cities can create a more efficient and sustainable transportation network.

Rebalancing Problems in Bike Sharing Systems

One of the key operational challenges in bike-share system is to maintain a balanced distribution across stations and times to meet user demands. Some stations frequently experience bike shortages where the users cannot get a bike from the docks when they need them. However, some stations frequently experience surpluses where the users cannot return the bikes to the docks when they need them. This rebalance issues become extremely serious during the peak hours due to assymetric commuting patterns. Without timely rebalancing, users may find it difficult to rent or return bikes on timly manners, leading to user dissatisfaction and reduced ridership. Efficient reblancing ahead of user demands ensures the system’s availability and reliability, which also involves real-time data analysis and prediction.

Proposed Rebalancing Strategies

To address this issue, we propose a data-driven approach to predict the bike demand across each stations and time intervals. By analyzing historical data, we can identify patterns and trends in bike usage, allowing us to make informed decisions about where and when to redistribute bikes. This approach not only improves the efficiency of the bike-share system but also enhances the overall user experience by ensuring that bikes are available when and where they are needed. Predictions will be made several hours in advance using the models that incorporate features such as time of day, day of week, historical usage patterns, weather conditions, and current station capacity. By leveraging these data sources, we can optimize the distribution of bikes across the system, reducing the likelihood of shortages and surpluses.

The model will be focused rebalance efforts on the stations in Philadelphia, PA. The model will be trained using the historical bike share data for the month of May, 2022. The model will be used to predict the bike demand for the next 24 hours, and the results will be used to inform rebalancing efforts.

Data Preprocessing and Feature Engineering

Data Preparation

Bike data

The bike share data is obtained from the Indego. The data includes information on bike trips, including start and end times, start and end stations, and bike IDs. The data is cleaned and preprocessed to remove any duplicates or missing values. The data is then aggregated to hourly intervals to create a time series of bike demand for each station. May 2022 data was selected for this analysis as the temporal frame.

bike<-read.csv("data/indego-trips-2022-q2.csv")


may <- bike %>%
  mutate(start_time = mdy_hm(start_time)) %>%
  filter(month(start_time) == 5) %>%
  mutate(interval60 = floor_date(ymd_hms(start_time), unit = "hour"),
         interval15 = floor_date(ymd_hms(start_time), unit = "15 mins"),
         week = week(interval60),
         dotw = wday(interval60, label=TRUE))

may<-may%>%
  filter(!is.na(ymd_hms(start_time)))

Population data

At the same time, census tract geometries of the entire Philadelphia county was acquired from the US Census Bureau to provide the geographic zone boundaries for further analysis if needed.

philCensus <-
  get_acs(geography = "tract",
          variables = c("B01003_001", "B19013_001",
                        "B02001_002", "B08013_001",
                        "B08012_001", "B08301_001",
                        "B08301_010", "B01002_001"),
          year = 2022,
          state = "PA",
          geometry = TRUE,
          county=c("Philadelphia"),
          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)

philTracts <-
  philCensus %>%
  as.data.frame() %>%
  distinct(GEOID, .keep_all = TRUE) %>%
  select(GEOID, geometry) %>%
  st_sf

Since the raw bikeshare data is a non-spatial layer, only containing latitute and longitude of the start and end stations, we need to join the bikeshare data with the census tract data to obtain the census tract information for each trip. The st_join function from the sf package is used to perform a spatial join between the bikeshare data and the census tract data. We are only interested in the start location of each ride because we want to predict the capacity of each station when the user need a bike to start the ride. This requires us to filter out rows with missing latitude and longitude.

dat_census <- st_join(may %>%
          filter(is.na(start_lon) == FALSE &
                   is.na(start_lat) == FALSE &
                   is.na(end_lon) == FALSE &
                   is.na(end_lat) == FALSE) %>%
          st_as_sf(., coords = c("start_lon", "start_lat"), crs = 4326),
        philTracts %>%
          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() %>%
  select(-geometry)%>%
  st_as_sf(., coords = c("end_lon", "end_lat"), crs = 4326) %>%
  st_join(., philTracts %>%
            st_transform(crs=4326),
          join=st_intersects,
          left = TRUE) %>%
  rename(Destination.Tract = GEOID)  %>%
  mutate(to_longitude = unlist(map(geometry, 1)),
         to_latitude = unlist(map(geometry, 2)))%>%
  as.data.frame() %>%
  select(-geometry)

The following map shows all the starting point of the rides in Philadelphia during May 2022.

ggplot()+
  geom_sf(data = philTracts %>%
          st_transform(crs=4326), fill="white")+
  geom_point(data = may, aes(x=start_lon, y = start_lat),
            color = "#6E0E0A", alpha = 1, size = 0.3) +
  ylim(min(may$start_lat), max(may$start_lat))+
  xlim(min(may$start_lon), max(may$start_lon)) +
  labs(title = "Location of Rides Starting Points in Philadelphia")+
  mapTheme

### Weather data The weather data is obtained from the National Weather Service (Philadelphia International Airport). The data includes information on temperature, precipitation, and wind speed. The data is cleaned and preprocessed to remove any duplicates or missing values. The data is then aggregated to hourly intervals to create a time series of weather conditions for each station.

weather.Panel <-
  riem_measures(station = "PHL", date_start = "2022-05-01", date_end = "2022-05-31") %>%
  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))

Feature Engineering & Exploratory Analysis

Temporal Analysis

Starting with temporal analysis, we first visualized the bikeshare trips per hour in Philadelphia in May 2023. It is clear that there’s some form of circularity in the data at both peak hours and non-peak hours during single day. We may also see that after five number of high peaks are usually by two lower peaks (weekday and weekend). These correspond the trends mentioned in the introduction section that bikeshare are much more needed during the weekdays rush hours compared to the weekends and late night hours.

ggplot(dat_census %>%
         group_by(interval60) %>%
         tally())+
  geom_line(aes(x = interval60, y = n))+
  labs(title="Bike share trips per hr. Philadelphia, May, 2022",
       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, size=0.8))

Then, we visualize the distribution of trips by stations at each hour in May. The distribution is right skewed, where most stations have zero or very few number of trips each hour. However, there are few stations that have a very high number of trips each hour on particular day in May 2022.

ggplot(dat_census %>%
         group_by(interval60, start_station) %>%
         tally())+
  geom_histogram(aes(n), binwidth = 5, fill="#6E0E0A")+
  labs(title="Bike share trips per hour by station. Philadelphia, May, 2022",
       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, size=0.8))

Temporary Feature Engineer

We perform feature engineering using lubrudate package to extract temporal information for each usage. the usage was calculating into 60 min and 15 min interval based on staring time for each ride. And then, the ride was further categorize into “Overnight”, “AM Rush”, “Mid-Day” and “PM Rush” based on the hour of the day and into weekday and weekend based on the day of the week.

We visualize the distribution of the mean number trips at each station during different time intervals of the day. During the PM rush hour, the average number of trips is the highest, followed by the mid-day and AM rush hour. The overnight period has the lowest average number of trips, with only few station with an average of 3 trips or more.

palette4 <- c("#124E78","#F0F0C9","#F2BB05","#6E0E0A")
dat_census %>%
        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, fill=time_of_day), binwidth = 1)+
  scale_fill_manual(values = palette4, name="Time of Day") +
  labs(title="Mean Number of Hourly Trips Per Station. Philadelphia, May, 2022",
       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))

After that, we visualize the distribution of the mean number of trips at each station during different days of the week. From the chart, we can see that the Monday, Tuesday, Wednesday, and Thursday shows similar temporal patterns, with similar and highest number of trips by the time of the day. The Friday and Sturday shows similar patterns, with smaller number of people utilize the system. And, Sunday shows a uniquepeak pattern than any other weekdays or weekends.But in general, lowest trip count occur in the early morning while highest trip count occur in the late afternoon.

ggplot(dat_census %>% mutate(hour = hour(start_time)))+
     geom_freqpoly(aes(hour, color = dotw), binwidth = 3,lwd=0.8)+
  labs(title="Bike share trips in Philadelphia, by day of the week, May, 2022",
       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))

We also compared the different usage between weekdays and weekends. The chart shows that the weekday usage is much higher than the weekend usage. The weekday usage shows a smaller peak during the AM rush hour and an even bigger peak during the PM rush hour. The weekend usage shows a smaller peak during the late morning and early afternoon, with a gradual decline in usage throughout the day.

ggplot(dat_census %>%
         mutate(hour = hour(start_time),
                weekend = ifelse(dotw %in% c("Sun", "Sat"), "Weekend", "Weekday")))+
     geom_freqpoly(aes(hour, color = weekend), binwidth = 1)+
  labs(title="Bike share trips in Philadelphia - weekend vs weekday, May, 2022",
       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))

Spatial and Temporal Analysis

We visualize the distribution of the trips count at each station during different time intervals on weekday and weekend. As the map shows below, it indicate that the weekday usage is much higher than the weekend usage. On both weekday and weekend, most of the trip starts station clustered around the central city Philadelphia area, with a few trips starts in the west Philadelphia, South Philadelphia. These distribution suggests that trip demands is especially higher in the central city area and major transportation facilities. There is probably need to rebalance the bikes in these areas to meet the demand.

ggplot()+
  geom_sf(data = philTracts %>%
          st_transform(crs=4326))+
  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 = 3)+
  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="Bike share trips per hr by station. Philadelphia, May, 2022")+
  mapTheme

Weather Analysis

May in Philadelphia was characterized by steadily warming spring days—morning lows in the 50s °F climbing to highs in the mid-80s by month’s end—with regular 15–20 °F. Percipitation were d mostly light tbut with a few intense downpours at the end of the month. Winds generally hovered around 5–15 mph. These brief wet and blustery interludes punctuated otherwise mild, comfortable weather throughout May.

grid.arrange(
  ggplot(weather.Panel, aes(interval60,Precipitation)) + geom_line() +
  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(),
        panel.border = element_rect(colour = "grey", fill=NA, size=0.8)),
  ggplot(weather.Panel, aes(interval60,Wind_Speed)) + geom_line() +
    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(),
        panel.border = element_rect(colour = "grey", fill=NA, size=0.8)),
  ggplot(weather.Panel, aes(interval60,Temperature)) + geom_line() +
    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(),
        panel.border = element_rect(colour = "grey", fill=NA, size=0.8)),
  top="Weather Data - Philadelphia (PHL) - May, 2022")

Space-Time Panel

We created a study panel where each instance in the panel is a unique combination of space and time. In other words, each row will represent the ride at a particular station during a particular hour. After that, we add some more information to this panel. This includes counting the number of rides at this station at this particular hour, adding weather information, and bringing in census data.

study.panel <-
  expand.grid(interval60=unique(dat_census$interval60),
              start_station = unique(dat_census$start_station)) %>%
  left_join(., dat_census %>%
              select(start_station, Origin.Tract, start_lon, start_lat )%>%
              distinct() %>%
              group_by(start_station) %>%
              slice(1))

ride.panel <-
  dat_census %>%
  mutate(Trip_Counter = 1) %>%
  right_join(study.panel) %>%
  group_by(interval60, start_station,  Origin.Tract, start_lon, start_lat) %>%
  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)

ride.panel <-
  left_join(ride.panel, philCensus %>%
              as.data.frame() %>%
              select(-geometry), by = c("Origin.Tract" = "GEOID"))

ride.panel <-
  ride.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) == 148,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 = ifelse(is.na(holidayLag) == TRUE, 0, holidayLag))

Weather Correlation

With the study panel, we could perform some exploratory analysis to prepare for further regression model. First, we want to see the relationship between the trip counts and the weather conditions. The figure below shows a generally significant, positive, and strong linear relationship between temperature and trip counts. This means in general, when the temperature increase, bikeshare demand tend to increase as well.

ride.panel %>%
  group_by(interval60) %>%
  summarize(Trip_Count = mean(Trip_Count),
            Temperature = first(Temperature)) %>%
  mutate(week = week(interval60)) %>%
  ggplot(aes(Temperature, Trip_Count)) +
    geom_point(color = "black") + geom_smooth(method = "lm", se= FALSE, color="#124E78") +
    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))

We would also like to see the relationship between the trip counts and the wind speed. The figure below shows in the first two weeks, there is a generally negative linear relationship between wind speed and trip counts. However, in the last two weeks, the relationship is positive. These non-constant relationship may indicate that the wind speed is not the sole predictors influence the ridership.

ride.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(color = "black") + geom_smooth(method = "lm", se= FALSE, color="#124E78") +
    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 trip counts exhibit temporal autocorrelation, incorporating lagged features would improve prediction accuracy. Trip volumes at a given hour are often strongly correlated with those in the preceding and following hours, so knowing the count at one time point makes it easier to estimate volumes in adjacent intervals. To capture this effect, we engineered six distinct types of hourly lag features.

The table and charts below show the Pearson correlations between current trip counts and six lagged variables. Correlation strength falls off as lag increases: immediate (1 h) and daily (24 h) lags show the strongest positive links, mid-range lags weaken steadily, and the 12-hour lag even flips to a negative relationship. These results show that the best predictors are the hour just before and the same hour on the previous day, while hours farther away are less helpful—and the hour 12 hours earlier can even show an opposite trend.

library(dplyr)
library(tidyr)
library(kableExtra)

# compute mean-by-hour, gather lags, then corr by lag variable
corr_df <- as.data.frame(ride.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) %>%
  summarise(Correlation = round(cor(Value, Trip_Count), 2)) %>%
  ungroup()

# render with kableExtra
corr_df %>%
  kable(
    format = "html",
    col.names = c("Lag Variable", "Correlation with Trip Count"),
    caption = "Correlation Between Lagged Trip Counts and Current Trip Count"
  ) %>%
  kable_styling(
    full_width = FALSE,
    position = "center",
    bootstrap_options = c("striped", "hover", "condensed")
  )
Correlation Between Lagged Trip Counts and Current Trip Count
Lag Variable Correlation with Trip Count
lagHour 0.90
lag2Hours 0.73
lag3Hours 0.53
lag4Hours 0.35
lag12Hours -0.52
lag1day 0.80
as.data.frame(ride.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 = 1, color = "black") + geom_smooth(method = "lm", se= FALSE, color="#124E78") +
  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))

Model Training and Validation

Data Splitting

We split the data into a training set and a test set. The training set includes data from week 20 to week 22, while the test set includes data from week 18 to week 19. The training set is used to train the model, while the test set is used to validate the model. By doing time based splitting, we preserve the natural temporal order, prevent any leakage of information from one period into another. This could ensure that our validation genuinely reflects the model’s ability to generalize to unseen time intervals.

ride.Train <- filter(ride.panel, week >= 20)
ride.Test <- filter(ride.panel, week < 20)

Model Training

We trained five different models using the training dataset with different focus:

  • reg1: A baseline temporal model captures the daily and weekly temperatures effects on trip counts, with hourly fixed effects to capture the cyclical nature of bike share demand.
  • reg2: A spatial model captures the spatial effects of bike share demand, with weekly and weekend fixed effects to capture the cyclical nature of bike share demand.
  • reg3: A temporal and spatial model captures the daily and weekly temperatures and percipitation effects on trip counts, with hourly fixed effects to capture the cyclical nature of bike share demand.
  • reg4: A autoregressive temporal and spatial model captures the daily and weekly temperatures and percipitation effects on trip counts, with hourly fixed effects to capture the cyclical nature of bike share demand, and lagged features to capture the serial correlation.
  • reg5: Add holiday adjustment for reg4 model to capture the holiday effects on trip counts, with hourly fixed effects to capture the cyclical nature of bike share demand, and lagged features to capture the serial correlation.
reg1 <-
  lm(Trip_Count ~  factor(hour(interval60)) + factor(dotw) + Temperature,  data=ride.Train)

reg2 <-
  lm(Trip_Count ~  start_station +  factor(dotw)+ Temperature,  data=ride.Train)

reg3 <-
  lm(Trip_Count ~  start_station + factor(hour(interval60)) + factor(dotw) + Temperature + Precipitation,
     data=ride.Train)

reg4 <-
  lm(Trip_Count ~  start_station +  factor(hour(interval60)) +  factor(dotw) + Temperature + Precipitation +
                   lagHour + lag2Hours +lag3Hours + lag12Hours + lag1day,
     data=ride.Train)

reg5 <-
  lm(Trip_Count ~  start_station + factor(hour(interval60)) +  factor(dotw) + Temperature + Precipitation +
                   lagHour + lag2Hours +lag3Hours +lag12Hours + lag1day + holidayLag + holiday,
     data=ride.Train)

Model Validation

We used the time based validation to evaluate the five different model performance. The validation is done by calculating the mean absolute error (MAE) and standard deviation of absolute error (RMSE) for each model. The MAE and RMSE are calculated for each week in the test set. The results are then summarized in following table.

The table shows that the reg4 and reg5 models yield the same and lowest MAE and RMSE on both week 18 and week 19 (testing datasets), which indicate a better performance than the other models. Since the reg5 model is more complex than the reg4 model, we can conclude that the reg4 model is the best model for predicting bike share demand in Philadelphia and adding additional holiday effects doesn’t improve the model performance.

ride.Test.weekNest <-
  ride.Test %>%
  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 %>%
  select(week, Regression, MAE, sd_AE) %>%
  arrange(week, Regression) %>%
  kable(
    format = "html",
    digits = 2,
    col.names = c("Week", "Model", "Mean Absolute Error (MAE)", "SD of Absolute Error (RMSE)"),
    caption = "Weekly MAE and Standard Deviation of Absolute Errors by Regression Model"
  ) %>%
  kable_styling(
    full_width = FALSE,
    position = "center",
    bootstrap_options = c("striped", "hover", "condensed")
  ) %>%
  collapse_rows(
    columns = 1,
    valign = "top",
    row_group_label_position = "stack"
  )
Weekly MAE and Standard Deviation of Absolute Errors by Regression Model
Week Model Mean Absolute Error (MAE) SD of Absolute Error (RMSE)
18 ATime_FE 0.73 0.83
BSpace_FE 0.74 0.89
CTime_Space_FE 0.73 0.82
DTime_Space_FE_timeLags 0.64 0.75
ETime_Space_FE_timeLags_holidayLags 0.64 0.75
19 ATime_FE 0.71 0.83
BSpace_FE 0.70 0.90
CTime_Space_FE 0.72 0.82
DTime_Space_FE_timeLags 0.61 0.75
ETime_Space_FE_timeLags_holidayLags 0.61 0.75

Model Evaluation and Rebalancing Plan

Model evaluation

As shown above and in the following chart, the reg4 and reg5 models yield the same and lowest MAE and RMSE on both week 18 and week 19 (testing datasets), which indicate a better performance than the other models. In the following section, we check the model performance by comparing the predicted and observed trip counts from the temporal and spatial aspects.

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 = "Mean Absolute Errors by model specification and week") +
  plotTheme

Temporal Evalution

In this section, we compare the predicted and observed trip counts from the temporal aspect. We plot the predicted and observed trip counts for each model across different time period to check whether the model capture the temporal effects of bike share demand.

  • reg1: the baseline model 1 effectively capture the temporal effects of bike share demand, but mostly during one single day, but it fails to capture the different demand across weekday and weekend.
  • reg2: the spatial model 2 didn’t capture the temporal effect of the bike share demand, because the model only capture the spatial effects of bike share demand, but not the temporal effects.
  • reg3: similar to model 1, the temporal and spatial model 3 effectively capture the temporal effects during different time period of one day for bike share demand, but it fails to capture the different demand across weekday and weekend.
  • reg4: the autoregressive model 4 effectively capture the temporal effects of bike share demand not only across different time intervals of one day, but also across different weekdays and weekends.
  • reg5: adding the holiday effects to model 4 doesn’t improve the model performance from the temporal aspect. These may be possible that there are no holiday present during the test period or there are no significant different demand for bike share during holidays.
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 = "Predicted/Observed bike share time series", subtitle = "Philadelphia; 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_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))

Spatial Evaluation

In this section, we compare the predicted and observed trip counts from the spatial aspect. We plot the predicted and observed trip counts for each model across different stations to check whether the model capture the spatial effects of bike share demand.

The map below shows that the Mean Absolute Error (MAE) of the predicted trip counts for each model across different stations. It reveals that substantially larger errors in the central urban core than in outlying areas. This pattern likely reflects the dense clustering of trip origins downtown, a unique spatial characteristic that all models fail to capture adequately.

plot1<-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)) %>%
    select(interval60, start_station, start_lon, start_lat, Observed, Prediction, Regression) %>%
    unnest() %>%
  filter(Regression == "DTime_Space_FE_timeLags") %>%
  group_by(start_station, start_lon, start_lat) %>%
  summarize(MAE = mean(abs(Observed-Prediction), na.rm = TRUE))%>%
ggplot(.)+
  geom_sf(data = philCensus, color = "grey", fill = "transparent")+
  geom_point(aes(x = start_lon, y = start_lat, color = MAE),
             fill = "transparent", alpha = 0.7)+
  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="Mean Abs Error, Test Set, Model 4")+
  mapTheme

plot2<-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)) %>%
    select(interval60, start_station, start_lon, start_lat, Observed, Prediction, Regression) %>%
    unnest() %>%
  filter(Regression == "CTime_Space_FE") %>%
  group_by(start_station, start_lon, start_lat) %>%
  summarize(MAE = mean(abs(Observed-Prediction), na.rm = TRUE))%>%
ggplot(.)+
  geom_sf(data = philCensus, color = "grey", fill = "transparent")+
  geom_point(aes(x = start_lon, y = start_lat, color = MAE),
             fill = "transparent", alpha = 0.7)+
  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="Mean Abs Error, Test Set, Model 3")+
  mapTheme

plot3<-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)) %>%
    select(interval60, start_station, start_lon, start_lat, Observed, Prediction, Regression) %>%
    unnest() %>%
  filter(Regression == "BSpace_FE") %>%
  group_by(start_station, start_lon, start_lat) %>%
  summarize(MAE = mean(abs(Observed-Prediction), na.rm = TRUE))%>%
ggplot(.)+
  geom_sf(data = philCensus, color = "grey", fill = "transparent")+
  geom_point(aes(x = start_lon, y = start_lat, color = MAE),
             fill = "transparent", alpha = 0.7)+
  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="Mean Abs Error, Test Set, Model 2")+
  mapTheme

plot4<- 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)) %>%
    select(interval60, start_station, start_lon, start_lat, Observed, Prediction, Regression) %>%
    unnest() %>%
  filter(Regression == "ATime_FE") %>%
  group_by(start_station, start_lon, start_lat) %>%
  summarize(MAE = mean(abs(Observed-Prediction), na.rm = TRUE))%>%
ggplot(.)+
  geom_sf(data = philCensus, color = "grey", fill = "transparent")+
  geom_point(aes(x = start_lon, y = start_lat, color = MAE),
             fill = "transparent", alpha = 0.7)+
  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="Mean Abs Error, Test Set, Model 1")+
  mapTheme

plot5<- 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)) %>%
    select(interval60, start_station, start_lon, start_lat, Observed, Prediction, Regression) %>%
    unnest() %>%
  filter(Regression == "ETime_Space_FE_timeLags_holidayLags") %>%
  group_by(start_station, start_lon, start_lat) %>%
  summarize(MAE = mean(abs(Observed-Prediction), na.rm = TRUE))%>%
  ggplot(.)+
  geom_sf(data = philCensus, color = "grey", fill = "transparent")+
  geom_point(aes(x = start_lon, y = start_lat, color = MAE),
             fill = "transparent", alpha = 0.7)+
  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="Mean Abs Error, Test Set, Model 5")+
  mapTheme

library(gridExtra)
library(grid)
library(ggpubr)
library(patchwork)


library(patchwork)

plot4 + plot3 + plot3 + plot1 + plot5 +
  plot_layout(ncol = 3) +
  plot_annotation(
    title = "Mean Absolute Errors, Test Set",
    theme = theme(plot.title = element_text(size = 12, face = "bold"))
  )

## Error Exploration

The spatial MAE panels reveal that prediction errors concentrate in the city’s core—most notably during weekday AM and PM rush—where MAE frequently. By contrast, mid‐day and overnight intervals show uniformly low errors, reflecting both lower absolute volumes and more regular demand patterns. Weekend rush periods also exhibit smaller MAE than weekday peaks. These patterns point to systematic under‐ or over‐prediction where trip starts are both densest and most volatile, and where simple lag‐based linear effects struggle to capture sudden surges or drop‐offs in usage.

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) ) %>%
    select(interval60, start_station, start_lon,
           start_lat, Observed, Prediction, Regression,
           dotw) %>%
    unnest() %>%
  filter(Regression == "ETime_Space_FE_timeLags_holidayLags")%>%
  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 = philCensus, color = "grey", fill = "transparent")+
  geom_point(aes(x = start_lon, y = start_lat, color = MAE),
             fill = "transparent", size = 3, 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))+
  facet_grid(weekend~time_of_day)+
  labs(title="Mean Absolute Errors, Test Set")+
  mapTheme

Socio-Economic Factors

In addition, although the current model include weather, temporal variable, temporal lagged variables, and even holiday effects, the model do omit the critical socio-economic and land-use factors that may also influence the bikeshare demand. As shown in following charts, median household income, public transportation access and percentage of racial group all show a correlation with the error term of the models, which indicate the model may be improved by including these variables.

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),
           Percent_Taking_Public_Trans = map(data, pull, Percent_Taking_Public_Trans),
           Med_Inc = map(data, pull, Med_Inc),
           Percent_White = map(data, pull, Percent_White)) %>%
    select(interval60, start_station, start_lon,
           start_lat, Observed, Prediction, Regression,
           dotw, Percent_Taking_Public_Trans, Med_Inc, Percent_White) %>%
    unnest() %>%
  filter(Regression == "ETime_Space_FE_timeLags_holidayLags")%>%
  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")) %>%
  filter(time_of_day == "AM Rush") %>%
  group_by(start_station, Percent_Taking_Public_Trans, Med_Inc, Percent_White) %>%
  summarize(MAE = mean(abs(Observed-Prediction), na.rm = TRUE))%>%
  gather(-start_station, -MAE, key = "variable", value = "value")%>%
  ggplot(.)+
  #geom_sf(data = chicagoCensus, color = "grey", fill = "transparent")+
  geom_point(aes(x = value, y = MAE), alpha = 0.4)+
  geom_smooth(aes(x = value, y = MAE), method = "lm", se= FALSE)+
  facet_wrap(~variable, scales = "free")+
  labs(title="Errors as a function of socio-economic variables",
       y="Mean Absolute Error (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, size=0.8))

Potential improvment

For potential model improvement, the bikeshare data were considered count data, which not strictly considered as continuous variable. The relationship between the count data and the predictors are not perfect linear relationship. The model could be improved by using Poisson regression or Negative Binomial regression, which are more suitable for count data and reduce heterogeneity.

In addition, enrich the feature set with the socio-economic and land-use, and experiment with interaction terms (e.g., hour × precipitation, station × holiday) or tree-based learners (e.g., gradient boosting) to capture nonlinearities—steps that together should reduce central-city MAE and better handle demand surges.

Rabalancing Application

Based on the model predictions described above, the rebalancing strategy can be significantly enhanced by targeting both when and where bike shortages or surpluses are most likely to occur. The reg4 model incorporating spatial, temporal, and lagged demand features, predicts bike-share usage at the station-hour level with strong accuracy. This allows operators to allocate rebalancing trucks proactively toward high-demand stations during critical periods, especially during PM rush hours. Since the ridership is higher in Philadelphia’s dense downtown core, most of the bikes will need to put back into the central cities areas. By anticipating demand peaks several hours ahead, managers can schedule truck deployments more efficiently to avoid both bike shortages at high-usage stations and overflow at popular return stations.

Additionally, the model’s time-specific forecasts could support dynamic incentive programs. For instance, users could be encouraged through discounts or credits to pick up bikes from surplus stations or return bikes to deficit stations, based on near-future predictions. This dual approach—targeted truck reallocation and demand-shaping incentives—can help maintain a better system balance, improve user satisfaction, and reduce the operational burden on rebalancing teams.

Conclusion

The model built for predicting bike-share demand gave useful forecasts at the station and hour level to ensure the efficiency and effective of the system. The best model (reg4) accurately captured how bike demand changes during the day and between weekdays and weekends. This helps bike-share operators plan ahead by sending rebalancing trucks to busy stations before shortages happen, especially during afternoon rush hours in downtown Philadelphia. The model can also support offering incentives to users, encouraging them to pick up or drop off bikes where they are most needed.

However, accurately predicting bikeshare demand still presents some challenges. The model had bigger prediction errors in the downtown area during peak times, where demand changes quickly. Also, it did not include important factors like income levels or access to public transit, which seem to affect bike demand. In the future, the model could be improved by adding these kinds of data and by using better methods for count data, like Poisson regression or more advanced machine learning models, to make the predictions even more accurate.

LS0tCnRpdGxlOiAiQXNzaWdubWVudCA1OiBTcGFjZS1UaW1lIFByZWRpY3Rpb24gb2YgQmlrZSBTaGFyZSBEZW1hbmQiCmF1dGhvcjogIlpoYW5jaGFvIFlhbmciCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogY2VydWxlYW4KICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIHRvYzogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB0cnVlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoc2YpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KHRpZ3JpcykKbGlicmFyeSh0aWR5Y2Vuc3VzKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkocmllbSkKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShSU29jcmF0YSkKYGBgCgpgYGB7ciwgaW5jbHVkZT1GQUxTRX0KcGxvdFRoZW1lIDwtIHRoZW1lKAogIHBsb3QudGl0bGUgPWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemUgPSA2KSwKICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAjIFNldCB0aGUgZW50aXJlIGNoYXJ0IHJlZ2lvbiB0byBibGFuawogIHBhbmVsLmJhY2tncm91bmQ9ZWxlbWVudF9ibGFuaygpLAogIHBsb3QuYmFja2dyb3VuZD1lbGVtZW50X2JsYW5rKCksCiAgI3BhbmVsLmJvcmRlcj1lbGVtZW50X3JlY3QoY29sb3VyPSIjRjBGMEYwIiksCiAgIyBGb3JtYXQgdGhlIGdyaWQKICBwYW5lbC5ncmlkLm1ham9yPWVsZW1lbnRfbGluZShjb2xvdXI9IiNEMEQwRDAiLHNpemU9LjIpLAogIGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpKQoKbWFwVGhlbWUgPC0gdGhlbWUocGxvdC50aXRsZSA9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgICAgICAgICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksCiAgICAgICAgICAgICAgICAgIGF4aXMubGluZT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBheGlzLnRpY2tzPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgcGFuZWwuYmFja2dyb3VuZD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIHBhbmVsLmJvcmRlcj1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3I9ZWxlbWVudF9saW5lKGNvbG91ciA9ICd0cmFuc3BhcmVudCcpLAogICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIsCiAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsCiAgICAgICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDEsIDEsIDEsIDEsICdjbScpLAogICAgICAgICAgICAgICAgICBsZWdlbmQua2V5LmhlaWdodCA9IHVuaXQoMSwgImNtIiksIGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KDAuMiwgImNtIikpCgpwYWxldHRlNSA8LSBjKCIjMTI0RTc4IiwiI0YwRjBDOSIsIiNGMkJCMDUiLCIjRDc0RTA5IiwiIzZFMEUwQSIpCnBhbGV0dGU0IDwtIGMoIiNEMkZCRDQiLCIjOTJCQ0FCIiwiIzUyN0Q4MiIsIiMxMjNGNUEiKQpwYWxldHRlMiA8LSBjKCIjNmJhZWQ2IiwiIzA4NTE5YyIpCmBgYAojIEludHJvZHVjdGlvbgoKIyMgQmlrZSBTaGFyZSBTeXN0ZW1zIFJvbGUgaW4gVXJiYW4gTW9iaWxpdGllcwoKQmlrZSBzaGFyZSBzeXN0ZW0gYXJlIG9uZSBvZiB0aGUgY3JpdGljYWwgY29tcG9uZW50cyBvZiB1cmJhbiBtb2JpbGl0eS4gVGhleSBwcm92aWRlIGEgZmxleGlibGUgYW5kIGNvbnZlbmllbnQgbW9kZSBvZiB0cmFuc3BvcnRhdGlvbiBmb3Igc2hvcnQgdHJpcHMsIHJlZHVjaW5nIGNvbmdlc3Rpb24gYW5kIHByb21vdGluZyBzdXN0YWluYWJsZSB0cmF2ZWwuIFRoZSBzeXN0ZW1zIGFyZSBkZXNpZ25lZCB0byBiZSB1c2VyLWZyaWVuZGx5LCB3aXRoIGVhc3kgYWNjZXNzIHRvIGJpa2VzIGFuZCBkb2NraW5nIHN0YXRpb25zLCBtYWtpbmcgdGhlbSBhbiBhdHRyYWN0aXZlIG9wdGlvbiBmb3IgY29tbXV0ZXJzIGFuZCB0b3VyaXN0cyBhbGlrZS4gQXQgdGhlIHNhbWUgdGltZSwgYmlrZSBzaGFyaW5nIHN5c3RlbXMgZmlsbCBhIGdhcCBpbiB0aGUgcHVibGljIHRyYW5zcG9ydGF0aW9uIHN5c3RlbSwgcHJvdmlkaW5nIGEgbGFzdC1taWxlIHNvbHV0aW9uIGZvciB1c2VycyB3aG8gbmVlZCB0byB0cmF2ZWwgc2hvcnQgZGlzdGFuY2VzLiBCeSBpbnRlZ3JhdGluZyBiaWtlIHNoYXJpbmcgd2l0aCBvdGhlciBtb2RlcyBvZiB0cmFuc3BvcnRhdGlvbiwgc3VjaCBhcyBidXNlcyBhbmQgdHJhaW5zLCBjaXRpZXMgY2FuIGNyZWF0ZSBhIG1vcmUgZWZmaWNpZW50IGFuZCBzdXN0YWluYWJsZSB0cmFuc3BvcnRhdGlvbiBuZXR3b3JrLgoKIyMgUmViYWxhbmNpbmcgUHJvYmxlbXMgaW4gQmlrZSBTaGFyaW5nIFN5c3RlbXMKCk9uZSBvZiB0aGUga2V5IG9wZXJhdGlvbmFsIGNoYWxsZW5nZXMgaW4gYmlrZS1zaGFyZSBzeXN0ZW0gaXMgdG8gbWFpbnRhaW4gYSBiYWxhbmNlZCBkaXN0cmlidXRpb24gYWNyb3NzIHN0YXRpb25zIGFuZCB0aW1lcyB0byBtZWV0IHVzZXIgZGVtYW5kcy4gU29tZSBzdGF0aW9ucyBmcmVxdWVudGx5IGV4cGVyaWVuY2UgYmlrZSBzaG9ydGFnZXMgd2hlcmUgdGhlIHVzZXJzIGNhbm5vdCBnZXQgYSBiaWtlIGZyb20gdGhlIGRvY2tzIHdoZW4gdGhleSBuZWVkIHRoZW0uIEhvd2V2ZXIsIHNvbWUgc3RhdGlvbnMgZnJlcXVlbnRseSBleHBlcmllbmNlIHN1cnBsdXNlcyB3aGVyZSB0aGUgdXNlcnMgY2Fubm90IHJldHVybiB0aGUgYmlrZXMgdG8gdGhlIGRvY2tzIHdoZW4gdGhleSBuZWVkIHRoZW0uIFRoaXMgcmViYWxhbmNlIGlzc3VlcyBiZWNvbWUgZXh0cmVtZWx5IHNlcmlvdXMgZHVyaW5nIHRoZSBwZWFrIGhvdXJzIGR1ZSB0byBhc3N5bWV0cmljIGNvbW11dGluZyBwYXR0ZXJucy4gV2l0aG91dCB0aW1lbHkgcmViYWxhbmNpbmcsIHVzZXJzIG1heSBmaW5kIGl0IGRpZmZpY3VsdCB0byByZW50IG9yIHJldHVybiBiaWtlcyBvbiB0aW1seSBtYW5uZXJzLCBsZWFkaW5nIHRvIHVzZXIgZGlzc2F0aXNmYWN0aW9uIGFuZCByZWR1Y2VkIHJpZGVyc2hpcC4gRWZmaWNpZW50IHJlYmxhbmNpbmcgYWhlYWQgb2YgdXNlciBkZW1hbmRzIGVuc3VyZXMgdGhlIHN5c3RlbSdzIGF2YWlsYWJpbGl0eSBhbmQgcmVsaWFiaWxpdHksIHdoaWNoIGFsc28gaW52b2x2ZXMgcmVhbC10aW1lIGRhdGEgYW5hbHlzaXMgYW5kIHByZWRpY3Rpb24uCgojIyBQcm9wb3NlZCBSZWJhbGFuY2luZyBTdHJhdGVnaWVzCgpUbyBhZGRyZXNzIHRoaXMgaXNzdWUsIHdlIHByb3Bvc2UgYSBkYXRhLWRyaXZlbiBhcHByb2FjaCB0byBwcmVkaWN0IHRoZSBiaWtlIGRlbWFuZCBhY3Jvc3MgZWFjaCBzdGF0aW9ucyBhbmQgdGltZSBpbnRlcnZhbHMuIEJ5IGFuYWx5emluZyBoaXN0b3JpY2FsIGRhdGEsIHdlIGNhbiBpZGVudGlmeSBwYXR0ZXJucyBhbmQgdHJlbmRzIGluIGJpa2UgdXNhZ2UsIGFsbG93aW5nIHVzIHRvIG1ha2UgaW5mb3JtZWQgZGVjaXNpb25zIGFib3V0IHdoZXJlIGFuZCB3aGVuIHRvIHJlZGlzdHJpYnV0ZSBiaWtlcy4gVGhpcyBhcHByb2FjaCBub3Qgb25seSBpbXByb3ZlcyB0aGUgZWZmaWNpZW5jeSBvZiB0aGUgYmlrZS1zaGFyZSBzeXN0ZW0gYnV0IGFsc28gZW5oYW5jZXMgdGhlIG92ZXJhbGwgdXNlciBleHBlcmllbmNlIGJ5IGVuc3VyaW5nIHRoYXQgYmlrZXMgYXJlIGF2YWlsYWJsZSB3aGVuIGFuZCB3aGVyZSB0aGV5IGFyZSBuZWVkZWQuIFByZWRpY3Rpb25zIHdpbGwgYmUgbWFkZSBzZXZlcmFsIGhvdXJzIGluIGFkdmFuY2UgdXNpbmcgdGhlIG1vZGVscyB0aGF0IGluY29ycG9yYXRlIGZlYXR1cmVzIHN1Y2ggYXMgdGltZSBvZiBkYXksIGRheSBvZiB3ZWVrLCBoaXN0b3JpY2FsIHVzYWdlIHBhdHRlcm5zLCB3ZWF0aGVyIGNvbmRpdGlvbnMsIGFuZCBjdXJyZW50IHN0YXRpb24gY2FwYWNpdHkuIEJ5IGxldmVyYWdpbmcgdGhlc2UgZGF0YSBzb3VyY2VzLCB3ZSBjYW4gb3B0aW1pemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBiaWtlcyBhY3Jvc3MgdGhlIHN5c3RlbSwgcmVkdWNpbmcgdGhlIGxpa2VsaWhvb2Qgb2Ygc2hvcnRhZ2VzIGFuZCBzdXJwbHVzZXMuCgpUaGUgbW9kZWwgd2lsbCBiZSBmb2N1c2VkIHJlYmFsYW5jZSBlZmZvcnRzIG9uIHRoZSBzdGF0aW9ucyBpbiBQaGlsYWRlbHBoaWEsIFBBLiBUaGUgbW9kZWwgd2lsbCBiZSB0cmFpbmVkIHVzaW5nIHRoZSBoaXN0b3JpY2FsIGJpa2Ugc2hhcmUgZGF0YSBmb3IgdGhlIG1vbnRoIG9mIE1heSwgMjAyMi4gVGhlIG1vZGVsIHdpbGwgYmUgdXNlZCB0byBwcmVkaWN0IHRoZSBiaWtlIGRlbWFuZCBmb3IgdGhlIG5leHQgMjQgaG91cnMsIGFuZCB0aGUgcmVzdWx0cyB3aWxsIGJlIHVzZWQgdG8gaW5mb3JtIHJlYmFsYW5jaW5nIGVmZm9ydHMuCgojIERhdGEgUHJlcHJvY2Vzc2luZyBhbmQgRmVhdHVyZSBFbmdpbmVlcmluZwoKIyMgRGF0YSBQcmVwYXJhdGlvbgoKIyMjIEJpa2UgZGF0YQoKVGhlIGJpa2Ugc2hhcmUgZGF0YSBpcyBvYnRhaW5lZCBmcm9tIHRoZSBJbmRlZ28uIFRoZSBkYXRhIGluY2x1ZGVzIGluZm9ybWF0aW9uIG9uIGJpa2UgdHJpcHMsIGluY2x1ZGluZyBzdGFydCBhbmQgZW5kIHRpbWVzLCBzdGFydCBhbmQgZW5kIHN0YXRpb25zLCBhbmQgYmlrZSBJRHMuIFRoZSBkYXRhIGlzIGNsZWFuZWQgYW5kIHByZXByb2Nlc3NlZCB0byByZW1vdmUgYW55IGR1cGxpY2F0ZXMgb3IgbWlzc2luZyB2YWx1ZXMuIFRoZSBkYXRhIGlzIHRoZW4gYWdncmVnYXRlZCB0byBob3VybHkgaW50ZXJ2YWxzIHRvIGNyZWF0ZSBhIHRpbWUgc2VyaWVzIG9mIGJpa2UgZGVtYW5kIGZvciBlYWNoIHN0YXRpb24uIE1heSAyMDIyIGRhdGEgd2FzIHNlbGVjdGVkIGZvciB0aGlzIGFuYWx5c2lzIGFzIHRoZSB0ZW1wb3JhbCBmcmFtZS4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpiaWtlPC1yZWFkLmNzdigiZGF0YS9pbmRlZ28tdHJpcHMtMjAyMi1xMi5jc3YiKQoKCm1heSA8LSBiaWtlICU+JQogIG11dGF0ZShzdGFydF90aW1lID0gbWR5X2htKHN0YXJ0X3RpbWUpKSAlPiUKICBmaWx0ZXIobW9udGgoc3RhcnRfdGltZSkgPT0gNSkgJT4lCiAgbXV0YXRlKGludGVydmFsNjAgPSBmbG9vcl9kYXRlKHltZF9obXMoc3RhcnRfdGltZSksIHVuaXQgPSAiaG91ciIpLAogICAgICAgICBpbnRlcnZhbDE1ID0gZmxvb3JfZGF0ZSh5bWRfaG1zKHN0YXJ0X3RpbWUpLCB1bml0ID0gIjE1IG1pbnMiKSwKICAgICAgICAgd2VlayA9IHdlZWsoaW50ZXJ2YWw2MCksCiAgICAgICAgIGRvdHcgPSB3ZGF5KGludGVydmFsNjAsIGxhYmVsPVRSVUUpKQoKbWF5PC1tYXklPiUKICBmaWx0ZXIoIWlzLm5hKHltZF9obXMoc3RhcnRfdGltZSkpKQpgYGAKCiMjIyBQb3B1bGF0aW9uIGRhdGEKCkF0IHRoZSBzYW1lIHRpbWUsIGNlbnN1cyB0cmFjdCBnZW9tZXRyaWVzIG9mIHRoZSBlbnRpcmUgUGhpbGFkZWxwaGlhIGNvdW50eSB3YXMgYWNxdWlyZWQgZnJvbSB0aGUgVVMgQ2Vuc3VzIEJ1cmVhdSB0byBwcm92aWRlIHRoZSBnZW9ncmFwaGljIHpvbmUgYm91bmRhcmllcyBmb3IgZnVydGhlciBhbmFseXNpcyBpZiBuZWVkZWQuCgpgYGB7ciwgcmVzdWx0cz0naGlkZScsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnBoaWxDZW5zdXMgPC0KICBnZXRfYWNzKGdlb2dyYXBoeSA9ICJ0cmFjdCIsCiAgICAgICAgICB2YXJpYWJsZXMgPSBjKCJCMDEwMDNfMDAxIiwgIkIxOTAxM18wMDEiLAogICAgICAgICAgICAgICAgICAgICAgICAiQjAyMDAxXzAwMiIsICJCMDgwMTNfMDAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgIkIwODAxMl8wMDEiLCAiQjA4MzAxXzAwMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJCMDgzMDFfMDEwIiwgIkIwMTAwMl8wMDEiKSwKICAgICAgICAgIHllYXIgPSAyMDIyLAogICAgICAgICAgc3RhdGUgPSAiUEEiLAogICAgICAgICAgZ2VvbWV0cnkgPSBUUlVFLAogICAgICAgICAgY291bnR5PWMoIlBoaWxhZGVscGhpYSIpLAogICAgICAgICAgb3V0cHV0ID0gIndpZGUiKSAlPiUKICByZW5hbWUoVG90YWxfUG9wID0gIEIwMTAwM18wMDFFLAogICAgICAgICBNZWRfSW5jID0gQjE5MDEzXzAwMUUsCiAgICAgICAgIE1lZF9BZ2UgPSBCMDEwMDJfMDAxRSwKICAgICAgICAgV2hpdGVfUG9wID0gQjAyMDAxXzAwMkUsCiAgICAgICAgIFRyYXZlbF9UaW1lID0gQjA4MDEzXzAwMUUsCiAgICAgICAgIE51bV9Db21tdXRlcnMgPSBCMDgwMTJfMDAxRSwKICAgICAgICAgTWVhbnNfb2ZfVHJhbnNwb3J0ID0gQjA4MzAxXzAwMUUsCiAgICAgICAgIFRvdGFsX1B1YmxpY19UcmFucyA9IEIwODMwMV8wMTBFKSAlPiUKICBzZWxlY3QoVG90YWxfUG9wLCBNZWRfSW5jLCBXaGl0ZV9Qb3AsIFRyYXZlbF9UaW1lLAogICAgICAgICBNZWFuc19vZl9UcmFuc3BvcnQsIFRvdGFsX1B1YmxpY19UcmFucywKICAgICAgICAgTWVkX0FnZSwKICAgICAgICAgR0VPSUQsIGdlb21ldHJ5KSAlPiUKICBtdXRhdGUoUGVyY2VudF9XaGl0ZSA9IFdoaXRlX1BvcCAvIFRvdGFsX1BvcCwKICAgICAgICAgTWVhbl9Db21tdXRlX1RpbWUgPSBUcmF2ZWxfVGltZSAvIFRvdGFsX1B1YmxpY19UcmFucywKICAgICAgICAgUGVyY2VudF9UYWtpbmdfUHVibGljX1RyYW5zID0gVG90YWxfUHVibGljX1RyYW5zIC8gTWVhbnNfb2ZfVHJhbnNwb3J0KQoKcGhpbFRyYWN0cyA8LQogIHBoaWxDZW5zdXMgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIGRpc3RpbmN0KEdFT0lELCAua2VlcF9hbGwgPSBUUlVFKSAlPiUKICBzZWxlY3QoR0VPSUQsIGdlb21ldHJ5KSAlPiUKICBzdF9zZgpgYGAKClNpbmNlIHRoZSByYXcgYmlrZXNoYXJlIGRhdGEgaXMgYSBub24tc3BhdGlhbCBsYXllciwgb25seSBjb250YWluaW5nIGxhdGl0dXRlIGFuZCBsb25naXR1ZGUgb2YgdGhlIHN0YXJ0IGFuZCBlbmQgc3RhdGlvbnMsIHdlIG5lZWQgdG8gam9pbiB0aGUgYmlrZXNoYXJlIGRhdGEgd2l0aCB0aGUgY2Vuc3VzIHRyYWN0IGRhdGEgdG8gb2J0YWluIHRoZSBjZW5zdXMgdHJhY3QgaW5mb3JtYXRpb24gZm9yIGVhY2ggdHJpcC4gVGhlIGBzdF9qb2luYCBmdW5jdGlvbiBmcm9tIHRoZSBgc2ZgIHBhY2thZ2UgaXMgdXNlZCB0byBwZXJmb3JtIGEgc3BhdGlhbCBqb2luIGJldHdlZW4gdGhlIGJpa2VzaGFyZSBkYXRhIGFuZCB0aGUgY2Vuc3VzIHRyYWN0IGRhdGEuIFdlIGFyZSBvbmx5IGludGVyZXN0ZWQgaW4gdGhlIHN0YXJ0IGxvY2F0aW9uIG9mIGVhY2ggcmlkZSBiZWNhdXNlIHdlIHdhbnQgdG8gcHJlZGljdCB0aGUgY2FwYWNpdHkgb2YgZWFjaCBzdGF0aW9uIHdoZW4gdGhlIHVzZXIgbmVlZCBhIGJpa2UgdG8gc3RhcnQgdGhlIHJpZGUuIFRoaXMgcmVxdWlyZXMgdXMgdG8gZmlsdGVyIG91dCByb3dzIHdpdGggbWlzc2luZyBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlLgpgYGB7cn0KZGF0X2NlbnN1cyA8LSBzdF9qb2luKG1heSAlPiUKICAgICAgICAgIGZpbHRlcihpcy5uYShzdGFydF9sb24pID09IEZBTFNFICYKICAgICAgICAgICAgICAgICAgIGlzLm5hKHN0YXJ0X2xhdCkgPT0gRkFMU0UgJgogICAgICAgICAgICAgICAgICAgaXMubmEoZW5kX2xvbikgPT0gRkFMU0UgJgogICAgICAgICAgICAgICAgICAgaXMubmEoZW5kX2xhdCkgPT0gRkFMU0UpICU+JQogICAgICAgICAgc3RfYXNfc2YoLiwgY29vcmRzID0gYygic3RhcnRfbG9uIiwgInN0YXJ0X2xhdCIpLCBjcnMgPSA0MzI2KSwKICAgICAgICBwaGlsVHJhY3RzICU+JQogICAgICAgICAgc3RfdHJhbnNmb3JtKGNycz00MzI2KSwKICAgICAgICBqb2luPXN0X2ludGVyc2VjdHMsCiAgICAgICAgICAgICAgbGVmdCA9IFRSVUUpICU+JQogIHJlbmFtZShPcmlnaW4uVHJhY3QgPSBHRU9JRCkgJT4lCiAgbXV0YXRlKHN0YXJ0X2xvbiA9IHVubGlzdChtYXAoZ2VvbWV0cnksIDEpKSwKICAgICAgICAgc3RhcnRfbGF0ID0gdW5saXN0KG1hcChnZW9tZXRyeSwgMikpKSU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBzZWxlY3QoLWdlb21ldHJ5KSU+JQogIHN0X2FzX3NmKC4sIGNvb3JkcyA9IGMoImVuZF9sb24iLCAiZW5kX2xhdCIpLCBjcnMgPSA0MzI2KSAlPiUKICBzdF9qb2luKC4sIHBoaWxUcmFjdHMgJT4lCiAgICAgICAgICAgIHN0X3RyYW5zZm9ybShjcnM9NDMyNiksCiAgICAgICAgICBqb2luPXN0X2ludGVyc2VjdHMsCiAgICAgICAgICBsZWZ0ID0gVFJVRSkgJT4lCiAgcmVuYW1lKERlc3RpbmF0aW9uLlRyYWN0ID0gR0VPSUQpICAlPiUKICBtdXRhdGUodG9fbG9uZ2l0dWRlID0gdW5saXN0KG1hcChnZW9tZXRyeSwgMSkpLAogICAgICAgICB0b19sYXRpdHVkZSA9IHVubGlzdChtYXAoZ2VvbWV0cnksIDIpKSklPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgc2VsZWN0KC1nZW9tZXRyeSkKYGBgCgpUaGUgZm9sbG93aW5nIG1hcCBzaG93cyBhbGwgdGhlIHN0YXJ0aW5nIHBvaW50IG9mIHRoZSByaWRlcyBpbiBQaGlsYWRlbHBoaWEgZHVyaW5nIE1heSAyMDIyLgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9Nn0KZ2dwbG90KCkrCiAgZ2VvbV9zZihkYXRhID0gcGhpbFRyYWN0cyAlPiUKICAgICAgICAgIHN0X3RyYW5zZm9ybShjcnM9NDMyNiksIGZpbGw9IndoaXRlIikrCiAgZ2VvbV9wb2ludChkYXRhID0gbWF5LCBhZXMoeD1zdGFydF9sb24sIHkgPSBzdGFydF9sYXQpLAogICAgICAgICAgICBjb2xvciA9ICIjNkUwRTBBIiwgYWxwaGEgPSAxLCBzaXplID0gMC4zKSArCiAgeWxpbShtaW4obWF5JHN0YXJ0X2xhdCksIG1heChtYXkkc3RhcnRfbGF0KSkrCiAgeGxpbShtaW4obWF5JHN0YXJ0X2xvbiksIG1heChtYXkkc3RhcnRfbG9uKSkgKwogIGxhYnModGl0bGUgPSAiTG9jYXRpb24gb2YgUmlkZXMgU3RhcnRpbmcgUG9pbnRzIGluIFBoaWxhZGVscGhpYSIpKwogIG1hcFRoZW1lCmBgYAojIyMgV2VhdGhlciBkYXRhClRoZSB3ZWF0aGVyIGRhdGEgaXMgb2J0YWluZWQgZnJvbSB0aGUgTmF0aW9uYWwgV2VhdGhlciBTZXJ2aWNlIChQaGlsYWRlbHBoaWEgSW50ZXJuYXRpb25hbCBBaXJwb3J0KS4gVGhlIGRhdGEgaW5jbHVkZXMgaW5mb3JtYXRpb24gb24gdGVtcGVyYXR1cmUsIHByZWNpcGl0YXRpb24sIGFuZCB3aW5kIHNwZWVkLiBUaGUgZGF0YSBpcyBjbGVhbmVkIGFuZCBwcmVwcm9jZXNzZWQgdG8gcmVtb3ZlIGFueSBkdXBsaWNhdGVzIG9yIG1pc3NpbmcgdmFsdWVzLiBUaGUgZGF0YSBpcyB0aGVuIGFnZ3JlZ2F0ZWQgdG8gaG91cmx5IGludGVydmFscyB0byBjcmVhdGUgYSB0aW1lIHNlcmllcyBvZiB3ZWF0aGVyIGNvbmRpdGlvbnMgZm9yIGVhY2ggc3RhdGlvbi4KCmBgYHtyfQp3ZWF0aGVyLlBhbmVsIDwtCiAgcmllbV9tZWFzdXJlcyhzdGF0aW9uID0gIlBITCIsIGRhdGVfc3RhcnQgPSAiMjAyMi0wNS0wMSIsIGRhdGVfZW5kID0gIjIwMjItMDUtMzEiKSAlPiUKICBkcGx5cjo6c2VsZWN0KHZhbGlkLCB0bXBmLCBwMDFpLCBza250KSU+JQogIHJlcGxhY2UoaXMubmEoLiksIDApICU+JQogICAgbXV0YXRlKGludGVydmFsNjAgPSB5bWRfaChzdWJzdHIodmFsaWQsMSwxMykpKSAlPiUKICAgIG11dGF0ZSh3ZWVrID0gd2VlayhpbnRlcnZhbDYwKSwKICAgICAgICAgICBkb3R3ID0gd2RheShpbnRlcnZhbDYwLCBsYWJlbD1UUlVFKSkgJT4lCiAgICBncm91cF9ieShpbnRlcnZhbDYwKSAlPiUKICAgIHN1bW1hcml6ZShUZW1wZXJhdHVyZSA9IG1heCh0bXBmKSwKICAgICAgICAgICAgICBQcmVjaXBpdGF0aW9uID0gc3VtKHAwMWkpLAogICAgICAgICAgICAgIFdpbmRfU3BlZWQgPSBtYXgoc2tudCkpICU+JQogICAgbXV0YXRlKFRlbXBlcmF0dXJlID0gaWZlbHNlKFRlbXBlcmF0dXJlID09IDAsIDQyLCBUZW1wZXJhdHVyZSkpCmBgYAoKIyMgRmVhdHVyZSBFbmdpbmVlcmluZyAmIEV4cGxvcmF0b3J5IEFuYWx5c2lzCgojIyMgVGVtcG9yYWwgQW5hbHlzaXMKClN0YXJ0aW5nIHdpdGggdGVtcG9yYWwgYW5hbHlzaXMsIHdlIGZpcnN0IHZpc3VhbGl6ZWQgdGhlIGJpa2VzaGFyZSB0cmlwcyBwZXIgaG91ciBpbiBQaGlsYWRlbHBoaWEgaW4gTWF5IDIwMjMuIEl0IGlzIGNsZWFyIHRoYXQgdGhlcmUncyBzb21lIGZvcm0gb2YgY2lyY3VsYXJpdHkgaW4gdGhlIGRhdGEgYXQgYm90aCBwZWFrIGhvdXJzIGFuZCBub24tcGVhayBob3VycyBkdXJpbmcgc2luZ2xlIGRheS4gV2UgbWF5IGFsc28gc2VlIHRoYXQgYWZ0ZXIgZml2ZSBudW1iZXIgb2YgaGlnaCBwZWFrcyBhcmUgdXN1YWxseSBieSB0d28gbG93ZXIgcGVha3MgKHdlZWtkYXkgYW5kIHdlZWtlbmQpLiBUaGVzZSBjb3JyZXNwb25kIHRoZSB0cmVuZHMgbWVudGlvbmVkIGluIHRoZSBpbnRyb2R1Y3Rpb24gc2VjdGlvbiB0aGF0IGJpa2VzaGFyZSBhcmUgbXVjaCBtb3JlIG5lZWRlZCBkdXJpbmcgdGhlIHdlZWtkYXlzIHJ1c2ggaG91cnMgY29tcGFyZWQgdG8gdGhlIHdlZWtlbmRzIGFuZCBsYXRlIG5pZ2h0IGhvdXJzLgoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTQsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmdncGxvdChkYXRfY2Vuc3VzICU+JQogICAgICAgICBncm91cF9ieShpbnRlcnZhbDYwKSAlPiUKICAgICAgICAgdGFsbHkoKSkrCiAgZ2VvbV9saW5lKGFlcyh4ID0gaW50ZXJ2YWw2MCwgeSA9IG4pKSsKICBsYWJzKHRpdGxlPSJCaWtlIHNoYXJlIHRyaXBzIHBlciBoci4gUGhpbGFkZWxwaGlhLCBNYXksIDIwMjIiLAogICAgICAgeD0iRGF0ZSIsCiAgICAgICB5PSJOdW1iZXIgb2YgdHJpcHMiKSsKICAgIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkpCmBgYApUaGVuLCB3ZSB2aXN1YWxpemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0cmlwcyBieSBzdGF0aW9ucyBhdCBlYWNoIGhvdXIgaW4gTWF5LiBUaGUgZGlzdHJpYnV0aW9uIGlzIHJpZ2h0IHNrZXdlZCwgd2hlcmUgbW9zdCBzdGF0aW9ucyBoYXZlIHplcm8gb3IgdmVyeSBmZXcgbnVtYmVyIG9mIHRyaXBzIGVhY2ggaG91ci4gSG93ZXZlciwgdGhlcmUgYXJlIGZldyBzdGF0aW9ucyB0aGF0IGhhdmUgYSB2ZXJ5IGhpZ2ggbnVtYmVyIG9mIHRyaXBzIGVhY2ggaG91ciBvbiBwYXJ0aWN1bGFyIGRheSBpbiBNYXkgMjAyMi4KCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD02fQpnZ3Bsb3QoZGF0X2NlbnN1cyAlPiUKICAgICAgICAgZ3JvdXBfYnkoaW50ZXJ2YWw2MCwgc3RhcnRfc3RhdGlvbikgJT4lCiAgICAgICAgIHRhbGx5KCkpKwogIGdlb21faGlzdG9ncmFtKGFlcyhuKSwgYmlud2lkdGggPSA1LCBmaWxsPSIjNkUwRTBBIikrCiAgbGFicyh0aXRsZT0iQmlrZSBzaGFyZSB0cmlwcyBwZXIgaG91ciBieSBzdGF0aW9uLiBQaGlsYWRlbHBoaWEsIE1heSwgMjAyMiIsCiAgICAgICB4PSJUcmlwIENvdW50cyIsCiAgICAgICB5PSJOdW1iZXIgb2YgU3RhdGlvbnMiKSsKICAgIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkpCmBgYAoKIyMjIFRlbXBvcmFyeSBGZWF0dXJlIEVuZ2luZWVyCgpXZSBwZXJmb3JtIGZlYXR1cmUgZW5naW5lZXJpbmcgdXNpbmcgYGx1YnJ1ZGF0ZWAgcGFja2FnZSB0byBleHRyYWN0IHRlbXBvcmFsIGluZm9ybWF0aW9uIGZvciBlYWNoIHVzYWdlLiB0aGUgdXNhZ2Ugd2FzIGNhbGN1bGF0aW5nIGludG8gNjAgbWluIGFuZCAxNSBtaW4gaW50ZXJ2YWwgYmFzZWQgb24gc3RhcmluZyB0aW1lIGZvciBlYWNoIHJpZGUuIEFuZCB0aGVuLCB0aGUgcmlkZSB3YXMgZnVydGhlciBjYXRlZ29yaXplIGludG8gIk92ZXJuaWdodCIsICJBTSBSdXNoIiwgIk1pZC1EYXkiIGFuZCAiUE0gUnVzaCIgYmFzZWQgb24gdGhlIGhvdXIgb2YgdGhlIGRheSBhbmQgaW50byB3ZWVrZGF5IGFuZCB3ZWVrZW5kIGJhc2VkIG9uIHRoZSBkYXkgb2YgdGhlIHdlZWsuCgpXZSB2aXN1YWxpemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgbWVhbiBudW1iZXIgdHJpcHMgYXQgZWFjaCBzdGF0aW9uIGR1cmluZyBkaWZmZXJlbnQgdGltZSBpbnRlcnZhbHMgb2YgdGhlIGRheS4gRHVyaW5nIHRoZSBQTSBydXNoIGhvdXIsIHRoZSBhdmVyYWdlIG51bWJlciBvZiB0cmlwcyBpcyB0aGUgaGlnaGVzdCwgZm9sbG93ZWQgYnkgdGhlIG1pZC1kYXkgYW5kIEFNIHJ1c2ggaG91ci4gVGhlIG92ZXJuaWdodCBwZXJpb2QgaGFzIHRoZSBsb3dlc3QgYXZlcmFnZSBudW1iZXIgb2YgdHJpcHMsIHdpdGggb25seSBmZXcgc3RhdGlvbiB3aXRoIGFuIGF2ZXJhZ2Ugb2YgMyB0cmlwcyBvciBtb3JlLgoKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD02fQpwYWxldHRlNCA8LSBjKCIjMTI0RTc4IiwiI0YwRjBDOSIsIiNGMkJCMDUiLCIjNkUwRTBBIikKZGF0X2NlbnN1cyAlPiUKICAgICAgICBtdXRhdGUodGltZV9vZl9kYXkgPSBjYXNlX3doZW4oaG91cihpbnRlcnZhbDYwKSA8IDcgfCBob3VyKGludGVydmFsNjApID4gMTggfiAiT3Zlcm5pZ2h0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSA3ICYgaG91cihpbnRlcnZhbDYwKSA8IDEwIH4gIkFNIFJ1c2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3VyKGludGVydmFsNjApID49IDEwICYgaG91cihpbnRlcnZhbDYwKSA8IDE1IH4gIk1pZC1EYXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3VyKGludGVydmFsNjApID49IDE1ICYgaG91cihpbnRlcnZhbDYwKSA8PSAxOCB+ICJQTSBSdXNoIikpJT4lCiAgICAgICAgIGdyb3VwX2J5KGludGVydmFsNjAsIHN0YXJ0X3N0YXRpb24sIHRpbWVfb2ZfZGF5KSAlPiUKICAgICAgICAgdGFsbHkoKSU+JQogIGdyb3VwX2J5KHN0YXJ0X3N0YXRpb24sIHRpbWVfb2ZfZGF5KSU+JQogIHN1bW1hcml6ZShtZWFuX3RyaXBzID0gbWVhbihuKSklPiUKICBnZ3Bsb3QoKSsKICBnZW9tX2hpc3RvZ3JhbShhZXMobWVhbl90cmlwcywgZmlsbD10aW1lX29mX2RheSksIGJpbndpZHRoID0gMSkrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTQsIG5hbWU9IlRpbWUgb2YgRGF5IikgKwogIGxhYnModGl0bGU9Ik1lYW4gTnVtYmVyIG9mIEhvdXJseSBUcmlwcyBQZXIgU3RhdGlvbi4gUGhpbGFkZWxwaGlhLCBNYXksIDIwMjIiLAogICAgICAgeD0iTnVtYmVyIG9mIHRyaXBzIiwKICAgICAgIHk9IkZyZXF1ZW5jeSIpKwogIGZhY2V0X3dyYXAofnRpbWVfb2ZfZGF5KSsKICAgIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkpCmBgYAoKQWZ0ZXIgdGhhdCwgd2UgdmlzdWFsaXplIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIG1lYW4gbnVtYmVyIG9mIHRyaXBzIGF0IGVhY2ggc3RhdGlvbiBkdXJpbmcgZGlmZmVyZW50IGRheXMgb2YgdGhlIHdlZWsuIEZyb20gdGhlIGNoYXJ0LCB3ZSBjYW4gc2VlIHRoYXQgdGhlIE1vbmRheSwgVHVlc2RheSwgV2VkbmVzZGF5LCBhbmQgVGh1cnNkYXkgc2hvd3Mgc2ltaWxhciB0ZW1wb3JhbCBwYXR0ZXJucywgd2l0aCBzaW1pbGFyIGFuZCBoaWdoZXN0IG51bWJlciBvZiB0cmlwcyBieSB0aGUgdGltZSBvZiB0aGUgZGF5LiBUaGUgRnJpZGF5IGFuZCBTdHVyZGF5IHNob3dzIHNpbWlsYXIgcGF0dGVybnMsIHdpdGggc21hbGxlciBudW1iZXIgb2YgcGVvcGxlIHV0aWxpemUgdGhlIHN5c3RlbS4gQW5kLCBTdW5kYXkgc2hvd3MgYSB1bmlxdWVwZWFrIHBhdHRlcm4gdGhhbiBhbnkgb3RoZXIgd2Vla2RheXMgb3Igd2Vla2VuZHMuQnV0IGluIGdlbmVyYWwsIGxvd2VzdCB0cmlwIGNvdW50IG9jY3VyIGluIHRoZSBlYXJseSBtb3JuaW5nIHdoaWxlIGhpZ2hlc3QgdHJpcCBjb3VudCBvY2N1ciBpbiB0aGUgbGF0ZSBhZnRlcm5vb24uCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpnZ3Bsb3QoZGF0X2NlbnN1cyAlPiUgbXV0YXRlKGhvdXIgPSBob3VyKHN0YXJ0X3RpbWUpKSkrCiAgICAgZ2VvbV9mcmVxcG9seShhZXMoaG91ciwgY29sb3IgPSBkb3R3KSwgYmlud2lkdGggPSAzLGx3ZD0wLjgpKwogIGxhYnModGl0bGU9IkJpa2Ugc2hhcmUgdHJpcHMgaW4gUGhpbGFkZWxwaGlhLCBieSBkYXkgb2YgdGhlIHdlZWssIE1heSwgMjAyMiIsCiAgICAgICB4PSJIb3VyIiwKICAgICAgIHk9IlRyaXAgQ291bnRzIikrCiAgICB0aGVtZShwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTApLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpKQpgYGAKV2UgYWxzbyBjb21wYXJlZCB0aGUgZGlmZmVyZW50IHVzYWdlIGJldHdlZW4gd2Vla2RheXMgYW5kIHdlZWtlbmRzLiBUaGUgY2hhcnQgc2hvd3MgdGhhdCB0aGUgd2Vla2RheSB1c2FnZSBpcyBtdWNoIGhpZ2hlciB0aGFuIHRoZSB3ZWVrZW5kIHVzYWdlLiBUaGUgd2Vla2RheSB1c2FnZSBzaG93cyBhIHNtYWxsZXIgcGVhayBkdXJpbmcgdGhlIEFNIHJ1c2ggaG91ciBhbmQgYW4gZXZlbiBiaWdnZXIgcGVhayBkdXJpbmcgdGhlIFBNIHJ1c2ggaG91ci4gVGhlIHdlZWtlbmQgdXNhZ2Ugc2hvd3MgYSBzbWFsbGVyIHBlYWsgZHVyaW5nIHRoZSBsYXRlIG1vcm5pbmcgYW5kIGVhcmx5IGFmdGVybm9vbiwgd2l0aCBhIGdyYWR1YWwgZGVjbGluZSBpbiB1c2FnZSB0aHJvdWdob3V0IHRoZSBkYXkuCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD02fQpnZ3Bsb3QoZGF0X2NlbnN1cyAlPiUKICAgICAgICAgbXV0YXRlKGhvdXIgPSBob3VyKHN0YXJ0X3RpbWUpLAogICAgICAgICAgICAgICAgd2Vla2VuZCA9IGlmZWxzZShkb3R3ICVpbiUgYygiU3VuIiwgIlNhdCIpLCAiV2Vla2VuZCIsICJXZWVrZGF5IikpKSsKICAgICBnZW9tX2ZyZXFwb2x5KGFlcyhob3VyLCBjb2xvciA9IHdlZWtlbmQpLCBiaW53aWR0aCA9IDEpKwogIGxhYnModGl0bGU9IkJpa2Ugc2hhcmUgdHJpcHMgaW4gUGhpbGFkZWxwaGlhIC0gd2Vla2VuZCB2cyB3ZWVrZGF5LCBNYXksIDIwMjIiLAogICAgICAgeD0iSG91ciIsCiAgICAgICB5PSJUcmlwIENvdW50cyIpKwogICAgdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEwKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIHNpemU9MC44KSkKYGBgCgojIyMgU3BhdGlhbCBhbmQgVGVtcG9yYWwgQW5hbHlzaXMKV2UgdmlzdWFsaXplIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHRyaXBzIGNvdW50IGF0IGVhY2ggc3RhdGlvbiBkdXJpbmcgZGlmZmVyZW50IHRpbWUgaW50ZXJ2YWxzIG9uIHdlZWtkYXkgYW5kIHdlZWtlbmQuIEFzIHRoZSBtYXAgc2hvd3MgYmVsb3csIGl0IGluZGljYXRlIHRoYXQgdGhlIHdlZWtkYXkgdXNhZ2UgaXMgbXVjaCBoaWdoZXIgdGhhbiB0aGUgd2Vla2VuZCB1c2FnZS4gT24gYm90aCB3ZWVrZGF5IGFuZCB3ZWVrZW5kLCBtb3N0IG9mIHRoZSB0cmlwIHN0YXJ0cyBzdGF0aW9uIGNsdXN0ZXJlZCBhcm91bmQgdGhlIGNlbnRyYWwgY2l0eSBQaGlsYWRlbHBoaWEgYXJlYSwgd2l0aCBhIGZldyB0cmlwcyBzdGFydHMgaW4gdGhlIHdlc3QgUGhpbGFkZWxwaGlhLCBTb3V0aCBQaGlsYWRlbHBoaWEuIFRoZXNlIGRpc3RyaWJ1dGlvbiBzdWdnZXN0cyB0aGF0IHRyaXAgZGVtYW5kcyBpcyBlc3BlY2lhbGx5IGhpZ2hlciBpbiB0aGUgY2VudHJhbCBjaXR5IGFyZWEgYW5kIG1ham9yIHRyYW5zcG9ydGF0aW9uIGZhY2lsaXRpZXMuIFRoZXJlIGlzIHByb2JhYmx5IG5lZWQgdG8gcmViYWxhbmNlIHRoZSBiaWtlcyBpbiB0aGVzZSBhcmVhcyB0byBtZWV0IHRoZSBkZW1hbmQuCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEwfQpnZ3Bsb3QoKSsKICBnZW9tX3NmKGRhdGEgPSBwaGlsVHJhY3RzICU+JQogICAgICAgICAgc3RfdHJhbnNmb3JtKGNycz00MzI2KSkrCiAgZ2VvbV9wb2ludChkYXRhID0gZGF0X2NlbnN1cyAlPiUKICAgICAgICAgICAgbXV0YXRlKGhvdXIgPSBob3VyKHN0YXJ0X3RpbWUpLAogICAgICAgICAgICAgICAgd2Vla2VuZCA9IGlmZWxzZShkb3R3ICVpbiUgYygiU3VuIiwgIlNhdCIpLCAiV2Vla2VuZCIsICJXZWVrZGF5IiksCiAgICAgICAgICAgICAgICB0aW1lX29mX2RheSA9IGNhc2Vfd2hlbihob3VyKGludGVydmFsNjApIDwgNyB8IGhvdXIoaW50ZXJ2YWw2MCkgPiAxOCB+ICJPdmVybmlnaHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3VyKGludGVydmFsNjApID49IDcgJiBob3VyKGludGVydmFsNjApIDwgMTAgfiAiQU0gUnVzaCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIoaW50ZXJ2YWw2MCkgPj0gMTAgJiBob3VyKGludGVydmFsNjApIDwgMTUgfiAiTWlkLURheSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIoaW50ZXJ2YWw2MCkgPj0gMTUgJiBob3VyKGludGVydmFsNjApIDw9IDE4IH4gIlBNIFJ1c2giKSklPiUKICAgICAgICAgICAgICBncm91cF9ieShzdGFydF9zdGF0aW9uLCBzdGFydF9sYXQsIHN0YXJ0X2xvbiwgd2Vla2VuZCwgdGltZV9vZl9kYXkpICU+JQogICAgICAgICAgICAgIHRhbGx5KCksCiAgICAgICAgICAgIGFlcyh4PXN0YXJ0X2xvbiwgeSA9IHN0YXJ0X2xhdCwgY29sb3IgPSBuKSwKICAgICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGFscGhhID0gMC40LCBzaXplID0gMykrCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXMoZGlyZWN0aW9uID0gLTEsCiAgZGlzY3JldGUgPSBGQUxTRSwgb3B0aW9uID0gIkQiKSsKICB5bGltKG1pbihkYXRfY2Vuc3VzJHN0YXJ0X2xhdCksIG1heChkYXRfY2Vuc3VzJHN0YXJ0X2xhdCkpKwogIHhsaW0obWluKGRhdF9jZW5zdXMkc3RhcnRfbG9uKSwgbWF4KGRhdF9jZW5zdXMkc3RhcnRfbG9uKSkrCiAgZmFjZXRfZ3JpZCh3ZWVrZW5kIH4gdGltZV9vZl9kYXkpKwogIGxhYnModGl0bGU9IkJpa2Ugc2hhcmUgdHJpcHMgcGVyIGhyIGJ5IHN0YXRpb24uIFBoaWxhZGVscGhpYSwgTWF5LCAyMDIyIikrCiAgbWFwVGhlbWUKYGBgCgoKIyMjIFdlYXRoZXIgQW5hbHlzaXMKCk1heSBpbiBQaGlsYWRlbHBoaWEgd2FzIGNoYXJhY3Rlcml6ZWQgYnkgc3RlYWRpbHkgd2FybWluZyBzcHJpbmcgZGF5c+KAlG1vcm5pbmcgbG93cyBpbiB0aGUgNTBzIMKwRiBjbGltYmluZyB0byBoaWdocyBpbiB0aGUgbWlkLTgwcyBieSBtb250aOKAmXMgZW5k4oCUd2l0aCByZWd1bGFyIDE14oCTMjAgwrBGLiBQZXJjaXBpdGF0aW9uIHdlcmUgZCBtb3N0bHkgbGlnaHQgdGJ1dCB3aXRoIGEgZmV3IGludGVuc2UgZG93bnBvdXJzIGF0IHRoZSBlbmQgb2YgdGhlIG1vbnRoLiBXaW5kcyBnZW5lcmFsbHkgaG92ZXJlZCBhcm91bmQgNeKAkzE1IG1waC4gVGhlc2UgYnJpZWYgd2V0IGFuZCBibHVzdGVyeSBpbnRlcmx1ZGVzIHB1bmN0dWF0ZWQgb3RoZXJ3aXNlIG1pbGQsIGNvbWZvcnRhYmxlIHdlYXRoZXIgdGhyb3VnaG91dCBNYXkuCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OH0KZ3JpZC5hcnJhbmdlKAogIGdncGxvdCh3ZWF0aGVyLlBhbmVsLCBhZXMoaW50ZXJ2YWw2MCxQcmVjaXBpdGF0aW9uKSkgKyBnZW9tX2xpbmUoKSArCiAgbGFicyh0aXRsZT0iUGVyY2lwaXRhdGlvbiIsIHg9IkhvdXIiLCB5PSJQZXJlY2lwaXRhdGlvbiIpICsgdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEwKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIHNpemU9MC44KSksCiAgZ2dwbG90KHdlYXRoZXIuUGFuZWwsIGFlcyhpbnRlcnZhbDYwLFdpbmRfU3BlZWQpKSArIGdlb21fbGluZSgpICsKICAgIGxhYnModGl0bGU9IldpbmQgU3BlZWQiLCB4PSJIb3VyIiwgeT0iV2luZCBTcGVlZCIpICt0aGVtZShwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTApLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpKSwKICBnZ3Bsb3Qod2VhdGhlci5QYW5lbCwgYWVzKGludGVydmFsNjAsVGVtcGVyYXR1cmUpKSArIGdlb21fbGluZSgpICsKICAgIGxhYnModGl0bGU9IlRlbXBlcmF0dXJlIiwgeD0iSG91ciIsIHk9IlRlbXBlcmF0dXJlIikgK3RoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkpLAogIHRvcD0iV2VhdGhlciBEYXRhIC0gUGhpbGFkZWxwaGlhIChQSEwpIC0gTWF5LCAyMDIyIikKYGBgCgoKIyMgU3BhY2UtVGltZSBQYW5lbApXZSBjcmVhdGVkIGEgc3R1ZHkgcGFuZWwgd2hlcmUgZWFjaCBpbnN0YW5jZSBpbiB0aGUgcGFuZWwgaXMgYSB1bmlxdWUgY29tYmluYXRpb24gb2Ygc3BhY2UgYW5kIHRpbWUuIEluIG90aGVyIHdvcmRzLCBlYWNoIHJvdyB3aWxsIHJlcHJlc2VudCB0aGUgcmlkZSBhdCBhIHBhcnRpY3VsYXIgc3RhdGlvbiBkdXJpbmcgYSBwYXJ0aWN1bGFyIGhvdXIuIEFmdGVyIHRoYXQsIHdlIGFkZCBzb21lIG1vcmUgaW5mb3JtYXRpb24gdG8gdGhpcyBwYW5lbC4gVGhpcyBpbmNsdWRlcyBjb3VudGluZyB0aGUgbnVtYmVyIG9mIHJpZGVzIGF0IHRoaXMgc3RhdGlvbiBhdCB0aGlzIHBhcnRpY3VsYXIgaG91ciwgYWRkaW5nIHdlYXRoZXIgaW5mb3JtYXRpb24sIGFuZCBicmluZ2luZyBpbiBjZW5zdXMgZGF0YS4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpzdHVkeS5wYW5lbCA8LQogIGV4cGFuZC5ncmlkKGludGVydmFsNjA9dW5pcXVlKGRhdF9jZW5zdXMkaW50ZXJ2YWw2MCksCiAgICAgICAgICAgICAgc3RhcnRfc3RhdGlvbiA9IHVuaXF1ZShkYXRfY2Vuc3VzJHN0YXJ0X3N0YXRpb24pKSAlPiUKICBsZWZ0X2pvaW4oLiwgZGF0X2NlbnN1cyAlPiUKICAgICAgICAgICAgICBzZWxlY3Qoc3RhcnRfc3RhdGlvbiwgT3JpZ2luLlRyYWN0LCBzdGFydF9sb24sIHN0YXJ0X2xhdCApJT4lCiAgICAgICAgICAgICAgZGlzdGluY3QoKSAlPiUKICAgICAgICAgICAgICBncm91cF9ieShzdGFydF9zdGF0aW9uKSAlPiUKICAgICAgICAgICAgICBzbGljZSgxKSkKCnJpZGUucGFuZWwgPC0KICBkYXRfY2Vuc3VzICU+JQogIG11dGF0ZShUcmlwX0NvdW50ZXIgPSAxKSAlPiUKICByaWdodF9qb2luKHN0dWR5LnBhbmVsKSAlPiUKICBncm91cF9ieShpbnRlcnZhbDYwLCBzdGFydF9zdGF0aW9uLCAgT3JpZ2luLlRyYWN0LCBzdGFydF9sb24sIHN0YXJ0X2xhdCkgJT4lCiAgc3VtbWFyaXplKFRyaXBfQ291bnQgPSBzdW0oVHJpcF9Db3VudGVyLCBuYS5ybT1UKSkgJT4lCiAgbGVmdF9qb2luKHdlYXRoZXIuUGFuZWwpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBmaWx0ZXIoaXMubmEoc3RhcnRfc3RhdGlvbikgPT0gRkFMU0UpICU+JQogIG11dGF0ZSh3ZWVrID0gd2VlayhpbnRlcnZhbDYwKSwKICAgICAgICAgZG90dyA9IHdkYXkoaW50ZXJ2YWw2MCwgbGFiZWwgPSBUUlVFKSkgJT4lCiAgZmlsdGVyKGlzLm5hKE9yaWdpbi5UcmFjdCkgPT0gRkFMU0UpCgpyaWRlLnBhbmVsIDwtCiAgbGVmdF9qb2luKHJpZGUucGFuZWwsIHBoaWxDZW5zdXMgJT4lCiAgICAgICAgICAgICAgYXMuZGF0YS5mcmFtZSgpICU+JQogICAgICAgICAgICAgIHNlbGVjdCgtZ2VvbWV0cnkpLCBieSA9IGMoIk9yaWdpbi5UcmFjdCIgPSAiR0VPSUQiKSkKCnJpZGUucGFuZWwgPC0KICByaWRlLnBhbmVsICU+JQogIGFycmFuZ2Uoc3RhcnRfc3RhdGlvbiwgaW50ZXJ2YWw2MCkgJT4lCiAgbXV0YXRlKGxhZ0hvdXIgPSBkcGx5cjo6bGFnKFRyaXBfQ291bnQsMSksCiAgICAgICAgIGxhZzJIb3VycyA9IGRwbHlyOjpsYWcoVHJpcF9Db3VudCwyKSwKICAgICAgICAgbGFnM0hvdXJzID0gZHBseXI6OmxhZyhUcmlwX0NvdW50LDMpLAogICAgICAgICBsYWc0SG91cnMgPSBkcGx5cjo6bGFnKFRyaXBfQ291bnQsNCksCiAgICAgICAgIGxhZzEySG91cnMgPSBkcGx5cjo6bGFnKFRyaXBfQ291bnQsMTIpLAogICAgICAgICBsYWcxZGF5ID0gZHBseXI6OmxhZyhUcmlwX0NvdW50LDI0KSwKICAgICAgICAgaG9saWRheSA9IGlmZWxzZSh5ZGF5KGludGVydmFsNjApID09IDE0OCwxLDApKSAlPiUKICAgbXV0YXRlKGRheSA9IHlkYXkoaW50ZXJ2YWw2MCkpICU+JQogICBtdXRhdGUoaG9saWRheUxhZyA9IGNhc2Vfd2hlbihkcGx5cjo6bGFnKGhvbGlkYXksIDEpID09IDEgfiAiUGx1c09uZURheSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjpsYWcoaG9saWRheSwgMikgPT0gMSB+ICJQbHVzdFR3b0RheXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6bGFnKGhvbGlkYXksIDMpID09IDEgfiAiUGx1c3RUaHJlZURheXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6bGVhZChob2xpZGF5LCAxKSA9PSAxIH4gIk1pbnVzT25lRGF5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OmxlYWQoaG9saWRheSwgMikgPT0gMSB+ICJNaW51c1R3b0RheXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6bGVhZChob2xpZGF5LCAzKSA9PSAxIH4gIk1pbnVzVGhyZWVEYXlzIiksCiAgICAgICAgIGhvbGlkYXlMYWcgPSBpZmVsc2UoaXMubmEoaG9saWRheUxhZykgPT0gVFJVRSwgMCwgaG9saWRheUxhZykpCmBgYAoKIyMjIFdlYXRoZXIgQ29ycmVsYXRpb24KCldpdGggdGhlIHN0dWR5IHBhbmVsLCB3ZSBjb3VsZCBwZXJmb3JtIHNvbWUgZXhwbG9yYXRvcnkgYW5hbHlzaXMgdG8gcHJlcGFyZSBmb3IgZnVydGhlciByZWdyZXNzaW9uIG1vZGVsLiBGaXJzdCwgd2Ugd2FudCB0byBzZWUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB0cmlwIGNvdW50cyBhbmQgdGhlIHdlYXRoZXIgY29uZGl0aW9ucy4gVGhlIGZpZ3VyZSBiZWxvdyBzaG93cyBhIGdlbmVyYWxseSBzaWduaWZpY2FudCwgcG9zaXRpdmUsIGFuZCBzdHJvbmcgbGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRlbXBlcmF0dXJlIGFuZCB0cmlwIGNvdW50cy4gVGhpcyBtZWFucyBpbiBnZW5lcmFsLCB3aGVuIHRoZSB0ZW1wZXJhdHVyZSBpbmNyZWFzZSwgYmlrZXNoYXJlIGRlbWFuZCB0ZW5kIHRvIGluY3JlYXNlIGFzIHdlbGwuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTR9CnJpZGUucGFuZWwgJT4lCiAgZ3JvdXBfYnkoaW50ZXJ2YWw2MCkgJT4lCiAgc3VtbWFyaXplKFRyaXBfQ291bnQgPSBtZWFuKFRyaXBfQ291bnQpLAogICAgICAgICAgICBUZW1wZXJhdHVyZSA9IGZpcnN0KFRlbXBlcmF0dXJlKSkgJT4lCiAgbXV0YXRlKHdlZWsgPSB3ZWVrKGludGVydmFsNjApKSAlPiUKICBnZ3Bsb3QoYWVzKFRlbXBlcmF0dXJlLCBUcmlwX0NvdW50KSkgKwogICAgZ2VvbV9wb2ludChjb2xvciA9ICJibGFjayIpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9IEZBTFNFLCBjb2xvcj0iIzEyNEU3OCIpICsKICAgIGZhY2V0X3dyYXAofndlZWssIG5jb2w9OCkgKwogICAgbGFicyh0aXRsZT0iVHJpcCBDb3VudCBBcyBhIEZ1Y3Rpb24gb2YgVGVtcGVyYXR1cmUgYnkgV2VlayIsCiAgICAgICAgIHg9IlRlbXBlcmF0dXJlIiwgeT0iTWVhbiBUcmlwIENvdW50IikgKwogIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkpCmBgYAoKV2Ugd291bGQgYWxzbyBsaWtlIHRvIHNlZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHRyaXAgY291bnRzIGFuZCB0aGUgd2luZCBzcGVlZC4gVGhlIGZpZ3VyZSBiZWxvdyBzaG93cyBpbiB0aGUgZmlyc3QgdHdvIHdlZWtzLCB0aGVyZSBpcyBhIGdlbmVyYWxseSBuZWdhdGl2ZSBsaW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gd2luZCBzcGVlZCBhbmQgdHJpcCBjb3VudHMuIEhvd2V2ZXIsIGluIHRoZSBsYXN0IHR3byB3ZWVrcywgdGhlIHJlbGF0aW9uc2hpcCBpcyBwb3NpdGl2ZS4gVGhlc2Ugbm9uLWNvbnN0YW50IHJlbGF0aW9uc2hpcCBtYXkgaW5kaWNhdGUgdGhhdCB0aGUgd2luZCBzcGVlZCBpcyBub3QgdGhlIHNvbGUgcHJlZGljdG9ycyBpbmZsdWVuY2UgdGhlIHJpZGVyc2hpcC4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NH0KcmlkZS5wYW5lbCAlPiUKICBncm91cF9ieShpbnRlcnZhbDYwKSAlPiUKICBzdW1tYXJpemUoVHJpcF9Db3VudCA9IG1lYW4oVHJpcF9Db3VudCksCiAgICAgICAgICAgIFdpbmQgPSBmaXJzdChXaW5kX1NwZWVkKSkgJT4lCiAgbXV0YXRlKHdlZWsgPSB3ZWVrKGludGVydmFsNjApKSAlPiUKICBnZ3Bsb3QoYWVzKFdpbmQsIFRyaXBfQ291bnQpKSArCiAgICBnZW9tX3BvaW50KGNvbG9yID0gImJsYWNrIikgKyBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT0gRkFMU0UsIGNvbG9yPSIjMTI0RTc4IikgKwogICAgZmFjZXRfd3JhcCh+d2VlaywgbmNvbD04KSArCiAgICBsYWJzKHRpdGxlPSJUcmlwIENvdW50IEFzIGEgRnVjdGlvbiBvZiBXaW5kIGJ5IFdlZWsiLAogICAgICAgICB4PSJXaW5kIFNwZWVkIiwgeT0iTWVhbiBUcmlwIENvdW50IikgKwogIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkpICsKICAgIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkpCmBgYAoKIyMjIFNlcmlhbCBDb3JyZWxhdGlvbgoKSWYgdHJpcCBjb3VudHMgZXhoaWJpdCB0ZW1wb3JhbCBhdXRvY29ycmVsYXRpb24sIGluY29ycG9yYXRpbmcgbGFnZ2VkIGZlYXR1cmVzIHdvdWxkIGltcHJvdmUgcHJlZGljdGlvbiBhY2N1cmFjeS4gVHJpcCB2b2x1bWVzIGF0IGEgZ2l2ZW4gaG91ciBhcmUgb2Z0ZW4gc3Ryb25nbHkgY29ycmVsYXRlZCB3aXRoIHRob3NlIGluIHRoZSBwcmVjZWRpbmcgYW5kIGZvbGxvd2luZyBob3Vycywgc28ga25vd2luZyB0aGUgY291bnQgYXQgb25lIHRpbWUgcG9pbnQgbWFrZXMgaXQgZWFzaWVyIHRvIGVzdGltYXRlIHZvbHVtZXMgaW4gYWRqYWNlbnQgaW50ZXJ2YWxzLiBUbyBjYXB0dXJlIHRoaXMgZWZmZWN0LCB3ZSBlbmdpbmVlcmVkIHNpeCBkaXN0aW5jdCB0eXBlcyBvZiBob3VybHkgbGFnIGZlYXR1cmVzLgoKVGhlIHRhYmxlIGFuZCBjaGFydHMgYmVsb3cgc2hvdyB0aGUgUGVhcnNvbiBjb3JyZWxhdGlvbnMgYmV0d2VlbiBjdXJyZW50IHRyaXAgY291bnRzIGFuZCBzaXggbGFnZ2VkIHZhcmlhYmxlcy4gQ29ycmVsYXRpb24gc3RyZW5ndGggZmFsbHMgb2ZmIGFzIGxhZyBpbmNyZWFzZXM6IGltbWVkaWF0ZSAoMSBoKSBhbmQgZGFpbHkgKDI0IGgpIGxhZ3Mgc2hvdyB0aGUgc3Ryb25nZXN0IHBvc2l0aXZlIGxpbmtzLCBtaWQtcmFuZ2UgbGFncyB3ZWFrZW4gc3RlYWRpbHksIGFuZCB0aGUgMTItaG91ciBsYWcgZXZlbiBmbGlwcyB0byBhIG5lZ2F0aXZlIHJlbGF0aW9uc2hpcC4gVGhlc2UgcmVzdWx0cyBzaG93IHRoYXQgdGhlIGJlc3QgcHJlZGljdG9ycyBhcmUgdGhlIGhvdXIganVzdCBiZWZvcmUgYW5kIHRoZSBzYW1lIGhvdXIgb24gdGhlIHByZXZpb3VzIGRheSwgd2hpbGUgaG91cnMgZmFydGhlciBhd2F5IGFyZSBsZXNzIGhlbHBmdWzigJRhbmQgdGhlIGhvdXIgMTIgaG91cnMgZWFybGllciBjYW4gZXZlbiBzaG93IGFuIG9wcG9zaXRlIHRyZW5kLgoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoa2FibGVFeHRyYSkKCiMgY29tcHV0ZSBtZWFuLWJ5LWhvdXIsIGdhdGhlciBsYWdzLCB0aGVuIGNvcnIgYnkgbGFnIHZhcmlhYmxlCmNvcnJfZGYgPC0gYXMuZGF0YS5mcmFtZShyaWRlLnBhbmVsKSAlPiUKICBncm91cF9ieShpbnRlcnZhbDYwKSAlPiUKICBzdW1tYXJpc2VfYXQodmFycyhzdGFydHNfd2l0aCgibGFnIiksICJUcmlwX0NvdW50IiksIG1lYW4sIG5hLnJtID0gVFJVRSkgJT4lCiAgZ2F0aGVyKFZhcmlhYmxlLCBWYWx1ZSwgLWludGVydmFsNjAsIC1UcmlwX0NvdW50KSAlPiUKICBtdXRhdGUoVmFyaWFibGUgPSBmYWN0b3IoCiAgICBWYXJpYWJsZSwKICAgIGxldmVscyA9IGMoImxhZ0hvdXIiLCJsYWcySG91cnMiLCJsYWczSG91cnMiLCJsYWc0SG91cnMiLCJsYWcxMkhvdXJzIiwibGFnMWRheSIpCiAgKSkgJT4lCiAgZ3JvdXBfYnkoVmFyaWFibGUpICU+JQogIHN1bW1hcmlzZShDb3JyZWxhdGlvbiA9IHJvdW5kKGNvcihWYWx1ZSwgVHJpcF9Db3VudCksIDIpKSAlPiUKICB1bmdyb3VwKCkKCiMgcmVuZGVyIHdpdGgga2FibGVFeHRyYQpjb3JyX2RmICU+JQogIGthYmxlKAogICAgZm9ybWF0ID0gImh0bWwiLAogICAgY29sLm5hbWVzID0gYygiTGFnIFZhcmlhYmxlIiwgIkNvcnJlbGF0aW9uIHdpdGggVHJpcCBDb3VudCIpLAogICAgY2FwdGlvbiA9ICJDb3JyZWxhdGlvbiBCZXR3ZWVuIExhZ2dlZCBUcmlwIENvdW50cyBhbmQgQ3VycmVudCBUcmlwIENvdW50IgogICkgJT4lCiAga2FibGVfc3R5bGluZygKICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwKICAgIHBvc2l0aW9uID0gImNlbnRlciIsCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIikKICApCgpgYGAKCmBgYHtyLG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00fQphcy5kYXRhLmZyYW1lKHJpZGUucGFuZWwpICU+JQogICAgZ3JvdXBfYnkoaW50ZXJ2YWw2MCkgJT4lCiAgICBzdW1tYXJpc2VfYXQodmFycyhzdGFydHNfd2l0aCgibGFnIiksICJUcmlwX0NvdW50IiksIG1lYW4sIG5hLnJtID0gVFJVRSkgJT4lCiAgICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtaW50ZXJ2YWw2MCwgLVRyaXBfQ291bnQpICU+JQogIGdncGxvdChhZXMoeCA9IFZhbHVlICwgeSA9IFRyaXBfQ291bnQpKSArCiAgICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJibGFjayIpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9IEZBTFNFLCBjb2xvcj0iIzEyNEU3OCIpICsKICBmYWNldF93cmFwKH5WYXJpYWJsZSwgbmNvbCA9IDYpKwogIGxhYnMoeCA9ICJWYXJpYWJsZSIsIHkgPSAiQ29ycmVsYXRpb24iLCB0aXRsZSA9ICJDb3JyZWxhdGlvbiBCZXR3ZWVuIFNlcmlhbCBMYWcgVHJpcHMgYW5kIFRyaXAgQ291bnQiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKwogIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkpCmBgYAoKIyBNb2RlbCBUcmFpbmluZyBhbmQgVmFsaWRhdGlvbgoKIyMgRGF0YSBTcGxpdHRpbmcKV2Ugc3BsaXQgdGhlIGRhdGEgaW50byBhIHRyYWluaW5nIHNldCBhbmQgYSB0ZXN0IHNldC4gVGhlIHRyYWluaW5nIHNldCBpbmNsdWRlcyBkYXRhIGZyb20gd2VlayAyMCB0byB3ZWVrIDIyLCB3aGlsZSB0aGUgdGVzdCBzZXQgaW5jbHVkZXMgZGF0YSBmcm9tIHdlZWsgMTggdG8gd2VlayAxOS4gVGhlIHRyYWluaW5nIHNldCBpcyB1c2VkIHRvIHRyYWluIHRoZSBtb2RlbCwgd2hpbGUgdGhlIHRlc3Qgc2V0IGlzIHVzZWQgdG8gdmFsaWRhdGUgdGhlIG1vZGVsLiBCeSBkb2luZyB0aW1lIGJhc2VkIHNwbGl0dGluZywgd2UgcHJlc2VydmUgdGhlIG5hdHVyYWwgdGVtcG9yYWwgb3JkZXIsIHByZXZlbnQgYW55IGxlYWthZ2Ugb2YgaW5mb3JtYXRpb24gZnJvbSBvbmUgcGVyaW9kIGludG8gYW5vdGhlci4gVGhpcyBjb3VsZCBlbnN1cmUgdGhhdCBvdXIgdmFsaWRhdGlvbiBnZW51aW5lbHkgcmVmbGVjdHMgdGhlIG1vZGVs4oCZcyBhYmlsaXR5IHRvIGdlbmVyYWxpemUgdG8gdW5zZWVuIHRpbWUgaW50ZXJ2YWxzLgoKYGBge3J9CnJpZGUuVHJhaW4gPC0gZmlsdGVyKHJpZGUucGFuZWwsIHdlZWsgPj0gMjApCnJpZGUuVGVzdCA8LSBmaWx0ZXIocmlkZS5wYW5lbCwgd2VlayA8IDIwKQpgYGAKCiMjIE1vZGVsIFRyYWluaW5nCldlIHRyYWluZWQgZml2ZSBkaWZmZXJlbnQgbW9kZWxzIHVzaW5nIHRoZSB0cmFpbmluZyBkYXRhc2V0IHdpdGggZGlmZmVyZW50IGZvY3VzOgoKLSBgcmVnMWA6IEEgYmFzZWxpbmUgdGVtcG9yYWwgbW9kZWwgY2FwdHVyZXMgdGhlIGRhaWx5IGFuZCB3ZWVrbHkgdGVtcGVyYXR1cmVzIGVmZmVjdHMgb24gdHJpcCBjb3VudHMsIHdpdGggaG91cmx5IGZpeGVkIGVmZmVjdHMgdG8gY2FwdHVyZSB0aGUgY3ljbGljYWwgbmF0dXJlIG9mIGJpa2Ugc2hhcmUgZGVtYW5kLgotIGByZWcyYDogQSBzcGF0aWFsIG1vZGVsIGNhcHR1cmVzIHRoZSBzcGF0aWFsIGVmZmVjdHMgb2YgYmlrZSBzaGFyZSBkZW1hbmQsIHdpdGggd2Vla2x5IGFuZCB3ZWVrZW5kIGZpeGVkIGVmZmVjdHMgdG8gY2FwdHVyZSB0aGUgY3ljbGljYWwgbmF0dXJlIG9mIGJpa2Ugc2hhcmUgZGVtYW5kLgotIGByZWczYDogQSB0ZW1wb3JhbCBhbmQgc3BhdGlhbCBtb2RlbCBjYXB0dXJlcyB0aGUgZGFpbHkgYW5kIHdlZWtseSB0ZW1wZXJhdHVyZXMgYW5kIHBlcmNpcGl0YXRpb24gZWZmZWN0cyBvbiB0cmlwIGNvdW50cywgd2l0aCBob3VybHkgZml4ZWQgZWZmZWN0cyB0byBjYXB0dXJlIHRoZSBjeWNsaWNhbCBuYXR1cmUgb2YgYmlrZSBzaGFyZSBkZW1hbmQuCi0gYHJlZzRgOiBBIGF1dG9yZWdyZXNzaXZlIHRlbXBvcmFsIGFuZCBzcGF0aWFsIG1vZGVsIGNhcHR1cmVzIHRoZSBkYWlseSBhbmQgd2Vla2x5IHRlbXBlcmF0dXJlcyBhbmQgcGVyY2lwaXRhdGlvbiBlZmZlY3RzIG9uIHRyaXAgY291bnRzLCB3aXRoIGhvdXJseSBmaXhlZCBlZmZlY3RzIHRvIGNhcHR1cmUgdGhlIGN5Y2xpY2FsIG5hdHVyZSBvZiBiaWtlIHNoYXJlIGRlbWFuZCwgYW5kIGxhZ2dlZCBmZWF0dXJlcyB0byBjYXB0dXJlIHRoZSBzZXJpYWwgY29ycmVsYXRpb24uCi0gYHJlZzVgOiBBZGQgaG9saWRheSBhZGp1c3RtZW50IGZvciBgcmVnNGAgbW9kZWwgdG8gY2FwdHVyZSB0aGUgaG9saWRheSBlZmZlY3RzIG9uIHRyaXAgY291bnRzLCB3aXRoIGhvdXJseSBmaXhlZCBlZmZlY3RzIHRvIGNhcHR1cmUgdGhlIGN5Y2xpY2FsIG5hdHVyZSBvZiBiaWtlIHNoYXJlIGRlbWFuZCwgYW5kIGxhZ2dlZCBmZWF0dXJlcyB0byBjYXB0dXJlIHRoZSBzZXJpYWwgY29ycmVsYXRpb24uCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcmVnMSA8LQogIGxtKFRyaXBfQ291bnQgfiAgZmFjdG9yKGhvdXIoaW50ZXJ2YWw2MCkpICsgZmFjdG9yKGRvdHcpICsgVGVtcGVyYXR1cmUsICBkYXRhPXJpZGUuVHJhaW4pCgpyZWcyIDwtCiAgbG0oVHJpcF9Db3VudCB+ICBzdGFydF9zdGF0aW9uICsgIGZhY3Rvcihkb3R3KSsgVGVtcGVyYXR1cmUsICBkYXRhPXJpZGUuVHJhaW4pCgpyZWczIDwtCiAgbG0oVHJpcF9Db3VudCB+ICBzdGFydF9zdGF0aW9uICsgZmFjdG9yKGhvdXIoaW50ZXJ2YWw2MCkpICsgZmFjdG9yKGRvdHcpICsgVGVtcGVyYXR1cmUgKyBQcmVjaXBpdGF0aW9uLAogICAgIGRhdGE9cmlkZS5UcmFpbikKCnJlZzQgPC0KICBsbShUcmlwX0NvdW50IH4gIHN0YXJ0X3N0YXRpb24gKyAgZmFjdG9yKGhvdXIoaW50ZXJ2YWw2MCkpICsgIGZhY3Rvcihkb3R3KSArIFRlbXBlcmF0dXJlICsgUHJlY2lwaXRhdGlvbiArCiAgICAgICAgICAgICAgICAgICBsYWdIb3VyICsgbGFnMkhvdXJzICtsYWczSG91cnMgKyBsYWcxMkhvdXJzICsgbGFnMWRheSwKICAgICBkYXRhPXJpZGUuVHJhaW4pCgpyZWc1IDwtCiAgbG0oVHJpcF9Db3VudCB+ICBzdGFydF9zdGF0aW9uICsgZmFjdG9yKGhvdXIoaW50ZXJ2YWw2MCkpICsgIGZhY3Rvcihkb3R3KSArIFRlbXBlcmF0dXJlICsgUHJlY2lwaXRhdGlvbiArCiAgICAgICAgICAgICAgICAgICBsYWdIb3VyICsgbGFnMkhvdXJzICtsYWczSG91cnMgK2xhZzEySG91cnMgKyBsYWcxZGF5ICsgaG9saWRheUxhZyArIGhvbGlkYXksCiAgICAgZGF0YT1yaWRlLlRyYWluKQpgYGAKCiMjIE1vZGVsIFZhbGlkYXRpb24KCldlIHVzZWQgdGhlIHRpbWUgYmFzZWQgdmFsaWRhdGlvbiB0byBldmFsdWF0ZSB0aGUgZml2ZSBkaWZmZXJlbnQgbW9kZWwgcGVyZm9ybWFuY2UuIFRoZSB2YWxpZGF0aW9uIGlzIGRvbmUgYnkgY2FsY3VsYXRpbmcgdGhlIG1lYW4gYWJzb2x1dGUgZXJyb3IgKE1BRSkgYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvZiBhYnNvbHV0ZSBlcnJvciAoUk1TRSkgZm9yIGVhY2ggbW9kZWwuIFRoZSBNQUUgYW5kIFJNU0UgYXJlIGNhbGN1bGF0ZWQgZm9yIGVhY2ggd2VlayBpbiB0aGUgdGVzdCBzZXQuIFRoZSByZXN1bHRzIGFyZSB0aGVuIHN1bW1hcml6ZWQgaW4gZm9sbG93aW5nIHRhYmxlLgoKVGhlIHRhYmxlIHNob3dzIHRoYXQgdGhlIGByZWc0YCBhbmQgYHJlZzVgIG1vZGVscyB5aWVsZCB0aGUgc2FtZSBhbmQgbG93ZXN0IE1BRSBhbmQgUk1TRSBvbiBib3RoIHdlZWsgMTggYW5kIHdlZWsgMTkgKHRlc3RpbmcgZGF0YXNldHMpLCB3aGljaCBpbmRpY2F0ZSBhIGJldHRlciBwZXJmb3JtYW5jZSB0aGFuIHRoZSBvdGhlciBtb2RlbHMuIFNpbmNlIHRoZSBgcmVnNWAgbW9kZWwgaXMgbW9yZSBjb21wbGV4IHRoYW4gdGhlIGByZWc0YCBtb2RlbCwgd2UgY2FuIGNvbmNsdWRlIHRoYXQgdGhlIGByZWc0YCBtb2RlbCBpcyB0aGUgYmVzdCBtb2RlbCBmb3IgcHJlZGljdGluZyBiaWtlIHNoYXJlIGRlbWFuZCBpbiBQaGlsYWRlbHBoaWEgYW5kIGFkZGluZyBhZGRpdGlvbmFsIGhvbGlkYXkgZWZmZWN0cyBkb2Vzbid0IGltcHJvdmUgdGhlIG1vZGVsIHBlcmZvcm1hbmNlLiAgCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcmlkZS5UZXN0LndlZWtOZXN0IDwtCiAgcmlkZS5UZXN0ICU+JQogIG5lc3QoLXdlZWspCgptb2RlbF9wcmVkIDwtIGZ1bmN0aW9uKGRhdCwgZml0KXsKICAgcHJlZCA8LSBwcmVkaWN0KGZpdCwgbmV3ZGF0YSA9IGRhdCl9Cgp3ZWVrX3ByZWRpY3Rpb25zIDwtCiAgcmlkZS5UZXN0LndlZWtOZXN0ICU+JQogICAgbXV0YXRlKEFUaW1lX0ZFID0gbWFwKC54ID0gZGF0YSwgZml0ID0gcmVnMSwgLmYgPSBtb2RlbF9wcmVkKSwKICAgICAgICAgICBCU3BhY2VfRkUgPSBtYXAoLnggPSBkYXRhLCBmaXQgPSByZWcyLCAuZiA9IG1vZGVsX3ByZWQpLAogICAgICAgICAgIENUaW1lX1NwYWNlX0ZFID0gbWFwKC54ID0gZGF0YSwgZml0ID0gcmVnMywgLmYgPSBtb2RlbF9wcmVkKSwKICAgICAgICAgICBEVGltZV9TcGFjZV9GRV90aW1lTGFncyA9IG1hcCgueCA9IGRhdGEsIGZpdCA9IHJlZzQsIC5mID0gbW9kZWxfcHJlZCksCiAgICAgICAgICAgRVRpbWVfU3BhY2VfRkVfdGltZUxhZ3NfaG9saWRheUxhZ3MgPSBtYXAoLnggPSBkYXRhLCBmaXQgPSByZWc1LCAuZiA9IG1vZGVsX3ByZWQpKSAlPiUKICAgIGdhdGhlcihSZWdyZXNzaW9uLCBQcmVkaWN0aW9uLCAtZGF0YSwgLXdlZWspICU+JQogICAgbXV0YXRlKE9ic2VydmVkID0gbWFwKGRhdGEsIHB1bGwsIFRyaXBfQ291bnQpLAogICAgICAgICAgIEFic29sdXRlX0Vycm9yID0gbWFwMihPYnNlcnZlZCwgUHJlZGljdGlvbiwgIH4gYWJzKC54IC0gLnkpKSwKICAgICAgICAgICBNQUUgPSBtYXBfZGJsKEFic29sdXRlX0Vycm9yLCBtZWFuLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgIHNkX0FFID0gbWFwX2RibChBYnNvbHV0ZV9FcnJvciwgc2QsIG5hLnJtID0gVFJVRSkpCgp3ZWVrX3ByZWRpY3Rpb25zICU+JQogIHNlbGVjdCh3ZWVrLCBSZWdyZXNzaW9uLCBNQUUsIHNkX0FFKSAlPiUKICBhcnJhbmdlKHdlZWssIFJlZ3Jlc3Npb24pICU+JQogIGthYmxlKAogICAgZm9ybWF0ID0gImh0bWwiLAogICAgZGlnaXRzID0gMiwKICAgIGNvbC5uYW1lcyA9IGMoIldlZWsiLCAiTW9kZWwiLCAiTWVhbiBBYnNvbHV0ZSBFcnJvciAoTUFFKSIsICJTRCBvZiBBYnNvbHV0ZSBFcnJvciAoUk1TRSkiKSwKICAgIGNhcHRpb24gPSAiV2Vla2x5IE1BRSBhbmQgU3RhbmRhcmQgRGV2aWF0aW9uIG9mIEFic29sdXRlIEVycm9ycyBieSBSZWdyZXNzaW9uIE1vZGVsIgogICkgJT4lCiAga2FibGVfc3R5bGluZygKICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwKICAgIHBvc2l0aW9uID0gImNlbnRlciIsCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIikKICApICU+JQogIGNvbGxhcHNlX3Jvd3MoCiAgICBjb2x1bW5zID0gMSwKICAgIHZhbGlnbiA9ICJ0b3AiLAogICAgcm93X2dyb3VwX2xhYmVsX3Bvc2l0aW9uID0gInN0YWNrIgogICkKYGBgCgoKCgojIE1vZGVsIEV2YWx1YXRpb24gYW5kIFJlYmFsYW5jaW5nIFBsYW4KCiMjIE1vZGVsIGV2YWx1YXRpb24KCkFzIHNob3duIGFib3ZlIGFuZCBpbiB0aGUgZm9sbG93aW5nIGNoYXJ0LCB0aGUgYHJlZzRgIGFuZCBgcmVnNWAgbW9kZWxzIHlpZWxkIHRoZSBzYW1lIGFuZCBsb3dlc3QgTUFFIGFuZCBSTVNFIG9uIGJvdGggd2VlayAxOCBhbmQgd2VlayAxOSAodGVzdGluZyBkYXRhc2V0cyksIHdoaWNoIGluZGljYXRlIGEgYmV0dGVyIHBlcmZvcm1hbmNlIHRoYW4gdGhlIG90aGVyIG1vZGVscy4gSW4gdGhlIGZvbGxvd2luZyBzZWN0aW9uLCB3ZSBjaGVjayB0aGUgbW9kZWwgcGVyZm9ybWFuY2UgYnkgY29tcGFyaW5nIHRoZSBwcmVkaWN0ZWQgYW5kIG9ic2VydmVkIHRyaXAgY291bnRzIGZyb20gdGhlIHRlbXBvcmFsIGFuZCBzcGF0aWFsIGFzcGVjdHMuIAoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fQp3ZWVrX3ByZWRpY3Rpb25zICU+JQogIGRwbHlyOjpzZWxlY3Qod2VlaywgUmVncmVzc2lvbiwgTUFFKSAlPiUKICBnYXRoZXIoVmFyaWFibGUsIE1BRSwgLVJlZ3Jlc3Npb24sIC13ZWVrKSAlPiUKICBnZ3Bsb3QoYWVzKHdlZWssIE1BRSkpICsKICAgIGdlb21fYmFyKGFlcyhmaWxsID0gUmVncmVzc2lvbiksIHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSkgKwogICAgbGFicyh0aXRsZSA9ICJNZWFuIEFic29sdXRlIEVycm9ycyBieSBtb2RlbCBzcGVjaWZpY2F0aW9uIGFuZCB3ZWVrIikgKwogIHBsb3RUaGVtZQpgYGAKCiMjIyBUZW1wb3JhbCBFdmFsdXRpb24KSW4gdGhpcyBzZWN0aW9uLCB3ZSBjb21wYXJlIHRoZSBwcmVkaWN0ZWQgYW5kIG9ic2VydmVkIHRyaXAgY291bnRzIGZyb20gdGhlIHRlbXBvcmFsIGFzcGVjdC4gV2UgcGxvdCB0aGUgcHJlZGljdGVkIGFuZCBvYnNlcnZlZCB0cmlwIGNvdW50cyBmb3IgZWFjaCBtb2RlbCBhY3Jvc3MgZGlmZmVyZW50IHRpbWUgcGVyaW9kIHRvIGNoZWNrIHdoZXRoZXIgdGhlIG1vZGVsIGNhcHR1cmUgdGhlIHRlbXBvcmFsIGVmZmVjdHMgb2YgYmlrZSBzaGFyZSBkZW1hbmQuIAoKLSBgcmVnMWA6IHRoZSBiYXNlbGluZSBtb2RlbCAxIGVmZmVjdGl2ZWx5IGNhcHR1cmUgdGhlIHRlbXBvcmFsIGVmZmVjdHMgb2YgYmlrZSBzaGFyZSBkZW1hbmQsIGJ1dCBtb3N0bHkgZHVyaW5nIG9uZSBzaW5nbGUgZGF5LCBidXQgaXQgZmFpbHMgdG8gY2FwdHVyZSB0aGUgZGlmZmVyZW50IGRlbWFuZCBhY3Jvc3Mgd2Vla2RheSBhbmQgd2Vla2VuZC4gCi0gYHJlZzJgOiB0aGUgc3BhdGlhbCBtb2RlbCAyIGRpZG4ndCBjYXB0dXJlIHRoZSB0ZW1wb3JhbCBlZmZlY3Qgb2YgdGhlIGJpa2Ugc2hhcmUgZGVtYW5kLCBiZWNhdXNlIHRoZSBtb2RlbCBvbmx5IGNhcHR1cmUgdGhlIHNwYXRpYWwgZWZmZWN0cyBvZiBiaWtlIHNoYXJlIGRlbWFuZCwgYnV0IG5vdCB0aGUgdGVtcG9yYWwgZWZmZWN0cy4KLSBgcmVnM2A6IHNpbWlsYXIgdG8gbW9kZWwgMSwgdGhlIHRlbXBvcmFsIGFuZCBzcGF0aWFsIG1vZGVsIDMgZWZmZWN0aXZlbHkgY2FwdHVyZSB0aGUgdGVtcG9yYWwgZWZmZWN0cyBkdXJpbmcgZGlmZmVyZW50IHRpbWUgcGVyaW9kIG9mIG9uZSBkYXkgZm9yIGJpa2Ugc2hhcmUgZGVtYW5kLCBidXQgaXQgZmFpbHMgdG8gY2FwdHVyZSB0aGUgZGlmZmVyZW50IGRlbWFuZCBhY3Jvc3Mgd2Vla2RheSBhbmQgd2Vla2VuZC4KLSBgcmVnNGA6IHRoZSBhdXRvcmVncmVzc2l2ZSBtb2RlbCA0IGVmZmVjdGl2ZWx5IGNhcHR1cmUgdGhlIHRlbXBvcmFsIGVmZmVjdHMgb2YgYmlrZSBzaGFyZSBkZW1hbmQgbm90IG9ubHkgYWNyb3NzIGRpZmZlcmVudCB0aW1lIGludGVydmFscyBvZiBvbmUgZGF5LCBidXQgYWxzbyBhY3Jvc3MgZGlmZmVyZW50IHdlZWtkYXlzIGFuZCB3ZWVrZW5kcy4KLSBgcmVnNWA6IGFkZGluZyB0aGUgaG9saWRheSBlZmZlY3RzIHRvIG1vZGVsIDQgZG9lc24ndCBpbXByb3ZlIHRoZSBtb2RlbCBwZXJmb3JtYW5jZSBmcm9tIHRoZSB0ZW1wb3JhbCBhc3BlY3QuIFRoZXNlIG1heSBiZSBwb3NzaWJsZSB0aGF0IHRoZXJlIGFyZSBubyBob2xpZGF5IHByZXNlbnQgZHVyaW5nIHRoZSB0ZXN0IHBlcmlvZCBvciB0aGVyZSBhcmUgbm8gc2lnbmlmaWNhbnQgZGlmZmVyZW50IGRlbWFuZCBmb3IgYmlrZSBzaGFyZSBkdXJpbmcgaG9saWRheXMuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OH0Kd2Vla19wcmVkaWN0aW9ucyAlPiUKICAgIG11dGF0ZShpbnRlcnZhbDYwID0gbWFwKGRhdGEsIHB1bGwsIGludGVydmFsNjApLAogICAgICAgICAgIHN0YXJ0X3N0YXRpb24gPSBtYXAoZGF0YSwgcHVsbCwgc3RhcnRfc3RhdGlvbikpICU+JQogICAgZHBseXI6OnNlbGVjdChpbnRlcnZhbDYwLCBzdGFydF9zdGF0aW9uLCBPYnNlcnZlZCwgUHJlZGljdGlvbiwgUmVncmVzc2lvbikgJT4lCiAgICB1bm5lc3QoKSAlPiUKICAgIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1SZWdyZXNzaW9uLCAtaW50ZXJ2YWw2MCwgLXN0YXJ0X3N0YXRpb24pICU+JQogICAgZ3JvdXBfYnkoUmVncmVzc2lvbiwgVmFyaWFibGUsIGludGVydmFsNjApICU+JQogICAgc3VtbWFyaXplKFZhbHVlID0gc3VtKFZhbHVlKSkgJT4lCiAgICBnZ3Bsb3QoYWVzKGludGVydmFsNjAsIFZhbHVlLCBjb2xvdXI9VmFyaWFibGUpKSArCiAgICAgIGdlb21fbGluZShzaXplID0gMS4xKSArCiAgICAgIGZhY2V0X3dyYXAoflJlZ3Jlc3Npb24sIG5jb2w9MSkgKwogICAgICBsYWJzKHRpdGxlID0gIlByZWRpY3RlZC9PYnNlcnZlZCBiaWtlIHNoYXJlIHRpbWUgc2VyaWVzIiwgc3VidGl0bGUgPSAiUGhpbGFkZWxwaGlhOyBBIHRlc3Qgc2V0IG9mIDIgd2Vla3MiLCAgeCA9ICJIb3VyIiwgeT0gIlN0YXRpb24gVHJpcHMiKSArCiAgICAgICAgdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEwKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIHNpemU9MC44KSkKYGBgCgojIyMgU3BhdGlhbCBFdmFsdWF0aW9uCkluIHRoaXMgc2VjdGlvbiwgd2UgY29tcGFyZSB0aGUgcHJlZGljdGVkIGFuZCBvYnNlcnZlZCB0cmlwIGNvdW50cyBmcm9tIHRoZSBzcGF0aWFsIGFzcGVjdC4gV2UgcGxvdCB0aGUgcHJlZGljdGVkIGFuZCBvYnNlcnZlZCB0cmlwIGNvdW50cyBmb3IgZWFjaCBtb2RlbCBhY3Jvc3MgZGlmZmVyZW50IHN0YXRpb25zIHRvIGNoZWNrIHdoZXRoZXIgdGhlIG1vZGVsIGNhcHR1cmUgdGhlIHNwYXRpYWwgZWZmZWN0cyBvZiBiaWtlIHNoYXJlIGRlbWFuZC4KClRoZSBtYXAgYmVsb3cgc2hvd3MgdGhhdCB0aGUgTWVhbiBBYnNvbHV0ZSBFcnJvciAoTUFFKSBvZiB0aGUgcHJlZGljdGVkIHRyaXAgY291bnRzIGZvciBlYWNoIG1vZGVsIGFjcm9zcyBkaWZmZXJlbnQgc3RhdGlvbnMuIEl0IHJldmVhbHMgdGhhdCBzdWJzdGFudGlhbGx5IGxhcmdlciBlcnJvcnMgaW4gdGhlIGNlbnRyYWwgdXJiYW4gY29yZSB0aGFuIGluIG91dGx5aW5nIGFyZWFzLiBUaGlzIHBhdHRlcm4gbGlrZWx5IHJlZmxlY3RzIHRoZSBkZW5zZSBjbHVzdGVyaW5nIG9mIHRyaXAgb3JpZ2lucyBkb3dudG93biwgYSB1bmlxdWUgc3BhdGlhbCBjaGFyYWN0ZXJpc3RpYyB0aGF0IGFsbCBtb2RlbHMgZmFpbCB0byBjYXB0dXJlIGFkZXF1YXRlbHkuCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9CnBsb3QxPC13ZWVrX3ByZWRpY3Rpb25zICU+JQogICAgbXV0YXRlKGludGVydmFsNjAgPSBtYXAoZGF0YSwgcHVsbCwgaW50ZXJ2YWw2MCksCiAgICAgICAgICAgc3RhcnRfc3RhdGlvbiA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9zdGF0aW9uKSwKICAgICAgICAgICBzdGFydF9sYXQgPSBtYXAoZGF0YSwgcHVsbCwgc3RhcnRfbGF0KSwKICAgICAgICAgICBzdGFydF9sb24gPSBtYXAoZGF0YSwgcHVsbCwgc3RhcnRfbG9uKSkgJT4lCiAgICBzZWxlY3QoaW50ZXJ2YWw2MCwgc3RhcnRfc3RhdGlvbiwgc3RhcnRfbG9uLCBzdGFydF9sYXQsIE9ic2VydmVkLCBQcmVkaWN0aW9uLCBSZWdyZXNzaW9uKSAlPiUKICAgIHVubmVzdCgpICU+JQogIGZpbHRlcihSZWdyZXNzaW9uID09ICJEVGltZV9TcGFjZV9GRV90aW1lTGFncyIpICU+JQogIGdyb3VwX2J5KHN0YXJ0X3N0YXRpb24sIHN0YXJ0X2xvbiwgc3RhcnRfbGF0KSAlPiUKICBzdW1tYXJpemUoTUFFID0gbWVhbihhYnMoT2JzZXJ2ZWQtUHJlZGljdGlvbiksIG5hLnJtID0gVFJVRSkpJT4lCmdncGxvdCguKSsKICBnZW9tX3NmKGRhdGEgPSBwaGlsQ2Vuc3VzLCBjb2xvciA9ICJncmV5IiwgZmlsbCA9ICJ0cmFuc3BhcmVudCIpKwogIGdlb21fcG9pbnQoYWVzKHggPSBzdGFydF9sb24sIHkgPSBzdGFydF9sYXQsIGNvbG9yID0gTUFFKSwKICAgICAgICAgICAgIGZpbGwgPSAidHJhbnNwYXJlbnQiLCBhbHBoYSA9IDAuNykrCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXMoZGlyZWN0aW9uID0gLTEsCiAgZGlzY3JldGUgPSBGQUxTRSwgb3B0aW9uID0gIkQiKSsKICB5bGltKG1pbihkYXRfY2Vuc3VzJHN0YXJ0X2xhdCksIG1heChkYXRfY2Vuc3VzJHN0YXJ0X2xhdCkpKwogIHhsaW0obWluKGRhdF9jZW5zdXMkc3RhcnRfbG9uKSwgbWF4KGRhdF9jZW5zdXMkc3RhcnRfbG9uKSkrCiAgbGFicyh0aXRsZT0iTWVhbiBBYnMgRXJyb3IsIFRlc3QgU2V0LCBNb2RlbCA0IikrCiAgbWFwVGhlbWUKCnBsb3QyPC13ZWVrX3ByZWRpY3Rpb25zICU+JQogICAgbXV0YXRlKGludGVydmFsNjAgPSBtYXAoZGF0YSwgcHVsbCwgaW50ZXJ2YWw2MCksCiAgICAgICAgICAgc3RhcnRfc3RhdGlvbiA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9zdGF0aW9uKSwKICAgICAgICAgICBzdGFydF9sYXQgPSBtYXAoZGF0YSwgcHVsbCwgc3RhcnRfbGF0KSwKICAgICAgICAgICBzdGFydF9sb24gPSBtYXAoZGF0YSwgcHVsbCwgc3RhcnRfbG9uKSkgJT4lCiAgICBzZWxlY3QoaW50ZXJ2YWw2MCwgc3RhcnRfc3RhdGlvbiwgc3RhcnRfbG9uLCBzdGFydF9sYXQsIE9ic2VydmVkLCBQcmVkaWN0aW9uLCBSZWdyZXNzaW9uKSAlPiUKICAgIHVubmVzdCgpICU+JQogIGZpbHRlcihSZWdyZXNzaW9uID09ICJDVGltZV9TcGFjZV9GRSIpICU+JQogIGdyb3VwX2J5KHN0YXJ0X3N0YXRpb24sIHN0YXJ0X2xvbiwgc3RhcnRfbGF0KSAlPiUKICBzdW1tYXJpemUoTUFFID0gbWVhbihhYnMoT2JzZXJ2ZWQtUHJlZGljdGlvbiksIG5hLnJtID0gVFJVRSkpJT4lCmdncGxvdCguKSsKICBnZW9tX3NmKGRhdGEgPSBwaGlsQ2Vuc3VzLCBjb2xvciA9ICJncmV5IiwgZmlsbCA9ICJ0cmFuc3BhcmVudCIpKwogIGdlb21fcG9pbnQoYWVzKHggPSBzdGFydF9sb24sIHkgPSBzdGFydF9sYXQsIGNvbG9yID0gTUFFKSwKICAgICAgICAgICAgIGZpbGwgPSAidHJhbnNwYXJlbnQiLCBhbHBoYSA9IDAuNykrCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXMoZGlyZWN0aW9uID0gLTEsCiAgZGlzY3JldGUgPSBGQUxTRSwgb3B0aW9uID0gIkQiKSsKICB5bGltKG1pbihkYXRfY2Vuc3VzJHN0YXJ0X2xhdCksIG1heChkYXRfY2Vuc3VzJHN0YXJ0X2xhdCkpKwogIHhsaW0obWluKGRhdF9jZW5zdXMkc3RhcnRfbG9uKSwgbWF4KGRhdF9jZW5zdXMkc3RhcnRfbG9uKSkrCiAgbGFicyh0aXRsZT0iTWVhbiBBYnMgRXJyb3IsIFRlc3QgU2V0LCBNb2RlbCAzIikrCiAgbWFwVGhlbWUKCnBsb3QzPC13ZWVrX3ByZWRpY3Rpb25zICU+JQogICAgbXV0YXRlKGludGVydmFsNjAgPSBtYXAoZGF0YSwgcHVsbCwgaW50ZXJ2YWw2MCksCiAgICAgICAgICAgc3RhcnRfc3RhdGlvbiA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9zdGF0aW9uKSwKICAgICAgICAgICBzdGFydF9sYXQgPSBtYXAoZGF0YSwgcHVsbCwgc3RhcnRfbGF0KSwKICAgICAgICAgICBzdGFydF9sb24gPSBtYXAoZGF0YSwgcHVsbCwgc3RhcnRfbG9uKSkgJT4lCiAgICBzZWxlY3QoaW50ZXJ2YWw2MCwgc3RhcnRfc3RhdGlvbiwgc3RhcnRfbG9uLCBzdGFydF9sYXQsIE9ic2VydmVkLCBQcmVkaWN0aW9uLCBSZWdyZXNzaW9uKSAlPiUKICAgIHVubmVzdCgpICU+JQogIGZpbHRlcihSZWdyZXNzaW9uID09ICJCU3BhY2VfRkUiKSAlPiUKICBncm91cF9ieShzdGFydF9zdGF0aW9uLCBzdGFydF9sb24sIHN0YXJ0X2xhdCkgJT4lCiAgc3VtbWFyaXplKE1BRSA9IG1lYW4oYWJzKE9ic2VydmVkLVByZWRpY3Rpb24pLCBuYS5ybSA9IFRSVUUpKSU+JQpnZ3Bsb3QoLikrCiAgZ2VvbV9zZihkYXRhID0gcGhpbENlbnN1cywgY29sb3IgPSAiZ3JleSIsIGZpbGwgPSAidHJhbnNwYXJlbnQiKSsKICBnZW9tX3BvaW50KGFlcyh4ID0gc3RhcnRfbG9uLCB5ID0gc3RhcnRfbGF0LCBjb2xvciA9IE1BRSksCiAgICAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50IiwgYWxwaGEgPSAwLjcpKwogIHNjYWxlX2NvbG91cl92aXJpZGlzKGRpcmVjdGlvbiA9IC0xLAogIGRpc2NyZXRlID0gRkFMU0UsIG9wdGlvbiA9ICJEIikrCiAgeWxpbShtaW4oZGF0X2NlbnN1cyRzdGFydF9sYXQpLCBtYXgoZGF0X2NlbnN1cyRzdGFydF9sYXQpKSsKICB4bGltKG1pbihkYXRfY2Vuc3VzJHN0YXJ0X2xvbiksIG1heChkYXRfY2Vuc3VzJHN0YXJ0X2xvbikpKwogIGxhYnModGl0bGU9Ik1lYW4gQWJzIEVycm9yLCBUZXN0IFNldCwgTW9kZWwgMiIpKwogIG1hcFRoZW1lCgpwbG90NDwtIHdlZWtfcHJlZGljdGlvbnMgJT4lCiAgICBtdXRhdGUoaW50ZXJ2YWw2MCA9IG1hcChkYXRhLCBwdWxsLCBpbnRlcnZhbDYwKSwKICAgICAgICAgICBzdGFydF9zdGF0aW9uID0gbWFwKGRhdGEsIHB1bGwsIHN0YXJ0X3N0YXRpb24pLAogICAgICAgICAgIHN0YXJ0X2xhdCA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9sYXQpLAogICAgICAgICAgIHN0YXJ0X2xvbiA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9sb24pKSAlPiUKICAgIHNlbGVjdChpbnRlcnZhbDYwLCBzdGFydF9zdGF0aW9uLCBzdGFydF9sb24sIHN0YXJ0X2xhdCwgT2JzZXJ2ZWQsIFByZWRpY3Rpb24sIFJlZ3Jlc3Npb24pICU+JQogICAgdW5uZXN0KCkgJT4lCiAgZmlsdGVyKFJlZ3Jlc3Npb24gPT0gIkFUaW1lX0ZFIikgJT4lCiAgZ3JvdXBfYnkoc3RhcnRfc3RhdGlvbiwgc3RhcnRfbG9uLCBzdGFydF9sYXQpICU+JQogIHN1bW1hcml6ZShNQUUgPSBtZWFuKGFicyhPYnNlcnZlZC1QcmVkaWN0aW9uKSwgbmEucm0gPSBUUlVFKSklPiUKZ2dwbG90KC4pKwogIGdlb21fc2YoZGF0YSA9IHBoaWxDZW5zdXMsIGNvbG9yID0gImdyZXkiLCBmaWxsID0gInRyYW5zcGFyZW50IikrCiAgZ2VvbV9wb2ludChhZXMoeCA9IHN0YXJ0X2xvbiwgeSA9IHN0YXJ0X2xhdCwgY29sb3IgPSBNQUUpLAogICAgICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGFscGhhID0gMC43KSsKICBzY2FsZV9jb2xvdXJfdmlyaWRpcyhkaXJlY3Rpb24gPSAtMSwKICBkaXNjcmV0ZSA9IEZBTFNFLCBvcHRpb24gPSAiRCIpKwogIHlsaW0obWluKGRhdF9jZW5zdXMkc3RhcnRfbGF0KSwgbWF4KGRhdF9jZW5zdXMkc3RhcnRfbGF0KSkrCiAgeGxpbShtaW4oZGF0X2NlbnN1cyRzdGFydF9sb24pLCBtYXgoZGF0X2NlbnN1cyRzdGFydF9sb24pKSsKICBsYWJzKHRpdGxlPSJNZWFuIEFicyBFcnJvciwgVGVzdCBTZXQsIE1vZGVsIDEiKSsKICBtYXBUaGVtZQoKcGxvdDU8LSB3ZWVrX3ByZWRpY3Rpb25zICU+JQogICAgbXV0YXRlKGludGVydmFsNjAgPSBtYXAoZGF0YSwgcHVsbCwgaW50ZXJ2YWw2MCksCiAgICAgICAgICAgc3RhcnRfc3RhdGlvbiA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9zdGF0aW9uKSwKICAgICAgICAgICBzdGFydF9sYXQgPSBtYXAoZGF0YSwgcHVsbCwgc3RhcnRfbGF0KSwKICAgICAgICAgICBzdGFydF9sb24gPSBtYXAoZGF0YSwgcHVsbCwgc3RhcnRfbG9uKSkgJT4lCiAgICBzZWxlY3QoaW50ZXJ2YWw2MCwgc3RhcnRfc3RhdGlvbiwgc3RhcnRfbG9uLCBzdGFydF9sYXQsIE9ic2VydmVkLCBQcmVkaWN0aW9uLCBSZWdyZXNzaW9uKSAlPiUKICAgIHVubmVzdCgpICU+JQogIGZpbHRlcihSZWdyZXNzaW9uID09ICJFVGltZV9TcGFjZV9GRV90aW1lTGFnc19ob2xpZGF5TGFncyIpICU+JQogIGdyb3VwX2J5KHN0YXJ0X3N0YXRpb24sIHN0YXJ0X2xvbiwgc3RhcnRfbGF0KSAlPiUKICBzdW1tYXJpemUoTUFFID0gbWVhbihhYnMoT2JzZXJ2ZWQtUHJlZGljdGlvbiksIG5hLnJtID0gVFJVRSkpJT4lCiAgZ2dwbG90KC4pKwogIGdlb21fc2YoZGF0YSA9IHBoaWxDZW5zdXMsIGNvbG9yID0gImdyZXkiLCBmaWxsID0gInRyYW5zcGFyZW50IikrCiAgZ2VvbV9wb2ludChhZXMoeCA9IHN0YXJ0X2xvbiwgeSA9IHN0YXJ0X2xhdCwgY29sb3IgPSBNQUUpLAogICAgICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGFscGhhID0gMC43KSsKICBzY2FsZV9jb2xvdXJfdmlyaWRpcyhkaXJlY3Rpb24gPSAtMSwKICBkaXNjcmV0ZSA9IEZBTFNFLCBvcHRpb24gPSAiRCIpKwogIHlsaW0obWluKGRhdF9jZW5zdXMkc3RhcnRfbGF0KSwgbWF4KGRhdF9jZW5zdXMkc3RhcnRfbGF0KSkrCiAgeGxpbShtaW4oZGF0X2NlbnN1cyRzdGFydF9sb24pLCBtYXgoZGF0X2NlbnN1cyRzdGFydF9sb24pKSsKICBsYWJzKHRpdGxlPSJNZWFuIEFicyBFcnJvciwgVGVzdCBTZXQsIE1vZGVsIDUiKSsKICBtYXBUaGVtZQoKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkoZ3JpZCkKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkocGF0Y2h3b3JrKQoKCmxpYnJhcnkocGF0Y2h3b3JrKQoKcGxvdDQgKyBwbG90MyArIHBsb3QzICsgcGxvdDEgKyBwbG90NSArCiAgcGxvdF9sYXlvdXQobmNvbCA9IDMpICsKICBwbG90X2Fubm90YXRpb24oCiAgICB0aXRsZSA9ICJNZWFuIEFic29sdXRlIEVycm9ycywgVGVzdCBTZXQiLAogICAgdGhlbWUgPSB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIikpCiAgKQoKCmBgYAojIyBFcnJvciBFeHBsb3JhdGlvbgoKVGhlIHNwYXRpYWwgTUFFIHBhbmVscyByZXZlYWwgdGhhdCBwcmVkaWN0aW9uIGVycm9ycyBjb25jZW50cmF0ZSBpbiB0aGUgY2l0eeKAmXMgY29yZeKAlG1vc3Qgbm90YWJseSBkdXJpbmcgd2Vla2RheSBBTSBhbmQgUE0gcnVzaOKAlHdoZXJlIE1BRSBmcmVxdWVudGx5LiBCeSBjb250cmFzdCwgbWlk4oCQZGF5IGFuZCBvdmVybmlnaHQgaW50ZXJ2YWxzIHNob3cgdW5pZm9ybWx5IGxvdyBlcnJvcnMsIHJlZmxlY3RpbmcgYm90aCBsb3dlciBhYnNvbHV0ZSB2b2x1bWVzIGFuZCBtb3JlIHJlZ3VsYXIgZGVtYW5kIHBhdHRlcm5zLiBXZWVrZW5kIHJ1c2ggcGVyaW9kcyBhbHNvIGV4aGliaXQgc21hbGxlciBNQUUgdGhhbiB3ZWVrZGF5IHBlYWtzLiBUaGVzZSBwYXR0ZXJucyBwb2ludCB0byBzeXN0ZW1hdGljIHVuZGVy4oCQIG9yIG92ZXLigJBwcmVkaWN0aW9uIHdoZXJlIHRyaXAgc3RhcnRzIGFyZSBib3RoIGRlbnNlc3QgYW5kIG1vc3Qgdm9sYXRpbGUsIGFuZCB3aGVyZSBzaW1wbGUgbGFn4oCQYmFzZWQgbGluZWFyIGVmZmVjdHMgc3RydWdnbGUgdG8gY2FwdHVyZSBzdWRkZW4gc3VyZ2VzIG9yIGRyb3DigJBvZmZzIGluIHVzYWdlLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMH0Kd2Vla19wcmVkaWN0aW9ucyAlPiUKICAgIG11dGF0ZShpbnRlcnZhbDYwID0gbWFwKGRhdGEsIHB1bGwsIGludGVydmFsNjApLAogICAgICAgICAgIHN0YXJ0X3N0YXRpb24gPSBtYXAoZGF0YSwgcHVsbCwgc3RhcnRfc3RhdGlvbiksCiAgICAgICAgICAgc3RhcnRfbGF0ID0gbWFwKGRhdGEsIHB1bGwsIHN0YXJ0X2xhdCksCiAgICAgICAgICAgc3RhcnRfbG9uID0gbWFwKGRhdGEsIHB1bGwsIHN0YXJ0X2xvbiksCiAgICAgICAgICAgZG90dyA9IG1hcChkYXRhLCBwdWxsLCBkb3R3KSApICU+JQogICAgc2VsZWN0KGludGVydmFsNjAsIHN0YXJ0X3N0YXRpb24sIHN0YXJ0X2xvbiwKICAgICAgICAgICBzdGFydF9sYXQsIE9ic2VydmVkLCBQcmVkaWN0aW9uLCBSZWdyZXNzaW9uLAogICAgICAgICAgIGRvdHcpICU+JQogICAgdW5uZXN0KCkgJT4lCiAgZmlsdGVyKFJlZ3Jlc3Npb24gPT0gIkVUaW1lX1NwYWNlX0ZFX3RpbWVMYWdzX2hvbGlkYXlMYWdzIiklPiUKICBtdXRhdGUod2Vla2VuZCA9IGlmZWxzZShkb3R3ICVpbiUgYygiU3VuIiwgIlNhdCIpLCAiV2Vla2VuZCIsICJXZWVrZGF5IiksCiAgICAgICAgIHRpbWVfb2ZfZGF5ID0gY2FzZV93aGVuKGhvdXIoaW50ZXJ2YWw2MCkgPCA3IHwgaG91cihpbnRlcnZhbDYwKSA+IDE4IH4gIk92ZXJuaWdodCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIoaW50ZXJ2YWw2MCkgPj0gNyAmIGhvdXIoaW50ZXJ2YWw2MCkgPCAxMCB+ICJBTSBSdXNoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSAxMCAmIGhvdXIoaW50ZXJ2YWw2MCkgPCAxNSB+ICJNaWQtRGF5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSAxNSAmIGhvdXIoaW50ZXJ2YWw2MCkgPD0gMTggfiAiUE0gUnVzaCIpKSAlPiUKICBncm91cF9ieShzdGFydF9zdGF0aW9uLCB3ZWVrZW5kLCB0aW1lX29mX2RheSwgc3RhcnRfbG9uLCBzdGFydF9sYXQpICU+JQogIHN1bW1hcml6ZShNQUUgPSBtZWFuKGFicyhPYnNlcnZlZC1QcmVkaWN0aW9uKSwgbmEucm0gPSBUUlVFKSklPiUKICBnZ3Bsb3QoLikrCiAgZ2VvbV9zZihkYXRhID0gcGhpbENlbnN1cywgY29sb3IgPSAiZ3JleSIsIGZpbGwgPSAidHJhbnNwYXJlbnQiKSsKICBnZW9tX3BvaW50KGFlcyh4ID0gc3RhcnRfbG9uLCB5ID0gc3RhcnRfbGF0LCBjb2xvciA9IE1BRSksCiAgICAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50Iiwgc2l6ZSA9IDMsIGFscGhhID0gMC40KSsKICBzY2FsZV9jb2xvdXJfdmlyaWRpcyhkaXJlY3Rpb24gPSAtMSwKICBkaXNjcmV0ZSA9IEZBTFNFLCBvcHRpb24gPSAiRCIpKwogIHlsaW0obWluKGRhdF9jZW5zdXMkc3RhcnRfbGF0KSwgbWF4KGRhdF9jZW5zdXMkc3RhcnRfbGF0KSkrCiAgeGxpbShtaW4oZGF0X2NlbnN1cyRzdGFydF9sb24pLCBtYXgoZGF0X2NlbnN1cyRzdGFydF9sb24pKSsKICBmYWNldF9ncmlkKHdlZWtlbmR+dGltZV9vZl9kYXkpKwogIGxhYnModGl0bGU9Ik1lYW4gQWJzb2x1dGUgRXJyb3JzLCBUZXN0IFNldCIpKwogIG1hcFRoZW1lCgpgYGAKCiMjIyBTb2Npby1FY29ub21pYyBGYWN0b3JzCgpJbiBhZGRpdGlvbiwgYWx0aG91Z2ggdGhlIGN1cnJlbnQgbW9kZWwgaW5jbHVkZSB3ZWF0aGVyLCB0ZW1wb3JhbCB2YXJpYWJsZSwgdGVtcG9yYWwgbGFnZ2VkIHZhcmlhYmxlcywgYW5kIGV2ZW4gaG9saWRheSBlZmZlY3RzLCB0aGUgbW9kZWwgZG8gb21pdCB0aGUgY3JpdGljYWwgc29jaW8tZWNvbm9taWMgYW5kIGxhbmQtdXNlIGZhY3RvcnMgdGhhdCBtYXkgYWxzbyBpbmZsdWVuY2UgdGhlIGJpa2VzaGFyZSBkZW1hbmQuIEFzIHNob3duIGluIGZvbGxvd2luZyBjaGFydHMsIG1lZGlhbiBob3VzZWhvbGQgaW5jb21lLCBwdWJsaWMgdHJhbnNwb3J0YXRpb24gYWNjZXNzIGFuZCBwZXJjZW50YWdlIG9mIHJhY2lhbCBncm91cCBhbGwgc2hvdyBhIGNvcnJlbGF0aW9uIHdpdGggdGhlIGVycm9yIHRlcm0gb2YgdGhlIG1vZGVscywgd2hpY2ggaW5kaWNhdGUgdGhlIG1vZGVsIG1heSBiZSBpbXByb3ZlZCBieSBpbmNsdWRpbmcgdGhlc2UgdmFyaWFibGVzLiAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9IDR9CndlZWtfcHJlZGljdGlvbnMgJT4lCiAgICBtdXRhdGUoaW50ZXJ2YWw2MCA9IG1hcChkYXRhLCBwdWxsLCBpbnRlcnZhbDYwKSwKICAgICAgICAgICBzdGFydF9zdGF0aW9uID0gbWFwKGRhdGEsIHB1bGwsIHN0YXJ0X3N0YXRpb24pLAogICAgICAgICAgIHN0YXJ0X2xhdCA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9sYXQpLAogICAgICAgICAgIHN0YXJ0X2xvbiA9IG1hcChkYXRhLCBwdWxsLCBzdGFydF9sb24pLAogICAgICAgICAgIGRvdHcgPSBtYXAoZGF0YSwgcHVsbCwgZG90dyksCiAgICAgICAgICAgUGVyY2VudF9UYWtpbmdfUHVibGljX1RyYW5zID0gbWFwKGRhdGEsIHB1bGwsIFBlcmNlbnRfVGFraW5nX1B1YmxpY19UcmFucyksCiAgICAgICAgICAgTWVkX0luYyA9IG1hcChkYXRhLCBwdWxsLCBNZWRfSW5jKSwKICAgICAgICAgICBQZXJjZW50X1doaXRlID0gbWFwKGRhdGEsIHB1bGwsIFBlcmNlbnRfV2hpdGUpKSAlPiUKICAgIHNlbGVjdChpbnRlcnZhbDYwLCBzdGFydF9zdGF0aW9uLCBzdGFydF9sb24sCiAgICAgICAgICAgc3RhcnRfbGF0LCBPYnNlcnZlZCwgUHJlZGljdGlvbiwgUmVncmVzc2lvbiwKICAgICAgICAgICBkb3R3LCBQZXJjZW50X1Rha2luZ19QdWJsaWNfVHJhbnMsIE1lZF9JbmMsIFBlcmNlbnRfV2hpdGUpICU+JQogICAgdW5uZXN0KCkgJT4lCiAgZmlsdGVyKFJlZ3Jlc3Npb24gPT0gIkVUaW1lX1NwYWNlX0ZFX3RpbWVMYWdzX2hvbGlkYXlMYWdzIiklPiUKICBtdXRhdGUod2Vla2VuZCA9IGlmZWxzZShkb3R3ICVpbiUgYygiU3VuIiwgIlNhdCIpLCAiV2Vla2VuZCIsICJXZWVrZGF5IiksCiAgICAgICAgIHRpbWVfb2ZfZGF5ID0gY2FzZV93aGVuKGhvdXIoaW50ZXJ2YWw2MCkgPCA3IHwgaG91cihpbnRlcnZhbDYwKSA+IDE4IH4gIk92ZXJuaWdodCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIoaW50ZXJ2YWw2MCkgPj0gNyAmIGhvdXIoaW50ZXJ2YWw2MCkgPCAxMCB+ICJBTSBSdXNoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSAxMCAmIGhvdXIoaW50ZXJ2YWw2MCkgPCAxNSB+ICJNaWQtRGF5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG91cihpbnRlcnZhbDYwKSA+PSAxNSAmIGhvdXIoaW50ZXJ2YWw2MCkgPD0gMTggfiAiUE0gUnVzaCIpKSAlPiUKICBmaWx0ZXIodGltZV9vZl9kYXkgPT0gIkFNIFJ1c2giKSAlPiUKICBncm91cF9ieShzdGFydF9zdGF0aW9uLCBQZXJjZW50X1Rha2luZ19QdWJsaWNfVHJhbnMsIE1lZF9JbmMsIFBlcmNlbnRfV2hpdGUpICU+JQogIHN1bW1hcml6ZShNQUUgPSBtZWFuKGFicyhPYnNlcnZlZC1QcmVkaWN0aW9uKSwgbmEucm0gPSBUUlVFKSklPiUKICBnYXRoZXIoLXN0YXJ0X3N0YXRpb24sIC1NQUUsIGtleSA9ICJ2YXJpYWJsZSIsIHZhbHVlID0gInZhbHVlIiklPiUKICBnZ3Bsb3QoLikrCiAgI2dlb21fc2YoZGF0YSA9IGNoaWNhZ29DZW5zdXMsIGNvbG9yID0gImdyZXkiLCBmaWxsID0gInRyYW5zcGFyZW50IikrCiAgZ2VvbV9wb2ludChhZXMoeCA9IHZhbHVlLCB5ID0gTUFFKSwgYWxwaGEgPSAwLjQpKwogIGdlb21fc21vb3RoKGFlcyh4ID0gdmFsdWUsIHkgPSBNQUUpLCBtZXRob2QgPSAibG0iLCBzZT0gRkFMU0UpKwogIGZhY2V0X3dyYXAofnZhcmlhYmxlLCBzY2FsZXMgPSAiZnJlZSIpKwogIGxhYnModGl0bGU9IkVycm9ycyBhcyBhIGZ1bmN0aW9uIG9mIHNvY2lvLWVjb25vbWljIHZhcmlhYmxlcyIsCiAgICAgICB5PSJNZWFuIEFic29sdXRlIEVycm9yIChUcmlwcykiKSsKICAgIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkpCmBgYAoKIyMjIFBvdGVudGlhbCBpbXByb3ZtZW50CkZvciBwb3RlbnRpYWwgbW9kZWwgaW1wcm92ZW1lbnQsIHRoZSBiaWtlc2hhcmUgZGF0YSB3ZXJlIGNvbnNpZGVyZWQgY291bnQgZGF0YSwgd2hpY2ggbm90IHN0cmljdGx5IGNvbnNpZGVyZWQgYXMgY29udGludW91cyB2YXJpYWJsZS4gVGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBjb3VudCBkYXRhIGFuZCB0aGUgcHJlZGljdG9ycyBhcmUgbm90IHBlcmZlY3QgbGluZWFyIHJlbGF0aW9uc2hpcC4gVGhlIG1vZGVsIGNvdWxkIGJlIGltcHJvdmVkIGJ5IHVzaW5nIFBvaXNzb24gcmVncmVzc2lvbiBvciBOZWdhdGl2ZSBCaW5vbWlhbCByZWdyZXNzaW9uLCB3aGljaCBhcmUgbW9yZSBzdWl0YWJsZSBmb3IgY291bnQgZGF0YSBhbmQgcmVkdWNlIGhldGVyb2dlbmVpdHkuIAoKSW4gYWRkaXRpb24sIGVucmljaCB0aGUgZmVhdHVyZSBzZXQgd2l0aCB0aGUgc29jaW8tZWNvbm9taWMgYW5kIGxhbmQtdXNlLCBhbmQgZXhwZXJpbWVudCB3aXRoIGludGVyYWN0aW9uIHRlcm1zIChlLmcuLCAqKmhvdXIgw5cgcHJlY2lwaXRhdGlvbiwgc3RhdGlvbiDDlyBob2xpZGF5KiopIG9yIHRyZWUtYmFzZWQgbGVhcm5lcnMgKGUuZy4sIGdyYWRpZW50IGJvb3N0aW5nKSB0byBjYXB0dXJlIG5vbmxpbmVhcml0aWVz4oCUc3RlcHMgdGhhdCB0b2dldGhlciBzaG91bGQgcmVkdWNlIGNlbnRyYWwtY2l0eSBNQUUgYW5kIGJldHRlciBoYW5kbGUgZGVtYW5kIHN1cmdlcy4KCiMjIFJhYmFsYW5jaW5nIEFwcGxpY2F0aW9uCgpCYXNlZCBvbiB0aGUgbW9kZWwgcHJlZGljdGlvbnMgZGVzY3JpYmVkIGFib3ZlLCB0aGUgcmViYWxhbmNpbmcgc3RyYXRlZ3kgY2FuIGJlIHNpZ25pZmljYW50bHkgZW5oYW5jZWQgYnkgdGFyZ2V0aW5nIGJvdGggd2hlbiBhbmQgd2hlcmUgYmlrZSBzaG9ydGFnZXMgb3Igc3VycGx1c2VzIGFyZSBtb3N0IGxpa2VseSB0byBvY2N1ci4gVGhlIGByZWc0YCBtb2RlbCBpbmNvcnBvcmF0aW5nIHNwYXRpYWwsIHRlbXBvcmFsLCBhbmQgbGFnZ2VkIGRlbWFuZCBmZWF0dXJlcywgcHJlZGljdHMgYmlrZS1zaGFyZSB1c2FnZSBhdCB0aGUgc3RhdGlvbi1ob3VyIGxldmVsIHdpdGggc3Ryb25nIGFjY3VyYWN5LiBUaGlzIGFsbG93cyBvcGVyYXRvcnMgdG8gYWxsb2NhdGUgcmViYWxhbmNpbmcgdHJ1Y2tzIHByb2FjdGl2ZWx5IHRvd2FyZCBoaWdoLWRlbWFuZCBzdGF0aW9ucyBkdXJpbmcgY3JpdGljYWwgcGVyaW9kcywgZXNwZWNpYWxseSBkdXJpbmcgUE0gcnVzaCBob3Vycy4gU2luY2UgdGhlIHJpZGVyc2hpcCBpcyBoaWdoZXIgaW4gUGhpbGFkZWxwaGlh4oCZcyBkZW5zZSBkb3dudG93biBjb3JlLCBtb3N0IG9mIHRoZSBiaWtlcyB3aWxsIG5lZWQgdG8gcHV0IGJhY2sgaW50byB0aGUgY2VudHJhbCBjaXRpZXMgYXJlYXMuIEJ5IGFudGljaXBhdGluZyBkZW1hbmQgcGVha3Mgc2V2ZXJhbCBob3VycyBhaGVhZCwgbWFuYWdlcnMgY2FuIHNjaGVkdWxlIHRydWNrIGRlcGxveW1lbnRzIG1vcmUgZWZmaWNpZW50bHkgdG8gYXZvaWQgYm90aCBiaWtlIHNob3J0YWdlcyBhdCBoaWdoLXVzYWdlIHN0YXRpb25zIGFuZCBvdmVyZmxvdyBhdCBwb3B1bGFyIHJldHVybiBzdGF0aW9ucy4KCkFkZGl0aW9uYWxseSwgdGhlIG1vZGVs4oCZcyB0aW1lLXNwZWNpZmljIGZvcmVjYXN0cyBjb3VsZCBzdXBwb3J0IGR5bmFtaWMgaW5jZW50aXZlIHByb2dyYW1zLiBGb3IgaW5zdGFuY2UsIHVzZXJzIGNvdWxkIGJlIGVuY291cmFnZWQgdGhyb3VnaCBkaXNjb3VudHMgb3IgY3JlZGl0cyB0byBwaWNrIHVwIGJpa2VzIGZyb20gc3VycGx1cyBzdGF0aW9ucyBvciByZXR1cm4gYmlrZXMgdG8gZGVmaWNpdCBzdGF0aW9ucywgYmFzZWQgb24gbmVhci1mdXR1cmUgcHJlZGljdGlvbnMuIFRoaXMgZHVhbCBhcHByb2FjaOKAlHRhcmdldGVkIHRydWNrIHJlYWxsb2NhdGlvbiBhbmQgZGVtYW5kLXNoYXBpbmcgaW5jZW50aXZlc+KAlGNhbiBoZWxwIG1haW50YWluIGEgYmV0dGVyIHN5c3RlbSBiYWxhbmNlLCBpbXByb3ZlIHVzZXIgc2F0aXNmYWN0aW9uLCBhbmQgcmVkdWNlIHRoZSBvcGVyYXRpb25hbCBidXJkZW4gb24gcmViYWxhbmNpbmcgdGVhbXMuIAoKIyBDb25jbHVzaW9uCgpUaGUgbW9kZWwgYnVpbHQgZm9yIHByZWRpY3RpbmcgYmlrZS1zaGFyZSBkZW1hbmQgZ2F2ZSB1c2VmdWwgZm9yZWNhc3RzIGF0IHRoZSBzdGF0aW9uIGFuZCBob3VyIGxldmVsIHRvIGVuc3VyZSB0aGUgZWZmaWNpZW5jeSBhbmQgZWZmZWN0aXZlIG9mIHRoZSBzeXN0ZW0uIFRoZSBiZXN0IG1vZGVsIChgcmVnNGApIGFjY3VyYXRlbHkgY2FwdHVyZWQgaG93IGJpa2UgZGVtYW5kIGNoYW5nZXMgZHVyaW5nIHRoZSBkYXkgYW5kIGJldHdlZW4gd2Vla2RheXMgYW5kIHdlZWtlbmRzLiBUaGlzIGhlbHBzIGJpa2Utc2hhcmUgb3BlcmF0b3JzIHBsYW4gYWhlYWQgYnkgc2VuZGluZyByZWJhbGFuY2luZyB0cnVja3MgdG8gYnVzeSBzdGF0aW9ucyBiZWZvcmUgc2hvcnRhZ2VzIGhhcHBlbiwgZXNwZWNpYWxseSBkdXJpbmcgYWZ0ZXJub29uIHJ1c2ggaG91cnMgaW4gZG93bnRvd24gUGhpbGFkZWxwaGlhLiBUaGUgbW9kZWwgY2FuIGFsc28gc3VwcG9ydCBvZmZlcmluZyBpbmNlbnRpdmVzIHRvIHVzZXJzLCBlbmNvdXJhZ2luZyB0aGVtIHRvIHBpY2sgdXAgb3IgZHJvcCBvZmYgYmlrZXMgd2hlcmUgdGhleSBhcmUgbW9zdCBuZWVkZWQuCgpIb3dldmVyLCBhY2N1cmF0ZWx5IHByZWRpY3RpbmcgYmlrZXNoYXJlIGRlbWFuZCBzdGlsbCBwcmVzZW50cyBzb21lIGNoYWxsZW5nZXMuIFRoZSBtb2RlbCBoYWQgYmlnZ2VyIHByZWRpY3Rpb24gZXJyb3JzIGluIHRoZSBkb3dudG93biBhcmVhIGR1cmluZyBwZWFrIHRpbWVzLCB3aGVyZSBkZW1hbmQgY2hhbmdlcyBxdWlja2x5LiBBbHNvLCBpdCBkaWQgbm90IGluY2x1ZGUgaW1wb3J0YW50IGZhY3RvcnMgbGlrZSBpbmNvbWUgbGV2ZWxzIG9yIGFjY2VzcyB0byBwdWJsaWMgdHJhbnNpdCwgd2hpY2ggc2VlbSB0byBhZmZlY3QgYmlrZSBkZW1hbmQuIEluIHRoZSBmdXR1cmUsIHRoZSBtb2RlbCBjb3VsZCBiZSBpbXByb3ZlZCBieSBhZGRpbmcgdGhlc2Uga2luZHMgb2YgZGF0YSBhbmQgYnkgdXNpbmcgYmV0dGVyIG1ldGhvZHMgZm9yIGNvdW50IGRhdGEsIGxpa2UgUG9pc3NvbiByZWdyZXNzaW9uIG9yIG1vcmUgYWR2YW5jZWQgbWFjaGluZSBsZWFybmluZyBtb2RlbHMsIHRvIG1ha2UgdGhlIHByZWRpY3Rpb25zIGV2ZW4gbW9yZSBhY2N1cmF0ZS4=