solutions 

Overview

This Notebook will demonstrate how you can use caladaptR to:

By the end of the Notebook, you will have a time series plot of projected maximum annual temperature for a point location using the four recommended GCMs for California under RCP 4.5.


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.1)
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.0, GDAL 3.2.1, PROJ 7.2.1
library(tidyr)
library(conflicted)
conflict_prefer("filter", "dplyr", quiet = TRUE)
conflict_prefer("count", "dplyr", quiet = TRUE)
conflict_prefer("select", "dplyr", quiet = TRUE)

Part I. Get data for a point location

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.


Your turn

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" 
 [8] "CMCC-CMS"   "GFDL-CM3"   "HadGEM2-CC" "ens32avg"   "ens32max"   "ens32min"  
scenarios
[1] "rcp45"      "rcp85"      "historical"
cvars
 [1] "tasmax"     "tasmin"     "pr"         "swe"        "baseflow"   "ET"         "rainfall"  
 [8] "runoff"     "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()

  |                                                                                                    
  |                                                                                              |   0%
  |                                                                                                    
  |======                                                                                        |   7%
  |                                                                                                    
  |=============                                                                                 |  13%
  |                                                                                                    
  |===================                                                                           |  20%
  |                                                                                                    
  |=========================                                                                     |  27%
  |                                                                                                    
  |===============================                                                               |  33%
  |                                                                                                    
  |======================================                                                        |  40%
  |                                                                                                    
  |============================================                                                  |  47%
  |                                                                                                    
  |==================================================                                            |  53%
  |                                                                                                    
  |========================================================                                      |  60%
  |                                                                                                    
  |===============================================================                               |  67%
  |                                                                                                    
  |=====================================================================                         |  73%
  |                                                                                                    
  |===========================================================================                   |  80%
  |                                                                                                    
  |=================================================================================             |  87%
  |                                                                                                    
  |========================================================================================      |  93%
  |                                                                                                    
  |==============================================================================================| 100%
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)")

Your Turn

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)")

Part II. Retrieve County 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() 

  |                                                                                                    
  |                                                                                              |   0%
  |                                                                                                    
  |=============                                                                                 |  14%
  |                                                                                                    
  |===========================                                                                   |  29%
  |                                                                                                    
  |========================================                                                      |  43%
  |                                                                                                    
  |======================================================                                        |  57%
  |                                                                                                    
  |===================================================================                           |  71%
  |                                                                                                    
  |=================================================================================             |  86%
  |                                                                                                    
  |==============================================================================================| 100%
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 III. Finding Data

Half the battle of working with climate data is finding the name of the dataset you’re interested in.

Searching 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()

As you can see there are almost 950 datasets!


Pro Tip:

  • you can download a fresh copy of the Cal-Adapt raster series catalog with ca_catalog_fetch()


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: 659
  xmin: -124.5625
  xmax: -113.375
  ymin: 31.5625
  ymax: 43.75


Your Turn

How many datasets are from gridMET? Answer.

ca_catalog_search("gridmet", quiet = TRUE) %>% length()
[1] 13


Specifying Datasets in API Request Objects

Many of the raster series datasets are LOCA downscaled modeled climate variables (including all Scripps) and their derivatives (i.e., VIC). These data 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().


Your Turn

Import the tmmn_year_avg_gridmet (slug) dataset for Del Norte, Siskyou, and Modoc counties. What are the units for this dataset? Answer

## Get the fips code for Del Norte, Siskyou, and Modoc counties
ca_aoipreset_geom("counties") %>% 
  st_drop_geometry() %>% 
  filter(name %in% c("Del Norte", "Siskiyou", "Modoc")) %>% 
  select(name, state_name, fips)
Reading layer `counties' from data source `C:\Users\Andy\AppData\Local\R\cache\R\caladaptr\counties.gpkg' using driver `GPKG'
Simple feature collection with 87 features and 54 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -13871160 ymin: 3833648 xmax: -12625080 ymax: 5416187
Projected CRS: WGS 84 / Pseudo-Mercator
north_cap <- ca_loc_aoipreset(type = "counties", idfld = "fips", 
                              idval = c("06015", "06049", "06093")) %>% 
  ca_slug("tmmn_year_avg_gridmet") %>% 
  ca_options(spatial_ag = "mean")

north_cap %>% ca_preflight()
General issues
 - none found
Issues for querying values
 - none found
Issues for downloading rasters
 - none found
plot(north_cap)

north_tbl <- north_cap %>% ca_getvals_tbl()

north_tbl %>% head()

The units are Kelvin.

LS0tDQp0aXRsZTogIkdldHRpbmcgU3RhcnRlZCB3aXRoIGNhbGFkYXB0UiINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogDQogICAgY3NzOiBodHRwczovL3VjYW5yLWlnaXMuZ2l0aHViLmlvL2NhbGFkYXB0ci1yZXMvYXNzZXRzL25iX2NzczAxLmNzcw0KICAgIGluY2x1ZGVzOg0KICAgICAgYmVmb3JlX2JvZHk6IGh0dHBzOi8vdWNhbnItaWdpcy5naXRodWIuaW8vY2FsYWRhcHRyLXJlcy9hc3NldHMvbmJfaGRyc29sbi5odG1sDQogICAgICBhZnRlcl9ib2R5OiBodHRwczovL3VjYW5yLWlnaXMuZ2l0aHViLmlvL2NhbGFkYXB0ci1yZXMvYXNzZXRzL25iX2Zvb3RlcjAxLmh0bWwNCi0tLQ0KDQojIE92ZXJ2aWV3DQoNClRoaXMgTm90ZWJvb2sgd2lsbCBkZW1vbnN0cmF0ZSBob3cgeW91IGNhbiB1c2UgY2FsYWRhcHRSIHRvOg0KDQotIGNyZWF0ZSBhIENhbC1BZGFwdCBBUEkgcmVxdWVzdCBvYmplY3QgZm9yIGkpIGEgcG9pbnQgbG9jYXRpb24sIGFuZCBpaSkgYSBjb3VudHkNCi0gZmV0Y2ggZGF0YSBmcm9tIHRoZSBDYWwtQWRhcHQgc2VydmVyICAgIA0KLSBwbG90IHRoZSBkYXRhICANCg0KQnkgdGhlIGVuZCBvZiB0aGUgTm90ZWJvb2ssIHlvdSB3aWxsIGhhdmUgYSB0aW1lIHNlcmllcyBwbG90IG9mIHByb2plY3RlZCBtYXhpbXVtIGFubnVhbCB0ZW1wZXJhdHVyZSBmb3IgYSBwb2ludCBsb2NhdGlvbiB1c2luZyB0aGUgZm91ciByZWNvbW1lbmRlZCBHQ01zIGZvciBDYWxpZm9ybmlhIHVuZGVyIFJDUCA0LjUuDQoNCiFbXShodHRwczovL3VjYW5yLWlnaXMuZ2l0aHViLmlvL2NhbGFkYXB0ci1yZXMvaW1hZ2VzL3B0LXRhc21heC1wbG90XzQwMHgyNjh4MjU2LnBuZyl7Y2xhc3M9J2NlbnRlcmVkJ30NCg0KXA0KDQojIFNldHVwDQoNClRoZSBmaXJzdCB0aGluZyB3ZSBkbyBpcyB0byBsb2FkIGNhbGFkYXB0UiBhbmQgdGhlIG90aGVyIHBhY2thZ2Ugd2UncmUgZ29pbmcgdG8gbmVlZC4gKElmIHlvdSBoYXZlbid0IGluc3RhbGxlZCB0aGVzZSB5ZXQsIHNlZSB0aGlzIFtzZXR1cCBzY3JpcHRdKGh0dHBzOi8vZ2l0aHViLmNvbS91Y2Fuci1pZ2lzL2NhbGFkYXB0ci1yZXMvYmxvYi9tYWluL2RvY3Mvd29ya3Nob3BzL2NhX2ludHJvX29jdDIxL3NjcmlwdHMvY2FsYWRhcHRyX3NldHVwLlIpKS4gDQoNCmBgYHtyIGNodW5rMDEsIG1lc3NhZ2U9VFJVRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naG9sZCd9DQpsaWJyYXJ5KGNhbGFkYXB0cikNCmxpYnJhcnkodW5pdHMpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShzZikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGNvbmZsaWN0ZWQpDQpjb25mbGljdF9wcmVmZXIoImZpbHRlciIsICJkcGx5ciIsIHF1aWV0ID0gVFJVRSkNCmNvbmZsaWN0X3ByZWZlcigiY291bnQiLCAiZHBseXIiLCBxdWlldCA9IFRSVUUpDQpjb25mbGljdF9wcmVmZXIoInNlbGVjdCIsICJkcGx5ciIsIHF1aWV0ID0gVFJVRSkNCmBgYA0KDQojIFBhcnQgSS4gR2V0IGRhdGEgZm9yIGEgcG9pbnQgbG9jYXRpb24NCg0KIyMgMVwuIENyZWF0ZSB0aGUgQVBJIFJlcXVlc3QgDQoNClRoZSBmaXJzdCBzdGVwIGluIGdldHRpbmcgY2xpbWF0ZSB2YXJpYWJsZXMgYmFjayBpcyB0byBjcmVhdGUgYSBDYWwtQWRhcHQgQVBJIHJlcXVlc3Qgb2JqZWN0LiBUaGlzIGludm9sdmVzIHN0cmluZ2luZyB0b2dldGhlciBhIHNlcmllcyBvZiBmdW5jdGlvbnMgdGhhdCBzcGVjaWZ5IHRoZSBwaWVjZXMgb2YgdGhlIHJlcXVlc3QuIA0KDQpUaGUgZm9sbG93aW5nIHdpbGwgY3JlYXRlIGFuIEFQSSByZXF1ZXN0IGZvciBwcm9qZWN0ZWQgdGVtcGVyYXR1cmUgZGF0YSBmcm9tIFNjcmlwcHM6DQoNCmBgYHtyIGNodW5rMDJ9DQpwdDFfY2FwIDwtIGNhX2xvY19wdChjb29yZHMgPSBjKC0xMTkuMTY4NzY1LCAzNS40ODc4MDIpKSAlPiUNCiAgY2FfZ2NtKGdjbXNbMTo0XSkgJT4lICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogIGNhX3NjZW5hcmlvKGMoInJjcDQ1IiwgInJjcDg1IikpICU+JQ0KICBjYV9wZXJpb2QoInllYXIiKSAlPiUNCiAgY2FfeWVhcnMoc3RhcnQgPSAyMDMwLCBlbmQgPSAyMDgwKSAlPiUNCiAgY2FfY3ZhcihjKCJ0YXNtaW4iLCAidGFzbWF4IikpDQpgYGANCg0KXA0KDQoqKlBybyBUaXA6KioNCg0KLSB0aGUgb3JkZXIgb2YgdGhlIGNvbnN0cnVjdG9yIGZ1bmN0aW9ucyBkb2Vzbid0IG1hdHRlcg0KDQotIHdoZW4gZW50ZXJpbmcgY29vcmRpbmF0ZXMsIHRoZXkgbXVzdCBiZSBpKSBpbiBkZWNpbWFsIGRlZ3JlZXMsIGFuZCBpaSkgZm9ybWF0dGVkIGFzICpsb25naXR1ZGUsIGxhdGl0dWRlKiAoaW4gdGhhdCBvcmRlciEpDQoNCi0gdGhpcyBleGFtcGxlIGNyZWF0ZXMgYW4gQVBJIHJlcXVlc3QgZm9yIG1vZGVsZWQgY2xpbWF0ZSBkYXRhOyBmb3Igb3RoZXIgZGF0YXNldHMgeW91IG1pZ2h0IHVzZSBkaWZmZXJlbnQgY29uc3RydWN0b3IgZnVuY3Rpb25zDQoNCi0geW91IGRvbid0IGhhdmUgdG8gbWVtb3JpemUgYSBidW5jaCBvZiBrZXl3b3Jkcy4gYGNhbGFkYXB0cmAgaGFzIHNldmVyYWwgYnVpbHQtaW4gY29uc3RhbnRzIHRoYXQgY29udGFpbiB0aGUgdmFsdWVzIHlvdSBjYW4gcGFzcyB0byBBUEkgY29uc3RydWN0aW9uIGZ1bmN0aW9ucywgaW5jbHVkaW5nIGBnY21zYCwgYHNjZW5hcmlvc2AsIGBjdmFyc2AsIGFuZCBgcGVyaW9kc2AuDQoNClwNCg0KIyMjIyBZb3VyIHR1cm4NCg0KRW50ZXIgdGhlIGZvbGxvd2luZyBjb25zdGFudHMgdG8gc2VlIHdoYXQgdGhleSBjb250YWluIGBnY21zYCwgYHNjZW5hcmlvc2AsIGBjdmFyc2AsIGFuZCBgcGVyaW9kc2AgW0Fuc3dlcl0oaHR0cHM6Ly9iaXQubHkvM2tSVVA1WSkuDQoNCmBgYHtyIGNodW5rMDN9DQpnY21zDQoNCnNjZW5hcmlvcw0KDQpjdmFycw0KDQpwZXJpb2RzDQpgYGANCg0KKipQcm8gVGlwOioqDQoNCi0gbm90IGV2ZXJ5IGNvbWJpbmF0aW9uIG9mIEdDTSwgc2NlbmFyaW8sIGNsaW1hdGUgdmFyaWFibGUsIGFuZCBwZXJpb2QgaGFzIGEgZGF0YSBzZXQNCg0KLSB0aGVzZSBjb25zdGFudHMgYXJlIHVzZWZ1bCBmb3IgY29uc3RydWN0aW5nIEFQSSByZXF1ZXN0cyBmb3IgbW9kZWxlZCBjbGltYXRlIGRhdGE7IHRoZXkgbWF5IG5vdCBiZSBuZWVkZWQgZm9yIG90aGVyIGRhdGFzZXRzDQoNClwNCg0KIyMgMlwuIEV4YW1pbmUgYW4gQVBJIFJlcXVlc3QNCg0KVG8gc2VlIHdoYXQncyBpbiBhbiBBUEkgcmVxdWVzdCBvYmplY3QsIHR5cGUgaXRzIG5hbWUgYXQgdGhlIGNvbnNvbGU6DQoNCmBgYHtyIGNodW5rMDR9DQpwdDFfY2FwDQpgYGANCg0KKipQcm8gVGlwOioqDQoNCi0geW91IGNhbiBjdXN0b21pemUgdGhlIGNvbG9ycyB3aXRoIGBjYV9zZXR0aW5ncygpYC4gTGV0IGBjb25zb2xlX2NvbG9yc2AgPSBgImRhcmsiYCBvciBgbGlnaHRgIGRlcGVuZGluZyBvbiB5b3VyIFJTdHVkaW8gY29sb3IgYmFja2dyb3VuZC4NCg0KYGBge3IgY2h1bmswNX0NCmNhX3NldHRpbmdzKGNvbnNvbGVfY29sb3JzID0gImRhcmsiKQ0KcHQxX2NhcA0KYGBgDQoNClwNCg0KWW91IGNhbiBkb3VibGUtY2hlY2sgaWYgeW91ciBBUEkgcmVxdWVzdCBpcyBjb21wbGV0ZSBieSBwYXNzaW5nIGl0IHRvIGBjYV9wcmVmbGlnaHQoKWA6DQoNCmBgYHtyIGNodW5rMDZ9DQpwdDFfY2FwICU+JSBjYV9wcmVmbGlnaHQoKQ0KYGBgDQoNClwNCg0KVG8gdmVyaWZ5IHRoZSBsb2NhdGlvbiBpbiBhbiBBUEkgcmVxdWVzdCwgeW91IGNhbiBwbG90IGl0Og0KDQpgYGB7ciBjaHVuazA3fQ0KcGxvdChwdDFfY2FwKQ0KYGBgDQoNCioqUHJvIFRpcDoqKg0KDQotIFRvIHZpZXcgdGhlIExPQ0EgZ3JpZCBjZWxscywgYWRkIGBsb2NhZ3JpZCA9IFRSVUVgIHRvIHRoZSBwbG90IGNvbW1hbmQuDQoNCmBgYHtyIGNodW5rMDh9DQpwbG90KHB0MV9jYXAsIGxvY2FncmlkID0gVFJVRSkNCmBgYA0KDQoNCiMjIyMgWW91ciBUdXJuOg0KDQpXaGVyZSBpcyB0aGlzIHBvaW50IGxvY2F0ZWQ/IFtBbnN3ZXJdKGh0dHBzOi8vYml0Lmx5LzN1cVV4OTYpLg0KDQpBbnN3ZXI6IGluIFNoYWZ0ZXIgKG91dHNpZGUgQmFrZXJzZmllbGQpLCBLZXJuIENvdW50eSwgQ0ENCg0KXA0KDQojIyAzXC4gRmV0Y2ggRGF0YQ0KDQpOb3cgaXQncyB0aW1lIHRvIGZldGNoIGRhdGEgYnkgZmVlZGluZyBvdXIgQVBJIHJlcXVlc3QgaW50byBgY2FfZ2V0dmFsc190YmwoKWAuIFRoZSBvYmplY3QgcmV0dXJuZWQgd2lsbCBiZSBhIHRpYmJsZSAoZGF0YSBmcmFtZSk6ICANCg0KYGBge3IgY2h1bmswOSwgY2FjaGUgPSBUUlVFfQ0KcHQxX3RibCA8LSBwdDFfY2FwICU+JSBjYV9nZXR2YWxzX3RibCgpDQoNCmhlYWQocHQxX3RibCkNCmBgYA0KDQpcDQoNCiMjIDRcLiBXcmFuZ2xlIHRoZSBSZXN1bHRzIGZvciBQbG90dGluZw0KDQpUbyBwcm9kdWNlIHRoZSBkZXNpcmVkIHRpbWUgc2VyaWVzIHBsb3QsIHdlIG5lZWQgdG8gaSkgcHVsbCBvdXQganVzdCB2YWx1ZXMgZm9yIFJDUCA0LjUsIGFuZCBpaSkgY29udmVydCBkZWdyZWVzIHRvICYjMTc2O0YuIEZvciB0aGUgdW5pdCBjb252ZXJzaW9uLCB3ZSBjYW4gdXNlIHRoZSBoYW5keSBgc2V0X3VuaXRzYCBmdW5jdGlvbiBmcm9tIHRoZSBgdW5pdHNgIHBhY2thZ2UuDQoNCmBgYHtyIGNodW5rMTB9DQpwdDFfcmNwNDVfdGJsIDwtIHB0MV90YmwgJT4lDQogIGZpbHRlcihzY2VuYXJpbyA9PSAicmNwNDUiLCBjdmFyID09ICJ0YXNtYXgiKSAlPiUNCiAgbXV0YXRlKHRlbXBfZiA9IHNldF91bml0cyh2YWwsIGRlZ0YpKQ0KDQpwdDFfcmNwNDVfdGJsICU+JSBoZWFkKCkNCmBgYA0KDQpcDQoNCiMjIDVcLiBQbG90IHRoZSBUaW1lIFNlcmllcw0KDQpQbG90IHRoZXNlIHdpdGggZ2dwbG90Og0KDQpgYGB7ciBjaHVuazExLCBjYWNoZSA9IFRSVUV9DQpnZ3Bsb3QoZGF0YSA9IHB0MV9yY3A0NV90YmwsIGFlcyh4ID0gYXMuRGF0ZShkdCksIHkgPSBhcy5udW1lcmljKHRlbXBfZikpKSArDQogIGdlb21fbGluZShhZXMoY29sb3I9Z2NtKSkgKw0KICBsYWJzKHRpdGxlID0gIkF2ZXJhZ2UgTWF4aW11bSBEYWlseSBUZW1wZXJhdHVyZSBQZXIgWWVhciBmb3IgUkNQNC41IiwgeCA9ICJ5ZWFyIiwgeSA9ICJ0ZW1wIChGKSIpDQpgYGANCg0KIyMjIyBZb3VyIFR1cm4NCg0KTW9kaWZ5IHRoZSBhYm92ZSB0byBjcmVhdGUgYSBzaW1pbGFyIHBsb3QgZm9yIFJDUCA4LjUuIFtBbnN3ZXJdKGh0dHBzOi8vYml0Lmx5LzNGNFZpdHkpLg0KDQpgYGB7ciBjaHVuazEyfQ0KcHQxX3JjcDg1X3RibCA8LSBwdDFfdGJsICU+JQ0KICBmaWx0ZXIoc2NlbmFyaW8gPT0gInJjcDg1IiwgY3ZhciA9PSAidGFzbWF4IikgJT4lDQogIG11dGF0ZSh0ZW1wX2YgPSBzZXRfdW5pdHModmFsLCBkZWdGKSkNCg0KZ2dwbG90KGRhdGEgPSBwdDFfcmNwODVfdGJsLCBhZXMoeCA9IGFzLkRhdGUoZHQpLCB5ID0gYXMubnVtZXJpYyh0ZW1wX2YpKSkgKw0KICBnZW9tX2xpbmUoYWVzKGNvbG9yPWdjbSkpICsNCiAgbGFicyh0aXRsZSA9ICJBdmVyYWdlIE1heGltdW0gRGFpbHkgVGVtcGVyYXR1cmUgUGVyIFllYXIgZm9yIFJDUDguNSIsIHggPSAieWVhciIsIHkgPSAidGVtcCAoRikiKQ0KYGBgDQoNCiMgUGFydCBJSS4gUmV0cmlldmUgQ291bnR5IERhdGEgDQoNCkluIHRoaXMgc2VjdGlvbiwgd2UnbGwgZmV0Y2ggYW5kIHJldHJpZXZlIGF2ZXJhZ2UgZGFpbHkgbWluaW11bSB0ZW1wZXJhdHVyZSBieSB5ZWFyIGZvciBhIHNpbmdsZSBjb3VudHksIGFuZCBjcmVhdGUgYSB0aW1lLXNlcmllcyBwbG90IHNob3dpbmcgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBSQ1A4NSBhbmQgUkNQNDU6DQoNCiFbXShodHRwczovL3VjYW5yLWlnaXMuZ2l0aHViLmlvL2NhbGFkYXB0ci1yZXMvaW1hZ2VzL2NudHlfdGFzbWluX2RpZmZfNDAweDI2OXgyNTYucG5nKXtjbGFzcz0nY2VudGVyZWQnfQ0KDQojIyBQcmVzZXQgQXJlYXMtb2YtSW50ZXJlc3QNCg0KRm9yIHRoaXMgZXhlcmNpc2UsIHdlIG5lZWQgdG8gdXNlIGEgKipQcmVzZXQgQXJlYS1vZi1JbnRlcmVzdCoqLiANCg0KVGhlIENhbC1BZGFwdCBBUEkgaGFzIGEgbnVtYmVyIG9mICdwcmVzZXQnIGFyZWFzLW9mLWludGVyZXN0IChhbHNvIGNhbGxlZCBib3VuZGFyeSBsYXllcnMpIHRoYXQgeW91IGNhbiB1c2UgY29va2llLWN1dHRlciBzdHlsZSB3aGVuIHJldHJpZXZpbmcgY2xpbWF0ZSBkYXRhLiBUaGUgYWR2YW50YWdlIG9mIHVzaW5nIGFuIEFPSSBQcmVzZXQgaXMgdGhhdCB5b3UgZG9uJ3QgbmVlZCB0byBpbXBvcnQgYSBHSVMgbGF5ZXIgdG8gcXVlcnkgYWNjb3JkaW5nIHRvIHRoZXNlIGNvbW1vbiBmZWF0dXJlcy4gWW91IGp1c3QgbmVlZCB0byBrbm93IHRoZSBuYW1lIG9yIElEIG51bWJlciBvZiB0aGUgZmVhdHVyZShzKSB5b3UncmUgaW50ZXJlc3RlZCBpbi4NCg0KVGhlIGZvbGxvd2luZyBBT0kgUHJlc2V0cyBhcmUgYXZhaWxhYmxlOg0KDQpgYGB7ciBjaHVuazEzLCBjYWNoZSA9IEZBTFNFfQ0KYW9pcHJlc2V0X3R5cGVzDQpgYGANCg0KKipQcm8gVGlwOioqDQoNCiAtIGFsbCBvZiB0aGUgUHJlc2V0IEFPSXMgY2FuIGJlIGltcG9ydGVkIGludG8gUiBhcyBzZiBvYmplY3RzIHdpdGggYGNhX2FvaXByZXNldF9nZW9tKClgLg0KDQpcDQoNCiMjIDFcLiBGaW5kIHRoZSBGSVBTIGNvZGUgZm9yIEtpbmdzIENvdW50eQ0KDQpUbyB1c2UgYW4gQU9JIFByZXNldCwgeW91IG5lZWQgdG8gc3BlY2lmeSB3aGljaCBmZWF0dXJlKHMpIHlvdSdyZSBpbnRlcmVzdGVkIGluIGJ5IHByb3ZpZGluZyB0aGUgdmFsdWUocykgZm9yIG9uZSBvZiB0aGUgaWQgZmllbGRzLiBUaGUgc3BlY2lmaWMgY29sdW1ucyBhdmFpbGFibGUgZm9yIGlkZW50aWZ5aW5nIGZlYXR1cmVzIHZhcnkgYWNjb3JkaW5nIHRvIHByZXNldC4gWW91IGNhbiB2aWV3IHRoZSBpZCBjb2x1bW5zIGFuZCB0aGVpciB2YWx1ZXMgZm9yIGFuIEFPSSBQcmVzZXQgdXNpbmcgdGhlIGJ1aWx0LWluIGBhb2lwcmVzZXRfaWR2YWxgIGNvbnN0YW50LiBGb3IgZXhhbXBsZSB0aGUgY291bnRpZXMgbGF5ZXIgYWxsb3dzIHlvdSB0byBzcGVjaWZ5IGEgY291bnR5IGJ5IG5hbWUsIGZpcHMgY29kZSwgb3IgaWQuIFJlbWVtYmVyIHRoYXQgZXZlcnl0aGluZyBpbiBSIGlzIGNhc2Utc2Vuc2l0aXZlIQ0KDQpgYGB7ciBjaHVuazE0LCBjYWNoZSA9IFRSVUV9DQphb2lwcmVzZXRfaWR2YWwkY291bnRpZXMNCmBgYA0KDQpZb3UgY2FuIGZpbmQgY291bnR5IGZpcHMgY29kZXMgb24gR29vZ2xlIC0ganVzdCBiZSBzdXJlIHRvIHVzZSB0aGUgNi1jaGFyYWN0ZXIgdmVyc2lvbiB3aGljaCBpbmNsdWRlcyB0aGUgc3RhdGUuIEFsdGVybmF0ZWx5LCB5b3UgY2FuIFZpZXcgdGhlIGF0dHJpYnV0ZSB0YWJsZSBvZiB0aGUgY291bnRpZXMgcHJlc2V0IGxheWVyOiANCg0KYGBge3IgY2h1bmsxNSwgbWVzc2FnZSA9IEZBTFNFfQ0KIyBjYV9hb2lwcmVzZXRfZ2VvbSgiY291bnRpZXMiKSAlPiUgVmlldygpDQpgYGANCg0KRm9yIHRoaXMgZXhhbXBsZSwgd2UnbGwgbG9vayBhdCAqKktpbmdzIENvdW50eSoqIChGSVBTID0gYDA2MDMxYCkuDQoNClwNCg0KIyMgMlwuIENyZWF0ZSB0aGUgQVBJIFJlcXVlc3QNCg0KTGV0J3MgY3JlYXRlIGFuIEFQSSByZXF1ZXN0IGZvciBLaW5ncyBDb3VudHkuIE5vdGUgYmVsb3cgdGhlIGluY2x1c2lvbiBvZiBgY2Ffb3B0aW9ucygpYCB0byBzcGVjaWZ5IGhvdyB3ZSB3YW50IHRvIGFnZ3JlZ2F0ZSB0aGUgcGl4ZWxzIHRoYXQgZmFsbCB3aXRoaW4gdGhlIGNvdW50cnkuIFRoaXMgaXMgcmVxdWlyZWQgd2hlbmV2ZXIgeW91IHF1ZXJ5IHBvbHlnb24gZmVhdHVyZXMuDQoNCmBgYHtyIGNodW5rMTYsIGNhY2hlID0gVFJVRX0NCmNudHlfY2FwIDwtIGNhX2xvY19hb2lwcmVzZXQodHlwZT0iY291bnRpZXMiLCBpZGZsZCA9ICJmaXBzIiwgaWR2YWwgPSAiMDYwMzEiKSAlPiUNCiAgY2FfZ2NtKGdjbXNbMTo0XSkgJT4lDQogIGNhX3NjZW5hcmlvKGMoInJjcDQ1IiwgInJjcDg1IikpICU+JQ0KICBjYV9wZXJpb2QoInllYXIiKSAlPiUNCiAgY2FfeWVhcnMoc3RhcnQgPSAyMDMwLCBlbmQgPSAyMDgwKSAlPiUNCiAgY2FfY3ZhcihjKCJ0YXNtaW4iKSkgJT4lDQogIGNhX29wdGlvbnMoc3BhdGlhbF9hZyA9ICJtYXgiKQ0KDQpjbnR5X2NhcA0KDQpjbnR5X2NhcCAlPiUgY2FfcHJlZmxpZ2h0KCkNCmBgYA0KDQoqKlBybyBUaXA6KioNCg0KLSBBbiBBUEkgcmVxdWVzdCBjYW4gaGF2ZSBtb3JlIHRoYW4gb25lIGxvY2F0aW9uLiBJZiB1c2luZyBBT0kgUHJlc2V0cywgcGFzcyBtdWx0aXBsZSB2YWx1ZXMgdG8gYGlkdmFsYCwgb3Igb21pdCBgaWR2YWxgIGNvbXBsZXRlbHkgYW5kIGFsbCBmZWF0dXJlcyBpbiB0aGUgbGF5ZXIgd2lsbCBiZSBxdWVyaWVkLg0KDQpcDQoNCkFzIGJlZm9yZSwgd2UgY2FuIHBsb3QgdGhlIEFQSSByZXF1ZXN0IHRvIGRvdWJsZS1jaGVjayB3ZSBnb3QgdGhlIHJpZ2h0IGxvY2F0aW9uOiANCg0KYGBge3IgY2h1bmsxNywgY2FjaGUgPSBUUlVFfQ0KcGxvdChjbnR5X2NhcCwgbG9jYWdyaWQgPSBUUlVFKQ0KYGBgDQoNClwNCg0KIyMgM1wuIEZldGNoIERhdGENCg0KRmV0Y2ggZGF0YSB3aXRoIGBjYV9nZXR2YWxzX3RibCgpYDoNCg0KYGBge3IgY2h1bmsxOCwgY2FjaGUgPSBUUlVFfQ0KY250eV90YmwgPC0gY250eV9jYXAgJT4lIGNhX2dldHZhbHNfdGJsKCkgDQoNCmNudHlfdGJsICU+JSBoZWFkKCkNCmBgYA0KDQpcDQoNCiMjIDRcLiBXcmFuZ2xlIFJlc3VsdHMgZm9yIFBsb3R0aW5nDQoNClRvIGNvbXB1dGUgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBSQ1AgOC41IGFuZCBSQ1AgNC41LCB3ZSBuZWVkIHRvIHNwbGl0IHRoZW0gaW50byBzZXBhcmF0ZSBjb2x1bW5zLiBUaGlzIGlzIGFuIGV4YW1wbGUgb2YgZ29pbmcgZnJvbSBhICdsb25nJyBmb3JtYXQgdG8gYSAnd2lkZScgZm9ybWF0LiBGb3J0dW5hdGVseSwgdGhlIHRpZHlyIHBhY2thZ2UgaGFzIGEgZnVuY3Rpb24gY2FsbGVkIGBwaXZvdF93aWRlcmAgdGhhdCBjYW4gZG8gdGhpcyBpbiBzaW5nbGUgY29tbWFuZC4gV2hpbGUgd2UncmUgYXQgaXQgd2UnbGwgY29udmVydCB0aGUgdGVtcCB0byBkZWdyZWVzIEZhaHJlbmhlaXQ6DQoNCmBgYHtyIGNodW5rMTksIGNhY2hlID0gVFJVRX0NCmNudHlfZGlmZl9yY3A4NV80NV90YmwgPC0gY250eV90YmwgJT4lIA0KICBtdXRhdGUodGVtcF9mID0gc2V0X3VuaXRzKHZhbCwgZGVnRikpICU+JSANCiAgc2VsZWN0KGZpcHMsIGdjbSwgc2NlbmFyaW8sIGR0LCB0ZW1wX2YpICU+JSANCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHNjZW5hcmlvLCB2YWx1ZXNfZnJvbSA9IHRlbXBfZikNCg0KaGVhZChjbnR5X2RpZmZfcmNwODVfNDVfdGJsKQ0KYGBgDQoNClwNCg0KIyMgNVwuIFBsb3QgDQoNCk5vdyB3ZSdyZSByZWFkeSB0byBtYWtlIHRoZSBwbG90LiBTaW5jZSB3ZSdyZSBtYWlubHkgaW50ZXJlc3RlZCBpbiB0aGUgdHJlbmQsIHdlJ2xsIGFkZCBhIHNtb290aGluZyBsaW5lIHVzaW5nIGBnZW9tX3Ntb290aCgpYDoNCg0KYGBge3IgY2h1bmsyMCwgY2FjaGUgPSBUUlVFfQ0KZ2dwbG90KGRhdGEgPSBjbnR5X2RpZmZfcmNwODVfNDVfdGJsLCBhZXMoeCA9IGFzLkRhdGUoZHQpLCB5ID0gYXMubnVtZXJpYyhyY3A4NSAtIHJjcDQ1KSkpICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvcj1nY20pKSArDQogIGdlb21fc21vb3RoKG1ldGhvZD1sbSwgZm9ybXVsYSA9IHkgfiB4KSArDQogIGxhYnModGl0bGUgPSAiRGlmZmVyZW5jZSBiZXR3ZWVuIFJDUDguNSBhbmQgUkNQNC41IGluIHRoZSBBdmVyYWdlIERhaWx5IFxuTWluaW11bSBUZW1wZXJhdHVyZSBmb3IgS2luZ3MgQ291bnR5IiwgeCA9ICJ5ZWFyIiwgeSA9ICJ0ZW1wIChGKSIpDQpgYGANCg0KVGhpcyBwbG90IHNob3dzIHRoYXQgYXMgdGltZSBnb2VzIG9uLCB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIFJDUDQuNSBhbmQgUkNQOC41IGdldHMgYmlnZ2VyIGFuZCBiaWdnZXIuDQoNClwNCg0KIyBQYXJ0IElJSS4gRmluZGluZyBEYXRhDQoNCkhhbGYgdGhlIGJhdHRsZSBvZiB3b3JraW5nIHdpdGggY2xpbWF0ZSBkYXRhIGlzIGZpbmRpbmcgdGhlIG5hbWUgb2YgdGhlIGRhdGFzZXQgeW91J3JlIGludGVyZXN0ZWQgaW4uDQoNCiMjIFNlYXJjaGluZyB0aGUgQ2FsLUFkYXB0IERhdGEgQ2F0YWxvZw0KDQpjYWxhZGFwdFIgY29tZXMgd2l0aCBhIGNvcHkgb2YgdGhlIENhbC1BZGFwdCByYXN0ZXIgc2VyaWVzIGRhdGEgY2F0YWxvZywgd2hpY2ggeW91IGNhbiBhY2Nlc3Mgd2l0aCBgY2FfY2F0YWxvZ19ycygpYDoNCg0KYGBge3IgY2h1bmsyMX0NCiMgY2FfY2F0YWxvZ19ycygpICU+JSBWaWV3KCkNCmBgYA0KDQpBcyB5b3UgY2FuIHNlZSB0aGVyZSBhcmUgYWxtb3N0IDk1MCBkYXRhc2V0cyENCg0KXA0KDQoqKlBybyBUaXA6KioNCg0KLSB5b3UgY2FuIGRvd25sb2FkIGEgZnJlc2ggY29weSBvZiB0aGUgQ2FsLUFkYXB0IHJhc3RlciBzZXJpZXMgY2F0YWxvZyB3aXRoIGBjYV9jYXRhbG9nX2ZldGNoKClgIA0KDQpcDQoNCk9uZSB3YXkgeW91IGNhbiBzZWFyY2ggZm9yIGRhdGFzZXRzIGlzIHRvIHVzZSB0aGUgZmlsdGVyIGJveGVzIGFib3ZlIGVhY2ggY29sdW1uIGluIHRoZSBSU3R1ZGlvIFZpZXcgcGFuZS4gRm9yIGV4YW1wbGUgc2VhcmNoIGZvciBsYXllcnMgd2hvc2UgbmFtZSBjb250YWlucyB0aGUgd29yZCAnc25vdycuDQoNCmNhbGFkYXB0UiBhbHNvIGhhcyBhIHNlYXJjaCBmdW5jdGlvbiBgY2FfY2F0YWxvZ19zZWFyY2goKWAuIFlvdSBjYW4gdGhpcyBmdW5jdGlvbiB0byBmaW5kIGRhdGFzZXRzIHVzaW5nIGEga2V5IHdvcmQgb3IgcGhyYXNlLiBZb3UgY2FuIGFsc28gdXNlIHRoaXMgZnVuY3Rpb24gdG8gc2VlIHRoZSBkZXRhaWxzIGZvciBhIHNwZWNpZmljIHNsdWcsIGV4YW1wbGU6DQoNCmBgYHtyIGNodW5rMjJ9DQpjYV9jYXRhbG9nX3NlYXJjaCgic3dlX2RheV9lbnMzMmF2Z19yY3A0NSIpDQpgYGANCg0KXA0KDQojIyMjIFlvdXIgVHVybg0KDQpIb3cgbWFueSBkYXRhc2V0cyBhcmUgZnJvbSBncmlkTUVUPyBbQW5zd2VyXShodHRwczovL2JpdC5seS8zbTd6TXZBKS4NCg0KYGBge3IgY2h1bmsyM30NCmNhX2NhdGFsb2dfc2VhcmNoKCJncmlkbWV0IiwgcXVpZXQgPSBUUlVFKSAlPiUgbGVuZ3RoKCkNCmBgYA0KDQoNClwNCg0KIyMgU3BlY2lmeWluZyBEYXRhc2V0cyBpbiBBUEkgUmVxdWVzdCBPYmplY3RzDQoNCk1hbnkgb2YgdGhlIHJhc3RlciBzZXJpZXMgZGF0YXNldHMgYXJlICoqTE9DQSBkb3duc2NhbGVkIG1vZGVsZWQgY2xpbWF0ZSB2YXJpYWJsZXMqKiAoaW5jbHVkaW5nIGFsbCBTY3JpcHBzKSBhbmQgKip0aGVpciBkZXJpdmF0aXZlcyoqIChpLmUuLCBWSUMpLiBUaGVzZSBkYXRhIGNhbiBiZSBzcGVjaWZpZWQgdXNpbmcgdGhlIGNvbnN0cnVjdG9yIGZ1bmN0aW9uczogYGNhX2djbSgpYCArIGBjYV9zY2VuYXJpbygpYCArIGBjYV9jdmFyKClgICsgYGNhX3BlcmlvZCgpYC4NCg0KKipMaXZuZWggZGF0YSoqIChvYnNlcnZlZCBoaXN0b3JpY2FsIHZhcmlhYmxlcyBiYXNlZCBvbiBzcGF0aWFsbHkgaW50ZXJwb2xhdGVkIG1lYXN1cmVtZW50cykgY2FuIGJlIHNwZWNpZmllZCB3aXRoIGBjYV9saXZuZWgoKWAgKyBgY2FfY3ZhcigpYCArIGBjYV9wZXJpb2QoKWAuDQoNCioqRXZlcnl0aGluZyBlbHNlKiogY2FuIGJlIHNwZWNpZmllZCBieSBzbHVnLCB1c2luZyBgY2Ffc2x1ZygpYC4NCg0KXA0KDQojIyMjIFlvdXIgVHVybg0KDQpJbXBvcnQgdGhlIGB0bW1uX3llYXJfYXZnX2dyaWRtZXRgIChzbHVnKSBkYXRhc2V0IGZvciBEZWwgTm9ydGUsIFNpc2t5b3UsIGFuZCBNb2RvYyBjb3VudGllcy4gV2hhdCBhcmUgdGhlIHVuaXRzIGZvciB0aGlzIGRhdGFzZXQ/IFtBbnN3ZXJdKGh0dHBzOi8vYml0Lmx5LzNGOTV2b0cpDQoNCmBgYHtyIGNodW5rMjR9DQojIyBHZXQgdGhlIGZpcHMgY29kZSBmb3IgRGVsIE5vcnRlLCBTaXNreW91LCBhbmQgTW9kb2MgY291bnRpZXMNCmNhX2FvaXByZXNldF9nZW9tKCJjb3VudGllcyIpICU+JSANCiAgc3RfZHJvcF9nZW9tZXRyeSgpICU+JSANCiAgZmlsdGVyKG5hbWUgJWluJSBjKCJEZWwgTm9ydGUiLCAiU2lza2l5b3UiLCAiTW9kb2MiKSkgJT4lIA0KICBzZWxlY3QobmFtZSwgc3RhdGVfbmFtZSwgZmlwcykNCg0Kbm9ydGhfY2FwIDwtIGNhX2xvY19hb2lwcmVzZXQodHlwZSA9ICJjb3VudGllcyIsIGlkZmxkID0gImZpcHMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkdmFsID0gYygiMDYwMTUiLCAiMDYwNDkiLCAiMDYwOTMiKSkgJT4lIA0KICBjYV9zbHVnKCJ0bW1uX3llYXJfYXZnX2dyaWRtZXQiKSAlPiUgDQogIGNhX29wdGlvbnMoc3BhdGlhbF9hZyA9ICJtZWFuIikNCg0Kbm9ydGhfY2FwICU+JSBjYV9wcmVmbGlnaHQoKQ0KDQpwbG90KG5vcnRoX2NhcCkNCg0Kbm9ydGhfdGJsIDwtIG5vcnRoX2NhcCAlPiUgY2FfZ2V0dmFsc190YmwoKQ0KDQpub3J0aF90YmwgJT4lIGhlYWQoKQ0KYGBgDQoNClRoZSB1bml0cyBhcmUgKipLZWx2aW4qKi4NCg0K