This (untaught) lecture considers the use of transformations in
regression
Students who have taken 161.220 or 161.250 Data Analysis will have
seen discussion of transformations, while students who carry on to
161.331 Biostatistics will see a more rigorous discussion of the impact
of transforming variables.
Purpose of transformations
Often the observable relationship between Y and x
is not a straight line, but we may be able to transform Y to a
new response variable \(Y_{new}\),
and/or X to a new response variable \(X_{new}\), in such a way that the
relationship between \(Y_{new}\) and
\(X_{new}\) is a straight line:
\[ Y_{new} = \beta_0 + \beta_1 X_{new} +
\varepsilon_{new}\]
In that case we can do our analysis on the ‘new’ scales, and then
back-transform the results.
We still regard the analysis as being in the category of a linear
regression, since the \(\beta\)s are
separated by pluses, so we can estimate parameters using standard
regression methods (linear algebra, etc.).
Sometimes the choice of transformation is suggested by theoretical
considerations. But if we don’t have theory to fall back on, then we
have to use trial and error, guided by educated guesswork. We rely
heavily on finding a model where residuals \(\varepsilon_{new}\) are
well-behaved.
Rationale: if there is no pattern or structure left in the residuals
then the model for the expected value (line) \(E[Y_{new}|x_{new}]\) must be capturing the
gist (main message) of the relationship.
Note it is not enough to use a transformation to make the underlying
curve linear. We have to take care of the errors as well. This means
conducting a residual analysis, just like any other regression models we
fit.
Basically there are three reasons why one would do a
transformation
(1) to make the relationship linear (not curved)
(2) to make the errors more normal
(3) to equalise the spread (variance) of residuals
Sometimes a single transformation may fix all three problems. But
sometimes we may have to compromise.
Example: Consider the relationship between the density of planting
onions and the yield:
Onions = read.csv(file = "../../data/Onions.csv", header = TRUE)
par(mfrow = c(1, 2))
Onions.lm = lm(yield ~ density, data = Onions)
plot(yield ~ density, data = Onions)
plot(residuals(Onions.lm) ~ density, data = Onions)
The fitted line plot and residuals both show curvature.
So we need to try a transformation. We will begin by looking at the
ladder of powers.
Ladder of powers
This refers to transformations of the form \(y^p\) or \(x^p\) for some power p.
Usually we restrict ourselves to multiples of \(\frac12\) for p, but we don’t
strictly have to
\(p=3 \longrightarrow y^3\)
cubing
\(p=2 \longrightarrow y^2\)
squaring
\(p=1 \longrightarrow y^1\)
untransformed
\(p=0.5 \longrightarrow y^{0.5} =
\sqrt{y}\) square root
\(p=0 \longrightarrow \log(y)\)
natural logarithm
\(p=-0.5 \longrightarrow
-1/\sqrt{y}\) reciprocal square root
\(p=-1 \longrightarrow -1/y\)
reciprocal
The p=0 case is an ‘odd man out’ in that \(y^0 \equiv 1\) for any value of y.
But \(\log(y)\) takes its place in the
ladder. The multiplication by minus one for negative powers is not
essential, but it keeps points that were on the right of the original
graph still on the right, and those on the left of the original graph
still on the left.
As we move up the ladder of powers, data become increasingly
right-skewed.
As we move down the ladder, data become increasingly left-skewed.
So if we start with right-skewed data, then going down the ladder of
powers will massage the data towards symmetry.
par(mfrow = c(1, 2))
y = (rnorm(100, 10, 2))^3
hist(y)
hist(sqrt(y))
Onions example
Log-transforming y or x separately doesn’t seem to
fix the curvature.
Onions.lm = lm(log(yield) ~ density, data = Onions)
plot(log(yield) ~ density, data = Onions)
plot(residuals(Onions.lm) ~ density, data = Onions)
Onions.lm = lm(yield ~ log(density), data = Onions)
plot(yield ~ log(density), data = Onions)
plot(residuals(Onions.lm) ~ log(density), data = Onions)
A reciprocal transformation of y seems to give us a straight
line, but the residuals show heteroscedasticity - the cluster of points
on the left of the residual plot (density<5) have much smaller
(vertical) spread of residuals.
par(mfrow = c(1, 2))
Onions.lm = lm(1/yield ~ density, data = Onions)
plot(1/yield ~ density, data = Onions)
plot(residuals(Onions.lm) ~ density, data = Onions)
Transforming both x and y does the job. The
curvature is fixed and the residuals, though not perfect, are more
even.
par(mfrow = c(1, 2))
Onions.lm = lm(log(yield) ~ log(density), data = Onions)
plot(log(yield) ~ log(density), data = Onions)
plot(residuals(Onions.lm) ~ log(density), data = Onions)
log transform as a model for percentage change
We consider the following data on the number of subscribers to
cellular phones in the USA, from the mid-1980s. The number of
subscribers is given at 6-monthly intervals (see the variable “Period”).
A plot of Y=Subscribers versus x=Period gives a curve.
Note this is clearly a time series, and so adjacent time points will not
be independent, which violates one of the assumptions of regression
modelling. However we will ignore this fact for the moment, so we can
see what happens.
Cellular = read.csv(file = "../../data/Cellular.csv")
head(Cellular)
Period Month Year Subscribers
1 1 12 1984 91600
2 2 6 1985 203600
3 3 12 1985 340213
4 4 6 1986 500000
5 5 12 1986 681825
6 6 6 1987 883779
plot(Subscribers ~ Period, data = Cellular)
This looks like an exponential growth model \[E[ Subscribers] = S_0 e^{\beta_1\times Period }
\]
where \(S_0\) is the number of
subscribers at \(Period=0\) and \(\beta_1\) is a growth rate indicating how
fast the number of subscribers is growing in powers of \(e= 2.71828\). For example if \(\beta_1 = 0.02\) then the number of
subscribers is expected to grow to \(e^{0.02}=\) 1.0202 the size it was before,
i.e. a 2.02% increase.
Now we try a log transformation. If my guess of the model is correct,
then a plot of log(Y) versus x should give a straight
line. In fact, the reverse argument is also true : if we get a straight
line, then we know the exponential growth curve was correct.
plot(log(Subscribers) ~ Period, data = Cellular)
OK so clearly it is not a straight line, but reflects different parts
of a products market cycle: initially few people had cell phones so any
increase seemed rapid. But later, after about \(Period=10\) the market for cell phones was
maturing and the increase seems about linear.
Since it is more important to predict the future than the past(!)
let’s drop the early data and see if we get a good model for \(Period\ge 10\).
Cellular2 = Cellular |>
filter(Period > 9)
Cellular2.lm = lm(log(Subscribers) ~ Period, data = Cellular2)
plot(residuals(Cellular2.lm) ~ Period, data = Cellular2)
The points now look close to a line, except the first one. This point
also stands out among the residuals. We will try deleting this one more
point.
Cellular3 = Cellular |>
filter(Period > 10)
Cellular3.lm = lm(log(Subscribers) ~ Period, data = Cellular3)
plot(residuals(Cellular3.lm) ~ Period, data = Cellular3)
The residuals now look patternless, so we will declare it a good
model. The line is \[\hat {log(y)} =
exp(13.020897 + 0.187908 x) = 451755.8 e^{0.187908 x} \]
Substituting x = 23, (the last period in the dataset) the fitted
number of subscribers was 34030866 (only a 0.73% difference from the
observed value of 33785661).
To predict for the next time period, we would substitute x=24.
Putting it another way, since
\[\hat Y_{24} = 451755.8 e^{0.187908\times
(23 +1)} = \hat Y_{23} \times 1.206722 \]
we can summarize by saying that we expect a 20.7% growth in the number
of cell phone subscribers in the next 6 months.
This calculation highlights the following important fact: a
linear model for log(Y) corresponds to a model for percentage growth (or
percentage decline)
This is a major reason why the log transform is used a lot in
economic data, as so many measures of growth are given in percentage
terms (e.g. interest rates).
Transformations for Count data (Extension work)
This section looks at Transformation To Constant Variance (Correcting
Heteroscedasticity)
We have seen that heteroscedasticity (unequal spread) can arise
because the errors occur on a different scale to the one we have
measured on (e.g. the response should be measured on a log scale and if
we do that then multiplicative errors become constant.) But this is not
the only potential source of heteroscedasticity.
Binomial Data
Suppose our data are the number of ‘successes’ in a set of n
independent trials. For example Y might be the number of people
answering “yes” to a question, or the number of stocks of a certain type
that show a rise in response to a news event. We call such data
“Binomial” . The assumption of independent trials refers to the idea
that the outcome from one person (or one stock) does not in itself
affect the outcome from any other person (or stock). It turns out that
some mathematical theory suggests we transform using \[ Y_{new} = sin^{-1}(\sqrt y )\]
Here \(sin^{-1}\) is the inverse
sine transformation (or arcsine) that you may remember from trigonometry
in your high-school maths course.
This approach has fallen out of fashion in the 21st
century because of development of methods called “generalized linear
models” that work with Binomial models directly.
Poisson Data
This is another mathematical model used for data consisting of
counts, but in this case where there is no fixed upper limit on the
number of responses one might obtain (contrast to the Binomial, where
the \(Y \le n\)).
For example we may be counting the number of customers responding to
an advertisement. Then for practical purposes we cannot stipulate a
maximum Y (or at least only a very large maximum, such as the
population of a country, and we don’t expect the data to go anywhere
near that value. )
It is again assumed that the events counted happen independently of
each other (e.g. people make up their own minds whether or not to be
customers).
A mathematical characteristic of Poisson data is that the mean and
variance are equal. We write this as \[ E[Y]
= \mu = \mbox{Variance}(Y) ~~,\] where\(\mu\) is the average rate of events per
unit time (e.g. customers per day).
Putting it in terms of the standard deviation of Y, \(sd(Y) = \sqrt Y\).
Some mathematical theory suggests that we should be able to stabilise
the variance of Poisson data by analysing the transformed data \(Y_{new} = \sqrt Y\) (or \(\sqrt{Y+1}\) if the Y values are small).
The latter was the transformation used to fit the data on
mountain-climbing fatalities on Mt Everest, in Topic 08.
Negative Binomial
If the data are the result of counting events which are not
independent, but exhibit clustering (so that the rate at which the
random events occur seems to vary from time to time), then a
mathematical model that may be suitable is called the Negative Binomial
distribution.
For this type of data, sometimes called “clustered Poisson”, the
variance tends to be bigger than the mean (\(\mu<\sigma^2\)). A common practice is to
transform the response to \(Y_{new} =
log(1+Y)\) or sometimes \(log(0.5+Y)\).
Example: How many magazines do you read?
We consider some data on the number of magazines read by a sample of
New Zealanders, versus their education level (variable “Educgrp”). The
data were generously provided by a market research company, AGB McNair.
Randomly selected respondents were shown the covers of a number of
popular magazines (e.g. Women’s Weekly) and were asked whether they had
read those issues. We deal here with the total number of magazines read.
Educgrp is a categorical variable, with levels
1= Primary School only
2= Secondary School but no NCEA or School Certificate
3= NCEA level 1 / School Certificate
4=NCEA level 2 or 3 / University Entrance or Bursary
5=Technical or Trade Qualification
6=Other Tertiary (not university)
7=University Qualification
8=Studied at University but not completed
The following Boxplot shows the number of magazines read for each
level of Educgrp, with error bounds. The medians seem to show a weak
upward trend, but the graph is swamped by so many outliers.
Read = read.csv(file = "../../data/READ.csv", header = TRUE)
head(Read)
Mar.Stat Educgrp Gender Agegrp Ethnicty SES PersIncm FamIncm Y1 Newspaper
1 1 3 2 15 2 4 4 6 3 12
2 1 7 2 14 8 1 1 5 4 0
3 1 5 2 14 2 3 1 3 4 0
4 2 2 2 6 2 4 1 8 3 5
5 1 7 1 13 2 2 4 5 1 6
6 3 2 1 15 2 2 4 3 5 11
Magazines sqrtY3 lnMags1 AnyPaper AvidReader
1 5 2.236068 1.791759 1 1
2 0 0.000000 0.000000 0 0
3 3 1.732051 1.386294 0 0
4 3 1.732051 1.386294 1 0
5 5 2.236068 1.791759 1 0
6 11 3.316625 2.484907 1 1
boxplot(Magazines ~ Educgrp, data = Read)
Since the number of magazines is a count, we can try the \(\sqrt Y\) and \(log(1+Y)\) transformations. Note we need
the 1+ because many people read zero magazines, and we cannot
take \(log(0)\).
boxplot(sqrt(Magazines) ~ Educgrp, data = Read)
boxplot(log(1 + Magazines) ~ Educgrp, data = Read)
It looks as if either transformation would do, but the \(log(1+Y)\) version is slightly more
symmetrical.
The pattern of medians suggests we could model the relationship
between number of log(1+magazines) and education level using a nonlinear
relationship, covered in another lecture.
The course 161.331 shows how to model Poisson, Negative Binomial, and
other types of data using Generalized Linear Models.
LS0tDQp0aXRsZTogIkxlY3R1cmUgNDM6IEFwcGVuZGl4OiBUcmFuc2Zvcm1hdGlvbnMgaW4gcmVncmVzc2lvbiBtb2RlbHMiDQpzdWJ0aXRsZTogMTYxLjI1MSBSZWdyZXNzaW9uIE1vZGVsbGluZw0KYXV0aG9yOiAiUHJlc2VudGVkIGJ5IE1hdHRoZXcgUGF3bGV5IDxNLlBhd2xleUBtYXNzZXkuYWMubno+IiAgDQpkYXRlOiAiV2VlayAzIG9mIFNlbWVzdGVyIDIsIGByIGx1YnJpZGF0ZTo6eWVhcihsdWJyaWRhdGU6Om5vdygpKWAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiB5ZXRpDQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICBodG1sX25vdGVib29rOg0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0aGVtZTogeWV0aQ0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgaW9zbGlkZXNfcHJlc2VudGF0aW9uOg0KICAgIHdpZGVzY3JlZW46IHRydWUNCiAgICBzbWFsbGVyOiB0cnVlDQogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgc2xpZHlfcHJlc2VudGF0aW9uOiANCiAgICB0aGVtZTogeWV0aQ0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KDQoNCg0KPCEtLS0gRGF0YSBpcyBvbg0KaHR0cHM6Ly9yLXJlc291cmNlcy5tYXNzZXkuYWMubnovZGF0YS8xNjEyNTEvDQotLS0+DQoNCmBgYHtyIHNldHVwLCBwdXJsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeShrbml0cikNCm9wdHNfY2h1bmskc2V0KGRldj1jKCJwbmciLCAicGRmIikpDQpvcHRzX2NodW5rJHNldChmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD03LCBmaWcucGF0aD0iRmlndXJlcy8iLCBmaWcuYWx0PSJ1bmxhYmVsbGVkIikNCm9wdHNfY2h1bmskc2V0KGNvbW1lbnQ9IiIsIGZpZy5hbGlnbj0iY2VudGVyIiwgdGlkeT1UUlVFKQ0Kb3B0aW9ucyhrbml0ci5rYWJsZS5OQSA9ICcnKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGJyb29tKQ0KYGBgDQoNCg0KPCEtLS0gRG8gbm90IGVkaXQgYW55dGhpbmcgYWJvdmUgdGhpcyBsaW5lLiAtLS0+DQoNClRoaXMgKHVudGF1Z2h0KSBsZWN0dXJlIGNvbnNpZGVycyB0aGUgdXNlIG9mIHRyYW5zZm9ybWF0aW9ucyBpbiByZWdyZXNzaW9uDQoNCg0KU3R1ZGVudHMgd2hvIGhhdmUgdGFrZW4gMTYxLjIyMCBvciAxNjEuMjUwIERhdGEgQW5hbHlzaXMgd2lsbCBoYXZlIHNlZW4gZGlzY3Vzc2lvbiBvZiB0cmFuc2Zvcm1hdGlvbnMsIHdoaWxlIHN0dWRlbnRzIHdobyBjYXJyeSBvbiB0byAxNjEuMzMxIEJpb3N0YXRpc3RpY3Mgd2lsbCBzZWUgYSBtb3JlIHJpZ29yb3VzIGRpc2N1c3Npb24gb2YgdGhlIGltcGFjdCBvZiB0cmFuc2Zvcm1pbmcgdmFyaWFibGVzLg0KDQoNCiMjIFB1cnBvc2Ugb2YgdHJhbnNmb3JtYXRpb25zDQoNCk9mdGVuIHRoZSBvYnNlcnZhYmxlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuICpZKiBhbmQgKngqIGlzIG5vdCBhIHN0cmFpZ2h0IGxpbmUsIGJ1dCB3ZSBtYXkgYmUgYWJsZSB0byB0cmFuc2Zvcm0gICpZKiB0byBhIG5ldyByZXNwb25zZSB2YXJpYWJsZSAkWV97bmV3fSQsICAgYW5kL29yICAqWCogIHRvIGEgbmV3IHJlc3BvbnNlIHZhcmlhYmxlICRYX3tuZXd9JCwgIGluIHN1Y2ggYSB3YXkgdGhhdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gJFlfe25ld30kIGFuZCAkWF97bmV3fSQgaXMgYSBzdHJhaWdodCBsaW5lOiANCg0KICQkICAgWV97bmV3fSA9IFxiZXRhXzAgKyBcYmV0YV8xIFhfe25ld30gKyBcdmFyZXBzaWxvbl97bmV3fSQkICAgICAgICAgDQoNCkluIHRoYXQgY2FzZSB3ZSBjYW4gZG8gb3VyIGFuYWx5c2lzIG9uIHRoZSDigJhuZXfigJkgc2NhbGVzLCBhbmQgdGhlbiAqKipiYWNrLXRyYW5zZm9ybSB0aGUgcmVzdWx0cyoqKi4gICANCg0KV2Ugc3RpbGwgcmVnYXJkIHRoZSBhbmFseXNpcyBhcyBiZWluZyBpbiB0aGUgY2F0ZWdvcnkgb2YgYSAgbGluZWFyIHJlZ3Jlc3Npb24sIHNpbmNlIHRoZSAkXGJldGEkcyBhcmUgc2VwYXJhdGVkIGJ5IHBsdXNlcywgc28gd2UgY2FuIGVzdGltYXRlIHBhcmFtZXRlcnMgdXNpbmcgc3RhbmRhcmQgcmVncmVzc2lvbiBtZXRob2RzIChsaW5lYXIgYWxnZWJyYSwgZXRjLikuIA0KDQpTb21ldGltZXMgdGhlIGNob2ljZSBvZiB0cmFuc2Zvcm1hdGlvbiBpcyBzdWdnZXN0ZWQgYnkgdGhlb3JldGljYWwgY29uc2lkZXJhdGlvbnMuICBCdXQgaWYgd2UgZG9u4oCZdCBoYXZlIHRoZW9yeSB0byBmYWxsIGJhY2sgb24sICB0aGVuIHdlIGhhdmUgdG8gdXNlIHRyaWFsIGFuZCBlcnJvciwgZ3VpZGVkIGJ5IGVkdWNhdGVkIGd1ZXNzd29yay4gIFdlIHJlbHkgaGVhdmlseSBvbiBmaW5kaW5nIGEgbW9kZWwgd2hlcmUgKioqcmVzaWR1YWxzICRcdmFyZXBzaWxvbl97bmV3fSQgYXJlIHdlbGwtYmVoYXZlZCoqKi4NCg0KUmF0aW9uYWxlOiAgaWYgdGhlcmUgaXMgbm8gcGF0dGVybiBvciBzdHJ1Y3R1cmUgbGVmdCBpbiB0aGUgcmVzaWR1YWxzIHRoZW4gdGhlIG1vZGVsIGZvciB0aGUgZXhwZWN0ZWQgdmFsdWUgKGxpbmUpICRFW1lfe25ld318eF97bmV3fV0kIG11c3QgYmUgY2FwdHVyaW5nIHRoZSBnaXN0IChtYWluIG1lc3NhZ2UpIG9mIHRoZSByZWxhdGlvbnNoaXAuICANCg0KTm90ZSBpdCBpcyBub3QgZW5vdWdoIHRvIHVzZSBhIHRyYW5zZm9ybWF0aW9uIHRvIG1ha2UgdGhlIHVuZGVybHlpbmcgY3VydmUgbGluZWFyLiAgV2UgaGF2ZSB0byB0YWtlIGNhcmUgb2YgdGhlIGVycm9ycyBhcyB3ZWxsLiAgVGhpcyBtZWFucyBjb25kdWN0aW5nIGEgcmVzaWR1YWwgYW5hbHlzaXMsIGp1c3QgbGlrZSBhbnkgb3RoZXIgcmVncmVzc2lvbiBtb2RlbHMgd2UgZml0Lg0KDQpCYXNpY2FsbHkgdGhlcmUgYXJlIHRocmVlIHJlYXNvbnMgd2h5IG9uZSB3b3VsZCBkbyBhIHRyYW5zZm9ybWF0aW9uDQogIA0KKiooMSkgdG8gbWFrZSB0aGUgcmVsYXRpb25zaGlwIGxpbmVhciAobm90IGN1cnZlZCkqKg0KDQoqKigyKSB0byBtYWtlIHRoZSBlcnJvcnMgbW9yZSBub3JtYWwqKg0KDQoqKigzKSB0byBlcXVhbGlzZSB0aGUgc3ByZWFkICh2YXJpYW5jZSkgb2YgcmVzaWR1YWxzICoqDQoNClNvbWV0aW1lcyBhIHNpbmdsZSB0cmFuc2Zvcm1hdGlvbiBtYXkgZml4IGFsbCB0aHJlZSBwcm9ibGVtcy4gQnV0IHNvbWV0aW1lcyB3ZSBtYXkgaGF2ZSB0byBjb21wcm9taXNlLg0KDQpFeGFtcGxlOiBDb25zaWRlciB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIGRlbnNpdHkgb2YgcGxhbnRpbmcgb25pb25zIGFuZCB0aGUgeWllbGQ6DQoNCg0KYGBge3Igb25pb25zIGxpbmVhcn0NCk9uaW9ucyA9IHJlYWQuY3N2KGZpbGU9Ii4uLy4uL2RhdGEvT25pb25zLmNzdiIsaGVhZGVyPVRSVUUpDQpwYXIobWZyb3c9YygxLDIpKQ0KT25pb25zLmxtID0gbG0oeWllbGR+ZGVuc2l0eSAsIGRhdGE9T25pb25zKQ0KcGxvdCh5aWVsZH5kZW5zaXR5ICwgZGF0YT1PbmlvbnMpDQpwbG90KHJlc2lkdWFscyhPbmlvbnMubG0pfiBkZW5zaXR5LCBkYXRhPU9uaW9ucykNCmBgYA0KDQpUaGUgZml0dGVkIGxpbmUgcGxvdCBhbmQgcmVzaWR1YWxzIGJvdGggc2hvdyBjdXJ2YXR1cmUuIA0KDQpTbyB3ZSBuZWVkIHRvIHRyeSBhIHRyYW5zZm9ybWF0aW9uLiAgV2Ugd2lsbCBiZWdpbiBieSBsb29raW5nIGF0IHRoZSBsYWRkZXIgb2YgcG93ZXJzLiANCg0KIyMgTGFkZGVyIG9mIHBvd2Vycw0KDQpUaGlzIHJlZmVycyB0byB0cmFuc2Zvcm1hdGlvbnMgb2YgdGhlIGZvcm0gICR5XnAkIG9yICR4XnAkIGZvciBzb21lIHBvd2VyICpwKi4gIA0KVXN1YWxseSB3ZSByZXN0cmljdCBvdXJzZWx2ZXMgdG8gbXVsdGlwbGVzIG9mICRcZnJhYzEyJCBmb3IgKnAqLCBidXQgd2UgZG9uJ3Qgc3RyaWN0bHkgaGF2ZSB0byANCg0KLSAkcD0zIFxsb25ncmlnaHRhcnJvdyB5XjMkIGN1YmluZyAJIA0KDQotICRwPTIgXGxvbmdyaWdodGFycm93IHleMiQgc3F1YXJpbmcgDQoNCi0gJHA9MSBcbG9uZ3JpZ2h0YXJyb3cgeV4xJCB1bnRyYW5zZm9ybWVkIA0KDQotICRwPTAuNSBcbG9uZ3JpZ2h0YXJyb3cgeV57MC41fSA9IFxzcXJ0e3l9JCBzcXVhcmUgcm9vdA0KDQotICRwPTAgXGxvbmdyaWdodGFycm93IFxsb2coeSkkICBuYXR1cmFsIGxvZ2FyaXRobSANCg0KLSAkcD0tMC41IFxsb25ncmlnaHRhcnJvdyAtMS9cc3FydHt5fSQgcmVjaXByb2NhbCBzcXVhcmUgcm9vdA0KDQotICRwPS0xIFxsb25ncmlnaHRhcnJvdyAtMS95JCAgcmVjaXByb2NhbCANCg0KVGhlICpwPTAqIGNhc2UgaXMgYW4gJ29kZCBtYW4gb3V0JyBpbiB0aGF0ICR5XjAgXGVxdWl2IDEkIGZvciBhbnkgdmFsdWUgb2YgKnkqLiBCdXQgICRcbG9nKHkpJCB0YWtlcyBpdHMgcGxhY2UgaW4gdGhlIGxhZGRlci4gICBUaGUgbXVsdGlwbGljYXRpb24gYnkgbWludXMgb25lIGZvciBuZWdhdGl2ZSBwb3dlcnMgaXMgbm90IGVzc2VudGlhbCwgYnV0IGl0IGtlZXBzIHBvaW50cyB0aGF0IHdlcmUgb24gdGhlIHJpZ2h0IG9mIHRoZSBvcmlnaW5hbCBncmFwaCBzdGlsbCBvbiB0aGUgcmlnaHQsIGFuZCB0aG9zZSBvbiB0aGUgbGVmdCBvZiB0aGUgb3JpZ2luYWwgZ3JhcGggc3RpbGwgb24gdGhlIGxlZnQuIA0KDQpBcyB3ZSBtb3ZlIHVwIHRoZSBsYWRkZXIgb2YgcG93ZXJzLCBkYXRhIGJlY29tZSBpbmNyZWFzaW5nbHkgcmlnaHQtc2tld2VkLiANCg0KQXMgd2UgbW92ZSBkb3duIHRoZSBsYWRkZXIsIGRhdGEgYmVjb21lIGluY3JlYXNpbmdseSBsZWZ0LXNrZXdlZC4NCg0KU28gaWYgd2Ugc3RhcnQgd2l0aCByaWdodC1za2V3ZWQgZGF0YSwgdGhlbiBnb2luZyBkb3duIHRoZSBsYWRkZXIgb2YgcG93ZXJzIHdpbGwgbWFzc2FnZSB0aGUgZGF0YSB0b3dhcmRzIHN5bW1ldHJ5LiANCg0KYGBge3IgbGFkZGVyIG9mIHBvd2Vyc30NCnBhcihtZnJvdz1jKDEsMikpDQp5PSAocm5vcm0oMTAwLCAxMCwyKSleMw0KaGlzdCh5KQ0KaGlzdChzcXJ0KHkpKQ0KaGlzdChsb2coeSkpDQpoaXN0KC0xL3kpDQpgYGANCg0KIyMgT25pb25zIGV4YW1wbGUNCg0KTG9nLXRyYW5zZm9ybWluZyAqeSogb3IgKngqIHNlcGFyYXRlbHkgZG9lc24ndCBzZWVtIHRvIGZpeCB0aGUgY3VydmF0dXJlLiANCg0KYGBge3IgT25pb25zWVRyYW5zZm9ybX0NCk9uaW9ucy5sbSA9IGxtKGxvZyh5aWVsZCl+ZGVuc2l0eSAsIGRhdGE9T25pb25zKQ0KcGxvdChsb2coeWllbGQpfmRlbnNpdHkgLCBkYXRhPU9uaW9ucykNCnBsb3QocmVzaWR1YWxzKE9uaW9ucy5sbSl+IGRlbnNpdHksIGRhdGE9T25pb25zKQ0KYGBgDQoNCmBgYHtyIE9uaW9uc1hUcmFuc2Zvcm19DQpPbmlvbnMubG0gPSBsbSh5aWVsZH5sb2coZGVuc2l0eSkgLCBkYXRhPU9uaW9ucykNCnBsb3QoeWllbGR+bG9nKGRlbnNpdHkpICwgZGF0YT1PbmlvbnMpDQpwbG90KHJlc2lkdWFscyhPbmlvbnMubG0pfiBsb2coZGVuc2l0eSksIGRhdGE9T25pb25zKQ0KYGBgDQoNCkEgcmVjaXByb2NhbCB0cmFuc2Zvcm1hdGlvbiBvZiAqeSogc2VlbXMgdG8gZ2l2ZSB1cyBhIHN0cmFpZ2h0IGxpbmUsIGJ1dCB0aGUgcmVzaWR1YWxzIHNob3cgaGV0ZXJvc2NlZGFzdGljaXR5IC0gdGhlIGNsdXN0ZXIgb2YgcG9pbnRzIG9uIHRoZSAgbGVmdCBvZiB0aGUgcmVzaWR1YWwgcGxvdCAoZGVuc2l0eTw1KSBoYXZlIG11Y2ggc21hbGxlciAodmVydGljYWwpIHNwcmVhZCBvZiByZXNpZHVhbHMuDQoNCmBgYHtyIE9uaW9uc1JlY2lwcm9jYWxUcmFuc2Zvcm1zfQ0KcGFyKG1mcm93PWMoMSwyKSkNCk9uaW9ucy5sbSA9IGxtKDEveWllbGR+ZGVuc2l0eSAsIGRhdGE9T25pb25zKQ0KcGxvdCgxL3lpZWxkfmRlbnNpdHkgLCBkYXRhPU9uaW9ucykNCnBsb3QocmVzaWR1YWxzKE9uaW9ucy5sbSl+IGRlbnNpdHksIGRhdGE9T25pb25zKQ0KYGBgIA0KDQoNClRyYW5zZm9ybWluZyBib3RoICp4KiBhbmQgKnkqIGRvZXMgdGhlIGpvYi4gVGhlIGN1cnZhdHVyZSBpcyBmaXhlZCBhbmQgdGhlIHJlc2lkdWFscywgdGhvdWdoIG5vdCBwZXJmZWN0LCBhcmUgbW9yZSBldmVuLiAgDQoNCmBgYHtyIE9uaW9ucyBib3RoIHRyYW5zZm9ybX0NCnBhcihtZnJvdz1jKDEsMikpDQpPbmlvbnMubG0gPSBsbShsb2coeWllbGQpfmxvZyhkZW5zaXR5ICksIGRhdGE9T25pb25zKQ0KcGxvdChsb2coeWllbGQpfmxvZyhkZW5zaXR5KSAsIGRhdGE9T25pb25zKQ0KcGxvdChyZXNpZHVhbHMoT25pb25zLmxtKX4gbG9nKGRlbnNpdHkpLCBkYXRhPU9uaW9ucykNCmBgYA0KDQojIyBJbnRlcnByZXRpbmcgZGlmZmVyZW50IHRyYW5zZm9ybWF0aW9ucw0KDQpTdXBwb3NlICAgJCQgeSAgPSBBIHheYiBcZXRhICQkDQp3aGVyZSAkXGV0YSQgcmVmZXIgdG8gbXVsdGlwbGljYXRpdmUgZXJyb3JzLiAgIFRoaXMgZm9ybXVsYSBkZXNjcmliZXMgYSBsb3Qgb2YgcGhlbm9tZW5hIHdpdGggcmFwaWQgZ3Jvd3RoIChiPjEpLCAgZGltaW5pc2hpbmctcmV0dXJucyBncm93dGggICgkMDwgYjwxJCksICBvciByZWNpcHJvY2FsLXR5cGUgZGVjYXkgJGIgPDAkLiAgDQoNClRha2luZyBsb2dzIG9mIGJvdGggc2lkZXMgb2YgdGhlIGVxdWF0aW9uIGdpdmVzICAkJGxvZyh5KSA9IGxvZyhBKSArIGIgbG9nKHgpICsgbG9nKFxldGEpJCQNCndoaWNoIHdlIGNhbiByZW5hbWUgYXMgICAkJFlfe25ld30gPSBcYmV0YV8wICsgXGJldGFfMSBYX3tuZXd9ICsgXHZhcmVwc2lsb24kJA0KDQogZm9yICRcYmV0YV8wPSBsb2coQSkkLCAgJFxiZXRhXzE9YiQsIGFuZCAkXHZhcmVwc2lsb249IGxvZyhcZXRhKSQuDQogDQpTbyBpZiB3ZSBwbG90ICRsb2coeSkkIGFnYWluc3QgJGxvZyh4KSQgYW5kIGdldCBhIHN0cmFpZ2h0IGxpbmUgd2l0aCBjb25zdGFudCBzcHJlYWQgb2YgZXJyb3JzLCAgd2UgY2FuIGNvbmNsdWRlIHRoYXQgdGhlIG1vZGVsICR5ID0gQSB4XmIgXGV0YSQgd2FzIGNvcnJlY3QuICBXZSBjYW4gcHJlZGljdCB0aGUgdmFsdWVzIG9mICR5JCBieSBiYWNrLXRyYW5zZm9ybWluZyB0aGUgcHJlZGljdGVkIHZhbHVlczsgICAkJFxoYXQgeSA9ICBlXntcaGF0IFlfe25ld319ICA9IFxleHAoXGhhdCBZX3tuZXd9KS4kJCAgIFRoZSByZXNpZHVhbHMgaW4gdGhlIG9yaWdpbmFsIHNjYWxlIGFyZSAgJGUgPSB5IC0gXGhhdCB5JC4gDQogDQoNClNpbWlsYXJseSBpZiAkJCB5ID0gQyBlXnsgYnh9XGV0YSAgPSAgZV57IGErIGJ4ICsgXHZhcmVwc2lsb259ICQkDQooRXhwb25lbnRpYWwgZ3Jvd3RoIG9yIGRlY2F5KSB0aGVuIGEgcGxvdCBvZiAkbG9nKHkpJCBhZ2FpbnN0ICR4JCBzaG91bGQgeWllbGQgYSBzdHJhaWdodCBsaW5lIHdpdGggY29uc3RhbnQgc3ByZWFkIG9mIGVycm9ycy4gDQoNClN1cHBvc2Ugb24gdGhlIG90aGVyIGhhbmQgd2UgaGF2ZSBhIG1vZGVsICQkXHNxcnQgeSAgPSBcYmV0YV8wICsgXGJldGFfMSB4ICsgXHZhcmVwc2lsb24gIH5+LiQkIA0KVGhlIHByZWRpY3RlZCB2YWx1ZXMgb2YgJHkkICBhcmUgICQkXGhhdCB5ID0gXGxlZnQoIFxoYXRcYmV0YV8wICsgXGhhdFxiZXRhXzEgeFxyaWdodCleMiQkDQphbmQgd2UgY2FuIGNhbGN1bGF0ZSB0aGUgcmVzaWR1YWxzIGluIHRoZSBvcmlnaW5hbCBzY2FsZSBhcyAkZSA9IHkgLSBcaGF0IHkkLiAoT2YgY291cnNlIGlmICB0aGUgZXJyb3JzIGFyZSBub3JtYWwgb24gdGhlICRcc3FydCB5JCBzY2FsZSB0aGV5IHdpbGwgbm90IGJlIG5vcm1hbCBvbiB0aGUgb3JkaW5hcnkgJHkkLXNjYWxlLiApDQoNCiMjIGxvZyB0cmFuc2Zvcm0gYXMgYSBtb2RlbCBmb3IgcGVyY2VudGFnZSBjaGFuZ2UNCg0KV2UgY29uc2lkZXIgdGhlIGZvbGxvd2luZyBkYXRhIG9uIHRoZSBudW1iZXIgb2Ygc3Vic2NyaWJlcnMgdG8gY2VsbHVsYXIgcGhvbmVzIGluIHRoZSBVU0EsIGZyb20gdGhlIG1pZC0xOTgwcy4gDQpUaGUgbnVtYmVyIG9mIHN1YnNjcmliZXJzIGlzICBnaXZlbiBhdCA2LW1vbnRobHkgaW50ZXJ2YWxzIChzZWUgdGhlIHZhcmlhYmxlIOKAnFBlcmlvZOKAnSkuICAgICBBIHBsb3Qgb2YgICpZKj1TdWJzY3JpYmVycyB2ZXJzdXMgKngqPVBlcmlvZCAgZ2l2ZXMgYSBjdXJ2ZS4gICBOb3RlIHRoaXMgaXMgY2xlYXJseSBhIHRpbWUgc2VyaWVzLCBhbmQgc28gYWRqYWNlbnQgdGltZSBwb2ludHMgd2lsbCBub3QgYmUgaW5kZXBlbmRlbnQsIHdoaWNoIHZpb2xhdGVzIG9uZSBvZiB0aGUgYXNzdW1wdGlvbnMgb2YgcmVncmVzc2lvbiBtb2RlbGxpbmcuICBIb3dldmVyIHdlIHdpbGwgaWdub3JlIHRoaXMgZmFjdCBmb3IgdGhlIG1vbWVudCwgc28gd2UgY2FuIHNlZSB3aGF0IGhhcHBlbnMuIA0KDQpgYGB7ciByZWFkIENlbGx1bGFyfQ0KQ2VsbHVsYXIgPSByZWFkLmNzdihmaWxlID0gIi4uLy4uL2RhdGEvQ2VsbHVsYXIuY3N2IikNCmhlYWQoQ2VsbHVsYXIpDQpwbG90KFN1YnNjcmliZXJzIH4gUGVyaW9kLCBkYXRhPUNlbGx1bGFyKQ0KYGBgDQoNClRoaXMgbG9va3MgbGlrZSBhbiBleHBvbmVudGlhbCBncm93dGggbW9kZWwgDQokJEVbIFN1YnNjcmliZXJzXSA9IFNfMCBlXntcYmV0YV8xXHRpbWVzIFBlcmlvZCB9ICQkDQoNCndoZXJlICRTXzAkIGlzIHRoZSBudW1iZXIgb2Ygc3Vic2NyaWJlcnMgYXQgJFBlcmlvZD0wJA0KYW5kICRcYmV0YV8xJCBpcyBhIGdyb3d0aCByYXRlIGluZGljYXRpbmcgaG93IGZhc3QgdGhlIG51bWJlciBvZiBzdWJzY3JpYmVycyBpcyBncm93aW5nIGluIHBvd2VycyBvZiAkZT0gMi43MTgyOCQuICAgRm9yIGV4YW1wbGUgaWYgJFxiZXRhXzEgPSAwLjAyJCB0aGVuIHRoZSBudW1iZXIgb2Ygc3Vic2NyaWJlcnMgaXMgZXhwZWN0ZWQgdG8gZ3JvdyB0byAkZV57MC4wMn09JCBgciByb3VuZChleHAoMC4wMiksNClgIHRoZSBzaXplIGl0IHdhcyBiZWZvcmUsIGkuZS4gYSBgciByb3VuZCgoZXhwKDAuMDIpLTEpKjEwMCwgMilgJQ0KaW5jcmVhc2UuIA0KDQpOb3cgd2UgdHJ5IGEgbG9nIHRyYW5zZm9ybWF0aW9uLiBJZiBteSBndWVzcyBvZiB0aGUgbW9kZWwgIGlzIGNvcnJlY3QsICB0aGVuICBhICBwbG90IG9mICpsb2coWSkqICB2ZXJzdXMgICp4KiAgc2hvdWxkIGdpdmUgYSBzdHJhaWdodCBsaW5lLiBJbiBmYWN0LCB0aGUgcmV2ZXJzZSBhcmd1bWVudCBpcyBhbHNvIHRydWUgOiBpZiB3ZSBnZXQgYSBzdHJhaWdodCBsaW5lLCB0aGVuIHdlIGtub3cgdGhlIGV4cG9uZW50aWFsIGdyb3d0aCBjdXJ2ZSB3YXMgY29ycmVjdC4NCg0KYGBge3IgZmlyc3QgbG9nIHBsb3R9DQpwbG90KGxvZyhTdWJzY3JpYmVycyl+IFBlcmlvZCwgZGF0YT1DZWxsdWxhcikNCmBgYA0KDQpPSyBzbyBjbGVhcmx5IGl0IGlzIG5vdCBhIHN0cmFpZ2h0IGxpbmUsIGJ1dCByZWZsZWN0cyBkaWZmZXJlbnQgcGFydHMgb2YgYSBwcm9kdWN0cyBtYXJrZXQgY3ljbGU6IGluaXRpYWxseSBmZXcgcGVvcGxlIGhhZCBjZWxsIHBob25lcyBzbyBhbnkgaW5jcmVhc2Ugc2VlbWVkIHJhcGlkLiBCdXQgbGF0ZXIsIGFmdGVyIGFib3V0ICRQZXJpb2Q9MTAkIHRoZSBtYXJrZXQgZm9yIGNlbGwgcGhvbmVzIHdhcyBtYXR1cmluZyBhbmQgdGhlIGluY3JlYXNlIHNlZW1zIGFib3V0IGxpbmVhci4gDQoNClNpbmNlIGl0IGlzIG1vcmUgaW1wb3J0YW50IHRvIHByZWRpY3QgdGhlIGZ1dHVyZSB0aGFuIHRoZSBwYXN0KCEpIGxldCdzIGRyb3AgdGhlIGVhcmx5IGRhdGEgYW5kIHNlZSBpZiB3ZSBnZXQgYSBnb29kIG1vZGVsIGZvciAkUGVyaW9kXGdlIDEwJC4NCg0KYGBge3IgU2Vjb25kIGxvZyBwbG90fQ0KQ2VsbHVsYXIyID0gQ2VsbHVsYXIgfD4gZmlsdGVyKFBlcmlvZD45KQ0KQ2VsbHVsYXIyLmxtID0gbG0obG9nKFN1YnNjcmliZXJzKX5QZXJpb2QsIGRhdGE9Q2VsbHVsYXIyICkNCnBsb3QocmVzaWR1YWxzKENlbGx1bGFyMi5sbSkgflBlcmlvZCAsIGRhdGE9Q2VsbHVsYXIyKQ0KYGBgDQoNClRoZSBwb2ludHMgbm93IGxvb2sgY2xvc2UgdG8gYSAgbGluZSwgZXhjZXB0IHRoZSBmaXJzdCBvbmUuIFRoaXMgcG9pbnQgYWxzbyBzdGFuZHMgb3V0IGFtb25nIHRoZSByZXNpZHVhbHMuICBXZSB3aWxsIHRyeSBkZWxldGluZyB0aGlzIG9uZSBtb3JlIHBvaW50LiAgDQoNCmBgYHtyIFRoaXJkIGxvZyBwbG90fQ0KQ2VsbHVsYXIzID0gQ2VsbHVsYXIgfD4gZmlsdGVyKFBlcmlvZD4xMCkNCkNlbGx1bGFyMy5sbSA9IGxtKGxvZyhTdWJzY3JpYmVycyl+UGVyaW9kLCBkYXRhPUNlbGx1bGFyMykNCnBsb3QocmVzaWR1YWxzKENlbGx1bGFyMy5sbSkgflBlcmlvZCAsIGRhdGE9Q2VsbHVsYXIzKQ0KYGBgDQoNClRoZSByZXNpZHVhbHMgbm93IGxvb2sgcGF0dGVybmxlc3MsIHNvIHdlIHdpbGwgZGVjbGFyZSBpdCBhIGdvb2QgbW9kZWwuIFRoZSBsaW5lIGlzICQkXGhhdCB7bG9nKHkpfSA9IGV4cCgxMy4wMjA4OTcgICsgIDAuMTg3OTA4IHgpID0gIDQ1MTc1NS44IGVeezAuMTg3OTA4IHh9ICAkJA0KDQpTdWJzdGl0dXRpbmcgeCA9IDIzLCAgKHRoZSBsYXN0IHBlcmlvZCBpbiB0aGUgZGF0YXNldCkgIHRoZSBmaXR0ZWQgbnVtYmVyIG9mIHN1YnNjcmliZXJzIHdhcyAgMzQwMzA4NjYgIChvbmx5IGEgIGByIHJvdW5kKDEwMCooMzQwMzA4NjYvMzM3ODU2NjEtMSkgLDIpYCUgZGlmZmVyZW5jZSBmcm9tIHRoZSBvYnNlcnZlZCB2YWx1ZSBvZiAzMzc4NTY2MSkuICANCg0KVG8gcHJlZGljdCBmb3IgdGhlIG5leHQgdGltZSBwZXJpb2QsIHdlIHdvdWxkIHN1YnN0aXR1dGUgeD0yNC4gDQpQdXR0aW5nIGl0IGFub3RoZXIgd2F5LCBzaW5jZSAgDQokJFxoYXQgWV97MjR9ID0gICA0NTE3NTUuOCBlXnswLjE4NzkwOFx0aW1lcyAoMjMgKzEpfSA9IFxoYXQgWV97MjN9IFx0aW1lcyAxLjIwNjcyMiAkJCAgDQp3ZSBjYW4gc3VtbWFyaXplIGJ5IHNheWluZyB0aGF0IHdlIGV4cGVjdCBhIDIwLjclIGdyb3d0aCBpbiB0aGUgbnVtYmVyIG9mIGNlbGwgcGhvbmUgc3Vic2NyaWJlcnMgaW4gdGhlIG5leHQgNiBtb250aHMuIA0KDQpUaGlzIGNhbGN1bGF0aW9uIGhpZ2hsaWdodHMgdGhlIGZvbGxvd2luZyBpbXBvcnRhbnQgZmFjdDogKiphIGxpbmVhciBtb2RlbCBmb3IgbG9nKFkpICBjb3JyZXNwb25kcyB0byBhIG1vZGVsIGZvciBwZXJjZW50YWdlIGdyb3d0aCAgKG9yIHBlcmNlbnRhZ2UgZGVjbGluZSkgKioNCg0KVGhpcyBpcyBhIG1ham9yIHJlYXNvbiB3aHkgdGhlIGxvZyB0cmFuc2Zvcm0gaXMgdXNlZCBhIGxvdCBpbiBlY29ub21pYyBkYXRhLCBhcyBzbyBtYW55IG1lYXN1cmVzIG9mIGdyb3d0aCBhcmUgZ2l2ZW4gaW4gcGVyY2VudGFnZSB0ZXJtcyAoZS5nLiBpbnRlcmVzdCByYXRlcykuICANCg0KIyMgVHJhbnNmb3JtYXRpb25zIGZvciBDb3VudCBkYXRhIChFeHRlbnNpb24gd29yaykNCg0KVGhpcyBzZWN0aW9uIGxvb2tzIGF0IFRyYW5zZm9ybWF0aW9uIFRvIENvbnN0YW50IFZhcmlhbmNlIChDb3JyZWN0aW5nIEhldGVyb3NjZWRhc3RpY2l0eSkNCg0KV2UgaGF2ZSBzZWVuIHRoYXQgaGV0ZXJvc2NlZGFzdGljaXR5ICh1bmVxdWFsIHNwcmVhZCkgY2FuIGFyaXNlIGJlY2F1c2UgdGhlIGVycm9ycyAgb2NjdXIgb24gYSBkaWZmZXJlbnQgc2NhbGUgIHRvIHRoZSBvbmUgd2UgaGF2ZSBtZWFzdXJlZCBvbiAoZS5nLiB0aGUgcmVzcG9uc2Ugc2hvdWxkIGJlIG1lYXN1cmVkIG9uIGEgbG9nIHNjYWxlIGFuZCBpZiB3ZSBkbyB0aGF0IHRoZW4gbXVsdGlwbGljYXRpdmUgZXJyb3JzIGJlY29tZSBjb25zdGFudC4pICAgQnV0IHRoaXMgaXMgbm90IHRoZSBvbmx5IHBvdGVudGlhbCBzb3VyY2Ugb2YgaGV0ZXJvc2NlZGFzdGljaXR5Lg0KICANCiMjIyBCaW5vbWlhbCBEYXRhDQoNClN1cHBvc2Ugb3VyIGRhdGEgYXJlIHRoZSBudW1iZXIgb2YgIOKAmHN1Y2Nlc3Nlc+KAmSAgaW4gYSBzZXQgb2YgICBuIGluZGVwZW5kZW50IHRyaWFscy4gIEZvciBleGFtcGxlICpZKiBtaWdodCBiZSB0aGUgbnVtYmVyIG9mIHBlb3BsZSBhbnN3ZXJpbmcg4oCceWVz4oCdIHRvIGEgcXVlc3Rpb24sIG9yIHRoZSBudW1iZXIgb2Ygc3RvY2tzIG9mIGEgY2VydGFpbiB0eXBlIHRoYXQgc2hvdyBhIHJpc2UgaW4gcmVzcG9uc2UgdG8gYSBuZXdzIGV2ZW50LiAgIFdlIGNhbGwgc3VjaCBkYXRhICDigJxCaW5vbWlhbOKAnSAuICAgICBUaGUgYXNzdW1wdGlvbiBvZiBpbmRlcGVuZGVudCB0cmlhbHMgcmVmZXJzIHRvIHRoZSAgIGlkZWEgdGhhdCB0aGUgb3V0Y29tZSBmcm9tIG9uZSBwZXJzb24gKG9yIG9uZSBzdG9jaykgZG9lcyBub3QgaW4gaXRzZWxmIGFmZmVjdCB0aGUgb3V0Y29tZSBmcm9tIGFueSBvdGhlciBwZXJzb24gKG9yIHN0b2NrKS4NCkl0IHR1cm5zIG91dCB0aGF0IHNvbWUgbWF0aGVtYXRpY2FsIHRoZW9yeSBzdWdnZXN0cyB3ZSB0cmFuc2Zvcm0gdXNpbmcgDQokJCBZX3tuZXd9ID0gc2luXnstMX0oXHNxcnQgeSApJCQNCg0KSGVyZSAgJHNpbl57LTF9JCAgIGlzIHRoZSBpbnZlcnNlIHNpbmUgdHJhbnNmb3JtYXRpb24gIChvciBhcmNzaW5lKSAgdGhhdCB5b3UgbWF5ICByZW1lbWJlciBmcm9tICB0cmlnb25vbWV0cnkgIGluIHlvdXIgaGlnaC1zY2hvb2wgbWF0aHMgY291cnNlLiAgIA0KDQpUaGlzIGFwcHJvYWNoIGhhcyBmYWxsZW4gb3V0IG9mIGZhc2hpb24gaW4gdGhlIDIxXnN0XiBjZW50dXJ5IGJlY2F1c2Ugb2YgZGV2ZWxvcG1lbnQgb2YgbWV0aG9kcyBjYWxsZWQgImdlbmVyYWxpemVkIGxpbmVhciBtb2RlbHMiIHRoYXQgd29yayB3aXRoIEJpbm9taWFsIG1vZGVscyBkaXJlY3RseS4gDQoNCiMjIyBQb2lzc29uIERhdGEgICAgICAgICAgDQoNClRoaXMgaXMgYW5vdGhlciBtYXRoZW1hdGljYWwgbW9kZWwgdXNlZCBmb3IgZGF0YSBjb25zaXN0aW5nIG9mIGNvdW50cywgIGJ1dCBpbiB0aGlzIGNhc2Ugd2hlcmUgdGhlcmUgaXMgbm8gZml4ZWQgdXBwZXIgbGltaXQgb24gdGhlIG51bWJlciBvZiByZXNwb25zZXMgb25lIG1pZ2h0IG9idGFpbiAgKGNvbnRyYXN0IHRvIHRoZSBCaW5vbWlhbCwgd2hlcmUgdGhlICRZIFxsZSBuJCkuICANCg0KRm9yIGV4YW1wbGUgd2UgbWF5IGJlIGNvdW50aW5nIHRoZSBudW1iZXIgb2YgY3VzdG9tZXJzIHJlc3BvbmRpbmcgdG8gYW4gYWR2ZXJ0aXNlbWVudC4gVGhlbiBmb3IgcHJhY3RpY2FsIHB1cnBvc2VzIHdlIGNhbm5vdCBzdGlwdWxhdGUgYSBtYXhpbXVtICpZKiAob3IgYXQgbGVhc3Qgb25seSBhIHZlcnkgbGFyZ2UgbWF4aW11bSwgIHN1Y2ggYXMgdGhlIHBvcHVsYXRpb24gb2YgYSBjb3VudHJ5LCBhbmQgd2UgZG9u4oCZdCBleHBlY3QgdGhlIGRhdGEgdG8gZ28gYW55d2hlcmUgbmVhciB0aGF0IHZhbHVlLiApDQoNCkl0IGlzIGFnYWluIGFzc3VtZWQgdGhhdCB0aGUgZXZlbnRzIGNvdW50ZWQgaGFwcGVuIGluZGVwZW5kZW50bHkgb2YgZWFjaCBvdGhlciAgKGUuZy4gcGVvcGxlIG1ha2UgdXAgdGhlaXIgb3duIG1pbmRzIHdoZXRoZXIgb3Igbm90IHRvIGJlIGN1c3RvbWVycykuIA0KDQpBIG1hdGhlbWF0aWNhbCAgIGNoYXJhY3RlcmlzdGljIG9mIFBvaXNzb24gZGF0YSBpcyB0aGF0IHRoZSBtZWFuIGFuZCB2YXJpYW5jZSBhcmUgZXF1YWwuICAgIFdlIHdyaXRlIHRoaXMgYXMgICAkJCAgRVtZXSA9ICBcbXUgPSBcbWJveHtWYXJpYW5jZX0oWSkgfn4sJCQgDQp3aGVyZSRcbXUkIGlzIHRoZSBhdmVyYWdlIHJhdGUgb2YgZXZlbnRzIHBlciB1bml0IHRpbWUgICAoZS5nLiBjdXN0b21lcnMgcGVyIGRheSkuICAgICAgDQpQdXR0aW5nIGl0IGluIHRlcm1zIG9mIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgWSwgICRzZChZKSA9IFxzcXJ0IFkkLiANCg0KIFNvbWUgbWF0aGVtYXRpY2FsIHRoZW9yeSAgc3VnZ2VzdHMgdGhhdCB3ZSBzaG91bGQgYmUgYWJsZSB0byBzdGFiaWxpc2UgdGhlIHZhcmlhbmNlICBvZiBQb2lzc29uIGRhdGEgIGJ5IGFuYWx5c2luZyB0aGUgdHJhbnNmb3JtZWQgZGF0YSAgJFlfe25ld30gPSBcc3FydCBZJCAgKG9yICRcc3FydHtZKzF9JCAgaWYgdGhlIFkgdmFsdWVzIGFyZSBzbWFsbCkuIFRoZSBsYXR0ZXIgd2FzIHRoZSB0cmFuc2Zvcm1hdGlvbiB1c2VkIHRvIGZpdCB0aGUgZGF0YSBvbiBtb3VudGFpbi1jbGltYmluZyBmYXRhbGl0aWVzIG9uIE10IEV2ZXJlc3QsIGluIFRvcGljIDA4LiANCg0KIyMjICBOZWdhdGl2ZSBCaW5vbWlhbCAgIA0KDQpJZiAgdGhlIGRhdGEgYXJlIHRoZSByZXN1bHQgb2YgY291bnRpbmcgZXZlbnRzIHdoaWNoIGFyZSBub3QgaW5kZXBlbmRlbnQsICBidXQgZXhoaWJpdCBjbHVzdGVyaW5nIChzbyB0aGF0IHRoZSByYXRlIGF0IHdoaWNoIHRoZSByYW5kb20gZXZlbnRzIG9jY3VyIHNlZW1zIHRvIHZhcnkgZnJvbSB0aW1lIHRvIHRpbWUpLCAgdGhlbiBhIG1hdGhlbWF0aWNhbCBtb2RlbCB0aGF0IG1heSBiZSBzdWl0YWJsZSBpcyBjYWxsZWQgdGhlIE5lZ2F0aXZlIEJpbm9taWFsIGRpc3RyaWJ1dGlvbi4gICANCg0KRm9yIHRoaXMgdHlwZSBvZiBkYXRhLCBzb21ldGltZXMgY2FsbGVkIOKAnGNsdXN0ZXJlZCBQb2lzc29u4oCdLCAgdGhlIHZhcmlhbmNlIHRlbmRzIHRvIGJlIGJpZ2dlciB0aGFuIHRoZSBtZWFuICAoJFxtdTxcc2lnbWFeMiQpLiAgQSBjb21tb24gIHByYWN0aWNlIGlzIHRvIHRyYW5zZm9ybSB0aGUgcmVzcG9uc2UgdG8gICRZX3tuZXd9ID0gbG9nKDErWSkkICAgb3Igc29tZXRpbWVzICRsb2coMC41K1kpJC4NCg0KIyMjIEV4YW1wbGU6IEhvdyBtYW55IG1hZ2F6aW5lcyBkbyB5b3UgcmVhZD8gDQoNCldlIGNvbnNpZGVyIHNvbWUgZGF0YSBvbiB0aGUgbnVtYmVyIG9mIG1hZ2F6aW5lcyByZWFkIGJ5IGEgc2FtcGxlIG9mIE5ldyBaZWFsYW5kZXJzLCB2ZXJzdXMgdGhlaXIgZWR1Y2F0aW9uIGxldmVsICh2YXJpYWJsZSDigJxFZHVjZ3Jw4oCdKS4gIFRoZSBkYXRhIHdlcmUgZ2VuZXJvdXNseSBwcm92aWRlZCBieSBhIG1hcmtldCByZXNlYXJjaCBjb21wYW55LCAgQUdCIE1jTmFpci4gIFJhbmRvbWx5IHNlbGVjdGVkIHJlc3BvbmRlbnRzIHdlcmUgc2hvd24gdGhlIGNvdmVycyBvZiBhIG51bWJlciBvZiBwb3B1bGFyIG1hZ2F6aW5lcyAoZS5nLiBXb21lbuKAmXMgV2Vla2x5KSBhbmQgd2VyZSBhc2tlZCB3aGV0aGVyIHRoZXkgaGFkIHJlYWQgdGhvc2UgaXNzdWVzLiAgV2UgZGVhbCBoZXJlIHdpdGggdGhlIHRvdGFsIG51bWJlciBvZiBtYWdhemluZXMgcmVhZC4gIEVkdWNncnAgaXMgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSwgd2l0aCBsZXZlbHMNCg0KLSAxPSBQcmltYXJ5IFNjaG9vbCBvbmx5DQoNCi0gMj0gU2Vjb25kYXJ5IFNjaG9vbCBidXQgbm8gTkNFQSAgb3IgU2Nob29sIENlcnRpZmljYXRlDQoNCi0gMz0gTkNFQSBsZXZlbCAxIC8gU2Nob29sIENlcnRpZmljYXRlDQoNCi0gND1OQ0VBIGxldmVsIDIgb3IgMyAvIFVuaXZlcnNpdHkgRW50cmFuY2Ugb3IgQnVyc2FyeQ0KDQotIDU9VGVjaG5pY2FsIG9yIFRyYWRlIFF1YWxpZmljYXRpb24gCQ0KDQotIDY9T3RoZXIgVGVydGlhcnkgKG5vdCB1bml2ZXJzaXR5KQ0KDQotIDc9VW5pdmVyc2l0eSBRdWFsaWZpY2F0aW9uCQ0KDQotIDg9U3R1ZGllZCBhdCBVbml2ZXJzaXR5IGJ1dCBub3QgY29tcGxldGVkDQoNClRoZSBmb2xsb3dpbmcgQm94cGxvdCBzaG93cyB0aGUgIG51bWJlciBvZiBtYWdhemluZXMgcmVhZCBmb3IgZWFjaCBsZXZlbCBvZiBFZHVjZ3JwLCB3aXRoIGVycm9yIGJvdW5kcy4gIFRoZSBtZWRpYW5zIHNlZW0gdG8gc2hvdyBhIHdlYWsgdXB3YXJkIHRyZW5kLCBidXQgdGhlIGdyYXBoIGlzIHN3YW1wZWQgYnkgc28gbWFueSBvdXRsaWVycy4NCg0KYGBge3IgUkVBRCBkYXRhfQ0KUmVhZD0gcmVhZC5jc3YoIGZpbGU9Ii4uLy4uL2RhdGEvUkVBRC5jc3YiLGhlYWRlcj1UUlVFKQ0KaGVhZChSZWFkKQ0KYm94cGxvdChNYWdhemluZXN+IEVkdWNncnAsIGRhdGE9UmVhZCkNCmBgYA0KDQpTaW5jZSB0aGUgbnVtYmVyIG9mIG1hZ2F6aW5lcyBpcyBhIGNvdW50LCB3ZSBjYW4gdHJ5IHRoZSAkXHNxcnQgWSQgYW5kICRsb2coMStZKSQgdHJhbnNmb3JtYXRpb25zLiAgTm90ZSB3ZSBuZWVkIHRoZSAqMSsqIGJlY2F1c2UgbWFueSBwZW9wbGUgcmVhZCB6ZXJvIG1hZ2F6aW5lcywgYW5kIHdlIGNhbm5vdCB0YWtlICRsb2coMCkkLiANCg0KYGBge3IgUkVBRCB0cmFuc2Zvcm1lZH0NCmJveHBsb3Qoc3FydChNYWdhemluZXMpfiBFZHVjZ3JwLCBkYXRhPVJlYWQpDQpib3hwbG90KGxvZygxK01hZ2F6aW5lcyl+IEVkdWNncnAsIGRhdGE9UmVhZCkNCmBgYA0KDQpJdCBsb29rcyBhcyBpZiBlaXRoZXIgdHJhbnNmb3JtYXRpb24gd291bGQgZG8sIGJ1dCB0aGUgJGxvZygxK1kpJCB2ZXJzaW9uIGlzIHNsaWdodGx5IG1vcmUgc3ltbWV0cmljYWwuIA0KDQpUaGUgcGF0dGVybiBvZiBtZWRpYW5zIHN1Z2dlc3RzIHdlIGNvdWxkIG1vZGVsIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBudW1iZXIgb2YgbG9nKDErbWFnYXppbmVzKSBhbmQgZWR1Y2F0aW9uIGxldmVsIHVzaW5nIGEgbm9ubGluZWFyIHJlbGF0aW9uc2hpcCwgY292ZXJlZCBpbiBhbm90aGVyIGxlY3R1cmUuDQoNClRoZSBjb3Vyc2UgMTYxLjMzMSBzaG93cyBob3cgdG8gbW9kZWwgUG9pc3NvbiwgTmVnYXRpdmUgQmlub21pYWwsIGFuZCBvdGhlciB0eXBlcyBvZiBkYXRhIHVzaW5nIEdlbmVyYWxpemVkIExpbmVhciBNb2RlbHMuDQoNCg0K