Introduction
For this assignment, I chose to analyze the 311 “Alley Pothole
Complaints” across the city of Chicago in correlation with forced-entry
burglaries. I selected this variable because Pennsylvanians deal with a
fair share of potholes, so I am curious about the pothole issues in
Chicago and whether failing road infrastructure, designated in the form
of 311 complaints,has any relationship to property crime.
I expect potholes to show spatial clustering similar to burglaries,
concentrated in areas with lower socioeconomic status. However, the
predictive relationship may be modest once we control for demographic
and housing characteristics, as both phenomena likely share common
underlying causes (poverty, tenure patterns, age structure) rather than
having a direct causal link.
Load and Explore Data
This chunk loads the spatial boundaries needed for analysis. The
police districts will be used for spatial cross-validation
(Leave-One-Group-Out), while the beats provide finer administrative
units and the Chicago boundary defines our study area.
Then I load the 311 pothole data, ensuring the same ESPG as the
Chicago Boundary, a local coordnate reference system of Chicago,
Illinois.
Exploratory Spatial Analysis
The following visualizations explore the spatial distribution of
pothole complaints across Chicago. The point map shows the raw locations
of all 311 calls, while the kernel density estimation reveals underlying
intensity patterns by smoothing point locations into a continuous
surface.

Map 1 (left) reveals the 70,046 pothole complaints are clustered
rather than evenly distributed across Chicago. Map 2 (right) affirms
this pattern: the highest intensity appears in the north/northwestern
area and extends southwest along major corridors.
This distribution may reflect several factors: (1) differing road
quality across neighborhoods, (2) varying neighborhoods clusters of
people willing to complain, or (3) actual differences in infrastructure
maintenance priorities.
Fishnet Grid
For evenly dispersed analysis, creating a fishnet grid assigns a
square area of a certain height and length within a boundary and each
square area is of equal proportion. This acts as a container for
summarizing and analyzing data within each cell, comparing spatial data
uniformly. Our fishnet will be 500m x 500m.
Create Fishnet
# Create a 500m x 500m grid over Chicago
fishnet <- st_make_grid(
chicagoBound,
cellsize = 500,
square = TRUE
) %>%
st_sf() %>%
mutate(uniqueID = row_number())
# Properly clip to Chicago boundary using intersection
fishnet <- st_intersection(fishnet, chicagoBound) %>%
mutate(uniqueID = row_number()) # Re-number after clipping
cat("✓ Created fishnet grid\n")
## ✓ Created fishnet grid
cat(" - Number of cells:", nrow(fishnet), "\n")
## - Number of cells: 2458
By using spatial intersection to clip the grid to Chicago’s boundary,
I exclude cells that fall outside the city limits. This ensures that our
analysis only includes areas where both 311 calls and burglaries can
legitimately occur, avoiding edge effects from partial cells.
Aggregate Burglaries and Potholes to Grid
This chunk performs a spatial join of point-level burglary incidents
to the 500m x 500m fishnet.
burglaries_fish <- st_join(burglaries, fishnet, join = st_within)%>%
st_drop_geometry() %>%
group_by(uniqueID) %>%
summarize(countBurglaries = n())
fishnet <- fishnet %>%
left_join(burglaries_fish, by = "uniqueID") %>%
mutate(countBurglaries = replace_na(countBurglaries,0))
# aggregate stats
cat("\nBurglary count distribution:\n")
Burglary count distribution:
summary(fishnet$countBurglaries)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.000 0.000 2.000 3.035 5.000 40.000
cat("\nCells with zero burglaries:",
sum(fishnet$countBurglaries == 0),
"/", nrow(fishnet),
"(", round(100 * sum(fishnet$countBurglaries == 0) / nrow(fishnet), 1), "%)\n")
Cells with zero burglaries: 788 / 2458 ( 32.1 %)
About 32.1% of cells have zero burglaries. This zero-inflation is
typical of crime data and motivates our use of count regression models
(Poisson or Negative Binomial) rather than ordinary least squares. The
maximum count is 40 burglaries in a single cell, indicating spatial
heterogeneity in crime risk.
Now I aggregate pothole 311 calls to the same grid:
# Aggregate potholes to fishnet
potholes_fish <- st_join(holes311, fishnet, join = st_within) %>%
st_drop_geometry() %>%
group_by(uniqueID) %>%
summarize(countPotholes = n())
# Join back to fishnet
fishnet <- fishnet %>%
left_join(potholes_fish, by = "uniqueID") %>%
mutate(countPotholes = replace_na(countPotholes, 0))
cat("\nPothole count distribution:\n")
##
## Pothole count distribution:
summary(fishnet$countPotholes)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0 2.0 15.0 28.4 40.0 265.0
cat("\nCells with zero potholes:",
sum(fishnet$countPotholes == 0),
"/", nrow(fishnet),
"(", round(100 * sum(fishnet$countPotholes == 0) / nrow(fishnet), 1), "%)\n")
##
## Cells with zero potholes: 486 / 2458 ( 19.8 %)
Visual Grid-Level Counts
# Map burglary counts
p1 <- ggplot() +
geom_sf(data = fishnet, aes(fill = countBurglaries), color = NA) +
scale_fill_viridis_c(
name = "Count",
option = "plasma",
trans = "sqrt",
breaks = c(0, 1, 5, 10, 20)
) +
labs(title = "Burglary Counts per Cell") +
theme_crime()
# Map pothole counts
p2 <- ggplot() +
geom_sf(data = fishnet, aes(fill = countPotholes), color = NA) +
scale_fill_viridis_c(
name = "Count",
option = "plasma",
trans = "sqrt",
breaks = c(0, 20, 50, 100, 200)
) +
labs(title = "Pothole Complaint Counts per Cell") +
theme_crime()
p1 + p2 +
plot_annotation(
title = "Spatial Distribution of Burglaries and Potholes at 500m Grid"
)

Both burglaries and potholes show spatial clustering, but with
different patterns. Burglaries concentrate on the South and West sides
and pothole reports cluster more heavily in the north and central
corridors, similar to the KDE map from earlier.This suggests that
burglaries and 311 pothole complaints are not directly correlated.
Spatial Features Engineering
To understand proximity effects and spatial structure, I will
engineer three types of features: nearest-neighbor distances, hot-spot
identification via Local Moran’s I, and distance to identified hot
spots.
Calculate K-Nearest Neighbor Features
# Getting Coordinates
fishnet_coords <- st_coordinates(st_centroid(fishnet))
## Warning: st_centroid assumes attributes are constant over geometries
pothole_coords <- st_coordinates(holes311)
# Calculate k nearest neighbors (k=3 for average distance to 3 nearest potholes)
nn_result <- get.knnx(pothole_coords, fishnet_coords, k = 3)
# Add mean distance to fishnet
fishnet <- fishnet %>%
mutate(holes311.nn = rowMeans(nn_result$nn.dist))
cat("✓ Calculated nearest neighbor distances\n")
## ✓ Calculated nearest neighbor distances
cat(" Summary of mean distance to 3 nearest potholes (feet):\n")
## Summary of mean distance to 3 nearest potholes (feet):
summary(fishnet$holes311.nn)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 4.844 62.515 114.630 216.389 236.654 1845.254
Using the average distance to the three nearest potholes (rather than
just the single nearest) provides a more robust measure of local pothole
intensity while reducing sensitivity to isolated outliers.
Calculate Kernel Density Estimation
Now I compute a kernel density surface for burglaries to use as a
baseline comparison model:
# Convert burglaries to point pattern object
burglaries_ppp <- as.ppp(
st_coordinates(burglaries),
W = as.owin(st_bbox(chicagoBound))
)
# Calculate KDE with 1km bandwidth
kde_burglaries <- density.ppp(
burglaries_ppp,
sigma = 1000, # 1km = ~3280 feet
edge = TRUE # Edge correction
)
# Convert to terra raster
kde_raster <- rast(kde_burglaries)
# Extract KDE values to fishnet cells
fishnet <- fishnet %>%
mutate(
kde_value = terra::extract(
kde_raster,
vect(fishnet),
fun = mean,
na.rm = TRUE
)[, 2]
)
cat("✓ Calculated KDE values\n")
## ✓ Calculated KDE values
Visualize Hotpots
Will create a choropleth map highlights HOTHTO LOW LOW
# Visualize hot spots
ggplot() +
geom_sf(
data = fishnet,
aes(fill = moran_class),
color = NA
) +
scale_fill_manual(
values = c(
"High-High" = "#d7191c",
"High-Low" = "#fdae61",
"Low-High" = "#abd9e9",
"Low-Low" = "#2c7bb6",
"Not Significant" = "gray90"
),
name = "Cluster Type"
) +
labs(
title = "Local Moran's I: Pothole Hot Spots",
subtitle = "High-High clusters represent statistically significant hot spots of infrastructure complaints"
) +
theme_crime()

The LISA map reveals distinct pothole hot spots in the northern area
and parts of the northwest. These High-High clusters indicate areas
where both the cell and its neighbors have elevated pothole counts,
representing persistent infrastructure maintenance challenges. The South
Side show as Low-Low or Not Significant for potholes.
Calculate the Distance to the Hotspots
# Get centroids of "High-High" cells (hot spots)
hotspots <- fishnet %>%
filter(moran_class == "High-High") %>%
st_centroid()
# Calculate distance from each cell to nearest hot spot
if (nrow(hotspots) > 0) {
fishnet <- fishnet %>%
mutate(
dist_to_hotspot = as.numeric(
st_distance(st_centroid(fishnet), hotspots %>% st_union())
)
)
cat("✓ Calculated distance to abandoned car hot spots\n")
cat(" - Number of hot spot cells:", nrow(hotspots), "\n")
} else {
fishnet <- fishnet %>%
mutate(dist_to_hotspot = 0)
cat("⚠ No significant hot spots found\n")
}
✓ Calculated distance to abandoned car hot spots
- Number of hot spot cells: 333
Count Regression Models
With our spatial features and controls in place, I now fit count
regression models to predict burglary risk from pothole patterns and
neighborhood characteristics
Prepare Modeling Frame
# Create clean modeling dataset
# Handle duplicate columns from joins (rename .y versions to clean names)
# Only rename if .y columns exist
if ("pct_black.y" %in% names(fishnet)) {
fishnet <- fishnet %>%
rename(
pct_black = pct_black.y,
pct_hisp = pct_hisp.y,
pct_poverty = pct_poverty.y,
pct_owner = pct_owner.y,
med_income = med_income.y,
med_house_age = med_house_age.y
) %>%
# Remove .x versions if they exist
select(-any_of(c("pct_black.x", "pct_hisp.x", "pct_poverty.x",
"pct_owner.x", "med_income.x", "med_house_age.x")))
}
# Create modeling frame
fishnet_model <- fishnet %>%
st_drop_geometry() %>%
select(
uniqueID, District,
countBurglaries,
countPotholes, holes311.nn, dist_to_hotspot,
pct_black, pct_hisp, pct_poverty, pct_owner,
med_income, med_house_age
) %>%
mutate(
# Scale percentages to proportions (0-1 scale)
across(c(pct_black, pct_hisp, pct_poverty, pct_owner), ~ .x / 100),
# Log-transform income to reduce skew
log_income = log(pmax(med_income, 1))
) %>%
drop_na()
cat("✓ Prepared modeling dataset\n")
cat(" Observations:", nrow(fishnet_model), "\n")
cat(" Variables:", ncol(fishnet_model), "\n")
Fit Poisson Regression
model_poisson <- glm(
countBurglaries ~ countPotholes + holes311.nn + dist_to_hotspot +
pct_poverty + pct_owner + log_income + med_house_age +
pct_black + pct_hisp,
data = fishnet_model,
family = poisson(link = "log")
)
summary(model_poisson)
##
## Call:
## glm(formula = countBurglaries ~ countPotholes + holes311.nn +
## dist_to_hotspot + pct_poverty + pct_owner + log_income +
## med_house_age + pct_black + pct_hisp, family = poisson(link = "log"),
## data = fishnet_model)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -5.4464919855 1.1839580502 -4.600 0.00000422 ***
## countPotholes 0.0031887820 0.0002528470 12.612 < 0.0000000000000002 ***
## holes311.nn -0.0043541969 0.0001323631 -32.896 < 0.0000000000000002 ***
## dist_to_hotspot -0.0000001236 0.0000025339 -0.049 0.9611
## pct_poverty 0.0348708559 0.1152633152 0.303 0.7622
## pct_owner -1.2662212555 0.0592704672 -21.363 < 0.0000000000000002 ***
## log_income 0.4210284282 0.0372017577 11.317 < 0.0000000000000002 ***
## med_house_age 0.0012957838 0.0006107403 2.122 0.0339 *
## pct_black 1.2307797075 0.0439432565 28.008 < 0.0000000000000002 ***
## pct_hisp 0.5757431484 0.0469122769 12.273 < 0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for poisson family taken to be 1)
##
## Null deviance: 15746 on 4239 degrees of freedom
## Residual deviance: 10329 on 4230 degrees of freedom
## AIC: 21403
##
## Number of Fisher Scoring iterations: 5
Check for Overdispersion
Poisson regression assumes mean = variance. Real count data often
violates this (overdispersion).
# Calculate dispersion parameter
dispersion <- sum(residuals(model_poisson, type = "pearson")^2) /
model_poisson$df.residual
cat("Dispersion test:\n")
## Dispersion test:
cat(" Dispersion parameter:", round(dispersion, 2), "\n")
## Dispersion parameter: 2.66
cat(" Rule of thumb: >1.5 suggests overdispersion\n")
## Rule of thumb: >1.5 suggests overdispersion
if (dispersion > 1.5) {
cat(" ⚠ Overdispersion detected! Negative Binomial model recommended.\n")
} else {
cat(" ✓ Dispersion acceptable for Poisson model.\n")
}
## ⚠ Overdispersion detected! Negative Binomial model recommended.
The dispersion parameter is 2.66, well above the 1.5 threshold. This
indicates substantial overdispersion—the variance in burglary counts is
much larger than the mean would suggest under Poisson assumptions. This
overdispersion can arise from unobserved heterogeneity, spatial
clustering, or other sources of extra-Poisson variation. The Negative
Binomial model adds a dispersion parameter to accommodate this.
Fit Negative Binomial Regression
# Fit Negative Binomial model
model_nb <- glm.nb(
countBurglaries ~ countPotholes + holes311.nn + dist_to_hotspot +
pct_poverty + pct_owner + log_income + med_house_age +
pct_black + pct_hisp,
data = fishnet_model
)
# Summary
summary(model_nb)
##
## Call:
## glm.nb(formula = countBurglaries ~ countPotholes + holes311.nn +
## dist_to_hotspot + pct_poverty + pct_owner + log_income +
## med_house_age + pct_black + pct_hisp, data = fishnet_model,
## init.theta = 2.694873504, link = log)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -7.692191191 1.940882180 -3.963 0.000073937897784 ***
## countPotholes 0.002969053 0.000436792 6.797 0.000000000010652 ***
## holes311.nn -0.004700638 0.000189326 -24.828 < 0.0000000000000002 ***
## dist_to_hotspot -0.000011495 0.000004056 -2.834 0.0046 **
## pct_poverty 0.047496256 0.194622093 0.244 0.8072
## pct_owner -1.248623750 0.095199805 -13.116 < 0.0000000000000002 ***
## log_income 0.435801915 0.061008178 7.143 0.000000000000911 ***
## med_house_age 0.002368163 0.001001381 2.365 0.0180 *
## pct_black 1.352116075 0.069437760 19.472 < 0.0000000000000002 ***
## pct_hisp 0.644614812 0.073419728 8.780 < 0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for Negative Binomial(2.6949) family taken to be 1)
##
## Null deviance: 7189.4 on 4239 degrees of freedom
## Residual deviance: 4626.6 on 4230 degrees of freedom
## AIC: 19020
##
## Number of Fisher Scoring iterations: 1
##
##
## Theta: 2.695
## Std. Err.: 0.109
##
## 2 x log-likelihood: -18998.163
# Compare AIC (lower is better)
cat("\nModel Comparison:\n")
##
## Model Comparison:
cat("Poisson AIC:", round(AIC(model_poisson), 1), "\n")
## Poisson AIC: 21403.2
cat("Negative Binomial AIC:", round(AIC(model_nb), 1), "\n")
## Negative Binomial AIC: 19020.2
# Select best model for predictions
final_model <- model_nb
cat("\n✓ Selected Negative Binomial as final model\n")
##
## ✓ Selected Negative Binomial as final model
The Negative Binomial model has a substantially lower AIC 19020.2
indicating this is a better model fit.
Spatial Cross-Validation (LOGO-CV)
To assess model generalizability, I implement Leave-One-Group-Out
(LOGO) cross-validation using police districts as spatial folds. This
tests whether the model trained on most of the city can accurately
predict burglaries in held-out districts, simulating a real-world
deployment scenario where we might want to forecast crime in a new
area.
districts <- unique(fishnet_model$District)
cv_results <- data.frame()
for (i in seq_along(districts)) {
test_district <- districts[i]
# Split data spatially
train_data <- filter(fishnet_model, District != test_district)
test_data <- filter(fishnet_model, District == test_district)
# Fit model on training folds
m_cv <- glm.nb(
countBurglaries ~ countPotholes + holes311.nn + dist_to_hotspot +
pct_poverty + pct_owner + log_income + med_house_age +
pct_black + pct_hisp,
data = train_data
)
# Predict on held-out district
predictions <- predict(m_cv, newdata = test_data, type = "response")
# Calculate errors
mae <- mean(abs(test_data$countBurglaries - predictions))
rmse <- sqrt(mean((test_data$countBurglaries - predictions)^2))
cv_results <- rbind(cv_results, data.frame(
District = test_district,
n_test = nrow(test_data),
mae = mae,
rmse = rmse
))
cat("Fold", i, "/", length(districts), "- District", test_district,
"- MAE:", round(mae, 2), "\n")
}
## Fold 1 / 22 - District 4 - MAE: 1.88
## Fold 2 / 22 - District 5 - MAE: 2.06
## Fold 3 / 22 - District 22 - MAE: 2.04
## Fold 4 / 22 - District 6 - MAE: 2.8
## Fold 5 / 22 - District 8 - MAE: 2.4
## Fold 6 / 22 - District 7 - MAE: 2.95
## Fold 7 / 22 - District 3 - MAE: 5.35
## Fold 8 / 22 - District 2 - MAE: 3.53
## Fold 9 / 22 - District 9 - MAE: 1.93
## Fold 10 / 22 - District 10 - MAE: 2.66
## Fold 11 / 22 - District 1 - MAE: 1.95
## Fold 12 / 22 - District 12 - MAE: 3.51
## Fold 13 / 22 - District 15 - MAE: 3.27
## Fold 14 / 22 - District 11 - MAE: 4.32
## Fold 15 / 22 - District 18 - MAE: 2.34
## Fold 16 / 22 - District 25 - MAE: 2.91
## Fold 17 / 22 - District 14 - MAE: 2.96
## Fold 18 / 22 - District 19 - MAE: 2.04
## Fold 19 / 22 - District 16 - MAE: 1.56
## Fold 20 / 22 - District 17 - MAE: 1.65
## Fold 21 / 22 - District 20 - MAE: 2.03
## Fold 22 / 22 - District 24 - MAE: 2.16
# Overall results
cat("\n✓ Cross-Validation Complete\n")
##
## ✓ Cross-Validation Complete
cat("Mean MAE:", round(mean(cv_results$mae), 2), "\n")
## Mean MAE: 2.65
cat("Mean RMSE:", round(mean(cv_results$rmse), 2), "\n")
## Mean RMSE: 3.5
# Show results
cv_results %>%
arrange(desc(mae)) %>%
kable(
digits = 2,
caption = "LOGO CV Results by District (Negative Binomial Model)",
col.names = c("District", "N Test Cells", "MAE", "RMSE")
) %>%
kable_styling(bootstrap_options = c("striped", "hover"))
LOGO CV Results by District (Negative Binomial Model)
|
District
|
N Test Cells
|
MAE
|
RMSE
|
|
3
|
120
|
5.35
|
6.86
|
|
11
|
124
|
4.32
|
5.08
|
|
2
|
181
|
3.53
|
4.24
|
|
12
|
203
|
3.51
|
4.69
|
|
15
|
77
|
3.27
|
3.78
|
|
14
|
164
|
2.96
|
4.19
|
|
7
|
149
|
2.95
|
3.65
|
|
25
|
228
|
2.91
|
3.80
|
|
6
|
154
|
2.80
|
4.10
|
|
10
|
175
|
2.66
|
3.37
|
|
8
|
421
|
2.40
|
3.65
|
|
18
|
95
|
2.34
|
3.74
|
|
24
|
111
|
2.16
|
2.71
|
|
5
|
203
|
2.06
|
2.71
|
|
19
|
234
|
2.04
|
2.54
|
|
22
|
229
|
2.04
|
2.48
|
|
20
|
79
|
2.03
|
2.46
|
|
1
|
60
|
1.95
|
2.40
|
|
9
|
291
|
1.93
|
2.53
|
|
4
|
428
|
1.88
|
3.88
|
|
17
|
214
|
1.65
|
2.22
|
|
16
|
300
|
1.56
|
1.90
|
The hardest-to-predict districts are 3 (MAE: 5.35) and 11 (MAE:
4.32). These districts likely have unique characteristics not captured
by the citywide model—perhaps different burglary-pothole relationships,
distinct demographics, or reporting patterns.
The mean MAE of 2.65 burglaries per cell indicates that, on average,
our predictions are off by about 3 incidents per 500m cell. Given that
median burglary count is 3, this represents moderate predictive
accuracy.
Model Predictions and Comparison
Generate Final Predictions
I now refit the model on all 2017 data to generate final predictions
for comparison with a simple KDE baseline.
# Fit final model on all data (already done as 'final_model')
# Add NB predictions back to fishnet
fishnet <- fishnet %>%
mutate(
prediction_nb = predict(final_model, fishnet_model, type = "response")[match(uniqueID, fishnet_model$uniqueID)]
)
# Normalize KDE to same scale as counts
kde_sum <- sum(fishnet$kde_value, na.rm = TRUE)
count_sum <- sum(fishnet$countBurglaries, na.rm = TRUE)
fishnet <- fishnet %>%
mutate(
prediction_kde = (kde_value / kde_sum) * count_sum
)
cat("✓ Generated predictions\n")
## ✓ Generated predictions
cat(" Prediction range (NB):",
round(min(fishnet$prediction_nb, na.rm=TRUE), 2), "to",
round(max(fishnet$prediction_nb, na.rm=TRUE), 2), "\n")
## Prediction range (NB): 0 to 12.95
Visualize Predictions vs. Actuals
# Create three maps
p1 <- ggplot() +
geom_sf(data = fishnet, aes(fill = countBurglaries), color = NA) +
scale_fill_viridis_c(name = "Count", option = "plasma", limits = c(0, 15)) +
labs(title = "Actual Burglaries") +
theme_crime()
p2 <- ggplot() +
geom_sf(data = fishnet, aes(fill = prediction_nb), color = NA) +
scale_fill_viridis_c(name = "Predicted", option = "plasma", limits = c(0, 15)) +
labs(title = "Model Predictions (Neg. Binomial)") +
theme_crime()
p3 <- ggplot() +
geom_sf(data = fishnet, aes(fill = prediction_kde), color = NA) +
scale_fill_viridis_c(name = "Predicted", option = "plasma", limits = c(0, 15)) +
labs(title = "KDE Baseline Predictions") +
theme_crime()
p1 + p2 + p3 +
plot_annotation(
title = "Actual vs. Predicted Burglaries (2017)",
subtitle = "Does our complex model outperform simple KDE?"
)

The Negative Binomial model produces a smoother, more interpretable
risk surface that reflects both spatial patterns and neighborhood
structure. The KDE baseline closely follows historical hotspots but
offers less nuance, mainly following previous burglary report locations.
The model, by incorporating potholes and demographics, can identify
at-risk areas based on structural characteristics even in cells with low
historical burglary counts.
Error Maps
fishnet <- fishnet %>%
mutate(
error_nb = countBurglaries - prediction_nb,
error_kde = countBurglaries - prediction_kde
)
p1 <- ggplot() +
geom_sf(data = fishnet, aes(fill = error_nb), color = NA) +
scale_fill_gradient2(
name = "Error",
low = "#2166ac",
mid = "grey90",
high = "#b2182b",
midpoint = 0,
limits = c(-10, 10)
) +
labs(title = "NB Model Errors") +
theme_crime()
p2 <- ggplot() +
geom_sf(data = fishnet, aes(fill = error_kde), color = NA) +
scale_fill_gradient2(
name = "Error",
low = "#2166ac",
mid = "grey90",
high = "#b2182b",
midpoint = 0,
limits = c(-10, 10)
) +
labs(title = "KDE Baseline Errors") +
theme_crime()
p1 + p2 +
plot_annotation(
title = "Prediction Errors: Actual minus Predicted",
subtitle = "Red = underprediction, Blue = overprediction"
)

Both approaches struggle in the same high-crime corridors on the
South and West sides, where they tend to underpredict (red). The model
shows slightly more spatial structure in its errors—systematic
overprediction in some low-crime areas and underprediction in emerging
hotspots. This suggests room for improvement through additional spatial
features or interaction terms.
Summary Statistics and Findings
Key Findings
Technical Performance:
- Cross-validation MAE: 2.65 burglaries per cell
- Model vs. KDE: KDE performs slightly better in-sample (MAE 2.16
vs. 2.36), but the NB model is still useful because it lets us see how
different neighborhood factors relate to burglary patterns
Spatial Patterns:
- Burglaries are highly clustered in south and west areas
- Potholes cluster differently, more in north-central area
- ocal Moran’s I hot spots seem to line up more with areas of
disinvestment and infrastructure issues than with crime
specifically
Model Limitations:
- Overdispersion: Yes (dispersion = 2.66), addressed via Negative
Binomial
- Spatial autocorrelation in residuals: Likely present, as shown by
error patterns
- Cells with zero counts: 18.1% which is typical in crime data and
something the model has to account for
To Summarize:
Potholes and burglaries happen in the same places not because of
causation, but because both are symptoms of deeper neighborhood
issues.
CHALLENGE TASK: Temporal Validation (2018)
The final test of model quality is predicting the future. I use the
2017-trained model to forecast 2018 burglary patterns, assessing whether
spatial structure and pothole associations remain stable over time.
Load Data and Aggregate to the Fishnet
# Query filtered for forcible entry burglaries in 2018
burg2018 <- read_csv(here("labs/lab4/data/burglaries_2018.csv"))%>%
filter(!is.na(Latitude), !is.na(Longitude))%>%
filter(Description == "FORCIBLE ENTRY")%>%
st_as_sf(coords = c("Longitude", "Latitude"), crs = 4326) %>%
st_transform('ESRI:102271')
## Rows: 11747 Columns: 22
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (12): Case Number, Date, Block, IUCR, Primary Type, Description, Locatio...
## dbl (8): ID, Ward, Community Area, X Coordinate, Y Coordinate, Year, Latitu...
## lgl (2): Arrest, Domestic
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Aggregate to fishnet
burg2018_fish <- st_join(burg2018, fishnet, join = st_within)%>%
st_drop_geometry() %>%
group_by(uniqueID) %>%
summarize(countBurglaries2018 = n())
fishnet <- fishnet %>%
left_join(burg2018_fish, by = "uniqueID")
cat("✓ Loaded 2018 burglaries\n")
## ✓ Loaded 2018 burglaries
cat(" Total incidents:", nrow(burg2018), "\n")
## Total incidents: 6614
cat(" Date range:", min(burg2018$Date, na.rm=TRUE), "to", max(burg2018$Date, na.rm=TRUE), "\n")
## Date range: 01/01/2018 01:00:00 AM to 12/31/2018 12:30:00 PM
The grid acts as a consistent geographic frame across years. By
aggregating 2017 and 2018 burglaries to the same cells, I can directly
compare predictions vs. actual at matching locations. The potholes and
census features are from 2017, which tests whether these structural
predictors apply to the 2018 data.
Use 2017 Model to Predict 2018 Counts
# Make a column to store predicted data outcomes
fishnet <- fishnet %>%
mutate(
prediction_2018 = predict(final_model, newdata = fishnet_model, type = "response")[match(uniqueID, fishnet_model$uniqueID)]
)
cat("✓ Generated 2018 predictions from 2017 model\n")
## ✓ Generated 2018 predictions from 2017 model
Calculate Temporal Validation Metrics
#Fixing column name
fishnet <- fishnet %>%
mutate(countBurglaries2018 = replace_na(countBurglaries2018, 0))
# Calculate MAE and RMSE for 2018 predictions
temporal_metrics <- fishnet %>%
st_drop_geometry() %>%
filter(!is.na(countBurglaries2018), !is.na(prediction_2018)) %>%
summarize(
temporal_mae = mean(abs(countBurglaries2018 - prediction_2018)),
temporal_rmse = sqrt(mean((countBurglaries2018 - prediction_2018)^2))
)
cat("Temporal Validation (2018):\n")
## Temporal Validation (2018):
cat(" MAE:", round(temporal_metrics$temporal_mae, 2), "\n")
## MAE: 9.06
cat(" RMSE:", round(temporal_metrics$temporal_rmse, 2), "\n")
## RMSE: 15.69
Compare Spatial vs. Temporal Validation
# Compare LOGO CV (spatial) vs 2018 (temporal)
validation_comparison <- tibble(
Validation = c("Spatial (LOGO CV 2017)", "Temporal (2018)"),
MAE = c(mean(cv_results$mae), temporal_metrics$temporal_mae),
RMSE = c(mean(cv_results$rmse), temporal_metrics$temporal_rmse)
)
validation_comparison %>%
kable(
digits = 2,
caption = "Spatial vs. Temporal Validation Comparison"
) %>%
kable_styling(bootstrap_options = c("striped", "hover"))
Spatial vs. Temporal Validation Comparison
|
Validation
|
MAE
|
RMSE
|
|
Spatial (LOGO CV 2017)
|
2.65
|
3.50
|
|
Temporal (2018)
|
9.06
|
15.69
|
Interpretation: Temporal prediction (2018 MAE: 9.06)
is worse than spatial cross-validation (2017 MAE: 2.65).
This is expected because:
- True change over time: Burglary patterns may have
shifted between 2017-2018
- Feature staleness: Our potholes and census data are
from 2017; they don’t capture 2018 updates
- Harder test: Temporal validation is a more
realistic test—it simulates forecasting future outcomes using past
data
Since temporal MAE is substantially higher, it suggests our model
captures the 2017 spatial structure well but has limited predictive
power for future burglary patterns. This is common in crime forecasting
and highlights the importance of regularly updating models.
Visualize 2018 Predictions and Errors
fishnet <- fishnet %>%
mutate(error_2018 = countBurglaries2018 - prediction_2018)
p1 <- ggplot() +
geom_sf(data = fishnet, aes(fill = countBurglaries2018), color = NA) +
scale_fill_viridis_c(name = "Count", option = "plasma", limits = c(0, 15)) +
labs(title = "Actual 2018 Burglaries") +
theme_crime()
p2 <- ggplot() +
geom_sf(data = fishnet, aes(fill = prediction_2018), color = NA) +
scale_fill_viridis_c(name = "Predicted", option = "plasma", limits = c(0, 15)) +
labs(title = "Predicted 2018 (from 2017 model)") +
theme_crime()
p3 <- ggplot() +
geom_sf(data = fishnet, aes(fill = error_2018), color = NA) +
scale_fill_gradient2(
name = "Error",
low = "#2166ac",
mid = "grey90",
high = "#b2182b",
midpoint = 0,
limits = c(-10, 10)
) +
labs(title = "2018 Prediction Errors") +
theme_crime()
(p1 + p2 + p3) +
plot_annotation(
title = "Temporal Validation: 2018 Predictions from 2017 Model"
)

The 2018 error map reveals systematic patterns: -
Underprediction (red): Small clusters on south and west
sides where burglaries increased or new hotspots emerged -
Overprediction (blue): Areas where crime declined more
than the model expected
These patterns suggest our model captures baseline spatial risk but
misses temporal dynamics. To improve forecasting, we might: 1.
Incorporate lagged crime counts to capture momentum 2. Add time-varying
predictors (unemployment, foreclosures) 3. Use dynamic updating (refit
monthly/quarterly)
| ## Conclusions |
| ### Overview I was tasked with building a spatial
predictive model examining the relation of 311 alley pothole complaints
relate to burglary patterns in Chicago in both 2017 and 2018. Using a
500m x 500m fishnet grid for uniform spatial units for analysis, I then
calculate Local Moran’s I calculations, LISA visualizations, poisson
regression, and negative binomial regression to test pothole data and
other structural features on burglaries. |
| Did pothole patterns predict burglary
risk? |
| Partially. Pothole counts and proximity showed
associations with burglaries, but these effects largely disappeared
after controlling for socioeconomic characteristics. This suggests both
are driven by shared structural conditions—neighborhood disinvestment,
poverty, and housing market weakness—rather than potholes directly
causing crime. |
| Did the model outperform the KDE
baseline? |
| Not in raw predictive accuracy on 2017 data. The KDE
achieved slightly lower MAE (2.16 vs. 2.36). However, the regression
model provides interpretability and highlights actionable risk factors,
offering insight into prominent structural variables. |
| How well did predictions generalize across
space and time? |
| Spatial cross-validation (LOGO CV) showed moderate
performance (MAE = 2.65), while temporal predictions (2018) were less
accurate MAE = 9.06, reflecting the challenge of forecasting dynamic
crime patterns. |
LS0tDQp0aXRsZTogIlByZWRpY3RpdmUgUG9saWNpbmciDQpzdWJ0aXRsZTogIk1VU0EgNTA4MCAtIEZhbGwgMjAyNSINCmF1dGhvcjogIkFsZXggU3RhdWZmZXIiDQpkYXRlOiBOb3ZlbWJlciAxNywgMjAyNQ0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRoZW1lOiB5ZXRpDQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19kZXB0aDogMw0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogZmFsc2UNCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgc2VsZl9jb250YWluZWQ6IHRydWUNCiAgICBkZl9wcmludDogcGFnZWQNCi0tLQ0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyAqKkludHJvZHVjdGlvbioqDQoNCkZvciB0aGlzIGFzc2lnbm1lbnQsIEkgY2hvc2UgdG8gYW5hbHl6ZSB0aGUgMzExICJBbGxleSBQb3Rob2xlIENvbXBsYWludHMiIGFjcm9zcyB0aGUgY2l0eSBvZiBDaGljYWdvIGluIGNvcnJlbGF0aW9uIHdpdGggZm9yY2VkLWVudHJ5IGJ1cmdsYXJpZXMuIEkgc2VsZWN0ZWQgdGhpcyB2YXJpYWJsZSBiZWNhdXNlIFBlbm5zeWx2YW5pYW5zIGRlYWwgd2l0aCBhIGZhaXIgc2hhcmUgb2YgcG90aG9sZXMsIHNvIEkgYW0gY3VyaW91cyBhYm91dCB0aGUgcG90aG9sZSBpc3N1ZXMgaW4gQ2hpY2FnbyBhbmQgd2hldGhlciBmYWlsaW5nIHJvYWQgaW5mcmFzdHJ1Y3R1cmUsIGRlc2lnbmF0ZWQgaW4gdGhlIGZvcm0gb2YgMzExIGNvbXBsYWludHMsaGFzIGFueSByZWxhdGlvbnNoaXAgdG8gcHJvcGVydHkgY3JpbWUuDQoNCkkgZXhwZWN0IHBvdGhvbGVzIHRvIHNob3cgc3BhdGlhbCBjbHVzdGVyaW5nIHNpbWlsYXIgdG8gYnVyZ2xhcmllcywgY29uY2VudHJhdGVkIGluIGFyZWFzIHdpdGggbG93ZXIgc29jaW9lY29ub21pYyBzdGF0dXMuIEhvd2V2ZXIsIHRoZSBwcmVkaWN0aXZlIHJlbGF0aW9uc2hpcCBtYXkgYmUgbW9kZXN0IG9uY2Ugd2UgY29udHJvbCBmb3IgZGVtb2dyYXBoaWMgYW5kIGhvdXNpbmcgY2hhcmFjdGVyaXN0aWNzLCBhcyBib3RoIHBoZW5vbWVuYSBsaWtlbHkgc2hhcmUgY29tbW9uIHVuZGVybHlpbmcgY2F1c2VzIChwb3ZlcnR5LCB0ZW51cmUgcGF0dGVybnMsIGFnZSBzdHJ1Y3R1cmUpIHJhdGhlciB0aGFuIGhhdmluZyBhIGRpcmVjdCBjYXVzYWwgbGluay4NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGUgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBjb21tZW50PScnfQ0KDQojIExvYWQgcmVxdWlyZWQgcGFja2FnZXMNCmxpYnJhcnkodGlkeXZlcnNlKSAgICAgICMgRGF0YSBtYW5pcHVsYXRpb24NCmxpYnJhcnkoc2YpICAgICAgICAgICAgICMgU3BhdGlhbCBvcGVyYXRpb25zDQpsaWJyYXJ5KGhlcmUpICAgICAgICAgICAjIFJlbGF0aXZlIGZpbGUgcGF0aHMNCmxpYnJhcnkodmlyaWRpcykgICAgICAgICMgQ29sb3Igc2NhbGVzDQpsaWJyYXJ5KHRlcnJhKSAgICAgICAgICAjIFJhc3RlciBvcGVyYXRpb25zIChyZXBsYWNlcyAncmFzdGVyJykNCmxpYnJhcnkoc3BkZXApICAgICAgICAgICMgU3BhdGlhbCBkZXBlbmRlbmNlDQpsaWJyYXJ5KEZOTikgICAgICAgICAgICAjIEZhc3QgbmVhcmVzdCBuZWlnaGJvcnMNCmxpYnJhcnkoTUFTUykgICAgICAgICAgICMgTmVnYXRpdmUgYmlub21pYWwgcmVncmVzc2lvbg0KbGlicmFyeShwYXRjaHdvcmspICAgICAgIyBQbG90IGNvbXBvc2l0aW9uIChyZXBsYWNlcyBncmlkL2dyaWRFeHRyYSkNCmxpYnJhcnkoa25pdHIpICAgICAgICAgICMgVGFibGVzDQpsaWJyYXJ5KGthYmxlRXh0cmEpICAgICAjIFRhYmxlIGZvcm1hdHRpbmcNCmxpYnJhcnkoY2xhc3NJbnQpICAgICAgICMgQ2xhc3NpZmljYXRpb24gaW50ZXJ2YWxzDQpsaWJyYXJ5KHRpZHljZW5zdXMpDQoNCiMgU3BhdHN0YXQgc3BsaXQgaW50byBzdWItcGFja2FnZXMNCmxpYnJhcnkoc3BhdHN0YXQuZ2VvbSkgICAgIyBTcGF0aWFsIGdlb21ldHJpZXMNCmxpYnJhcnkoc3BhdHN0YXQuZXhwbG9yZSkgIyBTcGF0aWFsIGV4cGxvcmF0aW9uL0tERQ0KDQojIFNldCBvcHRpb25zDQpvcHRpb25zKHNjaXBlbiA9IDk5OSkgICMgTm8gc2NpZW50aWZpYyBub3RhdGlvbg0Kc2V0LnNlZWQoNTA4MCkgICAgICAgICAjIFJlcHJvZHVjaWJpbGl0eQ0KDQojIEZpeCBuYW1lc3BhY2UgY29uZmxpY3RzICh0ZXJyYSBtYXNrcyBkcGx5ciBmdW5jdGlvbnMpDQpzZWxlY3QgPC0gZHBseXI6OnNlbGVjdA0KZmlsdGVyIDwtIGRwbHlyOjpmaWx0ZXINCm11dGF0ZSA8LSBkcGx5cjo6bXV0YXRlDQpyZW5hbWUgPC0gZHBseXI6OnJlbmFtZQ0KDQojIENyZWF0ZSBjb25zaXN0ZW50IHRoZW1lIGZvciB2aXN1YWxpemF0aW9ucw0KdGhlbWVfY3JpbWUgPC0gZnVuY3Rpb24oYmFzZV9zaXplID0gMTEpIHsNCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSBiYXNlX3NpemUpICsNCiAgICB0aGVtZSgNCiAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IGJhc2Vfc2l6ZSArIDEpLA0KICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJncmF5MzAiLCBzaXplID0gYmFzZV9zaXplIC0gMSksDQogICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLA0KICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkNCiAgICApDQp9DQoNCiMgU2V0IGFzIGRlZmF1bHQNCnRoZW1lX3NldCh0aGVtZV9jcmltZSgpKQ0KDQpjYXQoIuKckyBBbGwgcGFja2FnZXMgbG9hZGVkIHN1Y2Nlc3NmdWxseSFcbiIpDQpjYXQoIuKckyBXb3JraW5nIGRpcmVjdG9yeToiLCBnZXR3ZCgpLCAiXG4iKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAqKkxvYWQgYW5kIEV4cGxvcmUgRGF0YSoqDQoNClRoaXMgY2h1bmsgbG9hZHMgdGhlIHNwYXRpYWwgYm91bmRhcmllcyBuZWVkZWQgZm9yIGFuYWx5c2lzLiBUaGUgcG9saWNlIGRpc3RyaWN0cyB3aWxsIGJlIHVzZWQgZm9yIHNwYXRpYWwgY3Jvc3MtdmFsaWRhdGlvbiAoTGVhdmUtT25lLUdyb3VwLU91dCksIHdoaWxlIHRoZSBiZWF0cyBwcm92aWRlIGZpbmVyIGFkbWluaXN0cmF0aXZlIHVuaXRzIGFuZCB0aGUgQ2hpY2FnbyBib3VuZGFyeSBkZWZpbmVzIG91ciBzdHVkeSBhcmVhLg0KDQpgYGB7ciBsb2FkLXNwYXRpYWwtZGF0YSwgaW5jbHVkZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY29tbWVudCA9ICcnfQ0KDQojIExvYWRpbmcgcG9saWNlIGRpc3RyaWN0cw0KcG9saWNlRGlzdHJpY3RzIDwtIA0KICBzdF9yZWFkKCJodHRwczovL2RhdGEuY2l0eW9mY2hpY2Fnby5vcmcvYXBpL2dlb3NwYXRpYWwvMjR6dC1qcGZuP21ldGhvZD1leHBvcnQmZm9ybWF0PUdlb0pTT04iKSAlPiUNCiAgc3RfdHJhbnNmb3JtKCdFU1JJOjEwMjI3MScpICU+JQ0KICBkcGx5cjo6c2VsZWN0KERpc3RyaWN0ID0gZGlzdF9udW0pDQoNCiMgTG9hZGluZyBwb2xpY2UgYmVhdHMgDQpwb2xpY2VCZWF0cyA8LSANCiAgc3RfcmVhZCgiaHR0cHM6Ly9kYXRhLmNpdHlvZmNoaWNhZ28ub3JnL2FwaS9nZW9zcGF0aWFsL245aXQtaHN0dz9tZXRob2Q9ZXhwb3J0JmZvcm1hdD1HZW9KU09OIikgJT4lDQogIHN0X3RyYW5zZm9ybSgnRVNSSToxMDIyNzEnKSAlPiUNCiAgZHBseXI6OnNlbGVjdChCZWF0ID0gYmVhdF9udW0pDQoNCiMgTG9hZGluZyBDaGljYWdvIEJvdW5kYXJ5DQpjaGljYWdvQm91bmQgPC0gDQogIHN0X3JlYWQoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS91cmJhblNwYXRpYWwvUHVibGljLVBvbGljeS1BbmFseXRpY3MtTGFuZGluZy9tYXN0ZXIvREFUQS9DaGFwdGVyNS9jaGljYWdvQm91bmRhcnkuZ2VvanNvbiIpJT4lDQogIHN0X3RyYW5zZm9ybSgnRVNSSToxMDIyNzEnKSANCg0KY2F0KCJMb2FkZWQgc3BhdGlhbCBib3VuZGFyaWVzIFxuIikNCmNhdCgiIC0gUG9saWNlIGRpc3RyaWN0czoiLCBucm93KHBvbGljZURpc3RyaWN0cyksICJcbiIpDQpjYXQoIiAtIFBvbGljZSBiZWF0czoiLCBucm93KHBvbGljZUJlYXRzKSwgIlxuIikNCmBgYA0KDQpUaGVuIEkgbG9hZCB0aGUgMzExIHBvdGhvbGUgZGF0YSwgZW5zdXJpbmcgdGhlIHNhbWUgRVNQRyBhcyB0aGUgQ2hpY2FnbyBCb3VuZGFyeSwgYSBsb2NhbCBjb29yZG5hdGUgcmVmZXJlbmNlIHN5c3RlbSBvZiBDaGljYWdvLCBJbGxpbm9pcy4NCg0KYGBge3IgYnVyZ2xhcmllcywgbWVzc2FnZT1GQUxTRSwgZWNobyA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlID0gRkFMU0V9DQpidXJnbGFyaWVzIDwtIHN0X3JlYWQoaGVyZSgibGFicy9sYWI0L2RhdGEvYnVyZ2xhcmllcy5zaHAiKSkgJT4lIA0KICBzdF90cmFuc2Zvcm0oJ0VTUkk6MTAyMjcxJykNCg0KIyMgSSBxdWVyaWVkIG9uIHRoZSB3ZWJzaXRlIHRoZW4gZG93bmxvYWRlZCB0aGUgcXVlcmllZCBjc3YuIA0KaG9sZXMzMTEgPC0gcmVhZC5jc3YoaGVyZSgibGFicy9sYWI0L2RhdGEvMzExX1NlcnZpY2VfUmVxdWVzdHNfMjAyNTExMTEuY3N2IikpICU+JQ0KICBmaWx0ZXIoIWlzLm5hKExBVElUVURFKSwgIWlzLm5hKExPTkdJVFVERSkpICU+JQ0KICBzdF9hc19zZihjb29yZHMgPSBjKCJMT05HSVRVREUiLCAiTEFUSVRVREUiKSwgY3JzID0gNDMyNikgJT4lDQogIHN0X3RyYW5zZm9ybSgnRVNSSToxMDIyNzEnKQ0KDQpkaXN0cmljdFN1bSA8LSBob2xlczMxMSAlPiUNCiAgc3RfZHJvcF9nZW9tZXRyeSgpICU+JQ0KICBjb3VudChQT0xJQ0VfRElTVFJJQ1QsIHNvcnQgPSBUUlVFKQ0KDQojdXNpbmcgY2F0IHRvIGNoZWNrIGRhdGENCmNhdCgiXG4gTG9hZGVkIGJ1cmdsYXJ5IGRhdGEgXG4iKQ0KY2F0KCIgIC0gTnVtYmVyIG9mIGJ1cmdsYXJpZXM6IiwgbnJvdyhidXJnbGFyaWVzKSwgIlxuIikNCmNhdCgiICAtIENSUzoiLCBzdF9jcnMoYnVyZ2xhcmllcykkaW5wdXQsICJcbiIpDQpjYXQoIiAgLSBEYXRlIHJhbmdlOiIsIG1pbihidXJnbGFyaWVzJGRhdGUsIG5hLnJtID0gVFJVRSksICJ0byIsIA0KICAgIG1heChidXJnbGFyaWVzJGRhdGUsIG5hLnJtID0gVFJVRSksICJcbiIpDQoNCmNhdCgiXG4gTG9hZGVkIDMxMSBkYXRhIFxuIikNCmNhdCgiICAtIE51bWJlciBvZiBvYnNlcnZhdGlvbnM6IiwgbnJvdyhob2xlczMxMSksICJcbiIpDQpjYXQoIiAgLSBDUlM6Iiwgc3RfY3JzKGhvbGVzMzExKSRpbnB1dCwgIlxuIikNCmNhdCgiICAtIERpc3RyaWN0IiwgZGlzdHJpY3RTdW0kUE9MSUNFX0RJU1RSSUNUWzFdLCAiaGFzIHRoZSBtb3N0IHBvdGhvbGUgMzExIGNhbGxzIGF0XG4iLCBkaXN0cmljdFN1bSRuWzFdKQ0KDQpgYGANCg0KIyMgRXhwbG9yYXRvcnkgU3BhdGlhbCBBbmFseXNpcw0KDQpUaGUgZm9sbG93aW5nIHZpc3VhbGl6YXRpb25zIGV4cGxvcmUgdGhlIHNwYXRpYWwgZGlzdHJpYnV0aW9uIG9mIHBvdGhvbGUgY29tcGxhaW50cyBhY3Jvc3MgQ2hpY2Fnby4gVGhlIHBvaW50IG1hcCBzaG93cyB0aGUgcmF3IGxvY2F0aW9ucyBvZiBhbGwgMzExIGNhbGxzLCB3aGlsZSB0aGUga2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbiByZXZlYWxzIHVuZGVybHlpbmcgaW50ZW5zaXR5IHBhdHRlcm5zIGJ5IHNtb290aGluZyBwb2ludCBsb2NhdGlvbnMgaW50byBhIGNvbnRpbnVvdXMgc3VyZmFjZS4NCg0KDQpgYGB7ciB2aXN1YWxpemUtcG9pbnRzLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NSwgZWNobyA9IEZBTFNFLCAgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZSA9IFRSVUV9DQoNCiNwb2ludCBtYXAgb2YgcG90aG9sZXMgDQpwMSA8LSBnZ3Bsb3QoKSArIA0KICBnZW9tX3NmKGRhdGEgPSBjaGljYWdvQm91bmQsIGZpbGwgPSAiZ3JleTc1IiwgY29sb3IgPSAiZ3JleTQiKSArDQogIGdlb21fc2YoZGF0YSA9IGhvbGVzMzExLCBjb2xvciA9ICIjMjVGMkY1IixzaXplID0gMC4xLCBhbHBoYSA9IDAuNCkgKyANCiAgbGFicyh0aXRsZSA9ICJQb3Rob2xlIExvY2F0aW9ucyIsDQogICAgICAgc3VidGl0bGUgPSBwYXN0ZTAoIkNoaWNhZ28gMjAxNywgbiA9ICIsIG5yb3coaG9sZXMzMTEpKSkNCg0KI2tlcm5lbCBkZW5zaXR5IG1hcCBvZiBwb3Rob2xlcyANCnAyIDwtIGdncGxvdCgpICsgDQogIGdlb21fc2YoZGF0YSA9IGNoaWNhZ29Cb3VuZCwgZmlsbCA9ICJncmV5NzUiLCBjb2xvciA9ICJncmV5NCIpICsNCiAgZ2VvbV9kZW5zaXR5XzJkX2ZpbGxlZCgNCiAgICBkYXRhID0gZGF0YS5mcmFtZShzdF9jb29yZGluYXRlcyhob2xlczMxMSkpLA0KICAgIGFlcyhYLCBZKSwNCiAgICBhbHBoYSA9IDAuNywNCiAgICBiaW5zID0gOA0KICApICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoDQogICAgb3B0aW9uID0gInBsYXNtYSIsDQogICAgZGlyZWN0aW9uID0gLTEsDQogICAgZ3VpZGUgPSAibm9uZSIgIA0KICApICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJEZW5zaXR5IFN1cmZhY2UiLA0KICAgIHN1YnRpdGxlID0gIktlcm5lbCBkZW5zaXR5IGVzdGltYXRpb24iDQogICkNCg0KIyBjb21iaW5lIHBsb3RzIA0KcDEgKyBwMiArDQogIHBsb3RfYW5ub3RhdGlvbigNCiAgICB0aXRsZSA9ICJTcGF0aWFsIERpc3RyaWJ1dGlvbiBvZiAzMTEgUG90aG9sZSBDb21wbGFpbnRzIGluIENoaWFnbyAyMDE3IiwNCiAgICB0YWdfbGV2ZWxzID0gJzEnDQogICkNCg0KYGBgDQoNCk1hcCAxIChsZWZ0KSByZXZlYWxzIHRoZSA3MCwwNDYgcG90aG9sZSBjb21wbGFpbnRzIGFyZSBjbHVzdGVyZWQgcmF0aGVyIHRoYW4gZXZlbmx5IGRpc3RyaWJ1dGVkIGFjcm9zcyBDaGljYWdvLiBNYXAgMiAocmlnaHQpIGFmZmlybXMgdGhpcyBwYXR0ZXJuOiB0aGUgaGlnaGVzdCBpbnRlbnNpdHkgYXBwZWFycyBpbiB0aGUgbm9ydGgvbm9ydGh3ZXN0ZXJuIGFyZWEgYW5kIGV4dGVuZHMgc291dGh3ZXN0IGFsb25nIG1ham9yIGNvcnJpZG9ycy4NCg0KVGhpcyBkaXN0cmlidXRpb24gbWF5IHJlZmxlY3Qgc2V2ZXJhbCBmYWN0b3JzOiAoMSkgZGlmZmVyaW5nIHJvYWQgcXVhbGl0eSBhY3Jvc3MgbmVpZ2hib3Job29kcywgKDIpIHZhcnlpbmcgbmVpZ2hib3Job29kcyBjbHVzdGVycyBvZiBwZW9wbGUgd2lsbGluZyB0byBjb21wbGFpbiwgb3IgKDMpIGFjdHVhbCBkaWZmZXJlbmNlcyBpbiBpbmZyYXN0cnVjdHVyZSBtYWludGVuYW5jZSBwcmlvcml0aWVzLiANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIEZpc2huZXQgR3JpZA0KDQpGb3IgZXZlbmx5IGRpc3BlcnNlZCBhbmFseXNpcywgY3JlYXRpbmcgYSBmaXNobmV0IGdyaWQgYXNzaWducyBhIHNxdWFyZSBhcmVhIG9mIGEgY2VydGFpbiBoZWlnaHQgYW5kIGxlbmd0aCB3aXRoaW4gYSBib3VuZGFyeSBhbmQgZWFjaCBzcXVhcmUgYXJlYSBpcyBvZiBlcXVhbCBwcm9wb3J0aW9uLiBUaGlzIGFjdHMgYXMgYSBjb250YWluZXIgZm9yIHN1bW1hcml6aW5nIGFuZCBhbmFseXppbmcgZGF0YSB3aXRoaW4gZWFjaCBjZWxsLCBjb21wYXJpbmcgc3BhdGlhbCBkYXRhIHVuaWZvcm1seS4gT3VyIGZpc2huZXQgd2lsbCBiZSA1MDBtIHggNTAwbS4NCg0KLS0tDQoNCiMjIyBDcmVhdGUgRmlzaG5ldA0KDQpgYGB7ciBjcmVhdGUtZmlzaG5ldCwgd2FybmluZz1GQUxTRX0NCiMgQ3JlYXRlIGEgNTAwbSB4IDUwMG0gZ3JpZCBvdmVyIENoaWNhZ28NCmZpc2huZXQgPC0gc3RfbWFrZV9ncmlkKA0KICBjaGljYWdvQm91bmQsDQogIGNlbGxzaXplID0gNTAwLA0KICBzcXVhcmUgPSBUUlVFDQopICU+JQ0KICBzdF9zZigpICU+JQ0KICBtdXRhdGUodW5pcXVlSUQgPSByb3dfbnVtYmVyKCkpDQoNCiMgUHJvcGVybHkgY2xpcCB0byBDaGljYWdvIGJvdW5kYXJ5IHVzaW5nIGludGVyc2VjdGlvbg0KZmlzaG5ldCA8LSBzdF9pbnRlcnNlY3Rpb24oZmlzaG5ldCwgY2hpY2Fnb0JvdW5kKSAlPiUNCiAgbXV0YXRlKHVuaXF1ZUlEID0gcm93X251bWJlcigpKSAgIyBSZS1udW1iZXIgYWZ0ZXIgY2xpcHBpbmcNCg0KY2F0KCLinJMgQ3JlYXRlZCBmaXNobmV0IGdyaWRcbiIpDQpjYXQoIiAgLSBOdW1iZXIgb2YgY2VsbHM6IiwgbnJvdyhmaXNobmV0KSwgIlxuIikNCmBgYA0KDQpCeSB1c2luZyBzcGF0aWFsIGludGVyc2VjdGlvbiB0byBjbGlwIHRoZSBncmlkIHRvIENoaWNhZ28ncyBib3VuZGFyeSwgSSBleGNsdWRlIGNlbGxzIHRoYXQgZmFsbCBvdXRzaWRlIHRoZSBjaXR5IGxpbWl0cy4gVGhpcyBlbnN1cmVzIHRoYXQgb3VyIGFuYWx5c2lzIG9ubHkgaW5jbHVkZXMgYXJlYXMgd2hlcmUgYm90aCAzMTEgY2FsbHMgYW5kIGJ1cmdsYXJpZXMgY2FuIGxlZ2l0aW1hdGVseSBvY2N1ciwgYXZvaWRpbmcgZWRnZSBlZmZlY3RzIGZyb20gcGFydGlhbCBjZWxscy4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyBBZ2dyZWdhdGUgQnVyZ2xhcmllcyBhbmQgUG90aG9sZXMgdG8gR3JpZA0KDQpUaGlzIGNodW5rIHBlcmZvcm1zIGEgc3BhdGlhbCBqb2luIG9mIHBvaW50LWxldmVsIGJ1cmdsYXJ5IGluY2lkZW50cyB0byB0aGUgNTAwbSB4IDUwMG0gZmlzaG5ldC4NCg0KYGBge3IgYWdncmVnYXRlLWJ1cmdsYXJpZXMsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTYsIGNvbW1lbnQ9Jyd9DQpidXJnbGFyaWVzX2Zpc2ggPC0gc3Rfam9pbihidXJnbGFyaWVzLCBmaXNobmV0LCBqb2luID0gc3Rfd2l0aGluKSU+JQ0KICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lDQogIGdyb3VwX2J5KHVuaXF1ZUlEKSAlPiUNCiAgc3VtbWFyaXplKGNvdW50QnVyZ2xhcmllcyA9IG4oKSkNCg0KZmlzaG5ldCA8LSBmaXNobmV0ICU+JQ0KICBsZWZ0X2pvaW4oYnVyZ2xhcmllc19maXNoLCBieSA9ICJ1bmlxdWVJRCIpICU+JQ0KICBtdXRhdGUoY291bnRCdXJnbGFyaWVzID0gcmVwbGFjZV9uYShjb3VudEJ1cmdsYXJpZXMsMCkpDQoNCiMgYWdncmVnYXRlIHN0YXRzDQpjYXQoIlxuQnVyZ2xhcnkgY291bnQgZGlzdHJpYnV0aW9uOlxuIikNCnN1bW1hcnkoZmlzaG5ldCRjb3VudEJ1cmdsYXJpZXMpDQpjYXQoIlxuQ2VsbHMgd2l0aCB6ZXJvIGJ1cmdsYXJpZXM6IiwgDQogICAgc3VtKGZpc2huZXQkY291bnRCdXJnbGFyaWVzID09IDApLCANCiAgICAiLyIsIG5yb3coZmlzaG5ldCksDQogICAgIigiLCByb3VuZCgxMDAgKiBzdW0oZmlzaG5ldCRjb3VudEJ1cmdsYXJpZXMgPT0gMCkgLyBucm93KGZpc2huZXQpLCAxKSwgIiUpXG4iKQ0KDQpgYGANCg0KQWJvdXQgYHIgcm91bmQoMTAwICogc3VtKGZpc2huZXQkY291bnRCdXJnbGFyaWVzID09IDApIC8gbnJvdyhmaXNobmV0KSwgMSlgJSBvZiBjZWxscyBoYXZlIHplcm8gYnVyZ2xhcmllcy4gVGhpcyB6ZXJvLWluZmxhdGlvbiBpcyB0eXBpY2FsIG9mIGNyaW1lIGRhdGEgYW5kIG1vdGl2YXRlcyBvdXIgdXNlIG9mIGNvdW50IHJlZ3Jlc3Npb24gbW9kZWxzIChQb2lzc29uIG9yIE5lZ2F0aXZlIEJpbm9taWFsKSByYXRoZXIgdGhhbiBvcmRpbmFyeSBsZWFzdCBzcXVhcmVzLiBUaGUgbWF4aW11bSBjb3VudCBpcyBgciBtYXgoZmlzaG5ldCRjb3VudEJ1cmdsYXJpZXMpYCBidXJnbGFyaWVzIGluIGEgc2luZ2xlIGNlbGwsIGluZGljYXRpbmcgc3BhdGlhbCBoZXRlcm9nZW5laXR5IGluIGNyaW1lIHJpc2suDQoNCi0tLQ0KDQpOb3cgSSBhZ2dyZWdhdGUgcG90aG9sZSAzMTEgY2FsbHMgdG8gdGhlIHNhbWUgZ3JpZDoNCg0KYGBge3IsIGFnZy1wb3R9DQojIEFnZ3JlZ2F0ZSBwb3Rob2xlcyB0byBmaXNobmV0DQpwb3Rob2xlc19maXNoIDwtIHN0X2pvaW4oaG9sZXMzMTEsIGZpc2huZXQsIGpvaW4gPSBzdF93aXRoaW4pICU+JQ0KICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lDQogIGdyb3VwX2J5KHVuaXF1ZUlEKSAlPiUNCiAgc3VtbWFyaXplKGNvdW50UG90aG9sZXMgPSBuKCkpDQoNCiMgSm9pbiBiYWNrIHRvIGZpc2huZXQNCmZpc2huZXQgPC0gZmlzaG5ldCAlPiUNCiAgbGVmdF9qb2luKHBvdGhvbGVzX2Zpc2gsIGJ5ID0gInVuaXF1ZUlEIikgJT4lDQogIG11dGF0ZShjb3VudFBvdGhvbGVzID0gcmVwbGFjZV9uYShjb3VudFBvdGhvbGVzLCAwKSkNCg0KY2F0KCJcblBvdGhvbGUgY291bnQgZGlzdHJpYnV0aW9uOlxuIikNCnN1bW1hcnkoZmlzaG5ldCRjb3VudFBvdGhvbGVzKQ0KY2F0KCJcbkNlbGxzIHdpdGggemVybyBwb3Rob2xlczoiLCANCiAgICBzdW0oZmlzaG5ldCRjb3VudFBvdGhvbGVzID09IDApLCANCiAgICAiLyIsIG5yb3coZmlzaG5ldCksDQogICAgIigiLCByb3VuZCgxMDAgKiBzdW0oZmlzaG5ldCRjb3VudFBvdGhvbGVzID09IDApIC8gbnJvdyhmaXNobmV0KSwgMSksICIlKVxuIikNCmBgYA0KLS0tDQoNCiMjIyBWaXN1YWwgR3JpZC1MZXZlbCBDb3VudHMNCg0KYGBge3IgdmlzLWZpc2gsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTZ9DQojIE1hcCBidXJnbGFyeSBjb3VudHMNCnAxIDwtIGdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gZmlzaG5ldCwgYWVzKGZpbGwgPSBjb3VudEJ1cmdsYXJpZXMpLCBjb2xvciA9IE5BKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKA0KICAgIG5hbWUgPSAiQ291bnQiLA0KICAgIG9wdGlvbiA9ICJwbGFzbWEiLA0KICAgIHRyYW5zID0gInNxcnQiLA0KICAgIGJyZWFrcyA9IGMoMCwgMSwgNSwgMTAsIDIwKQ0KICApICsNCiAgbGFicyh0aXRsZSA9ICJCdXJnbGFyeSBDb3VudHMgcGVyIENlbGwiKSArDQogIHRoZW1lX2NyaW1lKCkNCg0KIyBNYXAgcG90aG9sZSBjb3VudHMgIA0KcDIgPC0gZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSBmaXNobmV0LCBhZXMoZmlsbCA9IGNvdW50UG90aG9sZXMpLCBjb2xvciA9IE5BKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKA0KICAgIG5hbWUgPSAiQ291bnQiLA0KICAgIG9wdGlvbiA9ICJwbGFzbWEiLA0KICAgIHRyYW5zID0gInNxcnQiLA0KICAgIGJyZWFrcyA9IGMoMCwgMjAsIDUwLCAxMDAsIDIwMCkNCiAgKSArDQogIGxhYnModGl0bGUgPSAiUG90aG9sZSBDb21wbGFpbnQgQ291bnRzIHBlciBDZWxsIikgKw0KICB0aGVtZV9jcmltZSgpDQoNCnAxICsgcDIgKw0KICBwbG90X2Fubm90YXRpb24oDQogICAgdGl0bGUgPSAiU3BhdGlhbCBEaXN0cmlidXRpb24gb2YgQnVyZ2xhcmllcyBhbmQgUG90aG9sZXMgYXQgNTAwbSBHcmlkIg0KICApDQpgYGANCg0KQm90aCBidXJnbGFyaWVzIGFuZCBwb3Rob2xlcyBzaG93IHNwYXRpYWwgY2x1c3RlcmluZywgYnV0IHdpdGggIGRpZmZlcmVudCBwYXR0ZXJucy4gQnVyZ2xhcmllcyBjb25jZW50cmF0ZSBvbiB0aGUgU291dGggYW5kIFdlc3Qgc2lkZXMgYW5kIHBvdGhvbGUgcmVwb3J0cyBjbHVzdGVyIG1vcmUgaGVhdmlseSBpbiB0aGUgbm9ydGggYW5kIGNlbnRyYWwgY29ycmlkb3JzLCBzaW1pbGFyIHRvIHRoZSBLREUgbWFwIGZyb20gZWFybGllci5UaGlzIHN1Z2dlc3RzIHRoYXQgYnVyZ2xhcmllcyBhbmQgMzExIHBvdGhvbGUgY29tcGxhaW50cyBhcmUgbm90IGRpcmVjdGx5IGNvcnJlbGF0ZWQuIA0KDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyBTcGF0aWFsIEZlYXR1cmVzIEVuZ2luZWVyaW5nDQoNClRvIHVuZGVyc3RhbmQgcHJveGltaXR5IGVmZmVjdHMgYW5kIHNwYXRpYWwgc3RydWN0dXJlLCBJIHdpbGwgZW5naW5lZXIgdGhyZWUgdHlwZXMgb2YgZmVhdHVyZXM6IG5lYXJlc3QtbmVpZ2hib3IgZGlzdGFuY2VzLCBob3Qtc3BvdCBpZGVudGlmaWNhdGlvbiB2aWEgTG9jYWwgTW9yYW4ncyBJLCBhbmQgZGlzdGFuY2UgdG8gaWRlbnRpZmllZCBob3Qgc3BvdHMuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgQ2FsY3VsYXRlIEstTmVhcmVzdCBOZWlnaGJvciBGZWF0dXJlcw0KDQpgYGB7ciBrbm4tZmVhdHVyZX0NCg0KIyBHZXR0aW5nIENvb3JkaW5hdGVzDQpmaXNobmV0X2Nvb3JkcyA8LSBzdF9jb29yZGluYXRlcyhzdF9jZW50cm9pZChmaXNobmV0KSkNCnBvdGhvbGVfY29vcmRzIDwtIHN0X2Nvb3JkaW5hdGVzKGhvbGVzMzExKQ0KDQojIENhbGN1bGF0ZSBrIG5lYXJlc3QgbmVpZ2hib3JzIChrPTMgZm9yIGF2ZXJhZ2UgZGlzdGFuY2UgdG8gMyBuZWFyZXN0IHBvdGhvbGVzKQ0Kbm5fcmVzdWx0IDwtIGdldC5rbm54KHBvdGhvbGVfY29vcmRzLCBmaXNobmV0X2Nvb3JkcywgayA9IDMpDQoNCiMgQWRkIG1lYW4gZGlzdGFuY2UgdG8gZmlzaG5ldA0KZmlzaG5ldCA8LSBmaXNobmV0ICU+JQ0KICBtdXRhdGUoaG9sZXMzMTEubm4gPSByb3dNZWFucyhubl9yZXN1bHQkbm4uZGlzdCkpDQoNCmNhdCgi4pyTIENhbGN1bGF0ZWQgbmVhcmVzdCBuZWlnaGJvciBkaXN0YW5jZXNcbiIpDQpjYXQoIiAgU3VtbWFyeSBvZiBtZWFuIGRpc3RhbmNlIHRvIDMgbmVhcmVzdCBwb3Rob2xlcyAoZmVldCk6XG4iKQ0Kc3VtbWFyeShmaXNobmV0JGhvbGVzMzExLm5uKQ0KYGBgDQoNClVzaW5nIHRoZSBhdmVyYWdlIGRpc3RhbmNlIHRvIHRoZSB0aHJlZSBuZWFyZXN0IHBvdGhvbGVzIChyYXRoZXIgdGhhbiBqdXN0IHRoZSBzaW5nbGUgbmVhcmVzdCkgcHJvdmlkZXMgYSBtb3JlIHJvYnVzdCBtZWFzdXJlIG9mIGxvY2FsIHBvdGhvbGUgaW50ZW5zaXR5IHdoaWxlIHJlZHVjaW5nIHNlbnNpdGl2aXR5IHRvIGlzb2xhdGVkIG91dGxpZXJzLg0KDQotLS0NCg0KIyMjIENhbGN1bGF0ZSBLZXJuZWwgRGVuc2l0eSBFc3RpbWF0aW9uDQoNCk5vdyBJIGNvbXB1dGUgYSBrZXJuZWwgZGVuc2l0eSBzdXJmYWNlIGZvciBidXJnbGFyaWVzIHRvIHVzZSBhcyBhIGJhc2VsaW5lIGNvbXBhcmlzb24gbW9kZWw6DQoNCmBgYHtyIGNhbGN1bGF0ZS1rZGUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIENvbnZlcnQgYnVyZ2xhcmllcyB0byBwb2ludCBwYXR0ZXJuIG9iamVjdA0KYnVyZ2xhcmllc19wcHAgPC0gYXMucHBwKA0KICBzdF9jb29yZGluYXRlcyhidXJnbGFyaWVzKSwNCiAgVyA9IGFzLm93aW4oc3RfYmJveChjaGljYWdvQm91bmQpKQ0KKQ0KDQojIENhbGN1bGF0ZSBLREUgd2l0aCAxa20gYmFuZHdpZHRoDQprZGVfYnVyZ2xhcmllcyA8LSBkZW5zaXR5LnBwcCgNCiAgYnVyZ2xhcmllc19wcHAsDQogIHNpZ21hID0gMTAwMCwgICMgMWttID0gfjMyODAgZmVldA0KICBlZGdlID0gVFJVRSAgICAjIEVkZ2UgY29ycmVjdGlvbg0KKQ0KDQojIENvbnZlcnQgdG8gdGVycmEgcmFzdGVyDQprZGVfcmFzdGVyIDwtIHJhc3Qoa2RlX2J1cmdsYXJpZXMpDQoNCiMgRXh0cmFjdCBLREUgdmFsdWVzIHRvIGZpc2huZXQgY2VsbHMNCmZpc2huZXQgPC0gZmlzaG5ldCAlPiUNCiAgbXV0YXRlKA0KICAgIGtkZV92YWx1ZSA9IHRlcnJhOjpleHRyYWN0KA0KICAgICAga2RlX3Jhc3RlciwNCiAgICAgIHZlY3QoZmlzaG5ldCksDQogICAgICBmdW4gPSBtZWFuLA0KICAgICAgbmEucm0gPSBUUlVFDQogICAgKVssIDJdDQogICkNCg0KY2F0KCLinJMgQ2FsY3VsYXRlZCBLREUgdmFsdWVzXG4iKQ0KYGBgDQoNCi0tLQ0KDQojIyMgUGVyZm9ybSBMb2NhbCBNb3JhbidzIEkgQW5hbHlzaXMNCg0KTG9jYWwgTW9yYW4ncyBJIGlkZW50aWZpZXMgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBjbHVzdGVycyBhbmQgb3V0bGllcnMgaW4gdGhlIHBvdGhvbGUgZGlzdHJpYnV0aW9uLiBIaWdoLUhpZ2ggY2x1c3RlcnMgKGhvdCBzcG90cykgaW5kaWNhdGUgYXJlYXMgd2hlcmUgYm90aCB0aGUgZm9jYWwgY2VsbCBhbmQgaXRzIG5laWdoYm9ycyBoYXZlIGhpZ2ggcG90aG9sZSBjb3VudHMuDQoNCmBgYHtyIG1vcmFucy1wb3Rob2xlcywgd2FybmluZz1GQUxTRX0NCg0KIyBGdW5jdGlvbiB0byBjYWxjdWxhdGUgTG9jYWwgTW9yYW4ncyBJDQpjYWxjdWxhdGVfbG9jYWxfbW9yYW5zIDwtIGZ1bmN0aW9uKGRhdGEsIHZhcmlhYmxlLCBrID0gNSkgew0KICANCiAgIyBDcmVhdGUgc3BhdGlhbCB3ZWlnaHRzDQogIGNvb3JkcyA8LSBzdF9jb29yZGluYXRlcyhzdF9jZW50cm9pZChkYXRhKSkNCiAgbmVpZ2hib3JzIDwtIGtubjJuYihrbmVhcm5laWdoKGNvb3JkcywgayA9IGspKQ0KICB3ZWlnaHRzIDwtIG5iMmxpc3R3KG5laWdoYm9ycywgc3R5bGUgPSAiVyIsIHplcm8ucG9saWN5ID0gVFJVRSkNCiAgDQogICMgQ2FsY3VsYXRlIExvY2FsIE1vcmFuJ3MgSQ0KICBsb2NhbF9tb3JhbiA8LSBsb2NhbG1vcmFuKGRhdGFbW3ZhcmlhYmxlXV0sIHdlaWdodHMpDQogIA0KICAjIENsYXNzaWZ5IGNsdXN0ZXJzDQogIG1lYW5fdmFsIDwtIG1lYW4oZGF0YVtbdmFyaWFibGVdXSwgbmEucm0gPSBUUlVFKQ0KICANCiAgZGF0YSAlPiUNCiAgICBtdXRhdGUoDQogICAgICBsb2NhbF9pID0gbG9jYWxfbW9yYW5bLCAxXSwNCiAgICAgIHBfdmFsdWUgPSBsb2NhbF9tb3JhblssIDVdLA0KICAgICAgaXNfc2lnbmlmaWNhbnQgPSBwX3ZhbHVlIDwgMC4wNSwNCiAgICAgIA0KICAgICAgbW9yYW5fY2xhc3MgPSBjYXNlX3doZW4oDQogICAgICAgICFpc19zaWduaWZpY2FudCB+ICJOb3QgU2lnbmlmaWNhbnQiLA0KICAgICAgICBsb2NhbF9pID4gMCAmIC5kYXRhW1t2YXJpYWJsZV1dID4gbWVhbl92YWwgfiAiSGlnaC1IaWdoIiwNCiAgICAgICAgbG9jYWxfaSA+IDAgJiAuZGF0YVtbdmFyaWFibGVdXSA8PSBtZWFuX3ZhbCB+ICJMb3ctTG93IiwNCiAgICAgICAgbG9jYWxfaSA8IDAgJiAuZGF0YVtbdmFyaWFibGVdXSA+IG1lYW5fdmFsIH4gIkhpZ2gtTG93IiwNCiAgICAgICAgbG9jYWxfaSA8IDAgJiAuZGF0YVtbdmFyaWFibGVdXSA8PSBtZWFuX3ZhbCB+ICJMb3ctSGlnaCIsDQogICAgICAgIFRSVUUgfiAiTm90IFNpZ25pZmljYW50Ig0KICAgICAgKQ0KICAgICkNCn0NCg0KIyBBcHBseSB0byBwb3Rob2xlcw0KZmlzaG5ldCA8LSBjYWxjdWxhdGVfbG9jYWxfbW9yYW5zKGZpc2huZXQsICJjb3VudFBvdGhvbGVzIiwgayA9IDUpDQoNCmNhdCgi4pyTIENhbGN1bGF0ZWQgTG9jYWwgTW9yYW4ncyBJXG4iKQ0KY2F0KCIgIEhvdCBzcG90cyAoSGlnaC1IaWdoKToiLCBzdW0oZmlzaG5ldCRtb3Jhbl9jbGFzcyA9PSAiSGlnaC1IaWdoIiksICJcbiIpDQpjYXQoIiAgQ29sZCBzcG90cyAoTG93LUxvdyk6Iiwgc3VtKGZpc2huZXQkbW9yYW5fY2xhc3MgPT0gIkxvdy1Mb3ciKSwgIlxuIikNCiAgICANCmBgYA0KDQotLS0NCg0KIyMjIFZpc3VhbGl6ZSBIb3Rwb3RzDQoNCldpbGwgY3JlYXRlIGEgY2hvcm9wbGV0aCBtYXAgaGlnaGxpZ2h0cyBIT1RIVE8gTE9XIExPVw0KDQpgYGB7ciB2aXMtbW9yYW5zLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD02fQ0KDQojIFZpc3VhbGl6ZSBob3Qgc3BvdHMNCmdncGxvdCgpICsNCiAgZ2VvbV9zZigNCiAgICBkYXRhID0gZmlzaG5ldCwNCiAgICBhZXMoZmlsbCA9IG1vcmFuX2NsYXNzKSwNCiAgICBjb2xvciA9IE5BDQogICkgKw0KICBzY2FsZV9maWxsX21hbnVhbCgNCiAgICB2YWx1ZXMgPSBjKA0KICAgICAgIkhpZ2gtSGlnaCIgPSAiI2Q3MTkxYyIsDQogICAgICAiSGlnaC1Mb3ciID0gIiNmZGFlNjEiLA0KICAgICAgIkxvdy1IaWdoIiA9ICIjYWJkOWU5IiwNCiAgICAgICJMb3ctTG93IiA9ICIjMmM3YmI2IiwNCiAgICAgICJOb3QgU2lnbmlmaWNhbnQiID0gImdyYXk5MCINCiAgICApLA0KICAgIG5hbWUgPSAiQ2x1c3RlciBUeXBlIg0KICApICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJMb2NhbCBNb3JhbidzIEk6IFBvdGhvbGUgSG90IFNwb3RzIiwNCiAgICBzdWJ0aXRsZSA9ICJIaWdoLUhpZ2ggY2x1c3RlcnMgcmVwcmVzZW50IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgaG90IHNwb3RzIG9mIGluZnJhc3RydWN0dXJlIGNvbXBsYWludHMiDQogICkgKw0KICB0aGVtZV9jcmltZSgpDQpgYGANCg0KVGhlIExJU0EgbWFwIHJldmVhbHMgZGlzdGluY3QgcG90aG9sZSBob3Qgc3BvdHMgaW4gdGhlIG5vcnRoZXJuIGFyZWEgYW5kIHBhcnRzIG9mIHRoZSBub3J0aHdlc3QuIFRoZXNlIEhpZ2gtSGlnaCBjbHVzdGVycyBpbmRpY2F0ZSBhcmVhcyB3aGVyZSBib3RoIHRoZSBjZWxsIGFuZCBpdHMgbmVpZ2hib3JzIGhhdmUgZWxldmF0ZWQgcG90aG9sZSBjb3VudHMsIHJlcHJlc2VudGluZyBwZXJzaXN0ZW50IGluZnJhc3RydWN0dXJlIG1haW50ZW5hbmNlIGNoYWxsZW5nZXMuIFRoZSBTb3V0aCBTaWRlIHNob3cgYXMgTG93LUxvdyBvciBOb3QgU2lnbmlmaWNhbnQgZm9yIHBvdGhvbGVzLg0KDQotLS0NCg0KIyMjIENhbGN1bGF0ZSB0aGUgRGlzdGFuY2UgdG8gdGhlIEhvdHNwb3RzDQoNCmBgYHtyIGhvdHNwb3RzLWRpc3QsIHdhcm5pbmc9RkFMU0UsICBjb21tZW50PScnfQ0KIyBHZXQgY2VudHJvaWRzIG9mICJIaWdoLUhpZ2giIGNlbGxzIChob3Qgc3BvdHMpDQpob3RzcG90cyA8LSBmaXNobmV0ICU+JQ0KICBmaWx0ZXIobW9yYW5fY2xhc3MgPT0gIkhpZ2gtSGlnaCIpICU+JQ0KICBzdF9jZW50cm9pZCgpDQoNCiMgQ2FsY3VsYXRlIGRpc3RhbmNlIGZyb20gZWFjaCBjZWxsIHRvIG5lYXJlc3QgaG90IHNwb3QNCmlmIChucm93KGhvdHNwb3RzKSA+IDApIHsNCiAgZmlzaG5ldCA8LSBmaXNobmV0ICU+JQ0KICAgIG11dGF0ZSgNCiAgICAgIGRpc3RfdG9faG90c3BvdCA9IGFzLm51bWVyaWMoDQogICAgICAgIHN0X2Rpc3RhbmNlKHN0X2NlbnRyb2lkKGZpc2huZXQpLCBob3RzcG90cyAlPiUgc3RfdW5pb24oKSkNCiAgICAgICkNCiAgICApDQogIGNhdCgi4pyTIENhbGN1bGF0ZWQgZGlzdGFuY2UgdG8gYWJhbmRvbmVkIGNhciBob3Qgc3BvdHNcbiIpDQogIGNhdCgiICAtIE51bWJlciBvZiBob3Qgc3BvdCBjZWxsczoiLCBucm93KGhvdHNwb3RzKSwgIlxuIikNCn0gZWxzZSB7DQogIGZpc2huZXQgPC0gZmlzaG5ldCAlPiUNCiAgICBtdXRhdGUoZGlzdF90b19ob3RzcG90ID0gMCkNCiAgY2F0KCLimqAgTm8gc2lnbmlmaWNhbnQgaG90IHNwb3RzIGZvdW5kXG4iKQ0KfQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgSm9pbiBBZGRpdGlvbmFsIEluZm9ybWF0aW9uIGZvciBDcm9zcy1WYWxpZGF0aW9uDQoNClRvIGF2b2lkIG9taXR0ZWQgdmFyaWFibGUgYmlhcywgSSBpbmNvcnBvcmF0ZSBBbWVyaWNhbiBDb21tdW5pdHkgU3VydmV5IChBQ1MpIGRlbW9ncmFwaGljIGFuZCBzb2Npb2Vjb25vbWljIGNvbnRyb2xzLCBhbmQgam9pbiBwb2xpY2UgZGlzdHJpY3QgaWRlbnRpZmllcnMgZm9yIHNwYXRpYWwgY3Jvc3MtdmFsaWRhdGlvbi4NCg0KYGBge3Igam9pbi1hY3MtZGF0YSwgbWVzc2FnZT1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQojIEpvaW4gZGlzdHJpY3QgaW5mb3JtYXRpb24gZm9yIHNwYXRpYWwgY3Jvc3MtdmFsaWRhdGlvbg0KZmlzaG5ldCA8LSBzdF9qb2luKA0KICBmaXNobmV0LA0KICBwb2xpY2VEaXN0cmljdHMsDQogIGpvaW4gPSBzdF93aXRoaW4sDQogIGxlZnQgPSBUUlVFDQopDQoNCiMgSGFuZGxlIGRpc3RyaWN0IGNvbHVtbiBuYW1lDQppZiAoImRpc3RfbnVtIiAlaW4lIG5hbWVzKGZpc2huZXQpKSB7DQogIGZpc2huZXQgPC0gZmlzaG5ldCAlPiUgZHBseXI6OnJlbmFtZShEaXN0cmljdCA9IGRpc3RfbnVtKQ0KfQ0KDQpmaXNobmV0IDwtIGZpc2huZXQgJT4lIGRwbHlyOjpmaWx0ZXIoIWlzLm5hKERpc3RyaWN0KSkNCg0KY2F0KCLinJMgSm9pbmVkIHBvbGljZSBkaXN0cmljdHNcbiIpDQpjYXQoIiAgLSBEaXN0cmljdHM6IiwgbGVuZ3RoKHVuaXF1ZShmaXNobmV0JERpc3RyaWN0KSksICJcbiIpDQpjYXQoIiAgLSBDZWxsczoiLCBucm93KGZpc2huZXQpLCAiXG4iKQ0KDQojIERlZmluZSBBQ1MgdmFyaWFibGVzDQphY3NfdmFycyA8LSBjKA0KICB0b3RfcG9wID0gIkIwMzAwMl8wMDEiLCAjdG90YWwgcG9wdWxhdGlvbg0KICBtZWRfaW5jID0gIkIxOTAxM18wMDEiLA0KICB3aGl0ZSA9ICJCMDMwMDJfMDAzIiwNCiAgYmxhY2sgPSAiQjAzMDAyXzAwNCIsDQogIGhpc3AgPSAiQjAzMDAyXzAxMiIsDQogIHBvdl90b3QgPSAiQjE3MDAxXzAwMSIsDQogIHBvdl9udW0gPSAiQjE3MDAxXzAwMiIsDQogIG9jY190b3QgPSAiQjI1MDAzXzAwMSIsDQogIG93bmVyID0gIkIyNTAwM18wMDIiLA0KICBtZWRfYWdlID0gIkIyNTAzNV8wMDEiDQopDQoNCiMgUHVsbCAyMDE3IEFDUyBkYXRhIGZvciBDb29rIENvdW50eQ0KdHJhY3RzIDwtIGdldF9hY3MoDQogIGdlb2dyYXBoeSA9ICJ0cmFjdCIsDQogIHN0YXRlID0gIklMIiwNCiAgY291bnR5ID0gIkNvb2siLA0KICB5ZWFyID0gMjAxNywNCiAgdmFyaWFibGVzID0gYWNzX3ZhcnMsDQogIHN1cnZleSA9ICJhY3M1IiwNCiAgb3V0cHV0ID0gIndpZGUiLA0KICBnZW9tZXRyeSA9IFRSVUUNCikNCg0KIyBQcm9jZXNzIGFuZCBqb2luIHRvIGZpc2huZXQNCnRyYWN0c19jaGkgPC0gdHJhY3RzICU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKGNoaWNhZ29Cb3VuZCkpICU+JQ0KICBzdF9maWx0ZXIoY2hpY2Fnb0JvdW5kLCAucHJlZGljYXRlID0gc3RfaW50ZXJzZWN0cykgJT4lDQogIGRwbHlyOjptdXRhdGUoDQogICAgcGN0X2JsYWNrID0gMTAwICogYmxhY2tFIC8gcG1heCh0b3RfcG9wRSwgMSksDQogICAgcGN0X2hpc3AgPSAxMDAgKiBoaXNwRSAvIHBtYXgodG90X3BvcEUsIDEpLA0KICAgIHBjdF9wb3ZlcnR5ID0gMTAwICogcG92X251bUUgLyBwbWF4KHBvdl90b3RFLCAxKSwNCiAgICBwY3Rfb3duZXIgPSAxMDAgKiBvd25lckUgLyBwbWF4KG9jY190b3RFLCAxKSwNCiAgICBtZWRfaW5jb21lID0gbWVkX2luY0UsDQogICAgbWVkX2hvdXNlX2FnZSA9IG1lZF9hZ2VFDQogICkgJT4lDQogIGRwbHlyOjpzZWxlY3QoR0VPSUQsIHBjdF9ibGFjaywgcGN0X2hpc3AsIHBjdF9wb3ZlcnR5LCBwY3Rfb3duZXIsIA0KICAgICAgICAgICAgICAgIG1lZF9pbmNvbWUsIG1lZF9ob3VzZV9hZ2UpDQoNCiMgU3BhdGlhbCBqb2luIHRvIGZpc2huZXQNCmZpc2huZXQgPC0gZmlzaG5ldCAlPiUNCiAgc3Rfam9pbih0cmFjdHNfY2hpLCBqb2luID0gc3RfaW50ZXJzZWN0cywgbGVmdCA9IFRSVUUpDQoNCmNhdCgi4pyTIEpvaW5lZCBBQ1MgY2Vuc3VzIGRhdGFcbiIpDQpjYXQoIiAgQ29udHJvbHM6IHBvdmVydHksIHJhY2UvZXRobmljaXR5LCB0ZW51cmUsIGluY29tZSwgaG91c2luZyBhZ2VcbiIpDQpgYGANCi0tLQ0KDQojIyBDb3VudCBSZWdyZXNzaW9uIE1vZGVscw0KDQpXaXRoIG91ciBzcGF0aWFsIGZlYXR1cmVzIGFuZCBjb250cm9scyBpbiBwbGFjZSwgSSBub3cgZml0IGNvdW50IHJlZ3Jlc3Npb24gbW9kZWxzIHRvIHByZWRpY3QgYnVyZ2xhcnkgcmlzayBmcm9tIHBvdGhvbGUgcGF0dGVybnMgYW5kIG5laWdoYm9yaG9vZCBjaGFyYWN0ZXJpc3RpY3MNCg0KLS0tDQoNCiMjIyBQcmVwYXJlIE1vZGVsaW5nIEZyYW1lDQoNCmBgYHtyIHByZXBhcmUtbW9kZWwsIG1lc3NhZ2U9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KIyBDcmVhdGUgY2xlYW4gbW9kZWxpbmcgZGF0YXNldA0KDQojIEhhbmRsZSBkdXBsaWNhdGUgY29sdW1ucyBmcm9tIGpvaW5zIChyZW5hbWUgLnkgdmVyc2lvbnMgdG8gY2xlYW4gbmFtZXMpDQojIE9ubHkgcmVuYW1lIGlmIC55IGNvbHVtbnMgZXhpc3QNCmlmICgicGN0X2JsYWNrLnkiICVpbiUgbmFtZXMoZmlzaG5ldCkpIHsNCiAgZmlzaG5ldCA8LSBmaXNobmV0ICU+JQ0KICAgIHJlbmFtZSgNCiAgICAgIHBjdF9ibGFjayA9IHBjdF9ibGFjay55LA0KICAgICAgcGN0X2hpc3AgPSBwY3RfaGlzcC55LA0KICAgICAgcGN0X3BvdmVydHkgPSBwY3RfcG92ZXJ0eS55LA0KICAgICAgcGN0X293bmVyID0gcGN0X293bmVyLnksDQogICAgICBtZWRfaW5jb21lID0gbWVkX2luY29tZS55LA0KICAgICAgbWVkX2hvdXNlX2FnZSA9IG1lZF9ob3VzZV9hZ2UueQ0KICAgICkgJT4lDQogICAgIyBSZW1vdmUgLnggdmVyc2lvbnMgaWYgdGhleSBleGlzdA0KICAgIHNlbGVjdCgtYW55X29mKGMoInBjdF9ibGFjay54IiwgInBjdF9oaXNwLngiLCAicGN0X3BvdmVydHkueCIsIA0KICAgICAgICAgICAgICAgICAgICAgInBjdF9vd25lci54IiwgIm1lZF9pbmNvbWUueCIsICJtZWRfaG91c2VfYWdlLngiKSkpDQp9DQoNCiMgQ3JlYXRlIG1vZGVsaW5nIGZyYW1lDQpmaXNobmV0X21vZGVsIDwtIGZpc2huZXQgJT4lDQogIHN0X2Ryb3BfZ2VvbWV0cnkoKSAlPiUNCiAgc2VsZWN0KA0KICAgIHVuaXF1ZUlELCBEaXN0cmljdCwNCiAgICBjb3VudEJ1cmdsYXJpZXMsDQogICAgY291bnRQb3Rob2xlcywgaG9sZXMzMTEubm4sIGRpc3RfdG9faG90c3BvdCwNCiAgICBwY3RfYmxhY2ssIHBjdF9oaXNwLCBwY3RfcG92ZXJ0eSwgcGN0X293bmVyLA0KICAgIG1lZF9pbmNvbWUsIG1lZF9ob3VzZV9hZ2UNCiAgKSAlPiUNCiAgbXV0YXRlKA0KICAgICMgU2NhbGUgcGVyY2VudGFnZXMgdG8gcHJvcG9ydGlvbnMgKDAtMSBzY2FsZSkNCiAgICBhY3Jvc3MoYyhwY3RfYmxhY2ssIHBjdF9oaXNwLCBwY3RfcG92ZXJ0eSwgcGN0X293bmVyKSwgfiAueCAvIDEwMCksDQogICAgIyBMb2ctdHJhbnNmb3JtIGluY29tZSB0byByZWR1Y2Ugc2tldw0KICAgIGxvZ19pbmNvbWUgPSBsb2cocG1heChtZWRfaW5jb21lLCAxKSkNCiAgKSAlPiUNCiAgZHJvcF9uYSgpDQoNCmNhdCgi4pyTIFByZXBhcmVkIG1vZGVsaW5nIGRhdGFzZXRcbiIpDQpjYXQoIiAgT2JzZXJ2YXRpb25zOiIsIG5yb3coZmlzaG5ldF9tb2RlbCksICJcbiIpDQpjYXQoIiAgVmFyaWFibGVzOiIsIG5jb2woZmlzaG5ldF9tb2RlbCksICJcbiIpDQpgYGANCi0tLQ0KDQojIyMgRml0IFBvaXNzb24gUmVncmVzc2lvbg0KDQpgYGB7ciBmaXQtcG9pc3Nvbn0NCm1vZGVsX3BvaXNzb24gPC0gZ2xtKA0KICBjb3VudEJ1cmdsYXJpZXMgfiBjb3VudFBvdGhvbGVzICsgaG9sZXMzMTEubm4gKyBkaXN0X3RvX2hvdHNwb3QgKw0KICAgIHBjdF9wb3ZlcnR5ICsgcGN0X293bmVyICsgbG9nX2luY29tZSArIG1lZF9ob3VzZV9hZ2UgKw0KICAgIHBjdF9ibGFjayArIHBjdF9oaXNwLA0KICBkYXRhID0gZmlzaG5ldF9tb2RlbCwNCiAgZmFtaWx5ID0gcG9pc3NvbihsaW5rID0gImxvZyIpDQopDQoNCg0Kc3VtbWFyeShtb2RlbF9wb2lzc29uKQ0KYGBgDQotLS0NCg0KIyMjIENoZWNrIGZvciBPdmVyZGlzcGVyc2lvbg0KDQpQb2lzc29uIHJlZ3Jlc3Npb24gYXNzdW1lcyBtZWFuID0gdmFyaWFuY2UuIFJlYWwgY291bnQgZGF0YSBvZnRlbiB2aW9sYXRlcyB0aGlzIChvdmVyZGlzcGVyc2lvbikuDQoNCmBgYHtyIGNoZWNrLW92ZXJkaXNwZXJzaW9ufQ0KIyBDYWxjdWxhdGUgZGlzcGVyc2lvbiBwYXJhbWV0ZXINCmRpc3BlcnNpb24gPC0gc3VtKHJlc2lkdWFscyhtb2RlbF9wb2lzc29uLCB0eXBlID0gInBlYXJzb24iKV4yKSAvIA0KICBtb2RlbF9wb2lzc29uJGRmLnJlc2lkdWFsDQoNCmNhdCgiRGlzcGVyc2lvbiB0ZXN0OlxuIikNCmNhdCgiICBEaXNwZXJzaW9uIHBhcmFtZXRlcjoiLCByb3VuZChkaXNwZXJzaW9uLCAyKSwgIlxuIikNCmNhdCgiICBSdWxlIG9mIHRodW1iOiA+MS41IHN1Z2dlc3RzIG92ZXJkaXNwZXJzaW9uXG4iKQ0KDQppZiAoZGlzcGVyc2lvbiA+IDEuNSkgew0KICBjYXQoIiAg4pqgIE92ZXJkaXNwZXJzaW9uIGRldGVjdGVkISBOZWdhdGl2ZSBCaW5vbWlhbCBtb2RlbCByZWNvbW1lbmRlZC5cbiIpDQp9IGVsc2Ugew0KICBjYXQoIiAg4pyTIERpc3BlcnNpb24gYWNjZXB0YWJsZSBmb3IgUG9pc3NvbiBtb2RlbC5cbiIpDQp9DQoNCmBgYA0KDQpUaGUgZGlzcGVyc2lvbiBwYXJhbWV0ZXIgaXMgYHIgcm91bmQoZGlzcGVyc2lvbiwgMilgLCB3ZWxsIGFib3ZlIHRoZSAxLjUgdGhyZXNob2xkLiBUaGlzIGluZGljYXRlcyBzdWJzdGFudGlhbCBvdmVyZGlzcGVyc2lvbuKAlHRoZSB2YXJpYW5jZSBpbiBidXJnbGFyeSBjb3VudHMgaXMgbXVjaCBsYXJnZXIgdGhhbiB0aGUgbWVhbiB3b3VsZCBzdWdnZXN0IHVuZGVyIFBvaXNzb24gYXNzdW1wdGlvbnMuIFRoaXMgb3ZlcmRpc3BlcnNpb24gY2FuIGFyaXNlIGZyb20gdW5vYnNlcnZlZCBoZXRlcm9nZW5laXR5LCBzcGF0aWFsIGNsdXN0ZXJpbmcsIG9yIG90aGVyIHNvdXJjZXMgb2YgZXh0cmEtUG9pc3NvbiB2YXJpYXRpb24uIFRoZSBOZWdhdGl2ZSBCaW5vbWlhbCBtb2RlbCBhZGRzIGEgZGlzcGVyc2lvbiBwYXJhbWV0ZXIgdG8gYWNjb21tb2RhdGUgdGhpcy4NCg0KIyMjIEZpdCBOZWdhdGl2ZSBCaW5vbWlhbCBSZWdyZXNzaW9uDQoNCmBgYHtyIGZpdC1uZWdiaW59DQojIEZpdCBOZWdhdGl2ZSBCaW5vbWlhbCBtb2RlbA0KbW9kZWxfbmIgPC0gZ2xtLm5iKA0KICBjb3VudEJ1cmdsYXJpZXMgfiBjb3VudFBvdGhvbGVzICsgaG9sZXMzMTEubm4gKyBkaXN0X3RvX2hvdHNwb3QgKw0KICAgIHBjdF9wb3ZlcnR5ICsgcGN0X293bmVyICsgbG9nX2luY29tZSArIG1lZF9ob3VzZV9hZ2UgKw0KICAgIHBjdF9ibGFjayArIHBjdF9oaXNwLA0KICBkYXRhID0gZmlzaG5ldF9tb2RlbA0KKQ0KIyBTdW1tYXJ5DQpzdW1tYXJ5KG1vZGVsX25iKQ0KDQojIENvbXBhcmUgQUlDIChsb3dlciBpcyBiZXR0ZXIpDQpjYXQoIlxuTW9kZWwgQ29tcGFyaXNvbjpcbiIpDQpjYXQoIlBvaXNzb24gQUlDOiIsIHJvdW5kKEFJQyhtb2RlbF9wb2lzc29uKSwgMSksICJcbiIpDQpjYXQoIk5lZ2F0aXZlIEJpbm9taWFsIEFJQzoiLCByb3VuZChBSUMobW9kZWxfbmIpLCAxKSwgIlxuIikNCg0KIyBTZWxlY3QgYmVzdCBtb2RlbCBmb3IgcHJlZGljdGlvbnMNCmZpbmFsX21vZGVsIDwtIG1vZGVsX25iDQpjYXQoIlxu4pyTIFNlbGVjdGVkIE5lZ2F0aXZlIEJpbm9taWFsIGFzIGZpbmFsIG1vZGVsXG4iKQ0KYGBgDQoNClRoZSBOZWdhdGl2ZSBCaW5vbWlhbCBtb2RlbCBoYXMgYSBzdWJzdGFudGlhbGx5IGxvd2VyIEFJQyBgciByb3VuZChBSUMobW9kZWxfbmIpLCAxKWAgaW5kaWNhdGluZyB0aGlzIGlzIGEgYmV0dGVyIG1vZGVsIGZpdC4gDQoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIFNwYXRpYWwgQ3Jvc3MtVmFsaWRhdGlvbiAoTE9HTy1DVikNCg0KVG8gYXNzZXNzIG1vZGVsIGdlbmVyYWxpemFiaWxpdHksIEkgaW1wbGVtZW50IExlYXZlLU9uZS1Hcm91cC1PdXQgKExPR08pIGNyb3NzLXZhbGlkYXRpb24gdXNpbmcgcG9saWNlIGRpc3RyaWN0cyBhcyBzcGF0aWFsIGZvbGRzLiBUaGlzIHRlc3RzIHdoZXRoZXIgdGhlIG1vZGVsIHRyYWluZWQgb24gbW9zdCBvZiB0aGUgY2l0eSBjYW4gYWNjdXJhdGVseSBwcmVkaWN0IGJ1cmdsYXJpZXMgaW4gaGVsZC1vdXQgZGlzdHJpY3RzLCBzaW11bGF0aW5nIGEgcmVhbC13b3JsZCBkZXBsb3ltZW50IHNjZW5hcmlvIHdoZXJlIHdlIG1pZ2h0IHdhbnQgdG8gZm9yZWNhc3QgY3JpbWUgaW4gYSBuZXcgYXJlYS4NCg0KYGBge3Igc3BhdGlhbC1jdn0NCmRpc3RyaWN0cyA8LSB1bmlxdWUoZmlzaG5ldF9tb2RlbCREaXN0cmljdCkNCmN2X3Jlc3VsdHMgPC0gZGF0YS5mcmFtZSgpDQoNCmZvciAoaSBpbiBzZXFfYWxvbmcoZGlzdHJpY3RzKSkgew0KICB0ZXN0X2Rpc3RyaWN0IDwtIGRpc3RyaWN0c1tpXQ0KICANCiAgIyBTcGxpdCBkYXRhIHNwYXRpYWxseQ0KICB0cmFpbl9kYXRhIDwtIGZpbHRlcihmaXNobmV0X21vZGVsLCBEaXN0cmljdCAhPSB0ZXN0X2Rpc3RyaWN0KQ0KICB0ZXN0X2RhdGEgPC0gZmlsdGVyKGZpc2huZXRfbW9kZWwsIERpc3RyaWN0ID09IHRlc3RfZGlzdHJpY3QpDQogIA0KICAjIEZpdCBtb2RlbCBvbiB0cmFpbmluZyBmb2xkcw0KICBtX2N2IDwtIGdsbS5uYigNCiAgICBjb3VudEJ1cmdsYXJpZXMgfiBjb3VudFBvdGhvbGVzICsgaG9sZXMzMTEubm4gKyBkaXN0X3RvX2hvdHNwb3QgKw0KICAgICAgcGN0X3BvdmVydHkgKyBwY3Rfb3duZXIgKyBsb2dfaW5jb21lICsgbWVkX2hvdXNlX2FnZSArDQogICAgICBwY3RfYmxhY2sgKyBwY3RfaGlzcCwNCiAgICBkYXRhID0gdHJhaW5fZGF0YQ0KICApDQogIA0KICAjIFByZWRpY3Qgb24gaGVsZC1vdXQgZGlzdHJpY3QNCiAgcHJlZGljdGlvbnMgPC0gcHJlZGljdChtX2N2LCBuZXdkYXRhID0gdGVzdF9kYXRhLCB0eXBlID0gInJlc3BvbnNlIikNCiAgDQogICMgQ2FsY3VsYXRlIGVycm9ycw0KICBtYWUgPC0gbWVhbihhYnModGVzdF9kYXRhJGNvdW50QnVyZ2xhcmllcyAtIHByZWRpY3Rpb25zKSkNCiAgcm1zZSA8LSBzcXJ0KG1lYW4oKHRlc3RfZGF0YSRjb3VudEJ1cmdsYXJpZXMgLSBwcmVkaWN0aW9ucyleMikpDQogIA0KICBjdl9yZXN1bHRzIDwtIHJiaW5kKGN2X3Jlc3VsdHMsIGRhdGEuZnJhbWUoDQogICAgRGlzdHJpY3QgPSB0ZXN0X2Rpc3RyaWN0LA0KICAgIG5fdGVzdCA9IG5yb3codGVzdF9kYXRhKSwNCiAgICBtYWUgPSBtYWUsDQogICAgcm1zZSA9IHJtc2UNCiAgKSkNCiAgDQogIGNhdCgiRm9sZCIsIGksICIvIiwgbGVuZ3RoKGRpc3RyaWN0cyksICItIERpc3RyaWN0IiwgdGVzdF9kaXN0cmljdCwNCiAgICAgICItIE1BRToiLCByb3VuZChtYWUsIDIpLCAiXG4iKQ0KfQ0KDQojIE92ZXJhbGwgcmVzdWx0cw0KY2F0KCJcbuKckyBDcm9zcy1WYWxpZGF0aW9uIENvbXBsZXRlXG4iKQ0KY2F0KCJNZWFuIE1BRToiLCByb3VuZChtZWFuKGN2X3Jlc3VsdHMkbWFlKSwgMiksICJcbiIpDQpjYXQoIk1lYW4gUk1TRToiLCByb3VuZChtZWFuKGN2X3Jlc3VsdHMkcm1zZSksIDIpLCAiXG4iKQ0KYGBgDQoNCmBgYHtyIGN2LXJlc3VsdHMtdGFibGV9DQojIFNob3cgcmVzdWx0cw0KY3ZfcmVzdWx0cyAlPiUNCiAgYXJyYW5nZShkZXNjKG1hZSkpICU+JQ0KICBrYWJsZSgNCiAgICBkaWdpdHMgPSAyLA0KICAgIGNhcHRpb24gPSAiTE9HTyBDViBSZXN1bHRzIGJ5IERpc3RyaWN0IChOZWdhdGl2ZSBCaW5vbWlhbCBNb2RlbCkiLA0KICAgIGNvbC5uYW1lcyA9IGMoIkRpc3RyaWN0IiwgIk4gVGVzdCBDZWxscyIsICJNQUUiLCAiUk1TRSIpDQogICkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpDQpgYGANCg0KVGhlIGhhcmRlc3QtdG8tcHJlZGljdCBkaXN0cmljdHMgYXJlIGByIGN2X3Jlc3VsdHMkRGlzdHJpY3Rbd2hpY2gubWF4KGN2X3Jlc3VsdHMkbWFlKV1gIChNQUU6IGByIHJvdW5kKG1heChjdl9yZXN1bHRzJG1hZSksIDIpYCkgYW5kIGByIGN2X3Jlc3VsdHMkRGlzdHJpY3Rbb3JkZXIoY3ZfcmVzdWx0cyRtYWUsIGRlY3JlYXNpbmc9VFJVRSlbMl1dYCAoTUFFOiBgciByb3VuZChzb3J0KGN2X3Jlc3VsdHMkbWFlLCBkZWNyZWFzaW5nPVRSVUUpWzJdLCAyKWApLiBUaGVzZSBkaXN0cmljdHMgbGlrZWx5IGhhdmUgdW5pcXVlIGNoYXJhY3RlcmlzdGljcyBub3QgY2FwdHVyZWQgYnkgdGhlIGNpdHl3aWRlIG1vZGVs4oCUcGVyaGFwcyBkaWZmZXJlbnQgYnVyZ2xhcnktcG90aG9sZSByZWxhdGlvbnNoaXBzLCBkaXN0aW5jdCBkZW1vZ3JhcGhpY3MsIG9yIHJlcG9ydGluZyBwYXR0ZXJucy4NCg0KVGhlIG1lYW4gTUFFIG9mIGByIHJvdW5kKG1lYW4oY3ZfcmVzdWx0cyRtYWUpLCAyKWAgYnVyZ2xhcmllcyBwZXIgY2VsbCBpbmRpY2F0ZXMgdGhhdCwgb24gYXZlcmFnZSwgb3VyIHByZWRpY3Rpb25zIGFyZSBvZmYgYnkgYWJvdXQgYHIgcm91bmQobWVhbihjdl9yZXN1bHRzJG1hZSkpYCBpbmNpZGVudHMgcGVyIDUwMG0gY2VsbC4gR2l2ZW4gdGhhdCBtZWRpYW4gYnVyZ2xhcnkgY291bnQgaXMgYHIgbWVkaWFuKGZpc2huZXQkY291bnRCdXJnbGFyaWVzKWAsIHRoaXMgcmVwcmVzZW50cyBtb2RlcmF0ZSBwcmVkaWN0aXZlIGFjY3VyYWN5Lg0KDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyBNb2RlbCBQcmVkaWN0aW9ucyBhbmQgQ29tcGFyaXNvbg0KDQojIyMgR2VuZXJhdGUgRmluYWwgUHJlZGljdGlvbnMNCg0KSSBub3cgcmVmaXQgdGhlIG1vZGVsIG9uIGFsbCAyMDE3IGRhdGEgdG8gZ2VuZXJhdGUgZmluYWwgcHJlZGljdGlvbnMgZm9yIGNvbXBhcmlzb24gd2l0aCBhIHNpbXBsZSBLREUgYmFzZWxpbmUuDQoNCmBgYHtyIGZpbmFsLXByZWRpY3Rpb25zfQ0KIyBGaXQgZmluYWwgbW9kZWwgb24gYWxsIGRhdGEgKGFscmVhZHkgZG9uZSBhcyAnZmluYWxfbW9kZWwnKQ0KDQojIEFkZCBOQiBwcmVkaWN0aW9ucyBiYWNrIHRvIGZpc2huZXQNCmZpc2huZXQgPC0gZmlzaG5ldCAlPiUNCiAgbXV0YXRlKA0KICAgIHByZWRpY3Rpb25fbmIgPSBwcmVkaWN0KGZpbmFsX21vZGVsLCBmaXNobmV0X21vZGVsLCB0eXBlID0gInJlc3BvbnNlIilbbWF0Y2godW5pcXVlSUQsIGZpc2huZXRfbW9kZWwkdW5pcXVlSUQpXQ0KICApDQoNCiMgTm9ybWFsaXplIEtERSB0byBzYW1lIHNjYWxlIGFzIGNvdW50cw0Ka2RlX3N1bSA8LSBzdW0oZmlzaG5ldCRrZGVfdmFsdWUsIG5hLnJtID0gVFJVRSkNCmNvdW50X3N1bSA8LSBzdW0oZmlzaG5ldCRjb3VudEJ1cmdsYXJpZXMsIG5hLnJtID0gVFJVRSkNCg0KZmlzaG5ldCA8LSBmaXNobmV0ICU+JQ0KICBtdXRhdGUoDQogICAgcHJlZGljdGlvbl9rZGUgPSAoa2RlX3ZhbHVlIC8ga2RlX3N1bSkgKiBjb3VudF9zdW0NCiAgKQ0KDQpjYXQoIuKckyBHZW5lcmF0ZWQgcHJlZGljdGlvbnNcbiIpDQpjYXQoIiAgUHJlZGljdGlvbiByYW5nZSAoTkIpOiIsIA0KICAgIHJvdW5kKG1pbihmaXNobmV0JHByZWRpY3Rpb25fbmIsIG5hLnJtPVRSVUUpLCAyKSwgInRvIiwNCiAgICByb3VuZChtYXgoZmlzaG5ldCRwcmVkaWN0aW9uX25iLCBuYS5ybT1UUlVFKSwgMiksICJcbiIpDQpgYGANCi0tLQ0KDQojIyMgVmlzdWFsaXplIFByZWRpY3Rpb25zIHZzLiBBY3R1YWxzDQoNCmBgYHtyIGNvbXBhcmUtbW9kZWxzLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NH0NCiMgQ3JlYXRlIHRocmVlIG1hcHMNCnAxIDwtIGdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gZmlzaG5ldCwgYWVzKGZpbGwgPSBjb3VudEJ1cmdsYXJpZXMpLCBjb2xvciA9IE5BKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG5hbWUgPSAiQ291bnQiLCBvcHRpb24gPSAicGxhc21hIiwgbGltaXRzID0gYygwLCAxNSkpICsNCiAgbGFicyh0aXRsZSA9ICJBY3R1YWwgQnVyZ2xhcmllcyIpICsNCiAgdGhlbWVfY3JpbWUoKQ0KDQpwMiA8LSBnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IGZpc2huZXQsIGFlcyhmaWxsID0gcHJlZGljdGlvbl9uYiksIGNvbG9yID0gTkEpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MobmFtZSA9ICJQcmVkaWN0ZWQiLCBvcHRpb24gPSAicGxhc21hIiwgbGltaXRzID0gYygwLCAxNSkpICsNCiAgbGFicyh0aXRsZSA9ICJNb2RlbCBQcmVkaWN0aW9ucyAoTmVnLiBCaW5vbWlhbCkiKSArDQogIHRoZW1lX2NyaW1lKCkNCg0KcDMgPC0gZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSBmaXNobmV0LCBhZXMoZmlsbCA9IHByZWRpY3Rpb25fa2RlKSwgY29sb3IgPSBOQSkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhuYW1lID0gIlByZWRpY3RlZCIsIG9wdGlvbiA9ICJwbGFzbWEiLCBsaW1pdHMgPSBjKDAsIDE1KSkgKw0KICBsYWJzKHRpdGxlID0gIktERSBCYXNlbGluZSBQcmVkaWN0aW9ucyIpICsNCiAgdGhlbWVfY3JpbWUoKQ0KDQpwMSArIHAyICsgcDMgKw0KICBwbG90X2Fubm90YXRpb24oDQogICAgdGl0bGUgPSAiQWN0dWFsIHZzLiBQcmVkaWN0ZWQgQnVyZ2xhcmllcyAoMjAxNykiLA0KICAgIHN1YnRpdGxlID0gIkRvZXMgb3VyIGNvbXBsZXggbW9kZWwgb3V0cGVyZm9ybSBzaW1wbGUgS0RFPyINCiAgKQ0KYGBgDQoNClRoZSBOZWdhdGl2ZSBCaW5vbWlhbCBtb2RlbCBwcm9kdWNlcyBhIHNtb290aGVyLCBtb3JlIGludGVycHJldGFibGUgcmlzayBzdXJmYWNlIHRoYXQgcmVmbGVjdHMgYm90aCBzcGF0aWFsIHBhdHRlcm5zIGFuZCBuZWlnaGJvcmhvb2Qgc3RydWN0dXJlLiBUaGUgS0RFIGJhc2VsaW5lIGNsb3NlbHkgZm9sbG93cyBoaXN0b3JpY2FsIGhvdHNwb3RzIGJ1dCBvZmZlcnMgbGVzcyBudWFuY2UsIG1haW5seSBmb2xsb3dpbmcgcHJldmlvdXMgYnVyZ2xhcnkgcmVwb3J0IGxvY2F0aW9ucy4gVGhlIG1vZGVsLCBieSBpbmNvcnBvcmF0aW5nIHBvdGhvbGVzIGFuZCBkZW1vZ3JhcGhpY3MsIGNhbiBpZGVudGlmeSBhdC1yaXNrIGFyZWFzIGJhc2VkIG9uIHN0cnVjdHVyYWwgY2hhcmFjdGVyaXN0aWNzIGV2ZW4gaW4gY2VsbHMgd2l0aCBsb3cgaGlzdG9yaWNhbCBidXJnbGFyeSBjb3VudHMuDQoNCiMjIyBDb21wYXJlIFBlcmZvcm1hbmNlIE1ldHJpY3MNCg0KYGBge3IgbW9kZWwtY29tcGFyaXNvbi1tZXRyaWNzfQ0KIyBDYWxjdWxhdGUgcGVyZm9ybWFuY2UgbWV0cmljcw0KY29tcGFyaXNvbiA8LSBmaXNobmV0ICU+JQ0KICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lDQogIGZpbHRlcighaXMubmEocHJlZGljdGlvbl9uYiksICFpcy5uYShwcmVkaWN0aW9uX2tkZSkpICU+JQ0KICBzdW1tYXJpemUoDQogICAgbW9kZWxfbWFlID0gbWVhbihhYnMoY291bnRCdXJnbGFyaWVzIC0gcHJlZGljdGlvbl9uYikpLA0KICAgIG1vZGVsX3Jtc2UgPSBzcXJ0KG1lYW4oKGNvdW50QnVyZ2xhcmllcyAtIHByZWRpY3Rpb25fbmIpXjIpKSwNCiAgICBrZGVfbWFlID0gbWVhbihhYnMoY291bnRCdXJnbGFyaWVzIC0gcHJlZGljdGlvbl9rZGUpKSwNCiAgICBrZGVfcm1zZSA9IHNxcnQobWVhbigoY291bnRCdXJnbGFyaWVzIC0gcHJlZGljdGlvbl9rZGUpXjIpKQ0KICApDQoNCmNvbXBhcmlzb24gJT4lDQogIHBpdm90X2xvbmdlcihldmVyeXRoaW5nKCksIG5hbWVzX3RvID0gIm1ldHJpYyIsIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JQ0KICBzZXBhcmF0ZShtZXRyaWMsIGludG8gPSBjKCJhcHByb2FjaCIsICJtZXRyaWMiKSwgc2VwID0gIl8iKSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IG1ldHJpYywgdmFsdWVzX2Zyb20gPSB2YWx1ZSkgJT4lDQogIGthYmxlKA0KICAgIGRpZ2l0cyA9IDIsDQogICAgY2FwdGlvbiA9ICJJbi1TYW1wbGUgUGVyZm9ybWFuY2U6IE1vZGVsIHZzLiBLREUgQmFzZWxpbmUiDQogICkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpDQoNCmBgYA0KDQpUaGUgS0RFIGJhc2VsaW5lIGhhcyBsb3dlciBpbi1zYW1wbGUgUk1TRSB0aGFuIHRoZSBOZWdhdGl2ZSBCaW5vbWlhbCBtb2RlbC4gVGhpcyBpcyBub3Qgc3VycHJpc2luZywgYmVjYXVzZSBLREUgaXMgYXBwbGllZCBkaXJlY3RseSB0byB0aGUgb2JzZXJ2ZWQgY3JpbWUgbG9jYXRpb25zLCBtZWFuaW5nLCAid2hlcmUgY3JpbWUgaGFwcGVuZWQgYmVmb3JlLCBpdCB3aWxsIGhhcHBlbiBhZ2FpbiIuIEFzIGEgcmVzdWx0LCBLREUgcGVyZm9ybXMgZXh0cmVtZWx5IHdlbGwgaW4tc2FtcGxlIGJ1dCBoYXMgbGltaXRlZCBhYmlsaXR5IHRvIGdlbmVyYWxpemUgc3BhdGlhbCBwYXR0ZXJucyB0byBuZXcgbG9jYXRpb25zLiBJbiBjb250cmFzdCwgdGhlIHJlZ3Jlc3Npb24gbW9kZWwgaW5jb3Jwb3JhdGVzIHN0cnVjdHVyYWwgZmVhdHVyZXMgYW5kIHRodXMgcHJpb3JpdGl6ZXMgb3V0LW9mLXNhbXBsZSBwcmVkaWN0aXZlIHBlcmZvcm1hbmNlLiBUaGUgcmVhbCB0ZXN0IG9mIG1vZGVsIHF1YWxpdHkgY29tZXMgZnJvbSB0aGUgc3BhdGlhbCBhbmQgdGVtcG9yYWwgdmFsaWRhdGlvbiByZXN1bHRzLCB3aGVyZSBLREUgdHlwaWNhbGx5IHBlcmZvcm1zIHdvcnNlLg0KDQojIyMgRXJyb3IgTWFwcw0KIA0KYGBge3IgZXJyb3ItbWFwcywgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTV9DQpmaXNobmV0IDwtIGZpc2huZXQgJT4lDQogIG11dGF0ZSgNCiAgICBlcnJvcl9uYiA9IGNvdW50QnVyZ2xhcmllcyAtIHByZWRpY3Rpb25fbmIsDQogICAgZXJyb3Jfa2RlID0gY291bnRCdXJnbGFyaWVzIC0gcHJlZGljdGlvbl9rZGUNCiAgKQ0KDQpwMSA8LSBnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IGZpc2huZXQsIGFlcyhmaWxsID0gZXJyb3JfbmIpLCBjb2xvciA9IE5BKSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKA0KICAgIG5hbWUgPSAiRXJyb3IiLA0KICAgIGxvdyA9ICIjMjE2NmFjIiwNCiAgICBtaWQgPSAiZ3JleTkwIiwNCiAgICBoaWdoID0gIiNiMjE4MmIiLA0KICAgIG1pZHBvaW50ID0gMCwNCiAgICBsaW1pdHMgPSBjKC0xMCwgMTApDQogICkgKw0KICBsYWJzKHRpdGxlID0gIk5CIE1vZGVsIEVycm9ycyIpICsNCiAgdGhlbWVfY3JpbWUoKQ0KDQpwMiA8LSBnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IGZpc2huZXQsIGFlcyhmaWxsID0gZXJyb3Jfa2RlKSwgY29sb3IgPSBOQSkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50MigNCiAgICBuYW1lID0gIkVycm9yIiwNCiAgICBsb3cgPSAiIzIxNjZhYyIsDQogICAgbWlkID0gImdyZXk5MCIsDQogICAgaGlnaCA9ICIjYjIxODJiIiwNCiAgICBtaWRwb2ludCA9IDAsDQogICAgbGltaXRzID0gYygtMTAsIDEwKQ0KICApICsNCiAgbGFicyh0aXRsZSA9ICJLREUgQmFzZWxpbmUgRXJyb3JzIikgKw0KICB0aGVtZV9jcmltZSgpDQoNCnAxICsgcDIgKw0KICBwbG90X2Fubm90YXRpb24oDQogICAgdGl0bGUgPSAiUHJlZGljdGlvbiBFcnJvcnM6IEFjdHVhbCBtaW51cyBQcmVkaWN0ZWQiLA0KICAgIHN1YnRpdGxlID0gIlJlZCA9IHVuZGVycHJlZGljdGlvbiwgQmx1ZSA9IG92ZXJwcmVkaWN0aW9uIg0KICApDQpgYGANCg0KQm90aCBhcHByb2FjaGVzIHN0cnVnZ2xlIGluIHRoZSBzYW1lIGhpZ2gtY3JpbWUgY29ycmlkb3JzIG9uIHRoZSBTb3V0aCBhbmQgV2VzdCBzaWRlcywgd2hlcmUgdGhleSB0ZW5kIHRvIHVuZGVycHJlZGljdCAocmVkKS4gVGhlIG1vZGVsIHNob3dzIHNsaWdodGx5IG1vcmUgc3BhdGlhbCBzdHJ1Y3R1cmUgaW4gaXRzIGVycm9yc+KAlHN5c3RlbWF0aWMgb3ZlcnByZWRpY3Rpb24gaW4gc29tZSBsb3ctY3JpbWUgYXJlYXMgYW5kIHVuZGVycHJlZGljdGlvbiBpbiBlbWVyZ2luZyBob3RzcG90cy4gVGhpcyBzdWdnZXN0cyByb29tIGZvciBpbXByb3ZlbWVudCB0aHJvdWdoIGFkZGl0aW9uYWwgc3BhdGlhbCBmZWF0dXJlcyBvciBpbnRlcmFjdGlvbiB0ZXJtcy4NCg0KIyMgU3VtbWFyeSBTdGF0aXN0aWNzIGFuZCBGaW5kaW5ncw0KDQojIyMgTW9kZWwgUGVyZm9ybWFuY2UgU3VtbWFyeQ0KDQpgYGB7ciBzdW1tYXJ5LXRhYmxlfQ0Kc3VtbWFyeV9zdGF0cyA8LSBkYXRhLmZyYW1lKA0KICBNZXRyaWMgPSBjKA0KICAgICJNZWFuIExPR08gQ1YgTUFFIiwNCiAgICAiTWVhbiBMT0dPIENWIFJNU0UiLA0KICAgICJJbi1zYW1wbGUgTUFFIChNb2RlbCkiLA0KICAgICJJbi1zYW1wbGUgTUFFIChLREUpIiwNCiAgICAiQUlDIChOZWdhdGl2ZSBCaW5vbWlhbCkiLA0KICAgICJOdW1iZXIgb2YgY2VsbHMiLA0KICAgICJDZWxscyB3aXRoIDAgYnVyZ2xhcmllcyIsDQogICAgIk1vc3QgcHJlZGljdGl2ZSB2YXJpYWJsZSINCiAgKSwNCiAgVmFsdWUgPSBjKA0KICAgIHJvdW5kKG1lYW4oY3ZfcmVzdWx0cyRtYWUpLCAyKSwNCiAgICByb3VuZChtZWFuKGN2X3Jlc3VsdHMkcm1zZSksIDIpLA0KICAgIHJvdW5kKGNvbXBhcmlzb24kbW9kZWxfbWFlLCAyKSwNCiAgICByb3VuZChjb21wYXJpc29uJGtkZV9tYWUsIDIpLA0KICAgIHJvdW5kKEFJQyhmaW5hbF9tb2RlbCksIDIpLA0KICAgIG5yb3coZmlzaG5ldCksDQogICAgcGFzdGUwKHN1bShmaXNobmV0JGNvdW50QnVyZ2xhcmllcyA9PSAwKSwgIiAoIiwgDQogICAgICAgICAgIHJvdW5kKDEwMCpzdW0oZmlzaG5ldCRjb3VudEJ1cmdsYXJpZXMgPT0gMCkvbnJvdyhmaXNobmV0KSwgMiksICIlKSIpLA0KICAgIG5hbWVzKHdoaWNoLm1heChhYnMoY29lZihmaW5hbF9tb2RlbClbLTFdKSkpDQogICkNCikNCg0Kc3VtbWFyeV9zdGF0cyAlPiUNCiAga2FibGUoDQogICAgY2FwdGlvbiA9ICJNb2RlbCBQZXJmb3JtYW5jZSBTdW1tYXJ5IFN0YXRpc3RpY3MiLA0KICAgIGNvbC5uYW1lcyA9IGMoIk1ldHJpYyIsICJWYWx1ZSIpDQogICkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpDQpgYGANCg0KIyMjIEtleSBGaW5kaW5ncw0KDQoqKlRlY2huaWNhbCBQZXJmb3JtYW5jZToqKg0KDQotIENyb3NzLXZhbGlkYXRpb24gTUFFOiBgciByb3VuZChtZWFuKGN2X3Jlc3VsdHMkbWFlKSwgMilgIGJ1cmdsYXJpZXMgcGVyIGNlbGwNCi0gTW9kZWwgdnMuIEtERTogS0RFIHBlcmZvcm1zIHNsaWdodGx5IGJldHRlciBpbi1zYW1wbGUgKE1BRSBgciByb3VuZChjb21wYXJpc29uJGtkZV9tYWUsIDIpYCB2cy4gYHIgcm91bmQoY29tcGFyaXNvbiRtb2RlbF9tYWUsIDIpYCksIGJ1dCB0aGUgTkIgbW9kZWwgaXMgc3RpbGwgdXNlZnVsIGJlY2F1c2UgaXQgbGV0cyB1cyBzZWUgaG93IGRpZmZlcmVudCBuZWlnaGJvcmhvb2QgZmFjdG9ycyByZWxhdGUgdG8gYnVyZ2xhcnkgcGF0dGVybnMNCg0KKipTcGF0aWFsIFBhdHRlcm5zOioqDQoNCi0gQnVyZ2xhcmllcyBhcmUgaGlnaGx5IGNsdXN0ZXJlZCBpbiBzb3V0aCBhbmQgd2VzdCBhcmVhcw0KLSBQb3Rob2xlcyBjbHVzdGVyIGRpZmZlcmVudGx5LCBtb3JlIGluIG5vcnRoLWNlbnRyYWwgYXJlYQ0KLSBvY2FsIE1vcmFu4oCZcyBJIGhvdCBzcG90cyBzZWVtIHRvIGxpbmUgdXAgbW9yZSB3aXRoIGFyZWFzIG9mIGRpc2ludmVzdG1lbnQgYW5kIGluZnJhc3RydWN0dXJlIGlzc3VlcyB0aGFuIHdpdGggY3JpbWUgc3BlY2lmaWNhbGx5DQoNCioqTW9kZWwgTGltaXRhdGlvbnM6KioNCg0KLSBPdmVyZGlzcGVyc2lvbjogWWVzIChkaXNwZXJzaW9uID0gYHIgcm91bmQoZGlzcGVyc2lvbiwgMilgKSwgYWRkcmVzc2VkIHZpYSBOZWdhdGl2ZSBCaW5vbWlhbA0KLSBTcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiBpbiByZXNpZHVhbHM6IExpa2VseSBwcmVzZW50LCBhcyBzaG93biBieSBlcnJvciBwYXR0ZXJucw0KLSBDZWxscyB3aXRoIHplcm8gY291bnRzOiBgciByb3VuZCgxMDAqc3VtKGZpc2huZXQkY291bnRCdXJnbGFyaWVzID09IDApL25yb3coZmlzaG5ldCksIDEpYCUgd2hpY2ggaXMgdHlwaWNhbCBpbiBjcmltZSBkYXRhIGFuZCBzb21ldGhpbmcgdGhlIG1vZGVsIGhhcyB0byBhY2NvdW50IGZvcg0KDQoqKlRvIFN1bW1hcml6ZSoqOg0KDQpQb3Rob2xlcyBhbmQgYnVyZ2xhcmllcyBoYXBwZW4gaW4gdGhlIHNhbWUgcGxhY2VzIG5vdCBiZWNhdXNlIG9mIGNhdXNhdGlvbiwgYnV0IGJlY2F1c2UgYm90aCBhcmUgc3ltcHRvbXMgb2YgZGVlcGVyIG5laWdoYm9yaG9vZCBpc3N1ZXMuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyBDSEFMTEVOR0UgVEFTSzogVGVtcG9yYWwgVmFsaWRhdGlvbiAoMjAxOCkNCg0KVGhlIGZpbmFsIHRlc3Qgb2YgbW9kZWwgcXVhbGl0eSBpcyBwcmVkaWN0aW5nIHRoZSBmdXR1cmUuIEkgdXNlIHRoZSAyMDE3LXRyYWluZWQgbW9kZWwgdG8gZm9yZWNhc3QgMjAxOCBidXJnbGFyeSBwYXR0ZXJucywgYXNzZXNzaW5nIHdoZXRoZXIgc3BhdGlhbCBzdHJ1Y3R1cmUgYW5kIHBvdGhvbGUgYXNzb2NpYXRpb25zIHJlbWFpbiBzdGFibGUgb3ZlciB0aW1lLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIExvYWQgRGF0YSBhbmQgQWdncmVnYXRlIHRvIHRoZSBGaXNobmV0DQoNCmBgYHtyIDIwMTgtYnVyZy1maXNoLCB3YXJuaW5nPUZBTFNFLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFfQ0KIyBRdWVyeSBmaWx0ZXJlZCBmb3IgZm9yY2libGUgZW50cnkgYnVyZ2xhcmllcyBpbiAyMDE4DQpidXJnMjAxOCA8LSByZWFkX2NzdihoZXJlKCJsYWJzL2xhYjQvZGF0YS9idXJnbGFyaWVzXzIwMTguY3N2IikpJT4lDQogIGZpbHRlcighaXMubmEoTGF0aXR1ZGUpLCAhaXMubmEoTG9uZ2l0dWRlKSklPiUNCiAgZmlsdGVyKERlc2NyaXB0aW9uID09ICJGT1JDSUJMRSBFTlRSWSIpJT4lDQogIHN0X2FzX3NmKGNvb3JkcyA9IGMoIkxvbmdpdHVkZSIsICJMYXRpdHVkZSIpLCBjcnMgPSA0MzI2KSAlPiUNCiAgc3RfdHJhbnNmb3JtKCdFU1JJOjEwMjI3MScpDQoNCiMgQWdncmVnYXRlIHRvIGZpc2huZXQNCmJ1cmcyMDE4X2Zpc2ggPC0gc3Rfam9pbihidXJnMjAxOCwgZmlzaG5ldCwgam9pbiA9IHN0X3dpdGhpbiklPiUNCiAgc3RfZHJvcF9nZW9tZXRyeSgpICU+JQ0KICBncm91cF9ieSh1bmlxdWVJRCkgJT4lDQogIHN1bW1hcml6ZShjb3VudEJ1cmdsYXJpZXMyMDE4ID0gbigpKQ0KDQpmaXNobmV0IDwtIGZpc2huZXQgJT4lDQogIGxlZnRfam9pbihidXJnMjAxOF9maXNoLCBieSA9ICJ1bmlxdWVJRCIpIA0KDQpjYXQoIuKckyBMb2FkZWQgMjAxOCBidXJnbGFyaWVzXG4iKQ0KY2F0KCIgIFRvdGFsIGluY2lkZW50czoiLCBucm93KGJ1cmcyMDE4KSwgIlxuIikNCmNhdCgiICBEYXRlIHJhbmdlOiIsIG1pbihidXJnMjAxOCREYXRlLCBuYS5ybT1UUlVFKSwgInRvIiwgbWF4KGJ1cmcyMDE4JERhdGUsIG5hLnJtPVRSVUUpLCAiXG4iKQ0KYGBgDQoNClRoZSBncmlkIGFjdHMgYXMgYSBjb25zaXN0ZW50IGdlb2dyYXBoaWMgZnJhbWUgYWNyb3NzIHllYXJzLiBCeSBhZ2dyZWdhdGluZyAyMDE3IGFuZCAyMDE4IGJ1cmdsYXJpZXMgdG8gdGhlIHNhbWUgY2VsbHMsIEkgY2FuIGRpcmVjdGx5IGNvbXBhcmUgcHJlZGljdGlvbnMgdnMuIGFjdHVhbCBhdCBtYXRjaGluZyBsb2NhdGlvbnMuIFRoZSBwb3Rob2xlcyBhbmQgY2Vuc3VzIGZlYXR1cmVzIGFyZSBmcm9tIDIwMTcsIHdoaWNoIHRlc3RzIHdoZXRoZXIgdGhlc2Ugc3RydWN0dXJhbCBwcmVkaWN0b3JzIGFwcGx5IHRvIHRoZSAyMDE4IGRhdGEuDQoNCiMjIyBVc2UgMjAxNyBNb2RlbCB0byBQcmVkaWN0IDIwMTggQ291bnRzDQoNCmBgYHtyIHByZWRpY3QtMjAxOH0NCiMgTWFrZSBhIGNvbHVtbiB0byBzdG9yZSBwcmVkaWN0ZWQgZGF0YSBvdXRjb21lcw0KZmlzaG5ldCA8LSBmaXNobmV0ICU+JQ0KICBtdXRhdGUoDQogICAgcHJlZGljdGlvbl8yMDE4ID0gcHJlZGljdChmaW5hbF9tb2RlbCwgbmV3ZGF0YSA9IGZpc2huZXRfbW9kZWwsIHR5cGUgPSAicmVzcG9uc2UiKVttYXRjaCh1bmlxdWVJRCwgZmlzaG5ldF9tb2RlbCR1bmlxdWVJRCldDQogICkNCg0KY2F0KCLinJMgR2VuZXJhdGVkIDIwMTggcHJlZGljdGlvbnMgZnJvbSAyMDE3IG1vZGVsXG4iKQ0KYGBgDQoNCiMjIyBDYWxjdWxhdGUgVGVtcG9yYWwgVmFsaWRhdGlvbiBNZXRyaWNzDQoNCmBgYHtyIHRlbXBvcmFsLW1ldHJpY3N9DQojRml4aW5nIGNvbHVtbiBuYW1lDQpmaXNobmV0IDwtIGZpc2huZXQgJT4lDQogbXV0YXRlKGNvdW50QnVyZ2xhcmllczIwMTggPSByZXBsYWNlX25hKGNvdW50QnVyZ2xhcmllczIwMTgsIDApKQ0KDQojIENhbGN1bGF0ZSBNQUUgYW5kIFJNU0UgZm9yIDIwMTggcHJlZGljdGlvbnMNCnRlbXBvcmFsX21ldHJpY3MgPC0gZmlzaG5ldCAlPiUNCiAgc3RfZHJvcF9nZW9tZXRyeSgpICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGNvdW50QnVyZ2xhcmllczIwMTgpLCAhaXMubmEocHJlZGljdGlvbl8yMDE4KSkgJT4lDQogIHN1bW1hcml6ZSgNCiAgICB0ZW1wb3JhbF9tYWUgPSBtZWFuKGFicyhjb3VudEJ1cmdsYXJpZXMyMDE4IC0gcHJlZGljdGlvbl8yMDE4KSksDQogICAgdGVtcG9yYWxfcm1zZSA9IHNxcnQobWVhbigoY291bnRCdXJnbGFyaWVzMjAxOCAtIHByZWRpY3Rpb25fMjAxOCleMikpDQogICkNCmNhdCgiVGVtcG9yYWwgVmFsaWRhdGlvbiAoMjAxOCk6XG4iKQ0KY2F0KCIgIE1BRToiLCByb3VuZCh0ZW1wb3JhbF9tZXRyaWNzJHRlbXBvcmFsX21hZSwgMiksICJcbiIpDQpjYXQoIiAgUk1TRToiLCByb3VuZCh0ZW1wb3JhbF9tZXRyaWNzJHRlbXBvcmFsX3Jtc2UsIDIpLCAiXG4iKQ0KYGBgDQojIyMgQ29tcGFyZSBTcGF0aWFsIHZzLiBUZW1wb3JhbCBWYWxpZGF0aW9uDQoNCmBgYHtyIGNvbXBhcmUtc3BhdGlhbC10ZW1wb3JhbH0NCiMgQ29tcGFyZSBMT0dPIENWIChzcGF0aWFsKSB2cyAyMDE4ICh0ZW1wb3JhbCkNCnZhbGlkYXRpb25fY29tcGFyaXNvbiA8LSB0aWJibGUoDQogIFZhbGlkYXRpb24gPSBjKCJTcGF0aWFsIChMT0dPIENWIDIwMTcpIiwgIlRlbXBvcmFsICgyMDE4KSIpLA0KICBNQUUgPSBjKG1lYW4oY3ZfcmVzdWx0cyRtYWUpLCB0ZW1wb3JhbF9tZXRyaWNzJHRlbXBvcmFsX21hZSksDQogIFJNU0UgPSBjKG1lYW4oY3ZfcmVzdWx0cyRybXNlKSwgdGVtcG9yYWxfbWV0cmljcyR0ZW1wb3JhbF9ybXNlKQ0KKQ0KDQp2YWxpZGF0aW9uX2NvbXBhcmlzb24gJT4lDQogIGthYmxlKA0KICAgIGRpZ2l0cyA9IDIsDQogICAgY2FwdGlvbiA9ICJTcGF0aWFsIHZzLiBUZW1wb3JhbCBWYWxpZGF0aW9uIENvbXBhcmlzb24iDQogICkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpDQpgYGANCg0KKipJbnRlcnByZXRhdGlvbioqOiBUZW1wb3JhbCBwcmVkaWN0aW9uICgyMDE4IE1BRTogYHIgcm91bmQodGVtcG9yYWxfbWV0cmljcyR0ZW1wb3JhbF9tYWUsIDIpYCkgaXMgYHIgaWZlbHNlKHRlbXBvcmFsX21ldHJpY3MkdGVtcG9yYWxfbWFlID4gbWVhbihjdl9yZXN1bHRzJG1hZSksICJ3b3JzZSIsICJiZXR0ZXIiKWAgdGhhbiBzcGF0aWFsIGNyb3NzLXZhbGlkYXRpb24gKDIwMTcgTUFFOiBgciByb3VuZChtZWFuKGN2X3Jlc3VsdHMkbWFlKSwgMilgKS4gDQoNClRoaXMgaXMgZXhwZWN0ZWQgYmVjYXVzZToNCg0KMS4gKipUcnVlIGNoYW5nZSBvdmVyIHRpbWUqKjogQnVyZ2xhcnkgcGF0dGVybnMgbWF5IGhhdmUgc2hpZnRlZCBiZXR3ZWVuIDIwMTctMjAxOA0KMi4gKipGZWF0dXJlIHN0YWxlbmVzcyoqOiBPdXIgcG90aG9sZXMgYW5kIGNlbnN1cyBkYXRhIGFyZSBmcm9tIDIwMTc7IHRoZXkgZG9uJ3QgY2FwdHVyZSAyMDE4IHVwZGF0ZXMNCjMuICoqSGFyZGVyIHRlc3QqKjogVGVtcG9yYWwgdmFsaWRhdGlvbiBpcyBhIG1vcmUgcmVhbGlzdGljIHRlc3TigJRpdCBzaW11bGF0ZXMgZm9yZWNhc3RpbmcgZnV0dXJlIG91dGNvbWVzIHVzaW5nIHBhc3QgZGF0YQ0KDQpTaW5jZSB0ZW1wb3JhbCBNQUUgaXMgc3Vic3RhbnRpYWxseSBoaWdoZXIsIGl0IHN1Z2dlc3RzIG91ciBtb2RlbCBjYXB0dXJlcyB0aGUgMjAxNyBzcGF0aWFsIHN0cnVjdHVyZSB3ZWxsIGJ1dCBoYXMgbGltaXRlZCBwcmVkaWN0aXZlIHBvd2VyIGZvciBmdXR1cmUgYnVyZ2xhcnkgcGF0dGVybnMuIFRoaXMgaXMgY29tbW9uIGluIGNyaW1lIGZvcmVjYXN0aW5nIGFuZCBoaWdobGlnaHRzIHRoZSBpbXBvcnRhbmNlIG9mIHJlZ3VsYXJseSB1cGRhdGluZyBtb2RlbHMuDQoNCiMjIyBWaXN1YWxpemUgMjAxOCBQcmVkaWN0aW9ucyBhbmQgRXJyb3JzDQoNCmBgYHtyIHRlbXBvcmFsLXZpeiwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTh9DQpmaXNobmV0IDwtIGZpc2huZXQgJT4lDQogIG11dGF0ZShlcnJvcl8yMDE4ID0gY291bnRCdXJnbGFyaWVzMjAxOCAtIHByZWRpY3Rpb25fMjAxOCkNCg0KcDEgPC0gZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSBmaXNobmV0LCBhZXMoZmlsbCA9IGNvdW50QnVyZ2xhcmllczIwMTgpLCBjb2xvciA9IE5BKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG5hbWUgPSAiQ291bnQiLCBvcHRpb24gPSAicGxhc21hIiwgbGltaXRzID0gYygwLCAxNSkpICsNCiAgbGFicyh0aXRsZSA9ICJBY3R1YWwgMjAxOCBCdXJnbGFyaWVzIikgKw0KICB0aGVtZV9jcmltZSgpDQoNCnAyIDwtIGdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gZmlzaG5ldCwgYWVzKGZpbGwgPSBwcmVkaWN0aW9uXzIwMTgpLCBjb2xvciA9IE5BKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG5hbWUgPSAiUHJlZGljdGVkIiwgb3B0aW9uID0gInBsYXNtYSIsIGxpbWl0cyA9IGMoMCwgMTUpKSArDQogIGxhYnModGl0bGUgPSAiUHJlZGljdGVkIDIwMTggKGZyb20gMjAxNyBtb2RlbCkiKSArDQogIHRoZW1lX2NyaW1lKCkNCg0KcDMgPC0gZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSBmaXNobmV0LCBhZXMoZmlsbCA9IGVycm9yXzIwMTgpLCBjb2xvciA9IE5BKSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKA0KICAgIG5hbWUgPSAiRXJyb3IiLA0KICAgIGxvdyA9ICIjMjE2NmFjIiwNCiAgICBtaWQgPSAiZ3JleTkwIiwNCiAgICBoaWdoID0gIiNiMjE4MmIiLA0KICAgIG1pZHBvaW50ID0gMCwNCiAgICBsaW1pdHMgPSBjKC0xMCwgMTApDQogICkgKw0KICBsYWJzKHRpdGxlID0gIjIwMTggUHJlZGljdGlvbiBFcnJvcnMiKSArDQogIHRoZW1lX2NyaW1lKCkNCg0KKHAxICsgcDIgKyBwMykgKw0KICBwbG90X2Fubm90YXRpb24oDQogICAgdGl0bGUgPSAiVGVtcG9yYWwgVmFsaWRhdGlvbjogMjAxOCBQcmVkaWN0aW9ucyBmcm9tIDIwMTcgTW9kZWwiDQogICkNCmBgYA0KDQpUaGUgMjAxOCBlcnJvciBtYXAgcmV2ZWFscyBzeXN0ZW1hdGljIHBhdHRlcm5zOg0KLSAqKlVuZGVycHJlZGljdGlvbiAocmVkKSoqOiBTbWFsbCBjbHVzdGVycyBvbiBzb3V0aCBhbmQgd2VzdCBzaWRlcyB3aGVyZSBidXJnbGFyaWVzIGluY3JlYXNlZCBvciBuZXcgaG90c3BvdHMgZW1lcmdlZA0KLSAqKk92ZXJwcmVkaWN0aW9uIChibHVlKSoqOiBBcmVhcyB3aGVyZSBjcmltZSBkZWNsaW5lZCBtb3JlIHRoYW4gdGhlIG1vZGVsIGV4cGVjdGVkDQoNClRoZXNlIHBhdHRlcm5zIHN1Z2dlc3Qgb3VyIG1vZGVsIGNhcHR1cmVzIGJhc2VsaW5lIHNwYXRpYWwgcmlzayBidXQgbWlzc2VzIHRlbXBvcmFsIGR5bmFtaWNzLiBUbyBpbXByb3ZlIGZvcmVjYXN0aW5nLCB3ZSBtaWdodDoNCjEuIEluY29ycG9yYXRlIGxhZ2dlZCBjcmltZSBjb3VudHMgdG8gY2FwdHVyZSBtb21lbnR1bQ0KMi4gQWRkIHRpbWUtdmFyeWluZyBwcmVkaWN0b3JzICh1bmVtcGxveW1lbnQsIGZvcmVjbG9zdXJlcykNCjMuIFVzZSBkeW5hbWljIHVwZGF0aW5nIChyZWZpdCBtb250aGx5L3F1YXJ0ZXJseSkNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIyBDb25jbHVzaW9ucw0KDQojIyMgT3ZlcnZpZXcNCkkgd2FzIHRhc2tlZCB3aXRoIGJ1aWxkaW5nIGEgc3BhdGlhbCBwcmVkaWN0aXZlIG1vZGVsIGV4YW1pbmluZyB0aGUgcmVsYXRpb24gb2YgMzExIGFsbGV5IHBvdGhvbGUgY29tcGxhaW50cyByZWxhdGUgdG8gYnVyZ2xhcnkgcGF0dGVybnMgaW4gQ2hpY2FnbyBpbiBib3RoIDIwMTcgYW5kIDIwMTguIFVzaW5nIGEgNTAwbSB4IDUwMG0gZmlzaG5ldCBncmlkIGZvciB1bmlmb3JtIHNwYXRpYWwgdW5pdHMgZm9yIGFuYWx5c2lzLCBJIHRoZW4gY2FsY3VsYXRlIExvY2FsIE1vcmFuJ3MgSSBjYWxjdWxhdGlvbnMsIExJU0EgdmlzdWFsaXphdGlvbnMsIHBvaXNzb24gcmVncmVzc2lvbiwgYW5kIG5lZ2F0aXZlIGJpbm9taWFsIHJlZ3Jlc3Npb24gdG8gdGVzdCBwb3Rob2xlIGRhdGEgYW5kIG90aGVyIHN0cnVjdHVyYWwgZmVhdHVyZXMgb24gYnVyZ2xhcmllcy4NCg0KKipEaWQgcG90aG9sZSBwYXR0ZXJucyBwcmVkaWN0IGJ1cmdsYXJ5IHJpc2s/KioNCg0KUGFydGlhbGx5LiBQb3Rob2xlIGNvdW50cyBhbmQgcHJveGltaXR5IHNob3dlZCBhc3NvY2lhdGlvbnMgd2l0aCBidXJnbGFyaWVzLCBidXQgdGhlc2UgZWZmZWN0cyBsYXJnZWx5IGRpc2FwcGVhcmVkIGFmdGVyIGNvbnRyb2xsaW5nIGZvciBzb2Npb2Vjb25vbWljIGNoYXJhY3RlcmlzdGljcy4gVGhpcyBzdWdnZXN0cyBib3RoIGFyZSBkcml2ZW4gYnkgc2hhcmVkIHN0cnVjdHVyYWwgY29uZGl0aW9uc+KAlG5laWdoYm9yaG9vZCBkaXNpbnZlc3RtZW50LCBwb3ZlcnR5LCBhbmQgaG91c2luZyBtYXJrZXQgd2Vha25lc3PigJRyYXRoZXIgdGhhbiBwb3Rob2xlcyBkaXJlY3RseSBjYXVzaW5nIGNyaW1lLg0KDQoNCioqRGlkIHRoZSBtb2RlbCBvdXRwZXJmb3JtIHRoZSBLREUgYmFzZWxpbmU/KioNCg0KTm90IGluIHJhdyBwcmVkaWN0aXZlIGFjY3VyYWN5IG9uIDIwMTcgZGF0YS4gVGhlIEtERSBhY2hpZXZlZCBzbGlnaHRseSBsb3dlciBNQUUgKGByIHJvdW5kKGNvbXBhcmlzb24ka2RlX21hZSwgMilgIHZzLiBgciByb3VuZChjb21wYXJpc29uJG1vZGVsX21hZSwgMilgKS4gSG93ZXZlciwgdGhlIHJlZ3Jlc3Npb24gbW9kZWwgcHJvdmlkZXMgaW50ZXJwcmV0YWJpbGl0eSBhbmQgaGlnaGxpZ2h0cyBhY3Rpb25hYmxlIHJpc2sgZmFjdG9ycywgb2ZmZXJpbmcgaW5zaWdodCBpbnRvIHByb21pbmVudCBzdHJ1Y3R1cmFsIHZhcmlhYmxlcy4gDQoNCioqSG93IHdlbGwgZGlkIHByZWRpY3Rpb25zIGdlbmVyYWxpemUgYWNyb3NzIHNwYWNlIGFuZCB0aW1lPyoqDQoNClNwYXRpYWwgY3Jvc3MtdmFsaWRhdGlvbiAoTE9HTyBDVikgc2hvd2VkIG1vZGVyYXRlIHBlcmZvcm1hbmNlIChNQUUgPSBgciByb3VuZChtZWFuKGN2X3Jlc3VsdHMkbWFlKSwgMilgKSwgIHdoaWxlIHRlbXBvcmFsIHByZWRpY3Rpb25zICgyMDE4KSB3ZXJlIGxlc3MgYWNjdXJhdGUgTUFFID0gYHIgcm91bmQodGVtcG9yYWxfbWV0cmljcyR0ZW1wb3JhbF9tYWUsIDIpYCwgcmVmbGVjdGluZyB0aGUgY2hhbGxlbmdlIG9mIGZvcmVjYXN0aW5nIGR5bmFtaWMgY3JpbWUgcGF0dGVybnMuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyMgRmluYWwgUmVtYXJrcw0KDQpVbHRpbWF0ZWx5LCB0aGlzIGFuYWx5c2lzIHJldmVhbHMgdGhhdCBwb3Rob2xlcyBhcmUgYSBzeW1wdG9tLCBub3QgYSBjYXVzZSwgb2YgdGhlIGNvbmRpdGlvbnMgdGhhdCBlbGV2YXRlIGJ1cmdsYXJ5IHJpc2suIFdoaWxlIHNwYXRpYWwgbW9kZWxzIGNhbiBpZGVudGlmeSB3aGVyZSBjcmltZSBjb25jZW50cmF0ZXMsIHVuZGVyc3RhbmRpbmcgd2h5IHJlcXVpcmVzIGV4YW1pbmluZyB0aGUgc3RydWN0dXJhbCBpbmVxdWFsaXRpZXMgdGhhdCBzaGFwZSBib3RoIGluZnJhc3RydWN0dXJlIHF1YWxpdHkgYW5kIHB1YmxpYyBzYWZldHkuIEZvciBDaGljYWdvIGFuZCBjaXRpZXMgbGlrZSBpdCwgZHVyYWJsZSBjcmltZSByZWR1Y3Rpb24gZGVwZW5kcyBsZXNzIG9uIHByZWRpY3RpbmcgaG90IHNwb3RzIHRoYW4gb24gYWRkcmVzc2luZyB0aGUgZWNvbm9taWMgYW5kIHNvY2lhbCBjb25kaXRpb25zIHRoYXQgY3JlYXRlIHRoZW0NCg0K