View the
latest recording of this lecture
Sometimes we need to specify a different line or curve for different
ranges of our predictor variable. Such a model is called
piecewise.
the Indianapolis 500 data
This data has the winning speed (an average over 500 miles) for a
very famous motor race. This is not a complete series; the race is held
in almost every year, with some notable exceptions.
Download Indianap500.csv
## Indy = read.csv("Indianap500.csv", header = TRUE)
plot(Speed ~ Year, data = Indy)
In this example there appear to be three separate line segments, with
possibly different slopes before World War I, between the two wars, and
after World War II. (The race was not run while the USA was active in
the world wars.)
Allowing for changes in slope
The change points are called *knots, usually we
insist that the lines join up at the knots (no discontinuities) but each
context will have to be judged on its merits.
A model with a single change in slope at knot \(\kappa\) would have equation \[ y = \beta_0 + \beta_1x + \beta_2 (x-\kappa)_+ +
\varepsilon\]
where the term \((x-\kappa)_+\) is
defined as: \(x-\kappa\) if \(x>\kappa\); and 0 if \(x \le \kappa\).
This can be calculated in R as ifelse(x<k, 0, x-k)
read as if x is less than k, then use zero, else use
x-k.
If there are two knots, \(\kappa_1\)
and \(\kappa_2\), the same code can be
used multiple times.
In the Indianapolis data we have gaps in the years due to the races
being cancelled in 1917-18 and 1942-45 due to the USA participating in
the wars. We will use the midpoints of the missing years as the knots:
\(\kappa_1 = 1917.5\) and \(\kappa_2= 1943.5\). Thus we fit the
model
\[ y = \beta_0 + \beta_1 x + \beta_2
(x-\kappa_1)_+ + \beta_3 (x-\kappa_2)_+ + \varepsilon\]
Knot1 = 1917.5
Knot2 = 1943.5
Indy |>
mutate(t2 = ifelse(Year < Knot1, 0, Year - Knot1), t3 = ifelse(Year < Knot2,
0, Year - Knot2)) -> Indy
Indy |>
head() |>
kable()
74.602 |
1911 |
0 |
0 |
0 |
0 |
78.719 |
1912 |
0 |
0 |
0 |
0 |
75.933 |
1913 |
0 |
0 |
0 |
0 |
82.474 |
1914 |
0 |
0 |
0 |
0 |
89.840 |
1915 |
0 |
0 |
0 |
0 |
84.001 |
1916 |
0 |
0 |
0 |
0 |
Indy |>
tail() |>
kable()
50 |
144.317 |
1966 |
48.5 |
22.5 |
1 |
1 |
51 |
151.207 |
1967 |
49.5 |
23.5 |
1 |
1 |
52 |
152.882 |
1968 |
50.5 |
24.5 |
1 |
1 |
53 |
156.867 |
1969 |
51.5 |
25.5 |
1 |
1 |
54 |
155.749 |
1970 |
52.5 |
26.5 |
1 |
1 |
55 |
157.735 |
1971 |
53.5 |
27.5 |
1 |
1 |
Indy.pce0 <- lm(Speed ~ Year, data = Indy)
Indy.pce1 <- lm(Speed ~ Year + t2 + t3, data = Indy)
summary(Indy.pce1)
Call:
lm(formula = Speed ~ Year + t2 + t3, data = Indy)
Residuals:
Min 1Q Median 3Q Max
-5.6072 -2.0465 -0.3068 1.5065 7.8508
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -3723.0220 700.5807 -5.314 2.38e-06 ***
Year 1.9878 0.3657 5.435 1.55e-06 ***
t2 -0.9716 0.4070 -2.387 0.020710 *
t3 0.4690 0.1170 4.009 0.000199 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 2.978 on 51 degrees of freedom
Multiple R-squared: 0.9844, Adjusted R-squared: 0.9835
F-statistic: 1075 on 3 and 51 DF, p-value: < 2.2e-16
anova(Indy.pce0, Indy.pce1)
Analysis of Variance Table
Model 1: Speed ~ Year
Model 2: Speed ~ Year + t2 + t3
Res.Df RSS Df Sum of Sq F Pr(>F)
1 53 595.20
2 51 452.16 2 143.04 8.0668 0.0009038 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Indy |>
mutate(Fits = fitted.values(Indy.pce1)) |>
ggplot(aes(y = Speed, x = Year)) + geom_point(color = "blue") + geom_point(aes(y = Fits),
color = "red") + labs(y = "Average speed")
Note that, before WWI, the winning speed was rising by \(\approx 2\) mph per year. The second slope
coefficient is \(\approx -1\), which
modifies the previous speed. So between the two World Wars the winning
speed was rising by \(\approx 1\) mph
per year. The third slope coefficient is \(\approx 0.5\) , which means that after WWII
the winning speed was rising by \(\approx
1.5\) mph per year.
Allowing for discontinuity
The preceding analysis indicated a significant change in slope at the
two wars. The next question is: Was there a discontinuity? That is, do
the line segments have to join up at the knot point? To examine this
hypothesis, we must fit a separate intercept for the line in each of the
three segments. This can be accomplished by adding the two extra
indicator variables, const2 and const3, to the model above to allow the
intercepts, in addition to the slopes, to differ between the
segments.
Indy |>
mutate(Const2 = ifelse(Year > Knot1, 1, 0), Const3 = ifelse(Year > Knot2, 1,
0)) -> Indy
Indy.pce2 <- lm(Speed ~ Year + t2 + t3 + Const2 + Const3, data = Indy)
summary(Indy.pce2)
Call:
lm(formula = Speed ~ Year + t2 + t3 + Const2 + Const3, data = Indy)
Residuals:
Min 1Q Median 3Q Max
-6.4757 -1.5098 -0.1915 1.5079 5.5981
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -4669.9643 1177.1425 -3.967 0.000237 ***
Year 2.4828 0.6152 4.036 0.000190 ***
t2 -1.2202 0.6205 -1.967 0.054913 .
t3 0.3914 0.1052 3.720 0.000513 ***
Const2 -4.8004 2.9102 -1.650 0.105442
Const3 -7.1176 1.6596 -4.289 8.41e-05 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 2.573 on 49 degrees of freedom
Multiple R-squared: 0.9888, Adjusted R-squared: 0.9877
F-statistic: 867.5 on 5 and 49 DF, p-value: < 2.2e-16
anova(Indy.pce1, Indy.pce2)
Analysis of Variance Table
Model 1: Speed ~ Year + t2 + t3
Model 2: Speed ~ Year + t2 + t3 + Const2 + Const3
Res.Df RSS Df Sum of Sq F Pr(>F)
1 51 452.16
2 49 324.52 2 127.64 9.6366 0.0002956 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
The anova() indicates that, overall, the model is significantly
improved by allowing for discontinuity. The regression coefficients show
this is almost entirely due to the change at the second knot, not the
first.
The significant coefficient of Const2 says that, if the lines before
and after 1943.5 were extended through the period 1941-45, then there
would be a drop of 7.1 mph. In practice it looks as if Speed just
stopped rising between the wars, rather than actually dropping.
Indy$Fits = fitted.values(Indy.pce2)
Indy |>
ggplot(aes(y = Speed, x = Year)) + geom_point(color = "blue") + geom_point(aes(y = Fits),
color = "red") + labs(y = "Average speed")
Extending the piecewise approach
The approach can be extended to allow piecewise quadratic or cubic
curves.
Often the curves are constrained so that the sections are not only
continuous but smooth i.e. have continuous first derivative at the knots
(and of course everywhere else). Such curves are called ‘splines’. Curve
fitting by cubic splines is a very old technique. Various splines are
available in R, but we will not pursue them any further in this course.
In the past splines have frequently been used as a technique for
smoothing data, but we can use Lowess curves for that purpose.
It is assumed the knots \(\kappa_1\)
and \(\kappa_2\) are known and do not
have to be estimated. Sometimes this is reasonable. For example
x may represent time and \(\kappa_1\) and \(\kappa_2\) are known events eg. war, stock
market crash, etc.
If the position of the knots do have to be estimated then we have a
much more complicated statistical problem. This is an area of relatively
recent statistical research (e.g. in the last 30 years.) An example
where the knot needs to be estimated is where there is some sort of
biological tipping point, where the system responds differently to
stimuli beyond a certain (unknown) level.
One way of fitting such models is using a nonlinear regression
model.
Example: Child Lung Function Data
FEV (forced expiratory volume) is a measure of lung function. The
data include determinations of FEV on 318 female children who were seen
in a childhood respiratory disease study in Massachusetts in the U.S.
Child’s age (in years) also recorded. It is of interest to model FEV
(response) as function of age.
Download fev.csv
Data source: Tager, I. B., Weiss, S. T., Rosner, B., and Speizer, F.
E. (1979). Effect of parental cigarette smoking on pulmonary function in
children. American Journal of Epidemiology,
110, 15-26.
## Fev <- read.csv(file = "fev.csv", header = TRUE)
plot(FEV ~ Age, data = Fev)
The scatter plot of data indicates that relationship between FEV and
age is not linear. We saw how to model FEV as a polynomial in age and
decided on the fourth order polynomial model:
Fev.lm4 <- lm(FEV ~ poly(Age, 4), data = Fev)
If we plot this we find the model seems to have a strange wobble, and
steep increasing curves at both ends of the age range.
Fev |>
mutate(Fits = fitted.values(Fev.lm4)) |>
ggplot(aes(y = FEV, x = Age)) + geom_point() + geom_point(aes(y = Fits), color = "red")
Now suppose instead we fit a piecewise model. Visually it looks like
the FEV values flatten out from Age=12 onwards.
k <- 11.5
Fev = Fev |>
mutate(Age2 = ifelse(Age < k, 0, Age - k))
Fev.pce1 <- lm(FEV ~ Age + Age2, data = Fev)
summary(Fev.pce1)
Call:
lm(formula = FEV ~ Age + Age2, data = Fev)
Residuals:
Min 1Q Median 3Q Max
-1.15825 -0.28309 0.01454 0.24766 0.91692
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.20796 0.10561 1.969 0.0498 *
Age 0.24083 0.01157 20.818 <2e-16 ***
Age2 -0.23117 0.02612 -8.850 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.3905 on 315 degrees of freedom
Multiple R-squared: 0.6365, Adjusted R-squared: 0.6342
F-statistic: 275.8 on 2 and 315 DF, p-value: < 2.2e-16
Fev |>
mutate(Fits = fitted.values(Fev.pce1)) |>
ggplot(aes(y = FEV, x = Age)) + geom_point() + geom_point(aes(y = Fits), color = "red")
[1] 0.3897186
[1] 0.3905442
The residual standard error for the simple piecewise model is almost
the same as for the more complicated fourth-degree polynomial, and the
graph looks more biologically reasonable, so we would probably prefer
the piecewise model.
The outcome is that by constructing new variables, we have increased
our ability to fit meaningful and more easily interpretable models. We
haven’t even allowed for some curvature in the piecewise model, but that
will need to be left for extension work.
LS0tDQp0aXRsZTogIkxlY3R1cmUgMTg6IFBpZWNld2lzZSBSZWdyZXNzaW9uIE1vZGVscyINCnN1YnRpdGxlOiAxNjEuMjUxIFJlZ3Jlc3Npb24gTW9kZWxsaW5nDQphdXRob3I6ICJQcmVzZW50ZWQgYnkgSm9uYXRoYW4gR29kZnJleSA8YS5qLmdvZGZyZXlAbWFzc2V5LmFjLm56PiIgIA0KZGF0ZTogIldlZWsgNiBvZiBTZW1lc3RlciAyLCBgciBsdWJyaWRhdGU6OnllYXIobHVicmlkYXRlOjpub3coKSlgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0aGVtZTogeWV0aQ0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgaHRtbF9ub3RlYm9vazoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHlldGkNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogIGlvc2xpZGVzX3ByZXNlbnRhdGlvbjoNCiAgICB3aWRlc2NyZWVuOiB0cnVlDQogICAgc21hbGxlcjogdHJ1ZQ0KICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0DQogIHNsaWR5X3ByZXNlbnRhdGlvbjogDQogICAgdGhlbWU6IHlldGkNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KLS0tDQoNCg0KDQoNCg0KW1ZpZXcgdGhlIGxhdGVzdCByZWNvcmRpbmcgb2YgdGhpcyBsZWN0dXJlXShodHRwczovL1ItUmVzb3VyY2VzLm1hc3NleS5hYy5uei92aWRlb3MvMjUxTDE4Lm1wNCkNCjwhLS0tIERhdGEgaXMgb24NCmh0dHBzOi8vci1yZXNvdXJjZXMubWFzc2V5LmFjLm56L2RhdGEvMTYxMjUxLw0KLS0tPg0KDQpgYGB7ciBzZXR1cCwgcHVybD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkoa25pdHIpDQpvcHRzX2NodW5rJHNldChkZXY9YygicG5nIiwgInBkZiIpKQ0Kb3B0c19jaHVuayRzZXQoZmlnLmhlaWdodD02LCBmaWcud2lkdGg9NywgZmlnLnBhdGg9IkZpZ3VyZXMvIiwgZmlnLmFsdD0idW5sYWJlbGxlZCIpDQpvcHRzX2NodW5rJHNldChjb21tZW50PSIiLCBmaWcuYWxpZ249ImNlbnRlciIsIHRpZHk9VFJVRSkNCm9wdGlvbnMoa25pdHIua2FibGUuTkEgPSAnJykNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShicm9vbSkNCmBgYA0KDQoNCjwhLS0tIERvIG5vdCBlZGl0IGFueXRoaW5nIGFib3ZlIHRoaXMgbGluZS4gLS0tPg0KDQpTb21ldGltZXMgd2UgbmVlZCB0byBzcGVjaWZ5IGEgZGlmZmVyZW50IGxpbmUgb3IgY3VydmUgZm9yIGRpZmZlcmVudCByYW5nZXMgb2YgIG91ciBwcmVkaWN0b3IgdmFyaWFibGUuICBTdWNoIGEgbW9kZWwgaXMgY2FsbGVkICoqcGllY2V3aXNlKiouIA0KDQojIyMgdGhlIEluZGlhbmFwb2xpcyA1MDAgZGF0YQ0KDQpUaGlzIGRhdGEgaGFzIHRoZSB3aW5uaW5nIHNwZWVkIChhbiBhdmVyYWdlIG92ZXIgNTAwIG1pbGVzKSBmb3IgYSB2ZXJ5IGZhbW91cyBtb3RvciByYWNlLiBUaGlzIGlzIG5vdCBhIGNvbXBsZXRlIHNlcmllczsgdGhlIHJhY2UgaXMgaGVsZCBpbiBhbG1vc3QgZXZlcnkgeWVhciwgd2l0aCBzb21lIG5vdGFibGUgZXhjZXB0aW9ucy4NCg0KDQoNCmByIHhmdW46OmVtYmVkX2ZpbGUoIi4uLy4uL2RhdGEvSW5kaWFuYXA1MDAuY3N2IilgDQoNCmBgYHtyIEluZGlhbmFwb2xpcyA1MDAgd2lubmluZyBzcGVlZCwgZXZhbD0tMSwgZWNobz0tMn0NCkluZHkgPSByZWFkLmNzdigiSW5kaWFuYXA1MDAuY3N2IixoZWFkZXI9VFJVRSkNCkluZHkgPSByZWFkLmNzdigiLi4vLi4vZGF0YS9JbmRpYW5hcDUwMC5jc3YiLGhlYWRlcj1UUlVFKQ0KcGxvdChTcGVlZH4gWWVhciwgZGF0YT1JbmR5KQ0KYGBgDQoNCkluIHRoaXMgZXhhbXBsZSB0aGVyZSBhcHBlYXIgdG8gYmUgdGhyZWUgc2VwYXJhdGUgbGluZSBzZWdtZW50cywgd2l0aCBwb3NzaWJseSBkaWZmZXJlbnQgc2xvcGVzIGJlZm9yZSBXb3JsZCBXYXIgSSwgYmV0d2VlbiB0aGUgdHdvIHdhcnMsIGFuZCBhZnRlciBXb3JsZCBXYXIgSUkuICAoVGhlIHJhY2Ugd2FzIG5vdCBydW4gd2hpbGUgdGhlIFVTQSB3YXMgYWN0aXZlIGluIHRoZSB3b3JsZCB3YXJzLikNCg0KDQojIyBBbGxvd2luZyBmb3IgY2hhbmdlcyBpbiBzbG9wZQ0KDQoNClRoZSBjaGFuZ2UgcG9pbnRzIGFyZSBjYWxsZWQgKioqa25vdHMqKiwgdXN1YWxseSB3ZSBpbnNpc3QgdGhhdCB0aGUgbGluZXMgam9pbiB1cCBhdCB0aGUga25vdHMgKG5vIGRpc2NvbnRpbnVpdGllcykgYnV0IGVhY2ggY29udGV4dCB3aWxsIGhhdmUgdG8gYmUganVkZ2VkIG9uIGl0cyBtZXJpdHMuICANCg0KQSBtb2RlbCB3aXRoIGEgc2luZ2xlIGNoYW5nZSBpbiBzbG9wZSBhdCBrbm90ICRca2FwcGEkIHdvdWxkIGhhdmUgZXF1YXRpb24gICQkIHkgPSBcYmV0YV8wICsgXGJldGFfMXggKyBcYmV0YV8yICh4LVxrYXBwYSlfKyAgKyBcdmFyZXBzaWxvbiQkDQoNCndoZXJlIHRoZSB0ZXJtICQoeC1ca2FwcGEpXyskIGlzIGRlZmluZWQgYXM6ICR4LVxrYXBwYSQgIGlmICAkeD5ca2FwcGEkOyAgICBhbmQgIDAgIGlmICAkeCAgXGxlIFxrYXBwYSQuDQoNClRoaXMgY2FuIGJlIGNhbGN1bGF0ZWQgaW4gUiBhcyAgYGlmZWxzZSh4PGssIDAsIHgtaylgIHJlYWQgYXMgaWYgKngqIGlzIGxlc3MgdGhhbiAqayosIHRoZW4gdXNlIHplcm8sIGVsc2UgdXNlICp4LWsqLg0KDQoNCg0KDQoNCklmIHRoZXJlIGFyZSB0d28ga25vdHMsICRca2FwcGFfMSQgYW5kICRca2FwcGFfMiQsIHRoZSBzYW1lIGNvZGUgY2FuIGJlIHVzZWQgbXVsdGlwbGUgdGltZXMuDQoNCg0KSW4gdGhlIEluZGlhbmFwb2xpcyBkYXRhIHdlIGhhdmUgZ2FwcyBpbiB0aGUgeWVhcnMgZHVlIHRvIHRoZSByYWNlcyBiZWluZyBjYW5jZWxsZWQgaW4gIDE5MTctMTggIGFuZCAgMTk0Mi00NSBkdWUgdG8gdGhlIFVTQSBwYXJ0aWNpcGF0aW5nIGluIHRoZSB3YXJzLiAgV2Ugd2lsbCB1c2UgdGhlIG1pZHBvaW50cyBvZiB0aGUgbWlzc2luZyB5ZWFycyBhcyB0aGUga25vdHM6ICRca2FwcGFfMSA9IDE5MTcuNSQgYW5kICRca2FwcGFfMj0gMTk0My41JC4gIFRodXMgd2UgZml0IHRoZSBtb2RlbA0KDQokJCB5ID0gXGJldGFfMCArIFxiZXRhXzEgeCArIFxiZXRhXzIgKHgtXGthcHBhXzEpXysgKyBcYmV0YV8zICh4LVxrYXBwYV8yKV8rICsgXHZhcmVwc2lsb24kJA0KDQoNCmBgYHtyIEluZGlhbmFwb2xpcy5jaGFuZ2Uuc2xvcGVzfQ0KS25vdDE9MTkxNy41OyBLbm90MiA9IDE5NDMuNQ0KSW5keSB8PiANCiAgbXV0YXRlKHQyID0gaWZlbHNlKFllYXI8S25vdDEsIDAsIFllYXItS25vdDEpLA0KICAgICAgICAgdDMgPSBpZmVsc2UoWWVhcjxLbm90MiwgMCwgWWVhci1Lbm90MikpIC0+IEluZHkNCg0KSW5keSB8PiBoZWFkKCkgfD4ga2FibGUoKQ0KSW5keSB8PiB0YWlsKCkgfD4ga2FibGUoKQ0KDQpJbmR5LnBjZTAgPC0gbG0oU3BlZWQgfiBZZWFyLCBkYXRhID0gSW5keSkNCkluZHkucGNlMSA8LSBsbShTcGVlZCB+IFllYXIgKyB0MiArIHQzLCBkYXRhID0gSW5keSkNCnN1bW1hcnkoSW5keS5wY2UxKQ0KYW5vdmEoSW5keS5wY2UwLCBJbmR5LnBjZTEpDQpgYGAgDQoNCg0KYGBge3IgSW5kaWFuYXBvbGlzQ2hhbmdlU2xvcGVzfQ0KSW5keSB8PiBtdXRhdGUoRml0cyA9IGZpdHRlZC52YWx1ZXMoSW5keS5wY2UxKSkgfD4gDQpnZ3Bsb3QoYWVzKHk9U3BlZWQsIHg9WWVhcikpICsNCiAgZ2VvbV9wb2ludChjb2xvciA9ICJibHVlIikgKw0KICBnZW9tX3BvaW50KGFlcyh5ID0gRml0cyksIGNvbG9yID0gInJlZCIpICsNCmxhYnMoeT0iQXZlcmFnZSBzcGVlZCIpDQpgYGANCg0KTm90ZSB0aGF0LCBiZWZvcmUgV1dJLCB0aGUgd2lubmluZyBzcGVlZCB3YXMgcmlzaW5nIGJ5ICRcYXBwcm94IDIkIG1waCBwZXIgeWVhci4gIFRoZSBzZWNvbmQgc2xvcGUgY29lZmZpY2llbnQgaXMgDQokXGFwcHJveCAtMSQsICB3aGljaCBtb2RpZmllcyB0aGUgcHJldmlvdXMgc3BlZWQuICBTbyBiZXR3ZWVuIHRoZSB0d28gV29ybGQgV2FycyB0aGUgd2lubmluZyBzcGVlZCB3YXMgcmlzaW5nIGJ5ICRcYXBwcm94IDEkIG1waCBwZXIgeWVhci4gICBUaGUgdGhpcmQgc2xvcGUgY29lZmZpY2llbnQgaXMgJFxhcHByb3ggMC41JCAsIHdoaWNoIG1lYW5zIHRoYXQgYWZ0ZXIgV1dJSSB0aGUgd2lubmluZyBzcGVlZCB3YXMgcmlzaW5nIGJ5ICRcYXBwcm94IDEuNSQgbXBoIHBlciB5ZWFyLiANCg0KDQojIyBBbGxvd2luZyBmb3IgZGlzY29udGludWl0eQ0KDQpUaGUgcHJlY2VkaW5nIGFuYWx5c2lzIGluZGljYXRlZCBhIHNpZ25pZmljYW50IGNoYW5nZSBpbiBzbG9wZSBhdCB0aGUgdHdvIHdhcnMuIFRoZSBuZXh0IHF1ZXN0aW9uIGlzOiBXYXMgdGhlcmUgYSBkaXNjb250aW51aXR5PyBUaGF0IGlzLCBkbyB0aGUgbGluZSBzZWdtZW50cyBoYXZlIHRvIGpvaW4gdXAgYXQgdGhlIGtub3QgcG9pbnQ/DQpUbyBleGFtaW5lIHRoaXMgaHlwb3RoZXNpcywgd2UgbXVzdCBmaXQgYSBzZXBhcmF0ZSBpbnRlcmNlcHQgZm9yIHRoZSBsaW5lIGluIGVhY2ggb2YgdGhlIHRocmVlIHNlZ21lbnRzLiBUaGlzIGNhbiBiZSBhY2NvbXBsaXNoZWQgYnkgYWRkaW5nIHRoZSB0d28gZXh0cmEgaW5kaWNhdG9yIHZhcmlhYmxlcywgY29uc3QyIGFuZCBjb25zdDMsIHRvIHRoZSBtb2RlbCBhYm92ZSB0byBhbGxvdyB0aGUgaW50ZXJjZXB0cywgaW4gYWRkaXRpb24gdG8gdGhlIHNsb3BlcywgdG8gZGlmZmVyIGJldHdlZW4gdGhlIHNlZ21lbnRzLg0KDQogDQpgYGB7ciBMaW5lc1dpdGhEaXNjb250aW51aXR5fQ0KSW5keSB8PiANCiAgbXV0YXRlKENvbnN0MiA9IGlmZWxzZShZZWFyID4gS25vdDEsIDEsIDApLA0KICAgICAgICAgQ29uc3QzID0gaWZlbHNlKFllYXIgPiBLbm90MiwgMSwgMCkpIC0+IEluZHkNCg0KSW5keS5wY2UyIDwtIGxtKFNwZWVkIH4gWWVhciArIHQyICsgdDMgKyBDb25zdDIgKyBDb25zdDMsIGRhdGEgPSBJbmR5KQ0Kc3VtbWFyeShJbmR5LnBjZTIpDQphbm92YShJbmR5LnBjZTEsIEluZHkucGNlMikNCmBgYA0KDQoNCg0KVGhlIGFub3ZhKCkgaW5kaWNhdGVzIHRoYXQsIG92ZXJhbGwsIHRoZSBtb2RlbCBpcyBzaWduaWZpY2FudGx5IGltcHJvdmVkIGJ5IGFsbG93aW5nIGZvciBkaXNjb250aW51aXR5LiBUaGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgc2hvdyB0aGlzIGlzIGFsbW9zdCBlbnRpcmVseSBkdWUgdG8gdGhlIGNoYW5nZSBhdCB0aGUgc2Vjb25kIGtub3QsIG5vdCB0aGUgZmlyc3QuIA0KDQpUaGUgc2lnbmlmaWNhbnQgY29lZmZpY2llbnQgb2YgQ29uc3QyIHNheXMgdGhhdCwgaWYgdGhlIGxpbmVzDQpiZWZvcmUgYW5kIGFmdGVyIDE5NDMuNSB3ZXJlIGV4dGVuZGVkIHRocm91Z2ggdGhlIHBlcmlvZCAxOTQxLTQ1LCB0aGVuIHRoZXJlIHdvdWxkIGJlIGEgZHJvcCBvZiA3LjEgbXBoLiBJbiBwcmFjdGljZSBpdCBsb29rcyBhcyBpZiAgU3BlZWQganVzdCBzdG9wcGVkIHJpc2luZw0KYmV0d2VlbiB0aGUgd2FycywgcmF0aGVyIHRoYW4gYWN0dWFsbHkgZHJvcHBpbmcuDQoNCg0KYGBge3IgSW5kaWFuYXBvbGlzRGlzY29udGludWl0aWVzfQ0KSW5keSRGaXRzID0gZml0dGVkLnZhbHVlcyhJbmR5LnBjZTIpDQpJbmR5IHw+IGdncGxvdChhZXMoeT1TcGVlZCwgeD1ZZWFyKSkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gImJsdWUiKSArDQogIGdlb21fcG9pbnQoYWVzKHkgPSBGaXRzKSwgY29sb3IgPSAicmVkIikgKw0KbGFicyh5PSJBdmVyYWdlIHNwZWVkIikNCmBgYA0KDQoNCg0KIyMgRXh0ZW5kaW5nIHRoZSBwaWVjZXdpc2UgYXBwcm9hY2gNCg0KVGhlIGFwcHJvYWNoIGNhbiBiZSBleHRlbmRlZCB0byBhbGxvdyBwaWVjZXdpc2UgcXVhZHJhdGljIG9yIGN1YmljIGN1cnZlcy4NCg0KT2Z0ZW4gdGhlIGN1cnZlcyBhcmUgY29uc3RyYWluZWQgc28gdGhhdCB0aGUgc2VjdGlvbnMgYXJlIG5vdCBvbmx5IGNvbnRpbnVvdXMgYnV0IHNtb290aCBpLmUuIGhhdmUgY29udGludW91cyBmaXJzdCBkZXJpdmF0aXZlIGF0IHRoZSBrbm90cyAoYW5kIG9mIGNvdXJzZSBldmVyeXdoZXJlIGVsc2UpLiAgIFN1Y2ggY3VydmVzIGFyZSBjYWxsZWQgIOKAmHNwbGluZXPigJkuICAgIEN1cnZlIGZpdHRpbmcgYnkgY3ViaWMgc3BsaW5lcyBpcyBhIHZlcnkgb2xkIHRlY2huaXF1ZS4gICBWYXJpb3VzIHNwbGluZXMgYXJlIGF2YWlsYWJsZSBpbiBSLCAgYnV0ICB3ZSB3aWxsIG5vdCBwdXJzdWUgdGhlbSBhbnkgZnVydGhlciBpbiB0aGlzIGNvdXJzZS4gSW4gdGhlIHBhc3Qgc3BsaW5lcyBoYXZlIGZyZXF1ZW50bHkgYmVlbiB1c2VkIGFzIGEgdGVjaG5pcXVlIGZvciBzbW9vdGhpbmcgZGF0YSwgYnV0IHdlIGNhbiB1c2UgTG93ZXNzIGN1cnZlcyBmb3IgdGhhdCBwdXJwb3NlLiANCg0KSXQgaXMgIGFzc3VtZWQgdGhlIGtub3RzICAkXGthcHBhXzEkIGFuZCAkXGthcHBhXzIkIGFyZSBrbm93biBhbmQgZG8gbm90IGhhdmUgdG8gYmUgZXN0aW1hdGVkLiBTb21ldGltZXMgdGhpcyBpcyByZWFzb25hYmxlLiAgRm9yIGV4YW1wbGUgICAqeCogICBtYXkgcmVwcmVzZW50IHRpbWUgYW5kICAkXGthcHBhXzEkIGFuZCAkXGthcHBhXzIkIGFyZSBrbm93biBldmVudHMgIGVnLiB3YXIsIHN0b2NrIG1hcmtldCBjcmFzaCwgZXRjLiAgDQoNCklmIHRoZSBwb3NpdGlvbiBvZiB0aGUga25vdHMgZG8gaGF2ZSB0byBiZSBlc3RpbWF0ZWQgICB0aGVuIHdlIGhhdmUgIGEgbXVjaCBtb3JlIGNvbXBsaWNhdGVkIHN0YXRpc3RpY2FsIHByb2JsZW0uICBUaGlzIGlzIGFuIGFyZWEgb2YgcmVsYXRpdmVseSByZWNlbnQgc3RhdGlzdGljYWwgcmVzZWFyY2ggKGUuZy4gaW4gdGhlIGxhc3QgMzAgeWVhcnMuKSAgQW4gZXhhbXBsZSB3aGVyZSB0aGUga25vdCBuZWVkcyB0byBiZSBlc3RpbWF0ZWQgaXMgd2hlcmUgdGhlcmUgaXMgc29tZSBzb3J0IG9mIGJpb2xvZ2ljYWwgdGlwcGluZyBwb2ludCwgd2hlcmUgdGhlIHN5c3RlbSByZXNwb25kcyBkaWZmZXJlbnRseSB0byBzdGltdWxpIGJleW9uZCBhIGNlcnRhaW4gKHVua25vd24pIGxldmVsLiANCg0KT25lIHdheSBvZiBmaXR0aW5nIHN1Y2ggbW9kZWxzIGlzIHVzaW5nIGEgbm9ubGluZWFyICByZWdyZXNzaW9uIG1vZGVsLg0KDQoNCiMjIEV4YW1wbGU6IENoaWxkIEx1bmcgRnVuY3Rpb24gRGF0YQ0KDQoNCkZFViAoZm9yY2VkIGV4cGlyYXRvcnkgdm9sdW1lKSBpcyBhIG1lYXN1cmUgb2YgbHVuZyBmdW5jdGlvbi4NClRoZSBkYXRhIGluY2x1ZGUgZGV0ZXJtaW5hdGlvbnMgb2YgRkVWIG9uIDMxOCBmZW1hbGUgY2hpbGRyZW4gd2hvIHdlcmUgc2VlbiBpbiBhIGNoaWxkaG9vZCByZXNwaXJhdG9yeSBkaXNlYXNlIHN0dWR5IGluIE1hc3NhY2h1c2V0dHMgaW4gdGhlIFUuUy4NCkNoaWxkJ3MgYWdlIChpbiB5ZWFycykgYWxzbyByZWNvcmRlZC4gIEl0IGlzICBvZiBpbnRlcmVzdCB0byBtb2RlbCBGRVYgKHJlc3BvbnNlKSBhcyBmdW5jdGlvbiBvZiBhZ2UuDQoNCg0KYHIgeGZ1bjo6ZW1iZWRfZmlsZSgiLi4vLi4vZGF0YS9mZXYuY3N2IilgDQoNCg0KIERhdGEgc291cmNlOiBUYWdlciwgSS4gQi4sIFdlaXNzLCBTLiBULiwgUm9zbmVyLCBCLiwgYW5kIFNwZWl6ZXIsDQpGLiBFLiAoMTk3OSkuIEVmZmVjdCBvZiBwYXJlbnRhbCBjaWdhcmV0dGUgc21va2luZyBvbiBwdWxtb25hcnkgZnVuY3Rpb24gaW4gY2hpbGRyZW4uICpBbWVyaWNhbiBKb3VybmFsIG9mIEVwaWRlbWlvbG9neSosICoqMTEwKiosIDE1LTI2LiANCg0KDQpgYGB7ciBnZXRGZXZEYXRhLCBldmFsPS0xLCBlY2hvPS0yfQ0KRmV2IDwtIHJlYWQuY3N2KGZpbGU9ImZldi5jc3YiLCBoZWFkZXI9VFJVRSkNCkZldiA8LSByZWFkLmNzdihmaWxlPSIuLi8uLi9kYXRhL2Zldi5jc3YiLCBoZWFkZXI9VFJVRSkNCnBsb3QoRkVWfkFnZSwgZGF0YT1GZXYpDQpgYGANCg0KVGhlIHNjYXR0ZXIgcGxvdCBvZiBkYXRhIGluZGljYXRlcyB0aGF0IHJlbGF0aW9uc2hpcCBiZXR3ZWVuIEZFViBhbmQgYWdlIGlzIG5vdCBsaW5lYXIuDQpXZSBzYXcgaG93IHRvIG1vZGVsIEZFViBhcyBhIHBvbHlub21pYWwgaW4gYWdlIGFuZCBkZWNpZGVkIG9uIHRoZSBmb3VydGggb3JkZXIgcG9seW5vbWlhbCBtb2RlbDoNCg0KDQpgYGB7ciBGZXYucG9seX0NCkZldi5sbTQgPC0gbG0oRkVWfnBvbHkoQWdlLDQpLCBkYXRhPUZldikNCmBgYA0KDQpJZiB3ZSBwbG90IHRoaXMgd2UgZmluZCB0aGUgbW9kZWwgc2VlbXMgdG8gaGF2ZSBhIHN0cmFuZ2Ugd29iYmxlLCBhbmQgc3RlZXAgaW5jcmVhc2luZyBjdXJ2ZXMgYXQgYm90aCBlbmRzIG9mIHRoZSBhZ2UgcmFuZ2UuDQoNCmBgYHtyIHF1YXJ0aWMgcGxvdH0NCkZldiB8PiBtdXRhdGUoRml0cyA9IGZpdHRlZC52YWx1ZXMoRmV2LmxtNCkpIHw+DQpnZ3Bsb3QoYWVzKHk9RkVWLCB4PUFnZSkpICArIGdlb21fcG9pbnQoKSArDQpnZW9tX3BvaW50KGFlcyh5PUZpdHMpLCBjb2xvciA9ICJyZWQiKQ0KYGBgDQoNCk5vdyBzdXBwb3NlIGluc3RlYWQgd2UgZml0IGEgcGllY2V3aXNlIG1vZGVsLiAgVmlzdWFsbHkgaXQgbG9va3MgbGlrZSB0aGUgRkVWIHZhbHVlcyBmbGF0dGVuIG91dCBmcm9tIEFnZT0xMiBvbndhcmRzLg0KDQoNCmBgYHtyIEZldiBwaWVjZXdpc2V9DQprIDwtIDExLjUNCkZldiA9IEZldiB8PiBtdXRhdGUoQWdlMiA9IGlmZWxzZShBZ2U8aywgMCwgQWdlLWspKQ0KDQoNCkZldi5wY2UxIDwtIGxtKEZFViB+IEFnZSArIEFnZTIsIGRhdGEgPSBGZXYpDQpzdW1tYXJ5KEZldi5wY2UxKQ0KYGBgDQoNCmBgYHtyIGZpdHNGZXYucGNlMX0NCkZldiB8PiBtdXRhdGUoRml0cz1maXR0ZWQudmFsdWVzKEZldi5wY2UxKSkgfD4NCmdncGxvdChhZXMoeT1GRVYsIHg9QWdlKSkgKyBnZW9tX3BvaW50KCkgKw0KZ2VvbV9wb2ludChhZXMoeT1GaXRzKSwgY29sb3IgPSAicmVkIikNCmBgYA0KDQpgYGB7ciBzdW1tRmV2LnBjZTF9DQpzdW1tYXJ5KEZldi5sbTQpJHNpZ21hDQpzdW1tYXJ5KEZldi5wY2UxKSRzaWdtYQ0KYGBgDQoNCg0KDQpUaGUgcmVzaWR1YWwgc3RhbmRhcmQgZXJyb3IgZm9yIHRoZSBzaW1wbGUgcGllY2V3aXNlIG1vZGVsIGlzIGFsbW9zdCB0aGUgc2FtZSBhcyBmb3IgdGhlIG1vcmUgY29tcGxpY2F0ZWQgZm91cnRoLWRlZ3JlZSBwb2x5bm9taWFsLCAgYW5kIHRoZSBncmFwaCBsb29rcyBtb3JlIGJpb2xvZ2ljYWxseSByZWFzb25hYmxlLCBzbyB3ZSB3b3VsZCBwcm9iYWJseSBwcmVmZXIgdGhlIHBpZWNld2lzZSBtb2RlbC4gDQoNCg0KDQoNClRoZSBvdXRjb21lIGlzIHRoYXQgYnkgY29uc3RydWN0aW5nIG5ldyB2YXJpYWJsZXMsIHdlIGhhdmUgaW5jcmVhc2VkIG91ciBhYmlsaXR5IHRvIGZpdCBtZWFuaW5nZnVsIGFuZCBtb3JlIGVhc2lseSBpbnRlcnByZXRhYmxlIG1vZGVscy4gV2UgaGF2ZW4ndCBldmVuIGFsbG93ZWQgZm9yIHNvbWUgY3VydmF0dXJlIGluIHRoZSBwaWVjZXdpc2UgbW9kZWwsIGJ1dCB0aGF0IHdpbGwgbmVlZCB0byBiZSBsZWZ0IGZvciBleHRlbnNpb24gd29yay4NCg0KDQo=