To conduct an accessibility analysis, we need: 1) OSM Road Data 2) Shapefile of the study area 3) GTFS feed
We use these inputs to make queries on Open Trip Planner
Part 1: OSM Data
Define the geographic extents as a vector of c(xmin, ymin, xmax, ymax)
library(osmdata)
library(tidyverse)
# opq used to build an overpass query
# we define the geographic extents of the area we want to query from OSM. I have added the extents of the GCR as a vector
# add_osm_feature() used to specify what we are querying. It takes key, value pairs but here I want all types of highways so I specify the key only
# For a list of specific values to query, see https://wiki.openstreetmap.org/wiki/Key:highway
query <- opq(bbox = c(30.801369, 29.705080, 31.874483, 30.390664)) %>%
add_osm_feature(key = 'highway')
# here we want the data in xml format. Ideally we would use pbf data, as it is more compact and makes the analysis faster, but osmdata_pdf() returns empty files. Issue discussed here https://github.com/ropensci/osmdata/issues/74
osmdata_xml(query, filename = 'greater-cairo.osm')
# the xml file is downloaded to the project working directory
You can plot to see if the roads have been downloaded. This takes around 10 minutes and the sp file is huge. I will not do it here, but for checking purposes, this is how it would be done:
convert xml to sp using osmdata_sp(query)
plot sp::plot(highways_greaterCairo$osm_lines) highways_greaterCairo <- osmdata_sp(query)
An alternative to xml is pbf files. These are more compact and presumably make the forthcoming analysis faster. They can be downloaded from the HOT OSM export tool https://export.hotosm.org/en/v3/exports/new/formats?fbclid=IwAR3m_1bDZK2sYsA-jtPRPYGg9R7kTqt5hzjP88x3p2yvRd4i9tr11i2tG60
I tried to use a pbf file but the analysis was not quicker so I stuck to the xml file extracted from r
Part 2: Importing Shapefiles for Analysis
In this step I import a shapefile of Greater Cairo. The shapefile has the region divided into hexagons with the population and number of jobs in each hexagon
library(sf)
# import the shapefile
cairo_hexagons <- st_read("Cairo Shapefiles/H3-res8/H3res-8_GCR_4326.shp")
It is imported and in the corrected crs EPSG:4326
Let’s plot to check if it looks right
library(tmap)
# plot
tm_shape(cairo_hexagons) +
tm_polygons()
It looks right but the area in the top right needs to be removed. This is the 10th of Ramadan City and is not part of the GCR
# Remove 10th of Ramadan (it is in Sharqeya)
# dplyr::filter with !grepl. This removes all rows that contain 10th of Ramadan OR '10 of Rammadan' in nam_tfc column
cairo_hexagons <- cairo_hexagons %>% filter(!grepl('10th of Ramadan|10 Of Rammadan', nam_tfc))
# plot to check that it worked
tm_shape(cairo_hexagons) +
tm_polygons()
The next step is to get the centroid of each hexagon. This is because Open Trip Planner takes queries from lat lon coordinates
# get centroids in seperate feature
h3_centroids <- st_centroid(cairo_hexagons)
# centroids are provided as geometry but we need the lat and lon in two seperate columns
# I use this function to split c(lat, lon) to two seperate columns
# FROM JM London (https://github.com/r-spatial/sf/issues/231)
# lat = Y lon = X
sfc_as_cols <- function(x, names = c("lon","lat")) {
stopifnot(inherits(x,"sf") && inherits(sf::st_geometry(x),"sfc_POINT"))
ret <- sf::st_coordinates(x)
ret <- tibble::as_tibble(ret)
stopifnot(length(names) == ncol(ret))
x <- x[ , !names(x) %in% names]
ret <- setNames(ret,names)
dplyr::bind_cols(x,ret)
}
# add lon and lat columns to dataframe using sfc_as_cols function
h3_centroids <- sfc_as_cols(h3_centroids)
# two additional columns (lat and lon) have been added to the sfc
h3_centroids
Part 3: Open Trip Planner
I use the package by Malcolm Morgan to query OTP through R https://docs.ropensci.org/opentripplanner/index.html
3.1: Setting Up OTP
library(opentripplanner)
# OTP expects its data to be stored in a specific structure
# I create a new folder called Open-Trip-Planner in my wd
#dir.create() creates a subfolder called "OTP-Cairo-All"
path_data <- file.path("Open-Trip-Planner", "OTP-Cairo-All")
dir.create(path_data)
'Open-Trip-Planner/OTP-Cairo-All' already exists
Now we need to download OTP and save it to the “OTP-Cairo-All” subfolder
# otp_dl_jar function will download the OTP and save it in the folder we created
# The function returns the path to the OTP jar file.
path_otp <- otp_dl_jar(path_data)
The next step is to add the GTFS and OSM files to the created folder. Create a subfolder in OTP-Cairo-All called graphs then create a subfolder in graphs called default Add the OSM and GTFS inside ‘default’ I followed this folder structure:
- OTP-Cairo-All (folder - created above)
- graphs (folder)
- default (folder)
- osm.pbf (data) # Required OSM road map - ADDED
- gtfs.zip (data) # Optional GTFS data - ADDED
I add a GTFS file with all public transport in the GCR (Formal + Informal)
(available here https://github.com/Hussein-Mahfouz/GIS-Coursework/tree/master/GTFS%20Feeds)
This data is used to build a Graph object which is the base for OpenTripPlanner
# Building an OTP Graph
# This code will create a new file Graph.obj that will be saved in the location defined by path_data.
# memory argument assigns more memory to building graph (to speed it up). R assigns 2GB by default
log <- otp_build_graph(otp = path_otp, dir = path_data, memory = 6000, analyst = TRUE)
Now we are ready to launch OpenTripPlanner
3.2: Querying OTP
Now that OTP is launched and connected to r, we can start querying
# LOOPING FUNCTION TO GET REACH ISOCHRONE OF EACH HEXAGON CENTROID
# variable with number of hexagons (for looping function)
nrows <- nrow(h3_centroids)
# empty list to store output
reachPolygons<-list()
# get reach polygon for each centroid and store in reachPolygons list
for (i in 1:nrows){
reachPolygons[[i]] <- otp_isochrone(otpcon = otpcon,
fromPlace = c(h3_centroids$lon[i], h3_centroids$lat[i]),
mode = c("WALK", "TRANSIT"),
maxWalkDistance = 1000,
date_time = as.POSIXct(strptime("2019-08-05 09:00", "%Y-%m-%d %H:%M")),
cutoffSec = 3600) # Cut offs in seconds
}
Lets plot one of the isochrones to see if this worked
tmap_mode("view")
tmap mode set to interactive viewing
tm_shape(reachPolygons[[568]]) +
tm_fill(col = "antiquewhite2") +
tm_borders()
The shape reachPolygons[[568]] is invalid. See sf::st_is_validLinking to GEOS 3.7.2, GDAL 2.4.2, PROJ 5.2.0
row names were found from a short variable and have been discarded
3.4 Repeating Query for Formal Modes
Now let’s do the analysis again using a GTFS feed with formal transport modes only. This means we have to subset the GTFS feed and keep only the formal agencies. In OpenTripPlanner, there is a bannedAgencies argument, but this does not exist in the OpenTripPlanner R Package (see the documentation here: https://docs.ropensci.org/opentripplanner/reference/otp_isochrone.html?q=otp%20_%20isoch )
This is definitely a useful enhancement and I am thinking of requesting it on Github
There is one R package that handles GTFS feeds but it doesn’t filter by agencies https://github.com/ipeaGIT/gtfs2gps/blob/master/vignettes/intro_to_gtfs2gps.md
I ended up using a java application that can be accessed from the command line. It subsets GTFS feeds in a matter of seconds. Check the ‘How to Reduce your GTFS’ section http://developer.onebusaway.org/modules/onebusaway-gtfs-modules/1.3.4-SNAPSHOT/onebusaway-gtfs-transformer-cli.html
I added a text file with arguments for the agencies I want to retain (formal agencies) {“op”:“retain”, “match”:{“file”:“agency.txt”, “agency_id”:“CTA”}} {“op”:“retain”, “match”:{“file”:“agency.txt”, “agency_id”:“CTA_M”}} {“op”:“retain”, “match”:{“file”:“agency.txt”, “agency_id”:“NAT”}}
Now repeat the steps done above to create a directory
First thing is to disconnect from OTP. i found that if I don’t do this, OTP runs the analysis on the old graph that has the GTFS file with all agencies (even though I provide a new path…)
otp_stop(warn=FALSE)
Now let’s give it the new path
library(opentripplanner)
# create subfolder called "OTP-Cairo-Formal"
path_data <- file.path("Open-Trip-Planner", "OTP-Cairo-Formal")
dir.create(path_data)
# otp_dl_jar function will download the OTP and save it in the folder we created
# The function returns the path to the OTP jar file.
path_otp <- otp_dl_jar(path_data)
Before building the graph, add the GTFS feed and the osm road layer in the correct folder. See 3.1.1: Setting UP OTP
log <- otp_build_graph(otp = path_otp, dir = path_data, memory = 6000, analyst = TRUE)
# Launch OTP and load the graph
otp_setup(otp = path_otp, dir = path_data)
# connect r to otp
otpcon <- otp_connect()
Now we can run the analysis again using formal agencies only.
# empty list to store output
reachPolygonsFormal<-list()
# get reach polygon for each centroid and store in reachPolygons list
for (i in 1:nrows){
reachPolygonsFormal[[i]] <- otp_isochrone(otpcon = otpcon,
fromPlace = c(h3_centroids$lon[i], h3_centroids$lat[i]),
mode = c("WALK", "TRANSIT"),
maxWalkDistance = 1000,
date_time = as.POSIXct(strptime("2019-08-05 09:00", "%Y-%m-%d %H:%M")),
cutoffSec = 3600) # Cut offs in seconds
}
Now we’re done with otp. Make sure to terminate the Java OTP instance (it takes up 2GB of precious memory)
# warn equals false so that it doesn't prompt me to confirm
otp_stop(warn=FALSE)
3.3 Calculating Job Reach and Accessibility Scores
3.3.1 Intersecting Isochrones with Hexagons
Now that we have the isochrones, we need to calculate how many jobs each isochrone intersects with.
For each intersected hexagon, we get: (area of intersection / total area of hexagon) * jobs in Hexagon
We then sum all the results to get the number of jobs accessible for the hexagon we are querying from
ALL AGENCIES:
# empty list to store output
totalJobs <-list()
library(lwgeom) # for st_make_valid (this handles invalid geometries https://github.com/r-spatial/sf/issues/870)
for (i in 1:nrows){
totalJobs[[i]] <- 0 # there are some points that OTP couldn't route from. try() used to assign 0 value to these points
try({
totalJobs[[i]] <- reachPolygons[[i]] %>%
st_make_valid() %>% # some geometries are invlid (this prevents an error)
st_intersection(cairo_hexagons) %>% # intersect reachPolygon with cairo_hexagons
mutate(int_area = (st_area(.)/1000000) %>% as.numeric()) %>% # add column with intersection area with each hexagon
mutate(int_jobs = (int_area/area)*jobsLFScou) %>% # add column with int_jobs: jobs intersected
summarise(total_jobs = sum(int_jobs)) # summarize and get the sum of jobs intersected by reachPolygon
})
}
FORMAL AGENCIES:
# empty list to store output
totalJobsFormal <-list()
library(lwgeom) # for st_make_valid (this handles invalid geometries https://github.com/r-spatial/sf/issues/870)
for (i in 1:nrows){
totalJobsFormal[[i]] <- 0 # there are some points that OTP couldn't route from. try() used to assign 0 value to these points
try({
totalJobsFormal[[i]] <- reachPolygonsFormal[[i]] %>%
st_make_valid() %>% # some geometries are invlid (this prevents an error)
st_intersection(cairo_hexagons) %>% # intersect reachPolygon with cairo_hexagons
mutate(int_area = (st_area(.)/1000000) %>% as.numeric()) %>% # add column with intersection area with each hexagon
mutate(int_jobs = (int_area/area)*jobsLFScou) %>% # int_jobs is the jobs intersected
summarise(total_jobs = sum(int_jobs)) # gets one value for jobs intersected by reachPolygon
})
}
Now that we have the job reach of each hexagon, we need to transfer these values form the totalJobs list back to the cairo_hexagons sf so that we can calculate accessibility scores and, eventually, plot the results
# add a column for the number of jobs reachable within 60 minutes using ALL MODES of public transport
for (i in 1:nrows){
cairo_hexagons$jobs_60_all[i] <- 0 #set default value = O: will be given to bad geometries
try({
cairo_hexagons$jobs_60_all[i] = totalJobs[[i]][[1]] # add totalJobs to new column in cairo_hexagons called jobs_60_all
})
}
#add column for the number of jobs reachable within 60 minutes using FORMAL MODES public transport
for (i in 1:nrows){
cairo_hexagons$jobs_60_formal[i] <- 0 #set default value = O: will be given to bad geometries
try({
cairo_hexagons$jobs_60_formal[i] = totalJobsFormal[[i]][[1]] # add totalJobs to new column in cairo_hexagons called jobs_60_all
})
}
3.3.2 Calculating Accessibility Scores
Now lets get the accessibility score for each hexagon. This is the % of the total jobs in the GCR that are reachable from this hexagon
# percentage of jobs accessible from each hexagon - ALL MODES OF PUBLIC TRANSPORT
cairo_hexagons$access_per_60_all = ((cairo_hexagons$jobs_60_all/ sum(cairo_hexagons$jobsLFScou))*100)
# percentage of jobs accessible from each hexagon - FORMAL MODES OF PUBLIC TRANSPORT
cairo_hexagons$access_per_60_formal = ((cairo_hexagons$jobs_60_formal/ sum(cairo_hexagons$jobsLFScou))*100)
3.3.3 Scores for Entire Study Area
To calculate the accessibility score for the entire study area:
1- weigh each hexagons job reach/accessibility score by its population (multiply them)
2- divide the sum by the total population of the GCR
# as.numeric used to return a number instead of a matrix
# Average jobs reached using all modes
GCR_avg_jobs_all <- as.numeric((cairo_hexagons$jobs_60_all %*% cairo_hexagons$pop2018cap) / sum(cairo_hexagons$pop2018cap))
cat(" Average Job Reach Using All Modes :", GCR_avg_jobs_all, "\n")
# Average accessibility using all Modes (%):
GCR_avg_acc_all <- as.numeric((cairo_hexagons$access_per_60_all %*% cairo_hexagons$pop2018cap) / sum(cairo_hexagons$pop2018cap))
cat(" Average Accessibility Using All Modes :", GCR_avg_acc_all, "\n")
# Average jobs reached using formal modes
GCR_avg_jobs_formal <- as.numeric((cairo_hexagons$jobs_60_formal %*% cairo_hexagons$pop2018cap) / sum(cairo_hexagons$pop2018cap))
cat(" Average Job Reach Using Only Formal Modes :", GCR_avg_jobs_formal, "\n")
# # Average accessibility using formal Modes (%):
GCR_avg_acc_formal <- as.numeric((cairo_hexagons$access_per_60_formal %*% cairo_hexagons$pop2018cap) / sum(cairo_hexagons$pop2018cap))
cat(" Average Accessibility Using Only Formal Modes :", GCR_avg_acc_formal)
Let’s check the regional level scores (scores for central, inner and outer Cairo)
library(dplyr)
cairo_df <- as.data.frame(cairo_hexagons) #convert sf to dataframe
cairo_df %>% group_by(znng_t_) %>%
summarise(score_formal = (access_per_60_formal %*% pop2018cap) / sum(pop2018cap), # formal transit modes
jobs_formal = (jobs_60_formal %*% pop2018cap) / sum(pop2018cap),
jobs_all = (jobs_60_all %*% pop2018cap) / sum(pop2018cap),
score_all = (access_per_60_all %*% pop2018cap) / sum(pop2018cap), # all transit modes
inc_abs = (score_all - score_formal), # accessibility increase
inc_rel = ((score_all - score_formal)/ score_formal)*100, # % increase FROM formal modes TO all modes
inc_factor = (score_all/score_formal)) # increase as a multiple
5 Maps
Lets visualize how accessibility varies throughout the study area
5.1 Importing Shapefiles for Visulizations
library(sf)
# to plot metro as line
cairo_metro <- st_read("Cairo Shapefiles/Metro_Trips_TfC.shp")
# to add text labels for the new towns on the outskirts
cairo_new_towns <- st_read("Cairo Shapefiles/Central-Inner_Shiyakha_NDC_CAPMAS.shp") %>%
filter(zoning_tfc == "Outer")
# Remove 10th of Ramadan City and Giza_Outer Labels)
cairo_new_towns <- cairo_new_towns[!duplicated(cairo_new_towns$name_citya),] %>%
filter(!grepl('Giza_Outer|10th of Ramdan', name_citya))
5.2 Visualizing Job Reach
5.2.1 All modes of public transport
library(tmap)
tmap_mode("plot")
# breaks argument used instead of style
breaks = c(0, 10000, 20000, 100000, 500000, 1000000, 2500000)
tm_shape(cairo_hexagons) +
tm_fill("jobs_60_all",
#style = "jenks", # used instead of user defined breaks
breaks = breaks,
palette = 'GnBu', # for specific colors: c('#d7191c', '#fdae61', '#ffffbf', '#abdda4', '#2b83ba', '#253494')
#legend.hist = TRUE,
title = "Jobs Within 60 min \n(Public Transit, AM Peak)") +
tm_layout(title = "Accessibility Across the GCR", # add a title
title.size = 1.5,
title.color = "azure4",
title.position = c("left", "top"),
inner.margins = c(0.09, 0.10, 0.10, 0.08), # increase map margins to make space for legend
fontfamily = 'Georgia',
#bg.color = "grey95",
frame = TRUE) +
tm_borders(col = "grey40", lwd = 0.1)+
tm_legend(title.size=0.9,
text.size = 0.6,
#frame = "grey",
position = c("right", "bottom")) +
tm_scale_bar(color.dark = "gray60",
position = c("left", "bottom")) +
tm_shape(cairo_metro) +
tm_lines(col = 'firebrick4', lwd = 1.5, alpha = 0.8) +
tm_add_legend(type = "line", labels = 'Cairo Metro', col = 'firebrick4', lwd = 1.5) +
tm_shape(cairo_new_towns) +
tm_text(text = "name_citya", col = 'black', size = 0.7,
alpha = 0.7, bg.color = "white", bg.alpha = 0.5)
LS0tCnRpdGxlOiAiVHJhbnNwb3J0IEFjY2Vzc2liaWxpdHkgaW4gdGhlIEdDUiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVG8gY29uZHVjdCBhbiBhY2Nlc3NpYmlsaXR5IGFuYWx5c2lzLCB3ZSBuZWVkOgoxKSBPU00gUm9hZCBEYXRhCjIpIFNoYXBlZmlsZSBvZiB0aGUgc3R1ZHkgYXJlYQozKSBHVEZTIGZlZWQgCgpXZSB1c2UgdGhlc2UgaW5wdXRzIHRvIG1ha2UgcXVlcmllcyBvbiBPcGVuIFRyaXAgUGxhbm5lcgoKIyMgUGFydCAxOiBPU00gRGF0YQoKRGVmaW5lIHRoZSBnZW9ncmFwaGljIGV4dGVudHMgYXMgYSB2ZWN0b3Igb2YgYyh4bWluLCB5bWluLCB4bWF4LCB5bWF4KQoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9VFJVRX0KbGlicmFyeShvc21kYXRhKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCiMgb3BxIHVzZWQgdG8gYnVpbGQgYW4gb3ZlcnBhc3MgcXVlcnkKIyB3ZSBkZWZpbmUgdGhlIGdlb2dyYXBoaWMgZXh0ZW50cyBvZiB0aGUgYXJlYSB3ZSB3YW50IHRvIHF1ZXJ5IGZyb20gT1NNLiBJIGhhdmUgYWRkZWQgdGhlIGV4dGVudHMgb2YgdGhlIEdDUiBhcyBhIHZlY3RvcgojIGFkZF9vc21fZmVhdHVyZSgpIHVzZWQgdG8gc3BlY2lmeSB3aGF0IHdlIGFyZSBxdWVyeWluZy4gSXQgdGFrZXMga2V5LCB2YWx1ZSBwYWlycyBidXQgaGVyZSBJIHdhbnQgYWxsIHR5cGVzIG9mIGhpZ2h3YXlzIHNvIEkgc3BlY2lmeSB0aGUga2V5IG9ubHkKIyBGb3IgYSBsaXN0IG9mIHNwZWNpZmljIHZhbHVlcyB0byBxdWVyeSwgc2VlIGh0dHBzOi8vd2lraS5vcGVuc3RyZWV0bWFwLm9yZy93aWtpL0tleTpoaWdod2F5IApxdWVyeSA8LSBvcHEoYmJveCA9IGMoMzAuODAxMzY5LCAyOS43MDUwODAsIDMxLjg3NDQ4MywgMzAuMzkwNjY0KSkgJT4lCiAgYWRkX29zbV9mZWF0dXJlKGtleSA9ICdoaWdod2F5JykgICAKCiMgaGVyZSB3ZSB3YW50IHRoZSBkYXRhIGluIHhtbCBmb3JtYXQuIElkZWFsbHkgd2Ugd291bGQgdXNlIHBiZiBkYXRhLCBhcyBpdCBpcyBtb3JlIGNvbXBhY3QgYW5kIG1ha2VzIHRoZSBhbmFseXNpcyBmYXN0ZXIsIGJ1dCBvc21kYXRhX3BkZigpIHJldHVybnMgZW1wdHkgZmlsZXMuIElzc3VlIGRpc2N1c3NlZCBoZXJlIGh0dHBzOi8vZ2l0aHViLmNvbS9yb3BlbnNjaS9vc21kYXRhL2lzc3Vlcy83NApvc21kYXRhX3htbChxdWVyeSwgZmlsZW5hbWUgPSAnZ3JlYXRlci1jYWlyby5vc20nKQoKIyB0aGUgeG1sIGZpbGUgaXMgZG93bmxvYWRlZCB0byB0aGUgcHJvamVjdCB3b3JraW5nIGRpcmVjdG9yeQpgYGAKCgpZb3UgY2FuIHBsb3QgdG8gc2VlIGlmIHRoZSByb2FkcyBoYXZlIGJlZW4gZG93bmxvYWRlZC4gVGhpcyB0YWtlcyBhcm91bmQgMTAgbWludXRlcyBhbmQgdGhlIHNwIGZpbGUgaXMgaHVnZS4KSSB3aWxsIG5vdCBkbyBpdCBoZXJlLCBidXQgZm9yIGNoZWNraW5nIHB1cnBvc2VzLCB0aGlzIGlzIGhvdyBpdCB3b3VsZCBiZSBkb25lOgoKMSkgY29udmVydCB4bWwgdG8gc3AgdXNpbmcgb3NtZGF0YV9zcChxdWVyeSkKCjIpIHBsb3Qgc3A6OnBsb3QoaGlnaHdheXNfZ3JlYXRlckNhaXJvJG9zbV9saW5lcykKaGlnaHdheXNfZ3JlYXRlckNhaXJvIDwtIG9zbWRhdGFfc3AocXVlcnkpCgoKQW4gYWx0ZXJuYXRpdmUgdG8geG1sIGlzIHBiZiBmaWxlcy4gVGhlc2UgYXJlIG1vcmUgY29tcGFjdCBhbmQgcHJlc3VtYWJseSBtYWtlIHRoZSBmb3J0aGNvbWluZyBhbmFseXNpcyBmYXN0ZXIuClRoZXkgY2FuIGJlIGRvd25sb2FkZWQgZnJvbSB0aGUgSE9UIE9TTSBleHBvcnQgdG9vbCBodHRwczovL2V4cG9ydC5ob3Rvc20ub3JnL2VuL3YzL2V4cG9ydHMvbmV3L2Zvcm1hdHM/ZmJjbGlkPUl3QVIzbV8xYkRaSzJzWXNBLWp0UFJQWUdnOVI3a1RxdDVoempQODh4M3AyeXZSZDRpOXRyMTFpMnRHNjAKCkkgdHJpZWQgdG8gdXNlIGEgcGJmIGZpbGUgYnV0IHRoZSBhbmFseXNpcyB3YXMgbm90IHF1aWNrZXIgc28gSSBzdHVjayB0byB0aGUgeG1sIGZpbGUgZXh0cmFjdGVkIGZyb20gcgoKIyMgUGFydCAyOiBJbXBvcnRpbmcgU2hhcGVmaWxlcyBmb3IgQW5hbHlzaXMKCkluIHRoaXMgc3RlcCBJIGltcG9ydCBhIHNoYXBlZmlsZSBvZiBHcmVhdGVyIENhaXJvLiBUaGUgc2hhcGVmaWxlIGhhcyB0aGUgcmVnaW9uIGRpdmlkZWQgaW50byBoZXhhZ29ucyB3aXRoIHRoZSBwb3B1bGF0aW9uIGFuZCBudW1iZXIgb2Ygam9icyBpbiBlYWNoIGhleGFnb24KCmBgYHtyfQpsaWJyYXJ5KHNmKQoKIyBpbXBvcnQgdGhlIHNoYXBlZmlsZQpjYWlyb19oZXhhZ29ucyA8LSBzdF9yZWFkKCJDYWlybyBTaGFwZWZpbGVzL0gzLXJlczgvSDNyZXMtOF9HQ1JfNDMyNi5zaHAiKQpgYGAKCkl0IGlzIGltcG9ydGVkIGFuZCBpbiB0aGUgY29ycmVjdGVkIGNycyBFUFNHOjQzMjYKCkxldCdzIHBsb3QgdG8gY2hlY2sgaWYgaXQgbG9va3MgcmlnaHQKYGBge3J9CmxpYnJhcnkodG1hcCkKCiMgcGxvdAp0bV9zaGFwZShjYWlyb19oZXhhZ29ucykgKwogICAgICAgICAgICAgIHRtX3BvbHlnb25zKCkKYGBgCgpJdCBsb29rcyByaWdodCBidXQgdGhlIGFyZWEgaW4gdGhlIHRvcCByaWdodCBuZWVkcyB0byBiZSByZW1vdmVkLiBUaGlzIGlzIHRoZSAxMHRoIG9mIFJhbWFkYW4gQ2l0eSBhbmQgaXMgbm90IHBhcnQgb2YgdGhlIEdDUgpgYGB7cn0KIyBSZW1vdmUgMTB0aCBvZiBSYW1hZGFuIChpdCBpcyBpbiBTaGFycWV5YSkKIyBkcGx5cjo6ZmlsdGVyIHdpdGggIWdyZXBsLiBUaGlzIHJlbW92ZXMgYWxsIHJvd3MgdGhhdCBjb250YWluIDEwdGggb2YgUmFtYWRhbiBPUiAnMTAgb2YgUmFtbWFkYW4nIGluIG5hbV90ZmMgY29sdW1uCgpjYWlyb19oZXhhZ29ucyA8LSBjYWlyb19oZXhhZ29ucyAlPiUgZmlsdGVyKCFncmVwbCgnMTB0aCBvZiBSYW1hZGFufDEwIE9mIFJhbW1hZGFuJywgbmFtX3RmYykpCgojIHBsb3QgdG8gY2hlY2sgdGhhdCBpdCB3b3JrZWQKdG1fc2hhcGUoY2Fpcm9faGV4YWdvbnMpICsKICAgICAgICAgICAgICB0bV9wb2x5Z29ucygpCmBgYAoKVGhlIG5leHQgc3RlcCBpcyB0byBnZXQgdGhlIGNlbnRyb2lkIG9mIGVhY2ggaGV4YWdvbi4gClRoaXMgaXMgYmVjYXVzZSBPcGVuIFRyaXAgUGxhbm5lciB0YWtlcyBxdWVyaWVzIGZyb20gbGF0IGxvbiBjb29yZGluYXRlcwoKYGBge3J9CiMgZ2V0IGNlbnRyb2lkcyBpbiBzZXBlcmF0ZSBmZWF0dXJlIApoM19jZW50cm9pZHMgPC0gc3RfY2VudHJvaWQoY2Fpcm9faGV4YWdvbnMpCiMgY2VudHJvaWRzIGFyZSBwcm92aWRlZCBhcyBnZW9tZXRyeSBidXQgd2UgbmVlZCB0aGUgbGF0IGFuZCBsb24gaW4gdHdvIHNlcGVyYXRlIGNvbHVtbnMKIyBJIHVzZSB0aGlzIGZ1bmN0aW9uIHRvIHNwbGl0IGMobGF0LCBsb24pIHRvIHR3byBzZXBlcmF0ZSBjb2x1bW5zICAKIyBGUk9NIEpNIExvbmRvbiAoaHR0cHM6Ly9naXRodWIuY29tL3Itc3BhdGlhbC9zZi9pc3N1ZXMvMjMxKQojIGxhdCA9IFkgbG9uID0gWApzZmNfYXNfY29scyA8LSBmdW5jdGlvbih4LCBuYW1lcyA9IGMoImxvbiIsImxhdCIpKSB7CiAgc3RvcGlmbm90KGluaGVyaXRzKHgsInNmIikgJiYgaW5oZXJpdHMoc2Y6OnN0X2dlb21ldHJ5KHgpLCJzZmNfUE9JTlQiKSkKICByZXQgPC0gc2Y6OnN0X2Nvb3JkaW5hdGVzKHgpCiAgcmV0IDwtIHRpYmJsZTo6YXNfdGliYmxlKHJldCkKICBzdG9waWZub3QobGVuZ3RoKG5hbWVzKSA9PSBuY29sKHJldCkpCiAgeCA8LSB4WyAsICFuYW1lcyh4KSAlaW4lIG5hbWVzXQogIHJldCA8LSBzZXROYW1lcyhyZXQsbmFtZXMpCiAgZHBseXI6OmJpbmRfY29scyh4LHJldCkKICAKfQoKIyBhZGQgbG9uIGFuZCBsYXQgY29sdW1ucyB0byBkYXRhZnJhbWUgdXNpbmcgc2ZjX2FzX2NvbHMgZnVuY3Rpb24KaDNfY2VudHJvaWRzIDwtIHNmY19hc19jb2xzKGgzX2NlbnRyb2lkcykKCiMgdHdvIGFkZGl0aW9uYWwgY29sdW1ucyAobGF0IGFuZCBsb24pIGhhdmUgYmVlbiBhZGRlZCB0byB0aGUgc2ZjCmgzX2NlbnRyb2lkcwpgYGAKCiMjIFBhcnQgMzogT3BlbiBUcmlwIFBsYW5uZXIKCkkgdXNlIHRoZSBwYWNrYWdlIGJ5IE1hbGNvbG0gTW9yZ2FuIHRvIHF1ZXJ5IE9UUCB0aHJvdWdoIFIgaHR0cHM6Ly9kb2NzLnJvcGVuc2NpLm9yZy9vcGVudHJpcHBsYW5uZXIvaW5kZXguaHRtbAoKIyMjIDMuMTogU2V0dGluZyBVcCBPVFAKCmBgYHtyfQpsaWJyYXJ5KG9wZW50cmlwcGxhbm5lcikKCiMgT1RQIGV4cGVjdHMgaXRzIGRhdGEgdG8gYmUgc3RvcmVkIGluIGEgc3BlY2lmaWMgc3RydWN0dXJlCiMgSSBjcmVhdGUgYSBuZXcgZm9sZGVyIGNhbGxlZCBPcGVuLVRyaXAtUGxhbm5lciBpbiBteSB3ZAojZGlyLmNyZWF0ZSgpIGNyZWF0ZXMgYSBzdWJmb2xkZXIgY2FsbGVkICJPVFAtQ2Fpcm8tQWxsIgpwYXRoX2RhdGEgPC0gZmlsZS5wYXRoKCJPcGVuLVRyaXAtUGxhbm5lciIsICJPVFAtQ2Fpcm8tQWxsIikKZGlyLmNyZWF0ZShwYXRoX2RhdGEpIApgYGAKCk5vdyB3ZSBuZWVkIHRvIGRvd25sb2FkIE9UUCBhbmQgc2F2ZSBpdCB0byB0aGUgIk9UUC1DYWlyby1BbGwiIHN1YmZvbGRlcgoKYGBge3J9CiMgb3RwX2RsX2phciBmdW5jdGlvbiB3aWxsIGRvd25sb2FkIHRoZSBPVFAgYW5kIHNhdmUgaXQgaW4gdGhlIGZvbGRlciB3ZSBjcmVhdGVkCiMgVGhlIGZ1bmN0aW9uIHJldHVybnMgdGhlIHBhdGggdG8gdGhlIE9UUCBqYXIgZmlsZS4KcGF0aF9vdHAgPC0gb3RwX2RsX2phcihwYXRoX2RhdGEpCmBgYAoKVGhlIG5leHQgc3RlcCBpcyB0byBhZGQgdGhlIEdURlMgYW5kIE9TTSBmaWxlcyB0byB0aGUgY3JlYXRlZCBmb2xkZXIuIApDcmVhdGUgYSBzdWJmb2xkZXIgaW4gT1RQLUNhaXJvLUFsbCBjYWxsZWQgZ3JhcGhzIHRoZW4gY3JlYXRlIGEgc3ViZm9sZGVyIGluIGdyYXBocyBjYWxsZWQgZGVmYXVsdApBZGQgdGhlIE9TTSBhbmQgR1RGUyBpbnNpZGUgJ2RlZmF1bHQnCkkgZm9sbG93ZWQgdGhpcyBmb2xkZXIgc3RydWN0dXJlOgogCiogT1RQLUNhaXJvLUFsbCAgICAgICAgICAgICAoZm9sZGVyIC0gY3JlYXRlZCBhYm92ZSkKICAgICogZ3JhcGhzICAgKGZvbGRlcikKICAgICAgICAgKiBkZWZhdWx0ICAoZm9sZGVyKQogICAgICAgICAgICAgICogb3NtLnBiZiAgICAoZGF0YSkgICAgICAgICAgIyBSZXF1aXJlZCBPU00gcm9hZCBtYXAgICAtIEFEREVECiAgICAgICAgICAgICAgKiBndGZzLnppcCAgIChkYXRhKSAgICAgICAgICAjIE9wdGlvbmFsIEdURlMgZGF0YSAgICAgIC0gQURERUQKICAgICAgIAoKSSBhZGQgYSBHVEZTIGZpbGUgd2l0aCBhbGwgcHVibGljIHRyYW5zcG9ydCBpbiB0aGUgR0NSIChGb3JtYWwgKyBJbmZvcm1hbCkKCihhdmFpbGFibGUgaGVyZSBodHRwczovL2dpdGh1Yi5jb20vSHVzc2Vpbi1NYWhmb3V6L0dJUy1Db3Vyc2V3b3JrL3RyZWUvbWFzdGVyL0dURlMlMjBGZWVkcykKClRoaXMgZGF0YSBpcyB1c2VkIHRvIGJ1aWxkIGEgR3JhcGggb2JqZWN0IHdoaWNoIGlzIHRoZSBiYXNlIGZvciBPcGVuVHJpcFBsYW5uZXIKYGBge3J9CiMgQnVpbGRpbmcgYW4gT1RQIEdyYXBoCgojIFRoaXMgY29kZSB3aWxsIGNyZWF0ZSBhIG5ldyBmaWxlIEdyYXBoLm9iaiB0aGF0IHdpbGwgYmUgc2F2ZWQgaW4gdGhlIGxvY2F0aW9uIGRlZmluZWQgYnkgcGF0aF9kYXRhLgojIG1lbW9yeSBhcmd1bWVudCBhc3NpZ25zIG1vcmUgbWVtb3J5IHRvIGJ1aWxkaW5nIGdyYXBoICh0byBzcGVlZCBpdCB1cCkuIFIgYXNzaWducyAyR0IgYnkgZGVmYXVsdApsb2cgPC0gb3RwX2J1aWxkX2dyYXBoKG90cCA9IHBhdGhfb3RwLCBkaXIgPSBwYXRoX2RhdGEsIG1lbW9yeSA9IDYwMDAsIGFuYWx5c3QgPSBUUlVFKSAgCmBgYAoKTm93IHdlIGFyZSByZWFkeSB0byBsYXVuY2ggT3BlblRyaXBQbGFubmVyCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQojIExhdW5jaCBPVFAgYW5kIGxvYWQgdGhlIGdyYXBoCm90cF9zZXR1cChvdHAgPSBwYXRoX290cCwgZGlyID0gcGF0aF9kYXRhKQoKIyBjb25uZWN0IHIgdG8gb3RwCm90cGNvbiA8LSBvdHBfY29ubmVjdCgpCmBgYAoKIyMjIDMuMjogUXVlcnlpbmcgT1RQCgpOb3cgdGhhdCBPVFAgaXMgbGF1bmNoZWQgYW5kIGNvbm5lY3RlZCB0byByLCB3ZSBjYW4gc3RhcnQgcXVlcnlpbmcKCmBgYHtyfQojIExPT1BJTkcgRlVOQ1RJT04gVE8gR0VUIFJFQUNIIElTT0NIUk9ORSBPRiBFQUNIIEhFWEFHT04gQ0VOVFJPSUQKCiMgdmFyaWFibGUgd2l0aCBudW1iZXIgb2YgaGV4YWdvbnMgKGZvciBsb29waW5nIGZ1bmN0aW9uKQpucm93cyA8LSBucm93KGgzX2NlbnRyb2lkcykKCiMgZW1wdHkgbGlzdCB0byBzdG9yZSBvdXRwdXQKcmVhY2hQb2x5Z29uczwtbGlzdCgpCgojIGdldCByZWFjaCBwb2x5Z29uIGZvciBlYWNoIGNlbnRyb2lkIGFuZCBzdG9yZSBpbiByZWFjaFBvbHlnb25zIGxpc3QKZm9yIChpIGluIDE6bnJvd3MpewogIHJlYWNoUG9seWdvbnNbW2ldXSA8LSBvdHBfaXNvY2hyb25lKG90cGNvbiA9IG90cGNvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmcm9tUGxhY2UgPSBjKGgzX2NlbnRyb2lkcyRsb25baV0sIGgzX2NlbnRyb2lkcyRsYXRbaV0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGUgPSBjKCJXQUxLIiwgIlRSQU5TSVQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhXYWxrRGlzdGFuY2UgPSAxMDAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGVfdGltZSA9IGFzLlBPU0lYY3Qoc3RycHRpbWUoIjIwMTktMDgtMDUgMDk6MDAiLCAiJVktJW0tJWQgJUg6JU0iKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3V0b2ZmU2VjID0gMzYwMCkgIyBDdXQgb2ZmcyBpbiBzZWNvbmRzCn0KCmBgYAoKTGV0cyBwbG90IG9uZSBvZiB0aGUgaXNvY2hyb25lcyB0byBzZWUgaWYgdGhpcyB3b3JrZWQKCmBgYHtyfQp0bWFwX21vZGUoInZpZXciKQoKdG1fc2hhcGUocmVhY2hQb2x5Z29uc1tbNTY4XV0pICsKICAgICAgICAgICAgdG1fZmlsbChjb2wgPSAiYW50aXF1ZXdoaXRlMiIpICsKICAgICAgICAgICAgdG1fYm9yZGVycygpCmBgYAoKIyMjIDMuNCBSZXBlYXRpbmcgUXVlcnkgZm9yIEZvcm1hbCBNb2RlcwoKTm93IGxldCdzIGRvIHRoZSBhbmFseXNpcyBhZ2FpbiB1c2luZyBhIEdURlMgZmVlZCB3aXRoIGZvcm1hbCB0cmFuc3BvcnQgbW9kZXMgb25seS4gVGhpcyBtZWFucyB3ZSBoYXZlIHRvIHN1YnNldCB0aGUgR1RGUyBmZWVkIGFuZCBrZWVwIG9ubHkgdGhlIGZvcm1hbCBhZ2VuY2llcy4gSW4gT3BlblRyaXBQbGFubmVyLCB0aGVyZSBpcyBhIGJhbm5lZEFnZW5jaWVzIGFyZ3VtZW50LCBidXQgdGhpcyBkb2VzIG5vdCBleGlzdCBpbiB0aGUgT3BlblRyaXBQbGFubmVyIFIgUGFja2FnZSAoc2VlIHRoZSBkb2N1bWVudGF0aW9uIGhlcmU6IGh0dHBzOi8vZG9jcy5yb3BlbnNjaS5vcmcvb3BlbnRyaXBwbGFubmVyL3JlZmVyZW5jZS9vdHBfaXNvY2hyb25lLmh0bWw/cT1vdHAlMjBfJTIwaXNvY2ggKQoKVGhpcyBpcyBkZWZpbml0ZWx5IGEgdXNlZnVsIGVuaGFuY2VtZW50IGFuZCBJIGFtIHRoaW5raW5nIG9mIHJlcXVlc3RpbmcgaXQgb24gR2l0aHViIAoKVGhlcmUgaXMgb25lIFIgcGFja2FnZSB0aGF0IGhhbmRsZXMgR1RGUyBmZWVkcyBidXQgaXQgZG9lc24ndCBmaWx0ZXIgYnkgYWdlbmNpZXMgaHR0cHM6Ly9naXRodWIuY29tL2lwZWFHSVQvZ3RmczJncHMvYmxvYi9tYXN0ZXIvdmlnbmV0dGVzL2ludHJvX3RvX2d0ZnMyZ3BzLm1kCgpJIGVuZGVkIHVwIHVzaW5nIGEgamF2YSBhcHBsaWNhdGlvbiB0aGF0IGNhbiBiZSBhY2Nlc3NlZCBmcm9tIHRoZSBjb21tYW5kIGxpbmUuIEl0IHN1YnNldHMgR1RGUyBmZWVkcyBpbiBhIG1hdHRlciBvZiBzZWNvbmRzLiBDaGVjayB0aGUgJ0hvdyB0byBSZWR1Y2UgeW91ciBHVEZTJyBzZWN0aW9uIApodHRwOi8vZGV2ZWxvcGVyLm9uZWJ1c2F3YXkub3JnL21vZHVsZXMvb25lYnVzYXdheS1ndGZzLW1vZHVsZXMvMS4zLjQtU05BUFNIT1Qvb25lYnVzYXdheS1ndGZzLXRyYW5zZm9ybWVyLWNsaS5odG1sCgpJIGFkZGVkIGEgdGV4dCBmaWxlIHdpdGggYXJndW1lbnRzIGZvciB0aGUgYWdlbmNpZXMgSSB3YW50IHRvIHJldGFpbiAoZm9ybWFsIGFnZW5jaWVzKQp7Im9wIjoicmV0YWluIiwgIm1hdGNoIjp7ImZpbGUiOiJhZ2VuY3kudHh0IiwgImFnZW5jeV9pZCI6IkNUQSJ9fQp7Im9wIjoicmV0YWluIiwgIm1hdGNoIjp7ImZpbGUiOiJhZ2VuY3kudHh0IiwgImFnZW5jeV9pZCI6IkNUQV9NIn19Cnsib3AiOiJyZXRhaW4iLCAibWF0Y2giOnsiZmlsZSI6ImFnZW5jeS50eHQiLCAiYWdlbmN5X2lkIjoiTkFUIn19CgpOb3cgcmVwZWF0IHRoZSBzdGVwcyBkb25lIGFib3ZlIHRvIGNyZWF0ZSBhIGRpcmVjdG9yeQoKRmlyc3QgdGhpbmcgaXMgdG8gZGlzY29ubmVjdCBmcm9tIE9UUC4gaSBmb3VuZCB0aGF0IGlmIEkgZG9uJ3QgZG8gdGhpcywgT1RQIHJ1bnMgdGhlIGFuYWx5c2lzIG9uIHRoZSBvbGQgZ3JhcGggdGhhdCBoYXMgdGhlIEdURlMgZmlsZSB3aXRoIGFsbCBhZ2VuY2llcyAoZXZlbiB0aG91Z2ggSSBwcm92aWRlIGEgbmV3IHBhdGguLi4pCgpgYGB7cn0Kb3RwX3N0b3Aod2Fybj1GQUxTRSkKYGBgCgpOb3cgbGV0J3MgZ2l2ZSBpdCB0aGUgbmV3IHBhdGgKCmBgYHtyfQpsaWJyYXJ5KG9wZW50cmlwcGxhbm5lcikKIyBjcmVhdGUgc3ViZm9sZGVyIGNhbGxlZCAiT1RQLUNhaXJvLUZvcm1hbCIKcGF0aF9kYXRhIDwtIGZpbGUucGF0aCgiT3Blbi1UcmlwLVBsYW5uZXIiLCAiT1RQLUNhaXJvLUZvcm1hbCIpCmRpci5jcmVhdGUocGF0aF9kYXRhKSAKIyBvdHBfZGxfamFyIGZ1bmN0aW9uIHdpbGwgZG93bmxvYWQgdGhlIE9UUCBhbmQgc2F2ZSBpdCBpbiB0aGUgZm9sZGVyIHdlIGNyZWF0ZWQKIyBUaGUgZnVuY3Rpb24gcmV0dXJucyB0aGUgcGF0aCB0byB0aGUgT1RQIGphciBmaWxlLgpwYXRoX290cCA8LSBvdHBfZGxfamFyKHBhdGhfZGF0YSkKYGBgCgoKQmVmb3JlIGJ1aWxkaW5nIHRoZSBncmFwaCwgYWRkIHRoZSBHVEZTIGZlZWQgYW5kIHRoZSBvc20gcm9hZCBsYXllciBpbiB0aGUgY29ycmVjdCBmb2xkZXIuIFNlZSAzLjEuMTogU2V0dGluZyBVUCBPVFAKCgpgYGB7cn0KbG9nIDwtIG90cF9idWlsZF9ncmFwaChvdHAgPSBwYXRoX290cCwgZGlyID0gcGF0aF9kYXRhLCBtZW1vcnkgPSA2MDAwLCBhbmFseXN0ID0gVFJVRSkgIAojIExhdW5jaCBPVFAgYW5kIGxvYWQgdGhlIGdyYXBoCm90cF9zZXR1cChvdHAgPSBwYXRoX290cCwgZGlyID0gcGF0aF9kYXRhKQoKIyBjb25uZWN0IHIgdG8gb3RwCm90cGNvbiA8LSBvdHBfY29ubmVjdCgpCmBgYAoKTm93IHdlIGNhbiBydW4gdGhlIGFuYWx5c2lzIGFnYWluIHVzaW5nIGZvcm1hbCBhZ2VuY2llcyBvbmx5LiAKCgpgYGB7cn0KIyBlbXB0eSBsaXN0IHRvIHN0b3JlIG91dHB1dApyZWFjaFBvbHlnb25zRm9ybWFsPC1saXN0KCkKIyBnZXQgcmVhY2ggcG9seWdvbiBmb3IgZWFjaCBjZW50cm9pZCBhbmQgc3RvcmUgaW4gcmVhY2hQb2x5Z29ucyBsaXN0CmZvciAoaSBpbiAxOm5yb3dzKXsKICByZWFjaFBvbHlnb25zRm9ybWFsW1tpXV0gPC0gb3RwX2lzb2Nocm9uZShvdHBjb24gPSBvdHBjb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnJvbVBsYWNlID0gYyhoM19jZW50cm9pZHMkbG9uW2ldLCBoM19jZW50cm9pZHMkbGF0W2ldKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlID0gYygiV0FMSyIsICJUUkFOU0lUIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4V2Fsa0Rpc3RhbmNlID0gMTAwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRlX3RpbWUgPSBhcy5QT1NJWGN0KHN0cnB0aW1lKCIyMDE5LTA4LTA1IDA5OjAwIiwgIiVZLSVtLSVkICVIOiVNIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1dG9mZlNlYyA9IDM2MDApICMgQ3V0IG9mZnMgaW4gc2Vjb25kcwp9CmBgYAoKCk5vdyB3ZSdyZSBkb25lIHdpdGggb3RwLiBNYWtlIHN1cmUgdG8gdGVybWluYXRlIHRoZSBKYXZhIE9UUCBpbnN0YW5jZSAoaXQgdGFrZXMgdXAgMkdCIG9mIHByZWNpb3VzIG1lbW9yeSkKCmBgYHtyfQojIHdhcm4gZXF1YWxzIGZhbHNlIHNvIHRoYXQgaXQgZG9lc24ndCBwcm9tcHQgbWUgdG8gY29uZmlybQpvdHBfc3RvcCh3YXJuPUZBTFNFKQpgYGAKCiMjIyAzLjMgQ2FsY3VsYXRpbmcgSm9iIFJlYWNoIGFuZCBBY2Nlc3NpYmlsaXR5IFNjb3JlcyAKCiMjIyMgMy4zLjEgSW50ZXJzZWN0aW5nIElzb2Nocm9uZXMgd2l0aCBIZXhhZ29ucwoKTm93IHRoYXQgd2UgaGF2ZSB0aGUgaXNvY2hyb25lcywgd2UgbmVlZCB0byBjYWxjdWxhdGUgaG93IG1hbnkgam9icyBlYWNoIGlzb2Nocm9uZSBpbnRlcnNlY3RzIHdpdGguIAoKRm9yIGVhY2ggaW50ZXJzZWN0ZWQgaGV4YWdvbiwgd2UgZ2V0OiAoYXJlYSBvZiBpbnRlcnNlY3Rpb24gLyB0b3RhbCBhcmVhIG9mIGhleGFnb24pICogam9icyBpbiBIZXhhZ29uCgpXZSB0aGVuIHN1bSBhbGwgdGhlIHJlc3VsdHMgdG8gZ2V0IHRoZSBudW1iZXIgb2Ygam9icyBhY2Nlc3NpYmxlIGZvciB0aGUgaGV4YWdvbiB3ZSBhcmUgcXVlcnlpbmcgZnJvbQoKQUxMIEFHRU5DSUVTOgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBlbXB0eSBsaXN0IHRvIHN0b3JlIG91dHB1dAp0b3RhbEpvYnMgPC1saXN0KCkKCmxpYnJhcnkobHdnZW9tKSAjIGZvciBzdF9tYWtlX3ZhbGlkICh0aGlzIGhhbmRsZXMgaW52YWxpZCBnZW9tZXRyaWVzIGh0dHBzOi8vZ2l0aHViLmNvbS9yLXNwYXRpYWwvc2YvaXNzdWVzLzg3MCkKCmZvciAoaSBpbiAxOm5yb3dzKXsKICB0b3RhbEpvYnNbW2ldXSA8LSAwICAgICMgdGhlcmUgYXJlIHNvbWUgcG9pbnRzIHRoYXQgT1RQIGNvdWxkbid0IHJvdXRlIGZyb20uIHRyeSgpIHVzZWQgdG8gYXNzaWduIDAgdmFsdWUgdG8gdGhlc2UgcG9pbnRzCiAgdHJ5KHsKICAgIHRvdGFsSm9ic1tbaV1dIDwtIHJlYWNoUG9seWdvbnNbW2ldXSAlPiUgCiAgICAgIHN0X21ha2VfdmFsaWQoKSAlPiUgICAjIHNvbWUgZ2VvbWV0cmllcyBhcmUgaW52bGlkICh0aGlzIHByZXZlbnRzIGFuIGVycm9yKQogICAgICBzdF9pbnRlcnNlY3Rpb24oY2Fpcm9faGV4YWdvbnMpICU+JSAgICAgICAgICAjIGludGVyc2VjdCByZWFjaFBvbHlnb24gd2l0aCBjYWlyb19oZXhhZ29ucwogICAgICBtdXRhdGUoaW50X2FyZWEgPSAoc3RfYXJlYSguKS8xMDAwMDAwKSAlPiUgYXMubnVtZXJpYygpKSAlPiUgIyBhZGQgY29sdW1uIHdpdGggaW50ZXJzZWN0aW9uIGFyZWEgd2l0aCBlYWNoIGhleGFnb24KICAgICAgbXV0YXRlKGludF9qb2JzID0gKGludF9hcmVhL2FyZWEpKmpvYnNMRlNjb3UpICU+JSAjIGFkZCBjb2x1bW4gd2l0aCBpbnRfam9iczogam9icyBpbnRlcnNlY3RlZAogICAgICBzdW1tYXJpc2UodG90YWxfam9icyA9IHN1bShpbnRfam9icykpICAjIHN1bW1hcml6ZSBhbmQgZ2V0IHRoZSBzdW0gb2Ygam9icyBpbnRlcnNlY3RlZCBieSByZWFjaFBvbHlnb24KICB9KQp9CgpgYGAKCkZPUk1BTCBBR0VOQ0lFUzoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgZW1wdHkgbGlzdCB0byBzdG9yZSBvdXRwdXQKdG90YWxKb2JzRm9ybWFsIDwtbGlzdCgpCgpsaWJyYXJ5KGx3Z2VvbSkgIyBmb3Igc3RfbWFrZV92YWxpZCAodGhpcyBoYW5kbGVzIGludmFsaWQgZ2VvbWV0cmllcyBodHRwczovL2dpdGh1Yi5jb20vci1zcGF0aWFsL3NmL2lzc3Vlcy84NzApCgpmb3IgKGkgaW4gMTpucm93cyl7CiAgdG90YWxKb2JzRm9ybWFsW1tpXV0gPC0gMCAjIHRoZXJlIGFyZSBzb21lIHBvaW50cyB0aGF0IE9UUCBjb3VsZG4ndCByb3V0ZSBmcm9tLiB0cnkoKSB1c2VkIHRvIGFzc2lnbiAwIHZhbHVlIHRvIHRoZXNlIHBvaW50cwogIHRyeSh7CiAgICB0b3RhbEpvYnNGb3JtYWxbW2ldXSA8LSByZWFjaFBvbHlnb25zRm9ybWFsW1tpXV0gJT4lIAogICAgICBzdF9tYWtlX3ZhbGlkKCkgJT4lICAgIyBzb21lIGdlb21ldHJpZXMgYXJlIGludmxpZCAodGhpcyBwcmV2ZW50cyBhbiBlcnJvcikKICAgICAgc3RfaW50ZXJzZWN0aW9uKGNhaXJvX2hleGFnb25zKSAlPiUgICAgICAgICAgIyBpbnRlcnNlY3QgcmVhY2hQb2x5Z29uIHdpdGggY2Fpcm9faGV4YWdvbnMKICAgICAgbXV0YXRlKGludF9hcmVhID0gKHN0X2FyZWEoLikvMTAwMDAwMCkgJT4lIGFzLm51bWVyaWMoKSkgJT4lICMgYWRkIGNvbHVtbiB3aXRoIGludGVyc2VjdGlvbiBhcmVhIHdpdGggZWFjaCBoZXhhZ29uCiAgICAgIG11dGF0ZShpbnRfam9icyA9IChpbnRfYXJlYS9hcmVhKSpqb2JzTEZTY291KSAlPiUgIyBpbnRfam9icyBpcyB0aGUgam9icyBpbnRlcnNlY3RlZCAKICAgICAgc3VtbWFyaXNlKHRvdGFsX2pvYnMgPSBzdW0oaW50X2pvYnMpKSAgIyBnZXRzIG9uZSB2YWx1ZSBmb3Igam9icyBpbnRlcnNlY3RlZCBieSByZWFjaFBvbHlnb24KICB9KQp9CmBgYAoKTm93IHRoYXQgd2UgaGF2ZSB0aGUgam9iIHJlYWNoIG9mIGVhY2ggaGV4YWdvbiwgd2UgbmVlZCB0byB0cmFuc2ZlciB0aGVzZSB2YWx1ZXMgZm9ybSB0aGUgdG90YWxKb2JzIGxpc3QgYmFjayB0byB0aGUgY2Fpcm9faGV4YWdvbnMgc2Ygc28gdGhhdCB3ZSBjYW4gY2FsY3VsYXRlIGFjY2Vzc2liaWxpdHkgc2NvcmVzIGFuZCwgZXZlbnR1YWxseSwgcGxvdCB0aGUgcmVzdWx0cwoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBhZGQgYSBjb2x1bW4gZm9yIHRoZSBudW1iZXIgb2Ygam9icyByZWFjaGFibGUgd2l0aGluIDYwIG1pbnV0ZXMgdXNpbmcgQUxMIE1PREVTIG9mIHB1YmxpYyB0cmFuc3BvcnQKZm9yIChpIGluIDE6bnJvd3MpewogIGNhaXJvX2hleGFnb25zJGpvYnNfNjBfYWxsW2ldIDwtIDAgICAgI3NldCBkZWZhdWx0IHZhbHVlID0gTzogd2lsbCBiZSBnaXZlbiB0byBiYWQgZ2VvbWV0cmllcwogIHRyeSh7CiAgY2Fpcm9faGV4YWdvbnMkam9ic182MF9hbGxbaV0gPSB0b3RhbEpvYnNbW2ldXVtbMV1dICMgYWRkIHRvdGFsSm9icyB0byBuZXcgY29sdW1uIGluIGNhaXJvX2hleGFnb25zIGNhbGxlZCBqb2JzXzYwX2FsbAogIH0pCn0KCiNhZGQgY29sdW1uIGZvciB0aGUgbnVtYmVyIG9mIGpvYnMgcmVhY2hhYmxlIHdpdGhpbiA2MCBtaW51dGVzIHVzaW5nIEZPUk1BTCBNT0RFUyBwdWJsaWMgdHJhbnNwb3J0CmZvciAoaSBpbiAxOm5yb3dzKXsKICBjYWlyb19oZXhhZ29ucyRqb2JzXzYwX2Zvcm1hbFtpXSA8LSAwICAgICNzZXQgZGVmYXVsdCB2YWx1ZSA9IE86IHdpbGwgYmUgZ2l2ZW4gdG8gYmFkIGdlb21ldHJpZXMKICB0cnkoewogIGNhaXJvX2hleGFnb25zJGpvYnNfNjBfZm9ybWFsW2ldID0gdG90YWxKb2JzRm9ybWFsW1tpXV1bWzFdXSAjIGFkZCB0b3RhbEpvYnMgdG8gbmV3IGNvbHVtbiBpbiBjYWlyb19oZXhhZ29ucyBjYWxsZWQgam9ic182MF9hbGwKICB9KQp9CgpgYGAKCiMjIyMgMy4zLjIgQ2FsY3VsYXRpbmcgQWNjZXNzaWJpbGl0eSBTY29yZXMKCk5vdyBsZXRzIGdldCB0aGUgYWNjZXNzaWJpbGl0eSBzY29yZSBmb3IgZWFjaCBoZXhhZ29uLiBUaGlzIGlzIHRoZSAlIG9mIHRoZSB0b3RhbCBqb2JzIGluIHRoZSBHQ1IgdGhhdCBhcmUgcmVhY2hhYmxlIGZyb20gdGhpcyBoZXhhZ29uCgpgYGB7cn0KIyBwZXJjZW50YWdlIG9mIGpvYnMgYWNjZXNzaWJsZSBmcm9tIGVhY2ggaGV4YWdvbiAtIEFMTCBNT0RFUyBPRiBQVUJMSUMgVFJBTlNQT1JUCmNhaXJvX2hleGFnb25zJGFjY2Vzc19wZXJfNjBfYWxsID0gKChjYWlyb19oZXhhZ29ucyRqb2JzXzYwX2FsbC8gc3VtKGNhaXJvX2hleGFnb25zJGpvYnNMRlNjb3UpKSoxMDApCgojIHBlcmNlbnRhZ2Ugb2Ygam9icyBhY2Nlc3NpYmxlIGZyb20gZWFjaCBoZXhhZ29uIC0gRk9STUFMIE1PREVTIE9GIFBVQkxJQyBUUkFOU1BPUlQKY2Fpcm9faGV4YWdvbnMkYWNjZXNzX3Blcl82MF9mb3JtYWwgPSAoKGNhaXJvX2hleGFnb25zJGpvYnNfNjBfZm9ybWFsLyBzdW0oY2Fpcm9faGV4YWdvbnMkam9ic0xGU2NvdSkpKjEwMCkKYGBgCiAKIyMjIyAzLjMuMyBTY29yZXMgZm9yIEVudGlyZSBTdHVkeSBBcmVhCgpUbyBjYWxjdWxhdGUgdGhlIGFjY2Vzc2liaWxpdHkgc2NvcmUgZm9yIHRoZSBlbnRpcmUgc3R1ZHkgYXJlYToKCjEtIHdlaWdoIGVhY2ggaGV4YWdvbnMgam9iIHJlYWNoL2FjY2Vzc2liaWxpdHkgc2NvcmUgYnkgaXRzIHBvcHVsYXRpb24gKG11bHRpcGx5IHRoZW0pCgoyLSBkaXZpZGUgdGhlIHN1bSBieSB0aGUgdG90YWwgcG9wdWxhdGlvbiBvZiB0aGUgR0NSCgpgYGB7cn0KIyBhcy5udW1lcmljIHVzZWQgdG8gcmV0dXJuIGEgbnVtYmVyIGluc3RlYWQgb2YgYSBtYXRyaXgKCiMgQXZlcmFnZSBqb2JzIHJlYWNoZWQgdXNpbmcgYWxsIG1vZGVzCkdDUl9hdmdfam9ic19hbGwgPC0gYXMubnVtZXJpYygoY2Fpcm9faGV4YWdvbnMkam9ic182MF9hbGwgJSolIGNhaXJvX2hleGFnb25zJHBvcDIwMThjYXApIC8gc3VtKGNhaXJvX2hleGFnb25zJHBvcDIwMThjYXApKQpjYXQoIiBBdmVyYWdlIEpvYiBSZWFjaCBVc2luZyBBbGwgTW9kZXMgOiIsIEdDUl9hdmdfam9ic19hbGwsICJcbiIpCiMgQXZlcmFnZSBhY2Nlc3NpYmlsaXR5IHVzaW5nIGFsbCBNb2RlcyAoJSk6CkdDUl9hdmdfYWNjX2FsbCA8LSBhcy5udW1lcmljKChjYWlyb19oZXhhZ29ucyRhY2Nlc3NfcGVyXzYwX2FsbCAlKiUgY2Fpcm9faGV4YWdvbnMkcG9wMjAxOGNhcCkgLyBzdW0oY2Fpcm9faGV4YWdvbnMkcG9wMjAxOGNhcCkpCmNhdCgiIEF2ZXJhZ2UgQWNjZXNzaWJpbGl0eSBVc2luZyBBbGwgTW9kZXMgOiIsIEdDUl9hdmdfYWNjX2FsbCwgIlxuIikKIyBBdmVyYWdlIGpvYnMgcmVhY2hlZCB1c2luZyBmb3JtYWwgbW9kZXMKR0NSX2F2Z19qb2JzX2Zvcm1hbCA8LSBhcy5udW1lcmljKChjYWlyb19oZXhhZ29ucyRqb2JzXzYwX2Zvcm1hbCAlKiUgY2Fpcm9faGV4YWdvbnMkcG9wMjAxOGNhcCkgLyBzdW0oY2Fpcm9faGV4YWdvbnMkcG9wMjAxOGNhcCkpCmNhdCgiIEF2ZXJhZ2UgSm9iIFJlYWNoIFVzaW5nIE9ubHkgRm9ybWFsIE1vZGVzIDoiLCBHQ1JfYXZnX2pvYnNfZm9ybWFsLCAiXG4iKQojICMgQXZlcmFnZSBhY2Nlc3NpYmlsaXR5IHVzaW5nIGZvcm1hbCBNb2RlcyAoJSk6CkdDUl9hdmdfYWNjX2Zvcm1hbCA8LSBhcy5udW1lcmljKChjYWlyb19oZXhhZ29ucyRhY2Nlc3NfcGVyXzYwX2Zvcm1hbCAlKiUgY2Fpcm9faGV4YWdvbnMkcG9wMjAxOGNhcCkgLyBzdW0oY2Fpcm9faGV4YWdvbnMkcG9wMjAxOGNhcCkpCmNhdCgiIEF2ZXJhZ2UgQWNjZXNzaWJpbGl0eSBVc2luZyBPbmx5IEZvcm1hbCBNb2RlcyA6IiwgR0NSX2F2Z19hY2NfZm9ybWFsKQoKYGBgCkxldCdzIGNoZWNrIHRoZSByZWdpb25hbCBsZXZlbCBzY29yZXMgKHNjb3JlcyBmb3IgY2VudHJhbCwgaW5uZXIgYW5kIG91dGVyIENhaXJvKQoKYGBge3J9CmxpYnJhcnkoZHBseXIpCgpjYWlyb19kZiA8LSBhcy5kYXRhLmZyYW1lKGNhaXJvX2hleGFnb25zKSAgICNjb252ZXJ0IHNmIHRvIGRhdGFmcmFtZQpjYWlyb19kZiAlPiUgZ3JvdXBfYnkoem5uZ190XykgJT4lIAogIHN1bW1hcmlzZShzY29yZV9mb3JtYWwgPSAoYWNjZXNzX3Blcl82MF9mb3JtYWwgJSolIHBvcDIwMThjYXApIC8gc3VtKHBvcDIwMThjYXApLCAjIGZvcm1hbCB0cmFuc2l0IG1vZGVzCiAgICAgICAgICAgIGpvYnNfZm9ybWFsICA9IChqb2JzXzYwX2Zvcm1hbCAlKiUgcG9wMjAxOGNhcCkgLyBzdW0ocG9wMjAxOGNhcCksCiAgICAgICAgICAgIGpvYnNfYWxsICAgICA9IChqb2JzXzYwX2FsbCAlKiUgcG9wMjAxOGNhcCkgLyBzdW0ocG9wMjAxOGNhcCksCiAgICAgICAgICAgIHNjb3JlX2FsbCAgICA9IChhY2Nlc3NfcGVyXzYwX2FsbCAlKiUgcG9wMjAxOGNhcCkgLyBzdW0ocG9wMjAxOGNhcCksICMgYWxsIHRyYW5zaXQgbW9kZXMKICAgICAgICAgICAgaW5jX2FicyAgICAgID0gKHNjb3JlX2FsbCAtIHNjb3JlX2Zvcm1hbCksICAgIyBhY2Nlc3NpYmlsaXR5IGluY3JlYXNlCiAgICAgICAgICAgIGluY19yZWwgICAgICA9ICgoc2NvcmVfYWxsIC0gc2NvcmVfZm9ybWFsKS8gc2NvcmVfZm9ybWFsKSoxMDAsICAgIyAlIGluY3JlYXNlIEZST00gZm9ybWFsIG1vZGVzIFRPIGFsbCBtb2RlcwogICAgICAgICAgICBpbmNfZmFjdG9yICAgPSAoc2NvcmVfYWxsL3Njb3JlX2Zvcm1hbCkpICAgICMgaW5jcmVhc2UgYXMgYSBtdWx0aXBsZQpgYGAKCgojIyA0IENhbGN1bGF0aW5nIGltcGFjdCBvZiBJbmZvcm1hbCBUcmFuc3BvcnQKClRoZSBhbmFseXNpcyBoYXMgbm93IGJlZW4gcnVuIHR3aWNlOgoxKSBBbGwgcHVibGljIFRyYW5zcG9ydCBNb2RlcwoyKSBGb3JtYWwgUHVibGljIFRyYW5zcG9ydCBNb2RlcwoKTGV0J3MgY2hlY2sgdGhlIGRpZmZlcmVuY2UgaW4gam9iIHJlYWNoIAoKYGBge3J9CiMgQWRkIGEgY29sdW1uIHNob3dpbmcgdGhlIGRpZmZlcmVuY2UgaW4gam9iIHJlYWNoIChpLmUgaG93IG11Y2ggaW5mb3JtYWwgdHJhbnNpdCBleHRlbmRzIGpvYiByZWFjaCkKY2Fpcm9faGV4YWdvbnMkam9ic182MF9pbmYgPSBjYWlyb19oZXhhZ29ucyRqb2JzXzYwX2FsbCAtIGNhaXJvX2hleGFnb25zJGpvYnNfNjBfZm9ybWFsCmBgYAoKVGhlIGFib3ZlIGdpdmVzIG5lZ2F0aXZlIHZhbHVlcyBmb3IgMTIgaGV4YWdvbnMuIFRoZXNlIHZhbHVlcyBhcmUgYWxsIGNsb3NlIHRvIDAuIFRoaXMgaXMgYW4gb3RwIGlzc3VlIHNpbmNlIGl0IGRvZXMgbm90Cm1ha2Ugc2Vuc2UgZm9yIGlzb2Nocm9uZXMgdG8gc2hyaW5rIHdoZW4gbW9yZSB0cmFuc2l0IG9wdGlvbnMgYXJlIGF2YWlsYWJsZS4gVG8gYXZvaWQgbmVnYXRpdmUgdmFsdWVzLCBJIHVzZSBhIGZvciAKbG9vcCB0byByZXBsYWNlIHRoZW0gd2l0aCB6ZXJvcwoKYGBge3J9CmZvciAoaSBpbiAxOm5yb3dzKXsKICBpZiAoY2Fpcm9faGV4YWdvbnMkam9ic182MF9hbGxbaV0gPCBjYWlyb19oZXhhZ29ucyRqb2JzXzYwX2Zvcm1hbFtpXSl7CiAgICBjYWlyb19oZXhhZ29ucyRqb2JzXzYwX2luZltpXSA9IDAKICB9CiAgZWxzZXsKICAgIGNhaXJvX2hleGFnb25zJGpvYnNfNjBfaW5mW2ldID0gY2Fpcm9faGV4YWdvbnMkam9ic182MF9hbGxbaV0gLSBjYWlyb19oZXhhZ29ucyRqb2JzXzYwX2Zvcm1hbFtpXQogIH0KfQpgYGAKCkRpZmZlcmVuY2UgaW4gYWNjZXNzaWJpbGl0eSBzY29yZXMKCmBgYHtyfQojIEdldCBhZGRpdGlvbmFsICUgb2Ygam9icyByZWFjaGVkLiAKY2Fpcm9faGV4YWdvbnMkYWNjZXNzX3Blcl82MF9pbmYgPSBjYWlyb19oZXhhZ29ucyRhY2Nlc3NfcGVyXzYwX2FsbCAtIGNhaXJvX2hleGFnb25zJGFjY2Vzc19wZXJfNjBfZm9ybWFsCgpgYGAKCldlIGFsc28gaGF2ZSB0aGUgaXNzdWUgb2YgbmVnYXRpdmUgdmFsdWVzIGZvciB0aGUgc2FtZSAxMiBoZXhhZ29ucy4gRm9yIGxvb3AgaXQgaXM6CgpgYGB7cn0KIyBUaGUgYWJvdmUgd2lsbCBhbHNvIGdpdmUgMTIgbmVnYXRpdmUgdmFsdWVzIChzYW1lIGlzc3VlIGFzIGpvYnNfNjBfaW5mKS4gRm9yIGxvb3AgdXNlZDoKZm9yIChpIGluIDE6bnJvd3MpewogIGlmIChjYWlyb19oZXhhZ29ucyRhY2Nlc3NfcGVyXzYwX2FsbFtpXSA8IGNhaXJvX2hleGFnb25zJGFjY2Vzc19wZXJfNjBfZm9ybWFsW2ldKXsKICAgIGNhaXJvX2hleGFnb25zJGFjY2Vzc19wZXJfNjBfaW5mW2ldID0gMAogIH0KICBlbHNlewogICAgY2Fpcm9faGV4YWdvbnMkYWNjZXNzX3Blcl82MF9pbmZbaV0gPSBjYWlyb19oZXhhZ29ucyRhY2Nlc3NfcGVyXzYwX2FsbFtpXSAtIGNhaXJvX2hleGFnb25zJGFjY2Vzc19wZXJfNjBfZm9ybWFsW2ldCiAgfQp9CmBgYAoKIyMgNSBNYXBzCgpMZXRzIHZpc3VhbGl6ZSBob3cgYWNjZXNzaWJpbGl0eSB2YXJpZXMgdGhyb3VnaG91dCB0aGUgc3R1ZHkgYXJlYSAKCiMjIyA1LjEgSW1wb3J0aW5nIFNoYXBlZmlsZXMgZm9yIFZpc3VsaXphdGlvbnMKCmBgYHtyfQpsaWJyYXJ5KHNmKQojIHRvIHBsb3QgbWV0cm8gYXMgbGluZQpjYWlyb19tZXRybyA8LSBzdF9yZWFkKCJDYWlybyBTaGFwZWZpbGVzL01ldHJvX1RyaXBzX1RmQy5zaHAiKQojIHRvIGFkZCB0ZXh0IGxhYmVscyBmb3IgdGhlIG5ldyB0b3ducyBvbiB0aGUgb3V0c2tpcnRzCmNhaXJvX25ld190b3ducyA8LSBzdF9yZWFkKCJDYWlybyBTaGFwZWZpbGVzL0NlbnRyYWwtSW5uZXJfU2hpeWFraGFfTkRDX0NBUE1BUy5zaHAiKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcih6b25pbmdfdGZjID09ICJPdXRlciIpCiMgUmVtb3ZlIDEwdGggb2YgUmFtYWRhbiBDaXR5IGFuZCBHaXphX091dGVyIExhYmVscykKY2Fpcm9fbmV3X3Rvd25zIDwtIGNhaXJvX25ld190b3duc1shZHVwbGljYXRlZChjYWlyb19uZXdfdG93bnMkbmFtZV9jaXR5YSksXSAlPiUgCiAgZmlsdGVyKCFncmVwbCgnR2l6YV9PdXRlcnwxMHRoIG9mIFJhbWRhbicsIG5hbWVfY2l0eWEpKQpgYGAKCiMjIyA1LjIgVmlzdWFsaXppbmcgSm9iIFJlYWNoIAoKIyMjIyA1LjIuMSBBbGwgbW9kZXMgb2YgcHVibGljIHRyYW5zcG9ydAoKYGBge3J9CmxpYnJhcnkodG1hcCkKdG1hcF9tb2RlKCJwbG90IikKCiMgYnJlYWtzIGFyZ3VtZW50IHVzZWQgaW5zdGVhZCBvZiBzdHlsZQpicmVha3MgPSBjKDAsIDEwMDAwLCAyMDAwMCwgMTAwMDAwLCA1MDAwMDAsIDEwMDAwMDAsIDI1MDAwMDApIAoKdG1fc2hhcGUoY2Fpcm9faGV4YWdvbnMpICsKICAgICAgdG1fZmlsbCgiam9ic182MF9hbGwiLAogICAgICAgICAgICAgICNzdHlsZSA9ICJqZW5rcyIsICAgICMgdXNlZCBpbnN0ZWFkIG9mIHVzZXIgZGVmaW5lZCBicmVha3MKICAgICAgICAgICAgICBicmVha3MgPSBicmVha3MsCiAgICAgICAgICAgICAgcGFsZXR0ZSA9ICdHbkJ1JywgIyBmb3Igc3BlY2lmaWMgY29sb3JzOiBjKCcjZDcxOTFjJywgJyNmZGFlNjEnLCAnI2ZmZmZiZicsICcjYWJkZGE0JywgJyMyYjgzYmEnLCAnIzI1MzQ5NCcpCiAgICAgICAgICAgICAgI2xlZ2VuZC5oaXN0ID0gVFJVRSwKICAgICAgICAgICAgICB0aXRsZSA9ICJKb2JzIFdpdGhpbiA2MCBtaW4gXG4oUHVibGljIFRyYW5zaXQsIEFNIFBlYWspIikgKwogICAgICB0bV9sYXlvdXQodGl0bGUgPSAiQWNjZXNzaWJpbGl0eSBBY3Jvc3MgdGhlIEdDUiIsICAgICAgICAjIGFkZCBhIHRpdGxlCiAgICAgICAgICAgICAgICB0aXRsZS5zaXplID0gMS41LAogICAgICAgICAgICAgICAgdGl0bGUuY29sb3IgPSAiYXp1cmU0IiwKICAgICAgICAgICAgICAgIHRpdGxlLnBvc2l0aW9uID0gYygibGVmdCIsICJ0b3AiKSwKICAgICAgICAgICAgICAgIGlubmVyLm1hcmdpbnMgPSBjKDAuMDksIDAuMTAsIDAuMTAsIDAuMDgpLCAgICAjIGluY3JlYXNlIG1hcCBtYXJnaW5zIHRvIG1ha2Ugc3BhY2UgZm9yIGxlZ2VuZAogICAgICAgICAgICAgICAgZm9udGZhbWlseSA9ICdHZW9yZ2lhJywKICAgICAgICAgICAgICAgICNiZy5jb2xvciA9ICJncmV5OTUiLAogICAgICAgICAgICAgICAgZnJhbWUgPSBUUlVFKSArCiAgICAgIHRtX2JvcmRlcnMoY29sID0gImdyZXk0MCIsIGx3ZCA9IDAuMSkrCiAgICAgIHRtX2xlZ2VuZCh0aXRsZS5zaXplPTAuOSwKICAgICAgICAgICAgICAgIHRleHQuc2l6ZSA9IDAuNiwKICAgICAgICAgICAgICAgICNmcmFtZSA9ICJncmV5IiwKICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gYygicmlnaHQiLCAiYm90dG9tIikpICsKICAgICAgdG1fc2NhbGVfYmFyKGNvbG9yLmRhcmsgPSAiZ3JheTYwIiwKICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gYygibGVmdCIsICJib3R0b20iKSkgKwogIHRtX3NoYXBlKGNhaXJvX21ldHJvKSArIAogICAgICB0bV9saW5lcyhjb2wgPSAnZmlyZWJyaWNrNCcsIGx3ZCA9IDEuNSwgYWxwaGEgPSAwLjgpICsKICB0bV9hZGRfbGVnZW5kKHR5cGUgPSAibGluZSIsIGxhYmVscyA9ICdDYWlybyBNZXRybycsIGNvbCA9ICdmaXJlYnJpY2s0JywgbHdkID0gMS41KSArIAogIHRtX3NoYXBlKGNhaXJvX25ld190b3ducykgKyAKICAgICAgdG1fdGV4dCh0ZXh0ID0gIm5hbWVfY2l0eWEiLCBjb2wgPSAnYmxhY2snLCBzaXplID0gMC43LCAKICAgICAgICAgICAgICBhbHBoYSA9IDAuNywgYmcuY29sb3IgPSAid2hpdGUiLCBiZy5hbHBoYSA9IDAuNSkKYGBgCgojIyMjIDUuMi4yIENvbXBhcmluZyBqb2IgcmVhY2ggdXNpbmcgYWxsIG1vZGVzIHRvIHRoYXQgdXNpbmcgZm9ybWFsIG1vZGVzCgpgYGB7cn0KCnRtX3NoYXBlKGNhaXJvX2hleGFnb25zKSArCiAgICAgIHRtX2ZpbGwoYygiam9ic182MF9hbGwiLCAiam9ic182MF9mb3JtYWwiKSwKICAgICAgICAgICAgICAjc3R5bGUgPSAiamVua3MiLCAgICAjIHVzZWQgaW5zdGVhZCBvZiB1c2VyIGRlZmluZWQgYnJlYWtzCiAgICAgICAgICAgICAgYnJlYWtzID0gYnJlYWtzLAogICAgICAgICAgICAgIHBhbGV0dGUgPSAiR25CdSIsCiAgICAgICAgICAgICAgI2xlZ2VuZC5oaXN0ID0gVFJVRSwKICAgICAgICAgICAgICB0aXRsZSA9ICJKb2JzIFJlYWNoYWJsZSBcbmluIDYwIG1pbiAoQU0gUGVhaykiKSArCiAgICAgIHRtX2xheW91dCh0aXRsZSA9IGMoIkFsbCBQdWJsaWMgVHJhbnNpdCIsICJGb3JtYWwgUHVibGljIFRyYW5zaXQiKSwgICAgICAgICMgYWRkIGEgdGl0bGUKICAgICAgICAgICAgICAgIHRpdGxlLnNpemUgPSAxLjIsCiAgICAgICAgICAgICAgICB0aXRsZS5wb3NpdGlvbiA9IGMoImxlZnQiLCAidG9wIiksCiAgICAgICAgICAgICAgICB0aXRsZS5jb2xvciA9ICJhenVyZTQiLAogICAgICAgICAgICAgICAgaW5uZXIubWFyZ2lucyA9IGMoMC4wOSwgMC4xMCwgMC4xMCwgMC4wOCksICAgICMgaW5jcmVhc2UgbWFwIG1hcmdpbnMgdG8gbWFrZSBzcGFjZSBmb3IgbGVnZW5kCiAgICAgICAgICAgICAgICBmb250ZmFtaWx5ID0gJ0dlb3JnaWEnLAogICAgICAgICAgICAgICAgI2JnLmNvbG9yID0gImFudGlxdWV3aGl0ZSIsCiAgICAgICAgICAgICAgICBmcmFtZSA9IFRSVUUpICsKICAgICAgdG1fYm9yZGVycyhjb2wgPSAiZ3JleTQwIiwgbHdkID0gMC4xKSsKICAgICAgdG1fbGVnZW5kKHRpdGxlLnNpemU9MC43LAogICAgICAgICAgICAgICAgdGV4dC5zaXplID0gMC41LAogICAgICAgICAgICAgICAgI2ZyYW1lID0gImdyZXkiLAogICAgICAgICAgICAgICAgcG9zaXRpb24gPSBjKCJyaWdodCIsICJib3R0b20iKSkgKwogICAgICB0bV9zY2FsZV9iYXIoY29sb3IuZGFyayA9ICJncmF5NjAiLAogICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBjKCJsZWZ0IiwgImJvdHRvbSIpKSArCiAgdG1fc2hhcGUoY2Fpcm9fbWV0cm8pICsgCiAgICAgIHRtX2xpbmVzKGNvbCA9ICdmaXJlYnJpY2s0JywgbHdkID0gMSwgYWxwaGEgPSAwLjgpICsKICAjIGFkZCBsZWdlbmQgZm9yIHRoZSBtZXRybwogIHRtX2FkZF9sZWdlbmQodHlwZSA9ICJsaW5lIiwgbGFiZWxzID0gJ0NhaXJvIE1ldHJvJywgY29sID0gJ2ZpcmVicmljazQnLCBsd2QgPSAxKSArCiAgIyB0d28gcGxvdHMgdG9nZXRoZXIgb24gb25lIHJvdwogIHRtX2ZhY2V0cyhucm93ID0gMSkKYGBgCgojIDUuMi4zIFNob3dpbmcgYWRkaXRpb25hbCBqb2IgcmVhY2ggd2hlbiBJbmZvcm1hbCBUcmFuc2l0IGlzIGFkZGVkCgpgYGB7cn0KCmJyZWFrc19kaWZmID0gYygwLCAxMDAsIDIwMDAwLCAxMDAwMDAsIDUwMDAwMCwgMTAwMDAwMCkgIAoKdG1fc2hhcGUoY2Fpcm9faGV4YWdvbnMpICsKICAgICAgdG1fZmlsbCgiam9ic182MF9pbmYiLAogICAgICAgICAgICAgICNzdHlsZSA9ICJwcmV0dHkiLCAgICAjIHVzZWQgaW5zdGVhZCBvZiB1c2VyIGRlZmluZWQgYnJlYWtzCiAgICAgICAgICAgICAgYnJlYWtzID0gYnJlYWtzX2RpZmYsCiAgICAgICAgICAgICAgcGFsZXR0ZSA9ICJPclJkIiwKICAgICAgICAgICAgICAjbGVnZW5kLmhpc3QgPSBUUlVFLAogICAgICAgICAgICAgIHRpdGxlID0gIkFkZGl0aW9uYWwgSm9icyBSZWFjaGVkIFxuV2hlbiBVc2luZyBJbmZvcm1hbCBUcmFuc2l0IikgKwogICAgICB0bV9sYXlvdXQodGl0bGUgPSAiRWZmZWN0IG9mIEluZm9ybWFsIFRyYW5zaXQgb24gQWNjZXNzaWJpbGl0eSIsICAgICAgICAjIGFkZCBhIHRpdGxlCiAgICAgICAgICAgICAgICB0aXRsZS5zaXplID0gMS4yLAogICAgICAgICAgICAgICAgdGl0bGUuY29sb3IgPSAiYXp1cmU0IiwKICAgICAgICAgICAgICAgIHRpdGxlLnBvc2l0aW9uID0gYygibGVmdCIsICJ0b3AiKSwKICAgICAgICAgICAgICAgIGlubmVyLm1hcmdpbnMgPSBjKDAuMDksIDAuMTAsIDAuMTAsIDAuMDgpLCAgICAjIGluY3JlYXNlIG1hcCBtYXJnaW5zIHRvIG1ha2Ugc3BhY2UgZm9yIGxlZ2VuZAogICAgICAgICAgICAgICAgZm9udGZhbWlseSA9ICdHZW9yZ2lhJywKICAgICAgICAgICAgICAgICNiZy5jb2xvciA9ICJncmV5OTUiLAogICAgICAgICAgICAgICAgZnJhbWUgPSBGQUxTRSkgKwogICAgICB0bV9ib3JkZXJzKGNvbCA9ICJncmV5NDAiLCBsd2QgPSAwLjEpKwogICAgICB0bV9sZWdlbmQodGl0bGUuc2l6ZT0wLjksCiAgICAgICAgICAgICAgICB0ZXh0LnNpemUgPSAwLjYsCiAgICAgICAgICAgICAgICAjZnJhbWUgPSAiZ3JleSIsCiAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IGMoInJpZ2h0IiwgImJvdHRvbSIpKSArCiAgICAgIHRtX3NjYWxlX2Jhcihjb2xvci5kYXJrID0gImdyYXk2MCIsCiAgICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IGMoImxlZnQiLCAiYm90dG9tIikpICsKICB0bV9zaGFwZShjYWlyb19tZXRybykgKyAKICAgICAgdG1fbGluZXMoY29sID0gJ2dyYXkyMycsIGx3ZCA9IDEuNSwgYWxwaGEgPSAwLjgpICsKICAjIGFkZCBsZWdlbmQgZm9yIHRoZSBtZXRybwogIHRtX2FkZF9sZWdlbmQodHlwZSA9ICJsaW5lIiwgbGFiZWxzID0gJ0NhaXJvIE1ldHJvJywgY29sID0gJ2dyYXkyMycsIGx3ZCA9IDEuNSkKYGBgCgo=