In this topic we will explore spatial data in R in order to visualize the spatial aspect of porpoise distribution.Besides the generic R functions, there are many specialized packages to visualize and analyze spatial data.

1. Making a map of the BPNS

Combining ggplot with the R packages that facilitate the handling of geospatial data, as maptools, rgdal, rgeos and mapproj, enables the construction of simple and complex maps.

library(maptools)
library(rgdal)
library(rgeos)
library(mapproj)

Using these packages we will construct a simple map of the Belgian Part of the North Sea (BPNS).

1.1 Read shapefiles

A common data format for geospatial data is the shapefile. In a shapefile, geospatial data are described as points, lines and polygons and can be analysed as such in GIS software. Marine Regions provides access to many shapefiles through the gazetteer. The folder Mapping contains some useful shapefiles.

list.files("Data day 1/Mapping")
[1] "banks"             "Coastal banks"     "eez"               "Hinder banks"     
[5] "netherlands_coast" "world_bay_gulf"    "Zeeland banks"    

The world_bay_gulf folder and file contains the shapefile of the Southern Bight. Below you can find three ways to import it into the R Environment. The readShapePoly() function is one of many to read spatial data. This particular function of the maptools package already specifies this particular shapefile is a polygon. The recommended readOGR() recognizes the type of spatial data automatically.

bight <- readOGR("Data day 1/Mapping/world_bay_gulf", layer = "world_bay_gulf")
OGR data source with driver: ESRI Shapefile 
Source: "Data day 1/Mapping/world_bay_gulf", layer: "world_bay_gulf"
with 1 features
It has 5 fields

Looking at the class, structure and plot of the shapefile allows to better understand the data type.

class(bight)
[1] "SpatialPolygonsDataFrame"
attr(,"package")
[1] "sp"
str(bight)

plot(bight)

1.2 Plot spatial data in ggplot

The fortify() function allows to easily convert an object of a spatial class to a regular dataframe. Though this transformation is not necessary for making a map in R, it is vital to map data in ggplot.

bightfort <- fortify(bight)
Regions defined for each Polygons
str(bightfort)
'data.frame':   25290 obs. of  7 variables:
 $ long : num  4.99 4.99 4.97 4.97 4.96 ...
 $ lat  : num  52.4 52.4 52.4 52.4 52.4 ...
 $ order: int  1 2 3 4 5 6 7 8 9 10 ...
 $ hole : logi  FALSE FALSE FALSE FALSE FALSE FALSE ...
 $ piece: Factor w/ 64 levels "1","2","3","4",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ id   : chr  "0" "0" "0" "0" ...
 $ group: Factor w/ 64 levels "0.1","0.2","0.3",..: 1 1 1 1 1 1 1 1 1 1 ...

Now, let’s start exploring these spatial data with ggplot! In order to plot the Southern Bight, we want a plot that connects the different points in our fortified data frame.

ggplot() + geom_path(data = bightfort, aes(x = long, y = lat))

This first try seems slightly demotivating! However, the issue is easily fixed! When fortifying your spatial data, the new data frame will group certain points. It is therefore important to maintain this grouping of the data, simply by telling ggplot group = group.

ggplot() + geom_path(data = bightfort, aes(x = long, y = lat, group = group))

1.3 Add layers to the plot

Now, we can add more information on this plot.The Belgian EEZ for example.

eez <- readOGR("Data day 1/Mapping/eez/eez.shp")

eez <- readOGR("Data day 1/Mapping/eez/eez.shp")
eezfort <- fortify(eez)
eezfort <- fortify(eez)
ggplot() + 
  geom_path(data = bightfort, aes(x = long, y = lat, group = group)) +
  geom_path(data = eezfort, aes(x = long, y = lat, group = group))

So now we read the EEZ shapefile, transformed it to a dataframe and simply added it to our plot with geom_path(). Be careful always to define x as longitude and y as latitude!

With coord_map(), R recognizes the plot is a map and will scale the dimensions of the axex correctly.

ggplot() + 
  coord_map() +
  geom_path(data = bightfort, aes(x = long, y = lat, group = group)) +
  geom_path(data = eezfort, aes(x = long, y = lat, group = group))

You can also define the limits of the axes with coord_map().

ggplot() + 
  coord_map(xlim = c(2,3.5), ylim = c(51,52)) +
  geom_path(data = bightfort, aes(x = long, y = lat, group = group)) +
  geom_path(data = eezfort, aes(x = long, y = lat, group = group))

Themes in R allow for prettier maps! The black and white theme can be applied with theme_bw().

ggplot() + 
  theme_bw() +
  theme(panel.background = element_rect(fill = "#0093b4"),
        panel.grid.major = element_line(linetype = "blank"),
        panel.grid.minor = element_line(linetype = "blank"),
        axis.title = element_blank(),
        axis.text = element_text(size = 16)) +
  coord_map(xlim = c(2,3.5), ylim = c(51,52)) +
  geom_path(data = bightfort, aes(x = long, y = lat, group = group)) +
  geom_path(data = eezfort, aes(x = long, y = lat, group = group))

Using theme(), a number of lay-out features of a plot can be determined. In the code above, we determined the background colour, removed the grid lines, removed the axis titles and changed the font size of the axis text. The numerous options for the use of the theme() function are documented here.

The background colour was defined here with a colour code. However, fill = “blue” would also work. More info on colours in R can be found in this useful cheatsheet and in this catalogue.

ggplot() + 
  theme_bw() +
  theme(panel.background = element_rect(fill = "#0093b4"),
        panel.grid.major = element_line(linetype = "blank"),
        panel.grid.minor = element_line(linetype = "blank"),
        axis.title = element_blank(),
        axis.text = element_text(size = 16)) +
  coord_map(xlim = c(2,3.5), ylim = c(51,52)) +
  geom_polygon(aes(x=long, y=lat, group=group), data = bightfort, fill = "white") +
  geom_path(data = bightfort, aes(x = long, y = lat, group = group)) +
  geom_path(data = eezfort, aes(x = long, y = lat, group = group))

When we define the colour (fill, possible confusion!) of the sea as “white”, we see there is an issue with Zeeland on our map. Apparently the shapefile does recognize the points of the peninsula to be part of the Southern Bight (and can draw a path between them), but it does not group them correctly. A possible solution involves the import of a file describing the coast of the Netherlands.

ggplot() + 
  theme_bw() +
  theme(panel.background = element_rect(fill = "#0093b4"),
        panel.grid.major = element_line(linetype = "blank"),
        panel.grid.minor = element_line(linetype = "blank"),
        axis.title = element_blank(),
        axis.text = element_text(size = 16)) +
  coord_map(xlim = c(2,3.5), ylim = c(51,52)) +
  geom_polygon(aes(x=long, y=lat, group=group), data = bightfort, fill = "white") +
  geom_polygon(aes(x=long, y=lat, group=group), data = netherlands_coastfort, fill = "#0093b4") +
  geom_path(data = bightfort, aes(x = long, y = lat, group = group)) +
  geom_path(data = eezfort, aes(x = long, y = lat, group = group))

Land and sea are now in a different colour! However, the map is still quite sober. Next up: let’s add sand banks!

ggplot() + 
  theme_bw() +
  theme(panel.background = element_rect(fill = "#0093b4"),
        panel.grid.major = element_line(linetype = "blank"),
        panel.grid.minor = element_line(linetype = "blank"),
        axis.title = element_blank(),
        axis.text = element_text(size = 16)) +
  coord_map(xlim = c(2,3.5), ylim = c(51,52)) +
  geom_polygon(aes(x=long, y=lat, group=group), data = bightfort, fill = "white") +
  geom_polygon(aes(x=long, y=lat, group=group), data = netherlands_coastfort, fill = "#0093b4") +
  geom_path(data = bightfort, aes(x = long, y = lat, group = group)) +
  geom_path(data = eezfort, aes(x = long, y = lat, group = group)) +
  geom_polygon(aes(x=long, y=lat, group=group), data = banksfort, fill = "#016483")

Including sand banks in our map facilitates orientation and is also prettier. Note that the order of geom_ arguments matters: the banks are now on top of the EEZ. Let’s add some more sand banks!

ggplot() + 
  theme_bw() +
  theme(panel.background = element_rect(fill = "#0093b4"),
        panel.grid.major = element_line(linetype = "blank"),
        panel.grid.minor = element_line(linetype = "blank"),
        axis.title = element_blank(),
        axis.text = element_text(size = 16)) +
  coord_map(xlim = c(2,3.5), ylim = c(51,52)) +
  geom_polygon(aes(x=long, y=lat, group=group), data = bightfort, fill = "white") +
  geom_polygon(aes(x=long, y=lat, group=group), data = netherlands_coastfort, fill = "#0093b4") +
  geom_path(data = bightfort, aes(x = long, y = lat, group = group)) +
  geom_path(data = eezfort, aes(x = long, y = lat, group = group)) +
  geom_polygon(aes(x=long, y=lat, group=group), data = banksfort, fill = "#016483") +
  geom_polygon(aes(x=long, y=lat, group=group), data = bankszeelandfort, fill = "#016483") +
  geom_polygon(aes(x=long, y=lat, group=group), data = bankscoastalfort, fill = "#016483") +
  geom_polygon(aes(x=long, y=lat, group=group), data = bankshinderfort, fill = "#016483")

We now have a simple map of the BPNS (with some nice VLIZ colours)! This code for making the map is neither the only, neither the best way, but it provides a simple outline for future reference!

1.4 Compile the code to construct the map in a function

We can join all this code in a function to load all shape files and plot the map.

load_shapes_plot_map() now makes our map with only one line of code!

load_shapes_plot_map()

You can also load all shapefiles in the environment and construct the map seperately. This might be the favoured option when you plot several maps.

bankscoastal <- readOGR("Data day 1/Mapping/Coastal banks/banks.shp")

bankscoastal <- readOGR("Data day 1/Mapping/Coastal banks/banks.shp")
bankshinder <- readOGR("Data day 1/Mapping/Hinder banks/banks.shp")
bankshinder <- readOGR("Data day 1/Mapping/Hinder banks/banks.shp")
netherlands_coast <- readOGR("Data day 1/Mapping/netherlands_coast/world_countries_coasts.shp")
bankscoastal <- readOGR("Data day 1/Mapping/Coastal banks/banks.shp")
bankshinder <- readOGR("Data day 1/Mapping/Hinder banks/banks.shp")
netherlands_coast <- readOGR("Data day 1/Mapping/netherlands_coast/world_countries_coasts.shp")
eezfort <- fortify(eez)
eezfort <- fortify(eez)
banksfort <- fortify(banks)
banksfort <- fortify(banks)
bightfort <- fortify(bight)
bightfort <- fortify(bight)
bankszeelandfort <- fortify(bankszeeland)
netherlands_coast <- readOGR("Data day 1/Mapping/netherlands_coast/world_countries_coasts.shp")
eezfort <- fortify(eez)
banksfort <- fortify(banks)
bightfort <- fortify(bight)
bankszeelandfort <- fortify(bankszeeland)
bankscoastalfort <- fortify(bankscoastal)
bightfort <- fortify(bight)
bankszeelandfort <- fortify(bankszeeland)
bankscoastalfort <- fortify(bankscoastal)
bankshinderfort <- fortify(bankshinder)
banksfort <- fortify(banks)
bightfort <- fortify(bight)
bankszeelandfort <- fortify(bankszeeland)
bankscoastalfort <- fortify(bankscoastal)
bankshinderfort <- fortify(bankshinder)
netherlands_coastfort <- fortify(netherlands_coast)
eezfort <- fortify(eez)
banksfort <- fortify(banks)
bightfort <- fortify(bight)
bankszeelandfort <- fortify(bankszeeland)
bankscoastalfort <- fortify(bankscoastal)
bankshinderfort <- fortify(bankshinder)
netherlands_coastfort <- fortify(netherlands_coast)
netherlands_coastfort <- filter(netherlands_coastfort, lat >51.36)

plot_map()

2. Plot the station locations on the map

First off, we group our station data in a new data frame stationdf. %>% creates a “pipeline” of code. This is an other way of formulating the combination of code to summarize a data frame.

Using plot_map, we can explore the location of the stations in our data.

plot_map() + geom_point(data = stationdf, aes(Longitude,Latitude), size = 4, colour = "red")

Now, we can add the station names.

plot_map() + geom_point(data = stationdf, aes(Longitude,Latitude), size = 4, colour = "red") + 
  geom_label(data = stationdf, aes(Longitude +0.01,Latitude,label = Station), hjust = 0)

This is very messy… By using the package ggrepel, you can avoid overlapping labels or text.

plot_map() + geom_point(data = stationdf, aes(Longitude,Latitude), size = 4, colour = "red") + 
  geom_label_repel(data = stationdf, aes(Longitude +0.01,Latitude,label = Station))

geom_label_repel() does the job, but our plot is still a bit messy. We can try to plot zones instead.

plot_map() + geom_point(data = zonedf, aes(Longitude,Latitude), size = 4, colour = "red") + 
  geom_label_repel(data = zonedf, aes(Longitude +0.01,Latitude,label = Zone))

3. Spatial data exploration

In order to plot detection related variables spatially, we first summarise them for each zone.

3.1 Mapping detections

plot_map() + geom_point(data = zonedf, aes(Longitude,Latitude, size = Milliseconds), colour = "red")

plot_map() + geom_point(data = zonedf, aes(Longitude,Latitude, size = Number_clicks_filtered), colour = "red")

plot_map() + geom_point(data = zonedf, aes(Longitude,Latitude, size = Dpm), colour = "red")

plot_map() + geom_point(data = zonedf, aes(Longitude,Latitude, size = Dp10m), colour = "red")

plot_map() + geom_point(data = zonedf, aes(Longitude,Latitude, size = Dph), colour = "red")

plot_map() + geom_point(data = zonedf, aes(Longitude,Latitude, size = Click_frequency), colour = "red")

plot_map() + geom_point(data = zonedf, aes(Longitude,Latitude, size = Click_intensity), colour = "red")

An alternative to first grouping and summarizing the data, is to use the group argument in ggplot.

plot_map() + geom_point(data = poddata_day, aes(Longitude,Latitude, size = Click_frequency, group=Zone), colour = "red")

3.2 Visualize spatiotemporal patterns

In order to get an idea of the porpoise distribution each month, we first summarize the data per zone and month.

We can now visualize the distribution pattern per month.

plot_map() + geom_point(data = zonedf[zonedf$Month == 1,], aes(Longitude,Latitude, size = Click_frequency), colour = "red") + ggtitle(1)

Instead of making 12 plots in 12 lines of code, we can make a list of plots.

lapply(unique(zonedf$Month), function(x){
  plot_map() + geom_point(data = zonedf[zonedf$Month == x,], aes(Longitude,Latitude, size = Click_frequency), colour = "red") + ggtitle(x)
})

An easy alternative to plot in chronological order:

In order to have the results of the 12 months displayed in one plot, we can apply facet_wrap().

plot_map() + geom_point(data = zonedf, aes(Longitude,Latitude, size = Click_frequency), colour = "red") + facet_wrap(~Month)
LS0tDQp0aXRsZTogIkMtUE9EIGRhdGEgd29ya3Nob3A6IE1hcHBpbmciDQphdXRob3I6ICJWTElaIC0gRmxhbmRlcnMgTWFyaW5lIEluc3RpdHV0ZSINCmRhdGU6ICJPY3RvYmVyIDUtNiwgMjAxNyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCkluIHRoaXMgdG9waWMgd2Ugd2lsbCBleHBsb3JlIHNwYXRpYWwgZGF0YSBpbiBSIGluIG9yZGVyIHRvIHZpc3VhbGl6ZSB0aGUgc3BhdGlhbCBhc3BlY3Qgb2YgcG9ycG9pc2UgZGlzdHJpYnV0aW9uLkJlc2lkZXMgdGhlIGdlbmVyaWMgUiBmdW5jdGlvbnMsIHRoZXJlIGFyZSBtYW55IHNwZWNpYWxpemVkIFtwYWNrYWdlc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3ZpZXdzL1NwYXRpYWwuaHRtbCkgdG8gdmlzdWFsaXplIGFuZCBhbmFseXplIHNwYXRpYWwgZGF0YS4NCg0KIyAxLiBNYWtpbmcgYSBtYXAgb2YgdGhlIEJQTlMNCkNvbWJpbmluZyAqKmdncGxvdCoqIHdpdGggdGhlIFIgcGFja2FnZXMgdGhhdCBmYWNpbGl0YXRlIHRoZSBoYW5kbGluZyBvZiBnZW9zcGF0aWFsIGRhdGEsIGFzIFttYXB0b29sc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL21hcHRvb2xzL21hcHRvb2xzLnBkZiksIFtyZ2RhbF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3JnZGFsL3JnZGFsLnBkZiksIFtyZ2Vvc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3JnZW9zL3JnZW9zLnBkZikgYW5kIFttYXBwcm9qXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvbWFwcHJvai9tYXBwcm9qLnBkZiksIGVuYWJsZXMgdGhlIGNvbnN0cnVjdGlvbiBvZiBzaW1wbGUgYW5kIGNvbXBsZXggbWFwcy4NCmBgYHtyLCBpbmNsdWRlID0gVH0NCmxpYnJhcnkobWFwdG9vbHMpDQpsaWJyYXJ5KHJnZGFsKQ0KbGlicmFyeShyZ2VvcykNCmxpYnJhcnkobWFwcHJvaikNCmBgYA0KVXNpbmcgdGhlc2UgcGFja2FnZXMgd2Ugd2lsbCBjb25zdHJ1Y3QgYSBzaW1wbGUgbWFwIG9mIHRoZSBCZWxnaWFuIFBhcnQgb2YgdGhlIE5vcnRoIFNlYSAoQlBOUykuDQoNCiMjIDEuMSBSZWFkIHNoYXBlZmlsZXMNCkEgY29tbW9uIGRhdGEgZm9ybWF0IGZvciBnZW9zcGF0aWFsIGRhdGEgaXMgdGhlIHNoYXBlZmlsZS4gSW4gYSBzaGFwZWZpbGUsIGdlb3NwYXRpYWwgZGF0YSBhcmUgZGVzY3JpYmVkIGFzIHBvaW50cywgbGluZXMgYW5kIHBvbHlnb25zIGFuZCBjYW4gYmUgYW5hbHlzZWQgYXMgc3VjaCBpbiBHSVMgc29mdHdhcmUuIFtNYXJpbmUgUmVnaW9uc10oaHR0cDovL3d3dy5tYXJpbmVyZWdpb25zLm9yZy8pIHByb3ZpZGVzIGFjY2VzcyB0byBtYW55IHNoYXBlZmlsZXMgdGhyb3VnaCB0aGUgW2dhemV0dGVlcl0oaHR0cDovL3d3dy5tYXJpbmVyZWdpb25zLm9yZy9nYXpldHRlZXIucGhwP3A9c2VhcmNoKS4gVGhlIGZvbGRlciAqKk1hcHBpbmcqKiBjb250YWlucyBzb21lIHVzZWZ1bCBzaGFwZWZpbGVzLg0KYGBge3J9DQpsaXN0LmZpbGVzKCJEYXRhIGRheSAxL01hcHBpbmciKQ0KYGBgDQoNClRoZSB3b3JsZF9iYXlfZ3VsZiBmb2xkZXIgYW5kIGZpbGUgY29udGFpbnMgdGhlIHNoYXBlZmlsZSBvZiB0aGUgW1NvdXRoZXJuIEJpZ2h0XShodHRwOi8vd3d3Lm1hcmluZXJlZ2lvbnMub3JnL2dhemV0dGVlci5waHA/cD1kZXRhaWxzJmlkPTIzOTkpLiBCZWxvdyB5b3UgY2FuIGZpbmQgdGhyZWUgd2F5cyB0byBpbXBvcnQgaXQgaW50byB0aGUgUiBFbnZpcm9ubWVudC4gVGhlICoqcmVhZFNoYXBlUG9seSgpKiogZnVuY3Rpb24gaXMgb25lIG9mIG1hbnkgdG8gcmVhZCBzcGF0aWFsIGRhdGEuIFRoaXMgcGFydGljdWxhciBmdW5jdGlvbiBvZiB0aGUgKiptYXB0b29scyoqIHBhY2thZ2UgYWxyZWFkeSBzcGVjaWZpZXMgdGhpcyBwYXJ0aWN1bGFyIHNoYXBlZmlsZSBpcyBhIHBvbHlnb24uIFRoZSByZWNvbW1lbmRlZCAqKnJlYWRPR1IoKSoqIHJlY29nbml6ZXMgdGhlIHR5cGUgb2Ygc3BhdGlhbCBkYXRhIGF1dG9tYXRpY2FsbHkuDQpgYGB7ciwgaW5jbHVkZT1UfQ0KYmlnaHQgPC0gcmVhZFNoYXBlUG9seSgiRGF0YSBkYXkgMS9NYXBwaW5nL3dvcmxkX2JheV9ndWxmL3dvcmxkX2JheV9ndWxmLnNocCIpDQpiaWdodCA8LSByZWFkT0dSKCJEYXRhIGRheSAxL01hcHBpbmcvd29ybGRfYmF5X2d1bGYvd29ybGRfYmF5X2d1bGYuc2hwIikNCmJpZ2h0IDwtIHJlYWRPR1IoIkRhdGEgZGF5IDEvTWFwcGluZy93b3JsZF9iYXlfZ3VsZiIsIGxheWVyID0gIndvcmxkX2JheV9ndWxmIikNCmBgYA0KDQpMb29raW5nIGF0IHRoZSBjbGFzcywgc3RydWN0dXJlIGFuZCBwbG90IG9mIHRoZSBzaGFwZWZpbGUgYWxsb3dzIHRvIGJldHRlciB1bmRlcnN0YW5kIHRoZSBkYXRhIHR5cGUuDQpgYGB7cn0NCmNsYXNzKGJpZ2h0KQ0KYGBgDQpgYGB7cn0NCnN0cihiaWdodCkNCmBgYA0KDQpgYGB7ciwgcmVzdWx0cz0naGlkZScsbWVzc2FnZT1GQUxTRX0NCnBsb3QoYmlnaHQpDQpgYGANCg0KIyMgMS4yIFBsb3Qgc3BhdGlhbCBkYXRhIGluIGdncGxvdA0KVGhlICoqZm9ydGlmeSgpKiogZnVuY3Rpb24gYWxsb3dzIHRvIGVhc2lseSBjb252ZXJ0IGFuIG9iamVjdCBvZiBhIHNwYXRpYWwgY2xhc3MgdG8gYSByZWd1bGFyIGRhdGFmcmFtZS4gVGhvdWdoIHRoaXMgdHJhbnNmb3JtYXRpb24gaXMgbm90IG5lY2Vzc2FyeSBmb3IgbWFraW5nIGEgbWFwIGluIFIsIGl0IGlzIHZpdGFsIHRvIG1hcCBkYXRhIGluICoqZ2dwbG90KiouDQpgYGB7cn0NCmJpZ2h0Zm9ydCA8LSBmb3J0aWZ5KGJpZ2h0KQ0KYGBgDQpgYGB7cn0NCnN0cihiaWdodGZvcnQpDQpgYGANCg0KTm93LCBsZXQncyBzdGFydCBleHBsb3JpbmcgdGhlc2Ugc3BhdGlhbCBkYXRhIHdpdGggKipnZ3Bsb3QqKiEgSW4gb3JkZXIgdG8gcGxvdCB0aGUgU291dGhlcm4gQmlnaHQsIHdlIHdhbnQgYSBwbG90IHRoYXQgY29ubmVjdHMgdGhlIGRpZmZlcmVudCBwb2ludHMgaW4gb3VyIGZvcnRpZmllZCBkYXRhIGZyYW1lLg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnLG1lc3NhZ2U9RkFMU0V9DQpnZ3Bsb3QoKSArIGdlb21fcGF0aChkYXRhID0gYmlnaHRmb3J0LCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQpKQ0KYGBgDQoNClRoaXMgZmlyc3QgdHJ5IHNlZW1zIHNsaWdodGx5IGRlbW90aXZhdGluZyEgSG93ZXZlciwgdGhlIGlzc3VlIGlzIGVhc2lseSBmaXhlZCEgV2hlbiBmb3J0aWZ5aW5nIHlvdXIgc3BhdGlhbCBkYXRhLCB0aGUgbmV3IGRhdGEgZnJhbWUgd2lsbCBncm91cCBjZXJ0YWluIHBvaW50cy4gSXQgaXMgdGhlcmVmb3JlIGltcG9ydGFudCB0byBtYWludGFpbiB0aGlzIGdyb3VwaW5nIG9mIHRoZSBkYXRhLCBzaW1wbHkgYnkgdGVsbGluZyAqKmdncGxvdCoqICpncm91cCA9IGdyb3VwKi4NCg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnLG1lc3NhZ2U9RkFMU0V9DQpnZ3Bsb3QoKSArIGdlb21fcGF0aChkYXRhID0gYmlnaHRmb3J0LCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApKQ0KYGBgDQoNCiMjIDEuMyBBZGQgbGF5ZXJzIHRvIHRoZSBwbG90DQpOb3csIHdlIGNhbiBhZGQgbW9yZSBpbmZvcm1hdGlvbiBvbiB0aGlzIHBsb3QuVGhlIEJlbGdpYW4gRUVaIGZvciBleGFtcGxlLg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnLG1lc3NhZ2U9RkFMU0V9DQplZXogPC0gcmVhZE9HUigiRGF0YSBkYXkgMS9NYXBwaW5nL2Vlei9lZXouc2hwIikNCmVlemZvcnQgPC0gZm9ydGlmeShlZXopDQpnZ3Bsb3QoKSArIA0KICBnZW9tX3BhdGgoZGF0YSA9IGJpZ2h0Zm9ydCwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSkgKw0KICBnZW9tX3BhdGgoZGF0YSA9IGVlemZvcnQsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCkpDQpgYGANClNvIG5vdyB3ZSByZWFkIHRoZSBFRVogc2hhcGVmaWxlLCB0cmFuc2Zvcm1lZCBpdCB0byBhIGRhdGFmcmFtZSBhbmQgc2ltcGx5IGFkZGVkIGl0IHRvIG91ciBwbG90IHdpdGggKipnZW9tX3BhdGgoKSoqLiBCZSBjYXJlZnVsIGFsd2F5cyB0byBkZWZpbmUgeCBhcyBsb25naXR1ZGUgYW5kIHkgYXMgbGF0aXR1ZGUhDQoNCldpdGggKipjb29yZF9tYXAoKSoqLCBSIHJlY29nbml6ZXMgdGhlIHBsb3QgaXMgYSBtYXAgYW5kIHdpbGwgc2NhbGUgdGhlIGRpbWVuc2lvbnMgb2YgdGhlIGF4ZXggY29ycmVjdGx5LiANCmBgYHtyLCByZXN1bHRzPSdoaWRlJyxtZXNzYWdlPUZBTFNFfQ0KZ2dwbG90KCkgKyANCiAgY29vcmRfbWFwKCkgKw0KICBnZW9tX3BhdGgoZGF0YSA9IGJpZ2h0Zm9ydCwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSkgKw0KICBnZW9tX3BhdGgoZGF0YSA9IGVlemZvcnQsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCkpDQpgYGANCg0KWW91IGNhbiBhbHNvIGRlZmluZSB0aGUgbGltaXRzIG9mIHRoZSBheGVzIHdpdGggKipjb29yZF9tYXAoKSoqLg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnLG1lc3NhZ2U9RkFMU0V9DQpnZ3Bsb3QoKSArIA0KICBjb29yZF9tYXAoeGxpbSA9IGMoMiwzLjUpLCB5bGltID0gYyg1MSw1MikpICsNCiAgZ2VvbV9wYXRoKGRhdGEgPSBiaWdodGZvcnQsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCkpICsNCiAgZ2VvbV9wYXRoKGRhdGEgPSBlZXpmb3J0LCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApKQ0KYGBgDQoNClRoZW1lcyBpbiBSIGFsbG93IGZvciBwcmV0dGllciBtYXBzISBUaGUgYmxhY2sgYW5kIHdoaXRlIHRoZW1lIGNhbiBiZSBhcHBsaWVkIHdpdGggKip0aGVtZV9idygpKiouIA0KYGBge3IsIHJlc3VsdHM9J2hpZGUnLG1lc3NhZ2U9RkFMU0V9DQpnZ3Bsb3QoKSArIA0KICB0aGVtZV9idygpICsNCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIiMwMDkzYjQiKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShsaW5ldHlwZSA9ICJibGFuayIpLA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9saW5lKGxpbmV0eXBlID0gImJsYW5rIiksDQogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpKSArDQogIGNvb3JkX21hcCh4bGltID0gYygyLDMuNSksIHlsaW0gPSBjKDUxLDUyKSkgKw0KICBnZW9tX3BhdGgoZGF0YSA9IGJpZ2h0Zm9ydCwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSkgKw0KICBnZW9tX3BhdGgoZGF0YSA9IGVlemZvcnQsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCkpDQpgYGANClVzaW5nICoqdGhlbWUoKSoqLCBhIG51bWJlciBvZiBsYXktb3V0IGZlYXR1cmVzIG9mIGEgcGxvdCBjYW4gYmUgZGV0ZXJtaW5lZC4gSW4gdGhlIGNvZGUgYWJvdmUsIHdlIGRldGVybWluZWQgdGhlIGJhY2tncm91bmQgY29sb3VyLCByZW1vdmVkIHRoZSBncmlkIGxpbmVzLCByZW1vdmVkIHRoZSBheGlzIHRpdGxlcyBhbmQgY2hhbmdlZCB0aGUgZm9udCBzaXplIG9mIHRoZSBheGlzIHRleHQuIFRoZSBudW1lcm91cyBvcHRpb25zIGZvciB0aGUgdXNlIG9mIHRoZSAqKnRoZW1lKCkqKiBmdW5jdGlvbiBhcmUgZG9jdW1lbnRlZCBbaGVyZV0oaHR0cDovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvdGhlbWUuaHRtbCkuIA0KDQpUaGUgYmFja2dyb3VuZCBjb2xvdXIgd2FzIGRlZmluZWQgaGVyZSB3aXRoIGEgY29sb3VyIGNvZGUuIEhvd2V2ZXIsICpmaWxsID0gImJsdWUiKiB3b3VsZCBhbHNvIHdvcmsuIE1vcmUgaW5mbyBvbiBjb2xvdXJzIGluIFIgY2FuIGJlIGZvdW5kIGluIHRoaXMgdXNlZnVsIFtjaGVhdHNoZWV0XShodHRwczovL3d3dy5uY2Vhcy51Y3NiLmVkdS9+ZnJhemllci9SU3BhdGlhbEd1aWRlcy9jb2xvclBhbGV0dGVDaGVhdHNoZWV0LnBkZikgYW5kIGluIHRoaXMgW2NhdGFsb2d1ZV0oaHR0cDovL3d3dy5zdGF0LmNvbHVtYmlhLmVkdS9+dHpoZW5nL2ZpbGVzL1Jjb2xvci5wZGYpLiANCg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnLG1lc3NhZ2U9RkFMU0V9DQpnZ3Bsb3QoKSArIA0KICB0aGVtZV9idygpICsNCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIiMwMDkzYjQiKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShsaW5ldHlwZSA9ICJibGFuayIpLA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9saW5lKGxpbmV0eXBlID0gImJsYW5rIiksDQogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpKSArDQogIGNvb3JkX21hcCh4bGltID0gYygyLDMuNSksIHlsaW0gPSBjKDUxLDUyKSkgKw0KICBnZW9tX3BvbHlnb24oYWVzKHg9bG9uZywgeT1sYXQsIGdyb3VwPWdyb3VwKSwgZGF0YSA9IGJpZ2h0Zm9ydCwgZmlsbCA9ICJ3aGl0ZSIpICsNCiAgZ2VvbV9wYXRoKGRhdGEgPSBiaWdodGZvcnQsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCkpICsNCiAgZ2VvbV9wYXRoKGRhdGEgPSBlZXpmb3J0LCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApKQ0KYGBgDQpXaGVuIHdlIGRlZmluZSB0aGUgY29sb3VyIChmaWxsLCBwb3NzaWJsZSBjb25mdXNpb24hKSBvZiB0aGUgc2VhIGFzICJ3aGl0ZSIsIHdlIHNlZSB0aGVyZSBpcyBhbiBpc3N1ZSB3aXRoIFplZWxhbmQgb24gb3VyIG1hcC4gQXBwYXJlbnRseSB0aGUgc2hhcGVmaWxlIGRvZXMgcmVjb2duaXplIHRoZSBwb2ludHMgb2YgdGhlIHBlbmluc3VsYSB0byBiZSBwYXJ0IG9mIHRoZSBTb3V0aGVybiBCaWdodCAoYW5kIGNhbiBkcmF3IGEgcGF0aCBiZXR3ZWVuIHRoZW0pLCBidXQgaXQgZG9lcyBub3QgZ3JvdXAgdGhlbSBjb3JyZWN0bHkuIEEgcG9zc2libGUgc29sdXRpb24gaW52b2x2ZXMgdGhlIGltcG9ydCBvZiBhIGZpbGUgZGVzY3JpYmluZyB0aGUgY29hc3Qgb2YgdGhlIE5ldGhlcmxhbmRzLg0KDQpgYGB7ciwgcmVzdWx0cz0naGlkZScsbWVzc2FnZT1GQUxTRX0NCm5ldGhlcmxhbmRzX2NvYXN0IDwtIHJlYWRPR1IoIkRhdGEgZGF5IDEvTWFwcGluZy9uZXRoZXJsYW5kc19jb2FzdC93b3JsZF9jb3VudHJpZXNfY29hc3RzLnNocCIpDQpuZXRoZXJsYW5kc19jb2FzdGZvcnQgPC0gZm9ydGlmeShuZXRoZXJsYW5kc19jb2FzdCkNCmdncGxvdCgpICsgDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiIzAwOTNiNCIpLA0KICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGxpbmV0eXBlID0gImJsYW5rIiksDQogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2xpbmUobGluZXR5cGUgPSAiYmxhbmsiKSwNCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNikpICsNCiAgY29vcmRfbWFwKHhsaW0gPSBjKDIsMy41KSwgeWxpbSA9IGMoNTEsNTIpKSArDQogIGdlb21fcG9seWdvbihhZXMoeD1sb25nLCB5PWxhdCwgZ3JvdXA9Z3JvdXApLCBkYXRhID0gYmlnaHRmb3J0LCBmaWxsID0gIndoaXRlIikgKw0KICBnZW9tX3BvbHlnb24oYWVzKHg9bG9uZywgeT1sYXQsIGdyb3VwPWdyb3VwKSwgZGF0YSA9IG5ldGhlcmxhbmRzX2NvYXN0Zm9ydCwgZmlsbCA9ICIjMDA5M2I0IikgKw0KICBnZW9tX3BhdGgoZGF0YSA9IGJpZ2h0Zm9ydCwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSkgKw0KICBnZW9tX3BhdGgoZGF0YSA9IGVlemZvcnQsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCkpDQpgYGANCg0KTGFuZCBhbmQgc2VhIGFyZSBub3cgaW4gYSBkaWZmZXJlbnQgY29sb3VyISBIb3dldmVyLCB0aGUgbWFwIGlzIHN0aWxsIHF1aXRlIHNvYmVyLiBOZXh0IHVwOiBsZXQncyBhZGQgc2FuZCBiYW5rcyENCmBgYHtyLCByZXN1bHRzPSdoaWRlJyxtZXNzYWdlPUZBTFNFfQ0KYmFua3MgPC0gcmVhZE9HUigiRGF0YSBkYXkgMS9NYXBwaW5nL2JhbmtzL2JhbmtzLnNocCIpDQpiYW5rc2ZvcnQgPC0gZm9ydGlmeShiYW5rcykNCmdncGxvdCgpICsgDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiIzAwOTNiNCIpLA0KICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGxpbmV0eXBlID0gImJsYW5rIiksDQogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2xpbmUobGluZXR5cGUgPSAiYmxhbmsiKSwNCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNikpICsNCiAgY29vcmRfbWFwKHhsaW0gPSBjKDIsMy41KSwgeWxpbSA9IGMoNTEsNTIpKSArDQogIGdlb21fcG9seWdvbihhZXMoeD1sb25nLCB5PWxhdCwgZ3JvdXA9Z3JvdXApLCBkYXRhID0gYmlnaHRmb3J0LCBmaWxsID0gIndoaXRlIikgKw0KICBnZW9tX3BvbHlnb24oYWVzKHg9bG9uZywgeT1sYXQsIGdyb3VwPWdyb3VwKSwgZGF0YSA9IG5ldGhlcmxhbmRzX2NvYXN0Zm9ydCwgZmlsbCA9ICIjMDA5M2I0IikgKw0KICBnZW9tX3BhdGgoZGF0YSA9IGJpZ2h0Zm9ydCwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSkgKw0KICBnZW9tX3BhdGgoZGF0YSA9IGVlemZvcnQsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCkpICsNCiAgZ2VvbV9wb2x5Z29uKGFlcyh4PWxvbmcsIHk9bGF0LCBncm91cD1ncm91cCksIGRhdGEgPSBiYW5rc2ZvcnQsIGZpbGwgPSAiIzAxNjQ4MyIpDQpgYGANCg0KSW5jbHVkaW5nIHNhbmQgYmFua3MgaW4gb3VyIG1hcCBmYWNpbGl0YXRlcyBvcmllbnRhdGlvbiBhbmQgaXMgYWxzbyBwcmV0dGllci4gTm90ZSB0aGF0IHRoZSBvcmRlciBvZiAqKmdlb21fKiogYXJndW1lbnRzIG1hdHRlcnM6IHRoZSBiYW5rcyBhcmUgbm93IG9uIHRvcCBvZiB0aGUgRUVaLiBMZXQncyBhZGQgc29tZSBtb3JlIHNhbmQgYmFua3MhDQpgYGB7ciwgcmVzdWx0cz0naGlkZScsbWVzc2FnZT1GQUxTRX0NCmJhbmtzemVlbGFuZCA8LSByZWFkT0dSKCJEYXRhIGRheSAxL01hcHBpbmcvWmVlbGFuZCBiYW5rcy9iYW5rcy5zaHAiKQ0KYmFua3Njb2FzdGFsIDwtIHJlYWRPR1IoIkRhdGEgZGF5IDEvTWFwcGluZy9Db2FzdGFsIGJhbmtzL2JhbmtzLnNocCIpDQpiYW5rc2hpbmRlciA8LSByZWFkT0dSKCJEYXRhIGRheSAxL01hcHBpbmcvSGluZGVyIGJhbmtzL2JhbmtzLnNocCIpDQpiYW5rc3plZWxhbmRmb3J0IDwtIGZvcnRpZnkoYmFua3N6ZWVsYW5kKQ0KYmFua3Njb2FzdGFsZm9ydCA8LSBmb3J0aWZ5KGJhbmtzY29hc3RhbCkNCmJhbmtzaGluZGVyZm9ydCA8LSBmb3J0aWZ5KGJhbmtzaGluZGVyKQ0KDQpnZ3Bsb3QoKSArIA0KICB0aGVtZV9idygpICsNCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIiMwMDkzYjQiKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShsaW5ldHlwZSA9ICJibGFuayIpLA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9saW5lKGxpbmV0eXBlID0gImJsYW5rIiksDQogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpKSArDQogIGNvb3JkX21hcCh4bGltID0gYygyLDMuNSksIHlsaW0gPSBjKDUxLDUyKSkgKw0KICBnZW9tX3BvbHlnb24oYWVzKHg9bG9uZywgeT1sYXQsIGdyb3VwPWdyb3VwKSwgZGF0YSA9IGJpZ2h0Zm9ydCwgZmlsbCA9ICJ3aGl0ZSIpICsNCiAgZ2VvbV9wb2x5Z29uKGFlcyh4PWxvbmcsIHk9bGF0LCBncm91cD1ncm91cCksIGRhdGEgPSBuZXRoZXJsYW5kc19jb2FzdGZvcnQsIGZpbGwgPSAiIzAwOTNiNCIpICsNCiAgZ2VvbV9wYXRoKGRhdGEgPSBiaWdodGZvcnQsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCkpICsNCiAgZ2VvbV9wYXRoKGRhdGEgPSBlZXpmb3J0LCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApKSArDQogIGdlb21fcG9seWdvbihhZXMoeD1sb25nLCB5PWxhdCwgZ3JvdXA9Z3JvdXApLCBkYXRhID0gYmFua3Nmb3J0LCBmaWxsID0gIiMwMTY0ODMiKSArDQogIGdlb21fcG9seWdvbihhZXMoeD1sb25nLCB5PWxhdCwgZ3JvdXA9Z3JvdXApLCBkYXRhID0gYmFua3N6ZWVsYW5kZm9ydCwgZmlsbCA9ICIjMDE2NDgzIikgKw0KICBnZW9tX3BvbHlnb24oYWVzKHg9bG9uZywgeT1sYXQsIGdyb3VwPWdyb3VwKSwgZGF0YSA9IGJhbmtzY29hc3RhbGZvcnQsIGZpbGwgPSAiIzAxNjQ4MyIpICsNCiAgZ2VvbV9wb2x5Z29uKGFlcyh4PWxvbmcsIHk9bGF0LCBncm91cD1ncm91cCksIGRhdGEgPSBiYW5rc2hpbmRlcmZvcnQsIGZpbGwgPSAiIzAxNjQ4MyIpDQpgYGANCldlIG5vdyBoYXZlIGEgc2ltcGxlIG1hcCBvZiB0aGUgQlBOUyAod2l0aCBzb21lIG5pY2UgVkxJWiBjb2xvdXJzKSEgVGhpcyBjb2RlIGZvciBtYWtpbmcgdGhlIG1hcCBpcyBuZWl0aGVyIHRoZSBvbmx5LCBuZWl0aGVyIHRoZSBiZXN0IHdheSwgYnV0IGl0IHByb3ZpZGVzIGEgc2ltcGxlIG91dGxpbmUgZm9yIGZ1dHVyZSByZWZlcmVuY2UhDQoNCiMjIDEuNCBDb21waWxlIHRoZSBjb2RlIHRvIGNvbnN0cnVjdCB0aGUgbWFwIGluIGEgZnVuY3Rpb24NCldlIGNhbiBqb2luIGFsbCB0aGlzIGNvZGUgaW4gYSBmdW5jdGlvbiB0byBsb2FkIGFsbCBzaGFwZSBmaWxlcyBhbmQgcGxvdCB0aGUgbWFwLg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnLG1lc3NhZ2U9RkFMU0V9DQpsb2FkX3NoYXBlc19wbG90X21hcCA8LSBmdW5jdGlvbigpIHsNCiAgYmFua3MgPC0gcmVhZE9HUigiRGF0YSBkYXkgMS9NYXBwaW5nL2JhbmtzL2JhbmtzLnNocCIpDQogIGVleiA8LSByZWFkT0dSKCJEYXRhIGRheSAxL01hcHBpbmcvZWV6L2Vlei5zaHAiKQ0KICBiaWdodCA8LSByZWFkT0dSKCJEYXRhIGRheSAxL01hcHBpbmcvd29ybGRfYmF5X2d1bGYvd29ybGRfYmF5X2d1bGYuc2hwIikNCiAgYmFua3N6ZWVsYW5kIDwtIHJlYWRPR1IoIkRhdGEgZGF5IDEvTWFwcGluZy9aZWVsYW5kIGJhbmtzL2JhbmtzLnNocCIpDQogIGJhbmtzY29hc3RhbCA8LSByZWFkT0dSKCJEYXRhIGRheSAxL01hcHBpbmcvQ29hc3RhbCBiYW5rcy9iYW5rcy5zaHAiKQ0KICBiYW5rc2hpbmRlciA8LSByZWFkT0dSKCJEYXRhIGRheSAxL01hcHBpbmcvSGluZGVyIGJhbmtzL2JhbmtzLnNocCIpDQogIG5ldGhlcmxhbmRzX2NvYXN0IDwtIHJlYWRPR1IoIkRhdGEgZGF5IDEvTWFwcGluZy9uZXRoZXJsYW5kc19jb2FzdC93b3JsZF9jb3VudHJpZXNfY29hc3RzLnNocCIpDQoNCiAgZWV6Zm9ydCA8LSBmb3J0aWZ5KGVleikNCiAgYmFua3Nmb3J0IDwtIGZvcnRpZnkoYmFua3MpDQogIGJpZ2h0Zm9ydCA8LSBmb3J0aWZ5KGJpZ2h0KQ0KICBiYW5rc3plZWxhbmRmb3J0IDwtIGZvcnRpZnkoYmFua3N6ZWVsYW5kKQ0KICBiYW5rc2NvYXN0YWxmb3J0IDwtIGZvcnRpZnkoYmFua3Njb2FzdGFsKQ0KICBiYW5rc2hpbmRlcmZvcnQgPC0gZm9ydGlmeShiYW5rc2hpbmRlcikNCiAgbmV0aGVybGFuZHNfY29hc3Rmb3J0IDwtIGZvcnRpZnkobmV0aGVybGFuZHNfY29hc3QpDQogIG5ldGhlcmxhbmRzX2NvYXN0Zm9ydCA8LSBmaWx0ZXIobmV0aGVybGFuZHNfY29hc3Rmb3J0LCBsYXQgPjUxLjM2KQ0KDQogIGdncGxvdCgpICsgDQogICAgY29vcmRfbWFwKHhsaW0gPSBjKDIsMy41KSwgeWxpbSA9IGMoNTEsNTIpKSArICNpbXBvcnRhbnQhIHRoaXMgbWFrZXMgdGhhdCB0aGUgcG9seWdvbnMgYXJlIG9rDQogICAgdGhlbWVfYncoKSArDQogICAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIiMwMDkzYjQiKSwNCiAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGxpbmV0eXBlID0gImJsYW5rIiksDQogICAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfbGluZShsaW5ldHlwZSA9ICJibGFuayIpLA0KICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNikpICsNCiAgICBnZW9tX3BvbHlnb24oYWVzKHg9bG9uZywgeT1sYXQsIGdyb3VwPWdyb3VwKSwgZGF0YSA9IGJpZ2h0Zm9ydCwgZmlsbCA9ICJ3aGl0ZSIpICsNCiAgICBnZW9tX3BvbHlnb24oYWVzKHg9bG9uZywgeT1sYXQsIGdyb3VwPWdyb3VwKSwgZGF0YSA9IG5ldGhlcmxhbmRzX2NvYXN0Zm9ydCwgZmlsbCA9ICIjMDA5M2I0IikgKyAgDQogICAgZ2VvbV9wYXRoKGFlcyh4PWxvbmcsIHk9bGF0LCBncm91cD1ncm91cCksIGRhdGEgPSBiaWdodGZvcnQpICsNCiAgICBnZW9tX3BvbHlnb24oYWVzKHg9bG9uZywgeT1sYXQsIGdyb3VwPWdyb3VwKSwgZGF0YSA9IGJhbmtzZm9ydCwgZmlsbCA9ICIjMDE2NDgzIikgKw0KICAgIGdlb21fcG9seWdvbihhZXMoeD1sb25nLCB5PWxhdCwgZ3JvdXA9Z3JvdXApLCBkYXRhID0gYmFua3N6ZWVsYW5kZm9ydCwgZmlsbCA9ICIjMDE2NDgzIikgKw0KICAgIGdlb21fcG9seWdvbihhZXMoeD1sb25nLCB5PWxhdCwgZ3JvdXA9Z3JvdXApLCBkYXRhID0gYmFua3Njb2FzdGFsZm9ydCwgZmlsbCA9ICIjMDE2NDgzIikgKw0KICAgIGdlb21fcG9seWdvbihhZXMoeD1sb25nLCB5PWxhdCwgZ3JvdXA9Z3JvdXApLCBkYXRhID0gYmFua3NoaW5kZXJmb3J0LCBmaWxsID0gIiMwMTY0ODMiKSArDQogICAgZ2VvbV9wYXRoKGFlcyh4PWxvbmcsIHk9bGF0KSwgZGF0YSA9IGVlemZvcnQsIGNvbG91ciA9ICJibGFjayIpDQp9DQpgYGANCioqbG9hZF9zaGFwZXNfcGxvdF9tYXAoKSoqIG5vdyBtYWtlcyBvdXIgbWFwIHdpdGggb25seSBvbmUgbGluZSBvZiBjb2RlIQ0KDQpgYGB7ciwgcmVzdWx0cz0naGlkZScsbWVzc2FnZT1GQUxTRX0NCmxvYWRfc2hhcGVzX3Bsb3RfbWFwKCkNCmBgYA0KDQpZb3UgY2FuIGFsc28gbG9hZCBhbGwgc2hhcGVmaWxlcyBpbiB0aGUgZW52aXJvbm1lbnQgYW5kIGNvbnN0cnVjdCB0aGUgbWFwIHNlcGVyYXRlbHkuIFRoaXMgbWlnaHQgYmUgdGhlIGZhdm91cmVkIG9wdGlvbiB3aGVuIHlvdSBwbG90IHNldmVyYWwgbWFwcy4NCmBgYHtyLCByZXN1bHRzPSdoaWRlJyxtZXNzYWdlPUZBTFNFfQ0KYmFua3MgPC0gcmVhZE9HUigiRGF0YSBkYXkgMS9NYXBwaW5nL2JhbmtzL2JhbmtzLnNocCIpDQplZXogPC0gcmVhZE9HUigiRGF0YSBkYXkgMS9NYXBwaW5nL2Vlei9lZXouc2hwIikNCmJpZ2h0IDwtIHJlYWRPR1IoIkRhdGEgZGF5IDEvTWFwcGluZy93b3JsZF9iYXlfZ3VsZi93b3JsZF9iYXlfZ3VsZi5zaHAiKQ0KYmFua3N6ZWVsYW5kIDwtIHJlYWRPR1IoIkRhdGEgZGF5IDEvTWFwcGluZy9aZWVsYW5kIGJhbmtzL2JhbmtzLnNocCIpDQpiYW5rc2NvYXN0YWwgPC0gcmVhZE9HUigiRGF0YSBkYXkgMS9NYXBwaW5nL0NvYXN0YWwgYmFua3MvYmFua3Muc2hwIikNCmJhbmtzaGluZGVyIDwtIHJlYWRPR1IoIkRhdGEgZGF5IDEvTWFwcGluZy9IaW5kZXIgYmFua3MvYmFua3Muc2hwIikNCm5ldGhlcmxhbmRzX2NvYXN0IDwtIHJlYWRPR1IoIkRhdGEgZGF5IDEvTWFwcGluZy9uZXRoZXJsYW5kc19jb2FzdC93b3JsZF9jb3VudHJpZXNfY29hc3RzLnNocCIpDQoNCmVlemZvcnQgPC0gZm9ydGlmeShlZXopDQpiYW5rc2ZvcnQgPC0gZm9ydGlmeShiYW5rcykNCmJpZ2h0Zm9ydCA8LSBmb3J0aWZ5KGJpZ2h0KQ0KYmFua3N6ZWVsYW5kZm9ydCA8LSBmb3J0aWZ5KGJhbmtzemVlbGFuZCkNCmJhbmtzY29hc3RhbGZvcnQgPC0gZm9ydGlmeShiYW5rc2NvYXN0YWwpDQpiYW5rc2hpbmRlcmZvcnQgPC0gZm9ydGlmeShiYW5rc2hpbmRlcikNCm5ldGhlcmxhbmRzX2NvYXN0Zm9ydCA8LSBmb3J0aWZ5KG5ldGhlcmxhbmRzX2NvYXN0KQ0KbmV0aGVybGFuZHNfY29hc3Rmb3J0IDwtIGZpbHRlcihuZXRoZXJsYW5kc19jb2FzdGZvcnQsIGxhdCA+NTEuMzYpDQoNCnBsb3RfbWFwIDwtIGZ1bmN0aW9uKCkgew0KICBnZ3Bsb3QoKSArIA0KICAgIGNvb3JkX21hcCh4bGltID0gYygyLDMuNSksIHlsaW0gPSBjKDUxLDUyKSkgKw0KICAgIHRoZW1lX2J3KCkgKw0KICAgIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICIjMDA5M2I0IiksDQogICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShsaW5ldHlwZSA9ICJibGFuayIpLA0KICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2xpbmUobGluZXR5cGUgPSAiYmxhbmsiKSwNCiAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpKSArDQogICAgZ2VvbV9wb2x5Z29uKGFlcyh4PWxvbmcsIHk9bGF0LCBncm91cD1ncm91cCksIGRhdGEgPSBiaWdodGZvcnQsIGZpbGwgPSAid2hpdGUiKSArDQogICAgZ2VvbV9wb2x5Z29uKGFlcyh4PWxvbmcsIHk9bGF0LCBncm91cD1ncm91cCksIGRhdGEgPSBuZXRoZXJsYW5kc19jb2FzdGZvcnQsIGZpbGwgPSAiIzAwOTNiNCIpICsgIA0KICAgIGdlb21fcGF0aChhZXMoeD1sb25nLCB5PWxhdCwgZ3JvdXA9Z3JvdXApLCBkYXRhID0gYmlnaHRmb3J0KSArDQogICAgZ2VvbV9wb2x5Z29uKGFlcyh4PWxvbmcsIHk9bGF0LCBncm91cD1ncm91cCksIGRhdGEgPSBiYW5rc2ZvcnQsIGZpbGwgPSAiIzAxNjQ4MyIpICsNCiAgICBnZW9tX3BvbHlnb24oYWVzKHg9bG9uZywgeT1sYXQsIGdyb3VwPWdyb3VwKSwgZGF0YSA9IGJhbmtzemVlbGFuZGZvcnQsIGZpbGwgPSAiIzAxNjQ4MyIpICsNCiAgICBnZW9tX3BvbHlnb24oYWVzKHg9bG9uZywgeT1sYXQsIGdyb3VwPWdyb3VwKSwgZGF0YSA9IGJhbmtzY29hc3RhbGZvcnQsIGZpbGwgPSAiIzAxNjQ4MyIpICsNCiAgICBnZW9tX3BvbHlnb24oYWVzKHg9bG9uZywgeT1sYXQsIGdyb3VwPWdyb3VwKSwgZGF0YSA9IGJhbmtzaGluZGVyZm9ydCwgZmlsbCA9ICIjMDE2NDgzIikgKw0KICAgIGdlb21fcGF0aChhZXMoeD1sb25nLCB5PWxhdCksIGRhdGEgPSBlZXpmb3J0LCBjb2xvdXIgPSAiYmxhY2siKQ0KfQ0KYGBgDQoNCmBgYHtyLCByZXN1bHRzPSdoaWRlJyxtZXNzYWdlPUZBTFNFfQ0KcGxvdF9tYXAoKQ0KYGBgDQoNCiMgMi4gUGxvdCB0aGUgc3RhdGlvbiBsb2NhdGlvbnMgb24gdGhlIG1hcA0KRmlyc3Qgb2ZmLCB3ZSBncm91cCBvdXIgc3RhdGlvbiBkYXRhIGluIGEgbmV3IGRhdGEgZnJhbWUgc3RhdGlvbmRmLiAqJT4lKiBjcmVhdGVzIGEgInBpcGVsaW5lIiBvZiBjb2RlLiBUaGlzIGlzIGFuIG90aGVyIHdheSBvZiBmb3JtdWxhdGluZyB0aGUgY29tYmluYXRpb24gb2YgY29kZSB0byBzdW1tYXJpemUgYSBkYXRhIGZyYW1lLiANCmBgYHtyLCBpbmNsdWRlPVR9DQpzdGF0aW9uZGYgPC0gZ3JvdXBfYnkocG9kZGF0YSwgU3RhdGlvbikgJT4lIHN1bW1hcmlzZShMYXRpdHVkZSA9IG1heChMYXRpdHVkZSksIExvbmdpdHVkZSA9IG1heChMb25naXR1ZGUpKQ0KYGBgDQoNClVzaW5nICpwbG90X21hcCosIHdlIGNhbiBleHBsb3JlIHRoZSBsb2NhdGlvbiBvZiB0aGUgc3RhdGlvbnMgaW4gb3VyIGRhdGEuDQpgYGB7ciwgcmVzdWx0cz0naGlkZScsbWVzc2FnZT1GQUxTRX0NCnBsb3RfbWFwKCkgKyBnZW9tX3BvaW50KGRhdGEgPSBzdGF0aW9uZGYsIGFlcyhMb25naXR1ZGUsTGF0aXR1ZGUpLCBzaXplID0gNCwgY29sb3VyID0gInJlZCIpDQpgYGANCg0KTm93LCB3ZSBjYW4gYWRkIHRoZSBzdGF0aW9uIG5hbWVzLg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnLG1lc3NhZ2U9RkFMU0V9DQpwbG90X21hcCgpICsgZ2VvbV9wb2ludChkYXRhID0gc3RhdGlvbmRmLCBhZXMoTG9uZ2l0dWRlLExhdGl0dWRlKSwgc2l6ZSA9IDQsIGNvbG91ciA9ICJyZWQiKSArIA0KICBnZW9tX2xhYmVsKGRhdGEgPSBzdGF0aW9uZGYsIGFlcyhMb25naXR1ZGUgKzAuMDEsTGF0aXR1ZGUsbGFiZWwgPSBTdGF0aW9uKSwgaGp1c3QgPSAwKQ0KYGBgDQoNClRoaXMgaXMgdmVyeSBtZXNzeS4uLiBCeSB1c2luZyB0aGUgcGFja2FnZSBbZ2dyZXBlbF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2dncmVwZWwvZ2dyZXBlbC5wZGYpLCB5b3UgY2FuIGF2b2lkIG92ZXJsYXBwaW5nIGxhYmVscyBvciB0ZXh0Lg0KYGBge3IsIGluY2x1ZGU9VH0NCmxpYnJhcnkoZ2dyZXBlbCkNCmBgYA0KDQpgYGB7ciwgcmVzdWx0cz0naGlkZScsbWVzc2FnZT1GQUxTRX0NCnBsb3RfbWFwKCkgKyBnZW9tX3BvaW50KGRhdGEgPSBzdGF0aW9uZGYsIGFlcyhMb25naXR1ZGUsTGF0aXR1ZGUpLCBzaXplID0gNCwgY29sb3VyID0gInJlZCIpICsgDQogIGdlb21fbGFiZWxfcmVwZWwoZGF0YSA9IHN0YXRpb25kZiwgYWVzKExvbmdpdHVkZSArMC4wMSxMYXRpdHVkZSxsYWJlbCA9IFN0YXRpb24pKQ0KYGBgDQoNCioqZ2VvbV9sYWJlbF9yZXBlbCgpKiogZG9lcyB0aGUgam9iLCBidXQgb3VyIHBsb3QgaXMgc3RpbGwgYSBiaXQgbWVzc3kuIFdlIGNhbiB0cnkgdG8gcGxvdCB6b25lcyBpbnN0ZWFkLg0KYGBge3IsIGluY2x1ZGU9VH0NCnpvbmVkZiA8LSBncm91cF9ieShwb2RkYXRhLCBab25lKSAlPiUgc3VtbWFyaXNlKExhdGl0dWRlID0gbWVhbihMYXRpdHVkZSksIExvbmdpdHVkZSA9IG1lYW4oTG9uZ2l0dWRlKSkNCmBgYA0KDQpgYGB7ciwgcmVzdWx0cz0naGlkZScsbWVzc2FnZT1GQUxTRX0NCnBsb3RfbWFwKCkgKyBnZW9tX3BvaW50KGRhdGEgPSB6b25lZGYsIGFlcyhMb25naXR1ZGUsTGF0aXR1ZGUpLCBzaXplID0gNCwgY29sb3VyID0gInJlZCIpICsgDQogIGdlb21fbGFiZWxfcmVwZWwoZGF0YSA9IHpvbmVkZiwgYWVzKExvbmdpdHVkZSArMC4wMSxMYXRpdHVkZSxsYWJlbCA9IFpvbmUpKQ0KYGBgDQoNCiMgMy4gU3BhdGlhbCBkYXRhIGV4cGxvcmF0aW9uDQpJbiBvcmRlciB0byBwbG90IGRldGVjdGlvbiByZWxhdGVkIHZhcmlhYmxlcyBzcGF0aWFsbHksIHdlIGZpcnN0IHN1bW1hcmlzZSB0aGVtIGZvciBlYWNoIHpvbmUuDQpgYGB7ciwgaW5jbHVkZT1UfQ0Kem9uZWRmIDwtIGdyb3VwX2J5KHBvZGRhdGFfZGF5LCBab25lKSAlPiUgDQogIHN1bW1hcmlzZShMYXRpdHVkZSA9IG1lYW4oTGF0aXR1ZGUpLCANCiAgICAgICAgICAgIExvbmdpdHVkZSA9IG1lYW4oTG9uZ2l0dWRlKSwNCiAgICAgICAgICAgIE1pbGxpc2Vjb25kcyA9IG1lYW4oTWlsbGlzZWNvbmRzKSwNCiAgICAgICAgICAgIE51bWJlcl9jbGlja3NfZmlsdGVyZWQgPSBtZWFuKE51bWJlcl9jbGlja3NfZmlsdGVyZWQpLA0KICAgICAgICAgICAgTnVtYmVyX2NsaWNrc190b3RhbCA9IG1lYW4oTnVtYmVyX2NsaWNrc190b3RhbCksDQogICAgICAgICAgICBMb3N0X21pbnV0ZXMgPSBtZWFuKExvc3RfbWludXRlcyksDQogICAgICAgICAgICBEcG0gPSBzdW0oRHBtKSwNCiAgICAgICAgICAgIERwMTBtID0gc3VtKERwMTBtKSwNCiAgICAgICAgICAgIERwaCA9IHN1bShEcGgpLA0KICAgICAgICAgICAgUmVjb3JkZWQgPSBzdW0oUmVjb3JkZWQpKQ0Kem9uZWRmJENsaWNrX2ZyZXF1ZW5jeSA8LSB6b25lZGYkRHBtL3pvbmVkZiRSZWNvcmRlZA0Kem9uZWRmJENsaWNrX2ludGVuc2l0eSA8LSB6b25lZGYkTnVtYmVyX2NsaWNrc19maWx0ZXJlZC96b25lZGYkQ2xpY2tfZnJlcXVlbmN5DQpgYGANCg0KIyMgMy4xIE1hcHBpbmcgZGV0ZWN0aW9ucw0KYGBge3IsIHJlc3VsdHM9J2hpZGUnLG1lc3NhZ2U9RkFMU0V9DQpwbG90X21hcCgpICsgZ2VvbV9wb2ludChkYXRhID0gem9uZWRmLCBhZXMoTG9uZ2l0dWRlLExhdGl0dWRlLCBzaXplID0gTWlsbGlzZWNvbmRzKSwgY29sb3VyID0gInJlZCIpDQpgYGANCg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnLG1lc3NhZ2U9RkFMU0V9DQpwbG90X21hcCgpICsgZ2VvbV9wb2ludChkYXRhID0gem9uZWRmLCBhZXMoTG9uZ2l0dWRlLExhdGl0dWRlLCBzaXplID0gTnVtYmVyX2NsaWNrc19maWx0ZXJlZCksIGNvbG91ciA9ICJyZWQiKQ0KYGBgDQoNCmBgYHtyLCByZXN1bHRzPSdoaWRlJyxtZXNzYWdlPUZBTFNFfQ0KcGxvdF9tYXAoKSArIGdlb21fcG9pbnQoZGF0YSA9IHpvbmVkZiwgYWVzKExvbmdpdHVkZSxMYXRpdHVkZSwgc2l6ZSA9IERwbSksIGNvbG91ciA9ICJyZWQiKQ0KYGBgDQoNCmBgYHtyLCByZXN1bHRzPSdoaWRlJyxtZXNzYWdlPUZBTFNFfQ0KcGxvdF9tYXAoKSArIGdlb21fcG9pbnQoZGF0YSA9IHpvbmVkZiwgYWVzKExvbmdpdHVkZSxMYXRpdHVkZSwgc2l6ZSA9IERwMTBtKSwgY29sb3VyID0gInJlZCIpDQpgYGANCg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnLG1lc3NhZ2U9RkFMU0V9DQpwbG90X21hcCgpICsgZ2VvbV9wb2ludChkYXRhID0gem9uZWRmLCBhZXMoTG9uZ2l0dWRlLExhdGl0dWRlLCBzaXplID0gRHBoKSwgY29sb3VyID0gInJlZCIpDQpgYGANCg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnLG1lc3NhZ2U9RkFMU0V9DQpwbG90X21hcCgpICsgZ2VvbV9wb2ludChkYXRhID0gem9uZWRmLCBhZXMoTG9uZ2l0dWRlLExhdGl0dWRlLCBzaXplID0gQ2xpY2tfZnJlcXVlbmN5KSwgY29sb3VyID0gInJlZCIpDQpgYGANCg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnLG1lc3NhZ2U9RkFMU0V9DQpwbG90X21hcCgpICsgZ2VvbV9wb2ludChkYXRhID0gem9uZWRmLCBhZXMoTG9uZ2l0dWRlLExhdGl0dWRlLCBzaXplID0gQ2xpY2tfaW50ZW5zaXR5KSwgY29sb3VyID0gInJlZCIpDQpgYGANCg0KQW4gYWx0ZXJuYXRpdmUgdG8gZmlyc3QgZ3JvdXBpbmcgYW5kIHN1bW1hcml6aW5nIHRoZSBkYXRhLCBpcyB0byB1c2UgdGhlICpncm91cCogYXJndW1lbnQgaW4gKipnZ3Bsb3QqKi4NCmBgYHtyLCByZXN1bHRzPSdoaWRlJyxtZXNzYWdlPUZBTFNFfQ0KcGxvdF9tYXAoKSArIGdlb21fcG9pbnQoZGF0YSA9IHBvZGRhdGFfZGF5LCBhZXMoTG9uZ2l0dWRlLExhdGl0dWRlLCBzaXplID0gQ2xpY2tfZnJlcXVlbmN5LCBncm91cD1ab25lKSwgY29sb3VyID0gInJlZCIpDQpgYGANCg0KIyMgMy4yIFZpc3VhbGl6ZSBzcGF0aW90ZW1wb3JhbCBwYXR0ZXJucw0KSW4gb3JkZXIgdG8gZ2V0IGFuIGlkZWEgb2YgdGhlIHBvcnBvaXNlIGRpc3RyaWJ1dGlvbiBlYWNoIG1vbnRoLCB3ZSBmaXJzdCBzdW1tYXJpemUgdGhlIGRhdGEgcGVyIHpvbmUgYW5kIG1vbnRoLg0KYGBge3IsIGluY2x1ZGU9VH0NCnpvbmVkZiA8LSBncm91cF9ieShwb2RkYXRhX2RheSwgWm9uZSwgbW9udGgoVGltZSkpICU+JSANCiAgc3VtbWFyaXNlKExhdGl0dWRlID0gbWVhbihMYXRpdHVkZSksIA0KICAgICAgICAgICAgTG9uZ2l0dWRlID0gbWVhbihMb25naXR1ZGUpLA0KICAgICAgICAgICAgTWlsbGlzZWNvbmRzID0gbWVhbihNaWxsaXNlY29uZHMpLA0KICAgICAgICAgICAgTnVtYmVyX2NsaWNrc19maWx0ZXJlZCA9IG1lYW4oTnVtYmVyX2NsaWNrc19maWx0ZXJlZCksDQogICAgICAgICAgICBOdW1iZXJfY2xpY2tzX3RvdGFsID0gbWVhbihOdW1iZXJfY2xpY2tzX3RvdGFsKSwNCiAgICAgICAgICAgIExvc3RfbWludXRlcyA9IG1lYW4oTG9zdF9taW51dGVzKSwNCiAgICAgICAgICAgIERwbSA9IHN1bShEcG0pLA0KICAgICAgICAgICAgRHAxMG0gPSBzdW0oRHAxMG0pLA0KICAgICAgICAgICAgRHBoID0gc3VtKERwaCksDQogICAgICAgICAgICBSZWNvcmRlZCA9IHN1bShSZWNvcmRlZCkpDQp6b25lZGYkQ2xpY2tfZnJlcXVlbmN5IDwtIHpvbmVkZiREcG0vem9uZWRmJFJlY29yZGVkDQp6b25lZGYkQ2xpY2tfaW50ZW5zaXR5IDwtIHpvbmVkZiROdW1iZXJfY2xpY2tzX2ZpbHRlcmVkL3pvbmVkZiRDbGlja19mcmVxdWVuY3kNCmNvbG5hbWVzKHpvbmVkZilbMl0gPC0gIk1vbnRoIg0KYGBgDQoNCldlIGNhbiBub3cgdmlzdWFsaXplIHRoZSBkaXN0cmlidXRpb24gcGF0dGVybiBwZXIgbW9udGguDQpgYGB7ciwgcmVzdWx0cz0naGlkZScsbWVzc2FnZT1GQUxTRX0NCnBsb3RfbWFwKCkgKyBnZW9tX3BvaW50KGRhdGEgPSB6b25lZGZbem9uZWRmJE1vbnRoID09IDEsXSwgYWVzKExvbmdpdHVkZSxMYXRpdHVkZSwgc2l6ZSA9IENsaWNrX2ZyZXF1ZW5jeSksIGNvbG91ciA9ICJyZWQiKSArIGdndGl0bGUoMSkNCmBgYA0KDQpJbnN0ZWFkIG9mIG1ha2luZyAxMiBwbG90cyBpbiAxMiBsaW5lcyBvZiBjb2RlLCB3ZSBjYW4gbWFrZSBhIGxpc3Qgb2YgcGxvdHMuDQpgYGB7ciwgcmVzdWx0cz0naGlkZScsbWVzc2FnZT1GQUxTRX0NCmxhcHBseSh1bmlxdWUoem9uZWRmJE1vbnRoKSwgZnVuY3Rpb24oeCl7DQogIHBsb3RfbWFwKCkgKyBnZW9tX3BvaW50KGRhdGEgPSB6b25lZGZbem9uZWRmJE1vbnRoID09IHgsXSwgYWVzKExvbmdpdHVkZSxMYXRpdHVkZSwgc2l6ZSA9IENsaWNrX2ZyZXF1ZW5jeSksIGNvbG91ciA9ICJyZWQiKSArIGdndGl0bGUoeCkNCn0pDQpgYGANCg0KQW4gZWFzeSBhbHRlcm5hdGl2ZSB0byBwbG90IGluIGNocm9ub2xvZ2ljYWwgb3JkZXI6DQpgYGB7ciwgcmVzdWx0cz0naGlkZScsbWVzc2FnZT1GQUxTRX0NCmxhcHBseSgxOjEyLCBmdW5jdGlvbih4KXsNCiAgcGxvdF9tYXAoKSArIGdlb21fcG9pbnQoZGF0YSA9IHpvbmVkZlt6b25lZGYkTW9udGggPT0geCxdLCBhZXMoTG9uZ2l0dWRlLExhdGl0dWRlLCBzaXplID0gQ2xpY2tfZnJlcXVlbmN5KSwgY29sb3VyID0gInJlZCIpICsgZ2d0aXRsZSh4KQ0KfSkNCmBgYA0KDQpJbiBvcmRlciB0byBoYXZlIHRoZSByZXN1bHRzIG9mIHRoZSAxMiBtb250aHMgZGlzcGxheWVkIGluIG9uZSBwbG90LCB3ZSBjYW4gYXBwbHkgKipmYWNldF93cmFwKCkqKi4gDQpgYGB7ciwgcmVzdWx0cz0naGlkZScsbWVzc2FnZT1GQUxTRX0NCnBsb3RfbWFwKCkgKyBnZW9tX3BvaW50KGRhdGEgPSB6b25lZGYsIGFlcyhMb25naXR1ZGUsTGF0aXR1ZGUsIHNpemUgPSBDbGlja19mcmVxdWVuY3kpLCBjb2xvdXIgPSAicmVkIikgKyBmYWNldF93cmFwKH5Nb250aCkNCmBgYA0KDQo=