In the previous lecture we looked at diagnostic tools for linear
regression models.
We saw that a model can appear inappropriate for the data because of
one or two odd points
In this lecture we will examine the issue of odd points in more
depth.
What Exactly is an Outlier?
There is no hard and fast definition.
Essentially, an outlier is defined as a data point that emanates from
a different model than do the rest of the data.
Notice that this makes the definition dependent on the model in
question.
Outlier county in Florida
Detecting Outliers
Outliers may be detected by:
- Inspection of a scatterplot of the data.
- Inspection of a fitted line plot (where fitted regression
line is superimposed on scatterplot).
- Plotting residuals versus fitted values or covariate.
Any data point that lies an unusual distance from the fitted line, or
has a large residual in comparison to the other data points, may be
considered an outlier.
Outliers for the Hill Racing Data
data(hills, package = "MASS")
Hills <- hills # We used Upper case before.
Hills.lm <- lm(time ~ dist, data = Hills)
plot(Hills.lm, which = 1)
Inspection of the plot of residuals versus fitted values suggests
three possible outliers: Knock Hill, Bens of Jura, and Lairig Ghru. Most
extreme is Bens of Jura.
The Knock Hill
outlier may be due to transcription error
— time
of 18.65 recorded as 78.65 minutes. See the help
page for this data.
What to Do When You Find Outliers
Do not ignore them!
First, investigate whether data have been mis-recorded (go back to
data source if possible).
If an outlier is not due to transcription errors, then it may need
removing before refitting the model.
In general one should refit the model after removing even a single
outlier, because removal of one outlier can alter the fitted model, and
hence make other points appear less (or more) inconsistent with the
model.
Other approaches covered in this course are to use a transformation
to reduce its impact, or use weighted regression to reduce its
impact.
The field of robust statistics (not covered in this course) is
concerned with more sophisticated ways of dealing with outliers.
Influence
Leverage
The extent of problems caused by an outlier depends on the amount of
influence that this data point has on the fitted model.
The potential influence of a data point can be measured by its
leverage. For the ith data point in a simple linear
regression, leverage is given by
\[h_{ii} = \frac{1}{n} + \frac{(x_i - \bar
x)^2}{s_{xx}}\]
Notice that:
- leverage depends only on the x-value, so that the actual
influence of a data point will not depend on the response value;
- data points with extreme x-values (i.e. far from \(\bar{x}\)) have greatest leverage.
As an aside, the sum of the leverages adds to the number of
parameters fitted in a model, so the average leverage will be
p/n.
Cook’s Distance
The actual influence of a data point can be measured by calculating
the change in parameter estimates between
the model built on all the data, and
the model built on data with the data point removed.
Cook’s distance is a measure of influence that is based on exactly
this kind of approach.
Cook’s distance depends on both the x and y-values
for a data point, and will be large for data points with both high
leverage and large standardized residual.
A value of Cook’s distance greater than one is a cause for
concern.
Using plots to detect influence
The fourth plot produced in R by plot()
applied to a
linear model is a plot of standardized residuals against leverage, with
contours marking extreme Cook’s distance.
plot(Hills.lm, which = 5)
For the hill racing data, Lairig Ghru and Bens of Jura have much
greater influence than other data points.
N.B. The names of observations appear here because the data is stored
with rownames
. How we import data is important.
You should observe that the which
argument is not set to
4 as you may have expected. There are actually 6 plots available but the
vast majority of need is served with plots 1,2,3, and 5.
Conclusions on the Hill Racing Data
The Scottish Hill Racing Data appears to contain some outliers.
Some of these outliers are data points with high leverage, and hence
may have a marked effect on the fitted model.
Recall that the definition of an outlier will depend on the model
that we choose to fit.
Perhaps the outliers only appear as odd points because the model is
inadequate.
For example, it may be the variable climb
has an
important effect on race time, and should be included in the model.
An Exercise
Remove one of the points flagged as unusual in the above analyses.
Then re-fit the model to the reduced data. Compare the fit of the two
models by plotting their fitted lines on the scatter plot of the
data.
Look at the residual analysis for your new model. Does removal of the
point you chose affect the status of the other points?
A Solution
The following code picks out the row for the maximum residual, and
then recomputes the linear model with that removed. To be explicit, the
code:
- make a row names variable so we can refer to them explicitly using
other
tidyverse
commands. N.B. The standard rownames are an
attribute so don’t work so nicely for the filter()
command
which operates on columns.
filter()
out the one we don’t want
- then revert to having rownames so that our graphs have labelled
points
- fit the same model using the reduced dataset.
library(tidyverse) # for data manipulation
library(broom) # going to sweep up a bunch of useful stuff...
augment(Hills.lm)
# A tibble: 35 × 9
.rownames time dist .fitted .resid .hat .sigma .cooksd .std.resid
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Greenmantle 16.1 2.5 16.0 0.0976 0.0529 20.3 7.06e-7 0.00502
2 Carnethy 48.4 6 45.1 3.21 0.0308 20.3 4.24e-4 0.163
3 Craig Dunain 33.6 6 45.1 -11.5 0.0308 20.2 5.44e-3 -0.585
4 Ben Rha 45.6 7.5 57.6 -12.0 0.0286 20.1 5.51e-3 -0.612
5 Ben Lomond 62.3 8 61.8 0.464 0.0288 20.3 8.25e-6 0.0236
6 Goatfell 73.2 8 61.8 11.4 0.0288 20.2 4.99e-3 0.580
7 Bens of Jura 205. 16 128. 76.2 0.0977 14.5 8.75e-1 4.02
8 Cairnpapple 36.4 6 45.1 -8.78 0.0308 20.2 3.17e-3 -0.447
9 Scolty 29.8 5 36.8 -7.06 0.0347 20.2 2.33e-3 -0.360
10 Traprain 39.8 6 45.1 -5.39 0.0308 20.2 1.20e-3 -0.274
# ℹ 25 more rows
Hills2 = Hills |>
rownames_to_column() |>
# so we can use them explicitly like a variable
filter(rowname != "Bens of Jura") |>
# filter out the one we don't want
column_to_rownames()
# go back to having rownames so that our graphs have labelled points
Hills2.lm <- lm(time ~ dist, data = Hills2)
augment(Hills2.lm)
# A tibble: 34 × 9
.rownames time dist .fitted .resid .hat .sigma .cooksd .std.resid
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Greenmantle 16.1 2.5 17.0 -0.957 0.0531 14.7 0.000129 -0.0679
2 Carnethy 48.4 6 43.8 4.57 0.0311 14.7 0.00165 0.320
3 Craig Dunain 33.6 6 43.8 -10.1 0.0311 14.6 0.00811 -0.711
4 Ben Rha 45.6 7.5 55.2 -9.65 0.0295 14.6 0.00694 -0.676
5 Ben Lomond 62.3 8 59.1 3.20 0.0300 14.7 0.000778 0.224
6 Goatfell 73.2 8 59.1 14.2 0.0300 14.5 0.0152 0.992
7 Cairnpapple 36.4 6 43.8 -7.42 0.0311 14.7 0.00435 -0.520
8 Scolty 29.8 5 36.1 -6.39 0.0348 14.7 0.00364 -0.449
9 Traprain 39.8 6 43.8 -4.03 0.0311 14.7 0.00129 -0.283
10 Lairig Ghru 193. 28 212. -19.2 0.475 13.9 1.52 -1.83
# ℹ 24 more rows
N.B. The augment()
command added columns starting with a
period containing several of the values we want for checking
assumptions; other added columns will only be explained if they are
needed in future. Watch that the first column though is called
.rownames
but we use the filter()
command on a
data set, not the results of augment()
plot(time ~ dist, data = Hills)
abline(Hills.lm, lty = 2)
abline(Hills2.lm, col = 2)
N.B. this form of plot()
and abline()
is
old-fashioned, but still good value
par(mfrow = c(2, 2))
plot(Hills2.lm)
Deleted residuals
Suppose we think an outlier is dragging the line towards itself, to
some extent hiding how unusual it is.
Suppose we were to delete the \(i^{th}\) data point and recalculate the
line with it gone. \[ \hat y_{ -i} =
\hat\beta_{0,-i} + \hat\beta_{1,-i} \,\, x \] for \(j=1,2,...,n\), \(j\ne i\). The \(-i\) notation means with the \(i\) point removed.
Since we can calculate this “deleted line” at any x, we can
do it at the value of \(x_i\).
A deleted residual is \[ e_{i, -i} = y_i
- \hat y_{i,-i}\]
We calculate these deleted residuals for each row \(i\) in turn.
We standardise each deleted residual using the variance calculated
with the \(i^{th}\) point removed. What
we get are known as studentized deleted residuals.
The R function rstudent()
calculates the deleted
residuals for us.
It is often helpful to plot them on the same graph as standardized
residuals. If all the points overlap then no data values are
influential. But the graph below immediately shows us that deleting the
two outliers in the left-middle of the plot would have a big effect, so
they are influential. However deleting the point on the right would not
change the model much, so it should be left alone.
plot(rstudent(Hills.lm) ~ predict(Hills.lm), col = "red", pch = "+", main = "Studentized Deleted Residuals")
points(rstandard(Hills.lm) ~ predict(Hills.lm), col = "black", pch = 1)
[1] 5.53731
[1] 4.018411
You might find the following commands useful.
max(cooks.distance(Hills.lm))
[1] 2.15456
max(cooks.distance(Hills2.lm))
[1] 1.517719
max(hatvalues(Hills.lm)) # leverages
[1] 0.4325145
max(hatvalues(Hills2.lm))
[1] 0.474975
LS0tDQp0aXRsZTogIkxlY3R1cmUgNjogT3V0bGllcnMgYW5kIEluZmx1ZW50aWFsIFBvaW50cyINCnN1YnRpdGxlOiAxNjEuMjUxIFJlZ3Jlc3Npb24gTW9kZWxsaW5nDQphdXRob3I6ICJQcmVzZW50ZWQgYnkgTWF0dGhldyBQYXdsZXkgPE0uUGF3bGV5QG1hc3NleS5hYy5uej4iICANCmRhdGU6ICJXZWVrIDIgb2YgU2VtZXN0ZXIgMiwgYHIgbHVicmlkYXRlOjp5ZWFyKGx1YnJpZGF0ZTo6bm93KCkpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHlldGkNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogIGh0bWxfbm90ZWJvb2s6DQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiB5ZXRpDQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICBpb3NsaWRlc19wcmVzZW50YXRpb246DQogICAgd2lkZXNjcmVlbjogdHJ1ZQ0KICAgIHNtYWxsZXI6IHRydWUNCiAgd29yZF9kb2N1bWVudDogZGVmYXVsdA0KICBzbGlkeV9wcmVzZW50YXRpb246IA0KICAgIHRoZW1lOiB5ZXRpDQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQoNCg0KDQo8IS0tLSBEYXRhIGlzIG9uDQpodHRwczovL3ItcmVzb3VyY2VzLm1hc3NleS5hYy5uei9kYXRhLzE2MTI1MS8NCi0tLT4NCg0KYGBge3Igc2V0dXAsIHB1cmw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpsaWJyYXJ5KGtuaXRyKQ0Kb3B0c19jaHVuayRzZXQoZGV2PWMoInBuZyIsICJwZGYiKSkNCm9wdHNfY2h1bmskc2V0KGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTcsIGZpZy5wYXRoPSJGaWd1cmVzLyIsIGZpZy5hbHQ9InVubGFiZWxsZWQiKQ0Kb3B0c19jaHVuayRzZXQoY29tbWVudD0iIiwgZmlnLmFsaWduPSJjZW50ZXIiLCB0aWR5PVRSVUUpDQpvcHRpb25zKGtuaXRyLmthYmxlLk5BID0gJycpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoYnJvb20pDQpgYGANCg0KDQo8IS0tLSBEbyBub3QgZWRpdCBhbnl0aGluZyBhYm92ZSB0aGlzIGxpbmUuIC0tLT4NCg0KSW4gdGhlIHByZXZpb3VzIGxlY3R1cmUgd2UgbG9va2VkIGF0IGRpYWdub3N0aWMgdG9vbHMgZm9yIGxpbmVhciByZWdyZXNzaW9uIG1vZGVscy4NCg0KV2Ugc2F3IHRoYXQgYSBtb2RlbCBjYW4gYXBwZWFyIGluYXBwcm9wcmlhdGUgZm9yIHRoZSBkYXRhIGJlY2F1c2Ugb2Ygb25lIG9yIHR3byBvZGQgcG9pbnRzDQoNCkluIHRoaXMgbGVjdHVyZSB3ZSB3aWxsIGV4YW1pbmUgdGhlIGlzc3VlIG9mIG9kZCBwb2ludHMgaW4gbW9yZSBkZXB0aC4NCg0KIyMgV2hhdCBFeGFjdGx5IGlzIGFuIE91dGxpZXI/DQoNClRoZXJlIGlzIG5vIGhhcmQgYW5kIGZhc3QgZGVmaW5pdGlvbi4NCg0KRXNzZW50aWFsbHksIGFuIG91dGxpZXIgaXMgZGVmaW5lZCBhcyBhIGRhdGEgcG9pbnQgdGhhdCBlbWFuYXRlcyBmcm9tIGEgZGlmZmVyZW50IG1vZGVsIHRoYW4gZG8gdGhlIHJlc3Qgb2YgdGhlIGRhdGEuDQoNCk5vdGljZSB0aGF0IHRoaXMgbWFrZXMgdGhlIGRlZmluaXRpb24gZGVwZW5kZW50IG9uIHRoZSBtb2RlbCBpbiBxdWVzdGlvbi4NCg0KIVtPdXRsaWVyIGNvdW50eSBpbiBGbG9yaWRhXSguLi9ncmFwaGljcy9vdXRsaWVyLmpwZykNCg0KDQoNCg0KDQojIyBEZXRlY3RpbmcgT3V0bGllcnMNCg0KICBPdXRsaWVycyBtYXkgYmUgZGV0ZWN0ZWQgYnk6DQoNCi0gSW5zcGVjdGlvbiBvZiBhIHNjYXR0ZXJwbG90IG9mIHRoZSBkYXRhLg0KLSBJbnNwZWN0aW9uIG9mIGEgKmZpdHRlZCBsaW5lIHBsb3QqICh3aGVyZSBmaXR0ZWQgcmVncmVzc2lvbiBsaW5lIGlzIHN1cGVyaW1wb3NlZCBvbiBzY2F0dGVycGxvdCkuDQotIFBsb3R0aW5nIHJlc2lkdWFscyB2ZXJzdXMgZml0dGVkIHZhbHVlcyBvciBjb3ZhcmlhdGUuDQoNCkFueSBkYXRhIHBvaW50IHRoYXQgbGllcyBhbiB1bnVzdWFsIGRpc3RhbmNlIGZyb20gdGhlIGZpdHRlZCBsaW5lLCBvciBoYXMgYSBsYXJnZSByZXNpZHVhbCBpbiBjb21wYXJpc29uIHRvIHRoZSBvdGhlciBkYXRhIHBvaW50cywgbWF5IGJlIGNvbnNpZGVyZWQgYW4gb3V0bGllci4NCg0KIyMgT3V0bGllcnMgZm9yIHRoZSBIaWxsIFJhY2luZyBEYXRhDQoNCmBgYHtyIGdldEhpbGxzRGF0YX0NCmRhdGEoaGlsbHMsIHBhY2thZ2U9Ik1BU1MiKQ0KSGlsbHMgPC0gaGlsbHMgIyBXZSB1c2VkIFVwcGVyIGNhc2UgYmVmb3JlLg0KYGBgDQoNCmBgYHtyIEhpbGxzLmxtfQ0KSGlsbHMubG0gPC0gbG0odGltZX5kaXN0LCBkYXRhPUhpbGxzKSANCmBgYA0KDQpgYGB7ciBIaWxscy5sbURpYWcxLCBmaWcuY2FwPSJSZXNpZHVhbHMgdiBmaXR0ZWQgcGxvdCBmb3IgSGlsbHMgZGF0YSJ9DQpwbG90KEhpbGxzLmxtLCB3aGljaD0xKQ0KYGBgDQoNCg0KSW5zcGVjdGlvbiBvZiB0aGUgcGxvdCBvZiByZXNpZHVhbHMgdmVyc3VzIGZpdHRlZCB2YWx1ZXMgc3VnZ2VzdHMgdGhyZWUgcG9zc2libGUgb3V0bGllcnM6IEtub2NrIEhpbGwsIEJlbnMgb2YgSnVyYSwgYW5kIExhaXJpZyBHaHJ1Lg0KTW9zdCBleHRyZW1lIGlzIEJlbnMgb2YgSnVyYS4NCg0KVGhlIGBLbm9jayBIaWxsYCBvdXRsaWVyIG1heSBiZSBkdWUgdG8gdHJhbnNjcmlwdGlvbiBlcnJvciAtLS0gIGB0aW1lYCAgb2YgMTguNjUgcmVjb3JkZWQgYXMgNzguNjUgbWludXRlcy4gU2VlIHRoZSBoZWxwIHBhZ2UgZm9yIHRoaXMgZGF0YS4NCg0KDQojIyBXaGF0IHRvIERvIFdoZW4gWW91IEZpbmQgT3V0bGllcnMNCg0KRG8gbm90IGlnbm9yZSB0aGVtIQ0KDQpGaXJzdCwgaW52ZXN0aWdhdGUgd2hldGhlciBkYXRhIGhhdmUgYmVlbiBtaXMtcmVjb3JkZWQgKGdvIGJhY2sgdG8gZGF0YSBzb3VyY2UgaWYgcG9zc2libGUpLg0KDQpJZiBhbiBvdXRsaWVyIGlzIG5vdCBkdWUgdG8gdHJhbnNjcmlwdGlvbiBlcnJvcnMsIHRoZW4gaXQgbWF5IG5lZWQgcmVtb3ZpbmcgYmVmb3JlIHJlZml0dGluZyB0aGUgbW9kZWwuDQoNCkluIGdlbmVyYWwgb25lIHNob3VsZCByZWZpdCB0aGUgbW9kZWwgYWZ0ZXIgcmVtb3ZpbmcgZXZlbiBhIHNpbmdsZSBvdXRsaWVyLCBiZWNhdXNlIHJlbW92YWwgb2Ygb25lIG91dGxpZXIgY2FuIGFsdGVyIHRoZSBmaXR0ZWQgbW9kZWwsIGFuZCBoZW5jZSBtYWtlIG90aGVyIHBvaW50cyBhcHBlYXIgbGVzcyAob3IgbW9yZSkgaW5jb25zaXN0ZW50IHdpdGggdGhlIG1vZGVsLg0KDQpPdGhlciBhcHByb2FjaGVzIGNvdmVyZWQgaW4gdGhpcyBjb3Vyc2UgYXJlIHRvIHVzZSBhIHRyYW5zZm9ybWF0aW9uIHRvIHJlZHVjZSBpdHMgaW1wYWN0LCBvciB1c2Ugd2VpZ2h0ZWQgcmVncmVzc2lvbiB0byByZWR1Y2UgaXRzIGltcGFjdC4NCg0KVGhlIGZpZWxkIG9mIHJvYnVzdCBzdGF0aXN0aWNzIChub3QgY292ZXJlZCBpbiB0aGlzIGNvdXJzZSkgaXMgY29uY2VybmVkIHdpdGggbW9yZSBzb3BoaXN0aWNhdGVkIHdheXMgb2YgZGVhbGluZyB3aXRoIG91dGxpZXJzLg0KDQojIyBJbmZsdWVuY2UNCg0KIyMjIExldmVyYWdlDQoNClRoZSBleHRlbnQgb2YgcHJvYmxlbXMgY2F1c2VkIGJ5IGFuIG91dGxpZXIgZGVwZW5kcyBvbiB0aGUgYW1vdW50IG9mIGluZmx1ZW5jZSB0aGF0IHRoaXMgZGF0YSBwb2ludCBoYXMgb24gdGhlIGZpdHRlZCBtb2RlbC4NCg0KVGhlIHBvdGVudGlhbCBpbmZsdWVuY2Ugb2YgYSBkYXRhIHBvaW50IGNhbiBiZSBtZWFzdXJlZCBieSBpdHMgbGV2ZXJhZ2UuIEZvciB0aGUgKmkqXnRoXiBkYXRhIHBvaW50IGluIGEgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uLCBsZXZlcmFnZSBpcyBnaXZlbiBieQ0KDQokJGhfe2lpfSA9IFxmcmFjezF9e259ICsgXGZyYWN7KHhfaSAtIFxiYXIgeCleMn17c197eHh9fSQkDQoNCk5vdGljZSB0aGF0Og0KDQotIGxldmVyYWdlIGRlcGVuZHMgb25seSBvbiB0aGUgKngqLXZhbHVlLCBzbyB0aGF0IHRoZSBhY3R1YWwgaW5mbHVlbmNlIG9mIGEgZGF0YSBwb2ludCB3aWxsIG5vdCBkZXBlbmQgb24gdGhlIHJlc3BvbnNlIHZhbHVlOw0KLSBkYXRhIHBvaW50cyB3aXRoIGV4dHJlbWUgKngqLXZhbHVlcyAoaS5lLiBmYXIgZnJvbSAkXGJhcnt4fSQpIGhhdmUgZ3JlYXRlc3QgbGV2ZXJhZ2UuDQoNCg0KQXMgYW4gYXNpZGUsIHRoZSBzdW0gb2YgdGhlIGxldmVyYWdlcyBhZGRzIHRvIHRoZSBudW1iZXIgb2YgcGFyYW1ldGVycyBmaXR0ZWQgaW4gYSBtb2RlbCwgc28gdGhlIGF2ZXJhZ2UgbGV2ZXJhZ2Ugd2lsbCBiZSAqcC9uKi4NCg0KDQojIyMgQ29vaydzIERpc3RhbmNlDQoNClRoZSBhY3R1YWwgaW5mbHVlbmNlIG9mIGEgZGF0YSBwb2ludCBjYW4gYmUgbWVhc3VyZWQgYnkgY2FsY3VsYXRpbmcgdGhlIGNoYW5nZSBpbiBwYXJhbWV0ZXIgZXN0aW1hdGVzIGJldHdlZW4NCg0KKGkpIHRoZSBtb2RlbCBidWlsdCBvbiBhbGwgdGhlIGRhdGEsIGFuZA0KDQooaWkpIHRoZSBtb2RlbCBidWlsdCBvbiBkYXRhIHdpdGggdGhlIGRhdGEgcG9pbnQgcmVtb3ZlZC4NCg0KDQogQ29vaydzIGRpc3RhbmNlIGlzIGEgbWVhc3VyZSBvZiBpbmZsdWVuY2UgdGhhdCBpcyBiYXNlZCBvbiBleGFjdGx5IHRoaXMga2luZCBvZiBhcHByb2FjaC4NCg0KQ29vaydzIGRpc3RhbmNlIGRlcGVuZHMgb24gYm90aCB0aGUgKngqIGFuZCAqeSotdmFsdWVzIGZvciBhIGRhdGEgcG9pbnQsIGFuZCB3aWxsIGJlIGxhcmdlIGZvciBkYXRhIHBvaW50cyB3aXRoIGJvdGggaGlnaCBsZXZlcmFnZSBhbmQgbGFyZ2Ugc3RhbmRhcmRpemVkIHJlc2lkdWFsLg0KDQpBIHZhbHVlIG9mIENvb2sncyBkaXN0YW5jZSBncmVhdGVyIHRoYW4gb25lIGlzIGEgY2F1c2UgZm9yIGNvbmNlcm4uDQoNCiMjIFVzaW5nIHBsb3RzIHRvIGRldGVjdCBpbmZsdWVuY2UNCg0KDQpUaGUgZm91cnRoIHBsb3QgcHJvZHVjZWQgaW4gUiBieSAgYHBsb3QoKWAgIGFwcGxpZWQgdG8gYSBsaW5lYXIgbW9kZWwgaXMgYSBwbG90IG9mIHN0YW5kYXJkaXplZCByZXNpZHVhbHMgYWdhaW5zdCBsZXZlcmFnZSwgd2l0aCBjb250b3VycyBtYXJraW5nIGV4dHJlbWUgQ29vaydzIGRpc3RhbmNlLg0KDQpgYGB7ciBIaWxscy5sbURpYWc0LCBmaWcuY2FwPSJGb3VydGggZGlhZ25vc3RpYyBwbG90IGZvciBIaWxscy5sbSJ9DQpwbG90KEhpbGxzLmxtLCB3aGljaD01KQ0KYGBgDQoNCg0KDQpGb3IgdGhlIGhpbGwgcmFjaW5nIGRhdGEsIExhaXJpZyBHaHJ1IGFuZCBCZW5zIG9mIEp1cmEgaGF2ZSBtdWNoIGdyZWF0ZXIgaW5mbHVlbmNlIHRoYW4gb3RoZXIgZGF0YSBwb2ludHMuDQoNCg0KTi5CLiBUaGUgbmFtZXMgb2Ygb2JzZXJ2YXRpb25zIGFwcGVhciBoZXJlIGJlY2F1c2UgdGhlIGRhdGEgaXMgc3RvcmVkIHdpdGggYHJvd25hbWVzYC4gSG93IHdlIGltcG9ydCBkYXRhIGlzIGltcG9ydGFudC4NCg0KWW91IHNob3VsZCBvYnNlcnZlIHRoYXQgdGhlIGB3aGljaGAgYXJndW1lbnQgaXMgbm90IHNldCB0byA0IGFzIHlvdSBtYXkgaGF2ZSBleHBlY3RlZC4gVGhlcmUgYXJlIGFjdHVhbGx5IDYgcGxvdHMgYXZhaWxhYmxlIGJ1dCB0aGUgdmFzdCBtYWpvcml0eSBvZiBuZWVkIGlzIHNlcnZlZCB3aXRoIHBsb3RzIDEsMiwzLCBhbmQgNS4NCg0KDQojIyBDb25jbHVzaW9ucyBvbiB0aGUgSGlsbCBSYWNpbmcgRGF0YQ0KDQpUaGUgU2NvdHRpc2ggSGlsbCBSYWNpbmcgRGF0YSBhcHBlYXJzIHRvIGNvbnRhaW4gc29tZSBvdXRsaWVycy4NCg0KU29tZSBvZiB0aGVzZSBvdXRsaWVycyBhcmUgZGF0YSBwb2ludHMgd2l0aCBoaWdoIGxldmVyYWdlLCBhbmQgaGVuY2UgbWF5IGhhdmUgYSBtYXJrZWQgZWZmZWN0IG9uIHRoZSBmaXR0ZWQgbW9kZWwuDQoNClJlY2FsbCB0aGF0IHRoZSBkZWZpbml0aW9uIG9mIGFuIG91dGxpZXIgd2lsbCBkZXBlbmQgb24gdGhlIG1vZGVsIHRoYXQgd2UgY2hvb3NlIHRvIGZpdC4NCg0KUGVyaGFwcyB0aGUgb3V0bGllcnMgb25seSBhcHBlYXIgYXMgb2RkIHBvaW50cyBiZWNhdXNlIHRoZSBtb2RlbCBpcyBpbmFkZXF1YXRlLg0KDQpGb3IgZXhhbXBsZSwgaXQgbWF5IGJlIHRoZSB2YXJpYWJsZSBgY2xpbWJgIGhhcyBhbiBpbXBvcnRhbnQgZWZmZWN0IG9uIHJhY2UgdGltZSwgYW5kIHNob3VsZCBiZSBpbmNsdWRlZCBpbiB0aGUgbW9kZWwuDQoNCg0KIyMgQW4gRXhlcmNpc2UNCg0KUmVtb3ZlIG9uZSBvZiB0aGUgcG9pbnRzIGZsYWdnZWQgYXMgdW51c3VhbCBpbiB0aGUgYWJvdmUgYW5hbHlzZXMuIFRoZW4gcmUtZml0IHRoZSBtb2RlbCB0byB0aGUgcmVkdWNlZCBkYXRhLiBDb21wYXJlIHRoZSBmaXQgb2YgdGhlIHR3byBtb2RlbHMgYnkgcGxvdHRpbmcgdGhlaXIgZml0dGVkIGxpbmVzIG9uIHRoZSBzY2F0dGVyIHBsb3Qgb2YgdGhlIGRhdGEuDQoNCkxvb2sgYXQgdGhlIHJlc2lkdWFsIGFuYWx5c2lzIGZvciB5b3VyIG5ldyBtb2RlbC4gRG9lcyByZW1vdmFsIG9mIHRoZSBwb2ludCB5b3UgY2hvc2UgYWZmZWN0IHRoZSBzdGF0dXMgb2YgdGhlIG90aGVyIHBvaW50cz8gDQoNCiMjIyBBIFNvbHV0aW9uDQoNClRoZSBmb2xsb3dpbmcgY29kZSBwaWNrcyBvdXQgdGhlIHJvdyBmb3IgdGhlIG1heGltdW0gcmVzaWR1YWwsIGFuZCB0aGVuIHJlY29tcHV0ZXMgdGhlIGxpbmVhciBtb2RlbCB3aXRoIHRoYXQgcmVtb3ZlZC4gVG8gYmUgZXhwbGljaXQsIHRoZSBjb2RlOg0KDQotIG1ha2UgYSByb3cgbmFtZXMgdmFyaWFibGUgc28gd2UgY2FuIHJlZmVyIHRvIHRoZW0gZXhwbGljaXRseSB1c2luZyBvdGhlciBgdGlkeXZlcnNlYCBjb21tYW5kcy4gTi5CLiBUaGUgc3RhbmRhcmQgcm93bmFtZXMgYXJlIGFuIGF0dHJpYnV0ZSBzbyBkb24ndCB3b3JrIHNvIG5pY2VseSBmb3IgdGhlIGBmaWx0ZXIoKWAgY29tbWFuZCB3aGljaCBvcGVyYXRlcyBvbiBjb2x1bW5zLg0KLSBgZmlsdGVyKClgICBvdXQgdGhlIG9uZSB3ZSBkb24ndCB3YW50DQotIHRoZW4gcmV2ZXJ0IHRvIGhhdmluZyByb3duYW1lcyBzbyB0aGF0IG91ciBncmFwaHMgaGF2ZSBsYWJlbGxlZCBwb2ludHMNCi0gZml0IHRoZSBzYW1lIG1vZGVsIHVzaW5nIHRoZSByZWR1Y2VkIGRhdGFzZXQuDQoNCmBgYHtyIEhpbGxzMi5sbX0NCmxpYnJhcnkodGlkeXZlcnNlKSAjIGZvciBkYXRhIG1hbmlwdWxhdGlvbg0KbGlicmFyeShicm9vbSkgIyBnb2luZyB0byBzd2VlcCB1cCBhIGJ1bmNoIG9mIHVzZWZ1bCBzdHVmZi4uLg0KYXVnbWVudChIaWxscy5sbSkNCkhpbGxzMiA9IEhpbGxzIHw+IHJvd25hbWVzX3RvX2NvbHVtbigpIHw+IA0KIyBzbyB3ZSBjYW4gdXNlIHRoZW0gZXhwbGljaXRseSBsaWtlIGEgdmFyaWFibGUNCmZpbHRlcihyb3duYW1lICE9ICJCZW5zIG9mIEp1cmEiKSB8PiANCiMgZmlsdGVyIG91dCB0aGUgb25lIHdlIGRvbid0IHdhbnQNCmNvbHVtbl90b19yb3duYW1lcygpIA0KIyBnbyBiYWNrIHRvIGhhdmluZyByb3duYW1lcyBzbyB0aGF0IG91ciBncmFwaHMgaGF2ZSBsYWJlbGxlZCBwb2ludHMNCg0KSGlsbHMyLmxtIDwtIGxtKHRpbWV+ZGlzdCwgZGF0YT1IaWxsczIpIA0KYXVnbWVudChIaWxsczIubG0pDQpgYGANCg0KTi5CLiBUaGUgYGF1Z21lbnQoKWAgY29tbWFuZCBhZGRlZCBjb2x1bW5zIHN0YXJ0aW5nIHdpdGggYSBwZXJpb2QgY29udGFpbmluZyBzZXZlcmFsIG9mIHRoZSB2YWx1ZXMgd2Ugd2FudCBmb3IgY2hlY2tpbmcgYXNzdW1wdGlvbnM7IG90aGVyIGFkZGVkIGNvbHVtbnMgd2lsbCBvbmx5IGJlIGV4cGxhaW5lZCBpZiB0aGV5IGFyZSBuZWVkZWQgaW4gZnV0dXJlLiBXYXRjaCB0aGF0IHRoZSBmaXJzdCBjb2x1bW4gdGhvdWdoIGlzIGNhbGxlZCBgLnJvd25hbWVzYCBidXQgd2UgdXNlIHRoZSBgZmlsdGVyKClgIGNvbW1hbmQgb24gYSBkYXRhIHNldCwgbm90IHRoZSByZXN1bHRzIG9mIGBhdWdtZW50KClgDQoNCiANCmBgYHtyIEhpbGxzRml0dGVkTGluZVBsb3QyLCBmaWcuY2FwPSJNb2RlbHMgZml0dGVkIHRvIEhpbGxzIGRhdGEgd2l0aCBhbmQgd2l0aG91dCB0aGUgQmVucyBvZiBKdXJhIHJhY2UuIn0NCnBsb3QodGltZX5kaXN0LCBkYXRhPUhpbGxzKSANCmFibGluZShIaWxscy5sbSwgbHR5PTIpDQphYmxpbmUoSGlsbHMyLmxtLCBjb2w9MikNCmBgYA0KDQpOLkIuIHRoaXMgZm9ybSBvZiBgcGxvdCgpYCBhbmQgYGFibGluZSgpYCBpcyBvbGQtZmFzaGlvbmVkLCBidXQgc3RpbGwgZ29vZCB2YWx1ZQ0KDQpgYGB7ciByZXNpZHNIaWxsczIubG0sIGZpZy5jYXA9IkRpYWdub3N0aWMgcGxvdHMgZm9yIEhpbGxzMi5sbSJ9DQpwYXIobWZyb3c9YygyLDIpKQ0KcGxvdChIaWxsczIubG0pDQpgYGANCg0KDQojIyBEZWxldGVkIHJlc2lkdWFscw0KDQpTdXBwb3NlIHdlIHRoaW5rIGFuIG91dGxpZXIgaXMgZHJhZ2dpbmcgdGhlIGxpbmUgdG93YXJkcyBpdHNlbGYsICB0byBzb21lIGV4dGVudCBoaWRpbmcgaG93IHVudXN1YWwgaXQgaXMuIA0KDQpTdXBwb3NlIHdlIHdlcmUgdG8gZGVsZXRlIHRoZSAkaV57dGh9JCBkYXRhIHBvaW50IGFuZCByZWNhbGN1bGF0ZSB0aGUgbGluZSB3aXRoIGl0IGdvbmUuICAkJCBcaGF0IHlfeyAgLWl9ID0gXGhhdFxiZXRhX3swLC1pfSArIFxoYXRcYmV0YV97MSwtaX0gXCxcLCAgIHggICAkJCBmb3IgJGo9MSwyLC4uLixuJCwgICRqXG5lIGkkLiAgVGhlICQtaSQgbm90YXRpb24gbWVhbnMgd2l0aCB0aGUgJGkkIHBvaW50IHJlbW92ZWQuDQoNClNpbmNlIHdlIGNhbiBjYWxjdWxhdGUgdGhpcyAiZGVsZXRlZCBsaW5lIiBhdCBhbnkgKngqLCAgd2UgY2FuIGRvIGl0IGF0IHRoZSB2YWx1ZSBvZiAkeF9pJC4gDQoNCkEgZGVsZXRlZCByZXNpZHVhbCBpcyAkJCBlX3tpLCAtaX0gID0geV9pIC0gXGhhdCB5X3tpLC1pfSQkDQoNCldlIGNhbGN1bGF0ZSB0aGVzZSBkZWxldGVkIHJlc2lkdWFscyBmb3IgZWFjaCByb3cgJGkkIGluIHR1cm4uDQoNCg0KV2Ugc3RhbmRhcmRpc2UgZWFjaCBkZWxldGVkIHJlc2lkdWFsIHVzaW5nIHRoZSB2YXJpYW5jZSBjYWxjdWxhdGVkIHdpdGggdGhlICRpXnt0aH0kIHBvaW50IHJlbW92ZWQuICBXaGF0IHdlIGdldCBhcmUgIGtub3duICBhcyAqc3R1ZGVudGl6ZWQgZGVsZXRlZCByZXNpZHVhbHMqLiANCg0KVGhlIFIgZnVuY3Rpb24gIGByc3R1ZGVudCgpYCAgIGNhbGN1bGF0ZXMgdGhlIGRlbGV0ZWQgcmVzaWR1YWxzIGZvciB1cy4gICANCg0KSXQgaXMgb2Z0ZW4gaGVscGZ1bCB0byBwbG90IHRoZW0gb24gdGhlIHNhbWUgZ3JhcGggYXMgc3RhbmRhcmRpemVkIHJlc2lkdWFscy4gSWYgYWxsIHRoZSBwb2ludHMgb3ZlcmxhcCB0aGVuIG5vIGRhdGEgdmFsdWVzIGFyZSBpbmZsdWVudGlhbC4gIEJ1dCB0aGUgZ3JhcGggYmVsb3cgaW1tZWRpYXRlbHkgc2hvd3MgdXMgdGhhdCBkZWxldGluZyB0aGUgdHdvIG91dGxpZXJzICBpbiB0aGUgbGVmdC1taWRkbGUgb2YgdGhlIHBsb3Qgd291bGQgaGF2ZSBhIGJpZyBlZmZlY3QsIHNvIHRoZXkgYXJlIGluZmx1ZW50aWFsLiBIb3dldmVyIGRlbGV0aW5nIHRoZSBwb2ludCBvbiB0aGUgcmlnaHQgd291bGQgbm90IGNoYW5nZSB0aGUgbW9kZWwgbXVjaCwgc28gaXQgc2hvdWxkIGJlIGxlZnQgYWxvbmUuICANCg0KYGBge3IgZGVsZXRlZCByZXNpZHVhbHN9DQpwbG90KCAgcnN0dWRlbnQoSGlsbHMubG0pIH5wcmVkaWN0KEhpbGxzLmxtKSwgY29sPSJyZWQiLHBjaD0iKyIgLCBtYWluPSJTdHVkZW50aXplZCBEZWxldGVkIFJlc2lkdWFscyIpDQpwb2ludHMoICByc3RhbmRhcmQoSGlsbHMubG0pIH5wcmVkaWN0KEhpbGxzLmxtKSwgY29sPSJibGFjayIscGNoPTEgKQ0KbWF4KHJzdHVkZW50KEhpbGxzLmxtKSkgIA0KbWF4KHJzdGFuZGFyZChIaWxscy5sbSkpDQpgYGANCg0KDQoNCg0KIyMgWW91IG1pZ2h0IGZpbmQgdGhlIGZvbGxvd2luZyBjb21tYW5kcyB1c2VmdWwuDQoNCmBgYHtyIG90aGVyVXNlZnVsfQ0KbWF4KGNvb2tzLmRpc3RhbmNlKEhpbGxzLmxtKSkNCm1heChjb29rcy5kaXN0YW5jZShIaWxsczIubG0pKQ0KbWF4KGhhdHZhbHVlcyhIaWxscy5sbSkpICMgbGV2ZXJhZ2VzDQptYXgoaGF0dmFsdWVzKEhpbGxzMi5sbSkpIA0KYGBgDQo=