solutionsÂ
Overview
This Notebook will demonstrate how you can use caladaptR to:
- create a Cal-Adapt API request object for a point location
- fetch data from Cal-Adapt
- plot the data as a time series
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)
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
Registered S3 methods overwritten by 'htmltools':
method from
print.html tools:rstudio
print.shiny.tag tools:rstudio
print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
method from
print.htmlwidget tools:rstudio
caladaptr (version 0.6.6)
URL: https://ucanr-igis.github.io/caladaptr
Bug reports: https://github.com/ucanr-igis/caladaptr/issues
library(units)
udunits database from C:/Users/Andy/Documents/R/win-library/4.1/units/share/udunits/udunits2.xml
library(ggplot2)
library(dplyr)
Attaching package: ‘dplyr’
The following objects are masked from ‘package:stats’:
filter, lag
The following objects are masked from ‘package:base’:
intersect, setdiff, setequal, union
library(sf)
Linking to GEOS 3.9.1, GDAL 3.2.1, PROJ 7.2.1; sf_use_s2() is TRUE
library(tidyr)
We use the conflicted
package to tell R which package we want it to use for some common function names:
library(conflicted)
conflict_prefer("filter", "dplyr", quiet = TRUE)
conflict_prefer("count", "dplyr", quiet = TRUE)
conflict_prefer("select", "dplyr", quiet = TRUE)
Part I. Get temperature data for a point location
In Part I, you generate a time series plot of projected maximum annual temperature like the one below for a point location using the four recommended GCMs for California and emissions scenario RCP 4.5.
1. Create the API Request
The first step in getting climate variables back is to create a Cal-Adapt API request object. This involves stringing together a series of functions that specify the pieces of the request.
The following will create an API request for projected temperature data from Scripps:
pt1_cap <- ca_loc_pt(coords = c(-119.168765, 35.487802)) %>%
ca_gcm(gcms[1:4]) %>%
ca_scenario(c("rcp45", "rcp85")) %>%
ca_period("year") %>%
ca_years(start = 2030, end = 2080) %>%
ca_cvar(c("tasmin", "tasmax"))
Pro Tip:
the order of the constructor functions doesn’t matter
when entering coordinates, they must be i) in decimal degrees, and ii) formatted as longitude, latitude (in that order!)
this example creates an API request for modeled climate data; for other datasets you might use different constructor functions
you don’t have to memorize a bunch of keywords. caladaptr
has several built-in constants that contain the values you can pass to API construction functions, including gcms
, scenarios
, cvars
, and periods
.
Challenge 1
Enter the following constants to see what they contain gcms
, scenarios
, cvars
, and periods
[Answer].
gcms
[1] "HadGEM2-ES" "CNRM-CM5" "CanESM2" "MIROC5" "ACCESS1-0" "CCSM4" "CESM1-BGC" "CMCC-CMS"
[9] "GFDL-CM3" "HadGEM2-CC" "ens32avg" "ens32max" "ens32min"
scenarios
[1] "rcp45" "rcp85" "historical"
cvars
[1] "tasmax" "tasmin" "pr" "swe" "baseflow" "et" "rainfall" "runoff"
[9] "snowfall" "soilMoist1" "Tair"
periods
[1] "day" "month" "year" "30yavg"
Pro Tip:
not every combination of GCM, scenario, climate variable, and period has a data set
these constants are useful for constructing API requests for modeled climate data; they may not be needed for other datasets
2. Examine an API Request
To see what’s in an API request object, type its name at the console:
pt1_cap
Cal-Adapt API Request
Location(s):
x: -119.169
y: 35.488
Variable(s): tasmin, tasmax
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
Pro Tip:
- you can customize the colors with
ca_settings()
. Let console_colors
= "dark"
or light
depending on your RStudio color background.
ca_settings(console_colors = "dark")
Console colors updated:
Accent 1
Accent 2
Accent 3
Accent 4
Message
Success
pt1_cap
Cal-Adapt API Request
Location(s):
x: -119.169
y: 35.488
Variable(s): tasmin, tasmax
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
You can double-check if your API request is complete by passing it to ca_preflight()
:
pt1_cap %>% ca_preflight()
General issues
- none found
Issues for querying values
- none found
Issues for downloading rasters
- none found
To verify the location in an API request, you can plot it:
plot(pt1_cap)
Pro Tip:
- To view the LOCA grid cells, add
locagrid = TRUE
to the plot command.
plot(pt1_cap, locagrid = TRUE)
Your Turn:
Where is this point located? [Answer].
Answer: in Shafter (outside Bakersfield), Kern County, CA
3. Fetch Data
Now it’s time to fetch data by feeding our API request into ca_getvals_tbl()
. The object returned will be a tibble (data frame):
pt1_tbl <- pt1_cap %>% ca_getvals_tbl(quiet = TRUE)
head(pt1_tbl)
4. Wrangle the Results for Plotting
To produce the desired time series plot, we need to i) pull out just values for RCP 4.5, and ii) convert degrees to °F. For the unit conversion, we can use the handy set_units
function from the units
package.
pt1_rcp45_tbl <- pt1_tbl %>%
filter(scenario == "rcp45", cvar == "tasmax") %>%
mutate(temp_f = set_units(val, degF))
pt1_rcp45_tbl %>% head()
5. Plot the Time Series
Plot these with ggplot:
ggplot(data = pt1_rcp45_tbl, aes(x = as.Date(dt), y = as.numeric(temp_f))) +
geom_line(aes(color=gcm)) +
labs(title = "Average Maximum Daily Temperature Per Year for RCP4.5", x = "year", y = "temp (F)")
Challenge 2
Modify the above to create a similar plot for RCP 8.5. [Answer].
pt1_rcp85_tbl <- pt1_tbl %>%
filter(scenario == "rcp85", cvar == "tasmax") %>%
mutate(temp_f = set_units(val, degF))
ggplot(data = pt1_rcp85_tbl, aes(x = as.Date(dt), y = as.numeric(temp_f))) +
geom_line(aes(color=gcm)) +
labs(title = "Average Maximum Daily Temperature Per Year for RCP8.5", x = "year", y = "temp (F)")
Challenge 3
For the same point, make a plot of average maximum daily temperature year for the same 4 GCMs for the period 1950-2005. [Hint]. [Answer].
pt1_hist_cap <- ca_loc_pt(coords = c(-119.168765, 35.487802)) %>%
ca_gcm(gcms[1:4]) %>%
ca_scenario("historical") %>%
ca_period("year") %>%
ca_years(start = 1950, end = 2005) %>%
ca_cvar("tasmax")
pt1_hist_cap %>% ca_preflight()
General issues
- none found
Issues for querying values
- none found
Issues for downloading rasters
- none found
pt1_hist_tbl <- pt1_hist_cap %>%
ca_getvals_tbl(quiet = TRUE)
pt1_hist_degf_tbl <- pt1_hist_tbl %>%
mutate(temp_f = set_units(val, degF))
ggplot(data = pt1_hist_degf_tbl, aes(x = as.Date(dt), y = as.numeric(temp_f))) +
geom_line(aes(color=gcm)) +
labs(title = "Average Maximum Daily Temperature Per Year for Historical Emission Scenario", x = "year", y = "temp (F)")
LS0tDQp0aXRsZTogIkdldHRpbmcgU3RhcnRlZCB3aXRoIGNhbGFkYXB0UiINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogDQogICAgY3NzOiBodHRwczovL3VjYW5yLWlnaXMuZ2l0aHViLmlvL2NhbGFkYXB0ci1yZXMvYXNzZXRzL25iX2NzczAxLmNzcw0KICAgIGluY2x1ZGVzOg0KICAgICAgYmVmb3JlX2JvZHk6IGh0dHBzOi8vdWNhbnItaWdpcy5naXRodWIuaW8vY2FsYWRhcHRyLXJlcy9hc3NldHMvbmJfaGRyc29sbi5odG1sDQogICAgICBhZnRlcl9ib2R5OiBodHRwczovL3VjYW5yLWlnaXMuZ2l0aHViLmlvL2NhbGFkYXB0ci1yZXMvYXNzZXRzL25iX2Zvb3RlcjAxLmh0bWwNCi0tLQ0KDQojIE92ZXJ2aWV3DQoNClRoaXMgTm90ZWJvb2sgd2lsbCBkZW1vbnN0cmF0ZSBob3cgeW91IGNhbiB1c2UgY2FsYWRhcHRSIHRvOg0KDQotIGNyZWF0ZSBhIENhbC1BZGFwdCBBUEkgcmVxdWVzdCBvYmplY3QgZm9yIGEgcG9pbnQgbG9jYXRpb24NCi0gZmV0Y2ggZGF0YSBmcm9tIENhbC1BZGFwdCAgDQotIHBsb3QgdGhlIGRhdGEgYXMgYSB0aW1lIHNlcmllcw0KDQpcDQoNCiMgU2V0dXANCg0KVGhlIGZpcnN0IHRoaW5nIHdlIGRvIGlzIHRvIGxvYWQgY2FsYWRhcHRSIGFuZCB0aGUgb3RoZXIgcGFja2FnZSB3ZSdyZSBnb2luZyB0byBuZWVkLiAoSWYgeW91IGhhdmVuJ3QgaW5zdGFsbGVkIHRoZXNlIHlldCwgc2VlIHRoaXMgW3NldHVwIHNjcmlwdF0oaHR0cHM6Ly9naXRodWIuY29tL3VjYW5yLWlnaXMvY2FsYWRhcHRyLXJlcy9ibG9iL21haW4vZG9jcy93b3Jrc2hvcHMvY2FfaW50cm9fYXByMjIvc2NyaXB0cy9jYWxhZGFwdHJfc2V0dXAuUikpLiANCg0KYGBge3IgY2h1bmswMSwgbWVzc2FnZT1UUlVFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdob2xkJ30NCmxpYnJhcnkoY2FsYWRhcHRyKQ0KbGlicmFyeSh1bml0cykNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeSh0aWR5cikNCmBgYA0KDQpXZSB1c2UgdGhlIGBjb25mbGljdGVkYCBwYWNrYWdlIHRvIHRlbGwgUiB3aGljaCBwYWNrYWdlIHdlIHdhbnQgaXQgdG8gdXNlIGZvciBzb21lIGNvbW1vbiBmdW5jdGlvbiBuYW1lczoNCg0KYGBge3IgY2h1bmswMiwgbWVzc2FnZT1UUlVFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdob2xkJ30NCmxpYnJhcnkoY29uZmxpY3RlZCkNCmNvbmZsaWN0X3ByZWZlcigiZmlsdGVyIiwgImRwbHlyIiwgcXVpZXQgPSBUUlVFKQ0KY29uZmxpY3RfcHJlZmVyKCJjb3VudCIsICJkcGx5ciIsIHF1aWV0ID0gVFJVRSkNCmNvbmZsaWN0X3ByZWZlcigic2VsZWN0IiwgImRwbHlyIiwgcXVpZXQgPSBUUlVFKQ0KYGBgDQoNClwNCg0KIyBQYXJ0IEkuIEdldCB0ZW1wZXJhdHVyZSBkYXRhIGZvciBhIHBvaW50IGxvY2F0aW9uDQoNCkluIFBhcnQgSSwgeW91IGdlbmVyYXRlIGEgdGltZSBzZXJpZXMgcGxvdCBvZiBwcm9qZWN0ZWQgbWF4aW11bSBhbm51YWwgdGVtcGVyYXR1cmUgbGlrZSB0aGUgb25lIGJlbG93IGZvciBhIHBvaW50IGxvY2F0aW9uIHVzaW5nIHRoZSBmb3VyIHJlY29tbWVuZGVkIEdDTXMgZm9yIENhbGlmb3JuaWEgYW5kIGVtaXNzaW9ucyBzY2VuYXJpbyBSQ1AgNC41Lg0KDQohW10oaHR0cHM6Ly91Y2Fuci1pZ2lzLmdpdGh1Yi5pby9jYWxhZGFwdHItcmVzL2ltYWdlcy9wdC10YXNtYXgtcGxvdF80MDB4MjY4eDI1Ni5wbmcpe2NsYXNzPSdjZW50ZXJlZCd9DQoNCg0KIyMgMVwuIENyZWF0ZSB0aGUgQVBJIFJlcXVlc3QgDQoNClRoZSBmaXJzdCBzdGVwIGluIGdldHRpbmcgY2xpbWF0ZSB2YXJpYWJsZXMgYmFjayBpcyB0byBjcmVhdGUgYSBDYWwtQWRhcHQgQVBJIHJlcXVlc3Qgb2JqZWN0LiBUaGlzIGludm9sdmVzIHN0cmluZ2luZyB0b2dldGhlciBhIHNlcmllcyBvZiBmdW5jdGlvbnMgdGhhdCBzcGVjaWZ5IHRoZSBwaWVjZXMgb2YgdGhlIHJlcXVlc3QuIA0KDQpUaGUgZm9sbG93aW5nIHdpbGwgY3JlYXRlIGFuIEFQSSByZXF1ZXN0IGZvciBwcm9qZWN0ZWQgdGVtcGVyYXR1cmUgZGF0YSBmcm9tIFNjcmlwcHM6DQoNCmBgYHtyIGNodW5rMDN9DQpwdDFfY2FwIDwtIGNhX2xvY19wdChjb29yZHMgPSBjKC0xMTkuMTY4NzY1LCAzNS40ODc4MDIpKSAlPiUNCiAgY2FfZ2NtKGdjbXNbMTo0XSkgJT4lICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogIGNhX3NjZW5hcmlvKGMoInJjcDQ1IiwgInJjcDg1IikpICU+JQ0KICBjYV9wZXJpb2QoInllYXIiKSAlPiUNCiAgY2FfeWVhcnMoc3RhcnQgPSAyMDMwLCBlbmQgPSAyMDgwKSAlPiUNCiAgY2FfY3ZhcihjKCJ0YXNtaW4iLCAidGFzbWF4IikpDQpgYGANCg0KXA0KDQoqKlBybyBUaXA6KioNCg0KLSB0aGUgb3JkZXIgb2YgdGhlIGNvbnN0cnVjdG9yIGZ1bmN0aW9ucyBkb2Vzbid0IG1hdHRlcg0KDQotIHdoZW4gZW50ZXJpbmcgY29vcmRpbmF0ZXMsIHRoZXkgbXVzdCBiZSBpKSBpbiBkZWNpbWFsIGRlZ3JlZXMsIGFuZCBpaSkgZm9ybWF0dGVkIGFzICpsb25naXR1ZGUsIGxhdGl0dWRlKiAoaW4gdGhhdCBvcmRlciEpDQoNCi0gdGhpcyBleGFtcGxlIGNyZWF0ZXMgYW4gQVBJIHJlcXVlc3QgZm9yIG1vZGVsZWQgY2xpbWF0ZSBkYXRhOyBmb3Igb3RoZXIgZGF0YXNldHMgeW91IG1pZ2h0IHVzZSBkaWZmZXJlbnQgY29uc3RydWN0b3IgZnVuY3Rpb25zDQoNCi0geW91IGRvbid0IGhhdmUgdG8gbWVtb3JpemUgYSBidW5jaCBvZiBrZXl3b3Jkcy4gYGNhbGFkYXB0cmAgaGFzIHNldmVyYWwgYnVpbHQtaW4gY29uc3RhbnRzIHRoYXQgY29udGFpbiB0aGUgdmFsdWVzIHlvdSBjYW4gcGFzcyB0byBBUEkgY29uc3RydWN0aW9uIGZ1bmN0aW9ucywgaW5jbHVkaW5nIGBnY21zYCwgYHNjZW5hcmlvc2AsIGBjdmFyc2AsIGFuZCBgcGVyaW9kc2AuDQoNClwNCg0KIyMjIENoYWxsZW5nZSAxDQoNCkVudGVyIHRoZSBmb2xsb3dpbmcgY29uc3RhbnRzIHRvIHNlZSB3aGF0IHRoZXkgY29udGFpbiBgZ2Ntc2AsIGBzY2VuYXJpb3NgLCBgY3ZhcnNgLCBhbmQgYHBlcmlvZHNgIFtbQW5zd2VyXShodHRwczovL2JpdC5seS8za1JVUDVZKV0uDQoNCmBgYHtyIGNodW5rMDR9DQpnY21zDQoNCnNjZW5hcmlvcw0KDQpjdmFycw0KDQpwZXJpb2RzDQpgYGANCg0KKipQcm8gVGlwOioqDQoNCi0gbm90IGV2ZXJ5IGNvbWJpbmF0aW9uIG9mIEdDTSwgc2NlbmFyaW8sIGNsaW1hdGUgdmFyaWFibGUsIGFuZCBwZXJpb2QgaGFzIGEgZGF0YSBzZXQNCg0KLSB0aGVzZSBjb25zdGFudHMgYXJlIHVzZWZ1bCBmb3IgY29uc3RydWN0aW5nIEFQSSByZXF1ZXN0cyBmb3IgbW9kZWxlZCBjbGltYXRlIGRhdGE7IHRoZXkgbWF5IG5vdCBiZSBuZWVkZWQgZm9yIG90aGVyIGRhdGFzZXRzDQoNClwNCg0KIyMgMlwuIEV4YW1pbmUgYW4gQVBJIFJlcXVlc3QNCg0KVG8gc2VlIHdoYXQncyBpbiBhbiBBUEkgcmVxdWVzdCBvYmplY3QsIHR5cGUgaXRzIG5hbWUgYXQgdGhlIGNvbnNvbGU6DQoNCmBgYHtyIGNodW5rMDV9DQpwdDFfY2FwDQpgYGANCg0KKipQcm8gVGlwOioqDQoNCi0geW91IGNhbiBjdXN0b21pemUgdGhlIGNvbG9ycyB3aXRoIGBjYV9zZXR0aW5ncygpYC4gTGV0IGBjb25zb2xlX2NvbG9yc2AgPSBgImRhcmsiYCBvciBgbGlnaHRgIGRlcGVuZGluZyBvbiB5b3VyIFJTdHVkaW8gY29sb3IgYmFja2dyb3VuZC4NCg0KYGBge3IgY2h1bmswNn0NCmNhX3NldHRpbmdzKGNvbnNvbGVfY29sb3JzID0gImRhcmsiKQ0KcHQxX2NhcA0KYGBgDQoNClwNCg0KWW91IGNhbiBkb3VibGUtY2hlY2sgaWYgeW91ciBBUEkgcmVxdWVzdCBpcyBjb21wbGV0ZSBieSBwYXNzaW5nIGl0IHRvIGBjYV9wcmVmbGlnaHQoKWA6DQoNCmBgYHtyIGNodW5rMDd9DQpwdDFfY2FwICU+JSBjYV9wcmVmbGlnaHQoKQ0KYGBgDQoNClwNCg0KVG8gdmVyaWZ5IHRoZSBsb2NhdGlvbiBpbiBhbiBBUEkgcmVxdWVzdCwgeW91IGNhbiBwbG90IGl0Og0KDQpgYGB7ciBjaHVuazA4LCBjYWNoZT1GQUxTRX0NCnBsb3QocHQxX2NhcCkNCmBgYA0KDQoqKlBybyBUaXA6KioNCg0KLSBUbyB2aWV3IHRoZSBMT0NBIGdyaWQgY2VsbHMsIGFkZCBgbG9jYWdyaWQgPSBUUlVFYCB0byB0aGUgcGxvdCBjb21tYW5kLg0KDQpgYGB7ciBjaHVuazA5LCBjYWNoZT1GQUxTRX0NCnBsb3QocHQxX2NhcCwgbG9jYWdyaWQgPSBUUlVFKQ0KYGBgDQoNCg0KIyMjIyBZb3VyIFR1cm46DQoNCldoZXJlIGlzIHRoaXMgcG9pbnQgbG9jYXRlZD8gW1tBbnN3ZXJdKGh0dHBzOi8vYml0Lmx5LzN1cVV4OTYpXS4NCg0KQW5zd2VyOiBpbiBTaGFmdGVyIChvdXRzaWRlIEJha2Vyc2ZpZWxkKSwgS2VybiBDb3VudHksIENBDQoNClwNCg0KIyMgM1wuIEZldGNoIERhdGENCg0KTm93IGl0J3MgdGltZSB0byBmZXRjaCBkYXRhIGJ5IGZlZWRpbmcgb3VyIEFQSSByZXF1ZXN0IGludG8gYGNhX2dldHZhbHNfdGJsKClgLiBUaGUgb2JqZWN0IHJldHVybmVkIHdpbGwgYmUgYSB0aWJibGUgKGRhdGEgZnJhbWUpOiAgDQoNCmBgYHtyIGNodW5rMTAsIGNhY2hlID0gVFJVRX0NCnB0MV90YmwgPC0gcHQxX2NhcCAlPiUgY2FfZ2V0dmFsc190YmwocXVpZXQgPSBUUlVFKQ0KDQpoZWFkKHB0MV90YmwpDQpgYGANCg0KXA0KDQojIyA0XC4gV3JhbmdsZSB0aGUgUmVzdWx0cyBmb3IgUGxvdHRpbmcNCg0KVG8gcHJvZHVjZSB0aGUgZGVzaXJlZCB0aW1lIHNlcmllcyBwbG90LCB3ZSBuZWVkIHRvIGkpIHB1bGwgb3V0IGp1c3QgdmFsdWVzIGZvciBSQ1AgNC41LCBhbmQgaWkpIGNvbnZlcnQgZGVncmVlcyB0byAmIzE3NjtGLiBGb3IgdGhlIHVuaXQgY29udmVyc2lvbiwgd2UgY2FuIHVzZSB0aGUgaGFuZHkgYHNldF91bml0c2AgZnVuY3Rpb24gZnJvbSB0aGUgYHVuaXRzYCBwYWNrYWdlLg0KDQpgYGB7ciBjaHVuazExfQ0KcHQxX3JjcDQ1X3RibCA8LSBwdDFfdGJsICU+JQ0KICBmaWx0ZXIoc2NlbmFyaW8gPT0gInJjcDQ1IiwgY3ZhciA9PSAidGFzbWF4IikgJT4lDQogIG11dGF0ZSh0ZW1wX2YgPSBzZXRfdW5pdHModmFsLCBkZWdGKSkNCg0KcHQxX3JjcDQ1X3RibCAlPiUgaGVhZCgpDQpgYGANCg0KXA0KDQojIyA1XC4gUGxvdCB0aGUgVGltZSBTZXJpZXMNCg0KUGxvdCB0aGVzZSB3aXRoIGdncGxvdDoNCg0KYGBge3IgY2h1bmsxMiwgY2FjaGUgPSBUUlVFfQ0KZ2dwbG90KGRhdGEgPSBwdDFfcmNwNDVfdGJsLCBhZXMoeCA9IGFzLkRhdGUoZHQpLCB5ID0gYXMubnVtZXJpYyh0ZW1wX2YpKSkgKw0KICBnZW9tX2xpbmUoYWVzKGNvbG9yPWdjbSkpICsNCiAgbGFicyh0aXRsZSA9ICJBdmVyYWdlIE1heGltdW0gRGFpbHkgVGVtcGVyYXR1cmUgUGVyIFllYXIgZm9yIFJDUDQuNSIsIHggPSAieWVhciIsIHkgPSAidGVtcCAoRikiKQ0KYGBgDQoNCiMjIyBDaGFsbGVuZ2UgMg0KDQpNb2RpZnkgdGhlIGFib3ZlIHRvIGNyZWF0ZSBhIHNpbWlsYXIgcGxvdCBmb3IgUkNQIDguNS4gW1tBbnN3ZXJdKGh0dHBzOi8vYml0Lmx5LzNGNFZpdHkpXS4NCg0KYGBge3IgY2h1bmsxM30NCnB0MV9yY3A4NV90YmwgPC0gcHQxX3RibCAlPiUNCiAgZmlsdGVyKHNjZW5hcmlvID09ICJyY3A4NSIsIGN2YXIgPT0gInRhc21heCIpICU+JQ0KICBtdXRhdGUodGVtcF9mID0gc2V0X3VuaXRzKHZhbCwgZGVnRikpDQoNCmdncGxvdChkYXRhID0gcHQxX3JjcDg1X3RibCwgYWVzKHggPSBhcy5EYXRlKGR0KSwgeSA9IGFzLm51bWVyaWModGVtcF9mKSkpICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvcj1nY20pKSArDQogIGxhYnModGl0bGUgPSAiQXZlcmFnZSBNYXhpbXVtIERhaWx5IFRlbXBlcmF0dXJlIFBlciBZZWFyIGZvciBSQ1A4LjUiLCB4ID0gInllYXIiLCB5ID0gInRlbXAgKEYpIikNCmBgYA0KDQojIyMgQ2hhbGxlbmdlIDMNCg0KRm9yIHRoZSBzYW1lIHBvaW50LCBtYWtlIGEgcGxvdCBvZiBhdmVyYWdlIG1heGltdW0gZGFpbHkgdGVtcGVyYXR1cmUgeWVhciBmb3IgdGhlIHNhbWUgNCBHQ01zIGZvciB0aGUgcGVyaW9kIDE5NTAtMjAwNS4gW1tIaW50XShodHRwczovL2JpdC5seS8zb1dCMTJWKV0uIFtbQW5zd2VyXShodHRwczovL2JpdC5seS8zSTNaVnAyKV0uDQoNCmBgYHtyIGNodW5rMTR9DQpwdDFfaGlzdF9jYXAgPC0gY2FfbG9jX3B0KGNvb3JkcyA9IGMoLTExOS4xNjg3NjUsIDM1LjQ4NzgwMikpICU+JQ0KICBjYV9nY20oZ2Ntc1sxOjRdKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgY2Ffc2NlbmFyaW8oImhpc3RvcmljYWwiKSAlPiUNCiAgY2FfcGVyaW9kKCJ5ZWFyIikgJT4lDQogIGNhX3llYXJzKHN0YXJ0ID0gMTk1MCwgZW5kID0gMjAwNSkgJT4lDQogIGNhX2N2YXIoInRhc21heCIpDQoNCnB0MV9oaXN0X2NhcCAlPiUgY2FfcHJlZmxpZ2h0KCkNCg0KcHQxX2hpc3RfdGJsIDwtIHB0MV9oaXN0X2NhcCAlPiUgDQogIGNhX2dldHZhbHNfdGJsKHF1aWV0ID0gVFJVRSkNCg0KcHQxX2hpc3RfZGVnZl90YmwgPC0gcHQxX2hpc3RfdGJsICU+JQ0KICBtdXRhdGUodGVtcF9mID0gc2V0X3VuaXRzKHZhbCwgZGVnRikpDQoNCmdncGxvdChkYXRhID0gcHQxX2hpc3RfZGVnZl90YmwsIGFlcyh4ID0gYXMuRGF0ZShkdCksIHkgPSBhcy5udW1lcmljKHRlbXBfZikpKSArDQogIGdlb21fbGluZShhZXMoY29sb3I9Z2NtKSkgKw0KICBsYWJzKHRpdGxlID0gIkF2ZXJhZ2UgTWF4aW11bSBEYWlseSBUZW1wZXJhdHVyZSBQZXIgWWVhciBmb3IgSGlzdG9yaWNhbCBFbWlzc2lvbiBTY2VuYXJpbyIsIHggPSAieWVhciIsIHkgPSAidGVtcCAoRikiKQ0KYGBgDQoNCg0K