solutions 

Overview

This Notebook will demonstrate how you can use caladaptR to:


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