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.

Old_Classification New_Classification
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:

  1. The total amount of wetlands and forest land cover area in 2021.
  2. The amount of sensitive land (wetland and forest) lost between 2011 and 2021.
  3. 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==