Overview
We begin by shifting our focus from statistical inference to
predictive modeling. Our primary concern changes from “Is variable \(X\) associated with variable \(Y\)?” to “How accurately can we estimate
future values of \(Y\) given an influx
of new, unseen data \(X\)?”
By the end of this hour, you will be able to:
Split data into training and testing partitions using modern
tidyverse-adjacent frameworks
(rsample).
Construct, interpret, and evaluate a multiple linear regression
model using lm().
Evaluate a model’s predictive performance using quantitative
metrics (RMSE, MAE, \(R^2\)).
Critically assess core statistical assumptions to protect our
model from overfitting and operational failure.
What is R?
Before we build complex predictive frameworks, let’s demystify the
environment we are working in. If you strip away the advanced machine
learning algorithms, R operates on a surprisingly simple three-tier
hierarchy.
At its core, R is just a high-powered calculator
When you type an expression into the console, R evaluates it and
immediately returns an output. It follows standard mathematical orders
of operation.
# R evaluates basic arithmetic operations instantly
2 + 2
[1] 4
# More complex scaling calculation
(300 * 0.045) + (50 * 0.18) - 5
[1] 17.5
R is organized around Functions
To scale beyond basic arithmetic, we use Functions. Think of a
function as a reusable machine: you give it an input (arguments), it
executes an underlying mathematical formula, and it returns an
output.
Instead of manually calculating a mean by adding elements and
dividing, we invoke a named function:
# Defining a quick vector of ad spends
sample_spend <- c(100, 150, 200, 300)
# Passing the vector into a built-in statistical function
mean(sample_spend)
[1] 187.5
Functions are organized into Packages
R comes with a “base” suite of tools, but the true power of the
language lies in its open-source ecosystem.
Developers wrap collections of specialized functions, datasets, and
documentation into cohesive bundles called Packages.
Think of R as the smartphone operating system, and packages as the
apps you download to give it new capabilities.
For example, standard R can fit a linear model, but we load the
tidyverse package to get advanced data manipulation
(mutate(), filter()) and elegant data
visualization (ggplot2).
We’ll leverage these packages to build a predictive data
pipeline.
The Scenario & Data Architecture
Imagine you are the Lead Data Scientist at a consumer goods firm. The
CMO has provided an advertising budget dataset
(marketing_data) outlining expenditures across three media
channels: YouTube, Facebook, and Print Media
(expressed in thousands of dollars), along with the resulting
Sales (in thousands of units).
Our goal is to build a model that predicts sales based on a
prospective advertising budget allocation.
Generating the Sandbox Data
To ensure total reproducibility, we will simulate a realistic
corporate dataset modeling these non-linear corporate dynamics.
# Set seed for reproducibility
set.seed(42)
n <- 200
marketing_data <- tibble(
youtube = runif(n, 10, 300),
facebook = runif(n, 5, 50),
newspaper = runif(n, 1, 40)
) %>%
mutate(
# Base sales = 5,000 units. Synergistic/linear impacts added with random Gaussian noise
sales = 5 + (0.045 * youtube) + (0.18 * facebook) + (0.005 * newspaper) + rnorm(n, 0, 2)
)
# Preview the architecture
glimpse(marketing_data)
Rows: 200
Columns: 4
$ youtube <dbl> 275.29375, 281.75187, 92.98047, 250.82981, 196.10620, 160.53783, 22…
$ facebook <dbl> 44.830296, 28.269997, 43.336894, 24.925832, 12.104605, 24.904609, 4…
$ newspaper <dbl> 1.885300, 21.016342, 25.598320, 17.332093, 35.291372, 5.211496, 39.…
$ sales <dbl> 25.457857, 24.393000, 17.190735, 22.330796, 15.887119, 16.617315, 2…
Exploratory Data Analysis (EDA)
Before fitting any predictive model, we must visualize our
distributions and relationships. We want to identify raw trends and
potential collinearity early.
# Pivot longer to generate clean, faceted ggplot visualizations
marketing_data %>%
pivot_longer(cols = c(youtube, facebook, newspaper),
names_to = "channel",
values_to = "budget") %>%
ggplot(aes(x = budget, y = sales)) +
geom_point(alpha = 0.6, color = "#636363") +
geom_smooth(method = "lm", se = TRUE, color = "#de2d26", linetype = "dashed") +
facet_wrap(~ channel, scales = "free_x") +
theme_minimal(base_size = 14) +
labs(
title = "Sales Volume vs. Advertising Spend by Channel",
subtitle = "Initial linear trend extraction for predictive viability",
x = "Ad Budget (Thousands of $)",
y = "Sales (Thousands of Units)"
)

Say Something Smart: I see strong, clean linear
trends for youtube and facebook. newspaper appears highly scattered
around the trendline, signaling that it might act as statistical noise
in our predictive model.
Fitting the Model
Let’s start with what you know. We will use 100% of our data to build
the model, and then use that exact same data to assess how “accurate” we
are.
\[ \text{Sales} = \beta_0 +
\beta_1(\text{YouTube}) + \beta_2(\text{Facebook}) +
\beta_3(\text{Newspaper}) + \epsilon \]
# Model fitting using the entire dataset
full_model <- lm(sales ~ youtube + facebook + newspaper, data = marketing_data)
# Tidy the model for later
model_coefficients <- broom::tidy(full_model)
tidy(full_model)
Whoa that’s ugly, we can clean it up
# Tidy, format, and add custom significance stars
full_model %>%
tidy() %>%
mutate(
# Round statistical estimates for readability
estimate = round(estimate, 3),
std.error = round(std.error, 3),
statistic = round(statistic, 2),
# Create custom significance stars based on p-value thresholds
stars = case_when(
p.value < 0.001 ~ "***",
p.value < 0.01 ~ "**",
p.value < 0.05 ~ "*",
p.value < 0.1 ~ ".",
TRUE ~ ""
),
# Format p-value column cleanly (handling incredibly small values)
p.value = if_else(p.value < 0.001, "< 0.001", as.character(round(p.value, 4))),
# Combine p-value and stars into a single clean column
`p-value` = paste(p.value, stars)
) %>%
select(Term = term, Estimate = estimate, `Std. Error` = std.error, `t-statistic` = statistic, `p-value`)
gt_summary has some nice functions for displaying
regression results
tbl_regression(full_model)
| Characteristic |
Beta |
95% CI |
p-value |
| youtube |
0.04 |
0.04, 0.05 |
<0.001 |
| facebook |
0.20 |
0.18, 0.22 |
<0.001 |
| newspaper |
0.01 |
-0.01, 0.04 |
0.2 |
| Abbreviation: CI = Confidence Interval |
Deconstructing the Output
YouTube (\(\beta_1\) =
0.042): Holding all other ad spend constant, every $1,000
increase in the YouTube budget yields an estimated increase of 42 units
sold.
Facebook (\(\beta_2\) =
0.202: Every $1,000 invested drives an estimated 202 units
sold, assuming other variables remain static.
Notice that Facebook exhibits a massive marginal return compared to
YouTube. In a business context, if the CMO has an unallocated budget,
capital should pivot aggressively toward Facebook to maximize rapid
acquisition scaling.
The Remedy: Train vs. Test Partitioning
To protect our firm from operational failure, we must evaluate our
model on unseen data. We use the rsample package to
partition our original dataset into an 80% Training Set (to build the
model) and a 20% Testing Set (held back completely as our proxy for the
future).
# Using rsample for rigorous partitioning
set.seed(123)
data_split <- initial_split(marketing_data, prop = 0.80)
train_data <- training(data_split)
test_data <- testing(data_split)
# Re-fit the model ONLY using the training slice
split_model <- lm(sales ~ youtube + facebook + newspaper, data = train_data)
The Ultimate Comparison: Train vs. Test Error
Let’s look at how the model performs on the data it was allowed to
see (Train) versus how it performs on the data we hid from it
(Test).
# 1. Predictions and Metrics on the Training Data (Pulling just the raw number)
train_pred <- train_data %>% add_predictions(split_model, var = "pred_sales")
metrics_train_rmse <- train_pred %>%
yardstick::rmse(truth = sales, estimate = pred_sales) %>%
pull(.estimate)
metrics_train_mae <- train_pred %>%
yardstick::mae(truth = sales, estimate = pred_sales) %>%
pull(.estimate)
metrics_train_rsq <- train_pred %>%
yardstick::rsq(truth = sales, estimate = pred_sales) %>%
pull(.estimate)
# 2. Predictions and Metrics on the Test Data (Pulling just the raw number)
test_pred <- test_data %>% add_predictions(split_model, var = "pred_sales")
metrics_test_rmse <- test_pred %>%
yardstick::rmse(truth = sales, estimate = pred_sales) %>%
pull(.estimate)
metrics_test_mae <- test_pred %>%
yardstick::mae(truth = sales, estimate = pred_sales) %>%
pull(.estimate)
metrics_test_rsq <- test_pred %>%
yardstick::rsq(truth = sales, estimate = pred_sales) %>%
pull(.estimate)
# 3. Build and format the comparison table
comparison_table <- tibble(
Metric = c("RMSE (Standard Deviation of Error)", "MAE (Average Absolute Error)", "Rsq (Coefficient of Determination)"),
`Training Data (Seen)` = c(metrics_train_rmse, metrics_train_mae, metrics_train_rsq),
`Testing Data (Unseen)` = c(metrics_test_rmse, metrics_test_mae, metrics_test_rsq)
) %>%
mutate(across(where(is.numeric), ~ round(.x, 3)))
# 4. Render the table beautifully for the lecture slides/HTML document
datatable(comparison_table, caption = "Out-of-Sample Performance Comparison")
While this result runs counter to the standard textbook rule that
“training error is always lower than test error,” seeing a
slightly lower test error is a very common phenomenon in practice. It
does not mean our model is broken or that our code is wrong.
Random Sampling Luck (The “Easy Test” Effect)
When we split a dataset into an 80/20 partition,
rsample::initial_split() assigns observations completely at
random.
Because our testing set only contains 20% of the data (40
observations out of 200), it is highly susceptible to sampling
variance. By pure random chance, the 40 observations that
landed in our test set happen to cluster slightly closer to the true
regression line, containing fewer extreme outliers or anomalous “noise”
points than the training set.
Think of it like a professor writing an exam: the training set was a
comprehensive, rugged study guide with a few incredibly hard trick
questions (outliers). The test set happened to randomly select 40 of the
more straightforward questions. The model didn’t suddenly get smarter;
it just took a slightly “easier” test.
What This Tells You About our Model
Seeing our test metrics match or beat our training metrics tells you
three highly positive things about our predictive architecture:
Zero Overfitting: If our model had overfit, it
would have memorized the training data’s noise, causing the training
error to be tiny (e.g., 0.5) and the test error to explode (e.g., 4.5).
The fact that they are tightly bounded (1.907 vs 1.661) proves our model
captured the true underlying signal without over-indexing on
sample quirks.
High Structural Stability: our model is
incredibly stable. The parameters (\(\beta\) weights) estimated on our training
data generalize beautifully to independent data.
The Variance is Small: The difference between an
RMSE of 1.91 and 1.66 on a sample size of this scale is statistically
negligible. It is well within the expected bounds of standard sampling
error.
How to Turn This Into a Brilliant Lecture Takeaway
This is the ultimate setup for our take-home exercise or live
demonstration. You can show our students how to prove that this is just
a quirk of random sampling variance by introducing
Cross-Validation or Re-sampling.
Tell our students: “Look at how the metrics shift if we change
the random seed that slices the data.”
If you rerun the split with a few different seeds, watch how the
relationship bounces around naturally:
# Seed 123 (current split) : Test Error < Training Error (Lucky Test Split)
# Seed 456 : Test Error > Training Error (Standard Textbook Split)
# Seed 789 : Test Error ≈ Training Error (Perfect Equilibrium Split)
The test error isn’t a fixed mathematical law but a random variable
with its own distribution, you elevate their understanding from basic
formula-following to true predictive data scientists. our model is
robust, our code is working perfectly, and here’s a real-world data
talking point for today’s class
Analyzing the Results in a Business Context
Usually when lm() optimizes parameters, it minimizes
errors specifically for the training sample’s unique quirks and random
noise. When confronted with the Testing Data, that specific noise is
gone. Only the true underlying signal remains. If our testing error is
close to our training error (as it is here), you have built a robust
model that can be safely used to project future sales for the corporate
marketing strategy. Generally, Testing Error is higher than the Training
Error. This is the fundamental reality of predictive modeling.
Diagnostic Audits: Visualizing the Errors (10 Minutes)
Let’s visualize our testing errors using ggplot2 to ensure our linear
assumptions hold up out-of-sample.
ggplot(test_pred, aes(x = pred_sales, y = sales)) +
geom_point(color = "#636363", size = 2, alpha = 0.8) +
geom_abline(intercept = 0, slope = 1, color = "#de2d26", linetype = "dashed", size = 1) +
theme_minimal(base_size = 14) +
labs(
title = "Actual Sales vs. Out-of-Sample Predictions",
subtitle = "The dashed red line represents perfect prediction",
x = "Predicted Sales (Unseen Data)",
y = "Actual Sales (Unseen Data)"
)

Summary for the C-Suite
By establishing foundational programming elements, making a
structural evaluation mistake, and correcting it through out-of-sample
validation, we proved our model’s practical utility. We can confidently
tell executive leadership that when deploying this model out into the
wild, our average forecasting error will be approximately 1.31 thousand
units.
Mini-Exercise
Add a highly complex, unnecessary polynomial term to the model (e.g.,
I(youtube^3)).
Re-calculate the Full Training Error and the Test Error. Watch how
the Training Error drops even lower, while the Test Error spikes wildly.
This will showcase extreme overfitting in action!
LS0tDQp0aXRsZTogIkxpbmVhciBSZWdyZXNzaW9uIGluIGEgUHJlZGljdGlvbiBDb250ZXh0Ig0Kc3VidGl0bGU6ICdTVEEgNjU0MzogUHJlZGljdGl2ZSBNb2RlbGluZycNCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICB0b2NfZGVwdGg6IDMNCiAgICB0aGVtZTogY2VydWxlYW4NCiAgICBoaWdobGlnaHQ6IHRhbmdvDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNSkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShicm9vbSkNCmxpYnJhcnkobW9kZWxyKQ0KbGlicmFyeShyc2FtcGxlKQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkobW9kZWxzdW1tYXJ5KQ0KbGlicmFyeShndHN1bW1hcnkpDQpsaWJyYXJ5KERUKQ0KYGBgDQoNCiMgT3ZlcnZpZXcgDQpXZSBiZWdpbiBieSBzaGlmdGluZyBvdXIgZm9jdXMgZnJvbSAgc3RhdGlzdGljYWwgaW5mZXJlbmNlIHRvIHByZWRpY3RpdmUgbW9kZWxpbmcuIE91ciBwcmltYXJ5IGNvbmNlcm4gY2hhbmdlcyBmcm9tICJJcyB2YXJpYWJsZSAkWCQgYXNzb2NpYXRlZCB3aXRoIHZhcmlhYmxlICRZJD8iIHRvICJIb3cgYWNjdXJhdGVseSBjYW4gd2UgZXN0aW1hdGUgZnV0dXJlIHZhbHVlcyBvZiAkWSQgZ2l2ZW4gYW4gaW5mbHV4IG9mIG5ldywgdW5zZWVuIGRhdGEgJFgkPyINCg0KQnkgdGhlIGVuZCBvZiB0aGlzIGhvdXIsIHlvdSB3aWxsIGJlIGFibGUgdG86ICANCg0KMS4gU3BsaXQgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHBhcnRpdGlvbnMgdXNpbmcgbW9kZXJuIGB0aWR5dmVyc2VgLWFkamFjZW50IGZyYW1ld29ya3MgKGByc2FtcGxlYCkuICAgDQoNCjIuIENvbnN0cnVjdCwgaW50ZXJwcmV0LCBhbmQgZXZhbHVhdGUgYSBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB1c2luZyBgbG0oKWAuICANCg0KMy4gRXZhbHVhdGUgYSBtb2RlbCdzIHByZWRpY3RpdmUgcGVyZm9ybWFuY2UgdXNpbmcgcXVhbnRpdGF0aXZlIG1ldHJpY3MgKFJNU0UsIE1BRSwgJFJeMiQpLiAgDQoNCjQuIENyaXRpY2FsbHkgYXNzZXNzIGNvcmUgc3RhdGlzdGljYWwgYXNzdW1wdGlvbnMgdG8gcHJvdGVjdCBvdXIgbW9kZWwgZnJvbSBvdmVyZml0dGluZyBhbmQgb3BlcmF0aW9uYWwgZmFpbHVyZS4gIA0KDQojIFdoYXQgaXMgUj8NCkJlZm9yZSB3ZSBidWlsZCBjb21wbGV4IHByZWRpY3RpdmUgZnJhbWV3b3JrcywgbGV04oCZcyBkZW15c3RpZnkgdGhlIGVudmlyb25tZW50IHdlIGFyZSB3b3JraW5nIGluLiBJZiB5b3Ugc3RyaXAgYXdheSB0aGUgYWR2YW5jZWQgbWFjaGluZSBsZWFybmluZyBhbGdvcml0aG1zLCBSIG9wZXJhdGVzIG9uIGEgc3VycHJpc2luZ2x5IHNpbXBsZSB0aHJlZS10aWVyIGhpZXJhcmNoeS4NCg0KIyMgQXQgaXRzIGNvcmUsIFIgaXMganVzdCBhIGhpZ2gtcG93ZXJlZCBjYWxjdWxhdG9yDQpXaGVuIHlvdSB0eXBlIGFuIGV4cHJlc3Npb24gaW50byB0aGUgY29uc29sZSwgUiBldmFsdWF0ZXMgaXQgYW5kIGltbWVkaWF0ZWx5IHJldHVybnMgYW4gb3V0cHV0LiBJdCBmb2xsb3dzIHN0YW5kYXJkIG1hdGhlbWF0aWNhbCBvcmRlcnMgb2Ygb3BlcmF0aW9uLg0KDQpgYGB7cn0NCiMgUiBldmFsdWF0ZXMgYmFzaWMgYXJpdGhtZXRpYyBvcGVyYXRpb25zIGluc3RhbnRseQ0KMiArIDINCg0KIyBNb3JlIGNvbXBsZXggc2NhbGluZyBjYWxjdWxhdGlvbg0KKDMwMCAqIDAuMDQ1KSArICg1MCAqIDAuMTgpIC0gNQ0KYGBgDQojIyBSIGlzIG9yZ2FuaXplZCBhcm91bmQgRnVuY3Rpb25zDQpUbyBzY2FsZSBiZXlvbmQgYmFzaWMgYXJpdGhtZXRpYywgd2UgdXNlIEZ1bmN0aW9ucy4gVGhpbmsgb2YgYSBmdW5jdGlvbiBhcyBhIHJldXNhYmxlIG1hY2hpbmU6IHlvdSBnaXZlIGl0IGFuIGlucHV0IChhcmd1bWVudHMpLCBpdCBleGVjdXRlcyBhbiB1bmRlcmx5aW5nIG1hdGhlbWF0aWNhbCBmb3JtdWxhLCBhbmQgaXQgcmV0dXJucyBhbiBvdXRwdXQuDQoNCkluc3RlYWQgb2YgbWFudWFsbHkgY2FsY3VsYXRpbmcgYSBtZWFuIGJ5IGFkZGluZyBlbGVtZW50cyBhbmQgZGl2aWRpbmcsIHdlIGludm9rZSBhIG5hbWVkIGZ1bmN0aW9uOg0KDQpgYGB7cn0NCiMgRGVmaW5pbmcgYSBxdWljayB2ZWN0b3Igb2YgYWQgc3BlbmRzDQpzYW1wbGVfc3BlbmQgPC0gYygxMDAsIDE1MCwgMjAwLCAzMDApDQoNCiMgUGFzc2luZyB0aGUgdmVjdG9yIGludG8gYSBidWlsdC1pbiBzdGF0aXN0aWNhbCBmdW5jdGlvbg0KbWVhbihzYW1wbGVfc3BlbmQpDQpgYGANCiMjIEZ1bmN0aW9ucyBhcmUgb3JnYW5pemVkIGludG8gUGFja2FnZXMNClIgY29tZXMgd2l0aCBhICJiYXNlIiBzdWl0ZSBvZiB0b29scywgYnV0IHRoZSB0cnVlIHBvd2VyIG9mIHRoZSBsYW5ndWFnZSBsaWVzIGluIGl0cyBvcGVuLXNvdXJjZSBlY29zeXN0ZW0uDQoNCkRldmVsb3BlcnMgd3JhcCBjb2xsZWN0aW9ucyBvZiBzcGVjaWFsaXplZCBmdW5jdGlvbnMsIGRhdGFzZXRzLCBhbmQgZG9jdW1lbnRhdGlvbiBpbnRvIGNvaGVzaXZlIGJ1bmRsZXMgY2FsbGVkIFBhY2thZ2VzLg0KDQpUaGluayBvZiBSIGFzIHRoZSBzbWFydHBob25lIG9wZXJhdGluZyBzeXN0ZW0sIGFuZCBwYWNrYWdlcyBhcyB0aGUgYXBwcyB5b3UgZG93bmxvYWQgdG8gZ2l2ZSBpdCBuZXcgY2FwYWJpbGl0aWVzLg0KDQpGb3IgZXhhbXBsZSwgc3RhbmRhcmQgUiBjYW4gZml0IGEgbGluZWFyIG1vZGVsLCBidXQgd2UgbG9hZCB0aGUgYHRpZHl2ZXJzZWAgcGFja2FnZSB0byBnZXQgYWR2YW5jZWQgZGF0YSBtYW5pcHVsYXRpb24gKGBtdXRhdGUoKWAsIGBmaWx0ZXIoKWApIGFuZCBlbGVnYW50IGRhdGEgdmlzdWFsaXphdGlvbiAoYGdncGxvdDJgKS4NCg0KV2UnbGwgbGV2ZXJhZ2UgdGhlc2UgcGFja2FnZXMgdG8gYnVpbGQgYSBwcmVkaWN0aXZlIGRhdGEgcGlwZWxpbmUuDQoNCiMgVGhlIFNjZW5hcmlvICYgRGF0YSBBcmNoaXRlY3R1cmUgDQoNCkltYWdpbmUgeW91IGFyZSB0aGUgTGVhZCBEYXRhIFNjaWVudGlzdCBhdCBhIGNvbnN1bWVyIGdvb2RzIGZpcm0uIFRoZSBDTU8gaGFzIHByb3ZpZGVkIGFuIGFkdmVydGlzaW5nIGJ1ZGdldCBkYXRhc2V0IChgbWFya2V0aW5nX2RhdGFgKSBvdXRsaW5pbmcgZXhwZW5kaXR1cmVzIGFjcm9zcyB0aHJlZSBtZWRpYSBjaGFubmVsczogX1lvdVR1YmVfLCBfRmFjZWJvb2tfLCBhbmQgX1ByaW50IE1lZGlhXyAoZXhwcmVzc2VkIGluIHRob3VzYW5kcyBvZiBkb2xsYXJzKSwgYWxvbmcgd2l0aCB0aGUgcmVzdWx0aW5nIF9TYWxlc18gKGluIHRob3VzYW5kcyBvZiB1bml0cykuDQoNCk91ciBnb2FsIGlzIHRvIGJ1aWxkIGEgbW9kZWwgdGhhdCBwcmVkaWN0cyBzYWxlcyBiYXNlZCBvbiBhIHByb3NwZWN0aXZlIGFkdmVydGlzaW5nIGJ1ZGdldCBhbGxvY2F0aW9uLg0KDQojIyBHZW5lcmF0aW5nIHRoZSBTYW5kYm94IERhdGENClRvIGVuc3VyZSB0b3RhbCByZXByb2R1Y2liaWxpdHksIHdlIHdpbGwgc2ltdWxhdGUgYSByZWFsaXN0aWMgY29ycG9yYXRlIGRhdGFzZXQgbW9kZWxpbmcgdGhlc2Ugbm9uLWxpbmVhciBjb3Jwb3JhdGUgZHluYW1pY3MuDQoNCmBgYHtyfQ0KIyBTZXQgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5DQpzZXQuc2VlZCg0MikNCg0KbiA8LSAyMDANCm1hcmtldGluZ19kYXRhIDwtIHRpYmJsZSgNCiAgeW91dHViZSAgID0gcnVuaWYobiwgMTAsIDMwMCksDQogIGZhY2Vib29rICA9IHJ1bmlmKG4sIDUsIDUwKSwNCiAgbmV3c3BhcGVyID0gcnVuaWYobiwgMSwgNDApDQopICU+JSANCiAgbXV0YXRlKA0KICAgICMgQmFzZSBzYWxlcyA9IDUsMDAwIHVuaXRzLiBTeW5lcmdpc3RpYy9saW5lYXIgaW1wYWN0cyBhZGRlZCB3aXRoIHJhbmRvbSBHYXVzc2lhbiBub2lzZQ0KICAgIHNhbGVzID0gNSArICgwLjA0NSAqIHlvdXR1YmUpICsgKDAuMTggKiBmYWNlYm9vaykgKyAoMC4wMDUgKiBuZXdzcGFwZXIpICsgcm5vcm0obiwgMCwgMikNCiAgKQ0KDQojIFByZXZpZXcgdGhlIGFyY2hpdGVjdHVyZQ0KZ2xpbXBzZShtYXJrZXRpbmdfZGF0YSkNCmBgYA0KIyMgRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcyAoRURBKSAgDQoNCkJlZm9yZSBmaXR0aW5nIGFueSBwcmVkaWN0aXZlIG1vZGVsLCB3ZSBtdXN0IHZpc3VhbGl6ZSBvdXIgZGlzdHJpYnV0aW9ucyBhbmQgcmVsYXRpb25zaGlwcy4gV2Ugd2FudCB0byBpZGVudGlmeSByYXcgdHJlbmRzIGFuZCBwb3RlbnRpYWwgY29sbGluZWFyaXR5IGVhcmx5Lg0KDQpgYGB7cn0NCiMgUGl2b3QgbG9uZ2VyIHRvIGdlbmVyYXRlIGNsZWFuLCBmYWNldGVkIGdncGxvdCB2aXN1YWxpemF0aW9ucw0KbWFya2V0aW5nX2RhdGEgJT4lDQogIHBpdm90X2xvbmdlcihjb2xzID0gYyh5b3V0dWJlLCBmYWNlYm9vaywgbmV3c3BhcGVyKSwgDQogICAgICAgICAgICAgICBuYW1lc190byA9ICJjaGFubmVsIiwgDQogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiYnVkZ2V0IikgJT4lDQogIGdncGxvdChhZXMoeCA9IGJ1ZGdldCwgeSA9IHNhbGVzKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC42LCBjb2xvciA9ICIjNjM2MzYzIikgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IFRSVUUsIGNvbG9yID0gIiNkZTJkMjYiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGZhY2V0X3dyYXAofiBjaGFubmVsLCBzY2FsZXMgPSAiZnJlZV94IikgKw0KICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDE0KSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiU2FsZXMgVm9sdW1lIHZzLiBBZHZlcnRpc2luZyBTcGVuZCBieSBDaGFubmVsIiwNCiAgICBzdWJ0aXRsZSA9ICJJbml0aWFsIGxpbmVhciB0cmVuZCBleHRyYWN0aW9uIGZvciBwcmVkaWN0aXZlIHZpYWJpbGl0eSIsDQogICAgeCA9ICJBZCBCdWRnZXQgKFRob3VzYW5kcyBvZiAkKSIsDQogICAgeSA9ICJTYWxlcyAoVGhvdXNhbmRzIG9mIFVuaXRzKSINCiAgKQ0KYGBgDQoNCl9fU2F5IFNvbWV0aGluZyBTbWFydF9fOiBJIHNlZSBzdHJvbmcsIGNsZWFuIGxpbmVhciB0cmVuZHMgZm9yIHlvdXR1YmUgYW5kIGZhY2Vib29rLiBuZXdzcGFwZXIgYXBwZWFycyBoaWdobHkgc2NhdHRlcmVkIGFyb3VuZCB0aGUgdHJlbmRsaW5lLCBzaWduYWxpbmcgdGhhdCBpdCBtaWdodCBhY3QgYXMgc3RhdGlzdGljYWwgbm9pc2UgaW4gb3VyIHByZWRpY3RpdmUgbW9kZWwuIA0KDQojIEZpdHRpbmcgdGhlIE1vZGVsDQoNCkxldCdzIHN0YXJ0IHdpdGggd2hhdCB5b3Uga25vdy4gV2Ugd2lsbCB1c2UgMTAwJSBvZiBvdXIgZGF0YSB0byBidWlsZCB0aGUgbW9kZWwsIGFuZCB0aGVuIHVzZSB0aGF0IGV4YWN0IHNhbWUgZGF0YSB0byBhc3Nlc3MgaG93ICJhY2N1cmF0ZSIgd2UgYXJlLg0KDQokJCBcdGV4dHtTYWxlc30gPSBcYmV0YV8wICsgXGJldGFfMShcdGV4dHtZb3VUdWJlfSkgKyBcYmV0YV8yKFx0ZXh0e0ZhY2Vib29rfSkgKyBcYmV0YV8zKFx0ZXh0e05ld3NwYXBlcn0pICsgXGVwc2lsb24gJCQNCmBgYHtyfQ0KIyBNb2RlbCBmaXR0aW5nIHVzaW5nIHRoZSBlbnRpcmUgZGF0YXNldA0KZnVsbF9tb2RlbCA8LSBsbShzYWxlcyB+IHlvdXR1YmUgKyBmYWNlYm9vayArIG5ld3NwYXBlciwgZGF0YSA9IG1hcmtldGluZ19kYXRhKQ0KDQojIFRpZHkgdGhlIG1vZGVsIGZvciBsYXRlcg0KbW9kZWxfY29lZmZpY2llbnRzIDwtIGJyb29tOjp0aWR5KGZ1bGxfbW9kZWwpDQoNCnRpZHkoZnVsbF9tb2RlbCkNCmBgYA0KV2hvYSB0aGF0J3MgdWdseSwgd2UgY2FuIGNsZWFuIGl0IHVwDQoNCmBgYHtyfQ0KIyBUaWR5LCBmb3JtYXQsIGFuZCBhZGQgY3VzdG9tIHNpZ25pZmljYW5jZSBzdGFycw0KZnVsbF9tb2RlbCAlPiUNCiAgdGlkeSgpICU+JQ0KICBtdXRhdGUoDQogICAgIyBSb3VuZCBzdGF0aXN0aWNhbCBlc3RpbWF0ZXMgZm9yIHJlYWRhYmlsaXR5DQogICAgZXN0aW1hdGUgICA9IHJvdW5kKGVzdGltYXRlLCAzKSwNCiAgICBzdGQuZXJyb3IgID0gcm91bmQoc3RkLmVycm9yLCAzKSwNCiAgICBzdGF0aXN0aWMgID0gcm91bmQoc3RhdGlzdGljLCAyKSwNCiAgICANCiAgICAjIENyZWF0ZSBjdXN0b20gc2lnbmlmaWNhbmNlIHN0YXJzIGJhc2VkIG9uIHAtdmFsdWUgdGhyZXNob2xkcw0KICAgIHN0YXJzID0gY2FzZV93aGVuKA0KICAgICAgcC52YWx1ZSA8IDAuMDAxIH4gIioqKiIsDQogICAgICBwLnZhbHVlIDwgMC4wMSAgfiAiKioiLA0KICAgICAgcC52YWx1ZSA8IDAuMDUgIH4gIioiLA0KICAgICAgcC52YWx1ZSA8IDAuMSAgIH4gIi4iLA0KICAgICAgVFJVRSAgICAgICAgICAgIH4gIiINCiAgICApLA0KICAgIA0KICAgICMgRm9ybWF0IHAtdmFsdWUgY29sdW1uIGNsZWFubHkgKGhhbmRsaW5nIGluY3JlZGlibHkgc21hbGwgdmFsdWVzKQ0KICAgIHAudmFsdWUgPSBpZl9lbHNlKHAudmFsdWUgPCAwLjAwMSwgIjwgMC4wMDEiLCBhcy5jaGFyYWN0ZXIocm91bmQocC52YWx1ZSwgNCkpKSwNCiAgICANCiAgICAjIENvbWJpbmUgcC12YWx1ZSBhbmQgc3RhcnMgaW50byBhIHNpbmdsZSBjbGVhbiBjb2x1bW4NCiAgICBgcC12YWx1ZWAgPSBwYXN0ZShwLnZhbHVlLCBzdGFycykNCiAgKSAlPiUNCiAgc2VsZWN0KFRlcm0gPSB0ZXJtLCBFc3RpbWF0ZSA9IGVzdGltYXRlLCBgU3RkLiBFcnJvcmAgPSBzdGQuZXJyb3IsIGB0LXN0YXRpc3RpY2AgPSBzdGF0aXN0aWMsIGBwLXZhbHVlYCkNCmBgYA0KDQpgZ3Rfc3VtbWFyeWAgaGFzIHNvbWUgbmljZSBmdW5jdGlvbnMgZm9yIGRpc3BsYXlpbmcgcmVncmVzc2lvbiByZXN1bHRzDQoNCmBgYHtyfQ0KdGJsX3JlZ3Jlc3Npb24oZnVsbF9tb2RlbCkNCmBgYA0KDQojIyBEZWNvbnN0cnVjdGluZyB0aGUgT3V0cHV0DQoNCiAtICoqWW91VHViZSAoJFxiZXRhXzEkID0gMC4wNDIpOioqIEhvbGRpbmcgYWxsIG90aGVyIGFkIHNwZW5kIGNvbnN0YW50LCBldmVyeSAkMSwwMDAgaW5jcmVhc2UgaW4gdGhlIFlvdVR1YmUgYnVkZ2V0IHlpZWxkcyBhbiBlc3RpbWF0ZWQgaW5jcmVhc2Ugb2YgNDIgdW5pdHMgc29sZC4NCg0KIC0gKipGYWNlYm9vayAoJFxiZXRhXzIkID0gMC4yMDI6KiogRXZlcnkgJDEsMDAwIGludmVzdGVkIGRyaXZlcyBhbiBlc3RpbWF0ZWQgMjAyIHVuaXRzIHNvbGQsIGFzc3VtaW5nIG90aGVyIHZhcmlhYmxlcyByZW1haW4gc3RhdGljLg0KDQpOb3RpY2UgdGhhdCBGYWNlYm9vayBleGhpYml0cyBhIG1hc3NpdmUgbWFyZ2luYWwgcmV0dXJuIGNvbXBhcmVkIHRvIFlvdVR1YmUuIEluIGEgYnVzaW5lc3MgY29udGV4dCwgaWYgdGhlIENNTyBoYXMgYW4gdW5hbGxvY2F0ZWQgYnVkZ2V0LCBjYXBpdGFsIHNob3VsZCBwaXZvdCBhZ2dyZXNzaXZlbHkgdG93YXJkIEZhY2Vib29rIHRvIG1heGltaXplIHJhcGlkIGFjcXVpc2l0aW9uIHNjYWxpbmcuDQogDQojIyBFdmFsdWF0aW5nIFRyYWluaW5nIFBlcmZvcm1hbmNlIE1ldHJpY3MNCg0KTm93LCBsZXQncyBjYWxjdWxhdGUgb3VyIFJvb3QgTWVhbiBTcXVhcmVkIEVycm9yIChSTVNFKSBhbmQgTWVhbiBBYnNvbHV0ZSBFcnJvciAoTUFFKSB1c2luZyB0aGlzIGZ1bGwgZGF0YXNldC4NCg0KYGBge3IgZnVsbF9tb2RlbF9tZXRyaWNzfQ0KIyAxLiBHZW5lcmF0ZSBwcmVkaWN0aW9ucyB1c2luZyBtb2RlbHINCmZ1bGxfcHJlZGljdGlvbnMgPC0gbWFya2V0aW5nX2RhdGEgJT4lDQogIGFkZF9wcmVkaWN0aW9ucyhmdWxsX21vZGVsLCB2YXIgPSAicHJlZF9zYWxlcyIpDQoNCiMgMi4gQ3JlYXRlIGEgdW5pZmllZCB5YXJkc3RpY2sgbWV0cmljIHNldA0KbWFya2V0aW5nX21ldHJpY3MgPC0geWFyZHN0aWNrOjptZXRyaWNfc2V0KHlhcmRzdGljazo6cm1zZSwgeWFyZHN0aWNrOjptYWUsIHlhcmRzdGljazo6cnNxKQ0KDQojIDMuIENhbGN1bGF0ZSBhbGwgbWV0cmljcyBzaW11bHRhbmVvdXNseSBpbnRvIGEgY2xlYW4gZGF0YSBmcmFtZQ0KdHJhaW5pbmdfbWV0cmljc190YWJsZSA8LSBmdWxsX3ByZWRpY3Rpb25zICU+JSANCiAgbWFya2V0aW5nX21ldHJpY3ModHJ1dGggPSBzYWxlcywgZXN0aW1hdGUgPSBwcmVkX3NhbGVzKQ0KDQojIDQuIEV4dHJhY3QgdGhlIHJhdyBudW1lcmljIHZhbHVlcyBmb3Igb3VyIGNhbGxvdXQgdGV4dCBiZWxvdw0KIyAoVXNpbmcgcHVsbCgpIHNhZmVseSBleHRyYWN0cyB0aGUgbnVtYmVyIGZyb20gdGhlIC5lc3RpbWF0ZSBjb2x1bW4pDQp0cmFpbl9ybXNlX251bSA8LSB0cmFpbmluZ19tZXRyaWNzX3RhYmxlICU+JSBmaWx0ZXIoLm1ldHJpYyA9PSAicm1zZSIpICU+JSBwdWxsKC5lc3RpbWF0ZSkNCnRyYWluX21hZV9udW0gIDwtIHRyYWluaW5nX21ldHJpY3NfdGFibGUgJT4lIGZpbHRlcigubWV0cmljID09ICJtYWUiKSAgJT4lIHB1bGwoLmVzdGltYXRlKQ0KdHJhaW5fcjJfbnVtICAgPC0gdHJhaW5pbmdfbWV0cmljc190YWJsZSAlPiUgZmlsdGVyKC5tZXRyaWMgPT0gInJzcSIpICAlPiUgcHVsbCguZXN0aW1hdGUpDQoNCiMgNS4gRm9ybWF0IHRoZSB0YWJsZSBjb2x1bW5zIG5pY2VseSBmb3IgdGhlIGxlY3R1cmUgbGF5b3V0DQp0cmFpbmluZ19tZXRyaWNzX2Zvcm1hdHRlZCA8LSB0cmFpbmluZ19tZXRyaWNzX3RhYmxlICU+JSANCiAgbXV0YXRlKA0KICAgIE1ldHJpYyA9IGMoIlJNU0UgKFN0YW5kYXJkIERldmlhdGlvbiBvZiBFcnJvcikiLCANCiAgICAgICAgICAgICAgICJNQUUgKEF2ZXJhZ2UgQWJzb2x1dGUgRXJyb3IpIiwgDQogICAgICAgICAgICAgICAiUsKyIChDb2VmZmljaWVudCBvZiBEZXRlcm1pbmF0aW9uKSIpLA0KICAgIGBUcmFpbmluZyBQZXJmb3JtYW5jZSBWYWx1ZWAgPSByb3VuZCguZXN0aW1hdGUsIDMpDQogICkgJT4lIA0KICBzZWxlY3QoTWV0cmljLCBgVHJhaW5pbmcgUGVyZm9ybWFuY2UgVmFsdWVgKQ0KDQojIDYuIFJlbmRlciB0aGUgdGFibGUgYmVhdXRpZnVsbHkgdXNpbmcga2FibGUNCmRhdGF0YWJsZSh0cmFpbmluZ19tZXRyaWNzX2Zvcm1hdHRlZCkNCmBgYA0KDQoNCg0KIyMjIFRoZSBJbGx1c2lvbiBvZiBTdWNjZXNzDQoNCk91ciBtb2RlbCBib2FzdHMgYW4gJFJeMiQgb2YgMC44NTUsIG1lYW5pbmcgaXQgZXhwbGFpbnMgODUuNSUgb2YgdGhlIHZhcmlhbmNlIGluIG91ciBkYXRhLiBPbiBhdmVyYWdlLCBvdXIgcHJlZGljdGlvbnMgb25seSBtaXNzIGJ5IDEuNDUgdGhvdXNhbmQgdW5pdHMuDQoNCklmIHlvdSBzaG93IHRoaXMgdG8gdGhlIENNTywgdGhleSBtYXkgYmUgdGhyaWxsZWQuIEJ1dCBtYXRoZW1hdGljYWxseSwgd2UgaGF2ZSBjaGVhdGVkLiBXZSBnYXZlIHRoZSBtb2RlbCB0aGUgYW5zd2VycyB0byB0aGUgdGVzdCBiZWZvcmUgaXQgdG9vayBpdC4gV2UgaGF2ZSBubyBvYmplY3RpdmUgYmFzZWxpbmUgdG8ga25vdyBpZiB0aGlzIG1vZGVsIGdlbmVyYWxpemVzIHRvIG5leHQgcXVhcnRlcidzIHVuc2VlbiBtYXJrZXRpbmcgY2FtcGFpZ25zLg0KDQpJbiBhIHRyYWRpdGlvbmFsIGVjb25vbWV0cmljcyBjbGFzcywgeW91IHVzZSAxMDAlIG9mIHRoZSBkYXRhIHRvIGVzdGltYXRlIG91ciBtb2RlbCBwYXJhbWV0ZXJzLiBJbiBwcmVkaWN0aXZlIG1vZGVsaW5nLCB0aGlzIGlzIGEgY2FyZGluYWwgc2luLiBJdCBsZWFkcyB0byBvdmVyZml0dGluZ+KAlGNhcHR1cmluZyB0aGUgcmFuZG9tIG5vaXNlIG9mIHRoZSBoaXN0b3JpY2FsIGRhdGFzZXQgaW5zdGVhZCBvZiB0aGUgdHJ1ZSB1bmRlcmx5aW5nIHNpZ25hbC4NCg0KV2UgbXVzdCBldmFsdWF0ZSBvdXIgbW9kZWwncyBwZXJmb3JtYW5jZSBvbiB1bnNlZW4gZGF0YS4gV2Ugd2lsbCBwYXJ0aXRpb24gb3VyIGRhdGEgaW50byBhbiA4MCUgVHJhaW5pbmcgU2V0IGFuZCBhIDIwJSBUZXN0aW5nIFNldC4NCg0KIyBUaGUgUmVtZWR5OiBUcmFpbiB2cy4gVGVzdCBQYXJ0aXRpb25pbmcgDQpUbyBwcm90ZWN0IG91ciBmaXJtIGZyb20gb3BlcmF0aW9uYWwgZmFpbHVyZSwgd2UgbXVzdCBldmFsdWF0ZSBvdXIgbW9kZWwgb24gdW5zZWVuIGRhdGEuIFdlIHVzZSB0aGUgYHJzYW1wbGVgIHBhY2thZ2UgdG8gcGFydGl0aW9uIG91ciBvcmlnaW5hbCBkYXRhc2V0IGludG8gYW4gODAlIFRyYWluaW5nIFNldCAodG8gYnVpbGQgdGhlIG1vZGVsKSBhbmQgYSAyMCUgVGVzdGluZyBTZXQgKGhlbGQgYmFjayBjb21wbGV0ZWx5IGFzIG91ciBwcm94eSBmb3IgdGhlIGZ1dHVyZSkuDQoNCmBgYHtyfQ0KIyBVc2luZyByc2FtcGxlIGZvciByaWdvcm91cyBwYXJ0aXRpb25pbmcNCnNldC5zZWVkKDEyMykgDQpkYXRhX3NwbGl0IDwtIGluaXRpYWxfc3BsaXQobWFya2V0aW5nX2RhdGEsIHByb3AgPSAwLjgwKQ0KDQp0cmFpbl9kYXRhIDwtIHRyYWluaW5nKGRhdGFfc3BsaXQpDQp0ZXN0X2RhdGEgIDwtIHRlc3RpbmcoZGF0YV9zcGxpdCkNCg0KIyBSZS1maXQgdGhlIG1vZGVsIE9OTFkgdXNpbmcgdGhlIHRyYWluaW5nIHNsaWNlDQpzcGxpdF9tb2RlbCA8LSBsbShzYWxlcyB+IHlvdXR1YmUgKyBmYWNlYm9vayArIG5ld3NwYXBlciwgZGF0YSA9IHRyYWluX2RhdGEpDQpgYGANCg0KIyMgVGhlIFVsdGltYXRlIENvbXBhcmlzb246IFRyYWluIHZzLiBUZXN0IEVycm9yDQoNCkxldCdzIGxvb2sgYXQgaG93IHRoZSBtb2RlbCBwZXJmb3JtcyBvbiB0aGUgZGF0YSBpdCB3YXMgYWxsb3dlZCB0byBzZWUgKFRyYWluKSB2ZXJzdXMgaG93IGl0IHBlcmZvcm1zIG9uIHRoZSBkYXRhIHdlIGhpZCBmcm9tIGl0IChUZXN0KS4NCg0KYGBge3IgY2FsY3VsYXRlX3NwbGl0X21ldHJpY3MsIHJlc3VsdHM9J2FzaXMnfQ0KIyAxLiBQcmVkaWN0aW9ucyBhbmQgTWV0cmljcyBvbiB0aGUgVHJhaW5pbmcgRGF0YSAoUHVsbGluZyBqdXN0IHRoZSByYXcgbnVtYmVyKQ0KdHJhaW5fcHJlZCA8LSB0cmFpbl9kYXRhICU+JSBhZGRfcHJlZGljdGlvbnMoc3BsaXRfbW9kZWwsIHZhciA9ICJwcmVkX3NhbGVzIikNCg0KbWV0cmljc190cmFpbl9ybXNlIDwtIHRyYWluX3ByZWQgJT4lIA0KICB5YXJkc3RpY2s6OnJtc2UodHJ1dGggPSBzYWxlcywgZXN0aW1hdGUgPSBwcmVkX3NhbGVzKSAlPiUgDQogIHB1bGwoLmVzdGltYXRlKQ0KDQptZXRyaWNzX3RyYWluX21hZSAgPC0gdHJhaW5fcHJlZCAlPiUgDQogIHlhcmRzdGljazo6bWFlKHRydXRoID0gc2FsZXMsIGVzdGltYXRlID0gcHJlZF9zYWxlcykgJT4lIA0KICBwdWxsKC5lc3RpbWF0ZSkNCg0KbWV0cmljc190cmFpbl9yc3EgIDwtIHRyYWluX3ByZWQgJT4lIA0KICB5YXJkc3RpY2s6OnJzcSh0cnV0aCA9IHNhbGVzLCBlc3RpbWF0ZSA9IHByZWRfc2FsZXMpICU+JSANCiAgcHVsbCguZXN0aW1hdGUpDQoNCiMgMi4gUHJlZGljdGlvbnMgYW5kIE1ldHJpY3Mgb24gdGhlIFRlc3QgRGF0YSAoUHVsbGluZyBqdXN0IHRoZSByYXcgbnVtYmVyKQ0KdGVzdF9wcmVkIDwtIHRlc3RfZGF0YSAlPiUgYWRkX3ByZWRpY3Rpb25zKHNwbGl0X21vZGVsLCB2YXIgPSAicHJlZF9zYWxlcyIpDQoNCm1ldHJpY3NfdGVzdF9ybXNlICA8LSB0ZXN0X3ByZWQgJT4lIA0KICB5YXJkc3RpY2s6OnJtc2UodHJ1dGggPSBzYWxlcywgZXN0aW1hdGUgPSBwcmVkX3NhbGVzKSAlPiUgDQogIHB1bGwoLmVzdGltYXRlKQ0KDQptZXRyaWNzX3Rlc3RfbWFlICAgPC0gdGVzdF9wcmVkICU+JSANCiAgeWFyZHN0aWNrOjptYWUodHJ1dGggPSBzYWxlcywgZXN0aW1hdGUgPSBwcmVkX3NhbGVzKSAlPiUgDQogIHB1bGwoLmVzdGltYXRlKQ0KDQptZXRyaWNzX3Rlc3RfcnNxICAgPC0gdGVzdF9wcmVkICU+JSANCiAgeWFyZHN0aWNrOjpyc3EodHJ1dGggPSBzYWxlcywgZXN0aW1hdGUgPSBwcmVkX3NhbGVzKSAlPiUgDQogIHB1bGwoLmVzdGltYXRlKQ0KDQojIDMuIEJ1aWxkIGFuZCBmb3JtYXQgdGhlIGNvbXBhcmlzb24gdGFibGUNCmNvbXBhcmlzb25fdGFibGUgPC0gdGliYmxlKA0KICBNZXRyaWMgPSBjKCJSTVNFIChTdGFuZGFyZCBEZXZpYXRpb24gb2YgRXJyb3IpIiwgIk1BRSAoQXZlcmFnZSBBYnNvbHV0ZSBFcnJvcikiLCAiUnNxIChDb2VmZmljaWVudCBvZiBEZXRlcm1pbmF0aW9uKSIpLA0KICBgVHJhaW5pbmcgRGF0YSAoU2VlbilgID0gYyhtZXRyaWNzX3RyYWluX3Jtc2UsIG1ldHJpY3NfdHJhaW5fbWFlLCBtZXRyaWNzX3RyYWluX3JzcSksDQogIGBUZXN0aW5nIERhdGEgKFVuc2VlbilgID0gYyhtZXRyaWNzX3Rlc3Rfcm1zZSwgbWV0cmljc190ZXN0X21hZSwgbWV0cmljc190ZXN0X3JzcSkNCikgJT4lIA0KICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCB+IHJvdW5kKC54LCAzKSkpDQoNCiMgNC4gUmVuZGVyIHRoZSB0YWJsZSBiZWF1dGlmdWxseSBmb3IgdGhlIGxlY3R1cmUgc2xpZGVzL0hUTUwgZG9jdW1lbnQNCmRhdGF0YWJsZShjb21wYXJpc29uX3RhYmxlLCBjYXB0aW9uID0gIk91dC1vZi1TYW1wbGUgUGVyZm9ybWFuY2UgQ29tcGFyaXNvbiIpDQpgYGANCg0KDQpXaGlsZSB0aGlzIHJlc3VsdCBydW5zIGNvdW50ZXIgdG8gdGhlIHN0YW5kYXJkIHRleHRib29rIHJ1bGUgdGhhdCAqInRyYWluaW5nIGVycm9yIGlzIGFsd2F5cyBsb3dlciB0aGFuIHRlc3QgZXJyb3IsIiogc2VlaW5nIGEgc2xpZ2h0bHkgbG93ZXIgdGVzdCBlcnJvciBpcyBhIHZlcnkgY29tbW9uIHBoZW5vbWVub24gaW4gcHJhY3RpY2UuIEl0IGRvZXMgbm90IG1lYW4gb3VyIG1vZGVsIGlzIGJyb2tlbiBvciB0aGF0IG91ciBjb2RlIGlzIHdyb25nLg0KDQojIyMgUmFuZG9tIFNhbXBsaW5nIEx1Y2sgKFRoZSAiRWFzeSBUZXN0IiBFZmZlY3QpDQoNCldoZW4gd2Ugc3BsaXQgYSBkYXRhc2V0IGludG8gYW4gODAvMjAgcGFydGl0aW9uLCBgcnNhbXBsZTo6aW5pdGlhbF9zcGxpdCgpYCBhc3NpZ25zIG9ic2VydmF0aW9ucyBjb21wbGV0ZWx5IGF0IHJhbmRvbS4NCg0KQmVjYXVzZSBvdXIgdGVzdGluZyBzZXQgb25seSBjb250YWlucyAyMCUgb2YgdGhlIGRhdGEgKDQwIG9ic2VydmF0aW9ucyBvdXQgb2YgMjAwKSwgaXQgaXMgaGlnaGx5IHN1c2NlcHRpYmxlIHRvICoqc2FtcGxpbmcgdmFyaWFuY2UqKi4gQnkgcHVyZSByYW5kb20gY2hhbmNlLCB0aGUgNDAgb2JzZXJ2YXRpb25zIHRoYXQgbGFuZGVkIGluIG91ciB0ZXN0IHNldCBoYXBwZW4gdG8gY2x1c3RlciBzbGlnaHRseSBjbG9zZXIgdG8gdGhlIHRydWUgcmVncmVzc2lvbiBsaW5lLCBjb250YWluaW5nIGZld2VyIGV4dHJlbWUgb3V0bGllcnMgb3IgYW5vbWFsb3VzICJub2lzZSIgcG9pbnRzIHRoYW4gdGhlIHRyYWluaW5nIHNldC4NCg0KVGhpbmsgb2YgaXQgbGlrZSBhIHByb2Zlc3NvciB3cml0aW5nIGFuIGV4YW06IHRoZSB0cmFpbmluZyBzZXQgd2FzIGEgY29tcHJlaGVuc2l2ZSwgcnVnZ2VkIHN0dWR5IGd1aWRlIHdpdGggYSBmZXcgaW5jcmVkaWJseSBoYXJkIHRyaWNrIHF1ZXN0aW9ucyAob3V0bGllcnMpLiBUaGUgdGVzdCBzZXQgaGFwcGVuZWQgdG8gcmFuZG9tbHkgc2VsZWN0IDQwIG9mIHRoZSBtb3JlIHN0cmFpZ2h0Zm9yd2FyZCBxdWVzdGlvbnMuIFRoZSBtb2RlbCBkaWRuJ3Qgc3VkZGVubHkgZ2V0IHNtYXJ0ZXI7IGl0IGp1c3QgdG9vayBhIHNsaWdodGx5ICJlYXNpZXIiIHRlc3QuDQoNCiMjIyBXaGF0IFRoaXMgVGVsbHMgWW91IEFib3V0IG91ciBNb2RlbA0KDQpTZWVpbmcgb3VyIHRlc3QgbWV0cmljcyBtYXRjaCBvciBiZWF0IG91ciB0cmFpbmluZyBtZXRyaWNzIHRlbGxzIHlvdSB0aHJlZSBoaWdobHkgcG9zaXRpdmUgdGhpbmdzIGFib3V0IG91ciBwcmVkaWN0aXZlIGFyY2hpdGVjdHVyZToNCg0KKiAqKlplcm8gT3ZlcmZpdHRpbmc6KiogSWYgb3VyIG1vZGVsIGhhZCBvdmVyZml0LCBpdCB3b3VsZCBoYXZlIG1lbW9yaXplZCB0aGUgdHJhaW5pbmcgZGF0YSdzIG5vaXNlLCBjYXVzaW5nIHRoZSB0cmFpbmluZyBlcnJvciB0byBiZSB0aW55IChlLmcuLCAwLjUpIGFuZCB0aGUgdGVzdCBlcnJvciB0byBleHBsb2RlIChlLmcuLCA0LjUpLiBUaGUgZmFjdCB0aGF0IHRoZXkgYXJlIHRpZ2h0bHkgYm91bmRlZCAoMS45MDcgdnMgMS42NjEpIHByb3ZlcyBvdXIgbW9kZWwgY2FwdHVyZWQgdGhlICp0cnVlIHVuZGVybHlpbmcgc2lnbmFsKiB3aXRob3V0IG92ZXItaW5kZXhpbmcgb24gc2FtcGxlIHF1aXJrcy4NCg0KKiAqKkhpZ2ggU3RydWN0dXJhbCBTdGFiaWxpdHk6Kiogb3VyIG1vZGVsIGlzIGluY3JlZGlibHkgc3RhYmxlLiBUaGUgcGFyYW1ldGVycyAoJFxiZXRhJCB3ZWlnaHRzKSBlc3RpbWF0ZWQgb24gb3VyIHRyYWluaW5nIGRhdGEgZ2VuZXJhbGl6ZSBiZWF1dGlmdWxseSB0byBpbmRlcGVuZGVudCBkYXRhLg0KDQoqICoqVGhlIFZhcmlhbmNlIGlzIFNtYWxsOioqIFRoZSBkaWZmZXJlbmNlIGJldHdlZW4gYW4gUk1TRSBvZiAxLjkxIGFuZCAxLjY2IG9uIGEgc2FtcGxlIHNpemUgb2YgdGhpcyBzY2FsZSBpcyBzdGF0aXN0aWNhbGx5IG5lZ2xpZ2libGUuIEl0IGlzIHdlbGwgd2l0aGluIHRoZSBleHBlY3RlZCBib3VuZHMgb2Ygc3RhbmRhcmQgc2FtcGxpbmcgZXJyb3IuDQoNCi0tLQ0KDQojIyMgSG93IHRvIFR1cm4gVGhpcyBJbnRvIGEgQnJpbGxpYW50IExlY3R1cmUgVGFrZWF3YXkNCg0KVGhpcyBpcyB0aGUgdWx0aW1hdGUgc2V0dXAgZm9yIG91ciB0YWtlLWhvbWUgZXhlcmNpc2Ugb3IgbGl2ZSBkZW1vbnN0cmF0aW9uLiBZb3UgY2FuIHNob3cgb3VyIHN0dWRlbnRzIGhvdyB0byBwcm92ZSB0aGF0IHRoaXMgaXMganVzdCBhIHF1aXJrIG9mIHJhbmRvbSBzYW1wbGluZyB2YXJpYW5jZSBieSBpbnRyb2R1Y2luZyAqKkNyb3NzLVZhbGlkYXRpb24qKiBvciAqKlJlLXNhbXBsaW5nKiouDQoNClRlbGwgb3VyIHN0dWRlbnRzOiAqIkxvb2sgYXQgaG93IHRoZSBtZXRyaWNzIHNoaWZ0IGlmIHdlIGNoYW5nZSB0aGUgcmFuZG9tIHNlZWQgdGhhdCBzbGljZXMgdGhlIGRhdGEuIioNCg0KSWYgeW91IHJlcnVuIHRoZSBzcGxpdCB3aXRoIGEgZmV3IGRpZmZlcmVudCBzZWVkcywgd2F0Y2ggaG93IHRoZSByZWxhdGlvbnNoaXAgYm91bmNlcyBhcm91bmQgbmF0dXJhbGx5Og0KDQpgYGByDQojIFNlZWQgMTIzIChjdXJyZW50IHNwbGl0KSAgOiBUZXN0IEVycm9yIDwgVHJhaW5pbmcgRXJyb3IgKEx1Y2t5IFRlc3QgU3BsaXQpDQojIFNlZWQgNDU2ICAgICAgICAgICAgICAgICAgOiBUZXN0IEVycm9yID4gVHJhaW5pbmcgRXJyb3IgKFN0YW5kYXJkIFRleHRib29rIFNwbGl0KQ0KIyBTZWVkIDc4OSAgICAgICAgICAgICAgICAgIDogVGVzdCBFcnJvciDiiYggVHJhaW5pbmcgRXJyb3IgKFBlcmZlY3QgRXF1aWxpYnJpdW0gU3BsaXQpDQpgYGANCg0KVGhlIHRlc3QgZXJyb3IgaXNuJ3QgYSBmaXhlZCBtYXRoZW1hdGljYWwgbGF3IGJ1dCBhIHJhbmRvbSB2YXJpYWJsZSB3aXRoIGl0cyBvd24gZGlzdHJpYnV0aW9uLCB5b3UgZWxldmF0ZSB0aGVpciB1bmRlcnN0YW5kaW5nIGZyb20gYmFzaWMgZm9ybXVsYS1mb2xsb3dpbmcgdG8gdHJ1ZSBwcmVkaWN0aXZlIGRhdGEgc2NpZW50aXN0cy4gb3VyIG1vZGVsIGlzIHJvYnVzdCwgb3VyIGNvZGUgaXMgd29ya2luZyBwZXJmZWN0bHksIGFuZCBoZXJlJ3MgYSByZWFsLXdvcmxkIGRhdGEgdGFsa2luZyBwb2ludCBmb3IgdG9kYXkncyBjbGFzcw0KDQojIEFuYWx5emluZyB0aGUgUmVzdWx0cyBpbiBhIEJ1c2luZXNzIENvbnRleHQNCg0KVXN1YWxseSB3aGVuIGBsbSgpYCBvcHRpbWl6ZXMgcGFyYW1ldGVycywgaXQgbWluaW1pemVzIGVycm9ycyBzcGVjaWZpY2FsbHkgZm9yIHRoZSB0cmFpbmluZyBzYW1wbGUncyB1bmlxdWUgcXVpcmtzIGFuZCByYW5kb20gbm9pc2UuIFdoZW4gY29uZnJvbnRlZCB3aXRoIHRoZSBUZXN0aW5nIERhdGEsIHRoYXQgc3BlY2lmaWMgbm9pc2UgaXMgZ29uZS4gT25seSB0aGUgdHJ1ZSB1bmRlcmx5aW5nIHNpZ25hbCByZW1haW5zLiBJZiBvdXIgdGVzdGluZyBlcnJvciBpcyBjbG9zZSB0byBvdXIgdHJhaW5pbmcgZXJyb3IgKGFzIGl0IGlzIGhlcmUpLCB5b3UgaGF2ZSBidWlsdCBhIHJvYnVzdCBtb2RlbCB0aGF0IGNhbiBiZSBzYWZlbHkgdXNlZCB0byBwcm9qZWN0IGZ1dHVyZSBzYWxlcyBmb3IgdGhlIGNvcnBvcmF0ZSBtYXJrZXRpbmcgc3RyYXRlZ3kuIEdlbmVyYWxseSwgVGVzdGluZyBFcnJvciBpcyBoaWdoZXIgdGhhbiB0aGUgVHJhaW5pbmcgRXJyb3IuIFRoaXMgaXMgdGhlIGZ1bmRhbWVudGFsIHJlYWxpdHkgb2YgcHJlZGljdGl2ZSBtb2RlbGluZy4NCg0KIyBEaWFnbm9zdGljIEF1ZGl0czogVmlzdWFsaXppbmcgdGhlIEVycm9ycyAoMTAgTWludXRlcykNCg0KTGV0J3MgdmlzdWFsaXplIG91ciB0ZXN0aW5nIGVycm9ycyB1c2luZyBnZ3Bsb3QyIHRvIGVuc3VyZSBvdXIgbGluZWFyIGFzc3VtcHRpb25zIGhvbGQgdXAgb3V0LW9mLXNhbXBsZS4NCg0KYGBge3J9DQpnZ3Bsb3QodGVzdF9wcmVkLCBhZXMoeCA9IHByZWRfc2FsZXMsIHkgPSBzYWxlcykpICsNCiAgZ2VvbV9wb2ludChjb2xvciA9ICIjNjM2MzYzIiwgc2l6ZSA9IDIsIGFscGhhID0gMC44KSArDQogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSwgY29sb3IgPSAiI2RlMmQyNiIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTQpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJBY3R1YWwgU2FsZXMgdnMuIE91dC1vZi1TYW1wbGUgUHJlZGljdGlvbnMiLA0KICAgIHN1YnRpdGxlID0gIlRoZSBkYXNoZWQgcmVkIGxpbmUgcmVwcmVzZW50cyBwZXJmZWN0IHByZWRpY3Rpb24iLA0KICAgIHggPSAiUHJlZGljdGVkIFNhbGVzIChVbnNlZW4gRGF0YSkiLA0KICAgIHkgPSAiQWN0dWFsIFNhbGVzIChVbnNlZW4gRGF0YSkiDQogICkNCmBgYA0KDQojIFN1bW1hcnkgZm9yIHRoZSBDLVN1aXRlDQoNCkJ5IGVzdGFibGlzaGluZyBmb3VuZGF0aW9uYWwgcHJvZ3JhbW1pbmcgZWxlbWVudHMsIG1ha2luZyBhIHN0cnVjdHVyYWwgZXZhbHVhdGlvbiBtaXN0YWtlLCBhbmQgY29ycmVjdGluZyBpdCB0aHJvdWdoIG91dC1vZi1zYW1wbGUgdmFsaWRhdGlvbiwgd2UgcHJvdmVkIG91ciBtb2RlbCdzIHByYWN0aWNhbCB1dGlsaXR5LiBXZSBjYW4gY29uZmlkZW50bHkgdGVsbCBleGVjdXRpdmUgbGVhZGVyc2hpcCB0aGF0IHdoZW4gZGVwbG95aW5nIHRoaXMgbW9kZWwgb3V0IGludG8gdGhlIHdpbGQsIG91ciBhdmVyYWdlIGZvcmVjYXN0aW5nIGVycm9yIHdpbGwgYmUgYXBwcm94aW1hdGVseSAxLjMxIHRob3VzYW5kIHVuaXRzLg0KDQojIyBNaW5pLUV4ZXJjaXNlDQoNCkFkZCBhIGhpZ2hseSBjb21wbGV4LCB1bm5lY2Vzc2FyeSBwb2x5bm9taWFsIHRlcm0gdG8gdGhlIG1vZGVsIChlLmcuLCBgSSh5b3V0dWJlXjMpYCkuDQoNClJlLWNhbGN1bGF0ZSB0aGUgRnVsbCBUcmFpbmluZyBFcnJvciBhbmQgdGhlIFRlc3QgRXJyb3IuIFdhdGNoIGhvdyB0aGUgVHJhaW5pbmcgRXJyb3IgZHJvcHMgZXZlbiBsb3dlciwgd2hpbGUgdGhlIFRlc3QgRXJyb3Igc3Bpa2VzIHdpbGRseS4gVGhpcyB3aWxsIHNob3djYXNlIGV4dHJlbWUgb3ZlcmZpdHRpbmcgaW4gYWN0aW9uIQ==