This geospatial statistical model uses routinely collected malaria case data, population data and remotely sensed data, such as open and vegetated water bodies, to estimate population living around open water bodies, expected malaria cases, and standardised morbidity ratio (SMR) of malaria. And ultimately, quantify the association between proximity to larval habitat and malaria risk in health facility catchment areas in Kasungu. The SMR compares the risk of morbidity in a population of interest with that of a standard population. In this case, our interest is to find out whether the number of dry season malaria cases in each catchment area are greater than we would expect given the malaria rate for the entire Kasungu district.

We do this by comparing what we observe (O) with what we would expect (E) if the risk of malaria was equal throughout Kasungu. The SMR statistical notation of catchment i can be written as follows: \[SMR_i = \frac{O_i}{E_i}\]

Buffers around waterbodies are created and then combined with population data in raster format to estimate the proprtion of catcment population living within 1km, 2km and 3km of water bodies. Subsequently, the observed malaria cases are modeled using Poisson regression to find out if living within various distances from water bodies is causing variability in malaria risk in Kasungu district. We hypothesize that the risk of being a case in a catchment is dependent on proximity to water bodies. The data used spans from 2017 to 2020 and was derived from digitized DHIS2 malaria records, accessibility mapping, aggregated population geospatial layer and TropWet tool in Google Earth Engine.

1 Load packages

Loading the R packages that will be used to read in, view, transform and model the malaria cases and spatial datasets.

suppressPackageStartupMessages({
  library(SpatialEpi)
  library(spdep)
  library(spaMM)
  library(popEpi)
  library(Epi)
  library(epitools)      # compute confidence intervals of malaria data
  library(tidyverse)
  library(caret)         # easily compute cross-validation methods to test model perfomance
  library(stats)
  library(MASS)          # fit negative binomial model
  library(performance)   # check linear model performance
  library(see)           # to plot model assumptions
  library(caTools)       # splitting data into training and test data sets
  library(glmmTMB)       # check overdispersion
  library(lme4)          # fit GLM
  library(report)        # automatically produce regression model reports
  library(qqplotr)
  library(DescTools)     # tools for descriptive statistics e.g., pseudo R-squared
  library(gtsummary)     # easily display regression model outputs, such as P value, CI
  library(ggpubr)
  library(plotly)
  library(lubridate)
  library(knitr)
  library(raster)
  library(rgdal)
  library(rgeos)
  library(readr)
  library(sf)
  library(sp)
  library(tmap)
  library(spdep)
  library(maptools)
  library(gridExtra)
  library(ggsci)
  library(grid)
  library(exactextractr)
  library(DataExplorer)
  library(thematic)
  library(mapview)
  library(kableExtra)    # create interactive tables
  library(gt)            # create beautiful HTML tables
  library(pander)        # improves the aesthetics of R outputs
  library(imputeTS)      # easily remove NAs
  `%>%` <- magrittr::`%>%`

})

1.1 Tell R where the data is

file.path(getwd(),"data")
## [1] "C:/Users/cnkolokosa/Documents/R/upscaled_2021_updated_May/upscaled_2021/data"
here::here()
## [1] "C:/Users/cnkolokosa/Documents/R/upscaled_2021_updated_May/upscaled_2021"

2 Load datasets

The total dry season malaria cases recorded at health-care facilities in Kasungu from 2017 to 2019 are contained in the KasunguData.csv sourced from https://dhis2.health.gov.mw/. The kasungu_facility_catchments_2004.shp shapefile also contains the population and health information within each health-facility catchment area in Kasungu district.

The aggregated population raster layers for Malawi e.g.,ku_pop_2017_1km_aggregated.tif were downloaded from the Open Spatial and Demographic and Data Research website: https://www.worldpop.org/geodata/country?iso3=MWI. These layers estimate total number of people per grid-cell. The units are number of people per pixel with country totals adjusted to match the corresponding official United Nations population estimates. The datasets were downloaded in Geotiff at a resolution of 1km and are projected in Geographic Coordinate System, WGS84.

The kasungu_water.shpand water_bodies layers contain open and vegetated waterbodies polygons, detected using the Tropical Wetland Unmixing Tool (TropWet). TropWet is a Google Earth Engine hosted toolbox that uses the Landsat archive to map tropical wetlands and can be accessed through: https://www.aber.ac.uk/en/dges/research/earth-observation-laboratory/research/tropwet/

# Kasungu dry season malaria data
# dry_season_malaria_2017_2020 <- readr::read_csv(
#   "https://raw.githubusercontent.com/ClintyNkolokosa/Analysis-of-dry-season-malaria-cases-in-Kasungu/main/data/dry_season_malaria_2017_2020.csv")

dry_season_malaria_2017_2020 <- read.csv(here::here("data/dry_season_malaria_2017_2020.csv"),
                                         stringsAsFactors = FALSE)

# Kasungu monthly NMCP confirmed malaria cases
monthly_malaria_2017_2021 <- read.csv(here::here("data/Kasungu monthly malaria 2017-2021.csv"),
                                      stringsAsFactors = FALSE)

monthly_malaria_2017_2021$date <- lubridate::ym(monthly_malaria_2017_2021$periodid) 

# Kasungu district boundary shapefile 
kasungu_district <- sf::st_read(here::here("data", "kasungu_district.shp"))
  
# Kasungu health facility catchments generated from accessibility mapping
malire_new <- sf::st_read(here::here("data", "zipatala_catchment_areas.shp")) |> 
              sf::st_transform(32736) # reproject to WGS UTM Zone 36 South

# Kasungu population raster layer
kasungu_population_2017 <- raster(here::here("data", "ku_pop_2017_1km_aggregated.tif"))

kasungu_population_2018 <- raster(here::here("data", "ku_pop_2018_1km_aggregated.tif"))

kasungu_population_2019 <- raster(here::here("data", "ku_pop_2019_1km_aggregated.tif"))

kasungu_population_2020 <- raster(here::here("data", "ku_pop_2020_1km_aggregated.tif"))

# Read in waterbodies polygons 
dryseason_waterbodies_2017 <- sf::st_read(here::here("data", "water_bodies_2017.shp"))

dryseason_waterbodies_2018 <- sf::st_read(here::here("data", "kasungu_2018_water.shp"))

dryseason_waterbodies_2019 <- sf::st_read(here::here("data", "kasungu_2019_water.shp"))

dryseason_waterbodies_2020 <- sf::st_read(here::here("data", "water_bodies_2020.shp"))

# Add a row ID to water bodies polygons 
dryseason_waterbodies_2017$ID <- 1:nrow(dryseason_waterbodies_2017)

dryseason_waterbodies_2018$ID <- 1:nrow(dryseason_waterbodies_2018)

dryseason_waterbodies_2019$ID <- 1:nrow(dryseason_waterbodies_2019)

dryseason_waterbodies_2020$ID <- 1:nrow(dryseason_waterbodies_2020)

2.1 View the dry season malaria case data

Lets have a closer look at the malaria dataset. We observe that Kasungu district has 30 health facilities classified as dispensary, health centre, district hospital and rural hospital, and the highest malaria cases were recorded at Kasungu District Hospital.

# Plotly bar chart -------------------------------------------------------------
bar_chart <- dry_season_malaria_2017_2020 |>  
  dplyr::filter(Names != "K2 Taso Clinic",          # Have missing malaria records
                Names != "Kalikeni Private Clinic",
                Names != "Kakwale Health Centre",
                Names != "St Andrews Community Hospital",
                Names != "St. Faith Health Centre",
                Names != "Chambwe Health Centre") |> 
  plotly::plot_ly(y = ~Names,
                  x = ~dr_2017,
                  type = "bar",
                  orientation = 'h',
                  name = "2017") |>
  plotly::add_trace(x = ~ dr_2018,
                    name = "2018") |>
  plotly::add_trace(x = ~ dr_2019,
                    name = "2019") |>
  plotly::add_trace(x = ~ dr_2020,
                    name = "2020") |> 
  plotly::layout(xaxis = list(title = "Total malaria cases"),
                 yaxis = list(title = " "),
                 hovermode = "compare",
                 margin = list(b = 10,
                               t = 10,
                               pad = 2))
bar_chart

Fig.1 The total malaria cases recorded at each health-care facility in Kasungu district

2.1.1 Yearly variation in malaria cases

ggplot2::ggplot(monthly_malaria_2017_2021) +
  aes(x = date, 
      y = NMCP) +
  geom_line(size = 0.6, 
            colour = "#112446") +
  scale_y_continuous(trans = "log2") +
  labs(x = "Year", 
       y = "Confirmed malaria cases") +
  theme_classic()
Fig.2: Seasonal variation in confirmed malaria cases

Fig.2: Seasonal variation in confirmed malaria cases

3 Kasungu health-care facilities and their catchment areas

Heath facility catchment area is the area from which a health facility attracts patients. Since the available official catchment areas are outdated and no recent spatial data about the catchment areas is available, new health facility catchments polygon were generated from generic accessibility mapping script adapted from https://malariaatlas.org/wp-content/uploads/accessibility/R_generic_accessibilty_mapping_script.r The script requires two user supplied datasets: the 2015 friction surface, which is available here: http://www.map.ox.ac.uk/accessibility_to_cities/, and a user-supplied .csv of points dry_season_malaria_2017_2020. The accumulated cost algorithm accCost and r.Cost algorithm in QGIS were run to make the final output map of new health facility catchment boundaries.

# Using the complete.cases() function to select health centres with complete 
# longitude and latitude coordinates.
zipatala <- dry_season_malaria_2017_2020[complete.cases(dry_season_malaria_2017_2020),] 

# Aggregate health facilities close to each other: 
#  a) Kasalika Health Centre and Kasungu District Hospital, 
#  b) Bua and Mziza Health Centres, and 
#  c) Kaluluma and Nkhamenya Rural Hospitals 
#  d) Anchor and Santhe Health Centres are combined in order to 
# generate catchment areas that are geographically correct

zipatala$dr_2017[which(
  zipatala$Names == "Kasungu District Hospital")] <- zipatala$dr_2017[which(
    zipatala$Names == "Kasungu District Hospital")] + zipatala$dr_2017[which(
      zipatala$Names == "Kasalika Health Centre")]

zipatala$dr_2018[which(
  zipatala$Names == "Kasungu District Hospital")] <- zipatala$dr_2018[which(
    zipatala$Names == "Kasungu District Hospital")] + zipatala$dr_2018[which(
      zipatala$Names == "Kasalika Health Centre")]

zipatala$dr_2019[which(
  zipatala$Names == "Kasungu District Hospital")] <- zipatala$dr_2019[which(
    zipatala$Names == "Kasungu District Hospital")] + zipatala$dr_2019[which(
      zipatala$Names == "Kasalika Health Centre")]

zipatala$dr_2020[which(
  zipatala$Names == "Kasungu District Hospital")] <- zipatala$dr_2020[which(
    zipatala$Names == "Kasungu District Hospital")] + zipatala$dr_2020[which(
      zipatala$Names == "Kasalika Health Centre")]
  
zipatala$dr_2017[which(
  zipatala$Names == "Nkhamenya Rural Hospital")] <- zipatala$dr_2017[which(
    zipatala$Names == "Nkhamenya Rural Hospital")] + zipatala$dr_2017[which(
      zipatala$Names == "Kaluluma Rural Hospital")]

zipatala$dr_2018[which(
  zipatala$Names == "Nkhamenya Rural Hospital")] <- zipatala$dr_2018[which(
    zipatala$Names == "Nkhamenya Rural Hospital")] +zipatala$dr_2018[which(
      zipatala$Names == "Kaluluma Rural Hospital")]

zipatala$dr_2019[which(
  zipatala$Names == "Nkhamenya Rural Hospital")] <- zipatala$dr_2019[which(
    zipatala$Names == "Nkhamenya Rural Hospital")] + zipatala$dr_2019[which(
      zipatala$Names == "Kaluluma Rural Hospital")]

zipatala$dr_2020[which(
  zipatala$Names == "Nkhamenya Rural Hospital")] <- zipatala$dr_2020[which(
    zipatala$Names == "Nkhamenya Rural Hospital")] + zipatala$dr_2020[which(
      zipatala$Names == "Kaluluma Rural Hospital")]

zipatala$dr_2017[which(
  zipatala$Names == "Mziza Health Centre")] <- zipatala$dr_2017[which(
    zipatala$Names == "Mziza Health Centre")] + zipatala$dr_2017[which(
      zipatala$Names == "Bua Health Centre")]

zipatala$dr_2018[which(
  zipatala$Names == "Mziza Health Centre")] <- zipatala$dr_2018[which(
    zipatala$Names == "Mziza Health Centre")] + zipatala$dr_2018[which(
      zipatala$Names == "Bua Health Centre")]

zipatala$dr_2019[which(
  zipatala$Names == "Mziza Health Centre")] <- zipatala$dr_2019[which(
    zipatala$Names == "Mziza Health Centre")] + zipatala$dr_2019[which(
      zipatala$Names == "Bua Health Centre")] 
  

zipatala$dr_2020[which(
  zipatala$Names == "Mziza Health Centre")] <- zipatala$dr_2020[which(
    zipatala$Names == "Mziza Health Centre")] + zipatala$dr_2020[which(
      zipatala$Names == "Bua Health Centre")]

zipatala$dr_2017[which(
  zipatala$Names == "Santhe Health Centre")] <- zipatala$dr_2017[which(
    zipatala$Names == "Santhe Health Centre")] + zipatala$dr_2017[which(
      zipatala$Names == "Anchor Farm")]

zipatala$dr_2018[which(
  zipatala$Names == "Santhe Health Centre")] <- zipatala$dr_2018[which(
    zipatala$Names == "Santhe Health Centre")] + zipatala$dr_2018[which(
      zipatala$Names == "Anchor Farm")]

zipatala$dr_2019[which(
  zipatala$Names == "Santhe Health Centre")] <- zipatala$dr_2019[which(
    zipatala$Names == "Santhe Health Centre")] + zipatala$dr_2019[which(
      zipatala$Names == "Anchor Farm")]

zipatala$dr_2020[which(
  zipatala$Names == "Santhe Health Centre")] <- zipatala$dr_2020[which(
    zipatala$Names == "Santhe Health Centre")] + zipatala$dr_2020[which(
      zipatala$Names == "Anchor Farm")]


# Drop out the other health facilities
zipatala_aggregated <- zipatala |>
  dplyr::filter(Names != "Kasalika Health Centre",
                Names != "Bua Health Centre",
                Names != "Kaluluma Rural Hospital",
                Names != "Anchor Farm")

# Save csv file
# write.csv(zipatala_aggregated, "data/zipatala_aggregated.csv")

# Convert to sf object
zipatala_aggregated_sf <- sf::st_as_sf(zipatala_aggregated,
                                        coords = c("LONGITU", "LATITUD"),
                                        crs = 4326, agr = "constant")

# st_write(zipatala_aggregated_sf, "data/zipatala_combined.shp")

3.1 View location of the health facilities in the new catchment areas

# Plot map
tm_shape(malire_new)+
  tm_polygons()+
  tm_shape(zipatala_aggregated_sf)+
  tm_dots(size = .3, 
          col = "blue", 
          alpha = 0.5)+
  tm_text("Names", 
          size = .3, 
          just = "top", 
          col = "black", 
          remove.overlap = TRUE)+
  tm_layout(frame = FALSE,
            title = "New Kasungu health facility \n catchment boundaries",
            title.size = .8, 
            title.position = c("left", "top"))+
  tm_compass(position=c("right", "top"))+
  tm_scale_bar(breaks = c(0, 10, 20), 
               text.size = .5)
Fig 3. Kasungu health-care facilities and catchment areas

Fig 3. Kasungu health-care facilities and catchment areas

3.2 Kasungu district estimated population per grid-cell

# Custom function to create a raster population map ----------------------------
create.population.map <- function(population.raster, title){
  # raster population map
  # arguments:
  #   population.raster:  aggregated population raster layer from WorldPop
  #   legend.title: legend title
  # returns:
  #   a tmap-element (plots a map)
  
  # set to interactive view
  # tmap::tmap_mode("view")
  
  # plot map
  tmap::tm_shape(population.raster)+
    tmap::tm_raster(palette = "-viridis", 
                    title = title,
                    breaks = c(0,100,200,400,600,800,1000,2000,4000,6000,8000))+
    tmap::tm_layout(legend.position = c("right", "bottom"),
                    frame = FALSE)+
    tmap::tm_scale_bar(position = c("left", "bottom"))
}

# Set to static map
 tmap_mode("plot")
 
# Invoking function ------------------------------------------------------------
estimated_pop_2017 <- create.population.map(kasungu_population_2017, title = "2017 Population")

estimated_pop_2018 <- create.population.map(kasungu_population_2018, title = "2018 Population")

estimated_pop_2019 <- create.population.map(kasungu_population_2019, title = "2019 Population")

estimated_pop_2020 <- create.population.map(kasungu_population_2020, title = "2020 Population")

# Layout the maps
tmap::tmap_arrange(estimated_pop_2017, estimated_pop_2018, 
                   estimated_pop_2019, estimated_pop_2020, nrow = 2) 
Fig.4 Estimated total number of people per 1km grid-cell

Fig.4 Estimated total number of people per 1km grid-cell

4 Assign dry season malaria cases and population density to new health facility catchments

The WorldPop aggregated population e.g. kasungu_population_2017.tif, and DHIS2 malaria dry_season_malaria_2017_2020 datasets are assigned to the new health facility catchments.

# Helper function that assigns malaria data from health facilities to their catchments areas 
assign.malaria.data <- function(catchment_boundary, malaria_data){
  # arguments:
  #   catchment_boundary: sf polygon object of new catchment boundaries
  #   malaria_data: sf point object with a data frame containing the dry season malaria cases
  # returns:
  #   catchments_malaria_sf: sf polygon object with a data frame containing dry season malaria cases


  # Convert sf objects to spatial
  catchment_shp <- as(catchment_boundary, "Spatial")
  
  malaria_shp <- as(malaria_data, "Spatial")

  # Match CRS
  malaria_shp <- spTransform(malaria_shp, crs(catchment_shp))

  # Overlay aggregated health facility points and extract 2017 - 2020 malaria cases
  # Using 'point.in.poly' to return a point spatial object, in this case location of health facilities
  # and estimated population instead of sp::over function, which simply returns 
  # a data frame, with the same no. rows.
  # Argument 'sp = TRUE' returns an sp class object, else returns sf class object
  # Joining the malaria and population dataset using only 'merge' function can't work due to 
  # non-unique columns and differences in row numbers
  
  hospitals_in_catchment <- spatialEco::point.in.poly(malaria_shp, catchment_shp, sp = TRUE) 

  # Add the extracted ID, health facility names and dry season malaria cases to 
  # the health facility catchments (hfc)
  hfc_malaria_shp <- merge(catchment_shp, hospitals_in_catchment, by.x = "DN", by.y = "rowID")

  # Convert the shapefile containing malaria data to sf-object
  hfc_malaria_sf <- sf::st_as_sf(hfc_malaria_shp)

  # Tidy the data by dropping columns not needed
  catchment_malaria <- hfc_malaria_sf |> 
    dplyr::select(-c(coords.x1, coords.x2))

  return(out = catchment_malaria)
}


# Invoking the function --------------------------------------------------------
malaria_by_catchment <- assign.malaria.data(malire_new, zipatala_aggregated_sf)

5 Assign population data to the health catchment areas

The population raster is a continuous gridded surface layer that has an estimated population density value to every square in their grid. The population values are extracted using raster::extract(), summed and apportioned to the catchment polygons.

# Helper unction to extract population from WorldPop raster file and assign
# the values to the new catchments.

extract.pop.values <- function(kasungu_pop_raster, catchments){
  # function to extract population from raster file and assign the population to catchments
  # arguments:
  #   kasungu_pop_raster: population raster file clipped to Kasungu district
  #   catchments: shapefile containing the polygons that we will use as boundaries
  # returns:
  #   catchments_malaria_pop_sf: sf polygon object containing malaria and population data
  
  # convert from sf to sp
  catchments_sp <- as(catchments, "Spatial")
  
  # Match extent i.e projection
  catchments_sp <- spTransform(catchments_sp, proj4string(kasungu_pop_raster))
  
  # Crop and mask the population raster to exclude Kasungu National Park
  pop_raster_clip <- raster::crop(kasungu_pop_raster, extent(catchments_sp)) |>
    raster::mask(catchments_sp)
  
  # Extracting zonal statistics from a population raster layer
  pop_by_catchment <- round(raster::extract(pop_raster_clip, catchments, fun = sum, na.rm = TRUE))
  
  pop_by_catchment_df <-  pop_by_catchment %>%  
  # apply unlist to the lists to have vectors as the list elements
  lapply(unlist) %>%  
  # convert vectors to data.frames
  lapply(as_tibble) %>%   
  # combine the list of data.frames
  bind_rows(., .id = "rowID") %>%   
  # rename the value variable
  dplyr::rename(pop = value)
  
  # Add row ID to column to catchment layer
  catchments$rowID <- 1:nrow(catchments)
  
  # Merge catchment areas and population data 
  pop_by_catchments <- merge(catchments, pop_by_catchment_df, by = "rowID")
  
  # Cleaning 'Inf' values
  pop_by_catchments |> 
    dplyr::mutate_if(is.numeric, list(~na_if(., Inf))) |> 
    dplyr::mutate_if(is.numeric, list(~na_if(., -Inf)))

  return(out = pop_by_catchments)
  
}

# Invoking the function -------------------------------------------------------------------------
malaria_pop_by_catchment_2017 <- extract.pop.values(kasungu_population_2017, malaria_by_catchment)

malaria_pop_by_catchment_2018 <- extract.pop.values(kasungu_population_2018, malaria_by_catchment)

malaria_pop_by_catchment_2019 <- extract.pop.values(kasungu_population_2019, malaria_by_catchment)

malaria_pop_by_catchment_2020 <- extract.pop.values(kasungu_population_2020, malaria_by_catchment)

5.1 View population by catchment maps

Estimated total number of people within health facility catchment areas.

# max(malaria_pop_by_catchment_2017$pop)
# [1] 143490
# max(malaria_pop_by_catchment_2018$pop)
# [1] 147175
# max(malaria_pop_by_catchment_2019$pop)
# [1] 151079
# max(malaria_pop_by_catchment_2020$pop)
# [1] 185282

# Custom function to create maps of estimated population by catchment areas --------------------------
create.population.map <- function(catchment.area, 
                                  variable = "pop", 
                                  title, 
                                  legend.title = "Estimated \n population"){
  # estimated population map
  # catchment.area: estimated population layer from nachulu function
  # variable: variable name (as character, in qoutes)
  # title: map title in quotes
  # legend.title: legend title in qoutes
  # returns:
  #   a tmap-element (plots a map)
  tm_shape(catchment.area)+
    tm_fill(col = variable, 
            breaks = c(0, 13000, 19000, 27000, 35000, 70000, 140000, 200000),
            palette = "YlOrBr",
            title = legend.title)+
    tm_borders(col = "grey",
               lwd = 0.4)+
    tm_layout(legend.position = c(0.75, "bottom"),
              legend.text.size = 0.6,
              legend.title.size = 0.8,
              frame = FALSE)+
    tm_credits(title, 
               position = c(0.3, 0.8), 
               size = 1)
}

# Invoking the function --------------------------------------------------------------------------
pop_by_catchment_2017 <- create.population.map(malaria_pop_by_catchment_2017, title = "2017")

pop_by_catchment_2018 <- create.population.map(malaria_pop_by_catchment_2018, title = "2018")

pop_by_catchment_2019 <- create.population.map(malaria_pop_by_catchment_2019, title = "2019")

pop_by_catchment_2020 <- create.population.map(malaria_pop_by_catchment_2020, title = "2020")

tmap::tmap_arrange(pop_by_catchment_2017, pop_by_catchment_2018,
                   pop_by_catchment_2019, pop_by_catchment_2020, ncol = 2)
Fig. 5: Estimated population by health facility catchment areas

Fig. 5: Estimated population by health facility catchment areas

5.2 Population density by catchment

# Helper function to calculate population density by catchment -----------------
calculate.population.density <- function(pop.data){
  
  # Convert to spatial object
  pop.sp <- as(pop.data, "Spatial")

  # Calculate area of catchment polygon in square kilometres
  pop.sp$area_sqkm <- round(rgeos::gArea(pop.sp, byid = TRUE) / (1000 * 1000))

  # Calculate population density
  pop.sp$pop_density <- round(pop.sp$pop / pop.sp$area_sqkm)
  
  # Convert back to sf object
  pop.sf <- sf::st_as_sf(pop.sp)
  
  return(pop.sf)
}

# Invoking function ------------------------------------------------------------
pop_density_2017 <- calculate.population.density(malaria_pop_by_catchment_2017)

pop_density_2018 <- calculate.population.density(malaria_pop_by_catchment_2018)

pop_density_2019 <- calculate.population.density(malaria_pop_by_catchment_2019)

pop_density_2020 <- calculate.population.density(malaria_pop_by_catchment_2020)

# Helper function to create population density maps ----------------------------
create.pop.density.map <- function(pop.density.data,
                                   variable = "pop_density", 
                                   title = NA, 
                                   legend.title = "Population \ndensity/km^2"){
  tm_shape(pop.density.data)+
    tm_fill(col = variable, 
            breaks = c(0, 50, 100, 150, 200, 250, 300, 350),
            palette = "-magma",
            title = legend.title)+
    tm_borders()+
    tm_layout(legend.position = c(0.75, "bottom"),
              legend.text.size = 0.6,
              legend.title.size = 0.8,
              frame = FALSE)+
    tm_credits(title, 
               position = c(0.3, 0.8), 
               size = 1)
}

# Invoking function ------------------------------------------------------------
pop_density_2017_map <- create.pop.density.map(pop_density_2017, title = "2017")

pop_density_2018_map <- create.pop.density.map(pop_density_2018, title = "2018")

pop_density_2019_map <- create.pop.density.map(pop_density_2019, title = "2019")

pop_density_2020_map <- create.pop.density.map(pop_density_2020, title = "2020")

# Layout maps
tmap::tmap_arrange(pop_density_2017_map, pop_density_2018_map,
                   pop_density_2019_map, pop_density_2020_map, ncol = 2)
Fig. 6: Estimated population density by health facility catchment areas

Fig. 6: Estimated population density by health facility catchment areas

6 Calculate the expected number of cases for each catchment area

The expected number of dry season malaria cases in catchment i are calculated as the observed risk (r) of malaria i.e. the total number of malaria cases in Kasungu district divided by the total population of the district, multiplied by the number of people in the catchment area: \[E_i = \frac{\sum_i O_i}{\sum_i N_i}\times N_i\]

The expected number of dry season malaria cases are calculated under the assumption that there is no spatial variation in risk, i.e., no difference in infection rates between the catchment areas.

# Compute and print the overall incidence of dry season malaria cases
overall_malaria_incidence_rate_2017 <- round(sum(
  malaria_pop_by_catchment_2017$dr_2017) / sum(
    malaria_pop_by_catchment_2017$pop), 2)

overall_malaria_incidence_rate_2017
## [1] 0.08
overall_malaria_incidence_rate_2018 <- round(sum(
  malaria_pop_by_catchment_2018$dr_2018) / sum(
    malaria_pop_by_catchment_2018$pop), 2)

overall_malaria_incidence_rate_2018
## [1] 0.09
overall_malaria_incidence_rate_2019 <- round(sum(
  malaria_pop_by_catchment_2019$dr_2019) / sum(
    malaria_pop_by_catchment_2019$pop), 2)

overall_malaria_incidence_rate_2019
## [1] 0.09
overall_malaria_incidence_rate_2020 <- round(sum(
  malaria_pop_by_catchment_2020$dr_2020) / sum(
    malaria_pop_by_catchment_2020$pop), 2)

overall_malaria_incidence_rate_2020
## [1] 0.12
# Calculate expected malaria cases ------------------------------------------------
expected_malaria_2017 <- malaria_pop_by_catchment_2017 |>
  dplyr::rename(
    observed_2017 = dr_2017,
     pop_2017 = pop) |> 
  dplyr::mutate(
    expected_2017 = round(sum(observed_2017)/sum(pop_2017, na.rm = TRUE)*pop_2017))

expected_malaria_2018 <- malaria_pop_by_catchment_2018 |>
  dplyr::rename(
    observed_2018 = dr_2018,
    pop_2018 = pop) |> 
  dplyr::mutate(
    expected_2018 = round(sum(observed_2018)/sum(pop_2018, na.rm = TRUE)*pop_2018))

expected_malaria_2019 <- malaria_pop_by_catchment_2019 |>
  dplyr::rename(
    observed_2019 = dr_2019,
    pop_2019 = pop) |>
  dplyr::mutate(
    expected_2019 = round(sum(observed_2019)/sum(pop_2019, na.rm = TRUE)*pop_2019)) 

expected_malaria_2020 <- malaria_pop_by_catchment_2020 |>
  dplyr::rename(
    observed_2020 = dr_2020,
    pop_2020 = pop) |>
  dplyr::mutate(
    expected_2020 = round(sum(observed_2020)/sum(pop_2020, na.rm = TRUE)*pop_2020))

7 Calculate the Standardised Morbidity Ratio of malaria incidences for each catchment area

The SMR compares the risk of morbidity in a population of interest with that of a standard population. In this case, our interest is to find out whether the number of dry season malaria cases in each catchment area are greater than we would expect given the malaria rate for the entire Kasungu district.

We do this by comparing what we observe (O) with what we would expect (E) if the risk of malaria was equal throughout Kasungu. The SMR of catchment i can be calculated as follows: \[SMR_i = \frac{O_i}{E_i}\]

SMRs above 1 represent high risk of dry season malaria and SMRs below 1, viceversa.

# Calculate the ratio of observed to expected (SMR) ----------------------------
SMR_2017 <- expected_malaria_2017 |>
  dplyr::mutate(SMR = round(observed_2017/expected_2017, 1)) |> 
  dplyr::select(rowID,Names, pop_2017, observed_2017, expected_2017, SMR) 

SMR_2018 <- expected_malaria_2018 |> 
  dplyr::mutate(SMR = round(observed_2018/expected_2018, 1)) |> 
  dplyr::select(rowID, Names, pop_2018, observed_2018, expected_2018, SMR) 

SMR_2019 <- expected_malaria_2019 |> 
  dplyr::mutate(SMR = round(observed_2019/expected_2019, 1)) |> 
  dplyr::select(rowID, Names, pop_2019, observed_2019, expected_2019, SMR) 

SMR_2020 <- expected_malaria_2020 |> 
  dplyr::mutate(SMR = round(observed_2020/expected_2020, 1)) |> 
  dplyr::select(rowID, Names, pop_2020, observed_2020, expected_2020, SMR)


# Create SMR tables ------------------------------------------------------------
SMR_table_2017 <- SMR_2017 |>
  dplyr::as_tibble() |>
  dplyr::rename(Health_facility = Names) |> 
  dplyr::select(-rowID, -geometry) |> 
  kable() |>
  kableExtra::kable_styling(full_width = FALSE)

SMR_table_2018 <- SMR_2018 |> 
  dplyr::as_tibble() |> 
  dplyr::rename(Health_facility = Names) |> 
  dplyr::select(-rowID, -geometry) |>
  kable() |> 
  kableExtra::kable_styling(full_width = FALSE)

SMR_table_2019 <- SMR_2019 |>
  dplyr::as_tibble() |>
  dplyr::rename(Health_facility = Names) |> 
  dplyr::select(-rowID, -geometry) |> 
  kable () |> 
  kableExtra::kable_styling(full_width = FALSE)

SMR_table_2020 <- SMR_2020 |>
  dplyr::as_tibble() |> 
  dplyr::rename(Health_facility = Names) |> 
  dplyr::select(-rowID, -geometry) |> 
  kable () |> 
  kableExtra::kable_styling(full_width = FALSE)

SMR_table_2017
Health_facility pop_2017 observed_2017 expected_2017 SMR
Lodjwa Health Centre 9923 564 826 0.7
Nkhamenya Rural Hospital 40154 2720 3344 0.8
Newa Mpasazi Health Centre 13879 216 1156 0.2
Mpepa /Chisinga Health Centre 27459 1523 2287 0.7
Mnyanja Health Centre 39950 1480 3327 0.4
Simlemba Health Centre 26999 1159 2249 0.5
Ofesi Health Centre 28098 1930 2340 0.8
Chulu Health Centre 27906 3482 2324 1.5
Kapelula Health Centre 35727 2970 2976 1.0
Livwezi Health Centre 22009 594 1833 0.3
Gogode Dispensary 13061 1553 1088 1.4
Dwangwa Dispensary 32704 1153 2724 0.4
Chamama Health Facility 20026 1005 1668 0.6
Wimbe Health Centre 11864 2558 988 2.6
Chinyama 12768 1140 1063 1.1
Mdunga Health Centre 18177 1382 1514 0.9
Mtunthama Health Centre 18744 1982 1561 1.3
Kasungu District Hospital 143490 14663 11951 1.2
Chamwabvi Health Centre 35353 2031 2945 0.7
Linyangwa Health Centre 17772 1987 1480 1.3
Mziza Health Centre 44295 4098 3689 1.1
Kawamba Health Centre 26385 3845 2198 1.7
Kamboni Health Centre 21226 2588 1768 1.5
Khola Health Centre 22664 1012 1888 0.5
Santhe Health Centre 43948 5668 3660 1.5
Mkhota Health Centre 23295 1487 1940 0.8
SMR_table_2018
Health_facility pop_2018 observed_2018 expected_2018 SMR
Lodjwa Health Centre 10281 1151 934 1.2
Nkhamenya Rural Hospital 41642 3343 3785 0.9
Newa Mpasazi Health Centre 14248 434 1295 0.3
Mpepa /Chisinga Health Centre 28488 2616 2589 1.0
Mnyanja Health Centre 41856 1715 3804 0.5
Simlemba Health Centre 27455 1506 2496 0.6
Ofesi Health Centre 29002 1773 2636 0.7
Chulu Health Centre 28832 3330 2621 1.3
Kapelula Health Centre 37630 3480 3420 1.0
Livwezi Health Centre 22544 1128 2049 0.6
Gogode Dispensary 13368 2550 1215 2.1
Dwangwa Dispensary 33534 1216 3048 0.4
Chamama Health Facility 20372 1226 1852 0.7
Wimbe Health Centre 11814 3167 1074 2.9
Chinyama 13138 1673 1194 1.4
Mdunga Health Centre 18928 1894 1720 1.1
Mtunthama Health Centre 19074 3358 1734 1.9
Kasungu District Hospital 147175 12019 13377 0.9
Chamwabvi Health Centre 36167 2079 3287 0.6
Linyangwa Health Centre 18032 1500 1639 0.9
Mziza Health Centre 46313 2291 4210 0.5
Kawamba Health Centre 26253 3881 2386 1.6
Kamboni Health Centre 21430 3250 1948 1.7
Khola Health Centre 23195 1697 2108 0.8
Santhe Health Centre 45063 6195 4096 1.5
Mkhota Health Centre 23884 4218 2171 1.9
SMR_table_2019
Health_facility pop_2019 observed_2019 expected_2019 SMR
Lodjwa Health Centre 10608 1168 909 1.3
Nkhamenya Rural Hospital 43293 3932 3709 1.1
Newa Mpasazi Health Centre 14780 626 1266 0.5
Mpepa /Chisinga Health Centre 29456 4169 2523 1.7
Mnyanja Health Centre 43783 2504 3751 0.7
Simlemba Health Centre 28076 1788 2405 0.7
Ofesi Health Centre 30065 2124 2576 0.8
Chulu Health Centre 29731 3537 2547 1.4
Kapelula Health Centre 39747 3357 3405 1.0
Livwezi Health Centre 22945 435 1966 0.2
Gogode Dispensary 13641 1469 1169 1.3
Dwangwa Dispensary 34415 1370 2948 0.5
Chamama Health Facility 20701 1127 1773 0.6
Wimbe Health Centre 11855 2162 1016 2.1
Chinyama 13475 1260 1154 1.1
Mdunga Health Centre 19960 1485 1710 0.9
Mtunthama Health Centre 19385 1718 1661 1.0
Kasungu District Hospital 151079 13052 12942 1.0
Chamwabvi Health Centre 36899 1180 3161 0.4
Linyangwa Health Centre 18279 2692 1566 1.7
Mziza Health Centre 48452 3135 4151 0.8
Kawamba Health Centre 26356 3469 2258 1.5
Kamboni Health Centre 21509 2537 1843 1.4
Khola Health Centre 23816 2139 2040 1.0
Santhe Health Centre 46196 5793 3957 1.5
Mkhota Health Centre 24429 2268 2093 1.1
SMR_table_2020
Health_facility pop_2020 observed_2020 expected_2020 SMR
Lodjwa Health Centre 13081 1788 1538 1.2
Nkhamenya Rural Hospital 53692 8539 6313 1.4
Newa Mpasazi Health Centre 18311 2182 2153 1.0
Mpepa /Chisinga Health Centre 36317 5186 4270 1.2
Mnyanja Health Centre 54649 6117 6426 1.0
Simlemba Health Centre 34240 5310 4026 1.3
Ofesi Health Centre 37240 2323 4379 0.5
Chulu Health Centre 36638 7160 4308 1.7
Kapelula Health Centre 50214 7297 5904 1.2
Livwezi Health Centre 27786 1028 3267 0.3
Gogode Dispensary 16681 2767 1961 1.4
Dwangwa Dispensary 42282 2869 4971 0.6
Chamama Health Facility 25248 635 2969 0.2
Wimbe Health Centre 14367 2233 1689 1.3
Chinyama 16463 1605 1936 0.8
Mdunga Health Centre 25108 3169 2952 1.1
Mtunthama Health Centre 23501 1882 2763 0.7
Kasungu District Hospital 185282 19393 21785 0.9
Chamwabvi Health Centre 45106 1128 5304 0.2
Linyangwa Health Centre 22144 4380 2604 1.7
Mziza Health Centre 60648 5791 7131 0.8
Kawamba Health Centre 32076 7073 3771 1.9
Kamboni Health Centre 25750 4665 3028 1.5
Khola Health Centre 29367 3426 3453 1.0
Santhe Health Centre 56704 6556 6667 1.0
Mkhota Health Centre 29986 4592 3526 1.3

7.1 View observed and expected dry season malaria cases

# Helper function to create maps of observed and expected dry season malaria cases
create.malaria.map <- function(malaria.data, 
                               variable = NA, 
                               title = NA, 
                               legend.title = NA){
  # observed and expected malaria incidence map
  # malaria.data: data frame containing observed and expected malaria cases
  # variable: variable name (as character, in quotes e.g. "observed")
  # title: map title in quotes
  # legend.title: legend title in quotes
  # returns:
  #   a tmap-element (plots a map)
  tm_shape(malaria.data)+
    tm_fill(col = variable, 
            breaks = c(0, 500, 1000, 2500, 5000, 10000, 15000, 20000, 25000),
            palette = "YlOrRd",
            title = legend.title)+
    tm_borders(lw = 0.3)+
    tm_layout(legend.position = c(0.75,"bottom"),
              legend.text.size = 0.5,
              legend.title.size = 0.7,
              frame = FALSE)+
    tm_credits(title, 
               position = c(0.2, 0.8), 
               size = 1)
}

# Invoking the function
# 2017 observed and expected malaria cases -------------------------------------
observed_malaria_2017_map <- create.malaria.map(malaria_pop_by_catchment_2017, 
                                                variable = "dr_2017",
                                                title = "2017",
                                                legend.title = "Observed malaria")

expected_malaria_2017_map <- create.malaria.map(expected_malaria_2017,
                                                variable = "expected_2017",
                                                title = "2017",
                                                legend.title = "Expected malaria")

# 2018 observed and expected malaria cases -------------------------------------
observed_malaria_2018_map <- create.malaria.map(malaria_pop_by_catchment_2018,
                                                variable = "dr_2018",
                                                title = "2018",
                                                legend.title = "Observed malaria")

expected_malaria_2018_map <- create.malaria.map(expected_malaria_2018,
                                                variable = "expected_2018",
                                                title = "2018",
                                                legend.title = "Expected malaria")

# 2019 observed and expected malaria cases -------------------------------------
observed_malaria_2019_map <- create.malaria.map(malaria_pop_by_catchment_2019,
                                                variable = "dr_2019",
                                                title = "2019",
                                                legend.title = "Observed malaria")

expected_malaria_2019_map <- create.malaria.map(expected_malaria_2019,
                                                variable = "expected_2019",
                                                title = "2019",
                                                legend.title = "Expected malaria")

# 2020 observed and expected malaria cases -------------------------------------
observed_malaria_2020_map <- create.malaria.map(malaria_pop_by_catchment_2020,
                                                variable = "dr_2020",
                                                title = "2020",
                                                legend.title = "Observed malaria")

expected_malaria_2020_map <- create.malaria.map(expected_malaria_2020,
                                                variable = "expected_2020",
                                                title = "2020",
                                                legend.title = "Expected malaria")

# Layout maps ------------------------------------------------------------------
tmap::tmap_arrange(observed_malaria_2017_map, expected_malaria_2017_map,
                   observed_malaria_2018_map, expected_malaria_2018_map, 
                   observed_malaria_2019_map, expected_malaria_2019_map,
                   observed_malaria_2020_map, expected_malaria_2020_map, ncol = 2)
Fig. 7: Observed and expected malaria incidence by health facility catchment area, Kasungu

Fig. 7: Observed and expected malaria incidence by health facility catchment area, Kasungu

7.2 View SMR by catchment

A ratio greater than 1.0 indicates that more malaria cases have occurred than would have been expected, while a ratio less than 1.0 indicates that less cases have occurred. This means that, catchments with SMRs above 1 have high dry season malaria risk.

# max(SMR_2017$SMR)
# [1] 2.6
# max(SMR_2018$SMR)
# [1] 2.9
# max(SMR_2019$SMR)
# [1] 2.1
# max(SMR_2020$SMR)
# [1] 1.9

# Define function to create maps of SMR by catchment ---------------------------
create.smr.map <- function(smr.data, 
                           variable = "SMR_category", 
                           title = NA, 
                           legend.title = "SMR"){
  # SMR by catchment map
  # smr.data: sf polygon object containing SMR by catchment data
  # variable: variable name (as character, in qoutes)
  # title: map title in quotes
  # legend.title: legend title in qoutes
  # returns:
  #   a tmap-element (plots a map)
  
  # create category column
  smr.data$SMR_category <- NA
  
  # assigning labels for the SMR estimate legends
  smr.category.list <- c("<0.50", "0.51 to 0.75", "0.76 to 0.99", "1.00", 
                         "1.10 to 1.24", "1.25 to 1.49", ">1.50")
  
  # assigning categories
  smr.data$SMR_category[smr.data$SMR >= 0.00 & smr.data$SMR < 0.49] = -3
  smr.data$SMR_category[smr.data$SMR >= 0.50 & smr.data$SMR < 0.75] = -2
  smr.data$SMR_category[smr.data$SMR >= 0.76 & smr.data$SMR < 0.99] = -1
  smr.data$SMR_category[smr.data$SMR >= 1.00 & smr.data$SMR < 1.09] = 0
  smr.data$SMR_category[smr.data$SMR >= 1.10 & smr.data$SMR < 1.24] = 1
  smr.data$SMR_category[smr.data$SMR >= 1.25 & smr.data$SMR < 1.49] = 2
  smr.data$SMR_category[smr.data$SMR >= 1.50 & smr.data$SMR < 3.00] = 3
  
  # generating divergent colour schemes [Blues - Light Blues – White – Light Reds – Reds]
  # smr.palette <- c("#33a6fe", "#cbe6fe", "#dfeffe", "#feb1b1", "#fe8e8e","#fe0000")
  
  tm_shape(smr.data)+
    tm_fill(col = variable, 
            style = "cat",
            palette = "-RdBu",
            title = legend.title,
            labels = smr.category.list)+
    tm_borders(lw = 0.6)+
    tm_layout(legend.position = c(0.75,"bottom"),
              legend.text.size = 0.5,
              legend.title.size = 0.7,
              frame = FALSE)+
    tm_credits(title, 
               position = c(0.2, 0.8), 
               size = 1)
}

# Invoking function ------------------------------------------------------------
SMR_2017_map <- create.smr.map(SMR_2017, title = "2017")

SMR_2018_map <- create.smr.map(SMR_2018, title = "2018")

SMR_2019_map <- create.smr.map(SMR_2019, title = "2019")

SMR_2020_map <- create.smr.map(SMR_2020, title = "2020")

# Layout maps ------------------------------------------------------------------
tmap::tmap_arrange(SMR_2017_map, SMR_2018_map, SMR_2019_map, SMR_2020_map, ncol = 2)
Fig. 8: Standardised morbidity ratio of malaria by health facility catchment

Fig. 8: Standardised morbidity ratio of malaria by health facility catchment

8 Calculate the proportion of the catchment population living within 1km, 2km, 3km of water bodies

First, using st_buffer, we compute 1km, 2km and 3km buffers around dry season water bodies obtained from LandSat satellite imagery using TropWet tool in Google Earth Engine. Then, geometry of the buffer features are then combined resulting in resolved internal boundaries to enable extracting population values in buffers from the WorldPop raster. Finally, we calculate the proportion of people in each catchment area living within water bodies.

# Combine and transform TropWet derived waterbody polygons -------------------------------

surface_waterbodies_2017 <- sf::st_buffer(dryseason_waterbodies_2017, dist = 30) |>
  sf::st_union() |>
  sf::st_cast("POLYGON") |>
  sf::st_as_sf()

surface_waterbodies_2018 <- sf::st_buffer(dryseason_waterbodies_2018, dist = 30) |>
  sf::st_union() |>
  sf::st_cast("POLYGON") |>
  sf::st_as_sf()

surface_waterbodies_2019 <- sf::st_buffer(dryseason_waterbodies_2019, dist = 30) |>
  sf::st_union() |>
  sf::st_cast("POLYGON") |>
  sf::st_as_sf()

surface_waterbodies_2020 <- sf::st_buffer(dryseason_waterbodies_2020, dist = 30) |>
  sf::st_union() |>
  sf::st_cast("POLYGON") |>
  sf::st_as_sf()


# Helper function to compute 1km, 2km and 3km buffers around the water bodies ---------------------

create.waterbody.buffer <- function(waterbody, distance, catchment){
  # function for creating buffers around waterbodies
  # arguments:
  #   waterbody:  waterbody shapefile
  #   distance: buffer distance in meters
  #   catchment: catchment area shapefile
  # returns:
  #   buffered waterbodies 
  
  # Create buffers around water bodies
  buffer_radius <- sf::st_buffer(waterbody, distance)
  
  # Dissolve the buffers
  # buffer_union <- sf::st_as_sf(st_cast(st_union(buffer_radius),"MULTIPOLYGON"))
  buffer_union <- sf::st_union(buffer_radius) |>
    sf::st_cast("MULTIPOLYGON") |>
    sf::st_as_sf()
  
  # Assign attributes of the 'catchment' to each of the water bodies. 
   buffer_intersect <- sf::st_intersection(buffer_union, catchment)
  
   buffer_intersect_sf <- sf::st_as_sf(buffer_intersect)
   
  # Convert the MULTIPOLYGON object into several POLYGON objects
   buffer_intersect_polygons <- sf::st_buffer(buffer_intersect_sf, 0.0) |>
     sf::st_cast("MULTIPOLYGON") |>
     sf::st_cast("POLYGON")
   
  
  # Polygons being seen to be in multiple catchments
   sf::st_intersects(buffer_intersect_polygons, catchment)
  
  # Make the assumption that the attribute is constant throughout the geometry
   sf::st_agr(buffer_intersect_polygons) = "constant"
   
   sf::st_agr(catchment) = "constant"
  
  return(out = buffer_intersect_polygons)
}


# Invoking function
# For 2017 TropWet surface water polygons --------------------------------------------------------
buffer_1km_2017 <- create.waterbody.buffer(waterbody = surface_waterbodies_2017, 
                                           distance = 1000, 
                                           catchment = malire_new)

buffer_2km_2017 <- create.waterbody.buffer(waterbody = surface_waterbodies_2017, 
                                           distance = 2000, 
                                           catchment = malire_new)

buffer_3km_2017 <- create.waterbody.buffer(waterbody = surface_waterbodies_2017, 
                                           distance = 3000,
                                           catchment = malire_new)

# For 2018 TropWet surface water polygons --------------------------------------------------------
buffer_1km_2018 <- create.waterbody.buffer(waterbody = surface_waterbodies_2018, 
                                           distance = 1000, 
                                           catchment = malire_new)

buffer_2km_2018 <- create.waterbody.buffer(waterbody = surface_waterbodies_2018, 
                                           distance = 2000, 
                                           catchment = malire_new)

buffer_3km_2018 <- create.waterbody.buffer(waterbody = surface_waterbodies_2018, 
                                           distance = 3000, 
                                           catchment = malire_new)
 
# For 2019 TropWet surface water polygons ------------------------------------------------------
buffer_1km_2019 <- create.waterbody.buffer(waterbody = surface_waterbodies_2019, 
                                           distance = 1000, 
                                           catchment = malire_new)

buffer_2km_2019 <- create.waterbody.buffer(waterbody = surface_waterbodies_2019, 
                                           distance = 2000, 
                                           catchment = malire_new)

buffer_3km_2019 <- create.waterbody.buffer(waterbody = surface_waterbodies_2019, 
                                           distance = 3000, 
                                           catchment = malire_new)

# For 2020 TropWet surface water polygons ------------------------------------------------------
buffer_1km_2020 <- create.waterbody.buffer(waterbody = surface_waterbodies_2020, 
                                           distance = 1000, 
                                           catchment = malire_new)

buffer_2km_2020 <- create.waterbody.buffer(waterbody = surface_waterbodies_2020, 
                                           distance = 2000, 
                                           catchment = malire_new)

buffer_3km_2020 <- create.waterbody.buffer(waterbody = surface_waterbodies_2020, 
                                           distance = 3000, 
                                           catchment = malire_new)

8.1 View the created waterbody buffers

Note that blank areas in the map represent catchment areas in which no water body was detected — this may be a limitation of using moderate resolution satellite imagery (>30m spatial resolution) to identify surface water.

# Map the buffers
create.buffer.map <- function(buffers, boundary = malire_new, title = NA){
  # function for creating buffer map in ggplot
  # arguments:
  #   buffer:  waterbodies buffer polygon layer
  #   boundary: health facility catchment polygons
  #   title: main title
  # returns:
  #   a map-element (plots a map)
  ggplot(data = buffers)+
     geom_sf()+
     geom_sf(data = boundary, 
             fill = NA)+
     theme_void()+
     labs(title = title)
}

# Invoking the function
# For 2017 -------------------------------------------------------------------------------
buffer_1km_2017_map <- create.buffer.map(buffer_1km_2017, title = "2017: 1km Buffers")

buffer_2km_2017_map <- create.buffer.map(buffer_2km_2017, title = "2017: 2km Buffers")

buffer_3km_2017_map <- create.buffer.map(buffer_3km_2017, title = "2017: 3km Buffers")

# For 2018 --------------------------------------------------------------------------------
buffer_1km_2018_map <- create.buffer.map(buffer_1km_2018, title = "2018: 1km Buffers")

buffer_2km_2018_map <- create.buffer.map(buffer_2km_2018, title = "2018: 2km Buffers")

buffer_3km_2018_map <- create.buffer.map(buffer_3km_2018, title = "2018: 3km Buffers")

# For 2019 ---------------------------------------------------------------------------------
buffer_1km_2019_map <- create.buffer.map(buffer_1km_2019, title = "2019: 1km Buffers")

buffer_2km_2019_map <- create.buffer.map(buffer_2km_2019, title = "2019: 2km Buffers")

buffer_3km_2019_map <- create.buffer.map(buffer_3km_2019, title = "2019: 3km Buffers")

# For 2020 --------------------------------------------------------------------------------
buffer_1km_2020_map <- create.buffer.map(buffer_1km_2020, title = "2020: 1km Buffers")

buffer_2km_2020_map <- create.buffer.map(buffer_2km_2020, title = "2020: 2km Buffers")

buffer_3km_2020_map <- create.buffer.map(buffer_3km_2020, title = "2020: 3km Buffers")
 
grid.arrange(buffer_1km_2017_map, buffer_1km_2018_map, buffer_1km_2019_map, buffer_1km_2020_map,
             buffer_2km_2017_map, buffer_2km_2018_map, buffer_2km_2019_map, buffer_2km_2020_map, 
             buffer_3km_2017_map, buffer_3km_2018_map, buffer_3km_2019_map, buffer_3km_2020_map, ncol = 4)
Fig 9. Buffers around dry season waterbodies in Kasungu

Fig 9. Buffers around dry season waterbodies in Kasungu

9 Extract the population living within waterbody buffers by catchment area

# Helper function to calculate estimated number of people living within waterbody buffers
# in each catchment area
estimate.buffer.pop <- function(catchment.population, buffers, catchment.area){
  
  # Extract population estimates from WorldPop raster
  buffers$buffer_pop <- raster::extract(catchment.population,
                                        buffers, 
                                        fun = sum, 
                                        na.rm = TRUE)
                                               
                                              
  # Find which catchment each polygon belongs to using its centroid - a point dataset 
  # representing the geographic center-points of the polygons 
  # buffer_by_catchment <- st_intersection(st_centroid(buffers), catchment.area)
  buffer_by_catchment <- sf::st_centroid(buffers) |>
    sf::st_intersection(catchment.area)
  
  # Notice that the buffer_catchment is comprised of separate POLYGONS (buffer_by_catchment$x). 
  # The first step is to “dissolve” away these POLYGONS into one MULTIPOLYGON. 
  # There is no sf equivalent to the QGIS or ArcMap “dissolve” operation. 
  # Instead we use a combination of group_by and summarize from the dplyr package. 
  # Stats::aggregate from sf package, and dplyr::summarize both do essentially the same.
   buffer_pop_aggregated <- buffer_by_catchment |> 
     dplyr::group_by(DN) |>
     dplyr::summarize(buffer_pop_aggregated = round(sum(buffer_pop, na.rm = TRUE)))

  buffer_pop <- merge(catchment.area, st_drop_geometry(buffer_pop_aggregated),
                      by = 'DN', all.x = TRUE)
  
  
  
  return(out = buffer_pop)
  
}

# Invoking the function and calculating proportion of 
# catchment population living within buffers
# 2017 buffer population -------------------------------------------------------
buffer_pop_1km_2017 <- estimate.buffer.pop(
  kasungu_population_2017, 
  buffer_1km_2017, 
   malaria_pop_by_catchment_2017) |> 
  dplyr::rename(catchment_pop = pop,
                buffer_pop = buffer_pop_aggregated) |>
  dplyr::mutate(
    prop_buffer_catchment_pop = round((buffer_pop/catchment_pop)*100))|> 
  dplyr::mutate(across(everything(), .fns = ~replace_na(.,0))) 

buffer_pop_2km_2017 <- estimate.buffer.pop(
  kasungu_population_2017,
  buffer_2km_2017,
  malaria_pop_by_catchment_2017) |>
  dplyr::rename(catchment_pop = pop,
                buffer_pop = buffer_pop_aggregated) |>
  dplyr::mutate(
    prop_buffer_catchment_pop = round((buffer_pop/catchment_pop)*100)) |> 
  dplyr::mutate(across(everything(), .fns = ~replace_na(.,0)))

buffer_pop_3km_2017 <- estimate.buffer.pop(
  kasungu_population_2017,
  buffer_3km_2017,
  malaria_pop_by_catchment_2017) |> 
  dplyr::rename(catchment_pop = pop,
                buffer_pop = buffer_pop_aggregated) |>
  dplyr::mutate(
    prop_buffer_catchment_pop = round((buffer_pop/catchment_pop)*100))|> 
  dplyr::mutate(across(everything(), .fns = ~replace_na(.,0)))

# 2018 buffer population -------------------------------------------------------
buffer_pop_1km_2018 <- estimate.buffer.pop(
  kasungu_population_2018,
  buffer_1km_2018,
  malaria_pop_by_catchment_2018) |> 
  dplyr::rename(catchment_pop = pop,
                buffer_pop = buffer_pop_aggregated) |> 
  dplyr::mutate(
    prop_buffer_catchment_pop = round((buffer_pop/catchment_pop)*100))|> 
  dplyr::mutate(across(everything(), .fns = ~replace_na(.,0)))

buffer_pop_2km_2018 <- estimate.buffer.pop(
  kasungu_population_2018,
  buffer_2km_2018,
  malaria_pop_by_catchment_2018) |> 
  dplyr::rename(catchment_pop = pop,
                buffer_pop = buffer_pop_aggregated) |>
  dplyr::mutate(
    prop_buffer_catchment_pop = round((buffer_pop/catchment_pop)*100))|> 
  dplyr::mutate(across(everything(), .fns = ~replace_na(.,0)))

buffer_pop_3km_2018 <- estimate.buffer.pop(
  kasungu_population_2018,
  buffer_3km_2018,
  malaria_pop_by_catchment_2018) |> 
  dplyr::rename(catchment_pop = pop,
                buffer_pop = buffer_pop_aggregated) |> 
  dplyr::mutate(
    prop_buffer_catchment_pop = round((buffer_pop/catchment_pop)*100))|> 
  dplyr::mutate(across(everything(), .fns = ~replace_na(.,0)))

# 2019 buffer population -------------------------------------------------------
buffer_pop_1km_2019 <- estimate.buffer.pop(
  kasungu_population_2019,
  buffer_1km_2019,
  malaria_pop_by_catchment_2019) |> 
  dplyr::rename(catchment_pop = pop,
                buffer_pop = buffer_pop_aggregated) |> 
  dplyr::mutate(
    prop_buffer_catchment_pop = round((buffer_pop/catchment_pop)*100))|> 
  dplyr::mutate(across(everything(), .fns = ~replace_na(.,0)))

buffer_pop_2km_2019 <- estimate.buffer.pop(
  kasungu_population_2019,
  buffer_2km_2019,
  malaria_pop_by_catchment_2019) |>
  dplyr::rename(catchment_pop = pop,
                buffer_pop = buffer_pop_aggregated) |> 
  dplyr::mutate(
    prop_buffer_catchment_pop = round((buffer_pop/catchment_pop)*100))|> 
  dplyr::mutate(across(everything(), .fns = ~replace_na(.,0))) # replace NA with zero

buffer_pop_3km_2019 <- estimate.buffer.pop(
  kasungu_population_2019,
  buffer_3km_2019,
  malaria_pop_by_catchment_2019) |> 
  dplyr::rename(catchment_pop = pop,
                buffer_pop = buffer_pop_aggregated) |>
  dplyr::mutate(
    prop_buffer_catchment_pop = round((buffer_pop/catchment_pop)*100))|> 
  dplyr::mutate(across(everything(), .fns = ~replace_na(.,0)))

# 2020 buffer population -------------------------------------------------------
buffer_pop_1km_2020 <- estimate.buffer.pop(
  kasungu_population_2020,
  buffer_1km_2020,
  malaria_pop_by_catchment_2020) |>
  dplyr::rename(catchment_pop = pop,
                buffer_pop = buffer_pop_aggregated) |> 
  dplyr::mutate(
    prop_buffer_catchment_pop = round((buffer_pop/catchment_pop)*100))|> 
  dplyr::mutate(across(everything(), .fns = ~replace_na(.,0)))

buffer_pop_2km_2020 <- estimate.buffer.pop(
  kasungu_population_2020,
  buffer_2km_2020,
  malaria_pop_by_catchment_2020) |> 
  dplyr::rename(catchment_pop = pop,
                buffer_pop = buffer_pop_aggregated) |> 
  dplyr::mutate(
    prop_buffer_catchment_pop = round((buffer_pop/catchment_pop)*100))|> 
  dplyr::mutate(across(everything(), .fns = ~replace_na(.,0)))

buffer_pop_3km_2020 <- estimate.buffer.pop(
  kasungu_population_2020,
  buffer_3km_2020,
  malaria_pop_by_catchment_2020) |> 
  dplyr::rename(catchment_pop = pop,
                buffer_pop = buffer_pop_aggregated) |> 
  dplyr::mutate(
    prop_buffer_catchment_pop = round((buffer_pop/catchment_pop)*100)) |> 
  dplyr::mutate(across(everything(), .fns = ~replace_na(.,0)))

9.1 Mapping proportion of catchment population living within waterbodies

# Helper function to create maps of proportion of people living in proximity ----------
# to water bodies in each catchment area
create.pop.proportion.map <- function(pop.data, 
                                      variable = "prop_buffer_catchment_pop", 
                                      title = NA, 
                                      legend.title = NA){
 
  # pop.data: sf polygon object containing proportion of catchment population 
  #           living within water bodies
  # variable: variable name (as character, in qoutes)
  # title: map title in quotes
  # legend.title: legend title in qoutes
  # returns:
  #   a tmap-element (plots a map)
 
  tm_shape(pop.data)+
    tm_fill(col = variable, 
            breaks = c(0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100),
            palette = "YlOrBr",
            title = legend.title)+
    tm_borders(lw = 0.3)+
    tm_layout(legend.position = c(0.8,"bottom"),
              legend.text.size = 0.5,
              legend.title.size = 0.7,
              frame = FALSE)+
    tm_credits(title, 
               position = c(0.25, 0.75), 
               size = 1)
}

# Invoking function 
# 2017 population proportion ---------------------------------------------------
pop_proportion_1km_2017_map <- create.pop.proportion.map(
  buffer_pop_1km_2017, 
  title = "2017",
  legend.title = "Population within \n1km buffers (%)")

pop_proportion_2km_2017_map <- create.pop.proportion.map(
  buffer_pop_2km_2017, 
  title = "2017",
  legend.title = "Population within \n2km buffers (%)")

pop_proportion_3km_2017_map <- create.pop.proportion.map(
  buffer_pop_3km_2017,
  title = "2017",
  legend.title = "Population within \n3km buffers (%)")

# 2018 population proportion ---------------------------------------------------
pop_proportion_1km_2018_map <- create.pop.proportion.map(
  buffer_pop_1km_2018,
  title = "2018",
  legend.title = "Population within \n1km buffers (%)")

pop_proportion_2km_2018_map <- create.pop.proportion.map(
  buffer_pop_2km_2018,
  title = "2018",
  legend.title = "Population within \n2km buffers (%)")

pop_proportion_3km_2018_map <- create.pop.proportion.map(
  buffer_pop_3km_2018,
  title = "2018",
  legend.title = "Population within \n3km buffers (%)")

# 2019 population proportion ---------------------------------------------------
pop_proportion_1km_2019_map <- create.pop.proportion.map(
  buffer_pop_1km_2019,
  title = "2019",
  legend.title = "Population within \n1km buffers (%)")

pop_proportion_2km_2019_map <- create.pop.proportion.map(
  buffer_pop_2km_2019,
  title = "2019",
  legend.title = "Population within \n2km buffers (%)")

pop_proportion_3km_2019_map <- create.pop.proportion.map(
  buffer_pop_3km_2019,
  title = "2019",
  legend.title = "Population within \n3km buffers (%)")

# 2020 population proportion ---------------------------------------------------
pop_proportion_1km_2020_map <- create.pop.proportion.map(
  buffer_pop_1km_2020,
  title = "2020",
  legend.title = "Population within \n1km buffers (%)")

pop_proportion_2km_2020_map <- create.pop.proportion.map(
  buffer_pop_2km_2020,
  title = "2020",
  legend.title = "Population within \n2km buffers (%)")

pop_proportion_3km_2020_map <- create.pop.proportion.map(
  buffer_pop_3km_2020,
  title = "2020",
  legend.title = "Population within \n3km buffers (%)")

# Layout maps ------------------------------------------------------------------
tmap::tmap_arrange(pop_proportion_1km_2017_map, pop_proportion_2km_2017_map, 
                   pop_proportion_3km_2017_map, pop_proportion_1km_2018_map,
                   pop_proportion_2km_2018_map, pop_proportion_3km_2018_map,
                   pop_proportion_1km_2019_map, pop_proportion_2km_2019_map,
                   pop_proportion_3km_2019_map, pop_proportion_1km_2020_map,
                   pop_proportion_2km_2020_map, pop_proportion_3km_2020_map, ncol = 3)
Fig. 10. Proportion of catchment population living around water bodies

Fig. 10. Proportion of catchment population living around water bodies

10 Scatter plots of SMR against the proportion of the catchment population living waterbody buffers

A correlation coeeficient of more than zero (cor.coeff r > 0.1) indicates some positive association between the SMR and the buffer population variables. That is, SMR of dry season malaria increases with increase in number of people surrounding water bodies. This implies that as the population of people living near water bodies increases, the risk of dry season malaria increases as well.

# Helper function to tidy and bind the SMR and proportion of -------------------
# buffer-catchment population data frames
tidy.data <- function(smr.df, 
                      proportion.pop.1km, 
                      proprotion.pop.2km,
                      proportion.pop.3km){

# Convert the sf objects to data frames-------------------------------------------
smr_df <- as.data.frame(smr.df) |> 
  dplyr::select(rowID, Names, SMR)

proportion_pop_1km_df <- as.data.frame(proportion.pop.1km) |> 
  dplyr::select(rowID, prop_pop_1km = `prop_buffer_catchment_pop`)

proportion_pop_2km_df <- as.data.frame(proprotion.pop.2km) |> 
  dplyr::select(rowID, prop_pop_2km = `prop_buffer_catchment_pop`)

proportion_pop_3km_df <- as.data.frame(proportion.pop.3km) |>
  dplyr::select(rowID, prop_pop_3km = `prop_buffer_catchment_pop`)

# Merge SMR and population data frames -----------------------------------------
combined_1 <- merge(smr_df, proportion_pop_1km_df, by = "rowID", all = TRUE)

combined_2 <- merge(proportion_pop_2km_df, proportion_pop_3km_df)

combined_fully <- merge(combined_1, combined_2, by = "rowID", all = TRUE)

}

# Invoking the function --------------------------------------------------------
smr_pop_2017 <- tidy.data(SMR_2017, buffer_pop_1km_2017, buffer_pop_2km_2017, buffer_pop_3km_2017)

smr_pop_2018 <- tidy.data(SMR_2018, buffer_pop_1km_2018, buffer_pop_2km_2018, buffer_pop_3km_2018)

smr_pop_2019 <- tidy.data(SMR_2019, buffer_pop_1km_2019, buffer_pop_2km_2019, buffer_pop_3km_2019)

smr_pop_2020 <- tidy.data(SMR_2020, buffer_pop_1km_2020, buffer_pop_2km_2020, buffer_pop_3km_2020)

# Helper function to create scatter plots --------------------------------------
create.scatter.plot <- function(smr.pop.df, 
                                independent.var = NA,
                                dependent.var = "SMR",
                                x.label = NA,
                                plot.title = NA){
  
  scatter.plot <- ggpubr::ggscatter(smr.pop.df,          # data frame
                                    x = independent.var, # x-axis variable
                                    y = dependent.var,   # y-axis variable
                                    add = "reg.line",    # Add regression line
                                    conf.int = TRUE,     # Add confidence interval
                                    add.params = list(color = "red",
                                                      fill = "lightgray"),
                                    palette = "jco",     # journal color palette. see ?ggpar
                                    xlab = x.label,      # x-axis label
                                    ylab = "SMR",        # y-axis label
                                    title = plot.title)+    
                  ggpubr::stat_cor(label.y = 3)+         # Add correlation coefficient
                  ggpubr::font("title", size = 10, face = "bold")+
                  ggpubr::font("xlab", size = 10)+
                  ggpubr::font("ylab", size = 10)
 
  return(scatter.plot)
  
}

# Invoking function 
# 2017 scatter plots ------------------------------------------------------------

scatter_1km_2017 <- create.scatter.plot(
  smr_pop_2017, independent.var = "prop_pop_1km",
  x.label = "Percentage of catchment population \nliving in 1km buffer",
  plot.title = "2017")

scatter_2km_2017 <- create.scatter.plot(
  smr_pop_2017, independent.var = "prop_pop_2km",
  x.label = "Percentage of catchment population \nliving in 2km buffer",
  plot.title = "2017")

scatter_3km_2017 <- create.scatter.plot(
  smr_pop_2017, independent.var = "prop_pop_3km",
  x.label = "Percentage of catchment population \nliving in 3km buffer",
  plot.title = "2017")

# 2018 scatter plots -----------------------------------------------------------
scatter_1km_2018 <- create.scatter.plot(
  smr_pop_2018, independent.var = "prop_pop_1km",
  x.label = "Percentage of catchment population \nliving in 1km buffer",
  plot.title = "2018")

scatter_2km_2018 <- create.scatter.plot(
  smr_pop_2018, independent.var = "prop_pop_2km",
  x.label = "Percentage of catchment population \nliving in 2km buffer",
  plot.title = "2018")

scatter_3km_2018 <- create.scatter.plot(
  smr_pop_2018, independent.var = "prop_pop_3km",
  x.label = "Percentage of catchment population \nliving in 3km buffer",
  plot.title = "2018")

# 2019 scatter plots -----------------------------------------------------------
scatter_1km_2019 <- create.scatter.plot(
  smr_pop_2019, independent.var = "prop_pop_1km",
  x.label = "Percentage of catchment population \nliving in 1km buffer",
  plot.title = "2019")

scatter_2km_2019 <- create.scatter.plot(
  smr_pop_2019, independent.var = "prop_pop_2km",
  x.label = "Percentage of catchment population \nliving in 2km buffer",
  plot.title = "2019")

scatter_3km_2019 <- create.scatter.plot(
  smr_pop_2019, independent.var = "prop_pop_3km",
  x.label = "Percentage of catchment population \nliving in 3km buffer",
  plot.title = "2019")

# 2020 scatter plots -----------------------------------------------------------
scatter_1km_2020 <- create.scatter.plot(
  smr_pop_2020, independent.var = "prop_pop_1km",
  x.label = "Percentage of catchment population \nliving in 1km buffer",
  plot.title = "2020")

scatter_2km_2020 <- create.scatter.plot(
  smr_pop_2020, independent.var = "prop_pop_2km",
  x.label = "Percentage of catchment population \nliving in 2km buffer",
  plot.title = "2020")

scatter_3km_2020 <- create.scatter.plot(
  smr_pop_2020, independent.var = "prop_pop_3km",
  x.label = "Percentage of catchment population \nliving in 3km buffer",
  plot.title = "2020")

# Arrange the plots ------------------------------------------------------------
ggpubr::ggarrange(scatter_1km_2017, scatter_2km_2017, scatter_3km_2017,
                  scatter_1km_2018, scatter_2km_2018, scatter_3km_2018,
                  scatter_1km_2019, scatter_2km_2019, scatter_3km_2019, 
                  scatter_1km_2020, scatter_2km_2020, scatter_3km_2020,
                  ncol = 3, nrow = 4)
Fig 11. Relationship between standardised morbidity ratio and living near waterbodies

Fig 11. Relationship between standardised morbidity ratio and living near waterbodies

11 Normality test

First, we check if the count data (dry season malaria cases) is normally distributed or follows a Poisson distribution.The Poisson distribution is a discrete distribution that measures the probability of a given number of events happening in a specified time period generated by a Poisson process. By Poisson processes, we mean processes that are discrete, independent, and mutually exclusive, e.g., dry season malaria cases.

# Combine data for model fitting -----------------------------------------------
model_data_2017 <- merge(expected_malaria_2017, smr_pop_2017, by = "rowID", all = TRUE) |>
  dplyr::select(-Names.y) |> 
  dplyr::rename(Names = Names.x)

model_data_2018 <-  merge(expected_malaria_2018, smr_pop_2018, by = "rowID", all = TRUE) |> 
  dplyr::select(-Names.y) |> 
  dplyr::rename(Names = Names.x)

model_data_2019 <-  merge(expected_malaria_2019, smr_pop_2019, by = "rowID", all = TRUE) |>
  dplyr::select(-Names.y) |> 
  dplyr::rename(Names = Names.x)

model_data_2020 <-  merge(expected_malaria_2020, smr_pop_2020, by = "rowID", all = TRUE) |> 
  dplyr::select(-Names.y) |>
  dplyr::rename(Names = Names.x)

# Normality test ---------------------------------------------------------------
# Check whether the dependent variable follows a poisson distribution
# Dry season malaria cases appear to be non normally distributed i.e highly skewed 

histogram_2017 <- ggplot2::ggplot(model_data_2017, aes(x = observed_2017)) +
  geom_histogram(fill = "white", color = "black")+
  geom_vline(aes(xintercept = mean(observed_2017)), 
             color = "blue",
             linetype = "dashed")+
  labs(title = "2017", x = "Observed malaria cases", y = "Count")+
  theme_classic()

histogram_2018 <- ggplot2::ggplot(model_data_2018, aes(x = observed_2018)) +
  geom_histogram(fill = "white", color = "black")+
  geom_vline(aes(xintercept = mean(observed_2018)), 
             color = "blue",
             linetype = "dashed")+
  labs(title = "2018", x = "Observed malaria cases", y = "Count")+
  theme_classic()

histogram_2019 <- ggplot2::ggplot(model_data_2019, aes(x = observed_2019)) +
  geom_histogram(fill = "white", color = "black")+
  geom_vline(aes(xintercept = mean(observed_2019)), 
             color = "blue",
             linetype = "dashed")+
  labs(title = "2019", x = "Observed malaria cases", y = "Count")+
  theme_classic()

histogram_2020 <- ggplot2::ggplot(model_data_2020, aes(x = observed_2020)) +
  geom_histogram(fill = "white", color = "black")+
  geom_vline(aes(xintercept = mean(observed_2020)), 
             color = "blue",
             linetype = "dashed")+
  labs(title = "2020", x = "Observed malaria cases", y = "Count")+
  theme_classic()

gridExtra::grid.arrange(histogram_2017, histogram_2018, histogram_2019, histogram_2020)
Fig 12. Poisson distribution of dry season malaria cases. Clearly, the data is not in the form of a bell curve like in a normal distribution. Many health facilities reported very few malaria cases. A few health facilities have a large number of cases making for a distribution that appears to be far from normal. Therefore, Poisson regression will be used to model our dry season malaria data.

Fig 12. Poisson distribution of dry season malaria cases. Clearly, the data is not in the form of a bell curve like in a normal distribution. Many health facilities reported very few malaria cases. A few health facilities have a large number of cases making for a distribution that appears to be far from normal. Therefore, Poisson regression will be used to model our dry season malaria data.

# Let’s check out the mean and variance of the dependent variable:

mean(model_data_2017$observed_2017)
## [1] 2491.923
var(model_data_2017$observed_2017)
## [1] 7693348
mean(model_data_2018$observed_2018)
## [1] 2795.769
var(model_data_2018$observed_2018)
## [1] 5090401
mean(model_data_2019$observed_2019)
## [1] 2711.385
var(model_data_2019$observed_2019)
## [1] 5947880
mean(model_data_2020$observed_2020)
## [1] 4580.538
var(model_data_2020$observed_2020)
## [1] 14157771
# The variance is much greater than the mean, which suggests that we will 
# have over-dispersion in the model.

12 Model fitting

\[ ln (E(y)) = 𝜷_0 + 𝜷_1 x + ln(𝒆_𝒊)\] where, dependent variable, 𝑦 = observed malaria cases; 𝐸(𝑦) = expected count value; independent variable, 𝑥 = percentage of people living near dams; and offset, 𝑒 = expected malaria cases.

To cope with the malaria count data coming from populations of different sizes, we specify an offset argument. This adds a constant term for each row of the data in the model. The log of the expected cases is used in the offset term.

Summary outputs:

Estimate : the intercept (\(𝜷_0\)) and the beta coefficient estimates associated to each predictor variable.

Std.Error : the standard error of the coefficient estimates. This represents the accuracy of the coefficients. The larger the standard error, the less confident we are about the estimate.

t value : the t-statistic, which is the coefficient estimate (column 2) divided by the standard error of the estimate (column 3). For a given the predictor, the t-statistic evaluates whether or not there is significant association between the predictor and the outcome variable, i.e., whether the beta coefficient of the predictor is significantly different from zero.

Pr(>|t|) : The p-value corresponding to the t-statistic. The smaller the p-value, the more significant the estimate is.

Residuals : Provide a quick view of the distribution of the residuals, which by definition have a mean zero. Therefore, the median should not be far from zero, and the minimum (min)and maximum (max) should be roughly equal in absolute value.

Coefficients: Shows the regression beta coefficients and their statistical significance. Predictor variables, that are significantly associated to the outcome variable, are marked by stars.

Residual standard error (RSE), and R-squared (\(R^2\)) metrics tell how well the model fits to our data. An (adjusted) \(R^2\) that is close to 1 indicates that a large proportion of the variability in the outcome has been explained by the regression model. A number near 0 indicates that the regression model did not explain much of the variability in the outcome.

# Fit generalised linear model -------------------------------------------------
# Defining model parameters:
# response variable: observed_2017, observed_2018, observed_2019, observed_2020 are
#                    recorded dry season malaria cases in that year
# risk factor: prop_pop_1km,  prop_pop_2km,  prop_pop_3km are the percentage of people living
#             within 1km, 2km and 3km buffers of water bodies, respectively.
# offset: expected_* is the number of malaria cases we would expect if the malaria rate
#          was equal in all the catchment areas

# 2017 -------------------------------------------------------------------------
model_1km_2017 <- glm(observed_2017~prop_pop_1km+offset(log(expected_2017)),
                      data = model_data_2017, family = poisson(link = "log"))

# Display the statistical summary of the model
# summary(model_1km_2017)
# generate model report according to best practices guidelines
report::report(model_1km_2017)
## We fitted a poisson model (estimated using ML) to predict observed_2017 with prop_pop_1km and expected_2017 (formula: observed_2017 ~ prop_pop_1km + offset(log(expected_2017))). The model's explanatory power is substantial (Nagelkerke's R2 = 1.00). The model's intercept, corresponding to prop_pop_1km = 0 and expected_2017 = 0, is at -0.29 (95% CI [-0.30, -0.28], p < .001). Within this model:
## 
##   - The effect of prop_pop_1km is statistically significant and positive (beta = 0.03, 95% CI [0.03, 0.03], p < .001; Std. beta = -0.37, 95% CI [-0.38, -0.36])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using

The estimated regression equation can be written as follow: observed malaria cases = -0.290015 + 0.032594 * percentage of catchment population living near dam. Using this formula, for each change in number of people living near dams, we can predict the number of dry season malaria case. Our estimated model: dry season malaria risk = -0.29 + 0.03 + ln(e) If the number of people living around dams was 0, dry season malaria risk would be -0.29. That is the estimate 0.29 can be interpreted as the dry season malaria risk in a catchment with no people living near waterbodies.

For example: - for a buffer population equal zero, we can expect -0.29 malaria cases. - for a buffer population equal 1000, we can expect round(-0.29 + 0.03 * 1000) = 30 malaria cases.

sjPlot::tab_model(model_1km_2017, digits = 4, digits.re = 4,
                  show.r2 = FALSE, show.aic = TRUE)
  observed_2017
Predictors Incidence Rate Ratios CI p
(Intercept) 0.7483 0.7384 – 0.7582 <0.001
prop_pop_1km 1.0331 1.0320 – 1.0343 <0.001
Observations 26
AIC 9834.177
# From the output above, the p-value is 0.001 - way less than the alpha value of 0.05, 
# we therefore reject our null hypothesis, meaning that there is a difference in 
# dry season malaria risk between people living close to water bodies than those living far away.


model_2km_2017 <- glm(observed_2017~1+prop_pop_2km+offset(log(expected_2017)),
                      data = model_data_2017, family = poisson(link = "log"))

# summary(model_2km_2017)
report::report(model_2km_2017)
## We fitted a poisson model (estimated using ML) to predict observed_2017 with prop_pop_2km and expected_2017 (formula: observed_2017 ~ 1 + prop_pop_2km + offset(log(expected_2017))). The model's explanatory power is substantial (Nagelkerke's R2 = 1.00). The model's intercept, corresponding to prop_pop_2km = 0 and expected_2017 = 0, is at -0.35 (95% CI [-0.36, -0.33], p < .001). Within this model:
## 
##   - The effect of prop_pop_2km is statistically significant and positive (beta = 0.01, 95% CI [0.01, 0.02], p < .001; Std. beta = -0.47, 95% CI [-0.48, -0.45])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using
sjPlot::tab_model(model_2km_2017, digits = 3, digits.re = 3,
                  show.r2 = FALSE, show.aic = TRUE)
  observed_2017
Predictors Incidence Rate Ratios CI p
(Intercept) 0.707 0.697 – 0.718 <0.001
prop_pop_2km 1.015 1.014 – 1.015 <0.001
Observations 26
AIC 9674.729
model_3km_2017 <- glm(observed_2017~1+prop_pop_3km+offset(log(expected_2017)),
                      data = model_data_2017, family = poisson(link = "log"))

#summary(model_3km_2017)
report::report(model_3km_2017)
## We fitted a poisson model (estimated using ML) to predict observed_2017 with prop_pop_3km and expected_2017 (formula: observed_2017 ~ 1 + prop_pop_3km + offset(log(expected_2017))). The model's explanatory power is substantial (Nagelkerke's R2 = 1.00). The model's intercept, corresponding to prop_pop_3km = 0 and expected_2017 = 0, is at -0.40 (95% CI [-0.42, -0.39], p < .001). Within this model:
## 
##   - The effect of prop_pop_3km is statistically significant and positive (beta = 0.01, 95% CI [0.01, 0.01], p < .001; Std. beta = -0.50, 95% CI [-0.51, -0.49])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using
sjPlot::tab_model(model_3km_2017, digits = 3, digits.re = 3,
                  show.r2 = FALSE, show.aic = TRUE)
  observed_2017
Predictors Incidence Rate Ratios CI p
(Intercept) 0.668 0.657 – 0.680 <0.001
prop_pop_3km 1.010 1.010 – 1.011 <0.001
Observations 26
AIC 9942.005
# 2018 -------------------------------------------------------------------------
model_1km_2018 <- glm(observed_2018~1+prop_pop_1km+offset(log(expected_2018)),
                      data = model_data_2018, family = poisson(link = "log"))

# summary(model_1km_2018)
report::report(model_1km_2018)
## We fitted a poisson model (estimated using ML) to predict observed_2018 with prop_pop_1km and expected_2018 (formula: observed_2018 ~ 1 + prop_pop_1km + offset(log(expected_2018))). The model's explanatory power is substantial (Nagelkerke's R2 = 1.00). The model's intercept, corresponding to prop_pop_1km = 0 and expected_2018 = 0, is at -0.29 (95% CI [-0.30, -0.27], p < .001). Within this model:
## 
##   - The effect of prop_pop_1km is statistically significant and positive (beta = 0.02, 95% CI [0.02, 0.02], p < .001; Std. beta = -0.58, 95% CI [-0.59, -0.56])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using
sjPlot::tab_model(model_1km_2018, digits = 3, digits.re = 3,
                  show.r2 = FALSE, show.aic = TRUE)
  observed_2018
Predictors Incidence Rate Ratios CI p
(Intercept) 0.750 0.740 – 0.761 <0.001
prop_pop_1km 1.024 1.023 – 1.025 <0.001
Observations 26
AIC 13953.927
model_2km_2018 <- glm(observed_2018~1+prop_pop_2km+offset(log(expected_2018)),
                      data = model_data_2018, family = poisson(link = "log"))

# summary(model_2km_2018)
report::report(model_2km_2018)
## We fitted a poisson model (estimated using ML) to predict observed_2018 with prop_pop_2km and expected_2018 (formula: observed_2018 ~ 1 + prop_pop_2km + offset(log(expected_2018))). The model's explanatory power is substantial (Nagelkerke's R2 = 1.00). The model's intercept, corresponding to prop_pop_2km = 0 and expected_2018 = 0, is at -0.30 (95% CI [-0.31, -0.28], p < .001). Within this model:
## 
##   - The effect of prop_pop_2km is statistically significant and positive (beta = 9.22e-03, 95% CI [8.81e-03, 9.62e-03], p < .001; Std. beta = -0.47, 95% CI [-0.48, -0.46])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using
sjPlot::tab_model(model_2km_2018, digits = 3, digits.re = 3,
                  show.r2 = FALSE, show.aic = TRUE)
  observed_2018
Predictors Incidence Rate Ratios CI p
(Intercept) 0.742 0.731 – 0.754 <0.001
prop_pop_2km 1.009 1.009 – 1.010 <0.001
Observations 26
AIC 14604.406
model_3km_2018 <- glm(observed_2018~1+prop_pop_3km+offset(log(expected_2018)),
                      data = model_data_2018, family = poisson(link = "log"))

# summary(model_3km_2018)
report::report(model_3km_2018)
## We fitted a poisson model (estimated using ML) to predict observed_2018 with prop_pop_3km and expected_2018 (formula: observed_2018 ~ 1 + prop_pop_3km + offset(log(expected_2018))). The model's explanatory power is substantial (Nagelkerke's R2 = 1.00). The model's intercept, corresponding to prop_pop_3km = 0 and expected_2018 = 0, is at -0.40 (95% CI [-0.42, -0.38], p < .001). Within this model:
## 
##   - The effect of prop_pop_3km is statistically significant and positive (beta = 8.03e-03, 95% CI [7.67e-03, 8.38e-03], p < .001; Std. beta = -0.47, 95% CI [-0.48, -0.45])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using
sjPlot::tab_model(model_3km_2018, digits = 3, digits.re = 3,
                  show.r2 = FALSE, show.aic = TRUE)
  observed_2018
Predictors Incidence Rate Ratios CI p
(Intercept) 0.668 0.655 – 0.681 <0.001
prop_pop_3km 1.008 1.008 – 1.008 <0.001
Observations 26
AIC 14554.845
# 2019 -------------------------------------------------------------------------
model_1km_2019 <- glm(observed_2019~1+prop_pop_1km+offset(log(expected_2019)),
                      data = model_data_2019, family = poisson(link = "log"))

# summary(model_1km_2019)
report::report(model_1km_2019)
## We fitted a poisson model (estimated using ML) to predict observed_2019 with prop_pop_1km and expected_2019 (formula: observed_2019 ~ 1 + prop_pop_1km + offset(log(expected_2019))). The model's explanatory power is substantial (Nagelkerke's R2 = 1.00). The model's intercept, corresponding to prop_pop_1km = 0 and expected_2019 = 0, is at -0.19 (95% CI [-0.20, -0.17], p < .001). Within this model:
## 
##   - The effect of prop_pop_1km is statistically significant and positive (beta = 0.01, 95% CI [9.39e-03, 0.01], p < .001; Std. beta = -0.42, 95% CI [-0.43, -0.41])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using
sjPlot::tab_model(model_1km_2019, digits = 3, digits.re = 3,
                  show.r2 = FALSE, show.aic = TRUE)
  observed_2019
Predictors Incidence Rate Ratios CI p
(Intercept) 0.830 0.817 – 0.844 <0.001
prop_pop_1km 1.010 1.009 – 1.011 <0.001
Observations 26
AIC 10313.731
model_2km_2019 <- glm(observed_2019~1+prop_pop_2km+offset(log(expected_2019)),
                      data = model_data_2019, family = poisson(link = "log"))

# summary(model_2km_2019)
report::report(model_2km_2019)
## We fitted a poisson model (estimated using ML) to predict observed_2019 with prop_pop_2km and expected_2019 (formula: observed_2019 ~ 1 + prop_pop_2km + offset(log(expected_2019))). The model's explanatory power is substantial (Nagelkerke's R2 = 0.96). The model's intercept, corresponding to prop_pop_2km = 0 and expected_2019 = 0, is at -0.08 (95% CI [-0.10, -0.06], p < .001). Within this model:
## 
##   - The effect of prop_pop_2km is statistically significant and positive (beta = 1.76e-03, 95% CI [1.38e-03, 2.14e-03], p < .001; Std. beta = -0.33, 95% CI [-0.34, -0.31])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using
sjPlot::tab_model(model_2km_2019, digits = 3, digits.re = 3,
                  show.r2 = FALSE, show.aic = TRUE)
  observed_2019
Predictors Incidence Rate Ratios CI p
(Intercept) 0.924 0.907 – 0.942 <0.001
prop_pop_2km 1.002 1.001 – 1.002 <0.001
Observations 26
AIC 10906.895
model_3km_2019 <- glm(observed_2019~1+prop_pop_3km+offset(log(expected_2019)),
                      data = model_data_2019, family = poisson(link = "log"))

# summary(model_3km_2019)
report::report(model_3km_2019)
## We fitted a poisson model (estimated using ML) to predict observed_2019 with prop_pop_3km and expected_2019 (formula: observed_2019 ~ 1 + prop_pop_3km + offset(log(expected_2019))). The model's explanatory power is substantial (Nagelkerke's R2 = 0.83). The model's intercept, corresponding to prop_pop_3km = 0 and expected_2019 = 0, is at -0.07 (95% CI [-0.10, -0.05], p < .001). Within this model:
## 
##   - The effect of prop_pop_3km is statistically significant and positive (beta = 1.08e-03, 95% CI [7.70e-04, 1.39e-03], p < .001; Std. beta = -0.43, 95% CI [-0.44, -0.41])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using
sjPlot::tab_model(model_3km_2019, digits = 3, digits.re = 3,
                  show.r2 = FALSE, show.aic = TRUE)
  observed_2019
Predictors Incidence Rate Ratios CI p
(Intercept) 0.929 0.909 – 0.950 <0.001
prop_pop_3km 1.001 1.001 – 1.001 <0.001
Observations 26
AIC 10942.072
# 2020 -------------------------------------------------------------------------
model_1km_2020 <- glm(observed_2020~1+prop_pop_1km+offset(log(expected_2020)),
                      data = model_data_2020, family = poisson(link = "log"))

# summary(model_1km_2020)
report::report(model_1km_2020)
## We fitted a poisson model (estimated using ML) to predict observed_2020 with prop_pop_1km and expected_2020 (formula: observed_2020 ~ 1 + prop_pop_1km + offset(log(expected_2020))). The model's explanatory power is weak (Nagelkerke's R2 = 0.08). The model's intercept, corresponding to prop_pop_1km = 0 and expected_2020 = 0, is at 5.44e-03 (95% CI [-3.63e-03, 0.01], p = 0.239). Within this model:
## 
##   - The effect of prop_pop_1km is statistically non-significant and negative (beta = -5.94e-04, 95% CI [-1.36e-03, 1.77e-04], p = 0.131; Std. beta = -0.49, 95% CI [-0.50, -0.49])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using
sjPlot::tab_model(model_1km_2020, digits = 3, digits.re = 3,
                  show.r2 = FALSE, show.aic = TRUE)
  observed_2020
Predictors Incidence Rate Ratios CI p
(Intercept) 1.005 0.996 – 1.015 0.239
prop_pop_1km 0.999 0.999 – 1.000 0.131
Observations 26
AIC 21077.777
model_2km_2020 <- glm(observed_2020~1+prop_pop_2km+offset(log(expected_2020)),
                      data = model_data_2020, family = poisson(link = "log"))

# summary(model_2km_2020)
report::report(model_2km_2020)
## We fitted a poisson model (estimated using ML) to predict observed_2020 with prop_pop_2km and expected_2020 (formula: observed_2020 ~ 1 + prop_pop_2km + offset(log(expected_2020))). The model's explanatory power is very weak (Nagelkerke's R2 = 0.01). The model's intercept, corresponding to prop_pop_2km = 0 and expected_2020 = 0, is at -2.27e-03 (95% CI [-0.01, 7.71e-03], p = 0.656). Within this model:
## 
##   - The effect of prop_pop_2km is statistically non-significant and positive (beta = 9.77e-05, 95% CI [-2.56e-04, 4.51e-04], p = 0.588; Std. beta = -0.58, 95% CI [-0.59, -0.57])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using
sjPlot::tab_model(model_2km_2020, digits = 3, digits.re = 3,
                  show.r2 = FALSE, show.aic = TRUE)
  observed_2020
Predictors Incidence Rate Ratios CI p
(Intercept) 0.998 0.988 – 1.008 0.656
prop_pop_2km 1.000 1.000 – 1.000 0.588
Observations 26
AIC 21079.760
model_3km_2020 <- glm(observed_2020~1+prop_pop_3km+offset(log(expected_2020)),
                      data = model_data_2020, family = poisson(link = "log"))

# summary(model_3km_2020)
report::report(model_3km_2020)
## We fitted a poisson model (estimated using ML) to predict observed_2020 with prop_pop_3km and expected_2020 (formula: observed_2020 ~ 1 + prop_pop_3km + offset(log(expected_2020))). The model's explanatory power is very weak (Nagelkerke's R2 = 4.42e-03). The model's intercept, corresponding to prop_pop_3km = 0 and expected_2020 = 0, is at -1.59e-03 (95% CI [-0.01, 9.18e-03], p = 0.773). Within this model:
## 
##   - The effect of prop_pop_3km is statistically non-significant and positive (beta = 4.14e-05, 95% CI [-1.97e-04, 2.80e-04], p = 0.734; Std. beta = -0.51, 95% CI [-0.52, -0.51])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using
sjPlot::tab_model(model_3km_2020, digits = 3, digits.re = 3,
                  show.r2 = FALSE, show.aic = TRUE)
  observed_2020
Predictors Incidence Rate Ratios CI p
(Intercept) 0.998 0.988 – 1.009 0.773
prop_pop_3km 1.000 1.000 – 1.000 0.734
Observations 26
AIC 21079.939

In the summaries above, we can see that all p-values, are less than 0.001 (except 2020 model), hence, the explanatory variable (percentage of people living near water bodies) has significant effect on dry season malaria cases. The Residual deviance is greater than the degrees of freedom, indicating that over-dispersion exists, as anticipated. This means that the estimates are correct, but the standard errors (Std. Error i.e., standard deviation) are unaccounted for by the model.

The Null deviance shows how well the response variable is predicted by a model that includes only the intercept (grand mean) whereas residual with the inclusion of independent variables. Above, for example model_1km_2017, we can see that the addition of 1 (25-24 = 1) independent variable decreased the deviance to 9587.9 from 12787.5. The greater difference in values means a poor fit. And the dispersion parameter is 399.4958 (9587.9/24) which is large. One possibility is that there are other important covariates that could be used to describe the differences in the observed dry season malaria cases. We consider overdispersion as a possible explanation of the significant lack-of-fit. Over-dispersion suggests that there is more variation in the response than the model implies.

13 Assess the performance and accuracy of the regression model.

Here, we evaluate how well the Poisson regression model is predicting the outcome of a new test data that have not been used to build the model i.e how close the prediction is close to the real value.

Two important metrics have been used to assess the performance of the predictive regression model:

Root Mean Squared Error, which measures the model prediction error. It corresponds to the average difference between the observed known values of the outcome and the predicted value by the model.RMSE is computed as RMSE = mean((observed - predicted)^2) |> sqrt(). The lower the RMSE, the better the model.

\[RMSE = \sqrt \frac {\Sigma ( y - \hat{y} )^2}{N}\]

Pseudo R-squared goodness-of-fit measure for count data in Poisson regression which is nonlinear and computed using deviance statistics:

\[D=2∑ni=1{Yilog(Yi/μi)−(Yi−μi)}\] where \[μi=exp(β^0+β^1X1+...+β^pXp)\] denotes the predicted mean for observation i based on the estimated model parameters. AJKOER (https://stats.stackexchange.com/users/54013/ajkoer), Basic R-Squared in Poisson Regression, URL (version: 2020-06-28): https://stats.stackexchange.com/q/474500

# Tidy model assessment data ---------------------------------------------------
model_assessment_data_2017 <- model_data_2017 |>
  dplyr::as_tibble() |>
  dplyr::select(Names, observed_2017, expected_2017,
                prop_pop_1km, prop_pop_2km, prop_pop_3km) 

model_assessment_data_2018 <- model_data_2018 |>
  dplyr::as_tibble() |>
  dplyr::select(Names, observed_2018, expected_2018,
                prop_pop_1km, prop_pop_2km, prop_pop_3km) 


model_assessment_data_2019 <- model_data_2019 |>
  dplyr::as_tibble() |>
  dplyr::select(Names, observed_2019, expected_2019,
                prop_pop_1km, prop_pop_2km, prop_pop_3km) 

model_assessment_data_2020 <- model_data_2020 |>
  dplyr::as_tibble() |>
  dplyr::select(Names, observed_2020, expected_2020,
                prop_pop_1km, prop_pop_2km, prop_pop_3km) 

# To do: LOOCV(Leave One Out Cross-Validation) and k-fold Cross Validation
# Split the data into training and test set ------------------------------------
set.seed(2) # generate random numbers

split_2017 <- caTools::sample.split(model_assessment_data_2017, 
                                    SplitRatio = 0.8) # use 80% of the data for training

train_2017 <- subset(model_assessment_data_2017, split = "TRUE")

test_2017 <- subset(model_assessment_data_2017, split = "FALSE")

# Build model
model_2017 <- glm(observed_2017~1+prop_pop_1km+offset(log(expected_2017)),
                      data = train_2017, family = 'poisson')

summary(model_2017)
## 
## Call:
## glm(formula = observed_2017 ~ 1 + prop_pop_1km + offset(log(expected_2017)), 
##     family = "poisson", data = train_2017)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -34.204  -13.024   -3.552   15.308   40.886  
## 
## Coefficients:
##               Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  -0.290015   0.006763  -42.88   <2e-16 ***
## prop_pop_1km  0.032594   0.000570   57.18   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 12787.5  on 25  degrees of freedom
## Residual deviance:  9587.9  on 24  degrees of freedom
## AIC: 9834.2
## 
## Number of Fisher Scoring iterations: 4
# Prediction
predicted_2017 <- predict(model_2017, test_2017, type = "response")

# Compare predicted vs actual dry season malaria cases
par(mfrow = c(2, 1))

# plot(test_2017$observed_2017, type = "b", lty = 1.8, col = "blue")

# plot(predicted_2017, type = "b", lty = 1.8, col = "red", add = TRUE)

barplot(test_2017$observed_2017, main = "Observed malaria cases",
        xlab = "Catchment area", ylab = "Observed malaria cases")

barplot(predicted_2017, main = "Predicted malaria cases",
        xlab = "Catchment area", ylab = "Predicted malaria cases")

par(mfrow = c(1, 1)) # Create a 2 x 2 plotting matrix


# Split the data into training and test set 
set.seed(123) # generate a sequence of random numbers
# 2017 -------------------------------------------------------------------------
training_samples_2017 <- model_assessment_data_2017$observed_2017 |>
  caret::createDataPartition(p = 0.8, list = FALSE) # use 80% of the data for training

train_data_2017  <- model_assessment_data_2017[training_samples_2017, ]

test_data_2017 <- model_assessment_data_2017[-training_samples_2017, ]

# 2018 
training_samples_2018 <- model_assessment_data_2018$observed_2018 |>
  caret::createDataPartition(p = 0.8, list = FALSE) # see ?createDataPartition

train_data_2018 <- model_assessment_data_2018[training_samples_2018, ]

test_data_2018 <- model_assessment_data_2018[-training_samples_2018, ]

# 2019 
training_samples_2019 <- model_assessment_data_2019$observed_2019 |>
  caret::createDataPartition(p = 0.8, list = FALSE)

train_data_2019 <- model_assessment_data_2019[training_samples_2019, ]

test_data_2019 <- model_assessment_data_2019[-training_samples_2019, ]

# 2020 
training_samples_2020 <- model_assessment_data_2020$observed_2020 |>
  caret::createDataPartition(p = 0.8, list = FALSE)

train_data_2020 <- model_assessment_data_2020[training_samples_2020, ]

test_data_2020 <- model_assessment_data_2020[-training_samples_2020, ]

# Make predictions using the test data in order to evaluate the performance 
# of our regression model aka goodness-of-fit. The "response" type of prediction
# is on the scale of the response variable. Thus for a default binomial model 
# the default predictions are of log-odds (probabilities on logit scale) and 
# type = "response" gives the predicted probabilities. 
# 2017 -------------------------------------------------------------------------
predictions_1km_2017 <- model_1km_2017 |>
  stats::predict.glm(test_data_2017, type = "response") # see ?stats::predict.glm

predictions_2km_2017 <- model_2km_2017 |>
  stats::predict.glm(test_data_2017, type = "response")

# 2018 
predictions_1km_2018 <- model_1km_2018 |>
  stats::predict.glm(test_data_2018, type = "response")

predictions_2km_2018 <- model_2km_2018 |>
  stats::predict.glm(test_data_2018, type = "response")

# 2019 
predictions_1km_2019 <- model_1km_2019 |>
  stats::predict.glm(test_data_2019, type = "response")

predictions_2km_2019 <- model_2km_2019 |>
  stats::predict.glm(test_data_2019, type = "response")

# 2020 
predictions_1km_2020 <- model_1km_2020 |>
  stats::predict.glm(test_data_2020, type = "response")

predictions_2km_2020 <- model_2km_2020 |>
  stats::predict.glm(test_data_2020, type = "response")

# Model performance ------------------------------------------------------------
# (a) Compute the prediction error, RMSE. The lower the RMSE, the better the model
# 2017
caret::RMSE(predictions_1km_2017, test_data_2017$observed_2017)
## [1] 753.5512
caret::RMSE(predictions_2km_2017, test_data_2017$observed_2017)
## [1] 656.2454
# 2018 
caret::RMSE(predictions_1km_2018, test_data_2018$observed_2018)
## [1] 1801.58
caret::RMSE(predictions_2km_2018, test_data_2018$observed_2018)
## [1] 1682.038
# 2019 
caret::RMSE(predictions_1km_2019, test_data_2019$observed_2019)
## [1] 1376.436
caret::RMSE(predictions_2km_2019, test_data_2019$observed_2019)
## [1] 1348.131
# 2020 
caret::RMSE(predictions_1km_2020, test_data_2020$observed_2020)
## [1] 2398.95
caret::RMSE(predictions_2km_2020, test_data_2020$observed_2020)
## [1] 2400.144
# (b) Compute pseudo R-square 
# We express the goodness of fit of the Poisson regression model by widely 
# used variants of pseudo R squared statistics, most of which are based on 
# the deviance of the model: 
#    - the Aldrich-Nelson pseudo-R2 with the Veall-Zimmermann correction, which
#      is the best approximation of the McKelvey-Zavoina.
# Efron, Aldrich-Nelson, McFadden and Nagelkerke approaches severely underestimate 
# the "true R2". -----------------------------------------------------------------

# DescTools::PseudoR2(model_1km_2017, "all")

round(DescTools::PseudoR2(model_1km_2017, c("AldrichNelson", "AIC", "LogLikNull",
                                            "LogLik", "G2", "VeallZimmermann")), 2)
##   AldrichNelson             AIC              G2 VeallZimmermann 
##            0.99         9834.18         3199.65            0.99
round(DescTools::PseudoR2(model_2km_2017, c("AldrichNelson", "AIC", "LogLikNull",
                                            "LogLik", "G2", "VeallZimmermann")), 2)
##   AldrichNelson             AIC              G2 VeallZimmermann 
##            0.99         9674.73         3359.10            0.99
round(DescTools::PseudoR2(model_1km_2018, c("AldrichNelson", "AIC", "LogLikNull",
                                            "LogLik", "G2", "VeallZimmermann")), 2)
##   AldrichNelson             AIC              G2 VeallZimmermann 
##            0.99        13953.93         2639.84            0.99
round(DescTools::PseudoR2(model_2km_2018, c("AldrichNelson", "AIC", "LogLikNull",
                                            "LogLik", "G2", "VeallZimmermann")), 2)
##   AldrichNelson             AIC              G2 VeallZimmermann 
##            0.99        14604.41         1989.36            0.99
round(DescTools::PseudoR2(model_1km_2019, c("AldrichNelson", "AIC", "LogLikNull",
                                            "LogLik", "G2", "VeallZimmermann")), 2)
##   AldrichNelson             AIC              G2 VeallZimmermann 
##            0.96        10313.73          675.17            0.97
round(DescTools::PseudoR2(model_2km_2019, c("AldrichNelson", "AIC", "LogLikNull",
                                            "LogLik", "G2", "VeallZimmermann")), 2)
##   AldrichNelson             AIC              G2 VeallZimmermann 
##            0.76        10906.89           82.01            0.76
round(DescTools::PseudoR2(model_1km_2020, c("AldrichNelson", "AIC", "LogLikNull",
                                            "LogLik", "G2", "VeallZimmermann")), 2)
##   AldrichNelson             AIC              G2 VeallZimmermann 
##            0.08        21077.78            2.28            0.08
round(DescTools::PseudoR2(model_2km_2020, c("AldrichNelson", "AIC", "LogLikNull",
                                            "LogLik", "G2", "VeallZimmermann")), 2)
##   AldrichNelson             AIC              G2 VeallZimmermann 
##            0.01        21079.76            0.29            0.01

13.0.1 Check for collinearity, normality or heteroscedasticity

# Check for collinearity, normality or heteroscedasticity --------------------

performance::check_model(model_1km_2017, theme = "see::theme_modern")

performance::check_model(model_2km_2017, theme = "see::theme_modern")

performance::check_model(model_1km_2018, theme = "see::theme_modern")

performance::check_model(model_2km_2018, theme = "see::theme_modern")

performance::check_model(model_1km_2019, theme = "see::theme_modern")

performance::check_model(model_2km_2019, theme = "see::theme_modern")

performance::check_model(model_1km_2020, theme = "see::theme_modern")

performance::check_model(model_2km_2020, theme = "see::theme_modern")

# Model performance summaries --------------------------------------------------
# Compute indices of model performance for the regression models and 
# compare the quality of the models.
# Note that all score value do not necessarily sum up to 100%. See ?compare_performance
model_comparison <- performance::compare_performance(model_1km_2017, model_2km_2017,
                                                     model_1km_2018, model_2km_2018, 
                                                     model_1km_2019, model_2km_2019,
                                                     model_1km_2020, model_2km_2020, 
                                                     metrics = "common", rank = TRUE)

model_comparison
## # Comparison of Model Performance Indices
## 
## Name           | Model |       AIC |       BIC | Nagelkerke's R2 |     RMSE | Performance-Score
## -----------------------------------------------------------------------------------------------
## model_2km_2017 |   glm |  9674.729 |  9677.245 |           1.000 |  906.793 |           100.00%
## model_1km_2017 |   glm |  9834.177 |  9836.693 |           1.000 |  934.411 |            98.50%
## model_1km_2019 |   glm | 10313.731 | 10316.247 |           1.000 |  952.120 |            95.89%
## model_2km_2019 |   glm | 10906.895 | 10909.411 |           0.957 |  972.461 |            91.62%
## model_1km_2018 |   glm | 13953.927 | 13956.443 |           1.000 | 1338.635 |            68.77%
## model_2km_2018 |   glm | 14604.406 | 14606.922 |           1.000 | 1363.406 |            65.20%
## model_1km_2020 |   glm | 21077.777 | 21080.293 |           0.084 | 1764.232 |             2.08%
## model_2km_2020 |   glm | 21079.760 | 21082.277 |           0.011 | 1772.332 |             0.00%

13.0.2 Comaprison of model indices

The 8 models are ranked on each of the four parameters: AIC, BIC, RMSE and adjusted R-squared. The radar chart gives an indication of which model performed well or poorly against each parameter. For example, model_1km_2020 and model_2km_2020 have significantly the lowest ranking for all the parameters when compared to the other models (model_1km_2017, model_2km_2017, model_1km_2018, model_1km_2018, model_1km_2019, model_2km_2019), which have higher ranking in AIC, BIC, RMSE and R-squared.

plot(performance::compare_performance(model_1km_2017, model_2km_2017,
                                      model_1km_2018, model_2km_2018, 
                                      model_1km_2019, model_2km_2019,
                                      model_1km_2020, model_2km_2020, 
                                      metrics = "common", rank = TRUE))

13.0.3 Check how well the fitted values line up with the observations

The fitted values appear to line up particularly well with the observed data, suggesting that prop_pop_* (i.e., proportion of catchment population living near water bodies) can help us understand malaria risk in the catchment areas.

# Helper function to create scatter plots to see how well 
# fitted values line up with observed malaria cases
plot.fitted.values <- function(fitted.values.df, model.df, title){
  
  # Remove missing values from model data since 
  # model fitting deletes missing observations
  model.df.complete <- model.df |> 
    tidyr::drop_na() |>  
    dplyr::rename_at(vars(starts_with("observed_")), ~ str_c("observed"))

  # Plot fitted versus observed values
  scatter.plot <- ggplot2::ggplot()+ 
                  ggplot2::geom_point(aes(fitted.values.df$fitted.values,
                                          model.df.complete$observed))+
                  ggplot2::theme_classic()+
                  ggplot2::labs(x = "Fitted values",
                                y = "Observed values",
                                title = title)
  return(scatter.plot)
}

# Invoking function 
# 2017 -------------------------------------------------------------------------
fitted_1km_2017 <- plot.fitted.values(model_1km_2017, model_data_2017, "2017: 1km model")

fitted_2km_2017 <- plot.fitted.values(model_2km_2017, model_data_2017, "2017: 2km model")

fitted_3km_2017 <- plot.fitted.values(model_3km_2017, model_data_2017, "2017: 3km model")

# 2018 -------------------------------------------------------------------------
fitted_1km_2018 <- plot.fitted.values(model_1km_2018, model_data_2018, "2018: 1km model")

fitted_2km_2018 <- plot.fitted.values(model_2km_2018, model_data_2018, "2018: 2km model")

fitted_3km_2018 <- plot.fitted.values(model_3km_2018, model_data_2018, "2018: 3km model")

# 2019 -------------------------------------------------------------------------
fitted_1km_2019 <- plot.fitted.values(model_1km_2019, model_data_2019, "2019: 1km model")

fitted_2km_2019 <- plot.fitted.values(model_2km_2019, model_data_2019, "2019: 2km model")

fitted_3km_2019 <- plot.fitted.values(model_3km_2019, model_data_2020, "2019: 3km model")

# 2020 -------------------------------------------------------------------------
fitted_1km_2020 <- plot.fitted.values(model_1km_2020, model_data_2020, "2020: 1km model")

fitted_2km_2020 <- plot.fitted.values(model_2km_2020, model_data_2020, "2020: 2km model")

fitted_3km_2020 <- plot.fitted.values(model_3km_2020, model_data_2020, "2020: 3km model")

# Layout scatter plots ---------------------------------------------------------
cowplot::plot_grid(fitted_1km_2017, fitted_2km_2017, 
                   fitted_1km_2018, fitted_2km_2018,
                   fitted_1km_2019, fitted_2km_2019, 
                   fitted_1km_2020, fitted_2km_2020,
                   ncol = 2, nrow = 4)
Fig. 12. How well the percentage of catchment population living around water bodies explain observed malaria incidence

Fig. 12. How well the percentage of catchment population living around water bodies explain observed malaria incidence

13.1 Test for residual spatial autocorrelation using adjacency as criterion

To understand whether similarity between malaria cases in catchment areas is a function of the distance between them or not, we incoporate a spatial dependency effect into the multivariate model and test test for spatial autocorrelation.

# Prep data
prep.spatial.dependency.data <- function(sf_2017, sf_2018, sf_2019, sf_2020){
  
  df_2017 <- sf_2017 |>
    dplyr::as_tibble() |> 
    dplyr::rename(observed_2018 = dr_2018,
                  observed_2019 = dr_2019,
                  observed_2020 = dr_2020,
                  SMR_2017 = SMR) |> 
    dplyr::select(rowID, Names, SMR_2017, geometry)
  
  df_2018 <- sf_2018 |> 
    dplyr::as_tibble() |> 
    dplyr::rename(SMR_2018 = SMR) |> 
    dplyr::select(rowID, SMR_2018)
  
  df_2019 <- sf_2019 |> 
    dplyr::as_tibble() |> 
    dplyr::rename(SMR_2019 = SMR) |> 
    dplyr::select(rowID, SMR_2019)
  
  df_2020 <- sf_2020 |> 
    dplyr::as_tibble() |> 
    dplyr::rename(SMR_2020 = SMR) |>
    dplyr::select(rowID, SMR_2020)
    
  
  spatial_dependency_data <- merge(
    merge(
      merge(
        df_2017, df_2018, by = "rowID", all = TRUE),
        df_2019, by = "rowID", all = TRUE),
        df_2020, by = "rowID", all = TRUE)
  
  return(spatial_dependency_data)
  
}

# Invoking function ------------------------------------------------------------
spatial_dependency_data <- prep.spatial.dependency.data(model_data_2017,
                                                        model_data_2018, 
                                                        model_data_2019,
                                                        model_data_2020)

# Find adjacent polygons i.e., make neigbhour list,
# Contiguity neighbors - all that share a boundary point
spatial_dependency_shp <- sf::st_as_sf(spatial_dependency_data) |> 
  as("Spatial")

catchment_neighbours <- spdep::poly2nb(spatial_dependency_shp)  # Queen contiguity

summary(catchment_neighbours)
## Neighbour list object:
## Number of regions: 26 
## Number of nonzero links: 98 
## Percentage nonzero weights: 14.49704 
## Average number of links: 3.769231 
## Link number distribution:
## 
## 1 2 3 4 5 6 7 8 
## 2 3 8 5 5 1 1 1 
## 2 least connected regions:
## 1 26 with 1 link
## 1 most connected region:
## 18 with 8 links
# Get coordinates from catchment polygons
# Get center points of each catchment area

coords <- coordinates(spatial_dependency_shp)


# View the connections
{plot(spatial_dependency_shp, asp = 1)+
plot(catchment_neighbours, coords, col = "blue", add = TRUE)}
Fig. 13. Neighbourhood matrix

Fig. 13. Neighbourhood matrix

## integer(0)
# Run a Moran I test on SMR
moran.test(spatial_dependency_data$SMR_2017, 
           nb2listw(catchment_neighbours))
## 
##  Moran I test under randomisation
## 
## data:  spatial_dependency_data$SMR_2017  
## weights: nb2listw(catchment_neighbours)    
## 
## Moran I statistic standard deviate = 1.0451, p-value = 0.148
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##        0.10287363       -0.04000000        0.01868994
moran.test(spatial_dependency_data$SMR_2018, 
           nb2listw(catchment_neighbours))
## 
##  Moran I test under randomisation
## 
## data:  spatial_dependency_data$SMR_2018  
## weights: nb2listw(catchment_neighbours)    
## 
## Moran I statistic standard deviate = 1.4625, p-value = 0.0718
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##        0.16260895       -0.04000000        0.01919258
moran.test(spatial_dependency_data$SMR_2019, 
           nb2listw(catchment_neighbours))
## 
##  Moran I test under randomisation
## 
## data:  spatial_dependency_data$SMR_2019  
## weights: nb2listw(catchment_neighbours)    
## 
## Moran I statistic standard deviate = 0.64086, p-value = 0.2608
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##        0.05113078       -0.04000000        0.02022102
moran.test(spatial_dependency_data$SMR_2020, 
           nb2listw(catchment_neighbours))
## 
##  Moran I test under randomisation
## 
## data:  spatial_dependency_data$SMR_2020  
## weights: nb2listw(catchment_neighbours)    
## 
## Moran I statistic standard deviate = 1.0093, p-value = 0.1564
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##        0.10407174       -0.04000000        0.02037431
# Run a Moran I MC test on SMR
moran.mc(spatial_dependency_data$SMR_2017, 
         nb2listw(catchment_neighbours), 
         nsim = 999)
## 
##  Monte-Carlo simulation of Moran I
## 
## data:  spatial_dependency_data$SMR_2017 
## weights: nb2listw(catchment_neighbours)  
## number of simulations + 1: 1000 
## 
## statistic = 0.10287, observed rank = 844, p-value = 0.156
## alternative hypothesis: greater
moran.mc(spatial_dependency_data$SMR_2018, 
         nb2listw(catchment_neighbours), 
         nsim = 999)
## 
##  Monte-Carlo simulation of Moran I
## 
## data:  spatial_dependency_data$SMR_2018 
## weights: nb2listw(catchment_neighbours)  
## number of simulations + 1: 1000 
## 
## statistic = 0.16261, observed rank = 908, p-value = 0.092
## alternative hypothesis: greater
moran.mc(spatial_dependency_data$SMR_2019, 
         nb2listw(catchment_neighbours), 
         nsim = 999)
## 
##  Monte-Carlo simulation of Moran I
## 
## data:  spatial_dependency_data$SMR_2019 
## weights: nb2listw(catchment_neighbours)  
## number of simulations + 1: 1000 
## 
## statistic = 0.051131, observed rank = 773, p-value = 0.227
## alternative hypothesis: greater
moran.mc(spatial_dependency_data$SMR_2020, 
         nb2listw(catchment_neighbours), 
         nsim = 999)
## 
##  Monte-Carlo simulation of Moran I
## 
## data:  spatial_dependency_data$SMR_2020 
## weights: nb2listw(catchment_neighbours)  
## number of simulations + 1: 1000 
## 
## statistic = 0.10407, observed rank = 838, p-value = 0.162
## alternative hypothesis: greater
# Run a Conditional Autoregressive (CAR) model, which allows us to incorporate 
# the spatial autocorrelation between neighbours within our GLM

# First, generate a weights matrix from a neighbours list with spatial weights
adj_matrix <- spdep::nb2mat(catchment_neighbours, style = "B") # see ?nb2mat

# Match row and column names with those of geographic location index 
rownames(adj_matrix) <- colnames(adj_matrix) <- spatial_dependency_data$rowID
# row.names(adj_matrix) <- NULL # alternatively

# Now we can fit the model. The spatial effect is called using the adjacency function which 
# requires the grouping factor (i.e. the rowID of each catchment area)

CAR_model_1km_2017 <- spaMM::fitme(observed_2017~prop_pop_1km+offset(log(expected_2017)),
                                   adjMatrix = adj_matrix, 
                                   data = model_data_2017, 
                                   family = 'poisson')

# Generate 95% CI
coefs <- as.data.frame(summary(CAR_model_1km_2017)$beta_table)
## formula: observed_2017 ~ prop_pop_1km + offset(log(expected_2017))
## Estimation of fixed effects by ML.
## family: poisson( link = log ) 
##  ------------ Fixed effects (beta) ------------
##              Estimate Cond. SE t-value
## (Intercept)  -0.29002 0.006763  -42.88
## prop_pop_1km  0.03259 0.000570   57.18
##  ------------- Likelihood values  -------------
##                         logLik
## p(h)   (Likelihood): -4915.088
# Moran's I contiguity test
MI_2017 <- spdep::moran(model_data_2017$observed_2017, 
                        nb2listw(catchment_neighbours),
                        length(model_data_2017$observed_2017),
                        Szero(nb2listw(catchment_neighbours)))

14 Yearly variation in observed malaria cases as a risk factor

The findings from the univariate model above suggest the risk of dry season malaria transmission varies depending on the year, so we consider a model with an interaction between proportion of people close to dams and year. We would like to explain dry season malaria risk based on the number of people living close to dams and the year.

We also compare the effect of removing intercept from the multiple linear regression. Mathematically, \(𝜷_0 = 0\). Hence our multivariate model without intercept can be written as: \(ln (E(y)) = {𝜷_1} {x_i}_{1} + ln(𝒆_𝒊)\) where, \(i = 1,2,3,⋯,n\)

# Prep model data --------------------------------------------------------------
df2017 <- model_data_2017 |> 
  dplyr::as_tibble() |> 
    dplyr::rename(observed_cases = observed_2017,
                  expected_cases = expected_2017,
                  health_facility = Names) |> 
    dplyr::select(-geometry, -fid,-DN, -X, -SMR, 
                  -pop_2017, -dr_2018, -dr_2019, -dr_2020)

df2017$year <- "2017" # add new column

# df2017 <- cbind(df2017, year = "2017") # alternatively

df2018 <- model_data_2018 |> 
  dplyr::as_tibble() |> 
    dplyr::rename(observed_cases = observed_2018,
                  expected_cases = expected_2018) |> 
    dplyr::select(-geometry, -fid,-DN, -X, -SMR,
                  -pop_2018,-dr_2017, -dr_2019, -dr_2020)

df2018$year <- "2018"

colnames(df2018) <- colnames(df2017) # match columns names

df2019 <- model_data_2019 |> 
  dplyr::as_tibble() |> 
    dplyr::rename(observed_cases = observed_2019,
                  expected_cases = expected_2019) |> 
    dplyr::select(-geometry, -fid,-DN, -X, -SMR,
                  -pop_2019, -dr_2017, -dr_2018, -dr_2020)

df2019$year <- "2019"

colnames(df2019) <- colnames(df2017) # match columns names

df2020 <- model_data_2020 |> 
  dplyr::as_tibble() |> 
    dplyr::rename(observed_cases = observed_2020,
                  expected_cases = expected_2020) |> 
    dplyr::select(-geometry, -fid,-DN, -X, -SMR,
                  -pop_2020, -dr_2017, -dr_2018, -dr_2019) 

df2020$year <- "2020"

colnames(df2020) <- colnames(df2017) # match columns names

model_data <- rbind(df2017, df2018, df2019, df2020)

model_data <- imputeTS::na.replace(model_data, 0) # replace NA with zero

# find the log(n) of each value in 'expected' column. It is the fourth column
log_expected <- log(model_data[ , 4]) |> 
  dplyr::rename(log_expected = expected_cases)

# add the log values to the dataframe using 'cbind()'
model_data <-  cbind(model_data, log_expected)
  


model_data |>     # View model data in table format
  gt::gt() |> 
  gt::tab_style(style = list(cell_text(align = "center")),
                locations = cells_column_labels() ) |> 
  gt::cols_label(health_facility = "Health facility",
                 observed_cases = "Observed cases",
                 expected_cases = "Expected cases",
                 prop_pop_1km = "Proportion of population in 1km buffers%",
                 prop_pop_2km = "Proportion of population in 2km buffers%",
                 prop_pop_3km   = "Proportion of population in 3km buffers%",
                 log_expected = "Log of expected cases",
                 year = "Year")
rowID Health facility Observed cases Expected cases Proportion of population in 1km buffers% Proportion of population in 2km buffers% Proportion of population in 3km buffers% Year Log of expected cases
1 Lodjwa Health Centre 564 826 10 7 19 2017 6.716595
2 Nkhamenya Rural Hospital 2720 3344 4 16 30 2017 8.114923
3 Newa Mpasazi Health Centre 216 1156 1 7 14 2017 7.052721
4 Mpepa /Chisinga Health Centre 1523 2287 0 0 0 2017 7.734996
5 Mnyanja Health Centre 1480 3327 2 6 19 2017 8.109826
6 Simlemba Health Centre 1159 2249 2 8 17 2017 7.718241
7 Ofesi Health Centre 1930 2340 0 2 2 2017 7.757906
8 Chulu Health Centre 3482 2324 7 23 45 2017 7.751045
9 Kapelula Health Centre 2970 2976 2 8 16 2017 7.998335
10 Livwezi Health Centre 594 1833 8 20 40 2017 7.513709
11 Gogode Dispensary 1553 1088 6 19 39 2017 6.992096
12 Dwangwa Dispensary 1153 2724 9 29 54 2017 7.909857
13 Chamama Health Facility 1005 1668 2 9 16 2017 7.419381
14 Wimbe Health Centre 2558 988 21 53 80 2017 6.895683
15 Chinyama 1140 1063 11 30 46 2017 6.968850
16 Mdunga Health Centre 1382 1514 0 0 0 2017 7.322510
17 Mtunthama Health Centre 1982 1561 21 64 82 2017 7.353082
18 Kasungu District Hospital 14663 11951 18 39 58 2017 9.388570
19 Chamwabvi Health Centre 2031 2945 7 24 46 2017 7.987864
20 Linyangwa Health Centre 1987 1480 5 20 41 2017 7.299797
21 Mziza Health Centre 4098 3689 13 30 51 2017 8.213111
22 Kawamba Health Centre 3845 2198 13 34 60 2017 7.695303
23 Kamboni Health Centre 2588 1768 6 20 33 2017 7.477604
24 Khola Health Centre 1012 1888 2 8 16 2017 7.543273
25 Santhe Health Centre 5668 3660 4 13 21 2017 8.205218
26 Mkhota Health Centre 1487 1940 5 17 24 2017 7.570443
1 Lodjwa Health Centre 1151 934 0 0 0 2018 6.839476
2 Nkhamenya Rural Hospital 3343 3785 3 6 13 2018 8.238801
3 Newa Mpasazi Health Centre 434 1295 1 7 16 2018 7.166266
4 Mpepa /Chisinga Health Centre 2616 2589 4 16 31 2018 7.859027
5 Mnyanja Health Centre 1715 3804 3 11 30 2018 8.243808
6 Simlemba Health Centre 1506 2496 3 10 23 2018 7.822445
7 Ofesi Health Centre 1773 2636 2 5 10 2018 7.877018
8 Chulu Health Centre 3330 2621 11 39 65 2018 7.871311
9 Kapelula Health Centre 3480 3420 9 27 51 2018 8.137396
10 Livwezi Health Centre 1128 2049 8 25 54 2018 7.625107
11 Gogode Dispensary 2550 1215 9 22 44 2018 7.102499
12 Dwangwa Dispensary 1216 3048 10 37 61 2018 8.022241
13 Chamama Health Facility 1226 1852 3 12 22 2018 7.524021
14 Wimbe Health Centre 3167 1074 20 54 80 2018 6.979145
15 Chinyama 1673 1194 14 33 48 2018 7.085064
16 Mdunga Health Centre 1894 1720 7 17 45 2018 7.450080
17 Mtunthama Health Centre 3358 1734 25 69 84 2018 7.458186
18 Kasungu District Hospital 12019 13377 19 50 66 2018 9.501292
19 Chamwabvi Health Centre 2079 3287 16 46 70 2018 8.097731
20 Linyangwa Health Centre 1500 1639 5 18 39 2018 7.401842
21 Mziza Health Centre 2291 4210 17 40 58 2018 8.345218
22 Kawamba Health Centre 3881 2386 33 62 84 2018 7.777374
23 Kamboni Health Centre 3250 1948 19 36 53 2018 7.574558
24 Khola Health Centre 1697 2108 5 14 28 2018 7.653495
25 Santhe Health Centre 6195 4096 6 21 36 2018 8.317766
26 Mkhota Health Centre 4218 2171 17 40 66 2018 7.682943
1 Lodjwa Health Centre 1168 909 10 3 10 2019 6.812345
2 Nkhamenya Rural Hospital 3932 3709 10 29 58 2019 8.218518
3 Newa Mpasazi Health Centre 626 1266 5 18 44 2019 7.143618
4 Mpepa /Chisinga Health Centre 4169 2523 3 11 19 2019 7.833204
5 Mnyanja Health Centre 2504 3751 7 17 36 2019 8.229778
6 Simlemba Health Centre 1788 2405 11 32 55 2019 7.785305
7 Ofesi Health Centre 2124 2576 5 20 36 2019 7.853993
8 Chulu Health Centre 3537 2547 27 60 86 2019 7.842671
9 Kapelula Health Centre 3357 3405 10 35 54 2019 8.133000
10 Livwezi Health Centre 435 1966 17 43 72 2019 7.583756
11 Gogode Dispensary 1469 1169 10 22 42 2019 7.063904
12 Dwangwa Dispensary 1370 2948 22 64 85 2019 7.988882
13 Chamama Health Facility 1127 1773 2 13 18 2019 7.480428
14 Wimbe Health Centre 2162 1016 21 54 81 2019 6.923629
15 Chinyama 1260 1154 14 35 48 2019 7.050989
16 Mdunga Health Centre 1485 1710 8 33 50 2019 7.444249
17 Mtunthama Health Centre 1718 1661 30 76 91 2019 7.415175
18 Kasungu District Hospital 13052 12942 25 58 90 2019 9.468233
19 Chamwabvi Health Centre 1180 3161 13 54 77 2019 8.058644
20 Linyangwa Health Centre 2692 1566 15 45 77 2019 7.356280
21 Mziza Health Centre 3135 4151 28 66 89 2019 8.331105
22 Kawamba Health Centre 3469 2258 42 78 94 2019 7.722235
23 Kamboni Health Centre 2537 1843 28 52 78 2019 7.519150
24 Khola Health Centre 2139 2040 10 19 34 2019 7.620705
25 Santhe Health Centre 5793 3957 24 58 81 2019 8.283241
26 Mkhota Health Centre 2268 2093 23 53 86 2019 7.646354
1 Lodjwa Health Centre 1788 1538 1 4 9 2020 7.338238
2 Nkhamenya Rural Hospital 8539 6313 5 20 42 2020 8.750366
3 Newa Mpasazi Health Centre 2182 2153 2 8 17 2020 7.674617
4 Mpepa /Chisinga Health Centre 5186 4270 0 0 0 2020 8.359369
5 Mnyanja Health Centre 6117 6426 1 2 5 2020 8.768108
6 Simlemba Health Centre 5310 4026 3 10 22 2020 8.300529
7 Ofesi Health Centre 2323 4379 0 3 2 2020 8.384576
8 Chulu Health Centre 7160 4308 13 36 54 2020 8.368229
9 Kapelula Health Centre 7297 5904 0 0 0 2020 8.683385
10 Livwezi Health Centre 1028 3267 7 17 38 2020 8.091627
11 Gogode Dispensary 2767 1961 7 20 40 2020 7.581210
12 Dwangwa Dispensary 2869 4971 9 34 58 2020 8.511376
13 Chamama Health Facility 635 2969 2 8 14 2020 7.995980
14 Wimbe Health Centre 2233 1689 21 53 80 2020 7.431892
15 Chinyama 1605 1936 11 28 45 2020 7.568379
16 Mdunga Health Centre 3169 2952 0 0 0 2020 7.990238
17 Mtunthama Health Centre 1882 2763 20 65 82 2020 7.924072
18 Kasungu District Hospital 19393 21785 19 40 62 2020 9.988977
19 Chamwabvi Health Centre 1128 5304 9 28 53 2020 8.576217
20 Linyangwa Health Centre 4380 2604 5 20 42 2020 7.864804
21 Mziza Health Centre 5791 7131 18 29 45 2020 8.872207
22 Kawamba Health Centre 7073 3771 19 42 64 2020 8.235095
23 Kamboni Health Centre 4665 3028 8 28 43 2020 8.015658
24 Khola Health Centre 3426 3453 4 13 21 2020 8.146999
25 Santhe Health Centre 6556 6667 5 17 31 2020 8.804925
26 Mkhota Health Centre 4592 3526 10 30 55 2020 8.167919
# Model fitting 
# 1km model --------------------------------------------------------------------
multivariate_1km <- glm(observed_cases~1+prop_pop_1km+year+offset(log(expected_cases)),
                        data = model_data, family = poisson(link = "log"))

#summary.glm(multivariate_1km)
report::report(multivariate_1km)
## We fitted a poisson model (estimated using ML) to predict observed_cases with prop_pop_1km, year and expected_cases (formula: observed_cases ~ 1 + prop_pop_1km + year + offset(log(expected_cases))). The model's explanatory power is substantial (Nagelkerke's R2 = 1.00). The model's intercept, corresponding to prop_pop_1km = 0, year = 2017 and expected_cases = 0, is at -0.11 (95% CI [-0.12, -0.10], p < .001). Within this model:
## 
##   - The effect of prop_pop_1km is statistically significant and positive (beta = 0.01, 95% CI [0.01, 0.01], p < .001; Std. beta = -0.54, 95% CI [-0.55, -0.54])
##   - The effect of year [2018] is statistically significant and negative (beta = -0.05, 95% CI [-0.06, -0.04], p < .001; Std. beta = -0.27, 95% CI [-0.29, -0.25])
##   - The effect of year [2019] is statistically significant and negative (beta = -0.13, 95% CI [-0.14, -0.12], p < .001; Std. beta = 0.30, 95% CI [0.29, 0.32])
##   - The effect of year [2020] is statistically significant and negative (beta = -0.01, 95% CI [-0.02, -4.60e-03], p = 0.004; Std. beta = -0.67, 95% CI [-0.68, -0.65])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using
# Check effect of removing intercept.
# When you remove an intercept from a regression model, you’re setting 
# it equal to 0 rather than estimating it from the data.
multivariate_1km_no_intercept <- glm(observed_cases~0+prop_pop_1km+year, # leaving the intercept out 
                                     offset = log(expected_cases),
                                     data = model_data, 
                                     family = poisson(link = "log"))

#summary.glm(multivariate_1km_no_intercept)
report::report(multivariate_1km_no_intercept)
## We fitted a poisson model (estimated using ML) to predict observed_cases with prop_pop_1km and year (formula: observed_cases ~ 0 + prop_pop_1km + year). The model's explanatory power is substantial (Nagelkerke's R2 = 1.00). The model's intercept, corresponding to prop_pop_1km = 0 and year = 2017, is at  (p ). Within this model:
## 
##   - The effect of prop_pop_1km is statistically significant and positive (beta = 0.01, 95% CI [0.01, 0.01], p < .001; Std. beta = -0.05, 95% CI [-0.06, -0.05])
##   - The effect of year [2017] is statistically significant and negative (beta = -0.11, 95% CI [-0.12, -0.10], p < .001; Std. beta = 8.79, 95% CI [8.78, 8.80])
##   - The effect of year [2018] is statistically significant and negative (beta = -0.16, 95% CI [-0.17, -0.15], p < .001; Std. beta = 8.65, 95% CI [8.64, 8.66])
##   - The effect of year [2019] is statistically significant and negative (beta = -0.24, 95% CI [-0.25, -0.23], p < .001; Std. beta = 8.76, 95% CI [8.74, 8.77])
##   - The effect of year [2020] is statistically significant and negative (beta = -0.12, 95% CI [-0.13, -0.12], p < .001; Std. beta = 8.57, 95% CI [8.57, 8.58])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using
sjPlot::tab_model(multivariate_1km, show.r2 = FALSE, show.aic = TRUE,
                  digits = 3, digits.re = 3)
  observed_cases
Predictors Incidence Rate Ratios CI p
(Intercept) 0.895 0.888 – 0.903 <0.001
prop_pop_1km 1.013 1.013 – 1.014 <0.001
year [2018] 0.955 0.945 – 0.966 <0.001
year [2019] 0.877 0.867 – 0.887 <0.001
year [2020] 0.986 0.977 – 0.995 0.004
Observations 104
AIC 58129.020
sjPlot::tab_model(multivariate_1km_no_intercept, 
                  show.r2 = FALSE, show.aic = TRUE,
                  digits = 3, digits.re = 3)
  observed_cases
Predictors Incidence Rate Ratios CI p
prop_pop_1km 1.013 1.013 – 1.014 <0.001
year [2017] 0.895 0.888 – 0.903 <0.001
year [2018] 0.855 0.847 – 0.863 <0.001
year [2019] 0.785 0.777 – 0.794 <0.001
year [2020] 0.883 0.876 – 0.889 <0.001
Observations 104
AIC 58129.020
# Alternatively
# multivariate_1km_rate <- glm(observed_cases~prop_pop_1km+year+offset(log_expected),
#                              data = model_data, family = poisson(link = "log"))
 
# summary(multivariate_1km_rate)
# report:report(multivariate_1km_rate)

# 2km model --------------------------------------------------------------------

multivariate_2km <- glm(observed_cases~1+prop_pop_2km+year+offset(log(expected_cases)),
                        data = model_data, family = 'poisson')

# summary(multivariate_2km)
report::report(multivariate_2km)
## We fitted a poisson model (estimated using ML) to predict observed_cases with prop_pop_2km, year and expected_cases (formula: observed_cases ~ 1 + prop_pop_2km + year + offset(log(expected_cases))). The model's explanatory power is substantial (Nagelkerke's R2 = 1.00). The model's intercept, corresponding to prop_pop_2km = 0, year = 2017 and expected_cases = 0, is at -0.11 (95% CI [-0.12, -0.11], p < .001). Within this model:
## 
##   - The effect of prop_pop_2km is statistically significant and positive (beta = 5.13e-03, 95% CI [4.93e-03, 5.33e-03], p < .001; Std. beta = -0.52, 95% CI [-0.52, -0.51])
##   - The effect of year [2018] is statistically significant and negative (beta = -0.05, 95% CI [-0.06, -0.04], p < .001; Std. beta = -0.10, 95% CI [-0.12, -0.09])
##   - The effect of year [2019] is statistically significant and negative (beta = -0.12, 95% CI [-0.13, -0.11], p < .001; Std. beta = 0.36, 95% CI [0.35, 0.38])
##   - The effect of year [2020] is statistically non-significant and negative (beta = -8.47e-03, 95% CI [-0.02, 1.11e-03], p = 0.083; Std. beta = -0.60, 95% CI [-0.61, -0.59])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using
# Check effect of removing intercept
multivariate_2km_no_intercept <- glm(observed_cases~prop_pop_2km+year-1,
                                     offset = log(expected_cases),
                                     data = model_data, 
                                     family = poisson(link = "log"))

# summary(multivariate_2km_no_intercept)
report::report(multivariate_2km_no_intercept)
## We fitted a poisson model (estimated using ML) to predict observed_cases with prop_pop_2km and year (formula: observed_cases ~ prop_pop_2km + year - 1). The model's explanatory power is substantial (Nagelkerke's R2 = 1.00). The model's intercept, corresponding to prop_pop_2km = 0 and year = 2017, is at  (p ). Within this model:
## 
##   - The effect of prop_pop_2km is statistically significant and positive (beta = 5.13e-03, 95% CI [4.93e-03, 5.33e-03], p < .001; Std. beta = -0.02, 95% CI [-0.03, -0.02])
##   - The effect of year [2017] is statistically significant and negative (beta = -0.11, 95% CI [-0.12, -0.11], p < .001; Std. beta = 8.78, 95% CI [8.77, 8.79])
##   - The effect of year [2018] is statistically significant and negative (beta = -0.16, 95% CI [-0.17, -0.15], p < .001; Std. beta = 8.64, 95% CI [8.63, 8.65])
##   - The effect of year [2019] is statistically significant and negative (beta = -0.23, 95% CI [-0.25, -0.22], p < .001; Std. beta = 8.72, 95% CI [8.71, 8.74])
##   - The effect of year [2020] is statistically significant and negative (beta = -0.12, 95% CI [-0.13, -0.12], p < .001; Std. beta = 8.58, 95% CI [8.57, 8.58])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using
sjPlot::tab_model(multivariate_2km, 
                  show.r2 = FALSE, show.aic = TRUE,
                  digits = 3, digits.re = 3)
  observed_cases
Predictors Incidence Rate Ratios CI p
(Intercept) 0.892 0.884 – 0.900 <0.001
prop_pop_2km 1.005 1.005 – 1.005 <0.001
year [2018] 0.953 0.942 – 0.963 <0.001
year [2019] 0.887 0.877 – 0.898 <0.001
year [2020] 0.992 0.982 – 1.001 0.083
Observations 104
AIC 59186.646
sjPlot::tab_model(multivariate_2km_no_intercept, 
                  show.r2 = FALSE, show.aic = TRUE,
                  digits = 3, digits.re = 3)
  observed_cases
Predictors Incidence Rate Ratios CI p
prop_pop_2km 1.005 1.005 – 1.005 <0.001
year [2017] 0.892 0.884 – 0.900 <0.001
year [2018] 0.850 0.842 – 0.858 <0.001
year [2019] 0.792 0.782 – 0.801 <0.001
year [2020] 0.885 0.878 – 0.891 <0.001
Observations 104
AIC 59186.646
# 3km model --------------------------------------------------------------------
multivariate_3km <- glm(observed_cases~prop_pop_3km+year+offset(log(expected_cases)),
                        data = model_data, family = 'poisson')


# summary(multivariate_3km)
report::report(multivariate_3km)
## We fitted a poisson model (estimated using ML) to predict observed_cases with prop_pop_3km, year and expected_cases (formula: observed_cases ~ prop_pop_3km + year + offset(log(expected_cases))). The model's explanatory power is substantial (Nagelkerke's R2 = 1.00). The model's intercept, corresponding to prop_pop_3km = 0, year = 2017 and expected_cases = 0, is at -0.13 (95% CI [-0.14, -0.12], p < .001). Within this model:
## 
##   - The effect of prop_pop_3km is statistically significant and positive (beta = 3.60e-03, 95% CI [3.45e-03, 3.75e-03], p < .001; Std. beta = -0.45, 95% CI [-0.46, -0.45])
##   - The effect of year [2018] is statistically significant and negative (beta = -0.04, 95% CI [-0.05, -0.03], p < .001; Std. beta = -0.17, 95% CI [-0.18, -0.15])
##   - The effect of year [2019] is statistically significant and negative (beta = -0.11, 95% CI [-0.12, -0.10], p < .001; Std. beta = 0.41, 95% CI [0.39, 0.42])
##   - The effect of year [2020] is statistically non-significant and negative (beta = -7.82e-03, 95% CI [-0.02, 1.75e-03], p = 0.109; Std. beta = -0.55, 95% CI [-0.57, -0.54])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using

In this combined dataset, we can see that the residual deviance is far from degrees of freedom, and the dispersion parameter is e.g., 576.9798 (57121/99 ) which is large. What does this indicate? This value indicates poor fit. That is, a significant difference between fitted values and observed values. This means that there is extra variance not accounted for by the model. One way to deal with over-dispersion is to run a quasi-Poisson model, which fits an extra dispersion parameter to account for that extra variance. The negative coefficient for the predictors is highly significant (p<2e-16). We also see that yearly variation influences observed cases negatively, but percentage of people living close to dams influences observed cases positively.

# Check effect of removing intercept
multivariate_3km_no_intercept <- glm(observed_cases~prop_pop_3km+year-1,
                                     offset = log(expected_cases),
                                     data = model_data, 
                                     family = poisson(link = "log"))

# summary(multivariate_3km_no_intercept)
report::report(multivariate_3km_no_intercept)
## We fitted a poisson model (estimated using ML) to predict observed_cases with prop_pop_3km and year (formula: observed_cases ~ prop_pop_3km + year - 1). The model's explanatory power is substantial (Nagelkerke's R2 = 1.00). The model's intercept, corresponding to prop_pop_3km = 0 and year = 2017, is at  (p ). Within this model:
## 
##   - The effect of prop_pop_3km is statistically significant and positive (beta = 3.60e-03, 95% CI [3.45e-03, 3.75e-03], p < .001; Std. beta = -0.02, 95% CI [-0.03, -0.02])
##   - The effect of year [2017] is statistically significant and negative (beta = -0.13, 95% CI [-0.14, -0.12], p < .001; Std. beta = 8.78, 95% CI [8.77, 8.79])
##   - The effect of year [2018] is statistically significant and negative (beta = -0.18, 95% CI [-0.19, -0.17], p < .001; Std. beta = 8.64, 95% CI [8.63, 8.65])
##   - The effect of year [2019] is statistically significant and negative (beta = -0.25, 95% CI [-0.26, -0.23], p < .001; Std. beta = 8.72, 95% CI [8.71, 8.74])
##   - The effect of year [2020] is statistically significant and negative (beta = -0.14, 95% CI [-0.15, -0.13], p < .001; Std. beta = 8.58, 95% CI [8.57, 8.58])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using
sjPlot::tab_model(multivariate_3km, 
                  show.r2 = FALSE, show.aic = TRUE,
                  digits = 3, digits.re = 3)
  observed_cases
Predictors Incidence Rate Ratios CI p
(Intercept) 0.875 0.866 – 0.883 <0.001
prop_pop_3km 1.004 1.003 – 1.004 <0.001
year [2018] 0.957 0.947 – 0.968 <0.001
year [2019] 0.893 0.883 – 0.904 <0.001
year [2020] 0.992 0.983 – 1.002 0.109
Observations 104
AIC 59540.390
sjPlot::tab_model(multivariate_3km_no_intercept, 
                  show.r2 = FALSE, show.aic = TRUE,
                  digits = 3, digits.re = 3)
  observed_cases
Predictors Incidence Rate Ratios CI p
prop_pop_3km 1.004 1.003 – 1.004 <0.001
year [2017] 0.875 0.866 – 0.883 <0.001
year [2018] 0.837 0.829 – 0.846 <0.001
year [2019] 0.781 0.771 – 0.791 <0.001
year [2020] 0.868 0.861 – 0.875 <0.001
Observations 104
AIC 59540.390
# Build regression model table -------------------------------------------------
regression_table_1km <- gtsummary::tbl_regression(multivariate_1km, 
                                                  exponentiate = FALSE) |>
  gtsummary::bold_p()


regression_table_2km <- gtsummary::tbl_regression(multivariate_2km) |>
  gtsummary::bold_p()


regression_table_3km <- gtsummary::tbl_regression(multivariate_3km, 
                                                  exponentiate = FALSE) |>
  gtsummary::bold_p()

# merge regression model tables ------------------------------------------------
table_merge <- gtsummary::tbl_merge(
  tbls = list(
    regression_table_1km,
    regression_table_2km,
    regression_table_3km
  ),
  tab_spanner = c("**Model 1km**",
                  "**Model 2km**",
                  "**Model 3km**")
)

table_merge
Characteristic Model 1km Model 2km Model 3km
log(IRR)1 95% CI1 p-value log(IRR)1 95% CI1 p-value log(IRR)1 95% CI1 p-value
prop_pop_1km 0.01 0.01, 0.01 <0.001
year
2017
2018 -0.05 -0.06, -0.04 <0.001 -0.05 -0.06, -0.04 <0.001 -0.04 -0.05, -0.03 <0.001
2019 -0.13 -0.14, -0.12 <0.001 -0.12 -0.13, -0.11 <0.001 -0.11 -0.12, -0.10 <0.001
2020 -0.01 -0.02, 0.00 0.004 -0.01 -0.02, 0.00 0.083 -0.01 -0.02, 0.00 0.11
prop_pop_2km 0.01 0.00, 0.01 <0.001
prop_pop_3km 0.00 0.00, 0.00 <0.001

1 IRR = Incidence Rate Ratio, CI = Confidence Interval

# Check model perfomance -------------------------------------------------------

performance::check_model(multivariate_1km, theme = "see::theme_modern")
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

performance::check_model(multivariate_2km, theme = "see::theme_modern")
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

performance::check_model(multivariate_3km, theme = "see::theme_modern")
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

performance::compare_performance(
  multivariate_1km, multivariate_2km, multivariate_3km,
  metrics = c("AIC", "BIC", "RMSE", "Sigma"), rank = TRUE)
## # Comparison of Model Performance Indices
## 
## Name             | Model |       AIC |       BIC |     RMSE |  Sigma | Performance-Score
## ----------------------------------------------------------------------------------------
## multivariate_1km |   glm | 58129.020 | 58142.242 | 1372.801 | 24.020 |            75.00%
## multivariate_2km |   glm | 59186.646 | 59199.868 | 1352.076 | 24.242 |            43.44%
## multivariate_3km |   glm | 59540.390 | 59553.611 | 1351.802 | 24.315 |            25.00%

15 Overdisperion

Here we apply a quassi-Poisson and negative binomial regression models to the multivariate model, which we have observed that it suffers from overdispersion issues under regular Poisson regression. One approach to dealing with overdispersion is to model the response using a negative binomial instead of a Poisson distribution. An advantage of this approach is that it introduces another parameter in addition to \(λ\), which gives the model more flexibility and, unlike the quasi-Poisson model, the negative binomial model assumes an explicit likelihood model.

Mathematically, negative binomial can be expressed as a Poisson model where \(λ\) is also random, following a gamma distribution. Specifically, if \(Y|λ∼Poisson(λ)\) and \(λ∼gamma(r, \frac{1-p}{p})\), then \(Y∼NegBinom(r, p)\) where \(E(Y) = \frac{pr}{1− p} = μ\) and \(Var(Y) = \frac{pr}{(1−p)^2} = μ+\frac{μ^2}{r}\). The overdispersion in this case is given by \(\frac{μ^2}{r}\), which approaches \(0\) as \(r\) increases (so smaller values of \(r\) indicate greater overdispersion).

In a quassi-Poisson model, the standard errors are inflated by multiplying the variance by \(\phi\), so that the standard errors are larger than the likelihood approach would imply; i.e., \(SEQ(\hat{β}) = √\hat{ϕ}∗SE(\hat{β})\), where \(Q\) stands for “quasi-Poisson” since multiplying variances by \(ϕ\) is an ad-hoc solution (Roback and Legler, 2021).

# Account for overdispersion using quasi-Poisson model--------------------------
quassipoisson_1km <- glm(observed_cases~prop_pop_1km+year, family = quasipoisson, 
                         offset = log(expected_cases), data = model_data)

# summary(quassipoisson_1km)
report::report(quassipoisson_1km)
## We fitted a poisson model (estimated using ML) to predict observed_cases with prop_pop_1km and year (formula: observed_cases ~ prop_pop_1km + year). The model's explanatory power is substantial (Nagelkerke's R2 = 1.00). The model's intercept, corresponding to prop_pop_1km = 0 and year = 2017, is at -0.11 (95% CI [-0.32, 0.09], t(99) = -1.06, p = 0.290). Within this model:
## 
##   - The effect of prop_pop_1km is statistically significant and positive (beta = 0.01, 95% CI [2.87e-03, 0.02], t(99) = 2.52, p = 0.013; Std. beta = -0.05, 95% CI [-1.22, 1.13])
##   - The effect of year [2018] is statistically non-significant and negative (beta = -0.05, 95% CI [-0.30, 0.21], t(99) = -0.35, p = 0.725; Std. beta = -0.14, 95% CI [-3.86, 3.87])
##   - The effect of year [2019] is statistically non-significant and negative (beta = -0.13, 95% CI [-0.41, 0.14], t(99) = -0.93, p = 0.352; Std. beta = -0.03, 95% CI [-3.88, 4.07])
##   - The effect of year [2020] is statistically non-significant and negative (beta = -0.01, 95% CI [-0.24, 0.22], t(99) = -0.12, p = 0.903; Std. beta = -0.21, 95% CI [-2.65, 3.61])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using
# In the absence of overdispersion, we expect the dispersion parameter estimate to be 1.0. 
# The estimated dispersion parameter here is much larger than 1.0 (594.1947) indicating
# overdispersion (extra variance) that should be accounted for. 

sjPlot::tab_model(quassipoisson_1km, show.r2 = FALSE,
                  digits = 3, digits.re = 3)
  observed_cases
Predictors Incidence Rate Ratios CI p
(Intercept) 0.895 0.726 – 1.092 0.290
prop_pop_1km 1.013 1.003 – 1.024 0.013
year [2018] 0.955 0.741 – 1.234 0.725
year [2019] 0.877 0.666 – 1.155 0.352
year [2020] 0.986 0.787 – 1.242 0.903
Observations 104
quassipoisson_2km <- glm(observed_cases~prop_pop_2km+year, family = quasipoisson, 
                         offset = log(expected_cases), data = model_data)

# summary(quassipoisson_2km)
report::report(quassipoisson_2km)
## We fitted a poisson model (estimated using ML) to predict observed_cases with prop_pop_2km and year (formula: observed_cases ~ prop_pop_2km + year). The model's explanatory power is substantial (Nagelkerke's R2 = 1.00). The model's intercept, corresponding to prop_pop_2km = 0 and year = 2017, is at -0.11 (95% CI [-0.33, 0.10], t(99) = -1.04, p = 0.299). Within this model:
## 
##   - The effect of prop_pop_2km is statistically significant and positive (beta = 5.13e-03, 95% CI [3.28e-04, 9.94e-03], t(99) = 2.09, p = 0.039; Std. beta = -0.02, 95% CI [-1.19, 1.28])
##   - The effect of year [2018] is statistically non-significant and negative (beta = -0.05, 95% CI [-0.31, 0.21], t(99) = -0.37, p = 0.713; Std. beta = -0.14, 95% CI [-3.82, 3.82])
##   - The effect of year [2019] is statistically non-significant and negative (beta = -0.12, 95% CI [-0.40, 0.16], t(99) = -0.84, p = 0.404; Std. beta = -0.06, 95% CI [-3.93, 4.04])
##   - The effect of year [2020] is statistically non-significant and negative (beta = -8.47e-03, 95% CI [-0.23, 0.22], t(99) = -0.07, p = 0.942; Std. beta = -0.20, 95% CI [-2.61, 3.55])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using
quassipoisson_3km <- glm(observed_cases~prop_pop_3km+year, family = quasipoisson, 
                         offset = log(expected_cases), data = model_data)

# summary(quassipoisson_3km)
report::report(quassipoisson_3km)
## We fitted a poisson model (estimated using ML) to predict observed_cases with prop_pop_3km and year (formula: observed_cases ~ prop_pop_3km + year). The model's explanatory power is substantial (Nagelkerke's R2 = 1.00). The model's intercept, corresponding to prop_pop_3km = 0 and year = 2017, is at -0.13 (95% CI [-0.37, 0.09], t(99) = -1.14, p = 0.259). Within this model:
## 
##   - The effect of prop_pop_3km is statistically non-significant and positive (beta = 3.60e-03, 95% CI [-4.06e-05, 7.29e-03], t(99) = 1.93, p = 0.057; Std. beta = -0.02, 95% CI [-1.12, 1.29])
##   - The effect of year [2018] is statistically non-significant and negative (beta = -0.04, 95% CI [-0.30, 0.21], t(99) = -0.33, p = 0.740; Std. beta = -0.14, 95% CI [-3.81, 3.80])
##   - The effect of year [2019] is statistically non-significant and negative (beta = -0.11, 95% CI [-0.39, 0.17], t(99) = -0.79, p = 0.431; Std. beta = -0.06, 95% CI [-3.94, 4.05])
##   - The effect of year [2020] is statistically non-significant and negative (beta = -7.82e-03, 95% CI [-0.23, 0.22], t(99) = -0.07, p = 0.947; Std. beta = -0.20, 95% CI [-2.61, 3.55])
## 
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset. 95% Confidence Intervals (CIs) and p-values were computed using
# Account for overdispersion using negative binomial model----------------------
binomial_1km <- MASS::glm.nb(observed_cases~prop_pop_1km+year, 
                             offset(log(expected_cases)), data = model_data)

summary(binomial_1km)
## 
## Call:
## MASS::glm.nb(formula = observed_cases ~ prop_pop_1km + year, 
##     data = model_data, weights = offset(log(expected_cases)), 
##     init.theta = 2.331719086, link = log)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -6.5122  -2.2287  -1.0880   0.8793   8.1660  
## 
## Coefficients:
##               Estimate Std. Error z value Pr(>|z|)    
## (Intercept)   7.515872   0.050701 148.240  < 2e-16 ***
## prop_pop_1km  0.040085   0.002934  13.663  < 2e-16 ***
## year2018     -0.019164   0.066298  -0.289 0.772541    
## year2019     -0.249465   0.071120  -3.508 0.000452 ***
## year2020      0.621513   0.064556   9.628  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for Negative Binomial(2.3317) family taken to be 1)
## 
##     Null deviance: 1179.62  on 103  degrees of freedom
## Residual deviance:  872.72  on  99  degrees of freedom
## AIC: 14469
## 
## Number of Fisher Scoring iterations: 1
## 
## 
##               Theta:  2.332 
##           Std. Err.:  0.108 
## 
##  2 x log-likelihood:  -14456.809
sjPlot::tab_model(binomial_1km, show.r2 = FALSE,
                  digits = 3, digits.re = 3)
  observed_cases
Predictors Incidence Rate Ratios CI p
(Intercept) 1836.969 1659.547 – 2038.241 <0.001
prop_pop_1km 1.041 1.035 – 1.047 <0.001
year [2018] 0.981 0.862 – 1.116 0.773
year [2019] 0.779 0.681 – 0.892 <0.001
year [2020] 1.862 1.640 – 2.113 <0.001
Observations 104
binomial_2km <- MASS::glm.nb(observed_cases~prop_pop_2km+year, 
                             offset(log(expected_cases)), data = model_data)

summary(binomial_2km)
## 
## Call:
## MASS::glm.nb(formula = observed_cases ~ prop_pop_2km + year, 
##     data = model_data, weights = offset(log(expected_cases)), 
##     init.theta = 2.245157282, link = log)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -6.4895  -2.1614  -1.0243   0.6682   8.8453  
## 
## Coefficients:
##               Estimate Std. Error z value Pr(>|z|)    
## (Intercept)   7.485606   0.053867 138.964  < 2e-16 ***
## prop_pop_2km  0.016519   0.001315  12.562  < 2e-16 ***
## year2018     -0.033563   0.067624  -0.496  0.61967    
## year2019     -0.236145   0.072419  -3.261  0.00111 ** 
## year2020      0.629752   0.065788   9.572  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for Negative Binomial(2.2452) family taken to be 1)
## 
##     Null deviance: 1135.87  on 103  degrees of freedom
## Residual deviance:  874.84  on  99  degrees of freedom
## AIC: 14504
## 
## Number of Fisher Scoring iterations: 1
## 
## 
##               Theta:  2.245 
##           Std. Err.:  0.104 
## 
##  2 x log-likelihood:  -14491.952
sjPlot::tab_model(binomial_1km, show.r2 = FALSE,
                  digits = 3, digits.re = 3)
  observed_cases
Predictors Incidence Rate Ratios CI p
(Intercept) 1836.969 1659.547 – 2038.241 <0.001
prop_pop_1km 1.041 1.035 – 1.047 <0.001
year [2018] 0.981 0.862 – 1.116 0.773
year [2019] 0.779 0.681 – 0.892 <0.001
year [2020] 1.862 1.640 – 2.113 <0.001
Observations 104
binomial_3km <- MASS::glm.nb(observed_cases~prop_pop_3km+year, 
                             offset(log(expected_cases)), data = model_data)

summary(binomial_3km)
## 
## Call:
## MASS::glm.nb(formula = observed_cases ~ prop_pop_3km + year, 
##     data = model_data, weights = offset(log(expected_cases)), 
##     init.theta = 2.216971935, link = log)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -6.4199  -2.1948  -1.0507   0.5095   9.0198  
## 
## Coefficients:
##               Estimate Std. Error z value Pr(>|z|)    
## (Intercept)   7.424712   0.058284 127.389  < 2e-16 ***
## prop_pop_3km  0.011655   0.001001  11.639  < 2e-16 ***
## year2018     -0.029366   0.068234  -0.430  0.66693    
## year2019     -0.234897   0.072969  -3.219  0.00129 ** 
## year2020      0.632333   0.066192   9.553  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for Negative Binomial(2.217) family taken to be 1)
## 
##     Null deviance: 1121.62  on 103  degrees of freedom
## Residual deviance:  875.57  on  99  degrees of freedom
## AIC: 14516
## 
## Number of Fisher Scoring iterations: 1
## 
## 
##               Theta:  2.217 
##           Std. Err.:  0.103 
## 
##  2 x log-likelihood:  -14503.732
sjPlot::tab_model(binomial_1km, show.r2 = FALSE,
                  digits = 3, digits.re = 3)
  observed_cases
Predictors Incidence Rate Ratios CI p
(Intercept) 1836.969 1659.547 – 2038.241 <0.001
prop_pop_1km 1.041 1.035 – 1.047 <0.001
year [2018] 0.981 0.862 – 1.116 0.773
year [2019] 0.779 0.681 – 0.892 <0.001
year [2020] 1.862 1.640 – 2.113 <0.001
Observations 104

16 Estimate prediction error

Since our dataset is small and partitioning it will lead to higher bias, therefore, we use the Leave one out cross validation (LOOCV) method which uses all data points to measure performance of the Poisson model. This method works as follow:

  1. Leave out one data point and build the model on the rest of the data set
  2. Test the model against the data point that is left out at step 1 and record the test error associated with the prediction
  3. Repeat the process for all data points
  4. Compute the overall prediction error by taking the average of all these test error estimates recorded at step 2.

James, Gareth, Daniela Witten, Trevor Hastie, and Robert Tibshirani. 2014. An Introduction to Statistical Learning: With Applications in R. Springer Publishing Company, Incorporated.

# Define training control
train_control <-  caret::trainControl(method = "LOOCV")

# Removing missing values
model_data_evaluation <- model_data[complete.cases(model_data),] 



# Train the model, fit a regression model and use LOOCV to evaluate perfomance
train_model_1km <- caret::train(
  observed_cases~1+prop_pop_1km+year+offset(log(expected_cases)),
  data = model_data_evaluation, method = "glm", 
  trControl = train_control, family = "poisson")

train_model_2km <- caret::train(
  observed_cases~1+prop_pop_2km+year+offset(log(expected_cases)),
  data = model_data_evaluation, method = "glm", 
  trControl = train_control, family = "poisson")


train_model_3km <- caret::train(
  observed_cases~1+prop_pop_3km+year+offset(log(expected_cases)),
  data = model_data_evaluation, method = "glm", 
  trControl = train_control, family = "poisson")

# Summarize the results
# The lower the Mean Absolute Error, the more closely a model can predict the actual observations
print(train_model_1km)
## Generalized Linear Model 
## 
## 104 samples
##   3 predictor
## 
## No pre-processing
## Resampling: Leave-One-Out Cross-Validation 
## Summary of sample sizes: 103, 103, 103, 103, 103, 103, ... 
## Resampling results:
## 
##   RMSE      Rsquared    MAE     
##   2854.871  0.07951775  1774.594
summary(train_model_1km)
## 
## Call:
## NULL
## 
## Deviance Residuals: 
##    Min      1Q  Median      3Q     Max  
## -70.91  -25.76  -11.04   10.69  138.63  
## 
## Coefficients:
##                Estimate Std. Error  z value Pr(>|z|)    
## (Intercept)   7.5596403  0.0042883 1762.873  < 2e-16 ***
## prop_pop_1km  0.0345465  0.0002076  166.381  < 2e-16 ***
## year2018     -0.0241893  0.0054887   -4.407 1.05e-05 ***
## year2019     -0.2740150  0.0059470  -46.076  < 2e-16 ***
## year2020      0.5769079  0.0048867  118.056  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 201942  on 103  degrees of freedom
## Residual deviance: 154281  on  99  degrees of freedom
## AIC: 155289
## 
## Number of Fisher Scoring iterations: 5
print(train_model_2km)
## Generalized Linear Model 
## 
## 104 samples
##   3 predictor
## 
## No pre-processing
## Resampling: Leave-One-Out Cross-Validation 
## Summary of sample sizes: 103, 103, 103, 103, 103, 103, ... 
## Resampling results:
## 
##   RMSE      Rsquared    MAE     
##   2920.994  0.04763413  1780.845
summary(train_model_2km)
## 
## Call:
## NULL
## 
## Deviance Residuals: 
##    Min      1Q  Median      3Q     Max  
## -80.94  -26.36  -11.11   11.99  148.88  
## 
## Coefficients:
##                Estimate Std. Error  z value Pr(>|z|)    
## (Intercept)   7.549e+00  4.478e-03 1685.834   <2e-16 ***
## prop_pop_2km  1.288e-02  9.391e-05  137.173   <2e-16 ***
## year2018      3.048e-03  5.472e-03    0.557    0.578    
## year2019     -1.985e-01  5.853e-03  -33.913   <2e-16 ***
## year2020      5.813e-01  4.887e-03  118.952   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 201942  on 103  degrees of freedom
## Residual deviance: 162246  on  99  degrees of freedom
## AIC: 163254
## 
## Number of Fisher Scoring iterations: 5
print(train_model_3km)
## Generalized Linear Model 
## 
## 104 samples
##   3 predictor
## 
## No pre-processing
## Resampling: Leave-One-Out Cross-Validation 
## Summary of sample sizes: 103, 103, 103, 103, 103, 103, ... 
## Resampling results:
## 
##   RMSE      Rsquared    MAE     
##   2904.301  0.05092564  1766.576
summary(train_model_3km)
## 
## Call:
## NULL
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -73.324  -25.390  -11.284    9.963  149.735  
## 
## Coefficients:
##                Estimate Std. Error  z value Pr(>|z|)    
## (Intercept)   7.464e+00  4.870e-03 1532.691   <2e-16 ***
## prop_pop_3km  9.929e-03  7.484e-05  132.678   <2e-16 ***
## year2018     -3.717e-03  5.477e-03   -0.679    0.497    
## year2019     -1.965e-01  5.848e-03  -33.596   <2e-16 ***
## year2020      5.834e-01  4.887e-03  119.391   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 201942  on 103  degrees of freedom
## Residual deviance: 162692  on  99  degrees of freedom
## AIC: 163699
## 
## Number of Fisher Scoring iterations: 5
LS0tDQp0aXRsZTogIkFuYWx5c2lzIG9mIFJvbGUgb2YgRGFtcyBvbiBSZWNvcmRlZCBEcnkgU2Vhc29uIE1hbGFyaWEgSW5jaWRlbmNlcyBpbiBLYXN1bmd1Ig0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KbGF0ZXhfZW5naW5lOiBNaUtUZVgNCmF1dGhvcjogQ2xpbnRvbiBOa29sb2tvc2ENCmRhdGU6ICIgTGFzdCBlZGl0ZWQgYHIgZm9ybWF0KFN5cy50aW1lKCksJyVkICVCICVZJylgIg0KLS0tDQoNClRoaXMgZ2Vvc3BhdGlhbCBzdGF0aXN0aWNhbCBtb2RlbCB1c2VzIHJvdXRpbmVseSBjb2xsZWN0ZWQgbWFsYXJpYSBjYXNlIGRhdGEsIHBvcHVsYXRpb24gZGF0YSBhbmQgcmVtb3RlbHkgc2Vuc2VkIGRhdGEsIHN1Y2ggYXMgb3BlbiBhbmQgdmVnZXRhdGVkIHdhdGVyIGJvZGllcywgdG8gZXN0aW1hdGUgcG9wdWxhdGlvbiBsaXZpbmcgYXJvdW5kIG9wZW4gd2F0ZXIgYm9kaWVzLCBleHBlY3RlZCBtYWxhcmlhIGNhc2VzLCBhbmQgYHN0YW5kYXJkaXNlZCBtb3JiaWRpdHkgcmF0aW9gIChgU01SYCkgb2YgbWFsYXJpYS4gQW5kIHVsdGltYXRlbHksIHF1YW50aWZ5IHRoZSBhc3NvY2lhdGlvbiBiZXR3ZWVuIHByb3hpbWl0eSB0byBsYXJ2YWwgaGFiaXRhdCBhbmQgbWFsYXJpYSByaXNrIGluIGhlYWx0aCBmYWNpbGl0eSBjYXRjaG1lbnQgYXJlYXMgaW4gS2FzdW5ndS4gVGhlIGBTTVJgIGNvbXBhcmVzIHRoZSByaXNrIG9mIG1vcmJpZGl0eSBpbiBhIHBvcHVsYXRpb24gb2YgaW50ZXJlc3Qgd2l0aCB0aGF0IG9mIGEgc3RhbmRhcmQgcG9wdWxhdGlvbi4gSW4gdGhpcyBjYXNlLCBvdXIgaW50ZXJlc3QgaXMgdG8gZmluZCBvdXQgd2hldGhlciB0aGUgbnVtYmVyIG9mIGRyeSBzZWFzb24gbWFsYXJpYSBjYXNlcyBpbiBlYWNoIGNhdGNobWVudCBhcmVhIGFyZSBncmVhdGVyIHRoYW4gd2Ugd291bGQgZXhwZWN0IGdpdmVuIHRoZSBtYWxhcmlhIHJhdGUgZm9yIHRoZSBlbnRpcmUgS2FzdW5ndSBkaXN0cmljdC4NCg0KV2UgZG8gdGhpcyBieSBjb21wYXJpbmcgd2hhdCB3ZSAqb2JzZXJ2ZSogKE8pIHdpdGggd2hhdCB3ZSB3b3VsZCAqZXhwZWN0KiAoRSkgaWYgdGhlIHJpc2sgb2YgbWFsYXJpYSB3YXMgZXF1YWwgdGhyb3VnaG91dCBLYXN1bmd1LiBUaGUgU01SIHN0YXRpc3RpY2FsIG5vdGF0aW9uIG9mIGNhdGNobWVudCAqaSogY2FuIGJlIHdyaXR0ZW4gYXMgZm9sbG93czogJCRTTVJfaSA9IFxmcmFje09faX17RV9pfSQkDQoNCkJ1ZmZlcnMgYXJvdW5kIHdhdGVyYm9kaWVzIGFyZSBjcmVhdGVkIGFuZCB0aGVuIGNvbWJpbmVkIHdpdGggcG9wdWxhdGlvbiBkYXRhIGluIHJhc3RlciBmb3JtYXQgdG8gZXN0aW1hdGUgdGhlIHByb3BydGlvbiBvZiBjYXRjbWVudCBwb3B1bGF0aW9uIGxpdmluZyB3aXRoaW4gMWttLCAya20gYW5kIDNrbSBvZiB3YXRlciBib2RpZXMuIFN1YnNlcXVlbnRseSwgdGhlIG9ic2VydmVkIG1hbGFyaWEgY2FzZXMgYXJlIG1vZGVsZWQgdXNpbmcgYFBvaXNzb24gcmVncmVzc2lvbmAgdG8gZmluZCBvdXQgaWYgbGl2aW5nIHdpdGhpbiB2YXJpb3VzIGRpc3RhbmNlcyBmcm9tIHdhdGVyIGJvZGllcyBpcyBjYXVzaW5nIHZhcmlhYmlsaXR5IGluIG1hbGFyaWEgcmlzayBpbiBLYXN1bmd1IGRpc3RyaWN0LiBXZSBoeXBvdGhlc2l6ZSB0aGF0IHRoZSByaXNrIG9mIGJlaW5nIGEgY2FzZSBpbiBhIGNhdGNobWVudCBpcyBkZXBlbmRlbnQgb24gcHJveGltaXR5IHRvIHdhdGVyIGJvZGllcy4gVGhlIGRhdGEgdXNlZCBzcGFucyBmcm9tIDIwMTcgdG8gMjAyMCBhbmQgd2FzIGRlcml2ZWQgZnJvbSBkaWdpdGl6ZWQgREhJUzIgbWFsYXJpYSByZWNvcmRzLCBhY2Nlc3NpYmlsaXR5IG1hcHBpbmcsIGFnZ3JlZ2F0ZWQgcG9wdWxhdGlvbiBnZW9zcGF0aWFsIGxheWVyIGFuZCBUcm9wV2V0IHRvb2wgaW4gR29vZ2xlIEVhcnRoIEVuZ2luZS4NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLmFsaWduID0gJ2NlbnRlcicsIGVjaG8gPSBUUlVFKQ0KDQojIHRoZW1hdGljOjp0aGVtYXRpY19ybWQoKQ0KDQpgYGANCg0KIyBMb2FkIHBhY2thZ2VzDQoNCkxvYWRpbmcgdGhlIFIgcGFja2FnZXMgdGhhdCB3aWxsIGJlIHVzZWQgdG8gcmVhZCBpbiwgdmlldywgdHJhbnNmb3JtIGFuZCBtb2RlbCB0aGUgbWFsYXJpYSBjYXNlcyBhbmQgc3BhdGlhbCBkYXRhc2V0cy4NCg0KYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsNCiAgbGlicmFyeShTcGF0aWFsRXBpKQ0KICBsaWJyYXJ5KHNwZGVwKQ0KICBsaWJyYXJ5KHNwYU1NKQ0KICBsaWJyYXJ5KHBvcEVwaSkNCiAgbGlicmFyeShFcGkpDQogIGxpYnJhcnkoZXBpdG9vbHMpICAgICAgIyBjb21wdXRlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIG9mIG1hbGFyaWEgZGF0YQ0KICBsaWJyYXJ5KHRpZHl2ZXJzZSkNCiAgbGlicmFyeShjYXJldCkgICAgICAgICAjIGVhc2lseSBjb21wdXRlIGNyb3NzLXZhbGlkYXRpb24gbWV0aG9kcyB0byB0ZXN0IG1vZGVsIHBlcmZvbWFuY2UNCiAgbGlicmFyeShzdGF0cykNCiAgbGlicmFyeShNQVNTKSAgICAgICAgICAjIGZpdCBuZWdhdGl2ZSBiaW5vbWlhbCBtb2RlbA0KICBsaWJyYXJ5KHBlcmZvcm1hbmNlKSAgICMgY2hlY2sgbGluZWFyIG1vZGVsIHBlcmZvcm1hbmNlDQogIGxpYnJhcnkoc2VlKSAgICAgICAgICAgIyB0byBwbG90IG1vZGVsIGFzc3VtcHRpb25zDQogIGxpYnJhcnkoY2FUb29scykgICAgICAgIyBzcGxpdHRpbmcgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0IGRhdGEgc2V0cw0KICBsaWJyYXJ5KGdsbW1UTUIpICAgICAgICMgY2hlY2sgb3ZlcmRpc3BlcnNpb24NCiAgbGlicmFyeShsbWU0KSAgICAgICAgICAjIGZpdCBHTE0NCiAgbGlicmFyeShyZXBvcnQpICAgICAgICAjIGF1dG9tYXRpY2FsbHkgcHJvZHVjZSByZWdyZXNzaW9uIG1vZGVsIHJlcG9ydHMNCiAgbGlicmFyeShxcXBsb3RyKQ0KICBsaWJyYXJ5KERlc2NUb29scykgICAgICMgdG9vbHMgZm9yIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MgZS5nLiwgcHNldWRvIFItc3F1YXJlZA0KICBsaWJyYXJ5KGd0c3VtbWFyeSkgICAgICMgZWFzaWx5IGRpc3BsYXkgcmVncmVzc2lvbiBtb2RlbCBvdXRwdXRzLCBzdWNoIGFzIFAgdmFsdWUsIENJDQogIGxpYnJhcnkoZ2dwdWJyKQ0KICBsaWJyYXJ5KHBsb3RseSkNCiAgbGlicmFyeShsdWJyaWRhdGUpDQogIGxpYnJhcnkoa25pdHIpDQogIGxpYnJhcnkocmFzdGVyKQ0KICBsaWJyYXJ5KHJnZGFsKQ0KICBsaWJyYXJ5KHJnZW9zKQ0KICBsaWJyYXJ5KHJlYWRyKQ0KICBsaWJyYXJ5KHNmKQ0KICBsaWJyYXJ5KHNwKQ0KICBsaWJyYXJ5KHRtYXApDQogIGxpYnJhcnkoc3BkZXApDQogIGxpYnJhcnkobWFwdG9vbHMpDQogIGxpYnJhcnkoZ3JpZEV4dHJhKQ0KICBsaWJyYXJ5KGdnc2NpKQ0KICBsaWJyYXJ5KGdyaWQpDQogIGxpYnJhcnkoZXhhY3RleHRyYWN0cikNCiAgbGlicmFyeShEYXRhRXhwbG9yZXIpDQogIGxpYnJhcnkodGhlbWF0aWMpDQogIGxpYnJhcnkobWFwdmlldykNCiAgbGlicmFyeShrYWJsZUV4dHJhKSAgICAjIGNyZWF0ZSBpbnRlcmFjdGl2ZSB0YWJsZXMNCiAgbGlicmFyeShndCkgICAgICAgICAgICAjIGNyZWF0ZSBiZWF1dGlmdWwgSFRNTCB0YWJsZXMNCiAgbGlicmFyeShwYW5kZXIpICAgICAgICAjIGltcHJvdmVzIHRoZSBhZXN0aGV0aWNzIG9mIFIgb3V0cHV0cw0KICBsaWJyYXJ5KGltcHV0ZVRTKSAgICAgICMgZWFzaWx5IHJlbW92ZSBOQXMNCiAgYCU+JWAgPC0gbWFncml0dHI6OmAlPiVgDQoNCn0pDQogIA0KDQpgYGANCg0KIyMgVGVsbCBSIHdoZXJlIHRoZSBkYXRhIGlzDQoNCmBgYHtyfQ0KZmlsZS5wYXRoKGdldHdkKCksImRhdGEiKQ0KDQpoZXJlOjpoZXJlKCkNCmBgYA0KDQojIExvYWQgZGF0YXNldHMNCg0KVGhlIHRvdGFsIGRyeSBzZWFzb24gbWFsYXJpYSBjYXNlcyByZWNvcmRlZCBhdCBoZWFsdGgtY2FyZSBmYWNpbGl0aWVzIGluIEthc3VuZ3UgZnJvbSAyMDE3IHRvIDIwMTkgYXJlIGNvbnRhaW5lZCBpbiB0aGUgYEthc3VuZ3VEYXRhLmNzdmAgc291cmNlZCBmcm9tIDxodHRwczovL2RoaXMyLmhlYWx0aC5nb3YubXcvPi4gVGhlIGBrYXN1bmd1X2ZhY2lsaXR5X2NhdGNobWVudHNfMjAwNC5zaHBgIHNoYXBlZmlsZSBhbHNvIGNvbnRhaW5zIHRoZSBwb3B1bGF0aW9uIGFuZCBoZWFsdGggaW5mb3JtYXRpb24gd2l0aGluIGVhY2ggaGVhbHRoLWZhY2lsaXR5IGNhdGNobWVudCBhcmVhIGluIEthc3VuZ3UgZGlzdHJpY3QuDQoNClRoZSBhZ2dyZWdhdGVkIHBvcHVsYXRpb24gcmFzdGVyIGxheWVycyBmb3IgTWFsYXdpIGUuZy4sYGt1X3BvcF8yMDE3XzFrbV9hZ2dyZWdhdGVkLnRpZmAgd2VyZSBkb3dubG9hZGVkIGZyb20gdGhlIE9wZW4gU3BhdGlhbCBhbmQgRGVtb2dyYXBoaWMgYW5kIERhdGEgUmVzZWFyY2ggd2Vic2l0ZTogPGh0dHBzOi8vd3d3LndvcmxkcG9wLm9yZy9nZW9kYXRhL2NvdW50cnk/aXNvMz1NV0k+LiBUaGVzZSBsYXllcnMgZXN0aW1hdGUgdG90YWwgbnVtYmVyIG9mIHBlb3BsZSBwZXIgZ3JpZC1jZWxsLiBUaGUgdW5pdHMgYXJlIG51bWJlciBvZiBwZW9wbGUgcGVyIHBpeGVsIHdpdGggY291bnRyeSB0b3RhbHMgYWRqdXN0ZWQgdG8gbWF0Y2ggdGhlIGNvcnJlc3BvbmRpbmcgb2ZmaWNpYWwgVW5pdGVkIE5hdGlvbnMgcG9wdWxhdGlvbiBlc3RpbWF0ZXMuIFRoZSBkYXRhc2V0cyB3ZXJlIGRvd25sb2FkZWQgaW4gR2VvdGlmZiBhdCBhIHJlc29sdXRpb24gb2YgMWttIGFuZCBhcmUgcHJvamVjdGVkIGluIEdlb2dyYXBoaWMgQ29vcmRpbmF0ZSBTeXN0ZW0sIFdHUzg0Lg0KDQpUaGUgYGthc3VuZ3Vfd2F0ZXIuc2hwYGFuZCBgd2F0ZXJfYm9kaWVzYCBsYXllcnMgY29udGFpbiBvcGVuIGFuZCB2ZWdldGF0ZWQgd2F0ZXJib2RpZXMgcG9seWdvbnMsIGRldGVjdGVkIHVzaW5nIHRoZSBUcm9waWNhbCBXZXRsYW5kIFVubWl4aW5nIFRvb2wgKFRyb3BXZXQpLiBUcm9wV2V0IGlzIGEgR29vZ2xlIEVhcnRoIEVuZ2luZSBob3N0ZWQgdG9vbGJveCB0aGF0IHVzZXMgdGhlIExhbmRzYXQgYXJjaGl2ZSB0byBtYXAgdHJvcGljYWwgd2V0bGFuZHMgYW5kIGNhbiBiZSBhY2Nlc3NlZCB0aHJvdWdoOiA8aHR0cHM6Ly93d3cuYWJlci5hYy51ay9lbi9kZ2VzL3Jlc2VhcmNoL2VhcnRoLW9ic2VydmF0aW9uLWxhYm9yYXRvcnkvcmVzZWFyY2gvdHJvcHdldC8+DQoNCmBgYHtyLCBLYXN1bmd1IHdhdGVyIGJvZGllcyBwb2x5Z29ucywgbWFsYXJpYSBjYXNlcyBieSBoZWFsdGggZmFjaWxpdHkgY2F0Y2htZW50cyBhbmQgcG9wdWxhdGlvbiBsYXllcnMsIG1lc3NhZ2U9IEZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCg0KIyBLYXN1bmd1IGRyeSBzZWFzb24gbWFsYXJpYSBkYXRhDQojIGRyeV9zZWFzb25fbWFsYXJpYV8yMDE3XzIwMjAgPC0gcmVhZHI6OnJlYWRfY3N2KA0KIyAgICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vQ2xpbnR5TmtvbG9rb3NhL0FuYWx5c2lzLW9mLWRyeS1zZWFzb24tbWFsYXJpYS1jYXNlcy1pbi1LYXN1bmd1L21haW4vZGF0YS9kcnlfc2Vhc29uX21hbGFyaWFfMjAxN18yMDIwLmNzdiIpDQoNCmRyeV9zZWFzb25fbWFsYXJpYV8yMDE3XzIwMjAgPC0gcmVhZC5jc3YoaGVyZTo6aGVyZSgiZGF0YS9kcnlfc2Vhc29uX21hbGFyaWFfMjAxN18yMDIwLmNzdiIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQoNCiMgS2FzdW5ndSBtb250aGx5IE5NQ1AgY29uZmlybWVkIG1hbGFyaWEgY2FzZXMNCm1vbnRobHlfbWFsYXJpYV8yMDE3XzIwMjEgPC0gcmVhZC5jc3YoaGVyZTo6aGVyZSgiZGF0YS9LYXN1bmd1IG1vbnRobHkgbWFsYXJpYSAyMDE3LTIwMjEuY3N2IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCg0KbW9udGhseV9tYWxhcmlhXzIwMTdfMjAyMSRkYXRlIDwtIGx1YnJpZGF0ZTo6eW0obW9udGhseV9tYWxhcmlhXzIwMTdfMjAyMSRwZXJpb2RpZCkgDQoNCiMgS2FzdW5ndSBkaXN0cmljdCBib3VuZGFyeSBzaGFwZWZpbGUgDQprYXN1bmd1X2Rpc3RyaWN0IDwtIHNmOjpzdF9yZWFkKGhlcmU6OmhlcmUoImRhdGEiLCAia2FzdW5ndV9kaXN0cmljdC5zaHAiKSkNCiAgDQojIEthc3VuZ3UgaGVhbHRoIGZhY2lsaXR5IGNhdGNobWVudHMgZ2VuZXJhdGVkIGZyb20gYWNjZXNzaWJpbGl0eSBtYXBwaW5nDQptYWxpcmVfbmV3IDwtIHNmOjpzdF9yZWFkKGhlcmU6OmhlcmUoImRhdGEiLCAiemlwYXRhbGFfY2F0Y2htZW50X2FyZWFzLnNocCIpKSB8PiANCiAgICAgICAgICAgICAgc2Y6OnN0X3RyYW5zZm9ybSgzMjczNikgIyByZXByb2plY3QgdG8gV0dTIFVUTSBab25lIDM2IFNvdXRoDQoNCiMgS2FzdW5ndSBwb3B1bGF0aW9uIHJhc3RlciBsYXllcg0Ka2FzdW5ndV9wb3B1bGF0aW9uXzIwMTcgPC0gcmFzdGVyKGhlcmU6OmhlcmUoImRhdGEiLCAia3VfcG9wXzIwMTdfMWttX2FnZ3JlZ2F0ZWQudGlmIikpDQoNCmthc3VuZ3VfcG9wdWxhdGlvbl8yMDE4IDwtIHJhc3RlcihoZXJlOjpoZXJlKCJkYXRhIiwgImt1X3BvcF8yMDE4XzFrbV9hZ2dyZWdhdGVkLnRpZiIpKQ0KDQprYXN1bmd1X3BvcHVsYXRpb25fMjAxOSA8LSByYXN0ZXIoaGVyZTo6aGVyZSgiZGF0YSIsICJrdV9wb3BfMjAxOV8xa21fYWdncmVnYXRlZC50aWYiKSkNCg0Ka2FzdW5ndV9wb3B1bGF0aW9uXzIwMjAgPC0gcmFzdGVyKGhlcmU6OmhlcmUoImRhdGEiLCAia3VfcG9wXzIwMjBfMWttX2FnZ3JlZ2F0ZWQudGlmIikpDQoNCiMgUmVhZCBpbiB3YXRlcmJvZGllcyBwb2x5Z29ucyANCmRyeXNlYXNvbl93YXRlcmJvZGllc18yMDE3IDwtIHNmOjpzdF9yZWFkKGhlcmU6OmhlcmUoImRhdGEiLCAid2F0ZXJfYm9kaWVzXzIwMTcuc2hwIikpDQoNCmRyeXNlYXNvbl93YXRlcmJvZGllc18yMDE4IDwtIHNmOjpzdF9yZWFkKGhlcmU6OmhlcmUoImRhdGEiLCAia2FzdW5ndV8yMDE4X3dhdGVyLnNocCIpKQ0KDQpkcnlzZWFzb25fd2F0ZXJib2RpZXNfMjAxOSA8LSBzZjo6c3RfcmVhZChoZXJlOjpoZXJlKCJkYXRhIiwgImthc3VuZ3VfMjAxOV93YXRlci5zaHAiKSkNCg0KZHJ5c2Vhc29uX3dhdGVyYm9kaWVzXzIwMjAgPC0gc2Y6OnN0X3JlYWQoaGVyZTo6aGVyZSgiZGF0YSIsICJ3YXRlcl9ib2RpZXNfMjAyMC5zaHAiKSkNCg0KIyBBZGQgYSByb3cgSUQgdG8gd2F0ZXIgYm9kaWVzIHBvbHlnb25zIA0KZHJ5c2Vhc29uX3dhdGVyYm9kaWVzXzIwMTckSUQgPC0gMTpucm93KGRyeXNlYXNvbl93YXRlcmJvZGllc18yMDE3KQ0KDQpkcnlzZWFzb25fd2F0ZXJib2RpZXNfMjAxOCRJRCA8LSAxOm5yb3coZHJ5c2Vhc29uX3dhdGVyYm9kaWVzXzIwMTgpDQoNCmRyeXNlYXNvbl93YXRlcmJvZGllc18yMDE5JElEIDwtIDE6bnJvdyhkcnlzZWFzb25fd2F0ZXJib2RpZXNfMjAxOSkNCg0KZHJ5c2Vhc29uX3dhdGVyYm9kaWVzXzIwMjAkSUQgPC0gMTpucm93KGRyeXNlYXNvbl93YXRlcmJvZGllc18yMDIwKQ0KDQpgYGANCg0KIyMgVmlldyB0aGUgZHJ5IHNlYXNvbiBtYWxhcmlhIGNhc2UgZGF0YQ0KTGV0cyBoYXZlIGEgY2xvc2VyIGxvb2sgYXQgdGhlIG1hbGFyaWEgZGF0YXNldC4NCldlIG9ic2VydmUgdGhhdCBLYXN1bmd1IGRpc3RyaWN0IGhhcyAzMCBoZWFsdGggZmFjaWxpdGllcyBjbGFzc2lmaWVkIGFzIGRpc3BlbnNhcnksIGhlYWx0aCBjZW50cmUsIGRpc3RyaWN0IGhvc3BpdGFsIGFuZCBydXJhbCBob3NwaXRhbCwgYW5kIHRoZSBoaWdoZXN0IG1hbGFyaWEgY2FzZXMgd2VyZSByZWNvcmRlZCBhdCBLYXN1bmd1IERpc3RyaWN0IEhvc3BpdGFsLg0KDQpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy5oZWlnaHQgPSAxMCwgZmlnLndpZHRoID0gOCwgZmlnLmNhcCA9ICdGaWcuMSBUaGUgdG90YWwgbWFsYXJpYSBjYXNlcyByZWNvcmRlZCBhdCBlYWNoIGhlYWx0aC1jYXJlIGZhY2lsaXR5IGluIEthc3VuZ3UgZGlzdHJpY3QnfQ0KDQojIFBsb3RseSBiYXIgY2hhcnQgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KYmFyX2NoYXJ0IDwtIGRyeV9zZWFzb25fbWFsYXJpYV8yMDE3XzIwMjAgfD4gIA0KICBkcGx5cjo6ZmlsdGVyKE5hbWVzICE9ICJLMiBUYXNvIENsaW5pYyIsICAgICAgICAgICMgSGF2ZSBtaXNzaW5nIG1hbGFyaWEgcmVjb3Jkcw0KICAgICAgICAgICAgICAgIE5hbWVzICE9ICJLYWxpa2VuaSBQcml2YXRlIENsaW5pYyIsDQogICAgICAgICAgICAgICAgTmFtZXMgIT0gIktha3dhbGUgSGVhbHRoIENlbnRyZSIsDQogICAgICAgICAgICAgICAgTmFtZXMgIT0gIlN0IEFuZHJld3MgQ29tbXVuaXR5IEhvc3BpdGFsIiwNCiAgICAgICAgICAgICAgICBOYW1lcyAhPSAiU3QuIEZhaXRoIEhlYWx0aCBDZW50cmUiLA0KICAgICAgICAgICAgICAgIE5hbWVzICE9ICJDaGFtYndlIEhlYWx0aCBDZW50cmUiKSB8PiANCiAgcGxvdGx5OjpwbG90X2x5KHkgPSB+TmFtZXMsDQogICAgICAgICAgICAgICAgICB4ID0gfmRyXzIwMTcsDQogICAgICAgICAgICAgICAgICB0eXBlID0gImJhciIsDQogICAgICAgICAgICAgICAgICBvcmllbnRhdGlvbiA9ICdoJywNCiAgICAgICAgICAgICAgICAgIG5hbWUgPSAiMjAxNyIpIHw+DQogIHBsb3RseTo6YWRkX3RyYWNlKHggPSB+IGRyXzIwMTgsDQogICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiMjAxOCIpIHw+DQogIHBsb3RseTo6YWRkX3RyYWNlKHggPSB+IGRyXzIwMTksDQogICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiMjAxOSIpIHw+DQogIHBsb3RseTo6YWRkX3RyYWNlKHggPSB+IGRyXzIwMjAsDQogICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiMjAyMCIpIHw+IA0KICBwbG90bHk6OmxheW91dCh4YXhpcyA9IGxpc3QodGl0bGUgPSAiVG90YWwgbWFsYXJpYSBjYXNlcyIpLA0KICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiICIpLA0KICAgICAgICAgICAgICAgICBob3Zlcm1vZGUgPSAiY29tcGFyZSIsDQogICAgICAgICAgICAgICAgIG1hcmdpbiA9IGxpc3QoYiA9IDEwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHQgPSAxMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYWQgPSAyKSkNCmJhcl9jaGFydA0KDQpgYGANCg0KIyMjIFllYXJseSB2YXJpYXRpb24gaW4gbWFsYXJpYSBjYXNlcw0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTEwLCBmaWcuY2FwPSAnRmlnLjI6IFNlYXNvbmFsIHZhcmlhdGlvbiBpbiBjb25maXJtZWQgbWFsYXJpYSBjYXNlcyd9DQoNCmdncGxvdDI6OmdncGxvdChtb250aGx5X21hbGFyaWFfMjAxN18yMDIxKSArDQogIGFlcyh4ID0gZGF0ZSwgDQogICAgICB5ID0gTk1DUCkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDAuNiwgDQogICAgICAgICAgICBjb2xvdXIgPSAiIzExMjQ0NiIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSArDQogIGxhYnMoeCA9ICJZZWFyIiwgDQogICAgICAgeSA9ICJDb25maXJtZWQgbWFsYXJpYSBjYXNlcyIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCmBgYA0KDQoNCiMgS2FzdW5ndSBoZWFsdGgtY2FyZSBmYWNpbGl0aWVzIGFuZCB0aGVpciBjYXRjaG1lbnQgYXJlYXMNCg0KSGVhdGggZmFjaWxpdHkgY2F0Y2htZW50IGFyZWEgaXMgdGhlIGFyZWEgZnJvbSB3aGljaCBhIGhlYWx0aCBmYWNpbGl0eSBhdHRyYWN0cyBwYXRpZW50cy4gU2luY2UgdGhlIGF2YWlsYWJsZSBvZmZpY2lhbCBjYXRjaG1lbnQgYXJlYXMgYXJlIG91dGRhdGVkIGFuZCBubyByZWNlbnQgc3BhdGlhbCBkYXRhIGFib3V0IHRoZSBjYXRjaG1lbnQgYXJlYXMgaXMgYXZhaWxhYmxlLCBuZXcgaGVhbHRoIGZhY2lsaXR5IGNhdGNobWVudHMgcG9seWdvbiB3ZXJlIGdlbmVyYXRlZCBmcm9tIGdlbmVyaWMgYWNjZXNzaWJpbGl0eSBtYXBwaW5nIHNjcmlwdCBhZGFwdGVkIGZyb20gPGh0dHBzOi8vbWFsYXJpYWF0bGFzLm9yZy93cC1jb250ZW50L3VwbG9hZHMvYWNjZXNzaWJpbGl0eS9SX2dlbmVyaWNfYWNjZXNzaWJpbHR5X21hcHBpbmdfc2NyaXB0LnI+IFRoZSBzY3JpcHQgcmVxdWlyZXMgdHdvIHVzZXIgc3VwcGxpZWQgZGF0YXNldHM6IHRoZSBgMjAxNSBmcmljdGlvbiBzdXJmYWNlYCwgd2hpY2ggaXMgYXZhaWxhYmxlIGhlcmU6IDxodHRwOi8vd3d3Lm1hcC5veC5hYy51ay9hY2Nlc3NpYmlsaXR5X3RvX2NpdGllcy8+LCBhbmQgYSB1c2VyLXN1cHBsaWVkIC5jc3Ygb2YgcG9pbnRzIGBkcnlfc2Vhc29uX21hbGFyaWFfMjAxN18yMDIwYC4gVGhlIGFjY3VtdWxhdGVkIGNvc3QgYWxnb3JpdGhtIGBhY2NDb3N0YCBhbmQgYHIuQ29zdGAgYWxnb3JpdGhtIGluIFFHSVMgd2VyZSBydW4gdG8gbWFrZSB0aGUgZmluYWwgb3V0cHV0IG1hcCBvZiBuZXcgaGVhbHRoIGZhY2lsaXR5IGNhdGNobWVudCBib3VuZGFyaWVzLg0KDQpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQoNCiMgVXNpbmcgdGhlIGNvbXBsZXRlLmNhc2VzKCkgZnVuY3Rpb24gdG8gc2VsZWN0IGhlYWx0aCBjZW50cmVzIHdpdGggY29tcGxldGUgDQojIGxvbmdpdHVkZSBhbmQgbGF0aXR1ZGUgY29vcmRpbmF0ZXMuDQp6aXBhdGFsYSA8LSBkcnlfc2Vhc29uX21hbGFyaWFfMjAxN18yMDIwW2NvbXBsZXRlLmNhc2VzKGRyeV9zZWFzb25fbWFsYXJpYV8yMDE3XzIwMjApLF0gDQoNCiMgQWdncmVnYXRlIGhlYWx0aCBmYWNpbGl0aWVzIGNsb3NlIHRvIGVhY2ggb3RoZXI6IA0KIyAgYSkgS2FzYWxpa2EgSGVhbHRoIENlbnRyZSBhbmQgS2FzdW5ndSBEaXN0cmljdCBIb3NwaXRhbCwgDQojICBiKSBCdWEgYW5kIE16aXphIEhlYWx0aCBDZW50cmVzLCBhbmQgDQojICBjKSBLYWx1bHVtYSBhbmQgTmtoYW1lbnlhIFJ1cmFsIEhvc3BpdGFscyANCiMgIGQpIEFuY2hvciBhbmQgU2FudGhlIEhlYWx0aCBDZW50cmVzIGFyZSBjb21iaW5lZCBpbiBvcmRlciB0byANCiMgZ2VuZXJhdGUgY2F0Y2htZW50IGFyZWFzIHRoYXQgYXJlIGdlb2dyYXBoaWNhbGx5IGNvcnJlY3QNCg0KemlwYXRhbGEkZHJfMjAxN1t3aGljaCgNCiAgemlwYXRhbGEkTmFtZXMgPT0gIkthc3VuZ3UgRGlzdHJpY3QgSG9zcGl0YWwiKV0gPC0gemlwYXRhbGEkZHJfMjAxN1t3aGljaCgNCiAgICB6aXBhdGFsYSROYW1lcyA9PSAiS2FzdW5ndSBEaXN0cmljdCBIb3NwaXRhbCIpXSArIHppcGF0YWxhJGRyXzIwMTdbd2hpY2goDQogICAgICB6aXBhdGFsYSROYW1lcyA9PSAiS2FzYWxpa2EgSGVhbHRoIENlbnRyZSIpXQ0KDQp6aXBhdGFsYSRkcl8yMDE4W3doaWNoKA0KICB6aXBhdGFsYSROYW1lcyA9PSAiS2FzdW5ndSBEaXN0cmljdCBIb3NwaXRhbCIpXSA8LSB6aXBhdGFsYSRkcl8yMDE4W3doaWNoKA0KICAgIHppcGF0YWxhJE5hbWVzID09ICJLYXN1bmd1IERpc3RyaWN0IEhvc3BpdGFsIildICsgemlwYXRhbGEkZHJfMjAxOFt3aGljaCgNCiAgICAgIHppcGF0YWxhJE5hbWVzID09ICJLYXNhbGlrYSBIZWFsdGggQ2VudHJlIildDQoNCnppcGF0YWxhJGRyXzIwMTlbd2hpY2goDQogIHppcGF0YWxhJE5hbWVzID09ICJLYXN1bmd1IERpc3RyaWN0IEhvc3BpdGFsIildIDwtIHppcGF0YWxhJGRyXzIwMTlbd2hpY2goDQogICAgemlwYXRhbGEkTmFtZXMgPT0gIkthc3VuZ3UgRGlzdHJpY3QgSG9zcGl0YWwiKV0gKyB6aXBhdGFsYSRkcl8yMDE5W3doaWNoKA0KICAgICAgemlwYXRhbGEkTmFtZXMgPT0gIkthc2FsaWthIEhlYWx0aCBDZW50cmUiKV0NCg0KemlwYXRhbGEkZHJfMjAyMFt3aGljaCgNCiAgemlwYXRhbGEkTmFtZXMgPT0gIkthc3VuZ3UgRGlzdHJpY3QgSG9zcGl0YWwiKV0gPC0gemlwYXRhbGEkZHJfMjAyMFt3aGljaCgNCiAgICB6aXBhdGFsYSROYW1lcyA9PSAiS2FzdW5ndSBEaXN0cmljdCBIb3NwaXRhbCIpXSArIHppcGF0YWxhJGRyXzIwMjBbd2hpY2goDQogICAgICB6aXBhdGFsYSROYW1lcyA9PSAiS2FzYWxpa2EgSGVhbHRoIENlbnRyZSIpXQ0KICANCnppcGF0YWxhJGRyXzIwMTdbd2hpY2goDQogIHppcGF0YWxhJE5hbWVzID09ICJOa2hhbWVueWEgUnVyYWwgSG9zcGl0YWwiKV0gPC0gemlwYXRhbGEkZHJfMjAxN1t3aGljaCgNCiAgICB6aXBhdGFsYSROYW1lcyA9PSAiTmtoYW1lbnlhIFJ1cmFsIEhvc3BpdGFsIildICsgemlwYXRhbGEkZHJfMjAxN1t3aGljaCgNCiAgICAgIHppcGF0YWxhJE5hbWVzID09ICJLYWx1bHVtYSBSdXJhbCBIb3NwaXRhbCIpXQ0KDQp6aXBhdGFsYSRkcl8yMDE4W3doaWNoKA0KICB6aXBhdGFsYSROYW1lcyA9PSAiTmtoYW1lbnlhIFJ1cmFsIEhvc3BpdGFsIildIDwtIHppcGF0YWxhJGRyXzIwMThbd2hpY2goDQogICAgemlwYXRhbGEkTmFtZXMgPT0gIk5raGFtZW55YSBSdXJhbCBIb3NwaXRhbCIpXSAremlwYXRhbGEkZHJfMjAxOFt3aGljaCgNCiAgICAgIHppcGF0YWxhJE5hbWVzID09ICJLYWx1bHVtYSBSdXJhbCBIb3NwaXRhbCIpXQ0KDQp6aXBhdGFsYSRkcl8yMDE5W3doaWNoKA0KICB6aXBhdGFsYSROYW1lcyA9PSAiTmtoYW1lbnlhIFJ1cmFsIEhvc3BpdGFsIildIDwtIHppcGF0YWxhJGRyXzIwMTlbd2hpY2goDQogICAgemlwYXRhbGEkTmFtZXMgPT0gIk5raGFtZW55YSBSdXJhbCBIb3NwaXRhbCIpXSArIHppcGF0YWxhJGRyXzIwMTlbd2hpY2goDQogICAgICB6aXBhdGFsYSROYW1lcyA9PSAiS2FsdWx1bWEgUnVyYWwgSG9zcGl0YWwiKV0NCg0KemlwYXRhbGEkZHJfMjAyMFt3aGljaCgNCiAgemlwYXRhbGEkTmFtZXMgPT0gIk5raGFtZW55YSBSdXJhbCBIb3NwaXRhbCIpXSA8LSB6aXBhdGFsYSRkcl8yMDIwW3doaWNoKA0KICAgIHppcGF0YWxhJE5hbWVzID09ICJOa2hhbWVueWEgUnVyYWwgSG9zcGl0YWwiKV0gKyB6aXBhdGFsYSRkcl8yMDIwW3doaWNoKA0KICAgICAgemlwYXRhbGEkTmFtZXMgPT0gIkthbHVsdW1hIFJ1cmFsIEhvc3BpdGFsIildDQoNCnppcGF0YWxhJGRyXzIwMTdbd2hpY2goDQogIHppcGF0YWxhJE5hbWVzID09ICJNeml6YSBIZWFsdGggQ2VudHJlIildIDwtIHppcGF0YWxhJGRyXzIwMTdbd2hpY2goDQogICAgemlwYXRhbGEkTmFtZXMgPT0gIk16aXphIEhlYWx0aCBDZW50cmUiKV0gKyB6aXBhdGFsYSRkcl8yMDE3W3doaWNoKA0KICAgICAgemlwYXRhbGEkTmFtZXMgPT0gIkJ1YSBIZWFsdGggQ2VudHJlIildDQoNCnppcGF0YWxhJGRyXzIwMThbd2hpY2goDQogIHppcGF0YWxhJE5hbWVzID09ICJNeml6YSBIZWFsdGggQ2VudHJlIildIDwtIHppcGF0YWxhJGRyXzIwMThbd2hpY2goDQogICAgemlwYXRhbGEkTmFtZXMgPT0gIk16aXphIEhlYWx0aCBDZW50cmUiKV0gKyB6aXBhdGFsYSRkcl8yMDE4W3doaWNoKA0KICAgICAgemlwYXRhbGEkTmFtZXMgPT0gIkJ1YSBIZWFsdGggQ2VudHJlIildDQoNCnppcGF0YWxhJGRyXzIwMTlbd2hpY2goDQogIHppcGF0YWxhJE5hbWVzID09ICJNeml6YSBIZWFsdGggQ2VudHJlIildIDwtIHppcGF0YWxhJGRyXzIwMTlbd2hpY2goDQogICAgemlwYXRhbGEkTmFtZXMgPT0gIk16aXphIEhlYWx0aCBDZW50cmUiKV0gKyB6aXBhdGFsYSRkcl8yMDE5W3doaWNoKA0KICAgICAgemlwYXRhbGEkTmFtZXMgPT0gIkJ1YSBIZWFsdGggQ2VudHJlIildIA0KICANCg0KemlwYXRhbGEkZHJfMjAyMFt3aGljaCgNCiAgemlwYXRhbGEkTmFtZXMgPT0gIk16aXphIEhlYWx0aCBDZW50cmUiKV0gPC0gemlwYXRhbGEkZHJfMjAyMFt3aGljaCgNCiAgICB6aXBhdGFsYSROYW1lcyA9PSAiTXppemEgSGVhbHRoIENlbnRyZSIpXSArIHppcGF0YWxhJGRyXzIwMjBbd2hpY2goDQogICAgICB6aXBhdGFsYSROYW1lcyA9PSAiQnVhIEhlYWx0aCBDZW50cmUiKV0NCg0KemlwYXRhbGEkZHJfMjAxN1t3aGljaCgNCiAgemlwYXRhbGEkTmFtZXMgPT0gIlNhbnRoZSBIZWFsdGggQ2VudHJlIildIDwtIHppcGF0YWxhJGRyXzIwMTdbd2hpY2goDQogICAgemlwYXRhbGEkTmFtZXMgPT0gIlNhbnRoZSBIZWFsdGggQ2VudHJlIildICsgemlwYXRhbGEkZHJfMjAxN1t3aGljaCgNCiAgICAgIHppcGF0YWxhJE5hbWVzID09ICJBbmNob3IgRmFybSIpXQ0KDQp6aXBhdGFsYSRkcl8yMDE4W3doaWNoKA0KICB6aXBhdGFsYSROYW1lcyA9PSAiU2FudGhlIEhlYWx0aCBDZW50cmUiKV0gPC0gemlwYXRhbGEkZHJfMjAxOFt3aGljaCgNCiAgICB6aXBhdGFsYSROYW1lcyA9PSAiU2FudGhlIEhlYWx0aCBDZW50cmUiKV0gKyB6aXBhdGFsYSRkcl8yMDE4W3doaWNoKA0KICAgICAgemlwYXRhbGEkTmFtZXMgPT0gIkFuY2hvciBGYXJtIildDQoNCnppcGF0YWxhJGRyXzIwMTlbd2hpY2goDQogIHppcGF0YWxhJE5hbWVzID09ICJTYW50aGUgSGVhbHRoIENlbnRyZSIpXSA8LSB6aXBhdGFsYSRkcl8yMDE5W3doaWNoKA0KICAgIHppcGF0YWxhJE5hbWVzID09ICJTYW50aGUgSGVhbHRoIENlbnRyZSIpXSArIHppcGF0YWxhJGRyXzIwMTlbd2hpY2goDQogICAgICB6aXBhdGFsYSROYW1lcyA9PSAiQW5jaG9yIEZhcm0iKV0NCg0KemlwYXRhbGEkZHJfMjAyMFt3aGljaCgNCiAgemlwYXRhbGEkTmFtZXMgPT0gIlNhbnRoZSBIZWFsdGggQ2VudHJlIildIDwtIHppcGF0YWxhJGRyXzIwMjBbd2hpY2goDQogICAgemlwYXRhbGEkTmFtZXMgPT0gIlNhbnRoZSBIZWFsdGggQ2VudHJlIildICsgemlwYXRhbGEkZHJfMjAyMFt3aGljaCgNCiAgICAgIHppcGF0YWxhJE5hbWVzID09ICJBbmNob3IgRmFybSIpXQ0KDQoNCiMgRHJvcCBvdXQgdGhlIG90aGVyIGhlYWx0aCBmYWNpbGl0aWVzDQp6aXBhdGFsYV9hZ2dyZWdhdGVkIDwtIHppcGF0YWxhIHw+DQogIGRwbHlyOjpmaWx0ZXIoTmFtZXMgIT0gIkthc2FsaWthIEhlYWx0aCBDZW50cmUiLA0KICAgICAgICAgICAgICAgIE5hbWVzICE9ICJCdWEgSGVhbHRoIENlbnRyZSIsDQogICAgICAgICAgICAgICAgTmFtZXMgIT0gIkthbHVsdW1hIFJ1cmFsIEhvc3BpdGFsIiwNCiAgICAgICAgICAgICAgICBOYW1lcyAhPSAiQW5jaG9yIEZhcm0iKQ0KDQojIFNhdmUgY3N2IGZpbGUNCiMgd3JpdGUuY3N2KHppcGF0YWxhX2FnZ3JlZ2F0ZWQsICJkYXRhL3ppcGF0YWxhX2FnZ3JlZ2F0ZWQuY3N2IikNCg0KIyBDb252ZXJ0IHRvIHNmIG9iamVjdA0KemlwYXRhbGFfYWdncmVnYXRlZF9zZiA8LSBzZjo6c3RfYXNfc2YoemlwYXRhbGFfYWdncmVnYXRlZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb29yZHMgPSBjKCJMT05HSVRVIiwgIkxBVElUVUQiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjcnMgPSA0MzI2LCBhZ3IgPSAiY29uc3RhbnQiKQ0KDQojIHN0X3dyaXRlKHppcGF0YWxhX2FnZ3JlZ2F0ZWRfc2YsICJkYXRhL3ppcGF0YWxhX2NvbWJpbmVkLnNocCIpDQoNCmBgYA0KDQojIyBWaWV3IGxvY2F0aW9uIG9mIHRoZSBoZWFsdGggZmFjaWxpdGllcyBpbiB0aGUgbmV3IGNhdGNobWVudCBhcmVhcw0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgZmlnLmNhcD0nRmlnIDMuIEthc3VuZ3UgaGVhbHRoLWNhcmUgZmFjaWxpdGllcyBhbmQgY2F0Y2htZW50IGFyZWFzJ30NCg0KIyBQbG90IG1hcA0KdG1fc2hhcGUobWFsaXJlX25ldykrDQogIHRtX3BvbHlnb25zKCkrDQogIHRtX3NoYXBlKHppcGF0YWxhX2FnZ3JlZ2F0ZWRfc2YpKw0KICB0bV9kb3RzKHNpemUgPSAuMywgDQogICAgICAgICAgY29sID0gImJsdWUiLCANCiAgICAgICAgICBhbHBoYSA9IDAuNSkrDQogIHRtX3RleHQoIk5hbWVzIiwgDQogICAgICAgICAgc2l6ZSA9IC4zLCANCiAgICAgICAgICBqdXN0ID0gInRvcCIsIA0KICAgICAgICAgIGNvbCA9ICJibGFjayIsIA0KICAgICAgICAgIHJlbW92ZS5vdmVybGFwID0gVFJVRSkrDQogIHRtX2xheW91dChmcmFtZSA9IEZBTFNFLA0KICAgICAgICAgICAgdGl0bGUgPSAiTmV3IEthc3VuZ3UgaGVhbHRoIGZhY2lsaXR5IFxuIGNhdGNobWVudCBib3VuZGFyaWVzIiwNCiAgICAgICAgICAgIHRpdGxlLnNpemUgPSAuOCwgDQogICAgICAgICAgICB0aXRsZS5wb3NpdGlvbiA9IGMoImxlZnQiLCAidG9wIikpKw0KICB0bV9jb21wYXNzKHBvc2l0aW9uPWMoInJpZ2h0IiwgInRvcCIpKSsNCiAgdG1fc2NhbGVfYmFyKGJyZWFrcyA9IGMoMCwgMTAsIDIwKSwgDQogICAgICAgICAgICAgICB0ZXh0LnNpemUgPSAuNSkNCg0KYGBgDQoNCiMjIEthc3VuZ3UgZGlzdHJpY3QgZXN0aW1hdGVkIHBvcHVsYXRpb24gcGVyIGdyaWQtY2VsbA0KDQpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGZpZy5oZWlnaHQgPSA5LCBmaWcud2lkdGggPSAxMCwgZmlnLmNhcCA9ICdGaWcuNCBFc3RpbWF0ZWQgdG90YWwgbnVtYmVyIG9mIHBlb3BsZSBwZXIgMWttIGdyaWQtY2VsbCd9DQoNCiMgQ3VzdG9tIGZ1bmN0aW9uIHRvIGNyZWF0ZSBhIHJhc3RlciBwb3B1bGF0aW9uIG1hcCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjcmVhdGUucG9wdWxhdGlvbi5tYXAgPC0gZnVuY3Rpb24ocG9wdWxhdGlvbi5yYXN0ZXIsIHRpdGxlKXsNCiAgIyByYXN0ZXIgcG9wdWxhdGlvbiBtYXANCiAgIyBhcmd1bWVudHM6DQogICMgICBwb3B1bGF0aW9uLnJhc3RlcjogIGFnZ3JlZ2F0ZWQgcG9wdWxhdGlvbiByYXN0ZXIgbGF5ZXIgZnJvbSBXb3JsZFBvcA0KICAjICAgbGVnZW5kLnRpdGxlOiBsZWdlbmQgdGl0bGUNCiAgIyByZXR1cm5zOg0KICAjICAgYSB0bWFwLWVsZW1lbnQgKHBsb3RzIGEgbWFwKQ0KICANCiAgIyBzZXQgdG8gaW50ZXJhY3RpdmUgdmlldw0KICAjIHRtYXA6OnRtYXBfbW9kZSgidmlldyIpDQogIA0KICAjIHBsb3QgbWFwDQogIHRtYXA6OnRtX3NoYXBlKHBvcHVsYXRpb24ucmFzdGVyKSsNCiAgICB0bWFwOjp0bV9yYXN0ZXIocGFsZXR0ZSA9ICItdmlyaWRpcyIsIA0KICAgICAgICAgICAgICAgICAgICB0aXRsZSA9IHRpdGxlLA0KICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDAsMTAwLDIwMCw0MDAsNjAwLDgwMCwxMDAwLDIwMDAsNDAwMCw2MDAwLDgwMDApKSsNCiAgICB0bWFwOjp0bV9sYXlvdXQobGVnZW5kLnBvc2l0aW9uID0gYygicmlnaHQiLCAiYm90dG9tIiksDQogICAgICAgICAgICAgICAgICAgIGZyYW1lID0gRkFMU0UpKw0KICAgIHRtYXA6OnRtX3NjYWxlX2Jhcihwb3NpdGlvbiA9IGMoImxlZnQiLCAiYm90dG9tIikpDQp9DQoNCiMgU2V0IHRvIHN0YXRpYyBtYXANCiB0bWFwX21vZGUoInBsb3QiKQ0KIA0KIyBJbnZva2luZyBmdW5jdGlvbiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmVzdGltYXRlZF9wb3BfMjAxNyA8LSBjcmVhdGUucG9wdWxhdGlvbi5tYXAoa2FzdW5ndV9wb3B1bGF0aW9uXzIwMTcsIHRpdGxlID0gIjIwMTcgUG9wdWxhdGlvbiIpDQoNCmVzdGltYXRlZF9wb3BfMjAxOCA8LSBjcmVhdGUucG9wdWxhdGlvbi5tYXAoa2FzdW5ndV9wb3B1bGF0aW9uXzIwMTgsIHRpdGxlID0gIjIwMTggUG9wdWxhdGlvbiIpDQoNCmVzdGltYXRlZF9wb3BfMjAxOSA8LSBjcmVhdGUucG9wdWxhdGlvbi5tYXAoa2FzdW5ndV9wb3B1bGF0aW9uXzIwMTksIHRpdGxlID0gIjIwMTkgUG9wdWxhdGlvbiIpDQoNCmVzdGltYXRlZF9wb3BfMjAyMCA8LSBjcmVhdGUucG9wdWxhdGlvbi5tYXAoa2FzdW5ndV9wb3B1bGF0aW9uXzIwMjAsIHRpdGxlID0gIjIwMjAgUG9wdWxhdGlvbiIpDQoNCiMgTGF5b3V0IHRoZSBtYXBzDQp0bWFwOjp0bWFwX2FycmFuZ2UoZXN0aW1hdGVkX3BvcF8yMDE3LCBlc3RpbWF0ZWRfcG9wXzIwMTgsIA0KICAgICAgICAgICAgICAgICAgIGVzdGltYXRlZF9wb3BfMjAxOSwgZXN0aW1hdGVkX3BvcF8yMDIwLCBucm93ID0gMikgDQoNCmBgYA0KDQojIEFzc2lnbiBkcnkgc2Vhc29uIG1hbGFyaWEgY2FzZXMgYW5kIHBvcHVsYXRpb24gZGVuc2l0eSB0byBuZXcgaGVhbHRoIGZhY2lsaXR5IGNhdGNobWVudHMNCg0KVGhlIFdvcmxkUG9wIGBhZ2dyZWdhdGVkIHBvcHVsYXRpb25gIGUuZy4gYGthc3VuZ3VfcG9wdWxhdGlvbl8yMDE3LnRpZmAsIGFuZCBESElTMiBtYWxhcmlhIGBkcnlfc2Vhc29uX21hbGFyaWFfMjAxN18yMDIwYCBkYXRhc2V0cyBhcmUgYXNzaWduZWQgdG8gdGhlIG5ldyBoZWFsdGggZmFjaWxpdHkgY2F0Y2htZW50cy4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIEhlbHBlciBmdW5jdGlvbiB0aGF0IGFzc2lnbnMgbWFsYXJpYSBkYXRhIGZyb20gaGVhbHRoIGZhY2lsaXRpZXMgdG8gdGhlaXIgY2F0Y2htZW50cyBhcmVhcyANCmFzc2lnbi5tYWxhcmlhLmRhdGEgPC0gZnVuY3Rpb24oY2F0Y2htZW50X2JvdW5kYXJ5LCBtYWxhcmlhX2RhdGEpew0KICAjIGFyZ3VtZW50czoNCiAgIyAgIGNhdGNobWVudF9ib3VuZGFyeTogc2YgcG9seWdvbiBvYmplY3Qgb2YgbmV3IGNhdGNobWVudCBib3VuZGFyaWVzDQogICMgICBtYWxhcmlhX2RhdGE6IHNmIHBvaW50IG9iamVjdCB3aXRoIGEgZGF0YSBmcmFtZSBjb250YWluaW5nIHRoZSBkcnkgc2Vhc29uIG1hbGFyaWEgY2FzZXMNCiAgIyByZXR1cm5zOg0KICAjICAgY2F0Y2htZW50c19tYWxhcmlhX3NmOiBzZiBwb2x5Z29uIG9iamVjdCB3aXRoIGEgZGF0YSBmcmFtZSBjb250YWluaW5nIGRyeSBzZWFzb24gbWFsYXJpYSBjYXNlcw0KDQoNCiAgIyBDb252ZXJ0IHNmIG9iamVjdHMgdG8gc3BhdGlhbA0KICBjYXRjaG1lbnRfc2hwIDwtIGFzKGNhdGNobWVudF9ib3VuZGFyeSwgIlNwYXRpYWwiKQ0KICANCiAgbWFsYXJpYV9zaHAgPC0gYXMobWFsYXJpYV9kYXRhLCAiU3BhdGlhbCIpDQoNCiAgIyBNYXRjaCBDUlMNCiAgbWFsYXJpYV9zaHAgPC0gc3BUcmFuc2Zvcm0obWFsYXJpYV9zaHAsIGNycyhjYXRjaG1lbnRfc2hwKSkNCg0KICAjIE92ZXJsYXkgYWdncmVnYXRlZCBoZWFsdGggZmFjaWxpdHkgcG9pbnRzIGFuZCBleHRyYWN0IDIwMTcgLSAyMDIwIG1hbGFyaWEgY2FzZXMNCiAgIyBVc2luZyAncG9pbnQuaW4ucG9seScgdG8gcmV0dXJuIGEgcG9pbnQgc3BhdGlhbCBvYmplY3QsIGluIHRoaXMgY2FzZSBsb2NhdGlvbiBvZiBoZWFsdGggZmFjaWxpdGllcw0KICAjIGFuZCBlc3RpbWF0ZWQgcG9wdWxhdGlvbiBpbnN0ZWFkIG9mIHNwOjpvdmVyIGZ1bmN0aW9uLCB3aGljaCBzaW1wbHkgcmV0dXJucyANCiAgIyBhIGRhdGEgZnJhbWUsIHdpdGggdGhlIHNhbWUgbm8uIHJvd3MuDQogICMgQXJndW1lbnQgJ3NwID0gVFJVRScgcmV0dXJucyBhbiBzcCBjbGFzcyBvYmplY3QsIGVsc2UgcmV0dXJucyBzZiBjbGFzcyBvYmplY3QNCiAgIyBKb2luaW5nIHRoZSBtYWxhcmlhIGFuZCBwb3B1bGF0aW9uIGRhdGFzZXQgdXNpbmcgb25seSAnbWVyZ2UnIGZ1bmN0aW9uIGNhbid0IHdvcmsgZHVlIHRvIA0KICAjIG5vbi11bmlxdWUgY29sdW1ucyBhbmQgZGlmZmVyZW5jZXMgaW4gcm93IG51bWJlcnMNCiAgDQogIGhvc3BpdGFsc19pbl9jYXRjaG1lbnQgPC0gc3BhdGlhbEVjbzo6cG9pbnQuaW4ucG9seShtYWxhcmlhX3NocCwgY2F0Y2htZW50X3NocCwgc3AgPSBUUlVFKSANCg0KICAjIEFkZCB0aGUgZXh0cmFjdGVkIElELCBoZWFsdGggZmFjaWxpdHkgbmFtZXMgYW5kIGRyeSBzZWFzb24gbWFsYXJpYSBjYXNlcyB0byANCiAgIyB0aGUgaGVhbHRoIGZhY2lsaXR5IGNhdGNobWVudHMgKGhmYykNCiAgaGZjX21hbGFyaWFfc2hwIDwtIG1lcmdlKGNhdGNobWVudF9zaHAsIGhvc3BpdGFsc19pbl9jYXRjaG1lbnQsIGJ5LnggPSAiRE4iLCBieS55ID0gInJvd0lEIikNCg0KICAjIENvbnZlcnQgdGhlIHNoYXBlZmlsZSBjb250YWluaW5nIG1hbGFyaWEgZGF0YSB0byBzZi1vYmplY3QNCiAgaGZjX21hbGFyaWFfc2YgPC0gc2Y6OnN0X2FzX3NmKGhmY19tYWxhcmlhX3NocCkNCg0KICAjIFRpZHkgdGhlIGRhdGEgYnkgZHJvcHBpbmcgY29sdW1ucyBub3QgbmVlZGVkDQogIGNhdGNobWVudF9tYWxhcmlhIDwtIGhmY19tYWxhcmlhX3NmIHw+IA0KICAgIGRwbHlyOjpzZWxlY3QoLWMoY29vcmRzLngxLCBjb29yZHMueDIpKQ0KDQogIHJldHVybihvdXQgPSBjYXRjaG1lbnRfbWFsYXJpYSkNCn0NCg0KDQojIEludm9raW5nIHRoZSBmdW5jdGlvbiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KbWFsYXJpYV9ieV9jYXRjaG1lbnQgPC0gYXNzaWduLm1hbGFyaWEuZGF0YShtYWxpcmVfbmV3LCB6aXBhdGFsYV9hZ2dyZWdhdGVkX3NmKQ0KDQpgYGANCg0KIyBBc3NpZ24gcG9wdWxhdGlvbiBkYXRhIHRvIHRoZSBoZWFsdGggY2F0Y2htZW50IGFyZWFzDQoNClRoZSBwb3B1bGF0aW9uIHJhc3RlciBpcyBhIGNvbnRpbnVvdXMgZ3JpZGRlZCBzdXJmYWNlIGxheWVyIHRoYXQgaGFzIGFuIGVzdGltYXRlZCBwb3B1bGF0aW9uIGRlbnNpdHkgdmFsdWUgdG8gZXZlcnkgc3F1YXJlIGluIHRoZWlyIGdyaWQuIFRoZSBwb3B1bGF0aW9uIHZhbHVlcyBhcmUgZXh0cmFjdGVkIHVzaW5nIGByYXN0ZXI6OmV4dHJhY3QoKWAsIHN1bW1lZCBhbmQgYXBwb3J0aW9uZWQgdG8gdGhlIGNhdGNobWVudCBwb2x5Z29ucy4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0V9DQoNCiMgSGVscGVyIHVuY3Rpb24gdG8gZXh0cmFjdCBwb3B1bGF0aW9uIGZyb20gV29ybGRQb3AgcmFzdGVyIGZpbGUgYW5kIGFzc2lnbg0KIyB0aGUgdmFsdWVzIHRvIHRoZSBuZXcgY2F0Y2htZW50cy4NCg0KZXh0cmFjdC5wb3AudmFsdWVzIDwtIGZ1bmN0aW9uKGthc3VuZ3VfcG9wX3Jhc3RlciwgY2F0Y2htZW50cyl7DQogICMgZnVuY3Rpb24gdG8gZXh0cmFjdCBwb3B1bGF0aW9uIGZyb20gcmFzdGVyIGZpbGUgYW5kIGFzc2lnbiB0aGUgcG9wdWxhdGlvbiB0byBjYXRjaG1lbnRzDQogICMgYXJndW1lbnRzOg0KICAjICAga2FzdW5ndV9wb3BfcmFzdGVyOiBwb3B1bGF0aW9uIHJhc3RlciBmaWxlIGNsaXBwZWQgdG8gS2FzdW5ndSBkaXN0cmljdA0KICAjICAgY2F0Y2htZW50czogc2hhcGVmaWxlIGNvbnRhaW5pbmcgdGhlIHBvbHlnb25zIHRoYXQgd2Ugd2lsbCB1c2UgYXMgYm91bmRhcmllcw0KICAjIHJldHVybnM6DQogICMgICBjYXRjaG1lbnRzX21hbGFyaWFfcG9wX3NmOiBzZiBwb2x5Z29uIG9iamVjdCBjb250YWluaW5nIG1hbGFyaWEgYW5kIHBvcHVsYXRpb24gZGF0YQ0KICANCiAgIyBjb252ZXJ0IGZyb20gc2YgdG8gc3ANCiAgY2F0Y2htZW50c19zcCA8LSBhcyhjYXRjaG1lbnRzLCAiU3BhdGlhbCIpDQogIA0KICAjIE1hdGNoIGV4dGVudCBpLmUgcHJvamVjdGlvbg0KICBjYXRjaG1lbnRzX3NwIDwtIHNwVHJhbnNmb3JtKGNhdGNobWVudHNfc3AsIHByb2o0c3RyaW5nKGthc3VuZ3VfcG9wX3Jhc3RlcikpDQogIA0KICAjIENyb3AgYW5kIG1hc2sgdGhlIHBvcHVsYXRpb24gcmFzdGVyIHRvIGV4Y2x1ZGUgS2FzdW5ndSBOYXRpb25hbCBQYXJrDQogIHBvcF9yYXN0ZXJfY2xpcCA8LSByYXN0ZXI6OmNyb3Aoa2FzdW5ndV9wb3BfcmFzdGVyLCBleHRlbnQoY2F0Y2htZW50c19zcCkpIHw+DQogICAgcmFzdGVyOjptYXNrKGNhdGNobWVudHNfc3ApDQogIA0KICAjIEV4dHJhY3Rpbmcgem9uYWwgc3RhdGlzdGljcyBmcm9tIGEgcG9wdWxhdGlvbiByYXN0ZXIgbGF5ZXINCiAgcG9wX2J5X2NhdGNobWVudCA8LSByb3VuZChyYXN0ZXI6OmV4dHJhY3QocG9wX3Jhc3Rlcl9jbGlwLCBjYXRjaG1lbnRzLCBmdW4gPSBzdW0sIG5hLnJtID0gVFJVRSkpDQogIA0KICBwb3BfYnlfY2F0Y2htZW50X2RmIDwtICBwb3BfYnlfY2F0Y2htZW50ICU+JSAgDQogICMgYXBwbHkgdW5saXN0IHRvIHRoZSBsaXN0cyB0byBoYXZlIHZlY3RvcnMgYXMgdGhlIGxpc3QgZWxlbWVudHMNCiAgbGFwcGx5KHVubGlzdCkgJT4lICANCiAgIyBjb252ZXJ0IHZlY3RvcnMgdG8gZGF0YS5mcmFtZXMNCiAgbGFwcGx5KGFzX3RpYmJsZSkgJT4lICAgDQogICMgY29tYmluZSB0aGUgbGlzdCBvZiBkYXRhLmZyYW1lcw0KICBiaW5kX3Jvd3MoLiwgLmlkID0gInJvd0lEIikgJT4lICAgDQogICMgcmVuYW1lIHRoZSB2YWx1ZSB2YXJpYWJsZQ0KICBkcGx5cjo6cmVuYW1lKHBvcCA9IHZhbHVlKQ0KICANCiAgIyBBZGQgcm93IElEIHRvIGNvbHVtbiB0byBjYXRjaG1lbnQgbGF5ZXINCiAgY2F0Y2htZW50cyRyb3dJRCA8LSAxOm5yb3coY2F0Y2htZW50cykNCiAgDQogICMgTWVyZ2UgY2F0Y2htZW50IGFyZWFzIGFuZCBwb3B1bGF0aW9uIGRhdGEgDQogIHBvcF9ieV9jYXRjaG1lbnRzIDwtIG1lcmdlKGNhdGNobWVudHMsIHBvcF9ieV9jYXRjaG1lbnRfZGYsIGJ5ID0gInJvd0lEIikNCiAgDQogICMgQ2xlYW5pbmcgJ0luZicgdmFsdWVzDQogIHBvcF9ieV9jYXRjaG1lbnRzIHw+IA0KICAgIGRwbHlyOjptdXRhdGVfaWYoaXMubnVtZXJpYywgbGlzdCh+bmFfaWYoLiwgSW5mKSkpIHw+IA0KICAgIGRwbHlyOjptdXRhdGVfaWYoaXMubnVtZXJpYywgbGlzdCh+bmFfaWYoLiwgLUluZikpKQ0KDQogIHJldHVybihvdXQgPSBwb3BfYnlfY2F0Y2htZW50cykNCiAgDQp9DQoNCiMgSW52b2tpbmcgdGhlIGZ1bmN0aW9uIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCm1hbGFyaWFfcG9wX2J5X2NhdGNobWVudF8yMDE3IDwtIGV4dHJhY3QucG9wLnZhbHVlcyhrYXN1bmd1X3BvcHVsYXRpb25fMjAxNywgbWFsYXJpYV9ieV9jYXRjaG1lbnQpDQoNCm1hbGFyaWFfcG9wX2J5X2NhdGNobWVudF8yMDE4IDwtIGV4dHJhY3QucG9wLnZhbHVlcyhrYXN1bmd1X3BvcHVsYXRpb25fMjAxOCwgbWFsYXJpYV9ieV9jYXRjaG1lbnQpDQoNCm1hbGFyaWFfcG9wX2J5X2NhdGNobWVudF8yMDE5IDwtIGV4dHJhY3QucG9wLnZhbHVlcyhrYXN1bmd1X3BvcHVsYXRpb25fMjAxOSwgbWFsYXJpYV9ieV9jYXRjaG1lbnQpDQoNCm1hbGFyaWFfcG9wX2J5X2NhdGNobWVudF8yMDIwIDwtIGV4dHJhY3QucG9wLnZhbHVlcyhrYXN1bmd1X3BvcHVsYXRpb25fMjAyMCwgbWFsYXJpYV9ieV9jYXRjaG1lbnQpDQoNCg0KYGBgDQoNCiMjIFZpZXcgcG9wdWxhdGlvbiBieSBjYXRjaG1lbnQgbWFwcw0KDQpFc3RpbWF0ZWQgdG90YWwgbnVtYmVyIG9mIHBlb3BsZSB3aXRoaW4gaGVhbHRoIGZhY2lsaXR5IGNhdGNobWVudCBhcmVhcy4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIGZpZy5oZWlnaHQgPSA1LCBmaWcud2lkdGggPSA5LCBmaWcuY2FwID0gJ0ZpZy4gNTogRXN0aW1hdGVkIHBvcHVsYXRpb24gYnkgaGVhbHRoIGZhY2lsaXR5IGNhdGNobWVudCBhcmVhcyd9DQojIG1heChtYWxhcmlhX3BvcF9ieV9jYXRjaG1lbnRfMjAxNyRwb3ApDQojIFsxXSAxNDM0OTANCiMgbWF4KG1hbGFyaWFfcG9wX2J5X2NhdGNobWVudF8yMDE4JHBvcCkNCiMgWzFdIDE0NzE3NQ0KIyBtYXgobWFsYXJpYV9wb3BfYnlfY2F0Y2htZW50XzIwMTkkcG9wKQ0KIyBbMV0gMTUxMDc5DQojIG1heChtYWxhcmlhX3BvcF9ieV9jYXRjaG1lbnRfMjAyMCRwb3ApDQojIFsxXSAxODUyODINCg0KIyBDdXN0b20gZnVuY3Rpb24gdG8gY3JlYXRlIG1hcHMgb2YgZXN0aW1hdGVkIHBvcHVsYXRpb24gYnkgY2F0Y2htZW50IGFyZWFzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjcmVhdGUucG9wdWxhdGlvbi5tYXAgPC0gZnVuY3Rpb24oY2F0Y2htZW50LmFyZWEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlID0gInBvcCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQudGl0bGUgPSAiRXN0aW1hdGVkIFxuIHBvcHVsYXRpb24iKXsNCiAgIyBlc3RpbWF0ZWQgcG9wdWxhdGlvbiBtYXANCiAgIyBjYXRjaG1lbnQuYXJlYTogZXN0aW1hdGVkIHBvcHVsYXRpb24gbGF5ZXIgZnJvbSBuYWNodWx1IGZ1bmN0aW9uDQogICMgdmFyaWFibGU6IHZhcmlhYmxlIG5hbWUgKGFzIGNoYXJhY3RlciwgaW4gcW91dGVzKQ0KICAjIHRpdGxlOiBtYXAgdGl0bGUgaW4gcXVvdGVzDQogICMgbGVnZW5kLnRpdGxlOiBsZWdlbmQgdGl0bGUgaW4gcW91dGVzDQogICMgcmV0dXJuczoNCiAgIyAgIGEgdG1hcC1lbGVtZW50IChwbG90cyBhIG1hcCkNCiAgdG1fc2hhcGUoY2F0Y2htZW50LmFyZWEpKw0KICAgIHRtX2ZpbGwoY29sID0gdmFyaWFibGUsIA0KICAgICAgICAgICAgYnJlYWtzID0gYygwLCAxMzAwMCwgMTkwMDAsIDI3MDAwLCAzNTAwMCwgNzAwMDAsIDE0MDAwMCwgMjAwMDAwKSwNCiAgICAgICAgICAgIHBhbGV0dGUgPSAiWWxPckJyIiwNCiAgICAgICAgICAgIHRpdGxlID0gbGVnZW5kLnRpdGxlKSsNCiAgICB0bV9ib3JkZXJzKGNvbCA9ICJncmV5IiwNCiAgICAgICAgICAgICAgIGx3ZCA9IDAuNCkrDQogICAgdG1fbGF5b3V0KGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC43NSwgImJvdHRvbSIpLA0KICAgICAgICAgICAgICBsZWdlbmQudGV4dC5zaXplID0gMC42LA0KICAgICAgICAgICAgICBsZWdlbmQudGl0bGUuc2l6ZSA9IDAuOCwNCiAgICAgICAgICAgICAgZnJhbWUgPSBGQUxTRSkrDQogICAgdG1fY3JlZGl0cyh0aXRsZSwgDQogICAgICAgICAgICAgICBwb3NpdGlvbiA9IGMoMC4zLCAwLjgpLCANCiAgICAgICAgICAgICAgIHNpemUgPSAxKQ0KfQ0KDQojIEludm9raW5nIHRoZSBmdW5jdGlvbiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KcG9wX2J5X2NhdGNobWVudF8yMDE3IDwtIGNyZWF0ZS5wb3B1bGF0aW9uLm1hcChtYWxhcmlhX3BvcF9ieV9jYXRjaG1lbnRfMjAxNywgdGl0bGUgPSAiMjAxNyIpDQoNCnBvcF9ieV9jYXRjaG1lbnRfMjAxOCA8LSBjcmVhdGUucG9wdWxhdGlvbi5tYXAobWFsYXJpYV9wb3BfYnlfY2F0Y2htZW50XzIwMTgsIHRpdGxlID0gIjIwMTgiKQ0KDQpwb3BfYnlfY2F0Y2htZW50XzIwMTkgPC0gY3JlYXRlLnBvcHVsYXRpb24ubWFwKG1hbGFyaWFfcG9wX2J5X2NhdGNobWVudF8yMDE5LCB0aXRsZSA9ICIyMDE5IikNCg0KcG9wX2J5X2NhdGNobWVudF8yMDIwIDwtIGNyZWF0ZS5wb3B1bGF0aW9uLm1hcChtYWxhcmlhX3BvcF9ieV9jYXRjaG1lbnRfMjAyMCwgdGl0bGUgPSAiMjAyMCIpDQoNCnRtYXA6OnRtYXBfYXJyYW5nZShwb3BfYnlfY2F0Y2htZW50XzIwMTcsIHBvcF9ieV9jYXRjaG1lbnRfMjAxOCwNCiAgICAgICAgICAgICAgICAgICBwb3BfYnlfY2F0Y2htZW50XzIwMTksIHBvcF9ieV9jYXRjaG1lbnRfMjAyMCwgbmNvbCA9IDIpDQoNCg0KYGBgDQoNCiMjIFBvcHVsYXRpb24gZGVuc2l0eSBieSBjYXRjaG1lbnQNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIGZpZy5oZWlnaHQgPSA1LCBmaWcud2lkdGggPSA5LCBmaWcuY2FwID0gJ0ZpZy4gNjogRXN0aW1hdGVkIHBvcHVsYXRpb24gZGVuc2l0eSBieSBoZWFsdGggZmFjaWxpdHkgY2F0Y2htZW50IGFyZWFzJ30NCiMgSGVscGVyIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBwb3B1bGF0aW9uIGRlbnNpdHkgYnkgY2F0Y2htZW50IC0tLS0tLS0tLS0tLS0tLS0tDQpjYWxjdWxhdGUucG9wdWxhdGlvbi5kZW5zaXR5IDwtIGZ1bmN0aW9uKHBvcC5kYXRhKXsNCiAgDQogICMgQ29udmVydCB0byBzcGF0aWFsIG9iamVjdA0KICBwb3Auc3AgPC0gYXMocG9wLmRhdGEsICJTcGF0aWFsIikNCg0KICAjIENhbGN1bGF0ZSBhcmVhIG9mIGNhdGNobWVudCBwb2x5Z29uIGluIHNxdWFyZSBraWxvbWV0cmVzDQogIHBvcC5zcCRhcmVhX3Nxa20gPC0gcm91bmQocmdlb3M6OmdBcmVhKHBvcC5zcCwgYnlpZCA9IFRSVUUpIC8gKDEwMDAgKiAxMDAwKSkNCg0KICAjIENhbGN1bGF0ZSBwb3B1bGF0aW9uIGRlbnNpdHkNCiAgcG9wLnNwJHBvcF9kZW5zaXR5IDwtIHJvdW5kKHBvcC5zcCRwb3AgLyBwb3Auc3AkYXJlYV9zcWttKQ0KICANCiAgIyBDb252ZXJ0IGJhY2sgdG8gc2Ygb2JqZWN0DQogIHBvcC5zZiA8LSBzZjo6c3RfYXNfc2YocG9wLnNwKQ0KICANCiAgcmV0dXJuKHBvcC5zZikNCn0NCg0KIyBJbnZva2luZyBmdW5jdGlvbiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCnBvcF9kZW5zaXR5XzIwMTcgPC0gY2FsY3VsYXRlLnBvcHVsYXRpb24uZGVuc2l0eShtYWxhcmlhX3BvcF9ieV9jYXRjaG1lbnRfMjAxNykNCg0KcG9wX2RlbnNpdHlfMjAxOCA8LSBjYWxjdWxhdGUucG9wdWxhdGlvbi5kZW5zaXR5KG1hbGFyaWFfcG9wX2J5X2NhdGNobWVudF8yMDE4KQ0KDQpwb3BfZGVuc2l0eV8yMDE5IDwtIGNhbGN1bGF0ZS5wb3B1bGF0aW9uLmRlbnNpdHkobWFsYXJpYV9wb3BfYnlfY2F0Y2htZW50XzIwMTkpDQoNCnBvcF9kZW5zaXR5XzIwMjAgPC0gY2FsY3VsYXRlLnBvcHVsYXRpb24uZGVuc2l0eShtYWxhcmlhX3BvcF9ieV9jYXRjaG1lbnRfMjAyMCkNCg0KIyBIZWxwZXIgZnVuY3Rpb24gdG8gY3JlYXRlIHBvcHVsYXRpb24gZGVuc2l0eSBtYXBzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNyZWF0ZS5wb3AuZGVuc2l0eS5tYXAgPC0gZnVuY3Rpb24ocG9wLmRlbnNpdHkuZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUgPSAicG9wX2RlbnNpdHkiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSBOQSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9ICJQb3B1bGF0aW9uIFxuZGVuc2l0eS9rbV4yIil7DQogIHRtX3NoYXBlKHBvcC5kZW5zaXR5LmRhdGEpKw0KICAgIHRtX2ZpbGwoY29sID0gdmFyaWFibGUsIA0KICAgICAgICAgICAgYnJlYWtzID0gYygwLCA1MCwgMTAwLCAxNTAsIDIwMCwgMjUwLCAzMDAsIDM1MCksDQogICAgICAgICAgICBwYWxldHRlID0gIi1tYWdtYSIsDQogICAgICAgICAgICB0aXRsZSA9IGxlZ2VuZC50aXRsZSkrDQogICAgdG1fYm9yZGVycygpKw0KICAgIHRtX2xheW91dChsZWdlbmQucG9zaXRpb24gPSBjKDAuNzUsICJib3R0b20iKSwNCiAgICAgICAgICAgICAgbGVnZW5kLnRleHQuc2l6ZSA9IDAuNiwNCiAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlLnNpemUgPSAwLjgsDQogICAgICAgICAgICAgIGZyYW1lID0gRkFMU0UpKw0KICAgIHRtX2NyZWRpdHModGl0bGUsIA0KICAgICAgICAgICAgICAgcG9zaXRpb24gPSBjKDAuMywgMC44KSwgDQogICAgICAgICAgICAgICBzaXplID0gMSkNCn0NCg0KIyBJbnZva2luZyBmdW5jdGlvbiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCnBvcF9kZW5zaXR5XzIwMTdfbWFwIDwtIGNyZWF0ZS5wb3AuZGVuc2l0eS5tYXAocG9wX2RlbnNpdHlfMjAxNywgdGl0bGUgPSAiMjAxNyIpDQoNCnBvcF9kZW5zaXR5XzIwMThfbWFwIDwtIGNyZWF0ZS5wb3AuZGVuc2l0eS5tYXAocG9wX2RlbnNpdHlfMjAxOCwgdGl0bGUgPSAiMjAxOCIpDQoNCnBvcF9kZW5zaXR5XzIwMTlfbWFwIDwtIGNyZWF0ZS5wb3AuZGVuc2l0eS5tYXAocG9wX2RlbnNpdHlfMjAxOSwgdGl0bGUgPSAiMjAxOSIpDQoNCnBvcF9kZW5zaXR5XzIwMjBfbWFwIDwtIGNyZWF0ZS5wb3AuZGVuc2l0eS5tYXAocG9wX2RlbnNpdHlfMjAyMCwgdGl0bGUgPSAiMjAyMCIpDQoNCiMgTGF5b3V0IG1hcHMNCnRtYXA6OnRtYXBfYXJyYW5nZShwb3BfZGVuc2l0eV8yMDE3X21hcCwgcG9wX2RlbnNpdHlfMjAxOF9tYXAsDQogICAgICAgICAgICAgICAgICAgcG9wX2RlbnNpdHlfMjAxOV9tYXAsIHBvcF9kZW5zaXR5XzIwMjBfbWFwLCBuY29sID0gMikNCg0KYGBgDQoNCiMgQ2FsY3VsYXRlIHRoZSBleHBlY3RlZCBudW1iZXIgb2YgY2FzZXMgZm9yIGVhY2ggY2F0Y2htZW50IGFyZWENCg0KVGhlIGBleHBlY3RlZGAgbnVtYmVyIG9mIGRyeSBzZWFzb24gbWFsYXJpYSBjYXNlcyBpbiBjYXRjaG1lbnQgKmkqIGFyZSBjYWxjdWxhdGVkIGFzIHRoZSBvYnNlcnZlZCByaXNrIChyKSBvZiBtYWxhcmlhIGkuZS4gdGhlIHRvdGFsIG51bWJlciBvZiBtYWxhcmlhIGNhc2VzIGluIEthc3VuZ3UgZGlzdHJpY3QgZGl2aWRlZCBieSB0aGUgdG90YWwgcG9wdWxhdGlvbiBvZiB0aGUgZGlzdHJpY3QsIG11bHRpcGxpZWQgYnkgdGhlIG51bWJlciBvZiBwZW9wbGUgaW4gdGhlIGNhdGNobWVudCBhcmVhOiAkJEVfaSA9IFxmcmFje1xzdW1faSBPX2l9e1xzdW1faSBOX2l9XHRpbWVzIE5faSQkDQoNClRoZSBgZXhwZWN0ZWRgIG51bWJlciBvZiBkcnkgc2Vhc29uIG1hbGFyaWEgY2FzZXMgYXJlIGNhbGN1bGF0ZWQgdW5kZXIgdGhlIGFzc3VtcHRpb24gdGhhdCB0aGVyZSBpcyBubyBzcGF0aWFsIHZhcmlhdGlvbiBpbiByaXNrLCBpLmUuLCBubyBkaWZmZXJlbmNlIGluIGluZmVjdGlvbiByYXRlcyBiZXR3ZWVuIHRoZSBjYXRjaG1lbnQgYXJlYXMuDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQ0KIyBDb21wdXRlIGFuZCBwcmludCB0aGUgb3ZlcmFsbCBpbmNpZGVuY2Ugb2YgZHJ5IHNlYXNvbiBtYWxhcmlhIGNhc2VzDQpvdmVyYWxsX21hbGFyaWFfaW5jaWRlbmNlX3JhdGVfMjAxNyA8LSByb3VuZChzdW0oDQogIG1hbGFyaWFfcG9wX2J5X2NhdGNobWVudF8yMDE3JGRyXzIwMTcpIC8gc3VtKA0KICAgIG1hbGFyaWFfcG9wX2J5X2NhdGNobWVudF8yMDE3JHBvcCksIDIpDQoNCm92ZXJhbGxfbWFsYXJpYV9pbmNpZGVuY2VfcmF0ZV8yMDE3DQoNCm92ZXJhbGxfbWFsYXJpYV9pbmNpZGVuY2VfcmF0ZV8yMDE4IDwtIHJvdW5kKHN1bSgNCiAgbWFsYXJpYV9wb3BfYnlfY2F0Y2htZW50XzIwMTgkZHJfMjAxOCkgLyBzdW0oDQogICAgbWFsYXJpYV9wb3BfYnlfY2F0Y2htZW50XzIwMTgkcG9wKSwgMikNCg0Kb3ZlcmFsbF9tYWxhcmlhX2luY2lkZW5jZV9yYXRlXzIwMTgNCg0Kb3ZlcmFsbF9tYWxhcmlhX2luY2lkZW5jZV9yYXRlXzIwMTkgPC0gcm91bmQoc3VtKA0KICBtYWxhcmlhX3BvcF9ieV9jYXRjaG1lbnRfMjAxOSRkcl8yMDE5KSAvIHN1bSgNCiAgICBtYWxhcmlhX3BvcF9ieV9jYXRjaG1lbnRfMjAxOSRwb3ApLCAyKQ0KDQpvdmVyYWxsX21hbGFyaWFfaW5jaWRlbmNlX3JhdGVfMjAxOQ0KDQpvdmVyYWxsX21hbGFyaWFfaW5jaWRlbmNlX3JhdGVfMjAyMCA8LSByb3VuZChzdW0oDQogIG1hbGFyaWFfcG9wX2J5X2NhdGNobWVudF8yMDIwJGRyXzIwMjApIC8gc3VtKA0KICAgIG1hbGFyaWFfcG9wX2J5X2NhdGNobWVudF8yMDIwJHBvcCksIDIpDQoNCm92ZXJhbGxfbWFsYXJpYV9pbmNpZGVuY2VfcmF0ZV8yMDIwDQoNCiMgQ2FsY3VsYXRlIGV4cGVjdGVkIG1hbGFyaWEgY2FzZXMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpleHBlY3RlZF9tYWxhcmlhXzIwMTcgPC0gbWFsYXJpYV9wb3BfYnlfY2F0Y2htZW50XzIwMTcgfD4NCiAgZHBseXI6OnJlbmFtZSgNCiAgICBvYnNlcnZlZF8yMDE3ID0gZHJfMjAxNywNCiAgICAgcG9wXzIwMTcgPSBwb3ApIHw+IA0KICBkcGx5cjo6bXV0YXRlKA0KICAgIGV4cGVjdGVkXzIwMTcgPSByb3VuZChzdW0ob2JzZXJ2ZWRfMjAxNykvc3VtKHBvcF8yMDE3LCBuYS5ybSA9IFRSVUUpKnBvcF8yMDE3KSkNCg0KZXhwZWN0ZWRfbWFsYXJpYV8yMDE4IDwtIG1hbGFyaWFfcG9wX2J5X2NhdGNobWVudF8yMDE4IHw+DQogIGRwbHlyOjpyZW5hbWUoDQogICAgb2JzZXJ2ZWRfMjAxOCA9IGRyXzIwMTgsDQogICAgcG9wXzIwMTggPSBwb3ApIHw+IA0KICBkcGx5cjo6bXV0YXRlKA0KICAgIGV4cGVjdGVkXzIwMTggPSByb3VuZChzdW0ob2JzZXJ2ZWRfMjAxOCkvc3VtKHBvcF8yMDE4LCBuYS5ybSA9IFRSVUUpKnBvcF8yMDE4KSkNCg0KZXhwZWN0ZWRfbWFsYXJpYV8yMDE5IDwtIG1hbGFyaWFfcG9wX2J5X2NhdGNobWVudF8yMDE5IHw+DQogIGRwbHlyOjpyZW5hbWUoDQogICAgb2JzZXJ2ZWRfMjAxOSA9IGRyXzIwMTksDQogICAgcG9wXzIwMTkgPSBwb3ApIHw+DQogIGRwbHlyOjptdXRhdGUoDQogICAgZXhwZWN0ZWRfMjAxOSA9IHJvdW5kKHN1bShvYnNlcnZlZF8yMDE5KS9zdW0ocG9wXzIwMTksIG5hLnJtID0gVFJVRSkqcG9wXzIwMTkpKSANCg0KZXhwZWN0ZWRfbWFsYXJpYV8yMDIwIDwtIG1hbGFyaWFfcG9wX2J5X2NhdGNobWVudF8yMDIwIHw+DQogIGRwbHlyOjpyZW5hbWUoDQogICAgb2JzZXJ2ZWRfMjAyMCA9IGRyXzIwMjAsDQogICAgcG9wXzIwMjAgPSBwb3ApIHw+DQogIGRwbHlyOjptdXRhdGUoDQogICAgZXhwZWN0ZWRfMjAyMCA9IHJvdW5kKHN1bShvYnNlcnZlZF8yMDIwKS9zdW0ocG9wXzIwMjAsIG5hLnJtID0gVFJVRSkqcG9wXzIwMjApKQ0KDQpgYGANCg0KIyBDYWxjdWxhdGUgdGhlIFN0YW5kYXJkaXNlZCBNb3JiaWRpdHkgUmF0aW8gb2YgbWFsYXJpYSBpbmNpZGVuY2VzIGZvciBlYWNoIGNhdGNobWVudCBhcmVhDQoNClRoZSBgU01SYCBjb21wYXJlcyB0aGUgcmlzayBvZiBtb3JiaWRpdHkgaW4gYSBwb3B1bGF0aW9uIG9mIGludGVyZXN0IHdpdGggdGhhdCBvZiBhIHN0YW5kYXJkIHBvcHVsYXRpb24uIEluIHRoaXMgY2FzZSwgb3VyIGludGVyZXN0IGlzIHRvIGZpbmQgb3V0IHdoZXRoZXIgdGhlIG51bWJlciBvZiBkcnkgc2Vhc29uIG1hbGFyaWEgY2FzZXMgaW4gZWFjaCBjYXRjaG1lbnQgYXJlYSBhcmUgZ3JlYXRlciB0aGFuIHdlIHdvdWxkIGV4cGVjdCBnaXZlbiB0aGUgbWFsYXJpYSByYXRlIGZvciB0aGUgZW50aXJlIEthc3VuZ3UgZGlzdHJpY3QuDQoNCldlIGRvIHRoaXMgYnkgY29tcGFyaW5nIHdoYXQgd2UgYG9ic2VydmVgIChPKSB3aXRoIHdoYXQgd2Ugd291bGQgYGV4cGVjdGAgKEUpIGlmIHRoZSByaXNrIG9mIG1hbGFyaWEgd2FzIGVxdWFsIHRocm91Z2hvdXQgS2FzdW5ndS4gVGhlIFNNUiBvZiBjYXRjaG1lbnQgKmkqIGNhbiBiZSBjYWxjdWxhdGVkIGFzIGZvbGxvd3M6ICQkU01SX2kgPSBcZnJhY3tPX2l9e0VfaX0kJA0KDQpTTVJzIGFib3ZlIDEgcmVwcmVzZW50IGhpZ2ggcmlzayBvZiBkcnkgc2Vhc29uIG1hbGFyaWEgYW5kIFNNUnMgYmVsb3cgMSwgdmljZXZlcnNhLg0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSByYXRpbyBvZiBvYnNlcnZlZCB0byBleHBlY3RlZCAoU01SKSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpTTVJfMjAxNyA8LSBleHBlY3RlZF9tYWxhcmlhXzIwMTcgfD4NCiAgZHBseXI6Om11dGF0ZShTTVIgPSByb3VuZChvYnNlcnZlZF8yMDE3L2V4cGVjdGVkXzIwMTcsIDEpKSB8PiANCiAgZHBseXI6OnNlbGVjdChyb3dJRCxOYW1lcywgcG9wXzIwMTcsIG9ic2VydmVkXzIwMTcsIGV4cGVjdGVkXzIwMTcsIFNNUikgDQoNClNNUl8yMDE4IDwtIGV4cGVjdGVkX21hbGFyaWFfMjAxOCB8PiANCiAgZHBseXI6Om11dGF0ZShTTVIgPSByb3VuZChvYnNlcnZlZF8yMDE4L2V4cGVjdGVkXzIwMTgsIDEpKSB8PiANCiAgZHBseXI6OnNlbGVjdChyb3dJRCwgTmFtZXMsIHBvcF8yMDE4LCBvYnNlcnZlZF8yMDE4LCBleHBlY3RlZF8yMDE4LCBTTVIpIA0KDQpTTVJfMjAxOSA8LSBleHBlY3RlZF9tYWxhcmlhXzIwMTkgfD4gDQogIGRwbHlyOjptdXRhdGUoU01SID0gcm91bmQob2JzZXJ2ZWRfMjAxOS9leHBlY3RlZF8yMDE5LCAxKSkgfD4gDQogIGRwbHlyOjpzZWxlY3Qocm93SUQsIE5hbWVzLCBwb3BfMjAxOSwgb2JzZXJ2ZWRfMjAxOSwgZXhwZWN0ZWRfMjAxOSwgU01SKSANCg0KU01SXzIwMjAgPC0gZXhwZWN0ZWRfbWFsYXJpYV8yMDIwIHw+IA0KICBkcGx5cjo6bXV0YXRlKFNNUiA9IHJvdW5kKG9ic2VydmVkXzIwMjAvZXhwZWN0ZWRfMjAyMCwgMSkpIHw+IA0KICBkcGx5cjo6c2VsZWN0KHJvd0lELCBOYW1lcywgcG9wXzIwMjAsIG9ic2VydmVkXzIwMjAsIGV4cGVjdGVkXzIwMjAsIFNNUikNCg0KDQojIENyZWF0ZSBTTVIgdGFibGVzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KU01SX3RhYmxlXzIwMTcgPC0gU01SXzIwMTcgfD4NCiAgZHBseXI6OmFzX3RpYmJsZSgpIHw+DQogIGRwbHlyOjpyZW5hbWUoSGVhbHRoX2ZhY2lsaXR5ID0gTmFtZXMpIHw+IA0KICBkcGx5cjo6c2VsZWN0KC1yb3dJRCwgLWdlb21ldHJ5KSB8PiANCiAga2FibGUoKSB8Pg0KICBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSkNCg0KU01SX3RhYmxlXzIwMTggPC0gU01SXzIwMTggfD4gDQogIGRwbHlyOjphc190aWJibGUoKSB8PiANCiAgZHBseXI6OnJlbmFtZShIZWFsdGhfZmFjaWxpdHkgPSBOYW1lcykgfD4gDQogIGRwbHlyOjpzZWxlY3QoLXJvd0lELCAtZ2VvbWV0cnkpIHw+DQogIGthYmxlKCkgfD4gDQogIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFKQ0KDQpTTVJfdGFibGVfMjAxOSA8LSBTTVJfMjAxOSB8Pg0KICBkcGx5cjo6YXNfdGliYmxlKCkgfD4NCiAgZHBseXI6OnJlbmFtZShIZWFsdGhfZmFjaWxpdHkgPSBOYW1lcykgfD4gDQogIGRwbHlyOjpzZWxlY3QoLXJvd0lELCAtZ2VvbWV0cnkpIHw+IA0KICBrYWJsZSAoKSB8PiANCiAga2FibGVFeHRyYTo6a2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UpDQoNClNNUl90YWJsZV8yMDIwIDwtIFNNUl8yMDIwIHw+DQogIGRwbHlyOjphc190aWJibGUoKSB8PiANCiAgZHBseXI6OnJlbmFtZShIZWFsdGhfZmFjaWxpdHkgPSBOYW1lcykgfD4gDQogIGRwbHlyOjpzZWxlY3QoLXJvd0lELCAtZ2VvbWV0cnkpIHw+IA0KICBrYWJsZSAoKSB8PiANCiAga2FibGVFeHRyYTo6a2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UpDQoNClNNUl90YWJsZV8yMDE3DQoNClNNUl90YWJsZV8yMDE4DQoNClNNUl90YWJsZV8yMDE5DQoNClNNUl90YWJsZV8yMDIwDQoNCmBgYA0KDQoNCiMjIFZpZXcgb2JzZXJ2ZWQgYW5kIGV4cGVjdGVkIGRyeSBzZWFzb24gbWFsYXJpYSBjYXNlcw0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTcsIGZpZy5jYXA9ICdGaWcuIDc6IE9ic2VydmVkIGFuZCBleHBlY3RlZCBtYWxhcmlhIGluY2lkZW5jZSBieSBoZWFsdGggZmFjaWxpdHkgY2F0Y2htZW50IGFyZWEsIEthc3VuZ3UnfQ0KIyBIZWxwZXIgZnVuY3Rpb24gdG8gY3JlYXRlIG1hcHMgb2Ygb2JzZXJ2ZWQgYW5kIGV4cGVjdGVkIGRyeSBzZWFzb24gbWFsYXJpYSBjYXNlcw0KY3JlYXRlLm1hbGFyaWEubWFwIDwtIGZ1bmN0aW9uKG1hbGFyaWEuZGF0YSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUgPSBOQSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSBOQSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gTkEpew0KICAjIG9ic2VydmVkIGFuZCBleHBlY3RlZCBtYWxhcmlhIGluY2lkZW5jZSBtYXANCiAgIyBtYWxhcmlhLmRhdGE6IGRhdGEgZnJhbWUgY29udGFpbmluZyBvYnNlcnZlZCBhbmQgZXhwZWN0ZWQgbWFsYXJpYSBjYXNlcw0KICAjIHZhcmlhYmxlOiB2YXJpYWJsZSBuYW1lIChhcyBjaGFyYWN0ZXIsIGluIHF1b3RlcyBlLmcuICJvYnNlcnZlZCIpDQogICMgdGl0bGU6IG1hcCB0aXRsZSBpbiBxdW90ZXMNCiAgIyBsZWdlbmQudGl0bGU6IGxlZ2VuZCB0aXRsZSBpbiBxdW90ZXMNCiAgIyByZXR1cm5zOg0KICAjICAgYSB0bWFwLWVsZW1lbnQgKHBsb3RzIGEgbWFwKQ0KICB0bV9zaGFwZShtYWxhcmlhLmRhdGEpKw0KICAgIHRtX2ZpbGwoY29sID0gdmFyaWFibGUsIA0KICAgICAgICAgICAgYnJlYWtzID0gYygwLCA1MDAsIDEwMDAsIDI1MDAsIDUwMDAsIDEwMDAwLCAxNTAwMCwgMjAwMDAsIDI1MDAwKSwNCiAgICAgICAgICAgIHBhbGV0dGUgPSAiWWxPclJkIiwNCiAgICAgICAgICAgIHRpdGxlID0gbGVnZW5kLnRpdGxlKSsNCiAgICB0bV9ib3JkZXJzKGx3ID0gMC4zKSsNCiAgICB0bV9sYXlvdXQobGVnZW5kLnBvc2l0aW9uID0gYygwLjc1LCJib3R0b20iKSwNCiAgICAgICAgICAgICAgbGVnZW5kLnRleHQuc2l6ZSA9IDAuNSwNCiAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlLnNpemUgPSAwLjcsDQogICAgICAgICAgICAgIGZyYW1lID0gRkFMU0UpKw0KICAgIHRtX2NyZWRpdHModGl0bGUsIA0KICAgICAgICAgICAgICAgcG9zaXRpb24gPSBjKDAuMiwgMC44KSwgDQogICAgICAgICAgICAgICBzaXplID0gMSkNCn0NCg0KIyBJbnZva2luZyB0aGUgZnVuY3Rpb24NCiMgMjAxNyBvYnNlcnZlZCBhbmQgZXhwZWN0ZWQgbWFsYXJpYSBjYXNlcyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpvYnNlcnZlZF9tYWxhcmlhXzIwMTdfbWFwIDwtIGNyZWF0ZS5tYWxhcmlhLm1hcChtYWxhcmlhX3BvcF9ieV9jYXRjaG1lbnRfMjAxNywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSA9ICJkcl8yMDE3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIjIwMTciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gIk9ic2VydmVkIG1hbGFyaWEiKQ0KDQpleHBlY3RlZF9tYWxhcmlhXzIwMTdfbWFwIDwtIGNyZWF0ZS5tYWxhcmlhLm1hcChleHBlY3RlZF9tYWxhcmlhXzIwMTcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSA9ICJleHBlY3RlZF8yMDE3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIjIwMTciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gIkV4cGVjdGVkIG1hbGFyaWEiKQ0KDQojIDIwMTggb2JzZXJ2ZWQgYW5kIGV4cGVjdGVkIG1hbGFyaWEgY2FzZXMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0Kb2JzZXJ2ZWRfbWFsYXJpYV8yMDE4X21hcCA8LSBjcmVhdGUubWFsYXJpYS5tYXAobWFsYXJpYV9wb3BfYnlfY2F0Y2htZW50XzIwMTgsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSA9ICJkcl8yMDE4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIjIwMTgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gIk9ic2VydmVkIG1hbGFyaWEiKQ0KDQpleHBlY3RlZF9tYWxhcmlhXzIwMThfbWFwIDwtIGNyZWF0ZS5tYWxhcmlhLm1hcChleHBlY3RlZF9tYWxhcmlhXzIwMTgsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSA9ICJleHBlY3RlZF8yMDE4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIjIwMTgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gIkV4cGVjdGVkIG1hbGFyaWEiKQ0KDQojIDIwMTkgb2JzZXJ2ZWQgYW5kIGV4cGVjdGVkIG1hbGFyaWEgY2FzZXMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0Kb2JzZXJ2ZWRfbWFsYXJpYV8yMDE5X21hcCA8LSBjcmVhdGUubWFsYXJpYS5tYXAobWFsYXJpYV9wb3BfYnlfY2F0Y2htZW50XzIwMTksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSA9ICJkcl8yMDE5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIjIwMTkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gIk9ic2VydmVkIG1hbGFyaWEiKQ0KDQpleHBlY3RlZF9tYWxhcmlhXzIwMTlfbWFwIDwtIGNyZWF0ZS5tYWxhcmlhLm1hcChleHBlY3RlZF9tYWxhcmlhXzIwMTksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSA9ICJleHBlY3RlZF8yMDE5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIjIwMTkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gIkV4cGVjdGVkIG1hbGFyaWEiKQ0KDQojIDIwMjAgb2JzZXJ2ZWQgYW5kIGV4cGVjdGVkIG1hbGFyaWEgY2FzZXMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0Kb2JzZXJ2ZWRfbWFsYXJpYV8yMDIwX21hcCA8LSBjcmVhdGUubWFsYXJpYS5tYXAobWFsYXJpYV9wb3BfYnlfY2F0Y2htZW50XzIwMjAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSA9ICJkcl8yMDIwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIjIwMjAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gIk9ic2VydmVkIG1hbGFyaWEiKQ0KDQpleHBlY3RlZF9tYWxhcmlhXzIwMjBfbWFwIDwtIGNyZWF0ZS5tYWxhcmlhLm1hcChleHBlY3RlZF9tYWxhcmlhXzIwMjAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSA9ICJleHBlY3RlZF8yMDIwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIjIwMjAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gIkV4cGVjdGVkIG1hbGFyaWEiKQ0KDQojIExheW91dCBtYXBzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KdG1hcDo6dG1hcF9hcnJhbmdlKG9ic2VydmVkX21hbGFyaWFfMjAxN19tYXAsIGV4cGVjdGVkX21hbGFyaWFfMjAxN19tYXAsDQogICAgICAgICAgICAgICAgICAgb2JzZXJ2ZWRfbWFsYXJpYV8yMDE4X21hcCwgZXhwZWN0ZWRfbWFsYXJpYV8yMDE4X21hcCwgDQogICAgICAgICAgICAgICAgICAgb2JzZXJ2ZWRfbWFsYXJpYV8yMDE5X21hcCwgZXhwZWN0ZWRfbWFsYXJpYV8yMDE5X21hcCwNCiAgICAgICAgICAgICAgICAgICBvYnNlcnZlZF9tYWxhcmlhXzIwMjBfbWFwLCBleHBlY3RlZF9tYWxhcmlhXzIwMjBfbWFwLCBuY29sID0gMikNCg0KDQoNCmBgYA0KDQojIyBWaWV3IFNNUiBieSBjYXRjaG1lbnQNCg0KQSByYXRpbyBncmVhdGVyIHRoYW4gMS4wIGluZGljYXRlcyB0aGF0IG1vcmUgbWFsYXJpYSBjYXNlcyBoYXZlIG9jY3VycmVkIHRoYW4gd291bGQgaGF2ZSBiZWVuIGV4cGVjdGVkLCB3aGlsZSBhIHJhdGlvIGxlc3MgdGhhbiAxLjAgaW5kaWNhdGVzIHRoYXQgbGVzcyBjYXNlcyBoYXZlIG9jY3VycmVkLiBUaGlzIG1lYW5zIHRoYXQsIGNhdGNobWVudHMgd2l0aCBTTVJzIGFib3ZlIDEgaGF2ZSBoaWdoIGRyeSBzZWFzb24gbWFsYXJpYSByaXNrLg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgZmlnLmhlaWdodCA9IDUsIGZpZy53aWR0aCA9IDcsIGZpZy5jYXAgPSAnRmlnLiA4OiBTdGFuZGFyZGlzZWQgbW9yYmlkaXR5IHJhdGlvIG9mIG1hbGFyaWEgYnkgaGVhbHRoIGZhY2lsaXR5IGNhdGNobWVudCd9DQoNCiMgbWF4KFNNUl8yMDE3JFNNUikNCiMgWzFdIDIuNg0KIyBtYXgoU01SXzIwMTgkU01SKQ0KIyBbMV0gMi45DQojIG1heChTTVJfMjAxOSRTTVIpDQojIFsxXSAyLjENCiMgbWF4KFNNUl8yMDIwJFNNUikNCiMgWzFdIDEuOQ0KDQojIERlZmluZSBmdW5jdGlvbiB0byBjcmVhdGUgbWFwcyBvZiBTTVIgYnkgY2F0Y2htZW50IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KY3JlYXRlLnNtci5tYXAgPC0gZnVuY3Rpb24oc21yLmRhdGEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUgPSAiU01SX2NhdGVnb3J5IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZSA9IE5BLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9ICJTTVIiKXsNCiAgIyBTTVIgYnkgY2F0Y2htZW50IG1hcA0KICAjIHNtci5kYXRhOiBzZiBwb2x5Z29uIG9iamVjdCBjb250YWluaW5nIFNNUiBieSBjYXRjaG1lbnQgZGF0YQ0KICAjIHZhcmlhYmxlOiB2YXJpYWJsZSBuYW1lIChhcyBjaGFyYWN0ZXIsIGluIHFvdXRlcykNCiAgIyB0aXRsZTogbWFwIHRpdGxlIGluIHF1b3Rlcw0KICAjIGxlZ2VuZC50aXRsZTogbGVnZW5kIHRpdGxlIGluIHFvdXRlcw0KICAjIHJldHVybnM6DQogICMgICBhIHRtYXAtZWxlbWVudCAocGxvdHMgYSBtYXApDQogIA0KICAjIGNyZWF0ZSBjYXRlZ29yeSBjb2x1bW4NCiAgc21yLmRhdGEkU01SX2NhdGVnb3J5IDwtIE5BDQogIA0KICAjIGFzc2lnbmluZyBsYWJlbHMgZm9yIHRoZSBTTVIgZXN0aW1hdGUgbGVnZW5kcw0KICBzbXIuY2F0ZWdvcnkubGlzdCA8LSBjKCI8MC41MCIsICIwLjUxIHRvIDAuNzUiLCAiMC43NiB0byAwLjk5IiwgIjEuMDAiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAiMS4xMCB0byAxLjI0IiwgIjEuMjUgdG8gMS40OSIsICI+MS41MCIpDQogIA0KICAjIGFzc2lnbmluZyBjYXRlZ29yaWVzDQogIHNtci5kYXRhJFNNUl9jYXRlZ29yeVtzbXIuZGF0YSRTTVIgPj0gMC4wMCAmIHNtci5kYXRhJFNNUiA8IDAuNDldID0gLTMNCiAgc21yLmRhdGEkU01SX2NhdGVnb3J5W3Ntci5kYXRhJFNNUiA+PSAwLjUwICYgc21yLmRhdGEkU01SIDwgMC43NV0gPSAtMg0KICBzbXIuZGF0YSRTTVJfY2F0ZWdvcnlbc21yLmRhdGEkU01SID49IDAuNzYgJiBzbXIuZGF0YSRTTVIgPCAwLjk5XSA9IC0xDQogIHNtci5kYXRhJFNNUl9jYXRlZ29yeVtzbXIuZGF0YSRTTVIgPj0gMS4wMCAmIHNtci5kYXRhJFNNUiA8IDEuMDldID0gMA0KICBzbXIuZGF0YSRTTVJfY2F0ZWdvcnlbc21yLmRhdGEkU01SID49IDEuMTAgJiBzbXIuZGF0YSRTTVIgPCAxLjI0XSA9IDENCiAgc21yLmRhdGEkU01SX2NhdGVnb3J5W3Ntci5kYXRhJFNNUiA+PSAxLjI1ICYgc21yLmRhdGEkU01SIDwgMS40OV0gPSAyDQogIHNtci5kYXRhJFNNUl9jYXRlZ29yeVtzbXIuZGF0YSRTTVIgPj0gMS41MCAmIHNtci5kYXRhJFNNUiA8IDMuMDBdID0gMw0KICANCiAgIyBnZW5lcmF0aW5nIGRpdmVyZ2VudCBjb2xvdXIgc2NoZW1lcyBbQmx1ZXMgLSBMaWdodCBCbHVlcyDigJMgV2hpdGUg4oCTIExpZ2h0IFJlZHMg4oCTIFJlZHNdDQogICMgc21yLnBhbGV0dGUgPC0gYygiIzMzYTZmZSIsICIjY2JlNmZlIiwgIiNkZmVmZmUiLCAiI2ZlYjFiMSIsICIjZmU4ZThlIiwiI2ZlMDAwMCIpDQogIA0KICB0bV9zaGFwZShzbXIuZGF0YSkrDQogICAgdG1fZmlsbChjb2wgPSB2YXJpYWJsZSwgDQogICAgICAgICAgICBzdHlsZSA9ICJjYXQiLA0KICAgICAgICAgICAgcGFsZXR0ZSA9ICItUmRCdSIsDQogICAgICAgICAgICB0aXRsZSA9IGxlZ2VuZC50aXRsZSwNCiAgICAgICAgICAgIGxhYmVscyA9IHNtci5jYXRlZ29yeS5saXN0KSsNCiAgICB0bV9ib3JkZXJzKGx3ID0gMC42KSsNCiAgICB0bV9sYXlvdXQobGVnZW5kLnBvc2l0aW9uID0gYygwLjc1LCJib3R0b20iKSwNCiAgICAgICAgICAgICAgbGVnZW5kLnRleHQuc2l6ZSA9IDAuNSwNCiAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlLnNpemUgPSAwLjcsDQogICAgICAgICAgICAgIGZyYW1lID0gRkFMU0UpKw0KICAgIHRtX2NyZWRpdHModGl0bGUsIA0KICAgICAgICAgICAgICAgcG9zaXRpb24gPSBjKDAuMiwgMC44KSwgDQogICAgICAgICAgICAgICBzaXplID0gMSkNCn0NCg0KIyBJbnZva2luZyBmdW5jdGlvbiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NClNNUl8yMDE3X21hcCA8LSBjcmVhdGUuc21yLm1hcChTTVJfMjAxNywgdGl0bGUgPSAiMjAxNyIpDQoNClNNUl8yMDE4X21hcCA8LSBjcmVhdGUuc21yLm1hcChTTVJfMjAxOCwgdGl0bGUgPSAiMjAxOCIpDQoNClNNUl8yMDE5X21hcCA8LSBjcmVhdGUuc21yLm1hcChTTVJfMjAxOSwgdGl0bGUgPSAiMjAxOSIpDQoNClNNUl8yMDIwX21hcCA8LSBjcmVhdGUuc21yLm1hcChTTVJfMjAyMCwgdGl0bGUgPSAiMjAyMCIpDQoNCiMgTGF5b3V0IG1hcHMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQp0bWFwOjp0bWFwX2FycmFuZ2UoU01SXzIwMTdfbWFwLCBTTVJfMjAxOF9tYXAsIFNNUl8yMDE5X21hcCwgU01SXzIwMjBfbWFwLCBuY29sID0gMikNCg0KYGBgDQoNCiMgQ2FsY3VsYXRlIHRoZSBwcm9wb3J0aW9uIG9mIHRoZSBjYXRjaG1lbnQgcG9wdWxhdGlvbiBsaXZpbmcgd2l0aGluIDFrbSwgMmttLCAza20gb2Ygd2F0ZXIgYm9kaWVzDQoNCkZpcnN0LCB1c2luZyBgc3RfYnVmZmVyYCwgd2UgY29tcHV0ZSAxa20sIDJrbSBhbmQgM2ttIGJ1ZmZlcnMgYXJvdW5kIGRyeSBzZWFzb24gd2F0ZXIgYm9kaWVzIG9idGFpbmVkIGZyb20gTGFuZFNhdCBzYXRlbGxpdGUgaW1hZ2VyeSB1c2luZyBgVHJvcFdldGAgdG9vbCBpbiBHb29nbGUgRWFydGggRW5naW5lLiBUaGVuLCBnZW9tZXRyeSBvZiB0aGUgYnVmZmVyIGZlYXR1cmVzIGFyZSB0aGVuIGNvbWJpbmVkIHJlc3VsdGluZyBpbiByZXNvbHZlZCBpbnRlcm5hbCBib3VuZGFyaWVzIHRvIGVuYWJsZSBleHRyYWN0aW5nIHBvcHVsYXRpb24gdmFsdWVzIGluIGJ1ZmZlcnMgZnJvbSB0aGUgV29ybGRQb3AgcmFzdGVyLiBGaW5hbGx5LCB3ZSBjYWxjdWxhdGUgdGhlIHByb3BvcnRpb24gb2YgcGVvcGxlIGluIGVhY2ggY2F0Y2htZW50IGFyZWEgbGl2aW5nIHdpdGhpbiB3YXRlciBib2RpZXMuDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBDb21iaW5lIGFuZCB0cmFuc2Zvcm0gVHJvcFdldCBkZXJpdmVkIHdhdGVyYm9keSBwb2x5Z29ucyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCnN1cmZhY2Vfd2F0ZXJib2RpZXNfMjAxNyA8LSBzZjo6c3RfYnVmZmVyKGRyeXNlYXNvbl93YXRlcmJvZGllc18yMDE3LCBkaXN0ID0gMzApIHw+DQogIHNmOjpzdF91bmlvbigpIHw+DQogIHNmOjpzdF9jYXN0KCJQT0xZR09OIikgfD4NCiAgc2Y6OnN0X2FzX3NmKCkNCg0Kc3VyZmFjZV93YXRlcmJvZGllc18yMDE4IDwtIHNmOjpzdF9idWZmZXIoZHJ5c2Vhc29uX3dhdGVyYm9kaWVzXzIwMTgsIGRpc3QgPSAzMCkgfD4NCiAgc2Y6OnN0X3VuaW9uKCkgfD4NCiAgc2Y6OnN0X2Nhc3QoIlBPTFlHT04iKSB8Pg0KICBzZjo6c3RfYXNfc2YoKQ0KDQpzdXJmYWNlX3dhdGVyYm9kaWVzXzIwMTkgPC0gc2Y6OnN0X2J1ZmZlcihkcnlzZWFzb25fd2F0ZXJib2RpZXNfMjAxOSwgZGlzdCA9IDMwKSB8Pg0KICBzZjo6c3RfdW5pb24oKSB8Pg0KICBzZjo6c3RfY2FzdCgiUE9MWUdPTiIpIHw+DQogIHNmOjpzdF9hc19zZigpDQoNCnN1cmZhY2Vfd2F0ZXJib2RpZXNfMjAyMCA8LSBzZjo6c3RfYnVmZmVyKGRyeXNlYXNvbl93YXRlcmJvZGllc18yMDIwLCBkaXN0ID0gMzApIHw+DQogIHNmOjpzdF91bmlvbigpIHw+DQogIHNmOjpzdF9jYXN0KCJQT0xZR09OIikgfD4NCiAgc2Y6OnN0X2FzX3NmKCkNCg0KDQojIEhlbHBlciBmdW5jdGlvbiB0byBjb21wdXRlIDFrbSwgMmttIGFuZCAza20gYnVmZmVycyBhcm91bmQgdGhlIHdhdGVyIGJvZGllcyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KY3JlYXRlLndhdGVyYm9keS5idWZmZXIgPC0gZnVuY3Rpb24od2F0ZXJib2R5LCBkaXN0YW5jZSwgY2F0Y2htZW50KXsNCiAgIyBmdW5jdGlvbiBmb3IgY3JlYXRpbmcgYnVmZmVycyBhcm91bmQgd2F0ZXJib2RpZXMNCiAgIyBhcmd1bWVudHM6DQogICMgICB3YXRlcmJvZHk6ICB3YXRlcmJvZHkgc2hhcGVmaWxlDQogICMgICBkaXN0YW5jZTogYnVmZmVyIGRpc3RhbmNlIGluIG1ldGVycw0KICAjICAgY2F0Y2htZW50OiBjYXRjaG1lbnQgYXJlYSBzaGFwZWZpbGUNCiAgIyByZXR1cm5zOg0KICAjICAgYnVmZmVyZWQgd2F0ZXJib2RpZXMgDQogIA0KICAjIENyZWF0ZSBidWZmZXJzIGFyb3VuZCB3YXRlciBib2RpZXMNCiAgYnVmZmVyX3JhZGl1cyA8LSBzZjo6c3RfYnVmZmVyKHdhdGVyYm9keSwgZGlzdGFuY2UpDQogIA0KICAjIERpc3NvbHZlIHRoZSBidWZmZXJzDQogICMgYnVmZmVyX3VuaW9uIDwtIHNmOjpzdF9hc19zZihzdF9jYXN0KHN0X3VuaW9uKGJ1ZmZlcl9yYWRpdXMpLCJNVUxUSVBPTFlHT04iKSkNCiAgYnVmZmVyX3VuaW9uIDwtIHNmOjpzdF91bmlvbihidWZmZXJfcmFkaXVzKSB8Pg0KICAgIHNmOjpzdF9jYXN0KCJNVUxUSVBPTFlHT04iKSB8Pg0KICAgIHNmOjpzdF9hc19zZigpDQogIA0KICAjIEFzc2lnbiBhdHRyaWJ1dGVzIG9mIHRoZSAnY2F0Y2htZW50JyB0byBlYWNoIG9mIHRoZSB3YXRlciBib2RpZXMuIA0KICAgYnVmZmVyX2ludGVyc2VjdCA8LSBzZjo6c3RfaW50ZXJzZWN0aW9uKGJ1ZmZlcl91bmlvbiwgY2F0Y2htZW50KQ0KICANCiAgIGJ1ZmZlcl9pbnRlcnNlY3Rfc2YgPC0gc2Y6OnN0X2FzX3NmKGJ1ZmZlcl9pbnRlcnNlY3QpDQogICANCiAgIyBDb252ZXJ0IHRoZSBNVUxUSVBPTFlHT04gb2JqZWN0IGludG8gc2V2ZXJhbCBQT0xZR09OIG9iamVjdHMNCiAgIGJ1ZmZlcl9pbnRlcnNlY3RfcG9seWdvbnMgPC0gc2Y6OnN0X2J1ZmZlcihidWZmZXJfaW50ZXJzZWN0X3NmLCAwLjApIHw+DQogICAgIHNmOjpzdF9jYXN0KCJNVUxUSVBPTFlHT04iKSB8Pg0KICAgICBzZjo6c3RfY2FzdCgiUE9MWUdPTiIpDQogICANCiAgDQogICMgUG9seWdvbnMgYmVpbmcgc2VlbiB0byBiZSBpbiBtdWx0aXBsZSBjYXRjaG1lbnRzDQogICBzZjo6c3RfaW50ZXJzZWN0cyhidWZmZXJfaW50ZXJzZWN0X3BvbHlnb25zLCBjYXRjaG1lbnQpDQogIA0KICAjIE1ha2UgdGhlIGFzc3VtcHRpb24gdGhhdCB0aGUgYXR0cmlidXRlIGlzIGNvbnN0YW50IHRocm91Z2hvdXQgdGhlIGdlb21ldHJ5DQogICBzZjo6c3RfYWdyKGJ1ZmZlcl9pbnRlcnNlY3RfcG9seWdvbnMpID0gImNvbnN0YW50Ig0KICAgDQogICBzZjo6c3RfYWdyKGNhdGNobWVudCkgPSAiY29uc3RhbnQiDQogIA0KICByZXR1cm4ob3V0ID0gYnVmZmVyX2ludGVyc2VjdF9wb2x5Z29ucykNCn0NCg0KDQojIEludm9raW5nIGZ1bmN0aW9uDQojIEZvciAyMDE3IFRyb3BXZXQgc3VyZmFjZSB3YXRlciBwb2x5Z29ucyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KYnVmZmVyXzFrbV8yMDE3IDwtIGNyZWF0ZS53YXRlcmJvZHkuYnVmZmVyKHdhdGVyYm9keSA9IHN1cmZhY2Vfd2F0ZXJib2RpZXNfMjAxNywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzdGFuY2UgPSAxMDAwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRjaG1lbnQgPSBtYWxpcmVfbmV3KQ0KDQpidWZmZXJfMmttXzIwMTcgPC0gY3JlYXRlLndhdGVyYm9keS5idWZmZXIod2F0ZXJib2R5ID0gc3VyZmFjZV93YXRlcmJvZGllc18yMDE3LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXN0YW5jZSA9IDIwMDAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGNobWVudCA9IG1hbGlyZV9uZXcpDQoNCmJ1ZmZlcl8za21fMjAxNyA8LSBjcmVhdGUud2F0ZXJib2R5LmJ1ZmZlcih3YXRlcmJvZHkgPSBzdXJmYWNlX3dhdGVyYm9kaWVzXzIwMTcsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3RhbmNlID0gMzAwMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRjaG1lbnQgPSBtYWxpcmVfbmV3KQ0KDQojIEZvciAyMDE4IFRyb3BXZXQgc3VyZmFjZSB3YXRlciBwb2x5Z29ucyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KYnVmZmVyXzFrbV8yMDE4IDwtIGNyZWF0ZS53YXRlcmJvZHkuYnVmZmVyKHdhdGVyYm9keSA9IHN1cmZhY2Vfd2F0ZXJib2RpZXNfMjAxOCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzdGFuY2UgPSAxMDAwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRjaG1lbnQgPSBtYWxpcmVfbmV3KQ0KDQpidWZmZXJfMmttXzIwMTggPC0gY3JlYXRlLndhdGVyYm9keS5idWZmZXIod2F0ZXJib2R5ID0gc3VyZmFjZV93YXRlcmJvZGllc18yMDE4LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXN0YW5jZSA9IDIwMDAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGNobWVudCA9IG1hbGlyZV9uZXcpDQoNCmJ1ZmZlcl8za21fMjAxOCA8LSBjcmVhdGUud2F0ZXJib2R5LmJ1ZmZlcih3YXRlcmJvZHkgPSBzdXJmYWNlX3dhdGVyYm9kaWVzXzIwMTgsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3RhbmNlID0gMzAwMCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0Y2htZW50ID0gbWFsaXJlX25ldykNCiANCiMgRm9yIDIwMTkgVHJvcFdldCBzdXJmYWNlIHdhdGVyIHBvbHlnb25zIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KYnVmZmVyXzFrbV8yMDE5IDwtIGNyZWF0ZS53YXRlcmJvZHkuYnVmZmVyKHdhdGVyYm9keSA9IHN1cmZhY2Vfd2F0ZXJib2RpZXNfMjAxOSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzdGFuY2UgPSAxMDAwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRjaG1lbnQgPSBtYWxpcmVfbmV3KQ0KDQpidWZmZXJfMmttXzIwMTkgPC0gY3JlYXRlLndhdGVyYm9keS5idWZmZXIod2F0ZXJib2R5ID0gc3VyZmFjZV93YXRlcmJvZGllc18yMDE5LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXN0YW5jZSA9IDIwMDAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGNobWVudCA9IG1hbGlyZV9uZXcpDQoNCmJ1ZmZlcl8za21fMjAxOSA8LSBjcmVhdGUud2F0ZXJib2R5LmJ1ZmZlcih3YXRlcmJvZHkgPSBzdXJmYWNlX3dhdGVyYm9kaWVzXzIwMTksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3RhbmNlID0gMzAwMCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0Y2htZW50ID0gbWFsaXJlX25ldykNCg0KIyBGb3IgMjAyMCBUcm9wV2V0IHN1cmZhY2Ugd2F0ZXIgcG9seWdvbnMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpidWZmZXJfMWttXzIwMjAgPC0gY3JlYXRlLndhdGVyYm9keS5idWZmZXIod2F0ZXJib2R5ID0gc3VyZmFjZV93YXRlcmJvZGllc18yMDIwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXN0YW5jZSA9IDEwMDAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGNobWVudCA9IG1hbGlyZV9uZXcpDQoNCmJ1ZmZlcl8ya21fMjAyMCA8LSBjcmVhdGUud2F0ZXJib2R5LmJ1ZmZlcih3YXRlcmJvZHkgPSBzdXJmYWNlX3dhdGVyYm9kaWVzXzIwMjAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3RhbmNlID0gMjAwMCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0Y2htZW50ID0gbWFsaXJlX25ldykNCg0KYnVmZmVyXzNrbV8yMDIwIDwtIGNyZWF0ZS53YXRlcmJvZHkuYnVmZmVyKHdhdGVyYm9keSA9IHN1cmZhY2Vfd2F0ZXJib2RpZXNfMjAyMCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzdGFuY2UgPSAzMDAwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRjaG1lbnQgPSBtYWxpcmVfbmV3KQ0KDQpgYGANCg0KIyMgVmlldyB0aGUgY3JlYXRlZCB3YXRlcmJvZHkgYnVmZmVycw0KDQpOb3RlIHRoYXQgYmxhbmsgYXJlYXMgaW4gdGhlIG1hcCByZXByZXNlbnQgY2F0Y2htZW50IGFyZWFzIGluIHdoaWNoIG5vIHdhdGVyIGJvZHkgd2FzIGRldGVjdGVkIOKAlCB0aGlzIG1heSBiZSBhIGxpbWl0YXRpb24gb2YgdXNpbmcgbW9kZXJhdGUgcmVzb2x1dGlvbiBzYXRlbGxpdGUgaW1hZ2VyeSAoPjMwbSBzcGF0aWFsIHJlc29sdXRpb24pIHRvIGlkZW50aWZ5IHN1cmZhY2Ugd2F0ZXIuDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTksIGZpZy53aWR0aD0xMCwgZmlnLmNhcD0nRmlnIDkuIEJ1ZmZlcnMgYXJvdW5kIGRyeSBzZWFzb24gd2F0ZXJib2RpZXMgaW4gS2FzdW5ndSd9DQoNCiMgTWFwIHRoZSBidWZmZXJzDQpjcmVhdGUuYnVmZmVyLm1hcCA8LSBmdW5jdGlvbihidWZmZXJzLCBib3VuZGFyeSA9IG1hbGlyZV9uZXcsIHRpdGxlID0gTkEpew0KICAjIGZ1bmN0aW9uIGZvciBjcmVhdGluZyBidWZmZXIgbWFwIGluIGdncGxvdA0KICAjIGFyZ3VtZW50czoNCiAgIyAgIGJ1ZmZlcjogIHdhdGVyYm9kaWVzIGJ1ZmZlciBwb2x5Z29uIGxheWVyDQogICMgICBib3VuZGFyeTogaGVhbHRoIGZhY2lsaXR5IGNhdGNobWVudCBwb2x5Z29ucw0KICAjICAgdGl0bGU6IG1haW4gdGl0bGUNCiAgIyByZXR1cm5zOg0KICAjICAgYSBtYXAtZWxlbWVudCAocGxvdHMgYSBtYXApDQogIGdncGxvdChkYXRhID0gYnVmZmVycykrDQogICAgIGdlb21fc2YoKSsNCiAgICAgZ2VvbV9zZihkYXRhID0gYm91bmRhcnksIA0KICAgICAgICAgICAgIGZpbGwgPSBOQSkrDQogICAgIHRoZW1lX3ZvaWQoKSsNCiAgICAgbGFicyh0aXRsZSA9IHRpdGxlKQ0KfQ0KDQojIEludm9raW5nIHRoZSBmdW5jdGlvbg0KIyBGb3IgMjAxNyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpidWZmZXJfMWttXzIwMTdfbWFwIDwtIGNyZWF0ZS5idWZmZXIubWFwKGJ1ZmZlcl8xa21fMjAxNywgdGl0bGUgPSAiMjAxNzogMWttIEJ1ZmZlcnMiKQ0KDQpidWZmZXJfMmttXzIwMTdfbWFwIDwtIGNyZWF0ZS5idWZmZXIubWFwKGJ1ZmZlcl8ya21fMjAxNywgdGl0bGUgPSAiMjAxNzogMmttIEJ1ZmZlcnMiKQ0KDQpidWZmZXJfM2ttXzIwMTdfbWFwIDwtIGNyZWF0ZS5idWZmZXIubWFwKGJ1ZmZlcl8za21fMjAxNywgdGl0bGUgPSAiMjAxNzogM2ttIEJ1ZmZlcnMiKQ0KDQojIEZvciAyMDE4IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpidWZmZXJfMWttXzIwMThfbWFwIDwtIGNyZWF0ZS5idWZmZXIubWFwKGJ1ZmZlcl8xa21fMjAxOCwgdGl0bGUgPSAiMjAxODogMWttIEJ1ZmZlcnMiKQ0KDQpidWZmZXJfMmttXzIwMThfbWFwIDwtIGNyZWF0ZS5idWZmZXIubWFwKGJ1ZmZlcl8ya21fMjAxOCwgdGl0bGUgPSAiMjAxODogMmttIEJ1ZmZlcnMiKQ0KDQpidWZmZXJfM2ttXzIwMThfbWFwIDwtIGNyZWF0ZS5idWZmZXIubWFwKGJ1ZmZlcl8za21fMjAxOCwgdGl0bGUgPSAiMjAxODogM2ttIEJ1ZmZlcnMiKQ0KDQojIEZvciAyMDE5IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KYnVmZmVyXzFrbV8yMDE5X21hcCA8LSBjcmVhdGUuYnVmZmVyLm1hcChidWZmZXJfMWttXzIwMTksIHRpdGxlID0gIjIwMTk6IDFrbSBCdWZmZXJzIikNCg0KYnVmZmVyXzJrbV8yMDE5X21hcCA8LSBjcmVhdGUuYnVmZmVyLm1hcChidWZmZXJfMmttXzIwMTksIHRpdGxlID0gIjIwMTk6IDJrbSBCdWZmZXJzIikNCg0KYnVmZmVyXzNrbV8yMDE5X21hcCA8LSBjcmVhdGUuYnVmZmVyLm1hcChidWZmZXJfM2ttXzIwMTksIHRpdGxlID0gIjIwMTk6IDNrbSBCdWZmZXJzIikNCg0KIyBGb3IgMjAyMCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KYnVmZmVyXzFrbV8yMDIwX21hcCA8LSBjcmVhdGUuYnVmZmVyLm1hcChidWZmZXJfMWttXzIwMjAsIHRpdGxlID0gIjIwMjA6IDFrbSBCdWZmZXJzIikNCg0KYnVmZmVyXzJrbV8yMDIwX21hcCA8LSBjcmVhdGUuYnVmZmVyLm1hcChidWZmZXJfMmttXzIwMjAsIHRpdGxlID0gIjIwMjA6IDJrbSBCdWZmZXJzIikNCg0KYnVmZmVyXzNrbV8yMDIwX21hcCA8LSBjcmVhdGUuYnVmZmVyLm1hcChidWZmZXJfM2ttXzIwMjAsIHRpdGxlID0gIjIwMjA6IDNrbSBCdWZmZXJzIikNCiANCmdyaWQuYXJyYW5nZShidWZmZXJfMWttXzIwMTdfbWFwLCBidWZmZXJfMWttXzIwMThfbWFwLCBidWZmZXJfMWttXzIwMTlfbWFwLCBidWZmZXJfMWttXzIwMjBfbWFwLA0KICAgICAgICAgICAgIGJ1ZmZlcl8ya21fMjAxN19tYXAsIGJ1ZmZlcl8ya21fMjAxOF9tYXAsIGJ1ZmZlcl8ya21fMjAxOV9tYXAsIGJ1ZmZlcl8ya21fMjAyMF9tYXAsIA0KICAgICAgICAgICAgIGJ1ZmZlcl8za21fMjAxN19tYXAsIGJ1ZmZlcl8za21fMjAxOF9tYXAsIGJ1ZmZlcl8za21fMjAxOV9tYXAsIGJ1ZmZlcl8za21fMjAyMF9tYXAsIG5jb2wgPSA0KQ0KDQoNCmBgYA0KDQojIEV4dHJhY3QgdGhlIHBvcHVsYXRpb24gbGl2aW5nIHdpdGhpbiB3YXRlcmJvZHkgYnVmZmVycyBieSBjYXRjaG1lbnQgYXJlYQ0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCg0KIyBIZWxwZXIgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIGVzdGltYXRlZCBudW1iZXIgb2YgcGVvcGxlIGxpdmluZyB3aXRoaW4gd2F0ZXJib2R5IGJ1ZmZlcnMNCiMgaW4gZWFjaCBjYXRjaG1lbnQgYXJlYQ0KZXN0aW1hdGUuYnVmZmVyLnBvcCA8LSBmdW5jdGlvbihjYXRjaG1lbnQucG9wdWxhdGlvbiwgYnVmZmVycywgY2F0Y2htZW50LmFyZWEpew0KICANCiAgIyBFeHRyYWN0IHBvcHVsYXRpb24gZXN0aW1hdGVzIGZyb20gV29ybGRQb3AgcmFzdGVyDQogIGJ1ZmZlcnMkYnVmZmVyX3BvcCA8LSByYXN0ZXI6OmV4dHJhY3QoY2F0Y2htZW50LnBvcHVsYXRpb24sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnVmZmVycywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuID0gc3VtLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAjIEZpbmQgd2hpY2ggY2F0Y2htZW50IGVhY2ggcG9seWdvbiBiZWxvbmdzIHRvIHVzaW5nIGl0cyBjZW50cm9pZCAtIGEgcG9pbnQgZGF0YXNldCANCiAgIyByZXByZXNlbnRpbmcgdGhlIGdlb2dyYXBoaWMgY2VudGVyLXBvaW50cyBvZiB0aGUgcG9seWdvbnMgDQogICMgYnVmZmVyX2J5X2NhdGNobWVudCA8LSBzdF9pbnRlcnNlY3Rpb24oc3RfY2VudHJvaWQoYnVmZmVycyksIGNhdGNobWVudC5hcmVhKQ0KICBidWZmZXJfYnlfY2F0Y2htZW50IDwtIHNmOjpzdF9jZW50cm9pZChidWZmZXJzKSB8Pg0KICAgIHNmOjpzdF9pbnRlcnNlY3Rpb24oY2F0Y2htZW50LmFyZWEpDQogIA0KICAjIE5vdGljZSB0aGF0IHRoZSBidWZmZXJfY2F0Y2htZW50IGlzIGNvbXByaXNlZCBvZiBzZXBhcmF0ZSBQT0xZR09OUyAoYnVmZmVyX2J5X2NhdGNobWVudCR4KS4gDQogICMgVGhlIGZpcnN0IHN0ZXAgaXMgdG8g4oCcZGlzc29sdmXigJ0gYXdheSB0aGVzZSBQT0xZR09OUyBpbnRvIG9uZSBNVUxUSVBPTFlHT04uIA0KICAjIFRoZXJlIGlzIG5vIHNmIGVxdWl2YWxlbnQgdG8gdGhlIFFHSVMgb3IgQXJjTWFwIOKAnGRpc3NvbHZl4oCdIG9wZXJhdGlvbi4gDQogICMgSW5zdGVhZCB3ZSB1c2UgYSBjb21iaW5hdGlvbiBvZiBncm91cF9ieSBhbmQgc3VtbWFyaXplIGZyb20gdGhlIGRwbHlyIHBhY2thZ2UuIA0KICAjIFN0YXRzOjphZ2dyZWdhdGUgZnJvbSBzZiBwYWNrYWdlLCBhbmQgZHBseXI6OnN1bW1hcml6ZSBib3RoIGRvIGVzc2VudGlhbGx5IHRoZSBzYW1lLg0KICAgYnVmZmVyX3BvcF9hZ2dyZWdhdGVkIDwtIGJ1ZmZlcl9ieV9jYXRjaG1lbnQgfD4gDQogICAgIGRwbHlyOjpncm91cF9ieShETikgfD4NCiAgICAgZHBseXI6OnN1bW1hcml6ZShidWZmZXJfcG9wX2FnZ3JlZ2F0ZWQgPSByb3VuZChzdW0oYnVmZmVyX3BvcCwgbmEucm0gPSBUUlVFKSkpDQoNCiAgYnVmZmVyX3BvcCA8LSBtZXJnZShjYXRjaG1lbnQuYXJlYSwgc3RfZHJvcF9nZW9tZXRyeShidWZmZXJfcG9wX2FnZ3JlZ2F0ZWQpLA0KICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gJ0ROJywgYWxsLnggPSBUUlVFKQ0KICANCiAgDQogIA0KICByZXR1cm4ob3V0ID0gYnVmZmVyX3BvcCkNCiAgDQp9DQoNCiMgSW52b2tpbmcgdGhlIGZ1bmN0aW9uIGFuZCBjYWxjdWxhdGluZyBwcm9wb3J0aW9uIG9mIA0KIyBjYXRjaG1lbnQgcG9wdWxhdGlvbiBsaXZpbmcgd2l0aGluIGJ1ZmZlcnMNCiMgMjAxNyBidWZmZXIgcG9wdWxhdGlvbiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpidWZmZXJfcG9wXzFrbV8yMDE3IDwtIGVzdGltYXRlLmJ1ZmZlci5wb3AoDQogIGthc3VuZ3VfcG9wdWxhdGlvbl8yMDE3LCANCiAgYnVmZmVyXzFrbV8yMDE3LCANCiAgIG1hbGFyaWFfcG9wX2J5X2NhdGNobWVudF8yMDE3KSB8PiANCiAgZHBseXI6OnJlbmFtZShjYXRjaG1lbnRfcG9wID0gcG9wLA0KICAgICAgICAgICAgICAgIGJ1ZmZlcl9wb3AgPSBidWZmZXJfcG9wX2FnZ3JlZ2F0ZWQpIHw+DQogIGRwbHlyOjptdXRhdGUoDQogICAgcHJvcF9idWZmZXJfY2F0Y2htZW50X3BvcCA9IHJvdW5kKChidWZmZXJfcG9wL2NhdGNobWVudF9wb3ApKjEwMCkpfD4gDQogIGRwbHlyOjptdXRhdGUoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgLmZucyA9IH5yZXBsYWNlX25hKC4sMCkpKSANCg0KYnVmZmVyX3BvcF8ya21fMjAxNyA8LSBlc3RpbWF0ZS5idWZmZXIucG9wKA0KICBrYXN1bmd1X3BvcHVsYXRpb25fMjAxNywNCiAgYnVmZmVyXzJrbV8yMDE3LA0KICBtYWxhcmlhX3BvcF9ieV9jYXRjaG1lbnRfMjAxNykgfD4NCiAgZHBseXI6OnJlbmFtZShjYXRjaG1lbnRfcG9wID0gcG9wLA0KICAgICAgICAgICAgICAgIGJ1ZmZlcl9wb3AgPSBidWZmZXJfcG9wX2FnZ3JlZ2F0ZWQpIHw+DQogIGRwbHlyOjptdXRhdGUoDQogICAgcHJvcF9idWZmZXJfY2F0Y2htZW50X3BvcCA9IHJvdW5kKChidWZmZXJfcG9wL2NhdGNobWVudF9wb3ApKjEwMCkpIHw+IA0KICBkcGx5cjo6bXV0YXRlKGFjcm9zcyhldmVyeXRoaW5nKCksIC5mbnMgPSB+cmVwbGFjZV9uYSguLDApKSkNCg0KYnVmZmVyX3BvcF8za21fMjAxNyA8LSBlc3RpbWF0ZS5idWZmZXIucG9wKA0KICBrYXN1bmd1X3BvcHVsYXRpb25fMjAxNywNCiAgYnVmZmVyXzNrbV8yMDE3LA0KICBtYWxhcmlhX3BvcF9ieV9jYXRjaG1lbnRfMjAxNykgfD4gDQogIGRwbHlyOjpyZW5hbWUoY2F0Y2htZW50X3BvcCA9IHBvcCwNCiAgICAgICAgICAgICAgICBidWZmZXJfcG9wID0gYnVmZmVyX3BvcF9hZ2dyZWdhdGVkKSB8Pg0KICBkcGx5cjo6bXV0YXRlKA0KICAgIHByb3BfYnVmZmVyX2NhdGNobWVudF9wb3AgPSByb3VuZCgoYnVmZmVyX3BvcC9jYXRjaG1lbnRfcG9wKSoxMDApKXw+IA0KICBkcGx5cjo6bXV0YXRlKGFjcm9zcyhldmVyeXRoaW5nKCksIC5mbnMgPSB+cmVwbGFjZV9uYSguLDApKSkNCg0KIyAyMDE4IGJ1ZmZlciBwb3B1bGF0aW9uIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmJ1ZmZlcl9wb3BfMWttXzIwMTggPC0gZXN0aW1hdGUuYnVmZmVyLnBvcCgNCiAga2FzdW5ndV9wb3B1bGF0aW9uXzIwMTgsDQogIGJ1ZmZlcl8xa21fMjAxOCwNCiAgbWFsYXJpYV9wb3BfYnlfY2F0Y2htZW50XzIwMTgpIHw+IA0KICBkcGx5cjo6cmVuYW1lKGNhdGNobWVudF9wb3AgPSBwb3AsDQogICAgICAgICAgICAgICAgYnVmZmVyX3BvcCA9IGJ1ZmZlcl9wb3BfYWdncmVnYXRlZCkgfD4gDQogIGRwbHlyOjptdXRhdGUoDQogICAgcHJvcF9idWZmZXJfY2F0Y2htZW50X3BvcCA9IHJvdW5kKChidWZmZXJfcG9wL2NhdGNobWVudF9wb3ApKjEwMCkpfD4gDQogIGRwbHlyOjptdXRhdGUoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgLmZucyA9IH5yZXBsYWNlX25hKC4sMCkpKQ0KDQpidWZmZXJfcG9wXzJrbV8yMDE4IDwtIGVzdGltYXRlLmJ1ZmZlci5wb3AoDQogIGthc3VuZ3VfcG9wdWxhdGlvbl8yMDE4LA0KICBidWZmZXJfMmttXzIwMTgsDQogIG1hbGFyaWFfcG9wX2J5X2NhdGNobWVudF8yMDE4KSB8PiANCiAgZHBseXI6OnJlbmFtZShjYXRjaG1lbnRfcG9wID0gcG9wLA0KICAgICAgICAgICAgICAgIGJ1ZmZlcl9wb3AgPSBidWZmZXJfcG9wX2FnZ3JlZ2F0ZWQpIHw+DQogIGRwbHlyOjptdXRhdGUoDQogICAgcHJvcF9idWZmZXJfY2F0Y2htZW50X3BvcCA9IHJvdW5kKChidWZmZXJfcG9wL2NhdGNobWVudF9wb3ApKjEwMCkpfD4gDQogIGRwbHlyOjptdXRhdGUoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgLmZucyA9IH5yZXBsYWNlX25hKC4sMCkpKQ0KDQpidWZmZXJfcG9wXzNrbV8yMDE4IDwtIGVzdGltYXRlLmJ1ZmZlci5wb3AoDQogIGthc3VuZ3VfcG9wdWxhdGlvbl8yMDE4LA0KICBidWZmZXJfM2ttXzIwMTgsDQogIG1hbGFyaWFfcG9wX2J5X2NhdGNobWVudF8yMDE4KSB8PiANCiAgZHBseXI6OnJlbmFtZShjYXRjaG1lbnRfcG9wID0gcG9wLA0KICAgICAgICAgICAgICAgIGJ1ZmZlcl9wb3AgPSBidWZmZXJfcG9wX2FnZ3JlZ2F0ZWQpIHw+IA0KICBkcGx5cjo6bXV0YXRlKA0KICAgIHByb3BfYnVmZmVyX2NhdGNobWVudF9wb3AgPSByb3VuZCgoYnVmZmVyX3BvcC9jYXRjaG1lbnRfcG9wKSoxMDApKXw+IA0KICBkcGx5cjo6bXV0YXRlKGFjcm9zcyhldmVyeXRoaW5nKCksIC5mbnMgPSB+cmVwbGFjZV9uYSguLDApKSkNCg0KIyAyMDE5IGJ1ZmZlciBwb3B1bGF0aW9uIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmJ1ZmZlcl9wb3BfMWttXzIwMTkgPC0gZXN0aW1hdGUuYnVmZmVyLnBvcCgNCiAga2FzdW5ndV9wb3B1bGF0aW9uXzIwMTksDQogIGJ1ZmZlcl8xa21fMjAxOSwNCiAgbWFsYXJpYV9wb3BfYnlfY2F0Y2htZW50XzIwMTkpIHw+IA0KICBkcGx5cjo6cmVuYW1lKGNhdGNobWVudF9wb3AgPSBwb3AsDQogICAgICAgICAgICAgICAgYnVmZmVyX3BvcCA9IGJ1ZmZlcl9wb3BfYWdncmVnYXRlZCkgfD4gDQogIGRwbHlyOjptdXRhdGUoDQogICAgcHJvcF9idWZmZXJfY2F0Y2htZW50X3BvcCA9IHJvdW5kKChidWZmZXJfcG9wL2NhdGNobWVudF9wb3ApKjEwMCkpfD4gDQogIGRwbHlyOjptdXRhdGUoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgLmZucyA9IH5yZXBsYWNlX25hKC4sMCkpKQ0KDQpidWZmZXJfcG9wXzJrbV8yMDE5IDwtIGVzdGltYXRlLmJ1ZmZlci5wb3AoDQogIGthc3VuZ3VfcG9wdWxhdGlvbl8yMDE5LA0KICBidWZmZXJfMmttXzIwMTksDQogIG1hbGFyaWFfcG9wX2J5X2NhdGNobWVudF8yMDE5KSB8Pg0KICBkcGx5cjo6cmVuYW1lKGNhdGNobWVudF9wb3AgPSBwb3AsDQogICAgICAgICAgICAgICAgYnVmZmVyX3BvcCA9IGJ1ZmZlcl9wb3BfYWdncmVnYXRlZCkgfD4gDQogIGRwbHlyOjptdXRhdGUoDQogICAgcHJvcF9idWZmZXJfY2F0Y2htZW50X3BvcCA9IHJvdW5kKChidWZmZXJfcG9wL2NhdGNobWVudF9wb3ApKjEwMCkpfD4gDQogIGRwbHlyOjptdXRhdGUoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgLmZucyA9IH5yZXBsYWNlX25hKC4sMCkpKSAjIHJlcGxhY2UgTkEgd2l0aCB6ZXJvDQoNCmJ1ZmZlcl9wb3BfM2ttXzIwMTkgPC0gZXN0aW1hdGUuYnVmZmVyLnBvcCgNCiAga2FzdW5ndV9wb3B1bGF0aW9uXzIwMTksDQogIGJ1ZmZlcl8za21fMjAxOSwNCiAgbWFsYXJpYV9wb3BfYnlfY2F0Y2htZW50XzIwMTkpIHw+IA0KICBkcGx5cjo6cmVuYW1lKGNhdGNobWVudF9wb3AgPSBwb3AsDQogICAgICAgICAgICAgICAgYnVmZmVyX3BvcCA9IGJ1ZmZlcl9wb3BfYWdncmVnYXRlZCkgfD4NCiAgZHBseXI6Om11dGF0ZSgNCiAgICBwcm9wX2J1ZmZlcl9jYXRjaG1lbnRfcG9wID0gcm91bmQoKGJ1ZmZlcl9wb3AvY2F0Y2htZW50X3BvcCkqMTAwKSl8PiANCiAgZHBseXI6Om11dGF0ZShhY3Jvc3MoZXZlcnl0aGluZygpLCAuZm5zID0gfnJlcGxhY2VfbmEoLiwwKSkpDQoNCiMgMjAyMCBidWZmZXIgcG9wdWxhdGlvbiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpidWZmZXJfcG9wXzFrbV8yMDIwIDwtIGVzdGltYXRlLmJ1ZmZlci5wb3AoDQogIGthc3VuZ3VfcG9wdWxhdGlvbl8yMDIwLA0KICBidWZmZXJfMWttXzIwMjAsDQogIG1hbGFyaWFfcG9wX2J5X2NhdGNobWVudF8yMDIwKSB8Pg0KICBkcGx5cjo6cmVuYW1lKGNhdGNobWVudF9wb3AgPSBwb3AsDQogICAgICAgICAgICAgICAgYnVmZmVyX3BvcCA9IGJ1ZmZlcl9wb3BfYWdncmVnYXRlZCkgfD4gDQogIGRwbHlyOjptdXRhdGUoDQogICAgcHJvcF9idWZmZXJfY2F0Y2htZW50X3BvcCA9IHJvdW5kKChidWZmZXJfcG9wL2NhdGNobWVudF9wb3ApKjEwMCkpfD4gDQogIGRwbHlyOjptdXRhdGUoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgLmZucyA9IH5yZXBsYWNlX25hKC4sMCkpKQ0KDQpidWZmZXJfcG9wXzJrbV8yMDIwIDwtIGVzdGltYXRlLmJ1ZmZlci5wb3AoDQogIGthc3VuZ3VfcG9wdWxhdGlvbl8yMDIwLA0KICBidWZmZXJfMmttXzIwMjAsDQogIG1hbGFyaWFfcG9wX2J5X2NhdGNobWVudF8yMDIwKSB8PiANCiAgZHBseXI6OnJlbmFtZShjYXRjaG1lbnRfcG9wID0gcG9wLA0KICAgICAgICAgICAgICAgIGJ1ZmZlcl9wb3AgPSBidWZmZXJfcG9wX2FnZ3JlZ2F0ZWQpIHw+IA0KICBkcGx5cjo6bXV0YXRlKA0KICAgIHByb3BfYnVmZmVyX2NhdGNobWVudF9wb3AgPSByb3VuZCgoYnVmZmVyX3BvcC9jYXRjaG1lbnRfcG9wKSoxMDApKXw+IA0KICBkcGx5cjo6bXV0YXRlKGFjcm9zcyhldmVyeXRoaW5nKCksIC5mbnMgPSB+cmVwbGFjZV9uYSguLDApKSkNCg0KYnVmZmVyX3BvcF8za21fMjAyMCA8LSBlc3RpbWF0ZS5idWZmZXIucG9wKA0KICBrYXN1bmd1X3BvcHVsYXRpb25fMjAyMCwNCiAgYnVmZmVyXzNrbV8yMDIwLA0KICBtYWxhcmlhX3BvcF9ieV9jYXRjaG1lbnRfMjAyMCkgfD4gDQogIGRwbHlyOjpyZW5hbWUoY2F0Y2htZW50X3BvcCA9IHBvcCwNCiAgICAgICAgICAgICAgICBidWZmZXJfcG9wID0gYnVmZmVyX3BvcF9hZ2dyZWdhdGVkKSB8PiANCiAgZHBseXI6Om11dGF0ZSgNCiAgICBwcm9wX2J1ZmZlcl9jYXRjaG1lbnRfcG9wID0gcm91bmQoKGJ1ZmZlcl9wb3AvY2F0Y2htZW50X3BvcCkqMTAwKSkgfD4gDQogIGRwbHlyOjptdXRhdGUoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgLmZucyA9IH5yZXBsYWNlX25hKC4sMCkpKQ0KDQoNCmBgYA0KDQojIyBNYXBwaW5nIHByb3BvcnRpb24gb2YgY2F0Y2htZW50IHBvcHVsYXRpb24gbGl2aW5nIHdpdGhpbiB3YXRlcmJvZGllcw0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZyA9IEZBTFNFLCBmaWcuaGVpZ2h0ID0gOSwgZmlnLndpZHRoID0gMTEsIGZpZy5jYXA9J0ZpZy4gMTAuIFByb3BvcnRpb24gb2YgY2F0Y2htZW50IHBvcHVsYXRpb24gbGl2aW5nIGFyb3VuZCB3YXRlciBib2RpZXMnfQ0KDQojIEhlbHBlciBmdW5jdGlvbiB0byBjcmVhdGUgbWFwcyBvZiBwcm9wb3J0aW9uIG9mIHBlb3BsZSBsaXZpbmcgaW4gcHJveGltaXR5IC0tLS0tLS0tLS0NCiMgdG8gd2F0ZXIgYm9kaWVzIGluIGVhY2ggY2F0Y2htZW50IGFyZWENCmNyZWF0ZS5wb3AucHJvcG9ydGlvbi5tYXAgPC0gZnVuY3Rpb24ocG9wLmRhdGEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSA9ICJwcm9wX2J1ZmZlcl9jYXRjaG1lbnRfcG9wIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gTkEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQudGl0bGUgPSBOQSl7DQogDQogICMgcG9wLmRhdGE6IHNmIHBvbHlnb24gb2JqZWN0IGNvbnRhaW5pbmcgcHJvcG9ydGlvbiBvZiBjYXRjaG1lbnQgcG9wdWxhdGlvbiANCiAgIyAgICAgICAgICAgbGl2aW5nIHdpdGhpbiB3YXRlciBib2RpZXMNCiAgIyB2YXJpYWJsZTogdmFyaWFibGUgbmFtZSAoYXMgY2hhcmFjdGVyLCBpbiBxb3V0ZXMpDQogICMgdGl0bGU6IG1hcCB0aXRsZSBpbiBxdW90ZXMNCiAgIyBsZWdlbmQudGl0bGU6IGxlZ2VuZCB0aXRsZSBpbiBxb3V0ZXMNCiAgIyByZXR1cm5zOg0KICAjICAgYSB0bWFwLWVsZW1lbnQgKHBsb3RzIGEgbWFwKQ0KIA0KICB0bV9zaGFwZShwb3AuZGF0YSkrDQogICAgdG1fZmlsbChjb2wgPSB2YXJpYWJsZSwgDQogICAgICAgICAgICBicmVha3MgPSBjKDAsIDEwLCAyMCwgMzAsIDQwLCA1MCwgNjAsIDcwLCA4MCwgOTAsIDEwMCksDQogICAgICAgICAgICBwYWxldHRlID0gIllsT3JCciIsDQogICAgICAgICAgICB0aXRsZSA9IGxlZ2VuZC50aXRsZSkrDQogICAgdG1fYm9yZGVycyhsdyA9IDAuMykrDQogICAgdG1fbGF5b3V0KGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44LCJib3R0b20iKSwNCiAgICAgICAgICAgICAgbGVnZW5kLnRleHQuc2l6ZSA9IDAuNSwNCiAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlLnNpemUgPSAwLjcsDQogICAgICAgICAgICAgIGZyYW1lID0gRkFMU0UpKw0KICAgIHRtX2NyZWRpdHModGl0bGUsIA0KICAgICAgICAgICAgICAgcG9zaXRpb24gPSBjKDAuMjUsIDAuNzUpLCANCiAgICAgICAgICAgICAgIHNpemUgPSAxKQ0KfQ0KDQojIEludm9raW5nIGZ1bmN0aW9uIA0KIyAyMDE3IHBvcHVsYXRpb24gcHJvcG9ydGlvbiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCnBvcF9wcm9wb3J0aW9uXzFrbV8yMDE3X21hcCA8LSBjcmVhdGUucG9wLnByb3BvcnRpb24ubWFwKA0KICBidWZmZXJfcG9wXzFrbV8yMDE3LCANCiAgdGl0bGUgPSAiMjAxNyIsDQogIGxlZ2VuZC50aXRsZSA9ICJQb3B1bGF0aW9uIHdpdGhpbiBcbjFrbSBidWZmZXJzICglKSIpDQoNCnBvcF9wcm9wb3J0aW9uXzJrbV8yMDE3X21hcCA8LSBjcmVhdGUucG9wLnByb3BvcnRpb24ubWFwKA0KICBidWZmZXJfcG9wXzJrbV8yMDE3LCANCiAgdGl0bGUgPSAiMjAxNyIsDQogIGxlZ2VuZC50aXRsZSA9ICJQb3B1bGF0aW9uIHdpdGhpbiBcbjJrbSBidWZmZXJzICglKSIpDQoNCnBvcF9wcm9wb3J0aW9uXzNrbV8yMDE3X21hcCA8LSBjcmVhdGUucG9wLnByb3BvcnRpb24ubWFwKA0KICBidWZmZXJfcG9wXzNrbV8yMDE3LA0KICB0aXRsZSA9ICIyMDE3IiwNCiAgbGVnZW5kLnRpdGxlID0gIlBvcHVsYXRpb24gd2l0aGluIFxuM2ttIGJ1ZmZlcnMgKCUpIikNCg0KIyAyMDE4IHBvcHVsYXRpb24gcHJvcG9ydGlvbiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCnBvcF9wcm9wb3J0aW9uXzFrbV8yMDE4X21hcCA8LSBjcmVhdGUucG9wLnByb3BvcnRpb24ubWFwKA0KICBidWZmZXJfcG9wXzFrbV8yMDE4LA0KICB0aXRsZSA9ICIyMDE4IiwNCiAgbGVnZW5kLnRpdGxlID0gIlBvcHVsYXRpb24gd2l0aGluIFxuMWttIGJ1ZmZlcnMgKCUpIikNCg0KcG9wX3Byb3BvcnRpb25fMmttXzIwMThfbWFwIDwtIGNyZWF0ZS5wb3AucHJvcG9ydGlvbi5tYXAoDQogIGJ1ZmZlcl9wb3BfMmttXzIwMTgsDQogIHRpdGxlID0gIjIwMTgiLA0KICBsZWdlbmQudGl0bGUgPSAiUG9wdWxhdGlvbiB3aXRoaW4gXG4ya20gYnVmZmVycyAoJSkiKQ0KDQpwb3BfcHJvcG9ydGlvbl8za21fMjAxOF9tYXAgPC0gY3JlYXRlLnBvcC5wcm9wb3J0aW9uLm1hcCgNCiAgYnVmZmVyX3BvcF8za21fMjAxOCwNCiAgdGl0bGUgPSAiMjAxOCIsDQogIGxlZ2VuZC50aXRsZSA9ICJQb3B1bGF0aW9uIHdpdGhpbiBcbjNrbSBidWZmZXJzICglKSIpDQoNCiMgMjAxOSBwb3B1bGF0aW9uIHByb3BvcnRpb24gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpwb3BfcHJvcG9ydGlvbl8xa21fMjAxOV9tYXAgPC0gY3JlYXRlLnBvcC5wcm9wb3J0aW9uLm1hcCgNCiAgYnVmZmVyX3BvcF8xa21fMjAxOSwNCiAgdGl0bGUgPSAiMjAxOSIsDQogIGxlZ2VuZC50aXRsZSA9ICJQb3B1bGF0aW9uIHdpdGhpbiBcbjFrbSBidWZmZXJzICglKSIpDQoNCnBvcF9wcm9wb3J0aW9uXzJrbV8yMDE5X21hcCA8LSBjcmVhdGUucG9wLnByb3BvcnRpb24ubWFwKA0KICBidWZmZXJfcG9wXzJrbV8yMDE5LA0KICB0aXRsZSA9ICIyMDE5IiwNCiAgbGVnZW5kLnRpdGxlID0gIlBvcHVsYXRpb24gd2l0aGluIFxuMmttIGJ1ZmZlcnMgKCUpIikNCg0KcG9wX3Byb3BvcnRpb25fM2ttXzIwMTlfbWFwIDwtIGNyZWF0ZS5wb3AucHJvcG9ydGlvbi5tYXAoDQogIGJ1ZmZlcl9wb3BfM2ttXzIwMTksDQogIHRpdGxlID0gIjIwMTkiLA0KICBsZWdlbmQudGl0bGUgPSAiUG9wdWxhdGlvbiB3aXRoaW4gXG4za20gYnVmZmVycyAoJSkiKQ0KDQojIDIwMjAgcG9wdWxhdGlvbiBwcm9wb3J0aW9uIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KcG9wX3Byb3BvcnRpb25fMWttXzIwMjBfbWFwIDwtIGNyZWF0ZS5wb3AucHJvcG9ydGlvbi5tYXAoDQogIGJ1ZmZlcl9wb3BfMWttXzIwMjAsDQogIHRpdGxlID0gIjIwMjAiLA0KICBsZWdlbmQudGl0bGUgPSAiUG9wdWxhdGlvbiB3aXRoaW4gXG4xa20gYnVmZmVycyAoJSkiKQ0KDQpwb3BfcHJvcG9ydGlvbl8ya21fMjAyMF9tYXAgPC0gY3JlYXRlLnBvcC5wcm9wb3J0aW9uLm1hcCgNCiAgYnVmZmVyX3BvcF8ya21fMjAyMCwNCiAgdGl0bGUgPSAiMjAyMCIsDQogIGxlZ2VuZC50aXRsZSA9ICJQb3B1bGF0aW9uIHdpdGhpbiBcbjJrbSBidWZmZXJzICglKSIpDQoNCnBvcF9wcm9wb3J0aW9uXzNrbV8yMDIwX21hcCA8LSBjcmVhdGUucG9wLnByb3BvcnRpb24ubWFwKA0KICBidWZmZXJfcG9wXzNrbV8yMDIwLA0KICB0aXRsZSA9ICIyMDIwIiwNCiAgbGVnZW5kLnRpdGxlID0gIlBvcHVsYXRpb24gd2l0aGluIFxuM2ttIGJ1ZmZlcnMgKCUpIikNCg0KIyBMYXlvdXQgbWFwcyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCnRtYXA6OnRtYXBfYXJyYW5nZShwb3BfcHJvcG9ydGlvbl8xa21fMjAxN19tYXAsIHBvcF9wcm9wb3J0aW9uXzJrbV8yMDE3X21hcCwgDQogICAgICAgICAgICAgICAgICAgcG9wX3Byb3BvcnRpb25fM2ttXzIwMTdfbWFwLCBwb3BfcHJvcG9ydGlvbl8xa21fMjAxOF9tYXAsDQogICAgICAgICAgICAgICAgICAgcG9wX3Byb3BvcnRpb25fMmttXzIwMThfbWFwLCBwb3BfcHJvcG9ydGlvbl8za21fMjAxOF9tYXAsDQogICAgICAgICAgICAgICAgICAgcG9wX3Byb3BvcnRpb25fMWttXzIwMTlfbWFwLCBwb3BfcHJvcG9ydGlvbl8ya21fMjAxOV9tYXAsDQogICAgICAgICAgICAgICAgICAgcG9wX3Byb3BvcnRpb25fM2ttXzIwMTlfbWFwLCBwb3BfcHJvcG9ydGlvbl8xa21fMjAyMF9tYXAsDQogICAgICAgICAgICAgICAgICAgcG9wX3Byb3BvcnRpb25fMmttXzIwMjBfbWFwLCBwb3BfcHJvcG9ydGlvbl8za21fMjAyMF9tYXAsIG5jb2wgPSAzKQ0KYGBgDQoNCiMgU2NhdHRlciBwbG90cyBvZiBTTVIgYWdhaW5zdCB0aGUgcHJvcG9ydGlvbiBvZiB0aGUgY2F0Y2htZW50IHBvcHVsYXRpb24gbGl2aW5nIHdhdGVyYm9keSBidWZmZXJzDQoNCkEgY29ycmVsYXRpb24gY29lZWZpY2llbnQgb2YgbW9yZSB0aGFuIHplcm8gKGNvci5jb2VmZiByIFw+IDAuMSkgaW5kaWNhdGVzIHNvbWUgcG9zaXRpdmUgYXNzb2NpYXRpb24gYmV0d2VlbiB0aGUgU01SIGFuZCB0aGUgYnVmZmVyIHBvcHVsYXRpb24gdmFyaWFibGVzLiBUaGF0IGlzLCBTTVIgb2YgZHJ5IHNlYXNvbiBtYWxhcmlhIGluY3JlYXNlcyB3aXRoIGluY3JlYXNlIGluIG51bWJlciBvZiBwZW9wbGUgc3Vycm91bmRpbmcgd2F0ZXIgYm9kaWVzLiBUaGlzIGltcGxpZXMgdGhhdCBhcyB0aGUgcG9wdWxhdGlvbiBvZiBwZW9wbGUgbGl2aW5nIG5lYXIgd2F0ZXIgYm9kaWVzIGluY3JlYXNlcywgdGhlIHJpc2sgb2YgZHJ5IHNlYXNvbiBtYWxhcmlhIGluY3JlYXNlcyBhcyB3ZWxsLg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmhlaWdodD0xNSwgZmlnLndpZHRoPTExLCBmaWcuY2FwPSdGaWcgMTEuIFJlbGF0aW9uc2hpcCBiZXR3ZWVuIHN0YW5kYXJkaXNlZCBtb3JiaWRpdHkgcmF0aW8gYW5kIGxpdmluZyBuZWFyIHdhdGVyYm9kaWVzJ30NCg0KIyBIZWxwZXIgZnVuY3Rpb24gdG8gdGlkeSBhbmQgYmluZCB0aGUgU01SIGFuZCBwcm9wb3J0aW9uIG9mIC0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgYnVmZmVyLWNhdGNobWVudCBwb3B1bGF0aW9uIGRhdGEgZnJhbWVzDQp0aWR5LmRhdGEgPC0gZnVuY3Rpb24oc21yLmRmLCANCiAgICAgICAgICAgICAgICAgICAgICBwcm9wb3J0aW9uLnBvcC4xa20sIA0KICAgICAgICAgICAgICAgICAgICAgIHByb3Byb3Rpb24ucG9wLjJrbSwNCiAgICAgICAgICAgICAgICAgICAgICBwcm9wb3J0aW9uLnBvcC4za20pew0KDQojIENvbnZlcnQgdGhlIHNmIG9iamVjdHMgdG8gZGF0YSBmcmFtZXMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpzbXJfZGYgPC0gYXMuZGF0YS5mcmFtZShzbXIuZGYpIHw+IA0KICBkcGx5cjo6c2VsZWN0KHJvd0lELCBOYW1lcywgU01SKQ0KDQpwcm9wb3J0aW9uX3BvcF8xa21fZGYgPC0gYXMuZGF0YS5mcmFtZShwcm9wb3J0aW9uLnBvcC4xa20pIHw+IA0KICBkcGx5cjo6c2VsZWN0KHJvd0lELCBwcm9wX3BvcF8xa20gPSBgcHJvcF9idWZmZXJfY2F0Y2htZW50X3BvcGApDQoNCnByb3BvcnRpb25fcG9wXzJrbV9kZiA8LSBhcy5kYXRhLmZyYW1lKHByb3Byb3Rpb24ucG9wLjJrbSkgfD4gDQogIGRwbHlyOjpzZWxlY3Qocm93SUQsIHByb3BfcG9wXzJrbSA9IGBwcm9wX2J1ZmZlcl9jYXRjaG1lbnRfcG9wYCkNCg0KcHJvcG9ydGlvbl9wb3BfM2ttX2RmIDwtIGFzLmRhdGEuZnJhbWUocHJvcG9ydGlvbi5wb3AuM2ttKSB8Pg0KICBkcGx5cjo6c2VsZWN0KHJvd0lELCBwcm9wX3BvcF8za20gPSBgcHJvcF9idWZmZXJfY2F0Y2htZW50X3BvcGApDQoNCiMgTWVyZ2UgU01SIGFuZCBwb3B1bGF0aW9uIGRhdGEgZnJhbWVzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjb21iaW5lZF8xIDwtIG1lcmdlKHNtcl9kZiwgcHJvcG9ydGlvbl9wb3BfMWttX2RmLCBieSA9ICJyb3dJRCIsIGFsbCA9IFRSVUUpDQoNCmNvbWJpbmVkXzIgPC0gbWVyZ2UocHJvcG9ydGlvbl9wb3BfMmttX2RmLCBwcm9wb3J0aW9uX3BvcF8za21fZGYpDQoNCmNvbWJpbmVkX2Z1bGx5IDwtIG1lcmdlKGNvbWJpbmVkXzEsIGNvbWJpbmVkXzIsIGJ5ID0gInJvd0lEIiwgYWxsID0gVFJVRSkNCg0KfQ0KDQojIEludm9raW5nIHRoZSBmdW5jdGlvbiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0Kc21yX3BvcF8yMDE3IDwtIHRpZHkuZGF0YShTTVJfMjAxNywgYnVmZmVyX3BvcF8xa21fMjAxNywgYnVmZmVyX3BvcF8ya21fMjAxNywgYnVmZmVyX3BvcF8za21fMjAxNykNCg0Kc21yX3BvcF8yMDE4IDwtIHRpZHkuZGF0YShTTVJfMjAxOCwgYnVmZmVyX3BvcF8xa21fMjAxOCwgYnVmZmVyX3BvcF8ya21fMjAxOCwgYnVmZmVyX3BvcF8za21fMjAxOCkNCg0Kc21yX3BvcF8yMDE5IDwtIHRpZHkuZGF0YShTTVJfMjAxOSwgYnVmZmVyX3BvcF8xa21fMjAxOSwgYnVmZmVyX3BvcF8ya21fMjAxOSwgYnVmZmVyX3BvcF8za21fMjAxOSkNCg0Kc21yX3BvcF8yMDIwIDwtIHRpZHkuZGF0YShTTVJfMjAyMCwgYnVmZmVyX3BvcF8xa21fMjAyMCwgYnVmZmVyX3BvcF8ya21fMjAyMCwgYnVmZmVyX3BvcF8za21fMjAyMCkNCg0KIyBIZWxwZXIgZnVuY3Rpb24gdG8gY3JlYXRlIHNjYXR0ZXIgcGxvdHMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNyZWF0ZS5zY2F0dGVyLnBsb3QgPC0gZnVuY3Rpb24oc21yLnBvcC5kZiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluZGVwZW5kZW50LnZhciA9IE5BLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXBlbmRlbnQudmFyID0gIlNNUiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHgubGFiZWwgPSBOQSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IE5BKXsNCiAgDQogIHNjYXR0ZXIucGxvdCA8LSBnZ3B1YnI6Omdnc2NhdHRlcihzbXIucG9wLmRmLCAgICAgICAgICAjIGRhdGEgZnJhbWUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBpbmRlcGVuZGVudC52YXIsICMgeC1heGlzIHZhcmlhYmxlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gZGVwZW5kZW50LnZhciwgICAjIHktYXhpcyB2YXJpYWJsZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWRkID0gInJlZy5saW5lIiwgICAgIyBBZGQgcmVncmVzc2lvbiBsaW5lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25mLmludCA9IFRSVUUsICAgICAjIEFkZCBjb25maWRlbmNlIGludGVydmFsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZGQucGFyYW1zID0gbGlzdChjb2xvciA9ICJyZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9ICJsaWdodGdyYXkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAiamNvIiwgICAgICMgam91cm5hbCBjb2xvciBwYWxldHRlLiBzZWUgP2dncGFyDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4bGFiID0geC5sYWJlbCwgICAgICAjIHgtYXhpcyBsYWJlbA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWxhYiA9ICJTTVIiLCAgICAgICAgIyB5LWF4aXMgbGFiZWwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gcGxvdC50aXRsZSkrICAgIA0KICAgICAgICAgICAgICAgICAgZ2dwdWJyOjpzdGF0X2NvcihsYWJlbC55ID0gMykrICAgICAgICAgIyBBZGQgY29ycmVsYXRpb24gY29lZmZpY2llbnQNCiAgICAgICAgICAgICAgICAgIGdncHVicjo6Zm9udCgidGl0bGUiLCBzaXplID0gMTAsIGZhY2UgPSAiYm9sZCIpKw0KICAgICAgICAgICAgICAgICAgZ2dwdWJyOjpmb250KCJ4bGFiIiwgc2l6ZSA9IDEwKSsNCiAgICAgICAgICAgICAgICAgIGdncHVicjo6Zm9udCgieWxhYiIsIHNpemUgPSAxMCkNCiANCiAgcmV0dXJuKHNjYXR0ZXIucGxvdCkNCiAgDQp9DQoNCiMgSW52b2tpbmcgZnVuY3Rpb24gDQojIDIwMTcgc2NhdHRlciBwbG90cyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0Kc2NhdHRlcl8xa21fMjAxNyA8LSBjcmVhdGUuc2NhdHRlci5wbG90KA0KICBzbXJfcG9wXzIwMTcsIGluZGVwZW5kZW50LnZhciA9ICJwcm9wX3BvcF8xa20iLA0KICB4LmxhYmVsID0gIlBlcmNlbnRhZ2Ugb2YgY2F0Y2htZW50IHBvcHVsYXRpb24gXG5saXZpbmcgaW4gMWttIGJ1ZmZlciIsDQogIHBsb3QudGl0bGUgPSAiMjAxNyIpDQoNCnNjYXR0ZXJfMmttXzIwMTcgPC0gY3JlYXRlLnNjYXR0ZXIucGxvdCgNCiAgc21yX3BvcF8yMDE3LCBpbmRlcGVuZGVudC52YXIgPSAicHJvcF9wb3BfMmttIiwNCiAgeC5sYWJlbCA9ICJQZXJjZW50YWdlIG9mIGNhdGNobWVudCBwb3B1bGF0aW9uIFxubGl2aW5nIGluIDJrbSBidWZmZXIiLA0KICBwbG90LnRpdGxlID0gIjIwMTciKQ0KDQpzY2F0dGVyXzNrbV8yMDE3IDwtIGNyZWF0ZS5zY2F0dGVyLnBsb3QoDQogIHNtcl9wb3BfMjAxNywgaW5kZXBlbmRlbnQudmFyID0gInByb3BfcG9wXzNrbSIsDQogIHgubGFiZWwgPSAiUGVyY2VudGFnZSBvZiBjYXRjaG1lbnQgcG9wdWxhdGlvbiBcbmxpdmluZyBpbiAza20gYnVmZmVyIiwNCiAgcGxvdC50aXRsZSA9ICIyMDE3IikNCg0KIyAyMDE4IHNjYXR0ZXIgcGxvdHMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCnNjYXR0ZXJfMWttXzIwMTggPC0gY3JlYXRlLnNjYXR0ZXIucGxvdCgNCiAgc21yX3BvcF8yMDE4LCBpbmRlcGVuZGVudC52YXIgPSAicHJvcF9wb3BfMWttIiwNCiAgeC5sYWJlbCA9ICJQZXJjZW50YWdlIG9mIGNhdGNobWVudCBwb3B1bGF0aW9uIFxubGl2aW5nIGluIDFrbSBidWZmZXIiLA0KICBwbG90LnRpdGxlID0gIjIwMTgiKQ0KDQpzY2F0dGVyXzJrbV8yMDE4IDwtIGNyZWF0ZS5zY2F0dGVyLnBsb3QoDQogIHNtcl9wb3BfMjAxOCwgaW5kZXBlbmRlbnQudmFyID0gInByb3BfcG9wXzJrbSIsDQogIHgubGFiZWwgPSAiUGVyY2VudGFnZSBvZiBjYXRjaG1lbnQgcG9wdWxhdGlvbiBcbmxpdmluZyBpbiAya20gYnVmZmVyIiwNCiAgcGxvdC50aXRsZSA9ICIyMDE4IikNCg0Kc2NhdHRlcl8za21fMjAxOCA8LSBjcmVhdGUuc2NhdHRlci5wbG90KA0KICBzbXJfcG9wXzIwMTgsIGluZGVwZW5kZW50LnZhciA9ICJwcm9wX3BvcF8za20iLA0KICB4LmxhYmVsID0gIlBlcmNlbnRhZ2Ugb2YgY2F0Y2htZW50IHBvcHVsYXRpb24gXG5saXZpbmcgaW4gM2ttIGJ1ZmZlciIsDQogIHBsb3QudGl0bGUgPSAiMjAxOCIpDQoNCiMgMjAxOSBzY2F0dGVyIHBsb3RzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpzY2F0dGVyXzFrbV8yMDE5IDwtIGNyZWF0ZS5zY2F0dGVyLnBsb3QoDQogIHNtcl9wb3BfMjAxOSwgaW5kZXBlbmRlbnQudmFyID0gInByb3BfcG9wXzFrbSIsDQogIHgubGFiZWwgPSAiUGVyY2VudGFnZSBvZiBjYXRjaG1lbnQgcG9wdWxhdGlvbiBcbmxpdmluZyBpbiAxa20gYnVmZmVyIiwNCiAgcGxvdC50aXRsZSA9ICIyMDE5IikNCg0Kc2NhdHRlcl8ya21fMjAxOSA8LSBjcmVhdGUuc2NhdHRlci5wbG90KA0KICBzbXJfcG9wXzIwMTksIGluZGVwZW5kZW50LnZhciA9ICJwcm9wX3BvcF8ya20iLA0KICB4LmxhYmVsID0gIlBlcmNlbnRhZ2Ugb2YgY2F0Y2htZW50IHBvcHVsYXRpb24gXG5saXZpbmcgaW4gMmttIGJ1ZmZlciIsDQogIHBsb3QudGl0bGUgPSAiMjAxOSIpDQoNCnNjYXR0ZXJfM2ttXzIwMTkgPC0gY3JlYXRlLnNjYXR0ZXIucGxvdCgNCiAgc21yX3BvcF8yMDE5LCBpbmRlcGVuZGVudC52YXIgPSAicHJvcF9wb3BfM2ttIiwNCiAgeC5sYWJlbCA9ICJQZXJjZW50YWdlIG9mIGNhdGNobWVudCBwb3B1bGF0aW9uIFxubGl2aW5nIGluIDNrbSBidWZmZXIiLA0KICBwbG90LnRpdGxlID0gIjIwMTkiKQ0KDQojIDIwMjAgc2NhdHRlciBwbG90cyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0Kc2NhdHRlcl8xa21fMjAyMCA8LSBjcmVhdGUuc2NhdHRlci5wbG90KA0KICBzbXJfcG9wXzIwMjAsIGluZGVwZW5kZW50LnZhciA9ICJwcm9wX3BvcF8xa20iLA0KICB4LmxhYmVsID0gIlBlcmNlbnRhZ2Ugb2YgY2F0Y2htZW50IHBvcHVsYXRpb24gXG5saXZpbmcgaW4gMWttIGJ1ZmZlciIsDQogIHBsb3QudGl0bGUgPSAiMjAyMCIpDQoNCnNjYXR0ZXJfMmttXzIwMjAgPC0gY3JlYXRlLnNjYXR0ZXIucGxvdCgNCiAgc21yX3BvcF8yMDIwLCBpbmRlcGVuZGVudC52YXIgPSAicHJvcF9wb3BfMmttIiwNCiAgeC5sYWJlbCA9ICJQZXJjZW50YWdlIG9mIGNhdGNobWVudCBwb3B1bGF0aW9uIFxubGl2aW5nIGluIDJrbSBidWZmZXIiLA0KICBwbG90LnRpdGxlID0gIjIwMjAiKQ0KDQpzY2F0dGVyXzNrbV8yMDIwIDwtIGNyZWF0ZS5zY2F0dGVyLnBsb3QoDQogIHNtcl9wb3BfMjAyMCwgaW5kZXBlbmRlbnQudmFyID0gInByb3BfcG9wXzNrbSIsDQogIHgubGFiZWwgPSAiUGVyY2VudGFnZSBvZiBjYXRjaG1lbnQgcG9wdWxhdGlvbiBcbmxpdmluZyBpbiAza20gYnVmZmVyIiwNCiAgcGxvdC50aXRsZSA9ICIyMDIwIikNCg0KIyBBcnJhbmdlIHRoZSBwbG90cyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmdncHVicjo6Z2dhcnJhbmdlKHNjYXR0ZXJfMWttXzIwMTcsIHNjYXR0ZXJfMmttXzIwMTcsIHNjYXR0ZXJfM2ttXzIwMTcsDQogICAgICAgICAgICAgICAgICBzY2F0dGVyXzFrbV8yMDE4LCBzY2F0dGVyXzJrbV8yMDE4LCBzY2F0dGVyXzNrbV8yMDE4LA0KICAgICAgICAgICAgICAgICAgc2NhdHRlcl8xa21fMjAxOSwgc2NhdHRlcl8ya21fMjAxOSwgc2NhdHRlcl8za21fMjAxOSwgDQogICAgICAgICAgICAgICAgICBzY2F0dGVyXzFrbV8yMDIwLCBzY2F0dGVyXzJrbV8yMDIwLCBzY2F0dGVyXzNrbV8yMDIwLA0KICAgICAgICAgICAgICAgICAgbmNvbCA9IDMsIG5yb3cgPSA0KQ0KDQpgYGANCg0KIyBOb3JtYWxpdHkgdGVzdA0KDQpGaXJzdCwgd2UgY2hlY2sgaWYgdGhlIGNvdW50IGRhdGEgKGRyeSBzZWFzb24gbWFsYXJpYSBjYXNlcykgaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgb3IgZm9sbG93cyBhIFBvaXNzb24gZGlzdHJpYnV0aW9uLlRoZSBQb2lzc29uIGRpc3RyaWJ1dGlvbiBpcyBhIGRpc2NyZXRlIGRpc3RyaWJ1dGlvbiB0aGF0IG1lYXN1cmVzIHRoZSBwcm9iYWJpbGl0eSBvZiBhIGdpdmVuIG51bWJlciBvZiBldmVudHMgaGFwcGVuaW5nIGluIGEgc3BlY2lmaWVkIHRpbWUgcGVyaW9kIGdlbmVyYXRlZCBieSBhIFBvaXNzb24gcHJvY2Vzcy4gQnkgUG9pc3NvbiBwcm9jZXNzZXMsIHdlIG1lYW4gcHJvY2Vzc2VzIHRoYXQgYXJlIGRpc2NyZXRlLCBpbmRlcGVuZGVudCwgYW5kIG11dHVhbGx5IGV4Y2x1c2l2ZSwgZS5nLiwgZHJ5IHNlYXNvbiBtYWxhcmlhIGNhc2VzLg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgZmlnLndpZHRoPTEwLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSAnRmlnIDEyLiBQb2lzc29uIGRpc3RyaWJ1dGlvbiBvZiBkcnkgc2Vhc29uIG1hbGFyaWEgY2FzZXMuIENsZWFybHksIHRoZSBkYXRhIGlzIG5vdCBpbiB0aGUgZm9ybSBvZiBhIGJlbGwgY3VydmUgbGlrZSBpbiBhIG5vcm1hbCBkaXN0cmlidXRpb24uIE1hbnkgaGVhbHRoIGZhY2lsaXRpZXMgcmVwb3J0ZWQgdmVyeSBmZXcgbWFsYXJpYSBjYXNlcy4gQSBmZXcgaGVhbHRoIGZhY2lsaXRpZXMgaGF2ZSBhIGxhcmdlIG51bWJlciBvZiBjYXNlcyBtYWtpbmcgZm9yIGEgZGlzdHJpYnV0aW9uIHRoYXQgYXBwZWFycyB0byBiZSBmYXIgZnJvbSBub3JtYWwuIFRoZXJlZm9yZSwgUG9pc3NvbiByZWdyZXNzaW9uIHdpbGwgYmUgdXNlZCB0byBtb2RlbCBvdXIgZHJ5IHNlYXNvbiBtYWxhcmlhIGRhdGEuJ30NCiMgQ29tYmluZSBkYXRhIGZvciBtb2RlbCBmaXR0aW5nIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQptb2RlbF9kYXRhXzIwMTcgPC0gbWVyZ2UoZXhwZWN0ZWRfbWFsYXJpYV8yMDE3LCBzbXJfcG9wXzIwMTcsIGJ5ID0gInJvd0lEIiwgYWxsID0gVFJVRSkgfD4NCiAgZHBseXI6OnNlbGVjdCgtTmFtZXMueSkgfD4gDQogIGRwbHlyOjpyZW5hbWUoTmFtZXMgPSBOYW1lcy54KQ0KDQptb2RlbF9kYXRhXzIwMTggPC0gIG1lcmdlKGV4cGVjdGVkX21hbGFyaWFfMjAxOCwgc21yX3BvcF8yMDE4LCBieSA9ICJyb3dJRCIsIGFsbCA9IFRSVUUpIHw+IA0KICBkcGx5cjo6c2VsZWN0KC1OYW1lcy55KSB8PiANCiAgZHBseXI6OnJlbmFtZShOYW1lcyA9IE5hbWVzLngpDQoNCm1vZGVsX2RhdGFfMjAxOSA8LSAgbWVyZ2UoZXhwZWN0ZWRfbWFsYXJpYV8yMDE5LCBzbXJfcG9wXzIwMTksIGJ5ID0gInJvd0lEIiwgYWxsID0gVFJVRSkgfD4NCiAgZHBseXI6OnNlbGVjdCgtTmFtZXMueSkgfD4gDQogIGRwbHlyOjpyZW5hbWUoTmFtZXMgPSBOYW1lcy54KQ0KDQptb2RlbF9kYXRhXzIwMjAgPC0gIG1lcmdlKGV4cGVjdGVkX21hbGFyaWFfMjAyMCwgc21yX3BvcF8yMDIwLCBieSA9ICJyb3dJRCIsIGFsbCA9IFRSVUUpIHw+IA0KICBkcGx5cjo6c2VsZWN0KC1OYW1lcy55KSB8Pg0KICBkcGx5cjo6cmVuYW1lKE5hbWVzID0gTmFtZXMueCkNCg0KIyBOb3JtYWxpdHkgdGVzdCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgQ2hlY2sgd2hldGhlciB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIGZvbGxvd3MgYSBwb2lzc29uIGRpc3RyaWJ1dGlvbg0KIyBEcnkgc2Vhc29uIG1hbGFyaWEgY2FzZXMgYXBwZWFyIHRvIGJlIG5vbiBub3JtYWxseSBkaXN0cmlidXRlZCBpLmUgaGlnaGx5IHNrZXdlZCANCg0KaGlzdG9ncmFtXzIwMTcgPC0gZ2dwbG90Mjo6Z2dwbG90KG1vZGVsX2RhdGFfMjAxNywgYWVzKHggPSBvYnNlcnZlZF8yMDE3KSkgKw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiYmxhY2siKSsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4ob2JzZXJ2ZWRfMjAxNykpLCANCiAgICAgICAgICAgICBjb2xvciA9ICJibHVlIiwNCiAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiKSsNCiAgbGFicyh0aXRsZSA9ICIyMDE3IiwgeCA9ICJPYnNlcnZlZCBtYWxhcmlhIGNhc2VzIiwgeSA9ICJDb3VudCIpKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KaGlzdG9ncmFtXzIwMTggPC0gZ2dwbG90Mjo6Z2dwbG90KG1vZGVsX2RhdGFfMjAxOCwgYWVzKHggPSBvYnNlcnZlZF8yMDE4KSkgKw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiYmxhY2siKSsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4ob2JzZXJ2ZWRfMjAxOCkpLCANCiAgICAgICAgICAgICBjb2xvciA9ICJibHVlIiwNCiAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiKSsNCiAgbGFicyh0aXRsZSA9ICIyMDE4IiwgeCA9ICJPYnNlcnZlZCBtYWxhcmlhIGNhc2VzIiwgeSA9ICJDb3VudCIpKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KaGlzdG9ncmFtXzIwMTkgPC0gZ2dwbG90Mjo6Z2dwbG90KG1vZGVsX2RhdGFfMjAxOSwgYWVzKHggPSBvYnNlcnZlZF8yMDE5KSkgKw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiYmxhY2siKSsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4ob2JzZXJ2ZWRfMjAxOSkpLCANCiAgICAgICAgICAgICBjb2xvciA9ICJibHVlIiwNCiAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiKSsNCiAgbGFicyh0aXRsZSA9ICIyMDE5IiwgeCA9ICJPYnNlcnZlZCBtYWxhcmlhIGNhc2VzIiwgeSA9ICJDb3VudCIpKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KaGlzdG9ncmFtXzIwMjAgPC0gZ2dwbG90Mjo6Z2dwbG90KG1vZGVsX2RhdGFfMjAyMCwgYWVzKHggPSBvYnNlcnZlZF8yMDIwKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiYmxhY2siKSsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4ob2JzZXJ2ZWRfMjAyMCkpLCANCiAgICAgICAgICAgICBjb2xvciA9ICJibHVlIiwNCiAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiKSsNCiAgbGFicyh0aXRsZSA9ICIyMDIwIiwgeCA9ICJPYnNlcnZlZCBtYWxhcmlhIGNhc2VzIiwgeSA9ICJDb3VudCIpKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoaGlzdG9ncmFtXzIwMTcsIGhpc3RvZ3JhbV8yMDE4LCBoaXN0b2dyYW1fMjAxOSwgaGlzdG9ncmFtXzIwMjApDQoNCiMgTGV04oCZcyBjaGVjayBvdXQgdGhlIG1lYW4gYW5kIHZhcmlhbmNlIG9mIHRoZSBkZXBlbmRlbnQgdmFyaWFibGU6DQoNCm1lYW4obW9kZWxfZGF0YV8yMDE3JG9ic2VydmVkXzIwMTcpDQp2YXIobW9kZWxfZGF0YV8yMDE3JG9ic2VydmVkXzIwMTcpDQoNCm1lYW4obW9kZWxfZGF0YV8yMDE4JG9ic2VydmVkXzIwMTgpDQp2YXIobW9kZWxfZGF0YV8yMDE4JG9ic2VydmVkXzIwMTgpDQoNCm1lYW4obW9kZWxfZGF0YV8yMDE5JG9ic2VydmVkXzIwMTkpDQp2YXIobW9kZWxfZGF0YV8yMDE5JG9ic2VydmVkXzIwMTkpDQoNCm1lYW4obW9kZWxfZGF0YV8yMDIwJG9ic2VydmVkXzIwMjApDQp2YXIobW9kZWxfZGF0YV8yMDIwJG9ic2VydmVkXzIwMjApDQoNCiMgVGhlIHZhcmlhbmNlIGlzIG11Y2ggZ3JlYXRlciB0aGFuIHRoZSBtZWFuLCB3aGljaCBzdWdnZXN0cyB0aGF0IHdlIHdpbGwgDQojIGhhdmUgb3Zlci1kaXNwZXJzaW9uIGluIHRoZSBtb2RlbC4NCg0KYGBgDQoNCiMgTW9kZWwgZml0dGluZw0KJCQgbG4gKEUoeSkpID0g8J2ct18wICsg8J2ct18xIHggKyBsbijwnZKGX/CdkoopJCQNCndoZXJlLA0KZGVwZW5kZW50IHZhcmlhYmxlLCDwnZGmID0gb2JzZXJ2ZWQgbWFsYXJpYSBjYXNlczsNCvCdkLgo8J2RpikgPSBleHBlY3RlZCBjb3VudCB2YWx1ZTsNCmluZGVwZW5kZW50IHZhcmlhYmxlLCDwnZGlID0gcGVyY2VudGFnZSBvZiBwZW9wbGUgbGl2aW5nIG5lYXIgZGFtczsgYW5kDQpvZmZzZXQsIPCdkZIgPSBleHBlY3RlZCBtYWxhcmlhIGNhc2VzLg0KDQpUbyBjb3BlIHdpdGggdGhlIG1hbGFyaWEgY291bnQgZGF0YSBjb21pbmcgZnJvbSBwb3B1bGF0aW9ucyBvZiBkaWZmZXJlbnQgc2l6ZXMsIHdlIHNwZWNpZnkgYW4gb2Zmc2V0IGFyZ3VtZW50LiBUaGlzIGFkZHMgYSBjb25zdGFudCB0ZXJtIGZvciBlYWNoIHJvdyBvZiB0aGUgZGF0YSBpbiB0aGUgbW9kZWwuIFRoZSBsb2cgb2YgdGhlIGV4cGVjdGVkIGNhc2VzIGlzIHVzZWQgaW4gdGhlIG9mZnNldCB0ZXJtLg0KDQoNClN1bW1hcnkgb3V0cHV0czoNCg0KYEVzdGltYXRlYCAgICA6IHRoZSBgaW50ZXJjZXB0YCAoJPCdnLdfMCQpIGFuZCB0aGUgYmV0YSBjb2VmZmljaWVudCBlc3RpbWF0ZXMgYXNzb2NpYXRlZCB0byBlYWNoIHByZWRpY3RvciB2YXJpYWJsZS4NCg0KYFN0ZC5FcnJvcmAgICA6IHRoZSBgc3RhbmRhcmQgZXJyb3JgIG9mIHRoZSBjb2VmZmljaWVudCBlc3RpbWF0ZXMuIFRoaXMgcmVwcmVzZW50cyB0aGUgYWNjdXJhY3kgb2YgdGhlIGNvZWZmaWNpZW50cy4gVGhlIGxhcmdlciB0aGUgc3RhbmRhcmQgZXJyb3IsIHRoZSBsZXNzIGNvbmZpZGVudCB3ZSBhcmUgYWJvdXQgdGhlIGVzdGltYXRlLg0KDQpgdCB2YWx1ZWAgICAgIDogdGhlIGB0LXN0YXRpc3RpY2AsIHdoaWNoIGlzIHRoZSBjb2VmZmljaWVudCBlc3RpbWF0ZSAoY29sdW1uIDIpIGRpdmlkZWQgYnkgdGhlIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBlc3RpbWF0ZSAoY29sdW1uIDMpLiBGb3IgYSBnaXZlbiB0aGUgcHJlZGljdG9yLCB0aGUgYHQtc3RhdGlzdGljYCBldmFsdWF0ZXMgd2hldGhlciBvciBub3QgdGhlcmUgaXMgc2lnbmlmaWNhbnQgYXNzb2NpYXRpb24gYmV0d2VlbiB0aGUgcHJlZGljdG9yIGFuZCB0aGUgb3V0Y29tZSB2YXJpYWJsZSwgaS5lLiwgd2hldGhlciB0aGUgYmV0YSBjb2VmZmljaWVudCBvZiB0aGUgcHJlZGljdG9yIGlzIHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGZyb20gemVyby4NCg0KYFByKD58dHwpYCAgICA6IFRoZSBgcC12YWx1ZWAgY29ycmVzcG9uZGluZyB0byB0aGUgYHQtc3RhdGlzdGljYC4gVGhlIHNtYWxsZXIgdGhlIGBwLXZhbHVlYCwgdGhlIG1vcmUgc2lnbmlmaWNhbnQgdGhlIGVzdGltYXRlIGlzLg0KDQpgUmVzaWR1YWxzYCAgIDogUHJvdmlkZSBhIHF1aWNrIHZpZXcgb2YgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgcmVzaWR1YWxzLCB3aGljaCBieSBkZWZpbml0aW9uIGhhdmUgYSBtZWFuIHplcm8uIFRoZXJlZm9yZSwgdGhlIGBtZWRpYW5gIHNob3VsZCBub3QgYmUgZmFyIGZyb20gemVybywgYW5kIHRoZSBtaW5pbXVtIChgbWluYClhbmQgbWF4aW11bSAoYG1heGApIHNob3VsZCBiZSByb3VnaGx5IGVxdWFsIGluIGFic29sdXRlIHZhbHVlLg0KDQpgQ29lZmZpY2llbnRzYDogU2hvd3MgdGhlIHJlZ3Jlc3Npb24gYmV0YSBjb2VmZmljaWVudHMgYW5kIHRoZWlyIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZS4gUHJlZGljdG9yIHZhcmlhYmxlcywgdGhhdCBhcmUgc2lnbmlmaWNhbnRseSBhc3NvY2lhdGVkIHRvIHRoZSBvdXRjb21lIHZhcmlhYmxlLCBhcmUgbWFya2VkIGJ5IHN0YXJzLg0KDQpgUmVzaWR1YWwgc3RhbmRhcmQgZXJyb3JgIChgUlNFYCksIGFuZCBgUi1zcXVhcmVkYCAoJFJeMiQpICBtZXRyaWNzIHRlbGwgaG93IHdlbGwgdGhlIG1vZGVsIGZpdHMgdG8gb3VyIGRhdGEuIEFuIChhZGp1c3RlZCkgJFJeMiQgdGhhdCBpcyBjbG9zZSB0byAxIGluZGljYXRlcyB0aGF0IGEgbGFyZ2UgcHJvcG9ydGlvbiBvZiB0aGUgdmFyaWFiaWxpdHkgaW4gdGhlIG91dGNvbWUgaGFzIGJlZW4gZXhwbGFpbmVkIGJ5IHRoZSByZWdyZXNzaW9uIG1vZGVsLiBBIG51bWJlciBuZWFyIDAgaW5kaWNhdGVzIHRoYXQgdGhlIHJlZ3Jlc3Npb24gbW9kZWwgZGlkIG5vdCBleHBsYWluIG11Y2ggb2YgdGhlIHZhcmlhYmlsaXR5IGluIHRoZSBvdXRjb21lLg0KDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQ0KIyBGaXQgZ2VuZXJhbGlzZWQgbGluZWFyIG1vZGVsIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgRGVmaW5pbmcgbW9kZWwgcGFyYW1ldGVyczoNCiMgcmVzcG9uc2UgdmFyaWFibGU6IG9ic2VydmVkXzIwMTcsIG9ic2VydmVkXzIwMTgsIG9ic2VydmVkXzIwMTksIG9ic2VydmVkXzIwMjAgYXJlDQojICAgICAgICAgICAgICAgICAgICByZWNvcmRlZCBkcnkgc2Vhc29uIG1hbGFyaWEgY2FzZXMgaW4gdGhhdCB5ZWFyDQojIHJpc2sgZmFjdG9yOiBwcm9wX3BvcF8xa20sICBwcm9wX3BvcF8ya20sICBwcm9wX3BvcF8za20gYXJlIHRoZSBwZXJjZW50YWdlIG9mIHBlb3BsZSBsaXZpbmcNCiMgICAgICAgICAgICAgd2l0aGluIDFrbSwgMmttIGFuZCAza20gYnVmZmVycyBvZiB3YXRlciBib2RpZXMsIHJlc3BlY3RpdmVseS4NCiMgb2Zmc2V0OiBleHBlY3RlZF8qIGlzIHRoZSBudW1iZXIgb2YgbWFsYXJpYSBjYXNlcyB3ZSB3b3VsZCBleHBlY3QgaWYgdGhlIG1hbGFyaWEgcmF0ZQ0KIyAgICAgICAgICB3YXMgZXF1YWwgaW4gYWxsIHRoZSBjYXRjaG1lbnQgYXJlYXMNCg0KIyAyMDE3IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCm1vZGVsXzFrbV8yMDE3IDwtIGdsbShvYnNlcnZlZF8yMDE3fnByb3BfcG9wXzFrbStvZmZzZXQobG9nKGV4cGVjdGVkXzIwMTcpKSwNCiAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gbW9kZWxfZGF0YV8yMDE3LCBmYW1pbHkgPSBwb2lzc29uKGxpbmsgPSAibG9nIikpDQoNCiMgRGlzcGxheSB0aGUgc3RhdGlzdGljYWwgc3VtbWFyeSBvZiB0aGUgbW9kZWwNCiMgc3VtbWFyeShtb2RlbF8xa21fMjAxNykNCiMgZ2VuZXJhdGUgbW9kZWwgcmVwb3J0IGFjY29yZGluZyB0byBiZXN0IHByYWN0aWNlcyBndWlkZWxpbmVzDQpyZXBvcnQ6OnJlcG9ydChtb2RlbF8xa21fMjAxNykNCg0KYGBgDQpUaGUgZXN0aW1hdGVkIHJlZ3Jlc3Npb24gZXF1YXRpb24gY2FuIGJlIHdyaXR0ZW4gYXMgZm9sbG93Og0KIG9ic2VydmVkIG1hbGFyaWEgY2FzZXMgPSAtMC4yOTAwMTUgKyAwLjAzMjU5NCAqIHBlcmNlbnRhZ2Ugb2YgY2F0Y2htZW50IHBvcHVsYXRpb24gbGl2aW5nIG5lYXIgZGFtLiANClVzaW5nIHRoaXMgZm9ybXVsYSwgZm9yIGVhY2ggY2hhbmdlIGluIG51bWJlciBvZiBwZW9wbGUgbGl2aW5nIG5lYXIgZGFtcywgd2UgY2FuIHByZWRpY3QgdGhlIG51bWJlciBvZiBkcnkgc2Vhc29uIG1hbGFyaWEgY2FzZS4NCk91ciBlc3RpbWF0ZWQgbW9kZWw6DQogICAgICAgZHJ5IHNlYXNvbiBtYWxhcmlhIHJpc2sgPSAtMC4yOSArIDAuMDMgKyBsbihlKQ0KSWYgdGhlIG51bWJlciBvZiBwZW9wbGUgbGl2aW5nIGFyb3VuZCBkYW1zIHdhcyAwLCBkcnkgc2Vhc29uIG1hbGFyaWEgcmlzayB3b3VsZCBiZSAtMC4yOS4gVGhhdCBpcyB0aGUgZXN0aW1hdGUgMC4yOSBjYW4gYmUgaW50ZXJwcmV0ZWQgYXMgdGhlIGRyeSBzZWFzb24gbWFsYXJpYSByaXNrIGluIGEgY2F0Y2htZW50IHdpdGggbm8gcGVvcGxlIGxpdmluZyBuZWFyIHdhdGVyYm9kaWVzLg0KDQpGb3IgZXhhbXBsZToNCiAgIC0gZm9yIGEgYnVmZmVyIHBvcHVsYXRpb24gZXF1YWwgemVybywgd2UgY2FuIGV4cGVjdCAtMC4yOSBtYWxhcmlhIGNhc2VzLg0KICAgLSBmb3IgYSBidWZmZXIgcG9wdWxhdGlvbiBlcXVhbCAxMDAwLCB3ZSBjYW4gZXhwZWN0ICByb3VuZCgtMC4yOSArIDAuMDMgKiAxMDAwKSA9IDMwIG1hbGFyaWEgY2FzZXMuDQoNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0V9DQoNCnNqUGxvdDo6dGFiX21vZGVsKG1vZGVsXzFrbV8yMDE3LCBkaWdpdHMgPSA0LCBkaWdpdHMucmUgPSA0LA0KICAgICAgICAgICAgICAgICAgc2hvdy5yMiA9IEZBTFNFLCBzaG93LmFpYyA9IFRSVUUpDQoNCiMgRnJvbSB0aGUgb3V0cHV0IGFib3ZlLCB0aGUgcC12YWx1ZSBpcyAwLjAwMSAtIHdheSBsZXNzIHRoYW4gdGhlIGFscGhhIHZhbHVlIG9mIDAuMDUsIA0KIyB3ZSB0aGVyZWZvcmUgcmVqZWN0IG91ciBudWxsIGh5cG90aGVzaXMsIG1lYW5pbmcgdGhhdCB0aGVyZSBpcyBhIGRpZmZlcmVuY2UgaW4gDQojIGRyeSBzZWFzb24gbWFsYXJpYSByaXNrIGJldHdlZW4gcGVvcGxlIGxpdmluZyBjbG9zZSB0byB3YXRlciBib2RpZXMgdGhhbiB0aG9zZSBsaXZpbmcgZmFyIGF3YXkuDQoNCg0KbW9kZWxfMmttXzIwMTcgPC0gZ2xtKG9ic2VydmVkXzIwMTd+MStwcm9wX3BvcF8ya20rb2Zmc2V0KGxvZyhleHBlY3RlZF8yMDE3KSksDQogICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IG1vZGVsX2RhdGFfMjAxNywgZmFtaWx5ID0gcG9pc3NvbihsaW5rID0gImxvZyIpKQ0KDQojIHN1bW1hcnkobW9kZWxfMmttXzIwMTcpDQpyZXBvcnQ6OnJlcG9ydChtb2RlbF8ya21fMjAxNykNCg0Kc2pQbG90Ojp0YWJfbW9kZWwobW9kZWxfMmttXzIwMTcsIGRpZ2l0cyA9IDMsIGRpZ2l0cy5yZSA9IDMsDQogICAgICAgICAgICAgICAgICBzaG93LnIyID0gRkFMU0UsIHNob3cuYWljID0gVFJVRSkNCg0KbW9kZWxfM2ttXzIwMTcgPC0gZ2xtKG9ic2VydmVkXzIwMTd+MStwcm9wX3BvcF8za20rb2Zmc2V0KGxvZyhleHBlY3RlZF8yMDE3KSksDQogICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IG1vZGVsX2RhdGFfMjAxNywgZmFtaWx5ID0gcG9pc3NvbihsaW5rID0gImxvZyIpKQ0KDQojc3VtbWFyeShtb2RlbF8za21fMjAxNykNCnJlcG9ydDo6cmVwb3J0KG1vZGVsXzNrbV8yMDE3KQ0KDQpzalBsb3Q6OnRhYl9tb2RlbChtb2RlbF8za21fMjAxNywgZGlnaXRzID0gMywgZGlnaXRzLnJlID0gMywNCiAgICAgICAgICAgICAgICAgIHNob3cucjIgPSBGQUxTRSwgc2hvdy5haWMgPSBUUlVFKQ0KDQojIDIwMTggLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KbW9kZWxfMWttXzIwMTggPC0gZ2xtKG9ic2VydmVkXzIwMTh+MStwcm9wX3BvcF8xa20rb2Zmc2V0KGxvZyhleHBlY3RlZF8yMDE4KSksDQogICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IG1vZGVsX2RhdGFfMjAxOCwgZmFtaWx5ID0gcG9pc3NvbihsaW5rID0gImxvZyIpKQ0KDQojIHN1bW1hcnkobW9kZWxfMWttXzIwMTgpDQpyZXBvcnQ6OnJlcG9ydChtb2RlbF8xa21fMjAxOCkNCg0Kc2pQbG90Ojp0YWJfbW9kZWwobW9kZWxfMWttXzIwMTgsIGRpZ2l0cyA9IDMsIGRpZ2l0cy5yZSA9IDMsDQogICAgICAgICAgICAgICAgICBzaG93LnIyID0gRkFMU0UsIHNob3cuYWljID0gVFJVRSkNCg0KbW9kZWxfMmttXzIwMTggPC0gZ2xtKG9ic2VydmVkXzIwMTh+MStwcm9wX3BvcF8ya20rb2Zmc2V0KGxvZyhleHBlY3RlZF8yMDE4KSksDQogICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IG1vZGVsX2RhdGFfMjAxOCwgZmFtaWx5ID0gcG9pc3NvbihsaW5rID0gImxvZyIpKQ0KDQojIHN1bW1hcnkobW9kZWxfMmttXzIwMTgpDQpyZXBvcnQ6OnJlcG9ydChtb2RlbF8ya21fMjAxOCkNCg0Kc2pQbG90Ojp0YWJfbW9kZWwobW9kZWxfMmttXzIwMTgsIGRpZ2l0cyA9IDMsIGRpZ2l0cy5yZSA9IDMsDQogICAgICAgICAgICAgICAgICBzaG93LnIyID0gRkFMU0UsIHNob3cuYWljID0gVFJVRSkNCg0KbW9kZWxfM2ttXzIwMTggPC0gZ2xtKG9ic2VydmVkXzIwMTh+MStwcm9wX3BvcF8za20rb2Zmc2V0KGxvZyhleHBlY3RlZF8yMDE4KSksDQogICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IG1vZGVsX2RhdGFfMjAxOCwgZmFtaWx5ID0gcG9pc3NvbihsaW5rID0gImxvZyIpKQ0KDQojIHN1bW1hcnkobW9kZWxfM2ttXzIwMTgpDQpyZXBvcnQ6OnJlcG9ydChtb2RlbF8za21fMjAxOCkNCg0Kc2pQbG90Ojp0YWJfbW9kZWwobW9kZWxfM2ttXzIwMTgsIGRpZ2l0cyA9IDMsIGRpZ2l0cy5yZSA9IDMsDQogICAgICAgICAgICAgICAgICBzaG93LnIyID0gRkFMU0UsIHNob3cuYWljID0gVFJVRSkNCg0KIyAyMDE5IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCm1vZGVsXzFrbV8yMDE5IDwtIGdsbShvYnNlcnZlZF8yMDE5fjErcHJvcF9wb3BfMWttK29mZnNldChsb2coZXhwZWN0ZWRfMjAxOSkpLA0KICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBtb2RlbF9kYXRhXzIwMTksIGZhbWlseSA9IHBvaXNzb24obGluayA9ICJsb2ciKSkNCg0KIyBzdW1tYXJ5KG1vZGVsXzFrbV8yMDE5KQ0KcmVwb3J0OjpyZXBvcnQobW9kZWxfMWttXzIwMTkpDQoNCnNqUGxvdDo6dGFiX21vZGVsKG1vZGVsXzFrbV8yMDE5LCBkaWdpdHMgPSAzLCBkaWdpdHMucmUgPSAzLA0KICAgICAgICAgICAgICAgICAgc2hvdy5yMiA9IEZBTFNFLCBzaG93LmFpYyA9IFRSVUUpDQoNCg0KbW9kZWxfMmttXzIwMTkgPC0gZ2xtKG9ic2VydmVkXzIwMTl+MStwcm9wX3BvcF8ya20rb2Zmc2V0KGxvZyhleHBlY3RlZF8yMDE5KSksDQogICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IG1vZGVsX2RhdGFfMjAxOSwgZmFtaWx5ID0gcG9pc3NvbihsaW5rID0gImxvZyIpKQ0KDQojIHN1bW1hcnkobW9kZWxfMmttXzIwMTkpDQpyZXBvcnQ6OnJlcG9ydChtb2RlbF8ya21fMjAxOSkNCg0Kc2pQbG90Ojp0YWJfbW9kZWwobW9kZWxfMmttXzIwMTksIGRpZ2l0cyA9IDMsIGRpZ2l0cy5yZSA9IDMsDQogICAgICAgICAgICAgICAgICBzaG93LnIyID0gRkFMU0UsIHNob3cuYWljID0gVFJVRSkNCg0KbW9kZWxfM2ttXzIwMTkgPC0gZ2xtKG9ic2VydmVkXzIwMTl+MStwcm9wX3BvcF8za20rb2Zmc2V0KGxvZyhleHBlY3RlZF8yMDE5KSksDQogICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IG1vZGVsX2RhdGFfMjAxOSwgZmFtaWx5ID0gcG9pc3NvbihsaW5rID0gImxvZyIpKQ0KDQojIHN1bW1hcnkobW9kZWxfM2ttXzIwMTkpDQpyZXBvcnQ6OnJlcG9ydChtb2RlbF8za21fMjAxOSkNCg0Kc2pQbG90Ojp0YWJfbW9kZWwobW9kZWxfM2ttXzIwMTksIGRpZ2l0cyA9IDMsIGRpZ2l0cy5yZSA9IDMsDQogICAgICAgICAgICAgICAgICBzaG93LnIyID0gRkFMU0UsIHNob3cuYWljID0gVFJVRSkNCg0KIyAyMDIwIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCm1vZGVsXzFrbV8yMDIwIDwtIGdsbShvYnNlcnZlZF8yMDIwfjErcHJvcF9wb3BfMWttK29mZnNldChsb2coZXhwZWN0ZWRfMjAyMCkpLA0KICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBtb2RlbF9kYXRhXzIwMjAsIGZhbWlseSA9IHBvaXNzb24obGluayA9ICJsb2ciKSkNCg0KIyBzdW1tYXJ5KG1vZGVsXzFrbV8yMDIwKQ0KcmVwb3J0OjpyZXBvcnQobW9kZWxfMWttXzIwMjApDQoNCnNqUGxvdDo6dGFiX21vZGVsKG1vZGVsXzFrbV8yMDIwLCBkaWdpdHMgPSAzLCBkaWdpdHMucmUgPSAzLA0KICAgICAgICAgICAgICAgICAgc2hvdy5yMiA9IEZBTFNFLCBzaG93LmFpYyA9IFRSVUUpDQoNCm1vZGVsXzJrbV8yMDIwIDwtIGdsbShvYnNlcnZlZF8yMDIwfjErcHJvcF9wb3BfMmttK29mZnNldChsb2coZXhwZWN0ZWRfMjAyMCkpLA0KICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBtb2RlbF9kYXRhXzIwMjAsIGZhbWlseSA9IHBvaXNzb24obGluayA9ICJsb2ciKSkNCg0KIyBzdW1tYXJ5KG1vZGVsXzJrbV8yMDIwKQ0KcmVwb3J0OjpyZXBvcnQobW9kZWxfMmttXzIwMjApDQoNCnNqUGxvdDo6dGFiX21vZGVsKG1vZGVsXzJrbV8yMDIwLCBkaWdpdHMgPSAzLCBkaWdpdHMucmUgPSAzLA0KICAgICAgICAgICAgICAgICAgc2hvdy5yMiA9IEZBTFNFLCBzaG93LmFpYyA9IFRSVUUpDQoNCm1vZGVsXzNrbV8yMDIwIDwtIGdsbShvYnNlcnZlZF8yMDIwfjErcHJvcF9wb3BfM2ttK29mZnNldChsb2coZXhwZWN0ZWRfMjAyMCkpLA0KICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBtb2RlbF9kYXRhXzIwMjAsIGZhbWlseSA9IHBvaXNzb24obGluayA9ICJsb2ciKSkNCg0KIyBzdW1tYXJ5KG1vZGVsXzNrbV8yMDIwKQ0KcmVwb3J0OjpyZXBvcnQobW9kZWxfM2ttXzIwMjApDQoNCnNqUGxvdDo6dGFiX21vZGVsKG1vZGVsXzNrbV8yMDIwLCBkaWdpdHMgPSAzLCBkaWdpdHMucmUgPSAzLA0KICAgICAgICAgICAgICAgICAgc2hvdy5yMiA9IEZBTFNFLCBzaG93LmFpYyA9IFRSVUUpDQoNCg0KDQpgYGANCg0KSW4gdGhlIHN1bW1hcmllcyBhYm92ZSwgd2UgY2FuIHNlZSB0aGF0IGFsbCAqcCotdmFsdWVzLCBhcmUgbGVzcyB0aGFuIDAuMDAxIChleGNlcHQgMjAyMCBtb2RlbCksIGhlbmNlLCB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGUgKHBlcmNlbnRhZ2Ugb2YgcGVvcGxlIGxpdmluZyBuZWFyIHdhdGVyIGJvZGllcykgaGFzIHNpZ25pZmljYW50IGVmZmVjdCBvbiBkcnkgc2Vhc29uIG1hbGFyaWEgY2FzZXMuIFRoZSBgUmVzaWR1YWwgZGV2aWFuY2VgIGlzIGdyZWF0ZXIgdGhhbiB0aGUgYGRlZ3JlZXMgb2YgZnJlZWRvbWAsIGluZGljYXRpbmcgdGhhdCBvdmVyLWRpc3BlcnNpb24gZXhpc3RzLCBhcyBhbnRpY2lwYXRlZC4gVGhpcyBtZWFucyB0aGF0IHRoZSBlc3RpbWF0ZXMgYXJlIGNvcnJlY3QsIGJ1dCB0aGUgc3RhbmRhcmQgZXJyb3JzIChgU3RkLiBFcnJvcmAgaS5lLiwgc3RhbmRhcmQgZGV2aWF0aW9uKSBhcmUgdW5hY2NvdW50ZWQgZm9yIGJ5IHRoZSBtb2RlbC4NCg0KVGhlIGBOdWxsIGRldmlhbmNlYCBzaG93cyBob3cgd2VsbCB0aGUgcmVzcG9uc2UgdmFyaWFibGUgaXMgcHJlZGljdGVkIGJ5IGEgbW9kZWwgdGhhdCBpbmNsdWRlcyBvbmx5IHRoZSBgaW50ZXJjZXB0YCAoZ3JhbmQgbWVhbikgd2hlcmVhcyByZXNpZHVhbCB3aXRoIHRoZSBpbmNsdXNpb24gb2YgaW5kZXBlbmRlbnQgdmFyaWFibGVzLg0KQWJvdmUsIGZvciBleGFtcGxlIG1vZGVsXzFrbV8yMDE3LCB3ZSBjYW4gc2VlIHRoYXQgdGhlIGFkZGl0aW9uIG9mIDEgKDI1LTI0ID0gMSkgaW5kZXBlbmRlbnQgdmFyaWFibGUgZGVjcmVhc2VkIHRoZSBkZXZpYW5jZSB0byA5NTg3LjkgZnJvbSAxMjc4Ny41LiBUaGUgZ3JlYXRlciBkaWZmZXJlbmNlIGluIHZhbHVlcyBtZWFucyBhIHBvb3IgZml0LiBBbmQgdGhlIGBkaXNwZXJzaW9uIHBhcmFtZXRlcmAgaXMgMzk5LjQ5NTggKDk1ODcuOS8yNCkgd2hpY2ggaXMgbGFyZ2UuIE9uZSBwb3NzaWJpbGl0eSBpcyB0aGF0IHRoZXJlIGFyZSBvdGhlciBpbXBvcnRhbnQgY292YXJpYXRlcyB0aGF0IGNvdWxkIGJlIHVzZWQgdG8gZGVzY3JpYmUgdGhlIGRpZmZlcmVuY2VzIGluIHRoZSBvYnNlcnZlZCBkcnkgc2Vhc29uIG1hbGFyaWEgY2FzZXMuIFdlIGNvbnNpZGVyIG92ZXJkaXNwZXJzaW9uIGFzIGEgcG9zc2libGUgZXhwbGFuYXRpb24gb2YgdGhlIHNpZ25pZmljYW50IGxhY2stb2YtZml0LiBPdmVyLWRpc3BlcnNpb24gc3VnZ2VzdHMgdGhhdCB0aGVyZSBpcyBtb3JlIHZhcmlhdGlvbiBpbiB0aGUgcmVzcG9uc2UgdGhhbiB0aGUgbW9kZWwgaW1wbGllcy4NCg0KIyBBc3Nlc3MgdGhlIHBlcmZvcm1hbmNlIGFuZCBhY2N1cmFjeSBvZiB0aGUgcmVncmVzc2lvbiBtb2RlbC4gDQpIZXJlLCB3ZSBldmFsdWF0ZSBob3cgd2VsbCB0aGUgYFBvaXNzb24gcmVncmVzc2lvbmAgbW9kZWwgaXMgcHJlZGljdGluZyB0aGUgb3V0Y29tZSBvZiBhIG5ldyB0ZXN0IGRhdGEgdGhhdCBoYXZlIG5vdCBiZWVuIHVzZWQgdG8gYnVpbGQgdGhlIG1vZGVsIGkuZSBob3cgY2xvc2UgdGhlIHByZWRpY3Rpb24gaXMgY2xvc2UgdG8gdGhlIHJlYWwgdmFsdWUuDQoNClR3byBpbXBvcnRhbnQgbWV0cmljcyBoYXZlIGJlZW4gdXNlZCB0byBhc3Nlc3MgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSBwcmVkaWN0aXZlIHJlZ3Jlc3Npb24gbW9kZWw6DQoNCmBSb290IE1lYW4gU3F1YXJlZCBFcnJvcmAsIHdoaWNoIG1lYXN1cmVzIHRoZSBtb2RlbCBwcmVkaWN0aW9uIGVycm9yLiBJdCBjb3JyZXNwb25kcyB0byB0aGUgYXZlcmFnZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIG9ic2VydmVkIGtub3duIHZhbHVlcyBvZiB0aGUgb3V0Y29tZSBhbmQgdGhlIHByZWRpY3RlZCB2YWx1ZSBieSB0aGUgbW9kZWwuYFJNU0VgIGlzIGNvbXB1dGVkIGFzIGBSTVNFID0gbWVhbigob2JzZXJ2ZWQgLSBwcmVkaWN0ZWQpXjIpIHw+IHNxcnQoKWAuIFRoZSBsb3dlciB0aGUgYFJNU0VgLCB0aGUgYmV0dGVyIHRoZSBtb2RlbC4NCg0KDQokJFJNU0UgPSBcc3FydCBcZnJhYyB7XFNpZ21hICggeSAtIFxoYXR7eX0gKV4yfXtOfSQkDQoNCmBQc2V1ZG8gUi1zcXVhcmVkIGdvb2RuZXNzLW9mLWZpdGAgbWVhc3VyZSBmb3IgY291bnQgZGF0YSBpbiBQb2lzc29uIHJlZ3Jlc3Npb24gd2hpY2ggaXMgbm9ubGluZWFyIGFuZCBjb21wdXRlZCB1c2luZyBkZXZpYW5jZSBzdGF0aXN0aWNzOg0KDQokJEQ9MuKIkW5pPTF7WWlsb2coWWkvzrxpKeKIkihZaeKIks68aSl9JCQNCndoZXJlDQokJM68aT1leHAozrJeMCvOsl4xWDErLi4uK86yXnBYcCkkJCANCmRlbm90ZXMgdGhlIHByZWRpY3RlZCBtZWFuIGZvciBvYnNlcnZhdGlvbiBpIGJhc2VkIG9uIHRoZSBlc3RpbWF0ZWQgbW9kZWwgcGFyYW1ldGVycy4gQUpLT0VSIChodHRwczovL3N0YXRzLnN0YWNrZXhjaGFuZ2UuY29tL3VzZXJzLzU0MDEzL2Fqa29lciksIEJhc2ljIFItU3F1YXJlZCBpbiBQb2lzc29uIFJlZ3Jlc3Npb24sIFVSTCAodmVyc2lvbjogMjAyMC0wNi0yOCk6IGh0dHBzOi8vc3RhdHMuc3RhY2tleGNoYW5nZS5jb20vcS80NzQ1MDAgDQoNCg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmhlaWdodD0xMSwgZmlnLndpZHRoPTEwfQ0KDQojIFRpZHkgbW9kZWwgYXNzZXNzbWVudCBkYXRhIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KbW9kZWxfYXNzZXNzbWVudF9kYXRhXzIwMTcgPC0gbW9kZWxfZGF0YV8yMDE3IHw+DQogIGRwbHlyOjphc190aWJibGUoKSB8Pg0KICBkcGx5cjo6c2VsZWN0KE5hbWVzLCBvYnNlcnZlZF8yMDE3LCBleHBlY3RlZF8yMDE3LA0KICAgICAgICAgICAgICAgIHByb3BfcG9wXzFrbSwgcHJvcF9wb3BfMmttLCBwcm9wX3BvcF8za20pIA0KDQptb2RlbF9hc3Nlc3NtZW50X2RhdGFfMjAxOCA8LSBtb2RlbF9kYXRhXzIwMTggfD4NCiAgZHBseXI6OmFzX3RpYmJsZSgpIHw+DQogIGRwbHlyOjpzZWxlY3QoTmFtZXMsIG9ic2VydmVkXzIwMTgsIGV4cGVjdGVkXzIwMTgsDQogICAgICAgICAgICAgICAgcHJvcF9wb3BfMWttLCBwcm9wX3BvcF8ya20sIHByb3BfcG9wXzNrbSkgDQoNCg0KbW9kZWxfYXNzZXNzbWVudF9kYXRhXzIwMTkgPC0gbW9kZWxfZGF0YV8yMDE5IHw+DQogIGRwbHlyOjphc190aWJibGUoKSB8Pg0KICBkcGx5cjo6c2VsZWN0KE5hbWVzLCBvYnNlcnZlZF8yMDE5LCBleHBlY3RlZF8yMDE5LA0KICAgICAgICAgICAgICAgIHByb3BfcG9wXzFrbSwgcHJvcF9wb3BfMmttLCBwcm9wX3BvcF8za20pIA0KDQptb2RlbF9hc3Nlc3NtZW50X2RhdGFfMjAyMCA8LSBtb2RlbF9kYXRhXzIwMjAgfD4NCiAgZHBseXI6OmFzX3RpYmJsZSgpIHw+DQogIGRwbHlyOjpzZWxlY3QoTmFtZXMsIG9ic2VydmVkXzIwMjAsIGV4cGVjdGVkXzIwMjAsDQogICAgICAgICAgICAgICAgcHJvcF9wb3BfMWttLCBwcm9wX3BvcF8ya20sIHByb3BfcG9wXzNrbSkgDQoNCiMgVG8gZG86IExPT0NWKExlYXZlIE9uZSBPdXQgQ3Jvc3MtVmFsaWRhdGlvbikgYW5kIGstZm9sZCBDcm9zcyBWYWxpZGF0aW9uDQojIFNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Qgc2V0IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0Kc2V0LnNlZWQoMikgIyBnZW5lcmF0ZSByYW5kb20gbnVtYmVycw0KDQpzcGxpdF8yMDE3IDwtIGNhVG9vbHM6OnNhbXBsZS5zcGxpdChtb2RlbF9hc3Nlc3NtZW50X2RhdGFfMjAxNywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTcGxpdFJhdGlvID0gMC44KSAjIHVzZSA4MCUgb2YgdGhlIGRhdGEgZm9yIHRyYWluaW5nDQoNCnRyYWluXzIwMTcgPC0gc3Vic2V0KG1vZGVsX2Fzc2Vzc21lbnRfZGF0YV8yMDE3LCBzcGxpdCA9ICJUUlVFIikNCg0KdGVzdF8yMDE3IDwtIHN1YnNldChtb2RlbF9hc3Nlc3NtZW50X2RhdGFfMjAxNywgc3BsaXQgPSAiRkFMU0UiKQ0KDQojIEJ1aWxkIG1vZGVsDQptb2RlbF8yMDE3IDwtIGdsbShvYnNlcnZlZF8yMDE3fjErcHJvcF9wb3BfMWttK29mZnNldChsb2coZXhwZWN0ZWRfMjAxNykpLA0KICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbl8yMDE3LCBmYW1pbHkgPSAncG9pc3NvbicpDQoNCnN1bW1hcnkobW9kZWxfMjAxNykNCg0KIyBQcmVkaWN0aW9uDQpwcmVkaWN0ZWRfMjAxNyA8LSBwcmVkaWN0KG1vZGVsXzIwMTcsIHRlc3RfMjAxNywgdHlwZSA9ICJyZXNwb25zZSIpDQoNCiMgQ29tcGFyZSBwcmVkaWN0ZWQgdnMgYWN0dWFsIGRyeSBzZWFzb24gbWFsYXJpYSBjYXNlcw0KcGFyKG1mcm93ID0gYygyLCAxKSkNCg0KIyBwbG90KHRlc3RfMjAxNyRvYnNlcnZlZF8yMDE3LCB0eXBlID0gImIiLCBsdHkgPSAxLjgsIGNvbCA9ICJibHVlIikNCg0KIyBwbG90KHByZWRpY3RlZF8yMDE3LCB0eXBlID0gImIiLCBsdHkgPSAxLjgsIGNvbCA9ICJyZWQiLCBhZGQgPSBUUlVFKQ0KDQpiYXJwbG90KHRlc3RfMjAxNyRvYnNlcnZlZF8yMDE3LCBtYWluID0gIk9ic2VydmVkIG1hbGFyaWEgY2FzZXMiLA0KICAgICAgICB4bGFiID0gIkNhdGNobWVudCBhcmVhIiwgeWxhYiA9ICJPYnNlcnZlZCBtYWxhcmlhIGNhc2VzIikNCg0KYmFycGxvdChwcmVkaWN0ZWRfMjAxNywgbWFpbiA9ICJQcmVkaWN0ZWQgbWFsYXJpYSBjYXNlcyIsDQogICAgICAgIHhsYWIgPSAiQ2F0Y2htZW50IGFyZWEiLCB5bGFiID0gIlByZWRpY3RlZCBtYWxhcmlhIGNhc2VzIikNCg0KcGFyKG1mcm93ID0gYygxLCAxKSkgIyBDcmVhdGUgYSAyIHggMiBwbG90dGluZyBtYXRyaXgNCg0KDQojIFNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Qgc2V0IA0Kc2V0LnNlZWQoMTIzKSAjIGdlbmVyYXRlIGEgc2VxdWVuY2Ugb2YgcmFuZG9tIG51bWJlcnMNCiMgMjAxNyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQp0cmFpbmluZ19zYW1wbGVzXzIwMTcgPC0gbW9kZWxfYXNzZXNzbWVudF9kYXRhXzIwMTckb2JzZXJ2ZWRfMjAxNyB8Pg0KICBjYXJldDo6Y3JlYXRlRGF0YVBhcnRpdGlvbihwID0gMC44LCBsaXN0ID0gRkFMU0UpICMgdXNlIDgwJSBvZiB0aGUgZGF0YSBmb3IgdHJhaW5pbmcNCg0KdHJhaW5fZGF0YV8yMDE3ICA8LSBtb2RlbF9hc3Nlc3NtZW50X2RhdGFfMjAxN1t0cmFpbmluZ19zYW1wbGVzXzIwMTcsIF0NCg0KdGVzdF9kYXRhXzIwMTcgPC0gbW9kZWxfYXNzZXNzbWVudF9kYXRhXzIwMTdbLXRyYWluaW5nX3NhbXBsZXNfMjAxNywgXQ0KDQojIDIwMTggDQp0cmFpbmluZ19zYW1wbGVzXzIwMTggPC0gbW9kZWxfYXNzZXNzbWVudF9kYXRhXzIwMTgkb2JzZXJ2ZWRfMjAxOCB8Pg0KICBjYXJldDo6Y3JlYXRlRGF0YVBhcnRpdGlvbihwID0gMC44LCBsaXN0ID0gRkFMU0UpICMgc2VlID9jcmVhdGVEYXRhUGFydGl0aW9uDQoNCnRyYWluX2RhdGFfMjAxOCA8LSBtb2RlbF9hc3Nlc3NtZW50X2RhdGFfMjAxOFt0cmFpbmluZ19zYW1wbGVzXzIwMTgsIF0NCg0KdGVzdF9kYXRhXzIwMTggPC0gbW9kZWxfYXNzZXNzbWVudF9kYXRhXzIwMThbLXRyYWluaW5nX3NhbXBsZXNfMjAxOCwgXQ0KDQojIDIwMTkgDQp0cmFpbmluZ19zYW1wbGVzXzIwMTkgPC0gbW9kZWxfYXNzZXNzbWVudF9kYXRhXzIwMTkkb2JzZXJ2ZWRfMjAxOSB8Pg0KICBjYXJldDo6Y3JlYXRlRGF0YVBhcnRpdGlvbihwID0gMC44LCBsaXN0ID0gRkFMU0UpDQoNCnRyYWluX2RhdGFfMjAxOSA8LSBtb2RlbF9hc3Nlc3NtZW50X2RhdGFfMjAxOVt0cmFpbmluZ19zYW1wbGVzXzIwMTksIF0NCg0KdGVzdF9kYXRhXzIwMTkgPC0gbW9kZWxfYXNzZXNzbWVudF9kYXRhXzIwMTlbLXRyYWluaW5nX3NhbXBsZXNfMjAxOSwgXQ0KDQojIDIwMjAgDQp0cmFpbmluZ19zYW1wbGVzXzIwMjAgPC0gbW9kZWxfYXNzZXNzbWVudF9kYXRhXzIwMjAkb2JzZXJ2ZWRfMjAyMCB8Pg0KICBjYXJldDo6Y3JlYXRlRGF0YVBhcnRpdGlvbihwID0gMC44LCBsaXN0ID0gRkFMU0UpDQoNCnRyYWluX2RhdGFfMjAyMCA8LSBtb2RlbF9hc3Nlc3NtZW50X2RhdGFfMjAyMFt0cmFpbmluZ19zYW1wbGVzXzIwMjAsIF0NCg0KdGVzdF9kYXRhXzIwMjAgPC0gbW9kZWxfYXNzZXNzbWVudF9kYXRhXzIwMjBbLXRyYWluaW5nX3NhbXBsZXNfMjAyMCwgXQ0KDQojIE1ha2UgcHJlZGljdGlvbnMgdXNpbmcgdGhlIHRlc3QgZGF0YSBpbiBvcmRlciB0byBldmFsdWF0ZSB0aGUgcGVyZm9ybWFuY2UgDQojIG9mIG91ciByZWdyZXNzaW9uIG1vZGVsIGFrYSBnb29kbmVzcy1vZi1maXQuIFRoZSAicmVzcG9uc2UiIHR5cGUgb2YgcHJlZGljdGlvbg0KIyBpcyBvbiB0aGUgc2NhbGUgb2YgdGhlIHJlc3BvbnNlIHZhcmlhYmxlLiBUaHVzIGZvciBhIGRlZmF1bHQgYmlub21pYWwgbW9kZWwgDQojIHRoZSBkZWZhdWx0IHByZWRpY3Rpb25zIGFyZSBvZiBsb2ctb2RkcyAocHJvYmFiaWxpdGllcyBvbiBsb2dpdCBzY2FsZSkgYW5kIA0KIyB0eXBlID0gInJlc3BvbnNlIiBnaXZlcyB0aGUgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMuIA0KIyAyMDE3IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCnByZWRpY3Rpb25zXzFrbV8yMDE3IDwtIG1vZGVsXzFrbV8yMDE3IHw+DQogIHN0YXRzOjpwcmVkaWN0LmdsbSh0ZXN0X2RhdGFfMjAxNywgdHlwZSA9ICJyZXNwb25zZSIpICMgc2VlID9zdGF0czo6cHJlZGljdC5nbG0NCg0KcHJlZGljdGlvbnNfMmttXzIwMTcgPC0gbW9kZWxfMmttXzIwMTcgfD4NCiAgc3RhdHM6OnByZWRpY3QuZ2xtKHRlc3RfZGF0YV8yMDE3LCB0eXBlID0gInJlc3BvbnNlIikNCg0KIyAyMDE4IA0KcHJlZGljdGlvbnNfMWttXzIwMTggPC0gbW9kZWxfMWttXzIwMTggfD4NCiAgc3RhdHM6OnByZWRpY3QuZ2xtKHRlc3RfZGF0YV8yMDE4LCB0eXBlID0gInJlc3BvbnNlIikNCg0KcHJlZGljdGlvbnNfMmttXzIwMTggPC0gbW9kZWxfMmttXzIwMTggfD4NCiAgc3RhdHM6OnByZWRpY3QuZ2xtKHRlc3RfZGF0YV8yMDE4LCB0eXBlID0gInJlc3BvbnNlIikNCg0KIyAyMDE5IA0KcHJlZGljdGlvbnNfMWttXzIwMTkgPC0gbW9kZWxfMWttXzIwMTkgfD4NCiAgc3RhdHM6OnByZWRpY3QuZ2xtKHRlc3RfZGF0YV8yMDE5LCB0eXBlID0gInJlc3BvbnNlIikNCg0KcHJlZGljdGlvbnNfMmttXzIwMTkgPC0gbW9kZWxfMmttXzIwMTkgfD4NCiAgc3RhdHM6OnByZWRpY3QuZ2xtKHRlc3RfZGF0YV8yMDE5LCB0eXBlID0gInJlc3BvbnNlIikNCg0KIyAyMDIwIA0KcHJlZGljdGlvbnNfMWttXzIwMjAgPC0gbW9kZWxfMWttXzIwMjAgfD4NCiAgc3RhdHM6OnByZWRpY3QuZ2xtKHRlc3RfZGF0YV8yMDIwLCB0eXBlID0gInJlc3BvbnNlIikNCg0KcHJlZGljdGlvbnNfMmttXzIwMjAgPC0gbW9kZWxfMmttXzIwMjAgfD4NCiAgc3RhdHM6OnByZWRpY3QuZ2xtKHRlc3RfZGF0YV8yMDIwLCB0eXBlID0gInJlc3BvbnNlIikNCg0KIyBNb2RlbCBwZXJmb3JtYW5jZSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgKGEpIENvbXB1dGUgdGhlIHByZWRpY3Rpb24gZXJyb3IsIFJNU0UuIFRoZSBsb3dlciB0aGUgUk1TRSwgdGhlIGJldHRlciB0aGUgbW9kZWwNCiMgMjAxNw0KY2FyZXQ6OlJNU0UocHJlZGljdGlvbnNfMWttXzIwMTcsIHRlc3RfZGF0YV8yMDE3JG9ic2VydmVkXzIwMTcpDQoNCmNhcmV0OjpSTVNFKHByZWRpY3Rpb25zXzJrbV8yMDE3LCB0ZXN0X2RhdGFfMjAxNyRvYnNlcnZlZF8yMDE3KQ0KDQojIDIwMTggDQpjYXJldDo6Uk1TRShwcmVkaWN0aW9uc18xa21fMjAxOCwgdGVzdF9kYXRhXzIwMTgkb2JzZXJ2ZWRfMjAxOCkNCg0KY2FyZXQ6OlJNU0UocHJlZGljdGlvbnNfMmttXzIwMTgsIHRlc3RfZGF0YV8yMDE4JG9ic2VydmVkXzIwMTgpDQoNCiMgMjAxOSANCmNhcmV0OjpSTVNFKHByZWRpY3Rpb25zXzFrbV8yMDE5LCB0ZXN0X2RhdGFfMjAxOSRvYnNlcnZlZF8yMDE5KQ0KDQpjYXJldDo6Uk1TRShwcmVkaWN0aW9uc18ya21fMjAxOSwgdGVzdF9kYXRhXzIwMTkkb2JzZXJ2ZWRfMjAxOSkNCg0KIyAyMDIwIA0KY2FyZXQ6OlJNU0UocHJlZGljdGlvbnNfMWttXzIwMjAsIHRlc3RfZGF0YV8yMDIwJG9ic2VydmVkXzIwMjApDQoNCmNhcmV0OjpSTVNFKHByZWRpY3Rpb25zXzJrbV8yMDIwLCB0ZXN0X2RhdGFfMjAyMCRvYnNlcnZlZF8yMDIwKQ0KDQojIChiKSBDb21wdXRlIHBzZXVkbyBSLXNxdWFyZSANCiMgV2UgZXhwcmVzcyB0aGUgZ29vZG5lc3Mgb2YgZml0IG9mIHRoZSBQb2lzc29uIHJlZ3Jlc3Npb24gbW9kZWwgYnkgd2lkZWx5IA0KIyB1c2VkIHZhcmlhbnRzIG9mIHBzZXVkbyBSIHNxdWFyZWQgc3RhdGlzdGljcywgbW9zdCBvZiB3aGljaCBhcmUgYmFzZWQgb24gDQojIHRoZSBkZXZpYW5jZSBvZiB0aGUgbW9kZWw6IA0KIyAgICAtIHRoZSBBbGRyaWNoLU5lbHNvbiBwc2V1ZG8tUjIgd2l0aCB0aGUgVmVhbGwtWmltbWVybWFubiBjb3JyZWN0aW9uLCB3aGljaA0KIyAgICAgIGlzIHRoZSBiZXN0IGFwcHJveGltYXRpb24gb2YgdGhlIE1jS2VsdmV5LVphdm9pbmEuDQojIEVmcm9uLCBBbGRyaWNoLU5lbHNvbiwgTWNGYWRkZW4gYW5kIE5hZ2Vsa2Vya2UgYXBwcm9hY2hlcyBzZXZlcmVseSB1bmRlcmVzdGltYXRlIA0KIyB0aGUgInRydWUgUjIiLiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIERlc2NUb29sczo6UHNldWRvUjIobW9kZWxfMWttXzIwMTcsICJhbGwiKQ0KDQpyb3VuZChEZXNjVG9vbHM6OlBzZXVkb1IyKG1vZGVsXzFrbV8yMDE3LCBjKCJBbGRyaWNoTmVsc29uIiwgIkFJQyIsICJMb2dMaWtOdWxsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxvZ0xpayIsICJHMiIsICJWZWFsbFppbW1lcm1hbm4iKSksIDIpDQoNCnJvdW5kKERlc2NUb29sczo6UHNldWRvUjIobW9kZWxfMmttXzIwMTcsIGMoIkFsZHJpY2hOZWxzb24iLCAiQUlDIiwgIkxvZ0xpa051bGwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTG9nTGlrIiwgIkcyIiwgIlZlYWxsWmltbWVybWFubiIpKSwgMikNCg0Kcm91bmQoRGVzY1Rvb2xzOjpQc2V1ZG9SMihtb2RlbF8xa21fMjAxOCwgYygiQWxkcmljaE5lbHNvbiIsICJBSUMiLCAiTG9nTGlrTnVsbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMb2dMaWsiLCAiRzIiLCAiVmVhbGxaaW1tZXJtYW5uIikpLCAyKQ0KDQpyb3VuZChEZXNjVG9vbHM6OlBzZXVkb1IyKG1vZGVsXzJrbV8yMDE4LCBjKCJBbGRyaWNoTmVsc29uIiwgIkFJQyIsICJMb2dMaWtOdWxsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxvZ0xpayIsICJHMiIsICJWZWFsbFppbW1lcm1hbm4iKSksIDIpDQoNCnJvdW5kKERlc2NUb29sczo6UHNldWRvUjIobW9kZWxfMWttXzIwMTksIGMoIkFsZHJpY2hOZWxzb24iLCAiQUlDIiwgIkxvZ0xpa051bGwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTG9nTGlrIiwgIkcyIiwgIlZlYWxsWmltbWVybWFubiIpKSwgMikNCg0Kcm91bmQoRGVzY1Rvb2xzOjpQc2V1ZG9SMihtb2RlbF8ya21fMjAxOSwgYygiQWxkcmljaE5lbHNvbiIsICJBSUMiLCAiTG9nTGlrTnVsbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMb2dMaWsiLCAiRzIiLCAiVmVhbGxaaW1tZXJtYW5uIikpLCAyKQ0KDQpyb3VuZChEZXNjVG9vbHM6OlBzZXVkb1IyKG1vZGVsXzFrbV8yMDIwLCBjKCJBbGRyaWNoTmVsc29uIiwgIkFJQyIsICJMb2dMaWtOdWxsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxvZ0xpayIsICJHMiIsICJWZWFsbFppbW1lcm1hbm4iKSksIDIpDQoNCnJvdW5kKERlc2NUb29sczo6UHNldWRvUjIobW9kZWxfMmttXzIwMjAsIGMoIkFsZHJpY2hOZWxzb24iLCAiQUlDIiwgIkxvZ0xpa051bGwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTG9nTGlrIiwgIkcyIiwgIlZlYWxsWmltbWVybWFubiIpKSwgMikNCg0KDQpgYGANCg0KIyMjIENoZWNrIGZvciBjb2xsaW5lYXJpdHksIG5vcm1hbGl0eSBvciBoZXRlcm9zY2VkYXN0aWNpdHkNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuaGVpZ2h0PTE3LCBmaWcud2lkdGg9MTZ9DQojIENoZWNrIGZvciBjb2xsaW5lYXJpdHksIG5vcm1hbGl0eSBvciBoZXRlcm9zY2VkYXN0aWNpdHkgLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KcGVyZm9ybWFuY2U6OmNoZWNrX21vZGVsKG1vZGVsXzFrbV8yMDE3LCB0aGVtZSA9ICJzZWU6OnRoZW1lX21vZGVybiIpDQoNCnBlcmZvcm1hbmNlOjpjaGVja19tb2RlbChtb2RlbF8ya21fMjAxNywgdGhlbWUgPSAic2VlOjp0aGVtZV9tb2Rlcm4iKQ0KDQpwZXJmb3JtYW5jZTo6Y2hlY2tfbW9kZWwobW9kZWxfMWttXzIwMTgsIHRoZW1lID0gInNlZTo6dGhlbWVfbW9kZXJuIikNCg0KcGVyZm9ybWFuY2U6OmNoZWNrX21vZGVsKG1vZGVsXzJrbV8yMDE4LCB0aGVtZSA9ICJzZWU6OnRoZW1lX21vZGVybiIpDQoNCnBlcmZvcm1hbmNlOjpjaGVja19tb2RlbChtb2RlbF8xa21fMjAxOSwgdGhlbWUgPSAic2VlOjp0aGVtZV9tb2Rlcm4iKQ0KDQpwZXJmb3JtYW5jZTo6Y2hlY2tfbW9kZWwobW9kZWxfMmttXzIwMTksIHRoZW1lID0gInNlZTo6dGhlbWVfbW9kZXJuIikNCg0KcGVyZm9ybWFuY2U6OmNoZWNrX21vZGVsKG1vZGVsXzFrbV8yMDIwLCB0aGVtZSA9ICJzZWU6OnRoZW1lX21vZGVybiIpDQoNCnBlcmZvcm1hbmNlOjpjaGVja19tb2RlbChtb2RlbF8ya21fMjAyMCwgdGhlbWUgPSAic2VlOjp0aGVtZV9tb2Rlcm4iKQ0KDQojIE1vZGVsIHBlcmZvcm1hbmNlIHN1bW1hcmllcyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBDb21wdXRlIGluZGljZXMgb2YgbW9kZWwgcGVyZm9ybWFuY2UgZm9yIHRoZSByZWdyZXNzaW9uIG1vZGVscyBhbmQgDQojIGNvbXBhcmUgdGhlIHF1YWxpdHkgb2YgdGhlIG1vZGVscy4NCiMgTm90ZSB0aGF0IGFsbCBzY29yZSB2YWx1ZSBkbyBub3QgbmVjZXNzYXJpbHkgc3VtIHVwIHRvIDEwMCUuIFNlZSA/Y29tcGFyZV9wZXJmb3JtYW5jZQ0KbW9kZWxfY29tcGFyaXNvbiA8LSBwZXJmb3JtYW5jZTo6Y29tcGFyZV9wZXJmb3JtYW5jZShtb2RlbF8xa21fMjAxNywgbW9kZWxfMmttXzIwMTcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsXzFrbV8yMDE4LCBtb2RlbF8ya21fMjAxOCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsXzFrbV8yMDE5LCBtb2RlbF8ya21fMjAxOSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfMWttXzIwMjAsIG1vZGVsXzJrbV8yMDIwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0cmljcyA9ICJjb21tb24iLCByYW5rID0gVFJVRSkNCg0KbW9kZWxfY29tcGFyaXNvbg0KYGBgDQoNCg0KIyMjIENvbWFwcmlzb24gb2YgbW9kZWwgaW5kaWNlcw0KDQpUaGUgOCBtb2RlbHMgYXJlIHJhbmtlZCBvbiBlYWNoIG9mIHRoZSBmb3VyIHBhcmFtZXRlcnM6IGBBSUNgLCBgQklDYCwgYFJNU0VgIGFuZCBhZGp1c3RlZCBgUi1zcXVhcmVkYC4gVGhlIHJhZGFyIGNoYXJ0IGdpdmVzIGFuIGluZGljYXRpb24gb2Ygd2hpY2ggbW9kZWwgcGVyZm9ybWVkIHdlbGwgb3IgcG9vcmx5IGFnYWluc3QgZWFjaCBwYXJhbWV0ZXIuIEZvciBleGFtcGxlLCBgbW9kZWxfMWttXzIwMjBgIGFuZCBgbW9kZWxfMmttXzIwMjBgIGhhdmUgc2lnbmlmaWNhbnRseSB0aGUgbG93ZXN0IHJhbmtpbmcgZm9yIGFsbCB0aGUgcGFyYW1ldGVycyB3aGVuIGNvbXBhcmVkIHRvIHRoZSBvdGhlciBtb2RlbHMgKGBtb2RlbF8xa21fMjAxN2AsIGBtb2RlbF8ya21fMjAxN2AsIGBtb2RlbF8xa21fMjAxOGAsIGBtb2RlbF8xa21fMjAxOGAsIGBtb2RlbF8xa21fMjAxOWAsIGBtb2RlbF8ya21fMjAxOWApLCB3aGljaCBoYXZlIGhpZ2hlciByYW5raW5nIGluIGBBSUNgLCBgQklDYCwgYFJNU0VgIGFuZCBgUi1zcXVhcmVkYC4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD0xMX0NCnBsb3QocGVyZm9ybWFuY2U6OmNvbXBhcmVfcGVyZm9ybWFuY2UobW9kZWxfMWttXzIwMTcsIG1vZGVsXzJrbV8yMDE3LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbF8xa21fMjAxOCwgbW9kZWxfMmttXzIwMTgsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbF8xa21fMjAxOSwgbW9kZWxfMmttXzIwMTksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsXzFrbV8yMDIwLCBtb2RlbF8ya21fMjAyMCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldHJpY3MgPSAiY29tbW9uIiwgcmFuayA9IFRSVUUpKQ0KYGBgDQoNCg0KIyMjIENoZWNrIGhvdyB3ZWxsIHRoZSBmaXR0ZWQgdmFsdWVzIGxpbmUgdXAgd2l0aCB0aGUgb2JzZXJ2YXRpb25zDQoNClRoZSBmaXR0ZWQgdmFsdWVzIGFwcGVhciB0byBsaW5lIHVwIHBhcnRpY3VsYXJseSB3ZWxsIHdpdGggdGhlIG9ic2VydmVkIGRhdGEsIHN1Z2dlc3RpbmcgdGhhdCBgcHJvcF9wb3BfKmAgKGkuZS4sIHByb3BvcnRpb24gb2YgY2F0Y2htZW50IHBvcHVsYXRpb24gbGl2aW5nIG5lYXIgd2F0ZXIgYm9kaWVzKSBjYW4gaGVscCB1cyB1bmRlcnN0YW5kIG1hbGFyaWEgcmlzayBpbiB0aGUgY2F0Y2htZW50IGFyZWFzLg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgZmlnLmhlaWdodD0xNSwgZmlnLndpZHRoPTksIGZpZy5jYXA9J0ZpZy4gMTIuIEhvdyB3ZWxsIHRoZSBwZXJjZW50YWdlIG9mIGNhdGNobWVudCBwb3B1bGF0aW9uIGxpdmluZyBhcm91bmQgd2F0ZXIgYm9kaWVzIGV4cGxhaW4gb2JzZXJ2ZWQgbWFsYXJpYSBpbmNpZGVuY2UnfQ0KIyBIZWxwZXIgZnVuY3Rpb24gdG8gY3JlYXRlIHNjYXR0ZXIgcGxvdHMgdG8gc2VlIGhvdyB3ZWxsIA0KIyBmaXR0ZWQgdmFsdWVzIGxpbmUgdXAgd2l0aCBvYnNlcnZlZCBtYWxhcmlhIGNhc2VzDQpwbG90LmZpdHRlZC52YWx1ZXMgPC0gZnVuY3Rpb24oZml0dGVkLnZhbHVlcy5kZiwgbW9kZWwuZGYsIHRpdGxlKXsNCiAgDQogICMgUmVtb3ZlIG1pc3NpbmcgdmFsdWVzIGZyb20gbW9kZWwgZGF0YSBzaW5jZSANCiAgIyBtb2RlbCBmaXR0aW5nIGRlbGV0ZXMgbWlzc2luZyBvYnNlcnZhdGlvbnMNCiAgbW9kZWwuZGYuY29tcGxldGUgPC0gbW9kZWwuZGYgfD4gDQogICAgdGlkeXI6OmRyb3BfbmEoKSB8PiAgDQogICAgZHBseXI6OnJlbmFtZV9hdCh2YXJzKHN0YXJ0c193aXRoKCJvYnNlcnZlZF8iKSksIH4gc3RyX2MoIm9ic2VydmVkIikpDQoNCiAgIyBQbG90IGZpdHRlZCB2ZXJzdXMgb2JzZXJ2ZWQgdmFsdWVzDQogIHNjYXR0ZXIucGxvdCA8LSBnZ3Bsb3QyOjpnZ3Bsb3QoKSsgDQogICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjpnZW9tX3BvaW50KGFlcyhmaXR0ZWQudmFsdWVzLmRmJGZpdHRlZC52YWx1ZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbC5kZi5jb21wbGV0ZSRvYnNlcnZlZCkpKw0KICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYygpKw0KICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6bGFicyh4ID0gIkZpdHRlZCB2YWx1ZXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gIk9ic2VydmVkIHZhbHVlcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gdGl0bGUpDQogIHJldHVybihzY2F0dGVyLnBsb3QpDQp9DQoNCiMgSW52b2tpbmcgZnVuY3Rpb24gDQojIDIwMTcgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KZml0dGVkXzFrbV8yMDE3IDwtIHBsb3QuZml0dGVkLnZhbHVlcyhtb2RlbF8xa21fMjAxNywgbW9kZWxfZGF0YV8yMDE3LCAiMjAxNzogMWttIG1vZGVsIikNCg0KZml0dGVkXzJrbV8yMDE3IDwtIHBsb3QuZml0dGVkLnZhbHVlcyhtb2RlbF8ya21fMjAxNywgbW9kZWxfZGF0YV8yMDE3LCAiMjAxNzogMmttIG1vZGVsIikNCg0KZml0dGVkXzNrbV8yMDE3IDwtIHBsb3QuZml0dGVkLnZhbHVlcyhtb2RlbF8za21fMjAxNywgbW9kZWxfZGF0YV8yMDE3LCAiMjAxNzogM2ttIG1vZGVsIikNCg0KIyAyMDE4IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmZpdHRlZF8xa21fMjAxOCA8LSBwbG90LmZpdHRlZC52YWx1ZXMobW9kZWxfMWttXzIwMTgsIG1vZGVsX2RhdGFfMjAxOCwgIjIwMTg6IDFrbSBtb2RlbCIpDQoNCmZpdHRlZF8ya21fMjAxOCA8LSBwbG90LmZpdHRlZC52YWx1ZXMobW9kZWxfMmttXzIwMTgsIG1vZGVsX2RhdGFfMjAxOCwgIjIwMTg6IDJrbSBtb2RlbCIpDQoNCmZpdHRlZF8za21fMjAxOCA8LSBwbG90LmZpdHRlZC52YWx1ZXMobW9kZWxfM2ttXzIwMTgsIG1vZGVsX2RhdGFfMjAxOCwgIjIwMTg6IDNrbSBtb2RlbCIpDQoNCiMgMjAxOSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpmaXR0ZWRfMWttXzIwMTkgPC0gcGxvdC5maXR0ZWQudmFsdWVzKG1vZGVsXzFrbV8yMDE5LCBtb2RlbF9kYXRhXzIwMTksICIyMDE5OiAxa20gbW9kZWwiKQ0KDQpmaXR0ZWRfMmttXzIwMTkgPC0gcGxvdC5maXR0ZWQudmFsdWVzKG1vZGVsXzJrbV8yMDE5LCBtb2RlbF9kYXRhXzIwMTksICIyMDE5OiAya20gbW9kZWwiKQ0KDQpmaXR0ZWRfM2ttXzIwMTkgPC0gcGxvdC5maXR0ZWQudmFsdWVzKG1vZGVsXzNrbV8yMDE5LCBtb2RlbF9kYXRhXzIwMjAsICIyMDE5OiAza20gbW9kZWwiKQ0KDQojIDIwMjAgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KZml0dGVkXzFrbV8yMDIwIDwtIHBsb3QuZml0dGVkLnZhbHVlcyhtb2RlbF8xa21fMjAyMCwgbW9kZWxfZGF0YV8yMDIwLCAiMjAyMDogMWttIG1vZGVsIikNCg0KZml0dGVkXzJrbV8yMDIwIDwtIHBsb3QuZml0dGVkLnZhbHVlcyhtb2RlbF8ya21fMjAyMCwgbW9kZWxfZGF0YV8yMDIwLCAiMjAyMDogMmttIG1vZGVsIikNCg0KZml0dGVkXzNrbV8yMDIwIDwtIHBsb3QuZml0dGVkLnZhbHVlcyhtb2RlbF8za21fMjAyMCwgbW9kZWxfZGF0YV8yMDIwLCAiMjAyMDogM2ttIG1vZGVsIikNCg0KIyBMYXlvdXQgc2NhdHRlciBwbG90cyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNvd3Bsb3Q6OnBsb3RfZ3JpZChmaXR0ZWRfMWttXzIwMTcsIGZpdHRlZF8ya21fMjAxNywgDQogICAgICAgICAgICAgICAgICAgZml0dGVkXzFrbV8yMDE4LCBmaXR0ZWRfMmttXzIwMTgsDQogICAgICAgICAgICAgICAgICAgZml0dGVkXzFrbV8yMDE5LCBmaXR0ZWRfMmttXzIwMTksIA0KICAgICAgICAgICAgICAgICAgIGZpdHRlZF8xa21fMjAyMCwgZml0dGVkXzJrbV8yMDIwLA0KICAgICAgICAgICAgICAgICAgIG5jb2wgPSAyLCBucm93ID0gNCkNCg0KYGBgDQoNCiMjIFRlc3QgZm9yIHJlc2lkdWFsIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uIHVzaW5nIGFkamFjZW5jeSBhcyBjcml0ZXJpb24NCg0KVG8gdW5kZXJzdGFuZCB3aGV0aGVyIHNpbWlsYXJpdHkgYmV0d2VlbiBtYWxhcmlhIGNhc2VzIGluIGNhdGNobWVudCBhcmVhcyBpcyBhIGZ1bmN0aW9uIG9mIHRoZSBkaXN0YW5jZSBiZXR3ZWVuIHRoZW0gb3Igbm90LCB3ZSBpbmNvcG9yYXRlIGEgc3BhdGlhbCBkZXBlbmRlbmN5IGVmZmVjdCBpbnRvIHRoZSBtdWx0aXZhcmlhdGUgbW9kZWwgYW5kIHRlc3QgdGVzdCBmb3Igc3BhdGlhbCBhdXRvY29ycmVsYXRpb24uIA0KDQpgYGB7ciwgZmlnLmtlZXA9J2FsbCcsIGZpZy5jYXA9J0ZpZy4gMTMuIE5laWdoYm91cmhvb2QgbWF0cml4J30NCiMgUHJlcCBkYXRhDQpwcmVwLnNwYXRpYWwuZGVwZW5kZW5jeS5kYXRhIDwtIGZ1bmN0aW9uKHNmXzIwMTcsIHNmXzIwMTgsIHNmXzIwMTksIHNmXzIwMjApew0KICANCiAgZGZfMjAxNyA8LSBzZl8yMDE3IHw+DQogICAgZHBseXI6OmFzX3RpYmJsZSgpIHw+IA0KICAgIGRwbHlyOjpyZW5hbWUob2JzZXJ2ZWRfMjAxOCA9IGRyXzIwMTgsDQogICAgICAgICAgICAgICAgICBvYnNlcnZlZF8yMDE5ID0gZHJfMjAxOSwNCiAgICAgICAgICAgICAgICAgIG9ic2VydmVkXzIwMjAgPSBkcl8yMDIwLA0KICAgICAgICAgICAgICAgICAgU01SXzIwMTcgPSBTTVIpIHw+IA0KICAgIGRwbHlyOjpzZWxlY3Qocm93SUQsIE5hbWVzLCBTTVJfMjAxNywgZ2VvbWV0cnkpDQogIA0KICBkZl8yMDE4IDwtIHNmXzIwMTggfD4gDQogICAgZHBseXI6OmFzX3RpYmJsZSgpIHw+IA0KICAgIGRwbHlyOjpyZW5hbWUoU01SXzIwMTggPSBTTVIpIHw+IA0KICAgIGRwbHlyOjpzZWxlY3Qocm93SUQsIFNNUl8yMDE4KQ0KICANCiAgZGZfMjAxOSA8LSBzZl8yMDE5IHw+IA0KICAgIGRwbHlyOjphc190aWJibGUoKSB8PiANCiAgICBkcGx5cjo6cmVuYW1lKFNNUl8yMDE5ID0gU01SKSB8PiANCiAgICBkcGx5cjo6c2VsZWN0KHJvd0lELCBTTVJfMjAxOSkNCiAgDQogIGRmXzIwMjAgPC0gc2ZfMjAyMCB8PiANCiAgICBkcGx5cjo6YXNfdGliYmxlKCkgfD4gDQogICAgZHBseXI6OnJlbmFtZShTTVJfMjAyMCA9IFNNUikgfD4NCiAgICBkcGx5cjo6c2VsZWN0KHJvd0lELCBTTVJfMjAyMCkNCiAgICANCiAgDQogIHNwYXRpYWxfZGVwZW5kZW5jeV9kYXRhIDwtIG1lcmdlKA0KICAgIG1lcmdlKA0KICAgICAgbWVyZ2UoDQogICAgICAgIGRmXzIwMTcsIGRmXzIwMTgsIGJ5ID0gInJvd0lEIiwgYWxsID0gVFJVRSksDQogICAgICAgIGRmXzIwMTksIGJ5ID0gInJvd0lEIiwgYWxsID0gVFJVRSksDQogICAgICAgIGRmXzIwMjAsIGJ5ID0gInJvd0lEIiwgYWxsID0gVFJVRSkNCiAgDQogIHJldHVybihzcGF0aWFsX2RlcGVuZGVuY3lfZGF0YSkNCiAgDQp9DQoNCiMgSW52b2tpbmcgZnVuY3Rpb24gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpzcGF0aWFsX2RlcGVuZGVuY3lfZGF0YSA8LSBwcmVwLnNwYXRpYWwuZGVwZW5kZW5jeS5kYXRhKG1vZGVsX2RhdGFfMjAxNywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfZGF0YV8yMDE4LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfZGF0YV8yMDE5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbF9kYXRhXzIwMjApDQoNCiMgRmluZCBhZGphY2VudCBwb2x5Z29ucyBpLmUuLCBtYWtlIG5laWdiaG91ciBsaXN0LA0KIyBDb250aWd1aXR5IG5laWdoYm9ycyAtIGFsbCB0aGF0IHNoYXJlIGEgYm91bmRhcnkgcG9pbnQNCnNwYXRpYWxfZGVwZW5kZW5jeV9zaHAgPC0gc2Y6OnN0X2FzX3NmKHNwYXRpYWxfZGVwZW5kZW5jeV9kYXRhKSB8PiANCiAgYXMoIlNwYXRpYWwiKQ0KDQpjYXRjaG1lbnRfbmVpZ2hib3VycyA8LSBzcGRlcDo6cG9seTJuYihzcGF0aWFsX2RlcGVuZGVuY3lfc2hwKSAgIyBRdWVlbiBjb250aWd1aXR5DQoNCnN1bW1hcnkoY2F0Y2htZW50X25laWdoYm91cnMpDQoNCiMgR2V0IGNvb3JkaW5hdGVzIGZyb20gY2F0Y2htZW50IHBvbHlnb25zDQojIEdldCBjZW50ZXIgcG9pbnRzIG9mIGVhY2ggY2F0Y2htZW50IGFyZWENCg0KY29vcmRzIDwtIGNvb3JkaW5hdGVzKHNwYXRpYWxfZGVwZW5kZW5jeV9zaHApDQoNCg0KIyBWaWV3IHRoZSBjb25uZWN0aW9ucw0Ke3Bsb3Qoc3BhdGlhbF9kZXBlbmRlbmN5X3NocCwgYXNwID0gMSkrDQpwbG90KGNhdGNobWVudF9uZWlnaGJvdXJzLCBjb29yZHMsIGNvbCA9ICJibHVlIiwgYWRkID0gVFJVRSl9DQoNCiMgUnVuIGEgTW9yYW4gSSB0ZXN0IG9uIFNNUg0KbW9yYW4udGVzdChzcGF0aWFsX2RlcGVuZGVuY3lfZGF0YSRTTVJfMjAxNywgDQogICAgICAgICAgIG5iMmxpc3R3KGNhdGNobWVudF9uZWlnaGJvdXJzKSkNCg0KbW9yYW4udGVzdChzcGF0aWFsX2RlcGVuZGVuY3lfZGF0YSRTTVJfMjAxOCwgDQogICAgICAgICAgIG5iMmxpc3R3KGNhdGNobWVudF9uZWlnaGJvdXJzKSkNCg0KbW9yYW4udGVzdChzcGF0aWFsX2RlcGVuZGVuY3lfZGF0YSRTTVJfMjAxOSwgDQogICAgICAgICAgIG5iMmxpc3R3KGNhdGNobWVudF9uZWlnaGJvdXJzKSkNCg0KbW9yYW4udGVzdChzcGF0aWFsX2RlcGVuZGVuY3lfZGF0YSRTTVJfMjAyMCwgDQogICAgICAgICAgIG5iMmxpc3R3KGNhdGNobWVudF9uZWlnaGJvdXJzKSkNCg0KIyBSdW4gYSBNb3JhbiBJIE1DIHRlc3Qgb24gU01SDQptb3Jhbi5tYyhzcGF0aWFsX2RlcGVuZGVuY3lfZGF0YSRTTVJfMjAxNywgDQogICAgICAgICBuYjJsaXN0dyhjYXRjaG1lbnRfbmVpZ2hib3VycyksIA0KICAgICAgICAgbnNpbSA9IDk5OSkNCg0KbW9yYW4ubWMoc3BhdGlhbF9kZXBlbmRlbmN5X2RhdGEkU01SXzIwMTgsIA0KICAgICAgICAgbmIybGlzdHcoY2F0Y2htZW50X25laWdoYm91cnMpLCANCiAgICAgICAgIG5zaW0gPSA5OTkpDQoNCm1vcmFuLm1jKHNwYXRpYWxfZGVwZW5kZW5jeV9kYXRhJFNNUl8yMDE5LCANCiAgICAgICAgIG5iMmxpc3R3KGNhdGNobWVudF9uZWlnaGJvdXJzKSwgDQogICAgICAgICBuc2ltID0gOTk5KQ0KDQptb3Jhbi5tYyhzcGF0aWFsX2RlcGVuZGVuY3lfZGF0YSRTTVJfMjAyMCwgDQogICAgICAgICBuYjJsaXN0dyhjYXRjaG1lbnRfbmVpZ2hib3VycyksIA0KICAgICAgICAgbnNpbSA9IDk5OSkNCg0KIyBSdW4gYSBDb25kaXRpb25hbCBBdXRvcmVncmVzc2l2ZSAoQ0FSKSBtb2RlbCwgd2hpY2ggYWxsb3dzIHVzIHRvIGluY29ycG9yYXRlIA0KIyB0aGUgc3BhdGlhbCBhdXRvY29ycmVsYXRpb24gYmV0d2VlbiBuZWlnaGJvdXJzIHdpdGhpbiBvdXIgR0xNDQoNCiMgRmlyc3QsIGdlbmVyYXRlIGEgd2VpZ2h0cyBtYXRyaXggZnJvbSBhIG5laWdoYm91cnMgbGlzdCB3aXRoIHNwYXRpYWwgd2VpZ2h0cw0KYWRqX21hdHJpeCA8LSBzcGRlcDo6bmIybWF0KGNhdGNobWVudF9uZWlnaGJvdXJzLCBzdHlsZSA9ICJCIikgIyBzZWUgP25iMm1hdA0KDQojIE1hdGNoIHJvdyBhbmQgY29sdW1uIG5hbWVzIHdpdGggdGhvc2Ugb2YgZ2VvZ3JhcGhpYyBsb2NhdGlvbiBpbmRleCANCnJvd25hbWVzKGFkal9tYXRyaXgpIDwtIGNvbG5hbWVzKGFkal9tYXRyaXgpIDwtIHNwYXRpYWxfZGVwZW5kZW5jeV9kYXRhJHJvd0lEDQojIHJvdy5uYW1lcyhhZGpfbWF0cml4KSA8LSBOVUxMICMgYWx0ZXJuYXRpdmVseQ0KDQojIE5vdyB3ZSBjYW4gZml0IHRoZSBtb2RlbC4gVGhlIHNwYXRpYWwgZWZmZWN0IGlzIGNhbGxlZCB1c2luZyB0aGUgYWRqYWNlbmN5IGZ1bmN0aW9uIHdoaWNoIA0KIyByZXF1aXJlcyB0aGUgZ3JvdXBpbmcgZmFjdG9yIChpLmUuIHRoZSByb3dJRCBvZiBlYWNoIGNhdGNobWVudCBhcmVhKQ0KDQpDQVJfbW9kZWxfMWttXzIwMTcgPC0gc3BhTU06OmZpdG1lKG9ic2VydmVkXzIwMTd+cHJvcF9wb3BfMWttK29mZnNldChsb2coZXhwZWN0ZWRfMjAxNykpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZGpNYXRyaXggPSBhZGpfbWF0cml4LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IG1vZGVsX2RhdGFfMjAxNywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICdwb2lzc29uJykNCg0KIyBHZW5lcmF0ZSA5NSUgQ0kNCmNvZWZzIDwtIGFzLmRhdGEuZnJhbWUoc3VtbWFyeShDQVJfbW9kZWxfMWttXzIwMTcpJGJldGFfdGFibGUpDQoNCg0KIyBNb3JhbidzIEkgY29udGlndWl0eSB0ZXN0DQpNSV8yMDE3IDwtIHNwZGVwOjptb3Jhbihtb2RlbF9kYXRhXzIwMTckb2JzZXJ2ZWRfMjAxNywgDQogICAgICAgICAgICAgICAgICAgICAgICBuYjJsaXN0dyhjYXRjaG1lbnRfbmVpZ2hib3VycyksDQogICAgICAgICAgICAgICAgICAgICAgICBsZW5ndGgobW9kZWxfZGF0YV8yMDE3JG9ic2VydmVkXzIwMTcpLA0KICAgICAgICAgICAgICAgICAgICAgICAgU3plcm8obmIybGlzdHcoY2F0Y2htZW50X25laWdoYm91cnMpKSkNCg0KDQoNCmBgYA0KDQoNCiMgWWVhcmx5IHZhcmlhdGlvbiBpbiBvYnNlcnZlZCBtYWxhcmlhIGNhc2VzIGFzIGEgcmlzayBmYWN0b3INCg0KVGhlIGZpbmRpbmdzIGZyb20gdGhlIHVuaXZhcmlhdGUgbW9kZWwgYWJvdmUgc3VnZ2VzdCB0aGUgcmlzayBvZiBkcnkgc2Vhc29uIG1hbGFyaWEgdHJhbnNtaXNzaW9uIHZhcmllcyBkZXBlbmRpbmcgb24gdGhlIHllYXIsIHNvIHdlIGNvbnNpZGVyIGEgbW9kZWwgd2l0aCBhbiBpbnRlcmFjdGlvbiBiZXR3ZWVuIHByb3BvcnRpb24gb2YgcGVvcGxlIGNsb3NlIHRvIGRhbXMgYW5kIHllYXIuIFdlIHdvdWxkIGxpa2UgdG8gZXhwbGFpbiBkcnkgc2Vhc29uIG1hbGFyaWEgcmlzayBiYXNlZCBvbiB0aGUgbnVtYmVyIG9mIHBlb3BsZSBsaXZpbmcgY2xvc2UgdG8gZGFtcyBhbmQgdGhlIHllYXIuDQoNCldlIGFsc28gY29tcGFyZSB0aGUgZWZmZWN0IG9mIHJlbW92aW5nIGludGVyY2VwdCBmcm9tIHRoZSBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbi4gTWF0aGVtYXRpY2FsbHksICTwnZy3XzAgPSAwJC4gSGVuY2Ugb3VyIG11bHRpdmFyaWF0ZSBtb2RlbCB3aXRob3V0IGludGVyY2VwdCBjYW4gYmUgd3JpdHRlbiBhczogJGxuIChFKHkpKSA9IHvwnZy3XzF9IHt4X2l9X3sxfSArIGxuKPCdkoZf8J2SiikkIHdoZXJlLCAgJGkgPSAxLDIsMyzii68sbiQNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5oZWlnaHQ9MTQsIGZpZy53aWR0aD0xNX0NCiMgUHJlcCBtb2RlbCBkYXRhIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpkZjIwMTcgPC0gbW9kZWxfZGF0YV8yMDE3IHw+IA0KICBkcGx5cjo6YXNfdGliYmxlKCkgfD4gDQogICAgZHBseXI6OnJlbmFtZShvYnNlcnZlZF9jYXNlcyA9IG9ic2VydmVkXzIwMTcsDQogICAgICAgICAgICAgICAgICBleHBlY3RlZF9jYXNlcyA9IGV4cGVjdGVkXzIwMTcsDQogICAgICAgICAgICAgICAgICBoZWFsdGhfZmFjaWxpdHkgPSBOYW1lcykgfD4gDQogICAgZHBseXI6OnNlbGVjdCgtZ2VvbWV0cnksIC1maWQsLUROLCAtWCwgLVNNUiwgDQogICAgICAgICAgICAgICAgICAtcG9wXzIwMTcsIC1kcl8yMDE4LCAtZHJfMjAxOSwgLWRyXzIwMjApDQoNCmRmMjAxNyR5ZWFyIDwtICIyMDE3IiAjIGFkZCBuZXcgY29sdW1uDQoNCiMgZGYyMDE3IDwtIGNiaW5kKGRmMjAxNywgeWVhciA9ICIyMDE3IikgIyBhbHRlcm5hdGl2ZWx5DQoNCmRmMjAxOCA8LSBtb2RlbF9kYXRhXzIwMTggfD4gDQogIGRwbHlyOjphc190aWJibGUoKSB8PiANCiAgICBkcGx5cjo6cmVuYW1lKG9ic2VydmVkX2Nhc2VzID0gb2JzZXJ2ZWRfMjAxOCwNCiAgICAgICAgICAgICAgICAgIGV4cGVjdGVkX2Nhc2VzID0gZXhwZWN0ZWRfMjAxOCkgfD4gDQogICAgZHBseXI6OnNlbGVjdCgtZ2VvbWV0cnksIC1maWQsLUROLCAtWCwgLVNNUiwNCiAgICAgICAgICAgICAgICAgIC1wb3BfMjAxOCwtZHJfMjAxNywgLWRyXzIwMTksIC1kcl8yMDIwKQ0KDQpkZjIwMTgkeWVhciA8LSAiMjAxOCINCg0KY29sbmFtZXMoZGYyMDE4KSA8LSBjb2xuYW1lcyhkZjIwMTcpICMgbWF0Y2ggY29sdW1ucyBuYW1lcw0KDQpkZjIwMTkgPC0gbW9kZWxfZGF0YV8yMDE5IHw+IA0KICBkcGx5cjo6YXNfdGliYmxlKCkgfD4gDQogICAgZHBseXI6OnJlbmFtZShvYnNlcnZlZF9jYXNlcyA9IG9ic2VydmVkXzIwMTksDQogICAgICAgICAgICAgICAgICBleHBlY3RlZF9jYXNlcyA9IGV4cGVjdGVkXzIwMTkpIHw+IA0KICAgIGRwbHlyOjpzZWxlY3QoLWdlb21ldHJ5LCAtZmlkLC1ETiwgLVgsIC1TTVIsDQogICAgICAgICAgICAgICAgICAtcG9wXzIwMTksIC1kcl8yMDE3LCAtZHJfMjAxOCwgLWRyXzIwMjApDQoNCmRmMjAxOSR5ZWFyIDwtICIyMDE5Ig0KDQpjb2xuYW1lcyhkZjIwMTkpIDwtIGNvbG5hbWVzKGRmMjAxNykgIyBtYXRjaCBjb2x1bW5zIG5hbWVzDQoNCmRmMjAyMCA8LSBtb2RlbF9kYXRhXzIwMjAgfD4gDQogIGRwbHlyOjphc190aWJibGUoKSB8PiANCiAgICBkcGx5cjo6cmVuYW1lKG9ic2VydmVkX2Nhc2VzID0gb2JzZXJ2ZWRfMjAyMCwNCiAgICAgICAgICAgICAgICAgIGV4cGVjdGVkX2Nhc2VzID0gZXhwZWN0ZWRfMjAyMCkgfD4gDQogICAgZHBseXI6OnNlbGVjdCgtZ2VvbWV0cnksIC1maWQsLUROLCAtWCwgLVNNUiwNCiAgICAgICAgICAgICAgICAgIC1wb3BfMjAyMCwgLWRyXzIwMTcsIC1kcl8yMDE4LCAtZHJfMjAxOSkgDQoNCmRmMjAyMCR5ZWFyIDwtICIyMDIwIg0KDQpjb2xuYW1lcyhkZjIwMjApIDwtIGNvbG5hbWVzKGRmMjAxNykgIyBtYXRjaCBjb2x1bW5zIG5hbWVzDQoNCm1vZGVsX2RhdGEgPC0gcmJpbmQoZGYyMDE3LCBkZjIwMTgsIGRmMjAxOSwgZGYyMDIwKQ0KDQptb2RlbF9kYXRhIDwtIGltcHV0ZVRTOjpuYS5yZXBsYWNlKG1vZGVsX2RhdGEsIDApICMgcmVwbGFjZSBOQSB3aXRoIHplcm8NCg0KIyBmaW5kIHRoZSBsb2cobikgb2YgZWFjaCB2YWx1ZSBpbiAnZXhwZWN0ZWQnIGNvbHVtbi4gSXQgaXMgdGhlIGZvdXJ0aCBjb2x1bW4NCmxvZ19leHBlY3RlZCA8LSBsb2cobW9kZWxfZGF0YVsgLCA0XSkgfD4gDQogIGRwbHlyOjpyZW5hbWUobG9nX2V4cGVjdGVkID0gZXhwZWN0ZWRfY2FzZXMpDQoNCiMgYWRkIHRoZSBsb2cgdmFsdWVzIHRvIHRoZSBkYXRhZnJhbWUgdXNpbmcgJ2NiaW5kKCknDQptb2RlbF9kYXRhIDwtICBjYmluZChtb2RlbF9kYXRhLCBsb2dfZXhwZWN0ZWQpDQogIA0KDQoNCm1vZGVsX2RhdGEgfD4gICAgICMgVmlldyBtb2RlbCBkYXRhIGluIHRhYmxlIGZvcm1hdA0KICBndDo6Z3QoKSB8PiANCiAgZ3Q6OnRhYl9zdHlsZShzdHlsZSA9IGxpc3QoY2VsbF90ZXh0KGFsaWduID0gImNlbnRlciIpKSwNCiAgICAgICAgICAgICAgICBsb2NhdGlvbnMgPSBjZWxsc19jb2x1bW5fbGFiZWxzKCkgKSB8PiANCiAgZ3Q6OmNvbHNfbGFiZWwoaGVhbHRoX2ZhY2lsaXR5ID0gIkhlYWx0aCBmYWNpbGl0eSIsDQogICAgICAgICAgICAgICAgIG9ic2VydmVkX2Nhc2VzID0gIk9ic2VydmVkIGNhc2VzIiwNCiAgICAgICAgICAgICAgICAgZXhwZWN0ZWRfY2FzZXMgPSAiRXhwZWN0ZWQgY2FzZXMiLA0KICAgICAgICAgICAgICAgICBwcm9wX3BvcF8xa20gPSAiUHJvcG9ydGlvbiBvZiBwb3B1bGF0aW9uIGluIDFrbSBidWZmZXJzJSIsDQogICAgICAgICAgICAgICAgIHByb3BfcG9wXzJrbSA9ICJQcm9wb3J0aW9uIG9mIHBvcHVsYXRpb24gaW4gMmttIGJ1ZmZlcnMlIiwNCiAgICAgICAgICAgICAgICAgcHJvcF9wb3BfM2ttCT0gIlByb3BvcnRpb24gb2YgcG9wdWxhdGlvbiBpbiAza20gYnVmZmVycyUiLA0KICAgICAgICAgICAgICAgICBsb2dfZXhwZWN0ZWQgPSAiTG9nIG9mIGV4cGVjdGVkIGNhc2VzIiwNCiAgICAgICAgICAgICAgICAgeWVhciA9ICJZZWFyIikNCg0KIyBNb2RlbCBmaXR0aW5nIA0KIyAxa20gbW9kZWwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCm11bHRpdmFyaWF0ZV8xa20gPC0gZ2xtKG9ic2VydmVkX2Nhc2VzfjErcHJvcF9wb3BfMWttK3llYXIrb2Zmc2V0KGxvZyhleHBlY3RlZF9jYXNlcykpLA0KICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IG1vZGVsX2RhdGEsIGZhbWlseSA9IHBvaXNzb24obGluayA9ICJsb2ciKSkNCg0KI3N1bW1hcnkuZ2xtKG11bHRpdmFyaWF0ZV8xa20pDQpyZXBvcnQ6OnJlcG9ydChtdWx0aXZhcmlhdGVfMWttKQ0KDQoNCiMgQ2hlY2sgZWZmZWN0IG9mIHJlbW92aW5nIGludGVyY2VwdC4NCiMgV2hlbiB5b3UgcmVtb3ZlIGFuIGludGVyY2VwdCBmcm9tIGEgcmVncmVzc2lvbiBtb2RlbCwgeW914oCZcmUgc2V0dGluZyANCiMgaXQgZXF1YWwgdG8gMCByYXRoZXIgdGhhbiBlc3RpbWF0aW5nIGl0IGZyb20gdGhlIGRhdGEuDQptdWx0aXZhcmlhdGVfMWttX25vX2ludGVyY2VwdCA8LSBnbG0ob2JzZXJ2ZWRfY2FzZXN+MCtwcm9wX3BvcF8xa20reWVhciwgIyBsZWF2aW5nIHRoZSBpbnRlcmNlcHQgb3V0IA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9mZnNldCA9IGxvZyhleHBlY3RlZF9jYXNlcyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IG1vZGVsX2RhdGEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9IHBvaXNzb24obGluayA9ICJsb2ciKSkNCg0KI3N1bW1hcnkuZ2xtKG11bHRpdmFyaWF0ZV8xa21fbm9faW50ZXJjZXB0KQ0KcmVwb3J0OjpyZXBvcnQobXVsdGl2YXJpYXRlXzFrbV9ub19pbnRlcmNlcHQpDQoNCnNqUGxvdDo6dGFiX21vZGVsKG11bHRpdmFyaWF0ZV8xa20sIHNob3cucjIgPSBGQUxTRSwgc2hvdy5haWMgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgZGlnaXRzID0gMywgZGlnaXRzLnJlID0gMykNCg0Kc2pQbG90Ojp0YWJfbW9kZWwobXVsdGl2YXJpYXRlXzFrbV9ub19pbnRlcmNlcHQsIA0KICAgICAgICAgICAgICAgICAgc2hvdy5yMiA9IEZBTFNFLCBzaG93LmFpYyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICBkaWdpdHMgPSAzLCBkaWdpdHMucmUgPSAzKQ0KDQoNCg0KIyBBbHRlcm5hdGl2ZWx5DQojIG11bHRpdmFyaWF0ZV8xa21fcmF0ZSA8LSBnbG0ob2JzZXJ2ZWRfY2FzZXN+cHJvcF9wb3BfMWttK3llYXIrb2Zmc2V0KGxvZ19leHBlY3RlZCksDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IG1vZGVsX2RhdGEsIGZhbWlseSA9IHBvaXNzb24obGluayA9ICJsb2ciKSkNCiANCiMgc3VtbWFyeShtdWx0aXZhcmlhdGVfMWttX3JhdGUpDQojIHJlcG9ydDpyZXBvcnQobXVsdGl2YXJpYXRlXzFrbV9yYXRlKQ0KDQojIDJrbSBtb2RlbCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQptdWx0aXZhcmlhdGVfMmttIDwtIGdsbShvYnNlcnZlZF9jYXNlc34xK3Byb3BfcG9wXzJrbSt5ZWFyK29mZnNldChsb2coZXhwZWN0ZWRfY2FzZXMpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBtb2RlbF9kYXRhLCBmYW1pbHkgPSAncG9pc3NvbicpDQoNCiMgc3VtbWFyeShtdWx0aXZhcmlhdGVfMmttKQ0KcmVwb3J0OjpyZXBvcnQobXVsdGl2YXJpYXRlXzJrbSkNCg0KIyBDaGVjayBlZmZlY3Qgb2YgcmVtb3ZpbmcgaW50ZXJjZXB0DQptdWx0aXZhcmlhdGVfMmttX25vX2ludGVyY2VwdCA8LSBnbG0ob2JzZXJ2ZWRfY2FzZXN+cHJvcF9wb3BfMmttK3llYXItMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQgPSBsb2coZXhwZWN0ZWRfY2FzZXMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBtb2RlbF9kYXRhLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBwb2lzc29uKGxpbmsgPSAibG9nIikpDQoNCiMgc3VtbWFyeShtdWx0aXZhcmlhdGVfMmttX25vX2ludGVyY2VwdCkNCnJlcG9ydDo6cmVwb3J0KG11bHRpdmFyaWF0ZV8ya21fbm9faW50ZXJjZXB0KQ0KDQpzalBsb3Q6OnRhYl9tb2RlbChtdWx0aXZhcmlhdGVfMmttLCANCiAgICAgICAgICAgICAgICAgIHNob3cucjIgPSBGQUxTRSwgc2hvdy5haWMgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgZGlnaXRzID0gMywgZGlnaXRzLnJlID0gMykNCg0Kc2pQbG90Ojp0YWJfbW9kZWwobXVsdGl2YXJpYXRlXzJrbV9ub19pbnRlcmNlcHQsIA0KICAgICAgICAgICAgICAgICAgc2hvdy5yMiA9IEZBTFNFLCBzaG93LmFpYyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICBkaWdpdHMgPSAzLCBkaWdpdHMucmUgPSAzKQ0KDQoNCiMgM2ttIG1vZGVsIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQptdWx0aXZhcmlhdGVfM2ttIDwtIGdsbShvYnNlcnZlZF9jYXNlc35wcm9wX3BvcF8za20reWVhcitvZmZzZXQobG9nKGV4cGVjdGVkX2Nhc2VzKSksDQogICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gbW9kZWxfZGF0YSwgZmFtaWx5ID0gJ3BvaXNzb24nKQ0KDQoNCiMgc3VtbWFyeShtdWx0aXZhcmlhdGVfM2ttKQ0KcmVwb3J0OjpyZXBvcnQobXVsdGl2YXJpYXRlXzNrbSkNCmBgYA0KSW4gdGhpcyBjb21iaW5lZCBkYXRhc2V0LCB3ZSBjYW4gc2VlIHRoYXQgdGhlIGByZXNpZHVhbCBkZXZpYW5jZWAgaXMgZmFyIGZyb20gYGRlZ3JlZXMgb2YgZnJlZWRvbWAsIGFuZCB0aGUgYGRpc3BlcnNpb24gcGFyYW1ldGVyYCBpcyBlLmcuLCA1NzYuOTc5OCAoNTcxMjEvOTkgKSB3aGljaCBpcyBsYXJnZS4NCldoYXQgZG9lcyB0aGlzIGluZGljYXRlPyBUaGlzIHZhbHVlIGluZGljYXRlcyBwb29yIGZpdC4gVGhhdCBpcywgYSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGJldHdlZW4gZml0dGVkIHZhbHVlcyBhbmQgb2JzZXJ2ZWQgdmFsdWVzLg0KVGhpcyBtZWFucyB0aGF0IHRoZXJlIGlzIGV4dHJhIHZhcmlhbmNlIG5vdCBhY2NvdW50ZWQgZm9yIGJ5IHRoZSBtb2RlbC4gT25lIHdheSB0byBkZWFsIHdpdGggb3Zlci1kaXNwZXJzaW9uIGlzIHRvIHJ1biBhIGBxdWFzaS1Qb2lzc29uYCBtb2RlbCwgd2hpY2ggZml0cyBhbiBleHRyYSBkaXNwZXJzaW9uIHBhcmFtZXRlciB0byBhY2NvdW50IGZvciB0aGF0IGV4dHJhIHZhcmlhbmNlLg0KVGhlIG5lZ2F0aXZlIGNvZWZmaWNpZW50IGZvciB0aGUgcHJlZGljdG9ycyBpcyBoaWdobHkgc2lnbmlmaWNhbnQgKGBwPDJlLTE2YCkuDQpXZSBhbHNvIHNlZSB0aGF0IHllYXJseSB2YXJpYXRpb24gaW5mbHVlbmNlcyBvYnNlcnZlZCBjYXNlcyBuZWdhdGl2ZWx5LCBidXQgcGVyY2VudGFnZSBvZiBwZW9wbGUgbGl2aW5nIGNsb3NlIHRvIGRhbXMgaW5mbHVlbmNlcyBvYnNlcnZlZCBjYXNlcyBwb3NpdGl2ZWx5LiANCg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgZmlnLmhlaWdodD0xNywgZmlnLndpZHRoPTE2fQ0KIyBDaGVjayBlZmZlY3Qgb2YgcmVtb3ZpbmcgaW50ZXJjZXB0DQptdWx0aXZhcmlhdGVfM2ttX25vX2ludGVyY2VwdCA8LSBnbG0ob2JzZXJ2ZWRfY2FzZXN+cHJvcF9wb3BfM2ttK3llYXItMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQgPSBsb2coZXhwZWN0ZWRfY2FzZXMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBtb2RlbF9kYXRhLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBwb2lzc29uKGxpbmsgPSAibG9nIikpDQoNCiMgc3VtbWFyeShtdWx0aXZhcmlhdGVfM2ttX25vX2ludGVyY2VwdCkNCnJlcG9ydDo6cmVwb3J0KG11bHRpdmFyaWF0ZV8za21fbm9faW50ZXJjZXB0KQ0KDQpzalBsb3Q6OnRhYl9tb2RlbChtdWx0aXZhcmlhdGVfM2ttLCANCiAgICAgICAgICAgICAgICAgIHNob3cucjIgPSBGQUxTRSwgc2hvdy5haWMgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgZGlnaXRzID0gMywgZGlnaXRzLnJlID0gMykNCg0Kc2pQbG90Ojp0YWJfbW9kZWwobXVsdGl2YXJpYXRlXzNrbV9ub19pbnRlcmNlcHQsIA0KICAgICAgICAgICAgICAgICAgc2hvdy5yMiA9IEZBTFNFLCBzaG93LmFpYyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICBkaWdpdHMgPSAzLCBkaWdpdHMucmUgPSAzKQ0KDQojIEJ1aWxkIHJlZ3Jlc3Npb24gbW9kZWwgdGFibGUgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KcmVncmVzc2lvbl90YWJsZV8xa20gPC0gZ3RzdW1tYXJ5Ojp0YmxfcmVncmVzc2lvbihtdWx0aXZhcmlhdGVfMWttLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwb25lbnRpYXRlID0gRkFMU0UpIHw+DQogIGd0c3VtbWFyeTo6Ym9sZF9wKCkNCg0KDQpyZWdyZXNzaW9uX3RhYmxlXzJrbSA8LSBndHN1bW1hcnk6OnRibF9yZWdyZXNzaW9uKG11bHRpdmFyaWF0ZV8ya20pIHw+DQogIGd0c3VtbWFyeTo6Ym9sZF9wKCkNCg0KDQpyZWdyZXNzaW9uX3RhYmxlXzNrbSA8LSBndHN1bW1hcnk6OnRibF9yZWdyZXNzaW9uKG11bHRpdmFyaWF0ZV8za20sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHBvbmVudGlhdGUgPSBGQUxTRSkgfD4NCiAgZ3RzdW1tYXJ5Ojpib2xkX3AoKQ0KDQojIG1lcmdlIHJlZ3Jlc3Npb24gbW9kZWwgdGFibGVzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KdGFibGVfbWVyZ2UgPC0gZ3RzdW1tYXJ5Ojp0YmxfbWVyZ2UoDQogIHRibHMgPSBsaXN0KA0KICAgIHJlZ3Jlc3Npb25fdGFibGVfMWttLA0KICAgIHJlZ3Jlc3Npb25fdGFibGVfMmttLA0KICAgIHJlZ3Jlc3Npb25fdGFibGVfM2ttDQogICksDQogIHRhYl9zcGFubmVyID0gYygiKipNb2RlbCAxa20qKiIsDQogICAgICAgICAgICAgICAgICAiKipNb2RlbCAya20qKiIsDQogICAgICAgICAgICAgICAgICAiKipNb2RlbCAza20qKiIpDQopDQoNCnRhYmxlX21lcmdlDQoNCiMgQ2hlY2sgbW9kZWwgcGVyZm9tYW5jZSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCnBlcmZvcm1hbmNlOjpjaGVja19tb2RlbChtdWx0aXZhcmlhdGVfMWttLCB0aGVtZSA9ICJzZWU6OnRoZW1lX21vZGVybiIpDQoNCnBlcmZvcm1hbmNlOjpjaGVja19tb2RlbChtdWx0aXZhcmlhdGVfMmttLCB0aGVtZSA9ICJzZWU6OnRoZW1lX21vZGVybiIpDQoNCnBlcmZvcm1hbmNlOjpjaGVja19tb2RlbChtdWx0aXZhcmlhdGVfM2ttLCB0aGVtZSA9ICJzZWU6OnRoZW1lX21vZGVybiIpDQoNCnBlcmZvcm1hbmNlOjpjb21wYXJlX3BlcmZvcm1hbmNlKA0KICBtdWx0aXZhcmlhdGVfMWttLCBtdWx0aXZhcmlhdGVfMmttLCBtdWx0aXZhcmlhdGVfM2ttLA0KICBtZXRyaWNzID0gYygiQUlDIiwgIkJJQyIsICJSTVNFIiwgIlNpZ21hIiksIHJhbmsgPSBUUlVFKQ0KDQoNCmBgYA0KDQojIE92ZXJkaXNwZXJpb24NCg0KSGVyZSB3ZSBhcHBseSBhIGBxdWFzc2ktUG9pc3NvbmAgYW5kIGBuZWdhdGl2ZSBiaW5vbWlhbGAgcmVncmVzc2lvbiBtb2RlbHMgdG8gdGhlIG11bHRpdmFyaWF0ZSBtb2RlbCwgd2hpY2ggd2UgaGF2ZSBvYnNlcnZlZCB0aGF0IGl0IHN1ZmZlcnMgZnJvbSBvdmVyZGlzcGVyc2lvbiBpc3N1ZXMgdW5kZXIgcmVndWxhciBgUG9pc3NvbmAgcmVncmVzc2lvbi4gT25lIGFwcHJvYWNoIHRvIGRlYWxpbmcgd2l0aCBvdmVyZGlzcGVyc2lvbiBpcyB0byBtb2RlbCB0aGUgcmVzcG9uc2UgdXNpbmcgYSBgbmVnYXRpdmUgYmlub21pYWxgIGluc3RlYWQgb2YgYSBgUG9pc3NvbmAgZGlzdHJpYnV0aW9uLiBBbiBhZHZhbnRhZ2Ugb2YgdGhpcyBhcHByb2FjaCBpcyB0aGF0IGl0IGludHJvZHVjZXMgYW5vdGhlciBwYXJhbWV0ZXIgaW4gYWRkaXRpb24gdG8gJM67JCwgd2hpY2ggZ2l2ZXMgdGhlIG1vZGVsIG1vcmUgZmxleGliaWxpdHkgYW5kLCB1bmxpa2UgdGhlIGBxdWFzaS1Qb2lzc29uIG1vZGVsYCwgdGhlIGBuZWdhdGl2ZSBiaW5vbWlhbGAgbW9kZWwgYXNzdW1lcyBhbiBleHBsaWNpdCBsaWtlbGlob29kIG1vZGVsLiANCg0KDQpNYXRoZW1hdGljYWxseSwgYG5lZ2F0aXZlIGJpbm9taWFsYCBjYW4gYmUgZXhwcmVzc2VkIGFzIGEgYFBvaXNzb25gIG1vZGVsIHdoZXJlICTOuyQgaXMgYWxzbyByYW5kb20sIGZvbGxvd2luZyBhIGdhbW1hIGRpc3RyaWJ1dGlvbi4gU3BlY2lmaWNhbGx5LCBpZiAkWXzOu+KIvFBvaXNzb24ozrspJCBhbmQgJM674oi8Z2FtbWEociwgXGZyYWN7MS1wfXtwfSkkLCB0aGVuICRZ4oi8TmVnQmlub20ociwgcCkkIHdoZXJlICRFKFkpID0gXGZyYWN7cHJ9ezHiiJIgcH0gPSDOvCQgYW5kICRWYXIoWSkgPSBcZnJhY3twcn17KDHiiJJwKV4yfSA9IM68K1xmcmFje868XjJ9e3J9JC4gVGhlIG92ZXJkaXNwZXJzaW9uIGluIHRoaXMgY2FzZSBpcyBnaXZlbiBieSAkXGZyYWN7zrxeMn17cn0kLCB3aGljaCBhcHByb2FjaGVzICQwJCBhcyAkciQgaW5jcmVhc2VzIChzbyBzbWFsbGVyIHZhbHVlcyBvZiAkciQgaW5kaWNhdGUgZ3JlYXRlciBvdmVyZGlzcGVyc2lvbikuDQoNCg0KSW4gYSBgcXVhc3NpLVBvaXNzb25gIG1vZGVsLCB0aGUgYHN0YW5kYXJkIGVycm9yc2AgYXJlIGluZmxhdGVkIGJ5IG11bHRpcGx5aW5nIHRoZSBgdmFyaWFuY2VgIGJ5ICRccGhpJCwgc28gdGhhdCB0aGUgYHN0YW5kYXJkIGVycm9yc2AgYXJlIGxhcmdlciB0aGFuIHRoZSBsaWtlbGlob29kIGFwcHJvYWNoIHdvdWxkIGltcGx5OyBpLmUuLCAkU0VRKFxoYXR7zrJ9KSA9IOKImlxoYXR7z5V94oiXU0UoXGhhdHvOsn0pJCwgd2hlcmUgJFEkIHN0YW5kcyBmb3Ig4oCccXVhc2ktUG9pc3NvbuKAnSBzaW5jZSBtdWx0aXBseWluZyB2YXJpYW5jZXMgYnkgJM+VJCBpcyBhbiBhZC1ob2Mgc29sdXRpb24gKFJvYmFjayBhbmQgTGVnbGVyLCAyMDIxKS4NCg0KDQoNCg0KYGBge3J9DQojIEFjY291bnQgZm9yIG92ZXJkaXNwZXJzaW9uIHVzaW5nIHF1YXNpLVBvaXNzb24gbW9kZWwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KcXVhc3NpcG9pc3Nvbl8xa20gPC0gZ2xtKG9ic2VydmVkX2Nhc2VzfnByb3BfcG9wXzFrbSt5ZWFyLCBmYW1pbHkgPSBxdWFzaXBvaXNzb24sIA0KICAgICAgICAgICAgICAgICAgICAgICAgIG9mZnNldCA9IGxvZyhleHBlY3RlZF9jYXNlcyksIGRhdGEgPSBtb2RlbF9kYXRhKQ0KDQojIHN1bW1hcnkocXVhc3NpcG9pc3Nvbl8xa20pDQpyZXBvcnQ6OnJlcG9ydChxdWFzc2lwb2lzc29uXzFrbSkNCiMgSW4gdGhlIGFic2VuY2Ugb2Ygb3ZlcmRpc3BlcnNpb24sIHdlIGV4cGVjdCB0aGUgZGlzcGVyc2lvbiBwYXJhbWV0ZXIgZXN0aW1hdGUgdG8gYmUgMS4wLiANCiMgVGhlIGVzdGltYXRlZCBkaXNwZXJzaW9uIHBhcmFtZXRlciBoZXJlIGlzIG11Y2ggbGFyZ2VyIHRoYW4gMS4wICg1OTQuMTk0NykgaW5kaWNhdGluZw0KIyBvdmVyZGlzcGVyc2lvbiAoZXh0cmEgdmFyaWFuY2UpIHRoYXQgc2hvdWxkIGJlIGFjY291bnRlZCBmb3IuIA0KDQpzalBsb3Q6OnRhYl9tb2RlbChxdWFzc2lwb2lzc29uXzFrbSwgc2hvdy5yMiA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgZGlnaXRzID0gMywgZGlnaXRzLnJlID0gMykNCg0KcXVhc3NpcG9pc3Nvbl8ya20gPC0gZ2xtKG9ic2VydmVkX2Nhc2VzfnByb3BfcG9wXzJrbSt5ZWFyLCBmYW1pbHkgPSBxdWFzaXBvaXNzb24sIA0KICAgICAgICAgICAgICAgICAgICAgICAgIG9mZnNldCA9IGxvZyhleHBlY3RlZF9jYXNlcyksIGRhdGEgPSBtb2RlbF9kYXRhKQ0KDQojIHN1bW1hcnkocXVhc3NpcG9pc3Nvbl8ya20pDQpyZXBvcnQ6OnJlcG9ydChxdWFzc2lwb2lzc29uXzJrbSkNCg0KcXVhc3NpcG9pc3Nvbl8za20gPC0gZ2xtKG9ic2VydmVkX2Nhc2VzfnByb3BfcG9wXzNrbSt5ZWFyLCBmYW1pbHkgPSBxdWFzaXBvaXNzb24sIA0KICAgICAgICAgICAgICAgICAgICAgICAgIG9mZnNldCA9IGxvZyhleHBlY3RlZF9jYXNlcyksIGRhdGEgPSBtb2RlbF9kYXRhKQ0KDQojIHN1bW1hcnkocXVhc3NpcG9pc3Nvbl8za20pDQpyZXBvcnQ6OnJlcG9ydChxdWFzc2lwb2lzc29uXzNrbSkNCg0KIyBBY2NvdW50IGZvciBvdmVyZGlzcGVyc2lvbiB1c2luZyBuZWdhdGl2ZSBiaW5vbWlhbCBtb2RlbC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmJpbm9taWFsXzFrbSA8LSBNQVNTOjpnbG0ubmIob2JzZXJ2ZWRfY2FzZXN+cHJvcF9wb3BfMWttK3llYXIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQobG9nKGV4cGVjdGVkX2Nhc2VzKSksIGRhdGEgPSBtb2RlbF9kYXRhKQ0KDQpzdW1tYXJ5KGJpbm9taWFsXzFrbSkNCg0Kc2pQbG90Ojp0YWJfbW9kZWwoYmlub21pYWxfMWttLCBzaG93LnIyID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICBkaWdpdHMgPSAzLCBkaWdpdHMucmUgPSAzKQ0KDQoNCmJpbm9taWFsXzJrbSA8LSBNQVNTOjpnbG0ubmIob2JzZXJ2ZWRfY2FzZXN+cHJvcF9wb3BfMmttK3llYXIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQobG9nKGV4cGVjdGVkX2Nhc2VzKSksIGRhdGEgPSBtb2RlbF9kYXRhKQ0KDQpzdW1tYXJ5KGJpbm9taWFsXzJrbSkNCg0Kc2pQbG90Ojp0YWJfbW9kZWwoYmlub21pYWxfMWttLCBzaG93LnIyID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICBkaWdpdHMgPSAzLCBkaWdpdHMucmUgPSAzKQ0KDQoNCmJpbm9taWFsXzNrbSA8LSBNQVNTOjpnbG0ubmIob2JzZXJ2ZWRfY2FzZXN+cHJvcF9wb3BfM2ttK3llYXIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQobG9nKGV4cGVjdGVkX2Nhc2VzKSksIGRhdGEgPSBtb2RlbF9kYXRhKQ0KDQpzdW1tYXJ5KGJpbm9taWFsXzNrbSkNCg0Kc2pQbG90Ojp0YWJfbW9kZWwoYmlub21pYWxfMWttLCBzaG93LnIyID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICBkaWdpdHMgPSAzLCBkaWdpdHMucmUgPSAzKQ0KDQpgYGANCg0KDQojIEVzdGltYXRlIHByZWRpY3Rpb24gZXJyb3INCg0KU2luY2Ugb3VyIGRhdGFzZXQgaXMgc21hbGwgYW5kIHBhcnRpdGlvbmluZyBpdCB3aWxsIGxlYWQgdG8gaGlnaGVyIGJpYXMsIHRoZXJlZm9yZSwgd2UgdXNlIHRoZSBgTGVhdmUgb25lIG91dCBjcm9zcyB2YWxpZGF0aW9uYCAoTE9PQ1YpIG1ldGhvZCB3aGljaCB1c2VzIGFsbCBkYXRhIHBvaW50cyB0byBtZWFzdXJlIHBlcmZvcm1hbmNlIG9mIHRoZSBQb2lzc29uIG1vZGVsLg0KVGhpcyBtZXRob2Qgd29ya3MgYXMgZm9sbG93Og0KDQoxLiBMZWF2ZSBvdXQgb25lIGRhdGEgcG9pbnQgYW5kIGJ1aWxkIHRoZSBtb2RlbCBvbiB0aGUgcmVzdCBvZiB0aGUgZGF0YSBzZXQNCjIuIFRlc3QgdGhlIG1vZGVsIGFnYWluc3QgdGhlIGRhdGEgcG9pbnQgdGhhdCBpcyBsZWZ0IG91dCBhdCBzdGVwIDEgYW5kIHJlY29yZCB0aGUgdGVzdCBlcnJvciBhc3NvY2lhdGVkIHdpdGggdGhlIHByZWRpY3Rpb24NCjMuIFJlcGVhdCB0aGUgcHJvY2VzcyBmb3IgYWxsIGRhdGEgcG9pbnRzDQo0LiBDb21wdXRlIHRoZSBvdmVyYWxsIHByZWRpY3Rpb24gZXJyb3IgYnkgdGFraW5nIHRoZSBhdmVyYWdlIG9mIGFsbCB0aGVzZSB0ZXN0IGVycm9yIGVzdGltYXRlcyByZWNvcmRlZCBhdCBzdGVwIDIuDQoNCipKYW1lcywgR2FyZXRoLCBEYW5pZWxhIFdpdHRlbiwgVHJldm9yIEhhc3RpZSwgYW5kIFJvYmVydCBUaWJzaGlyYW5pLiAyMDE0LiBBbiBJbnRyb2R1Y3Rpb24gdG8gU3RhdGlzdGljYWwgTGVhcm5pbmc6IFdpdGggQXBwbGljYXRpb25zIGluIFIuIFNwcmluZ2VyIFB1Ymxpc2hpbmcgQ29tcGFueSwgSW5jb3Jwb3JhdGVkLioNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0V9DQoNCiMgRGVmaW5lIHRyYWluaW5nIGNvbnRyb2wNCnRyYWluX2NvbnRyb2wgPC0gIGNhcmV0Ojp0cmFpbkNvbnRyb2wobWV0aG9kID0gIkxPT0NWIikNCg0KIyBSZW1vdmluZyBtaXNzaW5nIHZhbHVlcw0KbW9kZWxfZGF0YV9ldmFsdWF0aW9uIDwtIG1vZGVsX2RhdGFbY29tcGxldGUuY2FzZXMobW9kZWxfZGF0YSksXSANCg0KDQoNCiMgVHJhaW4gdGhlIG1vZGVsLCBmaXQgYSByZWdyZXNzaW9uIG1vZGVsIGFuZCB1c2UgTE9PQ1YgdG8gZXZhbHVhdGUgcGVyZm9tYW5jZQ0KdHJhaW5fbW9kZWxfMWttIDwtIGNhcmV0Ojp0cmFpbigNCiAgb2JzZXJ2ZWRfY2FzZXN+MStwcm9wX3BvcF8xa20reWVhcitvZmZzZXQobG9nKGV4cGVjdGVkX2Nhc2VzKSksDQogIGRhdGEgPSBtb2RlbF9kYXRhX2V2YWx1YXRpb24sIG1ldGhvZCA9ICJnbG0iLCANCiAgdHJDb250cm9sID0gdHJhaW5fY29udHJvbCwgZmFtaWx5ID0gInBvaXNzb24iKQ0KDQp0cmFpbl9tb2RlbF8ya20gPC0gY2FyZXQ6OnRyYWluKA0KICBvYnNlcnZlZF9jYXNlc34xK3Byb3BfcG9wXzJrbSt5ZWFyK29mZnNldChsb2coZXhwZWN0ZWRfY2FzZXMpKSwNCiAgZGF0YSA9IG1vZGVsX2RhdGFfZXZhbHVhdGlvbiwgbWV0aG9kID0gImdsbSIsIA0KICB0ckNvbnRyb2wgPSB0cmFpbl9jb250cm9sLCBmYW1pbHkgPSAicG9pc3NvbiIpDQoNCg0KdHJhaW5fbW9kZWxfM2ttIDwtIGNhcmV0Ojp0cmFpbigNCiAgb2JzZXJ2ZWRfY2FzZXN+MStwcm9wX3BvcF8za20reWVhcitvZmZzZXQobG9nKGV4cGVjdGVkX2Nhc2VzKSksDQogIGRhdGEgPSBtb2RlbF9kYXRhX2V2YWx1YXRpb24sIG1ldGhvZCA9ICJnbG0iLCANCiAgdHJDb250cm9sID0gdHJhaW5fY29udHJvbCwgZmFtaWx5ID0gInBvaXNzb24iKQ0KDQojIFN1bW1hcml6ZSB0aGUgcmVzdWx0cw0KIyBUaGUgbG93ZXIgdGhlIE1lYW4gQWJzb2x1dGUgRXJyb3IsIHRoZSBtb3JlIGNsb3NlbHkgYSBtb2RlbCBjYW4gcHJlZGljdCB0aGUgYWN0dWFsIG9ic2VydmF0aW9ucw0KcHJpbnQodHJhaW5fbW9kZWxfMWttKQ0Kc3VtbWFyeSh0cmFpbl9tb2RlbF8xa20pDQoNCnByaW50KHRyYWluX21vZGVsXzJrbSkNCnN1bW1hcnkodHJhaW5fbW9kZWxfMmttKQ0KDQoNCnByaW50KHRyYWluX21vZGVsXzNrbSkNCnN1bW1hcnkodHJhaW5fbW9kZWxfM2ttKQ0KDQoNCmBgYA0KDQoNCg0K