Introduction to Regression - Lesson 1
Putting it into perspective
✅ There are many types of regression methods, and which one you pick
depends on the answer you’re looking for. If you want to predict the
probable height for a person of a given age, you’d use
linear regression
, as you’re seeking a numeric
value. If you’re interested in discovering whether a type of
cuisine should be considered vegan or not, you’re looking for a
category assignment so you would use
logistic regression
. You’ll learn more about logistic
regression later. Think a bit about some questions you can ask of data,
and which of these methods would be more appropriate.
In this section, you will work with a small
dataset about diabetes. Imagine that you wanted to test a treatment
for diabetic patients. Machine Learning models might help you determine
which patients would respond better to the treatment, based on
combinations of variables. Even a very basic regression model, when
visualized, might show information about variables that would help you
organize your theoretical clinical trials.
That said, let’s get started on this task!
2. The diabetes dataset
In this exercise, we’ll put our regression skills into display by
making predictions on a diabetes dataset. The diabetes
dataset includes 442 samples
of data around diabetes,
with 10 predictor feature variables, age
, sex
,
body mass index
, average blood pressure
, and
six blood serum measurements
as well as an outcome variable
y
: a quantitative measure of disease progression one year
after baseline.
Number of predictors |
First 10 columns are numeric predictive values |
Outcome/Target |
Column 11 is a quantitative measure of disease progression one year
after baseline |
Predictor Information |
- age age in years
- sex
- bmi body mass index
- bp average blood pressure
- s1 tc, total serum cholesterol
- s2 ldl, low-density lipoproteins
- s3 hdl, high-density lipoproteins
- s4 tch, total cholesterol / HDL
- s5 ltg, possibly log of serum triglycerides level
- s6 glu, blood sugar level
|
🎓 Remember, this is supervised learning, and we need a named ‘y’
target.
Before you can manipulate data with R, you need to import the data
into R’s memory, or build a connection to the data that R can use to
access the data remotely.
The readr package, which
is part of the Tidyverse, provides a fast and friendly way to read
rectangular data into R.
Now, let’s load the diabetes dataset provided in this source URL: https://www4.stat.ncsu.edu/~boos/var.select/diabetes.html
Also, we’ll perform a sanity check on our data using
glimpse()
and dsiplay the first 5 rows using
slice()
.
Before going any further, let’s introduce something you will
encounter quite often in R code: the pipe operator
%>%
The pipe operator (%>%
) performs operations in
logical sequence by passing an object forward into a function or call
expression. You can think of the pipe operator as saying “and then” in
your code.
# Import the data set
diabetes <- read_table2(file = "https://www4.stat.ncsu.edu/~boos/var.select/diabetes.rwrite1.txt")
# Get a glimpse and dimensions of the data
glimpse(diabetes)
## Rows: 442
## Columns: 11
## $ age <dbl> 0.038075906, -0.001882017, 0.085298906, -0.089062939, 0.005383060,~
## $ sex <dbl> 0.05068012, -0.04464164, 0.05068012, -0.04464164, -0.04464164, -0.~
## $ bmi <dbl> 0.061696207, -0.051474061, 0.044451213, -0.011595015, -0.036384692~
## $ map <dbl> 0.021872355, -0.026327835, -0.005670611, -0.036656447, 0.021872355~
## $ tc <dbl> -0.044223498, -0.008448724, -0.045599451, 0.012190569, 0.003934852~
## $ ldl <dbl> -3.482076e-02, -1.916334e-02, -3.419447e-02, 2.499059e-02, 1.55961~
## $ hdl <dbl> -0.043400846, 0.074411564, -0.032355932, -0.036037570, 0.008142084~
## $ tch <dbl> -0.002592262, -0.039493383, -0.002592262, 0.034308859, -0.00259226~
## $ ltg <dbl> 0.019908421, -0.068329744, 0.002863771, 0.022692023, -0.031991445,~
## $ glu <dbl> -0.017646125, -0.092204050, -0.025930339, -0.009361911, -0.0466408~
## $ y <dbl> 151, 75, 141, 206, 135, 97, 138, 63, 110, 310, 101, 69, 179, 185, ~
# Select the first 5 rows of the data
diabetes %>%
slice(1:5)
glimpse()
shows us that this data has 442 rows and 11
columns with all the columns being of data type double
glimpse() and slice() are functions in dplyr
. Dplyr, part
of the Tidyverse, is a grammar of data manipulation that provides a
consistent set of verbs that help you solve the most common data
manipulation challenges
Now that we have the data, let’s narrow down to one feature
(bmi
) to target for this exercise. This will require us to
select the desired columns. So, how do we do this?
dplyr::select()
allows us to select (and optionally rename) columns in a data
frame.
# Select predictor feature `bmi` and outcome `y`
diabetes_select <- diabetes %>%
select(c(bmi, y))
# Print the first 5 rows
diabetes_select %>%
slice(1:5)
3. Training and Testing data
It’s common practice in supervised learning to split the
data into two subsets; a (typically larger) set with which to train the
model, and a smaller “hold-back” set with which to see how the model
performed.
Now that we have data ready, we can see if a machine can help
determine a logical split between the numbers in this dataset. We can
use the rsample
package, which is part of the Tidymodels framework, to create an object
that contains the information on how to split the data, and
then two more rsample functions to extract the created training and
testing sets:
set.seed(2056)
# Split 67% of the data for training and the rest for testing
diabetes_split <- diabetes_select %>%
initial_split(prop = 0.67)
# Extract the resulting train and test sets
diabetes_train <- training(diabetes_split)
diabetes_test <- testing(diabetes_split)
# Print the first 3 rows of the training set
diabetes_train %>%
slice(1:3)
4. Train a linear regression model with Tidymodels
Now we are ready to train our model!
In Tidymodels, you specify models using parsnip()
by
specifying three concepts:
Model type differentiates models such as linear
regression, logistic regression, decision tree models, and so
forth.
Model mode includes common options like
regression and classification; some model types support either of these
while some only have one mode.
Model engine is the computational tool which
will be used to fit the model. Often these are R packages, such as
"lm"
or
"ranger"
This modeling information is captured in a model specification, so
let’s build one!
# Build a linear model specification
lm_spec <-
# Type
linear_reg() %>%
# Engine
set_engine("lm") %>%
# Mode
set_mode("regression")
# Print the model specification
lm_spec
## Linear Regression Model Specification (regression)
##
## Computational engine: lm
After a model has been specified, the model can be
estimated
or trained
using the fit()
function, typically using a formula and some data.
y ~ .
means we’ll fit y
as the predicted
quantity/target, explained by all the predictors/features ie,
.
(in this case, we only have one predictor:
bmi
)
# Build a linear model specification
lm_spec <- linear_reg() %>%
set_engine("lm") %>%
set_mode("regression")
# Train a linear regression model
lm_mod <- lm_spec %>%
fit(y ~ ., data = diabetes_train)
# Print the model
lm_mod
## parsnip model object
##
##
## Call:
## stats::lm(formula = y ~ ., data = data)
##
## Coefficients:
## (Intercept) bmi
## 154.7 996.3
From the model output, we can see the coefficients learned during
training. They represent the coefficients of the line of best fit that
gives us the lowest overall error between the actual and predicted
variable.
5. Make predictions on the test set
Now that we’ve trained a model, we can use it to predict the disease
progression y for the test dataset using parsnip::predict().
This will be used to draw the line between data groups.
# Make predictions for the test set
predictions <- lm_mod %>%
predict(new_data = diabetes_test)
# Print out some of the predictions
predictions %>%
slice(1:5)
Woohoo! 💃🕺 We just trained a model and used it to make
predictions!
When making predictions, the tidymodels convention is to always
produce a tibble/data frame of results with standardized column names.
This makes it easy to combine the original data and the predictions in a
usable format for subsequent operations such as plotting.
dplyr::bind_cols()
efficiently binds multiple data
frames column.
# Combine the predictions and the original test set
results <- diabetes_test %>%
bind_cols(predictions)
results %>%
slice(1:5)
6. Plot modelling results
Now, its time to see this visually 📈. We’ll create a scatter plot of
all the y
and bmi
values of the test set, then
use the predictions to draw a line in the most appropriate place,
between the model’s data groupings.
R has several systems for making graphs, but ggplot2
is
one of the most elegant and most versatile. This allows you to compose
graphs by combining independent components.
# Set a theme for the plot
theme_set(theme_light())
# Create a scatter plot
results %>%
ggplot(aes(x = bmi)) +
# Add a scatter plot
geom_point(aes(y = y), size = 1.6) +
# Add a line plot
geom_line(aes(y = .pred), color = "blue", size = 1.5)

✅ Think a bit about what’s going on here. A straight line is running
through many small dots of data, but what is it doing exactly? Can you
see how you should be able to use this line to predict where a new,
unseen data point should fit in relationship to the plot’s y axis? Try
to put into words the practical use of this model.
Congratulations, you built your first linear regression model,
created a prediction with it, and displayed it in a plot!
LS0tDQp0aXRsZTogJ0J1aWxkIGEgcmVncmVzc2lvbiBtb2RlbDogR2V0IHN0YXJ0ZWQgd2l0aCBSIGFuZCBUaWR5bW9kZWxzIGZvciByZWdyZXNzaW9uIG1vZGVscycNCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgICB0aGVtZTogZmxhdGx5DQogICAgaGlnaGxpZ2h0OiBicmVlemVkYXJrDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KLS0tDQoNCiMjIEludHJvZHVjdGlvbiB0byBSZWdyZXNzaW9uIC0gTGVzc29uIDENCg0KIyMjIyBQdXR0aW5nIGl0IGludG8gcGVyc3BlY3RpdmUNCg0K4pyFIFRoZXJlIGFyZSBtYW55IHR5cGVzIG9mIHJlZ3Jlc3Npb24gbWV0aG9kcywgYW5kIHdoaWNoIG9uZSB5b3UgcGljayBkZXBlbmRzIG9uIHRoZSBhbnN3ZXIgeW91J3JlIGxvb2tpbmcgZm9yLiBJZiB5b3Ugd2FudCB0byBwcmVkaWN0IHRoZSBwcm9iYWJsZSBoZWlnaHQgZm9yIGEgcGVyc29uIG9mIGEgZ2l2ZW4gYWdlLCB5b3UnZCB1c2UgYGxpbmVhciByZWdyZXNzaW9uYCwgYXMgeW91J3JlIHNlZWtpbmcgYSAqKm51bWVyaWMgdmFsdWUqKi4gSWYgeW91J3JlIGludGVyZXN0ZWQgaW4gZGlzY292ZXJpbmcgd2hldGhlciBhIHR5cGUgb2YgY3Vpc2luZSBzaG91bGQgYmUgY29uc2lkZXJlZCB2ZWdhbiBvciBub3QsIHlvdSdyZSBsb29raW5nIGZvciBhICoqY2F0ZWdvcnkgYXNzaWdubWVudCoqIHNvIHlvdSB3b3VsZCB1c2UgYGxvZ2lzdGljIHJlZ3Jlc3Npb25gLiBZb3UnbGwgbGVhcm4gbW9yZSBhYm91dCBsb2dpc3RpYyByZWdyZXNzaW9uIGxhdGVyLiBUaGluayBhIGJpdCBhYm91dCBzb21lIHF1ZXN0aW9ucyB5b3UgY2FuIGFzayBvZiBkYXRhLCBhbmQgd2hpY2ggb2YgdGhlc2UgbWV0aG9kcyB3b3VsZCBiZSBtb3JlIGFwcHJvcHJpYXRlLg0KDQpJbiB0aGlzIHNlY3Rpb24sIHlvdSB3aWxsIHdvcmsgd2l0aCBhIFtzbWFsbCBkYXRhc2V0IGFib3V0IGRpYWJldGVzXShodHRwczovL3d3dzQuc3RhdC5uY3N1LmVkdS9+Ym9vcy92YXIuc2VsZWN0L2RpYWJldGVzLmh0bWwpLiBJbWFnaW5lIHRoYXQgeW91IHdhbnRlZCB0byB0ZXN0IGEgdHJlYXRtZW50IGZvciBkaWFiZXRpYyBwYXRpZW50cy4gTWFjaGluZSBMZWFybmluZyBtb2RlbHMgbWlnaHQgaGVscCB5b3UgZGV0ZXJtaW5lIHdoaWNoIHBhdGllbnRzIHdvdWxkIHJlc3BvbmQgYmV0dGVyIHRvIHRoZSB0cmVhdG1lbnQsIGJhc2VkIG9uIGNvbWJpbmF0aW9ucyBvZiB2YXJpYWJsZXMuIEV2ZW4gYSB2ZXJ5IGJhc2ljIHJlZ3Jlc3Npb24gbW9kZWwsIHdoZW4gdmlzdWFsaXplZCwgbWlnaHQgc2hvdyBpbmZvcm1hdGlvbiBhYm91dCB2YXJpYWJsZXMgdGhhdCB3b3VsZCBoZWxwIHlvdSBvcmdhbml6ZSB5b3VyIHRoZW9yZXRpY2FsIGNsaW5pY2FsIHRyaWFscy4NCg0KVGhhdCBzYWlkLCBsZXQncyBnZXQgc3RhcnRlZCBvbiB0aGlzIHRhc2shDQoNCiFbQXJ0d29yayBieSBcQGFsbGlzb25faG9yc3RdKC4uLy4uL2ltYWdlcy9lbmNvdVJhZ2UuanBnKXt3aWR0aD0iNjMwIn0NCg0KIyMgMS4gTG9hZGluZyB1cCBvdXIgdG9vbCBzZXQNCg0KRm9yIHRoaXMgdGFzaywgd2UnbGwgcmVxdWlyZSB0aGUgZm9sbG93aW5nIHBhY2thZ2VzOg0KDQotICAgYHRpZHl2ZXJzZWA6IFRoZSBbdGlkeXZlcnNlXShodHRwczovL3d3dy50aWR5dmVyc2Uub3JnLykgaXMgYSBbY29sbGVjdGlvbiBvZiBSIHBhY2thZ2VzXShodHRwczovL3d3dy50aWR5dmVyc2Uub3JnL3BhY2thZ2VzKSBkZXNpZ25lZCB0byBtYWtlcyBkYXRhIHNjaWVuY2UgZmFzdGVyLCBlYXNpZXIgYW5kIG1vcmUgZnVuIQ0KDQotICAgYHRpZHltb2RlbHNgOiBUaGUgW3RpZHltb2RlbHNdKGh0dHBzOi8vd3d3LnRpZHltb2RlbHMub3JnLykgZnJhbWV3b3JrIGlzIGEgW2NvbGxlY3Rpb24gb2YgcGFja2FnZXNdKGh0dHBzOi8vd3d3LnRpZHltb2RlbHMub3JnL3BhY2thZ2VzLykgZm9yIG1vZGVsaW5nIGFuZCBtYWNoaW5lIGxlYXJuaW5nLg0KDQpZb3UgY2FuIGhhdmUgdGhlbSBpbnN0YWxsZWQgYXM6DQoNCmBpbnN0YWxsLnBhY2thZ2VzKGMoInRpZHl2ZXJzZSIsICJ0aWR5bW9kZWxzIikpYA0KDQpUaGUgc2NyaXB0IGJlbG93IGNoZWNrcyB3aGV0aGVyIHlvdSBoYXZlIHRoZSBwYWNrYWdlcyByZXF1aXJlZCB0byBjb21wbGV0ZSB0aGlzIG1vZHVsZSBhbmQgaW5zdGFsbHMgdGhlbSBmb3IgeW91IGluIGNhc2UgdGhleSBhcmUgbWlzc2luZy4NCg0KYGBge3IsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KaWYgKCFyZXF1aXJlKCJwYWNtYW4iKSkgaW5zdGFsbC5wYWNrYWdlcygicGFjbWFuIikNCnBhY21hbjo6cF9sb2FkKHRpZHl2ZXJzZSwgdGlkeW1vZGVscykNCmBgYA0KDQpOb3csIGxldCdzIGxvYWQgdGhlc2UgYXdlc29tZSBwYWNrYWdlcyBhbmQgbWFrZSB0aGVtIGF2YWlsYWJsZSBpbiBvdXIgY3VycmVudCBSIHNlc3Npb24uIChUaGlzIGlzIGZvciBtZXJlIGlsbHVzdHJhdGlvbiwgYHBhY21hbjo6cF9sb2FkKClgIGFscmVhZHkgZGlkIHRoYXQgZm9yIHlvdSkNCg0KYGBge3IgbG9hZF90aWR5X3ZlcnNlX21vZGVscywgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQojIGxvYWQgdGhlIGNvcmUgVGlkeXZlcnNlIHBhY2thZ2VzDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCg0KIyBsb2FkIHRoZSBjb3JlIFRpZHltb2RlbHMgcGFja2FnZXMNCmxpYnJhcnkodGlkeW1vZGVscykNCg0KDQpgYGANCg0KIyMgMi4gVGhlIGRpYWJldGVzIGRhdGFzZXQNCg0KSW4gdGhpcyBleGVyY2lzZSwgd2UnbGwgcHV0IG91ciByZWdyZXNzaW9uIHNraWxscyBpbnRvIGRpc3BsYXkgYnkgbWFraW5nIHByZWRpY3Rpb25zIG9uIGEgZGlhYmV0ZXMgZGF0YXNldC4gVGhlIFtkaWFiZXRlcyBkYXRhc2V0XShodHRwczovL3d3dzQuc3RhdC5uY3N1LmVkdS9+Ym9vcy92YXIuc2VsZWN0L2RpYWJldGVzLnJ3cml0ZTEudHh0KSBpbmNsdWRlcyBgNDQyIHNhbXBsZXNgIG9mIGRhdGEgYXJvdW5kIGRpYWJldGVzLCB3aXRoIDEwIHByZWRpY3RvciBmZWF0dXJlIHZhcmlhYmxlcywgYGFnZWAsIGBzZXhgLCBgYm9keSBtYXNzIGluZGV4YCwgYGF2ZXJhZ2UgYmxvb2QgcHJlc3N1cmVgLCBhbmQgYHNpeCBibG9vZCBzZXJ1bSBtZWFzdXJlbWVudHNgIGFzIHdlbGwgYXMgYW4gb3V0Y29tZSB2YXJpYWJsZSBgeWA6IGEgcXVhbnRpdGF0aXZlIG1lYXN1cmUgb2YgZGlzZWFzZSBwcm9ncmVzc2lvbiBvbmUgeWVhciBhZnRlciBiYXNlbGluZS4NCg0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCAqKk51bWJlciBvZiBvYnNlcnZhdGlvbnMqKiB8ICoqNDQyKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKz09PT09PT09PT09PT09PT09PT09PT09PT09PT0rPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Kw0KfCAqKk51bWJlciBvZiBwcmVkaWN0b3JzKiogICB8IEZpcnN0IDEwIGNvbHVtbnMgYXJlIG51bWVyaWMgcHJlZGljdGl2ZSB2YWx1ZXMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCAqKk91dGNvbWUvVGFyZ2V0KiogICAgICAgICB8IENvbHVtbiAxMSBpcyBhIHF1YW50aXRhdGl2ZSBtZWFzdXJlIG9mIGRpc2Vhc2UgcHJvZ3Jlc3Npb24gb25lIHllYXIgYWZ0ZXIgYmFzZWxpbmUgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCAqKlByZWRpY3RvciBJbmZvcm1hdGlvbioqICB8IC0gICBhZ2UgYWdlIGluIHllYXJzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IC0gICBzZXggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IC0gICBibWkgYm9keSBtYXNzIGluZGV4ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IC0gICBicCBhdmVyYWdlIGJsb29kIHByZXNzdXJlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IC0gICBzMSB0YywgdG90YWwgc2VydW0gY2hvbGVzdGVyb2wgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IC0gICBzMiBsZGwsIGxvdy1kZW5zaXR5IGxpcG9wcm90ZWlucyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IC0gICBzMyBoZGwsIGhpZ2gtZGVuc2l0eSBsaXBvcHJvdGVpbnMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IC0gICBzNCB0Y2gsIHRvdGFsIGNob2xlc3Rlcm9sIC8gSERMICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IC0gICBzNSBsdGcsIHBvc3NpYmx5IGxvZyBvZiBzZXJ1bSB0cmlnbHljZXJpZGVzIGxldmVsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IC0gICBzNiBnbHUsIGJsb29kIHN1Z2FyIGxldmVsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KDQo+IPCfjpMgUmVtZW1iZXIsIHRoaXMgaXMgc3VwZXJ2aXNlZCBsZWFybmluZywgYW5kIHdlIG5lZWQgYSBuYW1lZCAneScgdGFyZ2V0Lg0KDQpCZWZvcmUgeW91IGNhbiBtYW5pcHVsYXRlIGRhdGEgd2l0aCBSLCB5b3UgbmVlZCB0byBpbXBvcnQgdGhlIGRhdGEgaW50byBSJ3MgbWVtb3J5LCBvciBidWlsZCBhIGNvbm5lY3Rpb24gdG8gdGhlIGRhdGEgdGhhdCBSIGNhbiB1c2UgdG8gYWNjZXNzIHRoZSBkYXRhIHJlbW90ZWx5LlwNCg0KPiBUaGUgW3JlYWRyXShodHRwczovL3JlYWRyLnRpZHl2ZXJzZS5vcmcvKSBwYWNrYWdlLCB3aGljaCBpcyBwYXJ0IG9mIHRoZSBUaWR5dmVyc2UsIHByb3ZpZGVzIGEgZmFzdCBhbmQgZnJpZW5kbHkgd2F5IHRvIHJlYWQgcmVjdGFuZ3VsYXIgZGF0YSBpbnRvIFIuDQoNCk5vdywgbGV0J3MgbG9hZCB0aGUgZGlhYmV0ZXMgZGF0YXNldCBwcm92aWRlZCBpbiB0aGlzIHNvdXJjZSBVUkw6IDxodHRwczovL3d3dzQuc3RhdC5uY3N1LmVkdS9+Ym9vcy92YXIuc2VsZWN0L2RpYWJldGVzLmh0bWw+DQoNCkFsc28sIHdlJ2xsIHBlcmZvcm0gYSBzYW5pdHkgY2hlY2sgb24gb3VyIGRhdGEgdXNpbmcgYGdsaW1wc2UoKWAgYW5kIGRzaXBsYXkgdGhlIGZpcnN0IDUgcm93cyB1c2luZyBgc2xpY2UoKWAuDQoNCkJlZm9yZSBnb2luZyBhbnkgZnVydGhlciwgbGV0J3MgaW50cm9kdWNlIHNvbWV0aGluZyB5b3Ugd2lsbCBlbmNvdW50ZXIgcXVpdGUgb2Z0ZW4gaW4gUiBjb2RlOiB0aGUgcGlwZSBvcGVyYXRvciBgJT4lYA0KDQpUaGUgcGlwZSBvcGVyYXRvciAoYCU+JWApIHBlcmZvcm1zIG9wZXJhdGlvbnMgaW4gbG9naWNhbCBzZXF1ZW5jZSBieSBwYXNzaW5nIGFuIG9iamVjdCBmb3J3YXJkIGludG8gYSBmdW5jdGlvbiBvciBjYWxsIGV4cHJlc3Npb24uIFlvdSBjYW4gdGhpbmsgb2YgdGhlIHBpcGUgb3BlcmF0b3IgYXMgc2F5aW5nICJhbmQgdGhlbiIgaW4geW91ciBjb2RlLlwNCg0KYGBge3IgbG9hZF9kYXRhc2V0LCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgSW1wb3J0IHRoZSBkYXRhIHNldA0KZGlhYmV0ZXMgPC0gcmVhZF90YWJsZTIoZmlsZSA9ICJodHRwczovL3d3dzQuc3RhdC5uY3N1LmVkdS9+Ym9vcy92YXIuc2VsZWN0L2RpYWJldGVzLnJ3cml0ZTEudHh0IikNCg0KDQojIEdldCBhIGdsaW1wc2UgYW5kIGRpbWVuc2lvbnMgb2YgdGhlIGRhdGENCmdsaW1wc2UoZGlhYmV0ZXMpDQoNCg0KIyBTZWxlY3QgdGhlIGZpcnN0IDUgcm93cyBvZiB0aGUgZGF0YQ0KZGlhYmV0ZXMgJT4lIA0KICBzbGljZSgxOjUpDQoNCmBgYA0KDQpgZ2xpbXBzZSgpYCBzaG93cyB1cyB0aGF0IHRoaXMgZGF0YSBoYXMgNDQyIHJvd3MgYW5kIDExIGNvbHVtbnMgd2l0aCBhbGwgdGhlIGNvbHVtbnMgYmVpbmcgb2YgZGF0YSB0eXBlIGBkb3VibGVgDQoNCj4gZ2xpbXBzZSgpIGFuZCBzbGljZSgpIGFyZSBmdW5jdGlvbnMgaW4gW2BkcGx5cmBdKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy8pLiBEcGx5ciwgcGFydCBvZiB0aGUgVGlkeXZlcnNlLCBpcyBhIGdyYW1tYXIgb2YgZGF0YSBtYW5pcHVsYXRpb24gdGhhdCBwcm92aWRlcyBhIGNvbnNpc3RlbnQgc2V0IG9mIHZlcmJzIHRoYXQgaGVscCB5b3Ugc29sdmUgdGhlIG1vc3QgY29tbW9uIGRhdGEgbWFuaXB1bGF0aW9uIGNoYWxsZW5nZXMNCg0KTm93IHRoYXQgd2UgaGF2ZSB0aGUgZGF0YSwgbGV0J3MgbmFycm93IGRvd24gdG8gb25lIGZlYXR1cmUgKGBibWlgKSB0byB0YXJnZXQgZm9yIHRoaXMgZXhlcmNpc2UuIFRoaXMgd2lsbCByZXF1aXJlIHVzIHRvIHNlbGVjdCB0aGUgZGVzaXJlZCBjb2x1bW5zLiBTbywgaG93IGRvIHdlIGRvIHRoaXM/DQoNCltgZHBseXI6OnNlbGVjdCgpYF0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9zZWxlY3QuaHRtbCkgYWxsb3dzIHVzIHRvICpzZWxlY3QqIChhbmQgb3B0aW9uYWxseSByZW5hbWUpIGNvbHVtbnMgaW4gYSBkYXRhIGZyYW1lLg0KDQpgYGB7ciBzZWxlY3QsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KIyBTZWxlY3QgcHJlZGljdG9yIGZlYXR1cmUgYGJtaWAgYW5kIG91dGNvbWUgYHlgDQpkaWFiZXRlc19zZWxlY3QgPC0gZGlhYmV0ZXMgJT4lIA0KICBzZWxlY3QoYyhibWksIHkpKQ0KDQojIFByaW50IHRoZSBmaXJzdCA1IHJvd3MNCmRpYWJldGVzX3NlbGVjdCAlPiUgDQogIHNsaWNlKDE6NSkNCmBgYA0KDQojIyAzLiBUcmFpbmluZyBhbmQgVGVzdGluZyBkYXRhDQoNCkl0J3MgY29tbW9uIHByYWN0aWNlIGluIHN1cGVydmlzZWQgbGVhcm5pbmcgdG8gKnNwbGl0KiB0aGUgZGF0YSBpbnRvIHR3byBzdWJzZXRzOyBhICh0eXBpY2FsbHkgbGFyZ2VyKSBzZXQgd2l0aCB3aGljaCB0byB0cmFpbiB0aGUgbW9kZWwsIGFuZCBhIHNtYWxsZXIgImhvbGQtYmFjayIgc2V0IHdpdGggd2hpY2ggdG8gc2VlIGhvdyB0aGUgbW9kZWwgcGVyZm9ybWVkLg0KDQpOb3cgdGhhdCB3ZSBoYXZlIGRhdGEgcmVhZHksIHdlIGNhbiBzZWUgaWYgYSBtYWNoaW5lIGNhbiBoZWxwIGRldGVybWluZSBhIGxvZ2ljYWwgc3BsaXQgYmV0d2VlbiB0aGUgbnVtYmVycyBpbiB0aGlzIGRhdGFzZXQuIFdlIGNhbiB1c2UgdGhlIFtyc2FtcGxlXShodHRwczovL3RpZHltb2RlbHMuZ2l0aHViLmlvL3JzYW1wbGUvKSBwYWNrYWdlLCB3aGljaCBpcyBwYXJ0IG9mIHRoZSBUaWR5bW9kZWxzIGZyYW1ld29yaywgdG8gY3JlYXRlIGFuIG9iamVjdCB0aGF0IGNvbnRhaW5zIHRoZSBpbmZvcm1hdGlvbiBvbiAqaG93KiB0byBzcGxpdCB0aGUgZGF0YSwgYW5kIHRoZW4gdHdvIG1vcmUgcnNhbXBsZSBmdW5jdGlvbnMgdG8gZXh0cmFjdCB0aGUgY3JlYXRlZCB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzOg0KDQpgYGB7ciBzcGxpdCwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQpzZXQuc2VlZCgyMDU2KQ0KIyBTcGxpdCA2NyUgb2YgdGhlIGRhdGEgZm9yIHRyYWluaW5nIGFuZCB0aGUgcmVzdCBmb3IgdGVzdGluZw0KZGlhYmV0ZXNfc3BsaXQgPC0gZGlhYmV0ZXNfc2VsZWN0ICU+JSANCiAgaW5pdGlhbF9zcGxpdChwcm9wID0gMC42NykNCg0KIyBFeHRyYWN0IHRoZSByZXN1bHRpbmcgdHJhaW4gYW5kIHRlc3Qgc2V0cw0KZGlhYmV0ZXNfdHJhaW4gPC0gdHJhaW5pbmcoZGlhYmV0ZXNfc3BsaXQpDQpkaWFiZXRlc190ZXN0IDwtIHRlc3RpbmcoZGlhYmV0ZXNfc3BsaXQpDQoNCiMgUHJpbnQgdGhlIGZpcnN0IDMgcm93cyBvZiB0aGUgdHJhaW5pbmcgc2V0DQpkaWFiZXRlc190cmFpbiAlPiUgDQogIHNsaWNlKDE6MykNCg0KYGBgDQoNCiMjIDQuIFRyYWluIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgd2l0aCBUaWR5bW9kZWxzDQoNCk5vdyB3ZSBhcmUgcmVhZHkgdG8gdHJhaW4gb3VyIG1vZGVsIQ0KDQpJbiBUaWR5bW9kZWxzLCB5b3Ugc3BlY2lmeSBtb2RlbHMgdXNpbmcgYHBhcnNuaXAoKWAgYnkgc3BlY2lmeWluZyB0aHJlZSBjb25jZXB0czoNCg0KLSAgIE1vZGVsICoqdHlwZSoqIGRpZmZlcmVudGlhdGVzIG1vZGVscyBzdWNoIGFzIGxpbmVhciByZWdyZXNzaW9uLCBsb2dpc3RpYyByZWdyZXNzaW9uLCBkZWNpc2lvbiB0cmVlIG1vZGVscywgYW5kIHNvIGZvcnRoLg0KDQotICAgTW9kZWwgKiptb2RlKiogaW5jbHVkZXMgY29tbW9uIG9wdGlvbnMgbGlrZSByZWdyZXNzaW9uIGFuZCBjbGFzc2lmaWNhdGlvbjsgc29tZSBtb2RlbCB0eXBlcyBzdXBwb3J0IGVpdGhlciBvZiB0aGVzZSB3aGlsZSBzb21lIG9ubHkgaGF2ZSBvbmUgbW9kZS4NCg0KLSAgIE1vZGVsICoqZW5naW5lKiogaXMgdGhlIGNvbXB1dGF0aW9uYWwgdG9vbCB3aGljaCB3aWxsIGJlIHVzZWQgdG8gZml0IHRoZSBtb2RlbC4gT2Z0ZW4gdGhlc2UgYXJlIFIgcGFja2FnZXMsIHN1Y2ggYXMgKipgImxtImAqKiBvciAqKmAicmFuZ2VyImAqKg0KDQpUaGlzIG1vZGVsaW5nIGluZm9ybWF0aW9uIGlzIGNhcHR1cmVkIGluIGEgbW9kZWwgc3BlY2lmaWNhdGlvbiwgc28gbGV0J3MgYnVpbGQgb25lIQ0KDQpgYGB7ciBsbV9tb2RlbF9zcGVjLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgQnVpbGQgYSBsaW5lYXIgbW9kZWwgc3BlY2lmaWNhdGlvbg0KbG1fc3BlYyA8LSANCiAgIyBUeXBlDQogIGxpbmVhcl9yZWcoKSAlPiUgDQogICMgRW5naW5lDQogIHNldF9lbmdpbmUoImxtIikgJT4lIA0KICAjIE1vZGUNCiAgc2V0X21vZGUoInJlZ3Jlc3Npb24iKQ0KDQoNCiMgUHJpbnQgdGhlIG1vZGVsIHNwZWNpZmljYXRpb24NCmxtX3NwZWMNCg0KYGBgDQoNCkFmdGVyIGEgbW9kZWwgaGFzIGJlZW4gKnNwZWNpZmllZCosIHRoZSBtb2RlbCBjYW4gYmUgYGVzdGltYXRlZGAgb3IgYHRyYWluZWRgIHVzaW5nIHRoZSBbYGZpdCgpYF0oaHR0cHM6Ly9wYXJzbmlwLnRpZHltb2RlbHMub3JnL3JlZmVyZW5jZS9maXQuaHRtbCkgZnVuY3Rpb24sIHR5cGljYWxseSB1c2luZyBhIGZvcm11bGEgYW5kIHNvbWUgZGF0YS4NCg0KYHkgfiAuYCBtZWFucyB3ZSdsbCBmaXQgYHlgIGFzIHRoZSBwcmVkaWN0ZWQgcXVhbnRpdHkvdGFyZ2V0LCBleHBsYWluZWQgYnkgYWxsIHRoZSBwcmVkaWN0b3JzL2ZlYXR1cmVzIGllLCBgLmAgKGluIHRoaXMgY2FzZSwgd2Ugb25seSBoYXZlIG9uZSBwcmVkaWN0b3I6IGBibWlgICkNCg0KYGBge3IgdHJhaW4sIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KIyBCdWlsZCBhIGxpbmVhciBtb2RlbCBzcGVjaWZpY2F0aW9uDQpsbV9zcGVjIDwtIGxpbmVhcl9yZWcoKSAlPiUgDQogIHNldF9lbmdpbmUoImxtIikgJT4lDQogIHNldF9tb2RlKCJyZWdyZXNzaW9uIikNCg0KDQojIFRyYWluIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwNCmxtX21vZCA8LSBsbV9zcGVjICU+JSANCiAgZml0KHkgfiAuLCBkYXRhID0gZGlhYmV0ZXNfdHJhaW4pDQoNCiMgUHJpbnQgdGhlIG1vZGVsDQpsbV9tb2QNCmBgYA0KDQpGcm9tIHRoZSBtb2RlbCBvdXRwdXQsIHdlIGNhbiBzZWUgdGhlIGNvZWZmaWNpZW50cyBsZWFybmVkIGR1cmluZyB0cmFpbmluZy4gVGhleSByZXByZXNlbnQgdGhlIGNvZWZmaWNpZW50cyBvZiB0aGUgbGluZSBvZiBiZXN0IGZpdCB0aGF0IGdpdmVzIHVzIHRoZSBsb3dlc3Qgb3ZlcmFsbCBlcnJvciBiZXR3ZWVuIHRoZSBhY3R1YWwgYW5kIHByZWRpY3RlZCB2YXJpYWJsZS4NCg0KIyMgNS4gTWFrZSBwcmVkaWN0aW9ucyBvbiB0aGUgdGVzdCBzZXQNCg0KTm93IHRoYXQgd2UndmUgdHJhaW5lZCBhIG1vZGVsLCB3ZSBjYW4gdXNlIGl0IHRvIHByZWRpY3QgdGhlIGRpc2Vhc2UgcHJvZ3Jlc3Npb24geSBmb3IgdGhlIHRlc3QgZGF0YXNldCB1c2luZyBbcGFyc25pcDo6cHJlZGljdCgpXShodHRwczovL3BhcnNuaXAudGlkeW1vZGVscy5vcmcvcmVmZXJlbmNlL3ByZWRpY3QubW9kZWxfZml0Lmh0bWwpLiBUaGlzIHdpbGwgYmUgdXNlZCB0byBkcmF3IHRoZSBsaW5lIGJldHdlZW4gZGF0YSBncm91cHMuDQoNCmBgYHtyIHRlc3QsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KIyBNYWtlIHByZWRpY3Rpb25zIGZvciB0aGUgdGVzdCBzZXQNCnByZWRpY3Rpb25zIDwtIGxtX21vZCAlPiUgDQogIHByZWRpY3QobmV3X2RhdGEgPSBkaWFiZXRlc190ZXN0KQ0KDQojIFByaW50IG91dCBzb21lIG9mIHRoZSBwcmVkaWN0aW9ucw0KcHJlZGljdGlvbnMgJT4lIA0KICBzbGljZSgxOjUpDQpgYGANCg0KV29vaG9vISDwn5KD8J+VuiBXZSBqdXN0IHRyYWluZWQgYSBtb2RlbCBhbmQgdXNlZCBpdCB0byBtYWtlIHByZWRpY3Rpb25zIQ0KDQpXaGVuIG1ha2luZyBwcmVkaWN0aW9ucywgdGhlIHRpZHltb2RlbHMgY29udmVudGlvbiBpcyB0byBhbHdheXMgcHJvZHVjZSBhIHRpYmJsZS9kYXRhIGZyYW1lIG9mIHJlc3VsdHMgd2l0aCBzdGFuZGFyZGl6ZWQgY29sdW1uIG5hbWVzLiBUaGlzIG1ha2VzIGl0IGVhc3kgdG8gY29tYmluZSB0aGUgb3JpZ2luYWwgZGF0YSBhbmQgdGhlIHByZWRpY3Rpb25zIGluIGEgdXNhYmxlIGZvcm1hdCBmb3Igc3Vic2VxdWVudCBvcGVyYXRpb25zIHN1Y2ggYXMgcGxvdHRpbmcuDQoNCmBkcGx5cjo6YmluZF9jb2xzKClgIGVmZmljaWVudGx5IGJpbmRzIG11bHRpcGxlIGRhdGEgZnJhbWVzIGNvbHVtbi4NCg0KYGBge3IgdGVzdF9wcmVkLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCiMgQ29tYmluZSB0aGUgcHJlZGljdGlvbnMgYW5kIHRoZSBvcmlnaW5hbCB0ZXN0IHNldA0KcmVzdWx0cyA8LSBkaWFiZXRlc190ZXN0ICU+JSANCiAgYmluZF9jb2xzKHByZWRpY3Rpb25zKQ0KDQoNCnJlc3VsdHMgJT4lIA0KICBzbGljZSgxOjUpDQpgYGANCg0KIyMgNi4gUGxvdCBtb2RlbGxpbmcgcmVzdWx0cw0KDQpOb3csIGl0cyB0aW1lIHRvIHNlZSB0aGlzIHZpc3VhbGx5IPCfk4guIFdlJ2xsIGNyZWF0ZSBhIHNjYXR0ZXIgcGxvdCBvZiBhbGwgdGhlIGB5YCBhbmQgYGJtaWAgdmFsdWVzIG9mIHRoZSB0ZXN0IHNldCwgdGhlbiB1c2UgdGhlIHByZWRpY3Rpb25zIHRvIGRyYXcgYSBsaW5lIGluIHRoZSBtb3N0IGFwcHJvcHJpYXRlIHBsYWNlLCBiZXR3ZWVuIHRoZSBtb2RlbCdzIGRhdGEgZ3JvdXBpbmdzLg0KDQpSIGhhcyBzZXZlcmFsIHN5c3RlbXMgZm9yIG1ha2luZyBncmFwaHMsIGJ1dCBgZ2dwbG90MmAgaXMgb25lIG9mIHRoZSBtb3N0IGVsZWdhbnQgYW5kIG1vc3QgdmVyc2F0aWxlLiBUaGlzIGFsbG93cyB5b3UgdG8gY29tcG9zZSBncmFwaHMgYnkgKipjb21iaW5pbmcgaW5kZXBlbmRlbnQgY29tcG9uZW50cyoqLg0KDQpgYGB7ciBwbG90X3ByZWQsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KIyBTZXQgYSB0aGVtZSBmb3IgdGhlIHBsb3QNCnRoZW1lX3NldCh0aGVtZV9saWdodCgpKQ0KIyBDcmVhdGUgYSBzY2F0dGVyIHBsb3QNCnJlc3VsdHMgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBibWkpKSArDQogICMgQWRkIGEgc2NhdHRlciBwbG90DQogIGdlb21fcG9pbnQoYWVzKHkgPSB5KSwgc2l6ZSA9IDEuNikgKw0KICAjIEFkZCBhIGxpbmUgcGxvdA0KICBnZW9tX2xpbmUoYWVzKHkgPSAucHJlZCksIGNvbG9yID0gImJsdWUiLCBzaXplID0gMS41KQ0KICANCmBgYA0KDQo+IOKchSBUaGluayBhIGJpdCBhYm91dCB3aGF0J3MgZ29pbmcgb24gaGVyZS4gQSBzdHJhaWdodCBsaW5lIGlzIHJ1bm5pbmcgdGhyb3VnaCBtYW55IHNtYWxsIGRvdHMgb2YgZGF0YSwgYnV0IHdoYXQgaXMgaXQgZG9pbmcgZXhhY3RseT8gQ2FuIHlvdSBzZWUgaG93IHlvdSBzaG91bGQgYmUgYWJsZSB0byB1c2UgdGhpcyBsaW5lIHRvIHByZWRpY3Qgd2hlcmUgYSBuZXcsIHVuc2VlbiBkYXRhIHBvaW50IHNob3VsZCBmaXQgaW4gcmVsYXRpb25zaGlwIHRvIHRoZSBwbG90J3MgeSBheGlzPyBUcnkgdG8gcHV0IGludG8gd29yZHMgdGhlIHByYWN0aWNhbCB1c2Ugb2YgdGhpcyBtb2RlbC4NCg0KQ29uZ3JhdHVsYXRpb25zLCB5b3UgYnVpbHQgeW91ciBmaXJzdCBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCwgY3JlYXRlZCBhIHByZWRpY3Rpb24gd2l0aCBpdCwgYW5kIGRpc3BsYXllZCBpdCBpbiBhIHBsb3QhDQo=