1. Conceptualizing a spatial vector objects in R
In vector GIS we deal with, points, lines, and polygons, like so:
Exercise 1
Discuss with your neighbor: What information do we need to store in order to define points, lines, polygons in geographic space?
# - lat/lon coordinates
# - projection
# - what type (point/line/poly)
# - if polygon, is it a hole or not
# - attribute data
# * ... ?
There are currently two main approaches in R to handle geographic vector data.
The sp
package
The first package to provide classes and methods for spatial data types in R is called sp
. Development of the sp
package began in the early 2000s in an attempt to standardize how spatial data would be treated in R and to allow for better interoperability between different analysis packages that use spatial data. The package (first release on CRAN in 2005) provides classes and methods to create points, lines, polygons, and grids and to operate on them. About 350 of the spatial analysis packages use the spatial data types that are implemented in sp
i.e. they “depend” on the sp
package and many more are indirectly dependent.
The foundational structure for any spatial object in sp
is the Spatial
class. It has two “slots” (new-style S4 class objects in R have pre-defined components called slots):
This basic structure is then extended, depending on the characteristics of the spatial object (point, line, polygon).
To build up a spatial object in sp
we could follow these steps:
I. Create geometric objects (topology)
Points (which may have 2 or 3 dimensions) are the most basic spatial data objects. They are generated out of either a single coordinate or a set of coordinates, like a two-column matrix or a dataframe with a column for latitude and one for longitude.
Lines are generated out of Line
objects. A Line
object is a spaghetti collection of 2D coordinates and is generated out of a two-column matrix or a dataframe with a column for latitude and one for longitude. A Lines
object is a list of one or more Line
objects, for example all the contours at a single elevation.
Polygons are generated out of Polygon
objects. A Polygon
object is a spaghetti collection of 2D coordinates with equal first and last coordinates and is generated out of a two-column matrix or a dataframe with a column for latitude and one for longitude. A Polygons
object is a list of one or more Polygon
objects, for example islands belonging to the same country.
See here for a very simple example for how to create a Line
object:
ln <- Line(matrix(runif(6), ncol=2))
str(ln)
Formal class 'Line' [package "sp"] with 1 slot
..@ coords: num [1:3, 1:2] 0.248 0.993 0.536 0.772 0.67 ...
See here for a very simple example for how to create a Lines
object:
lns <- Lines(list(ln), ID = "a") # this contains just one Line!
str(lns)
Formal class 'Lines' [package "sp"] with 2 slots
..@ Lines:List of 1
.. ..$ :Formal class 'Line' [package "sp"] with 1 slot
.. .. .. ..@ coords: num [1:3, 1:2] 0.248 0.993 0.536 0.772 0.67 ...
..@ ID : chr "a"
- Create spatial objects
Spatial*
object (*
stands for Points, Lines, or Polygons).
This step adds the bounding box (automatically) and the slot for the Coordinate Reference System or CRS (which needs to be filled with a value manually). SpatialPoints
can be directly generated out of the coordinates. SpatialLines
and SpatialPolygons
objects are generated using lists of Lines
or Polygons
objects respectively (more below).
See here for how to create a SpatialLines
object:
sp_lns <- SpatialLines(list(lns))
str(sp_lns)
Formal class 'SpatialLines' [package "sp"] with 3 slots
..@ lines :List of 1
.. ..$ :Formal class 'Lines' [package "sp"] with 2 slots
.. .. .. ..@ Lines:List of 1
.. .. .. .. ..$ :Formal class 'Line' [package "sp"] with 1 slot
.. .. .. .. .. .. ..@ coords: num [1:3, 1:2] 0.248 0.993 0.536 0.772 0.67 ...
.. .. .. ..@ ID : chr "a"
..@ bbox : num [1:2, 1:2] 0.248 0.261 0.993 0.772
.. ..- attr(*, "dimnames")=List of 2
.. .. ..$ : chr [1:2] "x" "y"
.. .. ..$ : chr [1:2] "min" "max"
..@ proj4string:Formal class 'CRS' [package "sp"] with 1 slot
.. .. ..@ projargs: chr NA
- Add attributes (Optional:)
Add a data frame with attribute data, which will turn your Spatial*
object into a Spatial*DataFrame
object. The points in a SpatialPoints
object may be associated with a row of attributes to create a SpatialPointsDataFrame
object. The coordinates and attributes may, but do not have to be keyed to each other using ID values.
SpatialLinesDataFrame
and SpatialPolygonsDataFrame
objects are defined using SpatialLines
and SpatialPolygons
objects and data frames. The ID fields are here required to match the data frame row names.
See here for how to create a SpatialLinesDataframe
:
dfr <- data.frame(id = "a", use = "road", cars_per_hour = 10) # note how we use the ID from above!
sp_lns_dfr <- SpatialLinesDataFrame(sp_lns, dfr, match.ID = "id")
str(sp_lns_dfr)
Formal class 'SpatialLinesDataFrame' [package "sp"] with 4 slots
..@ data :'data.frame': 1 obs. of 3 variables:
.. ..$ id : Factor w/ 1 level "a": 1
.. ..$ use : Factor w/ 1 level "road": 1
.. ..$ cars_per_hour: num 10
..@ lines :List of 1
.. ..$ :Formal class 'Lines' [package "sp"] with 2 slots
.. .. .. ..@ Lines:List of 1
.. .. .. .. ..$ :Formal class 'Line' [package "sp"] with 1 slot
.. .. .. .. .. .. ..@ coords: num [1:3, 1:2] 0.248 0.993 0.536 0.772 0.67 ...
.. .. .. ..@ ID : chr "a"
..@ bbox : num [1:2, 1:2] 0.248 0.261 0.993 0.772
.. ..- attr(*, "dimnames")=List of 2
.. .. ..$ : chr [1:2] "x" "y"
.. .. ..$ : chr [1:2] "min" "max"
..@ proj4string:Formal class 'CRS' [package "sp"] with 1 slot
.. .. ..@ projargs: chr NA
A number of spatial methods are available for the classes in sp
. Among the ones I use more frequently are:
bbox() |
returns the bounding box coordinates |
proj4string() |
sets or retrieves projection attributes using the CRS object. |
CRS() |
creates an object of class of coordinate reference system arguments |
spplot() |
plots a separate map of all the attributes unless specified otherwise |
coordinates() |
set or retrieve the spatial coordinates. For spatial polygons it returns the centroids. |
over(a, b) |
used for example to retrieve the polygon or grid indices on a set of points |
spsample() |
sampling of spatial points within the spatial extent of objects |
The sf
package
The second package, first released on CRAN in late October 2016, is called sf
. It implements a formal standard called “Simple Features” that specifies a storage and access model of spatial geometries (point, line, polygon). A feature geometry is called simple when it consists of points connected by straight line pieces, and does not intersect itself. This standard has been adopted widely, not only by spatial databases such as PostGIS, but also more recent standards such as GeoJSON.
If you work with PostGis or GeoJSON you may have come across the WKT (well-known text) format, for example like these:
POINT (30 10)
LINESTRING (30 10, 10 30, 40 40)
POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))
sf
implements this standard natively in R. Data are structured and conceptualized very differently from the sp
approach.
In sf
spatial objects are stored as a simple data frame with a special column that contains the information for the geographic coordinates. That special column is a list with the same length as the number of rows in the data frame. Each of the individual list elements then can be of any length needed to hold the coordinates that correspond to an individual feature.
To create a spatial object manually the basic steps would be:
I. Create geometric objects (topology)
Geometric objects (simple features) can be created from a numeric vector, matrix or a list with the coordinates. They are called sfg
objects for Simple Feature Geometry.
See here for an example of how a LINESTRING sfg
object is created:
lnstr_sfg <- st_linestring(matrix(runif(6), ncol=2))
class(lnstr_sfg)
[1] "XY" "LINESTRING" "sfg"
- Combine all individual single feature objects for the special column.
In order to work our way towards a data frame for all features we create what is called an sfc
object with all individual features, which stands for Simple Feature Collection. The sfc
object also holds the bounding box and the projection information.
See here for an example of how a sfc
object is created:
(lnstr_sfc <- st_sfc(lnstr_sfg)) # just one feature here
Geometry set for 1 feature
geometry type: LINESTRING
dimension: XY
bbox: xmin: 0.4720064 ymin: 0.01635082 xmax: 0.905913 ymax: 0.2816711
epsg (SRID): NA
proj4string: NA
class(lnstr_sfc)
[1] "sfc_LINESTRING" "sfc"
- Add attributes.
We now combine the dataframe with the attributes and the simple feature collection. See here how its done.
(lnstr_sf <- st_sf(dfr , lnstr_sfc))
Simple feature collection with 1 feature and 3 fields
geometry type: LINESTRING
dimension: XY
bbox: xmin: 0.4720064 ymin: 0.01635082 xmax: 0.905913 ymax: 0.2816711
epsg (SRID): NA
proj4string: NA
id use cars_per_hour lnstr_sfc
1 a road 10 LINESTRING(0.47200637566857...
class(lnstr_sf)
[1] "sf" "data.frame"
There are many methods available in the sf
package, to find out use methods(class="sp")
Here are some of the other highlights of sf
you might be interested in:
provides fast I/O, particularly relevant for large files
(I did a quick microbenchmarking: st_read() took 23.1749 milliseconds and readOGR() took 462.1470 milliseconds for the philly shapefile.)
directly reads from and writes to spatial databases such as PostGIS
stay tuned for a new ggplot
release that will be able to read and plot the sf
format without the need of conversion to a data frame, like the sp
format
Note that sp
and sf
are not the only way spatial objects are conceptualized in R. Other spatial packages may use their own class definitions for spatial data (for example spatstat
). Usuallly you can find functions that convert sp
and increasingly sf
objects to and from these formats.
Exercise 2
Similarly to the example above generate a Point object in R. Use both, the sp
and the sf
“approach”.
- Create a matrix
pts
of random numbers with two columns and as many rows as you like. These are your points.
- Create a dataframe
attrib_df
with the same number of rows as your pts
matrix and a column that holds an attribute. You can make up any attribute.
- Use the appropriate commands and
pts
to create
- a
SpatialPointsDataFrame
and
- an
sf
object with a gemoetry column of class sfc_POINT
.
- Try to subset your spatial object using the attribute you have added and the way you are used to from regular data frames.
- How do you determine the bounding box of your spatial object?
Try before you peek!
pts <- matrix(runif(20), ncol=2) # the matrix with the points
attrib_df <- data.frame(an_attribute = rep(LETTERS[1:5], each = 2)) # attribute table
## sp approach ##
pts_sp <- SpatialPoints(pts) # create sp object
pts_spdf <- SpatialPointsDataFrame(pts_sp, attrib_df) # add attributes
summary(pts_spdf)
## sf approach ##
pts_sfg_list <- lapply(seq_len(nrow(pts)), function(i) st_point(pts[i,])) # a simple feature geometry
pts_sfc <- st_sfc(pts_sfg_list) # a simple feature collection
pts_sf <- st_sf(pts_sfc, attrib_df) # an sf object
pts_sf
# Some subsetting with sp:
pts_spdf$an_attribute # column with attribute only -- this is a vecor
subset(pts_spdf, an_attribute == "A") # subset with attribute A -- this is an SP object
# Some subsetting with sf:
pts_sf$an_attribute # column with attribute only -- this is a vecor
subset(pts_sf, an_attribute == "A") # subset with attribute A -- this is an SF object
# bounding box:
bbox(pts_spdf)
st_bbox(pts_sf)
2. Creating a spatial object from a lat/lon table
Often in your research might have a spreadsheet that contains latitude, longitude and perhaps some attribute values. You know how to read the spreadsheet into a data frame with read.table
or read.csv
. We can then very easily convert the table into a spatial object in R.
A SpatialPointsDataFrame
object can be created directly from a table by specifying which columns contain the coordinates. This can be done in one step by using the coordinates()
function. As mentioned above this function can be used not only to retrieve spatial coordinates but also to set them, which is done in R fashion with:
coordinates(myDataframe) <- value
value
can have different forms – in this context needs to be a character vector which specifies the data frame’s columns for the longitude and latitude (x,y) coordinates.
If we use this on a data frame it automatically converts the data frame object into a SpatialPointsDataFrame
object.
An sf
object can be created from a data frame in a similarly easy way. We take advantage of the st_as_sf()
function which converts any foreign object into an sf
object. Similarly to above, it requires an argument coords
, which in the case of point data needs to be a vector that specifies the data frame’s columns for the longitude and latitude (x,y) coordinates.
my_sf_object <- st_as_sf(myDataframe, coords)
Note that coordinates()
replaces the original data frame, while st_as_sf()
creates a new object and leaves the original data frame untouched.
Exercise 3
- Download and unzip
RSpatialDataTypes.zip
- Use
read.csv()
to read PhiladelphiaZIPHousing.csv
into a dataframe in R and name it ph_df
.
- Use
head()
to examine the first few lines of the dataframe. What information does it contain?
- Use
class()
to examine which object class the table belongs to.
- Convert the
ph_df
data frame into an sf
object with st_as_sf()
- Convert the
ph_df
data frame into a spatial object with using the coordinates
function.
- Use
class(ph_df)
again to examine which object class the table belongs to now.
Try before you peek!
ph_df <- read.csv("~/Desktop/RSpatialDataTypes/PhiladelphiaZIPHousing.csv")
head(ph_df)
class(ph_df)
# sp
ph_sf <- st_as_sf(ph_df , coords = c("lon", "lat"))
class(ph_sf)
# sf
coordinates(ph_df) <- c("lon", "lat")
class(ph_df) # !!
A brief, but important word about projection.
Note that both the SpatialPointsDataFrame
and the sf
POINTS object you just created do not have a projection defined. It is ok to plot, but be aware that for any meaningful spatial operation you will need to define a projection.
This is how it’s done:
is.projected(ph_df) # see if a projection is defined
proj4string(ph_df) <- CRS("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs") # this is WGS84
is.projected(ph_df) # voila! hm. wait a minute..
# For the `sf` object you want to use
st_crs(ph_sf)
st_crs(ph_sf) <- 4326 # we can use EPSG as numeric here
st_crs(ph_sf)
3. Loading shape files into R
How to work with rgdal
In order to read spatial data into R and turn them into Spatial*
family objects we rely on the rgdal
package. It provides us direct access to the powerful GDAL library from within R.
We can read in and write out spatial data using:
readOGR() and writeOGR() (for vector)
readGDAL() and writeGDAL() (for raster/grids)
The parameters provided for each function vary depending on the exact spatial file type you are reading. We will take an ESRI shapefile as an example. A shapefile - as you know - consists of various files of the same name, but with different extensions. They should all be in one directory and that is what R expects.
When reading in a shapefile, readOGR()
requires the following two arguments:
datasource name (dsn) # the path to the folder that contains the files
# this is a path to the folder, not a filename!
layer name (layer) # the shapefile name WITHOUT extension
# this is not a path but just the name of the file!
Setting these arguments correctly can be cause of much headache for beginners, so let me spell it out:
Firstly, you obviously need to know the name of shapefile.
Secondly, you need to know the name and location of the folder that contains all the shapefile parts.
Lastly, readOGR
only reads the file and dumps it on your screen. But similarly to reading in csv tables you want to actually work with the file, so you need to assign it to an R object.
For example:
- I have a shapefile called
myShapefile.shp
and all its associated files (like .dbf, .prj, .shx, …) in a directory called myShapefileDir
in my desktop folder,
- I have my R working directory set to my desktop folder,
- I want to assign the shape file to an R object called
myShape
.
Then my command to read this shapefile would look like this:
myShape <- readOGR(dsn = "myShapefileDir", layer = "myShapefile")
or in short:
myShape <- readOGR("myShapefileDir", "myShapefile")
Now let’s do this.
Exercise 4
- Load the
rgdal
package.
- Determine the location of the folder enclosing the
PhillyTotalPopHHinc
shapefile.
- Read
PhillyTotalPopHHinc
into an object called philly
. Make sure you provide the appropriate directory information.
- Examine the object, for example with
summary()
or class()
- Plot it.
- Take a look at the column names of the attribute data with
names()
- Take a look at the attribute data with
head()
- Select a subset of polygons with a median household income (
medHHinc
) of over 60000.
- Add that to the plot. In red.
Try before you peek!
library(rgdal)
philly <- readOGR("/Users/cengel/Desktop/RSpatialDataTypes/Philly/", "PhillyTotalPopHHinc")
# side note: unlike read.csv readOGR does not understand the ~ as valid element of a path. This (on Mac) will not work:
# philly <- readOGR("~/Desktop/RSpatialDataTypes/Philly/", "PhillyTotalPopHHinc")
summary(philly)
class(philly)
names(philly)
head(philly)
plot(philly)
philly_rich <- subset(philly, medHHinc > 60000)
plot(philly_rich, add=T, col="red")
GDAL supports over 200 raster formats and vector formats. Use ogrDrivers()
and gdalDrivers()
(without arguments) to find out which formats your rgdal
install can handle.
How to do this in sf
sf
also relies on GDAL, but we don’t need to load a separate R library to read data in. We can use st_read()
, which simply takes the path of the directory with the shapefile as argument.
So let’s do the same as above using the sf
package.
# read in
philly_sf <- st_read("~/Desktop/RSpatialDataTypes/Philly/")
# take a look at what we've got
names(philly_sf)
# note the added geometry column, as compared to:
names(philly)
# plot works differently here:
plot(philly_sf)
# to do the same as above we need to directly print the geometry column
st_geometry(philly_sf) # use this method to retreive geometry
plot(st_geometry(philly_sf))
# subset the familar way
philly_sf_rich <- subset(philly_sf, medHHinc > 60000)
plot(st_geometry(philly_sf_rich), add=T, col="red")
4. Raster data
Dealing with raster data and map algebra deserves its own separate workshop, so this is just to acknowledge that you can work with raster data in R as well.
Raster files, as you probably know, have a much more compact data structure than vectors. Because of their regular structure the coordinates do not need to be recorded for each pixel or cell in the rectangular extent. A raster is defined by:
- a CRS
- coordinates of its origin
- a distance or cell size in each direction
- a dimension or numbers of cells in each direction
- an array of cell values
Given this structure, coordinates for any cell can be computed and don’t need to be stored.
In sp
the GridTopology
class is the key element of raster representations. It contains
- the center coordinate pair of the south-west raster cell,
- the two cell sizes in the metric of the coordinates, giving the step to successive centres, and
- the numbers of cells for each dimension.
A simple grid can be built like this:
# specify the grid topology with the following parameters:
# - the smallest coordinates for each dimension, here: 0,0
# - cell size in each dimension, here: 1,1
# - number of cells in each dimension, here: 5,5
gtopo <- GridTopology(c(0,0), c(1,1), c(5,5)) # create the grid
datafr <- data.frame(runif(25)) # make up some data
SpGdf <- SpatialGridDataFrame(gtopo, datafr) # create the grid data frame
summary(SpGdf)
A very good alternative is the raster
package, which works slightly differently.
The raster
package is a major extension of spatial data classes to access large rasters and in particular to process very large files. It includes object classes for RasterLayer
, RasterStacks
, and RasterBricks
, functions for converting among these classes, and operators for computations on the raster data. Conversion from sp
type objects into raster
type objects is easy.
If we wanted to do the same as above, namely creating the same raster object from scratch we would do the following:
# specify the RasterLayer with the following parameters:
# - minimum x coordinate (left border)
# - minimum y coordinate (bottom border)
# - maximum x coordinate (right border)
# - maximum y coordinate (top border)
# - resolution (cell size) in each dimension
r <- raster(xmn=-0.5, ymn=-0.5, xmx=4.5, ymx=4.5, resolution=c(1,1))
r
So here we have created an object of type RasterLayer
, as compared to above, where we created an object of type GridTopology
.
Compare this to the output from above and note something important here: Different from the grid object we generated from scratch, this raster object has a CRS defined! If the crs argument is missing when creating the Raster object, the x coordinates are within -360 and 360 and the y coordinates are within -90 and 90, the WGS84 projection is used by default!
Good to know.
To add some values to the cells we could the following. Be aware that different from the GridTopology
object above, which we converted to a SpatialGridDataFrame
when adding values, this object here remains a RasterLayer
.
class(r)
r <- setValues(r, runif(25))
class(r)
plot(r); points(coordinates(r), pch=3)
(See the rasterVis
package for more advanced plotting of Raster*
objects.)
RasterLayer objects can also be created from a matrix.
class(volcano)
volcano.r <- raster(volcano)
class(volcano.r)
To read in a raster file we can use readGDAL()
from the sp
package, which requires only the filename of the raster as argument.
The respective function in raster
package is called raster()
.
Exercise 5
- Load the
raster
library
- Read in the DEM using the
raster()
function
- Examine by typing the name you gave the DEM
- Extract contour lines and plot them with
contour()
Try before you peek!
library(raster)
dem.r <- raster("~/Desktop/RSpatialDataTypes/DEM_10m/bushkill_pa.dem")
dem.r
contour(dem.r)
There are currently over 170 R packages on CRAN for reading, visualising, and analysing (geographical) spatial data. I highly recommend taking a look at that page if you are exploring spatial analysis with R.
LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIFNwYXRpYWwgRGF0YSBUeXBlcyBpbiBSIgphdXRob3I6ICJjbGF1ZGlhIGVuZ2VsIgpkYXRlOiAnTGFzdCB1cGRhdGVkOiBgciBmb3JtYXQoU3lzLnRpbWUoKSwgIiVCICVkLCAlWSIpYCcKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKLS0tCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQojIyBHbG9iYWwgY29kZSBvcHRpb25zCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvPVRSVUUsCgkgICAgICAgICAgICAgY2FjaGU9VFJVRSwKICAgICAgICAgICAgICAgcHJvbXB0PUZBTFNFLAogICAgICAgICAgICAgICB0aWR5PVRSVUUsCiAgICAgICAgICAgICAgIGNvbW1lbnQ9TkEsCiAgICAgICAgICAgICAgIG1lc3NhZ2U9RkFMU0UsCiAgICAgICAgICAgICAgIHdhcm5pbmc9RkFMU0UpCgojIyBsaWJyYXJpZXMgbmVlZGVkIGZvciBSIGNvZGUgZXhhbXBsZXMKbGlicmFyeShzcCkKbGlicmFyeShyZ2RhbCkKbGlicmFyeShyYXN0ZXIpCmxpYnJhcnkoc2YpCgojIyBvYmplY3QgbmVlZGVkIGZvciBleGFtcGxlIGJlbG93CnBoaWxseSA8LSByZWFkT0dSKCIvVXNlcnMvY2VuZ2VsL0Rlc2t0b3AvUlNwYXRpYWxEYXRhVHlwZXMvUGhpbGx5LyIsICJQaGlsbHlUb3RhbFBvcEhIaW5jIikKYGBgCgoqKioKCkZvciB0aGlzIG1vZHVsZSB5b3UgbmVlZCB0byBoYXZlIHRoZSBmb2xsb3dpbmcgbGlicmFyaWVzIGluc3RhbGxlZCBhbmQgbG9hZGVkOgoKLSBbYHNwYF0oaHR0cHM6Ly9DUkFOLlItcHJvamVjdC5vcmcvcGFja2FnZT1zcCkKLSBbYHJnZGFsYF0oaHR0cHM6Ly9DUkFOLlItcHJvamVjdC5vcmcvcGFja2FnZT1yZ2RhbCkKLSBbYHNmYF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvcGFja2FnZT1zZikKLSBbYHJhc3RlcmBdKGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9cmFzdGVyKSBbTm90ZSB0aGF0IHRoZSBsYXRlc3QgYHJhc3RlcmAoMi41LTgpIGRlcGVuZHMgb24gYHNwYCAxLjItMCBvciBsYXRlciwgc28gbWFrZSBzdXJlIHRoYXQgeW91ciB2ZXJzaW9ucyBhbGlnbi5dCgpEYXRhIGZpbGUgbmVlZGVkIGlzIFtSU3BhdGlhbERhdGFUeXBlcy56aXBdKGh0dHBzOi8vd3d3LmRyb3Bib3guY29tL3MvZzVwOGIxeGkyazVseWR3L1JTcGF0aWFsRGF0YVR5cGVzLnppcD9kbD0xKS4gCgoKKioqCgojIyAxLiBDb25jZXB0dWFsaXppbmcgYSBzcGF0aWFsIHZlY3RvciBvYmplY3RzIGluIFIKCkluIHZlY3RvciBHSVMgd2UgZGVhbCB3aXRoLCBwb2ludHMsIGxpbmVzLCBhbmQgcG9seWdvbnMsIGxpa2Ugc286CgpgYGB7ciBlY2hvPUZBTFNFfQpweCA8LSBjKDUsIDcsIDgsIDksIDgsIDcsIDYpIApweSA8LSBjKDcsIDMsIDQsIDgsIDksIDE1LCAxNCkgCnBsb3QocHgsIHB5LCB0eXBlPSJuIiwgYXhlcz1GLCB4bGFiID0gJycsIHlsYWIgPSAnJykgCnBvbHlnb24ocHgsIHB5LCBjb2wgPSAia2hha2kxIikKcG9pbnRzKGMoNiwgOSwgOCwgOC41KSwgYyg5LCAxNCwgOCwgOSksIHBjaD0yMCwgY29sID0gInBlYWNocHVmZjQiLCBsd2QgPSAzKQpsaW5lcyhjKDUsIDYsIDcsIDgpLCBjKDUsIDYsMTAsIDExKSwgY29sID0gInN0ZWVsYmx1ZTEiLCAgbHdkID0gMykKbGluZXMoYyg4LCA5KSwgYygxNCwgMTIpLCBjb2wgPSAiZGFyayBncmVlbiIsIGx3ZCA9IDMpCmBgYAoKKioqCgojIyMgRXhlcmNpc2UgMQoKRGlzY3VzcyB3aXRoIHlvdXIgbmVpZ2hib3I6IFdoYXQgaW5mb3JtYXRpb24gZG8gd2UgbmVlZCB0byBzdG9yZSBpbiBvcmRlciB0byBkZWZpbmUgcG9pbnRzLCBsaW5lcywgcG9seWdvbnMgaW4gZ2VvZ3JhcGhpYyBzcGFjZT8KCmBgYHtyfQojIC0gbGF0L2xvbiBjb29yZGluYXRlcwojIC0gcHJvamVjdGlvbgojIC0gd2hhdCB0eXBlIChwb2ludC9saW5lL3BvbHkpCiMgLSBpZiBwb2x5Z29uLCBpcyBpdCBhIGhvbGUgb3Igbm90CiMgLSBhdHRyaWJ1dGUgZGF0YQojICogLi4uID8KYGBgCgoKKioqCgpUaGVyZSBhcmUgY3VycmVudGx5IHR3byBtYWluIGFwcHJvYWNoZXMgaW4gUiB0byBoYW5kbGUgZ2VvZ3JhcGhpYyB2ZWN0b3IgZGF0YS4gCgojIyMgVGhlIGBzcGAgcGFja2FnZQoKVGhlIGZpcnN0IHBhY2thZ2UgdG8gcHJvdmlkZSBjbGFzc2VzIGFuZCBtZXRob2RzIGZvciBzcGF0aWFsIGRhdGEgdHlwZXMgaW4gUiBpcyBjYWxsZWQgW2BzcGBdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3BhY2thZ2U9c3ApW14xXS4gRGV2ZWxvcG1lbnQgb2YgdGhlIGBzcGAgcGFja2FnZSBiZWdhbiBpbiB0aGUgZWFybHkgMjAwMHMgaW4gYW4gYXR0ZW1wdCB0byBzdGFuZGFyZGl6ZSBob3cgc3BhdGlhbCBkYXRhIHdvdWxkIGJlIHRyZWF0ZWQgaW4gUiBhbmQgdG8gYWxsb3cgZm9yIGJldHRlciBpbnRlcm9wZXJhYmlsaXR5IGJldHdlZW4gZGlmZmVyZW50IGFuYWx5c2lzIHBhY2thZ2VzIHRoYXQgdXNlIHNwYXRpYWwgZGF0YS4gVGhlIHBhY2thZ2UgKGZpcnN0IHJlbGVhc2Ugb24gQ1JBTiBpbiAyMDA1KSBwcm92aWRlcyBjbGFzc2VzIGFuZCBtZXRob2RzIHRvIGNyZWF0ZSBfcG9pbnRzXywgX2xpbmVzXywgX3BvbHlnb25zXywgYW5kIF9ncmlkc18gYW5kIHRvIG9wZXJhdGUgb24gdGhlbS4gQWJvdXQgMzUwIG9mIHRoZSBzcGF0aWFsIGFuYWx5c2lzIHBhY2thZ2VzIHVzZSB0aGUgc3BhdGlhbCBkYXRhIHR5cGVzIHRoYXQgYXJlIGltcGxlbWVudGVkIGluIGBzcGAgaS5lLiB0aGV5ICJkZXBlbmQiIG9uIHRoZSBgc3BgIHBhY2thZ2UgYW5kIG1hbnkgbW9yZSBhcmUgaW5kaXJlY3RseSBkZXBlbmRlbnQuCgpbXjFdOiBSIEJpdmFuZCAoMjAxMSkgW0ludHJvZHVjdGlvbiB0byByZXByZXNlbnRpbmcgc3BhdGlhbCBvYmplY3RzIGluIFJdKGh0dHA6Ly9nZW9zdGF0LWNvdXJzZS5vcmcvc3lzdGVtL2ZpbGVzL21vbmRheV9zbGlkZXMucGRmKQoKClRoZSBmb3VuZGF0aW9uYWwgc3RydWN0dXJlIGZvciBhbnkgc3BhdGlhbCBvYmplY3QgaW4gYHNwYCBpcyB0aGUgYFNwYXRpYWxgIGNsYXNzLiBJdCBoYXMgdHdvICJzbG90cyIgKFtuZXctc3R5bGUgUzQgY2xhc3Mgb2JqZWN0cyBpbiBSIGhhdmUgcHJlLWRlZmluZWQgY29tcG9uZW50cyBjYWxsZWQgc2xvdHNdKGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9hLzQ3MTQwODApKToKCiogYSBfX2JvdW5kaW5nIGJveF9fIAogICAgICAKKiBhIF9fQ1JTIGNsYXNzIG9iamVjdF9fIHRvIGRlZmluZSB0aGUgQ29vcmRpbmF0ZSBSZWZlcmVuY2UgU3lzdGVtIAoKVGhpcyBiYXNpYyBzdHJ1Y3R1cmUgaXMgdGhlbiBleHRlbmRlZCwgZGVwZW5kaW5nIG9uIHRoZSBjaGFyYWN0ZXJpc3RpY3Mgb2YgdGhlIHNwYXRpYWwgb2JqZWN0IChwb2ludCwgbGluZSwgcG9seWdvbikuCgpUbyBidWlsZCB1cCBhIHNwYXRpYWwgb2JqZWN0IGluIGBzcGAgd2UgY291bGQgZm9sbG93IHRoZXNlIHN0ZXBzOiAgCgo+IEkuIENyZWF0ZSBnZW9tZXRyaWMgb2JqZWN0cyAodG9wb2xvZ3kpCgpfX1BvaW50c19fICh3aGljaCBtYXkgaGF2ZSAyIG9yIDMgZGltZW5zaW9ucykgYXJlIHRoZSBtb3N0IGJhc2ljIHNwYXRpYWwgZGF0YSBvYmplY3RzLiBUaGV5IGFyZSBnZW5lcmF0ZWQgb3V0IG9mIGVpdGhlciBhIHNpbmdsZSBjb29yZGluYXRlIG9yIGEgc2V0IG9mIGNvb3JkaW5hdGVzLCBsaWtlIGEgdHdvLWNvbHVtbiBtYXRyaXggb3IgYSBkYXRhZnJhbWUgd2l0aCBhIGNvbHVtbiBmb3IgbGF0aXR1ZGUgYW5kIG9uZSBmb3IgbG9uZ2l0dWRlLiAgCl9fTGluZXNfXyBhcmUgZ2VuZXJhdGVkIG91dCBvZiBgTGluZWAgb2JqZWN0cy4gQSBgTGluZWAgb2JqZWN0IGlzIGEgc3BhZ2hldHRpIGNvbGxlY3Rpb24gb2YgMkQgY29vcmRpbmF0ZXNbXjJdIGFuZCBpcyBnZW5lcmF0ZWQgb3V0IG9mIGEgdHdvLWNvbHVtbiBtYXRyaXggb3IgYSBkYXRhZnJhbWUgd2l0aCBhIGNvbHVtbiBmb3IgbGF0aXR1ZGUgYW5kIG9uZSBmb3IgbG9uZ2l0dWRlLiBBIGBMaW5lc2Agb2JqZWN0IGlzIGEgX19saXN0X18gb2Ygb25lIG9yIG1vcmUgYExpbmVgIG9iamVjdHMsIGZvciBleGFtcGxlIGFsbCB0aGUgY29udG91cnMgYXQgYSBzaW5nbGUgZWxldmF0aW9uLiAgCl9fUG9seWdvbnNfXyBhcmUgZ2VuZXJhdGVkIG91dCBvZiBgUG9seWdvbmAgb2JqZWN0cy4gQSBgUG9seWdvbmAgb2JqZWN0IGlzIGEgc3BhZ2hldHRpIGNvbGxlY3Rpb24gb2YgMkQgY29vcmRpbmF0ZXMgd2l0aCBlcXVhbCBmaXJzdCBhbmQgbGFzdCBjb29yZGluYXRlcyBhbmQgaXMgZ2VuZXJhdGVkIG91dCBvZiBhIHR3by1jb2x1bW4gbWF0cml4IG9yIGEgZGF0YWZyYW1lIHdpdGggYSBjb2x1bW4gZm9yIGxhdGl0dWRlIGFuZCBvbmUgZm9yIGxvbmdpdHVkZS4gQSBgUG9seWdvbnNgIG9iamVjdCBpcyBhIF9fbGlzdF9fIG9mIG9uZSBvciBtb3JlIGBQb2x5Z29uYCBvYmplY3RzLCBmb3IgZXhhbXBsZSBpc2xhbmRzIGJlbG9uZ2luZyB0byB0aGUgc2FtZSBjb3VudHJ5LgoKW14yXTogQ29vcmRpbmF0ZXMgc2hvdWxkIGJlIG9mIHR5cGUgZG91YmxlIGFuZCB3aWxsIGJlIHByb21vdGVkIGlmIG5vdC4KClNlZSBoZXJlIGZvciBhIHZlcnkgc2ltcGxlIGV4YW1wbGUgZm9yIGhvdyB0byBjcmVhdGUgYSBgTGluZWAgb2JqZWN0OgpgYGB7cn0KbG4gPC0gTGluZShtYXRyaXgocnVuaWYoNiksIG5jb2w9MikpCnN0cihsbikKYGBgClNlZSBoZXJlIGZvciBhIHZlcnkgc2ltcGxlIGV4YW1wbGUgZm9yIGhvdyB0byBjcmVhdGUgYSBgTGluZXNgIG9iamVjdDoKYGBge3J9CmxucyA8LSBMaW5lcyhsaXN0KGxuKSwgSUQgPSAiYSIpICMgdGhpcyBjb250YWlucyBqdXN0IG9uZSBMaW5lIQpzdHIobG5zKQpgYGAKCiAKPiBJSS4gQ3JlYXRlIHNwYXRpYWwgb2JqZWN0cyBgU3BhdGlhbCpgIG9iamVjdCAoYCpgIHN0YW5kcyBmb3IgUG9pbnRzLCBMaW5lcywgb3IgUG9seWdvbnMpLiAKClRoaXMgc3RlcCBhZGRzIHRoZSBib3VuZGluZyBib3ggKGF1dG9tYXRpY2FsbHkpIGFuZCB0aGUgc2xvdCBmb3IgdGhlIENvb3JkaW5hdGUgUmVmZXJlbmNlIFN5c3RlbSBvciBDUlMgKHdoaWNoIG5lZWRzIHRvIGJlIGZpbGxlZCB3aXRoIGEgdmFsdWUgbWFudWFsbHkpLiBgU3BhdGlhbFBvaW50c2AgY2FuIGJlIGRpcmVjdGx5IGdlbmVyYXRlZCBvdXQgb2YgdGhlIGNvb3JkaW5hdGVzLiAgYFNwYXRpYWxMaW5lc2AgYW5kIGBTcGF0aWFsUG9seWdvbnNgIG9iamVjdHMgYXJlIGdlbmVyYXRlZCB1c2luZyBsaXN0cyBvZiBgTGluZXNgIG9yIGBQb2x5Z29uc2Agb2JqZWN0cyByZXNwZWN0aXZlbHkgKG1vcmUgYmVsb3cpLgoKU2VlIGhlcmUgZm9yIGhvdyB0byBjcmVhdGUgYSBgU3BhdGlhbExpbmVzYCBvYmplY3Q6CmBgYHtyfQpzcF9sbnMgPC0gU3BhdGlhbExpbmVzKGxpc3QobG5zKSkKc3RyKHNwX2xucykKYGBgCiAKPiBJSUkuIEFkZCBhdHRyaWJ1dGVzIChfT3B0aW9uYWxfOikgCgpBZGQgYSBkYXRhIGZyYW1lIHdpdGggYXR0cmlidXRlIGRhdGEsIHdoaWNoIHdpbGwgdHVybiB5b3VyIGBTcGF0aWFsKmAgb2JqZWN0IGludG8gYSBgU3BhdGlhbCpEYXRhRnJhbWVgIG9iamVjdC4gIFRoZSBwb2ludHMgaW4gYSBgU3BhdGlhbFBvaW50c2Agb2JqZWN0IG1heSBiZSBhc3NvY2lhdGVkIHdpdGggYSByb3cgb2YgYXR0cmlidXRlcyB0byBjcmVhdGUgYSBgU3BhdGlhbFBvaW50c0RhdGFGcmFtZWAgb2JqZWN0LiBUaGUgY29vcmRpbmF0ZXMgYW5kIGF0dHJpYnV0ZXMgbWF5LCBidXQgZG8gbm90IGhhdmUgdG8gYmUga2V5ZWQgdG8gZWFjaCBvdGhlciB1c2luZyBJRCB2YWx1ZXMuICAKYFNwYXRpYWxMaW5lc0RhdGFGcmFtZWAgYW5kIGBTcGF0aWFsUG9seWdvbnNEYXRhRnJhbWVgIG9iamVjdHMgYXJlIGRlZmluZWQgdXNpbmcgYFNwYXRpYWxMaW5lc2AgYW5kIGBTcGF0aWFsUG9seWdvbnNgIG9iamVjdHMgYW5kIGRhdGEgZnJhbWVzLiBUaGUgSUQgZmllbGRzIGFyZSBoZXJlIHJlcXVpcmVkIHRvIG1hdGNoIHRoZSBkYXRhIGZyYW1lIHJvdyBuYW1lcy4KClNlZSBoZXJlIGZvciBob3cgdG8gY3JlYXRlIGEgYFNwYXRpYWxMaW5lc0RhdGFmcmFtZWA6CgpgYGB7cn0KZGZyIDwtIGRhdGEuZnJhbWUoaWQgPSAiYSIsIHVzZSA9ICJyb2FkIiwgY2Fyc19wZXJfaG91ciA9IDEwKSAjIG5vdGUgaG93IHdlIHVzZSB0aGUgSUQgZnJvbSBhYm92ZSEKc3BfbG5zX2RmciA8LSBTcGF0aWFsTGluZXNEYXRhRnJhbWUoc3BfbG5zLCBkZnIsIG1hdGNoLklEID0gImlkIikKc3RyKHNwX2xuc19kZnIpCmBgYAoKQSBudW1iZXIgb2Ygc3BhdGlhbCBtZXRob2RzIGFyZSBhdmFpbGFibGUgZm9yIHRoZSBjbGFzc2VzIGluIGBzcGAuIEFtb25nIHRoZSBvbmVzIEkgdXNlIG1vcmUgZnJlcXVlbnRseSBhcmU6CgpmdW5jdGlvbiB8IGFuZCB3aGF0IGl0IGRvZXMKLS0tLS0tLS0tLS0tIHwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmBiYm94KClgIHwgcmV0dXJucyB0aGUgYm91bmRpbmcgYm94IGNvb3JkaW5hdGVzCmBwcm9qNHN0cmluZygpYCB8IHNldHMgb3IgcmV0cmlldmVzIHByb2plY3Rpb24gYXR0cmlidXRlcyB1c2luZyB0aGUgQ1JTIG9iamVjdC4KYENSUygpYCB8IGNyZWF0ZXMgYW4gb2JqZWN0IG9mIGNsYXNzIG9mIGNvb3JkaW5hdGUgcmVmZXJlbmNlIHN5c3RlbSBhcmd1bWVudHMKYHNwcGxvdCgpYCB8IHBsb3RzIGEgc2VwYXJhdGUgbWFwIG9mIGFsbCB0aGUgYXR0cmlidXRlcyB1bmxlc3Mgc3BlY2lmaWVkIG90aGVyd2lzZQpgY29vcmRpbmF0ZXMoKWAgfCBzZXQgb3IgcmV0cmlldmUgdGhlIHNwYXRpYWwgY29vcmRpbmF0ZXMuIEZvciBzcGF0aWFsIHBvbHlnb25zIGl0IHJldHVybnMgdGhlIGNlbnRyb2lkcy4KYG92ZXIoYSwgYilgIHwgdXNlZCBmb3IgZXhhbXBsZSB0byByZXRyaWV2ZSB0aGUgcG9seWdvbiBvciBncmlkIGluZGljZXMgb24gYSBzZXQgb2YgcG9pbnRzCmBzcHNhbXBsZSgpYCB8IHNhbXBsaW5nIG9mIHNwYXRpYWwgcG9pbnRzIHdpdGhpbiB0aGUgc3BhdGlhbCBleHRlbnQgb2Ygb2JqZWN0cwoKIyMjIFRoZSBgc2ZgIHBhY2thZ2UKClRoZSBzZWNvbmQgcGFja2FnZSwgZmlyc3QgcmVsZWFzZWQgb24gQ1JBTiBpbiBsYXRlIE9jdG9iZXIgMjAxNiwgaXMgY2FsbGVkIFtgc2ZgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9wYWNrYWdlPXNmKVteM10uIEl0IGltcGxlbWVudHMgYSBmb3JtYWwgc3RhbmRhcmQgY2FsbGVkIFsiU2ltcGxlIEZlYXR1cmVzIl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU2ltcGxlX0ZlYXR1cmVzKSB0aGF0IHNwZWNpZmllcyBhIHN0b3JhZ2UgYW5kIGFjY2VzcyBtb2RlbCBvZiBzcGF0aWFsIGdlb21ldHJpZXMgKHBvaW50LCBsaW5lLCBwb2x5Z29uKS4gQSBmZWF0dXJlIGdlb21ldHJ5IGlzIGNhbGxlZCBzaW1wbGUgd2hlbiBpdCBjb25zaXN0cyBvZiBwb2ludHMgY29ubmVjdGVkIGJ5IHN0cmFpZ2h0IGxpbmUgcGllY2VzLCBhbmQgZG9lcyBub3QgaW50ZXJzZWN0IGl0c2VsZi4gVGhpcyBzdGFuZGFyZCBoYXMgYmVlbiBhZG9wdGVkIHdpZGVseSwgbm90IG9ubHkgYnkgc3BhdGlhbCBkYXRhYmFzZXMgc3VjaCBhcyBQb3N0R0lTLCBidXQgYWxzbyBtb3JlIHJlY2VudCBzdGFuZGFyZHMgc3VjaCBhcyBHZW9KU09OLiAKClteM106IEUuIFBlYmVzbWEgJiBSLiBCaXZhbmQgKDIwMTYpW1NwYXRpYWwgZGF0YSBpbiBSOiBzaW1wbGUgZmVhdHVyZXMgYW5kCmZ1dHVyZSBwZXJzcGVjdGl2ZXNdKGh0dHA6Ly9wZWJlc21hLnN0YWZmLmlmZ2kuZGUvcGViZXNtYV9zZnIucGRmKQoKSWYgeW91IHdvcmsgd2l0aCBQb3N0R2lzIG9yIEdlb0pTT04geW91IG1heSBoYXZlIGNvbWUgYWNyb3NzIHRoZSBbV0tUICh3ZWxsLWtub3duIHRleHQpXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9XZWxsLWtub3duX3RleHQpIGZvcm1hdCwgZm9yIGV4YW1wbGUgbGlrZSB0aGVzZTogCgogICAgUE9JTlQgKDMwIDEwKQogICAgTElORVNUUklORyAoMzAgMTAsIDEwIDMwLCA0MCA0MCkKICAgIFBPTFlHT04gKCgzMCAxMCwgNDAgNDAsIDIwIDQwLCAxMCAyMCwgMzAgMTApKQoKYHNmYCBpbXBsZW1lbnRzIHRoaXMgc3RhbmRhcmQgbmF0aXZlbHkgaW4gUi4gRGF0YSBhcmUgc3RydWN0dXJlZCBhbmQgY29uY2VwdHVhbGl6ZWQgdmVyeSBkaWZmZXJlbnRseSBmcm9tIHRoZSBgc3BgIGFwcHJvYWNoLgoKSW4gYHNmYCBzcGF0aWFsIG9iamVjdHMgYXJlIHN0b3JlZCBhcyBhIHNpbXBsZSBkYXRhIGZyYW1lIHdpdGggYSBzcGVjaWFsIGNvbHVtbiB0aGF0IGNvbnRhaW5zIHRoZSBpbmZvcm1hdGlvbiBmb3IgdGhlIGdlb2dyYXBoaWMgY29vcmRpbmF0ZXMuIFRoYXQgc3BlY2lhbCBjb2x1bW4gaXMgYSBsaXN0IHdpdGggdGhlIHNhbWUgbGVuZ3RoIGFzIHRoZSBudW1iZXIgb2Ygcm93cyBpbiB0aGUgZGF0YSBmcmFtZS4gRWFjaCBvZiB0aGUgaW5kaXZpZHVhbCBsaXN0IGVsZW1lbnRzIHRoZW4gY2FuIGJlIG9mIGFueSBsZW5ndGggbmVlZGVkIHRvIGhvbGQgdGhlIGNvb3JkaW5hdGVzIHRoYXQgY29ycmVzcG9uZCB0byBhbiBpbmRpdmlkdWFsIGZlYXR1cmUuICAKClRvIGNyZWF0ZSBhIHNwYXRpYWwgb2JqZWN0IG1hbnVhbGx5IHRoZSBiYXNpYyBzdGVwcyB3b3VsZCBiZTogIAoKPiBJLiBDcmVhdGUgZ2VvbWV0cmljIG9iamVjdHMgKHRvcG9sb2d5KSAgCgpHZW9tZXRyaWMgb2JqZWN0cyAoc2ltcGxlIGZlYXR1cmVzKSBjYW4gYmUgY3JlYXRlZCBmcm9tIGEgbnVtZXJpYyB2ZWN0b3IsIG1hdHJpeCBvciBhIGxpc3Qgd2l0aCB0aGUgY29vcmRpbmF0ZXMuIFRoZXkgYXJlIGNhbGxlZCBgc2ZnYCBvYmplY3RzIGZvciBTaW1wbGUgRmVhdHVyZSBHZW9tZXRyeS4KClNlZSBoZXJlIGZvciBhbiBleGFtcGxlIG9mIGhvdyBhIExJTkVTVFJJTkcgYHNmZ2Agb2JqZWN0IGlzIGNyZWF0ZWQ6CmBgYHtyfQpsbnN0cl9zZmcgPC0gc3RfbGluZXN0cmluZyhtYXRyaXgocnVuaWYoNiksIG5jb2w9MikpIApjbGFzcyhsbnN0cl9zZmcpCmBgYAoKPiBJSS4gQ29tYmluZSBhbGwgaW5kaXZpZHVhbCBzaW5nbGUgZmVhdHVyZSBvYmplY3RzIGZvciB0aGUgc3BlY2lhbCBjb2x1bW4uIAoKSW4gb3JkZXIgdG8gd29yayBvdXIgd2F5IHRvd2FyZHMgYSBkYXRhIGZyYW1lIGZvciBhbGwgZmVhdHVyZXMgd2UgY3JlYXRlIHdoYXQgaXMgY2FsbGVkIGFuIGBzZmNgIG9iamVjdCB3aXRoIGFsbCBpbmRpdmlkdWFsIGZlYXR1cmVzLCB3aGljaCBzdGFuZHMgZm9yIFNpbXBsZSBGZWF0dXJlIENvbGxlY3Rpb24uIFRoZSBgc2ZjYCBvYmplY3QgYWxzbyBob2xkcyB0aGUgYm91bmRpbmcgYm94IGFuZCB0aGUgcHJvamVjdGlvbiBpbmZvcm1hdGlvbi4KClNlZSBoZXJlIGZvciBhbiBleGFtcGxlIG9mIGhvdyBhIGBzZmNgIG9iamVjdCBpcyBjcmVhdGVkOgpgYGB7cn0KKGxuc3RyX3NmYyA8LSBzdF9zZmMobG5zdHJfc2ZnKSkgIyBqdXN0IG9uZSBmZWF0dXJlIGhlcmUKY2xhc3MobG5zdHJfc2ZjKSAKYGBgCgo+IElJSS4gQWRkIGF0dHJpYnV0ZXMuIAoKV2Ugbm93IGNvbWJpbmUgdGhlIGRhdGFmcmFtZSB3aXRoIHRoZSBhdHRyaWJ1dGVzIGFuZCB0aGUgc2ltcGxlIGZlYXR1cmUgY29sbGVjdGlvbi4KU2VlIGhlcmUgaG93IGl0cyBkb25lLgpgYGB7cn0KKGxuc3RyX3NmIDwtIHN0X3NmKGRmciAsIGxuc3RyX3NmYykpCmNsYXNzKGxuc3RyX3NmKQpgYGAKClRoZXJlIGFyZSBtYW55IG1ldGhvZHMgYXZhaWxhYmxlIGluIHRoZSBgc2ZgIHBhY2thZ2UsIHRvIGZpbmQgb3V0IHVzZSBgbWV0aG9kcyhjbGFzcz0ic3AiKWAKCkhlcmUgYXJlIHNvbWUgb2YgdGhlIG90aGVyIGhpZ2hsaWdodHMgb2YgYHNmYCB5b3UgbWlnaHQgYmUgaW50ZXJlc3RlZCBpbjoKCi0gcHJvdmlkZXMgKipmYXN0KiogSS9PLCBwYXJ0aWN1bGFybHkgcmVsZXZhbnQgZm9yIGxhcmdlIGZpbGVzIAoKICAgIChJIGRpZCBhIHF1aWNrIG1pY3JvYmVuY2htYXJraW5nOiBzdF9yZWFkKCkgdG9vayAyMy4xNzQ5IG1pbGxpc2Vjb25kcyBhbmQgcmVhZE9HUigpIHRvb2sKNDYyLjE0NzAgbWlsbGlzZWNvbmRzIGZvciB0aGUgcGhpbGx5IHNoYXBlZmlsZS4pCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAotIGRpcmVjdGx5IHJlYWRzIGZyb20gYW5kIHdyaXRlcyB0byBzcGF0aWFsICoqZGF0YWJhc2VzKiogc3VjaCBhcyBQb3N0R0lTCgotIHN0YXkgdHVuZWQgZm9yIGEgbmV3IGBnZ3Bsb3RgIHJlbGVhc2UgdGhhdCB3aWxsIGJlIGFibGUgdG8gcmVhZCBhbmQgcGxvdCB0aGUgYHNmYCBmb3JtYXQgd2l0aG91dCB0aGUgbmVlZCBvZiBjb252ZXJzaW9uIHRvIGEgZGF0YSBmcmFtZSwgbGlrZSB0aGUgYHNwYCBmb3JtYXQKCgpOb3RlIHRoYXQgYHNwYCBhbmQgYHNmYCBhcmUgbm90IHRoZSBvbmx5IHdheSBzcGF0aWFsIG9iamVjdHMgYXJlIGNvbmNlcHR1YWxpemVkIGluIFIuIE90aGVyIHNwYXRpYWwgcGFja2FnZXMgbWF5IHVzZSB0aGVpciBvd24gY2xhc3MgZGVmaW5pdGlvbnMgZm9yIHNwYXRpYWwgZGF0YSAoZm9yIGV4YW1wbGUgYHNwYXRzdGF0YCkuIFVzdWFsbGx5IHlvdSBjYW4gZmluZCBmdW5jdGlvbnMgdGhhdCBjb252ZXJ0IGBzcGAgYW5kIGluY3JlYXNpbmdseSBgc2ZgIG9iamVjdHMgdG8gYW5kIGZyb20gdGhlc2UgZm9ybWF0cy4KCioqKgojIyMgRXhlcmNpc2UgMgoKU2ltaWxhcmx5IHRvIHRoZSBleGFtcGxlIGFib3ZlIGdlbmVyYXRlIGEgUG9pbnQgb2JqZWN0IGluIFIuIFVzZSBib3RoLCB0aGUgYHNwYCBhbmQgdGhlIGBzZmAgImFwcHJvYWNoIi4KCjEuIENyZWF0ZSBhIG1hdHJpeCBgcHRzYCBvZiByYW5kb20gbnVtYmVycyB3aXRoIHR3byBjb2x1bW5zIGFuZCBhcyBtYW55IHJvd3MgYXMgeW91IGxpa2UuIFRoZXNlIGFyZSB5b3VyIHBvaW50cy4KMi4gQ3JlYXRlIGEgZGF0YWZyYW1lIGBhdHRyaWJfZGZgIHdpdGggdGhlIHNhbWUgbnVtYmVyIG9mIHJvd3MgYXMgeW91ciBgcHRzYCBtYXRyaXggIGFuZCBhIGNvbHVtbiB0aGF0IGhvbGRzIGFuIGF0dHJpYnV0ZS4gWW91IGNhbiBtYWtlIHVwIGFueSBhdHRyaWJ1dGUuCjMuIFVzZSB0aGUgYXBwcm9wcmlhdGUgY29tbWFuZHMgYW5kIGBwdHNgIHRvIGNyZWF0ZSAKICAtIGEgYFNwYXRpYWxQb2ludHNEYXRhRnJhbWVgIGFuZCAKICAtIGFuIGBzZmAgb2JqZWN0IHdpdGggYSBnZW1vZXRyeSBjb2x1bW4gb2YgY2xhc3MgYHNmY19QT0lOVGAuCjQuIFRyeSB0byBzdWJzZXQgeW91ciBzcGF0aWFsIG9iamVjdCB1c2luZyB0aGUgYXR0cmlidXRlIHlvdSBoYXZlIGFkZGVkIGFuZCB0aGUgd2F5IHlvdSBhcmUgdXNlZCB0byBmcm9tIHJlZ3VsYXIgZGF0YSBmcmFtZXMuCjUuIEhvdyBkbyB5b3UgZGV0ZXJtaW5lIHRoZSBib3VuZGluZyBib3ggb2YgeW91ciBzcGF0aWFsIG9iamVjdD8KCj4gVHJ5IGJlZm9yZSB5b3UgcGVlayEgCgpgYGB7ciBldmFsPUZBTFNFfQpwdHMgPC0gbWF0cml4KHJ1bmlmKDIwKSwgbmNvbD0yKSAjIHRoZSBtYXRyaXggd2l0aCB0aGUgcG9pbnRzCmF0dHJpYl9kZiA8LSBkYXRhLmZyYW1lKGFuX2F0dHJpYnV0ZSA9IHJlcChMRVRURVJTWzE6NV0sIGVhY2ggPSAyKSkgIyBhdHRyaWJ1dGUgdGFibGUgCgojIyBzcCBhcHByb2FjaCAjIwpwdHNfc3AgPC0gU3BhdGlhbFBvaW50cyhwdHMpICMgY3JlYXRlIHNwIG9iamVjdApwdHNfc3BkZiA8LSBTcGF0aWFsUG9pbnRzRGF0YUZyYW1lKHB0c19zcCwgYXR0cmliX2RmKSAjIGFkZCBhdHRyaWJ1dGVzCnN1bW1hcnkocHRzX3NwZGYpCgojIyBzZiBhcHByb2FjaCAjIwpwdHNfc2ZnX2xpc3QgPC0gbGFwcGx5KHNlcV9sZW4obnJvdyhwdHMpKSwgZnVuY3Rpb24oaSkgc3RfcG9pbnQocHRzW2ksXSkpICMgYSBzaW1wbGUgZmVhdHVyZSBnZW9tZXRyeQpwdHNfc2ZjIDwtIHN0X3NmYyhwdHNfc2ZnX2xpc3QpICAgICAjIGEgc2ltcGxlIGZlYXR1cmUgY29sbGVjdGlvbiAKcHRzX3NmIDwtIHN0X3NmKHB0c19zZmMsIGF0dHJpYl9kZikgIyBhbiBzZiBvYmplY3QKcHRzX3NmCgojIFNvbWUgc3Vic2V0dGluZyB3aXRoIHNwOgpwdHNfc3BkZiRhbl9hdHRyaWJ1dGUgICAgIyBjb2x1bW4gd2l0aCBhdHRyaWJ1dGUgb25seSAtLSB0aGlzIGlzIGEgdmVjb3IKc3Vic2V0KHB0c19zcGRmLCBhbl9hdHRyaWJ1dGUgPT0gIkEiKSAjIHN1YnNldCB3aXRoIGF0dHJpYnV0ZSBBIC0tIHRoaXMgaXMgYW4gU1Agb2JqZWN0CgojIFNvbWUgc3Vic2V0dGluZyB3aXRoIHNmOgpwdHNfc2YkYW5fYXR0cmlidXRlICMgY29sdW1uIHdpdGggYXR0cmlidXRlIG9ubHkgLS0gdGhpcyBpcyBhIHZlY29yCnN1YnNldChwdHNfc2YsIGFuX2F0dHJpYnV0ZSA9PSAiQSIpICAjIHN1YnNldCB3aXRoIGF0dHJpYnV0ZSBBIC0tIHRoaXMgaXMgYW4gU0Ygb2JqZWN0CgojIGJvdW5kaW5nIGJveDoKYmJveChwdHNfc3BkZikKc3RfYmJveChwdHNfc2YpCmBgYAoKKioqCgojIDIuIENyZWF0aW5nIGEgc3BhdGlhbCBvYmplY3QgZnJvbSBhIGxhdC9sb24gdGFibGUKCk9mdGVuIGluIHlvdXIgcmVzZWFyY2ggbWlnaHQgaGF2ZSBhIHNwcmVhZHNoZWV0IHRoYXQgY29udGFpbnMgbGF0aXR1ZGUsIGxvbmdpdHVkZSBhbmQgcGVyaGFwcyBzb21lIGF0dHJpYnV0ZSB2YWx1ZXMuIFlvdSBrbm93IGhvdyB0byByZWFkIHRoZSBzcHJlYWRzaGVldCBpbnRvIGEgZGF0YSBmcmFtZSB3aXRoIGByZWFkLnRhYmxlYCBvciBgcmVhZC5jc3ZgLiBXZSBjYW4gdGhlbiB2ZXJ5IGVhc2lseSBjb252ZXJ0IHRoZSB0YWJsZSBpbnRvIGEgc3BhdGlhbCBvYmplY3QgaW4gUi4KCkEgYFNwYXRpYWxQb2ludHNEYXRhRnJhbWVgIG9iamVjdCBjYW4gYmUgY3JlYXRlZCBkaXJlY3RseSBmcm9tIGEgdGFibGUgYnkgc3BlY2lmeWluZyB3aGljaCBjb2x1bW5zIGNvbnRhaW4gdGhlIGNvb3JkaW5hdGVzLiBUaGlzIGNhbiBiZSBkb25lIGluIG9uZSBzdGVwIGJ5IHVzaW5nIHRoZSBgY29vcmRpbmF0ZXMoKWAgZnVuY3Rpb24uIEFzIG1lbnRpb25lZCBhYm92ZSB0aGlzIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIG5vdCBvbmx5IHRvIHJldHJpZXZlIHNwYXRpYWwgY29vcmRpbmF0ZXMgYnV0IGFsc28gdG8gc2V0IHRoZW0sIHdoaWNoIGlzIGRvbmUgaW4gUiBmYXNoaW9uIHdpdGg6CgogICAgY29vcmRpbmF0ZXMobXlEYXRhZnJhbWUpIDwtIHZhbHVlCgpgdmFsdWVgIGNhbiBoYXZlIGRpZmZlcmVudCBmb3JtcyAtLSBpbiB0aGlzIGNvbnRleHQgbmVlZHMgdG8gYmUgYSBjaGFyYWN0ZXIgdmVjdG9yIHdoaWNoIHNwZWNpZmllcyB0aGUgZGF0YSBmcmFtZSdzIGNvbHVtbnMgZm9yIHRoZSBsb25naXR1ZGUgYW5kIGxhdGl0dWRlICh4LHkpIGNvb3JkaW5hdGVzLgoKSWYgd2UgdXNlIHRoaXMgb24gYSBkYXRhIGZyYW1lIGl0IGF1dG9tYXRpY2FsbHkgY29udmVydHMgdGhlIGRhdGEgZnJhbWUgb2JqZWN0IGludG8gYSBgU3BhdGlhbFBvaW50c0RhdGFGcmFtZWAgb2JqZWN0LgoKQW4gYHNmYCBvYmplY3QgY2FuIGJlIGNyZWF0ZWQgZnJvbSBhIGRhdGEgZnJhbWUgaW4gYSBzaW1pbGFybHkgZWFzeSB3YXkuIFdlIHRha2UgYWR2YW50YWdlIG9mIHRoZSBgc3RfYXNfc2YoKWAgZnVuY3Rpb24gd2hpY2ggY29udmVydHMgYW55IGZvcmVpZ24gb2JqZWN0IGludG8gYW4gYHNmYCBvYmplY3QuIFNpbWlsYXJseSB0byBhYm92ZSwgaXQgcmVxdWlyZXMgYW4gYXJndW1lbnQgYGNvb3Jkc2AsIHdoaWNoIGluIHRoZSBjYXNlIG9mIHBvaW50IGRhdGEgbmVlZHMgdG8gYmUgYSB2ZWN0b3IgdGhhdCBzcGVjaWZpZXMgdGhlIGRhdGEgZnJhbWUncyBjb2x1bW5zIGZvciB0aGUgbG9uZ2l0dWRlIGFuZCBsYXRpdHVkZSAoeCx5KSBjb29yZGluYXRlcy4gCgogICAgbXlfc2Zfb2JqZWN0IDwtIHN0X2FzX3NmKG15RGF0YWZyYW1lLCBjb29yZHMpCgpOb3RlIHRoYXQgYGNvb3JkaW5hdGVzKClgIHJlcGxhY2VzIHRoZSBvcmlnaW5hbCBkYXRhIGZyYW1lLCB3aGlsZSBgc3RfYXNfc2YoKWAgY3JlYXRlcyBhIG5ldyBvYmplY3QgYW5kIGxlYXZlcyB0aGUgb3JpZ2luYWwgZGF0YSBmcmFtZSB1bnRvdWNoZWQuCgoKKioqCiMjIyBFeGVyY2lzZSAzCgoxLiBEb3dubG9hZCBhbmQgdW56aXAgW2BSU3BhdGlhbERhdGFUeXBlcy56aXBgXShodHRwczovL3d3dy5kcm9wYm94LmNvbS9zL2c1cDhiMXhpMms1bHlkdy9SU3BhdGlhbERhdGFUeXBlcy56aXA/ZGw9MSkKMi4gVXNlIGByZWFkLmNzdigpYCB0byByZWFkIGBQaGlsYWRlbHBoaWFaSVBIb3VzaW5nLmNzdmAgaW50byBhIGRhdGFmcmFtZSBpbiBSIGFuZCBuYW1lIGl0IGBwaF9kZmAuCjMuIFVzZSBgaGVhZCgpYCB0byBleGFtaW5lIHRoZSBmaXJzdCBmZXcgbGluZXMgb2YgdGhlIGRhdGFmcmFtZS4gV2hhdCBpbmZvcm1hdGlvbiBkb2VzIGl0IGNvbnRhaW4/CjQuIFVzZSBgY2xhc3MoKWAgdG8gZXhhbWluZSB3aGljaCBvYmplY3QgY2xhc3MgdGhlIHRhYmxlIGJlbG9uZ3MgdG8uCjUuIENvbnZlcnQgdGhlIGBwaF9kZmAgZGF0YSBmcmFtZSBpbnRvIGFuIGBzZmAgb2JqZWN0IHdpdGggYHN0X2FzX3NmKClgCjYuIENvbnZlcnQgdGhlIGBwaF9kZmAgZGF0YSBmcmFtZSBpbnRvIGEgc3BhdGlhbCBvYmplY3Qgd2l0aCB1c2luZyB0aGUgYGNvb3JkaW5hdGVzYCBmdW5jdGlvbi4KNi4gVXNlIGBjbGFzcyhwaF9kZilgYWdhaW4gdG8gZXhhbWluZSB3aGljaCBvYmplY3QgY2xhc3MgdGhlIHRhYmxlIGJlbG9uZ3MgdG8gbm93LgoKPiBUcnkgYmVmb3JlIHlvdSBwZWVrISAKCmBgYHtyIGV2YWw9RkFMU0V9CnBoX2RmIDwtIHJlYWQuY3N2KCJ+L0Rlc2t0b3AvUlNwYXRpYWxEYXRhVHlwZXMvUGhpbGFkZWxwaGlhWklQSG91c2luZy5jc3YiKQpoZWFkKHBoX2RmKQpjbGFzcyhwaF9kZikKCiMgc3AgCnBoX3NmIDwtIHN0X2FzX3NmKHBoX2RmICwgY29vcmRzID0gYygibG9uIiwgImxhdCIpKQpjbGFzcyhwaF9zZikKCiMgc2YKY29vcmRpbmF0ZXMocGhfZGYpIDwtIGMoImxvbiIsICJsYXQiKQpjbGFzcyhwaF9kZikgIyAhIQpgYGAKCiMjIyBBIGJyaWVmLCBidXQgaW1wb3J0YW50IHdvcmQgYWJvdXQgcHJvamVjdGlvbi4KCk5vdGUgdGhhdCBib3RoIHRoZSBgU3BhdGlhbFBvaW50c0RhdGFGcmFtZWAgYW5kIHRoZSBgc2ZgIFBPSU5UUyBvYmplY3QgeW91IGp1c3QgY3JlYXRlZCBfX2RvIG5vdF9fIGhhdmUgYSBwcm9qZWN0aW9uIGRlZmluZWQuIEl0IGlzIG9rIHRvIHBsb3QsIGJ1dCBiZSBhd2FyZSB0aGF0IGZvciBhbnkgbWVhbmluZ2Z1bCBzcGF0aWFsIG9wZXJhdGlvbiB5b3Ugd2lsbCBuZWVkIHRvIGRlZmluZSBhIHByb2plY3Rpb24uIAoKVGhpcyBpcyBob3cgaXQncyBkb25lOgoKYGBge3IgZXZhbD1GQUxTRX0KaXMucHJvamVjdGVkKHBoX2RmKSAjIHNlZSBpZiBhIHByb2plY3Rpb24gaXMgZGVmaW5lZCAgCnByb2o0c3RyaW5nKHBoX2RmKSA8LSBDUlMoIitwcm9qPWxvbmdsYXQgK2VsbHBzPVdHUzg0ICtkYXR1bT1XR1M4NCArbm9fZGVmcyIpICMgdGhpcyBpcyBXR1M4NAppcy5wcm9qZWN0ZWQocGhfZGYpICMgdm9pbGEhIGhtLiB3YWl0IGEgbWludXRlLi4KCgojIEZvciB0aGUgYHNmYCBvYmplY3QgeW91IHdhbnQgdG8gdXNlIApzdF9jcnMocGhfc2YpCnN0X2NycyhwaF9zZikgPC0gNDMyNiAjIHdlIGNhbiB1c2UgRVBTRyBhcyBudW1lcmljIGhlcmUKc3RfY3JzKHBoX3NmKQpgYGAKKioqCgojIDMuIExvYWRpbmcgc2hhcGUgZmlsZXMgaW50byBSCgojIyMgSG93IHRvIHdvcmsgd2l0aCBgcmdkYWxgCgpJbiBvcmRlciB0byByZWFkIHNwYXRpYWwgZGF0YSBpbnRvIFIgYW5kIHR1cm4gdGhlbSBpbnRvIGBTcGF0aWFsKmAgZmFtaWx5IG9iamVjdHMgd2UgcmVseSBvbiB0aGUgYHJnZGFsYCBwYWNrYWdlLiBJdCBwcm92aWRlcyB1cyBkaXJlY3QgYWNjZXNzIHRvIHRoZSBwb3dlcmZ1bCBbR0RBTCBsaWJyYXJ5XShodHRwOi8vZ2RhbC5vcmcpIGZyb20gd2l0aGluIFIuIAoKV2UgY2FuIHJlYWQgaW4gYW5kIHdyaXRlIG91dCBzcGF0aWFsIGRhdGEgdXNpbmc6CgogICAgcmVhZE9HUigpIGFuZCB3cml0ZU9HUigpIChmb3IgdmVjdG9yKSAgCiAgICByZWFkR0RBTCgpIGFuZCB3cml0ZUdEQUwoKSAoZm9yIHJhc3Rlci9ncmlkcykKClRoZSBwYXJhbWV0ZXJzIHByb3ZpZGVkIGZvciBlYWNoIGZ1bmN0aW9uIHZhcnkgZGVwZW5kaW5nIG9uIHRoZSBleGFjdCBzcGF0aWFsIGZpbGUgdHlwZSB5b3UgYXJlIHJlYWRpbmcuIFdlIHdpbGwgdGFrZSBhbiBFU1JJIHNoYXBlZmlsZSBhcyBhbiBleGFtcGxlLiBBIHNoYXBlZmlsZSAtIGFzIHlvdSBrbm93IC0gW2NvbnNpc3RzIG9mIHZhcmlvdXMgZmlsZXMgb2YgdGhlIHNhbWUgbmFtZSwgYnV0IHdpdGggZGlmZmVyZW50IGV4dGVuc2lvbnNdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1NoYXBlZmlsZSkuIFRoZXkgc2hvdWxkIGFsbCBiZSBpbiBvbmUgZGlyZWN0b3J5IGFuZCB0aGF0IGlzIHdoYXQgUiBleHBlY3RzLgoKV2hlbiByZWFkaW5nIGluIGEgc2hhcGVmaWxlLCBgcmVhZE9HUigpYCByZXF1aXJlcyB0aGUgZm9sbG93aW5nIHR3byBhcmd1bWVudHM6CgogICAgZGF0YXNvdXJjZSBuYW1lIChkc24pICAjIHRoZSBwYXRoIHRvIHRoZSBmb2xkZXIgdGhhdCBjb250YWlucyB0aGUgZmlsZXMKICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGlzIGlzIGEgcGF0aCB0byB0aGUgZm9sZGVyLCBub3QgYSBmaWxlbmFtZSEKICAgIGxheWVyIG5hbWUgKGxheWVyKSAgICAgIyB0aGUgc2hhcGVmaWxlIG5hbWUgV0lUSE9VVCBleHRlbnNpb24KICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGlzIGlzIG5vdCBhIHBhdGggYnV0IGp1c3QgdGhlIG5hbWUgb2YgdGhlIGZpbGUhCgpTZXR0aW5nIHRoZXNlIGFyZ3VtZW50cyBjb3JyZWN0bHkgY2FuIGJlIGNhdXNlIG9mIG11Y2ggaGVhZGFjaGUgZm9yIGJlZ2lubmVycywgc28gbGV0IG1lIHNwZWxsIGl0IG91dDoKCi0gRmlyc3RseSwgeW91IG9idmlvdXNseSBuZWVkIHRvIGtub3cgdGhlIG5hbWUgb2Ygc2hhcGVmaWxlLgoKLSBTZWNvbmRseSwgeW91IG5lZWQgdG8ga25vdyB0aGUgbmFtZSBhbmQgbG9jYXRpb24gb2YgdGhlIGZvbGRlciB0aGF0IGNvbnRhaW5zIGFsbCB0aGUgc2hhcGVmaWxlIHBhcnRzLgoKLSBMYXN0bHksIGByZWFkT0dSYCBvbmx5IHJlYWRzIHRoZSBmaWxlIGFuZCBkdW1wcyBpdCBvbiB5b3VyIHNjcmVlbi4gQnV0IHNpbWlsYXJseSB0byByZWFkaW5nIGluIGNzdiB0YWJsZXMgeW91IHdhbnQgdG8gYWN0dWFsbHkgd29yayB3aXRoIHRoZSBmaWxlLCBzbyB5b3UgbmVlZCB0byBhc3NpZ24gaXQgdG8gYW4gUiBvYmplY3QuCgpGb3IgZXhhbXBsZToKCi0gSSBoYXZlIGEgc2hhcGVmaWxlIGNhbGxlZCBgbXlTaGFwZWZpbGUuc2hwYCBhbmQgYWxsIGl0cyBhc3NvY2lhdGVkIGZpbGVzIChsaWtlIF8uZGJmLCAucHJqLCAuc2h4XywgLi4uKSBpbiBhIGRpcmVjdG9yeSBjYWxsZWQgYG15U2hhcGVmaWxlRGlyYCBpbiBteSBkZXNrdG9wIGZvbGRlciwgCi0gSSBoYXZlIG15IFIgd29ya2luZyBkaXJlY3Rvcnkgc2V0IHRvIG15IGRlc2t0b3AgZm9sZGVyLAotIEkgd2FudCB0byBhc3NpZ24gdGhlIHNoYXBlIGZpbGUgdG8gYW4gUiBvYmplY3QgY2FsbGVkIGBteVNoYXBlYC4KClRoZW4gbXkgY29tbWFuZCB0byByZWFkIHRoaXMgc2hhcGVmaWxlIHdvdWxkIGxvb2sgbGlrZSB0aGlzOgpgYGAKbXlTaGFwZSA8LSByZWFkT0dSKGRzbiA9ICJteVNoYXBlZmlsZURpciIsIGxheWVyID0gIm15U2hhcGVmaWxlIikKYGBgCm9yIGluIHNob3J0OgpgYGAKbXlTaGFwZSA8LSByZWFkT0dSKCJteVNoYXBlZmlsZURpciIsICJteVNoYXBlZmlsZSIpCmBgYAoKTm93IGxldCdzIGRvIHRoaXMuCgoqKioKIyMjIyBFeGVyY2lzZSA0CgoxLiBMb2FkIHRoZSBgcmdkYWxgIHBhY2thZ2UuCjIuIERldGVybWluZSB0aGUgbG9jYXRpb24gb2YgdGhlIGZvbGRlciBlbmNsb3NpbmcgdGhlIGBQaGlsbHlUb3RhbFBvcEhIaW5jYCBzaGFwZWZpbGUuCjMuIFJlYWQgYFBoaWxseVRvdGFsUG9wSEhpbmNgIGludG8gYW4gb2JqZWN0IGNhbGxlZCBgcGhpbGx5YC4gTWFrZSBzdXJlIHlvdSBwcm92aWRlIHRoZSBhcHByb3ByaWF0ZSBkaXJlY3RvcnkgaW5mb3JtYXRpb24uCjQuIEV4YW1pbmUgdGhlIG9iamVjdCwgZm9yIGV4YW1wbGUgd2l0aCBgc3VtbWFyeSgpYCBvciBgY2xhc3MoKWAKNS4gUGxvdCBpdC4KNi4gVGFrZSBhIGxvb2sgYXQgdGhlIGNvbHVtbiBuYW1lcyBvZiB0aGUgYXR0cmlidXRlIGRhdGEgd2l0aCBgbmFtZXMoKWAKNy4gVGFrZSBhIGxvb2sgYXQgdGhlIGF0dHJpYnV0ZSBkYXRhIHdpdGggYGhlYWQoKWAKOC4gU2VsZWN0IGEgc3Vic2V0IG9mIHBvbHlnb25zIHdpdGggYSBtZWRpYW4gaG91c2Vob2xkIGluY29tZSAoYG1lZEhIaW5jYCkgb2Ygb3ZlciA2MDAwMC4gIAo5LiBBZGQgdGhhdCB0byB0aGUgcGxvdC4gSW4gcmVkLgoKPiBUcnkgYmVmb3JlIHlvdSBwZWVrIQoKYGBge3IgZXZhbD1GfQpsaWJyYXJ5KHJnZGFsKQpwaGlsbHkgPC0gcmVhZE9HUigiL1VzZXJzL2NlbmdlbC9EZXNrdG9wL1JTcGF0aWFsRGF0YVR5cGVzL1BoaWxseS8iLCAiUGhpbGx5VG90YWxQb3BISGluYyIpIAojIHNpZGUgbm90ZTogdW5saWtlIHJlYWQuY3N2IHJlYWRPR1IgZG9lcyBub3QgdW5kZXJzdGFuZCB0aGUgfiBhcyB2YWxpZCBlbGVtZW50IG9mIGEgcGF0aC4gVGhpcyAob24gTWFjKSB3aWxsIG5vdCB3b3JrOgojIHBoaWxseSA8LSByZWFkT0dSKCJ+L0Rlc2t0b3AvUlNwYXRpYWxEYXRhVHlwZXMvUGhpbGx5LyIsICJQaGlsbHlUb3RhbFBvcEhIaW5jIikKc3VtbWFyeShwaGlsbHkpCmNsYXNzKHBoaWxseSkKbmFtZXMocGhpbGx5KQpoZWFkKHBoaWxseSkKcGxvdChwaGlsbHkpCnBoaWxseV9yaWNoIDwtIHN1YnNldChwaGlsbHksIG1lZEhIaW5jID4gNjAwMDApCnBsb3QocGhpbGx5X3JpY2gsIGFkZD1ULCBjb2w9InJlZCIpCmBgYAoKKioqCgpHREFMIHN1cHBvcnRzIG92ZXIgMjAwIFtyYXN0ZXIgZm9ybWF0c10oaHR0cDovL3d3dy5nZGFsLm9yZy9mb3JtYXRzX2xpc3QuaHRtbCkgYW5kIFt2ZWN0b3IgZm9ybWF0c10oaHR0cDovL3d3dy5nZGFsLm9yZy9vZ3JfZm9ybWF0cy5odG1sKS4gVXNlIGBvZ3JEcml2ZXJzKClgIGFuZCBgZ2RhbERyaXZlcnMoKWAgKHdpdGhvdXQgYXJndW1lbnRzKSB0byBmaW5kIG91dCB3aGljaCBmb3JtYXRzIHlvdXIgYHJnZGFsYCBpbnN0YWxsIGNhbiBoYW5kbGUuCgoKIyMjIEhvdyB0byBkbyB0aGlzIGluIGBzZmAKCmBzZmAgYWxzbyByZWxpZXMgb24gR0RBTCwgYnV0IHdlIGRvbid0IG5lZWQgdG8gbG9hZCBhIHNlcGFyYXRlIFIgbGlicmFyeSB0byByZWFkIGRhdGEgaW4uIFdlIGNhbiB1c2UgYHN0X3JlYWQoKWAsIHdoaWNoIHNpbXBseSB0YWtlcyB0aGUgcGF0aCBvZiB0aGUgZGlyZWN0b3J5IHdpdGggdGhlIHNoYXBlZmlsZSBhcyBhcmd1bWVudC4gCgpTbyBsZXQncyBkbyB0aGUgc2FtZSBhcyBhYm92ZSB1c2luZyB0aGUgYHNmYCBwYWNrYWdlLgoKYGBge3IgZXZhbD1GQUxTRX0KIyByZWFkIGluCnBoaWxseV9zZiA8LSBzdF9yZWFkKCJ+L0Rlc2t0b3AvUlNwYXRpYWxEYXRhVHlwZXMvUGhpbGx5LyIpCgojIHRha2UgYSBsb29rIGF0IHdoYXQgd2UndmUgZ290Cm5hbWVzKHBoaWxseV9zZikKIyBub3RlIHRoZSBhZGRlZCBnZW9tZXRyeSBjb2x1bW4sIGFzIGNvbXBhcmVkIHRvOgpuYW1lcyhwaGlsbHkpCgojIHBsb3Qgd29ya3MgZGlmZmVyZW50bHkgaGVyZToKcGxvdChwaGlsbHlfc2YpCiMgdG8gZG8gdGhlIHNhbWUgYXMgYWJvdmUgd2UgbmVlZCB0byBkaXJlY3RseSBwcmludCB0aGUgZ2VvbWV0cnkgY29sdW1uCnN0X2dlb21ldHJ5KHBoaWxseV9zZikgICAgICAgICMgdXNlIHRoaXMgbWV0aG9kIHRvIHJldHJlaXZlIGdlb21ldHJ5CmBgYApgYGB7cn0KcGxvdChzdF9nZW9tZXRyeShwaGlsbHlfc2YpKQoKIyBzdWJzZXQgdGhlIGZhbWlsYXIgd2F5CnBoaWxseV9zZl9yaWNoIDwtIHN1YnNldChwaGlsbHlfc2YsIG1lZEhIaW5jID4gNjAwMDApCnBsb3Qoc3RfZ2VvbWV0cnkocGhpbGx5X3NmX3JpY2gpLCBhZGQ9VCwgY29sPSJyZWQiKQpgYGAKCgojIDQuIFJhc3RlciBkYXRhCgpEZWFsaW5nIHdpdGggcmFzdGVyIGRhdGEgYW5kIG1hcCBhbGdlYnJhIGRlc2VydmVzIGl0cyBvd24gc2VwYXJhdGUgd29ya3Nob3AsIHNvIHRoaXMgaXMganVzdCB0byBhY2tub3dsZWRnZSB0aGF0IHlvdSBjYW4gd29yayB3aXRoIHJhc3RlciBkYXRhIGluIFIgYXMgd2VsbC4KClJhc3RlciBmaWxlcywgYXMgeW91IHByb2JhYmx5IGtub3csIGhhdmUgYSBtdWNoIG1vcmUgY29tcGFjdCBkYXRhIHN0cnVjdHVyZSB0aGFuIHZlY3RvcnMuIEJlY2F1c2Ugb2YgdGhlaXIgcmVndWxhciBzdHJ1Y3R1cmUgdGhlIGNvb3JkaW5hdGVzIGRvIG5vdCBuZWVkIHRvIGJlIHJlY29yZGVkIGZvciBlYWNoIHBpeGVsIG9yIGNlbGwgaW4gdGhlIHJlY3Rhbmd1bGFyIGV4dGVudC4gQSByYXN0ZXIgaXMgZGVmaW5lZCBieToKCi0gYSBDUlMgCi0gY29vcmRpbmF0ZXMgb2YgaXRzIG9yaWdpbiAKLSBhIGRpc3RhbmNlIG9yIGNlbGwgc2l6ZSBpbiBlYWNoIGRpcmVjdGlvbiAKLSBhIGRpbWVuc2lvbiBvciBudW1iZXJzIG9mIGNlbGxzIGluIGVhY2ggZGlyZWN0aW9uCi0gYW4gYXJyYXkgb2YgY2VsbCB2YWx1ZXMgCgpHaXZlbiB0aGlzIHN0cnVjdHVyZSwgY29vcmRpbmF0ZXMgZm9yIGFueSBjZWxsIGNhbiBiZSBjb21wdXRlZCBhbmQgZG9uJ3QgbmVlZCB0byBiZSBzdG9yZWQuIAoKSW4gYHNwYCB0aGUgYEdyaWRUb3BvbG9neWAgY2xhc3MgaXMgdGhlIGtleSBlbGVtZW50IG9mIHJhc3RlciByZXByZXNlbnRhdGlvbnNbXjRdLiBJdCBjb250YWlucyAKCiogdGhlIGNlbnRlciBjb29yZGluYXRlIHBhaXIgb2YgdGhlIHNvdXRoLXdlc3QgcmFzdGVyIGNlbGwsIAoqIHRoZSB0d28gY2VsbCBzaXplcyBpbiB0aGUgbWV0cmljIG9mIHRoZSBjb29yZGluYXRlcywgZ2l2aW5nIHRoZSBzdGVwIHRvIHN1Y2Nlc3NpdmUgY2VudHJlcywgYW5kIAoqIHRoZSBudW1iZXJzIG9mIGNlbGxzIGZvciBlYWNoIGRpbWVuc2lvbi4gCgpbXjRdOiBUaGVyZSBpcyBhbHNvIGEgYFNwYXRpYWxQaXhlbHNgIG9iamVjdCB3aGljaCBzdG9yZXMgZ3JpZCB0b3BvbG9neSBhbmQgY29vcmRpbmF0ZXMgb2YgdGhlIGFjdHVhbCBwb2ludHMuCgpBIHNpbXBsZSBncmlkIGNhbiBiZSBidWlsdCBsaWtlIHRoaXM6CgpgYGB7ciB0aWR5PUZ9CiMgc3BlY2lmeSB0aGUgZ3JpZCB0b3BvbG9neSB3aXRoIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVyczoKIyAtIHRoZSBzbWFsbGVzdCBjb29yZGluYXRlcyBmb3IgZWFjaCBkaW1lbnNpb24sIGhlcmU6IDAsMAojIC0gY2VsbCBzaXplIGluIGVhY2ggZGltZW5zaW9uLCBoZXJlOiAxLDEgCiMgLSBudW1iZXIgb2YgY2VsbHMgaW4gZWFjaCBkaW1lbnNpb24sIGhlcmU6IDUsNQpndG9wbyA8LSBHcmlkVG9wb2xvZ3koYygwLDApLCBjKDEsMSksIGMoNSw1KSkgIyBjcmVhdGUgdGhlIGdyaWQKZGF0YWZyIDwtIGRhdGEuZnJhbWUocnVuaWYoMjUpKSAjIG1ha2UgdXAgc29tZSBkYXRhClNwR2RmIDwtIFNwYXRpYWxHcmlkRGF0YUZyYW1lKGd0b3BvLCBkYXRhZnIpICMgY3JlYXRlIHRoZSBncmlkIGRhdGEgZnJhbWUKc3VtbWFyeShTcEdkZikKYGBgCgpBIHZlcnkgZ29vZCBhbHRlcm5hdGl2ZSBpcyB0aGUgYHJhc3RlcmAgcGFja2FnZSwgd2hpY2ggd29ya3Mgc2xpZ2h0bHkgZGlmZmVyZW50bHkuICAKVGhlIGByYXN0ZXJgIHBhY2thZ2UgaXMgYSBtYWpvciBleHRlbnNpb24gb2Ygc3BhdGlhbCBkYXRhIGNsYXNzZXMgdG8gYWNjZXNzIGxhcmdlIHJhc3RlcnMgYW5kIGluIHBhcnRpY3VsYXIgdG8gcHJvY2VzcyB2ZXJ5IGxhcmdlIGZpbGVzLiBJdCBpbmNsdWRlcyBvYmplY3QgY2xhc3NlcyBmb3IgYFJhc3RlckxheWVyYCwgYFJhc3RlclN0YWNrc2AsIGFuZCBgUmFzdGVyQnJpY2tzYCwgZnVuY3Rpb25zIGZvciBjb252ZXJ0aW5nIGFtb25nIHRoZXNlIGNsYXNzZXMsIGFuZCBvcGVyYXRvcnMgZm9yIGNvbXB1dGF0aW9ucyBvbiB0aGUgcmFzdGVyIGRhdGEuIENvbnZlcnNpb24gZnJvbSBgc3BgIHR5cGUgb2JqZWN0cyBpbnRvIGByYXN0ZXJgIHR5cGUgb2JqZWN0cyBpcyBlYXN5LgoKSWYgd2Ugd2FudGVkIHRvIGRvIHRoZSBzYW1lIGFzIGFib3ZlLCBuYW1lbHkgY3JlYXRpbmcgdGhlIHNhbWUgcmFzdGVyIG9iamVjdCBmcm9tIHNjcmF0Y2ggd2Ugd291bGQgZG8gdGhlIGZvbGxvd2luZzoKCmBgYHtyIHRpZHk9Rn0KIyBzcGVjaWZ5IHRoZSBSYXN0ZXJMYXllciB3aXRoIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVyczoKIyAtIG1pbmltdW0geCBjb29yZGluYXRlIChsZWZ0IGJvcmRlcikKIyAtIG1pbmltdW0geSBjb29yZGluYXRlIChib3R0b20gYm9yZGVyKQojIC0gbWF4aW11bSB4IGNvb3JkaW5hdGUgKHJpZ2h0IGJvcmRlcikKIyAtIG1heGltdW0geSBjb29yZGluYXRlICh0b3AgYm9yZGVyKQojIC0gcmVzb2x1dGlvbiAoY2VsbCBzaXplKSBpbiBlYWNoIGRpbWVuc2lvbgpyIDwtIHJhc3Rlcih4bW49LTAuNSwgeW1uPS0wLjUsIHhteD00LjUsIHlteD00LjUsIHJlc29sdXRpb249YygxLDEpKQpyCmBgYAoKU28gaGVyZSB3ZSBoYXZlIGNyZWF0ZWQgYW4gb2JqZWN0IG9mIHR5cGUgYFJhc3RlckxheWVyYCwgYXMgY29tcGFyZWQgdG8gYWJvdmUsIHdoZXJlIHdlIGNyZWF0ZWQgYW4gb2JqZWN0IG9mIHR5cGUgYEdyaWRUb3BvbG9neWAuCgpDb21wYXJlIHRoaXMgdG8gdGhlIG91dHB1dCBmcm9tIGFib3ZlIGFuZCBfX25vdGUgc29tZXRoaW5nIGltcG9ydGFudCBoZXJlX186IERpZmZlcmVudCBmcm9tIHRoZSBncmlkIG9iamVjdCB3ZSBnZW5lcmF0ZWQgZnJvbSBzY3JhdGNoLCB0aGlzIHJhc3RlciBvYmplY3QgaGFzIGEgQ1JTIGRlZmluZWQhIElmIHRoZSBjcnMgYXJndW1lbnQgaXMgbWlzc2luZyB3aGVuIGNyZWF0aW5nIHRoZSBSYXN0ZXIgb2JqZWN0LCB0aGUgeCBjb29yZGluYXRlcyBhcmUgd2l0aGluIC0zNjAgYW5kIDM2MCBhbmQgdGhlIHkgY29vcmRpbmF0ZXMgYXJlIHdpdGhpbiAtOTAgYW5kIDkwLCB0aGUgV0dTODQgcHJvamVjdGlvbiBpcyB1c2VkIGJ5IGRlZmF1bHQhIAoKR29vZCB0byBrbm93LgoKVG8gYWRkIHNvbWUgdmFsdWVzIHRvIHRoZSBjZWxscyB3ZSBjb3VsZCB0aGUgZm9sbG93aW5nLiBCZSBhd2FyZSB0aGF0IGRpZmZlcmVudCBmcm9tIHRoZSBgR3JpZFRvcG9sb2d5YCBvYmplY3QgYWJvdmUsIHdoaWNoIHdlIGNvbnZlcnRlZCB0byBhIGBTcGF0aWFsR3JpZERhdGFGcmFtZWAgd2hlbiBhZGRpbmcgdmFsdWVzLCB0aGlzIG9iamVjdCBoZXJlIHJlbWFpbnMgYSBgUmFzdGVyTGF5ZXJgLgoKYGBge3IgdGlkeT1GfQpjbGFzcyhyKQpyIDwtIHNldFZhbHVlcyhyLCBydW5pZigyNSkpCmNsYXNzKHIpCnBsb3Qocik7IHBvaW50cyhjb29yZGluYXRlcyhyKSwgcGNoPTMpCmBgYAoKKFNlZSB0aGUgW2ByYXN0ZXJWaXNgIHBhY2thZ2VdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9yYXN0ZXJWaXMvaW5kZXguaHRtbCkgZm9yIG1vcmUgYWR2YW5jZWQgcGxvdHRpbmcgb2YgYFJhc3RlcipgIG9iamVjdHMuKQoKUmFzdGVyTGF5ZXIgb2JqZWN0cyBjYW4gYWxzbyBiZSBjcmVhdGVkIGZyb20gYSBtYXRyaXguCgpgYGB7cn0KY2xhc3Modm9sY2FubykKdm9sY2Fuby5yIDwtIHJhc3Rlcih2b2xjYW5vKQpjbGFzcyh2b2xjYW5vLnIpCmBgYAoKVG8gcmVhZCBpbiBhIHJhc3RlciBmaWxlIHdlIGNhbiB1c2UgYHJlYWRHREFMKClgIGZyb20gdGhlIGBzcGAgcGFja2FnZSwgd2hpY2ggcmVxdWlyZXMgb25seSB0aGUgZmlsZW5hbWUgb2YgdGhlIHJhc3RlciBhcyBhcmd1bWVudC4KClRoZSByZXNwZWN0aXZlIGZ1bmN0aW9uIGluIGByYXN0ZXJgIHBhY2thZ2UgaXMgY2FsbGVkIGByYXN0ZXIoKWAuCgoqKioKCiMjIyMgRXhlcmNpc2UgNQoKMS4gTG9hZCB0aGUgYHJhc3RlcmAgbGlicmFyeQoyLiBSZWFkIGluIHRoZSBERU0gdXNpbmcgdGhlIGByYXN0ZXIoKWAgZnVuY3Rpb24KMy4gRXhhbWluZSBieSB0eXBpbmcgdGhlIG5hbWUgeW91IGdhdmUgdGhlIERFTSAKNC4gRXh0cmFjdCBjb250b3VyIGxpbmVzIGFuZCBwbG90IHRoZW0gd2l0aCBgY29udG91cigpYAoKPiBUcnkgYmVmb3JlIHlvdSBwZWVrIQoKYGBge3IgZXZhbD1GfQpsaWJyYXJ5KHJhc3RlcikKZGVtLnIgPC0gcmFzdGVyKCJ+L0Rlc2t0b3AvUlNwYXRpYWxEYXRhVHlwZXMvREVNXzEwbS9idXNoa2lsbF9wYS5kZW0iKQpkZW0ucgpjb250b3VyKGRlbS5yKQpgYGAKCioqKgoKClRoZXJlIGFyZSBjdXJyZW50bHkgb3ZlciAxNzAgW1IgcGFja2FnZXMgb24gQ1JBTiBmb3IgcmVhZGluZywgdmlzdWFsaXNpbmcsIGFuZCBhbmFseXNpbmcgKGdlb2dyYXBoaWNhbCkgc3BhdGlhbCBkYXRhXShodHRwOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi92aWV3cy9TcGF0aWFsLmh0bWwpLiBJIGhpZ2hseSByZWNvbW1lbmQgdGFraW5nIGEgbG9vayBhdCB0aGF0IHBhZ2UgaWYgeW91IGFyZSBleHBsb3Jpbmcgc3BhdGlhbCBhbmFseXNpcyB3aXRoIFIu