Cartography

“The art or technique of making maps”

Factors that affect design

  • Objective: what information do you want to convey?
  • Audience: who will be using your map? Is your map easily understood by your target audience?
  • Reality and generalization: when making a small map using data with a high level of detail, how much of this detail do you want/need to retain? What information can be generalized?
  • Map scale: is your data better visualized on a small-scale or a large-scale?
  • Technical limits: are you constrained by printer capability? Does your map need to load quickly if being view online?

Basic elements of a Map

  • Title
  • Legend
  • Scale
  • North arrow
  • Additional information: this could be a brief desciption of your map, data sources, projection information, author information ect

Effective Maps

  • Communicates a clear message
  • Appropriate projection
  • Data at appropriate level of generalization
  • Clear symbology: symbols match the data and are easy to distinguish from one another
  • Good figure-ground organization (layout)
  • Good visual hierarchy: is the reader’s eye drawn to the most important information? This can be achieved through size, color, placement on map layout

Common Types of Maps

Dot-Density

  • Uses point symbology to visualize a geographic distribution
  • Rely on visual scatter to show spatial patterns
  • Each dot represents a constant number of things (people, things, any quanifiable phenomena)

This link provides a walk through of making dot density maps from census data

tidycensus package is used to download population data from the US Census Bureau

tigris package is used to download shapefiles from the US Census and load them into R as sf objects Here is a helpful guide if you are interested in learning more about the tigris package.

Examples of available datasets

Function Datasets available Years available
nation() cartographic (1:5m; 1:20m) 2013-2019
divisions() cartographic (1:500k; 1:5m; 1:20m) 2013-2019
regions() cartographic (1:500k; 1:5m; 1:20m) 2013-2019
states() TIGER/Line; cartographic (1:500k; 1:5m; 1:20m) 1990, 2000, 2010-2019
counties() TIGER/Line; cartographic (1:500k; 1:5m; 1:20m) 1990, 2000, 2010-2019
tracts() TIGER/Line; cartographic (1:500k) 1990, 2000, 2010-2019
block_groups() TIGER/Line; cartographic (1:500k) 1990, 2000, 2010-2019
blocks() TIGER/Line 2000, 2010-2019
places() TIGER/Line; cartographic (1:500k) 2011-2019
pumas() TIGER/Line; cartographic (1:500k) 2012-2019
school_districts() TIGER/Line; cartographic 2011-2019
zctas() TIGER/Line; cartographic (1:500k) 2000, 2010, 2012-2019
congressional_districts() TIGER/Line; cartographic (1:500k; 1:5m; 1:20m) 2011-2019
state_legislative_districts() TIGER/Line; cartographic (1:500k) 2011-2019
voting_districts() TIGER/Line 2012
area_water() TIGER/Line 2011-2019
linear_water() TIGER/Line 2011-2019
ggplot() +
  geom_sf(data = rva, fill = "white", lwd = 1) +
  geom_sf(data = water, fill = "lightblue", colour = "lightblue") +
  geom_sf(data = roads, colour = "grey") +
  geom_sf(data = points,          #the creation of the points data is shown in a chunk not included in the published document, but is included if you download this markdown file
          aes(colour = education,
              fill = education),
          size = .5) +
  scale_color_brewer(type = "div", palette = 4,
                     name = "Education") +
  scale_fill_brewer(type = "div", palette = 4) +
  theme_minimal() +
  ggtitle("Distribution of educational attainment in Richmond, Virginia (2019)",
            "1 dot equals 10 people") +
  guides(fill = "none")

Proportional Symbols

  • Size of symbols is related to the value of phenomenon represented – the larger the symbol the “more” of something exists at that location
  • Most basic method is to scale circles directly proportionate to the data
  • Can be used with both numeric or ordered catagorical data

For this example I again used data from tidycensus and tigris. The code for processing this data is availble in the markdown document

Note that the circles are plotted as points, not polygons. Proportionate circle sizes are made using aes(size = data$attribute)

#proportionate circle sizes are made

ggplot() +
  geom_sf(data = counties, fill = "lightblue", color = "lightblue") +
  geom_sf(data = acs, fill = "grey95") +
  geom_sf(data = points, aes(size = estimate), color = "lightsalmon2") +  #data processing for the points data is shown in the previous chunk (visible in the markdown document). Points are created using centroids of county polygons
  theme_minimal() +
  ggtitle("Population of Virginia Counties",
          "2019") +
  labs( size = "Population") +
  scale_size_continuous(labels = comma)

Choropleth

  • Represent data through shading or symbols on polygons
  • Popular because they easily represent variability data across a region

For this example I again used data from tidycensus and tigris. The code for processing this data is availble in the markdown document

The scaling of color is made by aes(fill = data$attribute)

Choropleth

Isoline

  • Lines connect points knows or estimated to have equal vales
  • Used in maps of temperature, precipitation, air pressure, climate classifications, ect
  • Topographic maps: type of isoline map depicting elevation

The package ggisobands can be used to plot isobands into ggplot2. x, y, and z values are required in aes() The scaled colors are achieved in aes(fill =)

link to download data

In this example the starting data is a Digital Elevation Model (DEM) raster of Downtown Richmond. geom_isobands creates isobands from point data, so we must convert our raster to points and then put them in a data frame. After that, they’re ready to go!

library(ggisoband)

DEM_5m <- raster("C:/Users/Charis/Documents/Spring2021/GISinR/GISinR/DEM_5m.tif")

DEM_pts <- rasterToPoints(DEM_5m, spatial = TRUE)       #I transformed my raster data into a data frame of points, each with an x, y, and z column (z being elevation)
DEM_df <- data.frame(DEM_pts)
DEM_df$DEM_5m <- ifelse(DEM_df$DEM_5m <0, 0, DEM_df$DEM_5m) #There was a section with very negative numbers that I assumed to be a defect in the data, so I made any negative values 0 to clean it up


ggplot(DEM_df, aes(x=x, y=y, z=DEM_5m)) +             #You need an x, y, and z in the aes()
  geom_isobands(aes(fill = stat(zmin)), color = NA) + #the stat function is used to tell ggplot that you want it to use a calculated aesthetic produced by the statistic 
  scale_fill_viridis_c()

]

There is a package, isoband that generates contour lines that can be used with grid.path() or grid.polyline() from the grid library. To use this data in ggplot it must be converted to an sf object. A vignette of that package is available here.

Robin Lovelace explain spatial vectorization and shows how to use the rasterToContour() function

Here is the same map using color= instead of fill=

Basic Map Making

This is what we started working through during class. The data is from the rnaturalearth library

note - returnclass default is sp


world <- ne_countries(scale = "medium", returnclass = "sf")
class(world)
[1] "sf"         "data.frame"
world <- world %>%
  select(income_grp, subregion, name, name_long, name_alt, pop_est, level, subunit, type, economy)

This is a plain old ggplot, no bells or whistles


ggplot(world) +
  geom_sf() -> p

p

Title, subtitle & axis labels

Title is added using ggtitle() Subtitle is added using subtitle = within the ggtitle() function axis labels are added with xlab and ylab

p +
  xlab("Longitude") +
  ylab("Latitude") +
  ggtitle("World Map",
          subtitle = paste0("(" ,length(unique(world$name)), "countries)" )) -> p #note that I'm using the `paste0()` function to insert information from my data into my subtitle

p

NA
NA

Color

Custom palette

You can design your own custom palette using hex codes or the assigned color names built into R

my_colors <- c("#999999", "#E69F00", "#56B4E9", "#009E73",
          "#F0E442", "#0072B2", "#D55E00")

ggplot(world) +
  geom_sf(aes(fill = economy))+
  scale_fill_manual(values = my_colors)

It may be helpful to use a website like coolors to choose colors for your palette

Built-it color palettes

Color Palette Cheat Sheet

There are many packages with pre-defined colors and palettes

  • viridis

  • RColorBrewer

Into Wes Anderson’s color schemes? There’s a library for that

  • wesanderson

I chose the viridis_c palette

  • trans = transformation of data, in this case square root

  • option = different color palettes: A-E, default is D

Let’s switch over to the all-too-familiar beetle dataset

beetle_url <- "https://raw.githubusercontent.com/dyerlab/ENVS-Lectures/master/data/Araptus_Disperal_Bias.csv"

read_csv(beetle_url) %>%
  st_as_sf( coords=c("Longitude","Latitude"), crs=4326 ) %>%    #transform to sf
  st_union() %>%
  st_buffer(dist = 1) %>%
  st_bbox() -> bounds #will use bounds to crop our world data to the beetle area

read_csv(beetle_url) %>%
  st_as_sf(coords=c("Longitude","Latitude"), crs = 4326) -> beetle

ggplot(world) +
  geom_sf() +
  xlab("Longitude") +
  ylab("Latitude") +            #note that the xlim and ylim values are from `bounds`
  ggtitle("Beetles") +
  coord_sf( xlim = c(-116, -108), ylim = c(22, 31), expand = FALSE) -> b

b

The result is the world dataset cropped to the bettle dataset

Scale bar and North arrow

Both the scale bar and north arrow come from package ggspatial

library(ggspatial)

b+
  annotation_scale(location = "br", style = "bar") +
  annotation_north_arrow(location = "br", width = unit(.5, "cm"),
                         height = unit(.75, "cm"),
                         pad_y = unit(0.75, "cm"),
                         pad_x = unit(0.5, "cm"),
                         style = north_arrow_orienteering(
                           text_size = 8
                         )) -> b

b

changed extent to see more countries

Adding names

Layouts

Side-by-Side

library(cowplot)

plot_grid(world_map, Mexico, nrow = 1, rel_widths = c(2.3, 1)) -> p
p

Inset Map


ggplot() +
  geom_sf(data = counties, fill = "lightblue", color = "lightblue") +
  geom_sf(data = acs, fill = "grey") +
  theme_minimal() -> VA_map

VA_map



ggplot() +
  geom_sf(data = rva, fill = "grey95", lwd = 1) +
  geom_sf(data = water, fill = "lightblue", colour = "lightblue") +
  geom_sf(data = roads, colour = "grey")+
  annotate("text", x = -77.57, y = 37.62, label= "Richmond", size = 3) +
  theme_void() +
  coord_sf(datum = NA) -> RVA_map

RVA_map


st_bbox(counties)
     xmin      ymin      xmax      ymax 
-83.67539  36.54085 -75.16643  39.46601 
st_bbox(rva)
     xmin      ymin      xmax      ymax 
-77.60117  37.44655 -77.38551  37.60281 
ggdraw(VA_map) +
  draw_plot(RVA_map,
            width = 0.25, height = 0.25* 5/2, 
        x = .15, y = .35) -> p
p

Adding arrows


arrowA <- data.frame(x1 = 18, x2 = 25, y1 = 3.45, y2 = 3.55)

ggplot() +
  geom_sf(data = rva, fill = "grey95", lwd = 1) +
  geom_sf(data = water, fill = "lightblue", colour = "lightblue") +
  geom_sf(data = roads, colour = "grey")+
  annotate("text", x = -77.575, y = 37.62, label= "Richmond", size = 3)+
  theme_void() +
  theme(panel.grid.major = element_line(colour = gray(0.5), linetype = "dashed", 
         size = 0.5), panel.background = element_rect(fill = "white"), 
         panel.border = element_rect(fill = NA)) +
  coord_sf(datum = NA) -> RVA_map

ggdraw(xlim = c(0, 35), ylim = c(0, 7.5)) +
  draw_plot(VA_map, x = 0, y = 0, width = 25, height = 7.5) +
  draw_plot(RVA_map, x = 24, y = .5, width = 10, height = 7)+
  geom_segment(aes(x = x1, y = y1, xend = x2, yend = y2), data = arrowA, 
        arrow = arrow(), lineend = "round") -> arrow_box

arrow_box

LS0tDQp0aXRsZTogIkNhcnRvZ3JhcGh5Ig0KYXV0aG9yOiAiQ2hhcmlzIERlYWR3eWxlciINCmRhdGU6ICIzLzEwLzIwMjEiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCgllY2hvID0gVFJVRSwNCgltZXNzYWdlID0gRkFMU0UsDQoJd2FybmluZyA9IEZBTFNFLA0KCWNhY2hlID0gVFJVRQ0KKQ0KbGlicmFyeShzZikNCmxpYnJhcnkobWFwcykNCmxpYnJhcnkodW5pdHMpDQpsaWJyYXJ5KHJnZGFsKQ0KbGlicmFyeShyZ2VvcykNCmxpYnJhcnkoR0dhbGx5KQ0KbGlicmFyeShyYXN0ZXIpDQpsaWJyYXJ5KG1hcHByb2opDQpsaWJyYXJ5KGxlYWZsZXQpDQpsaWJyYXJ5KHJuYXR1cmFsZWFydGgpDQpsaWJyYXJ5KHJuYXR1cmFsZWFydGhkYXRhKQ0KbGlicmFyeSggcmVhZHIgKQ0KbGlicmFyeSggdGlkeXZlcnNlICkNCmxpYnJhcnkoIGx1YnJpZGF0ZSApDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShodG1sdG9vbHMpDQoNCmxpYnJhcnkoc2NhbGVzKQ0KbGlicmFyeSh0aWR5Y2Vuc3VzKQ0KbGlicmFyeSh0aWdyaXMpDQpgYGANCg0KDQojIENhcnRvZ3JhcGh5DQoNCioiVGhlIGFydCBvciB0ZWNobmlxdWUgb2YgbWFraW5nIG1hcHMiKg0KDQojIyMgRmFjdG9ycyB0aGF0IGFmZmVjdCBkZXNpZ24NCi0gKipPYmplY3RpdmU6Kiogd2hhdCBpbmZvcm1hdGlvbiBkbyB5b3Ugd2FudCB0byBjb252ZXk/DQotICoqQXVkaWVuY2U6Kiogd2hvIHdpbGwgYmUgdXNpbmcgeW91ciBtYXA/IElzIHlvdXIgbWFwIGVhc2lseSB1bmRlcnN0b29kIGJ5IHlvdXIgdGFyZ2V0IGF1ZGllbmNlPw0KLSAqKlJlYWxpdHkgYW5kIGdlbmVyYWxpemF0aW9uOioqIHdoZW4gbWFraW5nIGEgc21hbGwgbWFwIHVzaW5nIGRhdGEgd2l0aCBhIGhpZ2ggbGV2ZWwgb2YgZGV0YWlsLCBob3cgbXVjaCBvZiB0aGlzIGRldGFpbCBkbyB5b3Ugd2FudC9uZWVkIHRvIHJldGFpbj8gV2hhdCBpbmZvcm1hdGlvbiBjYW4gYmUgZ2VuZXJhbGl6ZWQ/DQotICoqTWFwIHNjYWxlOioqIGlzIHlvdXIgZGF0YSBiZXR0ZXIgdmlzdWFsaXplZCBvbiBhIHNtYWxsLXNjYWxlIG9yIGEgbGFyZ2Utc2NhbGU/DQotICoqVGVjaG5pY2FsIGxpbWl0czoqKiBhcmUgeW91IGNvbnN0cmFpbmVkIGJ5IHByaW50ZXIgY2FwYWJpbGl0eT8gRG9lcyB5b3VyIG1hcCBuZWVkIHRvIGxvYWQgcXVpY2tseSBpZiBiZWluZyB2aWV3IG9ubGluZT8NCg0KIyMjIEJhc2ljIGVsZW1lbnRzIG9mIGEgTWFwDQoNCi0gVGl0bGUNCi0gTGVnZW5kDQotIFNjYWxlDQotIE5vcnRoIGFycm93DQotIEFkZGl0aW9uYWwgaW5mb3JtYXRpb246IHRoaXMgY291bGQgYmUgYSBicmllZiBkZXNjaXB0aW9uIG9mIHlvdXIgbWFwLCAgZGF0YSBzb3VyY2VzLCBwcm9qZWN0aW9uIGluZm9ybWF0aW9uLCBhdXRob3IgaW5mb3JtYXRpb24gZWN0DQoNCg0KIyMjIEVmZmVjdGl2ZSBNYXBzDQoNCi0gQ29tbXVuaWNhdGVzIGEgY2xlYXIgbWVzc2FnZQ0KLSBBcHByb3ByaWF0ZSBwcm9qZWN0aW9uDQotIERhdGEgYXQgYXBwcm9wcmlhdGUgbGV2ZWwgb2YgZ2VuZXJhbGl6YXRpb24NCi0gQ2xlYXIgc3ltYm9sb2d5OiBzeW1ib2xzIG1hdGNoIHRoZSBkYXRhIGFuZCBhcmUgZWFzeSB0byBkaXN0aW5ndWlzaCBmcm9tIG9uZSBhbm90aGVyDQotIEdvb2QgZmlndXJlLWdyb3VuZCBvcmdhbml6YXRpb24gKGxheW91dCkNCi0gR29vZCB2aXN1YWwgaGllcmFyY2h5OiBpcyB0aGUgcmVhZGVyJ3MgZXllIGRyYXduIHRvIHRoZSBtb3N0IGltcG9ydGFudCBpbmZvcm1hdGlvbj8gVGhpcyBjYW4gYmUgYWNoaWV2ZWQgdGhyb3VnaCBzaXplLCBjb2xvciwgcGxhY2VtZW50IG9uIG1hcCBsYXlvdXQNCg0KDQojIENvbW1vbiBUeXBlcyBvZiBNYXBzDQoNCiMjIyBEb3QtRGVuc2l0eQ0KLSBVc2VzIHBvaW50IHN5bWJvbG9neSB0byB2aXN1YWxpemUgYSBnZW9ncmFwaGljIGRpc3RyaWJ1dGlvbiANCi0gUmVseSBvbiB2aXN1YWwgc2NhdHRlciB0byBzaG93IHNwYXRpYWwgcGF0dGVybnMgDQotIEVhY2ggZG90IHJlcHJlc2VudHMgYSBjb25zdGFudCBudW1iZXIgb2YgdGhpbmdzIChwZW9wbGUsIHRoaW5ncywgYW55IHF1YW5pZmlhYmxlIHBoZW5vbWVuYSkNCg0KDQpbVGhpcyBsaW5rIHByb3ZpZGVzIGEgd2FsayB0aHJvdWdoIG9mIG1ha2luZyBkb3QgZGVuc2l0eSBtYXBzIGZyb20gY2Vuc3VzIGRhdGFdKGh0dHBzOi8vZ2l0aHViLmNvbS90YXJha2MwMi90YXJha2MwMi5naXRodWIuaW8vYmxvYi9tYXN0ZXIvZG90LWRlbnNpdHkvaW5kZXgubWQpDQoNCmB0aWR5Y2Vuc3VzYCBwYWNrYWdlIGlzIHVzZWQgdG8gZG93bmxvYWQgcG9wdWxhdGlvbiBkYXRhIGZyb20gdGhlIFVTIENlbnN1cyBCdXJlYXUgDQoNCmB0aWdyaXNgIHBhY2thZ2UgaXMgdXNlZCB0byBkb3dubG9hZCBzaGFwZWZpbGVzIGZyb20gdGhlIFVTIENlbnN1cyBhbmQgbG9hZCB0aGVtIGludG8gUiBhcyBgc2ZgIG9iamVjdHMNCiAgICBbSGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL3dhbGtlcmtlL3RpZ3Jpcy9ibG9iL21hc3Rlci9SRUFETUUubWQpIGlzIGEgaGVscGZ1bCBndWlkZSBpZiB5b3UgYXJlIGludGVyZXN0ZWQgaW4gbGVhcm5pbmcgbW9yZSBhYm91dCB0aGUgYHRpZ3Jpc2AgcGFja2FnZS4gDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpsaWJyYXJ5KHRpZHljZW5zdXMpDQpsaWJyYXJ5KHRpZ3JpcykNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShzZikNCg0KDQpvcHRpb25zKHRpZ3Jpc191c2VfY2FjaGUgPSBUUlVFKSAjY2FjaGUgZGF0YSBzbyB5b3UgZG9uJ3QgaGF2ZSB0byByZWRvd25sb2FkIGV2ZXJ5dGltZSB5b3UgcnVuIHRoaXMgY2h1bmsNCg0KDQp2MTkgPC0gbG9hZF92YXJpYWJsZXMoMjAxOSwgImFjczUiLCBjYWNoZSA9IFRSVUUpICNsb2FkIGluIDIwMTkgZGF0YSwgYWNzNSByZWZlcnMgdG8gdGhlIDUgeWVhciBBbWVyaWNhbiBDb21tdW5pdHkgU3VydmV5DQogICAgICAgICAgICAgICAgICANCnYxOSAlPiUNCiAgICBtdXRhdGUodGFibGUgPSBzdHJfZXh0cmFjdChuYW1lLCAiXi4rXyIpKSAlPiUNCiAgICBmaWx0ZXIoc3RyX2RldGVjdChjb25jZXB0LCAiRURVQ0FUSU9OQUwgQVRUQUlOTUVOVCIpKSAlPiUNCiAgICBkcGx5cjo6c2VsZWN0KHRhYmxlLCBjb25jZXB0KSAlPiUgZGlzdGluY3QgJT4lIHByaW50KG4gPSBJbmYpDQoNCiAjY2FsbCBhIHRhYmxlIHNvIHlvdSBjYW4gc29ydCB0aHJvdWdoIGNlbnN1cyB0YWJsZXMgZGlyZWN0bHkgdGhyb3VnaCBSDQojY2Vuc3VzX2FwaV9rZXkoImtleSMiLCBpbnN0YWxsID0gVFJVRSkgICN5b3Ugd2lsbCBuZWVkIHRvIGRvd25sb2FkIGEgY2Vuc3VzIGFwaSBrZXkuIFRoaXMgaXMgZG9uZSB0aGUgdGhlIFVTIENlbnN1cyBXZWJzaXRlDQoNCmFjcyA8LSBnZXRfYWNzKCJ0cmFjdCIsIHRhYmxlID0gIkIxNTAwMyIsIGNhY2hlX3RhYmxlID0gVFJVRSwgICNzZWxlY3QgdGhlIHRhYmxlIHlvdSB3YW50DQogICAgICAgICAgICAgICBnZW9tZXRyeSA9IFRSVUUsIHN0YXRlID0gIjUxIiwgY291bnR5ID0gIjc2MCIsICAjVmlyZ2luaWEgaXMgc3RhdGUgNTEsIGNpdHkgb2YgUmljaG1vbmQgaXMgY291bnR5IDc2MA0KICAgICAgICAgICAgICAgeWVhciA9IDIwMTksIG91dHB1dCA9ICJ0aWR5IikNCg0KDQphY3MgPC0gYWNzICU+JQ0KICBtdXRhdGUoDQogICAgaWQgPSBzdHJfZXh0cmFjdCh2YXJpYWJsZSwgIlswLTldezN9JCIpICU+JSBhcy5pbnRlZ2VyICAgICN1c2UgdGlkeXZlcnNlIGNvbW1hbmRzIHRvIGNsYXNzaWZ5IGRhdGENCiAgKSAlPiUNCiAgZmlsdGVyKGlkID4gMSkgJT4lDQogIG11dGF0ZShlZHVjYXRpb24gPSBjYXNlX3doZW4oDQogICAgaWQgJT4lIGJldHdlZW4oMiwgMTYpIH4gIk5vIEhTIGRpcGxvbWEiLA0KICAgIGlkICU+JSBiZXR3ZWVuKDE3LCAyMSkgfiAiSFMsIG5vIEJhY2hlbG9ycyIsDQogICAgaWQgPT0gMjIgfiAiQmFjaGVsb3JzIiwNCiAgICBpZCA+IDIyIH4gIlBvc3QtQmFjaGVsb3JzIg0KICApKSAlPiUNCiAgZ3JvdXBfYnkoR0VPSUQsIGVkdWNhdGlvbikgJT4lDQogIHN1bW1hcmlzZShlc3RpbWF0ZSA9IHN1bShlc3RpbWF0ZSkpDQoNCg0KYGBgDQoNCg0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KI0NyZWF0aW5nIERvdHMNCg0KI3RoaXMgY29kZSBjcmVhdGVzIHRoZSBkb3RzIGZvciB0aGUgcGxvdC4gSSB1c2VkIHRoZSBsaW5rIHBvc3RlZCBhYm92ZSB0byB3YWxrIG15c2VsZiB0aHJvdWdoIHRoaXMgc3RlcA0KDQphY3Nfc3BsaXQgPC0gYWNzICU+JQ0KICBmaWx0ZXIoZXN0aW1hdGUgPiA1MCkgJT4lDQogIHNwbGl0KC4kZWR1Y2F0aW9uKQ0KDQoNCmdlbmVyYXRlX3NhbXBsZXMgPC0gZnVuY3Rpb24oZGF0YSkNCiAgc3VwcHJlc3NNZXNzYWdlcyhzdF9zYW1wbGUoZGF0YSwgc2l6ZSA9IHJvdW5kKGRhdGEkZXN0aW1hdGUgLyAxMCkpKQ0KDQpwb2ludHMgPC0gbWFwKGFjc19zcGxpdCwgZ2VuZXJhdGVfc2FtcGxlcykNCnBvaW50cyA8LSBpbWFwKHBvaW50cywNCiAgICAgICAgICAgICAgIH5zdF9zZihkYXRhX2ZyYW1lKGVkdWNhdGlvbiA9IHJlcCgueSwgbGVuZ3RoKC54KSkpLA0KICAgICAgICAgICAgICAgICAgICAgIGdlb21ldHJ5ID0gLngpKQ0KcG9pbnRzIDwtIGRvLmNhbGwocmJpbmQsIHBvaW50cykNCg0KDQpwb2ludHMgPC0gcG9pbnRzICU+JQ0KICBncm91cF9ieShlZHVjYXRpb24pICU+JQ0KICBzdW1tYXJpc2UoKQ0KDQpwb2ludHMgPC0gcG9pbnRzICU+JQ0KICBtdXRhdGUoZWR1Y2F0aW9uID0gZmFjdG9yKA0KICAgIGVkdWNhdGlvbiwNCiAgICBsZXZlbHMgPSBjKCJObyBIUyBkaXBsb21hIiwgIkhTLCBubyBCYWNoZWxvcnMiLA0KICAgICAgICAgICAgICAgIkJhY2hlbG9ycyIsICJQb3N0LUJhY2hlbG9ycyIpKSkNCg0KcG9pbnRzIDwtIHBvaW50cyAlPiUNCiAgbXV0YXRlKG5fcG9pbnRzID0gbWFwX2ludChnZW9tZXRyeSwgbnJvdykpDQpgYGANCioqRXhhbXBsZXMgb2YgYXZhaWxhYmxlIGRhdGFzZXRzKioNCg0KfCBGdW5jdGlvbiB8IERhdGFzZXRzIGF2YWlsYWJsZSB8IFllYXJzIGF2YWlsYWJsZSB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IGBuYXRpb24oKWAgfCBjYXJ0b2dyYXBoaWMgKDE6NW07IDE6MjBtKSB8IDIwMTMtMjAxOSB8DQp8IGBkaXZpc2lvbnMoKWAgfCBjYXJ0b2dyYXBoaWMgKDE6NTAwazsgMTo1bTsgMToyMG0pIHwgMjAxMy0yMDE5IHwNCnwgYHJlZ2lvbnMoKWAgfCBjYXJ0b2dyYXBoaWMgKDE6NTAwazsgMTo1bTsgMToyMG0pIHwgMjAxMy0yMDE5IHwNCnwgYHN0YXRlcygpYCB8IFRJR0VSL0xpbmU7IGNhcnRvZ3JhcGhpYyAoMTo1MDBrOyAxOjVtOyAxOjIwbSkgfCAxOTkwLCAyMDAwLCAyMDEwLTIwMTkgfA0KfCBgY291bnRpZXMoKWAgfCBUSUdFUi9MaW5lOyBjYXJ0b2dyYXBoaWMgKDE6NTAwazsgMTo1bTsgMToyMG0pIHwgMTk5MCwgMjAwMCwgMjAxMC0yMDE5IHwNCnwgYHRyYWN0cygpYCB8IFRJR0VSL0xpbmU7IGNhcnRvZ3JhcGhpYyAoMTo1MDBrKSB8IDE5OTAsIDIwMDAsIDIwMTAtMjAxOSB8DQp8IGBibG9ja19ncm91cHMoKWAgfCBUSUdFUi9MaW5lOyBjYXJ0b2dyYXBoaWMgKDE6NTAwaykgfCAxOTkwLCAyMDAwLCAyMDEwLTIwMTkgfA0KfCBgYmxvY2tzKClgIHwgVElHRVIvTGluZSB8IDIwMDAsIDIwMTAtMjAxOSB8DQp8IGBwbGFjZXMoKWAgfCBUSUdFUi9MaW5lOyBjYXJ0b2dyYXBoaWMgKDE6NTAwaykgfCAyMDExLTIwMTkgfA0KfCBgcHVtYXMoKWAgfCBUSUdFUi9MaW5lOyBjYXJ0b2dyYXBoaWMgKDE6NTAwaykgfCAyMDEyLTIwMTkgfA0KfCBgc2Nob29sX2Rpc3RyaWN0cygpYCB8IFRJR0VSL0xpbmU7IGNhcnRvZ3JhcGhpYyB8IDIwMTEtMjAxOSB8DQp8IGB6Y3RhcygpYCB8IFRJR0VSL0xpbmU7IGNhcnRvZ3JhcGhpYyAoMTo1MDBrKSB8IDIwMDAsIDIwMTAsIDIwMTItMjAxOSB8DQp8IGBjb25ncmVzc2lvbmFsX2Rpc3RyaWN0cygpYCB8IFRJR0VSL0xpbmU7IGNhcnRvZ3JhcGhpYyAoMTo1MDBrOyAxOjVtOyAxOjIwbSkgfCAyMDExLTIwMTkgfA0KfCBgc3RhdGVfbGVnaXNsYXRpdmVfZGlzdHJpY3RzKClgIHwgVElHRVIvTGluZTsgY2FydG9ncmFwaGljICgxOjUwMGspIHwgMjAxMS0yMDE5IHwNCnwgYHZvdGluZ19kaXN0cmljdHMoKWAgfCBUSUdFUi9MaW5lIHwgMjAxMiB8DQp8IGBhcmVhX3dhdGVyKClgIHwgVElHRVIvTGluZSB8IDIwMTEtMjAxOSB8DQp8IGBsaW5lYXJfd2F0ZXIoKWAgfCBUSUdFUi9MaW5lIHwgMjAxMS0yMDE5IHwNCmBgYHtyfQ0KbGlicmFyeSh0aWdyaXMpDQoNCndhdGVyIDwtIHRpZ3Jpczo6YXJlYV93YXRlcigiNTEiLCAiNzYwIikgICMgVkEgaXMgc3RhdGUgNTEsIFJWQSBpcyBjb3VudHkgNzYwDQpyb2FkcyA8LSB0aWdyaXM6OnJvYWRzKCI1MSIsICI3NjAiKQ0KY291bnRpZXMgPC0gdGlncmlzOjpjb3VudGllcyhzdGF0ZSA9ICI1MSIpICNmaWx0ZXIgVkEgZGF0YSB0byBnZXQgYSBtYXAgb2YgUmljaG1vbmQNCnJ2YSA8LSBjb3VudGllcyAlPiUNCiAgZmlsdGVyKENPVU5UWUZQID09IDc2MCkNCmBgYA0KDQoNCmBgYHtyLCBvdXQud2lkdGg9ICcxMDAlJywgZXZhbD0gRkFMU0UsIGVjaG89VFJVRX0NCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gcnZhLCBmaWxsID0gIndoaXRlIiwgbHdkID0gMSkgKw0KICBnZW9tX3NmKGRhdGEgPSB3YXRlciwgZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvdXIgPSAibGlnaHRibHVlIikgKw0KICBnZW9tX3NmKGRhdGEgPSByb2FkcywgY29sb3VyID0gImdyZXkiKSArDQogIGdlb21fc2YoZGF0YSA9IHBvaW50cywgICAgICAgICAgI3RoZSBjcmVhdGlvbiBvZiB0aGUgcG9pbnRzIGRhdGEgaXMgc2hvd24gaW4gYSBjaHVuayBub3QgaW5jbHVkZWQgaW4gdGhlIHB1Ymxpc2hlZCBkb2N1bWVudCwgYnV0IGlzIGluY2x1ZGVkIGlmIHlvdSBkb3dubG9hZCB0aGlzIG1hcmtkb3duIGZpbGUNCiAgICAgICAgICBhZXMoY29sb3VyID0gZWR1Y2F0aW9uLA0KICAgICAgICAgICAgICBmaWxsID0gZWR1Y2F0aW9uKSwNCiAgICAgICAgICBzaXplID0gLjUpICsNCiAgc2NhbGVfY29sb3JfYnJld2VyKHR5cGUgPSAiZGl2IiwgcGFsZXR0ZSA9IDQsDQogICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkVkdWNhdGlvbiIpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIodHlwZSA9ICJkaXYiLCBwYWxldHRlID0gNCkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgZWR1Y2F0aW9uYWwgYXR0YWlubWVudCBpbiBSaWNobW9uZCwgVmlyZ2luaWEgKDIwMTkpIiwNCiAgICAgICAgICAgICIxIGRvdCBlcXVhbHMgMTAgcGVvcGxlIikgKw0KICBndWlkZXMoZmlsbCA9ICJub25lIikNCg0KYGBgDQoNCiFbXShodHRwczovL2xpdmUuc3RhdGljZmxpY2tyLmNvbS82NTUzNS81MTAyMTYzMzY5N18zYzBiNzUzYWJjX3ouanBnKQ0KDQoNCiMjIyBQcm9wb3J0aW9uYWwgU3ltYm9scw0KLSBTaXplIG9mIHN5bWJvbHMgaXMgcmVsYXRlZCB0byB0aGUgdmFsdWUgb2YgcGhlbm9tZW5vbiByZXByZXNlbnRlZCAtLSB0aGUgbGFyZ2VyIHRoZSBzeW1ib2wgdGhlICJtb3JlIiBvZiBzb21ldGhpbmcgZXhpc3RzIGF0IHRoYXQgbG9jYXRpb24NCi0gTW9zdCBiYXNpYyBtZXRob2QgaXMgdG8gc2NhbGUgY2lyY2xlcyBkaXJlY3RseSBwcm9wb3J0aW9uYXRlIHRvIHRoZSBkYXRhDQotIENhbiBiZSB1c2VkIHdpdGggYm90aCBudW1lcmljIG9yIG9yZGVyZWQgY2F0YWdvcmljYWwgZGF0YQ0KDQpGb3IgdGhpcyBleGFtcGxlIEkgYWdhaW4gdXNlZCBkYXRhIGZyb20gYHRpZHljZW5zdXNgIGFuZCBgdGlncmlzYC4gVGhlIGNvZGUgZm9yIHByb2Nlc3NpbmcgdGhpcyBkYXRhIGlzIGF2YWlsYmxlIGluIHRoZSBtYXJrZG93biBkb2N1bWVudA0KDQpgYGB7ciwgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCnYxOSAlPiUNCiAgbXV0YXRlKHRhYmxlID0gc3RyX2V4dHJhY3QobmFtZSwgIl4uK18iKSkgJT4lDQogIGZpbHRlcihzdHJfZGV0ZWN0KGNvbmNlcHQsICJUT1RBTCBQT1BVTEFUSU9OIikpICU+JSAgICN2ZXJ5IHNpbWlsYXIgcHJvY2VzcyB0byB0aGUgZWR1Y2F0aW9uIGRhdGEgYWJvdmUNCiAgc2VsZWN0KHRhYmxlLCBjb25jZXB0KSAlPiUNCiAgZGlzdGluY3QoKSAlPiUNCiAgcHJpbnQobiA9IEluZikNCg0KYWNzIDwtIGdldF9hY3MoImNvdW50eSIsIHRhYmxlID0gIkIwMTAwMyIsIGNhY2hlX3RhYmxlID0gVFJVRSwgICNhc2tpbmcgZm9yIGRhdGEgZnJvbSB0aGUgQW1lcmljYW4gY29tbXVuaXR5IHN1cnZleSBhdCB0aGUgY291bnR5IGxldmVsLiB0YWJsZSA9ICJCMDEwMDMiIHNlbGVjdHMgdG90YWwgcG9wdWxhdGlvbiBkYXRhDQogICAgICAgICAgICAgICBnZW9tZXRyeSA9IFRSVUUsIHN0YXRlID0gIjUxIiwgI1ZpcmdpbmlhIGlzIHN0YXRlIDUxDQogICAgICAgICAgICAgICB5ZWFyID0gMjAxOSwgb3V0cHV0ID0gInRpZHkiKQ0KcG9pbnRzIDwtIGFjcyAlPiUNCiAgc3RfY2VudHJvaWQob2ZfbGFyZ2VzdF9wb2x5Z29uID0gVFJVRSkgICMgY3JlYXRlIHBvaW50cyB1c2luZyBjZW50cm9pZHMgb2YgcG9seWdvbnMNCmBgYA0KDQpOb3RlIHRoYXQgdGhlIGNpcmNsZXMgYXJlIHBsb3R0ZWQgYXMgcG9pbnRzLCBub3QgcG9seWdvbnMuIFByb3BvcnRpb25hdGUgY2lyY2xlIHNpemVzIGFyZSBtYWRlIHVzaW5nIGBhZXMoc2l6ZSA9IGRhdGEkYXR0cmlidXRlKWANCg0KYGBge3IsIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0NCg0KI3Byb3BvcnRpb25hdGUgY2lyY2xlIHNpemVzIGFyZSBtYWRlDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gY291bnRpZXMsIGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3IgPSAibGlnaHRibHVlIikgKw0KICBnZW9tX3NmKGRhdGEgPSBhY3MsIGZpbGwgPSAiZ3JleTk1IikgKw0KICBnZW9tX3NmKGRhdGEgPSBwb2ludHMsIGFlcyhzaXplID0gZXN0aW1hdGUpLCBjb2xvciA9ICJsaWdodHNhbG1vbjIiKSArICAjZGF0YSBwcm9jZXNzaW5nIGZvciB0aGUgcG9pbnRzIGRhdGEgaXMgc2hvd24gaW4gdGhlIHByZXZpb3VzIGNodW5rICh2aXNpYmxlIGluIHRoZSBtYXJrZG93biBkb2N1bWVudCkuIFBvaW50cyBhcmUgY3JlYXRlZCB1c2luZyBjZW50cm9pZHMgb2YgY291bnR5IHBvbHlnb25zDQogIHRoZW1lX21pbmltYWwoKSArDQogIGdndGl0bGUoIlBvcHVsYXRpb24gb2YgVmlyZ2luaWEgQ291bnRpZXMiLA0KICAgICAgICAgICIyMDE5IikgKw0KICBsYWJzKCBzaXplID0gIlBvcHVsYXRpb24iKSArDQogIHNjYWxlX3NpemVfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkNCg0KIA0KYGBgDQoNCiFbXShodHRwczovL2xpdmUuc3RhdGljZmxpY2tyLmNvbS82NTUzNS81MTAyMDgwNDMxM19iODhiMTU4MjY3X2IuanBnKQ0KDQoNCiMjIyBDaG9yb3BsZXRoDQotIFJlcHJlc2VudCBkYXRhIHRocm91Z2ggc2hhZGluZyBvciBzeW1ib2xzIG9uIHBvbHlnb25zDQotIFBvcHVsYXIgYmVjYXVzZSB0aGV5IGVhc2lseSByZXByZXNlbnQgdmFyaWFiaWxpdHkgZGF0YSBhY3Jvc3MgYSByZWdpb24NCg0KRm9yIHRoaXMgZXhhbXBsZSBJIGFnYWluIHVzZWQgZGF0YSBmcm9tIGB0aWR5Y2Vuc3VzYCBhbmQgYHRpZ3Jpc2AuIFRoZSBjb2RlIGZvciBwcm9jZXNzaW5nIHRoaXMgZGF0YSBpcyBhdmFpbGJsZSBpbiB0aGUgbWFya2Rvd24gZG9jdW1lbnQNCg0KYGBge3IsIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQoNCmFjcyA8LSBhY3MgJT4lDQogIG11dGF0ZShxdWFudCA9IGN1dChlc3RpbWF0ZSwgcXVhbnRpbGUoZXN0aW1hdGUsIHNlcSgwLDEsIGxlbiA9IDEwKSksICAjdXNpbmcgc2FtZSBwb3B1bGF0aW9uIGRhdGEgYXMgYWJvdmUNCiAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGUubG93ZXN0ID0gVFJVRSkpDQoNCmFjcyRwb3B1bGF0aW9uIDwtIGxvZyhhY3MkZXN0aW1hdGUpDQoNCmBgYA0KDQoNClRoZSBzY2FsaW5nIG9mIGNvbG9yIGlzIG1hZGUgYnkgYGFlcyhmaWxsID0gZGF0YSRhdHRyaWJ1dGUpYA0KDQpgYGB7ciwgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCg0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSBjb3VudGllcywgZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvciA9ICJsaWdodGJsdWUiKSArICAgICAgICAjdGhpcyBmaXJzdCBzdGVwIGlzIG9ubHkgZm9yIGFlc3RoZXRpY3MgKG1ha2VzIHRoZSBhcmVhcyBvZiB3YXRlciBibHVlKQ0KICBnZW9tX3NmKGRhdGEgPSBhY3MsIGFlcyhmaWxsID0gcG9wdWxhdGlvbiksIGNvbG9yID0gImJsYWNrIiwgbHdkID0gLjUpICsgICN0aGlzIHN0ZXAgY29udHJvbHMgdGhlIGNvbG9yIGZvciB0aGUgY2hvcm9wbGV0aCBtYXANCiAgc2NhbGVfZmlsbF9jb250aW51b3VzKHR5cGUgPSAidmlyaWRpcyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJQb3B1bGF0aW9uIChsb2cgdHJhbnNmb3JtZWQpIikrICAgICAjSSBsb2cgdHJhbnNmb3JtZWQgdGhpcyBkYXRhIGJlY2F1c2UgTm9ydGhlcm4gVmlyZ2luaWEgaXMgc28gbXVjaCBtb3JlIGRlbnNlbHkgcG9wdWxhdGVkIHRoYW4gdGhlIHJlc3Qgb2YgdGhlIHN0YXRlDQogIHRoZW1lX21pbmltYWwoKSArDQogIGdndGl0bGUoIlBvcHVsYXRpb24gRGVuc2l0eSBvZiBWaXJnaW5pYSBDb3VudGllcyIsDQogICAgICAgICAgIjIwMTkiKQ0KDQpgYGANCg0KIVtDaG9yb3BsZXRoXShodHRwczovL2xpdmUuc3RhdGljZmxpY2tyLmNvbS82NTUzNS81MTAyMTYzMzY2Ml83YzhjZGNkNjIzX2IuanBnKQ0KDQoNCg0KIyMjIElzb2xpbmUNCi0gTGluZXMgY29ubmVjdCBwb2ludHMga25vd3Mgb3IgZXN0aW1hdGVkIHRvIGhhdmUgZXF1YWwgdmFsZXMNCi0gVXNlZCBpbiBtYXBzIG9mIHRlbXBlcmF0dXJlLCBwcmVjaXBpdGF0aW9uLCBhaXIgcHJlc3N1cmUsIGNsaW1hdGUgY2xhc3NpZmljYXRpb25zLCBlY3QNCi0gVG9wb2dyYXBoaWMgbWFwczogdHlwZSBvZiBpc29saW5lIG1hcCBkZXBpY3RpbmcgZWxldmF0aW9uDQoNClRoZSBwYWNrYWdlIGBnZ2lzb2JhbmRzYCBjYW4gYmUgdXNlZCB0byBwbG90IGlzb2JhbmRzIGludG8gYGdncGxvdDJgLg0KeCwgeSwgYW5kIHogdmFsdWVzIGFyZSByZXF1aXJlZCBpbiBgYWVzKClgDQpUaGUgc2NhbGVkIGNvbG9ycyBhcmUgYWNoaWV2ZWQgaW4gYGFlcyhmaWxsID0pYA0KDQpbbGluayB0byBkb3dubG9hZCBkYXRhXShodHRwczovL2dpdGh1Yi5jb20vZHllcmxhYi9FTlZTLUxlY3R1cmVzL2Jsb2IvMTgwOWY3NmI5MWM0MzRmODRmNDE2YmNlN2NkM2FhZjcwMzNjMjk5Ni9kYXRhL0RFTV81bS50aWYpDQoNCkluIHRoaXMgZXhhbXBsZSB0aGUgc3RhcnRpbmcgZGF0YSBpcyBhIERpZ2l0YWwgRWxldmF0aW9uIE1vZGVsIChERU0pIHJhc3RlciBvZiBEb3dudG93biBSaWNobW9uZC4gYGdlb21faXNvYmFuZHNgIGNyZWF0ZXMgaXNvYmFuZHMgZnJvbSBwb2ludCBkYXRhLCBzbyB3ZSBtdXN0IGNvbnZlcnQgb3VyIHJhc3RlciB0byBwb2ludHMgYW5kIHRoZW4gcHV0IHRoZW0gaW4gYSBkYXRhIGZyYW1lLiBBZnRlciB0aGF0LCB0aGV5J3JlIHJlYWR5IHRvIGdvIQ0KDQpgYGB7ciwgZXZhbD1GQUxTRSwgZWNobz1UUlVFfQ0KbGlicmFyeShnZ2lzb2JhbmQpDQoNCkRFTV81bSA8LSByYXN0ZXIoIkM6L1VzZXJzL0NoYXJpcy9Eb2N1bWVudHMvU3ByaW5nMjAyMS9HSVNpblIvR0lTaW5SL0RFTV81bS50aWYiKQ0KDQpERU1fcHRzIDwtIHJhc3RlclRvUG9pbnRzKERFTV81bSwgc3BhdGlhbCA9IFRSVUUpICAgICAgICNJIHRyYW5zZm9ybWVkIG15IHJhc3RlciBkYXRhIGludG8gYSBkYXRhIGZyYW1lIG9mIHBvaW50cywgZWFjaCB3aXRoIGFuIHgsIHksIGFuZCB6IGNvbHVtbiAoeiBiZWluZyBlbGV2YXRpb24pDQpERU1fZGYgPC0gZGF0YS5mcmFtZShERU1fcHRzKQ0KREVNX2RmJERFTV81bSA8LSBpZmVsc2UoREVNX2RmJERFTV81bSA8MCwgMCwgREVNX2RmJERFTV81bSkgI1RoZXJlIHdhcyBhIHNlY3Rpb24gd2l0aCB2ZXJ5IG5lZ2F0aXZlIG51bWJlcnMgdGhhdCBJIGFzc3VtZWQgdG8gYmUgYSBkZWZlY3QgaW4gdGhlIGRhdGEsIHNvIEkgbWFkZSBhbnkgbmVnYXRpdmUgdmFsdWVzIDAgdG8gY2xlYW4gaXQgdXANCg0KDQpnZ3Bsb3QoREVNX2RmLCBhZXMoeD14LCB5PXksIHo9REVNXzVtKSkgKyAgICAgICAgICAgICAjWW91IG5lZWQgYW4geCwgeSwgYW5kIHogaW4gdGhlIGFlcygpDQogIGdlb21faXNvYmFuZHMoYWVzKGZpbGwgPSBzdGF0KHptaW4pKSwgY29sb3IgPSBOQSkgKyAjdGhlIHN0YXQgZnVuY3Rpb24gaXMgdXNlZCB0byB0ZWxsIGdncGxvdCB0aGF0IHlvdSB3YW50IGl0IHRvIHVzZSBhIGNhbGN1bGF0ZWQgYWVzdGhldGljIHByb2R1Y2VkIGJ5IHRoZSBzdGF0aXN0aWMgDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkNCmBgYA0KDQohW10oaHR0cHM6Ly9saXZlLnN0YXRpY2ZsaWNrci5jb20vNjU1MzUvNTEwMjE1MzE4OTFfN2EyYmEzNzY3ZV9iLmpwZyldDQoNClRoZXJlIGlzIGEgcGFja2FnZSwgYGlzb2JhbmRgIHRoYXQgZ2VuZXJhdGVzIGNvbnRvdXIgbGluZXMgdGhhdCBjYW4gYmUgdXNlZCB3aXRoIGBncmlkLnBhdGgoKWAgb3IgYGdyaWQucG9seWxpbmUoKWAgZnJvbSB0aGUgYGdyaWRgIGxpYnJhcnkuIFRvIHVzZSB0aGlzIGRhdGEgaW4gZ2dwbG90IGl0IG11c3QgYmUgY29udmVydGVkIHRvIGFuIGBzZmAgb2JqZWN0LiBBIHZpZ25ldHRlIG9mIHRoYXQgcGFja2FnZSBpcyBhdmFpbGFibGUgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS93aWxrZWxhYi9pc29iYW5kKS4NCg0KW1JvYmluIExvdmVsYWNlXShodHRwczovL2dlb2NvbXByLnJvYmlubG92ZWxhY2UubmV0L2dlb21ldHJpYy1vcGVyYXRpb25zLmh0bWwjcmFzdGVyLXZlY3RvcikgZXhwbGFpbiBzcGF0aWFsIHZlY3Rvcml6YXRpb24gYW5kIHNob3dzIGhvdyB0byB1c2UgdGhlIGByYXN0ZXJUb0NvbnRvdXIoKWAgZnVuY3Rpb24NCg0KDQpIZXJlIGlzIHRoZSBzYW1lIG1hcCB1c2luZyBgY29sb3I9YCBpbnN0ZWFkIG9mIGBmaWxsPWANCmBgYHtyLCBldmFsPUZBTFNFLCBlY2hvPUZBTFNFfQ0KDQojSGVyZSBpcyB0aGUgc2FtZSBtYXAgdXNpbmcgYGNvbG9yPWAgaW5zdGVhZCBvZiBgZmlsbD1gdG8gZ2V0IGlzb2xpbmVzIGluc3RlYWQgb2YgaXNvYmFuZHMNCg0KZ2dwbG90KERFTV9kZiwgYWVzKHg9eCwgeT15LCB6PURFTV81bSkpICsNCiAgZ2VvbV9pc29iYW5kcyhhZXMoY29sb3IgPSBzdGF0KHptaW4pKSwgZmlsbCA9IE5BKSArDQogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KDQojIEJhc2ljIE1hcCBNYWtpbmcNCg0KDQpUaGlzIGlzIHdoYXQgd2Ugc3RhcnRlZCB3b3JraW5nIHRocm91Z2ggZHVyaW5nIGNsYXNzLiBUaGUgZGF0YSBpcyBmcm9tIHRoZSBgcm5hdHVyYWxlYXJ0aGAgbGlicmFyeQ0KYGBge3J9DQpsaWJyYXJ5KCJnZ3Bsb3QyIikNCnRoZW1lX3NldCh0aGVtZV9idygpKSAgI2lmIHlvdSBoYXZlIGEgdGhlbWUgeW91IGtub3cgeW91IGxpa2UsIHN0aWNrIGl0IGhlcmUgc28geW91IGRvbid0IGhhdmUgdG8ga2VlcCB0eXBpbmcgaXQgaW4NCmxpYnJhcnkoInNmIikNCg0KbGlicmFyeSgicm5hdHVyYWxlYXJ0aCIpDQpsaWJyYXJ5KCJybmF0dXJhbGVhcnRoZGF0YSIpDQoNCmBgYA0KDQoNCm5vdGUgLSBgcmV0dXJuY2xhc3NgIGRlZmF1bHQgaXMgYHNwYA0KYGBge3J9DQoNCndvcmxkIDwtIG5lX2NvdW50cmllcyhzY2FsZSA9ICJtZWRpdW0iLCByZXR1cm5jbGFzcyA9ICJzZiIpDQpjbGFzcyh3b3JsZCkNCg0Kd29ybGQgPC0gd29ybGQgJT4lDQogIHNlbGVjdChpbmNvbWVfZ3JwLCBzdWJyZWdpb24sIG5hbWUsIG5hbWVfbG9uZywgbmFtZV9hbHQsIHBvcF9lc3QsIGxldmVsLCBzdWJ1bml0LCB0eXBlLCBlY29ub215KQ0KDQpgYGANCg0KVGhpcyBpcyBhIHBsYWluIG9sZCBnZ3Bsb3QsIG5vIGJlbGxzIG9yIHdoaXN0bGVzDQpgYGB7cn0NCg0KZ2dwbG90KHdvcmxkKSArDQogIGdlb21fc2YoKSAtPiBwDQoNCnANCg0KYGBgDQoNCiMjIyBUaXRsZSwgc3VidGl0bGUgJiBheGlzIGxhYmVscw0KDQpUaXRsZSBpcyBhZGRlZCB1c2luZyBgZ2d0aXRsZSgpYA0KU3VidGl0bGUgaXMgYWRkZWQgdXNpbmcgYHN1YnRpdGxlID1gIHdpdGhpbiB0aGUgYGdndGl0bGUoKWAgZnVuY3Rpb24NCmF4aXMgbGFiZWxzIGFyZSBhZGRlZCB3aXRoIGB4bGFiYCBhbmQgYHlsYWJgDQoNCmBgYHtyfQ0KcCArDQogIHhsYWIoIkxvbmdpdHVkZSIpICsNCiAgeWxhYigiTGF0aXR1ZGUiKSArDQogIGdndGl0bGUoIldvcmxkIE1hcCIsDQogICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZTAoIigiICxsZW5ndGgodW5pcXVlKHdvcmxkJG5hbWUpKSwgImNvdW50cmllcykiICkpIC0+IHAgI25vdGUgdGhhdCBJJ20gdXNpbmcgdGhlIGBwYXN0ZTAoKWAgZnVuY3Rpb24gdG8gaW5zZXJ0IGluZm9ybWF0aW9uIGZyb20gbXkgZGF0YSBpbnRvIG15IHN1YnRpdGxlDQoNCnANCg0KDQpgYGANCg0KDQojIyBDb2xvcg0KDQoqKkN1c3RvbSBwYWxldHRlKioNCg0KWW91IGNhbiBkZXNpZ24geW91ciBvd24gY3VzdG9tIHBhbGV0dGUgdXNpbmcgaGV4IGNvZGVzIG9yIHRoZSBhc3NpZ25lZCBjb2xvciBuYW1lcyBidWlsdCBpbnRvIFINCg0KYGBge3J9DQpnZ3Bsb3Qod29ybGQpICsNCiAgZ2VvbV9zZihhZXMoZmlsbCA9IGVjb25vbXkpKSAtPiBwbG90DQpgYGANCg0KYGBge3J9DQpwbG90ICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiZGFya3JlZCIsInJlZCIsInRvbWF0byIsICJvcmFuZ2UiLCAiZ29sZCIsICJ5ZWxsb3ciLCAibGltZWdyZWVuIikpDQpgYGANCg0KICANCmBgYHtyfQ0KbXlfY29sb3JzIDwtIGMoIiM5OTk5OTkiLCAiI0U2OUYwMCIsICIjNTZCNEU5IiwgIiMwMDlFNzMiLA0KICAgICAgICAgICIjRjBFNDQyIiwgIiMwMDcyQjIiLCAiI0Q1NUUwMCIpDQoNCmdncGxvdCh3b3JsZCkgKw0KICBnZW9tX3NmKGFlcyhmaWxsID0gZWNvbm9teSkpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBteV9jb2xvcnMpDQoNCmBgYA0KDQoNCmBgYHtyIGVjaG89VFJVRX0NCm15X2NvbG9ycyA8LSBjKCJkYXJrcmVkIiwidG9tYXRvIiwib3JhbmdlIiwibGltZWdyZWVuIiwiYmx1ZSIsIm1lZGl1bXB1cnBsZTIiLCJwaW5rIikNCg0KcGxvdCArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IG15X2NvbG9ycykNCmBgYA0KDQpJdCBtYXkgYmUgaGVscGZ1bCB0byB1c2UgYSB3ZWJzaXRlIGxpa2UgW2Nvb2xvcnNdKGh0dHBzOi8vY29vbG9ycy5jby9wYWxldHRlcy90cmVuZGluZykgdG8gY2hvb3NlIGNvbG9ycyBmb3IgeW91ciBwYWxldHRlDQoNCg0KDQoqKkJ1aWx0LWl0IGNvbG9yIHBhbGV0dGVzKioNCg0KW0NvbG9yIFBhbGV0dGUgQ2hlYXQgU2hlZXRdKGh0dHBzOi8vd3d3Lm5jZWFzLnVjc2IuZWR1L3NpdGVzL2RlZmF1bHQvZmlsZXMvMjAyMC0wNC9jb2xvclBhbGV0dGVDaGVhdHNoZWV0LnBkZikNCg0KVGhlcmUgYXJlIG1hbnkgcGFja2FnZXMgd2l0aCBwcmUtZGVmaW5lZCBjb2xvcnMgYW5kIHBhbGV0dGVzDQoNCi0gYHZpcmlkaXNgDQohW10oaHR0cHM6Ly93d3cuZGF0YW5vdmlhLmNvbS9lbi93cC1jb250ZW50L3VwbG9hZHMvZG4tdHV0b3JpYWxzL2dncGxvdDIvZmlndXJlcy8wMjktci1jb2xvci1wYWxldHRlcy12aXJpZGlzLWNvbG9yLXNjYWxlcy0xLnBuZyl7d2lkdGg9NTAlfQ0KDQoNCg0KLSBgUkNvbG9yQnJld2VyYA0KDQohW10oaHR0cHM6Ly93d3cuZGF0YW5vdmlhLmNvbS9lbi93cC1jb250ZW50L3VwbG9hZHMvZG4tdHV0b3JpYWxzL2dncGxvdDIvZmlndXJlcy8wMjktci1jb2xvci1wYWxldHRlcy1yY29sb3JicmV3ZXItcGFsZXR0ZXMtMS5wbmcpe3dpZHRoPTUwJX0NCg0KDQpJbnRvIFdlcyBBbmRlcnNvbidzIGNvbG9yIHNjaGVtZXM/IFRoZXJlJ3MgYSBsaWJyYXJ5IGZvciB0aGF0DQoNCi0gYHdlc2FuZGVyc29uYA0KDQohW10oaHR0cHM6Ly93d3cuZGF0YW5vdmlhLmNvbS9lbi93cC1jb250ZW50L3VwbG9hZHMvZG4tdHV0b3JpYWxzL2dncGxvdDIvZmlndXJlcy8wMjktci1jb2xvci1wYWxldHRlcy13ZXMtYW5kZXJzb24tY29sb3ItcGFsZXR0ZXMtci0xLnBuZyl7d2lkdGg9NTAlfQ0KDQoNCkkgY2hvc2UgdGhlIGB2aXJpZGlzX2NgIHBhbGV0dGUNCg0KLSBgdHJhbnMgPSBgIHRyYW5zZm9ybWF0aW9uIG9mIGRhdGEsIGluIHRoaXMgY2FzZSBzcXVhcmUgcm9vdA0KDQotIGBvcHRpb24gPSBgIGRpZmZlcmVudCBjb2xvciBwYWxldHRlczogQS1FLCBkZWZhdWx0IGlzIEQNCmBgYHtyIGVjaG89VFJVRX0NCg0KcCArDQogIGdlb21fc2YoYWVzKGZpbGwgPSBwb3BfZXN0KSkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfYyggb3B0aW9uID0gIkQiLCB0cmFucyA9ICJzcXJ0IikgLT4gcCAgIyBub3RlIHRoYXQgdGhlIGRhdGEgaXMgdHJhbnNmb3JtZWQNCg0KcA0KYGBgDQoNCkxldCdzIHN3aXRjaCBvdmVyIHRvIHRoZSBhbGwtdG9vLWZhbWlsaWFyIGJlZXRsZSBkYXRhc2V0DQoNCmBgYHtyIGVjaG89VFJVRX0NCmJlZXRsZV91cmwgPC0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9keWVybGFiL0VOVlMtTGVjdHVyZXMvbWFzdGVyL2RhdGEvQXJhcHR1c19EaXNwZXJhbF9CaWFzLmNzdiINCg0KcmVhZF9jc3YoYmVldGxlX3VybCkgJT4lDQogIHN0X2FzX3NmKCBjb29yZHM9YygiTG9uZ2l0dWRlIiwiTGF0aXR1ZGUiKSwgY3JzPTQzMjYgKSAlPiUgICAgI3RyYW5zZm9ybSB0byBzZg0KICBzdF91bmlvbigpICU+JQ0KICBzdF9idWZmZXIoZGlzdCA9IDEpICU+JQ0KICBzdF9iYm94KCkgLT4gYm91bmRzICN3aWxsIHVzZSBib3VuZHMgdG8gY3JvcCBvdXIgd29ybGQgZGF0YSB0byB0aGUgYmVldGxlIGFyZWENCg0KcmVhZF9jc3YoYmVldGxlX3VybCkgJT4lDQogIHN0X2FzX3NmKGNvb3Jkcz1jKCJMb25naXR1ZGUiLCJMYXRpdHVkZSIpLCBjcnMgPSA0MzI2KSAtPiBiZWV0bGUNCg0KZ2dwbG90KHdvcmxkKSArDQogIGdlb21fc2YoKSArDQogIHhsYWIoIkxvbmdpdHVkZSIpICsNCiAgeWxhYigiTGF0aXR1ZGUiKSArICAgICAgICAgICAgI25vdGUgdGhhdCB0aGUgeGxpbSBhbmQgeWxpbSB2YWx1ZXMgYXJlIGZyb20gYGJvdW5kc2ANCiAgZ2d0aXRsZSgiQmVldGxlcyIpICsNCiAgY29vcmRfc2YoIHhsaW0gPSBjKC0xMTYsIC0xMDgpLCB5bGltID0gYygyMiwgMzEpLCBleHBhbmQgPSBGQUxTRSkgLT4gYg0KDQpiDQpgYGANClRoZSByZXN1bHQgaXMgdGhlIHdvcmxkIGRhdGFzZXQgY3JvcHBlZCB0byB0aGUgYmV0dGxlIGRhdGFzZXQNCg0KIyMgU2NhbGUgYmFyIGFuZCBOb3J0aCBhcnJvdw0KDQpCb3RoIHRoZSBzY2FsZSBiYXIgYW5kIG5vcnRoIGFycm93IGNvbWUgZnJvbSBwYWNrYWdlIGBnZ3NwYXRpYWxgDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3NwYXRpYWwpDQoNCmIrDQogIGFubm90YXRpb25fc2NhbGUobG9jYXRpb24gPSAiYnIiLCBzdHlsZSA9ICJiYXIiKSArICAjcHV0IGEgYmFyIHN0eWxlIHNjYWxlIGJhciBpbiB0aGUgYm90dG9tIHJpZ2h0DQogIGFubm90YXRpb25fbm9ydGhfYXJyb3cobG9jYXRpb24gPSAiYnIiLCAgICAgICAgICAgICAjcHV0IHRoZSBub3J0aCBhcnJvdyBhbHNvIGluIHRoZSBib3R0b20gcmlnaHQNCiAgICAgICAgICAgICAgICAgICAgICAgICB3aWR0aCA9IHVuaXQoLjUsICJjbSIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBoZWlnaHQgPSB1bml0KC43NSwgImNtIiksICAgICNhc3NpZ24gYXJyb3cgaGVpZ2h0IGFuZCB3aWR0aA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBhZF95ID0gdW5pdCgwLjc1LCAiY20iKSwgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgcGFkX3ggPSB1bml0KDAuNSwgImNtIiksICAgICAjZ2l2ZSB0aGUgYXJyb3cgc29tZSBwYWRkaW5nIGJldHdlZW4gaXQgYW5kIHRoZSBheGVzDQogICAgICAgICAgICAgICAgICAgICAgICAgc3R5bGUgPSBub3J0aF9hcnJvd19vcmllbnRlZXJpbmcoICAjZGVmaW5lIHRoZSBzdHlsZSAmIHNpemUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHRfc2l6ZSA9IDgNCiAgICAgICAgICAgICAgICAgICAgICAgICApKSAtPiBiDQoNCmINCmBgYA0KDQoNCg0KYGBge3J9DQpnZ3Bsb3Qod29ybGQpICsNCiAgZ2VvbV9zZigpICsNCiAgeGxhYigiTG9uZ2l0dWRlIikgKw0KICB5bGFiKCJMYXRpdHVkZSIpICsNCiAgZ2d0aXRsZSgiQmVldGxlcyIpICsNCiAgY29vcmRfc2YoIHhsaW0gPSBjKC0xMTgsIC04NSksIA0KICAgICAgICAgICAgeWxpbSA9IGMoMTAsIDM1KSwgDQogICAgICAgICAgICBleHBhbmQgPSBGQUxTRSkgKw0KICBhbm5vdGF0aW9uX3NjYWxlKGxvY2F0aW9uID0gImJsIiwgDQogICAgICAgICAgICAgICAgICAgc3R5bGUgPSAiYmFyIikgKw0KICBhbm5vdGF0aW9uX25vcnRoX2Fycm93KGxvY2F0aW9uID0gImJsIiwgd2lkdGggPSB1bml0KC41LCAiY20iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBoZWlnaHQgPSB1bml0KC43NSwgImNtIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgcGFkX3kgPSB1bml0KDAuNzUsICJjbSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBhZF94ID0gdW5pdCgwLjUsICJjbSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHN0eWxlID0gbm9ydGhfYXJyb3dfb3JpZW50ZWVyaW5nKA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdGV4dF9zaXplID0gOA0KICAgICAgICAgICAgICAgICAgICAgICAgICkpIC0+IHANCg0KcA0KYGBgDQoNCg0KY2hhbmdlZCBleHRlbnQgdG8gc2VlIG1vcmUgY291bnRyaWVzDQoNCg0KIyMjIEFkZGluZyBuYW1lcw0KYGBge3J9DQpsaWJyYXJ5KGdncmVwZWwpDQoNCndvcmxkX3BvaW50czwtIHN0X2NlbnRyb2lkKHdvcmxkKQ0Kd29ybGRfcG9pbnRzIDwtIGNiaW5kKHdvcmxkLCBzdF9jb29yZGluYXRlcyhzdF9jZW50cm9pZCh3b3JsZCRnZW9tZXRyeSkpKQ0KDQpteV9wb2ludHMgPC0gd29ybGRfcG9pbnRzICU+JQ0KICBmaWx0ZXIoIFggPiAtMTE4ICYgWCA8IC04NSkgJT4lDQogIGZpbHRlciggWSA+IDEwICYgWSA8IDM1KQ0KDQpucm93KG15X3BvaW50cykNCnN1bW1hcnkod29ybGRfcG9pbnRzJFkpDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhPXdvcmxkLCBmaWxsID0gInNlYXNoZWxsIikgKw0KICB4bGFiKCJMb25naXR1ZGUiKSArDQogIHlsYWIoIkxhdGl0dWRlIikgKw0KICBnZ3RpdGxlKCJNZXhpY28iLCBzdWJ0aXRsZSA9ICImIEZyaWVuZHMiKSArDQogIGNvb3JkX3NmKCB4bGltID0gYygtMTE4LCAtODUpLCANCiAgICAgICAgICAgIHlsaW0gPSBjKDEwLCAzNSksIA0KICAgICAgICAgICAgZXhwYW5kID0gRkFMU0UpICsNCiAgYW5ub3RhdGlvbl9zY2FsZShsb2NhdGlvbiA9ICJibCIsIA0KICAgICAgICAgICAgICAgICAgIHN0eWxlID0gImJhciIpICsNCiAgYW5ub3RhdGlvbl9ub3J0aF9hcnJvdyhsb2NhdGlvbiA9ICJibCIsIHdpZHRoID0gdW5pdCguNSwgImNtIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgaGVpZ2h0ID0gdW5pdCguNzUsICJjbSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBhZF95ID0gdW5pdCgwLjc1LCAiY20iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBwYWRfeCA9IHVuaXQoMC41LCAiY20iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBzdHlsZSA9IG5vcnRoX2Fycm93X29yaWVudGVlcmluZygNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHRfc2l6ZSA9IDgNCiAgICAgICAgICAgICAgICAgICAgICAgICApKSArDQogIGdlb21fdGV4dF9yZXBlbChkYXRhPSBteV9wb2ludHMsYWVzKHg9WCwgeT1ZLCBsYWJlbD1uYW1lKSwgDQogICAgICAgICAgICAgICAgICBjb2xvciA9ICJyb3lhbGJsdWU0IiwgZm9udGZhY2UgPSAiYm9sZCIsDQogICAgICAgICAgICAgICAgICBzaXplID0gMy41LA0KICAgICAgICAgICAgICAgICAgcG9pbnQucGFkZGluZyA9IDAsDQogICAgICAgICAgICAgICAgICBib3gucGFkZGluZyA9IHVuaXQoLjUsICJjbSIpKSAgICsNCiAgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwNCiAgICAgICAgICAgICAgIHggPSAtOTAsIHkgPSAyNiwgDQogICAgICAgICAgICAgICBsYWJlbCA9ICJHdWxmIG9mIE1leGljbyIsIA0KICAgICAgICAgICAgICAgZm9udGZhY2UgPSAiaXRhbGljIiwgDQogICAgICAgICAgICAgICBjb2xvciA9ICJncmV5MjIiLCANCiAgICAgICAgICAgICAgIHNpemUgPSA0KSArDQogICAgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwNCiAgICAgICAgICAgICAgIHggPSAtMTEzLCB5ID0gMjEsIA0KICAgICAgICAgICAgICAgbGFiZWwgPSAiUGFjaWZpYyBPY2VhbiIsIA0KICAgICAgICAgICAgICAgZm9udGZhY2UgPSAiaXRhbGljIiwgDQogICAgICAgICAgICAgICBjb2xvciA9ICJncmV5MjIiLCANCiAgICAgICAgICAgICAgIHNpemUgPSA0KSArDQogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJhenVyZSIpKSAtPiBNZXhpY29fbWFwDQoNCk1leGljb19tYXANCmBgYA0KDQoNCg0KYGBge3J9DQptZXhpY28gPC0gbmVfY291bnRyaWVzKHNjYWxlID0gIm1lZGl1bSIsIHR5cGUgPSAiY291bnRyaWVzIiwgY291bnRyeSA9ICJNZXhpY28iLCByZXR1cm5jbGFzcyA9IGMoInNmIikpDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gbWV4aWNvLCAgYWVzKGZpbGwgPSAic3VidW5pdCIpKSArDQogIGdlb21fc2YoZGF0YSA9IGJlZXRsZSwgY29sb3IgPSAiZGFya3JlZCIpICsNCiAgeGxhYigiTG9uZ2l0dWRlIikgKw0KICB5bGFiKCJMYXRpdHVkZSIpICsNCiAgZ2d0aXRsZSgiQmVldGxlcyIpICsNCiAgY29vcmRfc2YoIHhsaW0gPSBjKC0xMTYsIC0xMDgpLCB5bGltID0gYygyMiwgMzEpLCBleHBhbmQgPSBGQUxTRSkgKw0KICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiYXp1cmUiKSkNCg0KYGBgDQoNCg0KIyMjIExheW91dHMNCg0KU2lkZS1ieS1TaWRlICANCmBgYHtyfQ0KbGlicmFyeShnZ3RoZW1lcykNCg0KZ2dwbG90KGRhdGEgPSB3b3JsZCkgKw0KICBnZW9tX3NmKGFlcyhmaWxsID0gcmVnaW9uX3diKSkgKw0KICBnZ3RpdGxlKCJXb3JsZCIpICsNCiAgbGFicyhmaWxsID0gIlJlZ2lvbiIsIHNpemUgPSAyKSArDQogIGdlb21fcmVjdCh4bWluID0gLTExNiwgeG1heCA9IC0xMDgsIHltaW4gPSAyMiwgeW1heCA9IDMxLCANCiAgICAgZmlsbCA9IE5BLCBjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMSkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgpICsNCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImF6dXJlIiksDQogICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEpKSAtPiB3b3JsZF9tYXANCg0Kd29ybGRfbWFwDQoNCg0KDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YT13b3JsZCwgZmlsbCA9ICJzZWFzaGVsbCIpICsNCiAgeGxhYigiTG9uZ2l0dWRlIikgKw0KICB5bGFiKCJMYXRpdHVkZSIpICsNCiAgZ2d0aXRsZSgiTWV4aWNvIikgKw0KICBjb29yZF9zZiggeGxpbSA9IGMoLTExOCwgLTg1KSwgDQogICAgICAgICAgICB5bGltID0gYygxMCwgMzUpLCANCiAgICAgICAgICAgIGV4cGFuZCA9IEZBTFNFKSArDQogIGFubm90YXRlKGdlb20gPSAidGV4dCIsDQogICAgICAgICAgICAgICB4ID0gLTkwLCB5ID0gMjYsIA0KICAgICAgICAgICAgICAgbGFiZWwgPSAiR3VsZiBvZiBNZXhpY28iLCANCiAgICAgICAgICAgICAgIGZvbnRmYWNlID0gIml0YWxpYyIsIA0KICAgICAgICAgICAgICAgY29sb3IgPSAiZ3JleTIyIiwgDQogICAgICAgICAgICAgICBzaXplID0gMSkgKw0KICAgIGFubm90YXRlKGdlb20gPSAidGV4dCIsDQogICAgICAgICAgICAgICB4ID0gLTExMywgeSA9IDIxLCANCiAgICAgICAgICAgICAgIGxhYmVsID0gIlBhY2lmaWMgT2NlYW4iLCANCiAgICAgICAgICAgICAgIGZvbnRmYWNlID0gIml0YWxpYyIsIA0KICAgICAgICAgICAgICAgY29sb3IgPSAiZ3JleTIyIiwgDQogICAgICAgICAgICAgICBzaXplID0gMSkgKw0KICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiYXp1cmUiKSkgLT4gTWV4aWNvDQpgYGANCg0KDQoNCmBgYHtyfQ0KbGlicmFyeShjb3dwbG90KQ0KDQpwbG90X2dyaWQod29ybGRfbWFwLCBNZXhpY28sIG5yb3cgPSAxLCByZWxfd2lkdGhzID0gYygyLjMsIDEpKSAtPiBwDQpwDQoNCmBgYA0KDQpJbnNldCBNYXANCg0KYGBge3J9DQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gY291bnRpZXMsIGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3IgPSAibGlnaHRibHVlIikgKw0KICBnZW9tX3NmKGRhdGEgPSBhY3MsIGZpbGwgPSAiZ3JleSIpICsNCiAgdGhlbWVfbWluaW1hbCgpIC0+IFZBX21hcA0KDQpWQV9tYXANCg0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHJ2YSwgZmlsbCA9ICJncmV5OTUiLCBsd2QgPSAxKSArDQogIGdlb21fc2YoZGF0YSA9IHdhdGVyLCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG91ciA9ICJsaWdodGJsdWUiKSArDQogIGdlb21fc2YoZGF0YSA9IHJvYWRzLCBjb2xvdXIgPSAiZ3JleSIpKw0KICBhbm5vdGF0ZSgidGV4dCIsIHggPSAtNzcuNTcsIHkgPSAzNy42MiwgbGFiZWw9ICJSaWNobW9uZCIsIHNpemUgPSAzKSArDQogIHRoZW1lX3ZvaWQoKSArDQogIGNvb3JkX3NmKGRhdHVtID0gTkEpIC0+IFJWQV9tYXANCg0KUlZBX21hcA0KDQpgYGANCg0KYGBge3J9DQoNCnN0X2Jib3goY291bnRpZXMpDQpzdF9iYm94KHJ2YSkNCg0KZ2dkcmF3KFZBX21hcCkgKw0KICBkcmF3X3Bsb3QoUlZBX21hcCwNCiAgICAgICAgICAgIHdpZHRoID0gMC4yNSwgaGVpZ2h0ID0gMC4yNSogNS8yLCANCiAgICAgICAgeCA9IC4xNSwgeSA9IC4zNSkgLT4gcA0KcA0KDQpgYGANCg0KDQoNCg0KQWRkaW5nIGFycm93cw0KDQpgYGB7cn0NCg0KYXJyb3dBIDwtIGRhdGEuZnJhbWUoeDEgPSAxOCwgeDIgPSAyNSwgeTEgPSAzLjQ1LCB5MiA9IDMuNTUpDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gcnZhLCBmaWxsID0gImdyZXk5NSIsIGx3ZCA9IDEpICsNCiAgZ2VvbV9zZihkYXRhID0gd2F0ZXIsIGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3VyID0gImxpZ2h0Ymx1ZSIpICsNCiAgZ2VvbV9zZihkYXRhID0gcm9hZHMsIGNvbG91ciA9ICJncmV5IikrDQogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IC03Ny41NzUsIHkgPSAzNy42MiwgbGFiZWw9ICJSaWNobW9uZCIsIHNpemUgPSAzKSsNCiAgdGhlbWVfdm9pZCgpICsNCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSBncmF5KDAuNSksIGxpbmV0eXBlID0gImRhc2hlZCIsIA0KICAgICAgICAgc2l6ZSA9IDAuNSksIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpLCANCiAgICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEpKSArDQogIGNvb3JkX3NmKGRhdHVtID0gTkEpIC0+IFJWQV9tYXANCg0KZ2dkcmF3KHhsaW0gPSBjKDAsIDM1KSwgeWxpbSA9IGMoMCwgNy41KSkgKw0KICBkcmF3X3Bsb3QoVkFfbWFwLCB4ID0gMCwgeSA9IDAsIHdpZHRoID0gMjUsIGhlaWdodCA9IDcuNSkgKw0KICBkcmF3X3Bsb3QoUlZBX21hcCwgeCA9IDI0LCB5ID0gLjUsIHdpZHRoID0gMTAsIGhlaWdodCA9IDcpKw0KICBnZW9tX3NlZ21lbnQoYWVzKHggPSB4MSwgeSA9IHkxLCB4ZW5kID0geDIsIHllbmQgPSB5MiksIGRhdGEgPSBhcnJvd0EsIA0KICAgICAgICBhcnJvdyA9IGFycm93KCksIGxpbmVlbmQgPSAicm91bmQiKSAtPiBhcnJvd19ib3gNCg0KYXJyb3dfYm94DQoNCmBgYA0KDQoNCg0K