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

Perform Local Moran’s I Analysis

Local Moran’s I identifies statistically significant clusters and outliers in the pothole distribution. High-High clusters (hot spots) indicate areas where both the focal cell and its neighbors have high pothole counts.

# Function to calculate Local Moran's I
calculate_local_morans <- function(data, variable, k = 5) {
  
  # Create spatial weights
  coords <- st_coordinates(st_centroid(data))
  neighbors <- knn2nb(knearneigh(coords, k = k))
  weights <- nb2listw(neighbors, style = "W", zero.policy = TRUE)
  
  # Calculate Local Moran's I
  local_moran <- localmoran(data[[variable]], weights)
  
  # Classify clusters
  mean_val <- mean(data[[variable]], na.rm = TRUE)
  
  data %>%
    mutate(
      local_i = local_moran[, 1],
      p_value = local_moran[, 5],
      is_significant = p_value < 0.05,
      
      moran_class = case_when(
        !is_significant ~ "Not Significant",
        local_i > 0 & .data[[variable]] > mean_val ~ "High-High",
        local_i > 0 & .data[[variable]] <= mean_val ~ "Low-Low",
        local_i < 0 & .data[[variable]] > mean_val ~ "High-Low",
        local_i < 0 & .data[[variable]] <= mean_val ~ "Low-High",
        TRUE ~ "Not Significant"
      )
    )
}

# Apply to potholes
fishnet <- calculate_local_morans(fishnet, "countPotholes", k = 5)

cat("✓ Calculated Local Moran's I\n")
## ✓ Calculated Local Moran's I
cat("  Hot spots (High-High):", sum(fishnet$moran_class == "High-High"), "\n")
##   Hot spots (High-High): 333
cat("  Cold spots (Low-Low):", sum(fishnet$moran_class == "Low-Low"), "\n")
##   Cold spots (Low-Low): 0

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 

Join Additional Information for Cross-Validation

To avoid omitted variable bias, I incorporate American Community Survey (ACS) demographic and socioeconomic controls, and join police district identifiers for spatial cross-validation.

# Join district information for spatial cross-validation
fishnet <- st_join(
  fishnet,
  policeDistricts,
  join = st_within,
  left = TRUE
)

# Handle district column name
if ("dist_num" %in% names(fishnet)) {
  fishnet <- fishnet %>% dplyr::rename(District = dist_num)
}

fishnet <- fishnet %>% dplyr::filter(!is.na(District))

cat("✓ Joined police districts\n")
cat("  - Districts:", length(unique(fishnet$District)), "\n")
cat("  - Cells:", nrow(fishnet), "\n")

# Define ACS variables
acs_vars <- c(
  tot_pop = "B03002_001", #total population
  med_inc = "B19013_001",
  white = "B03002_003",
  black = "B03002_004",
  hisp = "B03002_012",
  pov_tot = "B17001_001",
  pov_num = "B17001_002",
  occ_tot = "B25003_001",
  owner = "B25003_002",
  med_age = "B25035_001"
)

# Pull 2017 ACS data for Cook County
tracts <- get_acs(
  geography = "tract",
  state = "IL",
  county = "Cook",
  year = 2017,
  variables = acs_vars,
  survey = "acs5",
  output = "wide",
  geometry = TRUE
)
## Warning: • You have not set a Census API key. Users without a key are limited to 500
## queries per day and may experience performance limitations.
## ℹ For best results, get a Census API key at
## http://api.census.gov/data/key_signup.html and then supply the key to the
## `census_api_key()` function to use it throughout your tidycensus session.
## This warning is displayed once per session.
# Process and join to fishnet
tracts_chi <- tracts %>%
  st_transform(st_crs(chicagoBound)) %>%
  st_filter(chicagoBound, .predicate = st_intersects) %>%
  dplyr::mutate(
    pct_black = 100 * blackE / pmax(tot_popE, 1),
    pct_hisp = 100 * hispE / pmax(tot_popE, 1),
    pct_poverty = 100 * pov_numE / pmax(pov_totE, 1),
    pct_owner = 100 * ownerE / pmax(occ_totE, 1),
    med_income = med_incE,
    med_house_age = med_ageE
  ) %>%
  dplyr::select(GEOID, pct_black, pct_hisp, pct_poverty, pct_owner, 
                med_income, med_house_age)

# Spatial join to fishnet
fishnet <- fishnet %>%
  st_join(tracts_chi, join = st_intersects, left = TRUE)

cat("✓ Joined ACS census data\n")
cat("  Controls: poverty, race/ethnicity, tenure, income, housing age\n")

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.

Compare Performance Metrics

# Calculate performance metrics
comparison <- fishnet %>%
  st_drop_geometry() %>%
  filter(!is.na(prediction_nb), !is.na(prediction_kde)) %>%
  summarize(
    model_mae = mean(abs(countBurglaries - prediction_nb)),
    model_rmse = sqrt(mean((countBurglaries - prediction_nb)^2)),
    kde_mae = mean(abs(countBurglaries - prediction_kde)),
    kde_rmse = sqrt(mean((countBurglaries - prediction_kde)^2))
  )

comparison %>%
  pivot_longer(everything(), names_to = "metric", values_to = "value") %>%
  separate(metric, into = c("approach", "metric"), sep = "_") %>%
  pivot_wider(names_from = metric, values_from = value) %>%
  kable(
    digits = 2,
    caption = "In-Sample Performance: Model vs. KDE Baseline"
  ) %>%
  kable_styling(bootstrap_options = c("striped", "hover"))
In-Sample Performance: Model vs. KDE Baseline
approach mae rmse
model 2.36 3.38
kde 2.16 3.05

The KDE baseline has lower in-sample RMSE than the Negative Binomial model. This is not surprising, because KDE is applied directly to the observed crime locations, meaning, “where crime happened before, it will happen again”. As a result, KDE performs extremely well in-sample but has limited ability to generalize spatial patterns to new locations. In contrast, the regression model incorporates structural features and thus prioritizes out-of-sample predictive performance. The real test of model quality comes from the spatial and temporal validation results, where KDE typically performs worse.

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

Model Performance Summary

summary_stats <- data.frame(
  Metric = c(
    "Mean LOGO CV MAE",
    "Mean LOGO CV RMSE",
    "In-sample MAE (Model)",
    "In-sample MAE (KDE)",
    "AIC (Negative Binomial)",
    "Number of cells",
    "Cells with 0 burglaries",
    "Most predictive variable"
  ),
  Value = c(
    round(mean(cv_results$mae), 2),
    round(mean(cv_results$rmse), 2),
    round(comparison$model_mae, 2),
    round(comparison$kde_mae, 2),
    round(AIC(final_model), 2),
    nrow(fishnet),
    paste0(sum(fishnet$countBurglaries == 0), " (", 
           round(100*sum(fishnet$countBurglaries == 0)/nrow(fishnet), 2), "%)"),
    names(which.max(abs(coef(final_model)[-1])))
  )
)

summary_stats %>%
  kable(
    caption = "Model Performance Summary Statistics",
    col.names = c("Metric", "Value")
  ) %>%
  kable_styling(bootstrap_options = c("striped", "hover"))
Model Performance Summary Statistics
Metric Value
Mean LOGO CV MAE 2.65
Mean LOGO CV RMSE 3.5
In-sample MAE (Model) 2.36
In-sample MAE (KDE) 2.16
AIC (Negative Binomial) 19020.16
Number of cells 4269
Cells with 0 burglaries 773 (18.11%)
Most predictive variable pct_black

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:

  1. True change over time: Burglary patterns may have shifted between 2017-2018
  2. Feature staleness: Our potholes and census data are from 2017; they don’t capture 2018 updates
  3. 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.

Final Remarks

Ultimately, this analysis reveals that potholes are a symptom, not a cause, of the conditions that elevate burglary risk. While spatial models can identify where crime concentrates, understanding why requires examining the structural inequalities that shape both infrastructure quality and public safety. For Chicago and cities like it, durable crime reduction depends less on predicting hot spots than on addressing the economic and social conditions that create them

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