About this R Notebook

R Notebooks are a ‘flavor’ of R markdown that combine plain text and R commands in code chunks. (You can download the Rmd file from the ‘code’ button at the top of the page.) You run code chunks in the document line-by-line, and the output appears immediately below the code chunk.

If you’re in RStudio, you can minimize the console window (and probably close the right-hand panes as well). You won’t need it, because when you run R commands in a R Notebook the output appears below the code chunk (not the console). This takes some getting used to if you’re used to working at the console.

Keyboard shortcuts you can use within a R Notebook:

  • run the current line of R: ctrl + enter
  • run everything in the current code chunk: ctrl + shift + enter
  • insert a new code chunk: ctrl + alt + i

Chill Portions: Background

‘Chill hours’ and ‘Chill portions’ are commonly used to estimate when tree crops will “wake up” from their winter slumber and start to grow their fruit / nuts. The timing and rate of chill also affects fruit productivity and scheduling of management actions (including harvest). Many tree crops (e.g., nuts) require a certain number of hours at chilly temperatures in order for the tree to blossom and the fruit to ripen. This presents a concern for climate change, because if the winters aren’t cold enough the operation may no longer be economically viable.

Computing ‘chill hours’ essentially involves adding up the total amount of time during the cold season within a temperature range (e.g., 32 - 45 °F). ‘Chill portions’ is a similar calculation, however instead of simply adding up the number of hours within a certain temperature range, additional weights are assigned based on bands of temperature and/or warming periods periods between cold spells (this mimics tree physiology thus producing better predictions).

Computing chill hours and chill portions requires hourly temperature data. You may be lucky enough to have hourly temperature recordings from weather stations for historic and real-time analyses. However for projected climate scenarios, the smallest unit of time for the CMIP5 family of climate models is daily min and max. This creates a conundrum if you want to explore how cumulative chill hours may change due to climate change.

To deal with this, we can estimate hourly temperatures based on the daily minimum and maximum temperature. The model that does this uses the time of sunrise and sunset to model temperature variation over the day. The chillR package, developed by Dr. Eike Luedeling, has functions for modeling hourly temperatures and computing chill portions (see the chillR Vignette for details).

See also:

Luedeling, E., M. Zhang and E. H. Girvetz (2009). Climatic Changes Lead to Declining Winter Chill for Fruit and Nut Trees in California during 1950–2099. PLOS ONE 4(7): e6166.

Exercise Summary

In this example, we:

  1. Download from Cal-Adapt downscaled daily projected minimum and maximum temperatures for a single point on the west side of the San Joaquin Valley from September 2080 thru June 2090 (10 growing seasons), for two emissions scenarios and 10 GCMs (global climate modesl).

  2. Use chillR to compute a) modeled hourly temperatures, and b) accumulated chill portions.

  3. Plot the results

Note: This notebook is primarily intended to demonstrate how to write code to compute chill portions using projected climate data. It is not presented as best practice for assessing the viability of tree crops under climate change!

Want to skip the details? You can ⇊ cut to the chase ⇊.

Setup

The following chunk will install any packages you don’t already have that will be needed below:

pkgs_req <- c("remotes", "ggplot2", "dplyr", "tmap", "conflicted", "tidyr", "lubridate", "tibble", "chillR", "stringr", "magrittr")
The working directory was changed to d:/github/cal-adapt/caladaptr inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
pkgs_missing <- pkgs_req[!(pkgs_req %in% installed.packages()[,"Package"])]
if (length(pkgs_missing)) install.packages(pkgs_missing, dependencies=TRUE)

Install caladaptr from GitHub (if needed):

## Uncomment and run the following line if you don't have the latest version of caladaptr
## devtools::install_github("ucanr-igis/caladaptr")

Load all the libraries we’ll be using below:

library(caladaptr)
library(units)
library(ggplot2)
library(dplyr)
library(conflicted)
library(tidyr)
library(lubridate)
library(tibble)
library(chillR)
library(magrittr)
library(stringr)
library(sf)

The last setup task is to define your preferences when you’re forced to use an ambiguous function name (i.e., a function that exists in more than one package). This is particularly needed with a few common generic functions from dplyr:

conflict_prefer("filter", "dplyr", quiet = TRUE)
conflict_prefer("count", "dplyr", quiet = TRUE)
conflict_prefer("select", "dplyr", quiet = TRUE)

Getting Started: Chill Portions for a Single Season

1) Create a Cal-Adapt API Request

The first step in getting climate data with caladaptR is to create an API request object. This involves stringing together a series of functions together that specify the pieces of the request.

To illustrate how to write code for this analysis, we’ll create an API request to query a single point, single GCM, and a single scenario for a single season. This of course is not the recommended practice, and you should always look at a minimum 10-20 year time spans and take averages.

## Pick a location
pt1_xy <- c(-119.964, 36.019)

## Create the API Request object
pt1_cap <- ca_loc_pt(coords = pt1_xy) %>%
  ca_gcm(gcms[1]) %>%
  ca_scenario("rcp45") %>%
  ca_period("day") %>%
  ca_dates(start = "2080-09-01", end = "2081-06-30") %>%
  ca_cvar(c("tasmax", "tasmin"))

pt1_cap
Cal-Adapt API Request
Location(s): 
  x: -119.964
  y: 36.019
Variable(s): tasmax, tasmin
Temporal aggregration period(s): day
GCM(s): HadGEM2-ES
Scenario(s): rcp45
Dates: 2080-09-01 to 2081-06-30
 

To verify the location saved in an API request, you can plot it. (Note we still haven’t fetched any data yet, this just shows you the location the request will ask for.)

plot(pt1_cap)

2) Fetch Data from Cal-Adapt

Next we fetch the temperature data with ca_getvals_tbl(), which returns a tibble. We then use mutate() to create a new column with the temperature values in Celsius.

pt1_tbl <- pt1_cap %>% 
  ca_getvals_tbl(quiet = TRUE) %>% 
  mutate(temp_c = set_units(val, degC)) %>% 
  select(id, cvar, period, gcm, scenario, dt, temp_c)

head(pt1_tbl)

3) Wrangle Results into the Format Required by chillR

Guided by chillR’s documentation and Vignette on hourly temperature records, we prepare a five-column tibble with the projected temperature values:

library(tidyr); library(lubridate)

pt1_dyr_tbl <- pt1_tbl %>%
  mutate(DATE = as.POSIXct(format(dt), tz="America/Los_Angeles")) %>%
  mutate(Year = as.integer(year(DATE)), 
         Month = as.integer(month(DATE)), 
         Day = day(DATE),
         temp_c = as.numeric(temp_c)) %>%
  select(cvar, temp_c, Year, Month, Day) %>%
  pivot_wider(names_from = cvar, values_from = temp_c) %>%
  rename(Tmax = tasmax, Tmin = tasmin)

head(pt1_dyr_tbl)

4) Model Hourly Temperature

Now we’re ready to compute the hourly temps. In addition to the daily min/max values, we need to pass the latitude of our site (which is used to determine sunrise and sunset time):

library(chillR)

pt1_hrtmp_wide <- make_hourly_temps(latitude = pt1_xy[2],
                                year_file = pt1_dyr_tbl,
                                keep_sunrise_sunset = FALSE)
head(pt1_hrtmp_wide)

5) Plot Modelled Hourly Temps

To plot the hourly temperatures, we need to convert the ‘wide’ format of the table to a ‘long’ format. Fortunately tidyr has everything we need to pivot between long and wide formats:

pt1_hrtmp_long <- pt1_hrtmp_wide %>%
  pivot_longer(cols = starts_with("Hour_"),
               names_to = "Hour",
               names_prefix = "Hour_",
               names_transform = list(hour = as.integer),
               values_to = "temp_c") %>%
  mutate(date_hour = ISOdatetime(Year, Month, Day, Hour, 0, 0, tz = "America/Los_Angeles")) %>%
  select(date_hour, temp_c) %>%
  arrange(date_hour)

head(pt1_hrtmp_long)

Now we can plot the hourly temperatures. To illustrate we’ll plot just the month of December, 2079:

ts_start <- as.Date("2080-12-01")
ts_end <- as.Date("2080-12-31")

pt1_hrtmp_onewk <- pt1_hrtmp_long %>% 
  filter(date_hour >= ts_start, date_hour <= ts_end)

ggplot(data = pt1_hrtmp_onewk,
       aes(x = date_hour, y = temp_c)) +
  geom_line(aes(color="red"), show.legend = FALSE) +
  labs(title = "Modelled Hourly Temperature for Pt 1: RCP 4.5", 
     subtitle = "December 1-31, 2080",
     caption = "GCM: HadGEM2-ES",
     x = "date", y = "Chill Portion") +
  theme(plot.caption = element_text(hjust = 0),
        plot.margin = margin(20, 20, 20, 20),
        plot.background = element_rect(colour = "gray50", size = 3))

6) Compute Chill Portions

The Dynamic_Model() function from chillR computes cumulative chill portions. We can add these values to the table as a new column with mutate():

pt1_hrtmpchill_long <- pt1_hrtmp_long %>% 
  mutate(accum_chill_prtn = Dynamic_Model(pt1_hrtmp_long$temp_c))

head(pt1_hrtmpchill_long)

Lastly, we plot the accumulated chill portions with ggplot():

ggplot(data = pt1_hrtmpchill_long,
       aes(x = date_hour, y = accum_chill_prtn)) +
  geom_line(aes(color="red"), show.legend = FALSE) +
  labs(title = "Projected Chill Portion Accumulation for Pt 1: RCP 4.5", 
       subtitle = "Sept 2080 - June 2081",
       caption = "GCM: HadGEM2-ES",
       x = "date", y = "Chill Portion") +
  theme(plot.caption = element_text(hjust = 0),
        plot.margin = margin(20, 20, 20, 20),
        plot.background = element_rect(colour = "gray50", size = 3))

7) Identify when Accumulated Chill Reaches a Threshhold

Suppose you’re hoping to grow the Kerman variety of Pistachios in 2080, which require [54 accumulated chill hours] (http://fruitsandnuts.ucdavis.edu/Weather_Services/chilling_accumulation_models/CropChillReq/) to ripen. We can easily find the date when the required accumulated chill is predicted to be reached:

chill_min <- 54

chill_date <- pt1_hrtmpchill_long %>% 
  filter(accum_chill_prtn > chill_min) %>% 
  slice(1) %>% 
  pull(date_hour) %>% 
  as.Date()

chill_date
[1] "2081-02-08"

Compare Chill Accumulation for Multiple Growing Seasons, Climate Models, and Emissions Scenarios

Climate models are not crystal balls, so thinking about the consequences of climate change generally involves looking at the predictions from a) a number of climate models (GCMs), 2) multiple emissions scenarios, and 3) multiple years.

Here we’ll compute the date when the chill portion reaches 36 for the 10 GCMs recommended for California, and 10 growing seasons. That should give us 100 maturation dates, which will plot using a box plot. We’ll do this twice, once with RCP 4.5 (low emissions scenario) and RCP 8.5 (higher emission scenario).

1) Create the API Request

## Pick a location
pt1_xy <- c(-119.964, 36.019)

pt1_10yrs_cap <- ca_loc_pt(coords = pt1_xy) %>%
  ca_gcm(gcms[1:10]) %>%
  ca_scenario(c("rcp45", "rcp85")) %>%
  ca_period("day") %>%
  ca_dates(start = "2080-09-01", end = "2090-06-30") %>%
  ca_cvar(c("tasmax", "tasmin"))

pt1_10yrs_cap
Cal-Adapt API Request
Location(s): 
  x: -119.964
  y: 36.019
Variable(s): tasmax, tasmin
Temporal aggregration period(s): day
GCM(s): HadGEM2-ES, CNRM-CM5, CanESM2, MIROC5, ACCESS1-0, CCSM4, CESM1-BGC, CMCC-CMS, GFDL-CM3, HadGEM2-CC
Scenario(s): rcp45, rcp85
Dates: 2080-09-01 to 2090-06-30
 

2) Fetch Data

Our API request asks for >140,000 temperature values, so fetching the data could take a minute or two.

pt1_10yrs_tbl <- pt1_10yrs_cap %>% ca_getvals_tbl(quiet = TRUE) 

dim(pt1_10yrs_tbl)
[1] 143600      8
head(pt1_10yrs_tbl)

3) Munge the Results

This tibble has daily min/max values from September 1 2079 thru June 30, 2090. We need to break it up into groups by 1) growing season, 2) GCM, and 3) RCP. Grouping it up by GCM and RCP will be easy - there are already columns for those. We need to add a column for growing season however, which (for the purposes of this example) we define as September thru June. In other words, every day from Nov 1 2079 thru June 30 2080 should be part of growing season 2080, Nov 2080 thru June 2081 should be part of growing season 2081, and so on.

To construct the growing season column, we’ll first create columns for Year, Month, and Day. Later on, we’ll use these columns in a formula to compute the growing season year.

pt1_10yrs_ymd_tbl <- pt1_10yrs_tbl %>% 
  mutate(Year = as.integer(substr(dt, 1, 4)), 
         Month = as.integer(substr(dt, 6, 7)),
         Day = as.integer(substr(dt, 9, 10))) %>% 
  select(cvar, gcm, scenario, Year, Month, Day, val)

head(pt1_10yrs_ymd_tbl)

We can now add a column for growing season using a case_when statement. While we’re at it, we’ll remove rows that don’t belong to any growing season, and convert the temperature from Kelvin to Celcius.

pt1_10yrs_ymd_gs_tbl <- pt1_10yrs_ymd_tbl %>% 
  mutate(gs = case_when(Month <= 6 ~ Year,
                        Month >= 9 ~ as.integer(Year + 1))) %>% 
  filter(!is.na(gs)) %>% 
  mutate(temp_c = set_units(val, degC))

head(pt1_10yrs_ymd_gs_tbl)

Next, we convert this ‘long’ format into a ‘wide’ format which chillR::make_hourly_temps() requires:

library(tidyr); library(lubridate)

pt1_10yrs_ymd_gcmrcp_gs_tbl <- pt1_10yrs_ymd_gs_tbl %>%
  select(cvar, temp_c, Year, Month, Day, gcm, scenario, gs) %>%
  pivot_wider(names_from = cvar, values_from = temp_c) %>%
  rename(Tmax = tasmax, Tmin = tasmin)

dim(pt1_10yrs_ymd_gcmrcp_gs_tbl)
[1] 60640     8
head(pt1_10yrs_ymd_gcmrcp_gs_tbl)

4) Model Hourly Temperature

Now we’re ready to compute the hourly temps. In addition to the daily min/max values, we need to pass the latitude of our site (which is used to determine sunrise and sunset time):

pt1_10yrs_hrtmp_wide <- make_hourly_temps(latitude = pt1_xy[2],
                                year_file = pt1_10yrs_ymd_gcmrcp_gs_tbl,
                                keep_sunrise_sunset = FALSE)
dim(pt1_10yrs_hrtmp_wide)
[1] 60640    33
head(pt1_10yrs_hrtmp_wide)

Now we go from wide back to long, and recreate the date-time column. This generates over a million rows so it can take several seconds.

pt1_10yrs_hrtmp_long <- pt1_10yrs_hrtmp_wide %>%
  pivot_longer(cols = starts_with("Hour_"),
               names_to = "Hour",
               names_prefix = "Hour_",
               names_transform = list(Hour = as.integer),
               values_to = "temp_c") %>%
  mutate(date_hour = ISOdatetime(Year, Month, Day, Hour, 0, 0, tz = "America/Los_Angeles")) %>%
  arrange(date_hour)

dim(pt1_10yrs_hrtmp_long)
[1] 1455360      12
head(pt1_10yrs_hrtmp_long)

To reality-check, we can plot a portion of the hourly temperature:

ts_start <- as.Date("2081-01-01")
ts_end <- as.Date("2081-03-01")

sample_time_series <- pt1_10yrs_hrtmp_long %>% 
  filter(date_hour >= ts_start, date_hour <= ts_end, 
         gcm == "CanESM2", scenario == "rcp45") %>% 
  arrange(date_hour)

dim(sample_time_series)
[1] 1417   12
ggplot(data = sample_time_series,
       aes(x = date_hour, y = as.numeric(temp_c))) +
  geom_line(aes(color="red"), show.legend = FALSE) +
  labs(title = "Modelled Hourly Temperature", x = "date", y = "temp (C)") +
  theme(plot.caption = element_text(hjust = 0),
        plot.margin = margin(20, 20, 20, 20),
        plot.background = element_rect(colour = "gray50", size = 3))

5) Compute Chill Portions

Now we’re ready to compute cumulative chill portions. We need to break this up by growing season, gcm and rcp.

pt1_10yrs_hrtmpcp <- pt1_10yrs_hrtmp_long %>% 
  mutate(temp_c = as.numeric(temp_c)) %>% 
  group_by(gs, gcm, scenario) %>% 
  mutate(accum_chill_prtn = Dynamic_Model(temp_c))

head(pt1_10yrs_hrtmpcp)

View the maximum accumulated chill portion per RCP:

pt1_10yrs_maxchill <- pt1_10yrs_hrtmpcp %>% 
  group_by(scenario, gs, gcm) %>% 
  summarise(max_chill = max(accum_chill_prtn), .groups = 'drop') 

pt1_10yrs_maxchill %>% head()

6) Compute Safe Chill

For each RCP, we compute the ‘safe winter chill’ value. ‘Safe winter chill’ is the minimum chill portion one can expect 90% of the time (Luedeling et al, 2009). For growers, safe winter chill reflects the economic need for an orchard operation to produce good yields in most years, rather than an average year. Here it is computed as the 10% quantile of all end-of-season chill portions in the analysis (ie., all GCMs and all growing seasons 2080-2090), with the assumption that all of these outcomes are equally likely.

## Compute safe winter chill for each RCP
safe_chill_tbl <- pt1_10yrs_maxchill %>% 
  group_by(scenario) %>% 
  summarize(safe_chill = quantile(max_chill, probs = 0.1), .groups = "drop")

safe_chill_tbl

Plot distribution of chill portion:

## Plot the histogram and overlay the safe chill value
projected_histo <- ggplot(pt1_10yrs_maxchill, aes(x=max_chill)) + 
  geom_histogram() +
  geom_vline(data = safe_chill_tbl, color = "red", aes(xintercept = safe_chill), size = 1) +
  facet_wrap("scenario", nrow = 1) +
  labs(title = "Maximum Projected Chill Portion", 
     subtitle = "2080 - 2090",
     caption = paste0(
       str_wrap(paste0("GCMs: ", paste(unique(pt1_10yrs_maxchill$gcm), collapse = ", "), width = 105)),
       "\n",
       str_wrap("The red line is the 10% quantile and represents the safe winter chill (the mimimum chill portion one may expect 90% of the time assumming all projections are equally likely).", width = 105)),
     x = "end-of-season chill portion", y = "count") +
  theme(plot.caption = element_text(hjust = 0),
        plot.margin = margin(20, 20, 20, 20),
        plot.background = element_rect(colour = "gray50", size = 3))

projected_histo

7) How Many Growing Seasons Reach the Minimum Chill Requirement?

Next, for each RCP we compute how many projected temperature series resulted in at least 54 accumulated chill portions needed for a viable crop of the Kerman cultivar of Pistachios:

chill_min <- 54

pt1_10yrs_prct_reached_min <- pt1_10yrs_hrtmpcp %>% 
  group_by(gs, gcm, scenario) %>% 
  summarise(reached_thresh = max(accum_chill_prtn) >= chill_min) %>% 
  ungroup() %>% 
  group_by(scenario) %>% 
  summarise(percent_reached_thresh = sum(reached_thresh) / n()) %>% 
  mutate(time_period = "2081 - 2090", percent_reached_thresh = scales::percent(percent_reached_thresh)) %>% 
  relocate(time_period, scenario, percent_reached_thresh)

pt1_10yrs_prct_reached_min

8) Accumulated Chill Curves

Next, we plot the curves for accumulated chill for RCP 4.5 and 8.5. We have 10 growing seasons and 10 GCMs, for a total of 100 curves. The lines that end above the minimum chill requirement represent simulations where the minimum chill for a viable crop was reached.

## First make a copy of data, manually set the year to the same value (so the dates are in the same range), and keep
## only midnight values

pt1_10yrs_hrtmpcp_4plot <- pt1_10yrs_hrtmpcp %>% 
  filter(Hour == 0) %>% 
  mutate(YearPlot = if_else(Month >= 9, 1970, 1971)) %>% 
  mutate(date_hour = ISOdatetime(YearPlot, Month, Day, Hour, 0, 0, tz = "America/Los_Angeles"),
         gs_gcm = paste(gs, gcm, sep = "_")) %>% 
  select(scenario, gs, gcm, gs_gcm, date_hour, accum_chill_prtn)

## Create plot
ggplot(data = pt1_10yrs_hrtmpcp_4plot %>% filter(scenario == "rcp45"),
       aes(x = date_hour, y = accum_chill_prtn)) +
  geom_line(aes(color=gs_gcm), show.legend = FALSE) +
  geom_hline(yintercept = chill_min, size = 1) +
  labs(title = "Projected Chill Portion Accumulation for Pt 1: RCP 4.5", 
       subtitle = "2080 - 2090",
       caption = str_wrap(paste0("GCMs: ", paste(unique(pt1_10yrs_hrtmpcp_4plot$gcm), collapse = ", "), width = 60)),
       x = "date", y = "Chill Portion") +
  theme(plot.caption = element_text(hjust = 0),
        plot.margin = margin(20, 20, 20, 20),
        plot.background = element_rect(colour = "gray50", size = 3))

## RCP 85
ggplot(data = pt1_10yrs_hrtmpcp_4plot %>% filter(scenario == "rcp85"),
       aes(x = date_hour, y = accum_chill_prtn)) +
  geom_line(aes(color=gs_gcm), show.legend = FALSE) +
  geom_hline(yintercept = chill_min, size = 1) +
  labs(title = "Projected Chill Portion Accumulation for Pt 1: RCP 8.5", 
       subtitle = "2080 - 2090",
       caption = stringr::str_wrap(paste0("GCMs: ", paste(unique(pt1_10yrs_hrtmpcp_4plot$gcm), collapse = ", "), width = 60)),
       x = "date", y = "Chill Portion") +
  theme(plot.caption = element_text(hjust = 0),
        plot.margin = margin(20, 20, 20, 20),
        plot.background = element_rect(colour = "gray50", size = 3))

9) Distribution of Required Accumulation Date

Here we plot the distribution of projected dates when the accumulated chill reaches the minimum requirement.

## Number of runs for each emissions scenario = number of growing seasons * number of gcms
num_runs <- length(unique(pt1_10yrs_hrtmpcp$gs)) * length(unique(pt1_10yrs_hrtmpcp$gcm)) 

## For each run, find the first date when the minimum chill portion was reached. 
pt1_10yrs_scen_chilldt <- pt1_10yrs_hrtmpcp %>% 
  filter(Hour == 0) %>% 
  filter(accum_chill_prtn >= chill_min) %>% 
  group_by(gs, gcm) %>% 
  slice_min(order_by = date_hour, n = 1) %>% 
  ungroup() %>%
  mutate(date_1970 = ISOdate(year = 1970, month = Month, day = Day)) %>% 
  select(scenario, date_1970)

## we're getting one NA - investigate
## pt1_10yrs_scen_chilldt %>% filter(is.na(date_1970))  

## Make a data frame for the total number of dates and middle date per scenario 
## (This is for the labels on the box plot)
scen_stats_tbl <- pt1_10yrs_scen_chilldt %>% 
  group_by(scenario) %>% 
  summarize(num_dates = n(), mid_dt = median(date_1970, na.rm = TRUE))
`summarise()` ungrouping output (override with `.groups` argument)
## Make a list object for easy reference. NOT NEEDED ANYMORE
## num_runs_lst <- scen_stats_tbl %$% split(num_dates, scenario) 

## Plot the results
ggplot(pt1_10yrs_scen_chilldt, aes(date_1970 , scenario)) + 
  geom_boxplot() +
  geom_label(data = scen_stats_tbl, 
            aes(label=paste0("n=", num_dates), x = mid_dt + (3.5 * 24 * 3600)), 
            size = 3.5, fill = "white", label.size = 0) +
  labs(title = paste0("Date Accumlated Chill Portion Reaches ", chill_min ), 
       subtitle = "2080 - 2090",
       caption = str_wrap(paste("GCMs: ", paste(unique(pt1_10yrs_hrtmpcp_4plot$gcm), collapse = ", ")), width = 80),
       x = "date", y = "Emissions Scenario") +
  theme(plot.caption = element_text(hjust = 0),
        plot.margin = margin(20, 20, 20, 20),
        plot.background = element_rect(colour = "gray50", size = 3))

Historical Baseline

When looking at historical data, we can either use modeled or observed. Here we use modeled because 1) modeled data has been shown to matches observed patterns very well, and 2) it facilitates comparisons with modeled predictions.

## Pick a location
pt1_xy <- c(-119.964, 36.019)

pt1_baseline_cap <- ca_loc_pt(coords = pt1_xy) %>%
  ca_gcm(gcms[1:10]) %>%
  ca_scenario(c("historical")) %>%
  ca_period("day") %>%
  ca_dates(start = "1995-01-01", end = "2005-12-31") %>%
  ca_cvar(c("tasmax", "tasmin"))

pt1_baseline_cap
Cal-Adapt API Request
Location(s): 
  x: -119.964
  y: 36.019
Variable(s): tasmax, tasmin
Temporal aggregration period(s): day
GCM(s): HadGEM2-ES, CNRM-CM5, CanESM2, MIROC5, ACCESS1-0, CCSM4, CESM1-BGC, CMCC-CMS, GFDL-CM3, HadGEM2-CC
Scenario(s): historical
Dates: 1995-01-01 to 2005-12-31
 
pt1_baseline_tbl <- pt1_baseline_cap %>% ca_getvals_tbl(quiet = TRUE) 
dim(pt1_baseline_tbl)
[1] 80360     8
head(pt1_baseline_tbl)

Add columns:

pt1_baseline_ymd_tbl <- pt1_baseline_tbl %>% 
  mutate(Year = as.integer(substr(dt, 1, 4)), 
         Month = as.integer(substr(dt, 6, 7)),
         Day = as.integer(substr(dt, 9, 10))) %>% 
  select(cvar, gcm, scenario, Year, Month, Day, val)
pt1_baseline_ymd_tbl

Add growing season column:

pt1_baseline_ymd_gs_tbl <- pt1_baseline_ymd_tbl %>% 
  mutate(gs = case_when(Month <= 6 ~ Year,
                        Month >= 9 ~ as.integer(Year + 1))) %>% 
  filter(!is.na(gs)) %>% 
  mutate(temp_c = set_units(val, degC))
head(pt1_baseline_ymd_gs_tbl)

Pivot wider (make tasmax and tasmin separate columns):

pt1_baseline_ymd_gcmrcp_gs_tbl <- pt1_baseline_ymd_gs_tbl %>%
  select(cvar, temp_c, Year, Month, Day, gcm, scenario, gs) %>%
  pivot_wider(names_from = cvar, values_from = temp_c) %>%
  rename(Tmax = tasmax, Tmin = tasmin)

dim(pt1_baseline_ymd_gcmrcp_gs_tbl)
[1] 33360     8
head(pt1_baseline_ymd_gcmrcp_gs_tbl)
NA

Compute hourly temps (in wide format):

pt1_baseline_hrtmp_wide <- make_hourly_temps(latitude = pt1_xy[2],
                                year_file = pt1_baseline_ymd_gcmrcp_gs_tbl,
                                keep_sunrise_sunset = FALSE)
dim(pt1_baseline_hrtmp_wide)
[1] 33360    33
head(pt1_baseline_hrtmp_wide)

Convert wide to long:

pt1_baseline_hrtmp_long <- pt1_baseline_hrtmp_wide %>%
  pivot_longer(cols = starts_with("Hour_"),
               names_to = "Hour",
               names_prefix = "Hour_",
               names_transform = list(Hour = as.integer),
               values_to = "temp_c") %>%
  mutate(date_hour = ISOdatetime(Year, Month, Day, Hour, 0, 0, tz = "America/Los_Angeles")) %>%
  arrange(date_hour)

dim(pt1_baseline_hrtmp_long)
[1] 800640     12
head(pt1_baseline_hrtmp_long)

Compute baseline chill portions:

pt1_baseline_hrtmpcp <- pt1_baseline_hrtmp_long %>% 
  mutate(temp_c = as.numeric(temp_c)) %>% 
  group_by(gs, gcm, scenario) %>% 
  mutate(accum_chill_prtn = Dynamic_Model(temp_c))

head(pt1_baseline_hrtmpcp)
NA

Compute the max-chill reached each growing season / GCM during the baseline period. Next we compute the ‘safe chill’ (10% quantile):

pt1_baseline_maxchill <- pt1_baseline_hrtmpcp %>% 
  group_by(scenario, gs, gcm) %>% 
  summarise(max_chill = max(accum_chill_prtn), .groups = 'drop') 

dim(pt1_baseline_maxchill)
[1] 120   4
pt1_baseline_maxchill %>% head()

safe_chill_baseline_tbl <- pt1_baseline_maxchill %>% 
  group_by(scenario) %>% 
  summarize(safe_chill = quantile(max_chill, probs = 0.1), .groups = "drop") 

safe_chill_baseline_tbl

Plot distribution of the baseline maximum chill portion:

## Plot the histogram and overlay the safe chill value
baseline_histo <- ggplot(pt1_baseline_maxchill, aes(x=max_chill)) + 
  geom_histogram() +
  geom_vline(data = safe_chill_baseline_tbl, color = "red", aes(xintercept = safe_chill), size = 1) +
  facet_wrap("scenario", nrow = 1) +
  labs(title = "Maximum Projected Chill Portion", 
     subtitle = "1995 - 2005",
     caption = paste0(
       str_wrap(paste0("GCMs: ", paste(unique(pt1_baseline_maxchill$gcm), collapse = ", "), width = 105)),
       "\n",
       str_wrap("The red line is the 10% quantile and represents the safe winter chill (the mimimum chill portion one may expect 90% of the time assumming all projections are equally likely).", width = 105)),
     x = "end-of-season chill portion", y = "count") +
  theme(plot.caption = element_text(hjust = 0),
        plot.margin = margin(20, 20, 20, 20),
        plot.background = element_rect(colour = "gray50", size = 3))

baseline_histo

How Many Growing Seasons and Simulated (Backcast) Climate Conditions During the Baseline Seasons Reached Our Minimum Chill Requirement?

chill_min <- 54

pt1_baseline_prct_reached_min <- pt1_baseline_hrtmpcp %>% 
  group_by(gs, gcm, scenario) %>% 
  summarise(reached_thresh = max(accum_chill_prtn) >= chill_min) %>% 
  ungroup() %>% 
  group_by(scenario) %>% 
  summarise(percent_reached_thresh = sum(reached_thresh) / n()) %>% 
  mutate(time_period = "1995-2005", percent_reached_thresh = scales::percent(percent_reached_thresh)) %>% 
  relocate(time_period, scenario, percent_reached_thresh)

pt1_baseline_prct_reached_min
NA

Conclusions

In this analysis we examined projected and historic accumulated chill portions for this point:

plot(pt1_10yrs_cap)

We looked at the projected temperatures at this location according to 10 GCMs in the recent historic time period (1995-2005), as well as projected for 2080-2090 under two carbon emissions scenarios (RCP4.5 & 8.5). Our analysis shows that the percent of seasons & models where the maximum chill portion by the end of winter is greater than or equal to the amount of chill needed to grow Kerman variety of Pistachios (54) was:

pt1_baseline_prct_reached_min %>% 
  bind_rows(pt1_10yrs_prct_reached_min)

We also computed the ‘safe-chill’ portion for each of the time periods. This is the amount of chill one can expect to reach 90% of the time:

safe_chill_baseline_tbl %>% bind_rows(safe_chill_tbl)

baseline_histo

projected_histo

Interpretation

This example was primarily an exercise on how to write code, and should be taken with a grain of salt. However the results we came up with suggest that this location may continue to be viable for the Kerman variety of Pistachios in 2080-2090 under emission scenario 4.5, but may not produce enough winter chill portions consistently enough to be economically viable under RCP 8.5.

This analysis was based on the assumption that all 10 GCMs recommended for California under the 4th Climate Change Assessment are equally likely. We also interpolated hourly temperatures based on a generic statistical model that was not specific to this location. We could improve this analysis by:

  1. Selecting GCMs that are best suited for California’s San Joaquin Valley
  2. Reviewing the latest literature to assess whether emission scenario 4.5 or 8.5 seems more likely
  3. Waiting for CMIP6 projected climate data which will include sub-daily temperature values (e.g., every 2 hours)

Questions and Next Steps

Is this a good way to combine / average results from multiple models, multiple years?

Should we use actual or modelled data for the historical baseline?

Would it be useful to also compute chill hours? Other agroclimatic metrics?

Do you always start computing chill portions on Sept 1? Should this be a variable?

Add caveats about not looking at a single model, single-year.

Turn this into a Shiny app with selectable start date and required number of chill portions.

LS0tDQp0aXRsZTogIk1vZGVsaW5nIENoaWxsIEFjY3VtdWxhdGlvbiBVbmRlciBDbGltYXRlIENoYW5nZSB3aXRoIENhbC1BZGFwdCBEYXRhIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCi0tLQ0KDQpgYGB7Y3NzIGVjaG8gPSBGQUxTRX0NCmgxIHsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGZvbnQtc2l6ZTogMjRweDsNCiAgY29sb3I6IGRhcmtvbGl2ZWdyZWVuOw0KICBib3JkZXItdG9wOiAzcHggc29saWQgZGltZ3JleTsNCiAgbWFyZ2luLXRvcDogMWVtOw0KICBwYWRkaW5nLXRvcDogMC41ZW07DQp9DQpoMS50aXRsZSB7DQogIGNvbG9yOiBibGFjazsNCiAgYm9yZGVyOiBub25lOw0KfQ0KaDIgew0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1zaXplOiAyMnB4Ow0KICBjb2xvcjogZGltZ3JheTsNCn0NCg0KaDMgew0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1zaXplOiAxOHB4Ow0KICBjb2xvcjogYmxhY2s7DQp9DQoNCmBgYA0KDQo8cD48aW1nIHNyYz0iaHR0cHM6Ly91Y2Fuci1pZ2lzLmdpdGh1Yi5pby9jYWxhZGFwdHIvcmVmZXJlbmNlL2ZpZ3VyZXMvY2FsYWRhcHRyLWJldGFfbG9nby5zdmciIHdpZHRoPSIyNDAiIC8+PC9wPg0KDQojIEFib3V0IHRoaXMgUiBOb3RlYm9vaw0KDQpSIE5vdGVib29rcyBhcmUgYSAnZmxhdm9yJyBvZiBSIG1hcmtkb3duIHRoYXQgY29tYmluZSBwbGFpbiB0ZXh0IGFuZCBSIGNvbW1hbmRzIGluIGNvZGUgY2h1bmtzLiAoWW91IGNhbiBkb3dubG9hZCB0aGUgUm1kIGZpbGUgZnJvbSB0aGUgJ2NvZGUnIGJ1dHRvbiBhdCB0aGUgdG9wIG9mIHRoZSBwYWdlLikgWW91IHJ1biBjb2RlIGNodW5rcyBpbiB0aGUgZG9jdW1lbnQgbGluZS1ieS1saW5lLCBhbmQgdGhlIG91dHB1dCBhcHBlYXJzIGltbWVkaWF0ZWx5IGJlbG93IHRoZSBjb2RlIGNodW5rLg0KDQpJZiB5b3UncmUgaW4gUlN0dWRpbywgeW91IGNhbiAqbWluaW1pemUgdGhlIGNvbnNvbGUgd2luZG93KiAoYW5kIHByb2JhYmx5IGNsb3NlIHRoZSByaWdodC1oYW5kIHBhbmVzIGFzIHdlbGwpLiBZb3Ugd29uJ3QgbmVlZCBpdCwgIGJlY2F1c2Ugd2hlbiB5b3UgcnVuIFIgY29tbWFuZHMgaW4gYSBSIE5vdGVib29rIHRoZSAqb3V0cHV0IGFwcGVhcnMgYmVsb3cgdGhlIGNvZGUgY2h1bmsqIChub3QgdGhlIGNvbnNvbGUpLiBUaGlzIHRha2VzIHNvbWUgZ2V0dGluZyB1c2VkIHRvIGlmIHlvdSdyZSB1c2VkIHRvIHdvcmtpbmcgYXQgdGhlIGNvbnNvbGUuIA0KDQpLZXlib2FyZCBzaG9ydGN1dHMgeW91IGNhbiB1c2Ugd2l0aGluIGEgUiBOb3RlYm9vazogIA0KDQotIHJ1biB0aGUgY3VycmVudCBsaW5lIG9mIFI6ICpjdHJsICsgZW50ZXIqICANCi0gcnVuIGV2ZXJ5dGhpbmcgaW4gdGhlIGN1cnJlbnQgY29kZSBjaHVuazogKmN0cmwgKyBzaGlmdCArIGVudGVyKiAgDQotIGluc2VydCBhIG5ldyBjb2RlIGNodW5rOiAqY3RybCArIGFsdCArIGkqICANCg0KIyBDaGlsbCBQb3J0aW9uczogQmFja2dyb3VuZA0KDQpbJ0NoaWxsIGhvdXJzJ10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ2hpbGxpbmdfcmVxdWlyZW1lbnQpIGFuZCAnQ2hpbGwgcG9ydGlvbnMnIGFyZSBjb21tb25seSB1c2VkIHRvIGVzdGltYXRlIHdoZW4gdHJlZSBjcm9wcyB3aWxsICJ3YWtlIHVwIiBmcm9tIHRoZWlyIHdpbnRlciBzbHVtYmVyIGFuZCBzdGFydCB0byBncm93IHRoZWlyIGZydWl0IC8gbnV0cy4gVGhlIHRpbWluZyBhbmQgcmF0ZSBvZiBjaGlsbCBhbHNvIGFmZmVjdHMgZnJ1aXQgcHJvZHVjdGl2aXR5IGFuZCBzY2hlZHVsaW5nIG9mIG1hbmFnZW1lbnQgYWN0aW9ucyAoaW5jbHVkaW5nIGhhcnZlc3QpLiBNYW55IHRyZWUgY3JvcHMgKGUuZy4sIG51dHMpIHJlcXVpcmUgYSBjZXJ0YWluIG51bWJlciBvZiBob3VycyBhdCBjaGlsbHkgdGVtcGVyYXR1cmVzIGluIG9yZGVyIGZvciB0aGUgdHJlZSB0byBibG9zc29tIGFuZCB0aGUgZnJ1aXQgdG8gcmlwZW4uIFRoaXMgcHJlc2VudHMgYSBjb25jZXJuIGZvciBjbGltYXRlIGNoYW5nZSwgYmVjYXVzZSBpZiB0aGUgd2ludGVycyBhcmVuJ3QgY29sZCBlbm91Z2ggdGhlIG9wZXJhdGlvbiBtYXkgbm8gbG9uZ2VyIGJlIGVjb25vbWljYWxseSB2aWFibGUuIA0KDQpDb21wdXRpbmcgJ2NoaWxsIGhvdXJzJyBlc3NlbnRpYWxseSBpbnZvbHZlcyBhZGRpbmcgdXAgdGhlIHRvdGFsIGFtb3VudCBvZiB0aW1lIGR1cmluZyB0aGUgY29sZCBzZWFzb24gd2l0aGluIGEgIHRlbXBlcmF0dXJlIHJhbmdlIChlLmcuLCAzMiAtIDQ1ICYjMTc2O0YpLiAnQ2hpbGwgcG9ydGlvbnMnIGlzIGEgc2ltaWxhciBjYWxjdWxhdGlvbiwgaG93ZXZlciBpbnN0ZWFkIG9mIHNpbXBseSBhZGRpbmcgdXAgdGhlIG51bWJlciBvZiBob3VycyB3aXRoaW4gYSBjZXJ0YWluIHRlbXBlcmF0dXJlIHJhbmdlLCBhZGRpdGlvbmFsIHdlaWdodHMgYXJlIGFzc2lnbmVkIGJhc2VkIG9uIGJhbmRzIG9mIHRlbXBlcmF0dXJlIGFuZC9vciB3YXJtaW5nIHBlcmlvZHMgcGVyaW9kcyBiZXR3ZWVuIGNvbGQgc3BlbGxzICh0aGlzIG1pbWljcyB0cmVlIHBoeXNpb2xvZ3kgdGh1cyBwcm9kdWNpbmcgYmV0dGVyIHByZWRpY3Rpb25zKS4NCg0KQ29tcHV0aW5nIGNoaWxsIGhvdXJzIGFuZCBjaGlsbCBwb3J0aW9ucyByZXF1aXJlcyBob3VybHkgdGVtcGVyYXR1cmUgZGF0YS4gWW91IG1heSBiZSBsdWNreSBlbm91Z2ggdG8gaGF2ZSBob3VybHkgdGVtcGVyYXR1cmUgcmVjb3JkaW5ncyBmcm9tIHdlYXRoZXIgc3RhdGlvbnMgZm9yIGhpc3RvcmljIGFuZCByZWFsLXRpbWUgYW5hbHlzZXMuIEhvd2V2ZXIgZm9yIHByb2plY3RlZCBjbGltYXRlIHNjZW5hcmlvcywgdGhlIHNtYWxsZXN0IHVuaXQgb2YgdGltZSBmb3IgdGhlIENNSVA1IGZhbWlseSBvZiBjbGltYXRlIG1vZGVscyBpcyBkYWlseSBtaW4gYW5kIG1heC4gVGhpcyBjcmVhdGVzIGEgY29udW5kcnVtIGlmIHlvdSB3YW50IHRvIGV4cGxvcmUgaG93IGN1bXVsYXRpdmUgY2hpbGwgaG91cnMgbWF5IGNoYW5nZSBkdWUgdG8gY2xpbWF0ZSBjaGFuZ2UuDQoNClRvIGRlYWwgd2l0aCB0aGlzLCB3ZSBjYW4gZXN0aW1hdGUgaG91cmx5IHRlbXBlcmF0dXJlcyBiYXNlZCBvbiB0aGUgZGFpbHkgbWluaW11bSBhbmQgbWF4aW11bSB0ZW1wZXJhdHVyZS4gVGhlIG1vZGVsIHRoYXQgZG9lcyB0aGlzIHVzZXMgdGhlIHRpbWUgb2Ygc3VucmlzZSBhbmQgc3Vuc2V0IHRvIG1vZGVsIHRlbXBlcmF0dXJlIHZhcmlhdGlvbiBvdmVyIHRoZSBkYXkuIFRoZSBbKipjaGlsbFIqKl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvcGFja2FnZT1jaGlsbFIpIHBhY2thZ2UsIGRldmVsb3BlZCBieSBbRHIuIEVpa2UgTHVlZGVsaW5nXShodHRwOi8vZWlrZWx1ZWRlbGluZy5jb20vKSwgaGFzIGZ1bmN0aW9ucyBmb3IgbW9kZWxpbmcgaG91cmx5IHRlbXBlcmF0dXJlcyBhbmQgY29tcHV0aW5nIGNoaWxsIHBvcnRpb25zIChzZWUgdGhlIGNoaWxsUiBbVmlnbmV0dGVdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9jaGlsbFIvdmlnbmV0dGVzL2hvdXJseV90ZW1wZXJhdHVyZXMuaHRtbCkgZm9yIGRldGFpbHMpLg0KDQpTZWUgYWxzbzoNCg0KTHVlZGVsaW5nLCBFLiwgTS4gWmhhbmcgYW5kIEUuIEguIEdpcnZldHogKDIwMDkpLiAqQ2xpbWF0aWMgQ2hhbmdlcyBMZWFkIHRvIERlY2xpbmluZyBXaW50ZXIgQ2hpbGwgZm9yIEZydWl0IGFuZCBOdXQgVHJlZXMgaW4gQ2FsaWZvcm5pYSBkdXJpbmcgMTk1MOKAkzIwOTkqLiBbUExPUyBPTkUgNCg3KTogZTYxNjZdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMzcxL2pvdXJuYWwucG9uZS4wMDA2MTY2KS4NCg0KIyMgRXhlcmNpc2UgU3VtbWFyeQ0KDQpJbiB0aGlzIGV4YW1wbGUsIHdlOg0KDQoxKSBEb3dubG9hZCBmcm9tIENhbC1BZGFwdCBkb3duc2NhbGVkICoqZGFpbHkgcHJvamVjdGVkIG1pbmltdW0gYW5kIG1heGltdW0gdGVtcGVyYXR1cmVzKiogZm9yIGEgKipzaW5nbGUgcG9pbnQqKiBvbiB0aGUgd2VzdCBzaWRlIG9mIHRoZSBTYW4gSm9hcXVpbiBWYWxsZXkgZnJvbSBTZXB0ZW1iZXIgMjA4MCB0aHJ1IEp1bmUgMjA5MCAoKioxMCBncm93aW5nIHNlYXNvbnMqKiksIGZvciAqKnR3byBlbWlzc2lvbnMgc2NlbmFyaW9zKiogYW5kICoqMTAgR0NNcyoqIChnbG9iYWwgY2xpbWF0ZSBtb2Rlc2wpLg0KDQoyKSBVc2UgY2hpbGxSIHRvIGNvbXB1dGUgYSkgbW9kZWxlZCBob3VybHkgdGVtcGVyYXR1cmVzLCBhbmQgYikgYWNjdW11bGF0ZWQgY2hpbGwgcG9ydGlvbnMuDQoNCjMpIFBsb3QgdGhlIHJlc3VsdHMNCg0KKipOb3RlOioqIFRoaXMgbm90ZWJvb2sgaXMgcHJpbWFyaWx5IGludGVuZGVkIHRvIGRlbW9uc3RyYXRlICpob3cgdG8gd3JpdGUgY29kZSogdG8gY29tcHV0ZSBjaGlsbCBwb3J0aW9ucyB1c2luZyBwcm9qZWN0ZWQgY2xpbWF0ZSBkYXRhLiBJdCBpcyAqbm90KiBwcmVzZW50ZWQgYXMgKmJlc3QgcHJhY3RpY2UqIGZvciBhc3Nlc3NpbmcgdGhlIHZpYWJpbGl0eSBvZiB0cmVlIGNyb3BzIHVuZGVyIGNsaW1hdGUgY2hhbmdlIQ0KDQpXYW50IHRvIHNraXAgdGhlIGRldGFpbHM/IFlvdSBjYW4gWyoqJmRkYXJyOyBjdXQgdG8gdGhlIGNoYXNlICZkZGFycjsqKl0oI2NvbmNsdXNpb25zKS4NCg0KIyBTZXR1cA0KDQpUaGUgZm9sbG93aW5nIGNodW5rIHdpbGwgaW5zdGFsbCBhbnkgcGFja2FnZXMgeW91IGRvbid0IGFscmVhZHkgaGF2ZSB0aGF0IHdpbGwgYmUgbmVlZGVkIGJlbG93Og0KDQpgYGB7ciBsb2FkX3BrZ3MsIGNhY2hlID0gVFJVRX0NCnBrZ3NfcmVxIDwtIGMoInJlbW90ZXMiLCAiZ2dwbG90MiIsICJkcGx5ciIsICJ0bWFwIiwgImNvbmZsaWN0ZWQiLCAidGlkeXIiLCAibHVicmlkYXRlIiwgInRpYmJsZSIsICJjaGlsbFIiLCAic3RyaW5nciIsICJtYWdyaXR0ciIpDQpwa2dzX21pc3NpbmcgPC0gcGtnc19yZXFbIShwa2dzX3JlcSAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpWywiUGFja2FnZSJdKV0NCmlmIChsZW5ndGgocGtnc19taXNzaW5nKSkgaW5zdGFsbC5wYWNrYWdlcyhwa2dzX21pc3NpbmcsIGRlcGVuZGVuY2llcz1UUlVFKQ0KYGBgDQoNCkluc3RhbGwgYGNhbGFkYXB0cmAgZnJvbSBHaXRIdWIgKGlmIG5lZWRlZCk6DQoNCmBgYHtyfQ0KIyMgVW5jb21tZW50IGFuZCBydW4gdGhlIGZvbGxvd2luZyBsaW5lIGlmIHlvdSBkb24ndCBoYXZlIHRoZSBsYXRlc3QgdmVyc2lvbiBvZiBjYWxhZGFwdHINCiMjIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigidWNhbnItaWdpcy9jYWxhZGFwdHIiKQ0KYGBgDQoNCkxvYWQgYWxsIHRoZSBsaWJyYXJpZXMgd2UnbGwgYmUgdXNpbmcgYmVsb3c6DQoNCmBgYHtyIGxpYnJhcnlfYWxsLCBtZXNzYWdlID0gRkFMU0V9DQpsaWJyYXJ5KGNhbGFkYXB0cikNCmxpYnJhcnkodW5pdHMpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShjb25mbGljdGVkKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeSh0aWJibGUpDQpsaWJyYXJ5KGNoaWxsUikNCmxpYnJhcnkobWFncml0dHIpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KHNmKQ0KYGBgDQoNClRoZSBsYXN0IHNldHVwIHRhc2sgaXMgdG8gZGVmaW5lIHlvdXIgcHJlZmVyZW5jZXMgd2hlbiB5b3UncmUgZm9yY2VkIHRvIHVzZSBhbiBhbWJpZ3VvdXMgZnVuY3Rpb24gbmFtZSAoaS5lLiwgYSBmdW5jdGlvbiB0aGF0IGV4aXN0cyBpbiBtb3JlIHRoYW4gb25lIHBhY2thZ2UpLiBUaGlzIGlzIHBhcnRpY3VsYXJseSBuZWVkZWQgd2l0aCBhIGZldyBjb21tb24gZ2VuZXJpYyBmdW5jdGlvbnMgZnJvbSBgZHBseXJgOg0KDQpgYGB7ciBzZXRfY29uZmxpY3RzfQ0KY29uZmxpY3RfcHJlZmVyKCJmaWx0ZXIiLCAiZHBseXIiLCBxdWlldCA9IFRSVUUpDQpjb25mbGljdF9wcmVmZXIoImNvdW50IiwgImRwbHlyIiwgcXVpZXQgPSBUUlVFKQ0KY29uZmxpY3RfcHJlZmVyKCJzZWxlY3QiLCAiZHBseXIiLCBxdWlldCA9IFRSVUUpDQpgYGANCg0KDQojIEdldHRpbmcgU3RhcnRlZDogQ2hpbGwgUG9ydGlvbnMgZm9yIGEgU2luZ2xlIFNlYXNvbg0KDQojIyAxKSBDcmVhdGUgYSBDYWwtQWRhcHQgQVBJIFJlcXVlc3QNCg0KVGhlIGZpcnN0IHN0ZXAgaW4gZ2V0dGluZyBjbGltYXRlIGRhdGEgd2l0aCBjYWxhZGFwdFIgaXMgdG8gY3JlYXRlIGFuIEFQSSByZXF1ZXN0IG9iamVjdC4gVGhpcyBpbnZvbHZlcyBzdHJpbmdpbmcgdG9nZXRoZXIgYSBzZXJpZXMgb2YgZnVuY3Rpb25zIHRvZ2V0aGVyIHRoYXQgc3BlY2lmeSB0aGUgcGllY2VzIG9mIHRoZSByZXF1ZXN0LiANCg0KVG8gaWxsdXN0cmF0ZSBob3cgdG8gd3JpdGUgY29kZSBmb3IgdGhpcyBhbmFseXNpcywgd2UnbGwgY3JlYXRlIGFuIEFQSSByZXF1ZXN0IHRvIHF1ZXJ5IGEgc2luZ2xlIHBvaW50LCBzaW5nbGUgR0NNLCBhbmQgYSBzaW5nbGUgc2NlbmFyaW8gZm9yIGEgc2luZ2xlIHNlYXNvbi4gVGhpcyBvZiBjb3Vyc2UgaXMgbm90IHRoZSByZWNvbW1lbmRlZCBwcmFjdGljZSwgYW5kIHlvdSBzaG91bGQgYWx3YXlzIGxvb2sgYXQgYSBtaW5pbXVtIDEwLTIwIHllYXIgdGltZSBzcGFucyBhbmQgdGFrZSBhdmVyYWdlcy4gDQoNCmBgYHtyfQ0KIyMgUGljayBhIGxvY2F0aW9uDQpwdDFfeHkgPC0gYygtMTE5Ljk2NCwgMzYuMDE5KQ0KDQojIyBDcmVhdGUgdGhlIEFQSSBSZXF1ZXN0IG9iamVjdA0KcHQxX2NhcCA8LSBjYV9sb2NfcHQoY29vcmRzID0gcHQxX3h5KSAlPiUNCiAgY2FfZ2NtKGdjbXNbMV0pICU+JQ0KICBjYV9zY2VuYXJpbygicmNwNDUiKSAlPiUNCiAgY2FfcGVyaW9kKCJkYXkiKSAlPiUNCiAgY2FfZGF0ZXMoc3RhcnQgPSAiMjA4MC0wOS0wMSIsIGVuZCA9ICIyMDgxLTA2LTMwIikgJT4lDQogIGNhX2N2YXIoYygidGFzbWF4IiwgInRhc21pbiIpKQ0KDQpwdDFfY2FwDQpgYGANClRvIHZlcmlmeSB0aGUgbG9jYXRpb24gc2F2ZWQgaW4gYW4gQVBJIHJlcXVlc3QsIHlvdSBjYW4gcGxvdCBpdC4gKE5vdGUgd2Ugc3RpbGwgaGF2ZW4ndCBmZXRjaGVkIGFueSBkYXRhIHlldCwgdGhpcyBqdXN0IHNob3dzIHlvdSB0aGUgbG9jYXRpb24gdGhlIHJlcXVlc3Qgd2lsbCBhc2sgZm9yLikNCg0KYGBge3IgcGxvdF9jYXAxfQ0KcGxvdChwdDFfY2FwKQ0KYGBgDQoNCiMjIDIpIEZldGNoIERhdGEgZnJvbSBDYWwtQWRhcHQNCg0KTmV4dCB3ZSBmZXRjaCB0aGUgdGVtcGVyYXR1cmUgZGF0YSB3aXRoIGBjYV9nZXR2YWxzX3RibCgpYCwgd2hpY2ggcmV0dXJucyBhIHRpYmJsZS4gV2UgdGhlbiB1c2UgYG11dGF0ZSgpYCB0byBjcmVhdGUgYSBuZXcgY29sdW1uIHdpdGggdGhlIHRlbXBlcmF0dXJlIHZhbHVlcyBpbiBDZWxzaXVzLg0KDQpgYGB7ciBjYXAxX2xzdCwgY2FjaGUgPSBUUlVFfQ0KcHQxX3RibCA8LSBwdDFfY2FwICU+JSANCiAgY2FfZ2V0dmFsc190YmwocXVpZXQgPSBUUlVFKSAlPiUgDQogIG11dGF0ZSh0ZW1wX2MgPSBzZXRfdW5pdHModmFsLCBkZWdDKSkgJT4lIA0KICBzZWxlY3QoaWQsIGN2YXIsIHBlcmlvZCwgZ2NtLCBzY2VuYXJpbywgZHQsIHRlbXBfYykNCg0KaGVhZChwdDFfdGJsKQ0KYGBgDQoNCiMjIDMpIFdyYW5nbGUgUmVzdWx0cyBpbnRvIHRoZSBGb3JtYXQgUmVxdWlyZWQgYnkgYGNoaWxsUmANCg0KR3VpZGVkIGJ5IGNoaWxsUidzIGRvY3VtZW50YXRpb24gYW5kIFZpZ25ldHRlIG9uIFtob3VybHkgdGVtcGVyYXR1cmUgcmVjb3Jkc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2NoaWxsUi92aWduZXR0ZXMvaG91cmx5X3RlbXBlcmF0dXJlcy5odG1sKSwgd2UgcHJlcGFyZSBhIGZpdmUtY29sdW1uIHRpYmJsZSB3aXRoIHRoZSBwcm9qZWN0ZWQgdGVtcGVyYXR1cmUgdmFsdWVzOg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXIpOyBsaWJyYXJ5KGx1YnJpZGF0ZSkNCg0KcHQxX2R5cl90YmwgPC0gcHQxX3RibCAlPiUNCiAgbXV0YXRlKERBVEUgPSBhcy5QT1NJWGN0KGZvcm1hdChkdCksIHR6PSJBbWVyaWNhL0xvc19BbmdlbGVzIikpICU+JQ0KICBtdXRhdGUoWWVhciA9IGFzLmludGVnZXIoeWVhcihEQVRFKSksIA0KICAgICAgICAgTW9udGggPSBhcy5pbnRlZ2VyKG1vbnRoKERBVEUpKSwgDQogICAgICAgICBEYXkgPSBkYXkoREFURSksDQogICAgICAgICB0ZW1wX2MgPSBhcy5udW1lcmljKHRlbXBfYykpICU+JQ0KICBzZWxlY3QoY3ZhciwgdGVtcF9jLCBZZWFyLCBNb250aCwgRGF5KSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGN2YXIsIHZhbHVlc19mcm9tID0gdGVtcF9jKSAlPiUNCiAgcmVuYW1lKFRtYXggPSB0YXNtYXgsIFRtaW4gPSB0YXNtaW4pDQoNCmhlYWQocHQxX2R5cl90YmwpDQpgYGANCg0KIyMgNCkgTW9kZWwgSG91cmx5IFRlbXBlcmF0dXJlDQoNCk5vdyB3ZSdyZSByZWFkeSB0byBjb21wdXRlIHRoZSBob3VybHkgdGVtcHMuIEluIGFkZGl0aW9uIHRvIHRoZSBkYWlseSBtaW4vbWF4IHZhbHVlcywgd2UgbmVlZCB0byBwYXNzIHRoZSBsYXRpdHVkZSBvZiBvdXIgc2l0ZSAod2hpY2ggaXMgdXNlZCB0byBkZXRlcm1pbmUgc3VucmlzZSBhbmQgc3Vuc2V0IHRpbWUpOg0KDQpgYGB7ciBjb21wdXRlX2hvdXJseSwgY2FjaGUgPSBUUlVFLCBtZXNzYWdlID0gRkFMU0V9DQpsaWJyYXJ5KGNoaWxsUikNCg0KcHQxX2hydG1wX3dpZGUgPC0gbWFrZV9ob3VybHlfdGVtcHMobGF0aXR1ZGUgPSBwdDFfeHlbMl0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXJfZmlsZSA9IHB0MV9keXJfdGJsLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZWVwX3N1bnJpc2Vfc3Vuc2V0ID0gRkFMU0UpDQpoZWFkKHB0MV9ocnRtcF93aWRlKQ0KYGBgDQoNCiMjIDUpIFBsb3QgTW9kZWxsZWQgSG91cmx5IFRlbXBzDQoNClRvIHBsb3QgdGhlIGhvdXJseSB0ZW1wZXJhdHVyZXMsIHdlIG5lZWQgdG8gY29udmVydCB0aGUgJ3dpZGUnIGZvcm1hdCBvZiB0aGUgdGFibGUgdG8gYSAnbG9uZycgZm9ybWF0LiBGb3J0dW5hdGVseSBgdGlkeXJgIGhhcyBldmVyeXRoaW5nIHdlIG5lZWQgdG8gW3Bpdm90IGJldHdlZW4gbG9uZyBhbmQgd2lkZSBmb3JtYXRzXShodHRwczovL3RpZHlyLnRpZHl2ZXJzZS5vcmcvYXJ0aWNsZXMvcGl2b3QuaHRtbCl7dGFyZ2V0PSJfYmxhbmsiIHJlbD0ibm9vcGVuZXIifTogDQoNCmBgYHtyfQ0KcHQxX2hydG1wX2xvbmcgPC0gcHQxX2hydG1wX3dpZGUgJT4lDQogIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoIkhvdXJfIiksDQogICAgICAgICAgICAgICBuYW1lc190byA9ICJIb3VyIiwNCiAgICAgICAgICAgICAgIG5hbWVzX3ByZWZpeCA9ICJIb3VyXyIsDQogICAgICAgICAgICAgICBuYW1lc190cmFuc2Zvcm0gPSBsaXN0KGhvdXIgPSBhcy5pbnRlZ2VyKSwNCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJ0ZW1wX2MiKSAlPiUNCiAgbXV0YXRlKGRhdGVfaG91ciA9IElTT2RhdGV0aW1lKFllYXIsIE1vbnRoLCBEYXksIEhvdXIsIDAsIDAsIHR6ID0gIkFtZXJpY2EvTG9zX0FuZ2VsZXMiKSkgJT4lDQogIHNlbGVjdChkYXRlX2hvdXIsIHRlbXBfYykgJT4lDQogIGFycmFuZ2UoZGF0ZV9ob3VyKQ0KDQpoZWFkKHB0MV9ocnRtcF9sb25nKQ0KYGBgDQoNCk5vdyB3ZSBjYW4gcGxvdCB0aGUgaG91cmx5IHRlbXBlcmF0dXJlcy4gVG8gaWxsdXN0cmF0ZSB3ZSdsbCBwbG90IGp1c3QgdGhlIG1vbnRoIG9mIERlY2VtYmVyLCAyMDc5Og0KDQpgYGB7ciBwbG90X2hvdXJseV90ZW1wLCBjYWNoZSA9IFRSVUV9DQp0c19zdGFydCA8LSBhcy5EYXRlKCIyMDgwLTEyLTAxIikNCnRzX2VuZCA8LSBhcy5EYXRlKCIyMDgwLTEyLTMxIikNCg0KcHQxX2hydG1wX29uZXdrIDwtIHB0MV9ocnRtcF9sb25nICU+JSANCiAgZmlsdGVyKGRhdGVfaG91ciA+PSB0c19zdGFydCwgZGF0ZV9ob3VyIDw9IHRzX2VuZCkNCg0KZ2dwbG90KGRhdGEgPSBwdDFfaHJ0bXBfb25ld2ssDQogICAgICAgYWVzKHggPSBkYXRlX2hvdXIsIHkgPSB0ZW1wX2MpKSArDQogIGdlb21fbGluZShhZXMoY29sb3I9InJlZCIpLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIGxhYnModGl0bGUgPSAiTW9kZWxsZWQgSG91cmx5IFRlbXBlcmF0dXJlIGZvciBQdCAxOiBSQ1AgNC41IiwgDQogICAgIHN1YnRpdGxlID0gIkRlY2VtYmVyIDEtMzEsIDIwODAiLA0KICAgICBjYXB0aW9uID0gIkdDTTogSGFkR0VNMi1FUyIsDQogICAgIHggPSAiZGF0ZSIsIHkgPSAiQ2hpbGwgUG9ydGlvbiIpICsNCiAgdGhlbWUocGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMCksDQogICAgICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDIwLCAyMCwgMjAsIDIwKSwNCiAgICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmF5NTAiLCBzaXplID0gMykpDQoNCmBgYA0KDQojIyA2KSBDb21wdXRlIENoaWxsIFBvcnRpb25zDQoNClRoZSBgRHluYW1pY19Nb2RlbCgpYCBmdW5jdGlvbiBmcm9tIGBjaGlsbFJgIGNvbXB1dGVzIGN1bXVsYXRpdmUgY2hpbGwgcG9ydGlvbnMuIFdlIGNhbiBhZGQgdGhlc2UgdmFsdWVzIHRvIHRoZSB0YWJsZSBhcyBhIG5ldyBjb2x1bW4gd2l0aCBgbXV0YXRlKClgOiAgDQoNCmBgYHtyfQ0KcHQxX2hydG1wY2hpbGxfbG9uZyA8LSBwdDFfaHJ0bXBfbG9uZyAlPiUgDQogIG11dGF0ZShhY2N1bV9jaGlsbF9wcnRuID0gRHluYW1pY19Nb2RlbChwdDFfaHJ0bXBfbG9uZyR0ZW1wX2MpKQ0KDQpoZWFkKHB0MV9ocnRtcGNoaWxsX2xvbmcpDQpgYGANCg0KTGFzdGx5LCB3ZSBwbG90IHRoZSBhY2N1bXVsYXRlZCBjaGlsbCBwb3J0aW9ucyB3aXRoIGBnZ3Bsb3QoKWA6DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBwdDFfaHJ0bXBjaGlsbF9sb25nLA0KICAgICAgIGFlcyh4ID0gZGF0ZV9ob3VyLCB5ID0gYWNjdW1fY2hpbGxfcHJ0bikpICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvcj0icmVkIiksIHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgbGFicyh0aXRsZSA9ICJQcm9qZWN0ZWQgQ2hpbGwgUG9ydGlvbiBBY2N1bXVsYXRpb24gZm9yIFB0IDE6IFJDUCA0LjUiLCANCiAgICAgICBzdWJ0aXRsZSA9ICJTZXB0IDIwODAgLSBKdW5lIDIwODEiLA0KICAgICAgIGNhcHRpb24gPSAiR0NNOiBIYWRHRU0yLUVTIiwNCiAgICAgICB4ID0gImRhdGUiLCB5ID0gIkNoaWxsIFBvcnRpb24iKSArDQogIHRoZW1lKHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDApLA0KICAgICAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigyMCwgMjAsIDIwLCAyMCksDQogICAgICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JheTUwIiwgc2l6ZSA9IDMpKQ0KDQpgYGANCg0KIyMgNykgSWRlbnRpZnkgd2hlbiBBY2N1bXVsYXRlZCBDaGlsbCBSZWFjaGVzIGEgVGhyZXNoaG9sZA0KDQpTdXBwb3NlIHlvdSdyZSBob3BpbmcgdG8gZ3JvdyB0aGUgS2VybWFuIHZhcmlldHkgb2YgUGlzdGFjaGlvcyBpbiAyMDgwLCB3aGljaCByZXF1aXJlIFsqKjU0IGFjY3VtdWxhdGVkIGNoaWxsIGhvdXJzKipdDQooaHR0cDovL2ZydWl0c2FuZG51dHMudWNkYXZpcy5lZHUvV2VhdGhlcl9TZXJ2aWNlcy9jaGlsbGluZ19hY2N1bXVsYXRpb25fbW9kZWxzL0Nyb3BDaGlsbFJlcS8pIHRvIHJpcGVuLiBXZSBjYW4gZWFzaWx5IGZpbmQgdGhlIGRhdGUgd2hlbiB0aGUgcmVxdWlyZWQgYWNjdW11bGF0ZWQgY2hpbGwgaXMgcHJlZGljdGVkIHRvIGJlIHJlYWNoZWQ6DQoNCg0KYGBge3J9DQpjaGlsbF9taW4gPC0gNTQNCg0KY2hpbGxfZGF0ZSA8LSBwdDFfaHJ0bXBjaGlsbF9sb25nICU+JSANCiAgZmlsdGVyKGFjY3VtX2NoaWxsX3BydG4gPiBjaGlsbF9taW4pICU+JSANCiAgc2xpY2UoMSkgJT4lIA0KICBwdWxsKGRhdGVfaG91cikgJT4lIA0KICBhcy5EYXRlKCkNCg0KY2hpbGxfZGF0ZQ0KYGBgDQoNCiMgQ29tcGFyZSBDaGlsbCBBY2N1bXVsYXRpb24gZm9yIE11bHRpcGxlIEdyb3dpbmcgU2Vhc29ucywgQ2xpbWF0ZSBNb2RlbHMsIGFuZCBFbWlzc2lvbnMgU2NlbmFyaW9zDQoNCkNsaW1hdGUgbW9kZWxzIGFyZSBub3QgY3J5c3RhbCBiYWxscywgc28gdGhpbmtpbmcgYWJvdXQgdGhlIGNvbnNlcXVlbmNlcyBvZiBjbGltYXRlIGNoYW5nZSBnZW5lcmFsbHkgaW52b2x2ZXMgbG9va2luZyBhdCB0aGUgcHJlZGljdGlvbnMgZnJvbSBhKSBhIG51bWJlciBvZiBjbGltYXRlIG1vZGVscyAoR0NNcyksIDIpIG11bHRpcGxlIGVtaXNzaW9ucyBzY2VuYXJpb3MsIGFuZCAzKSBtdWx0aXBsZSB5ZWFycy4NCg0KSGVyZSB3ZSdsbCBjb21wdXRlIHRoZSBkYXRlIHdoZW4gdGhlIGNoaWxsIHBvcnRpb24gcmVhY2hlcyAzNiBmb3IgdGhlICoqMTAgR0NNcyoqIHJlY29tbWVuZGVkIGZvciBDYWxpZm9ybmlhLCBhbmQgKioxMCBncm93aW5nIHNlYXNvbnMqKi4gVGhhdCBzaG91bGQgZ2l2ZSB1cyAxMDAgbWF0dXJhdGlvbiBkYXRlcywgd2hpY2ggd2lsbCBwbG90IHVzaW5nIGEgYm94IHBsb3QuIFdlJ2xsIGRvIHRoaXMgdHdpY2UsIG9uY2Ugd2l0aCBSQ1AgNC41IChsb3cgZW1pc3Npb25zIHNjZW5hcmlvKSBhbmQgUkNQIDguNSAoaGlnaGVyIGVtaXNzaW9uIHNjZW5hcmlvKS4NCg0KIyMgMSkgQ3JlYXRlIHRoZSBBUEkgUmVxdWVzdA0KDQpgYGB7ciBjYXAyX21ha2UsIGNhY2hlPVRSVUV9DQojIyBQaWNrIGEgbG9jYXRpb24NCnB0MV94eSA8LSBjKC0xMTkuOTY0LCAzNi4wMTkpDQoNCnB0MV8xMHlyc19jYXAgPC0gY2FfbG9jX3B0KGNvb3JkcyA9IHB0MV94eSkgJT4lDQogIGNhX2djbShnY21zWzE6MTBdKSAlPiUNCiAgY2Ffc2NlbmFyaW8oYygicmNwNDUiLCAicmNwODUiKSkgJT4lDQogIGNhX3BlcmlvZCgiZGF5IikgJT4lDQogIGNhX2RhdGVzKHN0YXJ0ID0gIjIwODAtMDktMDEiLCBlbmQgPSAiMjA5MC0wNi0zMCIpICU+JQ0KICBjYV9jdmFyKGMoInRhc21heCIsICJ0YXNtaW4iKSkNCg0KcHQxXzEweXJzX2NhcA0KYGBgDQoNCiMjIDIpIEZldGNoIERhdGENCg0KT3VyIEFQSSByZXF1ZXN0IGFza3MgZm9yID4xNDAsMDAwIHRlbXBlcmF0dXJlIHZhbHVlcywgc28gZmV0Y2hpbmcgdGhlIGRhdGEgY291bGQgdGFrZSBhIG1pbnV0ZSBvciB0d28uDQoNCmBgYHtyIHB0MV8xMHlyc190YmxfZmV0Y2gsIGNhY2hlID0gVFJVRX0NCnB0MV8xMHlyc190YmwgPC0gcHQxXzEweXJzX2NhcCAlPiUgY2FfZ2V0dmFsc190YmwocXVpZXQgPSBUUlVFKSANCg0KZGltKHB0MV8xMHlyc190YmwpDQpoZWFkKHB0MV8xMHlyc190YmwpDQpgYGANCg0KIyMgMykgTXVuZ2UgdGhlIFJlc3VsdHMNCg0KVGhpcyB0aWJibGUgaGFzIGRhaWx5IG1pbi9tYXggdmFsdWVzIGZyb20gU2VwdGVtYmVyIDEgMjA3OSB0aHJ1IEp1bmUgMzAsIDIwOTAuIFdlIG5lZWQgdG8gYnJlYWsgaXQgdXAgaW50byBncm91cHMgYnkgMSkgZ3Jvd2luZyBzZWFzb24sIDIpIEdDTSwgYW5kIDMpIFJDUC4gR3JvdXBpbmcgaXQgdXAgYnkgR0NNIGFuZCBSQ1Agd2lsbCBiZSBlYXN5IC0gdGhlcmUgYXJlIGFscmVhZHkgY29sdW1ucyBmb3IgdGhvc2UuIFdlIG5lZWQgdG8gYWRkIGEgY29sdW1uIGZvciBncm93aW5nIHNlYXNvbiBob3dldmVyLCB3aGljaCAoZm9yIHRoZSBwdXJwb3NlcyBvZiB0aGlzIGV4YW1wbGUpIHdlIGRlZmluZSBhcyBTZXB0ZW1iZXIgdGhydSBKdW5lLiBJbiBvdGhlciB3b3JkcywgZXZlcnkgZGF5IGZyb20gTm92IDEgMjA3OSB0aHJ1IEp1bmUgMzAgMjA4MCBzaG91bGQgYmUgcGFydCBvZiBncm93aW5nIHNlYXNvbiAyMDgwLCBOb3YgMjA4MCB0aHJ1IEp1bmUgMjA4MSBzaG91bGQgYmUgcGFydCBvZiBncm93aW5nIHNlYXNvbiAyMDgxLCBhbmQgc28gb24uIA0KDQpUbyBjb25zdHJ1Y3QgdGhlIGdyb3dpbmcgc2Vhc29uIGNvbHVtbiwgd2UnbGwgZmlyc3QgY3JlYXRlIGNvbHVtbnMgZm9yIFllYXIsIE1vbnRoLCBhbmQgRGF5LiBMYXRlciBvbiwgd2UnbGwgdXNlIHRoZXNlIGNvbHVtbnMgaW4gYSBmb3JtdWxhIHRvIGNvbXB1dGUgdGhlIGdyb3dpbmcgc2Vhc29uIHllYXIuDQoNCmBgYHtyfQ0KcHQxXzEweXJzX3ltZF90YmwgPC0gcHQxXzEweXJzX3RibCAlPiUgDQogIG11dGF0ZShZZWFyID0gYXMuaW50ZWdlcihzdWJzdHIoZHQsIDEsIDQpKSwgDQogICAgICAgICBNb250aCA9IGFzLmludGVnZXIoc3Vic3RyKGR0LCA2LCA3KSksDQogICAgICAgICBEYXkgPSBhcy5pbnRlZ2VyKHN1YnN0cihkdCwgOSwgMTApKSkgJT4lIA0KICBzZWxlY3QoY3ZhciwgZ2NtLCBzY2VuYXJpbywgWWVhciwgTW9udGgsIERheSwgdmFsKQ0KDQpoZWFkKHB0MV8xMHlyc195bWRfdGJsKQ0KYGBgDQoNCldlIGNhbiBub3cgYWRkIGEgY29sdW1uIGZvciBncm93aW5nIHNlYXNvbiB1c2luZyBhIGNhc2Vfd2hlbiBzdGF0ZW1lbnQuIFdoaWxlIHdlJ3JlIGF0IGl0LCB3ZSdsbCByZW1vdmUgcm93cyB0aGF0IGRvbid0IGJlbG9uZyB0byBhbnkgZ3Jvd2luZyBzZWFzb24sIGFuZCBjb252ZXJ0IHRoZSB0ZW1wZXJhdHVyZSBmcm9tIEtlbHZpbiB0byBDZWxjaXVzLg0KDQpgYGB7ciBwdDFfMTB5cnNfZ3J3c25fdGJsX21ha2UsIGNhY2hlID0gVFJVRX0NCnB0MV8xMHlyc195bWRfZ3NfdGJsIDwtIHB0MV8xMHlyc195bWRfdGJsICU+JSANCiAgbXV0YXRlKGdzID0gY2FzZV93aGVuKE1vbnRoIDw9IDYgfiBZZWFyLA0KICAgICAgICAgICAgICAgICAgICAgICAgTW9udGggPj0gOSB+IGFzLmludGVnZXIoWWVhciArIDEpKSkgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKGdzKSkgJT4lIA0KICBtdXRhdGUodGVtcF9jID0gc2V0X3VuaXRzKHZhbCwgZGVnQykpDQoNCmhlYWQocHQxXzEweXJzX3ltZF9nc190YmwpDQpgYGANCg0KTmV4dCwgd2UgY29udmVydCB0aGlzICdsb25nJyBmb3JtYXQgaW50byBhICd3aWRlJyBmb3JtYXQgd2hpY2ggYGNoaWxsUjo6bWFrZV9ob3VybHlfdGVtcHMoKWAgcmVxdWlyZXM6DQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5cik7IGxpYnJhcnkobHVicmlkYXRlKQ0KDQpwdDFfMTB5cnNfeW1kX2djbXJjcF9nc190YmwgPC0gcHQxXzEweXJzX3ltZF9nc190YmwgJT4lDQogIHNlbGVjdChjdmFyLCB0ZW1wX2MsIFllYXIsIE1vbnRoLCBEYXksIGdjbSwgc2NlbmFyaW8sIGdzKSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGN2YXIsIHZhbHVlc19mcm9tID0gdGVtcF9jKSAlPiUNCiAgcmVuYW1lKFRtYXggPSB0YXNtYXgsIFRtaW4gPSB0YXNtaW4pDQoNCmRpbShwdDFfMTB5cnNfeW1kX2djbXJjcF9nc190YmwpDQpoZWFkKHB0MV8xMHlyc195bWRfZ2NtcmNwX2dzX3RibCkNCmBgYA0KDQojIyA0KSBNb2RlbCBIb3VybHkgVGVtcGVyYXR1cmUNCg0KTm93IHdlJ3JlIHJlYWR5IHRvIGNvbXB1dGUgdGhlIGhvdXJseSB0ZW1wcy4gSW4gYWRkaXRpb24gdG8gdGhlIGRhaWx5IG1pbi9tYXggdmFsdWVzLCB3ZSBuZWVkIHRvIHBhc3MgdGhlIGxhdGl0dWRlIG9mIG91ciBzaXRlICh3aGljaCBpcyB1c2VkIHRvIGRldGVybWluZSBzdW5yaXNlIGFuZCBzdW5zZXQgdGltZSk6DQoNCmBgYHtyIGNvbXB1dGVfMTB5cnNfaG91cmx5LCBjYWNoZSA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRX0NCnB0MV8xMHlyc19ocnRtcF93aWRlIDwtIG1ha2VfaG91cmx5X3RlbXBzKGxhdGl0dWRlID0gcHQxX3h5WzJdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyX2ZpbGUgPSBwdDFfMTB5cnNfeW1kX2djbXJjcF9nc190YmwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtlZXBfc3VucmlzZV9zdW5zZXQgPSBGQUxTRSkNCmRpbShwdDFfMTB5cnNfaHJ0bXBfd2lkZSkNCmhlYWQocHQxXzEweXJzX2hydG1wX3dpZGUpDQpgYGANCg0KTm93IHdlIGdvIGZyb20gd2lkZSBiYWNrIHRvIGxvbmcsIGFuZCByZWNyZWF0ZSB0aGUgZGF0ZS10aW1lIGNvbHVtbi4gVGhpcyBnZW5lcmF0ZXMgb3ZlciBhIG1pbGxpb24gcm93cyBzbyBpdCBjYW4gdGFrZSBzZXZlcmFsIHNlY29uZHMuDQoNCmBgYHtyIHB0MV8xMHlyc19ocnRtcF9sb25nLCBjYWNoZSA9IFRSVUV9DQpwdDFfMTB5cnNfaHJ0bXBfbG9uZyA8LSBwdDFfMTB5cnNfaHJ0bXBfd2lkZSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgiSG91cl8iKSwNCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkhvdXIiLA0KICAgICAgICAgICAgICAgbmFtZXNfcHJlZml4ID0gIkhvdXJfIiwNCiAgICAgICAgICAgICAgIG5hbWVzX3RyYW5zZm9ybSA9IGxpc3QoSG91ciA9IGFzLmludGVnZXIpLA0KICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInRlbXBfYyIpICU+JQ0KICBtdXRhdGUoZGF0ZV9ob3VyID0gSVNPZGF0ZXRpbWUoWWVhciwgTW9udGgsIERheSwgSG91ciwgMCwgMCwgdHogPSAiQW1lcmljYS9Mb3NfQW5nZWxlcyIpKSAlPiUNCiAgYXJyYW5nZShkYXRlX2hvdXIpDQoNCmRpbShwdDFfMTB5cnNfaHJ0bXBfbG9uZykNCmhlYWQocHQxXzEweXJzX2hydG1wX2xvbmcpDQpgYGANCg0KVG8gcmVhbGl0eS1jaGVjaywgd2UgY2FuIHBsb3QgYSBwb3J0aW9uIG9mIHRoZSBob3VybHkgdGVtcGVyYXR1cmU6DQoNCmBgYHtyIHBsb3RfaG91cmx5X3RlbXAyLCBjYWNoZSA9IFRSVUV9DQp0c19zdGFydCA8LSBhcy5EYXRlKCIyMDgxLTAxLTAxIikNCnRzX2VuZCA8LSBhcy5EYXRlKCIyMDgxLTAzLTAxIikNCg0Kc2FtcGxlX3RpbWVfc2VyaWVzIDwtIHB0MV8xMHlyc19ocnRtcF9sb25nICU+JSANCiAgZmlsdGVyKGRhdGVfaG91ciA+PSB0c19zdGFydCwgZGF0ZV9ob3VyIDw9IHRzX2VuZCwgDQogICAgICAgICBnY20gPT0gIkNhbkVTTTIiLCBzY2VuYXJpbyA9PSAicmNwNDUiKSAlPiUgDQogIGFycmFuZ2UoZGF0ZV9ob3VyKQ0KDQpkaW0oc2FtcGxlX3RpbWVfc2VyaWVzKQ0KDQpnZ3Bsb3QoZGF0YSA9IHNhbXBsZV90aW1lX3NlcmllcywNCiAgICAgICBhZXMoeCA9IGRhdGVfaG91ciwgeSA9IGFzLm51bWVyaWModGVtcF9jKSkpICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvcj0icmVkIiksIHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgbGFicyh0aXRsZSA9ICJNb2RlbGxlZCBIb3VybHkgVGVtcGVyYXR1cmUiLCB4ID0gImRhdGUiLCB5ID0gInRlbXAgKEMpIikgKw0KICB0aGVtZShwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwKSwNCiAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMjAsIDIwLCAyMCwgMjApLA0KICAgICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyYXk1MCIsIHNpemUgPSAzKSkNCg0KYGBgDQoNCg0KIyMgNSkgQ29tcHV0ZSBDaGlsbCBQb3J0aW9ucw0KDQpOb3cgd2UncmUgcmVhZHkgdG8gY29tcHV0ZSBjdW11bGF0aXZlIGNoaWxsIHBvcnRpb25zLiBXZSBuZWVkIHRvIGJyZWFrIHRoaXMgdXAgYnkgZ3Jvd2luZyBzZWFzb24sIGdjbSBhbmQgcmNwLg0KDQpgYGB7ciBhY2N1bV9jaGlsbF9wcnRuX2NvbXB1dGUsIGNhY2hlID0gVFJVRX0NCnB0MV8xMHlyc19ocnRtcGNwIDwtIHB0MV8xMHlyc19ocnRtcF9sb25nICU+JSANCiAgbXV0YXRlKHRlbXBfYyA9IGFzLm51bWVyaWModGVtcF9jKSkgJT4lIA0KICBncm91cF9ieShncywgZ2NtLCBzY2VuYXJpbykgJT4lIA0KICBtdXRhdGUoYWNjdW1fY2hpbGxfcHJ0biA9IER5bmFtaWNfTW9kZWwodGVtcF9jKSkNCg0KaGVhZChwdDFfMTB5cnNfaHJ0bXBjcCkNCmBgYA0KDQpWaWV3IHRoZSBtYXhpbXVtIGFjY3VtdWxhdGVkIGNoaWxsIHBvcnRpb24gcGVyIFJDUDoNCg0KYGBge3J9DQpwdDFfMTB5cnNfbWF4Y2hpbGwgPC0gcHQxXzEweXJzX2hydG1wY3AgJT4lIA0KICBncm91cF9ieShzY2VuYXJpbywgZ3MsIGdjbSkgJT4lIA0KICBzdW1tYXJpc2UobWF4X2NoaWxsID0gbWF4KGFjY3VtX2NoaWxsX3BydG4pLCAuZ3JvdXBzID0gJ2Ryb3AnKSANCg0KcHQxXzEweXJzX21heGNoaWxsICU+JSBoZWFkKCkNCmBgYA0KDQojIyA2KSBDb21wdXRlIFNhZmUgQ2hpbGwNCg0KRm9yIGVhY2ggUkNQLCB3ZSBjb21wdXRlIHRoZSAnc2FmZSB3aW50ZXIgY2hpbGwnIHZhbHVlLiAnU2FmZSB3aW50ZXIgY2hpbGwnIGlzIHRoZSBtaW5pbXVtIGNoaWxsIHBvcnRpb24gb25lIGNhbiBleHBlY3QgOTAlIG9mIHRoZSB0aW1lIChbTHVlZGVsaW5nIGV0IGFsLCAyMDA5XShodHRwczovL2RvaS5vcmcvMTAuMTM3MS9qb3VybmFsLnBvbmUuMDAwNjE2NikpLiBGb3IgZ3Jvd2Vycywgc2FmZSB3aW50ZXIgY2hpbGwgcmVmbGVjdHMgdGhlIGVjb25vbWljIG5lZWQgZm9yIGFuIG9yY2hhcmQgb3BlcmF0aW9uIHRvIHByb2R1Y2UgZ29vZCB5aWVsZHMgaW4gbW9zdCB5ZWFycywgcmF0aGVyIHRoYW4gYW4gYXZlcmFnZSB5ZWFyLiBIZXJlIGl0IGlzIGNvbXB1dGVkIGFzIHRoZSAxMCUgcXVhbnRpbGUgb2YgYWxsIGVuZC1vZi1zZWFzb24gY2hpbGwgcG9ydGlvbnMgaW4gdGhlIGFuYWx5c2lzIChpZS4sIGFsbCBHQ01zIGFuZCBhbGwgZ3Jvd2luZyBzZWFzb25zIDIwODAtMjA5MCksIHdpdGggdGhlIGFzc3VtcHRpb24gdGhhdCBhbGwgb2YgdGhlc2Ugb3V0Y29tZXMgYXJlIGVxdWFsbHkgbGlrZWx5Lg0KDQpgYGB7ciBmaW5kX3NhZmVfY2hpbGwsIGNhY2hlPVRSVUV9DQojIyBDb21wdXRlIHNhZmUgd2ludGVyIGNoaWxsIGZvciBlYWNoIFJDUA0Kc2FmZV9jaGlsbF90YmwgPC0gcHQxXzEweXJzX21heGNoaWxsICU+JSANCiAgZ3JvdXBfYnkoc2NlbmFyaW8pICU+JSANCiAgc3VtbWFyaXplKHNhZmVfY2hpbGwgPSBxdWFudGlsZShtYXhfY2hpbGwsIHByb2JzID0gMC4xKSwgLmdyb3VwcyA9ICJkcm9wIikNCg0Kc2FmZV9jaGlsbF90YmwNCmBgYA0KDQpQbG90IGRpc3RyaWJ1dGlvbiBvZiBjaGlsbCBwb3J0aW9uOg0KDQpgYGB7ciBjaGlsbF9oaXN0bywgY2FjaGUgPSBUUlVFfQ0KIyMgUGxvdCB0aGUgaGlzdG9ncmFtIGFuZCBvdmVybGF5IHRoZSBzYWZlIGNoaWxsIHZhbHVlDQpwcm9qZWN0ZWRfaGlzdG8gPC0gZ2dwbG90KHB0MV8xMHlyc19tYXhjaGlsbCwgYWVzKHg9bWF4X2NoaWxsKSkgKyANCiAgZ2VvbV9oaXN0b2dyYW0oKSArDQogIGdlb21fdmxpbmUoZGF0YSA9IHNhZmVfY2hpbGxfdGJsLCBjb2xvciA9ICJyZWQiLCBhZXMoeGludGVyY2VwdCA9IHNhZmVfY2hpbGwpLCBzaXplID0gMSkgKw0KICBmYWNldF93cmFwKCJzY2VuYXJpbyIsIG5yb3cgPSAxKSArDQogIGxhYnModGl0bGUgPSAiTWF4aW11bSBQcm9qZWN0ZWQgQ2hpbGwgUG9ydGlvbiIsIA0KICAgICBzdWJ0aXRsZSA9ICIyMDgwIC0gMjA5MCIsDQogICAgIGNhcHRpb24gPSBwYXN0ZTAoDQogICAgICAgc3RyX3dyYXAocGFzdGUwKCJHQ01zOiAiLCBwYXN0ZSh1bmlxdWUocHQxXzEweXJzX21heGNoaWxsJGdjbSksIGNvbGxhcHNlID0gIiwgIiksIHdpZHRoID0gMTA1KSksDQogICAgICAgIlxuIiwNCiAgICAgICBzdHJfd3JhcCgiVGhlIHJlZCBsaW5lIGlzIHRoZSAxMCUgcXVhbnRpbGUgYW5kIHJlcHJlc2VudHMgdGhlIHNhZmUgd2ludGVyIGNoaWxsICh0aGUgbWltaW11bSBjaGlsbCBwb3J0aW9uIG9uZSBtYXkgZXhwZWN0IDkwJSBvZiB0aGUgdGltZSBhc3N1bW1pbmcgYWxsIHByb2plY3Rpb25zIGFyZSBlcXVhbGx5IGxpa2VseSkuIiwgd2lkdGggPSAxMDUpKSwNCiAgICAgeCA9ICJlbmQtb2Ytc2Vhc29uIGNoaWxsIHBvcnRpb24iLCB5ID0gImNvdW50IikgKw0KICB0aGVtZShwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwKSwNCiAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMjAsIDIwLCAyMCwgMjApLA0KICAgICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyYXk1MCIsIHNpemUgPSAzKSkNCg0KcHJvamVjdGVkX2hpc3RvDQpgYGANCg0KIyMgNykgSG93IE1hbnkgR3Jvd2luZyBTZWFzb25zIFJlYWNoIHRoZSBNaW5pbXVtIENoaWxsIFJlcXVpcmVtZW50Pw0KDQpOZXh0LCBmb3IgZWFjaCBSQ1Agd2UgY29tcHV0ZSBob3cgbWFueSBwcm9qZWN0ZWQgdGVtcGVyYXR1cmUgc2VyaWVzIHJlc3VsdGVkIGluIGF0IGxlYXN0IGByIGNoaWxsX21pbmAgYWNjdW11bGF0ZWQgY2hpbGwgcG9ydGlvbnMgbmVlZGVkIGZvciBhIHZpYWJsZSBjcm9wIG9mIHRoZSBbS2VybWFuIGN1bHRpdmFyIG9mIFBpc3RhY2hpb3NdKGh0dHA6Ly9mcnVpdHNhbmRudXRzLnVjZGF2aXMuZWR1L1dlYXRoZXJfU2VydmljZXMvY2hpbGxpbmdfYWNjdW11bGF0aW9uX21vZGVscy9Dcm9wQ2hpbGxSZXEvKToNCg0KYGBge3IgcHJjdF9taW5fY2hpbGxfcmVhY2hlZCwgY2FjaGU9VFJVRSwgbWVzc2FnZT1GQUxTRX0NCmNoaWxsX21pbiA8LSA1NA0KDQpwdDFfMTB5cnNfcHJjdF9yZWFjaGVkX21pbiA8LSBwdDFfMTB5cnNfaHJ0bXBjcCAlPiUgDQogIGdyb3VwX2J5KGdzLCBnY20sIHNjZW5hcmlvKSAlPiUgDQogIHN1bW1hcmlzZShyZWFjaGVkX3RocmVzaCA9IG1heChhY2N1bV9jaGlsbF9wcnRuKSA+PSBjaGlsbF9taW4pICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZ3JvdXBfYnkoc2NlbmFyaW8pICU+JSANCiAgc3VtbWFyaXNlKHBlcmNlbnRfcmVhY2hlZF90aHJlc2ggPSBzdW0ocmVhY2hlZF90aHJlc2gpIC8gbigpKSAlPiUgDQogIG11dGF0ZSh0aW1lX3BlcmlvZCA9ICIyMDgxIC0gMjA5MCIsIHBlcmNlbnRfcmVhY2hlZF90aHJlc2ggPSBzY2FsZXM6OnBlcmNlbnQocGVyY2VudF9yZWFjaGVkX3RocmVzaCkpICU+JSANCiAgcmVsb2NhdGUodGltZV9wZXJpb2QsIHNjZW5hcmlvLCBwZXJjZW50X3JlYWNoZWRfdGhyZXNoKQ0KDQpwdDFfMTB5cnNfcHJjdF9yZWFjaGVkX21pbg0KYGBgDQoNCg0KIyMgOCkgQWNjdW11bGF0ZWQgQ2hpbGwgQ3VydmVzDQoNCk5leHQsIHdlIHBsb3QgdGhlIGN1cnZlcyBmb3IgYWNjdW11bGF0ZWQgY2hpbGwgZm9yIFJDUCA0LjUgYW5kIDguNS4gV2UgaGF2ZSAxMCBncm93aW5nIHNlYXNvbnMgYW5kIDEwIEdDTXMsIGZvciBhIHRvdGFsIG9mIDEwMCBjdXJ2ZXMuIFRoZSBsaW5lcyB0aGF0IGVuZCBhYm92ZSB0aGUgbWluaW11bSBjaGlsbCByZXF1aXJlbWVudCByZXByZXNlbnQgc2ltdWxhdGlvbnMgd2hlcmUgdGhlIG1pbmltdW0gY2hpbGwgZm9yIGEgdmlhYmxlIGNyb3Agd2FzIHJlYWNoZWQuIA0KDQpgYGB7ciBwbG90X2FjY19jdXJ2ZXNfcmNwNDUsIGNhY2hlID0gVFJVRX0NCiMjIEZpcnN0IG1ha2UgYSBjb3B5IG9mIGRhdGEsIG1hbnVhbGx5IHNldCB0aGUgeWVhciB0byB0aGUgc2FtZSB2YWx1ZSAoc28gdGhlIGRhdGVzIGFyZSBpbiB0aGUgc2FtZSByYW5nZSksIGFuZCBrZWVwDQojIyBvbmx5IG1pZG5pZ2h0IHZhbHVlcw0KDQpwdDFfMTB5cnNfaHJ0bXBjcF80cGxvdCA8LSBwdDFfMTB5cnNfaHJ0bXBjcCAlPiUgDQogIGZpbHRlcihIb3VyID09IDApICU+JSANCiAgbXV0YXRlKFllYXJQbG90ID0gaWZfZWxzZShNb250aCA+PSA5LCAxOTcwLCAxOTcxKSkgJT4lIA0KICBtdXRhdGUoZGF0ZV9ob3VyID0gSVNPZGF0ZXRpbWUoWWVhclBsb3QsIE1vbnRoLCBEYXksIEhvdXIsIDAsIDAsIHR6ID0gIkFtZXJpY2EvTG9zX0FuZ2VsZXMiKSwNCiAgICAgICAgIGdzX2djbSA9IHBhc3RlKGdzLCBnY20sIHNlcCA9ICJfIikpICU+JSANCiAgc2VsZWN0KHNjZW5hcmlvLCBncywgZ2NtLCBnc19nY20sIGRhdGVfaG91ciwgYWNjdW1fY2hpbGxfcHJ0bikNCg0KIyMgQ3JlYXRlIHBsb3QNCmdncGxvdChkYXRhID0gcHQxXzEweXJzX2hydG1wY3BfNHBsb3QgJT4lIGZpbHRlcihzY2VuYXJpbyA9PSAicmNwNDUiKSwNCiAgICAgICBhZXMoeCA9IGRhdGVfaG91ciwgeSA9IGFjY3VtX2NoaWxsX3BydG4pKSArDQogIGdlb21fbGluZShhZXMoY29sb3I9Z3NfZ2NtKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBjaGlsbF9taW4sIHNpemUgPSAxKSArDQogIGxhYnModGl0bGUgPSAiUHJvamVjdGVkIENoaWxsIFBvcnRpb24gQWNjdW11bGF0aW9uIGZvciBQdCAxOiBSQ1AgNC41IiwgDQogICAgICAgc3VidGl0bGUgPSAiMjA4MCAtIDIwOTAiLA0KICAgICAgIGNhcHRpb24gPSBzdHJfd3JhcChwYXN0ZTAoIkdDTXM6ICIsIHBhc3RlKHVuaXF1ZShwdDFfMTB5cnNfaHJ0bXBjcF80cGxvdCRnY20pLCBjb2xsYXBzZSA9ICIsICIpLCB3aWR0aCA9IDYwKSksDQogICAgICAgeCA9ICJkYXRlIiwgeSA9ICJDaGlsbCBQb3J0aW9uIikgKw0KICB0aGVtZShwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwKSwNCiAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMjAsIDIwLCAyMCwgMjApLA0KICAgICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyYXk1MCIsIHNpemUgPSAzKSkNCmBgYA0KDQoNCg0KYGBge3IgcGxvdF9hY2NfY3VydmVzX3JjcDg1LCBjYWNoZSA9IFRSVUV9DQojIyBSQ1AgODUNCmdncGxvdChkYXRhID0gcHQxXzEweXJzX2hydG1wY3BfNHBsb3QgJT4lIGZpbHRlcihzY2VuYXJpbyA9PSAicmNwODUiKSwNCiAgICAgICBhZXMoeCA9IGRhdGVfaG91ciwgeSA9IGFjY3VtX2NoaWxsX3BydG4pKSArDQogIGdlb21fbGluZShhZXMoY29sb3I9Z3NfZ2NtKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBjaGlsbF9taW4sIHNpemUgPSAxKSArDQogIGxhYnModGl0bGUgPSAiUHJvamVjdGVkIENoaWxsIFBvcnRpb24gQWNjdW11bGF0aW9uIGZvciBQdCAxOiBSQ1AgOC41IiwgDQogICAgICAgc3VidGl0bGUgPSAiMjA4MCAtIDIwOTAiLA0KICAgICAgIGNhcHRpb24gPSBzdHJpbmdyOjpzdHJfd3JhcChwYXN0ZTAoIkdDTXM6ICIsIHBhc3RlKHVuaXF1ZShwdDFfMTB5cnNfaHJ0bXBjcF80cGxvdCRnY20pLCBjb2xsYXBzZSA9ICIsICIpLCB3aWR0aCA9IDYwKSksDQogICAgICAgeCA9ICJkYXRlIiwgeSA9ICJDaGlsbCBQb3J0aW9uIikgKw0KICB0aGVtZShwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwKSwNCiAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMjAsIDIwLCAyMCwgMjApLA0KICAgICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyYXk1MCIsIHNpemUgPSAzKSkNCmBgYA0KDQojIyA5KSBEaXN0cmlidXRpb24gb2YgUmVxdWlyZWQgQWNjdW11bGF0aW9uIERhdGUNCg0KSGVyZSB3ZSBwbG90IHRoZSBkaXN0cmlidXRpb24gb2YgcHJvamVjdGVkIGRhdGVzIHdoZW4gdGhlIGFjY3VtdWxhdGVkIGNoaWxsIHJlYWNoZXMgdGhlIG1pbmltdW0gcmVxdWlyZW1lbnQuDQoNCmBgYHtyfQ0KIyMgTnVtYmVyIG9mIHJ1bnMgZm9yIGVhY2ggZW1pc3Npb25zIHNjZW5hcmlvID0gbnVtYmVyIG9mIGdyb3dpbmcgc2Vhc29ucyAqIG51bWJlciBvZiBnY21zDQpudW1fcnVucyA8LSBsZW5ndGgodW5pcXVlKHB0MV8xMHlyc19ocnRtcGNwJGdzKSkgKiBsZW5ndGgodW5pcXVlKHB0MV8xMHlyc19ocnRtcGNwJGdjbSkpIA0KDQojIyBGb3IgZWFjaCBydW4sIGZpbmQgdGhlIGZpcnN0IGRhdGUgd2hlbiB0aGUgbWluaW11bSBjaGlsbCBwb3J0aW9uIHdhcyByZWFjaGVkLiANCnB0MV8xMHlyc19zY2VuX2NoaWxsZHQgPC0gcHQxXzEweXJzX2hydG1wY3AgJT4lIA0KICBmaWx0ZXIoSG91ciA9PSAwKSAlPiUgDQogIGZpbHRlcihhY2N1bV9jaGlsbF9wcnRuID49IGNoaWxsX21pbikgJT4lIA0KICBncm91cF9ieShncywgZ2NtKSAlPiUgDQogIHNsaWNlX21pbihvcmRlcl9ieSA9IGRhdGVfaG91ciwgbiA9IDEpICU+JSANCiAgdW5ncm91cCgpICU+JQ0KICBtdXRhdGUoZGF0ZV8xOTcwID0gSVNPZGF0ZSh5ZWFyID0gMTk3MCwgbW9udGggPSBNb250aCwgZGF5ID0gRGF5KSkgJT4lIA0KICBzZWxlY3Qoc2NlbmFyaW8sIGRhdGVfMTk3MCkNCg0KIyMgd2UncmUgZ2V0dGluZyBvbmUgTkEgLSBpbnZlc3RpZ2F0ZQ0KIyMgcHQxXzEweXJzX3NjZW5fY2hpbGxkdCAlPiUgZmlsdGVyKGlzLm5hKGRhdGVfMTk3MCkpICANCg0KIyMgTWFrZSBhIGRhdGEgZnJhbWUgZm9yIHRoZSB0b3RhbCBudW1iZXIgb2YgZGF0ZXMgYW5kIG1pZGRsZSBkYXRlIHBlciBzY2VuYXJpbyANCiMjIChUaGlzIGlzIGZvciB0aGUgbGFiZWxzIG9uIHRoZSBib3ggcGxvdCkNCnNjZW5fc3RhdHNfdGJsIDwtIHB0MV8xMHlyc19zY2VuX2NoaWxsZHQgJT4lIA0KICBncm91cF9ieShzY2VuYXJpbykgJT4lIA0KICBzdW1tYXJpemUobnVtX2RhdGVzID0gbigpLCBtaWRfZHQgPSBtZWRpYW4oZGF0ZV8xOTcwLCBuYS5ybSA9IFRSVUUpKQ0KDQojIyBNYWtlIGEgbGlzdCBvYmplY3QgZm9yIGVhc3kgcmVmZXJlbmNlLiBOT1QgTkVFREVEIEFOWU1PUkUNCiMjIG51bV9ydW5zX2xzdCA8LSBzY2VuX3N0YXRzX3RibCAlJCUgc3BsaXQobnVtX2RhdGVzLCBzY2VuYXJpbykgDQoNCiMjIFBsb3QgdGhlIHJlc3VsdHMNCmdncGxvdChwdDFfMTB5cnNfc2Nlbl9jaGlsbGR0LCBhZXMoZGF0ZV8xOTcwICwgc2NlbmFyaW8pKSArIA0KICBnZW9tX2JveHBsb3QoKSArDQogIGdlb21fbGFiZWwoZGF0YSA9IHNjZW5fc3RhdHNfdGJsLCANCiAgICAgICAgICAgIGFlcyhsYWJlbD1wYXN0ZTAoIm49IiwgbnVtX2RhdGVzKSwgeCA9IG1pZF9kdCArICgzLjUgKiAyNCAqIDM2MDApKSwgDQogICAgICAgICAgICBzaXplID0gMy41LCBmaWxsID0gIndoaXRlIiwgbGFiZWwuc2l6ZSA9IDApICsNCiAgbGFicyh0aXRsZSA9IHBhc3RlMCgiRGF0ZSBBY2N1bWxhdGVkIENoaWxsIFBvcnRpb24gUmVhY2hlcyAiLCBjaGlsbF9taW4gKSwgDQogICAgICAgc3VidGl0bGUgPSAiMjA4MCAtIDIwOTAiLA0KICAgICAgIGNhcHRpb24gPSBzdHJfd3JhcChwYXN0ZSgiR0NNczogIiwgcGFzdGUodW5pcXVlKHB0MV8xMHlyc19ocnRtcGNwXzRwbG90JGdjbSksIGNvbGxhcHNlID0gIiwgIikpLCB3aWR0aCA9IDgwKSwNCiAgICAgICB4ID0gImRhdGUiLCB5ID0gIkVtaXNzaW9ucyBTY2VuYXJpbyIpICsNCiAgdGhlbWUocGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMCksDQogICAgICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDIwLCAyMCwgMjAsIDIwKSwNCiAgICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmF5NTAiLCBzaXplID0gMykpDQpgYGANCg0KDQojIEhpc3RvcmljYWwgQmFzZWxpbmUNCg0KV2hlbiBsb29raW5nIGF0IGhpc3RvcmljYWwgZGF0YSwgd2UgY2FuIGVpdGhlciB1c2UgbW9kZWxlZCBvciBvYnNlcnZlZC4gSGVyZSB3ZSB1c2UgbW9kZWxlZCBiZWNhdXNlIDEpIG1vZGVsZWQgZGF0YSBoYXMgYmVlbiBzaG93biB0byBtYXRjaGVzIG9ic2VydmVkIHBhdHRlcm5zIHZlcnkgd2VsbCwgYW5kIDIpIGl0IGZhY2lsaXRhdGVzIGNvbXBhcmlzb25zIHdpdGggbW9kZWxlZCBwcmVkaWN0aW9ucy4NCg0KYGBge3IgY2FwMmJhc2VsaW5lX21ha2UsIGNhY2hlPVRSVUV9DQojIyBQaWNrIGEgbG9jYXRpb24NCnB0MV94eSA8LSBjKC0xMTkuOTY0LCAzNi4wMTkpDQoNCnB0MV9iYXNlbGluZV9jYXAgPC0gY2FfbG9jX3B0KGNvb3JkcyA9IHB0MV94eSkgJT4lDQogIGNhX2djbShnY21zWzE6MTBdKSAlPiUNCiAgY2Ffc2NlbmFyaW8oYygiaGlzdG9yaWNhbCIpKSAlPiUNCiAgY2FfcGVyaW9kKCJkYXkiKSAlPiUNCiAgY2FfZGF0ZXMoc3RhcnQgPSAiMTk5NS0wMS0wMSIsIGVuZCA9ICIyMDA1LTEyLTMxIikgJT4lDQogIGNhX2N2YXIoYygidGFzbWF4IiwgInRhc21pbiIpKQ0KDQpwdDFfYmFzZWxpbmVfY2FwDQpgYGANCg0KYGBge3IgcHQxX2Jhc2VsaW5lX3RibF9mZXRjaCwgY2FjaGUgPSBUUlVFfQ0KcHQxX2Jhc2VsaW5lX3RibCA8LSBwdDFfYmFzZWxpbmVfY2FwICU+JSBjYV9nZXR2YWxzX3RibChxdWlldCA9IFRSVUUpIA0KZGltKHB0MV9iYXNlbGluZV90YmwpDQpoZWFkKHB0MV9iYXNlbGluZV90YmwpDQpgYGANCg0KQWRkIGNvbHVtbnM6DQoNCmBgYHtyfQ0KcHQxX2Jhc2VsaW5lX3ltZF90YmwgPC0gcHQxX2Jhc2VsaW5lX3RibCAlPiUgDQogIG11dGF0ZShZZWFyID0gYXMuaW50ZWdlcihzdWJzdHIoZHQsIDEsIDQpKSwgDQogICAgICAgICBNb250aCA9IGFzLmludGVnZXIoc3Vic3RyKGR0LCA2LCA3KSksDQogICAgICAgICBEYXkgPSBhcy5pbnRlZ2VyKHN1YnN0cihkdCwgOSwgMTApKSkgJT4lIA0KICBzZWxlY3QoY3ZhciwgZ2NtLCBzY2VuYXJpbywgWWVhciwgTW9udGgsIERheSwgdmFsKQ0KcHQxX2Jhc2VsaW5lX3ltZF90YmwNCmBgYA0KDQpBZGQgZ3Jvd2luZyBzZWFzb24gY29sdW1uOg0KDQpgYGB7ciBiYXNlbGluZV9ncywgY2FjaGUgPSBUUlVFfQ0KcHQxX2Jhc2VsaW5lX3ltZF9nc190YmwgPC0gcHQxX2Jhc2VsaW5lX3ltZF90YmwgJT4lIA0KICBtdXRhdGUoZ3MgPSBjYXNlX3doZW4oTW9udGggPD0gNiB+IFllYXIsDQogICAgICAgICAgICAgICAgICAgICAgICBNb250aCA+PSA5IH4gYXMuaW50ZWdlcihZZWFyICsgMSkpKSAlPiUgDQogIGZpbHRlcighaXMubmEoZ3MpKSAlPiUgDQogIG11dGF0ZSh0ZW1wX2MgPSBzZXRfdW5pdHModmFsLCBkZWdDKSkNCmhlYWQocHQxX2Jhc2VsaW5lX3ltZF9nc190YmwpDQpgYGANCg0KUGl2b3Qgd2lkZXIgKG1ha2UgdGFzbWF4IGFuZCB0YXNtaW4gc2VwYXJhdGUgY29sdW1ucyk6DQoNCmBgYHtyIHB0MV9iYXNlbGluZV95bWRfZ2NtcmNwX2dzX3RibCwgY2FjaGUgPSBUUlVFfQ0KcHQxX2Jhc2VsaW5lX3ltZF9nY21yY3BfZ3NfdGJsIDwtIHB0MV9iYXNlbGluZV95bWRfZ3NfdGJsICU+JQ0KICBzZWxlY3QoY3ZhciwgdGVtcF9jLCBZZWFyLCBNb250aCwgRGF5LCBnY20sIHNjZW5hcmlvLCBncykgJT4lDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBjdmFyLCB2YWx1ZXNfZnJvbSA9IHRlbXBfYykgJT4lDQogIHJlbmFtZShUbWF4ID0gdGFzbWF4LCBUbWluID0gdGFzbWluKQ0KDQpkaW0ocHQxX2Jhc2VsaW5lX3ltZF9nY21yY3BfZ3NfdGJsKQ0KaGVhZChwdDFfYmFzZWxpbmVfeW1kX2djbXJjcF9nc190YmwpDQoNCmBgYA0KQ29tcHV0ZSBob3VybHkgdGVtcHMgKGluIHdpZGUgZm9ybWF0KToNCg0KYGBge3IgcHQxX2Jhc2VsaW5lX2hydG1wX3dpZGUsIGNhY2hlID0gVFJVRX0NCnB0MV9iYXNlbGluZV9ocnRtcF93aWRlIDwtIG1ha2VfaG91cmx5X3RlbXBzKGxhdGl0dWRlID0gcHQxX3h5WzJdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyX2ZpbGUgPSBwdDFfYmFzZWxpbmVfeW1kX2djbXJjcF9nc190YmwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtlZXBfc3VucmlzZV9zdW5zZXQgPSBGQUxTRSkNCmRpbShwdDFfYmFzZWxpbmVfaHJ0bXBfd2lkZSkNCmhlYWQocHQxX2Jhc2VsaW5lX2hydG1wX3dpZGUpDQpgYGANCg0KQ29udmVydCB3aWRlIHRvIGxvbmc6DQoNCmBgYHtyIHB0MV9iYXNlbGluZV9ocnRtcF9sb25nLCBjYWNoZSA9IFRSVUV9DQpwdDFfYmFzZWxpbmVfaHJ0bXBfbG9uZyA8LSBwdDFfYmFzZWxpbmVfaHJ0bXBfd2lkZSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgiSG91cl8iKSwNCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkhvdXIiLA0KICAgICAgICAgICAgICAgbmFtZXNfcHJlZml4ID0gIkhvdXJfIiwNCiAgICAgICAgICAgICAgIG5hbWVzX3RyYW5zZm9ybSA9IGxpc3QoSG91ciA9IGFzLmludGVnZXIpLA0KICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInRlbXBfYyIpICU+JQ0KICBtdXRhdGUoZGF0ZV9ob3VyID0gSVNPZGF0ZXRpbWUoWWVhciwgTW9udGgsIERheSwgSG91ciwgMCwgMCwgdHogPSAiQW1lcmljYS9Mb3NfQW5nZWxlcyIpKSAlPiUNCiAgYXJyYW5nZShkYXRlX2hvdXIpDQoNCmRpbShwdDFfYmFzZWxpbmVfaHJ0bXBfbG9uZykNCmhlYWQocHQxX2Jhc2VsaW5lX2hydG1wX2xvbmcpDQpgYGANCg0KQ29tcHV0ZSBiYXNlbGluZSBjaGlsbCBwb3J0aW9uczoNCg0KYGBge3IgcHQxX2Jhc2VsaW5lX2hydG1wY3AsIGNhY2hlID0gVFJVRX0NCnB0MV9iYXNlbGluZV9ocnRtcGNwIDwtIHB0MV9iYXNlbGluZV9ocnRtcF9sb25nICU+JSANCiAgbXV0YXRlKHRlbXBfYyA9IGFzLm51bWVyaWModGVtcF9jKSkgJT4lIA0KICBncm91cF9ieShncywgZ2NtLCBzY2VuYXJpbykgJT4lIA0KICBtdXRhdGUoYWNjdW1fY2hpbGxfcHJ0biA9IER5bmFtaWNfTW9kZWwodGVtcF9jKSkNCg0KaGVhZChwdDFfYmFzZWxpbmVfaHJ0bXBjcCkNCg0KYGBgDQoNCkNvbXB1dGUgdGhlIG1heC1jaGlsbCByZWFjaGVkIGVhY2ggZ3Jvd2luZyBzZWFzb24gLyBHQ00gZHVyaW5nIHRoZSBiYXNlbGluZSBwZXJpb2QuIE5leHQgd2UgY29tcHV0ZSB0aGUgJ3NhZmUgY2hpbGwnICgxMCUgcXVhbnRpbGUpOg0KDQpgYGB7ciBwdDFfYmFzZWxpbmVfbWF4Y2hpbGwsIGNhY2hlID0gVFJVRX0NCnB0MV9iYXNlbGluZV9tYXhjaGlsbCA8LSBwdDFfYmFzZWxpbmVfaHJ0bXBjcCAlPiUgDQogIGdyb3VwX2J5KHNjZW5hcmlvLCBncywgZ2NtKSAlPiUgDQogIHN1bW1hcmlzZShtYXhfY2hpbGwgPSBtYXgoYWNjdW1fY2hpbGxfcHJ0biksIC5ncm91cHMgPSAnZHJvcCcpIA0KDQpkaW0ocHQxX2Jhc2VsaW5lX21heGNoaWxsKQ0KcHQxX2Jhc2VsaW5lX21heGNoaWxsICU+JSBoZWFkKCkNCg0Kc2FmZV9jaGlsbF9iYXNlbGluZV90YmwgPC0gcHQxX2Jhc2VsaW5lX21heGNoaWxsICU+JSANCiAgZ3JvdXBfYnkoc2NlbmFyaW8pICU+JSANCiAgc3VtbWFyaXplKHNhZmVfY2hpbGwgPSBxdWFudGlsZShtYXhfY2hpbGwsIHByb2JzID0gMC4xKSwgLmdyb3VwcyA9ICJkcm9wIikgDQoNCnNhZmVfY2hpbGxfYmFzZWxpbmVfdGJsDQpgYGANCg0KUGxvdCBkaXN0cmlidXRpb24gb2YgdGhlIGJhc2VsaW5lIG1heGltdW0gY2hpbGwgcG9ydGlvbjoNCg0KYGBge3IgY2hpbGxfYmFzZWxpbmVfaGlzdG8sIGNhY2hlID0gVFJVRX0NCiMjIFBsb3QgdGhlIGhpc3RvZ3JhbSBhbmQgb3ZlcmxheSB0aGUgc2FmZSBjaGlsbCB2YWx1ZQ0KYmFzZWxpbmVfaGlzdG8gPC0gZ2dwbG90KHB0MV9iYXNlbGluZV9tYXhjaGlsbCwgYWVzKHg9bWF4X2NoaWxsKSkgKyANCiAgZ2VvbV9oaXN0b2dyYW0oKSArDQogIGdlb21fdmxpbmUoZGF0YSA9IHNhZmVfY2hpbGxfYmFzZWxpbmVfdGJsLCBjb2xvciA9ICJyZWQiLCBhZXMoeGludGVyY2VwdCA9IHNhZmVfY2hpbGwpLCBzaXplID0gMSkgKw0KICBmYWNldF93cmFwKCJzY2VuYXJpbyIsIG5yb3cgPSAxKSArDQogIGxhYnModGl0bGUgPSAiTWF4aW11bSBQcm9qZWN0ZWQgQ2hpbGwgUG9ydGlvbiIsIA0KICAgICBzdWJ0aXRsZSA9ICIxOTk1IC0gMjAwNSIsDQogICAgIGNhcHRpb24gPSBwYXN0ZTAoDQogICAgICAgc3RyX3dyYXAocGFzdGUwKCJHQ01zOiAiLCBwYXN0ZSh1bmlxdWUocHQxX2Jhc2VsaW5lX21heGNoaWxsJGdjbSksIGNvbGxhcHNlID0gIiwgIiksIHdpZHRoID0gMTA1KSksDQogICAgICAgIlxuIiwNCiAgICAgICBzdHJfd3JhcCgiVGhlIHJlZCBsaW5lIGlzIHRoZSAxMCUgcXVhbnRpbGUgYW5kIHJlcHJlc2VudHMgdGhlIHNhZmUgd2ludGVyIGNoaWxsICh0aGUgbWltaW11bSBjaGlsbCBwb3J0aW9uIG9uZSBtYXkgZXhwZWN0IDkwJSBvZiB0aGUgdGltZSBhc3N1bW1pbmcgYWxsIHByb2plY3Rpb25zIGFyZSBlcXVhbGx5IGxpa2VseSkuIiwgd2lkdGggPSAxMDUpKSwNCiAgICAgeCA9ICJlbmQtb2Ytc2Vhc29uIGNoaWxsIHBvcnRpb24iLCB5ID0gImNvdW50IikgKw0KICB0aGVtZShwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwKSwNCiAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMjAsIDIwLCAyMCwgMjApLA0KICAgICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyYXk1MCIsIHNpemUgPSAzKSkNCg0KYmFzZWxpbmVfaGlzdG8NCmBgYA0KDQpIb3cgTWFueSBHcm93aW5nIFNlYXNvbnMgYW5kIFNpbXVsYXRlZCAoQmFja2Nhc3QpIENsaW1hdGUgQ29uZGl0aW9ucyBEdXJpbmcgdGhlIEJhc2VsaW5lIFNlYXNvbnMgUmVhY2hlZCBPdXIgTWluaW11bSBDaGlsbCBSZXF1aXJlbWVudD8NCg0KYGBge3IgYmFzZWxpbmVfcHJjdF9taW5fY2hpbGxfcmVhY2hlZCwgY2FjaGU9VFJVRSwgbWVzc2FnZT1GQUxTRX0NCmNoaWxsX21pbiA8LSA1NA0KDQpwdDFfYmFzZWxpbmVfcHJjdF9yZWFjaGVkX21pbiA8LSBwdDFfYmFzZWxpbmVfaHJ0bXBjcCAlPiUgDQogIGdyb3VwX2J5KGdzLCBnY20sIHNjZW5hcmlvKSAlPiUgDQogIHN1bW1hcmlzZShyZWFjaGVkX3RocmVzaCA9IG1heChhY2N1bV9jaGlsbF9wcnRuKSA+PSBjaGlsbF9taW4pICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZ3JvdXBfYnkoc2NlbmFyaW8pICU+JSANCiAgc3VtbWFyaXNlKHBlcmNlbnRfcmVhY2hlZF90aHJlc2ggPSBzdW0ocmVhY2hlZF90aHJlc2gpIC8gbigpKSAlPiUgDQogIG11dGF0ZSh0aW1lX3BlcmlvZCA9ICIxOTk1LTIwMDUiLCBwZXJjZW50X3JlYWNoZWRfdGhyZXNoID0gc2NhbGVzOjpwZXJjZW50KHBlcmNlbnRfcmVhY2hlZF90aHJlc2gpKSAlPiUgDQogIHJlbG9jYXRlKHRpbWVfcGVyaW9kLCBzY2VuYXJpbywgcGVyY2VudF9yZWFjaGVkX3RocmVzaCkNCg0KcHQxX2Jhc2VsaW5lX3ByY3RfcmVhY2hlZF9taW4NCg0KYGBgDQoNCiMgQ29uY2x1c2lvbnMNCg0KSW4gdGhpcyBhbmFseXNpcyB3ZSBleGFtaW5lZCBwcm9qZWN0ZWQgYW5kIGhpc3RvcmljIGFjY3VtdWxhdGVkIGNoaWxsIHBvcnRpb25zIGZvciB0aGlzIHBvaW50Og0KDQpgYGB7ciBwdDFfMTB5cnNfY2FwX3Bsb3QsIGNhY2hlID0gVFJVRX0NCnBsb3QocHQxXzEweXJzX2NhcCkNCmBgYA0KDQpXZSBsb29rZWQgYXQgdGhlIHByb2plY3RlZCB0ZW1wZXJhdHVyZXMgYXQgdGhpcyBsb2NhdGlvbiBhY2NvcmRpbmcgdG8gMTAgR0NNcyBpbiB0aGUgcmVjZW50IGhpc3RvcmljIHRpbWUgcGVyaW9kICgxOTk1LTIwMDUpLCBhcyB3ZWxsIGFzIHByb2plY3RlZCBmb3IgMjA4MC0yMDkwIHVuZGVyIHR3byBjYXJib24gZW1pc3Npb25zIHNjZW5hcmlvcyAoUkNQNC41ICYgOC41KS4gT3VyIGFuYWx5c2lzIHNob3dzIHRoYXQgdGhlIHBlcmNlbnQgb2Ygc2Vhc29ucyAmIG1vZGVscyB3aGVyZSB0aGUgbWF4aW11bSBjaGlsbCBwb3J0aW9uIGJ5IHRoZSBlbmQgb2Ygd2ludGVyIGlzIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byB0aGUgYW1vdW50IG9mIGNoaWxsIG5lZWRlZCB0byBncm93IEtlcm1hbiB2YXJpZXR5IG9mIFBpc3RhY2hpb3MgKGByIGNoaWxsX21pbmApIHdhczoNCg0KYGBge3IgcGVyY250X3JlYWNoZWRfYWxsX3RibCwgY2FjaGUgPSBUUlVFfQ0KcHQxX2Jhc2VsaW5lX3ByY3RfcmVhY2hlZF9taW4gJT4lIA0KICBiaW5kX3Jvd3MocHQxXzEweXJzX3ByY3RfcmVhY2hlZF9taW4pDQpgYGANCg0KV2UgYWxzbyBjb21wdXRlZCB0aGUgJ3NhZmUtY2hpbGwnIHBvcnRpb24gZm9yIGVhY2ggb2YgdGhlIHRpbWUgcGVyaW9kcy4gVGhpcyBpcyB0aGUgYW1vdW50IG9mIGNoaWxsIG9uZSBjYW4gZXhwZWN0IHRvIHJlYWNoIDkwJSBvZiB0aGUgdGltZToNCg0KYGBge3IgaGlzdG9zX3JlcGVhdGVkLCBjYWNoZSA9IFRSVUV9DQpzYWZlX2NoaWxsX2Jhc2VsaW5lX3RibCAlPiUgYmluZF9yb3dzKHNhZmVfY2hpbGxfdGJsKQ0KDQpiYXNlbGluZV9oaXN0bw0KcHJvamVjdGVkX2hpc3RvDQpgYGANCg0KIyMgSW50ZXJwcmV0YXRpb24NCg0KVGhpcyBleGFtcGxlIHdhcyBwcmltYXJpbHkgYW4gZXhlcmNpc2Ugb24gaG93IHRvIHdyaXRlIGNvZGUsIGFuZCBzaG91bGQgYmUgdGFrZW4gd2l0aCBhIGdyYWluIG9mIHNhbHQuIEhvd2V2ZXIgdGhlIHJlc3VsdHMgd2UgY2FtZSB1cCB3aXRoIHN1Z2dlc3QgdGhhdCB0aGlzIGxvY2F0aW9uIG1heSBjb250aW51ZSB0byBiZSB2aWFibGUgZm9yIHRoZSBLZXJtYW4gdmFyaWV0eSBvZiBQaXN0YWNoaW9zIGluIDIwODAtMjA5MCB1bmRlciBlbWlzc2lvbiBzY2VuYXJpbyA0LjUsIGJ1dCBtYXkgbm90IHByb2R1Y2UgZW5vdWdoIHdpbnRlciBjaGlsbCBwb3J0aW9ucyBjb25zaXN0ZW50bHkgZW5vdWdoIHRvIGJlIGVjb25vbWljYWxseSB2aWFibGUgdW5kZXIgUkNQIDguNS4gDQoNClRoaXMgYW5hbHlzaXMgd2FzIGJhc2VkIG9uIHRoZSBhc3N1bXB0aW9uIHRoYXQgYWxsIDEwIEdDTXMgcmVjb21tZW5kZWQgZm9yIENhbGlmb3JuaWEgdW5kZXIgdGhlIDR0aCBDbGltYXRlIENoYW5nZSBBc3Nlc3NtZW50IGFyZSBlcXVhbGx5IGxpa2VseS4gV2UgYWxzbyBpbnRlcnBvbGF0ZWQgaG91cmx5IHRlbXBlcmF0dXJlcyBiYXNlZCBvbiBhIGdlbmVyaWMgc3RhdGlzdGljYWwgbW9kZWwgdGhhdCB3YXMgbm90IHNwZWNpZmljIHRvIHRoaXMgbG9jYXRpb24uIFdlIGNvdWxkIGltcHJvdmUgdGhpcyBhbmFseXNpcyBieToNCg0KMS4gU2VsZWN0aW5nIEdDTXMgdGhhdCBhcmUgYmVzdCBzdWl0ZWQgZm9yIENhbGlmb3JuaWEncyBTYW4gSm9hcXVpbiBWYWxsZXkgIA0KMS4gUmV2aWV3aW5nIHRoZSBsYXRlc3QgbGl0ZXJhdHVyZSB0byBhc3Nlc3Mgd2hldGhlciBlbWlzc2lvbiBzY2VuYXJpbyA0LjUgb3IgOC41IHNlZW1zIG1vcmUgbGlrZWx5DQoxLiBXYWl0aW5nIGZvciBDTUlQNiBwcm9qZWN0ZWQgY2xpbWF0ZSBkYXRhIHdoaWNoIHdpbGwgaW5jbHVkZSBzdWItZGFpbHkgdGVtcGVyYXR1cmUgdmFsdWVzIChlLmcuLCBldmVyeSAyIGhvdXJzKQ0KDQojIFF1ZXN0aW9ucyBhbmQgTmV4dCBTdGVwcw0KDQpJcyB0aGlzIGEgZ29vZCB3YXkgdG8gY29tYmluZSAvIGF2ZXJhZ2UgcmVzdWx0cyBmcm9tIG11bHRpcGxlIG1vZGVscywgbXVsdGlwbGUgeWVhcnM/DQoNClNob3VsZCB3ZSB1c2UgYWN0dWFsIG9yIG1vZGVsbGVkIGRhdGEgZm9yIHRoZSBoaXN0b3JpY2FsIGJhc2VsaW5lPw0KDQpXb3VsZCBpdCBiZSB1c2VmdWwgdG8gYWxzbyBjb21wdXRlICoqY2hpbGwgaG91cnMqKj8gT3RoZXIgYWdyb2NsaW1hdGljIG1ldHJpY3M/DQoNCkRvIHlvdSBhbHdheXMgc3RhcnQgY29tcHV0aW5nIGNoaWxsIHBvcnRpb25zIG9uIFNlcHQgMT8gU2hvdWxkIHRoaXMgYmUgYSB2YXJpYWJsZT8NCg0KQWRkIGNhdmVhdHMgYWJvdXQgbm90IGxvb2tpbmcgYXQgYSBzaW5nbGUgbW9kZWwsIHNpbmdsZS15ZWFyLg0KDQpUdXJuIHRoaXMgaW50byBhIFNoaW55IGFwcCB3aXRoIHNlbGVjdGFibGUgc3RhcnQgZGF0ZSBhbmQgcmVxdWlyZWQgbnVtYmVyIG9mIGNoaWxsIHBvcnRpb25zLiANCg0KDQo=