Setup
The first thing we do is to load caladaptR and the other package we’re going to need. (If you haven’t installed these yet, see this setup script).
library(caladaptr)
library(units)
library(ggplot2)
library(dplyr)
library(sf)
library(tidyr)
library(conflicted)
conflict_prefer("filter", "dplyr", quiet = TRUE)
conflict_prefer("count", "dplyr", quiet = TRUE)
conflict_prefer("select", "dplyr", quiet = TRUE)
Part I. Retrieve County Level Data
In this section, we’ll fetch and retrieve average daily minimum temperature by year for a single county, and create a time-series plot showing the difference between RCP85 and RCP45:
Preset Areas-of-Interest
For this exercise, we need to use a Preset Area-of-Interest.
The Cal-Adapt API has a number of ‘preset’ areas-of-interest (also called boundary layers) that you can use cookie-cutter style when retrieving climate data. The advantage of using an AOI Preset is that you don’t need to import a GIS layer to query according to these common features. You just need to know the name or ID number of the feature(s) you’re interested in.
The following AOI Presets are available:
aoipreset_types
[1] "censustracts" "counties" "cdistricts" "ccc4aregions" "climregions"
[6] "hydrounits" "irwm" "electricutilities" "wecc-load-area" "evtlocations"
[11] "place"
Pro Tip:
- all of the Preset AOIs can be imported into R as sf objects with
ca_aoipreset_geom()
.
1. Find the FIPS code for Kings County
To use an AOI Preset, you need to specify which feature(s) you’re interested in by providing the value(s) for one of the id fields. The specific columns available for identifying features vary according to preset. You can view the id columns and their values for an AOI Preset using the built-in aoipreset_idval
constant. For example the counties layer allows you to specify a county by name, fips code, or id. Remember that everything in R is case-sensitive!
aoipreset_idval$counties
You can find county fips codes on Google - just be sure to use the 6-character version which includes the state. Alternately, you can View the attribute table of the counties preset layer:
# ca_aoipreset_geom("counties") %>% View()
For this example, we’ll look at Kings County (FIPS = 06031
).
2. Create the API Request
Let’s create an API request for Kings County. Note below the inclusion of ca_options()
to specify how we want to aggregate the pixels that fall within the country. This is required whenever you query polygon features.
cnty_cap <- ca_loc_aoipreset(type="counties", idfld = "fips", idval = "06031") %>%
ca_gcm(gcms[1:4]) %>%
ca_scenario(c("rcp45", "rcp85")) %>%
ca_period("year") %>%
ca_years(start = 2030, end = 2080) %>%
ca_cvar(c("tasmin")) %>%
ca_options(spatial_ag = "max")
cnty_cap
Cal-Adapt API Request
Location(s):
AOI Preset: counties
fips(s): 06031
Variable(s): tasmin
Temporal aggregration period(s): year
GCM(s): HadGEM2-ES, CNRM-CM5, CanESM2, MIROC5
Scenario(s): rcp45, rcp85
Dates: 2030-01-01 to 2080-12-31
Options:
spatial ag: max
cnty_cap %>% ca_preflight()
General issues
- none found
Issues for querying values
- none found
Issues for downloading rasters
- none found
Pro Tip:
- An API request can have more than one location. If using AOI Presets, pass multiple values to
idval
, or omit idval
completely and all features in the layer will be queried.
As before, we can plot the API request to double-check we got the right location:
plot(cnty_cap, locagrid = TRUE)
3. Fetch Data
Fetch data with ca_getvals_tbl()
:
cnty_tbl <- cnty_cap %>% ca_getvals_tbl(quiet = TRUE)
cnty_tbl %>% head()
4. Wrangle Results for Plotting
To compute the difference between RCP 8.5 and RCP 4.5, we need to split them into separate columns. This is an example of going from a ‘long’ format to a ‘wide’ format. Fortunately, the tidyr package has a function called pivot_wider
that can do this in single command. While we’re at it we’ll convert the temp to degrees Fahrenheit:
cnty_diff_rcp85_45_tbl <- cnty_tbl %>%
mutate(temp_f = set_units(val, degF)) %>%
select(fips, gcm, scenario, dt, temp_f) %>%
pivot_wider(names_from = scenario, values_from = temp_f)
head(cnty_diff_rcp85_45_tbl)
5. Plot
Now we’re ready to make the plot. Since we’re mainly interested in the trend, we’ll add a smoothing line using geom_smooth()
:
ggplot(data = cnty_diff_rcp85_45_tbl, aes(x = as.Date(dt), y = as.numeric(rcp85 - rcp45))) +
geom_line(aes(color=gcm)) +
geom_smooth(method=lm, formula = y ~ x) +
labs(title = "Difference between RCP8.5 and RCP4.5 in the Average Daily \nMinimum Temperature for Kings County", x = "year", y = "temp (F)")
This plot shows that as time goes on, the difference between RCP4.5 and RCP8.5 gets bigger and bigger.
Part II. Finding Data
Half the battle of working with climate data is finding the name of the dataset you’re interested in.
Browsing the Cal-Adapt Data Catalog
caladaptR comes with a copy of the Cal-Adapt raster series data catalog, which you can access with ca_catalog_rs()
:
# ca_catalog_rs() %>% View()
Challenge 1
How many raster datasets are there in the Cal-Adapt catalog? [Answer].
## Your answer here
Pro Tip:
- you can download a fresh copy of the Cal-Adapt raster series catalog with
ca_catalog_fetch()
Searching for a Dataset
One way you can search for datasets is to use the filter boxes above each column in the RStudio View pane. For example search for layers whose name contains the word ‘snow’.
caladaptR also has a search function ca_catalog_search()
. You can this function to find datasets using a key word or phrase. You can also use this function to see the details for a specific slug, example:
ca_catalog_search("swe_day_ens32avg_rcp45")
swe_day_ens32avg_rcp45
name: LOCA VIC daily snow water equivalent for RCP 4.5, derived from 32 LOCA models ensemble average
url: https://api.cal-adapt.org/api/series/swe_day_ens32avg_rcp45/
tres: daily
begin: 2006-01-01T00:00:00Z
end: 2098-12-31T00:00:00Z
units: mm
num_rast: 1
id: 638
xmin: -124.5625
xmax: -113.375
ymin: 31.5625
ymax: 43.75
Challenge 2
How many datasets are from gridMET? [Answer].
## Your answer here
Specifying Datasets
When you construct an API Request object, you can mix-and-match functions to specify the dataset you want:
LOCA downscaled modeled climate variables (including all Scripps) and their derivatives (i.e., VIC) can be specified using the constructor functions: ca_gcm()
+ ca_scenario()
+ ca_cvar()
+ ca_period()
.
Livneh data (observed historical variables based on spatially interpolated measurements) can be specified with ca_livneh()
+ ca_cvar()
+ ca_period()
.
everything else can be specified by slug, using ca_slug()
.
Challenge 3
What is the raster dataset with the slug tmmn_day_gridmet
? For which years is it available? What are the units? [Hint]. [Answer].
## Your answer here
In this example, we’ll explore daily projected climate data for a point location. We select the UC Lindcove Research and Extension Center (LREC), a field station in the San Joaquin Valley which has been a leading center for citrus research for decades. The trees are getting old and need to be replaced soon so its worth asking - is citrus production still going to viable in this part of California at the end of the century?
Create the API Request
Let’s start by getting 20 years worth of the daily maximum temperature for the 4 priority GCMs and 2 RCPs.
lrec_tasmax_prj_cap <- ca_loc_pt(coords = c(-119.060, 36.359), id = 1) %>%
ca_period("day") %>%
ca_gcm(gcms[1:4]) %>%
ca_scenario(c("rcp45", "rcp85")) %>%
ca_cvar("tasmax") %>%
ca_years(start = 2080, end = 2099)
lrec_tasmax_prj_cap %>% ca_preflight()
General issues
- none found
Issues for querying values
- none found
Issues for downloading rasters
- none found
plot(lrec_tasmax_prj_cap)
Fetch Data
Now we’re ready to fetch data:
lrec_tasmax_prj_tbl <- lrec_tasmax_prj_cap %>% ca_getvals_tbl(quiet = TRUE)
## backup: lrec_tasmax_prj_tbl <- readRDS("data/lrec_tasmax_prj_tbl.rds")
dim(lrec_tasmax_prj_tbl)
[1] 58440 8
head(lrec_tasmax_prj_tbl)
Create a Box Plot of the Maximum Daily Temperature Values by Month
To make a box plot, we need to first add columns for Fahrenheit, month, and year:
library(lubridate)
lrec_tasmax_prj_tmpf_tbl <- lrec_tasmax_prj_tbl %>%
mutate(temp_f = set_units(val, degF), month = month(dt), year = year(dt))
head(lrec_tasmax_prj_tmpf_tbl)
For each month, let make a box plot of the temperature values for each emission scenario, treating all GCMs as equally likely:
ggplot(lrec_tasmax_prj_tmpf_tbl, aes(x = as.factor(month), y = as.numeric(temp_f))) +
geom_boxplot() +
facet_grid(scenario ~ .) +
labs(title = "Maximum Daily Temperature by Month", x = "month", y = "temp (F)",
subtitle = "Lindcove REC, 4 GCMs combined, 2080-2099")
Count Extreme Heat Days
An extreme heat day is generally identified when the maximum temperature exceeds a threshold. The threshold can be chosen based on the historical range, or a biophysical process. For this example, we’ll select 105 °F.
Let’s count the total number of days the temperature exceeded 105 °F for each RCP. We start by adding a logical column whether the temperature exceeded our threshold.
lrec_hotday_tbl <- lrec_tasmax_prj_tmpf_tbl %>%
mutate(really_hot = (temp_f >= set_units(105, degF))) %>%
select(-spag, -val, -month)
head(lrec_hotday_tbl)
We can count the number of extreme heat days with a simple expression:
num_hot_days <- lrec_hotday_tbl %>%
group_by(scenario, really_hot) %>%
count()
num_hot_days
We can improve the readability of this table by making each scenario a separate column. This is an example of pivoting, which you can handle using tidyr::pivot_wider
.
num_hot_days %>% pivot_wider(names_from = scenario, values_from = n)
Challenge 4
Count the number of extreme heat days using a threshold of 110 Fahrenheit. Compute the number of extreme heat days per month by scenario. [Answer].
## Your answer here
Pro Tip
- To see how to count the number consecutive heat days (i.e., heat spells), see this notebook.
Visualize the Distribution of Historical Observed Daily Precipitation by Decade
Our goal here is to look at the distribution of daily precipitation by making a histogram for each decade. We’ll use observed rainfall data from Livneh.
lrec_pr_livn_cap <- ca_loc_pt(coords = c(-119.060, 36.359), id = 1) %>%
ca_livneh(TRUE) %>%
ca_period("day") %>%
ca_cvar("pr") %>%
ca_years(start = 1950, end = 2009)
lrec_pr_livn_cap %>% ca_preflight()
General issues
- none found
Issues for querying values
- none found
Issues for downloading rasters
- none found
lrec_pr_livn_tbl <- lrec_pr_livn_cap %>%
ca_getvals_tbl() %>%
rename(pr_mmday = val)
## backup: lrec_pr_prj_tbl <- readRDS("./data/lrec_pr_prj_tbl.rds")
dim(lrec_pr_livn_tbl)
[1] 21915 7
head(lrec_pr_livn_tbl)
Finally, we’ll plot histograms of the precipitation values, logged because of the highly skewed distribution.
library(lubridate)
lrec_prdec_livn_tbl <- lrec_pr_livn_tbl %>%
mutate(pr_mmday_num = as.numeric(pr_mmday),
decade = floor(year(dt) / 10) * 10) %>%
select(decade, pr_mmday_num)
ggplot(lrec_prdec_livn_tbl, aes(x=log(pr_mmday_num))) +
geom_histogram() +
facet_wrap( ~ decade)
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 17073 rows containing non-finite values (stat_bin).
LS0tDQp0aXRsZTogIkdldHRpbmcgU3RhcnRlZCB3aXRoIGNhbGFkYXB0UiINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogDQogICAgY3NzOiBodHRwczovL3VjYW5yLWlnaXMuZ2l0aHViLmlvL2NhbGFkYXB0ci1yZXMvYXNzZXRzL25iX2NzczAxLmNzcw0KICAgIGluY2x1ZGVzOg0KICAgICAgYWZ0ZXJfYm9keTogaHR0cHM6Ly91Y2Fuci1pZ2lzLmdpdGh1Yi5pby9jYWxhZGFwdHItcmVzL2Fzc2V0cy9uYl9mb290ZXIwMS5odG1sDQotLS0NCg0KIyBPdmVydmlldw0KDQpUaGlzIE5vdGVib29rIHdpbGwgZGVtb25zdHJhdGUgaG93IHlvdSBjYW4gdXNlIGNhbGFkYXB0UiB0bzoNCg0KLSBjcmVhdGUgYW4gQVBJIFJlcXVlc3QgdXNpbmcgYSBwcmVzZXQgYXJlYS1vZi1pbnRlcmVzdCAoY291bnR5KQ0KLSB1c2UgcGl2b3Rfd2lkZXIgdG8gZ28gZnJvbSBhIGxvbmcgdG8gd2lkZSBmb3JtYXQgIA0KLSBhZGQgYSB0cmVuZCBsaW5lIHRvIGEgcGxvdCAgDQotIGV4cGxvcmUgdGhlIENhbC1BZGFwdCBkYXRhIGNhdGFsb2cgIA0KLSBzcGVjaWZ5IExpdm5laCBkYXRhc2V0cyAgDQotIHdyYW5nbGUgcmVzdWx0cyBmb3IgZGlmZmVyZW50IHN1bW1hcmllcyBhbmQgdmlzdWFsaXphdGlvbnMgIA0KDQpcDQoNCiMgU2V0dXANCg0KVGhlIGZpcnN0IHRoaW5nIHdlIGRvIGlzIHRvIGxvYWQgY2FsYWRhcHRSIGFuZCB0aGUgb3RoZXIgcGFja2FnZSB3ZSdyZSBnb2luZyB0byBuZWVkLiAoSWYgeW91IGhhdmVuJ3QgaW5zdGFsbGVkIHRoZXNlIHlldCwgc2VlIHRoaXMgW3NldHVwIHNjcmlwdF0oaHR0cHM6Ly9naXRodWIuY29tL3VjYW5yLWlnaXMvY2FsYWRhcHRyLXJlcy9ibG9iL21haW4vZG9jcy93b3Jrc2hvcHMvY2FfaW50cm9fb2N0MjEvc2NyaXB0cy9jYWxhZGFwdHJfc2V0dXAuUikpLiANCg0KYGBge3IgY2h1bmswMSwgbWVzc2FnZT1UUlVFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdob2xkJ30NCmxpYnJhcnkoY2FsYWRhcHRyKQ0KbGlicmFyeSh1bml0cykNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoY29uZmxpY3RlZCkNCmNvbmZsaWN0X3ByZWZlcigiZmlsdGVyIiwgImRwbHlyIiwgcXVpZXQgPSBUUlVFKQ0KY29uZmxpY3RfcHJlZmVyKCJjb3VudCIsICJkcGx5ciIsIHF1aWV0ID0gVFJVRSkNCmNvbmZsaWN0X3ByZWZlcigic2VsZWN0IiwgImRwbHlyIiwgcXVpZXQgPSBUUlVFKQ0KYGBgDQoNCiMgUGFydCBJLiBSZXRyaWV2ZSBDb3VudHkgTGV2ZWwgRGF0YSANCg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSdsbCBmZXRjaCBhbmQgcmV0cmlldmUgYXZlcmFnZSBkYWlseSBtaW5pbXVtIHRlbXBlcmF0dXJlIGJ5IHllYXIgZm9yIGEgc2luZ2xlIGNvdW50eSwgYW5kIGNyZWF0ZSBhIHRpbWUtc2VyaWVzIHBsb3Qgc2hvd2luZyB0aGUgKmRpZmZlcmVuY2UqIGJldHdlZW4gUkNQODUgYW5kIFJDUDQ1Og0KDQohW10oaHR0cHM6Ly91Y2Fuci1pZ2lzLmdpdGh1Yi5pby9jYWxhZGFwdHItcmVzL2ltYWdlcy9jbnR5X3Rhc21pbl9kaWZmXzQwMHgyNjl4MjU2LnBuZyl7Y2xhc3M9J2NlbnRlcmVkJ30NCg0KIyMgUHJlc2V0IEFyZWFzLW9mLUludGVyZXN0DQoNCkZvciB0aGlzIGV4ZXJjaXNlLCB3ZSBuZWVkIHRvIHVzZSBhICoqUHJlc2V0IEFyZWEtb2YtSW50ZXJlc3QqKi4gDQoNClRoZSBDYWwtQWRhcHQgQVBJIGhhcyBhIG51bWJlciBvZiAncHJlc2V0JyBhcmVhcy1vZi1pbnRlcmVzdCAoYWxzbyBjYWxsZWQgYm91bmRhcnkgbGF5ZXJzKSB0aGF0IHlvdSBjYW4gdXNlIGNvb2tpZS1jdXR0ZXIgc3R5bGUgd2hlbiByZXRyaWV2aW5nIGNsaW1hdGUgZGF0YS4gVGhlIGFkdmFudGFnZSBvZiB1c2luZyBhbiBBT0kgUHJlc2V0IGlzIHRoYXQgeW91IGRvbid0IG5lZWQgdG8gaW1wb3J0IGEgR0lTIGxheWVyIHRvIHF1ZXJ5IGFjY29yZGluZyB0byB0aGVzZSBjb21tb24gZmVhdHVyZXMuIFlvdSBqdXN0IG5lZWQgdG8ga25vdyB0aGUgbmFtZSBvciBJRCBudW1iZXIgb2YgdGhlIGZlYXR1cmUocykgeW91J3JlIGludGVyZXN0ZWQgaW4uDQoNClRoZSBmb2xsb3dpbmcgQU9JIFByZXNldHMgYXJlIGF2YWlsYWJsZToNCg0KYGBge3IgY2h1bmswMiwgY2FjaGUgPSBGQUxTRX0NCmFvaXByZXNldF90eXBlcw0KYGBgDQoNClwNCg0KKipQcm8gVGlwOioqDQoNCiAtIGFsbCBvZiB0aGUgUHJlc2V0IEFPSXMgY2FuIGJlIGltcG9ydGVkIGludG8gUiBhcyBzZiBvYmplY3RzIHdpdGggYGNhX2FvaXByZXNldF9nZW9tKClgLg0KDQpcDQoNCiMjIDFcLiBGaW5kIHRoZSBGSVBTIGNvZGUgZm9yIEtpbmdzIENvdW50eQ0KDQpUbyB1c2UgYW4gQU9JIFByZXNldCwgeW91IG5lZWQgdG8gc3BlY2lmeSB3aGljaCBmZWF0dXJlKHMpIHlvdSdyZSBpbnRlcmVzdGVkIGluIGJ5IHByb3ZpZGluZyB0aGUgdmFsdWUocykgZm9yIG9uZSBvZiB0aGUgaWQgZmllbGRzLiBUaGUgc3BlY2lmaWMgY29sdW1ucyBhdmFpbGFibGUgZm9yIGlkZW50aWZ5aW5nIGZlYXR1cmVzIHZhcnkgYWNjb3JkaW5nIHRvIHByZXNldC4gWW91IGNhbiB2aWV3IHRoZSBpZCBjb2x1bW5zIGFuZCB0aGVpciB2YWx1ZXMgZm9yIGFuIEFPSSBQcmVzZXQgdXNpbmcgdGhlIGJ1aWx0LWluIGBhb2lwcmVzZXRfaWR2YWxgIGNvbnN0YW50LiBGb3IgZXhhbXBsZSB0aGUgY291bnRpZXMgbGF5ZXIgYWxsb3dzIHlvdSB0byBzcGVjaWZ5IGEgY291bnR5IGJ5IG5hbWUsIGZpcHMgY29kZSwgb3IgaWQuIFJlbWVtYmVyIHRoYXQgZXZlcnl0aGluZyBpbiBSIGlzIGNhc2Utc2Vuc2l0aXZlIQ0KDQpgYGB7ciBjaHVuazAzLCBjYWNoZSA9IFRSVUV9DQphb2lwcmVzZXRfaWR2YWwkY291bnRpZXMNCmBgYA0KDQpZb3UgY2FuIGZpbmQgY291bnR5IGZpcHMgY29kZXMgb24gR29vZ2xlIC0ganVzdCBiZSBzdXJlIHRvIHVzZSB0aGUgNi1jaGFyYWN0ZXIgdmVyc2lvbiB3aGljaCBpbmNsdWRlcyB0aGUgc3RhdGUuIEFsdGVybmF0ZWx5LCB5b3UgY2FuIFZpZXcgdGhlIGF0dHJpYnV0ZSB0YWJsZSBvZiB0aGUgY291bnRpZXMgcHJlc2V0IGxheWVyOiANCg0KYGBge3IgY2h1bmswNCwgbWVzc2FnZSA9IEZBTFNFfQ0KIyBjYV9hb2lwcmVzZXRfZ2VvbSgiY291bnRpZXMiKSAlPiUgVmlldygpDQpgYGANCg0KRm9yIHRoaXMgZXhhbXBsZSwgd2UnbGwgbG9vayBhdCAqKktpbmdzIENvdW50eSoqIChGSVBTID0gYDA2MDMxYCkuDQoNClwNCg0KIyMgMlwuIENyZWF0ZSB0aGUgQVBJIFJlcXVlc3QNCg0KTGV0J3MgY3JlYXRlIGFuIEFQSSByZXF1ZXN0IGZvciBLaW5ncyBDb3VudHkuIE5vdGUgYmVsb3cgdGhlIGluY2x1c2lvbiBvZiBgY2Ffb3B0aW9ucygpYCB0byBzcGVjaWZ5IGhvdyB3ZSB3YW50IHRvIGFnZ3JlZ2F0ZSB0aGUgcGl4ZWxzIHRoYXQgZmFsbCB3aXRoaW4gdGhlIGNvdW50cnkuIFRoaXMgaXMgcmVxdWlyZWQgd2hlbmV2ZXIgeW91IHF1ZXJ5IHBvbHlnb24gZmVhdHVyZXMuDQoNCmBgYHtyIGNodW5rMDUsIGNhY2hlID0gVFJVRX0NCmNudHlfY2FwIDwtIGNhX2xvY19hb2lwcmVzZXQodHlwZT0iY291bnRpZXMiLCBpZGZsZCA9ICJmaXBzIiwgaWR2YWwgPSAiMDYwMzEiKSAlPiUNCiAgY2FfZ2NtKGdjbXNbMTo0XSkgJT4lDQogIGNhX3NjZW5hcmlvKGMoInJjcDQ1IiwgInJjcDg1IikpICU+JQ0KICBjYV9wZXJpb2QoInllYXIiKSAlPiUNCiAgY2FfeWVhcnMoc3RhcnQgPSAyMDMwLCBlbmQgPSAyMDgwKSAlPiUNCiAgY2FfY3ZhcihjKCJ0YXNtaW4iKSkgJT4lDQogIGNhX29wdGlvbnMoc3BhdGlhbF9hZyA9ICJtYXgiKQ0KDQpjbnR5X2NhcA0KDQpjbnR5X2NhcCAlPiUgY2FfcHJlZmxpZ2h0KCkNCmBgYA0KXA0KDQoqKlBybyBUaXA6KioNCg0KLSBBbiBBUEkgcmVxdWVzdCBjYW4gaGF2ZSBtb3JlIHRoYW4gb25lIGxvY2F0aW9uLiBJZiB1c2luZyBBT0kgUHJlc2V0cywgcGFzcyBtdWx0aXBsZSB2YWx1ZXMgdG8gYGlkdmFsYCwgb3Igb21pdCBgaWR2YWxgIGNvbXBsZXRlbHkgYW5kIGFsbCBmZWF0dXJlcyBpbiB0aGUgbGF5ZXIgd2lsbCBiZSBxdWVyaWVkLg0KDQpcDQoNCkFzIGJlZm9yZSwgd2UgY2FuIHBsb3QgdGhlIEFQSSByZXF1ZXN0IHRvIGRvdWJsZS1jaGVjayB3ZSBnb3QgdGhlIHJpZ2h0IGxvY2F0aW9uOiANCg0KYGBge3IgY2h1bmswNiwgY2FjaGUgPSBGQUxTRX0NCnBsb3QoY250eV9jYXAsIGxvY2FncmlkID0gVFJVRSkNCmBgYA0KDQpcDQoNCiMjIDNcLiBGZXRjaCBEYXRhDQoNCkZldGNoIGRhdGEgd2l0aCBgY2FfZ2V0dmFsc190YmwoKWA6DQoNCmBgYHtyIGNodW5rMDcsIGNhY2hlID0gVFJVRX0NCmNudHlfdGJsIDwtIGNudHlfY2FwICU+JSBjYV9nZXR2YWxzX3RibChxdWlldCA9IFRSVUUpDQoNCmNudHlfdGJsICU+JSBoZWFkKCkNCmBgYA0KDQpcDQoNCiMjIDRcLiBXcmFuZ2xlIFJlc3VsdHMgZm9yIFBsb3R0aW5nDQoNClRvIGNvbXB1dGUgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBSQ1AgOC41IGFuZCBSQ1AgNC41LCB3ZSBuZWVkIHRvIHNwbGl0IHRoZW0gaW50byBzZXBhcmF0ZSBjb2x1bW5zLiBUaGlzIGlzIGFuIGV4YW1wbGUgb2YgZ29pbmcgZnJvbSBhICdsb25nJyBmb3JtYXQgdG8gYSAnd2lkZScgZm9ybWF0LiBGb3J0dW5hdGVseSwgdGhlIHRpZHlyIHBhY2thZ2UgaGFzIGEgZnVuY3Rpb24gY2FsbGVkIGBwaXZvdF93aWRlcmAgdGhhdCBjYW4gZG8gdGhpcyBpbiBzaW5nbGUgY29tbWFuZC4gV2hpbGUgd2UncmUgYXQgaXQgd2UnbGwgY29udmVydCB0aGUgdGVtcCB0byBkZWdyZWVzIEZhaHJlbmhlaXQ6DQoNCmBgYHtyIGNodW5rMDgsIGNhY2hlID0gVFJVRX0NCmNudHlfZGlmZl9yY3A4NV80NV90YmwgPC0gY250eV90YmwgJT4lIA0KICBtdXRhdGUodGVtcF9mID0gc2V0X3VuaXRzKHZhbCwgZGVnRikpICU+JSANCiAgc2VsZWN0KGZpcHMsIGdjbSwgc2NlbmFyaW8sIGR0LCB0ZW1wX2YpICU+JSANCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHNjZW5hcmlvLCB2YWx1ZXNfZnJvbSA9IHRlbXBfZikNCg0KaGVhZChjbnR5X2RpZmZfcmNwODVfNDVfdGJsKQ0KYGBgDQoNClwNCg0KIyMgNVwuIFBsb3QgDQoNCk5vdyB3ZSdyZSByZWFkeSB0byBtYWtlIHRoZSBwbG90LiBTaW5jZSB3ZSdyZSBtYWlubHkgaW50ZXJlc3RlZCBpbiB0aGUgdHJlbmQsIHdlJ2xsIGFkZCBhIHNtb290aGluZyBsaW5lIHVzaW5nIGBnZW9tX3Ntb290aCgpYDoNCg0KYGBge3IgY2h1bmswOSwgY2FjaGUgPSBUUlVFfQ0KZ2dwbG90KGRhdGEgPSBjbnR5X2RpZmZfcmNwODVfNDVfdGJsLCBhZXMoeCA9IGFzLkRhdGUoZHQpLCB5ID0gYXMubnVtZXJpYyhyY3A4NSAtIHJjcDQ1KSkpICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvcj1nY20pKSArDQogIGdlb21fc21vb3RoKG1ldGhvZD1sbSwgZm9ybXVsYSA9IHkgfiB4KSArDQogIGxhYnModGl0bGUgPSAiRGlmZmVyZW5jZSBiZXR3ZWVuIFJDUDguNSBhbmQgUkNQNC41IGluIHRoZSBBdmVyYWdlIERhaWx5IFxuTWluaW11bSBUZW1wZXJhdHVyZSBmb3IgS2luZ3MgQ291bnR5IiwgeCA9ICJ5ZWFyIiwgeSA9ICJ0ZW1wIChGKSIpDQpgYGANCg0KVGhpcyBwbG90IHNob3dzIHRoYXQgYXMgdGltZSBnb2VzIG9uLCB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIFJDUDQuNSBhbmQgUkNQOC41IGdldHMgYmlnZ2VyIGFuZCBiaWdnZXIuDQoNClwNCg0KIyBQYXJ0IElJLiBGaW5kaW5nIERhdGENCg0KSGFsZiB0aGUgYmF0dGxlIG9mIHdvcmtpbmcgd2l0aCBjbGltYXRlIGRhdGEgaXMgZmluZGluZyB0aGUgbmFtZSBvZiB0aGUgZGF0YXNldCB5b3UncmUgaW50ZXJlc3RlZCBpbi4NCg0KIyMgQnJvd3NpbmcgdGhlIENhbC1BZGFwdCBEYXRhIENhdGFsb2cNCg0KY2FsYWRhcHRSIGNvbWVzIHdpdGggYSBjb3B5IG9mIHRoZSBDYWwtQWRhcHQgcmFzdGVyIHNlcmllcyBkYXRhIGNhdGFsb2csIHdoaWNoIHlvdSBjYW4gYWNjZXNzIHdpdGggYGNhX2NhdGFsb2dfcnMoKWA6DQoNCmBgYHtyIGNodW5rMTB9DQojIGNhX2NhdGFsb2dfcnMoKSAlPiUgVmlldygpDQpgYGANCg0KXA0KDQojIyMgQ2hhbGxlbmdlIDENCg0KSG93IG1hbnkgcmFzdGVyIGRhdGFzZXRzIGFyZSB0aGVyZSBpbiB0aGUgQ2FsLUFkYXB0IGNhdGFsb2c/IFtbQW5zd2VyXShodHRwczovL2JpdC5seS8zSnZETGZ5KV0uDQoNCmBgYHtyIGNodW5rMTF9DQojIyBZb3VyIGFuc3dlciBoZXJlDQoNCmBgYA0KDQpcDQoNCioqUHJvIFRpcDoqKg0KDQotIHlvdSBjYW4gZG93bmxvYWQgYSBmcmVzaCBjb3B5IG9mIHRoZSBDYWwtQWRhcHQgcmFzdGVyIHNlcmllcyBjYXRhbG9nIHdpdGggYGNhX2NhdGFsb2dfZmV0Y2goKWAgDQoNClwNCg0KIyMgU2VhcmNoaW5nIGZvciBhIERhdGFzZXQNCg0KT25lIHdheSB5b3UgY2FuIHNlYXJjaCBmb3IgZGF0YXNldHMgaXMgdG8gdXNlIHRoZSBmaWx0ZXIgYm94ZXMgYWJvdmUgZWFjaCBjb2x1bW4gaW4gdGhlIFJTdHVkaW8gVmlldyBwYW5lLiBGb3IgZXhhbXBsZSBzZWFyY2ggZm9yIGxheWVycyB3aG9zZSBuYW1lIGNvbnRhaW5zIHRoZSB3b3JkICdzbm93Jy4NCg0KY2FsYWRhcHRSIGFsc28gaGFzIGEgc2VhcmNoIGZ1bmN0aW9uIGBjYV9jYXRhbG9nX3NlYXJjaCgpYC4gWW91IGNhbiB0aGlzIGZ1bmN0aW9uIHRvIGZpbmQgZGF0YXNldHMgdXNpbmcgYSBrZXkgd29yZCBvciBwaHJhc2UuIFlvdSBjYW4gYWxzbyB1c2UgdGhpcyBmdW5jdGlvbiB0byBzZWUgdGhlIGRldGFpbHMgZm9yIGEgc3BlY2lmaWMgc2x1ZywgZXhhbXBsZToNCg0KYGBge3IgY2h1bmsxMn0NCmNhX2NhdGFsb2dfc2VhcmNoKCJzd2VfZGF5X2VuczMyYXZnX3JjcDQ1IikNCmBgYA0KDQpcDQoNCiMjIyBDaGFsbGVuZ2UgMg0KDQpIb3cgbWFueSBkYXRhc2V0cyBhcmUgZnJvbSBncmlkTUVUPyBbW0Fuc3dlcl0oaHR0cHM6Ly9iaXQubHkvM203ek12QSldLg0KDQpgYGB7ciBjaHVuazEzfQ0KIyMgWW91ciBhbnN3ZXIgaGVyZQ0KDQpgYGANCg0KXA0KDQojIyBTcGVjaWZ5aW5nIERhdGFzZXRzDQoNCldoZW4geW91IGNvbnN0cnVjdCBhbiBBUEkgUmVxdWVzdCBvYmplY3QsIHlvdSBjYW4gbWl4LWFuZC1tYXRjaCBmdW5jdGlvbnMgdG8gc3BlY2lmeSB0aGUgZGF0YXNldCB5b3Ugd2FudDoNCg0KKipMT0NBIGRvd25zY2FsZWQgbW9kZWxlZCBjbGltYXRlIHZhcmlhYmxlcyoqIChpbmNsdWRpbmcgYWxsIFNjcmlwcHMpIGFuZCAqKnRoZWlyIGRlcml2YXRpdmVzKiogKGkuZS4sIFZJQykgY2FuIGJlIHNwZWNpZmllZCB1c2luZyB0aGUgY29uc3RydWN0b3IgZnVuY3Rpb25zOiBgY2FfZ2NtKClgICsgYGNhX3NjZW5hcmlvKClgICsgYGNhX2N2YXIoKWAgKyBgY2FfcGVyaW9kKClgLg0KDQoqKkxpdm5laCBkYXRhKiogKG9ic2VydmVkIGhpc3RvcmljYWwgdmFyaWFibGVzIGJhc2VkIG9uIHNwYXRpYWxseSBpbnRlcnBvbGF0ZWQgbWVhc3VyZW1lbnRzKSBjYW4gYmUgc3BlY2lmaWVkIHdpdGggYGNhX2xpdm5laCgpYCArIGBjYV9jdmFyKClgICsgYGNhX3BlcmlvZCgpYC4NCg0KKipldmVyeXRoaW5nIGVsc2UqKiBjYW4gYmUgc3BlY2lmaWVkIGJ5IHNsdWcsIHVzaW5nIGBjYV9zbHVnKClgLg0KDQpcDQoNCiMjIyBDaGFsbGVuZ2UgMw0KDQpXaGF0IGlzIHRoZSByYXN0ZXIgZGF0YXNldCB3aXRoIHRoZSBzbHVnIGB0bW1uX2RheV9ncmlkbWV0YD8gRm9yIHdoaWNoIHllYXJzIGlzIGl0IGF2YWlsYWJsZT8gV2hhdCBhcmUgdGhlIHVuaXRzPyBbW0hpbnRdKGh0dHBzOi8vYml0Lmx5LzNnUWI2cDApXS4gW1tBbnN3ZXJdKGh0dHBzOi8vYml0Lmx5LzNMSmwwYXkpXS4NCg0KYGBge3IgY2h1bmsxNH0NCiMjIFlvdXIgYW5zd2VyIGhlcmUNCg0KYGBgDQoNClwNCg0KIyBFeHBsb3JlIHRoZSBDbGltYXRlIEZ1dHVyZSBvZiBbTGluZGNvdmUgUmVzZWFyY2ggYW5kIEV4dGVuc2lvbiBDZW50ZXJdKGh0dHA6Ly9scmVjLnVjYW5yLmVkdS8pDQoNCkluIHRoaXMgZXhhbXBsZSwgd2UnbGwgZXhwbG9yZSBkYWlseSBwcm9qZWN0ZWQgY2xpbWF0ZSBkYXRhIGZvciBhIHBvaW50IGxvY2F0aW9uLiBXZSBzZWxlY3QgdGhlIFVDIFsqKkxpbmRjb3ZlIFJlc2VhcmNoIGFuZCBFeHRlbnNpb24gQ2VudGVyKipdKGh0dHA6Ly9scmVjLnVjYW5yLmVkdS8pIChMUkVDKSwgYSBmaWVsZCBzdGF0aW9uIGluIHRoZSBTYW4gSm9hcXVpbiBWYWxsZXkgd2hpY2ggaGFzIGJlZW4gYSBsZWFkaW5nIGNlbnRlciBmb3IgY2l0cnVzIHJlc2VhcmNoIGZvciBkZWNhZGVzLiBUaGUgdHJlZXMgYXJlIGdldHRpbmcgb2xkIGFuZCBuZWVkIHRvIGJlIHJlcGxhY2VkIHNvb24gc28gaXRzIHdvcnRoIGFza2luZyAtIGlzIGNpdHJ1cyBwcm9kdWN0aW9uIHN0aWxsIGdvaW5nIHRvIHZpYWJsZSBpbiB0aGlzIHBhcnQgb2YgQ2FsaWZvcm5pYSBhdCB0aGUgZW5kIG9mIHRoZSBjZW50dXJ5Pw0KDQpcDQoNCiMjIENyZWF0ZSB0aGUgQVBJIFJlcXVlc3QNCg0KTGV0J3Mgc3RhcnQgYnkgZ2V0dGluZyAyMCB5ZWFycyB3b3J0aCBvZiB0aGUgZGFpbHkgbWF4aW11bSB0ZW1wZXJhdHVyZSBmb3IgdGhlIDQgcHJpb3JpdHkgR0NNcyBhbmQgMiBSQ1BzLiANCg0KYGBge3IgIGNodW5rMTV9DQpscmVjX3Rhc21heF9wcmpfY2FwIDwtIGNhX2xvY19wdChjb29yZHMgPSBjKC0xMTkuMDYwLCAzNi4zNTkpLCBpZCA9IDEpICU+JSANCiAgY2FfcGVyaW9kKCJkYXkiKSAlPiUgDQogIGNhX2djbShnY21zWzE6NF0pICU+JSANCiAgY2Ffc2NlbmFyaW8oYygicmNwNDUiLCAicmNwODUiKSkgJT4lIA0KICBjYV9jdmFyKCJ0YXNtYXgiKSAlPiUgDQogIGNhX3llYXJzKHN0YXJ0ID0gMjA4MCwgZW5kID0gMjA5OSkNCiAgDQpscmVjX3Rhc21heF9wcmpfY2FwICU+JSBjYV9wcmVmbGlnaHQoKQ0KYGBgDQoNCmBgYHtyIGNodW5rMTYsIGNhY2hlID0gRkFMU0V9DQpwbG90KGxyZWNfdGFzbWF4X3Byal9jYXApDQpgYGANCg0KXA0KDQojIyBGZXRjaCBEYXRhDQoNCk5vdyB3ZSdyZSByZWFkeSB0byBmZXRjaCBkYXRhOg0KDQpgYGB7ciBjaHVuazE3fQ0KbHJlY190YXNtYXhfcHJqX3RibCA8LSBscmVjX3Rhc21heF9wcmpfY2FwICU+JSBjYV9nZXR2YWxzX3RibChxdWlldCA9IFRSVUUpDQoNCiMjIGJhY2t1cDogbHJlY190YXNtYXhfcHJqX3RibCA8LSByZWFkUkRTKCJkYXRhL2xyZWNfdGFzbWF4X3Byal90YmwucmRzIikNCg0KZGltKGxyZWNfdGFzbWF4X3Byal90YmwpDQpoZWFkKGxyZWNfdGFzbWF4X3Byal90YmwpDQpgYGANCg0KXA0KDQojIyBDcmVhdGUgYSBCb3ggUGxvdCBvZiB0aGUgTWF4aW11bSBEYWlseSBUZW1wZXJhdHVyZSBWYWx1ZXMgYnkgTW9udGgNCg0KVG8gbWFrZSBhIGJveCBwbG90LCB3ZSBuZWVkIHRvIGZpcnN0IGFkZCBjb2x1bW5zIGZvciBGYWhyZW5oZWl0LCBtb250aCwgYW5kIHllYXI6DQoNCmBgYHtyIGNodW5rMTh9DQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxyZWNfdGFzbWF4X3Byal90bXBmX3RibCA8LSBscmVjX3Rhc21heF9wcmpfdGJsICU+JSANCiAgbXV0YXRlKHRlbXBfZiA9IHNldF91bml0cyh2YWwsIGRlZ0YpLCBtb250aCA9IG1vbnRoKGR0KSwgeWVhciA9IHllYXIoZHQpKQ0KaGVhZChscmVjX3Rhc21heF9wcmpfdG1wZl90YmwpDQpgYGANCg0KRm9yIGVhY2ggbW9udGgsIGxldCBtYWtlIGEgYm94IHBsb3Qgb2YgdGhlIHRlbXBlcmF0dXJlIHZhbHVlcyBmb3IgZWFjaCBlbWlzc2lvbiBzY2VuYXJpbywgdHJlYXRpbmcgYWxsIEdDTXMgYXMgZXF1YWxseSBsaWtlbHk6DQoNCmBgYHtyIGNodW5rMTl9DQpnZ3Bsb3QobHJlY190YXNtYXhfcHJqX3RtcGZfdGJsLCBhZXMoeCA9IGFzLmZhY3Rvcihtb250aCksIHkgPSBhcy5udW1lcmljKHRlbXBfZikpKSArIA0KICBnZW9tX2JveHBsb3QoKSArDQogIGZhY2V0X2dyaWQoc2NlbmFyaW8gfiAuKSArDQogIGxhYnModGl0bGUgPSAiTWF4aW11bSBEYWlseSBUZW1wZXJhdHVyZSBieSBNb250aCIsIHggPSAibW9udGgiLCB5ID0gInRlbXAgKEYpIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJMaW5kY292ZSBSRUMsIDQgR0NNcyBjb21iaW5lZCwgMjA4MC0yMDk5IikNCmBgYA0KDQpcDQoNCiMjIyBDb3VudCBFeHRyZW1lIEhlYXQgRGF5cw0KDQpBbiBleHRyZW1lIGhlYXQgZGF5IGlzIGdlbmVyYWxseSBpZGVudGlmaWVkIHdoZW4gdGhlIG1heGltdW0gdGVtcGVyYXR1cmUgZXhjZWVkcyBhIHRocmVzaG9sZC4gVGhlIHRocmVzaG9sZCBjYW4gYmUgY2hvc2VuIGJhc2VkIG9uIHRoZSBoaXN0b3JpY2FsIHJhbmdlLCBvciBhIGJpb3BoeXNpY2FsIHByb2Nlc3MuIEZvciB0aGlzIGV4YW1wbGUsIHdlJ2xsIHNlbGVjdCAxMDUgJiMxNzY7Ri4NCg0KTGV0J3MgY291bnQgdGhlIHRvdGFsIG51bWJlciBvZiBkYXlzIHRoZSB0ZW1wZXJhdHVyZSBleGNlZWRlZCAxMDUgJiMxNzY7RiBmb3IgZWFjaCBSQ1AuIFdlIHN0YXJ0IGJ5IGFkZGluZyBhIGxvZ2ljYWwgY29sdW1uIHdoZXRoZXIgdGhlIHRlbXBlcmF0dXJlIGV4Y2VlZGVkIG91ciB0aHJlc2hvbGQuDQoNCmBgYHtyICBjaHVuazIwfQ0KbHJlY19ob3RkYXlfdGJsIDwtIGxyZWNfdGFzbWF4X3Byal90bXBmX3RibCAlPiUgDQogIG11dGF0ZShyZWFsbHlfaG90ID0gKHRlbXBfZiA+PSBzZXRfdW5pdHMoMTA1LCBkZWdGKSkpICU+JSANCiAgc2VsZWN0KC1zcGFnLCAtdmFsLCAtbW9udGgpDQoNCmhlYWQobHJlY19ob3RkYXlfdGJsKQ0KYGBgDQoNCldlIGNhbiBjb3VudCB0aGUgbnVtYmVyIG9mIGV4dHJlbWUgaGVhdCBkYXlzIHdpdGggYSBzaW1wbGUgZXhwcmVzc2lvbjoNCg0KYGBge3IgY2h1bmsyMX0NCm51bV9ob3RfZGF5cyA8LSBscmVjX2hvdGRheV90YmwgJT4lDQogIGdyb3VwX2J5KHNjZW5hcmlvLCByZWFsbHlfaG90KSAlPiUgDQogIGNvdW50KCkNCg0KbnVtX2hvdF9kYXlzDQpgYGANCg0KV2UgY2FuIGltcHJvdmUgdGhlIHJlYWRhYmlsaXR5IG9mIHRoaXMgdGFibGUgYnkgbWFraW5nIGVhY2ggc2NlbmFyaW8gYSBzZXBhcmF0ZSBjb2x1bW4uIFRoaXMgaXMgYW4gZXhhbXBsZSBvZiBbcGl2b3RpbmddKGh0dHBzOi8vdGlkeXIudGlkeXZlcnNlLm9yZy9hcnRpY2xlcy9waXZvdC5odG1sKSwgd2hpY2ggeW91IGNhbiBoYW5kbGUgdXNpbmcgYHRpZHlyOjpwaXZvdF93aWRlcmAuDQoNCmBgYHtyIGNodW5rMjJ9DQpudW1faG90X2RheXMgJT4lIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBzY2VuYXJpbywgdmFsdWVzX2Zyb20gPSBuKQ0KYGBgDQoNClwNCg0KIyMjIENoYWxsZW5nZSA0DQoNCkNvdW50IHRoZSBudW1iZXIgb2YgZXh0cmVtZSBoZWF0IGRheXMgdXNpbmcgYSB0aHJlc2hvbGQgb2YgMTEwIEZhaHJlbmhlaXQuIENvbXB1dGUgdGhlIG51bWJlciBvZiBleHRyZW1lIGhlYXQgZGF5cyBwZXIgbW9udGggYnkgc2NlbmFyaW8uIFtbQW5zd2VyXShodHRwczovL2JpdC5seS8zR1B3Mkh1KV0uDQoNCmBgYHtyIGNodW5rMjN9DQojIyBZb3VyIGFuc3dlciBoZXJlDQoNCg0KYGBgDQoNClwNCg0KKipQcm8gVGlwKioNCg0KIC0gVG8gc2VlIGhvdyB0byBjb3VudCB0aGUgbnVtYmVyIGNvbnNlY3V0aXZlIGhlYXQgZGF5cyAoaS5lLiwgaGVhdCBzcGVsbHMpLCBzZWUgW3RoaXMgbm90ZWJvb2tdKGh0dHBzOi8vdWNhbnItaWdpcy5naXRodWIuaW8vY2FsYWRhcHRyLXJlcy9ub3RlYm9va3MvY2FsYWRhcHRyX2ludHJvLm5iLmh0bWwjZXhhbXBsZS0zLWFuYWx5emUtZXh0cmVtZS1oZWF0LWluLWEtY2Vuc3VzLXRyYWN0KS4gDQogDQpcDQoNCiMjIFZpc3VhbGl6ZSB0aGUgRGlzdHJpYnV0aW9uIG9mIEhpc3RvcmljYWwgT2JzZXJ2ZWQgRGFpbHkgUHJlY2lwaXRhdGlvbiBieSBEZWNhZGUNCg0KT3VyIGdvYWwgaGVyZSBpcyB0byBsb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgZGFpbHkgcHJlY2lwaXRhdGlvbiBieSBtYWtpbmcgYSBoaXN0b2dyYW0gZm9yIGVhY2ggZGVjYWRlLiBXZSdsbCB1c2Ugb2JzZXJ2ZWQgcmFpbmZhbGwgZGF0YSBmcm9tIExpdm5laC4NCg0KYGBge3IgY2h1bmsyNH0NCmxyZWNfcHJfbGl2bl9jYXAgPC0gY2FfbG9jX3B0KGNvb3JkcyA9IGMoLTExOS4wNjAsIDM2LjM1OSksIGlkID0gMSkgJT4lIA0KICBjYV9saXZuZWgoVFJVRSkgJT4lIA0KICBjYV9wZXJpb2QoImRheSIpICU+JSANCiAgY2FfY3ZhcigicHIiKSAlPiUgDQogIGNhX3llYXJzKHN0YXJ0ID0gMTk1MCwgZW5kID0gMjAwOSkNCg0KbHJlY19wcl9saXZuX2NhcCAlPiUgY2FfcHJlZmxpZ2h0KCkNCmBgYA0KYGBge3IgY2h1bmsyNX0NCmxyZWNfcHJfbGl2bl90YmwgPC0gbHJlY19wcl9saXZuX2NhcCAlPiUgDQogIGNhX2dldHZhbHNfdGJsKCkgJT4lIA0KICByZW5hbWUocHJfbW1kYXkgPSB2YWwpICANCg0KIyMgYmFja3VwOiBscmVjX3ByX3Byal90YmwgPC0gcmVhZFJEUygiLi9kYXRhL2xyZWNfcHJfcHJqX3RibC5yZHMiKQ0KDQpkaW0obHJlY19wcl9saXZuX3RibCkNCmhlYWQobHJlY19wcl9saXZuX3RibCkNCmBgYA0KDQpGaW5hbGx5LCB3ZSdsbCBwbG90IGhpc3RvZ3JhbXMgb2YgdGhlIHByZWNpcGl0YXRpb24gdmFsdWVzLCBsb2dnZWQgYmVjYXVzZSBvZiB0aGUgaGlnaGx5IHNrZXdlZCBkaXN0cmlidXRpb24uDQoNCmBgYHtyIGNodW5rMjZ9DQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCg0KbHJlY19wcmRlY19saXZuX3RibCA8LSBscmVjX3ByX2xpdm5fdGJsICU+JSANCiAgbXV0YXRlKHByX21tZGF5X251bSA9IGFzLm51bWVyaWMocHJfbW1kYXkpLA0KICAgICAgICAgZGVjYWRlID0gZmxvb3IoeWVhcihkdCkgLyAxMCkgKiAxMCkgJT4lIA0KICBzZWxlY3QoZGVjYWRlLCBwcl9tbWRheV9udW0pDQoNCmdncGxvdChscmVjX3ByZGVjX2xpdm5fdGJsLCBhZXMoeD1sb2cocHJfbW1kYXlfbnVtKSkpICsgDQogIGdlb21faGlzdG9ncmFtKCkgKw0KICBmYWNldF93cmFwKCB+IGRlY2FkZSkNCg0KYGBgDQoNCiMgQ29uY2x1c2lvbg0KDQpPbmNlIHlvdSBnZXQgY2xpbWF0ZSBkYXRhIGluIFIgYXMgZGF0YSBmcmFtZXMsIHRoZXJlJ3MgYSBsb3QgeW91IGNhbiBkbyB3aXRoIGl0IQ0KDQpcDQoNCg0KDQoNCg==