Transferable Bike Network Planning: Reinforcement Learning for Pareto-Optimal Lane Reallocation

I’ve always hated travelling to and from from airport to my place in this mumbai traffic and it would for sure beat my favourite playlist. Since I don’t engage in any deleterious habits, my traffic survival kit with all amazing snacks for the ride and petrol bill collectively a decent down payment on a house in Andheri. A poor joke, I know. And of course there’s always some new flyover or metro pillar coming up everywhere yet not a single sign of traffic easing. That said, I’m fairly certain most people would be furrowing their brows in total, grudging recognition. Thousands of dollars are spent yearly on expanding car lanes throughout the cities worldwide. Yet cities everywhere are more congested than ever.

Recent events such as Kuala Lumpur’s “Bangun KL” campaign tried to solve gridlock by asking people to start their day earlier, offering discounted coffee as an incentive. However, most commuters’ schedules are already fixed which includes parents dropping children at school by 6:30 am, teachers, civil servants, and shift workers who cannot shift their hours. Meanwhile a study by Institute for Transportation and Development Policy India reveals Mumbai as the most congested city in the world. The vehicle numbers have tripled in 15 years while 90% of Mumbaikars are stuck in peak‑hour traffic where 55% of traffic crawls below 20 km/h. The city loses ₹3,600 crores ($485 million) annually in productivity and fuel loss, 40 to 50% people lose personal time and 750 deaths per year are attributed to pollution[1]. A striking finding from the study is that just 19% of people (travelling by private cars, motorised two-wheelers, taxis and auto rickshaw) consume 80% of road space meaning a small minority of private vehicles create the majority of congestion. The usual “fixes” (widening roads or shifting schedules) ignore induced demand.

Induced demand or induced traffic states that expanding highway capacity make vehicle use higher which in the long run do not provide lasting traffic congestion relief [2]. An example is the Katy Freeway in Texas which connects Houston’s suburbs to its downtown. Over the past two decades, this freeway has undergone several expansions totaling nearly $3 billion, culminating in a massive 23 lanes. While the road’s vehicle capacity has certainly grown the travel times have only worsened. As a result, many consider the project a complete waste of public funds since its original aim was to reduce congestion. On the Katy Freeway, hundreds of homes and businesses were torn down to accommodate the widening project, forcing people, jobs, and commerce to relocate farther from the city center. That displacement ultimately increased the number of freeway users. These changes fueled additional demand: people now live farther from their workplaces and must commute in. Walking is impossible due to the distance, and public transit is either unreliable or nonexistent. Consequently, residents have no choice but to drive using a freeway they never needed before [3].

Instead of endlessly widening roads for cars which only induces more demand, one of the best ways cities can solve congestion is by investing in a mix of transport modes i.e. trains, buses, walking, and cycling. Mumbai, for example, is building 101.43 km (63.03 mi) of metro network on top of its existing suburban train netwrok which spreads over 450 kilometres. But metros and trains alone cannot solve the last‑mile problem where people still need a reliable way to reach stations from their homes or offices, often short distances that are too far to walk but too close to justify driving. Cycling fills that gap perfectly as it is space‑efficient. A bike lane of the same width can move 7,000–8,000 people per hour while a single car lane can move roughly 1,000–2,000 people. Bicycles also take up far less parking and road space, reduce pollution, and improve public health. This project investigates whether reinforcement learning (RL) can overcome these limitations. Traditional optimization methods, such as linear programming, can answer these questions but are computationally expensive and cannot transfer solutions from one city to another. By training RL agents on urban street networks, we aim to produce bike lane allocations that are not only efficient but also transferable across districts. In the following section, we describe the training process, the environment setup, the state and action spaces, and the reward function designed to balance bike and car travel times.

Data Used

The data was acquired from1

  1. The raw data consists of Street network graph which includes Nodes (intersections), edges (streets) with attributes (length, gradient, speed limit, number of lanes). This data is obtained from OpenStreetMap (OSM).

  2. Raw data also includes travel demand data which is represented as Origin-Destination (OD) matrix. This data was captured from bike sharing data, census and travel surveys.

  3. Lastly the raw data is enriched with factors such as elevation raster, parking data and transit routes which helps in improving travel time calculations or constraints.

Using snman library the raw OSM data is converted into an undirected graph. Each edge stores: length(d(e)), gradient(δ(e)), speed limit (θ(e)), capacity in lane count or width (Λ(e)). The undirected graph is then converted into an directed auxiliary graph where each undirected edge is replaced with two reciprocal directed edges and the gradient is reversed for the opposite direction. Next travel times are computed for each directed edge. From real life travel demand data the nodes which have higher demand are selected and optional auxiliary OD pairs (chain of all nodes) are added to enforce strong connectivity of the car network, these have zero weight in the objective. There are two district networks which have been choosen , one of which is Affoltern(Zurich) and another one is Brichplatz (Zurich).

library(sf)        # for reading spatial data (GeoPackage)
library(tidyverse) # for data manipulation and plotting
library(igraph)    # for network analysis (optional)
library(sf)
library(dplyr)
library(purrr)
library(patchwork)
library(ggplot2)
library(gt)
library(scales)
library(plotly)
library(tidyr)
library(hrbrthemes)


instances <- c("affoltern", "birchplatz")

summary_table <- map_dfr(instances, function(inst) {
  edges <- st_read(paste0("data/", inst, "/edges_all_attributes.gpkg"), quiet = TRUE)
  nodes <- st_read(paste0("data/", inst, "/nodes_all_attributes.gpkg"), quiet = TRUE)
  od <- read.csv(paste0("data/", inst, "/od_matrix.csv"))
  
  tibble(
    Instance = inst,
    Nodes = nrow(nodes),
    Edges = nrow(edges),
    Total_Length_km = sum(edges$length, na.rm = TRUE) / 1000,
    Edges_with_BikeLane = sum(grepl("P", edges$ln_desc, fixed = TRUE)),
    Pct_BikeLane = round(mean(grepl("P", edges$ln_desc, fixed = TRUE)) * 100, 1),
    OD_Pairs = nrow(od),
    Total_Trips = sum(od$trips, na.rm = TRUE),
    Avg_Trips_per_OD = round(mean(od$trips), 1)
  )
})

print(summary_table)

snman[4] processing outputs three files, two gpkg files, one of which has nodes and other edges. Last file is the od matrix. Affoltern network has 231 node while birchplatz has 314. Similarly birchplatz (431) has more edges than affoltern network (290). We can anticipate a longer training time for the birchplatz network. Both of the initial networks has 1 protected bike lane.

instance_affoltern <- "affoltern"
edges_affoltern <- st_read(paste0("data/", instance_affoltern, "/edges_all_attributes.gpkg"))
Reading layer `street_graph_edges' from data source `C:\Users\user\Documents\bike_lane_research\outputs_bikr_rl\data\affoltern\edges_all_attributes.gpkg' using driver `GPKG'
Simple feature collection with 290 features and 62 fields
Geometry type: LINESTRING
Dimension:     XY
Bounding box:  xmin: 8.491903 ymin: 47.41154 xmax: 8.519319 ymax: 47.42838
Geodetic CRS:  WGS 84
nodes_affoltern <- st_read(paste0("data/", instance_affoltern, "/nodes_all_attributes.gpkg"))
Reading layer `street_graph_nodes' from data source `C:\Users\user\Documents\bike_lane_research\outputs_bikr_rl\data\affoltern\nodes_all_attributes.gpkg' using driver `GPKG'
Simple feature collection with 231 features and 8 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: 8.491903 ymin: 47.41154 xmax: 8.519112 ymax: 47.42838
Geodetic CRS:  WGS 84
od_affoltern <- read_csv(paste0("data/", instance_affoltern, "/od_matrix.csv"))

instance_birchplatz <- "birchplatz"
edges_birchplatz <- st_read(paste0("data/", instance_birchplatz , "/edges_all_attributes.gpkg"))
Reading layer `street_graph_edges' from data source `C:\Users\user\Documents\bike_lane_research\outputs_bikr_rl\data\birchplatz\edges_all_attributes.gpkg' using driver `GPKG'
Simple feature collection with 431 features and 64 fields
Geometry type: LINESTRING
Dimension:     XY
Bounding box:  xmin: 8.518686 ymin: 47.39858 xmax: 8.550276 ymax: 47.4148
Geodetic CRS:  WGS 84
nodes_birchplatz <- st_read(paste0("data/", instance_birchplatz , "/nodes_all_attributes.gpkg"))
Reading layer `street_graph_nodes' from data source `C:\Users\user\Documents\bike_lane_research\outputs_bikr_rl\data\birchplatz\nodes_all_attributes.gpkg' using driver `GPKG'
Simple feature collection with 314 features and 8 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: 8.518686 ymin: 47.39879 xmax: 8.549898 ymax: 47.4143
Geodetic CRS:  WGS 84
od_birchplatz <- read_csv(paste0("data/", instance_birchplatz , "/od_matrix.csv"))
# Basic info
#glimpse(edges_birchplatz)

# Edge length distribution Affoltern
p1 <- ggplot(edges_affoltern, aes(x = length)) +
  geom_histogram(bins = 50, fill = "darkblue") +
  labs(title = "Edge length distribution Affoltern", x = "Length (meters)")

# Edge length distribution Birchplatz
p2 <- ggplot(edges_birchplatz, aes(x = length)) +
  geom_histogram(bins = 50, fill = "steelblue") +
  labs(title = "Edge length distribution Birchplatz", x = "Length (meters)")

patchwork_1 <- (p1 + p2)
patchwork_1 + plot_annotation(
  title = 'Figure 1: Edge distribution across two quaters in zurich-Switzerland',
)




# Gradient (if column exists)
if("grade" %in% colnames(edges_affoltern)) {
  p3 <- ggplot(edges_affoltern, aes(x = grade)) +
    geom_histogram(bins = 50, fill = "lightgreen") +
    labs(title = "Grade distribution Affoltern", x = "Grade (%)")
}

# Gradient (if column exists)
if("grade" %in% colnames(edges_birchplatz)) {
  p4 <- ggplot(edges_birchplatz, aes(x = grade)) +
    geom_histogram(bins = 50, fill = "darkgreen") +
    labs(title = "Grade distribution BirchPlatz", x = "Grade (%)")
}

patchwork_2 <- (p3 + p4)
patchwork_2 + plot_annotation(
  title = 'Figure 2: Gradient distribution of streets across two quaters in zurich-Switzerland',
)

NA
NA
p1 <- ggplot() +
  geom_sf(data = edges_affoltern, color = "grey", linewidth = 0.3) +
  geom_sf(data = nodes_affoltern, size = 0.5, color = "red") +
  labs(title = paste("Figure 3: Street Network of", instance_affoltern)) +
  theme_void()

p2 <- ggplot() +
  geom_sf(data = edges_birchplatz, color = "grey", linewidth = 0.3) +
  geom_sf(data = nodes_birchplatz, size = 0.5, color = "purple") +
  labs(title = paste("Figure 4: Street Network of", instance_birchplatz)) +
  theme_void()
p1+p2

Baseline Method

The baseline method used in this project is a linear programming solution developed by [5] which is a mathematical recipe that balances car and bike travel times. For every street segment, the optimization decides how much space to give to cars and how much to bikes, with bike lanes needing only half the width of car lanes. It considers where people actually want to travel (the origin‑destination pairs) and ensures that both car and bike networks stay connected. The solver first finds a “fractional” solution (e.g 0.7 of a lane for bikes), a rounding algorithm then picks edges with the highest fractional bike capacity and fixes them as whole bike lanes, re‑solving the LP every k steps. This makes sure that cars still have a way to get everywhere. By repeating the whole process with different weights on car vs. bike importance, the planner obtains a full set of Pareto‑optimal networks, each showing how much bike improvement costs in terms of car travel time.

PPO method

The Reinforcement learning agent environment in this project is named as BikeNetworkEnv and it defines state space, action space, reward function, and transition dynamics.

State Space

The state is a flattened vector of features for every lane edge in the graph. For each directed lane edge the agent sees three features. The features name with their description and value type are given in the below table-:

Table 3: Features of the State Vector

Feature Discription Type
distance Length of the edge (km) float
gradient Slope in percent (positive uphill, negative downhill) float
is_bike 1 if the lane is already a bike lane (lanetype == “P”), else 0 bool

The full observation is a 1D array of length (3 × number_of_directed_lane_edges). So if there are 50 lane edges the vector has 150 features. This state representation is used because the agent need to verify the physical properties of each edge to determine whether it will be beneficial to convert it into bike lane. The is_bike feature is there so that the agent avoids redundant actions.

Action Space

Each action corresponds to selecting one specific directed lane edge and attempting to convert it from a car lane (M>) to a bike lane (P).

Reward

The reward is designed to encourage the agent to reduce bike travel time while keeping car travel time low, and to avoid disconnecting the car network.

When the RL agent takes an action, below points are considered for the reward.

If 1. The edge is already a bike lane then -5 penalty is received. Else 1. The edge is first temporarily converted 2. Check if the remaining car network is strongly connected. If 1. not connected then the conversion is rejected and -20 penalty is given. Else 1. If connected then the conversion is accepted, the new bike and car travel times are computed and the reward equation (i) is calculated.

Reward = 100 / (bike_time + ε) - 0.7 × car_time (i)

(100 / bike_time ) gives high reward when bike time is low. (-0.7 * car_time) penalizes increases in car travel. The values 100 and 0.7 were chosen empirically and can be further tuned using the hyper parameter tuning.

Initial State and Episode Termination

At the start of each episode, the graph reset to the initial car‑only configuration (all lanes M>). The agent then sequentially converts edges. An episode ends after a fixed number of steps (max_steps).

Model Training

The agent is trained as follows-:

  1. The lane graph and the OD matrix are loaded for specific instances(cities/districts).
  2. The BikeNetworkEnv is loaded with lane graph and OD matrix.
  3. PPO algorithm is used for training. PPO is used because it works well with continuous as well as discrete action spaces. Furthermore, It is less sensitive to hyperparameters than vanilla policy gradient methods.
  4. The model is trained for 10000 timesteps.
  5. the trained model is saved to a zip file.

How does PPO work in this case?

The agent runs many episodes and in each episode it takes multiple steps defined by the hyperparameter (max_steps). At each step, it:

  1. Observes the current state (edge distances, gradients, bike flags).

  2. Samples an action according to the policy’s probabilities (or chooses the action with highest probability during inference).

  3. Executes the action, receives a reward, and moves to the next state.

  4. Stores (state, action, reward, next_state, done) in a buffer.

After the agent collects a batch of experiences, PPO updates the policy by:

  1. Computing the advantage which determines how much better an action was than the average expect turn.

  2. Clipping the policy update to avoid taking too large a step.

The objective is to increase the probability of actnthat led to higher rewards but only within a trust region.

Inference After Training

Once trained, the agent is used to rank edges inead of solving an LP. The current graph (with some edges already converted to bike lanes) is used to build the observation vector in exactly the same way as in the environment. Since, the RL agent is trained on a specific graph ( e.g affoltern) which has a fixed number of edges. The graphs determines the observation space. When later the trained agent is loaded to a different city or district, the new graph may have a different number of lane edges. Thus, the observation vector length would differ from what the neural network expects. To overcome, this mismatch a padding/truncation function is added.

If the new graph has fewer edges than the training graph we pad the observation vector with zeros to reach the expected length. The network sees zeros for those “missing” edges, which essentially means they have minimal influence.

If the new graph has more edges than the training graph we truncate or ignore the extra edges. This means the agent cannot act on those extra edges at all, this is one of the severe limitation of the model which can be taken into account for future work.

Despite the limitations the RL agent can be trained once on a representative graph which can then be applied in inference on any other district or part of the city. This is a huge practical advantage if many places need to be optimised for bike lanes.

Two PPO agents were trained, one on Affoltern map and another on Birchplatz network. The training insights are as follows-:

These insights were created using wandb, the original wandb report2 includes interactive graph for further data.

S2V_DQN

The S2V‑DQN agent was inspired by the work of Dai et al. (NIPS 2017), who combined graph embedding (Structure2Vec) with deep Q‑learning to learn greedy heuristics for combinatorial optimization problems over graphs.

State Representation

In s2v_dqn agent the state is a line graph of the original lane graph. State consists of

  1. Nodes – Each node (which corresponds to a directed lane edge in original lane graph). each node has 3 features including distance, gradient, is_bike. 2 . An adjacency matrix that tells the neural network how edges are connected.

  2. Valid mask – binary mask indicating which edges can be legally converted without disconnecting the car network.

Training

The S2V‑DQN agent represents the current state of the lane network as a line graph where each node corresponds to a directed lane edge and edges in this line graph connect two lane edges if they share a common intersection. Each node is associated with a feature vector of length three including the edge’s length, its gradient, and a binary indicator of whether it is already a bike lane. The agent then applies a message‑passing neural network (Structure2Vec) that iteratively updates node embeddings by aggregating information from neighbouring nodes. After n iterations, each node’s embedding encodes not only its own properties but also the structure and features of the local neighbourhood. A global pooling operation (mean) over all node embeddings produces a single vector that summarises the entire network state.

For each lane edge, the agent concatenates its individual embedding with the global graph summary and passes the result through a small multi‑layer perceptron (two hidden layers) to compute a scalar Q‑value. This Q‑value estimates the expected long‑term reward if that edge is converted from a car lane to a bike lane given the current network state. Edges that are critical for car connectivity like bridges or arterial roads that if removed would disconnect the graph, receive low Q‑values while edges whose conversion would significantly reduce bike travel time without harming car travel time receive high Q‑values. The message‑passing mechanism allows the agent to reason about network‑wide consequences. During inference Q‑value is used to rank edges during inference a higher Q means that the lane is a better candidate for conversion.

Unlike on policy method PPO, s2v_dqn is a Off-policy method. It stores past experiences in a replay buffer and samples randomly to update the Q‑network.Although, it is generally more sample‑efficient than on‑policy methods, it can be unstable if hyperparameters are not tuned. Handling variable graph representation.

We used padding/truncation in PPO agent to make it accomodable for a variety of city networks. However, this is not required by s2v_dqn as the graph representation is inherently variable‑size which means that the model processes a set of nodes and edges via message passing, which works for any number of edges. PyTorch Geometric’s Batch class collates multiple graphs of different sizes into a single batch. Despite its advantages, one main limitation of s2v_dqn is that it requires careful reward scaling and target network updates.

An S2V_DQN agent was trained on Birchplatz network. The training insights are as follows-:

Above insights were created using wandb, the original wandb report3 includes interactive graph for further data.

Results

library(dplyr)
library(purrr)
library(tidyr)

# Root directory (where all experiment folders live)
root_dir <- "C:/Users/user/Documents/bike_lane_research/outputs_bikr_rl/s2v_dqn"

# Find all CSV files matching the pattern
file_list <- list.files(
  path = root_dir,
  pattern = "real_pareto_optimize_od_1.0_50\\.csv$",
  recursive = TRUE,
  full.names = TRUE
)

# Function to extract both experiment and location
read_and_label <- function(path) {
  df <- read.csv(path)
  
  # Get relative path from root_dir
  rel_path <- gsub(normalizePath(root_dir, winslash = "/"), "", normalizePath(path, winslash = "/"))
  # Example: "/bike_lane_agent_affoltern_20260405_1523/affoltern/real_pareto_optimize_od_1.0_50.csv"
  
  # Split by "/"
  parts <- strsplit(rel_path, "/")[[1]]
  # parts[1] is empty (starts with /), parts[2] = experiment folder, parts[3] = location (affoltern/birchplatz)
  experiment <- parts[2]
  location <- parts[3]
  
  df$experiment <- experiment
  df$location <- location
  return(df)
}

# Combine all data
combined_df <- map_df(file_list, read_and_label)

# Reshape to long format (bike/car categories)
df_long <- combined_df %>%
  pivot_longer(
    cols = c(bike_edges, car_edges, bike_time, car_time),
    names_to = c("mode", ".value"),
    names_pattern = "(bike|car)_(.*)"
  )
# Now columns: ..., mode, edges, time, experiment, location

df <- df_long

df <- df %>%
  group_by(bike_edges_added, experiment, location, mode) %>%
  mutate(duplicate_id = row_number()) %>%
  ungroup()

df_wide <- df %>%
  pivot_wider(
    id_cols = c(bike_edges_added, experiment, location, duplicate_id),
    names_from = mode,
    values_from = time
  ) %>%
  rename(bike_time = bike, car_time = car) %>%
  select(-duplicate_id)   # remove the helper column

Comparison of Final Perceived Bike and Car Travel Times

library(gt)

summary_table <- df_wide %>%
  arrange(location, experiment, bike_edges_added) %>%   # ensure correct order
  group_by(location, experiment) %>%
  summarise(
    init_bike_time = first(bike_time),
    init_car_time = first(car_time),
    final_bike_time = last(bike_time),
    final_car_time = last(car_time),
    num_points = n()
  ) %>%
  ungroup() %>%
  # Round numeric columns for readability
  mutate(across(where(is.numeric), ~ round(.x, 3)))

# Create gt table
gt(summary_table) %>%
  tab_header(
    title = "Table 4: Initial and final travel times and number of Pareto points",
  ) %>%
  cols_label(
    location = "Location",
    experiment = "Algorithm",
    init_bike_time = "Initial bike time",
    init_car_time = "Initial car time",
    final_bike_time = "Final bike time (seconds)",
    final_car_time = "Final car time",
    num_points = "Bike Edges"
  ) %>%
  fmt_number(columns = c(init_bike_time, init_car_time, final_bike_time, final_car_time),
             decimals = 2) %>%
  tab_options(
    table.font.size = 12,
    heading.title.font.size = 14,
    heading.subtitle.font.size = 12
  )
Table 4: Initial and final travel times and number of Pareto points
Location Algorithm Initial bike time Initial car time Final bike time (seconds) Final car time Bike Edges
affoltern Linear_Programming 7.19 1.83 3.87 2.60 130
affoltern PPO_Trained_On_Affoltern 7.19 1.83 4.49 3.84 83
affoltern PPO_Trained_On_Birchplatz 7.19 1.83 4.50 3.62 87
affoltern S2V_DQN_Trained_On_Birchplatz 7.19 1.83 4.44 3.83 86
birchplatz Linear_Programming 6.27 1.58 3.22 2.51 267
birchplatz PPO_Trained_On_Affoltern 6.27 1.58 3.92 3.13 154
birchplatz PPO_Trained_On_Birchplatz 6.27 1.58 3.79 3.41 176
birchplatz S2V_DQN_Trained_On_Birchplatz 6.27 1.58 3.72 3.27 174

Table 4 reveals a consistent trade‑off between optimality and resource efficiency. Linear Programming (LP) achieves the lowest final bike travel time in both districts (3.87 s in Affoltern, 3.22 s in Birchplatz) but it does so by reallocating substantially more bike lanes (130 and 267, respectively). In contrast, all RL agents (PPO and S2V‑DQN) produce slightly higher final bike times (3.72–4.50 s) while using roughly 40% fewer bike lanes in Affoltern (83–87 vs. 130) and 35–40% fewer in Birchplatz (154–176 vs. 267).



# Filter for bike mode and affoltern location
df_bike_affoltern <- df %>%
  filter(mode == "bike", location == "affoltern")

p <- df_bike_affoltern %>%
  ggplot(aes(x = edges, y = time, fill = experiment)) +
  geom_area(alpha = 0.5) +
  facet_wrap(~ experiment) +          # one subplot per experiment
  labs(x = "Edges", y = "Time", title = "Figure 5: Change in Perceived Bike Time on Affoltern as The Algorithms Progress Add Bike Lanes Over Time") +
  theme_ipsum()

# Make interactive
p1 <- ggplotly(p)
p1
NA
NA

Plot shows that

# Filter for bike mode and birchplatz location
df_bike_affoltern <- df %>%
  filter(mode == "bike", location == "birchplatz")

p <- df_bike_affoltern %>%
  ggplot(aes(x = edges, y = time, fill = experiment)) +
  geom_area(alpha = 0.5) +
  facet_wrap(~ experiment) +          # one subplot per experiment
  labs(x = "Edges", y = "Time", title = "Figure 6: Change in Perceived Bike Time on Birchplatz as The Algorithms Progress Add Bike Lanes Over Time") +
  theme_ipsum()

# Make interactive
p2 <- ggplotly(p)
p2
NA

The table shows that Linear Programming (LP) not only achieves the lowest final bike travel times but also limits the increase in car travel times more effectively than the RL agents. Starting from initial car times of 1.83 s (Affoltern) and 1.58 s (Birchplatz) LP increases car times to 2.60 s (+42%) and 2.51 s (+59%) respectively. In contrast, all RL agents result in substantially higher car times. For example for Affoltern, the best RL agent (PPO trained on Affoltern) final car time reaches 3.84 s (+110%) and for Birchplatz, S2V‑DQN reaches 3.27 s (+107%) while PPO trained on Birchplatz reaches 3.41 s (+116%). This is despite RL agents using far fewer bike lanes (e.g., 83 vs. 130 in Affoltern). The finding suggests that LP is more car‑aware where it carefully selects lanes that improve bikeability while minimizing disruption to car traffic. RL agents, even when trained with a reward that includes car travel time appear to allocate bike lanes in a way that inadvertently increases car travel time more than LP does.

One way to improve the RL agent can be increasing the training budget and extensive hyperparameter tuning as they had relatively short training budgets (e.g., 635–2489 seconds). Another, way is more thorough reward engineering.


# Filter for bike mode and affoltern location
df_bike_affoltern <- df %>%
  filter(mode == "car", location == "affoltern")

p <- df_bike_affoltern %>%
  ggplot(aes(x = edges, y = time, fill = experiment)) +
  geom_area(alpha = 0.5) +
  facet_wrap(~ experiment) +          # one subplot per experiment
   labs(x = "Edges", y = "Time", title = "Figure 7: Change in Perceived Car Time on Affoltern as The Algorithms Progress Add Bike Lanes Over Time") +
  theme_ipsum()

# Make interactive
p1 <- ggplotly(p)
p1

# Filter for bike mode and birchplatz location
df_bike_affoltern <- df %>%
  filter(mode == "car", location == "birchplatz")

p <- df_bike_affoltern %>%
  ggplot(aes(x = edges, y = time, fill = experiment)) +
  geom_area(alpha = 0.5) +
  facet_wrap(~ experiment) +          # one subplot per experiment
  labs(x = "Edges", y = "Time", title = "Figure 8: Change in Perceived Car Time on Birchplatz as The Algorithms Add Bike Lanes Over Time") +
  theme_ipsum()

# Make interactive
p2 <- ggplotly(p)
p2
NA

Comparison of Training and Pareto Computation Time

Table 5 : Summary of training time (seconds) and Pareto computation time (seconds)

Method Training time (s) Affoltern (Pareto computation time, s) Birchplatz (Pareto computation time, s)
Affoltern_PPO 635 10.85 35.50
Birchplatz_PPO 2489 13.37 37.12
LP_Affoltern 117.63
LP_Birchplatz 3911.71
Birchplatz_s2vdqn 11421 12.85 73.85

The results in the table above highlight a clear trade‑off between optimality and transferability. The Linear Programming (LP) approach achieves the lowest perceived bike travel time in both districts (3.87 s for Affoltern, 3.22 s for Birchplatz), improving upon the initial times by about 46% and 49% respectively. However, LP is not transferable which is why each new district requires a full, time‑intensive re‑optimization from scratch (as reflected in the table). In contrast, reinforcement learning agents (PPO, S2V‑DQN) can be trained once on one district and then applied to another with negligible inference cost. For example, PPO trained on Birchplatz achieves a final bike time of 4.50 s on Affoltern, only 0.63 s higher than LP while using significantly fewer bike edges (87 vs. 130). Similarly, on Birchplatz itself, the best RL agent (S2V‑DQN) reaches 3.72 s, 0.50 s above LP but with about 100 fewer bike lanes. The disadvantage of RL is its worse final bike time (less aggressive network reallocation) and the upfront training cost (up to 11421 s for S2V‑DQN). The advantage is clear where after training an RL agent can instantly produce a viable Pareto‑optimal network for any new city, whereas LP must be re‑solved each time a prohibitive overhead for large‑scale or iterative urban planning. Thus, LP remains the gold standard for static, per‑city optimization, while RL offers a transferable alternative when computational budgets or repeated applications are a concern.

library(dplyr)
library(ggplot2) 
library(magick)
library(patchwork)

base_dir <- "C:/Users/user/Documents/bike_lane_research/outputs_bikr_rl/s2v_dqn"
sub_dir_1 <- "PPO_Trained_On_Affoltern/graphs"
sub_dir_2 <- "PPO_Trained_On_Birchplatz/graphs"
sub_dir_3 <- "Linear_Programming/graphs"
sub_dir_4 <- "S2V_DQN_Trained_On_Birchplatz/graphs"

# Read images and add titles
plt1 <- image_read(file.path(base_dir, sub_dir_1, "affoltern_bike_network.png")) %>%
  image_ggplot() +
  ggtitle("PPO (Trained on Affoltern)") +
  theme(plot.title = element_text(size = 10, hjust = 0.5))

plt2 <- image_read(file.path(base_dir, sub_dir_2, "affoltern_bike_network.png")) %>%
  image_ggplot() +
  ggtitle("PPO (Trained on Birchplatz)") +
  theme(plot.title = element_text(size = 10, hjust = 0.5))

plt3 <- image_read(file.path(base_dir, sub_dir_3, "affoltern_bike_network.png")) %>%
  image_ggplot() +
  ggtitle("Linear Programming") +
  theme(plot.title = element_text(size = 10, hjust = 0.5))

plt4 <- image_read(file.path(base_dir, sub_dir_4, "affoltern_bike_network.png")) %>%
  image_ggplot() +
  ggtitle("S2V-DQN (Trained on Birchplatz)") +
  theme(plot.title = element_text(size = 10, hjust = 0.5))

# Arrange with patchwork and add overall caption
(plt1 | plt2) / (plt3 | plt4) +
  plot_annotation(
    title = "Figure 9: Affoltern Bike Networks by Algorithm",
    theme = theme(plot.title = element_text(size = 14, hjust = 0.5))
  )

The final bike network for Affoltern reveals that the linear programming method produces the most elaborate and well‑connected network. The networks generated by PPO(Affoltern), PPO(Birchplatz) and S2V‑DQN(Birchplatz) exhibit similar connectivity and complexity, all ranking lower than the LP results.


plt1 <- image_read(file.path(base_dir, sub_dir_1, "birchplatz_bike_network.png")) %>%
  image_ggplot() +
  ggtitle("PPO (Trained on Affoltern)") +
  theme(plot.title = element_text(size = 10, hjust = 0.5))

plt2 <- image_read(file.path(base_dir, sub_dir_2, "birchplatz_bike_network.png")) %>%
  image_ggplot() +
  ggtitle("PPO (Trained on Birchplatz)") +
  theme(plot.title = element_text(size = 10, hjust = 0.5))

plt3 <- image_read(file.path(base_dir, sub_dir_3, "birchplatz_bike_network.png")) %>%
  image_ggplot() +
  ggtitle("Linear Programming") +
  theme(plot.title = element_text(size = 10, hjust = 0.5))

plt4 <- image_read(file.path(base_dir, sub_dir_4, "birchplatz_bike_network.png")) %>%
  image_ggplot() +
  ggtitle("S2V-DQN (Trained on Birchplatz)") +
  theme(plot.title = element_text(size = 10, hjust = 0.5))


(plt1 | plt2) / (plt3 | plt4) +
  plot_annotation(
    title = "Figure 10:Birchplatz Bike Networks by Algorithm",
    theme = theme(plot.title = element_text(size = 14, hjust = 0.5))
  )

Similar to Affoltern network, the final bike network for Birchplatz reveals that the linear programming method produces the most elaborate and well‑connected network, followed by S2V‑DQN .The networks generated by PPO trained on Affoltern and PPO trained on Birchplatz exhibit similar connectivity and complexity, both ranking lower than the LP and S2V‑DQN results.


plt1 <- image_read(file.path(base_dir, sub_dir_1, "affoltern_car_network.png")) %>%
  image_ggplot() +
  ggtitle("PPO (Trained on Affoltern)") +
  theme(plot.title = element_text(size = 10, hjust = 0.5))

plt2 <- image_read(file.path(base_dir, sub_dir_2, "affoltern_car_network.png")) %>%
  image_ggplot() +
  ggtitle("PPO (Trained on Birchplatz)") +
  theme(plot.title = element_text(size = 10, hjust = 0.5))

plt3 <- image_read(file.path(base_dir, sub_dir_3, "affoltern_car_network.png")) %>%
  image_ggplot() +
  ggtitle("Linear Programming") +
  theme(plot.title = element_text(size = 10, hjust = 0.5))

plt4 <- image_read(file.path(base_dir, sub_dir_4, "affoltern_car_network.png")) %>%
  image_ggplot() +
  ggtitle("S2V-DQN (Trained on Birchplatz)") +
  theme(plot.title = element_text(size = 10, hjust = 0.5))


(plt1 | plt2) / (plt3 | plt4) +
  plot_annotation(
    title = "Figure 11: Affoltern Car Networks by Algorithm",
    theme = theme(plot.title = element_text(size = 14, hjust = 0.5))
  )


plt1 <- image_read(file.path(base_dir, sub_dir_1, "birchplatz_car_network.png")) %>%
  image_ggplot() +
  ggtitle("PPO (Trained on Affoltern)") +
  theme(plot.title = element_text(size = 10, hjust = 0.5))

plt2 <- image_read(file.path(base_dir, sub_dir_2, "birchplatz_car_network.png")) %>%
  image_ggplot() +
  ggtitle("PPO (Trained on Birchplatz)") +
  theme(plot.title = element_text(size = 10, hjust = 0.5))

plt3 <- image_read(file.path(base_dir, sub_dir_3, "birchplatz_car_network.png")) %>%
  image_ggplot() +
  ggtitle("Linear Programming") +
  theme(plot.title = element_text(size = 10, hjust = 0.5))

plt4 <- image_read(file.path(base_dir, sub_dir_4, "birchplatz_car_network.png")) %>%
  image_ggplot() +
  ggtitle("S2V-DQN (Trained on Birchplatz)") +
  theme(plot.title = element_text(size = 10, hjust = 0.5))


(plt1 | plt2) / (plt3 | plt4) +
  plot_annotation(
    title = "Figure 12: Birchplatz Car Networks by Algorithm",
    theme = theme(plot.title = element_text(size = 14, hjust = 0.5))
  )

REFERENCES

[1] “Taming Traffic,” Institute for Transportation and Development Policy - Promoting sustainable and equitable transportation worldwide, Mar. 22, 2021. https://itdp.org/publication/taming-traffic

[2] K. Hymel, “If you build it, they will drive: Measuring induced demand for vehicle travel in urban areas,” Transport Policy, vol. 76, pp. 57–66, Apr. 2019, doi: https://doi.org/10.1016/j.tranpol.2018.12.006.

[3] “Road widening and induced demand explained | TomTom Newsroom,” TomTom, 2024. https://www.tomtom.com/newsroom/explainers-and-insights/induced-demand-explained/

[4] Ballo, L., M. Raubal and K.W. Axhausen (2024) Designing an E-Bike City: An automated process for network-wide multimodal road space reallocation, Journal of Cycling and Micromobility Research, 2, 100048.

[5] Wiedemann, N., Nöbel, C., Martin, H., Ballo, L., & Raubal, M. (2024). Bike network planning in limited urban space. arXiv preprint arXiv:2405.01770.


  1. https://wandb.ai/hsra/Bike_Lane_Project/reports/PPO-Agents-for-Bike-Lane-Optimization--VmlldzoxNjYyODE1Ng↩︎

  2. https://wandb.ai/hsra/Bike_Lane_Project/reports/PPO-Agents-for-Bike-Lane-Optimization--VmlldzoxNjYyODE1Ng↩︎

  3. https://wandb.ai/hsra/S2V_DQN/reports/S2V_DQN--VmlldzoxNjYyOTkwNw↩︎

LS0tDQp0aXRsZTogIiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KZWRpdG9yX29wdGlvbnM6IA0KICBtYXJrZG93bjogDQogICAgd3JhcDogc2VudGVuY2UNCi0tLQ0KDQojIyBUcmFuc2ZlcmFibGUgQmlrZSBOZXR3b3JrIFBsYW5uaW5nOiBSZWluZm9yY2VtZW50IExlYXJuaW5nIGZvciBQYXJldG8tT3B0aW1hbCBMYW5lIFJlYWxsb2NhdGlvbg0KDQpJJ3ZlIGFsd2F5cyBoYXRlZCB0cmF2ZWxsaW5nIHRvIGFuZCBmcm9tIGZyb20gYWlycG9ydCB0byBteSBwbGFjZSBpbiB0aGlzIG11bWJhaSB0cmFmZmljIGFuZCBpdCB3b3VsZCBmb3Igc3VyZSBiZWF0IG15IGZhdm91cml0ZSBwbGF5bGlzdC4NClNpbmNlIEkgZG9uJ3QgZW5nYWdlIGluIGFueSBkZWxldGVyaW91cyBoYWJpdHMsIG15IHRyYWZmaWMgc3Vydml2YWwga2l0IHdpdGggYWxsIGFtYXppbmcgc25hY2tzIGZvciB0aGUgcmlkZSBhbmQgcGV0cm9sIGJpbGwgY29sbGVjdGl2ZWx5IGEgZGVjZW50IGRvd24gcGF5bWVudCBvbiBhIGhvdXNlIGluIEFuZGhlcmkuDQpBIHBvb3Igam9rZSwgSSBrbm93Lg0KQW5kIG9mIGNvdXJzZSB0aGVyZSdzIGFsd2F5cyBzb21lIG5ldyBmbHlvdmVyIG9yIG1ldHJvIHBpbGxhciBjb21pbmcgdXAgZXZlcnl3aGVyZSB5ZXQgbm90IGEgc2luZ2xlIHNpZ24gb2YgdHJhZmZpYyBlYXNpbmcuDQpUaGF0IHNhaWQsIEknbSBmYWlybHkgY2VydGFpbiBtb3N0IHBlb3BsZSB3b3VsZCBiZSBmdXJyb3dpbmcgdGhlaXIgYnJvd3MgaW4gdG90YWwsIGdydWRnaW5nIHJlY29nbml0aW9uLg0KVGhvdXNhbmRzIG9mIGRvbGxhcnMgYXJlIHNwZW50IHllYXJseSBvbiBleHBhbmRpbmcgY2FyIGxhbmVzIHRocm91Z2hvdXQgdGhlIGNpdGllcyB3b3JsZHdpZGUuDQpZZXQgY2l0aWVzIGV2ZXJ5d2hlcmUgYXJlIG1vcmUgY29uZ2VzdGVkIHRoYW4gZXZlci4NCg0KUmVjZW50IGV2ZW50cyBzdWNoIGFzIEt1YWxhIEx1bXB1cuKAmXMg4oCcQmFuZ3VuIEtM4oCdIGNhbXBhaWduIHRyaWVkIHRvIHNvbHZlIGdyaWRsb2NrIGJ5IGFza2luZyBwZW9wbGUgdG8gc3RhcnQgdGhlaXIgZGF5IGVhcmxpZXIsIG9mZmVyaW5nIGRpc2NvdW50ZWQgY29mZmVlIGFzIGFuIGluY2VudGl2ZS4NCkhvd2V2ZXIsIG1vc3QgY29tbXV0ZXJz4oCZIHNjaGVkdWxlcyBhcmUgYWxyZWFkeSBmaXhlZCB3aGljaCBpbmNsdWRlcyBwYXJlbnRzIGRyb3BwaW5nIGNoaWxkcmVuIGF0IHNjaG9vbCBieSA2OjMw4oCvYW0sIHRlYWNoZXJzLCBjaXZpbCBzZXJ2YW50cywgYW5kIHNoaWZ0IHdvcmtlcnMgd2hvIGNhbm5vdCBzaGlmdCB0aGVpciBob3Vycy4NCk1lYW53aGlsZSBhIHN0dWR5IGJ5IEluc3RpdHV0ZSBmb3IgVHJhbnNwb3J0YXRpb24gYW5kIERldmVsb3BtZW50IFBvbGljeSBJbmRpYSByZXZlYWxzIE11bWJhaSBhcyB0aGUgbW9zdCBjb25nZXN0ZWQgY2l0eSBpbiB0aGUgd29ybGQuDQpUaGUgdmVoaWNsZSBudW1iZXJzIGhhdmUgdHJpcGxlZCBpbiAxNSB5ZWFycyB3aGlsZSA5MCUgb2YgTXVtYmFpa2FycyBhcmUgc3R1Y2sgaW4gcGVha+KAkWhvdXIgdHJhZmZpYyB3aGVyZSA1NSUgb2YgdHJhZmZpYyBjcmF3bHMgYmVsb3cgMjDigK9rbS9oLg0KVGhlIGNpdHkgbG9zZXMg4oK5Myw2MDAgY3JvcmVzIChcJDQ4NSBtaWxsaW9uKSBhbm51YWxseSBpbiBwcm9kdWN0aXZpdHkgYW5kIGZ1ZWwgbG9zcywgNDAgdG8gNTAlIHBlb3BsZSBsb3NlIHBlcnNvbmFsIHRpbWUgYW5kIDc1MCBkZWF0aHMgcGVyIHllYXIgYXJlIGF0dHJpYnV0ZWQgdG8gcG9sbHV0aW9uWzFdLg0KQSBzdHJpa2luZyBmaW5kaW5nIGZyb20gdGhlIHN0dWR5IGlzIHRoYXQganVzdCAxOSUgb2YgcGVvcGxlICh0cmF2ZWxsaW5nIGJ5IHByaXZhdGUgY2FycywgbW90b3Jpc2VkIHR3by13aGVlbGVycywgdGF4aXMgYW5kIGF1dG8gcmlja3NoYXcpIGNvbnN1bWUgODAlIG9mIHJvYWQgc3BhY2UgbWVhbmluZyBhIHNtYWxsIG1pbm9yaXR5IG9mIHByaXZhdGUgdmVoaWNsZXMgY3JlYXRlIHRoZSBtYWpvcml0eSBvZiBjb25nZXN0aW9uLg0KVGhlIHVzdWFsIOKAnGZpeGVz4oCdICh3aWRlbmluZyByb2FkcyBvciBzaGlmdGluZyBzY2hlZHVsZXMpIGlnbm9yZSBpbmR1Y2VkIGRlbWFuZC4NCg0KIVtdKGltYWdlcy9jbGlwYm9hcmQtNDg5NzY1Mjc4LnBuZykNCg0KSW5kdWNlZCBkZW1hbmQgb3IgaW5kdWNlZCB0cmFmZmljIHN0YXRlcyB0aGF0IGV4cGFuZGluZyBoaWdod2F5IGNhcGFjaXR5IG1ha2UgdmVoaWNsZSB1c2UgaGlnaGVyIHdoaWNoIGluIHRoZSBsb25nIHJ1biBkbyBub3QgcHJvdmlkZSBsYXN0aW5nIHRyYWZmaWMgY29uZ2VzdGlvbiByZWxpZWYgWzJdLg0KQW4gZXhhbXBsZSBpcyB0aGUgS2F0eSBGcmVld2F5IGluIFRleGFzIHdoaWNoIGNvbm5lY3RzIEhvdXN0b27igJlzIHN1YnVyYnMgdG8gaXRzIGRvd250b3duLg0KT3ZlciB0aGUgcGFzdCB0d28gZGVjYWRlcywgdGhpcyBmcmVld2F5IGhhcyB1bmRlcmdvbmUgc2V2ZXJhbCBleHBhbnNpb25zIHRvdGFsaW5nIG5lYXJseSBcJDMgYmlsbGlvbiwgY3VsbWluYXRpbmcgaW4gYSBtYXNzaXZlIDIzIGxhbmVzLg0KV2hpbGUgdGhlIHJvYWTigJlzIHZlaGljbGUgY2FwYWNpdHkgaGFzIGNlcnRhaW5seSBncm93biB0aGUgdHJhdmVsIHRpbWVzIGhhdmUgb25seSB3b3JzZW5lZC4NCkFzIGEgcmVzdWx0LCBtYW55IGNvbnNpZGVyIHRoZSBwcm9qZWN0IGEgY29tcGxldGUgd2FzdGUgb2YgcHVibGljIGZ1bmRzIHNpbmNlIGl0cyBvcmlnaW5hbCBhaW0gd2FzIHRvIHJlZHVjZSBjb25nZXN0aW9uLg0KT24gdGhlIEthdHkgRnJlZXdheSwgaHVuZHJlZHMgb2YgaG9tZXMgYW5kIGJ1c2luZXNzZXMgd2VyZSB0b3JuIGRvd24gdG8gYWNjb21tb2RhdGUgdGhlIHdpZGVuaW5nIHByb2plY3QsIGZvcmNpbmcgcGVvcGxlLCBqb2JzLCBhbmQgY29tbWVyY2UgdG8gcmVsb2NhdGUgZmFydGhlciBmcm9tIHRoZSBjaXR5IGNlbnRlci4NClRoYXQgZGlzcGxhY2VtZW50IHVsdGltYXRlbHkgaW5jcmVhc2VkIHRoZSBudW1iZXIgb2YgZnJlZXdheSB1c2Vycy4NClRoZXNlIGNoYW5nZXMgZnVlbGVkIGFkZGl0aW9uYWwgZGVtYW5kOiBwZW9wbGUgbm93IGxpdmUgZmFydGhlciBmcm9tIHRoZWlyIHdvcmtwbGFjZXMgYW5kIG11c3QgY29tbXV0ZSBpbi4NCldhbGtpbmcgaXMgaW1wb3NzaWJsZSBkdWUgdG8gdGhlIGRpc3RhbmNlLCBhbmQgcHVibGljIHRyYW5zaXQgaXMgZWl0aGVyIHVucmVsaWFibGUgb3Igbm9uZXhpc3RlbnQuDQpDb25zZXF1ZW50bHksIHJlc2lkZW50cyBoYXZlIG5vIGNob2ljZSBidXQgdG8gZHJpdmUgdXNpbmcgYSBmcmVld2F5IHRoZXkgbmV2ZXIgbmVlZGVkIGJlZm9yZSBbM10uDQoNCkluc3RlYWQgb2YgZW5kbGVzc2x5IHdpZGVuaW5nIHJvYWRzIGZvciBjYXJzIHdoaWNoIG9ubHkgaW5kdWNlcyBtb3JlIGRlbWFuZCwgb25lIG9mIHRoZSBiZXN0IHdheXMgY2l0aWVzIGNhbiBzb2x2ZSBjb25nZXN0aW9uIGlzIGJ5IGludmVzdGluZyBpbiBhIG1peCBvZiB0cmFuc3BvcnQgbW9kZXMgaS5lLiB0cmFpbnMsIGJ1c2VzLCB3YWxraW5nLCBhbmQgY3ljbGluZy4NCk11bWJhaSwgZm9yIGV4YW1wbGUsIGlzIGJ1aWxkaW5nIDEwMS40MyBrbSAoNjMuMDMgbWkpIG9mIG1ldHJvIG5ldHdvcmsgb24gdG9wIG9mIGl0cyBleGlzdGluZyBzdWJ1cmJhbiB0cmFpbiBuZXR3cm9rIHdoaWNoIHNwcmVhZHMgb3ZlciA0NTAga2lsb21ldHJlcy4NCkJ1dCBtZXRyb3MgYW5kIHRyYWlucyBhbG9uZSBjYW5ub3Qgc29sdmUgdGhlIGxhc3TigJFtaWxlIHByb2JsZW0gd2hlcmUgcGVvcGxlIHN0aWxsIG5lZWQgYSByZWxpYWJsZSB3YXkgdG8gcmVhY2ggc3RhdGlvbnMgZnJvbSB0aGVpciBob21lcyBvciBvZmZpY2VzLCBvZnRlbiBzaG9ydCBkaXN0YW5jZXMgdGhhdCBhcmUgdG9vIGZhciB0byB3YWxrIGJ1dCB0b28gY2xvc2UgdG8ganVzdGlmeSBkcml2aW5nLg0KQ3ljbGluZyBmaWxscyB0aGF0IGdhcCBwZXJmZWN0bHkgYXMgaXQgaXMgc3BhY2XigJFlZmZpY2llbnQuDQpBIGJpa2UgbGFuZSBvZiB0aGUgc2FtZSB3aWR0aCBjYW4gbW92ZSA3LDAwMOKAkzgsMDAwIHBlb3BsZSBwZXIgaG91ciB3aGlsZSBhIHNpbmdsZSBjYXIgbGFuZSBjYW4gbW92ZSByb3VnaGx5IDEsMDAw4oCTMiwwMDAgcGVvcGxlLg0KQmljeWNsZXMgYWxzbyB0YWtlIHVwIGZhciBsZXNzIHBhcmtpbmcgYW5kIHJvYWQgc3BhY2UsIHJlZHVjZSBwb2xsdXRpb24sIGFuZCBpbXByb3ZlIHB1YmxpYyBoZWFsdGguDQpUaGlzIHByb2plY3QgaW52ZXN0aWdhdGVzIHdoZXRoZXIgcmVpbmZvcmNlbWVudCBsZWFybmluZyAoUkwpIGNhbiBvdmVyY29tZSB0aGVzZSBsaW1pdGF0aW9ucy4NClRyYWRpdGlvbmFsIG9wdGltaXphdGlvbiBtZXRob2RzLCBzdWNoIGFzIGxpbmVhciBwcm9ncmFtbWluZywgY2FuIGFuc3dlciB0aGVzZSBxdWVzdGlvbnMgYnV0IGFyZSBjb21wdXRhdGlvbmFsbHkgZXhwZW5zaXZlIGFuZCBjYW5ub3QgdHJhbnNmZXIgc29sdXRpb25zIGZyb20gb25lIGNpdHkgdG8gYW5vdGhlci4NCkJ5IHRyYWluaW5nIFJMIGFnZW50cyBvbiB1cmJhbiBzdHJlZXQgbmV0d29ya3MsIHdlIGFpbSB0byBwcm9kdWNlIGJpa2UgbGFuZSBhbGxvY2F0aW9ucyB0aGF0IGFyZSBub3Qgb25seSBlZmZpY2llbnQgYnV0IGFsc28gdHJhbnNmZXJhYmxlIGFjcm9zcyBkaXN0cmljdHMuDQpJbiB0aGUgZm9sbG93aW5nIHNlY3Rpb24sIHdlIGRlc2NyaWJlIHRoZSB0cmFpbmluZyBwcm9jZXNzLCB0aGUgZW52aXJvbm1lbnQgc2V0dXAsIHRoZSBzdGF0ZSBhbmQgYWN0aW9uIHNwYWNlcywgYW5kIHRoZSByZXdhcmQgZnVuY3Rpb24gZGVzaWduZWQgdG8gYmFsYW5jZSBiaWtlIGFuZCBjYXIgdHJhdmVsIHRpbWVzLg0KDQojIyMgRGF0YSBVc2VkDQoNClRoZSBkYXRhIHdhcyBhY3F1aXJlZCBmcm9tW14xXQ0KDQpbXjFdOiA8aHR0cHM6Ly93YW5kYi5haS9oc3JhL0Jpa2VfTGFuZV9Qcm9qZWN0L3JlcG9ydHMvUFBPLUFnZW50cy1mb3ItQmlrZS1MYW5lLU9wdGltaXphdGlvbi0tVm1sbGR6b3hOall5T0RFMU5nPg0KDQoxLiAgVGhlIHJhdyBkYXRhIGNvbnNpc3RzIG9mIFN0cmVldCBuZXR3b3JrIGdyYXBoIHdoaWNoIGluY2x1ZGVzIE5vZGVzIChpbnRlcnNlY3Rpb25zKSwgZWRnZXMgKHN0cmVldHMpIHdpdGggYXR0cmlidXRlcyAobGVuZ3RoLCBncmFkaWVudCwgc3BlZWQgbGltaXQsIG51bWJlciBvZiBsYW5lcykuDQogICAgVGhpcyBkYXRhIGlzIG9idGFpbmVkIGZyb20gT3BlblN0cmVldE1hcCAoT1NNKS4NCg0KMi4gIFJhdyBkYXRhIGFsc28gaW5jbHVkZXMgdHJhdmVsIGRlbWFuZCBkYXRhIHdoaWNoIGlzIHJlcHJlc2VudGVkIGFzIE9yaWdpbi1EZXN0aW5hdGlvbiAoT0QpIG1hdHJpeC4NCiAgICBUaGlzIGRhdGEgd2FzIGNhcHR1cmVkIGZyb20gYmlrZSBzaGFyaW5nIGRhdGEsIGNlbnN1cyBhbmQgdHJhdmVsIHN1cnZleXMuDQoNCjMuICBMYXN0bHkgdGhlIHJhdyBkYXRhIGlzIGVucmljaGVkIHdpdGggZmFjdG9ycyBzdWNoIGFzIGVsZXZhdGlvbiByYXN0ZXIsIHBhcmtpbmcgZGF0YSBhbmQgdHJhbnNpdCByb3V0ZXMgd2hpY2ggaGVscHMgaW4gaW1wcm92aW5nIHRyYXZlbCB0aW1lIGNhbGN1bGF0aW9ucyBvciBjb25zdHJhaW50cy4NCg0KVXNpbmcgc25tYW4gbGlicmFyeSB0aGUgcmF3IE9TTSBkYXRhIGlzIGNvbnZlcnRlZCBpbnRvIGFuIHVuZGlyZWN0ZWQgZ3JhcGguDQpFYWNoIGVkZ2Ugc3RvcmVzOiBsZW5ndGgoZChlKSksIGdyYWRpZW50KM60KGUpKSwgc3BlZWQgbGltaXQgKM64KGUpKSwgY2FwYWNpdHkgaW4gbGFuZSBjb3VudCBvciB3aWR0aCAozpsoZSkpLg0KVGhlIHVuZGlyZWN0ZWQgZ3JhcGggaXMgdGhlbiBjb252ZXJ0ZWQgaW50byBhbiBkaXJlY3RlZCBhdXhpbGlhcnkgZ3JhcGggd2hlcmUgZWFjaCB1bmRpcmVjdGVkIGVkZ2UgaXMgcmVwbGFjZWQgd2l0aCB0d28gcmVjaXByb2NhbCBkaXJlY3RlZCBlZGdlcyBhbmQgdGhlIGdyYWRpZW50IGlzIHJldmVyc2VkIGZvciB0aGUgb3Bwb3NpdGUgZGlyZWN0aW9uLg0KTmV4dCB0cmF2ZWwgdGltZXMgYXJlIGNvbXB1dGVkIGZvciBlYWNoIGRpcmVjdGVkIGVkZ2UuDQpGcm9tIHJlYWwgbGlmZSB0cmF2ZWwgZGVtYW5kIGRhdGEgdGhlIG5vZGVzIHdoaWNoIGhhdmUgaGlnaGVyIGRlbWFuZCBhcmUgc2VsZWN0ZWQgYW5kIG9wdGlvbmFsIGF1eGlsaWFyeSBPRCBwYWlycyAoY2hhaW4gb2YgYWxsIG5vZGVzKSBhcmUgYWRkZWQgdG8gZW5mb3JjZSBzdHJvbmcgY29ubmVjdGl2aXR5IG9mIHRoZSBjYXIgbmV0d29yaywgdGhlc2UgaGF2ZSB6ZXJvIHdlaWdodCBpbiB0aGUgb2JqZWN0aXZlLg0KVGhlcmUgYXJlIHR3byBkaXN0cmljdCBuZXR3b3JrcyB3aGljaCBoYXZlIGJlZW4gY2hvb3NlbiAsIG9uZSBvZiB3aGljaCBpcyBBZmZvbHRlcm4oWnVyaWNoKSBhbmQgYW5vdGhlciBvbmUgaXMgQnJpY2hwbGF0eiAoWnVyaWNoKS4NCg0KYGBge3J9DQpsaWJyYXJ5KHNmKSAgICAgICAgIyBmb3IgcmVhZGluZyBzcGF0aWFsIGRhdGEgKEdlb1BhY2thZ2UpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkgIyBmb3IgZGF0YSBtYW5pcHVsYXRpb24gYW5kIHBsb3R0aW5nDQpsaWJyYXJ5KGlncmFwaCkgICAgIyBmb3IgbmV0d29yayBhbmFseXNpcyAob3B0aW9uYWwpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocHVycnIpDQpsaWJyYXJ5KHBhdGNod29yaykNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZ3QpDQpsaWJyYXJ5KHNjYWxlcykNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoaHJicnRoZW1lcykNCmBgYA0KDQpgYGB7cn0NCg0KDQppbnN0YW5jZXMgPC0gYygiYWZmb2x0ZXJuIiwgImJpcmNocGxhdHoiKQ0KDQpzdW1tYXJ5X3RhYmxlIDwtIG1hcF9kZnIoaW5zdGFuY2VzLCBmdW5jdGlvbihpbnN0KSB7DQogIGVkZ2VzIDwtIHN0X3JlYWQocGFzdGUwKCJkYXRhLyIsIGluc3QsICIvZWRnZXNfYWxsX2F0dHJpYnV0ZXMuZ3BrZyIpLCBxdWlldCA9IFRSVUUpDQogIG5vZGVzIDwtIHN0X3JlYWQocGFzdGUwKCJkYXRhLyIsIGluc3QsICIvbm9kZXNfYWxsX2F0dHJpYnV0ZXMuZ3BrZyIpLCBxdWlldCA9IFRSVUUpDQogIG9kIDwtIHJlYWQuY3N2KHBhc3RlMCgiZGF0YS8iLCBpbnN0LCAiL29kX21hdHJpeC5jc3YiKSkNCiAgDQogIHRpYmJsZSgNCiAgICBJbnN0YW5jZSA9IGluc3QsDQogICAgTm9kZXMgPSBucm93KG5vZGVzKSwNCiAgICBFZGdlcyA9IG5yb3coZWRnZXMpLA0KICAgIFRvdGFsX0xlbmd0aF9rbSA9IHN1bShlZGdlcyRsZW5ndGgsIG5hLnJtID0gVFJVRSkgLyAxMDAwLA0KICAgIEVkZ2VzX3dpdGhfQmlrZUxhbmUgPSBzdW0oZ3JlcGwoIlAiLCBlZGdlcyRsbl9kZXNjLCBmaXhlZCA9IFRSVUUpKSwNCiAgICBQY3RfQmlrZUxhbmUgPSByb3VuZChtZWFuKGdyZXBsKCJQIiwgZWRnZXMkbG5fZGVzYywgZml4ZWQgPSBUUlVFKSkgKiAxMDAsIDEpLA0KICAgIE9EX1BhaXJzID0gbnJvdyhvZCksDQogICAgVG90YWxfVHJpcHMgPSBzdW0ob2QkdHJpcHMsIG5hLnJtID0gVFJVRSksDQogICAgQXZnX1RyaXBzX3Blcl9PRCA9IHJvdW5kKG1lYW4ob2QkdHJpcHMpLCAxKQ0KICApDQp9KQ0KDQpwcmludChzdW1tYXJ5X3RhYmxlKQ0KYGBgDQoNCnNubWFuWzRdIHByb2Nlc3Npbmcgb3V0cHV0cyB0aHJlZSBmaWxlcywgdHdvIGdwa2cgZmlsZXMsIG9uZSBvZiB3aGljaCBoYXMgbm9kZXMgYW5kIG90aGVyIGVkZ2VzLg0KTGFzdCBmaWxlIGlzIHRoZSBvZCBtYXRyaXguDQpBZmZvbHRlcm4gbmV0d29yayBoYXMgMjMxIG5vZGUgd2hpbGUgYmlyY2hwbGF0eiBoYXMgMzE0Lg0KU2ltaWxhcmx5IGJpcmNocGxhdHogKDQzMSkgaGFzIG1vcmUgZWRnZXMgdGhhbiBhZmZvbHRlcm4gbmV0d29yayAoMjkwKS4NCldlIGNhbiBhbnRpY2lwYXRlIGEgbG9uZ2VyIHRyYWluaW5nIHRpbWUgZm9yIHRoZSBiaXJjaHBsYXR6IG5ldHdvcmsuDQpCb3RoIG9mIHRoZSBpbml0aWFsIG5ldHdvcmtzIGhhcyAxIHByb3RlY3RlZCBiaWtlIGxhbmUuDQoNCmBgYHtyfQ0KaW5zdGFuY2VfYWZmb2x0ZXJuIDwtICJhZmZvbHRlcm4iDQplZGdlc19hZmZvbHRlcm4gPC0gc3RfcmVhZChwYXN0ZTAoImRhdGEvIiwgaW5zdGFuY2VfYWZmb2x0ZXJuLCAiL2VkZ2VzX2FsbF9hdHRyaWJ1dGVzLmdwa2ciKSkNCm5vZGVzX2FmZm9sdGVybiA8LSBzdF9yZWFkKHBhc3RlMCgiZGF0YS8iLCBpbnN0YW5jZV9hZmZvbHRlcm4sICIvbm9kZXNfYWxsX2F0dHJpYnV0ZXMuZ3BrZyIpKQ0Kb2RfYWZmb2x0ZXJuIDwtIHJlYWRfY3N2KHBhc3RlMCgiZGF0YS8iLCBpbnN0YW5jZV9hZmZvbHRlcm4sICIvb2RfbWF0cml4LmNzdiIpKQ0KDQppbnN0YW5jZV9iaXJjaHBsYXR6IDwtICJiaXJjaHBsYXR6Ig0KZWRnZXNfYmlyY2hwbGF0eiA8LSBzdF9yZWFkKHBhc3RlMCgiZGF0YS8iLCBpbnN0YW5jZV9iaXJjaHBsYXR6ICwgIi9lZGdlc19hbGxfYXR0cmlidXRlcy5ncGtnIikpDQpub2Rlc19iaXJjaHBsYXR6IDwtIHN0X3JlYWQocGFzdGUwKCJkYXRhLyIsIGluc3RhbmNlX2JpcmNocGxhdHogLCAiL25vZGVzX2FsbF9hdHRyaWJ1dGVzLmdwa2ciKSkNCm9kX2JpcmNocGxhdHogPC0gcmVhZF9jc3YocGFzdGUwKCJkYXRhLyIsIGluc3RhbmNlX2JpcmNocGxhdHogLCAiL29kX21hdHJpeC5jc3YiKSkNCg0KYGBgDQoNCmBgYHtyfQ0KIyBCYXNpYyBpbmZvDQojZ2xpbXBzZShlZGdlc19iaXJjaHBsYXR6KQ0KDQojIEVkZ2UgbGVuZ3RoIGRpc3RyaWJ1dGlvbiBBZmZvbHRlcm4NCnAxIDwtIGdncGxvdChlZGdlc19hZmZvbHRlcm4sIGFlcyh4ID0gbGVuZ3RoKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNTAsIGZpbGwgPSAiZGFya2JsdWUiKSArDQogIGxhYnModGl0bGUgPSAiRWRnZSBsZW5ndGggZGlzdHJpYnV0aW9uIEFmZm9sdGVybiIsIHggPSAiTGVuZ3RoIChtZXRlcnMpIikNCg0KIyBFZGdlIGxlbmd0aCBkaXN0cmlidXRpb24gQmlyY2hwbGF0eg0KcDIgPC0gZ2dwbG90KGVkZ2VzX2JpcmNocGxhdHosIGFlcyh4ID0gbGVuZ3RoKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNTAsIGZpbGwgPSAic3RlZWxibHVlIikgKw0KICBsYWJzKHRpdGxlID0gIkVkZ2UgbGVuZ3RoIGRpc3RyaWJ1dGlvbiBCaXJjaHBsYXR6IiwgeCA9ICJMZW5ndGggKG1ldGVycykiKQ0KDQpwYXRjaHdvcmtfMSA8LSAocDEgKyBwMikNCnBhdGNod29ya18xICsgcGxvdF9hbm5vdGF0aW9uKA0KICB0aXRsZSA9ICdGaWd1cmUgMTogRWRnZSBkaXN0cmlidXRpb24gYWNyb3NzIHR3byBxdWF0ZXJzIGluIHp1cmljaC1Td2l0emVybGFuZCcsDQopDQoNCg0KDQojIEdyYWRpZW50IChpZiBjb2x1bW4gZXhpc3RzKQ0KaWYoImdyYWRlIiAlaW4lIGNvbG5hbWVzKGVkZ2VzX2FmZm9sdGVybikpIHsNCiAgcDMgPC0gZ2dwbG90KGVkZ2VzX2FmZm9sdGVybiwgYWVzKHggPSBncmFkZSkpICsNCiAgICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNTAsIGZpbGwgPSAibGlnaHRncmVlbiIpICsNCiAgICBsYWJzKHRpdGxlID0gIkdyYWRlIGRpc3RyaWJ1dGlvbiBBZmZvbHRlcm4iLCB4ID0gIkdyYWRlICglKSIpDQp9DQoNCiMgR3JhZGllbnQgKGlmIGNvbHVtbiBleGlzdHMpDQppZigiZ3JhZGUiICVpbiUgY29sbmFtZXMoZWRnZXNfYmlyY2hwbGF0eikpIHsNCiAgcDQgPC0gZ2dwbG90KGVkZ2VzX2JpcmNocGxhdHosIGFlcyh4ID0gZ3JhZGUpKSArDQogICAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDUwLCBmaWxsID0gImRhcmtncmVlbiIpICsNCiAgICBsYWJzKHRpdGxlID0gIkdyYWRlIGRpc3RyaWJ1dGlvbiBCaXJjaFBsYXR6IiwgeCA9ICJHcmFkZSAoJSkiKQ0KfQ0KDQpwYXRjaHdvcmtfMiA8LSAocDMgKyBwNCkNCnBhdGNod29ya18yICsgcGxvdF9hbm5vdGF0aW9uKA0KICB0aXRsZSA9ICdGaWd1cmUgMjogR3JhZGllbnQgZGlzdHJpYnV0aW9uIG9mIHN0cmVldHMgYWNyb3NzIHR3byBxdWF0ZXJzIGluIHp1cmljaC1Td2l0emVybGFuZCcsDQopDQoNCg0KYGBgDQoNCmBgYHtyfQ0KcDEgPC0gZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSBlZGdlc19hZmZvbHRlcm4sIGNvbG9yID0gImdyZXkiLCBsaW5ld2lkdGggPSAwLjMpICsNCiAgZ2VvbV9zZihkYXRhID0gbm9kZXNfYWZmb2x0ZXJuLCBzaXplID0gMC41LCBjb2xvciA9ICJyZWQiKSArDQogIGxhYnModGl0bGUgPSBwYXN0ZSgiRmlndXJlIDM6IFN0cmVldCBOZXR3b3JrIG9mIiwgaW5zdGFuY2VfYWZmb2x0ZXJuKSkgKw0KICB0aGVtZV92b2lkKCkNCg0KcDIgPC0gZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSBlZGdlc19iaXJjaHBsYXR6LCBjb2xvciA9ICJncmV5IiwgbGluZXdpZHRoID0gMC4zKSArDQogIGdlb21fc2YoZGF0YSA9IG5vZGVzX2JpcmNocGxhdHosIHNpemUgPSAwLjUsIGNvbG9yID0gInB1cnBsZSIpICsNCiAgbGFicyh0aXRsZSA9IHBhc3RlKCJGaWd1cmUgNDogU3RyZWV0IE5ldHdvcmsgb2YiLCBpbnN0YW5jZV9iaXJjaHBsYXR6KSkgKw0KICB0aGVtZV92b2lkKCkNCnAxK3AyDQpgYGANCg0KIyMjIEJhc2VsaW5lIE1ldGhvZA0KDQpUaGUgYmFzZWxpbmUgbWV0aG9kIHVzZWQgaW4gdGhpcyBwcm9qZWN0IGlzIGEgbGluZWFyIHByb2dyYW1taW5nIHNvbHV0aW9uIGRldmVsb3BlZCBieSBbNV0gd2hpY2ggaXMgYSBtYXRoZW1hdGljYWwgcmVjaXBlIHRoYXQgYmFsYW5jZXMgY2FyIGFuZCBiaWtlIHRyYXZlbCB0aW1lcy4NCkZvciBldmVyeSBzdHJlZXQgc2VnbWVudCwgdGhlIG9wdGltaXphdGlvbiBkZWNpZGVzIGhvdyBtdWNoIHNwYWNlIHRvIGdpdmUgdG8gY2FycyBhbmQgaG93IG11Y2ggdG8gYmlrZXMsIHdpdGggYmlrZSBsYW5lcyBuZWVkaW5nIG9ubHkgaGFsZiB0aGUgd2lkdGggb2YgY2FyIGxhbmVzLg0KSXQgY29uc2lkZXJzIHdoZXJlIHBlb3BsZSBhY3R1YWxseSB3YW50IHRvIHRyYXZlbCAodGhlIG9yaWdpbuKAkWRlc3RpbmF0aW9uIHBhaXJzKSBhbmQgZW5zdXJlcyB0aGF0IGJvdGggY2FyIGFuZCBiaWtlIG5ldHdvcmtzIHN0YXkgY29ubmVjdGVkLg0KVGhlIHNvbHZlciBmaXJzdCBmaW5kcyBhIOKAnGZyYWN0aW9uYWzigJ0gc29sdXRpb24gKGUuZyAwLjcgb2YgYSBsYW5lIGZvciBiaWtlcyksIGEgcm91bmRpbmcgYWxnb3JpdGhtIHRoZW4gcGlja3MgZWRnZXMgd2l0aCB0aGUgaGlnaGVzdCBmcmFjdGlvbmFsIGJpa2UgY2FwYWNpdHkgYW5kIGZpeGVzIHRoZW0gYXMgd2hvbGUgYmlrZSBsYW5lcywgcmXigJFzb2x2aW5nIHRoZSBMUCBldmVyeSBrIHN0ZXBzLg0KVGhpcyBtYWtlcyBzdXJlIHRoYXQgY2FycyBzdGlsbCBoYXZlIGEgd2F5IHRvIGdldCBldmVyeXdoZXJlLg0KQnkgcmVwZWF0aW5nIHRoZSB3aG9sZSBwcm9jZXNzIHdpdGggZGlmZmVyZW50IHdlaWdodHMgb24gY2FyIHZzLiBiaWtlIGltcG9ydGFuY2UsIHRoZSBwbGFubmVyIG9idGFpbnMgYSBmdWxsIHNldCBvZiBQYXJldG/igJFvcHRpbWFsIG5ldHdvcmtzLCBlYWNoIHNob3dpbmcgaG93IG11Y2ggYmlrZSBpbXByb3ZlbWVudCBjb3N0cyBpbiB0ZXJtcyBvZiBjYXIgdHJhdmVsIHRpbWUuDQoNCiMjIyBQUE8gbWV0aG9kDQoNClRoZSBSZWluZm9yY2VtZW50IGxlYXJuaW5nIGFnZW50IGVudmlyb25tZW50IGluIHRoaXMgcHJvamVjdCBpcyBuYW1lZCBhcyBCaWtlTmV0d29ya0VudiBhbmQgaXQgZGVmaW5lcyBzdGF0ZSBzcGFjZSwgYWN0aW9uIHNwYWNlLCByZXdhcmQgZnVuY3Rpb24sIGFuZCB0cmFuc2l0aW9uIGR5bmFtaWNzLg0KDQojIyMjICpTdGF0ZSBTcGFjZSoNCg0KVGhlIHN0YXRlIGlzIGEgZmxhdHRlbmVkIHZlY3RvciBvZiBmZWF0dXJlcyBmb3IgZXZlcnkgbGFuZSBlZGdlIGluIHRoZSBncmFwaC4NCkZvciBlYWNoIGRpcmVjdGVkIGxhbmUgZWRnZSB0aGUgYWdlbnQgc2VlcyB0aHJlZSBmZWF0dXJlcy4NClRoZSBmZWF0dXJlcyBuYW1lIHdpdGggdGhlaXIgZGVzY3JpcHRpb24gYW5kIHZhbHVlIHR5cGUgYXJlIGdpdmVuIGluIHRoZSBiZWxvdyB0YWJsZS06DQoNClRhYmxlIDM6IEZlYXR1cmVzIG9mIHRoZSBTdGF0ZSBWZWN0b3INCg0KfCBGZWF0dXJlIHwgRGlzY3JpcHRpb24gfCBUeXBlIHwNCnw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS06fDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tOnwNCnwgZGlzdGFuY2UgfCBMZW5ndGggb2YgdGhlIGVkZ2UgKGttKSB8IGZsb2F0IHwNCnwgZ3JhZGllbnQgfCBTbG9wZSBpbiBwZXJjZW50IChwb3NpdGl2ZSB1cGhpbGwsIG5lZ2F0aXZlIGRvd25oaWxsKSB8IGZsb2F0IHwNCnwgaXNfYmlrZSB8IDEgaWYgdGhlIGxhbmUgaXMgYWxyZWFkeSBhIGJpa2UgbGFuZSAobGFuZXR5cGUgPT0gIlAiKSwgZWxzZSAwIHwgYm9vbCB8DQoNClRoZSBmdWxsIG9ic2VydmF0aW9uIGlzIGEgMUQgYXJyYXkgb2YgbGVuZ3RoICgzIMOXIG51bWJlcl9vZl9kaXJlY3RlZF9sYW5lX2VkZ2VzKS4NClNvIGlmIHRoZXJlIGFyZSA1MCBsYW5lIGVkZ2VzIHRoZSB2ZWN0b3IgaGFzIDE1MCBmZWF0dXJlcy4NClRoaXMgc3RhdGUgcmVwcmVzZW50YXRpb24gaXMgdXNlZCBiZWNhdXNlIHRoZSBhZ2VudCBuZWVkIHRvIHZlcmlmeSB0aGUgcGh5c2ljYWwgcHJvcGVydGllcyBvZiBlYWNoIGVkZ2UgdG8gZGV0ZXJtaW5lIHdoZXRoZXIgaXQgd2lsbCBiZSBiZW5lZmljaWFsIHRvIGNvbnZlcnQgaXQgaW50byBiaWtlIGxhbmUuDQpUaGUgaXNfYmlrZSBmZWF0dXJlIGlzIHRoZXJlIHNvIHRoYXQgdGhlIGFnZW50IGF2b2lkcyByZWR1bmRhbnQgYWN0aW9ucy4NCg0KIyMjIyAqQWN0aW9uIFNwYWNlKg0KDQpFYWNoIGFjdGlvbiBjb3JyZXNwb25kcyB0byBzZWxlY3Rpbmcgb25lIHNwZWNpZmljIGRpcmVjdGVkIGxhbmUgZWRnZSBhbmQgYXR0ZW1wdGluZyB0byBjb252ZXJ0IGl0IGZyb20gYSBjYXIgbGFuZSAoTVw+KSB0byBhIGJpa2UgbGFuZSAoUCkuDQoNCiMjIyMgKlJld2FyZCoNCg0KVGhlIHJld2FyZCBpcyBkZXNpZ25lZCB0byBlbmNvdXJhZ2UgdGhlIGFnZW50IHRvIHJlZHVjZSBiaWtlIHRyYXZlbCB0aW1lIHdoaWxlIGtlZXBpbmcgY2FyIHRyYXZlbCB0aW1lIGxvdywgYW5kIHRvIGF2b2lkIGRpc2Nvbm5lY3RpbmcgdGhlIGNhciBuZXR3b3JrLg0KDQpXaGVuIHRoZSBSTCBhZ2VudCB0YWtlcyBhbiBhY3Rpb24sIGJlbG93IHBvaW50cyBhcmUgY29uc2lkZXJlZCBmb3IgdGhlIHJld2FyZC4NCg0KSWYgMS4NClRoZSBlZGdlIGlzIGFscmVhZHkgYSBiaWtlIGxhbmUgdGhlbiAtNSBwZW5hbHR5IGlzIHJlY2VpdmVkLg0KRWxzZSAxLg0KVGhlIGVkZ2UgaXMgZmlyc3QgdGVtcG9yYXJpbHkgY29udmVydGVkIDIuDQpDaGVjayBpZiB0aGUgcmVtYWluaW5nIGNhciBuZXR3b3JrIGlzIHN0cm9uZ2x5IGNvbm5lY3RlZC4NCklmIDEuDQpub3QgY29ubmVjdGVkIHRoZW4gdGhlIGNvbnZlcnNpb24gaXMgcmVqZWN0ZWQgYW5kIC0yMCBwZW5hbHR5IGlzIGdpdmVuLg0KRWxzZSAxLg0KSWYgY29ubmVjdGVkIHRoZW4gdGhlIGNvbnZlcnNpb24gaXMgYWNjZXB0ZWQsIHRoZSBuZXcgYmlrZSBhbmQgY2FyIHRyYXZlbCB0aW1lcyBhcmUgY29tcHV0ZWQgYW5kIHRoZSByZXdhcmQgZXF1YXRpb24gKGkpIGlzIGNhbGN1bGF0ZWQuDQoNCiFbXShpbWFnZXMvY2xpcGJvYXJkLTM4ODgzNTcwMzQucG5nKQ0KDQo+IFJld2FyZCA9IDEwMCAvIChiaWtlX3RpbWUgKyDOtSkgLSAwLjcgw5cgY2FyX3RpbWUgKGkpDQoNCigxMDAgLyBiaWtlX3RpbWUgKSBnaXZlcyBoaWdoIHJld2FyZCB3aGVuIGJpa2UgdGltZSBpcyBsb3cuDQooLTAuNyBcKiBjYXJfdGltZSkgcGVuYWxpemVzIGluY3JlYXNlcyBpbiBjYXIgdHJhdmVsLg0KVGhlIHZhbHVlcyAxMDAgYW5kIDAuNyB3ZXJlIGNob3NlbiBlbXBpcmljYWxseSBhbmQgY2FuIGJlIGZ1cnRoZXIgdHVuZWQgdXNpbmcgdGhlIGh5cGVyIHBhcmFtZXRlciB0dW5pbmcuDQoNCiMjIyMgSW5pdGlhbCBTdGF0ZSBhbmQgRXBpc29kZSBUZXJtaW5hdGlvbg0KDQpBdCB0aGUgc3RhcnQgb2YgZWFjaCBlcGlzb2RlLCB0aGUgZ3JhcGggcmVzZXQgdG8gdGhlIGluaXRpYWwgY2Fy4oCRb25seSBjb25maWd1cmF0aW9uIChhbGwgbGFuZXMgTVw+KS4NClRoZSBhZ2VudCB0aGVuIHNlcXVlbnRpYWxseSBjb252ZXJ0cyBlZGdlcy4NCkFuIGVwaXNvZGUgZW5kcyBhZnRlciBhIGZpeGVkIG51bWJlciBvZiBzdGVwcyAobWF4X3N0ZXBzKS4NCg0KIyMjIyAqTW9kZWwgVHJhaW5pbmcqDQoNClRoZSBhZ2VudCBpcyB0cmFpbmVkIGFzIGZvbGxvd3MtOg0KDQoxLiAgVGhlIGxhbmUgZ3JhcGggYW5kIHRoZSBPRCBtYXRyaXggYXJlIGxvYWRlZCBmb3Igc3BlY2lmaWMgaW5zdGFuY2VzKGNpdGllcy9kaXN0cmljdHMpLg0KMi4gIFRoZSBCaWtlTmV0d29ya0VudiBpcyBsb2FkZWQgd2l0aCBsYW5lIGdyYXBoIGFuZCBPRCBtYXRyaXguDQozLiAgUFBPIGFsZ29yaXRobSBpcyB1c2VkIGZvciB0cmFpbmluZy4gUFBPIGlzIHVzZWQgYmVjYXVzZSBpdCB3b3JrcyB3ZWxsIHdpdGggY29udGludW91cyBhcyB3ZWxsIGFzIGRpc2NyZXRlIGFjdGlvbiBzcGFjZXMuIEZ1cnRoZXJtb3JlLCBJdCBpcyBsZXNzIHNlbnNpdGl2ZSB0byBoeXBlcnBhcmFtZXRlcnMgdGhhbiB2YW5pbGxhIHBvbGljeSBncmFkaWVudCBtZXRob2RzLg0KNC4gIFRoZSBtb2RlbCBpcyB0cmFpbmVkIGZvciAxMDAwMCB0aW1lc3RlcHMuDQo1LiAgdGhlIHRyYWluZWQgbW9kZWwgaXMgc2F2ZWQgdG8gYSB6aXAgZmlsZS4NCg0KIyMjIyAqSG93IGRvZXMgUFBPIHdvcmsgaW4gdGhpcyBjYXNlPyoNCg0KVGhlIGFnZW50IHJ1bnMgbWFueSBlcGlzb2RlcyBhbmQgaW4gZWFjaCBlcGlzb2RlIGl0IHRha2VzIG11bHRpcGxlIHN0ZXBzIGRlZmluZWQgYnkgdGhlIGh5cGVycGFyYW1ldGVyIChtYXhfc3RlcHMpLg0KQXQgZWFjaCBzdGVwLCBpdDoNCg0KMS4gIE9ic2VydmVzIHRoZSBjdXJyZW50IHN0YXRlIChlZGdlIGRpc3RhbmNlcywgZ3JhZGllbnRzLCBiaWtlIGZsYWdzKS4NCg0KMi4gIFNhbXBsZXMgYW4gYWN0aW9uIGFjY29yZGluZyB0byB0aGUgcG9saWN54oCZcyBwcm9iYWJpbGl0aWVzIChvciBjaG9vc2VzIHRoZSBhY3Rpb24gd2l0aCBoaWdoZXN0IHByb2JhYmlsaXR5IGR1cmluZyBpbmZlcmVuY2UpLg0KDQozLiAgRXhlY3V0ZXMgdGhlIGFjdGlvbiwgcmVjZWl2ZXMgYSByZXdhcmQsIGFuZCBtb3ZlcyB0byB0aGUgbmV4dCBzdGF0ZS4NCg0KNC4gIFN0b3JlcyAoc3RhdGUsIGFjdGlvbiwgcmV3YXJkLCBuZXh0X3N0YXRlLCBkb25lKSBpbiBhIGJ1ZmZlci4NCg0KQWZ0ZXIgdGhlIGFnZW50IGNvbGxlY3RzIGEgYmF0Y2ggb2YgZXhwZXJpZW5jZXMsIFBQTyB1cGRhdGVzIHRoZSBwb2xpY3kgYnk6DQoNCjEuICBDb21wdXRpbmcgdGhlIGFkdmFudGFnZSB3aGljaCBkZXRlcm1pbmVzIGhvdyBtdWNoIGJldHRlciBhbiBhY3Rpb24gd2FzIHRoYW4gdGhlIGF2ZXJhZ2UgZXhwZWN0IHR1cm4uDQoNCjIuICBDbGlwcGluZyB0aGUgcG9saWN5IHVwZGF0ZSB0byBhdm9pZCB0YWtpbmcgdG9vIGxhcmdlIGEgc3RlcC4NCg0KVGhlIG9iamVjdGl2ZSBpcyB0byBpbmNyZWFzZSB0aGUgcHJvYmFiaWxpdHkgb2YgYWN0bnRoYXQgbGVkIHRvIGhpZ2hlciByZXdhcmRzIGJ1dCBvbmx5IHdpdGhpbiBhIHRydXN0IHJlZ2lvbi4NCg0KIyMjIyAqSW5mZXJlbmNlIEFmdGVyIFRyYWluaW5nKg0KDQpPbmNlIHRyYWluZWQsIHRoZSBhZ2VudCBpcyB1c2VkIHRvIHJhbmsgZWRnZXMgaW5lYWQgb2Ygc29sdmluZyBhbiBMUC4NClRoZSBjdXJyZW50IGdyYXBoICh3aXRoIHNvbWUgZWRnZXMgYWxyZWFkeSBjb252ZXJ0ZWQgdG8gYmlrZSBsYW5lcykgaXMgdXNlZCB0byBidWlsZCB0aGUgb2JzZXJ2YXRpb24gdmVjdG9yIGluIGV4YWN0bHkgdGhlIHNhbWUgd2F5IGFzIGluIHRoZSBlbnZpcm9ubWVudC4NClNpbmNlLCB0aGUgUkwgYWdlbnQgaXMgdHJhaW5lZCBvbiBhIHNwZWNpZmljIGdyYXBoICggZS5nIGFmZm9sdGVybikgd2hpY2ggaGFzIGEgZml4ZWQgbnVtYmVyIG9mIGVkZ2VzLg0KVGhlIGdyYXBocyBkZXRlcm1pbmVzIHRoZSBvYnNlcnZhdGlvbiBzcGFjZS4NCldoZW4gbGF0ZXIgdGhlIHRyYWluZWQgYWdlbnQgaXMgbG9hZGVkIHRvIGEgZGlmZmVyZW50IGNpdHkgb3IgZGlzdHJpY3QsIHRoZSBuZXcgZ3JhcGggbWF5IGhhdmUgYSBkaWZmZXJlbnQgbnVtYmVyIG9mIGxhbmUgZWRnZXMuDQpUaHVzLCB0aGUgb2JzZXJ2YXRpb24gdmVjdG9yIGxlbmd0aCB3b3VsZCBkaWZmZXIgZnJvbSB3aGF0IHRoZSBuZXVyYWwgbmV0d29yayBleHBlY3RzLg0KVG8gb3ZlcmNvbWUsIHRoaXMgbWlzbWF0Y2ggYSBwYWRkaW5nL3RydW5jYXRpb24gZnVuY3Rpb24gaXMgYWRkZWQuDQoNCklmIHRoZSBuZXcgZ3JhcGggaGFzIGZld2VyIGVkZ2VzIHRoYW4gdGhlIHRyYWluaW5nIGdyYXBoIHdlIHBhZCB0aGUgb2JzZXJ2YXRpb24gdmVjdG9yIHdpdGggemVyb3MgdG8gcmVhY2ggdGhlIGV4cGVjdGVkIGxlbmd0aC4NClRoZSBuZXR3b3JrIHNlZXMgemVyb3MgZm9yIHRob3NlIOKAnG1pc3NpbmfigJ0gZWRnZXMsIHdoaWNoIGVzc2VudGlhbGx5IG1lYW5zIHRoZXkgaGF2ZSBtaW5pbWFsIGluZmx1ZW5jZS4NCg0KSWYgdGhlIG5ldyBncmFwaCBoYXMgbW9yZSBlZGdlcyB0aGFuIHRoZSB0cmFpbmluZyBncmFwaCB3ZSB0cnVuY2F0ZSBvciBpZ25vcmUgdGhlIGV4dHJhIGVkZ2VzLg0KVGhpcyBtZWFucyB0aGUgYWdlbnQgY2Fubm90IGFjdCBvbiB0aG9zZSBleHRyYSBlZGdlcyBhdCBhbGwsIHRoaXMgaXMgb25lIG9mIHRoZSBzZXZlcmUgbGltaXRhdGlvbiBvZiB0aGUgbW9kZWwgd2hpY2ggY2FuIGJlIHRha2VuIGludG8gYWNjb3VudCBmb3IgZnV0dXJlIHdvcmsuDQoNCkRlc3BpdGUgdGhlIGxpbWl0YXRpb25zIHRoZSBSTCBhZ2VudCBjYW4gYmUgdHJhaW5lZCBvbmNlIG9uIGEgcmVwcmVzZW50YXRpdmUgZ3JhcGggd2hpY2ggY2FuIHRoZW4gYmUgYXBwbGllZCBpbiBpbmZlcmVuY2Ugb24gYW55IG90aGVyIGRpc3RyaWN0IG9yIHBhcnQgb2YgdGhlIGNpdHkuDQpUaGlzIGlzIGEgaHVnZSBwcmFjdGljYWwgYWR2YW50YWdlIGlmIG1hbnkgcGxhY2VzIG5lZWQgdG8gYmUgb3B0aW1pc2VkIGZvciBiaWtlIGxhbmVzLg0KDQpUd28gUFBPIGFnZW50cyB3ZXJlIHRyYWluZWQsIG9uZSBvbiBBZmZvbHRlcm4gbWFwIGFuZCBhbm90aGVyIG9uIEJpcmNocGxhdHogbmV0d29yay4NClRoZSB0cmFpbmluZyBpbnNpZ2h0cyBhcmUgYXMgZm9sbG93cy06DQoNCiFbXShpbWFnZXMvY2xpcGJvYXJkLTE1MTMzOTk2MTkucG5nKQ0KDQohW10oaW1hZ2VzL2NsaXBib2FyZC00NDA5NzU4MjcucG5nKQ0KDQohW10oaW1hZ2VzL2NsaXBib2FyZC0yMzQ5MDYxODE4LnBuZykNCg0KVGhlc2UgaW5zaWdodHMgd2VyZSBjcmVhdGVkIHVzaW5nIHdhbmRiLCB0aGUgb3JpZ2luYWwgd2FuZGIgcmVwb3J0W14yXSBpbmNsdWRlcyBpbnRlcmFjdGl2ZSBncmFwaCBmb3IgZnVydGhlciBkYXRhLg0KDQpbXjJdOiA8aHR0cHM6Ly93YW5kYi5haS9oc3JhL0Jpa2VfTGFuZV9Qcm9qZWN0L3JlcG9ydHMvUFBPLUFnZW50cy1mb3ItQmlrZS1MYW5lLU9wdGltaXphdGlvbi0tVm1sbGR6b3hOall5T0RFMU5nPg0KDQojIyMgUzJWX0RRTg0KDQpUaGUgUzJW4oCRRFFOIGFnZW50IHdhcyBpbnNwaXJlZCBieSB0aGUgd29yayBvZiBEYWkgZXQgYWwuIChOSVBTIDIwMTcpLCB3aG8gY29tYmluZWQgZ3JhcGggZW1iZWRkaW5nIChTdHJ1Y3R1cmUyVmVjKSB3aXRoIGRlZXAgUeKAkWxlYXJuaW5nIHRvIGxlYXJuIGdyZWVkeSBoZXVyaXN0aWNzIGZvciBjb21iaW5hdG9yaWFsIG9wdGltaXphdGlvbiBwcm9ibGVtcyBvdmVyIGdyYXBocy4NCg0KIyMjIyAqU3RhdGUgUmVwcmVzZW50YXRpb24qDQoNCkluIHMydl9kcW4gYWdlbnQgdGhlIHN0YXRlIGlzIGEgbGluZSBncmFwaCBvZiB0aGUgb3JpZ2luYWwgbGFuZSBncmFwaC4NClN0YXRlIGNvbnNpc3RzIG9mDQoNCjEuICBOb2RlcyDigJMgRWFjaCBub2RlICh3aGljaCBjb3JyZXNwb25kcyB0byBhIGRpcmVjdGVkIGxhbmUgZWRnZSBpbiBvcmlnaW5hbCBsYW5lIGdyYXBoKS4NCiAgICBlYWNoIG5vZGUgaGFzIDMgZmVhdHVyZXMgaW5jbHVkaW5nIGRpc3RhbmNlLCBncmFkaWVudCwgaXNfYmlrZS4NCiAgICAyIC4NCiAgICBBbiBhZGphY2VuY3kgbWF0cml4IHRoYXQgdGVsbHMgdGhlIG5ldXJhbCBuZXR3b3JrIGhvdyBlZGdlcyBhcmUgY29ubmVjdGVkLg0KDQoyLiAgVmFsaWQgbWFzayDigJMgYmluYXJ5IG1hc2sgaW5kaWNhdGluZyB3aGljaCBlZGdlcyBjYW4gYmUgbGVnYWxseSBjb252ZXJ0ZWQgd2l0aG91dCBkaXNjb25uZWN0aW5nIHRoZSBjYXIgbmV0d29yay4NCg0KIyMjIyAqVHJhaW5pbmcqDQoNClRoZSBTMlbigJFEUU4gYWdlbnQgcmVwcmVzZW50cyB0aGUgY3VycmVudCBzdGF0ZSBvZiB0aGUgbGFuZSBuZXR3b3JrIGFzIGEgbGluZSBncmFwaCB3aGVyZSBlYWNoIG5vZGUgY29ycmVzcG9uZHMgdG8gYSBkaXJlY3RlZCBsYW5lIGVkZ2UgYW5kIGVkZ2VzIGluIHRoaXMgbGluZSBncmFwaCBjb25uZWN0IHR3byBsYW5lIGVkZ2VzIGlmIHRoZXkgc2hhcmUgYSBjb21tb24gaW50ZXJzZWN0aW9uLg0KRWFjaCBub2RlIGlzIGFzc29jaWF0ZWQgd2l0aCBhIGZlYXR1cmUgdmVjdG9yIG9mIGxlbmd0aCB0aHJlZSBpbmNsdWRpbmcgdGhlIGVkZ2XigJlzIGxlbmd0aCwgaXRzIGdyYWRpZW50LCBhbmQgYSBiaW5hcnkgaW5kaWNhdG9yIG9mIHdoZXRoZXIgaXQgaXMgYWxyZWFkeSBhIGJpa2UgbGFuZS4NClRoZSBhZ2VudCB0aGVuIGFwcGxpZXMgYSBtZXNzYWdl4oCRcGFzc2luZyBuZXVyYWwgbmV0d29yayAoU3RydWN0dXJlMlZlYykgdGhhdCBpdGVyYXRpdmVseSB1cGRhdGVzIG5vZGUgZW1iZWRkaW5ncyBieSBhZ2dyZWdhdGluZyBpbmZvcm1hdGlvbiBmcm9tIG5laWdoYm91cmluZyBub2Rlcy4NCkFmdGVyIG4gaXRlcmF0aW9ucywgZWFjaCBub2Rl4oCZcyBlbWJlZGRpbmcgZW5jb2RlcyBub3Qgb25seSBpdHMgb3duIHByb3BlcnRpZXMgYnV0IGFsc28gdGhlIHN0cnVjdHVyZSBhbmQgZmVhdHVyZXMgb2YgdGhlIGxvY2FsIG5laWdoYm91cmhvb2QuDQpBIGdsb2JhbCBwb29saW5nIG9wZXJhdGlvbiAobWVhbikgb3ZlciBhbGwgbm9kZSBlbWJlZGRpbmdzIHByb2R1Y2VzIGEgc2luZ2xlIHZlY3RvciB0aGF0IHN1bW1hcmlzZXMgdGhlIGVudGlyZSBuZXR3b3JrIHN0YXRlLg0KDQpGb3IgZWFjaCBsYW5lIGVkZ2UsIHRoZSBhZ2VudCBjb25jYXRlbmF0ZXMgaXRzIGluZGl2aWR1YWwgZW1iZWRkaW5nIHdpdGggdGhlIGdsb2JhbCBncmFwaCBzdW1tYXJ5IGFuZCBwYXNzZXMgdGhlIHJlc3VsdCB0aHJvdWdoIGEgc21hbGwgbXVsdGnigJFsYXllciBwZXJjZXB0cm9uICh0d28gaGlkZGVuIGxheWVycykgdG8gY29tcHV0ZSBhIHNjYWxhciBR4oCRdmFsdWUuDQpUaGlzIFHigJF2YWx1ZSBlc3RpbWF0ZXMgdGhlIGV4cGVjdGVkIGxvbmfigJF0ZXJtIHJld2FyZCBpZiB0aGF0IGVkZ2UgaXMgY29udmVydGVkIGZyb20gYSBjYXIgbGFuZSB0byBhIGJpa2UgbGFuZSBnaXZlbiB0aGUgY3VycmVudCBuZXR3b3JrIHN0YXRlLg0KRWRnZXMgdGhhdCBhcmUgY3JpdGljYWwgZm9yIGNhciBjb25uZWN0aXZpdHkgbGlrZSBicmlkZ2VzIG9yIGFydGVyaWFsIHJvYWRzIHRoYXQgaWYgcmVtb3ZlZCB3b3VsZCBkaXNjb25uZWN0IHRoZSBncmFwaCwgcmVjZWl2ZSBsb3cgUeKAkXZhbHVlcyB3aGlsZSBlZGdlcyB3aG9zZSBjb252ZXJzaW9uIHdvdWxkIHNpZ25pZmljYW50bHkgcmVkdWNlIGJpa2UgdHJhdmVsIHRpbWUgd2l0aG91dCBoYXJtaW5nIGNhciB0cmF2ZWwgdGltZSByZWNlaXZlIGhpZ2ggUeKAkXZhbHVlcy4NClRoZSBtZXNzYWdl4oCRcGFzc2luZyBtZWNoYW5pc20gYWxsb3dzIHRoZSBhZ2VudCB0byByZWFzb24gYWJvdXQgbmV0d29ya+KAkXdpZGUgY29uc2VxdWVuY2VzLg0KRHVyaW5nIGluZmVyZW5jZSBR4oCRdmFsdWUgaXMgdXNlZCB0byByYW5rIGVkZ2VzIGR1cmluZyBpbmZlcmVuY2UgYSBoaWdoZXIgUSBtZWFucyB0aGF0IHRoZSBsYW5lIGlzIGEgYmV0dGVyIGNhbmRpZGF0ZSBmb3IgY29udmVyc2lvbi4NCg0KVW5saWtlIG9uIHBvbGljeSBtZXRob2QgUFBPLCBzMnZfZHFuIGlzIGEgT2ZmLXBvbGljeSBtZXRob2QuDQpJdCBzdG9yZXMgcGFzdCBleHBlcmllbmNlcyBpbiBhIHJlcGxheSBidWZmZXIgYW5kIHNhbXBsZXMgcmFuZG9tbHkgdG8gdXBkYXRlIHRoZSBR4oCRbmV0d29yay5BbHRob3VnaCwgaXQgaXMgZ2VuZXJhbGx5IG1vcmUgc2FtcGxl4oCRZWZmaWNpZW50IHRoYW4gb27igJFwb2xpY3kgbWV0aG9kcywgaXQgY2FuIGJlIHVuc3RhYmxlIGlmIGh5cGVycGFyYW1ldGVycyBhcmUgbm90IHR1bmVkLg0KSGFuZGxpbmcgdmFyaWFibGUgZ3JhcGggcmVwcmVzZW50YXRpb24uDQoNCldlIHVzZWQgcGFkZGluZy90cnVuY2F0aW9uIGluIFBQTyBhZ2VudCB0byBtYWtlIGl0IGFjY29tb2RhYmxlIGZvciBhIHZhcmlldHkgb2YgY2l0eSBuZXR3b3Jrcy4NCkhvd2V2ZXIsIHRoaXMgaXMgbm90IHJlcXVpcmVkIGJ5IHMydl9kcW4gYXMgdGhlIGdyYXBoIHJlcHJlc2VudGF0aW9uIGlzIGluaGVyZW50bHkgdmFyaWFibGXigJFzaXplIHdoaWNoIG1lYW5zIHRoYXQgdGhlIG1vZGVsIHByb2Nlc3NlcyBhIHNldCBvZiBub2RlcyBhbmQgZWRnZXMgdmlhIG1lc3NhZ2UgcGFzc2luZywgd2hpY2ggd29ya3MgZm9yIGFueSBudW1iZXIgb2YgZWRnZXMuDQpQeVRvcmNoIEdlb21ldHJpY+KAmXMgQmF0Y2ggY2xhc3MgY29sbGF0ZXMgbXVsdGlwbGUgZ3JhcGhzIG9mIGRpZmZlcmVudCBzaXplcyBpbnRvIGEgc2luZ2xlIGJhdGNoLg0KRGVzcGl0ZSBpdHMgYWR2YW50YWdlcywgb25lIG1haW4gbGltaXRhdGlvbiBvZiBzMnZfZHFuIGlzIHRoYXQgaXQgcmVxdWlyZXMgY2FyZWZ1bCByZXdhcmQgc2NhbGluZyBhbmQgdGFyZ2V0IG5ldHdvcmsgdXBkYXRlcy4NCg0KQW4gUzJWX0RRTiBhZ2VudCB3YXMgdHJhaW5lZCBvbiBCaXJjaHBsYXR6IG5ldHdvcmsuDQpUaGUgdHJhaW5pbmcgaW5zaWdodHMgYXJlIGFzIGZvbGxvd3MtOg0KDQohW10oaW1hZ2VzL2NsaXBib2FyZC0yNDkwOTc0NzQ4LnBuZykNCg0KIVtdKGltYWdlcy9jbGlwYm9hcmQtMTUyMzc3NTI4Mi5wbmcpDQoNCkFib3ZlIGluc2lnaHRzIHdlcmUgY3JlYXRlZCB1c2luZyB3YW5kYiwgdGhlIG9yaWdpbmFsIHdhbmRiIHJlcG9ydFteM10gaW5jbHVkZXMgaW50ZXJhY3RpdmUgZ3JhcGggZm9yIGZ1cnRoZXIgZGF0YS4NCg0KW14zXTogPGh0dHBzOi8vd2FuZGIuYWkvaHNyYS9TMlZfRFFOL3JlcG9ydHMvUzJWX0RRTi0tVm1sbGR6b3hOall5T1Rrd053Pg0KDQojIyMgUmVzdWx0cw0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHB1cnJyKQ0KbGlicmFyeSh0aWR5cikNCg0KIyBSb290IGRpcmVjdG9yeSAod2hlcmUgYWxsIGV4cGVyaW1lbnQgZm9sZGVycyBsaXZlKQ0Kcm9vdF9kaXIgPC0gIkM6L1VzZXJzL3VzZXIvRG9jdW1lbnRzL2Jpa2VfbGFuZV9yZXNlYXJjaC9vdXRwdXRzX2Jpa3JfcmwvczJ2X2RxbiINCg0KIyBGaW5kIGFsbCBDU1YgZmlsZXMgbWF0Y2hpbmcgdGhlIHBhdHRlcm4NCmZpbGVfbGlzdCA8LSBsaXN0LmZpbGVzKA0KICBwYXRoID0gcm9vdF9kaXIsDQogIHBhdHRlcm4gPSAicmVhbF9wYXJldG9fb3B0aW1pemVfb2RfMS4wXzUwXFwuY3N2JCIsDQogIHJlY3Vyc2l2ZSA9IFRSVUUsDQogIGZ1bGwubmFtZXMgPSBUUlVFDQopDQoNCiMgRnVuY3Rpb24gdG8gZXh0cmFjdCBib3RoIGV4cGVyaW1lbnQgYW5kIGxvY2F0aW9uDQpyZWFkX2FuZF9sYWJlbCA8LSBmdW5jdGlvbihwYXRoKSB7DQogIGRmIDwtIHJlYWQuY3N2KHBhdGgpDQogIA0KICAjIEdldCByZWxhdGl2ZSBwYXRoIGZyb20gcm9vdF9kaXINCiAgcmVsX3BhdGggPC0gZ3N1Yihub3JtYWxpemVQYXRoKHJvb3RfZGlyLCB3aW5zbGFzaCA9ICIvIiksICIiLCBub3JtYWxpemVQYXRoKHBhdGgsIHdpbnNsYXNoID0gIi8iKSkNCiAgIyBFeGFtcGxlOiAiL2Jpa2VfbGFuZV9hZ2VudF9hZmZvbHRlcm5fMjAyNjA0MDVfMTUyMy9hZmZvbHRlcm4vcmVhbF9wYXJldG9fb3B0aW1pemVfb2RfMS4wXzUwLmNzdiINCiAgDQogICMgU3BsaXQgYnkgIi8iDQogIHBhcnRzIDwtIHN0cnNwbGl0KHJlbF9wYXRoLCAiLyIpW1sxXV0NCiAgIyBwYXJ0c1sxXSBpcyBlbXB0eSAoc3RhcnRzIHdpdGggLyksIHBhcnRzWzJdID0gZXhwZXJpbWVudCBmb2xkZXIsIHBhcnRzWzNdID0gbG9jYXRpb24gKGFmZm9sdGVybi9iaXJjaHBsYXR6KQ0KICBleHBlcmltZW50IDwtIHBhcnRzWzJdDQogIGxvY2F0aW9uIDwtIHBhcnRzWzNdDQogIA0KICBkZiRleHBlcmltZW50IDwtIGV4cGVyaW1lbnQNCiAgZGYkbG9jYXRpb24gPC0gbG9jYXRpb24NCiAgcmV0dXJuKGRmKQ0KfQ0KDQojIENvbWJpbmUgYWxsIGRhdGENCmNvbWJpbmVkX2RmIDwtIG1hcF9kZihmaWxlX2xpc3QsIHJlYWRfYW5kX2xhYmVsKQ0KDQojIFJlc2hhcGUgdG8gbG9uZyBmb3JtYXQgKGJpa2UvY2FyIGNhdGVnb3JpZXMpDQpkZl9sb25nIDwtIGNvbWJpbmVkX2RmICU+JQ0KICBwaXZvdF9sb25nZXIoDQogICAgY29scyA9IGMoYmlrZV9lZGdlcywgY2FyX2VkZ2VzLCBiaWtlX3RpbWUsIGNhcl90aW1lKSwNCiAgICBuYW1lc190byA9IGMoIm1vZGUiLCAiLnZhbHVlIiksDQogICAgbmFtZXNfcGF0dGVybiA9ICIoYmlrZXxjYXIpXyguKikiDQogICkNCiMgTm93IGNvbHVtbnM6IC4uLiwgbW9kZSwgZWRnZXMsIHRpbWUsIGV4cGVyaW1lbnQsIGxvY2F0aW9uDQoNCmRmIDwtIGRmX2xvbmcNCg0KZGYgPC0gZGYgJT4lDQogIGdyb3VwX2J5KGJpa2VfZWRnZXNfYWRkZWQsIGV4cGVyaW1lbnQsIGxvY2F0aW9uLCBtb2RlKSAlPiUNCiAgbXV0YXRlKGR1cGxpY2F0ZV9pZCA9IHJvd19udW1iZXIoKSkgJT4lDQogIHVuZ3JvdXAoKQ0KDQpkZl93aWRlIDwtIGRmICU+JQ0KICBwaXZvdF93aWRlcigNCiAgICBpZF9jb2xzID0gYyhiaWtlX2VkZ2VzX2FkZGVkLCBleHBlcmltZW50LCBsb2NhdGlvbiwgZHVwbGljYXRlX2lkKSwNCiAgICBuYW1lc19mcm9tID0gbW9kZSwNCiAgICB2YWx1ZXNfZnJvbSA9IHRpbWUNCiAgKSAlPiUNCiAgcmVuYW1lKGJpa2VfdGltZSA9IGJpa2UsIGNhcl90aW1lID0gY2FyKSAlPiUNCiAgc2VsZWN0KC1kdXBsaWNhdGVfaWQpICAgIyByZW1vdmUgdGhlIGhlbHBlciBjb2x1bW4NCg0KDQoNCmBgYA0KDQojIyMjICpDb21wYXJpc29uIG9mIEZpbmFsIFBlcmNlaXZlZCBCaWtlIGFuZCBDYXIgVHJhdmVsIFRpbWVzKg0KDQpgYGB7cn0NCmxpYnJhcnkoZ3QpDQoNCnN1bW1hcnlfdGFibGUgPC0gZGZfd2lkZSAlPiUNCiAgYXJyYW5nZShsb2NhdGlvbiwgZXhwZXJpbWVudCwgYmlrZV9lZGdlc19hZGRlZCkgJT4lICAgIyBlbnN1cmUgY29ycmVjdCBvcmRlcg0KICBncm91cF9ieShsb2NhdGlvbiwgZXhwZXJpbWVudCkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBpbml0X2Jpa2VfdGltZSA9IGZpcnN0KGJpa2VfdGltZSksDQogICAgaW5pdF9jYXJfdGltZSA9IGZpcnN0KGNhcl90aW1lKSwNCiAgICBmaW5hbF9iaWtlX3RpbWUgPSBsYXN0KGJpa2VfdGltZSksDQogICAgZmluYWxfY2FyX3RpbWUgPSBsYXN0KGNhcl90aW1lKSwNCiAgICBudW1fcG9pbnRzID0gbigpDQogICkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgIyBSb3VuZCBudW1lcmljIGNvbHVtbnMgZm9yIHJlYWRhYmlsaXR5DQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIH4gcm91bmQoLngsIDMpKSkNCg0KIyBDcmVhdGUgZ3QgdGFibGUNCmd0KHN1bW1hcnlfdGFibGUpICU+JQ0KICB0YWJfaGVhZGVyKA0KICAgIHRpdGxlID0gIlRhYmxlIDQ6IEluaXRpYWwgYW5kIGZpbmFsIHRyYXZlbCB0aW1lcyBhbmQgbnVtYmVyIG9mIFBhcmV0byBwb2ludHMiLA0KICApICU+JQ0KICBjb2xzX2xhYmVsKA0KICAgIGxvY2F0aW9uID0gIkxvY2F0aW9uIiwNCiAgICBleHBlcmltZW50ID0gIkFsZ29yaXRobSIsDQogICAgaW5pdF9iaWtlX3RpbWUgPSAiSW5pdGlhbCBiaWtlIHRpbWUiLA0KICAgIGluaXRfY2FyX3RpbWUgPSAiSW5pdGlhbCBjYXIgdGltZSIsDQogICAgZmluYWxfYmlrZV90aW1lID0gIkZpbmFsIGJpa2UgdGltZSAoc2Vjb25kcykiLA0KICAgIGZpbmFsX2Nhcl90aW1lID0gIkZpbmFsIGNhciB0aW1lIiwNCiAgICBudW1fcG9pbnRzID0gIkJpa2UgRWRnZXMiDQogICkgJT4lDQogIGZtdF9udW1iZXIoY29sdW1ucyA9IGMoaW5pdF9iaWtlX3RpbWUsIGluaXRfY2FyX3RpbWUsIGZpbmFsX2Jpa2VfdGltZSwgZmluYWxfY2FyX3RpbWUpLA0KICAgICAgICAgICAgIGRlY2ltYWxzID0gMikgJT4lDQogIHRhYl9vcHRpb25zKA0KICAgIHRhYmxlLmZvbnQuc2l6ZSA9IDEyLA0KICAgIGhlYWRpbmcudGl0bGUuZm9udC5zaXplID0gMTQsDQogICAgaGVhZGluZy5zdWJ0aXRsZS5mb250LnNpemUgPSAxMg0KICApDQpgYGANCg0KVGFibGUgNCByZXZlYWxzIGEgY29uc2lzdGVudCB0cmFkZeKAkW9mZiBiZXR3ZWVuIG9wdGltYWxpdHkgYW5kIHJlc291cmNlIGVmZmljaWVuY3kuDQpMaW5lYXIgUHJvZ3JhbW1pbmcgKExQKSBhY2hpZXZlcyB0aGUgbG93ZXN0IGZpbmFsIGJpa2UgdHJhdmVsIHRpbWUgaW4gYm90aCBkaXN0cmljdHMgKDMuODfigK9zIGluIEFmZm9sdGVybiwgMy4yMuKAr3MgaW4gQmlyY2hwbGF0eikgYnV0IGl0IGRvZXMgc28gYnkgcmVhbGxvY2F0aW5nIHN1YnN0YW50aWFsbHkgbW9yZSBiaWtlIGxhbmVzICgxMzAgYW5kIDI2NywgcmVzcGVjdGl2ZWx5KS4NCkluIGNvbnRyYXN0LCBhbGwgUkwgYWdlbnRzIChQUE8gYW5kIFMyVuKAkURRTikgcHJvZHVjZSBzbGlnaHRseSBoaWdoZXIgZmluYWwgYmlrZSB0aW1lcyAoMy43MuKAkzQuNTDigK9zKSB3aGlsZSB1c2luZyByb3VnaGx5IDQwJSBmZXdlciBiaWtlIGxhbmVzIGluIEFmZm9sdGVybiAoODPigJM4NyB2cy4gMTMwKSBhbmQgMzXigJM0MCUgZmV3ZXIgaW4gQmlyY2hwbGF0eiAoMTU04oCTMTc2IHZzLiAyNjcpLg0KDQpgYGB7cn0NCg0KDQojIEZpbHRlciBmb3IgYmlrZSBtb2RlIGFuZCBhZmZvbHRlcm4gbG9jYXRpb24NCmRmX2Jpa2VfYWZmb2x0ZXJuIDwtIGRmICU+JQ0KICBmaWx0ZXIobW9kZSA9PSAiYmlrZSIsIGxvY2F0aW9uID09ICJhZmZvbHRlcm4iKQ0KDQpwIDwtIGRmX2Jpa2VfYWZmb2x0ZXJuICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBlZGdlcywgeSA9IHRpbWUsIGZpbGwgPSBleHBlcmltZW50KSkgKw0KICBnZW9tX2FyZWEoYWxwaGEgPSAwLjUpICsNCiAgZmFjZXRfd3JhcCh+IGV4cGVyaW1lbnQpICsgICAgICAgICAgIyBvbmUgc3VicGxvdCBwZXIgZXhwZXJpbWVudA0KICBsYWJzKHggPSAiRWRnZXMiLCB5ID0gIlRpbWUiLCB0aXRsZSA9ICJGaWd1cmUgNTogQ2hhbmdlIGluIFBlcmNlaXZlZCBCaWtlIFRpbWUgb24gQWZmb2x0ZXJuIGFzIFRoZSBBbGdvcml0aG1zIFByb2dyZXNzIEFkZCBCaWtlIExhbmVzIE92ZXIgVGltZSIpICsNCiAgdGhlbWVfaXBzdW0oKQ0KDQojIE1ha2UgaW50ZXJhY3RpdmUNCnAxIDwtIGdncGxvdGx5KHApDQpwMQ0KDQoNCmBgYA0KDQpQbG90IHNob3dzIHRoYXQNCg0KYGBge3J9DQojIEZpbHRlciBmb3IgYmlrZSBtb2RlIGFuZCBiaXJjaHBsYXR6IGxvY2F0aW9uDQpkZl9iaWtlX2FmZm9sdGVybiA8LSBkZiAlPiUNCiAgZmlsdGVyKG1vZGUgPT0gImJpa2UiLCBsb2NhdGlvbiA9PSAiYmlyY2hwbGF0eiIpDQoNCnAgPC0gZGZfYmlrZV9hZmZvbHRlcm4gJT4lDQogIGdncGxvdChhZXMoeCA9IGVkZ2VzLCB5ID0gdGltZSwgZmlsbCA9IGV4cGVyaW1lbnQpKSArDQogIGdlb21fYXJlYShhbHBoYSA9IDAuNSkgKw0KICBmYWNldF93cmFwKH4gZXhwZXJpbWVudCkgKyAgICAgICAgICAjIG9uZSBzdWJwbG90IHBlciBleHBlcmltZW50DQogIGxhYnMoeCA9ICJFZGdlcyIsIHkgPSAiVGltZSIsIHRpdGxlID0gIkZpZ3VyZSA2OiBDaGFuZ2UgaW4gUGVyY2VpdmVkIEJpa2UgVGltZSBvbiBCaXJjaHBsYXR6IGFzIFRoZSBBbGdvcml0aG1zIFByb2dyZXNzIEFkZCBCaWtlIExhbmVzIE92ZXIgVGltZSIpICsNCiAgdGhlbWVfaXBzdW0oKQ0KDQojIE1ha2UgaW50ZXJhY3RpdmUNCnAyIDwtIGdncGxvdGx5KHApDQpwMg0KDQpgYGANCg0KVGhlIHRhYmxlIHNob3dzIHRoYXQgTGluZWFyIFByb2dyYW1taW5nIChMUCkgbm90IG9ubHkgYWNoaWV2ZXMgdGhlIGxvd2VzdCBmaW5hbCBiaWtlIHRyYXZlbCB0aW1lcyBidXQgYWxzbyBsaW1pdHMgdGhlIGluY3JlYXNlIGluIGNhciB0cmF2ZWwgdGltZXMgbW9yZSBlZmZlY3RpdmVseSB0aGFuIHRoZSBSTCBhZ2VudHMuDQpTdGFydGluZyBmcm9tIGluaXRpYWwgY2FyIHRpbWVzIG9mIDEuODPigK9zIChBZmZvbHRlcm4pIGFuZCAxLjU44oCvcyAoQmlyY2hwbGF0eikgTFAgaW5jcmVhc2VzIGNhciB0aW1lcyB0byAyLjYw4oCvcyAoKzQyJSkgYW5kIDIuNTHigK9zICgrNTklKSByZXNwZWN0aXZlbHkuDQpJbiBjb250cmFzdCwgYWxsIFJMIGFnZW50cyByZXN1bHQgaW4gc3Vic3RhbnRpYWxseSBoaWdoZXIgY2FyIHRpbWVzLg0KRm9yIGV4YW1wbGUgZm9yIEFmZm9sdGVybiwgdGhlIGJlc3QgUkwgYWdlbnQgKFBQTyB0cmFpbmVkIG9uIEFmZm9sdGVybikgZmluYWwgY2FyIHRpbWUgcmVhY2hlcyAzLjg04oCvcyAoKzExMCUpIGFuZCBmb3IgQmlyY2hwbGF0eiwgUzJW4oCRRFFOIHJlYWNoZXMgMy4yN+KAr3MgKCsxMDclKSB3aGlsZSBQUE8gdHJhaW5lZCBvbiBCaXJjaHBsYXR6IHJlYWNoZXMgMy40MeKAr3MgKCsxMTYlKS4NClRoaXMgaXMgZGVzcGl0ZSBSTCBhZ2VudHMgdXNpbmcgZmFyIGZld2VyIGJpa2UgbGFuZXMgKGUuZy4sIDgzIHZzLiAxMzAgaW4gQWZmb2x0ZXJuKS4NClRoZSBmaW5kaW5nIHN1Z2dlc3RzIHRoYXQgTFAgaXMgbW9yZSBjYXLigJFhd2FyZSB3aGVyZSBpdCBjYXJlZnVsbHkgc2VsZWN0cyBsYW5lcyB0aGF0IGltcHJvdmUgYmlrZWFiaWxpdHkgd2hpbGUgbWluaW1pemluZyBkaXNydXB0aW9uIHRvIGNhciB0cmFmZmljLg0KUkwgYWdlbnRzLCBldmVuIHdoZW4gdHJhaW5lZCB3aXRoIGEgcmV3YXJkIHRoYXQgaW5jbHVkZXMgY2FyIHRyYXZlbCB0aW1lIGFwcGVhciB0byBhbGxvY2F0ZSBiaWtlIGxhbmVzIGluIGEgd2F5IHRoYXQgaW5hZHZlcnRlbnRseSBpbmNyZWFzZXMgY2FyIHRyYXZlbCB0aW1lIG1vcmUgdGhhbiBMUCBkb2VzLg0KDQpPbmUgd2F5IHRvIGltcHJvdmUgdGhlIFJMIGFnZW50IGNhbiBiZSBpbmNyZWFzaW5nIHRoZSB0cmFpbmluZyBidWRnZXQgYW5kIGV4dGVuc2l2ZSBoeXBlcnBhcmFtZXRlciB0dW5pbmcgYXMgdGhleSBoYWQgcmVsYXRpdmVseSBzaG9ydCB0cmFpbmluZyBidWRnZXRzIChlLmcuLCA2MzXigJMyNDg5IHNlY29uZHMpLg0KQW5vdGhlciwgd2F5IGlzIG1vcmUgdGhvcm91Z2ggcmV3YXJkIGVuZ2luZWVyaW5nLg0KDQpgYGB7cn0NCg0KIyBGaWx0ZXIgZm9yIGJpa2UgbW9kZSBhbmQgYWZmb2x0ZXJuIGxvY2F0aW9uDQpkZl9iaWtlX2FmZm9sdGVybiA8LSBkZiAlPiUNCiAgZmlsdGVyKG1vZGUgPT0gImNhciIsIGxvY2F0aW9uID09ICJhZmZvbHRlcm4iKQ0KDQpwIDwtIGRmX2Jpa2VfYWZmb2x0ZXJuICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBlZGdlcywgeSA9IHRpbWUsIGZpbGwgPSBleHBlcmltZW50KSkgKw0KICBnZW9tX2FyZWEoYWxwaGEgPSAwLjUpICsNCiAgZmFjZXRfd3JhcCh+IGV4cGVyaW1lbnQpICsgICAgICAgICAgIyBvbmUgc3VicGxvdCBwZXIgZXhwZXJpbWVudA0KICAgbGFicyh4ID0gIkVkZ2VzIiwgeSA9ICJUaW1lIiwgdGl0bGUgPSAiRmlndXJlIDc6IENoYW5nZSBpbiBQZXJjZWl2ZWQgQ2FyIFRpbWUgb24gQWZmb2x0ZXJuIGFzIFRoZSBBbGdvcml0aG1zIFByb2dyZXNzIEFkZCBCaWtlIExhbmVzIE92ZXIgVGltZSIpICsNCiAgdGhlbWVfaXBzdW0oKQ0KDQojIE1ha2UgaW50ZXJhY3RpdmUNCnAxIDwtIGdncGxvdGx5KHApDQpwMQ0KDQojIEZpbHRlciBmb3IgYmlrZSBtb2RlIGFuZCBiaXJjaHBsYXR6IGxvY2F0aW9uDQpkZl9iaWtlX2FmZm9sdGVybiA8LSBkZiAlPiUNCiAgZmlsdGVyKG1vZGUgPT0gImNhciIsIGxvY2F0aW9uID09ICJiaXJjaHBsYXR6IikNCg0KcCA8LSBkZl9iaWtlX2FmZm9sdGVybiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gZWRnZXMsIHkgPSB0aW1lLCBmaWxsID0gZXhwZXJpbWVudCkpICsNCiAgZ2VvbV9hcmVhKGFscGhhID0gMC41KSArDQogIGZhY2V0X3dyYXAofiBleHBlcmltZW50KSArICAgICAgICAgICMgb25lIHN1YnBsb3QgcGVyIGV4cGVyaW1lbnQNCiAgbGFicyh4ID0gIkVkZ2VzIiwgeSA9ICJUaW1lIiwgdGl0bGUgPSAiRmlndXJlIDg6IENoYW5nZSBpbiBQZXJjZWl2ZWQgQ2FyIFRpbWUgb24gQmlyY2hwbGF0eiBhcyBUaGUgQWxnb3JpdGhtcyBBZGQgQmlrZSBMYW5lcyBPdmVyIFRpbWUiKSArDQogIHRoZW1lX2lwc3VtKCkNCg0KIyBNYWtlIGludGVyYWN0aXZlDQpwMiA8LSBnZ3Bsb3RseShwKQ0KcDINCg0KYGBgDQoNCiMjIyMgKkNvbXBhcmlzb24gb2YgVHJhaW5pbmcgYW5kIFBhcmV0byBDb21wdXRhdGlvbiBUaW1lKg0KDQpUYWJsZSA1IDogU3VtbWFyeSBvZiB0cmFpbmluZyB0aW1lIChzZWNvbmRzKSBhbmQgUGFyZXRvIGNvbXB1dGF0aW9uIHRpbWUgKHNlY29uZHMpDQoNCnwgTWV0aG9kIHwgVHJhaW5pbmcgdGltZSAocykgfCBBZmZvbHRlcm4gKFBhcmV0byBjb21wdXRhdGlvbiB0aW1lLCBzKSB8IEJpcmNocGxhdHogKFBhcmV0byBjb21wdXRhdGlvbiB0aW1lLCBzKSB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tfA0KfCBBZmZvbHRlcm5fUFBPIHwgNjM1IHwgMTAuODUgfCAzNS41MCB8DQp8IEJpcmNocGxhdHpfUFBPIHwgMjQ4OSB8IDEzLjM3IHwgMzcuMTIgfA0KfCBMUF9BZmZvbHRlcm4gfCAgfCAxMTcuNjMgfCAgfA0KfCBMUF9CaXJjaHBsYXR6IHwgIHwgIHwgMzkxMS43MSB8DQp8IEJpcmNocGxhdHpfczJ2ZHFuIHwgMTE0MjEgfCAxMi44NSB8IDczLjg1IHwNCg0KVGhlIHJlc3VsdHMgaW4gdGhlIHRhYmxlIGFib3ZlIGhpZ2hsaWdodCBhIGNsZWFyIHRyYWRl4oCRb2ZmIGJldHdlZW4gb3B0aW1hbGl0eSBhbmQgdHJhbnNmZXJhYmlsaXR5Lg0KVGhlIExpbmVhciBQcm9ncmFtbWluZyAoTFApIGFwcHJvYWNoIGFjaGlldmVzIHRoZSBsb3dlc3QgcGVyY2VpdmVkIGJpa2UgdHJhdmVsIHRpbWUgaW4gYm90aCBkaXN0cmljdHMgKDMuODfigK9zIGZvciBBZmZvbHRlcm4sIDMuMjLigK9zIGZvciBCaXJjaHBsYXR6KSwgaW1wcm92aW5nIHVwb24gdGhlIGluaXRpYWwgdGltZXMgYnkgYWJvdXQgNDYlIGFuZCA0OSUgcmVzcGVjdGl2ZWx5Lg0KSG93ZXZlciwgTFAgaXMgbm90IHRyYW5zZmVyYWJsZSB3aGljaCBpcyB3aHkgZWFjaCBuZXcgZGlzdHJpY3QgcmVxdWlyZXMgYSBmdWxsLCB0aW1l4oCRaW50ZW5zaXZlIHJl4oCRb3B0aW1pemF0aW9uIGZyb20gc2NyYXRjaCAoYXMgcmVmbGVjdGVkIGluIHRoZSB0YWJsZSkuDQpJbiBjb250cmFzdCwgcmVpbmZvcmNlbWVudCBsZWFybmluZyBhZ2VudHMgKFBQTywgUzJW4oCRRFFOKSBjYW4gYmUgdHJhaW5lZCBvbmNlIG9uIG9uZSBkaXN0cmljdCBhbmQgdGhlbiBhcHBsaWVkIHRvIGFub3RoZXIgd2l0aCBuZWdsaWdpYmxlIGluZmVyZW5jZSBjb3N0Lg0KRm9yIGV4YW1wbGUsIFBQTyB0cmFpbmVkIG9uIEJpcmNocGxhdHogYWNoaWV2ZXMgYSBmaW5hbCBiaWtlIHRpbWUgb2YgNC41MOKAr3Mgb24gQWZmb2x0ZXJuLCBvbmx5IDAuNjPigK9zIGhpZ2hlciB0aGFuIExQIHdoaWxlIHVzaW5nIHNpZ25pZmljYW50bHkgZmV3ZXIgYmlrZSBlZGdlcyAoODcgdnMuIDEzMCkuDQpTaW1pbGFybHksIG9uIEJpcmNocGxhdHogaXRzZWxmLCB0aGUgYmVzdCBSTCBhZ2VudCAoUzJW4oCRRFFOKSByZWFjaGVzIDMuNzLigK9zLCAwLjUw4oCvcyBhYm92ZSBMUCBidXQgd2l0aCBhYm91dCAxMDAgZmV3ZXIgYmlrZSBsYW5lcy4NClRoZSBkaXNhZHZhbnRhZ2Ugb2YgUkwgaXMgaXRzIHdvcnNlIGZpbmFsIGJpa2UgdGltZSAobGVzcyBhZ2dyZXNzaXZlIG5ldHdvcmsgcmVhbGxvY2F0aW9uKSBhbmQgdGhlIHVwZnJvbnQgdHJhaW5pbmcgY29zdCAodXAgdG8gMTE0MjHigK9zIGZvciBTMlbigJFEUU4pLg0KVGhlIGFkdmFudGFnZSBpcyBjbGVhciB3aGVyZSBhZnRlciB0cmFpbmluZyBhbiBSTCBhZ2VudCBjYW4gaW5zdGFudGx5IHByb2R1Y2UgYSB2aWFibGUgUGFyZXRv4oCRb3B0aW1hbCBuZXR3b3JrIGZvciBhbnkgbmV3IGNpdHksIHdoZXJlYXMgTFAgbXVzdCBiZSByZeKAkXNvbHZlZCBlYWNoIHRpbWUgYSBwcm9oaWJpdGl2ZSBvdmVyaGVhZCBmb3IgbGFyZ2XigJFzY2FsZSBvciBpdGVyYXRpdmUgdXJiYW4gcGxhbm5pbmcuDQpUaHVzLCBMUCByZW1haW5zIHRoZSBnb2xkIHN0YW5kYXJkIGZvciBzdGF0aWMsIHBlcuKAkWNpdHkgb3B0aW1pemF0aW9uLCB3aGlsZSBSTCBvZmZlcnMgYSB0cmFuc2ZlcmFibGUgYWx0ZXJuYXRpdmUgd2hlbiBjb21wdXRhdGlvbmFsIGJ1ZGdldHMgb3IgcmVwZWF0ZWQgYXBwbGljYXRpb25zIGFyZSBhIGNvbmNlcm4uDQoNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikgDQpsaWJyYXJ5KG1hZ2ljaykNCmxpYnJhcnkocGF0Y2h3b3JrKQ0KDQpiYXNlX2RpciA8LSAiQzovVXNlcnMvdXNlci9Eb2N1bWVudHMvYmlrZV9sYW5lX3Jlc2VhcmNoL291dHB1dHNfYmlrcl9ybC9zMnZfZHFuIg0Kc3ViX2Rpcl8xIDwtICJQUE9fVHJhaW5lZF9Pbl9BZmZvbHRlcm4vZ3JhcGhzIg0Kc3ViX2Rpcl8yIDwtICJQUE9fVHJhaW5lZF9Pbl9CaXJjaHBsYXR6L2dyYXBocyINCnN1Yl9kaXJfMyA8LSAiTGluZWFyX1Byb2dyYW1taW5nL2dyYXBocyINCnN1Yl9kaXJfNCA8LSAiUzJWX0RRTl9UcmFpbmVkX09uX0JpcmNocGxhdHovZ3JhcGhzIg0KDQojIFJlYWQgaW1hZ2VzIGFuZCBhZGQgdGl0bGVzDQpwbHQxIDwtIGltYWdlX3JlYWQoZmlsZS5wYXRoKGJhc2VfZGlyLCBzdWJfZGlyXzEsICJhZmZvbHRlcm5fYmlrZV9uZXR3b3JrLnBuZyIpKSAlPiUNCiAgaW1hZ2VfZ2dwbG90KCkgKw0KICBnZ3RpdGxlKCJQUE8gKFRyYWluZWQgb24gQWZmb2x0ZXJuKSIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGhqdXN0ID0gMC41KSkNCg0KcGx0MiA8LSBpbWFnZV9yZWFkKGZpbGUucGF0aChiYXNlX2Rpciwgc3ViX2Rpcl8yLCAiYWZmb2x0ZXJuX2Jpa2VfbmV0d29yay5wbmciKSkgJT4lDQogIGltYWdlX2dncGxvdCgpICsNCiAgZ2d0aXRsZSgiUFBPIChUcmFpbmVkIG9uIEJpcmNocGxhdHopIikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgaGp1c3QgPSAwLjUpKQ0KDQpwbHQzIDwtIGltYWdlX3JlYWQoZmlsZS5wYXRoKGJhc2VfZGlyLCBzdWJfZGlyXzMsICJhZmZvbHRlcm5fYmlrZV9uZXR3b3JrLnBuZyIpKSAlPiUNCiAgaW1hZ2VfZ2dwbG90KCkgKw0KICBnZ3RpdGxlKCJMaW5lYXIgUHJvZ3JhbW1pbmciKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBoanVzdCA9IDAuNSkpDQoNCnBsdDQgPC0gaW1hZ2VfcmVhZChmaWxlLnBhdGgoYmFzZV9kaXIsIHN1Yl9kaXJfNCwgImFmZm9sdGVybl9iaWtlX25ldHdvcmsucG5nIikpICU+JQ0KICBpbWFnZV9nZ3Bsb3QoKSArDQogIGdndGl0bGUoIlMyVi1EUU4gKFRyYWluZWQgb24gQmlyY2hwbGF0eikiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBoanVzdCA9IDAuNSkpDQoNCiMgQXJyYW5nZSB3aXRoIHBhdGNod29yayBhbmQgYWRkIG92ZXJhbGwgY2FwdGlvbg0KKHBsdDEgfCBwbHQyKSAvIChwbHQzIHwgcGx0NCkgKw0KICBwbG90X2Fubm90YXRpb24oDQogICAgdGl0bGUgPSAiRmlndXJlIDk6IEFmZm9sdGVybiBCaWtlIE5ldHdvcmtzIGJ5IEFsZ29yaXRobSIsDQogICAgdGhlbWUgPSB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUpKQ0KICApDQoNCmBgYA0KDQpUaGUgZmluYWwgYmlrZSBuZXR3b3JrIGZvciBBZmZvbHRlcm4gcmV2ZWFscyB0aGF0IHRoZSBsaW5lYXIgcHJvZ3JhbW1pbmcgbWV0aG9kIHByb2R1Y2VzIHRoZSBtb3N0IGVsYWJvcmF0ZSBhbmQgd2VsbOKAkWNvbm5lY3RlZCBuZXR3b3JrLg0KVGhlIG5ldHdvcmtzIGdlbmVyYXRlZCBieSBQUE8oQWZmb2x0ZXJuKSwgUFBPKEJpcmNocGxhdHopIGFuZCBTMlbigJFEUU4oQmlyY2hwbGF0eikgZXhoaWJpdCBzaW1pbGFyIGNvbm5lY3Rpdml0eSBhbmQgY29tcGxleGl0eSwgYWxsIHJhbmtpbmcgbG93ZXIgdGhhbiB0aGUgTFAgcmVzdWx0cy4NCg0KYGBge3J9DQoNCnBsdDEgPC0gaW1hZ2VfcmVhZChmaWxlLnBhdGgoYmFzZV9kaXIsIHN1Yl9kaXJfMSwgImJpcmNocGxhdHpfYmlrZV9uZXR3b3JrLnBuZyIpKSAlPiUNCiAgaW1hZ2VfZ2dwbG90KCkgKw0KICBnZ3RpdGxlKCJQUE8gKFRyYWluZWQgb24gQWZmb2x0ZXJuKSIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGhqdXN0ID0gMC41KSkNCg0KcGx0MiA8LSBpbWFnZV9yZWFkKGZpbGUucGF0aChiYXNlX2Rpciwgc3ViX2Rpcl8yLCAiYmlyY2hwbGF0el9iaWtlX25ldHdvcmsucG5nIikpICU+JQ0KICBpbWFnZV9nZ3Bsb3QoKSArDQogIGdndGl0bGUoIlBQTyAoVHJhaW5lZCBvbiBCaXJjaHBsYXR6KSIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGhqdXN0ID0gMC41KSkNCg0KcGx0MyA8LSBpbWFnZV9yZWFkKGZpbGUucGF0aChiYXNlX2Rpciwgc3ViX2Rpcl8zLCAiYmlyY2hwbGF0el9iaWtlX25ldHdvcmsucG5nIikpICU+JQ0KICBpbWFnZV9nZ3Bsb3QoKSArDQogIGdndGl0bGUoIkxpbmVhciBQcm9ncmFtbWluZyIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGhqdXN0ID0gMC41KSkNCg0KcGx0NCA8LSBpbWFnZV9yZWFkKGZpbGUucGF0aChiYXNlX2Rpciwgc3ViX2Rpcl80LCAiYmlyY2hwbGF0el9iaWtlX25ldHdvcmsucG5nIikpICU+JQ0KICBpbWFnZV9nZ3Bsb3QoKSArDQogIGdndGl0bGUoIlMyVi1EUU4gKFRyYWluZWQgb24gQmlyY2hwbGF0eikiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBoanVzdCA9IDAuNSkpDQoNCg0KKHBsdDEgfCBwbHQyKSAvIChwbHQzIHwgcGx0NCkgKw0KICBwbG90X2Fubm90YXRpb24oDQogICAgdGl0bGUgPSAiRmlndXJlIDEwOkJpcmNocGxhdHogQmlrZSBOZXR3b3JrcyBieSBBbGdvcml0aG0iLA0KICAgIHRoZW1lID0gdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41KSkNCiAgKQ0KDQpgYGANCg0KU2ltaWxhciB0byBBZmZvbHRlcm4gbmV0d29yaywgdGhlIGZpbmFsIGJpa2UgbmV0d29yayBmb3IgQmlyY2hwbGF0eiByZXZlYWxzIHRoYXQgdGhlIGxpbmVhciBwcm9ncmFtbWluZyBtZXRob2QgcHJvZHVjZXMgdGhlIG1vc3QgZWxhYm9yYXRlIGFuZCB3ZWxs4oCRY29ubmVjdGVkIG5ldHdvcmssIGZvbGxvd2VkIGJ5IFMyVuKAkURRTiAuVGhlIG5ldHdvcmtzIGdlbmVyYXRlZCBieSBQUE8gdHJhaW5lZCBvbiBBZmZvbHRlcm4gYW5kIFBQTyB0cmFpbmVkIG9uIEJpcmNocGxhdHogZXhoaWJpdCBzaW1pbGFyIGNvbm5lY3Rpdml0eSBhbmQgY29tcGxleGl0eSwgYm90aCByYW5raW5nIGxvd2VyIHRoYW4gdGhlIExQIGFuZCBTMlbigJFEUU4gcmVzdWx0cy4NCg0KYGBge3J9DQoNCnBsdDEgPC0gaW1hZ2VfcmVhZChmaWxlLnBhdGgoYmFzZV9kaXIsIHN1Yl9kaXJfMSwgImFmZm9sdGVybl9jYXJfbmV0d29yay5wbmciKSkgJT4lDQogIGltYWdlX2dncGxvdCgpICsNCiAgZ2d0aXRsZSgiUFBPIChUcmFpbmVkIG9uIEFmZm9sdGVybikiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBoanVzdCA9IDAuNSkpDQoNCnBsdDIgPC0gaW1hZ2VfcmVhZChmaWxlLnBhdGgoYmFzZV9kaXIsIHN1Yl9kaXJfMiwgImFmZm9sdGVybl9jYXJfbmV0d29yay5wbmciKSkgJT4lDQogIGltYWdlX2dncGxvdCgpICsNCiAgZ2d0aXRsZSgiUFBPIChUcmFpbmVkIG9uIEJpcmNocGxhdHopIikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgaGp1c3QgPSAwLjUpKQ0KDQpwbHQzIDwtIGltYWdlX3JlYWQoZmlsZS5wYXRoKGJhc2VfZGlyLCBzdWJfZGlyXzMsICJhZmZvbHRlcm5fY2FyX25ldHdvcmsucG5nIikpICU+JQ0KICBpbWFnZV9nZ3Bsb3QoKSArDQogIGdndGl0bGUoIkxpbmVhciBQcm9ncmFtbWluZyIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGhqdXN0ID0gMC41KSkNCg0KcGx0NCA8LSBpbWFnZV9yZWFkKGZpbGUucGF0aChiYXNlX2Rpciwgc3ViX2Rpcl80LCAiYWZmb2x0ZXJuX2Nhcl9uZXR3b3JrLnBuZyIpKSAlPiUNCiAgaW1hZ2VfZ2dwbG90KCkgKw0KICBnZ3RpdGxlKCJTMlYtRFFOIChUcmFpbmVkIG9uIEJpcmNocGxhdHopIikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgaGp1c3QgPSAwLjUpKQ0KDQoNCihwbHQxIHwgcGx0MikgLyAocGx0MyB8IHBsdDQpICsNCiAgcGxvdF9hbm5vdGF0aW9uKA0KICAgIHRpdGxlID0gIkZpZ3VyZSAxMTogQWZmb2x0ZXJuIENhciBOZXR3b3JrcyBieSBBbGdvcml0aG0iLA0KICAgIHRoZW1lID0gdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41KSkNCiAgKQ0KYGBgDQoNCmBgYHtyfQ0KDQpwbHQxIDwtIGltYWdlX3JlYWQoZmlsZS5wYXRoKGJhc2VfZGlyLCBzdWJfZGlyXzEsICJiaXJjaHBsYXR6X2Nhcl9uZXR3b3JrLnBuZyIpKSAlPiUNCiAgaW1hZ2VfZ2dwbG90KCkgKw0KICBnZ3RpdGxlKCJQUE8gKFRyYWluZWQgb24gQWZmb2x0ZXJuKSIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGhqdXN0ID0gMC41KSkNCg0KcGx0MiA8LSBpbWFnZV9yZWFkKGZpbGUucGF0aChiYXNlX2Rpciwgc3ViX2Rpcl8yLCAiYmlyY2hwbGF0el9jYXJfbmV0d29yay5wbmciKSkgJT4lDQogIGltYWdlX2dncGxvdCgpICsNCiAgZ2d0aXRsZSgiUFBPIChUcmFpbmVkIG9uIEJpcmNocGxhdHopIikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgaGp1c3QgPSAwLjUpKQ0KDQpwbHQzIDwtIGltYWdlX3JlYWQoZmlsZS5wYXRoKGJhc2VfZGlyLCBzdWJfZGlyXzMsICJiaXJjaHBsYXR6X2Nhcl9uZXR3b3JrLnBuZyIpKSAlPiUNCiAgaW1hZ2VfZ2dwbG90KCkgKw0KICBnZ3RpdGxlKCJMaW5lYXIgUHJvZ3JhbW1pbmciKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBoanVzdCA9IDAuNSkpDQoNCnBsdDQgPC0gaW1hZ2VfcmVhZChmaWxlLnBhdGgoYmFzZV9kaXIsIHN1Yl9kaXJfNCwgImJpcmNocGxhdHpfY2FyX25ldHdvcmsucG5nIikpICU+JQ0KICBpbWFnZV9nZ3Bsb3QoKSArDQogIGdndGl0bGUoIlMyVi1EUU4gKFRyYWluZWQgb24gQmlyY2hwbGF0eikiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBoanVzdCA9IDAuNSkpDQoNCg0KKHBsdDEgfCBwbHQyKSAvIChwbHQzIHwgcGx0NCkgKw0KICBwbG90X2Fubm90YXRpb24oDQogICAgdGl0bGUgPSAiRmlndXJlIDEyOiBCaXJjaHBsYXR6IENhciBOZXR3b3JrcyBieSBBbGdvcml0aG0iLA0KICAgIHRoZW1lID0gdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41KSkNCiAgKQ0KYGBgDQoNCiMjIyMgKioqUkVGRVJFTkNFUyoqKg0KDQpbMV0g4oCcVGFtaW5nIFRyYWZmaWMs4oCdIEluc3RpdHV0ZSBmb3IgVHJhbnNwb3J0YXRpb24gYW5kIERldmVsb3BtZW50IFBvbGljeSAtIFByb21vdGluZyBzdXN0YWluYWJsZSBhbmQgZXF1aXRhYmxlIHRyYW5zcG9ydGF0aW9uIHdvcmxkd2lkZSwgTWFyLiAyMiwgMjAyMS4NCjxodHRwczovL2l0ZHAub3JnL3B1YmxpY2F0aW9uL3RhbWluZy10cmFmZmljPg0KDQpbMl0gSy4NCkh5bWVsLCDigJxJZiB5b3UgYnVpbGQgaXQsIHRoZXkgd2lsbCBkcml2ZTogTWVhc3VyaW5nIGluZHVjZWQgZGVtYW5kIGZvciB2ZWhpY2xlIHRyYXZlbCBpbiB1cmJhbiBhcmVhcyzigJ0gKlRyYW5zcG9ydCBQb2xpY3kqLCB2b2wuDQo3NiwgcHAuIDU34oCTNjYsIEFwci4gMjAxOSwgZG9pOiA8aHR0cHM6Ly9kb2kub3JnLzEwLjEwMTYvai50cmFucG9sLjIwMTguMTIuMDA2Lj4NCg0KWzNdIOKAnFJvYWQgd2lkZW5pbmcgYW5kIGluZHVjZWQgZGVtYW5kIGV4cGxhaW5lZCBcfCBUb21Ub20gTmV3c3Jvb20s4oCdIFRvbVRvbSwgMjAyNC4NCjxodHRwczovL3d3dy50b210b20uY29tL25ld3Nyb29tL2V4cGxhaW5lcnMtYW5kLWluc2lnaHRzL2luZHVjZWQtZGVtYW5kLWV4cGxhaW5lZC8+DQoNCls0XSBCYWxsbywgTC4sIE0uIFJhdWJhbCBhbmQgSy5XLg0KQXhoYXVzZW4gKDIwMjQpDQpbRGVzaWduaW5nIGFuIEUtQmlrZSBDaXR5OiBBbiBhdXRvbWF0ZWQgcHJvY2VzcyBmb3IgbmV0d29yay13aWRlIG11bHRpbW9kYWwgcm9hZCBzcGFjZSByZWFsbG9jYXRpb25dKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDE2L2ouamNtci4yMDI0LjEwMDA0OCksDQoqSm91cm5hbCBvZiBDeWNsaW5nIGFuZCBNaWNyb21vYmlsaXR5IFJlc2VhcmNoKiwgKioyKiosIDEwMDA0OC4NCg0KWzVdIFdpZWRlbWFubiwgTi4sIE7DtmJlbCwgQy4sIE1hcnRpbiwgSC4sIEJhbGxvLCBMLiwgJiBSYXViYWwsIE0uDQooMjAyNCkuDQpCaWtlIG5ldHdvcmsgcGxhbm5pbmcgaW4gbGltaXRlZCB1cmJhbiBzcGFjZS4NCmFyWGl2IHByZXByaW50IGFyWGl2OjI0MDUuMDE3NzAuDQo=