Richard A. Lent

Maps, R Notebooks, and Code Chunks

Making maps on a computer has traditionally required the use of desktop geographic information system (GIS) software such as ArcGIS or QGIS. An alternative is to use R, a free software environment for statistical computing and graphics. R has many features that allow it to read GIS data and produce both static and interactive maps. This document (which is an R Notebook) shows how to make maps with R and RStudio, using R base graphics and the maps and mapdata packages, in addition to the leaflet and tmap packages. We will also map U.S. Census data with help from the tigris and acs packages.

It is assumed that the reader has some familiarity with R and RStudio. For additional information see An Introduction to R and RStudio as a Research and Writing Platform. See also Why I love R Notebooks. Also please see Habitat structure and phenotypic variation in the invading butterfly Coenonympha tullia, an R Notebook that illustrates how to use R and RStudio to write a complete academic manuscript.

To install R, go to the R download site. You must install R before installing RStudio. After installing R, visit the RStudio download site to download the RStudio installer for your computer platform. Choose the RStudio Desktop Open Source License (where it says FREE). When you run the RStudio installer, answer “yes” if it asks to install the command-line tools. Some R packages require the command-line tools so that they can be compiled and installed.

This R Notebook may be viewed online, where you can download the R Notebook document (click the first button labeled Code, then click Download Rmd). The notebook is a plain text file named maps.Rmd, which can be opened in RStudio. The notebook contains text written using R Markdown, a simple formatting language, interspersed with blocks of R source code, called “chunks.” The code chunks can be executed independently and interactively from inside the notebook using controls contained in each chunk. Output is visible immediately beneath the code chunk. Within a code chunk, the # symbol is used to denote comments, which are ignored by the R interpreter and serve to document the code.

A code chunk begins and ends with three backticks (found to the left of the numeral 1 on your keyboard). Following the initial three backticks is a pair of curly braces containing the letter r to signify that it is an R code chunk. The curly braces can contain additional R options, and all R code must be contained inside of the backticks. You can use the Insert menu of the RStudio text editor to insert an empty R code chunk ready for coding.

The following chunk establishes global options that apply to all of the other chunks in the document. Each chunk has a short title, following the r after the first curly brace, making it easier to navigate the chunks in the RStudio text editor.

knitr::opts_chunk$set(
    echo = FALSE,
    message = FALSE,
    warning = FALSE,
    comment = NA
)

Next we install a series of R packages, and other packages that those packages require, which are needed for the examples. The code first makes a list of the packages we want to install, then checks if they are already installed. The packages and their dependencies are then installed and loaded into the R environment. If you are running this for the first time after you have installed R and RStudio, be patient, as the packages may take a while to download and install.

# List of required packages.
.packages = c("maps", "mapdata", "Hmisc", "leaflet", "htmlwidgets", "rgdal", "tmap", "tigris", "acs", "stringr", "stringi", "sp", "dplyr", "mapview", "knitr", "rmarkdown")
# Install CRAN packages if not already installed.
.inst <- .packages %in% installed.packages()
if(length(.packages[!.inst]) > 0) install.packages(.packages[!.inst])
# Load packages without printing messages. 
suppressMessages(invisible(lapply(.packages, require, character.only=TRUE)))

In the various code chunks, we use the R functions invisible and suppressMessages to eliminate unwanted console output and automatic printing of values returned from functions. This is handy when the return value is an intermediate map layer that we want to use later but do not want to be printed to the screen or to the R Notebook. We also use function arguments, such as verbose = FALSE, to suppress additional console output that would otherwise clutter the R Notebook.

Each of the following four R code chunks produces a map using different technologies in R. The code can be executed interactively from inside the R Notebook. The code can also be copy-pasted directly into the R console and run from there, or copy-pasted into an empty R script file and run as a standalone program. The first comment line of each code chunk contains a suggested name for the script file.

Map 1: Base R graphics with the maps and mapdata packages

Our first map is created using the base R graphics package along with the maps and mapdata packages. Base R graphics is the standard graphics package that comes with R. It works by writing a series of graphics commands to a graphics window. The maps package enables display of geographic maps using map databases contained in the mapdata package.

We start by reading an external data file called sites.csv into an R data frame, a spreadsheet-like structure in which rows represent individual items of interest and columns are variables measured for each item. sites.csv is a real dataset and is described in Habitat structure and phenotypic variation in the invading butterfly Coenonympha tullia. The maps package is then used to draw the boundaries of Vermont, New Hampshire, and Massachusetts. Then, the sites data frame is used to map the locations of 11 study sites using their latitude-longitude coordinates, and to plot site labels next to each location. Next, the base graphics text function is used to place text labels for each of the three states. Finally, a box is drawn around the map and a scale added.

The code chunk also performs a simple linear regression of some butterfly morphological variables (see Habitat structure and phenotypic variation in the invading butterfly Coenonympha tullia) and saves the regression residuals to the sites data frame. There is an optional line of R code that uses the residuals to control the size of the plotting symbol. This illustrates the great advantage of using R to make maps, in that it is easy (if you know R) to map all manner of statistical results.

The appearance of the map can be changed by altering the parameters passed into the various graphics statements. For example, the col parameter controls the color used to draw a particular feature, pch changes the plotting symbol, and cex alters the size of a plotting symbol. At the R console, you can enter show.col and show.pch to display available colors and plotting symbols, respectively. Also, the colors() function will list the names of 657 available colors that can be used with the col parameter.

# sitesmap1.R
# Map the sites data using the maps package.
# Use the maps package to plot state boundaries 
# contained in the mapdata package
# and then overlay site locations using the points 
# function. Additional text is plotted
# using the text function to locate text using the 
# latitude-longitude coordinates of the map.
# We then draw a box around the map using the box 
# function and add a scale with the map.scale function.
# All of this works because the base graphics package in R 
# will keep drawing a sequence of graphic statements to 
# the same graphics window until the window is closed 
# or a new graphics window is created.
# The Hmisc package has functions show.col and show.pch to 
# display available color codes and plotting symbols, respectively.
# Use colors() to list names of 657 additional colors.
# See also the R color cheatsheet:
# https://www.nceas.ucsb.edu/~frazier/RSpatialGuides/colorPaletteCheatsheet.pdf
library(maps)     # Draw geographical maps.
library(mapdata)  # Map databases.
library(Hmisc)    # Miscellaneous useful functions.
sites <- read.csv("http://college.holycross.edu/faculty/rlent/sites/sites.csv")
# Do a simple linear regression of butterfly forewing length vs thorax width, 
# and save the regression residuals to the sites data frame.
reg <- with(sites, lm(fwlength ~ thorax))
sites$resid <- reg$residuals
# Make the map.
map('state', c('Vermont','New Hampshire','Massachusetts'), 
    xlim=c(-74, -69), ylim=c(41, 45.5),  
    col='gray90', fill=TRUE, mar = c(0, 0, 1, 0))
with(sites, points(lon_dd, lat_dd, pch=16, col='black', cex=.7))
# Comment out the previous 'with' statement, and uncomment the following 
# 'with' statement, to size the site points by the magnitude of their regression residual.
# with(sites, points(lon_dd, lat_dd, pch=16, col='black', cex=abs(resid)+1))
with(sites, text(lon_dd-.15, lat_dd, cex=0.7, labels=code))
text(-72, 42.4, labels='MA', cex=1.5)
text(-71.5, 43.4, labels='NH', cex=1.5)
text(-72.9, 44.3, labels='VT', cex=1.5)
box()
map.scale(ratio = FALSE, metric = FALSE)

Map 2: The leaflet package

We next produce the same basic map as in the previous example but using the leaflet package, which produces a prettier, interactive map that can be saved as a web page, exported to an image file, or embedded in an R Markdown document. Leaflet is the leading open-source JavaScript library for mobile-friendly interactive maps. (See this example from the New York Times.) The Leaflet package is an R wrapper for the Leaflet JavaScript mapping library, making it easy to generate interactive maps based on spatial data you have in R.

The function leaflet() returns a Leaflet map widget object that can receive various map layers, such as base maps and point, line, and polygon features. (A list of free base map providers for Leaflet is here.) Leaflet can map spatial data from several sources in R, including data frames containing latitudes and longitudes in addition to maps from the maps package. An additional package called htmlwidgets is used to save the Leaflet widget to an HTML page. The mapshot function of the mapview package can save a Leaflet interactive map as a static image file, for inclusion in documents, presentations, etc. There is an optional line of code at the end of this chunk to perform this function.

# sitesmap2.R
# Map the sites data using the leaflet package.
# This creates a prettier, interactive map.
library(leaflet)
library(maps)
library(htmlwidgets) # To save the map as a web page.
# The data to map.
sites <- read.csv("http://college.holycross.edu/faculty/rlent/sites/sites.csv")
# State boundaries from the maps package. The fill option must be TRUE.
bounds <- map('state', c('Massachusetts', 'Vermont', 'New Hampshire'), fill=TRUE, plot=FALSE)
# A custom icon.
icons <- awesomeIcons(
    icon = 'disc',
    iconColor = 'black',
    library = 'ion', # Options are 'glyphicon', 'fa', 'ion'.
    markerColor = 'blue',
    squareMarker = TRUE
)
# Create the Leaflet map widget and add some map layers.
# We use the pipe operator %>% to streamline the adding of
# layers to the leaflet object. The pipe operator comes from 
# the magrittr package via the dplyr package.
map <- leaflet(data = sites) %>%
    # setView(-72.14600, 43.82977, zoom = 8) %>% 
    addProviderTiles("CartoDB.Positron", group = "Map") %>%
    addProviderTiles("Esri.WorldImagery", group = "Satellite") %>% 
    addProviderTiles("Esri.WorldShadedRelief", group = "Relief") %>%
    # Marker data are from the sites data frame. We need the ~ symbols
    # to indicate the columns of the data frame.
    addMarkers(~lon_dd, ~lat_dd, label = ~locality, group = "Sites") %>% 
    # addAwesomeMarkers(~lon_dd, ~lat_dd, label = ~locality, group = "Sites", icon=icons) %>%
    addPolygons(data=bounds, group="States", weight=2, fillOpacity = 0) %>%
    addScaleBar(position = "bottomleft") %>%
    addLayersControl(
        baseGroups = c("Map", "Satellite", "Relief"),
        overlayGroups = c("Sites", "States"),
        options = layersControlOptions(collapsed = FALSE)
    )
invisible(print(map))

# Save the interactive map to an HTML page.
saveWidget(map, file="sitesmap2.html", selfcontained=TRUE)
# Uncomment the following line to save the interactive map to a static image file.
# mapshot(map, file="sitesmap2.png")

Map 3: The tmap package

The tmap package “offers a flexible, layer-based, and easy to use approach to create thematic maps, such as choropleths and bubble maps.” See tmap in a nutshell for further details. Here we present a simple example of using tmap to create a thematic map from an ESRI shapefile. The shapefile is a common GIS vector data format that has been around for a long time, and is used to represent points, lines, and enclosed areas (polygons). A shapefile is actually composed of several files that function together to provide the geographic features (points, lines, or polygons) along with a data table containing attributes of each feature.

There are many online repositories of GIS data that provide downloadable map layers in shapefile format. Here we use a shapefile from the Massachusetts Office of Geographic Information (MassGIS) that contains polygons delineating areas of critical enviromental concern. The R package rgdal provides function readOGR to read a shapefile into a SpatialPolygonsDataFrame, which is an R data frame that is able to contain spatial data. We then use the qtm (“quick thematic map”) function of the tmap package to produce the map. The polygons are color-coded according to their names, which are contained in the field NAME from the attribute table of the original shapefile downloaded from MassGIS. The code also uses two functions from the R utils package. The download.file function downloads the shapefile from MassGIS, using a URL passed to it. The multiple files comprising the shapefile map layer are provided by MassGIS as a compressed zip file. We then use the unzip function to extract the shapefile components to our working directory. The shapefile layer, named acecs_poly, is then passed to the readOGR function so that it can be brought into R.

tmap can function in two modes: plot mode and view mode. Plot mode creates a non-interactive map using R graphical plotting, whereas view mode creates an interactive Leaflet map. The tmap_mode function can be used to switch between plot mode and view mode.

# shape.R
# Use the tmap package to make a thematic map from an ESRI shapefile.
# The shapefile is from MassGIS, Areas of Critical Environmental Concern. 
# See https://goo.gl/vQxakO.
library(rgdal)
library(tmap)
# Download and extract the data.
# The polygon shapefile will be extracted to the working directory (use getwd()).
url <- "http://wsgw.mass.gov/data/gispub/shape/state/acecs.zip"
download.file(url, destfile = "acecs.zip", quiet = TRUE)
unzip("acecs.zip")
suppressMessages(tmap_mode("view")) # Options are "plot", "view".
map <- readOGR("./","acecs_poly", stringsAsFactors = FALSE, verbose = FALSE)
invisible(print(qtm(map, "NAME", fill.title="Name", format = "Europe_wide")))

Map 4: Mapping U. S. Census Data with the acs, tigris, and leaflet packages

Our final example is a bit more complicated. We use the leaflet package to map some data downloaded from the U. S. Census Bureau, using the acs and tigris packages. The acs package downloads tabular census data, while the tigris package is used to load Census TIGER shapefiles into R. TIGER stands for Topologically Integrated Geographic Encoding and Referencing, and TIGER shapefiles contain features such as roads, railroads, rivers, as well as legal and statistical geographic areas used by the U. S. Census.

This example was compiled and modified from Manipulating and mapping US Census data in R using the acs, tigris and leaflet packages, using the easy(er) way described in that document. Basically, the census shapfiles are downloaded using tigris and read into R spatial data frames. The tablular census data are downloaded using acs and the merged into the shapefile’s attribute table. From there, the merged data can then be given to the leaflet function to produce the interactive map.

While appreciably more complex than our previous examples, this map serves to illustrate the degree of data access, processing, and spatial mapping that can be accomplished with R and RStudio.

# census.R
# Mapping U. S. Census Data with R using the acs, tigris and leaflet packages.
# R code compiled and modified from https://goo.gl/TvLCRo
# Load required R packages.
library(tigris)      # U. S. Census maps.
library(acs)         # U. S. Census data.
library(stringr)     # To pad fips codes.
library(rgdal)       # For readOGR and others.
library(sp)          # For spatial objects.
library(leaflet)     # For interactive maps.
library(dplyr)       # For working with data frames.
library(htmlwidgets) # For saving the map.
# Download and store XML tables used to look up variable codes, table names, 
# and other metadata associated with the acs package. (May not be needed.)
# acs.tables.install() 
# Download the spatial data (tigris).
# Using Federal Information Processing Standards (FIPS) codes for counties.
# You can view this layer at the R console with plot(tracts). 
counties <- c(5, 47, 61, 81, 85)
suppressMessages(tracts <- tracts(state = 'NY', county=c(5, 47, 61, 81, 85), cb=TRUE))
# We need an API key (Application Program Interface) to access U. S. Census data.
# You can get your own Census data API key from 
# http://api.census.gov/data/key_signup.html.
api.key.install(key="474205a8b83bc35e69282fe60f5ea05170a06161")
# Create a geographic set to grab tabular data (acs).
geo <- geo.make(state=c("NY"),
              county=c(5, 47, 61, 81, 85), tract="*")
# Retrieve data for the five year span ending in 2012.
# You can view this data table by typing 'income' at the R console.
# Use of col.names = "pretty" gives the full column names.
# If instead you want Census variable IDs use col.names="auto". 
income <- acs.fetch(endyear = 2012, span = 5, geography = geo,
                  table.number = "B19001", col.names = "pretty")
# Convert to a data.frame for merging
income_df <- data.frame(paste0(str_pad(income@geography$state, 2, "left", pad="0"), 
          str_pad(income@geography$county, 3, "left", pad="0"), 
          str_pad(income@geography$tract, 6, "left", pad="0")), 
          income@estimate[,c("Household Income: Total:",
          "Household Income: $200,000 or more")], 
          stringsAsFactors = FALSE)
income_df <- select(income_df, 1:3)
rownames(income_df) <- 1:nrow(income_df)
names(income_df) <- c("GEOID", "total", "over_200")
income_df$percent <- 100*(income_df$over_200/income_df$total)
income_merged <- geo_join(tracts, income_df, "GEOID", "GEOID")
# There are some tracts with no land that we should exclude
income_merged <- income_merged[income_merged$ALAND>0,]
popup <- paste0("GEOID: ", income_merged$GEOID, "<br>", "Percent of Households above $200k: ", round(income_merged$percent,2))
pal <- colorNumeric(
    palette = "YlGnBu",
    domain = income_merged$percent
)
censusmap <- leaflet() %>%
    addProviderTiles(providers$Esri.WorldTopoMap) %>%
    addPolygons(data = income_merged, 
                fillColor = ~pal(percent), 
                color = "#b2aeae", # Need to use hex color codes.
                fillOpacity = 0.7, 
                weight = 1, 
                smoothFactor = 0.2,
                popup = popup) %>%
    addLegend(pal = pal,
              values = income_merged$percent, 
              position = "bottomright", 
              title = "Percent of Households<br>above $200k",
              labFormat = labelFormat(suffix = "%")) %>%
    addScaleBar(position = "topright")
# Display and save the map.
invisible(print(censusmap))

saveWidget(censusmap, file="censusmap.html", selfcontained=TRUE)
LS0tCnRpdGxlOiAiTWFraW5nIE1hcHMgd2l0aCBSIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAotLS0KCiMjIyMgW1JpY2hhcmQgQS4gTGVudF0oaHR0cDovL2NvbGxlZ2UuaG9seWNyb3NzLmVkdS9mYWN1bHR5L3JsZW50LykKCiMjIyBNYXBzLCBSIE5vdGVib29rcywgYW5kIENvZGUgQ2h1bmtzCgpNYWtpbmcgbWFwcyBvbiBhIGNvbXB1dGVyIGhhcyB0cmFkaXRpb25hbGx5IHJlcXVpcmVkIHRoZSB1c2Ugb2YgZGVza3RvcCBnZW9ncmFwaGljIGluZm9ybWF0aW9uIHN5c3RlbSAoR0lTKSBzb2Z0d2FyZSBzdWNoIGFzIFtBcmNHSVNdKGh0dHA6Ly9kZXNrdG9wLmFyY2dpcy5jb20vZW4vKSBvciBbUUdJU10oaHR0cDovL3FnaXMub3JnKS4gQW4gYWx0ZXJuYXRpdmUgaXMgdG8gdXNlIFtSXShodHRwczovL3d3dy5yLXByb2plY3Qub3JnKSwgYSBmcmVlIHNvZnR3YXJlIGVudmlyb25tZW50IGZvciBzdGF0aXN0aWNhbCBjb21wdXRpbmcgYW5kIGdyYXBoaWNzLiBSIGhhcyBtYW55IGZlYXR1cmVzIHRoYXQgYWxsb3cgaXQgdG8gcmVhZCBHSVMgZGF0YSBhbmQgcHJvZHVjZSBib3RoIHN0YXRpYyBhbmQgaW50ZXJhY3RpdmUgbWFwcy4gVGhpcyBkb2N1bWVudCAod2hpY2ggaXMgYW4gW1IgTm90ZWJvb2tdKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vcl9ub3RlYm9va3MuaHRtbCkpIHNob3dzIGhvdyB0byBtYWtlIG1hcHMgd2l0aCBSIGFuZCBbUlN0dWRpb10oaHR0cHM6Ly93d3cucnN0dWRpby5jb20pLCB1c2luZyBSIGJhc2UgZ3JhcGhpY3MgYW5kIHRoZSBbbWFwc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL21hcHMvaW5kZXguaHRtbCkgYW5kIFttYXBkYXRhXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvbWFwZGF0YS9pbmRleC5odG1sKSBwYWNrYWdlcywgaW4gYWRkaXRpb24gdG8gdGhlIFtsZWFmbGV0XShodHRwOi8vcnN0dWRpby5naXRodWIuaW8vbGVhZmxldC8pIGFuZCBbdG1hcF0oaHR0cHM6Ly9naXRodWIuY29tL210ZW5uZWtlcy90bWFwKSBwYWNrYWdlcy4gV2Ugd2lsbCBhbHNvIG1hcCBVLlMuIENlbnN1cyBkYXRhIHdpdGggaGVscCBmcm9tIHRoZSBbdGlncmlzXShodHRwczovL2dpdGh1Yi5jb20vd2Fsa2Vya2UvdGlncmlzKSBhbmQgW2Fjc10oaHR0cHM6Ly9naXRodWIuY29tL2NyYW4vYWNzKSBwYWNrYWdlcy4KCkl0IGlzIGFzc3VtZWQgdGhhdCB0aGUgcmVhZGVyIGhhcyBzb21lIGZhbWlsaWFyaXR5IHdpdGggUiBhbmQgUlN0dWRpby4gRm9yIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gc2VlIFtJbnRyb2R1Y3Rpb24gdG8gUl0oaHR0cDovL3RlY2gubWUuaG9seWNyb3NzLmVkdS8yMDE1LzAzLzE3L2ludHJvZHVjdGlvbi10by1yLykgYW5kIFtSU3R1ZGlvIGFzIGEgUmVzZWFyY2ggYW5kIFdyaXRpbmcgUGxhdGZvcm1dKGh0dHA6Ly90ZWNoLm1lLmhvbHljcm9zcy5lZHUvMjAxNi8xMi8wMS9yc3R1ZGlvLWFzLWEtcmVzZWFyY2gtYW5kLXdyaXRpbmctcGxhdGZvcm0vKS4gU2VlIGFsc28gW1doeSBJIGxvdmUgUiBOb3RlYm9va3NdKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3J2aWV3cy8yMDE3LzAzLzE1L3doeS1pLWxvdmUtci1ub3RlYm9va3MvKS4gQWxzbyBwbGVhc2Ugc2VlIFtIYWJpdGF0IHN0cnVjdHVyZSBhbmQgcGhlbm90eXBpYyB2YXJpYXRpb24gaW4gdGhlIGludmFkaW5nIGJ1dHRlcmZseSBDb2Vub255bXBoYSB0dWxsaWFdKGh0dHA6Ly9jb2xsZWdlLmhvbHljcm9zcy5lZHUvZmFjdWx0eS9ybGVudC9zaXRlcy9zaXRlcy5uYi5odG1sKSwgYW4gUiBOb3RlYm9vayB0aGF0IGlsbHVzdHJhdGVzIGhvdyB0byB1c2UgUiBhbmQgUlN0dWRpbyB0byB3cml0ZSBhIGNvbXBsZXRlIGFjYWRlbWljIG1hbnVzY3JpcHQuCgpUbyBpbnN0YWxsIFIsIGdvIHRvIHRoZSBbUiBkb3dubG9hZCBzaXRlXShodHRwOi8vbGliLnN0YXQuY211LmVkdS9SL0NSQU4vKS4gWW91IG11c3QgaW5zdGFsbCBSIGJlZm9yZSBpbnN0YWxsaW5nIFJTdHVkaW8uIEFmdGVyIGluc3RhbGxpbmcgUiwgdmlzaXQgdGhlIFtSU3R1ZGlvIGRvd25sb2FkIHNpdGVdKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3Byb2R1Y3RzL3JzdHVkaW8vZG93bmxvYWQvKSB0byBkb3dubG9hZCB0aGUgUlN0dWRpbyBpbnN0YWxsZXIgZm9yIHlvdXIgY29tcHV0ZXIgcGxhdGZvcm0uIENob29zZSB0aGUgUlN0dWRpbyBEZXNrdG9wIE9wZW4gU291cmNlIExpY2Vuc2UgKHdoZXJlIGl0IHNheXMgRlJFRSkuIFdoZW4geW91IHJ1biB0aGUgUlN0dWRpbyBpbnN0YWxsZXIsIGFuc3dlciAieWVzIiBpZiBpdCBhc2tzIHRvIGluc3RhbGwgdGhlIGNvbW1hbmQtbGluZSB0b29scy4gU29tZSBSIHBhY2thZ2VzIHJlcXVpcmUgdGhlIGNvbW1hbmQtbGluZSB0b29scyBzbyB0aGF0IHRoZXkgY2FuIGJlIGNvbXBpbGVkIGFuZCBpbnN0YWxsZWQuCgpUaGlzIFIgTm90ZWJvb2sgbWF5IFtiZSB2aWV3ZWQgb25saW5lXShodHRwOi8vY29sbGVnZS5ob2x5Y3Jvc3MuZWR1L2ZhY3VsdHkvcmxlbnQvbWFwcy5uYi5odG1sKSwgd2hlcmUgeW91IGNhbiBkb3dubG9hZCB0aGUgUiBOb3RlYm9vayBkb2N1bWVudCAoY2xpY2sgdGhlIGZpcnN0IGJ1dHRvbiBsYWJlbGVkIGBDb2RlYCwgdGhlbiBjbGljayBgRG93bmxvYWQgUm1kYCkuIFRoZSBub3RlYm9vayBpcyBhIHBsYWluIHRleHQgZmlsZSBuYW1lZCBgbWFwcy5SbWRgLCB3aGljaCBjYW4gYmUgb3BlbmVkIGluIFJTdHVkaW8uIFRoZSBub3RlYm9vayBjb250YWlucyB0ZXh0IHdyaXR0ZW4gdXNpbmcgW1IgTWFya2Rvd25dKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vbGVzc29uLTEuaHRtbCksIGEgc2ltcGxlIGZvcm1hdHRpbmcgbGFuZ3VhZ2UsIGludGVyc3BlcnNlZCB3aXRoIGJsb2NrcyBvZiBSIHNvdXJjZSBjb2RlLCBjYWxsZWQgImNodW5rcy4iIFRoZSBjb2RlIGNodW5rcyBjYW4gYmUgZXhlY3V0ZWQgaW5kZXBlbmRlbnRseSBhbmQgaW50ZXJhY3RpdmVseSBmcm9tIGluc2lkZSB0aGUgbm90ZWJvb2sgdXNpbmcgY29udHJvbHMgY29udGFpbmVkIGluIGVhY2ggY2h1bmsuIE91dHB1dCBpcyB2aXNpYmxlIGltbWVkaWF0ZWx5IGJlbmVhdGggdGhlIGNvZGUgY2h1bmsuIFdpdGhpbiBhIGNvZGUgY2h1bmssIHRoZSBgI2Agc3ltYm9sIGlzIHVzZWQgdG8gZGVub3RlIGNvbW1lbnRzLCB3aGljaCBhcmUgaWdub3JlZCBieSB0aGUgUiBpbnRlcnByZXRlciBhbmQgc2VydmUgdG8gZG9jdW1lbnQgdGhlIGNvZGUuCgpBIGNvZGUgY2h1bmsgYmVnaW5zIGFuZCBlbmRzIHdpdGggdGhyZWUgYmFja3RpY2tzIChmb3VuZCB0byB0aGUgbGVmdCBvZiB0aGUgbnVtZXJhbCAxIG9uIHlvdXIga2V5Ym9hcmQpLiBGb2xsb3dpbmcgdGhlIGluaXRpYWwgdGhyZWUgYmFja3RpY2tzIGlzIGEgcGFpciBvZiBjdXJseSBicmFjZXMgY29udGFpbmluZyB0aGUgbGV0dGVyIGByYCB0byBzaWduaWZ5IHRoYXQgaXQgaXMgYW4gUiBjb2RlIGNodW5rLiBUaGUgY3VybHkgYnJhY2VzIGNhbiBjb250YWluIGFkZGl0aW9uYWwgUiBvcHRpb25zLCBhbmQgYWxsIFIgY29kZSBtdXN0IGJlIGNvbnRhaW5lZCBpbnNpZGUgb2YgdGhlIGJhY2t0aWNrcy4gWW91IGNhbiB1c2UgdGhlIGBJbnNlcnRgIG1lbnUgb2YgdGhlIFJTdHVkaW8gdGV4dCBlZGl0b3IgdG8gaW5zZXJ0IGFuIGVtcHR5IFIgY29kZSBjaHVuayByZWFkeSBmb3IgY29kaW5nLgoKVGhlIGZvbGxvd2luZyBjaHVuayBlc3RhYmxpc2hlcyBnbG9iYWwgb3B0aW9ucyB0aGF0IGFwcGx5IHRvIGFsbCBvZiB0aGUgb3RoZXIgY2h1bmtzIGluIHRoZSBkb2N1bWVudC4gRWFjaCBjaHVuayBoYXMgYSBzaG9ydCB0aXRsZSwgZm9sbG93aW5nIHRoZSBgcmAgYWZ0ZXIgdGhlIGZpcnN0IGN1cmx5IGJyYWNlLCBtYWtpbmcgaXQgZWFzaWVyIHRvIG5hdmlnYXRlIHRoZSBjaHVua3MgaW4gdGhlIFJTdHVkaW8gdGV4dCBlZGl0b3IuCgpgYGB7ciBDaHVuayBvcHRpb25zLCBlY2hvPVRSVUV9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKCWVjaG8gPSBGQUxTRSwKCW1lc3NhZ2UgPSBGQUxTRSwKCXdhcm5pbmcgPSBGQUxTRSwKCWNvbW1lbnQgPSBOQQopCmBgYAoKTmV4dCB3ZSBpbnN0YWxsIGEgc2VyaWVzIG9mIFtSIHBhY2thZ2VzXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvKSwgYW5kIG90aGVyIHBhY2thZ2VzIHRoYXQgdGhvc2UgcGFja2FnZXMgcmVxdWlyZSwgd2hpY2ggYXJlIG5lZWRlZCBmb3IgdGhlIGV4YW1wbGVzLiBUaGUgY29kZSBmaXJzdCBtYWtlcyBhIGxpc3Qgb2YgdGhlIHBhY2thZ2VzIHdlIHdhbnQgdG8gaW5zdGFsbCwgdGhlbiBjaGVja3MgaWYgdGhleSBhcmUgYWxyZWFkeSBpbnN0YWxsZWQuIFRoZSBwYWNrYWdlcyBhbmQgdGhlaXIgZGVwZW5kZW5jaWVzIGFyZSB0aGVuIGluc3RhbGxlZCBhbmQgbG9hZGVkIGludG8gdGhlIFIgZW52aXJvbm1lbnQuIElmIHlvdSBhcmUgcnVubmluZyB0aGlzIGZvciB0aGUgZmlyc3QgdGltZSBhZnRlciB5b3UgaGF2ZSBpbnN0YWxsZWQgUiBhbmQgUlN0dWRpbywgYmUgcGF0aWVudCwgYXMgdGhlIHBhY2thZ2VzIG1heSB0YWtlIGEgd2hpbGUgdG8gZG93bmxvYWQgYW5kIGluc3RhbGwuCgpgYGB7ciBJbnN0YWxsIHBhY2thZ2VzLCBlY2hvPVRSVUV9CiMgTGlzdCBvZiByZXF1aXJlZCBwYWNrYWdlcy4KLnBhY2thZ2VzID0gYygibWFwcyIsICJtYXBkYXRhIiwgIkhtaXNjIiwgImxlYWZsZXQiLCAiaHRtbHdpZGdldHMiLCAicmdkYWwiLCAidG1hcCIsICJ0aWdyaXMiLCAiYWNzIiwgInN0cmluZ3IiLCAic3RyaW5naSIsICJzcCIsICJkcGx5ciIsICJtYXB2aWV3IiwgImtuaXRyIiwgInJtYXJrZG93biIpCgojIEluc3RhbGwgQ1JBTiBwYWNrYWdlcyBpZiBub3QgYWxyZWFkeSBpbnN0YWxsZWQuCi5pbnN0IDwtIC5wYWNrYWdlcyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpCmlmKGxlbmd0aCgucGFja2FnZXNbIS5pbnN0XSkgPiAwKSBpbnN0YWxsLnBhY2thZ2VzKC5wYWNrYWdlc1shLmluc3RdKQoKIyBMb2FkIHBhY2thZ2VzIHdpdGhvdXQgcHJpbnRpbmcgbWVzc2FnZXMuIApzdXBwcmVzc01lc3NhZ2VzKGludmlzaWJsZShsYXBwbHkoLnBhY2thZ2VzLCByZXF1aXJlLCBjaGFyYWN0ZXIub25seT1UUlVFKSkpCmBgYAoKSW4gdGhlIHZhcmlvdXMgY29kZSBjaHVua3MsIHdlIHVzZSB0aGUgUiBmdW5jdGlvbnMgW2ludmlzaWJsZV0oaHR0cDovL3JmdW5jdGlvbi5jb20vYXJjaGl2ZXMvNzk5KSBhbmQgW3N1cHByZXNzTWVzc2FnZXNdKGh0dHBzOi8vc3RhdC5ldGh6LmNoL1ItbWFudWFsL1ItZGV2ZWwvbGlicmFyeS9iYXNlL2h0bWwvbWVzc2FnZS5odG1sKSB0byBlbGltaW5hdGUgdW53YW50ZWQgY29uc29sZSBvdXRwdXQgYW5kIGF1dG9tYXRpYyBwcmludGluZyBvZiB2YWx1ZXMgcmV0dXJuZWQgZnJvbSBmdW5jdGlvbnMuIFRoaXMgaXMgaGFuZHkgd2hlbiB0aGUgcmV0dXJuIHZhbHVlIGlzIGFuIGludGVybWVkaWF0ZSBtYXAgbGF5ZXIgdGhhdCB3ZSB3YW50IHRvIHVzZSBsYXRlciBidXQgZG8gbm90IHdhbnQgdG8gYmUgcHJpbnRlZCB0byB0aGUgc2NyZWVuIG9yIHRvIHRoZSBSIE5vdGVib29rLiBXZSBhbHNvIHVzZSBmdW5jdGlvbiBhcmd1bWVudHMsIHN1Y2ggYXMgYHZlcmJvc2UgPSBGQUxTRWAsIHRvIHN1cHByZXNzIGFkZGl0aW9uYWwgY29uc29sZSBvdXRwdXQgdGhhdCB3b3VsZCBvdGhlcndpc2UgY2x1dHRlciB0aGUgUiBOb3RlYm9vay4KCkVhY2ggb2YgdGhlIGZvbGxvd2luZyBmb3VyIFIgY29kZSBjaHVua3MgcHJvZHVjZXMgYSBtYXAgdXNpbmcgZGlmZmVyZW50IHRlY2hub2xvZ2llcyBpbiBSLiBUaGUgY29kZSBjYW4gYmUgZXhlY3V0ZWQgaW50ZXJhY3RpdmVseSBmcm9tIGluc2lkZSB0aGUgUiBOb3RlYm9vay4gVGhlIGNvZGUgY2FuIGFsc28gYmUgY29weS1wYXN0ZWQgZGlyZWN0bHkgaW50byB0aGUgUiBjb25zb2xlIGFuZCBydW4gZnJvbSB0aGVyZSwgb3IgY29weS1wYXN0ZWQgaW50byBhbiBlbXB0eSBSIHNjcmlwdCBmaWxlIGFuZCBydW4gYXMgYSBzdGFuZGFsb25lIHByb2dyYW0uIFRoZSBmaXJzdCBjb21tZW50IGxpbmUgb2YgZWFjaCBjb2RlIGNodW5rIGNvbnRhaW5zIGEgc3VnZ2VzdGVkIG5hbWUgZm9yIHRoZSBzY3JpcHQgZmlsZS4gCgojIyMgTWFwIDE6IEJhc2UgUiBncmFwaGljcyB3aXRoIHRoZSBgbWFwc2AgYW5kIGBtYXBkYXRhYCBwYWNrYWdlcwoKT3VyIGZpcnN0IG1hcCBpcyBjcmVhdGVkIHVzaW5nIHRoZSBiYXNlIFIgW2dyYXBoaWNzXShodHRwczovL3N0YXQuZXRoei5jaC9SLW1hbnVhbC9SLWRldmVsL2xpYnJhcnkvZ3JhcGhpY3MvaHRtbC8wMEluZGV4Lmh0bWwpIHBhY2thZ2UgYWxvbmcgd2l0aCB0aGUgYG1hcHNgIGFuZCBgbWFwZGF0YWAgcGFja2FnZXMuIEJhc2UgUiBncmFwaGljcyBpcyB0aGUgc3RhbmRhcmQgZ3JhcGhpY3MgcGFja2FnZSB0aGF0IGNvbWVzIHdpdGggUi4gSXQgd29ya3MgYnkgd3JpdGluZyBhIHNlcmllcyBvZiBncmFwaGljcyBjb21tYW5kcyB0byBhIGdyYXBoaWNzIHdpbmRvdy4gVGhlIGBtYXBzYCBwYWNrYWdlIGVuYWJsZXMgZGlzcGxheSBvZiBnZW9ncmFwaGljIG1hcHMgdXNpbmcgbWFwIGRhdGFiYXNlcyBjb250YWluZWQgaW4gdGhlIGBtYXBkYXRhYCBwYWNrYWdlLiAKCldlIHN0YXJ0IGJ5IHJlYWRpbmcgYW4gZXh0ZXJuYWwgZGF0YSBmaWxlIGNhbGxlZCBbc2l0ZXMuY3N2XShodHRwOi8vY29sbGVnZS5ob2x5Y3Jvc3MuZWR1L2ZhY3VsdHkvcmxlbnQvc2l0ZXMvc2l0ZXMuY3N2KSBpbnRvIGFuIFIgZGF0YSBmcmFtZSwgYSBzcHJlYWRzaGVldC1saWtlIHN0cnVjdHVyZSBpbiB3aGljaCByb3dzIHJlcHJlc2VudCBpbmRpdmlkdWFsIGl0ZW1zIG9mIGludGVyZXN0IGFuZCBjb2x1bW5zIGFyZSB2YXJpYWJsZXMgbWVhc3VyZWQgZm9yIGVhY2ggaXRlbS4gYHNpdGVzLmNzdmAgaXMgYSByZWFsIGRhdGFzZXQgYW5kIGlzIGRlc2NyaWJlZCBpbiBbSGFiaXRhdCBzdHJ1Y3R1cmUgYW5kIHBoZW5vdHlwaWMgdmFyaWF0aW9uIGluIHRoZSBpbnZhZGluZyBidXR0ZXJmbHkgQ29lbm9ueW1waGEgdHVsbGlhXShodHRwOi8vY29sbGVnZS5ob2x5Y3Jvc3MuZWR1L2ZhY3VsdHkvcmxlbnQvc2l0ZXMvc2l0ZXMubmIuaHRtbCkuIFRoZSBgbWFwc2AgcGFja2FnZSBpcyB0aGVuIHVzZWQgdG8gZHJhdyB0aGUgYm91bmRhcmllcyBvZiBWZXJtb250LCBOZXcgSGFtcHNoaXJlLCBhbmQgTWFzc2FjaHVzZXR0cy4gVGhlbiwgdGhlIGBzaXRlc2AgZGF0YSBmcmFtZSBpcyB1c2VkIHRvIG1hcCB0aGUgbG9jYXRpb25zIG9mIDExIHN0dWR5IHNpdGVzIHVzaW5nIHRoZWlyIGxhdGl0dWRlLWxvbmdpdHVkZSBjb29yZGluYXRlcywgYW5kIHRvIHBsb3Qgc2l0ZSBsYWJlbHMgbmV4dCB0byBlYWNoIGxvY2F0aW9uLiBOZXh0LCB0aGUgYmFzZSBncmFwaGljcyBbdGV4dF0oaHR0cHM6Ly9zdGF0LmV0aHouY2gvUi1tYW51YWwvUi1kZXZlbC9saWJyYXJ5L2dyYXBoaWNzL2h0bWwvdGV4dC5odG1sKSBmdW5jdGlvbiBpcyB1c2VkIHRvIHBsYWNlIHRleHQgbGFiZWxzIGZvciBlYWNoIG9mIHRoZSB0aHJlZSBzdGF0ZXMuIEZpbmFsbHksIGEgYm94IGlzIGRyYXduIGFyb3VuZCB0aGUgbWFwIGFuZCBhIHNjYWxlIGFkZGVkLgoKVGhlIGNvZGUgY2h1bmsgYWxzbyBwZXJmb3JtcyBhIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiBvZiBzb21lIGJ1dHRlcmZseSBtb3JwaG9sb2dpY2FsIAp2YXJpYWJsZXMgKHNlZSBbSGFiaXRhdCBzdHJ1Y3R1cmUgYW5kIHBoZW5vdHlwaWMgdmFyaWF0aW9uIGluIHRoZSBpbnZhZGluZyBidXR0ZXJmbHkgQ29lbm9ueW1waGEgdHVsbGlhXShodHRwOi8vY29sbGVnZS5ob2x5Y3Jvc3MuZWR1L2ZhY3VsdHkvcmxlbnQvc2l0ZXMvc2l0ZXMubmIuaHRtbCkpIGFuZCBzYXZlcyB0aGUgcmVncmVzc2lvbiByZXNpZHVhbHMgdG8gdGhlIGBzaXRlc2AgZGF0YSBmcmFtZS4gVGhlcmUgaXMgYW4gb3B0aW9uYWwgbGluZSBvZiBSIGNvZGUgdGhhdCB1c2VzIHRoZSByZXNpZHVhbHMgdG8gY29udHJvbCB0aGUgc2l6ZSBvZiB0aGUgcGxvdHRpbmcgc3ltYm9sLiBUaGlzIGlsbHVzdHJhdGVzIHRoZSBncmVhdCBhZHZhbnRhZ2Ugb2YgdXNpbmcgUiB0byBtYWtlIG1hcHMsIGluIHRoYXQgaXQgaXMgZWFzeSAoaWYgeW91IGtub3cgUikgdG8gbWFwIGFsbCBtYW5uZXIgb2Ygc3RhdGlzdGljYWwgcmVzdWx0cy4KClRoZSBhcHBlYXJhbmNlIG9mIHRoZSBtYXAgY2FuIGJlIGNoYW5nZWQgYnkgYWx0ZXJpbmcgdGhlIHBhcmFtZXRlcnMgcGFzc2VkIGludG8gdGhlIHZhcmlvdXMgZ3JhcGhpY3Mgc3RhdGVtZW50cy4gRm9yIGV4YW1wbGUsIHRoZSBgY29sYCBwYXJhbWV0ZXIgY29udHJvbHMgdGhlIGNvbG9yIHVzZWQgdG8gZHJhdyBhIHBhcnRpY3VsYXIgZmVhdHVyZSwgYHBjaGAgY2hhbmdlcyB0aGUgcGxvdHRpbmcgc3ltYm9sLCBhbmQgYGNleGAgYWx0ZXJzIHRoZSBzaXplIG9mIGEgcGxvdHRpbmcgc3ltYm9sLiBBdCB0aGUgUiBjb25zb2xlLCB5b3UgY2FuIGVudGVyIGBzaG93LmNvbGAgYW5kIGBzaG93LnBjaGAgdG8gZGlzcGxheSBhdmFpbGFibGUgY29sb3JzIGFuZCBwbG90dGluZyBzeW1ib2xzLCByZXNwZWN0aXZlbHkuIEFsc28sIHRoZSBgY29sb3JzKClgIGZ1bmN0aW9uIHdpbGwgbGlzdCB0aGUgbmFtZXMgb2YgNjU3IGF2YWlsYWJsZSBjb2xvcnMgdGhhdCBjYW4gYmUgdXNlZCB3aXRoIHRoZSBgY29sYCBwYXJhbWV0ZXIuCgpgYGB7ciBzaXRlc21hcDEuUiwgZWNobz1UUlVFfQojIHNpdGVzbWFwMS5SCgojIE1hcCB0aGUgc2l0ZXMgZGF0YSB1c2luZyB0aGUgbWFwcyBwYWNrYWdlLgoKIyBVc2UgdGhlIG1hcHMgcGFja2FnZSB0byBwbG90IHN0YXRlIGJvdW5kYXJpZXMgCiMgY29udGFpbmVkIGluIHRoZSBtYXBkYXRhIHBhY2thZ2UKIyBhbmQgdGhlbiBvdmVybGF5IHNpdGUgbG9jYXRpb25zIHVzaW5nIHRoZSBwb2ludHMgCiMgZnVuY3Rpb24uIEFkZGl0aW9uYWwgdGV4dCBpcyBwbG90dGVkCiMgdXNpbmcgdGhlIHRleHQgZnVuY3Rpb24gdG8gbG9jYXRlIHRleHQgdXNpbmcgdGhlIAojIGxhdGl0dWRlLWxvbmdpdHVkZSBjb29yZGluYXRlcyBvZiB0aGUgbWFwLgojIFdlIHRoZW4gZHJhdyBhIGJveCBhcm91bmQgdGhlIG1hcCB1c2luZyB0aGUgYm94IAojIGZ1bmN0aW9uIGFuZCBhZGQgYSBzY2FsZSB3aXRoIHRoZSBtYXAuc2NhbGUgZnVuY3Rpb24uCiMgQWxsIG9mIHRoaXMgd29ya3MgYmVjYXVzZSB0aGUgYmFzZSBncmFwaGljcyBwYWNrYWdlIGluIFIgCiMgd2lsbCBrZWVwIGRyYXdpbmcgYSBzZXF1ZW5jZSBvZiBncmFwaGljIHN0YXRlbWVudHMgdG8gCiMgdGhlIHNhbWUgZ3JhcGhpY3Mgd2luZG93IHVudGlsIHRoZSB3aW5kb3cgaXMgY2xvc2VkIAojIG9yIGEgbmV3IGdyYXBoaWNzIHdpbmRvdyBpcyBjcmVhdGVkLgoKIyBUaGUgSG1pc2MgcGFja2FnZSBoYXMgZnVuY3Rpb25zIHNob3cuY29sIGFuZCBzaG93LnBjaCB0byAKIyBkaXNwbGF5IGF2YWlsYWJsZSBjb2xvciBjb2RlcyBhbmQgcGxvdHRpbmcgc3ltYm9scywgcmVzcGVjdGl2ZWx5LgojIFVzZSBjb2xvcnMoKSB0byBsaXN0IG5hbWVzIG9mIDY1NyBhZGRpdGlvbmFsIGNvbG9ycy4KIyBTZWUgYWxzbyB0aGUgUiBjb2xvciBjaGVhdHNoZWV0OgojIGh0dHBzOi8vd3d3Lm5jZWFzLnVjc2IuZWR1L35mcmF6aWVyL1JTcGF0aWFsR3VpZGVzL2NvbG9yUGFsZXR0ZUNoZWF0c2hlZXQucGRmCgpsaWJyYXJ5KG1hcHMpICAgICAjIERyYXcgZ2VvZ3JhcGhpY2FsIG1hcHMuCmxpYnJhcnkobWFwZGF0YSkgICMgTWFwIGRhdGFiYXNlcy4KbGlicmFyeShIbWlzYykgICAgIyBNaXNjZWxsYW5lb3VzIHVzZWZ1bCBmdW5jdGlvbnMuCgpzaXRlcyA8LSByZWFkLmNzdigiaHR0cDovL2NvbGxlZ2UuaG9seWNyb3NzLmVkdS9mYWN1bHR5L3JsZW50L3NpdGVzL3NpdGVzLmNzdiIpCgojIERvIGEgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG9mIGJ1dHRlcmZseSBmb3Jld2luZyBsZW5ndGggdnMgdGhvcmF4IHdpZHRoLCAKIyBhbmQgc2F2ZSB0aGUgcmVncmVzc2lvbiByZXNpZHVhbHMgdG8gdGhlIHNpdGVzIGRhdGEgZnJhbWUuCnJlZyA8LSB3aXRoKHNpdGVzLCBsbShmd2xlbmd0aCB+IHRob3JheCkpCnNpdGVzJHJlc2lkIDwtIHJlZyRyZXNpZHVhbHMKCiMgTWFrZSB0aGUgbWFwLgptYXAoJ3N0YXRlJywgYygnVmVybW9udCcsJ05ldyBIYW1wc2hpcmUnLCdNYXNzYWNodXNldHRzJyksIAogICAgeGxpbT1jKC03NCwgLTY5KSwgeWxpbT1jKDQxLCA0NS41KSwgIAogICAgY29sPSdncmF5OTAnLCBmaWxsPVRSVUUsIG1hciA9IGMoMCwgMCwgMSwgMCkpCndpdGgoc2l0ZXMsIHBvaW50cyhsb25fZGQsIGxhdF9kZCwgcGNoPTE2LCBjb2w9J2JsYWNrJywgY2V4PS43KSkKIyBDb21tZW50IG91dCB0aGUgcHJldmlvdXMgJ3dpdGgnIHN0YXRlbWVudCwgYW5kIHVuY29tbWVudCB0aGUgZm9sbG93aW5nIAojICd3aXRoJyBzdGF0ZW1lbnQsIHRvIHNpemUgdGhlIHNpdGUgcG9pbnRzIGJ5IHRoZSBtYWduaXR1ZGUgb2YgdGhlaXIgcmVncmVzc2lvbiByZXNpZHVhbC4KIyB3aXRoKHNpdGVzLCBwb2ludHMobG9uX2RkLCBsYXRfZGQsIHBjaD0xNiwgY29sPSdibGFjaycsIGNleD1hYnMocmVzaWQpKzEpKQp3aXRoKHNpdGVzLCB0ZXh0KGxvbl9kZC0uMTUsIGxhdF9kZCwgY2V4PTAuNywgbGFiZWxzPWNvZGUpKQp0ZXh0KC03MiwgNDIuNCwgbGFiZWxzPSdNQScsIGNleD0xLjUpCnRleHQoLTcxLjUsIDQzLjQsIGxhYmVscz0nTkgnLCBjZXg9MS41KQp0ZXh0KC03Mi45LCA0NC4zLCBsYWJlbHM9J1ZUJywgY2V4PTEuNSkKYm94KCkKbWFwLnNjYWxlKHJhdGlvID0gRkFMU0UsIG1ldHJpYyA9IEZBTFNFKQpgYGAKCiMjIyBNYXAgMjogVGhlIGBsZWFmbGV0YCBwYWNrYWdlCgpXZSBuZXh0IHByb2R1Y2UgdGhlIHNhbWUgYmFzaWMgbWFwIGFzIGluIHRoZSBwcmV2aW91cyBleGFtcGxlIGJ1dCB1c2luZyB0aGUgW2xlYWZsZXRdKGh0dHBzOi8vcnN0dWRpby5naXRodWIuaW8vbGVhZmxldC8pIHBhY2thZ2UsIHdoaWNoIHByb2R1Y2VzIGEgcHJldHRpZXIsIGludGVyYWN0aXZlIG1hcCB0aGF0IGNhbiBiZSBzYXZlZCBhcyBhIHdlYiBwYWdlLCBleHBvcnRlZCB0byBhbiBpbWFnZSBmaWxlLCBvciBlbWJlZGRlZCBpbiBhbiBSIE1hcmtkb3duIGRvY3VtZW50LiBbTGVhZmxldF0oaHR0cDovL2xlYWZsZXRqcy5jb20vKSBpcyB0aGUgbGVhZGluZyBvcGVuLXNvdXJjZSBKYXZhU2NyaXB0IGxpYnJhcnkgZm9yIG1vYmlsZS1mcmllbmRseSBpbnRlcmFjdGl2ZSBtYXBzLiAoU2VlIFt0aGlzIGV4YW1wbGVdKGh0dHA6Ly93d3cubnl0aW1lcy5jb20vcHJvamVjdHMvZWxlY3Rpb25zLzIwMTMvbnljLXByaW1hcnkvbWF5b3IvbWFwLmh0bWwpIGZyb20gdGhlIE5ldyBZb3JrIFRpbWVzLikgVGhlIFtMZWFmbGV0IHBhY2thZ2VdKGh0dHBzOi8vcnN0dWRpby5naXRodWIuaW8vbGVhZmxldC8pIGlzIGFuIFIgd3JhcHBlciBmb3IgdGhlIExlYWZsZXQgSmF2YVNjcmlwdCBtYXBwaW5nIGxpYnJhcnksIG1ha2luZyBpdCBlYXN5IHRvIGdlbmVyYXRlIGludGVyYWN0aXZlIG1hcHMgYmFzZWQgb24gc3BhdGlhbCBkYXRhIHlvdSBoYXZlIGluIFIuCgpUaGUgZnVuY3Rpb24gYGxlYWZsZXQoKWAgcmV0dXJucyBhIExlYWZsZXQgbWFwIHdpZGdldCBvYmplY3QgdGhhdCBjYW4gcmVjZWl2ZSB2YXJpb3VzIG1hcCBsYXllcnMsIHN1Y2ggYXMgYmFzZSBtYXBzIGFuZCBwb2ludCwgbGluZSwgYW5kIHBvbHlnb24gZmVhdHVyZXMuIChBIGxpc3Qgb2YgZnJlZSBiYXNlIG1hcCBwcm92aWRlcnMgZm9yIExlYWZsZXQgaXMgW2hlcmVdKGh0dHA6Ly9sZWFmbGV0LWV4dHJhcy5naXRodWIuaW8vbGVhZmxldC1wcm92aWRlcnMvcHJldmlldy9pbmRleC5odG1sKS4pIExlYWZsZXQgY2FuIG1hcCBzcGF0aWFsIGRhdGEgZnJvbSBzZXZlcmFsIHNvdXJjZXMgaW4gUiwgaW5jbHVkaW5nIGRhdGEgZnJhbWVzIGNvbnRhaW5pbmcgbGF0aXR1ZGVzIGFuZCBsb25naXR1ZGVzIGluIGFkZGl0aW9uIHRvIG1hcHMgZnJvbSB0aGUgYG1hcHNgIHBhY2thZ2UuICBBbiBhZGRpdGlvbmFsIHBhY2thZ2UgY2FsbGVkIGBodG1sd2lkZ2V0c2AgaXMgdXNlZCB0byBzYXZlIHRoZSBMZWFmbGV0IHdpZGdldCB0byBhbiBIVE1MIHBhZ2UuIFRoZSBgbWFwc2hvdGAgZnVuY3Rpb24gb2YgdGhlIGBtYXB2aWV3YCBwYWNrYWdlIGNhbiBzYXZlIGEgTGVhZmxldCBpbnRlcmFjdGl2ZSBtYXAgYXMgYSBzdGF0aWMgaW1hZ2UgZmlsZSwgZm9yIGluY2x1c2lvbiBpbiBkb2N1bWVudHMsIHByZXNlbnRhdGlvbnMsIGV0Yy4gVGhlcmUgaXMgYW4gb3B0aW9uYWwgbGluZSBvZiBjb2RlIGF0IHRoZSBlbmQgb2YgdGhpcyBjaHVuayB0byBwZXJmb3JtIHRoaXMgZnVuY3Rpb24uCgoKYGBge3Igc2l0ZXNtYXAyLlIsIGVjaG89VFJVRX0KIyBzaXRlc21hcDIuUgoKIyBNYXAgdGhlIHNpdGVzIGRhdGEgdXNpbmcgdGhlIGxlYWZsZXQgcGFja2FnZS4KIyBUaGlzIGNyZWF0ZXMgYSBwcmV0dGllciwgaW50ZXJhY3RpdmUgbWFwLgoKbGlicmFyeShsZWFmbGV0KQpsaWJyYXJ5KG1hcHMpCmxpYnJhcnkoaHRtbHdpZGdldHMpICMgVG8gc2F2ZSB0aGUgbWFwIGFzIGEgd2ViIHBhZ2UuCgojIFRoZSBkYXRhIHRvIG1hcC4Kc2l0ZXMgPC0gcmVhZC5jc3YoImh0dHA6Ly9jb2xsZWdlLmhvbHljcm9zcy5lZHUvZmFjdWx0eS9ybGVudC9zaXRlcy9zaXRlcy5jc3YiKQoKIyBTdGF0ZSBib3VuZGFyaWVzIGZyb20gdGhlIG1hcHMgcGFja2FnZS4gVGhlIGZpbGwgb3B0aW9uIG11c3QgYmUgVFJVRS4KYm91bmRzIDwtIG1hcCgnc3RhdGUnLCBjKCdNYXNzYWNodXNldHRzJywgJ1Zlcm1vbnQnLCAnTmV3IEhhbXBzaGlyZScpLCBmaWxsPVRSVUUsIHBsb3Q9RkFMU0UpCgojIEEgY3VzdG9tIGljb24uCmljb25zIDwtIGF3ZXNvbWVJY29ucygKICAgIGljb24gPSAnZGlzYycsCiAgICBpY29uQ29sb3IgPSAnYmxhY2snLAogICAgbGlicmFyeSA9ICdpb24nLCAjIE9wdGlvbnMgYXJlICdnbHlwaGljb24nLCAnZmEnLCAnaW9uJy4KICAgIG1hcmtlckNvbG9yID0gJ2JsdWUnLAogICAgc3F1YXJlTWFya2VyID0gVFJVRQopCgojIENyZWF0ZSB0aGUgTGVhZmxldCBtYXAgd2lkZ2V0IGFuZCBhZGQgc29tZSBtYXAgbGF5ZXJzLgojIFdlIHVzZSB0aGUgcGlwZSBvcGVyYXRvciAlPiUgdG8gc3RyZWFtbGluZSB0aGUgYWRkaW5nIG9mCiMgbGF5ZXJzIHRvIHRoZSBsZWFmbGV0IG9iamVjdC4gVGhlIHBpcGUgb3BlcmF0b3IgY29tZXMgZnJvbSAKIyB0aGUgbWFncml0dHIgcGFja2FnZSB2aWEgdGhlIGRwbHlyIHBhY2thZ2UuCm1hcCA8LSBsZWFmbGV0KGRhdGEgPSBzaXRlcykgJT4lCiAgICAjIHNldFZpZXcoLTcyLjE0NjAwLCA0My44Mjk3Nywgem9vbSA9IDgpICU+JSAKICAgIGFkZFByb3ZpZGVyVGlsZXMoIkNhcnRvREIuUG9zaXRyb24iLCBncm91cCA9ICJNYXAiKSAlPiUKICAgIGFkZFByb3ZpZGVyVGlsZXMoIkVzcmkuV29ybGRJbWFnZXJ5IiwgZ3JvdXAgPSAiU2F0ZWxsaXRlIikgJT4lIAogICAgYWRkUHJvdmlkZXJUaWxlcygiRXNyaS5Xb3JsZFNoYWRlZFJlbGllZiIsIGdyb3VwID0gIlJlbGllZiIpICU+JQogICAgIyBNYXJrZXIgZGF0YSBhcmUgZnJvbSB0aGUgc2l0ZXMgZGF0YSBmcmFtZS4gV2UgbmVlZCB0aGUgfiBzeW1ib2xzCiAgICAjIHRvIGluZGljYXRlIHRoZSBjb2x1bW5zIG9mIHRoZSBkYXRhIGZyYW1lLgogICAgYWRkTWFya2Vycyh+bG9uX2RkLCB+bGF0X2RkLCBsYWJlbCA9IH5sb2NhbGl0eSwgZ3JvdXAgPSAiU2l0ZXMiKSAlPiUgCiAgICAjIGFkZEF3ZXNvbWVNYXJrZXJzKH5sb25fZGQsIH5sYXRfZGQsIGxhYmVsID0gfmxvY2FsaXR5LCBncm91cCA9ICJTaXRlcyIsIGljb249aWNvbnMpICU+JQogICAgYWRkUG9seWdvbnMoZGF0YT1ib3VuZHMsIGdyb3VwPSJTdGF0ZXMiLCB3ZWlnaHQ9MiwgZmlsbE9wYWNpdHkgPSAwKSAlPiUKICAgIGFkZFNjYWxlQmFyKHBvc2l0aW9uID0gImJvdHRvbWxlZnQiKSAlPiUKICAgIGFkZExheWVyc0NvbnRyb2woCiAgICAgICAgYmFzZUdyb3VwcyA9IGMoIk1hcCIsICJTYXRlbGxpdGUiLCAiUmVsaWVmIiksCiAgICAgICAgb3ZlcmxheUdyb3VwcyA9IGMoIlNpdGVzIiwgIlN0YXRlcyIpLAogICAgICAgIG9wdGlvbnMgPSBsYXllcnNDb250cm9sT3B0aW9ucyhjb2xsYXBzZWQgPSBGQUxTRSkKICAgICkKCmludmlzaWJsZShwcmludChtYXApKQojIFNhdmUgdGhlIGludGVyYWN0aXZlIG1hcCB0byBhbiBIVE1MIHBhZ2UuCnNhdmVXaWRnZXQobWFwLCBmaWxlPSJzaXRlc21hcDIuaHRtbCIsIHNlbGZjb250YWluZWQ9VFJVRSkKIyBVbmNvbW1lbnQgdGhlIGZvbGxvd2luZyBsaW5lIHRvIHNhdmUgdGhlIGludGVyYWN0aXZlIG1hcCB0byBhIHN0YXRpYyBpbWFnZSBmaWxlLgojIG1hcHNob3QobWFwLCBmaWxlPSJzaXRlc21hcDIucG5nIikKYGBgCgojIyMgTWFwIDM6IFRoZSBgdG1hcGAgcGFja2FnZQoKVGhlIFt0bWFwIHBhY2thZ2VdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy90bWFwL2luZGV4Lmh0bWwpICJvZmZlcnMgYSBmbGV4aWJsZSwgbGF5ZXItYmFzZWQsIGFuZCBlYXN5IHRvIHVzZSBhcHByb2FjaCB0byBjcmVhdGUgdGhlbWF0aWMgbWFwcywgc3VjaCBhcyBjaG9yb3BsZXRocyBhbmQgYnViYmxlIG1hcHMuIiBTZWUgW3RtYXAgaW4gYSBudXRzaGVsbF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3RtYXAvdmlnbmV0dGVzL3RtYXAtbnV0c2hlbGwuaHRtbCkgZm9yIGZ1cnRoZXIgZGV0YWlscy4gSGVyZSB3ZSBwcmVzZW50IGEgc2ltcGxlIGV4YW1wbGUgb2YgdXNpbmcgYHRtYXBgIHRvIGNyZWF0ZSBhIHRoZW1hdGljIG1hcCBmcm9tIGFuIFtFU1JJIHNoYXBlZmlsZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU2hhcGVmaWxlKS4gVGhlIHNoYXBlZmlsZSBpcyBhIGNvbW1vbiBHSVMgW3ZlY3RvciBkYXRhIGZvcm1hdF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvR0lTX2ZpbGVfZm9ybWF0cykgdGhhdCBoYXMgYmVlbiBhcm91bmQgZm9yIGEgbG9uZyB0aW1lLCBhbmQgaXMgdXNlZCB0byByZXByZXNlbnQgcG9pbnRzLCBsaW5lcywgYW5kIGVuY2xvc2VkIGFyZWFzIChwb2x5Z29ucykuIEEgc2hhcGVmaWxlIGlzIGFjdHVhbGx5IGNvbXBvc2VkIG9mIHNldmVyYWwgZmlsZXMgdGhhdCBmdW5jdGlvbiB0b2dldGhlciB0byBwcm92aWRlIHRoZSBnZW9ncmFwaGljIGZlYXR1cmVzIChwb2ludHMsIGxpbmVzLCBvciBwb2x5Z29ucykgYWxvbmcgd2l0aCBhIGRhdGEgdGFibGUgY29udGFpbmluZyBhdHRyaWJ1dGVzIG9mIGVhY2ggZmVhdHVyZS4gCgpUaGVyZSBhcmUgbWFueSBvbmxpbmUgcmVwb3NpdG9yaWVzIG9mIEdJUyBkYXRhIHRoYXQgcHJvdmlkZSBkb3dubG9hZGFibGUgbWFwIGxheWVycyBpbiBzaGFwZWZpbGUgZm9ybWF0LiBIZXJlIHdlIHVzZSBhIHNoYXBlZmlsZSBmcm9tIHRoZSBNYXNzYWNodXNldHRzIE9mZmljZSBvZiBHZW9ncmFwaGljIEluZm9ybWF0aW9uIChbTWFzc0dJU10oaHR0cDovL3d3dy5tYXNzLmdvdi9hbmYvcmVzZWFyY2gtYW5kLXRlY2gvaXQtc2Vydi1hbmQtc3VwcG9ydC9hcHBsaWNhdGlvbi1zZXJ2L29mZmljZS1vZi1nZW9ncmFwaGljLWluZm9ybWF0aW9uLW1hc3NnaXMvKSkgdGhhdCBjb250YWlucyBwb2x5Z29ucyBkZWxpbmVhdGluZyBbYXJlYXMgb2YgY3JpdGljYWwgZW52aXJvbWVudGFsIGNvbmNlcm5dKGh0dHA6Ly93d3cubWFzcy5nb3YvYW5mL3Jlc2VhcmNoLWFuZC10ZWNoL2l0LXNlcnYtYW5kLXN1cHBvcnQvYXBwbGljYXRpb24tc2Vydi9vZmZpY2Utb2YtZ2VvZ3JhcGhpYy1pbmZvcm1hdGlvbi1tYXNzZ2lzL2RhdGFsYXllcnMvYWNlY3MuaHRtbCkuIFRoZSBSIHBhY2thZ2UgYHJnZGFsYCBwcm92aWRlcyBmdW5jdGlvbiBgcmVhZE9HUmAgdG8gcmVhZCBhIHNoYXBlZmlsZSBpbnRvIGEgW1NwYXRpYWxQb2x5Z29uc0RhdGFGcmFtZV0oaHR0cDovL3d3dy5kcGkuaW5wZS5ici9naWxiZXJ0by90dXRvcmlhbHMvc29mdHdhcmUvUi1jb250cmliL3NwL2h0bWwvU3BhdGlhbFBvbHlnb25zRGF0YUZyYW1lLWNsYXNzLmh0bWwpLCB3aGljaCBpcyBhbiBSIGRhdGEgZnJhbWUgdGhhdCBpcyBhYmxlIHRvIGNvbnRhaW4gc3BhdGlhbCBkYXRhLiBXZSB0aGVuIHVzZSB0aGUgYHF0bWAgKCJxdWljayB0aGVtYXRpYyBtYXAiKSBmdW5jdGlvbiBvZiB0aGUgYHRtYXBgIHBhY2thZ2UgdG8gcHJvZHVjZSB0aGUgbWFwLiBUaGUgcG9seWdvbnMgYXJlIGNvbG9yLWNvZGVkIGFjY29yZGluZyB0byB0aGVpciBuYW1lcywgd2hpY2ggYXJlIGNvbnRhaW5lZCBpbiB0aGUgZmllbGQgTkFNRSBmcm9tIHRoZSBhdHRyaWJ1dGUgdGFibGUgb2YgdGhlIG9yaWdpbmFsIHNoYXBlZmlsZSBkb3dubG9hZGVkIGZyb20gTWFzc0dJUy4gVGhlIGNvZGUgYWxzbyB1c2VzIHR3byBmdW5jdGlvbnMgZnJvbSB0aGUgUiBbdXRpbHNdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9SLnV0aWxzL2luZGV4Lmh0bWwpIHBhY2thZ2UuIFRoZSBgZG93bmxvYWQuZmlsZWAgZnVuY3Rpb24gZG93bmxvYWRzIHRoZSBzaGFwZWZpbGUgZnJvbSBNYXNzR0lTLCB1c2luZyBhIFtVUkxdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1VSTCkgcGFzc2VkIHRvIGl0LiBUaGUgbXVsdGlwbGUgZmlsZXMgY29tcHJpc2luZyB0aGUgc2hhcGVmaWxlIG1hcCBsYXllciBhcmUgcHJvdmlkZWQgYnkgTWFzc0dJUyBhcyBhIGNvbXByZXNzZWQgW3ppcCBmaWxlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9aaXBfKGZpbGVfZm9ybWF0KSkuIFdlIHRoZW4gdXNlIHRoZSBgdW56aXBgIGZ1bmN0aW9uIHRvIGV4dHJhY3QgdGhlIHNoYXBlZmlsZSBjb21wb25lbnRzIHRvIG91ciB3b3JraW5nIGRpcmVjdG9yeS4gVGhlIHNoYXBlZmlsZSBsYXllciwgbmFtZWQgYGFjZWNzX3BvbHlgLCBpcyB0aGVuIHBhc3NlZCB0byB0aGUgYHJlYWRPR1JgIGZ1bmN0aW9uIHNvIHRoYXQgaXQgY2FuIGJlIGJyb3VnaHQgaW50byBSLgoKYHRtYXBgIGNhbiBmdW5jdGlvbiBpbiB0d28gbW9kZXM6IHBsb3QgbW9kZSBhbmQgdmlldyBtb2RlLiBQbG90IG1vZGUgY3JlYXRlcyBhIG5vbi1pbnRlcmFjdGl2ZSBtYXAgdXNpbmcgUiBncmFwaGljYWwgcGxvdHRpbmcsIHdoZXJlYXMgdmlldyBtb2RlIGNyZWF0ZXMgYW4gaW50ZXJhY3RpdmUgW0xlYWZsZXRdKGh0dHBzOi8vcnN0dWRpby5naXRodWIuaW8vbGVhZmxldC8pIG1hcC4gVGhlIGB0bWFwX21vZGVgIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHRvIHN3aXRjaCBiZXR3ZWVuIHBsb3QgbW9kZSBhbmQgdmlldyBtb2RlLgoKYGBge3Igc2hhcGUuUiwgZWNobz1UUlVFfQojIHNoYXBlLlIKCiMgVXNlIHRoZSB0bWFwIHBhY2thZ2UgdG8gbWFrZSBhIHRoZW1hdGljIG1hcCBmcm9tIGFuIEVTUkkgc2hhcGVmaWxlLgojIFRoZSBzaGFwZWZpbGUgaXMgZnJvbSBNYXNzR0lTLCBBcmVhcyBvZiBDcml0aWNhbCBFbnZpcm9ubWVudGFsIENvbmNlcm4uIAojIFNlZSBodHRwczovL2dvby5nbC92UXhha08uCgpsaWJyYXJ5KHJnZGFsKQpsaWJyYXJ5KHRtYXApCgojIERvd25sb2FkIGFuZCBleHRyYWN0IHRoZSBkYXRhLgojIFRoZSBwb2x5Z29uIHNoYXBlZmlsZSB3aWxsIGJlIGV4dHJhY3RlZCB0byB0aGUgd29ya2luZyBkaXJlY3RvcnkgKHVzZSBnZXR3ZCgpKS4KdXJsIDwtICJodHRwOi8vd3Nndy5tYXNzLmdvdi9kYXRhL2dpc3B1Yi9zaGFwZS9zdGF0ZS9hY2Vjcy56aXAiCmRvd25sb2FkLmZpbGUodXJsLCBkZXN0ZmlsZSA9ICJhY2Vjcy56aXAiLCBxdWlldCA9IFRSVUUpCnVuemlwKCJhY2Vjcy56aXAiKQoKc3VwcHJlc3NNZXNzYWdlcyh0bWFwX21vZGUoInZpZXciKSkgIyBPcHRpb25zIGFyZSAicGxvdCIsICJ2aWV3Ii4KCm1hcCA8LSByZWFkT0dSKCIuLyIsImFjZWNzX3BvbHkiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsIHZlcmJvc2UgPSBGQUxTRSkKaW52aXNpYmxlKHByaW50KHF0bShtYXAsICJOQU1FIiwgZmlsbC50aXRsZT0iTmFtZSIsIGZvcm1hdCA9ICJFdXJvcGVfd2lkZSIpKSkKYGBgCgojIyMgTWFwIDQ6IE1hcHBpbmcgVS4gUy4gQ2Vuc3VzIERhdGEgd2l0aCB0aGUgYGFjc2AsIGB0aWdyaXNgLCBhbmQgYGxlYWZsZXRgIHBhY2thZ2VzCgpPdXIgZmluYWwgZXhhbXBsZSBpcyBhIGJpdCBtb3JlIGNvbXBsaWNhdGVkLiBXZSB1c2UgdGhlIGBsZWFmbGV0YCBwYWNrYWdlIHRvIG1hcCBzb21lIGRhdGEgZG93bmxvYWRlZCBmcm9tIHRoZSBVLiBTLiBDZW5zdXMgQnVyZWF1LCB1c2luZyB0aGUgW2Fjc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2Fjcy9pbmRleC5odG1sKSBhbmQgW3RpZ3Jpc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3RpZ3Jpcy9pbmRleC5odG1sKSBwYWNrYWdlcy4gVGhlIGBhY3NgIHBhY2thZ2UgZG93bmxvYWRzIHRhYnVsYXIgY2Vuc3VzIGRhdGEsIHdoaWxlIHRoZSBgdGlncmlzYCBwYWNrYWdlIGlzIHVzZWQgdG8gbG9hZCBDZW5zdXMgW1RJR0VSXShodHRwczovL3d3dy5jZW5zdXMuZ292L2dlby9tYXBzLWRhdGEvZGF0YS90aWdlci5odG1sKSBzaGFwZWZpbGVzIGludG8gUi4gYFRJR0VSYCBzdGFuZHMgZm9yICoqVCoqb3BvbG9naWNhbGx5ICoqSSoqbnRlZ3JhdGVkICoqRyoqZW9ncmFwaGljICoqRSoqbmNvZGluZyBhbmQgKipSKiplZmVyZW5jaW5nLCBhbmQgVElHRVIgc2hhcGVmaWxlcyBjb250YWluIGZlYXR1cmVzIHN1Y2ggYXMgcm9hZHMsIHJhaWxyb2Fkcywgcml2ZXJzLCBhcyB3ZWxsIGFzIGxlZ2FsIGFuZCBzdGF0aXN0aWNhbCBnZW9ncmFwaGljIGFyZWFzIHVzZWQgYnkgdGhlIFUuIFMuIENlbnN1cy4KClRoaXMgZXhhbXBsZSB3YXMgY29tcGlsZWQgYW5kIG1vZGlmaWVkIGZyb20gW01hbmlwdWxhdGluZyBhbmQgbWFwcGluZyBVUyBDZW5zdXMgZGF0YSBpbiBSIHVzaW5nIHRoZSBhY3MsIHRpZ3JpcyBhbmQgbGVhZmxldCBwYWNrYWdlc10oaHR0cDovL3pldnJvc3MuY29tL2Jsb2cvMjAxNS8xMC8xNC9tYW5pcHVsYXRpbmctYW5kLW1hcHBpbmctdXMtY2Vuc3VzLWRhdGEtaW4tci11c2luZy10aGUtYWNzLXRpZ3Jpcy1hbmQtbGVhZmxldC1wYWNrYWdlcy0zLyksIHVzaW5nIFt0aGUgZWFzeShlcikgd2F5XShodHRwOi8vemV2cm9zcy5jb20vYmxvZy8yMDE1LzEwLzE0L21hbmlwdWxhdGluZy1hbmQtbWFwcGluZy11cy1jZW5zdXMtZGF0YS1pbi1yLXVzaW5nLXRoZS1hY3MtdGlncmlzLWFuZC1sZWFmbGV0LXBhY2thZ2VzLTMvI2NlbnN1cy1kYXRhLXRoZS1lYXN5ZXItd2F5KSBkZXNjcmliZWQgaW4gdGhhdCBkb2N1bWVudC4gQmFzaWNhbGx5LCB0aGUgY2Vuc3VzIHNoYXBmaWxlcyBhcmUgZG93bmxvYWRlZCB1c2luZyBgdGlncmlzYCBhbmQgcmVhZCBpbnRvIFIgc3BhdGlhbCBkYXRhIGZyYW1lcy4gVGhlIHRhYmx1bGFyIGNlbnN1cyBkYXRhIGFyZSBkb3dubG9hZGVkIHVzaW5nIGBhY3NgIGFuZCB0aGUgbWVyZ2VkIGludG8gdGhlIHNoYXBlZmlsZSdzIGF0dHJpYnV0ZSB0YWJsZS4gRnJvbSB0aGVyZSwgdGhlIG1lcmdlZCBkYXRhIGNhbiB0aGVuIGJlIGdpdmVuIHRvIHRoZSBgbGVhZmxldGAgZnVuY3Rpb24gdG8gcHJvZHVjZSB0aGUgaW50ZXJhY3RpdmUgbWFwLgoKV2hpbGUgYXBwcmVjaWFibHkgbW9yZSBjb21wbGV4IHRoYW4gb3VyIHByZXZpb3VzIGV4YW1wbGVzLCB0aGlzIG1hcCBzZXJ2ZXMgdG8gaWxsdXN0cmF0ZSB0aGUgZGVncmVlIG9mIGRhdGEgYWNjZXNzLCBwcm9jZXNzaW5nLCBhbmQgc3BhdGlhbCBtYXBwaW5nIHRoYXQgY2FuIGJlIGFjY29tcGxpc2hlZCB3aXRoIFIgYW5kIFJTdHVkaW8uCgpgYGB7ciBjZW5zdXMuUiwgZWNobz1UUlVFfQojIGNlbnN1cy5SCgojIE1hcHBpbmcgVS4gUy4gQ2Vuc3VzIERhdGEgd2l0aCBSIHVzaW5nIHRoZSBhY3MsIHRpZ3JpcyBhbmQgbGVhZmxldCBwYWNrYWdlcy4KIyBSIGNvZGUgY29tcGlsZWQgYW5kIG1vZGlmaWVkIGZyb20gaHR0cHM6Ly9nb28uZ2wvVHZMQ1JvCgojIExvYWQgcmVxdWlyZWQgUiBwYWNrYWdlcy4KbGlicmFyeSh0aWdyaXMpICAgICAgIyBVLiBTLiBDZW5zdXMgbWFwcy4KbGlicmFyeShhY3MpICAgICAgICAgIyBVLiBTLiBDZW5zdXMgZGF0YS4KbGlicmFyeShzdHJpbmdyKSAgICAgIyBUbyBwYWQgZmlwcyBjb2Rlcy4KbGlicmFyeShyZ2RhbCkgICAgICAgIyBGb3IgcmVhZE9HUiBhbmQgb3RoZXJzLgpsaWJyYXJ5KHNwKSAgICAgICAgICAjIEZvciBzcGF0aWFsIG9iamVjdHMuCmxpYnJhcnkobGVhZmxldCkgICAgICMgRm9yIGludGVyYWN0aXZlIG1hcHMuCmxpYnJhcnkoZHBseXIpICAgICAgICMgRm9yIHdvcmtpbmcgd2l0aCBkYXRhIGZyYW1lcy4KbGlicmFyeShodG1sd2lkZ2V0cykgIyBGb3Igc2F2aW5nIHRoZSBtYXAuCgojIERvd25sb2FkIGFuZCBzdG9yZSBYTUwgdGFibGVzIHVzZWQgdG8gbG9vayB1cCB2YXJpYWJsZSBjb2RlcywgdGFibGUgbmFtZXMsIAojIGFuZCBvdGhlciBtZXRhZGF0YSBhc3NvY2lhdGVkIHdpdGggdGhlIGFjcyBwYWNrYWdlLiAoTWF5IG5vdCBiZSBuZWVkZWQuKQojIGFjcy50YWJsZXMuaW5zdGFsbCgpIAoKIyBEb3dubG9hZCB0aGUgc3BhdGlhbCBkYXRhICh0aWdyaXMpLgojIFVzaW5nIEZlZGVyYWwgSW5mb3JtYXRpb24gUHJvY2Vzc2luZyBTdGFuZGFyZHMgKEZJUFMpIGNvZGVzIGZvciBjb3VudGllcy4KIyBZb3UgY2FuIHZpZXcgdGhpcyBsYXllciBhdCB0aGUgUiBjb25zb2xlIHdpdGggcGxvdCh0cmFjdHMpLiAKY291bnRpZXMgPC0gYyg1LCA0NywgNjEsIDgxLCA4NSkKc3VwcHJlc3NNZXNzYWdlcyh0cmFjdHMgPC0gdHJhY3RzKHN0YXRlID0gJ05ZJywgY291bnR5PWMoNSwgNDcsIDYxLCA4MSwgODUpLCBjYj1UUlVFKSkKCiMgV2UgbmVlZCBhbiBBUEkga2V5IChBcHBsaWNhdGlvbiBQcm9ncmFtIEludGVyZmFjZSkgdG8gYWNjZXNzIFUuIFMuIENlbnN1cyBkYXRhLgojIFlvdSBjYW4gZ2V0IHlvdXIgb3duIENlbnN1cyBkYXRhIEFQSSBrZXkgZnJvbSAKIyBodHRwOi8vYXBpLmNlbnN1cy5nb3YvZGF0YS9rZXlfc2lnbnVwLmh0bWwuCmFwaS5rZXkuaW5zdGFsbChrZXk9IjQ3NDIwNWE4YjgzYmMzNWU2OTI4MmZlNjBmNWVhMDUxNzBhMDYxNjEiKQoKIyBDcmVhdGUgYSBnZW9ncmFwaGljIHNldCB0byBncmFiIHRhYnVsYXIgZGF0YSAoYWNzKS4KZ2VvIDwtIGdlby5tYWtlKHN0YXRlPWMoIk5ZIiksCiAgICAgICAgICAgICAgY291bnR5PWMoNSwgNDcsIDYxLCA4MSwgODUpLCB0cmFjdD0iKiIpCgojIFJldHJpZXZlIGRhdGEgZm9yIHRoZSBmaXZlIHllYXIgc3BhbiBlbmRpbmcgaW4gMjAxMi4KIyBZb3UgY2FuIHZpZXcgdGhpcyBkYXRhIHRhYmxlIGJ5IHR5cGluZyAnaW5jb21lJyBhdCB0aGUgUiBjb25zb2xlLgojIFVzZSBvZiBjb2wubmFtZXMgPSAicHJldHR5IiBnaXZlcyB0aGUgZnVsbCBjb2x1bW4gbmFtZXMuCiMgSWYgaW5zdGVhZCB5b3Ugd2FudCBDZW5zdXMgdmFyaWFibGUgSURzIHVzZSBjb2wubmFtZXM9ImF1dG8iLiAKaW5jb21lIDwtIGFjcy5mZXRjaChlbmR5ZWFyID0gMjAxMiwgc3BhbiA9IDUsIGdlb2dyYXBoeSA9IGdlbywKICAgICAgICAgICAgICAgICAgdGFibGUubnVtYmVyID0gIkIxOTAwMSIsIGNvbC5uYW1lcyA9ICJwcmV0dHkiKQoKIyBDb252ZXJ0IHRvIGEgZGF0YS5mcmFtZSBmb3IgbWVyZ2luZwppbmNvbWVfZGYgPC0gZGF0YS5mcmFtZShwYXN0ZTAoc3RyX3BhZChpbmNvbWVAZ2VvZ3JhcGh5JHN0YXRlLCAyLCAibGVmdCIsIHBhZD0iMCIpLCAKICAgICAgICAgIHN0cl9wYWQoaW5jb21lQGdlb2dyYXBoeSRjb3VudHksIDMsICJsZWZ0IiwgcGFkPSIwIiksIAogICAgICAgICAgc3RyX3BhZChpbmNvbWVAZ2VvZ3JhcGh5JHRyYWN0LCA2LCAibGVmdCIsIHBhZD0iMCIpKSwgCiAgICAgICAgICBpbmNvbWVAZXN0aW1hdGVbLGMoIkhvdXNlaG9sZCBJbmNvbWU6IFRvdGFsOiIsCiAgICAgICAgICAiSG91c2Vob2xkIEluY29tZTogJDIwMCwwMDAgb3IgbW9yZSIpXSwgCiAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgppbmNvbWVfZGYgPC0gc2VsZWN0KGluY29tZV9kZiwgMTozKQpyb3duYW1lcyhpbmNvbWVfZGYpIDwtIDE6bnJvdyhpbmNvbWVfZGYpCm5hbWVzKGluY29tZV9kZikgPC0gYygiR0VPSUQiLCAidG90YWwiLCAib3Zlcl8yMDAiKQppbmNvbWVfZGYkcGVyY2VudCA8LSAxMDAqKGluY29tZV9kZiRvdmVyXzIwMC9pbmNvbWVfZGYkdG90YWwpCgppbmNvbWVfbWVyZ2VkIDwtIGdlb19qb2luKHRyYWN0cywgaW5jb21lX2RmLCAiR0VPSUQiLCAiR0VPSUQiKQojIFRoZXJlIGFyZSBzb21lIHRyYWN0cyB3aXRoIG5vIGxhbmQgdGhhdCB3ZSBzaG91bGQgZXhjbHVkZQppbmNvbWVfbWVyZ2VkIDwtIGluY29tZV9tZXJnZWRbaW5jb21lX21lcmdlZCRBTEFORD4wLF0KCnBvcHVwIDwtIHBhc3RlMCgiR0VPSUQ6ICIsIGluY29tZV9tZXJnZWQkR0VPSUQsICI8YnI+IiwgIlBlcmNlbnQgb2YgSG91c2Vob2xkcyBhYm92ZSAkMjAwazogIiwgcm91bmQoaW5jb21lX21lcmdlZCRwZXJjZW50LDIpKQpwYWwgPC0gY29sb3JOdW1lcmljKAogICAgcGFsZXR0ZSA9ICJZbEduQnUiLAogICAgZG9tYWluID0gaW5jb21lX21lcmdlZCRwZXJjZW50CikKY2Vuc3VzbWFwIDwtIGxlYWZsZXQoKSAlPiUKICAgIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJEVzcmkuV29ybGRUb3BvTWFwKSAlPiUKICAgIGFkZFBvbHlnb25zKGRhdGEgPSBpbmNvbWVfbWVyZ2VkLCAKICAgICAgICAgICAgICAgIGZpbGxDb2xvciA9IH5wYWwocGVyY2VudCksIAogICAgICAgICAgICAgICAgY29sb3IgPSAiI2IyYWVhZSIsICMgTmVlZCB0byB1c2UgaGV4IGNvbG9yIGNvZGVzLgogICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjcsIAogICAgICAgICAgICAgICAgd2VpZ2h0ID0gMSwgCiAgICAgICAgICAgICAgICBzbW9vdGhGYWN0b3IgPSAwLjIsCiAgICAgICAgICAgICAgICBwb3B1cCA9IHBvcHVwKSAlPiUKICAgIGFkZExlZ2VuZChwYWwgPSBwYWwsCiAgICAgICAgICAgICAgdmFsdWVzID0gaW5jb21lX21lcmdlZCRwZXJjZW50LCAKICAgICAgICAgICAgICBwb3NpdGlvbiA9ICJib3R0b21yaWdodCIsIAogICAgICAgICAgICAgIHRpdGxlID0gIlBlcmNlbnQgb2YgSG91c2Vob2xkczxicj5hYm92ZSAkMjAwayIsCiAgICAgICAgICAgICAgbGFiRm9ybWF0ID0gbGFiZWxGb3JtYXQoc3VmZml4ID0gIiUiKSkgJT4lCiAgICBhZGRTY2FsZUJhcihwb3NpdGlvbiA9ICJ0b3ByaWdodCIpCgojIERpc3BsYXkgYW5kIHNhdmUgdGhlIG1hcC4KaW52aXNpYmxlKHByaW50KGNlbnN1c21hcCkpCnNhdmVXaWRnZXQoY2Vuc3VzbWFwLCBmaWxlPSJjZW5zdXNtYXAuaHRtbCIsIHNlbGZjb250YWluZWQ9VFJVRSkKYGBgCgoKCgoKCg==