Statistical models allow us to answer questions using data.

The validity of the conclusions that we reach is contingent upon the appropriateness of the models that we use.

Once you have fitted a statistical model to data, it is therefore important to check the assumptions underlying the model.

The tools to check these assumptions are often referred to as model diagnostics.

We shall look at assumption checking for simple linear regression in this lecture.

Assumptions for Simple Linear Regression

Recall that the linear regression model is \[Y_i = \beta_0 + \beta_1 x_i + \varepsilon_i\]

where we make the following assumptions:

A1. \(E[\varepsilon_i] = 0\) for i=1, … ,n.

A2. \(\varepsilon_1, \ldots ,\varepsilon_n\) are independent.

A3. var\((\varepsilon_i) = \sigma^2\) for i=1,…,n (homoscedasticity).

A4. \(\varepsilon_1, \ldots ,\varepsilon_n\) are normally distributed.

Class Discussion: Interpret these assumptions, and think about situations in which each might fail.

Raw Residuals

The residuals (or raw residuals), \[\begin{aligned} e_i &=& y_i - \hat \mu_i\\ &=& y_i - \hat \beta_0 - \hat \beta_1 x_i~~~~~~~~~(i=1,\ldots ,n)\end{aligned}\] are a fundamental diagnostic aid.

These residuals are a kind of sample version of the error random variables \(\varepsilon_1,\ldots,\varepsilon_n\).

Consequently we can test the assumptions by seeing if they hold for \(e_1, \ldots ,e_n\) (instead of \(\varepsilon_1, \ldots ,\varepsilon_n\)).

Standardized Residuals

There is a technical problem with raw residuals as substitutes for the error terms: it can be shown that the variance of the ith raw residual is given by \[var(e_i) = \sigma^2 (1 - h_{ii})~~~~~~(i=1,\ldots,n)\] where \[h_{ii} = \frac{1}{n} + \frac{(x_i - \bar x)^2}{s_{xx}}.\]

Hence unlike error terms, raw residuals do not have equal variance.

It is therefore preferable to work with the standardized residuals, defined by \[r_i = \frac{e_i}{s \sqrt{1-h_{ii}}}.\]

We will not need to evaluate this formula; R can produce standardized residuals for us.

These standardized residuals have an approximately standard normal, N(0,1), distribution and thus common variance.

In reality this technical issue is not a biggie. In practice there is little visual difference between looking at a graph of ordinary residuals and a graph of standardized residuals. For example the following plots show ordinary and standardized residuals for the Hill data introduced below. It is very hard to pick any difference in the shape of the points.

unlabelled

Plots of raw (upper) and standardised (lower) residuals against the predictions from the simple model.

unlabelled

Plots of raw (upper) and standardised (lower) residuals against the predictions from the simple model.

So why do we bother with standardised residuals?

A practical reason is interpretability.

  • If the units in which y is measured are very interpretable (such as $, or kg) then the same units apply to the raw residuals, and so the graph will be more interpretable in the original units.
  • But suppose the units in which y is measured is rather arbitrary, such as the total from a certain number of questions on a questionnaire used to investigate some opinion, with each question being recorded on some arbitrary scale (e.g. 1=strongly disagree, 2=disagree, 3=neutral, 4=agree, 5 =strongly agree). In that case the precise values of y are not informative. E.g. if you were told your political score was 32, what does that mean? It would only have meaning if you knew what the average, and maximum and minimum attainable score were. So it is better to convert y to a z-score so that the numbers become more interpretable.

\[ z = \frac{y - \bar{y}}{s_y}\]

E.g. we can interpret z=0 as average, -2 as very low, z=+1 as above average, etc.

  • Similarly the raw residuals will have the same uninterpretable units as y, but we can interpret standardised residuals as z-scores. \(r_i=-2\) meaning the \(y_i\) value is far below the regression line, \(r_i=0\) as meaning the \(y_i\) value is on the regression line, and \(r_i= +2.5\) meaning the \(y_i\) value is unusually far above the regression line, etc.

Scottish Hill Racing Data

Variables are dist distance (miles) climb height gained (ft) time record time (minutes)

unlabelled

a hill runner

We will first consider simple linear regression of time on dist in R.

Reading the Data into R

data(hills, package = "MASS")
Hills <- hills
## Hills <- read.csv(file = "https://r-resources.massey.ac.nz/161251/data/hills.csv",
##     header = TRUE, row.names = 1)

The row.names=1 argument tells R that the first column of data in the comma separated values file hills.csv contains information which should provide the row names for the data (in this case, names of race locations).

The data is also available in the MASS package that comes with R. (Use one of these two data sources, not both!)

Fitting the Regression Model in R

Hills.lm <- lm(time ~ dist, data = Hills)
summary(Hills.lm)

Call:
lm(formula = time ~ dist, data = Hills)

Residuals:
    Min      1Q  Median      3Q     Max 
-35.745  -9.037  -4.201   2.849  76.170 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -4.8407     5.7562  -0.841    0.406    
dist          8.3305     0.6196  13.446 6.08e-15 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 19.96 on 33 degrees of freedom
Multiple R-squared:  0.8456,    Adjusted R-squared:  0.841 
F-statistic: 180.8 on 1 and 33 DF,  p-value: 6.084e-15
library(ggplot2)
ggplot(Hills, mapping = aes(x = time, y = dist)) + geom_point() + geom_smooth(method = "lm",
    se = FALSE) + ylab("Winning time of race") + xlab("Race distance")
`geom_smooth()` using formula = 'y ~ x'
unlabelled

Fitted line plot for the first model for the hill racing data

The fitted model is (to 2 decimal places)

\[E[\mbox{time}] = -4.84 + 8.33 \, \mbox{dist}\]

The P-value of \(6.083904\times 10^{-15}\) for testing whether the regression slope is zero. This is tiny, and indicates overwhelming evidence for (linear) dependence of race time on distance.

The R-squared statistic of 0.8456 indicates that distance explains quite a large proportion of the variation in race record times.

The command resid() returns (raw) residuals for a fitted linear regression model.

resid(Hills.lm)
     Greenmantle         Carnethy     Craig Dunain          Ben Rha 
      0.09757971       3.20798304     -11.49201696     -12.03770125 
      Ben Lomond         Goatfell     Bens of Jura      Cairnpapple 
      0.46407065      11.41407065      76.17042112      -8.77501696 
          Scolty         Traprain      Lairig Ghru           Dollar 
     -7.06156077      -5.39201696     -35.74505318       6.23843923 
         Lomonds      Cairn Table       Eildon Two        Cairngorm 
     -9.29861363      -1.00901696      -5.71333268      -6.21384173 
     Seven Hills       Knock Hill       Black Hill       Creag Beag 
    -13.36866650      58.49935161     -15.22933268      -8.40978887 
    Kildcon Hill Meall Ant-Suidhe   Half Ben Nevis         Cow Hill 
     -4.20064839       3.58412351       2.49098304       6.11280780 
   N Berwick Law       Creag Dubh        Burnswark        Largo Law 
     -1.46764839      -2.26410458     -10.70901696      -8.24456077 
         Criffel           Acmony        Ben Nevis      Knockfarrel 
      1.19275494     -15.86156077       7.11915827     -12.75901696 
   Two Breweries        Cockleroi     Moffat Chase 
     25.14250874      -4.54633268      -1.93540365 

These numbers are interpretable because the numbers are in units of minutes above or below what would be expected.

Let’s see the commands used to get those graphs of raw and standardised residuals shown earlier.

data(hills, package = "MASS")
Hills <- hills
Hills.lm = lm(time ~ dist, data = Hills)
plot(resid(Hills.lm) ~ predict.lm(Hills.lm), xlab = "Predictions", ylab = "Raw residuals")
unlabelled

Plots of raw (upper) and standardised (lower) residuals against the predictions from the simple model.

plot(rstandard(Hills.lm) ~ predict.lm(Hills.lm), xlab = "Predictions", ylab = "Standardised residuals")
unlabelled

Plots of raw (upper) and standardised (lower) residuals against the predictions from the simple model.

and some numerical details:

summary(resid(Hills.lm))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-35.745  -9.037  -4.201   0.000   2.849  76.170 
summary(rstandard(Hills.lm))
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
-2.377778 -0.460171 -0.215778 -0.009795  0.145043  4.018411 

Diagnostics for Failure of Model Assumptions

Assessing Assumption A1

Assumption A1 is equivalent to stating \(E[Y_i] = \beta_0 + \beta_1 x_i\)

Failure of A1 indicates that we are fitting the wrong mean function.

Looking for any trends in residual versus fitted values or a covariate is a useful diagnostic.

Assessing Assumption A2

If A2 is correct, then residuals should appear randomly scattered about zero if plotted against time order (if known), fitted values or the predictor.

Long sequences of positive residuals followed by sequences of negative residuals suggests that the error terms are not independent.

Failure of this assumption will mean standard errors will be incorrect, and so hypothesis tests and confidence intervals will be unreliable.

We will look at regression models for time series later in the course.

Assessing Assumption A3

If A3 is correct, then the spread of the (standardized) residuals about zero should not vary systematically with the fitted value or covariate.

Heteroscedasticity (unequal spread) is typically recognized as a “fanning out” of (standardized) residuals when plotted against fitted value or covariate. This means the errorvariance increases with the mean response. (Alternatively one could have fanning in).

Failure of this assumption will leave parameter estimates unbiased, but standard errors will be incorrect, and hence test results and confidence intervals will be unreliable.

A transformation of both response and predictor variables can sometimes help in this case.

An alternative strategy is to use weighted linear regression (covered later in the course).

Assessing Assumption A4

A normal probability plot, or normal Q-Q plot, is a standard graphical tool for assessing the normality of a data set.

A normal Q-Q plot of the (standardized) residuals should show points staying close to a straight line — curvature indicates a failure of A4.

On the other hand failure of the normality assumption for the error distribution is not usually a serious problem. Removal of (obvious) outliers will often improve the normality of the standardized residuals.

Diagnostics for Hill race s Example

The plot() command applied to the fitted model Hills.lm produces four useful diagnostic plots. The par() command put first splits the graphing window into a two-by-two grid so we can see all four graphs together.

par(mfrow = c(2, 2))
plot(Hills.lm)
unlabelled

Residual analysis plots for the first model applied to the hill racing data.

The first three of these plots are:

plot(Hills.lm, which = 1)
unlabelled

Residuals plotted against fitted values for the first model fitted to the hill racing data

This residual plot shows some data points with very large residuals.

The red line is a nonparametric estimate of trend (take care not to over-interpret) it.

Would you say there is strong evidence of curvature, or of increasing spread in residuals?

plot(Hills.lm, which = 2)
unlabelled

Normality plot of residuals for the first model fitted to the hill racing data

The Normal Q-Q plot is far from a straight line. This indicates failure of the normality assumption.

Note that this plot is heavily influenced by data points with large residuals.

plot(Hills.lm, which = 3)
unlabelled

Scale versus location plot for the first model fitted to the hill racing data

For the scale-location plot, we look for a trend for the line to be increasing (or decreasing) as we move from left to right. If so, it provides a hint that spread of residuals is increasing (or decreasing) with fitted value.

This graph suggests the error variance \(\sigma^2\) may not be constant for all data points.

What about assumption A2, independent error? We can just plot the residuals without an x variable.

plot(Hills.lm$residuals)
abline(h = 0, lty = 2)

unlabelled

The outliers make it hard to tell, but the graph suggests the residuals may not be entirely random - perhaps there is some relevant order in the data.

Conclusions from Diagnostics for Example

Linear regression model assumptions do not appear valid.

Hence conclusions based on this model are questionable.

Problems with model seem largely down to a few odd data points or outliers.

LS0tDQp0aXRsZTogIkxlY3R1cmUgNTogQ2hlY2tpbmcgdGhlIEFzc3VtcHRpb25zIg0Kc3VidGl0bGU6IDE2MS4yNTEgUmVncmVzc2lvbiBNb2RlbGxpbmcNCmF1dGhvcjogIlByZXNlbnRlZCBieSBNYXR0aGV3IFBhd2xleSA8TS5QYXdsZXlAbWFzc2V5LmFjLm56PiIgIA0KZGF0ZTogIldlZWsgMiBvZiBTZW1lc3RlciAyLCBgciBsdWJyaWRhdGU6OnllYXIobHVicmlkYXRlOjpub3coKSlgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0aGVtZTogeWV0aQ0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgaHRtbF9ub3RlYm9vazoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHlldGkNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogIGlvc2xpZGVzX3ByZXNlbnRhdGlvbjoNCiAgICB3aWRlc2NyZWVuOiB0cnVlDQogICAgc21hbGxlcjogdHJ1ZQ0KICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0DQogIHNsaWR5X3ByZXNlbnRhdGlvbjogDQogICAgdGhlbWU6IHlldGkNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KLS0tDQoNCg0KDQoNCjwhLS0tIERhdGEgaXMgb24NCmh0dHBzOi8vci1yZXNvdXJjZXMubWFzc2V5LmFjLm56L2RhdGEvMTYxMjUxLw0KLS0tPg0KDQpgYGB7ciBzZXR1cCwgcHVybD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkoa25pdHIpDQpvcHRzX2NodW5rJHNldChkZXY9YygicG5nIiwgInBkZiIpKQ0Kb3B0c19jaHVuayRzZXQoZmlnLmhlaWdodD02LCBmaWcud2lkdGg9NywgZmlnLnBhdGg9IkZpZ3VyZXMvIiwgZmlnLmFsdD0idW5sYWJlbGxlZCIpDQpvcHRzX2NodW5rJHNldChjb21tZW50PSIiLCBmaWcuYWxpZ249ImNlbnRlciIsIHRpZHk9VFJVRSkNCm9wdGlvbnMoa25pdHIua2FibGUuTkEgPSAnJykNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShicm9vbSkNCmBgYA0KDQoNCjwhLS0tIERvIG5vdCBlZGl0IGFueXRoaW5nIGFib3ZlIHRoaXMgbGluZS4gLS0tPg0KDQpTdGF0aXN0aWNhbCBtb2RlbHMgYWxsb3cgdXMgdG8gYW5zd2VyIHF1ZXN0aW9ucyB1c2luZyBkYXRhLg0KDQpUaGUgdmFsaWRpdHkgb2YgdGhlIGNvbmNsdXNpb25zIHRoYXQgd2UgcmVhY2ggaXMgY29udGluZ2VudCB1cG9uIHRoZSBhcHByb3ByaWF0ZW5lc3Mgb2YgdGhlIG1vZGVscyB0aGF0IHdlIHVzZS4NCg0KT25jZSB5b3UgaGF2ZSBmaXR0ZWQgYSBzdGF0aXN0aWNhbCBtb2RlbCB0byBkYXRhLCBpdCBpcyB0aGVyZWZvcmUgaW1wb3J0YW50IHRvIGNoZWNrIHRoZSBhc3N1bXB0aW9ucyB1bmRlcmx5aW5nIHRoZSBtb2RlbC4NCg0KVGhlIHRvb2xzIHRvIGNoZWNrIHRoZXNlIGFzc3VtcHRpb25zIGFyZSBvZnRlbiByZWZlcnJlZCB0byBhcyAqKm1vZGVsIGRpYWdub3N0aWNzKiouDQoNCldlIHNoYWxsIGxvb2sgYXQgYXNzdW1wdGlvbiBjaGVja2luZyBmb3Igc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIGluIHRoaXMgbGVjdHVyZS4NCg0KIyMgQXNzdW1wdGlvbnMgZm9yIFNpbXBsZSBMaW5lYXIgUmVncmVzc2lvbg0KDQpSZWNhbGwgdGhhdCB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgaXMNCiQkWV9pID0gXGJldGFfMCArIFxiZXRhXzEgeF9pICsgXHZhcmVwc2lsb25faSQkIA0KDQp3aGVyZSB3ZSBtYWtlIHRoZQ0KZm9sbG93aW5nIGFzc3VtcHRpb25zOg0KDQoqKipBMSoqKi4gICRFW1x2YXJlcHNpbG9uX2ldID0gMCQgZm9yICppPTEsIC4uLiAsbiouDQoNCioqKkEyKioqLiAgJFx2YXJlcHNpbG9uXzEsIFxsZG90cyAsXHZhcmVwc2lsb25fbiQgYXJlIGluZGVwZW5kZW50Lg0KDQoqKipBMyoqKi4gIHZhciQoXHZhcmVwc2lsb25faSkgPSBcc2lnbWFeMiQgZm9yICppPTEsLi4uLG4qICgqKmhvbW9zY2VkYXN0aWNpdHkqKikuDQoNCioqKkE0KioqLiAgJFx2YXJlcHNpbG9uXzEsIFxsZG90cyAsXHZhcmVwc2lsb25fbiQgYXJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLg0KDQoNCkNsYXNzIERpc2N1c3Npb246IEludGVycHJldCB0aGVzZSBhc3N1bXB0aW9ucywgYW5kIHRoaW5rIGFib3V0IHNpdHVhdGlvbnMgaW4gd2hpY2ggZWFjaCBtaWdodCBmYWlsLg0KDQojIyBSYXcgUmVzaWR1YWxzDQoNClRoZSByZXNpZHVhbHMgKG9yICoqcmF3IHJlc2lkdWFscyoqKSwgJCRcYmVnaW57YWxpZ25lZH0gZV9pICY9JiB5X2kgLSBcaGF0IFxtdV9pXFwgJj0mIHlfaSAtIFxoYXQgXGJldGFfMCAtIFxoYXQgXGJldGFfMSB4X2l+fn5+fn5+fn4oaT0xLFxsZG90cyAsbilcZW5ke2FsaWduZWR9JCQNCmFyZSBhIGZ1bmRhbWVudGFsIGRpYWdub3N0aWMgYWlkLg0KDQpUaGVzZSByZXNpZHVhbHMgYXJlIGEga2luZCBvZiBzYW1wbGUgdmVyc2lvbiBvZiB0aGUgZXJyb3IgcmFuZG9tIHZhcmlhYmxlcyAkXHZhcmVwc2lsb25fMSxcbGRvdHMsXHZhcmVwc2lsb25fbiQuDQoNCkNvbnNlcXVlbnRseSB3ZSBjYW4gdGVzdCB0aGUgYXNzdW1wdGlvbnMgYnkgc2VlaW5nIGlmIHRoZXkgaG9sZCBmb3IgJGVfMSwgXGxkb3RzICxlX24kIChpbnN0ZWFkIG9mICRcdmFyZXBzaWxvbl8xLCBcbGRvdHMgLFx2YXJlcHNpbG9uX24kKS4NCg0KIyMgU3RhbmRhcmRpemVkIFJlc2lkdWFscw0KDQpUaGVyZSBpcyBhICB0ZWNobmljYWwgcHJvYmxlbSB3aXRoIHJhdyByZXNpZHVhbHMgYXMgc3Vic3RpdHV0ZXMgZm9yIHRoZSBlcnJvciB0ZXJtczogaXQgY2FuIGJlIHNob3duIHRoYXQgdGhlIHZhcmlhbmNlIG9mIHRoZSAqaSpedGheIHJhdyByZXNpZHVhbCBpcyBnaXZlbiBieSANCiQkdmFyKGVfaSkgPSBcc2lnbWFeMiAoMSAtIGhfe2lpfSl+fn5+fn4oaT0xLFxsZG90cyxuKSQkIHdoZXJlDQokJGhfe2lpfSA9IFxmcmFjezF9e259ICsgXGZyYWN7KHhfaSAtIFxiYXIgeCleMn17c197eHh9fS4kJA0KDQpIZW5jZSB1bmxpa2UgZXJyb3IgdGVybXMsIHJhdyByZXNpZHVhbHMgZG8gbm90IGhhdmUgZXF1YWwgdmFyaWFuY2UuDQoNCkl0IGlzIHRoZXJlZm9yZSBwcmVmZXJhYmxlIHRvIHdvcmsgd2l0aCB0aGUgc3RhbmRhcmRpemVkIHJlc2lkdWFscywgZGVmaW5lZCBieSANCiQkcl9pID0gXGZyYWN7ZV9pfXtzIFxzcXJ0ezEtaF97aWl9fX0uJCQNCg0KV2Ugd2lsbCBub3QgbmVlZCB0byBldmFsdWF0ZSB0aGlzIGZvcm11bGE7IFIgY2FuIHByb2R1Y2Ugc3RhbmRhcmRpemVkIHJlc2lkdWFscyBmb3IgdXMuDQoNClRoZXNlIHN0YW5kYXJkaXplZCByZXNpZHVhbHMgaGF2ZSBhbiBhcHByb3hpbWF0ZWx5IHN0YW5kYXJkIG5vcm1hbCwgKk4oMCwxKSosIGRpc3RyaWJ1dGlvbiBhbmQgdGh1cyBjb21tb24gdmFyaWFuY2UuDQoNCg0KSW4gcmVhbGl0eSB0aGlzIHRlY2huaWNhbCBpc3N1ZSBpcyBub3QgYSBiaWdnaWUuICBJbiBwcmFjdGljZSB0aGVyZSBpcyBsaXR0bGUgdmlzdWFsIGRpZmZlcmVuY2UgYmV0d2VlbiBsb29raW5nIGF0IGEgZ3JhcGggb2Ygb3JkaW5hcnkgcmVzaWR1YWxzIGFuZCBhIGdyYXBoIG9mIHN0YW5kYXJkaXplZCByZXNpZHVhbHMuIEZvciBleGFtcGxlIHRoZSBmb2xsb3dpbmcgcGxvdHMgc2hvdyBvcmRpbmFyeSBhbmQgc3RhbmRhcmRpemVkIHJlc2lkdWFscyBmb3IgdGhlIEhpbGwgZGF0YSBpbnRyb2R1Y2VkIGJlbG93LiBJdCBpcyB2ZXJ5IGhhcmQgdG8gcGljayBhbnkgZGlmZmVyZW5jZSBpbiB0aGUgKioqc2hhcGUqKiogb2YgdGhlIHBvaW50cy4NCg0KYGBge3IgQ29tcGFyaW5nIHJlc2lkdWFsIHBsb3RzLCBlY2hvPUZBTFNFLCBmaWcuY2FwPSJQbG90cyBvZiByYXcgKHVwcGVyKSBhbmQgc3RhbmRhcmRpc2VkIChsb3dlcikgcmVzaWR1YWxzIGFnYWluc3QgdGhlIHByZWRpY3Rpb25zIGZyb20gdGhlIHNpbXBsZSBtb2RlbC4ifQ0KZGF0YShoaWxscywgcGFja2FnZSA9ICJNQVNTIikNCkhpbGxzIDwtIGhpbGxzDQpIaWxscy5sbSA9IGxtKHRpbWV+IGRpc3QsIGRhdGE9SGlsbHMpDQpwbG90KHJlc2lkKEhpbGxzLmxtKX4gcHJlZGljdC5sbShIaWxscy5sbSksIHhsYWI9IlByZWRpY3Rpb25zIiwgeWxhYj0iUmF3IHJlc2lkdWFscyIpDQpwbG90KHJzdGFuZGFyZChIaWxscy5sbSl+IHByZWRpY3QubG0oSGlsbHMubG0pLCB4bGFiPSJQcmVkaWN0aW9ucyIsIHlsYWI9IlN0YW5kYXJkaXNlZCByZXNpZHVhbHMiKQ0KYGBgDQoNCiMjIyBTbyB3aHkgZG8gd2UgYm90aGVyIHdpdGggc3RhbmRhcmRpc2VkIHJlc2lkdWFscz8gIA0KDQpBICBwcmFjdGljYWwgcmVhc29uIGlzIGludGVycHJldGFiaWxpdHkuDQoNCi0gSWYgdGhlIHVuaXRzIGluIHdoaWNoICp5KiBpcyBtZWFzdXJlZCBhcmUgdmVyeSBpbnRlcnByZXRhYmxlIChzdWNoIGFzIFwkLCBvciBrZykgIHRoZW4gdGhlIHNhbWUgdW5pdHMgYXBwbHkgdG8gdGhlIHJhdyByZXNpZHVhbHMsIGFuZCBzbyB0aGUgZ3JhcGggd2lsbCBiZSBtb3JlIGludGVycHJldGFibGUgaW4gdGhlIG9yaWdpbmFsIHVuaXRzLiANCi0gQnV0IHN1cHBvc2UgdGhlIHVuaXRzIGluIHdoaWNoICp5KiBpcyBtZWFzdXJlZCBpcyByYXRoZXIgYXJiaXRyYXJ5LCBzdWNoIGFzIHRoZSB0b3RhbCBmcm9tIGEgY2VydGFpbiBudW1iZXIgb2YgcXVlc3Rpb25zIG9uIGEgcXVlc3Rpb25uYWlyZSB1c2VkIHRvIGludmVzdGlnYXRlIHNvbWUgb3Bpbmlvbiwgd2l0aCBlYWNoIHF1ZXN0aW9uIGJlaW5nIHJlY29yZGVkIG9uIHNvbWUgYXJiaXRyYXJ5IHNjYWxlIChlLmcuIDE9c3Ryb25nbHkgZGlzYWdyZWUsIDI9ZGlzYWdyZWUsIDM9bmV1dHJhbCwgND1hZ3JlZSwgNSA9c3Ryb25nbHkgYWdyZWUpLiAgSW4gdGhhdCBjYXNlIHRoZSBwcmVjaXNlIHZhbHVlcyBvZiAqeSogYXJlIG5vdCBpbmZvcm1hdGl2ZS4gIEUuZy4gaWYgeW91IHdlcmUgdG9sZCB5b3VyIHBvbGl0aWNhbCBzY29yZSB3YXMgMzIsIHdoYXQgZG9lcyB0aGF0IG1lYW4/IEl0IHdvdWxkIG9ubHkgaGF2ZSBtZWFuaW5nIGlmIHlvdSBrbmV3IHdoYXQgdGhlIGF2ZXJhZ2UsIGFuZCBtYXhpbXVtIGFuZCBtaW5pbXVtIGF0dGFpbmFibGUgc2NvcmUgd2VyZS4gICBTbyBpdCBpcyBiZXR0ZXIgdG8gIGNvbnZlcnQgKnkqIHRvIGEgei1zY29yZSBzbyB0aGF0IHRoZSBudW1iZXJzIGJlY29tZSBtb3JlIGludGVycHJldGFibGUuDQoNCiQkIHogPSBcZnJhY3t5IC0gXGJhcnt5fX17c195fSQkDQoNCkUuZy4gd2UgY2FuIGludGVycHJldCB6PTAgYXMgYXZlcmFnZSwgIC0yIGFzIHZlcnkgbG93LCB6PSsxIGFzIGFib3ZlIGF2ZXJhZ2UsIGV0Yy4gDQoNCi0gU2ltaWxhcmx5IHRoZSByYXcgcmVzaWR1YWxzIHdpbGwgaGF2ZSB0aGUgc2FtZSB1bmludGVycHJldGFibGUgdW5pdHMgYXMgKnkqLCBidXQgd2UgY2FuIGludGVycHJldCBzdGFuZGFyZGlzZWQgcmVzaWR1YWxzIGFzIHotc2NvcmVzLiAkcl9pPS0yJCBtZWFuaW5nIHRoZSAkeV9pJCB2YWx1ZSBpcyBmYXIgYmVsb3cgdGhlIHJlZ3Jlc3Npb24gbGluZSwgICRyX2k9MCQgYXMgbWVhbmluZyB0aGUgJHlfaSQgdmFsdWUgaXMgb24gdGhlIHJlZ3Jlc3Npb24gbGluZSwgYW5kICRyX2k9ICsyLjUkIG1lYW5pbmcgdGhlICR5X2kkIHZhbHVlIGlzIHVudXN1YWxseSBmYXIgYWJvdmUgdGhlIHJlZ3Jlc3Npb24gbGluZSwgICBldGMuIA0KDQoNCg0KIyMgU2NvdHRpc2ggSGlsbCBSYWNpbmcgRGF0YQ0KDQoNCi0gRGF0YSBjb21wcmlzZSB0aGUgcmVjb3JkIHRpbWVzIGluIDE5ODQgZm9yIDM1IFNjb3R0aXNoIGhpbGwgcmFjZXMuDQoNClZhcmlhYmxlcyBhcmUgYGRpc3RgIGRpc3RhbmNlIChtaWxlcykgYGNsaW1iYCBoZWlnaHQgZ2FpbmVkIChmdCkgYHRpbWVgIHJlY29yZCB0aW1lIChtaW51dGVzKSAgDQoNCi0gRGF0YSBzb3VyY2U6IEEuQy4gQXRraW5zb24gKDE5ODYpIENvbW1lbnQ6IEFzcGVjdHMgb2YgZGlhZ25vc3RpYyByZWdyZXNzaW9uIGFuYWx5c2lzLiAqU3RhdGlzdGljYWwgU2NpZW5jZSogKioxKiosIHBwMzk3LTQwMi4NCg0KYGBge3IgUnVubmVySlBHLCBmaWcuY2FwPSJhIGhpbGwgcnVubmVyIiwgZWNobz1GQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIuLi9ncmFwaGljcy9oaWxscmFjZS5qcGciKQ0KYGBgDQoNCiAgDQoNCldlIHdpbGwgZmlyc3QgY29uc2lkZXIgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG9mIGB0aW1lYCBvbiBgZGlzdGAgaW4gUi4NCg0KDQpSZWFkaW5nIHRoZSBEYXRhIGludG8gUg0KDQpgYGB7ciBnZXRIaWxsc0RhdGEsIGV2YWw9LTN9DQpkYXRhKGhpbGxzLCBwYWNrYWdlPSJNQVNTIikgDQpIaWxscyA8LSBoaWxscw0KSGlsbHMgPC0gcmVhZC5jc3YoZmlsZT0iaHR0cHM6Ly9yLXJlc291cmNlcy5tYXNzZXkuYWMubnovMTYxMjUxL2RhdGEvaGlsbHMuY3N2IiwgaGVhZGVyPVRSVUUsIHJvdy5uYW1lcz0xKQ0KYGBgDQoNClRoZSBgcm93Lm5hbWVzPTFgIGFyZ3VtZW50IHRlbGxzIFIgdGhhdCB0aGUgZmlyc3QgY29sdW1uIG9mIGRhdGEgaW4gdGhlIGNvbW1hIHNlcGFyYXRlZCB2YWx1ZXMgZmlsZSBgciB4ZnVuOjplbWJlZF9maWxlKCIuLi8uLi9kYXRhL2hpbGxzLmNzdiIsIHRleHQ9ImhpbGxzLmNzdiIpYCBjb250YWlucyBpbmZvcm1hdGlvbiB3aGljaCBzaG91bGQgcHJvdmlkZSB0aGUgcm93IG5hbWVzIGZvciB0aGUgZGF0YSAoaW4gdGhpcyBjYXNlLCBuYW1lcyBvZiByYWNlIGxvY2F0aW9ucykuDQoNClRoZSBkYXRhIGlzIGFsc28gYXZhaWxhYmxlIGluIHRoZSBgTUFTU2AgcGFja2FnZSB0aGF0IGNvbWVzIHdpdGggUi4gKFVzZSBvbmUgb2YgdGhlc2UgdHdvIGRhdGEgc291cmNlcywgbm90IGJvdGghKQ0KDQoNCiMjIyBGaXR0aW5nIHRoZSBSZWdyZXNzaW9uIE1vZGVsIGluIFINCg0KYGBge3IgSGlsbHMubG19DQpIaWxscy5sbSA8LSBsbSh0aW1lfmRpc3QsIGRhdGE9SGlsbHMpIA0Kc3VtbWFyeShIaWxscy5sbSkNCmBgYA0KDQoNCmBgYHtyIEhpbGxzRml0dGVkTGluZVBsb3QsIGZpZy5jYXA9IkZpdHRlZCBsaW5lIHBsb3QgZm9yIHRoZSBmaXJzdCBtb2RlbCBmb3IgdGhlIGhpbGwgcmFjaW5nIGRhdGEifQ0KbGlicmFyeShnZ3Bsb3QyKQ0KZ2dwbG90KEhpbGxzLCBtYXBwaW5nID0gYWVzKHg9dGltZSwgeT1kaXN0KSkgKyBnZW9tX3BvaW50KCkgKw0KZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkgKw0KeWxhYigiV2lubmluZyB0aW1lIG9mIHJhY2UiKSArIHhsYWIoIlJhY2UgZGlzdGFuY2UiKSAgDQpgYGANCg0KDQoNClRoZSBmaXR0ZWQgbW9kZWwgaXMgKHRvIDIgZGVjaW1hbCBwbGFjZXMpDQoNCiQkRVtcbWJveHt0aW1lfV0gPSBgciByb3VuZChjb2VmKEhpbGxzLmxtKVsxXSwgMilgICsgYHIgcm91bmQoY29lZihIaWxscy5sbSlbMl0sIDIpYCBcLCBcbWJveHtkaXN0fSQkDQoNClRoZSAqUCotdmFsdWUgb2YgJGByIHN1bW1hcnkoSGlsbHMubG0pJGNvZWZmaWNpZW50c1siZGlzdCIsICJQcig+fHR8KSJdYCQgZm9yIHRlc3Rpbmcgd2hldGhlciB0aGUgcmVncmVzc2lvbiBzbG9wZSBpcyB6ZXJvLiBUaGlzIGlzIHRpbnksIGFuZCBpbmRpY2F0ZXMgb3ZlcndoZWxtaW5nIGV2aWRlbmNlIGZvciAobGluZWFyKSBkZXBlbmRlbmNlIG9mIHJhY2UgdGltZSBvbiBkaXN0YW5jZS4NCg0KVGhlIFItc3F1YXJlZCBzdGF0aXN0aWMgb2YgYHIgcm91bmQoc3VtbWFyeShIaWxscy5sbSkkci5zcXVhcmVkLDQpYCAgaW5kaWNhdGVzIHRoYXQgZGlzdGFuY2UgZXhwbGFpbnMgcXVpdGUgYSBsYXJnZSBwcm9wb3J0aW9uIG9mIHRoZSB2YXJpYXRpb24gaW4gcmFjZSByZWNvcmQgdGltZXMuDQoNClRoZSBjb21tYW5kIGByZXNpZCgpYCByZXR1cm5zIChyYXcpIHJlc2lkdWFscyBmb3IgYSBmaXR0ZWQgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwuDQoNCmBgYHtyIGdldFJlc2lkc30NCnJlc2lkKEhpbGxzLmxtKQ0KYGBgDQoNClRoZXNlIG51bWJlcnMgYXJlIGludGVycHJldGFibGUgYmVjYXVzZSB0aGUgbnVtYmVycyBhcmUgaW4gdW5pdHMgb2YgbWludXRlcyBhYm92ZSBvciBiZWxvdyB3aGF0IHdvdWxkIGJlIGV4cGVjdGVkLiANCg0KTGV0J3Mgc2VlIHRoZSBjb21tYW5kcyB1c2VkIHRvIGdldCB0aG9zZSBncmFwaHMgb2YgcmF3IGFuZCBzdGFuZGFyZGlzZWQgcmVzaWR1YWxzIHNob3duIGVhcmxpZXIuDQoNCmBgYHtyIENvbXBhcmluZ1Jlc2lkdWFsUGxvdHMyLCBmaWcuY2FwPSJQbG90cyBvZiByYXcgKHVwcGVyKSBhbmQgc3RhbmRhcmRpc2VkIChsb3dlcikgcmVzaWR1YWxzIGFnYWluc3QgdGhlIHByZWRpY3Rpb25zIGZyb20gdGhlIHNpbXBsZSBtb2RlbC4ifQ0KZGF0YShoaWxscywgcGFja2FnZSA9ICJNQVNTIikNCkhpbGxzIDwtIGhpbGxzDQpIaWxscy5sbSA9IGxtKHRpbWV+IGRpc3QsIGRhdGE9SGlsbHMpDQpwbG90KHJlc2lkKEhpbGxzLmxtKX4gcHJlZGljdC5sbShIaWxscy5sbSksIHhsYWI9IlByZWRpY3Rpb25zIiwgeWxhYj0iUmF3IHJlc2lkdWFscyIpDQpwbG90KHJzdGFuZGFyZChIaWxscy5sbSl+IHByZWRpY3QubG0oSGlsbHMubG0pLCB4bGFiPSJQcmVkaWN0aW9ucyIsIHlsYWI9IlN0YW5kYXJkaXNlZCByZXNpZHVhbHMiKQ0KYGBgDQoNCmFuZCBzb21lIG51bWVyaWNhbCBkZXRhaWxzOg0KDQpgYGB7ciBSZXNpZERldGFpbHN9DQpzdW1tYXJ5KHJlc2lkKEhpbGxzLmxtKSkNCnN1bW1hcnkoIHJzdGFuZGFyZChIaWxscy5sbSkpDQpgYGANCiANCg0KIyMgRGlhZ25vc3RpY3MgZm9yIEZhaWx1cmUgb2YgTW9kZWwgQXNzdW1wdGlvbnMNCg0KIyMjIEFzc2Vzc2luZyBBc3N1bXB0aW9uIEExDQoNCkFzc3VtcHRpb24gKioqQTEqKiogaXMgZXF1aXZhbGVudCB0byBzdGF0aW5nICRFW1lfaV0gPSBcYmV0YV8wICsgXGJldGFfMSB4X2kkDQoNCkZhaWx1cmUgb2YgKioqQTEqKiogaW5kaWNhdGVzIHRoYXQgd2UgYXJlIGZpdHRpbmcgdGhlIHdyb25nIG1lYW4gZnVuY3Rpb24uDQoNCkxvb2tpbmcgZm9yIGFueSB0cmVuZHMgaW4gcmVzaWR1YWwgdmVyc3VzIGZpdHRlZCB2YWx1ZXMgb3IgYSBjb3ZhcmlhdGUgaXMgYSB1c2VmdWwgZGlhZ25vc3RpYy4NCg0KDQojIyMgQXNzZXNzaW5nIEFzc3VtcHRpb24gQTINCg0KSWYgKioqQTIqKiogaXMgY29ycmVjdCwgdGhlbiByZXNpZHVhbHMgc2hvdWxkIGFwcGVhciByYW5kb21seSBzY2F0dGVyZWQgYWJvdXQgemVybyBpZiBwbG90dGVkIGFnYWluc3QgdGltZSBvcmRlciAoaWYga25vd24pLCBmaXR0ZWQgdmFsdWVzIG9yIHRoZSBwcmVkaWN0b3IuDQoNCkxvbmcgc2VxdWVuY2VzIG9mIHBvc2l0aXZlIHJlc2lkdWFscyBmb2xsb3dlZCBieSBzZXF1ZW5jZXMgb2YgbmVnYXRpdmUgcmVzaWR1YWxzIHN1Z2dlc3RzIHRoYXQgdGhlIGVycm9yIHRlcm1zIGFyZSBub3QgaW5kZXBlbmRlbnQuDQoNCkZhaWx1cmUgb2YgdGhpcyBhc3N1bXB0aW9uIHdpbGwgbWVhbiBzdGFuZGFyZCBlcnJvcnMgd2lsbCBiZSBpbmNvcnJlY3QsICBhbmQgc28gaHlwb3RoZXNpcyB0ZXN0cyAgYW5kIGNvbmZpZGVuY2UgIGludGVydmFscyB3aWxsIGJlIHVucmVsaWFibGUuDQoNCldlIHdpbGwgbG9vayBhdCByZWdyZXNzaW9uIG1vZGVscyBmb3IgdGltZSBzZXJpZXMgbGF0ZXIgaW4gdGhlIGNvdXJzZS4NCg0KIyMjIEFzc2Vzc2luZyBBc3N1bXB0aW9uIEEzDQoNCklmICoqKkEzKioqIGlzIGNvcnJlY3QsIHRoZW4gdGhlICoqc3ByZWFkKiogb2YgdGhlIChzdGFuZGFyZGl6ZWQpIHJlc2lkdWFscyBhYm91dCB6ZXJvIHNob3VsZCBub3QgdmFyeSBzeXN0ZW1hdGljYWxseSB3aXRoIHRoZSBmaXR0ZWQgdmFsdWUgb3IgY292YXJpYXRlLg0KDQoqKkhldGVyb3NjZWRhc3RpY2l0eSoqICAodW5lcXVhbCBzcHJlYWQpIGlzIHR5cGljYWxseSByZWNvZ25pemVkIGFzIGEgImZhbm5pbmcgb3V0IiBvZiAoc3RhbmRhcmRpemVkKSByZXNpZHVhbHMgd2hlbiBwbG90dGVkIGFnYWluc3QgZml0dGVkIHZhbHVlIG9yICBjb3ZhcmlhdGUuICBUaGlzIG1lYW5zIHRoZSAgZXJyb3J2YXJpYW5jZSBpbmNyZWFzZXMgd2l0aCB0aGUgbWVhbiByZXNwb25zZS4gKEFsdGVybmF0aXZlbHkgb25lIGNvdWxkIGhhdmUgZmFubmluZyBpbikuDQoNCkZhaWx1cmUgb2YgdGhpcyBhc3N1bXB0aW9uIHdpbGwgbGVhdmUgcGFyYW1ldGVyIGVzdGltYXRlcyB1bmJpYXNlZCwgYnV0IHN0YW5kYXJkIGVycm9ycyB3aWxsIGJlIGluY29ycmVjdCwgYW5kIGhlbmNlICB0ZXN0IHJlc3VsdHMgYW5kIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIHdpbGwgYmUgdW5yZWxpYWJsZS4NCg0KQSB0cmFuc2Zvcm1hdGlvbiBvZiBib3RoIHJlc3BvbnNlIGFuZCBwcmVkaWN0b3IgdmFyaWFibGVzICAgIGNhbiBzb21ldGltZXMgaGVscCBpbiB0aGlzIGNhc2UuDQoNCkFuIGFsdGVybmF0aXZlIHN0cmF0ZWd5IGlzIHRvIHVzZSAqKndlaWdodGVkIGxpbmVhciByZWdyZXNzaW9uKiogKGNvdmVyZWQgbGF0ZXIgaW4gdGhlIGNvdXJzZSkuDQoNCg0KDQoNCiMjIyBBc3Nlc3NpbmcgQXNzdW1wdGlvbiBBNA0KDQpBIG5vcm1hbCBwcm9iYWJpbGl0eSBwbG90LCBvciAqKm5vcm1hbCBRLVEgcGxvdCoqLCBpcyBhIHN0YW5kYXJkIGdyYXBoaWNhbCB0b29sIGZvciBhc3Nlc3NpbmcgdGhlIG5vcm1hbGl0eSBvZiBhIGRhdGEgc2V0Lg0KDQpBIG5vcm1hbCBRLVEgcGxvdCBvZiB0aGUgKHN0YW5kYXJkaXplZCkgcmVzaWR1YWxzIHNob3VsZCBzaG93IHBvaW50cyBzdGF5aW5nIGNsb3NlICB0byBhIHN0cmFpZ2h0IGxpbmUgLS0tIGN1cnZhdHVyZSBpbmRpY2F0ZXMgYSBmYWlsdXJlIG9mICAqKipBNCoqKi4NCg0KT24gdGhlIG90aGVyIGhhbmQgZmFpbHVyZSBvZiB0aGUgIG5vcm1hbGl0eSBhc3N1bXB0aW9uIGZvciB0aGUgICBlcnJvciBkaXN0cmlidXRpb24gaXMgbm90IHVzdWFsbHkgYSBzZXJpb3VzIHByb2JsZW0uICBSZW1vdmFsIG9mIChvYnZpb3VzKSBvdXRsaWVycyB3aWxsIG9mdGVuIGltcHJvdmUgdGhlIG5vcm1hbGl0eSAgb2YgdGhlIHN0YW5kYXJkaXplZCByZXNpZHVhbHMuDQoNCg0KIyMgRGlhZ25vc3RpY3MgZm9yIEhpbGwgcmFjZSBzIEV4YW1wbGUgDQoNClRoZSBgcGxvdCgpYCBjb21tYW5kIGFwcGxpZWQgdG8gdGhlIGZpdHRlZCBtb2RlbCBgSGlsbHMubG1gIHByb2R1Y2VzIGZvdXIgdXNlZnVsIGRpYWdub3N0aWMgcGxvdHMuIFRoZSBgcGFyKClgIGNvbW1hbmQgcHV0IGZpcnN0IHNwbGl0cyB0aGUgZ3JhcGhpbmcgd2luZG93IGludG8gYSB0d28tYnktdHdvIGdyaWQgc28gd2UgY2FuIHNlZSBhbGwgZm91ciBncmFwaHMgdG9nZXRoZXIuIA0KYGBge3IgSGlsbHMubG1EaWFnLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTAsIGZpZy5jYXA9IlJlc2lkdWFsIGFuYWx5c2lzIHBsb3RzIGZvciB0aGUgZmlyc3QgbW9kZWwgYXBwbGllZCB0byB0aGUgaGlsbCByYWNpbmcgZGF0YS4ifQ0KcGFyKG1mcm93PWMoMiwyKSkNCnBsb3QoSGlsbHMubG0pDQpgYGAgIA0KDQpUaGUgZmlyc3QgdGhyZWUgb2YgdGhlc2UgcGxvdHMgYXJlOg0KDQotIFBsb3Qgb2YgcmF3IHJlc2lkdWFscyB2ZXJzdXMgZml0dGVkIHZhbHVlcw0KLSBOb3JtYWwgUS1RIHBsb3QgZm9yIHN0YW5kYXJkaXplZCByZXNpZHVhbHMNCi0gU2NhbGUtbG9jYXRpb24gcGxvdCAoc3F1YXJlIHJvb3Qgb2YgYWJzb2x1dGUgc3RhbmRhcmRpemVkIHJlc2lkdWFscyBhZ2FpbnN0IGZpdHRlZCB2YWx1ZSkNCg0KYGBge3IgSGlsbHMubG1EaWFnMSwgZmlnLmNhcD0iUmVzaWR1YWxzIHBsb3R0ZWQgYWdhaW5zdCBmaXR0ZWQgdmFsdWVzIGZvciB0aGUgZmlyc3QgbW9kZWwgZml0dGVkIHRvIHRoZSBoaWxsIHJhY2luZyBkYXRhIn0NCnBsb3QoSGlsbHMubG0sIHdoaWNoPTEpDQpgYGANCg0KDQoNCg0KDQpUaGlzIHJlc2lkdWFsIHBsb3Qgc2hvd3Mgc29tZSBkYXRhIHBvaW50cyB3aXRoIHZlcnkgbGFyZ2UgcmVzaWR1YWxzLg0KDQpUaGUgcmVkIGxpbmUgaXMgYSBub25wYXJhbWV0cmljIGVzdGltYXRlIG9mIHRyZW5kICh0YWtlIGNhcmUgbm90IHRvIG92ZXItaW50ZXJwcmV0KSBpdC4NCg0KDQpXb3VsZCB5b3Ugc2F5IHRoZXJlIGlzIHN0cm9uZyBldmlkZW5jZSBvZiBjdXJ2YXR1cmUsIG9yIG9mIGluY3JlYXNpbmcgc3ByZWFkIGluIHJlc2lkdWFscz8NCg0KDQoNCg0KYGBge3IgSGlsbHMubG1EaWFnMiwgZmlnLmNhcD0iTm9ybWFsaXR5IHBsb3Qgb2YgcmVzaWR1YWxzIGZvciB0aGUgZmlyc3QgbW9kZWwgZml0dGVkIHRvIHRoZSBoaWxsIHJhY2luZyBkYXRhIn0NCnBsb3QoSGlsbHMubG0sIHdoaWNoPTIpDQpgYGANCg0KDQoNClRoZSBOb3JtYWwgUS1RIHBsb3QgaXMgZmFyIGZyb20gYSBzdHJhaWdodCBsaW5lLiBUaGlzIGluZGljYXRlcyBmYWlsdXJlIG9mIHRoZSBub3JtYWxpdHkgYXNzdW1wdGlvbi4NCg0KTm90ZSB0aGF0IHRoaXMgcGxvdCBpcyBoZWF2aWx5IGluZmx1ZW5jZWQgYnkgZGF0YSBwb2ludHMgd2l0aCBsYXJnZSByZXNpZHVhbHMuDQoNCmBgYHtyIEhpbGxzLmxtRGlhZzMsIGZpZy5jYXA9IlNjYWxlIHZlcnN1cyBsb2NhdGlvbiBwbG90IGZvciB0aGUgZmlyc3QgbW9kZWwgZml0dGVkIHRvIHRoZSBoaWxsIHJhY2luZyBkYXRhIn0NCnBsb3QoSGlsbHMubG0sIHdoaWNoPTMpDQpgYGANCg0KRm9yIHRoZSBzY2FsZS1sb2NhdGlvbiBwbG90LCB3ZSBsb29rIGZvciBhIHRyZW5kIGZvciB0aGUgbGluZSB0byBiZSBpbmNyZWFzaW5nIChvciBkZWNyZWFzaW5nKSBhcyB3ZSBtb3ZlIGZyb20gbGVmdCB0byByaWdodC4gSWYgc28sIGl0ICBwcm92aWRlcyBhIGhpbnQgdGhhdCBzcHJlYWQgb2YgcmVzaWR1YWxzIGlzIGluY3JlYXNpbmcgKG9yIGRlY3JlYXNpbmcpIHdpdGggZml0dGVkIHZhbHVlLiANCg0KVGhpcyBncmFwaCBzdWdnZXN0cyB0aGUgZXJyb3IgdmFyaWFuY2UgJFxzaWdtYV4yJCBtYXkgbm90IGJlIGNvbnN0YW50IGZvciBhbGwgZGF0YSBwb2ludHMuDQoNCg0KIyMjIFdoYXQgYWJvdXQgYXNzdW1wdGlvbiAqKipBMioqKiwgaW5kZXBlbmRlbnQgZXJyb3I/IFdlIGNhbiBqdXN0IHBsb3QgdGhlIHJlc2lkdWFscyB3aXRob3V0IGFuICp4KiB2YXJpYWJsZS4gDQoNCmBgYHtyIFBsb3RPZkVycm9yc1NlcXVlbmNlfQ0KcGxvdChIaWxscy5sbSRyZXNpZHVhbHMpDQphYmxpbmUoaD0wLCBsdHk9MikNCmBgYA0KDQpUaGUgb3V0bGllcnMgbWFrZSBpdCBoYXJkIHRvIHRlbGwsIGJ1dCB0aGUgZ3JhcGggc3VnZ2VzdHMgdGhlIHJlc2lkdWFscyBtYXkgbm90IGJlIGVudGlyZWx5IHJhbmRvbSAtIHBlcmhhcHMgdGhlcmUgaXMgc29tZSByZWxldmFudCBvcmRlciBpbiB0aGUgZGF0YS4gDQoNCg0KIyMgQ29uY2x1c2lvbnMgZnJvbSBEaWFnbm9zdGljcyBmb3IgRXhhbXBsZSANCg0KTGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgYXNzdW1wdGlvbnMgZG8gbm90IGFwcGVhciB2YWxpZC4NCg0KSGVuY2UgY29uY2x1c2lvbnMgYmFzZWQgb24gdGhpcyBtb2RlbCBhcmUgcXVlc3Rpb25hYmxlLg0KDQpQcm9ibGVtcyB3aXRoIG1vZGVsIHNlZW0gbGFyZ2VseSBkb3duIHRvIGEgZmV3ICoqb2RkKiogZGF0YSBwb2ludHMgb3IgKipvdXRsaWVycyoqLg0K