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)

unlabelled

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()
Speed Year t2 t3 const2 const3
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()
Speed Year t2 t3 const2 const3
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")

unlabelled

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")

unlabelled

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)

unlabelled

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")

unlabelled

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")

unlabelled

summary(Fev.lm4)$sigma
[1] 0.3897186
summary(Fev.pce1)$sigma
[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=