Prediction Parking Demand

1.Introduction

1.1. Research Background

SFMTA Since the Ford Model T was first introduced on October 1, 1908, the automobile has become an integral part of society. One of the problems that have plagued mankind at the same time is the problem of parking. Difficulty in parking first of all imposes additional time costs on individuals. Vehicles wandering on the streets in search of a parking space add to the congestion on the roads. At the same time, more exhaust emissions and environmental pollution are created. To address these problems, some local governments have attempted to ensure the availability of parking spaces by dynamically adjusting parking prices, such as the SFMTA in our study, which seeks to implement a dynamic parking pricing policy for on-street parkingmeters between 9 a.m. and 6 p.m., Monday through Saturday, every week. This is intended to use the increased parking prices to ensure that there are always one or two spaces available on each street for vehicles to park, thus making it difficult for vehicles to find a space.

1.2. User & Use Case

This study is intended to help the City of San Francisco better evaluate and utilize the existing dynamic pricing policy, i.e., to be able to predict and regulate the occupancy rate of parking spaces through dynamic pricing and various spatial and temporal factors to ensure the availability of parking spaces. To address this issue, first, we will download the relevant parking meter usage data from the SFMTA website. Secondly, we will also use US census data to filter some parameters related to parking occupancy. After completing the exploration and sorting of the above data, we will build a regression model to analyze and predict the occupancy rate of parking spaces. At the same time, we will evaluate the validity of our model using cross-validation methods.


2. Data Wrangling.

2.1. Setup

Let’s load relevant libraries and some graphic themes.

library(prettydoc)
library(rmdformats)
library(tidyverse)
library(ggplot2)
library(tidycensus)
library(sf)
library(spdep)
library(caret)
library(ckanr)
library(FNN)
library(grid)
library(gridExtra)
library(ggcorrplot)
library(osmdata)
library(tigris)
library(osmextract)
library(curl)
library(reshape2)
library(glue)
library(dismo)
library(spgwr)
library(MASS)
library(lme4)
library(data.table)
library(kableExtra)
library(stargazer)
library(RSocrata)
library(knitr)
library(gifski)
library(rjson)
library(riem)
library(gganimate)
library(viridis)
library(lubridate)
library(tigris)
library(geojsonio)
library(magrittr)

#-----Import external functions & a palette-----
root.dir = "https://raw.githubusercontent.com/urbanSpatial/Public-Policy-Analytics-Landing/master/DATA/"
source("https://raw.githubusercontent.com/urbanSpatial/Public-Policy-Analytics-Landing/master/functions.r")

plotTheme <- theme(
  plot.title =element_text(size=12),
  plot.subtitle = element_text(size=8),
  plot.caption = element_text(size = 6),
  axis.text.x = element_text(size = 10, angle = 45, hjust = 1),
  axis.text.y = element_text(size = 10),
  axis.title.y = element_text(size = 10),
  # Set the entire chart region to blank
  panel.background=element_blank(),
  plot.background=element_blank(),
  #panel.border=element_rect(colour="#F0F0F0"),
  # Format the grid
  panel.grid.major=element_line(colour="#D0D0D0",size=.2),
  axis.ticks=element_blank())

mapTheme1 <- function(base_size = 35, title_size = 45) {
  theme(
    text = element_text( color = "black"),
    plot.title = element_text(hjust = 0.5, size = title_size, colour = "black",face = "bold"), 
    plot.subtitle = element_text(hjust = 0.5,size=base_size,face="italic"),
    plot.caption = element_text(size=base_size,hjust=0),
    axis.ticks = element_blank(),
    panel.spacing = unit(6, 'lines'),
    panel.background = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.border = element_blank(),
    strip.background = element_rect(fill = "grey80", color = "white"),
    strip.text = element_text(size=base_size),
    axis.title = element_text(face = "bold",size=base_size),
    axis.text = element_blank(),
    plot.background = element_blank(),
    legend.background = element_blank(),
    legend.title = element_text(size=base_size,colour = "black", face = "italic"),
    legend.text = element_text(size=base_size,colour = "black", face = "italic"),
    strip.text.x = element_text(size = base_size,face = "bold")
  )
}

mapTheme <- theme(plot.title =element_text(size=12),
                  plot.subtitle = element_text(size=8),
                  plot.caption = element_text(size = 6),
                  axis.line=element_blank(),
                  axis.text.x=element_blank(),
                  axis.text.y=element_blank(),
                  axis.ticks=element_blank(),
                  axis.title.x=element_blank(),
                  axis.title.y=element_blank(),
                  panel.background=element_blank(),
                  panel.border=element_blank(),
                  panel.grid.major=element_line(colour = 'transparent'),
                  panel.grid.minor=element_blank(),
                  legend.direction = "vertical", 
                  legend.position = "right",
                  plot.margin = margin(1, 1, 1, 1, 'cm'),
                  legend.key.height = unit(1, "cm"), legend.key.width = unit(0.2, "cm"))

palette5 <- c("#eff3ff","#bdd7e7","#6baed6","#3182bd","#08519c")
palette4 <- c("#D2FBD4","#92BCAB","#527D82","#123F5A")
palette2 <- c("#6baed6","#08519c")
palette1_main <- "#174C4F"
palette1_assist <- '#F9B294'

2.2. Import Parking Data

Through the official website of San Francisco Municipal Transportation Agency (SFMTA), we can download the parking data since 2020. Due to the high demand of parking in San Francisco, the total data volume is 105 million rows. Therefore, when importing the api, we filtered the data to only get the data from 20220501 9am to 20220514 6pm. The two weeks of data are used to form the basic dataset of our model.

## This part variable has been saved locally as dat, location
dat <- read.socrata("https://data.sfgov.org/resource/imvp-dq3v.csv?$where=SESSION_START_DT%20between%20%272022-05-1T9:00:00%27%20and%20%272022-05-14T17:00:00%27")

location <- 
  read.socrata("https://data.sfgov.org/resource/8vzz-qzz9.csv") %>%
    st_as_sf(coords = c("latitude", "longitude"), crs = 4326, agr = "constant")%>%
    st_transform('ESRI:102271') %>% 
    distinct()
glimpse(dat)

By calculating the total time each parking meter is used during the day, we can here calculate the parking occupancy of each parking meter based on the post_id. Here we just process the data and wait for the analysis to be done later.

## This part variable has been saved locally as dat2, parking_rate
dat2 <- dat %>% 
  left_join(location, by=('post_id'='post_id')) %>% 
  dplyr::select(post_id, street_block, session_start_dt, session_end_dt, meter_event_type, gross_paid_amt, on_offstreet_type, ms_space_num, old_rate_area, street_name,shape,geometry) %>% 
    mutate(end_interval15 = floor_date(ymd_hms(session_end_dt), unit = "15 mins"),
         start_interval15 = floor_date(ymd_hms(session_start_dt), unit = "15 mins"),
         ms_space_num = ifelse(ms_space_num == 0, 1, ms_space_num))

parking_rate <- dat2 %>% 
  mutate(length = end_interval15 - start_interval15,
         parking_hour = as.numeric(length)/3600,) %>% 
  dplyr::select(start_interval15, street_block, length,gross_paid_amt,parking_hour,post_id) %>% 
  mutate(rate=gross_paid_amt/parking_hour) %>% 
  filter(parking_hour!=0) %>% 
  dplyr::select(post_id, start_interval15, rate, street_block)

2.3. Import Census Data

In US Census Data, we have selected the following data: TotalPop, Whites, AfricanAmericans, Asians, MedHHINC, MedRent. First, we believe that there is a direct relationship between population size and parking occupancy, which is the basic logic of supply and demand in the market. Second, we also argue that resident income and rents also have an impact on parking occupancy.

CensusData <- 
  get_acs(geography = "block group", 
          variables = c("B01003_001E","B02001_002E","B19013_001E","B25058_001E",'B02001_003E','B02001_005E'), 
          year=2019, state="CA", county="SAN FRANCISCO", geometry=T, output="wide") %>%
  st_transform('ESRI:102243') %>%
  rename(Census_TotalPop = B01003_001E, 
         Census_Whites = B02001_002E,
         Census_AfricanAmericans = B02001_003E,
         Census_Asians = B02001_005E,
         Census_MedHHInc = B19013_001E, 
         Census_MedRent = B25058_001E) %>%
  dplyr::select(-NAME,-starts_with("B")) %>%
  mutate(Census_pctWhite = ifelse(Census_TotalPop > 0, 100*Census_Whites / Census_TotalPop,0),
         Census_pctAfricanAmericans = ifelse(Census_TotalPop > 0, 100*Census_AfricanAmericans / Census_TotalPop,0),
         Census_pctAsians = ifelse(Census_TotalPop > 0, 100*Census_Asians / Census_TotalPop,0),
         Census_blockgroupareasqm = as.numeric(st_area(.)),
         Census_areaperpeople = ifelse(Census_blockgroupareasqm > 0,Census_blockgroupareasqm/Census_TotalPop,0),
         year = "2019") %>%
  dplyr::select(-Census_Whites,-Census_AfricanAmericans,-Census_Asians, -GEOID)
# Geometries
CensusData2 <- 
  get_acs(geography = "tract", 
          variables = c("B01003_001E"), 
          year=2019, state="CA", county="SAN FRANCISCO", geometry=T, output="wide") %>%
  st_transform('ESRI:102243') %>%
  dplyr::select(-NAME,-starts_with("B"))

#-----Join Parking Meter data to census data-----
trainData <-st_join(location,dplyr::select(CensusData,-Census_blockgroupareasqm,-year,-geometry,-Census_TotalPop))
trainData <-st_join(location,dplyr::select(CensusData2, GEOID, -geometry))
## plot parking meters
boundary = st_read("D:/Upenn/Upenn Lec/05-MUSA-508/Assign-Final/DATAEXPO/SF/trim.shp") %>%
  st_transform(crs) %>%
  filter(objectid == "32")
CensusData3 <- CensusData2 %>%  
  filter(GEOID!="06075980401")%>% 
  filter(GEOID!="06075017902")
ggplot()+
  geom_sf(data = CensusData3, color="gray50",fill = "transparent",size=1,linetype = "dashed")+
  geom_sf(data = boundary,fill = "transparent", color="black",size=100000000)+
  geom_sf(data = trainData, size = 1,color=palette1_main )+
  scale_color_manual(values = palette5,
                    name = "Home sale prices")+
  labs(title = "Map of Parking Meters in San Francisco", 
       subtitle = "In tract unit")+
  mapTheme()

ParkingMap

After importing the US Census Data of San Francisco, we can show the location information of the parking meters on the map. From the map, we can see that most of the Parking Meters are located in the northeast part of San Francisco.

2.4. Import Spatial Data

After we finish importing the censusdata, let’s import some spatial data. Using the data from openstreetmap, we can easily retrieve which facility locations we think are relevant to parking occupancy. And we use k-nearest-neighbor function and buffer to process these data. In the initial screening of the data, we selected a large number of data, such as the location of parks and restaurants. We use both of these algorithms to calculate the data information around each parking meter for subsequent analysis.

## Set the data obtain area from OSM
q0 <- opq(bbox = c(-122.55,37.70,-122.35,37.82))
crs = "ESRI:102243"

## Define the the function to obtain different data.
get_osm_1 <- function(bbox, key, value, geom, crs){
  object <- add_osm_feature(opq = bbox, key = key, value = value) %>%
    osmdata_sf(.)
  geom_name <- paste("osm_", geom, sep = "")
  object.sf <- st_geometry(object[[geom_name]]) %>%
    st_transform(crs) %>%
    st_sf() %>%
    cbind(., object[[geom_name]][[key]]) %>%
    rename(NAME = "object..geom_name....key..")
  return(object.sf)
}

# Buffer Count - Count the number of appearance within the buffer of Parking Meter
# Input: Parking Meter data (sf), object/amenity type, buffer radius, object data
buffer_count = function(trainData, type, radius, data){
  ParkingBuffer = st_buffer(trainData, radius)
  trainData[type] = st_intersects(ParkingBuffer, data) %>%
    sapply(., length)
  return(trainData)
}

# Buffer Area - for polygon amenities, calculate the aggregate area of all entries that intersect with the buffer of a Parking Meter
# Input: Parking Meter data (sf), object/amenity type, buffer radius, object data
buffer_area = function(trainData, type, radius, data){
  data <- mutate(data, area = st_area(data))
  trainData[type] = st_buffer(trainData, radius) %>%
    aggregate(data %>% dplyr:: select (geometry, area),., sum) %>%
    pull(area)
  return(trainData)
}

# K-near - Calculate the average distance of a Parking Meter to its nearest (1 to 5) amenity object(s)
# Input: Parking Meter data, object/amenity data, object/amenity type, geometry type (point or non-point)
knear <- function(trainData, data, type, geom){
  if (geom != "points") {
    data =  data %>% dplyr::select(geometry) %>%st_centroid(.) %>% st_coordinates
  }
  else {
    data = data %>% dplyr::select(geometry) %>% st_coordinates
  }
  trainDataCoords = st_coordinates(trainData)
  nn_1 = nn_function(trainDataCoords, data, 1)
  nn_2 = nn_function(trainDataCoords, data, 2)
  nn_3 = nn_function(trainDataCoords, data, 3)
  nn_4 = nn_function(trainDataCoords, data, 4)
  nn_5 = nn_function(trainDataCoords, data, 5)
  trainData[paste(type, "_nn1", sep = "")] = nn_1
  trainData[paste(type, "_nn2", sep = "")] = nn_2
  trainData[paste(type, "_nn3", sep = "")] = nn_3
  trainData[paste(type, "_nn4", sep = "")] = nn_4
  trainData[paste(type, "_nn5", sep = "")] = nn_5
  return(trainData)
}

# Import data(DO NOT FORGET TO ADD DATA HERE!!!!!!!,这儿做各种spatial操作的data,记得替换!!!!!)
trainData <- location
# Select the data from OSM using the functions above.
#-----Amenity: parks-----

# Get park data, same below
parks = get_osm_1(q0, "leisure", "park", "polygons", "ESRI:102243")

## How many parks within 1000 m buffer of each home, same below
trainData = buffer_count(trainData, "parkCount", 1000, parks)
## Aggregate park area of all intersected park, same below
trainData = buffer_area(trainData, "parkArea", 1000, parks)

#-----Amenity: restaurants-----

restaurants = get_osm_1(q0, "amenity", "restaurant", "points", crs)
trainData = buffer_count(trainData, "restaurantsCount", 1000, restaurants)

# Average distance to 1~5 nearest restaurant(s), same below
trainData = knear(trainData, restaurants, "restaurants", "points")

#-----Amenity: schools-----

schools = get_osm_1(q0, 'amenity', 'school', "polygons", "ESRI:102243")
trainData = buffer_count(trainData, "schoolCount", 1000, schools)
trainData = knear(trainData, schools, "schools", "polygons")

# Assign each home to its nearest school
schoolDistrict = voronoi(st_coordinates(schools %>% st_centroid())) %>% 
  st_as_sf() %>% 
  st_set_crs("ESRI:102243") %>%
  rename(schoolNo = id) %>%
  mutate(schoolNo = as.character(schoolNo))
trainData = st_join(trainData, schoolDistrict)

#-----Amenity: universities-----

# Get university data
universities = get_osm_1(q0, 'amenity', 'university', "polygons", "ESRI:102243")

# Whether university is within 1000 m buffer of each home
trainData = buffer_count(trainData, "universitiesCount", 1000, universities)

#-----Amenity: parking-----

parking = get_osm_1(q0, "amenity", "parking", "polygons", "ESRI:102243")
trainData = buffer_count(trainData, "parkingCount", 1000, parking)
trainData = buffer_area(trainData, "parkingArea", 1000, parking)
trainData = knear(trainData, parking, "parking", "polygons")

#-----Amenity: clinics-----

clinics = get_osm_1(q0, "amenity", "clinic", "points", "ESRI:102243")
trainData = knear(trainData, clinics, "clinics", "points")

#-----Amenity: hospitals-----

hospitals = get_osm_1(q0, "amenity", "hospital", "polygons", "ESRI:102243")
trainData = knear(trainData, hospitals, "hospitals", "polygons")

#-----Amenity: cinemas-----
cinemas = get_osm_1(q0, "amenity", "cinema", "points", "ESRI:102243")
trainData = buffer_count(trainData, "cinemasCount", 1000, cinemas)
trainData = buffer_count(trainData, "cinemasCount2", 2000, cinemas)

#-----Amenity: stadiums-----
stadiums = get_osm_1(q0, "building", "stadium", "polygons", "ESRI:102243")
trainData = buffer_count(trainData, "stadiumsCount", 1000, stadiums)

#-----Amenity: commerce-----
commercial = get_osm_1(q0, "building", "commercial", "polygons", "ESRI:102243")
trainData = knear(trainData, commercial, "commerce", "polygons")
trainData = buffer_count(trainData, "commerceCount", 1000, commercial)

#-----Amenity: retail-----
retail = get_osm_1(q0, "building", "retail", "polygons", "ESRI:102243")
trainData = buffer_count(trainData, "retailCount", 1000, retail)
trainData = knear(trainData, retail, "retail", "polygons")

#-----Amenity: common leisure space-----
commonleisure = get_osm_1(q0, "leisure", "common", "polygons", "ESRI:102243")
trainData = knear(trainData, commonleisure, "commonLeisure", "polygons")

#-----Amenity: fitness centers-----
fitness_centre = get_osm_1(q0, "leisure", "fitness_centre", "points", "ESRI:102243")
trainData = buffer_count(trainData, "fitnessCenterCount", 1000, fitness_centre)
trainData = knear(trainData, fitness_centre, "fitnessCenter", "pooints")

#-----Amenity: public gardens-----
garden = get_osm_1(q0, "leisure", "garden", "polygons", "ESRI:102243")
trainData = knear(trainData, garden, "gardens", "polygons")
trainData = buffer_count(trainData, "gardensCount", 1000, garden)

#-----Jobs: companies-----
companies = get_osm_1(q0, "office", "company", "points", "ESRI:102243")
trainData = knear(trainData, companies, "companies", "points")

#-----Transport: public transport-----
public_transport = get_osm_1(q0, "public_transport", "station", "points", "ESRI:102243")
trainData <-
  trainData %>% 
  mutate(
    public_transport_nn1 = nn_function(st_coordinates(trainData), st_coordinates(public_transport%>%dplyr::select(geometry)%>%st_centroid(.)), 1),
    public_transport_nn2 = nn_function(st_coordinates(trainData), st_coordinates(public_transport%>%dplyr::select(geometry)%>%st_centroid(.)), 2))
#-----Join trainData to census data-----

trainData1 <-st_join(trainData,dplyr::select(CensusData,-Census_blockgroupareasqm,-year,-geometry,-Census_TotalPop))
trainData1 <-st_join(trainData1,dplyr::select(CensusData2, GEOID, -geometry))
# Possible Distribution Transformation
trainDataNumeric2 = trainData1[,numericColumns] %>% 
  st_drop_geometry()
i=1
par(mfrow=c(65,1))
for (column in trainDataNumeric2) {
  names = names(trainDataNumeric2)
  name = names[i:i]
  par(mfrow=c(1,2));hist(as.numeric(column),breaks=80,main=c(name,"Before"));hist(log(1+as.numeric(column)),breaks=80,main=c(name,"After"))
  i = i+1
}
dev.off()
#-----Select Numeric Variables-----
numericColumns = unlist(lapply(trainData, is.numeric))
trainDataNumeric = trainData[,numericColumns] %>% 
  dplyr::select(-MUSA_ID,-toPredict)%>% 
  st_drop_geometry() %>% 
  gather(key,value, -price)

#-----Make scatter-plots of all numeric variables----

ggplot(trainDataNumeric, aes(x = value, y = price))+
  geom_point(size=.05,alpha=0.5)+
  geom_smooth(method = lm, se=F,colour = "#DC986D",size=0.5)+
  facet_wrap(~key, scales = "free",ncol = 8)+
  plotTheme()
# Possible Distribution Transformation
trainDataNumeric2 = trainData[,numericColumns] %>% 
  dplyr::select(-MUSA_ID,-toPredict)%>%
  st_drop_geometry()
i=1
par(mfrow=c(65,1))
for (column in trainDataNumeric2) {
  names = names(trainDataNumeric2)
  name = names[i:i]
  par(mfrow=c(1,2));hist(as.numeric(column),breaks=80,main=c(name,"Before"));hist(log(1+as.numeric(column)),breaks=80,main=c(name,"After"))
  i = i+1
}
dev.off()

2.5.Import time data

Time token

image:

Here we will analyze our algorithm for time, time token, the above graph illustrates our calculation rules, for a parking record on a parking meter, the time it occupies is rounded to 15min time period, for the same time period, different parking spaces under the same Geo_id, the above rounded time token is added up, we get the total time token for the first time period.

add_rate <- parking_rate %>% 
  group_by(street_block) %>% 
  summarize(rate= mean(rate))

panel_st_block <- panel_join %>%
  filter(occupancy<=1) %>% 
  mutate(count=1) %>% 
  group_by(street_block,start_interval15) %>% 
  summarize(occ_space = sum(occupancy),
            all_space = sum(count)) %>% 
  mutate(occupancy = occ_space/all_space) %>% 
  dplyr::select(-occ_space, -all_space) %>% 
  left_join(add_rate)


predictors_test <- panel_join %>% 
              count(post_id)

predictors <- trainData1 %>% 
  left_join(panel_join %>% 
              group_by(post_id, street_block)) %>%
  na.omit()

predictors <- predictors %>% 
  group_by(street_block) %>% 
  summarize(GEOID=first(GEOID.x),
            parkCount=mean(parkCount),
            parkArea=mean(parkArea),
            restaurantsCount=mean(restaurantsCount),
            restaurants_nn4=mean(restaurants_nn4),
            schoolCount=mean(schoolCount),
            schools_nn4=mean(schools_nn4),
            universitiesCount=mean(universitiesCount),
            parkingCount=mean(parkingCount),
            parkingArea=mean(parkingArea),
            parking_nn4=mean(parking_nn4),
            clinics_nn4=mean(clinics_nn4),
            hospitals_nn4=mean(hospitals_nn4),
            cinemasCount=mean(cinemasCount),
            stadiumsCount=mean(stadiumsCount),
            commerce_nn4=mean(commerce_nn4),
            commerceCount=mean(commerceCount),
            retailCount=mean(retailCount),
            retail_nn4=mean(retail_nn4),
            commonLeisure_nn4=mean(commonLeisure_nn4),
            fitnessCenterCount=mean(fitnessCenterCount),
            fitnessCenter_nn4=mean(fitnessCenter_nn4),
            gardens_nn4=mean(gardens_nn4),
            gardensCount=mean(gardensCount),
            companies_nn4=mean(companies_nn4),
            public_transport_nn1=mean(public_transport_nn1),
            public_transport_nn2=mean(public_transport_nn2),
            Census_MedHHInc=mean(Census_MedHHInc),
            Census_MedRent=mean(Census_MedRent),
            Census_pctWhite=mean(Census_pctWhite),
            Census_areaperpeople=mean(Census_areaperpeople),
            Census_pctAfricanAmericans=mean(Census_pctAfricanAmericans),
            Census_pctAsians=mean(Census_pctAsians))

train_data_1 <- panel_st_block %>% 
  left_join(predictors, by="street_block")

Space Lag & Time Lag

Here, we merge all the time-space predictors as the basis for our subsequent models.

space_lag <- train_data_1 %>% 
  dplyr::select(GEOID,start_interval15,occupancy) %>% 
  group_by(GEOID,start_interval15) %>% 
  summarize(lag_occ = mean(occupancy))

train_data_2 <- train_data_1 %>% 
  left_join(space_lag) 
train_data_3 <- train_data_2 %>% 
  mutate(hod = hour(start_interval15)) %>% 
  filter(hod>=9 & hod<18)

time_lag <- train_data_3 %>% 
  dplyr::select(street_block,start_interval15,occupancy,hod) %>% 
  arrange(start_interval15) %>% 
  mutate(lagHour = dplyr::lag(occupancy,4),
         lag2Hours = dplyr::lag(occupancy, 8),
         lag3Hours = dplyr::lag(occupancy,12),
         lag6Hours = dplyr::lag(occupancy, 24),
         lag1day = dplyr::lag(occupancy,36)) %>% 
  mutate(lagHour = replace(lagHour, hod>=9&hod<10, NA),
         lag2Hours = replace(lag2Hours, hod>=9&hod<11, NA),
         lag3Hours = replace(lag3Hours, hod>=9&hod<12, NA),
         lag6Hours = replace(lag6Hours, hod>=9&hod<15, NA),
         lag1day = replace(lag1day, is.na(lag1day), occupancy)) %>% 
  replace(is.na(.), 0) 

train_data_4 <- train_data_3 %>% 
  left_join(time_lag)

3. Data Exploratory

In this section we will analyze the data obtained in the previous section.

3.1. Parking Time Distribution

The first is the distribution of parking hours. In the SFMTA’s parking meter charging policy, only the hours from Monday to Saturday, 9am to 6pm, are charged. Therefore, outside of these hours, the number of parking meters recorded by the parking meter is greatly reduced. The most frequent parking is between 9am and 6pm, which is the main paid parking period, and the number of parking decreases as time goes by. This is in line with our perception of travel to work and parking situation.

## Distribution
distribution <- dat2 %>% 
  mutate(dtime = format(as.POSIXct(start_interval15), format="%H")) %>% 
  dplyr::select(dtime) %>%
  mutate(dtime=as.numeric(distribution$dtime)) %>% 
  filter(dtime>=5 & dtime<22)
barplot(table(distribution$dtime),
main = "Parking Time Distribution",
xlab = "Hour of a Day",
ylab = "Frequency")

image1

3.2.Parking Price Rate

Whether the cost of parking is related to the occupancy rate of parking is also a part we would like to know. The time period is divided into 9:00-10:00, 12:00-13:00, 15:00-16:00, 17:00-18:00 quadrants, and the pearson correlation is shown with the parking rate as the horizontal coordinate and the parking occupancy rate as the vertical coordinate. From the graph, we can see that most of the parking meters have a parking rate between 0.4 and 0.5. As the parking rate increases, the change of occupancy rate is quite limited.

## Parking price rate
price_rate <- train_data_4 %>% 
  filter(hod==9 | hod==12 | hod==15 | hod==17) %>% 
  select(occupancy, rate, hod)

image2 <- ggplot(price_rate)+             ##图表2 相关性散点图
  geom_point(aes(x = rate, 
                 y = occupancy))+ ###散点数据
  geom_smooth(aes(x = rate, 
                  y = occupancy), 
              method = "lm", se = FALSE)+ ###拟合直线
  facet_wrap(~hod, scales = "free",ncol=2)+
  labs(
    title = "Parking Price by Occupancy",
    subtitle = "On 9:00-10:00, 12:00-13:00, 15:00-16:00, 17:00-18:00 ",
    x="rate", 
    y="occupancy")

ggsave("Parking Price by Occupancy.jpg",width = 10,height = 20, image2)

image2

3.3. Occupancy by Day of Week

Which days of the week have higher occupancy rates is also one of our concerns. Because the SFMTA’s parking meter is free on weekdays, the fact that a lower occupancy rate is recorded on weekdays does not mean that the parking occupancy rate is actually lower. For the rest of the time period, the daily parking occupancy rate is guaranteed to be around 0.5.

## Occupancy by day of week
day_of_week <- train_data_4 %>% 
  select(occupancy,start_interval15) %>%
  mutate(dotw = wday(start_interval15, label=TRUE)) %>% 
  group_by(dotw) %>% 
  summarize(occupancy = mean(occupancy))
  
image3 <- ggplot(day_of_week, aes(x = dotw, y = occupancy)) + 
  geom_line(aes(group=1)) +
  labs(title="Parking Occupancy by day of week",
       x="Day of week", 
       y="Average Occupancy")+
     plotTheme
ggsave("Parking Price by day of week.png", image3)

image3

3.4. Parking Occupancy by day of week

Finally, the parking occupancy at different times of the day for different Geo_ids. The horizontal coordinate starts at 9 a.m. and ends at 6 p.m. For most Geo_ids, the parking occupancy always stays within the same interval.

## Rate by time
Rate_time <- train_data_4 %>% 
  mutate(dtime = format(as.POSIXct(start_interval15), format="%H")) %>% 
  group_by(dtime, GEOID) %>% 
  summarize(rate = mean(rate))

image4 <- ggplot(Rate_time, aes(x = dtime, y = rate)) + 
  geom_line(aes(group=1)) +
  facet_wrap(~GEOID, scales = "free",ncol=5)+
  labs(title="Parking Occupancy by day of week",
       x="Day of week", 
       y="Average Occupancy")+
     plotTheme

ggsave("Rate by time.jpg",width = 10, height = 40,image4)

image4


4. Regression Model

In this section, we analyze the parking occupancy in combination with time lag and spatial lag.

Our predictor includes the spatial elements (restaurantCount, restaurant_nn4, schoolCount, schools_nn4), time lag (lagHour, lag2Hours, lag3Hours, lag6Hours), and Censusdata (MedHHInc, etc.)

model_data_offcial <- train_data_4 %>% 
  mutate(dotw = wday(start_interval15, label=TRUE))


set.seed(3456)
trainIndex <- createDataPartition(y=paste(model_data_offcial$street_block,model_data_offcial$GEOID, model_data_offcial$dotw), p = .70,
                                  list = FALSE,
                                  times = 1)

parking.Train <- model_data_offcial[ trainIndex,]
parking.Test <- model_data_offcial[ -trainIndex,]

reg.training <- 
  lm(occupancy ~  rate + parkCount + parkArea + restaurantsCount + restaurants_nn4 + schoolCount + schools_nn4 + universitiesCount + parkingCount + parkingArea + parking_nn4 + clinics_nn4 + hospitals_nn4 + cinemasCount + stadiumsCount + commerce_nn4 + commerceCount + retailCount + retail_nn4 + commonLeisure_nn4 + fitnessCenterCount + fitnessCenter_nn4 + gardens_nn4 + gardensCount + companies_nn4 + public_transport_nn1 + public_transport_nn1 + Census_MedHHInc + Census_MedRent + Census_pctWhite + Census_areaperpeople + Census_pctAfricanAmericans + Census_pctAsians + lag_occ + hod + dotw + lagHour + lag2Hours + lag3Hours + lag6Hours + lag1day,  data=parking.Train)

# 不用展示R方
#    stargazer(reg.training, 
#              type = 'text', 
#              title = "OLS Regression",
#              covariate.labels = c())

4.1. Goodness of fit

For the results of the model, the MAE of the model = 0.0395,MAPE of the model = 0.824

model_pred <- function(dat, fit){
   pred <- predict(fit, newdata = dat)}

parking.Test$occupancy.Predict = predict(reg.training, parking.Test)

parking.Test <-
  parking.Test %>%
  mutate(occupancy.Error = occupancy.Predict - occupancy,
         occupancy.AbsError = abs(occupancy.Predict - occupancy),
         occupancy.APE = (abs(occupancy.Predict - occupancy)) / occupancy.Predict)

MAE <- mean(parking.Test$occupancy.AbsError, na.rm = T)
MAPE <- mean(parking.Test$occupancy.APE, na.rm = T)
MAE = c(MAE) %>% format(., digits = 3)
MAPE = c(MAPE) %>% format(., digits = 3)
Model = c("Model Fitness")
summaryTable1 = cbind(Model, MAE, MAPE)

kable(summaryTable1, digits = 1, caption = "Table. Prediction precision of the first two models") %>%
  kable_classic(full_width = T)%>%
  footnote()

image5

4.2. Examine Error Metrics for Accuracy

The following chart shows our predicted results compared to the actual results.The cyan line is the predicted result and the red line is the actual result. As can be seen in the same figure, the model presents a better prediction in terms of overall trend due to the inclusion of time lag. However, on the whole, the occupancy rate of the model prediction is lower compared to the actual situation. This leads to a situation where the predicted results appear in parallel to the actual results during the free hours of the day.

parking_test_plot <- parking.Test %>% 
    mutate(interval60 = floor_date(ymd_hms(start_interval15), unit = "60 mins"))%>% 
    dplyr::select(interval60, street_block, occupancy, occupancy.Predict) %>%
    unnest()%>%
    na.omit() %>%
    gather(Variable, Value, -interval60, -street_block) %>%
    group_by(Variable, interval60) %>%
    summarize(Value = mean(Value))

image6 <- parking_test_plot %>% 
    ggplot(aes(interval60, Value, color=Variable)) + 
      geom_line(size = 1.1) + 
      labs(title = "Predicted/Observed Occupancy", subtitle = "SF; A test set of 2 weeks",  x = "Hour", y= "Occupancy") +
      plotTheme
##save.image(file='D:/UPenn/Fall2022/5080Policy/Final/Final_data_model/.RData')

ggsave("Predicted/Observed Occupancy.jpg",width = 10, height = 5,image6)

image6


5. Generalizability

5.1. Cross Validation

In the cross-validation analysis, our results are as follows. Similar to the results of the test set, the MAE of the model = 0.0757, which is slightly imprecise compared to a distribution that occupies the range between 0 and 1.

# R program to implement
# K-fold cross-validation
 
# setting seed to generate a
# reproducible random sampling
set.seed(125)
 
# defining training control
# as cross-validation and
# value of K equal to 10
train_control <- trainControl(method = "cv",
                              number = 10)
 
# training the model by assigning sales column
# as target variable and rest other column
# as independent variable
model <- train(occupancy ~ dotw+lagHour+lag2Hours+lag3Hours+lag6Hours+lag1day, data = park.cross,
               method = "lm",
               trControl = train_control)
 
# printing model performance metrics
# along with other details
print(model)

image8


6. Application Development

Finally we will share our user-specific interface design. Our app is intended to manage the occupancy of parking spots by helping SFMTA officials with pricing.

image7

In this online interface, decision makers can view projections of future parking rates on certain streets by entering prices. image8

LS0tDQp0aXRsZTogIlByZWRpY3Rpb24gUGFya2luZyBEZW1hbmQiDQphdXRob3I6ICJZdWhhbyBKaWEgJiBaaWxlIFd1Ig0KZGF0ZTogIkRlY2VtYmVyIDE1LCAyMDIyIg0Kb3V0cHV0OiANCiAgcm1kZm9ybWF0czo6cmVhZHRoZWRvd246DQogICAgY29kZV9mb2xkaW5nOiAiaGlkZSINCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQoNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldChldmFsID0gRkFMU0UpDQpgYGANCg0KIyAxLkludHJvZHVjdGlvbg0KDQojIyAxLjEuIFJlc2VhcmNoIEJhY2tncm91bmQNCiFbU0ZNVEFdKEQ6L1VwZW5uL1VwZW5uIExlYy8wNS1NVVNBLTUwOC9Bc3NpZ24tRmluYWwvUFBUL0RFTU8gZm9yIDEyMDkvMS5wbmcpDQpTaW5jZSB0aGUgRm9yZCBNb2RlbCBUIHdhcyBmaXJzdCBpbnRyb2R1Y2VkIG9uIE9jdG9iZXIgMSwgMTkwOCwgdGhlIGF1dG9tb2JpbGUgaGFzIGJlY29tZSBhbiBpbnRlZ3JhbCBwYXJ0IG9mIHNvY2lldHkuIE9uZSBvZiB0aGUgcHJvYmxlbXMgdGhhdCBoYXZlIHBsYWd1ZWQgbWFua2luZCBhdCB0aGUgc2FtZSB0aW1lIGlzIHRoZSBwcm9ibGVtIG9mIHBhcmtpbmcuDQpEaWZmaWN1bHR5IGluIHBhcmtpbmcgZmlyc3Qgb2YgYWxsIGltcG9zZXMgYWRkaXRpb25hbCB0aW1lIGNvc3RzIG9uIGluZGl2aWR1YWxzLiBWZWhpY2xlcyB3YW5kZXJpbmcgb24gdGhlIHN0cmVldHMgaW4gc2VhcmNoIG9mIGEgcGFya2luZyBzcGFjZSBhZGQgdG8gdGhlIGNvbmdlc3Rpb24gb24gdGhlIHJvYWRzLiBBdCB0aGUgc2FtZSB0aW1lLCBtb3JlIGV4aGF1c3QgZW1pc3Npb25zIGFuZCBlbnZpcm9ubWVudGFsIHBvbGx1dGlvbiBhcmUgY3JlYXRlZC4NClRvIGFkZHJlc3MgdGhlc2UgcHJvYmxlbXMsIHNvbWUgbG9jYWwgZ292ZXJubWVudHMgaGF2ZSBhdHRlbXB0ZWQgdG8gZW5zdXJlIHRoZSBhdmFpbGFiaWxpdHkgb2YgcGFya2luZyBzcGFjZXMgYnkgZHluYW1pY2FsbHkgYWRqdXN0aW5nIHBhcmtpbmcgcHJpY2VzLCBzdWNoIGFzIHRoZSBTRk1UQSBpbiBvdXIgc3R1ZHksIHdoaWNoIHNlZWtzIHRvIGltcGxlbWVudCBhIGR5bmFtaWMgcGFya2luZyBwcmljaW5nIHBvbGljeSBmb3Igb24tc3RyZWV0IHBhcmtpbmdtZXRlcnMgYmV0d2VlbiA5IGEubS4gYW5kIDYgcC5tLiwgTW9uZGF5IHRocm91Z2ggU2F0dXJkYXksIGV2ZXJ5IHdlZWsuIFRoaXMgaXMgaW50ZW5kZWQgdG8gdXNlIHRoZSBpbmNyZWFzZWQgcGFya2luZyBwcmljZXMgdG8gZW5zdXJlIHRoYXQgdGhlcmUgYXJlIGFsd2F5cyBvbmUgb3IgdHdvIHNwYWNlcyBhdmFpbGFibGUgb24gZWFjaCBzdHJlZXQgZm9yIHZlaGljbGVzIHRvIHBhcmssIHRodXMgbWFraW5nIGl0IGRpZmZpY3VsdCBmb3IgdmVoaWNsZXMgdG8gZmluZCBhIHNwYWNlLg0KDQojIyAxLjIuIFVzZXIgJiBVc2UgQ2FzZQ0KVGhpcyBzdHVkeSBpcyBpbnRlbmRlZCB0byBoZWxwIHRoZSBDaXR5IG9mIFNhbiBGcmFuY2lzY28gYmV0dGVyIGV2YWx1YXRlIGFuZCB1dGlsaXplIHRoZSBleGlzdGluZyBkeW5hbWljIHByaWNpbmcgcG9saWN5LCBpLmUuLCB0byBiZSBhYmxlIHRvIHByZWRpY3QgYW5kIHJlZ3VsYXRlIHRoZSBvY2N1cGFuY3kgcmF0ZSBvZiBwYXJraW5nIHNwYWNlcyB0aHJvdWdoIGR5bmFtaWMgcHJpY2luZyBhbmQgdmFyaW91cyBzcGF0aWFsIGFuZCB0ZW1wb3JhbCBmYWN0b3JzIHRvIGVuc3VyZSB0aGUgYXZhaWxhYmlsaXR5IG9mIHBhcmtpbmcgc3BhY2VzLg0KVG8gYWRkcmVzcyB0aGlzIGlzc3VlLCBmaXJzdCwgd2Ugd2lsbCBkb3dubG9hZCB0aGUgcmVsZXZhbnQgcGFya2luZyBtZXRlciB1c2FnZSBkYXRhIGZyb20gdGhlIFNGTVRBIHdlYnNpdGUuIFNlY29uZGx5LCB3ZSB3aWxsIGFsc28gdXNlIFVTIGNlbnN1cyBkYXRhIHRvIGZpbHRlciBzb21lIHBhcmFtZXRlcnMgcmVsYXRlZCB0byBwYXJraW5nIG9jY3VwYW5jeS4gQWZ0ZXIgY29tcGxldGluZyB0aGUgZXhwbG9yYXRpb24gYW5kIHNvcnRpbmcgb2YgdGhlIGFib3ZlIGRhdGEsIHdlIHdpbGwgYnVpbGQgYSByZWdyZXNzaW9uIG1vZGVsIHRvIGFuYWx5emUgYW5kIHByZWRpY3QgdGhlIG9jY3VwYW5jeSByYXRlIG9mIHBhcmtpbmcgc3BhY2VzLiBBdCB0aGUgc2FtZSB0aW1lLCB3ZSB3aWxsIGV2YWx1YXRlIHRoZSB2YWxpZGl0eSBvZiBvdXIgbW9kZWwgdXNpbmcgY3Jvc3MtdmFsaWRhdGlvbiBtZXRob2RzLg0KDQotLS0NCg0KIyAyLiBEYXRhIFdyYW5nbGluZy4NCg0KIyMgMi4xLiBTZXR1cA0KDQpMZXQncyBsb2FkIHJlbGV2YW50IGxpYnJhcmllcyBhbmQgc29tZSBncmFwaGljIHRoZW1lcy4NCg0KYGBge3Igc2V0dXBfMTMsIG1lc3NhZ2U9RkFMU0UsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLWhpZGUnfQ0KbGlicmFyeShwcmV0dHlkb2MpDQpsaWJyYXJ5KHJtZGZvcm1hdHMpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkodGlkeWNlbnN1cykNCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KHNwZGVwKQ0KbGlicmFyeShjYXJldCkNCmxpYnJhcnkoY2thbnIpDQpsaWJyYXJ5KEZOTikNCmxpYnJhcnkoZ3JpZCkNCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KbGlicmFyeShnZ2NvcnJwbG90KQ0KbGlicmFyeShvc21kYXRhKQ0KbGlicmFyeSh0aWdyaXMpDQpsaWJyYXJ5KG9zbWV4dHJhY3QpDQpsaWJyYXJ5KGN1cmwpDQpsaWJyYXJ5KHJlc2hhcGUyKQ0KbGlicmFyeShnbHVlKQ0KbGlicmFyeShkaXNtbykNCmxpYnJhcnkoc3Bnd3IpDQpsaWJyYXJ5KE1BU1MpDQpsaWJyYXJ5KGxtZTQpDQpsaWJyYXJ5KGRhdGEudGFibGUpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpsaWJyYXJ5KHN0YXJnYXplcikNCmxpYnJhcnkoUlNvY3JhdGEpDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShnaWZza2kpDQpsaWJyYXJ5KHJqc29uKQ0KbGlicmFyeShyaWVtKQ0KbGlicmFyeShnZ2FuaW1hdGUpDQpsaWJyYXJ5KHZpcmlkaXMpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkodGlncmlzKQ0KbGlicmFyeShnZW9qc29uaW8pDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KDQojLS0tLS1JbXBvcnQgZXh0ZXJuYWwgZnVuY3Rpb25zICYgYSBwYWxldHRlLS0tLS0NCnJvb3QuZGlyID0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS91cmJhblNwYXRpYWwvUHVibGljLVBvbGljeS1BbmFseXRpY3MtTGFuZGluZy9tYXN0ZXIvREFUQS8iDQpzb3VyY2UoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS91cmJhblNwYXRpYWwvUHVibGljLVBvbGljeS1BbmFseXRpY3MtTGFuZGluZy9tYXN0ZXIvZnVuY3Rpb25zLnIiKQ0KDQpwbG90VGhlbWUgPC0gdGhlbWUoDQogIHBsb3QudGl0bGUgPWVsZW1lbnRfdGV4dChzaXplPTEyKSwNCiAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLA0KICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLA0KICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksDQogIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksDQogIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLA0KICAjIFNldCB0aGUgZW50aXJlIGNoYXJ0IHJlZ2lvbiB0byBibGFuaw0KICBwYW5lbC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSwNCiAgcGxvdC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSwNCiAgI3BhbmVsLmJvcmRlcj1lbGVtZW50X3JlY3QoY29sb3VyPSIjRjBGMEYwIiksDQogICMgRm9ybWF0IHRoZSBncmlkDQogIHBhbmVsLmdyaWQubWFqb3I9ZWxlbWVudF9saW5lKGNvbG91cj0iI0QwRDBEMCIsc2l6ZT0uMiksDQogIGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpKQ0KDQptYXBUaGVtZTEgPC0gZnVuY3Rpb24oYmFzZV9zaXplID0gMzUsIHRpdGxlX3NpemUgPSA0NSkgew0KICB0aGVtZSgNCiAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KCBjb2xvciA9ICJibGFjayIpLA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSB0aXRsZV9zaXplLCBjb2xvdXIgPSAiYmxhY2siLGZhY2UgPSAiYm9sZCIpLCANCiAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LHNpemU9YmFzZV9zaXplLGZhY2U9Iml0YWxpYyIpLA0KICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChzaXplPWJhc2Vfc2l6ZSxoanVzdD0wKSwNCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLnNwYWNpbmcgPSB1bml0KDYsICdsaW5lcycpLA0KICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiZ3JleTgwIiwgY29sb3IgPSAid2hpdGUiKSwNCiAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9YmFzZV9zaXplKSwNCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsc2l6ZT1iYXNlX3NpemUpLA0KICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9YmFzZV9zaXplLGNvbG91ciA9ICJibGFjayIsIGZhY2UgPSAiaXRhbGljIiksDQogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT1iYXNlX3NpemUsY29sb3VyID0gImJsYWNrIiwgZmFjZSA9ICJpdGFsaWMiKSwNCiAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IGJhc2Vfc2l6ZSxmYWNlID0gImJvbGQiKQ0KICApDQp9DQoNCm1hcFRoZW1lIDwtIHRoZW1lKHBsb3QudGl0bGUgPWVsZW1lbnRfdGV4dChzaXplPTEyKSwNCiAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwNCiAgICAgICAgICAgICAgICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksDQogICAgICAgICAgICAgICAgICBheGlzLmxpbmU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgYXhpcy50aWNrcz1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQ9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgcGFuZWwuYm9yZGVyPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3I9ZWxlbWVudF9saW5lKGNvbG91ciA9ICd0cmFuc3BhcmVudCcpLA0KICAgICAgICAgICAgICAgICAgcGFuZWwuZ3JpZC5taW5vcj1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gInZlcnRpY2FsIiwgDQogICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLA0KICAgICAgICAgICAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMSwgMSwgMSwgMSwgJ2NtJyksDQogICAgICAgICAgICAgICAgICBsZWdlbmQua2V5LmhlaWdodCA9IHVuaXQoMSwgImNtIiksIGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KDAuMiwgImNtIikpDQoNCnBhbGV0dGU1IDwtIGMoIiNlZmYzZmYiLCIjYmRkN2U3IiwiIzZiYWVkNiIsIiMzMTgyYmQiLCIjMDg1MTljIikNCnBhbGV0dGU0IDwtIGMoIiNEMkZCRDQiLCIjOTJCQ0FCIiwiIzUyN0Q4MiIsIiMxMjNGNUEiKQ0KcGFsZXR0ZTIgPC0gYygiIzZiYWVkNiIsIiMwODUxOWMiKQ0KcGFsZXR0ZTFfbWFpbiA8LSAiIzE3NEM0RiINCnBhbGV0dGUxX2Fzc2lzdCA8LSAnI0Y5QjI5NCcNCg0KYGBgDQoNCg0KIyMgMi4yLiBJbXBvcnQgUGFya2luZyBEYXRhDQpUaHJvdWdoIHRoZSBvZmZpY2lhbCB3ZWJzaXRlIG9mIFNhbiBGcmFuY2lzY28gTXVuaWNpcGFsIFRyYW5zcG9ydGF0aW9uIEFnZW5jeSAoU0ZNVEEpLCB3ZSBjYW4gZG93bmxvYWQgdGhlIHBhcmtpbmcgZGF0YSBzaW5jZSAyMDIwLiBEdWUgdG8gdGhlIGhpZ2ggZGVtYW5kIG9mIHBhcmtpbmcgaW4gU2FuIEZyYW5jaXNjbywgdGhlIHRvdGFsIGRhdGEgdm9sdW1lIGlzIDEwNSBtaWxsaW9uIHJvd3MuIFRoZXJlZm9yZSwgd2hlbiBpbXBvcnRpbmcgdGhlIGFwaSwgd2UgZmlsdGVyZWQgdGhlIGRhdGEgdG8gb25seSBnZXQgdGhlIGRhdGEgZnJvbSAyMDIyMDUwMSA5YW0gdG8gMjAyMjA1MTQgNnBtLiBUaGUgdHdvIHdlZWtzIG9mIGRhdGEgYXJlIHVzZWQgdG8gZm9ybSB0aGUgYmFzaWMgZGF0YXNldCBvZiBvdXIgbW9kZWwuDQoNCmBgYHtyIHJlYWRfZGF0fQ0KIyMgVGhpcyBwYXJ0IHZhcmlhYmxlIGhhcyBiZWVuIHNhdmVkIGxvY2FsbHkgYXMgZGF0LCBsb2NhdGlvbg0KZGF0IDwtIHJlYWQuc29jcmF0YSgiaHR0cHM6Ly9kYXRhLnNmZ292Lm9yZy9yZXNvdXJjZS9pbXZwLWRxM3YuY3N2PyR3aGVyZT1TRVNTSU9OX1NUQVJUX0RUJTIwYmV0d2VlbiUyMCUyNzIwMjItMDUtMVQ5OjAwOjAwJTI3JTIwYW5kJTIwJTI3MjAyMi0wNS0xNFQxNzowMDowMCUyNyIpDQoNCmxvY2F0aW9uIDwtIA0KICByZWFkLnNvY3JhdGEoImh0dHBzOi8vZGF0YS5zZmdvdi5vcmcvcmVzb3VyY2UvOHZ6ei1xeno5LmNzdiIpICU+JQ0KICAgIHN0X2FzX3NmKGNvb3JkcyA9IGMoImxhdGl0dWRlIiwgImxvbmdpdHVkZSIpLCBjcnMgPSA0MzI2LCBhZ3IgPSAiY29uc3RhbnQiKSU+JQ0KICAgIHN0X3RyYW5zZm9ybSgnRVNSSToxMDIyNzEnKSAlPiUgDQogICAgZGlzdGluY3QoKQ0KZ2xpbXBzZShkYXQpDQpgYGANCg0KQnkgY2FsY3VsYXRpbmcgdGhlIHRvdGFsIHRpbWUgZWFjaCBwYXJraW5nIG1ldGVyIGlzIHVzZWQgZHVyaW5nIHRoZSBkYXksIHdlIGNhbiBoZXJlIGNhbGN1bGF0ZSB0aGUgcGFya2luZyBvY2N1cGFuY3kgb2YgZWFjaCBwYXJraW5nIG1ldGVyIGJhc2VkIG9uIHRoZSBwb3N0X2lkLiBIZXJlIHdlIGp1c3QgcHJvY2VzcyB0aGUgZGF0YSBhbmQgd2FpdCBmb3IgdGhlIGFuYWx5c2lzIHRvIGJlIGRvbmUgbGF0ZXIuDQoNCmBgYHtyIGdsaW1wc2VfZGF0MX0NCiMjIFRoaXMgcGFydCB2YXJpYWJsZSBoYXMgYmVlbiBzYXZlZCBsb2NhbGx5IGFzIGRhdDIsIHBhcmtpbmdfcmF0ZQ0KZGF0MiA8LSBkYXQgJT4lIA0KICBsZWZ0X2pvaW4obG9jYXRpb24sIGJ5PSgncG9zdF9pZCc9J3Bvc3RfaWQnKSkgJT4lIA0KICBkcGx5cjo6c2VsZWN0KHBvc3RfaWQsIHN0cmVldF9ibG9jaywgc2Vzc2lvbl9zdGFydF9kdCwgc2Vzc2lvbl9lbmRfZHQsIG1ldGVyX2V2ZW50X3R5cGUsIGdyb3NzX3BhaWRfYW10LCBvbl9vZmZzdHJlZXRfdHlwZSwgbXNfc3BhY2VfbnVtLCBvbGRfcmF0ZV9hcmVhLCBzdHJlZXRfbmFtZSxzaGFwZSxnZW9tZXRyeSkgJT4lIA0KICAgIG11dGF0ZShlbmRfaW50ZXJ2YWwxNSA9IGZsb29yX2RhdGUoeW1kX2htcyhzZXNzaW9uX2VuZF9kdCksIHVuaXQgPSAiMTUgbWlucyIpLA0KICAgICAgICAgc3RhcnRfaW50ZXJ2YWwxNSA9IGZsb29yX2RhdGUoeW1kX2htcyhzZXNzaW9uX3N0YXJ0X2R0KSwgdW5pdCA9ICIxNSBtaW5zIiksDQogICAgICAgICBtc19zcGFjZV9udW0gPSBpZmVsc2UobXNfc3BhY2VfbnVtID09IDAsIDEsIG1zX3NwYWNlX251bSkpDQoNCnBhcmtpbmdfcmF0ZSA8LSBkYXQyICU+JSANCiAgbXV0YXRlKGxlbmd0aCA9IGVuZF9pbnRlcnZhbDE1IC0gc3RhcnRfaW50ZXJ2YWwxNSwNCiAgICAgICAgIHBhcmtpbmdfaG91ciA9IGFzLm51bWVyaWMobGVuZ3RoKS8zNjAwLCkgJT4lIA0KICBkcGx5cjo6c2VsZWN0KHN0YXJ0X2ludGVydmFsMTUsIHN0cmVldF9ibG9jaywgbGVuZ3RoLGdyb3NzX3BhaWRfYW10LHBhcmtpbmdfaG91cixwb3N0X2lkKSAlPiUgDQogIG11dGF0ZShyYXRlPWdyb3NzX3BhaWRfYW10L3BhcmtpbmdfaG91cikgJT4lIA0KICBmaWx0ZXIocGFya2luZ19ob3VyIT0wKSAlPiUgDQogIGRwbHlyOjpzZWxlY3QocG9zdF9pZCwgc3RhcnRfaW50ZXJ2YWwxNSwgcmF0ZSwgc3RyZWV0X2Jsb2NrKQ0KYGBgDQoNCiMjIDIuMy4gSW1wb3J0IENlbnN1cyBEYXRhDQoNCkluIFVTIENlbnN1cyBEYXRhLCB3ZSBoYXZlIHNlbGVjdGVkIHRoZSBmb2xsb3dpbmcgZGF0YTogVG90YWxQb3AsIFdoaXRlcywgQWZyaWNhbkFtZXJpY2FucywgQXNpYW5zLCBNZWRISElOQywgTWVkUmVudC4NCkZpcnN0LCB3ZSBiZWxpZXZlIHRoYXQgdGhlcmUgaXMgYSBkaXJlY3QgcmVsYXRpb25zaGlwIGJldHdlZW4gcG9wdWxhdGlvbiBzaXplIGFuZCBwYXJraW5nIG9jY3VwYW5jeSwgd2hpY2ggaXMgdGhlIGJhc2ljIGxvZ2ljIG9mIHN1cHBseSBhbmQgZGVtYW5kIGluIHRoZSBtYXJrZXQuIFNlY29uZCwgd2UgYWxzbyBhcmd1ZSB0aGF0IHJlc2lkZW50IGluY29tZSBhbmQgcmVudHMgYWxzbyBoYXZlIGFuIGltcGFjdCBvbiBwYXJraW5nIG9jY3VwYW5jeS4NCg0KYGBge3IgaW5zdGFsbF9jZW5zdXNfQVBJX2tleSwgd2FybmluZyA9IEZBTFNFLCBpbmNsdWRlPUZBTFNFLCBldmFsID0gVFJVRX0NCiMgSW5zdGFsbCBDZW5zdXMgQVBJIEtleQ0KdGlkeWNlbnN1czo6Y2Vuc3VzX2FwaV9rZXkoImU3OWYzNzA2YjZkNjEyNDk5NjhjNmNlODg3OTRmNmY1NTZlNWJmM2QiLCBvdmVyd3JpdGUgPSBUUlVFKQ0KYGBgDQoNCmBgYHtyIGdldF9jZW5zdXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNhY2hlPVRSVUUsIHJlc3VsdHMgPSAnaGlkZSd9DQpDZW5zdXNEYXRhIDwtIA0KICBnZXRfYWNzKGdlb2dyYXBoeSA9ICJibG9jayBncm91cCIsIA0KICAgICAgICAgIHZhcmlhYmxlcyA9IGMoIkIwMTAwM18wMDFFIiwiQjAyMDAxXzAwMkUiLCJCMTkwMTNfMDAxRSIsIkIyNTA1OF8wMDFFIiwnQjAyMDAxXzAwM0UnLCdCMDIwMDFfMDA1RScpLCANCiAgICAgICAgICB5ZWFyPTIwMTksIHN0YXRlPSJDQSIsIGNvdW50eT0iU0FOIEZSQU5DSVNDTyIsIGdlb21ldHJ5PVQsIG91dHB1dD0id2lkZSIpICU+JQ0KICBzdF90cmFuc2Zvcm0oJ0VTUkk6MTAyMjQzJykgJT4lDQogIHJlbmFtZShDZW5zdXNfVG90YWxQb3AgPSBCMDEwMDNfMDAxRSwgDQogICAgICAgICBDZW5zdXNfV2hpdGVzID0gQjAyMDAxXzAwMkUsDQogICAgICAgICBDZW5zdXNfQWZyaWNhbkFtZXJpY2FucyA9IEIwMjAwMV8wMDNFLA0KICAgICAgICAgQ2Vuc3VzX0FzaWFucyA9IEIwMjAwMV8wMDVFLA0KICAgICAgICAgQ2Vuc3VzX01lZEhISW5jID0gQjE5MDEzXzAwMUUsIA0KICAgICAgICAgQ2Vuc3VzX01lZFJlbnQgPSBCMjUwNThfMDAxRSkgJT4lDQogIGRwbHlyOjpzZWxlY3QoLU5BTUUsLXN0YXJ0c193aXRoKCJCIikpICU+JQ0KICBtdXRhdGUoQ2Vuc3VzX3BjdFdoaXRlID0gaWZlbHNlKENlbnN1c19Ub3RhbFBvcCA+IDAsIDEwMCpDZW5zdXNfV2hpdGVzIC8gQ2Vuc3VzX1RvdGFsUG9wLDApLA0KICAgICAgICAgQ2Vuc3VzX3BjdEFmcmljYW5BbWVyaWNhbnMgPSBpZmVsc2UoQ2Vuc3VzX1RvdGFsUG9wID4gMCwgMTAwKkNlbnN1c19BZnJpY2FuQW1lcmljYW5zIC8gQ2Vuc3VzX1RvdGFsUG9wLDApLA0KICAgICAgICAgQ2Vuc3VzX3BjdEFzaWFucyA9IGlmZWxzZShDZW5zdXNfVG90YWxQb3AgPiAwLCAxMDAqQ2Vuc3VzX0FzaWFucyAvIENlbnN1c19Ub3RhbFBvcCwwKSwNCiAgICAgICAgIENlbnN1c19ibG9ja2dyb3VwYXJlYXNxbSA9IGFzLm51bWVyaWMoc3RfYXJlYSguKSksDQogICAgICAgICBDZW5zdXNfYXJlYXBlcnBlb3BsZSA9IGlmZWxzZShDZW5zdXNfYmxvY2tncm91cGFyZWFzcW0gPiAwLENlbnN1c19ibG9ja2dyb3VwYXJlYXNxbS9DZW5zdXNfVG90YWxQb3AsMCksDQogICAgICAgICB5ZWFyID0gIjIwMTkiKSAlPiUNCiAgZHBseXI6OnNlbGVjdCgtQ2Vuc3VzX1doaXRlcywtQ2Vuc3VzX0FmcmljYW5BbWVyaWNhbnMsLUNlbnN1c19Bc2lhbnMsIC1HRU9JRCkNCmBgYA0KDQpgYGB7ciBleHRyYWN0X2dlb21ldHJpZXMgfQ0KIyBHZW9tZXRyaWVzDQpDZW5zdXNEYXRhMiA8LSANCiAgZ2V0X2FjcyhnZW9ncmFwaHkgPSAidHJhY3QiLCANCiAgICAgICAgICB2YXJpYWJsZXMgPSBjKCJCMDEwMDNfMDAxRSIpLCANCiAgICAgICAgICB5ZWFyPTIwMTksIHN0YXRlPSJDQSIsIGNvdW50eT0iU0FOIEZSQU5DSVNDTyIsIGdlb21ldHJ5PVQsIG91dHB1dD0id2lkZSIpICU+JQ0KICBzdF90cmFuc2Zvcm0oJ0VTUkk6MTAyMjQzJykgJT4lDQogIGRwbHlyOjpzZWxlY3QoLU5BTUUsLXN0YXJ0c193aXRoKCJCIikpDQoNCiMtLS0tLUpvaW4gUGFya2luZyBNZXRlciBkYXRhIHRvIGNlbnN1cyBkYXRhLS0tLS0NCnRyYWluRGF0YSA8LXN0X2pvaW4obG9jYXRpb24sZHBseXI6OnNlbGVjdChDZW5zdXNEYXRhLC1DZW5zdXNfYmxvY2tncm91cGFyZWFzcW0sLXllYXIsLWdlb21ldHJ5LC1DZW5zdXNfVG90YWxQb3ApKQ0KdHJhaW5EYXRhIDwtc3Rfam9pbihsb2NhdGlvbixkcGx5cjo6c2VsZWN0KENlbnN1c0RhdGEyLCBHRU9JRCwgLWdlb21ldHJ5KSkNCmBgYA0KDQpgYGB7ciwgZmlnLmhlaWdodD00MCwgZmlnLndpZHRoPTQwfQ0KIyMgcGxvdCBwYXJraW5nIG1ldGVycw0KYm91bmRhcnkgPSBzdF9yZWFkKCJEOi9VcGVubi9VcGVubiBMZWMvMDUtTVVTQS01MDgvQXNzaWduLUZpbmFsL0RBVEFFWFBPL1NGL3RyaW0uc2hwIikgJT4lDQogIHN0X3RyYW5zZm9ybShjcnMpICU+JQ0KICBmaWx0ZXIob2JqZWN0aWQgPT0gIjMyIikNCkNlbnN1c0RhdGEzIDwtIENlbnN1c0RhdGEyICU+JSAgDQogIGZpbHRlcihHRU9JRCE9IjA2MDc1OTgwNDAxIiklPiUgDQogIGZpbHRlcihHRU9JRCE9IjA2MDc1MDE3OTAyIikNCmdncGxvdCgpKw0KICBnZW9tX3NmKGRhdGEgPSBDZW5zdXNEYXRhMywgY29sb3I9ImdyYXk1MCIsZmlsbCA9ICJ0cmFuc3BhcmVudCIsc2l6ZT0xLGxpbmV0eXBlID0gImRhc2hlZCIpKw0KICBnZW9tX3NmKGRhdGEgPSBib3VuZGFyeSxmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3I9ImJsYWNrIixzaXplPTEwMDAwMDAwMCkrDQogIGdlb21fc2YoZGF0YSA9IHRyYWluRGF0YSwgc2l6ZSA9IDEsY29sb3I9cGFsZXR0ZTFfbWFpbiApKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsDQogICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiSG9tZSBzYWxlIHByaWNlcyIpKw0KICBsYWJzKHRpdGxlID0gIk1hcCBvZiBQYXJraW5nIE1ldGVycyBpbiBTYW4gRnJhbmNpc2NvIiwgDQogICAgICAgc3VidGl0bGUgPSAiSW4gdHJhY3QgdW5pdCIpKw0KICBtYXBUaGVtZSgpDQpgYGANCg0KIVtQYXJraW5nTWFwXShEOi9VcGVubi9VcGVubiBMZWMvMDUtTVVTQS01MDgvQXNzaWduLUZpbmFsL0ZpbmFsRGF0YU1vZGVsQmFja1VwL1BhcmtpbmdNYXAuanBnKQ0KDQpBZnRlciBpbXBvcnRpbmcgdGhlIFVTIENlbnN1cyBEYXRhIG9mIFNhbiBGcmFuY2lzY28sIHdlIGNhbiBzaG93IHRoZSBsb2NhdGlvbiBpbmZvcm1hdGlvbiBvZiB0aGUgcGFya2luZyBtZXRlcnMgb24gdGhlIG1hcC4gRnJvbSB0aGUgbWFwLCB3ZSBjYW4gc2VlIHRoYXQgbW9zdCBvZiB0aGUgUGFya2luZyBNZXRlcnMgYXJlIGxvY2F0ZWQgaW4gdGhlIG5vcnRoZWFzdCBwYXJ0IG9mIFNhbiBGcmFuY2lzY28uDQoNCiMjIDIuNC4gSW1wb3J0IFNwYXRpYWwgRGF0YQ0KDQpBZnRlciB3ZSBmaW5pc2ggaW1wb3J0aW5nIHRoZSBjZW5zdXNkYXRhLCBsZXQncyBpbXBvcnQgc29tZSBzcGF0aWFsIGRhdGEuIFVzaW5nIHRoZSBkYXRhIGZyb20gb3BlbnN0cmVldG1hcCwgd2UgY2FuIGVhc2lseSByZXRyaWV2ZSB3aGljaCBmYWNpbGl0eSBsb2NhdGlvbnMgd2UgdGhpbmsgYXJlIHJlbGV2YW50IHRvIHBhcmtpbmcgb2NjdXBhbmN5LiBBbmQgd2UgdXNlIGstbmVhcmVzdC1uZWlnaGJvciBmdW5jdGlvbiBhbmQgYnVmZmVyIHRvIHByb2Nlc3MgdGhlc2UgZGF0YS4NCkluIHRoZSBpbml0aWFsIHNjcmVlbmluZyBvZiB0aGUgZGF0YSwgd2Ugc2VsZWN0ZWQgYSBsYXJnZSBudW1iZXIgb2YgZGF0YSwgc3VjaCBhcyB0aGUgbG9jYXRpb24gb2YgcGFya3MgYW5kIHJlc3RhdXJhbnRzLiBXZSB1c2UgYm90aCBvZiB0aGVzZSBhbGdvcml0aG1zIHRvIGNhbGN1bGF0ZSB0aGUgZGF0YSBpbmZvcm1hdGlvbiBhcm91bmQgZWFjaCBwYXJraW5nIG1ldGVyIGZvciBzdWJzZXF1ZW50IGFuYWx5c2lzLg0KDQpgYGB7cn0NCiMjIFNldCB0aGUgZGF0YSBvYnRhaW4gYXJlYSBmcm9tIE9TTQ0KcTAgPC0gb3BxKGJib3ggPSBjKC0xMjIuNTUsMzcuNzAsLTEyMi4zNSwzNy44MikpDQpjcnMgPSAiRVNSSToxMDIyNDMiDQoNCiMjIERlZmluZSB0aGUgdGhlIGZ1bmN0aW9uIHRvIG9idGFpbiBkaWZmZXJlbnQgZGF0YS4NCmdldF9vc21fMSA8LSBmdW5jdGlvbihiYm94LCBrZXksIHZhbHVlLCBnZW9tLCBjcnMpew0KICBvYmplY3QgPC0gYWRkX29zbV9mZWF0dXJlKG9wcSA9IGJib3gsIGtleSA9IGtleSwgdmFsdWUgPSB2YWx1ZSkgJT4lDQogICAgb3NtZGF0YV9zZiguKQ0KICBnZW9tX25hbWUgPC0gcGFzdGUoIm9zbV8iLCBnZW9tLCBzZXAgPSAiIikNCiAgb2JqZWN0LnNmIDwtIHN0X2dlb21ldHJ5KG9iamVjdFtbZ2VvbV9uYW1lXV0pICU+JQ0KICAgIHN0X3RyYW5zZm9ybShjcnMpICU+JQ0KICAgIHN0X3NmKCkgJT4lDQogICAgY2JpbmQoLiwgb2JqZWN0W1tnZW9tX25hbWVdXVtba2V5XV0pICU+JQ0KICAgIHJlbmFtZShOQU1FID0gIm9iamVjdC4uZ2VvbV9uYW1lLi4uLmtleS4uIikNCiAgcmV0dXJuKG9iamVjdC5zZikNCn0NCg0KIyBCdWZmZXIgQ291bnQgLSBDb3VudCB0aGUgbnVtYmVyIG9mIGFwcGVhcmFuY2Ugd2l0aGluIHRoZSBidWZmZXIgb2YgUGFya2luZyBNZXRlcg0KIyBJbnB1dDogUGFya2luZyBNZXRlciBkYXRhIChzZiksIG9iamVjdC9hbWVuaXR5IHR5cGUsIGJ1ZmZlciByYWRpdXMsIG9iamVjdCBkYXRhDQpidWZmZXJfY291bnQgPSBmdW5jdGlvbih0cmFpbkRhdGEsIHR5cGUsIHJhZGl1cywgZGF0YSl7DQogIFBhcmtpbmdCdWZmZXIgPSBzdF9idWZmZXIodHJhaW5EYXRhLCByYWRpdXMpDQogIHRyYWluRGF0YVt0eXBlXSA9IHN0X2ludGVyc2VjdHMoUGFya2luZ0J1ZmZlciwgZGF0YSkgJT4lDQogICAgc2FwcGx5KC4sIGxlbmd0aCkNCiAgcmV0dXJuKHRyYWluRGF0YSkNCn0NCg0KIyBCdWZmZXIgQXJlYSAtIGZvciBwb2x5Z29uIGFtZW5pdGllcywgY2FsY3VsYXRlIHRoZSBhZ2dyZWdhdGUgYXJlYSBvZiBhbGwgZW50cmllcyB0aGF0IGludGVyc2VjdCB3aXRoIHRoZSBidWZmZXIgb2YgYSBQYXJraW5nIE1ldGVyDQojIElucHV0OiBQYXJraW5nIE1ldGVyIGRhdGEgKHNmKSwgb2JqZWN0L2FtZW5pdHkgdHlwZSwgYnVmZmVyIHJhZGl1cywgb2JqZWN0IGRhdGENCmJ1ZmZlcl9hcmVhID0gZnVuY3Rpb24odHJhaW5EYXRhLCB0eXBlLCByYWRpdXMsIGRhdGEpew0KICBkYXRhIDwtIG11dGF0ZShkYXRhLCBhcmVhID0gc3RfYXJlYShkYXRhKSkNCiAgdHJhaW5EYXRhW3R5cGVdID0gc3RfYnVmZmVyKHRyYWluRGF0YSwgcmFkaXVzKSAlPiUNCiAgICBhZ2dyZWdhdGUoZGF0YSAlPiUgZHBseXI6OiBzZWxlY3QgKGdlb21ldHJ5LCBhcmVhKSwuLCBzdW0pICU+JQ0KICAgIHB1bGwoYXJlYSkNCiAgcmV0dXJuKHRyYWluRGF0YSkNCn0NCg0KIyBLLW5lYXIgLSBDYWxjdWxhdGUgdGhlIGF2ZXJhZ2UgZGlzdGFuY2Ugb2YgYSBQYXJraW5nIE1ldGVyIHRvIGl0cyBuZWFyZXN0ICgxIHRvIDUpIGFtZW5pdHkgb2JqZWN0KHMpDQojIElucHV0OiBQYXJraW5nIE1ldGVyIGRhdGEsIG9iamVjdC9hbWVuaXR5IGRhdGEsIG9iamVjdC9hbWVuaXR5IHR5cGUsIGdlb21ldHJ5IHR5cGUgKHBvaW50IG9yIG5vbi1wb2ludCkNCmtuZWFyIDwtIGZ1bmN0aW9uKHRyYWluRGF0YSwgZGF0YSwgdHlwZSwgZ2VvbSl7DQogIGlmIChnZW9tICE9ICJwb2ludHMiKSB7DQogICAgZGF0YSA9ICBkYXRhICU+JSBkcGx5cjo6c2VsZWN0KGdlb21ldHJ5KSAlPiVzdF9jZW50cm9pZCguKSAlPiUgc3RfY29vcmRpbmF0ZXMNCiAgfQ0KICBlbHNlIHsNCiAgICBkYXRhID0gZGF0YSAlPiUgZHBseXI6OnNlbGVjdChnZW9tZXRyeSkgJT4lIHN0X2Nvb3JkaW5hdGVzDQogIH0NCiAgdHJhaW5EYXRhQ29vcmRzID0gc3RfY29vcmRpbmF0ZXModHJhaW5EYXRhKQ0KICBubl8xID0gbm5fZnVuY3Rpb24odHJhaW5EYXRhQ29vcmRzLCBkYXRhLCAxKQ0KICBubl8yID0gbm5fZnVuY3Rpb24odHJhaW5EYXRhQ29vcmRzLCBkYXRhLCAyKQ0KICBubl8zID0gbm5fZnVuY3Rpb24odHJhaW5EYXRhQ29vcmRzLCBkYXRhLCAzKQ0KICBubl80ID0gbm5fZnVuY3Rpb24odHJhaW5EYXRhQ29vcmRzLCBkYXRhLCA0KQ0KICBubl81ID0gbm5fZnVuY3Rpb24odHJhaW5EYXRhQ29vcmRzLCBkYXRhLCA1KQ0KICB0cmFpbkRhdGFbcGFzdGUodHlwZSwgIl9ubjEiLCBzZXAgPSAiIildID0gbm5fMQ0KICB0cmFpbkRhdGFbcGFzdGUodHlwZSwgIl9ubjIiLCBzZXAgPSAiIildID0gbm5fMg0KICB0cmFpbkRhdGFbcGFzdGUodHlwZSwgIl9ubjMiLCBzZXAgPSAiIildID0gbm5fMw0KICB0cmFpbkRhdGFbcGFzdGUodHlwZSwgIl9ubjQiLCBzZXAgPSAiIildID0gbm5fNA0KICB0cmFpbkRhdGFbcGFzdGUodHlwZSwgIl9ubjUiLCBzZXAgPSAiIildID0gbm5fNQ0KICByZXR1cm4odHJhaW5EYXRhKQ0KfQ0KDQojIEltcG9ydCBkYXRhKERPIE5PVCBGT1JHRVQgVE8gQUREIERBVEEgSEVSRSEhISEhISEs6L+Z5YS/5YGa5ZCE56eNc3BhdGlhbOaTjeS9nOeahGRhdGHvvIzorrDlvpfmm7/mjaLvvIHvvIHvvIHvvIHvvIEpDQp0cmFpbkRhdGEgPC0gbG9jYXRpb24NCmBgYA0KDQpgYGB7cn0NCiMgU2VsZWN0IHRoZSBkYXRhIGZyb20gT1NNIHVzaW5nIHRoZSBmdW5jdGlvbnMgYWJvdmUuDQojLS0tLS1BbWVuaXR5OiBwYXJrcy0tLS0tDQoNCiMgR2V0IHBhcmsgZGF0YSwgc2FtZSBiZWxvdw0KcGFya3MgPSBnZXRfb3NtXzEocTAsICJsZWlzdXJlIiwgInBhcmsiLCAicG9seWdvbnMiLCAiRVNSSToxMDIyNDMiKQ0KDQojIyBIb3cgbWFueSBwYXJrcyB3aXRoaW4gMTAwMCBtIGJ1ZmZlciBvZiBlYWNoIGhvbWUsIHNhbWUgYmVsb3cNCnRyYWluRGF0YSA9IGJ1ZmZlcl9jb3VudCh0cmFpbkRhdGEsICJwYXJrQ291bnQiLCAxMDAwLCBwYXJrcykNCiMjIEFnZ3JlZ2F0ZSBwYXJrIGFyZWEgb2YgYWxsIGludGVyc2VjdGVkIHBhcmssIHNhbWUgYmVsb3cNCnRyYWluRGF0YSA9IGJ1ZmZlcl9hcmVhKHRyYWluRGF0YSwgInBhcmtBcmVhIiwgMTAwMCwgcGFya3MpDQoNCiMtLS0tLUFtZW5pdHk6IHJlc3RhdXJhbnRzLS0tLS0NCg0KcmVzdGF1cmFudHMgPSBnZXRfb3NtXzEocTAsICJhbWVuaXR5IiwgInJlc3RhdXJhbnQiLCAicG9pbnRzIiwgY3JzKQ0KdHJhaW5EYXRhID0gYnVmZmVyX2NvdW50KHRyYWluRGF0YSwgInJlc3RhdXJhbnRzQ291bnQiLCAxMDAwLCByZXN0YXVyYW50cykNCg0KIyBBdmVyYWdlIGRpc3RhbmNlIHRvIDF+NSBuZWFyZXN0IHJlc3RhdXJhbnQocyksIHNhbWUgYmVsb3cNCnRyYWluRGF0YSA9IGtuZWFyKHRyYWluRGF0YSwgcmVzdGF1cmFudHMsICJyZXN0YXVyYW50cyIsICJwb2ludHMiKQ0KDQojLS0tLS1BbWVuaXR5OiBzY2hvb2xzLS0tLS0NCg0Kc2Nob29scyA9IGdldF9vc21fMShxMCwgJ2FtZW5pdHknLCAnc2Nob29sJywgInBvbHlnb25zIiwgIkVTUkk6MTAyMjQzIikNCnRyYWluRGF0YSA9IGJ1ZmZlcl9jb3VudCh0cmFpbkRhdGEsICJzY2hvb2xDb3VudCIsIDEwMDAsIHNjaG9vbHMpDQp0cmFpbkRhdGEgPSBrbmVhcih0cmFpbkRhdGEsIHNjaG9vbHMsICJzY2hvb2xzIiwgInBvbHlnb25zIikNCg0KIyBBc3NpZ24gZWFjaCBob21lIHRvIGl0cyBuZWFyZXN0IHNjaG9vbA0Kc2Nob29sRGlzdHJpY3QgPSB2b3Jvbm9pKHN0X2Nvb3JkaW5hdGVzKHNjaG9vbHMgJT4lIHN0X2NlbnRyb2lkKCkpKSAlPiUgDQogIHN0X2FzX3NmKCkgJT4lIA0KICBzdF9zZXRfY3JzKCJFU1JJOjEwMjI0MyIpICU+JQ0KICByZW5hbWUoc2Nob29sTm8gPSBpZCkgJT4lDQogIG11dGF0ZShzY2hvb2xObyA9IGFzLmNoYXJhY3RlcihzY2hvb2xObykpDQp0cmFpbkRhdGEgPSBzdF9qb2luKHRyYWluRGF0YSwgc2Nob29sRGlzdHJpY3QpDQoNCiMtLS0tLUFtZW5pdHk6IHVuaXZlcnNpdGllcy0tLS0tDQoNCiMgR2V0IHVuaXZlcnNpdHkgZGF0YQ0KdW5pdmVyc2l0aWVzID0gZ2V0X29zbV8xKHEwLCAnYW1lbml0eScsICd1bml2ZXJzaXR5JywgInBvbHlnb25zIiwgIkVTUkk6MTAyMjQzIikNCg0KIyBXaGV0aGVyIHVuaXZlcnNpdHkgaXMgd2l0aGluIDEwMDAgbSBidWZmZXIgb2YgZWFjaCBob21lDQp0cmFpbkRhdGEgPSBidWZmZXJfY291bnQodHJhaW5EYXRhLCAidW5pdmVyc2l0aWVzQ291bnQiLCAxMDAwLCB1bml2ZXJzaXRpZXMpDQoNCiMtLS0tLUFtZW5pdHk6IHBhcmtpbmctLS0tLQ0KDQpwYXJraW5nID0gZ2V0X29zbV8xKHEwLCAiYW1lbml0eSIsICJwYXJraW5nIiwgInBvbHlnb25zIiwgIkVTUkk6MTAyMjQzIikNCnRyYWluRGF0YSA9IGJ1ZmZlcl9jb3VudCh0cmFpbkRhdGEsICJwYXJraW5nQ291bnQiLCAxMDAwLCBwYXJraW5nKQ0KdHJhaW5EYXRhID0gYnVmZmVyX2FyZWEodHJhaW5EYXRhLCAicGFya2luZ0FyZWEiLCAxMDAwLCBwYXJraW5nKQ0KdHJhaW5EYXRhID0ga25lYXIodHJhaW5EYXRhLCBwYXJraW5nLCAicGFya2luZyIsICJwb2x5Z29ucyIpDQoNCiMtLS0tLUFtZW5pdHk6IGNsaW5pY3MtLS0tLQ0KDQpjbGluaWNzID0gZ2V0X29zbV8xKHEwLCAiYW1lbml0eSIsICJjbGluaWMiLCAicG9pbnRzIiwgIkVTUkk6MTAyMjQzIikNCnRyYWluRGF0YSA9IGtuZWFyKHRyYWluRGF0YSwgY2xpbmljcywgImNsaW5pY3MiLCAicG9pbnRzIikNCg0KIy0tLS0tQW1lbml0eTogaG9zcGl0YWxzLS0tLS0NCg0KaG9zcGl0YWxzID0gZ2V0X29zbV8xKHEwLCAiYW1lbml0eSIsICJob3NwaXRhbCIsICJwb2x5Z29ucyIsICJFU1JJOjEwMjI0MyIpDQp0cmFpbkRhdGEgPSBrbmVhcih0cmFpbkRhdGEsIGhvc3BpdGFscywgImhvc3BpdGFscyIsICJwb2x5Z29ucyIpDQoNCiMtLS0tLUFtZW5pdHk6IGNpbmVtYXMtLS0tLQ0KY2luZW1hcyA9IGdldF9vc21fMShxMCwgImFtZW5pdHkiLCAiY2luZW1hIiwgInBvaW50cyIsICJFU1JJOjEwMjI0MyIpDQp0cmFpbkRhdGEgPSBidWZmZXJfY291bnQodHJhaW5EYXRhLCAiY2luZW1hc0NvdW50IiwgMTAwMCwgY2luZW1hcykNCnRyYWluRGF0YSA9IGJ1ZmZlcl9jb3VudCh0cmFpbkRhdGEsICJjaW5lbWFzQ291bnQyIiwgMjAwMCwgY2luZW1hcykNCg0KIy0tLS0tQW1lbml0eTogc3RhZGl1bXMtLS0tLQ0Kc3RhZGl1bXMgPSBnZXRfb3NtXzEocTAsICJidWlsZGluZyIsICJzdGFkaXVtIiwgInBvbHlnb25zIiwgIkVTUkk6MTAyMjQzIikNCnRyYWluRGF0YSA9IGJ1ZmZlcl9jb3VudCh0cmFpbkRhdGEsICJzdGFkaXVtc0NvdW50IiwgMTAwMCwgc3RhZGl1bXMpDQoNCiMtLS0tLUFtZW5pdHk6IGNvbW1lcmNlLS0tLS0NCmNvbW1lcmNpYWwgPSBnZXRfb3NtXzEocTAsICJidWlsZGluZyIsICJjb21tZXJjaWFsIiwgInBvbHlnb25zIiwgIkVTUkk6MTAyMjQzIikNCnRyYWluRGF0YSA9IGtuZWFyKHRyYWluRGF0YSwgY29tbWVyY2lhbCwgImNvbW1lcmNlIiwgInBvbHlnb25zIikNCnRyYWluRGF0YSA9IGJ1ZmZlcl9jb3VudCh0cmFpbkRhdGEsICJjb21tZXJjZUNvdW50IiwgMTAwMCwgY29tbWVyY2lhbCkNCg0KIy0tLS0tQW1lbml0eTogcmV0YWlsLS0tLS0NCnJldGFpbCA9IGdldF9vc21fMShxMCwgImJ1aWxkaW5nIiwgInJldGFpbCIsICJwb2x5Z29ucyIsICJFU1JJOjEwMjI0MyIpDQp0cmFpbkRhdGEgPSBidWZmZXJfY291bnQodHJhaW5EYXRhLCAicmV0YWlsQ291bnQiLCAxMDAwLCByZXRhaWwpDQp0cmFpbkRhdGEgPSBrbmVhcih0cmFpbkRhdGEsIHJldGFpbCwgInJldGFpbCIsICJwb2x5Z29ucyIpDQoNCiMtLS0tLUFtZW5pdHk6IGNvbW1vbiBsZWlzdXJlIHNwYWNlLS0tLS0NCmNvbW1vbmxlaXN1cmUgPSBnZXRfb3NtXzEocTAsICJsZWlzdXJlIiwgImNvbW1vbiIsICJwb2x5Z29ucyIsICJFU1JJOjEwMjI0MyIpDQp0cmFpbkRhdGEgPSBrbmVhcih0cmFpbkRhdGEsIGNvbW1vbmxlaXN1cmUsICJjb21tb25MZWlzdXJlIiwgInBvbHlnb25zIikNCg0KIy0tLS0tQW1lbml0eTogZml0bmVzcyBjZW50ZXJzLS0tLS0NCmZpdG5lc3NfY2VudHJlID0gZ2V0X29zbV8xKHEwLCAibGVpc3VyZSIsICJmaXRuZXNzX2NlbnRyZSIsICJwb2ludHMiLCAiRVNSSToxMDIyNDMiKQ0KdHJhaW5EYXRhID0gYnVmZmVyX2NvdW50KHRyYWluRGF0YSwgImZpdG5lc3NDZW50ZXJDb3VudCIsIDEwMDAsIGZpdG5lc3NfY2VudHJlKQ0KdHJhaW5EYXRhID0ga25lYXIodHJhaW5EYXRhLCBmaXRuZXNzX2NlbnRyZSwgImZpdG5lc3NDZW50ZXIiLCAicG9vaW50cyIpDQoNCiMtLS0tLUFtZW5pdHk6IHB1YmxpYyBnYXJkZW5zLS0tLS0NCmdhcmRlbiA9IGdldF9vc21fMShxMCwgImxlaXN1cmUiLCAiZ2FyZGVuIiwgInBvbHlnb25zIiwgIkVTUkk6MTAyMjQzIikNCnRyYWluRGF0YSA9IGtuZWFyKHRyYWluRGF0YSwgZ2FyZGVuLCAiZ2FyZGVucyIsICJwb2x5Z29ucyIpDQp0cmFpbkRhdGEgPSBidWZmZXJfY291bnQodHJhaW5EYXRhLCAiZ2FyZGVuc0NvdW50IiwgMTAwMCwgZ2FyZGVuKQ0KDQojLS0tLS1Kb2JzOiBjb21wYW5pZXMtLS0tLQ0KY29tcGFuaWVzID0gZ2V0X29zbV8xKHEwLCAib2ZmaWNlIiwgImNvbXBhbnkiLCAicG9pbnRzIiwgIkVTUkk6MTAyMjQzIikNCnRyYWluRGF0YSA9IGtuZWFyKHRyYWluRGF0YSwgY29tcGFuaWVzLCAiY29tcGFuaWVzIiwgInBvaW50cyIpDQoNCiMtLS0tLVRyYW5zcG9ydDogcHVibGljIHRyYW5zcG9ydC0tLS0tDQpwdWJsaWNfdHJhbnNwb3J0ID0gZ2V0X29zbV8xKHEwLCAicHVibGljX3RyYW5zcG9ydCIsICJzdGF0aW9uIiwgInBvaW50cyIsICJFU1JJOjEwMjI0MyIpDQp0cmFpbkRhdGEgPC0NCiAgdHJhaW5EYXRhICU+JSANCiAgbXV0YXRlKA0KICAgIHB1YmxpY190cmFuc3BvcnRfbm4xID0gbm5fZnVuY3Rpb24oc3RfY29vcmRpbmF0ZXModHJhaW5EYXRhKSwgc3RfY29vcmRpbmF0ZXMocHVibGljX3RyYW5zcG9ydCU+JWRwbHlyOjpzZWxlY3QoZ2VvbWV0cnkpJT4lc3RfY2VudHJvaWQoLikpLCAxKSwNCiAgICBwdWJsaWNfdHJhbnNwb3J0X25uMiA9IG5uX2Z1bmN0aW9uKHN0X2Nvb3JkaW5hdGVzKHRyYWluRGF0YSksIHN0X2Nvb3JkaW5hdGVzKHB1YmxpY190cmFuc3BvcnQlPiVkcGx5cjo6c2VsZWN0KGdlb21ldHJ5KSU+JXN0X2NlbnRyb2lkKC4pKSwgMikpDQoNCmBgYA0KDQpgYGB7cn0NCiMtLS0tLUpvaW4gdHJhaW5EYXRhIHRvIGNlbnN1cyBkYXRhLS0tLS0NCg0KdHJhaW5EYXRhMSA8LXN0X2pvaW4odHJhaW5EYXRhLGRwbHlyOjpzZWxlY3QoQ2Vuc3VzRGF0YSwtQ2Vuc3VzX2Jsb2NrZ3JvdXBhcmVhc3FtLC15ZWFyLC1nZW9tZXRyeSwtQ2Vuc3VzX1RvdGFsUG9wKSkNCnRyYWluRGF0YTEgPC1zdF9qb2luKHRyYWluRGF0YTEsZHBseXI6OnNlbGVjdChDZW5zdXNEYXRhMiwgR0VPSUQsIC1nZW9tZXRyeSkpDQoNCmBgYA0KDQpgYGB7cn0NCiMgUG9zc2libGUgRGlzdHJpYnV0aW9uIFRyYW5zZm9ybWF0aW9uDQp0cmFpbkRhdGFOdW1lcmljMiA9IHRyYWluRGF0YTFbLG51bWVyaWNDb2x1bW5zXSAlPiUgDQogIHN0X2Ryb3BfZ2VvbWV0cnkoKQ0KaT0xDQpwYXIobWZyb3c9Yyg2NSwxKSkNCmZvciAoY29sdW1uIGluIHRyYWluRGF0YU51bWVyaWMyKSB7DQogIG5hbWVzID0gbmFtZXModHJhaW5EYXRhTnVtZXJpYzIpDQogIG5hbWUgPSBuYW1lc1tpOmldDQogIHBhcihtZnJvdz1jKDEsMikpO2hpc3QoYXMubnVtZXJpYyhjb2x1bW4pLGJyZWFrcz04MCxtYWluPWMobmFtZSwiQmVmb3JlIikpO2hpc3QobG9nKDErYXMubnVtZXJpYyhjb2x1bW4pKSxicmVha3M9ODAsbWFpbj1jKG5hbWUsIkFmdGVyIikpDQogIGkgPSBpKzENCn0NCmRldi5vZmYoKQ0KYGBgDQoNCmBgYHtyIGdsaW1wc2VfZGF0LCBlY2hvPUZBTFNFLCBldmFsPSBGQUxTRSB9DQpkYXQyIDwtIGRhdCAlPiUgDQogIGxlZnRfam9pbihsb2NhdGlvbiwgYnk9KCdwb3N0X2lkJz0ncG9zdF9pZCcpKSAlPiUgDQogIGRwbHlyOjpzZWxlY3QocG9zdF9pZCwgc3RyZWV0X2Jsb2NrLCBzZXNzaW9uX3N0YXJ0X2R0LCBzZXNzaW9uX2VuZF9kdCwgbWV0ZXJfZXZlbnRfdHlwZSwgZ3Jvc3NfcGFpZF9hbXQsIG9uX29mZnN0cmVldF90eXBlLCBtZXRlcl90eXBlLCBvbGRfcmF0ZV9hcmVhLCBzdHJlZXRfbmFtZSxzaGFwZSxnZW9tZXRyeSkgJT4lIA0KICAgIG11dGF0ZShlbmRfaW50ZXJ2YWwxNSA9IGZsb29yX2RhdGUoeW1kX2htcyhzZXNzaW9uX2VuZF9kdCksIHVuaXQgPSAiMTUgbWlucyIpLA0KICAgICAgICAgc3RhcnRfaW50ZXJ2YWwxNSA9IGZsb29yX2RhdGUoeW1kX2htcyhzZXNzaW9uX3N0YXJ0X2R0KSwgdW5pdCA9ICIxNSBtaW5zIikpDQpnbGltcHNlKGRhdDIpDQoNCmRpc3RyaWJ1dGlvbiA8LSBkYXQyICU+JSANCiAgbXV0YXRlKGR0aW1lID0gZm9ybWF0KGFzLlBPU0lYY3Qoc3RhcnRfaW50ZXJ2YWwxNSksIGZvcm1hdD0iJUg6JU06JVMiKSkgJT4lIA0KICBncm91cF9ieShkdGltZSkgJT4lIA0KICBjb3VudCgpDQoNCmxlbmd0aCA8LSBkYXQyICU+JSANCiAgbXV0YXRlKGRob3VyID0gaG91cihzdGFydF9pbnRlcnZhbDE1KSwNCiAgICAgICAgIGxlbmd0aCA9IGVuZF9pbnRlcnZhbDE1IC0gc3RhcnRfaW50ZXJ2YWwxNSwNCiAgICAgICAgIHBhcmtpbmdfaG91ciA9IGFzLm51bWVyaWMobGVuZ3RoKS8zNjAwLA0KICAgICAgICAgY291bnQgPTEpICU+JSANCiAgZmlsdGVyKGRob3VyPD0xNyAmIGRob3VyPj05KSAlPiUgDQogIGRwbHlyOjpzZWxlY3QoZGhvdXIsIHBhcmtpbmdfaG91ciwgY291bnQpICU+JSANCiAgZ3JvdXBfYnkoZGhvdXIscGFya2luZ19ob3VyKSAlPiUgDQogIHN1bW1hcml6ZShjaG91ciA9IHN1bShjb3VudCkpDQpgYGANCg0KYGBge3J9DQojLS0tLS1TZWxlY3QgTnVtZXJpYyBWYXJpYWJsZXMtLS0tLQ0KbnVtZXJpY0NvbHVtbnMgPSB1bmxpc3QobGFwcGx5KHRyYWluRGF0YSwgaXMubnVtZXJpYykpDQp0cmFpbkRhdGFOdW1lcmljID0gdHJhaW5EYXRhWyxudW1lcmljQ29sdW1uc10gJT4lIA0KICBkcGx5cjo6c2VsZWN0KC1NVVNBX0lELC10b1ByZWRpY3QpJT4lIA0KICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lIA0KICBnYXRoZXIoa2V5LHZhbHVlLCAtcHJpY2UpDQoNCiMtLS0tLU1ha2Ugc2NhdHRlci1wbG90cyBvZiBhbGwgbnVtZXJpYyB2YXJpYWJsZXMtLS0tDQoNCmdncGxvdCh0cmFpbkRhdGFOdW1lcmljLCBhZXMoeCA9IHZhbHVlLCB5ID0gcHJpY2UpKSsNCiAgZ2VvbV9wb2ludChzaXplPS4wNSxhbHBoYT0wLjUpKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSBsbSwgc2U9Rixjb2xvdXIgPSAiI0RDOTg2RCIsc2l6ZT0wLjUpKw0KICBmYWNldF93cmFwKH5rZXksIHNjYWxlcyA9ICJmcmVlIixuY29sID0gOCkrDQogIHBsb3RUaGVtZSgpDQpgYGANCg0KYGBge3J9DQojIFBvc3NpYmxlIERpc3RyaWJ1dGlvbiBUcmFuc2Zvcm1hdGlvbg0KdHJhaW5EYXRhTnVtZXJpYzIgPSB0cmFpbkRhdGFbLG51bWVyaWNDb2x1bW5zXSAlPiUgDQogIGRwbHlyOjpzZWxlY3QoLU1VU0FfSUQsLXRvUHJlZGljdCklPiUNCiAgc3RfZHJvcF9nZW9tZXRyeSgpDQppPTENCnBhcihtZnJvdz1jKDY1LDEpKQ0KZm9yIChjb2x1bW4gaW4gdHJhaW5EYXRhTnVtZXJpYzIpIHsNCiAgbmFtZXMgPSBuYW1lcyh0cmFpbkRhdGFOdW1lcmljMikNCiAgbmFtZSA9IG5hbWVzW2k6aV0NCiAgcGFyKG1mcm93PWMoMSwyKSk7aGlzdChhcy5udW1lcmljKGNvbHVtbiksYnJlYWtzPTgwLG1haW49YyhuYW1lLCJCZWZvcmUiKSk7aGlzdChsb2coMSthcy5udW1lcmljKGNvbHVtbikpLGJyZWFrcz04MCxtYWluPWMobmFtZSwiQWZ0ZXIiKSkNCiAgaSA9IGkrMQ0KfQ0KZGV2Lm9mZigpDQpgYGANCg0KIyMgMi41LkltcG9ydCB0aW1lIGRhdGENCg0KIyMjIFRpbWUgdG9rZW4NCg0KaW1hZ2U6ICFbXShEOi9VcGVubi9VcGVubiBMZWMvMDUtTVVTQS01MDgvQXNzaWduLUZpbmFsL1BQVC9ERU1PIGZvciAxMjA5LzYucG5nKQ0KDQpIZXJlIHdlIHdpbGwgYW5hbHl6ZSBvdXIgYWxnb3JpdGhtIGZvciB0aW1lLCB0aW1lIHRva2VuLCB0aGUgYWJvdmUgZ3JhcGggaWxsdXN0cmF0ZXMgb3VyIGNhbGN1bGF0aW9uIHJ1bGVzLCBmb3IgYSBwYXJraW5nIHJlY29yZCBvbiBhIHBhcmtpbmcgbWV0ZXIsIHRoZSB0aW1lIGl0IG9jY3VwaWVzIGlzIHJvdW5kZWQgdG8gMTVtaW4gdGltZSBwZXJpb2QsIGZvciB0aGUgc2FtZSB0aW1lIHBlcmlvZCwgZGlmZmVyZW50IHBhcmtpbmcgc3BhY2VzIHVuZGVyIHRoZSBzYW1lIEdlb19pZCwgdGhlIGFib3ZlIHJvdW5kZWQgdGltZSB0b2tlbiBpcyBhZGRlZCB1cCwgd2UgZ2V0IHRoZSB0b3RhbCB0aW1lIHRva2VuIGZvciB0aGUgZmlyc3QgdGltZSBwZXJpb2QuDQoNCmBgYHtyIG9jY3VwYW5jeSwgZWNobz1GQUxTRX0NCiMjIFRoaXMgcGFydCB2YXJpYWJsZSBoYXMgYmVlbiBzYXZlZCBsb2NhbGx5DQpkYXQwOTEyIDwtIGRhdDIgJT4lDQogIG11dGF0ZShzdF9ob3VyID0gaG91cihzdGFydF9pbnRlcnZhbDE1KSwNCiAgICAgICAgIGVuZF9ob3VyID0gaG91cihlbmRfaW50ZXJ2YWwxNSkpICU+JSANCiAgZmlsdGVyKHN0X2hvdXI+PTkgJiBlbmRfaG91cjwxMikgJT4lIA0KICBtdXRhdGUobGVuZ3RoID0gZW5kX2ludGVydmFsMTUgLSBzdGFydF9pbnRlcnZhbDE1LA0KICAgICAgICAgdG9rZW5zID0gYXMubnVtZXJpYyhsZW5ndGggKS8gOTAwLA0KICAgICAgICAgdG9rZW5zXzAwID0gaWZlbHNlKHRva2VucyA+PSAxLCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18wMSA9IGlmZWxzZSh0b2tlbnMgPj0gMiwgMSwgMCksDQogICAgICAgICB0b2tlbnNfMDIgPSBpZmVsc2UodG9rZW5zID49IDMsIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzAzID0gaWZlbHNlKHRva2VucyA+PSA0LCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18wNCA9IGlmZWxzZSh0b2tlbnMgPj0gNSwgMSwgMCksDQogICAgICAgICB0b2tlbnNfMDUgPSBpZmVsc2UodG9rZW5zID49IDYsIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzA2ID0gaWZlbHNlKHRva2VucyA+PSA3LCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18wNyA9IGlmZWxzZSh0b2tlbnMgPj0gOCwgMSwgMCksDQogICAgICAgICB0b2tlbnNfMDggPSBpZmVsc2UodG9rZW5zID49IDksIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzA5ID0gaWZlbHNlKHRva2VucyA+PSAxMCwgMSwgMCksDQogICAgICAgICB0b2tlbnNfMTAgPSBpZmVsc2UodG9rZW5zID49IDExLCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18xMSA9IGlmZWxzZSh0b2tlbnMgPj0gMTIsIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzEyID0gaWZlbHNlKHRva2VucyA+PSAxMywgMSwgMCksDQogICAgICAgICB0b2tlbnNfMTMgPSBpZmVsc2UodG9rZW5zID49IDE0LCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18xNCA9IGlmZWxzZSh0b2tlbnMgPj0gMTUsIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzE1ID0gaWZlbHNlKHRva2VucyA+PSAxNiwgMSwgMCksDQogICAgICAgICB0b2tlbnNfMTYgPSBpZmVsc2UodG9rZW5zID49IDE3LCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18xNyA9IGlmZWxzZSh0b2tlbnMgPj0gMTgsIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzE4ID0gaWZlbHNlKHRva2VucyA+PSAxOSwgMSwgMCksDQogICAgICAgICB0b2tlbnNfMTkgPSBpZmVsc2UodG9rZW5zID49IDIwLCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18yMCA9IGlmZWxzZSh0b2tlbnMgPj0gMjEsIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzIxID0gaWZlbHNlKHRva2VucyA+PSAyMiwgMSwgMCksDQogICAgICAgICB0b2tlbnNfMjIgPSBpZmVsc2UodG9rZW5zID49IDIzLCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18yMyA9IGlmZWxzZSh0b2tlbnMgPj0gMjQsIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzI0ID0gaWZlbHNlKHRva2VucyA+PSAyNSwgMSwgMCksDQogICAgICAgICB0b2tlbnNfMjUgPSBpZmVsc2UodG9rZW5zID49IDI2LCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18yNiA9IGlmZWxzZSh0b2tlbnMgPj0gMjcsIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzI3ID0gaWZlbHNlKHRva2VucyA+PSAyOCwgMSwgMCksDQogICAgICAgICB0b2tlbnNfMjggPSBpZmVsc2UodG9rZW5zID49IDI5LCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18yOSA9IGlmZWxzZSh0b2tlbnMgPj0gMzAsIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzMwID0gaWZlbHNlKHRva2VucyA+PSAzMSwgMSwgMCksDQogICAgICAgICB0b2tlbnNfMzEgPSBpZmVsc2UodG9rZW5zID49IDMyLCAxLCAwKSwgICAgICAgIA0KICAgICAgICAgdG9rZW5zXzMyID0gaWZlbHNlKHRva2VucyA+PSAzMywgMSwgMCksDQogICAgICAgICB0b2tlbnNfMzMgPSBpZmVsc2UodG9rZW5zID49IDM0LCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18zNCA9IGlmZWxzZSh0b2tlbnMgPj0gMzUsIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzM1ID0gaWZlbHNlKHRva2VucyA+PSAzNiwgMSwgMCkpICU+JQ0KICBncm91cF9ieShzdGFydF9pbnRlcnZhbDE1LCBwb3N0X2lkKSAlPiUNCiAgc3VtbWFyaXplKHRva2Vuc18wMCA9IHN1bSh0b2tlbnNfMDApLA0KICAgICAgICAgICAgdG9rZW5zXzAxID0gc3VtKHRva2Vuc18wMSksDQogICAgICAgICAgICB0b2tlbnNfMDIgPSBzdW0odG9rZW5zXzAyKSwNCiAgICAgICAgICAgIHRva2Vuc18wMyA9IHN1bSh0b2tlbnNfMDMpLA0KICAgICAgICAgICAgdG9rZW5zXzA0ID0gc3VtKHRva2Vuc18wNCksDQogICAgICAgICAgICB0b2tlbnNfMDUgPSBzdW0odG9rZW5zXzA1KSwNCiAgICAgICAgICAgIHRva2Vuc18wNiA9IHN1bSh0b2tlbnNfMDYpLA0KICAgICAgICAgICAgdG9rZW5zXzA3ID0gc3VtKHRva2Vuc18wNyksDQogICAgICAgICAgICB0b2tlbnNfMDggPSBzdW0odG9rZW5zXzA4KSwNCiAgICAgICAgICAgIHRva2Vuc18wOSA9IHN1bSh0b2tlbnNfMDkpLA0KICAgICAgICAgICAgdG9rZW5zXzEwID0gc3VtKHRva2Vuc18xMCksDQogICAgICAgICAgICB0b2tlbnNfMTEgPSBzdW0odG9rZW5zXzExKSwNCiAgICAgICAgICAgIHRva2Vuc18xMiA9IHN1bSh0b2tlbnNfMTIpLA0KICAgICAgICAgICAgdG9rZW5zXzEzID0gc3VtKHRva2Vuc18xMyksDQogICAgICAgICAgICB0b2tlbnNfMTQgPSBzdW0odG9rZW5zXzE0KSwNCiAgICAgICAgICAgIHRva2Vuc18xNSA9IHN1bSh0b2tlbnNfMTUpLA0KICAgICAgICAgICAgdG9rZW5zXzE2ID0gc3VtKHRva2Vuc18xNiksDQogICAgICAgICAgICB0b2tlbnNfMTcgPSBzdW0odG9rZW5zXzE3KSwNCiAgICAgICAgICAgIHRva2Vuc18xOCA9IHN1bSh0b2tlbnNfMTgpLA0KICAgICAgICAgICAgdG9rZW5zXzE5ID0gc3VtKHRva2Vuc18xOSksDQogICAgICAgICAgICB0b2tlbnNfMjAgPSBzdW0odG9rZW5zXzIwKSwNCiAgICAgICAgICAgIHRva2Vuc18yMSA9IHN1bSh0b2tlbnNfMjEpLA0KICAgICAgICAgICAgdG9rZW5zXzIyID0gc3VtKHRva2Vuc18yMiksDQogICAgICAgICAgICB0b2tlbnNfMjMgPSBzdW0odG9rZW5zXzIzKSwNCiAgICAgICAgICAgIHRva2Vuc18yNCA9IHN1bSh0b2tlbnNfMjQpLA0KICAgICAgICAgICAgdG9rZW5zXzI1ID0gc3VtKHRva2Vuc18yNSksDQogICAgICAgICAgICB0b2tlbnNfMjYgPSBzdW0odG9rZW5zXzI2KSwNCiAgICAgICAgICAgIHRva2Vuc18yNyA9IHN1bSh0b2tlbnNfMjcpLA0KICAgICAgICAgICAgdG9rZW5zXzI4ID0gc3VtKHRva2Vuc18yOCksDQogICAgICAgICAgICB0b2tlbnNfMjkgPSBzdW0odG9rZW5zXzI5KSwNCiAgICAgICAgICAgIHRva2Vuc18zMCA9IHN1bSh0b2tlbnNfMzApLA0KICAgICAgICAgICAgdG9rZW5zXzMxID0gc3VtKHRva2Vuc18zMSksDQogICAgICAgICAgICB0b2tlbnNfMzIgPSBzdW0odG9rZW5zXzMyKSwNCiAgICAgICAgICAgIHRva2Vuc18zMyA9IHN1bSh0b2tlbnNfMzMpLA0KICAgICAgICAgICAgdG9rZW5zXzM0ID0gc3VtKHRva2Vuc18zNCksDQogICAgICAgICAgICB0b2tlbnNfMzUgPSBzdW0odG9rZW5zXzM1KSkNCg0Kc3R1ZHkucGFuZWwwOTEyIDwtIA0KICBleHBhbmQuZ3JpZChzdGFydF9pbnRlcnZhbDE1PXVuaXF1ZShkYXQyJHN0YXJ0X2ludGVydmFsMTUpLCANCiAgICAgICAgICAgICAgcG9zdF9pZCA9IHVuaXF1ZShkYXQyJHBvc3RfaWQpKSAlPiUNCiAgbXV0YXRlKGRvdHkgPSB5ZGF5KHN0YXJ0X2ludGVydmFsMTUpKSAlPiUNCiAgbGVmdF9qb2luKC4sIGRhdDA5MTIpDQoNCnRyYW5zYWN0aW9uX3BhbmVsMV8xIDwtIHN0dWR5LnBhbmVsMDkxMiAlPiUNCiAgcmVwbGFjZShpcy5uYSguKSwgMCkgJT4lDQogIGFycmFuZ2Uoc3RhcnRfaW50ZXJ2YWwxNSkgJT4lDQogIGdyb3VwX2J5KHBvc3RfaWQsIGRvdHkpICU+JQ0KICBtdXRhdGUobGFnMDEgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18wMSkpID09IEZBTFNFLCBsYWcodG9rZW5zXzAxKSwgMCksDQogICAgICAgICBsYWcwMiA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzAyLDIpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18wMiwyKSwgMCksDQogICAgICAgICBsYWcwMyA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzAzLDMpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18wMywzKSwgMCksDQogICAgICAgICBsYWcwNCA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzA0LDQpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18wNCw0KSwgMCksDQogICAgICAgICBsYWcwNSA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzA1LDUpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18wNSw1KSwgMCksDQogICAgICAgICBsYWcwNiA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzA2LDYpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18wNiw2KSwgMCksDQogICAgICAgICBsYWcwNyA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzA3LDcpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18wNyw3KSwgMCksDQogICAgICAgICBsYWcwOCA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzA4LDgpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18wOCw4KSwgMCksDQogICAgICAgICBsYWcwOSA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzA4LDkpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18wOSw5KSwgMCksDQogICAgICAgICBsYWcxMCA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzEwLDEwKSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMTAsMTApLCAwKSwNCiAgICAgICAgIGxhZzExID0gaWZlbHNlKGlzLm5hKGxhZyh0b2tlbnNfMTEsMTEpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18xMSwxMSksIDApLA0KICAgICAgICAgbGFnMTIgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18xMiwxMikpID09IEZBTFNFLCBsYWcodG9rZW5zXzEyLDEyKSwgMCksDQogICAgICAgICBsYWcxMyA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzEzLDEzKSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMTMsMTMpLCAwKSwNCiAgICAgICAgIGxhZzE0ID0gaWZlbHNlKGlzLm5hKGxhZyh0b2tlbnNfMTQsMTQpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18xNCwxNCksIDApLA0KICAgICAgICAgbGFnMTUgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18xNSwxNSkpID09IEZBTFNFLCBsYWcodG9rZW5zXzE1LDE1KSwgMCksDQogICAgICAgICBsYWcxNiA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzE2LDE2KSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMTYsMTYpLCAwKSwNCiAgICAgICAgIGxhZzE3ID0gaWZlbHNlKGlzLm5hKGxhZyh0b2tlbnNfMTcsMTcpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18xNywxNyksIDApLA0KICAgICAgICAgbGFnMTggPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18xOCwxOCkpID09IEZBTFNFLCBsYWcodG9rZW5zXzE4LDE4KSwgMCksDQogICAgICAgICBsYWcxOSA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzE5LDE5KSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMTksMTkpLCAwKSwNCiAgICAgICAgIGxhZzIwID0gaWZlbHNlKGlzLm5hKGxhZyh0b2tlbnNfMjAsMjApKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18yMCwyMCksIDApLA0KICAgICAgICAgbGFnMjEgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18yMSwyMSkpID09IEZBTFNFLCBsYWcodG9rZW5zXzIxLDIxKSwgMCksDQogICAgICAgICBsYWcyMiA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzIyLDIyKSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMjIsMjIpLCAwKSwNCiAgICAgICAgIGxhZzIzID0gaWZlbHNlKGlzLm5hKGxhZyh0b2tlbnNfMjMsMjMpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18yMywyMyksIDApLA0KICAgICAgICAgbGFnMjQgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18yNCwzNCkpID09IEZBTFNFLCBsYWcodG9rZW5zXzI0LDI0KSwgMCksDQogICAgICAgICBsYWcyNSA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzI1LDI1KSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMjUsMjUpLCAwKSwNCiAgICAgICAgIGxhZzI2ID0gaWZlbHNlKGlzLm5hKGxhZyh0b2tlbnNfMjYsMjYpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18yNiwyNiksIDApLA0KICAgICAgICAgbGFnMjcgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18yNywyNykpID09IEZBTFNFLCBsYWcodG9rZW5zXzI3LDI3KSwgMCksDQogICAgICAgICBsYWcyOCA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzI4LDI4KSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMjgsMjgpLCAwKSwNCiAgICAgICAgIGxhZzI5ID0gaWZlbHNlKGlzLm5hKGxhZyh0b2tlbnNfMjksMjkpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18yOSwyOSksIDApLA0KICAgICAgICAgbGFnMzAgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18zMCwzMCkpID09IEZBTFNFLCBsYWcodG9rZW5zXzMwLDMwKSwgMCksDQogICAgICAgICBsYWczMSA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzMxLDMxKSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMzEsMzEpLCAwKSwNCiAgICAgICAgIGxhZzMyID0gaWZlbHNlKGlzLm5hKGxhZyh0b2tlbnNfMzIsMzIpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18zMiwzMiksIDApLA0KICAgICAgICAgbGFnMzMgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18zMywzMykpID09IEZBTFNFLCBsYWcodG9rZW5zXzMzLDMzKSwgMCksDQogICAgICAgICBsYWczNCA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzM0LDM0KSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMzQsMzQpLCAwKSwNCiAgICAgICAgIGxhZzM1ID0gaWZlbHNlKGlzLm5hKGxhZyh0b2tlbnNfMzUsMzUpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18zNSwzNSksIDApKSAlPiUNCiAgbXV0YXRlKG9jY3VwYW5jeSA9IHRva2Vuc18wMCArIGxhZzAxICsgbGFnMDIgKyBsYWcwMysgbGFnMDQgKyBsYWcwNSArIA0KICAgICAgICAgICBsYWcwNiArIGxhZzA3ICsgbGFnMDgrIGxhZzA5ICsgbGFnMTAgKyBsYWcxMSsgbGFnMTIrIA0KICAgICAgICAgICBsYWcxMysgbGFnMTYrIGxhZzE5KyBsYWcyMSsgbGFnMjQrIGxhZzI2KyBsYWcyOCsgbGFnMzArIGxhZzMyKyANCiAgICAgICAgICAgbGFnMTQrIGxhZzE3KyBsYWcyMCsgbGFnMjIrIGxhZzI1KyBsYWcyNysgbGFnMjkrIGxhZzMxKyBsYWczMysgDQogICAgICAgICAgIGxhZzE1KyBsYWcxOCsgbGFnMjErIGxhZzIzKyBsYWczNCsgbGFnMzUpICU+JQ0KICBmaWx0ZXIoaXMubmEob2NjdXBhbmN5KSA9PSBGQUxTRSkgJT4lDQogIHNlbGVjdChzdGFydF9pbnRlcnZhbDE1LCBwb3N0X2lkLCBvY2N1cGFuY3kpDQoNCg0KdHJhbnNhY3Rpb24wOTEyIDwtIHJiaW5kKHRyYW5zYWN0aW9uX3BhbmVsMSwgdHJhbnNhY3Rpb25fcGFuZWwxXzEpDQoJIyNzYXZlLmltYWdlKGZpbGU9J0Q6L1VQZW5uL0ZhbGwyMDIyLzUwODBQb2xpY3kvRmluYWwvLlJEYXRhJykNCg0KIyNwZXJpb2QyDQoNCmRhdDEyMTUgPC0gZGF0MiAlPiUNCiAgbXV0YXRlKHN0X2hvdXIgPSBob3VyKHN0YXJ0X2ludGVydmFsMTUpLA0KICAgICAgICAgZW5kX2hvdXIgPSBob3VyKGVuZF9pbnRlcnZhbDE1KSkgJT4lIA0KICBmaWx0ZXIoc3RfaG91cj49MTIgJiBlbmRfaG91cjwxNSkgJT4lIA0KICBtdXRhdGUobGVuZ3RoID0gZW5kX2ludGVydmFsMTUgLSBzdGFydF9pbnRlcnZhbDE1LA0KICAgICAgICAgdG9rZW5zID0gYXMubnVtZXJpYyhsZW5ndGggKS8gOTAwLA0KICAgICAgICAgdG9rZW5zXzAwID0gaWZlbHNlKHRva2VucyA+PSAxLCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18wMSA9IGlmZWxzZSh0b2tlbnMgPj0gMiwgMSwgMCksDQogICAgICAgICB0b2tlbnNfMDIgPSBpZmVsc2UodG9rZW5zID49IDMsIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzAzID0gaWZlbHNlKHRva2VucyA+PSA0LCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18wNCA9IGlmZWxzZSh0b2tlbnMgPj0gNSwgMSwgMCksDQogICAgICAgICB0b2tlbnNfMDUgPSBpZmVsc2UodG9rZW5zID49IDYsIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzA2ID0gaWZlbHNlKHRva2VucyA+PSA3LCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18wNyA9IGlmZWxzZSh0b2tlbnMgPj0gOCwgMSwgMCksDQogICAgICAgICB0b2tlbnNfMDggPSBpZmVsc2UodG9rZW5zID49IDksIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzA5ID0gaWZlbHNlKHRva2VucyA+PSAxMCwgMSwgMCksDQogICAgICAgICB0b2tlbnNfMTAgPSBpZmVsc2UodG9rZW5zID49IDExLCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18xMSA9IGlmZWxzZSh0b2tlbnMgPj0gMTIsIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzEyID0gaWZlbHNlKHRva2VucyA+PSAxMywgMSwgMCksDQogICAgICAgICB0b2tlbnNfMTMgPSBpZmVsc2UodG9rZW5zID49IDE0LCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18xNCA9IGlmZWxzZSh0b2tlbnMgPj0gMTUsIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzE1ID0gaWZlbHNlKHRva2VucyA+PSAxNiwgMSwgMCksDQogICAgICAgICB0b2tlbnNfMTYgPSBpZmVsc2UodG9rZW5zID49IDE3LCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18xNyA9IGlmZWxzZSh0b2tlbnMgPj0gMTgsIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzE4ID0gaWZlbHNlKHRva2VucyA+PSAxOSwgMSwgMCksDQogICAgICAgICB0b2tlbnNfMTkgPSBpZmVsc2UodG9rZW5zID49IDIwLCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18yMCA9IGlmZWxzZSh0b2tlbnMgPj0gMjEsIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzIxID0gaWZlbHNlKHRva2VucyA+PSAyMiwgMSwgMCksDQogICAgICAgICB0b2tlbnNfMjIgPSBpZmVsc2UodG9rZW5zID49IDIzLCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18yMyA9IGlmZWxzZSh0b2tlbnMgPj0gMjQsIDEsIDApKSAlPiUNCiAgZ3JvdXBfYnkoc3RhcnRfaW50ZXJ2YWwxNSwgcG9zdF9pZCkgJT4lDQogIHN1bW1hcml6ZSh0b2tlbnNfMDAgPSBzdW0odG9rZW5zXzAwKSwNCiAgICAgICAgICAgIHRva2Vuc18wMSA9IHN1bSh0b2tlbnNfMDEpLA0KICAgICAgICAgICAgdG9rZW5zXzAyID0gc3VtKHRva2Vuc18wMiksDQogICAgICAgICAgICB0b2tlbnNfMDMgPSBzdW0odG9rZW5zXzAzKSwNCiAgICAgICAgICAgIHRva2Vuc18wNCA9IHN1bSh0b2tlbnNfMDQpLA0KICAgICAgICAgICAgdG9rZW5zXzA1ID0gc3VtKHRva2Vuc18wNSksDQogICAgICAgICAgICB0b2tlbnNfMDYgPSBzdW0odG9rZW5zXzA2KSwNCiAgICAgICAgICAgIHRva2Vuc18wNyA9IHN1bSh0b2tlbnNfMDcpLA0KICAgICAgICAgICAgdG9rZW5zXzA4ID0gc3VtKHRva2Vuc18wOCksDQogICAgICAgICAgICB0b2tlbnNfMDkgPSBzdW0odG9rZW5zXzA5KSwNCiAgICAgICAgICAgIHRva2Vuc18xMCA9IHN1bSh0b2tlbnNfMTApLA0KICAgICAgICAgICAgdG9rZW5zXzExID0gc3VtKHRva2Vuc18xMSksDQogICAgICAgICAgICB0b2tlbnNfMTIgPSBzdW0odG9rZW5zXzEyKSwNCiAgICAgICAgICAgIHRva2Vuc18xMyA9IHN1bSh0b2tlbnNfMTMpLA0KICAgICAgICAgICAgdG9rZW5zXzE0ID0gc3VtKHRva2Vuc18xNCksDQogICAgICAgICAgICB0b2tlbnNfMTUgPSBzdW0odG9rZW5zXzE1KSwNCiAgICAgICAgICAgIHRva2Vuc18xNiA9IHN1bSh0b2tlbnNfMTYpLA0KICAgICAgICAgICAgdG9rZW5zXzE3ID0gc3VtKHRva2Vuc18xNyksDQogICAgICAgICAgICB0b2tlbnNfMTggPSBzdW0odG9rZW5zXzE4KSwNCiAgICAgICAgICAgIHRva2Vuc18xOSA9IHN1bSh0b2tlbnNfMTkpLA0KICAgICAgICAgICAgdG9rZW5zXzIwID0gc3VtKHRva2Vuc18yMCksDQogICAgICAgICAgICB0b2tlbnNfMjEgPSBzdW0odG9rZW5zXzIxKSwNCiAgICAgICAgICAgIHRva2Vuc18yMiA9IHN1bSh0b2tlbnNfMjIpLA0KICAgICAgICAgICAgdG9rZW5zXzIzID0gc3VtKHRva2Vuc18yMykpDQoNCnN0dWR5LnBhbmVsMTIxNSA8LSANCiAgZXhwYW5kLmdyaWQoc3RhcnRfaW50ZXJ2YWwxNT11bmlxdWUoZGF0MTIxNSRzdGFydF9pbnRlcnZhbDE1KSwgDQogICAgICAgICAgICAgIHBvc3RfaWQgPSB1bmlxdWUoZGF0MTIxNSRwb3N0X2lkKSkgJT4lDQogIG11dGF0ZShzdF9ob3VyID0gaG91cihzdGFydF9pbnRlcnZhbDE1KSkgJT4lIA0KICBmaWx0ZXIoc3RfaG91cj49MTIpICU+JSANCiAgbXV0YXRlKGRvdHkgPSB5ZGF5KHN0YXJ0X2ludGVydmFsMTUpKSAlPiUNCiAgbGVmdF9qb2luKC4sIGRhdDEyMTUpDQoNCnRyYW5zYWN0aW9uX3BhbmVsMiA8LSBzdHVkeS5wYW5lbDEyMTUgJT4lDQogIHJlcGxhY2UoaXMubmEoLiksIDApICU+JQ0KICBhcnJhbmdlKHN0YXJ0X2ludGVydmFsMTUpICU+JQ0KICBncm91cF9ieShwb3N0X2lkLCBkb3R5KSAlPiUNCiAgbXV0YXRlKGxhZzAxID0gaWZlbHNlKGlzLm5hKGxhZyh0b2tlbnNfMDEpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18wMSksIDApLA0KICAgICAgICAgbGFnMDIgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18wMiwyKSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMDIsMiksIDApLA0KICAgICAgICAgbGFnMDMgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18wMywzKSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMDMsMyksIDApLA0KICAgICAgICAgbGFnMDQgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18wNCw0KSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMDQsNCksIDApLA0KICAgICAgICAgbGFnMDUgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18wNSw1KSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMDUsNSksIDApLA0KICAgICAgICAgbGFnMDYgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18wNiw2KSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMDYsNiksIDApLA0KICAgICAgICAgbGFnMDcgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18wNyw3KSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMDcsNyksIDApLA0KICAgICAgICAgbGFnMDggPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18wOCw4KSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMDgsOCksIDApLA0KICAgICAgICAgbGFnMDkgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18wOCw5KSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMDksOSksIDApLA0KICAgICAgICAgbGFnMTAgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18xMCwxMCkpID09IEZBTFNFLCBsYWcodG9rZW5zXzEwLDEwKSwgMCksDQogICAgICAgICBsYWcxMSA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzExLDExKSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMTEsMTEpLCAwKSwNCiAgICAgICAgIGxhZzEyID0gaWZlbHNlKGlzLm5hKGxhZyh0b2tlbnNfMTIsMTIpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18xMiwxMiksIDApLA0KICAgICAgICAgbGFnMTMgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18xMywxMykpID09IEZBTFNFLCBsYWcodG9rZW5zXzEzLDEzKSwgMCksDQogICAgICAgICBsYWcxNCA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzE0LDE0KSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMTQsMTQpLCAwKSwNCiAgICAgICAgIGxhZzE1ID0gaWZlbHNlKGlzLm5hKGxhZyh0b2tlbnNfMTUsMTUpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18xNSwxNSksIDApLA0KICAgICAgICAgbGFnMTYgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18xNiwxNikpID09IEZBTFNFLCBsYWcodG9rZW5zXzE2LDE2KSwgMCksDQogICAgICAgICBsYWcxNyA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzE3LDE3KSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMTcsMTcpLCAwKSwNCiAgICAgICAgIGxhZzE4ID0gaWZlbHNlKGlzLm5hKGxhZyh0b2tlbnNfMTgsMTgpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18xOCwxOCksIDApLA0KICAgICAgICAgbGFnMTkgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18xOSwxOSkpID09IEZBTFNFLCBsYWcodG9rZW5zXzE5LDE5KSwgMCksDQogICAgICAgICBsYWcyMCA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzIwLDIwKSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMjAsMjApLCAwKSwNCiAgICAgICAgIGxhZzIxID0gaWZlbHNlKGlzLm5hKGxhZyh0b2tlbnNfMjEsMjEpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18yMSwyMSksIDApLA0KICAgICAgICAgbGFnMjIgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18yMiwyMikpID09IEZBTFNFLCBsYWcodG9rZW5zXzIyLDIyKSwgMCksDQogICAgICAgICBsYWcyMyA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzIzLDIzKSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMjMsMjMpLCAwKSkgJT4lDQogIG11dGF0ZShvY2N1cGFuY3kgPSB0b2tlbnNfMDAgKyBsYWcwMSArIGxhZzAyICsgbGFnMDMrIGxhZzA0ICsgbGFnMDUgKyANCiAgICAgICAgICAgbGFnMDYgKyBsYWcwNyArIGxhZzA4KyBsYWcwOSArIGxhZzEwICsgbGFnMTErIGxhZzEyKyANCiAgICAgICAgICAgbGFnMTMrIGxhZzE2KyBsYWcxOSsgbGFnMjErICANCiAgICAgICAgICAgbGFnMTQrIGxhZzE3KyBsYWcyMCsgbGFnMjIrICANCiAgICAgICAgICAgbGFnMTUrIGxhZzE4KyBsYWcyMSsgbGFnMjMpICU+JQ0KICBmaWx0ZXIoaXMubmEob2NjdXBhbmN5KSA9PSBGQUxTRSkgJT4lDQogIHNlbGVjdChzdGFydF9pbnRlcnZhbDE1LCBwb3N0X2lkLCBvY2N1cGFuY3kpDQoNCgkjI3NhdmUuaW1hZ2UoZmlsZT0nRDovVVBlbm4vRmFsbDIwMjIvNTA4MFBvbGljeS9GaW5hbC8uUkRhdGEnKQ0KDQojI3BlcmlvZDMNCg0KZGF0MTUxOCA8LSBkYXQyICU+JQ0KICBtdXRhdGUoc3RfaG91ciA9IGhvdXIoc3RhcnRfaW50ZXJ2YWwxNSksDQogICAgICAgICBlbmRfaG91ciA9IGhvdXIoZW5kX2ludGVydmFsMTUpKSAlPiUgDQogIGZpbHRlcihzdF9ob3VyPj0xNSAmIGVuZF9ob3VyPDE4KSAlPiUgDQogIG11dGF0ZShsZW5ndGggPSBlbmRfaW50ZXJ2YWwxNSAtIHN0YXJ0X2ludGVydmFsMTUsDQogICAgICAgICB0b2tlbnMgPSBhcy5udW1lcmljKGxlbmd0aCApLyA5MDAsDQogICAgICAgICB0b2tlbnNfMDAgPSBpZmVsc2UodG9rZW5zID49IDEsIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzAxID0gaWZlbHNlKHRva2VucyA+PSAyLCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18wMiA9IGlmZWxzZSh0b2tlbnMgPj0gMywgMSwgMCksDQogICAgICAgICB0b2tlbnNfMDMgPSBpZmVsc2UodG9rZW5zID49IDQsIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzA0ID0gaWZlbHNlKHRva2VucyA+PSA1LCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18wNSA9IGlmZWxzZSh0b2tlbnMgPj0gNiwgMSwgMCksDQogICAgICAgICB0b2tlbnNfMDYgPSBpZmVsc2UodG9rZW5zID49IDcsIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzA3ID0gaWZlbHNlKHRva2VucyA+PSA4LCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18wOCA9IGlmZWxzZSh0b2tlbnMgPj0gOSwgMSwgMCksDQogICAgICAgICB0b2tlbnNfMDkgPSBpZmVsc2UodG9rZW5zID49IDEwLCAxLCAwKSwNCiAgICAgICAgIHRva2Vuc18xMCA9IGlmZWxzZSh0b2tlbnMgPj0gMTEsIDEsIDApLA0KICAgICAgICAgdG9rZW5zXzExID0gaWZlbHNlKHRva2VucyA+PSAxMiwgMSwgMCkpICU+JQ0KICBncm91cF9ieShzdGFydF9pbnRlcnZhbDE1LCBwb3N0X2lkKSAlPiUNCiAgc3VtbWFyaXplKHRva2Vuc18wMCA9IHN1bSh0b2tlbnNfMDApLA0KICAgICAgICAgICAgdG9rZW5zXzAxID0gc3VtKHRva2Vuc18wMSksDQogICAgICAgICAgICB0b2tlbnNfMDIgPSBzdW0odG9rZW5zXzAyKSwNCiAgICAgICAgICAgIHRva2Vuc18wMyA9IHN1bSh0b2tlbnNfMDMpLA0KICAgICAgICAgICAgdG9rZW5zXzA0ID0gc3VtKHRva2Vuc18wNCksDQogICAgICAgICAgICB0b2tlbnNfMDUgPSBzdW0odG9rZW5zXzA1KSwNCiAgICAgICAgICAgIHRva2Vuc18wNiA9IHN1bSh0b2tlbnNfMDYpLA0KICAgICAgICAgICAgdG9rZW5zXzA3ID0gc3VtKHRva2Vuc18wNyksDQogICAgICAgICAgICB0b2tlbnNfMDggPSBzdW0odG9rZW5zXzA4KSwNCiAgICAgICAgICAgIHRva2Vuc18wOSA9IHN1bSh0b2tlbnNfMDkpLA0KICAgICAgICAgICAgdG9rZW5zXzEwID0gc3VtKHRva2Vuc18xMCksDQogICAgICAgICAgICB0b2tlbnNfMTEgPSBzdW0odG9rZW5zXzExKSkNCg0Kc3R1ZHkucGFuZWwxNTE4IDwtIA0KICBleHBhbmQuZ3JpZChzdGFydF9pbnRlcnZhbDE1PXVuaXF1ZShkYXQxNTE4JHN0YXJ0X2ludGVydmFsMTUpLCANCiAgICAgICAgICAgICAgcG9zdF9pZCA9IHVuaXF1ZShkYXQxNTE4JHBvc3RfaWQpKSAlPiUNCiAgbXV0YXRlKHN0X2hvdXIgPSBob3VyKHN0YXJ0X2ludGVydmFsMTUpKSAlPiUgDQogIGZpbHRlcihzdF9ob3VyPj0xNSkgJT4lIA0KICBtdXRhdGUoZG90eSA9IHlkYXkoc3RhcnRfaW50ZXJ2YWwxNSkpICU+JQ0KICBsZWZ0X2pvaW4oLiwgZGF0MTUxOCkNCg0KdHJhbnNhY3Rpb25fcGFuZWwzIDwtIHN0dWR5LnBhbmVsMTUxOCAlPiUNCiAgcmVwbGFjZShpcy5uYSguKSwgMCkgJT4lDQogIGFycmFuZ2Uoc3RhcnRfaW50ZXJ2YWwxNSkgJT4lDQogIGdyb3VwX2J5KHBvc3RfaWQsIGRvdHkpICU+JQ0KICBtdXRhdGUobGFnMDEgPSBpZmVsc2UoaXMubmEobGFnKHRva2Vuc18wMSkpID09IEZBTFNFLCBsYWcodG9rZW5zXzAxKSwgMCksDQogICAgICAgICBsYWcwMiA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzAyLDIpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18wMiwyKSwgMCksDQogICAgICAgICBsYWcwMyA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzAzLDMpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18wMywzKSwgMCksDQogICAgICAgICBsYWcwNCA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzA0LDQpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18wNCw0KSwgMCksDQogICAgICAgICBsYWcwNSA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzA1LDUpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18wNSw1KSwgMCksDQogICAgICAgICBsYWcwNiA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzA2LDYpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18wNiw2KSwgMCksDQogICAgICAgICBsYWcwNyA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzA3LDcpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18wNyw3KSwgMCksDQogICAgICAgICBsYWcwOCA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzA4LDgpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18wOCw4KSwgMCksDQogICAgICAgICBsYWcwOSA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzA4LDkpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18wOSw5KSwgMCksDQogICAgICAgICBsYWcxMCA9IGlmZWxzZShpcy5uYShsYWcodG9rZW5zXzEwLDEwKSkgPT0gRkFMU0UsIGxhZyh0b2tlbnNfMTAsMTApLCAwKSwNCiAgICAgICAgIGxhZzExID0gaWZlbHNlKGlzLm5hKGxhZyh0b2tlbnNfMTEsMTEpKSA9PSBGQUxTRSwgbGFnKHRva2Vuc18xMSwxMSksIDApKSAlPiUNCiAgbXV0YXRlKG9jY3VwYW5jeSA9IHRva2Vuc18wMCArIGxhZzAxICsgbGFnMDIgKyBsYWcwMysgbGFnMDQgKyBsYWcwNSArIA0KICAgICAgICAgICBsYWcwNiArIGxhZzA3ICsgbGFnMDgrIGxhZzA5ICsgbGFnMTAgKyBsYWcxMSkgJT4lDQogZmlsdGVyKGlzLm5hKG9jY3VwYW5jeSkgPT0gRkFMU0UpICU+JQ0KICBzZWxlY3Qoc3RhcnRfaW50ZXJ2YWwxNSwgcG9zdF9pZCwgb2NjdXBhbmN5KQ0KDQoJIyNzYXZlLmltYWdlKGZpbGU9J0Q6L1VQZW5uL0ZhbGwyMDIyLzUwODBQb2xpY3kvRmluYWwvLlJEYXRhJykNCg0KdHJhbnNhY3Rpb25fcGFuZWwxNTE4IDwtIHRyYW5zYWN0aW9uX3BhbmVsMyAlPiUgDQogIGxlZnRfam9pbihkYXQgJT4lIHNlbGVjdChwb3N0X2lkLCBzdHJlZXRfYmxvY2spICU+JSANCiAgdW5pcXVlKCkpDQoNCg0KdHJhbnNhY3Rpb25fcGFuZWwgPC0gdHJhbnNhY3Rpb25fcGFuZWwzICU+JSANCiAgZnVsbF9qb2luKHRyYW5zYWN0aW9uMDkxMiwgYnkgPSBjKCJzdGFydF9pbnRlcnZhbDE1IiwgInBvc3RfaWQiKSklPiUgDQogIGZ1bGxfam9pbih0cmFuc2FjdGlvbl9wYW5lbDIsIGJ5ID0gYygic3RhcnRfaW50ZXJ2YWwxNSIsICJwb3N0X2lkIikpDQoNCnRyYW5zYWN0aW9uX3BhbmVsW2lzLm5hKHRyYW5zYWN0aW9uX3BhbmVsKV0gPC0gMA0KDQpwYW5lbF9qb2luIDwtIHRyYW5zYWN0aW9uX3BhbmVsICU+JSANCiAgbXV0YXRlKG9jY3VwYW5jeSA9IG9jY3VwYW5jeS54K29jY3VwYW5jeS55K29jY3VwYW5jeSwNCiAgICAgICAgIGRvdHkgPSBkb3R5LngrZG90eS55K2RvdHkpICU+JSANCiAgc2VsZWN0KHBvc3RfaWQsIHN0YXJ0X2ludGVydmFsMTUsIG9jY3VwYW5jeSklPiUgDQogIGxlZnRfam9pbihkYXQyICU+JSBzZWxlY3QocG9zdF9pZCwgc3RyZWV0X2Jsb2NrKSAlPiUgDQogIHVuaXF1ZSgpKQ0KCSMjc2F2ZS5pbWFnZShmaWxlPSdEOi9VUGVubi9GYWxsMjAyMi81MDgwUG9saWN5L0ZpbmFsLy5SRGF0YScpDQoNCmBgYA0KDQpgYGB7cn0NCmFkZF9yYXRlIDwtIHBhcmtpbmdfcmF0ZSAlPiUgDQogIGdyb3VwX2J5KHN0cmVldF9ibG9jaykgJT4lIA0KICBzdW1tYXJpemUocmF0ZT0gbWVhbihyYXRlKSkNCg0KcGFuZWxfc3RfYmxvY2sgPC0gcGFuZWxfam9pbiAlPiUNCiAgZmlsdGVyKG9jY3VwYW5jeTw9MSkgJT4lIA0KICBtdXRhdGUoY291bnQ9MSkgJT4lIA0KICBncm91cF9ieShzdHJlZXRfYmxvY2ssc3RhcnRfaW50ZXJ2YWwxNSkgJT4lIA0KICBzdW1tYXJpemUob2NjX3NwYWNlID0gc3VtKG9jY3VwYW5jeSksDQogICAgICAgICAgICBhbGxfc3BhY2UgPSBzdW0oY291bnQpKSAlPiUgDQogIG11dGF0ZShvY2N1cGFuY3kgPSBvY2Nfc3BhY2UvYWxsX3NwYWNlKSAlPiUgDQogIGRwbHlyOjpzZWxlY3QoLW9jY19zcGFjZSwgLWFsbF9zcGFjZSkgJT4lIA0KICBsZWZ0X2pvaW4oYWRkX3JhdGUpDQoNCg0KcHJlZGljdG9yc190ZXN0IDwtIHBhbmVsX2pvaW4gJT4lIA0KICAgICAgICAgICAgICBjb3VudChwb3N0X2lkKQ0KDQpwcmVkaWN0b3JzIDwtIHRyYWluRGF0YTEgJT4lIA0KICBsZWZ0X2pvaW4ocGFuZWxfam9pbiAlPiUgDQogICAgICAgICAgICAgIGdyb3VwX2J5KHBvc3RfaWQsIHN0cmVldF9ibG9jaykpICU+JQ0KICBuYS5vbWl0KCkNCg0KcHJlZGljdG9ycyA8LSBwcmVkaWN0b3JzICU+JSANCiAgZ3JvdXBfYnkoc3RyZWV0X2Jsb2NrKSAlPiUgDQogIHN1bW1hcml6ZShHRU9JRD1maXJzdChHRU9JRC54KSwNCiAgICAgICAgICAgIHBhcmtDb3VudD1tZWFuKHBhcmtDb3VudCksDQogICAgICAgICAgICBwYXJrQXJlYT1tZWFuKHBhcmtBcmVhKSwNCiAgICAgICAgICAgIHJlc3RhdXJhbnRzQ291bnQ9bWVhbihyZXN0YXVyYW50c0NvdW50KSwNCiAgICAgICAgICAgIHJlc3RhdXJhbnRzX25uND1tZWFuKHJlc3RhdXJhbnRzX25uNCksDQogICAgICAgICAgICBzY2hvb2xDb3VudD1tZWFuKHNjaG9vbENvdW50KSwNCiAgICAgICAgICAgIHNjaG9vbHNfbm40PW1lYW4oc2Nob29sc19ubjQpLA0KICAgICAgICAgICAgdW5pdmVyc2l0aWVzQ291bnQ9bWVhbih1bml2ZXJzaXRpZXNDb3VudCksDQogICAgICAgICAgICBwYXJraW5nQ291bnQ9bWVhbihwYXJraW5nQ291bnQpLA0KICAgICAgICAgICAgcGFya2luZ0FyZWE9bWVhbihwYXJraW5nQXJlYSksDQogICAgICAgICAgICBwYXJraW5nX25uND1tZWFuKHBhcmtpbmdfbm40KSwNCiAgICAgICAgICAgIGNsaW5pY3Nfbm40PW1lYW4oY2xpbmljc19ubjQpLA0KICAgICAgICAgICAgaG9zcGl0YWxzX25uND1tZWFuKGhvc3BpdGFsc19ubjQpLA0KICAgICAgICAgICAgY2luZW1hc0NvdW50PW1lYW4oY2luZW1hc0NvdW50KSwNCiAgICAgICAgICAgIHN0YWRpdW1zQ291bnQ9bWVhbihzdGFkaXVtc0NvdW50KSwNCiAgICAgICAgICAgIGNvbW1lcmNlX25uND1tZWFuKGNvbW1lcmNlX25uNCksDQogICAgICAgICAgICBjb21tZXJjZUNvdW50PW1lYW4oY29tbWVyY2VDb3VudCksDQogICAgICAgICAgICByZXRhaWxDb3VudD1tZWFuKHJldGFpbENvdW50KSwNCiAgICAgICAgICAgIHJldGFpbF9ubjQ9bWVhbihyZXRhaWxfbm40KSwNCiAgICAgICAgICAgIGNvbW1vbkxlaXN1cmVfbm40PW1lYW4oY29tbW9uTGVpc3VyZV9ubjQpLA0KICAgICAgICAgICAgZml0bmVzc0NlbnRlckNvdW50PW1lYW4oZml0bmVzc0NlbnRlckNvdW50KSwNCiAgICAgICAgICAgIGZpdG5lc3NDZW50ZXJfbm40PW1lYW4oZml0bmVzc0NlbnRlcl9ubjQpLA0KICAgICAgICAgICAgZ2FyZGVuc19ubjQ9bWVhbihnYXJkZW5zX25uNCksDQogICAgICAgICAgICBnYXJkZW5zQ291bnQ9bWVhbihnYXJkZW5zQ291bnQpLA0KICAgICAgICAgICAgY29tcGFuaWVzX25uND1tZWFuKGNvbXBhbmllc19ubjQpLA0KICAgICAgICAgICAgcHVibGljX3RyYW5zcG9ydF9ubjE9bWVhbihwdWJsaWNfdHJhbnNwb3J0X25uMSksDQogICAgICAgICAgICBwdWJsaWNfdHJhbnNwb3J0X25uMj1tZWFuKHB1YmxpY190cmFuc3BvcnRfbm4yKSwNCiAgICAgICAgICAgIENlbnN1c19NZWRISEluYz1tZWFuKENlbnN1c19NZWRISEluYyksDQogICAgICAgICAgICBDZW5zdXNfTWVkUmVudD1tZWFuKENlbnN1c19NZWRSZW50KSwNCiAgICAgICAgICAgIENlbnN1c19wY3RXaGl0ZT1tZWFuKENlbnN1c19wY3RXaGl0ZSksDQogICAgICAgICAgICBDZW5zdXNfYXJlYXBlcnBlb3BsZT1tZWFuKENlbnN1c19hcmVhcGVycGVvcGxlKSwNCiAgICAgICAgICAgIENlbnN1c19wY3RBZnJpY2FuQW1lcmljYW5zPW1lYW4oQ2Vuc3VzX3BjdEFmcmljYW5BbWVyaWNhbnMpLA0KICAgICAgICAgICAgQ2Vuc3VzX3BjdEFzaWFucz1tZWFuKENlbnN1c19wY3RBc2lhbnMpKQ0KDQp0cmFpbl9kYXRhXzEgPC0gcGFuZWxfc3RfYmxvY2sgJT4lIA0KICBsZWZ0X2pvaW4ocHJlZGljdG9ycywgYnk9InN0cmVldF9ibG9jayIpDQoNCmBgYA0KDQojIyMgU3BhY2UgTGFnICYgVGltZSBMYWcNCg0KSGVyZSwgd2UgbWVyZ2UgYWxsIHRoZSB0aW1lLXNwYWNlIHByZWRpY3RvcnMgYXMgdGhlIGJhc2lzIGZvciBvdXIgc3Vic2VxdWVudCBtb2RlbHMuDQoNCmBgYHtyfQ0KDQpzcGFjZV9sYWcgPC0gdHJhaW5fZGF0YV8xICU+JSANCiAgZHBseXI6OnNlbGVjdChHRU9JRCxzdGFydF9pbnRlcnZhbDE1LG9jY3VwYW5jeSkgJT4lIA0KICBncm91cF9ieShHRU9JRCxzdGFydF9pbnRlcnZhbDE1KSAlPiUgDQogIHN1bW1hcml6ZShsYWdfb2NjID0gbWVhbihvY2N1cGFuY3kpKQ0KDQp0cmFpbl9kYXRhXzIgPC0gdHJhaW5fZGF0YV8xICU+JSANCiAgbGVmdF9qb2luKHNwYWNlX2xhZykgDQp0cmFpbl9kYXRhXzMgPC0gdHJhaW5fZGF0YV8yICU+JSANCiAgbXV0YXRlKGhvZCA9IGhvdXIoc3RhcnRfaW50ZXJ2YWwxNSkpICU+JSANCiAgZmlsdGVyKGhvZD49OSAmIGhvZDwxOCkNCg0KdGltZV9sYWcgPC0gdHJhaW5fZGF0YV8zICU+JSANCiAgZHBseXI6OnNlbGVjdChzdHJlZXRfYmxvY2ssc3RhcnRfaW50ZXJ2YWwxNSxvY2N1cGFuY3ksaG9kKSAlPiUgDQogIGFycmFuZ2Uoc3RhcnRfaW50ZXJ2YWwxNSkgJT4lIA0KICBtdXRhdGUobGFnSG91ciA9IGRwbHlyOjpsYWcob2NjdXBhbmN5LDQpLA0KICAgICAgICAgbGFnMkhvdXJzID0gZHBseXI6OmxhZyhvY2N1cGFuY3ksIDgpLA0KICAgICAgICAgbGFnM0hvdXJzID0gZHBseXI6OmxhZyhvY2N1cGFuY3ksMTIpLA0KICAgICAgICAgbGFnNkhvdXJzID0gZHBseXI6OmxhZyhvY2N1cGFuY3ksIDI0KSwNCiAgICAgICAgIGxhZzFkYXkgPSBkcGx5cjo6bGFnKG9jY3VwYW5jeSwzNikpICU+JSANCiAgbXV0YXRlKGxhZ0hvdXIgPSByZXBsYWNlKGxhZ0hvdXIsIGhvZD49OSZob2Q8MTAsIE5BKSwNCiAgICAgICAgIGxhZzJIb3VycyA9IHJlcGxhY2UobGFnMkhvdXJzLCBob2Q+PTkmaG9kPDExLCBOQSksDQogICAgICAgICBsYWczSG91cnMgPSByZXBsYWNlKGxhZzNIb3VycywgaG9kPj05JmhvZDwxMiwgTkEpLA0KICAgICAgICAgbGFnNkhvdXJzID0gcmVwbGFjZShsYWc2SG91cnMsIGhvZD49OSZob2Q8MTUsIE5BKSwNCiAgICAgICAgIGxhZzFkYXkgPSByZXBsYWNlKGxhZzFkYXksIGlzLm5hKGxhZzFkYXkpLCBvY2N1cGFuY3kpKSAlPiUgDQogIHJlcGxhY2UoaXMubmEoLiksIDApIA0KDQp0cmFpbl9kYXRhXzQgPC0gdHJhaW5fZGF0YV8zICU+JSANCiAgbGVmdF9qb2luKHRpbWVfbGFnKQ0KDQpgYGANCg0KLS0tDQoNCiMgMy4gRGF0YSBFeHBsb3JhdG9yeQ0KDQpJbiB0aGlzIHNlY3Rpb24gd2Ugd2lsbCBhbmFseXplIHRoZSBkYXRhIG9idGFpbmVkIGluIHRoZSBwcmV2aW91cyBzZWN0aW9uLg0KDQojIyAzLjEuIFBhcmtpbmcgVGltZSBEaXN0cmlidXRpb24NCg0KVGhlIGZpcnN0IGlzIHRoZSBkaXN0cmlidXRpb24gb2YgcGFya2luZyBob3Vycy4gSW4gdGhlIFNGTVRBJ3MgcGFya2luZyBtZXRlciBjaGFyZ2luZyBwb2xpY3ksIG9ubHkgdGhlIGhvdXJzIGZyb20gTW9uZGF5IHRvIFNhdHVyZGF5LCA5YW0gdG8gNnBtLCBhcmUgY2hhcmdlZC4gVGhlcmVmb3JlLCBvdXRzaWRlIG9mIHRoZXNlIGhvdXJzLCB0aGUgbnVtYmVyIG9mIHBhcmtpbmcgbWV0ZXJzIHJlY29yZGVkIGJ5IHRoZSBwYXJraW5nIG1ldGVyIGlzIGdyZWF0bHkgcmVkdWNlZC4NClRoZSBtb3N0IGZyZXF1ZW50IHBhcmtpbmcgaXMgYmV0d2VlbiA5YW0gYW5kIDZwbSwgd2hpY2ggaXMgdGhlIG1haW4gcGFpZCBwYXJraW5nIHBlcmlvZCwgYW5kIHRoZSBudW1iZXIgb2YgcGFya2luZyBkZWNyZWFzZXMgYXMgdGltZSBnb2VzIGJ5LiBUaGlzIGlzIGluIGxpbmUgd2l0aCBvdXIgcGVyY2VwdGlvbiBvZiB0cmF2ZWwgdG8gd29yayBhbmQgcGFya2luZyBzaXR1YXRpb24uDQoNCg0KYGBge3IsIGZpZy5oZWlnaHQ9MzAsIGZpZy53aWR0aD0xMn0NCiMjIERpc3RyaWJ1dGlvbg0KZGlzdHJpYnV0aW9uIDwtIGRhdDIgJT4lIA0KICBtdXRhdGUoZHRpbWUgPSBmb3JtYXQoYXMuUE9TSVhjdChzdGFydF9pbnRlcnZhbDE1KSwgZm9ybWF0PSIlSCIpKSAlPiUgDQogIGRwbHlyOjpzZWxlY3QoZHRpbWUpICU+JQ0KICBtdXRhdGUoZHRpbWU9YXMubnVtZXJpYyhkaXN0cmlidXRpb24kZHRpbWUpKSAlPiUgDQogIGZpbHRlcihkdGltZT49NSAmIGR0aW1lPDIyKQ0KYGBgDQpgYGB7cn0NCmJhcnBsb3QodGFibGUoZGlzdHJpYnV0aW9uJGR0aW1lKSwNCm1haW4gPSAiUGFya2luZyBUaW1lIERpc3RyaWJ1dGlvbiIsDQp4bGFiID0gIkhvdXIgb2YgYSBEYXkiLA0KeWxhYiA9ICJGcmVxdWVuY3kiKQ0KYGBgDQoNCiFbaW1hZ2UxXShEOi9VcGVubi9VcGVubiBMZWMvMDUtTVVTQS01MDgvQXNzaWduLUZpbmFsL0ZpbmFsRGF0YU1vZGVsQmFja1VwL1BhcmtpbmcgVGltZSBEaXN0cmlidXRpb24gLnBuZykNCg0KIyMgMy4yLlBhcmtpbmcgUHJpY2UgUmF0ZQ0KDQpXaGV0aGVyIHRoZSBjb3N0IG9mIHBhcmtpbmcgaXMgcmVsYXRlZCB0byB0aGUgb2NjdXBhbmN5IHJhdGUgb2YgcGFya2luZyBpcyBhbHNvIGEgcGFydCB3ZSB3b3VsZCBsaWtlIHRvIGtub3cuIFRoZSB0aW1lIHBlcmlvZCBpcyBkaXZpZGVkIGludG8gOTowMC0xMDowMCwgMTI6MDAtMTM6MDAsIDE1OjAwLTE2OjAwLCAxNzowMC0xODowMCBxdWFkcmFudHMsIGFuZCB0aGUgcGVhcnNvbiBjb3JyZWxhdGlvbiBpcyBzaG93biB3aXRoIHRoZSBwYXJraW5nIHJhdGUgYXMgdGhlIGhvcml6b250YWwgY29vcmRpbmF0ZSBhbmQgdGhlIHBhcmtpbmcgb2NjdXBhbmN5IHJhdGUgYXMgdGhlIHZlcnRpY2FsIGNvb3JkaW5hdGUuDQpGcm9tIHRoZSBncmFwaCwgd2UgY2FuIHNlZSB0aGF0IG1vc3Qgb2YgdGhlIHBhcmtpbmcgbWV0ZXJzIGhhdmUgYSBwYXJraW5nIHJhdGUgYmV0d2VlbiAwLjQgYW5kIDAuNS4gQXMgdGhlIHBhcmtpbmcgcmF0ZSBpbmNyZWFzZXMsIHRoZSBjaGFuZ2Ugb2Ygb2NjdXBhbmN5IHJhdGUgaXMgcXVpdGUgbGltaXRlZC4NCg0KYGBge3IsIGZpZy5oZWlnaHQ9MjAsIGZpZy53aWR0aD04fQ0KIyMgUGFya2luZyBwcmljZSByYXRlDQpwcmljZV9yYXRlIDwtIHRyYWluX2RhdGFfNCAlPiUgDQogIGZpbHRlcihob2Q9PTkgfCBob2Q9PTEyIHwgaG9kPT0xNSB8IGhvZD09MTcpICU+JSANCiAgc2VsZWN0KG9jY3VwYW5jeSwgcmF0ZSwgaG9kKQ0KDQppbWFnZTIgPC0gZ2dwbG90KHByaWNlX3JhdGUpKyAgICAgICAgICAgICAjI+WbvuihqDIg55u45YWz5oCn5pWj54K55Zu+DQogIGdlb21fcG9pbnQoYWVzKHggPSByYXRlLCANCiAgICAgICAgICAgICAgICAgeSA9IG9jY3VwYW5jeSkpKyAjIyPmlaPngrnmlbDmja4NCiAgZ2VvbV9zbW9vdGgoYWVzKHggPSByYXRlLCANCiAgICAgICAgICAgICAgICAgIHkgPSBvY2N1cGFuY3kpLCANCiAgICAgICAgICAgICAgbWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkrICMjI+aLn+WQiOebtOe6vw0KICBmYWNldF93cmFwKH5ob2QsIHNjYWxlcyA9ICJmcmVlIixuY29sPTIpKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlBhcmtpbmcgUHJpY2UgYnkgT2NjdXBhbmN5IiwNCiAgICBzdWJ0aXRsZSA9ICJPbiA5OjAwLTEwOjAwLCAxMjowMC0xMzowMCwgMTU6MDAtMTY6MDAsIDE3OjAwLTE4OjAwICIsDQogICAgeD0icmF0ZSIsIA0KICAgIHk9Im9jY3VwYW5jeSIpDQoNCmdnc2F2ZSgiUGFya2luZyBQcmljZSBieSBPY2N1cGFuY3kuanBnIix3aWR0aCA9IDEwLGhlaWdodCA9IDIwLCBpbWFnZTIpDQpgYGANCg0KIVtpbWFnZTJdKEQ6L1VwZW5uL1VwZW5uIExlYy8wNS1NVVNBLTUwOC9Bc3NpZ24tRmluYWwvRmluYWxEYXRhTW9kZWxCYWNrVXAvUGFya2luZyBQcmljZSBieSBPY2N1cGFuY3kuanBnKQ0KDQojIyAzLjMuIE9jY3VwYW5jeSBieSBEYXkgb2YgV2Vlaw0KDQpXaGljaCBkYXlzIG9mIHRoZSB3ZWVrIGhhdmUgaGlnaGVyIG9jY3VwYW5jeSByYXRlcyBpcyBhbHNvIG9uZSBvZiBvdXIgY29uY2VybnMuIEJlY2F1c2UgdGhlIFNGTVRBJ3MgcGFya2luZyBtZXRlciBpcyBmcmVlIG9uIHdlZWtkYXlzLCB0aGUgZmFjdCB0aGF0IGEgbG93ZXIgb2NjdXBhbmN5IHJhdGUgaXMgcmVjb3JkZWQgb24gd2Vla2RheXMgZG9lcyBub3QgbWVhbiB0aGF0IHRoZSBwYXJraW5nIG9jY3VwYW5jeSByYXRlIGlzIGFjdHVhbGx5IGxvd2VyLiBGb3IgdGhlIHJlc3Qgb2YgdGhlIHRpbWUgcGVyaW9kLCB0aGUgZGFpbHkgcGFya2luZyBvY2N1cGFuY3kgcmF0ZSBpcyBndWFyYW50ZWVkIHRvIGJlIGFyb3VuZCAwLjUuDQoNCmBgYHtyfQ0KIyMgT2NjdXBhbmN5IGJ5IGRheSBvZiB3ZWVrDQpkYXlfb2Zfd2VlayA8LSB0cmFpbl9kYXRhXzQgJT4lIA0KICBzZWxlY3Qob2NjdXBhbmN5LHN0YXJ0X2ludGVydmFsMTUpICU+JQ0KICBtdXRhdGUoZG90dyA9IHdkYXkoc3RhcnRfaW50ZXJ2YWwxNSwgbGFiZWw9VFJVRSkpICU+JSANCiAgZ3JvdXBfYnkoZG90dykgJT4lIA0KICBzdW1tYXJpemUob2NjdXBhbmN5ID0gbWVhbihvY2N1cGFuY3kpKQ0KICANCmltYWdlMyA8LSBnZ3Bsb3QoZGF5X29mX3dlZWssIGFlcyh4ID0gZG90dywgeSA9IG9jY3VwYW5jeSkpICsgDQogIGdlb21fbGluZShhZXMoZ3JvdXA9MSkpICsNCiAgbGFicyh0aXRsZT0iUGFya2luZyBPY2N1cGFuY3kgYnkgZGF5IG9mIHdlZWsiLA0KICAgICAgIHg9IkRheSBvZiB3ZWVrIiwgDQogICAgICAgeT0iQXZlcmFnZSBPY2N1cGFuY3kiKSsNCiAgICAgcGxvdFRoZW1lDQpnZ3NhdmUoIlBhcmtpbmcgUHJpY2UgYnkgZGF5IG9mIHdlZWsucG5nIiwgaW1hZ2UzKQ0KYGBgDQoNCiFbaW1hZ2UzXShEOi9VcGVubi9VcGVubiBMZWMvMDUtTVVTQS01MDgvQXNzaWduLUZpbmFsL0ZpbmFsRGF0YU1vZGVsQmFja1VwL1BhcmtpbmcgUHJpY2UgYnkgZGF5IG9mIHdlZWsucG5nKQ0KDQojIyAzLjQuIFBhcmtpbmcgT2NjdXBhbmN5IGJ5IGRheSBvZiB3ZWVrDQpGaW5hbGx5LCB0aGUgcGFya2luZyBvY2N1cGFuY3kgYXQgZGlmZmVyZW50IHRpbWVzIG9mIHRoZSBkYXkgZm9yIGRpZmZlcmVudCBHZW9faWRzLiBUaGUgaG9yaXpvbnRhbCBjb29yZGluYXRlIHN0YXJ0cyBhdCA5IGEubS4gYW5kIGVuZHMgYXQgNiBwLm0uIEZvciBtb3N0IEdlb19pZHMsIHRoZSBwYXJraW5nIG9jY3VwYW5jeSBhbHdheXMgc3RheXMgd2l0aGluIHRoZSBzYW1lIGludGVydmFsLg0KDQpgYGB7ciwgZmlnLmhlaWdodD0zMCwgZmlnLndpZHRoPTEwfQ0KIyMgUmF0ZSBieSB0aW1lDQpSYXRlX3RpbWUgPC0gdHJhaW5fZGF0YV80ICU+JSANCiAgbXV0YXRlKGR0aW1lID0gZm9ybWF0KGFzLlBPU0lYY3Qoc3RhcnRfaW50ZXJ2YWwxNSksIGZvcm1hdD0iJUgiKSkgJT4lIA0KICBncm91cF9ieShkdGltZSwgR0VPSUQpICU+JSANCiAgc3VtbWFyaXplKHJhdGUgPSBtZWFuKHJhdGUpKQ0KDQppbWFnZTQgPC0gZ2dwbG90KFJhdGVfdGltZSwgYWVzKHggPSBkdGltZSwgeSA9IHJhdGUpKSArIA0KICBnZW9tX2xpbmUoYWVzKGdyb3VwPTEpKSArDQogIGZhY2V0X3dyYXAofkdFT0lELCBzY2FsZXMgPSAiZnJlZSIsbmNvbD01KSsNCiAgbGFicyh0aXRsZT0iUGFya2luZyBPY2N1cGFuY3kgYnkgZGF5IG9mIHdlZWsiLA0KICAgICAgIHg9IkRheSBvZiB3ZWVrIiwgDQogICAgICAgeT0iQXZlcmFnZSBPY2N1cGFuY3kiKSsNCiAgICAgcGxvdFRoZW1lDQoNCmdnc2F2ZSgiUmF0ZSBieSB0aW1lLmpwZyIsd2lkdGggPSAxMCwgaGVpZ2h0ID0gNDAsaW1hZ2U0KQ0KYGBgDQoNCiFbaW1hZ2U0XShEOi9VcGVubi9VcGVubiBMZWMvMDUtTVVTQS01MDgvQXNzaWduLUZpbmFsL0ZpbmFsRGF0YU1vZGVsQmFja1VwL1JhdGUgYnkgdGltZS5qcGcpDQoNCi0tLQ0KDQojIDQuIFJlZ3Jlc3Npb24gTW9kZWwNCg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSBhbmFseXplIHRoZSBwYXJraW5nIG9jY3VwYW5jeSBpbiBjb21iaW5hdGlvbiB3aXRoIHRpbWUgbGFnIGFuZCBzcGF0aWFsIGxhZy4NCg0KT3VyIHByZWRpY3RvciBpbmNsdWRlcyB0aGUgc3BhdGlhbCBlbGVtZW50cyAocmVzdGF1cmFudENvdW50LCByZXN0YXVyYW50X25uNCwgc2Nob29sQ291bnQsIHNjaG9vbHNfbm40KSwgdGltZSBsYWcgKGxhZ0hvdXIsIGxhZzJIb3VycywgbGFnM0hvdXJzLCBsYWc2SG91cnMpLCBhbmQgQ2Vuc3VzZGF0YSAoTWVkSEhJbmMsIGV0Yy4pDQpgYGB7cn0NCg0KbW9kZWxfZGF0YV9vZmZjaWFsIDwtIHRyYWluX2RhdGFfNCAlPiUgDQogIG11dGF0ZShkb3R3ID0gd2RheShzdGFydF9pbnRlcnZhbDE1LCBsYWJlbD1UUlVFKSkNCg0KDQpzZXQuc2VlZCgzNDU2KQ0KdHJhaW5JbmRleCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHk9cGFzdGUobW9kZWxfZGF0YV9vZmZjaWFsJHN0cmVldF9ibG9jayxtb2RlbF9kYXRhX29mZmNpYWwkR0VPSUQsIG1vZGVsX2RhdGFfb2ZmY2lhbCRkb3R3KSwgcCA9IC43MCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZXMgPSAxKQ0KDQpwYXJraW5nLlRyYWluIDwtIG1vZGVsX2RhdGFfb2ZmY2lhbFsgdHJhaW5JbmRleCxdDQpwYXJraW5nLlRlc3QgPC0gbW9kZWxfZGF0YV9vZmZjaWFsWyAtdHJhaW5JbmRleCxdDQoNCnJlZy50cmFpbmluZyA8LSANCiAgbG0ob2NjdXBhbmN5IH4gIHJhdGUgKyBwYXJrQ291bnQgKyBwYXJrQXJlYSArIHJlc3RhdXJhbnRzQ291bnQgKyByZXN0YXVyYW50c19ubjQgKyBzY2hvb2xDb3VudCArIHNjaG9vbHNfbm40ICsgdW5pdmVyc2l0aWVzQ291bnQgKyBwYXJraW5nQ291bnQgKyBwYXJraW5nQXJlYSArIHBhcmtpbmdfbm40ICsgY2xpbmljc19ubjQgKyBob3NwaXRhbHNfbm40ICsgY2luZW1hc0NvdW50ICsgc3RhZGl1bXNDb3VudCArIGNvbW1lcmNlX25uNCArIGNvbW1lcmNlQ291bnQgKyByZXRhaWxDb3VudCArIHJldGFpbF9ubjQgKyBjb21tb25MZWlzdXJlX25uNCArIGZpdG5lc3NDZW50ZXJDb3VudCArIGZpdG5lc3NDZW50ZXJfbm40ICsgZ2FyZGVuc19ubjQgKyBnYXJkZW5zQ291bnQgKyBjb21wYW5pZXNfbm40ICsgcHVibGljX3RyYW5zcG9ydF9ubjEgKyBwdWJsaWNfdHJhbnNwb3J0X25uMSArIENlbnN1c19NZWRISEluYyArIENlbnN1c19NZWRSZW50ICsgQ2Vuc3VzX3BjdFdoaXRlICsgQ2Vuc3VzX2FyZWFwZXJwZW9wbGUgKyBDZW5zdXNfcGN0QWZyaWNhbkFtZXJpY2FucyArIENlbnN1c19wY3RBc2lhbnMgKyBsYWdfb2NjICsgaG9kICsgZG90dyArIGxhZ0hvdXIgKyBsYWcySG91cnMgKyBsYWczSG91cnMgKyBsYWc2SG91cnMgKyBsYWcxZGF5LCAgZGF0YT1wYXJraW5nLlRyYWluKQ0KDQojIOS4jeeUqOWxleekulLmlrkNCiMgICAgc3RhcmdhemVyKHJlZy50cmFpbmluZywgDQojICAgICAgICAgICAgICB0eXBlID0gJ3RleHQnLCANCiMgICAgICAgICAgICAgIHRpdGxlID0gIk9MUyBSZWdyZXNzaW9uIiwNCiMgICAgICAgICAgICAgIGNvdmFyaWF0ZS5sYWJlbHMgPSBjKCkpDQpgYGANCg0KDQojIyA0LjEuIEdvb2RuZXNzIG9mIGZpdA0KDQpGb3IgdGhlIHJlc3VsdHMgb2YgdGhlIG1vZGVsLCB0aGUgTUFFIG9mIHRoZSBtb2RlbCA9IDAuMDM5NSxNQVBFIG9mIHRoZSBtb2RlbCA9IDAuODI0DQoNCmBgYHtyfQ0KbW9kZWxfcHJlZCA8LSBmdW5jdGlvbihkYXQsIGZpdCl7DQogICBwcmVkIDwtIHByZWRpY3QoZml0LCBuZXdkYXRhID0gZGF0KX0NCg0KcGFya2luZy5UZXN0JG9jY3VwYW5jeS5QcmVkaWN0ID0gcHJlZGljdChyZWcudHJhaW5pbmcsIHBhcmtpbmcuVGVzdCkNCg0KcGFya2luZy5UZXN0IDwtDQogIHBhcmtpbmcuVGVzdCAlPiUNCiAgbXV0YXRlKG9jY3VwYW5jeS5FcnJvciA9IG9jY3VwYW5jeS5QcmVkaWN0IC0gb2NjdXBhbmN5LA0KICAgICAgICAgb2NjdXBhbmN5LkFic0Vycm9yID0gYWJzKG9jY3VwYW5jeS5QcmVkaWN0IC0gb2NjdXBhbmN5KSwNCiAgICAgICAgIG9jY3VwYW5jeS5BUEUgPSAoYWJzKG9jY3VwYW5jeS5QcmVkaWN0IC0gb2NjdXBhbmN5KSkgLyBvY2N1cGFuY3kuUHJlZGljdCkNCg0KTUFFIDwtIG1lYW4ocGFya2luZy5UZXN0JG9jY3VwYW5jeS5BYnNFcnJvciwgbmEucm0gPSBUKQ0KTUFQRSA8LSBtZWFuKHBhcmtpbmcuVGVzdCRvY2N1cGFuY3kuQVBFLCBuYS5ybSA9IFQpDQpNQUUgPSBjKE1BRSkgJT4lIGZvcm1hdCguLCBkaWdpdHMgPSAzKQ0KTUFQRSA9IGMoTUFQRSkgJT4lIGZvcm1hdCguLCBkaWdpdHMgPSAzKQ0KTW9kZWwgPSBjKCJNb2RlbCBGaXRuZXNzIikNCnN1bW1hcnlUYWJsZTEgPSBjYmluZChNb2RlbCwgTUFFLCBNQVBFKQ0KDQprYWJsZShzdW1tYXJ5VGFibGUxLCBkaWdpdHMgPSAxLCBjYXB0aW9uID0gIlRhYmxlLiBQcmVkaWN0aW9uIHByZWNpc2lvbiBvZiB0aGUgZmlyc3QgdHdvIG1vZGVscyIpICU+JQ0KICBrYWJsZV9jbGFzc2ljKGZ1bGxfd2lkdGggPSBUKSU+JQ0KICBmb290bm90ZSgpDQpgYGANCg0KIVtpbWFnZTVdKEQ6L1VwZW5uL1VwZW5uIExlYy8wNS1NVVNBLTUwOC9Bc3NpZ24tRmluYWwvRmluYWxEYXRhTW9kZWxCYWNrVXAvaW1hZ2U1LnBuZykNCg0KIyMgNC4yLiBFeGFtaW5lIEVycm9yIE1ldHJpY3MgZm9yIEFjY3VyYWN5DQpUaGUgZm9sbG93aW5nIGNoYXJ0IHNob3dzIG91ciBwcmVkaWN0ZWQgcmVzdWx0cyBjb21wYXJlZCB0byB0aGUgYWN0dWFsIHJlc3VsdHMuVGhlIGN5YW4gbGluZSBpcyB0aGUgcHJlZGljdGVkIHJlc3VsdCBhbmQgdGhlIHJlZCBsaW5lIGlzIHRoZSBhY3R1YWwgcmVzdWx0LiBBcyBjYW4gYmUgc2VlbiBpbiB0aGUgc2FtZSBmaWd1cmUsIHRoZSBtb2RlbCBwcmVzZW50cyBhIGJldHRlciBwcmVkaWN0aW9uIGluIHRlcm1zIG9mIG92ZXJhbGwgdHJlbmQgZHVlIHRvIHRoZSBpbmNsdXNpb24gb2YgdGltZSBsYWcuIEhvd2V2ZXIsIG9uIHRoZSB3aG9sZSwgdGhlIG9jY3VwYW5jeSByYXRlIG9mIHRoZSBtb2RlbCBwcmVkaWN0aW9uIGlzIGxvd2VyIGNvbXBhcmVkIHRvIHRoZSBhY3R1YWwgc2l0dWF0aW9uLiBUaGlzIGxlYWRzIHRvIGEgc2l0dWF0aW9uIHdoZXJlIHRoZSBwcmVkaWN0ZWQgcmVzdWx0cyBhcHBlYXIgaW4gcGFyYWxsZWwgdG8gdGhlIGFjdHVhbCByZXN1bHRzIGR1cmluZyB0aGUgZnJlZSBob3VycyBvZiB0aGUgZGF5Lg0KDQpgYGB7cn0NCnBhcmtpbmdfdGVzdF9wbG90IDwtIHBhcmtpbmcuVGVzdCAlPiUgDQogICAgbXV0YXRlKGludGVydmFsNjAgPSBmbG9vcl9kYXRlKHltZF9obXMoc3RhcnRfaW50ZXJ2YWwxNSksIHVuaXQgPSAiNjAgbWlucyIpKSU+JSANCiAgICBkcGx5cjo6c2VsZWN0KGludGVydmFsNjAsIHN0cmVldF9ibG9jaywgb2NjdXBhbmN5LCBvY2N1cGFuY3kuUHJlZGljdCkgJT4lDQogICAgdW5uZXN0KCklPiUNCiAgICBuYS5vbWl0KCkgJT4lDQogICAgZ2F0aGVyKFZhcmlhYmxlLCBWYWx1ZSwgLWludGVydmFsNjAsIC1zdHJlZXRfYmxvY2spICU+JQ0KICAgIGdyb3VwX2J5KFZhcmlhYmxlLCBpbnRlcnZhbDYwKSAlPiUNCiAgICBzdW1tYXJpemUoVmFsdWUgPSBtZWFuKFZhbHVlKSkNCg0KaW1hZ2U2IDwtIHBhcmtpbmdfdGVzdF9wbG90ICU+JSANCiAgICBnZ3Bsb3QoYWVzKGludGVydmFsNjAsIFZhbHVlLCBjb2xvcj1WYXJpYWJsZSkpICsgDQogICAgICBnZW9tX2xpbmUoc2l6ZSA9IDEuMSkgKyANCiAgICAgIGxhYnModGl0bGUgPSAiUHJlZGljdGVkL09ic2VydmVkIE9jY3VwYW5jeSIsIHN1YnRpdGxlID0gIlNGOyBBIHRlc3Qgc2V0IG9mIDIgd2Vla3MiLCAgeCA9ICJIb3VyIiwgeT0gIk9jY3VwYW5jeSIpICsNCiAgICAgIHBsb3RUaGVtZQ0KIyNzYXZlLmltYWdlKGZpbGU9J0Q6L1VQZW5uL0ZhbGwyMDIyLzUwODBQb2xpY3kvRmluYWwvRmluYWxfZGF0YV9tb2RlbC8uUkRhdGEnKQ0KDQpnZ3NhdmUoIlByZWRpY3RlZC9PYnNlcnZlZCBPY2N1cGFuY3kuanBnIix3aWR0aCA9IDEwLCBoZWlnaHQgPSA1LGltYWdlNikNCmBgYA0KDQohW2ltYWdlNl0oRDovVXBlbm4vVXBlbm4gTGVjLzA1LU1VU0EtNTA4L0Fzc2lnbi1GaW5hbC9GaW5hbERhdGFNb2RlbEJhY2tVcC9QcmVkaWN0ZWQvT2JzZXJ2ZWQgT2NjdXBhbmN5LmpwZykNCg0KLS0tDQoNCiMgNS4gR2VuZXJhbGl6YWJpbGl0eQ0KDQojIyA1LjEuIENyb3NzIFZhbGlkYXRpb24gDQoNCkluIHRoZSBjcm9zcy12YWxpZGF0aW9uIGFuYWx5c2lzLCBvdXIgcmVzdWx0cyBhcmUgYXMgZm9sbG93cy4gU2ltaWxhciB0byB0aGUgcmVzdWx0cyBvZiB0aGUgdGVzdCBzZXQsIHRoZSBNQUUgb2YgdGhlIG1vZGVsID0gMC4wNzU3LCB3aGljaCBpcyBzbGlnaHRseSBpbXByZWNpc2UgY29tcGFyZWQgdG8gYSBkaXN0cmlidXRpb24gdGhhdCBvY2N1cGllcyB0aGUgcmFuZ2UgYmV0d2VlbiAwIGFuZCAxLg0KDQpgYGB7cn0NCiMgUiBwcm9ncmFtIHRvIGltcGxlbWVudA0KIyBLLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbg0KIA0KIyBzZXR0aW5nIHNlZWQgdG8gZ2VuZXJhdGUgYQ0KIyByZXByb2R1Y2libGUgcmFuZG9tIHNhbXBsaW5nDQpzZXQuc2VlZCgxMjUpDQogDQojIGRlZmluaW5nIHRyYWluaW5nIGNvbnRyb2wNCiMgYXMgY3Jvc3MtdmFsaWRhdGlvbiBhbmQNCiMgdmFsdWUgb2YgSyBlcXVhbCB0byAxMA0KdHJhaW5fY29udHJvbCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlciA9IDEwKQ0KIA0KIyB0cmFpbmluZyB0aGUgbW9kZWwgYnkgYXNzaWduaW5nIHNhbGVzIGNvbHVtbg0KIyBhcyB0YXJnZXQgdmFyaWFibGUgYW5kIHJlc3Qgb3RoZXIgY29sdW1uDQojIGFzIGluZGVwZW5kZW50IHZhcmlhYmxlDQptb2RlbCA8LSB0cmFpbihvY2N1cGFuY3kgfiBkb3R3K2xhZ0hvdXIrbGFnMkhvdXJzK2xhZzNIb3VycytsYWc2SG91cnMrbGFnMWRheSwgZGF0YSA9IHBhcmsuY3Jvc3MsDQogICAgICAgICAgICAgICBtZXRob2QgPSAibG0iLA0KICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW5fY29udHJvbCkNCiANCiMgcHJpbnRpbmcgbW9kZWwgcGVyZm9ybWFuY2UgbWV0cmljcw0KIyBhbG9uZyB3aXRoIG90aGVyIGRldGFpbHMNCnByaW50KG1vZGVsKQ0KYGBgDQohW2ltYWdlOF0oRDovVXBlbm4vVXBlbm4gTGVjLzA1LU1VU0EtNTA4L0Fzc2lnbi1GaW5hbC9GaW5hbERhdGFNb2RlbEJhY2tVcC9DVlJlc3VsdC5wbmcpDQoNCi0tLQ0KDQojIDYuIEFwcGxpY2F0aW9uIERldmVsb3BtZW50DQoNCkZpbmFsbHkgd2Ugd2lsbCBzaGFyZSBvdXIgdXNlci1zcGVjaWZpYyBpbnRlcmZhY2UgZGVzaWduLiBPdXIgYXBwIGlzIGludGVuZGVkIHRvIG1hbmFnZSB0aGUgb2NjdXBhbmN5IG9mIHBhcmtpbmcgc3BvdHMgYnkgaGVscGluZyBTRk1UQSBvZmZpY2lhbHMgd2l0aCBwcmljaW5nLg0KDQohW2ltYWdlN10oRDovVXBlbm4vVXBlbm4gTGVjLzA1LU1VU0EtNTA4L0Fzc2lnbi1GaW5hbC9QUFQvREVNTyBmb3IgMTIwOS9pbWFnZSAxMC5wbmcpDQoNCkluIHRoaXMgb25saW5lIGludGVyZmFjZSwgZGVjaXNpb24gbWFrZXJzIGNhbiB2aWV3IHByb2plY3Rpb25zIG9mIGZ1dHVyZSBwYXJraW5nIHJhdGVzIG9uIGNlcnRhaW4gc3RyZWV0cyBieSBlbnRlcmluZyBwcmljZXMuDQohW2ltYWdlOF0oRDovVXBlbm4vVXBlbm4gTGVjLzA1LU1VU0EtNTA4L0Fzc2lnbi1GaW5hbC9QUFQvREVNTyBmb3IgMTIwOS9pbWFnZTExLnBuZykNCg0KDQo=