Dataset
While we have seen that biological data can be obtained from sites
such as Dryad and GBIF, we need to obtain data with
specific variables that might not be available on those sites. So to
start this exercise, we will download a *.csv file containing
information for Ophiophagus hannah (King Cobra) from movebank.org
that contains the appropriate variable to track movement from radio
transmitter data. Because this data has to be downloaded we will need to
go through the process of importing this information using the
read.csv() command. Once we have imported the data we can
view the structure.
If forked, this data will be available in the Data folder within the
repository. Additionally, ESRI Shapefile and Google KMZ formats will be
included if you are interested in working with those data types.
Import Dataset with read.csv
data <- read.csv("./Data/ophiophagus_hannah.csv")
Notice that this dataset contains more than species information and
x,y coordinates. While that information is important, this dataset also
contains time and date information (timestamp), sensor type, and UTM
data. In order to be sure that the dataset contains no outliers, we can
plot the data using ggplot and plotly to
interactively view the data.
qaqc_plot <- ggplot() + geom_point(data=data,
aes(utm.easting,utm.northing,
color=individual.local.identifier)) +
labs(x="Easting", y="Northing") +
guides(color=guide_legend("Identifier"))
plotly::ggplotly(qaqc_plot)
With plotly, similar to leaflet, we have
the ability to examine the spread of the data points and additional
information from various columns. From this plot we can see that there
are two individuals, OPHA1 and OPHA2, that were tracked for this study
and have very little overlap in their apparent range.
While we could continue with the current dataset to calculate home
range for the entire population, to simplify this exercises we will
instead focus on individuals.
Depending on the needs of your analysis, you may find a situation
where a dataset being provided by a collaborator or through a repository
contains a large number of individuals being recorded. In order to
separate these larger datasets into individuals, we can create a
function that uses the lapply command to apply a function
over a list or vector dataset. Specifically, this function will take the
original dataset, split it into separate files based on the
individual identifier, and create new *.csv files using the identifier
as the filename.
The anatomy of the function is as follows:
lapply(), apply the function over a list
split(), separates the data
function(), compose a series of steps to be applied to
the data
write.csv(), write a csv file
paste(), create a character string to be used for the
file name
If you examine the root folder you will now find the addition of two
new *.csv files: OPHA1 and OPHA2. We will use these files to run our
home range analyses. Alternatively, if you are provided a number of
individual *.csv files and need to quickly import the data into separate
data frames you could write a for loop that would look
something like this:
list <- gsub("\\.csv$","", list.files(pattern="\\.csv$"))
for(i in list){
assign(i, read.csv(paste(i, ".csv", sep="")))
}
All of the *.csv files in your root directory will now be available
in your global environment. However, the method we used above created
individual *.csv files for each individuals, so instead we will make a
list of those individual files as reference for the analyses when
necessary.
files <- list.files(path = ".", pattern = "[OPHA]+[0-9]+", full.names = TRUE)
In the list.files command above, path = "."
informs the location, in this case the root directory,
pattern = describes the way the files are named, in this
case OPHA followed by a number between 0-9, and full.names
describes how the files will be listed.
Analysis
To date, this will be one of the more code intensive exercises we
have attempted. While the code itself is not difficult, the
implementation is tedious due to the varying data types and analyses
performed. Additionally, while not run as a component of this exericse,
I am providing a script that will walk you through the steps to automate
an analysis for a number of individuals using a function and
pbapply which produces a progress bar in the
console detailing the level of completion.
Imagery
In previous exercises we used a number of different sources to obtain
imagery or basemaps for the various visualizations. As we have discussed
with packages like openstreetmap or ggmap
there can be restrictions that complicate their usefulness.
Additionally, some sources of raster imagery can be limited in their
geographic scope. In the previous
exercise we used Google Earth Engine to produce
images that could be used as basemaps for our analyses. So for this
exercise we will use GEE to obtain a Sentinel 2 image from Thailand and
utilize it as the background for our homerange analyses.
Because this code is javascript I will simply use a code
block to provide details on the script I used to locate, prepare, and
download the image. For more information refer to the previous exercise
or GEE documentation.
// Set variables for the location and imagery source
var thailand = /* color: #d63000 */ee.Geometry.Point([101.96660539060161, 14.546641122346026]),
sentinel2 = ee.ImageCollection("COPERNICUS/S2_HARMONIZED");
// Dates below used are closest available to data collection date
var image = ee.ImageCollection(sentinel2) // identify the sensor
.filterDate("2016-04-01", "2016-10-30") // filter specific dates
.filterBounds(thailand) // only include scenes that intersect with our point geometry
.sort("CLOUD_COVERAGE_ASSESSMENT") // filter by cloudy pixel percentage
.first(); // select the least cloudy scene
// Set visualization parameters, these can be adjusted within the code editor
var naturalcolor = {
bands: ["B4", "B3", "B2"],
min: 0,
max: 4000,
};
// Display images on the map
Map.addLayer(image, naturalcolor, "Natural Color Composite");
// Prepare the image and export in UTM 47; remember the data is from Thailand
var exportRGB = image.visualize({
bands: ["B4", "B3", "B2"],
min: 0,
max: 4000,
});
// CRS was set to appropriate EPSG and maxPixels were set to allow for large file size
Export.image.toDrive({
image: exportRGB,
scale: 10,
description: "thailand32647",
crs: "EPSG:32647",
maxPixels: 1e13,
});
Either set the save location to the project folder, or mannually copy
the imagery over to make it available in R. There is a rgee
package you can experiment with to incorporate this step into workflow
within R however, there is a little learning curve due to the majority
of the script being python-based. In order to make the resulting
*.geoTIFF file suitable for use in ggplot, we’ll need to
create an image stack and convert it to a dataframe. While this step
isn’t completely necessary, it does make the syntax of using the image
in ggplot more straightforwrd. Additionally, because image
swath from Sentinel 2 is 290km, we will also create two sizes
of crops for the varying spatial constraints of the data. This can be
adjusted throughout the analysis as needed to fit the specific
visualizations.
ortho <- "./Data/thailand32647.tif"
image_stack <- raster::stack(ortho)
crop_extent <- raster::extent(min(data$utm.easting-2000),
max(data$utm.easting+2000),
min(data$utm.northing-2000),
max(data$utm.northing+2000))
crop_full <- raster::extent(min(data$utm.easting-5000),
max(data$utm.easting+5000),
min(data$utm.northing-5000),
max(data$utm.northing+5000))
image_crop <- crop(image_stack, crop_extent)
image_full <- crop(image_stack, crop_full)
thailand <- as.data.frame(image_crop, xy = TRUE) %>% setNames(c("x", "y", "red", "green", "blue"))
thailand.full <- as.data.frame(image_full, xy = TRUE) %>% setNames(c("x", "y", "red", "green", "blue"))
ggplot()+geom_spatial_rgb(data = thailand, aes(x=x, y=y, r=red, g=green, b=blue))+coord_sf()

Home Range
Analysis
There are three basic types of home range analyses we will perform in
this exercise: Minimum Convex Polygon (MCP), Kernel-Density Estimation
(KDE), and Brownian Bridge Movement Model (BB). There are a number of
different tuning parameters that can be applied these analyses, however
in this exercise we will use the most basic versions of the
analysis.
In the section above we use the lapply command to loop a
function used to separate the original dataset into individual files.
This is a useful tool, however, when the function loops through dozens
or even hundreds of files, the process can take a long period of time to
complete. Using the pblapply command adds a progress bar
(i.e. pb) to the process which provides an estimated
time for completion of the function. We will use a similar process to
the one above, using the pblapply command to run MCP
analysis on the individual *.csv files.
Minimum Convex
Polygon
One of the most simple homerange analyses is a minimum convex
polygon or mcp. A mcp draws a polygon which
represents a specified minimum bounding geometry enclosing the data
points. This essentially connects the points along the farthest
geographic points enclosing them all in a single shape. For this
analysis we will focus on the OPHA1 file we separated from the larger
dataset using the adehabitatHR package.
A description of the steps within the following code will be
discussed following the output. The process works by establishing the
function and then running pblapply referring back to the
files we created in the dataset portion of this
exercise.
## Data for a single individual
opha1 <- read.csv('OPHA1.csv')
xy <- c(as.data.frame(opha1$utm.easting),as.data.frame(opha1$utm.northing))
## projecting the data to UTM
data.proj <- SpatialPointsDataFrame(xy,opha1, proj4string = CRS("+proj=utm +zone=47 +datum=WGS84 +units=m +no_defs"))
xy.sp <- SpatialPoints(data.proj@coords)
## MCP analysis
mcp.out <- mcp(xy.sp, percent=100, unout="ha")
## converting mcp to sf object for ggplot and points to df
mcp.sf <- st_as_sf(mcp.out)
mcp.points <- cbind((data.frame(xy)),opha1$individual.local.identifier)
colnames(mcp.points) <- c("x","y", "identifier")
# MCP plot
mcp.plot <- ggplot() + theme_bw() +
theme(axis.text.y = element_text(angle = 90, hjust = 0.5, vjust = 0.5)) +
theme(panel.grid = element_blank()) +
geom_spatial_rgb(data = thailand, aes(x=x, y=y, r=red, g=green, b=blue)) +
geom_sf(data=mcp.sf, alpha = 0.1) +
geom_point(data=mcp.points, aes(x=x, y=y), color = "lightgreen") +
labs(x="Easting (m)", y="Northing (m)", title=mcp.points$identifier) +
theme(legend.position="none", plot.title = element_text(face = "bold", hjust = 0.5)) +
coord_sf(xlim = c(820000, 814250), ylim = c(1604500, 1608000))
mcp.plot + annotate("text", x = 815000, y = 1607500, label = paste(round(mcp.out@data$area,2),"ha"),
fontface = "bold", size = 5, color = "white")

The anatomy of the script above is as follows:
read.csv(), read the data from a given *.csv file
- xy, create a coordinate file of the easting and northing data
SpatialPointsDataFrame(), project the data to UTM Zone
47
mcp(), computes home range using the Minimum Convex
Polygon estimator
st_as_sf, converts to a data frame for plotting with
ggplot2
colnames(), renames the columns
annotate, adds text to the plot
The rest of the information can be found by examining the help menu
for the various commands or looking at the display options for each
portion of the script.
Kernel-Density
Estimation
The same design above can be used to create KDE plots for each
individual in the dataset. Because the process is essentially the same,
only portions of the script that were not previously described will be
discussed.
## Data for a single individual
opha2 <- read.csv('OPHA2.csv')
x <- as.data.frame(opha2$utm.easting)
y <- as.data.frame(opha2$utm.northing)
xy <- c(x,y)
## projecting the data to UTM
data.proj <- SpatialPointsDataFrame(xy,opha2, proj4string = CRS("+proj=utm +zone=47 +datum=WGS84 +units=m +no_defs"))
xy.sp <- SpatialPoints(data.proj@coords)
## KDE analysis at 95, 85, 75
kde<-kernelUD(xy.sp, h="href", kern="bivnorm", grid=100)
ver95 <- getverticeshr(kde, 95)
ver85 <- getverticeshr(kde, 85)
ver75 <- getverticeshr(kde, 75)
## converting to sf object and df
kde.points <- cbind((data.frame(data.proj@coords)),opha2$individual.local.identifier)
colnames(kde.points) <- c("x","y","identifier")
kde95.sf <- st_as_sf(ver95)
kde85.sf <- st_as_sf(ver85)
kde75.sf <- st_as_sf(ver75)
##making the plot
kde.plot <- ggplot() + theme_bw() +
theme(axis.text.y = element_text(angle = 90, hjust = 0.5, vjust = 0.5)) +
geom_spatial_rgb(data = thailand, aes(x=x, y=y, r=red, g=green, b=blue)) +
geom_sf(data=kde95.sf, fill = "blue", alpha = 0.4) +
geom_sf(data=kde85.sf, fill = "green", alpha = 0.3) +
geom_sf(data=kde75.sf, fill = "yellow", alpha = 0.2) +
geom_point(data=kde.points, aes(x=x, y=y), color = "lightgray") +
labs(x="Easting (m)", y="Northing (m)", title=kde.points$identifier) +
theme(legend.position="none", plot.title = element_text(face = "bold", hjust = 0.5)) +
coord_sf(xlim = c(821900, 817000), ylim = c(1605000, 1615000))
kde.plot + annotate("text", x = 818000, y = 1614500, label = paste("95% ",round(ver95$area,2),"ha"),
fontface = "bold", size = 4, color = "white")

The anatomy of the function above is as follows:
kernelUD(), estimation of kernel home-range
getverticeshr(), extract home-range contour
The rest of the information can be found by examining the help menu
for the various commands or looking at the display options for each
portion of the script.
Automation Using
Functions
Depending on your desired results, the steps above can be included in
a function that will allow you to automate this process over a list of
files and create mcp or kde analyses for all the
individuals separately. Basically, we are taking all of the elements of
the scripts above, making some of the values generic (e.g. filename) and
creating the steps for the process that can be repeated over a list of
files that you created earlier in the exercises. The object called
files contains a list of all the *.csv files for each
individual. So we will apply the function, over the list, to automate
the analysis and visualizations for each individual. Because the
kde process is essentially the same as above, only portions of
the function that were not previously described will be discussed.
#run KDE creating plot for each individual with a progress bar
kde_raster <- function(filename){
data <- read.csv(file = filename)
x <- as.data.frame(data$utm.easting)
y <- as.data.frame(data$utm.northing)
xy <- c(x,y)
data.proj <- SpatialPointsDataFrame(xy,data, proj4string = CRS("+proj=utm +zone=47 +datum=WGS84 +units=m +no_defs"))
xy.sp <- SpatialPoints(data.proj@coords)
kde<-kernelUD(xy.sp, h="href", kern="bivnorm", grid=100)
ver95 <- getverticeshr(kde, 95)
ver85 <- getverticeshr(kde, 85)
ver75 <- getverticeshr(kde, 75)
kde.points <- cbind((data.frame(xy)),data$individual.local.identifier)
colnames(kde.points) <- c("x","y","identifier")
kde95.sf <- st_as_sf(ver95)
kde85.sf <- st_as_sf(ver85)
kde75.sf <- st_as_sf(ver75)
kde.plot <- ggplot() + theme_bw() +
theme(axis.text.y = element_text(angle = 90, hjust = 0.5, vjust = 0.5)) +
geom_spatial_rgb(data = thailand, aes(x=x, y=y, r=red, g=green, b=blue)) +
geom_sf(data=kde95.sf, fill = "blue", alpha = 0.4) +
geom_sf(data=kde85.sf, fill = "green", alpha = 0.3) +
geom_sf(data=kde75.sf, fill = "yellow", alpha = 0.2) +
geom_point(data=kde.points, aes(x=x, y=y), color = "lightgray") +
coord_sf(xlim = c(822000, 813950), ylim = c(1616250, 1604500)) +
labs(x="Easting (m)", y="Northing (m)", title=kde.points$identifier) +
theme(legend.position="none", plot.title = element_text(face = "bold", hjust = 0.5))
kde.plot + annotate("text", x = 815000, y = 1616000, label = paste("95% ",round(ver95$area,2)," ha"),
fontface = "bold", size = 5, color = "white")
}
pblapply(files, kde_raster)
| | 0 % ~calculating
|+++++++++++++++++++++++++ | 50% ~00s
|++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=01s
[[1]]
[[2]]
The anatomy of the script above is as follows: the
kde_raster <- function(filename){... function runs for
each file listed from the project directory in the files
list complete the following commands
read.csv(file = filename), there is no specific file
name needed as it will use each *.csv in the list as a new file
pbapply(), runs the function kde_raster
over the list of *.csv in the files object
The rest of the information can be found by examining the help menu
for the various commands or looking at the display options for each
portion of the script.
Try it yourself! Using the example script above, and following the
example functions, create a looping function with pblapply
to run the mcp analysis for each individual in the files list.
Continuous-Time
Movement Modeling
There is a more powerful package called ctmm that can be
used for the creation of conventional kernel-density estimates as well
as uniformly weighted kernel-density estimates. While I am less familiar
with the variables, objects, and setting for each model, I was able to
follow along with the vignette
for Autocorrelated Kernel Density Estimation on the package details
to create a similar kde analysis as above. All of the same
methods automation methods can be employed, however, there are some
difference to note.
For this portion of the exercises we will prepare the two OPHA *.csv
files for analysis using akde in the `ctmm
package.
## convert movebank to telemetry data
opha.telem <- as.telemetry(data, projection = "+proj=utm +zone=47 +datum=WGS84 +units=m +no_defs")
opha1.telem <- as.telemetry('OPHA1.csv', projection = "+proj=utm +zone=47 +datum=WGS84 +units=m +no_defs")
opha2.telem <- as.telemetry("OPHA2.csv", projection = "+proj=utm +zone=47 +datum=WGS84 +units=m +no_defs")
You can examine the data in the environment to see how the data was
restructured to be available for further analysis. Notice that a
projection type is listed for each object. This was used to ensure the
data would be geographically located rather than placed in general space
by meters.
# calculate fit object
opha2.guess <- ctmm.guess(opha2.telem, interactive=FALSE)
# compute akde and get summary information
opha2.ouf <- ctmm.fit(opha2.telem, opha2.guess)
wAKDE.opha2 <- akde(opha2.telem, opha2.ouf, weights=TRUE)

summary(wAKDE.opha2)
# basic plot
plot(opha2.telem, UD=wAKDE.opha2)

You can see the details of each of the object created in your
environment. The analysis create a class UD object that
cannot be used in ggplot without conversion. Additionally,
we will create an object specifically to use for the inclusion of area
on the final plot.
# convert to SPDF to convert to sf object for plotting in ggplot
ud.sp <- SpatialPolygonsDataFrame.UD(wAKDE.opha2)
ud.sf <- st_as_sf(ud.sp)
opha2.sf <- st_as_sf(opha2, coords = c("utm.easting","utm.northing")) %>% st_set_crs(32647)
# adke summary data
akde.summary <- summary(wAKDE.opha2)
akde.summary[["CI"]][3]
[1] 124.8182
# ggplot for akde
ctmm.akde <- ggplot() + geom_spatial_rgb(data = thailand.full, aes(x=x, y=y, r=red, g=green, b=blue)) +
geom_sf(data = ud.sf[3,], fill = "blue", alpha = 0.4) +
geom_sf(data = ud.sf[2,], fill = "green", alpha = 0.3) +
geom_sf(data = ud.sf[1,], fill = "yellow", alpha = 0.2) +
geom_sf(data = opha2.sf, color = "lightgray") +
coord_sf(xlim = c(825500, 813500), ylim = c(1618000, 1602000)) +
labs(x="Longitude", y="Latitude", title = opha2.sf$individual.local.identifier) +
theme(legend.position="none", plot.title = element_text(face = "bold", hjust = 0.5)) +
theme_bw() + theme(axis.text.y = element_text(angle = 90, hjust = 0.5, vjust = 0.5))
ctmm.akde + annotate("text", x = 815000, y = 1617000,
label = paste("Area ",round(akde.summary[["CI"]][3],2)," sqkm"),
fontface = "bold", size = 5, color = "white")

The details of this analysis are pretty new to me so I don’t yet have
a solid grasp on the various components of the script, but if you want
more information you can view:
Edelkind, J.L., Stalker, J.B., Beck, D.D., Denardo, D.F., Emplidge,
P.G., Gallardo, L., Gentry, C.M., Goode, M.J., Jennings, R.D., Jones.
J.L., Kwiatkowski, M.A., Nowak, E.M., Repp, R.A., Schuett, G.W.,
Sullivan, B.K., Tracy, C.R., and Gienger, C.M. (2025) Environmental
Influences on Space Use of Gila Monsters (Heloderma suspectum).
Herpetologica. https://doi.org/10.1655/Herpetologica-D-24-00053
This was simply an example of how to run the analysis and prepare the
resulting objects for visualization.
LS0tDQp0aXRsZTogSG9tZSBSYW5nZSBBbmFseXNpcyA8YnI+PHNtYWxsPkFkdmFuY2VkIERhdGEgQW5hbHl0aWNzPC9zbWFsbD48L2JyPg0KYXV0aG9yOiAiQXVzdGluIFBlYXkgU3RhdGUgVW5pdmVyc2l0eSINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgICByb3dzLnByaW50OiAxMA0KICAgIHRoZW1lOiBjb3Ntbw0KICAgIGhpZ2hsaWdodDogYnJlZXplZGFyaw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IG5vDQogICAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIHJvd3MucHJpbnQ6IDEwDQogICAgdGhlbWU6IGNvc21vDQogICAgaGlnaGxpZ2h0OiBicmVlemVkYXJrDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogbm8NCiAgICAgIHNtb290aF9zY3JvbGw6IHllcw0KZWRpdG9yX29wdGlvbnM6DQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCiAgbW9kZTogZ2ZtDQotLS0NCg0KYGBgez1odG1sfQ0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCg0KaDEudGl0bGUgew0KICBmb250LXNpemU6IDQwcHg7DQogIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICBjb2xvcjogQmxhY2s7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCg0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgZm9udC1zaXplOiAyNXB4Ow0KICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGNvbG9yOiAjRDAyMzQ5Ow0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQoNCmJvZHkgew0KICBmb250LWZhbWlseTogSGVsdmV0aWNhOw0KICBmb250LXNpemU6IDEycHQ7DQp9DQoNCi56b29tIHsNCiAgdHJhbnNmb3JtLW9yaWdpbjogNDAlIDUwJSAwOw0KICB0cmFuc2l0aW9uOiB0cmFuc2Zvcm0gLjJzOw0KICBtYXJnaW46IDAgYXV0bzsNCn0NCi56b29tIGltZ3sNCgl3aWR0aDphdXRvOw0KCWhlaWdodDphdXRvOwkNCn0NCi56b29tOmhvdmVyIHsNCiAgdHJhbnNmb3JtOiBzY2FsZSgyKTsNCn0NCg0KdGgsIHRkIHtwYWRkaW5nOiA1cHg7fQ0KDQo8L3N0eWxlPg0KYGBgDQoNCiMgSW50cm9kdWN0aW9uDQoNClRoaXMgZXhlcmNpc2Ugd2lsbCBidWlsZCBvbiB0aGUgc2tpbGxzIGFjcXVpcmVkIGluIHRoZSBbTWFwcGluZyBCYXNpY3NdKGh0dHBzOi8vY2hyaXNtZ2VudHJ5LmdpdGh1Yi5pby9NYXBwaW5nLUJhc2ljcy8pIGFuZCBbU3BlY2llcyBEaXN0cmlidXRpb24gTW9kZWxpbmddKGh0dHBzOi8vY2hyaXNtZ2VudHJ5LmdpdGh1Yi5pby9EaXN0cmlidXRpb24tTWFwcy8pIGV4ZXJjaXNlcyB0byBwZXJmb3JtICoqSG9tZSBSYW5nZSBBbmFseXNpcyoqIGFuZCBtYXAgdGhlIHJlc3VsdHMuIFdlIHdpbGwgc3RhcnQgYnkgZXhhbWluaW5nIHRoZSBzdHJ1Y3R1cmUgb2YgZGF0YSB1c2VkIGluIGhvbWUgcmFuZ2UgYW5hbHlzZXMsIHdlIHdpbGwgbG9vayBhdCB3YXlzIHRvIHBlcmZvcm0gUUEvUUMgY2hlY2tzIG9uIHRoZSBsb2NhdGlvbiBkYXRhLCBhbmQgZmluYWxseSB2YXJpb3VzIHdheXMgdG8gdmlzdWFsaXplIHRoZSBkYXRhLiBPbmUgb2YgdGhlIG5ldyBza2lsbHMgd2Ugd2lsbCBkaXNjdXNzIGluIHRoaXMgZXhlcmNpc2UgaXMgdG8gY3JlYXRlIGZ1bmN0aW9ucyBhbmQgbG9vcHMgdG8gcmVwbGljYXRlIGFuYWx5c2VzLiANCg0KIyMgUGFja2FnZXMgdXNlZCBpbiB0aGlzIGV4ZXJjaXNlDQoNClRoZXJlIGFyZSBzZXZlcmFsIHNwZWNpYWx0eSBwYWNrYWdlcyB0aGF0IHdpbGwgYmUgdXNlZCBpbiB0aGlzIGV4ZXJjaXNlIGR1ZSB0byB0aGUgc3BlY2lmaWMgbmF0dXJlIG9mIHRoZSBhbmFseXNlcy4gU29tZSBvZiB0aGVzZSBwYWNrYWdlcyB5b3Ugd2lsbCBuZWVkIHRvIGluc3RhbGwgd2hpbGUgc2V2ZXJhbCBvdGhlcnMgd2UgaGF2ZSB1c2VkIGluIHByZXZpb3VzIGV4ZXJjaXNlcyBhbmQgc2hvdWxkIGFscmVhZHkgYmUgaW5zdGFsbGVkLg0KDQo8cCBhbGlnbj0iY2VudGVyIj4NCnwgYGBgYWRlaGFiaXRhdEhSYGBgIHwgYGBgY3RtbWBgYCB8IGBgYHBiYXBwbHlgYGAgfCBgYGByYXN0ZXJgYGAgfCBgYGBzZmBgYCB8IGBgYHNwYGBgIHwgPGJyPjwvYnI+IHwgYGBgdGVycmFgYGAgfCBgYGB0ZXJyYWlucmBgYCB8IGBgYHRpZHl2ZXJzZWBgYCB8DQo8L3A+DQoNClRvIGJlZ2luLCB3ZSB3aWxsIGluc3RhbGwgdGhlIGZvbGxvd2luZzoNCg0KYGBge3IgUGFja2FnZXMsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnBhY21hbjo6cF9sb2FkKCJhZGVoYWJpdGF0SFIiLCAiY3RtbSIsICJwYmFwcGx5IiwgInJhc3RlciIsICJzZiIsICJzcCIsICJ0ZXJyYSIsICJ0ZXJyYWluciIsICJ0aWR5dmVyc2UiKQ0KYGBgDQoNCiMgRGF0YXNldA0KDQpXaGlsZSB3ZSBoYXZlIHNlZW4gdGhhdCBiaW9sb2dpY2FsIGRhdGEgY2FuIGJlIG9idGFpbmVkIGZyb20gc2l0ZXMgc3VjaCBhcyBbRHJ5YWRdKGh0dHBzOi8vZGF0YWRyeWFkLm9yZy9zZWFyY2gpIGFuZCBbR0JJRl0oaHR0cHM6Ly93d3cuZ2JpZi5vcmcvKSwgd2UgbmVlZCB0byBvYnRhaW4gZGF0YSB3aXRoIHNwZWNpZmljIHZhcmlhYmxlcyB0aGF0IG1pZ2h0IG5vdCBiZSBhdmFpbGFibGUgb24gdGhvc2Ugc2l0ZXMuIFNvIHRvIHN0YXJ0IHRoaXMgZXhlcmNpc2UsIHdlIHdpbGwgZG93bmxvYWQgYSBcKi5jc3YgZmlsZSBjb250YWluaW5nIGluZm9ybWF0aW9uIGZvciAqT3BoaW9waGFndXMgaGFubmFoKiAoS2luZyBDb2JyYSkgZnJvbSBbbW92ZWJhbmsub3JnXShodHRwczovL3d3dy5tb3ZlYmFuay5vcmcvcGFuZWxfZW1iZWRkZWRfbW92ZWJhbmtfd2ViYXBwP2d3dF9mcmFnbWVudD1wYWdlJTNEc2VhcmNoX21hcF9saW5rZWQlMkNpbmRpdmlkdWFsSWRzJTNENTU2NTc0MzY2JTJCNTU2NTc0Mzc3JTJDbGF0JTNEMTQuNTQ2NDkxOTY4NDY0MTMzJTJDbG9uJTNEMTAxLjk0ODY5Mjc5MjYxNDk1JTJDeiUzRDExKSB0aGF0IGNvbnRhaW5zIHRoZSBhcHByb3ByaWF0ZSB2YXJpYWJsZSB0byB0cmFjayBtb3ZlbWVudCBmcm9tIHJhZGlvIHRyYW5zbWl0dGVyIGRhdGEuIEJlY2F1c2UgdGhpcyBkYXRhIGhhcyB0byBiZSBkb3dubG9hZGVkIHdlIHdpbGwgbmVlZCB0byBnbyB0aHJvdWdoIHRoZSBwcm9jZXNzIG9mIGltcG9ydGluZyB0aGlzIGluZm9ybWF0aW9uIHVzaW5nIHRoZSBgYGByZWFkLmNzdigpYGBgIGNvbW1hbmQuIE9uY2Ugd2UgaGF2ZSBpbXBvcnRlZCB0aGUgZGF0YSB3ZSBjYW4gdmlldyB0aGUgc3RydWN0dXJlLiANCg0KPiBJZiBmb3JrZWQsIHRoaXMgZGF0YSB3aWxsIGJlIGF2YWlsYWJsZSBpbiB0aGUgRGF0YSBmb2xkZXIgd2l0aGluIHRoZSByZXBvc2l0b3J5LiBBZGRpdGlvbmFsbHksIEVTUkkgU2hhcGVmaWxlIGFuZCBHb29nbGUgS01aIGZvcm1hdHMgd2lsbCBiZSBpbmNsdWRlZCBpZiB5b3UgYXJlIGludGVyZXN0ZWQgaW4gd29ya2luZyB3aXRoIHRob3NlIGRhdGEgdHlwZXMuDQoNCjxkZXRhaWxzPjxzdW1tYXJ5PjxiaWc+SW1wb3J0IERhdGFzZXQgd2l0aCBgYGByZWFkLmNzdmBgYDwvYmlnPjwvc3VtbWFyeT4NCmBgYHtyIGRhdGEsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmRhdGEgPC0gcmVhZC5jc3YoIi4vRGF0YS9vcGhpb3BoYWd1c19oYW5uYWguY3N2IikNCmBgYA0KPC9kZXRhaWxzPg0KYGBge3Igc3RydWN0dXJlLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpoZWFkKGRhdGEsIDMpDQpgYGANCg0KTm90aWNlIHRoYXQgdGhpcyBkYXRhc2V0IGNvbnRhaW5zIG1vcmUgdGhhbiBzcGVjaWVzIGluZm9ybWF0aW9uIGFuZCB4LHkgY29vcmRpbmF0ZXMuIFdoaWxlIHRoYXQgaW5mb3JtYXRpb24gaXMgaW1wb3J0YW50LCB0aGlzIGRhdGFzZXQgYWxzbyBjb250YWlucyB0aW1lIGFuZCBkYXRlIGluZm9ybWF0aW9uICh0aW1lc3RhbXApLCBzZW5zb3IgdHlwZSwgYW5kIFVUTSBkYXRhLiBJbiBvcmRlciB0byBiZSBzdXJlIHRoYXQgdGhlIGRhdGFzZXQgY29udGFpbnMgbm8gb3V0bGllcnMsIHdlIGNhbiBwbG90IHRoZSBkYXRhIHVzaW5nIGBgYGdncGxvdGBgYCBhbmQgYGBgcGxvdGx5YGBgIHRvIGludGVyYWN0aXZlbHkgdmlldyB0aGUgZGF0YS4NCg0KYGBge3IgcGxvdGx5LCBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTgsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpxYXFjX3Bsb3QgPC0gZ2dwbG90KCkgKyBnZW9tX3BvaW50KGRhdGE9ZGF0YSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh1dG0uZWFzdGluZyx1dG0ubm9ydGhpbmcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcj1pbmRpdmlkdWFsLmxvY2FsLmlkZW50aWZpZXIpKSArDQogICAgICAgICAgICAgICAgICAgICAgICBsYWJzKHg9IkVhc3RpbmciLCB5PSJOb3J0aGluZyIpICsNCiAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlcyhjb2xvcj1ndWlkZV9sZWdlbmQoIklkZW50aWZpZXIiKSkNCg0KcGxvdGx5OjpnZ3Bsb3RseShxYXFjX3Bsb3QpDQpgYGANCg0KV2l0aCBgYGBwbG90bHlgYGAsIHNpbWlsYXIgdG8gYGBgbGVhZmxldGBgYCwgd2UgaGF2ZSB0aGUgYWJpbGl0eSB0byBleGFtaW5lIHRoZSBzcHJlYWQgb2YgdGhlIGRhdGEgcG9pbnRzIGFuZCBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIGZyb20gdmFyaW91cyBjb2x1bW5zLiBGcm9tIHRoaXMgcGxvdCB3ZSBjYW4gc2VlIHRoYXQgdGhlcmUgYXJlIHR3byBpbmRpdmlkdWFscywgT1BIQTEgYW5kIE9QSEEyLCB0aGF0IHdlcmUgdHJhY2tlZCBmb3IgdGhpcyBzdHVkeSBhbmQgaGF2ZSB2ZXJ5IGxpdHRsZSBvdmVybGFwIGluIHRoZWlyIGFwcGFyZW50IHJhbmdlLg0KDQpXaGlsZSB3ZSBjb3VsZCBjb250aW51ZSB3aXRoIHRoZSBjdXJyZW50IGRhdGFzZXQgdG8gY2FsY3VsYXRlIGhvbWUgcmFuZ2UgZm9yIHRoZSBlbnRpcmUgcG9wdWxhdGlvbiwgdG8gc2ltcGxpZnkgdGhpcyBleGVyY2lzZXMgd2Ugd2lsbCBpbnN0ZWFkIGZvY3VzIG9uIGluZGl2aWR1YWxzLg0KDQo8cCBhbGlnbj0iY2VudGVyIj4NCk1DUCBBbmFseXNpcyBFeGFtcGxlIHwgS0RFIEFuYWx5c2lzIEV4YW1wbGUNCi0gfCAtIA0KIVtdKC4vbWNwX2FsbC5wbmcgIk1DUCBmb3IgYWxsIHBvaW50cyIpIHwgIVtdKC4va2RlX2FsbC5wbmcgIktERSBmb3IgYWxsIHBvaW50cyIpDQo8L3A+DQoNCkRlcGVuZGluZyBvbiB0aGUgbmVlZHMgb2YgeW91ciBhbmFseXNpcywgeW91IG1heSBmaW5kIGEgc2l0dWF0aW9uIHdoZXJlIGEgZGF0YXNldCBiZWluZyBwcm92aWRlZCBieSBhIGNvbGxhYm9yYXRvciBvciB0aHJvdWdoIGEgcmVwb3NpdG9yeSBjb250YWlucyBhIGxhcmdlIG51bWJlciBvZiBpbmRpdmlkdWFscyBiZWluZyByZWNvcmRlZC4gSW4gb3JkZXIgdG8gc2VwYXJhdGUgdGhlc2UgbGFyZ2VyIGRhdGFzZXRzIGludG8gaW5kaXZpZHVhbHMsIHdlIGNhbiBjcmVhdGUgYSBmdW5jdGlvbiB0aGF0IHVzZXMgdGhlIGBgYGxhcHBseWBgYCBjb21tYW5kIHRvIGFwcGx5IGEgZnVuY3Rpb24gb3ZlciBhIGxpc3Qgb3IgdmVjdG9yIGRhdGFzZXQuIFNwZWNpZmljYWxseSwgdGhpcyBmdW5jdGlvbiB3aWxsIHRha2UgdGhlIG9yaWdpbmFsICpkYXRhKnNldCwgc3BsaXQgaXQgaW50byBzZXBhcmF0ZSBmaWxlcyBiYXNlZCBvbiB0aGUgaW5kaXZpZHVhbCBpZGVudGlmaWVyLCBhbmQgY3JlYXRlIG5ldyBcKi5jc3YgZmlsZXMgdXNpbmcgdGhlIGlkZW50aWZpZXIgYXMgdGhlIGZpbGVuYW1lLiANCg0KYGBge3IgbGFwcGx5IGZ1bmN0aW9uLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KbGFwcGx5KHNwbGl0KGRhdGEsIGRhdGEkaW5kaXZpZHVhbC5sb2NhbC5pZGVudGlmaWVyKSwgDQogICAgICAgZnVuY3Rpb24oeCl3cml0ZS5jc3YoeCwgZmlsZSA9IHBhc3RlKHgkaW5kaXZpZHVhbC5sb2NhbC5pZGVudGlmaWVyWzFdLCIuY3N2Iiwgc2VwID0gIiIpLCByb3cubmFtZXMgPSBGQUxTRSkpDQpgYGANCg0KVGhlIGFuYXRvbXkgb2YgdGhlIGZ1bmN0aW9uIGlzIGFzIGZvbGxvd3M6DQoNCi0gYGBgbGFwcGx5KClgYGAsIGFwcGx5IHRoZSBmdW5jdGlvbiBvdmVyIGEgbGlzdA0KLSBgYGBzcGxpdCgpYGBgLCBzZXBhcmF0ZXMgdGhlIGRhdGEgDQotIGBgYGZ1bmN0aW9uKClgYGAsIGNvbXBvc2UgYSBzZXJpZXMgb2Ygc3RlcHMgdG8gYmUgYXBwbGllZCB0byB0aGUgZGF0YQ0KLSBgYGB3cml0ZS5jc3YoKWBgYCwgd3JpdGUgYSBjc3YgZmlsZQ0KLSBgYGBwYXN0ZSgpYGBgLCBjcmVhdGUgYSBjaGFyYWN0ZXIgc3RyaW5nIHRvIGJlIHVzZWQgZm9yIHRoZSBmaWxlIG5hbWUNCg0KSWYgeW91IGV4YW1pbmUgdGhlIHJvb3QgZm9sZGVyIHlvdSB3aWxsIG5vdyBmaW5kIHRoZSBhZGRpdGlvbiBvZiB0d28gbmV3IFwqLmNzdiBmaWxlczogT1BIQTEgYW5kIE9QSEEyLiBXZSB3aWxsIHVzZSB0aGVzZSBmaWxlcyB0byBydW4gb3VyIGhvbWUgcmFuZ2UgYW5hbHlzZXMuIEFsdGVybmF0aXZlbHksIGlmIHlvdSBhcmUgcHJvdmlkZWQgYSBudW1iZXIgb2YgaW5kaXZpZHVhbCBcKi5jc3YgZmlsZXMgYW5kIG5lZWQgdG8gcXVpY2tseSBpbXBvcnQgdGhlIGRhdGEgaW50byBzZXBhcmF0ZSBkYXRhIGZyYW1lcyB5b3UgY291bGQgd3JpdGUgYSAqKmZvciBsb29wKiogdGhhdCB3b3VsZCBsb29rIHNvbWV0aGluZyBsaWtlIHRoaXM6DQoNCmBgYA0KbGlzdCA8LSBnc3ViKCJcXC5jc3YkIiwiIiwgbGlzdC5maWxlcyhwYXR0ZXJuPSJcXC5jc3YkIikpDQoNCmZvcihpIGluIGxpc3Qpew0KICBhc3NpZ24oaSwgcmVhZC5jc3YocGFzdGUoaSwgIi5jc3YiLCBzZXA9IiIpKSkNCn0NCmBgYA0KDQpBbGwgb2YgdGhlIFwqLmNzdiBmaWxlcyBpbiB5b3VyIHJvb3QgZGlyZWN0b3J5IHdpbGwgbm93IGJlIGF2YWlsYWJsZSBpbiB5b3VyIGdsb2JhbCBlbnZpcm9ubWVudC4gSG93ZXZlciwgdGhlIG1ldGhvZCB3ZSB1c2VkIGFib3ZlIGNyZWF0ZWQgaW5kaXZpZHVhbCBcKi5jc3YgZmlsZXMgZm9yIGVhY2ggaW5kaXZpZHVhbHMsIHNvIGluc3RlYWQgd2Ugd2lsbCBtYWtlIGEgbGlzdCBvZiB0aG9zZSBpbmRpdmlkdWFsIGZpbGVzIGFzIHJlZmVyZW5jZSBmb3IgdGhlIGFuYWx5c2VzIHdoZW4gbmVjZXNzYXJ5Lg0KDQpgYGB7ciBsaXN0LCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KZmlsZXMgPC0gbGlzdC5maWxlcyhwYXRoID0gIi4iLCBwYXR0ZXJuID0gIltPUEhBXStbMC05XSsiLCBmdWxsLm5hbWVzID0gVFJVRSkNCmBgYA0KDQpJbiB0aGUgYGBgbGlzdC5maWxlc2BgYCBjb21tYW5kIGFib3ZlLCBgYGBwYXRoID0gIi4iYGBgIGluZm9ybXMgdGhlIGxvY2F0aW9uLCBpbiB0aGlzIGNhc2UgdGhlIHJvb3QgZGlyZWN0b3J5LCBgYGBwYXR0ZXJuID1gYGAgZGVzY3JpYmVzIHRoZSB3YXkgdGhlIGZpbGVzIGFyZSBuYW1lZCwgaW4gdGhpcyBjYXNlIE9QSEEgZm9sbG93ZWQgYnkgYSBudW1iZXIgYmV0d2VlbiAwLTksIGFuZCBgYGBmdWxsLm5hbWVzYGBgIGRlc2NyaWJlcyBob3cgdGhlIGZpbGVzIHdpbGwgYmUgbGlzdGVkLiANCg0KIyBBbmFseXNpcw0KDQpUbyBkYXRlLCB0aGlzIHdpbGwgYmUgb25lIG9mIHRoZSBtb3JlIGNvZGUgaW50ZW5zaXZlIGV4ZXJjaXNlcyB3ZSBoYXZlIGF0dGVtcHRlZC4gV2hpbGUgdGhlIGNvZGUgaXRzZWxmIGlzIG5vdCBkaWZmaWN1bHQsIHRoZSBpbXBsZW1lbnRhdGlvbiBpcyB0ZWRpb3VzIGR1ZSB0byB0aGUgdmFyeWluZyBkYXRhIHR5cGVzIGFuZCBhbmFseXNlcyBwZXJmb3JtZWQuIEFkZGl0aW9uYWxseSwgd2hpbGUgbm90IHJ1biBhcyBhIGNvbXBvbmVudCBvZiB0aGlzIGV4ZXJpY3NlLCBJIGFtIHByb3ZpZGluZyBhIHNjcmlwdCB0aGF0IHdpbGwgd2FsayB5b3UgdGhyb3VnaCB0aGUgc3RlcHMgdG8gYXV0b21hdGUgYW4gYW5hbHlzaXMgZm9yIGEgbnVtYmVyIG9mIGluZGl2aWR1YWxzIHVzaW5nIGEgZnVuY3Rpb24gYW5kIGBgYHBiYXBwbHlgYGAgd2hpY2ggcHJvZHVjZXMgYSBfcHJvZ3Jlc3MgYmFyXyBpbiB0aGUgY29uc29sZSBkZXRhaWxpbmcgdGhlIGxldmVsIG9mIGNvbXBsZXRpb24uIA0KDQojIyBJbWFnZXJ5DQoNCkluIHByZXZpb3VzIGV4ZXJjaXNlcyB3ZSB1c2VkIGEgbnVtYmVyIG9mIGRpZmZlcmVudCBzb3VyY2VzIHRvIG9idGFpbiBpbWFnZXJ5IG9yIGJhc2VtYXBzIGZvciB0aGUgdmFyaW91cyB2aXN1YWxpemF0aW9ucy4gQXMgd2UgaGF2ZSBkaXNjdXNzZWQgd2l0aCBwYWNrYWdlcyBsaWtlIGBgYG9wZW5zdHJlZXRtYXBgYGAgb3IgYGBgZ2dtYXBgYGAgdGhlcmUgY2FuIGJlIHJlc3RyaWN0aW9ucyB0aGF0IGNvbXBsaWNhdGUgdGhlaXIgdXNlZnVsbmVzcy4gQWRkaXRpb25hbGx5LCBzb21lIHNvdXJjZXMgb2YgcmFzdGVyIGltYWdlcnkgY2FuIGJlIGxpbWl0ZWQgaW4gdGhlaXIgZ2VvZ3JhcGhpYyBzY29wZS4gSW4gdGhlIFtwcmV2aW91cyBleGVyY2lzZV0oaHR0cHM6Ly9jaHJpc21nZW50cnkuZ2l0aHViLmlvL0dvb2dsZS1FYXJ0aC1FbmdpbmUvKSB3ZSB1c2VkICoqR29vZ2xlIEVhcnRoIEVuZ2luZSoqIHRvIHByb2R1Y2UgaW1hZ2VzIHRoYXQgY291bGQgYmUgdXNlZCBhcyBiYXNlbWFwcyBmb3Igb3VyIGFuYWx5c2VzLiBTbyBmb3IgdGhpcyBleGVyY2lzZSB3ZSB3aWxsIHVzZSBHRUUgdG8gb2J0YWluIGEgU2VudGluZWwgMiBpbWFnZSBmcm9tIFRoYWlsYW5kIGFuZCB1dGlsaXplIGl0IGFzIHRoZSBiYWNrZ3JvdW5kIGZvciBvdXIgaG9tZXJhbmdlIGFuYWx5c2VzLg0KDQpCZWNhdXNlIHRoaXMgY29kZSBpcyBfamF2YXNjcmlwdF8gSSB3aWxsIHNpbXBseSB1c2UgYSBjb2RlIGJsb2NrIHRvIHByb3ZpZGUgZGV0YWlscyBvbiB0aGUgc2NyaXB0IEkgdXNlZCB0byBsb2NhdGUsIHByZXBhcmUsIGFuZCBkb3dubG9hZCB0aGUgaW1hZ2UuIEZvciBtb3JlIGluZm9ybWF0aW9uIHJlZmVyIHRvIHRoZSBwcmV2aW91cyBleGVyY2lzZSBvciBHRUUgZG9jdW1lbnRhdGlvbi4NCg0KYGBgDQovLyBTZXQgdmFyaWFibGVzIGZvciB0aGUgbG9jYXRpb24gYW5kIGltYWdlcnkgc291cmNlDQp2YXIgdGhhaWxhbmQgPSAvKiBjb2xvcjogI2Q2MzAwMCAqL2VlLkdlb21ldHJ5LlBvaW50KFsxMDEuOTY2NjA1MzkwNjAxNjEsIDE0LjU0NjY0MTEyMjM0NjAyNl0pLA0Kc2VudGluZWwyID0gZWUuSW1hZ2VDb2xsZWN0aW9uKCJDT1BFUk5JQ1VTL1MyX0hBUk1PTklaRUQiKTsNCiANCi8vIERhdGVzIGJlbG93IHVzZWQgYXJlIGNsb3Nlc3QgYXZhaWxhYmxlIHRvIGRhdGEgY29sbGVjdGlvbiBkYXRlDQp2YXIgaW1hZ2UgPSBlZS5JbWFnZUNvbGxlY3Rpb24oc2VudGluZWwyKSAvLyBpZGVudGlmeSB0aGUgc2Vuc29yDQogIC5maWx0ZXJEYXRlKCIyMDE2LTA0LTAxIiwgIjIwMTYtMTAtMzAiKSAvLyBmaWx0ZXIgc3BlY2lmaWMgZGF0ZXMNCiAgLmZpbHRlckJvdW5kcyh0aGFpbGFuZCkgLy8gb25seSBpbmNsdWRlIHNjZW5lcyB0aGF0IGludGVyc2VjdCB3aXRoIG91ciBwb2ludCBnZW9tZXRyeQ0KICAuc29ydCgiQ0xPVURfQ09WRVJBR0VfQVNTRVNTTUVOVCIpIC8vIGZpbHRlciBieSBjbG91ZHkgcGl4ZWwgcGVyY2VudGFnZQ0KICAuZmlyc3QoKTsgLy8gc2VsZWN0IHRoZSBsZWFzdCBjbG91ZHkgc2NlbmUgDQogDQovLyBTZXQgdmlzdWFsaXphdGlvbiBwYXJhbWV0ZXJzLCB0aGVzZSBjYW4gYmUgYWRqdXN0ZWQgd2l0aGluIHRoZSBjb2RlIGVkaXRvcg0KdmFyIG5hdHVyYWxjb2xvciA9IHsNCiAgYmFuZHM6IFsiQjQiLCAiQjMiLCAiQjIiXSwNCiAgbWluOiAwLA0KICBtYXg6IDQwMDAsDQp9Ow0KIA0KLy8gRGlzcGxheSBpbWFnZXMgb24gdGhlIG1hcA0KTWFwLmFkZExheWVyKGltYWdlLCBuYXR1cmFsY29sb3IsICJOYXR1cmFsIENvbG9yIENvbXBvc2l0ZSIpOw0KDQovLyBQcmVwYXJlIHRoZSBpbWFnZSBhbmQgZXhwb3J0IGluIFVUTSA0NzsgcmVtZW1iZXIgdGhlIGRhdGEgaXMgZnJvbSBUaGFpbGFuZA0KdmFyIGV4cG9ydFJHQiA9IGltYWdlLnZpc3VhbGl6ZSh7DQogIGJhbmRzOiBbIkI0IiwgIkIzIiwgIkIyIl0sDQogIG1pbjogMCwNCiAgbWF4OiA0MDAwLA0KfSk7DQoNCi8vIENSUyB3YXMgc2V0IHRvIGFwcHJvcHJpYXRlIEVQU0cgYW5kIG1heFBpeGVscyB3ZXJlIHNldCB0byBhbGxvdyBmb3IgbGFyZ2UgZmlsZSBzaXplIA0KRXhwb3J0LmltYWdlLnRvRHJpdmUoew0KICBpbWFnZTogZXhwb3J0UkdCLA0KICBzY2FsZTogMTAsDQogIGRlc2NyaXB0aW9uOiAidGhhaWxhbmQzMjY0NyIsDQogIGNyczogIkVQU0c6MzI2NDciLA0KICBtYXhQaXhlbHM6IDFlMTMsDQp9KTsNCmBgYA0KDQpFaXRoZXIgc2V0IHRoZSBzYXZlIGxvY2F0aW9uIHRvIHRoZSBwcm9qZWN0IGZvbGRlciwgb3IgbWFubnVhbGx5IGNvcHkgdGhlIGltYWdlcnkgb3ZlciB0byBtYWtlIGl0IGF2YWlsYWJsZSBpbiBSLiBUaGVyZSBpcyBhIGBgYHJnZWVgYGAgcGFja2FnZSB5b3UgY2FuIGV4cGVyaW1lbnQgd2l0aCB0byBpbmNvcnBvcmF0ZSB0aGlzIHN0ZXAgaW50byB3b3JrZmxvdyB3aXRoaW4gUiBob3dldmVyLCB0aGVyZSBpcyBhIGxpdHRsZSBsZWFybmluZyBjdXJ2ZSBkdWUgdG8gdGhlIG1ham9yaXR5IG9mIHRoZSBzY3JpcHQgYmVpbmcgcHl0aG9uLWJhc2VkLiBJbiBvcmRlciB0byBtYWtlIHRoZSByZXN1bHRpbmcgXCouZ2VvVElGRiBmaWxlIHN1aXRhYmxlIGZvciB1c2UgaW4gYGBgZ2dwbG90YGBgLCB3ZSdsbCBuZWVkIHRvIGNyZWF0ZSBhbiBpbWFnZSBzdGFjayBhbmQgY29udmVydCBpdCB0byBhIGRhdGFmcmFtZS4gV2hpbGUgdGhpcyBzdGVwIGlzbid0IGNvbXBsZXRlbHkgbmVjZXNzYXJ5LCBpdCBkb2VzIG1ha2UgdGhlIHN5bnRheCBvZiB1c2luZyB0aGUgaW1hZ2UgaW4gYGBgZ2dwbG90YGBgIG1vcmUgc3RyYWlnaHRmb3J3cmQuIEFkZGl0aW9uYWxseSwgYmVjYXVzZSBpbWFnZSBzd2F0aCBmcm9tIF9TZW50aW5lbCAyXyBpcyAyOTBrbSwgd2Ugd2lsbCBhbHNvIGNyZWF0ZSB0d28gc2l6ZXMgb2YgY3JvcHMgZm9yIHRoZSB2YXJ5aW5nIHNwYXRpYWwgY29uc3RyYWludHMgb2YgdGhlIGRhdGEuIFRoaXMgY2FuIGJlIGFkanVzdGVkIHRocm91Z2hvdXQgdGhlIGFuYWx5c2lzIGFzIG5lZWRlZCB0byBmaXQgdGhlIHNwZWNpZmljIHZpc3VhbGl6YXRpb25zLiANCg0KYGBge3IgaW1hZ2VyeSBwbG90LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTR9DQpvcnRobyA8LSAiLi9EYXRhL3RoYWlsYW5kMzI2NDcudGlmIg0KDQppbWFnZV9zdGFjayA8LSByYXN0ZXI6OnN0YWNrKG9ydGhvKQ0KDQpjcm9wX2V4dGVudCA8LSByYXN0ZXI6OmV4dGVudChtaW4oZGF0YSR1dG0uZWFzdGluZy0yMDAwKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXgoZGF0YSR1dG0uZWFzdGluZysyMDAwKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4oZGF0YSR1dG0ubm9ydGhpbmctMjAwMCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4KGRhdGEkdXRtLm5vcnRoaW5nKzIwMDApKQ0KDQpjcm9wX2Z1bGwgPC0gcmFzdGVyOjpleHRlbnQobWluKGRhdGEkdXRtLmVhc3RpbmctNTAwMCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heChkYXRhJHV0bS5lYXN0aW5nKzUwMDApLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4oZGF0YSR1dG0ubm9ydGhpbmctNTAwMCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heChkYXRhJHV0bS5ub3J0aGluZys1MDAwKSkNCg0KaW1hZ2VfY3JvcCA8LSBjcm9wKGltYWdlX3N0YWNrLCBjcm9wX2V4dGVudCkNCmltYWdlX2Z1bGwgPC0gY3JvcChpbWFnZV9zdGFjaywgY3JvcF9mdWxsKQ0KDQp0aGFpbGFuZCA8LSBhcy5kYXRhLmZyYW1lKGltYWdlX2Nyb3AsIHh5ID0gVFJVRSkgJT4lIHNldE5hbWVzKGMoIngiLCAieSIsICJyZWQiLCAiZ3JlZW4iLCAiYmx1ZSIpKQ0KdGhhaWxhbmQuZnVsbCA8LSBhcy5kYXRhLmZyYW1lKGltYWdlX2Z1bGwsIHh5ID0gVFJVRSkgJT4lIHNldE5hbWVzKGMoIngiLCAieSIsICJyZWQiLCAiZ3JlZW4iLCAiYmx1ZSIpKQ0KDQpnZ3Bsb3QoKStnZW9tX3NwYXRpYWxfcmdiKGRhdGEgPSB0aGFpbGFuZCwgYWVzKHg9eCwgeT15LCByPXJlZCwgZz1ncmVlbiwgYj1ibHVlKSkgKyBjb29yZF9zZigpDQpgYGANCg0KIyMgSG9tZSBSYW5nZSBBbmFseXNpcw0KDQpUaGVyZSBhcmUgdGhyZWUgYmFzaWMgdHlwZXMgb2YgaG9tZSByYW5nZSBhbmFseXNlcyB3ZSB3aWxsIHBlcmZvcm0gaW4gdGhpcyBleGVyY2lzZTogTWluaW11bSBDb252ZXggUG9seWdvbiAoTUNQKSwgS2VybmVsLURlbnNpdHkgRXN0aW1hdGlvbiAoS0RFKSwgYW5kIEJyb3duaWFuIEJyaWRnZSBNb3ZlbWVudCBNb2RlbCAoQkIpLiBUaGVyZSBhcmUgYSBudW1iZXIgb2YgZGlmZmVyZW50IHR1bmluZyBwYXJhbWV0ZXJzIHRoYXQgY2FuIGJlIGFwcGxpZWQgdGhlc2UgYW5hbHlzZXMsIGhvd2V2ZXIgaW4gdGhpcyBleGVyY2lzZSB3ZSB3aWxsIHVzZSB0aGUgbW9zdCBiYXNpYyB2ZXJzaW9ucyBvZiB0aGUgYW5hbHlzaXMuIA0KDQpJbiB0aGUgc2VjdGlvbiBhYm92ZSB3ZSB1c2UgdGhlIGBgYGxhcHBseWBgYCBjb21tYW5kIHRvIGxvb3AgYSBmdW5jdGlvbiB1c2VkIHRvIHNlcGFyYXRlIHRoZSBvcmlnaW5hbCBkYXRhc2V0IGludG8gaW5kaXZpZHVhbCBmaWxlcy4gVGhpcyBpcyBhIHVzZWZ1bCB0b29sLCBob3dldmVyLCB3aGVuIHRoZSBmdW5jdGlvbiBsb29wcyB0aHJvdWdoIGRvemVucyBvciBldmVuIGh1bmRyZWRzIG9mIGZpbGVzLCB0aGUgcHJvY2VzcyBjYW4gdGFrZSBhIGxvbmcgcGVyaW9kIG9mIHRpbWUgdG8gY29tcGxldGUuIFVzaW5nIHRoZSBgYGBwYmxhcHBseWBgYCBjb21tYW5kIGFkZHMgYSBwcm9ncmVzcyBiYXIgKGkuZS4gKipwYioqKSB0byB0aGUgcHJvY2VzcyB3aGljaCBwcm92aWRlcyBhbiBlc3RpbWF0ZWQgdGltZSBmb3IgY29tcGxldGlvbiBvZiB0aGUgZnVuY3Rpb24uIFdlIHdpbGwgdXNlIGEgc2ltaWxhciBwcm9jZXNzIHRvIHRoZSBvbmUgYWJvdmUsIHVzaW5nIHRoZSBgYGBwYmxhcHBseWBgYCBjb21tYW5kIHRvIHJ1biBNQ1AgYW5hbHlzaXMgb24gdGhlIGluZGl2aWR1YWwgXCouY3N2IGZpbGVzLiANCg0KIyMjIE1pbmltdW0gQ29udmV4IFBvbHlnb24NCg0KT25lIG9mIHRoZSBtb3N0IHNpbXBsZSBob21lcmFuZ2UgYW5hbHlzZXMgaXMgYSBfbWluaW11bSBjb252ZXggcG9seWdvbl8gb3IgKiptY3AqKi4gQSBtY3AgZHJhd3MgYSBwb2x5Z29uIHdoaWNoIHJlcHJlc2VudHMgYSBzcGVjaWZpZWQgbWluaW11bSBib3VuZGluZyBnZW9tZXRyeSBlbmNsb3NpbmcgdGhlIGRhdGEgcG9pbnRzLiBUaGlzIGVzc2VudGlhbGx5IGNvbm5lY3RzIHRoZSBwb2ludHMgYWxvbmcgdGhlIGZhcnRoZXN0IGdlb2dyYXBoaWMgcG9pbnRzIGVuY2xvc2luZyB0aGVtIGFsbCBpbiBhIHNpbmdsZSBzaGFwZS4gRm9yIHRoaXMgYW5hbHlzaXMgd2Ugd2lsbCBmb2N1cyBvbiB0aGUgT1BIQTEgZmlsZSB3ZSBzZXBhcmF0ZWQgZnJvbSB0aGUgbGFyZ2VyIGRhdGFzZXQgdXNpbmcgdGhlIGBgYGFkZWhhYml0YXRIUmBgYCBwYWNrYWdlLg0KDQpBIGRlc2NyaXB0aW9uIG9mIHRoZSBzdGVwcyB3aXRoaW4gdGhlIGZvbGxvd2luZyBjb2RlIHdpbGwgYmUgZGlzY3Vzc2VkIGZvbGxvd2luZyB0aGUgb3V0cHV0LiBUaGUgcHJvY2VzcyB3b3JrcyBieSBlc3RhYmxpc2hpbmcgdGhlIGZ1bmN0aW9uIGFuZCB0aGVuIHJ1bm5pbmcgYGBgcGJsYXBwbHlgYGAgcmVmZXJyaW5nIGJhY2sgdG8gdGhlICoqZmlsZXMqKiB3ZSBjcmVhdGVkIGluIHRoZSBkYXRhc2V0IHBvcnRpb24gb2YgdGhpcyBleGVyY2lzZS4NCg0KYGBge3IgTUNQIHBsb3QsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG89VFJVRSwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9Nn0NCiMjIERhdGEgZm9yIGEgc2luZ2xlIGluZGl2aWR1YWwNCm9waGExIDwtIHJlYWQuY3N2KCdPUEhBMS5jc3YnKQ0KeHkgPC0gYyhhcy5kYXRhLmZyYW1lKG9waGExJHV0bS5lYXN0aW5nKSxhcy5kYXRhLmZyYW1lKG9waGExJHV0bS5ub3J0aGluZykpDQoNCiMjIHByb2plY3RpbmcgdGhlIGRhdGEgdG8gVVRNDQpkYXRhLnByb2ogPC0gU3BhdGlhbFBvaW50c0RhdGFGcmFtZSh4eSxvcGhhMSwgcHJvajRzdHJpbmcgPSBDUlMoIitwcm9qPXV0bSArem9uZT00NyArZGF0dW09V0dTODQgK3VuaXRzPW0gK25vX2RlZnMiKSkNCnh5LnNwIDwtIFNwYXRpYWxQb2ludHMoZGF0YS5wcm9qQGNvb3JkcykNCg0KIyMgbWNwIGFuYWx5c2lzDQptY3Aub3V0IDwtIG1jcCh4eS5zcCwgcGVyY2VudD0xMDAsIHVub3V0PSJoYSIpDQoNCiMjIGNvbnZlcnRpbmcgbWNwIHRvIHNmIG9iamVjdCBmb3IgZ2dwbG90IGFuZCBwb2ludHMgdG8gZGYNCm1jcC5zZiA8LSBzdF9hc19zZihtY3Aub3V0KQ0KbWNwLnBvaW50cyA8LSBjYmluZCgoZGF0YS5mcmFtZSh4eSkpLG9waGExJGluZGl2aWR1YWwubG9jYWwuaWRlbnRpZmllcikNCmNvbG5hbWVzKG1jcC5wb2ludHMpIDwtIGMoIngiLCJ5IiwgImlkZW50aWZpZXIiKQ0KDQojIE1DUCBwbG90DQptY3AucGxvdCA8LSBnZ3Bsb3QoKSArIHRoZW1lX2J3KCkgKw0KICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDAuNSwgdmp1c3QgPSAwLjUpKSArIA0KICB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpKSArDQogIGdlb21fc3BhdGlhbF9yZ2IoZGF0YSA9IHRoYWlsYW5kLCBhZXMoeD14LCB5PXksIHI9cmVkLCBnPWdyZWVuLCBiPWJsdWUpKSArDQogIGdlb21fc2YoZGF0YT1tY3Auc2YsIGFscGhhID0gMC4xKSArDQogIGdlb21fcG9pbnQoZGF0YT1tY3AucG9pbnRzLCBhZXMoeD14LCB5PXkpLCBjb2xvciA9ICJsaWdodGdyZWVuIikgKyANCiAgbGFicyh4PSJFYXN0aW5nIChtKSIsIHk9Ik5vcnRoaW5nIChtKSIsIHRpdGxlPW1jcC5wb2ludHMkaWRlbnRpZmllcikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KSkgKw0KICBjb29yZF9zZih4bGltID0gYyg4MjAwMDAsIDgxNDI1MCksICB5bGltID0gYygxNjA0NTAwLCAxNjA4MDAwKSkNCm1jcC5wbG90ICsgYW5ub3RhdGUoInRleHQiLCB4ID0gODE1MDAwLCB5ID0gMTYwNzUwMCwgbGFiZWwgPSBwYXN0ZShyb3VuZChtY3Aub3V0QGRhdGEkYXJlYSwyKSwiaGEiKSwgDQogICAgICAgICAgICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiLCBzaXplID0gNSwgY29sb3IgPSAid2hpdGUiKQ0KYGBgDQoNClRoZSBhbmF0b215IG9mIHRoZSBzY3JpcHQgYWJvdmUgaXMgYXMgZm9sbG93czoNCg0KLSBgYGByZWFkLmNzdigpYGBgLCByZWFkIHRoZSBkYXRhIGZyb20gYSBnaXZlbiBcKi5jc3YgZmlsZQ0KLSB4eSwgY3JlYXRlIGEgY29vcmRpbmF0ZSBmaWxlIG9mIHRoZSBlYXN0aW5nIGFuZCBub3J0aGluZyBkYXRhDQotIGBgYFNwYXRpYWxQb2ludHNEYXRhRnJhbWUoKWBgYCwgcHJvamVjdCB0aGUgZGF0YSB0byBVVE0gWm9uZSA0Nw0KLSBgYGBtY3AoKWBgYCwgY29tcHV0ZXMgaG9tZSByYW5nZSB1c2luZyB0aGUgTWluaW11bSBDb252ZXggUG9seWdvbiBlc3RpbWF0b3INCi0gYGBgc3RfYXNfc2ZgYGAsIGNvbnZlcnRzIHRvIGEgZGF0YSBmcmFtZSBmb3IgcGxvdHRpbmcgd2l0aCBnZ3Bsb3QyDQotIGBgYGNvbG5hbWVzKClgYGAsIHJlbmFtZXMgdGhlIGNvbHVtbnMNCi0gYGBgYW5ub3RhdGVgYGAsIGFkZHMgdGV4dCB0byB0aGUgcGxvdA0KDQpUaGUgcmVzdCBvZiB0aGUgaW5mb3JtYXRpb24gY2FuIGJlIGZvdW5kIGJ5IGV4YW1pbmluZyB0aGUgaGVscCBtZW51IGZvciB0aGUgdmFyaW91cyBjb21tYW5kcyBvciBsb29raW5nIGF0IHRoZSBkaXNwbGF5IG9wdGlvbnMgZm9yIGVhY2ggcG9ydGlvbiBvZiB0aGUgc2NyaXB0Lg0KDQojIyMgS2VybmVsLURlbnNpdHkgRXN0aW1hdGlvbg0KDQpUaGUgc2FtZSBkZXNpZ24gYWJvdmUgY2FuIGJlIHVzZWQgdG8gY3JlYXRlIEtERSBwbG90cyBmb3IgZWFjaCBpbmRpdmlkdWFsIGluIHRoZSBkYXRhc2V0LiBCZWNhdXNlIHRoZSBwcm9jZXNzIGlzIGVzc2VudGlhbGx5IHRoZSBzYW1lLCBvbmx5IHBvcnRpb25zIG9mIHRoZSBzY3JpcHQgdGhhdCB3ZXJlIG5vdCBwcmV2aW91c2x5IGRlc2NyaWJlZCB3aWxsIGJlIGRpc2N1c3NlZC4NCg0KYGBge3IgS0RFIHBsb3QsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG89VFJVRSwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9Nn0NCiMjIERhdGEgZm9yIGEgc2luZ2xlIGluZGl2aWR1YWwNCm9waGEyIDwtIHJlYWQuY3N2KCdPUEhBMi5jc3YnKQ0KeCA8LSBhcy5kYXRhLmZyYW1lKG9waGEyJHV0bS5lYXN0aW5nKQ0KeSA8LSBhcy5kYXRhLmZyYW1lKG9waGEyJHV0bS5ub3J0aGluZykNCnh5IDwtIGMoeCx5KQ0KDQojIyBwcm9qZWN0aW5nIHRoZSBkYXRhIHRvIFVUTQ0KZGF0YS5wcm9qIDwtIFNwYXRpYWxQb2ludHNEYXRhRnJhbWUoeHksb3BoYTIsIHByb2o0c3RyaW5nID0gQ1JTKCIrcHJvaj11dG0gK3pvbmU9NDcgK2RhdHVtPVdHUzg0ICt1bml0cz1tICtub19kZWZzIikpDQp4eS5zcCA8LSBTcGF0aWFsUG9pbnRzKGRhdGEucHJvakBjb29yZHMpDQoNCiMjIEtERSBhbmFseXNpcyBhdCA5NSwgODUsIDc1DQprZGU8LWtlcm5lbFVEKHh5LnNwLCBoPSJocmVmIiwga2Vybj0iYml2bm9ybSIsIGdyaWQ9MTAwKQ0KdmVyOTUgPC0gZ2V0dmVydGljZXNocihrZGUsIDk1KQ0KdmVyODUgPC0gZ2V0dmVydGljZXNocihrZGUsIDg1KQ0KdmVyNzUgPC0gZ2V0dmVydGljZXNocihrZGUsIDc1KQ0KDQojIyBjb252ZXJ0aW5nIHRvIHNmIG9iamVjdCBhbmQgZGYNCmtkZS5wb2ludHMgPC0gY2JpbmQoKGRhdGEuZnJhbWUoZGF0YS5wcm9qQGNvb3JkcykpLG9waGEyJGluZGl2aWR1YWwubG9jYWwuaWRlbnRpZmllcikNCmNvbG5hbWVzKGtkZS5wb2ludHMpIDwtIGMoIngiLCJ5IiwiaWRlbnRpZmllciIpDQprZGU5NS5zZiA8LSBzdF9hc19zZih2ZXI5NSkNCmtkZTg1LnNmIDwtIHN0X2FzX3NmKHZlcjg1KQ0Ka2RlNzUuc2YgPC0gc3RfYXNfc2YodmVyNzUpDQoNCiMjbWFraW5nIHRoZSBwbG90DQprZGUucGxvdCA8LSBnZ3Bsb3QoKSArIHRoZW1lX2J3KCkgKw0KICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDAuNSwgdmp1c3QgPSAwLjUpKSArIA0KICBnZW9tX3NwYXRpYWxfcmdiKGRhdGEgPSB0aGFpbGFuZCwgYWVzKHg9eCwgeT15LCByPXJlZCwgZz1ncmVlbiwgYj1ibHVlKSkgKw0KICBnZW9tX3NmKGRhdGE9a2RlOTUuc2YsIGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC40KSArDQogIGdlb21fc2YoZGF0YT1rZGU4NS5zZiwgZmlsbCA9ICJncmVlbiIsIGFscGhhID0gMC4zKSArDQogIGdlb21fc2YoZGF0YT1rZGU3NS5zZiwgZmlsbCA9ICJ5ZWxsb3ciLCBhbHBoYSA9IDAuMikgKw0KICBnZW9tX3BvaW50KGRhdGE9a2RlLnBvaW50cywgYWVzKHg9eCwgeT15KSwgY29sb3IgPSAibGlnaHRncmF5IikgKyANCiAgbGFicyh4PSJFYXN0aW5nIChtKSIsIHk9Ik5vcnRoaW5nIChtKSIsIHRpdGxlPWtkZS5wb2ludHMkaWRlbnRpZmllcikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KSkgKw0KICBjb29yZF9zZih4bGltID0gYyg4MjE5MDAsIDgxNzAwMCksICB5bGltID0gYygxNjA1MDAwLCAxNjE1MDAwKSkNCmtkZS5wbG90ICsgYW5ub3RhdGUoInRleHQiLCB4ID0gODE4MDAwLCB5ID0gMTYxNDUwMCwgbGFiZWwgPSBwYXN0ZSgiOTUlICIscm91bmQodmVyOTUkYXJlYSwyKSwiaGEiKSwgDQogICAgICAgICAgICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiLCBzaXplID0gNCwgY29sb3IgPSAid2hpdGUiKQ0KYGBgDQoNClRoZSBhbmF0b215IG9mIHRoZSBmdW5jdGlvbiBhYm92ZSBpcyBhcyBmb2xsb3dzOg0KDQotIGBgYGtlcm5lbFVEKClgYGAsIGVzdGltYXRpb24gb2Yga2VybmVsIGhvbWUtcmFuZ2UNCi0gYGBgZ2V0dmVydGljZXNocigpYGBgLCBleHRyYWN0IGhvbWUtcmFuZ2UgY29udG91cg0KDQpUaGUgcmVzdCBvZiB0aGUgaW5mb3JtYXRpb24gY2FuIGJlIGZvdW5kIGJ5IGV4YW1pbmluZyB0aGUgaGVscCBtZW51IGZvciB0aGUgdmFyaW91cyBjb21tYW5kcyBvciBsb29raW5nIGF0IHRoZSBkaXNwbGF5IG9wdGlvbnMgZm9yIGVhY2ggcG9ydGlvbiBvZiB0aGUgc2NyaXB0Lg0KDQojIyMgQXV0b21hdGlvbiBVc2luZyBGdW5jdGlvbnMNCg0KRGVwZW5kaW5nIG9uIHlvdXIgZGVzaXJlZCByZXN1bHRzLCB0aGUgc3RlcHMgYWJvdmUgY2FuIGJlIGluY2x1ZGVkIGluIGEgZnVuY3Rpb24gdGhhdCB3aWxsIGFsbG93IHlvdSB0byBhdXRvbWF0ZSB0aGlzIHByb2Nlc3Mgb3ZlciBhIGxpc3Qgb2YgZmlsZXMgYW5kIGNyZWF0ZSBfbWNwXyBvciBfa2RlXyBhbmFseXNlcyBmb3IgYWxsIHRoZSBpbmRpdmlkdWFscyBzZXBhcmF0ZWx5LiBCYXNpY2FsbHksIHdlIGFyZSB0YWtpbmcgYWxsIG9mIHRoZSBlbGVtZW50cyBvZiB0aGUgc2NyaXB0cyBhYm92ZSwgbWFraW5nIHNvbWUgb2YgdGhlIHZhbHVlcyBnZW5lcmljIChlLmcuIGZpbGVuYW1lKSBhbmQgY3JlYXRpbmcgdGhlIHN0ZXBzIGZvciB0aGUgcHJvY2VzcyB0aGF0IGNhbiBiZSByZXBlYXRlZCBvdmVyIGEgbGlzdCBvZiBmaWxlcyB0aGF0IHlvdSBjcmVhdGVkIGVhcmxpZXIgaW4gdGhlIGV4ZXJjaXNlcy4gVGhlIG9iamVjdCBjYWxsZWQgYGBgZmlsZXNgYGAgY29udGFpbnMgYSBsaXN0IG9mIGFsbCB0aGUgXCouY3N2IGZpbGVzIGZvciBlYWNoIGluZGl2aWR1YWwuIFNvIHdlIHdpbGwgYXBwbHkgdGhlIGZ1bmN0aW9uLCBvdmVyIHRoZSBsaXN0LCB0byBhdXRvbWF0ZSB0aGUgYW5hbHlzaXMgYW5kIHZpc3VhbGl6YXRpb25zIGZvciBlYWNoIGluZGl2aWR1YWwuIEJlY2F1c2UgdGhlIF9rZGVfIHByb2Nlc3MgaXMgZXNzZW50aWFsbHkgdGhlIHNhbWUgYXMgYWJvdmUsIG9ubHkgcG9ydGlvbnMgb2YgdGhlIGZ1bmN0aW9uIHRoYXQgd2VyZSBub3QgcHJldmlvdXNseSBkZXNjcmliZWQgd2lsbCBiZSBkaXNjdXNzZWQuDQoNCmBgYHtyIGtkZSBwbG90IGF1dG9tYXRpb24sIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG89VFJVRSwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9Nn0NCiNydW4gS0RFIGNyZWF0aW5nIHBsb3QgZm9yIGVhY2ggaW5kaXZpZHVhbCB3aXRoIGEgcHJvZ3Jlc3MgYmFyDQprZGVfcmFzdGVyIDwtIGZ1bmN0aW9uKGZpbGVuYW1lKXsNCiAgZGF0YSA8LSByZWFkLmNzdihmaWxlID0gZmlsZW5hbWUpDQogIHggPC0gYXMuZGF0YS5mcmFtZShkYXRhJHV0bS5lYXN0aW5nKQ0KICB5IDwtIGFzLmRhdGEuZnJhbWUoZGF0YSR1dG0ubm9ydGhpbmcpDQogIHh5IDwtIGMoeCx5KQ0KICBkYXRhLnByb2ogPC0gU3BhdGlhbFBvaW50c0RhdGFGcmFtZSh4eSxkYXRhLCBwcm9qNHN0cmluZyA9IENSUygiK3Byb2o9dXRtICt6b25lPTQ3ICtkYXR1bT1XR1M4NCArdW5pdHM9bSArbm9fZGVmcyIpKQ0KICB4eS5zcCA8LSBTcGF0aWFsUG9pbnRzKGRhdGEucHJvakBjb29yZHMpDQogIGtkZTwta2VybmVsVUQoeHkuc3AsIGg9ImhyZWYiLCBrZXJuPSJiaXZub3JtIiwgZ3JpZD0xMDApDQogIHZlcjk1IDwtIGdldHZlcnRpY2VzaHIoa2RlLCA5NSkNCiAgdmVyODUgPC0gZ2V0dmVydGljZXNocihrZGUsIDg1KQ0KICB2ZXI3NSA8LSBnZXR2ZXJ0aWNlc2hyKGtkZSwgNzUpDQogIGtkZS5wb2ludHMgPC0gY2JpbmQoKGRhdGEuZnJhbWUoeHkpKSxkYXRhJGluZGl2aWR1YWwubG9jYWwuaWRlbnRpZmllcikNCiAgY29sbmFtZXMoa2RlLnBvaW50cykgPC0gYygieCIsInkiLCJpZGVudGlmaWVyIikNCiAga2RlOTUuc2YgPC0gc3RfYXNfc2YodmVyOTUpDQogIGtkZTg1LnNmIDwtIHN0X2FzX3NmKHZlcjg1KQ0KICBrZGU3NS5zZiA8LSBzdF9hc19zZih2ZXI3NSkNCiAga2RlLnBsb3QgPC0gZ2dwbG90KCkgKyB0aGVtZV9idygpICsgDQogICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAwLjUsIHZqdXN0ID0gMC41KSkgKyANCiAgICBnZW9tX3NwYXRpYWxfcmdiKGRhdGEgPSB0aGFpbGFuZCwgYWVzKHg9eCwgeT15LCByPXJlZCwgZz1ncmVlbiwgYj1ibHVlKSkgKw0KICAgIGdlb21fc2YoZGF0YT1rZGU5NS5zZiwgZmlsbCA9ICJibHVlIiwgYWxwaGEgPSAwLjQpICsNCiAgICBnZW9tX3NmKGRhdGE9a2RlODUuc2YsIGZpbGwgPSAiZ3JlZW4iLCBhbHBoYSA9IDAuMykgKw0KICAgIGdlb21fc2YoZGF0YT1rZGU3NS5zZiwgZmlsbCA9ICJ5ZWxsb3ciLCBhbHBoYSA9IDAuMikgKw0KICAgIGdlb21fcG9pbnQoZGF0YT1rZGUucG9pbnRzLCBhZXMoeD14LCB5PXkpLCBjb2xvciA9ICJsaWdodGdyYXkiKSArIA0KICAgIGNvb3JkX3NmKHhsaW0gPSBjKDgyMjAwMCwgODEzOTUwKSwgIHlsaW0gPSBjKDE2MTYyNTAsIDE2MDQ1MDApKSArDQogICAgbGFicyh4PSJFYXN0aW5nIChtKSIsIHk9Ik5vcnRoaW5nIChtKSIsIHRpdGxlPWtkZS5wb2ludHMkaWRlbnRpZmllcikgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwLjUpKQ0KICBrZGUucGxvdCArIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDgxNTAwMCwgeSA9IDE2MTYwMDAsIGxhYmVsID0gcGFzdGUoIjk1JSAiLHJvdW5kKHZlcjk1JGFyZWEsMiksIiBoYSIpLCANCiAgICAgICAgICAgICAgICAgICAgICBmb250ZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDUsIGNvbG9yID0gIndoaXRlIikNCn0NCg0KcGJsYXBwbHkoZmlsZXMsIGtkZV9yYXN0ZXIpDQpgYGANCjxwIGFsaWduPSJjZW50ZXIiPg0KS0RFIEFuYWx5c2lzIE9QSEExIHwgS0RFIEFuYWx5c2lzIE9QSEEyDQotIHwgLSANCiFbXSguL29waGExX2F1dG8ucG5nICJLREUgZm9yIE9QSEExIikgfCAhW10oLi9vcGhhMl9hdXRvLnBuZyAiS0RFIGZvciBPUEhBMiIpDQo8L3A+DQoNClRoZSBhbmF0b215IG9mIHRoZSBzY3JpcHQgYWJvdmUgaXMgYXMgZm9sbG93czogdGhlIGBgYGtkZV9yYXN0ZXIgPC0gZnVuY3Rpb24oZmlsZW5hbWUpey4uLmBgYCBmdW5jdGlvbiBydW5zIGZvciBlYWNoIGZpbGUgbGlzdGVkIGZyb20gdGhlIHByb2plY3QgZGlyZWN0b3J5IGluIHRoZSBgYGBmaWxlc2BgYCBsaXN0IGNvbXBsZXRlIHRoZSBmb2xsb3dpbmcgY29tbWFuZHMNCg0KLSBgYGByZWFkLmNzdihmaWxlID0gZmlsZW5hbWUpYGBgLCB0aGVyZSBpcyBubyBzcGVjaWZpYyBmaWxlIG5hbWUgbmVlZGVkIGFzIGl0IHdpbGwgdXNlIGVhY2ggXCouY3N2IGluIHRoZSBsaXN0IGFzIGEgbmV3IGZpbGUNCi0gYGBgcGJhcHBseSgpYGBgLCBydW5zIHRoZSBmdW5jdGlvbiBgYGBrZGVfcmFzdGVyYGBgIG92ZXIgdGhlIGxpc3Qgb2YgXCouY3N2IGluIHRoZSBmaWxlcyBvYmplY3QNCg0KVGhlIHJlc3Qgb2YgdGhlIGluZm9ybWF0aW9uIGNhbiBiZSBmb3VuZCBieSBleGFtaW5pbmcgdGhlIGhlbHAgbWVudSBmb3IgdGhlIHZhcmlvdXMgY29tbWFuZHMgb3IgbG9va2luZyBhdCB0aGUgZGlzcGxheSBvcHRpb25zIGZvciBlYWNoIHBvcnRpb24gb2YgdGhlIHNjcmlwdC4NCg0KPiBUcnkgaXQgeW91cnNlbGYhIFVzaW5nIHRoZSBleGFtcGxlIHNjcmlwdCBhYm92ZSwgYW5kIGZvbGxvd2luZyB0aGUgZXhhbXBsZSBmdW5jdGlvbnMsIGNyZWF0ZSBhIGxvb3BpbmcgZnVuY3Rpb24gd2l0aCBgYGBwYmxhcHBseWBgYCB0byBydW4gdGhlIG1jcCBhbmFseXNpcyBmb3IgZWFjaCBpbmRpdmlkdWFsIGluIHRoZSBmaWxlcyBsaXN0Lg0KDQojIyBDb250aW51b3VzLVRpbWUgTW92ZW1lbnQgTW9kZWxpbmcNCg0KVGhlcmUgaXMgYSBtb3JlIHBvd2VyZnVsIHBhY2thZ2UgY2FsbGVkIGBgYGN0bW1gYGAgdGhhdCBjYW4gYmUgdXNlZCBmb3IgdGhlIGNyZWF0aW9uIG9mICBjb252ZW50aW9uYWwga2VybmVsLWRlbnNpdHkgZXN0aW1hdGVzIGFzIHdlbGwgYXMgdW5pZm9ybWx5IHdlaWdodGVkIGtlcm5lbC1kZW5zaXR5IGVzdGltYXRlcy4gV2hpbGUgSSBhbSBsZXNzIGZhbWlsaWFyIHdpdGggdGhlIHZhcmlhYmxlcywgb2JqZWN0cywgYW5kIHNldHRpbmcgZm9yIGVhY2ggbW9kZWwsIEkgd2FzIGFibGUgdG8gZm9sbG93IGFsb25nIHdpdGggdGhlIFt2aWduZXR0ZV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2N0bW0vdmlnbmV0dGVzL2FrZGUuaHRtbCkgZm9yIEF1dG9jb3JyZWxhdGVkIEtlcm5lbCBEZW5zaXR5IEVzdGltYXRpb24gb24gdGhlIFtwYWNrYWdlIGRldGFpbHNdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9jdG1tLykgdG8gY3JlYXRlIGEgc2ltaWxhciBfa2RlXyBhbmFseXNpcyBhcyBhYm92ZS4gQWxsIG9mIHRoZSBzYW1lIG1ldGhvZHMgYXV0b21hdGlvbiBtZXRob2RzIGNhbiBiZSBlbXBsb3llZCwgaG93ZXZlciwgdGhlcmUgYXJlIHNvbWUgZGlmZmVyZW5jZSB0byBub3RlLg0KDQpGb3IgdGhpcyBwb3J0aW9uIG9mIHRoZSBleGVyY2lzZXMgd2Ugd2lsbCBwcmVwYXJlIHRoZSB0d28gT1BIQSBcKi5jc3YgZmlsZXMgZm9yIGFuYWx5c2lzIHVzaW5nIGBgYGFrZGVgYGAgaW4gdGhlIGBgYGN0bW1gYCBwYWNrYWdlLiANCg0KYGBge3IgY3RtbSB0ZWxlbWV0cnksIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG89VFJVRSwgcmVzdWx0cz0naGlkZSd9DQojIyBjb252ZXJ0IG1vdmViYW5rIHRvIHRlbGVtZXRyeSBkYXRhDQpvcGhhLnRlbGVtIDwtIGFzLnRlbGVtZXRyeShkYXRhLCBwcm9qZWN0aW9uID0gIitwcm9qPXV0bSArem9uZT00NyArZGF0dW09V0dTODQgK3VuaXRzPW0gK25vX2RlZnMiKQ0Kb3BoYTEudGVsZW0gPC0gYXMudGVsZW1ldHJ5KCdPUEhBMS5jc3YnLCBwcm9qZWN0aW9uID0gIitwcm9qPXV0bSArem9uZT00NyArZGF0dW09V0dTODQgK3VuaXRzPW0gK25vX2RlZnMiKQ0Kb3BoYTIudGVsZW0gPC0gYXMudGVsZW1ldHJ5KCJPUEhBMi5jc3YiLCBwcm9qZWN0aW9uID0gIitwcm9qPXV0bSArem9uZT00NyArZGF0dW09V0dTODQgK3VuaXRzPW0gK25vX2RlZnMiKQ0KYGBgDQoNCllvdSBjYW4gZXhhbWluZSB0aGUgZGF0YSBpbiB0aGUgZW52aXJvbm1lbnQgdG8gc2VlIGhvdyB0aGUgZGF0YSB3YXMgcmVzdHJ1Y3R1cmVkIHRvIGJlIGF2YWlsYWJsZSBmb3IgZnVydGhlciBhbmFseXNpcy4gTm90aWNlIHRoYXQgYSBwcm9qZWN0aW9uIHR5cGUgaXMgbGlzdGVkIGZvciBlYWNoIG9iamVjdC4gVGhpcyB3YXMgdXNlZCB0byBlbnN1cmUgdGhlIGRhdGEgd291bGQgYmUgZ2VvZ3JhcGhpY2FsbHkgbG9jYXRlZCByYXRoZXIgdGhhbiBwbGFjZWQgaW4gZ2VuZXJhbCBzcGFjZSBieSBtZXRlcnMuIA0KDQpgYGB7ciBjdG1tIGFrZGUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG89VFJVRSwgcmVzdWx0cz0naGlkZSd9DQojIGNhbGN1bGF0ZSBmaXQgb2JqZWN0DQpvcGhhMi5ndWVzcyA8LSBjdG1tLmd1ZXNzKG9waGEyLnRlbGVtLCBpbnRlcmFjdGl2ZT1GQUxTRSkNCg0KIyBjb21wdXRlIGFrZGUgYW5kIGdldCBzdW1tYXJ5IGluZm9ybWF0aW9uDQpvcGhhMi5vdWYgPC0gY3RtbS5maXQob3BoYTIudGVsZW0sIG9waGEyLmd1ZXNzKQ0Kd0FLREUub3BoYTIgPC0gYWtkZShvcGhhMi50ZWxlbSwgb3BoYTIub3VmLCB3ZWlnaHRzPVRSVUUpDQpzdW1tYXJ5KHdBS0RFLm9waGEyKQ0KDQojIGJhc2ljIHBsb3QNCnBsb3Qob3BoYTIudGVsZW0sIFVEPXdBS0RFLm9waGEyKQ0KYGBgDQoNCllvdSBjYW4gc2VlIHRoZSBkZXRhaWxzIG9mIGVhY2ggb2YgdGhlIG9iamVjdCBjcmVhdGVkIGluIHlvdXIgZW52aXJvbm1lbnQuIFRoZSBhbmFseXNpcyBjcmVhdGUgYSBjbGFzcyBgYGBVRGBgYCBvYmplY3QgdGhhdCBjYW5ub3QgYmUgdXNlZCBpbiBgYGBnZ3Bsb3RgYGAgd2l0aG91dCBjb252ZXJzaW9uLiBBZGRpdGlvbmFsbHksIHdlIHdpbGwgY3JlYXRlIGFuIG9iamVjdCBzcGVjaWZpY2FsbHkgdG8gdXNlIGZvciB0aGUgaW5jbHVzaW9uIG9mIGFyZWEgb24gdGhlIGZpbmFsIHBsb3QuDQoNCmBgYHtyIGFrZGUgcGxvdCwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjYWNoZT1UUlVFfQ0KIyBjb252ZXJ0IHRvIFNQREYgdG8gY29udmVydCB0byBzZiBvYmplY3QgZm9yIHBsb3R0aW5nIGluIGdncGxvdA0KdWQuc3AgPC0gU3BhdGlhbFBvbHlnb25zRGF0YUZyYW1lLlVEKHdBS0RFLm9waGEyKQ0KdWQuc2YgPC0gc3RfYXNfc2YodWQuc3ApDQoNCm9waGEyLnNmIDwtIHN0X2FzX3NmKG9waGEyLCBjb29yZHMgPSBjKCJ1dG0uZWFzdGluZyIsInV0bS5ub3J0aGluZyIpKSAlPiUgc3Rfc2V0X2NycygzMjY0NykNCg0KIyBhZGtlIHN1bW1hcnkgZGF0YQ0KYWtkZS5zdW1tYXJ5IDwtIHN1bW1hcnkod0FLREUub3BoYTIpDQpha2RlLnN1bW1hcnlbWyJDSSJdXVszXQ0KDQojIGdncGxvdCBmb3IgYWtkZQ0KY3RtbS5ha2RlIDwtIGdncGxvdCgpICsgZ2VvbV9zcGF0aWFsX3JnYihkYXRhID0gdGhhaWxhbmQuZnVsbCwgYWVzKHg9eCwgeT15LCByPXJlZCwgZz1ncmVlbiwgYj1ibHVlKSkgKw0KICBnZW9tX3NmKGRhdGEgPSB1ZC5zZlszLF0sIGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC40KSArIA0KICBnZW9tX3NmKGRhdGEgPSB1ZC5zZlsyLF0sIGZpbGwgPSAiZ3JlZW4iLCBhbHBoYSA9IDAuMykgKyANCiAgZ2VvbV9zZihkYXRhID0gdWQuc2ZbMSxdLCBmaWxsID0gInllbGxvdyIsIGFscGhhID0gMC4yKSArDQogIGdlb21fc2YoZGF0YSA9IG9waGEyLnNmLCBjb2xvciA9ICJsaWdodGdyYXkiKSArIA0KICBjb29yZF9zZih4bGltID0gYyg4MjU1MDAsIDgxMzUwMCksICB5bGltID0gYygxNjE4MDAwLCAxNjAyMDAwKSkgKw0KICBsYWJzKHg9IkxvbmdpdHVkZSIsIHk9IkxhdGl0dWRlIiwgdGl0bGUgPSBvcGhhMi5zZiRpbmRpdmlkdWFsLmxvY2FsLmlkZW50aWZpZXIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBoanVzdCA9IDAuNSkpICsNCiAgdGhlbWVfYncoKSArIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMC41LCB2anVzdCA9IDAuNSkpDQpjdG1tLmFrZGUgKyBhbm5vdGF0ZSgidGV4dCIsIHggPSA4MTUwMDAsIHkgPSAxNjE3MDAwLCANCiAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIkFyZWEgIixyb3VuZChha2RlLnN1bW1hcnlbWyJDSSJdXVszXSwyKSwiIHNxa20iKSwNCiAgICAgICAgICAgICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiLCBzaXplID0gNSwgY29sb3IgPSAid2hpdGUiKQ0KYGBgDQoNClRoZSBkZXRhaWxzIG9mIHRoaXMgYW5hbHlzaXMgYXJlIHByZXR0eSBuZXcgdG8gbWUgc28gSSBkb24ndCB5ZXQgaGF2ZSBhIHNvbGlkIGdyYXNwIG9uIHRoZSB2YXJpb3VzIGNvbXBvbmVudHMgb2YgdGhlIHNjcmlwdCwgYnV0IGlmIHlvdSB3YW50IG1vcmUgaW5mb3JtYXRpb24geW91IGNhbiB2aWV3Og0KDQo+IEVkZWxraW5kLCBKLkwuLCBTdGFsa2VyLCBKLkIuLCBCZWNrLCBELkQuLCBEZW5hcmRvLCBELkYuLCBFbXBsaWRnZSwgUC5HLiwgR2FsbGFyZG8sIEwuLCBHZW50cnksIEMuTS4sIEdvb2RlLCBNLkouLCBKZW5uaW5ncywgUi5ELiwgSm9uZXMuIEouTC4sIEt3aWF0a293c2tpLCBNLkEuLCBOb3dhaywgRS5NLiwgUmVwcCwgUi5BLiwgU2NodWV0dCwgRy5XLiwgU3VsbGl2YW4sIEIuSy4sIFRyYWN5LCBDLlIuLCBhbmQgR2llbmdlciwgQy5NLiAoMjAyNSkgRW52aXJvbm1lbnRhbCBJbmZsdWVuY2VzIG9uIFNwYWNlIFVzZSBvZiBHaWxhIE1vbnN0ZXJzIChIZWxvZGVybWEgc3VzcGVjdHVtKS4gSGVycGV0b2xvZ2ljYS4gaHR0cHM6Ly9kb2kub3JnLzEwLjE2NTUvSGVycGV0b2xvZ2ljYS1ELTI0LTAwMDUzDQoNClRoaXMgd2FzIHNpbXBseSBhbiBleGFtcGxlIG9mIGhvdyB0byBydW4gdGhlIGFuYWx5c2lzIGFuZCBwcmVwYXJlIHRoZSByZXN1bHRpbmcgb2JqZWN0cyBmb3IgdmlzdWFsaXphdGlvbi4gDQoNCiMgWU9VUiBUVVJOIQ0KDQpOb3cgaXTigJlzIHlvdXIgdHVybiEgQWx0aG91Z2ggc29tZSBvZiB0aGlzIG1pZ2h0IG5vdCBiZSBhcHBsaWNhYmxlIHRvIHlvdXIgdGhlc2lzIHJlc2VhcmNoLCB0cnkgdGhpcyBpbmZvcm1hdGlvbiBvdXQgb24gYSBkYXRhc2V0IG9mIHlvdXIgY2hvaWNlIGZyb20gYW55IG9mIHRoZSBzb3VyY2VzIGFib3ZlLiBJZiB5b3UgY2FuIGFwcGx5IHRoaXMgdG8geW91ciB0aGVzaXMsIHRoZW4gYWRkIGl0IHRvIHlvdXIgd2Vic2l0ZSEgT3RoZXJ3aXNlIGNyZWF0ZSBhIG5ldyByZXBvc2l0b3J5IGZvciB5b3VyIHByZXNlbnRhdGlvbiBpbiB0aGUgbmV4dCBjbGFzcy4=