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
For this notebook, you need to download and load a R package ggplot2.
library("ggplot2")
Data Description
You can download the “Marks Spencer.RData” file from Canvas. Please put the data in the same folder as the R notebook. After this, you can load the data into R.
load("Marks Spencer.RData")
The data includes the following variables:
- Week: the id’s of weeks in total 100 weeks
- Advertising: the advertising intensity
- Promotion: a factor of two-levels - “Yes” or “No”; the baseline is already set to “No”
- Price: the price index
- Brand_Equity: a measure of weekly brand equity of Marks & Spencer
- Sales: the overall sales
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 using two models, one model with Brand Equity and the other model without Brand Equity.
Run the code below to get a sneak peak of the data:
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
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, and the remaining 25 weeks to do prediction. 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.
Correlation between IVs and the DV
You can compute correlation coefficients as you learn from CMR. Alternatively, you can visualize the relationships between IVs and the DV with scatter plots. Here, we will only plot the relationship between Price and Sales as an example. Try other variables on your own.
ggplot(data = train, aes(x = Price, y = Sales)) + geom_point()

From the plot, it is clear that Price is negatively correlated with Sales.
Checking the VIFs
We also want to check if there’s any multi-collinearity problem. Essentially, 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 and a 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]
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.
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 322.1, with a p-value < 2.2e-16 < 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\] For example, if we check Brand Equity, the coefficient \(\beta_{brandequity}\) is 1.46, with a standard error of 0.06. The p-value < 2.2e-16 < 0.05. So, we reject the null hypothesis, and conclude that Brand Equity is of predictive value.
Model fit
The first index to look at is the residual standard errors, which form the foundation for other indexes such as R-squared. In our model, the value is 6.36. This is relatively low as the average Sales is 158.23.
mean(train$Sales)
## [1] 158.2321
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.9485 or 94.85%. 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 94.85% indicates a good fit.
Predicting with the model
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. We can also obtain the 95% confidence interval of the prediction so we can prepare for the worst- and best-case scenario.
sales_with_brand <- predict(model_with_brand,
newdata = test,
interval = "confidence")
#coerce into a data frame
sales_with_brand <- as.data.frame(sales_with_brand)
Next, let’s create a line plot of the predicted sales to see the predicted trend.
# add week id for plotting
sales_with_brand$week <- 76:100
ggplot(data = sales_with_brand,
aes(x = week, y = fit)) + geom_line()

From the plot, we can see a decreasing trend of sales.
Model Comparision
In this section, we are running a model without Brand Equity. 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): \[Sales = \beta_0+\beta_1Advertising + \beta_2Promotion + \beta_3Price +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_without_brand <- lm(Sales ~ 1 + Advertising + Promotion + Price,
data = train)
summary(model_without_brand)
##
## Call:
## lm(formula = Sales ~ 1 + Advertising + Promotion + Price, data = train)
##
## Residuals:
## Min 1Q Median 3Q Max
## -43.413 -17.598 2.055 17.177 33.894
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 225.630 17.182 13.131 < 2e-16 ***
## Advertising 2.916 2.131 1.368 0.1755
## PromotionYes -11.473 5.119 -2.241 0.0281 *
## Price -20.183 2.686 -7.515 1.32e-10 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 20.03 on 71 degrees of freedom
## Multiple R-squared: 0.4822, Adjusted R-squared: 0.4603
## F-statistic: 22.04 on 3 and 71 DF, p-value: 3.416e-10
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 Brand Equity is 0.9455 vs. 0.4603. Therefore, the model with Brand Equity fits the data better.
Next, let’s get the predicted sales of this model, using the same code as above.
sales_without_brand <- predict(model_without_brand,
newdata = test,
interval = "confidence")
#coerce into a data frame
sales_without_brand <- as.data.frame(sales_without_brand)
Again, we plot the predicted sales:
# add week id for plotting
sales_without_brand$week <- 76:100
ggplot(data = sales_without_brand,
aes(x = week, y = fit)) + geom_line()

From this plot, we do not see a clear decreasing trend in sales. To conclude, to correctly predict the sales decrease, the company also needs to consider consumer metrics such as Brand Equity.
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: 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_with_brand)
# 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 follows 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 rescale 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")
##
## One-sample Kolmogorov-Smirnov test
##
## data: std_error_term
## D = 0.077301, p-value = 0.7318
## alternative hypothesis: two-sided
From the test results, p-value is 0.73 > 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 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).
ggplot( , aes(y = std_error_term, x = scale(train$Sales)))+
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.
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.
LS0tDQp0aXRsZTogJ0xlY3R1cmUgMiAtIE1hcmtldCBSZXNwb25zZSBNb2RlbCcNCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogZGVmYXVsdA0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHRoZW1lOiByZWFkYWJsZQ0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiANCiAgICAgIGNvbGxhcHNlZDogbm8NCiAgICAgIHNtb290aF9zY3JvbGw6IG5vDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgcGRmX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQotLS0NCg0KIyBSZWFkLm1lDQpUaGUgc3RydWN0dXJlIG9mIHRoZSBub3RlYm9vayBpcyBhcyB0aGUgZm9sbG93aW5nOg0KDQoqIERhdGEgRGVzY3JpcHRpb246IGRlc2NyaXB0aW9uIG9mIHRoZSBkYXRhIGFuZCBtaW5pIGNhc2UNCiogNS1TdGVwIEZyYW1ld29yayBmb3IgUmVncmVzc2lvbiBBbmFseXNpcw0KKiBNb2RlbCBDb21wYXJpc29uOiBhIG1vZGVsIHdpdGhvdXQgYEJyYW5kIEVxdWl0eWAgZm9yIGNvbXBhcmlzb24NCiogUmlzayBDb250cm9sOiB0byBjaGVjayB0aGUgbm9ybWFsaXR5IGFuZCBlcXVhbCB2YXJpYW5jZSBhc3N1bXB0aW9ucw0KDQpGb3IgdGhpcyBub3RlYm9vaywgeW91IG5lZWQgdG8gZG93bmxvYWQgYW5kIGxvYWQgYSBSIHBhY2thZ2UgYGdncGxvdDJgLiANCmBgYHtyIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoImdncGxvdDIiKQ0KYGBgDQoNCg0KIyBEYXRhIERlc2NyaXB0aW9uIA0KWW91IGNhbiBkb3dubG9hZCB0aGUgIk1hcmtzIFNwZW5jZXIuUkRhdGEiIGZpbGUgZnJvbSBDYW52YXMuIFBsZWFzZSBwdXQgdGhlIGRhdGEgaW4gdGhlIHNhbWUgZm9sZGVyIGFzIHRoZSBSIG5vdGVib29rLiBBZnRlciB0aGlzLCB5b3UgY2FuIGxvYWQgdGhlIGRhdGEgaW50byBSLg0KYGBge3IgfQ0KbG9hZCgiTWFya3MgU3BlbmNlci5SRGF0YSIpDQpgYGANClRoZSBkYXRhIGluY2x1ZGVzIHRoZSBmb2xsb3dpbmcgdmFyaWFibGVzOiANCg0KKiBXZWVrOiB0aGUgaWQncyBvZiB3ZWVrcyBpbiB0b3RhbCAxMDAgd2Vla3MNCiogQWR2ZXJ0aXNpbmc6IHRoZSBhZHZlcnRpc2luZyBpbnRlbnNpdHkgDQoqIFByb21vdGlvbjogYSBmYWN0b3Igb2YgdHdvLWxldmVscyAtICJZZXMiIG9yICJObyI7IHRoZSBiYXNlbGluZSBpcyBhbHJlYWR5IHNldCB0byAiTm8iDQoqIFByaWNlOiB0aGUgcHJpY2UgaW5kZXgNCiogQnJhbmRfRXF1aXR5OiBhIG1lYXN1cmUgb2Ygd2Vla2x5IGJyYW5kIGVxdWl0eSBvZiBNYXJrcyAmIFNwZW5jZXINCiogU2FsZXM6IHRoZSBvdmVyYWxsIHNhbGVzIA0KDQpGcm9tIHRoZSBkYXRhLCB3ZSBrbm93IHRoZSBzYWxlcyBmcm9tIGBXZWVrIDFgIHRvIGBXZWVrIDc1YC4gVXNpbmcgdGhlIGZpcnN0IDc1IHdlZWtzIG9mIGRhdGEsIHdlIHdhbnQgdG8gcHJlZGljdCB0aGUgc2FsZXMgZnJvbSBgV2VlayA3NmAgdG8gYFdlZWsgMTAwYC4gSXQgaXMga25vd24gYWQgaG9jIHRoYXQgdGhlIHNhbGVzIG9mIE1hcmtzICYgU3BlbmNlciBkZWNyZWFzZWQgYWZ0ZXIgYFdlZWsgNzVgLiBUaGUgcXVlc3Rpb24gaXM6IDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkOyI+Y2FuIHdlIHByZWRpY3QgdGhlIHNhbGVzIGRlY3JlYXNlPzwvc3Bhbj4NCg0KV2UgYXJlIHVzaW5nIHR3byBtb2RlbHMsIG9uZSBtb2RlbCB3aXRoIEJyYW5kIEVxdWl0eSBhbmQgdGhlIG90aGVyIG1vZGVsIHdpdGhvdXQgQnJhbmQgRXF1aXR5LiANCg0KUnVuIHRoZSBjb2RlIGJlbG93IHRvIGdldCBhIHNuZWFrIHBlYWsgb2YgdGhlIGRhdGE6IA0KYGBge3J9DQpoZWFkKG1hcmtzX3NwZW5jZXIpDQpgYGANCg0KIyBSdW5uaW5nIFJlZ3Jlc3Npb24gQW5hbHlzaXMgU3RlcC1ieS1TdGVwIA0KSW4gdGhpcyBzZWN0aW9uLCB3ZSB3aWxsIHJ1biBhIGxpbmVhciByZWdyZXNzaW9uIHRvIHByZWRpY3Qgc2FsZXMgYnkgZm9sbG93aW5nIHRoZSBmcmFtZXdvcmsgZGlzY3Vzc2VkIGluIGNsYXNzIHN0ZXAtYnktc3RlcC4gRm9yIHRoaXMgYW5hbHlzaXMsIHdlIGFyZSB1c2luZyBgU2FsZXNgIGFzIHRoZSBEViBhbmQgYWxsIHRoZSBvdGhlciB2YXJpYWJsZXMgYXMgdGhlIElWcy4gDQoNCk5vdGU6IFRoZSBmcmFtZXdvcmsgaXMgcHJlc2VudGVkIHRvIGdpdmUgYSBzdHJ1Y3R1cmUgdG8gZm9sbG93IGFzIGEgbm92aWNlLiBJZiB5b3UgaGF2ZSBiZWNvbWUgYW4gZXhwZXJ0LCBubyBuZWVkIHRvIGZvbGxvdyBpdCBleGFjdGx5LiANCg0KV2UgYXJlIHVzaW5nIHRoZSBmaXJzdCA3NSB3ZWVrcyB0byBlc3RpbWF0ZSB0aGUgbW9kZWwsIGFuZCB0aGUgcmVtYWluaW5nIDI1IHdlZWtzIHRvIGRvIHByZWRpY3Rpb24uIFNvLCBmcm9tIHRoZSBmdWxsIGRhdGEsIHdlIGZpcnN0IG9idGFpbiBhIHRyYWluIHNldC4gDQoNCmBgYHtyfQ0KdHJhaW4gPC0gbWFya3Nfc3BlbmNlclsxOjc1LF0NCnN0cih0cmFpbikNCmBgYA0KDQojIyBFeGFtaW5pbmcgdGhlIGRhdGENCg0KVG8gZ2V0IGEgZ29vZCBwcmVkaWN0aW9uLCB3ZSB3YW50IGdvb2QgSVZzIHRoYXQgYXJlIHJlbGV2YW50IChvciBjb3JyZWxhdGVkIHdpdGggdGhlIERWKS4gSW4gYWRkaXRpb24sIHdlIGRvIE5PVCB3YW50IElWcyB0byBiZSBoaWdobHkgY29ycmVsYXRlZCB3aXRoIG9uZSBhbm90aGVyLCB3aGljaCBsZWFkIHRvIGluZm9ybWF0aW9uIHJlZHVuZGFuY3kgYW5kIGV2ZW50dWFsbHkgYmFkIHByZWRpY3Rpb25zLiANCg0KIyMjIENvcnJlbGF0aW9uIGJldHdlZW4gSVZzIGFuZCB0aGUgRFYNCg0KWW91IGNhbiBjb21wdXRlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cyBhcyB5b3UgbGVhcm4gZnJvbSBDTVIuIEFsdGVybmF0aXZlbHksIHlvdSBjYW4gdmlzdWFsaXplIHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gSVZzIGFuZCB0aGUgRFYgd2l0aCBzY2F0dGVyIHBsb3RzLiBIZXJlLCB3ZSB3aWxsIG9ubHkgcGxvdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYFByaWNlYCBhbmQgYFNhbGVzYCBhcyBhbiBleGFtcGxlLiBUcnkgb3RoZXIgdmFyaWFibGVzIG9uIHlvdXIgb3duLiANCg0KYGBge3IsIGZpZy53aWR0aD00LjUsIGZpZy5oZWlnaHQ9My4zNzV9DQpnZ3Bsb3QoZGF0YSA9IHRyYWluLCBhZXMoeCA9IFByaWNlLCB5ID0gU2FsZXMpKSArIGdlb21fcG9pbnQoKQ0KYGBgDQoNCkZyb20gdGhlIHBsb3QsIGl0IGlzIGNsZWFyIHRoYXQgYFByaWNlYCBpcyBuZWdhdGl2ZWx5IGNvcnJlbGF0ZWQgd2l0aCBgU2FsZXNgLiANCg0KIyMjIENoZWNraW5nIHRoZSBWSUZzDQoNCldlIGFsc28gd2FudCB0byBjaGVjayBpZiB0aGVyZSdzIGFueSBtdWx0aS1jb2xsaW5lYXJpdHkgcHJvYmxlbS4gRXNzZW50aWFsbHksIHdlIGRvIG5vdCB3YW50IElWcyB0byBiZSBoaWdobHkgY29ycmVsYXRlZC4gVG8gdGhpcyBlbmQsIHdlIGNhbGN1bGF0ZSB0aGUgVklGcy4gWW91IGFyZSBhbHJlYWR5IGdpdmVuIGEgZnVuY3Rpb24gY2FsbGVkIGB2aWZgIGFsb25nIHdpdGggdGhlIGRhdGEuIFRoaXMgZnVuY3Rpb24gdGFrZXMgdHdvIGlucHV0czogdGhlIG5hbWVzIG9mIElWcyB0aGF0IHlvdSB3YW50IHRvIGNoZWNrIGFuZCBhIGRhdGEgZnJhbWUgY29udGFpbmluZyB0aG9zZSBJVnMsIGFuZCBvdXRwdXRzIHRoZSBWSUYgdmFsdWVzIG9mIHRoZSB2YXJpYWJsZXMgKGFzIGEgdmVjdG9yKS4gIA0KDQpgYGB7cn0NCiMgZ2V0dGluZyB0aGUgdmFyaWFibGUgbmFtZXMgb2YgdGhlIElWcw0Kdm5hbWVzIDwtIGNvbG5hbWVzKHRyYWluKVsyOjVdDQp2aWYodm5hbWVzLHRyYWluKQ0KYGBgDQpUaGUgY3JpdGVyaW9uIGZvciB0aGUgVklGIHZhbHVlcyBpcyBpZiB0aGV5IGFyZSBsYXJnZXIgdGhhbiAxMC4gSW4gdGhlIG91dHB1dCwgbm8gVklGcyBhcmUgbGFyZ2VyIHRoYW4gMTAuIFNvLCB3ZSBkbyBub3QgaGF2ZSBjb2xsaW5lYXJpdHkgaXNzdWUuIA0KDQojIyMgRm9ybXVsYXRpbmcgdGhlIG1vZGVsDQoNCldlIGFyZSB1c2luZyBhbGwgdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlczogYEFkdmVydGlzaW5nYCwgYFByb21vdGlvbmAsIGBQcmljZWAgYW5kIGBCcmFuZF9FcXVpdHlgIHRvIHByZWRpY3QgYFNhbGVzYC4gVGhlIHJlZ3Jlc3Npb24gZXF1YXRpb24gaXMgYXMgdGhlIGZvbGxvd2luZzogDQokJFNhbGVzID0gXGJldGFfMCtcYmV0YV8xQWR2ZXJ0aXNpbmcgKyBcYmV0YV8yUHJvbW90aW9uICsgXGJldGFfM1ByaWNlICsgXGJldGFfNEJyYW5kRXF1aXR5K2UkJA0KIyMgRXN0aW1hdGluZyBUaGUgTW9kZWwNCk5leHQsIHdlIGVzdGltYXRlIG91ciBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB3aXRoIHRoZSBgbG1gIGZ1bmN0aW9uIGJ5IHRyYW5zbGF0aW5nIHRoZSBlcXVhdGlvbiBpbnRvIGFuIFIgZm9ybXVsYS4gUGxlYXNlIHNlZSBDYW52YXMgZm9yIHRoZSBmb3JtdWxhIGxhbmd1YWdlLiANCg0KYGBge3J9DQptb2RlbF93aXRoX2JyYW5kIDwtIGxtKFNhbGVzIH4gMSArIEFkdmVydGlzaW5nICsgUHJvbW90aW9uICsgUHJpY2UgKyANCiAgICAgICAgICAgICAgICAgICAgICAgIEJyYW5kX0VxdWl0eSwgZGF0YSA9IHRyYWluKQ0Kc3VtbWFyeShtb2RlbF93aXRoX2JyYW5kKQ0KYGBgDQojIyBWYWxpZGF0aW5nIHRoZSBtb2RlbA0KDQpGcm9tIHRoZSByZXN1bHRzIGFib3ZlLCB3ZSBmaXJzdCB2YWxpZGF0ZSB0aGUgbW9kZWwuIFdlIGNoZWNrIGEgZmV3IHRoaW5ncy4gDQoNCiMjIyBUaGUgb3ZlcmFsbCBzaWduaWZpY2FuY2UNCg0KVGhlIGlkZWEgb2YgdGhlIG92ZXJhbGwgc2lnbmlmaWNhbmNlIGlzIHRvIGNvbXBhcmUgdGhlIG1vZGVsIHdlIHJ1biB0byBhIGBudWxsIG1vZGVsYCB3aXRoIG5vIElWcy4gSWYgb3VyIG1vZGVsIHNpZ25pZmljYW50bHkgb3V0cGVyZm9ybXMgdGhlIG51bGwgbW9kZWwsIGl0IG1lYW5zIGl0J3Mgd29ydGggaXQuIE9yIGluIHN0YXRpc3RpY2FsIHRlcm0sIHdlIHRlc3QgdGhlIGBudWxsIGh5cG90aGVzaXNgIHRoYXQgdGhlIGNvZWZmaWNpZW50cyAoJFxiZXRhJCdzKSBvZiBhbGwgSVZzIGFyZSB6ZXJvLiBPciwgDQokJEhfMDpcYmV0YV8xPVxiZXRhXzI9Li4uPTAkJA0KRm9yIHRoaXMgbW9kZWwsIHRoZSBGLXN0YXQgaXMgMzIyLjEsIHdpdGggYSBwLXZhbHVlIDwgMi4yZS0xNiA8IDAuMDUuIFNvLCB3ZSBjYW4gcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMsIGFuZCB0aGUgbW9kZWwgaXMgb2YgcHJlZGljdGl2ZSB2YWx1ZS4gDQoNCiMjIyBUaGUgc2lnbmlmaWNhbmNlIG9mIGNvZWZmaWNpZW50cw0KDQpXZSBoYXZlIGluY2x1ZGVkIG11bHRpcGxlIElWcyBpbiB0aGUgbW9kZWwuIEhvd2V2ZXIsIGl0J3MgcG9zc2libGUgdGhhdCBhbiBJViBkb2VzIG5vdCBjb250cmlidXRlIHRvIHRoZSBwcmVkaWN0aW9uIGFjY3VyYWN5LiBUbyBjaGVjayB0aGlzLCB3ZSBmb2N1cyBvbiBhbiBpbmRpdmlkdWFsIGNvZWZmaWNpZW50IGFuZCB0ZXN0IHRoZSBmb2xsb3dpbmcgbnVsbCBoeXBvdGhlc2lzIGZvciBhIHBhcnRpY3VsYXIgSVY6IA0KJCRIXzA6XGJldGFfaz0wJCQNCkZvciBleGFtcGxlLCBpZiB3ZSBjaGVjayBgQnJhbmQgRXF1aXR5YCwgdGhlIGNvZWZmaWNpZW50ICRcYmV0YV97YnJhbmRlcXVpdHl9JCBpcyAxLjQ2LCB3aXRoIGEgc3RhbmRhcmQgZXJyb3Igb2YgMC4wNi4gVGhlIHAtdmFsdWUgPCAyLjJlLTE2IDwgMC4wNS4gU28sIHdlIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzLCBhbmQgY29uY2x1ZGUgdGhhdCBgQnJhbmQgRXF1aXR5YCBpcyBvZiBwcmVkaWN0aXZlIHZhbHVlLiANCg0KIyMjIE1vZGVsIGZpdA0KDQpUaGUgZmlyc3QgaW5kZXggdG8gbG9vayBhdCBpcyB0aGUgYHJlc2lkdWFsIHN0YW5kYXJkIGVycm9yc2AsIHdoaWNoIGZvcm0gdGhlIGZvdW5kYXRpb24gZm9yIG90aGVyIGluZGV4ZXMgc3VjaCBhcyBSLXNxdWFyZWQuIEluIG91ciBtb2RlbCwgdGhlIHZhbHVlIGlzIDYuMzYuIFRoaXMgaXMgcmVsYXRpdmVseSBsb3cgYXMgdGhlIGF2ZXJhZ2UgU2FsZXMgaXMgMTU4LjIzLiANCmBgYHtyfQ0KbWVhbih0cmFpbiRTYWxlcykNCmBgYA0KDQpUbyBzZWUgaG93IHdlbGwgdGhlIG1vZGVsIGZpdHMgd2l0aCBvdXIgZGF0YSwgd2UgbG9vayBhdCBgUi1zcXVhcmVkYC4gVGhlIGRpcmVjdCBpbnRlcnByZXRhdGlvbiBvZiBSLXNxdWFyZWQgaXMgdGhlIHBlcmNlbnRhZ2Ugb2YgdmFyaWF0aW9uIGV4cGxhaW5lZCBieSB0aGUgbW9kZWwuIEhlcmUsIHRoZSBSLXNxdWFyZWQgb2YgdGhlIG1vZGVsIGlzIDAuOTQ4NSBvciA5NC44NSUuIEl0IG1lYW5zIDk0Ljg1JSBvZiB2YXJpYXRpb24gaW4gdGhlIHNhbGVzIGlzIGNhcHR1cmVkIGJ5IHRoZSBtb2RlbC4gDQoNCjxzcGFuIHN0eWxlPSJjb2xvcjogcmVkOyI+VGhlcmUgaXMgbm8gY2xlYXIgY3V0b2ZmIHZhbHVlIGZvciBSLXNxdWFyZWQuPC9zcGFuPiBZb3UgbmVlZCB0byBjb25zaWRlciB0aGUgc3BlY2lmaWMgc2V0dGluZyB0byBqdWRnZSB3aGV0aGVyIHRoZSBSLXNxdWFyZWQgaXMgZ29vZCBlbm91Z2guIEZvciBleGFtcGxlLCBpbiBzYWxlcyBwcmVkaWN0aW9uLCB3ZSB1c3VhbGx5IGV4cGVjdCBhIGJpZyBSLXNxdWFyZWQgKGUuZy4sIDkwJSkuIFRoaXMgaXMgYmVjYXVzZSB0aGUgcmV0YWlsZXJzIG9mdGVudGltZXMgYXJlIGZhY2VkIHdpdGggYSByZWxhdGl2ZWx5IHN0YWJsZSBlbnZpcm9ubWVudCB3aGVyZSBjb25zdW1lcnMgc2hvdyBwZXJzaXN0ZW50IGhhYml0cyBvZiBidXlpbmcgcHJvZHVjdHMuIEluIG91ciBjYXNlLCB0aGUgdmFsdWUgb2YgOTQuODUlIGluZGljYXRlcyBhIGdvb2QgZml0LiANCg0KIyMgUHJlZGljdGluZyB3aXRoIHRoZSBtb2RlbA0KDQpBZnRlciBlc3RpbWF0aW5nIHRoZSBtb2RlbCwgd2UgY2FuIHVzZSBpdCB0byBkbyBwcmVkaWN0aW9uLiBJbiB0aGUgY2FzZSwgd2Ugd2FudCB0byBwcmVkaWN0IHRoZSBzYWxlcyBmcm9tIGBXZWVrIDc2YCB0byBgV2VlayAxMDBgLiBMZXQncyBmaXJzdCBjcmVhdGUgYSBwcmVkaWN0aW9uIGRhdGEgc2V0IChvciB0ZXN0IHNldCkuIA0KYGBge3J9DQp0ZXN0IDwtIG1hcmtzX3NwZW5jZXJbNzY6MTAwLF0NCnN0cih0ZXN0KQ0KYGBgDQpUbyBwcmVkaWN0IHRoZSBzYWxlcywgd2UgdXNlIHRoZSBgcHJlZGljdGAgZnVuY3Rpb24uIFBsZWFzZSB1c2UgYD9wcmVkaWN0YCBmb3IgbW9yZSBpbmZvcm1hdGlvbiBvZiB0aGlzIGZ1bmN0aW9uLiBXZSBjYW4gYWxzbyBvYnRhaW4gdGhlIDk1JSBjb25maWRlbmNlIGludGVydmFsIG9mIHRoZSBwcmVkaWN0aW9uIHNvIHdlIGNhbiBwcmVwYXJlIGZvciB0aGUgd29yc3QtIGFuZCBiZXN0LWNhc2Ugc2NlbmFyaW8uIA0KYGBge3J9DQpzYWxlc193aXRoX2JyYW5kIDwtIHByZWRpY3QobW9kZWxfd2l0aF9icmFuZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gdGVzdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnRlcnZhbCA9ICJjb25maWRlbmNlIikNCiNjb2VyY2UgaW50byBhIGRhdGEgZnJhbWUNCnNhbGVzX3dpdGhfYnJhbmQgPC0gYXMuZGF0YS5mcmFtZShzYWxlc193aXRoX2JyYW5kKQ0KYGBgDQpOZXh0LCBsZXQncyBjcmVhdGUgYSBsaW5lIHBsb3Qgb2YgdGhlIHByZWRpY3RlZCBzYWxlcyB0byBzZWUgdGhlIHByZWRpY3RlZCB0cmVuZC4gDQpgYGB7ciwgZmlnLndpZHRoPTQuNSwgZmlnLmhlaWdodD0zLjM3NX0NCiMgYWRkIHdlZWsgaWQgZm9yIHBsb3R0aW5nDQpzYWxlc193aXRoX2JyYW5kJHdlZWsgPC0gNzY6MTAwDQpnZ3Bsb3QoZGF0YSA9IHNhbGVzX3dpdGhfYnJhbmQsDQogICAgICAgYWVzKHggPSB3ZWVrLCB5ID0gZml0KSkgKyBnZW9tX2xpbmUoKQ0KYGBgDQoNCkZyb20gdGhlIHBsb3QsIHdlIGNhbiBzZWUgYSBkZWNyZWFzaW5nIHRyZW5kIG9mIHNhbGVzLiANCg0KIyBNb2RlbCBDb21wYXJpc2lvbg0KDQpJbiB0aGlzIHNlY3Rpb24sIHdlIGFyZSBydW5uaW5nIGEgbW9kZWwgd2l0aG91dCBgQnJhbmQgRXF1aXR5YC4gVGhlIGFuYWx5c2VzIGFyZSB0aGUgc2FtZSBhcyBpbiBTZWN0aW9uIDMuIFdlIHdvdWxkIG5vdCBkbyB0aGluZ3Mgc3RlcC1ieS1zdGVwLiBJZiB5b3UgYXJlIGludGVyZXN0ZWQsIHlvdSBjYW4gcHJhY3RpY2Ugd2l0aCB0aGlzIG1vZGVsIGJ5IHlvdXJzZWxmLiANCg0KVGhlIG1vZGVsIHNwZWNpZmljYXRpb24gaXMgYXMgdGhlIGZvbGxvd2luZyAod2l0aCBCcmFuZCBFcXVpdHkgbm90IGluY2x1ZGVkKToNCiQkU2FsZXMgPSBcYmV0YV8wK1xiZXRhXzFBZHZlcnRpc2luZyArIFxiZXRhXzJQcm9tb3Rpb24gKyBcYmV0YV8zUHJpY2UgK2UkJA0KTmV4dCwgd2UgZXN0aW1hdGUgb3VyIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHdpdGggdGhlIGBsbWAgZnVuY3Rpb24gYnkgdHJhbnNsYXRpbmcgdGhlIGVxdWF0aW9uIGludG8gYW4gUiBmb3JtdWxhLiBQbGVhc2Ugc2VlIENhbnZhcyBmb3IgdGhlIGZvcm11bGEgbGFuZ3VhZ2UuIA0KDQpgYGB7cn0NCm1vZGVsX3dpdGhvdXRfYnJhbmQgPC0gbG0oU2FsZXMgfiAxICsgQWR2ZXJ0aXNpbmcgKyBQcm9tb3Rpb24gKyBQcmljZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluKQ0Kc3VtbWFyeShtb2RlbF93aXRob3V0X2JyYW5kKQ0KYGBgDQpUbyBjb21wYXJlIHRoaXMgbW9kZWwgd2l0aCB0aGUgbW9kZWwgd2l0aCBCcmFuZCBFcXVpdHksIHdlIHVzZSBgYWRqdXN0ZWQgUi1zcXVhcmVkYC4gVGhpcyBpcyBiZWNhdXNlIHRoZSB0d28gbW9kZWwgaGF2ZSBkaWZmZXJlbnQgbm8uIG9mIElWcy4gU28sIHdlIG11c3QgYGFkanVzdGAgZm9yIHRoaXMgZGlmZmVyZW5jZS4gVGhlIGFkanVzdGVkIFItc3F1YXJlZCBvZiB0aGUgbW9kZWwgd2l0aCB2cy4gd2l0aG91dCBCcmFuZCBFcXVpdHkgaXMgMC45NDU1IHZzLiAwLjQ2MDMuIFRoZXJlZm9yZSwgdGhlIG1vZGVsIHdpdGggQnJhbmQgRXF1aXR5IGZpdHMgdGhlIGRhdGEgYmV0dGVyLiANCg0KTmV4dCwgbGV0J3MgZ2V0IHRoZSBwcmVkaWN0ZWQgc2FsZXMgb2YgdGhpcyBtb2RlbCwgdXNpbmcgdGhlIHNhbWUgY29kZSBhcyBhYm92ZS4gDQpgYGB7cn0NCnNhbGVzX3dpdGhvdXRfYnJhbmQgPC0gcHJlZGljdChtb2RlbF93aXRob3V0X2JyYW5kLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSB0ZXN0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGludGVydmFsID0gImNvbmZpZGVuY2UiKQ0KI2NvZXJjZSBpbnRvIGEgZGF0YSBmcmFtZQ0Kc2FsZXNfd2l0aG91dF9icmFuZCA8LSBhcy5kYXRhLmZyYW1lKHNhbGVzX3dpdGhvdXRfYnJhbmQpDQpgYGANCkFnYWluLCB3ZSBwbG90IHRoZSBwcmVkaWN0ZWQgc2FsZXM6IA0KYGBge3IsIGZpZy53aWR0aD00LjUsIGZpZy5oZWlnaHQ9My4zNzV9DQojIGFkZCB3ZWVrIGlkIGZvciBwbG90dGluZw0Kc2FsZXNfd2l0aG91dF9icmFuZCR3ZWVrIDwtIDc2OjEwMA0KZ2dwbG90KGRhdGEgPSBzYWxlc193aXRob3V0X2JyYW5kLA0KICAgICAgIGFlcyh4ID0gd2VlaywgeSA9IGZpdCkpICsgZ2VvbV9saW5lKCkNCmBgYA0KDQpGcm9tIHRoaXMgcGxvdCwgd2UgZG8gbm90IHNlZSBhIGNsZWFyIGRlY3JlYXNpbmcgdHJlbmQgaW4gc2FsZXMuIFRvIGNvbmNsdWRlLCB0byBjb3JyZWN0bHkgcHJlZGljdCB0aGUgc2FsZXMgZGVjcmVhc2UsIHRoZSBjb21wYW55IGFsc28gbmVlZHMgdG8gY29uc2lkZXIgY29uc3VtZXIgbWV0cmljcyBzdWNoIGFzIEJyYW5kIEVxdWl0eS4gDQoNCiMgUmlzayBDb250cm9sIA0KDQpBZnRlciBydW5uaW5nIHRoZSBtb2RlbHMsIHdlIHdhbnQgdG8gY2hlY2sgaWYgdGhlIHN0YXRpc3RpY2FsIGFzc3VtcHRpb25zIG9mIGxpbmVhciByZWdyZXNzaW9uIGFyZSBPa2F5LiBBcyBkaXNjdXNzZWQgaW4gY2xhc3MsIHRoZSB2aW9sYXRpb24gb2YgdGhlc2UgYXNzdW1wdGlvbnMgbGVhZHMgdG8gYmlhc2VkIGVzdGltYXRpb24gYW5kIGJhZCBwcmVkaWN0aW9uLiBPdmVyIHRoZSB5ZWFycywgcGVvcGxlIGhhdmUgZGV2ZWxvcGVkIG1hbnkgdGVzdHMgZm9yIHRoZXNlIGFzc3VtcHRpb25zLiBXZSB3aWxsIGZvY3VzIG9uIHR3byBhc3N1bXB0aW9uczogMSkgdGhlIG5vcm1hbGl0eSBhc3N1bXB0aW9uOyBhbmQgMikgdGhlIGVxdWFsIHZhcmlhbmNlIGFzc3VtcHRpb24uIA0KDQpXZSB3aWxsIHVzZSB0aGUgZnVsbCBtb2RlbCAodGhlIG1vZGVsIHdpdGggYnJhbmQgZXF1aXR5IG9yICkgYXMgYW4gZXhhbXBsZS4gRm9yIG90aGVyIG1vZGVscywgdGhlIHByb2NlZHVyZSB3b3JrcyB0aGUgc2FtZS4gDQoNCiMjIFRoZSBub3JtYWxpdHkgYXNzdW1wdGlvbiANCg0KRm9yIHRoZSBub3JtYWxpdHkgYXNzdW1wdGlvbiwgd2UgYXJlIHVzaW5nIEtTIHRlc3QuIEtTIHRlc3QgY2hlY2tzIGEgdmFyaWFibGUgYWdhaW5zdCBhIG5vcm1hbCBkaXN0cmlidXRpb24gYW5kIHRoZSB2YWx1ZSBvZiB0aGUgS1MgdGVzdCB0ZWxscyB5b3Ugd2hldGhlciB0aGUgdmFyaWFibGUgZm9sbG93cyBhIG5vcm1hbCBkaXN0cmlidXRpb24uIA0KDQpOb3RlIHRoYXQgdGhlIG51bGwgaHlwb3RoZXNpcyBvZiB0aGUgdGVzdCBpczogDQokJEhfMDogVGhlIFw6IHZhcmlhYmxlIFw6IGZvbGxvd3MgXDphIFw6IG5vcm1hbCBcOiBkaXN0cmlidXRpb24uJCQNCkZvciB0aGUgbm9ybWFsaXR5IGFzc3VtcHRpb24gdG8gaG9sZCwgdGhlIG51bGwgaHlwb3RoZXNpcyBzaG91bGQgbm90IGJlIHJlamVjdGVkLiBJbiBvdGhlciB3b3JkcywgdGhlIEtTIHRlc3Qgc2hvdWxkIE5PVCBiZSBzaWduaWZpY2FudCBpZiB0aGUgbm9ybWFsaXR5IGFzc3VtcHRpb24gaG9sZHMuIA0KDQpGaXJzdCwgd2Ugb2J0YWluIHRoZSByZXNpZHVhbHMgZnJvbSB0aGUgZXN0aW1hdGlvbiByZXN1bHRzIGFuZCB2aXN1YWxpemUgdGhlIGRpc3RyaWJ1dGlvbiB3aXRoIGEgaGlzdG9ncmFtLiANCg0KYGBge3IsIGZpZy53aWR0aD00LjUsIGZpZy5oZWlnaHQ9My4zNzV9DQojIG9idGFpbiB0aGUgcmVzaWR1YWxzIHVzaW5nIHRoZSByZXNpZHVhbHMoKSBmdW5jdGlvbg0KZXJyb3JfdGVybSA8LSByZXNpZHVhbHMobW9kZWxfd2l0aF9icmFuZCkNCg0KIyBidWlsZCBhIGhpc3RvZ3JhbQ0KIyBsZWF2ZSB0aGUgZGF0YSBlbXB0eSBhcyB3ZSBvbmx5IHBsb3QgYSB2YXJpYWJsZSBlcnJvcl90ZXJtDQpnZ3Bsb3QoICwgYWVzKHg9ZXJyb3JfdGVybSkpICsgZ2VvbV9oaXN0b2dyYW0oKQ0KDQpgYGANCg0KRnJvbSB0aGUgcGxvdCwgaXQgc2VlbXMgdGhlIGVycm9yX3Rlcm0gZm9sbG93cyBhIG5vcm1hbCBkaXN0cmlidXRpb24uIFdlIG1heSBmdXJ0aGVyIHVzZSBLUyB0ZXN0IHRvIGRvIHRoZSBmb3JtYWwgdGVzdGluZy4gTm90ZSB0aGF0IGJ5IGRlZmF1bHQsIHRoZSBLUyB0ZXN0IGNvbXBhcmVzIGEgdmFyaWFibGUgdG8gYSBzdGFuZGFyZCBub3JtYWwgZGlzdHJpYnV0aW9uLCBvciBhIG5vcm1hbCBkaXN0cmlidXRpb24gd2l0aCBtZWFuIDAgYW5kIHN0YW5kYXJkIGRldmlhdGlvbiAxLiBUaGVyZWZvcmUsIHdlIG5lZWQgdG8gZmlyc3QgcmVzY2FsZSB0aGUgcmVzaWR1YWxzIHRvIG1ha2UgaXQgY29uc2lzdGVudCB3aXRoIHRoZSBkZWZhdWx0IGRpc3RyaWJ1dGlvbi4gDQoNCmBgYHtyfQ0KIyBzY2FsZSgpIGZ1bmN0aW9uIHN0YW5kYXJkaXplcyBhIHZhcmlhYmxlIHVzZSBoZWxwKHNjYWxlKSB0byBzZWUgbW9yZS4NCnN0ZF9lcnJvcl90ZXJtIDwtIHNjYWxlKGVycm9yX3Rlcm0pDQoNCiMgdGhlIGtzIHRlc3Qgd2l0aCBrcy50ZXN0KCk7IA0KIyJwbm9ybSIgbWVhbnMgdGVzdGluZyBhZ2FpbnN0IHRoZSBzdGFuZGFyZCBub3JtYWwgZGlzdHJpYnV0aW9uDQprcy50ZXN0KHN0ZF9lcnJvcl90ZXJtLCJwbm9ybSIpDQpgYGANCkZyb20gdGhlIHRlc3QgcmVzdWx0cywgcC12YWx1ZSBpcyAwLjczID4gMC4wNS4gU28sIHdlIGNhbm5vdCByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcy4gVGhpcyBpbXBsaWVzIHRoZSBub3JtYWxpdHkgYXNzdW1wdGlvbiBpcyBPa2F5LiANCg0KIyMgVGhlIGVxdWFsIHZhcmlhbmNlIGFzc3VtcHRpb24NCkFub3RoZXIgYXNzdW1wdGlvbiBvZiBsaW5lYXIgcmVncmVzc2lvbiBpcyBhbGwgcmVzaWR1YWxzIGZvbGxvdyB0aGUgc2FtZSBub3JtYWwgZGlzdHJpYnV0aW9uIG9yIHRoZSBzYW1lIHZhcmlhbmNlLiBBbHRob3VnaCB0aGVyZSBpcyBhIGZvcm1hbCB0ZXN0IGZvciB0aGlzLCBpdCBpcyBiZXlvbmQgdGhlIHNjb3BlIG9mIHRoaXMgY291cnNlLiBJbnN0ZWFkLCB3ZSByZWx5IG9uIHBsb3R0aW5nIHRvIGV5ZWJhbGwgdGhpcyBhc3N1bXB0aW9uLg0KDQpUbyBwcm9kdWNlIHRoaXMgcGxvdCwgd2UgbmVlZCB0d28gdmFsdWVzOiB0aGUgcmVzaWR1YWxzIGFuZCB0aGUgRFYgKHNhbGVzIGluIHRoaXMgY2FzZSkuIFdlIGRvIGEgc2NhdHRlciBwbG90IHdpdGggdGhlIHJlc2lkdWFscyBhcyB0aGUgWS1heGlzIGFuZCB0aGUgRFYgYXMgdGhlIFgtYXhpcy4gSW4gYWRkaXRpb24sIHNvbWV0aW1lcyBpdCdzIG5vdCBzbyBlYXN5IHRvIHNlZSB0aGUgcGF0dGVybnMgZHVlIHRvIHRoZSBzY2FsZXMgb2YgdmFyaWFibGVzLiBTbywgd2UgcmVzY2FsZSB0aGUgdmFyaWFibGVzIGJ5IHN0YW5kYXJkaXphdGlvbiAod2l0aCB0aGUgYHNjYWxlKClgIGZ1bmN0aW9uKS4gDQoNCmBgYHtyLCBmaWcud2lkdGg9NC41LCBmaWcuaGVpZ2h0PTMuMzc1fQ0KZ2dwbG90KCAsIGFlcyh5ID0gc3RkX2Vycm9yX3Rlcm0sIHggPSBzY2FsZSh0cmFpbiRTYWxlcykpKSsgDQogIGdlb21fcG9pbnQoKSArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDApDQpgYGANCg0KVGhlIHRyaWNrIGlzIHRvIGNoZWNrIHdoZXRoZXIgYXQgZGlmZmVyZW50IHZhbHVlIG9mIHRoZSBEViAoc2FsZXMpLCB0aGUgc3BhbiBvciB0aGUgcmFuZ2Ugb2YgdGhlIHJlc2lkdWFscyBhcmUgc2ltaWxhci4gWW91IG1heSBhZGQgYXV4aWxpYXJ5IGxpbmVzIHRvIGhlbHAgeW91IGNoZWNrLg0KDQohW0VxdWFsIFZhcmlhbmNlIFNjYXR0ZXIgUGxvdF0oZXF1YWxfdmFyaWFuY2UuanBnKXt3aWR0aD02MCV9DQoNCkZyb20gdGhlIHBsb3QsIHdlIGFuIHNlZSB0aGF0IHRoZSByYW5nZSBvciBzcGFuIG9mIHJlc2lkdWFscyBhcmUgc2ltaWxhci4gU28sIHRoZSBlcXVhbCB2YXJpYW5jZSBhc3N1bXB0aW9uIGlzIGxpa2VseSB0byBob2xkLiBOb3RlIHRoYXQgYXMgd2UgcmVseSBvbiBleWViYWxsaW5nLCBpdCBjYW4gYmUgc3ViamVjdGl2ZS4gRm9yIHRoZSBsZWFybmluZyBwdXJwb3NlLCBhcyBsb25nIGFzIHlvdSBrbm93IGhvdyB0byB1c2UgdGhlIHBsb3QgdG8gZGV0ZWN0IGVxdWFsIHZhcmlhbmNlLCB5b3VyIGNvbmNsdXNpb24gaXMgbm90IGFuIGlzc3VlLiAg