Overview

This growth model utilizes land cover change scenario-based forecasting to estimate future growth projections in the greater Charlotte region, one of the largest and fastest growing areas of the US, to help build the case for expanding Charlotte’s Lynx light rail line.

1. Setup - Loading Libraries and Functions

library(tidyverse)
library(sf)
library(raster)
library(kableExtra)
library(tidycensus)
library(tigris)
library(FNN)
library(caret)
library(yardstick)
library(plotROC) 
library(ggrepel)
library(pROC)
library(grid)
library(gridExtra)
library(viridis)
library(igraph)
library(mapview)
library(FedData)


palette2 <- c("#1b9e77", "#d95f02")
palette4 <- c("#66c2a5", "#41b6c4", "#2b8cbe", "#d95f02")
palette5 <- c("#edf8fb", "#b2e2e2", "#66c2a4", "#2b8cbe", "#fc8d62") 
palette10 <- c("#f1eef6", "#d0d1e6", "#a6bddb", "#67a9cf", "#1c9099",
               "#41b6c4", "#66c2a5", "#fc8d62", "#e34a33", "#b30000")

1.1. Loading Custom build functions

#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)) }

# knn distance function

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)  
}

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)
  }

2. Loading Land Cover and MSA data

This model uses historical data from 2011 and 2019 to predict land change for 2027 in the greater Charlotte region, the boundary of which is defined as the Charlotte-Concord-Gastonia, NC-SC Metropolitan Statistical Area (MSA). Data come from the U.S. Census Bureau and land cover data come from the U.S. Geological Survey (USGS)’s National Land Cover Database (NLCD). Since the region spans two states, census data were retrieved separately for each state and then combined into a single dataset for spatial analysis. In North Carolina, the selected counties are Mecklenburg, Cabarrus, Gaston, Iredell, Lincoln, Rowan, Union, and Anson. In South Carolina, the included counties are York, Chester, and Lancaster.

2.1. Load the study area MSA

census_api_key("02524912081801068f04f286a086d19e9fc641ca", install = TRUE, overwrite = TRUE)
## [1] "02524912081801068f04f286a086d19e9fc641ca"
readRenviron("~/.Renviron")

# Define counties by state
charlotte_msa_counties <- list(
  NC = c("Mecklenburg", "Cabarrus", "Gaston", "Iredell", "Lincoln", 
         "Rowan", "Union", "Anson"),
  SC = c("091", "023", "057")
)

# Variable to download
acsVariables <- c(totpop = "B01001_001")


# Pull data by state and county
msa_tracts <- map_dfr(names(charlotte_msa_counties), function(st) {
  get_acs(
    geography = "tract",
    variables = acsVariables,
    state = st,
    county = charlotte_msa_counties[[st]],
    survey = "acs5",
    year = 2019,
    output = "wide",
    geometry = TRUE
  )
}) %>%
  st_transform(32119)  # NAD83 / North Carolina (ft)
##   |                                                                              |                                                                      |   0%  |                                                                              |=                                                                     |   1%  |                                                                              |=                                                                     |   2%  |                                                                              |==                                                                    |   2%  |                                                                              |==                                                                    |   3%  |                                                                              |====                                                                  |   6%  |                                                                              |=====                                                                 |   7%  |                                                                              |======                                                                |   9%  |                                                                              |========                                                              |  12%  |                                                                              |=========                                                             |  13%  |                                                                              |============                                                          |  17%  |                                                                              |==============                                                        |  21%  |                                                                              |==================                                                    |  26%  |                                                                              |=======================                                               |  32%  |                                                                              |===========================                                           |  39%  |                                                                              |============================                                          |  40%  |                                                                              |=============================                                         |  41%  |                                                                              |=============================                                         |  42%  |                                                                              |==============================                                        |  43%  |                                                                              |==============================                                        |  44%  |                                                                              |===============================                                       |  44%  |                                                                              |================================                                      |  45%  |                                                                              |=================================                                     |  47%  |                                                                              |===================================                                   |  51%  |                                                                              |===========================================                           |  61%  |                                                                              |============================================                          |  63%  |                                                                              |=================================================                     |  70%  |                                                                              |==================================================                    |  72%  |                                                                              |==========================================================            |  82%  |                                                                              |===========================================================           |  85%  |                                                                              |=================================================================     |  93%  |                                                                              |==================================================================    |  94%  |                                                                              |====================================================================  |  97%  |                                                                              |===================================================================== |  98%  |                                                                              |===================================================================== |  99%  |                                                                              |======================================================================| 100%
##   |                                                                              |                                                                      |   0%  |                                                                              |==                                                                    |   3%  |                                                                              |====                                                                  |   6%  |                                                                              |=========                                                             |  13%  |                                                                              |===========                                                           |  16%  |                                                                              |=============                                                         |  19%  |                                                                              |===============                                                       |  21%  |                                                                              |======================                                                |  32%  |                                                                              |==========================                                            |  37%  |                                                                              |============================                                          |  40%  |                                                                              |==============================                                        |  43%  |                                                                              |=====================================                                 |  53%  |                                                                              |==============================================                        |  66%  |                                                                              |====================================================                  |  75%  |                                                                              |========================================================              |  80%  |                                                                              |======================================================================| 100%
# Create unified boundary
msa_boundary <- msa_tracts %>%
  st_union() %>%
  st_as_sf()

ggplot() + 
    geom_sf(data = msa_boundary, fill = "#a6cee3", color = "#1f78b4") +
  geom_sf(data=msa_tracts, fill= NA,  color= "#e34a33") +
  labs(title = "Charlotte MSA Boundary") +
  theme_void()

2.2. Load Land Cover for t1 and t2

lc_2011 <- get_nlcd(
  template = msa_boundary,
  label = "charlotte_lc_2011",
  year = 2011) %>%
  raster(.)  


lc_2011_df <- as.data.frame(rasterToPoints(lc_2011))
colnames(lc_2011_df) <- c("x", "y", "landcover")  


landcover_labels <- c(
  "11" = "Open Water",
  "21" = "Developed, Open Space",
  "22" = "Developed, Low Intensity",
  "23" = "Developed, Medium Intensity",
  "24" = "Developed, High Intensity",
  "31" = "Barren Land",
  "41" = "Deciduous Forest",
  "42" = "Evergreen Forest",
  "43" = "Mixed Forest",
  "52" = "Shrub/Scrub",
  "71" = "Grassland/Herbaceous",
  "81" = "Pasture/Hay",
  "82" = "Cultivated Crops",
  "90" = "Woody Wetlands",
  "95" = "Emergent Herbaceous Wetlands"
)


ggplot(lc_2011_df, aes(x = x, y = y, fill = factor(landcover))) +
  geom_raster() +
  scale_fill_manual(
    name = "Land Cover Class",
    values = viridis::viridis(length(landcover_labels)),
    labels = landcover_labels[levels(factor(lc_2011_df$landcover))]
  ) +
  coord_equal() +
  labs(title = "Land Cover - 2011 (NLCD)", x = NULL, y = NULL) +
  theme_void() +
  theme(legend.position = "right", legend.title = element_text(size = 10))

The above map shows the distribution of landcover classes in the study area.

lc_2019 <- get_nlcd(
    template = msa_boundary,
    label = "charlotte_lc_2019",
    year = 2019
  ) %>%
  raster(.)

3. Detecting Land Cover Conversion

Here, we detect land cover change between 2011 and 2019 that has transitioned from undeveloped to developed land.

3.1. Resampling Rasters

lc_2011_rs <- aggregate(lc_2011, fact = 30, fun = "modal")

lc_2019_rs <- aggregate(lc_2019, fact = 30, fun = "modal")

3.2. Reclassifying As Developed and Undeveloped

reclassMatrix <- 
  matrix(c(
    0,12,0,
    12,24,1,
    24,Inf,0),
  ncol=3, byrow=T)

The above matrix reclassifies urban areas as 1 and everything else as 0.

developed_2011 <- 
  reclassify(lc_2011_rs, reclassMatrix)

developed_2019 <- 
  reclassify(lc_2019_rs, reclassMatrix)

The above creates binary rasters: 1 for developed, 0 for undeveloped.

development_change <- developed_2011 + developed_2019

Now, we are comparing two rasters cell by cell. Since we only have 1 and 0 in our rasters now, 0 + 0 = 0 means there was no change in undeveloped land, whereas 0 + 1 = 1 means land went from undeveloped to developed. We are not interested in developed land that remains developed (1 + 1 = 2), or developed land that has transitioned to undeveloped (1 + 0 = 1). For this growth projection, we are only interested in rasters that converted from 0 to 1 (undeveloped to developed).

hist(development_change,
     main = "Distribution of Development Change",
     xlab = "Development Change Value",
     col = palette2[2],   # Use the darker color for developed (value = 1)
     border = "white")

This histogram shows the number of cells of each value, and you can see the majority of cells remained undeveloped between 2011 and 2019 (value of 0), less than 400 have remained developed, and the smallest number of cells have transitioned between undeveloped to developed land use.

development_change[development_change != 1] <- NA

Here, we drop all values that are not 1, since this is the only change we are interested in forecasting to 2027.

3.3. Examining Land Cover Change

Let’s use Mapview to take a look at the development_change raster.

mapView(development_change, 
        col.regions = palette2, 
        na.color = "transparent", 
        layer.name = "Development Change")

The resulting map shows all raster grid cells in the MSA which were undeveloped in 2011 and transitioned to developed by 2019.

4. Creating the Fishnet

msa_fishnet <- 
  st_make_grid(msa_boundary %>%
                     st_transform(crs(development_change)),
                 cellsize = res(development_change)[1], 
                 square = TRUE) %>% 
  st_sf() %>% 
  st_intersection(., msa_boundary %>%
                    dplyr::select(x) %>%
                         st_transform(crs(development_change))) %>%
  mutate(uniqueID = rownames(.))

5. Join Raster Data to the Fishnet

5.1. Aggregating LC Change to fishnet

changePoints <-
  rasterToPoints(development_change) %>%
  as.data.frame() %>%
  st_as_sf(coords = c("x", "y"), 
           crs = st_crs(msa_fishnet))

fishnet <- 
  aggregate(changePoints, 
            msa_fishnet, 
            FUN=sum) %>%
  mutate(development_change = ifelse(is.na(layer) == TRUE , 0, 1),
         development_change = as.factor(development_change)) %>%
  dplyr::select(-layer)

5.1.1. Plotting Development Change

ggplot() +
  geom_sf(data=msa_boundary %>% 
            st_transform(crs(fishnet))) +
  geom_point(data=fishnet, 
             aes(x=xyC(fishnet)$x, 
                 y=xyC(fishnet)$y, 
                 colour=development_change)) +
  scale_colour_manual(values = palette2,
                      labels=c("No Change","New Development"),
                      name = "") +
  labs(title = "Land Cover Development Change", subtitle = "As fishnet centroids") +
  theme_void()

5.2. Aggregating Batch Land Cover Data to the Fishnet

Here, we aggregate our 2011 raster land cover data into a few categories and combine this with our binary classification of land that was developed between 2011 and 2019 to see the amount and location of development based on specific land cover types.

5.2.1. Reclassifying land cover

We are going to recode our NLCD data into a few reduced categories to be used in our analysis. Here is the classification logic:

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 Wetlands
Barren Land, Dwarf Scrub, and Grassland/Herbaceous Other Undeveloped
Water Water

In the code block below, we create new rasters, descriptively called developed_2011, forest_2011 etc , according to the NLCD codes that correspond to our classification scheme.

developed_2011 <- lc_2011_rs  %in% c(21, 22, 23, 24)
forest_2011 <- lc_2011_rs %in% c(41, 42, 42)
farm_2011 <- lc_2011_rs %in% c(81, 82)
wetlands_2011 <- lc_2011_rs %in% c(90, 95) 
otherUndeveloped_2011 <- lc_2011_rs %in% c(52, 71, 31)
water_2011 <- lc_2011_rs == 11

developed_2019 <- lc_2019_rs %in% c(21, 22, 23, 24)
forest_2019 <- lc_2019_rs %in% c(41, 42, 42)
farm_2019 <- lc_2019_rs %in% c(81, 82)
wetlands_2019 <- lc_2019_rs %in% c(90, 95) 
otherUndeveloped_2019 <- lc_2019_rs %in% c(52, 71, 31)
water_2019 <- lc_2019_rs == 11

5.2.2. Aggregating Rasters to the Fishnet

names(developed_2011) <- "developed_2011"
names(forest_2011) <- "forest_2011"
names(farm_2011) <- "farm_2011"
names(wetlands_2011) <- "wetlands_2011"
names(otherUndeveloped_2011) <- "otherUndeveloped_2011"
names(water_2011) <- "water_2011"

names(developed_2019) <- "developed_2019"
names(forest_2019) <- "forest_2019"
names(farm_2019) <- "farm_2019"
names(wetlands_2019) <- "wetlands_2019"
names(otherUndeveloped_2019) <- "otherUndeveloped_2019"
names(water_2019) <- "water_2019"
rasterList_2011 <- c(developed_2011,
                   forest_2011,
                   farm_2011,
                   wetlands_2011,
                   otherUndeveloped_2011,
                   water_2011)

rasterList_2019 <- c(developed_2019,
                   forest_2019,
                   farm_2019,
                   wetlands_2019,
                   otherUndeveloped_2019,
                   water_2019)
lcRasters_2011 <-
  aggregateRaster(rasterList_2011, 
                  msa_fishnet) %>%
  dplyr::select(developed_2011,
                   forest_2011,
                   farm_2011,
                   wetlands_2011,
                   otherUndeveloped_2011,
                   water_2011) %>%
  mutate_if(is.numeric,as.factor)

lcRasters_2019 <-
  aggregateRaster(rasterList_2019, 
                  msa_fishnet) %>%
  dplyr::select(developed_2019,
                   forest_2019,
                   farm_2019,
                   wetlands_2019,
                   otherUndeveloped_2019,
                   water_2019) %>%
  mutate_if(is.numeric,as.factor)

Finally, we take this data and map them to see where developed has occurred by land cover type.

lcRasters_2011 %>%
  st_centroid() %>%
 gather(key = "variable", value = "value", developed_2011:water_2011) %>% 
  mutate(X = xyC(.)$x,
         Y = xyC(.)$y) %>%
  ggplot() +
    geom_sf(data=msa_fishnet) +
    geom_point(aes(X,Y, colour=as.factor(value))) +
    facet_wrap(~variable) +
    scale_colour_manual(values = palette2,
                        labels=c("Other","Land Cover"),
                        name = "") +
    labs(title = "Land Cover Types, 2011",
         subtitle = "As fishnet centroids") +
   theme_void()

Predictably, the majority of new development has occurred in forests and farmland. It is notable that there has been development in areas classified as water, which is likely emergent wetlands that may not be wet throughout the entire year and could therefore more easily be filled in and built upon. There was only marginal development in classified wetlands areas and “other Undeveloped” lands.

6. Wrangle Census Data and Join To the Fishnet

6.1. Downloading Census Data via API

acs_vars <- c("B02001_001E")  

charlotte_msa_counties <- list(
  NC = c("Mecklenburg", "Cabarrus", "Gaston", "Iredell", "Lincoln", 
         "Rowan", "Union", "Anson"),
  SC = c("091", "023", "057")  # York, Chester, Lancaster (FIPS)
)


charPop11 <- map_dfr(names(charlotte_msa_counties), function(st) {
  get_acs(
    geography = "tract",
    variables = acs_vars,
    year = 2011,
    state = st,
    county = charlotte_msa_counties[[st]],
    geometry = TRUE,
    output = "wide"
  )
}) %>%
  dplyr::select(GEOID, NAME, all_of(acs_vars)) %>%
  rename(pop_2011 = B02001_001E) %>%
  st_transform(st_crs(msa_fishnet)) %>%
  st_buffer(-1)

We do this once more for t2, in this case, 2019.

charPop19 <- map_dfr(names(charlotte_msa_counties), function(st) {
  get_acs(
    geography = "tract",
    variables = acs_vars,
    year = 2019,
    state = st,
    county = charlotte_msa_counties[[st]],
    geometry = TRUE,
    output = "wide"
  )
}) %>%
  dplyr::select(GEOID, NAME, all_of(acs_vars)) %>%
  rename(pop_2019 = B02001_001E) %>%
  st_transform(st_crs(msa_fishnet)) %>%
  st_buffer(-1)

Then we can plot our data and see the spatial arrangement of population in our study area in t1 (2011) and t2 (2019).

One thing worth noting here is that the quantile symbology that we apply probably changes from t1 to t2 - so you can see that, on average, there was densification across the metro area on this time interval because the quantile ranges shift upwards.

grid.arrange(
ggplot() +
  geom_sf(data = charPop11, aes(fill=factor(ntile(pop_2011,5))), colour=NA) +
  scale_fill_manual(values = palette5,
                    labels=quintileBreaks(charPop11,"pop_2011"),
                   name="Quintile\nBreaks") +
  labs(title="Population, Charlotte–Concord–Gastonia- MSA: 2011") +
  theme_void(),

ggplot() +
  geom_sf(data = charPop19, aes(fill=factor(ntile(pop_2019,5))), colour=NA) +
  scale_fill_manual(values = palette5,
                    labels=quintileBreaks(charPop19,"pop_2019"),
                   name="Quintile\nBreaks") +
  labs(title="Population, Charlotte–Concord–Gastonia MSA: 2019") +
  theme_void(), ncol=2)

6.2. Aggregate Census Data with Aerially Weighted Interpolation

Here we use Areal Weighted Interpretation (AWI) to distribute the population within each tract across the fishnet grid cells; this method assumes that population is distributed evenly across the tract, which is reasonable here because we are not measuring population as an outcome that requires meaningful precision in this analysis. The output is a new fishnet for each year that includes a population estimate within each tract.

fishnetPopulation11 <-
  st_interpolate_aw(charPop11["pop_2011"], 
                    msa_fishnet, 
                    extensive=TRUE) %>%
  st_centroid() %>%
  st_join(msa_fishnet, ., join = st_intersects) %>%
  mutate(pop_2011 = replace_na(pop_2011,0)) %>%
  dplyr::select(pop_2011)

We repeat the process for our t2 data.

fishnetPopulation19 <-
  st_interpolate_aw(charPop19["pop_2019"],
                    msa_fishnet, 
                    extensive=TRUE) %>%
  st_centroid() %>%
  st_join(msa_fishnet, ., join = st_intersects) %>%
  mutate(pop_2019 = replace_na(pop_2019,0)) %>%
  dplyr::select(pop_2019)

How do these data look when we map them out and compare them to the census vectors? We can see that the modifiable aerial unit issue - where smaller tracts are higher density because of the logic of tract-drawing - no longer distorts the picture - we see increased density in the center, where we expect it.

grid.arrange(
ggplot() +
  geom_sf(data=charPop11, aes(fill=factor(ntile(pop_2011,5))),colour=NA) +
  scale_fill_manual(values = palette5,
                    labels=substr(quintileBreaks(charPop11,"pop_2011"),1,4),
                   name="Quintile\nBreaks") +
  labs(title="Population, Charlotte–Concord–Gastonia MSA: 2011",
       subtitle="Represented as tracts; Boundaries omitted") +
  theme_void(),

  ggplot() +
  geom_sf(data=fishnetPopulation11, 
         aes(fill=factor(ntile(pop_2011,5))),colour=NA) +
  scale_fill_manual(values = palette5,
                   labels=substr(quintileBreaks(fishnetPopulation11,"pop_2011"),1,4),
                   name="Quintile\nBreaks") +
  labs(title="Population, Charlotte–Concord–Gastonia MSA: 2011",
       subtitle="Represented as fishnet gridcells; Boundaries omitted") +
  theme_void(), ncol=2)

grid.arrange(
ggplot() +
  geom_sf(data=charPop19, aes(fill=factor(ntile(pop_2019,5))),colour=NA) +
  scale_fill_manual(values = palette5,
                    labels=substr(quintileBreaks(charPop19,"pop_2019"),1,4),
                   name="Quintile\nBreaks") +
  labs(title="Population, Charlotte–Concord–Gastonia MSA: 2019",
       subtitle="Represented as tracts; Boundaries omitted") +
  theme_void(),

  ggplot() +
  geom_sf(data=fishnetPopulation19, 
         aes(fill=factor(ntile(pop_2019,5))),colour=NA) +
  scale_fill_manual(values = palette5,
                   labels=substr(quintileBreaks(fishnetPopulation19,"pop_2019"),1,4),
                   name="Quintile\nBreaks") +
  labs(title="Population, Charlotte–Concord–Gastonia MSA: 2019",
       subtitle="Represented as fishnet gridcells; Boundaries omitted") +
  theme_void(), ncol=2)

7. Transportation And Infrastructure

Transportation access in the Charlotte MSA is critical for the growing economy, which includes a robust manufacturing sector in addition to financial, tech, and service-based employers. Automobiles are the primary method of transportation for the majority of residents, but Mecklenburg County also operates a transit system that includes two light rail lines and a bus system.

7.1. Download Highways

First highway vectors (primary_roads) are downloaded from the Tigris package - we project the data and subset it to the study area using st_intersection.

charHighways <- bind_rows(
  primary_secondary_roads(state = "NC"),
  primary_secondary_roads(state = "SC")
) %>%
  st_transform(st_crs(msa_boundary)) %>%  # Match to MSA CRS
  st_intersection(msa_boundary) %>%       # Clip to MSA
  st_transform(st_crs(fishnet))           # Match CRS to fishnet

Let’s make a map and examine the spatial relationship between highways and development.

ggplot() +
  geom_point(data=fishnet, 
             aes(x=xyC(fishnet)[,1], y=xyC(fishnet)[,2],colour=development_change),size=1.5) +
  geom_sf(data=charHighways) +
  scale_colour_manual(values = palette2,
                      labels=c("No Change","New Development")) +
  labs(title = "New Development and Highways",
       subtitle = "As fishnet centroids") +
  theme_void()

7.2. Calcuate Distance to Highways

Here, we pull highway data from 2011.

highwayPoints_fishnet_2011 <- msa_fishnet %>%
  st_centroid() %>%
  mutate(distance_highways_2011 = as.numeric(st_distance(., st_union(charHighways) %>% 
                                                      st_transform(st_crs(msa_fishnet))))) %>%
  as.data.frame() %>% 
  dplyr::select(-geometry) %>% 
  left_join(msa_fishnet, .) %>% 
  st_as_sf()

highwayPoints_fishnet_2019 <- highwayPoints_fishnet_2011 %>%
  rename(distance_highways_2019 = distance_highways_2011)
ggplot() +
  geom_sf(data=msa_boundary %>% st_transform(st_crs(highwayPoints_fishnet_2011))) +
  geom_point(data=highwayPoints_fishnet_2011, aes(x=xyC(highwayPoints_fishnet_2011)[,1], 
                                             y=xyC(highwayPoints_fishnet_2011)[,2], 
                 colour=factor(ntile(distance_highways_2011,5))),size=1.5) +
  scale_colour_manual(values = palette5,
                      labels=substr(quintileBreaks(highwayPoints_fishnet_2011,"distance_highways_2011"),1,8),
                      name="Quintile\nBreaks") +
  geom_sf(data=charHighways, colour = "red") +
  labs(title = "Distance to Highways (m)",
       subtitle = "As fishnet centroids; Highways visualized in red") +
  theme_void()

8. Calculate spatial lag of development

In this step, we calculate the average distance to each grid cell’s 2 nearest developed grid cells in 2011 using a new variable created by our k-nearest-neighbors custom function (nn_function) for spatial lag, lagDevelopment_2011 and lagDevelopment_2019.

fishnet$lagDevelopment_2011 <-
    nn_function(fishnet %>%
                  st_centroid() %>%
                  st_coordinates() %>%
                  as.data.frame(),
                lcRasters_2011 %>%
                  filter(developed_2011 == 1) %>%
                  st_centroid() %>%
                  st_coordinates() %>%
                  as.data.frame(),
                2)

fishnet$lagDevelopment_2019 <-
    nn_function(fishnet %>%
                  st_centroid() %>%
                  st_coordinates() %>%
                  as.data.frame(),
                lcRasters_2019 %>%
                  filter(developed_2019 == 1) %>%
                  st_centroid() %>%
                  st_coordinates() %>%
                  as.data.frame(),
                2)
ggplot() +
  geom_sf(data=msa_boundary %>% st_transform(st_crs(fishnet))) +
  geom_point(data=fishnet, 
             aes(x=xyC(fishnet)[,1], y=xyC(fishnet)[,2], 
                 colour=factor(ntile(lagDevelopment_2011,5))), size=1.5) +
  scale_colour_manual(values = palette5,
                     labels=substr(quintileBreaks(fishnet,"lagDevelopment_2011"),1,7),
                     name="Quintile\nBreaks") +
  labs(title = "Spatial Lag to 2011 Development, m",
       subtitle = "As fishnet centroids") +
  theme_void()

9. Political Boundaries

The MSA includes Mecklenburg, Cabarrus, Gaston, Iredell, Lincoln, Rowan, Union, and Anson Counties in NC and York, Chester, and Lancaster counties in SC.

options(tigris_class = "sf")

nc_counties <- counties(state = "NC") %>% st_as_sf()
sc_counties <- counties(state = "SC") %>% st_as_sf()


all_counties <- bind_rows(nc_counties, sc_counties) %>%
  st_transform(st_crs(msa_fishnet))


studyAreaCounties <- all_counties %>%
  filter(
    (STATEFP == "37" & NAME %in% c("Mecklenburg", "Cabarrus", "Gaston", "Iredell", 
                                   "Lincoln", "Rowan", "Union", "Anson")) |
    (STATEFP == "45" & COUNTYFP %in% c("091", "023", "057"))  # York, Chester, Lancaster
  )

We spatially join the studyAreaCounties object to our fishnet and create a countyFishnet where each cell is imparted with the name of the county it belongs to.

countyFishnet <- msa_fishnet %>%
  st_join(., studyAreaCounties %>%
            dplyr::select(NAME)) %>%
  as.data.frame() %>% 
  dplyr::select(uniqueID, NAME) %>% 
  left_join(msa_fishnet, .) %>% 
  st_as_sf() %>%
  group_by(uniqueID) %>%
  slice(1) %>%
  ungroup() %>%
  arrange(as.numeric(uniqueID))

10. Creating Our Data Set for Modeling

Here we compile our data to create a model which we can train for a future scenario projection. This includes:

For t1 (2011): - fishnet - highwayPoints_fishnet_2011 - fishnetPopulation11 - lcRasters_2011 - countyFishnet

For t2 (2019): - fishnet - highwayPoints_fishnet_2019 - fishnetPopulation19 - lcRasters_2019 - countyFishnet

dat_2011 <- 
  cbind( fishnet, highwayPoints_fishnet_2011, fishnetPopulation11, lcRasters_2011, countyFishnet) %>%
  as.data.frame() %>%
  dplyr::select(uniqueID, development_change, lagDevelopment_2011, distance_highways_2011, pop_2011,  
                developed_2011, forest_2011, farm_2011, wetlands_2011, otherUndeveloped_2011, water_2011,
                NAME, geometry) %>%
  filter(water_2011 == 0) %>%
  rename_with(~ str_remove(.x, "_2011"))

dat_2019 <- 
  cbind( fishnet, highwayPoints_fishnet_2019, fishnetPopulation19, lcRasters_2019, countyFishnet) %>%
  as.data.frame() %>%
  dplyr::select(uniqueID, development_change, lagDevelopment_2019, distance_highways_2019, pop_2019,  
                developed_2019, forest_2019, farm_2019, wetlands_2019, otherUndeveloped_2019, water_2019,
                NAME, geometry) %>%
  filter(water_2019 == 0)  %>%
  rename_with(~ str_remove(.x, "_2019"))

11. Feature Exploration

In this section we explore the extent to our possible predictors (distance to highways, spatial lag of development, and population change) are associated with development change. One note: In an initial run of the model, we did see that proximity to the Blue Line was a strong predictor of development between 2011 and 2019, but did not include the lines in our initial model after a conversation with Isabel. The Lynx Blue line was first built in 2007 and extended in 2018, while the Gold Line was completed in 2015. Because the transit infrastructure has not remained consistent throughout this time period, we did not include it here.

dat_2011 %>%
  dplyr::select(distance_highways,lagDevelopment,development_change, pop) %>%
  gather(Variable, Value, -development_change) %>%
  ggplot(., aes(development_change, Value, fill=development_change)) + 
    geom_bar(position = "dodge", stat = "summary", fun.y = "mean") +
    facet_wrap(~Variable, scales = "free") +
    scale_fill_manual(values = palette2,
                      labels=c("No Change","New Development"),
                      name="Mean Value") +
    labs(title="New Development as a Function of Continuous Variables") +
    theme_minimal() 

This histogram shows that on average, land that was developed between 2011 and 2019 was closer to highways, closer to other development, and was in areas with higher populations, indicating densification over sprawl.

When we look at the type of land converted in this time interval, we see it was predominately forested land, with some farms converted and only marginal “Other undeveloped” land converted, per the table below.

dat_2011 %>%
  dplyr::select(development_change, forest, farm, wetlands, otherUndeveloped) %>%
  gather(key = "Land_Cover_Type", Value, -development_change) %>%
     group_by(development_change, Land_Cover_Type) %>%
     summarize(n = sum(as.numeric(Value))) %>%
     ungroup() %>%
    mutate(Conversion_Rate = paste0(round(100 * n/sum(n), 2), "%")) %>%
    filter(development_change == 1) %>%
  dplyr::select(Land_Cover_Type,Conversion_Rate) %>%
  kable() %>% kable_styling(full_width = F)
Land_Cover_Type Conversion_Rate
farm 0.25%
forest 0.92%
otherUndeveloped 0.04%
wetlands 0%

12. Modeling

Here, we train and test our model with an even 50% of data split between the training and test sets to help keep a fair number of 1s in our training set.

12.1. Splitting our data

set.seed(3456)
trainIndex <- 
  createDataPartition(dat_2011$otherUndeveloped, p = .50,
                                  list = FALSE,
                                  times = 1)
datTrain <- dat_2011[ trainIndex,]
datTest  <- dat_2011[-trainIndex,]

12.2. Specifying models

We estimate six separate glm models - adding new variables for each.

  • Model1 includes only previous land cover types.

  • Model2 adds the lagDevelopment.

  • Model3 adds population

  • Model4 adds a fixed effect for county (NAME)

  • Model5 adds the distance to highway.

  • Model6 is a modification of Model5 which interacts distance to highway and development lag. The hypothesis here is that these two variables are related - the effect of one depends on the other. e.g. Distance to nearest development depends in part on access to transportation. Notice that the effect of both variables is significant in this specification, but both are not significant in Model5.

Model1 <- glm(development_change ~ wetlands + forest  + farm + 
                otherUndeveloped, 
              family="binomial"(link="logit"), data = datTrain)

Model2 <- glm(development_change ~ wetlands + forest  + farm + 
                otherUndeveloped + lagDevelopment, 
              family="binomial"(link="logit"), data = datTrain)
              
Model3 <- glm(development_change ~ wetlands + forest  + farm +
                otherUndeveloped + lagDevelopment + pop,
              family="binomial"(link="logit"), data = datTrain)          
              
Model4 <- glm(development_change ~ wetlands + forest  + farm + 
                otherUndeveloped + lagDevelopment + pop + NAME, 
              family="binomial"(link="logit"), data = datTrain) 

Model5 <- glm(development_change ~ wetlands + forest  + farm + 
                otherUndeveloped + lagDevelopment + pop + distance_highways + NAME, 
              family="binomial"(link="logit"), data = datTrain) 

Model6 <- glm(development_change ~ wetlands + forest  + farm + 
                otherUndeveloped + pop + lagDevelopment * distance_highways + NAME, 
              family="binomial"(link="logit"), data = datTrain)

The best-performing model was Model6, so this is which we use for validation and prediction.

summary(Model6)
## 
## Call:
## glm(formula = development_change ~ wetlands + forest + farm + 
##     otherUndeveloped + pop + lagDevelopment * distance_highways + 
##     NAME, family = binomial(link = "logit"), data = datTrain)
## 
## Coefficients:
##                                    Estimate Std. Error z value Pr(>|z|)    
## (Intercept)                      -3.931e+00  7.392e-01  -5.318 1.05e-07 ***
## wetlands1                        -1.326e+01  1.797e+03  -0.007    0.994    
## forest1                           2.520e+00  3.064e-01   8.223  < 2e-16 ***
## farm1                             1.683e+00  4.125e-01   4.080 4.50e-05 ***
## otherUndeveloped1                 3.546e+00  6.347e-01   5.586 2.32e-08 ***
## pop                               2.771e-05  5.412e-04   0.051    0.959    
## lagDevelopment                   -1.072e-03  2.708e-04  -3.958 7.57e-05 ***
## distance_highways                 1.968e-04  2.031e-04   0.969    0.333    
## NAMECabarrus                      4.390e-01  6.677e-01   0.658    0.511    
## NAMEChester                      -1.295e+00  1.167e+00  -1.109    0.267    
## NAMEGaston                       -8.743e-01  7.586e-01  -1.152    0.249    
## NAMEIredell                      -2.831e-01  7.204e-01  -0.393    0.694    
## NAMELancaster                     2.186e-01  7.342e-01   0.298    0.766    
## NAMELincoln                      -9.108e-01  9.349e-01  -0.974    0.330    
## NAMEMecklenburg                   4.304e-01  6.675e-01   0.645    0.519    
## NAMERowan                        -1.582e+01  5.154e+02  -0.031    0.976    
## NAMEUnion                         1.745e-01  6.757e-01   0.258    0.796    
## NAMEYork                          3.516e-01  6.590e-01   0.533    0.594    
## lagDevelopment:distance_highways -1.311e-07  1.456e-07  -0.901    0.368    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 1103.49  on 9209  degrees of freedom
## Residual deviance:  822.52  on 9191  degrees of freedom
##   (1 observation deleted due to missingness)
## AIC: 860.52
## 
## Number of Fisher Scoring iterations: 19

Here, we compare AICs for goodness of fit (lower is better).

data.frame(
  Model = c("Model1", "Model2", "Model3", "Model4", "Model5", "Model6"),
  AIC = c(Model1$aic, Model2$aic, Model3$aic, Model4$aic, Model5$aic, Model6$aic)
) %>%
  ggplot()+
  geom_bar(aes(x = Model, y = AIC), stat = "identity")+
  theme_minimal()

12.3. Validating our Model Using the Test Set

We create a new data frame testSetProbs that consists of our class (e.g. development_change as a 1 or 0), and probs - which is the prediction for each observation in our test set using Model6. The type parameter is set to response, which means our probs are measures of estimated probability from 0-1.

testSetProbs <- 
  data.frame(class = datTest$development_change,
             probs = predict(Model6, datTest, type="response")) 

This density plot is a key tool to figure out where we will set our threshold for classifying predicted probabilities as 1s or 0s (e.g. Predicted to Develop or Predicted Not To Develop).

Take a close look at this plot and see if you have some ideas about what threshold would separate out our 1s from our 0s most effectively with a minimum of error.

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") +
  theme_minimal()

12.3.1. Confusion matrix

Here, we set 0.05 as our cutoff value - above that value, we predict that cell is a 1, and below it, a 0.

We build a confusion matrix to see how accurate this model is overall, and what our error rates are. There are our four potential outcomes:

  • True Positive: We predicted a cell would convert from undeveloped to developed and it did convert.

  • False Positive: We predicted a cell would convert from undeveloped to developed and it did not convert.

  • True Negative: We predicted a cell would not convert from undeveloped to developed and it did not convert.

  • False Negative: We predicted a cell would not convert from undeveloped to developed and it did convert.

testSetProbs$predClass  = ifelse(testSetProbs$probs > .05 ,1,0)

caret::confusionMatrix(reference = as.factor(testSetProbs$class), 
                       data = as.factor(testSetProbs$predClass), 
                       positive = "1")
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction    0    1
##          0 8624   63
##          1  462   59
##                                           
##                Accuracy : 0.943           
##                  95% CI : (0.9381, 0.9476)
##     No Information Rate : 0.9868          
##     P-Value [Acc > NIR] : 1               
##                                           
##                   Kappa : 0.1656          
##                                           
##  Mcnemar's Test P-Value : <2e-16          
##                                           
##             Sensitivity : 0.483607        
##             Specificity : 0.949153        
##          Pos Pred Value : 0.113244        
##          Neg Pred Value : 0.992748        
##              Prevalence : 0.013249        
##          Detection Rate : 0.006407        
##    Detection Prevalence : 0.056581        
##       Balanced Accuracy : 0.716380        
##                                           
##        'Positive' Class : 1               
## 

Model Analysis

True Negatives (TN) = 8653

False Negatives (FN) = 63

False Positives (FP) = 434

True Positives (TP) = 58

The confusion matrix indicates that the model is highly effective at identifying cells that remain undeveloped, with a large number of true negatives. However, it struggles to accurately detect actual land conversions, as reflected in the low number of true positives.

The sensitivity (47.9%), or true positive rate, suggests that the model correctly identifies less than half of the cells that actually converted from undeveloped to developed. In contrast, the specificity (95.2%) shows that the model is very good at identifying cells that remained undeveloped.

While the overall accuracy (94.6%) appears high, this is primarily driven by the model’s strong performance in predicting the dominant class (non-conversion). This high accuracy can be misleading in imbalanced datasets like this one, where the majority of cells remain undeveloped.

12.3.2. ROC Curve

ggplot(testSetProbs, aes(d = as.numeric(class), m = probs)) + 
  geom_roc(n.cuts = 50, labels = FALSE) + 
  style_roc(theme = theme_grey) +
  geom_abline(slope = 1, intercept = 0, size = 1.5, color = 'grey') +
  theme_minimal()

library(plotROC)
# Ensure class is numeric (0 or 1)
testSetProbs$class <- as.numeric(as.character(testSetProbs$class))

# Calculate AUC
roc_plot <- ggplot(testSetProbs, aes(d = class, m = probs)) + 
  geom_roc(n.cuts = 50, labels = FALSE)

calc_auc(roc_plot)

The Area under the curve (AUC) is 87.5% which is higher than 70% which shows strong overall model fit.

12.4. Analyzing Error

12.4.1. Setting a Threshold

dat_2011_preds <-         
  dat_2011 %>%
    mutate(probs = predict(Model6, dat_2011, type="response")) %>%
   mutate(Threshold_5_Pct = as.factor(ifelse(probs >= 0.05 ,1,0)),
           Threshold_17_Pct =  as.factor(ifelse(probs >= 0.17 ,1,0))) %>%
  mutate(confResult_05 = case_when(Threshold_5_Pct == 0 & development_change == 0 ~ "True_Negative",
                              Threshold_5_Pct == 1 & development_change==1 ~ "True_Positive",
                              Threshold_5_Pct == 0 & development_change==1 ~ "False_Negative",
                              Threshold_5_Pct == 1 & development_change ==0 ~ "False_Positive")) %>%
  mutate(confResult_17 = case_when(Threshold_17_Pct == 0 & development_change == 0 ~ "True_Negative",
                              Threshold_17_Pct == 1 & development_change==1 ~ "True_Positive",
                              Threshold_17_Pct == 0 & development_change==1 ~ "False_Negative",
                              Threshold_17_Pct == 1 & development_change ==0 ~ "False_Positive")) %>%
  st_as_sf()
# Summarize by county and model type
dat_2011_preds %>%
  as.data.frame() %>%
  dplyr::select(confResult_05, confResult_17, NAME) %>%
  pivot_longer(cols = starts_with("confResult"), names_to = "Model_Type", values_to = "Confusion_Result") %>%
  group_by(NAME, Model_Type, Confusion_Result) %>%
  tally() %>%
  pivot_wider(names_from = Confusion_Result, values_from = n, values_fill = 0) %>% # Reshape to wide format
  mutate(TN_Rate_Specificity = 100*( True_Negative/(True_Negative+False_Positive)),
         TP_Rate_Sensitivity = 100*( True_Positive/(True_Positive + False_Negative))) %>%
  dplyr::select(NAME, Model_Type, TN_Rate_Specificity, TP_Rate_Sensitivity) %>%
  kable() %>%
  kable_styling()
NAME Model_Type TN_Rate_Specificity TP_Rate_Sensitivity
Anson confResult_05 98.56828 20.000000
Anson confResult_17 99.77974 0.000000
Cabarrus confResult_05 84.77331 64.516129
Cabarrus confResult_17 99.91446 0.000000
Chester confResult_05 100.00000 0.000000
Chester confResult_17 100.00000 0.000000
Gaston confResult_05 99.47276 0.000000
Gaston confResult_17 100.00000 0.000000
Iredell confResult_05 95.80420 30.000000
Iredell confResult_17 100.00000 0.000000
Lancaster confResult_05 95.48998 61.538461
Lancaster confResult_17 100.00000 0.000000
Lincoln confResult_05 100.00000 16.666667
Lincoln confResult_17 100.00000 0.000000
Mecklenburg confResult_05 83.40865 78.571429
Mecklenburg confResult_17 99.48354 3.571429
Rowan confResult_05 100.00000 0.000000
Rowan confResult_17 100.00000 0.000000
Union confResult_05 92.39709 58.620690
Union confResult_17 99.95157 0.000000
York confResult_05 93.38095 59.375000
York confResult_17 99.90476 0.000000
NA confResult_05 NaN NaN
NA confResult_17 NaN NaN
ggplot() +
  geom_sf(data= dat_2011_preds %>%
            st_centroid() %>%
               dplyr::select(confResult_05, confResult_17, geometry) %>%
               gather(key = "Variable", value = "Value", -geometry), 
             aes(colour=Value)) +
  facet_wrap(~Variable) +
  scale_colour_manual(values = c("red", "yellow", "blue", "grey"), labels=c("False Negative","False Positive", "True Negative", "True Positive"),
                      name="") +
  labs(title="Development Predictions - By Threshold") + 
  theme_void()

The table and map also highlights development patterns across different counties in the region. Counties such as Anson, Chester, and Gaston exhibit very high specificity but negligible sensitivity, indicating minimal land conversion from undeveloped to developed. These areas show limited development activity and can be considered potential targets for future transit-oriented development (TOD). In contrast, more urbanized counties like Mecklenburg and York display higher sensitivity, meaning the model correctly predicted a significant number of conversions reflecting ongoing development activity in these areas.

13. Making A Forecast

Here, we propose a path for a new Lynx Silver rail line from South Gastonia in Gaston County, NC to Monroe, NC, the seat of Union County. This route is proposed for several reasons:

  • Charlotte currently has two primary light rail lines within Mecklenburg County that serve downtown, but neither runs to the airport. With high visitor volume, business traffic, as well as three professional sports team stadiums located downtown, congestion in the region could be dramatically reduced with the extension of a new light rail line that serves communities to the east and west of downtown and connects them through the airport. The airport is pursuing a $4B expansion campaign that includes a renovated and expanded terminal and new runway, which could be built to serve this rail line.

  • The region’s largest population group is 18 to 44 year olds and the median age is 38, demographics that are more likely to utilize public transit. Although Lynx ridership levels are still not back to pre-COVID levels, ridership of the Gold line increased by nearly 14% and the Blue line’s ridership also increased by 12% between 2023 and 2024, indicating there is a market for increased access to public transit.

  • Rather than working across state boundaries, it will be easier to keep the line within North Carolina from a financing and implementation standpoint.

silver_line <- st_read("Data_Charlotte/Proposed_Silverline.dbf")
## Reading layer `Proposed_Silverline' from data source 
##   `C:\Users\goyal\Box\LEUM_Assignemt_4\Data_Charlotte\Proposed_Silverline.dbf' 
##   using driver `ESRI Shapefile'
## Simple feature collection with 1 feature and 1 field
## Geometry type: LINESTRING
## Dimension:     XY
## Bounding box:  xmin: 1375501 ymin: 445451.3 xmax: 1548474 ymax: 545096.7
## Projected CRS: NAD83 / North Carolina (ftUS)
silver_line_proj <- st_transform(silver_line, st_crs(fishnetPopulation19))

# Create a buffer (800 meters = ~2625 feet)
silver_line_buffer <- st_buffer(silver_line_proj, dist = 2625)

# Intersect buffered zone with 2019 fishnet population
pop_served <- fishnetPopulation19[st_intersects(fishnetPopulation19, silver_line_buffer, sparse = FALSE), ]

# Summarize population served
served_summary <- pop_served %>%
  st_drop_geometry() %>%
  summarize(population_served = sum(pop_2019, na.rm = TRUE))

print(served_summary)
##   population_served
## 1          325429.7
acsVariables2 <- c(
  income = "B19013_001", 
  total_pop = "B02001_001", 
  white_alone = "B02001_002")

msa_tracts2 <- map_dfr(names(charlotte_msa_counties), function(st) {
  get_acs(
    geography = "tract",
    variables = acsVariables2,
    state = st,
    county = charlotte_msa_counties[[st]],
    survey = "acs5",
    year = 2019,
    output = "wide",
    geometry = TRUE
  )
}) %>%
  st_transform(st_crs(fishnetPopulation19)) %>%
  mutate(
    pct_nonwhite = 100 * (total_popE - white_aloneE) / total_popE,
    income_group = ntile(incomeE, 6),
    race_group = ntile(pct_nonwhite, 6)
  )

##Income map
ggplot() +
  geom_sf(data = msa_tracts2, aes(fill = factor(income_group)), color = NA) +
  scale_fill_manual(values = palette10, name = "Income Quintile") + 
  geom_sf(data = silver_line_proj, color = "white", size = 1.2) +
  geom_sf(data = silver_line_buffer, fill = "lightblue", alpha = 0.3, color = NA) +
  labs(
    title = "Median Household Income and Silver Line Transit Access (2019)",
    subtitle = "Silver Line buffer represents 800m walk distance"
  ) +
  theme_void()

## Race map
ggplot() +
  geom_sf(data = msa_tracts2, aes(fill = factor(race_group)), color = NA) +
  scale_fill_manual(
    values = c("#FFA500", "#FF7F50", "#FF8C00", "#FF6347", "#FF4500", "#E67E22"),
    name = "% Non-White Quintile"
  ) +
  geom_sf(data = silver_line_proj, color = "white", size = 1.2) +
  geom_sf(data = silver_line_buffer, fill = "lightblue", alpha = 0.3, color = NA) +
  labs(
    title = "Racial Demographics and Silver Line Transit Access (2019)",
    subtitle = "Tracts grouped by % non-white residents"
  ) +
  theme_void()

## Population Map

ggplot() +
  geom_sf(data = fishnetPopulation19 %>%
            mutate(pop_quintile = factor(ntile(pop_2019, 6))), 
          aes(fill = pop_quintile), color = NA) +
  scale_fill_manual(values = c("#edf8e9", "#bae4b3", "#74c476", "#31a354", "#006d2c", "#00441b"), name = "Pop. Quintile") +
  geom_sf(data = silver_line_proj, color = "white", size = 1.2) +
  geom_sf(data = silver_line_buffer, fill = "lightblue", alpha = 0.3, color = NA) +
  labs(title = "Population Density and Silver Line Transit Access (2019)",
       subtitle = "Silver Line buffer represents 800m walk distance") +
  theme_void()

#plotting along with highways to give people currently using a highway an alternate mode of transportation

ggplot() +
  geom_sf(data = fishnetPopulation19 %>%
            mutate(pop_quintile = factor(ntile(pop_2019, 6))), 
          aes(fill = pop_quintile), color = NA) +
  scale_fill_viridis_d(name = "Pop. Quintile") +
  geom_sf(data = silver_line_proj, color = "white", size = 1.2) +
  geom_sf(data = silver_line_buffer, fill = "lightblue", alpha = 0.3, color = NA) +
  geom_sf(data = charHighways, color = "gray30", size = 0.5) +
  labs(title = "Population Density and Silver Line Transit Access with Highways (2019)",
       subtitle = "Silver Line buffer represents 800m walk distance") +
  theme_void()

Median Household Income and Silver Line Transit Access The map illustrates income inequality across the Charlotte MSA, where higher-income households are predominantly concentrated in already developed urban and suburban areas.

Racial Demographics This map reveals clear patterns of racial segregation within the MSA. Areas with a higher percentage of non-white residents tend to be spatially clustered and often located in tracts with less infrastructure investment and limited proximity to high-capacity transit corridors.

Population Density Population density is heavily concentrated in the urban core, particularly around central Charlotte, and gradually decreases toward the periphery. The second map overlays current highway infrastructure, revealing that even areas with low population density have significant highway connectivity. However, development in these regions remains slow, underscoring the need for expanded public transit and affordable, equitable access to the urban center.

Hence, we are proposing adding a new Lynx Silver line which will serve approximately 325,430 people based on 2019 population data within a 800m/0.5mi walkshed of the line.

dat_2019_sf <- st_as_sf(dat_2019)

silver_line <- st_transform(silver_line, st_crs(dat_2019_sf))
dat_2019_sf <- st_transform(dat_2019_sf, st_crs(silver_line))

centroids <- st_centroid(dat_2019_sf)
dist_to_silver <- as.numeric(st_distance(centroids, st_union(silver_line)))

dat_2019$distance_silver <- dist_to_silver

Predicting

dat_2027_preds <- dat_2019 %>%
    mutate(probs = predict(Model6, dat_2019, type="response") ,
           Prediction = as.factor(ifelse(probs >= 0.17 ,1,0))) %>%
  st_as_sf()

Now let’s map it - where are the cells that are classified as likely to develop by 2027? Note that blank cells are water.

ggplot(data=dat_2027_preds) +
  geom_point(aes(x=xyC(dat_2027_preds)[,1], 
                 y=xyC(dat_2027_preds)[,2], colour = Prediction)) +
  geom_sf(data = studyAreaCounties, fill = "transparent")+
  labs(title="Development Predictions, 2027") + 
  theme_void()

The above map highlights all cells which will convert to developed in 2027 taking into account the new transport infrastructure.

14. Assess Impact

Here, we assess the impact of our predictive model - how much development is to be expected in 2027 and where will it occur within the Charlotte region?

14.1. Impact Assessment

dat_2027_preds %>%
  as.data.frame() %>%
  filter(Prediction == 1) %>%
  tally() %>%
  rename(total_cells = n) %>%
  mutate(total_area_m = total_cells * res(lc_2019_rs)[1],
         total_km2 = total_area_m/1000000,
         total_mi2 = total_km2*0.386102) %>%
  kable() %>%
  kable_styling ()
total_cells total_area_m total_km2 total_mi2
55 49500 0.0495 0.019112

County-by-county forecast:

dat_2027_preds %>%
  as.data.frame() %>%
  filter(Prediction == 1) %>%
  group_by(NAME) %>%
  tally() %>%
  rename(total_cells = n) %>%
  mutate(total_area_m = total_cells * res(lc_2019_rs)[1],
         total_km2 = total_area_m/1000000,
         total_mi2 = total_km2*0.386102) %>%
  kable() %>%
  kable_styling ()
NAME total_cells total_area_m total_km2 total_mi2
Anson 5 4500 0.0045 0.0017375
Cabarrus 5 4500 0.0045 0.0017375
Lancaster 9 8100 0.0081 0.0031274
Mecklenburg 17 15300 0.0153 0.0059074
Union 8 7200 0.0072 0.0027799
York 11 9900 0.0099 0.0038224

Forecast by 2019 land cover type:

dat_2027_preds %>%
  as.data.frame() %>%
  filter(Prediction == 1) %>%
  dplyr::select(farm, otherUndeveloped, forest, wetlands, water, NAME) %>%
  gather(-NAME, key = "Variable", value = "Value") %>%
  group_by(NAME, Variable) %>%
  summarize(total_cells = sum(as.numeric(Value))) %>%
  mutate(total_area_m = total_cells * res(lc_2019_rs)[1],
         total_km2 = total_area_m/1000000,
         total_mi2 = total_km2*0.386102) %>%
  kable() %>%
  kable_styling ()
NAME Variable total_cells total_area_m total_km2 total_mi2
Anson farm 0 0 0.0000 0.0000000
Anson forest 0 0 0.0000 0.0000000
Anson otherUndeveloped 5 4500 0.0045 0.0017375
Anson water 0 0 0.0000 0.0000000
Anson wetlands 0 0 0.0000 0.0000000
Cabarrus farm 0 0 0.0000 0.0000000
Cabarrus forest 0 0 0.0000 0.0000000
Cabarrus otherUndeveloped 5 4500 0.0045 0.0017375
Cabarrus water 0 0 0.0000 0.0000000
Cabarrus wetlands 0 0 0.0000 0.0000000
Lancaster farm 0 0 0.0000 0.0000000
Lancaster forest 0 0 0.0000 0.0000000
Lancaster otherUndeveloped 9 8100 0.0081 0.0031274
Lancaster water 0 0 0.0000 0.0000000
Lancaster wetlands 0 0 0.0000 0.0000000
Mecklenburg farm 0 0 0.0000 0.0000000
Mecklenburg forest 0 0 0.0000 0.0000000
Mecklenburg otherUndeveloped 17 15300 0.0153 0.0059074
Mecklenburg water 0 0 0.0000 0.0000000
Mecklenburg wetlands 0 0 0.0000 0.0000000
Union farm 0 0 0.0000 0.0000000
Union forest 0 0 0.0000 0.0000000
Union otherUndeveloped 8 7200 0.0072 0.0027799
Union water 0 0 0.0000 0.0000000
Union wetlands 0 0 0.0000 0.0000000
York farm 0 0 0.0000 0.0000000
York forest 0 0 0.0000 0.0000000
York otherUndeveloped 11 9900 0.0099 0.0038224
York water 0 0 0.0000 0.0000000
York wetlands 0 0 0.0000 0.0000000

15. Recommendations

Planning context According to the U.S. Census Bureau, the city of Charlotte itself is the 15th most populous city in the US and the MSA is the largest metro area in the Carolinas and fourth largest in the southeastern US. According to the Charlotte Regional Business Alliance, the region’s population has grown by 20% since 2010 and is projected to grow an additional 12% by 2030, with an estimated 117 people moving into the region daily. The region has seen a 16% labor force growth since 2010 and has a lower unemployment rate and higher year-over-year employment change than the US on average, as well as the state averages for NC and SC.

Population and economic growth in the greater Charlotte region are due in part to the city’s role as a major financial center and transportation hub, with headquarters for Bank of America and Truist Financial, as well as the largest employment hub for Wells Fargo. Charlotte’s airport is the sixth busiest in the world, with nonstop destinations to 188 locations and an economic impact of nearly $40 billion.

Charlotte has two primary light rail lines within Mecklenburg County that serve downtown, but neither runs to the airport. With high visitor volume, business traffic, as well as three professional sports team stadiums located downtown, congestion in the region could be dramatically reduced with the extension of a new light rail line that serves communities to the east and west of downtown and connects them through the airport. The airport is pursuing a $4B expansion campaign that includes a renovated and expanded terminal and new runway, which could be built to serve this rail line.

Analysis Recommendations 1. Assess a Phased Rail Construction Approach and enable Transit-Oriented Development

In this analysis, we have forecasted potential development in 2027 in the broader Charlotte region based on land cover change between 2011 and 2019 and proposed creating a new Silver light rail line that would connect Charlotte’s downtown, airport, and communities to the west and east. Because the model was not trained with transit data (existing Blue and Gold Lynx lines), our results show limited support for this full route. We did not include the Lynx transit infrastructure because it has not remained consistent throughout our study time period (2011 - 2019), after discussing with Isabel. However, in an initial run of the model, we did see that proximity to the Blue Line was a strong predictor of development between 2011 and 2019. In retrospect, we should have incorporated at least the initial Blue Line (constructed 2007) into our model, along with highways, to better assess the influence of transit-oriented development. Unfortunately, we ran out of time to go back and try this again!

However, our current model does show that development in 2017 is expected to be concentrated in Mecklenburg County (which is where Charlotte’s airport and downtown are located). It is possible that adopting a phased construction approach could prove effective, focusing first on connecting downtown and the airport aligned with the already planned airport terminal expansion. The county’s public transit authority is mostly funded by a half-cent sales tax for transit paid by shoppers in Mecklenburg County, which has surged from $108M in FY19 to roughly $165 million in FY24; the rest comes from the federal government, the city, and fares. If the region wanted to finance a new Silver Lynx line, it could be financed with an increased tax rate (must be passed by voters) or by creating Tax Increment Financing (TIF) districts around rail stops to help finance expansion of the line and encourage more transit-oriented development (TOD) within a 0.5mi/800mi distance of the line.

2. Consider potential hidden impacts to wetlands. It is notable that the most common land cover type that experienced development change between 2011 and 2019 is “Other Undeveloped” lands. Additional analysis of soil type or other features of these areas would be extremely useful to determine more specifics. Although these lands could have been already-cleared sites being prepped for construction, it is also very possible that they were emergent wetlands which do not consistently appear wet or vegetated throughout the year. Wetlands are critical ecosystems that provide a variety of services and benefits like wildlife habitat and fisheries breeding grounds; recreation and tourism; flood risk reduction through erosion control, storm surge buffering, and water storage; and water filtering which improves water quality. Each wetland varies in its hydrology, plant community composition, soil types, biogeochemical processes, and other environmental factors. According to the North Carolina Division of Water Resources, riverine forest wetlands, mountain bogs, and pocosins are particularly common wetlands types in the Piedmont Region of the Carolinas, which may be obscured in the NLCD as forest, shrub, or bare land cover.

Wetlands across the US are facing increasing threats of degradation and loss thanks to development, hydrological alteration like levees and dams, invasive species, and sea level rise. In 2023, the North Carolina Legislature passed a law that substantially reduced protections for wetlands, making them more at risk from development pressures. To prevent the loss of these critical habitats and reduce flood risk for residents, implementing additional local restriction on development of wetlands would be beneficial to make up for the protections lost at the state level. In addition to local ordinances, the region could also pursue a wetlands mitigation bank to ensure there is no net loss of wetlands from development.

3. Protect prime forest and farm lands from development The majority of development our model predicts in 2027 will impact forest lands in Mecklenburg and Lancaster Counties. In order to restrict the impacts of this development on natural systems, the counties could consider implementing tree canopy ordinances that require a certain level of tree canopy coverage to be maintaned by developers. Some areas of Virginia do this already, and it helps to ensure tree replacement along with new development. Additionally, land conservations are an effective tool to help put high-quality land under permanent development restrictions. Both SC and NC offer tax credits to incentivize landowners to sell their development rights. Both counties could identify and contact the owners of prime forest and farm land to see if they would be interested in preserving their lands.

LS0tDQp0aXRsZTogIkFzc2lnbm1lbnQgNCAtIFVyYmFuIEdyb3d0aCBNb2RlbGxpbmciDQphdXRob3I6ICJBbmF1c2hrYSBHb3lhbCBhbmQgR3JhY2UgUm9nZXJzIg0KZGF0ZTogIk1heSAxMHRoLCAyMDI1Ig0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6ICJjb3NtbyINCiAgICBoaWdobGlnaHQ6ICJ0YW5nbyINCiAgICBkZl9wcmludDogInBhZ2VkIg0KICAgIGZvbnQ6ICJSb2JvdG8iDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFKQ0KYGBgDQoNCjxzdHlsZT4NCiAgLnN1cGVyYmlnaW1hZ2V7DQogICAgICBvdmVyZmxvdy14OnNjcm9sbDsNCiAgICAgIHdoaXRlLXNwYWNlOiBub3dyYXA7DQogIH0NCg0KICAuc3VwZXJiaWdpbWFnZSBpbWd7DQogICAgIG1heC13aWR0aDogbm9uZTsNCiAgfQ0KDQoNCjwvc3R5bGU+DQoNCiMgKk92ZXJ2aWV3Kg0KKlRoaXMgZ3Jvd3RoIG1vZGVsIHV0aWxpemVzIGxhbmQgY292ZXIgY2hhbmdlIHNjZW5hcmlvLWJhc2VkIGZvcmVjYXN0aW5nIHRvIGVzdGltYXRlIGZ1dHVyZSBncm93dGggcHJvamVjdGlvbnMgaW4gdGhlIGdyZWF0ZXIgQ2hhcmxvdHRlIHJlZ2lvbiwgb25lIG9mIHRoZSBsYXJnZXN0IGFuZCBmYXN0ZXN0IGdyb3dpbmcgYXJlYXMgb2YgdGhlIFVTLCB0byBoZWxwIGJ1aWxkIHRoZSBjYXNlIGZvciBleHBhbmRpbmcgQ2hhcmxvdHRl4oCZcyBMeW54IGxpZ2h0IHJhaWwgbGluZSouDQoNCiMgMS4gU2V0dXAgLSBMb2FkaW5nIExpYnJhcmllcyBhbmQgRnVuY3Rpb25zDQoNCmBgYHtyIGxvYWRfcGFja2FnZXMsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBjYWNoZSA9IFRSVUUsIHJlc3VsdHMgPSAiaGlkZSJ9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KHJhc3RlcikNCmxpYnJhcnkoa2FibGVFeHRyYSkNCmxpYnJhcnkodGlkeWNlbnN1cykNCmxpYnJhcnkodGlncmlzKQ0KbGlicmFyeShGTk4pDQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeSh5YXJkc3RpY2spDQpsaWJyYXJ5KHBsb3RST0MpIA0KbGlicmFyeShnZ3JlcGVsKQ0KbGlicmFyeShwUk9DKQ0KbGlicmFyeShncmlkKQ0KbGlicmFyeShncmlkRXh0cmEpDQpsaWJyYXJ5KHZpcmlkaXMpDQpsaWJyYXJ5KGlncmFwaCkNCmxpYnJhcnkobWFwdmlldykNCmxpYnJhcnkoRmVkRGF0YSkNCg0KDQpwYWxldHRlMiA8LSBjKCIjMWI5ZTc3IiwgIiNkOTVmMDIiKQ0KcGFsZXR0ZTQgPC0gYygiIzY2YzJhNSIsICIjNDFiNmM0IiwgIiMyYjhjYmUiLCAiI2Q5NWYwMiIpDQpwYWxldHRlNSA8LSBjKCIjZWRmOGZiIiwgIiNiMmUyZTIiLCAiIzY2YzJhNCIsICIjMmI4Y2JlIiwgIiNmYzhkNjIiKSANCnBhbGV0dGUxMCA8LSBjKCIjZjFlZWY2IiwgIiNkMGQxZTYiLCAiI2E2YmRkYiIsICIjNjdhOWNmIiwgIiMxYzkwOTkiLA0KICAgICAgICAgICAgICAgIiM0MWI2YzQiLCAiIzY2YzJhNSIsICIjZmM4ZDYyIiwgIiNlMzRhMzMiLCAiI2IzMDAwMCIpDQoNCmBgYA0KDQojIyAxLjEuIExvYWRpbmcgQ3VzdG9tIGJ1aWxkIGZ1bmN0aW9ucw0KDQpgYGB7ciBsb2FkIGZ1bmN0aW9ucywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlID0gVFJVRSwgcmVzdWx0cyA9ICdoaWRlJ30NCiN0aGlzIGZ1bmN0aW9uIGNvbnZlcnRzIGEgY29sdW1uIGluIHRvIHF1aW50aWxlcy4gSXQgaXMgdXNlZCBmb3IgbWFwcGluZy4NCnF1aW50aWxlQnJlYWtzIDwtIGZ1bmN0aW9uKGRmLHZhcmlhYmxlKSB7DQogICAgYXMuY2hhcmFjdGVyKHF1YW50aWxlKGRmW1t2YXJpYWJsZV1dLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjKC4wMSwuMiwuNCwuNiwuOCksbmEucm09VCkpDQp9DQoNCiNUaGlzIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHRvIGNvbnZlcnQgYSBwb2x5Z29uIHNmIHRvIGNlbnRyb2lkcyB4eSBjb29yZHMuDQp4eUMgPC0gZnVuY3Rpb24oYVBvbHlnb25TRikgew0KICBhcy5kYXRhLmZyYW1lKA0KICAgIGNiaW5kKHg9c3RfY29vcmRpbmF0ZXMoc3RfY2VudHJvaWQoYVBvbHlnb25TRikpWywxXSwNCiAgICAgICAgICB5PXN0X2Nvb3JkaW5hdGVzKHN0X2NlbnRyb2lkKGFQb2x5Z29uU0YpKVssMl0pKQ0KfSANCg0KI3RoaXMgZnVuY3Rpb24gY29udmVydCBhIHJhc3RlciB0byBhIGRhdGEgZnJhbWUgc28gaXQgY2FuIGJlIHBsb3R0ZWQgaW4gZ2dwbG90DQpyYXN0IDwtIGZ1bmN0aW9uKGluUmFzdGVyKSB7DQogIGRhdGEuZnJhbWUoDQogICAgeHlGcm9tQ2VsbChpblJhc3RlciwgMTpuY2VsbChpblJhc3RlcikpLCANCiAgICB2YWx1ZSA9IGdldFZhbHVlcyhpblJhc3RlcikpIH0NCg0KIyBrbm4gZGlzdGFuY2UgZnVuY3Rpb24NCg0Kbm5fZnVuY3Rpb24gPC0gZnVuY3Rpb24obWVhc3VyZUZyb20sbWVhc3VyZVRvLGspIHsNCiAgI2NvbnZlcnQgdGhlIHNmIGxheWVycyB0byBtYXRyaWNlcw0KICBtZWFzdXJlRnJvbV9NYXRyaXggPC0NCiAgICBhcy5tYXRyaXgobWVhc3VyZUZyb20pDQogIG1lYXN1cmVUb19NYXRyaXggPC0NCiAgICBhcy5tYXRyaXgobWVhc3VyZVRvKQ0KICBubiA8LSAgIA0KICAgIGdldC5rbm54KG1lYXN1cmVUbywgbWVhc3VyZUZyb20sIGspJG5uLmRpc3QNCiAgICBvdXRwdXQgPC0NCiAgICBhcy5kYXRhLmZyYW1lKG5uKSAlPiUNCiAgICByb3duYW1lc190b19jb2x1bW4odmFyID0gInRoaXNQb2ludCIpICU+JQ0KICAgIGdhdGhlcihwb2ludHMsIHBvaW50X2Rpc3RhbmNlLCBWMTpuY29sKC4pKSAlPiUNCiAgICBhcnJhbmdlKGFzLm51bWVyaWModGhpc1BvaW50KSkgJT4lDQogICAgZ3JvdXBfYnkodGhpc1BvaW50KSAlPiUNCiAgICBzdW1tYXJpemUocG9pbnREaXN0YW5jZSA9IG1lYW4ocG9pbnRfZGlzdGFuY2UpKSAlPiUNCiAgICBhcnJhbmdlKGFzLm51bWVyaWModGhpc1BvaW50KSkgJT4lIA0KICAgIGRwbHlyOjpzZWxlY3QoLXRoaXNQb2ludCkgJT4lDQogICAgcHVsbCgpDQogIA0KICByZXR1cm4ob3V0cHV0KSAgDQp9DQoNCmFnZ3JlZ2F0ZVJhc3RlciA8LSBmdW5jdGlvbihpbnB1dFJhc3Rlckxpc3QsIHRoZUZpc2huZXQpIHsNCiAgI2NyZWF0ZSBhbiBlbXB0eSBmaXNobmV0IHdpdGggdGhlIHNhbWUgZGltZW5zaW9ucyBhcyB0aGUgaW5wdXQgZmlzaG5ldA0KICB0aGVzZUZpc2huZXRzIDwtIHRoZUZpc2huZXQgJT4lIGRwbHlyOjpzZWxlY3QoKQ0KICAjZm9yIGVhY2ggcmFzdGVyIGluIHRoZSByYXN0ZXIgbGlzdA0KICBmb3IgKGkgaW4gaW5wdXRSYXN0ZXJMaXN0KSB7DQogICNjcmVhdGUgYSB2YXJpYWJsZSBuYW1lIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGl0aCByYXN0ZXINCiAgdmFyTmFtZSA8LSBuYW1lcyhpKQ0KICAjY29udmVydCByYXN0ZXIgdG8gcG9pbnRzIGFzIGFuIHNmDQogICAgdGhlc2VQb2ludHMgPC0NCiAgICAgIHJhc3RlclRvUG9pbnRzKGkpICU+JQ0KICAgICAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICAgICAgc3RfYXNfc2YoY29vcmRzID0gYygieCIsICJ5IiksIGNycyA9IHN0X2Nycyh0aGVGaXNobmV0KSkgJT4lDQogICAgICBmaWx0ZXIoLltbMV1dID09IDEpDQogICNhZ2dyZWdhdGUgdG8gdGhlIGZpc2huZXQNCiAgICB0aGlzRmlzaG5ldCA8LQ0KICAgICAgYWdncmVnYXRlKHRoZXNlUG9pbnRzLCB0aGVGaXNobmV0LCBsZW5ndGgpICU+JQ0KICAgICAgbXV0YXRlKCEhdmFyTmFtZSA6PSBpZmVsc2UoaXMubmEoLltbMV1dKSwwLDEpKQ0KICAjYWRkIHRvIHRoZSBsYXJnZXIgZmlzaG5ldA0KICAgIHRoZXNlRmlzaG5ldHMgPC0gY2JpbmQodGhlc2VGaXNobmV0cyx0aGlzRmlzaG5ldCkNCiAgfQ0KICAjb3V0cHV0IGFsbCBhZ2dyZWdhdGVzIGFzIG9uZSBsYXJnZSBmaXNobmV0DQogICByZXR1cm4odGhlc2VGaXNobmV0cykNCiAgfQ0KYGBgDQoNCiMgMi4gTG9hZGluZyBMYW5kIENvdmVyIGFuZCBNU0EgZGF0YQ0KDQpUaGlzIG1vZGVsIHVzZXMgaGlzdG9yaWNhbCBkYXRhIGZyb20gMjAxMSBhbmQgMjAxOSB0byBwcmVkaWN0IGxhbmQgY2hhbmdlIGZvciAyMDI3IGluIHRoZSBncmVhdGVyIENoYXJsb3R0ZSByZWdpb24sIHRoZSBib3VuZGFyeSBvZiB3aGljaCBpcyBkZWZpbmVkIGFzIHRoZSBDaGFybG90dGUtQ29uY29yZC1HYXN0b25pYSwgTkMtU0MgTWV0cm9wb2xpdGFuIFN0YXRpc3RpY2FsIEFyZWEgKE1TQSkuIERhdGEgY29tZSBmcm9tIHRoZSBVLlMuIENlbnN1cyBCdXJlYXUgYW5kIGxhbmQgY292ZXIgZGF0YSBjb21lIGZyb20gdGhlIFUuUy4gR2VvbG9naWNhbCBTdXJ2ZXkgKFVTR1MpJ3MgTmF0aW9uYWwgTGFuZCBDb3ZlciBEYXRhYmFzZSAoTkxDRCkuIFNpbmNlIHRoZSByZWdpb24gc3BhbnMgdHdvIHN0YXRlcywgY2Vuc3VzIGRhdGEgd2VyZSByZXRyaWV2ZWQgc2VwYXJhdGVseSBmb3IgZWFjaCBzdGF0ZSBhbmQgdGhlbiBjb21iaW5lZCBpbnRvIGEgc2luZ2xlIGRhdGFzZXQgZm9yIHNwYXRpYWwgYW5hbHlzaXMuIEluIE5vcnRoIENhcm9saW5hLCB0aGUgc2VsZWN0ZWQgY291bnRpZXMgYXJlIE1lY2tsZW5idXJnLCBDYWJhcnJ1cywgR2FzdG9uLCBJcmVkZWxsLCBMaW5jb2xuLCBSb3dhbiwgVW5pb24sIGFuZCBBbnNvbi4gSW4gU291dGggQ2Fyb2xpbmEsIHRoZSBpbmNsdWRlZCBjb3VudGllcyBhcmUgWW9yaywgQ2hlc3RlciwgYW5kIExhbmNhc3Rlci4NCg0KIyMgMi4xLiBMb2FkIHRoZSBzdHVkeSBhcmVhIE1TQQ0KDQpgYGB7ciBNU0EsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGNhY2hlPVRSVUV9DQoNCmNlbnN1c19hcGlfa2V5KCIwMjUyNDkxMjA4MTgwMTA2OGYwNGYyODZhMDg2ZDE5ZTlmYzY0MWNhIiwgaW5zdGFsbCA9IFRSVUUsIG92ZXJ3cml0ZSA9IFRSVUUpDQpyZWFkUmVudmlyb24oIn4vLlJlbnZpcm9uIikNCg0KIyBEZWZpbmUgY291bnRpZXMgYnkgc3RhdGUNCmNoYXJsb3R0ZV9tc2FfY291bnRpZXMgPC0gbGlzdCgNCiAgTkMgPSBjKCJNZWNrbGVuYnVyZyIsICJDYWJhcnJ1cyIsICJHYXN0b24iLCAiSXJlZGVsbCIsICJMaW5jb2xuIiwgDQogICAgICAgICAiUm93YW4iLCAiVW5pb24iLCAiQW5zb24iKSwNCiAgU0MgPSBjKCIwOTEiLCAiMDIzIiwgIjA1NyIpDQopDQoNCiMgVmFyaWFibGUgdG8gZG93bmxvYWQNCmFjc1ZhcmlhYmxlcyA8LSBjKHRvdHBvcCA9ICJCMDEwMDFfMDAxIikNCg0KDQojIFB1bGwgZGF0YSBieSBzdGF0ZSBhbmQgY291bnR5DQptc2FfdHJhY3RzIDwtIG1hcF9kZnIobmFtZXMoY2hhcmxvdHRlX21zYV9jb3VudGllcyksIGZ1bmN0aW9uKHN0KSB7DQogIGdldF9hY3MoDQogICAgZ2VvZ3JhcGh5ID0gInRyYWN0IiwNCiAgICB2YXJpYWJsZXMgPSBhY3NWYXJpYWJsZXMsDQogICAgc3RhdGUgPSBzdCwNCiAgICBjb3VudHkgPSBjaGFybG90dGVfbXNhX2NvdW50aWVzW1tzdF1dLA0KICAgIHN1cnZleSA9ICJhY3M1IiwNCiAgICB5ZWFyID0gMjAxOSwNCiAgICBvdXRwdXQgPSAid2lkZSIsDQogICAgZ2VvbWV0cnkgPSBUUlVFDQogICkNCn0pICU+JQ0KICBzdF90cmFuc2Zvcm0oMzIxMTkpICAjIE5BRDgzIC8gTm9ydGggQ2Fyb2xpbmEgKGZ0KQ0KDQojIENyZWF0ZSB1bmlmaWVkIGJvdW5kYXJ5DQptc2FfYm91bmRhcnkgPC0gbXNhX3RyYWN0cyAlPiUNCiAgc3RfdW5pb24oKSAlPiUNCiAgc3RfYXNfc2YoKQ0KDQpnZ3Bsb3QoKSArIA0KICAgIGdlb21fc2YoZGF0YSA9IG1zYV9ib3VuZGFyeSwgZmlsbCA9ICIjYTZjZWUzIiwgY29sb3IgPSAiIzFmNzhiNCIpICsNCiAgZ2VvbV9zZihkYXRhPW1zYV90cmFjdHMsIGZpbGw9IE5BLCAgY29sb3I9ICIjZTM0YTMzIikgKw0KICBsYWJzKHRpdGxlID0gIkNoYXJsb3R0ZSBNU0EgQm91bmRhcnkiKSArDQogIHRoZW1lX3ZvaWQoKQ0KDQpgYGANCg0KDQojIyAyLjIuIExvYWQgTGFuZCBDb3ZlciBmb3IgdDEgYW5kIHQyDQoNCmBgYHtyIExvYWQgTGFuZCBDb3Zlciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQoNCmxjXzIwMTEgPC0gZ2V0X25sY2QoDQogIHRlbXBsYXRlID0gbXNhX2JvdW5kYXJ5LA0KICBsYWJlbCA9ICJjaGFybG90dGVfbGNfMjAxMSIsDQogIHllYXIgPSAyMDExKSAlPiUNCiAgcmFzdGVyKC4pICANCg0KDQpsY18yMDExX2RmIDwtIGFzLmRhdGEuZnJhbWUocmFzdGVyVG9Qb2ludHMobGNfMjAxMSkpDQpjb2xuYW1lcyhsY18yMDExX2RmKSA8LSBjKCJ4IiwgInkiLCAibGFuZGNvdmVyIikgIA0KDQoNCmxhbmRjb3Zlcl9sYWJlbHMgPC0gYygNCiAgIjExIiA9ICJPcGVuIFdhdGVyIiwNCiAgIjIxIiA9ICJEZXZlbG9wZWQsIE9wZW4gU3BhY2UiLA0KICAiMjIiID0gIkRldmVsb3BlZCwgTG93IEludGVuc2l0eSIsDQogICIyMyIgPSAiRGV2ZWxvcGVkLCBNZWRpdW0gSW50ZW5zaXR5IiwNCiAgIjI0IiA9ICJEZXZlbG9wZWQsIEhpZ2ggSW50ZW5zaXR5IiwNCiAgIjMxIiA9ICJCYXJyZW4gTGFuZCIsDQogICI0MSIgPSAiRGVjaWR1b3VzIEZvcmVzdCIsDQogICI0MiIgPSAiRXZlcmdyZWVuIEZvcmVzdCIsDQogICI0MyIgPSAiTWl4ZWQgRm9yZXN0IiwNCiAgIjUyIiA9ICJTaHJ1Yi9TY3J1YiIsDQogICI3MSIgPSAiR3Jhc3NsYW5kL0hlcmJhY2VvdXMiLA0KICAiODEiID0gIlBhc3R1cmUvSGF5IiwNCiAgIjgyIiA9ICJDdWx0aXZhdGVkIENyb3BzIiwNCiAgIjkwIiA9ICJXb29keSBXZXRsYW5kcyIsDQogICI5NSIgPSAiRW1lcmdlbnQgSGVyYmFjZW91cyBXZXRsYW5kcyINCikNCg0KDQpnZ3Bsb3QobGNfMjAxMV9kZiwgYWVzKHggPSB4LCB5ID0geSwgZmlsbCA9IGZhY3RvcihsYW5kY292ZXIpKSkgKw0KICBnZW9tX3Jhc3RlcigpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwoDQogICAgbmFtZSA9ICJMYW5kIENvdmVyIENsYXNzIiwNCiAgICB2YWx1ZXMgPSB2aXJpZGlzOjp2aXJpZGlzKGxlbmd0aChsYW5kY292ZXJfbGFiZWxzKSksDQogICAgbGFiZWxzID0gbGFuZGNvdmVyX2xhYmVsc1tsZXZlbHMoZmFjdG9yKGxjXzIwMTFfZGYkbGFuZGNvdmVyKSldDQogICkgKw0KICBjb29yZF9lcXVhbCgpICsNCiAgbGFicyh0aXRsZSA9ICJMYW5kIENvdmVyIC0gMjAxMSAoTkxDRCkiLCB4ID0gTlVMTCwgeSA9IE5VTEwpICsNCiAgdGhlbWVfdm9pZCgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpDQoNCiANCmBgYA0KDQpUaGUgYWJvdmUgbWFwIHNob3dzIHRoZSBkaXN0cmlidXRpb24gb2YgbGFuZGNvdmVyIGNsYXNzZXMgaW4gdGhlIHN0dWR5IGFyZWEuIA0KDQpgYGB7ciBMYW5kIENvdmVyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgY2FjaGU9VFJVRX0NCg0KDQpsY18yMDE5IDwtIGdldF9ubGNkKA0KICAgIHRlbXBsYXRlID0gbXNhX2JvdW5kYXJ5LA0KICAgIGxhYmVsID0gImNoYXJsb3R0ZV9sY18yMDE5IiwNCiAgICB5ZWFyID0gMjAxOQ0KICApICU+JQ0KICByYXN0ZXIoLikNCmBgYA0KDQojIDMuIERldGVjdGluZyBMYW5kIENvdmVyIENvbnZlcnNpb24NCkhlcmUsIHdlIGRldGVjdCBsYW5kIGNvdmVyIGNoYW5nZSBiZXR3ZWVuIDIwMTEgYW5kIDIwMTkgdGhhdCBoYXMgdHJhbnNpdGlvbmVkIGZyb20gdW5kZXZlbG9wZWQgdG8gZGV2ZWxvcGVkIGxhbmQuDQoNCiMjIDMuMS4gUmVzYW1wbGluZyBSYXN0ZXJzDQoNCmBgYHtyIFJlc2FtcGxpbmcgUmFzdGVyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgY2FjaGU9VFJVRX0NCmxjXzIwMTFfcnMgPC0gYWdncmVnYXRlKGxjXzIwMTEsIGZhY3QgPSAzMCwgZnVuID0gIm1vZGFsIikNCg0KbGNfMjAxOV9ycyA8LSBhZ2dyZWdhdGUobGNfMjAxOSwgZmFjdCA9IDMwLCBmdW4gPSAibW9kYWwiKQ0KDQpgYGANCg0KIyMgMy4yLiBSZWNsYXNzaWZ5aW5nIEFzIERldmVsb3BlZCBhbmQgVW5kZXZlbG9wZWQNCg0KYGBge3IgUmVjbGFzc2lmeWluZywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQpyZWNsYXNzTWF0cml4IDwtIA0KICBtYXRyaXgoYygNCiAgICAwLDEyLDAsDQogICAgMTIsMjQsMSwNCiAgICAyNCxJbmYsMCksDQogIG5jb2w9MywgYnlyb3c9VCkNCmBgYA0KDQpUaGUgYWJvdmUgbWF0cml4IHJlY2xhc3NpZmllcyB1cmJhbiBhcmVhcyBhcyAxIGFuZCBldmVyeXRoaW5nIGVsc2UgYXMgMC4gDQoNCmBgYHtyIFJlY2xhc3NpZmljYXRpb24gT25lICwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQpkZXZlbG9wZWRfMjAxMSA8LSANCiAgcmVjbGFzc2lmeShsY18yMDExX3JzLCByZWNsYXNzTWF0cml4KQ0KDQpkZXZlbG9wZWRfMjAxOSA8LSANCiAgcmVjbGFzc2lmeShsY18yMDE5X3JzLCByZWNsYXNzTWF0cml4KQ0KDQpgYGANCg0KVGhlIGFib3ZlIGNyZWF0ZXMgYmluYXJ5IHJhc3RlcnM6IDEgZm9yIGRldmVsb3BlZCwgMCBmb3IgdW5kZXZlbG9wZWQuDQoNCmBgYHtyIEJpbmFyeSBSYXN0ZXIsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBjYWNoZT1UUlVFfQ0KDQpkZXZlbG9wbWVudF9jaGFuZ2UgPC0gZGV2ZWxvcGVkXzIwMTEgKyBkZXZlbG9wZWRfMjAxOQ0KDQpgYGANCg0KTm93LCB3ZSBhcmUgY29tcGFyaW5nIHR3byByYXN0ZXJzIGNlbGwgYnkgY2VsbC4gU2luY2Ugd2Ugb25seSBoYXZlIDEgYW5kIDAgaW4gb3VyIHJhc3RlcnMgbm93LCAwICsgMCA9IDAgbWVhbnMgdGhlcmUgd2FzIG5vIGNoYW5nZSBpbiB1bmRldmVsb3BlZCBsYW5kLCB3aGVyZWFzIDAgKyAxID0gMSBtZWFucyBsYW5kIHdlbnQgZnJvbSB1bmRldmVsb3BlZCB0byBkZXZlbG9wZWQuIFdlIGFyZSBub3QgaW50ZXJlc3RlZCBpbiBkZXZlbG9wZWQgbGFuZCB0aGF0IHJlbWFpbnMgZGV2ZWxvcGVkICgxICsgMSA9IDIpLCBvciBkZXZlbG9wZWQgbGFuZCB0aGF0IGhhcyB0cmFuc2l0aW9uZWQgdG8gdW5kZXZlbG9wZWQgKDEgKyAwID0gMSkuIEZvciB0aGlzIGdyb3d0aCBwcm9qZWN0aW9uLCB3ZSBhcmUgb25seSBpbnRlcmVzdGVkIGluIHJhc3RlcnMgdGhhdCBjb252ZXJ0ZWQgZnJvbSAwIHRvIDEgKHVuZGV2ZWxvcGVkIHRvIGRldmVsb3BlZCkuIA0KDQpgYGB7ciBIaXN0b2dyYW0sIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBjYWNoZT1UUlVFfQ0KaGlzdChkZXZlbG9wbWVudF9jaGFuZ2UsDQogICAgIG1haW4gPSAiRGlzdHJpYnV0aW9uIG9mIERldmVsb3BtZW50IENoYW5nZSIsDQogICAgIHhsYWIgPSAiRGV2ZWxvcG1lbnQgQ2hhbmdlIFZhbHVlIiwNCiAgICAgY29sID0gcGFsZXR0ZTJbMl0sICAgIyBVc2UgdGhlIGRhcmtlciBjb2xvciBmb3IgZGV2ZWxvcGVkICh2YWx1ZSA9IDEpDQogICAgIGJvcmRlciA9ICJ3aGl0ZSIpDQpgYGANCg0KVGhpcyBoaXN0b2dyYW0gc2hvd3MgdGhlIG51bWJlciBvZiBjZWxscyBvZiBlYWNoIHZhbHVlLCBhbmQgeW91IGNhbiBzZWUgdGhlIG1ham9yaXR5IG9mIGNlbGxzIHJlbWFpbmVkIHVuZGV2ZWxvcGVkIGJldHdlZW4gMjAxMSBhbmQgMjAxOSAodmFsdWUgb2YgMCksIGxlc3MgdGhhbiA0MDAgaGF2ZSByZW1haW5lZCBkZXZlbG9wZWQsIGFuZCB0aGUgc21hbGxlc3QgbnVtYmVyIG9mIGNlbGxzIGhhdmUgdHJhbnNpdGlvbmVkIGJldHdlZW4gdW5kZXZlbG9wZWQgdG8gZGV2ZWxvcGVkIGxhbmQgdXNlLg0KDQpgYGB7ciBEZXZlbG9wbWVudCBDaGFuZ2VzLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgY2FjaGU9VFJVRX0NCmRldmVsb3BtZW50X2NoYW5nZVtkZXZlbG9wbWVudF9jaGFuZ2UgIT0gMV0gPC0gTkENCg0KYGBgDQoNCkhlcmUsIHdlIGRyb3AgYWxsIHZhbHVlcyB0aGF0IGFyZSBub3QgMSwgc2luY2UgdGhpcyBpcyB0aGUgb25seSBjaGFuZ2Ugd2UgYXJlIGludGVyZXN0ZWQgaW4gZm9yZWNhc3RpbmcgdG8gMjAyNy4gDQoNCiMjIDMuMy4gRXhhbWluaW5nIExhbmQgQ292ZXIgQ2hhbmdlDQoNCkxldCdzIHVzZSBNYXB2aWV3IHRvIHRha2UgYSBsb29rIGF0IHRoZSBgZGV2ZWxvcG1lbnRfY2hhbmdlYCByYXN0ZXIuDQoNCmBgYHtyIExhbmQgQ292ZXIgQ2hhbmdlcywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQptYXBWaWV3KGRldmVsb3BtZW50X2NoYW5nZSwgDQogICAgICAgIGNvbC5yZWdpb25zID0gcGFsZXR0ZTIsIA0KICAgICAgICBuYS5jb2xvciA9ICJ0cmFuc3BhcmVudCIsIA0KICAgICAgICBsYXllci5uYW1lID0gIkRldmVsb3BtZW50IENoYW5nZSIpDQoNCmBgYA0KDQpUaGUgcmVzdWx0aW5nIG1hcCBzaG93cyBhbGwgcmFzdGVyIGdyaWQgY2VsbHMgaW4gdGhlIE1TQSB3aGljaCB3ZXJlIHVuZGV2ZWxvcGVkIGluIDIwMTEgYW5kIHRyYW5zaXRpb25lZCB0byBkZXZlbG9wZWQgYnkgMjAxOS4gDQoNCiMgNC4gQ3JlYXRpbmcgdGhlIEZpc2huZXQNCg0KYGBge3IgY3JlYXRpbmcgZmlzaG5ldCwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQptc2FfZmlzaG5ldCA8LSANCiAgc3RfbWFrZV9ncmlkKG1zYV9ib3VuZGFyeSAlPiUNCiAgICAgICAgICAgICAgICAgICAgIHN0X3RyYW5zZm9ybShjcnMoZGV2ZWxvcG1lbnRfY2hhbmdlKSksDQogICAgICAgICAgICAgICAgIGNlbGxzaXplID0gcmVzKGRldmVsb3BtZW50X2NoYW5nZSlbMV0sIA0KICAgICAgICAgICAgICAgICBzcXVhcmUgPSBUUlVFKSAlPiUgDQogIHN0X3NmKCkgJT4lIA0KICBzdF9pbnRlcnNlY3Rpb24oLiwgbXNhX2JvdW5kYXJ5ICU+JQ0KICAgICAgICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KHgpICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgIHN0X3RyYW5zZm9ybShjcnMoZGV2ZWxvcG1lbnRfY2hhbmdlKSkpICU+JQ0KICBtdXRhdGUodW5pcXVlSUQgPSByb3duYW1lcyguKSkNCmBgYA0KDQojIDUuIEpvaW4gUmFzdGVyIERhdGEgdG8gdGhlIEZpc2huZXQNCg0KIyMgNS4xLiBBZ2dyZWdhdGluZyBMQyBDaGFuZ2UgdG8gZmlzaG5ldA0KDQpgYGB7ciBMQyBDaGFuZ2UgdG8gZmlzaG5ldCwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQpjaGFuZ2VQb2ludHMgPC0NCiAgcmFzdGVyVG9Qb2ludHMoZGV2ZWxvcG1lbnRfY2hhbmdlKSAlPiUNCiAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICBzdF9hc19zZihjb29yZHMgPSBjKCJ4IiwgInkiKSwgDQogICAgICAgICAgIGNycyA9IHN0X2Nycyhtc2FfZmlzaG5ldCkpDQoNCmZpc2huZXQgPC0gDQogIGFnZ3JlZ2F0ZShjaGFuZ2VQb2ludHMsIA0KICAgICAgICAgICAgbXNhX2Zpc2huZXQsIA0KICAgICAgICAgICAgRlVOPXN1bSkgJT4lDQogIG11dGF0ZShkZXZlbG9wbWVudF9jaGFuZ2UgPSBpZmVsc2UoaXMubmEobGF5ZXIpID09IFRSVUUgLCAwLCAxKSwNCiAgICAgICAgIGRldmVsb3BtZW50X2NoYW5nZSA9IGFzLmZhY3RvcihkZXZlbG9wbWVudF9jaGFuZ2UpKSAlPiUNCiAgZHBseXI6OnNlbGVjdCgtbGF5ZXIpDQoNCmBgYA0KDQoNCiMjIyA1LjEuMS4gUGxvdHRpbmcgRGV2ZWxvcG1lbnQgQ2hhbmdlDQoNCmBgYHtyIFBsb3R0aW5nIERldmVsb3BtZW50IENoYW5nZSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YT1tc2FfYm91bmRhcnkgJT4lIA0KICAgICAgICAgICAgc3RfdHJhbnNmb3JtKGNycyhmaXNobmV0KSkpICsNCiAgZ2VvbV9wb2ludChkYXRhPWZpc2huZXQsIA0KICAgICAgICAgICAgIGFlcyh4PXh5QyhmaXNobmV0KSR4LCANCiAgICAgICAgICAgICAgICAgeT14eUMoZmlzaG5ldCkkeSwgDQogICAgICAgICAgICAgICAgIGNvbG91cj1kZXZlbG9wbWVudF9jaGFuZ2UpKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5ldyBEZXZlbG9wbWVudCIpLA0KICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiIikgKw0KICBsYWJzKHRpdGxlID0gIkxhbmQgQ292ZXIgRGV2ZWxvcG1lbnQgQ2hhbmdlIiwgc3VidGl0bGUgPSAiQXMgZmlzaG5ldCBjZW50cm9pZHMiKSArDQogIHRoZW1lX3ZvaWQoKQ0KYGBgDQoNCg0KIyMgNS4yLiBBZ2dyZWdhdGluZyBCYXRjaCBMYW5kIENvdmVyIERhdGEgdG8gdGhlIEZpc2huZXQNCkhlcmUsIHdlIGFnZ3JlZ2F0ZSBvdXIgMjAxMSByYXN0ZXIgbGFuZCBjb3ZlciBkYXRhIGludG8gYSBmZXcgY2F0ZWdvcmllcyBhbmQgY29tYmluZSB0aGlzIHdpdGggb3VyIGJpbmFyeSBjbGFzc2lmaWNhdGlvbiBvZiBsYW5kIHRoYXQgd2FzIGRldmVsb3BlZCBiZXR3ZWVuIDIwMTEgYW5kIDIwMTkgdG8gc2VlIHRoZSBhbW91bnQgYW5kIGxvY2F0aW9uIG9mIGRldmVsb3BtZW50IGJhc2VkIG9uIHNwZWNpZmljIGxhbmQgY292ZXIgdHlwZXMuDQoNCiMjIyA1LjIuMS4gUmVjbGFzc2lmeWluZyBsYW5kIGNvdmVyDQoNCldlIGFyZSBnb2luZyB0byByZWNvZGUgb3VyIE5MQ0QgZGF0YSBpbnRvIGEgZmV3IHJlZHVjZWQgY2F0ZWdvcmllcyB0byBiZSB1c2VkIGluIG91ciBhbmFseXNpcy4gSGVyZSBpcyB0aGUgY2xhc3NpZmljYXRpb24gbG9naWM6IA0KDQp8IE9sZF9DbGFzc2lmaWNhdGlvbiAgICAgICAgICAgICB8IE5ld19DbGFzc2lmaWNhdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IE9wZW4gU3BhY2UgYXMgd2VsbCBhcyBMb3csIE1lZGl1bSBhbmQgSGlnaCBJbnRlbnNpdHkgRGV2ZWxvcG1lbnQgfCBEZXZlbG9wZWQgfA0KfCBEZWNpZHVvdXMsIEV2ZXJncmVlbiwgYW5kIE1peGVkIEZvcmVzdCB8ICBGb3Jlc3QgfA0KfCBQYXN0dXJlL0hheSBhbmQgQ3VsdGl2YXRlZCBDcm9wcyB8IEZhcm0gfA0KfCBXb29keSBhbmQgRW1lcmdlbnQgSGVyYmFjZW91cyBXZXRsYW5kcyB8IFdldGxhbmRzIHwNCnwgQmFycmVuIExhbmQsIER3YXJmIFNjcnViLCBhbmQgR3Jhc3NsYW5kL0hlcmJhY2VvdXMgfCBPdGhlciBVbmRldmVsb3BlZCB8DQp8IFdhdGVyIHwgV2F0ZXIgfA0KDQoNCkluIHRoZSBjb2RlIGJsb2NrIGJlbG93LCB3ZSBjcmVhdGUgbmV3IHJhc3RlcnMsIGRlc2NyaXB0aXZlbHkgY2FsbGVkIGBkZXZlbG9wZWRfMjAxMWAsIGBmb3Jlc3RfMjAxMWAgZXRjICwgYWNjb3JkaW5nIHRvIHRoZSBOTENEIGNvZGVzIHRoYXQgY29ycmVzcG9uZCB0byBvdXIgY2xhc3NpZmljYXRpb24gc2NoZW1lLiANCg0KYGBge3IgTkxDRCBjb2Rlcywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQoNCmRldmVsb3BlZF8yMDExIDwtIGxjXzIwMTFfcnMgICVpbiUgYygyMSwgMjIsIDIzLCAyNCkNCmZvcmVzdF8yMDExIDwtIGxjXzIwMTFfcnMgJWluJSBjKDQxLCA0MiwgNDIpDQpmYXJtXzIwMTEgPC0gbGNfMjAxMV9ycyAlaW4lIGMoODEsIDgyKQ0Kd2V0bGFuZHNfMjAxMSA8LSBsY18yMDExX3JzICVpbiUgYyg5MCwgOTUpIA0Kb3RoZXJVbmRldmVsb3BlZF8yMDExIDwtIGxjXzIwMTFfcnMgJWluJSBjKDUyLCA3MSwgMzEpDQp3YXRlcl8yMDExIDwtIGxjXzIwMTFfcnMgPT0gMTENCg0KZGV2ZWxvcGVkXzIwMTkgPC0gbGNfMjAxOV9ycyAlaW4lIGMoMjEsIDIyLCAyMywgMjQpDQpmb3Jlc3RfMjAxOSA8LSBsY18yMDE5X3JzICVpbiUgYyg0MSwgNDIsIDQyKQ0KZmFybV8yMDE5IDwtIGxjXzIwMTlfcnMgJWluJSBjKDgxLCA4MikNCndldGxhbmRzXzIwMTkgPC0gbGNfMjAxOV9ycyAlaW4lIGMoOTAsIDk1KSANCm90aGVyVW5kZXZlbG9wZWRfMjAxOSA8LSBsY18yMDE5X3JzICVpbiUgYyg1MiwgNzEsIDMxKQ0Kd2F0ZXJfMjAxOSA8LSBsY18yMDE5X3JzID09IDExDQpgYGANCg0KDQojIyMgNS4yLjIuIEFnZ3JlZ2F0aW5nIFJhc3RlcnMgdG8gdGhlIEZpc2huZXQNCg0KYGBge3IgQWdncmVnYXRpbmcgUmFzdGVycywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQoNCm5hbWVzKGRldmVsb3BlZF8yMDExKSA8LSAiZGV2ZWxvcGVkXzIwMTEiDQpuYW1lcyhmb3Jlc3RfMjAxMSkgPC0gImZvcmVzdF8yMDExIg0KbmFtZXMoZmFybV8yMDExKSA8LSAiZmFybV8yMDExIg0KbmFtZXMod2V0bGFuZHNfMjAxMSkgPC0gIndldGxhbmRzXzIwMTEiDQpuYW1lcyhvdGhlclVuZGV2ZWxvcGVkXzIwMTEpIDwtICJvdGhlclVuZGV2ZWxvcGVkXzIwMTEiDQpuYW1lcyh3YXRlcl8yMDExKSA8LSAid2F0ZXJfMjAxMSINCg0KbmFtZXMoZGV2ZWxvcGVkXzIwMTkpIDwtICJkZXZlbG9wZWRfMjAxOSINCm5hbWVzKGZvcmVzdF8yMDE5KSA8LSAiZm9yZXN0XzIwMTkiDQpuYW1lcyhmYXJtXzIwMTkpIDwtICJmYXJtXzIwMTkiDQpuYW1lcyh3ZXRsYW5kc18yMDE5KSA8LSAid2V0bGFuZHNfMjAxOSINCm5hbWVzKG90aGVyVW5kZXZlbG9wZWRfMjAxOSkgPC0gIm90aGVyVW5kZXZlbG9wZWRfMjAxOSINCm5hbWVzKHdhdGVyXzIwMTkpIDwtICJ3YXRlcl8yMDE5Ig0KYGBgDQoNCg0KYGBge3IgRmlzaG5ldCBSYXN0ZXIgQWdncmVnYXRlLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgY2FjaGU9VFJVRX0NCnJhc3Rlckxpc3RfMjAxMSA8LSBjKGRldmVsb3BlZF8yMDExLA0KICAgICAgICAgICAgICAgICAgIGZvcmVzdF8yMDExLA0KICAgICAgICAgICAgICAgICAgIGZhcm1fMjAxMSwNCiAgICAgICAgICAgICAgICAgICB3ZXRsYW5kc18yMDExLA0KICAgICAgICAgICAgICAgICAgIG90aGVyVW5kZXZlbG9wZWRfMjAxMSwNCiAgICAgICAgICAgICAgICAgICB3YXRlcl8yMDExKQ0KDQpyYXN0ZXJMaXN0XzIwMTkgPC0gYyhkZXZlbG9wZWRfMjAxOSwNCiAgICAgICAgICAgICAgICAgICBmb3Jlc3RfMjAxOSwNCiAgICAgICAgICAgICAgICAgICBmYXJtXzIwMTksDQogICAgICAgICAgICAgICAgICAgd2V0bGFuZHNfMjAxOSwNCiAgICAgICAgICAgICAgICAgICBvdGhlclVuZGV2ZWxvcGVkXzIwMTksDQogICAgICAgICAgICAgICAgICAgd2F0ZXJfMjAxOSkNCmBgYA0KDQoNCg0KYGBge3IgRmlzaG5ldCBSYXN0ZXIgQWdncmVnYXRlIE9uZSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQpsY1Jhc3RlcnNfMjAxMSA8LQ0KICBhZ2dyZWdhdGVSYXN0ZXIocmFzdGVyTGlzdF8yMDExLCANCiAgICAgICAgICAgICAgICAgIG1zYV9maXNobmV0KSAlPiUNCiAgZHBseXI6OnNlbGVjdChkZXZlbG9wZWRfMjAxMSwNCiAgICAgICAgICAgICAgICAgICBmb3Jlc3RfMjAxMSwNCiAgICAgICAgICAgICAgICAgICBmYXJtXzIwMTEsDQogICAgICAgICAgICAgICAgICAgd2V0bGFuZHNfMjAxMSwNCiAgICAgICAgICAgICAgICAgICBvdGhlclVuZGV2ZWxvcGVkXzIwMTEsDQogICAgICAgICAgICAgICAgICAgd2F0ZXJfMjAxMSkgJT4lDQogIG11dGF0ZV9pZihpcy5udW1lcmljLGFzLmZhY3RvcikNCg0KbGNSYXN0ZXJzXzIwMTkgPC0NCiAgYWdncmVnYXRlUmFzdGVyKHJhc3Rlckxpc3RfMjAxOSwgDQogICAgICAgICAgICAgICAgICBtc2FfZmlzaG5ldCkgJT4lDQogIGRwbHlyOjpzZWxlY3QoZGV2ZWxvcGVkXzIwMTksDQogICAgICAgICAgICAgICAgICAgZm9yZXN0XzIwMTksDQogICAgICAgICAgICAgICAgICAgZmFybV8yMDE5LA0KICAgICAgICAgICAgICAgICAgIHdldGxhbmRzXzIwMTksDQogICAgICAgICAgICAgICAgICAgb3RoZXJVbmRldmVsb3BlZF8yMDE5LA0KICAgICAgICAgICAgICAgICAgIHdhdGVyXzIwMTkpICU+JQ0KICBtdXRhdGVfaWYoaXMubnVtZXJpYyxhcy5mYWN0b3IpDQoNCmBgYA0KDQpGaW5hbGx5LCB3ZSB0YWtlIHRoaXMgZGF0YSBhbmQgbWFwIHRoZW0gdG8gc2VlIHdoZXJlIGRldmVsb3BlZCBoYXMgb2NjdXJyZWQgYnkgbGFuZCBjb3ZlciB0eXBlLg0KDQpgYGB7ciBGaXNobmV0IFJhc3RlciBBZ2dyZWdhdGUgVHdvLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgY2FjaGU9VFJVRX0NCmxjUmFzdGVyc18yMDExICU+JQ0KICBzdF9jZW50cm9pZCgpICU+JQ0KIGdhdGhlcihrZXkgPSAidmFyaWFibGUiLCB2YWx1ZSA9ICJ2YWx1ZSIsIGRldmVsb3BlZF8yMDExOndhdGVyXzIwMTEpICU+JSANCiAgbXV0YXRlKFggPSB4eUMoLikkeCwNCiAgICAgICAgIFkgPSB4eUMoLikkeSkgJT4lDQogIGdncGxvdCgpICsNCiAgICBnZW9tX3NmKGRhdGE9bXNhX2Zpc2huZXQpICsNCiAgICBnZW9tX3BvaW50KGFlcyhYLFksIGNvbG91cj1hcy5mYWN0b3IodmFsdWUpKSkgKw0KICAgIGZhY2V0X3dyYXAofnZhcmlhYmxlKSArDQogICAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJPdGhlciIsIkxhbmQgQ292ZXIiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiIikgKw0KICAgIGxhYnModGl0bGUgPSAiTGFuZCBDb3ZlciBUeXBlcywgMjAxMSIsDQogICAgICAgICBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkcyIpICsNCiAgIHRoZW1lX3ZvaWQoKQ0KYGBgDQoNCg0KUHJlZGljdGFibHksIHRoZSBtYWpvcml0eSBvZiBuZXcgZGV2ZWxvcG1lbnQgaGFzIG9jY3VycmVkIGluIGZvcmVzdHMgYW5kIGZhcm1sYW5kLiBJdCBpcyBub3RhYmxlIHRoYXQgdGhlcmUgaGFzIGJlZW4gZGV2ZWxvcG1lbnQgaW4gYXJlYXMgY2xhc3NpZmllZCBhcyB3YXRlciwgd2hpY2ggaXMgbGlrZWx5IGVtZXJnZW50IHdldGxhbmRzIHRoYXQgbWF5IG5vdCBiZSB3ZXQgdGhyb3VnaG91dCB0aGUgZW50aXJlIHllYXIgYW5kIGNvdWxkIHRoZXJlZm9yZSBtb3JlIGVhc2lseSBiZSBmaWxsZWQgaW4gYW5kIGJ1aWx0IHVwb24uIFRoZXJlIHdhcyBvbmx5IG1hcmdpbmFsIGRldmVsb3BtZW50IGluIGNsYXNzaWZpZWQgd2V0bGFuZHMgYXJlYXMgYW5kICJvdGhlciBVbmRldmVsb3BlZCIgbGFuZHMuDQoNCiMgNi4gV3JhbmdsZSBDZW5zdXMgRGF0YSBhbmQgSm9pbiBUbyB0aGUgRmlzaG5ldA0KDQojIyA2LjEuIERvd25sb2FkaW5nIENlbnN1cyBEYXRhIHZpYSBBUEkNCg0KYGBge3IgQ2Vuc3VzIERhdGEgdmlhIEFQSSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHJlc3VsdHMgPSAiaGlkZSIsIGNhY2hlPVRSVUV9DQoNCmFjc192YXJzIDwtIGMoIkIwMjAwMV8wMDFFIikgIA0KDQpjaGFybG90dGVfbXNhX2NvdW50aWVzIDwtIGxpc3QoDQogIE5DID0gYygiTWVja2xlbmJ1cmciLCAiQ2FiYXJydXMiLCAiR2FzdG9uIiwgIklyZWRlbGwiLCAiTGluY29sbiIsIA0KICAgICAgICAgIlJvd2FuIiwgIlVuaW9uIiwgIkFuc29uIiksDQogIFNDID0gYygiMDkxIiwgIjAyMyIsICIwNTciKSAgIyBZb3JrLCBDaGVzdGVyLCBMYW5jYXN0ZXIgKEZJUFMpDQopDQoNCg0KY2hhclBvcDExIDwtIG1hcF9kZnIobmFtZXMoY2hhcmxvdHRlX21zYV9jb3VudGllcyksIGZ1bmN0aW9uKHN0KSB7DQogIGdldF9hY3MoDQogICAgZ2VvZ3JhcGh5ID0gInRyYWN0IiwNCiAgICB2YXJpYWJsZXMgPSBhY3NfdmFycywNCiAgICB5ZWFyID0gMjAxMSwNCiAgICBzdGF0ZSA9IHN0LA0KICAgIGNvdW50eSA9IGNoYXJsb3R0ZV9tc2FfY291bnRpZXNbW3N0XV0sDQogICAgZ2VvbWV0cnkgPSBUUlVFLA0KICAgIG91dHB1dCA9ICJ3aWRlIg0KICApDQp9KSAlPiUNCiAgZHBseXI6OnNlbGVjdChHRU9JRCwgTkFNRSwgYWxsX29mKGFjc192YXJzKSkgJT4lDQogIHJlbmFtZShwb3BfMjAxMSA9IEIwMjAwMV8wMDFFKSAlPiUNCiAgc3RfdHJhbnNmb3JtKHN0X2Nycyhtc2FfZmlzaG5ldCkpICU+JQ0KICBzdF9idWZmZXIoLTEpDQoNCmBgYA0KDQpXZSBkbyB0aGlzIG9uY2UgbW9yZSBmb3IgYHQyYCwgaW4gdGhpcyBjYXNlLCAyMDE5Lg0KDQoNCmBgYHtyIENlbnN1cyBEYXRhIE1hcHBpbmcsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCByZXN1bHRzID0gImhpZGUiLCBjYWNoZT1UUlVFfQ0KDQpjaGFyUG9wMTkgPC0gbWFwX2RmcihuYW1lcyhjaGFybG90dGVfbXNhX2NvdW50aWVzKSwgZnVuY3Rpb24oc3QpIHsNCiAgZ2V0X2FjcygNCiAgICBnZW9ncmFwaHkgPSAidHJhY3QiLA0KICAgIHZhcmlhYmxlcyA9IGFjc192YXJzLA0KICAgIHllYXIgPSAyMDE5LA0KICAgIHN0YXRlID0gc3QsDQogICAgY291bnR5ID0gY2hhcmxvdHRlX21zYV9jb3VudGllc1tbc3RdXSwNCiAgICBnZW9tZXRyeSA9IFRSVUUsDQogICAgb3V0cHV0ID0gIndpZGUiDQogICkNCn0pICU+JQ0KICBkcGx5cjo6c2VsZWN0KEdFT0lELCBOQU1FLCBhbGxfb2YoYWNzX3ZhcnMpKSAlPiUNCiAgcmVuYW1lKHBvcF8yMDE5ID0gQjAyMDAxXzAwMUUpICU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKG1zYV9maXNobmV0KSkgJT4lDQogIHN0X2J1ZmZlcigtMSkNCg0KYGBgDQoNClRoZW4gd2UgY2FuIHBsb3Qgb3VyIGRhdGEgYW5kIHNlZSB0aGUgc3BhdGlhbCBhcnJhbmdlbWVudCBvZiBwb3B1bGF0aW9uIGluIG91ciBzdHVkeSBhcmVhIGluIHQxICgyMDExKSBhbmQgdDIgKDIwMTkpLg0KDQpPbmUgdGhpbmcgd29ydGggbm90aW5nIGhlcmUgaXMgdGhhdCB0aGUgcXVhbnRpbGUgc3ltYm9sb2d5IHRoYXQgd2UgYXBwbHkgcHJvYmFibHkgY2hhbmdlcyBmcm9tIHQxIHRvIHQyIC0gc28geW91IGNhbiBzZWUgdGhhdCwgb24gYXZlcmFnZSwgdGhlcmUgd2FzIGRlbnNpZmljYXRpb24gYWNyb3NzIHRoZSBtZXRybyBhcmVhIG9uIHRoaXMgdGltZSBpbnRlcnZhbCBiZWNhdXNlIHRoZSBxdWFudGlsZSByYW5nZXMgc2hpZnQgdXB3YXJkcy4NCg0KPGRpdiBjbGFzcz0ic3VwZXJiaWdpbWFnZSI+DQoNCmBgYHtyIENlbnN1cyBEYXRhIE1hcHBpbmcgT25lICwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy5oZWlnaHQ9IDgsIGZpZy53aWR0aD0gMTEsIGNhY2hlPVRSVUV9DQpncmlkLmFycmFuZ2UoDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IGNoYXJQb3AxMSwgYWVzKGZpbGw9ZmFjdG9yKG50aWxlKHBvcF8yMDExLDUpKSksIGNvbG91cj1OQSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXF1aW50aWxlQnJlYWtzKGNoYXJQb3AxMSwicG9wXzIwMTEiKSwNCiAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZVxuQnJlYWtzIikgKw0KICBsYWJzKHRpdGxlPSJQb3B1bGF0aW9uLCBDaGFybG90dGXigJNDb25jb3Jk4oCTR2FzdG9uaWEtIE1TQTogMjAxMSIpICsNCiAgdGhlbWVfdm9pZCgpLA0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IGNoYXJQb3AxOSwgYWVzKGZpbGw9ZmFjdG9yKG50aWxlKHBvcF8yMDE5LDUpKSksIGNvbG91cj1OQSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXF1aW50aWxlQnJlYWtzKGNoYXJQb3AxOSwicG9wXzIwMTkiKSwNCiAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZVxuQnJlYWtzIikgKw0KICBsYWJzKHRpdGxlPSJQb3B1bGF0aW9uLCBDaGFybG90dGXigJNDb25jb3Jk4oCTR2FzdG9uaWEgTVNBOiAyMDE5IikgKw0KICB0aGVtZV92b2lkKCksIG5jb2w9MikNCmBgYA0KDQojIyA2LjIuIEFnZ3JlZ2F0ZSBDZW5zdXMgRGF0YSB3aXRoIEFlcmlhbGx5IFdlaWdodGVkIEludGVycG9sYXRpb24NCg0KSGVyZSB3ZSB1c2UgQXJlYWwgV2VpZ2h0ZWQgSW50ZXJwcmV0YXRpb24gKEFXSSkgdG8gZGlzdHJpYnV0ZSB0aGUgcG9wdWxhdGlvbiB3aXRoaW4gZWFjaCB0cmFjdCBhY3Jvc3MgdGhlIGZpc2huZXQgZ3JpZCBjZWxsczsgdGhpcyBtZXRob2QgYXNzdW1lcyB0aGF0IHBvcHVsYXRpb24gaXMgZGlzdHJpYnV0ZWQgZXZlbmx5IGFjcm9zcyB0aGUgdHJhY3QsIHdoaWNoIGlzIHJlYXNvbmFibGUgaGVyZSBiZWNhdXNlIHdlIGFyZSBub3QgbWVhc3VyaW5nIHBvcHVsYXRpb24gYXMgYW4gb3V0Y29tZSB0aGF0IHJlcXVpcmVzIG1lYW5pbmdmdWwgcHJlY2lzaW9uIGluIHRoaXMgYW5hbHlzaXMuIFRoZSBvdXRwdXQgaXMgYSBuZXcgZmlzaG5ldCBmb3IgZWFjaCB5ZWFyIHRoYXQgaW5jbHVkZXMgYSBwb3B1bGF0aW9uIGVzdGltYXRlIHdpdGhpbiBlYWNoIHRyYWN0Lg0KDQoNCmBgYHtyIEFlcmlhbGx5IFdlaWdodGVkIEludGVycG9sYXRpb24sIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBjYWNoZT1UUlVFfQ0KZmlzaG5ldFBvcHVsYXRpb24xMSA8LQ0KICBzdF9pbnRlcnBvbGF0ZV9hdyhjaGFyUG9wMTFbInBvcF8yMDExIl0sIA0KICAgICAgICAgICAgICAgICAgICBtc2FfZmlzaG5ldCwgDQogICAgICAgICAgICAgICAgICAgIGV4dGVuc2l2ZT1UUlVFKSAlPiUNCiAgc3RfY2VudHJvaWQoKSAlPiUNCiAgc3Rfam9pbihtc2FfZmlzaG5ldCwgLiwgam9pbiA9IHN0X2ludGVyc2VjdHMpICU+JQ0KICBtdXRhdGUocG9wXzIwMTEgPSByZXBsYWNlX25hKHBvcF8yMDExLDApKSAlPiUNCiAgZHBseXI6OnNlbGVjdChwb3BfMjAxMSkNCmBgYA0KDQpXZSByZXBlYXQgdGhlIHByb2Nlc3MgZm9yIG91ciBgdDJgIGRhdGEuDQoNCg0KYGBge3IgQWVyaWFsbHkgV2VpZ2h0ZWQgSW50ZXJwb2xhdGlvbiBPbmUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBjYWNoZT1UUlVFfQ0KDQpmaXNobmV0UG9wdWxhdGlvbjE5IDwtDQogIHN0X2ludGVycG9sYXRlX2F3KGNoYXJQb3AxOVsicG9wXzIwMTkiXSwNCiAgICAgICAgICAgICAgICAgICAgbXNhX2Zpc2huZXQsIA0KICAgICAgICAgICAgICAgICAgICBleHRlbnNpdmU9VFJVRSkgJT4lDQogIHN0X2NlbnRyb2lkKCkgJT4lDQogIHN0X2pvaW4obXNhX2Zpc2huZXQsIC4sIGpvaW4gPSBzdF9pbnRlcnNlY3RzKSAlPiUNCiAgbXV0YXRlKHBvcF8yMDE5ID0gcmVwbGFjZV9uYShwb3BfMjAxOSwwKSkgJT4lDQogIGRwbHlyOjpzZWxlY3QocG9wXzIwMTkpDQpgYGANCg0KSG93IGRvIHRoZXNlIGRhdGEgbG9vayB3aGVuIHdlIG1hcCB0aGVtIG91dCBhbmQgY29tcGFyZSB0aGVtIHRvIHRoZSBjZW5zdXMgdmVjdG9ycz8gV2UgY2FuIHNlZSB0aGF0IHRoZSBtb2RpZmlhYmxlIGFlcmlhbCB1bml0IGlzc3VlIC0gd2hlcmUgc21hbGxlciB0cmFjdHMgYXJlIGhpZ2hlciBkZW5zaXR5IGJlY2F1c2Ugb2YgdGhlIGxvZ2ljIG9mIHRyYWN0LWRyYXdpbmcgLSBubyBsb25nZXIgZGlzdG9ydHMgdGhlIHBpY3R1cmUgLSB3ZSBzZWUgaW5jcmVhc2VkIGRlbnNpdHkgaW4gdGhlIGNlbnRlciwgd2hlcmUgd2UgZXhwZWN0IGl0Lg0KDQo8ZGl2IGNsYXNzPSJzdXBlcmJpZ2ltYWdlIj4NCmBgYHtyIEFlcmlhbGx5IFdlaWdodGVkIEludGVycG9sYXRpb24gdHdvLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodCA9IDgsIGZpZy53aWR0aD0gMTEsIGNhY2hlPVRSVUV9DQoNCmdyaWQuYXJyYW5nZSgNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhPWNoYXJQb3AxMSwgYWVzKGZpbGw9ZmFjdG9yKG50aWxlKHBvcF8yMDExLDUpKSksY29sb3VyPU5BKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGU1LA0KICAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKGNoYXJQb3AxMSwicG9wXzIwMTEiKSwxLDQpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlXG5CcmVha3MiKSArDQogIGxhYnModGl0bGU9IlBvcHVsYXRpb24sIENoYXJsb3R0ZeKAk0NvbmNvcmTigJNHYXN0b25pYSBNU0E6IDIwMTEiLA0KICAgICAgIHN1YnRpdGxlPSJSZXByZXNlbnRlZCBhcyB0cmFjdHM7IEJvdW5kYXJpZXMgb21pdHRlZCIpICsNCiAgdGhlbWVfdm9pZCgpLA0KDQogIGdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhPWZpc2huZXRQb3B1bGF0aW9uMTEsIA0KICAgICAgICAgYWVzKGZpbGw9ZmFjdG9yKG50aWxlKHBvcF8yMDExLDUpKSksY29sb3VyPU5BKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGU1LA0KICAgICAgICAgICAgICAgICAgIGxhYmVscz1zdWJzdHIocXVpbnRpbGVCcmVha3MoZmlzaG5ldFBvcHVsYXRpb24xMSwicG9wXzIwMTEiKSwxLDQpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlXG5CcmVha3MiKSArDQogIGxhYnModGl0bGU9IlBvcHVsYXRpb24sIENoYXJsb3R0ZeKAk0NvbmNvcmTigJNHYXN0b25pYSBNU0E6IDIwMTEiLA0KICAgICAgIHN1YnRpdGxlPSJSZXByZXNlbnRlZCBhcyBmaXNobmV0IGdyaWRjZWxsczsgQm91bmRhcmllcyBvbWl0dGVkIikgKw0KICB0aGVtZV92b2lkKCksIG5jb2w9MikNCg0KZ3JpZC5hcnJhbmdlKA0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGE9Y2hhclBvcDE5LCBhZXMoZmlsbD1mYWN0b3IobnRpbGUocG9wXzIwMTksNSkpKSxjb2xvdXI9TkEpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsDQogICAgICAgICAgICAgICAgICAgIGxhYmVscz1zdWJzdHIocXVpbnRpbGVCcmVha3MoY2hhclBvcDE5LCJwb3BfMjAxOSIpLDEsNCksDQogICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGVcbkJyZWFrcyIpICsNCiAgbGFicyh0aXRsZT0iUG9wdWxhdGlvbiwgQ2hhcmxvdHRl4oCTQ29uY29yZOKAk0dhc3RvbmlhIE1TQTogMjAxOSIsDQogICAgICAgc3VidGl0bGU9IlJlcHJlc2VudGVkIGFzIHRyYWN0czsgQm91bmRhcmllcyBvbWl0dGVkIikgKw0KICB0aGVtZV92b2lkKCksDQoNCiAgZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGE9ZmlzaG5ldFBvcHVsYXRpb24xOSwgDQogICAgICAgICBhZXMoZmlsbD1mYWN0b3IobnRpbGUocG9wXzIwMTksNSkpKSxjb2xvdXI9TkEpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsDQogICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhmaXNobmV0UG9wdWxhdGlvbjE5LCJwb3BfMjAxOSIpLDEsNCksDQogICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGVcbkJyZWFrcyIpICsNCiAgbGFicyh0aXRsZT0iUG9wdWxhdGlvbiwgQ2hhcmxvdHRl4oCTQ29uY29yZOKAk0dhc3RvbmlhIE1TQTogMjAxOSIsDQogICAgICAgc3VidGl0bGU9IlJlcHJlc2VudGVkIGFzIGZpc2huZXQgZ3JpZGNlbGxzOyBCb3VuZGFyaWVzIG9taXR0ZWQiKSArDQogIHRoZW1lX3ZvaWQoKSwgbmNvbD0yKQ0KDQpgYGANCjwvZGl2Pg0KDQojIDcuIFRyYW5zcG9ydGF0aW9uIEFuZCBJbmZyYXN0cnVjdHVyZQ0KDQpUcmFuc3BvcnRhdGlvbiBhY2Nlc3MgaW4gdGhlIENoYXJsb3R0ZSBNU0EgaXMgY3JpdGljYWwgZm9yIHRoZSBncm93aW5nIGVjb25vbXksIHdoaWNoIGluY2x1ZGVzIGEgcm9idXN0IG1hbnVmYWN0dXJpbmcgc2VjdG9yIGluIGFkZGl0aW9uIHRvIGZpbmFuY2lhbCwgdGVjaCwgYW5kIHNlcnZpY2UtYmFzZWQgZW1wbG95ZXJzLiBBdXRvbW9iaWxlcyBhcmUgdGhlIHByaW1hcnkgbWV0aG9kIG9mIHRyYW5zcG9ydGF0aW9uIGZvciB0aGUgbWFqb3JpdHkgb2YgcmVzaWRlbnRzLCBidXQgTWVja2xlbmJ1cmcgQ291bnR5IGFsc28gb3BlcmF0ZXMgYSB0cmFuc2l0IHN5c3RlbSB0aGF0IGluY2x1ZGVzIHR3byBsaWdodCByYWlsIGxpbmVzIGFuZCBhIGJ1cyBzeXN0ZW0uIA0KDQojIyA3LjEuIERvd25sb2FkIEhpZ2h3YXlzDQoNCkZpcnN0IGhpZ2h3YXkgdmVjdG9ycyAoYHByaW1hcnlfcm9hZHNgKSBhcmUgZG93bmxvYWRlZCBmcm9tIHRoZSBgVGlncmlzYCBwYWNrYWdlIC0gd2UgcHJvamVjdCB0aGUgZGF0YSBhbmQgc3Vic2V0IGl0IHRvIHRoZSBzdHVkeSBhcmVhIHVzaW5nIGBzdF9pbnRlcnNlY3Rpb25gLg0KDQpgYGB7ciBEb3dubG9hZCBIaWdod2F5cywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHJlc3VsdHMgPSAiaGlkZSIsIGNhY2hlPVRSVUV9DQoNCmNoYXJIaWdod2F5cyA8LSBiaW5kX3Jvd3MoDQogIHByaW1hcnlfc2Vjb25kYXJ5X3JvYWRzKHN0YXRlID0gIk5DIiksDQogIHByaW1hcnlfc2Vjb25kYXJ5X3JvYWRzKHN0YXRlID0gIlNDIikNCikgJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnMobXNhX2JvdW5kYXJ5KSkgJT4lICAjIE1hdGNoIHRvIE1TQSBDUlMNCiAgc3RfaW50ZXJzZWN0aW9uKG1zYV9ib3VuZGFyeSkgJT4lICAgICAgICMgQ2xpcCB0byBNU0ENCiAgc3RfdHJhbnNmb3JtKHN0X2NycyhmaXNobmV0KSkgICAgICAgICAgICMgTWF0Y2ggQ1JTIHRvIGZpc2huZXQNCg0KYGBgDQoNCkxldCdzIG1ha2UgYSBtYXAgYW5kIGV4YW1pbmUgdGhlIHNwYXRpYWwgcmVsYXRpb25zaGlwIGJldHdlZW4gaGlnaHdheXMgYW5kIGRldmVsb3BtZW50Lg0KDQpgYGB7ciBwbG90X2hpZ2h3YXksIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT0gRkFMU0UsIGNhY2hlPVRSVUV9DQpnZ3Bsb3QoKSArDQogIGdlb21fcG9pbnQoZGF0YT1maXNobmV0LCANCiAgICAgICAgICAgICBhZXMoeD14eUMoZmlzaG5ldClbLDFdLCB5PXh5QyhmaXNobmV0KVssMl0sY29sb3VyPWRldmVsb3BtZW50X2NoYW5nZSksc2l6ZT0xLjUpICsNCiAgZ2VvbV9zZihkYXRhPWNoYXJIaWdod2F5cykgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSkgKw0KICBsYWJzKHRpdGxlID0gIk5ldyBEZXZlbG9wbWVudCBhbmQgSGlnaHdheXMiLA0KICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzIikgKw0KICB0aGVtZV92b2lkKCkNCmBgYA0KDQojIyA3LjIuIENhbGN1YXRlIERpc3RhbmNlIHRvIEhpZ2h3YXlzDQoNCkhlcmUsIHdlIHB1bGwgaGlnaHdheSBkYXRhIGZyb20gMjAxMS4NCg0KYGBge3IgRGlzdGFuY2UgdG8gSGlnaHdheXMsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBjYWNoZT1UUlVFfQ0KaGlnaHdheVBvaW50c19maXNobmV0XzIwMTEgPC0gbXNhX2Zpc2huZXQgJT4lDQogIHN0X2NlbnRyb2lkKCkgJT4lDQogIG11dGF0ZShkaXN0YW5jZV9oaWdod2F5c18yMDExID0gYXMubnVtZXJpYyhzdF9kaXN0YW5jZSguLCBzdF91bmlvbihjaGFySGlnaHdheXMpICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0X3RyYW5zZm9ybShzdF9jcnMobXNhX2Zpc2huZXQpKSkpKSAlPiUNCiAgYXMuZGF0YS5mcmFtZSgpICU+JSANCiAgZHBseXI6OnNlbGVjdCgtZ2VvbWV0cnkpICU+JSANCiAgbGVmdF9qb2luKG1zYV9maXNobmV0LCAuKSAlPiUgDQogIHN0X2FzX3NmKCkNCg0KaGlnaHdheVBvaW50c19maXNobmV0XzIwMTkgPC0gaGlnaHdheVBvaW50c19maXNobmV0XzIwMTEgJT4lDQogIHJlbmFtZShkaXN0YW5jZV9oaWdod2F5c18yMDE5ID0gZGlzdGFuY2VfaGlnaHdheXNfMjAxMSkNCg0KYGBgDQoNCmBgYHtyIERpc3RhbmNlIHRvIEhpZ2h3YXlzIG9uZSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhPW1zYV9ib3VuZGFyeSAlPiUgc3RfdHJhbnNmb3JtKHN0X2NycyhoaWdod2F5UG9pbnRzX2Zpc2huZXRfMjAxMSkpKSArDQogIGdlb21fcG9pbnQoZGF0YT1oaWdod2F5UG9pbnRzX2Zpc2huZXRfMjAxMSwgYWVzKHg9eHlDKGhpZ2h3YXlQb2ludHNfZmlzaG5ldF8yMDExKVssMV0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeT14eUMoaGlnaHdheVBvaW50c19maXNobmV0XzIwMTEpWywyXSwgDQogICAgICAgICAgICAgICAgIGNvbG91cj1mYWN0b3IobnRpbGUoZGlzdGFuY2VfaGlnaHdheXNfMjAxMSw1KSkpLHNpemU9MS41KSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhoaWdod2F5UG9pbnRzX2Zpc2huZXRfMjAxMSwiZGlzdGFuY2VfaGlnaHdheXNfMjAxMSIpLDEsOCksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGVcbkJyZWFrcyIpICsNCiAgZ2VvbV9zZihkYXRhPWNoYXJIaWdod2F5cywgY29sb3VyID0gInJlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0YW5jZSB0byBIaWdod2F5cyAobSkiLA0KICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzOyBIaWdod2F5cyB2aXN1YWxpemVkIGluIHJlZCIpICsNCiAgdGhlbWVfdm9pZCgpDQpgYGANCg0KIyA4LiBDYWxjdWxhdGUgc3BhdGlhbCBsYWcgb2YgZGV2ZWxvcG1lbnQNCg0KSW4gdGhpcyBzdGVwLCB3ZSBjYWxjdWxhdGUgdGhlIGF2ZXJhZ2UgZGlzdGFuY2UgdG8gZWFjaCBncmlkIGNlbGwncyAyIG5lYXJlc3QgZGV2ZWxvcGVkIGdyaWQgY2VsbHMgaW4gMjAxMSB1c2luZyBhIG5ldyB2YXJpYWJsZSBjcmVhdGVkIGJ5IG91ciBrLW5lYXJlc3QtbmVpZ2hib3JzIGN1c3RvbSBmdW5jdGlvbiAoYG5uX2Z1bmN0aW9uYCkgZm9yIHNwYXRpYWwgbGFnLCBgbGFnRGV2ZWxvcG1lbnRfMjAxMWAgYW5kIGBsYWdEZXZlbG9wbWVudF8yMDE5YC4gDQoNCmBgYHtyIHNwYXRpYWwgbGFnLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgY2FjaGU9VFJVRX0NCmZpc2huZXQkbGFnRGV2ZWxvcG1lbnRfMjAxMSA8LQ0KICAgIG5uX2Z1bmN0aW9uKGZpc2huZXQgJT4lDQogICAgICAgICAgICAgICAgICBzdF9jZW50cm9pZCgpICU+JQ0KICAgICAgICAgICAgICAgICAgc3RfY29vcmRpbmF0ZXMoKSAlPiUNCiAgICAgICAgICAgICAgICAgIGFzLmRhdGEuZnJhbWUoKSwNCiAgICAgICAgICAgICAgICBsY1Jhc3RlcnNfMjAxMSAlPiUNCiAgICAgICAgICAgICAgICAgIGZpbHRlcihkZXZlbG9wZWRfMjAxMSA9PSAxKSAlPiUNCiAgICAgICAgICAgICAgICAgIHN0X2NlbnRyb2lkKCkgJT4lDQogICAgICAgICAgICAgICAgICBzdF9jb29yZGluYXRlcygpICU+JQ0KICAgICAgICAgICAgICAgICAgYXMuZGF0YS5mcmFtZSgpLA0KICAgICAgICAgICAgICAgIDIpDQoNCmZpc2huZXQkbGFnRGV2ZWxvcG1lbnRfMjAxOSA8LQ0KICAgIG5uX2Z1bmN0aW9uKGZpc2huZXQgJT4lDQogICAgICAgICAgICAgICAgICBzdF9jZW50cm9pZCgpICU+JQ0KICAgICAgICAgICAgICAgICAgc3RfY29vcmRpbmF0ZXMoKSAlPiUNCiAgICAgICAgICAgICAgICAgIGFzLmRhdGEuZnJhbWUoKSwNCiAgICAgICAgICAgICAgICBsY1Jhc3RlcnNfMjAxOSAlPiUNCiAgICAgICAgICAgICAgICAgIGZpbHRlcihkZXZlbG9wZWRfMjAxOSA9PSAxKSAlPiUNCiAgICAgICAgICAgICAgICAgIHN0X2NlbnRyb2lkKCkgJT4lDQogICAgICAgICAgICAgICAgICBzdF9jb29yZGluYXRlcygpICU+JQ0KICAgICAgICAgICAgICAgICAgYXMuZGF0YS5mcmFtZSgpLA0KICAgICAgICAgICAgICAgIDIpDQpgYGANCg0KYGBge3Igc3BhdGlhbCBsYWcgUGxvdCwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YT1tc2FfYm91bmRhcnkgJT4lIHN0X3RyYW5zZm9ybShzdF9jcnMoZmlzaG5ldCkpKSArDQogIGdlb21fcG9pbnQoZGF0YT1maXNobmV0LCANCiAgICAgICAgICAgICBhZXMoeD14eUMoZmlzaG5ldClbLDFdLCB5PXh5QyhmaXNobmV0KVssMl0sIA0KICAgICAgICAgICAgICAgICBjb2xvdXI9ZmFjdG9yKG50aWxlKGxhZ0RldmVsb3BtZW50XzIwMTEsNSkpKSwgc2l6ZT0xLjUpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwNCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1zdWJzdHIocXVpbnRpbGVCcmVha3MoZmlzaG5ldCwibGFnRGV2ZWxvcG1lbnRfMjAxMSIpLDEsNyksDQogICAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZVxuQnJlYWtzIikgKw0KICBsYWJzKHRpdGxlID0gIlNwYXRpYWwgTGFnIHRvIDIwMTEgRGV2ZWxvcG1lbnQsIG0iLA0KICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzIikgKw0KICB0aGVtZV92b2lkKCkNCmBgYA0KDQoNCiMgOS4gUG9saXRpY2FsIEJvdW5kYXJpZXMNCg0KVGhlIE1TQSBpbmNsdWRlcyBNZWNrbGVuYnVyZywgQ2FiYXJydXMsIEdhc3RvbiwgSXJlZGVsbCwgTGluY29sbiwgUm93YW4sIFVuaW9uLCBhbmQgQW5zb24gQ291bnRpZXMgaW4gTkMgYW5kIFlvcmssIENoZXN0ZXIsIGFuZCBMYW5jYXN0ZXIgY291bnRpZXMgaW4gU0MuIA0KDQoNCmBgYHtyIFBvbGl0aWNhbCBCb3VuZGFyaWVzLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cyA9ICJoaWRlIiwgY2FjaGU9VFJVRX0NCg0Kb3B0aW9ucyh0aWdyaXNfY2xhc3MgPSAic2YiKQ0KDQpuY19jb3VudGllcyA8LSBjb3VudGllcyhzdGF0ZSA9ICJOQyIpICU+JSBzdF9hc19zZigpDQpzY19jb3VudGllcyA8LSBjb3VudGllcyhzdGF0ZSA9ICJTQyIpICU+JSBzdF9hc19zZigpDQoNCg0KYWxsX2NvdW50aWVzIDwtIGJpbmRfcm93cyhuY19jb3VudGllcywgc2NfY291bnRpZXMpICU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKG1zYV9maXNobmV0KSkNCg0KDQpzdHVkeUFyZWFDb3VudGllcyA8LSBhbGxfY291bnRpZXMgJT4lDQogIGZpbHRlcigNCiAgICAoU1RBVEVGUCA9PSAiMzciICYgTkFNRSAlaW4lIGMoIk1lY2tsZW5idXJnIiwgIkNhYmFycnVzIiwgIkdhc3RvbiIsICJJcmVkZWxsIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMaW5jb2xuIiwgIlJvd2FuIiwgIlVuaW9uIiwgIkFuc29uIikpIHwNCiAgICAoU1RBVEVGUCA9PSAiNDUiICYgQ09VTlRZRlAgJWluJSBjKCIwOTEiLCAiMDIzIiwgIjA1NyIpKSAgIyBZb3JrLCBDaGVzdGVyLCBMYW5jYXN0ZXINCiAgKQ0KDQpgYGANCg0KV2Ugc3BhdGlhbGx5IGpvaW4gdGhlIGBzdHVkeUFyZWFDb3VudGllc2Agb2JqZWN0IHRvIG91ciBmaXNobmV0IGFuZCBjcmVhdGUgYSBgY291bnR5RmlzaG5ldGAgd2hlcmUgZWFjaCBjZWxsIGlzIGltcGFydGVkIHdpdGggdGhlIG5hbWUgb2YgdGhlIGNvdW50eSBpdCBiZWxvbmdzIHRvLg0KDQpgYGB7ciBQb2xpdGljYWwgQm91bmRhcmllcyBqb2luLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgY2FjaGU9VFJVRX0NCmNvdW50eUZpc2huZXQgPC0gbXNhX2Zpc2huZXQgJT4lDQogIHN0X2pvaW4oLiwgc3R1ZHlBcmVhQ291bnRpZXMgJT4lDQogICAgICAgICAgICBkcGx5cjo6c2VsZWN0KE5BTUUpKSAlPiUNCiAgYXMuZGF0YS5mcmFtZSgpICU+JSANCiAgZHBseXI6OnNlbGVjdCh1bmlxdWVJRCwgTkFNRSkgJT4lIA0KICBsZWZ0X2pvaW4obXNhX2Zpc2huZXQsIC4pICU+JSANCiAgc3RfYXNfc2YoKSAlPiUNCiAgZ3JvdXBfYnkodW5pcXVlSUQpICU+JQ0KICBzbGljZSgxKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBhcnJhbmdlKGFzLm51bWVyaWModW5pcXVlSUQpKQ0KICANCmBgYA0KDQojIDEwLiBDcmVhdGluZyBPdXIgRGF0YSBTZXQgZm9yIE1vZGVsaW5nDQoNCkhlcmUgd2UgY29tcGlsZSBvdXIgZGF0YSB0byBjcmVhdGUgYSBtb2RlbCB3aGljaCB3ZSBjYW4gdHJhaW4gZm9yIGEgZnV0dXJlIHNjZW5hcmlvIHByb2plY3Rpb24uIFRoaXMgaW5jbHVkZXM6DQoNCkZvciB0MSAoMjAxMSk6DQotIGZpc2huZXQNCi0gaGlnaHdheVBvaW50c19maXNobmV0XzIwMTENCi0gZmlzaG5ldFBvcHVsYXRpb24xMQ0KLSBsY1Jhc3RlcnNfMjAxMQ0KLSBjb3VudHlGaXNobmV0DQoNCkZvciB0MiAoMjAxOSk6DQotIGZpc2huZXQNCi0gaGlnaHdheVBvaW50c19maXNobmV0XzIwMTkNCi0gZmlzaG5ldFBvcHVsYXRpb24xOQ0KLSBsY1Jhc3RlcnNfMjAxOQ0KLSBjb3VudHlGaXNobmV0DQoNCg0KYGBge3IgRGF0YSBTZXQgZm9yIE1vZGVsaW5nLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgY2FjaGU9VFJVRX0NCg0KZGF0XzIwMTEgPC0gDQogIGNiaW5kKCBmaXNobmV0LCBoaWdod2F5UG9pbnRzX2Zpc2huZXRfMjAxMSwgZmlzaG5ldFBvcHVsYXRpb24xMSwgbGNSYXN0ZXJzXzIwMTEsIGNvdW50eUZpc2huZXQpICU+JQ0KICBhcy5kYXRhLmZyYW1lKCkgJT4lDQogIGRwbHlyOjpzZWxlY3QodW5pcXVlSUQsIGRldmVsb3BtZW50X2NoYW5nZSwgbGFnRGV2ZWxvcG1lbnRfMjAxMSwgZGlzdGFuY2VfaGlnaHdheXNfMjAxMSwgcG9wXzIwMTEsICANCiAgICAgICAgICAgICAgICBkZXZlbG9wZWRfMjAxMSwgZm9yZXN0XzIwMTEsIGZhcm1fMjAxMSwgd2V0bGFuZHNfMjAxMSwgb3RoZXJVbmRldmVsb3BlZF8yMDExLCB3YXRlcl8yMDExLA0KICAgICAgICAgICAgICAgIE5BTUUsIGdlb21ldHJ5KSAlPiUNCiAgZmlsdGVyKHdhdGVyXzIwMTEgPT0gMCkgJT4lDQogIHJlbmFtZV93aXRoKH4gc3RyX3JlbW92ZSgueCwgIl8yMDExIikpDQoNCmRhdF8yMDE5IDwtIA0KICBjYmluZCggZmlzaG5ldCwgaGlnaHdheVBvaW50c19maXNobmV0XzIwMTksIGZpc2huZXRQb3B1bGF0aW9uMTksIGxjUmFzdGVyc18yMDE5LCBjb3VudHlGaXNobmV0KSAlPiUNCiAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICBkcGx5cjo6c2VsZWN0KHVuaXF1ZUlELCBkZXZlbG9wbWVudF9jaGFuZ2UsIGxhZ0RldmVsb3BtZW50XzIwMTksIGRpc3RhbmNlX2hpZ2h3YXlzXzIwMTksIHBvcF8yMDE5LCAgDQogICAgICAgICAgICAgICAgZGV2ZWxvcGVkXzIwMTksIGZvcmVzdF8yMDE5LCBmYXJtXzIwMTksIHdldGxhbmRzXzIwMTksIG90aGVyVW5kZXZlbG9wZWRfMjAxOSwgd2F0ZXJfMjAxOSwNCiAgICAgICAgICAgICAgICBOQU1FLCBnZW9tZXRyeSkgJT4lDQogIGZpbHRlcih3YXRlcl8yMDE5ID09IDApICAlPiUNCiAgcmVuYW1lX3dpdGgofiBzdHJfcmVtb3ZlKC54LCAiXzIwMTkiKSkNCiAgDQpgYGANCg0KIyAxMS4gRmVhdHVyZSBFeHBsb3JhdGlvbg0KDQpJbiB0aGlzIHNlY3Rpb24gd2UgZXhwbG9yZSB0aGUgZXh0ZW50IHRvIG91ciBwb3NzaWJsZSBwcmVkaWN0b3JzIChkaXN0YW5jZSB0byBoaWdod2F5cywgc3BhdGlhbCBsYWcgb2YgZGV2ZWxvcG1lbnQsIGFuZCBwb3B1bGF0aW9uIGNoYW5nZSkgYXJlIGFzc29jaWF0ZWQgd2l0aCBkZXZlbG9wbWVudCBjaGFuZ2UuIE9uZSBub3RlOiBJbiBhbiBpbml0aWFsIHJ1biBvZiB0aGUgbW9kZWwsIHdlIGRpZCBzZWUgdGhhdCBwcm94aW1pdHkgdG8gdGhlIEJsdWUgTGluZSB3YXMgYSBzdHJvbmcgcHJlZGljdG9yIG9mIGRldmVsb3BtZW50IGJldHdlZW4gMjAxMSBhbmQgMjAxOSwgYnV0IGRpZCBub3QgaW5jbHVkZSB0aGUgbGluZXMgaW4gb3VyIGluaXRpYWwgbW9kZWwgYWZ0ZXIgYSBjb252ZXJzYXRpb24gd2l0aCBJc2FiZWwuIFRoZSBMeW54IEJsdWUgbGluZSB3YXMgZmlyc3QgYnVpbHQgaW4gMjAwNyBhbmQgZXh0ZW5kZWQgaW4gMjAxOCwgd2hpbGUgdGhlIEdvbGQgTGluZSB3YXMgY29tcGxldGVkIGluIDIwMTUuIEJlY2F1c2UgdGhlIHRyYW5zaXQgaW5mcmFzdHJ1Y3R1cmUgaGFzIG5vdCByZW1haW5lZCBjb25zaXN0ZW50IHRocm91Z2hvdXQgdGhpcyB0aW1lIHBlcmlvZCwgd2UgZGlkIG5vdCBpbmNsdWRlIGl0IGhlcmUuDQoNCmBgYHtyIEZlYXR1cmUgRXhwbG9yYXRpb24sIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBjYWNoZT1UUlVFfQ0KZGF0XzIwMTEgJT4lDQogIGRwbHlyOjpzZWxlY3QoZGlzdGFuY2VfaGlnaHdheXMsbGFnRGV2ZWxvcG1lbnQsZGV2ZWxvcG1lbnRfY2hhbmdlLCBwb3ApICU+JQ0KICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtZGV2ZWxvcG1lbnRfY2hhbmdlKSAlPiUNCiAgZ2dwbG90KC4sIGFlcyhkZXZlbG9wbWVudF9jaGFuZ2UsIFZhbHVlLCBmaWxsPWRldmVsb3BtZW50X2NoYW5nZSkpICsgDQogICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiLCBzdGF0ID0gInN1bW1hcnkiLCBmdW4ueSA9ICJtZWFuIikgKw0KICAgIGZhY2V0X3dyYXAoflZhcmlhYmxlLCBzY2FsZXMgPSAiZnJlZSIpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwNCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiTm8gQ2hhbmdlIiwiTmV3IERldmVsb3BtZW50IiksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZT0iTWVhbiBWYWx1ZSIpICsNCiAgICBsYWJzKHRpdGxlPSJOZXcgRGV2ZWxvcG1lbnQgYXMgYSBGdW5jdGlvbiBvZiBDb250aW51b3VzIFZhcmlhYmxlcyIpICsNCiAgICB0aGVtZV9taW5pbWFsKCkgDQpgYGANCg0KVGhpcyBoaXN0b2dyYW0gc2hvd3MgdGhhdCBvbiBhdmVyYWdlLCBsYW5kIHRoYXQgd2FzIGRldmVsb3BlZCBiZXR3ZWVuIDIwMTEgYW5kIDIwMTkgd2FzIGNsb3NlciB0byBoaWdod2F5cywgY2xvc2VyIHRvIG90aGVyIGRldmVsb3BtZW50LCBhbmQgd2FzIGluIGFyZWFzIHdpdGggaGlnaGVyIHBvcHVsYXRpb25zLCBpbmRpY2F0aW5nIGRlbnNpZmljYXRpb24gb3ZlciBzcHJhd2wuDQoNCldoZW4gd2UgbG9vayBhdCB0aGUgdHlwZSBvZiBsYW5kIGNvbnZlcnRlZCBpbiB0aGlzIHRpbWUgaW50ZXJ2YWwsIHdlIHNlZSBpdCB3YXMgcHJlZG9taW5hdGVseSBmb3Jlc3RlZCBsYW5kLCB3aXRoIHNvbWUgZmFybXMgY29udmVydGVkIGFuZCBvbmx5IG1hcmdpbmFsICJPdGhlciB1bmRldmVsb3BlZCIgbGFuZCBjb252ZXJ0ZWQsIHBlciB0aGUgdGFibGUgYmVsb3cuDQoNCg0KYGBge3IgdHlwZSBvZiBsYW5kIGNvbnZlcnRlZCwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQpkYXRfMjAxMSAlPiUNCiAgZHBseXI6OnNlbGVjdChkZXZlbG9wbWVudF9jaGFuZ2UsIGZvcmVzdCwgZmFybSwgd2V0bGFuZHMsIG90aGVyVW5kZXZlbG9wZWQpICU+JQ0KICBnYXRoZXIoa2V5ID0gIkxhbmRfQ292ZXJfVHlwZSIsIFZhbHVlLCAtZGV2ZWxvcG1lbnRfY2hhbmdlKSAlPiUNCiAgICAgZ3JvdXBfYnkoZGV2ZWxvcG1lbnRfY2hhbmdlLCBMYW5kX0NvdmVyX1R5cGUpICU+JQ0KICAgICBzdW1tYXJpemUobiA9IHN1bShhcy5udW1lcmljKFZhbHVlKSkpICU+JQ0KICAgICB1bmdyb3VwKCkgJT4lDQogICAgbXV0YXRlKENvbnZlcnNpb25fUmF0ZSA9IHBhc3RlMChyb3VuZCgxMDAgKiBuL3N1bShuKSwgMiksICIlIikpICU+JQ0KICAgIGZpbHRlcihkZXZlbG9wbWVudF9jaGFuZ2UgPT0gMSkgJT4lDQogIGRwbHlyOjpzZWxlY3QoTGFuZF9Db3Zlcl9UeXBlLENvbnZlcnNpb25fUmF0ZSkgJT4lDQogIGthYmxlKCkgJT4lIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEYpDQpgYGANCg0KIyAxMi4gTW9kZWxpbmcNCg0KSGVyZSwgd2UgdHJhaW4gYW5kIHRlc3Qgb3VyIG1vZGVsIHdpdGggYW4gZXZlbiA1MCUgb2YgZGF0YSBzcGxpdCBiZXR3ZWVuIHRoZSB0cmFpbmluZyBhbmQgdGVzdCBzZXRzIHRvIGhlbHAga2VlcCBhIGZhaXIgbnVtYmVyIG9mIDFzIGluIG91ciB0cmFpbmluZyBzZXQuDQoNCiMjIDEyLjEuIFNwbGl0dGluZyBvdXIgZGF0YQ0KDQpgYGB7ciBNb2RlbGluZywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQpzZXQuc2VlZCgzNDU2KQ0KdHJhaW5JbmRleCA8LSANCiAgY3JlYXRlRGF0YVBhcnRpdGlvbihkYXRfMjAxMSRvdGhlclVuZGV2ZWxvcGVkLCBwID0gLjUwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lcyA9IDEpDQpkYXRUcmFpbiA8LSBkYXRfMjAxMVsgdHJhaW5JbmRleCxdDQpkYXRUZXN0ICA8LSBkYXRfMjAxMVstdHJhaW5JbmRleCxdDQoNCmBgYA0KDQojIyAxMi4yLiBTcGVjaWZ5aW5nIG1vZGVscw0KDQpXZSBlc3RpbWF0ZSBzaXggc2VwYXJhdGUgYGdsbWAgbW9kZWxzIC0gYWRkaW5nIG5ldyB2YXJpYWJsZXMgZm9yIGVhY2guDQoNCi0gYE1vZGVsMWAgaW5jbHVkZXMgb25seSBwcmV2aW91cyBsYW5kIGNvdmVyIHR5cGVzLiANCg0KLSBgTW9kZWwyYCBhZGRzIHRoZSBgbGFnRGV2ZWxvcG1lbnRgLiANCg0KLSBgTW9kZWwzYCBhZGRzIHBvcHVsYXRpb24NCg0KLSBgTW9kZWw0YCBhZGRzIGEgZml4ZWQgZWZmZWN0IGZvciBjb3VudHkgKGBOQU1FYCkNCg0KLSBgTW9kZWw1YCBhZGRzIHRoZSBkaXN0YW5jZSB0byBoaWdod2F5Lg0KDQotIGBNb2RlbDZgIGlzIGEgbW9kaWZpY2F0aW9uIG9mIGBNb2RlbDVgIHdoaWNoICppbnRlcmFjdHMqIGRpc3RhbmNlIHRvIGhpZ2h3YXkgYW5kIGRldmVsb3BtZW50IGxhZy4gVGhlIGh5cG90aGVzaXMgaGVyZSBpcyB0aGF0IHRoZXNlIHR3byB2YXJpYWJsZXMgYXJlIHJlbGF0ZWQgLSB0aGUgZWZmZWN0IG9mIG9uZSBkZXBlbmRzIG9uIHRoZSBvdGhlci4gZS5nLiBEaXN0YW5jZSB0byBuZWFyZXN0IGRldmVsb3BtZW50IGRlcGVuZHMgaW4gcGFydCBvbiBhY2Nlc3MgdG8gdHJhbnNwb3J0YXRpb24uIE5vdGljZSB0aGF0IHRoZSBlZmZlY3Qgb2YgYm90aCB2YXJpYWJsZXMgaXMgc2lnbmlmaWNhbnQgaW4gdGhpcyBzcGVjaWZpY2F0aW9uLCBidXQgYm90aCBhcmUgbm90IHNpZ25pZmljYW50IGluIGBNb2RlbDVgLg0KIA0KYGBge3IgU3BlY2lmeWluZyBtb2RlbHMsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBjYWNoZT1UUlVFfQ0KTW9kZWwxIDwtIGdsbShkZXZlbG9wbWVudF9jaGFuZ2UgfiB3ZXRsYW5kcyArIGZvcmVzdCAgKyBmYXJtICsgDQogICAgICAgICAgICAgICAgb3RoZXJVbmRldmVsb3BlZCwgDQogICAgICAgICAgICAgIGZhbWlseT0iYmlub21pYWwiKGxpbms9ImxvZ2l0IiksIGRhdGEgPSBkYXRUcmFpbikNCg0KTW9kZWwyIDwtIGdsbShkZXZlbG9wbWVudF9jaGFuZ2UgfiB3ZXRsYW5kcyArIGZvcmVzdCAgKyBmYXJtICsgDQogICAgICAgICAgICAgICAgb3RoZXJVbmRldmVsb3BlZCArIGxhZ0RldmVsb3BtZW50LCANCiAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKQ0KICAgICAgICAgICAgICANCk1vZGVsMyA8LSBnbG0oZGV2ZWxvcG1lbnRfY2hhbmdlIH4gd2V0bGFuZHMgKyBmb3Jlc3QgICsgZmFybSArDQogICAgICAgICAgICAgICAgb3RoZXJVbmRldmVsb3BlZCArIGxhZ0RldmVsb3BtZW50ICsgcG9wLA0KICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pICAgICAgICAgIA0KICAgICAgICAgICAgICANCk1vZGVsNCA8LSBnbG0oZGV2ZWxvcG1lbnRfY2hhbmdlIH4gd2V0bGFuZHMgKyBmb3Jlc3QgICsgZmFybSArIA0KICAgICAgICAgICAgICAgIG90aGVyVW5kZXZlbG9wZWQgKyBsYWdEZXZlbG9wbWVudCArIHBvcCArIE5BTUUsIA0KICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pIA0KDQpNb2RlbDUgPC0gZ2xtKGRldmVsb3BtZW50X2NoYW5nZSB+IHdldGxhbmRzICsgZm9yZXN0ICArIGZhcm0gKyANCiAgICAgICAgICAgICAgICBvdGhlclVuZGV2ZWxvcGVkICsgbGFnRGV2ZWxvcG1lbnQgKyBwb3AgKyBkaXN0YW5jZV9oaWdod2F5cyArIE5BTUUsIA0KICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pIA0KDQpNb2RlbDYgPC0gZ2xtKGRldmVsb3BtZW50X2NoYW5nZSB+IHdldGxhbmRzICsgZm9yZXN0ICArIGZhcm0gKyANCiAgICAgICAgICAgICAgICBvdGhlclVuZGV2ZWxvcGVkICsgcG9wICsgbGFnRGV2ZWxvcG1lbnQgKiBkaXN0YW5jZV9oaWdod2F5cyArIE5BTUUsIA0KICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pDQpgYGANCg0KDQpUaGUgYmVzdC1wZXJmb3JtaW5nIG1vZGVsIHdhcyBNb2RlbDYsIHNvIHRoaXMgaXMgd2hpY2ggd2UgdXNlIGZvciB2YWxpZGF0aW9uIGFuZCBwcmVkaWN0aW9uLg0KDQoNCmBgYHtyIGJlc3QtcGVyZm9ybWluZyBtb2RlbCwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQoNCnN1bW1hcnkoTW9kZWw2KQ0KDQpgYGANCg0KSGVyZSwgd2UgY29tcGFyZSBBSUNzIGZvciBnb29kbmVzcyBvZiBmaXQgKGxvd2VyIGlzIGJldHRlcikuDQoNCmBgYHtyIGNvbXBhcmUgQUlDcywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQpkYXRhLmZyYW1lKA0KICBNb2RlbCA9IGMoIk1vZGVsMSIsICJNb2RlbDIiLCAiTW9kZWwzIiwgIk1vZGVsNCIsICJNb2RlbDUiLCAiTW9kZWw2IiksDQogIEFJQyA9IGMoTW9kZWwxJGFpYywgTW9kZWwyJGFpYywgTW9kZWwzJGFpYywgTW9kZWw0JGFpYywgTW9kZWw1JGFpYywgTW9kZWw2JGFpYykNCikgJT4lDQogIGdncGxvdCgpKw0KICBnZW9tX2JhcihhZXMoeCA9IE1vZGVsLCB5ID0gQUlDKSwgc3RhdCA9ICJpZGVudGl0eSIpKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCiMjIDEyLjMuIFZhbGlkYXRpbmcgb3VyIE1vZGVsIFVzaW5nIHRoZSBUZXN0IFNldA0KDQpXZSBjcmVhdGUgYSBuZXcgZGF0YSBmcmFtZSBgdGVzdFNldFByb2JzYCB0aGF0IGNvbnNpc3RzIG9mIG91ciBgY2xhc3NgIChlLmcuIGRldmVsb3BtZW50X2NoYW5nZSBhcyBhIDEgb3IgMCksIGFuZCBgcHJvYnNgIC0gd2hpY2ggaXMgdGhlIHByZWRpY3Rpb24gZm9yIGVhY2ggb2JzZXJ2YXRpb24gaW4gb3VyIHRlc3Qgc2V0IHVzaW5nIGBNb2RlbDZgLiBUaGUgYHR5cGVgIHBhcmFtZXRlciBpcyBzZXQgdG8gYHJlc3BvbnNlYCwgd2hpY2ggbWVhbnMgb3VyIGBwcm9ic2AgYXJlIG1lYXN1cmVzIG9mIGVzdGltYXRlZCBwcm9iYWJpbGl0eSBmcm9tIDAtMS4NCg0KYGBge3IgVmFsaWRhdGluZyBvdXIgTW9kZWwsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBjYWNoZT1UUlVFfQ0KdGVzdFNldFByb2JzIDwtIA0KICBkYXRhLmZyYW1lKGNsYXNzID0gZGF0VGVzdCRkZXZlbG9wbWVudF9jaGFuZ2UsDQogICAgICAgICAgICAgcHJvYnMgPSBwcmVkaWN0KE1vZGVsNiwgZGF0VGVzdCwgdHlwZT0icmVzcG9uc2UiKSkgDQpgYGANCg0KVGhpcyBkZW5zaXR5IHBsb3QgaXMgYSBrZXkgdG9vbCB0byBmaWd1cmUgb3V0IHdoZXJlIHdlIHdpbGwgc2V0IG91ciB0aHJlc2hvbGQgZm9yIGNsYXNzaWZ5aW5nIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIGFzIDFzIG9yIDBzIChlLmcuIFByZWRpY3RlZCB0byBEZXZlbG9wIG9yIFByZWRpY3RlZCBOb3QgVG8gRGV2ZWxvcCkuDQoNClRha2UgYSBjbG9zZSBsb29rIGF0IHRoaXMgcGxvdCBhbmQgc2VlIGlmIHlvdSBoYXZlIHNvbWUgaWRlYXMgYWJvdXQgd2hhdCB0aHJlc2hvbGQgd291bGQgc2VwYXJhdGUgb3V0IG91ciAxcyBmcm9tIG91ciAwcyBtb3N0IGVmZmVjdGl2ZWx5IHdpdGggYSBtaW5pbXVtIG9mIGVycm9yLg0KDQpgYGB7ciBkZW5zaXR5IHBsb3QsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBjYWNoZT1UUlVFfSAgDQpnZ3Bsb3QodGVzdFNldFByb2JzLCBhZXMocHJvYnMpKSArDQogIGdlb21fZGVuc2l0eShhZXMoZmlsbD1jbGFzcyksIGFscGhhPTAuNSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5ldyBEZXZlbG9wbWVudCIpKSArDQogIGxhYnModGl0bGUgPSAiSGlzdG9ncmFtIG9mIHRlc3Qgc2V0IHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIiwNCiAgICAgICB4PSJQcmVkaWN0ZWQgUHJvYmFiaWxpdGllcyIseT0iRGVuc2l0eSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KDQojIyMgMTIuMy4xLiBDb25mdXNpb24gbWF0cml4DQoNCkhlcmUsIHdlIHNldCAwLjA1IGFzIG91ciBjdXRvZmYgdmFsdWUgLSBhYm92ZSB0aGF0IHZhbHVlLCB3ZSBwcmVkaWN0IHRoYXQgY2VsbCBpcyBhIGAxYCwgYW5kIGJlbG93IGl0LCBhIGAwYC4NCg0KV2UgYnVpbGQgYSBjb25mdXNpb24gbWF0cml4IHRvIHNlZSBob3cgYWNjdXJhdGUgdGhpcyBtb2RlbCBpcyBvdmVyYWxsLCBhbmQgd2hhdCBvdXIgZXJyb3IgcmF0ZXMgYXJlLiBUaGVyZSBhcmUgb3VyIGZvdXIgcG90ZW50aWFsIG91dGNvbWVzOg0KDQotIFRydWUgUG9zaXRpdmU6IFdlIHByZWRpY3RlZCBhIGNlbGwgd291bGQgY29udmVydCBmcm9tIHVuZGV2ZWxvcGVkIHRvIGRldmVsb3BlZCBhbmQgaXQgKmRpZCogY29udmVydC4NCg0KLSBGYWxzZSBQb3NpdGl2ZTogV2UgcHJlZGljdGVkIGEgY2VsbCB3b3VsZCBjb252ZXJ0IGZyb20gdW5kZXZlbG9wZWQgdG8gZGV2ZWxvcGVkIGFuZCBpdCAqZGlkIG5vdCogY29udmVydC4NCg0KLSBUcnVlIE5lZ2F0aXZlOiBXZSBwcmVkaWN0ZWQgYSBjZWxsIHdvdWxkICpub3QqIGNvbnZlcnQgZnJvbSB1bmRldmVsb3BlZCB0byBkZXZlbG9wZWQgYW5kIGl0ICpkaWQgbm90KiBjb252ZXJ0Lg0KDQotIEZhbHNlIE5lZ2F0aXZlOiBXZSBwcmVkaWN0ZWQgYSBjZWxsIHdvdWxkICpub3QqIGNvbnZlcnQgZnJvbSB1bmRldmVsb3BlZCB0byBkZXZlbG9wZWQgYW5kIGl0ICpkaWQqIGNvbnZlcnQuDQoNCg0KYGBge3IgQ29uZnVzaW9uIG1hdHJpeCwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQp0ZXN0U2V0UHJvYnMkcHJlZENsYXNzICA9IGlmZWxzZSh0ZXN0U2V0UHJvYnMkcHJvYnMgPiAuMDUgLDEsMCkNCg0KY2FyZXQ6OmNvbmZ1c2lvbk1hdHJpeChyZWZlcmVuY2UgPSBhcy5mYWN0b3IodGVzdFNldFByb2JzJGNsYXNzKSwgDQogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBhcy5mYWN0b3IodGVzdFNldFByb2JzJHByZWRDbGFzcyksIA0KICAgICAgICAgICAgICAgICAgICAgICBwb3NpdGl2ZSA9ICIxIikNCg0KYGBgDQoqTW9kZWwgQW5hbHlzaXMqDQoNClRydWUgTmVnYXRpdmVzIChUTikgPSA4NjUzDQoNCkZhbHNlIE5lZ2F0aXZlcyAoRk4pID0gNjMNCg0KRmFsc2UgUG9zaXRpdmVzIChGUCkgPSA0MzQNCg0KVHJ1ZSBQb3NpdGl2ZXMgKFRQKSA9IDU4DQoNClRoZSBjb25mdXNpb24gbWF0cml4IGluZGljYXRlcyB0aGF0IHRoZSBtb2RlbCBpcyBoaWdobHkgZWZmZWN0aXZlIGF0IGlkZW50aWZ5aW5nIGNlbGxzIHRoYXQgcmVtYWluIHVuZGV2ZWxvcGVkLCB3aXRoIGEgbGFyZ2UgbnVtYmVyIG9mIHRydWUgbmVnYXRpdmVzLiBIb3dldmVyLCBpdCBzdHJ1Z2dsZXMgdG8gYWNjdXJhdGVseSBkZXRlY3QgYWN0dWFsIGxhbmQgY29udmVyc2lvbnMsIGFzIHJlZmxlY3RlZCBpbiB0aGUgbG93IG51bWJlciBvZiB0cnVlIHBvc2l0aXZlcy4NCg0KVGhlIHNlbnNpdGl2aXR5ICg0Ny45JSksIG9yIHRydWUgcG9zaXRpdmUgcmF0ZSwgc3VnZ2VzdHMgdGhhdCB0aGUgbW9kZWwgY29ycmVjdGx5IGlkZW50aWZpZXMgbGVzcyB0aGFuIGhhbGYgb2YgdGhlIGNlbGxzIHRoYXQgYWN0dWFsbHkgY29udmVydGVkIGZyb20gdW5kZXZlbG9wZWQgdG8gZGV2ZWxvcGVkLiBJbiBjb250cmFzdCwgdGhlIHNwZWNpZmljaXR5ICg5NS4yJSkgc2hvd3MgdGhhdCB0aGUgbW9kZWwgaXMgdmVyeSBnb29kIGF0IGlkZW50aWZ5aW5nIGNlbGxzIHRoYXQgcmVtYWluZWQgdW5kZXZlbG9wZWQuDQoNCldoaWxlIHRoZSBvdmVyYWxsIGFjY3VyYWN5ICg5NC42JSkgYXBwZWFycyBoaWdoLCB0aGlzIGlzIHByaW1hcmlseSBkcml2ZW4gYnkgdGhlIG1vZGVs4oCZcyBzdHJvbmcgcGVyZm9ybWFuY2UgaW4gcHJlZGljdGluZyB0aGUgZG9taW5hbnQgY2xhc3MgKG5vbi1jb252ZXJzaW9uKS4gVGhpcyBoaWdoIGFjY3VyYWN5IGNhbiBiZSBtaXNsZWFkaW5nIGluIGltYmFsYW5jZWQgZGF0YXNldHMgbGlrZSB0aGlzIG9uZSwgd2hlcmUgdGhlIG1ham9yaXR5IG9mIGNlbGxzIHJlbWFpbiB1bmRldmVsb3BlZC4NCg0KDQojIyMgMTIuMy4yLiBST0MgQ3VydmUNCg0KYGBge3Igcm9jX2N1cnZlLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgY2FjaGU9VFJVRX0NCg0KZ2dwbG90KHRlc3RTZXRQcm9icywgYWVzKGQgPSBhcy5udW1lcmljKGNsYXNzKSwgbSA9IHByb2JzKSkgKyANCiAgZ2VvbV9yb2Mobi5jdXRzID0gNTAsIGxhYmVscyA9IEZBTFNFKSArIA0KICBzdHlsZV9yb2ModGhlbWUgPSB0aGVtZV9ncmV5KSArDQogIGdlb21fYWJsaW5lKHNsb3BlID0gMSwgaW50ZXJjZXB0ID0gMCwgc2l6ZSA9IDEuNSwgY29sb3IgPSAnZ3JleScpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmxpYnJhcnkocGxvdFJPQykNCiMgRW5zdXJlIGNsYXNzIGlzIG51bWVyaWMgKDAgb3IgMSkNCnRlc3RTZXRQcm9icyRjbGFzcyA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3Rlcih0ZXN0U2V0UHJvYnMkY2xhc3MpKQ0KDQojIENhbGN1bGF0ZSBBVUMNCnJvY19wbG90IDwtIGdncGxvdCh0ZXN0U2V0UHJvYnMsIGFlcyhkID0gY2xhc3MsIG0gPSBwcm9icykpICsgDQogIGdlb21fcm9jKG4uY3V0cyA9IDUwLCBsYWJlbHMgPSBGQUxTRSkNCg0KY2FsY19hdWMocm9jX3Bsb3QpDQoNCmBgYA0KVGhlIEFyZWEgdW5kZXIgdGhlIGN1cnZlIChBVUMpIGlzIDg3LjUlIHdoaWNoIGlzIGhpZ2hlciB0aGFuIDcwJSB3aGljaCBzaG93cyBzdHJvbmcgb3ZlcmFsbCBtb2RlbCBmaXQuIA0KIA0KIyMgMTIuNC4gQW5hbHl6aW5nIEVycm9yDQoNCiMjIyAxMi40LjEuIFNldHRpbmcgYSBUaHJlc2hvbGQNCg0KDQpgYGB7ciBTZXR0aW5nIGEgVGhyZXNob2xkLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgY2FjaGU9VFJVRX0NCmRhdF8yMDExX3ByZWRzIDwtICAgICAgICAgDQogIGRhdF8yMDExICU+JQ0KICAgIG11dGF0ZShwcm9icyA9IHByZWRpY3QoTW9kZWw2LCBkYXRfMjAxMSwgdHlwZT0icmVzcG9uc2UiKSkgJT4lDQogICBtdXRhdGUoVGhyZXNob2xkXzVfUGN0ID0gYXMuZmFjdG9yKGlmZWxzZShwcm9icyA+PSAwLjA1ICwxLDApKSwNCiAgICAgICAgICAgVGhyZXNob2xkXzE3X1BjdCA9ICBhcy5mYWN0b3IoaWZlbHNlKHByb2JzID49IDAuMTcgLDEsMCkpKSAlPiUNCiAgbXV0YXRlKGNvbmZSZXN1bHRfMDUgPSBjYXNlX3doZW4oVGhyZXNob2xkXzVfUGN0ID09IDAgJiBkZXZlbG9wbWVudF9jaGFuZ2UgPT0gMCB+ICJUcnVlX05lZ2F0aXZlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRocmVzaG9sZF81X1BjdCA9PSAxICYgZGV2ZWxvcG1lbnRfY2hhbmdlPT0xIH4gIlRydWVfUG9zaXRpdmUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGhyZXNob2xkXzVfUGN0ID09IDAgJiBkZXZlbG9wbWVudF9jaGFuZ2U9PTEgfiAiRmFsc2VfTmVnYXRpdmUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGhyZXNob2xkXzVfUGN0ID09IDEgJiBkZXZlbG9wbWVudF9jaGFuZ2UgPT0wIH4gIkZhbHNlX1Bvc2l0aXZlIikpICU+JQ0KICBtdXRhdGUoY29uZlJlc3VsdF8xNyA9IGNhc2Vfd2hlbihUaHJlc2hvbGRfMTdfUGN0ID09IDAgJiBkZXZlbG9wbWVudF9jaGFuZ2UgPT0gMCB+ICJUcnVlX05lZ2F0aXZlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRocmVzaG9sZF8xN19QY3QgPT0gMSAmIGRldmVsb3BtZW50X2NoYW5nZT09MSB+ICJUcnVlX1Bvc2l0aXZlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRocmVzaG9sZF8xN19QY3QgPT0gMCAmIGRldmVsb3BtZW50X2NoYW5nZT09MSB+ICJGYWxzZV9OZWdhdGl2ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUaHJlc2hvbGRfMTdfUGN0ID09IDEgJiBkZXZlbG9wbWVudF9jaGFuZ2UgPT0wIH4gIkZhbHNlX1Bvc2l0aXZlIikpICU+JQ0KICBzdF9hc19zZigpDQpgYGANCg0KDQpgYGB7ciBzdW1tYXJpemluZyBieSBjb3VudHkgYW5kIG1vZGVsIHR5cGUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBjYWNoZT1UUlVFfQ0KDQojIFN1bW1hcml6ZSBieSBjb3VudHkgYW5kIG1vZGVsIHR5cGUNCmRhdF8yMDExX3ByZWRzICU+JQ0KICBhcy5kYXRhLmZyYW1lKCkgJT4lDQogIGRwbHlyOjpzZWxlY3QoY29uZlJlc3VsdF8wNSwgY29uZlJlc3VsdF8xNywgTkFNRSkgJT4lDQogIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoImNvbmZSZXN1bHQiKSwgbmFtZXNfdG8gPSAiTW9kZWxfVHlwZSIsIHZhbHVlc190byA9ICJDb25mdXNpb25fUmVzdWx0IikgJT4lDQogIGdyb3VwX2J5KE5BTUUsIE1vZGVsX1R5cGUsIENvbmZ1c2lvbl9SZXN1bHQpICU+JQ0KICB0YWxseSgpICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gQ29uZnVzaW9uX1Jlc3VsdCwgdmFsdWVzX2Zyb20gPSBuLCB2YWx1ZXNfZmlsbCA9IDApICU+JSAjIFJlc2hhcGUgdG8gd2lkZSBmb3JtYXQNCiAgbXV0YXRlKFROX1JhdGVfU3BlY2lmaWNpdHkgPSAxMDAqKCBUcnVlX05lZ2F0aXZlLyhUcnVlX05lZ2F0aXZlK0ZhbHNlX1Bvc2l0aXZlKSksDQogICAgICAgICBUUF9SYXRlX1NlbnNpdGl2aXR5ID0gMTAwKiggVHJ1ZV9Qb3NpdGl2ZS8oVHJ1ZV9Qb3NpdGl2ZSArIEZhbHNlX05lZ2F0aXZlKSkpICU+JQ0KICBkcGx5cjo6c2VsZWN0KE5BTUUsIE1vZGVsX1R5cGUsIFROX1JhdGVfU3BlY2lmaWNpdHksIFRQX1JhdGVfU2Vuc2l0aXZpdHkpICU+JQ0KICBrYWJsZSgpICU+JQ0KICBrYWJsZV9zdHlsaW5nKCkNCg0KYGBgDQoNCjxkaXYgY2xhc3M9InN1cGVyYmlnaW1hZ2UiPg0KYGBge3Igc2hvd2luZyBwcmVkaWN0aW9ucyBieSB0aHJlc2hvbGQsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT0gRkFMU0UsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGg9IDgsIGNhY2hlPVRSVUV9DQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YT0gZGF0XzIwMTFfcHJlZHMgJT4lDQogICAgICAgICAgICBzdF9jZW50cm9pZCgpICU+JQ0KICAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdChjb25mUmVzdWx0XzA1LCBjb25mUmVzdWx0XzE3LCBnZW9tZXRyeSkgJT4lDQogICAgICAgICAgICAgICBnYXRoZXIoa2V5ID0gIlZhcmlhYmxlIiwgdmFsdWUgPSAiVmFsdWUiLCAtZ2VvbWV0cnkpLCANCiAgICAgICAgICAgICBhZXMoY29sb3VyPVZhbHVlKSkgKw0KICBmYWNldF93cmFwKH5WYXJpYWJsZSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoInJlZCIsICJ5ZWxsb3ciLCAiYmx1ZSIsICJncmV5IiksIGxhYmVscz1jKCJGYWxzZSBOZWdhdGl2ZSIsIkZhbHNlIFBvc2l0aXZlIiwgIlRydWUgTmVnYXRpdmUiLCAiVHJ1ZSBQb3NpdGl2ZSIpLA0KICAgICAgICAgICAgICAgICAgICAgIG5hbWU9IiIpICsNCiAgbGFicyh0aXRsZT0iRGV2ZWxvcG1lbnQgUHJlZGljdGlvbnMgLSBCeSBUaHJlc2hvbGQiKSArIA0KICB0aGVtZV92b2lkKCkNCmBgYA0KPC9kaXY+DQoNClRoZSB0YWJsZSBhbmQgbWFwIGFsc28gaGlnaGxpZ2h0cyBkZXZlbG9wbWVudCBwYXR0ZXJucyBhY3Jvc3MgZGlmZmVyZW50IGNvdW50aWVzIGluIHRoZSByZWdpb24uIENvdW50aWVzIHN1Y2ggYXMgQW5zb24sIENoZXN0ZXIsIGFuZCBHYXN0b24gZXhoaWJpdCB2ZXJ5IGhpZ2ggc3BlY2lmaWNpdHkgYnV0IG5lZ2xpZ2libGUgc2Vuc2l0aXZpdHksIGluZGljYXRpbmcgbWluaW1hbCBsYW5kIGNvbnZlcnNpb24gZnJvbSB1bmRldmVsb3BlZCB0byBkZXZlbG9wZWQuIFRoZXNlIGFyZWFzIHNob3cgbGltaXRlZCBkZXZlbG9wbWVudCBhY3Rpdml0eSBhbmQgY2FuIGJlIGNvbnNpZGVyZWQgcG90ZW50aWFsIHRhcmdldHMgZm9yIGZ1dHVyZSB0cmFuc2l0LW9yaWVudGVkIGRldmVsb3BtZW50IChUT0QpLiBJbiBjb250cmFzdCwgbW9yZSB1cmJhbml6ZWQgY291bnRpZXMgbGlrZSBNZWNrbGVuYnVyZyBhbmQgWW9yayBkaXNwbGF5IGhpZ2hlciBzZW5zaXRpdml0eSwgbWVhbmluZyB0aGUgbW9kZWwgY29ycmVjdGx5IHByZWRpY3RlZCBhIHNpZ25pZmljYW50IG51bWJlciBvZiBjb252ZXJzaW9ucyByZWZsZWN0aW5nIG9uZ29pbmcgZGV2ZWxvcG1lbnQgYWN0aXZpdHkgaW4gdGhlc2UgYXJlYXMuDQoNCg0KIyAxMy4gTWFraW5nIEEgRm9yZWNhc3QNCg0KSGVyZSwgd2UgcHJvcG9zZSBhIHBhdGggZm9yIGEgbmV3IEx5bnggU2lsdmVyIHJhaWwgbGluZSBmcm9tIFNvdXRoIEdhc3RvbmlhIGluIEdhc3RvbiBDb3VudHksIE5DIHRvIE1vbnJvZSwgTkMsIHRoZSBzZWF0IG9mIFVuaW9uIENvdW50eS4gVGhpcyByb3V0ZSBpcyBwcm9wb3NlZCBmb3Igc2V2ZXJhbCByZWFzb25zOiANCg0KLSBDaGFybG90dGUgY3VycmVudGx5IGhhcyB0d28gcHJpbWFyeSBsaWdodCByYWlsIGxpbmVzIHdpdGhpbiBNZWNrbGVuYnVyZyBDb3VudHkgdGhhdCBzZXJ2ZSBkb3dudG93biwgYnV0IG5laXRoZXIgcnVucyB0byB0aGUgYWlycG9ydC4gV2l0aCBoaWdoIHZpc2l0b3Igdm9sdW1lLCBidXNpbmVzcyB0cmFmZmljLCBhcyB3ZWxsIGFzIHRocmVlIHByb2Zlc3Npb25hbCBzcG9ydHMgdGVhbSBzdGFkaXVtcyBsb2NhdGVkIGRvd250b3duLCBjb25nZXN0aW9uIGluIHRoZSByZWdpb24gY291bGQgYmUgZHJhbWF0aWNhbGx5IHJlZHVjZWQgd2l0aCB0aGUgZXh0ZW5zaW9uIG9mIGEgbmV3IGxpZ2h0IHJhaWwgbGluZSB0aGF0IHNlcnZlcyBjb21tdW5pdGllcyB0byB0aGUgZWFzdCBhbmQgd2VzdCBvZiBkb3dudG93biBhbmQgY29ubmVjdHMgdGhlbSB0aHJvdWdoIHRoZSBhaXJwb3J0LiBUaGUgYWlycG9ydCBpcyBwdXJzdWluZyBhICQ0QiBleHBhbnNpb24gY2FtcGFpZ24gdGhhdCBpbmNsdWRlcyBhIHJlbm92YXRlZCBhbmQgZXhwYW5kZWQgdGVybWluYWwgYW5kIG5ldyBydW53YXksIHdoaWNoIGNvdWxkIGJlIGJ1aWx0IHRvIHNlcnZlIHRoaXMgcmFpbCBsaW5lLg0KDQotIFRoZSByZWdpb27igJlzIGxhcmdlc3QgcG9wdWxhdGlvbiBncm91cCBpcyAxOCB0byA0NCB5ZWFyIG9sZHMgYW5kIHRoZSBtZWRpYW4gYWdlIGlzIDM4LCBkZW1vZ3JhcGhpY3MgdGhhdCBhcmUgbW9yZSBsaWtlbHkgdG8gdXRpbGl6ZSBwdWJsaWMgdHJhbnNpdC4gQWx0aG91Z2ggTHlueCByaWRlcnNoaXAgbGV2ZWxzIGFyZSBzdGlsbCBub3QgYmFjayB0byBwcmUtQ09WSUQgbGV2ZWxzLCByaWRlcnNoaXAgb2YgdGhlIEdvbGQgbGluZSBpbmNyZWFzZWQgYnkgbmVhcmx5IDE0JSBhbmQgdGhlIEJsdWUgbGluZSdzIHJpZGVyc2hpcCBhbHNvIGluY3JlYXNlZCBieSAxMiUgYmV0d2VlbiAyMDIzIGFuZCAyMDI0LCBpbmRpY2F0aW5nIHRoZXJlIGlzIGEgbWFya2V0IGZvciBpbmNyZWFzZWQgYWNjZXNzIHRvIHB1YmxpYyB0cmFuc2l0Lg0KDQotIFJhdGhlciB0aGFuIHdvcmtpbmcgYWNyb3NzIHN0YXRlIGJvdW5kYXJpZXMsIGl0IHdpbGwgYmUgZWFzaWVyIHRvIGtlZXAgdGhlIGxpbmUgd2l0aGluIE5vcnRoIENhcm9saW5hIGZyb20gYSBmaW5hbmNpbmcgYW5kIGltcGxlbWVudGF0aW9uIHN0YW5kcG9pbnQuDQoNCg0KYGBge3IgUHVibGljIHRyYW5zaXQgbGluZSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPSBGQUxTRSwgY2FjaGU9VFJVRX0NCg0Kc2lsdmVyX2xpbmUgPC0gc3RfcmVhZCgiRGF0YV9DaGFybG90dGUvUHJvcG9zZWRfU2lsdmVybGluZS5kYmYiKQ0KDQpzaWx2ZXJfbGluZV9wcm9qIDwtIHN0X3RyYW5zZm9ybShzaWx2ZXJfbGluZSwgc3RfY3JzKGZpc2huZXRQb3B1bGF0aW9uMTkpKQ0KDQojIENyZWF0ZSBhIGJ1ZmZlciAoODAwIG1ldGVycyA9IH4yNjI1IGZlZXQpDQpzaWx2ZXJfbGluZV9idWZmZXIgPC0gc3RfYnVmZmVyKHNpbHZlcl9saW5lX3Byb2osIGRpc3QgPSAyNjI1KQ0KDQojIEludGVyc2VjdCBidWZmZXJlZCB6b25lIHdpdGggMjAxOSBmaXNobmV0IHBvcHVsYXRpb24NCnBvcF9zZXJ2ZWQgPC0gZmlzaG5ldFBvcHVsYXRpb24xOVtzdF9pbnRlcnNlY3RzKGZpc2huZXRQb3B1bGF0aW9uMTksIHNpbHZlcl9saW5lX2J1ZmZlciwgc3BhcnNlID0gRkFMU0UpLCBdDQoNCiMgU3VtbWFyaXplIHBvcHVsYXRpb24gc2VydmVkDQpzZXJ2ZWRfc3VtbWFyeSA8LSBwb3Bfc2VydmVkICU+JQ0KICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lDQogIHN1bW1hcml6ZShwb3B1bGF0aW9uX3NlcnZlZCA9IHN1bShwb3BfMjAxOSwgbmEucm0gPSBUUlVFKSkNCg0KcHJpbnQoc2VydmVkX3N1bW1hcnkpDQoNCmFjc1ZhcmlhYmxlczIgPC0gYygNCiAgaW5jb21lID0gIkIxOTAxM18wMDEiLCANCiAgdG90YWxfcG9wID0gIkIwMjAwMV8wMDEiLCANCiAgd2hpdGVfYWxvbmUgPSAiQjAyMDAxXzAwMiIpDQoNCm1zYV90cmFjdHMyIDwtIG1hcF9kZnIobmFtZXMoY2hhcmxvdHRlX21zYV9jb3VudGllcyksIGZ1bmN0aW9uKHN0KSB7DQogIGdldF9hY3MoDQogICAgZ2VvZ3JhcGh5ID0gInRyYWN0IiwNCiAgICB2YXJpYWJsZXMgPSBhY3NWYXJpYWJsZXMyLA0KICAgIHN0YXRlID0gc3QsDQogICAgY291bnR5ID0gY2hhcmxvdHRlX21zYV9jb3VudGllc1tbc3RdXSwNCiAgICBzdXJ2ZXkgPSAiYWNzNSIsDQogICAgeWVhciA9IDIwMTksDQogICAgb3V0cHV0ID0gIndpZGUiLA0KICAgIGdlb21ldHJ5ID0gVFJVRQ0KICApDQp9KSAlPiUNCiAgc3RfdHJhbnNmb3JtKHN0X2NycyhmaXNobmV0UG9wdWxhdGlvbjE5KSkgJT4lDQogIG11dGF0ZSgNCiAgICBwY3Rfbm9ud2hpdGUgPSAxMDAgKiAodG90YWxfcG9wRSAtIHdoaXRlX2Fsb25lRSkgLyB0b3RhbF9wb3BFLA0KICAgIGluY29tZV9ncm91cCA9IG50aWxlKGluY29tZUUsIDYpLA0KICAgIHJhY2VfZ3JvdXAgPSBudGlsZShwY3Rfbm9ud2hpdGUsIDYpDQogICkNCg0KIyNJbmNvbWUgbWFwDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IG1zYV90cmFjdHMyLCBhZXMoZmlsbCA9IGZhY3RvcihpbmNvbWVfZ3JvdXApKSwgY29sb3IgPSBOQSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMTAsIG5hbWUgPSAiSW5jb21lIFF1aW50aWxlIikgKyANCiAgZ2VvbV9zZihkYXRhID0gc2lsdmVyX2xpbmVfcHJvaiwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMS4yKSArDQogIGdlb21fc2YoZGF0YSA9IHNpbHZlcl9saW5lX2J1ZmZlciwgZmlsbCA9ICJsaWdodGJsdWUiLCBhbHBoYSA9IDAuMywgY29sb3IgPSBOQSkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIk1lZGlhbiBIb3VzZWhvbGQgSW5jb21lIGFuZCBTaWx2ZXIgTGluZSBUcmFuc2l0IEFjY2VzcyAoMjAxOSkiLA0KICAgIHN1YnRpdGxlID0gIlNpbHZlciBMaW5lIGJ1ZmZlciByZXByZXNlbnRzIDgwMG0gd2FsayBkaXN0YW5jZSINCiAgKSArDQogIHRoZW1lX3ZvaWQoKQ0KDQojIyBSYWNlIG1hcA0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSBtc2FfdHJhY3RzMiwgYWVzKGZpbGwgPSBmYWN0b3IocmFjZV9ncm91cCkpLCBjb2xvciA9IE5BKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKA0KICAgIHZhbHVlcyA9IGMoIiNGRkE1MDAiLCAiI0ZGN0Y1MCIsICIjRkY4QzAwIiwgIiNGRjYzNDciLCAiI0ZGNDUwMCIsICIjRTY3RTIyIiksDQogICAgbmFtZSA9ICIlIE5vbi1XaGl0ZSBRdWludGlsZSINCiAgKSArDQogIGdlb21fc2YoZGF0YSA9IHNpbHZlcl9saW5lX3Byb2osIGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDEuMikgKw0KICBnZW9tX3NmKGRhdGEgPSBzaWx2ZXJfbGluZV9idWZmZXIsIGZpbGwgPSAibGlnaHRibHVlIiwgYWxwaGEgPSAwLjMsIGNvbG9yID0gTkEpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJSYWNpYWwgRGVtb2dyYXBoaWNzIGFuZCBTaWx2ZXIgTGluZSBUcmFuc2l0IEFjY2VzcyAoMjAxOSkiLA0KICAgIHN1YnRpdGxlID0gIlRyYWN0cyBncm91cGVkIGJ5ICUgbm9uLXdoaXRlIHJlc2lkZW50cyINCiAgKSArDQogIHRoZW1lX3ZvaWQoKQ0KDQojIyBQb3B1bGF0aW9uIE1hcA0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IGZpc2huZXRQb3B1bGF0aW9uMTkgJT4lDQogICAgICAgICAgICBtdXRhdGUocG9wX3F1aW50aWxlID0gZmFjdG9yKG50aWxlKHBvcF8yMDE5LCA2KSkpLCANCiAgICAgICAgICBhZXMoZmlsbCA9IHBvcF9xdWludGlsZSksIGNvbG9yID0gTkEpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI2VkZjhlOSIsICIjYmFlNGIzIiwgIiM3NGM0NzYiLCAiIzMxYTM1NCIsICIjMDA2ZDJjIiwgIiMwMDQ0MWIiKSwgbmFtZSA9ICJQb3AuIFF1aW50aWxlIikgKw0KICBnZW9tX3NmKGRhdGEgPSBzaWx2ZXJfbGluZV9wcm9qLCBjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAxLjIpICsNCiAgZ2VvbV9zZihkYXRhID0gc2lsdmVyX2xpbmVfYnVmZmVyLCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGFscGhhID0gMC4zLCBjb2xvciA9IE5BKSArDQogIGxhYnModGl0bGUgPSAiUG9wdWxhdGlvbiBEZW5zaXR5IGFuZCBTaWx2ZXIgTGluZSBUcmFuc2l0IEFjY2VzcyAoMjAxOSkiLA0KICAgICAgIHN1YnRpdGxlID0gIlNpbHZlciBMaW5lIGJ1ZmZlciByZXByZXNlbnRzIDgwMG0gd2FsayBkaXN0YW5jZSIpICsNCiAgdGhlbWVfdm9pZCgpDQoNCiNwbG90dGluZyBhbG9uZyB3aXRoIGhpZ2h3YXlzIHRvIGdpdmUgcGVvcGxlIGN1cnJlbnRseSB1c2luZyBhIGhpZ2h3YXkgYW4gYWx0ZXJuYXRlIG1vZGUgb2YgdHJhbnNwb3J0YXRpb24NCg0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSBmaXNobmV0UG9wdWxhdGlvbjE5ICU+JQ0KICAgICAgICAgICAgbXV0YXRlKHBvcF9xdWludGlsZSA9IGZhY3RvcihudGlsZShwb3BfMjAxOSwgNikpKSwgDQogICAgICAgICAgYWVzKGZpbGwgPSBwb3BfcXVpbnRpbGUpLCBjb2xvciA9IE5BKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKG5hbWUgPSAiUG9wLiBRdWludGlsZSIpICsNCiAgZ2VvbV9zZihkYXRhID0gc2lsdmVyX2xpbmVfcHJvaiwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMS4yKSArDQogIGdlb21fc2YoZGF0YSA9IHNpbHZlcl9saW5lX2J1ZmZlciwgZmlsbCA9ICJsaWdodGJsdWUiLCBhbHBoYSA9IDAuMywgY29sb3IgPSBOQSkgKw0KICBnZW9tX3NmKGRhdGEgPSBjaGFySGlnaHdheXMsIGNvbG9yID0gImdyYXkzMCIsIHNpemUgPSAwLjUpICsNCiAgbGFicyh0aXRsZSA9ICJQb3B1bGF0aW9uIERlbnNpdHkgYW5kIFNpbHZlciBMaW5lIFRyYW5zaXQgQWNjZXNzIHdpdGggSGlnaHdheXMgKDIwMTkpIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJTaWx2ZXIgTGluZSBidWZmZXIgcmVwcmVzZW50cyA4MDBtIHdhbGsgZGlzdGFuY2UiKSArDQogIHRoZW1lX3ZvaWQoKQ0KDQpgYGANCipNZWRpYW4gSG91c2Vob2xkIEluY29tZSBhbmQgU2lsdmVyIExpbmUgVHJhbnNpdCBBY2Nlc3MqDQpUaGUgbWFwIGlsbHVzdHJhdGVzIGluY29tZSBpbmVxdWFsaXR5IGFjcm9zcyB0aGUgQ2hhcmxvdHRlIE1TQSwgd2hlcmUgaGlnaGVyLWluY29tZSBob3VzZWhvbGRzIGFyZSBwcmVkb21pbmFudGx5IGNvbmNlbnRyYXRlZCBpbiBhbHJlYWR5IGRldmVsb3BlZCB1cmJhbiBhbmQgc3VidXJiYW4gYXJlYXMuDQoNCipSYWNpYWwgRGVtb2dyYXBoaWNzKg0KVGhpcyBtYXAgcmV2ZWFscyBjbGVhciBwYXR0ZXJucyBvZiByYWNpYWwgc2VncmVnYXRpb24gd2l0aGluIHRoZSBNU0EuIEFyZWFzIHdpdGggYSBoaWdoZXIgcGVyY2VudGFnZSBvZiBub24td2hpdGUgcmVzaWRlbnRzIHRlbmQgdG8gYmUgc3BhdGlhbGx5IGNsdXN0ZXJlZCBhbmQgb2Z0ZW4gbG9jYXRlZCBpbiB0cmFjdHMgd2l0aCBsZXNzIGluZnJhc3RydWN0dXJlIGludmVzdG1lbnQgYW5kIGxpbWl0ZWQgcHJveGltaXR5IHRvIGhpZ2gtY2FwYWNpdHkgdHJhbnNpdCBjb3JyaWRvcnMuIA0KDQoqUG9wdWxhdGlvbiBEZW5zaXR5Kg0KUG9wdWxhdGlvbiBkZW5zaXR5IGlzIGhlYXZpbHkgY29uY2VudHJhdGVkIGluIHRoZSB1cmJhbiBjb3JlLCBwYXJ0aWN1bGFybHkgYXJvdW5kIGNlbnRyYWwgQ2hhcmxvdHRlLCBhbmQgZ3JhZHVhbGx5IGRlY3JlYXNlcyB0b3dhcmQgdGhlIHBlcmlwaGVyeS4gVGhlIHNlY29uZCBtYXAgb3ZlcmxheXMgY3VycmVudCBoaWdod2F5IGluZnJhc3RydWN0dXJlLCByZXZlYWxpbmcgdGhhdCBldmVuIGFyZWFzIHdpdGggbG93IHBvcHVsYXRpb24gZGVuc2l0eSBoYXZlIHNpZ25pZmljYW50IGhpZ2h3YXkgY29ubmVjdGl2aXR5LiBIb3dldmVyLCBkZXZlbG9wbWVudCBpbiB0aGVzZSByZWdpb25zIHJlbWFpbnMgc2xvdywgdW5kZXJzY29yaW5nIHRoZSBuZWVkIGZvciBleHBhbmRlZCBwdWJsaWMgdHJhbnNpdCBhbmQgYWZmb3JkYWJsZSwgZXF1aXRhYmxlIGFjY2VzcyB0byB0aGUgdXJiYW4gY2VudGVyLg0KDQpIZW5jZSwgd2UgYXJlIHByb3Bvc2luZyBhZGRpbmcgYSBuZXcgTHlueCBTaWx2ZXIgbGluZSB3aGljaCB3aWxsIHNlcnZlIGFwcHJveGltYXRlbHkgMzI1LDQzMCBwZW9wbGUgYmFzZWQgb24gMjAxOSBwb3B1bGF0aW9uIGRhdGEgd2l0aGluIGEgODAwbS8wLjVtaSB3YWxrc2hlZCBvZiB0aGUgbGluZS4NCg0KYGBge3IgYWRkaW5nIG5ldyBsaW5lLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgY2FjaGU9VFJVRX0NCg0KZGF0XzIwMTlfc2YgPC0gc3RfYXNfc2YoZGF0XzIwMTkpDQoNCnNpbHZlcl9saW5lIDwtIHN0X3RyYW5zZm9ybShzaWx2ZXJfbGluZSwgc3RfY3JzKGRhdF8yMDE5X3NmKSkNCmRhdF8yMDE5X3NmIDwtIHN0X3RyYW5zZm9ybShkYXRfMjAxOV9zZiwgc3RfY3JzKHNpbHZlcl9saW5lKSkNCg0KY2VudHJvaWRzIDwtIHN0X2NlbnRyb2lkKGRhdF8yMDE5X3NmKQ0KZGlzdF90b19zaWx2ZXIgPC0gYXMubnVtZXJpYyhzdF9kaXN0YW5jZShjZW50cm9pZHMsIHN0X3VuaW9uKHNpbHZlcl9saW5lKSkpDQoNCmRhdF8yMDE5JGRpc3RhbmNlX3NpbHZlciA8LSBkaXN0X3RvX3NpbHZlcg0KYGBgDQojIyMjIFByZWRpY3RpbmcNCg0KYGBge3IgcHJlZGljdGluZywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQpkYXRfMjAyN19wcmVkcyA8LSBkYXRfMjAxOSAlPiUNCiAgICBtdXRhdGUocHJvYnMgPSBwcmVkaWN0KE1vZGVsNiwgZGF0XzIwMTksIHR5cGU9InJlc3BvbnNlIikgLA0KICAgICAgICAgICBQcmVkaWN0aW9uID0gYXMuZmFjdG9yKGlmZWxzZShwcm9icyA+PSAwLjE3ICwxLDApKSkgJT4lDQogIHN0X2FzX3NmKCkNCg0KYGBgDQoNCk5vdyBsZXQncyBtYXAgaXQgLSB3aGVyZSBhcmUgdGhlIGNlbGxzIHRoYXQgYXJlIGNsYXNzaWZpZWQgYXMgbGlrZWx5IHRvIGRldmVsb3AgYnkgMjAyNz8gTm90ZSB0aGF0IGJsYW5rIGNlbGxzIGFyZSB3YXRlci4NCg0KDQpgYGB7ciBtYXAgb2YgcHJlZGljdGlvbnMsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcuaGVpZ2h0PSA4LCBmaWcud2lkdGg9IDgsIGNhY2hlPVRSVUV9DQpnZ3Bsb3QoZGF0YT1kYXRfMjAyN19wcmVkcykgKw0KICBnZW9tX3BvaW50KGFlcyh4PXh5QyhkYXRfMjAyN19wcmVkcylbLDFdLCANCiAgICAgICAgICAgICAgICAgeT14eUMoZGF0XzIwMjdfcHJlZHMpWywyXSwgY29sb3VyID0gUHJlZGljdGlvbikpICsNCiAgZ2VvbV9zZihkYXRhID0gc3R1ZHlBcmVhQ291bnRpZXMsIGZpbGwgPSAidHJhbnNwYXJlbnQiKSsNCiAgbGFicyh0aXRsZT0iRGV2ZWxvcG1lbnQgUHJlZGljdGlvbnMsIDIwMjciKSArIA0KICB0aGVtZV92b2lkKCkNCmBgYA0KVGhlIGFib3ZlIG1hcCBoaWdobGlnaHRzIGFsbCBjZWxscyB3aGljaCB3aWxsIGNvbnZlcnQgdG8gZGV2ZWxvcGVkIGluIDIwMjcgdGFraW5nIGludG8gYWNjb3VudCB0aGUgbmV3IHRyYW5zcG9ydCBpbmZyYXN0cnVjdHVyZS4gDQoNCg0KIyAxNC4gQXNzZXNzIEltcGFjdA0KDQpIZXJlLCB3ZSBhc3Nlc3MgdGhlIGltcGFjdCBvZiBvdXIgcHJlZGljdGl2ZSBtb2RlbCAtIGhvdyBtdWNoIGRldmVsb3BtZW50IGlzIHRvIGJlIGV4cGVjdGVkIGluIDIwMjcgYW5kIHdoZXJlIHdpbGwgaXQgb2NjdXIgd2l0aGluIHRoZSBDaGFybG90dGUgcmVnaW9uPw0KDQojIyAxNC4xLiBJbXBhY3QgQXNzZXNzbWVudA0KDQpgYGB7ciBpbXBhY3QgYXNzZXNzbWVudCwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQpkYXRfMjAyN19wcmVkcyAlPiUNCiAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICBmaWx0ZXIoUHJlZGljdGlvbiA9PSAxKSAlPiUNCiAgdGFsbHkoKSAlPiUNCiAgcmVuYW1lKHRvdGFsX2NlbGxzID0gbikgJT4lDQogIG11dGF0ZSh0b3RhbF9hcmVhX20gPSB0b3RhbF9jZWxscyAqIHJlcyhsY18yMDE5X3JzKVsxXSwNCiAgICAgICAgIHRvdGFsX2ttMiA9IHRvdGFsX2FyZWFfbS8xMDAwMDAwLA0KICAgICAgICAgdG90YWxfbWkyID0gdG90YWxfa20yKjAuMzg2MTAyKSAlPiUNCiAga2FibGUoKSAlPiUNCiAga2FibGVfc3R5bGluZyAoKQ0KDQpgYGANCg0KQ291bnR5LWJ5LWNvdW50eSBmb3JlY2FzdDoNCg0KYGBge3IgY291bnR5IGZvcmVjYXN0LCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgY2FjaGU9VFJVRX0NCmRhdF8yMDI3X3ByZWRzICU+JQ0KICBhcy5kYXRhLmZyYW1lKCkgJT4lDQogIGZpbHRlcihQcmVkaWN0aW9uID09IDEpICU+JQ0KICBncm91cF9ieShOQU1FKSAlPiUNCiAgdGFsbHkoKSAlPiUNCiAgcmVuYW1lKHRvdGFsX2NlbGxzID0gbikgJT4lDQogIG11dGF0ZSh0b3RhbF9hcmVhX20gPSB0b3RhbF9jZWxscyAqIHJlcyhsY18yMDE5X3JzKVsxXSwNCiAgICAgICAgIHRvdGFsX2ttMiA9IHRvdGFsX2FyZWFfbS8xMDAwMDAwLA0KICAgICAgICAgdG90YWxfbWkyID0gdG90YWxfa20yKjAuMzg2MTAyKSAlPiUNCiAga2FibGUoKSAlPiUNCiAga2FibGVfc3R5bGluZyAoKQ0KDQpgYGANCg0KRm9yZWNhc3QgYnkgMjAxOSBsYW5kIGNvdmVyIHR5cGU6DQoNCmBgYHtyIGZvcmVjYXN0IGJ5IDIwMTkgbGFuZCBjb3Zlciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlPVRSVUV9DQpkYXRfMjAyN19wcmVkcyAlPiUNCiAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICBmaWx0ZXIoUHJlZGljdGlvbiA9PSAxKSAlPiUNCiAgZHBseXI6OnNlbGVjdChmYXJtLCBvdGhlclVuZGV2ZWxvcGVkLCBmb3Jlc3QsIHdldGxhbmRzLCB3YXRlciwgTkFNRSkgJT4lDQogIGdhdGhlcigtTkFNRSwga2V5ID0gIlZhcmlhYmxlIiwgdmFsdWUgPSAiVmFsdWUiKSAlPiUNCiAgZ3JvdXBfYnkoTkFNRSwgVmFyaWFibGUpICU+JQ0KICBzdW1tYXJpemUodG90YWxfY2VsbHMgPSBzdW0oYXMubnVtZXJpYyhWYWx1ZSkpKSAlPiUNCiAgbXV0YXRlKHRvdGFsX2FyZWFfbSA9IHRvdGFsX2NlbGxzICogcmVzKGxjXzIwMTlfcnMpWzFdLA0KICAgICAgICAgdG90YWxfa20yID0gdG90YWxfYXJlYV9tLzEwMDAwMDAsDQogICAgICAgICB0b3RhbF9taTIgPSB0b3RhbF9rbTIqMC4zODYxMDIpICU+JQ0KICBrYWJsZSgpICU+JQ0KICBrYWJsZV9zdHlsaW5nICgpDQoNCmBgYA0KDQojIDE1LiBSZWNvbW1lbmRhdGlvbnMNCg0KKlBsYW5uaW5nIGNvbnRleHQqDQpBY2NvcmRpbmcgdG8gdGhlIFUuUy4gQ2Vuc3VzIEJ1cmVhdSwgdGhlIGNpdHkgb2YgQ2hhcmxvdHRlIGl0c2VsZiBpcyB0aGUgMTV0aCBtb3N0IHBvcHVsb3VzIGNpdHkgaW4gdGhlIFVTIGFuZCB0aGUgTVNBIGlzIHRoZSBsYXJnZXN0IG1ldHJvIGFyZWEgaW4gdGhlIENhcm9saW5hcyBhbmQgZm91cnRoIGxhcmdlc3QgaW4gdGhlIHNvdXRoZWFzdGVybiBVUy4gQWNjb3JkaW5nIHRvIHRoZSBDaGFybG90dGUgUmVnaW9uYWwgQnVzaW5lc3MgQWxsaWFuY2UsIHRoZSByZWdpb27igJlzIHBvcHVsYXRpb24gaGFzIGdyb3duIGJ5IDIwJSBzaW5jZSAyMDEwIGFuZCBpcyBwcm9qZWN0ZWQgdG8gZ3JvdyBhbiBhZGRpdGlvbmFsIDEyJSBieSAyMDMwLCB3aXRoIGFuIGVzdGltYXRlZCAxMTcgcGVvcGxlIG1vdmluZyBpbnRvIHRoZSByZWdpb24gZGFpbHkuIFRoZSByZWdpb24gaGFzIHNlZW4gYSAxNiUgbGFib3IgZm9yY2UgZ3Jvd3RoIHNpbmNlIDIwMTAgYW5kIGhhcyBhIGxvd2VyIHVuZW1wbG95bWVudCByYXRlIGFuZCBoaWdoZXIgeWVhci1vdmVyLXllYXIgZW1wbG95bWVudCBjaGFuZ2UgdGhhbiB0aGUgVVMgb24gYXZlcmFnZSwgYXMgd2VsbCBhcyB0aGUgc3RhdGUgYXZlcmFnZXMgZm9yIE5DIGFuZCBTQy4NCg0KUG9wdWxhdGlvbiBhbmQgZWNvbm9taWMgZ3Jvd3RoIGluIHRoZSBncmVhdGVyIENoYXJsb3R0ZSByZWdpb24gYXJlIGR1ZSBpbiBwYXJ0IHRvIHRoZSBjaXR54oCZcyByb2xlIGFzIGEgbWFqb3IgZmluYW5jaWFsIGNlbnRlciBhbmQgdHJhbnNwb3J0YXRpb24gaHViLCB3aXRoIGhlYWRxdWFydGVycyBmb3IgQmFuayBvZiBBbWVyaWNhIGFuZCBUcnVpc3QgRmluYW5jaWFsLCBhcyB3ZWxsIGFzIHRoZSBsYXJnZXN0IGVtcGxveW1lbnQgaHViIGZvciBXZWxscyBGYXJnby4gQ2hhcmxvdHRl4oCZcyBhaXJwb3J0IGlzIHRoZSBzaXh0aCBidXNpZXN0IGluIHRoZSB3b3JsZCwgd2l0aCBub25zdG9wIGRlc3RpbmF0aW9ucyB0byAxODggbG9jYXRpb25zIGFuZCBhbiBlY29ub21pYyBpbXBhY3Qgb2YgbmVhcmx5ICQ0MCBiaWxsaW9uLiANCg0KQ2hhcmxvdHRlIGhhcyB0d28gcHJpbWFyeSBsaWdodCByYWlsIGxpbmVzIHdpdGhpbiBNZWNrbGVuYnVyZyBDb3VudHkgdGhhdCBzZXJ2ZSBkb3dudG93biwgYnV0IG5laXRoZXIgcnVucyB0byB0aGUgYWlycG9ydC4gV2l0aCBoaWdoIHZpc2l0b3Igdm9sdW1lLCBidXNpbmVzcyB0cmFmZmljLCBhcyB3ZWxsIGFzIHRocmVlIHByb2Zlc3Npb25hbCBzcG9ydHMgdGVhbSBzdGFkaXVtcyBsb2NhdGVkIGRvd250b3duLCBjb25nZXN0aW9uIGluIHRoZSByZWdpb24gY291bGQgYmUgZHJhbWF0aWNhbGx5IHJlZHVjZWQgd2l0aCB0aGUgZXh0ZW5zaW9uIG9mIGEgbmV3IGxpZ2h0IHJhaWwgbGluZSB0aGF0IHNlcnZlcyBjb21tdW5pdGllcyB0byB0aGUgZWFzdCBhbmQgd2VzdCBvZiBkb3dudG93biBhbmQgY29ubmVjdHMgdGhlbSB0aHJvdWdoIHRoZSBhaXJwb3J0LiBUaGUgYWlycG9ydCBpcyBwdXJzdWluZyBhICQ0QiBleHBhbnNpb24gY2FtcGFpZ24gdGhhdCBpbmNsdWRlcyBhIHJlbm92YXRlZCBhbmQgZXhwYW5kZWQgdGVybWluYWwgYW5kIG5ldyBydW53YXksIHdoaWNoIGNvdWxkIGJlIGJ1aWx0IHRvIHNlcnZlIHRoaXMgcmFpbCBsaW5lLg0KDQoqQW5hbHlzaXMgUmVjb21tZW5kYXRpb25zKg0KXzEuIEFzc2VzcyBhIFBoYXNlZCBSYWlsIENvbnN0cnVjdGlvbiBBcHByb2FjaCBhbmQgZW5hYmxlIFRyYW5zaXQtT3JpZW50ZWQgRGV2ZWxvcG1lbnRfDQoNCkluIHRoaXMgYW5hbHlzaXMsIHdlIGhhdmUgZm9yZWNhc3RlZCBwb3RlbnRpYWwgZGV2ZWxvcG1lbnQgaW4gMjAyNyBpbiB0aGUgYnJvYWRlciBDaGFybG90dGUgcmVnaW9uIGJhc2VkIG9uIGxhbmQgY292ZXIgY2hhbmdlIGJldHdlZW4gMjAxMSBhbmQgMjAxOSBhbmQgcHJvcG9zZWQgY3JlYXRpbmcgYSBuZXcgU2lsdmVyIGxpZ2h0IHJhaWwgbGluZSB0aGF0IHdvdWxkIGNvbm5lY3QgQ2hhcmxvdHRlJ3MgZG93bnRvd24sIGFpcnBvcnQsIGFuZCBjb21tdW5pdGllcyB0byB0aGUgd2VzdCBhbmQgZWFzdC4gQmVjYXVzZSB0aGUgbW9kZWwgd2FzIG5vdCB0cmFpbmVkIHdpdGggdHJhbnNpdCBkYXRhIChleGlzdGluZyBCbHVlIGFuZCBHb2xkIEx5bnggbGluZXMpLCBvdXIgcmVzdWx0cyBzaG93IGxpbWl0ZWQgc3VwcG9ydCBmb3IgdGhpcyBmdWxsIHJvdXRlLiBXZSBkaWQgbm90IGluY2x1ZGUgdGhlIEx5bnggdHJhbnNpdCBpbmZyYXN0cnVjdHVyZSAgYmVjYXVzZSBpdCBoYXMgbm90IHJlbWFpbmVkIGNvbnNpc3RlbnQgdGhyb3VnaG91dCBvdXIgc3R1ZHkgdGltZSBwZXJpb2QgKDIwMTEgLSAyMDE5KSwgYWZ0ZXIgZGlzY3Vzc2luZyB3aXRoIElzYWJlbC4gSG93ZXZlciwgaW4gYW4gaW5pdGlhbCBydW4gb2YgdGhlIG1vZGVsLCB3ZSBkaWQgc2VlIHRoYXQgcHJveGltaXR5IHRvIHRoZSBCbHVlIExpbmUgd2FzIGEgc3Ryb25nIHByZWRpY3RvciBvZiBkZXZlbG9wbWVudCBiZXR3ZWVuIDIwMTEgYW5kIDIwMTkuIEluIHJldHJvc3BlY3QsIHdlIHNob3VsZCBoYXZlIGluY29ycG9yYXRlZCBhdCBsZWFzdCB0aGUgaW5pdGlhbCBCbHVlIExpbmUgKGNvbnN0cnVjdGVkIDIwMDcpIGludG8gb3VyIG1vZGVsLCBhbG9uZyB3aXRoIGhpZ2h3YXlzLCB0byBiZXR0ZXIgYXNzZXNzIHRoZSBpbmZsdWVuY2Ugb2YgdHJhbnNpdC1vcmllbnRlZCBkZXZlbG9wbWVudC4gVW5mb3J0dW5hdGVseSwgd2UgcmFuIG91dCBvZiB0aW1lIHRvIGdvIGJhY2sgYW5kIHRyeSB0aGlzIGFnYWluIQ0KDQpIb3dldmVyLCBvdXIgY3VycmVudCBtb2RlbCBkb2VzIHNob3cgdGhhdCBkZXZlbG9wbWVudCBpbiAyMDE3IGlzIGV4cGVjdGVkIHRvIGJlIGNvbmNlbnRyYXRlZCBpbiBNZWNrbGVuYnVyZyBDb3VudHkgKHdoaWNoIGlzIHdoZXJlIENoYXJsb3R0ZSdzIGFpcnBvcnQgYW5kIGRvd250b3duIGFyZSBsb2NhdGVkKS4gSXQgaXMgcG9zc2libGUgdGhhdCBhZG9wdGluZyBhIHBoYXNlZCBjb25zdHJ1Y3Rpb24gYXBwcm9hY2ggY291bGQgcHJvdmUgZWZmZWN0aXZlLCBmb2N1c2luZyBmaXJzdCBvbiBjb25uZWN0aW5nIGRvd250b3duIGFuZCB0aGUgYWlycG9ydCBhbGlnbmVkIHdpdGggdGhlIGFscmVhZHkgcGxhbm5lZCBhaXJwb3J0IHRlcm1pbmFsIGV4cGFuc2lvbi4gVGhlIGNvdW50eeKAmXMgcHVibGljIHRyYW5zaXQgYXV0aG9yaXR5IGlzIG1vc3RseSBmdW5kZWQgYnkgYSBoYWxmLWNlbnQgc2FsZXMgdGF4IGZvciB0cmFuc2l0IHBhaWQgYnkgc2hvcHBlcnMgaW4gTWVja2xlbmJ1cmcgQ291bnR5LCB3aGljaCBoYXMgc3VyZ2VkIGZyb20gJDEwOE0gaW4gRlkxOSB0byByb3VnaGx5ICQxNjUgbWlsbGlvbiBpbiBGWTI0OyB0aGUgcmVzdCBjb21lcyBmcm9tIHRoZSBmZWRlcmFsIGdvdmVybm1lbnQsIHRoZSBjaXR5LCBhbmQgZmFyZXMuIElmIHRoZSByZWdpb24gd2FudGVkIHRvIGZpbmFuY2UgYSBuZXcgU2lsdmVyIEx5bnggbGluZSwgaXQgY291bGQgYmUgZmluYW5jZWQgd2l0aCBhbiBpbmNyZWFzZWQgdGF4IHJhdGUgKG11c3QgYmUgcGFzc2VkIGJ5IHZvdGVycykgb3IgYnkgY3JlYXRpbmcgVGF4IEluY3JlbWVudCBGaW5hbmNpbmcgKFRJRikgZGlzdHJpY3RzIGFyb3VuZCByYWlsIHN0b3BzIHRvIGhlbHAgZmluYW5jZSBleHBhbnNpb24gb2YgdGhlIGxpbmUgYW5kIGVuY291cmFnZSBtb3JlIHRyYW5zaXQtb3JpZW50ZWQgZGV2ZWxvcG1lbnQgKFRPRCkgd2l0aGluIGEgMC41bWkvODAwbWkgZGlzdGFuY2Ugb2YgdGhlIGxpbmUuDQoNCl8yLiBDb25zaWRlciBwb3RlbnRpYWwgaGlkZGVuIGltcGFjdHMgdG8gd2V0bGFuZHMuXw0KSXQgaXMgbm90YWJsZSB0aGF0IHRoZSBtb3N0IGNvbW1vbiBsYW5kIGNvdmVyIHR5cGUgdGhhdCBleHBlcmllbmNlZCBkZXZlbG9wbWVudCBjaGFuZ2UgYmV0d2VlbiAyMDExIGFuZCAyMDE5IGlzICJPdGhlciBVbmRldmVsb3BlZCIgbGFuZHMuIEFkZGl0aW9uYWwgYW5hbHlzaXMgb2Ygc29pbCB0eXBlIG9yIG90aGVyIGZlYXR1cmVzIG9mIHRoZXNlIGFyZWFzIHdvdWxkIGJlIGV4dHJlbWVseSB1c2VmdWwgdG8gZGV0ZXJtaW5lIG1vcmUgc3BlY2lmaWNzLiBBbHRob3VnaCB0aGVzZSBsYW5kcyBjb3VsZCBoYXZlIGJlZW4gYWxyZWFkeS1jbGVhcmVkIHNpdGVzIGJlaW5nIHByZXBwZWQgZm9yIGNvbnN0cnVjdGlvbiwgaXQgaXMgYWxzbyB2ZXJ5IHBvc3NpYmxlIHRoYXQgdGhleSB3ZXJlIGVtZXJnZW50IHdldGxhbmRzIHdoaWNoIGRvIG5vdCBjb25zaXN0ZW50bHkgYXBwZWFyIHdldCBvciB2ZWdldGF0ZWQgdGhyb3VnaG91dCB0aGUgeWVhci4gV2V0bGFuZHMgYXJlIGNyaXRpY2FsIGVjb3N5c3RlbXMgdGhhdCBwcm92aWRlIGEgdmFyaWV0eSBvZiBzZXJ2aWNlcyBhbmQgYmVuZWZpdHMgbGlrZSB3aWxkbGlmZSBoYWJpdGF0IGFuZCBmaXNoZXJpZXMgYnJlZWRpbmcgZ3JvdW5kczsgcmVjcmVhdGlvbiBhbmQgdG91cmlzbTsgZmxvb2QgcmlzayByZWR1Y3Rpb24gdGhyb3VnaCBlcm9zaW9uIGNvbnRyb2wsIHN0b3JtIHN1cmdlIGJ1ZmZlcmluZywgYW5kIHdhdGVyIHN0b3JhZ2U7IGFuZCB3YXRlciBmaWx0ZXJpbmcgd2hpY2ggaW1wcm92ZXMgd2F0ZXIgcXVhbGl0eS4gRWFjaCB3ZXRsYW5kIHZhcmllcyBpbiBpdHMgaHlkcm9sb2d5LCBwbGFudCBjb21tdW5pdHkgY29tcG9zaXRpb24sIHNvaWwgdHlwZXMsIGJpb2dlb2NoZW1pY2FsIHByb2Nlc3NlcywgYW5kIG90aGVyIGVudmlyb25tZW50YWwgZmFjdG9ycy4gQWNjb3JkaW5nIHRvIHRoZSBOb3J0aCBDYXJvbGluYSBEaXZpc2lvbiBvZiBXYXRlciBSZXNvdXJjZXMsIHJpdmVyaW5lIGZvcmVzdCB3ZXRsYW5kcywgbW91bnRhaW4gYm9ncywgYW5kIHBvY29zaW5zIGFyZSBwYXJ0aWN1bGFybHkgY29tbW9uIHdldGxhbmRzIHR5cGVzIGluIHRoZSBQaWVkbW9udCBSZWdpb24gb2YgdGhlIENhcm9saW5hcywgd2hpY2ggbWF5IGJlIG9ic2N1cmVkIGluIHRoZSBOTENEIGFzIGZvcmVzdCwgc2hydWIsIG9yIGJhcmUgbGFuZCBjb3Zlci4NCg0KV2V0bGFuZHMgYWNyb3NzIHRoZSBVUyBhcmUgZmFjaW5nIGluY3JlYXNpbmcgdGhyZWF0cyBvZiBkZWdyYWRhdGlvbiBhbmQgbG9zcyB0aGFua3MgdG8gZGV2ZWxvcG1lbnQsIGh5ZHJvbG9naWNhbCBhbHRlcmF0aW9uIGxpa2UgbGV2ZWVzIGFuZCBkYW1zLCBpbnZhc2l2ZSBzcGVjaWVzLCBhbmQgc2VhIGxldmVsIHJpc2UuIEluIDIwMjMsIHRoZSBOb3J0aCBDYXJvbGluYSBMZWdpc2xhdHVyZSBwYXNzZWQgYSBsYXcgdGhhdCBzdWJzdGFudGlhbGx5IHJlZHVjZWQgcHJvdGVjdGlvbnMgZm9yIHdldGxhbmRzLCBtYWtpbmcgdGhlbSBtb3JlIGF0IHJpc2sgZnJvbSBkZXZlbG9wbWVudCBwcmVzc3VyZXMuIFRvIHByZXZlbnQgdGhlIGxvc3Mgb2YgdGhlc2UgY3JpdGljYWwgaGFiaXRhdHMgYW5kIHJlZHVjZSBmbG9vZCByaXNrIGZvciByZXNpZGVudHMsIGltcGxlbWVudGluZyBhZGRpdGlvbmFsIGxvY2FsIHJlc3RyaWN0aW9uIG9uIGRldmVsb3BtZW50IG9mIHdldGxhbmRzIHdvdWxkIGJlIGJlbmVmaWNpYWwgdG8gbWFrZSB1cCBmb3IgdGhlIHByb3RlY3Rpb25zIGxvc3QgYXQgdGhlIHN0YXRlIGxldmVsLiBJbiBhZGRpdGlvbiB0byBsb2NhbCBvcmRpbmFuY2VzLCB0aGUgcmVnaW9uIGNvdWxkIGFsc28gcHVyc3VlIGEgd2V0bGFuZHMgbWl0aWdhdGlvbiBiYW5rIHRvIGVuc3VyZSB0aGVyZSBpcyBubyBuZXQgbG9zcyBvZiB3ZXRsYW5kcyBmcm9tIGRldmVsb3BtZW50Lg0KDQpfMy4gUHJvdGVjdCBwcmltZSBmb3Jlc3QgYW5kIGZhcm0gbGFuZHMgZnJvbSBkZXZlbG9wbWVudF8NClRoZSBtYWpvcml0eSBvZiBkZXZlbG9wbWVudCBvdXIgbW9kZWwgcHJlZGljdHMgaW4gMjAyNyB3aWxsIGltcGFjdCBmb3Jlc3QgbGFuZHMgaW4gTWVja2xlbmJ1cmcgYW5kIExhbmNhc3RlciBDb3VudGllcy4gSW4gb3JkZXIgdG8gcmVzdHJpY3QgdGhlIGltcGFjdHMgb2YgdGhpcyBkZXZlbG9wbWVudCBvbiBuYXR1cmFsIHN5c3RlbXMsIHRoZSBjb3VudGllcyBjb3VsZCBjb25zaWRlciBpbXBsZW1lbnRpbmcgdHJlZSBjYW5vcHkgb3JkaW5hbmNlcyB0aGF0IHJlcXVpcmUgYSBjZXJ0YWluIGxldmVsIG9mIHRyZWUgY2Fub3B5IGNvdmVyYWdlIHRvIGJlIG1haW50YW5lZCBieSBkZXZlbG9wZXJzLiBTb21lIGFyZWFzIG9mIFZpcmdpbmlhIGRvIHRoaXMgYWxyZWFkeSwgYW5kIGl0IGhlbHBzIHRvIGVuc3VyZSB0cmVlIHJlcGxhY2VtZW50IGFsb25nIHdpdGggbmV3IGRldmVsb3BtZW50LiBBZGRpdGlvbmFsbHksIGxhbmQgY29uc2VydmF0aW9ucyBhcmUgYW4gZWZmZWN0aXZlIHRvb2wgdG8gaGVscCBwdXQgaGlnaC1xdWFsaXR5IGxhbmQgdW5kZXIgcGVybWFuZW50IGRldmVsb3BtZW50IHJlc3RyaWN0aW9ucy4gQm90aCBTQyBhbmQgTkMgb2ZmZXIgdGF4IGNyZWRpdHMgdG8gaW5jZW50aXZpemUgbGFuZG93bmVycyB0byBzZWxsIHRoZWlyIGRldmVsb3BtZW50IHJpZ2h0cy4gQm90aCBjb3VudGllcyBjb3VsZCBpZGVudGlmeSBhbmQgY29udGFjdCB0aGUgb3duZXJzIG9mIHByaW1lIGZvcmVzdCBhbmQgZmFybSBsYW5kIHRvIHNlZSBpZiB0aGV5IHdvdWxkIGJlIGludGVyZXN0ZWQgaW4gcHJlc2VydmluZyB0aGVpciBsYW5kcy4NCg0KDQo=