1 Introduction

This exercise will build on the skills acquired in the Mapping Basics and Species Distribution Modeling exercises to perform Home Range Analysis and map the results. We will start by examining the structure of data used in home range analyses, we will look at ways to perform QA/QC checks on the location data, and finally various ways to visualize the data. One of the new skills we will discuss in this exercise is to create functions and loops to replicate analyses.

1.1 Packages used in this exercise

There are several specialty packages that will be used in this exercise due to the specific nature of the analyses. Some of these packages you will need to install while several others we have used in previous exercises and should already be installed.

adehabitatHR | ctmm | pbapply | raster | sf | sp |
| terra | terrainr | tidyverse |

To begin, we will install the following:

pacman::p_load("adehabitatHR", "ctmm", "pbapply", "raster", "sf", "sp", "terra", "terrainr", "tidyverse")

2 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")
head(data, 3)

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.

MCP Analysis Example KDE Analysis Example

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.

3 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.

3.1 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()

3.2 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.

3.2.1 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.

3.2.2 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.

3.2.3 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]]

KDE Analysis OPHA1 KDE Analysis OPHA2

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.

3.3 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.

4 YOUR TURN!

Now it’s your turn! Although some of this might not be applicable to your thesis research, try this information out on a dataset of your choice from any of the sources above. If you can apply this to your thesis, then add it to your website! Otherwise create a new repository for your presentation in the next class.

LS0tDQp0aXRsZTogSG9tZSBSYW5nZSBBbmFseXNpcyA8YnI+PHNtYWxsPkFkdmFuY2VkIERhdGEgQW5hbHl0aWNzPC9zbWFsbD48L2JyPg0KYXV0aG9yOiAiQXVzdGluIFBlYXkgU3RhdGUgVW5pdmVyc2l0eSINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgICByb3dzLnByaW50OiAxMA0KICAgIHRoZW1lOiBjb3Ntbw0KICAgIGhpZ2hsaWdodDogYnJlZXplZGFyaw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IG5vDQogICAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIHJvd3MucHJpbnQ6IDEwDQogICAgdGhlbWU6IGNvc21vDQogICAgaGlnaGxpZ2h0OiBicmVlemVkYXJrDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogbm8NCiAgICAgIHNtb290aF9zY3JvbGw6IHllcw0KZWRpdG9yX29wdGlvbnM6DQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCiAgbW9kZTogZ2ZtDQotLS0NCg0KYGBgez1odG1sfQ0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCg0KaDEudGl0bGUgew0KICBmb250LXNpemU6IDQwcHg7DQogIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICBjb2xvcjogQmxhY2s7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCg0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgZm9udC1zaXplOiAyNXB4Ow0KICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGNvbG9yOiAjRDAyMzQ5Ow0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQoNCmJvZHkgew0KICBmb250LWZhbWlseTogSGVsdmV0aWNhOw0KICBmb250LXNpemU6IDEycHQ7DQp9DQoNCi56b29tIHsNCiAgdHJhbnNmb3JtLW9yaWdpbjogNDAlIDUwJSAwOw0KICB0cmFuc2l0aW9uOiB0cmFuc2Zvcm0gLjJzOw0KICBtYXJnaW46IDAgYXV0bzsNCn0NCi56b29tIGltZ3sNCgl3aWR0aDphdXRvOw0KCWhlaWdodDphdXRvOwkNCn0NCi56b29tOmhvdmVyIHsNCiAgdHJhbnNmb3JtOiBzY2FsZSgyKTsNCn0NCg0KdGgsIHRkIHtwYWRkaW5nOiA1cHg7fQ0KDQo8L3N0eWxlPg0KYGBgDQoNCiMgSW50cm9kdWN0aW9uDQoNClRoaXMgZXhlcmNpc2Ugd2lsbCBidWlsZCBvbiB0aGUgc2tpbGxzIGFjcXVpcmVkIGluIHRoZSBbTWFwcGluZyBCYXNpY3NdKGh0dHBzOi8vY2hyaXNtZ2VudHJ5LmdpdGh1Yi5pby9NYXBwaW5nLUJhc2ljcy8pIGFuZCBbU3BlY2llcyBEaXN0cmlidXRpb24gTW9kZWxpbmddKGh0dHBzOi8vY2hyaXNtZ2VudHJ5LmdpdGh1Yi5pby9EaXN0cmlidXRpb24tTWFwcy8pIGV4ZXJjaXNlcyB0byBwZXJmb3JtICoqSG9tZSBSYW5nZSBBbmFseXNpcyoqIGFuZCBtYXAgdGhlIHJlc3VsdHMuIFdlIHdpbGwgc3RhcnQgYnkgZXhhbWluaW5nIHRoZSBzdHJ1Y3R1cmUgb2YgZGF0YSB1c2VkIGluIGhvbWUgcmFuZ2UgYW5hbHlzZXMsIHdlIHdpbGwgbG9vayBhdCB3YXlzIHRvIHBlcmZvcm0gUUEvUUMgY2hlY2tzIG9uIHRoZSBsb2NhdGlvbiBkYXRhLCBhbmQgZmluYWxseSB2YXJpb3VzIHdheXMgdG8gdmlzdWFsaXplIHRoZSBkYXRhLiBPbmUgb2YgdGhlIG5ldyBza2lsbHMgd2Ugd2lsbCBkaXNjdXNzIGluIHRoaXMgZXhlcmNpc2UgaXMgdG8gY3JlYXRlIGZ1bmN0aW9ucyBhbmQgbG9vcHMgdG8gcmVwbGljYXRlIGFuYWx5c2VzLiANCg0KIyMgUGFja2FnZXMgdXNlZCBpbiB0aGlzIGV4ZXJjaXNlDQoNClRoZXJlIGFyZSBzZXZlcmFsIHNwZWNpYWx0eSBwYWNrYWdlcyB0aGF0IHdpbGwgYmUgdXNlZCBpbiB0aGlzIGV4ZXJjaXNlIGR1ZSB0byB0aGUgc3BlY2lmaWMgbmF0dXJlIG9mIHRoZSBhbmFseXNlcy4gU29tZSBvZiB0aGVzZSBwYWNrYWdlcyB5b3Ugd2lsbCBuZWVkIHRvIGluc3RhbGwgd2hpbGUgc2V2ZXJhbCBvdGhlcnMgd2UgaGF2ZSB1c2VkIGluIHByZXZpb3VzIGV4ZXJjaXNlcyBhbmQgc2hvdWxkIGFscmVhZHkgYmUgaW5zdGFsbGVkLg0KDQo8cCBhbGlnbj0iY2VudGVyIj4NCnwgYGBgYWRlaGFiaXRhdEhSYGBgIHwgYGBgY3RtbWBgYCB8IGBgYHBiYXBwbHlgYGAgfCBgYGByYXN0ZXJgYGAgfCBgYGBzZmBgYCB8IGBgYHNwYGBgIHwgPGJyPjwvYnI+IHwgYGBgdGVycmFgYGAgfCBgYGB0ZXJyYWlucmBgYCB8IGBgYHRpZHl2ZXJzZWBgYCB8DQo8L3A+DQoNClRvIGJlZ2luLCB3ZSB3aWxsIGluc3RhbGwgdGhlIGZvbGxvd2luZzoNCg0KYGBge3IgUGFja2FnZXMsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnBhY21hbjo6cF9sb2FkKCJhZGVoYWJpdGF0SFIiLCAiY3RtbSIsICJwYmFwcGx5IiwgInJhc3RlciIsICJzZiIsICJzcCIsICJ0ZXJyYSIsICJ0ZXJyYWluciIsICJ0aWR5dmVyc2UiKQ0KYGBgDQoNCiMgRGF0YXNldA0KDQpXaGlsZSB3ZSBoYXZlIHNlZW4gdGhhdCBiaW9sb2dpY2FsIGRhdGEgY2FuIGJlIG9idGFpbmVkIGZyb20gc2l0ZXMgc3VjaCBhcyBbRHJ5YWRdKGh0dHBzOi8vZGF0YWRyeWFkLm9yZy9zZWFyY2gpIGFuZCBbR0JJRl0oaHR0cHM6Ly93d3cuZ2JpZi5vcmcvKSwgd2UgbmVlZCB0byBvYnRhaW4gZGF0YSB3aXRoIHNwZWNpZmljIHZhcmlhYmxlcyB0aGF0IG1pZ2h0IG5vdCBiZSBhdmFpbGFibGUgb24gdGhvc2Ugc2l0ZXMuIFNvIHRvIHN0YXJ0IHRoaXMgZXhlcmNpc2UsIHdlIHdpbGwgZG93bmxvYWQgYSBcKi5jc3YgZmlsZSBjb250YWluaW5nIGluZm9ybWF0aW9uIGZvciAqT3BoaW9waGFndXMgaGFubmFoKiAoS2luZyBDb2JyYSkgZnJvbSBbbW92ZWJhbmsub3JnXShodHRwczovL3d3dy5tb3ZlYmFuay5vcmcvcGFuZWxfZW1iZWRkZWRfbW92ZWJhbmtfd2ViYXBwP2d3dF9mcmFnbWVudD1wYWdlJTNEc2VhcmNoX21hcF9saW5rZWQlMkNpbmRpdmlkdWFsSWRzJTNENTU2NTc0MzY2JTJCNTU2NTc0Mzc3JTJDbGF0JTNEMTQuNTQ2NDkxOTY4NDY0MTMzJTJDbG9uJTNEMTAxLjk0ODY5Mjc5MjYxNDk1JTJDeiUzRDExKSB0aGF0IGNvbnRhaW5zIHRoZSBhcHByb3ByaWF0ZSB2YXJpYWJsZSB0byB0cmFjayBtb3ZlbWVudCBmcm9tIHJhZGlvIHRyYW5zbWl0dGVyIGRhdGEuIEJlY2F1c2UgdGhpcyBkYXRhIGhhcyB0byBiZSBkb3dubG9hZGVkIHdlIHdpbGwgbmVlZCB0byBnbyB0aHJvdWdoIHRoZSBwcm9jZXNzIG9mIGltcG9ydGluZyB0aGlzIGluZm9ybWF0aW9uIHVzaW5nIHRoZSBgYGByZWFkLmNzdigpYGBgIGNvbW1hbmQuIE9uY2Ugd2UgaGF2ZSBpbXBvcnRlZCB0aGUgZGF0YSB3ZSBjYW4gdmlldyB0aGUgc3RydWN0dXJlLiANCg0KPiBJZiBmb3JrZWQsIHRoaXMgZGF0YSB3aWxsIGJlIGF2YWlsYWJsZSBpbiB0aGUgRGF0YSBmb2xkZXIgd2l0aGluIHRoZSByZXBvc2l0b3J5LiBBZGRpdGlvbmFsbHksIEVTUkkgU2hhcGVmaWxlIGFuZCBHb29nbGUgS01aIGZvcm1hdHMgd2lsbCBiZSBpbmNsdWRlZCBpZiB5b3UgYXJlIGludGVyZXN0ZWQgaW4gd29ya2luZyB3aXRoIHRob3NlIGRhdGEgdHlwZXMuDQoNCjxkZXRhaWxzPjxzdW1tYXJ5PjxiaWc+SW1wb3J0IERhdGFzZXQgd2l0aCBgYGByZWFkLmNzdmBgYDwvYmlnPjwvc3VtbWFyeT4NCmBgYHtyIGRhdGEsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmRhdGEgPC0gcmVhZC5jc3YoIi4vRGF0YS9vcGhpb3BoYWd1c19oYW5uYWguY3N2IikNCmBgYA0KPC9kZXRhaWxzPg0KYGBge3Igc3RydWN0dXJlLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpoZWFkKGRhdGEsIDMpDQpgYGANCg0KTm90aWNlIHRoYXQgdGhpcyBkYXRhc2V0IGNvbnRhaW5zIG1vcmUgdGhhbiBzcGVjaWVzIGluZm9ybWF0aW9uIGFuZCB4LHkgY29vcmRpbmF0ZXMuIFdoaWxlIHRoYXQgaW5mb3JtYXRpb24gaXMgaW1wb3J0YW50LCB0aGlzIGRhdGFzZXQgYWxzbyBjb250YWlucyB0aW1lIGFuZCBkYXRlIGluZm9ybWF0aW9uICh0aW1lc3RhbXApLCBzZW5zb3IgdHlwZSwgYW5kIFVUTSBkYXRhLiBJbiBvcmRlciB0byBiZSBzdXJlIHRoYXQgdGhlIGRhdGFzZXQgY29udGFpbnMgbm8gb3V0bGllcnMsIHdlIGNhbiBwbG90IHRoZSBkYXRhIHVzaW5nIGBgYGdncGxvdGBgYCBhbmQgYGBgcGxvdGx5YGBgIHRvIGludGVyYWN0aXZlbHkgdmlldyB0aGUgZGF0YS4NCg0KYGBge3IgcGxvdGx5LCBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTgsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpxYXFjX3Bsb3QgPC0gZ2dwbG90KCkgKyBnZW9tX3BvaW50KGRhdGE9ZGF0YSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh1dG0uZWFzdGluZyx1dG0ubm9ydGhpbmcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcj1pbmRpdmlkdWFsLmxvY2FsLmlkZW50aWZpZXIpKSArDQogICAgICAgICAgICAgICAgICAgICAgICBsYWJzKHg9IkVhc3RpbmciLCB5PSJOb3J0aGluZyIpICsNCiAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlcyhjb2xvcj1ndWlkZV9sZWdlbmQoIklkZW50aWZpZXIiKSkNCg0KcGxvdGx5OjpnZ3Bsb3RseShxYXFjX3Bsb3QpDQpgYGANCg0KV2l0aCBgYGBwbG90bHlgYGAsIHNpbWlsYXIgdG8gYGBgbGVhZmxldGBgYCwgd2UgaGF2ZSB0aGUgYWJpbGl0eSB0byBleGFtaW5lIHRoZSBzcHJlYWQgb2YgdGhlIGRhdGEgcG9pbnRzIGFuZCBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIGZyb20gdmFyaW91cyBjb2x1bW5zLiBGcm9tIHRoaXMgcGxvdCB3ZSBjYW4gc2VlIHRoYXQgdGhlcmUgYXJlIHR3byBpbmRpdmlkdWFscywgT1BIQTEgYW5kIE9QSEEyLCB0aGF0IHdlcmUgdHJhY2tlZCBmb3IgdGhpcyBzdHVkeSBhbmQgaGF2ZSB2ZXJ5IGxpdHRsZSBvdmVybGFwIGluIHRoZWlyIGFwcGFyZW50IHJhbmdlLg0KDQpXaGlsZSB3ZSBjb3VsZCBjb250aW51ZSB3aXRoIHRoZSBjdXJyZW50IGRhdGFzZXQgdG8gY2FsY3VsYXRlIGhvbWUgcmFuZ2UgZm9yIHRoZSBlbnRpcmUgcG9wdWxhdGlvbiwgdG8gc2ltcGxpZnkgdGhpcyBleGVyY2lzZXMgd2Ugd2lsbCBpbnN0ZWFkIGZvY3VzIG9uIGluZGl2aWR1YWxzLg0KDQo8cCBhbGlnbj0iY2VudGVyIj4NCk1DUCBBbmFseXNpcyBFeGFtcGxlIHwgS0RFIEFuYWx5c2lzIEV4YW1wbGUNCi0gfCAtIA0KIVtdKC4vbWNwX2FsbC5wbmcgIk1DUCBmb3IgYWxsIHBvaW50cyIpIHwgIVtdKC4va2RlX2FsbC5wbmcgIktERSBmb3IgYWxsIHBvaW50cyIpDQo8L3A+DQoNCkRlcGVuZGluZyBvbiB0aGUgbmVlZHMgb2YgeW91ciBhbmFseXNpcywgeW91IG1heSBmaW5kIGEgc2l0dWF0aW9uIHdoZXJlIGEgZGF0YXNldCBiZWluZyBwcm92aWRlZCBieSBhIGNvbGxhYm9yYXRvciBvciB0aHJvdWdoIGEgcmVwb3NpdG9yeSBjb250YWlucyBhIGxhcmdlIG51bWJlciBvZiBpbmRpdmlkdWFscyBiZWluZyByZWNvcmRlZC4gSW4gb3JkZXIgdG8gc2VwYXJhdGUgdGhlc2UgbGFyZ2VyIGRhdGFzZXRzIGludG8gaW5kaXZpZHVhbHMsIHdlIGNhbiBjcmVhdGUgYSBmdW5jdGlvbiB0aGF0IHVzZXMgdGhlIGBgYGxhcHBseWBgYCBjb21tYW5kIHRvIGFwcGx5IGEgZnVuY3Rpb24gb3ZlciBhIGxpc3Qgb3IgdmVjdG9yIGRhdGFzZXQuIFNwZWNpZmljYWxseSwgdGhpcyBmdW5jdGlvbiB3aWxsIHRha2UgdGhlIG9yaWdpbmFsICpkYXRhKnNldCwgc3BsaXQgaXQgaW50byBzZXBhcmF0ZSBmaWxlcyBiYXNlZCBvbiB0aGUgaW5kaXZpZHVhbCBpZGVudGlmaWVyLCBhbmQgY3JlYXRlIG5ldyBcKi5jc3YgZmlsZXMgdXNpbmcgdGhlIGlkZW50aWZpZXIgYXMgdGhlIGZpbGVuYW1lLiANCg0KYGBge3IgbGFwcGx5IGZ1bmN0aW9uLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KbGFwcGx5KHNwbGl0KGRhdGEsIGRhdGEkaW5kaXZpZHVhbC5sb2NhbC5pZGVudGlmaWVyKSwgDQogICAgICAgZnVuY3Rpb24oeCl3cml0ZS5jc3YoeCwgZmlsZSA9IHBhc3RlKHgkaW5kaXZpZHVhbC5sb2NhbC5pZGVudGlmaWVyWzFdLCIuY3N2Iiwgc2VwID0gIiIpLCByb3cubmFtZXMgPSBGQUxTRSkpDQpgYGANCg0KVGhlIGFuYXRvbXkgb2YgdGhlIGZ1bmN0aW9uIGlzIGFzIGZvbGxvd3M6DQoNCi0gYGBgbGFwcGx5KClgYGAsIGFwcGx5IHRoZSBmdW5jdGlvbiBvdmVyIGEgbGlzdA0KLSBgYGBzcGxpdCgpYGBgLCBzZXBhcmF0ZXMgdGhlIGRhdGEgDQotIGBgYGZ1bmN0aW9uKClgYGAsIGNvbXBvc2UgYSBzZXJpZXMgb2Ygc3RlcHMgdG8gYmUgYXBwbGllZCB0byB0aGUgZGF0YQ0KLSBgYGB3cml0ZS5jc3YoKWBgYCwgd3JpdGUgYSBjc3YgZmlsZQ0KLSBgYGBwYXN0ZSgpYGBgLCBjcmVhdGUgYSBjaGFyYWN0ZXIgc3RyaW5nIHRvIGJlIHVzZWQgZm9yIHRoZSBmaWxlIG5hbWUNCg0KSWYgeW91IGV4YW1pbmUgdGhlIHJvb3QgZm9sZGVyIHlvdSB3aWxsIG5vdyBmaW5kIHRoZSBhZGRpdGlvbiBvZiB0d28gbmV3IFwqLmNzdiBmaWxlczogT1BIQTEgYW5kIE9QSEEyLiBXZSB3aWxsIHVzZSB0aGVzZSBmaWxlcyB0byBydW4gb3VyIGhvbWUgcmFuZ2UgYW5hbHlzZXMuIEFsdGVybmF0aXZlbHksIGlmIHlvdSBhcmUgcHJvdmlkZWQgYSBudW1iZXIgb2YgaW5kaXZpZHVhbCBcKi5jc3YgZmlsZXMgYW5kIG5lZWQgdG8gcXVpY2tseSBpbXBvcnQgdGhlIGRhdGEgaW50byBzZXBhcmF0ZSBkYXRhIGZyYW1lcyB5b3UgY291bGQgd3JpdGUgYSAqKmZvciBsb29wKiogdGhhdCB3b3VsZCBsb29rIHNvbWV0aGluZyBsaWtlIHRoaXM6DQoNCmBgYA0KbGlzdCA8LSBnc3ViKCJcXC5jc3YkIiwiIiwgbGlzdC5maWxlcyhwYXR0ZXJuPSJcXC5jc3YkIikpDQoNCmZvcihpIGluIGxpc3Qpew0KICBhc3NpZ24oaSwgcmVhZC5jc3YocGFzdGUoaSwgIi5jc3YiLCBzZXA9IiIpKSkNCn0NCmBgYA0KDQpBbGwgb2YgdGhlIFwqLmNzdiBmaWxlcyBpbiB5b3VyIHJvb3QgZGlyZWN0b3J5IHdpbGwgbm93IGJlIGF2YWlsYWJsZSBpbiB5b3VyIGdsb2JhbCBlbnZpcm9ubWVudC4gSG93ZXZlciwgdGhlIG1ldGhvZCB3ZSB1c2VkIGFib3ZlIGNyZWF0ZWQgaW5kaXZpZHVhbCBcKi5jc3YgZmlsZXMgZm9yIGVhY2ggaW5kaXZpZHVhbHMsIHNvIGluc3RlYWQgd2Ugd2lsbCBtYWtlIGEgbGlzdCBvZiB0aG9zZSBpbmRpdmlkdWFsIGZpbGVzIGFzIHJlZmVyZW5jZSBmb3IgdGhlIGFuYWx5c2VzIHdoZW4gbmVjZXNzYXJ5Lg0KDQpgYGB7ciBsaXN0LCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KZmlsZXMgPC0gbGlzdC5maWxlcyhwYXRoID0gIi4iLCBwYXR0ZXJuID0gIltPUEhBXStbMC05XSsiLCBmdWxsLm5hbWVzID0gVFJVRSkNCmBgYA0KDQpJbiB0aGUgYGBgbGlzdC5maWxlc2BgYCBjb21tYW5kIGFib3ZlLCBgYGBwYXRoID0gIi4iYGBgIGluZm9ybXMgdGhlIGxvY2F0aW9uLCBpbiB0aGlzIGNhc2UgdGhlIHJvb3QgZGlyZWN0b3J5LCBgYGBwYXR0ZXJuID1gYGAgZGVzY3JpYmVzIHRoZSB3YXkgdGhlIGZpbGVzIGFyZSBuYW1lZCwgaW4gdGhpcyBjYXNlIE9QSEEgZm9sbG93ZWQgYnkgYSBudW1iZXIgYmV0d2VlbiAwLTksIGFuZCBgYGBmdWxsLm5hbWVzYGBgIGRlc2NyaWJlcyBob3cgdGhlIGZpbGVzIHdpbGwgYmUgbGlzdGVkLiANCg0KIyBBbmFseXNpcw0KDQpUbyBkYXRlLCB0aGlzIHdpbGwgYmUgb25lIG9mIHRoZSBtb3JlIGNvZGUgaW50ZW5zaXZlIGV4ZXJjaXNlcyB3ZSBoYXZlIGF0dGVtcHRlZC4gV2hpbGUgdGhlIGNvZGUgaXRzZWxmIGlzIG5vdCBkaWZmaWN1bHQsIHRoZSBpbXBsZW1lbnRhdGlvbiBpcyB0ZWRpb3VzIGR1ZSB0byB0aGUgdmFyeWluZyBkYXRhIHR5cGVzIGFuZCBhbmFseXNlcyBwZXJmb3JtZWQuIEFkZGl0aW9uYWxseSwgd2hpbGUgbm90IHJ1biBhcyBhIGNvbXBvbmVudCBvZiB0aGlzIGV4ZXJpY3NlLCBJIGFtIHByb3ZpZGluZyBhIHNjcmlwdCB0aGF0IHdpbGwgd2FsayB5b3UgdGhyb3VnaCB0aGUgc3RlcHMgdG8gYXV0b21hdGUgYW4gYW5hbHlzaXMgZm9yIGEgbnVtYmVyIG9mIGluZGl2aWR1YWxzIHVzaW5nIGEgZnVuY3Rpb24gYW5kIGBgYHBiYXBwbHlgYGAgd2hpY2ggcHJvZHVjZXMgYSBfcHJvZ3Jlc3MgYmFyXyBpbiB0aGUgY29uc29sZSBkZXRhaWxpbmcgdGhlIGxldmVsIG9mIGNvbXBsZXRpb24uIA0KDQojIyBJbWFnZXJ5DQoNCkluIHByZXZpb3VzIGV4ZXJjaXNlcyB3ZSB1c2VkIGEgbnVtYmVyIG9mIGRpZmZlcmVudCBzb3VyY2VzIHRvIG9idGFpbiBpbWFnZXJ5IG9yIGJhc2VtYXBzIGZvciB0aGUgdmFyaW91cyB2aXN1YWxpemF0aW9ucy4gQXMgd2UgaGF2ZSBkaXNjdXNzZWQgd2l0aCBwYWNrYWdlcyBsaWtlIGBgYG9wZW5zdHJlZXRtYXBgYGAgb3IgYGBgZ2dtYXBgYGAgdGhlcmUgY2FuIGJlIHJlc3RyaWN0aW9ucyB0aGF0IGNvbXBsaWNhdGUgdGhlaXIgdXNlZnVsbmVzcy4gQWRkaXRpb25hbGx5LCBzb21lIHNvdXJjZXMgb2YgcmFzdGVyIGltYWdlcnkgY2FuIGJlIGxpbWl0ZWQgaW4gdGhlaXIgZ2VvZ3JhcGhpYyBzY29wZS4gSW4gdGhlIFtwcmV2aW91cyBleGVyY2lzZV0oaHR0cHM6Ly9jaHJpc21nZW50cnkuZ2l0aHViLmlvL0dvb2dsZS1FYXJ0aC1FbmdpbmUvKSB3ZSB1c2VkICoqR29vZ2xlIEVhcnRoIEVuZ2luZSoqIHRvIHByb2R1Y2UgaW1hZ2VzIHRoYXQgY291bGQgYmUgdXNlZCBhcyBiYXNlbWFwcyBmb3Igb3VyIGFuYWx5c2VzLiBTbyBmb3IgdGhpcyBleGVyY2lzZSB3ZSB3aWxsIHVzZSBHRUUgdG8gb2J0YWluIGEgU2VudGluZWwgMiBpbWFnZSBmcm9tIFRoYWlsYW5kIGFuZCB1dGlsaXplIGl0IGFzIHRoZSBiYWNrZ3JvdW5kIGZvciBvdXIgaG9tZXJhbmdlIGFuYWx5c2VzLg0KDQpCZWNhdXNlIHRoaXMgY29kZSBpcyBfamF2YXNjcmlwdF8gSSB3aWxsIHNpbXBseSB1c2UgYSBjb2RlIGJsb2NrIHRvIHByb3ZpZGUgZGV0YWlscyBvbiB0aGUgc2NyaXB0IEkgdXNlZCB0byBsb2NhdGUsIHByZXBhcmUsIGFuZCBkb3dubG9hZCB0aGUgaW1hZ2UuIEZvciBtb3JlIGluZm9ybWF0aW9uIHJlZmVyIHRvIHRoZSBwcmV2aW91cyBleGVyY2lzZSBvciBHRUUgZG9jdW1lbnRhdGlvbi4NCg0KYGBgDQovLyBTZXQgdmFyaWFibGVzIGZvciB0aGUgbG9jYXRpb24gYW5kIGltYWdlcnkgc291cmNlDQp2YXIgdGhhaWxhbmQgPSAvKiBjb2xvcjogI2Q2MzAwMCAqL2VlLkdlb21ldHJ5LlBvaW50KFsxMDEuOTY2NjA1MzkwNjAxNjEsIDE0LjU0NjY0MTEyMjM0NjAyNl0pLA0Kc2VudGluZWwyID0gZWUuSW1hZ2VDb2xsZWN0aW9uKCJDT1BFUk5JQ1VTL1MyX0hBUk1PTklaRUQiKTsNCiANCi8vIERhdGVzIGJlbG93IHVzZWQgYXJlIGNsb3Nlc3QgYXZhaWxhYmxlIHRvIGRhdGEgY29sbGVjdGlvbiBkYXRlDQp2YXIgaW1hZ2UgPSBlZS5JbWFnZUNvbGxlY3Rpb24oc2VudGluZWwyKSAvLyBpZGVudGlmeSB0aGUgc2Vuc29yDQogIC5maWx0ZXJEYXRlKCIyMDE2LTA0LTAxIiwgIjIwMTYtMTAtMzAiKSAvLyBmaWx0ZXIgc3BlY2lmaWMgZGF0ZXMNCiAgLmZpbHRlckJvdW5kcyh0aGFpbGFuZCkgLy8gb25seSBpbmNsdWRlIHNjZW5lcyB0aGF0IGludGVyc2VjdCB3aXRoIG91ciBwb2ludCBnZW9tZXRyeQ0KICAuc29ydCgiQ0xPVURfQ09WRVJBR0VfQVNTRVNTTUVOVCIpIC8vIGZpbHRlciBieSBjbG91ZHkgcGl4ZWwgcGVyY2VudGFnZQ0KICAuZmlyc3QoKTsgLy8gc2VsZWN0IHRoZSBsZWFzdCBjbG91ZHkgc2NlbmUgDQogDQovLyBTZXQgdmlzdWFsaXphdGlvbiBwYXJhbWV0ZXJzLCB0aGVzZSBjYW4gYmUgYWRqdXN0ZWQgd2l0aGluIHRoZSBjb2RlIGVkaXRvcg0KdmFyIG5hdHVyYWxjb2xvciA9IHsNCiAgYmFuZHM6IFsiQjQiLCAiQjMiLCAiQjIiXSwNCiAgbWluOiAwLA0KICBtYXg6IDQwMDAsDQp9Ow0KIA0KLy8gRGlzcGxheSBpbWFnZXMgb24gdGhlIG1hcA0KTWFwLmFkZExheWVyKGltYWdlLCBuYXR1cmFsY29sb3IsICJOYXR1cmFsIENvbG9yIENvbXBvc2l0ZSIpOw0KDQovLyBQcmVwYXJlIHRoZSBpbWFnZSBhbmQgZXhwb3J0IGluIFVUTSA0NzsgcmVtZW1iZXIgdGhlIGRhdGEgaXMgZnJvbSBUaGFpbGFuZA0KdmFyIGV4cG9ydFJHQiA9IGltYWdlLnZpc3VhbGl6ZSh7DQogIGJhbmRzOiBbIkI0IiwgIkIzIiwgIkIyIl0sDQogIG1pbjogMCwNCiAgbWF4OiA0MDAwLA0KfSk7DQoNCi8vIENSUyB3YXMgc2V0IHRvIGFwcHJvcHJpYXRlIEVQU0cgYW5kIG1heFBpeGVscyB3ZXJlIHNldCB0byBhbGxvdyBmb3IgbGFyZ2UgZmlsZSBzaXplIA0KRXhwb3J0LmltYWdlLnRvRHJpdmUoew0KICBpbWFnZTogZXhwb3J0UkdCLA0KICBzY2FsZTogMTAsDQogIGRlc2NyaXB0aW9uOiAidGhhaWxhbmQzMjY0NyIsDQogIGNyczogIkVQU0c6MzI2NDciLA0KICBtYXhQaXhlbHM6IDFlMTMsDQp9KTsNCmBgYA0KDQpFaXRoZXIgc2V0IHRoZSBzYXZlIGxvY2F0aW9uIHRvIHRoZSBwcm9qZWN0IGZvbGRlciwgb3IgbWFubnVhbGx5IGNvcHkgdGhlIGltYWdlcnkgb3ZlciB0byBtYWtlIGl0IGF2YWlsYWJsZSBpbiBSLiBUaGVyZSBpcyBhIGBgYHJnZWVgYGAgcGFja2FnZSB5b3UgY2FuIGV4cGVyaW1lbnQgd2l0aCB0byBpbmNvcnBvcmF0ZSB0aGlzIHN0ZXAgaW50byB3b3JrZmxvdyB3aXRoaW4gUiBob3dldmVyLCB0aGVyZSBpcyBhIGxpdHRsZSBsZWFybmluZyBjdXJ2ZSBkdWUgdG8gdGhlIG1ham9yaXR5IG9mIHRoZSBzY3JpcHQgYmVpbmcgcHl0aG9uLWJhc2VkLiBJbiBvcmRlciB0byBtYWtlIHRoZSByZXN1bHRpbmcgXCouZ2VvVElGRiBmaWxlIHN1aXRhYmxlIGZvciB1c2UgaW4gYGBgZ2dwbG90YGBgLCB3ZSdsbCBuZWVkIHRvIGNyZWF0ZSBhbiBpbWFnZSBzdGFjayBhbmQgY29udmVydCBpdCB0byBhIGRhdGFmcmFtZS4gV2hpbGUgdGhpcyBzdGVwIGlzbid0IGNvbXBsZXRlbHkgbmVjZXNzYXJ5LCBpdCBkb2VzIG1ha2UgdGhlIHN5bnRheCBvZiB1c2luZyB0aGUgaW1hZ2UgaW4gYGBgZ2dwbG90YGBgIG1vcmUgc3RyYWlnaHRmb3J3cmQuIEFkZGl0aW9uYWxseSwgYmVjYXVzZSBpbWFnZSBzd2F0aCBmcm9tIF9TZW50aW5lbCAyXyBpcyAyOTBrbSwgd2Ugd2lsbCBhbHNvIGNyZWF0ZSB0d28gc2l6ZXMgb2YgY3JvcHMgZm9yIHRoZSB2YXJ5aW5nIHNwYXRpYWwgY29uc3RyYWludHMgb2YgdGhlIGRhdGEuIFRoaXMgY2FuIGJlIGFkanVzdGVkIHRocm91Z2hvdXQgdGhlIGFuYWx5c2lzIGFzIG5lZWRlZCB0byBmaXQgdGhlIHNwZWNpZmljIHZpc3VhbGl6YXRpb25zLiANCg0KYGBge3IgaW1hZ2VyeSBwbG90LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTR9DQpvcnRobyA8LSAiLi9EYXRhL3RoYWlsYW5kMzI2NDcudGlmIg0KDQppbWFnZV9zdGFjayA8LSByYXN0ZXI6OnN0YWNrKG9ydGhvKQ0KDQpjcm9wX2V4dGVudCA8LSByYXN0ZXI6OmV4dGVudChtaW4oZGF0YSR1dG0uZWFzdGluZy0yMDAwKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXgoZGF0YSR1dG0uZWFzdGluZysyMDAwKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4oZGF0YSR1dG0ubm9ydGhpbmctMjAwMCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4KGRhdGEkdXRtLm5vcnRoaW5nKzIwMDApKQ0KDQpjcm9wX2Z1bGwgPC0gcmFzdGVyOjpleHRlbnQobWluKGRhdGEkdXRtLmVhc3RpbmctNTAwMCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heChkYXRhJHV0bS5lYXN0aW5nKzUwMDApLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4oZGF0YSR1dG0ubm9ydGhpbmctNTAwMCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heChkYXRhJHV0bS5ub3J0aGluZys1MDAwKSkNCg0KaW1hZ2VfY3JvcCA8LSBjcm9wKGltYWdlX3N0YWNrLCBjcm9wX2V4dGVudCkNCmltYWdlX2Z1bGwgPC0gY3JvcChpbWFnZV9zdGFjaywgY3JvcF9mdWxsKQ0KDQp0aGFpbGFuZCA8LSBhcy5kYXRhLmZyYW1lKGltYWdlX2Nyb3AsIHh5ID0gVFJVRSkgJT4lIHNldE5hbWVzKGMoIngiLCAieSIsICJyZWQiLCAiZ3JlZW4iLCAiYmx1ZSIpKQ0KdGhhaWxhbmQuZnVsbCA8LSBhcy5kYXRhLmZyYW1lKGltYWdlX2Z1bGwsIHh5ID0gVFJVRSkgJT4lIHNldE5hbWVzKGMoIngiLCAieSIsICJyZWQiLCAiZ3JlZW4iLCAiYmx1ZSIpKQ0KDQpnZ3Bsb3QoKStnZW9tX3NwYXRpYWxfcmdiKGRhdGEgPSB0aGFpbGFuZCwgYWVzKHg9eCwgeT15LCByPXJlZCwgZz1ncmVlbiwgYj1ibHVlKSkgKyBjb29yZF9zZigpDQpgYGANCg0KIyMgSG9tZSBSYW5nZSBBbmFseXNpcw0KDQpUaGVyZSBhcmUgdGhyZWUgYmFzaWMgdHlwZXMgb2YgaG9tZSByYW5nZSBhbmFseXNlcyB3ZSB3aWxsIHBlcmZvcm0gaW4gdGhpcyBleGVyY2lzZTogTWluaW11bSBDb252ZXggUG9seWdvbiAoTUNQKSwgS2VybmVsLURlbnNpdHkgRXN0aW1hdGlvbiAoS0RFKSwgYW5kIEJyb3duaWFuIEJyaWRnZSBNb3ZlbWVudCBNb2RlbCAoQkIpLiBUaGVyZSBhcmUgYSBudW1iZXIgb2YgZGlmZmVyZW50IHR1bmluZyBwYXJhbWV0ZXJzIHRoYXQgY2FuIGJlIGFwcGxpZWQgdGhlc2UgYW5hbHlzZXMsIGhvd2V2ZXIgaW4gdGhpcyBleGVyY2lzZSB3ZSB3aWxsIHVzZSB0aGUgbW9zdCBiYXNpYyB2ZXJzaW9ucyBvZiB0aGUgYW5hbHlzaXMuIA0KDQpJbiB0aGUgc2VjdGlvbiBhYm92ZSB3ZSB1c2UgdGhlIGBgYGxhcHBseWBgYCBjb21tYW5kIHRvIGxvb3AgYSBmdW5jdGlvbiB1c2VkIHRvIHNlcGFyYXRlIHRoZSBvcmlnaW5hbCBkYXRhc2V0IGludG8gaW5kaXZpZHVhbCBmaWxlcy4gVGhpcyBpcyBhIHVzZWZ1bCB0b29sLCBob3dldmVyLCB3aGVuIHRoZSBmdW5jdGlvbiBsb29wcyB0aHJvdWdoIGRvemVucyBvciBldmVuIGh1bmRyZWRzIG9mIGZpbGVzLCB0aGUgcHJvY2VzcyBjYW4gdGFrZSBhIGxvbmcgcGVyaW9kIG9mIHRpbWUgdG8gY29tcGxldGUuIFVzaW5nIHRoZSBgYGBwYmxhcHBseWBgYCBjb21tYW5kIGFkZHMgYSBwcm9ncmVzcyBiYXIgKGkuZS4gKipwYioqKSB0byB0aGUgcHJvY2VzcyB3aGljaCBwcm92aWRlcyBhbiBlc3RpbWF0ZWQgdGltZSBmb3IgY29tcGxldGlvbiBvZiB0aGUgZnVuY3Rpb24uIFdlIHdpbGwgdXNlIGEgc2ltaWxhciBwcm9jZXNzIHRvIHRoZSBvbmUgYWJvdmUsIHVzaW5nIHRoZSBgYGBwYmxhcHBseWBgYCBjb21tYW5kIHRvIHJ1biBNQ1AgYW5hbHlzaXMgb24gdGhlIGluZGl2aWR1YWwgXCouY3N2IGZpbGVzLiANCg0KIyMjIE1pbmltdW0gQ29udmV4IFBvbHlnb24NCg0KT25lIG9mIHRoZSBtb3N0IHNpbXBsZSBob21lcmFuZ2UgYW5hbHlzZXMgaXMgYSBfbWluaW11bSBjb252ZXggcG9seWdvbl8gb3IgKiptY3AqKi4gQSBtY3AgZHJhd3MgYSBwb2x5Z29uIHdoaWNoIHJlcHJlc2VudHMgYSBzcGVjaWZpZWQgbWluaW11bSBib3VuZGluZyBnZW9tZXRyeSBlbmNsb3NpbmcgdGhlIGRhdGEgcG9pbnRzLiBUaGlzIGVzc2VudGlhbGx5IGNvbm5lY3RzIHRoZSBwb2ludHMgYWxvbmcgdGhlIGZhcnRoZXN0IGdlb2dyYXBoaWMgcG9pbnRzIGVuY2xvc2luZyB0aGVtIGFsbCBpbiBhIHNpbmdsZSBzaGFwZS4gRm9yIHRoaXMgYW5hbHlzaXMgd2Ugd2lsbCBmb2N1cyBvbiB0aGUgT1BIQTEgZmlsZSB3ZSBzZXBhcmF0ZWQgZnJvbSB0aGUgbGFyZ2VyIGRhdGFzZXQgdXNpbmcgdGhlIGBgYGFkZWhhYml0YXRIUmBgYCBwYWNrYWdlLg0KDQpBIGRlc2NyaXB0aW9uIG9mIHRoZSBzdGVwcyB3aXRoaW4gdGhlIGZvbGxvd2luZyBjb2RlIHdpbGwgYmUgZGlzY3Vzc2VkIGZvbGxvd2luZyB0aGUgb3V0cHV0LiBUaGUgcHJvY2VzcyB3b3JrcyBieSBlc3RhYmxpc2hpbmcgdGhlIGZ1bmN0aW9uIGFuZCB0aGVuIHJ1bm5pbmcgYGBgcGJsYXBwbHlgYGAgcmVmZXJyaW5nIGJhY2sgdG8gdGhlICoqZmlsZXMqKiB3ZSBjcmVhdGVkIGluIHRoZSBkYXRhc2V0IHBvcnRpb24gb2YgdGhpcyBleGVyY2lzZS4NCg0KYGBge3IgTUNQIHBsb3QsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG89VFJVRSwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9Nn0NCiMjIERhdGEgZm9yIGEgc2luZ2xlIGluZGl2aWR1YWwNCm9waGExIDwtIHJlYWQuY3N2KCdPUEhBMS5jc3YnKQ0KeHkgPC0gYyhhcy5kYXRhLmZyYW1lKG9waGExJHV0bS5lYXN0aW5nKSxhcy5kYXRhLmZyYW1lKG9waGExJHV0bS5ub3J0aGluZykpDQoNCiMjIHByb2plY3RpbmcgdGhlIGRhdGEgdG8gVVRNDQpkYXRhLnByb2ogPC0gU3BhdGlhbFBvaW50c0RhdGFGcmFtZSh4eSxvcGhhMSwgcHJvajRzdHJpbmcgPSBDUlMoIitwcm9qPXV0bSArem9uZT00NyArZGF0dW09V0dTODQgK3VuaXRzPW0gK25vX2RlZnMiKSkNCnh5LnNwIDwtIFNwYXRpYWxQb2ludHMoZGF0YS5wcm9qQGNvb3JkcykNCg0KIyMgbWNwIGFuYWx5c2lzDQptY3Aub3V0IDwtIG1jcCh4eS5zcCwgcGVyY2VudD0xMDAsIHVub3V0PSJoYSIpDQoNCiMjIGNvbnZlcnRpbmcgbWNwIHRvIHNmIG9iamVjdCBmb3IgZ2dwbG90IGFuZCBwb2ludHMgdG8gZGYNCm1jcC5zZiA8LSBzdF9hc19zZihtY3Aub3V0KQ0KbWNwLnBvaW50cyA8LSBjYmluZCgoZGF0YS5mcmFtZSh4eSkpLG9waGExJGluZGl2aWR1YWwubG9jYWwuaWRlbnRpZmllcikNCmNvbG5hbWVzKG1jcC5wb2ludHMpIDwtIGMoIngiLCJ5IiwgImlkZW50aWZpZXIiKQ0KDQojIE1DUCBwbG90DQptY3AucGxvdCA8LSBnZ3Bsb3QoKSArIHRoZW1lX2J3KCkgKw0KICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDAuNSwgdmp1c3QgPSAwLjUpKSArIA0KICB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpKSArDQogIGdlb21fc3BhdGlhbF9yZ2IoZGF0YSA9IHRoYWlsYW5kLCBhZXMoeD14LCB5PXksIHI9cmVkLCBnPWdyZWVuLCBiPWJsdWUpKSArDQogIGdlb21fc2YoZGF0YT1tY3Auc2YsIGFscGhhID0gMC4xKSArDQogIGdlb21fcG9pbnQoZGF0YT1tY3AucG9pbnRzLCBhZXMoeD14LCB5PXkpLCBjb2xvciA9ICJsaWdodGdyZWVuIikgKyANCiAgbGFicyh4PSJFYXN0aW5nIChtKSIsIHk9Ik5vcnRoaW5nIChtKSIsIHRpdGxlPW1jcC5wb2ludHMkaWRlbnRpZmllcikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KSkgKw0KICBjb29yZF9zZih4bGltID0gYyg4MjAwMDAsIDgxNDI1MCksICB5bGltID0gYygxNjA0NTAwLCAxNjA4MDAwKSkNCm1jcC5wbG90ICsgYW5ub3RhdGUoInRleHQiLCB4ID0gODE1MDAwLCB5ID0gMTYwNzUwMCwgbGFiZWwgPSBwYXN0ZShyb3VuZChtY3Aub3V0QGRhdGEkYXJlYSwyKSwiaGEiKSwgDQogICAgICAgICAgICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiLCBzaXplID0gNSwgY29sb3IgPSAid2hpdGUiKQ0KYGBgDQoNClRoZSBhbmF0b215IG9mIHRoZSBzY3JpcHQgYWJvdmUgaXMgYXMgZm9sbG93czoNCg0KLSBgYGByZWFkLmNzdigpYGBgLCByZWFkIHRoZSBkYXRhIGZyb20gYSBnaXZlbiBcKi5jc3YgZmlsZQ0KLSB4eSwgY3JlYXRlIGEgY29vcmRpbmF0ZSBmaWxlIG9mIHRoZSBlYXN0aW5nIGFuZCBub3J0aGluZyBkYXRhDQotIGBgYFNwYXRpYWxQb2ludHNEYXRhRnJhbWUoKWBgYCwgcHJvamVjdCB0aGUgZGF0YSB0byBVVE0gWm9uZSA0Nw0KLSBgYGBtY3AoKWBgYCwgY29tcHV0ZXMgaG9tZSByYW5nZSB1c2luZyB0aGUgTWluaW11bSBDb252ZXggUG9seWdvbiBlc3RpbWF0b3INCi0gYGBgc3RfYXNfc2ZgYGAsIGNvbnZlcnRzIHRvIGEgZGF0YSBmcmFtZSBmb3IgcGxvdHRpbmcgd2l0aCBnZ3Bsb3QyDQotIGBgYGNvbG5hbWVzKClgYGAsIHJlbmFtZXMgdGhlIGNvbHVtbnMNCi0gYGBgYW5ub3RhdGVgYGAsIGFkZHMgdGV4dCB0byB0aGUgcGxvdA0KDQpUaGUgcmVzdCBvZiB0aGUgaW5mb3JtYXRpb24gY2FuIGJlIGZvdW5kIGJ5IGV4YW1pbmluZyB0aGUgaGVscCBtZW51IGZvciB0aGUgdmFyaW91cyBjb21tYW5kcyBvciBsb29raW5nIGF0IHRoZSBkaXNwbGF5IG9wdGlvbnMgZm9yIGVhY2ggcG9ydGlvbiBvZiB0aGUgc2NyaXB0Lg0KDQojIyMgS2VybmVsLURlbnNpdHkgRXN0aW1hdGlvbg0KDQpUaGUgc2FtZSBkZXNpZ24gYWJvdmUgY2FuIGJlIHVzZWQgdG8gY3JlYXRlIEtERSBwbG90cyBmb3IgZWFjaCBpbmRpdmlkdWFsIGluIHRoZSBkYXRhc2V0LiBCZWNhdXNlIHRoZSBwcm9jZXNzIGlzIGVzc2VudGlhbGx5IHRoZSBzYW1lLCBvbmx5IHBvcnRpb25zIG9mIHRoZSBzY3JpcHQgdGhhdCB3ZXJlIG5vdCBwcmV2aW91c2x5IGRlc2NyaWJlZCB3aWxsIGJlIGRpc2N1c3NlZC4NCg0KYGBge3IgS0RFIHBsb3QsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG89VFJVRSwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9Nn0NCiMjIERhdGEgZm9yIGEgc2luZ2xlIGluZGl2aWR1YWwNCm9waGEyIDwtIHJlYWQuY3N2KCdPUEhBMi5jc3YnKQ0KeCA8LSBhcy5kYXRhLmZyYW1lKG9waGEyJHV0bS5lYXN0aW5nKQ0KeSA8LSBhcy5kYXRhLmZyYW1lKG9waGEyJHV0bS5ub3J0aGluZykNCnh5IDwtIGMoeCx5KQ0KDQojIyBwcm9qZWN0aW5nIHRoZSBkYXRhIHRvIFVUTQ0KZGF0YS5wcm9qIDwtIFNwYXRpYWxQb2ludHNEYXRhRnJhbWUoeHksb3BoYTIsIHByb2o0c3RyaW5nID0gQ1JTKCIrcHJvaj11dG0gK3pvbmU9NDcgK2RhdHVtPVdHUzg0ICt1bml0cz1tICtub19kZWZzIikpDQp4eS5zcCA8LSBTcGF0aWFsUG9pbnRzKGRhdGEucHJvakBjb29yZHMpDQoNCiMjIEtERSBhbmFseXNpcyBhdCA5NSwgODUsIDc1DQprZGU8LWtlcm5lbFVEKHh5LnNwLCBoPSJocmVmIiwga2Vybj0iYml2bm9ybSIsIGdyaWQ9MTAwKQ0KdmVyOTUgPC0gZ2V0dmVydGljZXNocihrZGUsIDk1KQ0KdmVyODUgPC0gZ2V0dmVydGljZXNocihrZGUsIDg1KQ0KdmVyNzUgPC0gZ2V0dmVydGljZXNocihrZGUsIDc1KQ0KDQojIyBjb252ZXJ0aW5nIHRvIHNmIG9iamVjdCBhbmQgZGYNCmtkZS5wb2ludHMgPC0gY2JpbmQoKGRhdGEuZnJhbWUoZGF0YS5wcm9qQGNvb3JkcykpLG9waGEyJGluZGl2aWR1YWwubG9jYWwuaWRlbnRpZmllcikNCmNvbG5hbWVzKGtkZS5wb2ludHMpIDwtIGMoIngiLCJ5IiwiaWRlbnRpZmllciIpDQprZGU5NS5zZiA8LSBzdF9hc19zZih2ZXI5NSkNCmtkZTg1LnNmIDwtIHN0X2FzX3NmKHZlcjg1KQ0Ka2RlNzUuc2YgPC0gc3RfYXNfc2YodmVyNzUpDQoNCiMjbWFraW5nIHRoZSBwbG90DQprZGUucGxvdCA8LSBnZ3Bsb3QoKSArIHRoZW1lX2J3KCkgKw0KICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDAuNSwgdmp1c3QgPSAwLjUpKSArIA0KICBnZW9tX3NwYXRpYWxfcmdiKGRhdGEgPSB0aGFpbGFuZCwgYWVzKHg9eCwgeT15LCByPXJlZCwgZz1ncmVlbiwgYj1ibHVlKSkgKw0KICBnZW9tX3NmKGRhdGE9a2RlOTUuc2YsIGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC40KSArDQogIGdlb21fc2YoZGF0YT1rZGU4NS5zZiwgZmlsbCA9ICJncmVlbiIsIGFscGhhID0gMC4zKSArDQogIGdlb21fc2YoZGF0YT1rZGU3NS5zZiwgZmlsbCA9ICJ5ZWxsb3ciLCBhbHBoYSA9IDAuMikgKw0KICBnZW9tX3BvaW50KGRhdGE9a2RlLnBvaW50cywgYWVzKHg9eCwgeT15KSwgY29sb3IgPSAibGlnaHRncmF5IikgKyANCiAgbGFicyh4PSJFYXN0aW5nIChtKSIsIHk9Ik5vcnRoaW5nIChtKSIsIHRpdGxlPWtkZS5wb2ludHMkaWRlbnRpZmllcikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KSkgKw0KICBjb29yZF9zZih4bGltID0gYyg4MjE5MDAsIDgxNzAwMCksICB5bGltID0gYygxNjA1MDAwLCAxNjE1MDAwKSkNCmtkZS5wbG90ICsgYW5ub3RhdGUoInRleHQiLCB4ID0gODE4MDAwLCB5ID0gMTYxNDUwMCwgbGFiZWwgPSBwYXN0ZSgiOTUlICIscm91bmQodmVyOTUkYXJlYSwyKSwiaGEiKSwgDQogICAgICAgICAgICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiLCBzaXplID0gNCwgY29sb3IgPSAid2hpdGUiKQ0KYGBgDQoNClRoZSBhbmF0b215IG9mIHRoZSBmdW5jdGlvbiBhYm92ZSBpcyBhcyBmb2xsb3dzOg0KDQotIGBgYGtlcm5lbFVEKClgYGAsIGVzdGltYXRpb24gb2Yga2VybmVsIGhvbWUtcmFuZ2UNCi0gYGBgZ2V0dmVydGljZXNocigpYGBgLCBleHRyYWN0IGhvbWUtcmFuZ2UgY29udG91cg0KDQpUaGUgcmVzdCBvZiB0aGUgaW5mb3JtYXRpb24gY2FuIGJlIGZvdW5kIGJ5IGV4YW1pbmluZyB0aGUgaGVscCBtZW51IGZvciB0aGUgdmFyaW91cyBjb21tYW5kcyBvciBsb29raW5nIGF0IHRoZSBkaXNwbGF5IG9wdGlvbnMgZm9yIGVhY2ggcG9ydGlvbiBvZiB0aGUgc2NyaXB0Lg0KDQojIyMgQXV0b21hdGlvbiBVc2luZyBGdW5jdGlvbnMNCg0KRGVwZW5kaW5nIG9uIHlvdXIgZGVzaXJlZCByZXN1bHRzLCB0aGUgc3RlcHMgYWJvdmUgY2FuIGJlIGluY2x1ZGVkIGluIGEgZnVuY3Rpb24gdGhhdCB3aWxsIGFsbG93IHlvdSB0byBhdXRvbWF0ZSB0aGlzIHByb2Nlc3Mgb3ZlciBhIGxpc3Qgb2YgZmlsZXMgYW5kIGNyZWF0ZSBfbWNwXyBvciBfa2RlXyBhbmFseXNlcyBmb3IgYWxsIHRoZSBpbmRpdmlkdWFscyBzZXBhcmF0ZWx5LiBCYXNpY2FsbHksIHdlIGFyZSB0YWtpbmcgYWxsIG9mIHRoZSBlbGVtZW50cyBvZiB0aGUgc2NyaXB0cyBhYm92ZSwgbWFraW5nIHNvbWUgb2YgdGhlIHZhbHVlcyBnZW5lcmljIChlLmcuIGZpbGVuYW1lKSBhbmQgY3JlYXRpbmcgdGhlIHN0ZXBzIGZvciB0aGUgcHJvY2VzcyB0aGF0IGNhbiBiZSByZXBlYXRlZCBvdmVyIGEgbGlzdCBvZiBmaWxlcyB0aGF0IHlvdSBjcmVhdGVkIGVhcmxpZXIgaW4gdGhlIGV4ZXJjaXNlcy4gVGhlIG9iamVjdCBjYWxsZWQgYGBgZmlsZXNgYGAgY29udGFpbnMgYSBsaXN0IG9mIGFsbCB0aGUgXCouY3N2IGZpbGVzIGZvciBlYWNoIGluZGl2aWR1YWwuIFNvIHdlIHdpbGwgYXBwbHkgdGhlIGZ1bmN0aW9uLCBvdmVyIHRoZSBsaXN0LCB0byBhdXRvbWF0ZSB0aGUgYW5hbHlzaXMgYW5kIHZpc3VhbGl6YXRpb25zIGZvciBlYWNoIGluZGl2aWR1YWwuIEJlY2F1c2UgdGhlIF9rZGVfIHByb2Nlc3MgaXMgZXNzZW50aWFsbHkgdGhlIHNhbWUgYXMgYWJvdmUsIG9ubHkgcG9ydGlvbnMgb2YgdGhlIGZ1bmN0aW9uIHRoYXQgd2VyZSBub3QgcHJldmlvdXNseSBkZXNjcmliZWQgd2lsbCBiZSBkaXNjdXNzZWQuDQoNCmBgYHtyIGtkZSBwbG90IGF1dG9tYXRpb24sIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG89VFJVRSwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9Nn0NCiNydW4gS0RFIGNyZWF0aW5nIHBsb3QgZm9yIGVhY2ggaW5kaXZpZHVhbCB3aXRoIGEgcHJvZ3Jlc3MgYmFyDQprZGVfcmFzdGVyIDwtIGZ1bmN0aW9uKGZpbGVuYW1lKXsNCiAgZGF0YSA8LSByZWFkLmNzdihmaWxlID0gZmlsZW5hbWUpDQogIHggPC0gYXMuZGF0YS5mcmFtZShkYXRhJHV0bS5lYXN0aW5nKQ0KICB5IDwtIGFzLmRhdGEuZnJhbWUoZGF0YSR1dG0ubm9ydGhpbmcpDQogIHh5IDwtIGMoeCx5KQ0KICBkYXRhLnByb2ogPC0gU3BhdGlhbFBvaW50c0RhdGFGcmFtZSh4eSxkYXRhLCBwcm9qNHN0cmluZyA9IENSUygiK3Byb2o9dXRtICt6b25lPTQ3ICtkYXR1bT1XR1M4NCArdW5pdHM9bSArbm9fZGVmcyIpKQ0KICB4eS5zcCA8LSBTcGF0aWFsUG9pbnRzKGRhdGEucHJvakBjb29yZHMpDQogIGtkZTwta2VybmVsVUQoeHkuc3AsIGg9ImhyZWYiLCBrZXJuPSJiaXZub3JtIiwgZ3JpZD0xMDApDQogIHZlcjk1IDwtIGdldHZlcnRpY2VzaHIoa2RlLCA5NSkNCiAgdmVyODUgPC0gZ2V0dmVydGljZXNocihrZGUsIDg1KQ0KICB2ZXI3NSA8LSBnZXR2ZXJ0aWNlc2hyKGtkZSwgNzUpDQogIGtkZS5wb2ludHMgPC0gY2JpbmQoKGRhdGEuZnJhbWUoeHkpKSxkYXRhJGluZGl2aWR1YWwubG9jYWwuaWRlbnRpZmllcikNCiAgY29sbmFtZXMoa2RlLnBvaW50cykgPC0gYygieCIsInkiLCJpZGVudGlmaWVyIikNCiAga2RlOTUuc2YgPC0gc3RfYXNfc2YodmVyOTUpDQogIGtkZTg1LnNmIDwtIHN0X2FzX3NmKHZlcjg1KQ0KICBrZGU3NS5zZiA8LSBzdF9hc19zZih2ZXI3NSkNCiAga2RlLnBsb3QgPC0gZ2dwbG90KCkgKyB0aGVtZV9idygpICsgDQogICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAwLjUsIHZqdXN0ID0gMC41KSkgKyANCiAgICBnZW9tX3NwYXRpYWxfcmdiKGRhdGEgPSB0aGFpbGFuZCwgYWVzKHg9eCwgeT15LCByPXJlZCwgZz1ncmVlbiwgYj1ibHVlKSkgKw0KICAgIGdlb21fc2YoZGF0YT1rZGU5NS5zZiwgZmlsbCA9ICJibHVlIiwgYWxwaGEgPSAwLjQpICsNCiAgICBnZW9tX3NmKGRhdGE9a2RlODUuc2YsIGZpbGwgPSAiZ3JlZW4iLCBhbHBoYSA9IDAuMykgKw0KICAgIGdlb21fc2YoZGF0YT1rZGU3NS5zZiwgZmlsbCA9ICJ5ZWxsb3ciLCBhbHBoYSA9IDAuMikgKw0KICAgIGdlb21fcG9pbnQoZGF0YT1rZGUucG9pbnRzLCBhZXMoeD14LCB5PXkpLCBjb2xvciA9ICJsaWdodGdyYXkiKSArIA0KICAgIGNvb3JkX3NmKHhsaW0gPSBjKDgyMjAwMCwgODEzOTUwKSwgIHlsaW0gPSBjKDE2MTYyNTAsIDE2MDQ1MDApKSArDQogICAgbGFicyh4PSJFYXN0aW5nIChtKSIsIHk9Ik5vcnRoaW5nIChtKSIsIHRpdGxlPWtkZS5wb2ludHMkaWRlbnRpZmllcikgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwLjUpKQ0KICBrZGUucGxvdCArIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDgxNTAwMCwgeSA9IDE2MTYwMDAsIGxhYmVsID0gcGFzdGUoIjk1JSAiLHJvdW5kKHZlcjk1JGFyZWEsMiksIiBoYSIpLCANCiAgICAgICAgICAgICAgICAgICAgICBmb250ZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDUsIGNvbG9yID0gIndoaXRlIikNCn0NCg0KcGJsYXBwbHkoZmlsZXMsIGtkZV9yYXN0ZXIpDQpgYGANCjxwIGFsaWduPSJjZW50ZXIiPg0KS0RFIEFuYWx5c2lzIE9QSEExIHwgS0RFIEFuYWx5c2lzIE9QSEEyDQotIHwgLSANCiFbXSguL29waGExX2F1dG8ucG5nICJLREUgZm9yIE9QSEExIikgfCAhW10oLi9vcGhhMl9hdXRvLnBuZyAiS0RFIGZvciBPUEhBMiIpDQo8L3A+DQoNClRoZSBhbmF0b215IG9mIHRoZSBzY3JpcHQgYWJvdmUgaXMgYXMgZm9sbG93czogdGhlIGBgYGtkZV9yYXN0ZXIgPC0gZnVuY3Rpb24oZmlsZW5hbWUpey4uLmBgYCBmdW5jdGlvbiBydW5zIGZvciBlYWNoIGZpbGUgbGlzdGVkIGZyb20gdGhlIHByb2plY3QgZGlyZWN0b3J5IGluIHRoZSBgYGBmaWxlc2BgYCBsaXN0IGNvbXBsZXRlIHRoZSBmb2xsb3dpbmcgY29tbWFuZHMNCg0KLSBgYGByZWFkLmNzdihmaWxlID0gZmlsZW5hbWUpYGBgLCB0aGVyZSBpcyBubyBzcGVjaWZpYyBmaWxlIG5hbWUgbmVlZGVkIGFzIGl0IHdpbGwgdXNlIGVhY2ggXCouY3N2IGluIHRoZSBsaXN0IGFzIGEgbmV3IGZpbGUNCi0gYGBgcGJhcHBseSgpYGBgLCBydW5zIHRoZSBmdW5jdGlvbiBgYGBrZGVfcmFzdGVyYGBgIG92ZXIgdGhlIGxpc3Qgb2YgXCouY3N2IGluIHRoZSBmaWxlcyBvYmplY3QNCg0KVGhlIHJlc3Qgb2YgdGhlIGluZm9ybWF0aW9uIGNhbiBiZSBmb3VuZCBieSBleGFtaW5pbmcgdGhlIGhlbHAgbWVudSBmb3IgdGhlIHZhcmlvdXMgY29tbWFuZHMgb3IgbG9va2luZyBhdCB0aGUgZGlzcGxheSBvcHRpb25zIGZvciBlYWNoIHBvcnRpb24gb2YgdGhlIHNjcmlwdC4NCg0KPiBUcnkgaXQgeW91cnNlbGYhIFVzaW5nIHRoZSBleGFtcGxlIHNjcmlwdCBhYm92ZSwgYW5kIGZvbGxvd2luZyB0aGUgZXhhbXBsZSBmdW5jdGlvbnMsIGNyZWF0ZSBhIGxvb3BpbmcgZnVuY3Rpb24gd2l0aCBgYGBwYmxhcHBseWBgYCB0byBydW4gdGhlIG1jcCBhbmFseXNpcyBmb3IgZWFjaCBpbmRpdmlkdWFsIGluIHRoZSBmaWxlcyBsaXN0Lg0KDQojIyBDb250aW51b3VzLVRpbWUgTW92ZW1lbnQgTW9kZWxpbmcNCg0KVGhlcmUgaXMgYSBtb3JlIHBvd2VyZnVsIHBhY2thZ2UgY2FsbGVkIGBgYGN0bW1gYGAgdGhhdCBjYW4gYmUgdXNlZCBmb3IgdGhlIGNyZWF0aW9uIG9mICBjb252ZW50aW9uYWwga2VybmVsLWRlbnNpdHkgZXN0aW1hdGVzIGFzIHdlbGwgYXMgdW5pZm9ybWx5IHdlaWdodGVkIGtlcm5lbC1kZW5zaXR5IGVzdGltYXRlcy4gV2hpbGUgSSBhbSBsZXNzIGZhbWlsaWFyIHdpdGggdGhlIHZhcmlhYmxlcywgb2JqZWN0cywgYW5kIHNldHRpbmcgZm9yIGVhY2ggbW9kZWwsIEkgd2FzIGFibGUgdG8gZm9sbG93IGFsb25nIHdpdGggdGhlIFt2aWduZXR0ZV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2N0bW0vdmlnbmV0dGVzL2FrZGUuaHRtbCkgZm9yIEF1dG9jb3JyZWxhdGVkIEtlcm5lbCBEZW5zaXR5IEVzdGltYXRpb24gb24gdGhlIFtwYWNrYWdlIGRldGFpbHNdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9jdG1tLykgdG8gY3JlYXRlIGEgc2ltaWxhciBfa2RlXyBhbmFseXNpcyBhcyBhYm92ZS4gQWxsIG9mIHRoZSBzYW1lIG1ldGhvZHMgYXV0b21hdGlvbiBtZXRob2RzIGNhbiBiZSBlbXBsb3llZCwgaG93ZXZlciwgdGhlcmUgYXJlIHNvbWUgZGlmZmVyZW5jZSB0byBub3RlLg0KDQpGb3IgdGhpcyBwb3J0aW9uIG9mIHRoZSBleGVyY2lzZXMgd2Ugd2lsbCBwcmVwYXJlIHRoZSB0d28gT1BIQSBcKi5jc3YgZmlsZXMgZm9yIGFuYWx5c2lzIHVzaW5nIGBgYGFrZGVgYGAgaW4gdGhlIGBgYGN0bW1gYCBwYWNrYWdlLiANCg0KYGBge3IgY3RtbSB0ZWxlbWV0cnksIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG89VFJVRSwgcmVzdWx0cz0naGlkZSd9DQojIyBjb252ZXJ0IG1vdmViYW5rIHRvIHRlbGVtZXRyeSBkYXRhDQpvcGhhLnRlbGVtIDwtIGFzLnRlbGVtZXRyeShkYXRhLCBwcm9qZWN0aW9uID0gIitwcm9qPXV0bSArem9uZT00NyArZGF0dW09V0dTODQgK3VuaXRzPW0gK25vX2RlZnMiKQ0Kb3BoYTEudGVsZW0gPC0gYXMudGVsZW1ldHJ5KCdPUEhBMS5jc3YnLCBwcm9qZWN0aW9uID0gIitwcm9qPXV0bSArem9uZT00NyArZGF0dW09V0dTODQgK3VuaXRzPW0gK25vX2RlZnMiKQ0Kb3BoYTIudGVsZW0gPC0gYXMudGVsZW1ldHJ5KCJPUEhBMi5jc3YiLCBwcm9qZWN0aW9uID0gIitwcm9qPXV0bSArem9uZT00NyArZGF0dW09V0dTODQgK3VuaXRzPW0gK25vX2RlZnMiKQ0KYGBgDQoNCllvdSBjYW4gZXhhbWluZSB0aGUgZGF0YSBpbiB0aGUgZW52aXJvbm1lbnQgdG8gc2VlIGhvdyB0aGUgZGF0YSB3YXMgcmVzdHJ1Y3R1cmVkIHRvIGJlIGF2YWlsYWJsZSBmb3IgZnVydGhlciBhbmFseXNpcy4gTm90aWNlIHRoYXQgYSBwcm9qZWN0aW9uIHR5cGUgaXMgbGlzdGVkIGZvciBlYWNoIG9iamVjdC4gVGhpcyB3YXMgdXNlZCB0byBlbnN1cmUgdGhlIGRhdGEgd291bGQgYmUgZ2VvZ3JhcGhpY2FsbHkgbG9jYXRlZCByYXRoZXIgdGhhbiBwbGFjZWQgaW4gZ2VuZXJhbCBzcGFjZSBieSBtZXRlcnMuIA0KDQpgYGB7ciBjdG1tIGFrZGUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG89VFJVRSwgcmVzdWx0cz0naGlkZSd9DQojIGNhbGN1bGF0ZSBmaXQgb2JqZWN0DQpvcGhhMi5ndWVzcyA8LSBjdG1tLmd1ZXNzKG9waGEyLnRlbGVtLCBpbnRlcmFjdGl2ZT1GQUxTRSkNCg0KIyBjb21wdXRlIGFrZGUgYW5kIGdldCBzdW1tYXJ5IGluZm9ybWF0aW9uDQpvcGhhMi5vdWYgPC0gY3RtbS5maXQob3BoYTIudGVsZW0sIG9waGEyLmd1ZXNzKQ0Kd0FLREUub3BoYTIgPC0gYWtkZShvcGhhMi50ZWxlbSwgb3BoYTIub3VmLCB3ZWlnaHRzPVRSVUUpDQpzdW1tYXJ5KHdBS0RFLm9waGEyKQ0KDQojIGJhc2ljIHBsb3QNCnBsb3Qob3BoYTIudGVsZW0sIFVEPXdBS0RFLm9waGEyKQ0KYGBgDQoNCllvdSBjYW4gc2VlIHRoZSBkZXRhaWxzIG9mIGVhY2ggb2YgdGhlIG9iamVjdCBjcmVhdGVkIGluIHlvdXIgZW52aXJvbm1lbnQuIFRoZSBhbmFseXNpcyBjcmVhdGUgYSBjbGFzcyBgYGBVRGBgYCBvYmplY3QgdGhhdCBjYW5ub3QgYmUgdXNlZCBpbiBgYGBnZ3Bsb3RgYGAgd2l0aG91dCBjb252ZXJzaW9uLiBBZGRpdGlvbmFsbHksIHdlIHdpbGwgY3JlYXRlIGFuIG9iamVjdCBzcGVjaWZpY2FsbHkgdG8gdXNlIGZvciB0aGUgaW5jbHVzaW9uIG9mIGFyZWEgb24gdGhlIGZpbmFsIHBsb3QuDQoNCmBgYHtyIGFrZGUgcGxvdCwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjYWNoZT1UUlVFfQ0KIyBjb252ZXJ0IHRvIFNQREYgdG8gY29udmVydCB0byBzZiBvYmplY3QgZm9yIHBsb3R0aW5nIGluIGdncGxvdA0KdWQuc3AgPC0gU3BhdGlhbFBvbHlnb25zRGF0YUZyYW1lLlVEKHdBS0RFLm9waGEyKQ0KdWQuc2YgPC0gc3RfYXNfc2YodWQuc3ApDQoNCm9waGEyLnNmIDwtIHN0X2FzX3NmKG9waGEyLCBjb29yZHMgPSBjKCJ1dG0uZWFzdGluZyIsInV0bS5ub3J0aGluZyIpKSAlPiUgc3Rfc2V0X2NycygzMjY0NykNCg0KIyBhZGtlIHN1bW1hcnkgZGF0YQ0KYWtkZS5zdW1tYXJ5IDwtIHN1bW1hcnkod0FLREUub3BoYTIpDQpha2RlLnN1bW1hcnlbWyJDSSJdXVszXQ0KDQojIGdncGxvdCBmb3IgYWtkZQ0KY3RtbS5ha2RlIDwtIGdncGxvdCgpICsgZ2VvbV9zcGF0aWFsX3JnYihkYXRhID0gdGhhaWxhbmQuZnVsbCwgYWVzKHg9eCwgeT15LCByPXJlZCwgZz1ncmVlbiwgYj1ibHVlKSkgKw0KICBnZW9tX3NmKGRhdGEgPSB1ZC5zZlszLF0sIGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC40KSArIA0KICBnZW9tX3NmKGRhdGEgPSB1ZC5zZlsyLF0sIGZpbGwgPSAiZ3JlZW4iLCBhbHBoYSA9IDAuMykgKyANCiAgZ2VvbV9zZihkYXRhID0gdWQuc2ZbMSxdLCBmaWxsID0gInllbGxvdyIsIGFscGhhID0gMC4yKSArDQogIGdlb21fc2YoZGF0YSA9IG9waGEyLnNmLCBjb2xvciA9ICJsaWdodGdyYXkiKSArIA0KICBjb29yZF9zZih4bGltID0gYyg4MjU1MDAsIDgxMzUwMCksICB5bGltID0gYygxNjE4MDAwLCAxNjAyMDAwKSkgKw0KICBsYWJzKHg9IkxvbmdpdHVkZSIsIHk9IkxhdGl0dWRlIiwgdGl0bGUgPSBvcGhhMi5zZiRpbmRpdmlkdWFsLmxvY2FsLmlkZW50aWZpZXIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBoanVzdCA9IDAuNSkpICsNCiAgdGhlbWVfYncoKSArIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMC41LCB2anVzdCA9IDAuNSkpDQpjdG1tLmFrZGUgKyBhbm5vdGF0ZSgidGV4dCIsIHggPSA4MTUwMDAsIHkgPSAxNjE3MDAwLCANCiAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIkFyZWEgIixyb3VuZChha2RlLnN1bW1hcnlbWyJDSSJdXVszXSwyKSwiIHNxa20iKSwNCiAgICAgICAgICAgICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiLCBzaXplID0gNSwgY29sb3IgPSAid2hpdGUiKQ0KYGBgDQoNClRoZSBkZXRhaWxzIG9mIHRoaXMgYW5hbHlzaXMgYXJlIHByZXR0eSBuZXcgdG8gbWUgc28gSSBkb24ndCB5ZXQgaGF2ZSBhIHNvbGlkIGdyYXNwIG9uIHRoZSB2YXJpb3VzIGNvbXBvbmVudHMgb2YgdGhlIHNjcmlwdCwgYnV0IGlmIHlvdSB3YW50IG1vcmUgaW5mb3JtYXRpb24geW91IGNhbiB2aWV3Og0KDQo+IEVkZWxraW5kLCBKLkwuLCBTdGFsa2VyLCBKLkIuLCBCZWNrLCBELkQuLCBEZW5hcmRvLCBELkYuLCBFbXBsaWRnZSwgUC5HLiwgR2FsbGFyZG8sIEwuLCBHZW50cnksIEMuTS4sIEdvb2RlLCBNLkouLCBKZW5uaW5ncywgUi5ELiwgSm9uZXMuIEouTC4sIEt3aWF0a293c2tpLCBNLkEuLCBOb3dhaywgRS5NLiwgUmVwcCwgUi5BLiwgU2NodWV0dCwgRy5XLiwgU3VsbGl2YW4sIEIuSy4sIFRyYWN5LCBDLlIuLCBhbmQgR2llbmdlciwgQy5NLiAoMjAyNSkgRW52aXJvbm1lbnRhbCBJbmZsdWVuY2VzIG9uIFNwYWNlIFVzZSBvZiBHaWxhIE1vbnN0ZXJzIChIZWxvZGVybWEgc3VzcGVjdHVtKS4gSGVycGV0b2xvZ2ljYS4gaHR0cHM6Ly9kb2kub3JnLzEwLjE2NTUvSGVycGV0b2xvZ2ljYS1ELTI0LTAwMDUzDQoNClRoaXMgd2FzIHNpbXBseSBhbiBleGFtcGxlIG9mIGhvdyB0byBydW4gdGhlIGFuYWx5c2lzIGFuZCBwcmVwYXJlIHRoZSByZXN1bHRpbmcgb2JqZWN0cyBmb3IgdmlzdWFsaXphdGlvbi4gDQoNCiMgWU9VUiBUVVJOIQ0KDQpOb3cgaXTigJlzIHlvdXIgdHVybiEgQWx0aG91Z2ggc29tZSBvZiB0aGlzIG1pZ2h0IG5vdCBiZSBhcHBsaWNhYmxlIHRvIHlvdXIgdGhlc2lzIHJlc2VhcmNoLCB0cnkgdGhpcyBpbmZvcm1hdGlvbiBvdXQgb24gYSBkYXRhc2V0IG9mIHlvdXIgY2hvaWNlIGZyb20gYW55IG9mIHRoZSBzb3VyY2VzIGFib3ZlLiBJZiB5b3UgY2FuIGFwcGx5IHRoaXMgdG8geW91ciB0aGVzaXMsIHRoZW4gYWRkIGl0IHRvIHlvdXIgd2Vic2l0ZSEgT3RoZXJ3aXNlIGNyZWF0ZSBhIG5ldyByZXBvc2l0b3J5IGZvciB5b3VyIHByZXNlbnRhdGlvbiBpbiB0aGUgbmV4dCBjbGFzcy4=