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 from
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).
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.
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
| 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-:
- The lane graph and the OD matrix are loaded for specific
instances(cities/districts).
- The BikeNetworkEnv is loaded with lane graph and OD matrix.
- 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.
- The model is trained for 10000 timesteps.
- 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:
Observes the current state (edge distances, gradients, bike
flags).
Samples an action according to the policy’s probabilities (or
chooses the action with highest probability during inference).
Executes the action, receives a reward, and moves to the next
state.
Stores (state, action, reward, next_state, done) in a
buffer.
After the agent collects a batch of experiences, PPO updates the
policy by:
Computing the advantage which determines how much better an
action was than the average expect turn.
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 report 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
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.
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 report 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)
| 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.
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=