1. R Setup and Packages

Markdown Setup

The easiest way to manage packages is using pacman and its p_load() function.

Install pacman once in the RStudio Console. Then use p_load() to install missing packages and load them.

install.packages("pacman", repos = "https://cloud.r-project.org")

Package management functions

pacman::p_load()    # Installs missing packages and loads them
pacman::p_install() # Installs packages
pacman::p_unload()  # Unloads packages
pacman::p_update()  # Updates installed packages
pacman::p_version() # Checks package versions

Load packages

pacman::p_load(
  tidyverse,
  janitor,
  readxl,
  here,
  lubridate,
  skimr,
  broom,
  fixest
)

Package Reference

tidyverse — Data manipulation and visualization

A collection of packages for data manipulation, organization, visualization, and analysis.

Common functions:

select()       # Select columns
filter()       # Filter rows
mutate()       # Create or modify variables
summarise()    # Calculate summary statistics
group_by()     # Group observations
arrange()      # Sort observations
ggplot()       # Create visualizations
janitor — Data cleaning

Useful for cleaning messy datasets and creating frequency tables.

clean_names()       # Standardize column names
tabyl()             # Create frequency tables
get_dupes(data, ID) # Identify duplicate observations
readxl — Import Excel data

Used to import Excel spreadsheets into R.

read_excel("data.xlsx") # Import an Excel worksheet
excel_sheets("data.xlsx") # List worksheet names
here — Project file paths

Creates project-relative file paths rather than relying on absolute paths.

here("data", "raw", "data.csv")
lubridate — Dates and times

Simplifies working with dates, times, and time intervals.

year(date)              # Extract year
month(date)             # Extract month
day(date)               # Extract day
ymd("2026-11-12")       # Convert year-month-day string
skimr — Data summaries

Provides a detailed overview of a dataset, including descriptive statistics and missing values.

skim(data)
broom — Organize model results

Converts statistical model outputs into tidy data frames.

tidy(model)    # Coefficients, standard errors, p-values
glance(model)  # Model-level statistics
augment(model) # Observation-level results
fixest — Econometrics and regression

Useful for OLS, fixed-effects regression, instrumental variables, and models with robust or clustered standard errors.

feols(y ~ x1 + x2, data = data)

# Fixed effects
feols(y ~ x1 + x2 | year + region, data = data)

2. Importing and Inspecting Data

Importing and Exporting

read_csv("data.csv")       # Import CSV file
read_excel("data.xlsx")    # Import Excel file
readRDS("data.rds")        # Import R data object

write_csv(data, "data.csv") # Export CSV
saveRDS(data, "data.rds")   # Save R data object

Inspecting Data

head(data)       # Preview first observations
tail(data)       # Preview last observations
glimpse(data)    # Inspect columns and data types
dim(data)        # Number of rows and columns
nrow(data)       # Number of rows
ncol(data)       # Number of columns
names(data)      # Column names
summary(data)    # Descriptive statistics
skim(data)       # Detailed dataset overview
distinct(data)   # Identify unique observations

Common Data Types

Numeric

Numbers that can be used in mathematical calculations.

Examples: electricity price, load, temperature, revenue.

price <- 45.75
load_mw <- 1200
Integer

Whole numbers without decimal places.

year <- 2026L
Character

Text or strings.

region <- "Pacific Northwest"
Logical

Boolean values: TRUE or FALSE.

is_weekend <- TRUE
Date and datetime

Values representing calendar dates or specific times.

date <- as.Date("2026-11-12")
Factor

Categorical variables with defined levels.

region <- factor(c("North", "South", "North"))

Missing Values

Missing observations are represented by NA in R.

is.na(data)                 # Identify missing values
sum(is.na(data))            # Count missing values
colSums(is.na(data))        # Missing values by column
drop_na(data)               # Remove rows with missing values
replace_na(data, list(x=0)) # Replace missing values in x
Important: Missing values in calculations

Many R functions return NA when a vector contains missing values.

Use na.rm = TRUE to remove missing values from a calculation.

mean(data$price, na.rm = TRUE)
sum(data$load, na.rm = TRUE)

3. Data Manipulation

dplyr Functions

The dplyr package is part of the tidyverse and provides a consistent set of functions for manipulating datasets.

Select and filter — Choose columns and rows
select(data, price, load)       # Select columns
filter(data, region == "West")  # Select rows
  • select() chooses which columns to keep.
  • filter() chooses which observations to keep based on conditions.
Mutate and rename — Create and modify variables
mutate(data, revenue = price * load)

rename(data, electricity_price = price)
  • mutate() creates new variables or modifies existing ones.
  • rename() changes column names.
Arrange — Sort observations
arrange(data, date)          # Ascending
arrange(data, desc(price))   # Descending
Group and summarize — Aggregate observations
data |>
  group_by(region) |>
  summarise(
    average_price = mean(price, na.rm = TRUE),
    total_load = sum(load, na.rm = TRUE),
    observations = n()
  )
  • group_by() defines groups for subsequent calculations.
  • summarise() calculates summary statistics.
  • n() counts observations within each group.
  • count() is a convenient shortcut for counting observations by group.
Other useful dplyr functions
count(data, region)          # Count observations
distinct(data, region)       # Unique values or combinations
across()                     # Apply functions across columns
case_when()                  # Create conditional variables
lag(price)                   # Previous observation
lead(price)                  # Next observation

Combining and Reshaping Datasets

Joining datasets

Joins combine datasets using one or more shared identifiers.

left_join(x, y, by = "ID")   # Keep all rows in x
right_join(x, y, by = "ID")  # Keep all rows in y
inner_join(x, y, by = "ID")  # Keep matching rows only
full_join(x, y, by = "ID")   # Keep all rows from both
anti_join(x, y, by = "ID")   # Keep unmatched rows from x

Important: Always check whether join keys are unique before joining. Duplicate keys can unexpectedly multiply rows and affect subsequent statistics and regressions.

count(data, ID)
janitor::get_dupes(data, ID)
Reshaping datasets

Reshaping changes how observations and variables are organized.

pivot_longer(
  data,
  cols = c(January, February, March),
  names_to = "month",
  values_to = "sales"
)

pivot_wider(
  data,
  names_from = month,
  values_from = sales
)
  • pivot_longer() converts wide data into long data by stacking columns into key-value rows.
  • pivot_wider() converts long data into wide data by spreading values across columns.

Pipes

Pipes pass the result of one operation into the next operation.

data |>
  filter(region == "West") |>
  group_by(year) |>
  summarise(
    average_price = mean(price, na.rm = TRUE)
  )

Common pipe operators:

  • |> Base R pipe.
  • %>% magrittr pipe, commonly used in tidyverse code.

Both allow operations to be written sequentially rather than nesting functions.

4. Statistics and Econometrics

Descriptive Statistics

Descriptive Statistics

Numerical summaries that describe the characteristics, distribution, and variability of a dataset.

Measure Definition
Mean Arithmetic average
Median Middle observation
Variance Average squared deviation from the mean
Standard deviation Square root of variance; measures spread in original units
Quantiles Values dividing a distribution into specified proportions
Interquartile range (IQR) 75th percentile minus 25th percentile
Minimum / Maximum Smallest and largest observations

Why they matter: Help identify unusual values, skewed distributions, missing data, and differences between groups.

R functions:

summary(data)
mean(x, na.rm = TRUE)
median(x, na.rm = TRUE)
sd(x, na.rm = TRUE)
var(x, na.rm = TRUE)
quantile(x, na.rm = TRUE)
Correlations

Measures the strength and direction of the relationship between two variables.

Pearson’s correlation coefficient, r, ranges from -1 to +1.

  • +1: Perfect positive linear relationship.
  • 0: No linear relationship.
  • -1: Perfect negative linear relationship.

Why they matter: Useful for exploring relationships between variables and identifying potential explanatory variables before regression analysis.

Important: Correlation does not imply causation. A correlation may reflect reverse causality, a third variable, or a shared trend.

R functions:

cor(x, y)
cor.test(x, y)
GGally::ggpairs(data)
Linear Regression

A statistical method used to estimate the relationship between an outcome variable (dependent variable) and one or more explanatory variables (independent variables).

Simple linear regression:

\[ Y_i = \beta_0 + \beta_1 X_i + \epsilon_i \]

Where:

  • Y: Outcome variable.
  • X: Explanatory variable.
  • β₀: Intercept.
  • β₁: Slope coefficient.
  • ε: Error term.

Interpretation: A coefficient represents the estimated change in the outcome associated with a one-unit increase in an explanatory variable, holding other included variables constant.

Example:

If the estimated coefficient on electricity load is 0.04, a one-unit increase in load is associated with a 0.04-unit increase in price, holding other included variables constant.

R functions:

lm(y ~ x, data = data)

lm(y ~ x1 + x2 + x3, data = data)

fixest::feols(y ~ x1 + x2, data = data)
Model Summaries

Regression output summarizes coefficient estimates, uncertainty, and model fit.

Output Meaning
Coefficient Estimated relationship between an explanatory variable and outcome
Standard error Estimated uncertainty around a coefficient
t-statistic Coefficient divided by its standard error
p-value Evidence against the null hypothesis under the model assumptions
R-squared Proportion of sample variation in the outcome explained by the fitted model
Adjusted R-squared R-squared adjusted for the number of explanatory variables
F-statistic Tests a joint restriction on model coefficients
Observations Number of observations used in estimation

Important: A high R-squared does not automatically mean a model is unbiased, causal, or useful for forecasting.

R functions:

summary(model)
broom::tidy(model)
broom::glance(model)
Predictions

Estimated outcome values generated using a fitted statistical model and specified explanatory-variable values.

Two important distinctions:

  • Fitted values: Predictions for observations already included in the estimation dataset.
  • Out-of-sample predictions: Predictions for new observations not used to estimate the model.

Why they matter: Useful for forecasting, scenario analysis, and estimating expected outcomes under different conditions.

Important: A prediction is not necessarily a causal estimate.

R functions:

predict(model)
predict(model, newdata = new_data)
Residuals

The differences between observed outcomes and the values predicted by a regression model.

\[ e_i = Y_i - \hat{Y}_i \]

  • Positive residual: Model underpredicted the observed value.
  • Negative residual: Model overpredicted the observed value.
  • Zero residual: Prediction exactly matches the observation.

Why they matter: Residuals help evaluate model fit and identify potential problems such as nonlinear relationships, outliers, heteroskedasticity, and autocorrelation.

R functions:

residuals(model)
fitted(model)
broom::augment(model)
plot(model)
Hypothesis Testing

A statistical procedure used to evaluate whether sample data provide sufficient evidence against a specified claim about a population or model parameter.

Two competing hypotheses:

  • Null hypothesis (H₀): The default claim, often that a coefficient equals zero or that no relationship exists.
  • Alternative hypothesis (H₁): The competing claim, such as a coefficient being different from zero.

Important terminology:

  • p-value: Probability of observing a test statistic at least as extreme as the one obtained, assuming the null hypothesis and model assumptions are true.
  • Significance level (α): Threshold chosen for rejecting the null hypothesis, commonly 0.05.
  • Type I error: Rejecting a true null hypothesis.
  • Type II error: Failing to reject a false null hypothesis.

Important: Statistical significance does not necessarily imply practical importance. We generally say “reject” or “fail to reject” the null hypothesis rather than prove or accept it.

R functions:

t.test(x, y)
summary(model)
Robust Standard Errors

Standard errors calculated using an estimator that is robust to certain violations of conventional regression assumptions, particularly heteroskedasticity.

Why they matter: Conventional standard errors may be incorrect when error variance is not constant, leading to unreliable t-statistics, p-values, and confidence intervals.

Important distinctions:

  • Regression coefficients generally remain the same.
  • Standard errors change.
  • t-statistics, p-values, and confidence intervals may change.

Robust standard errors do not fix biased coefficients, omitted variables, or endogeneity.

R functions:

sandwich::vcovHC(model, type = "HC1")

lmtest::coeftest(
  model,
  vcov = sandwich::vcovHC(model, type = "HC1")
)
Clustered Standard Errors

Standard errors that account for the possibility that observations within the same group are correlated.

Example: Hourly electricity prices within a region may share common influences such as weather, transmission infrastructure, and generation resources.

Clustering by region allows errors to be correlated within each region while assuming independence across regions.

Why they matter: Treating correlated observations as independent can underestimate uncertainty and produce misleading statistical significance.

Important: The number of independent clusters matters. A large number of observations does not compensate for having very few clusters.

R functions:

fixest::feols(
  price ~ load + temperature,
  data = energy_data,
  vcov = ~region
)
Fixed Effects and Panel Regression

Fixed effects control for unobserved characteristics of individual units that do not change over time.

Panel data follow the same units (such as regions, states, or firms) across multiple time periods.

A simplified model:

\[ Y_{it} = \beta X_{it} + \alpha_i + \epsilon_{it} \]

Where:

  • Y: Outcome for unit i at time t.
  • X: Explanatory variable.
  • β: Estimated coefficient.
  • α: Unit-specific fixed effect.
  • ε: Error term.

Interpretation: The coefficient is identified using variation within the same unit over time, rather than simply comparing differences between units.

Important: Time-invariant explanatory variables are absorbed by unit fixed effects. Fixed effects do not automatically control for unobserved factors that change over time.

R functions:

fixest::feols(
  price ~ load + temperature | region,
  data = energy_data
)

# Region and year fixed effects
fixest::feols(
  price ~ load + temperature | region + year,
  data = energy_data
)
Weighted Regression

A regression technique that assigns different weights to observations when estimating the model.

Ordinary least squares gives every observation equal weight. Weighted least squares gives observations different influence on the estimated coefficients.

\[ \min_{\beta} \sum_i w_i(Y_i-X_i\beta)^2 \]

Weights may be used for:

  • Unequal sampling probabilities.
  • Different levels of measurement precision.
  • Appropriately modeled heteroskedasticity.

Important: Survey weights, frequency weights, and inverse-variance weights have different meanings and should not be used interchangeably.

R functions:

lm(
  price ~ load + temperature,
  data = energy_data,
  weights = precision_weight
)
Instrumental Variables (IV)

A method used to estimate causal relationships when an explanatory variable is endogenous.

Endogeneity occurs when an explanatory variable is correlated with the regression error term.

Potential causes include:

  • Reverse causality.
  • Omitted variable bias.
  • Measurement error.
  • Simultaneous determination of variables.

An instrument must satisfy two key conditions:

  1. Relevance: The instrument is correlated with the endogenous explanatory variable.
  2. Exogeneity / exclusion restriction: The instrument affects the outcome only through the endogenous explanatory variable and is uncorrelated with the structural error term.

Example: Electricity prices and consumption may be simultaneously determined. An external source of price variation might help identify the effect of prices on consumption, provided the instrument is valid.

Important: A strong first-stage relationship does not prove an instrument is valid. Weak instruments can produce unreliable estimates, and the exclusion restriction is generally not directly testable.

R functions:

fixest::feols(
  consumption ~ temperature | price ~ instrument,
  data = energy_data
)
Heteroskedasticity

A condition in which the variance of the regression error term is not constant across observations.

  • Homoskedasticity: Constant error variance.
  • Heteroskedasticity: Nonconstant error variance.

Why it matters: OLS coefficients can remain unbiased under the usual exogeneity assumptions, but conventional standard errors are generally incorrect.

How to address it:

  • Use heteroskedasticity-robust standard errors.
  • Consider weighted least squares when the variance structure is appropriately understood.
  • Reconsider model specification if necessary.

R functions:

lmtest::bptest(model)
sandwich::vcovHC(model, type = "HC1")
Multicollinearity

A condition in which two or more explanatory variables are highly correlated with one another.

Why it matters:

  • Coefficient estimates may have large standard errors.
  • Individual coefficients may appear statistically insignificant.
  • Coefficient estimates may be sensitive to model specification.
  • It becomes difficult to isolate individual relationships.

Important: Multicollinearity does not automatically bias OLS estimates. It primarily affects precision and stability.

How to identify it:

  • Examine correlation matrices.
  • Calculate variance inflation factors (VIFs).

R functions:

cor(data)
car::vif(model)
Omitted Variable Bias (OVB)

Bias in regression coefficient estimates that occurs when an important variable is excluded from a model and is correlated with both an included explanatory variable and the outcome.

Two conditions:

  1. The omitted variable affects the outcome.
  2. The omitted variable is correlated with an included explanatory variable.

Example: Estimating the relationship between electricity prices and consumption without accounting for seasonality may bias the estimated price relationship if seasonality affects both.

How to address it:

  • Include relevant control variables.
  • Use fixed effects when appropriate.
  • Consider instrumental variables when endogeneity is present and a credible instrument exists.
  • Improve the research design.

Important: Adding more variables does not automatically improve a model. Inappropriate controls can introduce other forms of bias.

Fixed Effects vs. Random Effects

Two approaches to modeling differences between individual units in panel data.

Feature Fixed Effects Random Effects
Unit-specific differences May be correlated with explanatory variables Assumed uncorrelated with explanatory variables
Estimation Uses within-unit variation Uses within- and between-unit variation
Time-invariant variables Cannot estimate separate coefficients Can estimate coefficients
Main assumption Allows correlation between unit effects and regressors Assumes no correlation

Interpretation:

  • Fixed effects control for unobserved unit characteristics that may be correlated with explanatory variables.
  • Random effects assume those characteristics are uncorrelated with explanatory variables.

How to choose: Fixed effects are often preferable when unobserved unit-level characteristics may be correlated with regressors. Random effects may be appropriate when the stronger assumption is defensible.

R functions:

plm::plm(
  y ~ x1 + x2,
  data = panel_data,
  index = c("region", "year"),
  model = "within"
)

plm::plm(
  y ~ x1 + x2,
  data = panel_data,
  index = c("region", "year"),
  model = "random"
)

plm::phtest(fixed_model, random_model)
Interaction Terms

Variables created by multiplying two or more explanatory variables together. Interactions allow the relationship between one variable and the outcome to depend on another variable.

Example: The relationship between temperature and electricity demand may differ between weekdays and weekends.

\[ Load_i = \beta_0 + \beta_1 Temperature_i + \beta_2 Weekend_i + \beta_3(Temperature_i \times Weekend_i) + \epsilon_i \]

Interpretation:

  • β₀: Expected load when temperature is zero on weekdays.
  • β₁: Temperature slope on weekdays.
  • β₂: Weekend versus weekday difference when temperature is zero.
  • β₃: Difference in the temperature slope between weekends and weekdays.

The temperature slope is:

  • Weekdays: β₁
  • Weekends: β₁ + β₃

R syntax:

lm(
  load ~ temperature * weekend,
  data = energy_data
)

The * operator includes both main effects and the interaction:

temperature * weekend

# Equivalent to:
temperature + weekend + temperature:weekend

5. Data Visualization

ggplot2

The ggplot2 package creates visualizations using a layered approach.

Basic structure:

ggplot(data, aes(x = variable1, y = variable2)) +
  geom_point() +
  labs(
    title = "Relationship Between Variables",
    x = "Variable 1",
    y = "Variable 2"
  )

Common Geometries

Scatterplots

Show the relationship between two continuous variables.

ggplot(data, aes(x = load, y = price)) +
  geom_point()
Line graphs

Useful for visualizing trends over time.

ggplot(data, aes(x = date, y = price)) +
  geom_line()
Bar charts

Compare categories or groups.

ggplot(data, aes(x = region, y = average_price)) +
  geom_col()
Histograms

Visualize the distribution of a continuous variable.

ggplot(data, aes(x = price)) +
  geom_histogram()

6. Time-Series Analysis

Dates and Time

Electricity-market data frequently involve hourly, daily, monthly, and annual observations.

ymd("2026-11-12")       # Parse a date
year(date)              # Extract year
month(date)             # Extract month
day(date)               # Extract day
hour(datetime)          # Extract hour
floor_date(date, "month") # Round down to month

Time-Series Concepts

Trends

Long-term increases or decreases in a variable over time.

Seasonality

Recurring patterns that occur at regular intervals, such as daily, weekly, or annual cycles.

Autocorrelation

The correlation of a variable with its own past values.

Lagged Variables

Variables representing previous observations.

data |>
  arrange(date) |>
  mutate(previous_price = lag(price))
Moving Averages

Averages calculated over a rolling window of observations to smooth short-term fluctuations.

7. Electricity Markets and Economic Applications

Electricity Market Fundamentals

Load and Demand

Electricity consumption over a specified period, commonly measured in megawatts (MW).

Generation Mix

The combination of resources used to generate electricity, including hydropower, natural gas, coal, wind, solar, and nuclear.

Marginal Cost

The additional cost of producing one more unit of electricity.

Locational Marginal Pricing (LMP)

The price of electricity at a particular location, reflecting the marginal cost of serving additional demand, including energy, congestion, and losses.

Transmission Congestion

Occurs when transmission constraints limit the ability to move electricity between locations.

Resource Adequacy

The ability of the electricity system to maintain sufficient resources to meet demand reliably.

Ancillary Services

Services that help maintain grid reliability, including frequency regulation and operating reserves.

Hydropower

Electricity generated from moving water. Reservoir levels, streamflow, water availability, and operating constraints influence hydropower generation.

Western Electricity Markets

CAISO

California Independent System Operator.

WEIM

Western Energy Imbalance Market.

EDAM

Extended Day-Ahead Market.

Markets+

A Western day-ahead electricity market initiative.

RTO / ISO

Regional Transmission Organization / Independent System Operator. Organizations that coordinate electricity transmission and wholesale market operations.

8. Useful R Shortcuts and Troubleshooting

Common RStudio Shortcuts

Action Mac Windows / Linux
Run current line or selection Cmd + Enter Ctrl + Enter
Insert assignment operator Option + - Alt + -
Reformat code Cmd + Shift + A Ctrl + Shift + A
Comment / uncomment Cmd + Shift + C Ctrl + Shift + C
Insert code chunk Option + Cmd + I Ctrl + Alt + I

Common Troubleshooting

Object not found

R cannot find the object referenced in the code.

Check spelling, capitalization, and whether the object has been created or imported.

Could not find function

The function may belong to a package that has not been loaded.

Load the package or use its namespace:

library(dplyr)

dplyr::filter(data, price > 50)
Unexpected NA values

Check whether missing observations or mismatched data types are affecting calculations and joins.

sum(is.na(data))
str(data)
Unexpected number of rows after joining

Check whether the join keys contain duplicates.

count(data, ID)
janitor::get_dupes(data, ID)
Package installation errors

Make sure R has a CRAN repository selected.

install.packages(
  "package_name",
  repos = "https://cloud.r-project.org"
)

9. Experimental Data Analysis

Importing and defining data

Data downloaded from https://transmission.bpa.gov/business/operations/wind/baltwg3.aspx. Real-time data from the last seven days, updated every five minutes.

Includes data on load and VER (Variable Energy Resource, usually solar or wind, which change with the conditions), as well as hydro, fossil/biomass, interchange, and nuclear.

Term Definition Constraints
Load The amount of electrical power being consumed by customers and other electricity users at any given point in time. Usually measured in megawatts (MW). This represents electricity demand not generation.
VER (Variable Energy Resources) Electricity generation resources whose output varies based on environmental conditions rather than being fully dispatchable on demand. Common examples include wind or solar generation. Dependent on weather conditions, which can be unstable, but not always unpredictable.
Hydroelectric Generated by converting the energy of moving or falling water into electrical energy using turbines and generators. Can be adjusted relatively quickly by controlling water flowing through turbines. Water availability, reservoir levels, environmental requirements, and operational restrictions.
Fossil/Biomass Generation Generated by the burning of fossil fuels of biomass. Examples: Natural gas, coal, organic matter used as fuel. Most fossil fuel plants are dispatchable, and output is easily adjusted. Emissions and environmental regulations. Operating costs depend on fuel prices.
Nuclear Generated through nuclear fission, where splitting atomic nuclei releases heat which produces steam to drive a turbine. Provides relatively consistent, continuous generation, but has high construction cost and is generally less flexible.
Net Interchange The net amount of electricity transferred between a balancing authority (ie BPA) and neighboring balancing authorities.
Generation Electricity produced by power plants and other resources.
Dispatch The process of determining which generation resources operate and at what output levels.
Dispatchable Generation Generation that operators can control or adjust when needed, subject to operating constraints.
Ramp Rate How quickly a generation resource can increase or decrease its output.
Peak Load The maximum electricity demand during a specific period.
Load Forecasting Predicting the future electricity demand.
Generation Mix The combination of different resources used to produce electricity.
MW (megawatt) A unit of power, measuring the instantaneous rate of electricity production or consumption.
MWh (megawatt-hour) A unit of energy, measuring electricity produced or consumed over time.

Note: The dataset reports five-minute MW readings. These are measurements of power at particular points in time, not the total energy generated over each five-minute interval.

Simply, \[Generation + Net Imports ≈ Load \] Expanding on that, \[Hydro + VER + Fossil/Biomass + Nuclear + Net Imports ≈ Load \]

# Import BPA balancing authority data
load_data <- read_tsv(
  "~/Desktop/BPA_Data.txt"
) %>%
  clean_names() %>%
  mutate(
    date_time = mdy_hm(date_time, tz = "America/Los_Angeles")
  )

# Inspect imported data
# glimpse(load_data)
# summary(load_data)
Exploratory data analysis
Data overview & cleaning
# Number of observations and variables
dim(load_data)
#> [1] 2016    7
# Variable names
names(load_data)
#> [1] "date_time"      "load"           "ver"            "hydro"         
#> [5] "fossil_biomass" "interchange"    "nuclear"
# Check data types
glimpse(load_data)
#> Rows: 2,016
#> Columns: 7
#> $ date_time      <dttm> 2026-11-07 00:00:00, 2026-11-07 00:05:00, 2026-11-07 0…
#> $ load           <dbl> 6191, 6188, 6189, 6172, 6142, 6143, 6133, 6095, 6030, 6…
#> $ ver            <dbl> 446, 391, 373, 361, 363, 363, 372, 384, 380, 379, 380, …
#> $ hydro          <dbl> 5484, 5547, 5502, 5511, 5517, 5484, 5467, 5508, 5458, 5…
#> $ fossil_biomass <dbl> 779, 781, 781, 782, 782, 782, 782, 780, 781, 782, 781, …
#> $ interchange    <dbl> 1702, 1717, 1656, 1674, 1699, 1671, 1677, 1766, 1774, 1…
#> $ nuclear        <dbl> 1144, 1146, 1148, 1151, 1149, 1146, 1149, 1151, 1145, 1…
# Check for missing values
colSums(is.na(load_data))
#>      date_time           load            ver          hydro fossil_biomass 
#>              0            159            159            159            159 
#>    interchange        nuclear 
#>            159            159
# Check for duplicate timestamps
sum(duplicated(load_data$date_time))
#> [1] 0
# Check date range
range(load_data$date_time)
#> [1] "2026-11-07 00:00:00 PST" "2026-11-13 23:55:00 PST"
# We need to drop for November 13, since we only have partial day data. 

# Count missing observations by date
load_data %>%
  group_by(date = as.Date(date_time)) %>%
  summarise(
    total_observations = n(),
    missing_load = sum(is.na(load)),
    valid_load = sum(!is.na(load)),
    .groups = "drop"
  )
# Display timestamps with missing measurements
load_data %>%
  filter(is.na(load)) %>%
  select(date_time)
  # print(n = 30)

# Retain only complete calendar days
complete_data <- load_data %>%
  mutate(date = as.Date(date_time)) %>%
  group_by(date) %>%
  filter(
    n() == 288,
    all(
      if_all(
        c(load, ver, hydro, fossil_biomass, interchange, nuclear),
        ~ !is.na(.x)
      )
    )
  ) %>%
  ungroup()
# Calculate descriptive statistics
descriptive_stats <- complete_data %>%
  summarise(
    across(
      c(load, ver, hydro, fossil_biomass, interchange, nuclear),
      list(
        N = ~sum(!is.na(.x)),
        Mean = ~mean(.x, na.rm = TRUE),
        Median = ~median(.x, na.rm = TRUE),
        SD = ~sd(.x, na.rm = TRUE),
        Min = ~min(.x, na.rm = TRUE),
        Max = ~max(.x, na.rm = TRUE)
      )
    )
  ) %>%
  pivot_longer(
    cols = everything(),
    names_to = c("Variable", ".value"),
    names_pattern = "(.+)_(N|Mean|Median|SD|Min|Max)"
  ) %>%
  mutate(
    Variable = recode(
      Variable,
      load = "Load",
      ver = "Variable Energy Resources",
      hydro = "Hydro",
      fossil_biomass = "Fossil/Biomass",
      interchange = "Net Interchange",
      nuclear = "Nuclear"
    ),
    across(where(is.numeric), ~round(.x, 2))
  )

# Display formatted table
descriptive_stats %>%
  knitr::kable(
    caption = "Descriptive Statistics: BPA Balancing Authority",
    col.names = c(
      "Variable",
      "N",
      "Mean (MW)",
      "Median (MW)",
      "Std. Dev. (MW)",
      "Minimum (MW)",
      "Maximum (MW)"
    ),
    align = c("l", rep("r", 6))
  ) %>%
  kableExtra::kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = TRUE,
    position = "center"
  )
Descriptive Statistics: BPA Balancing Authority
Variable N Mean (MW) Median (MW) Std. Dev. (MW) Minimum (MW) Maximum (MW)
Load 1440 6896.68 6955.5 590.26 5732 8132
Variable Energy Resources 1440 414.26 161.0 534.41 5 2367
Hydro 1440 6567.88 6539.0 968.35 4058 8789
Fossil/Biomass 1440 984.71 1092.0 253.63 489 1200
Net Interchange 1440 2258.71 2211.5 518.70 1082 3790
Nuclear 1440 1147.98 1148.0 4.00 1125 1157
# Summarize data completeness by day
daily_data_quality <- load_data %>%
  mutate(date = as.Date(date_time)) %>%
  group_by(date) %>%
  summarise(
    observations = n(),
    missing = sum(is.na(load)),
    valid = sum(!is.na(load)),
    completeness = round(valid / 288 * 100, 1),
    .groups = "drop"
  )

# Display data-quality table
daily_data_quality %>%
  knitr::kable(
    caption = "Daily Data Completeness",
    col.names = c(
      "Date",
      "Observations",
      "Missing",
      "Valid",
      "Completeness (%)"
    ),
    align = c("l", "r", "r", "r", "r")
  ) %>%
  kableExtra::kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  )
Daily Data Completeness
Date Observations Missing Valid Completeness (%)
2026-11-07 192 0 192 66.7
2026-11-08 288 0 288 100.0
2026-11-09 288 0 288 100.0
2026-11-10 288 0 288 100.0
2026-11-11 288 0 288 100.0
2026-11-12 288 0 288 100.0
2026-11-13 288 63 225 78.1
2026-11-14 96 96 0 0.0
Daily summaries: load and generation

For this, i will ise complete_data so we are comparing full days rather than accidentally including incomplete days. For each day, we will calculate:

  • Average Load: typical balancing area load throughout the day
  • Peak Load: highest five-minute load reading
  • Minimum load: lowest five-minute load reading
  • Average generation by resource
  • Average interchange
# Calculate daily load and generation summaries
daily_summary <- complete_data %>%
  group_by(date) %>%
  summarise(
    # Date
    day_of_week = wday(first(date), label = TRUE, abbr = FALSE), #extracts day of week, returning teh first value in the "date" group

    # Load
    observations = n(),
    avg_load = mean(load),
    peak_load = max(load),
    peak_time = format(date_time[which.max(load)], "%H:%M"),
    min_load = min(load),
    min_time = format(date_time[which.min(load)], "%H:%M"),

    # Generation
    avg_ver = mean(ver),
    avg_hydro = mean(hydro),
    avg_fossil_biomass = mean(fossil_biomass),
    avg_nuclear = mean(nuclear),

    # Interchange
    avg_interchange = mean(interchange),

    .groups = "drop"
  )

# Display daily summary table
daily_summary %>%
  knitr::kable(
    caption = "Daily Load and Generation Summary",
    col.names = c(
      "Date",
      "Day",
      "Observations",
      "Average Load (MW)",
      "Peak Load (MW)",
      "Peak Time",
      "Minimum Load (MW)",
      "Minimum Time",
      "Average VER (MW)",
      "Average Hydro (MW)",
      "Average Fossil/Biomass (MW)",
      "Average Nuclear (MW)",
      "Average Interchange (MW)"
    ),
    align = c("l", "l", rep("r", 11)),
    digits = 1
  ) %>%
  kableExtra::kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = TRUE,
    position = "center"
  )
Daily Load and Generation Summary
Date Day Observations Average Load (MW) Peak Load (MW) Peak Time Minimum Load (MW) Minimum Time Average VER (MW) Average Hydro (MW) Average Fossil/Biomass (MW) Average Nuclear (MW) Average Interchange (MW)
2026-11-08 Sunday 288 6481.1 7057 17:35 5732 02:00 1240.4 5813.8 549.7 1144.6 2308.1
2026-11-09 Monday 288 7009.9 7779 07:40 5979 02:00 125.6 6790.7 985.6 1150.4 2082.6
2026-11-10 Tuesday 288 7206.6 8132 17:30 6030 03:00 523.8 6874.0 1142.9 1149.4 2524.0
2026-11-11 Wednesday 288 6872.9 7820 17:30 5931 03:30 128.3 6667.7 1112.2 1147.5 2223.8
2026-11-12 Thursday 288 6912.9 7581 08:10 5893 02:30 53.1 6693.2 1133.1 1148.0 2155.0

The above table shows all of the complete days in the dataset (November 8 - November 12). The lowest load occurred on November 8, which is s Sunday. This tracks, as industry is not as active on weekends. We see a higher interchange on this day as well. The minimum load time is relatively consistent across each day, ranging from 2am - 3:30am. Again, this makes sense, as the vast majority of people are asleep at this time.

We see the peak load times vary, however. Two days (the 9th and the 12th) see peak times in the morning, at 7:40am and 8:10am respectively. The other days have almost remarkable consistency, with peak load occurring in the five minutes between 5:30pm and 5:35pm.

The average VER is significantly higher on Sunday. Sunday was sunnier than other days, so this may indicate a higher access to solar power.

Daily energy calculations: MW to MWh

Remember: MW (megawatts) are a measure of power at a point in time. MWh (megawatt-hours) are a measure of energy over a period of time. The data contains five-minute MW observations.

We can estimate energy by multiplying each observation by the length of its interval: \[ MWh = MW \times \frac{5}{60}\]

We can now calculate daily energy using complete_data again.

# Calculate daily energy consumption and generation
daily_energy <- complete_data %>%
  group_by(date) %>%
  summarise(
    # Energy consumption
    load_mwh = sum(load * (5 / 60)),

    # Energy generation by resource
    ver_mwh = sum(ver * (5 / 60)),
    hydro_mwh = sum(hydro * (5 / 60)),
    fossil_biomass_mwh = sum(fossil_biomass * (5 / 60)),
    nuclear_mwh = sum(nuclear * (5 / 60)),

    # Total reported generation
    total_generation_mwh = sum(
      (ver + hydro + fossil_biomass + nuclear) * (5 / 60)
    ),

    .groups = "drop"
  )

# Display daily energy table
daily_energy %>%
  knitr::kable(
    caption = "Daily Energy Consumption and Generation",
    col.names = c(
      "Date",
      "Load (MWh)",
      "VER (MWh)",
      "Hydro (MWh)",
      "Fossil/Biomass (MWh)",
      "Nuclear (MWh)",
      "Total Generation (MWh)"
    ),
    align = c("l", rep("r", 6)),
    digits = 1
  ) %>%
  kableExtra::kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = TRUE,
    position = "center"
  )
Daily Energy Consumption and Generation
Date Load (MWh) VER (MWh) Hydro (MWh) Fossil/Biomass (MWh) Nuclear (MWh) Total Generation (MWh)
2026-11-08 155545.8 29770.3 139530.7 13192.5 27470.9 209964.4
2026-11-09 168237.1 3013.7 162977.8 23655.3 27608.4 217255.2
2026-11-10 172957.7 12571.7 164976.2 27429.9 27584.8 232562.8
2026-11-11 164950.7 3079.4 160024.7 26692.8 27541.0 217338.0
2026-11-12 165910.8 1275.6 160636.2 27195.0 27552.1 216658.9
Hourly load profiles

First we need to calculate the average load by hour. Each hour contains 12 5-minute observations. We will group all five days together by hour, then calculate the average load across all observations recorded during that hour.

# Calculate average load by hour of day
hourly_load <- complete_data %>%
  mutate(
    hour = lubridate::hour(date_time)
  ) %>%
  group_by(hour) %>%
  summarise(
    avg_load = mean(load),
    min_load = min(load),
    max_load = max(load),
    sd_load = sd(load),
    observations = n(),
    .groups = "drop"
  )

# Display hourly load profile
hourly_load %>%
  knitr::kable(
    caption = "Average Hourly BPA Balancing Authority Load",
    col.names = c(
      "Hour",
      "Average Load (MW)",
      "Minimum Load (MW)",
      "Maximum Load (MW)",
      "Std. Dev. (MW)",
      "Observations"
    ),
    align = c("l", rep("r", 5)),
    digits = 1
  ) %>%
  kableExtra::kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = TRUE,
    position = "center"
  )
Average Hourly BPA Balancing Authority Load
Hour Average Load (MW) Minimum Load (MW) Maximum Load (MW) Std. Dev. (MW) Observations
0 6129.3 5837 6421 138.9 60
1 6021.7 5738 6271 134.8 60
2 5978.7 5732 6184 122.0 60
3 5997.3 5742 6178 127.1 60
4 6162.4 5790 6503 197.9 60
5 6528.3 5894 7030 318.6 60
6 7005.9 6142 7629 428.2 60
7 7306.6 6396 7779 427.4 60
8 7364.9 6728 7778 319.7 60
9 7354.1 6869 7639 265.2 60
10 7291.5 6841 7641 257.0 60
11 7232.4 6822 7599 264.3 60
12 7199.9 6759 7575 283.2 60
13 7161.6 6701 7548 296.1 60
14 7116.7 6659 7485 300.5 60
15 7154.4 6678 7655 308.4 60
16 7268.9 6606 8030 396.0 60
17 7526.4 6973 8132 362.9 60
18 7462.7 6875 8062 353.6 60
19 7334.5 6773 7949 344.0 60
20 7150.4 6600 7734 300.0 60
21 6885.8 6415 7434 256.8 60
22 6578.3 6180 7078 208.9 60
23 6307.5 5998 6637 161.0 60
# Plot average hourly load
ggplot(hourly_load, aes(x = hour, y = avg_load)) +
  geom_line(linewidth = 1) +
  geom_point(size = 2) +
  scale_x_continuous(
    breaks = 0:23,
    labels = sprintf("%02d:00", 0:23)
  ) +
  labs(
    title = "Average Hourly BPA Balancing Authority Load",
    subtitle = "Average five-minute load observations across November 8–12, 2026",
    x = "Hour of Day",
    y = "Average Load (MW)"
  ) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    plot.title = element_text(face = "bold")
  )

This is the pooled average, but it hides differences between individual days.

# Calculate average hourly load by day
daily_hourly_load <- complete_data %>%
  mutate(
    hour = lubridate::hour(date_time)
  ) %>%
  group_by(date, hour) %>%
  summarise(
    avg_load = mean(load),
    .groups = "drop"
  ) %>%
  mutate(
    day_of_week = lubridate::wday(
      date,
      label = TRUE,
      abbr = FALSE
    )
  )

# Compare hourly load profiles across days
ggplot(
  daily_hourly_load,
  aes(
    x = hour,
    y = avg_load,
    group = date,
    color = day_of_week
  )
) +
  geom_line(linewidth = 1) +
  scale_x_continuous(
    breaks = 0:23,
    labels = sprintf("%02d:00", 0:23)
  ) +
  scale_color_manual(
    values = c(
      "Sunday" = "#ED6B5A",
      "Monday" = "#E88504",
      "Tuesday" = "#DDBB25",
      "Wednesday" = "#6EAD47",
      "Thursday" = "#49A3A9",
      "Friday" = "#AE85CE",
      "Saturday" = "#E884CD"
    )
  ) +
  labs(
    title = "Hourly BPA Load Profiles by Day",
    x = "Hour of Day",
    y = "Average Load (MW)",
    color = "Day"
  ) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    plot.title = element_text(face = "bold")
  )

# Reshape data for time-series visualization
load_long <- load_data %>%
  pivot_longer(
    cols = c(load, ver, hydro, fossil_biomass, nuclear, interchange),
    names_to = "resource",
    values_to = "mw"
  ) %>%
  mutate(
    resource = recode(
      resource,
      load = "Load",
      ver = "VER",
      hydro = "Hydro",
      fossil_biomass = "Fossil/Biomass",
      nuclear = "Nuclear",
      interchange = "Net Interchange"
    )
  )

# Plot five-minute BPA load and generation data
ggplot(
  load_long,
  aes(
    x = date_time,
    y = mw,
    color = resource,
    group = resource
  )
) +
  geom_line(linewidth = 0.4, na.rm = TRUE) +

  scale_color_manual(
    values = c(
      "Load" = "#E41A1C",
      "VER" = "#228B22",
      "Hydro" = "#2878B5",
      "Fossil/Biomass" = "#8B6F47",
      "Nuclear" = "#7B3294",
      "Net Interchange" = "#777777"
    )
  ) +

  scale_x_datetime(
    date_breaks = "1 day",
    date_labels = "%b %d",
    timezone = "America/Los_Angeles",
    limits = range(load_data$date_time)
  ) +

  labs(
    title = "BPA Balancing Authority Load and Generation",
    subtitle = "Five-minute observations",
    x = "Date/Time",
    y = "MW",
    color = NULL
  ) +

  theme_minimal() +

  theme(
    plot.title = element_text(face = "bold"),
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "top",
    panel.grid.minor = element_blank()
  )

# Define plot boundaries
plot_start <- min(load_data$date_time, na.rm = TRUE)
plot_end <- max(load_data$date_time, na.rm = TRUE)

# Set vertical position for weekend labels
label_y <- max(load_data$load, na.rm = TRUE) * 1.08

# Identify weekend days
weekend_days <- tibble(
  date = seq(
    as.Date(plot_start, tz = "America/Los_Angeles"),
    as.Date(plot_end, tz = "America/Los_Angeles"),
    by = "day"
  )
) %>%
  filter(lubridate::wday(date) %in% c(1, 7)) %>%
  mutate(
    # Define midnight boundaries in Pacific time
    xmin = as.POSIXct(
      paste(date, "00:00:00"),
      tz = "America/Los_Angeles"
    ),
    xmax = as.POSIXct(
      paste(date + 1, "00:00:00"),
      tz = "America/Los_Angeles"
    ),

    # Restrict shading to the visible plotting window
    visible_start = pmax(xmin, plot_start),
    visible_end = pmin(xmax, plot_end),

    # Center labels within the visible shaded region
    label_x = visible_start +
      (visible_end - visible_start) / 2,

    label = weekdays(date)
  )

# Check weekend dates and shading boundaries
weekend_days %>%
  select(date, xmin, xmax, visible_start, visible_end, label_x, label)
# Plot five-minute BPA load and generation data
ggplot() +

  # Weekend background shading
  geom_rect(
    data = weekend_days,
    aes(
      xmin = visible_start,
      xmax = visible_end,
      ymin = -Inf,
      ymax = Inf
    ),
    fill = "#D9D9D9",
    alpha = 0.45,
    inherit.aes = FALSE
  ) +

  # Five-minute load and generation observations
  geom_line(
    data = load_long,
    aes(
      x = date_time,
      y = mw,
      color = resource,
      group = resource
    ),
    linewidth = 0.4,
    na.rm = TRUE
  ) +

  # Weekend labels
  geom_text(
    data = weekend_days,
    aes(
      x = label_x,
      y = label_y,
      label = label
    ),
    inherit.aes = FALSE,
    fontface = "bold",
    size = 4
  ) +

  # Resource colors
  scale_color_manual(
    breaks = c(
      "Load",
      "VER",
      "Hydro",
      "Fossil/Biomass",
      "Nuclear",
      "Net Interchange"
    ),
    values = c(
      "Load" = "#E41A1C",
      "VER" = "#228B22",
      "Hydro" = "#2878B5",
      "Fossil/Biomass" = "#8B6F47",
      "Nuclear" = "#7B3294",
      "Net Interchange" = "#777777"
    )
  ) +

  # Date axis
  scale_x_datetime(
    date_breaks = "1 day",
    date_labels = "%a\n%b %d",
    timezone = "America/Los_Angeles",
    limits = c(plot_start, plot_end),
    expand = expansion(mult = 0)
  ) +

  # Y-axis
  scale_y_continuous(
    expand = expansion(mult = c(0.02, 0.12))
  ) +

  labs(
    title = "BPA Balancing Authority Load and Generation",
    subtitle = "Five-minute observations | November 7–14, 2026",
    x = "Date/Time (Pacific)",
    y = "Power (MW)",
    color = NULL,
    caption = "Source: BPA Balancing Authority Load & Total VER. Five-minute SCADA observations."
  ) +

  theme_minimal() +

  theme(
    plot.title = element_text(face = "bold", size = 16),
    plot.subtitle = element_text(size = 11),
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "top",
    legend.box = "horizontal",
    panel.grid.minor = element_blank(),
    panel.grid.major.x = element_line(
      color = "grey80",
      linewidth = 0.5
    ),
    plot.caption = element_text(
      color = "grey40",
      size = 8
    )
  )

Forecasting

In a typical regression we may estimate \[Load_t = \beta_0 + Temperature_t + \epsilon_t\] to investigate the relationship between load and temperature.

In forecasting, the objective would be to predict a future observation as a function of past observations: \[\widehat{Load_{t+1}} = f(Load_t, Load_{t-1}, Load_{t-24}, ...)\]

In order to practice, I will be using 2025 data as my training data for forecasting, and the 2026 data as testing data. Data is sourced from bullet 5 of https://transmission.bpa.gov/Business/Operations/Wind/.

# Import 2025 BPA data
data_25 <- read_excel(
  "~/Downloads/WindGenTotalLoadYTD_2025.xlsx",
  skip = 1
) %>%
  clean_names()

# Import 2026 BPA data
data_26 <- read_excel(
  "~/Downloads/WindGenTotalLoadYTD_2026.xlsx",
  skip = 1
) %>%
  clean_names()

# Compare missing values by column
missing_comparison <- tibble(
  variable = names(data_25),
  missing_2025 = colSums(is.na(data_25)),
  missing_2026 = colSums(is.na(data_26))
)

# Display missing-value comparison
missing_comparison %>%
  knitr::kable(
    caption = "Missing Values: 2025 vs. 2026",
    col.names = c("Variable", "2025 Missing", "2026 Missing")
  )
Missing Values: 2025 vs. 2026
Variable 2025 Missing 2026 Missing
date_time 0 0
date_time_utc 0 0
total_wind_base_schedule_forecast_in_bpa_control_area_mw_scada_187517 2 0
total_wind_basepoint_forecast_in_bpa_control_area_mw_scada_103349 2 0
total_wind_generation_in_bpa_control_area_mw_scada_79687 2 27
total_bpa_control_area_load_mw_scada_45583 2 0
total_hydro_generation_mw_scada_79682 2 27
total_fossil_biomass_generation_mw_scada_164377 2 27
total_nuclear_generation_mw_70681 2 0
net_interchange_mw_scada_45581 2 0
total_solar_base_schedule_forecast_in_bpa_control_area_mw_scada_187513 13438 9135
total_solar_basepoint_forecast_in_bpa_control_area_mw_scada_177165 2 0
total_solar_generation_in_bpa_control_area_mw_scada_142383 2 27
# Compare total observations
nrow(data_25)
#> [1] 105120
nrow(data_26)
#> [1] 91008
Combining and inspecting the raw data
# Combine 2025 and 2026 datasets
bpa_raw <- bind_rows(
  data_25 %>% mutate(source_year = 2025),
  data_26 %>% mutate(source_year = 2026)
) %>%
  arrange(date_time)


# Count duplicated timestamps
sum(duplicated(bpa_raw$date_time))
#> [1] 24
# Display any duplicated timestamps
bpa_raw %>%
  filter(duplicated(date_time) |
         duplicated(date_time, fromLast = TRUE))
# Parse local and UTC timestamps
bpa_raw <- bpa_raw %>%
  mutate(
    date_time = mdy_hm(
      date_time,
      tz = "America/Los_Angeles"
    ),
    date_time_utc = mdy_hm(
      date_time_utc,
      tz = "UTC"
    )
  )

bpa_raw %>%
  filter(
    as.Date(date_time_utc, tz = "UTC") == as.Date("2026-11-01")
  ) %>%
  select(date_time, date_time_utc) %>%
  print(n = 30, width = Inf)
#> # A tibble: 288 × 2
#>    date_time           date_time_utc      
#>    <dttm>              <dttm>             
#>  1 2026-10-31 17:00:00 2026-11-01 00:00:00
#>  2 2026-10-31 17:05:00 2026-11-01 00:05:00
#>  3 2026-10-31 17:10:00 2026-11-01 00:10:00
#>  4 2026-10-31 17:15:00 2026-11-01 00:15:00
#>  5 2026-10-31 17:20:00 2026-11-01 00:20:00
#>  6 2026-10-31 17:25:00 2026-11-01 00:25:00
#>  7 2026-10-31 17:30:00 2026-11-01 00:30:00
#>  8 2026-10-31 17:35:00 2026-11-01 00:35:00
#>  9 2026-10-31 17:40:00 2026-11-01 00:40:00
#> 10 2026-10-31 17:45:00 2026-11-01 00:45:00
#> 11 2026-10-31 17:50:00 2026-11-01 00:50:00
#> 12 2026-10-31 17:55:00 2026-11-01 00:55:00
#> 13 2026-10-31 18:00:00 2026-11-01 01:00:00
#> 14 2026-10-31 18:05:00 2026-11-01 01:05:00
#> 15 2026-10-31 18:10:00 2026-11-01 01:10:00
#> 16 2026-10-31 18:15:00 2026-11-01 01:15:00
#> 17 2026-10-31 18:20:00 2026-11-01 01:20:00
#> 18 2026-10-31 18:25:00 2026-11-01 01:25:00
#> 19 2026-10-31 18:30:00 2026-11-01 01:30:00
#> 20 2026-10-31 18:35:00 2026-11-01 01:35:00
#> 21 2026-10-31 18:40:00 2026-11-01 01:40:00
#> 22 2026-10-31 18:45:00 2026-11-01 01:45:00
#> 23 2026-10-31 18:50:00 2026-11-01 01:50:00
#> 24 2026-10-31 18:55:00 2026-11-01 01:55:00
#> 25 2026-10-31 19:00:00 2026-11-01 02:00:00
#> 26 2026-10-31 19:05:00 2026-11-01 02:05:00
#> 27 2026-10-31 19:10:00 2026-11-01 02:10:00
#> 28 2026-10-31 19:15:00 2026-11-01 02:15:00
#> 29 2026-10-31 19:20:00 2026-11-01 02:20:00
#> 30 2026-10-31 19:25:00 2026-11-01 02:25:00
#> # ℹ 258 more rows
# Calculate the difference between consecutive UTC timestamps
bpa_raw <- bpa_raw %>%
  arrange(date_time_utc) %>%
  mutate(
    interval_minutes = as.numeric(
      difftime(
        date_time_utc,
        lag(date_time_utc),
        units = "mins"
      )
    )
  )

# Count observations by interval length
bpa_raw %>%
  count(interval_minutes, sort = TRUE)
# Count missing values in each variable
missing_summary <- bpa_raw %>%
  summarise(
    across(
      everything(),
      ~ sum(is.na(.x))
    )
  ) %>%
  pivot_longer(
    cols = everything(),
    names_to = "variable",
    values_to = "missing_values"
  ) %>%
  arrange(desc(missing_values))

knitr::kable(
  missing_summary,
  caption = "Missing Values by Variable"
)
Missing Values by Variable
variable missing_values
total_solar_base_schedule_forecast_in_bpa_control_area_mw_scada_187513 22573
total_wind_generation_in_bpa_control_area_mw_scada_79687 29
total_hydro_generation_mw_scada_79682 29
total_fossil_biomass_generation_mw_scada_164377 29
total_solar_generation_in_bpa_control_area_mw_scada_142383 29
total_wind_base_schedule_forecast_in_bpa_control_area_mw_scada_187517 2
total_wind_basepoint_forecast_in_bpa_control_area_mw_scada_103349 2
total_bpa_control_area_load_mw_scada_45583 2
total_nuclear_generation_mw_70681 2
net_interchange_mw_scada_45581 2
total_solar_basepoint_forecast_in_bpa_control_area_mw_scada_177165 2
interval_minutes 1
date_time 0
date_time_utc 0
source_year 0
# Identify observations with missing measurements
missing_patterns <- bpa_raw %>%
  mutate(
    missing_count = rowSums(
      is.na(
        across(
          c(
            total_wind_generation_in_bpa_control_area_mw_scada_79687,
            total_bpa_control_area_load_mw_scada_45583,
            total_hydro_generation_mw_scada_79682,
            total_fossil_biomass_generation_mw_scada_164377,
            total_nuclear_generation_mw_70681,
            net_interchange_mw_scada_45581,
            total_solar_generation_in_bpa_control_area_mw_scada_142383,
            total_solar_base_schedule_forecast_in_bpa_control_area_mw_scada_187513
          )
        )
      )
    )
  ) %>%
  filter(missing_count > 0) %>%
  select(date_time, source_year, missing_count)

# Summarize how many observations have each pattern
missing_patterns %>%
  count(source_year, missing_count)
# Missing solar base schedule forecast by year
bpa_raw %>%
  group_by(source_year) %>%
  summarise(
    total_observations = n(),
    missing_values = sum(is.na(
      total_solar_base_schedule_forecast_in_bpa_control_area_mw_scada_187513
    )),
    percent_missing = mean(is.na(
      total_solar_base_schedule_forecast_in_bpa_control_area_mw_scada_187513
    )) * 100,
    .groups = "drop"
  )
# Missing solar forecast by calendar date
bpa_raw %>%
  mutate(date = as.Date(date_time)) %>%
  group_by(source_year, date) %>%
  summarise(
    missing_values = sum(is.na(
      total_solar_base_schedule_forecast_in_bpa_control_area_mw_scada_187513
    )),
    .groups = "drop"
  ) %>%
  filter(missing_values > 0) %>%
  arrange(desc(missing_values)) %>%
  head(20)
# Identify which variables are missing at each affected timestamp
bpa_raw %>%
  filter(
    if_any(
      c(
        total_wind_generation_in_bpa_control_area_mw_scada_79687,
        total_bpa_control_area_load_mw_scada_45583,
        total_hydro_generation_mw_scada_79682,
        total_fossil_biomass_generation_mw_scada_164377,
        total_nuclear_generation_mw_70681,
        net_interchange_mw_scada_45581,
        total_solar_generation_in_bpa_control_area_mw_scada_142383,
        total_solar_base_schedule_forecast_in_bpa_control_area_mw_scada_187513
      ),
      is.na
    )
  ) %>%
  mutate(
    missing_variables = pmap_chr(
      across(
        c(
          total_wind_generation_in_bpa_control_area_mw_scada_79687,
          total_bpa_control_area_load_mw_scada_45583,
          total_hydro_generation_mw_scada_79682,
          total_fossil_biomass_generation_mw_scada_164377,
          total_nuclear_generation_mw_70681,
          net_interchange_mw_scada_45581,
          total_solar_generation_in_bpa_control_area_mw_scada_142383,
          total_solar_base_schedule_forecast_in_bpa_control_area_mw_scada_187513
        )
      ),
      \(...) {
        values <- list(...)
        names(values)[map_lgl(values, is.na)] %>%
          paste(collapse = ", ")
      }
    )
  ) %>%
  count(source_year, missing_variables, sort = TRUE) %>%
  print(n = Inf, width = Inf)
#> # A tibble: 4 × 3
#>   source_year
#>         <dbl>
#> 1        2025
#> 2        2026
#> 3        2026
#> 4        2025
#>   missing_variables                                                             
#>   <chr>                                                                         
#> 1 total_solar_base_schedule_forecast_in_bpa_control_area_mw_scada_187513        
#> 2 total_solar_base_schedule_forecast_in_bpa_control_area_mw_scada_187513        
#> 3 total_wind_generation_in_bpa_control_area_mw_scada_79687, total_hydro_generat…
#> 4 total_wind_generation_in_bpa_control_area_mw_scada_79687, total_bpa_control_a…
#>       n
#>   <int>
#> 1 13436
#> 2  9135
#> 3    27
#> 4     2
# Examine solar forecast missingness by hour and month
solar_missing_hourly <- bpa_raw %>%
  mutate(
    year = year(date_time),
    month = month(date_time, label = TRUE, abbr = FALSE),
    hour = hour(date_time)
  ) %>%
  group_by(year, month, hour) %>%
  summarise(
    observations = n(),
    missing = sum(is.na(
      total_solar_base_schedule_forecast_in_bpa_control_area_mw_scada_187513
    )),
    percent_missing = mean(is.na(
      total_solar_base_schedule_forecast_in_bpa_control_area_mw_scada_187513
    )) * 100,
    .groups = "drop"
  )

# Plot solar forecast missingness by hour
ggplot(
  solar_missing_hourly,
  aes(x = hour, y = month, fill = percent_missing)
) +
  geom_tile(color = "white", linewidth = 0.2) +
  facet_wrap(~ year) +
  scale_fill_gradient(
    low = "#FFF7EC",
    high = "#B30000",
    name = "% Missing"
  ) +
  scale_x_continuous(breaks = seq(0, 23, by = 2)) +
  labs(
    title = "Solar Base Schedule Forecast Missingness",
    subtitle = "Percentage of missing observations by hour and month",
    x = "Hour of Day",
    y = "Month"
  ) +
  theme_minimal() +
  theme(
    panel.grid = element_blank(),
    axis.text.x = element_text(angle = 0)
  )

We need to convert the 5 minute into hourly load first.

# Create an hourly load dataset
hourly_load <- bpa_raw %>%
  mutate(
    hour_utc = floor_date(date_time_utc, unit = "hour")
  ) %>%
  group_by(hour_utc) %>%
  summarise(
    load_mw = mean(
      total_bpa_control_area_load_mw_scada_45583,
      na.rm = TRUE
    ),
    observations = sum(
      !is.na(total_bpa_control_area_load_mw_scada_45583)
    ),
    .groups = "drop"
  ) %>%
  # Keep hours with at least 11 of 12 five-minute load observations
  filter(observations >= 11) %>%
  mutate(
    date_time = with_tz(hour_utc, tzone = "America/Los_Angeles"),
    date = as.Date(date_time),
    year = year(date_time),
    month = factor(month(date_time, label = TRUE, abbr = FALSE)),
    day_of_week = factor(
      wday(date_time, label = TRUE, abbr = FALSE),
      levels = c(
        "Monday", "Tuesday", "Wednesday", "Thursday",
        "Friday", "Saturday", "Sunday"
      )
    ),
    hour = factor(hour(date_time))
  )

# Inspect the hourly dataset
glimpse(hourly_load)
#> Rows: 16,344
#> Columns: 9
#> $ hour_utc     <dttm> 2025-01-01 08:00:00, 2025-01-01 09:00:00, 2025-01-01 10:…
#> $ load_mw      <dbl> 6965.417, 6888.667, 6820.667, 6818.500, 6908.250, 7133.58…
#> $ observations <int> 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 1…
#> $ date_time    <dttm> 2025-01-01 00:00:00, 2025-01-01 01:00:00, 2025-01-01 02:…
#> $ date         <date> 2025-01-01, 2025-01-01, 2025-01-01, 2025-01-01, 2025-01-…
#> $ year         <dbl> 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 202…
#> $ month        <ord> January, January, January, January, January, January, Jan…
#> $ day_of_week  <ord> Wednesday, Wednesday, Wednesday, Wednesday, Wednesday, We…
#> $ hour         <fct> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,…
# Check the number of hourly observations by year
hourly_load %>%
  count(year)
# Examine the first few observations
head(hourly_load, 10)

Model 1; Hourly BPA Load Forecasting

Our first model will use three kinds of information to predict hourly BPA load: 1. Yesterday’s load: What was demand at the same hour 24 hours ago? 2. Last week’s load: What was demand at the same hour seven days ago? 3. Calendar patterns: What hour, day of the week, and month is it? These are useful predictors because electricity demand has strong daily, weekly, and seasonal patterns.

# Create hourly load observations
hourly_data <- bpa_raw %>%
  mutate(
    hour_utc = floor_date(date_time_utc, unit = "hour")
  ) %>%
  group_by(hour_utc) %>%
  summarise(
    load_mw = mean(
      total_bpa_control_area_load_mw_scada_45583,
      na.rm = TRUE
    ),
    observations = sum(
      !is.na(total_bpa_control_area_load_mw_scada_45583)
    ),
    .groups = "drop"
  ) %>%
  mutate(
    date_time_local = with_tz(
      hour_utc,
      tzone = "America/Los_Angeles"
    ),
    hour = hour(date_time_local),
    weekday = wday(date_time_local, label = TRUE),
    month = month(date_time_local, label = TRUE)
  ) %>%
  arrange(hour_utc)

# Create lagged load variables
hourly_data <- hourly_data %>%
  mutate(
    load_lag_24 = lag(load_mw, 24),
    load_lag_168 = lag(load_mw, 168)
  )

# Define the testing period
test_start <- max(hourly_data$hour_utc) - days(30)

# Separate training and testing observations
training_data <- hourly_data %>%
  filter(
    hour_utc < test_start,
    !is.na(load_lag_24),
    !is.na(load_lag_168)
  )

testing_data <- hourly_data %>%
  filter(
    hour_utc >= test_start,
    !is.na(load_lag_24),
    !is.na(load_lag_168)
  )

# Check sample sizes
nrow(training_data)
#> [1] 15455
nrow(testing_data)
#> [1] 721
# Train hourly load forecasting model
load_model_1 <- lm(
  load_mw ~
    load_lag_24 +
    load_lag_168 +
    factor(hour) +
    factor(weekday) +
    factor(month),
  data = training_data
)

# Examine model results
summary(load_model_1)
#> 
#> Call:
#> lm(formula = load_mw ~ load_lag_24 + load_lag_168 + factor(hour) + 
#>     factor(weekday) + factor(month), data = training_data)
#> 
#> Residuals:
#>      Min       1Q   Median       3Q      Max 
#> -1093.12  -165.53    -4.74   156.59  1470.69 
#> 
#> Coefficients:
#>                     Estimate Std. Error t value Pr(>|t|)    
#> (Intercept)        550.38941   32.33611  17.021  < 2e-16 ***
#> load_lag_24          0.81830    0.00469 174.490  < 2e-16 ***
#> load_lag_168         0.09443    0.00471  20.047  < 2e-16 ***
#> factor(hour)1      -11.23011   15.60992  -0.719 0.471892    
#> factor(hour)2      -15.80771   15.64396  -1.010 0.312288    
#> factor(hour)3      -16.01995   15.62963  -1.025 0.305392    
#> factor(hour)4       -4.44846   15.60536  -0.285 0.775602    
#> factor(hour)5       23.22122   15.65102   1.484 0.137912    
#> factor(hour)6       59.86187   15.93314   3.757 0.000173 ***
#> factor(hour)7       86.19769   16.29768   5.289 1.25e-07 ***
#> factor(hour)8       94.91714   16.46166   5.766 8.28e-09 ***
#> factor(hour)9       94.17757   16.46834   5.719 1.09e-08 ***
#> factor(hour)10      88.90377   16.38459   5.426 5.85e-08 ***
#> factor(hour)11      84.78254   16.31688   5.196 2.06e-07 ***
#> factor(hour)12      81.60688   16.26710   5.017 5.32e-07 ***
#> factor(hour)13      79.43408   16.23479   4.893 1.00e-06 ***
#> factor(hour)14      79.23895   16.22874   4.883 1.06e-06 ***
#> factor(hour)15      83.20548   16.29070   5.108 3.30e-07 ***
#> factor(hour)16      95.20178   16.49854   5.770 8.06e-09 ***
#> factor(hour)17     111.00478   16.79405   6.610 3.98e-11 ***
#> factor(hour)18     116.27646   16.87912   6.889 5.85e-12 ***
#> factor(hour)19     111.62842   16.79176   6.648 3.07e-11 ***
#> factor(hour)20     100.66294   16.58239   6.070 1.31e-09 ***
#> factor(hour)21      79.73851   16.22745   4.914 9.02e-07 ***
#> factor(hour)22      49.00788   15.84503   3.093 0.001985 ** 
#> factor(hour)23      19.71193   15.64367   1.260 0.207668    
#> factor(weekday).L -288.83999    6.15153 -46.954  < 2e-16 ***
#> factor(weekday).Q -174.26966    6.27317 -27.780  < 2e-16 ***
#> factor(weekday).C  115.91315    5.98498  19.367  < 2e-16 ***
#> factor(weekday)^4 -180.77676    6.02393 -30.010  < 2e-16 ***
#> factor(weekday)^5   78.43297    5.97850  13.119  < 2e-16 ***
#> factor(weekday)^6    4.07925    5.95945   0.685 0.493669    
#> factor(month).L    -12.94205    9.86977  -1.311 0.189782    
#> factor(month).Q    144.51632   10.75391  13.438  < 2e-16 ***
#> factor(month).C     -9.36014    8.24737  -1.135 0.256425    
#> factor(month)^4     65.06748    8.70285   7.477 8.04e-14 ***
#> factor(month)^5      4.62057    8.97494   0.515 0.606679    
#> factor(month)^6    -67.28172    8.67032  -7.760 9.02e-15 ***
#> factor(month)^7    -12.13790    8.15232  -1.489 0.136537    
#> factor(month)^8     42.02975    7.85768   5.349 8.98e-08 ***
#> factor(month)^9     21.65816    7.61716   2.843 0.004470 ** 
#> factor(month)^10    25.56769    7.38470   3.462 0.000537 ***
#> factor(month)^11     2.38185    7.32396   0.325 0.745024    
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 280 on 15412 degrees of freedom
#> Multiple R-squared:  0.9128, Adjusted R-squared:  0.9126 
#> F-statistic:  3842 on 42 and 15412 DF,  p-value: < 2.2e-16

We are trying to forecast load_mw, or the hourly BPA balancing area electricity load, as measured in megawatts. We are using the information of load at the same hour of the previous day, same hour of the previous week, the hour of the day, the day of the week, and the month of the year. In other words, we are asking “given what electricity demand looked like recently, and knowing the time of day, day of the week, and time of year, how accurately can we predict electricity demand?

Before training the model we transformed the original 5-minute increments into hourly observations. This way we can work with an hour-interval lag as opposed to 5-minute lags.

We trained the model on all of the 2025 points, minus the final 30 days of observations. The test period is used for rolling one hour ahead predictions. At each hour the model can use actual load from the previous day and week. It is not forecasting all 30 days at once from a single starting point.

The model used is as follows: load_model_1 <- lm(load_mw ~ load_lag_24 + load_lag_168 + factor(hour) + factor(weekday) + factor(month), data = training_data)

Component What it Captures
load_mw The outcome we are predicting
load_lag_24 Previous day demand
load_lag_168 Previous week demand
factor(hour) Differences between hours of the day
factor(weekday) Differences between days of the week
factor(month) Differences between months

Conceptually, this translates to \[Load_t = \beta_0 + \beta_1Load_{t-24} + \beta_2Load_{t-168} + Hour Effects + Weekday Effects + Month Effects + \epsilon_t\]

Interpreting Results

The previous-day load coefficient is 0.8183, and the previous-week load’s is 0.0944, both statistically significant. Interpreted, the previous-day coefficient means that, holding all other predictors constant, a 1 MW increase in yesterday’s load at the same hour is associated with an estimated 0.818 MW increase in predicted load. The model is relying heavily on the prior day’s load. The prior week contributes additional information, but the estimated coefficient is much smaller.

The hour-of-day effects use the hour 0 (midnight) as the reference category. The strongest hourly effect in this output is at 6pm, approximately 116 MW above the midnight reference. This is consistent with the idea that electricity demand has a daily pattern. However, these are adjustments relative to the reference hour, not the average load difference between those hours.

However, the model is treating the labelled weekday and monthly variables as ordered factors. Instead of estimating separate coefficient foe each weekday or month relative to a reference category, it is estimating polynomial contrasts. We can re-run this model using ordinary categorical contrasts by converting the factors into character first.

load_model_2 <- lm(
  load_mw ~
    load_lag_24 +
    load_lag_168 +
    factor(hour) +
    factor(as.character(weekday)) +
    factor(as.character(month)),
  data = training_data
)

summary(load_model_2)
#> 
#> Call:
#> lm(formula = load_mw ~ load_lag_24 + load_lag_168 + factor(hour) + 
#>     factor(as.character(weekday)) + factor(as.character(month)), 
#>     data = training_data)
#> 
#> Residuals:
#>      Min       1Q   Median       3Q      Max 
#> -1093.12  -165.53    -4.74   156.59  1470.69 
#> 
#> Coefficients:
#>                                    Estimate Std. Error t value Pr(>|t|)    
#> (Intercept)                       393.47123   32.23982  12.205  < 2e-16 ***
#> load_lag_24                         0.81830    0.00469 174.490  < 2e-16 ***
#> load_lag_168                        0.09443    0.00471  20.047  < 2e-16 ***
#> factor(hour)1                     -11.23011   15.60992  -0.719 0.471892    
#> factor(hour)2                     -15.80771   15.64396  -1.010 0.312288    
#> factor(hour)3                     -16.01995   15.62963  -1.025 0.305392    
#> factor(hour)4                      -4.44846   15.60536  -0.285 0.775602    
#> factor(hour)5                      23.22122   15.65102   1.484 0.137912    
#> factor(hour)6                      59.86187   15.93314   3.757 0.000173 ***
#> factor(hour)7                      86.19769   16.29768   5.289 1.25e-07 ***
#> factor(hour)8                      94.91714   16.46166   5.766 8.28e-09 ***
#> factor(hour)9                      94.17757   16.46834   5.719 1.09e-08 ***
#> factor(hour)10                     88.90377   16.38459   5.426 5.85e-08 ***
#> factor(hour)11                     84.78254   16.31688   5.196 2.06e-07 ***
#> factor(hour)12                     81.60688   16.26710   5.017 5.32e-07 ***
#> factor(hour)13                     79.43408   16.23479   4.893 1.00e-06 ***
#> factor(hour)14                     79.23895   16.22874   4.883 1.06e-06 ***
#> factor(hour)15                     83.20548   16.29070   5.108 3.30e-07 ***
#> factor(hour)16                     95.20178   16.49854   5.770 8.06e-09 ***
#> factor(hour)17                    111.00478   16.79405   6.610 3.98e-11 ***
#> factor(hour)18                    116.27646   16.87912   6.889 5.85e-12 ***
#> factor(hour)19                    111.62842   16.79176   6.648 3.07e-11 ***
#> factor(hour)20                    100.66294   16.58239   6.070 1.31e-09 ***
#> factor(hour)21                     79.73851   16.22745   4.914 9.02e-07 ***
#> factor(hour)22                     49.00788   15.84503   3.093 0.001985 ** 
#> factor(hour)23                     19.71193   15.64367   1.260 0.207668    
#> factor(as.character(weekday))Mon  381.44711    8.65473  44.074  < 2e-16 ***
#> factor(as.character(weekday))Sat -156.96082    8.50648 -18.452  < 2e-16 ***
#> factor(as.character(weekday))Sun   58.79477    8.58580   6.848 7.78e-12 ***
#> factor(as.character(weekday))Thu   74.92730    8.44044   8.877  < 2e-16 ***
#> factor(as.character(weekday))Tue  193.16386    8.48212  22.773  < 2e-16 ***
#> factor(as.character(weekday))Wed   75.52602    8.45156   8.936  < 2e-16 ***
#> factor(as.character(month))Aug     45.13859   10.47619   4.309 1.65e-05 ***
#> factor(as.character(month))Dec    145.00957   13.27526  10.923  < 2e-16 ***
#> factor(as.character(month))Feb    105.31472   12.52439   8.409  < 2e-16 ***
#> factor(as.character(month))Jan    164.55224   12.86631  12.789  < 2e-16 ***
#> factor(as.character(month))Jul     77.02550   10.49074   7.342 2.21e-13 ***
#> factor(as.character(month))Jun     57.61167   10.45875   5.508 3.68e-08 ***
#> factor(as.character(month))Mar     57.08695   10.61246   5.379 7.59e-08 ***
#> factor(as.character(month))May     23.27254   10.40324   2.237 0.025297 *  
#> factor(as.character(month))Nov     95.74971   12.83087   7.462 8.95e-14 ***
#> factor(as.character(month))Oct     59.12605   11.45453   5.162 2.48e-07 ***
#> factor(as.character(month))Sep    -21.55200   10.50638  -2.051 0.040252 *  
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 280 on 15412 degrees of freedom
#> Multiple R-squared:  0.9128, Adjusted R-squared:  0.9126 
#> F-statistic:  3842 on 42 and 15412 DF,  p-value: < 2.2e-16

With this fixed, we see adjusted differences after accounting for the lagged loads and other calendar variables. It is using Friday as a weekday reference, April as a monthly reference, and midnight (hour 0) as an hourly reference.

Again, the previous day load remains our strongest predictor. its coefficient is about 0.82, compared with the 0.09 for previous-week load. Both are highly statistically significant. Weekday differences are also substantial in this model. Monday’s estimated effect is approximately 381 MW above Friday, while Saturday is approximately 157 MW below Friday. This suggests meaningful differenced in demand patterns across the week.

We also see seasonal differences. January and December have positiove coefficients relative to April. This is consistent with seasonal variation in electricity demand, although we would need to investigate temperature and weather before attributing those differences to any particular cause.

The R-squared remained the same across models, alongside the residual standard error and the F statistic. Our model seems to fit the training data well, but we must test it to be sure. ### Testing the Model

We test for: - MAE (mean absolute error): measures the average absolute difference between actual and predicted values. - RMSE (root mean squared error): similar to MAE, but instead of taking the absolute value of each error, it squares the error, averages those squared errors, then takes the squared root. This essentially weights the larger errors more. - MAPE (mean absolute percentage error): expresses forecasting errors as percentages rather than MW.

# Generate predictions for the testing period
testing_data <- testing_data %>%
  mutate(
    predicted_load = predict(
      load_model_2,
      newdata = testing_data
    ),

    # Simple forecasting benchmarks
    naive_24 = load_lag_24,
    naive_168 = load_lag_168
  )

# Compare forecasting accuracy
model_accuracy <- tibble(
  model = c(
    "Regression model",
    "Previous-day baseline",
    "Previous-week baseline"
  ),

  MAE = c(
    mean(abs(testing_data$load_mw - testing_data$predicted_load)),
    mean(abs(testing_data$load_mw - testing_data$naive_24)),
    mean(abs(testing_data$load_mw - testing_data$naive_168))
  ),

  RMSE = c(
    sqrt(mean((testing_data$load_mw - testing_data$predicted_load)^2)),
    sqrt(mean((testing_data$load_mw - testing_data$naive_24)^2)),
    sqrt(mean((testing_data$load_mw - testing_data$naive_168)^2))
  ),

  MAPE = c(
    mean(abs((testing_data$load_mw - testing_data$predicted_load) /
      testing_data$load_mw)) * 100,

    mean(abs((testing_data$load_mw - testing_data$naive_24) /
      testing_data$load_mw)) * 100,

    mean(abs((testing_data$load_mw - testing_data$naive_168) /
      testing_data$load_mw)) * 100
  )
)

# Display results
knitr::kable(
  model_accuracy,
  digits = 2,
  caption = "Hourly Load Forecasting Accuracy"
)
Hourly Load Forecasting Accuracy
model MAE RMSE MAPE
Regression model 192.40 238.42 2.74
Previous-day baseline 214.58 283.84 3.06
Previous-week baseline 308.89 390.02 4.44

Interpreting this, we see that our regression model including all of the variables were closer than simply relying on the previous day or week baseline. But, there is still some significant error in our predicting. One way to investigate is to calculate the forecast error for every observation and summarize it by hour of the day.

# Calculate forecasting errors
testing_data <- testing_data %>%
  mutate(
    forecast_error = load_mw - predicted_load,
    absolute_error = abs(forecast_error)
  )

# Summarize forecasting accuracy by hour
hourly_accuracy <- testing_data %>%
  group_by(hour) %>%
  summarise(
    MAE = mean(absolute_error),
    RMSE = sqrt(mean(forecast_error^2)),
    mean_error = mean(forecast_error),
    observations = n(),
    .groups = "drop"
  )

# Display results
knitr::kable(
  hourly_accuracy,
  digits = 2,
  caption = "Forecasting Accuracy by Hour of Day"
)
Forecasting Accuracy by Hour of Day
hour MAE RMSE mean_error observations
0 166.89 209.07 -6.36 30
1 177.19 218.46 -11.77 31
2 187.33 230.14 -10.49 30
3 197.99 244.35 -11.73 30
4 208.70 255.38 -18.71 30
5 234.76 277.47 -19.03 30
6 298.53 354.03 -10.89 30
7 332.34 383.67 -2.40 30
8 282.49 326.33 7.24 30
9 234.47 269.87 14.94 30
10 192.00 227.72 15.43 30
11 167.77 211.50 11.94 30
12 164.40 207.56 8.28 30
13 159.73 206.47 1.59 30
14 161.80 207.15 -5.35 30
15 170.64 213.58 -13.67 30
16 183.42 220.71 -11.17 30
17 178.56 216.09 -3.17 30
18 179.55 215.43 -2.57 30
19 183.44 213.09 -8.87 30
20 160.29 188.11 -7.47 30
21 140.80 167.33 -3.92 30
22 129.02 148.38 0.39 30
23 125.97 151.24 3.26 30
# Select the final seven days of the testing period
plot_start <- max(testing_data$hour_utc) - days(7)

forecast_plot_data <- testing_data %>%
  filter(hour_utc >= plot_start) %>%
  select(
    hour_utc,
    load_mw,
    predicted_load
  ) %>%
  pivot_longer(
    cols = c(load_mw, predicted_load),
    names_to = "series",
    values_to = "load"
  ) %>%
  mutate(
    series = recode(
      series,
      load_mw = "Actual Load",
      predicted_load = "Predicted Load"
    )
  )

# Plot actual and predicted load
ggplot(
  forecast_plot_data,
  aes(x = hour_utc, y = load, color = series)
) +
  geom_line(linewidth = 0.7) +
  scale_color_manual(
    values = c(
      "Actual Load" = "#0072B2",
      "Predicted Load" = "#D55E00"
    )
  ) +
  labs(
    title = "Actual vs. Predicted BPA Load",
    subtitle = "Final seven days of the testing period",
    x = "Date",
    y = "Load (MW)",
    color = NULL
  ) +
  theme_minimal() +
  theme(
    legend.position = "bottom"
  )

LS0tCnRpdGxlOiAiQlBBIFIgUmV2aWV3IgphdXRob3I6ICJNaXJhIENyb3NzIgpkYXRlOiAiTm92ZW1iZXIgMjAyNiIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogZmxhdGx5CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHRydWUKICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgZGZfcHJpbnQ6IHBhZ2VkCmVkaXRvcl9vcHRpb25zOiAKICBtYXJrZG93bjogCiAgICB3cmFwOiA3MgotLS0KCmBgYHs9aHRtbH0KPHN0eWxlPgoKLyogLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgogICBHRU5FUkFMIERPQ1VNRU5UIFNUWUxJTkcKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gKi8KLS0tCmJvZHkgewogIGZvbnQtc2l6ZTogMTVweDsKICBsaW5lLWhlaWdodDogMS43OwogIGNvbG9yOiAjMzQ0NDU0Owp9CgoubWFpbi1jb250YWluZXIgewogIG1heC13aWR0aDogMTEwMHB4Owp9CgovKiBNYWluIHRpdGxlICovCgpoMS50aXRsZSB7CiAgY29sb3I6ICMyNDUzNmI7CiAgZm9udC13ZWlnaHQ6IDcwMDsKICBtYXJnaW4tYm90dG9tOiA4cHg7Cn0KCi5hdXRob3IsIC5kYXRlIHsKICBjb2xvcjogIzcxODA5NjsKfQoKLyogTWFqb3Igc2VjdGlvbiBoZWFkaW5ncyAqLwoKaDIgewogIGNvbG9yOiAjMjQ1MzZiOwogIGZvbnQtd2VpZ2h0OiA3MDA7CiAgYm9yZGVyLWJvdHRvbTogMnB4IHNvbGlkICNkY2U1ZWI7CiAgcGFkZGluZy1ib3R0b206IDEwcHg7CiAgbWFyZ2luLXRvcDogNDVweDsKICBtYXJnaW4tYm90dG9tOiAyMnB4Owp9CgovKiBTdWJoZWFkaW5ncyAqLwoKaDMsIGg0IHsKICBjb2xvcjogIzM4Njc3ZjsKICBmb250LXdlaWdodDogNjAwOwogIG1hcmdpbi10b3A6IDI1cHg7Cn0KCi8qIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiAgIENPTExBUFNJQkxFIFRPUElDUwotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAqLwotLS0KZGV0YWlscy50b3BpYyB7CiAgYm9yZGVyOiAxcHggc29saWQgI2RjZTVlYjsKICBib3JkZXItcmFkaXVzOiA3cHg7CiAgbWFyZ2luOiAxMnB4IDA7CiAgYmFja2dyb3VuZDogI2ZmZmZmZjsKICBvdmVyZmxvdzogaGlkZGVuOwp9CgpkZXRhaWxzLnRvcGljIHN1bW1hcnkgewogIHBhZGRpbmc6IDEzcHggMTdweDsKICBjdXJzb3I6IHBvaW50ZXI7CiAgZm9udC13ZWlnaHQ6IDYwMDsKICBjb2xvcjogIzI0NTM2YjsKICBiYWNrZ3JvdW5kOiAjZjNmN2Y5OwogIGxpc3Qtc3R5bGU6IG5vbmU7Cn0KCmRldGFpbHMudG9waWMgc3VtbWFyeTo6LXdlYmtpdC1kZXRhaWxzLW1hcmtlciB7CiAgZGlzcGxheTogbm9uZTsKfQoKZGV0YWlscy50b3BpYyBzdW1tYXJ5OjpiZWZvcmUgewogIGNvbnRlbnQ6ICLilrgiOwogIGRpc3BsYXk6IGlubGluZS1ibG9jazsKICBtYXJnaW4tcmlnaHQ6IDEwcHg7CiAgY29sb3I6ICM1NDg0OWI7CiAgdHJhbnNpdGlvbjogdHJhbnNmb3JtIDAuMnM7Cn0KCmRldGFpbHMudG9waWNbb3Blbl0gc3VtbWFyeTo6YmVmb3JlIHsKICB0cmFuc2Zvcm06IHJvdGF0ZSg5MGRlZyk7Cn0KCmRldGFpbHMudG9waWMgc3VtbWFyeTpob3ZlciB7CiAgYmFja2dyb3VuZDogI2VhZjFmNTsKfQoKZGV0YWlscy50b3BpY1tvcGVuXSBzdW1tYXJ5IHsKICBib3JkZXItYm90dG9tOiAxcHggc29saWQgI2RjZTVlYjsKfQoKLnRvcGljLWNvbnRlbnQgewogIHBhZGRpbmc6IDE1cHggMjBweDsKfQoKLyogTmVzdGVkIHRvcGljIHNlY3Rpb25zICovCgpkZXRhaWxzLnN1YnRvcGljIHsKICBtYXJnaW46IDEwcHggMDsKICBib3JkZXItbGVmdDogM3B4IHNvbGlkICM4YWFmYzE7CiAgcGFkZGluZy1sZWZ0OiAxMnB4Owp9CgpkZXRhaWxzLnN1YnRvcGljIHN1bW1hcnkgewogIGN1cnNvcjogcG9pbnRlcjsKICBmb250LXdlaWdodDogNjAwOwogIGNvbG9yOiAjMzg2NzdmOwp9CgovKiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQogICBDT0RFIEZPUk1BVFRJTkcKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gKi8KLS0tCnByZSB7CiAgYm9yZGVyOiAxcHggc29saWQgI2RjZTVlYjsKICBib3JkZXItcmFkaXVzOiA3cHg7CiAgcGFkZGluZzogMTNweDsKICBtYXJnaW46IDEycHggMDsKfQoKY29kZSB7CiAgY29sb3I6ICM4YjQ1NjA7Cn0KCnByZSBjb2RlIHsKICBjb2xvcjogaW5oZXJpdDsKfQoKLyogLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KICAgVEFCTEVTCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICovCi0tLQp0YWJsZSB7CiAgbWFyZ2luOiAxOHB4IDA7CiAgYm9yZGVyLWNvbGxhcHNlOiBjb2xsYXBzZTsKfQoKdGggewogIGJhY2tncm91bmQ6ICNlZGYzZjY7CiAgY29sb3I6ICMyNDUzNmI7Cn0KCnRkLCB0aCB7CiAgcGFkZGluZzogOXB4IDEycHg7Cn0KCi8qIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiAgIENBTExPVVRTCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICovCi0tLQoubm90ZSB7CiAgYmFja2dyb3VuZDogI2YwZjVmODsKICBib3JkZXItbGVmdDogNHB4IHNvbGlkICM1NDg0OWI7CiAgcGFkZGluZzogMTJweCAxNnB4OwogIG1hcmdpbjogMTZweCAwOwogIGJvcmRlci1yYWRpdXM6IDRweDsKfQoKLndhcm5pbmcgewogIGJhY2tncm91bmQ6ICNmYmY2ZWI7CiAgYm9yZGVyLWxlZnQ6IDRweCBzb2xpZCAjYzY5YjQ1OwogIHBhZGRpbmc6IDEycHggMTZweDsKICBtYXJnaW46IDE2cHggMDsKICBib3JkZXItcmFkaXVzOiA0cHg7Cn0KCjwvc3R5bGU+CmBgYAoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBlY2hvID0gVFJVRSwKICBtZXNzYWdlID0gRkFMU0UsCiAgd2FybmluZyA9IEZBTFNFLAogIGNvbW1lbnQgPSAiIz4iCikKYGBgCgojIDEuIFIgU2V0dXAgYW5kIFBhY2thZ2VzCgojIyBNYXJrZG93biBTZXR1cAoKVGhlIGVhc2llc3Qgd2F5IHRvIG1hbmFnZSBwYWNrYWdlcyBpcyB1c2luZyBgcGFjbWFuYCBhbmQgaXRzIGBwX2xvYWQoKWAKZnVuY3Rpb24uCgpJbnN0YWxsIGBwYWNtYW5gIG9uY2UgaW4gdGhlIFJTdHVkaW8gQ29uc29sZS4gVGhlbiB1c2UgYHBfbG9hZCgpYCB0bwppbnN0YWxsIG1pc3NpbmcgcGFja2FnZXMgYW5kIGxvYWQgdGhlbS4KCmBgYHtyIGV2YWw9RkFMU0V9Cmluc3RhbGwucGFja2FnZXMoInBhY21hbiIsIHJlcG9zID0gImh0dHBzOi8vY2xvdWQuci1wcm9qZWN0Lm9yZyIpCmBgYAoKIyMjIFBhY2thZ2UgbWFuYWdlbWVudCBmdW5jdGlvbnMKCmBgYHtyIGV2YWw9RkFMU0V9CnBhY21hbjo6cF9sb2FkKCkgICAgIyBJbnN0YWxscyBtaXNzaW5nIHBhY2thZ2VzIGFuZCBsb2FkcyB0aGVtCnBhY21hbjo6cF9pbnN0YWxsKCkgIyBJbnN0YWxscyBwYWNrYWdlcwpwYWNtYW46OnBfdW5sb2FkKCkgICMgVW5sb2FkcyBwYWNrYWdlcwpwYWNtYW46OnBfdXBkYXRlKCkgICMgVXBkYXRlcyBpbnN0YWxsZWQgcGFja2FnZXMKcGFjbWFuOjpwX3ZlcnNpb24oKSAjIENoZWNrcyBwYWNrYWdlIHZlcnNpb25zCmBgYAoKIyMjIExvYWQgcGFja2FnZXMKCmBgYHtyfQpwYWNtYW46OnBfbG9hZCgKICB0aWR5dmVyc2UsCiAgamFuaXRvciwKICByZWFkeGwsCiAgaGVyZSwKICBsdWJyaWRhdGUsCiAgc2tpbXIsCiAgYnJvb20sCiAgZml4ZXN0CikKYGBgCgojIyBQYWNrYWdlIFJlZmVyZW5jZQoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5PnRpZHl2ZXJzZSDigJQgRGF0YSBtYW5pcHVsYXRpb24gYW5kIHZpc3VhbGl6YXRpb248L3N1bW1hcnk+CgpBIGNvbGxlY3Rpb24gb2YgcGFja2FnZXMgZm9yIGRhdGEgbWFuaXB1bGF0aW9uLCBvcmdhbml6YXRpb24sCnZpc3VhbGl6YXRpb24sIGFuZCBhbmFseXNpcy4KCkNvbW1vbiBmdW5jdGlvbnM6CgpgYGB7ciBldmFsPUZBTFNFfQpzZWxlY3QoKSAgICAgICAjIFNlbGVjdCBjb2x1bW5zCmZpbHRlcigpICAgICAgICMgRmlsdGVyIHJvd3MKbXV0YXRlKCkgICAgICAgIyBDcmVhdGUgb3IgbW9kaWZ5IHZhcmlhYmxlcwpzdW1tYXJpc2UoKSAgICAjIENhbGN1bGF0ZSBzdW1tYXJ5IHN0YXRpc3RpY3MKZ3JvdXBfYnkoKSAgICAgIyBHcm91cCBvYnNlcnZhdGlvbnMKYXJyYW5nZSgpICAgICAgIyBTb3J0IG9ic2VydmF0aW9ucwpnZ3Bsb3QoKSAgICAgICAjIENyZWF0ZSB2aXN1YWxpemF0aW9ucwpgYGAKCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5qYW5pdG9yIOKAlCBEYXRhIGNsZWFuaW5nPC9zdW1tYXJ5PgoKVXNlZnVsIGZvciBjbGVhbmluZyBtZXNzeSBkYXRhc2V0cyBhbmQgY3JlYXRpbmcgZnJlcXVlbmN5IHRhYmxlcy4KCmBgYHtyIGV2YWw9RkFMU0V9CmNsZWFuX25hbWVzKCkgICAgICAgIyBTdGFuZGFyZGl6ZSBjb2x1bW4gbmFtZXMKdGFieWwoKSAgICAgICAgICAgICAjIENyZWF0ZSBmcmVxdWVuY3kgdGFibGVzCmdldF9kdXBlcyhkYXRhLCBJRCkgIyBJZGVudGlmeSBkdXBsaWNhdGUgb2JzZXJ2YXRpb25zCmBgYAoKPC9kZXRhaWxzPgoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5PnJlYWR4bCDigJQgSW1wb3J0IEV4Y2VsIGRhdGE8L3N1bW1hcnk+CgpVc2VkIHRvIGltcG9ydCBFeGNlbCBzcHJlYWRzaGVldHMgaW50byBSLgoKYGBge3IgZXZhbD1GQUxTRX0KcmVhZF9leGNlbCgiZGF0YS54bHN4IikgIyBJbXBvcnQgYW4gRXhjZWwgd29ya3NoZWV0CmV4Y2VsX3NoZWV0cygiZGF0YS54bHN4IikgIyBMaXN0IHdvcmtzaGVldCBuYW1lcwpgYGAKCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5oZXJlIOKAlCBQcm9qZWN0IGZpbGUgcGF0aHM8L3N1bW1hcnk+CgpDcmVhdGVzIHByb2plY3QtcmVsYXRpdmUgZmlsZSBwYXRocyByYXRoZXIgdGhhbiByZWx5aW5nIG9uIGFic29sdXRlCnBhdGhzLgoKYGBge3IgZXZhbD1GQUxTRX0KaGVyZSgiZGF0YSIsICJyYXciLCAiZGF0YS5jc3YiKQpgYGAKCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5sdWJyaWRhdGUg4oCUIERhdGVzIGFuZCB0aW1lczwvc3VtbWFyeT4KClNpbXBsaWZpZXMgd29ya2luZyB3aXRoIGRhdGVzLCB0aW1lcywgYW5kIHRpbWUgaW50ZXJ2YWxzLgoKYGBge3IgZXZhbD1GQUxTRX0KeWVhcihkYXRlKSAgICAgICAgICAgICAgIyBFeHRyYWN0IHllYXIKbW9udGgoZGF0ZSkgICAgICAgICAgICAgIyBFeHRyYWN0IG1vbnRoCmRheShkYXRlKSAgICAgICAgICAgICAgICMgRXh0cmFjdCBkYXkKeW1kKCIyMDI2LTExLTEyIikgICAgICAgIyBDb252ZXJ0IHllYXItbW9udGgtZGF5IHN0cmluZwpgYGAKCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5za2ltciDigJQgRGF0YSBzdW1tYXJpZXM8L3N1bW1hcnk+CgpQcm92aWRlcyBhIGRldGFpbGVkIG92ZXJ2aWV3IG9mIGEgZGF0YXNldCwgaW5jbHVkaW5nIGRlc2NyaXB0aXZlCnN0YXRpc3RpY3MgYW5kIG1pc3NpbmcgdmFsdWVzLgoKYGBge3IgZXZhbD1GQUxTRX0Kc2tpbShkYXRhKQpgYGAKCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5icm9vbSDigJQgT3JnYW5pemUgbW9kZWwgcmVzdWx0czwvc3VtbWFyeT4KCkNvbnZlcnRzIHN0YXRpc3RpY2FsIG1vZGVsIG91dHB1dHMgaW50byB0aWR5IGRhdGEgZnJhbWVzLgoKYGBge3IgZXZhbD1GQUxTRX0KdGlkeShtb2RlbCkgICAgIyBDb2VmZmljaWVudHMsIHN0YW5kYXJkIGVycm9ycywgcC12YWx1ZXMKZ2xhbmNlKG1vZGVsKSAgIyBNb2RlbC1sZXZlbCBzdGF0aXN0aWNzCmF1Z21lbnQobW9kZWwpICMgT2JzZXJ2YXRpb24tbGV2ZWwgcmVzdWx0cwpgYGAKCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5maXhlc3Qg4oCUIEVjb25vbWV0cmljcyBhbmQgcmVncmVzc2lvbjwvc3VtbWFyeT4KClVzZWZ1bCBmb3IgT0xTLCBmaXhlZC1lZmZlY3RzIHJlZ3Jlc3Npb24sIGluc3RydW1lbnRhbCB2YXJpYWJsZXMsIGFuZAptb2RlbHMgd2l0aCByb2J1c3Qgb3IgY2x1c3RlcmVkIHN0YW5kYXJkIGVycm9ycy4KCmBgYHtyIGV2YWw9RkFMU0V9CmZlb2xzKHkgfiB4MSArIHgyLCBkYXRhID0gZGF0YSkKCiMgRml4ZWQgZWZmZWN0cwpmZW9scyh5IH4geDEgKyB4MiB8IHllYXIgKyByZWdpb24sIGRhdGEgPSBkYXRhKQpgYGAKCjwvZGV0YWlscz4KCiMgMi4gSW1wb3J0aW5nIGFuZCBJbnNwZWN0aW5nIERhdGEKCiMjIEltcG9ydGluZyBhbmQgRXhwb3J0aW5nCgpgYGB7ciBldmFsPUZBTFNFfQpyZWFkX2NzdigiZGF0YS5jc3YiKSAgICAgICAjIEltcG9ydCBDU1YgZmlsZQpyZWFkX2V4Y2VsKCJkYXRhLnhsc3giKSAgICAjIEltcG9ydCBFeGNlbCBmaWxlCnJlYWRSRFMoImRhdGEucmRzIikgICAgICAgICMgSW1wb3J0IFIgZGF0YSBvYmplY3QKCndyaXRlX2NzdihkYXRhLCAiZGF0YS5jc3YiKSAjIEV4cG9ydCBDU1YKc2F2ZVJEUyhkYXRhLCAiZGF0YS5yZHMiKSAgICMgU2F2ZSBSIGRhdGEgb2JqZWN0CmBgYAoKIyMgSW5zcGVjdGluZyBEYXRhCgpgYGB7ciBldmFsPUZBTFNFfQpoZWFkKGRhdGEpICAgICAgICMgUHJldmlldyBmaXJzdCBvYnNlcnZhdGlvbnMKdGFpbChkYXRhKSAgICAgICAjIFByZXZpZXcgbGFzdCBvYnNlcnZhdGlvbnMKZ2xpbXBzZShkYXRhKSAgICAjIEluc3BlY3QgY29sdW1ucyBhbmQgZGF0YSB0eXBlcwpkaW0oZGF0YSkgICAgICAgICMgTnVtYmVyIG9mIHJvd3MgYW5kIGNvbHVtbnMKbnJvdyhkYXRhKSAgICAgICAjIE51bWJlciBvZiByb3dzCm5jb2woZGF0YSkgICAgICAgIyBOdW1iZXIgb2YgY29sdW1ucwpuYW1lcyhkYXRhKSAgICAgICMgQ29sdW1uIG5hbWVzCnN1bW1hcnkoZGF0YSkgICAgIyBEZXNjcmlwdGl2ZSBzdGF0aXN0aWNzCnNraW0oZGF0YSkgICAgICAgIyBEZXRhaWxlZCBkYXRhc2V0IG92ZXJ2aWV3CmRpc3RpbmN0KGRhdGEpICAgIyBJZGVudGlmeSB1bmlxdWUgb2JzZXJ2YXRpb25zCmBgYAoKIyMgQ29tbW9uIERhdGEgVHlwZXMKCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5OdW1lcmljPC9zdW1tYXJ5PgoKTnVtYmVycyB0aGF0IGNhbiBiZSB1c2VkIGluIG1hdGhlbWF0aWNhbCBjYWxjdWxhdGlvbnMuCgpFeGFtcGxlczogZWxlY3RyaWNpdHkgcHJpY2UsIGxvYWQsIHRlbXBlcmF0dXJlLCByZXZlbnVlLgoKYGBge3IgZXZhbD1GQUxTRX0KcHJpY2UgPC0gNDUuNzUKbG9hZF9tdyA8LSAxMjAwCmBgYAoKPC9kZXRhaWxzPgoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5PkludGVnZXI8L3N1bW1hcnk+CgpXaG9sZSBudW1iZXJzIHdpdGhvdXQgZGVjaW1hbCBwbGFjZXMuCgpgYGB7ciBldmFsPUZBTFNFfQp5ZWFyIDwtIDIwMjZMCmBgYAoKPC9kZXRhaWxzPgoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5PkNoYXJhY3Rlcjwvc3VtbWFyeT4KClRleHQgb3Igc3RyaW5ncy4KCmBgYHtyIGV2YWw9RkFMU0V9CnJlZ2lvbiA8LSAiUGFjaWZpYyBOb3J0aHdlc3QiCmBgYAoKPC9kZXRhaWxzPgoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5PkxvZ2ljYWw8L3N1bW1hcnk+CgpCb29sZWFuIHZhbHVlczogVFJVRSBvciBGQUxTRS4KCmBgYHtyIGV2YWw9RkFMU0V9CmlzX3dlZWtlbmQgPC0gVFJVRQpgYGAKCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5EYXRlIGFuZCBkYXRldGltZTwvc3VtbWFyeT4KClZhbHVlcyByZXByZXNlbnRpbmcgY2FsZW5kYXIgZGF0ZXMgb3Igc3BlY2lmaWMgdGltZXMuCgpgYGB7ciBldmFsPUZBTFNFfQpkYXRlIDwtIGFzLkRhdGUoIjIwMjYtMTEtMTIiKQpgYGAKCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5GYWN0b3I8L3N1bW1hcnk+CgpDYXRlZ29yaWNhbCB2YXJpYWJsZXMgd2l0aCBkZWZpbmVkIGxldmVscy4KCmBgYHtyIGV2YWw9RkFMU0V9CnJlZ2lvbiA8LSBmYWN0b3IoYygiTm9ydGgiLCAiU291dGgiLCAiTm9ydGgiKSkKYGBgCgo8L2RldGFpbHM+CgojIyBNaXNzaW5nIFZhbHVlcwoKTWlzc2luZyBvYnNlcnZhdGlvbnMgYXJlIHJlcHJlc2VudGVkIGJ5IGBOQWAgaW4gUi4KCmBgYHtyIGV2YWw9RkFMU0V9CmlzLm5hKGRhdGEpICAgICAgICAgICAgICAgICAjIElkZW50aWZ5IG1pc3NpbmcgdmFsdWVzCnN1bShpcy5uYShkYXRhKSkgICAgICAgICAgICAjIENvdW50IG1pc3NpbmcgdmFsdWVzCmNvbFN1bXMoaXMubmEoZGF0YSkpICAgICAgICAjIE1pc3NpbmcgdmFsdWVzIGJ5IGNvbHVtbgpkcm9wX25hKGRhdGEpICAgICAgICAgICAgICAgIyBSZW1vdmUgcm93cyB3aXRoIG1pc3NpbmcgdmFsdWVzCnJlcGxhY2VfbmEoZGF0YSwgbGlzdCh4PTApKSAjIFJlcGxhY2UgbWlzc2luZyB2YWx1ZXMgaW4geApgYGAKCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5JbXBvcnRhbnQ6IE1pc3NpbmcgdmFsdWVzIGluIGNhbGN1bGF0aW9uczwvc3VtbWFyeT4KCk1hbnkgUiBmdW5jdGlvbnMgcmV0dXJuIGBOQWAgd2hlbiBhIHZlY3RvciBjb250YWlucyBtaXNzaW5nIHZhbHVlcy4KClVzZSBgbmEucm0gPSBUUlVFYCB0byByZW1vdmUgbWlzc2luZyB2YWx1ZXMgZnJvbSBhIGNhbGN1bGF0aW9uLgoKYGBge3IgZXZhbD1GQUxTRX0KbWVhbihkYXRhJHByaWNlLCBuYS5ybSA9IFRSVUUpCnN1bShkYXRhJGxvYWQsIG5hLnJtID0gVFJVRSkKYGBgCgo8L2RldGFpbHM+CgojIDMuIERhdGEgTWFuaXB1bGF0aW9uCgojIyBkcGx5ciBGdW5jdGlvbnMKClRoZSBgZHBseXJgIHBhY2thZ2UgaXMgcGFydCBvZiB0aGUgdGlkeXZlcnNlIGFuZCBwcm92aWRlcyBhIGNvbnNpc3RlbnQKc2V0IG9mIGZ1bmN0aW9ucyBmb3IgbWFuaXB1bGF0aW5nIGRhdGFzZXRzLgoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5PlNlbGVjdCBhbmQgZmlsdGVyIOKAlCBDaG9vc2UgY29sdW1ucyBhbmQgcm93czwvc3VtbWFyeT4KCmBgYHtyIGV2YWw9RkFMU0V9CnNlbGVjdChkYXRhLCBwcmljZSwgbG9hZCkgICAgICAgIyBTZWxlY3QgY29sdW1ucwpmaWx0ZXIoZGF0YSwgcmVnaW9uID09ICJXZXN0IikgICMgU2VsZWN0IHJvd3MKYGBgCgotICAgYHNlbGVjdCgpYCBjaG9vc2VzIHdoaWNoIGNvbHVtbnMgdG8ga2VlcC4KLSAgIGBmaWx0ZXIoKWAgY2hvb3NlcyB3aGljaCBvYnNlcnZhdGlvbnMgdG8ga2VlcCBiYXNlZCBvbiBjb25kaXRpb25zLgoKPC9kZXRhaWxzPgoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5Pk11dGF0ZSBhbmQgcmVuYW1lIOKAlCBDcmVhdGUgYW5kIG1vZGlmeSB2YXJpYWJsZXM8L3N1bW1hcnk+CgpgYGB7ciBldmFsPUZBTFNFfQptdXRhdGUoZGF0YSwgcmV2ZW51ZSA9IHByaWNlICogbG9hZCkKCnJlbmFtZShkYXRhLCBlbGVjdHJpY2l0eV9wcmljZSA9IHByaWNlKQpgYGAKCi0gICBgbXV0YXRlKClgIGNyZWF0ZXMgbmV3IHZhcmlhYmxlcyBvciBtb2RpZmllcyBleGlzdGluZyBvbmVzLgotICAgYHJlbmFtZSgpYCBjaGFuZ2VzIGNvbHVtbiBuYW1lcy4KCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5BcnJhbmdlIOKAlCBTb3J0IG9ic2VydmF0aW9uczwvc3VtbWFyeT4KCmBgYHtyIGV2YWw9RkFMU0V9CmFycmFuZ2UoZGF0YSwgZGF0ZSkgICAgICAgICAgIyBBc2NlbmRpbmcKYXJyYW5nZShkYXRhLCBkZXNjKHByaWNlKSkgICAjIERlc2NlbmRpbmcKYGBgCgo8L2RldGFpbHM+Cgo8ZGV0YWlscyBjbGFzcz0idG9waWMiPgoKPHN1bW1hcnk+R3JvdXAgYW5kIHN1bW1hcml6ZSDigJQgQWdncmVnYXRlIG9ic2VydmF0aW9uczwvc3VtbWFyeT4KCmBgYHtyIGV2YWw9RkFMU0V9CmRhdGEgfD4KICBncm91cF9ieShyZWdpb24pIHw+CiAgc3VtbWFyaXNlKAogICAgYXZlcmFnZV9wcmljZSA9IG1lYW4ocHJpY2UsIG5hLnJtID0gVFJVRSksCiAgICB0b3RhbF9sb2FkID0gc3VtKGxvYWQsIG5hLnJtID0gVFJVRSksCiAgICBvYnNlcnZhdGlvbnMgPSBuKCkKICApCmBgYAoKLSAgIGBncm91cF9ieSgpYCBkZWZpbmVzIGdyb3VwcyBmb3Igc3Vic2VxdWVudCBjYWxjdWxhdGlvbnMuCi0gICBgc3VtbWFyaXNlKClgIGNhbGN1bGF0ZXMgc3VtbWFyeSBzdGF0aXN0aWNzLgotICAgYG4oKWAgY291bnRzIG9ic2VydmF0aW9ucyB3aXRoaW4gZWFjaCBncm91cC4KLSAgIGBjb3VudCgpYCBpcyBhIGNvbnZlbmllbnQgc2hvcnRjdXQgZm9yIGNvdW50aW5nIG9ic2VydmF0aW9ucyBieQogICAgZ3JvdXAuCgo8L2RldGFpbHM+Cgo8ZGV0YWlscyBjbGFzcz0idG9waWMiPgoKPHN1bW1hcnk+T3RoZXIgdXNlZnVsIGRwbHlyIGZ1bmN0aW9uczwvc3VtbWFyeT4KCmBgYHtyIGV2YWw9RkFMU0V9CmNvdW50KGRhdGEsIHJlZ2lvbikgICAgICAgICAgIyBDb3VudCBvYnNlcnZhdGlvbnMKZGlzdGluY3QoZGF0YSwgcmVnaW9uKSAgICAgICAjIFVuaXF1ZSB2YWx1ZXMgb3IgY29tYmluYXRpb25zCmFjcm9zcygpICAgICAgICAgICAgICAgICAgICAgIyBBcHBseSBmdW5jdGlvbnMgYWNyb3NzIGNvbHVtbnMKY2FzZV93aGVuKCkgICAgICAgICAgICAgICAgICAjIENyZWF0ZSBjb25kaXRpb25hbCB2YXJpYWJsZXMKbGFnKHByaWNlKSAgICAgICAgICAgICAgICAgICAjIFByZXZpb3VzIG9ic2VydmF0aW9uCmxlYWQocHJpY2UpICAgICAgICAgICAgICAgICAgIyBOZXh0IG9ic2VydmF0aW9uCmBgYAoKPC9kZXRhaWxzPgoKIyMgQ29tYmluaW5nIGFuZCBSZXNoYXBpbmcgRGF0YXNldHMKCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5Kb2luaW5nIGRhdGFzZXRzPC9zdW1tYXJ5PgoKSm9pbnMgY29tYmluZSBkYXRhc2V0cyB1c2luZyBvbmUgb3IgbW9yZSBzaGFyZWQgaWRlbnRpZmllcnMuCgpgYGB7ciBldmFsPUZBTFNFfQpsZWZ0X2pvaW4oeCwgeSwgYnkgPSAiSUQiKSAgICMgS2VlcCBhbGwgcm93cyBpbiB4CnJpZ2h0X2pvaW4oeCwgeSwgYnkgPSAiSUQiKSAgIyBLZWVwIGFsbCByb3dzIGluIHkKaW5uZXJfam9pbih4LCB5LCBieSA9ICJJRCIpICAjIEtlZXAgbWF0Y2hpbmcgcm93cyBvbmx5CmZ1bGxfam9pbih4LCB5LCBieSA9ICJJRCIpICAgIyBLZWVwIGFsbCByb3dzIGZyb20gYm90aAphbnRpX2pvaW4oeCwgeSwgYnkgPSAiSUQiKSAgICMgS2VlcCB1bm1hdGNoZWQgcm93cyBmcm9tIHgKYGBgCgoqKkltcG9ydGFudDoqKiBBbHdheXMgY2hlY2sgd2hldGhlciBqb2luIGtleXMgYXJlIHVuaXF1ZSBiZWZvcmUgam9pbmluZy4KRHVwbGljYXRlIGtleXMgY2FuIHVuZXhwZWN0ZWRseSBtdWx0aXBseSByb3dzIGFuZCBhZmZlY3Qgc3Vic2VxdWVudApzdGF0aXN0aWNzIGFuZCByZWdyZXNzaW9ucy4KCmBgYHtyIGV2YWw9RkFMU0V9CmNvdW50KGRhdGEsIElEKQpqYW5pdG9yOjpnZXRfZHVwZXMoZGF0YSwgSUQpCmBgYAoKPC9kZXRhaWxzPgoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5PlJlc2hhcGluZyBkYXRhc2V0czwvc3VtbWFyeT4KClJlc2hhcGluZyBjaGFuZ2VzIGhvdyBvYnNlcnZhdGlvbnMgYW5kIHZhcmlhYmxlcyBhcmUgb3JnYW5pemVkLgoKYGBge3IgZXZhbD1GQUxTRX0KcGl2b3RfbG9uZ2VyKAogIGRhdGEsCiAgY29scyA9IGMoSmFudWFyeSwgRmVicnVhcnksIE1hcmNoKSwKICBuYW1lc190byA9ICJtb250aCIsCiAgdmFsdWVzX3RvID0gInNhbGVzIgopCgpwaXZvdF93aWRlcigKICBkYXRhLAogIG5hbWVzX2Zyb20gPSBtb250aCwKICB2YWx1ZXNfZnJvbSA9IHNhbGVzCikKYGBgCgotICAgYHBpdm90X2xvbmdlcigpYCBjb252ZXJ0cyB3aWRlIGRhdGEgaW50byBsb25nIGRhdGEgYnkgc3RhY2tpbmcKICAgIGNvbHVtbnMgaW50byBrZXktdmFsdWUgcm93cy4KLSAgIGBwaXZvdF93aWRlcigpYCBjb252ZXJ0cyBsb25nIGRhdGEgaW50byB3aWRlIGRhdGEgYnkgc3ByZWFkaW5nCiAgICB2YWx1ZXMgYWNyb3NzIGNvbHVtbnMuCgo8L2RldGFpbHM+CgojIyBQaXBlcwoKUGlwZXMgcGFzcyB0aGUgcmVzdWx0IG9mIG9uZSBvcGVyYXRpb24gaW50byB0aGUgbmV4dCBvcGVyYXRpb24uCgpgYGB7ciBldmFsPUZBTFNFfQpkYXRhIHw+CiAgZmlsdGVyKHJlZ2lvbiA9PSAiV2VzdCIpIHw+CiAgZ3JvdXBfYnkoeWVhcikgfD4KICBzdW1tYXJpc2UoCiAgICBhdmVyYWdlX3ByaWNlID0gbWVhbihwcmljZSwgbmEucm0gPSBUUlVFKQogICkKYGBgCgpDb21tb24gcGlwZSBvcGVyYXRvcnM6CgotICAgYHw+YCBCYXNlIFIgcGlwZS4KLSAgIGAlPiVgIG1hZ3JpdHRyIHBpcGUsIGNvbW1vbmx5IHVzZWQgaW4gdGlkeXZlcnNlIGNvZGUuCgpCb3RoIGFsbG93IG9wZXJhdGlvbnMgdG8gYmUgd3JpdHRlbiBzZXF1ZW50aWFsbHkgcmF0aGVyIHRoYW4gbmVzdGluZwpmdW5jdGlvbnMuCgojIDQuIFN0YXRpc3RpY3MgYW5kIEVjb25vbWV0cmljcwoKIyMgRGVzY3JpcHRpdmUgU3RhdGlzdGljcwoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5PkRlc2NyaXB0aXZlIFN0YXRpc3RpY3M8L3N1bW1hcnk+CgpOdW1lcmljYWwgc3VtbWFyaWVzIHRoYXQgZGVzY3JpYmUgdGhlIGNoYXJhY3RlcmlzdGljcywgZGlzdHJpYnV0aW9uLCBhbmQKdmFyaWFiaWxpdHkgb2YgYSBkYXRhc2V0LgoKfCBNZWFzdXJlIHwgRGVmaW5pdGlvbiB8CnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfAp8IE1lYW4gfCBBcml0aG1ldGljIGF2ZXJhZ2UgfAp8IE1lZGlhbiB8IE1pZGRsZSBvYnNlcnZhdGlvbiB8CnwgVmFyaWFuY2UgfCBBdmVyYWdlIHNxdWFyZWQgZGV2aWF0aW9uIGZyb20gdGhlIG1lYW4gfAp8IFN0YW5kYXJkIGRldmlhdGlvbiB8IFNxdWFyZSByb290IG9mIHZhcmlhbmNlOyBtZWFzdXJlcyBzcHJlYWQgaW4gb3JpZ2luYWwgdW5pdHMgfAp8IFF1YW50aWxlcyB8IFZhbHVlcyBkaXZpZGluZyBhIGRpc3RyaWJ1dGlvbiBpbnRvIHNwZWNpZmllZCBwcm9wb3J0aW9ucyB8CnwgSW50ZXJxdWFydGlsZSByYW5nZSAoSVFSKSB8IDc1dGggcGVyY2VudGlsZSBtaW51cyAyNXRoIHBlcmNlbnRpbGUgfAp8IE1pbmltdW0gLyBNYXhpbXVtIHwgU21hbGxlc3QgYW5kIGxhcmdlc3Qgb2JzZXJ2YXRpb25zIHwKCioqV2h5IHRoZXkgbWF0dGVyOioqIEhlbHAgaWRlbnRpZnkgdW51c3VhbCB2YWx1ZXMsIHNrZXdlZCBkaXN0cmlidXRpb25zLAptaXNzaW5nIGRhdGEsIGFuZCBkaWZmZXJlbmNlcyBiZXR3ZWVuIGdyb3Vwcy4KCioqUiBmdW5jdGlvbnM6KioKCmBgYHtyIGV2YWw9RkFMU0V9CnN1bW1hcnkoZGF0YSkKbWVhbih4LCBuYS5ybSA9IFRSVUUpCm1lZGlhbih4LCBuYS5ybSA9IFRSVUUpCnNkKHgsIG5hLnJtID0gVFJVRSkKdmFyKHgsIG5hLnJtID0gVFJVRSkKcXVhbnRpbGUoeCwgbmEucm0gPSBUUlVFKQpgYGAKCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5Db3JyZWxhdGlvbnM8L3N1bW1hcnk+CgpNZWFzdXJlcyB0aGUgc3RyZW5ndGggYW5kIGRpcmVjdGlvbiBvZiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdHdvCnZhcmlhYmxlcy4KClBlYXJzb24ncyBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCwgciwgcmFuZ2VzIGZyb20gLTEgdG8gKzEuCgotICAgKzE6IFBlcmZlY3QgcG9zaXRpdmUgbGluZWFyIHJlbGF0aW9uc2hpcC4KLSAgIDA6IE5vIGxpbmVhciByZWxhdGlvbnNoaXAuCi0gICAtMTogUGVyZmVjdCBuZWdhdGl2ZSBsaW5lYXIgcmVsYXRpb25zaGlwLgoKKipXaHkgdGhleSBtYXR0ZXI6KiogVXNlZnVsIGZvciBleHBsb3JpbmcgcmVsYXRpb25zaGlwcyBiZXR3ZWVuCnZhcmlhYmxlcyBhbmQgaWRlbnRpZnlpbmcgcG90ZW50aWFsIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyBiZWZvcmUKcmVncmVzc2lvbiBhbmFseXNpcy4KCioqSW1wb3J0YW50OioqIENvcnJlbGF0aW9uIGRvZXMgbm90IGltcGx5IGNhdXNhdGlvbi4gQSBjb3JyZWxhdGlvbiBtYXkKcmVmbGVjdCByZXZlcnNlIGNhdXNhbGl0eSwgYSB0aGlyZCB2YXJpYWJsZSwgb3IgYSBzaGFyZWQgdHJlbmQuCgoqKlIgZnVuY3Rpb25zOioqCgpgYGB7ciBldmFsPUZBTFNFfQpjb3IoeCwgeSkKY29yLnRlc3QoeCwgeSkKR0dhbGx5OjpnZ3BhaXJzKGRhdGEpCmBgYAoKPC9kZXRhaWxzPgoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5PkxpbmVhciBSZWdyZXNzaW9uPC9zdW1tYXJ5PgoKQSBzdGF0aXN0aWNhbCBtZXRob2QgdXNlZCB0byBlc3RpbWF0ZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYW4Kb3V0Y29tZSB2YXJpYWJsZSAoZGVwZW5kZW50IHZhcmlhYmxlKSBhbmQgb25lIG9yIG1vcmUgZXhwbGFuYXRvcnkKdmFyaWFibGVzIChpbmRlcGVuZGVudCB2YXJpYWJsZXMpLgoKU2ltcGxlIGxpbmVhciByZWdyZXNzaW9uOgoKJCQKWV9pID0gXGJldGFfMCArIFxiZXRhXzEgWF9pICsgXGVwc2lsb25faQokJAoKV2hlcmU6CgotICAgWTogT3V0Y29tZSB2YXJpYWJsZS4KLSAgIFg6IEV4cGxhbmF0b3J5IHZhcmlhYmxlLgotICAgzrLigoA6IEludGVyY2VwdC4KLSAgIM6y4oKBOiBTbG9wZSBjb2VmZmljaWVudC4KLSAgIM61OiBFcnJvciB0ZXJtLgoKKipJbnRlcnByZXRhdGlvbjoqKiBBIGNvZWZmaWNpZW50IHJlcHJlc2VudHMgdGhlIGVzdGltYXRlZCBjaGFuZ2UgaW4gdGhlCm91dGNvbWUgYXNzb2NpYXRlZCB3aXRoIGEgb25lLXVuaXQgaW5jcmVhc2UgaW4gYW4gZXhwbGFuYXRvcnkgdmFyaWFibGUsCmhvbGRpbmcgb3RoZXIgaW5jbHVkZWQgdmFyaWFibGVzIGNvbnN0YW50LgoKRXhhbXBsZToKCklmIHRoZSBlc3RpbWF0ZWQgY29lZmZpY2llbnQgb24gZWxlY3RyaWNpdHkgbG9hZCBpcyAwLjA0LCBhIG9uZS11bml0CmluY3JlYXNlIGluIGxvYWQgaXMgYXNzb2NpYXRlZCB3aXRoIGEgMC4wNC11bml0IGluY3JlYXNlIGluIHByaWNlLApob2xkaW5nIG90aGVyIGluY2x1ZGVkIHZhcmlhYmxlcyBjb25zdGFudC4KCioqUiBmdW5jdGlvbnM6KioKCmBgYHtyIGV2YWw9RkFMU0V9CmxtKHkgfiB4LCBkYXRhID0gZGF0YSkKCmxtKHkgfiB4MSArIHgyICsgeDMsIGRhdGEgPSBkYXRhKQoKZml4ZXN0OjpmZW9scyh5IH4geDEgKyB4MiwgZGF0YSA9IGRhdGEpCmBgYAoKPC9kZXRhaWxzPgoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5Pk1vZGVsIFN1bW1hcmllczwvc3VtbWFyeT4KClJlZ3Jlc3Npb24gb3V0cHV0IHN1bW1hcml6ZXMgY29lZmZpY2llbnQgZXN0aW1hdGVzLCB1bmNlcnRhaW50eSwgYW5kCm1vZGVsIGZpdC4KCnwgT3V0cHV0IHwgTWVhbmluZyB8CnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfAp8IENvZWZmaWNpZW50IHwgRXN0aW1hdGVkIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGFuIGV4cGxhbmF0b3J5IHZhcmlhYmxlIGFuZCBvdXRjb21lIHwKfCBTdGFuZGFyZCBlcnJvciB8IEVzdGltYXRlZCB1bmNlcnRhaW50eSBhcm91bmQgYSBjb2VmZmljaWVudCB8CnwgdC1zdGF0aXN0aWMgfCBDb2VmZmljaWVudCBkaXZpZGVkIGJ5IGl0cyBzdGFuZGFyZCBlcnJvciB8CnwgcC12YWx1ZSB8IEV2aWRlbmNlIGFnYWluc3QgdGhlIG51bGwgaHlwb3RoZXNpcyB1bmRlciB0aGUgbW9kZWwgYXNzdW1wdGlvbnMgfAp8IFItc3F1YXJlZCB8IFByb3BvcnRpb24gb2Ygc2FtcGxlIHZhcmlhdGlvbiBpbiB0aGUgb3V0Y29tZSBleHBsYWluZWQgYnkgdGhlIGZpdHRlZCBtb2RlbCB8CnwgQWRqdXN0ZWQgUi1zcXVhcmVkIHwgUi1zcXVhcmVkIGFkanVzdGVkIGZvciB0aGUgbnVtYmVyIG9mIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyB8CnwgRi1zdGF0aXN0aWMgfCBUZXN0cyBhIGpvaW50IHJlc3RyaWN0aW9uIG9uIG1vZGVsIGNvZWZmaWNpZW50cyB8CnwgT2JzZXJ2YXRpb25zIHwgTnVtYmVyIG9mIG9ic2VydmF0aW9ucyB1c2VkIGluIGVzdGltYXRpb24gfAoKKipJbXBvcnRhbnQ6KiogQSBoaWdoIFItc3F1YXJlZCBkb2VzIG5vdCBhdXRvbWF0aWNhbGx5IG1lYW4gYSBtb2RlbCBpcwp1bmJpYXNlZCwgY2F1c2FsLCBvciB1c2VmdWwgZm9yIGZvcmVjYXN0aW5nLgoKKipSIGZ1bmN0aW9uczoqKgoKYGBge3IgZXZhbD1GQUxTRX0Kc3VtbWFyeShtb2RlbCkKYnJvb206OnRpZHkobW9kZWwpCmJyb29tOjpnbGFuY2UobW9kZWwpCmBgYAoKPC9kZXRhaWxzPgoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5PlByZWRpY3Rpb25zPC9zdW1tYXJ5PgoKRXN0aW1hdGVkIG91dGNvbWUgdmFsdWVzIGdlbmVyYXRlZCB1c2luZyBhIGZpdHRlZCBzdGF0aXN0aWNhbCBtb2RlbCBhbmQKc3BlY2lmaWVkIGV4cGxhbmF0b3J5LXZhcmlhYmxlIHZhbHVlcy4KClR3byBpbXBvcnRhbnQgZGlzdGluY3Rpb25zOgoKLSAgICoqRml0dGVkIHZhbHVlczoqKiBQcmVkaWN0aW9ucyBmb3Igb2JzZXJ2YXRpb25zIGFscmVhZHkgaW5jbHVkZWQgaW4KICAgIHRoZSBlc3RpbWF0aW9uIGRhdGFzZXQuCi0gICAqKk91dC1vZi1zYW1wbGUgcHJlZGljdGlvbnM6KiogUHJlZGljdGlvbnMgZm9yIG5ldyBvYnNlcnZhdGlvbnMgbm90CiAgICB1c2VkIHRvIGVzdGltYXRlIHRoZSBtb2RlbC4KCioqV2h5IHRoZXkgbWF0dGVyOioqIFVzZWZ1bCBmb3IgZm9yZWNhc3RpbmcsIHNjZW5hcmlvIGFuYWx5c2lzLCBhbmQKZXN0aW1hdGluZyBleHBlY3RlZCBvdXRjb21lcyB1bmRlciBkaWZmZXJlbnQgY29uZGl0aW9ucy4KCioqSW1wb3J0YW50OioqIEEgcHJlZGljdGlvbiBpcyBub3QgbmVjZXNzYXJpbHkgYSBjYXVzYWwgZXN0aW1hdGUuCgoqKlIgZnVuY3Rpb25zOioqCgpgYGB7ciBldmFsPUZBTFNFfQpwcmVkaWN0KG1vZGVsKQpwcmVkaWN0KG1vZGVsLCBuZXdkYXRhID0gbmV3X2RhdGEpCmBgYAoKPC9kZXRhaWxzPgoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5PlJlc2lkdWFsczwvc3VtbWFyeT4KClRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIG9ic2VydmVkIG91dGNvbWVzIGFuZCB0aGUgdmFsdWVzIHByZWRpY3RlZCBieSBhCnJlZ3Jlc3Npb24gbW9kZWwuCgokJAplX2kgPSBZX2kgLSBcaGF0e1l9X2kKJCQKCi0gICBQb3NpdGl2ZSByZXNpZHVhbDogTW9kZWwgdW5kZXJwcmVkaWN0ZWQgdGhlIG9ic2VydmVkIHZhbHVlLgotICAgTmVnYXRpdmUgcmVzaWR1YWw6IE1vZGVsIG92ZXJwcmVkaWN0ZWQgdGhlIG9ic2VydmVkIHZhbHVlLgotICAgWmVybyByZXNpZHVhbDogUHJlZGljdGlvbiBleGFjdGx5IG1hdGNoZXMgdGhlIG9ic2VydmF0aW9uLgoKKipXaHkgdGhleSBtYXR0ZXI6KiogUmVzaWR1YWxzIGhlbHAgZXZhbHVhdGUgbW9kZWwgZml0IGFuZCBpZGVudGlmeQpwb3RlbnRpYWwgcHJvYmxlbXMgc3VjaCBhcyBub25saW5lYXIgcmVsYXRpb25zaGlwcywgb3V0bGllcnMsCmhldGVyb3NrZWRhc3RpY2l0eSwgYW5kIGF1dG9jb3JyZWxhdGlvbi4KCioqUiBmdW5jdGlvbnM6KioKCmBgYHtyIGV2YWw9RkFMU0V9CnJlc2lkdWFscyhtb2RlbCkKZml0dGVkKG1vZGVsKQpicm9vbTo6YXVnbWVudChtb2RlbCkKcGxvdChtb2RlbCkKYGBgCgo8L2RldGFpbHM+Cgo8ZGV0YWlscyBjbGFzcz0idG9waWMiPgoKPHN1bW1hcnk+SHlwb3RoZXNpcyBUZXN0aW5nPC9zdW1tYXJ5PgoKQSBzdGF0aXN0aWNhbCBwcm9jZWR1cmUgdXNlZCB0byBldmFsdWF0ZSB3aGV0aGVyIHNhbXBsZSBkYXRhIHByb3ZpZGUKc3VmZmljaWVudCBldmlkZW5jZSBhZ2FpbnN0IGEgc3BlY2lmaWVkIGNsYWltIGFib3V0IGEgcG9wdWxhdGlvbiBvcgptb2RlbCBwYXJhbWV0ZXIuCgpUd28gY29tcGV0aW5nIGh5cG90aGVzZXM6CgotICAgKipOdWxsIGh5cG90aGVzaXMgKEjigoApOioqIFRoZSBkZWZhdWx0IGNsYWltLCBvZnRlbiB0aGF0IGEKICAgIGNvZWZmaWNpZW50IGVxdWFscyB6ZXJvIG9yIHRoYXQgbm8gcmVsYXRpb25zaGlwIGV4aXN0cy4KLSAgICoqQWx0ZXJuYXRpdmUgaHlwb3RoZXNpcyAoSOKCgSk6KiogVGhlIGNvbXBldGluZyBjbGFpbSwgc3VjaCBhcyBhCiAgICBjb2VmZmljaWVudCBiZWluZyBkaWZmZXJlbnQgZnJvbSB6ZXJvLgoKSW1wb3J0YW50IHRlcm1pbm9sb2d5OgoKLSAgICoqcC12YWx1ZToqKiBQcm9iYWJpbGl0eSBvZiBvYnNlcnZpbmcgYSB0ZXN0IHN0YXRpc3RpYyBhdCBsZWFzdCBhcwogICAgZXh0cmVtZSBhcyB0aGUgb25lIG9idGFpbmVkLCBhc3N1bWluZyB0aGUgbnVsbCBoeXBvdGhlc2lzIGFuZCBtb2RlbAogICAgYXNzdW1wdGlvbnMgYXJlIHRydWUuCi0gICAqKlNpZ25pZmljYW5jZSBsZXZlbCAozrEpOioqIFRocmVzaG9sZCBjaG9zZW4gZm9yIHJlamVjdGluZyB0aGUgbnVsbAogICAgaHlwb3RoZXNpcywgY29tbW9ubHkgMC4wNS4KLSAgICoqVHlwZSBJIGVycm9yOioqIFJlamVjdGluZyBhIHRydWUgbnVsbCBoeXBvdGhlc2lzLgotICAgKipUeXBlIElJIGVycm9yOioqIEZhaWxpbmcgdG8gcmVqZWN0IGEgZmFsc2UgbnVsbCBoeXBvdGhlc2lzLgoKKipJbXBvcnRhbnQ6KiogU3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlIGRvZXMgbm90IG5lY2Vzc2FyaWx5IGltcGx5CnByYWN0aWNhbCBpbXBvcnRhbmNlLiBXZSBnZW5lcmFsbHkgc2F5ICJyZWplY3QiIG9yICJmYWlsIHRvIHJlamVjdCIgdGhlCm51bGwgaHlwb3RoZXNpcyByYXRoZXIgdGhhbiBwcm92ZSBvciBhY2NlcHQgaXQuCgoqKlIgZnVuY3Rpb25zOioqCgpgYGB7ciBldmFsPUZBTFNFfQp0LnRlc3QoeCwgeSkKc3VtbWFyeShtb2RlbCkKYGBgCgo8L2RldGFpbHM+Cgo8ZGV0YWlscyBjbGFzcz0idG9waWMiPgoKPHN1bW1hcnk+Um9idXN0IFN0YW5kYXJkIEVycm9yczwvc3VtbWFyeT4KClN0YW5kYXJkIGVycm9ycyBjYWxjdWxhdGVkIHVzaW5nIGFuIGVzdGltYXRvciB0aGF0IGlzIHJvYnVzdCB0byBjZXJ0YWluCnZpb2xhdGlvbnMgb2YgY29udmVudGlvbmFsIHJlZ3Jlc3Npb24gYXNzdW1wdGlvbnMsIHBhcnRpY3VsYXJseQpoZXRlcm9za2VkYXN0aWNpdHkuCgoqKldoeSB0aGV5IG1hdHRlcjoqKiBDb252ZW50aW9uYWwgc3RhbmRhcmQgZXJyb3JzIG1heSBiZSBpbmNvcnJlY3Qgd2hlbgplcnJvciB2YXJpYW5jZSBpcyBub3QgY29uc3RhbnQsIGxlYWRpbmcgdG8gdW5yZWxpYWJsZSB0LXN0YXRpc3RpY3MsCnAtdmFsdWVzLCBhbmQgY29uZmlkZW5jZSBpbnRlcnZhbHMuCgoqKkltcG9ydGFudCBkaXN0aW5jdGlvbnM6KioKCi0gICBSZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBnZW5lcmFsbHkgcmVtYWluIHRoZSBzYW1lLgotICAgU3RhbmRhcmQgZXJyb3JzIGNoYW5nZS4KLSAgIHQtc3RhdGlzdGljcywgcC12YWx1ZXMsIGFuZCBjb25maWRlbmNlIGludGVydmFscyBtYXkgY2hhbmdlLgoKUm9idXN0IHN0YW5kYXJkIGVycm9ycyBkbyBub3QgZml4IGJpYXNlZCBjb2VmZmljaWVudHMsIG9taXR0ZWQKdmFyaWFibGVzLCBvciBlbmRvZ2VuZWl0eS4KCioqUiBmdW5jdGlvbnM6KioKCmBgYHtyIGV2YWw9RkFMU0V9CnNhbmR3aWNoOjp2Y292SEMobW9kZWwsIHR5cGUgPSAiSEMxIikKCmxtdGVzdDo6Y29lZnRlc3QoCiAgbW9kZWwsCiAgdmNvdiA9IHNhbmR3aWNoOjp2Y292SEMobW9kZWwsIHR5cGUgPSAiSEMxIikKKQpgYGAKCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5DbHVzdGVyZWQgU3RhbmRhcmQgRXJyb3JzPC9zdW1tYXJ5PgoKU3RhbmRhcmQgZXJyb3JzIHRoYXQgYWNjb3VudCBmb3IgdGhlIHBvc3NpYmlsaXR5IHRoYXQgb2JzZXJ2YXRpb25zCndpdGhpbiB0aGUgc2FtZSBncm91cCBhcmUgY29ycmVsYXRlZC4KCkV4YW1wbGU6IEhvdXJseSBlbGVjdHJpY2l0eSBwcmljZXMgd2l0aGluIGEgcmVnaW9uIG1heSBzaGFyZSBjb21tb24KaW5mbHVlbmNlcyBzdWNoIGFzIHdlYXRoZXIsIHRyYW5zbWlzc2lvbiBpbmZyYXN0cnVjdHVyZSwgYW5kIGdlbmVyYXRpb24KcmVzb3VyY2VzLgoKQ2x1c3RlcmluZyBieSByZWdpb24gYWxsb3dzIGVycm9ycyB0byBiZSBjb3JyZWxhdGVkIHdpdGhpbiBlYWNoIHJlZ2lvbgp3aGlsZSBhc3N1bWluZyBpbmRlcGVuZGVuY2UgYWNyb3NzIHJlZ2lvbnMuCgoqKldoeSB0aGV5IG1hdHRlcjoqKiBUcmVhdGluZyBjb3JyZWxhdGVkIG9ic2VydmF0aW9ucyBhcyBpbmRlcGVuZGVudCBjYW4KdW5kZXJlc3RpbWF0ZSB1bmNlcnRhaW50eSBhbmQgcHJvZHVjZSBtaXNsZWFkaW5nIHN0YXRpc3RpY2FsCnNpZ25pZmljYW5jZS4KCioqSW1wb3J0YW50OioqIFRoZSBudW1iZXIgb2YgaW5kZXBlbmRlbnQgY2x1c3RlcnMgbWF0dGVycy4gQSBsYXJnZQpudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGRvZXMgbm90IGNvbXBlbnNhdGUgZm9yIGhhdmluZyB2ZXJ5IGZldyBjbHVzdGVycy4KCioqUiBmdW5jdGlvbnM6KioKCmBgYHtyIGV2YWw9RkFMU0V9CmZpeGVzdDo6ZmVvbHMoCiAgcHJpY2UgfiBsb2FkICsgdGVtcGVyYXR1cmUsCiAgZGF0YSA9IGVuZXJneV9kYXRhLAogIHZjb3YgPSB+cmVnaW9uCikKYGBgCgo8L2RldGFpbHM+Cgo8ZGV0YWlscyBjbGFzcz0idG9waWMiPgoKPHN1bW1hcnk+Rml4ZWQgRWZmZWN0cyBhbmQgUGFuZWwgUmVncmVzc2lvbjwvc3VtbWFyeT4KCkZpeGVkIGVmZmVjdHMgY29udHJvbCBmb3IgdW5vYnNlcnZlZCBjaGFyYWN0ZXJpc3RpY3Mgb2YgaW5kaXZpZHVhbCB1bml0cwp0aGF0IGRvIG5vdCBjaGFuZ2Ugb3ZlciB0aW1lLgoKUGFuZWwgZGF0YSBmb2xsb3cgdGhlIHNhbWUgdW5pdHMgKHN1Y2ggYXMgcmVnaW9ucywgc3RhdGVzLCBvciBmaXJtcykKYWNyb3NzIG11bHRpcGxlIHRpbWUgcGVyaW9kcy4KCkEgc2ltcGxpZmllZCBtb2RlbDoKCiQkCllfe2l0fSA9IFxiZXRhIFhfe2l0fSArIFxhbHBoYV9pICsgXGVwc2lsb25fe2l0fQokJAoKV2hlcmU6CgotICAgWTogT3V0Y29tZSBmb3IgdW5pdCBpIGF0IHRpbWUgdC4KLSAgIFg6IEV4cGxhbmF0b3J5IHZhcmlhYmxlLgotICAgzrI6IEVzdGltYXRlZCBjb2VmZmljaWVudC4KLSAgIM6xOiBVbml0LXNwZWNpZmljIGZpeGVkIGVmZmVjdC4KLSAgIM61OiBFcnJvciB0ZXJtLgoKKipJbnRlcnByZXRhdGlvbjoqKiBUaGUgY29lZmZpY2llbnQgaXMgaWRlbnRpZmllZCB1c2luZyB2YXJpYXRpb24gd2l0aGluCnRoZSBzYW1lIHVuaXQgb3ZlciB0aW1lLCByYXRoZXIgdGhhbiBzaW1wbHkgY29tcGFyaW5nIGRpZmZlcmVuY2VzCmJldHdlZW4gdW5pdHMuCgoqKkltcG9ydGFudDoqKiBUaW1lLWludmFyaWFudCBleHBsYW5hdG9yeSB2YXJpYWJsZXMgYXJlIGFic29yYmVkIGJ5IHVuaXQKZml4ZWQgZWZmZWN0cy4gRml4ZWQgZWZmZWN0cyBkbyBub3QgYXV0b21hdGljYWxseSBjb250cm9sIGZvciB1bm9ic2VydmVkCmZhY3RvcnMgdGhhdCBjaGFuZ2Ugb3ZlciB0aW1lLgoKKipSIGZ1bmN0aW9uczoqKgoKYGBge3IgZXZhbD1GQUxTRX0KZml4ZXN0OjpmZW9scygKICBwcmljZSB+IGxvYWQgKyB0ZW1wZXJhdHVyZSB8IHJlZ2lvbiwKICBkYXRhID0gZW5lcmd5X2RhdGEKKQoKIyBSZWdpb24gYW5kIHllYXIgZml4ZWQgZWZmZWN0cwpmaXhlc3Q6OmZlb2xzKAogIHByaWNlIH4gbG9hZCArIHRlbXBlcmF0dXJlIHwgcmVnaW9uICsgeWVhciwKICBkYXRhID0gZW5lcmd5X2RhdGEKKQpgYGAKCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5XZWlnaHRlZCBSZWdyZXNzaW9uPC9zdW1tYXJ5PgoKQSByZWdyZXNzaW9uIHRlY2huaXF1ZSB0aGF0IGFzc2lnbnMgZGlmZmVyZW50IHdlaWdodHMgdG8gb2JzZXJ2YXRpb25zCndoZW4gZXN0aW1hdGluZyB0aGUgbW9kZWwuCgpPcmRpbmFyeSBsZWFzdCBzcXVhcmVzIGdpdmVzIGV2ZXJ5IG9ic2VydmF0aW9uIGVxdWFsIHdlaWdodC4gV2VpZ2h0ZWQKbGVhc3Qgc3F1YXJlcyBnaXZlcyBvYnNlcnZhdGlvbnMgZGlmZmVyZW50IGluZmx1ZW5jZSBvbiB0aGUgZXN0aW1hdGVkCmNvZWZmaWNpZW50cy4KCiQkClxtaW5fe1xiZXRhfSBcc3VtX2kgd19pKFlfaS1YX2lcYmV0YSleMgokJAoKV2VpZ2h0cyBtYXkgYmUgdXNlZCBmb3I6CgotICAgVW5lcXVhbCBzYW1wbGluZyBwcm9iYWJpbGl0aWVzLgotICAgRGlmZmVyZW50IGxldmVscyBvZiBtZWFzdXJlbWVudCBwcmVjaXNpb24uCi0gICBBcHByb3ByaWF0ZWx5IG1vZGVsZWQgaGV0ZXJvc2tlZGFzdGljaXR5LgoKKipJbXBvcnRhbnQ6KiogU3VydmV5IHdlaWdodHMsIGZyZXF1ZW5jeSB3ZWlnaHRzLCBhbmQgaW52ZXJzZS12YXJpYW5jZQp3ZWlnaHRzIGhhdmUgZGlmZmVyZW50IG1lYW5pbmdzIGFuZCBzaG91bGQgbm90IGJlIHVzZWQgaW50ZXJjaGFuZ2VhYmx5LgoKKipSIGZ1bmN0aW9uczoqKgoKYGBge3IgZXZhbD1GQUxTRX0KbG0oCiAgcHJpY2UgfiBsb2FkICsgdGVtcGVyYXR1cmUsCiAgZGF0YSA9IGVuZXJneV9kYXRhLAogIHdlaWdodHMgPSBwcmVjaXNpb25fd2VpZ2h0CikKYGBgCgo8L2RldGFpbHM+Cgo8ZGV0YWlscyBjbGFzcz0idG9waWMiPgoKPHN1bW1hcnk+SW5zdHJ1bWVudGFsIFZhcmlhYmxlcyAoSVYpPC9zdW1tYXJ5PgoKQSBtZXRob2QgdXNlZCB0byBlc3RpbWF0ZSBjYXVzYWwgcmVsYXRpb25zaGlwcyB3aGVuIGFuIGV4cGxhbmF0b3J5CnZhcmlhYmxlIGlzIGVuZG9nZW5vdXMuCgpFbmRvZ2VuZWl0eSBvY2N1cnMgd2hlbiBhbiBleHBsYW5hdG9yeSB2YXJpYWJsZSBpcyBjb3JyZWxhdGVkIHdpdGggdGhlCnJlZ3Jlc3Npb24gZXJyb3IgdGVybS4KClBvdGVudGlhbCBjYXVzZXMgaW5jbHVkZToKCi0gICBSZXZlcnNlIGNhdXNhbGl0eS4KLSAgIE9taXR0ZWQgdmFyaWFibGUgYmlhcy4KLSAgIE1lYXN1cmVtZW50IGVycm9yLgotICAgU2ltdWx0YW5lb3VzIGRldGVybWluYXRpb24gb2YgdmFyaWFibGVzLgoKQW4gaW5zdHJ1bWVudCBtdXN0IHNhdGlzZnkgdHdvIGtleSBjb25kaXRpb25zOgoKMS4gICoqUmVsZXZhbmNlOioqIFRoZSBpbnN0cnVtZW50IGlzIGNvcnJlbGF0ZWQgd2l0aCB0aGUgZW5kb2dlbm91cwogICAgZXhwbGFuYXRvcnkgdmFyaWFibGUuCjIuICAqKkV4b2dlbmVpdHkgLyBleGNsdXNpb24gcmVzdHJpY3Rpb246KiogVGhlIGluc3RydW1lbnQgYWZmZWN0cyB0aGUKICAgIG91dGNvbWUgb25seSB0aHJvdWdoIHRoZSBlbmRvZ2Vub3VzIGV4cGxhbmF0b3J5IHZhcmlhYmxlIGFuZCBpcwogICAgdW5jb3JyZWxhdGVkIHdpdGggdGhlIHN0cnVjdHVyYWwgZXJyb3IgdGVybS4KCioqRXhhbXBsZToqKiBFbGVjdHJpY2l0eSBwcmljZXMgYW5kIGNvbnN1bXB0aW9uIG1heSBiZSBzaW11bHRhbmVvdXNseQpkZXRlcm1pbmVkLiBBbiBleHRlcm5hbCBzb3VyY2Ugb2YgcHJpY2UgdmFyaWF0aW9uIG1pZ2h0IGhlbHAgaWRlbnRpZnkKdGhlIGVmZmVjdCBvZiBwcmljZXMgb24gY29uc3VtcHRpb24sIHByb3ZpZGVkIHRoZSBpbnN0cnVtZW50IGlzIHZhbGlkLgoKKipJbXBvcnRhbnQ6KiogQSBzdHJvbmcgZmlyc3Qtc3RhZ2UgcmVsYXRpb25zaGlwIGRvZXMgbm90IHByb3ZlIGFuCmluc3RydW1lbnQgaXMgdmFsaWQuIFdlYWsgaW5zdHJ1bWVudHMgY2FuIHByb2R1Y2UgdW5yZWxpYWJsZSBlc3RpbWF0ZXMsCmFuZCB0aGUgZXhjbHVzaW9uIHJlc3RyaWN0aW9uIGlzIGdlbmVyYWxseSBub3QgZGlyZWN0bHkgdGVzdGFibGUuCgoqKlIgZnVuY3Rpb25zOioqCgpgYGB7ciBldmFsPUZBTFNFfQpmaXhlc3Q6OmZlb2xzKAogIGNvbnN1bXB0aW9uIH4gdGVtcGVyYXR1cmUgfCBwcmljZSB+IGluc3RydW1lbnQsCiAgZGF0YSA9IGVuZXJneV9kYXRhCikKYGBgCgo8L2RldGFpbHM+Cgo8ZGV0YWlscyBjbGFzcz0idG9waWMiPgoKPHN1bW1hcnk+SGV0ZXJvc2tlZGFzdGljaXR5PC9zdW1tYXJ5PgoKQSBjb25kaXRpb24gaW4gd2hpY2ggdGhlIHZhcmlhbmNlIG9mIHRoZSByZWdyZXNzaW9uIGVycm9yIHRlcm0gaXMgbm90CmNvbnN0YW50IGFjcm9zcyBvYnNlcnZhdGlvbnMuCgotICAgKipIb21vc2tlZGFzdGljaXR5OioqIENvbnN0YW50IGVycm9yIHZhcmlhbmNlLgotICAgKipIZXRlcm9za2VkYXN0aWNpdHk6KiogTm9uY29uc3RhbnQgZXJyb3IgdmFyaWFuY2UuCgoqKldoeSBpdCBtYXR0ZXJzOioqIE9MUyBjb2VmZmljaWVudHMgY2FuIHJlbWFpbiB1bmJpYXNlZCB1bmRlciB0aGUgdXN1YWwKZXhvZ2VuZWl0eSBhc3N1bXB0aW9ucywgYnV0IGNvbnZlbnRpb25hbCBzdGFuZGFyZCBlcnJvcnMgYXJlIGdlbmVyYWxseQppbmNvcnJlY3QuCgoqKkhvdyB0byBhZGRyZXNzIGl0OioqCgotICAgVXNlIGhldGVyb3NrZWRhc3RpY2l0eS1yb2J1c3Qgc3RhbmRhcmQgZXJyb3JzLgotICAgQ29uc2lkZXIgd2VpZ2h0ZWQgbGVhc3Qgc3F1YXJlcyB3aGVuIHRoZSB2YXJpYW5jZSBzdHJ1Y3R1cmUgaXMKICAgIGFwcHJvcHJpYXRlbHkgdW5kZXJzdG9vZC4KLSAgIFJlY29uc2lkZXIgbW9kZWwgc3BlY2lmaWNhdGlvbiBpZiBuZWNlc3NhcnkuCgoqKlIgZnVuY3Rpb25zOioqCgpgYGB7ciBldmFsPUZBTFNFfQpsbXRlc3Q6OmJwdGVzdChtb2RlbCkKc2FuZHdpY2g6OnZjb3ZIQyhtb2RlbCwgdHlwZSA9ICJIQzEiKQpgYGAKCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5NdWx0aWNvbGxpbmVhcml0eTwvc3VtbWFyeT4KCkEgY29uZGl0aW9uIGluIHdoaWNoIHR3byBvciBtb3JlIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyBhcmUgaGlnaGx5CmNvcnJlbGF0ZWQgd2l0aCBvbmUgYW5vdGhlci4KCioqV2h5IGl0IG1hdHRlcnM6KioKCi0gICBDb2VmZmljaWVudCBlc3RpbWF0ZXMgbWF5IGhhdmUgbGFyZ2Ugc3RhbmRhcmQgZXJyb3JzLgotICAgSW5kaXZpZHVhbCBjb2VmZmljaWVudHMgbWF5IGFwcGVhciBzdGF0aXN0aWNhbGx5IGluc2lnbmlmaWNhbnQuCi0gICBDb2VmZmljaWVudCBlc3RpbWF0ZXMgbWF5IGJlIHNlbnNpdGl2ZSB0byBtb2RlbCBzcGVjaWZpY2F0aW9uLgotICAgSXQgYmVjb21lcyBkaWZmaWN1bHQgdG8gaXNvbGF0ZSBpbmRpdmlkdWFsIHJlbGF0aW9uc2hpcHMuCgoqKkltcG9ydGFudDoqKiBNdWx0aWNvbGxpbmVhcml0eSBkb2VzIG5vdCBhdXRvbWF0aWNhbGx5IGJpYXMgT0xTCmVzdGltYXRlcy4gSXQgcHJpbWFyaWx5IGFmZmVjdHMgcHJlY2lzaW9uIGFuZCBzdGFiaWxpdHkuCgoqKkhvdyB0byBpZGVudGlmeSBpdDoqKgoKLSAgIEV4YW1pbmUgY29ycmVsYXRpb24gbWF0cmljZXMuCi0gICBDYWxjdWxhdGUgdmFyaWFuY2UgaW5mbGF0aW9uIGZhY3RvcnMgKFZJRnMpLgoKKipSIGZ1bmN0aW9uczoqKgoKYGBge3IgZXZhbD1GQUxTRX0KY29yKGRhdGEpCmNhcjo6dmlmKG1vZGVsKQpgYGAKCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5PbWl0dGVkIFZhcmlhYmxlIEJpYXMgKE9WQik8L3N1bW1hcnk+CgpCaWFzIGluIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQgZXN0aW1hdGVzIHRoYXQgb2NjdXJzIHdoZW4gYW4gaW1wb3J0YW50CnZhcmlhYmxlIGlzIGV4Y2x1ZGVkIGZyb20gYSBtb2RlbCBhbmQgaXMgY29ycmVsYXRlZCB3aXRoIGJvdGggYW4KaW5jbHVkZWQgZXhwbGFuYXRvcnkgdmFyaWFibGUgYW5kIHRoZSBvdXRjb21lLgoKVHdvIGNvbmRpdGlvbnM6CgoxLiAgVGhlIG9taXR0ZWQgdmFyaWFibGUgYWZmZWN0cyB0aGUgb3V0Y29tZS4KMi4gIFRoZSBvbWl0dGVkIHZhcmlhYmxlIGlzIGNvcnJlbGF0ZWQgd2l0aCBhbiBpbmNsdWRlZCBleHBsYW5hdG9yeQogICAgdmFyaWFibGUuCgoqKkV4YW1wbGU6KiogRXN0aW1hdGluZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZWxlY3RyaWNpdHkgcHJpY2VzIGFuZApjb25zdW1wdGlvbiB3aXRob3V0IGFjY291bnRpbmcgZm9yIHNlYXNvbmFsaXR5IG1heSBiaWFzIHRoZSBlc3RpbWF0ZWQKcHJpY2UgcmVsYXRpb25zaGlwIGlmIHNlYXNvbmFsaXR5IGFmZmVjdHMgYm90aC4KCioqSG93IHRvIGFkZHJlc3MgaXQ6KioKCi0gICBJbmNsdWRlIHJlbGV2YW50IGNvbnRyb2wgdmFyaWFibGVzLgotICAgVXNlIGZpeGVkIGVmZmVjdHMgd2hlbiBhcHByb3ByaWF0ZS4KLSAgIENvbnNpZGVyIGluc3RydW1lbnRhbCB2YXJpYWJsZXMgd2hlbiBlbmRvZ2VuZWl0eSBpcyBwcmVzZW50IGFuZCBhCiAgICBjcmVkaWJsZSBpbnN0cnVtZW50IGV4aXN0cy4KLSAgIEltcHJvdmUgdGhlIHJlc2VhcmNoIGRlc2lnbi4KCioqSW1wb3J0YW50OioqIEFkZGluZyBtb3JlIHZhcmlhYmxlcyBkb2VzIG5vdCBhdXRvbWF0aWNhbGx5IGltcHJvdmUgYQptb2RlbC4gSW5hcHByb3ByaWF0ZSBjb250cm9scyBjYW4gaW50cm9kdWNlIG90aGVyIGZvcm1zIG9mIGJpYXMuCgo8L2RldGFpbHM+Cgo8ZGV0YWlscyBjbGFzcz0idG9waWMiPgoKPHN1bW1hcnk+Rml4ZWQgRWZmZWN0cyB2cy4gUmFuZG9tIEVmZmVjdHM8L3N1bW1hcnk+CgpUd28gYXBwcm9hY2hlcyB0byBtb2RlbGluZyBkaWZmZXJlbmNlcyBiZXR3ZWVuIGluZGl2aWR1YWwgdW5pdHMgaW4gcGFuZWwKZGF0YS4KCnwgRmVhdHVyZSB8IEZpeGVkIEVmZmVjdHMgfCBSYW5kb20gRWZmZWN0cyB8CnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwKfCBVbml0LXNwZWNpZmljIGRpZmZlcmVuY2VzIHwgTWF5IGJlIGNvcnJlbGF0ZWQgd2l0aCBleHBsYW5hdG9yeSB2YXJpYWJsZXMgfCBBc3N1bWVkIHVuY29ycmVsYXRlZCB3aXRoIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyB8CnwgRXN0aW1hdGlvbiB8IFVzZXMgd2l0aGluLXVuaXQgdmFyaWF0aW9uIHwgVXNlcyB3aXRoaW4tIGFuZCBiZXR3ZWVuLXVuaXQgdmFyaWF0aW9uIHwKfCBUaW1lLWludmFyaWFudCB2YXJpYWJsZXMgfCBDYW5ub3QgZXN0aW1hdGUgc2VwYXJhdGUgY29lZmZpY2llbnRzIHwgQ2FuIGVzdGltYXRlIGNvZWZmaWNpZW50cyB8CnwgTWFpbiBhc3N1bXB0aW9uIHwgQWxsb3dzIGNvcnJlbGF0aW9uIGJldHdlZW4gdW5pdCBlZmZlY3RzIGFuZCByZWdyZXNzb3JzIHwgQXNzdW1lcyBubyBjb3JyZWxhdGlvbiB8CgoqKkludGVycHJldGF0aW9uOioqCgotICAgRml4ZWQgZWZmZWN0cyBjb250cm9sIGZvciB1bm9ic2VydmVkIHVuaXQgY2hhcmFjdGVyaXN0aWNzIHRoYXQgbWF5CiAgICBiZSBjb3JyZWxhdGVkIHdpdGggZXhwbGFuYXRvcnkgdmFyaWFibGVzLgotICAgUmFuZG9tIGVmZmVjdHMgYXNzdW1lIHRob3NlIGNoYXJhY3RlcmlzdGljcyBhcmUgdW5jb3JyZWxhdGVkIHdpdGgKICAgIGV4cGxhbmF0b3J5IHZhcmlhYmxlcy4KCioqSG93IHRvIGNob29zZToqKiBGaXhlZCBlZmZlY3RzIGFyZSBvZnRlbiBwcmVmZXJhYmxlIHdoZW4gdW5vYnNlcnZlZAp1bml0LWxldmVsIGNoYXJhY3RlcmlzdGljcyBtYXkgYmUgY29ycmVsYXRlZCB3aXRoIHJlZ3Jlc3NvcnMuIFJhbmRvbQplZmZlY3RzIG1heSBiZSBhcHByb3ByaWF0ZSB3aGVuIHRoZSBzdHJvbmdlciBhc3N1bXB0aW9uIGlzIGRlZmVuc2libGUuCgoqKlIgZnVuY3Rpb25zOioqCgpgYGB7ciBldmFsPUZBTFNFfQpwbG06OnBsbSgKICB5IH4geDEgKyB4MiwKICBkYXRhID0gcGFuZWxfZGF0YSwKICBpbmRleCA9IGMoInJlZ2lvbiIsICJ5ZWFyIiksCiAgbW9kZWwgPSAid2l0aGluIgopCgpwbG06OnBsbSgKICB5IH4geDEgKyB4MiwKICBkYXRhID0gcGFuZWxfZGF0YSwKICBpbmRleCA9IGMoInJlZ2lvbiIsICJ5ZWFyIiksCiAgbW9kZWwgPSAicmFuZG9tIgopCgpwbG06OnBodGVzdChmaXhlZF9tb2RlbCwgcmFuZG9tX21vZGVsKQpgYGAKCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5JbnRlcmFjdGlvbiBUZXJtczwvc3VtbWFyeT4KClZhcmlhYmxlcyBjcmVhdGVkIGJ5IG11bHRpcGx5aW5nIHR3byBvciBtb3JlIGV4cGxhbmF0b3J5IHZhcmlhYmxlcwp0b2dldGhlci4gSW50ZXJhY3Rpb25zIGFsbG93IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBvbmUgdmFyaWFibGUgYW5kCnRoZSBvdXRjb21lIHRvIGRlcGVuZCBvbiBhbm90aGVyIHZhcmlhYmxlLgoKRXhhbXBsZTogVGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRlbXBlcmF0dXJlIGFuZCBlbGVjdHJpY2l0eSBkZW1hbmQgbWF5CmRpZmZlciBiZXR3ZWVuIHdlZWtkYXlzIGFuZCB3ZWVrZW5kcy4KCiQkCkxvYWRfaSA9IFxiZXRhXzAgKyBcYmV0YV8xIFRlbXBlcmF0dXJlX2kKKyBcYmV0YV8yIFdlZWtlbmRfaQorIFxiZXRhXzMoVGVtcGVyYXR1cmVfaSBcdGltZXMgV2Vla2VuZF9pKQorIFxlcHNpbG9uX2kKJCQKCkludGVycHJldGF0aW9uOgoKLSAgIM6y4oKAOiBFeHBlY3RlZCBsb2FkIHdoZW4gdGVtcGVyYXR1cmUgaXMgemVybyBvbiB3ZWVrZGF5cy4KLSAgIM6y4oKBOiBUZW1wZXJhdHVyZSBzbG9wZSBvbiB3ZWVrZGF5cy4KLSAgIM6y4oKCOiBXZWVrZW5kIHZlcnN1cyB3ZWVrZGF5IGRpZmZlcmVuY2Ugd2hlbiB0ZW1wZXJhdHVyZSBpcyB6ZXJvLgotICAgzrLigoM6IERpZmZlcmVuY2UgaW4gdGhlIHRlbXBlcmF0dXJlIHNsb3BlIGJldHdlZW4gd2Vla2VuZHMgYW5kCiAgICB3ZWVrZGF5cy4KClRoZSB0ZW1wZXJhdHVyZSBzbG9wZSBpczoKCi0gICBXZWVrZGF5czogzrLigoEKLSAgIFdlZWtlbmRzOiDOsuKCgSArIM6y4oKDCgoqKlIgc3ludGF4OioqCgpgYGB7ciBldmFsPUZBTFNFfQpsbSgKICBsb2FkIH4gdGVtcGVyYXR1cmUgKiB3ZWVrZW5kLAogIGRhdGEgPSBlbmVyZ3lfZGF0YQopCmBgYAoKVGhlIGAqYCBvcGVyYXRvciBpbmNsdWRlcyBib3RoIG1haW4gZWZmZWN0cyBhbmQgdGhlIGludGVyYWN0aW9uOgoKYGBge3IgZXZhbD1GQUxTRX0KdGVtcGVyYXR1cmUgKiB3ZWVrZW5kCgojIEVxdWl2YWxlbnQgdG86CnRlbXBlcmF0dXJlICsgd2Vla2VuZCArIHRlbXBlcmF0dXJlOndlZWtlbmQKYGBgCgo8L2RldGFpbHM+CgojIDUuIERhdGEgVmlzdWFsaXphdGlvbgoKIyMgZ2dwbG90MgoKVGhlIGBnZ3Bsb3QyYCBwYWNrYWdlIGNyZWF0ZXMgdmlzdWFsaXphdGlvbnMgdXNpbmcgYSBsYXllcmVkIGFwcHJvYWNoLgoKQmFzaWMgc3RydWN0dXJlOgoKYGBge3IgZXZhbD1GQUxTRX0KZ2dwbG90KGRhdGEsIGFlcyh4ID0gdmFyaWFibGUxLCB5ID0gdmFyaWFibGUyKSkgKwogIGdlb21fcG9pbnQoKSArCiAgbGFicygKICAgIHRpdGxlID0gIlJlbGF0aW9uc2hpcCBCZXR3ZWVuIFZhcmlhYmxlcyIsCiAgICB4ID0gIlZhcmlhYmxlIDEiLAogICAgeSA9ICJWYXJpYWJsZSAyIgogICkKYGBgCgojIyBDb21tb24gR2VvbWV0cmllcwoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5PlNjYXR0ZXJwbG90czwvc3VtbWFyeT4KClNob3cgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHR3byBjb250aW51b3VzIHZhcmlhYmxlcy4KCmBgYHtyIGV2YWw9RkFMU0V9CmdncGxvdChkYXRhLCBhZXMoeCA9IGxvYWQsIHkgPSBwcmljZSkpICsKICBnZW9tX3BvaW50KCkKYGBgCgo8L2RldGFpbHM+Cgo8ZGV0YWlscyBjbGFzcz0idG9waWMiPgoKPHN1bW1hcnk+TGluZSBncmFwaHM8L3N1bW1hcnk+CgpVc2VmdWwgZm9yIHZpc3VhbGl6aW5nIHRyZW5kcyBvdmVyIHRpbWUuCgpgYGB7ciBldmFsPUZBTFNFfQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBkYXRlLCB5ID0gcHJpY2UpKSArCiAgZ2VvbV9saW5lKCkKYGBgCgo8L2RldGFpbHM+Cgo8ZGV0YWlscyBjbGFzcz0idG9waWMiPgoKPHN1bW1hcnk+QmFyIGNoYXJ0czwvc3VtbWFyeT4KCkNvbXBhcmUgY2F0ZWdvcmllcyBvciBncm91cHMuCgpgYGB7ciBldmFsPUZBTFNFfQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSByZWdpb24sIHkgPSBhdmVyYWdlX3ByaWNlKSkgKwogIGdlb21fY29sKCkKYGBgCgo8L2RldGFpbHM+Cgo8ZGV0YWlscyBjbGFzcz0idG9waWMiPgoKPHN1bW1hcnk+SGlzdG9ncmFtczwvc3VtbWFyeT4KClZpc3VhbGl6ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIGEgY29udGludW91cyB2YXJpYWJsZS4KCmBgYHtyIGV2YWw9RkFMU0V9CmdncGxvdChkYXRhLCBhZXMoeCA9IHByaWNlKSkgKwogIGdlb21faGlzdG9ncmFtKCkKYGBgCgo8L2RldGFpbHM+CgojIDYuIFRpbWUtU2VyaWVzIEFuYWx5c2lzCgojIyBEYXRlcyBhbmQgVGltZQoKRWxlY3RyaWNpdHktbWFya2V0IGRhdGEgZnJlcXVlbnRseSBpbnZvbHZlIGhvdXJseSwgZGFpbHksIG1vbnRobHksIGFuZAphbm51YWwgb2JzZXJ2YXRpb25zLgoKYGBge3IgZXZhbD1GQUxTRX0KeW1kKCIyMDI2LTExLTEyIikgICAgICAgIyBQYXJzZSBhIGRhdGUKeWVhcihkYXRlKSAgICAgICAgICAgICAgIyBFeHRyYWN0IHllYXIKbW9udGgoZGF0ZSkgICAgICAgICAgICAgIyBFeHRyYWN0IG1vbnRoCmRheShkYXRlKSAgICAgICAgICAgICAgICMgRXh0cmFjdCBkYXkKaG91cihkYXRldGltZSkgICAgICAgICAgIyBFeHRyYWN0IGhvdXIKZmxvb3JfZGF0ZShkYXRlLCAibW9udGgiKSAjIFJvdW5kIGRvd24gdG8gbW9udGgKYGBgCgojIyBUaW1lLVNlcmllcyBDb25jZXB0cwoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5PlRyZW5kczwvc3VtbWFyeT4KCkxvbmctdGVybSBpbmNyZWFzZXMgb3IgZGVjcmVhc2VzIGluIGEgdmFyaWFibGUgb3ZlciB0aW1lLgoKPC9kZXRhaWxzPgoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5PlNlYXNvbmFsaXR5PC9zdW1tYXJ5PgoKUmVjdXJyaW5nIHBhdHRlcm5zIHRoYXQgb2NjdXIgYXQgcmVndWxhciBpbnRlcnZhbHMsIHN1Y2ggYXMgZGFpbHksCndlZWtseSwgb3IgYW5udWFsIGN5Y2xlcy4KCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5BdXRvY29ycmVsYXRpb248L3N1bW1hcnk+CgpUaGUgY29ycmVsYXRpb24gb2YgYSB2YXJpYWJsZSB3aXRoIGl0cyBvd24gcGFzdCB2YWx1ZXMuCgo8L2RldGFpbHM+Cgo8ZGV0YWlscyBjbGFzcz0idG9waWMiPgoKPHN1bW1hcnk+TGFnZ2VkIFZhcmlhYmxlczwvc3VtbWFyeT4KClZhcmlhYmxlcyByZXByZXNlbnRpbmcgcHJldmlvdXMgb2JzZXJ2YXRpb25zLgoKYGBge3IgZXZhbD1GQUxTRX0KZGF0YSB8PgogIGFycmFuZ2UoZGF0ZSkgfD4KICBtdXRhdGUocHJldmlvdXNfcHJpY2UgPSBsYWcocHJpY2UpKQpgYGAKCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5Nb3ZpbmcgQXZlcmFnZXM8L3N1bW1hcnk+CgpBdmVyYWdlcyBjYWxjdWxhdGVkIG92ZXIgYSByb2xsaW5nIHdpbmRvdyBvZiBvYnNlcnZhdGlvbnMgdG8gc21vb3RoCnNob3J0LXRlcm0gZmx1Y3R1YXRpb25zLgoKPC9kZXRhaWxzPgoKIyA3LiBFbGVjdHJpY2l0eSBNYXJrZXRzIGFuZCBFY29ub21pYyBBcHBsaWNhdGlvbnMKCiMjIEVsZWN0cmljaXR5IE1hcmtldCBGdW5kYW1lbnRhbHMKCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5Mb2FkIGFuZCBEZW1hbmQ8L3N1bW1hcnk+CgpFbGVjdHJpY2l0eSBjb25zdW1wdGlvbiBvdmVyIGEgc3BlY2lmaWVkIHBlcmlvZCwgY29tbW9ubHkgbWVhc3VyZWQgaW4KbWVnYXdhdHRzIChNVykuCgo8L2RldGFpbHM+Cgo8ZGV0YWlscyBjbGFzcz0idG9waWMiPgoKPHN1bW1hcnk+R2VuZXJhdGlvbiBNaXg8L3N1bW1hcnk+CgpUaGUgY29tYmluYXRpb24gb2YgcmVzb3VyY2VzIHVzZWQgdG8gZ2VuZXJhdGUgZWxlY3RyaWNpdHksIGluY2x1ZGluZwpoeWRyb3Bvd2VyLCBuYXR1cmFsIGdhcywgY29hbCwgd2luZCwgc29sYXIsIGFuZCBudWNsZWFyLgoKPC9kZXRhaWxzPgoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5Pk1hcmdpbmFsIENvc3Q8L3N1bW1hcnk+CgpUaGUgYWRkaXRpb25hbCBjb3N0IG9mIHByb2R1Y2luZyBvbmUgbW9yZSB1bml0IG9mIGVsZWN0cmljaXR5LgoKPC9kZXRhaWxzPgoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5PkxvY2F0aW9uYWwgTWFyZ2luYWwgUHJpY2luZyAoTE1QKTwvc3VtbWFyeT4KClRoZSBwcmljZSBvZiBlbGVjdHJpY2l0eSBhdCBhIHBhcnRpY3VsYXIgbG9jYXRpb24sIHJlZmxlY3RpbmcgdGhlCm1hcmdpbmFsIGNvc3Qgb2Ygc2VydmluZyBhZGRpdGlvbmFsIGRlbWFuZCwgaW5jbHVkaW5nIGVuZXJneSwKY29uZ2VzdGlvbiwgYW5kIGxvc3Nlcy4KCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5UcmFuc21pc3Npb24gQ29uZ2VzdGlvbjwvc3VtbWFyeT4KCk9jY3VycyB3aGVuIHRyYW5zbWlzc2lvbiBjb25zdHJhaW50cyBsaW1pdCB0aGUgYWJpbGl0eSB0byBtb3ZlCmVsZWN0cmljaXR5IGJldHdlZW4gbG9jYXRpb25zLgoKPC9kZXRhaWxzPgoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5PlJlc291cmNlIEFkZXF1YWN5PC9zdW1tYXJ5PgoKVGhlIGFiaWxpdHkgb2YgdGhlIGVsZWN0cmljaXR5IHN5c3RlbSB0byBtYWludGFpbiBzdWZmaWNpZW50IHJlc291cmNlcwp0byBtZWV0IGRlbWFuZCByZWxpYWJseS4KCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5BbmNpbGxhcnkgU2VydmljZXM8L3N1bW1hcnk+CgpTZXJ2aWNlcyB0aGF0IGhlbHAgbWFpbnRhaW4gZ3JpZCByZWxpYWJpbGl0eSwgaW5jbHVkaW5nIGZyZXF1ZW5jeQpyZWd1bGF0aW9uIGFuZCBvcGVyYXRpbmcgcmVzZXJ2ZXMuCgo8L2RldGFpbHM+Cgo8ZGV0YWlscyBjbGFzcz0idG9waWMiPgoKPHN1bW1hcnk+SHlkcm9wb3dlcjwvc3VtbWFyeT4KCkVsZWN0cmljaXR5IGdlbmVyYXRlZCBmcm9tIG1vdmluZyB3YXRlci4gUmVzZXJ2b2lyIGxldmVscywgc3RyZWFtZmxvdywKd2F0ZXIgYXZhaWxhYmlsaXR5LCBhbmQgb3BlcmF0aW5nIGNvbnN0cmFpbnRzIGluZmx1ZW5jZSBoeWRyb3Bvd2VyCmdlbmVyYXRpb24uCgo8L2RldGFpbHM+CgojIyBXZXN0ZXJuIEVsZWN0cmljaXR5IE1hcmtldHMKCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5DQUlTTzwvc3VtbWFyeT4KCkNhbGlmb3JuaWEgSW5kZXBlbmRlbnQgU3lzdGVtIE9wZXJhdG9yLgoKPC9kZXRhaWxzPgoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5PldFSU08L3N1bW1hcnk+CgpXZXN0ZXJuIEVuZXJneSBJbWJhbGFuY2UgTWFya2V0LgoKPC9kZXRhaWxzPgoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5PkVEQU08L3N1bW1hcnk+CgpFeHRlbmRlZCBEYXktQWhlYWQgTWFya2V0LgoKPC9kZXRhaWxzPgoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5Pk1hcmtldHMrPC9zdW1tYXJ5PgoKQSBXZXN0ZXJuIGRheS1haGVhZCBlbGVjdHJpY2l0eSBtYXJrZXQgaW5pdGlhdGl2ZS4KCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5SVE8gLyBJU088L3N1bW1hcnk+CgpSZWdpb25hbCBUcmFuc21pc3Npb24gT3JnYW5pemF0aW9uIC8gSW5kZXBlbmRlbnQgU3lzdGVtIE9wZXJhdG9yLgpPcmdhbml6YXRpb25zIHRoYXQgY29vcmRpbmF0ZSBlbGVjdHJpY2l0eSB0cmFuc21pc3Npb24gYW5kIHdob2xlc2FsZQptYXJrZXQgb3BlcmF0aW9ucy4KCjwvZGV0YWlscz4KCiMgOC4gVXNlZnVsIFIgU2hvcnRjdXRzIGFuZCBUcm91Ymxlc2hvb3RpbmcKCiMjIENvbW1vbiBSU3R1ZGlvIFNob3J0Y3V0cwoKfCBBY3Rpb24gICAgICAgICAgICAgICAgICAgICAgICB8IE1hYyAgICAgICAgICAgICAgfCBXaW5kb3dzIC8gTGludXggIHwKfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLXwKfCBSdW4gY3VycmVudCBsaW5lIG9yIHNlbGVjdGlvbiB8IENtZCArIEVudGVyICAgICAgfCBDdHJsICsgRW50ZXIgICAgIHwKfCBJbnNlcnQgYXNzaWdubWVudCBvcGVyYXRvciAgICB8IE9wdGlvbiArIC0gICAgICAgfCBBbHQgKyAtICAgICAgICAgIHwKfCBSZWZvcm1hdCBjb2RlICAgICAgICAgICAgICAgICB8IENtZCArIFNoaWZ0ICsgQSAgfCBDdHJsICsgU2hpZnQgKyBBIHwKfCBDb21tZW50IC8gdW5jb21tZW50ICAgICAgICAgICB8IENtZCArIFNoaWZ0ICsgQyAgfCBDdHJsICsgU2hpZnQgKyBDIHwKfCBJbnNlcnQgY29kZSBjaHVuayAgICAgICAgICAgICB8IE9wdGlvbiArIENtZCArIEkgfCBDdHJsICsgQWx0ICsgSSAgIHwKCiMjIENvbW1vbiBUcm91Ymxlc2hvb3RpbmcKCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5PYmplY3Qgbm90IGZvdW5kPC9zdW1tYXJ5PgoKUiBjYW5ub3QgZmluZCB0aGUgb2JqZWN0IHJlZmVyZW5jZWQgaW4gdGhlIGNvZGUuCgpDaGVjayBzcGVsbGluZywgY2FwaXRhbGl6YXRpb24sIGFuZCB3aGV0aGVyIHRoZSBvYmplY3QgaGFzIGJlZW4gY3JlYXRlZApvciBpbXBvcnRlZC4KCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5Db3VsZCBub3QgZmluZCBmdW5jdGlvbjwvc3VtbWFyeT4KClRoZSBmdW5jdGlvbiBtYXkgYmVsb25nIHRvIGEgcGFja2FnZSB0aGF0IGhhcyBub3QgYmVlbiBsb2FkZWQuCgpMb2FkIHRoZSBwYWNrYWdlIG9yIHVzZSBpdHMgbmFtZXNwYWNlOgoKYGBge3IgZXZhbD1GQUxTRX0KbGlicmFyeShkcGx5cikKCmRwbHlyOjpmaWx0ZXIoZGF0YSwgcHJpY2UgPiA1MCkKYGBgCgo8L2RldGFpbHM+Cgo8ZGV0YWlscyBjbGFzcz0idG9waWMiPgoKPHN1bW1hcnk+VW5leHBlY3RlZCBOQSB2YWx1ZXM8L3N1bW1hcnk+CgpDaGVjayB3aGV0aGVyIG1pc3Npbmcgb2JzZXJ2YXRpb25zIG9yIG1pc21hdGNoZWQgZGF0YSB0eXBlcyBhcmUKYWZmZWN0aW5nIGNhbGN1bGF0aW9ucyBhbmQgam9pbnMuCgpgYGB7ciBldmFsPUZBTFNFfQpzdW0oaXMubmEoZGF0YSkpCnN0cihkYXRhKQpgYGAKCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJ0b3BpYyI+Cgo8c3VtbWFyeT5VbmV4cGVjdGVkIG51bWJlciBvZiByb3dzIGFmdGVyIGpvaW5pbmc8L3N1bW1hcnk+CgpDaGVjayB3aGV0aGVyIHRoZSBqb2luIGtleXMgY29udGFpbiBkdXBsaWNhdGVzLgoKYGBge3IgZXZhbD1GQUxTRX0KY291bnQoZGF0YSwgSUQpCmphbml0b3I6OmdldF9kdXBlcyhkYXRhLCBJRCkKYGBgCgo8L2RldGFpbHM+Cgo8ZGV0YWlscyBjbGFzcz0idG9waWMiPgoKPHN1bW1hcnk+UGFja2FnZSBpbnN0YWxsYXRpb24gZXJyb3JzPC9zdW1tYXJ5PgoKTWFrZSBzdXJlIFIgaGFzIGEgQ1JBTiByZXBvc2l0b3J5IHNlbGVjdGVkLgoKYGBge3IgZXZhbD1GQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygKICAicGFja2FnZV9uYW1lIiwKICByZXBvcyA9ICJodHRwczovL2Nsb3VkLnItcHJvamVjdC5vcmciCikKYGBgCgo8L2RldGFpbHM+CgojIDkuIEV4cGVyaW1lbnRhbCBEYXRhIEFuYWx5c2lzCgo8L2RldGFpbHM+Cgo8ZGV0YWlscyBjbGFzcz0idG9waWMiPgoKPHN1bW1hcnk+SW1wb3J0aW5nIGFuZCBkZWZpbmluZyBkYXRhPC9zdW1tYXJ5PgoKRGF0YSBkb3dubG9hZGVkIGZyb20KPGh0dHBzOi8vdHJhbnNtaXNzaW9uLmJwYS5nb3YvYnVzaW5lc3Mvb3BlcmF0aW9ucy93aW5kL2JhbHR3ZzMuYXNweD4uClJlYWwtdGltZSBkYXRhIGZyb20gdGhlIGxhc3Qgc2V2ZW4gZGF5cywgdXBkYXRlZCBldmVyeSBmaXZlIG1pbnV0ZXMuCgpJbmNsdWRlcyBkYXRhIG9uIGxvYWQgYW5kIFZFUiAoVmFyaWFibGUgRW5lcmd5IFJlc291cmNlLCB1c3VhbGx5IHNvbGFyCm9yIHdpbmQsIHdoaWNoIGNoYW5nZSB3aXRoIHRoZSBjb25kaXRpb25zKSwgYXMgd2VsbCBhcyBoeWRybywKZm9zc2lsL2Jpb21hc3MsIGludGVyY2hhbmdlLCBhbmQgbnVjbGVhci4KCnwgVGVybSB8IERlZmluaXRpb24gfCBDb25zdHJhaW50cyB8CnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwKfCBMb2FkIHwgVGhlIGFtb3VudCBvZiBlbGVjdHJpY2FsIHBvd2VyIGJlaW5nIGNvbnN1bWVkIGJ5IGN1c3RvbWVycyBhbmQgb3RoZXIgZWxlY3RyaWNpdHkgdXNlcnMgYXQgYW55IGdpdmVuIHBvaW50IGluIHRpbWUuIFVzdWFsbHkgbWVhc3VyZWQgaW4gbWVnYXdhdHRzIChNVykuIFRoaXMgcmVwcmVzZW50cyBlbGVjdHJpY2l0eSAqKmRlbWFuZCoqIG5vdCBnZW5lcmF0aW9uLiB8ICB8CnwgVkVSIChWYXJpYWJsZSBFbmVyZ3kgUmVzb3VyY2VzKSB8IEVsZWN0cmljaXR5IGdlbmVyYXRpb24gcmVzb3VyY2VzIHdob3NlIG91dHB1dCB2YXJpZXMgYmFzZWQgb24gZW52aXJvbm1lbnRhbCBjb25kaXRpb25zIHJhdGhlciB0aGFuIGJlaW5nIGZ1bGx5IGRpc3BhdGNoYWJsZSBvbiBkZW1hbmQuIENvbW1vbiBleGFtcGxlcyBpbmNsdWRlIHdpbmQgb3Igc29sYXIgZ2VuZXJhdGlvbi4gfCBEZXBlbmRlbnQgb24gd2VhdGhlciBjb25kaXRpb25zLCB3aGljaCBjYW4gYmUgdW5zdGFibGUsIGJ1dCBub3QgYWx3YXlzIHVucHJlZGljdGFibGUuIHwKfCBIeWRyb2VsZWN0cmljIHwgR2VuZXJhdGVkIGJ5IGNvbnZlcnRpbmcgdGhlIGVuZXJneSBvZiBtb3Zpbmcgb3IgZmFsbGluZyB3YXRlciBpbnRvIGVsZWN0cmljYWwgZW5lcmd5IHVzaW5nIHR1cmJpbmVzIGFuZCBnZW5lcmF0b3JzLiBDYW4gYmUgYWRqdXN0ZWQgcmVsYXRpdmVseSBxdWlja2x5IGJ5IGNvbnRyb2xsaW5nIHdhdGVyIGZsb3dpbmcgdGhyb3VnaCB0dXJiaW5lcy4gfCBXYXRlciBhdmFpbGFiaWxpdHksIHJlc2Vydm9pciBsZXZlbHMsIGVudmlyb25tZW50YWwgcmVxdWlyZW1lbnRzLCBhbmQgb3BlcmF0aW9uYWwgcmVzdHJpY3Rpb25zLiB8CnwgRm9zc2lsL0Jpb21hc3MgR2VuZXJhdGlvbiB8IEdlbmVyYXRlZCBieSB0aGUgYnVybmluZyBvZiBmb3NzaWwgZnVlbHMgb2YgYmlvbWFzcy4gRXhhbXBsZXM6IE5hdHVyYWwgZ2FzLCBjb2FsLCBvcmdhbmljIG1hdHRlciB1c2VkIGFzIGZ1ZWwuIE1vc3QgZm9zc2lsIGZ1ZWwgcGxhbnRzIGFyZSBkaXNwYXRjaGFibGUsIGFuZCBvdXRwdXQgaXMgZWFzaWx5IGFkanVzdGVkLiB8IEVtaXNzaW9ucyBhbmQgZW52aXJvbm1lbnRhbCByZWd1bGF0aW9ucy4gT3BlcmF0aW5nIGNvc3RzIGRlcGVuZCBvbiBmdWVsIHByaWNlcy4gfAp8IE51Y2xlYXIgfCBHZW5lcmF0ZWQgdGhyb3VnaCBudWNsZWFyIGZpc3Npb24sIHdoZXJlIHNwbGl0dGluZyBhdG9taWMgbnVjbGVpIHJlbGVhc2VzIGhlYXQgd2hpY2ggcHJvZHVjZXMgc3RlYW0gdG8gZHJpdmUgYSB0dXJiaW5lLiBQcm92aWRlcyByZWxhdGl2ZWx5IGNvbnNpc3RlbnQsIGNvbnRpbnVvdXMgZ2VuZXJhdGlvbiwgYnV0IGhhcyBoaWdoIGNvbnN0cnVjdGlvbiBjb3N0IGFuZCBpcyBnZW5lcmFsbHkgbGVzcyBmbGV4aWJsZS4gfCAgfAp8IE5ldCBJbnRlcmNoYW5nZSB8IFRoZSBuZXQgYW1vdW50IG9mIGVsZWN0cmljaXR5IHRyYW5zZmVycmVkIGJldHdlZW4gYSBiYWxhbmNpbmcgYXV0aG9yaXR5IChpZSBCUEEpIGFuZCBuZWlnaGJvcmluZyBiYWxhbmNpbmcgYXV0aG9yaXRpZXMuIHwgIHwKfCBHZW5lcmF0aW9uIHwgRWxlY3RyaWNpdHkgcHJvZHVjZWQgYnkgcG93ZXIgcGxhbnRzIGFuZCBvdGhlciByZXNvdXJjZXMuIHwgIHwKfCBEaXNwYXRjaCB8IFRoZSBwcm9jZXNzIG9mIGRldGVybWluaW5nIHdoaWNoIGdlbmVyYXRpb24gcmVzb3VyY2VzIG9wZXJhdGUgYW5kIGF0IHdoYXQgb3V0cHV0IGxldmVscy4gfCAgfAp8IERpc3BhdGNoYWJsZSBHZW5lcmF0aW9uIHwgR2VuZXJhdGlvbiB0aGF0IG9wZXJhdG9ycyBjYW4gY29udHJvbCBvciBhZGp1c3Qgd2hlbiBuZWVkZWQsIHN1YmplY3QgdG8gb3BlcmF0aW5nIGNvbnN0cmFpbnRzLiB8ICB8CnwgUmFtcCBSYXRlIHwgSG93IHF1aWNrbHkgYSBnZW5lcmF0aW9uIHJlc291cmNlIGNhbiBpbmNyZWFzZSBvciBkZWNyZWFzZSBpdHMgb3V0cHV0LiB8ICB8CnwgUGVhayBMb2FkIHwgVGhlIG1heGltdW0gZWxlY3RyaWNpdHkgZGVtYW5kIGR1cmluZyBhIHNwZWNpZmljIHBlcmlvZC4gfCAgfAp8IExvYWQgRm9yZWNhc3RpbmcgfCBQcmVkaWN0aW5nIHRoZSBmdXR1cmUgZWxlY3RyaWNpdHkgZGVtYW5kLiB8ICB8CnwgR2VuZXJhdGlvbiBNaXggfCBUaGUgY29tYmluYXRpb24gb2YgZGlmZmVyZW50IHJlc291cmNlcyB1c2VkIHRvIHByb2R1Y2UgZWxlY3RyaWNpdHkuIHwgIHwKfCBNVyAobWVnYXdhdHQpIHwgQSB1bml0IG9mIHBvd2VyLCBtZWFzdXJpbmcgdGhlIGluc3RhbnRhbmVvdXMgcmF0ZSBvZiBlbGVjdHJpY2l0eSBwcm9kdWN0aW9uIG9yIGNvbnN1bXB0aW9uLiB8ICB8CnwgTVdoIChtZWdhd2F0dC1ob3VyKSB8IEEgdW5pdCBvZiBlbmVyZ3ksIG1lYXN1cmluZyBlbGVjdHJpY2l0eSBwcm9kdWNlZCBvciBjb25zdW1lZCBvdmVyIHRpbWUuIHwgIHwKCk5vdGU6IFRoZSBkYXRhc2V0IHJlcG9ydHMgZml2ZS1taW51dGUgTVcgcmVhZGluZ3MuIFRoZXNlIGFyZQptZWFzdXJlbWVudHMgb2YgcG93ZXIgYXQgcGFydGljdWxhciBwb2ludHMgaW4gdGltZSwgbm90IHRoZSB0b3RhbCBlbmVyZ3kKZ2VuZXJhdGVkIG92ZXIgZWFjaCBmaXZlLW1pbnV0ZSBpbnRlcnZhbC4KClNpbXBseSwgJCRHZW5lcmF0aW9uICsgTmV0IEltcG9ydHMg4omIIExvYWQgJCQgRXhwYW5kaW5nIG9uIHRoYXQsCiQkSHlkcm8gKyBWRVIgKyBGb3NzaWwvQmlvbWFzcyArIE51Y2xlYXIgKyBOZXQgSW1wb3J0cyDiiYggTG9hZCAkJAoKYGBge3J9CiMgSW1wb3J0IEJQQSBiYWxhbmNpbmcgYXV0aG9yaXR5IGRhdGEKbG9hZF9kYXRhIDwtIHJlYWRfdHN2KAogICJ+L0Rlc2t0b3AvQlBBX0RhdGEudHh0IgopICU+JQogIGNsZWFuX25hbWVzKCkgJT4lCiAgbXV0YXRlKAogICAgZGF0ZV90aW1lID0gbWR5X2htKGRhdGVfdGltZSwgdHogPSAiQW1lcmljYS9Mb3NfQW5nZWxlcyIpCiAgKQoKIyBJbnNwZWN0IGltcG9ydGVkIGRhdGEKIyBnbGltcHNlKGxvYWRfZGF0YSkKIyBzdW1tYXJ5KGxvYWRfZGF0YSkKCmBgYAoKPC9kZXRhaWxzPgoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5PkV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXM8L3N1bW1hcnk+Cgo8ZGV0YWlscyBjbGFzcz0ic3VidG9waWMiPgoKPHN1bW1hcnk+RGF0YSBvdmVydmlldyAmIGNsZWFuaW5nPC9zdW1tYXJ5PgoKYGBge3J9CiMgTnVtYmVyIG9mIG9ic2VydmF0aW9ucyBhbmQgdmFyaWFibGVzCmRpbShsb2FkX2RhdGEpCgojIFZhcmlhYmxlIG5hbWVzCm5hbWVzKGxvYWRfZGF0YSkKCiMgQ2hlY2sgZGF0YSB0eXBlcwpnbGltcHNlKGxvYWRfZGF0YSkKCiMgQ2hlY2sgZm9yIG1pc3NpbmcgdmFsdWVzCmNvbFN1bXMoaXMubmEobG9hZF9kYXRhKSkKCiMgQ2hlY2sgZm9yIGR1cGxpY2F0ZSB0aW1lc3RhbXBzCnN1bShkdXBsaWNhdGVkKGxvYWRfZGF0YSRkYXRlX3RpbWUpKQoKIyBDaGVjayBkYXRlIHJhbmdlCnJhbmdlKGxvYWRfZGF0YSRkYXRlX3RpbWUpCgojIFdlIG5lZWQgdG8gZHJvcCBmb3IgTm92ZW1iZXIgMTMsIHNpbmNlIHdlIG9ubHkgaGF2ZSBwYXJ0aWFsIGRheSBkYXRhLiAKCiMgQ291bnQgbWlzc2luZyBvYnNlcnZhdGlvbnMgYnkgZGF0ZQpsb2FkX2RhdGEgJT4lCiAgZ3JvdXBfYnkoZGF0ZSA9IGFzLkRhdGUoZGF0ZV90aW1lKSkgJT4lCiAgc3VtbWFyaXNlKAogICAgdG90YWxfb2JzZXJ2YXRpb25zID0gbigpLAogICAgbWlzc2luZ19sb2FkID0gc3VtKGlzLm5hKGxvYWQpKSwKICAgIHZhbGlkX2xvYWQgPSBzdW0oIWlzLm5hKGxvYWQpKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApCgojIERpc3BsYXkgdGltZXN0YW1wcyB3aXRoIG1pc3NpbmcgbWVhc3VyZW1lbnRzCmxvYWRfZGF0YSAlPiUKICBmaWx0ZXIoaXMubmEobG9hZCkpICU+JQogIHNlbGVjdChkYXRlX3RpbWUpCiAgIyBwcmludChuID0gMzApCgojIFJldGFpbiBvbmx5IGNvbXBsZXRlIGNhbGVuZGFyIGRheXMKY29tcGxldGVfZGF0YSA8LSBsb2FkX2RhdGEgJT4lCiAgbXV0YXRlKGRhdGUgPSBhcy5EYXRlKGRhdGVfdGltZSkpICU+JQogIGdyb3VwX2J5KGRhdGUpICU+JQogIGZpbHRlcigKICAgIG4oKSA9PSAyODgsCiAgICBhbGwoCiAgICAgIGlmX2FsbCgKICAgICAgICBjKGxvYWQsIHZlciwgaHlkcm8sIGZvc3NpbF9iaW9tYXNzLCBpbnRlcmNoYW5nZSwgbnVjbGVhciksCiAgICAgICAgfiAhaXMubmEoLngpCiAgICAgICkKICAgICkKICApICU+JQogIHVuZ3JvdXAoKQpgYGAKCmBgYHtyfQojIENhbGN1bGF0ZSBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzCmRlc2NyaXB0aXZlX3N0YXRzIDwtIGNvbXBsZXRlX2RhdGEgJT4lCiAgc3VtbWFyaXNlKAogICAgYWNyb3NzKAogICAgICBjKGxvYWQsIHZlciwgaHlkcm8sIGZvc3NpbF9iaW9tYXNzLCBpbnRlcmNoYW5nZSwgbnVjbGVhciksCiAgICAgIGxpc3QoCiAgICAgICAgTiA9IH5zdW0oIWlzLm5hKC54KSksCiAgICAgICAgTWVhbiA9IH5tZWFuKC54LCBuYS5ybSA9IFRSVUUpLAogICAgICAgIE1lZGlhbiA9IH5tZWRpYW4oLngsIG5hLnJtID0gVFJVRSksCiAgICAgICAgU0QgPSB+c2QoLngsIG5hLnJtID0gVFJVRSksCiAgICAgICAgTWluID0gfm1pbigueCwgbmEucm0gPSBUUlVFKSwKICAgICAgICBNYXggPSB+bWF4KC54LCBuYS5ybSA9IFRSVUUpCiAgICAgICkKICAgICkKICApICU+JQogIHBpdm90X2xvbmdlcigKICAgIGNvbHMgPSBldmVyeXRoaW5nKCksCiAgICBuYW1lc190byA9IGMoIlZhcmlhYmxlIiwgIi52YWx1ZSIpLAogICAgbmFtZXNfcGF0dGVybiA9ICIoLispXyhOfE1lYW58TWVkaWFufFNEfE1pbnxNYXgpIgogICkgJT4lCiAgbXV0YXRlKAogICAgVmFyaWFibGUgPSByZWNvZGUoCiAgICAgIFZhcmlhYmxlLAogICAgICBsb2FkID0gIkxvYWQiLAogICAgICB2ZXIgPSAiVmFyaWFibGUgRW5lcmd5IFJlc291cmNlcyIsCiAgICAgIGh5ZHJvID0gIkh5ZHJvIiwKICAgICAgZm9zc2lsX2Jpb21hc3MgPSAiRm9zc2lsL0Jpb21hc3MiLAogICAgICBpbnRlcmNoYW5nZSA9ICJOZXQgSW50ZXJjaGFuZ2UiLAogICAgICBudWNsZWFyID0gIk51Y2xlYXIiCiAgICApLAogICAgYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCB+cm91bmQoLngsIDIpKQogICkKCiMgRGlzcGxheSBmb3JtYXR0ZWQgdGFibGUKZGVzY3JpcHRpdmVfc3RhdHMgJT4lCiAga25pdHI6OmthYmxlKAogICAgY2FwdGlvbiA9ICJEZXNjcmlwdGl2ZSBTdGF0aXN0aWNzOiBCUEEgQmFsYW5jaW5nIEF1dGhvcml0eSIsCiAgICBjb2wubmFtZXMgPSBjKAogICAgICAiVmFyaWFibGUiLAogICAgICAiTiIsCiAgICAgICJNZWFuIChNVykiLAogICAgICAiTWVkaWFuIChNVykiLAogICAgICAiU3RkLiBEZXYuIChNVykiLAogICAgICAiTWluaW11bSAoTVcpIiwKICAgICAgIk1heGltdW0gKE1XKSIKICAgICksCiAgICBhbGlnbiA9IGMoImwiLCByZXAoInIiLCA2KSkKICApICU+JQogIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiksCiAgICBmdWxsX3dpZHRoID0gVFJVRSwKICAgIHBvc2l0aW9uID0gImNlbnRlciIKICApCmBgYAoKYGBge3J9CiMgU3VtbWFyaXplIGRhdGEgY29tcGxldGVuZXNzIGJ5IGRheQpkYWlseV9kYXRhX3F1YWxpdHkgPC0gbG9hZF9kYXRhICU+JQogIG11dGF0ZShkYXRlID0gYXMuRGF0ZShkYXRlX3RpbWUpKSAlPiUKICBncm91cF9ieShkYXRlKSAlPiUKICBzdW1tYXJpc2UoCiAgICBvYnNlcnZhdGlvbnMgPSBuKCksCiAgICBtaXNzaW5nID0gc3VtKGlzLm5hKGxvYWQpKSwKICAgIHZhbGlkID0gc3VtKCFpcy5uYShsb2FkKSksCiAgICBjb21wbGV0ZW5lc3MgPSByb3VuZCh2YWxpZCAvIDI4OCAqIDEwMCwgMSksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKQoKIyBEaXNwbGF5IGRhdGEtcXVhbGl0eSB0YWJsZQpkYWlseV9kYXRhX3F1YWxpdHkgJT4lCiAga25pdHI6OmthYmxlKAogICAgY2FwdGlvbiA9ICJEYWlseSBEYXRhIENvbXBsZXRlbmVzcyIsCiAgICBjb2wubmFtZXMgPSBjKAogICAgICAiRGF0ZSIsCiAgICAgICJPYnNlcnZhdGlvbnMiLAogICAgICAiTWlzc2luZyIsCiAgICAgICJWYWxpZCIsCiAgICAgICJDb21wbGV0ZW5lc3MgKCUpIgogICAgKSwKICAgIGFsaWduID0gYygibCIsICJyIiwgInIiLCAiciIsICJyIikKICApICU+JQogIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiksCiAgICBmdWxsX3dpZHRoID0gRkFMU0UsCiAgICBwb3NpdGlvbiA9ICJjZW50ZXIiCiAgKQpgYGAKCjwvZGV0YWlscz4KCjxkZXRhaWxzIGNsYXNzPSJzdWJ0b3BpYyI+Cgo8c3VtbWFyeT5EYWlseSBzdW1tYXJpZXM6IGxvYWQgYW5kIGdlbmVyYXRpb248L3N1bW1hcnk+CgpGb3IgdGhpcywgaSB3aWxsIGlzZSBgY29tcGxldGVfZGF0YWAgc28gd2UgYXJlIGNvbXBhcmluZyBmdWxsIGRheXMKcmF0aGVyIHRoYW4gYWNjaWRlbnRhbGx5IGluY2x1ZGluZyBpbmNvbXBsZXRlIGRheXMuIEZvciBlYWNoIGRheSwgd2UKd2lsbCBjYWxjdWxhdGU6CgotICAgQXZlcmFnZSBMb2FkOiB0eXBpY2FsIGJhbGFuY2luZyBhcmVhIGxvYWQgdGhyb3VnaG91dCB0aGUgZGF5Ci0gICBQZWFrIExvYWQ6IGhpZ2hlc3QgZml2ZS1taW51dGUgbG9hZCByZWFkaW5nCi0gICBNaW5pbXVtIGxvYWQ6IGxvd2VzdCBmaXZlLW1pbnV0ZSBsb2FkIHJlYWRpbmcKLSAgIEF2ZXJhZ2UgZ2VuZXJhdGlvbiBieSByZXNvdXJjZQotICAgQXZlcmFnZSBpbnRlcmNoYW5nZQoKYGBge3J9CiMgQ2FsY3VsYXRlIGRhaWx5IGxvYWQgYW5kIGdlbmVyYXRpb24gc3VtbWFyaWVzCmRhaWx5X3N1bW1hcnkgPC0gY29tcGxldGVfZGF0YSAlPiUKICBncm91cF9ieShkYXRlKSAlPiUKICBzdW1tYXJpc2UoCiAgICAjIERhdGUKICAgIGRheV9vZl93ZWVrID0gd2RheShmaXJzdChkYXRlKSwgbGFiZWwgPSBUUlVFLCBhYmJyID0gRkFMU0UpLCAjZXh0cmFjdHMgZGF5IG9mIHdlZWssIHJldHVybmluZyB0ZWggZmlyc3QgdmFsdWUgaW4gdGhlICJkYXRlIiBncm91cAoKICAgICMgTG9hZAogICAgb2JzZXJ2YXRpb25zID0gbigpLAogICAgYXZnX2xvYWQgPSBtZWFuKGxvYWQpLAogICAgcGVha19sb2FkID0gbWF4KGxvYWQpLAogICAgcGVha190aW1lID0gZm9ybWF0KGRhdGVfdGltZVt3aGljaC5tYXgobG9hZCldLCAiJUg6JU0iKSwKICAgIG1pbl9sb2FkID0gbWluKGxvYWQpLAogICAgbWluX3RpbWUgPSBmb3JtYXQoZGF0ZV90aW1lW3doaWNoLm1pbihsb2FkKV0sICIlSDolTSIpLAoKICAgICMgR2VuZXJhdGlvbgogICAgYXZnX3ZlciA9IG1lYW4odmVyKSwKICAgIGF2Z19oeWRybyA9IG1lYW4oaHlkcm8pLAogICAgYXZnX2Zvc3NpbF9iaW9tYXNzID0gbWVhbihmb3NzaWxfYmlvbWFzcyksCiAgICBhdmdfbnVjbGVhciA9IG1lYW4obnVjbGVhciksCgogICAgIyBJbnRlcmNoYW5nZQogICAgYXZnX2ludGVyY2hhbmdlID0gbWVhbihpbnRlcmNoYW5nZSksCgogICAgLmdyb3VwcyA9ICJkcm9wIgogICkKCiMgRGlzcGxheSBkYWlseSBzdW1tYXJ5IHRhYmxlCmRhaWx5X3N1bW1hcnkgJT4lCiAga25pdHI6OmthYmxlKAogICAgY2FwdGlvbiA9ICJEYWlseSBMb2FkIGFuZCBHZW5lcmF0aW9uIFN1bW1hcnkiLAogICAgY29sLm5hbWVzID0gYygKICAgICAgIkRhdGUiLAogICAgICAiRGF5IiwKICAgICAgIk9ic2VydmF0aW9ucyIsCiAgICAgICJBdmVyYWdlIExvYWQgKE1XKSIsCiAgICAgICJQZWFrIExvYWQgKE1XKSIsCiAgICAgICJQZWFrIFRpbWUiLAogICAgICAiTWluaW11bSBMb2FkIChNVykiLAogICAgICAiTWluaW11bSBUaW1lIiwKICAgICAgIkF2ZXJhZ2UgVkVSIChNVykiLAogICAgICAiQXZlcmFnZSBIeWRybyAoTVcpIiwKICAgICAgIkF2ZXJhZ2UgRm9zc2lsL0Jpb21hc3MgKE1XKSIsCiAgICAgICJBdmVyYWdlIE51Y2xlYXIgKE1XKSIsCiAgICAgICJBdmVyYWdlIEludGVyY2hhbmdlIChNVykiCiAgICApLAogICAgYWxpZ24gPSBjKCJsIiwgImwiLCByZXAoInIiLCAxMSkpLAogICAgZGlnaXRzID0gMQogICkgJT4lCiAga2FibGVFeHRyYTo6a2FibGVfc3R5bGluZygKICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiKSwKICAgIGZ1bGxfd2lkdGggPSBUUlVFLAogICAgcG9zaXRpb24gPSAiY2VudGVyIgogICkKYGBgCgpUaGUgYWJvdmUgdGFibGUgc2hvd3MgYWxsIG9mIHRoZSBjb21wbGV0ZSBkYXlzIGluIHRoZSBkYXRhc2V0IChOb3ZlbWJlciA4IC0gTm92ZW1iZXIgMTIpLiBUaGUgbG93ZXN0IGxvYWQgb2NjdXJyZWQgb24gTm92ZW1iZXIgOCwgd2hpY2ggaXMgcyBTdW5kYXkuIFRoaXMgdHJhY2tzLCBhcyBpbmR1c3RyeSBpcyBub3QgYXMgYWN0aXZlIG9uIHdlZWtlbmRzLiBXZSBzZWUgYSBoaWdoZXIgaW50ZXJjaGFuZ2Ugb24gdGhpcyBkYXkgYXMgd2VsbC4gVGhlIG1pbmltdW0gbG9hZCB0aW1lIGlzIHJlbGF0aXZlbHkgY29uc2lzdGVudCBhY3Jvc3MgZWFjaCBkYXksIHJhbmdpbmcgZnJvbSAyYW0gLSAzOjMwYW0uIEFnYWluLCB0aGlzIG1ha2VzIHNlbnNlLCBhcyB0aGUgdmFzdCBtYWpvcml0eSBvZiBwZW9wbGUgYXJlIGFzbGVlcCBhdCB0aGlzIHRpbWUuIAoKV2Ugc2VlIHRoZSBwZWFrIGxvYWQgdGltZXMgdmFyeSwgaG93ZXZlci4gVHdvIGRheXMgKHRoZSA5dGggYW5kIHRoZSAxMnRoKSBzZWUgcGVhayB0aW1lcyBpbiB0aGUgbW9ybmluZywgYXQgNzo0MGFtIGFuZCA4OjEwYW0gcmVzcGVjdGl2ZWx5LiBUaGUgb3RoZXIgZGF5cyBoYXZlIGFsbW9zdCByZW1hcmthYmxlIGNvbnNpc3RlbmN5LCB3aXRoIHBlYWsgbG9hZCBvY2N1cnJpbmcgaW4gdGhlIGZpdmUgbWludXRlcyBiZXR3ZWVuIDU6MzBwbSBhbmQgNTozNXBtLiAKClRoZSBhdmVyYWdlIFZFUiBpcyBzaWduaWZpY2FudGx5IGhpZ2hlciBvbiBTdW5kYXkuIFN1bmRheSB3YXMgc3VubmllciB0aGFuIG90aGVyIGRheXMsIHNvIHRoaXMgbWF5IGluZGljYXRlIGEgaGlnaGVyIGFjY2VzcyB0byBzb2xhciBwb3dlci4gCgo8L2RldGFpbHM+Cgo8ZGV0YWlscyBjbGFzcz0ic3VidG9waWMiPgoKPHN1bW1hcnk+RGFpbHkgZW5lcmd5IGNhbGN1bGF0aW9uczogTVcgdG8gTVdoPC9zdW1tYXJ5PgoKKipSZW1lbWJlcjoqKiBNVyAobWVnYXdhdHRzKSBhcmUgYSBtZWFzdXJlIG9mIHBvd2VyICphdCBhIHBvaW50IGluIHRpbWUqLiBNV2ggKG1lZ2F3YXR0LWhvdXJzKSBhcmUgYSBtZWFzdXJlIG9mIGVuZXJneSAqb3ZlciBhIHBlcmlvZCBvZiB0aW1lKi4gVGhlIGRhdGEgY29udGFpbnMgZml2ZS1taW51dGUgTVcgb2JzZXJ2YXRpb25zLiAKCldlIGNhbiBlc3RpbWF0ZSBlbmVyZ3kgYnkgbXVsdGlwbHlpbmcgZWFjaCBvYnNlcnZhdGlvbiBieSB0aGUgbGVuZ3RoIG9mIGl0cyBpbnRlcnZhbDogJCQgTVdoID0gTVcgXHRpbWVzIFxmcmFjezV9ezYwfSQkCgpXZSBjYW4gbm93IGNhbGN1bGF0ZSBkYWlseSBlbmVyZ3kgdXNpbmcgYGNvbXBsZXRlX2RhdGFgIGFnYWluLiAKCmBgYHtyfQojIENhbGN1bGF0ZSBkYWlseSBlbmVyZ3kgY29uc3VtcHRpb24gYW5kIGdlbmVyYXRpb24KZGFpbHlfZW5lcmd5IDwtIGNvbXBsZXRlX2RhdGEgJT4lCiAgZ3JvdXBfYnkoZGF0ZSkgJT4lCiAgc3VtbWFyaXNlKAogICAgIyBFbmVyZ3kgY29uc3VtcHRpb24KICAgIGxvYWRfbXdoID0gc3VtKGxvYWQgKiAoNSAvIDYwKSksCgogICAgIyBFbmVyZ3kgZ2VuZXJhdGlvbiBieSByZXNvdXJjZQogICAgdmVyX213aCA9IHN1bSh2ZXIgKiAoNSAvIDYwKSksCiAgICBoeWRyb19td2ggPSBzdW0oaHlkcm8gKiAoNSAvIDYwKSksCiAgICBmb3NzaWxfYmlvbWFzc19td2ggPSBzdW0oZm9zc2lsX2Jpb21hc3MgKiAoNSAvIDYwKSksCiAgICBudWNsZWFyX213aCA9IHN1bShudWNsZWFyICogKDUgLyA2MCkpLAoKICAgICMgVG90YWwgcmVwb3J0ZWQgZ2VuZXJhdGlvbgogICAgdG90YWxfZ2VuZXJhdGlvbl9td2ggPSBzdW0oCiAgICAgICh2ZXIgKyBoeWRybyArIGZvc3NpbF9iaW9tYXNzICsgbnVjbGVhcikgKiAoNSAvIDYwKQogICAgKSwKCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKQoKIyBEaXNwbGF5IGRhaWx5IGVuZXJneSB0YWJsZQpkYWlseV9lbmVyZ3kgJT4lCiAga25pdHI6OmthYmxlKAogICAgY2FwdGlvbiA9ICJEYWlseSBFbmVyZ3kgQ29uc3VtcHRpb24gYW5kIEdlbmVyYXRpb24iLAogICAgY29sLm5hbWVzID0gYygKICAgICAgIkRhdGUiLAogICAgICAiTG9hZCAoTVdoKSIsCiAgICAgICJWRVIgKE1XaCkiLAogICAgICAiSHlkcm8gKE1XaCkiLAogICAgICAiRm9zc2lsL0Jpb21hc3MgKE1XaCkiLAogICAgICAiTnVjbGVhciAoTVdoKSIsCiAgICAgICJUb3RhbCBHZW5lcmF0aW9uIChNV2gpIgogICAgKSwKICAgIGFsaWduID0gYygibCIsIHJlcCgiciIsIDYpKSwKICAgIGRpZ2l0cyA9IDEKICApICU+JQogIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiksCiAgICBmdWxsX3dpZHRoID0gVFJVRSwKICAgIHBvc2l0aW9uID0gImNlbnRlciIKICApCmBgYAoKPC9kZXRhaWxzPgoKPGRldGFpbHMgY2xhc3M9InN1YnRvcGljIj4KCjxzdW1tYXJ5PkhvdXJseSBsb2FkIHByb2ZpbGVzPC9zdW1tYXJ5PgoKRmlyc3Qgd2UgbmVlZCB0byBjYWxjdWxhdGUgdGhlIGF2ZXJhZ2UgbG9hZCBieSBob3VyLiBFYWNoIGhvdXIgY29udGFpbnMgMTIgNS1taW51dGUgb2JzZXJ2YXRpb25zLiBXZSB3aWxsIGdyb3VwIGFsbCBmaXZlIGRheXMgdG9nZXRoZXIgYnkgaG91ciwgdGhlbiBjYWxjdWxhdGUgdGhlIGF2ZXJhZ2UgbG9hZCBhY3Jvc3MgYWxsIG9ic2VydmF0aW9ucyByZWNvcmRlZCBkdXJpbmcgdGhhdCBob3VyLiAKCmBgYHtyfQojIENhbGN1bGF0ZSBhdmVyYWdlIGxvYWQgYnkgaG91ciBvZiBkYXkKaG91cmx5X2xvYWQgPC0gY29tcGxldGVfZGF0YSAlPiUKICBtdXRhdGUoCiAgICBob3VyID0gbHVicmlkYXRlOjpob3VyKGRhdGVfdGltZSkKICApICU+JQogIGdyb3VwX2J5KGhvdXIpICU+JQogIHN1bW1hcmlzZSgKICAgIGF2Z19sb2FkID0gbWVhbihsb2FkKSwKICAgIG1pbl9sb2FkID0gbWluKGxvYWQpLAogICAgbWF4X2xvYWQgPSBtYXgobG9hZCksCiAgICBzZF9sb2FkID0gc2QobG9hZCksCiAgICBvYnNlcnZhdGlvbnMgPSBuKCksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKQoKIyBEaXNwbGF5IGhvdXJseSBsb2FkIHByb2ZpbGUKaG91cmx5X2xvYWQgJT4lCiAga25pdHI6OmthYmxlKAogICAgY2FwdGlvbiA9ICJBdmVyYWdlIEhvdXJseSBCUEEgQmFsYW5jaW5nIEF1dGhvcml0eSBMb2FkIiwKICAgIGNvbC5uYW1lcyA9IGMoCiAgICAgICJIb3VyIiwKICAgICAgIkF2ZXJhZ2UgTG9hZCAoTVcpIiwKICAgICAgIk1pbmltdW0gTG9hZCAoTVcpIiwKICAgICAgIk1heGltdW0gTG9hZCAoTVcpIiwKICAgICAgIlN0ZC4gRGV2LiAoTVcpIiwKICAgICAgIk9ic2VydmF0aW9ucyIKICAgICksCiAgICBhbGlnbiA9IGMoImwiLCByZXAoInIiLCA1KSksCiAgICBkaWdpdHMgPSAxCiAgKSAlPiUKICBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKAogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpLAogICAgZnVsbF93aWR0aCA9IFRSVUUsCiAgICBwb3NpdGlvbiA9ICJjZW50ZXIiCiAgKQoKIyBQbG90IGF2ZXJhZ2UgaG91cmx5IGxvYWQKZ2dwbG90KGhvdXJseV9sb2FkLCBhZXMoeCA9IGhvdXIsIHkgPSBhdmdfbG9hZCkpICsKICBnZW9tX2xpbmUobGluZXdpZHRoID0gMSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoCiAgICBicmVha3MgPSAwOjIzLAogICAgbGFiZWxzID0gc3ByaW50ZigiJTAyZDowMCIsIDA6MjMpCiAgKSArCiAgbGFicygKICAgIHRpdGxlID0gIkF2ZXJhZ2UgSG91cmx5IEJQQSBCYWxhbmNpbmcgQXV0aG9yaXR5IExvYWQiLAogICAgc3VidGl0bGUgPSAiQXZlcmFnZSBmaXZlLW1pbnV0ZSBsb2FkIG9ic2VydmF0aW9ucyBhY3Jvc3MgTm92ZW1iZXIgOOKAkzEyLCAyMDI2IiwKICAgIHggPSAiSG91ciBvZiBEYXkiLAogICAgeSA9ICJBdmVyYWdlIExvYWQgKE1XKSIKICApICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIikKICApCmBgYAoKVGhpcyBpcyB0aGUgcG9vbGVkIGF2ZXJhZ2UsIGJ1dCBpdCBoaWRlcyBkaWZmZXJlbmNlcyBiZXR3ZWVuIGluZGl2aWR1YWwgZGF5cy4gCgpgYGB7cn0KIyBDYWxjdWxhdGUgYXZlcmFnZSBob3VybHkgbG9hZCBieSBkYXkKZGFpbHlfaG91cmx5X2xvYWQgPC0gY29tcGxldGVfZGF0YSAlPiUKICBtdXRhdGUoCiAgICBob3VyID0gbHVicmlkYXRlOjpob3VyKGRhdGVfdGltZSkKICApICU+JQogIGdyb3VwX2J5KGRhdGUsIGhvdXIpICU+JQogIHN1bW1hcmlzZSgKICAgIGF2Z19sb2FkID0gbWVhbihsb2FkKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApICU+JQogIG11dGF0ZSgKICAgIGRheV9vZl93ZWVrID0gbHVicmlkYXRlOjp3ZGF5KAogICAgICBkYXRlLAogICAgICBsYWJlbCA9IFRSVUUsCiAgICAgIGFiYnIgPSBGQUxTRQogICAgKQogICkKCiMgQ29tcGFyZSBob3VybHkgbG9hZCBwcm9maWxlcyBhY3Jvc3MgZGF5cwpnZ3Bsb3QoCiAgZGFpbHlfaG91cmx5X2xvYWQsCiAgYWVzKAogICAgeCA9IGhvdXIsCiAgICB5ID0gYXZnX2xvYWQsCiAgICBncm91cCA9IGRhdGUsCiAgICBjb2xvciA9IGRheV9vZl93ZWVrCiAgKQopICsKICBnZW9tX2xpbmUobGluZXdpZHRoID0gMSkgKwogIHNjYWxlX3hfY29udGludW91cygKICAgIGJyZWFrcyA9IDA6MjMsCiAgICBsYWJlbHMgPSBzcHJpbnRmKCIlMDJkOjAwIiwgMDoyMykKICApICsKICBzY2FsZV9jb2xvcl9tYW51YWwoCiAgICB2YWx1ZXMgPSBjKAogICAgICAiU3VuZGF5IiA9ICIjRUQ2QjVBIiwKICAgICAgIk1vbmRheSIgPSAiI0U4ODUwNCIsCiAgICAgICJUdWVzZGF5IiA9ICIjRERCQjI1IiwKICAgICAgIldlZG5lc2RheSIgPSAiIzZFQUQ0NyIsCiAgICAgICJUaHVyc2RheSIgPSAiIzQ5QTNBOSIsCiAgICAgICJGcmlkYXkiID0gIiNBRTg1Q0UiLAogICAgICAiU2F0dXJkYXkiID0gIiNFODg0Q0QiCiAgICApCiAgKSArCiAgbGFicygKICAgIHRpdGxlID0gIkhvdXJseSBCUEEgTG9hZCBQcm9maWxlcyBieSBEYXkiLAogICAgeCA9ICJIb3VyIG9mIERheSIsCiAgICB5ID0gIkF2ZXJhZ2UgTG9hZCAoTVcpIiwKICAgIGNvbG9yID0gIkRheSIKICApICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIikKICApCmBgYAoKYGBge3J9CiMgUmVzaGFwZSBkYXRhIGZvciB0aW1lLXNlcmllcyB2aXN1YWxpemF0aW9uCmxvYWRfbG9uZyA8LSBsb2FkX2RhdGEgJT4lCiAgcGl2b3RfbG9uZ2VyKAogICAgY29scyA9IGMobG9hZCwgdmVyLCBoeWRybywgZm9zc2lsX2Jpb21hc3MsIG51Y2xlYXIsIGludGVyY2hhbmdlKSwKICAgIG5hbWVzX3RvID0gInJlc291cmNlIiwKICAgIHZhbHVlc190byA9ICJtdyIKICApICU+JQogIG11dGF0ZSgKICAgIHJlc291cmNlID0gcmVjb2RlKAogICAgICByZXNvdXJjZSwKICAgICAgbG9hZCA9ICJMb2FkIiwKICAgICAgdmVyID0gIlZFUiIsCiAgICAgIGh5ZHJvID0gIkh5ZHJvIiwKICAgICAgZm9zc2lsX2Jpb21hc3MgPSAiRm9zc2lsL0Jpb21hc3MiLAogICAgICBudWNsZWFyID0gIk51Y2xlYXIiLAogICAgICBpbnRlcmNoYW5nZSA9ICJOZXQgSW50ZXJjaGFuZ2UiCiAgICApCiAgKQoKIyBQbG90IGZpdmUtbWludXRlIEJQQSBsb2FkIGFuZCBnZW5lcmF0aW9uIGRhdGEKZ2dwbG90KAogIGxvYWRfbG9uZywKICBhZXMoCiAgICB4ID0gZGF0ZV90aW1lLAogICAgeSA9IG13LAogICAgY29sb3IgPSByZXNvdXJjZSwKICAgIGdyb3VwID0gcmVzb3VyY2UKICApCikgKwogIGdlb21fbGluZShsaW5ld2lkdGggPSAwLjQsIG5hLnJtID0gVFJVRSkgKwoKICBzY2FsZV9jb2xvcl9tYW51YWwoCiAgICB2YWx1ZXMgPSBjKAogICAgICAiTG9hZCIgPSAiI0U0MUExQyIsCiAgICAgICJWRVIiID0gIiMyMjhCMjIiLAogICAgICAiSHlkcm8iID0gIiMyODc4QjUiLAogICAgICAiRm9zc2lsL0Jpb21hc3MiID0gIiM4QjZGNDciLAogICAgICAiTnVjbGVhciIgPSAiIzdCMzI5NCIsCiAgICAgICJOZXQgSW50ZXJjaGFuZ2UiID0gIiM3Nzc3NzciCiAgICApCiAgKSArCgogIHNjYWxlX3hfZGF0ZXRpbWUoCiAgICBkYXRlX2JyZWFrcyA9ICIxIGRheSIsCiAgICBkYXRlX2xhYmVscyA9ICIlYiAlZCIsCiAgICB0aW1lem9uZSA9ICJBbWVyaWNhL0xvc19BbmdlbGVzIiwKICAgIGxpbWl0cyA9IHJhbmdlKGxvYWRfZGF0YSRkYXRlX3RpbWUpCiAgKSArCgogIGxhYnMoCiAgICB0aXRsZSA9ICJCUEEgQmFsYW5jaW5nIEF1dGhvcml0eSBMb2FkIGFuZCBHZW5lcmF0aW9uIiwKICAgIHN1YnRpdGxlID0gIkZpdmUtbWludXRlIG9ic2VydmF0aW9ucyIsCiAgICB4ID0gIkRhdGUvVGltZSIsCiAgICB5ID0gIk1XIiwKICAgIGNvbG9yID0gTlVMTAogICkgKwoKICB0aGVtZV9taW5pbWFsKCkgKwoKICB0aGVtZSgKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiksCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpCiAgKQpgYGAKYGBge3J9CiMgRGVmaW5lIHBsb3QgYm91bmRhcmllcwpwbG90X3N0YXJ0IDwtIG1pbihsb2FkX2RhdGEkZGF0ZV90aW1lLCBuYS5ybSA9IFRSVUUpCnBsb3RfZW5kIDwtIG1heChsb2FkX2RhdGEkZGF0ZV90aW1lLCBuYS5ybSA9IFRSVUUpCgojIFNldCB2ZXJ0aWNhbCBwb3NpdGlvbiBmb3Igd2Vla2VuZCBsYWJlbHMKbGFiZWxfeSA8LSBtYXgobG9hZF9kYXRhJGxvYWQsIG5hLnJtID0gVFJVRSkgKiAxLjA4CgojIElkZW50aWZ5IHdlZWtlbmQgZGF5cwp3ZWVrZW5kX2RheXMgPC0gdGliYmxlKAogIGRhdGUgPSBzZXEoCiAgICBhcy5EYXRlKHBsb3Rfc3RhcnQsIHR6ID0gIkFtZXJpY2EvTG9zX0FuZ2VsZXMiKSwKICAgIGFzLkRhdGUocGxvdF9lbmQsIHR6ID0gIkFtZXJpY2EvTG9zX0FuZ2VsZXMiKSwKICAgIGJ5ID0gImRheSIKICApCikgJT4lCiAgZmlsdGVyKGx1YnJpZGF0ZTo6d2RheShkYXRlKSAlaW4lIGMoMSwgNykpICU+JQogIG11dGF0ZSgKICAgICMgRGVmaW5lIG1pZG5pZ2h0IGJvdW5kYXJpZXMgaW4gUGFjaWZpYyB0aW1lCiAgICB4bWluID0gYXMuUE9TSVhjdCgKICAgICAgcGFzdGUoZGF0ZSwgIjAwOjAwOjAwIiksCiAgICAgIHR6ID0gIkFtZXJpY2EvTG9zX0FuZ2VsZXMiCiAgICApLAogICAgeG1heCA9IGFzLlBPU0lYY3QoCiAgICAgIHBhc3RlKGRhdGUgKyAxLCAiMDA6MDA6MDAiKSwKICAgICAgdHogPSAiQW1lcmljYS9Mb3NfQW5nZWxlcyIKICAgICksCgogICAgIyBSZXN0cmljdCBzaGFkaW5nIHRvIHRoZSB2aXNpYmxlIHBsb3R0aW5nIHdpbmRvdwogICAgdmlzaWJsZV9zdGFydCA9IHBtYXgoeG1pbiwgcGxvdF9zdGFydCksCiAgICB2aXNpYmxlX2VuZCA9IHBtaW4oeG1heCwgcGxvdF9lbmQpLAoKICAgICMgQ2VudGVyIGxhYmVscyB3aXRoaW4gdGhlIHZpc2libGUgc2hhZGVkIHJlZ2lvbgogICAgbGFiZWxfeCA9IHZpc2libGVfc3RhcnQgKwogICAgICAodmlzaWJsZV9lbmQgLSB2aXNpYmxlX3N0YXJ0KSAvIDIsCgogICAgbGFiZWwgPSB3ZWVrZGF5cyhkYXRlKQogICkKCiMgQ2hlY2sgd2Vla2VuZCBkYXRlcyBhbmQgc2hhZGluZyBib3VuZGFyaWVzCndlZWtlbmRfZGF5cyAlPiUKICBzZWxlY3QoZGF0ZSwgeG1pbiwgeG1heCwgdmlzaWJsZV9zdGFydCwgdmlzaWJsZV9lbmQsIGxhYmVsX3gsIGxhYmVsKQoKIyBQbG90IGZpdmUtbWludXRlIEJQQSBsb2FkIGFuZCBnZW5lcmF0aW9uIGRhdGEKZ2dwbG90KCkgKwoKICAjIFdlZWtlbmQgYmFja2dyb3VuZCBzaGFkaW5nCiAgZ2VvbV9yZWN0KAogICAgZGF0YSA9IHdlZWtlbmRfZGF5cywKICAgIGFlcygKICAgICAgeG1pbiA9IHZpc2libGVfc3RhcnQsCiAgICAgIHhtYXggPSB2aXNpYmxlX2VuZCwKICAgICAgeW1pbiA9IC1JbmYsCiAgICAgIHltYXggPSBJbmYKICAgICksCiAgICBmaWxsID0gIiNEOUQ5RDkiLAogICAgYWxwaGEgPSAwLjQ1LAogICAgaW5oZXJpdC5hZXMgPSBGQUxTRQogICkgKwoKICAjIEZpdmUtbWludXRlIGxvYWQgYW5kIGdlbmVyYXRpb24gb2JzZXJ2YXRpb25zCiAgZ2VvbV9saW5lKAogICAgZGF0YSA9IGxvYWRfbG9uZywKICAgIGFlcygKICAgICAgeCA9IGRhdGVfdGltZSwKICAgICAgeSA9IG13LAogICAgICBjb2xvciA9IHJlc291cmNlLAogICAgICBncm91cCA9IHJlc291cmNlCiAgICApLAogICAgbGluZXdpZHRoID0gMC40LAogICAgbmEucm0gPSBUUlVFCiAgKSArCgogICMgV2Vla2VuZCBsYWJlbHMKICBnZW9tX3RleHQoCiAgICBkYXRhID0gd2Vla2VuZF9kYXlzLAogICAgYWVzKAogICAgICB4ID0gbGFiZWxfeCwKICAgICAgeSA9IGxhYmVsX3ksCiAgICAgIGxhYmVsID0gbGFiZWwKICAgICksCiAgICBpbmhlcml0LmFlcyA9IEZBTFNFLAogICAgZm9udGZhY2UgPSAiYm9sZCIsCiAgICBzaXplID0gNAogICkgKwoKICAjIFJlc291cmNlIGNvbG9ycwogIHNjYWxlX2NvbG9yX21hbnVhbCgKICAgIGJyZWFrcyA9IGMoCiAgICAgICJMb2FkIiwKICAgICAgIlZFUiIsCiAgICAgICJIeWRybyIsCiAgICAgICJGb3NzaWwvQmlvbWFzcyIsCiAgICAgICJOdWNsZWFyIiwKICAgICAgIk5ldCBJbnRlcmNoYW5nZSIKICAgICksCiAgICB2YWx1ZXMgPSBjKAogICAgICAiTG9hZCIgPSAiI0U0MUExQyIsCiAgICAgICJWRVIiID0gIiMyMjhCMjIiLAogICAgICAiSHlkcm8iID0gIiMyODc4QjUiLAogICAgICAiRm9zc2lsL0Jpb21hc3MiID0gIiM4QjZGNDciLAogICAgICAiTnVjbGVhciIgPSAiIzdCMzI5NCIsCiAgICAgICJOZXQgSW50ZXJjaGFuZ2UiID0gIiM3Nzc3NzciCiAgICApCiAgKSArCgogICMgRGF0ZSBheGlzCiAgc2NhbGVfeF9kYXRldGltZSgKICAgIGRhdGVfYnJlYWtzID0gIjEgZGF5IiwKICAgIGRhdGVfbGFiZWxzID0gIiVhXG4lYiAlZCIsCiAgICB0aW1lem9uZSA9ICJBbWVyaWNhL0xvc19BbmdlbGVzIiwKICAgIGxpbWl0cyA9IGMocGxvdF9zdGFydCwgcGxvdF9lbmQpLAogICAgZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSAwKQogICkgKwoKICAjIFktYXhpcwogIHNjYWxlX3lfY29udGludW91cygKICAgIGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLjAyLCAwLjEyKSkKICApICsKCiAgbGFicygKICAgIHRpdGxlID0gIkJQQSBCYWxhbmNpbmcgQXV0aG9yaXR5IExvYWQgYW5kIEdlbmVyYXRpb24iLAogICAgc3VidGl0bGUgPSAiRml2ZS1taW51dGUgb2JzZXJ2YXRpb25zIHwgTm92ZW1iZXIgN+KAkzE0LCAyMDI2IiwKICAgIHggPSAiRGF0ZS9UaW1lIChQYWNpZmljKSIsCiAgICB5ID0gIlBvd2VyIChNVykiLAogICAgY29sb3IgPSBOVUxMLAogICAgY2FwdGlvbiA9ICJTb3VyY2U6IEJQQSBCYWxhbmNpbmcgQXV0aG9yaXR5IExvYWQgJiBUb3RhbCBWRVIuIEZpdmUtbWludXRlIFNDQURBIG9ic2VydmF0aW9ucy4iCiAgKSArCgogIHRoZW1lX21pbmltYWwoKSArCgogIHRoZW1lKAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTYpLAogICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEpLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgbGVnZW5kLmJveCA9ICJob3Jpem9udGFsIiwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2xpbmUoCiAgICAgIGNvbG9yID0gImdyZXk4MCIsCiAgICAgIGxpbmV3aWR0aCA9IDAuNQogICAgKSwKICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dCgKICAgICAgY29sb3IgPSAiZ3JleTQwIiwKICAgICAgc2l6ZSA9IDgKICAgICkKICApCmBgYAoKIyMgRm9yZWNhc3RpbmcgCgpJbiBhIHR5cGljYWwgcmVncmVzc2lvbiB3ZSBtYXkgZXN0aW1hdGUgJCRMb2FkX3QgPSBcYmV0YV8wICsgVGVtcGVyYXR1cmVfdCArIFxlcHNpbG9uX3QkJCB0byBpbnZlc3RpZ2F0ZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gbG9hZCBhbmQgdGVtcGVyYXR1cmUuIAoKSW4gZm9yZWNhc3RpbmcsIHRoZSBvYmplY3RpdmUgd291bGQgYmUgdG8gcHJlZGljdCBhIGZ1dHVyZSBvYnNlcnZhdGlvbiBhcyBhIGZ1bmN0aW9uIG9mIHBhc3Qgb2JzZXJ2YXRpb25zOiAkJFx3aWRlaGF0e0xvYWRfe3QrMX19ID0gZihMb2FkX3QsIExvYWRfe3QtMX0sIExvYWRfe3QtMjR9LCAuLi4pJCQKCkluIG9yZGVyIHRvIHByYWN0aWNlLCBJIHdpbGwgYmUgdXNpbmcgMjAyNSBkYXRhIGFzIG15IHRyYWluaW5nIGRhdGEgZm9yIGZvcmVjYXN0aW5nLCBhbmQgdGhlIDIwMjYgZGF0YSBhcyB0ZXN0aW5nIGRhdGEuIERhdGEgaXMgc291cmNlZCBmcm9tIGJ1bGxldCA1IG9mIGh0dHBzOi8vdHJhbnNtaXNzaW9uLmJwYS5nb3YvQnVzaW5lc3MvT3BlcmF0aW9ucy9XaW5kLy4gCgpgYGB7cn0KIyBJbXBvcnQgMjAyNSBCUEEgZGF0YQpkYXRhXzI1IDwtIHJlYWRfZXhjZWwoCiAgIn4vRG93bmxvYWRzL1dpbmRHZW5Ub3RhbExvYWRZVERfMjAyNS54bHN4IiwKICBza2lwID0gMQopICU+JQogIGNsZWFuX25hbWVzKCkKCiMgSW1wb3J0IDIwMjYgQlBBIGRhdGEKZGF0YV8yNiA8LSByZWFkX2V4Y2VsKAogICJ+L0Rvd25sb2Fkcy9XaW5kR2VuVG90YWxMb2FkWVREXzIwMjYueGxzeCIsCiAgc2tpcCA9IDEKKSAlPiUKICBjbGVhbl9uYW1lcygpCgojIENvbXBhcmUgbWlzc2luZyB2YWx1ZXMgYnkgY29sdW1uCm1pc3NpbmdfY29tcGFyaXNvbiA8LSB0aWJibGUoCiAgdmFyaWFibGUgPSBuYW1lcyhkYXRhXzI1KSwKICBtaXNzaW5nXzIwMjUgPSBjb2xTdW1zKGlzLm5hKGRhdGFfMjUpKSwKICBtaXNzaW5nXzIwMjYgPSBjb2xTdW1zKGlzLm5hKGRhdGFfMjYpKQopCgojIERpc3BsYXkgbWlzc2luZy12YWx1ZSBjb21wYXJpc29uCm1pc3NpbmdfY29tcGFyaXNvbiAlPiUKICBrbml0cjo6a2FibGUoCiAgICBjYXB0aW9uID0gIk1pc3NpbmcgVmFsdWVzOiAyMDI1IHZzLiAyMDI2IiwKICAgIGNvbC5uYW1lcyA9IGMoIlZhcmlhYmxlIiwgIjIwMjUgTWlzc2luZyIsICIyMDI2IE1pc3NpbmciKQogICkKCiMgQ29tcGFyZSB0b3RhbCBvYnNlcnZhdGlvbnMKbnJvdyhkYXRhXzI1KQpucm93KGRhdGFfMjYpCmBgYAoKPC9kZXRhaWxzPgoKPGRldGFpbHMgY2xhc3M9InRvcGljIj4KCjxzdW1tYXJ5PkNvbWJpbmluZyBhbmQgaW5zcGVjdGluZyB0aGUgcmF3IGRhdGE8L3N1bW1hcnk+CgpgYGB7cn0KIyBDb21iaW5lIDIwMjUgYW5kIDIwMjYgZGF0YXNldHMKYnBhX3JhdyA8LSBiaW5kX3Jvd3MoCiAgZGF0YV8yNSAlPiUgbXV0YXRlKHNvdXJjZV95ZWFyID0gMjAyNSksCiAgZGF0YV8yNiAlPiUgbXV0YXRlKHNvdXJjZV95ZWFyID0gMjAyNikKKSAlPiUKICBhcnJhbmdlKGRhdGVfdGltZSkKCgojIENvdW50IGR1cGxpY2F0ZWQgdGltZXN0YW1wcwpzdW0oZHVwbGljYXRlZChicGFfcmF3JGRhdGVfdGltZSkpCgojIERpc3BsYXkgYW55IGR1cGxpY2F0ZWQgdGltZXN0YW1wcwpicGFfcmF3ICU+JQogIGZpbHRlcihkdXBsaWNhdGVkKGRhdGVfdGltZSkgfAogICAgICAgICBkdXBsaWNhdGVkKGRhdGVfdGltZSwgZnJvbUxhc3QgPSBUUlVFKSkKCiMgUGFyc2UgbG9jYWwgYW5kIFVUQyB0aW1lc3RhbXBzCmJwYV9yYXcgPC0gYnBhX3JhdyAlPiUKICBtdXRhdGUoCiAgICBkYXRlX3RpbWUgPSBtZHlfaG0oCiAgICAgIGRhdGVfdGltZSwKICAgICAgdHogPSAiQW1lcmljYS9Mb3NfQW5nZWxlcyIKICAgICksCiAgICBkYXRlX3RpbWVfdXRjID0gbWR5X2htKAogICAgICBkYXRlX3RpbWVfdXRjLAogICAgICB0eiA9ICJVVEMiCiAgICApCiAgKQoKYnBhX3JhdyAlPiUKICBmaWx0ZXIoCiAgICBhcy5EYXRlKGRhdGVfdGltZV91dGMsIHR6ID0gIlVUQyIpID09IGFzLkRhdGUoIjIwMjYtMTEtMDEiKQogICkgJT4lCiAgc2VsZWN0KGRhdGVfdGltZSwgZGF0ZV90aW1lX3V0YykgJT4lCiAgcHJpbnQobiA9IDMwLCB3aWR0aCA9IEluZikKCiMgQ2FsY3VsYXRlIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gY29uc2VjdXRpdmUgVVRDIHRpbWVzdGFtcHMKYnBhX3JhdyA8LSBicGFfcmF3ICU+JQogIGFycmFuZ2UoZGF0ZV90aW1lX3V0YykgJT4lCiAgbXV0YXRlKAogICAgaW50ZXJ2YWxfbWludXRlcyA9IGFzLm51bWVyaWMoCiAgICAgIGRpZmZ0aW1lKAogICAgICAgIGRhdGVfdGltZV91dGMsCiAgICAgICAgbGFnKGRhdGVfdGltZV91dGMpLAogICAgICAgIHVuaXRzID0gIm1pbnMiCiAgICAgICkKICAgICkKICApCgojIENvdW50IG9ic2VydmF0aW9ucyBieSBpbnRlcnZhbCBsZW5ndGgKYnBhX3JhdyAlPiUKICBjb3VudChpbnRlcnZhbF9taW51dGVzLCBzb3J0ID0gVFJVRSkKCiMgQ291bnQgbWlzc2luZyB2YWx1ZXMgaW4gZWFjaCB2YXJpYWJsZQptaXNzaW5nX3N1bW1hcnkgPC0gYnBhX3JhdyAlPiUKICBzdW1tYXJpc2UoCiAgICBhY3Jvc3MoCiAgICAgIGV2ZXJ5dGhpbmcoKSwKICAgICAgfiBzdW0oaXMubmEoLngpKQogICAgKQogICkgJT4lCiAgcGl2b3RfbG9uZ2VyKAogICAgY29scyA9IGV2ZXJ5dGhpbmcoKSwKICAgIG5hbWVzX3RvID0gInZhcmlhYmxlIiwKICAgIHZhbHVlc190byA9ICJtaXNzaW5nX3ZhbHVlcyIKICApICU+JQogIGFycmFuZ2UoZGVzYyhtaXNzaW5nX3ZhbHVlcykpCgprbml0cjo6a2FibGUoCiAgbWlzc2luZ19zdW1tYXJ5LAogIGNhcHRpb24gPSAiTWlzc2luZyBWYWx1ZXMgYnkgVmFyaWFibGUiCikKCiMgSWRlbnRpZnkgb2JzZXJ2YXRpb25zIHdpdGggbWlzc2luZyBtZWFzdXJlbWVudHMKbWlzc2luZ19wYXR0ZXJucyA8LSBicGFfcmF3ICU+JQogIG11dGF0ZSgKICAgIG1pc3NpbmdfY291bnQgPSByb3dTdW1zKAogICAgICBpcy5uYSgKICAgICAgICBhY3Jvc3MoCiAgICAgICAgICBjKAogICAgICAgICAgICB0b3RhbF93aW5kX2dlbmVyYXRpb25faW5fYnBhX2NvbnRyb2xfYXJlYV9td19zY2FkYV83OTY4NywKICAgICAgICAgICAgdG90YWxfYnBhX2NvbnRyb2xfYXJlYV9sb2FkX213X3NjYWRhXzQ1NTgzLAogICAgICAgICAgICB0b3RhbF9oeWRyb19nZW5lcmF0aW9uX213X3NjYWRhXzc5NjgyLAogICAgICAgICAgICB0b3RhbF9mb3NzaWxfYmlvbWFzc19nZW5lcmF0aW9uX213X3NjYWRhXzE2NDM3NywKICAgICAgICAgICAgdG90YWxfbnVjbGVhcl9nZW5lcmF0aW9uX213XzcwNjgxLAogICAgICAgICAgICBuZXRfaW50ZXJjaGFuZ2VfbXdfc2NhZGFfNDU1ODEsCiAgICAgICAgICAgIHRvdGFsX3NvbGFyX2dlbmVyYXRpb25faW5fYnBhX2NvbnRyb2xfYXJlYV9td19zY2FkYV8xNDIzODMsCiAgICAgICAgICAgIHRvdGFsX3NvbGFyX2Jhc2Vfc2NoZWR1bGVfZm9yZWNhc3RfaW5fYnBhX2NvbnRyb2xfYXJlYV9td19zY2FkYV8xODc1MTMKICAgICAgICAgICkKICAgICAgICApCiAgICAgICkKICAgICkKICApICU+JQogIGZpbHRlcihtaXNzaW5nX2NvdW50ID4gMCkgJT4lCiAgc2VsZWN0KGRhdGVfdGltZSwgc291cmNlX3llYXIsIG1pc3NpbmdfY291bnQpCgojIFN1bW1hcml6ZSBob3cgbWFueSBvYnNlcnZhdGlvbnMgaGF2ZSBlYWNoIHBhdHRlcm4KbWlzc2luZ19wYXR0ZXJucyAlPiUKICBjb3VudChzb3VyY2VfeWVhciwgbWlzc2luZ19jb3VudCkKCiMgTWlzc2luZyBzb2xhciBiYXNlIHNjaGVkdWxlIGZvcmVjYXN0IGJ5IHllYXIKYnBhX3JhdyAlPiUKICBncm91cF9ieShzb3VyY2VfeWVhcikgJT4lCiAgc3VtbWFyaXNlKAogICAgdG90YWxfb2JzZXJ2YXRpb25zID0gbigpLAogICAgbWlzc2luZ192YWx1ZXMgPSBzdW0oaXMubmEoCiAgICAgIHRvdGFsX3NvbGFyX2Jhc2Vfc2NoZWR1bGVfZm9yZWNhc3RfaW5fYnBhX2NvbnRyb2xfYXJlYV9td19zY2FkYV8xODc1MTMKICAgICkpLAogICAgcGVyY2VudF9taXNzaW5nID0gbWVhbihpcy5uYSgKICAgICAgdG90YWxfc29sYXJfYmFzZV9zY2hlZHVsZV9mb3JlY2FzdF9pbl9icGFfY29udHJvbF9hcmVhX213X3NjYWRhXzE4NzUxMwogICAgKSkgKiAxMDAsCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKQoKIyBNaXNzaW5nIHNvbGFyIGZvcmVjYXN0IGJ5IGNhbGVuZGFyIGRhdGUKYnBhX3JhdyAlPiUKICBtdXRhdGUoZGF0ZSA9IGFzLkRhdGUoZGF0ZV90aW1lKSkgJT4lCiAgZ3JvdXBfYnkoc291cmNlX3llYXIsIGRhdGUpICU+JQogIHN1bW1hcmlzZSgKICAgIG1pc3NpbmdfdmFsdWVzID0gc3VtKGlzLm5hKAogICAgICB0b3RhbF9zb2xhcl9iYXNlX3NjaGVkdWxlX2ZvcmVjYXN0X2luX2JwYV9jb250cm9sX2FyZWFfbXdfc2NhZGFfMTg3NTEzCiAgICApKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApICU+JQogIGZpbHRlcihtaXNzaW5nX3ZhbHVlcyA+IDApICU+JQogIGFycmFuZ2UoZGVzYyhtaXNzaW5nX3ZhbHVlcykpICU+JQogIGhlYWQoMjApCgojIElkZW50aWZ5IHdoaWNoIHZhcmlhYmxlcyBhcmUgbWlzc2luZyBhdCBlYWNoIGFmZmVjdGVkIHRpbWVzdGFtcApicGFfcmF3ICU+JQogIGZpbHRlcigKICAgIGlmX2FueSgKICAgICAgYygKICAgICAgICB0b3RhbF93aW5kX2dlbmVyYXRpb25faW5fYnBhX2NvbnRyb2xfYXJlYV9td19zY2FkYV83OTY4NywKICAgICAgICB0b3RhbF9icGFfY29udHJvbF9hcmVhX2xvYWRfbXdfc2NhZGFfNDU1ODMsCiAgICAgICAgdG90YWxfaHlkcm9fZ2VuZXJhdGlvbl9td19zY2FkYV83OTY4MiwKICAgICAgICB0b3RhbF9mb3NzaWxfYmlvbWFzc19nZW5lcmF0aW9uX213X3NjYWRhXzE2NDM3NywKICAgICAgICB0b3RhbF9udWNsZWFyX2dlbmVyYXRpb25fbXdfNzA2ODEsCiAgICAgICAgbmV0X2ludGVyY2hhbmdlX213X3NjYWRhXzQ1NTgxLAogICAgICAgIHRvdGFsX3NvbGFyX2dlbmVyYXRpb25faW5fYnBhX2NvbnRyb2xfYXJlYV9td19zY2FkYV8xNDIzODMsCiAgICAgICAgdG90YWxfc29sYXJfYmFzZV9zY2hlZHVsZV9mb3JlY2FzdF9pbl9icGFfY29udHJvbF9hcmVhX213X3NjYWRhXzE4NzUxMwogICAgICApLAogICAgICBpcy5uYQogICAgKQogICkgJT4lCiAgbXV0YXRlKAogICAgbWlzc2luZ192YXJpYWJsZXMgPSBwbWFwX2NocigKICAgICAgYWNyb3NzKAogICAgICAgIGMoCiAgICAgICAgICB0b3RhbF93aW5kX2dlbmVyYXRpb25faW5fYnBhX2NvbnRyb2xfYXJlYV9td19zY2FkYV83OTY4NywKICAgICAgICAgIHRvdGFsX2JwYV9jb250cm9sX2FyZWFfbG9hZF9td19zY2FkYV80NTU4MywKICAgICAgICAgIHRvdGFsX2h5ZHJvX2dlbmVyYXRpb25fbXdfc2NhZGFfNzk2ODIsCiAgICAgICAgICB0b3RhbF9mb3NzaWxfYmlvbWFzc19nZW5lcmF0aW9uX213X3NjYWRhXzE2NDM3NywKICAgICAgICAgIHRvdGFsX251Y2xlYXJfZ2VuZXJhdGlvbl9td183MDY4MSwKICAgICAgICAgIG5ldF9pbnRlcmNoYW5nZV9td19zY2FkYV80NTU4MSwKICAgICAgICAgIHRvdGFsX3NvbGFyX2dlbmVyYXRpb25faW5fYnBhX2NvbnRyb2xfYXJlYV9td19zY2FkYV8xNDIzODMsCiAgICAgICAgICB0b3RhbF9zb2xhcl9iYXNlX3NjaGVkdWxlX2ZvcmVjYXN0X2luX2JwYV9jb250cm9sX2FyZWFfbXdfc2NhZGFfMTg3NTEzCiAgICAgICAgKQogICAgICApLAogICAgICBcKC4uLikgewogICAgICAgIHZhbHVlcyA8LSBsaXN0KC4uLikKICAgICAgICBuYW1lcyh2YWx1ZXMpW21hcF9sZ2wodmFsdWVzLCBpcy5uYSldICU+JQogICAgICAgICAgcGFzdGUoY29sbGFwc2UgPSAiLCAiKQogICAgICB9CiAgICApCiAgKSAlPiUKICBjb3VudChzb3VyY2VfeWVhciwgbWlzc2luZ192YXJpYWJsZXMsIHNvcnQgPSBUUlVFKSAlPiUKICBwcmludChuID0gSW5mLCB3aWR0aCA9IEluZikKYGBgCgoKYGBge3J9CiMgRXhhbWluZSBzb2xhciBmb3JlY2FzdCBtaXNzaW5nbmVzcyBieSBob3VyIGFuZCBtb250aApzb2xhcl9taXNzaW5nX2hvdXJseSA8LSBicGFfcmF3ICU+JQogIG11dGF0ZSgKICAgIHllYXIgPSB5ZWFyKGRhdGVfdGltZSksCiAgICBtb250aCA9IG1vbnRoKGRhdGVfdGltZSwgbGFiZWwgPSBUUlVFLCBhYmJyID0gRkFMU0UpLAogICAgaG91ciA9IGhvdXIoZGF0ZV90aW1lKQogICkgJT4lCiAgZ3JvdXBfYnkoeWVhciwgbW9udGgsIGhvdXIpICU+JQogIHN1bW1hcmlzZSgKICAgIG9ic2VydmF0aW9ucyA9IG4oKSwKICAgIG1pc3NpbmcgPSBzdW0oaXMubmEoCiAgICAgIHRvdGFsX3NvbGFyX2Jhc2Vfc2NoZWR1bGVfZm9yZWNhc3RfaW5fYnBhX2NvbnRyb2xfYXJlYV9td19zY2FkYV8xODc1MTMKICAgICkpLAogICAgcGVyY2VudF9taXNzaW5nID0gbWVhbihpcy5uYSgKICAgICAgdG90YWxfc29sYXJfYmFzZV9zY2hlZHVsZV9mb3JlY2FzdF9pbl9icGFfY29udHJvbF9hcmVhX213X3NjYWRhXzE4NzUxMwogICAgKSkgKiAxMDAsCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKQoKIyBQbG90IHNvbGFyIGZvcmVjYXN0IG1pc3NpbmduZXNzIGJ5IGhvdXIKZ2dwbG90KAogIHNvbGFyX21pc3NpbmdfaG91cmx5LAogIGFlcyh4ID0gaG91ciwgeSA9IG1vbnRoLCBmaWxsID0gcGVyY2VudF9taXNzaW5nKQopICsKICBnZW9tX3RpbGUoY29sb3IgPSAid2hpdGUiLCBsaW5ld2lkdGggPSAwLjIpICsKICBmYWNldF93cmFwKH4geWVhcikgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQoCiAgICBsb3cgPSAiI0ZGRjdFQyIsCiAgICBoaWdoID0gIiNCMzAwMDAiLAogICAgbmFtZSA9ICIlIE1pc3NpbmciCiAgKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAyMywgYnkgPSAyKSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJTb2xhciBCYXNlIFNjaGVkdWxlIEZvcmVjYXN0IE1pc3NpbmduZXNzIiwKICAgIHN1YnRpdGxlID0gIlBlcmNlbnRhZ2Ugb2YgbWlzc2luZyBvYnNlcnZhdGlvbnMgYnkgaG91ciBhbmQgbW9udGgiLAogICAgeCA9ICJIb3VyIG9mIERheSIsCiAgICB5ID0gIk1vbnRoIgogICkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoCiAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwKQogICkKYGBgCgpXZSBuZWVkIHRvIGNvbnZlcnQgdGhlIDUgbWludXRlIGludG8gaG91cmx5IGxvYWQgZmlyc3QuIAoKYGBge3J9CiMgQ3JlYXRlIGFuIGhvdXJseSBsb2FkIGRhdGFzZXQKaG91cmx5X2xvYWQgPC0gYnBhX3JhdyAlPiUKICBtdXRhdGUoCiAgICBob3VyX3V0YyA9IGZsb29yX2RhdGUoZGF0ZV90aW1lX3V0YywgdW5pdCA9ICJob3VyIikKICApICU+JQogIGdyb3VwX2J5KGhvdXJfdXRjKSAlPiUKICBzdW1tYXJpc2UoCiAgICBsb2FkX213ID0gbWVhbigKICAgICAgdG90YWxfYnBhX2NvbnRyb2xfYXJlYV9sb2FkX213X3NjYWRhXzQ1NTgzLAogICAgICBuYS5ybSA9IFRSVUUKICAgICksCiAgICBvYnNlcnZhdGlvbnMgPSBzdW0oCiAgICAgICFpcy5uYSh0b3RhbF9icGFfY29udHJvbF9hcmVhX2xvYWRfbXdfc2NhZGFfNDU1ODMpCiAgICApLAogICAgLmdyb3VwcyA9ICJkcm9wIgogICkgJT4lCiAgIyBLZWVwIGhvdXJzIHdpdGggYXQgbGVhc3QgMTEgb2YgMTIgZml2ZS1taW51dGUgbG9hZCBvYnNlcnZhdGlvbnMKICBmaWx0ZXIob2JzZXJ2YXRpb25zID49IDExKSAlPiUKICBtdXRhdGUoCiAgICBkYXRlX3RpbWUgPSB3aXRoX3R6KGhvdXJfdXRjLCB0em9uZSA9ICJBbWVyaWNhL0xvc19BbmdlbGVzIiksCiAgICBkYXRlID0gYXMuRGF0ZShkYXRlX3RpbWUpLAogICAgeWVhciA9IHllYXIoZGF0ZV90aW1lKSwKICAgIG1vbnRoID0gZmFjdG9yKG1vbnRoKGRhdGVfdGltZSwgbGFiZWwgPSBUUlVFLCBhYmJyID0gRkFMU0UpKSwKICAgIGRheV9vZl93ZWVrID0gZmFjdG9yKAogICAgICB3ZGF5KGRhdGVfdGltZSwgbGFiZWwgPSBUUlVFLCBhYmJyID0gRkFMU0UpLAogICAgICBsZXZlbHMgPSBjKAogICAgICAgICJNb25kYXkiLCAiVHVlc2RheSIsICJXZWRuZXNkYXkiLCAiVGh1cnNkYXkiLAogICAgICAgICJGcmlkYXkiLCAiU2F0dXJkYXkiLCAiU3VuZGF5IgogICAgICApCiAgICApLAogICAgaG91ciA9IGZhY3Rvcihob3VyKGRhdGVfdGltZSkpCiAgKQoKIyBJbnNwZWN0IHRoZSBob3VybHkgZGF0YXNldApnbGltcHNlKGhvdXJseV9sb2FkKQoKIyBDaGVjayB0aGUgbnVtYmVyIG9mIGhvdXJseSBvYnNlcnZhdGlvbnMgYnkgeWVhcgpob3VybHlfbG9hZCAlPiUKICBjb3VudCh5ZWFyKQoKIyBFeGFtaW5lIHRoZSBmaXJzdCBmZXcgb2JzZXJ2YXRpb25zCmhlYWQoaG91cmx5X2xvYWQsIDEwKQpgYGAKCiMjIE1vZGVsIDE7IEhvdXJseSBCUEEgTG9hZCBGb3JlY2FzdGluZwoKT3VyIGZpcnN0IG1vZGVsIHdpbGwgdXNlIHRocmVlIGtpbmRzIG9mIGluZm9ybWF0aW9uIHRvIHByZWRpY3QgaG91cmx5IEJQQSBsb2FkOgogICAgMS4gWWVzdGVyZGF5J3MgbG9hZDogV2hhdCB3YXMgZGVtYW5kIGF0IHRoZSBzYW1lIGhvdXIgMjQgaG91cnMgYWdvPwogICAgMi4gTGFzdCB3ZWVrJ3MgbG9hZDogV2hhdCB3YXMgZGVtYW5kIGF0IHRoZSBzYW1lIGhvdXIgc2V2ZW4gZGF5cyBhZ28/CiAgICAzLiBDYWxlbmRhciBwYXR0ZXJuczogV2hhdCBob3VyLCBkYXkgb2YgdGhlIHdlZWssIGFuZCBtb250aCBpcyBpdD8KVGhlc2UgYXJlIHVzZWZ1bCBwcmVkaWN0b3JzIGJlY2F1c2UgZWxlY3RyaWNpdHkgZGVtYW5kIGhhcyBzdHJvbmcgZGFpbHksIHdlZWtseSwgYW5kIHNlYXNvbmFsIHBhdHRlcm5zLgoKYGBge3J9CiMgQ3JlYXRlIGhvdXJseSBsb2FkIG9ic2VydmF0aW9ucwpob3VybHlfZGF0YSA8LSBicGFfcmF3ICU+JQogIG11dGF0ZSgKICAgIGhvdXJfdXRjID0gZmxvb3JfZGF0ZShkYXRlX3RpbWVfdXRjLCB1bml0ID0gImhvdXIiKQogICkgJT4lCiAgZ3JvdXBfYnkoaG91cl91dGMpICU+JQogIHN1bW1hcmlzZSgKICAgIGxvYWRfbXcgPSBtZWFuKAogICAgICB0b3RhbF9icGFfY29udHJvbF9hcmVhX2xvYWRfbXdfc2NhZGFfNDU1ODMsCiAgICAgIG5hLnJtID0gVFJVRQogICAgKSwKICAgIG9ic2VydmF0aW9ucyA9IHN1bSgKICAgICAgIWlzLm5hKHRvdGFsX2JwYV9jb250cm9sX2FyZWFfbG9hZF9td19zY2FkYV80NTU4MykKICAgICksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKSAlPiUKICBtdXRhdGUoCiAgICBkYXRlX3RpbWVfbG9jYWwgPSB3aXRoX3R6KAogICAgICBob3VyX3V0YywKICAgICAgdHpvbmUgPSAiQW1lcmljYS9Mb3NfQW5nZWxlcyIKICAgICksCiAgICBob3VyID0gaG91cihkYXRlX3RpbWVfbG9jYWwpLAogICAgd2Vla2RheSA9IHdkYXkoZGF0ZV90aW1lX2xvY2FsLCBsYWJlbCA9IFRSVUUpLAogICAgbW9udGggPSBtb250aChkYXRlX3RpbWVfbG9jYWwsIGxhYmVsID0gVFJVRSkKICApICU+JQogIGFycmFuZ2UoaG91cl91dGMpCgojIENyZWF0ZSBsYWdnZWQgbG9hZCB2YXJpYWJsZXMKaG91cmx5X2RhdGEgPC0gaG91cmx5X2RhdGEgJT4lCiAgbXV0YXRlKAogICAgbG9hZF9sYWdfMjQgPSBsYWcobG9hZF9tdywgMjQpLAogICAgbG9hZF9sYWdfMTY4ID0gbGFnKGxvYWRfbXcsIDE2OCkKICApCgojIERlZmluZSB0aGUgdGVzdGluZyBwZXJpb2QKdGVzdF9zdGFydCA8LSBtYXgoaG91cmx5X2RhdGEkaG91cl91dGMpIC0gZGF5cygzMCkKCiMgU2VwYXJhdGUgdHJhaW5pbmcgYW5kIHRlc3Rpbmcgb2JzZXJ2YXRpb25zCnRyYWluaW5nX2RhdGEgPC0gaG91cmx5X2RhdGEgJT4lCiAgZmlsdGVyKAogICAgaG91cl91dGMgPCB0ZXN0X3N0YXJ0LAogICAgIWlzLm5hKGxvYWRfbGFnXzI0KSwKICAgICFpcy5uYShsb2FkX2xhZ18xNjgpCiAgKQoKdGVzdGluZ19kYXRhIDwtIGhvdXJseV9kYXRhICU+JQogIGZpbHRlcigKICAgIGhvdXJfdXRjID49IHRlc3Rfc3RhcnQsCiAgICAhaXMubmEobG9hZF9sYWdfMjQpLAogICAgIWlzLm5hKGxvYWRfbGFnXzE2OCkKICApCgojIENoZWNrIHNhbXBsZSBzaXplcwpucm93KHRyYWluaW5nX2RhdGEpCm5yb3codGVzdGluZ19kYXRhKQoKIyBUcmFpbiBob3VybHkgbG9hZCBmb3JlY2FzdGluZyBtb2RlbApsb2FkX21vZGVsXzEgPC0gbG0oCiAgbG9hZF9tdyB+CiAgICBsb2FkX2xhZ18yNCArCiAgICBsb2FkX2xhZ18xNjggKwogICAgZmFjdG9yKGhvdXIpICsKICAgIGZhY3Rvcih3ZWVrZGF5KSArCiAgICBmYWN0b3IobW9udGgpLAogIGRhdGEgPSB0cmFpbmluZ19kYXRhCikKCiMgRXhhbWluZSBtb2RlbCByZXN1bHRzCnN1bW1hcnkobG9hZF9tb2RlbF8xKQpgYGAKV2UgYXJlIHRyeWluZyB0byBmb3JlY2FzdCBgbG9hZF9td2AsIG9yIHRoZSBob3VybHkgQlBBIGJhbGFuY2luZyBhcmVhIGVsZWN0cmljaXR5IGxvYWQsIGFzIG1lYXN1cmVkIGluIG1lZ2F3YXR0cy4gV2UgYXJlIHVzaW5nIHRoZSBpbmZvcm1hdGlvbiBvZiBsb2FkIGF0IHRoZSBzYW1lIGhvdXIgb2YgdGhlIHByZXZpb3VzIGRheSwgc2FtZSBob3VyIG9mIHRoZSBwcmV2aW91cyB3ZWVrLCB0aGUgaG91ciBvZiB0aGUgZGF5LCB0aGUgZGF5IG9mIHRoZSB3ZWVrLCBhbmQgdGhlIG1vbnRoIG9mIHRoZSB5ZWFyLiBJbiBvdGhlciB3b3Jkcywgd2UgYXJlIGFza2luZyAiZ2l2ZW4gd2hhdCBlbGVjdHJpY2l0eSBkZW1hbmQgbG9va2VkIGxpa2UgcmVjZW50bHksIGFuZCBrbm93aW5nIHRoZSB0aW1lIG9mIGRheSwgZGF5IG9mIHRoZSB3ZWVrLCBhbmQgdGltZSBvZiB5ZWFyLCBob3cgYWNjdXJhdGVseSBjYW4gd2UgcHJlZGljdCBlbGVjdHJpY2l0eSBkZW1hbmQ/CgpCZWZvcmUgdHJhaW5pbmcgdGhlIG1vZGVsIHdlIHRyYW5zZm9ybWVkIHRoZSBvcmlnaW5hbCA1LW1pbnV0ZSBpbmNyZW1lbnRzIGludG8gaG91cmx5IG9ic2VydmF0aW9ucy4gVGhpcyB3YXkgd2UgY2FuIHdvcmsgd2l0aCBhbiBob3VyLWludGVydmFsIGxhZyBhcyBvcHBvc2VkIHRvIDUtbWludXRlIGxhZ3MuIAoKV2UgdHJhaW5lZCB0aGUgbW9kZWwgb24gYWxsIG9mIHRoZSAyMDI1IHBvaW50cywgbWludXMgdGhlIGZpbmFsIDMwIGRheXMgb2Ygb2JzZXJ2YXRpb25zLiBUaGUgdGVzdCBwZXJpb2QgaXMgdXNlZCBmb3Igcm9sbGluZyBvbmUgaG91ciBhaGVhZCBwcmVkaWN0aW9ucy4gQXQgZWFjaCBob3VyIHRoZSBtb2RlbCBjYW4gdXNlIGFjdHVhbCBsb2FkIGZyb20gdGhlIHByZXZpb3VzIGRheSBhbmQgd2Vlay4gSXQgaXMgbm90IGZvcmVjYXN0aW5nIGFsbCAzMCBkYXlzIGF0IG9uY2UgZnJvbSBhIHNpbmdsZSBzdGFydGluZyBwb2ludC4gCgpUaGUgbW9kZWwgdXNlZCBpcyBhcyBmb2xsb3dzOgpgbG9hZF9tb2RlbF8xIDwtIGxtKGxvYWRfbXcgfiBsb2FkX2xhZ18yNCArIGxvYWRfbGFnXzE2OCArIGZhY3Rvcihob3VyKSArIGZhY3Rvcih3ZWVrZGF5KSArIGZhY3Rvcihtb250aCksIGRhdGEgPSB0cmFpbmluZ19kYXRhKWAKCnwgQ29tcG9uZW50IHwgV2hhdCBpdCBDYXB0dXJlcyB8CnwgLS0tIHwgLS0tIHwKfGBsb2FkX213YCB8IFRoZSBvdXRjb21lIHdlIGFyZSBwcmVkaWN0aW5nIHwKfCBgbG9hZF9sYWdfMjRgIHwgUHJldmlvdXMgZGF5IGRlbWFuZCB8CnwgYGxvYWRfbGFnXzE2OGAgfCBQcmV2aW91cyB3ZWVrIGRlbWFuZCB8IAp8IGBmYWN0b3IoaG91cilgIHwgRGlmZmVyZW5jZXMgYmV0d2VlbiBob3VycyBvZiB0aGUgZGF5IHwKfCBgZmFjdG9yKHdlZWtkYXkpYCB8IERpZmZlcmVuY2VzIGJldHdlZW4gZGF5cyBvZiB0aGUgd2VlayB8CnwgYGZhY3Rvcihtb250aClgIHwgRGlmZmVyZW5jZXMgYmV0d2VlbiBtb250aHMgfAoKQ29uY2VwdHVhbGx5LCB0aGlzIHRyYW5zbGF0ZXMgdG8gJCRMb2FkX3QgPSBcYmV0YV8wICsgXGJldGFfMUxvYWRfe3QtMjR9ICsgXGJldGFfMkxvYWRfe3QtMTY4fSArIEhvdXIgRWZmZWN0cyArIFdlZWtkYXkgRWZmZWN0cyArIE1vbnRoIEVmZmVjdHMgKyBcZXBzaWxvbl90JCQKCgojIyMgSW50ZXJwcmV0aW5nIFJlc3VsdHMKClRoZSBwcmV2aW91cy1kYXkgbG9hZCBjb2VmZmljaWVudCBpcyAwLjgxODMsIGFuZCB0aGUgcHJldmlvdXMtd2VlayBsb2FkJ3MgaXMgMC4wOTQ0LCBib3RoIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQuIEludGVycHJldGVkLCB0aGUgcHJldmlvdXMtZGF5IGNvZWZmaWNpZW50IG1lYW5zIHRoYXQsIGhvbGRpbmcgYWxsIG90aGVyIHByZWRpY3RvcnMgY29uc3RhbnQsIGEgMSBNVyBpbmNyZWFzZSBpbiB5ZXN0ZXJkYXkncyBsb2FkIGF0IHRoZSBzYW1lIGhvdXIgaXMgYXNzb2NpYXRlZCB3aXRoIGFuIGVzdGltYXRlZCAwLjgxOCBNVyBpbmNyZWFzZSBpbiBwcmVkaWN0ZWQgbG9hZC4gVGhlIG1vZGVsIGlzIHJlbHlpbmcgaGVhdmlseSBvbiB0aGUgcHJpb3IgZGF5J3MgbG9hZC4gVGhlIHByaW9yIHdlZWsgY29udHJpYnV0ZXMgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiwgYnV0IHRoZSBlc3RpbWF0ZWQgY29lZmZpY2llbnQgaXMgbXVjaCBzbWFsbGVyLiAKClRoZSBob3VyLW9mLWRheSBlZmZlY3RzIHVzZSB0aGUgaG91ciAwIChtaWRuaWdodCkgYXMgdGhlIHJlZmVyZW5jZSBjYXRlZ29yeS4gVGhlIHN0cm9uZ2VzdCBob3VybHkgZWZmZWN0IGluIHRoaXMgb3V0cHV0IGlzIGF0IDZwbSwgYXBwcm94aW1hdGVseSAxMTYgTVcgYWJvdmUgdGhlIG1pZG5pZ2h0IHJlZmVyZW5jZS4gVGhpcyBpcyBjb25zaXN0ZW50IHdpdGggdGhlIGlkZWEgdGhhdCBlbGVjdHJpY2l0eSBkZW1hbmQgaGFzIGEgZGFpbHkgcGF0dGVybi4gSG93ZXZlciwgdGhlc2UgYXJlIGFkanVzdG1lbnRzIHJlbGF0aXZlIHRvIHRoZSByZWZlcmVuY2UgaG91ciwgbm90IHRoZSBhdmVyYWdlIGxvYWQgZGlmZmVyZW5jZSBiZXR3ZWVuIHRob3NlIGhvdXJzLiAKCkhvd2V2ZXIsIHRoZSBtb2RlbCBpcyB0cmVhdGluZyB0aGUgbGFiZWxsZWQgd2Vla2RheSBhbmQgbW9udGhseSB2YXJpYWJsZXMgYXMgKm9yZGVyZWQgZmFjdG9ycyouIEluc3RlYWQgb2YgZXN0aW1hdGluZyBzZXBhcmF0ZSBjb2VmZmljaWVudCBmb2UgZWFjaCB3ZWVrZGF5IG9yIG1vbnRoIHJlbGF0aXZlIHRvIGEgcmVmZXJlbmNlIGNhdGVnb3J5LCBpdCBpcyBlc3RpbWF0aW5nIHBvbHlub21pYWwgY29udHJhc3RzLiBXZSBjYW4gcmUtcnVuIHRoaXMgbW9kZWwgdXNpbmcgb3JkaW5hcnkgY2F0ZWdvcmljYWwgY29udHJhc3RzIGJ5IGNvbnZlcnRpbmcgdGhlIGZhY3RvcnMgaW50byBjaGFyYWN0ZXIgZmlyc3QuIAoKYGBge3J9CmxvYWRfbW9kZWxfMiA8LSBsbSgKICBsb2FkX213IH4KICAgIGxvYWRfbGFnXzI0ICsKICAgIGxvYWRfbGFnXzE2OCArCiAgICBmYWN0b3IoaG91cikgKwogICAgZmFjdG9yKGFzLmNoYXJhY3Rlcih3ZWVrZGF5KSkgKwogICAgZmFjdG9yKGFzLmNoYXJhY3Rlcihtb250aCkpLAogIGRhdGEgPSB0cmFpbmluZ19kYXRhCikKCnN1bW1hcnkobG9hZF9tb2RlbF8yKQpgYGAKCldpdGggdGhpcyBmaXhlZCwgd2Ugc2VlIGFkanVzdGVkIGRpZmZlcmVuY2VzIGFmdGVyIGFjY291bnRpbmcgZm9yIHRoZSBsYWdnZWQgbG9hZHMgYW5kIG90aGVyIGNhbGVuZGFyIHZhcmlhYmxlcy4gSXQgaXMgdXNpbmcgRnJpZGF5IGFzIGEgd2Vla2RheSByZWZlcmVuY2UsIEFwcmlsIGFzIGEgbW9udGhseSByZWZlcmVuY2UsIGFuZCBtaWRuaWdodCAoaG91ciAwKSBhcyBhbiBob3VybHkgcmVmZXJlbmNlLiAKCkFnYWluLCB0aGUgcHJldmlvdXMgZGF5IGxvYWQgcmVtYWlucyBvdXIgc3Ryb25nZXN0IHByZWRpY3Rvci4gaXRzIGNvZWZmaWNpZW50IGlzIGFib3V0IDAuODIsIGNvbXBhcmVkIHdpdGggdGhlIDAuMDkgZm9yIHByZXZpb3VzLXdlZWsgbG9hZC4gQm90aCBhcmUgaGlnaGx5IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQuIFdlZWtkYXkgZGlmZmVyZW5jZXMgYXJlIGFsc28gc3Vic3RhbnRpYWwgaW4gdGhpcyBtb2RlbC4gTW9uZGF5J3MgZXN0aW1hdGVkIGVmZmVjdCBpcyBhcHByb3hpbWF0ZWx5IDM4MSBNVyBhYm92ZSBGcmlkYXksIHdoaWxlIFNhdHVyZGF5IGlzIGFwcHJveGltYXRlbHkgMTU3IE1XIGJlbG93IEZyaWRheS4gVGhpcyBzdWdnZXN0cyBtZWFuaW5nZnVsIGRpZmZlcmVuY2VkIGluIGRlbWFuZCBwYXR0ZXJucyBhY3Jvc3MgdGhlIHdlZWsuIAoKV2UgYWxzbyBzZWUgc2Vhc29uYWwgZGlmZmVyZW5jZXMuIEphbnVhcnkgYW5kIERlY2VtYmVyIGhhdmUgcG9zaXRpb3ZlIGNvZWZmaWNpZW50cyByZWxhdGl2ZSB0byBBcHJpbC4gVGhpcyBpcyBjb25zaXN0ZW50IHdpdGggc2Vhc29uYWwgdmFyaWF0aW9uIGluIGVsZWN0cmljaXR5IGRlbWFuZCwgYWx0aG91Z2ggd2Ugd291bGQgbmVlZCB0byBpbnZlc3RpZ2F0ZSB0ZW1wZXJhdHVyZSBhbmQgd2VhdGhlciBiZWZvcmUgYXR0cmlidXRpbmcgdGhvc2UgZGlmZmVyZW5jZXMgdG8gYW55IHBhcnRpY3VsYXIgY2F1c2UuIAoKVGhlIFItc3F1YXJlZCByZW1haW5lZCB0aGUgc2FtZSBhY3Jvc3MgbW9kZWxzLCBhbG9uZ3NpZGUgdGhlIHJlc2lkdWFsIHN0YW5kYXJkIGVycm9yIGFuZCB0aGUgRiBzdGF0aXN0aWMuICBPdXIgbW9kZWwgc2VlbXMgdG8gZml0IHRoZSB0cmFpbmluZyBkYXRhIHdlbGwsIGJ1dCB3ZSBtdXN0IHRlc3QgaXQgdG8gYmUgc3VyZS4gCiMjIyBUZXN0aW5nIHRoZSBNb2RlbAoKV2UgdGVzdCBmb3I6Ci0gICBNQUUgKG1lYW4gYWJzb2x1dGUgZXJyb3IpOiBtZWFzdXJlcyB0aGUgYXZlcmFnZSBhYnNvbHV0ZSBkaWZmZXJlbmNlIGJldHdlZW4gYWN0dWFsIGFuZCBwcmVkaWN0ZWQgdmFsdWVzLiAKLSAgIFJNU0UgKHJvb3QgbWVhbiBzcXVhcmVkIGVycm9yKTogc2ltaWxhciB0byBNQUUsIGJ1dCBpbnN0ZWFkIG9mIHRha2luZyB0aGUgYWJzb2x1dGUgdmFsdWUgb2YgZWFjaCBlcnJvciwgaXQgc3F1YXJlcyB0aGUgZXJyb3IsIGF2ZXJhZ2VzIHRob3NlIHNxdWFyZWQgZXJyb3JzLCB0aGVuIHRha2VzIHRoZSBzcXVhcmVkIHJvb3QuIFRoaXMgZXNzZW50aWFsbHkgd2VpZ2h0cyB0aGUgbGFyZ2VyIGVycm9ycyBtb3JlLiAKLSAgIE1BUEUgKG1lYW4gYWJzb2x1dGUgcGVyY2VudGFnZSBlcnJvcik6IGV4cHJlc3NlcyBmb3JlY2FzdGluZyBlcnJvcnMgYXMgcGVyY2VudGFnZXMgcmF0aGVyIHRoYW4gTVcuIAoKYGBge3J9CiMgR2VuZXJhdGUgcHJlZGljdGlvbnMgZm9yIHRoZSB0ZXN0aW5nIHBlcmlvZAp0ZXN0aW5nX2RhdGEgPC0gdGVzdGluZ19kYXRhICU+JQogIG11dGF0ZSgKICAgIHByZWRpY3RlZF9sb2FkID0gcHJlZGljdCgKICAgICAgbG9hZF9tb2RlbF8yLAogICAgICBuZXdkYXRhID0gdGVzdGluZ19kYXRhCiAgICApLAoKICAgICMgU2ltcGxlIGZvcmVjYXN0aW5nIGJlbmNobWFya3MKICAgIG5haXZlXzI0ID0gbG9hZF9sYWdfMjQsCiAgICBuYWl2ZV8xNjggPSBsb2FkX2xhZ18xNjgKICApCgojIENvbXBhcmUgZm9yZWNhc3RpbmcgYWNjdXJhY3kKbW9kZWxfYWNjdXJhY3kgPC0gdGliYmxlKAogIG1vZGVsID0gYygKICAgICJSZWdyZXNzaW9uIG1vZGVsIiwKICAgICJQcmV2aW91cy1kYXkgYmFzZWxpbmUiLAogICAgIlByZXZpb3VzLXdlZWsgYmFzZWxpbmUiCiAgKSwKCiAgTUFFID0gYygKICAgIG1lYW4oYWJzKHRlc3RpbmdfZGF0YSRsb2FkX213IC0gdGVzdGluZ19kYXRhJHByZWRpY3RlZF9sb2FkKSksCiAgICBtZWFuKGFicyh0ZXN0aW5nX2RhdGEkbG9hZF9tdyAtIHRlc3RpbmdfZGF0YSRuYWl2ZV8yNCkpLAogICAgbWVhbihhYnModGVzdGluZ19kYXRhJGxvYWRfbXcgLSB0ZXN0aW5nX2RhdGEkbmFpdmVfMTY4KSkKICApLAoKICBSTVNFID0gYygKICAgIHNxcnQobWVhbigodGVzdGluZ19kYXRhJGxvYWRfbXcgLSB0ZXN0aW5nX2RhdGEkcHJlZGljdGVkX2xvYWQpXjIpKSwKICAgIHNxcnQobWVhbigodGVzdGluZ19kYXRhJGxvYWRfbXcgLSB0ZXN0aW5nX2RhdGEkbmFpdmVfMjQpXjIpKSwKICAgIHNxcnQobWVhbigodGVzdGluZ19kYXRhJGxvYWRfbXcgLSB0ZXN0aW5nX2RhdGEkbmFpdmVfMTY4KV4yKSkKICApLAoKICBNQVBFID0gYygKICAgIG1lYW4oYWJzKCh0ZXN0aW5nX2RhdGEkbG9hZF9tdyAtIHRlc3RpbmdfZGF0YSRwcmVkaWN0ZWRfbG9hZCkgLwogICAgICB0ZXN0aW5nX2RhdGEkbG9hZF9tdykpICogMTAwLAoKICAgIG1lYW4oYWJzKCh0ZXN0aW5nX2RhdGEkbG9hZF9tdyAtIHRlc3RpbmdfZGF0YSRuYWl2ZV8yNCkgLwogICAgICB0ZXN0aW5nX2RhdGEkbG9hZF9tdykpICogMTAwLAoKICAgIG1lYW4oYWJzKCh0ZXN0aW5nX2RhdGEkbG9hZF9tdyAtIHRlc3RpbmdfZGF0YSRuYWl2ZV8xNjgpIC8KICAgICAgdGVzdGluZ19kYXRhJGxvYWRfbXcpKSAqIDEwMAogICkKKQoKIyBEaXNwbGF5IHJlc3VsdHMKa25pdHI6OmthYmxlKAogIG1vZGVsX2FjY3VyYWN5LAogIGRpZ2l0cyA9IDIsCiAgY2FwdGlvbiA9ICJIb3VybHkgTG9hZCBGb3JlY2FzdGluZyBBY2N1cmFjeSIKKQpgYGAKCkludGVycHJldGluZyB0aGlzLCB3ZSBzZWUgdGhhdCBvdXIgcmVncmVzc2lvbiBtb2RlbCBpbmNsdWRpbmcgYWxsIG9mIHRoZSB2YXJpYWJsZXMgd2VyZSBjbG9zZXIgdGhhbiBzaW1wbHkgcmVseWluZyBvbiB0aGUgcHJldmlvdXMgZGF5IG9yIHdlZWsgYmFzZWxpbmUuIEJ1dCwgdGhlcmUgaXMgc3RpbGwgc29tZSBzaWduaWZpY2FudCBlcnJvciBpbiBvdXIgcHJlZGljdGluZy4gT25lIHdheSB0byBpbnZlc3RpZ2F0ZSBpcyB0byBjYWxjdWxhdGUgdGhlIGZvcmVjYXN0IGVycm9yIGZvciBldmVyeSBvYnNlcnZhdGlvbiBhbmQgc3VtbWFyaXplIGl0IGJ5IGhvdXIgb2YgdGhlIGRheS4gCgpgYGB7cn0KIyBDYWxjdWxhdGUgZm9yZWNhc3RpbmcgZXJyb3JzCnRlc3RpbmdfZGF0YSA8LSB0ZXN0aW5nX2RhdGEgJT4lCiAgbXV0YXRlKAogICAgZm9yZWNhc3RfZXJyb3IgPSBsb2FkX213IC0gcHJlZGljdGVkX2xvYWQsCiAgICBhYnNvbHV0ZV9lcnJvciA9IGFicyhmb3JlY2FzdF9lcnJvcikKICApCgojIFN1bW1hcml6ZSBmb3JlY2FzdGluZyBhY2N1cmFjeSBieSBob3VyCmhvdXJseV9hY2N1cmFjeSA8LSB0ZXN0aW5nX2RhdGEgJT4lCiAgZ3JvdXBfYnkoaG91cikgJT4lCiAgc3VtbWFyaXNlKAogICAgTUFFID0gbWVhbihhYnNvbHV0ZV9lcnJvciksCiAgICBSTVNFID0gc3FydChtZWFuKGZvcmVjYXN0X2Vycm9yXjIpKSwKICAgIG1lYW5fZXJyb3IgPSBtZWFuKGZvcmVjYXN0X2Vycm9yKSwKICAgIG9ic2VydmF0aW9ucyA9IG4oKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApCgojIERpc3BsYXkgcmVzdWx0cwprbml0cjo6a2FibGUoCiAgaG91cmx5X2FjY3VyYWN5LAogIGRpZ2l0cyA9IDIsCiAgY2FwdGlvbiA9ICJGb3JlY2FzdGluZyBBY2N1cmFjeSBieSBIb3VyIG9mIERheSIKKQoKIyBTZWxlY3QgdGhlIGZpbmFsIHNldmVuIGRheXMgb2YgdGhlIHRlc3RpbmcgcGVyaW9kCnBsb3Rfc3RhcnQgPC0gbWF4KHRlc3RpbmdfZGF0YSRob3VyX3V0YykgLSBkYXlzKDcpCgpmb3JlY2FzdF9wbG90X2RhdGEgPC0gdGVzdGluZ19kYXRhICU+JQogIGZpbHRlcihob3VyX3V0YyA+PSBwbG90X3N0YXJ0KSAlPiUKICBzZWxlY3QoCiAgICBob3VyX3V0YywKICAgIGxvYWRfbXcsCiAgICBwcmVkaWN0ZWRfbG9hZAogICkgJT4lCiAgcGl2b3RfbG9uZ2VyKAogICAgY29scyA9IGMobG9hZF9tdywgcHJlZGljdGVkX2xvYWQpLAogICAgbmFtZXNfdG8gPSAic2VyaWVzIiwKICAgIHZhbHVlc190byA9ICJsb2FkIgogICkgJT4lCiAgbXV0YXRlKAogICAgc2VyaWVzID0gcmVjb2RlKAogICAgICBzZXJpZXMsCiAgICAgIGxvYWRfbXcgPSAiQWN0dWFsIExvYWQiLAogICAgICBwcmVkaWN0ZWRfbG9hZCA9ICJQcmVkaWN0ZWQgTG9hZCIKICAgICkKICApCgojIFBsb3QgYWN0dWFsIGFuZCBwcmVkaWN0ZWQgbG9hZApnZ3Bsb3QoCiAgZm9yZWNhc3RfcGxvdF9kYXRhLAogIGFlcyh4ID0gaG91cl91dGMsIHkgPSBsb2FkLCBjb2xvciA9IHNlcmllcykKKSArCiAgZ2VvbV9saW5lKGxpbmV3aWR0aCA9IDAuNykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCgKICAgIHZhbHVlcyA9IGMoCiAgICAgICJBY3R1YWwgTG9hZCIgPSAiIzAwNzJCMiIsCiAgICAgICJQcmVkaWN0ZWQgTG9hZCIgPSAiI0Q1NUUwMCIKICAgICkKICApICsKICBsYWJzKAogICAgdGl0bGUgPSAiQWN0dWFsIHZzLiBQcmVkaWN0ZWQgQlBBIExvYWQiLAogICAgc3VidGl0bGUgPSAiRmluYWwgc2V2ZW4gZGF5cyBvZiB0aGUgdGVzdGluZyBwZXJpb2QiLAogICAgeCA9ICJEYXRlIiwKICAgIHkgPSAiTG9hZCAoTVcpIiwKICAgIGNvbG9yID0gTlVMTAogICkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIgogICkKYGBgCgoK