This markdown is an updated version of a module written by Ken
Steif in 2019. It was an unpublished chapter from his book Public Policy
Analytics, and it is therefore very detailed. It has been updated to
keep it aligned with current packages. For the most part, the original
text is still used here, out of respect for the original material and
the author. However, alterations, annotations and supplemental code are
throughout.
For this project, we built a growth model based on a binary
logistic regression that predicts the probability of land cover change
at a raster cell level over a specified interval as a function of a)
Existing land cover, b) The location of infrastructure and c)
Demographic and economic spatial variables.
While we have used logistic regression to model spatial phenomena
like flooding before, this workflow is different in that we wrangle
raster data in R rather than doing it in ArcGIS.
The appendices to this document add some additional code and
alternative code blocks to what is in the chapter itself. You can use
these to augment the workflow to run entirely in R and load, downsample
and wrangle rasters.
Thanks very much to Jenna Epstein for helping to update and edit
this material in 2022. A note from Professor Michael Fichman.
1. Introduction
Regional urban development is an emergent outcome resulting from the
autonomous decisions of many different agents, including developers,
real estate buyers and tenants as well as planners and regulators. Each
of these groups optimize for a different set of bottom lines. Developers
seek profit, buying land at a low price, improving on that land, and
selling it at a premium. Households and firms consume housing and
commercial real estate balancing price constraints with access to
amenities and customers, respectively.
Planners regulate development by trading-off economic growth with the
mitigation of negative externalities toward the goal of economic and
environmental sustainability. This is a case study in managing these
trade-offs. The focus of this chapter is on Cook, DuPage, and Will
Counties in Illinois in the Chicago region. Chicago is located in Cook
County, IL. As a sprawling Metropolitan area, climate change and equity
concerns are forcing cities like Chicago to reconsider the role of land
use planning.
Land use planning that is both economically productive and
sustainable requires both supply and demand-side insights. Specifically,
the Planner must understand future demand for development and how that
demand contrasts with the supply of environmentally sensitive land. The
goal of this chapter is to model this interplay.
The next section wrangles a host of datasets including, Land Cover
and Land Cover Change from the USGS, Census demographics, and
transportation. Spatial lag features are also engineered from the land
cover change data hypothesizing that the time/space scale of development
between 2011 and 2021 can help predict new development in 2031.
Exploratory analysis is undertaken to investigate the relationship
between development and the aforementioned features. Findings then
motivate the estimation of a geospatial predictive model trained on new
development between 2011 and 2021. Those dynamics are then harnessed to
predict Development_Demand for 2031 - predictions interpreted as the
probability of new development here. The goal is not to create the most
accurate model, but to demonstrate the role for geospatial machine
learning in the land use modeling process.
Once predictions are validated for their accuracy, we evaluate their
confluence with environmentally sensitive land in counties throughout
the region, with particular emphasis on landscape fragmentation. The
final section uses predictions to ‘allocate’ new development across to
places where growth can have an economic impact without impeding
sustainability goals. It also tests a scenario for attracting new
development with an extension of regional rail (new transportation
infrastructure).
1.2. Setup
Below we load the libraries needed for the analysis as well as a
mapTheme and plotTheme. A set of palette
colors are also specified.
library(tidyverse)
library(sf)
library(terra)
library(raster)
library(knitr)
library(kableExtra)
library(tidycensus)
library(tigris)
library(FNN)
#library(QuantPsyc) # JE Note: in R 4.1, QuantPsyc package not available.
library(caret)
library(yardstick)
library(pscl)
library(plotROC)
library(ggrepel)
library(pROC)
library(grid)
library(gridExtra)
library(viridis)
library(igraph)
library(dplyr)
library(scales)
library(ggplot2)
plotTheme <- theme(
plot.title =element_text(size=12),
plot.subtitle = element_text(size=8),
plot.caption = element_text(size = 6),
axis.text.x = element_text(size = 10, angle = 45, hjust = 1),
axis.text.y = element_text(size = 10),
axis.title.y = element_text(size = 10),
# Set the entire chart region to blank
panel.background=element_blank(),
plot.background=element_blank(),
#panel.border=element_rect(colour="#F0F0F0"),
# Format the grid
panel.grid.major=element_line(colour="#D0D0D0",size=.75),
axis.ticks=element_blank())
mapTheme <- theme(plot.title =element_text(size=12),
plot.subtitle = element_text(size=8),
plot.caption = element_text(size = 6),
axis.line=element_blank(),
axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks=element_blank(),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
panel.background=element_blank(),
panel.border=element_blank(),
panel.grid.major=element_line(colour = 'transparent'),
panel.grid.minor=element_blank(),
legend.direction = "vertical",
legend.position = "right",
plot.margin = margin(1, 1, 1, 1, 'cm'),
legend.key.height = unit(1, "cm"), legend.key.width = unit(0.2, "cm"))
palette2 <- c("#41b6c4","#253494")
palette4 <- c("#a1dab4","#41b6c4","#2c7fb8","#253494")
palette5 <- c("#ffffcc","#a1dab4","#41b6c4","#2c7fb8","#253494")
palette6 <- c("#253494","#2c7fb8","#41b6c4","#a1dab4","#ffffcc")
palette10 <- c("#f7fcf0","#e0f3db","#ccebc5","#a8ddb5","#7bccc4",
"#4eb3d3","#2b8cbe","#0868ac","#084081","#f7fcf0")
We also include several helper functions.
quintilesBreaks takes a dataframe and a column and outputs
the quintiles breaks, helping shorten the below ggplot
calls.
It takes longer to ggplot a polygon fishnet with
geom_sf than it does to plot geom_point. To
cut down on plotting time, the xyC (for ‘XY Coordinates’)
takes a fishnet sf and converts it to a dataframe of grid
cell centroid coordinates.
rast is a function allowing us to quickly plot raster
values in ggplot.
#this function converts a column in to quintiles. It is used for mapping.
quintileBreaks <- function(df,variable) {
as.character(quantile(df[[variable]],
c(.01,.2,.4,.6,.8),na.rm=T))
}
#This function can be used to convert a polygon sf to centroids xy coords.
xyC <- function(aPolygonSF) {
as.data.frame(
cbind(x=st_coordinates(st_centroid(aPolygonSF))[,1],
y=st_coordinates(st_centroid(aPolygonSF))[,2]))
}
#this function convert a raster to a data frame so it can be plotted in ggplot
rast <- function(inRaster) {
data.frame(
xyFromCell(inRaster, 1:ncell(inRaster)),
value = getValues(inRaster)) }
2. Data Wrangling & Feature Engineering
In this section a considerable amount of vector and raster data is
wrangled together into a regression-ready dataset. The following
datasets are used:
2.2 - 2.3: Land cover data downloaded
from the Multi-Resolution Land Characteristics Consortium’s National
Land Cover Database (NLCD) includes annual land cover and land cover
change raster data for the entire country. These data are sampled to a
4,000 by 4,000 ft^2 fishnet, which will be used for this analysis. 2.4:
Population data is downloaded from the U.S. Census and joined to the
fishnet by distributing Census Tract population totals proportionally to
each grid cell. 2.5: Highway vectors are downloaded from the U.S. Census
TIGER Line shapefiles for 2019 and used to wrangle highway proximity
features. Regional rail vectors are downloaded from the City of Chicago.
It includes all Metra commuter rail lines in the Chicagoland region. For
the Chicago downtown area, we created a polygon for the Chicago Loop and
imported the shapefile. 2.6: The land cover change data is used to
engineer spatial lag features. 2.7: County polygons are downloaded using
the tigris package. 2.8: Each feature is wrangled into a
final dataset.
It is important to remember that land cover is not ‘land use’.
Typically, the former refers to phenomena on the Earth’s surface
including both the built environment and natural resources, while the
latter typically refers only to variation in the built environment.
Other raster features are created such as distance to highways, for
instance. These rasters are then integrated with a vector fishnet.
Additional feature engineering is performed on the vector-side providing
a simple, but comprehensive snapshot of the development process in and
around Chicago and Cook, DuPage, and Will Counties between 2011 and
2021.
2.2. Land Cover Change Data
The dependent variable we wish to forecast is land cover change
between 2011 and 2021. In this section, the land cover raster data is
loaded, reclassified and integrated with a vector fishnet. As before,
the fishnet will allow us to parametrize spatial relationships in a
regression context.
The table below shows descriptions of each categorical land cover
type in the land cover data. Below, we will reclassify these data into
more useful categories.
JE Note: Land cover categories can be found here
- will replace with markdown table at a later point.
Several raster layers have been provided for this analysis:
We read in ThreeCountyArea - this is the extent of
the study area
ThreeCountyLC_2011 is a raster of land cover in 2011
for the three counties: Cook, DuPage, and Will Counties.
ThreeCountyLC_2021 is a raster of land cover in 2021 for
the same geography. We have to calculate land cover change - where there
were conversions between one land cover and another on the time frame
2011-2021. We plot the raster using ggplot and the
rast function specified above.
Note that these rasters are projected as
NAD 1983 NAD83 Illinois East, crs = 3435. The original land
cover raster is at a 30 meter by 30 meter resolution. The rasters
provided are ultimately resampled up to 4000 feet by 4000 feet. The
Cook, DuPage, and Will County areas are 1,635 sq. miles; 336 sq. miles;
and 849 sq. miles, respectively. Together, the total area for all three
counties is about 2820 sq. miles, so this metro area is quite large. A
vector fishnet dataset of this size would be too computationally
intensive. By resampling we gain some computational efficiency but lose
some accuracy. Nevertheless, this approach works well for educational
purposes.
# Load required libraries
library(raster)
# Read the shapefile
IL_counties <- st_read("C:/Users/3lpaw/Desktop/ArcGIS Pro 3.2/EnvModeling/04_24_24_UrbanGrowthModeling/Downloaded_Data/IL_Counties/IL_BNDY_County_Py.shp")
# Change the coordinate system to NAD83 Illinois East
IL_counties <- st_transform(IL_counties, crs = 3435)
# Subset the data - Select only the 3 counties that we need: Cook, Du Page, and Will
IL_counties_subset <- IL_counties[IL_counties$COUNTY_NAM %in% c("WILL", "COOK", "DUPAGE"), c("COUNTY_NAM", "CO_FIPS", "geometry")]
# Write the subsetted data back to a shapefile
# st_write(IL_counties_subset, "C:/Users/3lpaw/Desktop/ArcGIS Pro 3.2/EnvModeling/04_24_24_UrbanGrowthModeling/Downloaded_Data/IL_Counties/ThreeCountyArea.shp")
ThreeCountyArea <- st_read("C:/Users/3lpaw/Desktop/ArcGIS Pro 3.2/EnvModeling/04_24_24_UrbanGrowthModeling/Downloaded_Data/IL_Counties/ThreeCountyArea.shp")
# Specify the coordinate system for the 3 IL counties to NAD83 Illinois East
ThreeCountyArea <- st_transform(ThreeCountyArea, crs = 3435)
# Set the desired raster resolution
#resolution <- 100 # Choose a suitable resolution (in meters or degrees, depending on your data)
# Define the extent of the raster
#extent <- extent(ThreeCountyArea)
# Create an empty raster layer with the specified resolution and extent
#empty_raster1 <- raster(extent, res = resolution)
# Rasterize the shapefile onto the empty raster layer
#rasterized <- rasterize(ThreeCountyArea, empty_raster1, field="CO_FIPS")
# Save the raster to a file
# writeRaster(rasterized, "C:/Users/3lpaw/Desktop/ArcGIS Pro 3.2/EnvModeling/04_24_24_UrbanGrowthModeling/Downloaded_Data/IL_Counties/ThreeCountyArea.tif", format = "GTiff")
ThreeCountyArea_Boundary <- raster("C:/Users/3lpaw/Desktop/ArcGIS Pro 3.2/EnvModeling/04_24_24_UrbanGrowthModeling/Downloaded_Data/IL_Counties/ThreeCountyArea.tif")
# Read the two land cover rasters, one for 2011 and one for 2021
ThreeCountyLC_2011 <- raster("C:/Users/3lpaw/Desktop/ArcGIS Pro 3.2/EnvModeling/04_24_24_UrbanGrowthModeling/Downloaded_Data/LC_2011_2021/3CountyLC_2011.tif")
ThreeCountyLC_2021 <- raster("C:/Users/3lpaw/Desktop/ArcGIS Pro 3.2/EnvModeling/04_24_24_UrbanGrowthModeling/Downloaded_Data/LC_2011_2021/3CountyLC_2021.tif")
# Reproject the rasters to the same coordinate system as the other data layers: NAD83 Illinois East, crs = 3435
ThreeCountyLC_2011 <- projectRaster(ThreeCountyLC_2011, crs = 3435)
ThreeCountyLC_2021 <- projectRaster(ThreeCountyLC_2021, crs = 3435)
We now calculate land cover change from 2011 to 2021. Reclassify 2011
and 2021 land cover databases to consist of 1 and 0 observations (e.g. 1
is the developed classes 13-24, 0 is everything else).
reclassMatrix <-
matrix(c(
-5,12,0,
12,24,1,
24,Inf,0),
ncol=3, byrow=T)
Developed_2011 <-
reclassify(ThreeCountyLC_2011,reclassMatrix)
# you can see the frequency of the values
freq(Developed_2011)
## value count
## [1,] 0 4184672
## [2,] 1 3576202
## [3,] NA 156298
Developed_2021 <-
reclassify(ThreeCountyLC_2021,reclassMatrix)
freq(Developed_2021)
## value count
## [1,] 0 4152752
## [2,] 1 3608122
## [3,] NA 156298
Then do some map algebra to find the places where land cover changed
(we calculate Development_change). Let’s see a quick histogram of the
values - these should range from 0 (undeveloped in 2011, undeveloped in
2021), 1 (undeveloped in 2011, developed in 2021 (presuming nothing went
from developed to undeveloped)), and 2 (developed in both periods). The
1’s represent the change. You can see the number of the values (0’s,
1’s, and 2’s) in the frequency table.
Development_change <- Developed_2011+Developed_2021
freq(Development_change)
## value count
## [1,] 0 4141691
## [2,] 1 54042
## [3,] 2 3565141
## [4,] NA 156298
hist(Development_change, xlim = c(0,2))

We now plot the 3 County Area and the land cover change from
2011-2021. Again, the 1s represent where land cover changed from not
developed to developed between 2011 and 2021. The 2s represent land
cover that was developed both in 2011 and 2021, so there was no change.
There is not a lot of development change because it is only one decade
and Chicago is already pretty developed, but there are some red areas
mostly in Will County to the southwest.
ggplot() +
geom_raster(data=rast(Development_change) %>% na.omit %>% filter(value > 0),
aes(x,y,fill=as.factor(value))) +
# scale_fill_viridis(direction = -1, discrete=TRUE, option = "A", name ="Land Cover\nChange") +
scale_fill_manual(values = c("2" = "gray", "1" = "red"), name = "Land Cover\nChange") +
labs(title = "Land Cover Change for the Three County Area, 2011-2021") +
mapTheme +
theme(legend.direction="horizontal") +
geom_sf(data=ThreeCountyArea, fill = "transparent", colour = "black")

Next, we reclassify the raster such that all the developed grid cell
values receive a value of 1 and all other values receive a value of 0.
This is done using a reclassify matrix. The matrix reads row by row. Row
1 says any grid cell ranging from 0 to 12 takes a value of 0; 13 or
greater through 24, a value of 1; and all other values take 0.
reclassMatrix2 <-
matrix(c(
0,0,
1,1,
2,0),
ncol=2, byrow=T)
reclassMatrix2
## [,1] [,2]
## [1,] 0 0
## [2,] 1 1
## [3,] 2 0
Now reclassify and convert all 0’s to NA.
We apply a name to the raster with names. This is done to
make it faster to join raster to the fishnet below. You can see the
frequency table of values with freq(Development_change2).
There are 52042 areas that changed from underdeveloped to developed in
the data.
Development_change2 <-
reclassify(Development_change,reclassMatrix2)
Development_change2[Development_change2 < 1] <- NA
names(Development_change2) <- "Dev_change"
freq(Development_change2)
## value count
## [1,] 1 54042
## [2,] NA 7863130
ggplot() +
geom_sf(data=ThreeCountyArea, fill = "transparent") +
geom_raster(data=rast(Development_change2) %>% na.omit,
aes(x,y,fill=as.factor(value))) +
scale_fill_viridis(discrete=TRUE, name ="Land Cover\nChange") +
labs(title="Development Land Use Change, 2011-2021") +
mapTheme

Next, the fishnet is created at 4000 by 4000 foot resolution and
subset it to the Three County Area with
st_intersection.
ThreeCounty_fishnet <-
st_make_grid(ThreeCountyArea, 4000) %>%
st_sf()
ThreeCounty_fishnet <-
ThreeCounty_fishnet[ThreeCountyArea,]
The vector fishnet is then plotted. Note that this plot takes a bit
of time to render because there are thousands of polygons.
ggplot() +
geom_sf(data=ThreeCounty_fishnet) +
labs(title="Fishnet, 4000 Foot Resolution") +
mapTheme

Then the raster is converted to points, which makes its joining to
the vector fishnet a bit faster. Now to extract the raster values into
the fishnet. There is a function in the raster package
called RasterToPolygon but it is quite slow.
Below, a slightly faster approach is develop that converts the raster
to an sf point layer and then joins the points to the
fishnet with aggregate. This works well because the raster
and the fishnet are of the same spatial resolution. Finally, the fishnet
variable Dev_change is created that is 1 where
new development has occurred and 0 where it has not. This
is our dependent variable and encoded as a factor.
To speed up the mapping process, fishnet polygons are converted to
centroid points using the xyC function.
changePoints <-
rasterToPoints(Development_change2) %>%
as.data.frame() %>%
st_as_sf(coords = c("x", "y"), crs = st_crs(ThreeCounty_fishnet))
fishnet <-
aggregate(changePoints, ThreeCounty_fishnet, sum) %>%
mutate(Dev_change = ifelse(is.na(Dev_change),0,1),
Dev_change = as.factor(Dev_change))
ggplot() +
geom_sf(data=ThreeCounty_fishnet) +
geom_point(data=fishnet,
aes(x=xyC(fishnet)$x, y=xyC(fishnet)$y, colour=Dev_change)) +
scale_colour_manual(values = palette2,
labels=c("No Change","New Development"),
name = "") +
labs(title = "Land Cover Development Change", subtitle = "As fishnet centroids") +
mapTheme

2.3. Land Cover in 2011
It is reasonable to hypothesize that the propensity of new
development is a function of existing land cover categories. In this
section we identify these other land cover categories from 2011 and
integrate each with the fishnet.
# Our shapefile with the boundary for the three counties is ThreeCountyArea
# Our land cover raster for 2011 for the three counties is ThreeCountyLC_2011
# ggplot() +
# geom_sf(data=ThreeCountyArea) +
# geom_raster(data=rast(ThreeCountyLC_2011) %>% na.omit %>% filter(value > 0),
# aes(x,y,fill=as.factor(value))) +
# scale_fill_viridis(discrete=TRUE, name ="") +
# labs(title = "Land Cover, 2011") +
# mapTheme +
# theme(legend.direction="horizontal")
The table below shows the approach taken to recoded existing land
cover codes into the categories used in our analysis. In the code block
below new rasters are generated and names are applied.
Naming ensures that when the raster is integrated with the fishnet, the
column reflects the appropriate raster.
| Open Space as well as Low, Medium and High Intensity
Development |
Developed |
| Deciduous, Evergreen, and Mixed Forest |
Forest |
| Pasture/Hay and Cultivated Crops |
Farm |
| Woody and Emergent Herbaceous Wetlands |
Woodlands |
| Barren Land, Dwarf Scrub, and Grassland/Herbaceous |
Other Undeveloped |
| Water |
Water |
developed <- ThreeCountyLC_2011 == 21 | ThreeCountyLC_2011 == 22 | ThreeCountyLC_2011 == 23 | ThreeCountyLC_2011 == 24
forest <- ThreeCountyLC_2011 == 41 | ThreeCountyLC_2011 == 42 | ThreeCountyLC_2011 == 43
farm <- ThreeCountyLC_2011 == 81 | ThreeCountyLC_2011 == 82
wetlands <- ThreeCountyLC_2011 == 90 | ThreeCountyLC_2011 == 95
otherUndeveloped <- ThreeCountyLC_2011 == 52 | ThreeCountyLC_2011 == 71 | ThreeCountyLC_2011 == 31
water <- ThreeCountyLC_2011 == 11
names(developed) <- "developed"
names(forest) <- "forest"
names(farm) <- "farm"
names(wetlands) <- "wetlands"
names(otherUndeveloped) <- "otherUndeveloped"
names(water) <- "water"
Next, each raster is aggregated to the fishnet by way of a function
called aggregateRaster. Here, the process used above to To
do this, a function is created below that loops through a list of
rasters, converts the ith raster to points, filters only points
that have value of 1 (ie. is the ith land cover
type), and then aggregates to the fishnet.
Here is the function.
aggregateRaster <- function(inputRasterList, theFishnet) {
#create an empty fishnet with the same dimensions as the input fishnet
theseFishnets <- theFishnet %>% dplyr::select()
#for each raster in the raster list
for (i in inputRasterList) {
#create a variable name corresponding to the ith raster
varName <- names(i)
#convert raster to points as an sf
thesePoints <-
rasterToPoints(i) %>%
as.data.frame() %>%
st_as_sf(coords = c("x", "y"), crs = st_crs(theFishnet)) %>%
filter(.[[1]] == 1)
#aggregate to the fishnet
thisFishnet <-
aggregate(thesePoints, theFishnet, length) %>%
mutate(!!varName := ifelse(is.na(.[[1]]),0,1))
#add to the larger fishnet
theseFishnets <- cbind(theseFishnets,thisFishnet)
}
#output all aggregates as one large fishnet
return(theseFishnets)
}
The theRasterList of land cover types in 2011 is created
and then fed into aggregateRaster. The result is converted
to long form grid cell centroids and plot as small multiple maps.
Note the inclusion of st_cast here which convert all
geometries to POLYGON. If you create a frequency table of
geometry types in aggregatedRasters, you will notice some
and handful of MULTIPOLYGONS. Try
table(st_geometry_type(aggregatedRasters)). These rogue
multipolygons break the xyC function which is designed to
find grid cell centroids. After all, there is no one centroid of several
combined polygons. Thus st_cast ensures all geometries are
just POLYGON. Look out for this function throughout the
remainder of this chapter.
theRasterList <- c(developed,forest,farm,wetlands,otherUndeveloped,water)
aggregatedRasters <-
aggregateRaster(theRasterList, ThreeCounty_fishnet) %>%
dplyr::select(developed,forest,farm,wetlands,otherUndeveloped,water) %>%
mutate_if(is.numeric,as.factor)
aggregatedRasters %>%
gather(var,value,developed:water) %>%
st_cast("POLYGON") %>% #just to make sure no weird geometries slipped in
mutate(X = xyC(.)$x,
Y = xyC(.)$y) %>%
ggplot() +
geom_sf(data=ThreeCountyArea) +
geom_point(aes(X,Y, colour=as.factor(value))) +
facet_wrap(~var) +
scale_colour_manual(values = palette2,
labels=c("Other","Land Cover"),
name = "") +
labs(title = "Land Cover Types, 2011",
subtitle = "As fishnet centroids") +
mapTheme

2.4. Census Data
Population and population change is obviously an critical demand-side
component of predicting Development_Demand. Census data for
both 2011 and 2021 can be downloaded quickly using the
tidycensus package. As illustrated below, these data are
downloaded at a census tract geography and thus, an approach is needed
to reconcile tracts and fishnet geometries. This is accomplished using a
technique called areal weighted interpolation.
Recall, you will need a census API key to download the census data
which must be input with census_api_key.
census_api_key("e5e96d76285beca3c6e1a9110d762a430ced4811", overwrite = TRUE)
First data is pulled for 2011 and reprojected.
# Pull population data for 2011
library(tidycensus)
library(tigris)
library(sf)
# Define the counties you want to retrieve data for
counties <- c("Cook", "DuPage", "Will County")
# Retrieve population data for 2011 and tract geometry
Pop_2011 <- get_acs(geography = "tract",
variables = "B01003_001",
year = 2011,
state = "IL",
county = counties,
geometry = TRUE) %>%
rename(Pop_2011 = estimate) %>%
st_transform(st_crs(ThreeCounty_fishnet))
# Write the spatial dataframe to a shapefile
st_write(Pop_2011,"C:/Users/3lpaw/Desktop/ArcGIS Pro 3.2/EnvModeling/04_24_24_UrbanGrowthModeling/Downloaded_Data/Population/Pop_2011.shp", append = TRUE)
# Clip Pop_2011 to the extent of ThreeCounty_fishnet. Otherwise, it includes an area of Lake Michigan
Pop_2011 <- st_intersection(Pop_2011, ThreeCounty_fishnet)
Now data for 2021 is downloaded. In this instance,
st_buffer is used to buffer the the tracts by -1ft. This is
done because tidycensus appears to return geometries that
are problematic when subjected to the area weighted interpolation
function below. As done in previous chapters, a very small buffer is
used to correct the geometries.
counties <- c("Cook", "DuPage", "Will County")
# Pull population data for 2021
Pop_2021 <- get_acs(geography = "tract",
variables = "B01003_001",
year = 2021,
state = "IL",
county = counties,
geometry = TRUE) %>%
rename(Pop_2021 = estimate) %>%
st_transform(st_crs(ThreeCounty_fishnet)) %>%
st_buffer(-1)
st_write(Pop_2021,"C:/Users/3lpaw/Desktop/ArcGIS Pro 3.2/EnvModeling/04_24_24_UrbanGrowthModeling/Downloaded_Data/Population/Pop_2021.shp", append = TRUE)
Both years of census data are then plotted.
class(Pop_2011)
## [1] "sf" "data.frame"
str(Pop_2011)
## Classes 'sf' and 'data.frame': 10316 obs. of 6 variables:
## $ GEOID : chr "17197884004" "17197884004" "17197884004" "17197884004" ...
## $ NAME : chr "Census Tract 8840.04, Will County, Illinois" "Census Tract 8840.04, Will County, Illinois" "Census Tract 8840.04, Will County, Illinois" "Census Tract 8840.04, Will County, Illinois" ...
## $ variable: chr "B01003_001" "B01003_001" "B01003_001" "B01003_001" ...
## $ Pop_2011: num 2552 2552 2552 2552 1555 ...
## $ moe : num 221 221 221 221 184 221 184 184 184 184 ...
## $ geometry:sfc_GEOMETRY of length 10316; first list element: List of 1
## ..$ : num [1:14, 1:2] 1010924 1009934 1008790 1008790 1008779 ...
## ..- attr(*, "class")= chr [1:3] "XY" "POLYGON" "sfg"
## - attr(*, "sf_column")= chr "geometry"
## - attr(*, "agr")= Factor w/ 3 levels "constant","aggregate",..: NA NA NA NA NA
## ..- attr(*, "names")= chr [1:5] "GEOID" "NAME" "variable" "Pop_2011" ...
str(Pop_2021)
## Classes 'sf' and 'data.frame': 1723 obs. of 6 variables:
## $ GEOID : chr "17031230200" "17197880113" "17031320400" "17031828701" ...
## $ NAME : chr "Census Tract 2302, Cook County, Illinois" "Census Tract 8801.13, Will County, Illinois" "Census Tract 3204, Cook County, Illinois" "Census Tract 8287.01, Cook County, Illinois" ...
## $ variable: chr "B01003_001" "B01003_001" "B01003_001" "B01003_001" ...
## $ Pop_2021: num 1979 4004 3058 4571 1201 ...
## $ moe : num 359 573 366 623 163 830 592 441 507 424 ...
## $ geometry:sfc_POLYGON of length 1723; first list element: List of 1
## ..$ : num [1:10, 1:2] 1152980 1153860 1153860 1154304 1154763 ...
## ..- attr(*, "class")= chr [1:3] "XY" "POLYGON" "sfg"
## - attr(*, "sf_column")= chr "geometry"
## - attr(*, "agr")= Factor w/ 3 levels "constant","aggregate",..: NA NA NA NA NA
## ..- attr(*, "names")= chr [1:5] "GEOID" "NAME" "variable" "Pop_2021" ...
# If the geometry column is missing or has a different name, rename it
Pop_2011 <- st_set_geometry(Pop_2011, "geometry")
Pop_2021 <- st_set_geometry(Pop_2021, "geometry")
library(gridExtra)
grid.arrange(
ggplot() +
geom_sf(data = Pop_2011, aes(fill = factor(ntile(Pop_2011, 5))), colour = NA) +
scale_fill_manual(values = palette5,
labels = quintileBreaks(Pop_2011, "Pop_2011"),
name = "Quintile\nBreaks") +
labs(title = "Population per Census Tract in Cook, Du Page, and Will Counties: 2011") +
mapTheme,
ggplot() +
geom_sf(data = Pop_2021, aes(fill = factor(ntile(Pop_2021, 5))), colour = NA) +
scale_fill_manual(values = palette5,
labels = quintileBreaks(Pop_2021, "Pop_2021"),
name = "Quintile\nBreaks") +
labs(title = "Population per Census Tract in Cook, Du Page, and Will Counties: 2021") +
mapTheme,
ncol = 2
)

Now to reconcile tract boundaries and fishnet grid cells. I’d like
you to pay particular attention to this process.
A spatial join would be inappropriate as it would assign the same
population value from one tract to the many intersecting grid cells.
Instead, the area weighted interpolation function,
st_interpolate_aw, assigns a proportion of a tract’s
population to a grid cell weighted by the proportion of the tract that
intersects the grid cell. This works best of course, when we assume that
the tract population is uniformly distributed across the tract. This is
typically not a great assumption. However, it is a reasonable here
particularly given population is a feature in a regression and not an
outcome that needs to be measured with significant precision.
The Census data Pop_2011, has a different spatial extent
than ThreeCounty_fishnet. Most notably, there are no
vectors where water is present. To maintain the needed 3851 grid cell
units (nrow(ThreeCounty_fishnet)), a a unique id is
created, fishnetID. Then the area weighted interpolation is
performed on population for the 2011 and 2021 layers.
Finally, the results are joined back (left_join) to
ThreeCounty_fishnet. This approach maintains a consistent
spatial extent.
It would be helpful for you to spend some time running through each
code block line by line. Areal weighted interpolation is a really strong
spatial analysis skill to have. You can do this in ArcGIS but there is
not automated approach.
ThreeCounty_fishnet <-
ThreeCounty_fishnet %>%
rownames_to_column("fishnetID") %>%
mutate(fishnetID = as.numeric(fishnetID)) %>%
dplyr::select(fishnetID)
fishnetPopulation11 <-
st_interpolate_aw(Pop_2011["Pop_2011"], ThreeCounty_fishnet, extensive=TRUE) %>%
as.data.frame(.) %>%
rownames_to_column(var = "fishnetID") %>%
left_join(ThreeCounty_fishnet %>%
mutate(fishnetID = as.character(fishnetID)),
., by=c("fishnetID"='fishnetID')) %>%
mutate(Pop_2011 = replace_na(Pop_2011,0)) %>%
dplyr::select(Pop_2011)
fishnetPopulation21 <-
st_interpolate_aw(Pop_2021["Pop_2021"],ThreeCounty_fishnet, extensive=TRUE) %>%
as.data.frame(.) %>%
rownames_to_column(var = "fishnetID") %>%
left_join(ThreeCounty_fishnet %>%
mutate(fishnetID = as.character(fishnetID)),
., by=c("fishnetID"='fishnetID')) %>%
mutate(Pop_2021 = replace_na(Pop_2021,0)) %>%
dplyr::select(Pop_2021)
fishnetPopulation <-
cbind(fishnetPopulation11,fishnetPopulation21) %>%
dplyr::select(Pop_2011,Pop_2021) %>%
mutate(pop_Change = Pop_2021 - Pop_2011)
For comparison purposes, both the 2021 census tract geometries and
the population weighted grid cells are plotted.
grid.arrange(
ggplot() +
geom_sf(data=Pop_2021, aes(fill=factor(ntile(Pop_2021,5))),colour=NA) +
scale_fill_manual(values = palette5,
labels=substr(quintileBreaks(Pop_2021,"Pop_2021"),1,4),
name="Quintile\nBreaks") +
labs(title="Population for Cook, Du Page, and Will Counties: 2021",
subtitle="Represented as tracts; Boundaries omitted") +
mapTheme,
ggplot() +
geom_sf(data=fishnetPopulation, aes(fill=factor(ntile(Pop_2021,5))),colour=NA) +
scale_fill_manual(values = palette5,
labels=substr(quintileBreaks(fishnetPopulation,"Pop_2021"),1,4),
name="Quintile\nBreaks") +
labs(title="Population for Cook, Du Page, and Will Counties: 2021",
subtitle="Represented as fishnet gridcells; Boundaries omitted") +
mapTheme, ncol=2)

2.5. Highway Distance
Accessibility is a key determinant of development potential
particularly in a sprawling city like Chicago. Accessibility features
are engineered by measuring distance from each grid cell to its nearest
highway.
First highway vectors are downloaded from the U.S. Census TIGER Line
2019 datasets in a shapefile format; projected and subset to the subset
using st_intersection. Below, new development is mapped
with the highway overlay.
ThreeCounties_Highways <-
st_read("C:/Users/3lpaw/Desktop/ArcGIS Pro 3.2/EnvModeling/04_24_24_UrbanGrowthModeling/Downloaded_Data/IL_highways/IL_highways/tl_2019_17_prisecroads.shp") %>%
st_transform(st_crs(ThreeCountyArea)) %>%
st_intersection(ThreeCountyArea)
ggplot() +
geom_point(data=fishnet,
aes(x=xyC(fishnet)[,1], y=xyC(fishnet)[,2],colour=Dev_change),size=1.5) +
geom_sf(data=ThreeCounties_Highways, colour = "red") +
scale_colour_manual(values = palette2,
labels=c("No Change","New Development")) +
labs(title = "New Development and Highways",
subtitle = "As fishnet centroids") +
mapTheme

Below are some great r-based raster skills. The distance from each
grid cell to its nearest highway segment is measured.
First, you can convert a the highway layer to raster. This can be
done by creating an emptyRaster of NA grid
cells at the same spatial extent as Development_change.
Then, highway_raster is created by converting
ThreeCounties_Highways to sp form and then to
applying rasterize. The raster is then converted to points
with rasterToPoints and st_as_sf, then
aggregate is used to calculate mean distance by grid
cell.
You may (but likely not) be interested in learning that
sp is the older spatial data convention in R. Although
sf is the new convention, raster/vector interactions still
require sp. The as function converts.
Instead, we used a slightly different method to calculate distance to
highways. Here is the code we didn’t use:
# Original code (not using):
# emptyRaster <- Development_change
# emptyRaster[] <- NA
#
# highway_raster <-
# as(ThreeCounties_Highways,'Spatial') %>%
# rasterize(.,emptyRaster)
#
# highway_raster_distance <- distance(highway_raster)
# names(highway_raster_distance) <- "distance_highways"
#
# highwayPoints <-
# rasterToPoints(highway_raster_distance) %>%
# as.data.frame() %>%
# st_as_sf(coords = c("x", "y"), crs = st_crs(ThreeCounty_fishnet))
#
# highwayPoints_fishnet <-
# aggregate(highwayPoints, ThreeCounty_fishnet, mean) %>%
# mutate(distance_highways = ifelse(is.na(distance_highways),0,distance_highways))
#
# ggplot() +
# geom_sf(data=ThreeCountyArea) +
# geom_point(data=highwayPoints_fishnet, aes(x=xyC(highwayPoints_fishnet)[,1],
# y=xyC(highwayPoints_fishnet)[,2],
# colour=factor(ntile(distance_highways,5))),size=1.5) +
# scale_colour_manual(values = palette5,
# labels=substr(quintileBreaks(highwayPoints_fishnet,"distance_highways"),1,8),
# name="Quintile\nBreaks") +
# geom_sf(data=houstonHighways, colour = "red") +
# labs(title = "Distance to Highways",
# subtitle = "As fishnet centroids; Highways visualized in red") +
# mapTheme
Instead, we read in the highways raster and the ThreeCounty_fishnet
and used st_nearest_feature to calculate the distance from each fishnet
cell centroid to the nearest highway. The variable highway_dist includes
these calculated distances to the nearest highway.
#Read in highways and transform to appropriate projection
# It's called "ThreeCounties_Highways" - we read this in earlier in the code
#Convert fishnet to centroids
centroid <- ThreeCounty_fishnet %>%
st_centroid()
#Determine nereast highway to each centroid
nearest_feat <- st_nearest_feature(centroid,ThreeCounties_Highways)
#Calcuate distance from each grid square centroid to nearest highway
ThreeCounty_fishnet$highway_dist <- as.double(st_distance(centroid, ThreeCounties_Highways[nearest_feat,], by_element=TRUE))
Highway_fishnet <- ThreeCounty_fishnet
#Make a quick sample map of the results
ggplot()+
geom_sf(data=Highway_fishnet,aes(fill=highway_dist),color='transparent')+
scale_fill_viridis_c(name='Distance to Highway (feet)')+
geom_sf(data=ThreeCounties_Highways,color='red')+
labs(title = "Distance to Highways",
subtitle = "Using fishnet centroids") +
theme_void()

There are a lot of federal and state highways in the three county
area, particularly closer to Chicago. There are some more rural and
suburban areas in the southern portion of the study area that are
further from highways.
2.5.b Distance to Regional Rail Lines (Metra)
Transit accessibility is a key determinant of development potential
particularly in a city like Chicago. Transit-oriented development is
commonly prioritized to create compact development. Accessibility
features are engineered by measuring distance from each grid cell to its
nearest regional rail line.
First regional rail line vectors are downloaded from the City of
Chicago open data website in shp format; projected and
subset to the subset using st_intersection. Below, new
development is mapped with the regional rail line overlay. The regional
rail lines are a part of the Metra system for the Chicagoland
region.
ThreeCounties_RegRail <-
st_read("C:/Users/3lpaw/Desktop/ArcGIS Pro 3.2/EnvModeling/04_24_24_UrbanGrowthModeling/Downloaded_Data/Metra_Lines/MetraLinesshp.shp") %>%
st_transform(st_crs(ThreeCountyArea)) %>%
st_intersection(ThreeCountyArea)
Next, we map the existing regional rail lines with our analysis of
development change from 2011-2021.
ggplot() +
geom_point(data=fishnet,
aes(x=xyC(fishnet)[,1], y=xyC(fishnet)[,2],colour=Dev_change),size=1.5) +
geom_sf(data=ThreeCounties_RegRail, colour = "red") +
scale_colour_manual(values = palette2,
labels=c("No Change","New Development")) +
labs(title = "New Development and Regional Rail Lines",
subtitle = "As fishnet centroids") +
mapTheme

Next, we calculate the distance to the nearest regional rail line for
each fishnet cell centroid and map it. This variable is called
RegRail_dist.
#Determine nearest rail line to each centroid
nearest_rail <- st_nearest_feature(centroid,ThreeCounties_RegRail)
#Calcuate distance from each grid square centroid to nearest rail line
ThreeCounty_fishnet$regrail_dist <- as.double(st_distance(centroid, ThreeCounties_RegRail[nearest_rail,], by_element=TRUE))
RegRail_fishnet <- ThreeCounty_fishnet #%>%
# select(fishnetID, geometry, regrail_dist)
#Make a quick sample map of the results
ggplot()+
geom_sf(data=RegRail_fishnet,aes(fill=regrail_dist),color='transparent')+
scale_fill_viridis_c(name='Distance to Regional Rail Lines (feet)')+
geom_sf(data=ThreeCounties_RegRail,color='red')+
labs(title = "Distance to Regional Rail Lines",
subtitle = "Using fishnet centroids") +
theme_void()

As you can see, most of the three county area is within 25,000 feet
(or a little over 4.5 miles) of a regional rail line. This is too large
of a distance for walking commuters, but it is accessible for people who
bike or drive or take other modes to the regional rail stations to
access the Chicago city region. To improve housing density, it is ideal
to create more developments in close proximity to regional rail
stations, ideally within a half mile for walkers or a mile or more for
bike/bus/car/other modal commutes.
2.5.c Distance to Downtown Chicago (The Loop)
Proximity to employment centers is a key determinant of development
particularly in a major city like Chicago and its surrounding commuter
suburbs. Features are engineered by measuring distance from each grid
cell to downtown Chicago, also known as the Loop.
First a polygon feature is traced around Downtown Chicago using
ArcGIS Pro. Below, new development is mapped with the Downtown_Chicago
feature overlay.
ThreeCounties_Downtown <-
st_read("C:/Users/3lpaw/Desktop/ArcGIS Pro 3.2/EnvModeling/04_24_24_UrbanGrowthModeling/Downloaded_Data/Downtown_Chicago/Downtown_Chicago.shp") %>%
st_transform(st_crs(ThreeCountyArea)) %>%
st_intersection(ThreeCountyArea)
## Reading layer `Downtown_Chicago' from data source
## `C:\Users\3lpaw\Desktop\ArcGIS Pro 3.2\EnvModeling\04_24_24_UrbanGrowthModeling\Downloaded_Data\Downtown_Chicago\Downtown_Chicago.shp'
## using driver `ESRI Shapefile'
## Simple feature collection with 1 feature and 1 field
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: -87.63707 ymin: 41.85304 xmax: -87.61388 ymax: 41.88842
## Geodetic CRS: NAD83
Next, we plot the Chicago Loop area seen in red and show the
development change from 2011-2021 in the three county area.
ggplot() +
geom_point(data=fishnet,
aes(x=xyC(fishnet)[,1], y=xyC(fishnet)[,2],colour=Dev_change),size=1.5) +
geom_sf(data=ThreeCounties_Downtown, colour = "black", fill = "red") +
scale_colour_manual(values = palette2,
labels=c("No Change","New Development")) +
labs(title = "New Development and Proximity to Downtown",
subtitle = "As fishnet centroids") +
mapTheme

Next, we plot the proximity to downtown variable (downtown_dist)
using fishnet centroids and map it.
library(scales)
#Determine distance to City Center centroid
near_downtown <- st_nearest_feature(centroid,ThreeCounties_Downtown)
#Calculate distance from each grid square centroid to the city center centroid
ThreeCounty_fishnet$downtown_dist <- as.double(st_distance(centroid, ThreeCounties_Downtown[near_downtown,], by_element=TRUE))
Downtown_fishnet <- ThreeCounty_fishnet
#%>% select(fishnetID, geometry, downtown_dist)
ggplot()+
geom_sf(data=Downtown_fishnet,aes(fill=downtown_dist),color='transparent')+
scale_fill_viridis_c(name='Proximity to Downtown (feet)', labels = comma)+
geom_sf(data=ThreeCounties_Downtown,color="black", fill = "red")+
labs(title = "Distance to Downtown Chicago",
subtitle = "Using fishnet centroids") +
theme_void()

2.5.d Distance to Lake Michigan
Proximity to the waterfront is a key determinant of development
particularly in a major city like Chicago and its surrounding commuter
suburbs. Features are engineered by measuring distance from each grid
cell to the Lake Michigan shoreline.
Below, new development is mapped with the Lake Michigan shoreline
feature overlay, which is shown in red.
ThreeCounties_LakeMichigan <-
st_read("C:/Users/3lpaw/Desktop/ArcGIS Pro 3.2/EnvModeling/04_24_24_UrbanGrowthModeling/Downloaded_Data/Lake_Michigan_Shoreline/lake_shore.shp") %>%
st_transform(st_crs(ThreeCountyArea)) %>%
st_intersection(ThreeCountyArea)
## Reading layer `lake_shore' from data source
## `C:\Users\3lpaw\Desktop\ArcGIS Pro 3.2\EnvModeling\04_24_24_UrbanGrowthModeling\Downloaded_Data\Lake_Michigan_Shoreline\lake_shore.shp'
## using driver `ESRI Shapefile'
## Simple feature collection with 1 feature and 1 field
## Geometry type: LINESTRING
## Dimension: XY
## Bounding box: xmin: -87.77002 ymin: 41.68835 xmax: -87.52472 ymax: 42.15241
## Geodetic CRS: NAD83
ggplot() +
geom_point(data=fishnet,
aes(x=xyC(fishnet)[,1], y=xyC(fishnet)[,2],colour=Dev_change),size=1.5) +
geom_sf(data=ThreeCounties_LakeMichigan, colour = "red") +
scale_colour_manual(values = palette2,
labels=c("No Change","New Development")) +
labs(title = "New Development and Proximity to Lake Michigan",
subtitle = "As fishnet centroids") +
mapTheme

Next, we calculate the distance to the lake (lake_dist) and map this
variable.
library(scales)
#Determine distance to City Center centroid
near_lake <- st_nearest_feature(centroid,ThreeCounties_LakeMichigan)
#Calculate distance from each grid square centroid to the lake centroid
ThreeCounty_fishnet$lake_dist <- as.double(st_distance(centroid, ThreeCounties_LakeMichigan[near_lake,], by_element=TRUE))
LakeMichigan_fishnet <- ThreeCounty_fishnet
#%>% select(fishnetID, geometry, lake_dist)
ggplot()+
geom_sf(data=LakeMichigan_fishnet,aes(fill=lake_dist),color='transparent')+
scale_fill_viridis_c(name='Proximity to Lake Michigan (feet)', labels = comma)+
geom_sf(data=ThreeCounties_LakeMichigan,color="black", fill = "red")+
labs(title = "Distance to Lake Michigan",
subtitle = "Using fishnet centroids") +
theme_void()

2.6. The Spatial Lag of Development
At the center of our model is a hypothesis that development demand
must in part, be a function of the pattern of existing development.
Development occurs where the market believes a higher and better use may
bring an investment return. In the case of sprawling region like
Houston, assuming the requisite demand, there is a clear return on
investment for converting farmland to suburban housing.
The traditional ‘bid-rent’ economic model of development posits that
development demand is a function of accessibility. This model works well
in cities where centralized locations offer the most accessibility.
However, it assumes that all consumers of land share the same
preferences for central city access. While urban land is valuable,
contemporary urbanism in regions like Houston show us that suburban
locations can be quite desirable as well.
Why is that? Hinterland locations do not offer direct access to jobs
and cultural amenities. Instead, residents trade-off accessibility for
larger lots and bigger homes; as well as a bundle of public services
like school quality. Developers are attracted to suburban and exurban
locations because of cheap land on ‘greenfield’ sites like farms and
open space.
The demand for greenfield development can vary substantially
depending on the existing spatial configuration of development. If
accessibility to central locations was the only underlying
consideration, developers would sprawl directly out to the periphery,
much like the dynamic we modeled in the Urban Growth Boundary chapter
(Chapter 2). As a space/time process, this would look much like spilled
milk, emanating from a central point outward across the kitchen
table.
Another option for developers is to move beyond the periphery onto
greenfield sites that are cheaper because they are even less accessible.
In this case, the space/time process looks small ‘patches’ of new
development dotting the landscape and “leapfrogging” from one greenfield
to the next. The economic incentive is to always develop beyond the
periphery, where land is cheapest. There are some real costs to this
model however. For one, when development is so diffuse, it is more
burdensome to efficiently deploy infrastructure like roads, sewers and
electicity. Second, leapfrog development fragmentats natural areas
reducing biodiversity and stressing the natural habitat of species that
need continuous open space to thrive.
In Chicago, as in many sprawling regions of the U.S. the economic
incentives that underlie sprawl likely encourage both the accessibility
and leapfrog models of development. For our purposes however, features
must be created to associate these patterns with development. Without
them, the model may lack the appropriate spatial experience on which to
forecast growth.
To keep it simple, we develop features associated with
accessibility-based patterns. In reality, the analyst should develop a
series of applicable features and test which best associate with the
outcome of interest. The problem becomes infinitely more difficult when
one realizes that sprawl patterns may differ throughout the study area -
if for instance, land use restrictions varied by county. Below we
estimate models using logistic regression, but higher level machine
learning algorithms, most notably, Random Forest, are more adept at
dealing with non-linearities across space.
Accessibility is measured by way of a spatial lag hypothesizing that
new development is a function of distance to existing development. The
shorter the distance, the more accessible a grid cell is to existing
development. This is measured by calculating the average distance from
each grid cell to its 2 nearest developed neighboring grid cells in 2011
using the nn_function. The function below calculates
average nearest neighbor distance between k point layers. The first
parameter specifies coordinates that we want to
measureFrom, in this case, fishnet centroids.
The second, indicates the point layer we wish to measureTo,
in this case, the fishnet centroids that were developed in 2011.
nn_function <- function(measureFrom,measureTo,k) {
#convert the sf layers to matrices
measureFrom_Matrix <-
as.matrix(measureFrom)
measureTo_Matrix <-
as.matrix(measureTo)
nn <-
get.knnx(measureTo, measureFrom, k)$nn.dist
output <-
as.data.frame(nn) %>%
rownames_to_column(var = "thisPoint") %>%
gather(points, point_distance, V1:ncol(.)) %>%
arrange(as.numeric(thisPoint)) %>%
group_by(thisPoint) %>%
summarize(pointDistance = mean(point_distance)) %>%
arrange(as.numeric(thisPoint)) %>%
dplyr::select(-thisPoint) %>%
pull()
return(output)
}
Why k=2? As k fluctuates, so does the
hypothesized scale of accessibility. One can test the effect of
different k parameters on model goodness of fit, but as mentioned, a
more sophisticated model would hypothesize that this scale can vary
significantly from city to suburb to rural town.
Next, the function appending the lag distance to
fishnet. There are 3 inputs. The fishnet which
is converted to a coordinate data frame with the xyC
function. 2011 developed areas are created using filter.
The map below illustrates relative accessibility from every grid cell to
nearby development.
fishnet$lagDevelopment <-
nn_function(xyC(fishnet),
xyC(filter(aggregatedRasters,developed==1)),
2)
ggplot() +
geom_sf(data = ThreeCountyArea, fill = "transparent") +
geom_point(data = fishnet,
aes(x = xyC(fishnet)[, 1], y = xyC(fishnet)[, 2], color = lagDevelopment),
size = 1.5) +
scale_color_gradientn(colors = palette5,
limits = range(fishnet$lagDevelopment),
name = "Lag Development",
labels = scales::comma) +
labs(title = "Spatial Lag to 2011 Development",
subtitle = "As fishnet centroids") +
mapTheme

# ggplot() +
# geom_sf(data=ThreeCountyArea, fill = "transparent") +
# geom_point(data=fishnet,
# aes(x=xyC(fishnet)[,1], y=xyC(fishnet)[,2],
# colour=factor(ntile(lagDevelopment,5))), size=1.5) +
# scale_colour_manual(values = palette5,
# labels=substr(quintileBreaks(fishnet,"lagDevelopment"),1,7),
# name="Quintile\nBreaks") +
# labs(title = "Spatial Lag to 2011 Development",
# subtitle = "As fishnet centroids") +
# mapTheme
2.7. Study Area Counties
The tigris package allows Illinois county geometries to
be downloaded. A spatial subset returns only the three counties we are
looking at. Note that the subset includes a negative 1000ft
st_buffer. This is done because the spatial extent of
ThreeCountyArea intersects county boundaries that are
actually outside of our study area. Buffering
ThreeCountyArea slightly limits the intersection range to
only those counties in the study area.
Once studyAreaCounties is created, it is
st_joined with dat such that each grid cells
knows which county it’s in.
We already have a shapefile and raster of the ThreeCountyArea, so we
skipped this code.
# We already have a shapefile of the 3 county study area.
# options(tigris_class = "sf")
#
# studyAreaCounties <-
# counties("Illinois") %>%
# st_transform(st_crs(ThreeCountyArea)) %>%
# dplyr::select(NAME) %>%
# .[st_buffer(ThreeCountyArea,-1000), , op=st_intersects]
We mapped the three counties for reference.
library(ggplot2)
# Calculate centroids for the polygons
county_centroids <- st_centroid(IL_counties_subset)
# Extract centroid coordinates
county_centroid_coords <- st_coordinates(county_centroids)
# Create a data frame with centroid coordinates
county_centroid_df <- data.frame(X = county_centroid_coords[, "X"], Y = county_centroid_coords[, "Y"])
# Add COUNTY_NAM column to centroid_df
county_centroid_df$COUNTY_NAM <- county_centroids$COUNTY_NAM
# Plot with text labels
ggplot() +
geom_sf(data = IL_counties_subset) +
geom_text(data = county_centroid_df, aes(label = COUNTY_NAM, x = X, y = Y), size = 3, color = "black") +
labs(title = "Study Area Counties") +
mapTheme

2.8. Create the Final Dataset
The last step is to bring together all the disparate feature layers
into a final dataset that can be used for analysis. The various fishnet
layers are cbind together, needed features are extracted
and the final fishnet, dat is then joined with
ThreeCountyArea to assign each grid cell to a county.
developed21 is created to designate those areas that have
already been developed through 2021. Finally, any grid cell that has a
water land cover designation is removed.
dat <-
cbind(
fishnet, fishnetPopulation, Highway_fishnet, RegRail_fishnet, Downtown_fishnet, LakeMichigan_fishnet, aggregatedRasters) %>%
dplyr::select(Dev_change, developed, forest, farm, wetlands, otherUndeveloped, water,
Pop_2011, Pop_2021, pop_Change, highway_dist, regrail_dist, downtown_dist, lake_dist, lagDevelopment) %>%
st_join(ThreeCountyArea) %>%
mutate(developed21 = ifelse(Dev_change == 1 & developed == 1, 0, developed)) %>%
filter(water == 0)
3. Exploratory Analysis
In this section we explore the extent to which each features is
associated with development change. If the goal was to predict a
continuous variable, scatterplots and correlation coefficients make this
process straightforward and relatively easy to explain to a
non-technical decison maker.
In this case however, the dependent variable is a binary outcome -
either a grid cell was developed between 2011 and 2021 or it wasn’t. In
this case, the relevant question is whether for a given feature, there
is a statistically significant difference between areas that changed and
areas that did not. These differences are explored in a set of plots
below. For models with lots of features, these plots could be compliment
by a series of difference in means statistical tests.
The below code block selects the distance variables and
spatial lag features, converts each to long form and plots each as bar
plots. Note that geom_bar calculates the
mean.
dat %>%
dplyr::select(pop_Change, highway_dist, regrail_dist, downtown_dist, lake_dist, lagDevelopment, Dev_change) %>%
gather(Variable, Value, -Dev_change, -geometry) %>%
ggplot(., aes(Dev_change, Value, fill=Dev_change)) +
geom_bar(position = "dodge", stat = "summary", fun.y = "mean") +
facet_wrap(~Variable) +
scale_fill_manual(values = palette2,
labels=c("No Change","New Development"),
name="") +
labs(title="New Development as a Function of the Continuous Variables") +
plotTheme

There are significant differences between no change and new
development (from 2011-2021) for the lake distance and downtown Chicago
distance variables. The bar plots for the other variables (Development
lag, distance to highways, population change, and distance to regional
rail) are above.
Next, the same visualization is created for the population related
variables. These plots inform which features should be included in the
model.
dat %>%
dplyr::select(Pop_2011, Pop_2021, pop_Change, Dev_change) %>%
gather(Variable, Value, -Dev_change, -geometry) %>%
ggplot(., aes(Dev_change, Value, fill=Dev_change)) +
geom_bar(position = "dodge", stat = "summary", fun.y = "mean") +
facet_wrap(~Variable) +
scale_fill_manual(values = palette2,
labels=c("No Change","New Development"),
name="") +
labs(title="New Development as a Function of Factor Variables") +
plotTheme

The population change variable is a bit of a tough one, because the
number, size, and shape of the Census tracts changed from the 2010 to
2020 decennial Census. If a Census tract had an increase in population,
then it split into two or more Census tracts and was given a different
tract ID number and boundary. This makes it a bit hard to compare
geographies across the decade, but we used the spatial lag feature to
address that issue.
Next, a table of land cover conversion between 2011 and 2021 is
created. The table suggests for instance, that 9% of farmland regionally
was converted to development between 2011 and 2021. This indicator
should be interpreted in the context of the scale changes we imposed on
the data by moving from a 30ft by 30ft raster to a 4000ft by 4000ft
fishnet. This is the same reason why the table suggests
developed area was then “developed”.
dat %>%
dplyr::select(Dev_change:otherUndeveloped,developed) %>%
gather(Land_Cover_Type, Value, -Dev_change, -geometry) %>%
st_set_geometry(NULL) %>%
group_by(Dev_change, Land_Cover_Type) %>%
summarize(n = sum(as.numeric(Value))) %>%
ungroup() %>%
mutate(Conversion_Rate = paste0(round(100 * n/sum(n), 2), "%")) %>%
filter(Dev_change == 1) %>%
dplyr::select(Land_Cover_Type,Conversion_Rate) %>%
kable() %>% kable_styling(full_width = F)
|
Land_Cover_Type
|
Conversion_Rate
|
|
developed
|
13.35%
|
|
farm
|
9.1%
|
|
forest
|
7.1%
|
|
otherUndeveloped
|
6.23%
|
|
wetlands
|
5.09%
|
4. Predicting for 2031
In this section, six separate logistic regression models are
estimated to predict development change between 2011 and 2021 - with
each subsequent model more sophisticated then the last. To do so, the
data is split into 50% training/test sets. Models are estimated on the
training set.
Normally, as in previous chapters, a results table row would be
generated for each model describing the accuracy and generalizability of
predictions for each specification. For brevity, a less sophisticated
approach is taken here, judging each by the McFadden or “Psuedo” R
Squared statistic on the test set. The model with the greatest goodness
of fit is then used for the purposes of prediction.
4.2. Modeling
First, dat is split into training and test sets. Note
how imbalanced the panel is with
table(datTrain$developed).
set.seed(3456)
trainIndex <-
createDataPartition(dat$developed, p = .50,
list = FALSE,
times = 1)
datTrain <- dat[ trainIndex,]
datTest <- dat[-trainIndex,]
nrow(dat)
## [1] 2808
table(datTrain$developed)
##
## 0 1
## 151 1254
Next six separate glm models are estimated adding new
variables for each. Figure 4.1 shows the Psuedo R-Squared associated
with each model.
Model1 includes only the 2011 land cover types.
Model2 adds the lagDevelopment. Models 3, 4
and 5 attempt three different approaches for modeling population change.
Model3 uses population in 2011; Model4 uses
2011 and 2021 population; and Model5 uses population
change. All are significant so which population feature should be
chosen? The answer lies in how the model will be used to forecast. By
modeling population change between 2011 and 2021, the model is well
specified to forecast 2031 development by having pop_Change
indicate change between 2021 and 2031. Model6 includes
distance to the highways and all other variables, and is the final model
employed for prediction.
Model1 <- glm(Dev_change ~ wetlands + forest + farm + otherUndeveloped,
family="binomial"(link="logit"), data = datTrain)
Model2 <- glm(Dev_change ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment,
family="binomial"(link="logit"), data = datTrain)
Model3 <- glm(Dev_change ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment + Pop_2011,
family="binomial"(link="logit"), data = datTrain)
Model4 <- glm(Dev_change ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment + Pop_2011 +
Pop_2021,
family="binomial"(link="logit"), data = datTrain)
Model5 <- glm(Dev_change ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment + pop_Change,
family="binomial"(link="logit"), data = datTrain)
Model6 <- glm(Dev_change ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment + pop_Change +
highway_dist + downtown_dist + lake_dist + regrail_dist,
family="binomial"(link="logit"), data = datTrain)
Working carefully through the below code block, a very concise
approach for creating a data frame of psudeo R Squares for each model
and plotting them for comparison. Recall, pR2 is the
function for psuedo R squared. Dissect the line that uses the
map_dfc function to see how this approach loops through the
models retrieving the goodness of fit for each.
modelList <- paste0("Model", 1:6)
map_dfc(modelList, function(x)pR2(get(x)))[4,] %>%
setNames(paste0("Model",1:6)) %>%
gather(Model,McFadden) %>%
ggplot(aes(Model,McFadden)) +
geom_bar(stat="identity") +
labs(title= "McFadden R-Squared by Model") +
plotTheme
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2

Next, a data frame is created that includes columns for the observed
development change, Dev_change, and one that includes
predicted probabilities for Model6. This data frame is then
used as an input to a density plot visualizing the distribution of
predicted probabilities by observed class. Only a small number of
predicted probabilities are greater than or equal to 50%
(nrow(filter(testSetProbs, probs >= .50)) / nrow(datTest)).
This makes good sense, given how rare of an event development is in our
dataset. Ultimately, in order to judge our model with a confusion
matrix, a smaller development classification threshold must be
employed.
testSetProbs <-
data.frame(class = datTest$Dev_change,
probs = predict(Model6, datTest, type="response"))
ggplot(testSetProbs, aes(probs)) +
geom_density(aes(fill=class), alpha=0.5) +
scale_fill_manual(values = palette2,
labels=c("No Change","New Development")) +
labs(title = "Histogram of test set predicted probabilities",
x="Predicted Probabilities",y="Density") +
plotTheme

4.3. Accuracy
Now to pick a predicted probability threshold to classify an area as
having new development. Recall, Sensitivity or the True
Positive rate is the proportion of actual positives (1’s) that were
predicted to be positive. For example, the Sensitivity in our model is
the rate of developed areas actually predicted as such.
Specificity or True Negative Rate is the proportion of actual
negatives (0’s) that were predicted to be negatives. For example, the
Specificity in our model is the rate of No Change areas that were
correctly predicted as No change.
It is important to consider what Planners would typically optimize
for given this use case. One approach is to maximize the number of 1’s
predicted correctly (Sensitivity) so as to not under or over-predict new
development. It may okay in this use case to incorrectly predict no
change as changed (Specificity). An abundance of False Negative errors
may be reasonable if Planners don’t mind over emphasizing development
potential. It is important to remember that below this potential will
evaluated alongside supply-side indicators such as the presence of
sensitive land.
There are some clear tradeoffs between Sensitivity and Specificity in
our model that deserve some exploration. To illustrate, two different
thresholds of 5% and 17% are explored. Predicted classes for both
thresholds are generated and instead of using the
confusionMatrix function from caret as we have
in the past, here confusion matrix metrics are derived from the
yardstick package. This allows us to group_by
the threshold and summarize the metrics of interest.
The options call below is required to tell
yardstick that the positive factor class in
testSetProbs is 1. Without it, yardstick will
by default, see the first factor level as 0 and flip the
confusion metrics around.
options(yardstick.event_first = FALSE)
testSetProbs <-
testSetProbs %>%
mutate(predClass_05 = as.factor(ifelse(testSetProbs$probs >= 0.05 ,1,0)),
predClass_17 = as.factor(ifelse(testSetProbs$probs >= 0.17 ,1,0)))
testSetProbs %>%
dplyr::select(-probs) %>%
gather(Variable, Value, -class) %>%
group_by(Variable) %>%
summarize(Sensitivity = round(yardstick::sens_vec(class,factor(Value)),2),
Specificity = round(yardstick::spec_vec(class,factor(Value)),2),
Accuracy = round(yardstick::accuracy_vec(class,factor(Value)),2)) %>%
kable() %>%
kable_styling(full_width = F)
|
Variable
|
Sensitivity
|
Specificity
|
Accuracy
|
|
predClass_05
|
0.03
|
0.99
|
0.35
|
|
predClass_17
|
0.21
|
0.91
|
0.44
|
The 5% threshold correctly predicts a lower number of new development
areas (Sensitivity), but incorrectly predicts a higher number of no
change areas (Specificity). As there are far more no change areas in the
data, this is reflected in a lower overall accuracy. Conversely, the 17%
threshold has a lower higher rate and a slightly lower Specificity rate.
Again, because the dataset is majority no change areas, this leads to a
higher Accuracy rate.
Given the use case, and the spatial distribution of land cover
change, it may be more useful to have a model that predicts generally
where new development occurs rather than one that predicts precisely
where. As illustrated below, the 17% threshold provides this outcome.
These trade-offs can be visualized in the plot below. Here the model is
used to predict for the entire dat dataset. Which threshold
looks more reasonable given the distribution of observed development
change?
Note that these indicators are converted as.factor so
they can be mapped with scale_color_manual.
predsForMap <-
dat %>%
mutate(probs = predict(Model6, dat, type="response") ,
Threshold_5_Pct = as.factor(ifelse(probs >= 0.05 ,1,0)),
Threshold_17_Pct = as.factor(ifelse(probs >= 0.17 ,1,0))) %>%
dplyr::select(Dev_change,Threshold_5_Pct,Threshold_17_Pct) %>%
gather(Variable,Value, -geometry) %>%
st_cast("POLYGON")
ggplot() +
geom_point(data=predsForMap, aes(x=xyC(predsForMap)[,1], y=xyC(predsForMap)[,2], colour=Value)) +
facet_wrap(~Variable) +
scale_colour_manual(values = palette2, labels=c("No Change","New Development"),
name="") +
labs(title="Development Predictions - Low Threshold") +
geom_sf(data=ThreeCountyArea, fill = "transparent") +
mapTheme

To provide a bit more insight, the code block below produces both
true positives (Sensitivity) and true negatives (Specificity) for each
grid cell by threshold type. Notice how the spatial pattern of
Sensitivity for both thresholds is relatively consistent, but the 5%
threshold misses most the study area with respect to Specificity.
ConfusionMatrix.metrics <-
dat %>%
mutate(probs = predict(Model6, dat, type="response") ,
Threshold_5_Pct = as.factor(ifelse(probs >= 0.05 ,1,0)),
Threshold_17_Pct = as.factor(ifelse(probs >= 0.17 ,1,0))) %>%
mutate(TrueP_05 = ifelse(Dev_change == 1 & Threshold_5_Pct == 1, 1,0),
TrueN_05 = ifelse(Dev_change == 0 & Threshold_5_Pct == 0, 1,0),
TrueP_17 = ifelse(Dev_change == 1 & Threshold_17_Pct == 1, 1,0),
TrueN_17 = ifelse(Dev_change == 0 & Threshold_17_Pct == 0, 1,0)) %>%
dplyr::select(., starts_with("True")) %>%
gather(Variable, Value, -geometry) %>%
st_cast("POLYGON")
ggplot(data=ConfusionMatrix.metrics) +
geom_point(aes(x=xyC(ConfusionMatrix.metrics)[,1],
y=xyC(ConfusionMatrix.metrics)[,2], colour = as.factor(Value))) +
facet_wrap(~Variable) +
scale_colour_manual(values = palette2, labels=c("Correct","Incorrect"),
name="") +
labs(title="Development Predictions - Low Threshold") +
geom_sf(data=ThreeCountyArea, fill = "transparent") +
mapTheme

4.4 Generalizability
For this use case, it matters little whether the model generalizes
well across random holdouts. Thus, regular cross-validation is
substituted for spatial cross-validation. The latter is explicitly
concerned with generalizability across space. The approach helps us
understand whether our model is comparable to each county in the study
area despite any possible differences in land use or land use
planning.
To test across-space generalizability, spatialCV
function is run, which iteratively loops through dat having
each county take a turn as the hold out test set. This is also called
‘Leave-one-group-out cross validation.’. A model is estimated for the n
- 1 counties that remain and used to predict for the hold
out county.
spatialCV <- function(dataFrame, uniqueID, dependentVariable, modelName) {
#initialize a data frame
endList <- list()
#create a list that is all the spatial group unqiue ids in the data frame (ie counties)
uniqueID_List <- unique(dataFrame[[uniqueID]])
x <- 1
y <- length(uniqueID_List)
#create a counter and while it is less than the number of counties...
while(x <= y)
{
#call a current county
currentUniqueID <- uniqueID_List[x]
#create a training set comprised of units not in that county and a test set of units
#that are that county
training <- dataFrame[ which(dataFrame[[uniqueID]] != uniqueID_List[x]),]
testing <- dataFrame[ which(dataFrame[[uniqueID]] == uniqueID_List[x]),]
#create seperate xy vectors
trainingX <- training[ , -which(names(training) %in% c(dependentVariable))]
testingX <- testing[ , -which(names(testing) %in% c(dependentVariable))]
trainY <- training[[dependentVariable]]
testY <- testing[[dependentVariable]]
#Calculate predictions on the test county as part of a data frame including the observed
#outcome and the unique county ID
thisPrediction <-
data.frame(class = testY,
probs = predict(modelName, testingX, type="response"),
county = currentUniqueID)
#Row bind the predictions to a data farme
endList <- rbind(endList, thisPrediction)
#iterate counter
x <- x + 1
}
#return the final list of counties and associated predictions
return (as.data.frame(endList))
}
Now the function is run; a 17% predicted probability threshhold is
set and a facetted ROC plot for each county is created.
spatialCV_counties <-
spatialCV(dat,"COUNTY_NAM","Dev_change", Model6) %>%
mutate(predClass = as.factor(ifelse(probs >= 0.17 ,1,0)))
To investigate the across-3county-area generalizability of the model,
the code block below produces and maps confusion matrix statistics by
county. It is important to ensure as above, that the
yardstick.event_first option is set.
Some interesting patterns emerge. First, results for those counties
with little Observed_Change are not meaningful. In this
case, all three counties have some “Observed_Change”. In places with
substantial new development, Sensitivity rates are comparable with the
results from the test set results on the entire study area. In DuPage
County, Sensitivity is 0.00, implying that the model didn’t correctly
identify any of the observed changes in DUPAGE county. This suggests the
model may struggle with predicting positive cases in this county, but
this is likely because there is little development change occurring in
general. Specificity is high across the board, because there are a lot
of existing developed areas and the model predicts an abundance of
developed areas, where there was no change during the specified time
period. Again, this is less of a concern because these estimates will be
offset by sensitive land cover in Section 7 below. Will County has the
highest Sensitivity level and the highest Accuracy rate of the three
counties. Overall, these confusion matrix metrics help us to understand
how well the model is performing in terms of the true positive and true
negative rates, and they suggest the model is generalizable to those
counties that underwent significant development change like Will
County.
spatialCV_metrics <-
spatialCV_counties %>%
group_by(county) %>%
summarize(Observed_Change = sum(as.numeric(as.character(class))),
Sensitivity = round(yardstick::sens_vec(class,predClass),2),
Specificity = round(yardstick::spec_vec(class,predClass),2),
Accuracy = round(yardstick::accuracy_vec(class,predClass),2))
spatialCV_metrics %>%
kable() %>%
kable_styling(full_width = F)
|
county
|
Observed_Change
|
Sensitivity
|
Specificity
|
Accuracy
|
|
COOK
|
346
|
0.19
|
0.90
|
0.39
|
|
DUPAGE
|
124
|
0.00
|
1.00
|
0.30
|
|
WILL
|
456
|
0.29
|
0.93
|
0.54
|
5. Predicting Land Cover Demand for 2031
At this point, a simple but useful model has been trained to predict
urban development between 2011 and 2021 as a function of baseline
features from 2011 including land cover, built environment and
population. Next, we are going to updated our features to reflect a 2021
baseline. Having done so, predictions from our new model would then be
fore 2031.
Generalizability is always the concern when forecasting, and for this
use case Planners must ask themselves whether the 2011-2021 3 IL County
experience generalizes to the 2021-2031 3 IL County experience. In other
words, have the macroeconomic real estate conditions changed
dramatically between the two time periods? This is question with no
definitive answer, but it useful to consider the exogenous factors that
may differentiate today’s Chicago area from that of 2011. The big for
instance is climate change. If the real estate market capitalized flood
risk into devastated areas going forward, this would effectively change
the nature of real estate demand in the region. Thus the pre-flood, 2011
experienced is no longer entirely relevant.
This would not completely invalidate the model if these changes are
marginal, as development demand predictions can be adjusted in Section 7
below. However, consider the usefulness of this approach for a lakefront
city that loses say 10% of its developable land to sea level rise in the
following decade.
For brevity, we only update two features in our model. First,
population change (pop_change) is updated using county
level population projections visualized in the plot below. The second is
lagDevelopment, which describes how predicted new
development relates in space to old development.
Once the features are updated, 2031 predictions are estimated and
mapped.
Below, lagDevelopment is mutate describing average
distance to 2021 development. Note that the field name,
lagDevelopment is unchanged (ie. not updated to
lagDevelopment_2021). This is done purposefully as model6
has a regression coefficient called lagDevelopment. If this
variable wasn’t present in our updated data frame then the
predict command would fail.
dat <-
dat %>%
mutate(lagDevelopment = nn_function(xyC(.), xyC(filter(.,developed21 == 1)),2))
Now to update population change. A new data frame,
Pop_2031 is created which includes 2021 population counts
and 2031 projections for each county in the study area. Population is
plotted by year and by county. Chicago’s Cook County is projected to see
the greatest population gains by far. Anecdotally, we know that much of
Cook County is already developed, which suggests its development
scenario will involve more ‘infill’ development then sprawl.
ThreeCounties_Pop_2021 <- get_acs(geography = "county",
variables = "B01003_001",
year = 2021,
state = "IL",
county = counties,
geometry = TRUE) %>%
mutate(County = gsub(" County, Illinois", "", NAME)) %>%
rename(CountyPop_2021 = estimate) %>%
st_transform(st_crs(ThreeCounty_fishnet))
##
|
| | 0%
|
|======= | 10%
|
|======= | 11%
|
|======== | 11%
|
|======== | 12%
|
|========= | 12%
|
|========= | 13%
|
|========== | 14%
|
|=========== | 15%
|
|============ | 17%
|
|============= | 18%
|
|============= | 19%
|
|============== | 19%
|
|============== | 20%
|
|=============== | 21%
|
|=============== | 22%
|
|================ | 22%
|
|================ | 23%
|
|================ | 24%
|
|================= | 24%
|
|================= | 25%
|
|================== | 26%
|
|=================== | 27%
|
|=================== | 28%
|
|==================== | 28%
|
|==================== | 29%
|
|===================== | 30%
|
|====================== | 31%
|
|====================== | 32%
|
|======================= | 32%
|
|======================= | 33%
|
|======================== | 34%
|
|======================== | 35%
|
|========================= | 35%
|
|========================= | 36%
|
|========================== | 37%
|
|=========================== | 38%
|
|============================ | 39%
|
|============================ | 40%
|
|============================ | 41%
|
|============================= | 41%
|
|============================= | 42%
|
|============================== | 43%
|
|=============================== | 44%
|
|=============================== | 45%
|
|================================ | 45%
|
|================================ | 46%
|
|================================= | 47%
|
|================================== | 48%
|
|================================== | 49%
|
|=================================== | 49%
|
|=================================== | 50%
|
|==================================== | 51%
|
|==================================== | 52%
|
|===================================== | 52%
|
|===================================== | 53%
|
|======================================= | 55%
|
|======================================== | 57%
|
|======================================== | 58%
|
|========================================= | 58%
|
|========================================= | 59%
|
|========================================== | 59%
|
|========================================== | 60%
|
|=========================================== | 61%
|
|=========================================== | 62%
|
|============================================ | 62%
|
|============================================ | 63%
|
|============================================= | 64%
|
|============================================= | 65%
|
|============================================== | 66%
|
|=============================================== | 67%
|
|================================================ | 68%
|
|================================================= | 70%
|
|================================================== | 71%
|
|=================================================== | 72%
|
|=================================================== | 73%
|
|==================================================== | 74%
|
|==================================================== | 75%
|
|===================================================== | 76%
|
|====================================================== | 77%
|
|======================================================= | 78%
|
|======================================================= | 79%
|
|======================================================== | 79%
|
|======================================================== | 80%
|
|======================================================== | 81%
|
|========================================================= | 81%
|
|========================================================= | 82%
|
|========================================================== | 82%
|
|========================================================== | 83%
|
|========================================================== | 84%
|
|=========================================================== | 84%
|
|=========================================================== | 85%
|
|============================================================ | 85%
|
|============================================================ | 86%
|
|============================================================= | 87%
|
|============================================================= | 88%
|
|============================================================== | 89%
|
|=============================================================== | 90%
|
|================================================================ | 92%
|
|================================================================= | 93%
|
|================================================================== | 94%
|
|=================================================================== | 95%
|
|=================================================================== | 96%
|
|==================================================================== | 97%
|
|==================================================================== | 98%
|
|===================================================================== | 99%
|
|======================================================================| 99%
|
|======================================================================| 100%
# Calculate the sum of CountyPop_2021 for all counties
all_counties_pop_2021 <- ThreeCounties_Pop_2021 %>%
summarize(AllCounties_Pop2021 = sum(CountyPop_2021))
# Add the sum to ThreeCounties_Pop_2021 as a new column
ThreeCounties_Pop_2021 <- ThreeCounties_Pop_2021 %>%
mutate(AllCounties_Pop2021 = all_counties_pop_2021$AllCounties_Pop2021)
# Need to include the population projections for 2031 for the three counties.
#these are population in 2020 from IL Department of Health linked here: https://dph.illinois.gov/content/dam/soi/en/web/idph/files/publications/population-projections-report-2010-2030.pdf
# Code for joining and plotting 2021 population and 2031 population projection:
# Perform the left join
Pop_2031 <-
data.frame(
COUNTY_NAM = c("Cook", "DuPage", "Will"),
pop_2021 = c(5265398, 934094, 696403),
pop_2031 = c(4689134, 946910, 902476)
)%>%
mutate(Counties = toupper(COUNTY_NAM))
# Left join with ThreeCounties_Pop_2021
Pop_2031 <- left_join(
Pop_2031,
ThreeCounties_Pop_2021 %>%
dplyr::select(County, CountyPop_2021, AllCounties_Pop2021) %>%
st_set_geometry(NULL),
by = c("COUNTY_NAM" = "County")
)
# Plotting 2021 and 2031 populations side by side
Pop_2031 %>%
pivot_longer(cols = starts_with("pop_"), names_to = "Year", values_to = "Population") %>%
ggplot(aes(x = reorder(COUNTY_NAM, -Population), y = Population, fill = Year)) +
geom_bar(stat = "identity", position = position_dodge(width = 0.9), color = "black") +
scale_fill_manual(values = palette2, labels = c("2021", "2031"), name = "Year") +
labs(title = "Population Change by County: 2021 - 2031",
x = "County", y = "Population") +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
plotTheme

Interestingly enough, Cook County is projected to have a decline in
population from 2021 to 2031. This is largely due to outmigration in
recent years with thousands of residents leaving the county to live
elsewhere. The rise of remote work schedules and the costs of housing
have driven people to move to new areas outside of the county, often in
other states. For Will County, the population is projected to increase
for 2031, likely in the areas closer to the Lake Michigan shoreline and
transit routes. Population is projected to largely remain the same for
DuPage County when comparing 2021 to 2031.
5.2. Predicting Development Demand
Next, the Pop_2031 table is joined to dat
and pop_change in order to ‘distribute’ the new population
across the study area. To do so, the the allocation of new population is
weighted by a grid cell’s existing population
(pop_2031.infill). 2010 population is subtracted from this
figure to get pop_Change. Finally, Model6 is
used to predict for 2020 given the updated population change and lag
development features.
The map of predicted probabilities that results is best thought of as
a measure of predicted development demand in 2031.
dat_infill <-
dat %>%
#calculate population change
left_join(Pop_2031, by = c("COUNTY_NAM" = "Counties")) %>%
mutate(proportion_of_county_pop = pop_2021 / AllCounties_Pop2021,
pop_2031.infill = proportion_of_county_pop * pop_2031,
pop_Change = round(pop_2031.infill - pop_2021),2) %>%
dplyr::select(-pop_2031, -AllCounties_Pop2021,
-proportion_of_county_pop, -pop_2031.infill) %>%
#predict for 2031
mutate(predict_2031.infill = predict(Model6,. , type="response"))
dat_infill %>%
ggplot() +
geom_point(aes(x=xyC(dat_infill)[,1], y=xyC(dat_infill)[,2], colour = factor(ntile(predict_2031.infill,5)))) +
scale_colour_manual(values = palette5,
labels=substr(quintileBreaks(dat_infill,"predict_2031.infill"),1,4),
name="Quintile\nBreaks") +
geom_sf(data=ThreeCountyArea, fill=NA, colour="black", size=1) +
labs(title= "Development Demand in 2031: Predicted Probabilities") +
mapTheme

There are higher probabilities for development demand in Will County,
in the south portion of the study area.
6. Comparing Predicted Development Demand & Environmental
Sensitivity
We now have a really strong indicator of development demand for 2031
to help guide local land use planning. Demand however, is only one side
of the equation. It must balanced with the supply of environmentally
sensitive land. Understanding the interplay between demand and supply is
the first stage of the ‘Allocation’ phase, where Planners ultimately
decide which land should be developed and which should not.
For this analysis farmland and undeveloped land are be deemed
Suitable, while environmentally sensitive areas like
wetlands and forest are be deemed Not Suitable. Below, 2021
land cover data is read in and several measures of environmental
sensitivity are created by county. These include:
- The total amount of wetlands and forest land cover area in
2021.
- The amount of sensitive land (wetland and forest) lost between 2011
and 2021.
- The total area of large sensitive landscape ‘patches’ in 2021.
The third metric warrants some further discussion. In the context of
leapfrog development, Section 2.6 discusses the concept of landscape
fragmentation - the idea that discontinuous development across space
carves out disjointed slivers of wilderness. This fragmentation reduces
biodiversity particularly for species that need room to roam. Below,
environmentally sensitive_regions are created to represent
large areas of unfragmented natural resources. We then consider the
total area of these clumps for each county.
6.2. 2021 Land Cover Data
To begin, the 2021 Land Cover data is read in and reclassified.
# We already did this. It's called ThreeCountyLC_2021
developed21 <- ThreeCountyLC_2021 == 21 | ThreeCountyLC_2021 == 22 | ThreeCountyLC_2021 == 23 | ThreeCountyLC_2021 == 24
forest21 <- ThreeCountyLC_2021 == 41 | ThreeCountyLC_2021 == 42 | ThreeCountyLC_2021 == 43
farm21 <- ThreeCountyLC_2021 == 81 | ThreeCountyLC_2021 == 82
wetlands21 <- ThreeCountyLC_2021 == 90 | ThreeCountyLC_2021 == 95
otherUndeveloped21 <- ThreeCountyLC_2021 == 52 | ThreeCountyLC_2021 == 71 | ThreeCountyLC_2021 == 31
water21 <- ThreeCountyLC_2021 == 11
names(developed21) <- "dev21"
names(forest21) <- "forest21"
names(farm21) <- "farm21"
names(wetlands21) <- "wetlands21"
names(otherUndeveloped21) <- "otherUndeveloped21"
names(water21) <- "water21"
This next step takes too long to plot because the rasters are large.
But the code is there just in case.
# ggplot() +
# geom_sf(data=ThreeCountyArea) +
# geom_raster(data = rbind(rast(ThreeCountyLC_2011) %>% mutate(label = "2011"),
# rast(ThreeCountyLC_2021) %>% mutate(label = "2021")) %>%
# na.omit %>% filter(value > 0),
# aes(x,y,fill=as.factor(value))) +
# facet_wrap(~label) +
# scale_fill_viridis(discrete=TRUE, name ="") +
# labs(title = "Land Cover, 2011 & 2021") +
# mapTheme #+ theme(legend.position = "none")
Next, each raster is aggregated to the fishnet using the
aggregateRaster function and 2021 land cover types are
mapped.
theRasterList21 <- c(developed21,forest21,farm21,wetlands21,otherUndeveloped21,water21)
dat2 <-
aggregateRaster(theRasterList21, dat) %>%
dplyr::select(dev21,forest21,farm21,wetlands21,otherUndeveloped21,water21) %>%
st_set_geometry(NULL) %>%
bind_cols(.,dat) %>%
st_sf() %>%
st_cast("POLYGON")
dat2 %>%
gather(var,value,dev21:water21) %>%
st_centroid() %>%
mutate(X = st_coordinates(.)[,1],
Y = st_coordinates(.)[,2]) %>%
ggplot() +
geom_sf(data=ThreeCountyArea) +
geom_point(aes(X,Y, colour=as.factor(value))) +
facet_wrap(~var) +
scale_colour_manual(values = palette2,
labels=c("Other","Land Cover"),
name = "") +
labs(title = "Land Cover Types, 2021",
subtitle = "As fishnet centroids") +
mapTheme

Note that there are wetlands and forest sprinkled throughout the
three county area, and there is a lot of farmland concentrated in the
southwest portion in Will County.
6.3. Sensitive Land Cover Lost
Below an indicator sensitive_lost is created indicating
grid cells that were either forest or wetlands in 2011 but were no
longer so in 2021. The output layer, sensitive_land_lost,
gives a sense for how development in the recent past has effected the
natural environment.
dat2 <-
dat2 %>%
mutate(sensitive_lost21 = ifelse(forest == 1 & forest21 == 0 |
wetlands == 1 & wetlands21 == 0,1,0))
ggplot() +
geom_point(data=dat2, aes(x=xyC(dat2)[,1], y=xyC(dat2)[,2], colour=as.factor(sensitive_lost21))) +
scale_colour_manual(values = palette2,
labels=c("No Change","Sensitive Lost"),
name = "") +
labs(title = "Sensitive lands lost: 2011 - 2021",
subtitle = "As fishnet centroids") +
geom_sf(data=ThreeCountyArea, fill = "transparent") +
mapTheme

This type of analysis is helpful to see where sensitive lands are
being lost throughout the three county area due to development.
Thankfully, there are only a small number of these lost areas from
2011-2021. If there were large areas lost, it would be helpful for
planners to implement iniatives to protect those sensitive areas from
development encroachment and allow them to continue providing
environmental benefits.
6.4 Landscape Fragmentation
In this section, the wetlands21 and
forest21 rasters are converted to contiguous
sensitive_regions using the raster::clump
function. This is equivalent to Region Group in ArcGIS. The raster
clumps are then converted to vector sf layers; dissolved
into unique regions; Acres are calculated; and the layers are converted
back to raster to be extracted back to the fishnet with
aggregateRaster. It is worth going through this code block
line by line. Note that only sensitive_regions with areas
greater than 1 acre are included.
We ended up not including this section in our analysis.
# emptyRaster <- Development_change
# emptyRaster[] <- NA
#
# sensitiveRegions <-
# raster::clump(wetlands21 + forest21) %>%
# rasterToPolygons() %>%
# st_as_sf() %>%
# group_by(clumps) %>%
# summarize() %>%
# mutate(Acres = as.numeric(st_area(.) * 0.0000229568)) %>%
# filter(Acres > 3954) %>%
# dplyr::select() %>%
# raster::rasterize(.,emptyRaster)
# sensitiveRegions[sensitiveRegions > 0] <- 1
# names(sensitiveRegions) <- "sensitiveRegions"
#
# dat2 <-
# aggregateRaster(c(sensitiveRegions), dat2) %>%
# dplyr::select(sensitiveRegions) %>%
# st_set_geometry(NULL) %>%
# bind_cols(.,dat2) %>%
# st_sf()
#
# ggplot() +
# geom_point(data=dat2, aes(x=xyC(dat2)[,1], y=xyC(dat2)[,2], colour=as.factor(sensitiveRegions))) +
# scale_colour_manual(values = palette2,
# labels=c("Other","Sensitive Regions"),
# name="") +
# labs(title = "Sensitive regions",
# subtitle = "Continous areas of either wetlands or forests\ngreater than 1 acre") +
# mapTheme
6.5. Summarize by County
The below dplyr statement takes as its input,
dat2, which was created in Sections 6.2 - 6.4 and wrangles
together a table of county-level, supply and demand metrics which can be
used to analyze suitability by county.
county_specific_metrics <-
dat2 %>%
#predict development demand from our model
mutate(Development_Demand = predict(Model6, dat2, type="response")) %>%
#get a count count of grid cells by county which we can use to calculate rates below
left_join(st_set_geometry(dat, NULL) %>% group_by(COUNTY_NAM) %>% summarize(count = n())) %>%
#calculate summary statistics by county
group_by(COUNTY_NAM) %>%
summarize(Total_Farmland = sum(farm21) / max(count),
Total_Forest = sum(forest21) / max(count),
Total_Wetlands = sum(wetlands21) / max(count),
Total_Undeveloped = sum(otherUndeveloped21) / max(count),
Sensitive_Land_Lost = sum(sensitive_lost21) / max(count),
#Sensitive_Regions = sum(sensitiveRegions) / max(count),
Mean_Development_Demand = mean(Development_Demand)) %>%
#get population data by county
left_join(Pop_2031, by = c("COUNTY_NAM" = "Counties")) %>%
mutate(Population_Change = pop_2031 - pop_2021,
Population_Change_Rate = Population_Change / pop_2031) %>%
dplyr::select(COUNTY_NAM,Total_Farmland, Total_Forest, Total_Wetlands, Total_Undeveloped, Sensitive_Land_Lost, Mean_Development_Demand, Population_Change_Rate)
Now a small multiple plot can be created providing both supply and
demand side analytics by county. The plot gives a sense for development
demand (Demand-Side), suitable land for development
(Suitable) and sensitive land
(Not Suitable).
In Will County, south of Chicago, the data suggests both population
and development demand will increase. At the same time, there is a high
rate of developable farmland. Will County has a large amount of forest
and wetlands areas, but otherwise it is well suitable to new
development.
Conversely, DuPage County, the county east of Chicago and Cook
County, has a significant amount of forest and wetland areas like Will
County, but it has a slightly higher amount of sensitive lands lost.
Comparatively, it has a lot less undeveloped land and farmland that is
suitable for new development. There are very low rates of mean
development demand and population growth.
Cook County has the lowest amounts of sensitive lands but also low
amounts of undeveloped areas and farmland as well. This is because
Chicago and most of the county has already been developed. And the
population is expected to continue to decline due to outmigration.
In these counties, there are some real trade-offs to be made between
suitable/sensitive land and development pressure.
county_specific_metrics %>%
gather(Variable, Value, -COUNTY_NAM, -geometry) %>%
mutate(Variable = factor(Variable, levels=c("Population_Change_Rate","Mean_Development_Demand",
"Total_Farmland","Total_Undeveloped","Total_Forest",
"Total_Wetlands","Sensitive_Land_Lost",
ordered = TRUE))) %>%
mutate(Planning_Designation = case_when(
Variable == "Population_Change_Rate" | Variable == "Mean_Development_Demand" ~ "Demand-Side",
Variable == "Total_Farmland" | Variable == "Total_Undeveloped" ~ "Suitable",
TRUE ~ "Not Suitable")) %>%
ggplot(aes(x=Variable, y=Value, fill=Planning_Designation)) +
geom_bar(stat="identity", position=position_dodge(), colour="black") +
facet_wrap(~COUNTY_NAM, ncol=5) +
coord_flip() +
scale_y_continuous(breaks = seq(-.25, 1, by = .25)) +
geom_vline(xintercept = 2.5) + geom_vline(xintercept = 4.5) +
scale_fill_manual(values=c("black","red","darkgreen")) +
labs(title= "County Specific Allocation Metrics", subtitle= "As rates", x="Indicator", y="Rate") +
plotTheme + theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position="bottom")

7. Allocation
Allocation is the final stage of the urban growth modeling process.
Now that both demand and supply is understood, Planners can allocate
development rights accordingly. Of course, this could take many forms of
regulation including zoning, subdivision approval or outright
conservation. In this section, demand and supply are visualized for Will
County.
First, development demand is predicted for Will County. Then a layer,
WillCounty_landUse is created, that includes indicators for
both previously developed land and environmentally unsuitable land. This
layer then is overlayed atop development demand and projected population
change to give the full supply and demand-side picture in Will
County.
There are some clear opportunities for development in Will County.
Significant infill opportunities exist along the southern boundary where
population change is projected to be greatest. There is also a good deal
of environmentally suitable land in the center of the county and closer
to the Lake Michigan shoreline to the east. This would be ideal space
for large housing or commercial developments. Will County has a lot of
forests and wetlands in the southwest corner of the county, so there are
sensitive land areas not suitable for development there.
WillCounty <- dat2 %>%
mutate(Development_Demand = predict(Model6, dat2, type = "response")) %>%
filter(COUNTY_NAM == "WILL")
WillCounty_landUse <- rbind(
filter(WillCounty, forest21 == 1 | wetlands21 == 1) %>%
dplyr::select() %>%
mutate(Land_Use = "Not Suitable"),
filter(WillCounty, dev21 == 1) %>%
dplyr::select() %>%
mutate(Land_Use = "Developed"))
# Calculate quantiles for Development Demand and Population Change
dev_demand_quantiles <- quantile(WillCounty$Development_Demand, probs = seq(0, 1, by = 0.25)) # Changed to 4 quantiles
pop_change_quantiles <- quantile(WillCounty$pop_Change, probs = seq(0, 1, by = 0.25)) # Calculate quantiles for pop_Change
grid.arrange(
ggplot() +
geom_sf(data = WillCounty, aes(fill = cut(Development_Demand, breaks = dev_demand_quantiles)), colour = NA) +
geom_point(data = WillCounty_landUse, aes(x = xyC(WillCounty_landUse)[, 1],
y = xyC(WillCounty_landUse)[, 2],
colour = Land_Use),
shape = 15, size = 2) +
geom_sf(data = st_intersection(ThreeCounties_Highways, filter(ThreeCountyArea, COUNTY_NAM == "WILL")), size = 2, colour = "gray") +
scale_fill_manual(values = palette5, name = "Development Demand",
labels = as.character(round(dev_demand_quantiles, digits = 2))) +
scale_colour_manual(values = c("black", "red")) +
labs(title = "Development Potential, 2031: Will County") + mapTheme +
guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)),
ggplot() +
geom_sf(data = WillCounty, aes(fill = cut(pop_Change, breaks = pop_change_quantiles)), colour = NA) +
geom_point(data = WillCounty_landUse, aes(x = xyC(WillCounty_landUse)[, 1],
y = xyC(WillCounty_landUse)[, 2],
colour = Land_Use),
shape = 15, size = 2) +
geom_sf(data = st_intersection(ThreeCounties_Highways, filter(ThreeCountyArea, COUNTY_NAM == "WILL")), size = 2, colour = "gray") +
scale_fill_manual(values = palette6, name = "Population Change",
labels = as.character(round(pop_change_quantiles, digits = 2))) +
scale_colour_manual(values = c("black", "red")) +
labs(title = "Projected Population, 2031: Will County") + mapTheme +
guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)), ncol = 2)

The plots above are created using a ggplot trick to show
what appears to be overlayed polygons (fishnet grid cells). ggplot does
not natively allow multiple aesthetics (aes) of the same
style. In practice, this means it is not possible to have two
scale_fill_manual parameters and thus, two legends for the
same map. This limitation is cleverly avoided by plotting
WillCounty_landUse as colored points (as opposed to filled
grid cells). In the geom_point parameter above, the points
are set to shape = 15, which is a filled box. This box can
then be sized to make it appear like a fishnet grid cell.
We stop short in actually allocating land to development. While the
model is well suited for understanding sprawl-style development, it is
not useful for understanding how new demand might be absorbed by
upzoning and densification of existing development. It would not be wise
to allocate the entire projected population to undeveloped land.
Instead, we’d prefer a more nuanced understanding of how local land use
laws might play a role. At this stage in the analysis however, the
Planner has all she needs to engage local stakeholders about future
development decisions.
7.2 Scenario 2: Estimating the Effect of New Transportation
We created a new regional rail line to simulate the extension of the
Metra rail system in the Chicago region. The rail extension expands the
system southwest into Will County. You can see the existing system and
the extension plotted.
MetraLineswExtension <-
st_read("C:/Users/3lpaw/Desktop/ArcGIS Pro 3.2/EnvModeling/04_24_24_UrbanGrowthModeling/Downloaded_Data/Metra_Lines/MetraLineswExtension.shp") %>%
st_transform(st_crs(ThreeCountyArea)) %>%
st_intersection(ThreeCountyArea)
library(gridExtra)
# Adjust the widths parameter to control the size of each plot
grid.arrange(
ggplot() +
geom_sf(data=ThreeCountyArea, fill = "transparent") +
geom_sf(data=ThreeCounties_RegRail, color = "red") +
labs(title = "Existing Regional Rail Lines") +
mapTheme,
ggplot() +
geom_sf(data=ThreeCountyArea, fill = "transparent") +
geom_sf(data=MetraLineswExtension, color = "red") +
labs(title = "Regional Rail Lines with Extension") +
mapTheme,
# Adjust widths as needed to control the size of each plot
nrow = 1,
widths = c(2, 2)
)

Now we recreate the regional rail fishnet, using the shapefile that
has the new rail extension and we calculate the distance to nearest rail
line again.
#Determine nearest regional rail line to each centroid
nearest_metra <- st_nearest_feature(centroid,MetraLineswExtension)
#Calcuate distance from each grid square centroid to nearest regional rail line
ThreeCounty_fishnet$metra_dist <- as.double(st_distance(centroid, MetraLineswExtension[nearest_metra,], by_element=TRUE))
Metra_fishnet <- ThreeCounty_fishnet #%>%
# select(fishnetID, geometry, regrail_dist)
#Make a quick sample map of the results
ggplot()+
geom_sf(data=Metra_fishnet,aes(fill=metra_dist),color='transparent')+
scale_fill_viridis_c(name='Distance to Regional Rail Lines (feet)')+
geom_sf(data=MetraLineswExtension,color='red')+
labs(title = "Distance to Regional Rail Lines with Extension",
subtitle = "Using fishnet centroids") +
theme_void()

Next, we recreate the final dataset to include the new variable
(regional rail with extension) also called metra_dist here. The other
fishnets are combined here to include all of the previous variables.
dat3 <-
cbind(
fishnet, fishnetPopulation, Highway_fishnet, RegRail_fishnet, Metra_fishnet, Downtown_fishnet, LakeMichigan_fishnet, aggregatedRasters) %>%
dplyr::select(Dev_change, developed, forest, farm, wetlands, otherUndeveloped, water,
Pop_2011, Pop_2021, pop_Change, highway_dist, regrail_dist, metra_dist, downtown_dist, lake_dist, lagDevelopment) %>%
st_join(ThreeCountyArea) %>%
mutate(developed21 = ifelse(Dev_change == 1 & developed == 1, 0, developed)) %>%
filter(water == 0)
Exploratory analysis:
In this section we explore the extent to which each features is
associated with development change. If the goal was to predict a
continuous variable, scatterplots and correlation coefficients make this
process straightforward and relatively easy to explain to a
non-technical decison maker.
In this case however, the dependent variable is a binary outcome -
either a grid cell was developed between 2011 and 2021 or it wasn’t. In
this case, the relevant question is whether for a given feature, there
is a statistically significant difference between areas that changed and
areas that did not. These differences are explored in a set of plots
below. For models with lots of features, these plots could be compliment
by a series of difference in means statistical tests.
The below code block selects the distance and spatial
lag features, converts each to long form and plots each as bar plots.
Note that geom_bar calculates the mean.
dat3 %>%
dplyr::select(pop_Change, highway_dist, regrail_dist, metra_dist, downtown_dist, lake_dist, lagDevelopment, Dev_change) %>%
gather(Variable, Value, -Dev_change, -geometry) %>%
ggplot(., aes(Dev_change, Value, fill=Dev_change)) +
geom_bar(position = "dodge", stat = "summary", fun.y = "mean") +
facet_wrap(~Variable) +
scale_fill_manual(values = palette2,
labels=c("No Change","New Development"),
name="") +
labs(title="New Development as a Function of the Continuous Variables") +
plotTheme

The plot for metra_dist (distance to nearest regional rail line,
including the new extension) does not look that different from the plot
for regrail_dist (includes only existing regional rail lines). However,
there is a slight difference between no change and new development for
the metra_dist variable.
7.2.a Modeling
First, dat3 is split into training and test sets.
set.seed(3456)
trainIndex3 <-
createDataPartition(dat3$developed, p = .50,
list = FALSE,
times = 1)
dat3Train <- dat3[ trainIndex3,]
dat3Test <- dat3[-trainIndex3,]
nrow(dat3)
## [1] 2808
Model7 includes distance to the highways and all other
variables including the new rail extension, and is the final model
employed for prediction. I removed the regrail_dist because it is the
existing rail lines without the new extension.
Model1 <- glm(Dev_change ~ wetlands + forest + farm + otherUndeveloped,
family="binomial"(link="logit"), data = datTrain)
Model2 <- glm(Dev_change ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment,
family="binomial"(link="logit"), data = datTrain)
Model3 <- glm(Dev_change ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment + Pop_2011,
family="binomial"(link="logit"), data = datTrain)
Model4 <- glm(Dev_change ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment + Pop_2011 +
Pop_2021,
family="binomial"(link="logit"), data = datTrain)
Model5 <- glm(Dev_change ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment + pop_Change,
family="binomial"(link="logit"), data = datTrain)
Model6 <- glm(Dev_change ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment + pop_Change +
highway_dist + downtown_dist + lake_dist + regrail_dist,
family="binomial"(link="logit"), data = datTrain)
Model7 <- glm(Dev_change ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment + pop_Change +
highway_dist + downtown_dist + lake_dist + metra_dist,
family="binomial"(link="logit"), data = dat3Train)
Comparing models:
modelList <- paste0("Model", 1:7)
map_dfc(modelList, function(x)pR2(get(x)))[4,] %>%
setNames(paste0("Model",1:7)) %>%
gather(Model,McFadden) %>%
ggplot(aes(Model,McFadden)) +
geom_bar(stat="identity") +
labs(title= "McFadden R-Squared by Model") +
plotTheme
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2

Next, a data frame is created that includes columns for the observed
development change, Dev_change, and one that includes
predicted probabilities for Model7. This data frame is then
used as an input to a density plot visualizing the distribution of
predicted probabilities by observed class. Only a small number of
predicted probabilities are greater than or equal to 50%
(nrow(filter(testSetProbs, probs >= .50)) / nrow(datTest)).
This makes good sense, given how rare of an event development is in our
dataset. Ultimately, in order to judge our model with a confusion
matrix, a smaller development classification threshold must be
employed.
testSetProbs2 <-
data.frame(class = dat3Test$Dev_change,
probs = predict(Model7, dat3Test, type="response"))
ggplot(testSetProbs2, aes(probs)) +
geom_density(aes(fill=class), alpha=0.5) +
scale_fill_manual(values = palette2,
labels=c("No Change","New Development")) +
labs(title = "Histogram of test set predicted probabilities",
x="Predicted Probabilities",y="Density") +
plotTheme

Calculating Accuracy for this model:
options(yardstick.event_first = FALSE)
testSetProbs2 <-
testSetProbs2 %>%
mutate(predClass_05 = as.factor(ifelse(testSetProbs2$probs >= 0.05 ,1,0)),
predClass_17 = as.factor(ifelse(testSetProbs2$probs >= 0.17 ,1,0)))
testSetProbs2 %>%
dplyr::select(-probs) %>%
gather(Variable, Value, -class) %>%
group_by(Variable) %>%
summarize(Sensitivity = round(yardstick::sens_vec(class,factor(Value)),2),
Specificity = round(yardstick::spec_vec(class,factor(Value)),2),
Accuracy = round(yardstick::accuracy_vec(class,factor(Value)),2)) %>%
kable() %>%
kable_styling(full_width = F)
|
Variable
|
Sensitivity
|
Specificity
|
Accuracy
|
|
predClass_05
|
0.03
|
0.99
|
0.35
|
|
predClass_17
|
0.24
|
0.91
|
0.46
|
The results are a bit similar to model 6, but the accuracy increased
by 0.01 for each variable.
predsForMap2 <-
dat3 %>%
mutate(probs = predict(Model7, dat3, type="response") ,
Threshold_5_Pct = as.factor(ifelse(probs >= 0.05 ,1,0)),
Threshold_17_Pct = as.factor(ifelse(probs >= 0.17 ,1,0))) %>%
dplyr::select(Dev_change,Threshold_5_Pct,Threshold_17_Pct) %>%
gather(Variable,Value, -geometry) %>%
st_cast("POLYGON")
ggplot() +
geom_point(data=predsForMap2, aes(x=xyC(predsForMap2)[,1], y=xyC(predsForMap2)[,2], colour=Value)) +
facet_wrap(~Variable) +
scale_colour_manual(values = palette2, labels=c("No Change","New Development"),
name="") +
labs(title="Development Predictions - Low Threshold") +
geom_sf(data=ThreeCountyArea, fill = "transparent") +
mapTheme

Predicting Development Demand
Next, the Pop_2031 table is joined to dat3
and pop_change in order to ‘distribute’ the new population
across the study area. To do so, the the allocation of new population is
weighted by a grid cell’s existing population
(pop_2031.infill). 2010 population is subtracted from this
figure to get pop_Change. Finally, Model7 is
used to predict for 2031 given the updated population change and lag
development features.
The map of predicted probabilities that results is best thought of as
a measure of predicted development demand in 2031.
dat3_infill <-
dat3 %>%
#calculate population change
left_join(Pop_2031, by = c("COUNTY_NAM" = "Counties")) %>%
mutate(proportion_of_county_pop = pop_2021 / AllCounties_Pop2021,
pop_2031.infill = proportion_of_county_pop * pop_2031,
pop_Change = round(pop_2031.infill - pop_2021),2) %>%
dplyr::select(-pop_2031, -AllCounties_Pop2021,
-proportion_of_county_pop, -pop_2031.infill) %>%
#predict for 2031
mutate(predict_2031.infill = predict(Model7,. , type="response"))
# Calculate quintile breaks
quintile_breaks <- quintileBreaks(dat3_infill, "predict_2031.infill")
# Sort the breaks in ascending order
sorted_breaks <- sort(quintile_breaks)
dat3_infill %>%
ggplot() +
geom_point(aes(x=xyC(dat3_infill)[,1], y=xyC(dat3_infill)[,2], colour = factor(ntile(predict_2031.infill,5)))) +
scale_colour_manual(values = palette5,
labels = substr(sorted_breaks, 1, 4),
name="Quintile\nBreaks") +
geom_sf(data=ThreeCountyArea, fill=NA, colour="black", size=1) +
labs(title= "Development Demand in 2031: Predicted Probabilities") +
mapTheme

Summarize by County
The below dplyr statement takes as its input,
dat3, which wrangles together a table of county-level,
supply and demand metrics which can be used to analyze suitability by
county.
Next, each raster is aggregated to the fishnet using the
aggregateRaster function and 2021 land cover types are
mapped.
theRasterList21 <- c(developed21,forest21,farm21,wetlands21,otherUndeveloped21,water21)
dat4 <-
aggregateRaster(theRasterList21, dat3) %>%
dplyr::select(dev21,forest21,farm21,wetlands21,otherUndeveloped21,water21) %>%
st_set_geometry(NULL) %>%
bind_cols(.,dat) %>%
st_sf() %>%
st_cast("POLYGON")
dat4 <- dat4 %>%
mutate(metra_dist = dat3$metra_dist)
dat4 %>%
gather(var,value,dev21:water21) %>%
st_centroid() %>%
mutate(X = st_coordinates(.)[,1],
Y = st_coordinates(.)[,2]) %>%
ggplot() +
geom_sf(data=ThreeCountyArea) +
geom_point(aes(X,Y, colour=as.factor(value))) +
facet_wrap(~var) +
scale_colour_manual(values = palette2,
labels=c("Other","Land Cover"),
name = "") +
labs(title = "Land Cover Types, 2021",
subtitle = "As fishnet centroids") +
mapTheme

Below an indicator sensitive_lost is created indicating
grid cells that were either forest or wetlands in 2011 but were no
longer so in 2021. The output layer, sensitive_land_lost,
gives a sense for how development in the recent past has effected the
natural environment.
(We are repeating these steps from before, but with the updated final
dataset (dat4).
dat4 <-
dat4 %>%
mutate(sensitive_lost21 = ifelse(forest == 1 & forest21 == 0 |
wetlands == 1 & wetlands21 == 0,1,0))
ggplot() +
geom_point(data=dat4, aes(x=xyC(dat4)[,1], y=xyC(dat4)[,2], colour=as.factor(sensitive_lost21))) +
scale_colour_manual(values = palette2,
labels=c("No Change","Sensitive Lost"),
name = "") +
labs(title = "Sensitive lands lost: 2011 - 2021",
subtitle = "As fishnet centroids") +
geom_sf(data=ThreeCountyArea, fill = "transparent") +
mapTheme

Next, we plot county-specific metrics.
county_specific_metrics_2 <-
dat4 %>%
#predict development demand from our model
mutate(Development_Demand = predict(Model7, dat4, type="response")) %>%
#get a count count of grid cells by county which we can use to calculate rates below
left_join(st_set_geometry(dat, NULL) %>% group_by(COUNTY_NAM) %>% summarize(count = n())) %>%
#calculate summary statistics by county
group_by(COUNTY_NAM) %>%
summarize(Total_Farmland = sum(farm21) / max(count),
Total_Forest = sum(forest21) / max(count),
Total_Wetlands = sum(wetlands21) / max(count),
Total_Undeveloped = sum(otherUndeveloped21) / max(count),
Sensitive_Land_Lost = sum(sensitive_lost21) / max(count),
#Sensitive_Regions = sum(sensitiveRegions) / max(count),
Mean_Development_Demand = mean(Development_Demand)) %>%
#get population data by county
left_join(Pop_2031, by = c("COUNTY_NAM" = "Counties")) %>%
mutate(Population_Change = pop_2031 - pop_2021,
Population_Change_Rate = Population_Change / pop_2031) %>%
dplyr::select(COUNTY_NAM,Total_Farmland, Total_Forest, Total_Wetlands, Total_Undeveloped, Sensitive_Land_Lost, Mean_Development_Demand, Population_Change_Rate)
Now a small multiple plot can be created providing both supply and
demand side analytics by county. The plot gives a sense for development
demand (Demand-Side), suitable land for development
(Suitable) and sensitive land
(Not Suitable).
This plot is similar to the results from the previous model, but now
we can see that development demand has increased for Will County.
Mean_Development_Demand increased slightly for Will County because we
used Model7 and the Metra line extension variable, compared to Model6
which used only existing Metra rail lines. Adding the regional rail line
extension into Will County would likely contribute to an increase in
development demand for areas near the transit route.
county_specific_metrics_2 %>%
gather(Variable, Value, -COUNTY_NAM, -geometry) %>%
mutate(Variable = factor(Variable, levels=c("Population_Change_Rate","Mean_Development_Demand",
"Total_Farmland","Total_Undeveloped","Total_Forest",
"Total_Wetlands","Sensitive_Land_Lost",
ordered = TRUE))) %>%
mutate(Planning_Designation = case_when(
Variable == "Population_Change_Rate" | Variable == "Mean_Development_Demand" ~ "Demand-Side",
Variable == "Total_Farmland" | Variable == "Total_Undeveloped" ~ "Suitable",
TRUE ~ "Not Suitable")) %>%
ggplot(aes(x=Variable, y=Value, fill=Planning_Designation)) +
geom_bar(stat="identity", position=position_dodge(), colour="black") +
facet_wrap(~COUNTY_NAM, ncol=5) +
coord_flip() +
scale_y_continuous(breaks = seq(-.25, 1, by = .25)) +
geom_vline(xintercept = 2.5) + geom_vline(xintercept = 4.5) +
scale_fill_manual(values=c("black","red","darkgreen")) +
labs(title= "County Specific Allocation Metrics", subtitle= "As rates", x="Indicator", y="Rate") +
plotTheme + theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position="bottom")

8. Appendix
This is new material describing how you can determine land cover
change from land cover rasters for two time periods
8.1. Calculating Land Cover Change
For your assignment, you are going to need to get land cover data for
two new time periods and figure out what areas developed in that
interval (and then model it). In the workflow above, we used a the NLCD
land cover change data set, but we could also have calculated our own
version using the 2001 and 2011 data sets. You could do this in R, or
ArcGIS for your assignment. Here is an abbreviated workflow for doing it
in R using the data from this exercise:
Reclassify 2001 and 2011 land cover databases to consist of 1 and 0
observations (e.g. 1 is the developed classes 13-24, 0 is everything
else).
# reclassMatrix <-
# matrix(c(
# 0,12,0,
# 12,24,1,
# 24,Inf,0),
# ncol=3, byrow=T)
# developed_2001 <-
# reclassify(lc_2001,reclassMatrix)
#
# developed_2011 <-
# reclassify(lc_2011,reclassMatrix)
Then do some map algebra to find the places where land cover changed.
Let’s see a quick histogram of the values - these should range from 0
(undeveloped in 2001, undeveloped in 2011), 1 (undeveloped in 2001,
developed in 2011 (presuming nothing went from developed to
undeveloped)), and 2 (developed in both periods). The 1’s represent the
change.
#
# development_change <- developed_2001+developed_2011
#
# hist(development_change)
We can subsequently turn any of the 0’s and 1’s to NA
# development_change[development_change != 1] <- NA
#
# ggplot() +
# geom_sf(data=houstonMSA) +
# geom_raster(data=rast(development_change) %>% na.omit,
# aes(x,y,fill=as.factor(value))) +
# scale_fill_viridis(discrete=TRUE, name ="Land Cover\nChange") +
# labs(title="Development land use change") +
# mapTheme
8.2. Downsampling Rasters
Notice that we used 4000x4000 unit grid cells in this analysis to
keep small grid cell sizes from crushing our laptops while we did this
plotting and geo-processing. This is very simple to do in R - the below
code takes our development_change raster and downsamples it
by a factor of two using the aggregate function. You could
load an original data set in at the beginning of your analysis and
downsample it before you get started.
#
# development_change
#
# aggregate(development_change, fact = 2)
8.3. Cropping Rasters
Say you have rasters that you would like to manipulate in R instead
of in ArcGIS. If you have data covering the study area, you can use this
as the extent to which you would like to crop the data, and then use
mask to clip the data to the exact boundaries.
First, read in the resampled raster of land cover around Atlanta,
Georgia from 2001 and then plot it.
# lc_atl_2001 <- raster("https://raw.githubusercontent.com/mafichman/CPLN_675/main/Week_14_15/data/atl_lc01_resamp_new.tif")
#
# plot(lc_atl_2001)
We will also read in Atlanta counties for the extent of the bounding
box, and then crop the raster to the counties.
# atl_counties <- st_read("https://raw.githubusercontent.com/mafichman/CPLN_675/main/Week_14_15/data/Counties_Atlanta_Region.geojson") %>% st_transform("ESRI:102667") # ESRI 1983 state plane GA west
#
# lc_atl_2001_crop <- crop(lc_atl_2001, extent(atl_counties))
#
# plot(lc_atl_2001_crop)
Then, mask the raster using the Atlanta are counties.
This is similar to the “clipping” process for vector data.
# lc_atl_2001_mask <- mask(lc_atl_2001_crop, atl_counties)
# plot(lc_atl_2001_mask)
8.4 Updated Census Data Calls
In section 2.4, census data was pulled using the
get_decennial; however, if you are using a different
timeframe, you you will use data from the American Community Survey
(ACS). You will replace get_decennial with
get_acs in your project’s workflow. The code chunk below
shows how to use get_acs to obtain population data from the
counties in the Houston MSA in 2019. Note that get_acs
returns both an estimate (denoted with an “E”) and a margin of error
(denoted with an “M”). We use the select command in
dplyr to only retain estimate version of the variable.
# # Specify which variable(s) you would like to grab. Here, only one (Total Population) is listed, but you could add more to the call.
# acs_vars <- c("B02001_001E")
#
# # Using "tract" as the geography and 2019 as the year, download data data for the Houston MSA counties listed.
# houstonPop19 <- get_acs(geography = "tract",
# variables = acs_vars,
# year = 2019,
# state = 48,
# geometry = TRUE,
# output = "wide",
# county=c("Harris COunty","San Jacinto","Montgomery","Liberty","Waller",
# "Austin","Chambers","Fort Bend","Brazoria","Galveston")) %>%
# rename(pop2019 = B02001_001E) %>%
# dplyr::select(-starts_with("B"))
#
# # Make sure to transform to the crs of the fishnet!
# houstonPop19 <- houstonPop19 %>%
# st_transform(st_crs(houstonMSA_fishnet))
LS0tDQp0aXRsZTogIkNQTE4gNjc1IC0gVXJiYW4gR3Jvd3RoIE1vZGVsaW5nIGZvciB0aGUgQ2hpY2FnbyBSZWdpb24sIDIwMjQiDQphdXRob3I6ICJMYXVyZW4gUGF3bG93c2tpIGFuZCBTb3BoaWUgTWFlcyINCmRhdGU6ICI1LzEwLzIwMjQiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSkNCmBgYA0KDQo8c3R5bGU+DQogIC5zdXBlcmJpZ2ltYWdlew0KICAgICAgb3ZlcmZsb3cteDpzY3JvbGw7DQogICAgICB3aGl0ZS1zcGFjZTogbm93cmFwOw0KICB9DQoNCiAgLnN1cGVyYmlnaW1hZ2UgaW1new0KICAgICBtYXgtd2lkdGg6IG5vbmU7DQogIH0NCg0KDQo8L3N0eWxlPg0KDQpfVGhpcyBtYXJrZG93biBpcyBhbiB1cGRhdGVkIHZlcnNpb24gb2YgYSBtb2R1bGUgd3JpdHRlbiBieSBLZW4gU3RlaWYgaW4gMjAxOS4gSXQgd2FzIGFuIHVucHVibGlzaGVkIGNoYXB0ZXIgZnJvbSBoaXMgYm9vayBQdWJsaWMgUG9saWN5IEFuYWx5dGljcywgYW5kIGl0IGlzIHRoZXJlZm9yZSB2ZXJ5IGRldGFpbGVkLiBJdCBoYXMgYmVlbiB1cGRhdGVkIHRvIGtlZXAgaXQgYWxpZ25lZCB3aXRoIGN1cnJlbnQgcGFja2FnZXMuIEZvciB0aGUgbW9zdCBwYXJ0LCB0aGUgb3JpZ2luYWwgdGV4dCBpcyBzdGlsbCB1c2VkIGhlcmUsIG91dCBvZiByZXNwZWN0IGZvciB0aGUgb3JpZ2luYWwgbWF0ZXJpYWwgYW5kIHRoZSBhdXRob3IuIEhvd2V2ZXIsIGFsdGVyYXRpb25zLCBhbm5vdGF0aW9ucyBhbmQgc3VwcGxlbWVudGFsIGNvZGUgYXJlIHRocm91Z2hvdXQuXw0KDQpfRm9yIHRoaXMgcHJvamVjdCwgd2UgYnVpbHQgYSBncm93dGggbW9kZWwgYmFzZWQgb24gYSBiaW5hcnkgbG9naXN0aWMgcmVncmVzc2lvbiB0aGF0IHByZWRpY3RzIHRoZSBwcm9iYWJpbGl0eSBvZiBsYW5kIGNvdmVyIGNoYW5nZSBhdCBhIHJhc3RlciBjZWxsIGxldmVsIG92ZXIgYSBzcGVjaWZpZWQgaW50ZXJ2YWwgYXMgYSBmdW5jdGlvbiBvZiBhKSBFeGlzdGluZyBsYW5kIGNvdmVyLCBiKSBUaGUgbG9jYXRpb24gb2YgaW5mcmFzdHJ1Y3R1cmUgYW5kIGMpIERlbW9ncmFwaGljIGFuZCBlY29ub21pYyBzcGF0aWFsIHZhcmlhYmxlcy5fDQoNCl9XaGlsZSB3ZSBoYXZlIHVzZWQgbG9naXN0aWMgcmVncmVzc2lvbiB0byBtb2RlbCBzcGF0aWFsIHBoZW5vbWVuYSBsaWtlIGZsb29kaW5nIGJlZm9yZSwgdGhpcyB3b3JrZmxvdyBpcyBkaWZmZXJlbnQgaW4gdGhhdCB3ZSB3cmFuZ2xlIHJhc3RlciBkYXRhIGluIFIgcmF0aGVyIHRoYW4gZG9pbmcgaXQgaW4gQXJjR0lTLl8NCg0KX1RoZSBhcHBlbmRpY2VzIHRvIHRoaXMgZG9jdW1lbnQgYWRkIHNvbWUgYWRkaXRpb25hbCBjb2RlIGFuZCBhbHRlcm5hdGl2ZSBjb2RlIGJsb2NrcyB0byB3aGF0IGlzIGluIHRoZSBjaGFwdGVyIGl0c2VsZi4gWW91IGNhbiB1c2UgdGhlc2UgdG8gYXVnbWVudCB0aGUgd29ya2Zsb3cgdG8gcnVuIGVudGlyZWx5IGluIFIgYW5kIGxvYWQsIGRvd25zYW1wbGUgYW5kIHdyYW5nbGUgcmFzdGVycy5fDQoNCl9UaGFua3MgdmVyeSBtdWNoIHRvIEplbm5hIEVwc3RlaW4gZm9yIGhlbHBpbmcgdG8gdXBkYXRlIGFuZCBlZGl0IHRoaXMgbWF0ZXJpYWwgaW4gMjAyMi4gQSBub3RlIGZyb20gUHJvZmVzc29yIE1pY2hhZWwgRmljaG1hbi5fDQoNCg0KIyAxLiBJbnRyb2R1Y3Rpb24NCg0KUmVnaW9uYWwgdXJiYW4gZGV2ZWxvcG1lbnQgaXMgYW4gZW1lcmdlbnQgb3V0Y29tZSByZXN1bHRpbmcgZnJvbSB0aGUgYXV0b25vbW91cyBkZWNpc2lvbnMgb2YgbWFueSBkaWZmZXJlbnQgYWdlbnRzLCBpbmNsdWRpbmcgZGV2ZWxvcGVycywgcmVhbCBlc3RhdGUgYnV5ZXJzIGFuZCB0ZW5hbnRzIGFzIHdlbGwgYXMgcGxhbm5lcnMgYW5kIHJlZ3VsYXRvcnMuIEVhY2ggb2YgdGhlc2UgZ3JvdXBzIG9wdGltaXplIGZvciBhIGRpZmZlcmVudCBzZXQgb2YgYm90dG9tIGxpbmVzLiBEZXZlbG9wZXJzIHNlZWsgcHJvZml0LCBidXlpbmcgbGFuZCBhdCBhIGxvdyBwcmljZSwgaW1wcm92aW5nIG9uIHRoYXQgbGFuZCwgYW5kIHNlbGxpbmcgaXQgYXQgYSBwcmVtaXVtLiBIb3VzZWhvbGRzIGFuZCBmaXJtcyBjb25zdW1lIGhvdXNpbmcgYW5kIGNvbW1lcmNpYWwgcmVhbCBlc3RhdGUgYmFsYW5jaW5nIHByaWNlIGNvbnN0cmFpbnRzIHdpdGggYWNjZXNzIHRvIGFtZW5pdGllcyBhbmQgY3VzdG9tZXJzLCByZXNwZWN0aXZlbHkuDQoNClBsYW5uZXJzIHJlZ3VsYXRlIGRldmVsb3BtZW50IGJ5IHRyYWRpbmctb2ZmIGVjb25vbWljIGdyb3d0aCB3aXRoIHRoZSBtaXRpZ2F0aW9uIG9mIG5lZ2F0aXZlIGV4dGVybmFsaXRpZXMgdG93YXJkIHRoZSBnb2FsIG9mIGVjb25vbWljIGFuZCBlbnZpcm9ubWVudGFsIHN1c3RhaW5hYmlsaXR5LiBUaGlzIGlzIGEgY2FzZSBzdHVkeSBpbiBtYW5hZ2luZyB0aGVzZSB0cmFkZS1vZmZzLiBUaGUgZm9jdXMgb2YgdGhpcyBjaGFwdGVyIGlzIG9uIENvb2ssIER1UGFnZSwgYW5kIFdpbGwgQ291bnRpZXMgaW4gSWxsaW5vaXMgaW4gdGhlIENoaWNhZ28gcmVnaW9uLiBDaGljYWdvIGlzIGxvY2F0ZWQgaW4gQ29vayBDb3VudHksIElMLiBBcyBhIHNwcmF3bGluZyBNZXRyb3BvbGl0YW4gYXJlYSwgY2xpbWF0ZSBjaGFuZ2UgYW5kIGVxdWl0eSBjb25jZXJucyBhcmUgZm9yY2luZyBjaXRpZXMgbGlrZSBDaGljYWdvIHRvIHJlY29uc2lkZXIgdGhlIHJvbGUgb2YgbGFuZCB1c2UgcGxhbm5pbmcuDQoNCkxhbmQgdXNlIHBsYW5uaW5nIHRoYXQgaXMgYm90aCBlY29ub21pY2FsbHkgcHJvZHVjdGl2ZSBhbmQgc3VzdGFpbmFibGUgcmVxdWlyZXMgYm90aCBzdXBwbHkgYW5kIGRlbWFuZC1zaWRlIGluc2lnaHRzLiBTcGVjaWZpY2FsbHksIHRoZSBQbGFubmVyIG11c3QgdW5kZXJzdGFuZCBmdXR1cmUgZGVtYW5kIGZvciBkZXZlbG9wbWVudCBhbmQgaG93IHRoYXQgZGVtYW5kIGNvbnRyYXN0cyB3aXRoIHRoZSBzdXBwbHkgb2YgZW52aXJvbm1lbnRhbGx5IHNlbnNpdGl2ZSBsYW5kLiBUaGUgZ29hbCBvZiB0aGlzIGNoYXB0ZXIgaXMgdG8gbW9kZWwgdGhpcyBpbnRlcnBsYXkuDQoNClRoZSBuZXh0IHNlY3Rpb24gd3JhbmdsZXMgYSBob3N0IG9mIGRhdGFzZXRzIGluY2x1ZGluZywgTGFuZCBDb3ZlciBhbmQgTGFuZCBDb3ZlciBDaGFuZ2UgZnJvbSB0aGUgVVNHUywgQ2Vuc3VzIGRlbW9ncmFwaGljcywgYW5kIHRyYW5zcG9ydGF0aW9uLiBTcGF0aWFsIGxhZyBmZWF0dXJlcyBhcmUgYWxzbyBlbmdpbmVlcmVkIGZyb20gdGhlIGxhbmQgY292ZXIgY2hhbmdlIGRhdGEgaHlwb3RoZXNpemluZyB0aGF0IHRoZSB0aW1lL3NwYWNlIHNjYWxlIG9mIGRldmVsb3BtZW50IGJldHdlZW4gMjAxMSBhbmQgMjAyMSBjYW4gaGVscCBwcmVkaWN0IG5ldyBkZXZlbG9wbWVudCBpbiAyMDMxLg0KDQpFeHBsb3JhdG9yeSBhbmFseXNpcyBpcyB1bmRlcnRha2VuIHRvIGludmVzdGlnYXRlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBkZXZlbG9wbWVudCBhbmQgdGhlIGFmb3JlbWVudGlvbmVkIGZlYXR1cmVzLiBGaW5kaW5ncyB0aGVuIG1vdGl2YXRlIHRoZSBlc3RpbWF0aW9uIG9mIGEgZ2Vvc3BhdGlhbCBwcmVkaWN0aXZlIG1vZGVsIHRyYWluZWQgb24gbmV3IGRldmVsb3BtZW50IGJldHdlZW4gMjAxMSBhbmQgMjAyMS4gVGhvc2UgZHluYW1pY3MgYXJlIHRoZW4gaGFybmVzc2VkIHRvIHByZWRpY3QgRGV2ZWxvcG1lbnRfRGVtYW5kIGZvciAyMDMxIC0gcHJlZGljdGlvbnMgaW50ZXJwcmV0ZWQgYXMgdGhlIHByb2JhYmlsaXR5IG9mIG5ldyBkZXZlbG9wbWVudCBoZXJlLiBUaGUgZ29hbCBpcyBub3QgdG8gY3JlYXRlIHRoZSBtb3N0IGFjY3VyYXRlIG1vZGVsLCBidXQgdG8gZGVtb25zdHJhdGUgdGhlIHJvbGUgZm9yIGdlb3NwYXRpYWwgbWFjaGluZSBsZWFybmluZyBpbiB0aGUgbGFuZCB1c2UgbW9kZWxpbmcgcHJvY2Vzcy4NCg0KT25jZSBwcmVkaWN0aW9ucyBhcmUgdmFsaWRhdGVkIGZvciB0aGVpciBhY2N1cmFjeSwgd2UgZXZhbHVhdGUgdGhlaXIgY29uZmx1ZW5jZSB3aXRoIGVudmlyb25tZW50YWxseSBzZW5zaXRpdmUgbGFuZCBpbiBjb3VudGllcyB0aHJvdWdob3V0IHRoZSByZWdpb24sIHdpdGggcGFydGljdWxhciBlbXBoYXNpcyBvbiBsYW5kc2NhcGUgZnJhZ21lbnRhdGlvbi4gVGhlIGZpbmFsIHNlY3Rpb24gdXNlcyBwcmVkaWN0aW9ucyB0byDigJhhbGxvY2F0ZeKAmSBuZXcgZGV2ZWxvcG1lbnQgYWNyb3NzIHRvIHBsYWNlcyB3aGVyZSBncm93dGggY2FuIGhhdmUgYW4gZWNvbm9taWMgaW1wYWN0IHdpdGhvdXQgaW1wZWRpbmcgc3VzdGFpbmFiaWxpdHkgZ29hbHMuIEl0IGFsc28gdGVzdHMgYSBzY2VuYXJpbyBmb3IgYXR0cmFjdGluZyBuZXcgZGV2ZWxvcG1lbnQgd2l0aCBhbiBleHRlbnNpb24gb2YgcmVnaW9uYWwgcmFpbCAobmV3IHRyYW5zcG9ydGF0aW9uIGluZnJhc3RydWN0dXJlKS4NCg0KIyAxLjIuIFNldHVwDQoNCkJlbG93IHdlIGxvYWQgdGhlIGxpYnJhcmllcyBuZWVkZWQgZm9yIHRoZSBhbmFseXNpcyBhcyB3ZWxsIGFzIGEgYG1hcFRoZW1lYCBhbmQgYHBsb3RUaGVtZWAuIEEgc2V0IG9mIHBhbGV0dGUgY29sb3JzIGFyZSBhbHNvIHNwZWNpZmllZC4NCg0KYGBge3IgbG9hZF9wYWNrYWdlcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cyA9ICJoaWRlIn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShzZikNCmxpYnJhcnkodGVycmEpDQpsaWJyYXJ5KHJhc3RlcikNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpsaWJyYXJ5KHRpZHljZW5zdXMpDQpsaWJyYXJ5KHRpZ3JpcykNCmxpYnJhcnkoRk5OKQ0KI2xpYnJhcnkoUXVhbnRQc3ljKSAjIEpFIE5vdGU6IGluIFIgNC4xLCBRdWFudFBzeWMgcGFja2FnZSBub3QgYXZhaWxhYmxlLg0KbGlicmFyeShjYXJldCkNCmxpYnJhcnkoeWFyZHN0aWNrKQ0KbGlicmFyeShwc2NsKQ0KbGlicmFyeShwbG90Uk9DKSANCmxpYnJhcnkoZ2dyZXBlbCkNCmxpYnJhcnkocFJPQykNCmxpYnJhcnkoZ3JpZCkNCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KbGlicmFyeSh2aXJpZGlzKQ0KbGlicmFyeShpZ3JhcGgpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShzY2FsZXMpDQpsaWJyYXJ5KGdncGxvdDIpDQoNCnBsb3RUaGVtZSA8LSB0aGVtZSgNCiAgcGxvdC50aXRsZSA9ZWxlbWVudF90ZXh0KHNpemU9MTIpLA0KICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksDQogIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksDQogIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwNCiAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwNCiAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksDQogICMgU2V0IHRoZSBlbnRpcmUgY2hhcnQgcmVnaW9uIHRvIGJsYW5rDQogIHBhbmVsLmJhY2tncm91bmQ9ZWxlbWVudF9ibGFuaygpLA0KICBwbG90LmJhY2tncm91bmQ9ZWxlbWVudF9ibGFuaygpLA0KICAjcGFuZWwuYm9yZGVyPWVsZW1lbnRfcmVjdChjb2xvdXI9IiNGMEYwRjAiKSwNCiAgIyBGb3JtYXQgdGhlIGdyaWQNCiAgcGFuZWwuZ3JpZC5tYWpvcj1lbGVtZW50X2xpbmUoY29sb3VyPSIjRDBEMEQwIixzaXplPS43NSksDQogIGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpKQ0KDQptYXBUaGVtZSA8LSB0aGVtZShwbG90LnRpdGxlID1lbGVtZW50X3RleHQoc2l6ZT0xMiksDQogICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksDQogICAgICAgICAgICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLA0KICAgICAgICAgICAgICAgICAgYXhpcy5saW5lPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIHBhbmVsLmJvcmRlcj1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yPWVsZW1lbnRfbGluZShjb2xvdXIgPSAndHJhbnNwYXJlbnQnKSwNCiAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3I9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIsIA0KICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwNCiAgICAgICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDEsIDEsIDEsIDEsICdjbScpLA0KICAgICAgICAgICAgICAgICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDEsICJjbSIpLCBsZWdlbmQua2V5LndpZHRoID0gdW5pdCgwLjIsICJjbSIpKQ0KDQpwYWxldHRlMiA8LSBjKCIjNDFiNmM0IiwiIzI1MzQ5NCIpDQpwYWxldHRlNCA8LSBjKCIjYTFkYWI0IiwiIzQxYjZjNCIsIiMyYzdmYjgiLCIjMjUzNDk0IikNCnBhbGV0dGU1IDwtIGMoIiNmZmZmY2MiLCIjYTFkYWI0IiwiIzQxYjZjNCIsIiMyYzdmYjgiLCIjMjUzNDk0IikNCnBhbGV0dGU2IDwtIGMoIiMyNTM0OTQiLCIjMmM3ZmI4IiwiIzQxYjZjNCIsIiNhMWRhYjQiLCIjZmZmZmNjIikNCnBhbGV0dGUxMCA8LSBjKCIjZjdmY2YwIiwiI2UwZjNkYiIsIiNjY2ViYzUiLCIjYThkZGI1IiwiIzdiY2NjNCIsDQogICAgICAgICAgICAgICAiIzRlYjNkMyIsIiMyYjhjYmUiLCIjMDg2OGFjIiwiIzA4NDA4MSIsIiNmN2ZjZjAiKQ0KYGBgDQoNCldlIGFsc28gaW5jbHVkZSBzZXZlcmFsIGhlbHBlciBmdW5jdGlvbnMuIGBxdWludGlsZXNCcmVha3NgIHRha2VzIGEgZGF0YWZyYW1lIGFuZCBhIGNvbHVtbiBhbmQgb3V0cHV0cyB0aGUgcXVpbnRpbGVzIGJyZWFrcywgaGVscGluZyBzaG9ydGVuIHRoZSBiZWxvdyBgZ2dwbG90YCBjYWxscy4NCg0KSXQgdGFrZXMgbG9uZ2VyIHRvIGBnZ3Bsb3RgIGEgcG9seWdvbiBmaXNobmV0IHdpdGggYGdlb21fc2ZgIHRoYW4gaXQgZG9lcyB0byBwbG90IGBnZW9tX3BvaW50YC4gVG8gY3V0IGRvd24gb24gcGxvdHRpbmcgdGltZSwgdGhlIGB4eUNgIChmb3Ig4oCYWFkgQ29vcmRpbmF0ZXPigJkpIHRha2VzIGEgZmlzaG5ldCBgc2ZgIGFuZCBjb252ZXJ0cyBpdCB0byBhIGRhdGFmcmFtZSBvZiBncmlkIGNlbGwgY2VudHJvaWQgY29vcmRpbmF0ZXMuDQoNCmByYXN0YCBpcyBhIGZ1bmN0aW9uIGFsbG93aW5nIHVzIHRvIHF1aWNrbHkgcGxvdCByYXN0ZXIgdmFsdWVzIGluIGBnZ3Bsb3RgLg0KDQpgYGB7ciAzLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCiN0aGlzIGZ1bmN0aW9uIGNvbnZlcnRzIGEgY29sdW1uIGluIHRvIHF1aW50aWxlcy4gSXQgaXMgdXNlZCBmb3IgbWFwcGluZy4NCnF1aW50aWxlQnJlYWtzIDwtIGZ1bmN0aW9uKGRmLHZhcmlhYmxlKSB7DQogICAgYXMuY2hhcmFjdGVyKHF1YW50aWxlKGRmW1t2YXJpYWJsZV1dLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjKC4wMSwuMiwuNCwuNiwuOCksbmEucm09VCkpDQp9DQoNCiNUaGlzIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHRvIGNvbnZlcnQgYSBwb2x5Z29uIHNmIHRvIGNlbnRyb2lkcyB4eSBjb29yZHMuDQp4eUMgPC0gZnVuY3Rpb24oYVBvbHlnb25TRikgew0KICBhcy5kYXRhLmZyYW1lKA0KICAgIGNiaW5kKHg9c3RfY29vcmRpbmF0ZXMoc3RfY2VudHJvaWQoYVBvbHlnb25TRikpWywxXSwNCiAgICAgICAgICB5PXN0X2Nvb3JkaW5hdGVzKHN0X2NlbnRyb2lkKGFQb2x5Z29uU0YpKVssMl0pKQ0KfSANCg0KI3RoaXMgZnVuY3Rpb24gY29udmVydCBhIHJhc3RlciB0byBhIGRhdGEgZnJhbWUgc28gaXQgY2FuIGJlIHBsb3R0ZWQgaW4gZ2dwbG90DQpyYXN0IDwtIGZ1bmN0aW9uKGluUmFzdGVyKSB7DQogIGRhdGEuZnJhbWUoDQogICAgeHlGcm9tQ2VsbChpblJhc3RlciwgMTpuY2VsbChpblJhc3RlcikpLCANCiAgICB2YWx1ZSA9IGdldFZhbHVlcyhpblJhc3RlcikpIH0NCmBgYA0KDQojIDIuIERhdGEgV3JhbmdsaW5nICYgRmVhdHVyZSBFbmdpbmVlcmluZw0KDQpJbiB0aGlzIHNlY3Rpb24gYSBjb25zaWRlcmFibGUgYW1vdW50IG9mIHZlY3RvciBhbmQgcmFzdGVyIGRhdGEgaXMgd3JhbmdsZWQgdG9nZXRoZXIgaW50byBhIHJlZ3Jlc3Npb24tcmVhZHkgZGF0YXNldC4gVGhlIGZvbGxvd2luZyBkYXRhc2V0cyBhcmUgdXNlZDoNCg0KMi4yIC0gMi4zOiBMYW5kIGNvdmVyIGRhdGEgW2Rvd25sb2FkZWRdKGh0dHBzOi8vd3d3Lm1ybGMuZ292L2RhdGEvbmxjZC1sYW5kLWNvdmVyLWNoYW5nZS1pbmRleC1jb251cykgZnJvbSB0aGUgTXVsdGktUmVzb2x1dGlvbiBMYW5kIENoYXJhY3RlcmlzdGljcyBDb25zb3J0aXVt4oCZcyBOYXRpb25hbCBMYW5kIENvdmVyIERhdGFiYXNlIChOTENEKSBpbmNsdWRlcyBhbm51YWwgbGFuZCBjb3ZlciBhbmQgbGFuZCBjb3ZlciBjaGFuZ2UgcmFzdGVyIGRhdGEgZm9yIHRoZSBlbnRpcmUgY291bnRyeS4gVGhlc2UgZGF0YSBhcmUgc2FtcGxlZCB0byBhIDQsMDAwIGJ5IDQsMDAwIGZ0XjIgZmlzaG5ldCwgd2hpY2ggd2lsbCBiZSB1c2VkIGZvciB0aGlzIGFuYWx5c2lzLiAyLjQ6IFBvcHVsYXRpb24gZGF0YSBpcyBkb3dubG9hZGVkIGZyb20gdGhlIFUuUy4gQ2Vuc3VzIGFuZCBqb2luZWQgdG8gdGhlIGZpc2huZXQgYnkgZGlzdHJpYnV0aW5nIENlbnN1cyBUcmFjdCBwb3B1bGF0aW9uIHRvdGFscyBwcm9wb3J0aW9uYWxseSB0byBlYWNoIGdyaWQgY2VsbC4gMi41OiBIaWdod2F5IHZlY3RvcnMgYXJlIGRvd25sb2FkZWQgZnJvbSB0aGUgVS5TLiBDZW5zdXMgVElHRVIgTGluZSBzaGFwZWZpbGVzIGZvciAyMDE5IGFuZCB1c2VkIHRvIHdyYW5nbGUgaGlnaHdheSBwcm94aW1pdHkgZmVhdHVyZXMuIFJlZ2lvbmFsIHJhaWwgdmVjdG9ycyBhcmUgZG93bmxvYWRlZCBmcm9tIHRoZSBDaXR5IG9mIENoaWNhZ28uIEl0IGluY2x1ZGVzIGFsbCBNZXRyYSBjb21tdXRlciByYWlsIGxpbmVzIGluIHRoZSBDaGljYWdvbGFuZCByZWdpb24uIEZvciB0aGUgQ2hpY2FnbyBkb3dudG93biBhcmVhLCB3ZSBjcmVhdGVkIGEgcG9seWdvbiBmb3IgdGhlIENoaWNhZ28gTG9vcCBhbmQgaW1wb3J0ZWQgdGhlIHNoYXBlZmlsZS4gMi42OiBUaGUgbGFuZCBjb3ZlciBjaGFuZ2UgZGF0YSBpcyB1c2VkIHRvIGVuZ2luZWVyIHNwYXRpYWwgbGFnIGZlYXR1cmVzLiAyLjc6IENvdW50eSBwb2x5Z29ucyBhcmUgZG93bmxvYWRlZCB1c2luZyB0aGUgYHRpZ3Jpc2AgcGFja2FnZS4gMi44OiBFYWNoIGZlYXR1cmUgaXMgd3JhbmdsZWQgaW50byBhIGZpbmFsIGRhdGFzZXQuDQoNCkl0IGlzIGltcG9ydGFudCB0byByZW1lbWJlciB0aGF0IGxhbmQgY292ZXIgaXMgbm90IOKAmGxhbmQgdXNl4oCZLiBUeXBpY2FsbHksIHRoZSBmb3JtZXIgcmVmZXJzIHRvIHBoZW5vbWVuYSBvbiB0aGUgRWFydGjigJlzIHN1cmZhY2UgaW5jbHVkaW5nIGJvdGggdGhlIGJ1aWx0IGVudmlyb25tZW50IGFuZCBuYXR1cmFsIHJlc291cmNlcywgd2hpbGUgdGhlIGxhdHRlciB0eXBpY2FsbHkgcmVmZXJzIG9ubHkgdG8gdmFyaWF0aW9uIGluIHRoZSBidWlsdCBlbnZpcm9ubWVudC4NCg0KT3RoZXIgcmFzdGVyIGZlYXR1cmVzIGFyZSBjcmVhdGVkIHN1Y2ggYXMgZGlzdGFuY2UgdG8gaGlnaHdheXMsIGZvciBpbnN0YW5jZS4gVGhlc2UgcmFzdGVycyBhcmUgdGhlbiBpbnRlZ3JhdGVkIHdpdGggYSB2ZWN0b3IgZmlzaG5ldC4gQWRkaXRpb25hbCBmZWF0dXJlIGVuZ2luZWVyaW5nIGlzIHBlcmZvcm1lZCBvbiB0aGUgdmVjdG9yLXNpZGUgcHJvdmlkaW5nIGEgc2ltcGxlLCBidXQgY29tcHJlaGVuc2l2ZSBzbmFwc2hvdCBvZiB0aGUgZGV2ZWxvcG1lbnQgcHJvY2VzcyBpbiBhbmQgYXJvdW5kIENoaWNhZ28gYW5kIENvb2ssIER1UGFnZSwgYW5kIFdpbGwgQ291bnRpZXMgYmV0d2VlbiAyMDExIGFuZCAyMDIxLg0KDQojIyAyLjIuIExhbmQgQ292ZXIgQ2hhbmdlIERhdGENCg0KVGhlIGRlcGVuZGVudCB2YXJpYWJsZSB3ZSB3aXNoIHRvIGZvcmVjYXN0IGlzIGxhbmQgY292ZXIgY2hhbmdlIGJldHdlZW4gMjAxMSBhbmQgMjAyMS4gSW4gdGhpcyBzZWN0aW9uLCB0aGUgbGFuZCBjb3ZlciByYXN0ZXIgZGF0YSBpcyBsb2FkZWQsIHJlY2xhc3NpZmllZCBhbmQgaW50ZWdyYXRlZCB3aXRoIGEgdmVjdG9yIGZpc2huZXQuIEFzIGJlZm9yZSwgdGhlIGZpc2huZXQgd2lsbCBhbGxvdyB1cyB0byBwYXJhbWV0cml6ZSBzcGF0aWFsIHJlbGF0aW9uc2hpcHMgaW4gYSByZWdyZXNzaW9uIGNvbnRleHQuDQoNClRoZSB0YWJsZSBiZWxvdyBzaG93cyBkZXNjcmlwdGlvbnMgb2YgZWFjaCBjYXRlZ29yaWNhbCBsYW5kIGNvdmVyIHR5cGUgaW4gdGhlIGxhbmQgY292ZXIgZGF0YS4gQmVsb3csIHdlIHdpbGwgcmVjbGFzc2lmeSB0aGVzZSBkYXRhIGludG8gbW9yZSB1c2VmdWwgY2F0ZWdvcmllcy4NCg0KIF9KRSBOb3RlOiBMYW5kIGNvdmVyIGNhdGVnb3JpZXMgY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL3d3dy5tcmxjLmdvdi9kYXRhL2xlZ2VuZHMvbmF0aW9uYWwtbGFuZC1jb3Zlci1kYXRhYmFzZS1jbGFzcy1sZWdlbmQtYW5kLWRlc2NyaXB0aW9uKSAtIHdpbGwgcmVwbGFjZSB3aXRoIG1hcmtkb3duIHRhYmxlIGF0IGEgbGF0ZXIgcG9pbnQuXyANCiANClNldmVyYWwgcmFzdGVyIGxheWVycyBoYXZlIGJlZW4gcHJvdmlkZWQgZm9yIHRoaXMgYW5hbHlzaXM6IA0KDQotIFdlIHJlYWQgaW4gYFRocmVlQ291bnR5QXJlYWAgLSB0aGlzIGlzIHRoZSBleHRlbnQgb2YgdGhlIHN0dWR5IGFyZWEgDQoNCi0gYFRocmVlQ291bnR5TENfMjAxMWAgaXMgYSByYXN0ZXIgb2YgbGFuZCBjb3ZlciBpbiAyMDExIGZvciB0aGUgdGhyZWUgY291bnRpZXM6IENvb2ssIER1UGFnZSwgYW5kIFdpbGwgQ291bnRpZXMuIGBUaHJlZUNvdW50eUxDXzIwMjFgIGlzIGEgcmFzdGVyIG9mIGxhbmQgY292ZXIgaW4gMjAyMSBmb3IgdGhlIHNhbWUgZ2VvZ3JhcGh5LiBXZSBoYXZlIHRvIGNhbGN1bGF0ZSBsYW5kIGNvdmVyIGNoYW5nZSAtIHdoZXJlIHRoZXJlIHdlcmUgY29udmVyc2lvbnMgYmV0d2VlbiBvbmUgbGFuZCBjb3ZlciBhbmQgYW5vdGhlciBvbiB0aGUgdGltZSBmcmFtZSAyMDExLTIwMjEuIFdlIHBsb3QgdGhlIHJhc3RlciB1c2luZyBgZ2dwbG90YCBhbmQgdGhlIGByYXN0YCBmdW5jdGlvbiBzcGVjaWZpZWQgYWJvdmUuDQoNCk5vdGUgdGhhdCB0aGVzZSByYXN0ZXJzIGFyZSBwcm9qZWN0ZWQgYXMgYE5BRCAxOTgzIE5BRDgzIElsbGlub2lzIEVhc3QsIGNycyA9IDM0MzVgLiBUaGUgb3JpZ2luYWwgbGFuZCBjb3ZlciByYXN0ZXIgaXMgYXQgYSAzMCBtZXRlciBieSAzMCBtZXRlciByZXNvbHV0aW9uLiBUaGUgcmFzdGVycyBwcm92aWRlZCBhcmUgdWx0aW1hdGVseSByZXNhbXBsZWQgdXAgdG8gNDAwMCBmZWV0IGJ5IDQwMDAgZmVldC4gVGhlIENvb2ssIER1UGFnZSwgYW5kIFdpbGwgQ291bnR5IGFyZWFzIGFyZSAxLDYzNSBzcS4gbWlsZXM7IDMzNiBzcS4gbWlsZXM7IGFuZCA4NDkgc3EuIG1pbGVzLCByZXNwZWN0aXZlbHkuIFRvZ2V0aGVyLCB0aGUgdG90YWwgYXJlYSBmb3IgYWxsIHRocmVlIGNvdW50aWVzIGlzIGFib3V0IDI4MjAgc3EuIG1pbGVzLCBzbyB0aGlzIG1ldHJvIGFyZWEgaXMgcXVpdGUgbGFyZ2UuIEEgdmVjdG9yIGZpc2huZXQgZGF0YXNldCBvZiB0aGlzIHNpemUgd291bGQgYmUgdG9vIGNvbXB1dGF0aW9uYWxseSBpbnRlbnNpdmUuIEJ5IHJlc2FtcGxpbmcgd2UgZ2FpbiBzb21lIGNvbXB1dGF0aW9uYWwgZWZmaWNpZW5jeSBidXQgbG9zZSBzb21lIGFjY3VyYWN5LiBOZXZlcnRoZWxlc3MsIHRoaXMgYXBwcm9hY2ggd29ya3Mgd2VsbCBmb3IgZWR1Y2F0aW9uYWwgcHVycG9zZXMuDQoNCg0KYGBge3IgbG9hZF9kYXRhLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cyA9ICJoaWRlIn0NCiMgTG9hZCByZXF1aXJlZCBsaWJyYXJpZXMNCmxpYnJhcnkocmFzdGVyKQ0KDQojIFJlYWQgdGhlIHNoYXBlZmlsZQ0KSUxfY291bnRpZXMgPC0gc3RfcmVhZCgiQzovVXNlcnMvM2xwYXcvRGVza3RvcC9BcmNHSVMgUHJvIDMuMi9FbnZNb2RlbGluZy8wNF8yNF8yNF9VcmJhbkdyb3d0aE1vZGVsaW5nL0Rvd25sb2FkZWRfRGF0YS9JTF9Db3VudGllcy9JTF9CTkRZX0NvdW50eV9QeS5zaHAiKQ0KIyBDaGFuZ2UgdGhlIGNvb3JkaW5hdGUgc3lzdGVtIHRvIE5BRDgzIElsbGlub2lzIEVhc3QNCklMX2NvdW50aWVzIDwtIHN0X3RyYW5zZm9ybShJTF9jb3VudGllcywgY3JzID0gMzQzNSkNCiMgU3Vic2V0IHRoZSBkYXRhIC0gU2VsZWN0IG9ubHkgdGhlIDMgY291bnRpZXMgdGhhdCB3ZSBuZWVkOiBDb29rLCBEdSBQYWdlLCBhbmQgV2lsbA0KSUxfY291bnRpZXNfc3Vic2V0IDwtIElMX2NvdW50aWVzW0lMX2NvdW50aWVzJENPVU5UWV9OQU0gJWluJSBjKCJXSUxMIiwgIkNPT0siLCAiRFVQQUdFIiksIGMoIkNPVU5UWV9OQU0iLCAiQ09fRklQUyIsICJnZW9tZXRyeSIpXQ0KDQojIFdyaXRlIHRoZSBzdWJzZXR0ZWQgZGF0YSBiYWNrIHRvIGEgc2hhcGVmaWxlDQojIHN0X3dyaXRlKElMX2NvdW50aWVzX3N1YnNldCwgIkM6L1VzZXJzLzNscGF3L0Rlc2t0b3AvQXJjR0lTIFBybyAzLjIvRW52TW9kZWxpbmcvMDRfMjRfMjRfVXJiYW5Hcm93dGhNb2RlbGluZy9Eb3dubG9hZGVkX0RhdGEvSUxfQ291bnRpZXMvVGhyZWVDb3VudHlBcmVhLnNocCIpDQpUaHJlZUNvdW50eUFyZWEgPC0gc3RfcmVhZCgiQzovVXNlcnMvM2xwYXcvRGVza3RvcC9BcmNHSVMgUHJvIDMuMi9FbnZNb2RlbGluZy8wNF8yNF8yNF9VcmJhbkdyb3d0aE1vZGVsaW5nL0Rvd25sb2FkZWRfRGF0YS9JTF9Db3VudGllcy9UaHJlZUNvdW50eUFyZWEuc2hwIikNCg0KIyBTcGVjaWZ5IHRoZSBjb29yZGluYXRlIHN5c3RlbSBmb3IgdGhlIDMgSUwgY291bnRpZXMgdG8gTkFEODMgSWxsaW5vaXMgRWFzdA0KVGhyZWVDb3VudHlBcmVhIDwtIHN0X3RyYW5zZm9ybShUaHJlZUNvdW50eUFyZWEsIGNycyA9IDM0MzUpDQoNCiMgU2V0IHRoZSBkZXNpcmVkIHJhc3RlciByZXNvbHV0aW9uDQojcmVzb2x1dGlvbiA8LSAxMDAgICMgQ2hvb3NlIGEgc3VpdGFibGUgcmVzb2x1dGlvbiAoaW4gbWV0ZXJzIG9yIGRlZ3JlZXMsIGRlcGVuZGluZyBvbiB5b3VyIGRhdGEpDQojIERlZmluZSB0aGUgZXh0ZW50IG9mIHRoZSByYXN0ZXINCiNleHRlbnQgPC0gZXh0ZW50KFRocmVlQ291bnR5QXJlYSkNCiMgQ3JlYXRlIGFuIGVtcHR5IHJhc3RlciBsYXllciB3aXRoIHRoZSBzcGVjaWZpZWQgcmVzb2x1dGlvbiBhbmQgZXh0ZW50DQojZW1wdHlfcmFzdGVyMSA8LSByYXN0ZXIoZXh0ZW50LCByZXMgPSByZXNvbHV0aW9uKQ0KIyBSYXN0ZXJpemUgdGhlIHNoYXBlZmlsZSBvbnRvIHRoZSBlbXB0eSByYXN0ZXIgbGF5ZXINCiNyYXN0ZXJpemVkIDwtIHJhc3Rlcml6ZShUaHJlZUNvdW50eUFyZWEsIGVtcHR5X3Jhc3RlcjEsIGZpZWxkPSJDT19GSVBTIikNCiMgU2F2ZSB0aGUgcmFzdGVyIHRvIGEgZmlsZQ0KIyB3cml0ZVJhc3RlcihyYXN0ZXJpemVkLCAiQzovVXNlcnMvM2xwYXcvRGVza3RvcC9BcmNHSVMgUHJvIDMuMi9FbnZNb2RlbGluZy8wNF8yNF8yNF9VcmJhbkdyb3d0aE1vZGVsaW5nL0Rvd25sb2FkZWRfRGF0YS9JTF9Db3VudGllcy9UaHJlZUNvdW50eUFyZWEudGlmIiwgZm9ybWF0ID0gIkdUaWZmIikNCg0KVGhyZWVDb3VudHlBcmVhX0JvdW5kYXJ5IDwtIHJhc3RlcigiQzovVXNlcnMvM2xwYXcvRGVza3RvcC9BcmNHSVMgUHJvIDMuMi9FbnZNb2RlbGluZy8wNF8yNF8yNF9VcmJhbkdyb3d0aE1vZGVsaW5nL0Rvd25sb2FkZWRfRGF0YS9JTF9Db3VudGllcy9UaHJlZUNvdW50eUFyZWEudGlmIikNCg0KIyBSZWFkIHRoZSB0d28gbGFuZCBjb3ZlciByYXN0ZXJzLCBvbmUgZm9yIDIwMTEgYW5kIG9uZSBmb3IgMjAyMQ0KVGhyZWVDb3VudHlMQ18yMDExIDwtIHJhc3RlcigiQzovVXNlcnMvM2xwYXcvRGVza3RvcC9BcmNHSVMgUHJvIDMuMi9FbnZNb2RlbGluZy8wNF8yNF8yNF9VcmJhbkdyb3d0aE1vZGVsaW5nL0Rvd25sb2FkZWRfRGF0YS9MQ18yMDExXzIwMjEvM0NvdW50eUxDXzIwMTEudGlmIikNCg0KVGhyZWVDb3VudHlMQ18yMDIxIDwtIHJhc3RlcigiQzovVXNlcnMvM2xwYXcvRGVza3RvcC9BcmNHSVMgUHJvIDMuMi9FbnZNb2RlbGluZy8wNF8yNF8yNF9VcmJhbkdyb3d0aE1vZGVsaW5nL0Rvd25sb2FkZWRfRGF0YS9MQ18yMDExXzIwMjEvM0NvdW50eUxDXzIwMjEudGlmIikNCg0KIyBSZXByb2plY3QgdGhlIHJhc3RlcnMgdG8gdGhlIHNhbWUgY29vcmRpbmF0ZSBzeXN0ZW0gYXMgdGhlIG90aGVyIGRhdGEgbGF5ZXJzOiBOQUQ4MyBJbGxpbm9pcyBFYXN0LCBjcnMgPSAzNDM1DQpUaHJlZUNvdW50eUxDXzIwMTEgPC0gcHJvamVjdFJhc3RlcihUaHJlZUNvdW50eUxDXzIwMTEsIGNycyA9IDM0MzUpDQpUaHJlZUNvdW50eUxDXzIwMjEgPC0gcHJvamVjdFJhc3RlcihUaHJlZUNvdW50eUxDXzIwMjEsIGNycyA9IDM0MzUpDQpgYGANCg0KV2Ugbm93IGNhbGN1bGF0ZSBsYW5kIGNvdmVyIGNoYW5nZSBmcm9tIDIwMTEgdG8gMjAyMS4gDQpSZWNsYXNzaWZ5IDIwMTEgYW5kIDIwMjEgbGFuZCBjb3ZlciBkYXRhYmFzZXMgdG8gY29uc2lzdCBvZiAxIGFuZCAwIG9ic2VydmF0aW9ucyAoZS5nLiAxIGlzIHRoZSBkZXZlbG9wZWQgY2xhc3NlcyAxMy0yNCwgMCBpcyBldmVyeXRoaW5nIGVsc2UpLg0KDQpgYGB7ciByZWNsYXNzX01hdHJpeCwgd2FybmluZz0gRkFMU0UsIG1lc3NhZ2U9IEZBTFNFfQ0KcmVjbGFzc01hdHJpeCA8LSANCiAgbWF0cml4KGMoDQogICAgLTUsMTIsMCwNCiAgICAxMiwyNCwxLA0KICAgIDI0LEluZiwwKSwNCiAgbmNvbD0zLCBieXJvdz1UKQ0KYGBgDQoNCmBgYHtyIHBsb3RfZGV2ZWxvcGVkY2hhbmdlLCB3YXJuaW5nPSBGQUxTRSwgbWVzc2FnZT0gRkFMU0V9DQpEZXZlbG9wZWRfMjAxMSA8LSANCiAgcmVjbGFzc2lmeShUaHJlZUNvdW50eUxDXzIwMTEscmVjbGFzc01hdHJpeCkNCiMgeW91IGNhbiBzZWUgdGhlIGZyZXF1ZW5jeSBvZiB0aGUgdmFsdWVzDQpmcmVxKERldmVsb3BlZF8yMDExKQ0KDQpEZXZlbG9wZWRfMjAyMSA8LSANCiAgcmVjbGFzc2lmeShUaHJlZUNvdW50eUxDXzIwMjEscmVjbGFzc01hdHJpeCkNCmZyZXEoRGV2ZWxvcGVkXzIwMjEpDQpgYGANCg0KVGhlbiBkbyBzb21lIG1hcCBhbGdlYnJhIHRvIGZpbmQgdGhlIHBsYWNlcyB3aGVyZSBsYW5kIGNvdmVyIGNoYW5nZWQgKHdlIGNhbGN1bGF0ZSBEZXZlbG9wbWVudF9jaGFuZ2UpLiBMZXTigJlzIHNlZSBhIHF1aWNrIGhpc3RvZ3JhbSBvZiB0aGUgdmFsdWVzIC0gdGhlc2Ugc2hvdWxkIHJhbmdlIGZyb20gMCAodW5kZXZlbG9wZWQgaW4gMjAxMSwgdW5kZXZlbG9wZWQgaW4gMjAyMSksIDEgKHVuZGV2ZWxvcGVkIGluIDIwMTEsIGRldmVsb3BlZCBpbiAyMDIxIChwcmVzdW1pbmcgbm90aGluZyB3ZW50IGZyb20gZGV2ZWxvcGVkIHRvIHVuZGV2ZWxvcGVkKSksIGFuZCAyIChkZXZlbG9wZWQgaW4gYm90aCBwZXJpb2RzKS4gVGhlIDHigJlzIHJlcHJlc2VudCB0aGUgY2hhbmdlLiBZb3UgY2FuIHNlZSB0aGUgbnVtYmVyIG9mIHRoZSB2YWx1ZXMgKDAncywgMSdzLCBhbmQgMidzKSBpbiB0aGUgZnJlcXVlbmN5IHRhYmxlLiANCg0KYGBge3IgcGxvdF9kZXZfY2hhbmdlX2NvdW50aWVzLCB3YXJuaW5nPSBGQUxTRSwgbWVzc2FnZT0gRkFMU0V9DQpEZXZlbG9wbWVudF9jaGFuZ2UgPC0gRGV2ZWxvcGVkXzIwMTErRGV2ZWxvcGVkXzIwMjENCmZyZXEoRGV2ZWxvcG1lbnRfY2hhbmdlKQ0KaGlzdChEZXZlbG9wbWVudF9jaGFuZ2UsIHhsaW0gPSBjKDAsMikpDQpgYGANCg0KV2Ugbm93IHBsb3QgdGhlIDMgQ291bnR5IEFyZWEgYW5kIHRoZSBsYW5kIGNvdmVyIGNoYW5nZSBmcm9tIDIwMTEtMjAyMS4gQWdhaW4sIHRoZSAxcyByZXByZXNlbnQgd2hlcmUgbGFuZCBjb3ZlciBjaGFuZ2VkIGZyb20gbm90IGRldmVsb3BlZCB0byBkZXZlbG9wZWQgYmV0d2VlbiAyMDExIGFuZCAyMDIxLiBUaGUgMnMgcmVwcmVzZW50IGxhbmQgY292ZXIgdGhhdCB3YXMgZGV2ZWxvcGVkIGJvdGggaW4gMjAxMSBhbmQgMjAyMSwgc28gdGhlcmUgd2FzIG5vIGNoYW5nZS4gVGhlcmUgaXMgbm90IGEgbG90IG9mIGRldmVsb3BtZW50IGNoYW5nZSBiZWNhdXNlIGl0IGlzIG9ubHkgb25lIGRlY2FkZSBhbmQgQ2hpY2FnbyBpcyBhbHJlYWR5IHByZXR0eSBkZXZlbG9wZWQsIGJ1dCB0aGVyZSBhcmUgc29tZSByZWQgYXJlYXMgbW9zdGx5IGluIFdpbGwgQ291bnR5IHRvIHRoZSBzb3V0aHdlc3QuIA0KDQpgYGB7ciBwbG90X2RldmNoYW5nZSwgd2FybmluZz0gRkFMU0UsIG1lc3NhZ2U9IEZBTFNFfQ0KZ2dwbG90KCkgKw0KICBnZW9tX3Jhc3RlcihkYXRhPXJhc3QoRGV2ZWxvcG1lbnRfY2hhbmdlKSAlPiUgbmEub21pdCAlPiUgZmlsdGVyKHZhbHVlID4gMCksIA0KICAgICAgICAgICAgICBhZXMoeCx5LGZpbGw9YXMuZmFjdG9yKHZhbHVlKSkpICsNCiAjIHNjYWxlX2ZpbGxfdmlyaWRpcyhkaXJlY3Rpb24gPSAtMSwgZGlzY3JldGU9VFJVRSwgb3B0aW9uID0gIkEiLCBuYW1lID0iTGFuZCBDb3ZlclxuQ2hhbmdlIikgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIyIiA9ICJncmF5IiwgIjEiID0gInJlZCIpLCBuYW1lID0gIkxhbmQgQ292ZXJcbkNoYW5nZSIpICsNCiAgbGFicyh0aXRsZSA9ICJMYW5kIENvdmVyIENoYW5nZSBmb3IgdGhlIFRocmVlIENvdW50eSBBcmVhLCAyMDExLTIwMjEiKSArDQogbWFwVGhlbWUgKw0KICB0aGVtZShsZWdlbmQuZGlyZWN0aW9uPSJob3Jpem9udGFsIikgKw0KZ2VvbV9zZihkYXRhPVRocmVlQ291bnR5QXJlYSwgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGNvbG91ciA9ICJibGFjayIpDQpgYGANCg0KTmV4dCwgd2UgcmVjbGFzc2lmeSB0aGUgcmFzdGVyIHN1Y2ggdGhhdCBhbGwgdGhlIGRldmVsb3BlZCBncmlkIGNlbGwgdmFsdWVzIHJlY2VpdmUgYSB2YWx1ZSBvZiAxIGFuZCBhbGwgb3RoZXIgdmFsdWVzIHJlY2VpdmUgYSB2YWx1ZSBvZiAwLiBUaGlzIGlzIGRvbmUgdXNpbmcgYSByZWNsYXNzaWZ5IG1hdHJpeC4gVGhlIG1hdHJpeCByZWFkcyByb3cgYnkgcm93LiBSb3cgMSBzYXlzIGFueSBncmlkIGNlbGwgcmFuZ2luZyBmcm9tIDAgdG8gMTIgdGFrZXMgYSB2YWx1ZSBvZiAwOyAxMyBvciBncmVhdGVyIHRocm91Z2ggMjQsIGEgdmFsdWUgb2YgMTsgYW5kIGFsbCBvdGhlciB2YWx1ZXMgdGFrZSAwLg0KDQpgYGB7ciA5LCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCnJlY2xhc3NNYXRyaXgyIDwtIA0KICBtYXRyaXgoYygNCiAgICAwLDAsDQogICAgMSwxLA0KICAgIDIsMCksDQogIG5jb2w9MiwgYnlyb3c9VCkNCg0KcmVjbGFzc01hdHJpeDINCmBgYA0KDQpOb3cgYHJlY2xhc3NpZnlgIGFuZCBjb252ZXJ0IGFsbCAw4oCZcyB0byBgTkFgLiBXZSBhcHBseSBhIG5hbWUgdG8gdGhlIHJhc3RlciB3aXRoIGBuYW1lc2AuIFRoaXMgaXMgZG9uZSB0byBtYWtlIGl0IGZhc3RlciB0byBqb2luIHJhc3RlciB0byB0aGUgZmlzaG5ldCBiZWxvdy4gWW91IGNhbiBzZWUgdGhlIGZyZXF1ZW5jeSB0YWJsZSBvZiB2YWx1ZXMgd2l0aCBgZnJlcShEZXZlbG9wbWVudF9jaGFuZ2UyKWAuIFRoZXJlIGFyZSA1MjA0MiBhcmVhcyB0aGF0IGNoYW5nZWQgZnJvbSB1bmRlcmRldmVsb3BlZCB0byBkZXZlbG9wZWQgaW4gdGhlIGRhdGEuDQoNCmBgYHtyIDEwLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCkRldmVsb3BtZW50X2NoYW5nZTIgPC0gDQogIHJlY2xhc3NpZnkoRGV2ZWxvcG1lbnRfY2hhbmdlLHJlY2xhc3NNYXRyaXgyKQ0KDQpEZXZlbG9wbWVudF9jaGFuZ2UyW0RldmVsb3BtZW50X2NoYW5nZTIgPCAxXSA8LSBOQQ0KDQpuYW1lcyhEZXZlbG9wbWVudF9jaGFuZ2UyKSA8LSAiRGV2X2NoYW5nZSINCmZyZXEoRGV2ZWxvcG1lbnRfY2hhbmdlMikNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhPVRocmVlQ291bnR5QXJlYSwgZmlsbCA9ICJ0cmFuc3BhcmVudCIpICsNCiAgZ2VvbV9yYXN0ZXIoZGF0YT1yYXN0KERldmVsb3BtZW50X2NoYW5nZTIpICU+JSBuYS5vbWl0LCANCiAgICAgICAgICAgICAgYWVzKHgseSxmaWxsPWFzLmZhY3Rvcih2YWx1ZSkpKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpcyhkaXNjcmV0ZT1UUlVFLCBuYW1lID0iTGFuZCBDb3ZlclxuQ2hhbmdlIikgKyANCiAgbGFicyh0aXRsZT0iRGV2ZWxvcG1lbnQgTGFuZCBVc2UgQ2hhbmdlLCAyMDExLTIwMjEiKSArDQogIG1hcFRoZW1lDQpgYGANCg0KTmV4dCwgdGhlIGZpc2huZXQgaXMgY3JlYXRlZCBhdCA0MDAwIGJ5IDQwMDAgZm9vdCByZXNvbHV0aW9uIGFuZCBzdWJzZXQgaXQgdG8gdGhlIFRocmVlIENvdW50eSBBcmVhIHdpdGggYHN0X2ludGVyc2VjdGlvbmAuIA0KDQpgYGB7ciAxMSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQpUaHJlZUNvdW50eV9maXNobmV0IDwtIA0KICBzdF9tYWtlX2dyaWQoVGhyZWVDb3VudHlBcmVhLCA0MDAwKSAlPiUNCiAgc3Rfc2YoKQ0KDQpUaHJlZUNvdW50eV9maXNobmV0IDwtDQogIFRocmVlQ291bnR5X2Zpc2huZXRbVGhyZWVDb3VudHlBcmVhLF0NCmBgYA0KDQpUaGUgdmVjdG9yIGZpc2huZXQgaXMgdGhlbiBwbG90dGVkLiBOb3RlIHRoYXQgdGhpcyBwbG90IHRha2VzIGEgYml0IG9mIHRpbWUgdG8gcmVuZGVyIGJlY2F1c2UgdGhlcmUgYXJlIHRob3VzYW5kcyBvZiBwb2x5Z29ucy4NCg0KYGBge3IgcGxvdF9maXNobmV0LCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2U9IEZBTFNFfQ0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGE9VGhyZWVDb3VudHlfZmlzaG5ldCkgKw0KICBsYWJzKHRpdGxlPSJGaXNobmV0LCA0MDAwIEZvb3QgUmVzb2x1dGlvbiIpICsNCiAgbWFwVGhlbWUNCmBgYA0KDQpUaGVuIHRoZSByYXN0ZXIgaXMgY29udmVydGVkIHRvIHBvaW50cywgd2hpY2ggbWFrZXMgaXRzIGpvaW5pbmcgdG8gdGhlIHZlY3RvciBmaXNobmV0IGEgYml0IGZhc3Rlci4gTm93IHRvIGV4dHJhY3QgdGhlIHJhc3RlciB2YWx1ZXMgaW50byB0aGUgZmlzaG5ldC4gVGhlcmUgaXMgYSBmdW5jdGlvbiBpbiB0aGUgYHJhc3RlcmAgcGFja2FnZSBjYWxsZWQgYFJhc3RlclRvUG9seWdvbmAgYnV0IGl0IGlzIHF1aXRlIHNsb3cuDQoNCkJlbG93LCBhIHNsaWdodGx5IGZhc3RlciBhcHByb2FjaCBpcyBkZXZlbG9wIHRoYXQgY29udmVydHMgdGhlIHJhc3RlciB0byBhbiBgc2ZgIHBvaW50IGxheWVyIGFuZCB0aGVuIGpvaW5zIHRoZSBwb2ludHMgdG8gdGhlIGZpc2huZXQgd2l0aCBgYWdncmVnYXRlYC4gVGhpcyB3b3JrcyB3ZWxsIGJlY2F1c2UgdGhlIHJhc3RlciBhbmQgdGhlIGZpc2huZXQgYXJlIG9mIHRoZSBzYW1lIHNwYXRpYWwgcmVzb2x1dGlvbi4gRmluYWxseSwgdGhlIGZpc2huZXQgdmFyaWFibGUgYERldl9jaGFuZ2VgIGlzIGNyZWF0ZWQgdGhhdCBpcyBgMWAgd2hlcmUgbmV3IGRldmVsb3BtZW50IGhhcyBvY2N1cnJlZCBhbmQgYDBgIHdoZXJlIGl0IGhhcyBub3QuIFRoaXMgaXMgb3VyIGRlcGVuZGVudCB2YXJpYWJsZSBhbmQgZW5jb2RlZCBhcyBhIGZhY3Rvci4NCg0KVG8gc3BlZWQgdXAgdGhlIG1hcHBpbmcgcHJvY2VzcywgZmlzaG5ldCBwb2x5Z29ucyBhcmUgY29udmVydGVkIHRvIGNlbnRyb2lkIHBvaW50cyB1c2luZyB0aGUgYHh5Q2AgZnVuY3Rpb24uIA0KDQoNCmBgYHtyIHBsb3RfZGV2X2NoYW5nZTIsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0KY2hhbmdlUG9pbnRzIDwtDQogIHJhc3RlclRvUG9pbnRzKERldmVsb3BtZW50X2NoYW5nZTIpICU+JQ0KICBhcy5kYXRhLmZyYW1lKCkgJT4lDQogIHN0X2FzX3NmKGNvb3JkcyA9IGMoIngiLCAieSIpLCBjcnMgPSBzdF9jcnMoVGhyZWVDb3VudHlfZmlzaG5ldCkpDQoNCmZpc2huZXQgPC0gDQogIGFnZ3JlZ2F0ZShjaGFuZ2VQb2ludHMsIFRocmVlQ291bnR5X2Zpc2huZXQsIHN1bSkgJT4lDQogIG11dGF0ZShEZXZfY2hhbmdlID0gaWZlbHNlKGlzLm5hKERldl9jaGFuZ2UpLDAsMSksDQogICAgICAgICBEZXZfY2hhbmdlID0gYXMuZmFjdG9yKERldl9jaGFuZ2UpKQ0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YT1UaHJlZUNvdW50eV9maXNobmV0KSArDQogIGdlb21fcG9pbnQoZGF0YT1maXNobmV0LCANCiAgICAgICAgICAgICBhZXMoeD14eUMoZmlzaG5ldCkkeCwgeT14eUMoZmlzaG5ldCkkeSwgY29sb3VyPURldl9jaGFuZ2UpKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5ldyBEZXZlbG9wbWVudCIpLA0KICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiIikgKw0KICBsYWJzKHRpdGxlID0gIkxhbmQgQ292ZXIgRGV2ZWxvcG1lbnQgQ2hhbmdlIiwgc3VidGl0bGUgPSAiQXMgZmlzaG5ldCBjZW50cm9pZHMiKSArDQogIG1hcFRoZW1lDQpgYGANCg0KDQojIyAyLjMuIExhbmQgQ292ZXIgaW4gMjAxMQ0KDQpJdCBpcyByZWFzb25hYmxlIHRvIGh5cG90aGVzaXplIHRoYXQgdGhlIHByb3BlbnNpdHkgb2YgbmV3IGRldmVsb3BtZW50IGlzIGEgZnVuY3Rpb24gb2YgZXhpc3RpbmcgbGFuZCBjb3ZlciBjYXRlZ29yaWVzLiBJbiB0aGlzIHNlY3Rpb24gd2UgaWRlbnRpZnkgdGhlc2Ugb3RoZXIgbGFuZCBjb3ZlciBjYXRlZ29yaWVzIGZyb20gMjAxMSBhbmQgaW50ZWdyYXRlIGVhY2ggd2l0aCB0aGUgZmlzaG5ldC4NCg0KYGBge3IgcGxvdF9MQ18yMDExLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCiMgT3VyIHNoYXBlZmlsZSB3aXRoIHRoZSBib3VuZGFyeSBmb3IgdGhlIHRocmVlIGNvdW50aWVzIGlzIFRocmVlQ291bnR5QXJlYQ0KIyBPdXIgbGFuZCBjb3ZlciByYXN0ZXIgZm9yIDIwMTEgZm9yIHRoZSB0aHJlZSBjb3VudGllcyBpcyBUaHJlZUNvdW50eUxDXzIwMTENCg0KIyBnZ3Bsb3QoKSArDQojICAgZ2VvbV9zZihkYXRhPVRocmVlQ291bnR5QXJlYSkgKw0KIyAgIGdlb21fcmFzdGVyKGRhdGE9cmFzdChUaHJlZUNvdW50eUxDXzIwMTEpICU+JSBuYS5vbWl0ICU+JSBmaWx0ZXIodmFsdWUgPiAwKSwgDQojICAgICAgICAgICAgICAgYWVzKHgseSxmaWxsPWFzLmZhY3Rvcih2YWx1ZSkpKSArDQojICAgc2NhbGVfZmlsbF92aXJpZGlzKGRpc2NyZXRlPVRSVUUsIG5hbWUgPSIiKSArDQojICAgbGFicyh0aXRsZSA9ICJMYW5kIENvdmVyLCAyMDExIikgKw0KIyAgIG1hcFRoZW1lICsNCiMgICB0aGVtZShsZWdlbmQuZGlyZWN0aW9uPSJob3Jpem9udGFsIikNCmBgYA0KDQpUaGUgdGFibGUgYmVsb3cgc2hvd3MgdGhlIGFwcHJvYWNoIHRha2VuIHRvIHJlY29kZWQgZXhpc3RpbmcgbGFuZCBjb3ZlciBjb2RlcyBpbnRvIHRoZSBjYXRlZ29yaWVzIHVzZWQgaW4gb3VyIGFuYWx5c2lzLiBJbiB0aGUgY29kZSBibG9jayBiZWxvdyBuZXcgcmFzdGVycyBhcmUgZ2VuZXJhdGVkIGFuZCBgbmFtZXNgIGFyZSBhcHBsaWVkLiBOYW1pbmcgZW5zdXJlcyB0aGF0IHdoZW4gdGhlIHJhc3RlciBpcyBpbnRlZ3JhdGVkIHdpdGggdGhlIGZpc2huZXQsIHRoZSBjb2x1bW4gcmVmbGVjdHMgdGhlIGFwcHJvcHJpYXRlIHJhc3Rlci4NCg0KfCBPbGRfQ2xhc3NpZmljYXRpb24gICAgICAgICAgICAgfCBOZXdfQ2xhc3NpZmljYXRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCBPcGVuIFNwYWNlIGFzIHdlbGwgYXMgTG93LCBNZWRpdW0gYW5kIEhpZ2ggSW50ZW5zaXR5IERldmVsb3BtZW50IHwgRGV2ZWxvcGVkIHwNCnwgRGVjaWR1b3VzLCBFdmVyZ3JlZW4sIGFuZCBNaXhlZCBGb3Jlc3QgfCAgRm9yZXN0IHwNCnwgUGFzdHVyZS9IYXkgYW5kIEN1bHRpdmF0ZWQgQ3JvcHMgfCBGYXJtIHwNCnwgV29vZHkgYW5kIEVtZXJnZW50IEhlcmJhY2VvdXMgV2V0bGFuZHMgfCBXb29kbGFuZHMgfA0KfCBCYXJyZW4gTGFuZCwgRHdhcmYgU2NydWIsIGFuZCBHcmFzc2xhbmQvSGVyYmFjZW91cyB8IE90aGVyIFVuZGV2ZWxvcGVkIHwNCnwgV2F0ZXIgfCBXYXRlciB8DQoNCmBgYHtyIDE1LCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCmRldmVsb3BlZCA8LSBUaHJlZUNvdW50eUxDXzIwMTEgPT0gMjEgfCBUaHJlZUNvdW50eUxDXzIwMTEgPT0gMjIgfCBUaHJlZUNvdW50eUxDXzIwMTEgPT0gMjMgfCBUaHJlZUNvdW50eUxDXzIwMTEgPT0gMjQNCmZvcmVzdCA8LSBUaHJlZUNvdW50eUxDXzIwMTEgPT0gNDEgfCBUaHJlZUNvdW50eUxDXzIwMTEgPT0gNDIgfCBUaHJlZUNvdW50eUxDXzIwMTEgPT0gNDMgDQpmYXJtIDwtIFRocmVlQ291bnR5TENfMjAxMSA9PSA4MSB8IFRocmVlQ291bnR5TENfMjAxMSA9PSA4MiANCndldGxhbmRzIDwtIFRocmVlQ291bnR5TENfMjAxMSA9PSA5MCB8IFRocmVlQ291bnR5TENfMjAxMSA9PSA5NSANCm90aGVyVW5kZXZlbG9wZWQgPC0gVGhyZWVDb3VudHlMQ18yMDExID09IDUyIHwgVGhyZWVDb3VudHlMQ18yMDExID09IDcxIHwgVGhyZWVDb3VudHlMQ18yMDExID09IDMxIA0Kd2F0ZXIgPC0gVGhyZWVDb3VudHlMQ18yMDExID09IDExDQoNCm5hbWVzKGRldmVsb3BlZCkgPC0gImRldmVsb3BlZCINCm5hbWVzKGZvcmVzdCkgPC0gImZvcmVzdCINCm5hbWVzKGZhcm0pIDwtICJmYXJtIg0KbmFtZXMod2V0bGFuZHMpIDwtICJ3ZXRsYW5kcyINCm5hbWVzKG90aGVyVW5kZXZlbG9wZWQpIDwtICJvdGhlclVuZGV2ZWxvcGVkIg0KbmFtZXMod2F0ZXIpIDwtICJ3YXRlciINCmBgYA0KDQpOZXh0LCBlYWNoIHJhc3RlciBpcyBhZ2dyZWdhdGVkIHRvIHRoZSBmaXNobmV0IGJ5IHdheSBvZiBhIGZ1bmN0aW9uIGNhbGxlZCBgYWdncmVnYXRlUmFzdGVyYC4gSGVyZSwgdGhlIHByb2Nlc3MgdXNlZCBhYm92ZSB0byBUbyBkbyB0aGlzLCBhIGZ1bmN0aW9uIGlzIGNyZWF0ZWQgYmVsb3cgdGhhdCBsb29wcyB0aHJvdWdoIGEgbGlzdCBvZiByYXN0ZXJzLCBjb252ZXJ0cyB0aGUgX2l0aF8gcmFzdGVyIHRvIHBvaW50cywgZmlsdGVycyBvbmx5IHBvaW50cyB0aGF0IGhhdmUgdmFsdWUgb2YgYDFgIChpZS4gaXMgdGhlIF9pdGhfIGxhbmQgY292ZXIgdHlwZSksIGFuZCB0aGVuIGFnZ3JlZ2F0ZXMgdG8gdGhlIGZpc2huZXQuDQoNCkhlcmUgaXMgdGhlIGZ1bmN0aW9uLg0KDQpgYGB7ciAxNiwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQphZ2dyZWdhdGVSYXN0ZXIgPC0gZnVuY3Rpb24oaW5wdXRSYXN0ZXJMaXN0LCB0aGVGaXNobmV0KSB7DQogICNjcmVhdGUgYW4gZW1wdHkgZmlzaG5ldCB3aXRoIHRoZSBzYW1lIGRpbWVuc2lvbnMgYXMgdGhlIGlucHV0IGZpc2huZXQNCiAgdGhlc2VGaXNobmV0cyA8LSB0aGVGaXNobmV0ICU+JSBkcGx5cjo6c2VsZWN0KCkNCiAgI2ZvciBlYWNoIHJhc3RlciBpbiB0aGUgcmFzdGVyIGxpc3QNCiAgZm9yIChpIGluIGlucHV0UmFzdGVyTGlzdCkgew0KICAjY3JlYXRlIGEgdmFyaWFibGUgbmFtZSBjb3JyZXNwb25kaW5nIHRvIHRoZSBpdGggcmFzdGVyDQogIHZhck5hbWUgPC0gbmFtZXMoaSkNCiAgI2NvbnZlcnQgcmFzdGVyIHRvIHBvaW50cyBhcyBhbiBzZg0KICAgIHRoZXNlUG9pbnRzIDwtDQogICAgICByYXN0ZXJUb1BvaW50cyhpKSAlPiUNCiAgICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUNCiAgICAgIHN0X2FzX3NmKGNvb3JkcyA9IGMoIngiLCAieSIpLCBjcnMgPSBzdF9jcnModGhlRmlzaG5ldCkpICU+JQ0KICAgICAgZmlsdGVyKC5bWzFdXSA9PSAxKQ0KICAjYWdncmVnYXRlIHRvIHRoZSBmaXNobmV0DQogICAgdGhpc0Zpc2huZXQgPC0NCiAgICAgIGFnZ3JlZ2F0ZSh0aGVzZVBvaW50cywgdGhlRmlzaG5ldCwgbGVuZ3RoKSAlPiUNCiAgICAgIG11dGF0ZSghIXZhck5hbWUgOj0gaWZlbHNlKGlzLm5hKC5bWzFdXSksMCwxKSkNCiAgI2FkZCB0byB0aGUgbGFyZ2VyIGZpc2huZXQNCiAgICB0aGVzZUZpc2huZXRzIDwtIGNiaW5kKHRoZXNlRmlzaG5ldHMsdGhpc0Zpc2huZXQpDQogIH0NCiAgI291dHB1dCBhbGwgYWdncmVnYXRlcyBhcyBvbmUgbGFyZ2UgZmlzaG5ldA0KICAgcmV0dXJuKHRoZXNlRmlzaG5ldHMpDQogIH0NCmBgYA0KDQpUaGUgYHRoZVJhc3Rlckxpc3RgIG9mIGxhbmQgY292ZXIgdHlwZXMgaW4gMjAxMSBpcyBjcmVhdGVkIGFuZCB0aGVuIGZlZCBpbnRvIGBhZ2dyZWdhdGVSYXN0ZXJgLiBUaGUgcmVzdWx0IGlzIGNvbnZlcnRlZCB0byBsb25nIGZvcm0gZ3JpZCBjZWxsIGNlbnRyb2lkcyBhbmQgcGxvdCBhcyBzbWFsbCBtdWx0aXBsZSBtYXBzLg0KDQpOb3RlIHRoZSBpbmNsdXNpb24gb2YgYHN0X2Nhc3RgIGhlcmUgd2hpY2ggY29udmVydCBhbGwgZ2VvbWV0cmllcyB0byBgUE9MWUdPTmAuIElmIHlvdSBjcmVhdGUgYSBmcmVxdWVuY3kgdGFibGUgb2YgZ2VvbWV0cnkgdHlwZXMgaW4gYGFnZ3JlZ2F0ZWRSYXN0ZXJzYCwgeW91IHdpbGwgbm90aWNlIHNvbWUgYW5kIGhhbmRmdWwgb2YgYE1VTFRJUE9MWUdPTlNgLiBUcnkgYHRhYmxlKHN0X2dlb21ldHJ5X3R5cGUoYWdncmVnYXRlZFJhc3RlcnMpYCkuIFRoZXNlIHJvZ3VlIG11bHRpcG9seWdvbnMgYnJlYWsgdGhlIGB4eUNgIGZ1bmN0aW9uIHdoaWNoIGlzIGRlc2lnbmVkIHRvIGZpbmQgZ3JpZCBjZWxsIGNlbnRyb2lkcy4gQWZ0ZXIgYWxsLCB0aGVyZSBpcyBubyBvbmUgY2VudHJvaWQgb2Ygc2V2ZXJhbCBjb21iaW5lZCBwb2x5Z29ucy4gVGh1cyBgc3RfY2FzdGAgZW5zdXJlcyBhbGwgZ2VvbWV0cmllcyBhcmUganVzdCBgUE9MWUdPTmAuIExvb2sgb3V0IGZvciB0aGlzIGZ1bmN0aW9uIHRocm91Z2hvdXQgdGhlIHJlbWFpbmRlciBvZiB0aGlzIGNoYXB0ZXIuDQoNCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCnRoZVJhc3Rlckxpc3QgPC0gYyhkZXZlbG9wZWQsZm9yZXN0LGZhcm0sd2V0bGFuZHMsb3RoZXJVbmRldmVsb3BlZCx3YXRlcikNCg0KYWdncmVnYXRlZFJhc3RlcnMgPC0NCiAgYWdncmVnYXRlUmFzdGVyKHRoZVJhc3Rlckxpc3QsIFRocmVlQ291bnR5X2Zpc2huZXQpICU+JQ0KICBkcGx5cjo6c2VsZWN0KGRldmVsb3BlZCxmb3Jlc3QsZmFybSx3ZXRsYW5kcyxvdGhlclVuZGV2ZWxvcGVkLHdhdGVyKSAlPiUNCiAgbXV0YXRlX2lmKGlzLm51bWVyaWMsYXMuZmFjdG9yKQ0KDQphZ2dyZWdhdGVkUmFzdGVycyAlPiUNCiAgZ2F0aGVyKHZhcix2YWx1ZSxkZXZlbG9wZWQ6d2F0ZXIpICU+JQ0KICBzdF9jYXN0KCJQT0xZR09OIikgJT4lICAgICNqdXN0IHRvIG1ha2Ugc3VyZSBubyB3ZWlyZCBnZW9tZXRyaWVzIHNsaXBwZWQgaW4NCiAgbXV0YXRlKFggPSB4eUMoLikkeCwNCiAgICAgICAgIFkgPSB4eUMoLikkeSkgJT4lDQogIGdncGxvdCgpICsNCiAgICBnZW9tX3NmKGRhdGE9VGhyZWVDb3VudHlBcmVhKSArDQogICAgZ2VvbV9wb2ludChhZXMoWCxZLCBjb2xvdXI9YXMuZmFjdG9yKHZhbHVlKSkpICsNCiAgICBmYWNldF93cmFwKH52YXIpICsNCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLA0KICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk90aGVyIiwiTGFuZCBDb3ZlciIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIiKSArDQogICAgbGFicyh0aXRsZSA9ICJMYW5kIENvdmVyIFR5cGVzLCAyMDExIiwNCiAgICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzIikgKw0KICAgbWFwVGhlbWUNCmBgYA0KDQojIyAyLjQuIENlbnN1cyBEYXRhDQoNClBvcHVsYXRpb24gYW5kIHBvcHVsYXRpb24gY2hhbmdlIGlzIG9idmlvdXNseSBhbiBjcml0aWNhbCBkZW1hbmQtc2lkZSBjb21wb25lbnQgb2YgcHJlZGljdGluZyBgRGV2ZWxvcG1lbnRfRGVtYW5kYC4gQ2Vuc3VzIGRhdGEgZm9yIGJvdGggMjAxMSBhbmQgMjAyMSBjYW4gYmUgZG93bmxvYWRlZCBxdWlja2x5IHVzaW5nIHRoZSBgdGlkeWNlbnN1c2AgcGFja2FnZS4gQXMgaWxsdXN0cmF0ZWQgYmVsb3csIHRoZXNlIGRhdGEgYXJlIGRvd25sb2FkZWQgYXQgYSBjZW5zdXMgdHJhY3QgZ2VvZ3JhcGh5IGFuZCB0aHVzLCBhbiBhcHByb2FjaCBpcyBuZWVkZWQgdG8gcmVjb25jaWxlIHRyYWN0cyBhbmQgZmlzaG5ldCBnZW9tZXRyaWVzLiBUaGlzIGlzIGFjY29tcGxpc2hlZCB1c2luZyBhIHRlY2huaXF1ZSBjYWxsZWQgYXJlYWwgd2VpZ2h0ZWQgaW50ZXJwb2xhdGlvbi4NCg0KUmVjYWxsLCB5b3Ugd2lsbCBuZWVkIGEgY2Vuc3VzIEFQSSBrZXkgdG8gZG93bmxvYWQgdGhlIGNlbnN1cyBkYXRhIHdoaWNoIG11c3QgYmUgaW5wdXQgd2l0aCBgY2Vuc3VzX2FwaV9rZXlgLiANCg0KYGBge3IgbG9hZF9rZXlfaGlkZSwgd2FybmluZz0gRkFMU0UsIGluY2x1ZGU9RkFMU0V9DQojIGNlbnN1c19rZXkgPC0gcmVhZC50YWJsZSgifi9HaXRIdWIvY2Vuc3VzX2tleS50eHQiLCBxdW90ZT0iXCIiLCBjb21tZW50LmNoYXI9IiIpDQojIGNlbnN1c19hcGlfa2V5KGNlbnN1c19rZXlbMV0gJT4lIGFzLmNoYXJhY3RlcigpLCBvdmVyd3JpdGUgPSBUUlVFKQ0KYGBgDQoNCmBgYHtyIGxvYWRfa2V5LCB3YXJuaW5nID0gRkFMU0UsIGV2YWwgPSBGQUxTRX0NCmNlbnN1c19hcGlfa2V5KCJlNWU5NmQ3NjI4NWJlY2EzYzZlMWE5MTEwZDc2MmE0MzBjZWQ0ODExIiwgb3ZlcndyaXRlID0gVFJVRSkNCmBgYA0KDQpGaXJzdCBkYXRhIGlzIHB1bGxlZCBmb3IgMjAxMSBhbmQgcmVwcm9qZWN0ZWQuDQoNCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cyA9ICJoaWRlIn0NCiMgUHVsbCBwb3B1bGF0aW9uIGRhdGEgZm9yIDIwMTENCmxpYnJhcnkodGlkeWNlbnN1cykNCmxpYnJhcnkodGlncmlzKQ0KbGlicmFyeShzZikNCg0KIyBEZWZpbmUgdGhlIGNvdW50aWVzIHlvdSB3YW50IHRvIHJldHJpZXZlIGRhdGEgZm9yDQpjb3VudGllcyA8LSBjKCJDb29rIiwgIkR1UGFnZSIsICJXaWxsIENvdW50eSIpDQoNCiMgUmV0cmlldmUgcG9wdWxhdGlvbiBkYXRhIGZvciAyMDExIGFuZCB0cmFjdCBnZW9tZXRyeQ0KUG9wXzIwMTEgPC0gZ2V0X2FjcyhnZW9ncmFwaHkgPSAidHJhY3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlcyA9ICJCMDEwMDNfMDAxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyID0gMjAxMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0ZSA9ICJJTCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgY291bnR5ID0gY291bnRpZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VvbWV0cnkgPSBUUlVFKSAlPiUNCiAgcmVuYW1lKFBvcF8yMDExID0gZXN0aW1hdGUpICU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKFRocmVlQ291bnR5X2Zpc2huZXQpKQ0KDQojIFdyaXRlIHRoZSBzcGF0aWFsIGRhdGFmcmFtZSB0byBhIHNoYXBlZmlsZQ0Kc3Rfd3JpdGUoUG9wXzIwMTEsIkM6L1VzZXJzLzNscGF3L0Rlc2t0b3AvQXJjR0lTIFBybyAzLjIvRW52TW9kZWxpbmcvMDRfMjRfMjRfVXJiYW5Hcm93dGhNb2RlbGluZy9Eb3dubG9hZGVkX0RhdGEvUG9wdWxhdGlvbi9Qb3BfMjAxMS5zaHAiLCBhcHBlbmQgPSBUUlVFKQ0KDQojIENsaXAgUG9wXzIwMTEgdG8gdGhlIGV4dGVudCBvZiBUaHJlZUNvdW50eV9maXNobmV0LiBPdGhlcndpc2UsIGl0IGluY2x1ZGVzIGFuIGFyZWEgb2YgTGFrZSBNaWNoaWdhbg0KUG9wXzIwMTEgPC0gc3RfaW50ZXJzZWN0aW9uKFBvcF8yMDExLCBUaHJlZUNvdW50eV9maXNobmV0KQ0KYGBgDQoNCk5vdyBkYXRhIGZvciAyMDIxIGlzIGRvd25sb2FkZWQuIEluIHRoaXMgaW5zdGFuY2UsIGBzdF9idWZmZXJgIGlzIHVzZWQgdG8gYnVmZmVyIHRoZSB0aGUgdHJhY3RzIGJ5IC0xZnQuIFRoaXMgaXMgZG9uZSBiZWNhdXNlIGB0aWR5Y2Vuc3VzYCBhcHBlYXJzIHRvIHJldHVybiBnZW9tZXRyaWVzIHRoYXQgYXJlIHByb2JsZW1hdGljIHdoZW4gc3ViamVjdGVkIHRvIHRoZSBhcmVhIHdlaWdodGVkIGludGVycG9sYXRpb24gZnVuY3Rpb24gYmVsb3cuIEFzIGRvbmUgaW4gcHJldmlvdXMgY2hhcHRlcnMsIGEgdmVyeSBzbWFsbCBidWZmZXIgaXMgdXNlZCB0byBjb3JyZWN0IHRoZSBnZW9tZXRyaWVzLg0KDQpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHJlc3VsdHMgPSAiaGlkZSJ9DQoNCmNvdW50aWVzIDwtIGMoIkNvb2siLCAiRHVQYWdlIiwgIldpbGwgQ291bnR5IikNCg0KIyBQdWxsIHBvcHVsYXRpb24gZGF0YSBmb3IgMjAyMQ0KUG9wXzIwMjEgPC0gZ2V0X2FjcyhnZW9ncmFwaHkgPSAidHJhY3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlcyA9ICJCMDEwMDNfMDAxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyID0gMjAyMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0ZSA9ICJJTCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgY291bnR5ID0gY291bnRpZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VvbWV0cnkgPSBUUlVFKSAlPiUNCiAgcmVuYW1lKFBvcF8yMDIxID0gZXN0aW1hdGUpICU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKFRocmVlQ291bnR5X2Zpc2huZXQpKSAlPiUNCiAgc3RfYnVmZmVyKC0xKQ0Kc3Rfd3JpdGUoUG9wXzIwMjEsIkM6L1VzZXJzLzNscGF3L0Rlc2t0b3AvQXJjR0lTIFBybyAzLjIvRW52TW9kZWxpbmcvMDRfMjRfMjRfVXJiYW5Hcm93dGhNb2RlbGluZy9Eb3dubG9hZGVkX0RhdGEvUG9wdWxhdGlvbi9Qb3BfMjAyMS5zaHAiLCBhcHBlbmQgPSBUUlVFKQ0KDQpgYGANCg0KQm90aCB5ZWFycyBvZiBjZW5zdXMgZGF0YSBhcmUgdGhlbiBwbG90dGVkLg0KDQo8ZGl2IGNsYXNzPSJzdXBlcmJpZ2ltYWdlIj4NCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodD0gOCwgZmlnLndpZHRoPSAxMX0NCmNsYXNzKFBvcF8yMDExKQ0Kc3RyKFBvcF8yMDExKQ0Kc3RyKFBvcF8yMDIxKQ0KDQojIElmIHRoZSBnZW9tZXRyeSBjb2x1bW4gaXMgbWlzc2luZyBvciBoYXMgYSBkaWZmZXJlbnQgbmFtZSwgcmVuYW1lIGl0DQpQb3BfMjAxMSA8LSBzdF9zZXRfZ2VvbWV0cnkoUG9wXzIwMTEsICJnZW9tZXRyeSIpDQpQb3BfMjAyMSA8LSBzdF9zZXRfZ2VvbWV0cnkoUG9wXzIwMjEsICJnZW9tZXRyeSIpDQoNCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KDQpncmlkLmFycmFuZ2UoDQogIGdncGxvdCgpICsNCiAgICBnZW9tX3NmKGRhdGEgPSBQb3BfMjAxMSwgYWVzKGZpbGwgPSBmYWN0b3IobnRpbGUoUG9wXzIwMTEsIDUpKSksIGNvbG91ciA9IE5BKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcXVpbnRpbGVCcmVha3MoUG9wXzIwMTEsICJQb3BfMjAxMSIpLA0KICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiUXVpbnRpbGVcbkJyZWFrcyIpICsNCiAgICBsYWJzKHRpdGxlID0gIlBvcHVsYXRpb24gcGVyIENlbnN1cyBUcmFjdCBpbiBDb29rLCBEdSBQYWdlLCBhbmQgV2lsbCBDb3VudGllczogMjAxMSIpICsNCiAgICBtYXBUaGVtZSwNCg0KICBnZ3Bsb3QoKSArDQogICAgZ2VvbV9zZihkYXRhID0gUG9wXzIwMjEsIGFlcyhmaWxsID0gZmFjdG9yKG50aWxlKFBvcF8yMDIxLCA1KSkpLCBjb2xvdXIgPSBOQSkgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGU1LA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHF1aW50aWxlQnJlYWtzKFBvcF8yMDIxLCAiUG9wXzIwMjEiKSwNCiAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIlF1aW50aWxlXG5CcmVha3MiKSArDQogICAgbGFicyh0aXRsZSA9ICJQb3B1bGF0aW9uIHBlciBDZW5zdXMgVHJhY3QgaW4gQ29vaywgRHUgUGFnZSwgYW5kIFdpbGwgQ291bnRpZXM6IDIwMjEiKSArDQogICAgbWFwVGhlbWUsIA0KICBuY29sID0gMg0KKQ0KYGBgDQo8L2Rpdj4NCg0KTm93IHRvIHJlY29uY2lsZSB0cmFjdCBib3VuZGFyaWVzIGFuZCBmaXNobmV0IGdyaWQgY2VsbHMuIEnigJlkIGxpa2UgeW91IHRvIHBheSBwYXJ0aWN1bGFyIGF0dGVudGlvbiB0byB0aGlzIHByb2Nlc3MuDQoNCkEgc3BhdGlhbCBqb2luIHdvdWxkIGJlIGluYXBwcm9wcmlhdGUgYXMgaXQgd291bGQgYXNzaWduIHRoZSBzYW1lIHBvcHVsYXRpb24gdmFsdWUgZnJvbSBvbmUgdHJhY3QgdG8gdGhlIG1hbnkgaW50ZXJzZWN0aW5nIGdyaWQgY2VsbHMuIEluc3RlYWQsIHRoZSBhcmVhIHdlaWdodGVkIGludGVycG9sYXRpb24gZnVuY3Rpb24sIGBzdF9pbnRlcnBvbGF0ZV9hd2AsIGFzc2lnbnMgYSBwcm9wb3J0aW9uIG9mIGEgdHJhY3TigJlzIHBvcHVsYXRpb24gdG8gYSBncmlkIGNlbGwgd2VpZ2h0ZWQgYnkgdGhlIHByb3BvcnRpb24gb2YgdGhlIHRyYWN0IHRoYXQgaW50ZXJzZWN0cyB0aGUgZ3JpZCBjZWxsLiBUaGlzIHdvcmtzIGJlc3Qgb2YgY291cnNlLCB3aGVuIHdlIGFzc3VtZSB0aGF0IHRoZSB0cmFjdCBwb3B1bGF0aW9uIGlzIHVuaWZvcm1seSBkaXN0cmlidXRlZCBhY3Jvc3MgdGhlIHRyYWN0LiBUaGlzIGlzIHR5cGljYWxseSBub3QgYSBncmVhdCBhc3N1bXB0aW9uLiBIb3dldmVyLCBpdCBpcyBhIHJlYXNvbmFibGUgaGVyZSBwYXJ0aWN1bGFybHkgZ2l2ZW4gcG9wdWxhdGlvbiBpcyBhIGZlYXR1cmUgaW4gYSByZWdyZXNzaW9uIGFuZCBub3QgYW4gb3V0Y29tZSB0aGF0IG5lZWRzIHRvIGJlIG1lYXN1cmVkIHdpdGggc2lnbmlmaWNhbnQgcHJlY2lzaW9uLg0KDQpUaGUgQ2Vuc3VzIGRhdGEgYFBvcF8yMDExYCwgaGFzIGEgZGlmZmVyZW50IHNwYXRpYWwgZXh0ZW50IHRoYW4gYFRocmVlQ291bnR5X2Zpc2huZXRgLiBNb3N0IG5vdGFibHksIHRoZXJlIGFyZSBubyB2ZWN0b3JzIHdoZXJlIHdhdGVyIGlzIHByZXNlbnQuIFRvIG1haW50YWluIHRoZSBuZWVkZWQgMzg1MSBncmlkIGNlbGwgdW5pdHMgYChucm93KFRocmVlQ291bnR5X2Zpc2huZXQpKWAsIGEgYSB1bmlxdWUgaWQgaXMgY3JlYXRlZCwgYGZpc2huZXRJRGAuIFRoZW4gdGhlIGFyZWEgd2VpZ2h0ZWQgaW50ZXJwb2xhdGlvbiBpcyBwZXJmb3JtZWQgb24gYHBvcHVsYXRpb25gIGZvciB0aGUgMjAxMSBhbmQgMjAyMSBsYXllcnMuIEZpbmFsbHksIHRoZSByZXN1bHRzIGFyZSBqb2luZWQgYmFjayAoYGxlZnRfam9pbmApIHRvIGBUaHJlZUNvdW50eV9maXNobmV0YC4gVGhpcyBhcHByb2FjaCBtYWludGFpbnMgYSBjb25zaXN0ZW50IHNwYXRpYWwgZXh0ZW50Lg0KDQpJdCB3b3VsZCBiZSBoZWxwZnVsIGZvciB5b3UgdG8gc3BlbmQgc29tZSB0aW1lIHJ1bm5pbmcgdGhyb3VnaCBlYWNoIGNvZGUgYmxvY2sgbGluZSBieSBsaW5lLiBBcmVhbCB3ZWlnaHRlZCBpbnRlcnBvbGF0aW9uIGlzIGEgcmVhbGx5IHN0cm9uZyBzcGF0aWFsIGFuYWx5c2lzIHNraWxsIHRvIGhhdmUuIFlvdSBjYW4gZG8gdGhpcyBpbiBBcmNHSVMgYnV0IHRoZXJlIGlzIG5vdCBhdXRvbWF0ZWQgYXBwcm9hY2guDQoNCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NClRocmVlQ291bnR5X2Zpc2huZXQgPC0NCiAgVGhyZWVDb3VudHlfZmlzaG5ldCAlPiUNCiAgcm93bmFtZXNfdG9fY29sdW1uKCJmaXNobmV0SUQiKSAlPiUgDQogIG11dGF0ZShmaXNobmV0SUQgPSBhcy5udW1lcmljKGZpc2huZXRJRCkpICU+JQ0KICBkcGx5cjo6c2VsZWN0KGZpc2huZXRJRCkNCg0KZmlzaG5ldFBvcHVsYXRpb24xMSA8LQ0KICBzdF9pbnRlcnBvbGF0ZV9hdyhQb3BfMjAxMVsiUG9wXzIwMTEiXSwgVGhyZWVDb3VudHlfZmlzaG5ldCwgZXh0ZW5zaXZlPVRSVUUpICU+JQ0KICBhcy5kYXRhLmZyYW1lKC4pICU+JQ0KICByb3duYW1lc190b19jb2x1bW4odmFyID0gImZpc2huZXRJRCIpICU+JQ0KICBsZWZ0X2pvaW4oVGhyZWVDb3VudHlfZmlzaG5ldCAlPiUNCiAgICAgICAgICAgICAgbXV0YXRlKGZpc2huZXRJRCA9IGFzLmNoYXJhY3RlcihmaXNobmV0SUQpKSwNCiAgICAgICAgICAgIC4sIGJ5PWMoImZpc2huZXRJRCI9J2Zpc2huZXRJRCcpKSAlPiUgDQogIG11dGF0ZShQb3BfMjAxMSA9IHJlcGxhY2VfbmEoUG9wXzIwMTEsMCkpICU+JQ0KICBkcGx5cjo6c2VsZWN0KFBvcF8yMDExKQ0KDQpmaXNobmV0UG9wdWxhdGlvbjIxIDwtDQogIHN0X2ludGVycG9sYXRlX2F3KFBvcF8yMDIxWyJQb3BfMjAyMSJdLFRocmVlQ291bnR5X2Zpc2huZXQsIGV4dGVuc2l2ZT1UUlVFKSAlPiUNCiAgYXMuZGF0YS5mcmFtZSguKSAlPiUNCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJmaXNobmV0SUQiKSAlPiUNCiAgbGVmdF9qb2luKFRocmVlQ291bnR5X2Zpc2huZXQgJT4lDQogICAgICAgICAgICAgIG11dGF0ZShmaXNobmV0SUQgPSBhcy5jaGFyYWN0ZXIoZmlzaG5ldElEKSksDQogICAgICAgICAgICAuLCBieT1jKCJmaXNobmV0SUQiPSdmaXNobmV0SUQnKSkgJT4lIA0KICBtdXRhdGUoUG9wXzIwMjEgPSByZXBsYWNlX25hKFBvcF8yMDIxLDApKSAlPiUNCiAgZHBseXI6OnNlbGVjdChQb3BfMjAyMSkNCg0KZmlzaG5ldFBvcHVsYXRpb24gPC0gDQogIGNiaW5kKGZpc2huZXRQb3B1bGF0aW9uMTEsZmlzaG5ldFBvcHVsYXRpb24yMSkgJT4lDQogIGRwbHlyOjpzZWxlY3QoUG9wXzIwMTEsUG9wXzIwMjEpICU+JQ0KICBtdXRhdGUocG9wX0NoYW5nZSA9IFBvcF8yMDIxIC0gUG9wXzIwMTEpDQpgYGANCg0KRm9yIGNvbXBhcmlzb24gcHVycG9zZXMsIGJvdGggdGhlIDIwMjEgY2Vuc3VzIHRyYWN0IGdlb21ldHJpZXMgYW5kIHRoZSBwb3B1bGF0aW9uIHdlaWdodGVkIGdyaWQgY2VsbHMgYXJlIHBsb3R0ZWQuDQoNCjxkaXYgY2xhc3M9InN1cGVyYmlnaW1hZ2UiPg0KYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcuaGVpZ2h0ID0gOCwgZmlnLndpZHRoPSAxMX0NCmdyaWQuYXJyYW5nZSgNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhPVBvcF8yMDIxLCBhZXMoZmlsbD1mYWN0b3IobnRpbGUoUG9wXzIwMjEsNSkpKSxjb2xvdXI9TkEpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsDQogICAgICAgICAgICAgICAgICAgIGxhYmVscz1zdWJzdHIocXVpbnRpbGVCcmVha3MoUG9wXzIwMjEsIlBvcF8yMDIxIiksMSw0KSwNCiAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZVxuQnJlYWtzIikgKw0KICBsYWJzKHRpdGxlPSJQb3B1bGF0aW9uIGZvciBDb29rLCBEdSBQYWdlLCBhbmQgV2lsbCBDb3VudGllczogMjAyMSIsDQogICAgICAgc3VidGl0bGU9IlJlcHJlc2VudGVkIGFzIHRyYWN0czsgQm91bmRhcmllcyBvbWl0dGVkIikgKw0KICBtYXBUaGVtZSwNCg0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGE9ZmlzaG5ldFBvcHVsYXRpb24sIGFlcyhmaWxsPWZhY3RvcihudGlsZShQb3BfMjAyMSw1KSkpLGNvbG91cj1OQSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwNCiAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKGZpc2huZXRQb3B1bGF0aW9uLCJQb3BfMjAyMSIpLDEsNCksDQogICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGVcbkJyZWFrcyIpICsNCiAgbGFicyh0aXRsZT0iUG9wdWxhdGlvbiBmb3IgQ29vaywgRHUgUGFnZSwgYW5kIFdpbGwgQ291bnRpZXM6IDIwMjEiLA0KICAgICAgIHN1YnRpdGxlPSJSZXByZXNlbnRlZCBhcyBmaXNobmV0IGdyaWRjZWxsczsgQm91bmRhcmllcyBvbWl0dGVkIikgKw0KICBtYXBUaGVtZSwgbmNvbD0yKQ0KYGBgDQo8L2Rpdj4NCg0KIyMgMi41LiBIaWdod2F5IERpc3RhbmNlDQoNCkFjY2Vzc2liaWxpdHkgaXMgYSBrZXkgZGV0ZXJtaW5hbnQgb2YgZGV2ZWxvcG1lbnQgcG90ZW50aWFsIHBhcnRpY3VsYXJseSBpbiBhIHNwcmF3bGluZyBjaXR5IGxpa2UgQ2hpY2Fnby4gIEFjY2Vzc2liaWxpdHkgZmVhdHVyZXMgYXJlIGVuZ2luZWVyZWQgYnkgbWVhc3VyaW5nIGRpc3RhbmNlIGZyb20gZWFjaCBncmlkIGNlbGwgdG8gaXRzIG5lYXJlc3QgaGlnaHdheS4NCg0KRmlyc3QgaGlnaHdheSB2ZWN0b3JzIGFyZSBkb3dubG9hZGVkIGZyb20gdGhlIFUuUy4gQ2Vuc3VzIFRJR0VSIExpbmUgMjAxOSBkYXRhc2V0cyBpbiBhIHNoYXBlZmlsZSBmb3JtYXQ7IHByb2plY3RlZCBhbmQgc3Vic2V0IHRvIHRoZSBzdWJzZXQgdXNpbmcgYHN0X2ludGVyc2VjdGlvbmAuIEJlbG93LCBuZXcgZGV2ZWxvcG1lbnQgaXMgbWFwcGVkIHdpdGggdGhlIGhpZ2h3YXkgb3ZlcmxheS4NCg0KYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCByZXN1bHRzID0gImhpZGUifQ0KVGhyZWVDb3VudGllc19IaWdod2F5cyA8LQ0KICBzdF9yZWFkKCJDOi9Vc2Vycy8zbHBhdy9EZXNrdG9wL0FyY0dJUyBQcm8gMy4yL0Vudk1vZGVsaW5nLzA0XzI0XzI0X1VyYmFuR3Jvd3RoTW9kZWxpbmcvRG93bmxvYWRlZF9EYXRhL0lMX2hpZ2h3YXlzL0lMX2hpZ2h3YXlzL3RsXzIwMTlfMTdfcHJpc2Vjcm9hZHMuc2hwIikgJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnMoVGhyZWVDb3VudHlBcmVhKSkgJT4lDQogIHN0X2ludGVyc2VjdGlvbihUaHJlZUNvdW50eUFyZWEpDQpgYGANCg0KYGBge3IgcGxvdF9oaWdod2F5X2RldmNoYW5nZSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPSBGQUxTRX0NCmdncGxvdCgpICsNCiAgZ2VvbV9wb2ludChkYXRhPWZpc2huZXQsIA0KICAgICAgICAgICAgIGFlcyh4PXh5QyhmaXNobmV0KVssMV0sIHk9eHlDKGZpc2huZXQpWywyXSxjb2xvdXI9RGV2X2NoYW5nZSksc2l6ZT0xLjUpICsNCiAgZ2VvbV9zZihkYXRhPVRocmVlQ291bnRpZXNfSGlnaHdheXMsIGNvbG91ciA9ICJyZWQiKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5ldyBEZXZlbG9wbWVudCIpKSArDQogIGxhYnModGl0bGUgPSAiTmV3IERldmVsb3BtZW50IGFuZCBIaWdod2F5cyIsDQogICAgICAgc3VidGl0bGUgPSAiQXMgZmlzaG5ldCBjZW50cm9pZHMiKSArDQogIG1hcFRoZW1lDQpgYGANCg0KQmVsb3cgYXJlIHNvbWUgZ3JlYXQgci1iYXNlZCByYXN0ZXIgc2tpbGxzLiBUaGUgZGlzdGFuY2UgZnJvbSBlYWNoIGdyaWQgY2VsbCB0byBpdHMgbmVhcmVzdCBoaWdod2F5IHNlZ21lbnQgaXMgbWVhc3VyZWQuDQoNCkZpcnN0LCB5b3UgY2FuIGNvbnZlcnQgYSB0aGUgaGlnaHdheSBsYXllciB0byByYXN0ZXIuIFRoaXMgY2FuIGJlIGRvbmUgYnkgY3JlYXRpbmcgYW4gYGVtcHR5UmFzdGVyYCBvZiBgTkFgIGdyaWQgY2VsbHMgYXQgdGhlIHNhbWUgc3BhdGlhbCBleHRlbnQgYXMgYERldmVsb3BtZW50X2NoYW5nZWAuIFRoZW4sIGBoaWdod2F5X3Jhc3RlcmAgaXMgY3JlYXRlZCBieSBjb252ZXJ0aW5nIGBUaHJlZUNvdW50aWVzX0hpZ2h3YXlzYCB0byBgc3BgIGZvcm0gYW5kIHRoZW4gdG8gYXBwbHlpbmcgYHJhc3Rlcml6ZWAuIFRoZSByYXN0ZXIgaXMgdGhlbiBjb252ZXJ0ZWQgdG8gcG9pbnRzIHdpdGggYHJhc3RlclRvUG9pbnRzYCBhbmQgYHN0X2FzX3NmYCwgdGhlbiBgYWdncmVnYXRlYCBpcyB1c2VkIHRvIGNhbGN1bGF0ZSBtZWFuIGRpc3RhbmNlIGJ5IGdyaWQgY2VsbC4NCg0KWW91IG1heSAoYnV0IGxpa2VseSBub3QpIGJlIGludGVyZXN0ZWQgaW4gbGVhcm5pbmcgdGhhdCBgc3BgIGlzIHRoZSBvbGRlciBzcGF0aWFsIGRhdGEgY29udmVudGlvbiBpbiBSLiBBbHRob3VnaCBgc2ZgIGlzIHRoZSBuZXcgY29udmVudGlvbiwgcmFzdGVyL3ZlY3RvciBpbnRlcmFjdGlvbnMgc3RpbGwgcmVxdWlyZSBgc3BgLiBUaGUgYGFzYCBmdW5jdGlvbiBjb252ZXJ0cy4NCg0KSW5zdGVhZCwgd2UgdXNlZCBhIHNsaWdodGx5IGRpZmZlcmVudCBtZXRob2QgdG8gY2FsY3VsYXRlIGRpc3RhbmNlIHRvIGhpZ2h3YXlzLiBIZXJlIGlzIHRoZSBjb2RlIHdlIGRpZG4ndCB1c2U6DQoNCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCiMgT3JpZ2luYWwgY29kZSAobm90IHVzaW5nKToNCiMgZW1wdHlSYXN0ZXIgPC0gRGV2ZWxvcG1lbnRfY2hhbmdlDQojIGVtcHR5UmFzdGVyW10gPC0gTkENCiMgDQojIGhpZ2h3YXlfcmFzdGVyIDwtIA0KIyAgIGFzKFRocmVlQ291bnRpZXNfSGlnaHdheXMsJ1NwYXRpYWwnKSAlPiUNCiMgICByYXN0ZXJpemUoLixlbXB0eVJhc3RlcikNCiMgDQojIGhpZ2h3YXlfcmFzdGVyX2Rpc3RhbmNlIDwtIGRpc3RhbmNlKGhpZ2h3YXlfcmFzdGVyKQ0KIyBuYW1lcyhoaWdod2F5X3Jhc3Rlcl9kaXN0YW5jZSkgPC0gImRpc3RhbmNlX2hpZ2h3YXlzIg0KIyANCiMgaGlnaHdheVBvaW50cyA8LQ0KIyAgIHJhc3RlclRvUG9pbnRzKGhpZ2h3YXlfcmFzdGVyX2Rpc3RhbmNlKSAlPiUNCiMgICBhcy5kYXRhLmZyYW1lKCkgJT4lDQojICAgc3RfYXNfc2YoY29vcmRzID0gYygieCIsICJ5IiksIGNycyA9IHN0X2NycyhUaHJlZUNvdW50eV9maXNobmV0KSkNCiMgDQojIGhpZ2h3YXlQb2ludHNfZmlzaG5ldCA8LSANCiMgICBhZ2dyZWdhdGUoaGlnaHdheVBvaW50cywgVGhyZWVDb3VudHlfZmlzaG5ldCwgbWVhbikgJT4lDQojICAgbXV0YXRlKGRpc3RhbmNlX2hpZ2h3YXlzID0gaWZlbHNlKGlzLm5hKGRpc3RhbmNlX2hpZ2h3YXlzKSwwLGRpc3RhbmNlX2hpZ2h3YXlzKSkNCiMgDQojIGdncGxvdCgpICsNCiMgICBnZW9tX3NmKGRhdGE9VGhyZWVDb3VudHlBcmVhKSArDQojICAgZ2VvbV9wb2ludChkYXRhPWhpZ2h3YXlQb2ludHNfZmlzaG5ldCwgYWVzKHg9eHlDKGhpZ2h3YXlQb2ludHNfZmlzaG5ldClbLDFdLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeT14eUMoaGlnaHdheVBvaW50c19maXNobmV0KVssMl0sIA0KIyAgICAgICAgICAgICAgICAgIGNvbG91cj1mYWN0b3IobnRpbGUoZGlzdGFuY2VfaGlnaHdheXMsNSkpKSxzaXplPTEuNSkgKw0KIyAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsDQojICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKGhpZ2h3YXlQb2ludHNfZmlzaG5ldCwiZGlzdGFuY2VfaGlnaHdheXMiKSwxLDgpLA0KIyAgICAgICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGVcbkJyZWFrcyIpICsNCiMgICBnZW9tX3NmKGRhdGE9aG91c3RvbkhpZ2h3YXlzLCBjb2xvdXIgPSAicmVkIikgKw0KIyAgIGxhYnModGl0bGUgPSAiRGlzdGFuY2UgdG8gSGlnaHdheXMiLA0KIyAgICAgICAgc3VidGl0bGUgPSAiQXMgZmlzaG5ldCBjZW50cm9pZHM7IEhpZ2h3YXlzIHZpc3VhbGl6ZWQgaW4gcmVkIikgKw0KIyAgIG1hcFRoZW1lDQpgYGANCg0KSW5zdGVhZCwgd2UgcmVhZCBpbiB0aGUgaGlnaHdheXMgcmFzdGVyIGFuZCB0aGUgVGhyZWVDb3VudHlfZmlzaG5ldCBhbmQgdXNlZCBzdF9uZWFyZXN0X2ZlYXR1cmUgdG8gY2FsY3VsYXRlIHRoZSBkaXN0YW5jZSBmcm9tIGVhY2ggZmlzaG5ldCBjZWxsIGNlbnRyb2lkIHRvIHRoZSBuZWFyZXN0IGhpZ2h3YXkuIFRoZSB2YXJpYWJsZSBoaWdod2F5X2Rpc3QgaW5jbHVkZXMgdGhlc2UgY2FsY3VsYXRlZCBkaXN0YW5jZXMgdG8gdGhlIG5lYXJlc3QgaGlnaHdheS4NCg0KYGBge3J9DQojUmVhZCBpbiBoaWdod2F5cyBhbmQgdHJhbnNmb3JtIHRvIGFwcHJvcHJpYXRlIHByb2plY3Rpb24NCiMgSXQncyBjYWxsZWQgIlRocmVlQ291bnRpZXNfSGlnaHdheXMiIC0gd2UgcmVhZCB0aGlzIGluIGVhcmxpZXIgaW4gdGhlIGNvZGUNCg0KI0NvbnZlcnQgZmlzaG5ldCB0byBjZW50cm9pZHMNCmNlbnRyb2lkIDwtIFRocmVlQ291bnR5X2Zpc2huZXQgJT4lDQogIHN0X2NlbnRyb2lkKCkNCg0KI0RldGVybWluZSBuZXJlYXN0IGhpZ2h3YXkgdG8gZWFjaCBjZW50cm9pZA0KbmVhcmVzdF9mZWF0IDwtIHN0X25lYXJlc3RfZmVhdHVyZShjZW50cm9pZCxUaHJlZUNvdW50aWVzX0hpZ2h3YXlzKQ0KDQojQ2FsY3VhdGUgZGlzdGFuY2UgZnJvbSBlYWNoIGdyaWQgc3F1YXJlIGNlbnRyb2lkIHRvIG5lYXJlc3QgaGlnaHdheQ0KVGhyZWVDb3VudHlfZmlzaG5ldCRoaWdod2F5X2Rpc3QgPC0gYXMuZG91YmxlKHN0X2Rpc3RhbmNlKGNlbnRyb2lkLCBUaHJlZUNvdW50aWVzX0hpZ2h3YXlzW25lYXJlc3RfZmVhdCxdLCBieV9lbGVtZW50PVRSVUUpKQ0KDQpIaWdod2F5X2Zpc2huZXQgPC0gVGhyZWVDb3VudHlfZmlzaG5ldA0KDQojTWFrZSBhIHF1aWNrIHNhbXBsZSBtYXAgb2YgdGhlIHJlc3VsdHMNCmdncGxvdCgpKw0KICBnZW9tX3NmKGRhdGE9SGlnaHdheV9maXNobmV0LGFlcyhmaWxsPWhpZ2h3YXlfZGlzdCksY29sb3I9J3RyYW5zcGFyZW50JykrDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG5hbWU9J0Rpc3RhbmNlIHRvIEhpZ2h3YXkgKGZlZXQpJykrDQogIGdlb21fc2YoZGF0YT1UaHJlZUNvdW50aWVzX0hpZ2h3YXlzLGNvbG9yPSdyZWQnKSsNCiAgbGFicyh0aXRsZSA9ICJEaXN0YW5jZSB0byBIaWdod2F5cyIsDQogICAgICAgc3VidGl0bGUgPSAiVXNpbmcgZmlzaG5ldCBjZW50cm9pZHMiKSArDQogIHRoZW1lX3ZvaWQoKQ0KYGBgDQoNClRoZXJlIGFyZSBhIGxvdCBvZiBmZWRlcmFsIGFuZCBzdGF0ZSBoaWdod2F5cyBpbiB0aGUgdGhyZWUgY291bnR5IGFyZWEsIHBhcnRpY3VsYXJseSBjbG9zZXIgdG8gQ2hpY2Fnby4gVGhlcmUgYXJlIHNvbWUgbW9yZSBydXJhbCBhbmQgc3VidXJiYW4gYXJlYXMgaW4gdGhlIHNvdXRoZXJuIHBvcnRpb24gb2YgdGhlIHN0dWR5IGFyZWEgdGhhdCBhcmUgZnVydGhlciBmcm9tIGhpZ2h3YXlzLg0KDQoNCiMjIDIuNS5iIERpc3RhbmNlIHRvIFJlZ2lvbmFsIFJhaWwgTGluZXMgKE1ldHJhKQ0KDQpUcmFuc2l0IGFjY2Vzc2liaWxpdHkgaXMgYSBrZXkgZGV0ZXJtaW5hbnQgb2YgZGV2ZWxvcG1lbnQgcG90ZW50aWFsIHBhcnRpY3VsYXJseSBpbiBhIGNpdHkgbGlrZSBDaGljYWdvLiBUcmFuc2l0LW9yaWVudGVkIGRldmVsb3BtZW50IGlzIGNvbW1vbmx5IHByaW9yaXRpemVkIHRvIGNyZWF0ZSBjb21wYWN0IGRldmVsb3BtZW50LiBBY2Nlc3NpYmlsaXR5IGZlYXR1cmVzIGFyZSBlbmdpbmVlcmVkIGJ5IG1lYXN1cmluZyBkaXN0YW5jZSBmcm9tIGVhY2ggZ3JpZCBjZWxsIHRvIGl0cyBuZWFyZXN0IHJlZ2lvbmFsIHJhaWwgbGluZS4NCg0KRmlyc3QgcmVnaW9uYWwgcmFpbCBsaW5lIHZlY3RvcnMgYXJlIGRvd25sb2FkZWQgZnJvbSB0aGUgQ2l0eSBvZiBDaGljYWdvIG9wZW4gZGF0YSB3ZWJzaXRlIGluIGBzaHBgIGZvcm1hdDsgcHJvamVjdGVkIGFuZCBzdWJzZXQgdG8gdGhlIHN1YnNldCB1c2luZyBgc3RfaW50ZXJzZWN0aW9uYC4gQmVsb3csIG5ldyBkZXZlbG9wbWVudCBpcyBtYXBwZWQgd2l0aCB0aGUgcmVnaW9uYWwgcmFpbCBsaW5lIG92ZXJsYXkuIFRoZSByZWdpb25hbCByYWlsIGxpbmVzIGFyZSBhIHBhcnQgb2YgdGhlIE1ldHJhIHN5c3RlbSBmb3IgdGhlIENoaWNhZ29sYW5kIHJlZ2lvbi4NCg0KDQpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHJlc3VsdHMgPSAiaGlkZSJ9DQpUaHJlZUNvdW50aWVzX1JlZ1JhaWwgPC0NCiAgc3RfcmVhZCgiQzovVXNlcnMvM2xwYXcvRGVza3RvcC9BcmNHSVMgUHJvIDMuMi9FbnZNb2RlbGluZy8wNF8yNF8yNF9VcmJhbkdyb3d0aE1vZGVsaW5nL0Rvd25sb2FkZWRfRGF0YS9NZXRyYV9MaW5lcy9NZXRyYUxpbmVzc2hwLnNocCIpICU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKFRocmVlQ291bnR5QXJlYSkpICU+JQ0KICBzdF9pbnRlcnNlY3Rpb24oVGhyZWVDb3VudHlBcmVhKQ0KYGBgDQoNCk5leHQsIHdlIG1hcCB0aGUgZXhpc3RpbmcgcmVnaW9uYWwgcmFpbCBsaW5lcyB3aXRoIG91ciBhbmFseXNpcyBvZiBkZXZlbG9wbWVudCBjaGFuZ2UgZnJvbSAyMDExLTIwMjEuDQoNCmBgYHtyIHBsb3RfaGlnaHdheSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPSBGQUxTRX0NCmdncGxvdCgpICsNCiAgZ2VvbV9wb2ludChkYXRhPWZpc2huZXQsIA0KICAgICAgICAgICAgIGFlcyh4PXh5QyhmaXNobmV0KVssMV0sIHk9eHlDKGZpc2huZXQpWywyXSxjb2xvdXI9RGV2X2NoYW5nZSksc2l6ZT0xLjUpICsNCiAgZ2VvbV9zZihkYXRhPVRocmVlQ291bnRpZXNfUmVnUmFpbCwgY29sb3VyID0gInJlZCIpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwNCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiTm8gQ2hhbmdlIiwiTmV3IERldmVsb3BtZW50IikpICsNCiAgbGFicyh0aXRsZSA9ICJOZXcgRGV2ZWxvcG1lbnQgYW5kIFJlZ2lvbmFsIFJhaWwgTGluZXMiLA0KICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzIikgKw0KICBtYXBUaGVtZQ0KYGBgDQoNCk5leHQsIHdlIGNhbGN1bGF0ZSB0aGUgZGlzdGFuY2UgdG8gdGhlIG5lYXJlc3QgcmVnaW9uYWwgcmFpbCBsaW5lIGZvciBlYWNoIGZpc2huZXQgY2VsbCBjZW50cm9pZCBhbmQgbWFwIGl0LiBUaGlzIHZhcmlhYmxlIGlzIGNhbGxlZCBSZWdSYWlsX2Rpc3QuDQoNCmBgYHtyfQ0KI0RldGVybWluZSBuZWFyZXN0IHJhaWwgbGluZSB0byBlYWNoIGNlbnRyb2lkDQpuZWFyZXN0X3JhaWwgPC0gc3RfbmVhcmVzdF9mZWF0dXJlKGNlbnRyb2lkLFRocmVlQ291bnRpZXNfUmVnUmFpbCkNCg0KI0NhbGN1YXRlIGRpc3RhbmNlIGZyb20gZWFjaCBncmlkIHNxdWFyZSBjZW50cm9pZCB0byBuZWFyZXN0IHJhaWwgbGluZQ0KVGhyZWVDb3VudHlfZmlzaG5ldCRyZWdyYWlsX2Rpc3QgPC0gYXMuZG91YmxlKHN0X2Rpc3RhbmNlKGNlbnRyb2lkLCBUaHJlZUNvdW50aWVzX1JlZ1JhaWxbbmVhcmVzdF9yYWlsLF0sIGJ5X2VsZW1lbnQ9VFJVRSkpDQoNClJlZ1JhaWxfZmlzaG5ldCA8LSBUaHJlZUNvdW50eV9maXNobmV0ICMlPiUNCiMgIHNlbGVjdChmaXNobmV0SUQsIGdlb21ldHJ5LCByZWdyYWlsX2Rpc3QpDQoNCiNNYWtlIGEgcXVpY2sgc2FtcGxlIG1hcCBvZiB0aGUgcmVzdWx0cw0KZ2dwbG90KCkrDQogIGdlb21fc2YoZGF0YT1SZWdSYWlsX2Zpc2huZXQsYWVzKGZpbGw9cmVncmFpbF9kaXN0KSxjb2xvcj0ndHJhbnNwYXJlbnQnKSsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MobmFtZT0nRGlzdGFuY2UgdG8gUmVnaW9uYWwgUmFpbCBMaW5lcyAoZmVldCknKSsNCiAgZ2VvbV9zZihkYXRhPVRocmVlQ291bnRpZXNfUmVnUmFpbCxjb2xvcj0ncmVkJykrDQogIGxhYnModGl0bGUgPSAiRGlzdGFuY2UgdG8gUmVnaW9uYWwgUmFpbCBMaW5lcyIsDQogICAgICAgc3VidGl0bGUgPSAiVXNpbmcgZmlzaG5ldCBjZW50cm9pZHMiKSArDQogIHRoZW1lX3ZvaWQoKQ0KYGBgDQoNCkFzIHlvdSBjYW4gc2VlLCBtb3N0IG9mIHRoZSB0aHJlZSBjb3VudHkgYXJlYSBpcyB3aXRoaW4gMjUsMDAwIGZlZXQgKG9yIGEgbGl0dGxlIG92ZXIgNC41IG1pbGVzKSBvZiBhIHJlZ2lvbmFsIHJhaWwgbGluZS4gVGhpcyBpcyB0b28gbGFyZ2Ugb2YgYSBkaXN0YW5jZSBmb3Igd2Fsa2luZyBjb21tdXRlcnMsIGJ1dCBpdCBpcyBhY2Nlc3NpYmxlIGZvciBwZW9wbGUgd2hvIGJpa2Ugb3IgZHJpdmUgb3IgdGFrZSBvdGhlciBtb2RlcyB0byB0aGUgcmVnaW9uYWwgcmFpbCBzdGF0aW9ucyB0byBhY2Nlc3MgdGhlIENoaWNhZ28gY2l0eSByZWdpb24uIFRvIGltcHJvdmUgaG91c2luZyBkZW5zaXR5LCBpdCBpcyBpZGVhbCB0byBjcmVhdGUgbW9yZSBkZXZlbG9wbWVudHMgaW4gY2xvc2UgcHJveGltaXR5IHRvIHJlZ2lvbmFsIHJhaWwgc3RhdGlvbnMsIGlkZWFsbHkgd2l0aGluIGEgaGFsZiBtaWxlIGZvciB3YWxrZXJzIG9yIGEgbWlsZSBvciBtb3JlIGZvciBiaWtlL2J1cy9jYXIvb3RoZXIgbW9kYWwgY29tbXV0ZXMuIA0KDQoNCiMgMi41LmMgRGlzdGFuY2UgdG8gRG93bnRvd24gQ2hpY2FnbyAoVGhlIExvb3ApDQoNClByb3hpbWl0eSB0byBlbXBsb3ltZW50IGNlbnRlcnMgaXMgYSBrZXkgZGV0ZXJtaW5hbnQgb2YgZGV2ZWxvcG1lbnQgcGFydGljdWxhcmx5IGluIGEgbWFqb3IgY2l0eSBsaWtlIENoaWNhZ28gYW5kIGl0cyBzdXJyb3VuZGluZyBjb21tdXRlciBzdWJ1cmJzLiBGZWF0dXJlcyBhcmUgZW5naW5lZXJlZCBieSBtZWFzdXJpbmcgZGlzdGFuY2UgZnJvbSBlYWNoIGdyaWQgY2VsbCB0byBkb3dudG93biBDaGljYWdvLCBhbHNvIGtub3duIGFzIHRoZSBMb29wLg0KDQpGaXJzdCBhIHBvbHlnb24gZmVhdHVyZSBpcyB0cmFjZWQgYXJvdW5kIERvd250b3duIENoaWNhZ28gdXNpbmcgQXJjR0lTIFByby4gQmVsb3csIG5ldyBkZXZlbG9wbWVudCBpcyBtYXBwZWQgd2l0aCB0aGUgRG93bnRvd25fQ2hpY2FnbyBmZWF0dXJlIG92ZXJsYXkuDQoNCmBgYHtyfQ0KVGhyZWVDb3VudGllc19Eb3dudG93biA8LQ0KIHN0X3JlYWQoIkM6L1VzZXJzLzNscGF3L0Rlc2t0b3AvQXJjR0lTIFBybyAzLjIvRW52TW9kZWxpbmcvMDRfMjRfMjRfVXJiYW5Hcm93dGhNb2RlbGluZy9Eb3dubG9hZGVkX0RhdGEvRG93bnRvd25fQ2hpY2Fnby9Eb3dudG93bl9DaGljYWdvLnNocCIpICU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKFRocmVlQ291bnR5QXJlYSkpICU+JQ0KICBzdF9pbnRlcnNlY3Rpb24oVGhyZWVDb3VudHlBcmVhKQ0KYGBgDQoNCk5leHQsIHdlIHBsb3QgdGhlIENoaWNhZ28gTG9vcCBhcmVhIHNlZW4gaW4gcmVkIGFuZCBzaG93IHRoZSBkZXZlbG9wbWVudCBjaGFuZ2UgZnJvbSAyMDExLTIwMjEgaW4gdGhlIHRocmVlIGNvdW50eSBhcmVhLg0KDQpgYGB7cn0NCmdncGxvdCgpICsNCiAgZ2VvbV9wb2ludChkYXRhPWZpc2huZXQsIA0KICAgICAgICAgICAgIGFlcyh4PXh5QyhmaXNobmV0KVssMV0sIHk9eHlDKGZpc2huZXQpWywyXSxjb2xvdXI9RGV2X2NoYW5nZSksc2l6ZT0xLjUpICsNCiAgZ2VvbV9zZihkYXRhPVRocmVlQ291bnRpZXNfRG93bnRvd24sIGNvbG91ciA9ICJibGFjayIsIGZpbGwgPSAicmVkIikgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSkgKw0KICBsYWJzKHRpdGxlID0gIk5ldyBEZXZlbG9wbWVudCBhbmQgUHJveGltaXR5IHRvIERvd250b3duIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkcyIpICsNCiAgbWFwVGhlbWUNCmBgYA0KDQpOZXh0LCB3ZSBwbG90IHRoZSBwcm94aW1pdHkgdG8gZG93bnRvd24gdmFyaWFibGUgKGRvd250b3duX2Rpc3QpIHVzaW5nIGZpc2huZXQgY2VudHJvaWRzIGFuZCBtYXAgaXQuIA0KDQpgYGB7cn0NCmxpYnJhcnkoc2NhbGVzKQ0KDQojRGV0ZXJtaW5lIGRpc3RhbmNlIHRvIENpdHkgQ2VudGVyIGNlbnRyb2lkDQpuZWFyX2Rvd250b3duIDwtIHN0X25lYXJlc3RfZmVhdHVyZShjZW50cm9pZCxUaHJlZUNvdW50aWVzX0Rvd250b3duKQ0KDQojQ2FsY3VsYXRlIGRpc3RhbmNlIGZyb20gZWFjaCBncmlkIHNxdWFyZSBjZW50cm9pZCB0byB0aGUgY2l0eSBjZW50ZXIgY2VudHJvaWQNClRocmVlQ291bnR5X2Zpc2huZXQkZG93bnRvd25fZGlzdCA8LSBhcy5kb3VibGUoc3RfZGlzdGFuY2UoY2VudHJvaWQsIFRocmVlQ291bnRpZXNfRG93bnRvd25bbmVhcl9kb3dudG93bixdLCBieV9lbGVtZW50PVRSVUUpKQ0KDQpEb3dudG93bl9maXNobmV0IDwtIFRocmVlQ291bnR5X2Zpc2huZXQgDQojJT4lICAgc2VsZWN0KGZpc2huZXRJRCwgZ2VvbWV0cnksIGRvd250b3duX2Rpc3QpDQoNCmdncGxvdCgpKw0KZ2VvbV9zZihkYXRhPURvd250b3duX2Zpc2huZXQsYWVzKGZpbGw9ZG93bnRvd25fZGlzdCksY29sb3I9J3RyYW5zcGFyZW50JykrDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG5hbWU9J1Byb3hpbWl0eSB0byBEb3dudG93biAoZmVldCknLCBsYWJlbHMgPSBjb21tYSkrDQogIGdlb21fc2YoZGF0YT1UaHJlZUNvdW50aWVzX0Rvd250b3duLGNvbG9yPSJibGFjayIsIGZpbGwgPSAicmVkIikrDQogIGxhYnModGl0bGUgPSAiRGlzdGFuY2UgdG8gRG93bnRvd24gQ2hpY2FnbyIsDQogICAgICAgc3VidGl0bGUgPSAiVXNpbmcgZmlzaG5ldCBjZW50cm9pZHMiKSArDQogIHRoZW1lX3ZvaWQoKQ0KYGBgDQoNCg0KDQojIDIuNS5kIERpc3RhbmNlIHRvIExha2UgTWljaGlnYW4NCg0KUHJveGltaXR5IHRvIHRoZSB3YXRlcmZyb250IGlzIGEga2V5IGRldGVybWluYW50IG9mIGRldmVsb3BtZW50IHBhcnRpY3VsYXJseSBpbiBhIG1ham9yIGNpdHkgbGlrZSBDaGljYWdvIGFuZCBpdHMgc3Vycm91bmRpbmcgY29tbXV0ZXIgc3VidXJicy4gRmVhdHVyZXMgYXJlIGVuZ2luZWVyZWQgYnkgbWVhc3VyaW5nIGRpc3RhbmNlIGZyb20gZWFjaCBncmlkIGNlbGwgdG8gdGhlIExha2UgTWljaGlnYW4gc2hvcmVsaW5lLg0KDQpCZWxvdywgbmV3IGRldmVsb3BtZW50IGlzIG1hcHBlZCB3aXRoIHRoZSBMYWtlIE1pY2hpZ2FuIHNob3JlbGluZSBmZWF0dXJlIG92ZXJsYXksIHdoaWNoIGlzIHNob3duIGluIHJlZC4NCg0KYGBge3J9DQpUaHJlZUNvdW50aWVzX0xha2VNaWNoaWdhbiA8LQ0KIHN0X3JlYWQoIkM6L1VzZXJzLzNscGF3L0Rlc2t0b3AvQXJjR0lTIFBybyAzLjIvRW52TW9kZWxpbmcvMDRfMjRfMjRfVXJiYW5Hcm93dGhNb2RlbGluZy9Eb3dubG9hZGVkX0RhdGEvTGFrZV9NaWNoaWdhbl9TaG9yZWxpbmUvbGFrZV9zaG9yZS5zaHAiKSAlPiUNCiAgc3RfdHJhbnNmb3JtKHN0X2NycyhUaHJlZUNvdW50eUFyZWEpKSAlPiUNCiAgc3RfaW50ZXJzZWN0aW9uKFRocmVlQ291bnR5QXJlYSkNCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwbG90KCkgKw0KICBnZW9tX3BvaW50KGRhdGE9ZmlzaG5ldCwgDQogICAgICAgICAgICAgYWVzKHg9eHlDKGZpc2huZXQpWywxXSwgeT14eUMoZmlzaG5ldClbLDJdLGNvbG91cj1EZXZfY2hhbmdlKSxzaXplPTEuNSkgKw0KICBnZW9tX3NmKGRhdGE9VGhyZWVDb3VudGllc19MYWtlTWljaGlnYW4sIGNvbG91ciA9ICJyZWQiKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5ldyBEZXZlbG9wbWVudCIpKSArDQogIGxhYnModGl0bGUgPSAiTmV3IERldmVsb3BtZW50IGFuZCBQcm94aW1pdHkgdG8gTGFrZSBNaWNoaWdhbiIsDQogICAgICAgc3VidGl0bGUgPSAiQXMgZmlzaG5ldCBjZW50cm9pZHMiKSArDQogIG1hcFRoZW1lDQpgYGANCg0KTmV4dCwgd2UgY2FsY3VsYXRlIHRoZSBkaXN0YW5jZSB0byB0aGUgbGFrZSAobGFrZV9kaXN0KSBhbmQgbWFwIHRoaXMgdmFyaWFibGUuIA0KDQpgYGB7cn0NCmxpYnJhcnkoc2NhbGVzKQ0KDQojRGV0ZXJtaW5lIGRpc3RhbmNlIHRvIENpdHkgQ2VudGVyIGNlbnRyb2lkDQpuZWFyX2xha2UgPC0gc3RfbmVhcmVzdF9mZWF0dXJlKGNlbnRyb2lkLFRocmVlQ291bnRpZXNfTGFrZU1pY2hpZ2FuKQ0KDQojQ2FsY3VsYXRlIGRpc3RhbmNlIGZyb20gZWFjaCBncmlkIHNxdWFyZSBjZW50cm9pZCB0byB0aGUgbGFrZSBjZW50cm9pZA0KVGhyZWVDb3VudHlfZmlzaG5ldCRsYWtlX2Rpc3QgPC0gYXMuZG91YmxlKHN0X2Rpc3RhbmNlKGNlbnRyb2lkLCBUaHJlZUNvdW50aWVzX0xha2VNaWNoaWdhbltuZWFyX2xha2UsXSwgYnlfZWxlbWVudD1UUlVFKSkNCg0KTGFrZU1pY2hpZ2FuX2Zpc2huZXQgPC0gVGhyZWVDb3VudHlfZmlzaG5ldCANCiMlPiUgc2VsZWN0KGZpc2huZXRJRCwgZ2VvbWV0cnksIGxha2VfZGlzdCkNCg0KZ2dwbG90KCkrDQpnZW9tX3NmKGRhdGE9TGFrZU1pY2hpZ2FuX2Zpc2huZXQsYWVzKGZpbGw9bGFrZV9kaXN0KSxjb2xvcj0ndHJhbnNwYXJlbnQnKSsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MobmFtZT0nUHJveGltaXR5IHRvIExha2UgTWljaGlnYW4gKGZlZXQpJywgbGFiZWxzID0gY29tbWEpKw0KICBnZW9tX3NmKGRhdGE9VGhyZWVDb3VudGllc19MYWtlTWljaGlnYW4sY29sb3I9ImJsYWNrIiwgZmlsbCA9ICJyZWQiKSsNCiAgbGFicyh0aXRsZSA9ICJEaXN0YW5jZSB0byBMYWtlIE1pY2hpZ2FuIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJVc2luZyBmaXNobmV0IGNlbnRyb2lkcyIpICsNCiAgdGhlbWVfdm9pZCgpDQpgYGANCg0KDQoNCiMjIDIuNi4gVGhlIFNwYXRpYWwgTGFnIG9mIERldmVsb3BtZW50DQoNCkF0IHRoZSBjZW50ZXIgb2Ygb3VyIG1vZGVsIGlzIGEgaHlwb3RoZXNpcyB0aGF0IGRldmVsb3BtZW50IGRlbWFuZCBtdXN0IGluIHBhcnQsIGJlIGEgZnVuY3Rpb24gb2YgdGhlIHBhdHRlcm4gb2YgZXhpc3RpbmcgZGV2ZWxvcG1lbnQuIERldmVsb3BtZW50IG9jY3VycyB3aGVyZSB0aGUgbWFya2V0IGJlbGlldmVzIGEgaGlnaGVyIGFuZCBiZXR0ZXIgdXNlIG1heSBicmluZyBhbiBpbnZlc3RtZW50IHJldHVybi4gSW4gdGhlIGNhc2Ugb2Ygc3ByYXdsaW5nIHJlZ2lvbiBsaWtlIEhvdXN0b24sIGFzc3VtaW5nIHRoZSByZXF1aXNpdGUgZGVtYW5kLCB0aGVyZSBpcyBhIGNsZWFyIHJldHVybiBvbiBpbnZlc3RtZW50IGZvciBjb252ZXJ0aW5nIGZhcm1sYW5kIHRvIHN1YnVyYmFuIGhvdXNpbmcuDQoNClRoZSB0cmFkaXRpb25hbCDigJhiaWQtcmVudOKAmSBlY29ub21pYyBtb2RlbCBvZiBkZXZlbG9wbWVudCBwb3NpdHMgdGhhdCBkZXZlbG9wbWVudCBkZW1hbmQgaXMgYSBmdW5jdGlvbiBvZiBhY2Nlc3NpYmlsaXR5LiBUaGlzIG1vZGVsIHdvcmtzIHdlbGwgaW4gY2l0aWVzIHdoZXJlIGNlbnRyYWxpemVkIGxvY2F0aW9ucyBvZmZlciB0aGUgbW9zdCBhY2Nlc3NpYmlsaXR5LiBIb3dldmVyLCBpdCBhc3N1bWVzIHRoYXQgYWxsIGNvbnN1bWVycyBvZiBsYW5kIHNoYXJlIHRoZSBzYW1lIHByZWZlcmVuY2VzIGZvciBjZW50cmFsIGNpdHkgYWNjZXNzLiBXaGlsZSB1cmJhbiBsYW5kIGlzIHZhbHVhYmxlLCBjb250ZW1wb3JhcnkgdXJiYW5pc20gaW4gcmVnaW9ucyBsaWtlIEhvdXN0b24gc2hvdyB1cyB0aGF0IHN1YnVyYmFuIGxvY2F0aW9ucyBjYW4gYmUgcXVpdGUgZGVzaXJhYmxlIGFzIHdlbGwuDQoNCldoeSBpcyB0aGF0PyBIaW50ZXJsYW5kIGxvY2F0aW9ucyBkbyBub3Qgb2ZmZXIgZGlyZWN0IGFjY2VzcyB0byBqb2JzIGFuZCBjdWx0dXJhbCBhbWVuaXRpZXMuIEluc3RlYWQsIHJlc2lkZW50cyB0cmFkZS1vZmYgYWNjZXNzaWJpbGl0eSBmb3IgbGFyZ2VyIGxvdHMgYW5kIGJpZ2dlciBob21lczsgYXMgd2VsbCBhcyBhIGJ1bmRsZSBvZiBwdWJsaWMgc2VydmljZXMgbGlrZSBzY2hvb2wgcXVhbGl0eS4gRGV2ZWxvcGVycyBhcmUgYXR0cmFjdGVkIHRvIHN1YnVyYmFuIGFuZCBleHVyYmFuIGxvY2F0aW9ucyBiZWNhdXNlIG9mIGNoZWFwIGxhbmQgb24g4oCYZ3JlZW5maWVsZOKAmSBzaXRlcyBsaWtlIGZhcm1zIGFuZCBvcGVuIHNwYWNlLg0KDQpUaGUgZGVtYW5kIGZvciBncmVlbmZpZWxkIGRldmVsb3BtZW50IGNhbiB2YXJ5IHN1YnN0YW50aWFsbHkgZGVwZW5kaW5nIG9uIHRoZSBleGlzdGluZyBzcGF0aWFsIGNvbmZpZ3VyYXRpb24gb2YgZGV2ZWxvcG1lbnQuIElmIGFjY2Vzc2liaWxpdHkgdG8gY2VudHJhbCBsb2NhdGlvbnMgd2FzIHRoZSBvbmx5IHVuZGVybHlpbmcgY29uc2lkZXJhdGlvbiwgZGV2ZWxvcGVycyB3b3VsZCBzcHJhd2wgZGlyZWN0bHkgb3V0IHRvIHRoZSBwZXJpcGhlcnksIG11Y2ggbGlrZSB0aGUgZHluYW1pYyB3ZSBtb2RlbGVkIGluIHRoZSBVcmJhbiBHcm93dGggQm91bmRhcnkgY2hhcHRlciAoQ2hhcHRlciAyKS4gQXMgYSBzcGFjZS90aW1lIHByb2Nlc3MsIHRoaXMgd291bGQgbG9vayBtdWNoIGxpa2Ugc3BpbGxlZCBtaWxrLCBlbWFuYXRpbmcgZnJvbSBhIGNlbnRyYWwgcG9pbnQgb3V0d2FyZCBhY3Jvc3MgdGhlIGtpdGNoZW4gdGFibGUuDQoNCkFub3RoZXIgb3B0aW9uIGZvciBkZXZlbG9wZXJzIGlzIHRvIG1vdmUgYmV5b25kIHRoZSBwZXJpcGhlcnkgb250byBncmVlbmZpZWxkIHNpdGVzIHRoYXQgYXJlIGNoZWFwZXIgYmVjYXVzZSB0aGV5IGFyZSBldmVuIGxlc3MgYWNjZXNzaWJsZS4gSW4gdGhpcyBjYXNlLCB0aGUgc3BhY2UvdGltZSBwcm9jZXNzIGxvb2tzIHNtYWxsIOKAmHBhdGNoZXPigJkgb2YgbmV3IGRldmVsb3BtZW50IGRvdHRpbmcgdGhlIGxhbmRzY2FwZSBhbmQg4oCcbGVhcGZyb2dnaW5n4oCdIGZyb20gb25lIGdyZWVuZmllbGQgdG8gdGhlIG5leHQuIFRoZSBlY29ub21pYyBpbmNlbnRpdmUgaXMgdG8gYWx3YXlzIGRldmVsb3AgYmV5b25kIHRoZSBwZXJpcGhlcnksIHdoZXJlIGxhbmQgaXMgY2hlYXBlc3QuIFRoZXJlIGFyZSBzb21lIHJlYWwgY29zdHMgdG8gdGhpcyBtb2RlbCBob3dldmVyLiBGb3Igb25lLCB3aGVuIGRldmVsb3BtZW50IGlzIHNvIGRpZmZ1c2UsIGl0IGlzIG1vcmUgYnVyZGVuc29tZSB0byBlZmZpY2llbnRseSBkZXBsb3kgaW5mcmFzdHJ1Y3R1cmUgbGlrZSByb2Fkcywgc2V3ZXJzIGFuZCBlbGVjdGljaXR5LiBTZWNvbmQsIGxlYXBmcm9nIGRldmVsb3BtZW50IGZyYWdtZW50YXRzIG5hdHVyYWwgYXJlYXMgcmVkdWNpbmcgYmlvZGl2ZXJzaXR5IGFuZCBzdHJlc3NpbmcgdGhlIG5hdHVyYWwgaGFiaXRhdCBvZiBzcGVjaWVzIHRoYXQgbmVlZCBjb250aW51b3VzIG9wZW4gc3BhY2UgdG8gdGhyaXZlLg0KDQpJbiBDaGljYWdvLCBhcyBpbiBtYW55IHNwcmF3bGluZyByZWdpb25zIG9mIHRoZSBVLlMuIHRoZSBlY29ub21pYyBpbmNlbnRpdmVzIHRoYXQgdW5kZXJsaWUgc3ByYXdsIGxpa2VseSBlbmNvdXJhZ2UgYm90aCB0aGUgYWNjZXNzaWJpbGl0eSBhbmQgbGVhcGZyb2cgbW9kZWxzIG9mIGRldmVsb3BtZW50LiBGb3Igb3VyIHB1cnBvc2VzIGhvd2V2ZXIsIGZlYXR1cmVzIG11c3QgYmUgY3JlYXRlZCB0byBhc3NvY2lhdGUgdGhlc2UgcGF0dGVybnMgd2l0aCBkZXZlbG9wbWVudC4gV2l0aG91dCB0aGVtLCB0aGUgbW9kZWwgbWF5IGxhY2sgdGhlIGFwcHJvcHJpYXRlIHNwYXRpYWwgZXhwZXJpZW5jZSBvbiB3aGljaCB0byBmb3JlY2FzdCBncm93dGguDQoNClRvIGtlZXAgaXQgc2ltcGxlLCB3ZSBkZXZlbG9wIGZlYXR1cmVzIGFzc29jaWF0ZWQgd2l0aCBhY2Nlc3NpYmlsaXR5LWJhc2VkIHBhdHRlcm5zLiBJbiByZWFsaXR5LCB0aGUgYW5hbHlzdCBzaG91bGQgZGV2ZWxvcCBhIHNlcmllcyBvZiBhcHBsaWNhYmxlIGZlYXR1cmVzIGFuZCB0ZXN0IHdoaWNoIGJlc3QgYXNzb2NpYXRlIHdpdGggdGhlIG91dGNvbWUgb2YgaW50ZXJlc3QuIFRoZSBwcm9ibGVtIGJlY29tZXMgaW5maW5pdGVseSBtb3JlIGRpZmZpY3VsdCB3aGVuIG9uZSByZWFsaXplcyB0aGF0IHNwcmF3bCBwYXR0ZXJucyBtYXkgZGlmZmVyIHRocm91Z2hvdXQgdGhlIHN0dWR5IGFyZWEgLSBpZiBmb3IgaW5zdGFuY2UsIGxhbmQgdXNlIHJlc3RyaWN0aW9ucyB2YXJpZWQgYnkgY291bnR5LiBCZWxvdyB3ZSBlc3RpbWF0ZSBtb2RlbHMgdXNpbmcgbG9naXN0aWMgcmVncmVzc2lvbiwgYnV0IGhpZ2hlciBsZXZlbCBtYWNoaW5lIGxlYXJuaW5nIGFsZ29yaXRobXMsIG1vc3Qgbm90YWJseSwgUmFuZG9tIEZvcmVzdCwgYXJlIG1vcmUgYWRlcHQgYXQgZGVhbGluZyB3aXRoIG5vbi1saW5lYXJpdGllcyBhY3Jvc3Mgc3BhY2UuDQoNCkFjY2Vzc2liaWxpdHkgaXMgbWVhc3VyZWQgYnkgd2F5IG9mIGEgc3BhdGlhbCBsYWcgaHlwb3RoZXNpemluZyB0aGF0IG5ldyBkZXZlbG9wbWVudCBpcyBhIGZ1bmN0aW9uIG9mIGRpc3RhbmNlIHRvIGV4aXN0aW5nIGRldmVsb3BtZW50LiBUaGUgc2hvcnRlciB0aGUgZGlzdGFuY2UsIHRoZSBtb3JlIGFjY2Vzc2libGUgYSBncmlkIGNlbGwgaXMgdG8gZXhpc3RpbmcgZGV2ZWxvcG1lbnQuIFRoaXMgaXMgbWVhc3VyZWQgYnkgY2FsY3VsYXRpbmcgdGhlIGF2ZXJhZ2UgZGlzdGFuY2UgZnJvbSBlYWNoIGdyaWQgY2VsbCB0byBpdHMgMiBuZWFyZXN0IGRldmVsb3BlZCBuZWlnaGJvcmluZyBncmlkIGNlbGxzIGluIDIwMTEgdXNpbmcgdGhlIGBubl9mdW5jdGlvbmAuIFRoZSBmdW5jdGlvbiBiZWxvdyBjYWxjdWxhdGVzIGF2ZXJhZ2UgbmVhcmVzdCBuZWlnaGJvciBkaXN0YW5jZSBiZXR3ZWVuIGsgcG9pbnQgbGF5ZXJzLiBUaGUgZmlyc3QgcGFyYW1ldGVyIHNwZWNpZmllcyBjb29yZGluYXRlcyB0aGF0IHdlIHdhbnQgdG8gYG1lYXN1cmVGcm9tYCwgaW4gdGhpcyBjYXNlLCBgZmlzaG5ldGAgY2VudHJvaWRzLiBUaGUgc2Vjb25kLCBpbmRpY2F0ZXMgdGhlIHBvaW50IGxheWVyIHdlIHdpc2ggdG8gYG1lYXN1cmVUb2AsIGluIHRoaXMgY2FzZSwgdGhlIGZpc2huZXQgY2VudHJvaWRzIHRoYXQgd2VyZSBkZXZlbG9wZWQgaW4gMjAxMS4NCg0KYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0Kbm5fZnVuY3Rpb24gPC0gZnVuY3Rpb24obWVhc3VyZUZyb20sbWVhc3VyZVRvLGspIHsNCiAgI2NvbnZlcnQgdGhlIHNmIGxheWVycyB0byBtYXRyaWNlcw0KICBtZWFzdXJlRnJvbV9NYXRyaXggPC0NCiAgICBhcy5tYXRyaXgobWVhc3VyZUZyb20pDQogIG1lYXN1cmVUb19NYXRyaXggPC0NCiAgICBhcy5tYXRyaXgobWVhc3VyZVRvKQ0KICBubiA8LSAgIA0KICAgIGdldC5rbm54KG1lYXN1cmVUbywgbWVhc3VyZUZyb20sIGspJG5uLmRpc3QNCiAgICBvdXRwdXQgPC0NCiAgICBhcy5kYXRhLmZyYW1lKG5uKSAlPiUNCiAgICByb3duYW1lc190b19jb2x1bW4odmFyID0gInRoaXNQb2ludCIpICU+JQ0KICAgIGdhdGhlcihwb2ludHMsIHBvaW50X2Rpc3RhbmNlLCBWMTpuY29sKC4pKSAlPiUNCiAgICBhcnJhbmdlKGFzLm51bWVyaWModGhpc1BvaW50KSkgJT4lDQogICAgZ3JvdXBfYnkodGhpc1BvaW50KSAlPiUNCiAgICBzdW1tYXJpemUocG9pbnREaXN0YW5jZSA9IG1lYW4ocG9pbnRfZGlzdGFuY2UpKSAlPiUNCiAgICBhcnJhbmdlKGFzLm51bWVyaWModGhpc1BvaW50KSkgJT4lIA0KICAgIGRwbHlyOjpzZWxlY3QoLXRoaXNQb2ludCkgJT4lDQogICAgcHVsbCgpDQogIA0KICByZXR1cm4ob3V0cHV0KSAgDQp9DQpgYGANCg0KV2h5IGBrPTJgPyBBcyBga2AgZmx1Y3R1YXRlcywgc28gZG9lcyB0aGUgaHlwb3RoZXNpemVkIHNjYWxlIG9mIGFjY2Vzc2liaWxpdHkuIE9uZSBjYW4gdGVzdCB0aGUgZWZmZWN0IG9mIGRpZmZlcmVudCBrIHBhcmFtZXRlcnMgb24gbW9kZWwgZ29vZG5lc3Mgb2YgZml0LCBidXQgYXMgbWVudGlvbmVkLCBhIG1vcmUgc29waGlzdGljYXRlZCBtb2RlbCB3b3VsZCBoeXBvdGhlc2l6ZSB0aGF0IHRoaXMgc2NhbGUgY2FuIHZhcnkgc2lnbmlmaWNhbnRseSBmcm9tIGNpdHkgdG8gc3VidXJiIHRvIHJ1cmFsIHRvd24uDQoNCk5leHQsIHRoZSBmdW5jdGlvbiBhcHBlbmRpbmcgdGhlIGxhZyBkaXN0YW5jZSB0byBgZmlzaG5ldGAuIFRoZXJlIGFyZSAzIGlucHV0cy4gVGhlIGBmaXNobmV0YCB3aGljaCBpcyBjb252ZXJ0ZWQgdG8gYSBjb29yZGluYXRlIGRhdGEgZnJhbWUgd2l0aCB0aGUgYHh5Q2AgZnVuY3Rpb24uIDIwMTEgZGV2ZWxvcGVkIGFyZWFzIGFyZSBjcmVhdGVkIHVzaW5nIGBmaWx0ZXJgLiBUaGUgbWFwIGJlbG93IGlsbHVzdHJhdGVzIHJlbGF0aXZlIGFjY2Vzc2liaWxpdHkgZnJvbSBldmVyeSBncmlkIGNlbGwgdG8gbmVhcmJ5IGRldmVsb3BtZW50Lg0KDQpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQpmaXNobmV0JGxhZ0RldmVsb3BtZW50IDwtDQogICAgbm5fZnVuY3Rpb24oeHlDKGZpc2huZXQpLA0KICAgICAgICAgICAgICAgIHh5QyhmaWx0ZXIoYWdncmVnYXRlZFJhc3RlcnMsZGV2ZWxvcGVkPT0xKSksDQogICAgICAgICAgICAgICAgMikNCg0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSBUaHJlZUNvdW50eUFyZWEsIGZpbGwgPSAidHJhbnNwYXJlbnQiKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IGZpc2huZXQsIA0KICAgICAgICAgICAgIGFlcyh4ID0geHlDKGZpc2huZXQpWywgMV0sIHkgPSB4eUMoZmlzaG5ldClbLCAyXSwgY29sb3IgPSBsYWdEZXZlbG9wbWVudCksIA0KICAgICAgICAgICAgIHNpemUgPSAxLjUpICsNCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IHBhbGV0dGU1LCANCiAgICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IHJhbmdlKGZpc2huZXQkbGFnRGV2ZWxvcG1lbnQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJMYWcgRGV2ZWxvcG1lbnQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gc2NhbGVzOjpjb21tYSkgKw0KICBsYWJzKHRpdGxlID0gIlNwYXRpYWwgTGFnIHRvIDIwMTEgRGV2ZWxvcG1lbnQiLA0KICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzIikgKw0KICBtYXBUaGVtZQ0KDQojIGdncGxvdCgpICsNCiMgICBnZW9tX3NmKGRhdGE9VGhyZWVDb3VudHlBcmVhLCBmaWxsID0gInRyYW5zcGFyZW50IikgKw0KIyAgIGdlb21fcG9pbnQoZGF0YT1maXNobmV0LCANCiMgICAgICAgICAgICAgIGFlcyh4PXh5QyhmaXNobmV0KVssMV0sIHk9eHlDKGZpc2huZXQpWywyXSwgDQojICAgICAgICAgICAgICAgICAgY29sb3VyPWZhY3RvcihudGlsZShsYWdEZXZlbG9wbWVudCw1KSkpLCBzaXplPTEuNSkgKw0KIyAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsDQojICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1zdWJzdHIocXVpbnRpbGVCcmVha3MoZmlzaG5ldCwibGFnRGV2ZWxvcG1lbnQiKSwxLDcpLA0KIyAgICAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZVxuQnJlYWtzIikgKw0KIyAgIGxhYnModGl0bGUgPSAiU3BhdGlhbCBMYWcgdG8gMjAxMSBEZXZlbG9wbWVudCIsDQojICAgICAgICBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkcyIpICsNCiMgICBtYXBUaGVtZQ0KYGBgDQoNCg0KIyMgMi43LiBTdHVkeSBBcmVhIENvdW50aWVzDQoNClRoZSBgdGlncmlzYCBwYWNrYWdlIGFsbG93cyBJbGxpbm9pcyBjb3VudHkgZ2VvbWV0cmllcyB0byBiZSBkb3dubG9hZGVkLiBBIHNwYXRpYWwgc3Vic2V0IHJldHVybnMgb25seSB0aGUgdGhyZWUgY291bnRpZXMgd2UgYXJlIGxvb2tpbmcgYXQuIE5vdGUgdGhhdCB0aGUgc3Vic2V0IGluY2x1ZGVzIGEgbmVnYXRpdmUgMTAwMGZ0IGBzdF9idWZmZXJgLiBUaGlzIGlzIGRvbmUgYmVjYXVzZSB0aGUgc3BhdGlhbCBleHRlbnQgb2YgYFRocmVlQ291bnR5QXJlYWAgaW50ZXJzZWN0cyBjb3VudHkgYm91bmRhcmllcyB0aGF0IGFyZSBhY3R1YWxseSBvdXRzaWRlIG9mIG91ciBzdHVkeSBhcmVhLiBCdWZmZXJpbmcgYFRocmVlQ291bnR5QXJlYWAgc2xpZ2h0bHkgbGltaXRzIHRoZSBpbnRlcnNlY3Rpb24gcmFuZ2UgdG8gb25seSB0aG9zZSBjb3VudGllcyBpbiB0aGUgc3R1ZHkgYXJlYS4NCg0KT25jZSBgc3R1ZHlBcmVhQ291bnRpZXNgIGlzIGNyZWF0ZWQsIGl0IGlzIGBzdF9qb2luYGVkIHdpdGggYGRhdGAgc3VjaCB0aGF0IGVhY2ggZ3JpZCBjZWxscyBrbm93cyB3aGljaCBjb3VudHkgaXTigJlzIGluLiANCg0KV2UgYWxyZWFkeSBoYXZlIGEgc2hhcGVmaWxlIGFuZCByYXN0ZXIgb2YgdGhlIFRocmVlQ291bnR5QXJlYSwgc28gd2Ugc2tpcHBlZCB0aGlzIGNvZGUuIA0KDQpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHJlc3VsdHMgPSAiaGlkZSJ9DQojIFdlIGFscmVhZHkgaGF2ZSBhIHNoYXBlZmlsZSBvZiB0aGUgMyBjb3VudHkgc3R1ZHkgYXJlYS4NCiMgb3B0aW9ucyh0aWdyaXNfY2xhc3MgPSAic2YiKQ0KIyANCiMgc3R1ZHlBcmVhQ291bnRpZXMgPC0gDQojICAgY291bnRpZXMoIklsbGlub2lzIikgJT4lDQojICAgc3RfdHJhbnNmb3JtKHN0X2NycyhUaHJlZUNvdW50eUFyZWEpKSAlPiUNCiMgICBkcGx5cjo6c2VsZWN0KE5BTUUpICU+JQ0KIyAgIC5bc3RfYnVmZmVyKFRocmVlQ291bnR5QXJlYSwtMTAwMCksICwgb3A9c3RfaW50ZXJzZWN0c10NCmBgYA0KDQpXZSBtYXBwZWQgdGhlIHRocmVlIGNvdW50aWVzIGZvciByZWZlcmVuY2UuDQoNCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCmxpYnJhcnkoZ2dwbG90MikNCg0KIyBDYWxjdWxhdGUgY2VudHJvaWRzIGZvciB0aGUgcG9seWdvbnMNCmNvdW50eV9jZW50cm9pZHMgPC0gc3RfY2VudHJvaWQoSUxfY291bnRpZXNfc3Vic2V0KQ0KDQojIEV4dHJhY3QgY2VudHJvaWQgY29vcmRpbmF0ZXMNCmNvdW50eV9jZW50cm9pZF9jb29yZHMgPC0gc3RfY29vcmRpbmF0ZXMoY291bnR5X2NlbnRyb2lkcykNCg0KIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggY2VudHJvaWQgY29vcmRpbmF0ZXMNCmNvdW50eV9jZW50cm9pZF9kZiA8LSBkYXRhLmZyYW1lKFggPSBjb3VudHlfY2VudHJvaWRfY29vcmRzWywgIlgiXSwgWSA9IGNvdW50eV9jZW50cm9pZF9jb29yZHNbLCAiWSJdKQ0KDQojIEFkZCBDT1VOVFlfTkFNIGNvbHVtbiB0byBjZW50cm9pZF9kZg0KY291bnR5X2NlbnRyb2lkX2RmJENPVU5UWV9OQU0gPC0gY291bnR5X2NlbnRyb2lkcyRDT1VOVFlfTkFNDQoNCiMgUGxvdCB3aXRoIHRleHQgbGFiZWxzDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IElMX2NvdW50aWVzX3N1YnNldCkgKw0KICBnZW9tX3RleHQoZGF0YSA9IGNvdW50eV9jZW50cm9pZF9kZiwgYWVzKGxhYmVsID0gQ09VTlRZX05BTSwgeCA9IFgsIHkgPSBZKSwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKw0KICBsYWJzKHRpdGxlID0gIlN0dWR5IEFyZWEgQ291bnRpZXMiKSArDQogIG1hcFRoZW1lDQpgYGANCg0KDQojIyAyLjguIENyZWF0ZSB0aGUgRmluYWwgRGF0YXNldA0KDQpUaGUgbGFzdCBzdGVwIGlzIHRvIGJyaW5nIHRvZ2V0aGVyIGFsbCB0aGUgZGlzcGFyYXRlIGZlYXR1cmUgbGF5ZXJzIGludG8gYSBmaW5hbCBkYXRhc2V0IHRoYXQgY2FuIGJlIHVzZWQgZm9yIGFuYWx5c2lzLiBUaGUgdmFyaW91cyBmaXNobmV0IGxheWVycyBhcmUgYGNiaW5kYCB0b2dldGhlciwgbmVlZGVkIGZlYXR1cmVzIGFyZSBleHRyYWN0ZWQgYW5kIHRoZSBmaW5hbCBmaXNobmV0LCBgZGF0YCBpcyB0aGVuIGpvaW5lZCB3aXRoIGBUaHJlZUNvdW50eUFyZWFgIHRvIGFzc2lnbiBlYWNoIGdyaWQgY2VsbCB0byBhIGNvdW50eS4gYGRldmVsb3BlZDIxYCBpcyBjcmVhdGVkIHRvIGRlc2lnbmF0ZSB0aG9zZSBhcmVhcyB0aGF0IGhhdmUgYWxyZWFkeSBiZWVuIGRldmVsb3BlZCB0aHJvdWdoIDIwMjEuIEZpbmFsbHksIGFueSBncmlkIGNlbGwgdGhhdCBoYXMgYSBgd2F0ZXJgIGxhbmQgY292ZXIgZGVzaWduYXRpb24gaXMgcmVtb3ZlZC4NCg0KYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0KZGF0IDwtIA0KICBjYmluZCgNCiAgICBmaXNobmV0LCBmaXNobmV0UG9wdWxhdGlvbiwgSGlnaHdheV9maXNobmV0LCBSZWdSYWlsX2Zpc2huZXQsIERvd250b3duX2Zpc2huZXQsIExha2VNaWNoaWdhbl9maXNobmV0LCAgYWdncmVnYXRlZFJhc3RlcnMpICU+JQ0KICBkcGx5cjo6c2VsZWN0KERldl9jaGFuZ2UsIGRldmVsb3BlZCwgZm9yZXN0LCBmYXJtLCB3ZXRsYW5kcywgb3RoZXJVbmRldmVsb3BlZCwgd2F0ZXIsDQogICAgICAgICAgICAgICAgUG9wXzIwMTEsIFBvcF8yMDIxLCBwb3BfQ2hhbmdlLCBoaWdod2F5X2Rpc3QsIHJlZ3JhaWxfZGlzdCwgZG93bnRvd25fZGlzdCwgbGFrZV9kaXN0LCBsYWdEZXZlbG9wbWVudCkgJT4lDQogIHN0X2pvaW4oVGhyZWVDb3VudHlBcmVhKSAlPiUNCiAgbXV0YXRlKGRldmVsb3BlZDIxID0gaWZlbHNlKERldl9jaGFuZ2UgPT0gMSAmIGRldmVsb3BlZCA9PSAxLCAwLCBkZXZlbG9wZWQpKSAlPiUNCiAgZmlsdGVyKHdhdGVyID09IDApIA0KYGBgDQoNCg0KIyAzLiBFeHBsb3JhdG9yeSBBbmFseXNpcw0KDQpJbiB0aGlzIHNlY3Rpb24gd2UgZXhwbG9yZSB0aGUgZXh0ZW50IHRvIHdoaWNoIGVhY2ggZmVhdHVyZXMgaXMgYXNzb2NpYXRlZCB3aXRoIGRldmVsb3BtZW50IGNoYW5nZS4gSWYgdGhlIGdvYWwgd2FzIHRvIHByZWRpY3QgYSBjb250aW51b3VzIHZhcmlhYmxlLCBzY2F0dGVycGxvdHMgYW5kIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cyBtYWtlIHRoaXMgcHJvY2VzcyBzdHJhaWdodGZvcndhcmQgYW5kIHJlbGF0aXZlbHkgZWFzeSB0byBleHBsYWluIHRvIGEgbm9uLXRlY2huaWNhbCBkZWNpc29uIG1ha2VyLg0KDQpJbiB0aGlzIGNhc2UgaG93ZXZlciwgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSBpcyBhIGJpbmFyeSBvdXRjb21lIC0gZWl0aGVyIGEgZ3JpZCBjZWxsIHdhcyBkZXZlbG9wZWQgYmV0d2VlbiAyMDExIGFuZCAyMDIxIG9yIGl0IHdhc27igJl0LiBJbiB0aGlzIGNhc2UsIHRoZSByZWxldmFudCBxdWVzdGlvbiBpcyB3aGV0aGVyIGZvciBhIGdpdmVuIGZlYXR1cmUsIHRoZXJlIGlzIGEgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGJldHdlZW4gYXJlYXMgdGhhdCBjaGFuZ2VkIGFuZCBhcmVhcyB0aGF0IGRpZCBub3QuIFRoZXNlIGRpZmZlcmVuY2VzIGFyZSBleHBsb3JlZCBpbiBhIHNldCBvZiBwbG90cyBiZWxvdy4gRm9yIG1vZGVscyB3aXRoIGxvdHMgb2YgZmVhdHVyZXMsIHRoZXNlIHBsb3RzIGNvdWxkIGJlIGNvbXBsaW1lbnQgYnkgYSBzZXJpZXMgb2YgZGlmZmVyZW5jZSBpbiBtZWFucyBzdGF0aXN0aWNhbCB0ZXN0cy4NCg0KVGhlIGJlbG93IGNvZGUgYmxvY2sgYHNlbGVjdGBzIHRoZSBkaXN0YW5jZSB2YXJpYWJsZXMgYW5kIHNwYXRpYWwgbGFnIGZlYXR1cmVzLCBjb252ZXJ0cyBlYWNoIHRvIGxvbmcgZm9ybSBhbmQgcGxvdHMgZWFjaCBhcyBiYXIgcGxvdHMuIE5vdGUgdGhhdCBgZ2VvbV9iYXJgIGNhbGN1bGF0ZXMgdGhlIGBtZWFuYC4NCg0KYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0KZGF0ICU+JQ0KICBkcGx5cjo6c2VsZWN0KHBvcF9DaGFuZ2UsIGhpZ2h3YXlfZGlzdCwgcmVncmFpbF9kaXN0LCBkb3dudG93bl9kaXN0LCBsYWtlX2Rpc3QsIGxhZ0RldmVsb3BtZW50LCBEZXZfY2hhbmdlKSAlPiUNCiAgZ2F0aGVyKFZhcmlhYmxlLCBWYWx1ZSwgLURldl9jaGFuZ2UsIC1nZW9tZXRyeSkgJT4lDQogIGdncGxvdCguLCBhZXMoRGV2X2NoYW5nZSwgVmFsdWUsIGZpbGw9RGV2X2NoYW5nZSkpICsgDQogICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiLCBzdGF0ID0gInN1bW1hcnkiLCBmdW4ueSA9ICJtZWFuIikgKw0KICAgIGZhY2V0X3dyYXAoflZhcmlhYmxlKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5ldyBEZXZlbG9wbWVudCIpLA0KICAgICAgICAgICAgICAgICAgICAgIG5hbWU9IiIpICsNCiAgICBsYWJzKHRpdGxlPSJOZXcgRGV2ZWxvcG1lbnQgYXMgYSBGdW5jdGlvbiBvZiB0aGUgQ29udGludW91cyBWYXJpYWJsZXMiKSArDQogICAgcGxvdFRoZW1lDQpgYGANCg0KVGhlcmUgYXJlIHNpZ25pZmljYW50IGRpZmZlcmVuY2VzIGJldHdlZW4gbm8gY2hhbmdlIGFuZCBuZXcgZGV2ZWxvcG1lbnQgKGZyb20gMjAxMS0yMDIxKSBmb3IgdGhlIGxha2UgZGlzdGFuY2UgYW5kIGRvd250b3duIENoaWNhZ28gZGlzdGFuY2UgdmFyaWFibGVzLiBUaGUgYmFyIHBsb3RzIGZvciB0aGUgb3RoZXIgdmFyaWFibGVzIChEZXZlbG9wbWVudCBsYWcsIGRpc3RhbmNlIHRvIGhpZ2h3YXlzLCBwb3B1bGF0aW9uIGNoYW5nZSwgYW5kIGRpc3RhbmNlIHRvIHJlZ2lvbmFsIHJhaWwpIGFyZSBhYm92ZS4NCg0KTmV4dCwgdGhlIHNhbWUgdmlzdWFsaXphdGlvbiBpcyBjcmVhdGVkIGZvciB0aGUgcG9wdWxhdGlvbiByZWxhdGVkIHZhcmlhYmxlcy4gVGhlc2UgcGxvdHMgaW5mb3JtIHdoaWNoIGZlYXR1cmVzIHNob3VsZCBiZSBpbmNsdWRlZCBpbiB0aGUgbW9kZWwuDQoNCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCmRhdCAlPiUNCiAgZHBseXI6OnNlbGVjdChQb3BfMjAxMSwgUG9wXzIwMjEsIHBvcF9DaGFuZ2UsIERldl9jaGFuZ2UpICU+JQ0KICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtRGV2X2NoYW5nZSwgLWdlb21ldHJ5KSAlPiUNCiAgZ2dwbG90KC4sIGFlcyhEZXZfY2hhbmdlLCBWYWx1ZSwgZmlsbD1EZXZfY2hhbmdlKSkgKyANCiAgICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIsIHN0YXQgPSAic3VtbWFyeSIsIGZ1bi55ID0gIm1lYW4iKSArDQogICAgZmFjZXRfd3JhcCh+VmFyaWFibGUpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwNCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiTm8gQ2hhbmdlIiwiTmV3IERldmVsb3BtZW50IiksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZT0iIikgKw0KICAgIGxhYnModGl0bGU9Ik5ldyBEZXZlbG9wbWVudCBhcyBhIEZ1bmN0aW9uIG9mIEZhY3RvciBWYXJpYWJsZXMiKSArDQogICAgcGxvdFRoZW1lDQpgYGANCg0KVGhlIHBvcHVsYXRpb24gY2hhbmdlIHZhcmlhYmxlIGlzIGEgYml0IG9mIGEgdG91Z2ggb25lLCBiZWNhdXNlIHRoZSBudW1iZXIsIHNpemUsIGFuZCBzaGFwZSBvZiB0aGUgQ2Vuc3VzIHRyYWN0cyBjaGFuZ2VkIGZyb20gdGhlIDIwMTAgdG8gMjAyMCBkZWNlbm5pYWwgQ2Vuc3VzLiBJZiBhIENlbnN1cyB0cmFjdCBoYWQgYW4gaW5jcmVhc2UgaW4gcG9wdWxhdGlvbiwgdGhlbiBpdCBzcGxpdCBpbnRvIHR3byBvciBtb3JlIENlbnN1cyB0cmFjdHMgYW5kIHdhcyBnaXZlbiBhIGRpZmZlcmVudCB0cmFjdCBJRCBudW1iZXIgYW5kIGJvdW5kYXJ5LiBUaGlzIG1ha2VzIGl0IGEgYml0IGhhcmQgdG8gY29tcGFyZSBnZW9ncmFwaGllcyBhY3Jvc3MgdGhlIGRlY2FkZSwgYnV0IHdlIHVzZWQgdGhlIHNwYXRpYWwgbGFnIGZlYXR1cmUgdG8gYWRkcmVzcyB0aGF0IGlzc3VlLiANCg0KDQpOZXh0LCBhIHRhYmxlIG9mIGxhbmQgY292ZXIgY29udmVyc2lvbiBiZXR3ZWVuIDIwMTEgYW5kIDIwMjEgaXMgY3JlYXRlZC4gVGhlIHRhYmxlIHN1Z2dlc3RzIGZvciBpbnN0YW5jZSwgdGhhdCA5JSBvZiBmYXJtbGFuZCByZWdpb25hbGx5IHdhcyBjb252ZXJ0ZWQgdG8gZGV2ZWxvcG1lbnQgYmV0d2VlbiAyMDExIGFuZCAyMDIxLiBUaGlzIGluZGljYXRvciBzaG91bGQgYmUgaW50ZXJwcmV0ZWQgaW4gdGhlIGNvbnRleHQgb2YgdGhlIHNjYWxlIGNoYW5nZXMgd2UgaW1wb3NlZCBvbiB0aGUgZGF0YSBieSBtb3ZpbmcgZnJvbSBhIDMwZnQgYnkgMzBmdCByYXN0ZXIgdG8gYSA0MDAwZnQgYnkgNDAwMGZ0IGZpc2huZXQuIFRoaXMgaXMgdGhlIHNhbWUgcmVhc29uIHdoeSB0aGUgdGFibGUgc3VnZ2VzdHMgYGRldmVsb3BlZGAgYXJlYSB3YXMgdGhlbiDigJxkZXZlbG9wZWTigJ0uDQoNCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCmRhdCAlPiUNCiAgZHBseXI6OnNlbGVjdChEZXZfY2hhbmdlOm90aGVyVW5kZXZlbG9wZWQsZGV2ZWxvcGVkKSAlPiUNCiAgZ2F0aGVyKExhbmRfQ292ZXJfVHlwZSwgVmFsdWUsIC1EZXZfY2hhbmdlLCAtZ2VvbWV0cnkpICU+JQ0KICAgc3Rfc2V0X2dlb21ldHJ5KE5VTEwpICU+JQ0KICAgICBncm91cF9ieShEZXZfY2hhbmdlLCBMYW5kX0NvdmVyX1R5cGUpICU+JQ0KICAgICBzdW1tYXJpemUobiA9IHN1bShhcy5udW1lcmljKFZhbHVlKSkpICU+JQ0KICAgICB1bmdyb3VwKCkgJT4lDQogICAgbXV0YXRlKENvbnZlcnNpb25fUmF0ZSA9IHBhc3RlMChyb3VuZCgxMDAgKiBuL3N1bShuKSwgMiksICIlIikpICU+JQ0KICAgIGZpbHRlcihEZXZfY2hhbmdlID09IDEpICU+JQ0KICBkcGx5cjo6c2VsZWN0KExhbmRfQ292ZXJfVHlwZSxDb252ZXJzaW9uX1JhdGUpICU+JQ0KICBrYWJsZSgpICU+JSBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGKQ0KYGBgDQoNCiMgNC4gUHJlZGljdGluZyBmb3IgMjAzMQ0KDQpJbiB0aGlzIHNlY3Rpb24sIHNpeCBzZXBhcmF0ZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscyBhcmUgZXN0aW1hdGVkIHRvIHByZWRpY3QgZGV2ZWxvcG1lbnQgY2hhbmdlIGJldHdlZW4gMjAxMSBhbmQgMjAyMSAtIHdpdGggZWFjaCBzdWJzZXF1ZW50IG1vZGVsIG1vcmUgc29waGlzdGljYXRlZCB0aGVuIHRoZSBsYXN0LiBUbyBkbyBzbywgdGhlIGRhdGEgaXMgc3BsaXQgaW50byA1MCUgdHJhaW5pbmcvdGVzdCBzZXRzLiBNb2RlbHMgYXJlIGVzdGltYXRlZCBvbiB0aGUgdHJhaW5pbmcgc2V0Lg0KDQpOb3JtYWxseSwgYXMgaW4gcHJldmlvdXMgY2hhcHRlcnMsIGEgcmVzdWx0cyB0YWJsZSByb3cgd291bGQgYmUgZ2VuZXJhdGVkIGZvciBlYWNoIG1vZGVsIGRlc2NyaWJpbmcgdGhlIGFjY3VyYWN5IGFuZCBnZW5lcmFsaXphYmlsaXR5IG9mIHByZWRpY3Rpb25zIGZvciBlYWNoIHNwZWNpZmljYXRpb24uIEZvciBicmV2aXR5LCBhIGxlc3Mgc29waGlzdGljYXRlZCBhcHByb2FjaCBpcyB0YWtlbiBoZXJlLCBqdWRnaW5nIGVhY2ggYnkgdGhlIE1jRmFkZGVuIG9yIOKAnFBzdWVkb+KAnSBSIFNxdWFyZWQgc3RhdGlzdGljIG9uIHRoZSB0ZXN0IHNldC4gVGhlIG1vZGVsIHdpdGggdGhlIGdyZWF0ZXN0IGdvb2RuZXNzIG9mIGZpdCBpcyB0aGVuIHVzZWQgZm9yIHRoZSBwdXJwb3NlcyBvZiBwcmVkaWN0aW9uLg0KDQojIyA0LjIuIE1vZGVsaW5nDQoNCkZpcnN0LCBgZGF0YCBpcyBzcGxpdCBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0IHNldHMuIE5vdGUgaG93IGltYmFsYW5jZWQgdGhlIHBhbmVsIGlzIHdpdGggYHRhYmxlKGRhdFRyYWluJGRldmVsb3BlZClgLg0KDQpgYGB7ciA0Niwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQpzZXQuc2VlZCgzNDU2KQ0KdHJhaW5JbmRleCA8LSANCiAgY3JlYXRlRGF0YVBhcnRpdGlvbihkYXQkZGV2ZWxvcGVkLCBwID0gLjUwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lcyA9IDEpDQpkYXRUcmFpbiA8LSBkYXRbIHRyYWluSW5kZXgsXQ0KZGF0VGVzdCAgPC0gZGF0Wy10cmFpbkluZGV4LF0NCg0KbnJvdyhkYXQpDQp0YWJsZShkYXRUcmFpbiRkZXZlbG9wZWQpDQpgYGANCg0KTmV4dCBzaXggc2VwYXJhdGUgYGdsbWAgbW9kZWxzIGFyZSBlc3RpbWF0ZWQgYWRkaW5nIG5ldyB2YXJpYWJsZXMgZm9yIGVhY2guIEZpZ3VyZSA0LjEgc2hvd3MgdGhlIFBzdWVkbyBSLVNxdWFyZWQgYXNzb2NpYXRlZCB3aXRoIGVhY2ggbW9kZWwuDQoNCmBNb2RlbDFgIGluY2x1ZGVzIG9ubHkgdGhlIDIwMTEgbGFuZCBjb3ZlciB0eXBlcy4gYE1vZGVsMmAgYWRkcyB0aGUgYGxhZ0RldmVsb3BtZW50YC4gTW9kZWxzIDMsIDQgYW5kIDUgYXR0ZW1wdCB0aHJlZSBkaWZmZXJlbnQgYXBwcm9hY2hlcyBmb3IgbW9kZWxpbmcgcG9wdWxhdGlvbiBjaGFuZ2UuIGBNb2RlbDNgIHVzZXMgcG9wdWxhdGlvbiBpbiAyMDExOyBgTW9kZWw0YCB1c2VzIDIwMTEgYW5kIDIwMjEgcG9wdWxhdGlvbjsgYW5kIGBNb2RlbDVgIHVzZXMgcG9wdWxhdGlvbiBjaGFuZ2UuIEFsbCBhcmUgc2lnbmlmaWNhbnQgc28gd2hpY2ggcG9wdWxhdGlvbiBmZWF0dXJlIHNob3VsZCBiZSBjaG9zZW4/IFRoZSBhbnN3ZXIgbGllcyBpbiBob3cgdGhlIG1vZGVsIHdpbGwgYmUgdXNlZCB0byBmb3JlY2FzdC4gQnkgbW9kZWxpbmcgcG9wdWxhdGlvbiBjaGFuZ2UgYmV0d2VlbiAyMDExIGFuZCAyMDIxLCB0aGUgbW9kZWwgaXMgd2VsbCBzcGVjaWZpZWQgdG8gZm9yZWNhc3QgMjAzMSBkZXZlbG9wbWVudCBieSBoYXZpbmcgYHBvcF9DaGFuZ2VgIGluZGljYXRlIGNoYW5nZSBiZXR3ZWVuIDIwMjEgYW5kIDIwMzEuIGBNb2RlbDZgIGluY2x1ZGVzIGRpc3RhbmNlIHRvIHRoZSBoaWdod2F5cyBhbmQgYWxsIG90aGVyIHZhcmlhYmxlcywgYW5kIGlzIHRoZSBmaW5hbCBtb2RlbCBlbXBsb3llZCBmb3IgcHJlZGljdGlvbi4NCg0KYGBge3IgNDcsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0KTW9kZWwxIDwtIGdsbShEZXZfY2hhbmdlIH4gd2V0bGFuZHMgKyBmb3Jlc3QgICsgZmFybSArIG90aGVyVW5kZXZlbG9wZWQsIA0KICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pDQoNCk1vZGVsMiA8LSBnbG0oRGV2X2NoYW5nZSB+IHdldGxhbmRzICsgZm9yZXN0ICArIGZhcm0gKyBvdGhlclVuZGV2ZWxvcGVkICsgbGFnRGV2ZWxvcG1lbnQsIA0KICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pDQogICAgICAgICAgICAgIA0KTW9kZWwzIDwtIGdsbShEZXZfY2hhbmdlIH4gd2V0bGFuZHMgKyBmb3Jlc3QgICsgZmFybSArIG90aGVyVW5kZXZlbG9wZWQgKyBsYWdEZXZlbG9wbWVudCArIFBvcF8yMDExLCANCiAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKSAgICAgICAgICANCiAgICAgICAgICAgICAgDQpNb2RlbDQgPC0gZ2xtKERldl9jaGFuZ2UgfiB3ZXRsYW5kcyArIGZvcmVzdCAgKyBmYXJtICsgb3RoZXJVbmRldmVsb3BlZCArIGxhZ0RldmVsb3BtZW50ICsgUG9wXzIwMTEgKyANCiAgICAgICAgICAgICAgUG9wXzIwMjEsIA0KICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pICAgICAgICAgICAgICANCiAgICAgICAgICAgIA0KTW9kZWw1IDwtIGdsbShEZXZfY2hhbmdlIH4gd2V0bGFuZHMgKyBmb3Jlc3QgICsgZmFybSArIG90aGVyVW5kZXZlbG9wZWQgKyBsYWdEZXZlbG9wbWVudCArIHBvcF9DaGFuZ2UsIA0KICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgDQpNb2RlbDYgPC0gZ2xtKERldl9jaGFuZ2UgfiB3ZXRsYW5kcyArIGZvcmVzdCAgKyBmYXJtICsgb3RoZXJVbmRldmVsb3BlZCArIGxhZ0RldmVsb3BtZW50ICsgcG9wX0NoYW5nZSArIA0KICAgICAgICAgICAgICBoaWdod2F5X2Rpc3QgKyBkb3dudG93bl9kaXN0ICsgbGFrZV9kaXN0ICsgcmVncmFpbF9kaXN0LCANCiAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKSANCmBgYA0KDQpXb3JraW5nIGNhcmVmdWxseSB0aHJvdWdoIHRoZSBiZWxvdyBjb2RlIGJsb2NrLCBhIHZlcnkgY29uY2lzZSBhcHByb2FjaCBmb3IgY3JlYXRpbmcgYSBkYXRhIGZyYW1lIG9mIHBzdWRlbyBSIFNxdWFyZXMgZm9yIGVhY2ggbW9kZWwgYW5kIHBsb3R0aW5nIHRoZW0gZm9yIGNvbXBhcmlzb24uIFJlY2FsbCwgYHBSMmAgaXMgdGhlIGZ1bmN0aW9uIGZvciBwc3VlZG8gUiBzcXVhcmVkLiBEaXNzZWN0IHRoZSBsaW5lIHRoYXQgdXNlcyB0aGUgYG1hcF9kZmNgIGZ1bmN0aW9uIHRvIHNlZSBob3cgdGhpcyBhcHByb2FjaCBsb29wcyB0aHJvdWdoIHRoZSBtb2RlbHMgcmV0cmlldmluZyB0aGUgZ29vZG5lc3Mgb2YgZml0IGZvciBlYWNoLg0KDQpgYGB7ciA0OCwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQptb2RlbExpc3QgPC0gcGFzdGUwKCJNb2RlbCIsIDE6NikNCm1hcF9kZmMobW9kZWxMaXN0LCBmdW5jdGlvbih4KXBSMihnZXQoeCkpKVs0LF0gJT4lDQogIHNldE5hbWVzKHBhc3RlMCgiTW9kZWwiLDE6NikpICU+JQ0KICBnYXRoZXIoTW9kZWwsTWNGYWRkZW4pICU+JQ0KICBnZ3Bsb3QoYWVzKE1vZGVsLE1jRmFkZGVuKSkgKw0KICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKw0KICAgIGxhYnModGl0bGU9ICJNY0ZhZGRlbiBSLVNxdWFyZWQgYnkgTW9kZWwiKSArDQogICAgcGxvdFRoZW1lDQpgYGANCg0KTmV4dCwgYSBkYXRhIGZyYW1lIGlzIGNyZWF0ZWQgdGhhdCBpbmNsdWRlcyBjb2x1bW5zIGZvciB0aGUgb2JzZXJ2ZWQgZGV2ZWxvcG1lbnQgY2hhbmdlLCBgRGV2X2NoYW5nZWAsIGFuZCBvbmUgdGhhdCBpbmNsdWRlcyBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyBmb3IgYE1vZGVsNmAuIFRoaXMgZGF0YSBmcmFtZSBpcyB0aGVuIHVzZWQgYXMgYW4gaW5wdXQgdG8gYSBkZW5zaXR5IHBsb3QgdmlzdWFsaXppbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyBieSBvYnNlcnZlZCBjbGFzcy4gT25seSBhIHNtYWxsIG51bWJlciBvZiBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyBhcmUgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIDUwJSBgKG5yb3coZmlsdGVyKHRlc3RTZXRQcm9icywgcHJvYnMgPj0gLjUwKSkgLyBucm93KGRhdFRlc3QpKWAuIFRoaXMgbWFrZXMgZ29vZCBzZW5zZSwgZ2l2ZW4gaG93IHJhcmUgb2YgYW4gZXZlbnQgZGV2ZWxvcG1lbnQgaXMgaW4gb3VyIGRhdGFzZXQuIFVsdGltYXRlbHksIGluIG9yZGVyIHRvIGp1ZGdlIG91ciBtb2RlbCB3aXRoIGEgY29uZnVzaW9uIG1hdHJpeCwgYSBzbWFsbGVyIGRldmVsb3BtZW50IGNsYXNzaWZpY2F0aW9uIHRocmVzaG9sZCBtdXN0IGJlIGVtcGxveWVkLg0KDQpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQp0ZXN0U2V0UHJvYnMgPC0gDQogIGRhdGEuZnJhbWUoY2xhc3MgPSBkYXRUZXN0JERldl9jaGFuZ2UsDQogICAgICAgICAgICAgcHJvYnMgPSBwcmVkaWN0KE1vZGVsNiwgZGF0VGVzdCwgdHlwZT0icmVzcG9uc2UiKSkgDQogIA0KZ2dwbG90KHRlc3RTZXRQcm9icywgYWVzKHByb2JzKSkgKw0KICBnZW9tX2RlbnNpdHkoYWVzKGZpbGw9Y2xhc3MpLCBhbHBoYT0wLjUpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsDQogICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSkgKw0KICBsYWJzKHRpdGxlID0gIkhpc3RvZ3JhbSBvZiB0ZXN0IHNldCBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyIsDQogICAgICAgeD0iUHJlZGljdGVkIFByb2JhYmlsaXRpZXMiLHk9IkRlbnNpdHkiKSArDQogIHBsb3RUaGVtZQ0KYGBgDQoNCiMjIDQuMy4gQWNjdXJhY3kNCg0KTm93IHRvIHBpY2sgYSBwcmVkaWN0ZWQgcHJvYmFiaWxpdHkgdGhyZXNob2xkIHRvIGNsYXNzaWZ5IGFuIGFyZWEgYXMgaGF2aW5nIG5ldyBkZXZlbG9wbWVudC4gUmVjYWxsLCAqU2Vuc2l0aXZpdHkqIG9yIHRoZSBUcnVlIFBvc2l0aXZlIHJhdGUgaXMgdGhlIHByb3BvcnRpb24gb2YgYWN0dWFsIHBvc2l0aXZlcyAoMeKAmXMpIHRoYXQgd2VyZSBwcmVkaWN0ZWQgdG8gYmUgcG9zaXRpdmUuIEZvciBleGFtcGxlLCB0aGUgU2Vuc2l0aXZpdHkgaW4gb3VyIG1vZGVsIGlzIHRoZSByYXRlIG9mIGRldmVsb3BlZCBhcmVhcyBhY3R1YWxseSBwcmVkaWN0ZWQgYXMgc3VjaC4gKlNwZWNpZmljaXR5KiBvciBUcnVlIE5lZ2F0aXZlIFJhdGUgaXMgdGhlIHByb3BvcnRpb24gb2YgYWN0dWFsIG5lZ2F0aXZlcyAoMOKAmXMpIHRoYXQgd2VyZSBwcmVkaWN0ZWQgdG8gYmUgbmVnYXRpdmVzLiBGb3IgZXhhbXBsZSwgdGhlIFNwZWNpZmljaXR5IGluIG91ciBtb2RlbCBpcyB0aGUgcmF0ZSBvZiBObyBDaGFuZ2UgYXJlYXMgdGhhdCB3ZXJlIGNvcnJlY3RseSBwcmVkaWN0ZWQgYXMgTm8gY2hhbmdlLg0KDQpJdCBpcyBpbXBvcnRhbnQgdG8gY29uc2lkZXIgd2hhdCBQbGFubmVycyB3b3VsZCB0eXBpY2FsbHkgb3B0aW1pemUgZm9yIGdpdmVuIHRoaXMgdXNlIGNhc2UuIE9uZSBhcHByb2FjaCBpcyB0byBtYXhpbWl6ZSB0aGUgbnVtYmVyIG9mIDHigJlzIHByZWRpY3RlZCBjb3JyZWN0bHkgKFNlbnNpdGl2aXR5KSBzbyBhcyB0byBub3QgdW5kZXIgb3Igb3Zlci1wcmVkaWN0IG5ldyBkZXZlbG9wbWVudC4gSXQgbWF5IG9rYXkgaW4gdGhpcyB1c2UgY2FzZSB0byBpbmNvcnJlY3RseSBwcmVkaWN0IG5vIGNoYW5nZSBhcyBjaGFuZ2VkIChTcGVjaWZpY2l0eSkuIEFuIGFidW5kYW5jZSBvZiBGYWxzZSBOZWdhdGl2ZSBlcnJvcnMgbWF5IGJlIHJlYXNvbmFibGUgaWYgUGxhbm5lcnMgZG9u4oCZdCBtaW5kIG92ZXIgZW1waGFzaXppbmcgZGV2ZWxvcG1lbnQgcG90ZW50aWFsLiBJdCBpcyBpbXBvcnRhbnQgdG8gcmVtZW1iZXIgdGhhdCBiZWxvdyB0aGlzIHBvdGVudGlhbCB3aWxsIGV2YWx1YXRlZCBhbG9uZ3NpZGUgc3VwcGx5LXNpZGUgaW5kaWNhdG9ycyBzdWNoIGFzIHRoZSBwcmVzZW5jZSBvZiBzZW5zaXRpdmUgbGFuZC4NCg0KVGhlcmUgYXJlIHNvbWUgY2xlYXIgdHJhZGVvZmZzIGJldHdlZW4gU2Vuc2l0aXZpdHkgYW5kIFNwZWNpZmljaXR5IGluIG91ciBtb2RlbCB0aGF0IGRlc2VydmUgc29tZSBleHBsb3JhdGlvbi4gVG8gaWxsdXN0cmF0ZSwgdHdvIGRpZmZlcmVudCB0aHJlc2hvbGRzIG9mIDUlIGFuZCAxNyUgYXJlIGV4cGxvcmVkLiBQcmVkaWN0ZWQgY2xhc3NlcyBmb3IgYm90aCB0aHJlc2hvbGRzIGFyZSBnZW5lcmF0ZWQgYW5kIGluc3RlYWQgb2YgdXNpbmcgdGhlIGBjb25mdXNpb25NYXRyaXhgIGZ1bmN0aW9uIGZyb20gYGNhcmV0YCBhcyB3ZSBoYXZlIGluIHRoZSBwYXN0LCBoZXJlIGNvbmZ1c2lvbiBtYXRyaXggbWV0cmljcyBhcmUgZGVyaXZlZCBmcm9tIHRoZSBgeWFyZHN0aWNrYCBwYWNrYWdlLiBUaGlzIGFsbG93cyB1cyB0byBgZ3JvdXBfYnlgIHRoZSB0aHJlc2hvbGQgYW5kIGBzdW1tYXJpemVgIHRoZSBtZXRyaWNzIG9mIGludGVyZXN0Lg0KDQpUaGUgYG9wdGlvbnNgIGNhbGwgYmVsb3cgaXMgcmVxdWlyZWQgdG8gdGVsbCBgeWFyZHN0aWNrYCB0aGF0IHRoZSBwb3NpdGl2ZSBmYWN0b3IgY2xhc3MgaW4gYHRlc3RTZXRQcm9ic2AgaXMgYDFgLiBXaXRob3V0IGl0LCB5YXJkc3RpY2sgd2lsbCBieSBkZWZhdWx0LCBzZWUgdGhlIGZpcnN0IGZhY3RvciBsZXZlbCBhcyBgMGAgYW5kIGZsaXAgdGhlIGNvbmZ1c2lvbiBtZXRyaWNzIGFyb3VuZC4NCg0KYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0Kb3B0aW9ucyh5YXJkc3RpY2suZXZlbnRfZmlyc3QgPSBGQUxTRSkNCg0KdGVzdFNldFByb2JzIDwtIA0KICB0ZXN0U2V0UHJvYnMgJT4lIA0KICBtdXRhdGUocHJlZENsYXNzXzA1ID0gYXMuZmFjdG9yKGlmZWxzZSh0ZXN0U2V0UHJvYnMkcHJvYnMgPj0gMC4wNSAsMSwwKSksDQogICAgICAgICBwcmVkQ2xhc3NfMTcgPSBhcy5mYWN0b3IoaWZlbHNlKHRlc3RTZXRQcm9icyRwcm9icyA+PSAwLjE3ICwxLDApKSkgDQoNCnRlc3RTZXRQcm9icyAlPiUNCiAgZHBseXI6OnNlbGVjdCgtcHJvYnMpICU+JQ0KICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtY2xhc3MpICU+JQ0KICBncm91cF9ieShWYXJpYWJsZSkgJT4lDQogIHN1bW1hcml6ZShTZW5zaXRpdml0eSA9IHJvdW5kKHlhcmRzdGljazo6c2Vuc192ZWMoY2xhc3MsZmFjdG9yKFZhbHVlKSksMiksDQogICAgICAgICAgICBTcGVjaWZpY2l0eSA9IHJvdW5kKHlhcmRzdGljazo6c3BlY192ZWMoY2xhc3MsZmFjdG9yKFZhbHVlKSksMiksDQogICAgICAgICAgICBBY2N1cmFjeSA9IHJvdW5kKHlhcmRzdGljazo6YWNjdXJhY3lfdmVjKGNsYXNzLGZhY3RvcihWYWx1ZSkpLDIpKSAlPiUgDQogIGthYmxlKCkgJT4lDQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEYpDQpgYGANCg0KVGhlIDUlIHRocmVzaG9sZCBjb3JyZWN0bHkgcHJlZGljdHMgYSBsb3dlciBudW1iZXIgb2YgbmV3IGRldmVsb3BtZW50IGFyZWFzIChTZW5zaXRpdml0eSksIGJ1dCBpbmNvcnJlY3RseSBwcmVkaWN0cyBhIGhpZ2hlciBudW1iZXIgb2Ygbm8gY2hhbmdlIGFyZWFzIChTcGVjaWZpY2l0eSkuIEFzIHRoZXJlIGFyZSBmYXIgbW9yZSBubyBjaGFuZ2UgYXJlYXMgaW4gdGhlIGRhdGEsIHRoaXMgaXMgcmVmbGVjdGVkIGluIGEgbG93ZXIgb3ZlcmFsbCBhY2N1cmFjeS4gQ29udmVyc2VseSwgdGhlIDE3JSB0aHJlc2hvbGQgaGFzIGEgbG93ZXIgaGlnaGVyIHJhdGUgYW5kIGEgc2xpZ2h0bHkgbG93ZXIgU3BlY2lmaWNpdHkgcmF0ZS4gQWdhaW4sIGJlY2F1c2UgdGhlIGRhdGFzZXQgaXMgbWFqb3JpdHkgbm8gY2hhbmdlIGFyZWFzLCB0aGlzIGxlYWRzIHRvIGEgaGlnaGVyIEFjY3VyYWN5IHJhdGUuDQoNCkdpdmVuIHRoZSB1c2UgY2FzZSwgYW5kIHRoZSBzcGF0aWFsIGRpc3RyaWJ1dGlvbiBvZiBsYW5kIGNvdmVyIGNoYW5nZSwgaXQgbWF5IGJlIG1vcmUgdXNlZnVsIHRvIGhhdmUgYSBtb2RlbCB0aGF0IHByZWRpY3RzIGdlbmVyYWxseSB3aGVyZSBuZXcgZGV2ZWxvcG1lbnQgb2NjdXJzIHJhdGhlciB0aGFuIG9uZSB0aGF0IHByZWRpY3RzIHByZWNpc2VseSB3aGVyZS4gQXMgaWxsdXN0cmF0ZWQgYmVsb3csIHRoZSAxNyUgdGhyZXNob2xkIHByb3ZpZGVzIHRoaXMgb3V0Y29tZS4gVGhlc2UgdHJhZGUtb2ZmcyBjYW4gYmUgdmlzdWFsaXplZCBpbiB0aGUgcGxvdCBiZWxvdy4gSGVyZSB0aGUgbW9kZWwgaXMgdXNlZCB0byBwcmVkaWN0IGZvciB0aGUgZW50aXJlIGBkYXRgIGRhdGFzZXQuIFdoaWNoIHRocmVzaG9sZCBsb29rcyBtb3JlIHJlYXNvbmFibGUgZ2l2ZW4gdGhlIGRpc3RyaWJ1dGlvbiBvZiBvYnNlcnZlZCBkZXZlbG9wbWVudCBjaGFuZ2U/DQoNCk5vdGUgdGhhdCB0aGVzZSBpbmRpY2F0b3JzIGFyZSBjb252ZXJ0ZWQgYGFzLmZhY3RvcmAgc28gdGhleSBjYW4gYmUgbWFwcGVkIHdpdGggYHNjYWxlX2NvbG9yX21hbnVhbGAuDQoNCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCnByZWRzRm9yTWFwIDwtICAgICAgICAgDQogIGRhdCAlPiUNCiAgICBtdXRhdGUocHJvYnMgPSBwcmVkaWN0KE1vZGVsNiwgZGF0LCB0eXBlPSJyZXNwb25zZSIpICwNCiAgICAgICAgICAgVGhyZXNob2xkXzVfUGN0ID0gYXMuZmFjdG9yKGlmZWxzZShwcm9icyA+PSAwLjA1ICwxLDApKSwNCiAgICAgICAgICAgVGhyZXNob2xkXzE3X1BjdCA9ICBhcy5mYWN0b3IoaWZlbHNlKHByb2JzID49IDAuMTcgLDEsMCkpKSAlPiUNCiAgICBkcGx5cjo6c2VsZWN0KERldl9jaGFuZ2UsVGhyZXNob2xkXzVfUGN0LFRocmVzaG9sZF8xN19QY3QpICU+JQ0KICAgIGdhdGhlcihWYXJpYWJsZSxWYWx1ZSwgLWdlb21ldHJ5KSAlPiUNCiAgICBzdF9jYXN0KCJQT0xZR09OIikNCmBgYA0KDQoNCjxkaXYgY2xhc3M9InN1cGVyYmlnaW1hZ2UiPg0KYGBge3IgNTIsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT0gRkFMU0UsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGg9IDh9DQpnZ3Bsb3QoKSArDQogIGdlb21fcG9pbnQoZGF0YT1wcmVkc0Zvck1hcCwgYWVzKHg9eHlDKHByZWRzRm9yTWFwKVssMV0sIHk9eHlDKHByZWRzRm9yTWFwKVssMl0sIGNvbG91cj1WYWx1ZSkpICsNCiAgZmFjZXRfd3JhcCh+VmFyaWFibGUpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5ldyBEZXZlbG9wbWVudCIpLA0KICAgICAgICAgICAgICAgICAgICAgIG5hbWU9IiIpICsNCiAgbGFicyh0aXRsZT0iRGV2ZWxvcG1lbnQgUHJlZGljdGlvbnMgLSBMb3cgVGhyZXNob2xkIikgKyANCiAgZ2VvbV9zZihkYXRhPVRocmVlQ291bnR5QXJlYSwgZmlsbCA9ICJ0cmFuc3BhcmVudCIpICsNCiAgbWFwVGhlbWUNCmBgYA0KPC9kaXY+DQoNClRvIHByb3ZpZGUgYSBiaXQgbW9yZSBpbnNpZ2h0LCB0aGUgY29kZSBibG9jayBiZWxvdyBwcm9kdWNlcyBib3RoIHRydWUgcG9zaXRpdmVzIChTZW5zaXRpdml0eSkgYW5kIHRydWUgbmVnYXRpdmVzIChTcGVjaWZpY2l0eSkgZm9yIGVhY2ggZ3JpZCBjZWxsIGJ5IHRocmVzaG9sZCB0eXBlLiBOb3RpY2UgaG93IHRoZSBzcGF0aWFsIHBhdHRlcm4gb2YgU2Vuc2l0aXZpdHkgZm9yIGJvdGggdGhyZXNob2xkcyBpcyByZWxhdGl2ZWx5IGNvbnNpc3RlbnQsIGJ1dCB0aGUgNSUgdGhyZXNob2xkIG1pc3NlcyBtb3N0IHRoZSBzdHVkeSBhcmVhIHdpdGggcmVzcGVjdCB0byBTcGVjaWZpY2l0eS4NCg0KYGBge3IgNTMsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0KQ29uZnVzaW9uTWF0cml4Lm1ldHJpY3MgPC0NCiAgZGF0ICU+JQ0KICAgIG11dGF0ZShwcm9icyA9IHByZWRpY3QoTW9kZWw2LCBkYXQsIHR5cGU9InJlc3BvbnNlIikgLA0KICAgICAgICAgICBUaHJlc2hvbGRfNV9QY3QgPSBhcy5mYWN0b3IoaWZlbHNlKHByb2JzID49IDAuMDUgLDEsMCkpLA0KICAgICAgICAgICBUaHJlc2hvbGRfMTdfUGN0ID0gIGFzLmZhY3RvcihpZmVsc2UocHJvYnMgPj0gMC4xNyAsMSwwKSkpICU+JQ0KICAgIG11dGF0ZShUcnVlUF8wNSA9IGlmZWxzZShEZXZfY2hhbmdlICA9PSAxICYgVGhyZXNob2xkXzVfUGN0ID09IDEsIDEsMCksDQogICAgICAgICAgIFRydWVOXzA1ID0gaWZlbHNlKERldl9jaGFuZ2UgID09IDAgJiBUaHJlc2hvbGRfNV9QY3QgPT0gMCwgMSwwKSwNCiAgICAgICAgICAgVHJ1ZVBfMTcgPSBpZmVsc2UoRGV2X2NoYW5nZSAgPT0gMSAmIFRocmVzaG9sZF8xN19QY3QgPT0gMSwgMSwwKSwNCiAgICAgICAgICAgVHJ1ZU5fMTcgPSBpZmVsc2UoRGV2X2NoYW5nZSAgPT0gMCAmIFRocmVzaG9sZF8xN19QY3QgPT0gMCwgMSwwKSkgJT4lDQogICAgZHBseXI6OnNlbGVjdCguLCBzdGFydHNfd2l0aCgiVHJ1ZSIpKSAlPiUNCiAgICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtZ2VvbWV0cnkpICU+JQ0KICAgIHN0X2Nhc3QoIlBPTFlHT04iKQ0KYGBgDQoNCg0KPGRpdiBjbGFzcz0ic3VwZXJiaWdpbWFnZSI+DQpgYGB7ciA1NCwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy5oZWlnaHQ9IDgsIGZpZy53aWR0aD0gOCB9DQpnZ3Bsb3QoZGF0YT1Db25mdXNpb25NYXRyaXgubWV0cmljcykgKw0KICBnZW9tX3BvaW50KGFlcyh4PXh5QyhDb25mdXNpb25NYXRyaXgubWV0cmljcylbLDFdLCANCiAgICAgICAgICAgICAgICAgeT14eUMoQ29uZnVzaW9uTWF0cml4Lm1ldHJpY3MpWywyXSwgY29sb3VyID0gYXMuZmFjdG9yKFZhbHVlKSkpICsNCiAgZmFjZXRfd3JhcCh+VmFyaWFibGUpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwgbGFiZWxzPWMoIkNvcnJlY3QiLCJJbmNvcnJlY3QiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgbmFtZT0iIikgKw0KICBsYWJzKHRpdGxlPSJEZXZlbG9wbWVudCBQcmVkaWN0aW9ucyAtIExvdyBUaHJlc2hvbGQiKSArIA0KICBnZW9tX3NmKGRhdGE9VGhyZWVDb3VudHlBcmVhLCBmaWxsID0gInRyYW5zcGFyZW50IikgKw0KICBtYXBUaGVtZQ0KYGBgDQo8L2Rpdj4NCg0KIyMgNC40IEdlbmVyYWxpemFiaWxpdHkNCg0KRm9yIHRoaXMgdXNlIGNhc2UsIGl0IG1hdHRlcnMgbGl0dGxlIHdoZXRoZXIgdGhlIG1vZGVsIGdlbmVyYWxpemVzIHdlbGwgYWNyb3NzIHJhbmRvbSBob2xkb3V0cy4gVGh1cywgcmVndWxhciBjcm9zcy12YWxpZGF0aW9uIGlzIHN1YnN0aXR1dGVkIGZvciBzcGF0aWFsIGNyb3NzLXZhbGlkYXRpb24uIFRoZSBsYXR0ZXIgaXMgZXhwbGljaXRseSBjb25jZXJuZWQgd2l0aCBnZW5lcmFsaXphYmlsaXR5IGFjcm9zcyBzcGFjZS4gVGhlIGFwcHJvYWNoIGhlbHBzIHVzIHVuZGVyc3RhbmQgd2hldGhlciBvdXIgbW9kZWwgaXMgY29tcGFyYWJsZSB0byBlYWNoIGNvdW50eSBpbiB0aGUgc3R1ZHkgYXJlYSBkZXNwaXRlIGFueSBwb3NzaWJsZSBkaWZmZXJlbmNlcyBpbiBsYW5kIHVzZSBvciBsYW5kIHVzZSBwbGFubmluZy4NCg0KVG8gdGVzdCBhY3Jvc3Mtc3BhY2UgZ2VuZXJhbGl6YWJpbGl0eSwgYHNwYXRpYWxDVmAgZnVuY3Rpb24gaXMgcnVuLCB3aGljaCBpdGVyYXRpdmVseSBsb29wcyB0aHJvdWdoIGBkYXRgIGhhdmluZyBlYWNoIGNvdW50eSB0YWtlIGEgdHVybiBhcyB0aGUgaG9sZCBvdXQgdGVzdCBzZXQuIFRoaXMgaXMgYWxzbyBjYWxsZWQg4oCYTGVhdmUtb25lLWdyb3VwLW91dCBjcm9zcyB2YWxpZGF0aW9uLuKAmS4gQSBtb2RlbCBpcyBlc3RpbWF0ZWQgZm9yIHRoZSBuIC0gMSBjb3VudGllcyB0aGF0IHJlbWFpbiBhbmQgdXNlZCB0byBgcHJlZGljdGAgZm9yIHRoZSBob2xkIG91dCBjb3VudHkuDQoNCmBgYHtyIDU1LCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCnNwYXRpYWxDViA8LSBmdW5jdGlvbihkYXRhRnJhbWUsIHVuaXF1ZUlELCBkZXBlbmRlbnRWYXJpYWJsZSwgbW9kZWxOYW1lKSB7DQoNCiNpbml0aWFsaXplIGEgZGF0YSBmcmFtZSANCmVuZExpc3QgPC0gbGlzdCgpDQoNCiNjcmVhdGUgYSBsaXN0IHRoYXQgaXMgYWxsIHRoZSBzcGF0aWFsIGdyb3VwIHVucWl1ZSBpZHMgaW4gdGhlIGRhdGEgZnJhbWUgKGllIGNvdW50aWVzKSAgICANCiAgdW5pcXVlSURfTGlzdCA8LSB1bmlxdWUoZGF0YUZyYW1lW1t1bmlxdWVJRF1dKSAgDQogIHggPC0gMQ0KICB5IDwtIGxlbmd0aCh1bmlxdWVJRF9MaXN0KQ0KICANCiNjcmVhdGUgYSBjb3VudGVyIGFuZCB3aGlsZSBpdCBpcyBsZXNzIHRoYW4gdGhlIG51bWJlciBvZiBjb3VudGllcy4uLiAgDQogIHdoaWxlKHggPD0geSkgDQogIHsNCiNjYWxsIGEgY3VycmVudCBjb3VudHkgICAgDQogICAgY3VycmVudFVuaXF1ZUlEIDwtIHVuaXF1ZUlEX0xpc3RbeF0NCiNjcmVhdGUgYSB0cmFpbmluZyBzZXQgY29tcHJpc2VkIG9mIHVuaXRzIG5vdCBpbiB0aGF0IGNvdW50eSBhbmQgYSB0ZXN0IHNldCBvZiB1bml0cw0KI3RoYXQgYXJlIHRoYXQgY291bnR5DQogICAgdHJhaW5pbmcgPC0gZGF0YUZyYW1lWyB3aGljaChkYXRhRnJhbWVbW3VuaXF1ZUlEXV0gIT0gdW5pcXVlSURfTGlzdFt4XSksXQ0KICAgIHRlc3RpbmcgPC0gZGF0YUZyYW1lWyB3aGljaChkYXRhRnJhbWVbW3VuaXF1ZUlEXV0gPT0gdW5pcXVlSURfTGlzdFt4XSksXQ0KI2NyZWF0ZSBzZXBlcmF0ZSB4eSB2ZWN0b3JzDQogICAgdHJhaW5pbmdYIDwtIHRyYWluaW5nWyAsIC13aGljaChuYW1lcyh0cmFpbmluZykgJWluJSBjKGRlcGVuZGVudFZhcmlhYmxlKSldDQogICAgdGVzdGluZ1ggPC0gdGVzdGluZ1sgLCAtd2hpY2gobmFtZXModGVzdGluZykgJWluJSBjKGRlcGVuZGVudFZhcmlhYmxlKSldDQogICAgDQogICAgdHJhaW5ZIDwtIHRyYWluaW5nW1tkZXBlbmRlbnRWYXJpYWJsZV1dDQogICAgdGVzdFkgPC0gdGVzdGluZ1tbZGVwZW5kZW50VmFyaWFibGVdXQ0KI0NhbGN1bGF0ZSBwcmVkaWN0aW9ucyBvbiB0aGUgdGVzdCBjb3VudHkgYXMgcGFydCBvZiBhIGRhdGEgZnJhbWUgaW5jbHVkaW5nIHRoZSBvYnNlcnZlZA0KI291dGNvbWUgYW5kIHRoZSB1bmlxdWUgY291bnR5IElEICAgIA0KICAgdGhpc1ByZWRpY3Rpb24gPC0gDQogICAgIGRhdGEuZnJhbWUoY2xhc3MgPSB0ZXN0WSwNCiAgICAgICAgICAgICAgICBwcm9icyA9IHByZWRpY3QobW9kZWxOYW1lLCB0ZXN0aW5nWCwgdHlwZT0icmVzcG9uc2UiKSwNCiAgICAgICAgICAgICAgICBjb3VudHkgPSBjdXJyZW50VW5pcXVlSUQpIA0KDQojUm93IGJpbmQgdGhlIHByZWRpY3Rpb25zIHRvIGEgZGF0YSBmYXJtZQ0KICAgZW5kTGlzdCA8LSByYmluZChlbmRMaXN0LCB0aGlzUHJlZGljdGlvbikNCiNpdGVyYXRlIGNvdW50ZXIgICAgDQogICAgeCA8LSB4ICsgMSANCiAgfSANCiNyZXR1cm4gdGhlIGZpbmFsIGxpc3Qgb2YgY291bnRpZXMgYW5kIGFzc29jaWF0ZWQgcHJlZGljdGlvbnMgIA0KICByZXR1cm4gKGFzLmRhdGEuZnJhbWUoZW5kTGlzdCkpDQp9DQpgYGANCg0KTm93IHRoZSBmdW5jdGlvbiBpcyBydW47IGEgMTclIHByZWRpY3RlZCBwcm9iYWJpbGl0eSB0aHJlc2hob2xkIGlzIHNldCBhbmQgYSBmYWNldHRlZCBST0MgcGxvdCBmb3IgZWFjaCBjb3VudHkgaXMgY3JlYXRlZC4gDQoNCmBgYHtyIDU2LCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCnNwYXRpYWxDVl9jb3VudGllcyA8LQ0KICBzcGF0aWFsQ1YoZGF0LCJDT1VOVFlfTkFNIiwiRGV2X2NoYW5nZSIsIE1vZGVsNikgJT4lDQogIG11dGF0ZShwcmVkQ2xhc3MgPSBhcy5mYWN0b3IoaWZlbHNlKHByb2JzID49IDAuMTcgLDEsMCkpKQ0KYGBgDQoNClRvIGludmVzdGlnYXRlIHRoZSBhY3Jvc3MtM2NvdW50eS1hcmVhIGdlbmVyYWxpemFiaWxpdHkgb2YgdGhlIG1vZGVsLCB0aGUgY29kZSBibG9jayBiZWxvdyBwcm9kdWNlcyBhbmQgbWFwcyBjb25mdXNpb24gbWF0cml4IHN0YXRpc3RpY3MgYnkgY291bnR5LiBJdCBpcyBpbXBvcnRhbnQgdG8gZW5zdXJlIGFzIGFib3ZlLCB0aGF0IHRoZSBgeWFyZHN0aWNrLmV2ZW50X2ZpcnN0YCBvcHRpb24gaXMgc2V0Lg0KDQpTb21lIGludGVyZXN0aW5nIHBhdHRlcm5zIGVtZXJnZS4gRmlyc3QsIHJlc3VsdHMgZm9yIHRob3NlIGNvdW50aWVzIHdpdGggbGl0dGxlIGBPYnNlcnZlZF9DaGFuZ2VgIGFyZSBub3QgbWVhbmluZ2Z1bC4gSW4gdGhpcyBjYXNlLCBhbGwgdGhyZWUgY291bnRpZXMgaGF2ZSBzb21lICJPYnNlcnZlZF9DaGFuZ2UiLiBJbiBwbGFjZXMgd2l0aCBzdWJzdGFudGlhbCBuZXcgZGV2ZWxvcG1lbnQsIFNlbnNpdGl2aXR5IHJhdGVzIGFyZSBjb21wYXJhYmxlIHdpdGggdGhlIHJlc3VsdHMgZnJvbSB0aGUgdGVzdCBzZXQgcmVzdWx0cyBvbiB0aGUgZW50aXJlIHN0dWR5IGFyZWEuIEluIER1UGFnZSBDb3VudHksIFNlbnNpdGl2aXR5IGlzIDAuMDAsIGltcGx5aW5nIHRoYXQgdGhlIG1vZGVsIGRpZG4ndCBjb3JyZWN0bHkgaWRlbnRpZnkgYW55IG9mIHRoZSBvYnNlcnZlZCBjaGFuZ2VzIGluIERVUEFHRSBjb3VudHkuIFRoaXMgc3VnZ2VzdHMgdGhlIG1vZGVsIG1heSBzdHJ1Z2dsZSB3aXRoIHByZWRpY3RpbmcgcG9zaXRpdmUgY2FzZXMgaW4gdGhpcyBjb3VudHksIGJ1dCB0aGlzIGlzIGxpa2VseSBiZWNhdXNlIHRoZXJlIGlzIGxpdHRsZSBkZXZlbG9wbWVudCBjaGFuZ2Ugb2NjdXJyaW5nIGluIGdlbmVyYWwuIFNwZWNpZmljaXR5IGlzIGhpZ2ggYWNyb3NzIHRoZSBib2FyZCwgYmVjYXVzZSB0aGVyZSBhcmUgYSBsb3Qgb2YgZXhpc3RpbmcgZGV2ZWxvcGVkIGFyZWFzIGFuZCB0aGUgbW9kZWwgcHJlZGljdHMgYW4gYWJ1bmRhbmNlIG9mIGRldmVsb3BlZCBhcmVhcywgd2hlcmUgdGhlcmUgd2FzIG5vIGNoYW5nZSBkdXJpbmcgdGhlIHNwZWNpZmllZCB0aW1lIHBlcmlvZC4gQWdhaW4sIHRoaXMgaXMgbGVzcyBvZiBhIGNvbmNlcm4gYmVjYXVzZSB0aGVzZSBlc3RpbWF0ZXMgd2lsbCBiZSBvZmZzZXQgYnkgc2Vuc2l0aXZlIGxhbmQgY292ZXIgaW4gU2VjdGlvbiA3IGJlbG93LiBXaWxsIENvdW50eSBoYXMgdGhlIGhpZ2hlc3QgU2Vuc2l0aXZpdHkgbGV2ZWwgYW5kIHRoZSBoaWdoZXN0IEFjY3VyYWN5IHJhdGUgb2YgdGhlIHRocmVlIGNvdW50aWVzLiBPdmVyYWxsLCB0aGVzZSBjb25mdXNpb24gbWF0cml4IG1ldHJpY3MgaGVscCB1cyB0byB1bmRlcnN0YW5kIGhvdyB3ZWxsIHRoZSBtb2RlbCBpcyBwZXJmb3JtaW5nIGluIHRlcm1zIG9mIHRoZSB0cnVlIHBvc2l0aXZlIGFuZCB0cnVlIG5lZ2F0aXZlIHJhdGVzLCBhbmQgdGhleSBzdWdnZXN0IHRoZSBtb2RlbCBpcyBnZW5lcmFsaXphYmxlIHRvIHRob3NlIGNvdW50aWVzIHRoYXQgdW5kZXJ3ZW50IHNpZ25pZmljYW50IGRldmVsb3BtZW50IGNoYW5nZSBsaWtlIFdpbGwgQ291bnR5Lg0KDQoNCmBgYHtyIDU3LCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCnNwYXRpYWxDVl9tZXRyaWNzIDwtDQogIHNwYXRpYWxDVl9jb3VudGllcyAlPiUgDQogICAgZ3JvdXBfYnkoY291bnR5KSAlPiUgDQogICAgc3VtbWFyaXplKE9ic2VydmVkX0NoYW5nZSA9IHN1bShhcy5udW1lcmljKGFzLmNoYXJhY3RlcihjbGFzcykpKSwNCiAgICAgICAgICAgICAgU2Vuc2l0aXZpdHkgPSByb3VuZCh5YXJkc3RpY2s6OnNlbnNfdmVjKGNsYXNzLHByZWRDbGFzcyksMiksDQogICAgICAgICAgICAgIFNwZWNpZmljaXR5ID0gcm91bmQoeWFyZHN0aWNrOjpzcGVjX3ZlYyhjbGFzcyxwcmVkQ2xhc3MpLDIpLA0KICAgICAgICAgICAgICBBY2N1cmFjeSA9IHJvdW5kKHlhcmRzdGljazo6YWNjdXJhY3lfdmVjKGNsYXNzLHByZWRDbGFzcyksMikpIA0KDQpzcGF0aWFsQ1ZfbWV0cmljcyAlPiUNCiAga2FibGUoKSAlPiUNCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRikNCmBgYA0KDQojIDUuIFByZWRpY3RpbmcgTGFuZCBDb3ZlciBEZW1hbmQgZm9yIDIwMzENCg0KQXQgdGhpcyBwb2ludCwgYSBzaW1wbGUgYnV0IHVzZWZ1bCBtb2RlbCBoYXMgYmVlbiB0cmFpbmVkIHRvIHByZWRpY3QgdXJiYW4gZGV2ZWxvcG1lbnQgYmV0d2VlbiAyMDExIGFuZCAyMDIxIGFzIGEgZnVuY3Rpb24gb2YgYmFzZWxpbmUgZmVhdHVyZXMgZnJvbSAyMDExIGluY2x1ZGluZyBsYW5kIGNvdmVyLCBidWlsdCBlbnZpcm9ubWVudCBhbmQgcG9wdWxhdGlvbi4gTmV4dCwgd2UgYXJlIGdvaW5nIHRvIHVwZGF0ZWQgb3VyIGZlYXR1cmVzIHRvIHJlZmxlY3QgYSAyMDIxIGJhc2VsaW5lLiBIYXZpbmcgZG9uZSBzbywgcHJlZGljdGlvbnMgZnJvbSBvdXIgbmV3IG1vZGVsIHdvdWxkIHRoZW4gYmUgZm9yZSAyMDMxLg0KDQpHZW5lcmFsaXphYmlsaXR5IGlzIGFsd2F5cyB0aGUgY29uY2VybiB3aGVuIGZvcmVjYXN0aW5nLCBhbmQgZm9yIHRoaXMgdXNlIGNhc2UgUGxhbm5lcnMgbXVzdCBhc2sgdGhlbXNlbHZlcyB3aGV0aGVyIHRoZSAyMDExLTIwMjEgMyBJTCBDb3VudHkgZXhwZXJpZW5jZSBnZW5lcmFsaXplcyB0byB0aGUgMjAyMS0yMDMxIDMgSUwgQ291bnR5IGV4cGVyaWVuY2UuIEluIG90aGVyIHdvcmRzLCBoYXZlIHRoZSBtYWNyb2Vjb25vbWljIHJlYWwgZXN0YXRlIGNvbmRpdGlvbnMgY2hhbmdlZCBkcmFtYXRpY2FsbHkgYmV0d2VlbiB0aGUgdHdvIHRpbWUgcGVyaW9kcz8gVGhpcyBpcyBxdWVzdGlvbiB3aXRoIG5vIGRlZmluaXRpdmUgYW5zd2VyLCBidXQgaXQgdXNlZnVsIHRvIGNvbnNpZGVyIHRoZSBleG9nZW5vdXMgZmFjdG9ycyB0aGF0IG1heSBkaWZmZXJlbnRpYXRlIHRvZGF54oCZcyBDaGljYWdvIGFyZWEgZnJvbSB0aGF0IG9mIDIwMTEuIFRoZSBiaWcgZm9yIGluc3RhbmNlIGlzIGNsaW1hdGUgY2hhbmdlLiBJZiB0aGUgcmVhbCBlc3RhdGUgbWFya2V0IGNhcGl0YWxpemVkIGZsb29kIHJpc2sgaW50byBkZXZhc3RhdGVkIGFyZWFzIGdvaW5nIGZvcndhcmQsIHRoaXMgd291bGQgZWZmZWN0aXZlbHkgY2hhbmdlIHRoZSBuYXR1cmUgb2YgcmVhbCBlc3RhdGUgZGVtYW5kIGluIHRoZSByZWdpb24uIFRodXMgdGhlIHByZS1mbG9vZCwgMjAxMSBleHBlcmllbmNlZCBpcyBubyBsb25nZXIgZW50aXJlbHkgcmVsZXZhbnQuDQoNClRoaXMgd291bGQgbm90IGNvbXBsZXRlbHkgaW52YWxpZGF0ZSB0aGUgbW9kZWwgaWYgdGhlc2UgY2hhbmdlcyBhcmUgbWFyZ2luYWwsIGFzIGRldmVsb3BtZW50IGRlbWFuZCBwcmVkaWN0aW9ucyBjYW4gYmUgYWRqdXN0ZWQgaW4gU2VjdGlvbiA3IGJlbG93LiBIb3dldmVyLCBjb25zaWRlciB0aGUgdXNlZnVsbmVzcyBvZiB0aGlzIGFwcHJvYWNoIGZvciBhIGxha2Vmcm9udCBjaXR5IHRoYXQgbG9zZXMgc2F5IDEwJSBvZiBpdHMgZGV2ZWxvcGFibGUgbGFuZCB0byBzZWEgbGV2ZWwgcmlzZSBpbiB0aGUgZm9sbG93aW5nIGRlY2FkZS4NCg0KRm9yIGJyZXZpdHksIHdlIG9ubHkgdXBkYXRlIHR3byBmZWF0dXJlcyBpbiBvdXIgbW9kZWwuIEZpcnN0LCBwb3B1bGF0aW9uIGNoYW5nZSAoYHBvcF9jaGFuZ2VgKSBpcyB1cGRhdGVkIHVzaW5nIGNvdW50eSBsZXZlbCBwb3B1bGF0aW9uIHByb2plY3Rpb25zIHZpc3VhbGl6ZWQgaW4gdGhlIHBsb3QgYmVsb3cuIFRoZSBzZWNvbmQgaXMgYGxhZ0RldmVsb3BtZW50YCwgd2hpY2ggZGVzY3JpYmVzIGhvdyBwcmVkaWN0ZWQgbmV3IGRldmVsb3BtZW50IHJlbGF0ZXMgaW4gc3BhY2UgdG8gb2xkIGRldmVsb3BtZW50Lg0KDQpPbmNlIHRoZSBmZWF0dXJlcyBhcmUgdXBkYXRlZCwgMjAzMSBwcmVkaWN0aW9ucyBhcmUgZXN0aW1hdGVkIGFuZCBtYXBwZWQuDQoNCkJlbG93LCBgbGFnRGV2ZWxvcG1lbnRgIGlzIG11dGF0ZSBkZXNjcmliaW5nIGF2ZXJhZ2UgZGlzdGFuY2UgdG8gMjAyMSBkZXZlbG9wbWVudC4gTm90ZSB0aGF0IHRoZSBmaWVsZCBuYW1lLCBgbGFnRGV2ZWxvcG1lbnRgIGlzIHVuY2hhbmdlZCAoaWUuIG5vdCB1cGRhdGVkIHRvIGBsYWdEZXZlbG9wbWVudF8yMDIxYCkuIFRoaXMgaXMgZG9uZSBwdXJwb3NlZnVsbHkgYXMgbW9kZWw2IGhhcyBhIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQgY2FsbGVkIGBsYWdEZXZlbG9wbWVudGAuIElmIHRoaXMgdmFyaWFibGUgd2FzbuKAmXQgcHJlc2VudCBpbiBvdXIgdXBkYXRlZCBkYXRhIGZyYW1lIHRoZW4gdGhlIGBwcmVkaWN0YCBjb21tYW5kIHdvdWxkIGZhaWwuDQoNCmBgYHtyIDU4LCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCmRhdCA8LQ0KICBkYXQgJT4lDQogIG11dGF0ZShsYWdEZXZlbG9wbWVudCA9IG5uX2Z1bmN0aW9uKHh5QyguKSwgeHlDKGZpbHRlciguLGRldmVsb3BlZDIxID09IDEpKSwyKSkNCmBgYA0KDQpOb3cgdG8gdXBkYXRlIHBvcHVsYXRpb24gY2hhbmdlLiBBIG5ldyBkYXRhIGZyYW1lLCBgUG9wXzIwMzFgIGlzIGNyZWF0ZWQgd2hpY2ggaW5jbHVkZXMgMjAyMSBwb3B1bGF0aW9uIGNvdW50cyBhbmQgMjAzMSBwcm9qZWN0aW9ucyBmb3IgZWFjaCBjb3VudHkgaW4gdGhlIHN0dWR5IGFyZWEuIFBvcHVsYXRpb24gaXMgcGxvdHRlZCBieSB5ZWFyIGFuZCBieSBjb3VudHkuIENoaWNhZ2/igJlzIENvb2sgQ291bnR5IGlzIHByb2plY3RlZCB0byBzZWUgdGhlIGdyZWF0ZXN0IHBvcHVsYXRpb24gZ2FpbnMgYnkgZmFyLiBBbmVjZG90YWxseSwgd2Uga25vdyB0aGF0IG11Y2ggb2YgQ29vayBDb3VudHkgaXMgYWxyZWFkeSBkZXZlbG9wZWQsIHdoaWNoIHN1Z2dlc3RzIGl0cyBkZXZlbG9wbWVudCBzY2VuYXJpbyB3aWxsIGludm9sdmUgbW9yZSDigJhpbmZpbGzigJkgZGV2ZWxvcG1lbnQgdGhlbiBzcHJhd2wuDQoNCmBgYHtyIDU5LCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NClRocmVlQ291bnRpZXNfUG9wXzIwMjEgPC0gZ2V0X2FjcyhnZW9ncmFwaHkgPSAiY291bnR5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZXMgPSAiQjAxMDAzXzAwMSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVhciA9IDIwMjEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhdGUgPSAiSUwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvdW50eSA9IGNvdW50aWVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlb21ldHJ5ID0gVFJVRSkgJT4lDQogIG11dGF0ZShDb3VudHkgPSBnc3ViKCIgQ291bnR5LCBJbGxpbm9pcyIsICIiLCBOQU1FKSkgJT4lDQogIHJlbmFtZShDb3VudHlQb3BfMjAyMSA9IGVzdGltYXRlKSAlPiUNCiAgc3RfdHJhbnNmb3JtKHN0X2NycyhUaHJlZUNvdW50eV9maXNobmV0KSkNCg0KIyBDYWxjdWxhdGUgdGhlIHN1bSBvZiBDb3VudHlQb3BfMjAyMSBmb3IgYWxsIGNvdW50aWVzDQphbGxfY291bnRpZXNfcG9wXzIwMjEgPC0gVGhyZWVDb3VudGllc19Qb3BfMjAyMSAlPiUNCiAgc3VtbWFyaXplKEFsbENvdW50aWVzX1BvcDIwMjEgPSBzdW0oQ291bnR5UG9wXzIwMjEpKQ0KDQojIEFkZCB0aGUgc3VtIHRvIFRocmVlQ291bnRpZXNfUG9wXzIwMjEgYXMgYSBuZXcgY29sdW1uDQpUaHJlZUNvdW50aWVzX1BvcF8yMDIxIDwtIFRocmVlQ291bnRpZXNfUG9wXzIwMjEgJT4lDQogIG11dGF0ZShBbGxDb3VudGllc19Qb3AyMDIxID0gYWxsX2NvdW50aWVzX3BvcF8yMDIxJEFsbENvdW50aWVzX1BvcDIwMjEpDQoNCiMgTmVlZCB0byBpbmNsdWRlIHRoZSBwb3B1bGF0aW9uIHByb2plY3Rpb25zIGZvciAyMDMxIGZvciB0aGUgdGhyZWUgY291bnRpZXMuIA0KI3RoZXNlIGFyZSBwb3B1bGF0aW9uIGluIDIwMjAgZnJvbSBJTCBEZXBhcnRtZW50IG9mIEhlYWx0aCBsaW5rZWQgaGVyZTogaHR0cHM6Ly9kcGguaWxsaW5vaXMuZ292L2NvbnRlbnQvZGFtL3NvaS9lbi93ZWIvaWRwaC9maWxlcy9wdWJsaWNhdGlvbnMvcG9wdWxhdGlvbi1wcm9qZWN0aW9ucy1yZXBvcnQtMjAxMC0yMDMwLnBkZg0KIyBDb2RlIGZvciBqb2luaW5nIGFuZCBwbG90dGluZyAyMDIxIHBvcHVsYXRpb24gYW5kIDIwMzEgcG9wdWxhdGlvbiBwcm9qZWN0aW9uOiANCiMgUGVyZm9ybSB0aGUgbGVmdCBqb2luDQpQb3BfMjAzMSA8LSANCiAgZGF0YS5mcmFtZSgNCiAgICBDT1VOVFlfTkFNID0gYygiQ29vayIsICJEdVBhZ2UiLCAiV2lsbCIpLA0KICAgIHBvcF8yMDIxID0gYyg1MjY1Mzk4LCA5MzQwOTQsIDY5NjQwMyksDQogICAgcG9wXzIwMzEgPSBjKDQ2ODkxMzQsIDk0NjkxMCwgOTAyNDc2KQ0KICApJT4lDQogIG11dGF0ZShDb3VudGllcyA9IHRvdXBwZXIoQ09VTlRZX05BTSkpDQoNCiMgTGVmdCBqb2luIHdpdGggVGhyZWVDb3VudGllc19Qb3BfMjAyMQ0KUG9wXzIwMzEgPC0gbGVmdF9qb2luKA0KICBQb3BfMjAzMSwNCiAgVGhyZWVDb3VudGllc19Qb3BfMjAyMSAlPiUNCiAgICBkcGx5cjo6c2VsZWN0KENvdW50eSwgQ291bnR5UG9wXzIwMjEsIEFsbENvdW50aWVzX1BvcDIwMjEpICU+JQ0KICAgIHN0X3NldF9nZW9tZXRyeShOVUxMKSwNCiAgYnkgPSBjKCJDT1VOVFlfTkFNIiA9ICJDb3VudHkiKQ0KKQ0KDQpgYGANCg0KYGBge3IgNjAsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0KIyBQbG90dGluZyAyMDIxIGFuZCAyMDMxIHBvcHVsYXRpb25zIHNpZGUgYnkgc2lkZQ0KUG9wXzIwMzEgJT4lDQogIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoInBvcF8iKSwgbmFtZXNfdG8gPSAiWWVhciIsIHZhbHVlc190byA9ICJQb3B1bGF0aW9uIikgJT4lDQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoQ09VTlRZX05BTSwgLVBvcHVsYXRpb24pLCB5ID0gUG9wdWxhdGlvbiwgZmlsbCA9IFllYXIpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC45KSwgY29sb3IgPSAiYmxhY2siKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLCBsYWJlbHMgPSBjKCIyMDIxIiwgIjIwMzEiKSwgbmFtZSA9ICJZZWFyIikgKw0KICBsYWJzKHRpdGxlID0gIlBvcHVsYXRpb24gQ2hhbmdlIGJ5IENvdW50eTogMjAyMSAtIDIwMzEiLA0KICAgICAgIHggPSAiQ291bnR5IiwgeSA9ICJQb3B1bGF0aW9uIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArDQogIHBsb3RUaGVtZQ0KYGBgDQoNCg0KSW50ZXJlc3RpbmdseSBlbm91Z2gsIENvb2sgQ291bnR5IGlzIHByb2plY3RlZCB0byBoYXZlIGEgZGVjbGluZSBpbiBwb3B1bGF0aW9uIGZyb20gMjAyMSB0byAyMDMxLiBUaGlzIGlzIGxhcmdlbHkgZHVlIHRvIG91dG1pZ3JhdGlvbiBpbiByZWNlbnQgeWVhcnMgd2l0aCB0aG91c2FuZHMgb2YgcmVzaWRlbnRzIGxlYXZpbmcgdGhlIGNvdW50eSB0byBsaXZlIGVsc2V3aGVyZS4gVGhlIHJpc2Ugb2YgcmVtb3RlIHdvcmsgc2NoZWR1bGVzIGFuZCB0aGUgY29zdHMgb2YgaG91c2luZyBoYXZlIGRyaXZlbiBwZW9wbGUgdG8gbW92ZSB0byBuZXcgYXJlYXMgb3V0c2lkZSBvZiB0aGUgY291bnR5LCBvZnRlbiBpbiBvdGhlciBzdGF0ZXMuIEZvciBXaWxsIENvdW50eSwgdGhlIHBvcHVsYXRpb24gaXMgcHJvamVjdGVkIHRvIGluY3JlYXNlIGZvciAyMDMxLCBsaWtlbHkgaW4gdGhlIGFyZWFzIGNsb3NlciB0byB0aGUgTGFrZSBNaWNoaWdhbiBzaG9yZWxpbmUgYW5kIHRyYW5zaXQgcm91dGVzLiBQb3B1bGF0aW9uIGlzIHByb2plY3RlZCB0byBsYXJnZWx5IHJlbWFpbiB0aGUgc2FtZSBmb3IgRHVQYWdlIENvdW50eSB3aGVuIGNvbXBhcmluZyAyMDIxIHRvIDIwMzEuDQoNCg0KIyMgNS4yLiBQcmVkaWN0aW5nIERldmVsb3BtZW50IERlbWFuZA0KDQpOZXh0LCB0aGUgYFBvcF8yMDMxYCB0YWJsZSBpcyBqb2luZWQgdG8gYGRhdGAgYW5kIGBwb3BfY2hhbmdlYCBpbiBvcmRlciB0byDigJhkaXN0cmlidXRl4oCZIHRoZSBuZXcgcG9wdWxhdGlvbiBhY3Jvc3MgdGhlIHN0dWR5IGFyZWEuIFRvIGRvIHNvLCB0aGUgdGhlIGFsbG9jYXRpb24gb2YgbmV3IHBvcHVsYXRpb24gaXMgd2VpZ2h0ZWQgYnkgYSBncmlkIGNlbGzigJlzIGV4aXN0aW5nIHBvcHVsYXRpb24gKGBwb3BfMjAzMS5pbmZpbGxgKS4gMjAxMCBwb3B1bGF0aW9uIGlzIHN1YnRyYWN0ZWQgZnJvbSB0aGlzIGZpZ3VyZSB0byBnZXQgYHBvcF9DaGFuZ2VgLiBGaW5hbGx5LCBgTW9kZWw2YCBpcyB1c2VkIHRvIHByZWRpY3QgZm9yIDIwMjAgZ2l2ZW4gdGhlIHVwZGF0ZWQgcG9wdWxhdGlvbiBjaGFuZ2UgYW5kIGxhZyBkZXZlbG9wbWVudCBmZWF0dXJlcy4NCg0KVGhlIG1hcCBvZiBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyB0aGF0IHJlc3VsdHMgaXMgYmVzdCB0aG91Z2h0IG9mIGFzIGEgbWVhc3VyZSBvZiBwcmVkaWN0ZWQgZGV2ZWxvcG1lbnQgZGVtYW5kIGluIDIwMzEuDQoNCmBgYHtyIDYxLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCmRhdF9pbmZpbGwgPC0NCiAgZGF0ICU+JQ0KICAjY2FsY3VsYXRlIHBvcHVsYXRpb24gY2hhbmdlDQogICAgbGVmdF9qb2luKFBvcF8yMDMxLCBieSA9IGMoIkNPVU5UWV9OQU0iID0gIkNvdW50aWVzIikpICU+JQ0KICAgIG11dGF0ZShwcm9wb3J0aW9uX29mX2NvdW50eV9wb3AgPSBwb3BfMjAyMSAvIEFsbENvdW50aWVzX1BvcDIwMjEsDQogICAgICAgICAgIHBvcF8yMDMxLmluZmlsbCA9IHByb3BvcnRpb25fb2ZfY291bnR5X3BvcCAqIHBvcF8yMDMxLA0KICAgICAgICAgICBwb3BfQ2hhbmdlID0gcm91bmQocG9wXzIwMzEuaW5maWxsIC0gcG9wXzIwMjEpLDIpICU+JQ0KICAgIGRwbHlyOjpzZWxlY3QoLXBvcF8yMDMxLCAtQWxsQ291bnRpZXNfUG9wMjAyMSwgDQogICAgICAgICAgICAgICAgICAtcHJvcG9ydGlvbl9vZl9jb3VudHlfcG9wLCAtcG9wXzIwMzEuaW5maWxsKSAlPiUNCiAgI3ByZWRpY3QgZm9yIDIwMzENCiAgICBtdXRhdGUocHJlZGljdF8yMDMxLmluZmlsbCA9IHByZWRpY3QoTW9kZWw2LC4gLCB0eXBlPSJyZXNwb25zZSIpKQ0KDQpkYXRfaW5maWxsICU+JQ0KICBnZ3Bsb3QoKSArICANCiAgZ2VvbV9wb2ludChhZXMoeD14eUMoZGF0X2luZmlsbClbLDFdLCB5PXh5QyhkYXRfaW5maWxsKVssMl0sIGNvbG91ciA9IGZhY3RvcihudGlsZShwcmVkaWN0XzIwMzEuaW5maWxsLDUpKSkpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhkYXRfaW5maWxsLCJwcmVkaWN0XzIwMzEuaW5maWxsIiksMSw0KSwNCiAgICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGVcbkJyZWFrcyIpICsNCiAgZ2VvbV9zZihkYXRhPVRocmVlQ291bnR5QXJlYSwgZmlsbD1OQSwgY29sb3VyPSJibGFjayIsIHNpemU9MSkgKw0KICBsYWJzKHRpdGxlPSAiRGV2ZWxvcG1lbnQgRGVtYW5kIGluIDIwMzE6IFByZWRpY3RlZCBQcm9iYWJpbGl0aWVzIikgKw0KICBtYXBUaGVtZQ0KDQpgYGANCg0KVGhlcmUgYXJlIGhpZ2hlciBwcm9iYWJpbGl0aWVzIGZvciBkZXZlbG9wbWVudCBkZW1hbmQgaW4gV2lsbCBDb3VudHksIGluIHRoZSBzb3V0aCBwb3J0aW9uIG9mIHRoZSBzdHVkeSBhcmVhLg0KDQoNCiMgNi4gQ29tcGFyaW5nIFByZWRpY3RlZCBEZXZlbG9wbWVudCBEZW1hbmQgJiBFbnZpcm9ubWVudGFsIFNlbnNpdGl2aXR5DQoNCldlIG5vdyBoYXZlIGEgcmVhbGx5IHN0cm9uZyBpbmRpY2F0b3Igb2YgZGV2ZWxvcG1lbnQgZGVtYW5kIGZvciAyMDMxIHRvIGhlbHAgZ3VpZGUgbG9jYWwgbGFuZCB1c2UgcGxhbm5pbmcuIERlbWFuZCBob3dldmVyLCBpcyBvbmx5IG9uZSBzaWRlIG9mIHRoZSBlcXVhdGlvbi4gSXQgbXVzdCBiYWxhbmNlZCB3aXRoIHRoZSBzdXBwbHkgb2YgZW52aXJvbm1lbnRhbGx5IHNlbnNpdGl2ZSBsYW5kLiBVbmRlcnN0YW5kaW5nIHRoZSBpbnRlcnBsYXkgYmV0d2VlbiBkZW1hbmQgYW5kIHN1cHBseSBpcyB0aGUgZmlyc3Qgc3RhZ2Ugb2YgdGhlIOKAmEFsbG9jYXRpb27igJkgcGhhc2UsIHdoZXJlIFBsYW5uZXJzIHVsdGltYXRlbHkgZGVjaWRlIHdoaWNoIGxhbmQgc2hvdWxkIGJlIGRldmVsb3BlZCBhbmQgd2hpY2ggc2hvdWxkIG5vdC4NCg0KRm9yIHRoaXMgYW5hbHlzaXMgZmFybWxhbmQgYW5kIHVuZGV2ZWxvcGVkIGxhbmQgYXJlIGJlIGRlZW1lZCBgU3VpdGFibGVgLCB3aGlsZSBlbnZpcm9ubWVudGFsbHkgc2Vuc2l0aXZlIGFyZWFzIGxpa2Ugd2V0bGFuZHMgYW5kIGZvcmVzdCBhcmUgYmUgZGVlbWVkIGBOb3QgU3VpdGFibGVgLiBCZWxvdywgMjAyMSBsYW5kIGNvdmVyIGRhdGEgaXMgcmVhZCBpbiBhbmQgc2V2ZXJhbCBtZWFzdXJlcyBvZiBlbnZpcm9ubWVudGFsIHNlbnNpdGl2aXR5IGFyZSBjcmVhdGVkIGJ5IGNvdW50eS4gVGhlc2UgaW5jbHVkZToNCg0KMS4gVGhlIHRvdGFsIGFtb3VudCBvZiB3ZXRsYW5kcyBhbmQgZm9yZXN0IGxhbmQgY292ZXIgYXJlYSBpbiAyMDIxLg0KMi4gVGhlIGFtb3VudCBvZiBzZW5zaXRpdmUgbGFuZCAod2V0bGFuZCBhbmQgZm9yZXN0KSBsb3N0IGJldHdlZW4gMjAxMSBhbmQgMjAyMS4NCjMuIFRoZSB0b3RhbCBhcmVhIG9mIGxhcmdlIHNlbnNpdGl2ZSBsYW5kc2NhcGUg4oCYcGF0Y2hlc+KAmSBpbiAyMDIxLg0KDQpUaGUgdGhpcmQgbWV0cmljIHdhcnJhbnRzIHNvbWUgZnVydGhlciBkaXNjdXNzaW9uLiBJbiB0aGUgY29udGV4dCBvZiBsZWFwZnJvZyBkZXZlbG9wbWVudCwgU2VjdGlvbiAyLjYgZGlzY3Vzc2VzIHRoZSBjb25jZXB0IG9mIGxhbmRzY2FwZSBmcmFnbWVudGF0aW9uIC0gdGhlIGlkZWEgdGhhdCBkaXNjb250aW51b3VzIGRldmVsb3BtZW50IGFjcm9zcyBzcGFjZSBjYXJ2ZXMgb3V0IGRpc2pvaW50ZWQgc2xpdmVycyBvZiB3aWxkZXJuZXNzLiBUaGlzIGZyYWdtZW50YXRpb24gcmVkdWNlcyBiaW9kaXZlcnNpdHkgcGFydGljdWxhcmx5IGZvciBzcGVjaWVzIHRoYXQgbmVlZCByb29tIHRvIHJvYW0uIEJlbG93LCBlbnZpcm9ubWVudGFsbHkgYHNlbnNpdGl2ZV9yZWdpb25zYCBhcmUgY3JlYXRlZCB0byByZXByZXNlbnQgbGFyZ2UgYXJlYXMgb2YgdW5mcmFnbWVudGVkIG5hdHVyYWwgcmVzb3VyY2VzLiBXZSB0aGVuIGNvbnNpZGVyIHRoZSB0b3RhbCBhcmVhIG9mIHRoZXNlIGNsdW1wcyBmb3IgZWFjaCBjb3VudHkuDQoNCiMjIDYuMi4gMjAyMSBMYW5kIENvdmVyIERhdGENCg0KVG8gYmVnaW4sIHRoZSAyMDIxIExhbmQgQ292ZXIgZGF0YSBpcyByZWFkIGluIGFuZCByZWNsYXNzaWZpZWQuDQoNCmBgYHtyIDYyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCiMgV2UgYWxyZWFkeSBkaWQgdGhpcy4gSXQncyBjYWxsZWQgVGhyZWVDb3VudHlMQ18yMDIxDQoNCmRldmVsb3BlZDIxIDwtIFRocmVlQ291bnR5TENfMjAyMSA9PSAyMSB8IFRocmVlQ291bnR5TENfMjAyMSA9PSAyMiB8IFRocmVlQ291bnR5TENfMjAyMSA9PSAyMyB8IFRocmVlQ291bnR5TENfMjAyMSA9PSAyNA0KZm9yZXN0MjEgPC0gVGhyZWVDb3VudHlMQ18yMDIxID09IDQxIHwgVGhyZWVDb3VudHlMQ18yMDIxID09IDQyIHwgVGhyZWVDb3VudHlMQ18yMDIxID09IDQzIA0KZmFybTIxIDwtIFRocmVlQ291bnR5TENfMjAyMSA9PSA4MSB8IFRocmVlQ291bnR5TENfMjAyMSA9PSA4MiANCndldGxhbmRzMjEgPC0gVGhyZWVDb3VudHlMQ18yMDIxID09IDkwIHwgVGhyZWVDb3VudHlMQ18yMDIxID09IDk1IA0Kb3RoZXJVbmRldmVsb3BlZDIxIDwtIFRocmVlQ291bnR5TENfMjAyMSA9PSA1MiB8IFRocmVlQ291bnR5TENfMjAyMSA9PSA3MSB8IFRocmVlQ291bnR5TENfMjAyMSA9PSAzMSANCndhdGVyMjEgPC0gVGhyZWVDb3VudHlMQ18yMDIxID09IDExDQoNCm5hbWVzKGRldmVsb3BlZDIxKSA8LSAiZGV2MjEiDQpuYW1lcyhmb3Jlc3QyMSkgPC0gImZvcmVzdDIxIg0KbmFtZXMoZmFybTIxKSA8LSAiZmFybTIxIg0KbmFtZXMod2V0bGFuZHMyMSkgPC0gIndldGxhbmRzMjEiDQpuYW1lcyhvdGhlclVuZGV2ZWxvcGVkMjEpIDwtICJvdGhlclVuZGV2ZWxvcGVkMjEiDQpuYW1lcyh3YXRlcjIxKSA8LSAid2F0ZXIyMSINCmBgYA0KDQpUaGlzIG5leHQgc3RlcCB0YWtlcyB0b28gbG9uZyB0byBwbG90IGJlY2F1c2UgdGhlIHJhc3RlcnMgYXJlIGxhcmdlLiBCdXQgdGhlIGNvZGUgaXMgdGhlcmUganVzdCBpbiBjYXNlLg0KDQpgYGB7ciA2MmEsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBldmFsPUZBTFNFfQ0KIyBnZ3Bsb3QoKSArDQojICAgZ2VvbV9zZihkYXRhPVRocmVlQ291bnR5QXJlYSkgKw0KIyAgIGdlb21fcmFzdGVyKGRhdGEgPSByYmluZChyYXN0KFRocmVlQ291bnR5TENfMjAxMSkgJT4lIG11dGF0ZShsYWJlbCA9ICIyMDExIiksDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhc3QoVGhyZWVDb3VudHlMQ18yMDIxKSAlPiUgbXV0YXRlKGxhYmVsID0gIjIwMjEiKSkgJT4lIA0KIyAgICAgICAgICAgICAgIG5hLm9taXQgJT4lIGZpbHRlcih2YWx1ZSA+IDApLCANCiMgICAgICAgICAgICAgICBhZXMoeCx5LGZpbGw9YXMuZmFjdG9yKHZhbHVlKSkpICsNCiMgICBmYWNldF93cmFwKH5sYWJlbCkgKw0KIyAgIHNjYWxlX2ZpbGxfdmlyaWRpcyhkaXNjcmV0ZT1UUlVFLCBuYW1lID0iIikgKw0KIyAgIGxhYnModGl0bGUgPSAiTGFuZCBDb3ZlciwgMjAxMSAmIDIwMjEiKSArDQojICAgbWFwVGhlbWUgIysgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQoNCg0KTmV4dCwgZWFjaCByYXN0ZXIgaXMgYWdncmVnYXRlZCB0byB0aGUgZmlzaG5ldCB1c2luZyB0aGUgYGFnZ3JlZ2F0ZVJhc3RlcmAgZnVuY3Rpb24gYW5kIDIwMjEgbGFuZCBjb3ZlciB0eXBlcyBhcmUgbWFwcGVkLg0KDQpgYGB7ciA2Mywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQp0aGVSYXN0ZXJMaXN0MjEgPC0gYyhkZXZlbG9wZWQyMSxmb3Jlc3QyMSxmYXJtMjEsd2V0bGFuZHMyMSxvdGhlclVuZGV2ZWxvcGVkMjEsd2F0ZXIyMSkNCg0KZGF0MiA8LQ0KICBhZ2dyZWdhdGVSYXN0ZXIodGhlUmFzdGVyTGlzdDIxLCBkYXQpICU+JQ0KICBkcGx5cjo6c2VsZWN0KGRldjIxLGZvcmVzdDIxLGZhcm0yMSx3ZXRsYW5kczIxLG90aGVyVW5kZXZlbG9wZWQyMSx3YXRlcjIxKSAlPiUNCiAgc3Rfc2V0X2dlb21ldHJ5KE5VTEwpICU+JQ0KICBiaW5kX2NvbHMoLixkYXQpICU+JQ0KICBzdF9zZigpICU+JQ0KICBzdF9jYXN0KCJQT0xZR09OIikNCg0KZGF0MiAlPiUNCiAgZ2F0aGVyKHZhcix2YWx1ZSxkZXYyMTp3YXRlcjIxKSAlPiUNCiAgc3RfY2VudHJvaWQoKSAlPiUNCiAgbXV0YXRlKFggPSBzdF9jb29yZGluYXRlcyguKVssMV0sDQogICAgICAgICBZID0gc3RfY29vcmRpbmF0ZXMoLilbLDJdKSAlPiUNCiAgZ2dwbG90KCkgKw0KICAgIGdlb21fc2YoZGF0YT1UaHJlZUNvdW50eUFyZWEpICsNCiAgICBnZW9tX3BvaW50KGFlcyhYLFksIGNvbG91cj1hcy5mYWN0b3IodmFsdWUpKSkgKw0KICAgIGZhY2V0X3dyYXAofnZhcikgKw0KICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsDQogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiT3RoZXIiLCJMYW5kIENvdmVyIiksDQogICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIiIpICsNCiAgICBsYWJzKHRpdGxlID0gIkxhbmQgQ292ZXIgVHlwZXMsIDIwMjEiLA0KICAgICAgICAgc3VidGl0bGUgPSAiQXMgZmlzaG5ldCBjZW50cm9pZHMiKSArDQogICBtYXBUaGVtZQ0KYGBgDQoNCk5vdGUgdGhhdCB0aGVyZSBhcmUgd2V0bGFuZHMgYW5kIGZvcmVzdCBzcHJpbmtsZWQgdGhyb3VnaG91dCB0aGUgdGhyZWUgY291bnR5IGFyZWEsIGFuZCB0aGVyZSBpcyBhIGxvdCBvZiBmYXJtbGFuZCBjb25jZW50cmF0ZWQgaW4gdGhlIHNvdXRod2VzdCBwb3J0aW9uIGluIFdpbGwgQ291bnR5Lg0KDQoNCiMjIDYuMy4gU2Vuc2l0aXZlIExhbmQgQ292ZXIgTG9zdA0KDQpCZWxvdyBhbiBpbmRpY2F0b3IgYHNlbnNpdGl2ZV9sb3N0YCBpcyBjcmVhdGVkIGluZGljYXRpbmcgZ3JpZCBjZWxscyB0aGF0IHdlcmUgZWl0aGVyIGZvcmVzdCBvciB3ZXRsYW5kcyBpbiAyMDExIGJ1dCB3ZXJlIG5vIGxvbmdlciBzbyBpbiAyMDIxLiBUaGUgb3V0cHV0IGxheWVyLCBgc2Vuc2l0aXZlX2xhbmRfbG9zdGAsIGdpdmVzIGEgc2Vuc2UgZm9yIGhvdyBkZXZlbG9wbWVudCBpbiB0aGUgcmVjZW50IHBhc3QgaGFzIGVmZmVjdGVkIHRoZSBuYXR1cmFsIGVudmlyb25tZW50Lg0KDQpgYGB7ciA2NCwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGg9IDZ9DQpkYXQyIDwtDQogIGRhdDIgJT4lDQogICBtdXRhdGUoc2Vuc2l0aXZlX2xvc3QyMSA9IGlmZWxzZShmb3Jlc3QgPT0gMSAmIGZvcmVzdDIxID09IDAgfA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2V0bGFuZHMgPT0gMSAmIHdldGxhbmRzMjEgPT0gMCwxLDApKQ0KICAgICAgICAgICAgICAgICAgICAgIA0KZ2dwbG90KCkgKw0KICBnZW9tX3BvaW50KGRhdGE9ZGF0MiwgYWVzKHg9eHlDKGRhdDIpWywxXSwgeT14eUMoZGF0MilbLDJdLCBjb2xvdXI9YXMuZmFjdG9yKHNlbnNpdGl2ZV9sb3N0MjEpKSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJTZW5zaXRpdmUgTG9zdCIpLA0KICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiIikgKw0KICBsYWJzKHRpdGxlID0gIlNlbnNpdGl2ZSBsYW5kcyBsb3N0OiAyMDExIC0gMjAyMSIsDQogICAgICAgc3VidGl0bGUgPSAiQXMgZmlzaG5ldCBjZW50cm9pZHMiKSArDQogIGdlb21fc2YoZGF0YT1UaHJlZUNvdW50eUFyZWEsIGZpbGwgPSAidHJhbnNwYXJlbnQiKSArDQogIG1hcFRoZW1lDQpgYGANCg0KVGhpcyB0eXBlIG9mIGFuYWx5c2lzIGlzIGhlbHBmdWwgdG8gc2VlIHdoZXJlIHNlbnNpdGl2ZSBsYW5kcyBhcmUgYmVpbmcgbG9zdCB0aHJvdWdob3V0IHRoZSB0aHJlZSBjb3VudHkgYXJlYSBkdWUgdG8gZGV2ZWxvcG1lbnQuIFRoYW5rZnVsbHksIHRoZXJlIGFyZSBvbmx5IGEgc21hbGwgbnVtYmVyIG9mIHRoZXNlIGxvc3QgYXJlYXMgZnJvbSAyMDExLTIwMjEuIElmIHRoZXJlIHdlcmUgbGFyZ2UgYXJlYXMgbG9zdCwgaXQgd291bGQgYmUgaGVscGZ1bCBmb3IgcGxhbm5lcnMgdG8gaW1wbGVtZW50IGluaWF0aXZlcyB0byBwcm90ZWN0IHRob3NlIHNlbnNpdGl2ZSBhcmVhcyBmcm9tIGRldmVsb3BtZW50IGVuY3JvYWNobWVudCBhbmQgYWxsb3cgdGhlbSB0byBjb250aW51ZSBwcm92aWRpbmcgZW52aXJvbm1lbnRhbCBiZW5lZml0cy4gDQoNCg0KIyMgNi40IExhbmRzY2FwZSBGcmFnbWVudGF0aW9uDQoNCkluIHRoaXMgc2VjdGlvbiwgdGhlIGB3ZXRsYW5kczIxYCBhbmQgYGZvcmVzdDIxYCByYXN0ZXJzIGFyZSBjb252ZXJ0ZWQgdG8gY29udGlndW91cyBgc2Vuc2l0aXZlX3JlZ2lvbnNgIHVzaW5nIHRoZSBgcmFzdGVyOjpjbHVtcGAgZnVuY3Rpb24uIFRoaXMgaXMgZXF1aXZhbGVudCB0byBSZWdpb24gR3JvdXAgaW4gQXJjR0lTLiBUaGUgcmFzdGVyIGNsdW1wcyBhcmUgdGhlbiBjb252ZXJ0ZWQgdG8gdmVjdG9yIGBzZmAgbGF5ZXJzOyBkaXNzb2x2ZWQgaW50byB1bmlxdWUgcmVnaW9uczsgQWNyZXMgYXJlIGNhbGN1bGF0ZWQ7IGFuZCB0aGUgbGF5ZXJzIGFyZSBjb252ZXJ0ZWQgYmFjayB0byByYXN0ZXIgdG8gYmUgZXh0cmFjdGVkIGJhY2sgdG8gdGhlIGZpc2huZXQgd2l0aCBgYWdncmVnYXRlUmFzdGVyYC4gSXQgaXMgd29ydGggZ29pbmcgdGhyb3VnaCB0aGlzIGNvZGUgYmxvY2sgbGluZSBieSBsaW5lLiBOb3RlIHRoYXQgb25seSBgc2Vuc2l0aXZlX3JlZ2lvbnNgIHdpdGggYXJlYXMgZ3JlYXRlciB0aGFuIDEgYWNyZSBhcmUgaW5jbHVkZWQuDQoNCldlIGVuZGVkIHVwIG5vdCBpbmNsdWRpbmcgdGhpcyBzZWN0aW9uIGluIG91ciBhbmFseXNpcy4NCg0KYGBge3IgNjUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoPSA2fQ0KDQojICBlbXB0eVJhc3RlciA8LSBEZXZlbG9wbWVudF9jaGFuZ2UNCiMgIGVtcHR5UmFzdGVyW10gPC0gTkENCiMgDQojIHNlbnNpdGl2ZVJlZ2lvbnMgPC0gDQojICAgcmFzdGVyOjpjbHVtcCh3ZXRsYW5kczIxICsgZm9yZXN0MjEpICU+JQ0KIyAgIHJhc3RlclRvUG9seWdvbnMoKSAlPiUNCiMgICBzdF9hc19zZigpICU+JQ0KIyAgIGdyb3VwX2J5KGNsdW1wcykgJT4lIA0KIyAgIHN1bW1hcml6ZSgpICU+JQ0KIyAgICAgbXV0YXRlKEFjcmVzID0gYXMubnVtZXJpYyhzdF9hcmVhKC4pICogMC4wMDAwMjI5NTY4KSkgJT4lDQojICAgICBmaWx0ZXIoQWNyZXMgPiAzOTU0KSAgJT4lDQojICAgZHBseXI6OnNlbGVjdCgpICU+JQ0KIyAgIHJhc3Rlcjo6cmFzdGVyaXplKC4sZW1wdHlSYXN0ZXIpIA0KIyBzZW5zaXRpdmVSZWdpb25zW3NlbnNpdGl2ZVJlZ2lvbnMgPiAwXSA8LSAxICANCiMgbmFtZXMoc2Vuc2l0aXZlUmVnaW9ucykgPC0gInNlbnNpdGl2ZVJlZ2lvbnMiDQojIA0KIyBkYXQyIDwtDQojICAgYWdncmVnYXRlUmFzdGVyKGMoc2Vuc2l0aXZlUmVnaW9ucyksIGRhdDIpICU+JQ0KIyAgIGRwbHlyOjpzZWxlY3Qoc2Vuc2l0aXZlUmVnaW9ucykgJT4lDQojICAgc3Rfc2V0X2dlb21ldHJ5KE5VTEwpICU+JQ0KIyAgIGJpbmRfY29scyguLGRhdDIpICU+JQ0KIyAgIHN0X3NmKCkNCiMgDQojIGdncGxvdCgpICsNCiMgICBnZW9tX3BvaW50KGRhdGE9ZGF0MiwgYWVzKHg9eHlDKGRhdDIpWywxXSwgeT14eUMoZGF0MilbLDJdLCBjb2xvdXI9YXMuZmFjdG9yKHNlbnNpdGl2ZVJlZ2lvbnMpKSkgKw0KIyAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsDQojICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiT3RoZXIiLCJTZW5zaXRpdmUgUmVnaW9ucyIpLA0KIyAgICAgICAgICAgICAgICAgICAgICAgbmFtZT0iIikgKw0KIyAgIGxhYnModGl0bGUgPSAiU2Vuc2l0aXZlIHJlZ2lvbnMiLA0KIyAgICAgICAgc3VidGl0bGUgPSAiQ29udGlub3VzIGFyZWFzIG9mIGVpdGhlciB3ZXRsYW5kcyBvciBmb3Jlc3RzXG5ncmVhdGVyIHRoYW4gMSBhY3JlIikgKw0KIyAgIG1hcFRoZW1lDQpgYGANCg0KIyMgNi41LiBTdW1tYXJpemUgYnkgQ291bnR5DQoNClRoZSBiZWxvdyBgZHBseXJgIHN0YXRlbWVudCB0YWtlcyBhcyBpdHMgaW5wdXQsIGBkYXQyYCwgd2hpY2ggd2FzIGNyZWF0ZWQgaW4gU2VjdGlvbnMgNi4yIC0gNi40IGFuZCB3cmFuZ2xlcyB0b2dldGhlciBhIHRhYmxlIG9mIGNvdW50eS1sZXZlbCwgc3VwcGx5IGFuZCBkZW1hbmQgbWV0cmljcyB3aGljaCBjYW4gYmUgdXNlZCB0byBhbmFseXplIHN1aXRhYmlsaXR5IGJ5IGNvdW50eS4NCg0KYGBge3IgNjYsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0KY291bnR5X3NwZWNpZmljX21ldHJpY3MgPC0gDQogIGRhdDIgJT4lDQogICNwcmVkaWN0IGRldmVsb3BtZW50IGRlbWFuZCBmcm9tIG91ciBtb2RlbA0KICBtdXRhdGUoRGV2ZWxvcG1lbnRfRGVtYW5kID0gcHJlZGljdChNb2RlbDYsIGRhdDIsIHR5cGU9InJlc3BvbnNlIikpICU+JQ0KICAjZ2V0IGEgY291bnQgY291bnQgb2YgZ3JpZCBjZWxscyBieSBjb3VudHkgd2hpY2ggd2UgY2FuIHVzZSB0byBjYWxjdWxhdGUgcmF0ZXMgYmVsb3cNCiAgbGVmdF9qb2luKHN0X3NldF9nZW9tZXRyeShkYXQsIE5VTEwpICU+JSBncm91cF9ieShDT1VOVFlfTkFNKSAlPiUgc3VtbWFyaXplKGNvdW50ID0gbigpKSkgJT4lDQogICNjYWxjdWxhdGUgc3VtbWFyeSBzdGF0aXN0aWNzIGJ5IGNvdW50eQ0KICBncm91cF9ieShDT1VOVFlfTkFNKSAlPiUNCiAgc3VtbWFyaXplKFRvdGFsX0Zhcm1sYW5kID0gc3VtKGZhcm0yMSkgLyBtYXgoY291bnQpLA0KICAgICAgICAgICAgVG90YWxfRm9yZXN0ID0gc3VtKGZvcmVzdDIxKSAvIG1heChjb3VudCksDQogICAgICAgICAgICBUb3RhbF9XZXRsYW5kcyA9IHN1bSh3ZXRsYW5kczIxKSAvIG1heChjb3VudCksDQogICAgICAgICAgICBUb3RhbF9VbmRldmVsb3BlZCA9IHN1bShvdGhlclVuZGV2ZWxvcGVkMjEpIC8gbWF4KGNvdW50KSwNCiAgICAgICAgICAgIFNlbnNpdGl2ZV9MYW5kX0xvc3QgPSBzdW0oc2Vuc2l0aXZlX2xvc3QyMSkgLyBtYXgoY291bnQpLA0KICAgICAgICAgICAgI1NlbnNpdGl2ZV9SZWdpb25zID0gc3VtKHNlbnNpdGl2ZVJlZ2lvbnMpIC8gbWF4KGNvdW50KSwNCiAgICAgICAgICAgIE1lYW5fRGV2ZWxvcG1lbnRfRGVtYW5kID0gbWVhbihEZXZlbG9wbWVudF9EZW1hbmQpKSAlPiUNCiAgI2dldCBwb3B1bGF0aW9uIGRhdGEgYnkgY291bnR5DQogIGxlZnRfam9pbihQb3BfMjAzMSwgYnkgPSBjKCJDT1VOVFlfTkFNIiA9ICJDb3VudGllcyIpKSAlPiUgDQogICAgICAgICAgICBtdXRhdGUoUG9wdWxhdGlvbl9DaGFuZ2UgPSBwb3BfMjAzMSAtIHBvcF8yMDIxLA0KICAgICAgICAgICAgICAgICAgIFBvcHVsYXRpb25fQ2hhbmdlX1JhdGUgPSBQb3B1bGF0aW9uX0NoYW5nZSAvIHBvcF8yMDMxKSAlPiUNCiAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QoQ09VTlRZX05BTSxUb3RhbF9GYXJtbGFuZCwgVG90YWxfRm9yZXN0LCBUb3RhbF9XZXRsYW5kcywgVG90YWxfVW5kZXZlbG9wZWQsIFNlbnNpdGl2ZV9MYW5kX0xvc3QsIE1lYW5fRGV2ZWxvcG1lbnRfRGVtYW5kLCBQb3B1bGF0aW9uX0NoYW5nZV9SYXRlKQ0KDQpgYGANCg0KDQpOb3cgYSBzbWFsbCBtdWx0aXBsZSBwbG90IGNhbiBiZSBjcmVhdGVkIHByb3ZpZGluZyBib3RoIHN1cHBseSBhbmQgZGVtYW5kIHNpZGUgYW5hbHl0aWNzIGJ5IGNvdW50eS4gVGhlIHBsb3QgZ2l2ZXMgYSBzZW5zZSBmb3IgZGV2ZWxvcG1lbnQgZGVtYW5kIChgRGVtYW5kLVNpZGVgKSwgc3VpdGFibGUgbGFuZCBmb3IgZGV2ZWxvcG1lbnQgKGBTdWl0YWJsZWApIGFuZCBzZW5zaXRpdmUgbGFuZCAoYE5vdCBTdWl0YWJsZWApLg0KDQpJbiBXaWxsIENvdW50eSwgc291dGggb2YgQ2hpY2FnbywgdGhlIGRhdGEgc3VnZ2VzdHMgYm90aCBwb3B1bGF0aW9uIGFuZCBkZXZlbG9wbWVudCBkZW1hbmQgd2lsbCBpbmNyZWFzZS4gQXQgdGhlIHNhbWUgdGltZSwgdGhlcmUgaXMgYSBoaWdoIHJhdGUgb2YgZGV2ZWxvcGFibGUgZmFybWxhbmQuIFdpbGwgQ291bnR5IGhhcyBhIGxhcmdlIGFtb3VudCBvZiBmb3Jlc3QgYW5kIHdldGxhbmRzIGFyZWFzLCBidXQgb3RoZXJ3aXNlIGl0IGlzIHdlbGwgc3VpdGFibGUgdG8gbmV3IGRldmVsb3BtZW50Lg0KDQpDb252ZXJzZWx5LCBEdVBhZ2UgQ291bnR5LCB0aGUgY291bnR5IGVhc3Qgb2YgQ2hpY2FnbyBhbmQgQ29vayBDb3VudHksIGhhcyBhIHNpZ25pZmljYW50IGFtb3VudCBvZiBmb3Jlc3QgYW5kIHdldGxhbmQgYXJlYXMgbGlrZSBXaWxsIENvdW50eSwgYnV0IGl0IGhhcyBhIHNsaWdodGx5IGhpZ2hlciBhbW91bnQgb2Ygc2Vuc2l0aXZlIGxhbmRzIGxvc3QuIENvbXBhcmF0aXZlbHksIGl0IGhhcyBhIGxvdCBsZXNzIHVuZGV2ZWxvcGVkIGxhbmQgYW5kIGZhcm1sYW5kIHRoYXQgaXMgc3VpdGFibGUgZm9yIG5ldyBkZXZlbG9wbWVudC4gVGhlcmUgYXJlIHZlcnkgbG93IHJhdGVzIG9mIG1lYW4gZGV2ZWxvcG1lbnQgZGVtYW5kIGFuZCBwb3B1bGF0aW9uIGdyb3d0aC4gDQoNCkNvb2sgQ291bnR5IGhhcyB0aGUgbG93ZXN0IGFtb3VudHMgb2Ygc2Vuc2l0aXZlIGxhbmRzIGJ1dCBhbHNvIGxvdyBhbW91bnRzIG9mIHVuZGV2ZWxvcGVkIGFyZWFzIGFuZCBmYXJtbGFuZCBhcyB3ZWxsLiBUaGlzIGlzIGJlY2F1c2UgQ2hpY2FnbyBhbmQgbW9zdCBvZiB0aGUgY291bnR5IGhhcyBhbHJlYWR5IGJlZW4gZGV2ZWxvcGVkLiBBbmQgdGhlIHBvcHVsYXRpb24gaXMgZXhwZWN0ZWQgdG8gY29udGludWUgdG8gZGVjbGluZSBkdWUgdG8gb3V0bWlncmF0aW9uLg0KDQpJbiB0aGVzZSBjb3VudGllcywgdGhlcmUgYXJlIHNvbWUgcmVhbCB0cmFkZS1vZmZzIHRvIGJlIG1hZGUgYmV0d2VlbiBzdWl0YWJsZS9zZW5zaXRpdmUgbGFuZCBhbmQgZGV2ZWxvcG1lbnQgcHJlc3N1cmUuDQoNCmBgYHtyIDY3LCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCmNvdW50eV9zcGVjaWZpY19tZXRyaWNzICU+JQ0KICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtQ09VTlRZX05BTSwgLWdlb21ldHJ5KSAlPiUNCiAgbXV0YXRlKFZhcmlhYmxlID0gZmFjdG9yKFZhcmlhYmxlLCBsZXZlbHM9YygiUG9wdWxhdGlvbl9DaGFuZ2VfUmF0ZSIsIk1lYW5fRGV2ZWxvcG1lbnRfRGVtYW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVG90YWxfRmFybWxhbmQiLCJUb3RhbF9VbmRldmVsb3BlZCIsIlRvdGFsX0ZvcmVzdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRvdGFsX1dldGxhbmRzIiwiU2Vuc2l0aXZlX0xhbmRfTG9zdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFRSVUUpKSkgJT4lDQogIG11dGF0ZShQbGFubmluZ19EZXNpZ25hdGlvbiA9IGNhc2Vfd2hlbigNCiAgICBWYXJpYWJsZSA9PSAiUG9wdWxhdGlvbl9DaGFuZ2VfUmF0ZSIgfCBWYXJpYWJsZSA9PSAiTWVhbl9EZXZlbG9wbWVudF9EZW1hbmQiIH4gIkRlbWFuZC1TaWRlIiwNCiAgICBWYXJpYWJsZSA9PSAiVG90YWxfRmFybWxhbmQiIHwgVmFyaWFibGUgPT0gIlRvdGFsX1VuZGV2ZWxvcGVkIiAgICAgICAgICAgICAgIH4gIlN1aXRhYmxlIiwNCiAgICBUUlVFICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH4gIk5vdCBTdWl0YWJsZSIpKSAlPiUNCiAgZ2dwbG90KGFlcyh4PVZhcmlhYmxlLCB5PVZhbHVlLCBmaWxsPVBsYW5uaW5nX0Rlc2lnbmF0aW9uKSkgKw0KICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoKSwgY29sb3VyPSJibGFjayIpICsNCiAgICBmYWNldF93cmFwKH5DT1VOVFlfTkFNLCBuY29sPTUpICsNCiAgICBjb29yZF9mbGlwKCkgKw0KICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoLS4yNSwgMSwgYnkgPSAuMjUpKSArDQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMi41KSArIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDQuNSkgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJibGFjayIsInJlZCIsImRhcmtncmVlbiIpKSArDQogICAgbGFicyh0aXRsZT0gIkNvdW50eSBTcGVjaWZpYyBBbGxvY2F0aW9uIE1ldHJpY3MiLCBzdWJ0aXRsZT0gIkFzIHJhdGVzIiwgeD0iSW5kaWNhdG9yIiwgeT0iUmF0ZSIpICsNCiAgICBwbG90VGhlbWUgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLCBsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpDQpgYGANCg0KDQojIDcuIEFsbG9jYXRpb24NCg0KQWxsb2NhdGlvbiBpcyB0aGUgZmluYWwgc3RhZ2Ugb2YgdGhlIHVyYmFuIGdyb3d0aCBtb2RlbGluZyBwcm9jZXNzLiBOb3cgdGhhdCBib3RoIGRlbWFuZCBhbmQgc3VwcGx5IGlzIHVuZGVyc3Rvb2QsIFBsYW5uZXJzIGNhbiBhbGxvY2F0ZSBkZXZlbG9wbWVudCByaWdodHMgYWNjb3JkaW5nbHkuIE9mIGNvdXJzZSwgdGhpcyBjb3VsZCB0YWtlIG1hbnkgZm9ybXMgb2YgcmVndWxhdGlvbiBpbmNsdWRpbmcgem9uaW5nLCBzdWJkaXZpc2lvbiBhcHByb3ZhbCBvciBvdXRyaWdodCBjb25zZXJ2YXRpb24uIEluIHRoaXMgc2VjdGlvbiwgZGVtYW5kIGFuZCBzdXBwbHkgYXJlIHZpc3VhbGl6ZWQgZm9yIFdpbGwgQ291bnR5LiANCg0KRmlyc3QsIGRldmVsb3BtZW50IGRlbWFuZCBpcyBwcmVkaWN0ZWQgZm9yIFdpbGwgQ291bnR5LiBUaGVuIGEgbGF5ZXIsIGBXaWxsQ291bnR5X2xhbmRVc2VgIGlzIGNyZWF0ZWQsIHRoYXQgaW5jbHVkZXMgaW5kaWNhdG9ycyBmb3IgYm90aCBwcmV2aW91c2x5IGRldmVsb3BlZCBsYW5kIGFuZCBlbnZpcm9ubWVudGFsbHkgdW5zdWl0YWJsZSBsYW5kLiBUaGlzIGxheWVyIHRoZW4gaXMgb3ZlcmxheWVkIGF0b3AgZGV2ZWxvcG1lbnQgZGVtYW5kIGFuZCBwcm9qZWN0ZWQgcG9wdWxhdGlvbiBjaGFuZ2UgdG8gZ2l2ZSB0aGUgZnVsbCBzdXBwbHkgYW5kIGRlbWFuZC1zaWRlIHBpY3R1cmUgaW4gV2lsbCBDb3VudHkuDQoNClRoZXJlIGFyZSBzb21lIGNsZWFyIG9wcG9ydHVuaXRpZXMgZm9yIGRldmVsb3BtZW50IGluIFdpbGwgQ291bnR5LiBTaWduaWZpY2FudCBpbmZpbGwgb3Bwb3J0dW5pdGllcyBleGlzdCBhbG9uZyB0aGUgc291dGhlcm4gYm91bmRhcnkgd2hlcmUgcG9wdWxhdGlvbiBjaGFuZ2UgaXMgcHJvamVjdGVkIHRvIGJlIGdyZWF0ZXN0LiBUaGVyZSBpcyBhbHNvIGEgZ29vZCBkZWFsIG9mIGVudmlyb25tZW50YWxseSBzdWl0YWJsZSBsYW5kIGluIHRoZSBjZW50ZXIgb2YgdGhlIGNvdW50eSBhbmQgY2xvc2VyIHRvIHRoZSBMYWtlIE1pY2hpZ2FuIHNob3JlbGluZSB0byB0aGUgZWFzdC4gVGhpcyB3b3VsZCBiZSBpZGVhbCBzcGFjZSBmb3IgbGFyZ2UgaG91c2luZyBvciBjb21tZXJjaWFsIGRldmVsb3BtZW50cy4gV2lsbCBDb3VudHkgaGFzIGEgbG90IG9mIGZvcmVzdHMgYW5kIHdldGxhbmRzIGluIHRoZSBzb3V0aHdlc3QgY29ybmVyIG9mIHRoZSBjb3VudHksIHNvIHRoZXJlIGFyZSBzZW5zaXRpdmUgbGFuZCBhcmVhcyBub3Qgc3VpdGFibGUgZm9yIGRldmVsb3BtZW50IHRoZXJlLg0KDQo8ZGl2IGNsYXNzPSJzdXBlcmJpZ2ltYWdlIj4NCmBgYHtyIDY4LCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodD0gOCwgZmlnLndpZHRoPSAxMX0NCldpbGxDb3VudHkgPC0gZGF0MiAlPiUNCiAgbXV0YXRlKERldmVsb3BtZW50X0RlbWFuZCA9IHByZWRpY3QoTW9kZWw2LCBkYXQyLCB0eXBlID0gInJlc3BvbnNlIikpICU+JQ0KICBmaWx0ZXIoQ09VTlRZX05BTSA9PSAiV0lMTCIpIA0KDQpXaWxsQ291bnR5X2xhbmRVc2UgPC0gcmJpbmQoDQogIGZpbHRlcihXaWxsQ291bnR5LCBmb3Jlc3QyMSA9PSAxIHwgd2V0bGFuZHMyMSA9PSAxKSAlPiUNCiAgICBkcGx5cjo6c2VsZWN0KCkgJT4lDQogICAgbXV0YXRlKExhbmRfVXNlID0gIk5vdCBTdWl0YWJsZSIpLA0KICBmaWx0ZXIoV2lsbENvdW50eSwgZGV2MjEgPT0gMSkgJT4lDQogICAgZHBseXI6OnNlbGVjdCgpICU+JQ0KICAgIG11dGF0ZShMYW5kX1VzZSA9ICJEZXZlbG9wZWQiKSkNCg0KIyBDYWxjdWxhdGUgcXVhbnRpbGVzIGZvciBEZXZlbG9wbWVudCBEZW1hbmQgYW5kIFBvcHVsYXRpb24gQ2hhbmdlDQpkZXZfZGVtYW5kX3F1YW50aWxlcyA8LSBxdWFudGlsZShXaWxsQ291bnR5JERldmVsb3BtZW50X0RlbWFuZCwgcHJvYnMgPSBzZXEoMCwgMSwgYnkgPSAwLjI1KSkgIyBDaGFuZ2VkIHRvIDQgcXVhbnRpbGVzDQpwb3BfY2hhbmdlX3F1YW50aWxlcyA8LSBxdWFudGlsZShXaWxsQ291bnR5JHBvcF9DaGFuZ2UsIHByb2JzID0gc2VxKDAsIDEsIGJ5ID0gMC4yNSkpICMgQ2FsY3VsYXRlIHF1YW50aWxlcyBmb3IgcG9wX0NoYW5nZQ0KIA0KZ3JpZC5hcnJhbmdlKA0KICBnZ3Bsb3QoKSArDQogICAgZ2VvbV9zZihkYXRhID0gV2lsbENvdW50eSwgYWVzKGZpbGwgPSBjdXQoRGV2ZWxvcG1lbnRfRGVtYW5kLCBicmVha3MgPSBkZXZfZGVtYW5kX3F1YW50aWxlcykpLCBjb2xvdXIgPSBOQSkgKw0KICAgIGdlb21fcG9pbnQoZGF0YSA9IFdpbGxDb3VudHlfbGFuZFVzZSwgYWVzKHggPSB4eUMoV2lsbENvdW50eV9sYW5kVXNlKVssIDFdLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0geHlDKFdpbGxDb3VudHlfbGFuZFVzZSlbLCAyXSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyID0gTGFuZF9Vc2UpLA0KICAgICAgICAgICAgICAgc2hhcGUgPSAxNSwgc2l6ZSA9IDIpICsNCiAgICBnZW9tX3NmKGRhdGEgPSBzdF9pbnRlcnNlY3Rpb24oVGhyZWVDb3VudGllc19IaWdod2F5cywgZmlsdGVyKFRocmVlQ291bnR5QXJlYSwgQ09VTlRZX05BTSA9PSAiV0lMTCIpKSwgc2l6ZSA9IDIsIGNvbG91ciA9ICJncmF5IikgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGU1LCBuYW1lID0gIkRldmVsb3BtZW50IERlbWFuZCIsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYXMuY2hhcmFjdGVyKHJvdW5kKGRldl9kZW1hbmRfcXVhbnRpbGVzLCBkaWdpdHMgPSAyKSkpICsNCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwgInJlZCIpKSArIA0KICAgIGxhYnModGl0bGUgPSAiRGV2ZWxvcG1lbnQgUG90ZW50aWFsLCAyMDMxOiBXaWxsIENvdW50eSIpICsgbWFwVGhlbWUgKw0KICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSksIGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKSwNCg0KICBnZ3Bsb3QoKSArDQogICAgZ2VvbV9zZihkYXRhID0gV2lsbENvdW50eSwgYWVzKGZpbGwgPSBjdXQocG9wX0NoYW5nZSwgYnJlYWtzID0gcG9wX2NoYW5nZV9xdWFudGlsZXMpKSwgY29sb3VyID0gTkEpICsNCiAgICBnZW9tX3BvaW50KGRhdGEgPSBXaWxsQ291bnR5X2xhbmRVc2UsIGFlcyh4ID0geHlDKFdpbGxDb3VudHlfbGFuZFVzZSlbLCAxXSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IHh5QyhXaWxsQ291bnR5X2xhbmRVc2UpWywgMl0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IExhbmRfVXNlKSwNCiAgICAgICAgICAgICAgIHNoYXBlID0gMTUsIHNpemUgPSAyKSArDQogICAgZ2VvbV9zZihkYXRhID0gc3RfaW50ZXJzZWN0aW9uKFRocmVlQ291bnRpZXNfSGlnaHdheXMsIGZpbHRlcihUaHJlZUNvdW50eUFyZWEsIENPVU5UWV9OQU0gPT0gIldJTEwiKSksIHNpemUgPSAyLCBjb2xvdXIgPSAiZ3JheSIpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNiwgbmFtZSA9ICJQb3B1bGF0aW9uIENoYW5nZSIsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYXMuY2hhcmFjdGVyKHJvdW5kKHBvcF9jaGFuZ2VfcXVhbnRpbGVzLCBkaWdpdHMgPSAyKSkpICsNCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwgInJlZCIpKSArIA0KICAgIGxhYnModGl0bGUgPSAiUHJvamVjdGVkIFBvcHVsYXRpb24sIDIwMzE6IFdpbGwgQ291bnR5IikgKyBtYXBUaGVtZSArDQogICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSwgY29sb3VyID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMikpLCBuY29sID0gMikNCg0KYGBgDQo8L2Rpdj4NClRoZSBwbG90cyBhYm92ZSBhcmUgY3JlYXRlZCB1c2luZyBhIGBnZ3Bsb3RgIHRyaWNrIHRvIHNob3cgd2hhdCBhcHBlYXJzIHRvIGJlIG92ZXJsYXllZCBwb2x5Z29ucyAoZmlzaG5ldCBncmlkIGNlbGxzKS4gZ2dwbG90IGRvZXMgbm90IG5hdGl2ZWx5IGFsbG93IG11bHRpcGxlIGFlc3RoZXRpY3MgKGBhZXNgKSBvZiB0aGUgc2FtZSBzdHlsZS4gSW4gcHJhY3RpY2UsIHRoaXMgbWVhbnMgaXQgaXMgbm90IHBvc3NpYmxlIHRvIGhhdmUgdHdvIGBzY2FsZV9maWxsX21hbnVhbGAgcGFyYW1ldGVycyBhbmQgdGh1cywgdHdvIGxlZ2VuZHMgZm9yIHRoZSBzYW1lIG1hcC4gVGhpcyBsaW1pdGF0aW9uIGlzIGNsZXZlcmx5IGF2b2lkZWQgYnkgcGxvdHRpbmcgYFdpbGxDb3VudHlfbGFuZFVzZWAgYXMgY29sb3JlZCBwb2ludHMgKGFzIG9wcG9zZWQgdG8gZmlsbGVkIGdyaWQgY2VsbHMpLiBJbiB0aGUgYGdlb21fcG9pbnRgIHBhcmFtZXRlciBhYm92ZSwgdGhlIHBvaW50cyBhcmUgc2V0IHRvIGBzaGFwZSA9IDE1YCwgd2hpY2ggaXMgYSBmaWxsZWQgYm94LiBUaGlzIGJveCBjYW4gdGhlbiBiZSBzaXplZCB0byBtYWtlIGl0IGFwcGVhciBsaWtlIGEgZmlzaG5ldCBncmlkIGNlbGwuDQoNCldlIHN0b3Agc2hvcnQgaW4gYWN0dWFsbHkgYWxsb2NhdGluZyBsYW5kIHRvIGRldmVsb3BtZW50LiBXaGlsZSB0aGUgbW9kZWwgaXMgd2VsbCBzdWl0ZWQgZm9yIHVuZGVyc3RhbmRpbmcgc3ByYXdsLXN0eWxlIGRldmVsb3BtZW50LCBpdCBpcyBub3QgdXNlZnVsIGZvciB1bmRlcnN0YW5kaW5nIGhvdyBuZXcgZGVtYW5kIG1pZ2h0IGJlIGFic29yYmVkIGJ5IHVwem9uaW5nIGFuZCBkZW5zaWZpY2F0aW9uIG9mIGV4aXN0aW5nIGRldmVsb3BtZW50LiBJdCB3b3VsZCBub3QgYmUgd2lzZSB0byBhbGxvY2F0ZSB0aGUgZW50aXJlIHByb2plY3RlZCBwb3B1bGF0aW9uIHRvIHVuZGV2ZWxvcGVkIGxhbmQuIEluc3RlYWQsIHdl4oCZZCBwcmVmZXIgYSBtb3JlIG51YW5jZWQgdW5kZXJzdGFuZGluZyBvZiBob3cgbG9jYWwgbGFuZCB1c2UgbGF3cyBtaWdodCBwbGF5IGEgcm9sZS4gQXQgdGhpcyBzdGFnZSBpbiB0aGUgYW5hbHlzaXMgaG93ZXZlciwgdGhlIFBsYW5uZXIgaGFzIGFsbCBzaGUgbmVlZHMgdG8gZW5nYWdlIGxvY2FsIHN0YWtlaG9sZGVycyBhYm91dCBmdXR1cmUgZGV2ZWxvcG1lbnQgZGVjaXNpb25zLg0KDQoNCiMgNy4yIFNjZW5hcmlvIDI6IEVzdGltYXRpbmcgdGhlIEVmZmVjdCBvZiBOZXcgVHJhbnNwb3J0YXRpb24NCg0KV2UgY3JlYXRlZCBhIG5ldyByZWdpb25hbCByYWlsIGxpbmUgdG8gc2ltdWxhdGUgdGhlIGV4dGVuc2lvbiBvZiB0aGUgTWV0cmEgcmFpbCBzeXN0ZW0gaW4gdGhlIENoaWNhZ28gcmVnaW9uLiBUaGUgcmFpbCBleHRlbnNpb24gZXhwYW5kcyB0aGUgc3lzdGVtIHNvdXRod2VzdCBpbnRvIFdpbGwgQ291bnR5LiBZb3UgY2FuIHNlZSB0aGUgZXhpc3Rpbmcgc3lzdGVtIGFuZCB0aGUgZXh0ZW5zaW9uIHBsb3R0ZWQuIA0KDQpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHJlc3VsdHMgPSAiaGlkZSJ9DQpNZXRyYUxpbmVzd0V4dGVuc2lvbiA8LQ0KICBzdF9yZWFkKCJDOi9Vc2Vycy8zbHBhdy9EZXNrdG9wL0FyY0dJUyBQcm8gMy4yL0Vudk1vZGVsaW5nLzA0XzI0XzI0X1VyYmFuR3Jvd3RoTW9kZWxpbmcvRG93bmxvYWRlZF9EYXRhL01ldHJhX0xpbmVzL01ldHJhTGluZXN3RXh0ZW5zaW9uLnNocCIpICU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKFRocmVlQ291bnR5QXJlYSkpICU+JQ0KICBzdF9pbnRlcnNlY3Rpb24oVGhyZWVDb3VudHlBcmVhKQ0KYGBgDQoNCmBgYHtyIDcwfQ0KbGlicmFyeShncmlkRXh0cmEpDQoNCiMgQWRqdXN0IHRoZSB3aWR0aHMgcGFyYW1ldGVyIHRvIGNvbnRyb2wgdGhlIHNpemUgb2YgZWFjaCBwbG90DQpncmlkLmFycmFuZ2UoDQogIGdncGxvdCgpICsNCiAgICBnZW9tX3NmKGRhdGE9VGhyZWVDb3VudHlBcmVhLCBmaWxsID0gInRyYW5zcGFyZW50IikgKw0KICAgIGdlb21fc2YoZGF0YT1UaHJlZUNvdW50aWVzX1JlZ1JhaWwsIGNvbG9yID0gInJlZCIpICsNCiAgICBsYWJzKHRpdGxlID0gIkV4aXN0aW5nIFJlZ2lvbmFsIFJhaWwgTGluZXMiKSArDQogICAgbWFwVGhlbWUsDQoNCiAgZ2dwbG90KCkgKw0KICAgIGdlb21fc2YoZGF0YT1UaHJlZUNvdW50eUFyZWEsIGZpbGwgPSAidHJhbnNwYXJlbnQiKSArDQogICAgZ2VvbV9zZihkYXRhPU1ldHJhTGluZXN3RXh0ZW5zaW9uLCBjb2xvciA9ICJyZWQiKSArDQogICAgbGFicyh0aXRsZSA9ICJSZWdpb25hbCBSYWlsIExpbmVzIHdpdGggRXh0ZW5zaW9uIikgKw0KICAgIG1hcFRoZW1lLA0KICANCiAgIyBBZGp1c3Qgd2lkdGhzIGFzIG5lZWRlZCB0byBjb250cm9sIHRoZSBzaXplIG9mIGVhY2ggcGxvdA0KICBucm93ID0gMSwgDQogIHdpZHRocyA9IGMoMiwgMikNCikNCg0KYGBgDQoNCk5vdyB3ZSByZWNyZWF0ZSB0aGUgcmVnaW9uYWwgcmFpbCBmaXNobmV0LCB1c2luZyB0aGUgc2hhcGVmaWxlIHRoYXQgaGFzIHRoZSBuZXcgcmFpbCBleHRlbnNpb24gYW5kIHdlIGNhbGN1bGF0ZSB0aGUgZGlzdGFuY2UgdG8gbmVhcmVzdCByYWlsIGxpbmUgYWdhaW4uDQoNCg0KYGBge3J9DQojRGV0ZXJtaW5lIG5lYXJlc3QgcmVnaW9uYWwgcmFpbCBsaW5lIHRvIGVhY2ggY2VudHJvaWQNCm5lYXJlc3RfbWV0cmEgPC0gc3RfbmVhcmVzdF9mZWF0dXJlKGNlbnRyb2lkLE1ldHJhTGluZXN3RXh0ZW5zaW9uKQ0KDQojQ2FsY3VhdGUgZGlzdGFuY2UgZnJvbSBlYWNoIGdyaWQgc3F1YXJlIGNlbnRyb2lkIHRvIG5lYXJlc3QgcmVnaW9uYWwgcmFpbCBsaW5lDQpUaHJlZUNvdW50eV9maXNobmV0JG1ldHJhX2Rpc3QgPC0gYXMuZG91YmxlKHN0X2Rpc3RhbmNlKGNlbnRyb2lkLCBNZXRyYUxpbmVzd0V4dGVuc2lvbltuZWFyZXN0X21ldHJhLF0sIGJ5X2VsZW1lbnQ9VFJVRSkpDQoNCk1ldHJhX2Zpc2huZXQgPC0gVGhyZWVDb3VudHlfZmlzaG5ldCAjJT4lDQojICBzZWxlY3QoZmlzaG5ldElELCBnZW9tZXRyeSwgcmVncmFpbF9kaXN0KQ0KDQojTWFrZSBhIHF1aWNrIHNhbXBsZSBtYXAgb2YgdGhlIHJlc3VsdHMNCmdncGxvdCgpKw0KICBnZW9tX3NmKGRhdGE9TWV0cmFfZmlzaG5ldCxhZXMoZmlsbD1tZXRyYV9kaXN0KSxjb2xvcj0ndHJhbnNwYXJlbnQnKSsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MobmFtZT0nRGlzdGFuY2UgdG8gUmVnaW9uYWwgUmFpbCBMaW5lcyAoZmVldCknKSsNCiAgZ2VvbV9zZihkYXRhPU1ldHJhTGluZXN3RXh0ZW5zaW9uLGNvbG9yPSdyZWQnKSsNCiAgbGFicyh0aXRsZSA9ICJEaXN0YW5jZSB0byBSZWdpb25hbCBSYWlsIExpbmVzIHdpdGggRXh0ZW5zaW9uIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJVc2luZyBmaXNobmV0IGNlbnRyb2lkcyIpICsNCiAgdGhlbWVfdm9pZCgpDQpgYGANCg0KTmV4dCwgd2UgcmVjcmVhdGUgdGhlIGZpbmFsIGRhdGFzZXQgdG8gaW5jbHVkZSB0aGUgbmV3IHZhcmlhYmxlIChyZWdpb25hbCByYWlsIHdpdGggZXh0ZW5zaW9uKSBhbHNvIGNhbGxlZCBtZXRyYV9kaXN0IGhlcmUuIFRoZSBvdGhlciBmaXNobmV0cyBhcmUgY29tYmluZWQgaGVyZSB0byBpbmNsdWRlIGFsbCBvZiB0aGUgcHJldmlvdXMgdmFyaWFibGVzLg0KDQpgYGB7cn0NCmRhdDMgPC0NCiAgY2JpbmQoDQogICAgZmlzaG5ldCwgZmlzaG5ldFBvcHVsYXRpb24sIEhpZ2h3YXlfZmlzaG5ldCwgUmVnUmFpbF9maXNobmV0LCBNZXRyYV9maXNobmV0LCBEb3dudG93bl9maXNobmV0LCBMYWtlTWljaGlnYW5fZmlzaG5ldCwgYWdncmVnYXRlZFJhc3RlcnMpICU+JQ0KICBkcGx5cjo6c2VsZWN0KERldl9jaGFuZ2UsIGRldmVsb3BlZCwgZm9yZXN0LCBmYXJtLCB3ZXRsYW5kcywgb3RoZXJVbmRldmVsb3BlZCwgd2F0ZXIsDQogICAgICAgICAgICAgICAgUG9wXzIwMTEsIFBvcF8yMDIxLCBwb3BfQ2hhbmdlLCBoaWdod2F5X2Rpc3QsIHJlZ3JhaWxfZGlzdCwgbWV0cmFfZGlzdCwgZG93bnRvd25fZGlzdCwgbGFrZV9kaXN0LCBsYWdEZXZlbG9wbWVudCkgJT4lDQogIHN0X2pvaW4oVGhyZWVDb3VudHlBcmVhKSAlPiUNCiAgbXV0YXRlKGRldmVsb3BlZDIxID0gaWZlbHNlKERldl9jaGFuZ2UgPT0gMSAmIGRldmVsb3BlZCA9PSAxLCAwLCBkZXZlbG9wZWQpKSAlPiUNCiAgZmlsdGVyKHdhdGVyID09IDApDQpgYGANCg0KRXhwbG9yYXRvcnkgYW5hbHlzaXM6IA0KDQpJbiB0aGlzIHNlY3Rpb24gd2UgZXhwbG9yZSB0aGUgZXh0ZW50IHRvIHdoaWNoIGVhY2ggZmVhdHVyZXMgaXMgYXNzb2NpYXRlZCB3aXRoIGRldmVsb3BtZW50IGNoYW5nZS4gSWYgdGhlIGdvYWwgd2FzIHRvIHByZWRpY3QgYSBjb250aW51b3VzIHZhcmlhYmxlLCBzY2F0dGVycGxvdHMgYW5kIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cyBtYWtlIHRoaXMgcHJvY2VzcyBzdHJhaWdodGZvcndhcmQgYW5kIHJlbGF0aXZlbHkgZWFzeSB0byBleHBsYWluIHRvIGEgbm9uLXRlY2huaWNhbCBkZWNpc29uIG1ha2VyLg0KDQpJbiB0aGlzIGNhc2UgaG93ZXZlciwgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSBpcyBhIGJpbmFyeSBvdXRjb21lIC0gZWl0aGVyIGEgZ3JpZCBjZWxsIHdhcyBkZXZlbG9wZWQgYmV0d2VlbiAyMDExIGFuZCAyMDIxIG9yIGl0IHdhc27igJl0LiBJbiB0aGlzIGNhc2UsIHRoZSByZWxldmFudCBxdWVzdGlvbiBpcyB3aGV0aGVyIGZvciBhIGdpdmVuIGZlYXR1cmUsIHRoZXJlIGlzIGEgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGJldHdlZW4gYXJlYXMgdGhhdCBjaGFuZ2VkIGFuZCBhcmVhcyB0aGF0IGRpZCBub3QuIFRoZXNlIGRpZmZlcmVuY2VzIGFyZSBleHBsb3JlZCBpbiBhIHNldCBvZiBwbG90cyBiZWxvdy4gRm9yIG1vZGVscyB3aXRoIGxvdHMgb2YgZmVhdHVyZXMsIHRoZXNlIHBsb3RzIGNvdWxkIGJlIGNvbXBsaW1lbnQgYnkgYSBzZXJpZXMgb2YgZGlmZmVyZW5jZSBpbiBtZWFucyBzdGF0aXN0aWNhbCB0ZXN0cy4NCg0KVGhlIGJlbG93IGNvZGUgYmxvY2sgYHNlbGVjdGBzIHRoZSBkaXN0YW5jZSBhbmQgc3BhdGlhbCBsYWcgZmVhdHVyZXMsIGNvbnZlcnRzIGVhY2ggdG8gbG9uZyBmb3JtIGFuZCBwbG90cyBlYWNoIGFzIGJhciBwbG90cy4gTm90ZSB0aGF0IGBnZW9tX2JhcmAgY2FsY3VsYXRlcyB0aGUgYG1lYW5gLiANCg0KYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0KZGF0MyAlPiUNCiAgZHBseXI6OnNlbGVjdChwb3BfQ2hhbmdlLCBoaWdod2F5X2Rpc3QsIHJlZ3JhaWxfZGlzdCwgbWV0cmFfZGlzdCwgZG93bnRvd25fZGlzdCwgbGFrZV9kaXN0LCBsYWdEZXZlbG9wbWVudCwgRGV2X2NoYW5nZSkgJT4lDQogIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1EZXZfY2hhbmdlLCAtZ2VvbWV0cnkpICU+JQ0KICBnZ3Bsb3QoLiwgYWVzKERldl9jaGFuZ2UsIFZhbHVlLCBmaWxsPURldl9jaGFuZ2UpKSArIA0KICAgIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJzdW1tYXJ5IiwgZnVuLnkgPSAibWVhbiIpICsNCiAgICBmYWNldF93cmFwKH5WYXJpYWJsZSkgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSwNCiAgICAgICAgICAgICAgICAgICAgICBuYW1lPSIiKSArDQogICAgbGFicyh0aXRsZT0iTmV3IERldmVsb3BtZW50IGFzIGEgRnVuY3Rpb24gb2YgdGhlIENvbnRpbnVvdXMgVmFyaWFibGVzIikgKw0KICAgIHBsb3RUaGVtZQ0KYGBgDQoNClRoZSBwbG90IGZvciBtZXRyYV9kaXN0IChkaXN0YW5jZSB0byBuZWFyZXN0IHJlZ2lvbmFsIHJhaWwgbGluZSwgaW5jbHVkaW5nIHRoZSBuZXcgZXh0ZW5zaW9uKSBkb2VzIG5vdCBsb29rIHRoYXQgZGlmZmVyZW50IGZyb20gdGhlIHBsb3QgZm9yIHJlZ3JhaWxfZGlzdCAoaW5jbHVkZXMgb25seSBleGlzdGluZyByZWdpb25hbCByYWlsIGxpbmVzKS4gSG93ZXZlciwgdGhlcmUgaXMgYSBzbGlnaHQgZGlmZmVyZW5jZSBiZXR3ZWVuIG5vIGNoYW5nZSBhbmQgbmV3IGRldmVsb3BtZW50IGZvciB0aGUgbWV0cmFfZGlzdCB2YXJpYWJsZS4NCg0KDQojIyA3LjIuYSBNb2RlbGluZw0KDQpGaXJzdCwgYGRhdDNgIGlzIHNwbGl0IGludG8gdHJhaW5pbmcgYW5kIHRlc3Qgc2V0cy4gDQoNCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCnNldC5zZWVkKDM0NTYpDQp0cmFpbkluZGV4MyA8LSANCiAgY3JlYXRlRGF0YVBhcnRpdGlvbihkYXQzJGRldmVsb3BlZCwgcCA9IC41MCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZXMgPSAxKQ0KZGF0M1RyYWluIDwtIGRhdDNbIHRyYWluSW5kZXgzLF0NCmRhdDNUZXN0ICA8LSBkYXQzWy10cmFpbkluZGV4MyxdDQoNCm5yb3coZGF0MykNCmBgYA0KDQoNCmBNb2RlbDdgIGluY2x1ZGVzIGRpc3RhbmNlIHRvIHRoZSBoaWdod2F5cyBhbmQgYWxsIG90aGVyIHZhcmlhYmxlcyBpbmNsdWRpbmcgdGhlIG5ldyByYWlsIGV4dGVuc2lvbiwgYW5kIGlzIHRoZSBmaW5hbCBtb2RlbCBlbXBsb3llZCBmb3IgcHJlZGljdGlvbi4gSSByZW1vdmVkIHRoZSByZWdyYWlsX2Rpc3QgYmVjYXVzZSBpdCBpcyB0aGUgZXhpc3RpbmcgcmFpbCBsaW5lcyB3aXRob3V0IHRoZSBuZXcgZXh0ZW5zaW9uLg0KDQpgYGB7cn0NCk1vZGVsMSA8LSBnbG0oRGV2X2NoYW5nZSB+IHdldGxhbmRzICsgZm9yZXN0ICArIGZhcm0gKyBvdGhlclVuZGV2ZWxvcGVkLCANCiAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKQ0KDQpNb2RlbDIgPC0gZ2xtKERldl9jaGFuZ2UgfiB3ZXRsYW5kcyArIGZvcmVzdCAgKyBmYXJtICsgb3RoZXJVbmRldmVsb3BlZCArIGxhZ0RldmVsb3BtZW50LCANCiAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKQ0KICAgICAgICAgICAgICANCk1vZGVsMyA8LSBnbG0oRGV2X2NoYW5nZSB+IHdldGxhbmRzICsgZm9yZXN0ICArIGZhcm0gKyBvdGhlclVuZGV2ZWxvcGVkICsgbGFnRGV2ZWxvcG1lbnQgKyBQb3BfMjAxMSwgDQogICAgICAgICAgICAgIGZhbWlseT0iYmlub21pYWwiKGxpbms9ImxvZ2l0IiksIGRhdGEgPSBkYXRUcmFpbikgICAgICAgICAgDQogICAgICAgICAgICAgIA0KTW9kZWw0IDwtIGdsbShEZXZfY2hhbmdlIH4gd2V0bGFuZHMgKyBmb3Jlc3QgICsgZmFybSArIG90aGVyVW5kZXZlbG9wZWQgKyBsYWdEZXZlbG9wbWVudCArIFBvcF8yMDExICsgDQogICAgICAgICAgICAgIFBvcF8yMDIxLCANCiAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKSAgICAgICAgICAgICAgDQogICAgICAgICAgICANCk1vZGVsNSA8LSBnbG0oRGV2X2NoYW5nZSB+IHdldGxhbmRzICsgZm9yZXN0ICArIGZhcm0gKyBvdGhlclVuZGV2ZWxvcGVkICsgbGFnRGV2ZWxvcG1lbnQgKyBwb3BfQ2hhbmdlLCANCiAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKSAgICAgICAgICAgICAgDQogICAgICAgICAgICAgIA0KTW9kZWw2IDwtIGdsbShEZXZfY2hhbmdlIH4gd2V0bGFuZHMgKyBmb3Jlc3QgICsgZmFybSArIG90aGVyVW5kZXZlbG9wZWQgKyBsYWdEZXZlbG9wbWVudCArIHBvcF9DaGFuZ2UgKyANCiAgICAgICAgICAgICAgaGlnaHdheV9kaXN0ICsgZG93bnRvd25fZGlzdCArIGxha2VfZGlzdCArIHJlZ3JhaWxfZGlzdCwgDQogICAgICAgICAgICAgIGZhbWlseT0iYmlub21pYWwiKGxpbms9ImxvZ2l0IiksIGRhdGEgPSBkYXRUcmFpbikgDQoNCk1vZGVsNyA8LSBnbG0oRGV2X2NoYW5nZSB+IHdldGxhbmRzICsgZm9yZXN0ICArIGZhcm0gKyBvdGhlclVuZGV2ZWxvcGVkICsgbGFnRGV2ZWxvcG1lbnQgKyBwb3BfQ2hhbmdlICsgDQogICAgICAgICAgICAgIGhpZ2h3YXlfZGlzdCArIGRvd250b3duX2Rpc3QgKyBsYWtlX2Rpc3QgKyBtZXRyYV9kaXN0LCANCiAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdDNUcmFpbikgDQpgYGANCg0KQ29tcGFyaW5nIG1vZGVsczoNCg0KYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0KbW9kZWxMaXN0IDwtIHBhc3RlMCgiTW9kZWwiLCAxOjcpDQptYXBfZGZjKG1vZGVsTGlzdCwgZnVuY3Rpb24oeClwUjIoZ2V0KHgpKSlbNCxdICU+JQ0KICBzZXROYW1lcyhwYXN0ZTAoIk1vZGVsIiwxOjcpKSAlPiUNCiAgZ2F0aGVyKE1vZGVsLE1jRmFkZGVuKSAlPiUNCiAgZ2dwbG90KGFlcyhNb2RlbCxNY0ZhZGRlbikpICsNCiAgICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpICsNCiAgICBsYWJzKHRpdGxlPSAiTWNGYWRkZW4gUi1TcXVhcmVkIGJ5IE1vZGVsIikgKw0KICAgIHBsb3RUaGVtZQ0KYGBgDQoNCk5leHQsIGEgZGF0YSBmcmFtZSBpcyBjcmVhdGVkIHRoYXQgaW5jbHVkZXMgY29sdW1ucyBmb3IgdGhlIG9ic2VydmVkIGRldmVsb3BtZW50IGNoYW5nZSwgYERldl9jaGFuZ2VgLCBhbmQgb25lIHRoYXQgaW5jbHVkZXMgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgZm9yIGBNb2RlbDdgLiBUaGlzIGRhdGEgZnJhbWUgaXMgdGhlbiB1c2VkIGFzIGFuIGlucHV0IHRvIGEgZGVuc2l0eSBwbG90IHZpc3VhbGl6aW5nIHRoZSBkaXN0cmlidXRpb24gb2YgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgYnkgb2JzZXJ2ZWQgY2xhc3MuIE9ubHkgYSBzbWFsbCBudW1iZXIgb2YgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgYXJlIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byA1MCUgYChucm93KGZpbHRlcih0ZXN0U2V0UHJvYnMsIHByb2JzID49IC41MCkpIC8gbnJvdyhkYXRUZXN0KSlgLiBUaGlzIG1ha2VzIGdvb2Qgc2Vuc2UsIGdpdmVuIGhvdyByYXJlIG9mIGFuIGV2ZW50IGRldmVsb3BtZW50IGlzIGluIG91ciBkYXRhc2V0LiBVbHRpbWF0ZWx5LCBpbiBvcmRlciB0byBqdWRnZSBvdXIgbW9kZWwgd2l0aCBhIGNvbmZ1c2lvbiBtYXRyaXgsIGEgc21hbGxlciBkZXZlbG9wbWVudCBjbGFzc2lmaWNhdGlvbiB0aHJlc2hvbGQgbXVzdCBiZSBlbXBsb3llZC4NCg0KYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0KdGVzdFNldFByb2JzMiA8LSANCiAgZGF0YS5mcmFtZShjbGFzcyA9IGRhdDNUZXN0JERldl9jaGFuZ2UsDQogICAgICAgICAgICAgcHJvYnMgPSBwcmVkaWN0KE1vZGVsNywgZGF0M1Rlc3QsIHR5cGU9InJlc3BvbnNlIikpIA0KICANCmdncGxvdCh0ZXN0U2V0UHJvYnMyLCBhZXMocHJvYnMpKSArDQogIGdlb21fZGVuc2l0eShhZXMoZmlsbD1jbGFzcyksIGFscGhhPTAuNSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5ldyBEZXZlbG9wbWVudCIpKSArDQogIGxhYnModGl0bGUgPSAiSGlzdG9ncmFtIG9mIHRlc3Qgc2V0IHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIiwNCiAgICAgICB4PSJQcmVkaWN0ZWQgUHJvYmFiaWxpdGllcyIseT0iRGVuc2l0eSIpICsNCiAgcGxvdFRoZW1lDQpgYGANCg0KQ2FsY3VsYXRpbmcgQWNjdXJhY3kgZm9yIHRoaXMgbW9kZWw6DQoNCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCm9wdGlvbnMoeWFyZHN0aWNrLmV2ZW50X2ZpcnN0ID0gRkFMU0UpDQoNCnRlc3RTZXRQcm9iczIgPC0gDQogIHRlc3RTZXRQcm9iczIgJT4lIA0KICBtdXRhdGUocHJlZENsYXNzXzA1ID0gYXMuZmFjdG9yKGlmZWxzZSh0ZXN0U2V0UHJvYnMyJHByb2JzID49IDAuMDUgLDEsMCkpLA0KICAgICAgICAgcHJlZENsYXNzXzE3ID0gYXMuZmFjdG9yKGlmZWxzZSh0ZXN0U2V0UHJvYnMyJHByb2JzID49IDAuMTcgLDEsMCkpKSANCg0KdGVzdFNldFByb2JzMiAlPiUNCiAgZHBseXI6OnNlbGVjdCgtcHJvYnMpICU+JQ0KICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtY2xhc3MpICU+JQ0KICBncm91cF9ieShWYXJpYWJsZSkgJT4lDQogIHN1bW1hcml6ZShTZW5zaXRpdml0eSA9IHJvdW5kKHlhcmRzdGljazo6c2Vuc192ZWMoY2xhc3MsZmFjdG9yKFZhbHVlKSksMiksDQogICAgICAgICAgICBTcGVjaWZpY2l0eSA9IHJvdW5kKHlhcmRzdGljazo6c3BlY192ZWMoY2xhc3MsZmFjdG9yKFZhbHVlKSksMiksDQogICAgICAgICAgICBBY2N1cmFjeSA9IHJvdW5kKHlhcmRzdGljazo6YWNjdXJhY3lfdmVjKGNsYXNzLGZhY3RvcihWYWx1ZSkpLDIpKSAlPiUgDQogIGthYmxlKCkgJT4lDQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEYpDQpgYGANCg0KVGhlIHJlc3VsdHMgYXJlIGEgYml0IHNpbWlsYXIgdG8gbW9kZWwgNiwgYnV0IHRoZSBhY2N1cmFjeSBpbmNyZWFzZWQgYnkgMC4wMSBmb3IgZWFjaCB2YXJpYWJsZS4NCg0KYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0KcHJlZHNGb3JNYXAyIDwtICAgICAgICAgDQogIGRhdDMgJT4lDQogICAgbXV0YXRlKHByb2JzID0gcHJlZGljdChNb2RlbDcsIGRhdDMsIHR5cGU9InJlc3BvbnNlIikgLA0KICAgICAgICAgICBUaHJlc2hvbGRfNV9QY3QgPSBhcy5mYWN0b3IoaWZlbHNlKHByb2JzID49IDAuMDUgLDEsMCkpLA0KICAgICAgICAgICBUaHJlc2hvbGRfMTdfUGN0ID0gIGFzLmZhY3RvcihpZmVsc2UocHJvYnMgPj0gMC4xNyAsMSwwKSkpICU+JQ0KICAgIGRwbHlyOjpzZWxlY3QoRGV2X2NoYW5nZSxUaHJlc2hvbGRfNV9QY3QsVGhyZXNob2xkXzE3X1BjdCkgJT4lDQogICAgZ2F0aGVyKFZhcmlhYmxlLFZhbHVlLCAtZ2VvbWV0cnkpICU+JQ0KICAgIHN0X2Nhc3QoIlBPTFlHT04iKQ0KYGBgDQoNCg0KPGRpdiBjbGFzcz0ic3VwZXJiaWdpbWFnZSI+DQpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPSBGQUxTRSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aD0gOH0NCmdncGxvdCgpICsNCiAgZ2VvbV9wb2ludChkYXRhPXByZWRzRm9yTWFwMiwgYWVzKHg9eHlDKHByZWRzRm9yTWFwMilbLDFdLCB5PXh5QyhwcmVkc0Zvck1hcDIpWywyXSwgY29sb3VyPVZhbHVlKSkgKw0KICBmYWNldF93cmFwKH5WYXJpYWJsZSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLCBsYWJlbHM9YygiTm8gQ2hhbmdlIiwiTmV3IERldmVsb3BtZW50IiksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZT0iIikgKw0KICBsYWJzKHRpdGxlPSJEZXZlbG9wbWVudCBQcmVkaWN0aW9ucyAtIExvdyBUaHJlc2hvbGQiKSArIA0KICBnZW9tX3NmKGRhdGE9VGhyZWVDb3VudHlBcmVhLCBmaWxsID0gInRyYW5zcGFyZW50IikgKw0KICBtYXBUaGVtZQ0KYGBgDQoNCg0KUHJlZGljdGluZyBEZXZlbG9wbWVudCBEZW1hbmQNCg0KTmV4dCwgdGhlIGBQb3BfMjAzMWAgdGFibGUgaXMgam9pbmVkIHRvIGBkYXQzYCBhbmQgYHBvcF9jaGFuZ2VgIGluIG9yZGVyIHRvIOKAmGRpc3RyaWJ1dGXigJkgdGhlIG5ldyBwb3B1bGF0aW9uIGFjcm9zcyB0aGUgc3R1ZHkgYXJlYS4gVG8gZG8gc28sIHRoZSB0aGUgYWxsb2NhdGlvbiBvZiBuZXcgcG9wdWxhdGlvbiBpcyB3ZWlnaHRlZCBieSBhIGdyaWQgY2VsbOKAmXMgZXhpc3RpbmcgcG9wdWxhdGlvbiAoYHBvcF8yMDMxLmluZmlsbGApLiAyMDEwIHBvcHVsYXRpb24gaXMgc3VidHJhY3RlZCBmcm9tIHRoaXMgZmlndXJlIHRvIGdldCBgcG9wX0NoYW5nZWAuIEZpbmFsbHksIGBNb2RlbDdgIGlzIHVzZWQgdG8gcHJlZGljdCBmb3IgMjAzMSBnaXZlbiB0aGUgdXBkYXRlZCBwb3B1bGF0aW9uIGNoYW5nZSBhbmQgbGFnIGRldmVsb3BtZW50IGZlYXR1cmVzLg0KDQpUaGUgbWFwIG9mIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIHRoYXQgcmVzdWx0cyBpcyBiZXN0IHRob3VnaHQgb2YgYXMgYSBtZWFzdXJlIG9mIHByZWRpY3RlZCBkZXZlbG9wbWVudCBkZW1hbmQgaW4gMjAzMS4NCg0KYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0KZGF0M19pbmZpbGwgPC0NCiAgZGF0MyAlPiUNCiAgI2NhbGN1bGF0ZSBwb3B1bGF0aW9uIGNoYW5nZQ0KICAgIGxlZnRfam9pbihQb3BfMjAzMSwgYnkgPSBjKCJDT1VOVFlfTkFNIiA9ICJDb3VudGllcyIpKSAlPiUNCiAgICBtdXRhdGUocHJvcG9ydGlvbl9vZl9jb3VudHlfcG9wID0gcG9wXzIwMjEgLyBBbGxDb3VudGllc19Qb3AyMDIxLA0KICAgICAgICAgICBwb3BfMjAzMS5pbmZpbGwgPSBwcm9wb3J0aW9uX29mX2NvdW50eV9wb3AgKiBwb3BfMjAzMSwNCiAgICAgICAgICAgcG9wX0NoYW5nZSA9IHJvdW5kKHBvcF8yMDMxLmluZmlsbCAtIHBvcF8yMDIxKSwyKSAlPiUNCiAgICBkcGx5cjo6c2VsZWN0KC1wb3BfMjAzMSwgLUFsbENvdW50aWVzX1BvcDIwMjEsIA0KICAgICAgICAgICAgICAgICAgLXByb3BvcnRpb25fb2ZfY291bnR5X3BvcCwgLXBvcF8yMDMxLmluZmlsbCkgJT4lDQogICNwcmVkaWN0IGZvciAyMDMxDQogICAgbXV0YXRlKHByZWRpY3RfMjAzMS5pbmZpbGwgPSBwcmVkaWN0KE1vZGVsNywuICwgdHlwZT0icmVzcG9uc2UiKSkNCg0KIyBDYWxjdWxhdGUgcXVpbnRpbGUgYnJlYWtzDQpxdWludGlsZV9icmVha3MgPC0gcXVpbnRpbGVCcmVha3MoZGF0M19pbmZpbGwsICJwcmVkaWN0XzIwMzEuaW5maWxsIikNCg0KIyBTb3J0IHRoZSBicmVha3MgaW4gYXNjZW5kaW5nIG9yZGVyDQpzb3J0ZWRfYnJlYWtzIDwtIHNvcnQocXVpbnRpbGVfYnJlYWtzKQ0KDQpkYXQzX2luZmlsbCAlPiUNCiAgZ2dwbG90KCkgKyAgDQogIGdlb21fcG9pbnQoYWVzKHg9eHlDKGRhdDNfaW5maWxsKVssMV0sIHk9eHlDKGRhdDNfaW5maWxsKVssMl0sIGNvbG91ciA9IGZhY3RvcihudGlsZShwcmVkaWN0XzIwMzEuaW5maWxsLDUpKSkpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gc3Vic3RyKHNvcnRlZF9icmVha3MsIDEsIDQpLA0KICAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZVxuQnJlYWtzIikgKw0KICBnZW9tX3NmKGRhdGE9VGhyZWVDb3VudHlBcmVhLCBmaWxsPU5BLCBjb2xvdXI9ImJsYWNrIiwgc2l6ZT0xKSArDQogIGxhYnModGl0bGU9ICJEZXZlbG9wbWVudCBEZW1hbmQgaW4gMjAzMTogUHJlZGljdGVkIFByb2JhYmlsaXRpZXMiKSArDQogIG1hcFRoZW1lDQoNCmBgYA0KDQpTdW1tYXJpemUgYnkgQ291bnR5DQoNClRoZSBiZWxvdyBgZHBseXJgIHN0YXRlbWVudCB0YWtlcyBhcyBpdHMgaW5wdXQsIGBkYXQzYCwgd2hpY2ggd3JhbmdsZXMgdG9nZXRoZXIgYSB0YWJsZSBvZiBjb3VudHktbGV2ZWwsIHN1cHBseSBhbmQgZGVtYW5kIG1ldHJpY3Mgd2hpY2ggY2FuIGJlIHVzZWQgdG8gYW5hbHl6ZSBzdWl0YWJpbGl0eSBieSBjb3VudHkuDQoNCk5leHQsIGVhY2ggcmFzdGVyIGlzIGFnZ3JlZ2F0ZWQgdG8gdGhlIGZpc2huZXQgdXNpbmcgdGhlIGBhZ2dyZWdhdGVSYXN0ZXJgIGZ1bmN0aW9uIGFuZCAyMDIxIGxhbmQgY292ZXIgdHlwZXMgYXJlIG1hcHBlZC4NCg0KYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0KdGhlUmFzdGVyTGlzdDIxIDwtIGMoZGV2ZWxvcGVkMjEsZm9yZXN0MjEsZmFybTIxLHdldGxhbmRzMjEsb3RoZXJVbmRldmVsb3BlZDIxLHdhdGVyMjEpDQoNCmRhdDQgPC0NCiAgYWdncmVnYXRlUmFzdGVyKHRoZVJhc3Rlckxpc3QyMSwgZGF0MykgJT4lDQogIGRwbHlyOjpzZWxlY3QoZGV2MjEsZm9yZXN0MjEsZmFybTIxLHdldGxhbmRzMjEsb3RoZXJVbmRldmVsb3BlZDIxLHdhdGVyMjEpICU+JQ0KICBzdF9zZXRfZ2VvbWV0cnkoTlVMTCkgJT4lDQogIGJpbmRfY29scyguLGRhdCkgJT4lDQogIHN0X3NmKCkgJT4lDQogIHN0X2Nhc3QoIlBPTFlHT04iKQ0KDQpkYXQ0IDwtIGRhdDQgJT4lDQogIG11dGF0ZShtZXRyYV9kaXN0ID0gZGF0MyRtZXRyYV9kaXN0KQ0KDQpkYXQ0ICU+JQ0KICBnYXRoZXIodmFyLHZhbHVlLGRldjIxOndhdGVyMjEpICU+JQ0KICBzdF9jZW50cm9pZCgpICU+JQ0KICBtdXRhdGUoWCA9IHN0X2Nvb3JkaW5hdGVzKC4pWywxXSwNCiAgICAgICAgIFkgPSBzdF9jb29yZGluYXRlcyguKVssMl0pICU+JQ0KICBnZ3Bsb3QoKSArDQogICAgZ2VvbV9zZihkYXRhPVRocmVlQ291bnR5QXJlYSkgKw0KICAgIGdlb21fcG9pbnQoYWVzKFgsWSwgY29sb3VyPWFzLmZhY3Rvcih2YWx1ZSkpKSArDQogICAgZmFjZXRfd3JhcCh+dmFyKSArDQogICAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJPdGhlciIsIkxhbmQgQ292ZXIiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiIikgKw0KICAgIGxhYnModGl0bGUgPSAiTGFuZCBDb3ZlciBUeXBlcywgMjAyMSIsDQogICAgICAgICBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkcyIpICsNCiAgIG1hcFRoZW1lDQpgYGANCg0KDQpCZWxvdyBhbiBpbmRpY2F0b3IgYHNlbnNpdGl2ZV9sb3N0YCBpcyBjcmVhdGVkIGluZGljYXRpbmcgZ3JpZCBjZWxscyB0aGF0IHdlcmUgZWl0aGVyIGZvcmVzdCBvciB3ZXRsYW5kcyBpbiAyMDExIGJ1dCB3ZXJlIG5vIGxvbmdlciBzbyBpbiAyMDIxLiBUaGUgb3V0cHV0IGxheWVyLCBgc2Vuc2l0aXZlX2xhbmRfbG9zdGAsIGdpdmVzIGEgc2Vuc2UgZm9yIGhvdyBkZXZlbG9wbWVudCBpbiB0aGUgcmVjZW50IHBhc3QgaGFzIGVmZmVjdGVkIHRoZSBuYXR1cmFsIGVudmlyb25tZW50Lg0KDQooV2UgYXJlIHJlcGVhdGluZyB0aGVzZSBzdGVwcyBmcm9tIGJlZm9yZSwgYnV0IHdpdGggdGhlIHVwZGF0ZWQgZmluYWwgZGF0YXNldCAoZGF0NCkuDQoNCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aD0gNn0NCmRhdDQgPC0NCiAgZGF0NCAlPiUNCiAgIG11dGF0ZShzZW5zaXRpdmVfbG9zdDIxID0gaWZlbHNlKGZvcmVzdCA9PSAxICYgZm9yZXN0MjEgPT0gMCB8DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ZXRsYW5kcyA9PSAxICYgd2V0bGFuZHMyMSA9PSAwLDEsMCkpDQogICAgICAgICAgICAgICAgICAgICAgDQpnZ3Bsb3QoKSArDQogIGdlb21fcG9pbnQoZGF0YT1kYXQ0LCBhZXMoeD14eUMoZGF0NClbLDFdLCB5PXh5QyhkYXQ0KVssMl0sIGNvbG91cj1hcy5mYWN0b3Ioc2Vuc2l0aXZlX2xvc3QyMSkpKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk5vIENoYW5nZSIsIlNlbnNpdGl2ZSBMb3N0IiksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIiKSArDQogIGxhYnModGl0bGUgPSAiU2Vuc2l0aXZlIGxhbmRzIGxvc3Q6IDIwMTEgLSAyMDIxIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkcyIpICsNCiAgZ2VvbV9zZihkYXRhPVRocmVlQ291bnR5QXJlYSwgZmlsbCA9ICJ0cmFuc3BhcmVudCIpICsNCiAgbWFwVGhlbWUNCmBgYA0KDQpOZXh0LCB3ZSBwbG90IGNvdW50eS1zcGVjaWZpYyBtZXRyaWNzLg0KDQpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQpjb3VudHlfc3BlY2lmaWNfbWV0cmljc18yIDwtIA0KICBkYXQ0ICU+JQ0KICAjcHJlZGljdCBkZXZlbG9wbWVudCBkZW1hbmQgZnJvbSBvdXIgbW9kZWwNCiAgbXV0YXRlKERldmVsb3BtZW50X0RlbWFuZCA9IHByZWRpY3QoTW9kZWw3LCBkYXQ0LCB0eXBlPSJyZXNwb25zZSIpKSAlPiUNCiAgI2dldCBhIGNvdW50IGNvdW50IG9mIGdyaWQgY2VsbHMgYnkgY291bnR5IHdoaWNoIHdlIGNhbiB1c2UgdG8gY2FsY3VsYXRlIHJhdGVzIGJlbG93DQogIGxlZnRfam9pbihzdF9zZXRfZ2VvbWV0cnkoZGF0LCBOVUxMKSAlPiUgZ3JvdXBfYnkoQ09VTlRZX05BTSkgJT4lIHN1bW1hcml6ZShjb3VudCA9IG4oKSkpICU+JQ0KICAjY2FsY3VsYXRlIHN1bW1hcnkgc3RhdGlzdGljcyBieSBjb3VudHkNCiAgZ3JvdXBfYnkoQ09VTlRZX05BTSkgJT4lDQogIHN1bW1hcml6ZShUb3RhbF9GYXJtbGFuZCA9IHN1bShmYXJtMjEpIC8gbWF4KGNvdW50KSwNCiAgICAgICAgICAgIFRvdGFsX0ZvcmVzdCA9IHN1bShmb3Jlc3QyMSkgLyBtYXgoY291bnQpLA0KICAgICAgICAgICAgVG90YWxfV2V0bGFuZHMgPSBzdW0od2V0bGFuZHMyMSkgLyBtYXgoY291bnQpLA0KICAgICAgICAgICAgVG90YWxfVW5kZXZlbG9wZWQgPSBzdW0ob3RoZXJVbmRldmVsb3BlZDIxKSAvIG1heChjb3VudCksDQogICAgICAgICAgICBTZW5zaXRpdmVfTGFuZF9Mb3N0ID0gc3VtKHNlbnNpdGl2ZV9sb3N0MjEpIC8gbWF4KGNvdW50KSwNCiAgICAgICAgICAgICNTZW5zaXRpdmVfUmVnaW9ucyA9IHN1bShzZW5zaXRpdmVSZWdpb25zKSAvIG1heChjb3VudCksDQogICAgICAgICAgICBNZWFuX0RldmVsb3BtZW50X0RlbWFuZCA9IG1lYW4oRGV2ZWxvcG1lbnRfRGVtYW5kKSkgJT4lDQogICNnZXQgcG9wdWxhdGlvbiBkYXRhIGJ5IGNvdW50eQ0KICBsZWZ0X2pvaW4oUG9wXzIwMzEsIGJ5ID0gYygiQ09VTlRZX05BTSIgPSAiQ291bnRpZXMiKSkgJT4lIA0KICAgICAgICAgICAgbXV0YXRlKFBvcHVsYXRpb25fQ2hhbmdlID0gcG9wXzIwMzEgLSBwb3BfMjAyMSwNCiAgICAgICAgICAgICAgICAgICBQb3B1bGF0aW9uX0NoYW5nZV9SYXRlID0gUG9wdWxhdGlvbl9DaGFuZ2UgLyBwb3BfMjAzMSkgJT4lDQogICAgICAgICAgICBkcGx5cjo6c2VsZWN0KENPVU5UWV9OQU0sVG90YWxfRmFybWxhbmQsIFRvdGFsX0ZvcmVzdCwgVG90YWxfV2V0bGFuZHMsIFRvdGFsX1VuZGV2ZWxvcGVkLCBTZW5zaXRpdmVfTGFuZF9Mb3N0LCBNZWFuX0RldmVsb3BtZW50X0RlbWFuZCwgUG9wdWxhdGlvbl9DaGFuZ2VfUmF0ZSkNCg0KYGBgDQoNCg0KTm93IGEgc21hbGwgbXVsdGlwbGUgcGxvdCBjYW4gYmUgY3JlYXRlZCBwcm92aWRpbmcgYm90aCBzdXBwbHkgYW5kIGRlbWFuZCBzaWRlIGFuYWx5dGljcyBieSBjb3VudHkuIFRoZSBwbG90IGdpdmVzIGEgc2Vuc2UgZm9yIGRldmVsb3BtZW50IGRlbWFuZCAoYERlbWFuZC1TaWRlYCksIHN1aXRhYmxlIGxhbmQgZm9yIGRldmVsb3BtZW50IChgU3VpdGFibGVgKSBhbmQgc2Vuc2l0aXZlIGxhbmQgKGBOb3QgU3VpdGFibGVgKS4NCg0KVGhpcyBwbG90IGlzIHNpbWlsYXIgdG8gdGhlIHJlc3VsdHMgZnJvbSB0aGUgcHJldmlvdXMgbW9kZWwsIGJ1dCBub3cgd2UgY2FuIHNlZSB0aGF0IGRldmVsb3BtZW50IGRlbWFuZCBoYXMgaW5jcmVhc2VkIGZvciBXaWxsIENvdW50eS4gTWVhbl9EZXZlbG9wbWVudF9EZW1hbmQgaW5jcmVhc2VkIHNsaWdodGx5IGZvciBXaWxsIENvdW50eSBiZWNhdXNlIHdlIHVzZWQgTW9kZWw3IGFuZCB0aGUgTWV0cmEgbGluZSBleHRlbnNpb24gdmFyaWFibGUsIGNvbXBhcmVkIHRvIE1vZGVsNiB3aGljaCB1c2VkIG9ubHkgZXhpc3RpbmcgTWV0cmEgcmFpbCBsaW5lcy4gQWRkaW5nIHRoZSByZWdpb25hbCByYWlsIGxpbmUgZXh0ZW5zaW9uIGludG8gV2lsbCBDb3VudHkgd291bGQgbGlrZWx5IGNvbnRyaWJ1dGUgdG8gYW4gaW5jcmVhc2UgaW4gZGV2ZWxvcG1lbnQgZGVtYW5kIGZvciBhcmVhcyBuZWFyIHRoZSB0cmFuc2l0IHJvdXRlLg0KDQpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQpjb3VudHlfc3BlY2lmaWNfbWV0cmljc18yICU+JQ0KICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtQ09VTlRZX05BTSwgLWdlb21ldHJ5KSAlPiUNCiAgbXV0YXRlKFZhcmlhYmxlID0gZmFjdG9yKFZhcmlhYmxlLCBsZXZlbHM9YygiUG9wdWxhdGlvbl9DaGFuZ2VfUmF0ZSIsIk1lYW5fRGV2ZWxvcG1lbnRfRGVtYW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVG90YWxfRmFybWxhbmQiLCJUb3RhbF9VbmRldmVsb3BlZCIsIlRvdGFsX0ZvcmVzdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRvdGFsX1dldGxhbmRzIiwiU2Vuc2l0aXZlX0xhbmRfTG9zdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFRSVUUpKSkgJT4lDQogIG11dGF0ZShQbGFubmluZ19EZXNpZ25hdGlvbiA9IGNhc2Vfd2hlbigNCiAgICBWYXJpYWJsZSA9PSAiUG9wdWxhdGlvbl9DaGFuZ2VfUmF0ZSIgfCBWYXJpYWJsZSA9PSAiTWVhbl9EZXZlbG9wbWVudF9EZW1hbmQiIH4gIkRlbWFuZC1TaWRlIiwNCiAgICBWYXJpYWJsZSA9PSAiVG90YWxfRmFybWxhbmQiIHwgVmFyaWFibGUgPT0gIlRvdGFsX1VuZGV2ZWxvcGVkIiAgICAgICAgICAgICAgIH4gIlN1aXRhYmxlIiwNCiAgICBUUlVFICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH4gIk5vdCBTdWl0YWJsZSIpKSAlPiUNCiAgZ2dwbG90KGFlcyh4PVZhcmlhYmxlLCB5PVZhbHVlLCBmaWxsPVBsYW5uaW5nX0Rlc2lnbmF0aW9uKSkgKw0KICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoKSwgY29sb3VyPSJibGFjayIpICsNCiAgICBmYWNldF93cmFwKH5DT1VOVFlfTkFNLCBuY29sPTUpICsNCiAgICBjb29yZF9mbGlwKCkgKw0KICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoLS4yNSwgMSwgYnkgPSAuMjUpKSArDQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMi41KSArIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDQuNSkgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJibGFjayIsInJlZCIsImRhcmtncmVlbiIpKSArDQogICAgbGFicyh0aXRsZT0gIkNvdW50eSBTcGVjaWZpYyBBbGxvY2F0aW9uIE1ldHJpY3MiLCBzdWJ0aXRsZT0gIkFzIHJhdGVzIiwgeD0iSW5kaWNhdG9yIiwgeT0iUmF0ZSIpICsNCiAgICBwbG90VGhlbWUgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLCBsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpDQpgYGANCg0KDQoNCiMgOC4gQXBwZW5kaXgNCg0KX1RoaXMgaXMgbmV3IG1hdGVyaWFsIGRlc2NyaWJpbmcgaG93IHlvdSBjYW4gZGV0ZXJtaW5lIGxhbmQgY292ZXIgY2hhbmdlIGZyb20gbGFuZCBjb3ZlciByYXN0ZXJzIGZvciB0d28gdGltZSBwZXJpb2RzXw0KDQojIyA4LjEuIENhbGN1bGF0aW5nIExhbmQgQ292ZXIgQ2hhbmdlDQoNCkZvciB5b3VyIGFzc2lnbm1lbnQsIHlvdSBhcmUgZ29pbmcgdG8gbmVlZCB0byBnZXQgbGFuZCBjb3ZlciBkYXRhIGZvciB0d28gbmV3IHRpbWUgcGVyaW9kcyBhbmQgZmlndXJlIG91dCB3aGF0IGFyZWFzIGRldmVsb3BlZCBpbiB0aGF0IGludGVydmFsIChhbmQgdGhlbiBtb2RlbCBpdCkuIEluIHRoZSB3b3JrZmxvdyBhYm92ZSwgd2UgdXNlZCBhIHRoZSBOTENEIGxhbmQgY292ZXIgY2hhbmdlIGRhdGEgc2V0LCBidXQgd2UgY291bGQgYWxzbyBoYXZlIGNhbGN1bGF0ZWQgb3VyIG93biB2ZXJzaW9uIHVzaW5nIHRoZSAyMDAxIGFuZCAyMDExIGRhdGEgc2V0cy4gWW91IGNvdWxkIGRvIHRoaXMgaW4gUiwgb3IgQXJjR0lTIGZvciB5b3VyIGFzc2lnbm1lbnQuIEhlcmUgaXMgYW4gYWJicmV2aWF0ZWQgd29ya2Zsb3cgZm9yIGRvaW5nIGl0IGluIFIgdXNpbmcgdGhlIGRhdGEgZnJvbSB0aGlzIGV4ZXJjaXNlOg0KDQpSZWNsYXNzaWZ5IDIwMDEgYW5kIDIwMTEgbGFuZCBjb3ZlciBkYXRhYmFzZXMgdG8gY29uc2lzdCBvZiAxIGFuZCAwIG9ic2VydmF0aW9ucyAoZS5nLiAxIGlzIHRoZSBkZXZlbG9wZWQgY2xhc3NlcyAxMy0yNCwgMCBpcyBldmVyeXRoaW5nIGVsc2UpLiANCg0KYGBge3J9DQojIHJlY2xhc3NNYXRyaXggPC0gDQojICAgbWF0cml4KGMoDQojICAgICAwLDEyLDAsDQojICAgICAxMiwyNCwxLA0KIyAgICAgMjQsSW5mLDApLA0KIyAgIG5jb2w9MywgYnlyb3c9VCkNCmBgYA0KDQpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQojIGRldmVsb3BlZF8yMDAxIDwtIA0KIyAgIHJlY2xhc3NpZnkobGNfMjAwMSxyZWNsYXNzTWF0cml4KQ0KIyANCiMgZGV2ZWxvcGVkXzIwMTEgPC0gDQojICAgcmVjbGFzc2lmeShsY18yMDExLHJlY2xhc3NNYXRyaXgpDQoNCmBgYA0KDQpUaGVuIGRvIHNvbWUgbWFwIGFsZ2VicmEgdG8gZmluZCB0aGUgcGxhY2VzIHdoZXJlIGxhbmQgY292ZXIgY2hhbmdlZC4gTGV0J3Mgc2VlIGEgcXVpY2sgaGlzdG9ncmFtIG9mIHRoZSB2YWx1ZXMgLSB0aGVzZSBzaG91bGQgcmFuZ2UgZnJvbSAwICh1bmRldmVsb3BlZCBpbiAyMDAxLCB1bmRldmVsb3BlZCBpbiAyMDExKSwgMSAodW5kZXZlbG9wZWQgaW4gMjAwMSwgZGV2ZWxvcGVkIGluIDIwMTEgKHByZXN1bWluZyBub3RoaW5nIHdlbnQgZnJvbSBkZXZlbG9wZWQgdG8gdW5kZXZlbG9wZWQpKSwgYW5kIDIgKGRldmVsb3BlZCBpbiBib3RoIHBlcmlvZHMpLiBUaGUgMSdzIHJlcHJlc2VudCB0aGUgY2hhbmdlLg0KDQpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQojIA0KIyBkZXZlbG9wbWVudF9jaGFuZ2UgPC0gZGV2ZWxvcGVkXzIwMDErZGV2ZWxvcGVkXzIwMTENCiMgDQojIGhpc3QoZGV2ZWxvcG1lbnRfY2hhbmdlKQ0KYGBgDQoNCldlIGNhbiBzdWJzZXF1ZW50bHkgdHVybiBhbnkgb2YgdGhlIDAncyBhbmQgMSdzIHRvIE5BDQoNCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCiMgZGV2ZWxvcG1lbnRfY2hhbmdlW2RldmVsb3BtZW50X2NoYW5nZSAhPSAxXSA8LSBOQQ0KIyANCiMgZ2dwbG90KCkgKw0KIyAgIGdlb21fc2YoZGF0YT1ob3VzdG9uTVNBKSArDQojICAgZ2VvbV9yYXN0ZXIoZGF0YT1yYXN0KGRldmVsb3BtZW50X2NoYW5nZSkgJT4lIG5hLm9taXQsIA0KIyAgICAgICAgICAgICAgIGFlcyh4LHksZmlsbD1hcy5mYWN0b3IodmFsdWUpKSkgKw0KIyAgIHNjYWxlX2ZpbGxfdmlyaWRpcyhkaXNjcmV0ZT1UUlVFLCBuYW1lID0iTGFuZCBDb3ZlclxuQ2hhbmdlIikgKyANCiMgICBsYWJzKHRpdGxlPSJEZXZlbG9wbWVudCBsYW5kIHVzZSBjaGFuZ2UiKSArDQojICAgbWFwVGhlbWUNCmBgYA0KDQojIyA4LjIuIERvd25zYW1wbGluZyBSYXN0ZXJzDQoNCk5vdGljZSB0aGF0IHdlIHVzZWQgNDAwMHg0MDAwIHVuaXQgZ3JpZCBjZWxscyBpbiB0aGlzIGFuYWx5c2lzIHRvIGtlZXAgc21hbGwgZ3JpZCBjZWxsIHNpemVzIGZyb20gY3J1c2hpbmcgb3VyIGxhcHRvcHMgd2hpbGUgd2UgZGlkIHRoaXMgcGxvdHRpbmcgYW5kIGdlby1wcm9jZXNzaW5nLiBUaGlzIGlzIHZlcnkgc2ltcGxlIHRvIGRvIGluIFIgLSB0aGUgYmVsb3cgY29kZSB0YWtlcyBvdXIgYGRldmVsb3BtZW50X2NoYW5nZWAgcmFzdGVyIGFuZCBkb3duc2FtcGxlcyBpdCBieSBhIGZhY3RvciBvZiB0d28gdXNpbmcgdGhlIGBhZ2dyZWdhdGVgIGZ1bmN0aW9uLiBZb3UgY291bGQgbG9hZCBhbiBvcmlnaW5hbCBkYXRhIHNldCBpbiBhdCB0aGUgYmVnaW5uaW5nIG9mIHlvdXIgYW5hbHlzaXMgYW5kIGRvd25zYW1wbGUgaXQgYmVmb3JlIHlvdSBnZXQgc3RhcnRlZC4NCg0KYGBge3IsIHdhcm5pbmcgPSBGQUxTRX0NCiMgDQojIGRldmVsb3BtZW50X2NoYW5nZQ0KIyANCiMgYWdncmVnYXRlKGRldmVsb3BtZW50X2NoYW5nZSwgZmFjdCA9IDIpDQoNCmBgYA0KDQojIyA4LjMuIENyb3BwaW5nIFJhc3RlcnMNClNheSB5b3UgaGF2ZSByYXN0ZXJzIHRoYXQgeW91IHdvdWxkIGxpa2UgdG8gbWFuaXB1bGF0ZSBpbiBSIGluc3RlYWQgb2YgaW4gQXJjR0lTLiBJZiB5b3UgaGF2ZSBkYXRhIGNvdmVyaW5nIHRoZSBzdHVkeSBhcmVhLCB5b3UgY2FuIHVzZSB0aGlzIGFzIHRoZSBleHRlbnQgdG8gd2hpY2ggeW91IHdvdWxkIGxpa2UgdG8gY3JvcCB0aGUgZGF0YSwgYW5kIHRoZW4gdXNlIGBtYXNrYCB0byBjbGlwIHRoZSBkYXRhIHRvIHRoZSBleGFjdCBib3VuZGFyaWVzLg0KDQpGaXJzdCwgcmVhZCBpbiB0aGUgcmVzYW1wbGVkIHJhc3RlciBvZiBsYW5kIGNvdmVyIGFyb3VuZCBBdGxhbnRhLCBHZW9yZ2lhIGZyb20gMjAwMSBhbmQgdGhlbiBwbG90IGl0Lg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBsY19hdGxfMjAwMSA8LSByYXN0ZXIoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9tYWZpY2htYW4vQ1BMTl82NzUvbWFpbi9XZWVrXzE0XzE1L2RhdGEvYXRsX2xjMDFfcmVzYW1wX25ldy50aWYiKQ0KIyANCiMgcGxvdChsY19hdGxfMjAwMSkNCmBgYA0KDQoNCldlIHdpbGwgYWxzbyByZWFkIGluIEF0bGFudGEgY291bnRpZXMgZm9yIHRoZSBleHRlbnQgb2YgdGhlIGJvdW5kaW5nIGJveCwgYW5kIHRoZW4gYGNyb3BgIHRoZSByYXN0ZXIgdG8gdGhlIGNvdW50aWVzLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBhdGxfY291bnRpZXMgPC0gc3RfcmVhZCgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL21hZmljaG1hbi9DUExOXzY3NS9tYWluL1dlZWtfMTRfMTUvZGF0YS9Db3VudGllc19BdGxhbnRhX1JlZ2lvbi5nZW9qc29uIikgJT4lIHN0X3RyYW5zZm9ybSgiRVNSSToxMDI2NjciKSAjIEVTUkkgMTk4MyBzdGF0ZSBwbGFuZSBHQSB3ZXN0DQojIA0KIyBsY19hdGxfMjAwMV9jcm9wIDwtIGNyb3AobGNfYXRsXzIwMDEsIGV4dGVudChhdGxfY291bnRpZXMpKQ0KIyANCiMgcGxvdChsY19hdGxfMjAwMV9jcm9wKQ0KYGBgDQoNCg0KVGhlbiwgYG1hc2tgIHRoZSByYXN0ZXIgdXNpbmcgdGhlIEF0bGFudGEgYXJlIGNvdW50aWVzLiBUaGlzIGlzIHNpbWlsYXIgdG8gdGhlICJjbGlwcGluZyIgcHJvY2VzcyBmb3IgdmVjdG9yIGRhdGEuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIGxjX2F0bF8yMDAxX21hc2sgPC0gbWFzayhsY19hdGxfMjAwMV9jcm9wLCBhdGxfY291bnRpZXMpDQojIHBsb3QobGNfYXRsXzIwMDFfbWFzaykNCmBgYA0KDQoNCiMjIDguNCBVcGRhdGVkIENlbnN1cyBEYXRhIENhbGxzDQoNCkluIHNlY3Rpb24gMi40LCBjZW5zdXMgZGF0YSB3YXMgcHVsbGVkIHVzaW5nIHRoZSBgZ2V0X2RlY2VubmlhbGA7IGhvd2V2ZXIsIGlmIHlvdSBhcmUgdXNpbmcgYSBkaWZmZXJlbnQgdGltZWZyYW1lLCB5b3UgeW91IHdpbGwgdXNlIGRhdGEgZnJvbSB0aGUgQW1lcmljYW4gQ29tbXVuaXR5IFN1cnZleSAoQUNTKS4gWW91IHdpbGwgcmVwbGFjZSBgZ2V0X2RlY2VubmlhbGAgd2l0aCBgZ2V0X2Fjc2AgaW4geW91ciBwcm9qZWN0J3Mgd29ya2Zsb3cuIFRoZSBjb2RlIGNodW5rIGJlbG93IHNob3dzIGhvdyB0byB1c2UgYGdldF9hY3NgIHRvIG9idGFpbiBwb3B1bGF0aW9uIGRhdGEgZnJvbSB0aGUgY291bnRpZXMgaW4gdGhlIEhvdXN0b24gTVNBIGluIDIwMTkuIE5vdGUgdGhhdCBgZ2V0X2Fjc2AgcmV0dXJucyBib3RoIGFuIGVzdGltYXRlIChkZW5vdGVkIHdpdGggYW4gIkUiKSBhbmQgYSBtYXJnaW4gb2YgZXJyb3IgKGRlbm90ZWQgd2l0aCBhbiAiTSIpLiBXZSB1c2UgdGhlIGBzZWxlY3RgIGNvbW1hbmQgaW4gYGRwbHlyYCB0byBvbmx5IHJldGFpbiBlc3RpbWF0ZSB2ZXJzaW9uIG9mIHRoZSB2YXJpYWJsZS4NCg0KDQpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHJlc3VsdHMgPSAiaGlkZSJ9DQojICMgU3BlY2lmeSB3aGljaCB2YXJpYWJsZShzKSB5b3Ugd291bGQgbGlrZSB0byBncmFiLiBIZXJlLCBvbmx5IG9uZSAoVG90YWwgUG9wdWxhdGlvbikgaXMgbGlzdGVkLCBidXQgeW91IGNvdWxkIGFkZCBtb3JlIHRvIHRoZSBjYWxsLg0KIyBhY3NfdmFycyA8LSBjKCJCMDIwMDFfMDAxRSIpDQojIA0KIyAjIFVzaW5nICJ0cmFjdCIgYXMgdGhlIGdlb2dyYXBoeSBhbmQgMjAxOSBhcyB0aGUgeWVhciwgZG93bmxvYWQgZGF0YSBkYXRhIGZvciB0aGUgSG91c3RvbiBNU0EgY291bnRpZXMgbGlzdGVkLg0KIyBob3VzdG9uUG9wMTkgPC0gZ2V0X2FjcyhnZW9ncmFwaHkgPSAidHJhY3QiLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGVzID0gYWNzX3ZhcnMsIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyID0gMjAxOSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgc3RhdGUgPSA0OCwgDQojICAgICAgICAgICAgICAgICAgICAgICAgIGdlb21ldHJ5ID0gVFJVRSwgDQojICAgICAgICAgICAgICAgICAgICAgICAgIG91dHB1dCA9ICJ3aWRlIiwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgY291bnR5PWMoIkhhcnJpcyBDT3VudHkiLCJTYW4gSmFjaW50byIsIk1vbnRnb21lcnkiLCJMaWJlcnR5IiwiV2FsbGVyIiwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICJBdXN0aW4iLCJDaGFtYmVycyIsIkZvcnQgQmVuZCIsIkJyYXpvcmlhIiwiR2FsdmVzdG9uIikpICU+JQ0KIyAgICAgICAgICAgICAgICAgcmVuYW1lKHBvcDIwMTkgPSBCMDIwMDFfMDAxRSkgJT4lDQojICAgICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KC1zdGFydHNfd2l0aCgiQiIpKQ0KIyANCiMgIyBNYWtlIHN1cmUgdG8gdHJhbnNmb3JtIHRvIHRoZSBjcnMgb2YgdGhlIGZpc2huZXQhDQojIGhvdXN0b25Qb3AxOSA8LSBob3VzdG9uUG9wMTkgJT4lDQojICAgc3RfdHJhbnNmb3JtKHN0X2Nycyhob3VzdG9uTVNBX2Zpc2huZXQpKQ0KDQpgYGANCg==