Introduction
This project “Geospatial Risk Predictions of Armed Robbery Incidents
in Chicago” explores the application of predictive policing through
geospatial risk modeling to forecast the likelihood of armed robbery
incidents in Chicago. By utilizing historical crime data, socioeconomic
indicators, and spatial risk factors such as abandoned cars, street
light outages, and sanitation complaints, the model aims to identify
high-risk areas for armed robbery incidents. The study employs Poisson
regression to predict crime counts and compares the accuracy of spatial
models against traditional methods like kernel density estimation.
Emphasizing the importance of addressing bias and data selection issues,
the project aims to improve the generalizability and fairness of crime
prediction models across different neighborhoods, particularly
considering racial and socioeconomic disparities. Through spatial
cross-validation and the inclusion of spatial process features, this
analysis seeks to enhance predictive accuracy and offer insights into
better resource allocation for law enforcement.
The objective of this report is to develop a geospatial risk model
for predicting armed robbery incidents in Chicago. Robbery data is often
influenced by selection bias due to two key factors: not all robbery
incidents are reported to law enforcement, and areas with historically
high robbery rates, which receive increased police presence, tend to
continue reporting higher incidents. The underlying hypothesis is that
crime risk is driven by exposure to various geospatial risk and
protective factors, such as signs of blight or the presence of
recreation centers. As exposure to risk factors rises, so does the
likelihood of crime. In addition to identifying these risk factors, the
model incorporates spatial variables to improve prediction accuracy.
Poisson regression is used in this analysis due to its suitability for
modeling count data. 1
Crime Data Collection
All datasets for this study were sourced from Chicago Data Portal. The
Chicago Data Portal is an online platform that provides access to a wide
range of municipal datasets. The portal includes an extensive record of
crime incidents in Chicago, updated annually. For this analysis, crime
data for 2021 was downloaded using the RSocrata package, which allows
easy access to open data from platforms like Socrata, the software
behind the Chicago Data Portal.
This report specifically filter the armed robbery crime incidents in
Chicago. The armed robbery include: KNIFE / CUTTING INSTRUMENT, HANDGUN,
OTHER FIREARM, or OTHER DANGEROUS WEAPON. After filtering the crime data
for armed robbery incidents, we focused on those involving the following
weapon types: knife/cutting instrument, handgun, other firearms, or
other dangerous weapons. These specific categories provide a clearer
understanding of more serious and violent robbery offenses, as they pose
a higher threat to victims and are more likely to occur in areas with
distinct risk factors. By narrowing our analysis to armed robberies, we
aim to explore the spatial distribution of these incidents and assess
how environmental and social variables, such as abandoned buildings,
street light outages, and sanitation complaints, correlate with higher
occurrences of these violent crimes. This targeted approach enables us
to develop a more precise predictive model for robbery risk in different
areas of Chicago.
robbery21 <- read.socrata("https://data.cityofchicago.org/Public-Safety/Crimes-2021/dwme-t96c")
saveRDS(robbery21, "robbery21.rds")
# Read from the locally saved file
robbery21 <- readRDS("robbery21.rds") %>%
filter(Primary.Type == "ROBBERY", grepl("ARMED", Description)) %>%
mutate(x = gsub("[()]", "", Location)) %>%
separate(x,into= c("Y","X"), sep=",") %>%
mutate(X = as.numeric(X), Y = as.numeric(Y)) %>%
na.omit() %>%
st_as_sf(coords = c("X", "Y"), crs = 4326, agr = "constant") %>%
st_transform('ESRI:102271') %>%
distinct()
The map displays the spatial distribution of armed robbery incidents
in Chicago for 2021, revealing a clear pattern of concentration in
specific areas. The highest density of incidents occurs in the central
and southern parts of the city, with particular clustering around the
city’s south side and certain western neighborhoods. These areas likely
experience higher rates of violent crime due to a combination of
socio-economic factors and environmental conditions. In contrast, the
northern parts of the city, especially near the lakefront, show
significantly fewer armed robberies. This spatial pattern suggests that
certain regions may face more pronounced crime risks, possibly
influenced by urban infrastructure, economic distress, or other factors
that contribute to the likelihood of robbery incidents. Understanding
these clusters can help inform targeted interventions and crime
prevention strategies in high-risk areas.
ggplot() +
geom_sf(data = chicagoBoundary, fill = "grey") +
geom_sf(data = robbery21, colour="darkred", size=0.1, show.legend = "point") +
labs(title= "Armed Robbery Incidents in Chicago 2021") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, linewidth =0.8)
)

Below is the density map of armed robbery incidents in Chicago. The
density map of armed robbery incidents in Chicago reveals two prominent
hotspots. The first cluster is located in the central-western part of
the city, while the second significant concentration is found slightly
to the south of downtown. These areas experience the highest intensity
of armed robberies, indicating localized zones with increased risk. The
surrounding areas show a gradual decrease in density as we move away
from these core hotspots, suggesting that armed robberies are not evenly
distributed across the city but are instead concentrated in specific
regions. The northern and far southern parts of the city exhibit much
lower densities, indicating that armed robberies are relatively less
common in these areas. This pattern highlights the need for focused
crime prevention efforts in the identified high-risk zones.
options(scipen=0)
ggplot() +
geom_sf(data = chicagoBoundary, fill = "grey") +
stat_density2d(data = data.frame(st_coordinates(robbery21)),
aes(X, Y, fill = ..level.., alpha = ..level..),
size = 0.01, bins = 60, geom = 'polygon') +
scale_fill_viridis(option = "turbo", name = "Density") +
scale_alpha(range = c(0.00, 0.35), guide = "none") +
labs(title = "Density of Armed Robbery") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, linewidth=0.8)
)

Crime risk tends to fluctuate gradually across geographic space
rather than conforming to predefined administrative boundaries. To
effectively capture this continuous variation, point-level data can be
aggregated into a network of evenly spaced grid cells. The code provided
below generates such a fishnet for Chicago, offering a structured way to
represent crime risk spatially. This grid-based approach is ideal for
preparing the data for regression analysis, allowing for a smoother
depiction of crime trends across the urban landscape.
fishnet <-
st_make_grid(chicagoBoundary,
cellsize = 500,
square = TRUE) %>%
.[chicagoBoundary] %>% # fast way to select intersecting polygons
st_sf() %>%
mutate(uniqueID = 1:n())
ggplot() +
geom_sf(data=fishnet, color="black", fill="lightblue") +
labs(title = "Fishnet of Chicago") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, linewidth=0.8)
)

Once the fishnet was created, use it as the spatial framework to
visualize the distribution of robbery incidents. From this, it becomes
clear that the highest number of robberies are concentrated in a few
grid cells located in northeast Chicago. This pattern highlights the
emergence of a clustered spatial trend in robbery occurrences.
robbery21_net <-
dplyr::select(robbery21) %>%
mutate(countRobbery = 1) %>%
aggregate(., fishnet, sum) %>%
mutate(countRobbery = replace_na(countRobbery, 0),
uniqueID = 1:n(),
cvID = sample(round(nrow(fishnet) / 24),
size=nrow(fishnet), replace = TRUE))
ggplot() +
geom_sf(data = robbery21_net, aes(fill = countRobbery), color = NA) +
scale_fill_viridis(option = "turbo", name = "Robbery Counts") +
labs(title = "Count of Robberies for the Fishnet") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, linewidth=0.8)
)

The bar chart below illustrates the distribution of robbery incidents
in Chicago for 2021, revealing a highly skewed pattern. A large number
of grid cells have few or no robbery incidents, while only a small
number of grids experience significantly higher robbery counts. This
non-normal distribution suggests that Poisson regression is a suitable
method for analyzing this data, as it can handle the overdispersion and
count nature of the variable.
ggplot(robbery21_net, aes(x = countRobbery)) +
geom_histogram(fill = "steelblue", color = "#2c3e50", bins = 30) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1, face = "plain", size = 7),
axis.text.y = element_text(size = 7),
axis.title = element_text(size = 9),
plot.title = element_text(size = 14, face = "bold"),
plot.subtitle = element_text(size = 10),
panel.grid.major = element_line(color = "grey80"),
panel.grid.minor = element_blank(),
panel.border = element_rect(colour = "grey60", fill = NA, size = 0.8)) +
labs(title = "Distribution of Robberies in Chicago 2021",
subtitle = "Analysis of robbery incident distribution",
caption = "Data: Chicago Data Portal Crimes 2021") +
xlab("Robbery Incidents") +
ylab("Count")

Risk Factors
In this project, eight risk factors are included in the model to
predict robberies: reports of abandoned cars, street lights out,
graffiti, sanitation complaints, abandoned buildings, locations of
liquor stores selling alcohol to-go, ShotSpotter detections, and banks.
Neighborhoods with abandoned cars, street lights out, graffiti, and
sanitation issues often indicate neglect, economic distress, and reduced
community oversight, which can create an environment conducive to
criminal activities. Abandoned buildings can serve as hideouts for
criminals, further contributing to neighborhood disorder. Liquor stores
are associated with alcohol-related vulnerabilities, while ShotSpotter
detections point to areas with higher gun violence. Finally, banks, due
to the availability of cash, may attract robbers seeking financial gain.
These factors collectively highlight areas that may be more susceptible
to robberies.
abandonCars <-
read.socrata("https://data.cityofchicago.org/Service-Requests/311-Service-Requests-Abandoned-Vehicles/3c9v-pnva") %>%
mutate(year = substr(creation_date,1,4)) %>% filter(year == "2020") %>%
dplyr::select(Y = latitude, X = longitude) %>%
na.omit() %>%
st_as_sf(coords = c("X", "Y"), crs = 4326, agr = "constant") %>%
st_transform(st_crs(fishnet)) %>%
mutate(Legend = "Abandoned_Cars")
abandonBuildings <-
read.socrata("https://data.cityofchicago.org/Service-Requests/311-Service-Requests-Vacant-and-Abandoned-Building/7nii-7srd") %>%
mutate(year = substr(date_service_request_was_received,1,4)) %>% filter(year == "2018") %>%
dplyr::select(Y = latitude, X = longitude) %>%
na.omit() %>%
st_as_sf(coords = c("X", "Y"), crs = 4326, agr = "constant") %>%
st_transform(st_crs(fishnet)) %>%
mutate(Legend = "Abandoned_Buildings")
graffiti <-
read.socrata("https://data.cityofchicago.org/Service-Requests/311-Service-Requests-Graffiti-Removal-Historical/hec5-y4x5") %>%
mutate(year = substr(creation_date,1,4)) %>% filter(year == "2018") %>%
filter(where_is_the_graffiti_located_ %in% c("Front", "Rear", "Side")) %>%
dplyr::select(Y = latitude, X = longitude) %>%
na.omit() %>%
st_as_sf(coords = c("X", "Y"), crs = 4326, agr = "constant") %>%
st_transform(st_crs(fishnet)) %>%
mutate(Legend = "Graffiti")
streetLightsOut <-
read.socrata("https://data.cityofchicago.org/Service-Requests/311-Service-Requests-Street-Lights-All-Out/zuxi-7xem") %>%
mutate(year = substr(creation_date,1,4)) %>% filter(year == "2018") %>%
dplyr::select(Y = latitude, X = longitude) %>%
na.omit() %>%
st_as_sf(coords = c("X", "Y"), crs = 4326, agr = "constant") %>%
st_transform(st_crs(fishnet)) %>%
mutate(Legend = "Street_Lights_Out")
sanitation <-
read.socrata("https://data.cityofchicago.org/Service-Requests/311-Service-Requests-Sanitation-Code-Complaints-Hi/me59-5fac") %>%
mutate(year = substr(creation_date,1,4)) %>% filter(year == "2018") %>%
dplyr::select(Y = latitude, X = longitude) %>%
na.omit() %>%
st_as_sf(coords = c("X", "Y"), crs = 4326, agr = "constant") %>%
st_transform(st_crs(fishnet)) %>%
mutate(Legend = "Sanitation")
liquorRetail <-
read.socrata("https://data.cityofchicago.org/resource/nrmj-3kcf.json") %>%
filter(business_activity == "Retail Sales of Packaged Liquor") %>%
dplyr::select(Y = latitude, X = longitude) %>%
na.omit() %>%
st_as_sf(coords = c("X", "Y"), crs = 4326, agr = "constant") %>%
st_transform(st_crs(fishnet)) %>%
mutate(Legend = "Liquor_Retail")
shotSpotter <-
read.socrata("https://data.cityofchicago.org/Public-Safety/Violence-Reduction-Shotspotter-Alerts/3h7q-7mdb") %>%
dplyr::select(Y = latitude, X = longitude) %>%
na.omit() %>%
st_as_sf(coords = c("X", "Y"), crs = 4326, agr = "constant") %>%
st_transform(st_crs(fishnet)) %>%
mutate(Legend = "Shot_Spotter")
Banks <-
read.socrata("https://data.cityofchicago.org/Administration-Finance/Lending-Equity-Depository-Locations-2021/jgup-zt7k") %>%
dplyr::select(location) %>%
na.omit()
Banks <- Banks %>%
mutate(X = as.numeric(sub("POINT \\((-?\\d+\\.\\d+) \\d+\\.\\d+\\)", "\\1", Banks$location)),
Y = as.numeric(sub("POINT \\(-?\\d+\\.\\d+ (-?\\d+\\.\\d+)\\)", "\\1", Banks$location))) %>%
dplyr::select(-location) %>%
filter(!is.na(X) & !is.na(Y)) %>%
st_as_sf(coords = c("X", "Y"), crs = 4326, agr = "constant") %>%
st_transform(st_crs(fishnet)) %>%
mutate(Legend = "Bank")
vars_net <-
rbind(abandonCars,streetLightsOut,abandonBuildings,
liquorRetail, graffiti, sanitation, shotSpotter, Banks) %>%
st_join(., fishnet, join=st_within) %>%
st_drop_geometry() %>%
group_by(uniqueID, Legend) %>%
summarize(count = n()) %>%
full_join(fishnet) %>%
spread(Legend, count, fill=0) %>%
st_sf() %>%
dplyr::select(-`<NA>`) %>%
na.omit() %>%
ungroup()
The figure illustrates the distinct spatial patterns of each risk
factor across Chicago. Abandoned buildings are predominantly
concentrated in the southern part of the city, while banks and liquor
retail stores cluster around the central business district (CBD).
Abandoned cars, sanitation complaints, and street lights out complaints
are more evenly scattered throughout the city. Graffiti removal requests
are mainly concentrated near the city center, whereas ShotSpotter
detections tend to occur in areas outside the city center, highlighting
the spatial variability in factors associated with potential robbery
risks.
vars_net.long <-
gather(vars_net, Variable, value, -geometry, -uniqueID)
plot1 <- ggplot() +
geom_sf(data = vars_net.long %>% filter(Variable == "Abandoned_Buildings"), aes(fill=value), colour=NA) +
scale_fill_viridis(option = "turbo") +
labs(title = "Abandoned Buildings") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)
plot2 <- ggplot() +
geom_sf(data = vars_net.long %>% filter(Variable == "Abandoned_Cars"), aes(fill=value), colour=NA) +
scale_fill_viridis(option = "turbo") +
labs(title = "Abandoned Cars") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)
plot3 <- ggplot() +
geom_sf(data = vars_net.long %>% filter(Variable == "Bank"), aes(fill=value), colour=NA) +
scale_fill_viridis(option = "turbo") +
labs(title = "Banks") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)
plot4 <- ggplot() +
geom_sf(data = vars_net.long %>% filter(Variable == "Graffiti"), aes(fill=value), colour=NA) +
scale_fill_viridis(option = "turbo") +
labs(title = "Graffiti") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)
plot5 <- ggplot() +
geom_sf(data = vars_net.long %>% filter(Variable == "Liquor_Retail"), aes(fill=value), colour=NA) +
scale_fill_viridis(option = "turbo") +
labs(title = "Liquor Retail") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)
plot6 <- ggplot() +
geom_sf(data = vars_net.long %>% filter(Variable == "Sanitation"), aes(fill=value), colour=NA) +
scale_fill_viridis(option = "turbo") +
labs(title = "Sanitation") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)
plot7 <- ggplot() +
geom_sf(data = vars_net.long %>% filter(Variable == "Shot_Spotter"), aes(fill=value), colour=NA) +
scale_fill_viridis(option = "turbo") +
labs(title = "Shot Spotter") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)
plot8 <- ggplot() +
geom_sf(data = vars_net.long %>% filter(Variable == "Street_Lights_Out"), aes(fill=value), colour=NA) +
scale_fill_viridis(option = "turbo") +
labs(title = "Street Light Out") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)
wrap_plots(
plot1, plot3, plot2, plot4,
plot5, plot6, plot7, plot8,
ncol = 4
)

Feature Engineering
Nearest Distance
The first feature engineering approach we took is to calculate
average nearest neighbor distance to hypothesize a smoother exposure
relationship across space. Here, the nn_function is used. For each of
our risk factors, average nearest distance is calculated from the
centroid of each cell to its nearest three, abandoned cars, for
example.
st_c <- st_coordinates
st_coid <- st_centroid
vars_net <-
vars_net %>%
mutate(
Abandoned_Buildings.nn =
nn_function(st_c(st_coid(vars_net)), st_c(abandonBuildings),3),
Abandoned_Cars.nn =
nn_function(st_c(st_coid(vars_net)), st_c(abandonCars),3),
Graffiti.nn =
nn_function(st_c(st_coid(vars_net)), st_c(graffiti),3),
Liquor_Retail.nn =
nn_function(st_c(st_coid(vars_net)), st_c(liquorRetail),3),
Street_Lights_Out.nn =
nn_function(st_c(st_coid(vars_net)), st_c(streetLightsOut),3),
Sanitation.nn =
nn_function(st_c(st_coid(vars_net)), st_c(sanitation),3),
Shot_Spotter.nn =
nn_function(st_c(st_coid(vars_net)), st_c(shotSpotter),3))
vars_net <-
vars_net %>% mutate(Bank.nn = nn_function(st_c(st_coid(vars_net)), st_c(Banks),3))
The neighbor features provide a clearer picture of the spatial
relationships between different risk factors.The southern and
southeastern areas of Chicago appear to be most distant from many of
these risks, particularly abandoned buildings, abandoned cars, and
graffiti. Interestingly, the southwestern corner also shows some
distance from risk factors like banks and liquor stores. One notable
observation is that the northern part of the city is furthest from
ShotSpotter locations, while still being relatively close to other risk
factors such as sanitation issues and street lights out. The relative
absence of ShotSpotters in this area raises questions about their role:
are they an indicator of higher crime due to their concentration, or
does their presence suggest increased policing, making neighborhoods
safer? These observations highlight the need to examine whether
ShotSpotters function as a crime detection tool or a proxy for
neighborhood safety. These are important considerations for
understanding the broader dynamics of crime and safety in Chicago.
vars_net.long.nn <-
dplyr::select(vars_net, ends_with(".nn")) %>%
gather(Variable, value, -geometry)
p1.nn <- ggplot() +
geom_sf(data = vars_net.long.nn %>% filter(Variable == "Abandoned_Buildings.nn"), aes(fill=value), colour=NA) +
scale_fill_viridis(option = "turbo") +
labs(title = "Abandoned Buildings NN") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)
p2.nn <- ggplot() +
geom_sf(data = vars_net.long.nn %>% filter(Variable == "Abandoned_Cars.nn"), aes(fill=value), colour=NA) +
scale_fill_viridis(option = "turbo") +
labs(title = "Abandoned Cars NN") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)
p3.nn <- ggplot() +
geom_sf(data = vars_net.long.nn %>% filter(Variable == "Bank.nn"), aes(fill=value), colour=NA) +
scale_fill_viridis(option = "turbo") +
labs(title = "Banks NN") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)
p4.nn <- ggplot() +
geom_sf(data = vars_net.long.nn %>% filter(Variable == "Graffiti.nn"), aes(fill=value), colour=NA) +
scale_fill_viridis(option = "turbo") +
labs(title = "Graffiti NN") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)
p5.nn <- ggplot() +
geom_sf(data = vars_net.long.nn %>% filter(Variable == "Liquor_Retail.nn"), aes(fill=value), colour=NA) +
scale_fill_viridis(option = "turbo") +
labs(title = "Liquor Retail NN") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)
p6.nn <- ggplot() +
geom_sf(data = vars_net.long.nn %>% filter(Variable == "Sanitation.nn"), aes(fill=value), colour=NA) +
scale_fill_viridis(option = "turbo") +
labs(title = "Sanitation NN") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)
p7.nn <- ggplot() +
geom_sf(data = vars_net.long.nn %>% filter(Variable == "Shot_Spotter.nn"), aes(fill=value), colour=NA) +
scale_fill_viridis(option = "turbo") +
labs(title = "Shot Spotter NN") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)
p8.nn <- ggplot() +
geom_sf(data = vars_net.long.nn %>% filter(Variable == "Street_Lights_Out.nn"), aes(fill=value), colour=NA) +
scale_fill_viridis(option = "turbo") +
labs(title = "Street Light Out NN") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)
wrap_plots(
p1.nn, p3.nn, p2.nn, p4.nn,
p5.nn, p6.nn, p7.nn, p8.nn,
ncol = 4
)

Local Spatial Autocorrelation
identified robbery hotspots in Chicago using Local Moran’s I, which
examines whether robbery counts at a location are clustered relative to
neighboring cells. A spatial weights matrix was calculated using queen
contiguity to define neighboring relationships. The results, visualized
with Moran’s I values, p-values, and significant hotspots, show high
spatial autocorrelation in northeastern Chicago near the CBD. Areas with
significant hotspots (p-values ≤ 0.05) align with patterns seen in the
kernel density map, indicating clusters of higher robbery counts than
expected by chance.
final_net <-
left_join(robbery21_net, st_drop_geometry(vars_net), by="uniqueID")
final_net.nb <- poly2nb(as_Spatial(final_net), queen=TRUE)
final_net.weights <- nb2listw(final_net.nb, style="W", zero.policy=TRUE)
local_morans <- localmoran(final_net$countRobbery, final_net.weights, zero.policy=TRUE) %>%
as.data.frame()
final_net.localMorans <-
cbind(local_morans, as.data.frame(final_net)) %>%
st_sf() %>%
dplyr::select(Robbery_Count = countRobbery,
Local_Morans_I = Ii,
P_Value = `Pr(z != E(Ii))`) %>%
mutate(Significant_Hotspots = ifelse(P_Value <= 0.001, 1, 0)) %>%
gather(Variable, Value, -geometry)
moran1 <- ggplot() +
geom_sf(data = final_net.localMorans %>% filter(Variable == "Robbery_Count"), aes(fill=Value), colour=NA) +
scale_fill_viridis(option = "turbo") +
labs(title = "Robbery Count") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)
moran2 <- ggplot() +
geom_sf(data = final_net.localMorans %>% filter(Variable == "Local_Morans_I"), aes(fill=Value), colour=NA) +
scale_fill_viridis(option = "turbo") +
labs(title = "Local Morans I") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)
moran3 <- ggplot() +
geom_sf(data = final_net.localMorans %>% filter(Variable == "P_Value"), aes(fill=Value), colour=NA) +
scale_fill_viridis(option = "turbo") +
labs(title = "P Value") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)
moran4 <- ggplot() +
geom_sf(data = final_net.localMorans %>% filter(Variable == "Significant_Hotspots"), aes(fill=Value), colour=NA) +
scale_fill_viridis(option = "turbo") +
labs(title = "Signigicant Hotspots") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)
moran1 + moran2 + moran3 + moran4

Singificant Robbery Clusters
The map illustrates the distance to the nearest robbery hotspots
across Chicago, with darker colors indicating shorter distances. The
central and southern parts of the city exhibit the shortest distances to
robbery hotspots, as shown by the deep blue and black areas. This
suggests that these regions are located closer to areas with a high
concentration of robbery incidents. In contrast, the outer northern and
southern parts of the city, represented by yellow, green, and red hues,
are farther away from these hotspots, indicating lower proximity to
concentrated robbery activities. This pattern highlights the spatial
distribution of robbery risks in Chicago, with more concentrated robbery
activity occurring in specific regions, particularly in the central and
southern areas. The distance gradient provides insight into how robbery
risks decrease as one moves away from these core hotspots.
final_net <-
final_net %>%
mutate(robbery.isSig = ifelse(localmoran(final_net$countRobbery,
final_net.weights)[,5] <= 0.0000001, 1, 0)) %>%
mutate(robbery.isSig.dist = nn_function(st_coordinates(st_centroid(final_net)),
st_coordinates(st_centroid(
filter(final_net, robbery.isSig == 1))), 1))
ggplot() +
geom_sf(data = final_net, aes(fill=robbery.isSig.dist), colour=NA) +
scale_fill_viridis(option = "turbo", name="NN Distance") +
labs(title="Robbery NN Distance") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 10, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)

All Risk Factors
The figure shows scatter plots of the count of predictor variables
and their nearest neighbor distances in relation to robbery incidents.
All count-based features, such as abandoned buildings, cars, and
sanitation complaints, are positively correlated with the number of
robberies, indicating that areas with higher occurrences of these risk
factors tend to experience more robberies. The strongest positive
relationships are observed between robbery incidents and sanitation
complaints, ShotSpotter detections, and liquor retail locations.
Conversely, all nearest neighbor distance features are negatively
correlated with robbery incidents, meaning that as the distance to risk
factors increases, the number of robberies decreases. The strongest
negative correlations are seen with distance to significant robbery
hotspots, abandoned buildings, and liquor retail stores. This suggests
that proximity to these risk factors plays a significant role in robbery
patterns.
When it comes to model building, this information is critical for
feature selection. To avoid multicollinearity, either the count or the
nearest neighbor distance should be selected for each risk factor. In
this case, the model incorporates nearest neighbor distances, as they
tend to show stronger and more consistent correlations with robbery
incidents.
final_net_long <- final_net%>%
st_drop_geometry() %>%
dplyr::select(-c(uniqueID, cvID)) %>%
pivot_longer(cols = -countRobbery, # everything except measurement
names_to = "Type", # categorizes all quantitative variables into Type
values_to = "Number")
correlation.cor <-
final_net_long %>%
group_by(Type) %>%
summarize(correlation = cor(Number, countRobbery, use = "complete.obs"))
final_net_long %>%
ggplot(aes(x= Number, y = countRobbery)) +
geom_point(size = 0.01, color = "#000004") +
geom_text(data = correlation.cor, aes(label = paste("r =", round(correlation, 2))),
x=-Inf, y=Inf, vjust = 1.5, hjust = -.1, size=3) +
geom_smooth(method='lm', formula= y~x, lwd=0.5, se = FALSE, color = "#BB3754") +
facet_wrap(~ Type, scales = "free", ncol = 2, labeller= labeller(Type = c(
`Abandoned_Buildings` = "Abandoned Buildings",
`Abandoned_Buildings.nn` = "Distance to Abandoned Buildings",
`Abandoned_Cars` = "Abandoned Cars",
`Abandoned_Cars.nn` = "Distance to Abandoned Cars",
`Bank` = "Bank",
`Bank.nn` = "Distance to Bank",
`Graffiti` = "Graffiti",
`Graffiti.nn` = "Distance to Graffiti",
`Liquor_Retail` = "Liquor Retail",
`Liquor_Retail.nn` = "Distance to Liquor Retail",
`robbery.isSig` = "Significant Robbery",
`robbery.isSig.dist` = "Distance to Significant Robbery",
`Sanitation` = "Sanitation",
`Sanitation.nn` = "Distance to Sanitation",
`Shot_Spotter` = "Shot Spotter",
`Shot_Spotter.nn` = "Distance to Shot Spotter",
`Street_Lights_Out` = "Street Light Out",
`Street_Lights_Out.nn` = "Distance to Broken Street Light"))) +
labs(title = "Scatter Plots of Predictor Variables",
caption = "Data: Chicago Data Portal") +
theme(plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
axis.text.x=element_text(size=6),
axis.text.y=element_text(size=6),
axis.title=element_text(size=8),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8))

Poisson Regression
Random K-Fold CV
The table below presents goodness of fit metrics for four regression
models. The first set of models includes only risk factors ‘reg.vars’,
while the second set ‘reg.ss.vars’ incorporates both risk factors and
the Local Moran’s I spatial process. To evaluate model performance, two
types of cross-validation were conducted. The first method assigns a
randomly generated cvID to each grid cell, allowing for random k-fold
cross-validation to assess prediction accuracy.
reg.vars <- c("Abandoned_Buildings.nn", "Abandoned_Cars.nn", "Graffiti.nn",
"Liquor_Retail.nn", "Street_Lights_Out.nn", "Sanitation.nn",
"Shot_Spotter.nn", "Bank.nn")
reg.ss.vars <- c("Abandoned_Buildings.nn", "Abandoned_Cars.nn", "Graffiti.nn",
"Liquor_Retail.nn", "Street_Lights_Out.nn", "Sanitation.nn",
"Shot_Spotter.nn", "Bank.nn", "robbery.isSig", "robbery.isSig.dist")
reg.cv <- crossValidate(
dataset = final_net,
id = "cvID",
dependentVariable = "countRobbery",
indVariables = reg.vars) %>%
dplyr::select(cvID = cvID, countRobbery, Prediction, geometry)
reg.ss.cv <- crossValidate(
dataset = final_net,
id = "cvID",
dependentVariable = "countRobbery",
indVariables = reg.ss.vars) %>%
dplyr::select(cvID = cvID, countRobbery, Prediction, geometry)
Spatial LOGO CV
The second one hold out one neighborhood, train the model on the
remaining n-1 areas, predict for the hold out, and record the goodness
of fit. This is also called the spatial ‘Leave-one-group-out’
cross-validation (LOGO-CV), in which each neighborhood takes a turn as a
hold-out. The result of each analysis is a sf layer with observed and
predicted burglary counts.
neighborhoods <-
st_read("https://raw.githubusercontent.com/blackmad/neighborhoods/master/chicago.geojson") %>%
st_transform(st_crs(fishnet))
final_net <-
st_centroid(final_net) %>%
st_join(dplyr::select(neighborhoods, name)) %>%
st_drop_geometry() %>%
left_join(dplyr::select(final_net, geometry, uniqueID)) %>%
st_sf() %>%
na.omit()
reg.spatialCV <- crossValidate(
dataset = final_net,
id = "name",
dependentVariable = "countRobbery",
indVariables = reg.vars) %>%
dplyr::select(cvID = name, countRobbery, Prediction, geometry)
reg.ss.spatialCV <- crossValidate(
dataset = final_net,
id = "name",
dependentVariable = "countRobbery",
indVariables = reg.ss.vars) %>%
dplyr::select(cvID = name, countRobbery, Prediction, geometry)
Error Analysis
The code block below creates a long form reg.summary, that binds
together observed/predicted counts and errors for each grid cell and for
each regression. Residuals are calculated for each regression by
subtracting the original robbery incidents from predicted incidents.
reg.summary <-
rbind(
mutate(reg.cv, Error = Prediction - countRobbery,
Regression = "Random k-fold CV: Just Risk Factors"),
mutate(reg.ss.cv, Error = Prediction - countRobbery,
Regression = "Random k-fold CV: Spatial Process"),
mutate(reg.spatialCV, Error = Prediction - countRobbery,
Regression = "Spatial LOGO-CV: Just Risk Factors"),
mutate(reg.ss.spatialCV, Error = Prediction - countRobbery,
Regression = "Spatial LOGO-CV: Spatial Process")) %>%
st_sf()
Comparing the error across different regressions, we see that adding
spatial process features improve the model. In addition, the model
appears slightly less robust for the spatial cross-validation, probably
because LOGO-CV is such a conservative assumption.
error_by_reg_and_fold <-
reg.summary %>%
group_by(Regression, cvID) %>%
summarize(Mean_Error = mean(Prediction - countRobbery, na.rm = T),
MAE = mean(abs(Mean_Error), na.rm = T),
SD_MAE = mean(abs(Mean_Error), na.rm = T)) %>% ungroup()
st_drop_geometry(error_by_reg_and_fold) %>%
group_by(Regression) %>%
summarize(Mean_MAE = round(mean(MAE), 2),
SD_MAE = round(sd(MAE), 2)) %>%
kable(col.name=c("Regression", 'Mean Absolute Error','Standard Deviation MAE')) %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed")) %>%
row_spec(2, color = "black", background = "#FCFFA4") %>%
row_spec(4, color = "black", background = "#FCFFA4")
Regression
|
Mean Absolute Error
|
Standard Deviation MAE
|
Random k-fold CV: Just Risk Factors
|
0.34
|
0.29
|
Random k-fold CV: Spatial Process
|
0.32
|
0.27
|
Spatial LOGO-CV: Just Risk Factors
|
0.93
|
0.96
|
Spatial LOGO-CV: Spatial Process
|
0.69
|
0.77
|
The small multiple maps below visualizes the distribution of errors
by regression. For the random k-fold cv method, the errors are pretty
evenly distributed across space. However, for spatial logo-cv method,
higher errors occur at spatial hotspots.
error_by_reg_and_fold %>%
ggplot() +
geom_sf(aes(fill=MAE), color="transparent") +
scale_fill_viridis(option = "turbo", direction = -1) +
facet_wrap(~Regression, ncol = 4) +
labs(title = "MAE by Fold and Regression",
caption = "Data: Chicago Data Portal") +
theme(legend.position = "bottom",
axis.text.x=element_blank(),
axis.ticks.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8),
strip.text = element_text(size = 7))

Prediction Accuracy
Generalizability Test
Let’s now test for generalizability across racial-neighborhood
context. To test this proposition, tidycensus is used to pull race data
by census tract. Percentage minority population for each census tract is
calculated by comparing the total number of Latinx, African American,
and Asian population to the total population. Census tracts are split
into two groups: majority white and minority, depending on whether the
proportion of minority population in that census tract exceeds 60%.
census_api_key(dlgInput(
"Enter a Census API Key", # ask for an api key
Sys.getenv("CENSUS_API_KEY")
)$res,
overwrite = TRUE)
tracts21 <-
get_acs(geography = "tract",
variables = c("B02001_001E", # total population
"B02001_002E", # white population
"B02001_003E", # black population
"B02001_005E", # asian population
"B03002_012E"),
year=2021, state=17, county=031,
geometry=TRUE, output="wide") %>%
st_transform('ESRI:102271') %>%
rename(TotalPop = B02001_001E,
Whites = B02001_002E,
African_Americans = B02001_003E,
Asians = B02001_005E,
Latinx = B03002_012E) %>%
mutate(pctMinority = ifelse(TotalPop > 0, (African_Americans + Asians + Latinx ) / TotalPop, 0),
majority = ifelse(pctMinority > 0.5, "minority", "majority")) %>% .[neighborhoods,]
We may see that Chicago remains a very segregated city and there’s
clear racial boundary between places where more than 60% of the
population are minority and places that’s majority white. In particular,
northern and northeastern (CBD) part of Chicago are majority white while
the remaining parts are all made up of racial minorities.
ggplot() + geom_sf(data = na.omit(tracts21), aes(fill = majority), color = NA) +
scale_fill_manual(values = c("#BB3754", "#56106E"), name="Race Context") +
labs(title = "Race Context ",
caption = "Data: American Community Survey 2021") +
theme(
axis.text.x=element_blank(),
axis.ticks.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank(),
plot.title = element_text(size = 12, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.4)
)

Error is calculated by subtracting the observed robbery count from
the prediction. A positive difference represents an over-prediction. The
least ideal result is a model that over-predicts risk in minority areas,
and under-predicts in white areas. If reporting selection bias is an
issue, such a model may unfairly allocate police resource
disproportionately in black and brown communities. In our case,
over-prediction of minorities is an issue for random k-fold cv
regression, but not for logo-cv regressions. The latter two models
under-predict minority neighborhoods and over-predict majority white
neighborhood, making it looks like that this algorithms generalizes well
with respect to race.
joinrace <- st_centroid(reg.summary) %>%
st_intersection(tracts21 %>%dplyr:: select(pctMinority)) %>%
st_drop_geometry() %>%
group_by(cvID) %>%
summarize(meanMinor = mean(pctMinority))
reg.summary <- reg.summary %>%
left_join(joinrace, by = "cvID")
reg.summary %>%
mutate(raceContext = ifelse(meanMinor > .6, "Minority", "Majority White")) %>%
st_drop_geometry() %>%
group_by(Regression, raceContext) %>%
summarize(mean.Error = mean(Error, na.rm = T)) %>%
spread(raceContext, mean.Error) %>%
kable() %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed")) %>%
row_spec(2, color = "black", background = "#FCFFA4") %>%
row_spec(4, color = "black", background = "#FCFFA4")
Regression
|
Majority White
|
Minority
|
Random k-fold CV: Just Risk Factors
|
0.2067014
|
-0.0155049
|
Random k-fold CV: Spatial Process
|
0.1954263
|
-0.0142177
|
Spatial LOGO-CV: Just Risk Factors
|
0.2224433
|
-0.1437801
|
Spatial LOGO-CV: Spatial Process
|
0.1220346
|
-0.0872692
|
Kernel Desntiy Estimation
The utility of this algorithm is judged relative to an alternative
police allocation method: kernel density estimation. Kernel density
works by centering a smooth kernel, or curve, atop each crime point such
that the curve is at its highest directly over the point and the lowest
at the range of a circular search radius. The code block below creates a
Kernel density map of robbery with a 1000 foot search radius.
rob_ppp <- as.ppp(st_coordinates(robbery21), W = st_bbox(final_net))
rob_KD.1000 <- spatstat.explore::density.ppp(rob_ppp, 1000)
rob_KD.df <- data.frame(rasterToPoints(mask(raster(rob_KD.1000), as(neighborhoods, 'Spatial'))))
ggplot(data=rob_KD.df, aes(x=x, y=y)) +
geom_raster(aes(fill=layer)) +
coord_sf(crs=st_crs(final_net)) +
scale_fill_viridis(option = "turbo", name="Density") +
labs(title = "Kernel Density of Robbery 1000ft Radii") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)

Next, a new goodness of fit indicator is created to illustrate
whether the 2021 kernel density or risk predictions capture more of the
2022 robberies.
robbery22 <-
read.socrata("https://data.cityofchicago.org/Public-Safety/Crimes-2022/9hwr-2zxp") %>%
filter(Primary.Type == "ROBBERY") %>%
mutate(x = gsub("[()]", "", Location)) %>%
separate(x,into= c("Y","X"), sep=",") %>%
mutate(X = as.numeric(X),
Y = as.numeric(Y)) %>%
na.omit %>%
st_as_sf(coords = c("X", "Y"), crs = 4326, agr = "constant") %>%
st_transform('ESRI:102271') %>%
distinct() %>%
.[fishnet,]
The kernel density estimation was classified into 5 risk categories
and the count for the 2022 robberies were summarized into the same sf
layer. The purpose here is to see if the risk predictions capture more
observed burglaries than the kernel density. If so, then the risk
prediction model provides a more robust targeting tool for allocating
police resources.
rob_KDE_sum <- as.data.frame(rob_KD.1000) %>%
st_as_sf(coords = c("x", "y"), crs = st_crs(final_net)) %>%
aggregate(., final_net, mean)
kde_breaks <- classIntervals(rob_KDE_sum$value,
n = 5, "fisher")
rob_KDE_sf <- rob_KDE_sum %>%
mutate(label = "Kernel Density",
Risk_Category = classInt::findCols(kde_breaks),
Risk_Category = case_when(
Risk_Category == 5 ~ "5th",
Risk_Category == 4 ~ "4th",
Risk_Category == 3 ~ "3rd",
Risk_Category == 2 ~ "2nd",
Risk_Category == 1 ~ "1st")) %>%
cbind(
aggregate(
dplyr::select(robbery21) %>% mutate(robberyCount = 1), ., sum) %>%
mutate(robberyCount = replace_na(robberyCount, 0))) %>%
dplyr::select(label, Risk_Category, robberyCount)
The same process was repeated for the risk prediction. Note that both
predictions from the LOGO-CV with and without the spatial features is
being used here.
ml_breaks <- classIntervals(reg.ss.spatialCV$Prediction,
n = 5, "fisher")
rob_risk_sf <-
reg.ss.spatialCV %>%
mutate(label = "Risk Predictions Spatial",
Risk_Category =classInt::findCols(ml_breaks),
Risk_Category = case_when(
Risk_Category == 5 ~ "5th",
Risk_Category == 4 ~ "4th",
Risk_Category == 3 ~ "3rd",
Risk_Category == 2 ~ "2nd",
Risk_Category == 1 ~ "1st")) %>%
cbind(
aggregate(
dplyr::select(robbery21) %>% mutate(robberyCount = 1), ., sum) %>%
mutate(robberyCount = replace_na(robberyCount, 0))) %>%
dplyr::select(label,Risk_Category, robberyCount)
ml_breaks_simple <- classIntervals(reg.ss.cv$Prediction,
n = 5, "fisher")
rob_risk_sf_simple <-
reg.ss.cv %>%
mutate(label = "Risk Predictions Simple",
Risk_Category =classInt::findCols(ml_breaks_simple),
Risk_Category = case_when(
Risk_Category == 5 ~ "5th",
Risk_Category == 4 ~ "4th",
Risk_Category == 3 ~ "3rd",
Risk_Category == 2 ~ "2nd",
Risk_Category == 1 ~ "1st")) %>%
cbind(
aggregate(
dplyr::select(robbery21) %>% mutate(robberyCount = 1), ., sum) %>%
mutate(robberyCount = replace_na(robberyCount, 0))) %>%
dplyr::select(label,Risk_Category, robberyCount)
Prediction Comparison
Below a map is generated of the risk categories for all three model
types. A strongly fit model should show that the highest risk category
is uniquely targeted to places with a high density of burglary points.
The map shows that our risk prediction models perform as good as the
kernal density approach in capturing the distribution of robbery
incidents.
rbind(rob_KDE_sf, rob_risk_sf, rob_risk_sf_simple) %>%
na.omit() %>%
gather(Variable, Value, -label, -Risk_Category, -geometry) %>%
ggplot() +
geom_sf(aes(fill = Risk_Category), colour = NA) +
facet_wrap(~label, ) +
scale_fill_viridis(option = "turbo", discrete = TRUE, name = "Risk Category") +
geom_sf(data = sample_n(robbery22, 3000), size = .03, colour = "white") +
labs(title="Comparison of Kernel Density and Risk Predictions",
subtitle="2021 robbery risk predictions; 2022 armed robberies") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks =element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 9,face = "italic"),
plot.title = element_text(size = 12, face = "bold"),
panel.background = element_blank(),
panel.border = element_rect(colour = "grey", fill=NA, size=0.8)
)

An effective way to assess prediction accuracy is by using a
histogram. Ideally, a well-fitting model should show that the
highest-risk areas capture a larger proportion of 2022 robberies
compared to the kernel density estimate. However, in this case, the
kernel density model outperforms both risk prediction models in the
highest-risk regions. Despite this, the risk prediction models excel in
identifying armed robbery incidents in lower-risk areas that the kernel
density model overlooked.
rbind(rob_KDE_sf, rob_risk_sf, rob_risk_sf_simple) %>%
st_drop_geometry() %>%
na.omit() %>%
gather(Variable, Value, -label, -Risk_Category) %>%
group_by(label, Risk_Category) %>%
summarize(countRobbery = sum(Value)) %>%
ungroup() %>%
group_by(label) %>%
mutate(Pcnt_of_test_set_crimes = countRobbery / sum(countRobbery)) %>%
ggplot(aes(Risk_Category,Pcnt_of_test_set_crimes)) +
geom_bar(aes(fill=label), position="dodge", stat="identity") +
scale_fill_viridis(option = "turbo", discrete = TRUE, name = "Model", direction = -1) +
labs(title = "Risk Prediction vs. Kernel Density",
y = "% of Test Set Robberies (per model)",
x = "Risk Category") +
theme_bw() +
theme(axis.text.x = element_text(angle = 45, vjust = 0.5))

Conclusions
In conclusion, our model requires significant improvement before it
can be implemented effectively. Compared to traditional kernel density
estimation, it struggled to capture armed robbery incidents in the
highest-risk areas. One key issue is that many of our predictor
variables, such as reports of abandoned cars, graffiti, and sanitation
complaints, were outdated, last updated in 2019. Using these to predict
robberies in 2021 and 2022 likely led to inaccurate results, especially
since shifts in these risk factors (like graffiti removal) would alter
crime patterns. Up-to-date datasets are crucial for more accurate
predictions.
Additionally, the COVID-19 pandemic likely impacted armed robbery
patterns, with economic distress, lockdowns, and reallocation of police
resources all contributing to shifts in crime locations. Our model needs
to account for such factors to ensure predictions remain relevant over
time.
On a positive note, the model performed better in lower-risk areas,
where the kernel density method fell short. However, relying on a single
model may not capture the full complexity of armed robbery patterns. A
combined approach using both kernel density estimation and risk
prediction could improve accuracy. While we avoided overpredicting in
majority non-white neighborhoods, potential selection bias remains a
concern. Incorporating spatial features and using k-fold
cross-validation helped improve the model, but further refinement is
needed to ensure it generalizes well across different contexts and time
periods.
LS0tCnRpdGxlOiAiUmlzayBQcmVkaWN0aW9ucyBvZiBBcm1lZCBSb2JiZXJ5IEluY2lkZW50cyBpbiBDaGljYWdvIgphdXRob3I6ICJZdXRvbmcgSmlhbmciCmRhdGU6ICJPY3QsMTgsMjAyNCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogY29zbW8gCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogeWVzCmVkaXRvcl9vcHRpb25zOgogIG1hcmtkb3duOgogICAgd3JhcDogc2VudGVuY2UKLS0tCgoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKCmBgYHtyIGxvYWQgbGlicmFyaWVzLCBpbmNsdWRlPUZBTFNFfQoKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoc2YpCmxpYnJhcnkoUlNvY3JhdGEpCmxpYnJhcnkodmlyaWRpcykKbGlicmFyeShzcGF0c3RhdC5leHBsb3JlKQpsaWJyYXJ5KHJhc3RlcikKbGlicmFyeShzcGRlcCkKbGlicmFyeShGTk4pCmxpYnJhcnkoZ3JpZCkKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeSh0aWR5Y2Vuc3VzKQpsaWJyYXJ5KGNsYXNzSW50KSAgICMgZm9yIEtERSBhbmQgTUwgcmlzayBjbGFzcyBpbnRlcnZhbHMKbGlicmFyeShzdkRpYWxvZ3MpCmxpYnJhcnkocGF0Y2h3b3JrKQoKIyBmdW5jdGlvbnMKcm9vdC5kaXIgPSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3VyYmFuU3BhdGlhbC9QdWJsaWMtUG9saWN5LUFuYWx5dGljcy1MYW5kaW5nL21hc3Rlci9EQVRBLyIKc291cmNlKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vdXJiYW5TcGF0aWFsL1B1YmxpYy1Qb2xpY3ktQW5hbHl0aWNzLUxhbmRpbmcvbWFzdGVyL2Z1bmN0aW9ucy5yIikKCmNoaWNhZ29Cb3VuZGFyeSA8LSAKICBzdF9yZWFkKGZpbGUucGF0aChyb290LmRpciwiL0NoYXB0ZXI1L2NoaWNhZ29Cb3VuZGFyeS5nZW9qc29uIikpICU+JQogIHN0X3RyYW5zZm9ybSgnRVNSSToxMDIyNzEnKSAKCmBgYAoKCiMgSW50cm9kdWN0aW9uCgpUaGlzIHByb2plY3QgIkdlb3NwYXRpYWwgUmlzayBQcmVkaWN0aW9ucyBvZiBBcm1lZCBSb2JiZXJ5IEluY2lkZW50cyBpbiBDaGljYWdvIiBleHBsb3JlcyB0aGUgYXBwbGljYXRpb24gb2YgcHJlZGljdGl2ZSBwb2xpY2luZyB0aHJvdWdoIGdlb3NwYXRpYWwgcmlzayBtb2RlbGluZyB0byBmb3JlY2FzdCB0aGUgbGlrZWxpaG9vZCBvZiBhcm1lZCByb2JiZXJ5IGluY2lkZW50cyBpbiBDaGljYWdvLiBCeSB1dGlsaXppbmcgaGlzdG9yaWNhbCBjcmltZSBkYXRhLCBzb2Npb2Vjb25vbWljIGluZGljYXRvcnMsIGFuZCBzcGF0aWFsIHJpc2sgZmFjdG9ycyBzdWNoIGFzIGFiYW5kb25lZCBjYXJzLCBzdHJlZXQgbGlnaHQgb3V0YWdlcywgYW5kIHNhbml0YXRpb24gY29tcGxhaW50cywgdGhlIG1vZGVsIGFpbXMgdG8gaWRlbnRpZnkgaGlnaC1yaXNrIGFyZWFzIGZvciBhcm1lZCByb2JiZXJ5IGluY2lkZW50cy4gVGhlIHN0dWR5IGVtcGxveXMgUG9pc3NvbiByZWdyZXNzaW9uIHRvIHByZWRpY3QgY3JpbWUgY291bnRzIGFuZCBjb21wYXJlcyB0aGUgYWNjdXJhY3kgb2Ygc3BhdGlhbCBtb2RlbHMgYWdhaW5zdCB0cmFkaXRpb25hbCBtZXRob2RzIGxpa2Uga2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbi4gRW1waGFzaXppbmcgdGhlIGltcG9ydGFuY2Ugb2YgYWRkcmVzc2luZyBiaWFzIGFuZCBkYXRhIHNlbGVjdGlvbiBpc3N1ZXMsIHRoZSBwcm9qZWN0IGFpbXMgdG8gaW1wcm92ZSB0aGUgZ2VuZXJhbGl6YWJpbGl0eSBhbmQgZmFpcm5lc3Mgb2YgY3JpbWUgcHJlZGljdGlvbiBtb2RlbHMgYWNyb3NzIGRpZmZlcmVudCBuZWlnaGJvcmhvb2RzLCBwYXJ0aWN1bGFybHkgY29uc2lkZXJpbmcgcmFjaWFsIGFuZCBzb2Npb2Vjb25vbWljIGRpc3Bhcml0aWVzLiBUaHJvdWdoIHNwYXRpYWwgY3Jvc3MtdmFsaWRhdGlvbiBhbmQgdGhlIGluY2x1c2lvbiBvZiBzcGF0aWFsIHByb2Nlc3MgZmVhdHVyZXMsIHRoaXMgYW5hbHlzaXMgc2Vla3MgdG8gZW5oYW5jZSBwcmVkaWN0aXZlIGFjY3VyYWN5IGFuZCBvZmZlciBpbnNpZ2h0cyBpbnRvIGJldHRlciByZXNvdXJjZSBhbGxvY2F0aW9uIGZvciBsYXcgZW5mb3JjZW1lbnQuCgpUaGUgb2JqZWN0aXZlIG9mIHRoaXMgcmVwb3J0IGlzIHRvIGRldmVsb3AgYSBnZW9zcGF0aWFsIHJpc2sgbW9kZWwgZm9yIHByZWRpY3RpbmcgYXJtZWQgcm9iYmVyeSBpbmNpZGVudHMgaW4gQ2hpY2Fnby4gUm9iYmVyeSBkYXRhIGlzIG9mdGVuIGluZmx1ZW5jZWQgYnkgc2VsZWN0aW9uIGJpYXMgZHVlIHRvIHR3byBrZXkgZmFjdG9yczogbm90IGFsbCByb2JiZXJ5IGluY2lkZW50cyBhcmUgcmVwb3J0ZWQgdG8gbGF3IGVuZm9yY2VtZW50LCBhbmQgYXJlYXMgd2l0aCBoaXN0b3JpY2FsbHkgaGlnaCByb2JiZXJ5IHJhdGVzLCB3aGljaCByZWNlaXZlIGluY3JlYXNlZCBwb2xpY2UgcHJlc2VuY2UsIHRlbmQgdG8gY29udGludWUgcmVwb3J0aW5nIGhpZ2hlciBpbmNpZGVudHMuIFRoZSB1bmRlcmx5aW5nIGh5cG90aGVzaXMgaXMgdGhhdCBjcmltZSByaXNrIGlzIGRyaXZlbiBieSBleHBvc3VyZSB0byB2YXJpb3VzIGdlb3NwYXRpYWwgcmlzayBhbmQgcHJvdGVjdGl2ZSBmYWN0b3JzLCBzdWNoIGFzIHNpZ25zIG9mIGJsaWdodCBvciB0aGUgcHJlc2VuY2Ugb2YgcmVjcmVhdGlvbiBjZW50ZXJzLiBBcyBleHBvc3VyZSB0byByaXNrIGZhY3RvcnMgcmlzZXMsIHNvIGRvZXMgdGhlIGxpa2VsaWhvb2Qgb2YgY3JpbWUuIEluIGFkZGl0aW9uIHRvIGlkZW50aWZ5aW5nIHRoZXNlIHJpc2sgZmFjdG9ycywgdGhlIG1vZGVsIGluY29ycG9yYXRlcyBzcGF0aWFsIHZhcmlhYmxlcyB0byBpbXByb3ZlIHByZWRpY3Rpb24gYWNjdXJhY3kuIFBvaXNzb24gcmVncmVzc2lvbiBpcyB1c2VkIGluIHRoaXMgYW5hbHlzaXMgZHVlIHRvIGl0cyBzdWl0YWJpbGl0eSBmb3IgbW9kZWxpbmcgY291bnQgZGF0YS4gCjEKCgoKIyBDcmltZSBEYXRhIENvbGxlY3Rpb24KCkFsbCBkYXRhc2V0cyBmb3IgdGhpcyBzdHVkeSB3ZXJlIHNvdXJjZWQgZnJvbSBbQ2hpY2FnbyBEYXRhIFBvcnRhbF0oaHR0cHM6Ly9kYXRhLmNpdHlvZmNoaWNhZ28ub3JnLykuIFRoZSBDaGljYWdvIERhdGEgUG9ydGFsIGlzIGFuIG9ubGluZSBwbGF0Zm9ybSB0aGF0IHByb3ZpZGVzIGFjY2VzcyB0byBhIHdpZGUgcmFuZ2Ugb2YgbXVuaWNpcGFsIGRhdGFzZXRzLiBUaGUgcG9ydGFsIGluY2x1ZGVzIGFuIGV4dGVuc2l2ZSByZWNvcmQgb2YgY3JpbWUgaW5jaWRlbnRzIGluIENoaWNhZ28sIHVwZGF0ZWQgYW5udWFsbHkuIEZvciB0aGlzIGFuYWx5c2lzLCBjcmltZSBkYXRhIGZvciAyMDIxIHdhcyBkb3dubG9hZGVkIHVzaW5nIHRoZSBSU29jcmF0YSBwYWNrYWdlLCB3aGljaCBhbGxvd3MgZWFzeSBhY2Nlc3MgdG8gb3BlbiBkYXRhIGZyb20gcGxhdGZvcm1zIGxpa2UgU29jcmF0YSwgdGhlIHNvZnR3YXJlIGJlaGluZCB0aGUgQ2hpY2FnbyBEYXRhIFBvcnRhbC4gCgpUaGlzIHJlcG9ydCBzcGVjaWZpY2FsbHkgZmlsdGVyIHRoZSBhcm1lZCByb2JiZXJ5IGNyaW1lIGluY2lkZW50cyBpbiBDaGljYWdvLiBUaGUgYXJtZWQgcm9iYmVyeSBpbmNsdWRlOiBLTklGRSAvIENVVFRJTkcgSU5TVFJVTUVOVCwgSEFOREdVTiwgT1RIRVIgRklSRUFSTSwgb3IgT1RIRVIgREFOR0VST1VTIFdFQVBPTi4gQWZ0ZXIgZmlsdGVyaW5nIHRoZSBjcmltZSBkYXRhIGZvciBhcm1lZCByb2JiZXJ5IGluY2lkZW50cywgd2UgZm9jdXNlZCBvbiB0aG9zZSBpbnZvbHZpbmcgdGhlIGZvbGxvd2luZyB3ZWFwb24gdHlwZXM6IGtuaWZlL2N1dHRpbmcgaW5zdHJ1bWVudCwgaGFuZGd1biwgb3RoZXIgZmlyZWFybXMsIG9yIG90aGVyIGRhbmdlcm91cyB3ZWFwb25zLiBUaGVzZSBzcGVjaWZpYyBjYXRlZ29yaWVzIHByb3ZpZGUgYSBjbGVhcmVyIHVuZGVyc3RhbmRpbmcgb2YgbW9yZSBzZXJpb3VzIGFuZCB2aW9sZW50IHJvYmJlcnkgb2ZmZW5zZXMsIGFzIHRoZXkgcG9zZSBhIGhpZ2hlciB0aHJlYXQgdG8gdmljdGltcyBhbmQgYXJlIG1vcmUgbGlrZWx5IHRvIG9jY3VyIGluIGFyZWFzIHdpdGggZGlzdGluY3QgcmlzayBmYWN0b3JzLiBCeSBuYXJyb3dpbmcgb3VyIGFuYWx5c2lzIHRvIGFybWVkIHJvYmJlcmllcywgd2UgYWltIHRvIGV4cGxvcmUgdGhlIHNwYXRpYWwgZGlzdHJpYnV0aW9uIG9mIHRoZXNlIGluY2lkZW50cyBhbmQgYXNzZXNzIGhvdyBlbnZpcm9ubWVudGFsIGFuZCBzb2NpYWwgdmFyaWFibGVzLCBzdWNoIGFzIGFiYW5kb25lZCBidWlsZGluZ3MsIHN0cmVldCBsaWdodCBvdXRhZ2VzLCBhbmQgc2FuaXRhdGlvbiBjb21wbGFpbnRzLCBjb3JyZWxhdGUgd2l0aCBoaWdoZXIgb2NjdXJyZW5jZXMgb2YgdGhlc2UgdmlvbGVudCBjcmltZXMuIFRoaXMgdGFyZ2V0ZWQgYXBwcm9hY2ggZW5hYmxlcyB1cyB0byBkZXZlbG9wIGEgbW9yZSBwcmVjaXNlIHByZWRpY3RpdmUgbW9kZWwgZm9yIHJvYmJlcnkgcmlzayBpbiBkaWZmZXJlbnQgYXJlYXMgb2YgQ2hpY2Fnby4KCmBgYHtyIGxvYWQgb3V0Y29tZSB2YXJpYWJsZSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgcmVzdWx0cz0naGlkZSd9CnJvYmJlcnkyMSA8LSByZWFkLnNvY3JhdGEoImh0dHBzOi8vZGF0YS5jaXR5b2ZjaGljYWdvLm9yZy9QdWJsaWMtU2FmZXR5L0NyaW1lcy0yMDIxL2R3bWUtdDk2YyIpCnNhdmVSRFMocm9iYmVyeTIxLCAicm9iYmVyeTIxLnJkcyIpCiMgUmVhZCBmcm9tIHRoZSBsb2NhbGx5IHNhdmVkIGZpbGUKcm9iYmVyeTIxIDwtIHJlYWRSRFMoInJvYmJlcnkyMS5yZHMiKSAlPiUgCiAgZmlsdGVyKFByaW1hcnkuVHlwZSA9PSAiUk9CQkVSWSIsIGdyZXBsKCJBUk1FRCIsIERlc2NyaXB0aW9uKSkgJT4lCiAgbXV0YXRlKHggPSBnc3ViKCJbKCldIiwgIiIsIExvY2F0aW9uKSkgJT4lCiAgc2VwYXJhdGUoeCxpbnRvPSBjKCJZIiwiWCIpLCBzZXA9IiwiKSAlPiUKICBtdXRhdGUoWCA9IGFzLm51bWVyaWMoWCksIFkgPSBhcy5udW1lcmljKFkpKSAlPiUgCiAgbmEub21pdCgpICU+JQogIHN0X2FzX3NmKGNvb3JkcyA9IGMoIlgiLCAiWSIpLCBjcnMgPSA0MzI2LCBhZ3IgPSAiY29uc3RhbnQiKSAlPiUKICBzdF90cmFuc2Zvcm0oJ0VTUkk6MTAyMjcxJykgJT4lCiAgZGlzdGluY3QoKQoKYGBgCgpUaGUgbWFwIGRpc3BsYXlzIHRoZSBzcGF0aWFsIGRpc3RyaWJ1dGlvbiBvZiBhcm1lZCByb2JiZXJ5IGluY2lkZW50cyBpbiBDaGljYWdvIGZvciAyMDIxLCByZXZlYWxpbmcgYSBjbGVhciBwYXR0ZXJuIG9mIGNvbmNlbnRyYXRpb24gaW4gc3BlY2lmaWMgYXJlYXMuIFRoZSBoaWdoZXN0IGRlbnNpdHkgb2YgaW5jaWRlbnRzIG9jY3VycyBpbiB0aGUgY2VudHJhbCBhbmQgc291dGhlcm4gcGFydHMgb2YgdGhlIGNpdHksIHdpdGggcGFydGljdWxhciBjbHVzdGVyaW5nIGFyb3VuZCB0aGUgY2l0eSdzIHNvdXRoIHNpZGUgYW5kIGNlcnRhaW4gd2VzdGVybiBuZWlnaGJvcmhvb2RzLiBUaGVzZSBhcmVhcyBsaWtlbHkgZXhwZXJpZW5jZSBoaWdoZXIgcmF0ZXMgb2YgdmlvbGVudCBjcmltZSBkdWUgdG8gYSBjb21iaW5hdGlvbiBvZiBzb2Npby1lY29ub21pYyBmYWN0b3JzIGFuZCBlbnZpcm9ubWVudGFsIGNvbmRpdGlvbnMuIEluIGNvbnRyYXN0LCB0aGUgbm9ydGhlcm4gcGFydHMgb2YgdGhlIGNpdHksIGVzcGVjaWFsbHkgbmVhciB0aGUgbGFrZWZyb250LCBzaG93IHNpZ25pZmljYW50bHkgZmV3ZXIgYXJtZWQgcm9iYmVyaWVzLiBUaGlzIHNwYXRpYWwgcGF0dGVybiBzdWdnZXN0cyB0aGF0IGNlcnRhaW4gcmVnaW9ucyBtYXkgZmFjZSBtb3JlIHByb25vdW5jZWQgY3JpbWUgcmlza3MsIHBvc3NpYmx5IGluZmx1ZW5jZWQgYnkgdXJiYW4gaW5mcmFzdHJ1Y3R1cmUsIGVjb25vbWljIGRpc3RyZXNzLCBvciBvdGhlciBmYWN0b3JzIHRoYXQgY29udHJpYnV0ZSB0byB0aGUgbGlrZWxpaG9vZCBvZiByb2JiZXJ5IGluY2lkZW50cy4gVW5kZXJzdGFuZGluZyB0aGVzZSBjbHVzdGVycyBjYW4gaGVscCBpbmZvcm0gdGFyZ2V0ZWQgaW50ZXJ2ZW50aW9ucyBhbmQgY3JpbWUgcHJldmVudGlvbiBzdHJhdGVnaWVzIGluIGhpZ2gtcmlzayBhcmVhcy4KCgpgYGB7ciByb2JiZXJ5IGxvY2F0aW9ucywgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZmlnLmFsaWduID0gJ2NlbnRlcid9CgpnZ3Bsb3QoKSArIAogIGdlb21fc2YoZGF0YSA9IGNoaWNhZ29Cb3VuZGFyeSwgZmlsbCA9ICJncmV5IikgKwogIGdlb21fc2YoZGF0YSA9IHJvYmJlcnkyMSwgY29sb3VyPSJkYXJrcmVkIiwgc2l6ZT0wLjEsIHNob3cubGVnZW5kID0gInBvaW50IikgKwogIGxhYnModGl0bGU9ICJBcm1lZCBSb2JiZXJ5IEluY2lkZW50cyBpbiBDaGljYWdvIDIwMjEiKSArIAogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcyA9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIGxpbmV3aWR0aCA9MC44KQogICAgICAgICkKCmBgYAoKQmVsb3cgaXMgdGhlIGRlbnNpdHkgbWFwIG9mIGFybWVkIHJvYmJlcnkgaW5jaWRlbnRzIGluIENoaWNhZ28uIFRoZSBkZW5zaXR5IG1hcCBvZiBhcm1lZCByb2JiZXJ5IGluY2lkZW50cyBpbiBDaGljYWdvIHJldmVhbHMgdHdvIHByb21pbmVudCBob3RzcG90cy4gVGhlIGZpcnN0IGNsdXN0ZXIgaXMgbG9jYXRlZCBpbiB0aGUgY2VudHJhbC13ZXN0ZXJuIHBhcnQgb2YgdGhlIGNpdHksIHdoaWxlIHRoZSBzZWNvbmQgc2lnbmlmaWNhbnQgY29uY2VudHJhdGlvbiBpcyBmb3VuZCBzbGlnaHRseSB0byB0aGUgc291dGggb2YgZG93bnRvd24uIFRoZXNlIGFyZWFzIGV4cGVyaWVuY2UgdGhlIGhpZ2hlc3QgaW50ZW5zaXR5IG9mIGFybWVkIHJvYmJlcmllcywgaW5kaWNhdGluZyBsb2NhbGl6ZWQgem9uZXMgd2l0aCBpbmNyZWFzZWQgcmlzay4gVGhlIHN1cnJvdW5kaW5nIGFyZWFzIHNob3cgYSBncmFkdWFsIGRlY3JlYXNlIGluIGRlbnNpdHkgYXMgd2UgbW92ZSBhd2F5IGZyb20gdGhlc2UgY29yZSBob3RzcG90cywgc3VnZ2VzdGluZyB0aGF0IGFybWVkIHJvYmJlcmllcyBhcmUgbm90IGV2ZW5seSBkaXN0cmlidXRlZCBhY3Jvc3MgdGhlIGNpdHkgYnV0IGFyZSBpbnN0ZWFkIGNvbmNlbnRyYXRlZCBpbiBzcGVjaWZpYyByZWdpb25zLiBUaGUgbm9ydGhlcm4gYW5kIGZhciBzb3V0aGVybiBwYXJ0cyBvZiB0aGUgY2l0eSBleGhpYml0IG11Y2ggbG93ZXIgZGVuc2l0aWVzLCBpbmRpY2F0aW5nIHRoYXQgYXJtZWQgcm9iYmVyaWVzIGFyZSByZWxhdGl2ZWx5IGxlc3MgY29tbW9uIGluIHRoZXNlIGFyZWFzLiBUaGlzIHBhdHRlcm4gaGlnaGxpZ2h0cyB0aGUgbmVlZCBmb3IgZm9jdXNlZCBjcmltZSBwcmV2ZW50aW9uIGVmZm9ydHMgaW4gdGhlIGlkZW50aWZpZWQgaGlnaC1yaXNrIHpvbmVzLgoKYGBge3Igcm9iYmVyeSBkZW5zaXR5LCBmaWcud2lkdGg9NSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmFsaWduID0gJ2NlbnRlcid9Cm9wdGlvbnMoc2NpcGVuPTApCmdncGxvdCgpICsgCiAgZ2VvbV9zZihkYXRhID0gY2hpY2Fnb0JvdW5kYXJ5LCBmaWxsID0gImdyZXkiKSArCiAgc3RhdF9kZW5zaXR5MmQoZGF0YSA9IGRhdGEuZnJhbWUoc3RfY29vcmRpbmF0ZXMocm9iYmVyeTIxKSksIAogICAgICAgICAgICAgICAgIGFlcyhYLCBZLCBmaWxsID0gLi5sZXZlbC4uLCBhbHBoYSA9IC4ubGV2ZWwuLiksCiAgICAgICAgICAgICAgICAgc2l6ZSA9IDAuMDEsIGJpbnMgPSA2MCwgZ2VvbSA9ICdwb2x5Z29uJykgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAidHVyYm8iLCBuYW1lID0gIkRlbnNpdHkiKSArCiAgc2NhbGVfYWxwaGEocmFuZ2UgPSBjKDAuMDAsIDAuMzUpLCBndWlkZSA9ICJub25lIikgKwogIGxhYnModGl0bGUgPSAiRGVuc2l0eSBvZiBBcm1lZCBSb2JiZXJ5IikgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcyA9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIGxpbmV3aWR0aD0wLjgpCiAgICAgICAgKQpgYGAKCgpDcmltZSByaXNrIHRlbmRzIHRvIGZsdWN0dWF0ZSBncmFkdWFsbHkgYWNyb3NzIGdlb2dyYXBoaWMgc3BhY2UgcmF0aGVyIHRoYW4gY29uZm9ybWluZyB0byBwcmVkZWZpbmVkIGFkbWluaXN0cmF0aXZlIGJvdW5kYXJpZXMuIFRvIGVmZmVjdGl2ZWx5IGNhcHR1cmUgdGhpcyBjb250aW51b3VzIHZhcmlhdGlvbiwgcG9pbnQtbGV2ZWwgZGF0YSBjYW4gYmUgYWdncmVnYXRlZCBpbnRvIGEgbmV0d29yayBvZiBldmVubHkgc3BhY2VkIGdyaWQgY2VsbHMuIFRoZSBjb2RlIHByb3ZpZGVkIGJlbG93IGdlbmVyYXRlcyBzdWNoIGEgZmlzaG5ldCBmb3IgQ2hpY2Fnbywgb2ZmZXJpbmcgYSBzdHJ1Y3R1cmVkIHdheSB0byByZXByZXNlbnQgY3JpbWUgcmlzayBzcGF0aWFsbHkuIFRoaXMgZ3JpZC1iYXNlZCBhcHByb2FjaCBpcyBpZGVhbCBmb3IgcHJlcGFyaW5nIHRoZSBkYXRhIGZvciByZWdyZXNzaW9uIGFuYWx5c2lzLCBhbGxvd2luZyBmb3IgYSBzbW9vdGhlciBkZXBpY3Rpb24gb2YgY3JpbWUgdHJlbmRzIGFjcm9zcyB0aGUgdXJiYW4gbGFuZHNjYXBlLgoKCmBgYHtyIGNyZWF0ZSBmaXNobmV0LCBmaWcud2lkdGg9NSwgZmlnLmFsaWduID0gJ2NlbnRlcid9CmZpc2huZXQgPC0gCiAgc3RfbWFrZV9ncmlkKGNoaWNhZ29Cb3VuZGFyeSwKICAgICAgICAgICAgICAgY2VsbHNpemUgPSA1MDAsIAogICAgICAgICAgICAgICBzcXVhcmUgPSBUUlVFKSAlPiUKICAuW2NoaWNhZ29Cb3VuZGFyeV0gJT4lICAgICAgICAgICAgIyBmYXN0IHdheSB0byBzZWxlY3QgaW50ZXJzZWN0aW5nIHBvbHlnb25zCiAgc3Rfc2YoKSAlPiUKICBtdXRhdGUodW5pcXVlSUQgPSAxOm4oKSkKCmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9ZmlzaG5ldCwgY29sb3I9ImJsYWNrIiwgZmlsbD0ibGlnaHRibHVlIikgKwogIGxhYnModGl0bGUgPSAiRmlzaG5ldCBvZiBDaGljYWdvIikgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcyA9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIGxpbmV3aWR0aD0wLjgpCiAgICAgICAgKQpgYGAKCk9uY2UgdGhlIGZpc2huZXQgd2FzIGNyZWF0ZWQsIHVzZSBpdCBhcyB0aGUgc3BhdGlhbCBmcmFtZXdvcmsgdG8gdmlzdWFsaXplIHRoZSBkaXN0cmlidXRpb24gb2Ygcm9iYmVyeSBpbmNpZGVudHMuIEZyb20gdGhpcywgaXQgYmVjb21lcyBjbGVhciB0aGF0IHRoZSBoaWdoZXN0IG51bWJlciBvZiByb2JiZXJpZXMgYXJlIGNvbmNlbnRyYXRlZCBpbiBhIGZldyBncmlkIGNlbGxzIGxvY2F0ZWQgaW4gbm9ydGhlYXN0IENoaWNhZ28uIFRoaXMgcGF0dGVybiBoaWdobGlnaHRzIHRoZSBlbWVyZ2VuY2Ugb2YgYSBjbHVzdGVyZWQgc3BhdGlhbCB0cmVuZCBpbiByb2JiZXJ5IG9jY3VycmVuY2VzLgoKYGBge3Igam9pbiByb2JiZXJ5IHRvIGZpc2huZXQsIGZpZy5hbGlnbiA9ICdjZW50ZXInfQoKcm9iYmVyeTIxX25ldCA8LSAKICBkcGx5cjo6c2VsZWN0KHJvYmJlcnkyMSkgJT4lIAogIG11dGF0ZShjb3VudFJvYmJlcnkgPSAxKSAlPiUgCiAgYWdncmVnYXRlKC4sIGZpc2huZXQsIHN1bSkgJT4lCiAgbXV0YXRlKGNvdW50Um9iYmVyeSA9IHJlcGxhY2VfbmEoY291bnRSb2JiZXJ5LCAwKSwKICAgICAgICAgdW5pcXVlSUQgPSAxOm4oKSwKICAgICAgICAgY3ZJRCA9IHNhbXBsZShyb3VuZChucm93KGZpc2huZXQpIC8gMjQpLCAKICAgICAgICAgICAgICAgICAgICAgICBzaXplPW5yb3coZmlzaG5ldCksIHJlcGxhY2UgPSBUUlVFKSkKCmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGEgPSByb2JiZXJ5MjFfbmV0LCBhZXMoZmlsbCA9IGNvdW50Um9iYmVyeSksIGNvbG9yID0gTkEpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMob3B0aW9uID0gInR1cmJvIiwgbmFtZSA9ICJSb2JiZXJ5IENvdW50cyIpICsKICBsYWJzKHRpdGxlID0gIkNvdW50IG9mIFJvYmJlcmllcyBmb3IgdGhlIEZpc2huZXQiKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzID1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgbGluZXdpZHRoPTAuOCkKICAgICAgICApCmBgYAoKVGhlIGJhciBjaGFydCBiZWxvdyBpbGx1c3RyYXRlcyB0aGUgZGlzdHJpYnV0aW9uIG9mIHJvYmJlcnkgaW5jaWRlbnRzIGluIENoaWNhZ28gZm9yIDIwMjEsIHJldmVhbGluZyBhIGhpZ2hseSBza2V3ZWQgcGF0dGVybi4gQSBsYXJnZSBudW1iZXIgb2YgZ3JpZCBjZWxscyBoYXZlIGZldyBvciBubyByb2JiZXJ5IGluY2lkZW50cywgd2hpbGUgb25seSBhIHNtYWxsIG51bWJlciBvZiBncmlkcyBleHBlcmllbmNlIHNpZ25pZmljYW50bHkgaGlnaGVyIHJvYmJlcnkgY291bnRzLiBUaGlzIG5vbi1ub3JtYWwgZGlzdHJpYnV0aW9uIHN1Z2dlc3RzIHRoYXQgUG9pc3NvbiByZWdyZXNzaW9uIGlzIGEgc3VpdGFibGUgbWV0aG9kIGZvciBhbmFseXppbmcgdGhpcyBkYXRhLCBhcyBpdCBjYW4gaGFuZGxlIHRoZSBvdmVyZGlzcGVyc2lvbiBhbmQgY291bnQgbmF0dXJlIG9mIHRoZSB2YXJpYWJsZS4KCmBgYHtyIGRpc3RyaWJ1dGlvbiBvZiByb2JiZXJpZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5hbGlnbiA9ICdjZW50ZXInfQpnZ3Bsb3Qocm9iYmVyeTIxX25ldCwgYWVzKHggPSBjb3VudFJvYmJlcnkpKSArCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJzdGVlbGJsdWUiLCBjb2xvciA9ICIjMmMzZTUwIiwgYmlucyA9IDMwKSArIAogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBmYWNlID0gInBsYWluIiwgc2l6ZSA9IDcpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KSwgCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSksIAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLCAKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyZXk4MCIpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTYwIiwgZmlsbCA9IE5BLCBzaXplID0gMC44KSkgKyAKICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBSb2JiZXJpZXMgaW4gQ2hpY2FnbyAyMDIxIiwKICAgICAgIHN1YnRpdGxlID0gIkFuYWx5c2lzIG9mIHJvYmJlcnkgaW5jaWRlbnQgZGlzdHJpYnV0aW9uIiwKICAgICAgIGNhcHRpb24gPSAiRGF0YTogQ2hpY2FnbyBEYXRhIFBvcnRhbCBDcmltZXMgMjAyMSIpICsKICB4bGFiKCJSb2JiZXJ5IEluY2lkZW50cyIpICsKICB5bGFiKCJDb3VudCIpCgpgYGAKCiMgUmlzayBGYWN0b3JzCgpJbiB0aGlzIHByb2plY3QsIGVpZ2h0IHJpc2sgZmFjdG9ycyBhcmUgaW5jbHVkZWQgaW4gdGhlIG1vZGVsIHRvIHByZWRpY3Qgcm9iYmVyaWVzOiByZXBvcnRzIG9mIGFiYW5kb25lZCBjYXJzLCBzdHJlZXQgbGlnaHRzIG91dCwgZ3JhZmZpdGksIHNhbml0YXRpb24gY29tcGxhaW50cywgYWJhbmRvbmVkIGJ1aWxkaW5ncywgbG9jYXRpb25zIG9mIGxpcXVvciBzdG9yZXMgc2VsbGluZyBhbGNvaG9sIHRvLWdvLCBTaG90U3BvdHRlciBkZXRlY3Rpb25zLCBhbmQgYmFua3MuIE5laWdoYm9yaG9vZHMgd2l0aCBhYmFuZG9uZWQgY2Fycywgc3RyZWV0IGxpZ2h0cyBvdXQsIGdyYWZmaXRpLCBhbmQgc2FuaXRhdGlvbiBpc3N1ZXMgb2Z0ZW4gaW5kaWNhdGUgbmVnbGVjdCwgZWNvbm9taWMgZGlzdHJlc3MsIGFuZCByZWR1Y2VkIGNvbW11bml0eSBvdmVyc2lnaHQsIHdoaWNoIGNhbiBjcmVhdGUgYW4gZW52aXJvbm1lbnQgY29uZHVjaXZlIHRvIGNyaW1pbmFsIGFjdGl2aXRpZXMuIEFiYW5kb25lZCBidWlsZGluZ3MgY2FuIHNlcnZlIGFzIGhpZGVvdXRzIGZvciBjcmltaW5hbHMsIGZ1cnRoZXIgY29udHJpYnV0aW5nIHRvIG5laWdoYm9yaG9vZCBkaXNvcmRlci4gTGlxdW9yIHN0b3JlcyBhcmUgYXNzb2NpYXRlZCB3aXRoIGFsY29ob2wtcmVsYXRlZCB2dWxuZXJhYmlsaXRpZXMsIHdoaWxlIFNob3RTcG90dGVyIGRldGVjdGlvbnMgcG9pbnQgdG8gYXJlYXMgd2l0aCBoaWdoZXIgZ3VuIHZpb2xlbmNlLiBGaW5hbGx5LCBiYW5rcywgZHVlIHRvIHRoZSBhdmFpbGFiaWxpdHkgb2YgY2FzaCwgbWF5IGF0dHJhY3Qgcm9iYmVycyBzZWVraW5nIGZpbmFuY2lhbCBnYWluLiBUaGVzZSBmYWN0b3JzIGNvbGxlY3RpdmVseSBoaWdobGlnaHQgYXJlYXMgdGhhdCBtYXkgYmUgbW9yZSBzdXNjZXB0aWJsZSB0byByb2JiZXJpZXMuCgpgYGB7ciBsb2FkIHByZWRpY3RvciB2YXJpYWJsZXMsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmFiYW5kb25DYXJzIDwtIAogIHJlYWQuc29jcmF0YSgiaHR0cHM6Ly9kYXRhLmNpdHlvZmNoaWNhZ28ub3JnL1NlcnZpY2UtUmVxdWVzdHMvMzExLVNlcnZpY2UtUmVxdWVzdHMtQWJhbmRvbmVkLVZlaGljbGVzLzNjOXYtcG52YSIpICU+JQogICAgbXV0YXRlKHllYXIgPSBzdWJzdHIoY3JlYXRpb25fZGF0ZSwxLDQpKSAlPiUgZmlsdGVyKHllYXIgPT0gIjIwMjAiKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoWSA9IGxhdGl0dWRlLCBYID0gbG9uZ2l0dWRlKSAlPiUKICAgIG5hLm9taXQoKSAlPiUKICAgIHN0X2FzX3NmKGNvb3JkcyA9IGMoIlgiLCAiWSIpLCBjcnMgPSA0MzI2LCBhZ3IgPSAiY29uc3RhbnQiKSAlPiUKICAgIHN0X3RyYW5zZm9ybShzdF9jcnMoZmlzaG5ldCkpICU+JQogICAgbXV0YXRlKExlZ2VuZCA9ICJBYmFuZG9uZWRfQ2FycyIpCiAgCmFiYW5kb25CdWlsZGluZ3MgPC0gCiAgcmVhZC5zb2NyYXRhKCJodHRwczovL2RhdGEuY2l0eW9mY2hpY2Fnby5vcmcvU2VydmljZS1SZXF1ZXN0cy8zMTEtU2VydmljZS1SZXF1ZXN0cy1WYWNhbnQtYW5kLUFiYW5kb25lZC1CdWlsZGluZy83bmlpLTdzcmQiKSAlPiUKICAgIG11dGF0ZSh5ZWFyID0gc3Vic3RyKGRhdGVfc2VydmljZV9yZXF1ZXN0X3dhc19yZWNlaXZlZCwxLDQpKSAlPiUgIGZpbHRlcih5ZWFyID09ICIyMDE4IikgJT4lCiAgICBkcGx5cjo6c2VsZWN0KFkgPSBsYXRpdHVkZSwgWCA9IGxvbmdpdHVkZSkgJT4lCiAgICBuYS5vbWl0KCkgJT4lCiAgICBzdF9hc19zZihjb29yZHMgPSBjKCJYIiwgIlkiKSwgY3JzID0gNDMyNiwgYWdyID0gImNvbnN0YW50IikgJT4lCiAgICBzdF90cmFuc2Zvcm0oc3RfY3JzKGZpc2huZXQpKSAlPiUKICAgIG11dGF0ZShMZWdlbmQgPSAiQWJhbmRvbmVkX0J1aWxkaW5ncyIpCgpncmFmZml0aSA8LSAKICByZWFkLnNvY3JhdGEoImh0dHBzOi8vZGF0YS5jaXR5b2ZjaGljYWdvLm9yZy9TZXJ2aWNlLVJlcXVlc3RzLzMxMS1TZXJ2aWNlLVJlcXVlc3RzLUdyYWZmaXRpLVJlbW92YWwtSGlzdG9yaWNhbC9oZWM1LXk0eDUiKSAlPiUKICAgIG11dGF0ZSh5ZWFyID0gc3Vic3RyKGNyZWF0aW9uX2RhdGUsMSw0KSkgJT4lIGZpbHRlcih5ZWFyID09ICIyMDE4IikgJT4lCiAgICBmaWx0ZXIod2hlcmVfaXNfdGhlX2dyYWZmaXRpX2xvY2F0ZWRfICVpbiUgYygiRnJvbnQiLCAiUmVhciIsICJTaWRlIikpICU+JQogICAgZHBseXI6OnNlbGVjdChZID0gbGF0aXR1ZGUsIFggPSBsb25naXR1ZGUpICU+JQogICAgbmEub21pdCgpICU+JQogICAgc3RfYXNfc2YoY29vcmRzID0gYygiWCIsICJZIiksIGNycyA9IDQzMjYsIGFnciA9ICJjb25zdGFudCIpICU+JQogICAgc3RfdHJhbnNmb3JtKHN0X2NycyhmaXNobmV0KSkgJT4lCiAgICBtdXRhdGUoTGVnZW5kID0gIkdyYWZmaXRpIikKCnN0cmVldExpZ2h0c091dCA8LSAKICByZWFkLnNvY3JhdGEoImh0dHBzOi8vZGF0YS5jaXR5b2ZjaGljYWdvLm9yZy9TZXJ2aWNlLVJlcXVlc3RzLzMxMS1TZXJ2aWNlLVJlcXVlc3RzLVN0cmVldC1MaWdodHMtQWxsLU91dC96dXhpLTd4ZW0iKSAlPiUKICAgIG11dGF0ZSh5ZWFyID0gc3Vic3RyKGNyZWF0aW9uX2RhdGUsMSw0KSkgJT4lIGZpbHRlcih5ZWFyID09ICIyMDE4IikgJT4lCiAgICBkcGx5cjo6c2VsZWN0KFkgPSBsYXRpdHVkZSwgWCA9IGxvbmdpdHVkZSkgJT4lCiAgICBuYS5vbWl0KCkgJT4lCiAgICBzdF9hc19zZihjb29yZHMgPSBjKCJYIiwgIlkiKSwgY3JzID0gNDMyNiwgYWdyID0gImNvbnN0YW50IikgJT4lCiAgICBzdF90cmFuc2Zvcm0oc3RfY3JzKGZpc2huZXQpKSAlPiUKICAgIG11dGF0ZShMZWdlbmQgPSAiU3RyZWV0X0xpZ2h0c19PdXQiKQoKc2FuaXRhdGlvbiA8LQogIHJlYWQuc29jcmF0YSgiaHR0cHM6Ly9kYXRhLmNpdHlvZmNoaWNhZ28ub3JnL1NlcnZpY2UtUmVxdWVzdHMvMzExLVNlcnZpY2UtUmVxdWVzdHMtU2FuaXRhdGlvbi1Db2RlLUNvbXBsYWludHMtSGkvbWU1OS01ZmFjIikgJT4lCiAgICBtdXRhdGUoeWVhciA9IHN1YnN0cihjcmVhdGlvbl9kYXRlLDEsNCkpICU+JSBmaWx0ZXIoeWVhciA9PSAiMjAxOCIpICU+JQogICAgZHBseXI6OnNlbGVjdChZID0gbGF0aXR1ZGUsIFggPSBsb25naXR1ZGUpICU+JQogICAgbmEub21pdCgpICU+JQogICAgc3RfYXNfc2YoY29vcmRzID0gYygiWCIsICJZIiksIGNycyA9IDQzMjYsIGFnciA9ICJjb25zdGFudCIpICU+JQogICAgc3RfdHJhbnNmb3JtKHN0X2NycyhmaXNobmV0KSkgJT4lCiAgICBtdXRhdGUoTGVnZW5kID0gIlNhbml0YXRpb24iKQoKbGlxdW9yUmV0YWlsIDwtIAogIHJlYWQuc29jcmF0YSgiaHR0cHM6Ly9kYXRhLmNpdHlvZmNoaWNhZ28ub3JnL3Jlc291cmNlL25ybWotM2tjZi5qc29uIikgJT4lICAKICAgIGZpbHRlcihidXNpbmVzc19hY3Rpdml0eSA9PSAiUmV0YWlsIFNhbGVzIG9mIFBhY2thZ2VkIExpcXVvciIpICU+JQogICAgZHBseXI6OnNlbGVjdChZID0gbGF0aXR1ZGUsIFggPSBsb25naXR1ZGUpICU+JQogICAgbmEub21pdCgpICU+JQogICAgc3RfYXNfc2YoY29vcmRzID0gYygiWCIsICJZIiksIGNycyA9IDQzMjYsIGFnciA9ICJjb25zdGFudCIpICU+JQogICAgc3RfdHJhbnNmb3JtKHN0X2NycyhmaXNobmV0KSkgJT4lCiAgICBtdXRhdGUoTGVnZW5kID0gIkxpcXVvcl9SZXRhaWwiKQoKCnNob3RTcG90dGVyIDwtIAogIHJlYWQuc29jcmF0YSgiaHR0cHM6Ly9kYXRhLmNpdHlvZmNoaWNhZ28ub3JnL1B1YmxpYy1TYWZldHkvVmlvbGVuY2UtUmVkdWN0aW9uLVNob3RzcG90dGVyLUFsZXJ0cy8zaDdxLTdtZGIiKSAlPiUgIAogICAgZHBseXI6OnNlbGVjdChZID0gbGF0aXR1ZGUsIFggPSBsb25naXR1ZGUpICU+JQogICAgbmEub21pdCgpICU+JQogICAgc3RfYXNfc2YoY29vcmRzID0gYygiWCIsICJZIiksIGNycyA9IDQzMjYsIGFnciA9ICJjb25zdGFudCIpICU+JQogICAgc3RfdHJhbnNmb3JtKHN0X2NycyhmaXNobmV0KSkgJT4lCiAgICBtdXRhdGUoTGVnZW5kID0gIlNob3RfU3BvdHRlciIpCgpCYW5rcyA8LSAKICByZWFkLnNvY3JhdGEoImh0dHBzOi8vZGF0YS5jaXR5b2ZjaGljYWdvLm9yZy9BZG1pbmlzdHJhdGlvbi1GaW5hbmNlL0xlbmRpbmctRXF1aXR5LURlcG9zaXRvcnktTG9jYXRpb25zLTIwMjEvamd1cC16dDdrIikgJT4lICAKICBkcGx5cjo6c2VsZWN0KGxvY2F0aW9uKSAlPiUKICBuYS5vbWl0KCkKQmFua3MgPC0gQmFua3MgJT4lICAgCiAgbXV0YXRlKFggPSBhcy5udW1lcmljKHN1YigiUE9JTlQgXFwoKC0/XFxkK1xcLlxcZCspIFxcZCtcXC5cXGQrXFwpIiwgIlxcMSIsIEJhbmtzJGxvY2F0aW9uKSksCiAgICAgICAgIFkgPSBhcy5udW1lcmljKHN1YigiUE9JTlQgXFwoLT9cXGQrXFwuXFxkKyAoLT9cXGQrXFwuXFxkKylcXCkiLCAiXFwxIiwgQmFua3MkbG9jYXRpb24pKSkgJT4lCiAgZHBseXI6OnNlbGVjdCgtbG9jYXRpb24pICU+JSAKICBmaWx0ZXIoIWlzLm5hKFgpICYgIWlzLm5hKFkpKSAlPiUgCiAgc3RfYXNfc2YoY29vcmRzID0gYygiWCIsICJZIiksIGNycyA9IDQzMjYsIGFnciA9ICJjb25zdGFudCIpICU+JQogICAgc3RfdHJhbnNmb3JtKHN0X2NycyhmaXNobmV0KSkgJT4lCiAgICBtdXRhdGUoTGVnZW5kID0gIkJhbmsiKSAKICAKCmBgYAoKCgpgYGB7ciBqb2luIHByZWRpY3RvciB2YXJpYWJsZXMgdG8gZmlzaG5ldCwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KCnZhcnNfbmV0IDwtIAogIHJiaW5kKGFiYW5kb25DYXJzLHN0cmVldExpZ2h0c091dCxhYmFuZG9uQnVpbGRpbmdzLAogICAgICAgIGxpcXVvclJldGFpbCwgZ3JhZmZpdGksIHNhbml0YXRpb24sIHNob3RTcG90dGVyLCBCYW5rcykgJT4lCiAgc3Rfam9pbiguLCBmaXNobmV0LCBqb2luPXN0X3dpdGhpbikgJT4lCiAgc3RfZHJvcF9nZW9tZXRyeSgpICU+JQogIGdyb3VwX2J5KHVuaXF1ZUlELCBMZWdlbmQpICU+JQogIHN1bW1hcml6ZShjb3VudCA9IG4oKSkgJT4lCiAgICBmdWxsX2pvaW4oZmlzaG5ldCkgJT4lCiAgICBzcHJlYWQoTGVnZW5kLCBjb3VudCwgZmlsbD0wKSAlPiUKICAgIHN0X3NmKCkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KC1gPE5BPmApICU+JQogICAgbmEub21pdCgpICU+JQogICAgdW5ncm91cCgpCgpgYGAKClRoZSBmaWd1cmUgaWxsdXN0cmF0ZXMgdGhlIGRpc3RpbmN0IHNwYXRpYWwgcGF0dGVybnMgb2YgZWFjaCByaXNrIGZhY3RvciBhY3Jvc3MgQ2hpY2Fnby4gQWJhbmRvbmVkIGJ1aWxkaW5ncyBhcmUgcHJlZG9taW5hbnRseSBjb25jZW50cmF0ZWQgaW4gdGhlIHNvdXRoZXJuIHBhcnQgb2YgdGhlIGNpdHksIHdoaWxlIGJhbmtzIGFuZCBsaXF1b3IgcmV0YWlsIHN0b3JlcyBjbHVzdGVyIGFyb3VuZCB0aGUgY2VudHJhbCBidXNpbmVzcyBkaXN0cmljdCAoQ0JEKS4gQWJhbmRvbmVkIGNhcnMsIHNhbml0YXRpb24gY29tcGxhaW50cywgYW5kIHN0cmVldCBsaWdodHMgb3V0IGNvbXBsYWludHMgYXJlIG1vcmUgZXZlbmx5IHNjYXR0ZXJlZCB0aHJvdWdob3V0IHRoZSBjaXR5LiBHcmFmZml0aSByZW1vdmFsIHJlcXVlc3RzIGFyZSBtYWlubHkgY29uY2VudHJhdGVkIG5lYXIgdGhlIGNpdHkgY2VudGVyLCB3aGVyZWFzIFNob3RTcG90dGVyIGRldGVjdGlvbnMgdGVuZCB0byBvY2N1ciBpbiBhcmVhcyBvdXRzaWRlIHRoZSBjaXR5IGNlbnRlciwgaGlnaGxpZ2h0aW5nIHRoZSBzcGF0aWFsIHZhcmlhYmlsaXR5IGluIGZhY3RvcnMgYXNzb2NpYXRlZCB3aXRoIHBvdGVudGlhbCByb2JiZXJ5IHJpc2tzLgoKYGBge3IgdmlzdWFsaXplIHByZWRpY3RvciB2YXJpYWJsZXMsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTExLCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KdmFyc19uZXQubG9uZyA8LSAKICBnYXRoZXIodmFyc19uZXQsIFZhcmlhYmxlLCB2YWx1ZSwgLWdlb21ldHJ5LCAtdW5pcXVlSUQpCgpwbG90MSA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhID0gdmFyc19uZXQubG9uZyAlPiUgZmlsdGVyKFZhcmlhYmxlID09ICJBYmFuZG9uZWRfQnVpbGRpbmdzIiksIGFlcyhmaWxsPXZhbHVlKSwgY29sb3VyPU5BKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbiA9ICJ0dXJibyIpICsgCiAgbGFicyh0aXRsZSA9ICJBYmFuZG9uZWQgQnVpbGRpbmdzIikgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcyA9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIHNpemU9MC44KQogICAgICAgICkKCgpwbG90MiA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhID0gdmFyc19uZXQubG9uZyAlPiUgZmlsdGVyKFZhcmlhYmxlID09ICJBYmFuZG9uZWRfQ2FycyIpLCBhZXMoZmlsbD12YWx1ZSksIGNvbG91cj1OQSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAidHVyYm8iKSArIAogIGxhYnModGl0bGUgPSAiQWJhbmRvbmVkIENhcnMiKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzID1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpCiAgICAgICAgKQoKcGxvdDMgPC0gZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YSA9IHZhcnNfbmV0LmxvbmcgJT4lIGZpbHRlcihWYXJpYWJsZSA9PSAiQmFuayIpLCBhZXMoZmlsbD12YWx1ZSksIGNvbG91cj1OQSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAidHVyYm8iKSArIAogIGxhYnModGl0bGUgPSAiQmFua3MiKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzID1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpCiAgICAgICAgKQoKcGxvdDQgPC0gZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YSA9IHZhcnNfbmV0LmxvbmcgJT4lIGZpbHRlcihWYXJpYWJsZSA9PSAiR3JhZmZpdGkiKSwgYWVzKGZpbGw9dmFsdWUpLCBjb2xvdXI9TkEpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMob3B0aW9uID0gInR1cmJvIikgKyAKICBsYWJzKHRpdGxlID0gIkdyYWZmaXRpIikgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcyA9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIHNpemU9MC44KQogICAgICAgICkKCgpwbG90NSA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhID0gdmFyc19uZXQubG9uZyAlPiUgZmlsdGVyKFZhcmlhYmxlID09ICJMaXF1b3JfUmV0YWlsIiksIGFlcyhmaWxsPXZhbHVlKSwgY29sb3VyPU5BKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbiA9ICJ0dXJibyIpICsgCiAgbGFicyh0aXRsZSA9ICJMaXF1b3IgUmV0YWlsIikgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcyA9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIHNpemU9MC44KQogICAgICAgICkKCnBsb3Q2IDwtIGdncGxvdCgpICsKICBnZW9tX3NmKGRhdGEgPSB2YXJzX25ldC5sb25nICU+JSBmaWx0ZXIoVmFyaWFibGUgPT0gIlNhbml0YXRpb24iKSwgYWVzKGZpbGw9dmFsdWUpLCBjb2xvdXI9TkEpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMob3B0aW9uID0gInR1cmJvIikgKyAKICBsYWJzKHRpdGxlID0gIlNhbml0YXRpb24iKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzID1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpCiAgICAgICAgKQoKcGxvdDcgPC0gZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YSA9IHZhcnNfbmV0LmxvbmcgJT4lIGZpbHRlcihWYXJpYWJsZSA9PSAiU2hvdF9TcG90dGVyIiksIGFlcyhmaWxsPXZhbHVlKSwgY29sb3VyPU5BKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbiA9ICJ0dXJibyIpICsgCiAgbGFicyh0aXRsZSA9ICJTaG90IFNwb3R0ZXIiKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzID1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpCiAgICAgICAgKQoKCnBsb3Q4IDwtIGdncGxvdCgpICsKICBnZW9tX3NmKGRhdGEgPSB2YXJzX25ldC5sb25nICU+JSBmaWx0ZXIoVmFyaWFibGUgPT0gIlN0cmVldF9MaWdodHNfT3V0IiksIGFlcyhmaWxsPXZhbHVlKSwgY29sb3VyPU5BKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbiA9ICJ0dXJibyIpICsgCiAgbGFicyh0aXRsZSA9ICJTdHJlZXQgTGlnaHQgT3V0IikgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcyA9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIHNpemU9MC44KQogICAgICAgICkKCndyYXBfcGxvdHMoCiAgcGxvdDEsIHBsb3QzLCBwbG90MiwgcGxvdDQsCiAgcGxvdDUsIHBsb3Q2LCBwbG90NywgcGxvdDgsCiAgbmNvbCA9IDQgIAopCgoKYGBgCgojIEZlYXR1cmUgRW5naW5lZXJpbmcKCgojIyBOZWFyZXN0IERpc3RhbmNlCgpUaGUgZmlyc3QgZmVhdHVyZSBlbmdpbmVlcmluZyBhcHByb2FjaCB3ZSB0b29rIGlzIHRvIGNhbGN1bGF0ZSBhdmVyYWdlIG5lYXJlc3QgbmVpZ2hib3IgZGlzdGFuY2UgdG8gaHlwb3RoZXNpemUgYSBzbW9vdGhlciBleHBvc3VyZSByZWxhdGlvbnNoaXAgYWNyb3NzIHNwYWNlLiBIZXJlLCB0aGUgbm5fZnVuY3Rpb24gaXMgdXNlZC4gRm9yIGVhY2ggb2Ygb3VyIHJpc2sgZmFjdG9ycywgYXZlcmFnZSBuZWFyZXN0IGRpc3RhbmNlIGlzIGNhbGN1bGF0ZWQgZnJvbSB0aGUgY2VudHJvaWQgb2YgZWFjaCBjZWxsIHRvIGl0cyBuZWFyZXN0IHRocmVlLCBhYmFuZG9uZWQgY2FycywgZm9yIGV4YW1wbGUuIAoKYGBge3IgY2FsY3VsYXRlIG5lYXJlc3QgZGlzdGFuY2UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CgpzdF9jIDwtIHN0X2Nvb3JkaW5hdGVzCnN0X2NvaWQgPC0gc3RfY2VudHJvaWQKCnZhcnNfbmV0IDwtCiAgdmFyc19uZXQgJT4lCiAgICBtdXRhdGUoCiAgICAgIEFiYW5kb25lZF9CdWlsZGluZ3Mubm4gPQogICAgICAgIG5uX2Z1bmN0aW9uKHN0X2Moc3RfY29pZCh2YXJzX25ldCkpLCBzdF9jKGFiYW5kb25CdWlsZGluZ3MpLDMpLAogICAgICBBYmFuZG9uZWRfQ2Fycy5ubiA9CiAgICAgICAgbm5fZnVuY3Rpb24oc3RfYyhzdF9jb2lkKHZhcnNfbmV0KSksIHN0X2MoYWJhbmRvbkNhcnMpLDMpLAogICAgICBHcmFmZml0aS5ubiA9CiAgICAgICAgbm5fZnVuY3Rpb24oc3RfYyhzdF9jb2lkKHZhcnNfbmV0KSksIHN0X2MoZ3JhZmZpdGkpLDMpLAogICAgICBMaXF1b3JfUmV0YWlsLm5uID0KICAgICAgICBubl9mdW5jdGlvbihzdF9jKHN0X2NvaWQodmFyc19uZXQpKSwgc3RfYyhsaXF1b3JSZXRhaWwpLDMpLAogICAgICBTdHJlZXRfTGlnaHRzX091dC5ubiA9CiAgICAgICAgbm5fZnVuY3Rpb24oc3RfYyhzdF9jb2lkKHZhcnNfbmV0KSksIHN0X2Moc3RyZWV0TGlnaHRzT3V0KSwzKSwKICAgICAgU2FuaXRhdGlvbi5ubiA9CiAgICAgICAgbm5fZnVuY3Rpb24oc3RfYyhzdF9jb2lkKHZhcnNfbmV0KSksIHN0X2Moc2FuaXRhdGlvbiksMyksCiAgICAgIFNob3RfU3BvdHRlci5ubiA9CiAgICAgICAgbm5fZnVuY3Rpb24oc3RfYyhzdF9jb2lkKHZhcnNfbmV0KSksIHN0X2Moc2hvdFNwb3R0ZXIpLDMpKQoKdmFyc19uZXQgPC0KICB2YXJzX25ldCAlPiUgbXV0YXRlKEJhbmsubm4gPSBubl9mdW5jdGlvbihzdF9jKHN0X2NvaWQodmFyc19uZXQpKSwgc3RfYyhCYW5rcyksMykpCiAKCmBgYAoKVGhlIG5laWdoYm9yIGZlYXR1cmVzIHByb3ZpZGUgYSBjbGVhcmVyIHBpY3R1cmUgb2YgdGhlIHNwYXRpYWwgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIGRpZmZlcmVudCByaXNrIGZhY3RvcnMuVGhlIHNvdXRoZXJuIGFuZCBzb3V0aGVhc3Rlcm4gYXJlYXMgb2YgQ2hpY2FnbyBhcHBlYXIgdG8gYmUgbW9zdCBkaXN0YW50IGZyb20gbWFueSBvZiB0aGVzZSByaXNrcywgcGFydGljdWxhcmx5IGFiYW5kb25lZCBidWlsZGluZ3MsIGFiYW5kb25lZCBjYXJzLCBhbmQgZ3JhZmZpdGkuIEludGVyZXN0aW5nbHksIHRoZSBzb3V0aHdlc3Rlcm4gY29ybmVyIGFsc28gc2hvd3Mgc29tZSBkaXN0YW5jZSBmcm9tIHJpc2sgZmFjdG9ycyBsaWtlIGJhbmtzIGFuZCBsaXF1b3Igc3RvcmVzLiBPbmUgbm90YWJsZSBvYnNlcnZhdGlvbiBpcyB0aGF0IHRoZSBub3J0aGVybiBwYXJ0IG9mIHRoZSBjaXR5IGlzIGZ1cnRoZXN0IGZyb20gU2hvdFNwb3R0ZXIgbG9jYXRpb25zLCB3aGlsZSBzdGlsbCBiZWluZyByZWxhdGl2ZWx5IGNsb3NlIHRvIG90aGVyIHJpc2sgZmFjdG9ycyBzdWNoIGFzIHNhbml0YXRpb24gaXNzdWVzIGFuZCBzdHJlZXQgbGlnaHRzIG91dC4gVGhlIHJlbGF0aXZlIGFic2VuY2Ugb2YgU2hvdFNwb3R0ZXJzIGluIHRoaXMgYXJlYSByYWlzZXMgcXVlc3Rpb25zIGFib3V0IHRoZWlyIHJvbGU6IGFyZSB0aGV5IGFuIGluZGljYXRvciBvZiBoaWdoZXIgY3JpbWUgZHVlIHRvIHRoZWlyIGNvbmNlbnRyYXRpb24sIG9yIGRvZXMgdGhlaXIgcHJlc2VuY2Ugc3VnZ2VzdCBpbmNyZWFzZWQgcG9saWNpbmcsIG1ha2luZyBuZWlnaGJvcmhvb2RzIHNhZmVyPyBUaGVzZSBvYnNlcnZhdGlvbnMgaGlnaGxpZ2h0IHRoZSBuZWVkIHRvIGV4YW1pbmUgd2hldGhlciBTaG90U3BvdHRlcnMgZnVuY3Rpb24gYXMgYSBjcmltZSBkZXRlY3Rpb24gdG9vbCBvciBhIHByb3h5IGZvciBuZWlnaGJvcmhvb2Qgc2FmZXR5LiBUaGVzZSBhcmUgaW1wb3J0YW50IGNvbnNpZGVyYXRpb25zIGZvciB1bmRlcnN0YW5kaW5nIHRoZSBicm9hZGVyIGR5bmFtaWNzIG9mIGNyaW1lIGFuZCBzYWZldHkgaW4gQ2hpY2Fnby4KCmBgYHtyIHZpc3VhbGl6ZSBuZWFyZXN0IGRpc3RhbmNlLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMSwgZmlnLmFsaWduID0gJ2NlbnRlcid9Cgp2YXJzX25ldC5sb25nLm5uIDwtIAogIGRwbHlyOjpzZWxlY3QodmFyc19uZXQsIGVuZHNfd2l0aCgiLm5uIikpICU+JQogICAgZ2F0aGVyKFZhcmlhYmxlLCB2YWx1ZSwgLWdlb21ldHJ5KQoKCnAxLm5uIDwtIGdncGxvdCgpICsKICBnZW9tX3NmKGRhdGEgPSB2YXJzX25ldC5sb25nLm5uICU+JSBmaWx0ZXIoVmFyaWFibGUgPT0gIkFiYW5kb25lZF9CdWlsZGluZ3Mubm4iKSwgYWVzKGZpbGw9dmFsdWUpLCBjb2xvdXI9TkEpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMob3B0aW9uID0gInR1cmJvIikgKyAKICBsYWJzKHRpdGxlID0gIkFiYW5kb25lZCBCdWlsZGluZ3MgTk4iKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzID1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpCiAgICAgICAgKQoKCnAyLm5uIDwtIGdncGxvdCgpICsKICBnZW9tX3NmKGRhdGEgPSB2YXJzX25ldC5sb25nLm5uICU+JSBmaWx0ZXIoVmFyaWFibGUgPT0gIkFiYW5kb25lZF9DYXJzLm5uIiksIGFlcyhmaWxsPXZhbHVlKSwgY29sb3VyPU5BKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbiA9ICJ0dXJibyIpICsgCiAgbGFicyh0aXRsZSA9ICJBYmFuZG9uZWQgQ2FycyBOTiIpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MgPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkKICAgICAgICApCgpwMy5ubiA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhID0gdmFyc19uZXQubG9uZy5ubiAlPiUgZmlsdGVyKFZhcmlhYmxlID09ICJCYW5rLm5uIiksIGFlcyhmaWxsPXZhbHVlKSwgY29sb3VyPU5BKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbiA9ICJ0dXJibyIpICsgCiAgbGFicyh0aXRsZSA9ICJCYW5rcyBOTiIpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MgPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkKICAgICAgICApCgpwNC5ubiA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhID0gdmFyc19uZXQubG9uZy5ubiAlPiUgZmlsdGVyKFZhcmlhYmxlID09ICJHcmFmZml0aS5ubiIpLCBhZXMoZmlsbD12YWx1ZSksIGNvbG91cj1OQSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAidHVyYm8iKSArIAogIGxhYnModGl0bGUgPSAiR3JhZmZpdGkgTk4iKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzID1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpCiAgICAgICAgKQoKCnA1Lm5uIDwtIGdncGxvdCgpICsKICBnZW9tX3NmKGRhdGEgPSB2YXJzX25ldC5sb25nLm5uICU+JSBmaWx0ZXIoVmFyaWFibGUgPT0gIkxpcXVvcl9SZXRhaWwubm4iKSwgYWVzKGZpbGw9dmFsdWUpLCBjb2xvdXI9TkEpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMob3B0aW9uID0gInR1cmJvIikgKyAKICBsYWJzKHRpdGxlID0gIkxpcXVvciBSZXRhaWwgTk4iKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzID1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpCiAgICAgICAgKQoKcDYubm4gPC0gZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YSA9IHZhcnNfbmV0Lmxvbmcubm4gJT4lIGZpbHRlcihWYXJpYWJsZSA9PSAiU2FuaXRhdGlvbi5ubiIpLCBhZXMoZmlsbD12YWx1ZSksIGNvbG91cj1OQSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAidHVyYm8iKSArIAogIGxhYnModGl0bGUgPSAiU2FuaXRhdGlvbiBOTiIpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MgPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkKICAgICAgICApCgpwNy5ubiA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhID0gdmFyc19uZXQubG9uZy5ubiAlPiUgZmlsdGVyKFZhcmlhYmxlID09ICJTaG90X1Nwb3R0ZXIubm4iKSwgYWVzKGZpbGw9dmFsdWUpLCBjb2xvdXI9TkEpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMob3B0aW9uID0gInR1cmJvIikgKyAKICBsYWJzKHRpdGxlID0gIlNob3QgU3BvdHRlciBOTiIpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MgPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkKICAgICAgICApCgoKcDgubm4gPC0gZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YSA9IHZhcnNfbmV0Lmxvbmcubm4gJT4lIGZpbHRlcihWYXJpYWJsZSA9PSAiU3RyZWV0X0xpZ2h0c19PdXQubm4iKSwgYWVzKGZpbGw9dmFsdWUpLCBjb2xvdXI9TkEpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMob3B0aW9uID0gInR1cmJvIikgKyAKICBsYWJzKHRpdGxlID0gIlN0cmVldCBMaWdodCBPdXQgTk4iKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzID1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpCiAgICAgICAgKQoKd3JhcF9wbG90cygKICBwMS5ubiwgcDMubm4sIHAyLm5uLCBwNC5ubiwKICBwNS5ubiwgcDYubm4sIHA3Lm5uLCBwOC5ubiwKICBuY29sID0gNCAgCikKCgpgYGAKCgojIyBMb2NhbCBTcGF0aWFsIEF1dG9jb3JyZWxhdGlvbgoKaWRlbnRpZmllZCByb2JiZXJ5IGhvdHNwb3RzIGluIENoaWNhZ28gdXNpbmcgTG9jYWwgTW9yYW4ncyBJLCB3aGljaCBleGFtaW5lcyB3aGV0aGVyIHJvYmJlcnkgY291bnRzIGF0IGEgbG9jYXRpb24gYXJlIGNsdXN0ZXJlZCByZWxhdGl2ZSB0byBuZWlnaGJvcmluZyBjZWxscy4gQSBzcGF0aWFsIHdlaWdodHMgbWF0cml4IHdhcyBjYWxjdWxhdGVkIHVzaW5nIHF1ZWVuIGNvbnRpZ3VpdHkgdG8gZGVmaW5lIG5laWdoYm9yaW5nIHJlbGF0aW9uc2hpcHMuIFRoZSByZXN1bHRzLCB2aXN1YWxpemVkIHdpdGggTW9yYW4ncyBJIHZhbHVlcywgcC12YWx1ZXMsIGFuZCBzaWduaWZpY2FudCBob3RzcG90cywgc2hvdyBoaWdoIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uIGluIG5vcnRoZWFzdGVybiBDaGljYWdvIG5lYXIgdGhlIENCRC4gQXJlYXMgd2l0aCBzaWduaWZpY2FudCBob3RzcG90cyAocC12YWx1ZXMg4omkIDAuMDUpIGFsaWduIHdpdGggcGF0dGVybnMgc2VlbiBpbiB0aGUga2VybmVsIGRlbnNpdHkgbWFwLCBpbmRpY2F0aW5nIGNsdXN0ZXJzIG9mIGhpZ2hlciByb2JiZXJ5IGNvdW50cyB0aGFuIGV4cGVjdGVkIGJ5IGNoYW5jZS4KCgoKCmBgYHtyIGNhbGN1bGF0ZSBsb2NhbCBtb3JhbiBJLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKZmluYWxfbmV0IDwtCiAgbGVmdF9qb2luKHJvYmJlcnkyMV9uZXQsIHN0X2Ryb3BfZ2VvbWV0cnkodmFyc19uZXQpLCBieT0idW5pcXVlSUQiKQpmaW5hbF9uZXQubmIgPC0gcG9seTJuYihhc19TcGF0aWFsKGZpbmFsX25ldCksIHF1ZWVuPVRSVUUpCmZpbmFsX25ldC53ZWlnaHRzIDwtIG5iMmxpc3R3KGZpbmFsX25ldC5uYiwgc3R5bGU9IlciLCB6ZXJvLnBvbGljeT1UUlVFKQoKbG9jYWxfbW9yYW5zIDwtIGxvY2FsbW9yYW4oZmluYWxfbmV0JGNvdW50Um9iYmVyeSwgZmluYWxfbmV0LndlaWdodHMsIHplcm8ucG9saWN5PVRSVUUpICU+JSAKICBhcy5kYXRhLmZyYW1lKCkKCmZpbmFsX25ldC5sb2NhbE1vcmFucyA8LSAKICBjYmluZChsb2NhbF9tb3JhbnMsIGFzLmRhdGEuZnJhbWUoZmluYWxfbmV0KSkgJT4lIAogIHN0X3NmKCkgJT4lCiAgZHBseXI6OnNlbGVjdChSb2JiZXJ5X0NvdW50ID0gY291bnRSb2JiZXJ5LCAKICAgICAgICAgICAgICAgIExvY2FsX01vcmFuc19JID0gSWksIAogICAgICAgICAgICAgICAgUF9WYWx1ZSA9IGBQcih6ICE9IEUoSWkpKWApICU+JQogIG11dGF0ZShTaWduaWZpY2FudF9Ib3RzcG90cyA9IGlmZWxzZShQX1ZhbHVlIDw9IDAuMDAxLCAxLCAwKSkgJT4lCiAgZ2F0aGVyKFZhcmlhYmxlLCBWYWx1ZSwgLWdlb21ldHJ5KQpgYGAKCgpgYGB7ciB2aXN1YWxpemUgbW9yYW4gSSwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTEsIGZpZy5hbGlnbiA9ICdjZW50ZXInfQoKbW9yYW4xIDwtIGdncGxvdCgpICsKICBnZW9tX3NmKGRhdGEgPSBmaW5hbF9uZXQubG9jYWxNb3JhbnMgJT4lIGZpbHRlcihWYXJpYWJsZSA9PSAiUm9iYmVyeV9Db3VudCIpLCBhZXMoZmlsbD1WYWx1ZSksIGNvbG91cj1OQSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAidHVyYm8iKSArIAogIGxhYnModGl0bGUgPSAiUm9iYmVyeSBDb3VudCIpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MgPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkKICAgICAgICApCgptb3JhbjIgPC0gZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YSA9IGZpbmFsX25ldC5sb2NhbE1vcmFucyAlPiUgZmlsdGVyKFZhcmlhYmxlID09ICJMb2NhbF9Nb3JhbnNfSSIpLCBhZXMoZmlsbD1WYWx1ZSksIGNvbG91cj1OQSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAidHVyYm8iKSArIAogIGxhYnModGl0bGUgPSAiTG9jYWwgTW9yYW5zIEkiKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzID1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpCiAgICAgICAgKQoKbW9yYW4zIDwtIGdncGxvdCgpICsKICBnZW9tX3NmKGRhdGEgPSBmaW5hbF9uZXQubG9jYWxNb3JhbnMgJT4lIGZpbHRlcihWYXJpYWJsZSA9PSAiUF9WYWx1ZSIpLCBhZXMoZmlsbD1WYWx1ZSksIGNvbG91cj1OQSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAidHVyYm8iKSArIAogIGxhYnModGl0bGUgPSAiUCBWYWx1ZSIpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MgPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksZmFjZSA9ICJpdGFsaWMiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTAuOCkKICAgICAgICApCgoKbW9yYW40IDwtIGdncGxvdCgpICsKICBnZW9tX3NmKGRhdGEgPSBmaW5hbF9uZXQubG9jYWxNb3JhbnMgJT4lIGZpbHRlcihWYXJpYWJsZSA9PSAiU2lnbmlmaWNhbnRfSG90c3BvdHMiKSwgYWVzKGZpbGw9VmFsdWUpLCBjb2xvdXI9TkEpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMob3B0aW9uID0gInR1cmJvIikgKyAKICBsYWJzKHRpdGxlID0gIlNpZ25pZ2ljYW50IEhvdHNwb3RzIikgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcyA9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIHNpemU9MC44KQogICAgICAgICkKCm1vcmFuMSArIG1vcmFuMiArIG1vcmFuMyArIG1vcmFuNApgYGAKCiMjIFNpbmdpZmljYW50IFJvYmJlcnkgQ2x1c3RlcnMKClRoZSBtYXAgaWxsdXN0cmF0ZXMgdGhlIGRpc3RhbmNlIHRvIHRoZSBuZWFyZXN0IHJvYmJlcnkgaG90c3BvdHMgYWNyb3NzIENoaWNhZ28sIHdpdGggZGFya2VyIGNvbG9ycyBpbmRpY2F0aW5nIHNob3J0ZXIgZGlzdGFuY2VzLiBUaGUgY2VudHJhbCBhbmQgc291dGhlcm4gcGFydHMgb2YgdGhlIGNpdHkgZXhoaWJpdCB0aGUgc2hvcnRlc3QgZGlzdGFuY2VzIHRvIHJvYmJlcnkgaG90c3BvdHMsIGFzIHNob3duIGJ5IHRoZSBkZWVwIGJsdWUgYW5kIGJsYWNrIGFyZWFzLiBUaGlzIHN1Z2dlc3RzIHRoYXQgdGhlc2UgcmVnaW9ucyBhcmUgbG9jYXRlZCBjbG9zZXIgdG8gYXJlYXMgd2l0aCBhIGhpZ2ggY29uY2VudHJhdGlvbiBvZiByb2JiZXJ5IGluY2lkZW50cy4gSW4gY29udHJhc3QsIHRoZSBvdXRlciBub3J0aGVybiBhbmQgc291dGhlcm4gcGFydHMgb2YgdGhlIGNpdHksIHJlcHJlc2VudGVkIGJ5IHllbGxvdywgZ3JlZW4sIGFuZCByZWQgaHVlcywgYXJlIGZhcnRoZXIgYXdheSBmcm9tIHRoZXNlIGhvdHNwb3RzLCBpbmRpY2F0aW5nIGxvd2VyIHByb3hpbWl0eSB0byBjb25jZW50cmF0ZWQgcm9iYmVyeSBhY3Rpdml0aWVzLiBUaGlzIHBhdHRlcm4gaGlnaGxpZ2h0cyB0aGUgc3BhdGlhbCBkaXN0cmlidXRpb24gb2Ygcm9iYmVyeSByaXNrcyBpbiBDaGljYWdvLCB3aXRoIG1vcmUgY29uY2VudHJhdGVkIHJvYmJlcnkgYWN0aXZpdHkgb2NjdXJyaW5nIGluIHNwZWNpZmljIHJlZ2lvbnMsIHBhcnRpY3VsYXJseSBpbiB0aGUgY2VudHJhbCBhbmQgc291dGhlcm4gYXJlYXMuIFRoZSBkaXN0YW5jZSBncmFkaWVudCBwcm92aWRlcyBpbnNpZ2h0IGludG8gaG93IHJvYmJlcnkgcmlza3MgZGVjcmVhc2UgYXMgb25lIG1vdmVzIGF3YXkgZnJvbSB0aGVzZSBjb3JlIGhvdHNwb3RzLgoKYGBge3IgY2FsY3VsYXRlIHNpZ25pZmljYW50IHJvYmJlcnkgcG9pbnRzLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQoKZmluYWxfbmV0IDwtCiAgZmluYWxfbmV0ICU+JSAKICBtdXRhdGUocm9iYmVyeS5pc1NpZyA9IGlmZWxzZShsb2NhbG1vcmFuKGZpbmFsX25ldCRjb3VudFJvYmJlcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbmFsX25ldC53ZWlnaHRzKVssNV0gPD0gMC4wMDAwMDAxLCAxLCAwKSkgJT4lCiAgbXV0YXRlKHJvYmJlcnkuaXNTaWcuZGlzdCA9IG5uX2Z1bmN0aW9uKHN0X2Nvb3JkaW5hdGVzKHN0X2NlbnRyb2lkKGZpbmFsX25ldCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdF9jb29yZGluYXRlcyhzdF9jZW50cm9pZCgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoZmluYWxfbmV0LCByb2JiZXJ5LmlzU2lnID09IDEpKSksIDEpKQpgYGAKCgpgYGB7ciB2aXN1YWxpemUgcm9iYmVyeSBOTiBEaXN0YW5jZSwgZmlnLmFsaWduID0gJ2NlbnRlcid9CgpnZ3Bsb3QoKSArCiAgICAgIGdlb21fc2YoZGF0YSA9IGZpbmFsX25ldCwgYWVzKGZpbGw9cm9iYmVyeS5pc1NpZy5kaXN0KSwgY29sb3VyPU5BKSArCiAgICAgIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAidHVyYm8iLCBuYW1lPSJOTiBEaXN0YW5jZSIpICsKICAgICAgbGFicyh0aXRsZT0iUm9iYmVyeSBOTiBEaXN0YW5jZSIpICsKICAgICAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzID1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpCiAgICAgICAgKQoKCmBgYAoKIyMgQWxsIFJpc2sgRmFjdG9ycwoKVGhlIGZpZ3VyZSBzaG93cyBzY2F0dGVyIHBsb3RzIG9mIHRoZSBjb3VudCBvZiBwcmVkaWN0b3IgdmFyaWFibGVzIGFuZCB0aGVpciBuZWFyZXN0IG5laWdoYm9yIGRpc3RhbmNlcyBpbiByZWxhdGlvbiB0byByb2JiZXJ5IGluY2lkZW50cy4gQWxsIGNvdW50LWJhc2VkIGZlYXR1cmVzLCBzdWNoIGFzIGFiYW5kb25lZCBidWlsZGluZ3MsIGNhcnMsIGFuZCBzYW5pdGF0aW9uIGNvbXBsYWludHMsIGFyZSBwb3NpdGl2ZWx5IGNvcnJlbGF0ZWQgd2l0aCB0aGUgbnVtYmVyIG9mIHJvYmJlcmllcywgaW5kaWNhdGluZyB0aGF0IGFyZWFzIHdpdGggaGlnaGVyIG9jY3VycmVuY2VzIG9mIHRoZXNlIHJpc2sgZmFjdG9ycyB0ZW5kIHRvIGV4cGVyaWVuY2UgbW9yZSByb2JiZXJpZXMuIFRoZSBzdHJvbmdlc3QgcG9zaXRpdmUgcmVsYXRpb25zaGlwcyBhcmUgb2JzZXJ2ZWQgYmV0d2VlbiByb2JiZXJ5IGluY2lkZW50cyBhbmQgc2FuaXRhdGlvbiBjb21wbGFpbnRzLCBTaG90U3BvdHRlciBkZXRlY3Rpb25zLCBhbmQgbGlxdW9yIHJldGFpbCBsb2NhdGlvbnMuCgpDb252ZXJzZWx5LCBhbGwgbmVhcmVzdCBuZWlnaGJvciBkaXN0YW5jZSBmZWF0dXJlcyBhcmUgbmVnYXRpdmVseSBjb3JyZWxhdGVkIHdpdGggcm9iYmVyeSBpbmNpZGVudHMsIG1lYW5pbmcgdGhhdCBhcyB0aGUgZGlzdGFuY2UgdG8gcmlzayBmYWN0b3JzIGluY3JlYXNlcywgdGhlIG51bWJlciBvZiByb2JiZXJpZXMgZGVjcmVhc2VzLiBUaGUgc3Ryb25nZXN0IG5lZ2F0aXZlIGNvcnJlbGF0aW9ucyBhcmUgc2VlbiB3aXRoIGRpc3RhbmNlIHRvIHNpZ25pZmljYW50IHJvYmJlcnkgaG90c3BvdHMsIGFiYW5kb25lZCBidWlsZGluZ3MsIGFuZCBsaXF1b3IgcmV0YWlsIHN0b3Jlcy4gVGhpcyBzdWdnZXN0cyB0aGF0IHByb3hpbWl0eSB0byB0aGVzZSByaXNrIGZhY3RvcnMgcGxheXMgYSBzaWduaWZpY2FudCByb2xlIGluIHJvYmJlcnkgcGF0dGVybnMuCgpXaGVuIGl0IGNvbWVzIHRvIG1vZGVsIGJ1aWxkaW5nLCB0aGlzIGluZm9ybWF0aW9uIGlzIGNyaXRpY2FsIGZvciBmZWF0dXJlIHNlbGVjdGlvbi4gVG8gYXZvaWQgbXVsdGljb2xsaW5lYXJpdHksIGVpdGhlciB0aGUgY291bnQgb3IgdGhlIG5lYXJlc3QgbmVpZ2hib3IgZGlzdGFuY2Ugc2hvdWxkIGJlIHNlbGVjdGVkIGZvciBlYWNoIHJpc2sgZmFjdG9yLiBJbiB0aGlzIGNhc2UsIHRoZSBtb2RlbCBpbmNvcnBvcmF0ZXMgbmVhcmVzdCBuZWlnaGJvciBkaXN0YW5jZXMsIGFzIHRoZXkgdGVuZCB0byBzaG93IHN0cm9uZ2VyIGFuZCBtb3JlIGNvbnNpc3RlbnQgY29ycmVsYXRpb25zIHdpdGggcm9iYmVyeSBpbmNpZGVudHMuCgoKYGBge3IgY29ycmVsYXRpb24sIGZpZy5oZWlnaHQ9MTUsIGZpZy53aWR0aD04LCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KCmZpbmFsX25ldF9sb25nIDwtIGZpbmFsX25ldCU+JSAKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lIAogIGRwbHlyOjpzZWxlY3QoLWModW5pcXVlSUQsIGN2SUQpKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAtY291bnRSb2JiZXJ5LCAjIGV2ZXJ5dGhpbmcgZXhjZXB0IG1lYXN1cmVtZW50CiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIlR5cGUiLCAjIGNhdGVnb3JpemVzIGFsbCBxdWFudGl0YXRpdmUgdmFyaWFibGVzIGludG8gVHlwZQogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiTnVtYmVyIikgCgpjb3JyZWxhdGlvbi5jb3IgPC0KICBmaW5hbF9uZXRfbG9uZyAlPiUKICAgIGdyb3VwX2J5KFR5cGUpICU+JQogICAgc3VtbWFyaXplKGNvcnJlbGF0aW9uID0gY29yKE51bWJlciwgY291bnRSb2JiZXJ5LCB1c2UgPSAiY29tcGxldGUub2JzIikpCgpmaW5hbF9uZXRfbG9uZyAlPiUKICBnZ3Bsb3QoYWVzKHg9IE51bWJlciwgeSA9IGNvdW50Um9iYmVyeSkpICsKICBnZW9tX3BvaW50KHNpemUgPSAwLjAxLCBjb2xvciA9ICIjMDAwMDA0IikgKyAgCiAgZ2VvbV90ZXh0KGRhdGEgPSBjb3JyZWxhdGlvbi5jb3IsIGFlcyhsYWJlbCA9IHBhc3RlKCJyID0iLCByb3VuZChjb3JyZWxhdGlvbiwgMikpKSwKICAgICAgICAgICAgeD0tSW5mLCB5PUluZiwgdmp1c3QgPSAxLjUsIGhqdXN0ID0gLS4xLCBzaXplPTMpICsKICBnZW9tX3Ntb290aChtZXRob2Q9J2xtJywgZm9ybXVsYT0geX54LCBsd2Q9MC41LCBzZSA9IEZBTFNFLCBjb2xvciA9ICIjQkIzNzU0IikgKwogIGZhY2V0X3dyYXAofiBUeXBlLCBzY2FsZXMgPSAiZnJlZSIsIG5jb2wgPSAyLCBsYWJlbGxlcj0gbGFiZWxsZXIoVHlwZSA9IGMoCiAgICBgQWJhbmRvbmVkX0J1aWxkaW5nc2AgPSAiQWJhbmRvbmVkIEJ1aWxkaW5ncyIsCiAgICBgQWJhbmRvbmVkX0J1aWxkaW5ncy5ubmAgPSAiRGlzdGFuY2UgdG8gQWJhbmRvbmVkIEJ1aWxkaW5ncyIsCiAgICBgQWJhbmRvbmVkX0NhcnNgID0gIkFiYW5kb25lZCBDYXJzIiwKICAgIGBBYmFuZG9uZWRfQ2Fycy5ubmAgPSAiRGlzdGFuY2UgdG8gQWJhbmRvbmVkIENhcnMiLAogICAgYEJhbmtgID0gIkJhbmsiLAogICAgYEJhbmsubm5gID0gIkRpc3RhbmNlIHRvIEJhbmsiLAogICAgYEdyYWZmaXRpYCA9ICJHcmFmZml0aSIsCiAgICBgR3JhZmZpdGkubm5gID0gIkRpc3RhbmNlIHRvIEdyYWZmaXRpIiwKICAgIGBMaXF1b3JfUmV0YWlsYCA9ICJMaXF1b3IgUmV0YWlsIiwKICAgIGBMaXF1b3JfUmV0YWlsLm5uYCA9ICJEaXN0YW5jZSB0byBMaXF1b3IgUmV0YWlsIiwKICAgIGByb2JiZXJ5LmlzU2lnYCA9ICJTaWduaWZpY2FudCBSb2JiZXJ5IiwKICAgIGByb2JiZXJ5LmlzU2lnLmRpc3RgID0gIkRpc3RhbmNlIHRvIFNpZ25pZmljYW50IFJvYmJlcnkiLAogICAgYFNhbml0YXRpb25gID0gIlNhbml0YXRpb24iLAogICAgYFNhbml0YXRpb24ubm5gID0gIkRpc3RhbmNlIHRvIFNhbml0YXRpb24iLAogICAgYFNob3RfU3BvdHRlcmAgPSAiU2hvdCBTcG90dGVyIiwgCiAgICBgU2hvdF9TcG90dGVyLm5uYCA9ICJEaXN0YW5jZSB0byBTaG90IFNwb3R0ZXIiLAogICAgYFN0cmVldF9MaWdodHNfT3V0YCA9ICJTdHJlZXQgTGlnaHQgT3V0IiwKICAgIGBTdHJlZXRfTGlnaHRzX091dC5ubmAgPSAiRGlzdGFuY2UgdG8gQnJva2VuIFN0cmVldCBMaWdodCIpKSkgICsKICBsYWJzKHRpdGxlID0gIlNjYXR0ZXIgUGxvdHMgb2YgUHJlZGljdG9yIFZhcmlhYmxlcyIsCiAgICAgICBjYXB0aW9uID0gIkRhdGE6IENoaWNhZ28gRGF0YSBQb3J0YWwiKSArCiAgdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9NiksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9NiksIAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpKQoKYGBgCgoKIyBQb2lzc29uIFJlZ3Jlc3Npb24KCiMjIFJhbmRvbSBLLUZvbGQgQ1YKClRoZSB0YWJsZSBiZWxvdyBwcmVzZW50cyBnb29kbmVzcyBvZiBmaXQgbWV0cmljcyBmb3IgZm91ciByZWdyZXNzaW9uIG1vZGVscy4gVGhlIGZpcnN0IHNldCBvZiBtb2RlbHMgaW5jbHVkZXMgb25seSByaXNrIGZhY3RvcnMgJ3JlZy52YXJzJywgd2hpbGUgdGhlIHNlY29uZCBzZXQgJ3JlZy5zcy52YXJzJyBpbmNvcnBvcmF0ZXMgYm90aCByaXNrIGZhY3RvcnMgYW5kIHRoZSBMb2NhbCBNb3JhbidzIEkgc3BhdGlhbCBwcm9jZXNzLiBUbyBldmFsdWF0ZSBtb2RlbCBwZXJmb3JtYW5jZSwgdHdvIHR5cGVzIG9mIGNyb3NzLXZhbGlkYXRpb24gd2VyZSBjb25kdWN0ZWQuIFRoZSBmaXJzdCBtZXRob2QgYXNzaWducyBhIHJhbmRvbWx5IGdlbmVyYXRlZCBjdklEIHRvIGVhY2ggZ3JpZCBjZWxsLCBhbGxvd2luZyBmb3IgcmFuZG9tIGstZm9sZCBjcm9zcy12YWxpZGF0aW9uIHRvIGFzc2VzcyBwcmVkaWN0aW9uIGFjY3VyYWN5LgoKYGBge3IgcmFuZG9tIGstZm9sZCByZWdyZXNzaW9uLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30KCnJlZy52YXJzIDwtIGMoIkFiYW5kb25lZF9CdWlsZGluZ3Mubm4iLCAiQWJhbmRvbmVkX0NhcnMubm4iLCAiR3JhZmZpdGkubm4iLCAKICAgICAgICAgICAgICAiTGlxdW9yX1JldGFpbC5ubiIsICJTdHJlZXRfTGlnaHRzX091dC5ubiIsICJTYW5pdGF0aW9uLm5uIiwgCiAgICAgICAgICAgICAgIlNob3RfU3BvdHRlci5ubiIsICJCYW5rLm5uIikKCnJlZy5zcy52YXJzIDwtIGMoIkFiYW5kb25lZF9CdWlsZGluZ3Mubm4iLCAiQWJhbmRvbmVkX0NhcnMubm4iLCAiR3JhZmZpdGkubm4iLCAKICAgICAgICAgICAgICAgICAiTGlxdW9yX1JldGFpbC5ubiIsICJTdHJlZXRfTGlnaHRzX091dC5ubiIsICJTYW5pdGF0aW9uLm5uIiwgCiAgICAgICAgICAgICAgICAgIlNob3RfU3BvdHRlci5ubiIsICJCYW5rLm5uIiwgInJvYmJlcnkuaXNTaWciLCAicm9iYmVyeS5pc1NpZy5kaXN0IikKCnJlZy5jdiA8LSBjcm9zc1ZhbGlkYXRlKAogIGRhdGFzZXQgPSBmaW5hbF9uZXQsCiAgaWQgPSAiY3ZJRCIsCiAgZGVwZW5kZW50VmFyaWFibGUgPSAiY291bnRSb2JiZXJ5IiwKICBpbmRWYXJpYWJsZXMgPSByZWcudmFycykgJT4lCiAgICBkcGx5cjo6c2VsZWN0KGN2SUQgPSBjdklELCBjb3VudFJvYmJlcnksIFByZWRpY3Rpb24sIGdlb21ldHJ5KQoKcmVnLnNzLmN2IDwtIGNyb3NzVmFsaWRhdGUoCiAgZGF0YXNldCA9IGZpbmFsX25ldCwKICBpZCA9ICJjdklEIiwKICBkZXBlbmRlbnRWYXJpYWJsZSA9ICJjb3VudFJvYmJlcnkiLAogIGluZFZhcmlhYmxlcyA9IHJlZy5zcy52YXJzKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoY3ZJRCA9IGN2SUQsIGNvdW50Um9iYmVyeSwgUHJlZGljdGlvbiwgZ2VvbWV0cnkpCgpgYGAKCiMjIFNwYXRpYWwgTE9HTyBDVgoKVGhlIHNlY29uZCBvbmUgaG9sZCBvdXQgb25lIG5laWdoYm9yaG9vZCwgdHJhaW4gdGhlIG1vZGVsIG9uIHRoZSByZW1haW5pbmcgbi0xIGFyZWFzLCBwcmVkaWN0IGZvciB0aGUgaG9sZCBvdXQsIGFuZCByZWNvcmQgdGhlIGdvb2RuZXNzIG9mIGZpdC4gVGhpcyBpcyBhbHNvIGNhbGxlZCB0aGUgc3BhdGlhbCAnTGVhdmUtb25lLWdyb3VwLW91dCcgY3Jvc3MtdmFsaWRhdGlvbiAoTE9HTy1DViksIGluIHdoaWNoIGVhY2ggbmVpZ2hib3Job29kIHRha2VzIGEgdHVybiBhcyBhIGhvbGQtb3V0LiBUaGUgcmVzdWx0IG9mIGVhY2ggYW5hbHlzaXMgaXMgYSBzZiBsYXllciB3aXRoIG9ic2VydmVkIGFuZCBwcmVkaWN0ZWQgYnVyZ2xhcnkgY291bnRzLgoKYGBge3Igc3BhdGlhbCBsb2dvIGN2IHJlZ3Jlc3Npb24sIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQoKbmVpZ2hib3Job29kcyA8LSAKICBzdF9yZWFkKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vYmxhY2ttYWQvbmVpZ2hib3Job29kcy9tYXN0ZXIvY2hpY2Fnby5nZW9qc29uIikgJT4lCiAgc3RfdHJhbnNmb3JtKHN0X2NycyhmaXNobmV0KSkgCgpmaW5hbF9uZXQgPC0KICBzdF9jZW50cm9pZChmaW5hbF9uZXQpICU+JQogICAgc3Rfam9pbihkcGx5cjo6c2VsZWN0KG5laWdoYm9yaG9vZHMsIG5hbWUpKSAlPiUKICAgICAgc3RfZHJvcF9nZW9tZXRyeSgpICU+JQogICAgICBsZWZ0X2pvaW4oZHBseXI6OnNlbGVjdChmaW5hbF9uZXQsIGdlb21ldHJ5LCB1bmlxdWVJRCkpICU+JQogICAgICBzdF9zZigpICU+JQogIG5hLm9taXQoKQoKcmVnLnNwYXRpYWxDViA8LSBjcm9zc1ZhbGlkYXRlKAogIGRhdGFzZXQgPSBmaW5hbF9uZXQsCiAgaWQgPSAibmFtZSIsCiAgZGVwZW5kZW50VmFyaWFibGUgPSAiY291bnRSb2JiZXJ5IiwKICBpbmRWYXJpYWJsZXMgPSByZWcudmFycykgJT4lCiAgICBkcGx5cjo6c2VsZWN0KGN2SUQgPSBuYW1lLCBjb3VudFJvYmJlcnksIFByZWRpY3Rpb24sIGdlb21ldHJ5KQoKcmVnLnNzLnNwYXRpYWxDViA8LSBjcm9zc1ZhbGlkYXRlKAogIGRhdGFzZXQgPSBmaW5hbF9uZXQsCiAgaWQgPSAibmFtZSIsCiAgZGVwZW5kZW50VmFyaWFibGUgPSAiY291bnRSb2JiZXJ5IiwKICBpbmRWYXJpYWJsZXMgPSByZWcuc3MudmFycykgJT4lCiAgICBkcGx5cjo6c2VsZWN0KGN2SUQgPSBuYW1lLCBjb3VudFJvYmJlcnksIFByZWRpY3Rpb24sIGdlb21ldHJ5KQpgYGAKCiMjIEVycm9yIEFuYWx5c2lzCgpUaGUgY29kZSBibG9jayBiZWxvdyBjcmVhdGVzIGEgbG9uZyBmb3JtIHJlZy5zdW1tYXJ5LCB0aGF0IGJpbmRzIHRvZ2V0aGVyIG9ic2VydmVkL3ByZWRpY3RlZCBjb3VudHMgYW5kIGVycm9ycyBmb3IgZWFjaCBncmlkIGNlbGwgYW5kIGZvciBlYWNoIHJlZ3Jlc3Npb24uIFJlc2lkdWFscyBhcmUgY2FsY3VsYXRlZCBmb3IgZWFjaCByZWdyZXNzaW9uIGJ5IHN1YnRyYWN0aW5nIHRoZSBvcmlnaW5hbCByb2JiZXJ5IGluY2lkZW50cyBmcm9tIHByZWRpY3RlZCBpbmNpZGVudHMuIAoKYGBge3Igc3VtbWFyaXplIHJlZ3Jlc3Npb259CgpyZWcuc3VtbWFyeSA8LSAKICByYmluZCgKICAgIG11dGF0ZShyZWcuY3YsICAgICAgICAgICBFcnJvciA9IFByZWRpY3Rpb24gLSBjb3VudFJvYmJlcnksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUmVncmVzc2lvbiA9ICJSYW5kb20gay1mb2xkIENWOiBKdXN0IFJpc2sgRmFjdG9ycyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgbXV0YXRlKHJlZy5zcy5jdiwgICAgICAgIEVycm9yID0gUHJlZGljdGlvbiAtIGNvdW50Um9iYmVyeSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZWdyZXNzaW9uID0gIlJhbmRvbSBrLWZvbGQgQ1Y6IFNwYXRpYWwgUHJvY2VzcyIpLAogICAgCiAgICBtdXRhdGUocmVnLnNwYXRpYWxDViwgICAgRXJyb3IgPSBQcmVkaWN0aW9uIC0gY291bnRSb2JiZXJ5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJlZ3Jlc3Npb24gPSAiU3BhdGlhbCBMT0dPLUNWOiBKdXN0IFJpc2sgRmFjdG9ycyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgbXV0YXRlKHJlZy5zcy5zcGF0aWFsQ1YsIEVycm9yID0gUHJlZGljdGlvbiAtIGNvdW50Um9iYmVyeSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZWdyZXNzaW9uID0gIlNwYXRpYWwgTE9HTy1DVjogU3BhdGlhbCBQcm9jZXNzIikpICU+JQogICAgc3Rfc2YoKSAKYGBgCgpDb21wYXJpbmcgdGhlIGVycm9yIGFjcm9zcyBkaWZmZXJlbnQgcmVncmVzc2lvbnMsIHdlIHNlZSB0aGF0IGFkZGluZyBzcGF0aWFsIHByb2Nlc3MgZmVhdHVyZXMgaW1wcm92ZSB0aGUgbW9kZWwuIEluIGFkZGl0aW9uLCB0aGUgbW9kZWwgYXBwZWFycyBzbGlnaHRseSBsZXNzIHJvYnVzdCBmb3IgdGhlIHNwYXRpYWwgY3Jvc3MtdmFsaWRhdGlvbiwgcHJvYmFibHkgYmVjYXVzZSBMT0dPLUNWIGlzIHN1Y2ggYSBjb25zZXJ2YXRpdmUgYXNzdW1wdGlvbi4gCgpgYGB7ciBzdW1tYXJpemUgZXJyb3JzIGJ5IGZvbGQgYW5kIHJlZ3Jlc3Npb24sIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgplcnJvcl9ieV9yZWdfYW5kX2ZvbGQgPC0gCiAgcmVnLnN1bW1hcnkgJT4lCiAgICBncm91cF9ieShSZWdyZXNzaW9uLCBjdklEKSAlPiUgCiAgICBzdW1tYXJpemUoTWVhbl9FcnJvciA9IG1lYW4oUHJlZGljdGlvbiAtIGNvdW50Um9iYmVyeSwgbmEucm0gPSBUKSwKICAgICAgICAgICAgICBNQUUgPSBtZWFuKGFicyhNZWFuX0Vycm9yKSwgbmEucm0gPSBUKSwKICAgICAgICAgICAgICBTRF9NQUUgPSBtZWFuKGFicyhNZWFuX0Vycm9yKSwgbmEucm0gPSBUKSkgJT4lIHVuZ3JvdXAoKQoKc3RfZHJvcF9nZW9tZXRyeShlcnJvcl9ieV9yZWdfYW5kX2ZvbGQpICU+JQogIGdyb3VwX2J5KFJlZ3Jlc3Npb24pICU+JSAKICAgIHN1bW1hcml6ZShNZWFuX01BRSA9IHJvdW5kKG1lYW4oTUFFKSwgMiksCiAgICAgICAgICAgICAgU0RfTUFFID0gcm91bmQoc2QoTUFFKSwgMikpICU+JQogIGthYmxlKGNvbC5uYW1lPWMoIlJlZ3Jlc3Npb24iLCAnTWVhbiBBYnNvbHV0ZSBFcnJvcicsJ1N0YW5kYXJkIERldmlhdGlvbiBNQUUnKSkgJT4lCiAgICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiKSkgJT4lCiAgICByb3dfc3BlYygyLCBjb2xvciA9ICJibGFjayIsIGJhY2tncm91bmQgPSAiI0ZDRkZBNCIpICU+JQogICAgcm93X3NwZWMoNCwgY29sb3IgPSAiYmxhY2siLCBiYWNrZ3JvdW5kID0gIiNGQ0ZGQTQiKSAKCgpgYGAKClRoZSBzbWFsbCBtdWx0aXBsZSBtYXBzIGJlbG93IHZpc3VhbGl6ZXMgdGhlIGRpc3RyaWJ1dGlvbiBvZiBlcnJvcnMgYnkgcmVncmVzc2lvbi4gRm9yIHRoZSByYW5kb20gay1mb2xkIGN2IG1ldGhvZCwgdGhlIGVycm9ycyBhcmUgcHJldHR5IGV2ZW5seSBkaXN0cmlidXRlZCBhY3Jvc3Mgc3BhY2UuIEhvd2V2ZXIsIGZvciBzcGF0aWFsIGxvZ28tY3YgbWV0aG9kLCBoaWdoZXIgZXJyb3JzIG9jY3VyIGF0IHNwYXRpYWwgaG90c3BvdHMuIAoKYGBge3IgdmlzdWFsaXplIGVycm9yLGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTExLCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KCmVycm9yX2J5X3JlZ19hbmRfZm9sZCAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fc2YoYWVzKGZpbGw9TUFFKSwgY29sb3I9InRyYW5zcGFyZW50IikgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAidHVyYm8iLCBkaXJlY3Rpb24gPSAtMSkgKyAKICBmYWNldF93cmFwKH5SZWdyZXNzaW9uLCBuY29sID0gNCkgKwogICBsYWJzKHRpdGxlID0gIk1BRSBieSBGb2xkIGFuZCBSZWdyZXNzaW9uIiwKICAgICAgIGNhcHRpb24gPSAiRGF0YTogQ2hpY2FnbyBEYXRhIFBvcnRhbCIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcy55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0wLjgpLAogICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcpKSAKCmBgYAoKIyBQcmVkaWN0aW9uIEFjY3VyYWN5IAoKIyMgR2VuZXJhbGl6YWJpbGl0eSBUZXN0CgpMZXTigJlzIG5vdyB0ZXN0IGZvciBnZW5lcmFsaXphYmlsaXR5IGFjcm9zcyByYWNpYWwtbmVpZ2hib3Job29kIGNvbnRleHQuIFRvIHRlc3QgdGhpcyBwcm9wb3NpdGlvbiwgdGlkeWNlbnN1cyBpcyB1c2VkIHRvIHB1bGwgcmFjZSBkYXRhIGJ5IGNlbnN1cyB0cmFjdC4gUGVyY2VudGFnZSBtaW5vcml0eSBwb3B1bGF0aW9uIGZvciBlYWNoIGNlbnN1cyB0cmFjdCBpcyBjYWxjdWxhdGVkIGJ5IGNvbXBhcmluZyB0aGUgdG90YWwgbnVtYmVyIG9mIExhdGlueCwgQWZyaWNhbiBBbWVyaWNhbiwgYW5kIEFzaWFuIHBvcHVsYXRpb24gdG8gdGhlIHRvdGFsIHBvcHVsYXRpb24uIENlbnN1cyB0cmFjdHMgYXJlIHNwbGl0IGludG8gdHdvIGdyb3VwczogbWFqb3JpdHkgd2hpdGUgYW5kIG1pbm9yaXR5LCBkZXBlbmRpbmcgb24gd2hldGhlciB0aGUgcHJvcG9ydGlvbiBvZiBtaW5vcml0eSBwb3B1bGF0aW9uIGluIHRoYXQgY2Vuc3VzIHRyYWN0IGV4Y2VlZHMgNjAlLiAKCgpgYGB7ciBkb3dubG9hZCByYWNlIGRhdGEsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQoKY2Vuc3VzX2FwaV9rZXkoZGxnSW5wdXQoCiAgIkVudGVyIGEgQ2Vuc3VzIEFQSSBLZXkiLCAjIGFzayBmb3IgYW4gYXBpIGtleQogIFN5cy5nZXRlbnYoIkNFTlNVU19BUElfS0VZIikKKSRyZXMsCm92ZXJ3cml0ZSA9IFRSVUUpCnRyYWN0czIxIDwtIAogIGdldF9hY3MoZ2VvZ3JhcGh5ID0gInRyYWN0IiwgCiAgICAgICAgICB2YXJpYWJsZXMgPSBjKCJCMDIwMDFfMDAxRSIsICMgdG90YWwgcG9wdWxhdGlvbgogICAgICAgICAgICAiQjAyMDAxXzAwMkUiLCAjIHdoaXRlIHBvcHVsYXRpb24KICAgICAgICAgICAgIkIwMjAwMV8wMDNFIiwgIyBibGFjayBwb3B1bGF0aW9uCiAgICAgICAgICAgICJCMDIwMDFfMDA1RSIsICMgYXNpYW4gcG9wdWxhdGlvbgogICAgICAgICAgICAiQjAzMDAyXzAxMkUiKSwgCiAgICAgICAgICB5ZWFyPTIwMjEsIHN0YXRlPTE3LCBjb3VudHk9MDMxLCAKICAgICAgICAgIGdlb21ldHJ5PVRSVUUsIG91dHB1dD0id2lkZSIpICU+JQogIHN0X3RyYW5zZm9ybSgnRVNSSToxMDIyNzEnKSAlPiUgCiAgcmVuYW1lKFRvdGFsUG9wID0gQjAyMDAxXzAwMUUsIAogICAgICAgICBXaGl0ZXMgPSBCMDIwMDFfMDAyRSwKICAgICAgICAgQWZyaWNhbl9BbWVyaWNhbnMgPSBCMDIwMDFfMDAzRSwKICAgICAgICAgQXNpYW5zID0gQjAyMDAxXzAwNUUsCiAgICAgICAgIExhdGlueCA9IEIwMzAwMl8wMTJFKSAlPiUgCiAgbXV0YXRlKHBjdE1pbm9yaXR5ID0gaWZlbHNlKFRvdGFsUG9wID4gMCwgKEFmcmljYW5fQW1lcmljYW5zICsgQXNpYW5zICsgTGF0aW54ICkgLyBUb3RhbFBvcCwgMCksIAogICAgICAgICBtYWpvcml0eSA9IGlmZWxzZShwY3RNaW5vcml0eSA+IDAuNSwgIm1pbm9yaXR5IiwgIm1ham9yaXR5IikpICU+JSAgIC5bbmVpZ2hib3Job29kcyxdCgoKYGBgCgpXZSBtYXkgc2VlIHRoYXQgQ2hpY2FnbyByZW1haW5zIGEgdmVyeSBzZWdyZWdhdGVkIGNpdHkgYW5kIHRoZXJlJ3MgY2xlYXIgcmFjaWFsIGJvdW5kYXJ5IGJldHdlZW4gcGxhY2VzIHdoZXJlIG1vcmUgdGhhbiA2MCUgb2YgdGhlIHBvcHVsYXRpb24gYXJlIG1pbm9yaXR5IGFuZCBwbGFjZXMgdGhhdCdzIG1ham9yaXR5IHdoaXRlLiBJbiBwYXJ0aWN1bGFyLCBub3J0aGVybiBhbmQgbm9ydGhlYXN0ZXJuIChDQkQpIHBhcnQgb2YgQ2hpY2FnbyBhcmUgbWFqb3JpdHkgd2hpdGUgd2hpbGUgdGhlIHJlbWFpbmluZyBwYXJ0cyBhcmUgYWxsIG1hZGUgdXAgb2YgcmFjaWFsIG1pbm9yaXRpZXMuIAoKYGBge3IgdmlzdWFsaXplIHJhY2UgY29udGV4dCwgZmlnLmFsaWduID0gJ2NlbnRlcid9CgpnZ3Bsb3QoKSArIGdlb21fc2YoZGF0YSA9IG5hLm9taXQodHJhY3RzMjEpLCBhZXMoZmlsbCA9IG1ham9yaXR5KSwgY29sb3IgPSBOQSkgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI0JCMzc1NCIsICIjNTYxMDZFIiksIG5hbWU9IlJhY2UgQ29udGV4dCIpICsKICAgIGxhYnModGl0bGUgPSAiUmFjZSBDb250ZXh0ICIsCiAgICAgICAgIGNhcHRpb24gPSAiRGF0YTogQW1lcmljYW4gQ29tbXVuaXR5IFN1cnZleSAyMDIxIikgKwogIHRoZW1lKAogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIHNpemU9MC40KQogICAgICAgICkgCmBgYAoKRXJyb3IgaXMgY2FsY3VsYXRlZCBieSBzdWJ0cmFjdGluZyB0aGUgb2JzZXJ2ZWQgcm9iYmVyeSBjb3VudCBmcm9tIHRoZSBwcmVkaWN0aW9uLiBBIHBvc2l0aXZlIGRpZmZlcmVuY2UgcmVwcmVzZW50cyBhbiBvdmVyLXByZWRpY3Rpb24uIFRoZSBsZWFzdCBpZGVhbCByZXN1bHQgaXMgYSBtb2RlbCB0aGF0IG92ZXItcHJlZGljdHMgcmlzayBpbiBtaW5vcml0eSBhcmVhcywgYW5kIHVuZGVyLXByZWRpY3RzIGluIHdoaXRlIGFyZWFzLiBJZiByZXBvcnRpbmcgc2VsZWN0aW9uIGJpYXMgaXMgYW4gaXNzdWUsIHN1Y2ggYSBtb2RlbCBtYXkgdW5mYWlybHkgYWxsb2NhdGUgcG9saWNlIHJlc291cmNlIGRpc3Byb3BvcnRpb25hdGVseSBpbiBibGFjayBhbmQgYnJvd24gY29tbXVuaXRpZXMuIEluIG91ciBjYXNlLCBvdmVyLXByZWRpY3Rpb24gb2YgbWlub3JpdGllcyBpcyBhbiBpc3N1ZSBmb3IgcmFuZG9tIGstZm9sZCBjdiByZWdyZXNzaW9uLCBidXQgbm90IGZvciBsb2dvLWN2IHJlZ3Jlc3Npb25zLiBUaGUgbGF0dGVyIHR3byBtb2RlbHMgdW5kZXItcHJlZGljdCBtaW5vcml0eSBuZWlnaGJvcmhvb2RzIGFuZCBvdmVyLXByZWRpY3QgbWFqb3JpdHkgd2hpdGUgbmVpZ2hib3Job29kLCBtYWtpbmcgaXQgbG9va3MgbGlrZSB0aGF0IHRoaXMgYWxnb3JpdGhtcyBnZW5lcmFsaXplcyB3ZWxsIHdpdGggcmVzcGVjdCB0byByYWNlLiAKCmBgYHtyIGdlbmVyYWxpemFiaWxpdHkgYnkgcmFjZSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCmpvaW5yYWNlIDwtIHN0X2NlbnRyb2lkKHJlZy5zdW1tYXJ5KSAlPiUgCiAgc3RfaW50ZXJzZWN0aW9uKHRyYWN0czIxICU+JWRwbHlyOjogc2VsZWN0KHBjdE1pbm9yaXR5KSkgJT4lIAogIHN0X2Ryb3BfZ2VvbWV0cnkoKSAlPiUgCiAgZ3JvdXBfYnkoY3ZJRCkgJT4lIAogIHN1bW1hcml6ZShtZWFuTWlub3IgPSBtZWFuKHBjdE1pbm9yaXR5KSkKCgpyZWcuc3VtbWFyeSA8LSByZWcuc3VtbWFyeSAlPiUgCiAgbGVmdF9qb2luKGpvaW5yYWNlLCBieSA9ICJjdklEIikgCgoKcmVnLnN1bW1hcnkgJT4lIAogIG11dGF0ZShyYWNlQ29udGV4dCA9IGlmZWxzZShtZWFuTWlub3IgPiAuNiwgIk1pbm9yaXR5IiwgIk1ham9yaXR5IFdoaXRlIikpICU+JSAKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lIAogIGdyb3VwX2J5KFJlZ3Jlc3Npb24sIHJhY2VDb250ZXh0KSAlPiUKICBzdW1tYXJpemUobWVhbi5FcnJvciA9IG1lYW4oRXJyb3IsIG5hLnJtID0gVCkpICU+JQogIHNwcmVhZChyYWNlQ29udGV4dCwgbWVhbi5FcnJvcikgJT4lCiAga2FibGUoKSAlPiUKICAgIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpKSAlPiUKICAgIHJvd19zcGVjKDIsIGNvbG9yID0gImJsYWNrIiwgYmFja2dyb3VuZCA9ICIjRkNGRkE0IikgJT4lCiAgICByb3dfc3BlYyg0LCBjb2xvciA9ICJibGFjayIsIGJhY2tncm91bmQgPSAiI0ZDRkZBNCIpIAoKYGBgCgojIyBLZXJuZWwgRGVzbnRpeSBFc3RpbWF0aW9uCgpUaGUgdXRpbGl0eSBvZiB0aGlzIGFsZ29yaXRobSBpcyBqdWRnZWQgcmVsYXRpdmUgdG8gYW4gYWx0ZXJuYXRpdmUgcG9saWNlIGFsbG9jYXRpb24gbWV0aG9kOiBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0aW9uLiBLZXJuZWwgZGVuc2l0eSB3b3JrcyBieSBjZW50ZXJpbmcgYSBzbW9vdGgga2VybmVsLCBvciBjdXJ2ZSwgYXRvcCBlYWNoIGNyaW1lIHBvaW50IHN1Y2ggdGhhdCB0aGUgY3VydmUgaXMgYXQgaXRzIGhpZ2hlc3QgZGlyZWN0bHkgb3ZlciB0aGUgcG9pbnQgYW5kIHRoZSBsb3dlc3QgYXQgdGhlIHJhbmdlIG9mIGEgY2lyY3VsYXIgc2VhcmNoIHJhZGl1cy4gVGhlIGNvZGUgYmxvY2sgYmVsb3cgY3JlYXRlcyBhIEtlcm5lbCBkZW5zaXR5IG1hcCBvZiByb2JiZXJ5IHdpdGggYSAxMDAwIGZvb3Qgc2VhcmNoIHJhZGl1cy4gCiAKYGBge3IgY2FsY3VsYXRlIDIwMjEgS2VybmFsIERlbnNpdHksIGZpZy53aWR0aD02LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KCnJvYl9wcHAgPC0gYXMucHBwKHN0X2Nvb3JkaW5hdGVzKHJvYmJlcnkyMSksIFcgPSBzdF9iYm94KGZpbmFsX25ldCkpCnJvYl9LRC4xMDAwIDwtIHNwYXRzdGF0LmV4cGxvcmU6OmRlbnNpdHkucHBwKHJvYl9wcHAsIDEwMDApCnJvYl9LRC5kZiA8LSBkYXRhLmZyYW1lKHJhc3RlclRvUG9pbnRzKG1hc2socmFzdGVyKHJvYl9LRC4xMDAwKSwgYXMobmVpZ2hib3Job29kcywgJ1NwYXRpYWwnKSkpKQoKCmdncGxvdChkYXRhPXJvYl9LRC5kZiwgYWVzKHg9eCwgeT15KSkgKwogIGdlb21fcmFzdGVyKGFlcyhmaWxsPWxheWVyKSkgKyAKICBjb29yZF9zZihjcnM9c3RfY3JzKGZpbmFsX25ldCkpICsgCiAgc2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbiA9ICJ0dXJibyIsIG5hbWU9IkRlbnNpdHkiKSArCiAgbGFicyh0aXRsZSA9ICJLZXJuZWwgRGVuc2l0eSBvZiBSb2JiZXJ5IDEwMDBmdCBSYWRpaSIpICsKICAgIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcyA9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIHNpemU9MC44KQogICAgICAgICkKYGBgCgpOZXh0LCBhIG5ldyBnb29kbmVzcyBvZiBmaXQgaW5kaWNhdG9yIGlzIGNyZWF0ZWQgdG8gaWxsdXN0cmF0ZSB3aGV0aGVyIHRoZSAyMDIxIGtlcm5lbCBkZW5zaXR5IG9yIHJpc2sgcHJlZGljdGlvbnMgY2FwdHVyZSBtb3JlIG9mIHRoZSAyMDIyIHJvYmJlcmllcy4KCmBgYHtyIGRvd25sb2FkIDIwMjIgcm9iZXJ5IGRhdGEsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9Cgpyb2JiZXJ5MjIgPC0gCiAgcmVhZC5zb2NyYXRhKCJodHRwczovL2RhdGEuY2l0eW9mY2hpY2Fnby5vcmcvUHVibGljLVNhZmV0eS9DcmltZXMtMjAyMi85aHdyLTJ6eHAiKSAlPiUgCiAgIGZpbHRlcihQcmltYXJ5LlR5cGUgPT0gIlJPQkJFUlkiKSAlPiUgCiAgbXV0YXRlKHggPSBnc3ViKCJbKCldIiwgIiIsIExvY2F0aW9uKSkgJT4lCiAgc2VwYXJhdGUoeCxpbnRvPSBjKCJZIiwiWCIpLCBzZXA9IiwiKSAlPiUKICBtdXRhdGUoWCA9IGFzLm51bWVyaWMoWCksCiAgICAgICAgIFkgPSBhcy5udW1lcmljKFkpKSAlPiUgCiAgbmEub21pdCAlPiUKICBzdF9hc19zZihjb29yZHMgPSBjKCJYIiwgIlkiKSwgY3JzID0gNDMyNiwgYWdyID0gImNvbnN0YW50IikgJT4lCiAgc3RfdHJhbnNmb3JtKCdFU1JJOjEwMjI3MScpICU+JSAKICBkaXN0aW5jdCgpICU+JQogIC5bZmlzaG5ldCxdCgpgYGAKClRoZSBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0aW9uIHdhcyBjbGFzc2lmaWVkIGludG8gNSByaXNrIGNhdGVnb3JpZXMgYW5kIHRoZSBjb3VudCBmb3IgdGhlIDIwMjIgcm9iYmVyaWVzIHdlcmUgc3VtbWFyaXplZCBpbnRvIHRoZSBzYW1lIHNmIGxheWVyLiAgVGhlIHB1cnBvc2UgaGVyZSBpcyB0byBzZWUgaWYgdGhlIHJpc2sgcHJlZGljdGlvbnMgY2FwdHVyZSBtb3JlIG9ic2VydmVkIGJ1cmdsYXJpZXMgdGhhbiB0aGUga2VybmVsIGRlbnNpdHkuIElmIHNvLCB0aGVuIHRoZSByaXNrIHByZWRpY3Rpb24gbW9kZWwgcHJvdmlkZXMgYSBtb3JlIHJvYnVzdCB0YXJnZXRpbmcgdG9vbCBmb3IgYWxsb2NhdGluZyBwb2xpY2UgcmVzb3VyY2VzLiAKCmBgYHtyIGNhdGVnb3JpemUgS0RFfQoKcm9iX0tERV9zdW0gPC0gYXMuZGF0YS5mcmFtZShyb2JfS0QuMTAwMCkgJT4lCiAgc3RfYXNfc2YoY29vcmRzID0gYygieCIsICJ5IiksIGNycyA9IHN0X2NycyhmaW5hbF9uZXQpKSAlPiUKICBhZ2dyZWdhdGUoLiwgZmluYWxfbmV0LCBtZWFuKSAKCmtkZV9icmVha3MgPC0gY2xhc3NJbnRlcnZhbHMocm9iX0tERV9zdW0kdmFsdWUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4gPSA1LCAiZmlzaGVyIikKCnJvYl9LREVfc2YgPC0gcm9iX0tERV9zdW0gJT4lCiAgbXV0YXRlKGxhYmVsID0gIktlcm5lbCBEZW5zaXR5IiwKICAgICAgICAgUmlza19DYXRlZ29yeSA9IGNsYXNzSW50OjpmaW5kQ29scyhrZGVfYnJlYWtzKSwKICAgICAgICAgUmlza19DYXRlZ29yeSA9IGNhc2Vfd2hlbigKICAgICAgICAgICBSaXNrX0NhdGVnb3J5ID09IDUgfiAiNXRoIiwKICAgICAgICAgICBSaXNrX0NhdGVnb3J5ID09IDQgfiAiNHRoIiwKICAgICAgICAgICBSaXNrX0NhdGVnb3J5ID09IDMgfiAiM3JkIiwKICAgICAgICAgICBSaXNrX0NhdGVnb3J5ID09IDIgfiAiMm5kIiwKICAgICAgICAgICBSaXNrX0NhdGVnb3J5ID09IDEgfiAiMXN0IikpICU+JQogIGNiaW5kKAogICAgYWdncmVnYXRlKAogICAgICBkcGx5cjo6c2VsZWN0KHJvYmJlcnkyMSkgJT4lIG11dGF0ZShyb2JiZXJ5Q291bnQgPSAxKSwgLiwgc3VtKSAlPiUKICAgIG11dGF0ZShyb2JiZXJ5Q291bnQgPSByZXBsYWNlX25hKHJvYmJlcnlDb3VudCwgMCkpKSAlPiUKICBkcGx5cjo6c2VsZWN0KGxhYmVsLCBSaXNrX0NhdGVnb3J5LCByb2JiZXJ5Q291bnQpCgpgYGAKClRoZSBzYW1lIHByb2Nlc3Mgd2FzIHJlcGVhdGVkIGZvciB0aGUgcmlzayBwcmVkaWN0aW9uLiBOb3RlIHRoYXQgYm90aCBwcmVkaWN0aW9ucyBmcm9tIHRoZSBMT0dPLUNWIHdpdGggYW5kIHdpdGhvdXQgdGhlIHNwYXRpYWwgZmVhdHVyZXMgaXMgYmVpbmcgdXNlZCBoZXJlLgoKYGBge3IgY2F0ZWdvcml6ZSByYW5kb20gayBmb2xkfQoKbWxfYnJlYWtzIDwtIGNsYXNzSW50ZXJ2YWxzKHJlZy5zcy5zcGF0aWFsQ1YkUHJlZGljdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbiA9IDUsICJmaXNoZXIiKQpyb2Jfcmlza19zZiA8LQogIHJlZy5zcy5zcGF0aWFsQ1YgJT4lCiAgbXV0YXRlKGxhYmVsID0gIlJpc2sgUHJlZGljdGlvbnMgU3BhdGlhbCIsCiAgICAgICAgIFJpc2tfQ2F0ZWdvcnkgPWNsYXNzSW50OjpmaW5kQ29scyhtbF9icmVha3MpLAogICAgICAgICBSaXNrX0NhdGVnb3J5ID0gY2FzZV93aGVuKAogICAgICAgICAgIFJpc2tfQ2F0ZWdvcnkgPT0gNSB+ICI1dGgiLAogICAgICAgICAgIFJpc2tfQ2F0ZWdvcnkgPT0gNCB+ICI0dGgiLAogICAgICAgICAgIFJpc2tfQ2F0ZWdvcnkgPT0gMyB+ICIzcmQiLAogICAgICAgICAgIFJpc2tfQ2F0ZWdvcnkgPT0gMiB+ICIybmQiLAogICAgICAgICAgIFJpc2tfQ2F0ZWdvcnkgPT0gMSB+ICIxc3QiKSkgJT4lCiAgY2JpbmQoCiAgICBhZ2dyZWdhdGUoCiAgICAgIGRwbHlyOjpzZWxlY3Qocm9iYmVyeTIxKSAlPiUgbXV0YXRlKHJvYmJlcnlDb3VudCA9IDEpLCAuLCBzdW0pICU+JQogICAgICBtdXRhdGUocm9iYmVyeUNvdW50ID0gcmVwbGFjZV9uYShyb2JiZXJ5Q291bnQsIDApKSkgJT4lCiAgZHBseXI6OnNlbGVjdChsYWJlbCxSaXNrX0NhdGVnb3J5LCByb2JiZXJ5Q291bnQpCgptbF9icmVha3Nfc2ltcGxlIDwtIGNsYXNzSW50ZXJ2YWxzKHJlZy5zcy5jdiRQcmVkaWN0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuID0gNSwgImZpc2hlciIpCnJvYl9yaXNrX3NmX3NpbXBsZSA8LQogIHJlZy5zcy5jdiAlPiUKICBtdXRhdGUobGFiZWwgPSAiUmlzayBQcmVkaWN0aW9ucyBTaW1wbGUiLAogICAgICAgICBSaXNrX0NhdGVnb3J5ID1jbGFzc0ludDo6ZmluZENvbHMobWxfYnJlYWtzX3NpbXBsZSksCiAgICAgICAgIFJpc2tfQ2F0ZWdvcnkgPSBjYXNlX3doZW4oCiAgICAgICAgICAgUmlza19DYXRlZ29yeSA9PSA1IH4gIjV0aCIsCiAgICAgICAgICAgUmlza19DYXRlZ29yeSA9PSA0IH4gIjR0aCIsCiAgICAgICAgICAgUmlza19DYXRlZ29yeSA9PSAzIH4gIjNyZCIsCiAgICAgICAgICAgUmlza19DYXRlZ29yeSA9PSAyIH4gIjJuZCIsCiAgICAgICAgICAgUmlza19DYXRlZ29yeSA9PSAxIH4gIjFzdCIpKSAlPiUKICBjYmluZCgKICAgIGFnZ3JlZ2F0ZSgKICAgICAgZHBseXI6OnNlbGVjdChyb2JiZXJ5MjEpICU+JSBtdXRhdGUocm9iYmVyeUNvdW50ID0gMSksIC4sIHN1bSkgJT4lCiAgICAgIG11dGF0ZShyb2JiZXJ5Q291bnQgPSByZXBsYWNlX25hKHJvYmJlcnlDb3VudCwgMCkpKSAlPiUKICBkcGx5cjo6c2VsZWN0KGxhYmVsLFJpc2tfQ2F0ZWdvcnksIHJvYmJlcnlDb3VudCkKCmBgYAoKIyMgUHJlZGljdGlvbiBDb21wYXJpc29uCgpCZWxvdyBhIG1hcCBpcyBnZW5lcmF0ZWQgb2YgdGhlIHJpc2sgY2F0ZWdvcmllcyBmb3IgYWxsIHRocmVlIG1vZGVsIHR5cGVzLiBBIHN0cm9uZ2x5IGZpdCBtb2RlbCBzaG91bGQgc2hvdyB0aGF0IHRoZSBoaWdoZXN0IHJpc2sgY2F0ZWdvcnkgaXMgdW5pcXVlbHkgdGFyZ2V0ZWQgdG8gcGxhY2VzIHdpdGggYSBoaWdoIGRlbnNpdHkgb2YgYnVyZ2xhcnkgcG9pbnRzLiBUaGUgbWFwIHNob3dzIHRoYXQgb3VyIHJpc2sgcHJlZGljdGlvbiBtb2RlbHMgcGVyZm9ybSBhcyBnb29kIGFzIHRoZSBrZXJuYWwgZGVuc2l0eSBhcHByb2FjaCBpbiBjYXB0dXJpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiByb2JiZXJ5IGluY2lkZW50cy4gCgpgYGB7ciBwcmVkaWN0aW9uIGNvbXBhcmlzb25zIG1hcCwgZmlnLmFsaWduID0gJ2NlbnRlcid9CnJiaW5kKHJvYl9LREVfc2YsIHJvYl9yaXNrX3NmLCByb2Jfcmlza19zZl9zaW1wbGUpICU+JQogIG5hLm9taXQoKSAlPiUKICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtbGFiZWwsIC1SaXNrX0NhdGVnb3J5LCAtZ2VvbWV0cnkpICU+JQogIGdncGxvdCgpICsKICAgIGdlb21fc2YoYWVzKGZpbGwgPSBSaXNrX0NhdGVnb3J5KSwgY29sb3VyID0gTkEpICsKICAgIGZhY2V0X3dyYXAofmxhYmVsLCApICsKICAgIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAidHVyYm8iLCBkaXNjcmV0ZSA9IFRSVUUsIG5hbWUgPSAiUmlzayBDYXRlZ29yeSIpICsKICAgICAgZ2VvbV9zZihkYXRhID0gc2FtcGxlX24ocm9iYmVyeTIyLCAzMDAwKSwgc2l6ZSA9IC4wMywgY29sb3VyID0gIndoaXRlIikgKwogICAgbGFicyh0aXRsZT0iQ29tcGFyaXNvbiBvZiBLZXJuZWwgRGVuc2l0eSBhbmQgUmlzayBQcmVkaWN0aW9ucyIsCiAgICAgICAgIHN1YnRpdGxlPSIyMDIxIHJvYmJlcnkgcmlzayBwcmVkaWN0aW9uczsgMjAyMiBhcm1lZCByb2JiZXJpZXMiKSArIAogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcyA9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSxmYWNlID0gIml0YWxpYyIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIHNpemU9MC44KQogICAgICAgICkKYGBgCgpBbiBlZmZlY3RpdmUgd2F5IHRvIGFzc2VzcyBwcmVkaWN0aW9uIGFjY3VyYWN5IGlzIGJ5IHVzaW5nIGEgaGlzdG9ncmFtLiBJZGVhbGx5LCBhIHdlbGwtZml0dGluZyBtb2RlbCBzaG91bGQgc2hvdyB0aGF0IHRoZSBoaWdoZXN0LXJpc2sgYXJlYXMgY2FwdHVyZSBhIGxhcmdlciBwcm9wb3J0aW9uIG9mIDIwMjIgcm9iYmVyaWVzIGNvbXBhcmVkIHRvIHRoZSBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZS4gSG93ZXZlciwgaW4gdGhpcyBjYXNlLCB0aGUga2VybmVsIGRlbnNpdHkgbW9kZWwgb3V0cGVyZm9ybXMgYm90aCByaXNrIHByZWRpY3Rpb24gbW9kZWxzIGluIHRoZSBoaWdoZXN0LXJpc2sgcmVnaW9ucy4gRGVzcGl0ZSB0aGlzLCB0aGUgcmlzayBwcmVkaWN0aW9uIG1vZGVscyBleGNlbCBpbiBpZGVudGlmeWluZyBhcm1lZCByb2JiZXJ5IGluY2lkZW50cyBpbiBsb3dlci1yaXNrIGFyZWFzIHRoYXQgdGhlIGtlcm5lbCBkZW5zaXR5IG1vZGVsIG92ZXJsb29rZWQuCgoKCgoKYGBge3IgcHJlZGljdGlvbiBjb21wYXJpc29ucyBmaWd1cmUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy5hbGlnbiA9ICdjZW50ZXInfQpyYmluZChyb2JfS0RFX3NmLCByb2Jfcmlza19zZiwgcm9iX3Jpc2tfc2Zfc2ltcGxlKSAlPiUKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lCiAgbmEub21pdCgpICU+JQogIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1sYWJlbCwgLVJpc2tfQ2F0ZWdvcnkpICU+JQogIGdyb3VwX2J5KGxhYmVsLCBSaXNrX0NhdGVnb3J5KSAlPiUKICBzdW1tYXJpemUoY291bnRSb2JiZXJ5ID0gc3VtKFZhbHVlKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGdyb3VwX2J5KGxhYmVsKSAlPiUKICBtdXRhdGUoUGNudF9vZl90ZXN0X3NldF9jcmltZXMgPSBjb3VudFJvYmJlcnkgLyBzdW0oY291bnRSb2JiZXJ5KSkgJT4lCiAgICBnZ3Bsb3QoYWVzKFJpc2tfQ2F0ZWdvcnksUGNudF9vZl90ZXN0X3NldF9jcmltZXMpKSArCiAgICAgIGdlb21fYmFyKGFlcyhmaWxsPWxhYmVsKSwgcG9zaXRpb249ImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArCiAgICAgIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAidHVyYm8iLCBkaXNjcmV0ZSA9IFRSVUUsIG5hbWUgPSAiTW9kZWwiLCBkaXJlY3Rpb24gPSAtMSkgKwogICAgICBsYWJzKHRpdGxlID0gIlJpc2sgUHJlZGljdGlvbiB2cy4gS2VybmVsIERlbnNpdHkiLAogICAgICAgICAgIHkgPSAiJSBvZiBUZXN0IFNldCBSb2JiZXJpZXMgKHBlciBtb2RlbCkiLAogICAgICAgICAgIHggPSAiUmlzayBDYXRlZ29yeSIpICsKICB0aGVtZV9idygpICsKICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgdmp1c3QgPSAwLjUpKQpgYGAKCiMgQ29uY2x1c2lvbnMKCkluIGNvbmNsdXNpb24sIG91ciBtb2RlbCByZXF1aXJlcyBzaWduaWZpY2FudCBpbXByb3ZlbWVudCBiZWZvcmUgaXQgY2FuIGJlIGltcGxlbWVudGVkIGVmZmVjdGl2ZWx5LiBDb21wYXJlZCB0byB0cmFkaXRpb25hbCBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0aW9uLCBpdCBzdHJ1Z2dsZWQgdG8gY2FwdHVyZSBhcm1lZCByb2JiZXJ5IGluY2lkZW50cyBpbiB0aGUgaGlnaGVzdC1yaXNrIGFyZWFzLiBPbmUga2V5IGlzc3VlIGlzIHRoYXQgbWFueSBvZiBvdXIgcHJlZGljdG9yIHZhcmlhYmxlcywgc3VjaCBhcyByZXBvcnRzIG9mIGFiYW5kb25lZCBjYXJzLCBncmFmZml0aSwgYW5kIHNhbml0YXRpb24gY29tcGxhaW50cywgd2VyZSBvdXRkYXRlZCwgbGFzdCB1cGRhdGVkIGluIDIwMTkuIFVzaW5nIHRoZXNlIHRvIHByZWRpY3Qgcm9iYmVyaWVzIGluIDIwMjEgYW5kIDIwMjIgbGlrZWx5IGxlZCB0byBpbmFjY3VyYXRlIHJlc3VsdHMsIGVzcGVjaWFsbHkgc2luY2Ugc2hpZnRzIGluIHRoZXNlIHJpc2sgZmFjdG9ycyAobGlrZSBncmFmZml0aSByZW1vdmFsKSB3b3VsZCBhbHRlciBjcmltZSBwYXR0ZXJucy4gVXAtdG8tZGF0ZSBkYXRhc2V0cyBhcmUgY3J1Y2lhbCBmb3IgbW9yZSBhY2N1cmF0ZSBwcmVkaWN0aW9ucy4KCkFkZGl0aW9uYWxseSwgdGhlIENPVklELTE5IHBhbmRlbWljIGxpa2VseSBpbXBhY3RlZCBhcm1lZCByb2JiZXJ5IHBhdHRlcm5zLCB3aXRoIGVjb25vbWljIGRpc3RyZXNzLCBsb2NrZG93bnMsIGFuZCByZWFsbG9jYXRpb24gb2YgcG9saWNlIHJlc291cmNlcyBhbGwgY29udHJpYnV0aW5nIHRvIHNoaWZ0cyBpbiBjcmltZSBsb2NhdGlvbnMuIE91ciBtb2RlbCBuZWVkcyB0byBhY2NvdW50IGZvciBzdWNoIGZhY3RvcnMgdG8gZW5zdXJlIHByZWRpY3Rpb25zIHJlbWFpbiByZWxldmFudCBvdmVyIHRpbWUuCgpPbiBhIHBvc2l0aXZlIG5vdGUsIHRoZSBtb2RlbCBwZXJmb3JtZWQgYmV0dGVyIGluIGxvd2VyLXJpc2sgYXJlYXMsIHdoZXJlIHRoZSBrZXJuZWwgZGVuc2l0eSBtZXRob2QgZmVsbCBzaG9ydC4gSG93ZXZlciwgcmVseWluZyBvbiBhIHNpbmdsZSBtb2RlbCBtYXkgbm90IGNhcHR1cmUgdGhlIGZ1bGwgY29tcGxleGl0eSBvZiBhcm1lZCByb2JiZXJ5IHBhdHRlcm5zLiBBIGNvbWJpbmVkIGFwcHJvYWNoIHVzaW5nIGJvdGgga2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbiBhbmQgcmlzayBwcmVkaWN0aW9uIGNvdWxkIGltcHJvdmUgYWNjdXJhY3kuIFdoaWxlIHdlIGF2b2lkZWQgb3ZlcnByZWRpY3RpbmcgaW4gbWFqb3JpdHkgbm9uLXdoaXRlIG5laWdoYm9yaG9vZHMsIHBvdGVudGlhbCBzZWxlY3Rpb24gYmlhcyByZW1haW5zIGEgY29uY2Vybi4gSW5jb3Jwb3JhdGluZyBzcGF0aWFsIGZlYXR1cmVzIGFuZCB1c2luZyBrLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiBoZWxwZWQgaW1wcm92ZSB0aGUgbW9kZWwsIGJ1dCBmdXJ0aGVyIHJlZmluZW1lbnQgaXMgbmVlZGVkIHRvIGVuc3VyZSBpdCBnZW5lcmFsaXplcyB3ZWxsIGFjcm9zcyBkaWZmZXJlbnQgY29udGV4dHMgYW5kIHRpbWUgcGVyaW9kcy4=