Read.me
The structure of the notebook is as the following:
- Data Description: description of the data and mini case
- 5-Step Framework for Regression Analysis
- Model Comparison: a model without
Brand Equity for
comparison
- Risk Control: to check the normality and equal variance
assumptions
Make sure you have downloaded and installed the course package “MSR”
from Canvas. Check Canvas (in Module - Week 1) about how to do it. For
this notebook, you need to download and load a R package
ggplot2.
library(MSR)
library(ggplot2)
Data Description
We first load the Marks and Spencer data from the MSR package using
data() function.
data("marks_spencer")
head(marks_spencer)
## Week Advertising Promotion Price Brand_Equity Sales
## 1 1 6.643165 No 2.664169 99.01042 208.9463
## 2 2 6.551415 Yes 4.799289 99.31082 159.9861
## 3 3 3.606885 No 4.798553 99.44092 157.0999
## 4 4 4.323537 No 4.674879 99.52237 168.7835
## 5 5 5.859314 No 3.101983 99.53748 203.5160
## 6 6 6.366751 No 4.583886 99.67113 165.1949
For a detailed description of the data, please use the following
command:
help("marks_spencer") # or you can use ?marks_spencer
## starting httpd help server ... done
From the data, we know the sales from Week 1 to
Week 75. Using the first 75 weeks of data, we want to
predict the sales from Week 76 to Week 100. It
is known ad hoc that the sales of Marks & Spencer decreased after
Week 75. The question is: can we
predict the sales decrease?
We are comparing two models, one model with Brand Equity and the
other model without Brand Equity, to highlight the importance of
including consumer-related measures in prediction.
Running Regression
Analysis Step-by-Step
In this section, we will run a linear regression to predict sales by
following the framework discussed in class step-by-step. For this
analysis, we are using Sales as the DV and all the other
variables as the IVs.
Note: The framework is presented to give a structure to follow as a
novice. If you have become an expert, no need to follow it exactly.
We are using the first 75 weeks to estimate the model (or as the
train set), and the remaining 25 weeks to do prediction (or as the test
set). So, from the full data, we first obtain a train set.
train <- marks_spencer[1:75,]
str(train)
## 'data.frame': 75 obs. of 6 variables:
## $ Week : num 1 2 3 4 5 6 7 8 9 10 ...
## $ Advertising : num 6.64 6.55 3.61 4.32 5.86 ...
## $ Promotion : Factor w/ 2 levels "No","Yes": 1 2 1 1 1 1 1 1 2 1 ...
## $ Price : num 2.66 4.8 4.8 4.67 3.1 ...
## $ Brand_Equity: num 99 99.3 99.4 99.5 99.5 ...
## $ Sales : num 209 160 157 169 204 ...
Examining the
data
To get a good prediction, we want good IVs that are relevant (or
correlated with the DV). In addition, we do NOT want IVs to be highly
correlated with one another, which lead to information redundancy and
eventually bad predictions.
Checking the
VIFs
We want to check if there’s any multi-collinearity problem. We do NOT
want IVs to be highly correlated. To this end, we calculate the VIFs.
You are already given a function called vif along with the
data. This function takes two inputs: the names of IVs that you want to
check in a data frame and the data frame containing those IVs, and
outputs the VIF values of the variables (as a vector).
# getting the variable names of the IVs
vnames <- colnames(train)[2:5]
# calculating the vif's
vif(vnames,train)
## PromotionYes Advertising Price Brand_Equity
## 1.112713 1.047414 1.008407 1.068874
The criterion for the VIF values is if they are larger than 10. In
the output, no VIFs are larger than 10. So, we do not have collinearity
issue.
Estimating the
model
Next, we estimate our linear regression model with the
lm function by translating the equation into an R formula.
Please see Canvas for the formula language.
model_no_controls <- lm(Sales ~ Price, data = train)
summary(model_no_controls)
##
## Call:
## lm(formula = Sales ~ Price, data = train)
##
## Residuals:
## Min 1Q Median 3Q Max
## -38.868 -17.852 6.217 16.776 32.608
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 241.718 11.301 21.390 < 2e-16 ***
## Price -20.747 2.746 -7.556 9.57e-11 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 20.56 on 73 degrees of freedom
## Multiple R-squared: 0.4389, Adjusted R-squared: 0.4312
## F-statistic: 57.1 on 1 and 73 DF, p-value: 9.566e-11
Validating the
model
From the results above, we first validate the model. We check a few
things.
The overall
significance
The idea of the overall significance is to compare the model we run
to a null model with no IVs. If our model significantly
outperforms the null model, it means it’s worth it. Or in statistical
term, we test the null hypothesis that the coefficients
(\(\beta\)’s) of all IVs are zero. Or,
\[H_0:\beta_1=\beta_2=...=0\] For this
model, the F-stat is 57.1, with a p-value < 0.05. So, we can reject
the null hypothesis, and the model is of predictive value.
The significance of
coefficients
We have included multiple IVs in the model. However, it’s possible
that an IV does not contribute to the prediction accuracy. To check
this, we focus on an individual coefficient and test the following null
hypothesis for a particular IV: \[H_0:\beta_k=0\] The coefficient of price
\(\beta_\text{price}\) is -20.75, with
a standard error of 2.74. The p-value < 0.05. So, we reject the null
hypothesis, and conclude that Price is of predictive
value.
Model fit
To see how well the model fits with our data, we look at
R-squared. The direct interpretation of R-squared is the
percentage of variation explained by the model. Here, the R-squared of
the model is 0.4389 or 43.89%. It means 94.85% of variation in the sales
is captured by the model.
There is no clear cutoff value for
R-squared. You need to consider the specific setting to judge
whether the R-squared is good enough. For example, in sales prediction,
we usually expect a big R-squared (e.g., 90%). This is because the
retailers oftentimes are faced with a relatively stable environment
where consumers show persistent habits of buying products. In our case,
the value of 43.89% indicates a fit that is below expectation.
Making
predictions
After estimating the model, we can use it to do prediction. In the
case, we want to predict the sales from Week 76 to
Week 100. Let’s first create a prediction data set (or test
set).
test <- marks_spencer[76:100,]
str(test)
## 'data.frame': 25 obs. of 6 variables:
## $ Week : num 76 77 78 79 80 81 82 83 84 85 ...
## $ Advertising : num 6.57 6.61 6.55 6.94 5.43 ...
## $ Promotion : Factor w/ 2 levels "No","Yes": 1 2 1 1 1 1 2 2 2 1 ...
## $ Price : num 3.62 3.76 4.52 4.67 4.51 ...
## $ Brand_Equity: num 53 50.8 45.8 44.9 44.5 ...
## $ Sales : num NA NA NA NA NA NA NA NA NA NA ...
To predict the sales, we use the predict function.
Please use ?predict for more information of this
function.
sales_no_controls <- predict(model_no_controls,
newdata = test)
#coerce into a data frame
sales_no_controls <- as.data.frame(sales_no_controls)
Next, let’s create a line plot of the predicted sales to see the
predicted trend.
# add week id for plotting
sales_no_controls$week <- 76:100
# a line plot of the predicted sales
ggplot(data = sales_no_controls,
aes(x = week, y = sales_no_controls)) + geom_line()

Model Comparison
In this section, we are running a model control variables. The
analyses are the same as in Section 3. We would not do things
step-by-step. If you are interested, you can practice with this model by
yourself.
The model specification is as the following (with Brand Equity not
included): \[\text{Sales} = \beta_0+
\beta_1\text{Price} + \beta_2\text{Advertising} +
\beta_3\text{Promotion} + \beta_4\text{Brand_Equity} +e\] Next,
we estimate our linear regression model with the lm
function by translating the equation into an R formula. Please see
Canvas for the formula language.
model_with_controls <- lm(Sales ~ Price + Advertising + Promotion + Brand_Equity, data = train)
summary(model_with_controls)
##
## Call:
## lm(formula = Sales ~ Price + Advertising + Promotion + Brand_Equity,
## data = train)
##
## Residuals:
## Min 1Q Median 3Q Max
## -14.493 -3.082 0.006 4.011 13.866
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 102.34877 7.33386 13.956 <2e-16 ***
## Price -20.21582 0.85321 -23.694 <2e-16 ***
## Advertising 1.74314 0.67864 2.569 0.0123 *
## PromotionYes -0.78282 1.68069 -0.466 0.6428
## Brand_Equity 1.46121 0.05805 25.170 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 6.363 on 70 degrees of freedom
## Multiple R-squared: 0.9485, Adjusted R-squared: 0.9455
## F-statistic: 322.1 on 4 and 70 DF, p-value: < 2.2e-16
To compare this model with the model with Brand Equity, we use
adjusted R-squared. This is because the two model have
different no. of IVs. So, we must adjust for this
difference. The adjusted R-squared of the model with vs. without
controls are 0.9455 vs. 0.4312. Therefore, the model with controls fits
the data better.
The coefficient of price in this model is -20.22, larger than that in
the model without control variables (-20.75). The relatively small
change in price coefficient does not mean we have a good estimation of
price effect. It is possible that we fail to add other control
variables, such as seasonality or competition.
Risk Control
After running the models, we want to check if the statistical
assumptions of linear regression are Okay. As discussed in class, the
violation of these assumptions leads to biased estimation and bad
prediction. Over the years, people have developed many tests for these
assumptions. We will focus on two assumptions: 1) the normality
assumption; and 2) the equal variance assumption.
We will use the full model (the model with brand equity or ) as an
example. For other models, the procedure works the same.
The normality
assumption
For the normality assumption, we are using KS test. KS test checks a
variable against a normal distribution and the value of the KS test
tells you whether the variable follows a normal distribution.
Note that the null hypothesis of the test is: \[H_0: \text{The variable follows a normal
distribution}.\] For the normality assumption to hold, the null
hypothesis should not be rejected. In other words, the KS test should
NOT be significant if the normality assumption holds.
First, we obtain the residuals from the estimation results and
visualize the distribution with a histogram.
# obtain the residuals using the residuals() function
error_term <- residuals(model_no_controls)
# build a histogram
# leave the data empty as we only plot a variable error_term
ggplot( , aes(x=error_term)) + geom_histogram()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

From the plot, it seems the error_term does not follow a normal
distribution. We may further use KS test to do the formal testing. Note
that by default, the KS test compares a variable to a standard normal
distribution, or a normal distribution with mean 0 and standard
deviation 1. Therefore, we need to first re-scale the residuals to make
it consistent with the default distribution.
# scale() function standardizes a variable use help(scale) to see more.
std_error_term <- scale(error_term)
# the ks test with ks.test();
#"pnorm" means testing against the standard normal distribution
ks.test(std_error_term,"pnorm")
##
## Exact one-sample Kolmogorov-Smirnov test
##
## data: std_error_term
## D = 0.13614, p-value = 0.1129
## alternative hypothesis: two-sided
From the test results, p-value is 0.11 > 0.05. So, we cannot
reject the null hypothesis. This implies the normality assumption is
Okay.
The equal variance
assumption
Another assumption of linear regression is all residuals follow the
same normal distribution or the same variance. Although there is a
formal test for this, it is beyond the scope of this course. Instead, we
rely on plotting to eyeball this assumption.
To produce this plot, we need two values: the residuals and the DV
(sales in this case). We do a scatter plot with the residuals as the
Y-axis and the predicted DV as the X-axis. In addition, sometimes it’s
not so easy to see the patterns due to the scales of variables. So, we
rescale the variables by standardization (with the scale()
function).
# getting the predicted sales of the test set
test_sales_no_controls <- predict(model_no_controls)
# plotting to see the residuals vs. predicted sales
ggplot( , aes(y = std_error_term, x = scale(test_sales_no_controls)))+
geom_point() + geom_hline(yintercept = 0)

The trick is to check whether at different value of the DV (sales),
the span or the range of the residuals are similar. You may add
auxiliary lines to help you check.
Equal Variance Scatter Plot
From the plot, we an see that the range or span of residuals are
similar. So, the equal variance assumption is likely to hold. Note that
as we rely on eyeballing, it can be subjective. For the learning
purpose, as long as you know how to use the plot to detect equal
variance, your conclusion is not an issue.
LS0tDQp0aXRsZTogJ0xlY3R1cmUgMiAtIE1hcmtldCBSZXNwb25zZSBNb2RlbCcNCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogZGVmYXVsdA0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHRoZW1lOiByZWFkYWJsZQ0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiANCiAgICAgIGNvbGxhcHNlZDogbm8NCiAgICAgIHNtb290aF9zY3JvbGw6IG5vDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgcGRmX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQotLS0NCg0KIyBSZWFkLm1lDQpUaGUgc3RydWN0dXJlIG9mIHRoZSBub3RlYm9vayBpcyBhcyB0aGUgZm9sbG93aW5nOg0KDQoqIERhdGEgRGVzY3JpcHRpb246IGRlc2NyaXB0aW9uIG9mIHRoZSBkYXRhIGFuZCBtaW5pIGNhc2UNCiogNS1TdGVwIEZyYW1ld29yayBmb3IgUmVncmVzc2lvbiBBbmFseXNpcw0KKiBNb2RlbCBDb21wYXJpc29uOiBhIG1vZGVsIHdpdGhvdXQgYEJyYW5kIEVxdWl0eWAgZm9yIGNvbXBhcmlzb24NCiogUmlzayBDb250cm9sOiB0byBjaGVjayB0aGUgbm9ybWFsaXR5IGFuZCBlcXVhbCB2YXJpYW5jZSBhc3N1bXB0aW9ucw0KDQpNYWtlIHN1cmUgeW91IGhhdmUgZG93bmxvYWRlZCBhbmQgaW5zdGFsbGVkIHRoZSBjb3Vyc2UgcGFja2FnZSAiTVNSIiBmcm9tIENhbnZhcy4gQ2hlY2sgQ2FudmFzIChpbiBNb2R1bGUgLSBXZWVrIDEpIGFib3V0IGhvdyB0byBkbyBpdC4gRm9yIHRoaXMgbm90ZWJvb2ssIHlvdSBuZWVkIHRvIGRvd25sb2FkIGFuZCBsb2FkIGEgUiBwYWNrYWdlIGBnZ3Bsb3QyYC4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShNU1IpDQpsaWJyYXJ5KGdncGxvdDIpDQpgYGANCg0KIyBEYXRhIERlc2NyaXB0aW9uIA0KDQpXZSBmaXJzdCBsb2FkIHRoZSBNYXJrcyBhbmQgU3BlbmNlciBkYXRhIGZyb20gdGhlIE1TUiBwYWNrYWdlIHVzaW5nIGBkYXRhKClgIGZ1bmN0aW9uLg0KDQpgYGB7ciB9DQpkYXRhKCJtYXJrc19zcGVuY2VyIikNCmhlYWQobWFya3Nfc3BlbmNlcikNCmBgYA0KDQpGb3IgYSBkZXRhaWxlZCBkZXNjcmlwdGlvbiBvZiB0aGUgZGF0YSwgcGxlYXNlIHVzZSB0aGUgZm9sbG93aW5nIGNvbW1hbmQ6DQpgYGB7cn0NCmhlbHAoIm1hcmtzX3NwZW5jZXIiKSAjIG9yIHlvdSBjYW4gdXNlID9tYXJrc19zcGVuY2VyDQpgYGANCg0KRnJvbSB0aGUgZGF0YSwgd2Uga25vdyB0aGUgc2FsZXMgZnJvbSBgV2VlayAxYCB0byBgV2VlayA3NWAuIFVzaW5nIHRoZSBmaXJzdCA3NSB3ZWVrcyBvZiBkYXRhLCB3ZSB3YW50IHRvIHByZWRpY3QgdGhlIHNhbGVzIGZyb20gYFdlZWsgNzZgIHRvIGBXZWVrIDEwMGAuIEl0IGlzIGtub3duIGFkIGhvYyB0aGF0IHRoZSBzYWxlcyBvZiBNYXJrcyAmIFNwZW5jZXIgZGVjcmVhc2VkIGFmdGVyIGBXZWVrIDc1YC4gVGhlIHF1ZXN0aW9uIGlzOiA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZDsiPmNhbiB3ZSBwcmVkaWN0IHRoZSBzYWxlcyBkZWNyZWFzZT88L3NwYW4+DQoNCldlIGFyZSBjb21wYXJpbmcgdHdvIG1vZGVscywgb25lIG1vZGVsIHdpdGggQnJhbmQgRXF1aXR5IGFuZCB0aGUgb3RoZXIgbW9kZWwgd2l0aG91dCBCcmFuZCBFcXVpdHksIHRvIGhpZ2hsaWdodCB0aGUgaW1wb3J0YW5jZSBvZiBpbmNsdWRpbmcgY29uc3VtZXItcmVsYXRlZCBtZWFzdXJlcyBpbiBwcmVkaWN0aW9uLiANCg0KIyBSdW5uaW5nIFJlZ3Jlc3Npb24gQW5hbHlzaXMgU3RlcC1ieS1TdGVwIA0KDQpJbiB0aGlzIHNlY3Rpb24sIHdlIHdpbGwgcnVuIGEgbGluZWFyIHJlZ3Jlc3Npb24gdG8gcHJlZGljdCBzYWxlcyBieSBmb2xsb3dpbmcgdGhlIGZyYW1ld29yayBkaXNjdXNzZWQgaW4gY2xhc3Mgc3RlcC1ieS1zdGVwLiBGb3IgdGhpcyBhbmFseXNpcywgd2UgYXJlIHVzaW5nIGBTYWxlc2AgYXMgdGhlIERWIGFuZCBhbGwgdGhlIG90aGVyIHZhcmlhYmxlcyBhcyB0aGUgSVZzLiANCg0KTm90ZTogVGhlIGZyYW1ld29yayBpcyBwcmVzZW50ZWQgdG8gZ2l2ZSBhIHN0cnVjdHVyZSB0byBmb2xsb3cgYXMgYSBub3ZpY2UuIElmIHlvdSBoYXZlIGJlY29tZSBhbiBleHBlcnQsIG5vIG5lZWQgdG8gZm9sbG93IGl0IGV4YWN0bHkuIA0KDQpXZSBhcmUgdXNpbmcgdGhlIGZpcnN0IDc1IHdlZWtzIHRvIGVzdGltYXRlIHRoZSBtb2RlbCAob3IgYXMgdGhlIHRyYWluIHNldCksIGFuZCB0aGUgcmVtYWluaW5nIDI1IHdlZWtzIHRvIGRvIHByZWRpY3Rpb24gKG9yIGFzIHRoZSB0ZXN0IHNldCkuIFNvLCBmcm9tIHRoZSBmdWxsIGRhdGEsIHdlIGZpcnN0IG9idGFpbiBhIHRyYWluIHNldC4gDQoNCmBgYHtyfQ0KdHJhaW4gPC0gbWFya3Nfc3BlbmNlclsxOjc1LF0NCnN0cih0cmFpbikNCmBgYA0KDQojIyBFeGFtaW5pbmcgdGhlIGRhdGENCg0KVG8gZ2V0IGEgZ29vZCBwcmVkaWN0aW9uLCB3ZSB3YW50IGdvb2QgSVZzIHRoYXQgYXJlIHJlbGV2YW50IChvciBjb3JyZWxhdGVkIHdpdGggdGhlIERWKS4gSW4gYWRkaXRpb24sIHdlIGRvIE5PVCB3YW50IElWcyB0byBiZSBoaWdobHkgY29ycmVsYXRlZCB3aXRoIG9uZSBhbm90aGVyLCB3aGljaCBsZWFkIHRvIGluZm9ybWF0aW9uIHJlZHVuZGFuY3kgYW5kIGV2ZW50dWFsbHkgYmFkIHByZWRpY3Rpb25zLiANCg0KIyMjIENoZWNraW5nIHRoZSBWSUZzDQoNCldlIHdhbnQgdG8gY2hlY2sgaWYgdGhlcmUncyBhbnkgbXVsdGktY29sbGluZWFyaXR5IHByb2JsZW0uIFdlIGRvIE5PVCB3YW50IElWcyB0byBiZSBoaWdobHkgY29ycmVsYXRlZC4gVG8gdGhpcyBlbmQsIHdlIGNhbGN1bGF0ZSB0aGUgVklGcy4gWW91IGFyZSBhbHJlYWR5IGdpdmVuIGEgZnVuY3Rpb24gY2FsbGVkIGB2aWZgIGFsb25nIHdpdGggdGhlIGRhdGEuIFRoaXMgZnVuY3Rpb24gdGFrZXMgdHdvIGlucHV0czogdGhlIG5hbWVzIG9mIElWcyB0aGF0IHlvdSB3YW50IHRvIGNoZWNrIGluIGEgZGF0YSBmcmFtZSBhbmQgdGhlIGRhdGEgZnJhbWUgY29udGFpbmluZyB0aG9zZSBJVnMsIGFuZCBvdXRwdXRzIHRoZSBWSUYgdmFsdWVzIG9mIHRoZSB2YXJpYWJsZXMgKGFzIGEgdmVjdG9yKS4gIA0KDQpgYGB7cn0NCiMgZ2V0dGluZyB0aGUgdmFyaWFibGUgbmFtZXMgb2YgdGhlIElWcw0Kdm5hbWVzIDwtIGNvbG5hbWVzKHRyYWluKVsyOjVdDQoNCiMgY2FsY3VsYXRpbmcgdGhlIHZpZidzIA0KdmlmKHZuYW1lcyx0cmFpbikNCmBgYA0KDQpUaGUgY3JpdGVyaW9uIGZvciB0aGUgVklGIHZhbHVlcyBpcyBpZiB0aGV5IGFyZSBsYXJnZXIgdGhhbiAxMC4gSW4gdGhlIG91dHB1dCwgbm8gVklGcyBhcmUgbGFyZ2VyIHRoYW4gMTAuIFNvLCB3ZSBkbyBub3QgaGF2ZSBjb2xsaW5lYXJpdHkgaXNzdWUuIA0KDQojIyBGb3JtdWxhdGluZyB0aGUgbW9kZWwNCg0KV2Ugc3RhcnQgdGhlIHNpbXBsZXN0IG1vZGVsIG9mIHVzaW5nIGBQcmljZWAgYXMgdGhlIElWIGFuZCBgU2FsZXNgIGFzIHRoZSBEVi4gVGhlIHJlZ3Jlc3Npb24gZXF1YXRpb24gaXMgYXMgdGhlIGZvbGxvd2luZzogDQokJFx0ZXh0e1NhbGVzfSA9IFxiZXRhXzArXGJldGFfMVx0ZXh0e1ByaWNlfStlJCQNCg0KIyMgRXN0aW1hdGluZyB0aGUgbW9kZWwNCg0KTmV4dCwgd2UgZXN0aW1hdGUgb3VyIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHdpdGggdGhlIGBsbWAgZnVuY3Rpb24gYnkgdHJhbnNsYXRpbmcgdGhlIGVxdWF0aW9uIGludG8gYW4gUiBmb3JtdWxhLiBQbGVhc2Ugc2VlIENhbnZhcyBmb3IgdGhlIGZvcm11bGEgbGFuZ3VhZ2UuIA0KDQpgYGB7cn0NCm1vZGVsX25vX2NvbnRyb2xzIDwtIGxtKFNhbGVzIH4gUHJpY2UsIGRhdGEgPSB0cmFpbikNCnN1bW1hcnkobW9kZWxfbm9fY29udHJvbHMpDQpgYGANCg0KIyMgVmFsaWRhdGluZyB0aGUgbW9kZWwNCg0KRnJvbSB0aGUgcmVzdWx0cyBhYm92ZSwgd2UgZmlyc3QgdmFsaWRhdGUgdGhlIG1vZGVsLiBXZSBjaGVjayBhIGZldyB0aGluZ3MuIA0KDQojIyMgVGhlIG92ZXJhbGwgc2lnbmlmaWNhbmNlDQoNClRoZSBpZGVhIG9mIHRoZSBvdmVyYWxsIHNpZ25pZmljYW5jZSBpcyB0byBjb21wYXJlIHRoZSBtb2RlbCB3ZSBydW4gdG8gYSBgbnVsbCBtb2RlbGAgd2l0aCBubyBJVnMuIElmIG91ciBtb2RlbCBzaWduaWZpY2FudGx5IG91dHBlcmZvcm1zIHRoZSBudWxsIG1vZGVsLCBpdCBtZWFucyBpdCdzIHdvcnRoIGl0LiBPciBpbiBzdGF0aXN0aWNhbCB0ZXJtLCB3ZSB0ZXN0IHRoZSBgbnVsbCBoeXBvdGhlc2lzYCB0aGF0IHRoZSBjb2VmZmljaWVudHMgKCRcYmV0YSQncykgb2YgYWxsIElWcyBhcmUgemVyby4gT3IsIA0KJCRIXzA6XGJldGFfMT1cYmV0YV8yPS4uLj0wJCQNCkZvciB0aGlzIG1vZGVsLCB0aGUgRi1zdGF0IGlzIDU3LjEsIHdpdGggYSBwLXZhbHVlIDwgMC4wNS4gU28sIHdlIGNhbiByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcywgYW5kIHRoZSBtb2RlbCBpcyBvZiBwcmVkaWN0aXZlIHZhbHVlLiANCg0KIyMjIFRoZSBzaWduaWZpY2FuY2Ugb2YgY29lZmZpY2llbnRzDQoNCldlIGhhdmUgaW5jbHVkZWQgbXVsdGlwbGUgSVZzIGluIHRoZSBtb2RlbC4gSG93ZXZlciwgaXQncyBwb3NzaWJsZSB0aGF0IGFuIElWIGRvZXMgbm90IGNvbnRyaWJ1dGUgdG8gdGhlIHByZWRpY3Rpb24gYWNjdXJhY3kuIFRvIGNoZWNrIHRoaXMsIHdlIGZvY3VzIG9uIGFuIGluZGl2aWR1YWwgY29lZmZpY2llbnQgYW5kIHRlc3QgdGhlIGZvbGxvd2luZyBudWxsIGh5cG90aGVzaXMgZm9yIGEgcGFydGljdWxhciBJVjogDQokJEhfMDpcYmV0YV9rPTAkJA0KVGhlIGNvZWZmaWNpZW50IG9mIHByaWNlICRcYmV0YV9cdGV4dHtwcmljZX0kIGlzIC0yMC43NSwgd2l0aCBhIHN0YW5kYXJkIGVycm9yIG9mICAyLjc0LiBUaGUgcC12YWx1ZSA8IDAuMDUuIFNvLCB3ZSByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcywgYW5kIGNvbmNsdWRlIHRoYXQgYFByaWNlYCBpcyBvZiBwcmVkaWN0aXZlIHZhbHVlLiANCg0KIyMjIE1vZGVsIGZpdA0KDQpUbyBzZWUgaG93IHdlbGwgdGhlIG1vZGVsIGZpdHMgd2l0aCBvdXIgZGF0YSwgd2UgbG9vayBhdCBgUi1zcXVhcmVkYC4gVGhlIGRpcmVjdCBpbnRlcnByZXRhdGlvbiBvZiBSLXNxdWFyZWQgaXMgdGhlIHBlcmNlbnRhZ2Ugb2YgdmFyaWF0aW9uIGV4cGxhaW5lZCBieSB0aGUgbW9kZWwuIEhlcmUsIHRoZSBSLXNxdWFyZWQgb2YgdGhlIG1vZGVsIGlzIDAuNDM4OSBvciA0My44OSUuIEl0IG1lYW5zIDk0Ljg1JSBvZiB2YXJpYXRpb24gaW4gdGhlIHNhbGVzIGlzIGNhcHR1cmVkIGJ5IHRoZSBtb2RlbC4gDQoNCjxzcGFuIHN0eWxlPSJjb2xvcjogcmVkOyI+VGhlcmUgaXMgbm8gY2xlYXIgY3V0b2ZmIHZhbHVlIGZvciBSLXNxdWFyZWQuPC9zcGFuPiBZb3UgbmVlZCB0byBjb25zaWRlciB0aGUgc3BlY2lmaWMgc2V0dGluZyB0byBqdWRnZSB3aGV0aGVyIHRoZSBSLXNxdWFyZWQgaXMgZ29vZCBlbm91Z2guIEZvciBleGFtcGxlLCBpbiBzYWxlcyBwcmVkaWN0aW9uLCB3ZSB1c3VhbGx5IGV4cGVjdCBhIGJpZyBSLXNxdWFyZWQgKGUuZy4sIDkwJSkuIFRoaXMgaXMgYmVjYXVzZSB0aGUgcmV0YWlsZXJzIG9mdGVudGltZXMgYXJlIGZhY2VkIHdpdGggYSByZWxhdGl2ZWx5IHN0YWJsZSBlbnZpcm9ubWVudCB3aGVyZSBjb25zdW1lcnMgc2hvdyBwZXJzaXN0ZW50IGhhYml0cyBvZiBidXlpbmcgcHJvZHVjdHMuIEluIG91ciBjYXNlLCB0aGUgdmFsdWUgb2YgNDMuODklIGluZGljYXRlcyBhIGZpdCB0aGF0IGlzIGJlbG93IGV4cGVjdGF0aW9uLiANCg0KIyMgTWFraW5nIHByZWRpY3Rpb25zDQoNCkFmdGVyIGVzdGltYXRpbmcgdGhlIG1vZGVsLCB3ZSBjYW4gdXNlIGl0IHRvIGRvIHByZWRpY3Rpb24uIEluIHRoZSBjYXNlLCB3ZSB3YW50IHRvIHByZWRpY3QgdGhlIHNhbGVzIGZyb20gYFdlZWsgNzZgIHRvIGBXZWVrIDEwMGAuIExldCdzIGZpcnN0IGNyZWF0ZSBhIHByZWRpY3Rpb24gZGF0YSBzZXQgKG9yIHRlc3Qgc2V0KS4gDQoNCmBgYHtyfQ0KdGVzdCA8LSBtYXJrc19zcGVuY2VyWzc2OjEwMCxdDQpzdHIodGVzdCkNCmBgYA0KDQpUbyBwcmVkaWN0IHRoZSBzYWxlcywgd2UgdXNlIHRoZSBgcHJlZGljdGAgZnVuY3Rpb24uIFBsZWFzZSB1c2UgYD9wcmVkaWN0YCBmb3IgbW9yZSBpbmZvcm1hdGlvbiBvZiB0aGlzIGZ1bmN0aW9uLiANCg0KYGBge3J9DQpzYWxlc19ub19jb250cm9scyA8LSBwcmVkaWN0KG1vZGVsX25vX2NvbnRyb2xzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSB0ZXN0KQ0KI2NvZXJjZSBpbnRvIGEgZGF0YSBmcmFtZQ0Kc2FsZXNfbm9fY29udHJvbHMgPC0gYXMuZGF0YS5mcmFtZShzYWxlc19ub19jb250cm9scykNCmBgYA0KDQpOZXh0LCBsZXQncyBjcmVhdGUgYSBsaW5lIHBsb3Qgb2YgdGhlIHByZWRpY3RlZCBzYWxlcyB0byBzZWUgdGhlIHByZWRpY3RlZCB0cmVuZC4gDQoNCmBgYHtyLCBmaWcud2lkdGg9NC41LCBmaWcuaGVpZ2h0PTMuMzc1fQ0KIyBhZGQgd2VlayBpZCBmb3IgcGxvdHRpbmcNCnNhbGVzX25vX2NvbnRyb2xzJHdlZWsgPC0gNzY6MTAwDQoNCiMgYSBsaW5lIHBsb3Qgb2YgdGhlIHByZWRpY3RlZCBzYWxlcw0KZ2dwbG90KGRhdGEgPSBzYWxlc19ub19jb250cm9scywNCiAgICAgICBhZXMoeCA9IHdlZWssIHkgPSBzYWxlc19ub19jb250cm9scykpICsgZ2VvbV9saW5lKCkNCmBgYA0KDQojIE1vZGVsIENvbXBhcmlzb24NCg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSBhcmUgcnVubmluZyBhIG1vZGVsIGNvbnRyb2wgdmFyaWFibGVzLiBUaGUgYW5hbHlzZXMgYXJlIHRoZSBzYW1lIGFzIGluIFNlY3Rpb24gMy4gV2Ugd291bGQgbm90IGRvIHRoaW5ncyBzdGVwLWJ5LXN0ZXAuIElmIHlvdSBhcmUgaW50ZXJlc3RlZCwgeW91IGNhbiBwcmFjdGljZSB3aXRoIHRoaXMgbW9kZWwgYnkgeW91cnNlbGYuIA0KDQpUaGUgbW9kZWwgc3BlY2lmaWNhdGlvbiBpcyBhcyB0aGUgZm9sbG93aW5nICh3aXRoIEJyYW5kIEVxdWl0eSBub3QgaW5jbHVkZWQpOg0KJCRcdGV4dHtTYWxlc30gPSBcYmV0YV8wKyBcYmV0YV8xXHRleHR7UHJpY2V9ICsgXGJldGFfMlx0ZXh0e0FkdmVydGlzaW5nfSArIFxiZXRhXzNcdGV4dHtQcm9tb3Rpb259ICsgXGJldGFfNFx0ZXh0e0JyYW5kX0VxdWl0eX0gK2UkJA0KTmV4dCwgd2UgZXN0aW1hdGUgb3VyIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHdpdGggdGhlIGBsbWAgZnVuY3Rpb24gYnkgdHJhbnNsYXRpbmcgdGhlIGVxdWF0aW9uIGludG8gYW4gUiBmb3JtdWxhLiBQbGVhc2Ugc2VlIENhbnZhcyBmb3IgdGhlIGZvcm11bGEgbGFuZ3VhZ2UuIA0KDQpgYGB7cn0NCm1vZGVsX3dpdGhfY29udHJvbHMgPC0gbG0oU2FsZXMgfiBQcmljZSArIEFkdmVydGlzaW5nICsgUHJvbW90aW9uICsgQnJhbmRfRXF1aXR5LCBkYXRhID0gdHJhaW4pDQpzdW1tYXJ5KG1vZGVsX3dpdGhfY29udHJvbHMpDQpgYGANCg0KVG8gY29tcGFyZSB0aGlzIG1vZGVsIHdpdGggdGhlIG1vZGVsIHdpdGggQnJhbmQgRXF1aXR5LCB3ZSB1c2UgYGFkanVzdGVkIFItc3F1YXJlZGAuIFRoaXMgaXMgYmVjYXVzZSB0aGUgdHdvIG1vZGVsIGhhdmUgZGlmZmVyZW50IG5vLiBvZiBJVnMuIFNvLCB3ZSBtdXN0IGBhZGp1c3RgIGZvciB0aGlzIGRpZmZlcmVuY2UuIFRoZSBhZGp1c3RlZCBSLXNxdWFyZWQgb2YgdGhlIG1vZGVsIHdpdGggdnMuIHdpdGhvdXQgY29udHJvbHMgYXJlIDAuOTQ1NSB2cy4gMC40MzEyLiBUaGVyZWZvcmUsIHRoZSBtb2RlbCB3aXRoIGNvbnRyb2xzIGZpdHMgdGhlIGRhdGEgYmV0dGVyLiANCg0KVGhlIGNvZWZmaWNpZW50IG9mIHByaWNlIGluIHRoaXMgbW9kZWwgaXMgLTIwLjIyLCBsYXJnZXIgdGhhbiB0aGF0IGluIHRoZSBtb2RlbCB3aXRob3V0IGNvbnRyb2wgdmFyaWFibGVzICgtMjAuNzUpLiBUaGUgcmVsYXRpdmVseSBzbWFsbCBjaGFuZ2UgaW4gcHJpY2UgY29lZmZpY2llbnQgZG9lcyBub3QgbWVhbiB3ZSBoYXZlIGEgZ29vZCBlc3RpbWF0aW9uIG9mIHByaWNlIGVmZmVjdC4gSXQgaXMgcG9zc2libGUgdGhhdCB3ZSBmYWlsIHRvIGFkZCBvdGhlciBjb250cm9sIHZhcmlhYmxlcywgc3VjaCBhcyBzZWFzb25hbGl0eSBvciBjb21wZXRpdGlvbi4gDQoNCiMgUmlzayBDb250cm9sIA0KDQpBZnRlciBydW5uaW5nIHRoZSBtb2RlbHMsIHdlIHdhbnQgdG8gY2hlY2sgaWYgdGhlIHN0YXRpc3RpY2FsIGFzc3VtcHRpb25zIG9mIGxpbmVhciByZWdyZXNzaW9uIGFyZSBPa2F5LiBBcyBkaXNjdXNzZWQgaW4gY2xhc3MsIHRoZSB2aW9sYXRpb24gb2YgdGhlc2UgYXNzdW1wdGlvbnMgbGVhZHMgdG8gYmlhc2VkIGVzdGltYXRpb24gYW5kIGJhZCBwcmVkaWN0aW9uLiBPdmVyIHRoZSB5ZWFycywgcGVvcGxlIGhhdmUgZGV2ZWxvcGVkIG1hbnkgdGVzdHMgZm9yIHRoZXNlIGFzc3VtcHRpb25zLiBXZSB3aWxsIGZvY3VzIG9uIHR3byBhc3N1bXB0aW9uczogMSkgdGhlIG5vcm1hbGl0eSBhc3N1bXB0aW9uOyBhbmQgMikgdGhlIGVxdWFsIHZhcmlhbmNlIGFzc3VtcHRpb24uIA0KDQpXZSB3aWxsIHVzZSB0aGUgZnVsbCBtb2RlbCAodGhlIG1vZGVsIHdpdGggYnJhbmQgZXF1aXR5IG9yICkgYXMgYW4gZXhhbXBsZS4gRm9yIG90aGVyIG1vZGVscywgdGhlIHByb2NlZHVyZSB3b3JrcyB0aGUgc2FtZS4gDQoNCiMjIFRoZSBub3JtYWxpdHkgYXNzdW1wdGlvbiANCg0KRm9yIHRoZSBub3JtYWxpdHkgYXNzdW1wdGlvbiwgd2UgYXJlIHVzaW5nIEtTIHRlc3QuIEtTIHRlc3QgY2hlY2tzIGEgdmFyaWFibGUgYWdhaW5zdCBhIG5vcm1hbCBkaXN0cmlidXRpb24gYW5kIHRoZSB2YWx1ZSBvZiB0aGUgS1MgdGVzdCB0ZWxscyB5b3Ugd2hldGhlciB0aGUgdmFyaWFibGUgZm9sbG93cyBhIG5vcm1hbCBkaXN0cmlidXRpb24uIA0KDQpOb3RlIHRoYXQgdGhlIG51bGwgaHlwb3RoZXNpcyBvZiB0aGUgdGVzdCBpczogDQokJEhfMDogXHRleHR7VGhlIHZhcmlhYmxlIGZvbGxvd3MgYSBub3JtYWwgZGlzdHJpYnV0aW9ufS4kJA0KRm9yIHRoZSBub3JtYWxpdHkgYXNzdW1wdGlvbiB0byBob2xkLCB0aGUgbnVsbCBoeXBvdGhlc2lzIHNob3VsZCBub3QgYmUgcmVqZWN0ZWQuIEluIG90aGVyIHdvcmRzLCB0aGUgS1MgdGVzdCBzaG91bGQgTk9UIGJlIHNpZ25pZmljYW50IGlmIHRoZSBub3JtYWxpdHkgYXNzdW1wdGlvbiBob2xkcy4gDQoNCkZpcnN0LCB3ZSBvYnRhaW4gdGhlIHJlc2lkdWFscyBmcm9tIHRoZSBlc3RpbWF0aW9uIHJlc3VsdHMgYW5kIHZpc3VhbGl6ZSB0aGUgZGlzdHJpYnV0aW9uIHdpdGggYSBoaXN0b2dyYW0uIA0KDQpgYGB7ciwgZmlnLndpZHRoPTQuNSwgZmlnLmhlaWdodD0zLjM3NX0NCiMgb2J0YWluIHRoZSByZXNpZHVhbHMgdXNpbmcgdGhlIHJlc2lkdWFscygpIGZ1bmN0aW9uDQplcnJvcl90ZXJtIDwtIHJlc2lkdWFscyhtb2RlbF9ub19jb250cm9scykNCg0KIyBidWlsZCBhIGhpc3RvZ3JhbQ0KIyBsZWF2ZSB0aGUgZGF0YSBlbXB0eSBhcyB3ZSBvbmx5IHBsb3QgYSB2YXJpYWJsZSBlcnJvcl90ZXJtDQpnZ3Bsb3QoICwgYWVzKHg9ZXJyb3JfdGVybSkpICsgZ2VvbV9oaXN0b2dyYW0oKQ0KDQpgYGANCg0KRnJvbSB0aGUgcGxvdCwgaXQgc2VlbXMgdGhlIGVycm9yX3Rlcm0gZG9lcyBub3QgZm9sbG93IGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4gV2UgbWF5IGZ1cnRoZXIgdXNlIEtTIHRlc3QgdG8gZG8gdGhlIGZvcm1hbCB0ZXN0aW5nLiBOb3RlIHRoYXQgYnkgZGVmYXVsdCwgdGhlIEtTIHRlc3QgY29tcGFyZXMgYSB2YXJpYWJsZSB0byBhIHN0YW5kYXJkIG5vcm1hbCBkaXN0cmlidXRpb24sIG9yIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiB3aXRoIG1lYW4gMCBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIDEuIFRoZXJlZm9yZSwgd2UgbmVlZCB0byBmaXJzdCByZS1zY2FsZSB0aGUgcmVzaWR1YWxzIHRvIG1ha2UgaXQgY29uc2lzdGVudCB3aXRoIHRoZSBkZWZhdWx0IGRpc3RyaWJ1dGlvbi4gDQoNCmBgYHtyfQ0KIyBzY2FsZSgpIGZ1bmN0aW9uIHN0YW5kYXJkaXplcyBhIHZhcmlhYmxlIHVzZSBoZWxwKHNjYWxlKSB0byBzZWUgbW9yZS4NCnN0ZF9lcnJvcl90ZXJtIDwtIHNjYWxlKGVycm9yX3Rlcm0pDQoNCiMgdGhlIGtzIHRlc3Qgd2l0aCBrcy50ZXN0KCk7IA0KIyJwbm9ybSIgbWVhbnMgdGVzdGluZyBhZ2FpbnN0IHRoZSBzdGFuZGFyZCBub3JtYWwgZGlzdHJpYnV0aW9uDQprcy50ZXN0KHN0ZF9lcnJvcl90ZXJtLCJwbm9ybSIpDQpgYGANCg0KRnJvbSB0aGUgdGVzdCByZXN1bHRzLCBwLXZhbHVlIGlzIDAuMTEgPiAwLjA1LiBTbywgd2UgY2Fubm90IHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzLiBUaGlzIGltcGxpZXMgdGhlIG5vcm1hbGl0eSBhc3N1bXB0aW9uIGlzIE9rYXkuIA0KDQojIyBUaGUgZXF1YWwgdmFyaWFuY2UgYXNzdW1wdGlvbg0KDQpBbm90aGVyIGFzc3VtcHRpb24gb2YgbGluZWFyIHJlZ3Jlc3Npb24gaXMgYWxsIHJlc2lkdWFscyBmb2xsb3cgdGhlIHNhbWUgbm9ybWFsIGRpc3RyaWJ1dGlvbiBvciB0aGUgc2FtZSB2YXJpYW5jZS4gQWx0aG91Z2ggdGhlcmUgaXMgYSBmb3JtYWwgdGVzdCBmb3IgdGhpcywgaXQgaXMgYmV5b25kIHRoZSBzY29wZSBvZiB0aGlzIGNvdXJzZS4gSW5zdGVhZCwgd2UgcmVseSBvbiBwbG90dGluZyB0byBleWViYWxsIHRoaXMgYXNzdW1wdGlvbi4NCg0KVG8gcHJvZHVjZSB0aGlzIHBsb3QsIHdlIG5lZWQgdHdvIHZhbHVlczogdGhlIHJlc2lkdWFscyBhbmQgdGhlIERWIChzYWxlcyBpbiB0aGlzIGNhc2UpLiBXZSBkbyBhIHNjYXR0ZXIgcGxvdCB3aXRoIHRoZSByZXNpZHVhbHMgYXMgdGhlIFktYXhpcyBhbmQgdGhlIHByZWRpY3RlZCBEViBhcyB0aGUgWC1heGlzLiBJbiBhZGRpdGlvbiwgc29tZXRpbWVzIGl0J3Mgbm90IHNvIGVhc3kgdG8gc2VlIHRoZSBwYXR0ZXJucyBkdWUgdG8gdGhlIHNjYWxlcyBvZiB2YXJpYWJsZXMuIFNvLCB3ZSByZXNjYWxlIHRoZSB2YXJpYWJsZXMgYnkgc3RhbmRhcmRpemF0aW9uICh3aXRoIHRoZSBgc2NhbGUoKWAgZnVuY3Rpb24pLiANCg0KYGBge3IsIGZpZy53aWR0aD00LjUsIGZpZy5oZWlnaHQ9My4zNzV9DQojIGdldHRpbmcgdGhlIHByZWRpY3RlZCBzYWxlcyBvZiB0aGUgdGVzdCBzZXQNCnRlc3Rfc2FsZXNfbm9fY29udHJvbHMgPC0gcHJlZGljdChtb2RlbF9ub19jb250cm9scykNCg0KIyBwbG90dGluZyB0byBzZWUgdGhlIHJlc2lkdWFscyB2cy4gcHJlZGljdGVkIHNhbGVzDQpnZ3Bsb3QoICwgYWVzKHkgPSBzdGRfZXJyb3JfdGVybSwgeCA9IHNjYWxlKHRlc3Rfc2FsZXNfbm9fY29udHJvbHMpKSkrIA0KICBnZW9tX3BvaW50KCkgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwKQ0KYGBgDQoNClRoZSB0cmljayBpcyB0byBjaGVjayB3aGV0aGVyIGF0IGRpZmZlcmVudCB2YWx1ZSBvZiB0aGUgRFYgKHNhbGVzKSwgdGhlIHNwYW4gb3IgdGhlIHJhbmdlIG9mIHRoZSByZXNpZHVhbHMgYXJlIHNpbWlsYXIuIFlvdSBtYXkgYWRkIGF1eGlsaWFyeSBsaW5lcyB0byBoZWxwIHlvdSBjaGVjay4NCg0KIVtFcXVhbCBWYXJpYW5jZSBTY2F0dGVyIFBsb3RdKGVxdWFsX3ZhcmlhbmNlLmpwZyl7d2lkdGg9ODAlfQ0KDQpGcm9tIHRoZSBwbG90LCB3ZSBhbiBzZWUgdGhhdCB0aGUgcmFuZ2Ugb3Igc3BhbiBvZiByZXNpZHVhbHMgYXJlIHNpbWlsYXIuIFNvLCB0aGUgZXF1YWwgdmFyaWFuY2UgYXNzdW1wdGlvbiBpcyBsaWtlbHkgdG8gaG9sZC4gTm90ZSB0aGF0IGFzIHdlIHJlbHkgb24gZXllYmFsbGluZywgaXQgY2FuIGJlIHN1YmplY3RpdmUuIEZvciB0aGUgbGVhcm5pbmcgcHVycG9zZSwgYXMgbG9uZyBhcyB5b3Uga25vdyBob3cgdG8gdXNlIHRoZSBwbG90IHRvIGRldGVjdCBlcXVhbCB2YXJpYW5jZSwgeW91ciBjb25jbHVzaW9uIGlzIG5vdCBhbiBpc3N1ZS4gIA==