2022-02-14

Introduction

This notebook provides brief guidance on a number R packages that allow accessing economic databases directly through R. These packages take advantage of the increasing number of data providers making their databases accessible through APIs and the availability of R packages for making web requests (for instance, httr or jsonlite). This note covers the public sector data providers (e.g., international organizations, central banks or similar) primarily and leaves out the commercial ones (e.g., Bloomberg, Datastream).

db.nomics

db.nomics describes itself as the “world’s economic database” and it may deserve this description. At the time of writing, db.nomics aggregated datasets from more than 80 sources and provided access to them through a single API. Effectively, by using db.nomics you can ignore many of the R packages for other sources that I cover below, given that it already provides a unified interface to many of these datasets.

The team behind db.nomics provides an R package rdbnomics for accessing their database. I will show below how to get started with this package, while further details can be found on this page or in the vignette that you can access by typing vignette("rdbnomics") in the R console. The package is available through CRAN.

You can quickly check the list of available providers and retrieve the list of datasets (I recommend doing the latter by specifying the provider in the function call):

# load the package
library("rdbnomics")

# retrieve the list of providers (including the provider code)
providers <- rdb_providers()

# retrieve the list of datasets
datasets <- rdb_datasets(provider_code = "IMF", 
                         simplify = TRUE)

# retrive the list of data series (can take some time)
series <- rdb_series(provider_code = "IMF", 
                     dataset_code = "WEO:2021-04", 
                     simplify = TRUE, 
                     verbose = FALSE)

head(series)

rdb is the main function for sending data requests. You can download the series using only the ids argument (referring to an ID of a series). To do this, you need to combine the provider, dataset, and series codes into a single string as follows "provider_code/dataset_code/series_code". Alternatively, these parameters can be fed into the function separately using their own arguments. The following code shows two alternative ways of downloading the same data series using rdb.

# download the constant price GDP per capita series for Uzbekistan using ids or mask arguments

# by using the ids argument
df1 <- rdb(ids = "IMF/WEO:2021-04/UZB.NGDPRPC.national_currency")

# by using separate arguments
df2 <- rdb(provider_code = "IMF", 
           dataset_code = "WEO:2021-04", 
           mask = "UZB.NGDPRPC.national_currency")

# plot
ggplot(df1, aes(x = period, y = value, group = 1)) + 
    geom_line()

You can also use the dimensions argument of rdb to specify one or several dimensions (i.e., variable names) from the dataset (you will need to look up how exactly the variables are named to use this option).

# download the constant price GDP per capita series using the dimensions argument
df <- rdb(provider_code = "IMF", 
          dataset_code = "WEO:2021-04", 
          dimensions = list("weo-country" = "KAZ", 
                            "weo-subject" = "NGDPRPC", 
                            "unit" = "national_currency"))

Another option is to use the query argument to search for a series within a given dataset.

# download the data using a search term
df <- rdb(provider_code = "IMF", 
          dataset_code = "WEO:2021-04", 
          query = "Kazakhstan gross domestic product per capita constant prices national currency")
head(df)

Arguments of rdb accept vectors of strings allowing downloading multiple data series at once. The mask argument also has a convenient notation for combining the data request parameters.

# by using the ids argument and a vector of strings
df <- rdb(ids = c("IMF/WEO:2021-04/KAZ.PCPIPCH.pcent_change",
                  "IMF/WEO:2021-04/UZB.PCPIPCH.pcent_change"))

# by using the mask argument
    # download the series for two countries only
    df <- rdb(provider_code = "IMF", 
              dataset_code = "WEO:2021-04", 
              mask = "KAZ+UZB.PCPIPCH.pcent_change")
    
    # download the series for all countries
    df <- rdb(provider_code = "IMF", 
              dataset_code = "WEO:2021-04", 
              mask = ".PCPIPCH.pcent_change")
    
    # download all the data from the dataset for a given country
    df <- rdb(provider_code = "IMF", 
              dataset_code = "WEO:2021-04", 
              mask = "KAZ.")
    
    # download all the data from the dataset for two countries
    df <- rdb(provider_code = "IMF", 
              dataset_code = "WEO:2021-04", 
              mask = "KAZ+UZB.")

IMF

imfr is a convenient package for directly accessing the datasets provided by IMF.

# load the package
library("imfr")

You can use the imf_ids function to look up the IDs of available datasets:

# look up the ids of datasets
data_ids <- imf_ids(return_raw = FALSE)
rmarkdown::paged_table(data_ids)

Then, use the imf_codelist function to look up the codes for a specific database. The main information you need from this call is the name of an object listing all indicators within the dataset.

# look up the codes for a database
codelist <- imf_codelist(database_id = "BOP")
head(codelist)

You can then use the name of that object to download the list of all indicators with the imf_codes function:

# look up the list of indicators
codes <- imf_codes(codelist = "CL_INDICATOR_BOP")
rmarkdown::paged_table(codes)

Finally, use the imf_data function to download the data using the database and indicator IDs:

# download the data from IMF
df <- imf_data(database_id = "BOP", 
               indicator = "BMGS_BP6_USD", 
               country = "KZ",              # use the two-letter ISO code
               start = 1995, 
               end = current_year(), 
               freq = "A",
               print_url = FALSE)
# plot
ggplot(df, aes(x = year, y = BMGS_BP6_USD, group = 1)) + 
    geom_line() + 
    scale_x_discrete(breaks = seq(df$year[1], df$year[nrow(df)], by = 5))

You can also feed vectors of strings either to indicator or country arguments. Use "all" in the country argument to download the data for all countries.

# downloading two data series for two countries
df <- imf_data(database_id = "BOP", 
               indicator = "BMGS_BP6_USD", 
               country = c("KZ", "RU"), 
               start = 1995, 
               end = current_year(), 
               print_url = FALSE)

head(df)

While the imfr package does allow easily accessing the IMF API, the same IMF data appears better structured and organized when accessed using the db.nomics package.

World Bank

There are several R packages available for downloading the World Bank data. I will review WDI and wbstats.

WDI

The package already contains a data object with the list of available indicators:

# load the package
library("WDI")

# list of indicators
rmarkdown::paged_table(as.data.frame(WDI_data$series))

This local list of indicators can be updated to the latest version using the WDI_cache() function. If necessary, the output of this function can then be fed into the function for downloading data:

# update the list of indicators
new_list <- WDIcache()

# check if the updated list is identical to the one in the package
identical(new_list$series, WDI_data$series) 
[1] FALSE

The main function for downloading the data is WDI:

# download data from the WDI database
df <- WDI(country = "KZ", 
          indicator = "NY.GDP.PCAP.KN", 
          start = 1991, 
          end = 2020,
          extra = FALSE, 
          cache = new_list)

# plot
ggplot(df, aes(x = year, y = NY.GDP.PCAP.KN, group = 1)) + 
    geom_line()

The packages also comes with a useful search function (the function supports regular expressions)

WDIsearch("gdp.*capita.*constant.*LCU")
                      indicator                            name 
               "NY.GDP.PCAP.KN" "GDP per capita (constant LCU)" 

wbstats

This package is quite similar to WDI. It similarly comes with a data object that contains the list of available indicators:

# load the package
library(wbstats)

# the list of indicators
wb_cachelist$indicators

This data object can similarly be updated to the latest version and further fed into the function for downloading data.

# update the list of indicators
new_list <- wb_cache()

wb_data is the function for downloading the data:

# download data from the WDI database
df <- wb_data(indicator = "NY.GDP.PCAP.KN", 
              country = "KZ", 
              start_date = 1991, 
              end_date = 2020, 
              cache = new_list, 
              return_wide = FALSE)

rmarkdown::paged_table(df)

The package also has a convenient function for searching the relevant data series (that also supports regular expressions):

wb_search("gdp.*capita.*constant LCU")

Compared to the WDI::WDI() function, wbstats::wb_data appears to provide more flexibility and convenient options. For instance, the return_wide argument becomes very handy when downloading multiple series at once:

# download two indicators for two countries and transform to wide format
df <- wb_data(indicator = c("NY.GDP.PCAP.KN", "NY.GDP.PCAP.KD.ZG"), 
              country = c("KZ", "UZ"), 
              start_date = 1991, 
              end_date = 2020, 
              cache = new_list, 
              return_wide = TRUE)

rmarkdown::paged_table(df)

In addition, mrv (“most recent value”) and mrnev (“most recent non-empty value”) arguments instructing the function to download the latest available data are also very useful:

# addtional useful arguments of wb_data
df <- wb_data(indicator = "NY.GDP.PCAP.CD", 
              # mrv = 1,                       # number of most recent observations
              # gapfill = TRUE,                # fill in missing values by carrying forward the latest available value
              mrnev = 1)                       # number of most recent non-empty observations

head(df, 10)

I found both WDI and wbstats easy to use but wbstats provides few more convenient functionalities within its main function for downloading data (e.g., for dealing with missing observations and filling the gaps).

OECD

Similar to the packages above, the OECD package has with a function for downloading and looking up the list of available data series:

# load the package
library(OECD)

# look up the list of indicators
oecd_data_list <- get_datasets()
head(oecd_data_list, 10)

You can use the ID of a dataset to look up its structure using the get_data_structure function:

data_structure <- get_data_structure(dataset = "SNA_TABLE1")

These data structure objects are the list of data frames each documenting different dimensions of the dataset (e.g., the list of available indicators, frequency, unit of measure and others). Elements of this information will be necessary for downloading the actual data. get_dataset() is the main function for downloading the data series and you will need to use its filter argument to indicate specific dimensions to customize the data request. You can run this function with no input to the filter argument and the whole dataset will be downloaded in this case. But, let’s say, you want to download only data for Russian GDP (from the “Quarterly National Accounts” dataset) between 2000 and 2020:

# create the list of filters
data_filter <- list("country" = "RUS", 
                    "transaction" = "B1_GA", 
                    "measure" = "V")

# download the data
df <- get_dataset(dataset = "SNA_TABLE1", 
                  filter = data_filter,
                  start_time = 2000, 
                  end_time = 2020)

head(df)

Some datasets allow filtering using additional dimensions (e.g., frequency):

# create the list of filters
data_filter <- list("country" = "RUS", 
                    "transaction" = "B1_GE",
                    "measure" = "LNBQRSA", 
                    "frequency" = "Q")

# download the data
df <- get_dataset(dataset = "QNA", 
                  filter = data_filter,
                  start_time = 2005, 
                  end_time = 2020)

Ordering of filters implied by the dataset should be respected when creating the customized list of filters. For instance, specifying the variable name (B1_GE) in the list above after the frequency element will result in an error, since the list of filters will not correspond to the structure of the QNA dataset. In addition, some filters may be mandatory, while others are optional. In the example above, leaving out the indicator name (B1_GE) but retaining the other filters will result in an error. At the same time, leaving out the measure and frequency filters (LNBQRSA and Q) allows downloading the data series but with all the available measures and frequencies.

Figuring out the relevant filters and their correct ordering through the downloaded data structure object is not straightforward. An easier way to understand which filters are used to produce a given series is to look up the SDMX expression of the series (or of the group of data series) on the OECD website after applying the necessary filters. The SDMX expression equivalent to the list of filters above looks like this (go to Export -> SDMX (XML) -> SDMX DATA URL on this page to look up this expression):

https://stats.oecd.org/restsdmx/sdmx.ashx/GetData/QNA/RUS.B1_GE.LNBQRSA.Q/all?startTime=2005-Q1&endTime=2021-Q3

Searching for datasets using a search term can be done using the search_dataset() function:

search_dataset("QNA", oecd_data_list)

UN Comtrade

UN Comtrade is one of few sources of detailed data on international trade flows. There are several options for accessing this data and I will review comtradr and tradestatistics packages for doing that. UN Comtrade API page also shows how to download their data using a user-defined R function.

comtradr

ct_search is the main function in this package for downloading data:

# load the package
library(comtradr)

# download the data
df <- ct_search(reporters = "Kazakhstan", 
                partners = "All", 
                trade_direction = "exports",
                freq = "annual", 
                start_date = 2016, 
                end_date = 2020)

# plot
ggplot(df[df$partner == "World", ], aes(x = period, y = trade_value_usd/10^6, group = 1)) + 
    geom_line()

One limitation of the UN Comtrade API is that one can download no more than five consecutive years or months of data in a single request if either the reporters or partners argument includes "all" (see here for more details). But it’s not all that hopeless. If you need to download the data for more than five years (or all the available years) at once, then you will need to apply the restrictions of up to five elements to either the reporters or partners arguments (i.e., they should be a character vector of country names of length five or fewer). For instance, this query will work and download data from 1995 (earliest available) to 2020 for a single reporting country and five of its trading partners:

df <- ct_search(reporters = "Kazakhstan", 
                partners = c("Russian Federation", "Ukraine", "Belarus", "Uzbekistan", "Armenia"), 
                trade_direction = "exports",
                freq = "annual")

On the other hand, this query will return an error, because the partners argument includes a character vector of length greater than five):

df <- ct_search(reporters = "Kazakhstan", 
                partners = c("Russian Federation", "Ukraine", "Belarus", "Uzbekistan", "Armenia", "Azerbaijan"), 
                trade_direction = "exports",
                freq = "annual")

Making a request with multiple reporters and partners works well too:

df <- ct_search(reporters = c("Kazakhstan", "Uzbekistan"), 
                partners = c("Russian Federation", "Ukraine", "Belarus", "Armenia"), 
                trade_direction = "exports",
                freq = "annual")

tradestatistics

The tradestatistics package is a good alternative to comtrader for accessing the UN Comtrade data. The package’s author makes the same UN Comtrade data available through their own infrastructure with the added benefits of the data being provided in the “tidy” format, with additional processing, extra utility functions and fewer usage limitations on the API. ots_create_tidy_data is the main function for downloading the data. The function does not have an option for indicating the direction of trade and it downloads both export and import data.

# load the package
library(tradestatistics)

# download the data
df <- ots_create_tidy_data(years = 1995:2019, 
                           reporters = "kaz", 
                           partners = c("rus", "ukr", "blr", "uzb", "arm"), 
                           commodities = "all", 
                           table = "yrpc")

It’s possible to query data for multiple reporter countries at the same time:

# data at aggregated level (year - reporter - partner)
df <- ots_create_tidy_data(years = 2018:2019, 
                           reporters = c("kaz", "uzb"),
                           partners = c("rus", "ukr", "blr", "arm"),
                           commodities = "all", 
                           table = "yrp")
head(df)

The package provides access to a number of tables through the API. You can list them by calling the ots_tables object in the console.

# look up the li
ots_tables

Eurostat

There is a very convenient eurostat package for downloading data from Eurostat. You can use the get_eurostat_toc function for looking up the list of all the datasets and the search_eurostat function for searching datasets based on a search term.

# load the package
library("eurostat")

# get the list of all eurostat datasets
df <- get_eurostat_toc()
head(df)

Searching for relevant datasets using a search term:

# search for relevant datasets
res <- search_eurostat("exports", type = "table")
head(res)

After locating a necessary dataset, you will need its ID to download the data using the get_eurostat function:

# download the data
df <- get_eurostat(id = "ei_bsco_m",
                   time_format = "date",
                   filters = "none",
                   type = "code",
                   select_time = NULL,
                   cache = TRUE,
                   update_cache = FALSE,
                   cache_dir = NULL,
                   compress_file = TRUE,
                   stringsAsFactors = FALSE,
                   keepFlags = FALSE)

The above request downloads the whole dataset. You can use the filters argument to filter the dataset down to a smaller set:

# from the dataset on business/consumer surveys, download the consumer confidence indicator (indic = "BS-CSMI"), seasonally adjusted (s_adj = "SA") and only for Austria (geo = "ATQ")
df <- get_eurostat(id = "ei_bsco_m",
                   time_format = "date",
                   filters = list(indic = "BS-CSMCI",
                                  s_adj = "SA",
                                  geo = "AT"))

# plot
df <- df[!is.na(df$values), ]
ggplot(df, aes(x = time, y = values, group = 1)) + geom_line()

One very useful function in this package is label_eurostat. The function allows to quickly download the labels associated with the data table (i.e., the one that was initially downloaded using get_eurostat():

# get the data labels
labeled_df <- label_eurostat(df)
head(labeled_df)

The package also provides a useful function for downloading the geospatial data from Eurostat (get_eurostat_geospatial):

# get the data on the NEET (neither in employment nor education and training) rate by NUTS 2 regions
df <- get_eurostat(id = "edat_lfse_22", time_format = "raw") %>%
    dplyr::filter(sex == "T", 
                  age == "Y15-24", 
                  time == 2020, 
                  nchar(geo) == 4) %>%
    dplyr::mutate(cat = cut_to_classes(values, 
                                       style = "equal"))
# download the geospatial data
data_geo <- get_eurostat_geospatial(resolution = "20", 
                                    nuts_level = "2", 
                                    year = 2021)
# merge
data <- dplyr::inner_join(data_geo, df)
# plot
ggplot(data = data) +
  geom_sf(aes(fill = cat), color = "dim grey", size = 0.1) +
  scale_fill_brewer(palette = "Blues") +
  guides(fill = guide_legend(reverse = TRUE, title = "")) +
  labs(title = "Young people (age 15-24) neither in employment nor in\n education and training (NEET), 2020, percent",
       caption = "Source: Eurostat.") +
  theme_minimal() +
  theme(legend.position = c(.87, .75), 
        title = element_text(size = 9), 
        legend.text = element_text(size = 8), 
        legend.background = element_blank(),
        plot.caption = element_text(hjust = 0, size = 9)) + 
  coord_sf(xlim = c(-10, 45), ylim = c(35, 70))

ECB

There is an ecb package for downloading data from the ECB Statistical Data Warehouse. The list of available datasets can be downloaded using the get_dataflows function but the retrieved data frame is quite high level and identification of specific data series will require going through the ECB’s data website itself. The package doesn’t have a function for looking up and searching for specific series (which looks to be the result of such functionality missing in the API) itself.

# load the package
library(ecb)

# retrieve the list of datasets (referred to as "data flows")
head(get_dataflows())

Data series can be downloaded using the get_data function (I looked up the series key or ID on this page):

# download the data
df <- get_data(key = "CISS.D.U2.Z0Z.4F.EC.SS_CI.IDX", 
               filter = list(startPeriod = "2000-01-01", 
                             endPeriod = "2020-12-31", 
                             # updateAfter = "2000-01-01", 
                             # firstNObservations = 12, 
                             # lastNObservations = 12, 
                             detail = "full"))

# plot
ggplot(df, aes(x = as.Date(obstime), y = obsvalue)) + 
    geom_line() + 
    labs(title = "Composite Indicator of Systemic Streess, Euro area, index", 
         x = NULL, 
         y = NULL)

FRED

There are a few R packages for downloading data from FRED (e.g., fredr, FredR, alfred). I will briefly review only fredr, which appears to be quite comprehensive and it is still maintained (at the time of writing). To use the FRED API, you need to obtain the FRED API key that allows to access the database. fredr is the main function for downloading the data series.

# load the package
library(fredr)

# set the API key (the alternative is to add "FRED_API_KEY=you_api_key_here" to your .Renviron file)
# fredr_set_key("your_api_key_here")

# download the total US non-farm employment series
df <- fredr(series_id = "PAYEMS",
            observation_start = as.Date("1990-01-01"),
            observation_end = as.Date("2022-01-01"))

# plot
ggplot(df, aes(x = date, y = value)) + 
    geom_line() + 
    labs(title = "Employees on non-farm payrolls (thousand)")

The fredr (and the FRED API) allows downloading only one series at a time. One option for dealing with this limitation is to apply the fredr function to a vector of data series names:

# load the `purrr` package to access the `map_*` family of functions
library(purrr)

# vector of data series names
data_series <- c("IPDCONGD", "IPNCONGD")

# download the data using the `map_dfr` function
df <- map_dfr(data_series, 
              fredr, 
              observation_start = as.Date("1990-01-01"),
              observation_end = as.Date("2022-01-01"))

# plot
ggplot(df, aes(x = date, y = value, color = series_id)) + 
    geom_line() + 
    scale_color_manual(labels = c("Industrial production: durable consumer goods", 
                                  "Industrial production: non-durable consumer goods"), 
                       values = c("blue", "red")) +
    labs(title = "Industrial production",
         subtitle = "Durable and non-durable consumer goods, 2017=100") +
    theme(legend.position = "bottom", 
          legend.title = element_blank())

The package also provides a convenient function for searching through all the FRED series.

df <- fredr_series_search_text(search_text = "consumer price index", 
                               filter_variable = "seasonal_adjustment", 
                               order_by = "popularity", 
                               limit = 5)

# have a look
head(df)

What if there is no an R package?

Sometimes an API for accessing the data is available but there is no an easy to use R (API wrapper) package for taking advantage of the API quickly. In this case, the solution may be to use a number of utility packages for making web requests through R. In the brief example below, I make a request to the API of https://exchangeratesapi.io/, a service providing exchange rates data. This example follows the structure of the API of https://exchangeratesapi.io/ and details of a request to other services may differ.

# load the packages
library(httr)           # tools for working with HTTP
library(jsonlite)       # JSON parser

# identify the base URL 
base_url <- "http://api.exchangeratesapi.io/v1/"

# your access key
access_key <- "you_access_key"

# additional directory information (necessary for exchangeratesapi.io)
path <- "latest"

# build the URL for making a request (syntax of the API for other services will very likely differ)
api_url <- paste(base_url, path, "?", "access_key=", access_key, sep = "")

# make the GET request and check the status of the request
df1 <- httr::GET(url = api_url)
httr::http_status(df1)

# the alternative is to directly download and parse the json file
df2 <- jsonlite::fromJSON(txt = api_url)
df2$success

# create a data frame with the exchange rates from df1

# what is the base currency
df2$base

# convert the downloaded data (in 'raw' format) to characters (i.e., JSON) and parse the JSON
rates <- fromJSON(rawToChar(df1$content))

# simplify the lists of lists structure
rates <- as.data.frame(sapply(rates$rates, `[`))
head(rates)
$category
[1] "Success"

$reason
[1] "OK"

$message
[1] "Success: (200) OK"

[1] TRUE
[1] "EUR"
LS0tCnRpdGxlOiAiQWNjZXNzaW5nIG1hY3JvZWNvbm9taWMgZGF0YSB0aHJvdWdoIFIiCmF1dGhvcjogIlVtaWRqb24gQWJkdWxsYWV2IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDIKICAgIHRvY19mbG9hdDogeWVzCiAgICB0aGVtZTogY29zbW8KICAgIGhpZ2hsaWdodDogcHlnbWVudHMKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogJzInCiAgICBkZl9wcmludDogcGFnZWQKZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IDcyCi0tLQoKKioyMDIyLTAyLTE0KioKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfSAKa25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFKSAKYGBgCgpgYGB7ciBpbmNsdWRlID0gRkFMU0V9CmxpYnJhcnkoRFQpCmxpYnJhcnkocmRibm9taWNzKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocm1hcmtkb3duKQpgYGAKCiMjIEludHJvZHVjdGlvbgoKVGhpcyBub3RlYm9vayBwcm92aWRlcyBicmllZiBndWlkYW5jZSBvbiBhIG51bWJlciBgUmAgcGFja2FnZXMgdGhhdCBhbGxvdyBhY2Nlc3NpbmcgZWNvbm9taWMgZGF0YWJhc2VzIGRpcmVjdGx5IHRocm91Z2ggYFJgLiBUaGVzZSBwYWNrYWdlcyB0YWtlIGFkdmFudGFnZSBvZiB0aGUgaW5jcmVhc2luZyBudW1iZXIgb2YgZGF0YSBwcm92aWRlcnMgbWFraW5nIHRoZWlyIGRhdGFiYXNlcyBhY2Nlc3NpYmxlIHRocm91Z2ggW0FQSXNdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0FQSSkgYW5kIHRoZSBhdmFpbGFiaWxpdHkgb2YgYFJgIHBhY2thZ2VzIGZvciBtYWtpbmcgd2ViIHJlcXVlc3RzIChmb3IgaW5zdGFuY2UsIFtodHRyXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvaHR0ci8pIG9yIFtqc29ubGl0ZV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2pzb25saXRlL2luZGV4Lmh0bWwpKS4gVGhpcyBub3RlIGNvdmVycyB0aGUgcHVibGljIHNlY3RvciBkYXRhIHByb3ZpZGVycyAoZS5nLiwgaW50ZXJuYXRpb25hbCBvcmdhbml6YXRpb25zLCBjZW50cmFsIGJhbmtzIG9yIHNpbWlsYXIpIHByaW1hcmlseSBhbmQgbGVhdmVzIG91dCB0aGUgY29tbWVyY2lhbCBvbmVzIChlLmcuLCBCbG9vbWJlcmcsIERhdGFzdHJlYW0pLgoKIyMgZGIubm9taWNzCgpbZGIubm9taWNzXShodHRwczovL2RiLm5vbWljcy53b3JsZC8pIGRlc2NyaWJlcyBpdHNlbGYgYXMgdGhlICJ3b3JsZCdzIGVjb25vbWljIGRhdGFiYXNlIiBhbmQgaXQgbWF5IGRlc2VydmUgdGhpcyBkZXNjcmlwdGlvbi4gQXQgdGhlIHRpbWUgb2Ygd3JpdGluZywgZGIubm9taWNzIGFnZ3JlZ2F0ZWQgZGF0YXNldHMgZnJvbSBtb3JlIHRoYW4gODAgc291cmNlcyBhbmQgcHJvdmlkZWQgYWNjZXNzIHRvIHRoZW0gdGhyb3VnaCBhIFtzaW5nbGUKQVBJXShodHRwczovL2FwaS5kYi5ub21pY3Mud29ybGQvdjIyL2FwaWRvY3MpLiBFZmZlY3RpdmVseSwgYnkgdXNpbmcgZGIubm9taWNzIHlvdSBjYW4gaWdub3JlIG1hbnkgb2YgdGhlIGBSYCBwYWNrYWdlcyBmb3Igb3RoZXIgc291cmNlcyB0aGF0IEkgY292ZXIgYmVsb3csIGdpdmVuIHRoYXQgaXQgYWxyZWFkeSBwcm92aWRlcyBhIHVuaWZpZWQgaW50ZXJmYWNlIHRvIG1hbnkgb2YgdGhlc2UgZGF0YXNldHMuCgpUaGUgdGVhbSBiZWhpbmQgZGIubm9taWNzIHByb3ZpZGVzIGFuIGBSYCBwYWNrYWdlIFtyZGJub21pY3NdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9yZGJub21pY3MvaW5kZXguaHRtbCkKZm9yIGFjY2Vzc2luZyB0aGVpciBkYXRhYmFzZS4gSSB3aWxsIHNob3cgYmVsb3cgaG93IHRvIGdldCBzdGFydGVkIHdpdGggdGhpcyBwYWNrYWdlLCB3aGlsZSBmdXJ0aGVyIGRldGFpbHMgY2FuIGJlIGZvdW5kIG9uIFt0aGlzIHBhZ2VdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9yZGJub21pY3MvdmlnbmV0dGVzL3JkYm5vbWljcy5odG1sKSBvciBpbiB0aGUgdmlnbmV0dGUgdGhhdCB5b3UgY2FuIGFjY2VzcyBieSB0eXBpbmcgYHZpZ25ldHRlKCJyZGJub21pY3MiKWAgaW4gdGhlIGBSYCBjb25zb2xlLiBUaGUgcGFja2FnZSBpcyBhdmFpbGFibGUgdGhyb3VnaCBbQ1JBTl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvaW5kZXguaHRtbCkuCgpZb3UgY2FuIHF1aWNrbHkgY2hlY2sgdGhlIGxpc3Qgb2YgYXZhaWxhYmxlIHByb3ZpZGVycyBhbmQgcmV0cmlldmUgdGhlIGxpc3Qgb2YgZGF0YXNldHMgKEkgcmVjb21tZW5kIGRvaW5nIHRoZSBsYXR0ZXIgYnkgc3BlY2lmeWluZyB0aGUgcHJvdmlkZXIgaW4gdGhlIGZ1bmN0aW9uIGNhbGwpOgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gVFJVRSwgcmVzdWx0cz0naGlkZSd9CiMgbG9hZCB0aGUgcGFja2FnZQpsaWJyYXJ5KCJyZGJub21pY3MiKQoKIyByZXRyaWV2ZSB0aGUgbGlzdCBvZiBwcm92aWRlcnMgKGluY2x1ZGluZyB0aGUgcHJvdmlkZXIgY29kZSkKcHJvdmlkZXJzIDwtIHJkYl9wcm92aWRlcnMoKQoKIyByZXRyaWV2ZSB0aGUgbGlzdCBvZiBkYXRhc2V0cwpkYXRhc2V0cyA8LSByZGJfZGF0YXNldHMocHJvdmlkZXJfY29kZSA9ICJJTUYiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHNpbXBsaWZ5ID0gVFJVRSkKCiMgcmV0cml2ZSB0aGUgbGlzdCBvZiBkYXRhIHNlcmllcyAoY2FuIHRha2Ugc29tZSB0aW1lKQpzZXJpZXMgPC0gcmRiX3Nlcmllcyhwcm92aWRlcl9jb2RlID0gIklNRiIsIAogICAgICAgICAgICAgICAgICAgICBkYXRhc2V0X2NvZGUgPSAiV0VPOjIwMjEtMDQiLCAKICAgICAgICAgICAgICAgICAgICAgc2ltcGxpZnkgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFKQoKaGVhZChzZXJpZXMpCmBgYAoKYHJkYmAgaXMgdGhlIG1haW4gZnVuY3Rpb24gZm9yIHNlbmRpbmcgZGF0YSByZXF1ZXN0cy4gWW91IGNhbiBkb3dubG9hZCB0aGUgc2VyaWVzIHVzaW5nIG9ubHkgdGhlIGBpZHNgIGFyZ3VtZW50IChyZWZlcnJpbmcgdG8gYW4gSUQgb2YgYSBzZXJpZXMpLiBUbyBkbyB0aGlzLCB5b3UgbmVlZCB0byBjb21iaW5lIHRoZSBwcm92aWRlciwgZGF0YXNldCwgYW5kIHNlcmllcyBjb2RlcyBpbnRvIGEgc2luZ2xlIHN0cmluZyBhcyBmb2xsb3dzIGAicHJvdmlkZXJfY29kZS9kYXRhc2V0X2NvZGUvc2VyaWVzX2NvZGUiYC4gQWx0ZXJuYXRpdmVseSwgdGhlc2UgcGFyYW1ldGVycyBjYW4gYmUgZmVkIGludG8gdGhlIGZ1bmN0aW9uIHNlcGFyYXRlbHkgdXNpbmcgdGhlaXIgb3duIGFyZ3VtZW50cy4gVGhlIGZvbGxvd2luZyBjb2RlIHNob3dzIHR3byBhbHRlcm5hdGl2ZSB3YXlzIG9mIGRvd25sb2FkaW5nIHRoZSBzYW1lIGRhdGEgc2VyaWVzIHVzaW5nIGByZGJgLgoKYGBge3IsIGV2YWwgPSBUUlVFfQojIGRvd25sb2FkIHRoZSBjb25zdGFudCBwcmljZSBHRFAgcGVyIGNhcGl0YSBzZXJpZXMgZm9yIFV6YmVraXN0YW4gdXNpbmcgaWRzIG9yIG1hc2sgYXJndW1lbnRzCgojIGJ5IHVzaW5nIHRoZSBpZHMgYXJndW1lbnQKZGYxIDwtIHJkYihpZHMgPSAiSU1GL1dFTzoyMDIxLTA0L1VaQi5OR0RQUlBDLm5hdGlvbmFsX2N1cnJlbmN5IikKCiMgYnkgdXNpbmcgc2VwYXJhdGUgYXJndW1lbnRzCmRmMiA8LSByZGIocHJvdmlkZXJfY29kZSA9ICJJTUYiLCAKICAgICAgICAgICBkYXRhc2V0X2NvZGUgPSAiV0VPOjIwMjEtMDQiLCAKICAgICAgICAgICBtYXNrID0gIlVaQi5OR0RQUlBDLm5hdGlvbmFsX2N1cnJlbmN5IikKCiMgcGxvdApnZ3Bsb3QoZGYxLCBhZXMoeCA9IHBlcmlvZCwgeSA9IHZhbHVlLCBncm91cCA9IDEpKSArIAogICAgZ2VvbV9saW5lKCkKYGBgCgpZb3UgY2FuIGFsc28gdXNlIHRoZSBgZGltZW5zaW9uc2AgYXJndW1lbnQgb2YgYHJkYmAgdG8gc3BlY2lmeSBvbmUgb3Igc2V2ZXJhbCBkaW1lbnNpb25zIChpLmUuLCB2YXJpYWJsZSBuYW1lcykgZnJvbSB0aGUgZGF0YXNldCAoeW91IHdpbGwgbmVlZCB0byBsb29rIHVwIGhvdyBleGFjdGx5IHRoZSB2YXJpYWJsZXMgYXJlIG5hbWVkIHRvIHVzZSB0aGlzIG9wdGlvbikuCgpgYGB7ciwgZXZhbCA9IFRSVUV9CiMgZG93bmxvYWQgdGhlIGNvbnN0YW50IHByaWNlIEdEUCBwZXIgY2FwaXRhIHNlcmllcyB1c2luZyB0aGUgZGltZW5zaW9ucyBhcmd1bWVudApkZiA8LSByZGIocHJvdmlkZXJfY29kZSA9ICJJTUYiLCAKICAgICAgICAgIGRhdGFzZXRfY29kZSA9ICJXRU86MjAyMS0wNCIsIAogICAgICAgICAgZGltZW5zaW9ucyA9IGxpc3QoIndlby1jb3VudHJ5IiA9ICJLQVoiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ3ZW8tc3ViamVjdCIgPSAiTkdEUFJQQyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgInVuaXQiID0gIm5hdGlvbmFsX2N1cnJlbmN5IikpCmBgYAoKQW5vdGhlciBvcHRpb24gaXMgdG8gdXNlIHRoZSBgcXVlcnlgIGFyZ3VtZW50IHRvIHNlYXJjaCBmb3IgYSBzZXJpZXMgd2l0aGluIGEgZ2l2ZW4gZGF0YXNldC4KCmBgYHtyLCBldmFsID0gVFJVRX0KIyBkb3dubG9hZCB0aGUgZGF0YSB1c2luZyBhIHNlYXJjaCB0ZXJtCmRmIDwtIHJkYihwcm92aWRlcl9jb2RlID0gIklNRiIsIAogICAgICAgICAgZGF0YXNldF9jb2RlID0gIldFTzoyMDIxLTA0IiwgCiAgICAgICAgICBxdWVyeSA9ICJLYXpha2hzdGFuIGdyb3NzIGRvbWVzdGljIHByb2R1Y3QgcGVyIGNhcGl0YSBjb25zdGFudCBwcmljZXMgbmF0aW9uYWwgY3VycmVuY3kiKQpoZWFkKGRmKQpgYGAKCkFyZ3VtZW50cyBvZiBgcmRiYCBhY2NlcHQgdmVjdG9ycyBvZiBzdHJpbmdzIGFsbG93aW5nIGRvd25sb2FkaW5nIG11bHRpcGxlIGRhdGEgc2VyaWVzIGF0IG9uY2UuIFRoZSBgbWFza2AgYXJndW1lbnQgYWxzbyBoYXMgYSBjb252ZW5pZW50IG5vdGF0aW9uIGZvciBjb21iaW5pbmcgdGhlIGRhdGEgcmVxdWVzdCBwYXJhbWV0ZXJzLgoKYGBge3IsIGV2YWwgPSBUUlVFfQojIGJ5IHVzaW5nIHRoZSBpZHMgYXJndW1lbnQgYW5kIGEgdmVjdG9yIG9mIHN0cmluZ3MKZGYgPC0gcmRiKGlkcyA9IGMoIklNRi9XRU86MjAyMS0wNC9LQVouUENQSVBDSC5wY2VudF9jaGFuZ2UiLAogICAgICAgICAgICAgICAgICAiSU1GL1dFTzoyMDIxLTA0L1VaQi5QQ1BJUENILnBjZW50X2NoYW5nZSIpKQoKIyBieSB1c2luZyB0aGUgbWFzayBhcmd1bWVudAogICAgIyBkb3dubG9hZCB0aGUgc2VyaWVzIGZvciB0d28gY291bnRyaWVzIG9ubHkKICAgIGRmIDwtIHJkYihwcm92aWRlcl9jb2RlID0gIklNRiIsIAogICAgICAgICAgICAgIGRhdGFzZXRfY29kZSA9ICJXRU86MjAyMS0wNCIsIAogICAgICAgICAgICAgIG1hc2sgPSAiS0FaK1VaQi5QQ1BJUENILnBjZW50X2NoYW5nZSIpCiAgICAKICAgICMgZG93bmxvYWQgdGhlIHNlcmllcyBmb3IgYWxsIGNvdW50cmllcwogICAgZGYgPC0gcmRiKHByb3ZpZGVyX2NvZGUgPSAiSU1GIiwgCiAgICAgICAgICAgICAgZGF0YXNldF9jb2RlID0gIldFTzoyMDIxLTA0IiwgCiAgICAgICAgICAgICAgbWFzayA9ICIuUENQSVBDSC5wY2VudF9jaGFuZ2UiKQogICAgCiAgICAjIGRvd25sb2FkIGFsbCB0aGUgZGF0YSBmcm9tIHRoZSBkYXRhc2V0IGZvciBhIGdpdmVuIGNvdW50cnkKICAgIGRmIDwtIHJkYihwcm92aWRlcl9jb2RlID0gIklNRiIsIAogICAgICAgICAgICAgIGRhdGFzZXRfY29kZSA9ICJXRU86MjAyMS0wNCIsIAogICAgICAgICAgICAgIG1hc2sgPSAiS0FaLiIpCiAgICAKICAgICMgZG93bmxvYWQgYWxsIHRoZSBkYXRhIGZyb20gdGhlIGRhdGFzZXQgZm9yIHR3byBjb3VudHJpZXMKICAgIGRmIDwtIHJkYihwcm92aWRlcl9jb2RlID0gIklNRiIsIAogICAgICAgICAgICAgIGRhdGFzZXRfY29kZSA9ICJXRU86MjAyMS0wNCIsIAogICAgICAgICAgICAgIG1hc2sgPSAiS0FaK1VaQi4iKQpgYGAKCiMjIElNRgpbaW1mcl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2ltZnIvKSBpcyBhIGNvbnZlbmllbnQgcGFja2FnZSBmb3IgZGlyZWN0bHkgYWNjZXNzaW5nIHRoZSBkYXRhc2V0cyBwcm92aWRlZCBieSBbSU1GXShodHRwOi8vZGF0YS5pbWYub3JnLykuCgpgYGB7ciwgZXZhbCA9IFRSVUV9CiMgbG9hZCB0aGUgcGFja2FnZQpsaWJyYXJ5KCJpbWZyIikKYGBgCgpZb3UgY2FuIHVzZSB0aGUgYGltZl9pZHNgIGZ1bmN0aW9uIHRvIGxvb2sgdXAgdGhlIElEcyBvZiBhdmFpbGFibGUgZGF0YXNldHM6CgpgYGB7ciBlY2hvPVRSVUUsIHJlc3VsdHM9J2hpZGUnfQojIGxvb2sgdXAgdGhlIGlkcyBvZiBkYXRhc2V0cwpkYXRhX2lkcyA8LSBpbWZfaWRzKHJldHVybl9yYXcgPSBGQUxTRSkKcm1hcmtkb3duOjpwYWdlZF90YWJsZShkYXRhX2lkcykKYGBgCgpUaGVuLCB1c2UgdGhlIGBpbWZfY29kZWxpc3RgIGZ1bmN0aW9uIHRvIGxvb2sgdXAgdGhlIGNvZGVzIGZvciBhIHNwZWNpZmljIGRhdGFiYXNlLiBUaGUgbWFpbiBpbmZvcm1hdGlvbiB5b3UgbmVlZCBmcm9tIHRoaXMgY2FsbCBpcyB0aGUgbmFtZSBvZiBhbiBvYmplY3QgbGlzdGluZyBhbGwgaW5kaWNhdG9ycyB3aXRoaW4gdGhlIGRhdGFzZXQuCgpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQojIGxvb2sgdXAgdGhlIGNvZGVzIGZvciBhIGRhdGFiYXNlCmNvZGVsaXN0IDwtIGltZl9jb2RlbGlzdChkYXRhYmFzZV9pZCA9ICJCT1AiKQpoZWFkKGNvZGVsaXN0KQpgYGAKCllvdSBjYW4gdGhlbiB1c2UgdGhlIG5hbWUgb2YgdGhhdCBvYmplY3QgdG8gZG93bmxvYWQgdGhlIGxpc3Qgb2YgYWxsIGluZGljYXRvcnMgd2l0aCB0aGUgYGltZl9jb2Rlc2AgZnVuY3Rpb246CgpgYGB7ciwgZXZhbCA9IFRSVUUsIHJlc3VsdHM9J2hpZGUnfQojIGxvb2sgdXAgdGhlIGxpc3Qgb2YgaW5kaWNhdG9ycwpjb2RlcyA8LSBpbWZfY29kZXMoY29kZWxpc3QgPSAiQ0xfSU5ESUNBVE9SX0JPUCIpCnJtYXJrZG93bjo6cGFnZWRfdGFibGUoY29kZXMpCmBgYAoKRmluYWxseSwgdXNlIHRoZSBgaW1mX2RhdGFgIGZ1bmN0aW9uIHRvIGRvd25sb2FkIHRoZSBkYXRhIHVzaW5nIHRoZSBkYXRhYmFzZSBhbmQgaW5kaWNhdG9yIElEczoKCmBgYHtyLCBldmFsID0gVFJVRSwgcmVzdWx0cyA9ICdoaWRlJ30KIyBkb3dubG9hZCB0aGUgZGF0YSBmcm9tIElNRgpkZiA8LSBpbWZfZGF0YShkYXRhYmFzZV9pZCA9ICJCT1AiLCAKICAgICAgICAgICAgICAgaW5kaWNhdG9yID0gIkJNR1NfQlA2X1VTRCIsIAogICAgICAgICAgICAgICBjb3VudHJ5ID0gIktaIiwgICAgICAgICAgICAgICMgdXNlIHRoZSB0d28tbGV0dGVyIElTTyBjb2RlCiAgICAgICAgICAgICAgIHN0YXJ0ID0gMTk5NSwgCiAgICAgICAgICAgICAgIGVuZCA9IGN1cnJlbnRfeWVhcigpLCAKICAgICAgICAgICAgICAgZnJlcSA9ICJBIiwKICAgICAgICAgICAgICAgcHJpbnRfdXJsID0gRkFMU0UpCgojIHBsb3QKZ2dwbG90KGRmLCBhZXMoeCA9IHllYXIsIHkgPSBCTUdTX0JQNl9VU0QsIGdyb3VwID0gMSkpICsgCiAgICBnZW9tX2xpbmUoKSArIAogICAgc2NhbGVfeF9kaXNjcmV0ZShicmVha3MgPSBzZXEoZGYkeWVhclsxXSwgZGYkeWVhcltucm93KGRmKV0sIGJ5ID0gNSkpCmBgYAoKWW91IGNhbiBhbHNvIGZlZWQgdmVjdG9ycyBvZiBzdHJpbmdzIGVpdGhlciB0byBgaW5kaWNhdG9yYCBvciBgY291bnRyeWAgYXJndW1lbnRzLiBVc2UgYCJhbGwiYCBpbiB0aGUgYGNvdW50cnlgIGFyZ3VtZW50IHRvIGRvd25sb2FkIHRoZSBkYXRhIGZvciBhbGwgY291bnRyaWVzLgoKYGBge3IsIGV2YWwgPSBGQUxTRSwgcmVzdWx0cyA9ICdoaWRlJ30KIyBkb3dubG9hZGluZyB0d28gZGF0YSBzZXJpZXMgZm9yIHR3byBjb3VudHJpZXMKZGYgPC0gaW1mX2RhdGEoZGF0YWJhc2VfaWQgPSAiQk9QIiwgCiAgICAgICAgICAgICAgIGluZGljYXRvciA9ICJCTUdTX0JQNl9VU0QiLCAKICAgICAgICAgICAgICAgY291bnRyeSA9IGMoIktaIiwgIlJVIiksIAogICAgICAgICAgICAgICBzdGFydCA9IDE5OTUsIAogICAgICAgICAgICAgICBlbmQgPSBjdXJyZW50X3llYXIoKSwgCiAgICAgICAgICAgICAgIHByaW50X3VybCA9IEZBTFNFKQoKaGVhZChkZikKYGBgCgpXaGlsZSB0aGUgYGltZnJgIHBhY2thZ2UgZG9lcyBhbGxvdyBlYXNpbHkgYWNjZXNzaW5nIHRoZSBJTUYgQVBJLCB0aGUgc2FtZSBJTUYgZGF0YSBhcHBlYXJzIGJldHRlciBzdHJ1Y3R1cmVkIGFuZCBvcmdhbml6ZWQgd2hlbiBhY2Nlc3NlZCB1c2luZyB0aGUgYGRiLm5vbWljc2AgcGFja2FnZS4KCiMjIFdvcmxkIEJhbmsKClRoZXJlIGFyZSBzZXZlcmFsIGBSYCBwYWNrYWdlcyBhdmFpbGFibGUgZm9yIGRvd25sb2FkaW5nIHRoZSBXb3JsZCBCYW5rIGRhdGEuIEkgd2lsbCByZXZpZXcgW1dESV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL1dESS9pbmRleC5odG1sKSBhbmQgW3dic3RhdHNdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy93YnN0YXRzL2luZGV4Lmh0bWwpLiAKCiMjIyBbV0RJXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvV0RJL2luZGV4Lmh0bWwpCgpUaGUgcGFja2FnZSBhbHJlYWR5IGNvbnRhaW5zIGEgZGF0YSBvYmplY3Qgd2l0aCB0aGUgbGlzdCBvZiBhdmFpbGFibGUgaW5kaWNhdG9yczoKCmBgYHtyIGVjaG89VFJVRX0KIyBsb2FkIHRoZSBwYWNrYWdlCmxpYnJhcnkoIldESSIpCgojIGxpc3Qgb2YgaW5kaWNhdG9ycwpybWFya2Rvd246OnBhZ2VkX3RhYmxlKGFzLmRhdGEuZnJhbWUoV0RJX2RhdGEkc2VyaWVzKSkKYGBgCgpUaGlzIGxvY2FsIGxpc3Qgb2YgaW5kaWNhdG9ycyBjYW4gYmUgdXBkYXRlZCB0byB0aGUgbGF0ZXN0IHZlcnNpb24gdXNpbmcgdGhlIGBXRElfY2FjaGUoKWAgZnVuY3Rpb24uIElmIG5lY2Vzc2FyeSwgdGhlIG91dHB1dCBvZiB0aGlzIGZ1bmN0aW9uIGNhbiB0aGVuIGJlIGZlZCBpbnRvIHRoZSBmdW5jdGlvbiBmb3IgZG93bmxvYWRpbmcgZGF0YToKCmBgYHtyIGVjaG89VFJVRX0KIyB1cGRhdGUgdGhlIGxpc3Qgb2YgaW5kaWNhdG9ycwpuZXdfbGlzdCA8LSBXREljYWNoZSgpCgojIGNoZWNrIGlmIHRoZSB1cGRhdGVkIGxpc3QgaXMgaWRlbnRpY2FsIHRvIHRoZSBvbmUgaW4gdGhlIHBhY2thZ2UKaWRlbnRpY2FsKG5ld19saXN0JHNlcmllcywgV0RJX2RhdGEkc2VyaWVzKSAKYGBgCgpUaGUgbWFpbiBmdW5jdGlvbiBmb3IgZG93bmxvYWRpbmcgdGhlIGRhdGEgaXMgYFdESWA6CgpgYGB7ciBlY2hvPVRSVUV9CiMgZG93bmxvYWQgZGF0YSBmcm9tIHRoZSBXREkgZGF0YWJhc2UKZGYgPC0gV0RJKGNvdW50cnkgPSAiS1oiLCAKICAgICAgICAgIGluZGljYXRvciA9ICJOWS5HRFAuUENBUC5LTiIsIAogICAgICAgICAgc3RhcnQgPSAxOTkxLCAKICAgICAgICAgIGVuZCA9IDIwMjAsCiAgICAgICAgICBleHRyYSA9IEZBTFNFLCAKICAgICAgICAgIGNhY2hlID0gbmV3X2xpc3QpCgojIHBsb3QKZ2dwbG90KGRmLCBhZXMoeCA9IHllYXIsIHkgPSBOWS5HRFAuUENBUC5LTiwgZ3JvdXAgPSAxKSkgKyAKICAgIGdlb21fbGluZSgpCmBgYAoKVGhlIHBhY2thZ2VzIGFsc28gY29tZXMgd2l0aCBhIHVzZWZ1bCBzZWFyY2ggZnVuY3Rpb24gKHRoZSBmdW5jdGlvbiBzdXBwb3J0cyBbcmVndWxhciBleHByZXNzaW9uc10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9zdHJpbmdzLmh0bWwjbWF0Y2hpbmctcGF0dGVybnMtd2l0aC1yZWd1bGFyLWV4cHJlc3Npb25zKSkKCmBgYHtyIGVjaG89VFJVRX0KV0RJc2VhcmNoKCJnZHAuKmNhcGl0YS4qY29uc3RhbnQuKkxDVSIpCmBgYAoKIyMjIFt3YnN0YXRzXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvd2JzdGF0cy9pbmRleC5odG1sKQoKVGhpcyBwYWNrYWdlIGlzIHF1aXRlIHNpbWlsYXIgdG8gW1dESV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL1dESS9pbmRleC5odG1sKS4gSXQgc2ltaWxhcmx5IGNvbWVzIHdpdGggYSBkYXRhIG9iamVjdCB0aGF0IGNvbnRhaW5zIHRoZSBsaXN0IG9mIGF2YWlsYWJsZSBpbmRpY2F0b3JzOgoKYGBge3IgZWNobz1UUlVFfQojIGxvYWQgdGhlIHBhY2thZ2UKbGlicmFyeSh3YnN0YXRzKQoKIyB0aGUgbGlzdCBvZiBpbmRpY2F0b3JzCndiX2NhY2hlbGlzdCRpbmRpY2F0b3JzCmBgYAoKVGhpcyBkYXRhIG9iamVjdCBjYW4gc2ltaWxhcmx5IGJlIHVwZGF0ZWQgdG8gdGhlIGxhdGVzdCB2ZXJzaW9uIGFuZCBmdXJ0aGVyIGZlZCBpbnRvIHRoZSBmdW5jdGlvbiBmb3IgZG93bmxvYWRpbmcgZGF0YS4KCmBgYHtyIGVjaG89VFJVRX0KIyB1cGRhdGUgdGhlIGxpc3Qgb2YgaW5kaWNhdG9ycwpuZXdfbGlzdCA8LSB3Yl9jYWNoZSgpCmBgYAoKYHdiX2RhdGFgIGlzIHRoZSBmdW5jdGlvbiBmb3IgZG93bmxvYWRpbmcgdGhlIGRhdGE6CgpgYGB7ciBlY2hvPVRSVUV9CiMgZG93bmxvYWQgZGF0YSBmcm9tIHRoZSBXREkgZGF0YWJhc2UKZGYgPC0gd2JfZGF0YShpbmRpY2F0b3IgPSAiTlkuR0RQLlBDQVAuS04iLCAKICAgICAgICAgICAgICBjb3VudHJ5ID0gIktaIiwgCiAgICAgICAgICAgICAgc3RhcnRfZGF0ZSA9IDE5OTEsIAogICAgICAgICAgICAgIGVuZF9kYXRlID0gMjAyMCwgCiAgICAgICAgICAgICAgY2FjaGUgPSBuZXdfbGlzdCwgCiAgICAgICAgICAgICAgcmV0dXJuX3dpZGUgPSBGQUxTRSkKCnJtYXJrZG93bjo6cGFnZWRfdGFibGUoZGYpCmBgYAoKVGhlIHBhY2thZ2UgYWxzbyBoYXMgYSBjb252ZW5pZW50IGZ1bmN0aW9uIGZvciBzZWFyY2hpbmcgdGhlIHJlbGV2YW50IGRhdGEgc2VyaWVzICh0aGF0IGFsc28gc3VwcG9ydHMgcmVndWxhciBleHByZXNzaW9ucyk6CgpgYGB7ciBlY2hvPVRSVUV9CndiX3NlYXJjaCgiZ2RwLipjYXBpdGEuKmNvbnN0YW50IExDVSIpCmBgYAoKQ29tcGFyZWQgdG8gdGhlIGBXREk6OldESSgpYCBmdW5jdGlvbiwgYHdic3RhdHM6OndiX2RhdGFgIGFwcGVhcnMgdG8gcHJvdmlkZSBtb3JlIGZsZXhpYmlsaXR5IGFuZCBjb252ZW5pZW50IG9wdGlvbnMuIEZvciBpbnN0YW5jZSwgdGhlIGByZXR1cm5fd2lkZWAgYXJndW1lbnQgYmVjb21lcyB2ZXJ5IGhhbmR5IHdoZW4gZG93bmxvYWRpbmcgbXVsdGlwbGUgc2VyaWVzIGF0IG9uY2U6CgpgYGB7ciBlY2hvPVRSVUV9CiMgZG93bmxvYWQgdHdvIGluZGljYXRvcnMgZm9yIHR3byBjb3VudHJpZXMgYW5kIHRyYW5zZm9ybSB0byB3aWRlIGZvcm1hdApkZiA8LSB3Yl9kYXRhKGluZGljYXRvciA9IGMoIk5ZLkdEUC5QQ0FQLktOIiwgIk5ZLkdEUC5QQ0FQLktELlpHIiksIAogICAgICAgICAgICAgIGNvdW50cnkgPSBjKCJLWiIsICJVWiIpLCAKICAgICAgICAgICAgICBzdGFydF9kYXRlID0gMTk5MSwgCiAgICAgICAgICAgICAgZW5kX2RhdGUgPSAyMDIwLCAKICAgICAgICAgICAgICBjYWNoZSA9IG5ld19saXN0LCAKICAgICAgICAgICAgICByZXR1cm5fd2lkZSA9IFRSVUUpCgpybWFya2Rvd246OnBhZ2VkX3RhYmxlKGRmKQpgYGAKCkluIGFkZGl0aW9uLCBgbXJ2YCAoIm1vc3QgcmVjZW50IHZhbHVlIikgYW5kIGBtcm5ldmAgKCJtb3N0IHJlY2VudCBub24tZW1wdHkgdmFsdWUiKSBhcmd1bWVudHMgaW5zdHJ1Y3RpbmcgdGhlIGZ1bmN0aW9uIHRvIGRvd25sb2FkIHRoZSBsYXRlc3QgYXZhaWxhYmxlIGRhdGEgYXJlIGFsc28gdmVyeSB1c2VmdWw6CgpgYGB7ciBlY2hvPVRSVUV9CiMgYWRkdGlvbmFsIHVzZWZ1bCBhcmd1bWVudHMgb2Ygd2JfZGF0YQpkZiA8LSB3Yl9kYXRhKGluZGljYXRvciA9ICJOWS5HRFAuUENBUC5DRCIsIAogICAgICAgICAgICAgICMgbXJ2ID0gMSwgICAgICAgICAgICAgICAgICAgICAgICMgbnVtYmVyIG9mIG1vc3QgcmVjZW50IG9ic2VydmF0aW9ucwogICAgICAgICAgICAgICMgZ2FwZmlsbCA9IFRSVUUsICAgICAgICAgICAgICAgICMgZmlsbCBpbiBtaXNzaW5nIHZhbHVlcyBieSBjYXJyeWluZyBmb3J3YXJkIHRoZSBsYXRlc3QgYXZhaWxhYmxlIHZhbHVlCiAgICAgICAgICAgICAgbXJuZXYgPSAxKSAgICAgICAgICAgICAgICAgICAgICAgIyBudW1iZXIgb2YgbW9zdCByZWNlbnQgbm9uLWVtcHR5IG9ic2VydmF0aW9ucwoKaGVhZChkZiwgMTApCmBgYAoKSSBmb3VuZCBib3RoIGBXRElgIGFuZCBgd2JzdGF0c2AgZWFzeSB0byB1c2UgYnV0IGB3YnN0YXRzYCBwcm92aWRlcyBmZXcgbW9yZSBjb252ZW5pZW50IGZ1bmN0aW9uYWxpdGllcyB3aXRoaW4gaXRzIG1haW4gZnVuY3Rpb24gZm9yIGRvd25sb2FkaW5nIGRhdGEgKGUuZy4sIGZvciBkZWFsaW5nIHdpdGggbWlzc2luZyBvYnNlcnZhdGlvbnMgYW5kIGZpbGxpbmcgdGhlIGdhcHMpLgoKIyMgT0VDRAoKU2ltaWxhciB0byB0aGUgcGFja2FnZXMgYWJvdmUsIHRoZSBbT0VDRF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL09FQ0QvKSBwYWNrYWdlIGhhcyB3aXRoIGEgZnVuY3Rpb24gZm9yIGRvd25sb2FkaW5nIGFuZCBsb29raW5nIHVwIHRoZSBsaXN0IG9mIGF2YWlsYWJsZSBkYXRhIHNlcmllczoKCmBgYHtyLCBldmFsID0gVFJVRX0KIyBsb2FkIHRoZSBwYWNrYWdlCmxpYnJhcnkoT0VDRCkKCiMgbG9vayB1cCB0aGUgbGlzdCBvZiBpbmRpY2F0b3JzCm9lY2RfZGF0YV9saXN0IDwtIGdldF9kYXRhc2V0cygpCmhlYWQob2VjZF9kYXRhX2xpc3QsIDEwKQpgYGAKCllvdSBjYW4gdXNlIHRoZSBJRCBvZiBhIGRhdGFzZXQgdG8gbG9vayB1cCBpdHMgc3RydWN0dXJlIHVzaW5nIHRoZSBgZ2V0X2RhdGFfc3RydWN0dXJlYCBmdW5jdGlvbjoKYGBge3IsIGV2YWwgPSBUUlVFfQpkYXRhX3N0cnVjdHVyZSA8LSBnZXRfZGF0YV9zdHJ1Y3R1cmUoZGF0YXNldCA9ICJTTkFfVEFCTEUxIikKYGBgCgpUaGVzZSBkYXRhIHN0cnVjdHVyZSBvYmplY3RzIGFyZSB0aGUgbGlzdCBvZiBkYXRhIGZyYW1lcyBlYWNoIGRvY3VtZW50aW5nIGRpZmZlcmVudCBkaW1lbnNpb25zIG9mIHRoZSBkYXRhc2V0IChlLmcuLCB0aGUgbGlzdCBvZiBhdmFpbGFibGUgaW5kaWNhdG9ycywgZnJlcXVlbmN5LCB1bml0IG9mIG1lYXN1cmUgYW5kIG90aGVycykuIEVsZW1lbnRzIG9mIHRoaXMgaW5mb3JtYXRpb24gd2lsbCBiZSBuZWNlc3NhcnkgZm9yIGRvd25sb2FkaW5nIHRoZSBhY3R1YWwgZGF0YS4gYGdldF9kYXRhc2V0KClgIGlzIHRoZSBtYWluIGZ1bmN0aW9uIGZvciBkb3dubG9hZGluZyB0aGUgZGF0YSBzZXJpZXMgYW5kIHlvdSB3aWxsIG5lZWQgdG8gdXNlIGl0cyBgZmlsdGVyYCBhcmd1bWVudCB0byBpbmRpY2F0ZSBzcGVjaWZpYyBkaW1lbnNpb25zIHRvIGN1c3RvbWl6ZSB0aGUgZGF0YSByZXF1ZXN0LiBZb3UgY2FuIHJ1biB0aGlzIGZ1bmN0aW9uIHdpdGggbm8gaW5wdXQgdG8gdGhlIGBmaWx0ZXJgIGFyZ3VtZW50IGFuZCB0aGUgd2hvbGUgZGF0YXNldCB3aWxsIGJlIGRvd25sb2FkZWQgaW4gdGhpcyBjYXNlLiBCdXQsIGxldCdzIHNheSwgeW91IHdhbnQgdG8gZG93bmxvYWQgb25seSBkYXRhIGZvciBSdXNzaWFuIEdEUCAoZnJvbSB0aGUgIlF1YXJ0ZXJseSBOYXRpb25hbCBBY2NvdW50cyIgZGF0YXNldCkgYmV0d2VlbiAyMDAwIGFuZCAyMDIwOgoKYGBge3IsIGV2YWwgPSBUUlVFfQojIGNyZWF0ZSB0aGUgbGlzdCBvZiBmaWx0ZXJzCmRhdGFfZmlsdGVyIDwtIGxpc3QoImNvdW50cnkiID0gIlJVUyIsIAogICAgICAgICAgICAgICAgICAgICJ0cmFuc2FjdGlvbiIgPSAiQjFfR0EiLCAKICAgICAgICAgICAgICAgICAgICAibWVhc3VyZSIgPSAiViIpCgojIGRvd25sb2FkIHRoZSBkYXRhCmRmIDwtIGdldF9kYXRhc2V0KGRhdGFzZXQgPSAiU05BX1RBQkxFMSIsIAogICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBkYXRhX2ZpbHRlciwKICAgICAgICAgICAgICAgICAgc3RhcnRfdGltZSA9IDIwMDAsIAogICAgICAgICAgICAgICAgICBlbmRfdGltZSA9IDIwMjApCgpoZWFkKGRmKQpgYGAKClNvbWUgZGF0YXNldHMgYWxsb3cgZmlsdGVyaW5nIHVzaW5nIGFkZGl0aW9uYWwgZGltZW5zaW9ucyAoZS5nLiwgZnJlcXVlbmN5KToKCmBgYHtyLCBldmFsID0gVFJVRX0KIyBjcmVhdGUgdGhlIGxpc3Qgb2YgZmlsdGVycwpkYXRhX2ZpbHRlciA8LSBsaXN0KCJjb3VudHJ5IiA9ICJSVVMiLCAKICAgICAgICAgICAgICAgICAgICAidHJhbnNhY3Rpb24iID0gIkIxX0dFIiwKICAgICAgICAgICAgICAgICAgICAibWVhc3VyZSIgPSAiTE5CUVJTQSIsIAogICAgICAgICAgICAgICAgICAgICJmcmVxdWVuY3kiID0gIlEiKQoKIyBkb3dubG9hZCB0aGUgZGF0YQpkZiA8LSBnZXRfZGF0YXNldChkYXRhc2V0ID0gIlFOQSIsIAogICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBkYXRhX2ZpbHRlciwKICAgICAgICAgICAgICAgICAgc3RhcnRfdGltZSA9IDIwMDUsIAogICAgICAgICAgICAgICAgICBlbmRfdGltZSA9IDIwMjApCmBgYAoKT3JkZXJpbmcgb2YgZmlsdGVycyBpbXBsaWVkIGJ5IHRoZSBkYXRhc2V0IHNob3VsZCBiZSByZXNwZWN0ZWQgd2hlbiBjcmVhdGluZyB0aGUgY3VzdG9taXplZCBsaXN0IG9mIGZpbHRlcnMuIEZvciBpbnN0YW5jZSwgc3BlY2lmeWluZyB0aGUgdmFyaWFibGUgbmFtZSAoYEIxX0dFYCkgaW4gdGhlIGxpc3QgYWJvdmUgYWZ0ZXIgdGhlIGBmcmVxdWVuY3lgIGVsZW1lbnQgd2lsbCByZXN1bHQgaW4gYW4gZXJyb3IsIHNpbmNlIHRoZSBsaXN0IG9mIGZpbHRlcnMgd2lsbCBub3QgY29ycmVzcG9uZCB0byB0aGUgc3RydWN0dXJlIG9mIHRoZSBgUU5BYCBkYXRhc2V0LiBJbiBhZGRpdGlvbiwgc29tZSBmaWx0ZXJzIG1heSBiZSBtYW5kYXRvcnksIHdoaWxlIG90aGVycyBhcmUgb3B0aW9uYWwuIEluIHRoZSBleGFtcGxlIGFib3ZlLCBsZWF2aW5nIG91dCB0aGUgaW5kaWNhdG9yIG5hbWUgKGBCMV9HRWApIGJ1dCByZXRhaW5pbmcgdGhlIG90aGVyIGZpbHRlcnMgd2lsbCByZXN1bHQgaW4gYW4gZXJyb3IuIEF0IHRoZSBzYW1lIHRpbWUsIGxlYXZpbmcgb3V0IHRoZSBtZWFzdXJlIGFuZCBmcmVxdWVuY3kgZmlsdGVycyAoYExOQlFSU0FgIGFuZCBgUWApIGFsbG93cyBkb3dubG9hZGluZyB0aGUgZGF0YSBzZXJpZXMgYnV0IHdpdGggYWxsIHRoZSBhdmFpbGFibGUgbWVhc3VyZXMgYW5kIGZyZXF1ZW5jaWVzLgoKRmlndXJpbmcgb3V0IHRoZSByZWxldmFudCBmaWx0ZXJzIGFuZCB0aGVpciBjb3JyZWN0IG9yZGVyaW5nIHRocm91Z2ggdGhlIGRvd25sb2FkZWQgZGF0YSBzdHJ1Y3R1cmUgb2JqZWN0IGlzIG5vdCBzdHJhaWdodGZvcndhcmQuIEFuIGVhc2llciB3YXkgdG8gdW5kZXJzdGFuZCB3aGljaCBmaWx0ZXJzIGFyZSB1c2VkIHRvIHByb2R1Y2UgYSBnaXZlbiBzZXJpZXMgaXMgdG8gbG9vayB1cCB0aGUgU0RNWCBleHByZXNzaW9uIG9mIHRoZSBzZXJpZXMgKG9yIG9mIHRoZSBncm91cCBvZiBkYXRhIHNlcmllcykgb24gdGhlIFtPRUNEIHdlYnNpdGVdKGh0dHBzOi8vc3RhdHMub2VjZC5vcmcvSW5kZXguYXNweD9EYXRhU2V0Q29kZT1RTkEjKSBhZnRlciBhcHBseWluZyB0aGUgbmVjZXNzYXJ5IGZpbHRlcnMuIFRoZSBTRE1YIGV4cHJlc3Npb24gZXF1aXZhbGVudCB0byB0aGUgbGlzdCBvZiBmaWx0ZXJzIGFib3ZlIGxvb2tzIGxpa2UgdGhpcyAoZ28gdG8gYEV4cG9ydCAtPiBTRE1YIChYTUwpIC0+IFNETVggREFUQSBVUkxgIG9uIFt0aGlzIHBhZ2VdKGh0dHBzOi8vc3RhdHMub2VjZC5vcmcvSW5kZXguYXNweD9EYXRhU2V0Q29kZT1RTkEjKSB0byBsb29rIHVwIHRoaXMgZXhwcmVzc2lvbik6Cgo+IGh0dHBzOi8vc3RhdHMub2VjZC5vcmcvcmVzdHNkbXgvc2RteC5hc2h4L0dldERhdGEvUU5BL1JVUy5CMV9HRS5MTkJRUlNBLlEvYWxsP3N0YXJ0VGltZT0yMDA1LVExJmVuZFRpbWU9MjAyMS1RMwoKU2VhcmNoaW5nIGZvciBkYXRhc2V0cyB1c2luZyBhIHNlYXJjaCB0ZXJtIGNhbiBiZSBkb25lIHVzaW5nIHRoZSBgc2VhcmNoX2RhdGFzZXQoKWAgZnVuY3Rpb246CgpgYGB7ciwgZWNobyA9IFRSVUV9CnNlYXJjaF9kYXRhc2V0KCJRTkEiLCBvZWNkX2RhdGFfbGlzdCkKYGBgCgojIyBVTiBDb210cmFkZQoKW1VOIENvbXRyYWRlXShodHRwczovL2NvbXRyYWRlLnVuLm9yZy8pIGlzIG9uZSBvZiBmZXcgc291cmNlcyBvZiBkZXRhaWxlZCBkYXRhIG9uIGludGVybmF0aW9uYWwgdHJhZGUgZmxvd3MuIFRoZXJlIGFyZSBzZXZlcmFsIG9wdGlvbnMgZm9yIGFjY2Vzc2luZyB0aGlzIGRhdGEgYW5kIEkgd2lsbCByZXZpZXcgW2NvbXRyYWRyXShodHRwczovL2Nsb3VkLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2NvbXRyYWRyL3ZpZ25ldHRlcy9jb210cmFkci12aWduZXR0ZS5odG1sKSBhbmQgW3RyYWRlc3RhdGlzdGljc10oaHR0cHM6Ly9kb2NzLnJvcGVuc2NpLm9yZy90cmFkZXN0YXRpc3RpY3MvaW5kZXguaHRtbCkgcGFja2FnZXMgZm9yIGRvaW5nIHRoYXQuIFtVTiBDb210cmFkZSBBUEldKGh0dHBzOi8vY29tdHJhZGUudW4ub3JnL0RhdGEvRG9jL2FwaS9leC9yKSBwYWdlIGFsc28gc2hvd3MgaG93IHRvIGRvd25sb2FkIHRoZWlyIGRhdGEgdXNpbmcgYSB1c2VyLWRlZmluZWQgUiBmdW5jdGlvbi4KCiMjIyBbYGNvbXRyYWRyYF0oaHR0cHM6Ly9jbG91ZC5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9jb210cmFkci92aWduZXR0ZXMvY29tdHJhZHItdmlnbmV0dGUuaHRtbCkKCmBjdF9zZWFyY2hgIGlzIHRoZSBtYWluIGZ1bmN0aW9uIGluIHRoaXMgcGFja2FnZSBmb3IgZG93bmxvYWRpbmcgZGF0YToKCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9RkFMU0V9CiMgbG9hZCB0aGUgcGFja2FnZQpsaWJyYXJ5KGNvbXRyYWRyKQoKIyBkb3dubG9hZCB0aGUgZGF0YQpkZiA8LSBjdF9zZWFyY2gocmVwb3J0ZXJzID0gIkthemFraHN0YW4iLCAKICAgICAgICAgICAgICAgIHBhcnRuZXJzID0gIkFsbCIsIAogICAgICAgICAgICAgICAgdHJhZGVfZGlyZWN0aW9uID0gImV4cG9ydHMiLAogICAgICAgICAgICAgICAgZnJlcSA9ICJhbm51YWwiLCAKICAgICAgICAgICAgICAgIHN0YXJ0X2RhdGUgPSAyMDE2LCAKICAgICAgICAgICAgICAgIGVuZF9kYXRlID0gMjAyMCkKCiMgcGxvdApnZ3Bsb3QoZGZbZGYkcGFydG5lciA9PSAiV29ybGQiLCBdLCBhZXMoeCA9IHBlcmlvZCwgeSA9IHRyYWRlX3ZhbHVlX3VzZC8xMF42LCBncm91cCA9IDEpKSArIAogICAgZ2VvbV9saW5lKCkKYGBgCgpPbmUgbGltaXRhdGlvbiBvZiB0aGUgVU4gQ29tdHJhZGUgQVBJIGlzIHRoYXQgb25lIGNhbiBkb3dubG9hZCBubyBtb3JlIHRoYW4gZml2ZSBjb25zZWN1dGl2ZSB5ZWFycyBvciBtb250aHMgb2YgZGF0YSBpbiBhIHNpbmdsZSByZXF1ZXN0IGlmIGVpdGhlciB0aGUgYHJlcG9ydGVyc2Agb3IgYHBhcnRuZXJzYCBhcmd1bWVudCBpbmNsdWRlcyBgImFsbCJgIChzZWUgW2hlcmVdKGh0dHBzOi8vY29tdHJhZGUudW4ub3JnL2RhdGEvZG9jL2FwaS8jTGltaXRzKSBmb3IgbW9yZSBkZXRhaWxzKS4gQnV0IGl0J3Mgbm90IGFsbCB0aGF0IGhvcGVsZXNzLiBJZiB5b3UgbmVlZCB0byBkb3dubG9hZCB0aGUgZGF0YSBmb3IgbW9yZSB0aGFuIGZpdmUgeWVhcnMgKG9yIGFsbCB0aGUgYXZhaWxhYmxlIHllYXJzKSBhdCBvbmNlLCB0aGVuIHlvdSB3aWxsIG5lZWQgdG8gYXBwbHkgdGhlIHJlc3RyaWN0aW9ucyBvZiB1cCB0byBmaXZlIGVsZW1lbnRzIHRvIGVpdGhlciB0aGUgYHJlcG9ydGVyc2Agb3IgYHBhcnRuZXJzYCBhcmd1bWVudHMgKGkuZS4sIHRoZXkgc2hvdWxkIGJlIGEgY2hhcmFjdGVyIHZlY3RvciBvZiBjb3VudHJ5IG5hbWVzIG9mIGxlbmd0aCBmaXZlIG9yIGZld2VyKS4gRm9yIGluc3RhbmNlLCB0aGlzIHF1ZXJ5IHdpbGwgd29yayBhbmQgZG93bmxvYWQgZGF0YSBmcm9tIDE5OTUgKGVhcmxpZXN0IGF2YWlsYWJsZSkgdG8gMjAyMCBmb3IgYSBzaW5nbGUgcmVwb3J0aW5nIGNvdW50cnkgYW5kIGZpdmUgb2YgaXRzIHRyYWRpbmcgcGFydG5lcnM6CgpgYGB7ciBlY2hvPVRSVUV9CmRmIDwtIGN0X3NlYXJjaChyZXBvcnRlcnMgPSAiS2F6YWtoc3RhbiIsIAogICAgICAgICAgICAgICAgcGFydG5lcnMgPSBjKCJSdXNzaWFuIEZlZGVyYXRpb24iLCAiVWtyYWluZSIsICJCZWxhcnVzIiwgIlV6YmVraXN0YW4iLCAiQXJtZW5pYSIpLCAKICAgICAgICAgICAgICAgIHRyYWRlX2RpcmVjdGlvbiA9ICJleHBvcnRzIiwKICAgICAgICAgICAgICAgIGZyZXEgPSAiYW5udWFsIikKYGBgCgpPbiB0aGUgb3RoZXIgaGFuZCwgdGhpcyBxdWVyeSB3aWxsIHJldHVybiBhbiBlcnJvciwgYmVjYXVzZSB0aGUgYHBhcnRuZXJzYCBhcmd1bWVudCBpbmNsdWRlcyBhIGNoYXJhY3RlciB2ZWN0b3Igb2YgbGVuZ3RoIGdyZWF0ZXIgdGhhbiBmaXZlKToKCmBgYHtyIGV2YWw9RkFMU0V9CmRmIDwtIGN0X3NlYXJjaChyZXBvcnRlcnMgPSAiS2F6YWtoc3RhbiIsIAogICAgICAgICAgICAgICAgcGFydG5lcnMgPSBjKCJSdXNzaWFuIEZlZGVyYXRpb24iLCAiVWtyYWluZSIsICJCZWxhcnVzIiwgIlV6YmVraXN0YW4iLCAiQXJtZW5pYSIsICJBemVyYmFpamFuIiksIAogICAgICAgICAgICAgICAgdHJhZGVfZGlyZWN0aW9uID0gImV4cG9ydHMiLAogICAgICAgICAgICAgICAgZnJlcSA9ICJhbm51YWwiKQpgYGAKCk1ha2luZyBhIHJlcXVlc3Qgd2l0aCBtdWx0aXBsZSByZXBvcnRlcnMgYW5kIHBhcnRuZXJzIHdvcmtzIHdlbGwgdG9vOgoKYGBge3IgZWNobz1UUlVFfQpkZiA8LSBjdF9zZWFyY2gocmVwb3J0ZXJzID0gYygiS2F6YWtoc3RhbiIsICJVemJla2lzdGFuIiksIAogICAgICAgICAgICAgICAgcGFydG5lcnMgPSBjKCJSdXNzaWFuIEZlZGVyYXRpb24iLCAiVWtyYWluZSIsICJCZWxhcnVzIiwgIkFybWVuaWEiKSwgCiAgICAgICAgICAgICAgICB0cmFkZV9kaXJlY3Rpb24gPSAiZXhwb3J0cyIsCiAgICAgICAgICAgICAgICBmcmVxID0gImFubnVhbCIpCmBgYAoKIyMjIFtgdHJhZGVzdGF0aXN0aWNzYF0oaHR0cHM6Ly9kb2NzLnJvcGVuc2NpLm9yZy90cmFkZXN0YXRpc3RpY3MvaW5kZXguaHRtbCkKClRoZSBbYHRyYWRlc3RhdGlzdGljc2BdKGh0dHBzOi8vZG9jcy5yb3BlbnNjaS5vcmcvdHJhZGVzdGF0aXN0aWNzL2luZGV4Lmh0bWwpIHBhY2thZ2UgaXMgYSBnb29kIGFsdGVybmF0aXZlIHRvIGBjb210cmFkZXJgIGZvciBhY2Nlc3NpbmcgdGhlIFVOIENvbXRyYWRlIGRhdGEuIFRoZSBwYWNrYWdlJ3MgYXV0aG9yIG1ha2VzIHRoZSBzYW1lIFVOIENvbXRyYWRlIGRhdGEgYXZhaWxhYmxlIHRocm91Z2ggdGhlaXIgb3duIGluZnJhc3RydWN0dXJlIHdpdGggdGhlIGFkZGVkIGJlbmVmaXRzIG9mIHRoZSBkYXRhIGJlaW5nIHByb3ZpZGVkIGluIHRoZSBbInRpZHkiIGZvcm1hdF0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei90aWR5LWRhdGEuaHRtbCksIHdpdGggYWRkaXRpb25hbCBwcm9jZXNzaW5nLCBleHRyYSB1dGlsaXR5IGZ1bmN0aW9ucyBhbmQgZmV3ZXIgdXNhZ2UgbGltaXRhdGlvbnMgb24gdGhlIEFQSS4gYG90c19jcmVhdGVfdGlkeV9kYXRhYCBpcyB0aGUgbWFpbiBmdW5jdGlvbiBmb3IgZG93bmxvYWRpbmcgdGhlIGRhdGEuIFRoZSBmdW5jdGlvbiBkb2VzIG5vdCBoYXZlIGFuIG9wdGlvbiBmb3IgaW5kaWNhdGluZyB0aGUgZGlyZWN0aW9uIG9mIHRyYWRlIGFuZCBpdCBkb3dubG9hZHMgYm90aCBleHBvcnQgYW5kIGltcG9ydCBkYXRhLgoKYGBge3IgZXZhbD1UUlVFLCByZXN1bHRzPSdoaWRlJ30KIyBsb2FkIHRoZSBwYWNrYWdlCmxpYnJhcnkodHJhZGVzdGF0aXN0aWNzKQoKIyBkb3dubG9hZCB0aGUgZGF0YQpkZiA8LSBvdHNfY3JlYXRlX3RpZHlfZGF0YSh5ZWFycyA9IDE5OTU6MjAxOSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcG9ydGVycyA9ICJrYXoiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFydG5lcnMgPSBjKCJydXMiLCAidWtyIiwgImJsciIsICJ1emIiLCAiYXJtIiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb21tb2RpdGllcyA9ICJhbGwiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFibGUgPSAieXJwYyIpCmBgYAoKSXQncyBwb3NzaWJsZSB0byBxdWVyeSBkYXRhIGZvciBtdWx0aXBsZSByZXBvcnRlciBjb3VudHJpZXMgYXQgdGhlIHNhbWUgdGltZToKCmBgYHtyIGV2YWw9VFJVRSwgcmVzdWx0cz0naGlkZSd9CiMgZGF0YSBhdCBhZ2dyZWdhdGVkIGxldmVsICh5ZWFyIC0gcmVwb3J0ZXIgLSBwYXJ0bmVyKQpkZiA8LSBvdHNfY3JlYXRlX3RpZHlfZGF0YSh5ZWFycyA9IDIwMTg6MjAxOSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcG9ydGVycyA9IGMoImtheiIsICJ1emIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFydG5lcnMgPSBjKCJydXMiLCAidWtyIiwgImJsciIsICJhcm0iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tbW9kaXRpZXMgPSAiYWxsIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhYmxlID0gInlycCIpCgpoZWFkKGRmKQpgYGAKVGhlIHBhY2thZ2UgcHJvdmlkZXMgYWNjZXNzIHRvIGEgbnVtYmVyIG9mIHRhYmxlcyB0aHJvdWdoIHRoZSBBUEkuIFlvdSBjYW4gbGlzdCB0aGVtIGJ5IGNhbGxpbmcgdGhlIGBvdHNfdGFibGVzYCBvYmplY3QgaW4gdGhlIGNvbnNvbGUuCgpgYGB7ciBlY2hvPVRSVUUsIHBhZ2VkLnByaW50PVRSVUV9CiMgbG9vayB1cCB0aGUgbGkKb3RzX3RhYmxlcwpgYGAKCiMjIEV1cm9zdGF0CgpUaGVyZSBpcyBhIHZlcnkgY29udmVuaWVudCBbZXVyb3N0YXRdKGh0dHBzOi8vcm9wZW5nb3YuZ2l0aHViLmlvL2V1cm9zdGF0L2luZGV4Lmh0bWwpIHBhY2thZ2UgZm9yIGRvd25sb2FkaW5nIGRhdGEgZnJvbSBbRXVyb3N0YXRdKGh0dHBzOi8vZWMuZXVyb3BhLmV1L2V1cm9zdGF0KS4gWW91IGNhbiB1c2UgdGhlIGBnZXRfZXVyb3N0YXRfdG9jYCBmdW5jdGlvbiBmb3IgbG9va2luZyB1cCB0aGUgbGlzdCBvZiBhbGwgdGhlCmRhdGFzZXRzIGFuZCB0aGUgYHNlYXJjaF9ldXJvc3RhdGAgZnVuY3Rpb24gZm9yIHNlYXJjaGluZyBkYXRhc2V0cyBiYXNlZCBvbiBhIHNlYXJjaCB0ZXJtLgoKYGBge3IgZXZhbCA9IFRSVUV9CiMgbG9hZCB0aGUgcGFja2FnZQpsaWJyYXJ5KCJldXJvc3RhdCIpCgojIGdldCB0aGUgbGlzdCBvZiBhbGwgZXVyb3N0YXQgZGF0YXNldHMKZGYgPC0gZ2V0X2V1cm9zdGF0X3RvYygpCmhlYWQoZGYpCmBgYAoKU2VhcmNoaW5nIGZvciByZWxldmFudCBkYXRhc2V0cyB1c2luZyBhIHNlYXJjaCB0ZXJtOgoKYGBge3IgZWNobz1UUlVFfQojIHNlYXJjaCBmb3IgcmVsZXZhbnQgZGF0YXNldHMKcmVzIDwtIHNlYXJjaF9ldXJvc3RhdCgiZXhwb3J0cyIsIHR5cGUgPSAidGFibGUiKQpoZWFkKHJlcykKYGBgCgpBZnRlciBsb2NhdGluZyBhIG5lY2Vzc2FyeSBkYXRhc2V0LCB5b3Ugd2lsbCBuZWVkIGl0cyBJRCB0byBkb3dubG9hZCB0aGUgZGF0YSB1c2luZyB0aGUgYGdldF9ldXJvc3RhdGAgZnVuY3Rpb246CgpgYGB7ciBlY2hvPVRSVUUsIHJlc3VsdHM9J2hpZGUnfQojIGRvd25sb2FkIHRoZSBkYXRhCmRmIDwtIGdldF9ldXJvc3RhdChpZCA9ICJlaV9ic2NvX20iLAogICAgICAgICAgICAgICAgICAgdGltZV9mb3JtYXQgPSAiZGF0ZSIsCiAgICAgICAgICAgICAgICAgICBmaWx0ZXJzID0gIm5vbmUiLAogICAgICAgICAgICAgICAgICAgdHlwZSA9ICJjb2RlIiwKICAgICAgICAgICAgICAgICAgIHNlbGVjdF90aW1lID0gTlVMTCwKICAgICAgICAgICAgICAgICAgIGNhY2hlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgIHVwZGF0ZV9jYWNoZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgY2FjaGVfZGlyID0gTlVMTCwKICAgICAgICAgICAgICAgICAgIGNvbXByZXNzX2ZpbGUgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAga2VlcEZsYWdzID0gRkFMU0UpCmBgYAoKVGhlIGFib3ZlIHJlcXVlc3QgZG93bmxvYWRzIHRoZSB3aG9sZSBkYXRhc2V0LiBZb3UgY2FuIHVzZSB0aGUgYGZpbHRlcnNgIGFyZ3VtZW50IHRvIGZpbHRlciB0aGUgZGF0YXNldCBkb3duIHRvIGEgc21hbGxlciBzZXQ6CgpgYGB7ciBlY2hvPVRSVUV9CiMgZnJvbSB0aGUgZGF0YXNldCBvbiBidXNpbmVzcy9jb25zdW1lciBzdXJ2ZXlzLCBkb3dubG9hZCB0aGUgY29uc3VtZXIgY29uZmlkZW5jZSBpbmRpY2F0b3IgKGluZGljID0gIkJTLUNTTUkiKSwgc2Vhc29uYWxseSBhZGp1c3RlZCAoc19hZGogPSAiU0EiKSBhbmQgb25seSBmb3IgQXVzdHJpYSAoZ2VvID0gIkFUUSIpCmRmIDwtIGdldF9ldXJvc3RhdChpZCA9ICJlaV9ic2NvX20iLAogICAgICAgICAgICAgICAgICAgdGltZV9mb3JtYXQgPSAiZGF0ZSIsCiAgICAgICAgICAgICAgICAgICBmaWx0ZXJzID0gbGlzdChpbmRpYyA9ICJCUy1DU01DSSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzX2FkaiA9ICJTQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW8gPSAiQVQiKSkKCiMgcGxvdApkZiA8LSBkZlshaXMubmEoZGYkdmFsdWVzKSwgXQpnZ3Bsb3QoZGYsIGFlcyh4ID0gdGltZSwgeSA9IHZhbHVlcywgZ3JvdXAgPSAxKSkgKyBnZW9tX2xpbmUoKQpgYGAKCk9uZSB2ZXJ5IHVzZWZ1bCBmdW5jdGlvbiBpbiB0aGlzIHBhY2thZ2UgaXMgYGxhYmVsX2V1cm9zdGF0YC4gVGhlIGZ1bmN0aW9uIGFsbG93cyB0byBxdWlja2x5IGRvd25sb2FkIHRoZSBsYWJlbHMgYXNzb2NpYXRlZCB3aXRoIHRoZSBkYXRhIHRhYmxlIChpLmUuLCB0aGUgb25lIHRoYXQgd2FzIGluaXRpYWxseSBkb3dubG9hZGVkIHVzaW5nIGBnZXRfZXVyb3N0YXQoKWA6CgpgYGB7ciBlY2hvPVRSVUV9CiMgZ2V0IHRoZSBkYXRhIGxhYmVscwpsYWJlbGVkX2RmIDwtIGxhYmVsX2V1cm9zdGF0KGRmKQpoZWFkKGxhYmVsZWRfZGYpCmBgYAoKVGhlIHBhY2thZ2UgYWxzbyBwcm92aWRlcyBhIHVzZWZ1bCBmdW5jdGlvbiBmb3IgZG93bmxvYWRpbmcgdGhlIGdlb3NwYXRpYWwgZGF0YSBmcm9tIEV1cm9zdGF0IChgZ2V0X2V1cm9zdGF0X2dlb3NwYXRpYWxgKToKCmBgYHtyLCBlY2hvPVRSVUUsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA2LCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30KIyBnZXQgdGhlIGRhdGEgb24gdGhlIE5FRVQgKG5laXRoZXIgaW4gZW1wbG95bWVudCBub3IgZWR1Y2F0aW9uIGFuZCB0cmFpbmluZykgcmF0ZSBieSBOVVRTIDIgcmVnaW9ucwpkZiA8LSBnZXRfZXVyb3N0YXQoaWQgPSAiZWRhdF9sZnNlXzIyIiwgdGltZV9mb3JtYXQgPSAicmF3IikgJT4lCiAgICBkcGx5cjo6ZmlsdGVyKHNleCA9PSAiVCIsIAogICAgICAgICAgICAgICAgICBhZ2UgPT0gIlkxNS0yNCIsIAogICAgICAgICAgICAgICAgICB0aW1lID09IDIwMjAsIAogICAgICAgICAgICAgICAgICBuY2hhcihnZW8pID09IDQpICU+JQogICAgZHBseXI6Om11dGF0ZShjYXQgPSBjdXRfdG9fY2xhc3Nlcyh2YWx1ZXMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHlsZSA9ICJlcXVhbCIpKQoKIyBkb3dubG9hZCB0aGUgZ2Vvc3BhdGlhbCBkYXRhCmRhdGFfZ2VvIDwtIGdldF9ldXJvc3RhdF9nZW9zcGF0aWFsKHJlc29sdXRpb24gPSAiMjAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnV0c19sZXZlbCA9ICIyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgPSAyMDIxKQoKIyBtZXJnZQpkYXRhIDwtIGRwbHlyOjppbm5lcl9qb2luKGRhdGFfZ2VvLCBkZikKCiMgcGxvdApnZ3Bsb3QoZGF0YSA9IGRhdGEpICsKICBnZW9tX3NmKGFlcyhmaWxsID0gY2F0KSwgY29sb3IgPSAiZGltIGdyZXkiLCBzaXplID0gMC4xKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJCbHVlcyIpICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChyZXZlcnNlID0gVFJVRSwgdGl0bGUgPSAiIikpICsKICBsYWJzKHRpdGxlID0gIllvdW5nIHBlb3BsZSAoYWdlIDE1LTI0KSBuZWl0aGVyIGluIGVtcGxveW1lbnQgbm9yIGluXG4gZWR1Y2F0aW9uIGFuZCB0cmFpbmluZyAoTkVFVCksIDIwMjAsIHBlcmNlbnQiLAogICAgICAgY2FwdGlvbiA9ICJTb3VyY2U6IEV1cm9zdGF0LiIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoLjg3LCAuNzUpLCAKICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSksIAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSwgCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMCwgc2l6ZSA9IDkpKSArIAogIGNvb3JkX3NmKHhsaW0gPSBjKC0xMCwgNDUpLCB5bGltID0gYygzNSwgNzApKQpgYGAKIyMgRUNCCgpUaGVyZSBpcyBhbiBbZWNiXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZWNiL3ZpZ25ldHRlcy9lY2Jfc2R3Lmh0bWwpIHBhY2thZ2UgZm9yIGRvd25sb2FkaW5nIGRhdGEgZnJvbSB0aGUgW0VDQiBTdGF0aXN0aWNhbCBEYXRhIFdhcmVob3VzZV0oaHR0cHM6Ly9zZHcuZWNiLmV1cm9wYS5ldS9ob21lLmRvKS4gVGhlIGxpc3Qgb2YgYXZhaWxhYmxlIGRhdGFzZXRzIGNhbiBiZSBkb3dubG9hZGVkIHVzaW5nIHRoZSBgZ2V0X2RhdGFmbG93c2AgZnVuY3Rpb24gYnV0IHRoZSByZXRyaWV2ZWQgZGF0YSBmcmFtZSBpcyBxdWl0ZSBoaWdoIGxldmVsIGFuZCBpZGVudGlmaWNhdGlvbiBvZiBzcGVjaWZpYyBkYXRhIHNlcmllcyB3aWxsIHJlcXVpcmUgZ29pbmcgdGhyb3VnaCB0aGUgRUNCJ3MgW2RhdGEgd2Vic2l0ZV0oaHR0cHM6Ly9zZHcuZWNiLmV1cm9wYS5ldS9ob21lLmRvKSBpdHNlbGYuIFRoZSBwYWNrYWdlIGRvZXNuJ3QgaGF2ZSBhIGZ1bmN0aW9uIGZvciBsb29raW5nIHVwIGFuZCBzZWFyY2hpbmcgZm9yIHNwZWNpZmljIHNlcmllcyAod2hpY2ggbG9va3MgdG8gYmUgdGhlIHJlc3VsdCBvZiBzdWNoIGZ1bmN0aW9uYWxpdHkgbWlzc2luZyBpbiB0aGUKW0FQSV0oaHR0cHM6Ly9zZHctd3NyZXN0LmVjYi5ldXJvcGEuZXUvaGVscC8pKSBpdHNlbGYuCgpgYGB7ciwgZWNobz1UUlVFLCByZXN1bHRzPSdoaWRlJ30KIyBsb2FkIHRoZSBwYWNrYWdlCmxpYnJhcnkoZWNiKQoKIyByZXRyaWV2ZSB0aGUgbGlzdCBvZiBkYXRhc2V0cyAocmVmZXJyZWQgdG8gYXMgImRhdGEgZmxvd3MiKQpoZWFkKGdldF9kYXRhZmxvd3MoKSkKYGBgCgpEYXRhIHNlcmllcyBjYW4gYmUgZG93bmxvYWRlZCB1c2luZyB0aGUgYGdldF9kYXRhYCBmdW5jdGlvbiAoSSBsb29rZWQgdXAgdGhlIHNlcmllcyBga2V5YCBvciBJRCBvbiBbdGhpcyBwYWdlXShodHRwczovL3Nkdy5lY2IuZXVyb3BhLmV1L2Jyb3dzZS5kbz9ub2RlPTk2ODk2ODYpKToKCmBgYHtyIGVjaG89VFJVRX0KIyBkb3dubG9hZCB0aGUgZGF0YQpkZiA8LSBnZXRfZGF0YShrZXkgPSAiQ0lTUy5ELlUyLlowWi40Ri5FQy5TU19DSS5JRFgiLCAKICAgICAgICAgICAgICAgZmlsdGVyID0gbGlzdChzdGFydFBlcmlvZCA9ICIyMDAwLTAxLTAxIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kUGVyaW9kID0gIjIwMjAtMTItMzEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHVwZGF0ZUFmdGVyID0gIjIwMDAtMDEtMDEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZpcnN0Tk9ic2VydmF0aW9ucyA9IDEyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGxhc3ROT2JzZXJ2YXRpb25zID0gMTIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRldGFpbCA9ICJmdWxsIikpCgojIHBsb3QKZ2dwbG90KGRmLCBhZXMoeCA9IGFzLkRhdGUob2JzdGltZSksIHkgPSBvYnN2YWx1ZSkpICsgCiAgICBnZW9tX2xpbmUoKSArIAogICAgbGFicyh0aXRsZSA9ICJDb21wb3NpdGUgSW5kaWNhdG9yIG9mIFN5c3RlbWljIFN0cmVlc3MsIEV1cm8gYXJlYSwgaW5kZXgiLCAKICAgICAgICAgeCA9IE5VTEwsIAogICAgICAgICB5ID0gTlVMTCkKYGBgCgojIyBGUkVECgpUaGVyZSBhcmUgYSBmZXcgUiBwYWNrYWdlcyBmb3IgZG93bmxvYWRpbmcgZGF0YSBmcm9tIFtGUkVEXShodHRwczovL2ZyZWQuc3Rsb3Vpc2ZlZC5vcmcvKSAoZS5nLiwgW2BmcmVkcmBdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9mcmVkci8pLCBbYEZyZWRSYF0oaHR0cHM6Ly9naXRodWIuY29tL2pjaXplbC9GcmVkUiksIFtgYWxmcmVkYF0oaHR0cHM6Ly9naXRodWIuY29tL29ubm9rbGVlbi9hbGZyZWQpKS4gSSB3aWxsIGJyaWVmbHkgcmV2aWV3IG9ubHkgYGZyZWRyYCwgd2hpY2ggYXBwZWFycyB0byBiZSBxdWl0ZSBjb21wcmVoZW5zaXZlIGFuZCBpdCBpcyBzdGlsbCBtYWludGFpbmVkIChhdCB0aGUgdGltZSBvZiB3cml0aW5nKS4gVG8gdXNlIHRoZSBGUkVEIEFQSSwgeW91IG5lZWQgdG8gb2J0YWluIHRoZSBGUkVEIFtBUEkga2V5XShodHRwczovL2ZyZWQuc3Rsb3Vpc2ZlZC5vcmcvZG9jcy9hcGkvYXBpX2tleS5odG1sKSB0aGF0IGFsbG93cyB0byBhY2Nlc3MgdGhlIGRhdGFiYXNlLiBgZnJlZHJgIGlzIHRoZSBtYWluIGZ1bmN0aW9uIGZvciBkb3dubG9hZGluZyB0aGUgZGF0YSBzZXJpZXMuCgpgYGB7ciBlY2hvPVRSVUV9CiMgbG9hZCB0aGUgcGFja2FnZQpsaWJyYXJ5KGZyZWRyKQoKIyBzZXQgdGhlIEFQSSBrZXkgKHRoZSBhbHRlcm5hdGl2ZSBpcyB0byBhZGQgIkZSRURfQVBJX0tFWT15b3VfYXBpX2tleV9oZXJlIiB0byB5b3VyIC5SZW52aXJvbiBmaWxlKQojIGZyZWRyX3NldF9rZXkoInlvdXJfYXBpX2tleV9oZXJlIikKCiMgZG93bmxvYWQgdGhlIHRvdGFsIFVTIG5vbi1mYXJtIGVtcGxveW1lbnQgc2VyaWVzCmRmIDwtIGZyZWRyKHNlcmllc19pZCA9ICJQQVlFTVMiLAogICAgICAgICAgICBvYnNlcnZhdGlvbl9zdGFydCA9IGFzLkRhdGUoIjE5OTAtMDEtMDEiKSwKICAgICAgICAgICAgb2JzZXJ2YXRpb25fZW5kID0gYXMuRGF0ZSgiMjAyMi0wMS0wMSIpKQoKIyBwbG90CmdncGxvdChkZiwgYWVzKHggPSBkYXRlLCB5ID0gdmFsdWUpKSArIAogICAgZ2VvbV9saW5lKCkgKyAKICAgIGxhYnModGl0bGUgPSAiRW1wbG95ZWVzIG9uIG5vbi1mYXJtIHBheXJvbGxzICh0aG91c2FuZCkiKQpgYGAKClRoZSBgZnJlZHJgIChhbmQgdGhlIEZSRUQgQVBJKSBhbGxvd3MgZG93bmxvYWRpbmcgb25seSBvbmUgc2VyaWVzIGF0IGEgdGltZS4gT25lIG9wdGlvbiBmb3IgZGVhbGluZyB3aXRoIHRoaXMgbGltaXRhdGlvbiBpcyB0byBhcHBseSB0aGUgYGZyZWRyYCBmdW5jdGlvbiB0byBhIHZlY3RvciBvZiBkYXRhIHNlcmllcyBuYW1lczoKCmBgYHtyIGVjaG89VFJVRX0KIyBsb2FkIHRoZSBgcHVycnJgIHBhY2thZ2UgdG8gYWNjZXNzIHRoZSBgbWFwXypgIGZhbWlseSBvZiBmdW5jdGlvbnMKbGlicmFyeShwdXJycikKCiMgdmVjdG9yIG9mIGRhdGEgc2VyaWVzIG5hbWVzCmRhdGFfc2VyaWVzIDwtIGMoIklQRENPTkdEIiwgIklQTkNPTkdEIikKCiMgZG93bmxvYWQgdGhlIGRhdGEgdXNpbmcgdGhlIGBtYXBfZGZyYCBmdW5jdGlvbgpkZiA8LSBtYXBfZGZyKGRhdGFfc2VyaWVzLCAKICAgICAgICAgICAgICBmcmVkciwgCiAgICAgICAgICAgICAgb2JzZXJ2YXRpb25fc3RhcnQgPSBhcy5EYXRlKCIxOTkwLTAxLTAxIiksCiAgICAgICAgICAgICAgb2JzZXJ2YXRpb25fZW5kID0gYXMuRGF0ZSgiMjAyMi0wMS0wMSIpKQoKIyBwbG90CmdncGxvdChkZiwgYWVzKHggPSBkYXRlLCB5ID0gdmFsdWUsIGNvbG9yID0gc2VyaWVzX2lkKSkgKyAKICAgIGdlb21fbGluZSgpICsgCiAgICBzY2FsZV9jb2xvcl9tYW51YWwobGFiZWxzID0gYygiSW5kdXN0cmlhbCBwcm9kdWN0aW9uOiBkdXJhYmxlIGNvbnN1bWVyIGdvb2RzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSW5kdXN0cmlhbCBwcm9kdWN0aW9uOiBub24tZHVyYWJsZSBjb25zdW1lciBnb29kcyIpLCAKICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCJibHVlIiwgInJlZCIpKSArCiAgICBsYWJzKHRpdGxlID0gIkluZHVzdHJpYWwgcHJvZHVjdGlvbiIsCiAgICAgICAgIHN1YnRpdGxlID0gIkR1cmFibGUgYW5kIG5vbi1kdXJhYmxlIGNvbnN1bWVyIGdvb2RzLCAyMDE3PTEwMCIpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCAKICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpUaGUgcGFja2FnZSBhbHNvIHByb3ZpZGVzIGEgY29udmVuaWVudCBmdW5jdGlvbiBmb3Igc2VhcmNoaW5nIHRocm91Z2ggYWxsIHRoZSBGUkVEIHNlcmllcy4gCmBgYHtyIGVjaG89VFJVRX0KZGYgPC0gZnJlZHJfc2VyaWVzX3NlYXJjaF90ZXh0KHNlYXJjaF90ZXh0ID0gImNvbnN1bWVyIHByaWNlIGluZGV4IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXJfdmFyaWFibGUgPSAic2Vhc29uYWxfYWRqdXN0bWVudCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJfYnkgPSAicG9wdWxhcml0eSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGltaXQgPSA1KQoKIyBoYXZlIGEgbG9vawpoZWFkKGRmKQpgYGAKCiMjIFdoYXQgaWYgdGhlcmUgaXMgbm8gYW4gYFJgIHBhY2thZ2U/ClNvbWV0aW1lcyBhbiBBUEkgZm9yIGFjY2Vzc2luZyB0aGUgZGF0YSBpcyBhdmFpbGFibGUgYnV0IHRoZXJlIGlzIG5vIGFuIGVhc3kgdG8gdXNlIGBSYCAoQVBJIHdyYXBwZXIpIHBhY2thZ2UgZm9yIHRha2luZyBhZHZhbnRhZ2Ugb2YgdGhlIEFQSSBxdWlja2x5LiBJbiB0aGlzIGNhc2UsIHRoZSBzb2x1dGlvbiBtYXkgYmUgdG8gdXNlIGEgbnVtYmVyIG9mIHV0aWxpdHkgcGFja2FnZXMgZm9yIG1ha2luZyB3ZWIgcmVxdWVzdHMgdGhyb3VnaCBgUmAuIEluIHRoZSBicmllZiBleGFtcGxlIGJlbG93LCBJIG1ha2UgYSByZXF1ZXN0IHRvIHRoZSBBUEkgb2YgW2h0dHBzOi8vZXhjaGFuZ2VyYXRlc2FwaS5pby9dKGh0dHBzOi8vZXhjaGFuZ2VyYXRlc2FwaS5pby8pLCBhIHNlcnZpY2UgcHJvdmlkaW5nIGV4Y2hhbmdlIHJhdGVzIGRhdGEuIFRoaXMgZXhhbXBsZSBmb2xsb3dzIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIEFQSSBvZiBbaHR0cHM6Ly9leGNoYW5nZXJhdGVzYXBpLmlvL10oaHR0cHM6Ly9leGNoYW5nZXJhdGVzYXBpLmlvLykgYW5kIGRldGFpbHMgb2YgYSByZXF1ZXN0IHRvIG90aGVyIHNlcnZpY2VzIG1heSBkaWZmZXIuCgpgYGB7ciBlY2hvPVRSVUUsIGV2YWw9RkFMU0V9CiMgbG9hZCB0aGUgcGFja2FnZXMKbGlicmFyeShodHRyKSAgICAgICAgICAgIyB0b29scyBmb3Igd29ya2luZyB3aXRoIEhUVFAKbGlicmFyeShqc29ubGl0ZSkgICAgICAgIyBKU09OIHBhcnNlcgoKIyBpZGVudGlmeSB0aGUgYmFzZSBVUkwgCmJhc2VfdXJsIDwtICJodHRwOi8vYXBpLmV4Y2hhbmdlcmF0ZXNhcGkuaW8vdjEvIgoKIyB5b3VyIGFjY2VzcyBrZXkKYWNjZXNzX2tleSA8LSAieW91X2FjY2Vzc19rZXkiCgojIGFkZGl0aW9uYWwgZGlyZWN0b3J5IGluZm9ybWF0aW9uIChuZWNlc3NhcnkgZm9yIGV4Y2hhbmdlcmF0ZXNhcGkuaW8pCnBhdGggPC0gImxhdGVzdCIKCiMgYnVpbGQgdGhlIFVSTCBmb3IgbWFraW5nIGEgcmVxdWVzdCAoc3ludGF4IG9mIHRoZSBBUEkgZm9yIG90aGVyIHNlcnZpY2VzIHdpbGwgdmVyeSBsaWtlbHkgZGlmZmVyKQphcGlfdXJsIDwtIHBhc3RlKGJhc2VfdXJsLCBwYXRoLCAiPyIsICJhY2Nlc3Nfa2V5PSIsIGFjY2Vzc19rZXksIHNlcCA9ICIiKQoKIyBtYWtlIHRoZSBHRVQgcmVxdWVzdCBhbmQgY2hlY2sgdGhlIHN0YXR1cyBvZiB0aGUgcmVxdWVzdApkZjEgPC0gaHR0cjo6R0VUKHVybCA9IGFwaV91cmwpCmh0dHI6Omh0dHBfc3RhdHVzKGRmMSkKCiMgdGhlIGFsdGVybmF0aXZlIGlzIHRvIGRpcmVjdGx5IGRvd25sb2FkIGFuZCBwYXJzZSB0aGUganNvbiBmaWxlCmRmMiA8LSBqc29ubGl0ZTo6ZnJvbUpTT04odHh0ID0gYXBpX3VybCkKZGYyJHN1Y2Nlc3MKCiMgY3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIHRoZSBleGNoYW5nZSByYXRlcyBmcm9tIGRmMQoKIyB3aGF0IGlzIHRoZSBiYXNlIGN1cnJlbmN5CmRmMiRiYXNlCgojIGNvbnZlcnQgdGhlIGRvd25sb2FkZWQgZGF0YSAoaW4gJ3JhdycgZm9ybWF0KSB0byBjaGFyYWN0ZXJzIChpLmUuLCBKU09OKSBhbmQgcGFyc2UgdGhlIEpTT04KcmF0ZXMgPC0gZnJvbUpTT04ocmF3VG9DaGFyKGRmMSRjb250ZW50KSkKCiMgc2ltcGxpZnkgdGhlIGxpc3RzIG9mIGxpc3RzIHN0cnVjdHVyZQpyYXRlcyA8LSBhcy5kYXRhLmZyYW1lKHNhcHBseShyYXRlcyRyYXRlcywgYFtgKSkKaGVhZChyYXRlcykKYGBgCgpgYGB7ciBlY2hvPUZBTFNFLCBldmFsPVRSVUUsIHJlc3VsdHM9J2hpZGUnfQojIGxvYWQgdGhlIHBhY2thZ2VzCmxpYnJhcnkoaHR0cikgICAgICAgICAgICMgdG9vbHMgZm9yIHdvcmtpbmcgd2l0aCBIVFRQCmxpYnJhcnkoanNvbmxpdGUpICAgICAgICMgSlNPTiBwYXJzZXIKYGBgCgpgYGB7ciBlY2hvPUZBTFNFLCBldmFsPVRSVUV9CiMgaWRlbnRpZnkgdGhlIGJhc2UgVVJMIApiYXNlX3VybCA8LSAiaHR0cDovL2FwaS5leGNoYW5nZXJhdGVzYXBpLmlvL3YxLyIKCiMgeW91ciBhY2Nlc3Mga2V5CmFjY2Vzc19rZXkgPC0gIjkxYjVkOTkzOTM5ZjBkYzVjYzM3NGQzNjYxMzQyNzUzIgoKIyBhZGRpdGlvbmFsIGRpcmVjdG9yeSBpbmZvcm1hdGlvbiAobmVjZXNzYXJ5IGZvciBleGNoYW5nZXJhdGVzYXBpLmlvKQpwYXRoIDwtICJsYXRlc3QiCgojIGJ1aWxkIHRoZSBVUkwgZm9yIG1ha2luZyBhIHJlcXVlc3QgKHN5bnRheCBvZiB0aGUgQVBJIGZvciBvdGhlciBzZXJ2aWNlcyB3aWxsIHZlcnkgbGlrZWx5IGRpZmZlcikKYXBpX3VybCA8LSBwYXN0ZShiYXNlX3VybCwgcGF0aCwgIj8iLCAiYWNjZXNzX2tleT0iLCBhY2Nlc3Nfa2V5LCBzZXAgPSAiIikKCiMgbWFrZSB0aGUgR0VUIHJlcXVlc3QgYW5kIGNoZWNrIHRoZSBzdGF0dXMgb2YgdGhlIHJlcXVlc3QKZGYxIDwtIGh0dHI6OkdFVCh1cmwgPSBhcGlfdXJsKQpodHRyOjpodHRwX3N0YXR1cyhkZjEpCgojIHRoZSBhbHRlcm5hdGl2ZSBpcyB0byBkaXJlY3RseSBkb3dubG9hZCBhbmQgcGFyc2UgdGhlIGpzb24gZmlsZQpkZjIgPC0ganNvbmxpdGU6OmZyb21KU09OKHR4dCA9IGFwaV91cmwpCmRmMiRzdWNjZXNzCgojIGNyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCB0aGUgZXhjaGFuZ2UgcmF0ZXMgZnJvbSBkZjEKCiMgd2hhdCBpcyB0aGUgYmFzZSBjdXJyZW5jeQpkZjIkYmFzZQoKIyBjb252ZXJ0IHRoZSBkb3dubG9hZGVkIGRhdGEgKGluICdyYXcnIGZvcm1hdCkgdG8gY2hhcmFjdGVycyAoaS5lLiwgSlNPTikgYW5kIHBhcnNlIHRoZSBKU09OCnJhdGVzIDwtIGZyb21KU09OKHJhd1RvQ2hhcihkZjEkY29udGVudCkpCgojIHNpbXBsaWZ5IHRoZSBsaXN0cyBvZiBsaXN0cyBzdHJ1Y3R1cmUgaGF2ZSBhIGxvb2sgYXQgdGhlIG91dHB1dApyYXRlcyA8LSBhcy5kYXRhLmZyYW1lKHNhcHBseShyYXRlcyRyYXRlcywgYFtgKSkKaGVhZChyYXRlcykKYGBgCgoKCg==