View the latest recording of this lecture

This topic introduces the concept of nonlinear regression models, iterative fitting by direct search, Gauss-Newton algorithm.

When the Response is a Nonlinear Function of the Regression Parameters

We consider what happens when the relationship between \(Y\) (response) and \(X\) (predictor) is known to be a particular curve that cannot be made linear by transformation.

That is, suppose \[ y = f(x) + \varepsilon\]

Where \(f(x)\) denotes some function of \(x\) (e.g. a curve), and where the error variation \(\varepsilon\) around the curve is normally distributed with constant variance as before.

Suppose we have a general idea of the shape of the curve, but the precise details depend on some unknown parameters, say \(\alpha, \beta\) and \(\gamma\).

The task, as in ordinary linear regression, reduces to that of estimating the parameters. Now in principle we can estimate \(\alpha, \beta\) and \(\gamma\) by least squares as before. That is, we simply choose those estimates that make the squared error of our curve as small as possible: i.e. the least squares problem is to minimize \[ SS_{error} = \sum \left(y_i - f(x_i; \alpha,\beta,\gamma )\right) ^2 \] or equivalently minimise the residual standard error \(S = \sqrt{SS_{error}/{(n-p)}}\).

The only difficulty or difference to the previous situation is that we do not have simple formulas to use arising out of the matrix linear algebra, as the curve \(f(x)\) is not linear in \(\alpha, \beta\) and \(\gamma\).

A couple of examples of functions \(f(x)\) that are nonlinear in the parameters are:

  1. Suppose

\[ f(x_i; \alpha,\beta_0,\beta_1) = \alpha~~ \frac{ e^{\beta_0+ \beta_1 x}}{ 1+e^{\beta_0+ \beta_1 x} } + \varepsilon\]

This is the so-called logistic curve, which models a phenomenon (such as sales) that goes from zero to some upper limit \(\alpha\).

(Note \(\alpha\) is an asymptote for the curve but the individual data can go randomly above or below the curve).

This model cannot be made linear, but in principle we can use any mathematical method we wish to find those estimates of \(\alpha,\beta_0\),\(\beta_1\) that minimize
\(SS_{error} = \sum (y_i - \hat y_i )^2\) where \[\hat y_i = \hat\alpha~~\frac{e^{\hat\beta_0+ \hat\beta_1 x} }{ 1+e^{\hat\beta_0+ \hat\beta_1 x} } ~~.\]

  1. A somewhat simpler example, but also nonlinear, is

\[ y_i = \beta_0 + \beta_1 x_i^{\alpha} \]

where \(\alpha\) is unknown. Again this cannot be made linear in \(\alpha,\beta_0\) and \(\beta_1\) but in this example the model fitting could be done simply by guessing \(\alpha\) and creating a new explanatory variable \(x_{new} = x^\alpha\). Then \(\beta_0\) and \(\beta_1\) can be found by linear regression of \(y\) on \(x_{new}\).

The Gauss-Newton Algorithm

The alternative to using direct search is to use a mathematical technique such as the Gauss-Newton Algorithm .

The idea of the algorithm is to approximate the curve \(f(x_i; \alpha,\beta,\gamma)\) by a sum of straight lines involving \(\alpha,\beta\) and \(\gamma\). That is, a linearized approximation to the curve.

The mathematical ideas are in an word document on Stream, but are not examinable.

The main points are:

  1. The process of model-fitting is iterative. That is, it involves a series of repeated linear regressions with slightly different data each time, converging to a fixed answer as we get closer and closer to the optimal estimates.

  2. The Gauss-Newton method is guaranteed to converge quickly to the optimal estimates, provided that our initial starting point is close enough.

  3. The method also produces approximate hypothesis tests and confidence intervals.

The nls() nonlinear least squares procedure implements the Gauss-Newton algorithm.

We need to specify the formula, and (preferably) reasonable starting values.

manhours.nls = nls(Manhours ~ beta0 + beta1 * (Cases^alpha), start = list(alpha = 0.25,
    beta0 = -16000, beta1 = 4400), data = manhours)

summary(manhours.nls)

Formula: Manhours ~ beta0 + beta1 * (Cases^alpha)

Parameters:
        Estimate Std. Error t value Pr(>|t|)
alpha  2.689e-01  1.585e-01   1.697    0.115
beta0 -1.463e+04  1.256e+04  -1.164    0.267
beta1  3.622e+03  5.954e+03   0.608    0.554

Residual standard error: 909.7 on 12 degrees of freedom

Number of iterations to convergence: 6 
Achieved convergence tolerance: 2.462e-06
plot(Manhours ~ Cases)
lines(predict(manhours.nls) ~ Cases)

unlabelled

We can calculate an approximate confidence interval for \(\alpha\) using \[ \hat\alpha \pm t_{n-p}(0.025)*SE(\hat\alpha)\]

summary(manhours.nls)$coefficients
           Estimate   Std. Error    t value  Pr(>|t|)
alpha  2.689232e-01     0.158476  1.6969329 0.1154707
beta0 -1.462572e+04 12564.190279 -1.1640799 0.2670141
beta1  3.621670e+03  5954.344902  0.6082398 0.5543666
est = summary(manhours.nls)$coefficients[1]
se = summary(manhours.nls)$coefficients[4]
lower = est + qt(0.025, df = 15 - 3) * se
upper = est + qt(0.975, df = 15 - 3) * se
cbind(est, se, lower, upper)
           est       se       lower     upper
[1,] 0.2689232 0.158476 -0.07636641 0.6142128

We can conclude from this that we could not reject a hypothesis that \(\alpha\) equalled any value between -0.07 and 0.6.

In particular, we could have stuck with the square-root transformation (\(\alpha=0.5\)) or used the log transformation (recall this takes the place of \(\alpha=0\) in the ladder of powers).

Example: A Growth Curve Model with an Maximum

Recall the fev data relating forced expiration volume (FEV) to girls’ ages. This seemed to flatten off after about age 10.

We will model the growth using a logistic curve with unknown maximum.

\[ f(x_i; \alpha,\beta_0,\beta_1) = \alpha~~\frac{e^{\beta_0+ \beta_1 x}}{ 1+e^{\beta_0+ \beta_1 x} } + \varepsilon\]

Download FEV.csv

## fev <- read.csv(file = "fev.csv", header = TRUE)

FEV = fev$FEV
Age = fev$Age

plot(FEV ~ Age)

unlabelled

From the graph the maximum \(\alpha \approx 3\), so setting \[FEV \approx 3 ~~\frac{e^{\beta_0 + \beta_1 Age}}{ 1+ e^{\beta_0 + \beta_1 Age}}\] we can rearrange this as
\[1+ e^{\beta_0 + \beta_1 Age} \approx \frac{3}{FEV} ~ e^{\beta_0 + \beta_1 Age} \]

Gather up the \(e^{\beta_0 + \beta_1 Age}\) terms

\[ \frac{1}{3/FEV - 1} \approx e^{\beta_0 + \beta_1 Age}\] and then if we take logs we can use the left-hand side as the “response” in a regression

\[ Y_{new} = \log\left(\frac{1}{3/FEV - 1}\right)\approx \beta_0 + \beta_1 Age \] to give us starting values for \(\beta_0\) and \(\beta_1\):

start.calc = lm(-log(3/FEV - 1) ~ Age)
Warning in log(3/FEV - 1): NaNs produced
summary(start.calc)

Call:
lm(formula = -log(3/FEV - 1) ~ Age)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.9603 -0.5223 -0.1590  0.2990  5.0842 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -1.30548    0.21129  -6.179 2.78e-09 ***
Age          0.28493    0.02226  12.799  < 2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.9443 on 238 degrees of freedom
  (78 observations deleted due to missingness)
Multiple R-squared:  0.4077,    Adjusted R-squared:  0.4052 
F-statistic: 163.8 on 1 and 238 DF,  p-value: < 2.2e-16

So we will use starting values \(\alpha=3\), \(\beta_0= -1.3\) and \(\beta_1=0.28\)

fev.nls = nls(FEV ~ alpha * exp(beta0 + beta1 * Age)/(1 + exp(beta0 + beta1 * Age)),
    start = list(alpha = 3, beta0 = -1.3, beta1 = 0.28))
summary(fev.nls)

Formula: FEV ~ alpha * exp(beta0 + beta1 * Age)/(1 + exp(beta0 + beta1 * 
    Age))

Parameters:
      Estimate Std. Error t value Pr(>|t|)    
alpha  3.21164    0.08682   36.99   <2e-16 ***
beta0 -2.16822    0.21482  -10.09   <2e-16 ***
beta1  0.36561    0.03620   10.10   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.3956 on 315 degrees of freedom

Number of iterations to convergence: 5 
Achieved convergence tolerance: 6.266e-06
plot(FEV ~ Age)
lines(30:200/10, predict(fev.nls, newdata = data.frame(Age = 30:200/10)), lty = 1,
    col = 2, lwd = 3)

unlabelled

This has converged. Note the residual standard error is 0.3956, which is slightly bigger than for the piecewise model looked at earlier (S=0.39) but the curve seems more reasonable than having a sharp corner.

We can get a slightly better fit by adding a quadratic term in Age. In the following we use starting value \(\beta_2=0\).

fev.nls2 = nls(FEV ~ alpha * exp(beta0 + beta1 * Age + beta2 * Age^2)/(1 + exp(beta0 +
    beta1 * Age + beta2 * Age^2)), start = list(alpha = 3, beta0 = -1.3, beta1 = 0.28,
    beta2 = 0), data = fev)
summary(fev.nls2)

Formula: FEV ~ alpha * exp(beta0 + beta1 * Age + beta2 * Age^2)/(1 + exp(beta0 + 
    beta1 * Age + beta2 * Age^2))

Parameters:
      Estimate Std. Error t value Pr(>|t|)    
alpha  3.03273    0.05788  52.393  < 2e-16 ***
beta0 -0.06772    0.75358  -0.090  0.92846    
beta1 -0.23896    0.21936  -1.089  0.27684    
beta2  0.04437    0.01619   2.741  0.00647 ** 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.3906 on 314 degrees of freedom

Number of iterations to convergence: 7 
Achieved convergence tolerance: 2.889e-06
plot(FEV ~ Age)
lines(30:200/10, predict(fev.nls2, newdata = data.frame(Age = 30:200/10)), lty = 1,
    col = 2, lwd = 3)

unlabelled

Piecewise Regression with an Unknown Knot

Recall that sometimes we wish to fit a piecewise regression model, but the location of the knot is unknown.

This then is becomes a nonlinear regression model, with the location of the knot taking the role of the alpha parameter above (not meaning that it enters the equation the same way, but that if the knot is known, then every other parameter can be fitted by linear regression).

Strictly speaking the Gauss-Newton algorithm does not quite work, as the knot gives a ‘corner’ to the lines, or even a jump in the level of the lines, and this means that the calculus on which the method was originally based breaks down. However the nls() routine can be made to cope.

Nonlinear regression for a change-slope model: the Thai Baht.

The data here refer to the Thai Baht Exchange Rate \(Y\) vs Time (time measured in hundreds of days, hDays. ) The graph below shows \(Y\) vs hDays with a cubic model. The model looks like it would be unsafe for extrapolation on the right.

Download baht.csv

## baht = read.csv("baht.csv", header = TRUE)

Y = baht$Y
hDays = baht$hDays

plot(Y ~ hDays)
cubic = lm(Y ~ poly(hDays, 3))
summary(cubic)$sigma
[1] 0.008618117
lines(predict(cubic) ~ hDays, col = 2, lwd = 2)

unlabelled

We consider a piecewise model \[ Y = \beta_0 + \beta_1. hDays + \beta_2 (hDays-k)_+ \] where \(k\) is unknown but probably near 2. We will use this guess to give starting values for the other parameters.

start.guess = lm(Y ~ hDays + I((abs(hDays - 2) + hDays - 2)/2))
summary(start.guess)

Call:
lm(formula = Y ~ hDays + I((abs(hDays - 2) + hDays - 2)/2))

Residuals:
      Min        1Q    Median        3Q       Max 
-0.024944 -0.005216  0.001020  0.004923  0.037731 

Coefficients:
                                    Estimate Std. Error t value Pr(>|t|)    
(Intercept)                        0.3443525  0.0013566  253.84   <2e-16 ***
hDays                             -0.0201913  0.0008924  -22.63   <2e-16 ***
I((abs(hDays - 2) + hDays - 2)/2)  0.0219204  0.0012007   18.26   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.008564 on 386 degrees of freedom
Multiple R-squared:  0.6141,    Adjusted R-squared:  0.6121 
F-statistic: 307.2 on 2 and 386 DF,  p-value: < 2.2e-16

This gives starting estimates \(\beta_0\approx 0.34, ~~ \beta_1 \approx -0.02,~~ \beta_2 \approx 0.022, ~~k=2\).

The following code fits the model, but some iteration control parameters were needed, presumably because of the corner. (Other statistical software such as SPSS and Minitab have less problems fitting this model.)

baht.nls = nls(Y ~ beta0 + beta1 * hDays + beta2 * I((abs(hDays - k) + hDays - k)/2),
    start = list(beta0 = 0.34, beta1 = -0.02, beta2 = 0.022, k = 2), data = baht,
    nls.control(warnOnly = TRUE, minFactor = 1/2048))
Warning in nls(Y ~ beta0 + beta1 * hDays + beta2 * I((abs(hDays - k) + hDays -
: step factor 0.000244141 reduced below 'minFactor' of 0.000488281
summary(baht.nls)

Formula: Y ~ beta0 + beta1 * hDays + beta2 * I((abs(hDays - k) + hDays - 
    k)/2)

Parameters:
       Estimate Std. Error t value Pr(>|t|)    
beta0  0.349459   0.001597  218.88   <2e-16 ***
beta1 -0.027767   0.001772  -15.67   <2e-16 ***
beta2  0.028657   0.001821   15.73   <2e-16 ***
k      1.579995   0.064909   24.34   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.008074 on 385 degrees of freedom

Number of iterations till stop: 14 
Achieved convergence tolerance: 0.008517
Reason stopped: step factor 0.000244141 reduced below 'minFactor' of 0.000488281
plot(Y ~ hDays)
lines(predict(baht.nls) ~ hDays, col = 2, lwd = 3)

unlabelled

The graph seems much more reasonable. Note it changed the \(k\) to 1.57.

In terms of residual standard error, which is the better-fitting model, the cubic or the change-point model? (From above the cubic graph S =0.008618).

LS0tDQp0aXRsZTogIkxlY3R1cmUgNDU6IE5vbmxpbmVhciBsZWFzdCBzcXVhcmVzIg0Kc3VidGl0bGU6IDE2MS4yNTEgUmVncmVzc2lvbiBNb2RlbGxpbmcNCmF1dGhvcjogIlByZXNlbnRlZCBieSBKb25hdGhhbiBHb2RmcmV5IDxhLmouZ29kZnJleUBtYXNzZXkuYWMubno+IiAgDQpkYXRlOiAiV2VlayAxMiBvZiBTZW1lc3RlciAyLCBgciBsdWJyaWRhdGU6OnllYXIobHVicmlkYXRlOjpub3coKSlgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0aGVtZTogeWV0aQ0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgaHRtbF9ub3RlYm9vazoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHlldGkNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogIGlvc2xpZGVzX3ByZXNlbnRhdGlvbjoNCiAgICB3aWRlc2NyZWVuOiB0cnVlDQogICAgc21hbGxlcjogdHJ1ZQ0KICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0DQogIHNsaWR5X3ByZXNlbnRhdGlvbjogDQogICAgdGhlbWU6IHlldGkNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KLS0tDQoNCg0KDQoNCg0KW1ZpZXcgdGhlIGxhdGVzdCByZWNvcmRpbmcgb2YgdGhpcyBsZWN0dXJlXShodHRwczovL1ItUmVzb3VyY2VzLm1hc3NleS5hYy5uei92aWRlb3MvMjUxTDM3Lm1wNCkNCjwhLS0tIERhdGEgaXMgb24NCmh0dHBzOi8vci1yZXNvdXJjZXMubWFzc2V5LmFjLm56L2RhdGEvMTYxMjUxLw0KLS0tPg0KDQpgYGB7ciBzZXR1cCwgcHVybD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkoa25pdHIpDQpvcHRzX2NodW5rJHNldChkZXY9YygicG5nIiwgInBkZiIpKQ0Kb3B0c19jaHVuayRzZXQoZmlnLmhlaWdodD02LCBmaWcud2lkdGg9NywgZmlnLnBhdGg9IkZpZ3VyZXMvIiwgZmlnLmFsdD0idW5sYWJlbGxlZCIpDQpvcHRzX2NodW5rJHNldChjb21tZW50PSIiLCBmaWcuYWxpZ249ImNlbnRlciIsIHRpZHk9VFJVRSkNCm9wdGlvbnMoa25pdHIua2FibGUuTkEgPSAnJykNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShicm9vbSkNCmBgYA0KDQoNCjwhLS0tIERvIG5vdCBlZGl0IGFueXRoaW5nIGFib3ZlIHRoaXMgbGluZS4gLS0tPg0KDQoNClRoaXMgdG9waWMgaW50cm9kdWNlcyB0aGUgY29uY2VwdCBvZiBub25saW5lYXIgcmVncmVzc2lvbiBtb2RlbHMsIGl0ZXJhdGl2ZSBmaXR0aW5nIGJ5IGRpcmVjdCBzZWFyY2gsIEdhdXNzLU5ld3RvbiBhbGdvcml0aG0uIA0KDQojIyBXaGVuIHRoZSBSZXNwb25zZSBpcyBhIE5vbmxpbmVhciBGdW5jdGlvbiBvZiB0aGUgUmVncmVzc2lvbiBQYXJhbWV0ZXJzDQoNCldlIGNvbnNpZGVyIHdoYXQgaGFwcGVucyB3aGVuIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiAkWSQgKHJlc3BvbnNlKSBhbmQgJFgkIChwcmVkaWN0b3IpIGlzIGtub3duIHRvIGJlIGEgcGFydGljdWxhciBjdXJ2ZSB0aGF0IGNhbm5vdCBiZSBtYWRlIGxpbmVhciBieSB0cmFuc2Zvcm1hdGlvbi4NCg0KVGhhdCBpcywgc3VwcG9zZQkJJCQJeSA9IGYoeCkgICsgIFx2YXJlcHNpbG9uJCQgICAgDQoNCldoZXJlICAkZih4KSQgIGRlbm90ZXMgc29tZSBmdW5jdGlvbiBvZiAkeCQgKGUuZy4gYSBjdXJ2ZSksICBhbmQgd2hlcmUgdGhlIGVycm9yIHZhcmlhdGlvbiAkXHZhcmVwc2lsb24kIGFyb3VuZCB0aGUgY3VydmUgaXMgIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIHdpdGggY29uc3RhbnQgdmFyaWFuY2UgYXMgYmVmb3JlLiAgDQoNClN1cHBvc2Ugd2UgaGF2ZSBhIGdlbmVyYWwgaWRlYSBvZiB0aGUgc2hhcGUgb2YgdGhlIGN1cnZlLCBidXQgdGhlIHByZWNpc2UgZGV0YWlscyBkZXBlbmQgb24gc29tZSB1bmtub3duIHBhcmFtZXRlcnMsIHNheSAgJFxhbHBoYSwgXGJldGEkIGFuZCAkXGdhbW1hJC4gDQoNClRoZSB0YXNrLCBhcyBpbiBvcmRpbmFyeSBsaW5lYXIgcmVncmVzc2lvbiwgcmVkdWNlcyB0byB0aGF0IG9mIGVzdGltYXRpbmcgdGhlIHBhcmFtZXRlcnMuICBOb3cgaW4gcHJpbmNpcGxlIHdlIGNhbiBlc3RpbWF0ZSAkXGFscGhhLCBcYmV0YSQgYW5kICRcZ2FtbWEkICAgYnkgbGVhc3Qgc3F1YXJlcyBhcyBiZWZvcmUuICBUaGF0IGlzLCAgd2Ugc2ltcGx5IGNob29zZSB0aG9zZSBlc3RpbWF0ZXMgdGhhdCBtYWtlIHRoZSBzcXVhcmVkIGVycm9yIG9mIG91ciBjdXJ2ZSBhcyBzbWFsbCBhcyBwb3NzaWJsZTogDQppLmUuIHRoZSBsZWFzdCBzcXVhcmVzIHByb2JsZW0gaXMgdG8gIG1pbmltaXplDQokJCBTU197ZXJyb3J9ID0gXHN1bSBcbGVmdCh5X2kgLSBmKHhfaTsgXGFscGhhLFxiZXRhLFxnYW1tYSApXHJpZ2h0KSBeMiAkJA0Kb3IgZXF1aXZhbGVudGx5IG1pbmltaXNlIHRoZSByZXNpZHVhbCBzdGFuZGFyZCBlcnJvciANCiRTID0gXHNxcnR7U1Nfe2Vycm9yfS97KG4tcCl9fSQuIA0KDQogIA0KDQpUaGUgb25seSBkaWZmaWN1bHR5IG9yIGRpZmZlcmVuY2UgdG8gdGhlIHByZXZpb3VzIHNpdHVhdGlvbiBpcyB0aGF0IHdlIGRvIG5vdCBoYXZlIHNpbXBsZSBmb3JtdWxhcyB0byB1c2UgYXJpc2luZyBvdXQgb2YgdGhlIG1hdHJpeCBsaW5lYXIgYWxnZWJyYSwgYXMgdGhlIGN1cnZlICRmKHgpJCBpcyBub3QgbGluZWFyIGluICRcYWxwaGEsIFxiZXRhJCBhbmQgJFxnYW1tYSQuICAgICANCg0KQSBjb3VwbGUgb2YgZXhhbXBsZXMgb2YgZnVuY3Rpb25zICRmKHgpJCB0aGF0IGFyZSBub25saW5lYXIgaW4gdGhlIHBhcmFtZXRlcnMgYXJlOiANCg0KDQoxLiAgIFN1cHBvc2UgDQoNCiQkIGYoeF9pOyBcYWxwaGEsXGJldGFfMCxcYmV0YV8xKSA9ICBcYWxwaGF+fiBcZnJhY3sgZV57XGJldGFfMCsgXGJldGFfMSB4fX17IDErZV57XGJldGFfMCsgXGJldGFfMSB4fSB9ICArIFx2YXJlcHNpbG9uJCQNCg0KIFRoaXMgaXMgdGhlIHNvLWNhbGxlZCBsb2dpc3RpYyBjdXJ2ZSwgIHdoaWNoIG1vZGVscyBhIHBoZW5vbWVub24gIChzdWNoIGFzIHNhbGVzKSB0aGF0IGdvZXMgZnJvbSB6ZXJvICAgdG8gc29tZSB1cHBlciBsaW1pdCAkXGFscGhhJC4gIA0KIA0KIChOb3RlICRcYWxwaGEkIGlzIGFuIGFzeW1wdG90ZSBmb3IgdGhlICoqY3VydmUqKiBidXQgdGhlIGluZGl2aWR1YWwgZGF0YSBjYW4gZ28gcmFuZG9tbHkgYWJvdmUgb3IgYmVsb3cgdGhlIGN1cnZlKS4gICANCiANClRoaXMgbW9kZWwgIGNhbm5vdCBiZSAgbWFkZSBsaW5lYXIsIGJ1dCAgaW4gcHJpbmNpcGxlIHdlICBjYW4gdXNlIGFueSBtYXRoZW1hdGljYWwgbWV0aG9kIHdlIHdpc2ggIHRvIGZpbmQgdGhvc2UgZXN0aW1hdGVzICBvZiAkXGFscGhhLFxiZXRhXzAkLCRcYmV0YV8xJCB0aGF0IG1pbmltaXplICAgDQokU1Nfe2Vycm9yfSA9IFxzdW0gKHlfaSAtIFxoYXQgeV9pICleMiQgDQp3aGVyZSANCiQkXGhhdCB5X2kgPSBcaGF0XGFscGhhfn5cZnJhY3tlXntcaGF0XGJldGFfMCsgXGhhdFxiZXRhXzEgeH0gfXsgMStlXntcaGF0XGJldGFfMCsgXGhhdFxiZXRhXzEgeH0gfSB+fi4kJA0KDQoNCg0KMi4JQSBzb21ld2hhdCBzaW1wbGVyIGV4YW1wbGUsIGJ1dCBhbHNvIG5vbmxpbmVhciwgaXMgDQoNCiQkIHlfaSA9IFxiZXRhXzAgKyBcYmV0YV8xIHhfaV57XGFscGhhfSAkJA0KDQoNCgkJIA0Kd2hlcmUgJFxhbHBoYSQgaXMgIHVua25vd24uICBBZ2FpbiB0aGlzIGNhbm5vdCBiZSBtYWRlIGxpbmVhciBpbiAkXGFscGhhLFxiZXRhXzAkIGFuZCAkXGJldGFfMSQgYnV0IGluIHRoaXMgZXhhbXBsZSB0aGUgbW9kZWwgZml0dGluZyBjb3VsZCBiZSBkb25lIHNpbXBseSBieSBndWVzc2luZyAkXGFscGhhJCBhbmQgY3JlYXRpbmcgYSBuZXcgZXhwbGFuYXRvcnkgdmFyaWFibGUgJHhfe25ld30gICA9ICB4XlxhbHBoYSQuIA0KVGhlbiAkXGJldGFfMCQgYW5kICRcYmV0YV8xJCBjYW4gYmUgZm91bmQgYnkgbGluZWFyIHJlZ3Jlc3Npb24gb2YgJHkkIG9uICR4X3tuZXd9JC4gDQoNCg0KIyMgQ2FzZSBTdHVkeSB1c2luZyAgRGlyZWN0IFNlYXJjaA0KVGhlIGZvbGxvd2luZyBleGFtcGxlIGNvbmNlcm5zIHNvbWUgZGF0YSBvbiANCiRZJCA9IHRoZSBudW1iZXIgb2YgICptYW5ob3VycyogdXNlZCBpbiBzdXJnaWNhbCBjYXNlcyBpbiAxNSBVUyBuYXZ5IGhvc3BpdGFscywgICAgdmVyc3VzICAgJFgkPSB0aGUgbnVtYmVyIG9mICpjYXNlcyogdHJlYXRlZC4NCg0KVGhlIHB1cnBvc2Ugb2YgdGhlIGFuYWx5c2lzIHdhcyB0byBkZXZlbG9wIGEgcHJlbGltaW5hcnkgbWFucG93ZXIgZXF1YXRpb24gZm9yIGVzdGltYXRpb24gb2YgbWFuaG91cnMgcGVyIG1vbnRoIGV4cGVuZGVkIG9uIHN1cmdpY2FsIHNlcnZpY2VzLg0KDQpgciB4ZnVuOjplbWJlZF9maWxlKCIuLi8uLi9kYXRhL01hbmhvdXJzLmNzdiIpYA0KDQpgYGB7ciByZWFkIE1hbmhvdXJzLCBldmFsPS0xLCBlY2hvPS0yfQ0KbWFuaG91cnMgPSByZWFkLmNzdigiTWFuaG91cnMuY3N2IixoZWFkZXI9VFJVRSkNCm1hbmhvdXJzID0gcmVhZC5jc3YoIi4uLy4uL2RhdGEvTWFuaG91cnMuY3N2IixoZWFkZXI9VFJVRSkNCg0KbWFuaG91cnMgDQpwbG90KE1hbmhvdXJzIH4gQ2FzZXMsIGRhdGE9bWFuaG91cnMpIA0KbWgxID0gbG0oTWFuaG91cnN+Q2FzZXMsIGRhdGE9bWFuaG91cnMpICANCmFibGluZShjb2VmKG1oMSkpDQpgYGANCg0KV2UgY291bGQgdXNlIHRyaWFsIGFuZCBlcnJvci4gQSBzdHJhaWdodC1saW5lIG1vZGVsICRcYWxwaGE9MSQgZG9lcyBub3Qgc2VlbSBzdWl0YWJsZS4gV2UgY291bGQgdHJ5IGEgbW9kZWwgJFxzcXJ0IFgkICAoaS5lLiAkXGFscGhhPTAuNSQpLiAgIA0KDQoNCmBgYHtyIGZpcnN0IGZpdHN9DQoNCkNhc2VzPSBtYW5ob3VycyRDYXNlcw0KTWFuaG91cnMgPSBtYW5ob3VycyRNYW5ob3Vycw0KDQpzdW1tYXJ5KG1oMSkNCg0KQ2FzZW5ldyA9Q2FzZXNeMC41DQptaDIgPSBsbShNYW5ob3Vyc34gQ2FzZW5ldyAgKQ0Kc3VtbWFyeShtaDIpDQpwbG90KCBNYW5ob3VycyB+IENhc2VuZXcsIG1haW49IkNhc2VzIF4gMC41IikgDQphYmxpbmUoY29lZihtaDIpKQ0KYGBgDQoNCk5vdGUgdGhlIHJlc2lkdWFsIHN0YW5kYXJkIGVycm9yIGlzIHNtYWxsZXIgZm9yIHRoaXMgbW9kZWwuIA0KTGV0J3MgdHJ5IHRoZSBmb3VydGggcm9vdC4NCg0KDQpgYGB7ciBzZWNvbmQgZml0fQ0KQ2FzZW5ldyA9Q2FzZXNeMC4yNQ0KbWgzID0gbG0oTWFuaG91cnN+IENhc2VuZXcgICkNCnN1bW1hcnkobWgzKQ0KcGxvdCggTWFuaG91cnMgfiBDYXNlbmV3LCBtYWluPSJDYXNlcyBeIDAuMjUiKSANCmFibGluZShjb2VmKG1oMykpDQpgYGANCg0KDQpBZ2FpbiB0aGUgcmVzaWR1YWwgc3RhbmRhcmQgZXJyb3IgaXMgc21hbGxlci4gIFN1cHBvc2Ugd2UgdHJ5IGEgZ3JpZCBvZiB2YWx1ZXMgZm9yICRcYWxwaGEkDQoNCmBgYHtyIGdyaWQxfQ0KYWxwaGEgPSByc2U9IHJlcCgwLDEwKQ0KZm9yKCBpIGluIDE6MTAgKXsNCmFscGhhW2ldID0gaS8xMA0KY2FzZW5ldyA9IENhc2VzXmFscGhhW2ldDQpyc2VbaV0gPSBzdW1tYXJ5KGxtKE1hbmhvdXJzfmNhc2VuZXcpKSRzaWdtYX0NCmNiaW5kKGFscGhhLHJzZSkNCmBgYA0KDQpJdCBsb29rcyBsaWtlIHRoZSBtaW5pbXVtIGlzIGZvciAkXGFscGhhJCBiZXR3ZWVuIDAuMiBhbmQgMC40DQoNCg0KYGBge3IgZ3JpZDJ9DQphbHBoYSA9IHJzZT0gcmVwKDAsMjApDQpmb3IoIGkgaW4gMToyMCApew0KYWxwaGFbaV0gPSAwLjIrIGkvMTAwDQpjYXNlbmV3ID0gQ2FzZXNeYWxwaGFbaV0NCnJzZVtpXSA9IHN1bW1hcnkobG0oTWFuaG91cnN+Y2FzZW5ldykpJHNpZ21hfQ0KY2JpbmQoYWxwaGEscnNlKQ0KYGBgDQpJdCBsb29rcyBsaWtlIHRoZSBtaW5pbXVtIGlzIGZvciAkXGFscGhhJCBiZXR3ZWVuIDAuMjYgYW5kIDAuMjguDQpgYGB7ciBncmlkM30NCmFscGhhID0gcnNlPSByZXAoMCwyMCkNCmZvciggaSBpbiAxOjIwICl7DQphbHBoYVtpXSA9IDAuMjYrIGkvMTAwMA0KY2FzZW5ldyA9IENhc2VzXmFscGhhW2ldDQpyc2VbaV0gPSBzdW1tYXJ5KGxtKE1hbmhvdXJzfmNhc2VuZXcpKSRzaWdtYX0NCmNiaW5kKGFscGhhLHJzZSkNCmBgYA0KSXQgbG9va3MgbGlrZSB0aGUgbWluaW11bSAkUyQgaXMgb2J0YWluZWQgZm9yICRcYWxwaGEgXGFwcHJveCAwLjI2OSQuICBUaGUgcHJvY2VkdXJlIGNvdWxkIGJlIHJlcGVhdGVkIHRvIG9idGFpbiB0aGUgbWluaW11bSBSU0UgdG8gbW9yZSBzaWduaWZpY2FudCBmaWd1cmVzIGJ1dCwgaW4gdmlldyBvZiB0aGUgc21hbGwgY2hhbmdlcyB3ZSBhcmUgYWNoaWV2aW5nLCAgbGl0dGxlIHdpbGwgYmUgYWNoaWV2ZWQgYnkgZ29pbmcgZnVydGhlci4gDQoNCmBgYHtyIGxhc3QgZml0IH0NCmNhc2VuZXcgPUNhc2VzXjAuMjY5DQptaDMgPSBsbShNYW5ob3Vyc34gY2FzZW5ldyAgKQ0Kc3VtbWFyeShtaDMpDQpwbG90KCBNYW5ob3VycyB+IGNhc2VuZXcsIG1haW49IkNhc2VzIF4gMC4yNjkiKSANCmFibGluZShjb2VmKG1oMykpDQpgYGANCg0KIyMjIENvbW1lbnRzIG9uIGRpcmVjdCBzZWFyY2gNCg0KRGlyZWN0IHNlYXJjaGluZyBvbiBhIGdyaWQgbWF5IGdldCB0byBhIHNvbHV0aW9uLCBidXQgdGhlIG1ldGhvZCBoYXMgZGlzYWR2YW50YWdlcy4gDQoNCjEuIEl0IGlzIHRpbWUtY29uc3VtaW5nLCByZXF1aXJpbmcgcHJvZ3JhbW1pbmcgYW5kL29yIGh1bWFuIGludGVydmVudGlvbi4NCg0KMi4gSWYgd2UgaGF2ZSBzZXZlcmFsIHBhcmFtZXRlcnMgdG8gc2VhcmNoIGZvciwgd2UgbWF5IGhhdmUgdG8gdXNlIGEgZ3JpZCBpbiB0d28sIHRocmVlIG9yIG1vcmUgZGltZW5zaW9ucywgd2hpY2ggaXMgY29tcHV0YXRpb25hbGx5IGludGVuc2l2ZS4gDQoNCjMuIEV2ZW4gb25jZSB3ZSBnZXQgdG8gYSBwb2ludCBlc3RpbWF0ZSwgd2UgZG8gbm90IGhhdmUgYSBzdGFuZGFyZCBlcnJvciBmb3IgdGhlIGVzdGltYXRlLCBzbyB3ZSBkbyBub3Qga25vdyByZWFsbHkgaG93IGFjY3VyYXRlIGl0IGlzLiAgV2UgY2Fubm90IGNvbXB1dGUgY29uZmlkZW5jZSBpbnRlcnZhbHMgb3IgaHlwb3RoZXNpcyB0ZXN0cyByZWdhcmRpbmcgdGhlIHBhcmFtZXRlcnMuIA0KDQpXZSBub3cgY29uc2lkZXIgYSBtb3JlIGNvbnZlbmllbnQgbWV0aG9kIHRoYXQgb3ZlcmNvbWVzIGFsbCB0aGVzZSBoYW5kaWNhcHMuIA0KDQojIyBUaGUgR2F1c3MtTmV3dG9uIEFsZ29yaXRobQ0KDQpUaGUgYWx0ZXJuYXRpdmUgdG8gdXNpbmcgZGlyZWN0IHNlYXJjaCBpcyB0byB1c2UgYSBtYXRoZW1hdGljYWwgdGVjaG5pcXVlIHN1Y2ggYXMgdGhlIEdhdXNzLU5ld3RvbiBBbGdvcml0aG0gLg0KDQpUaGUgaWRlYSBvZiB0aGUgYWxnb3JpdGhtIGlzIHRvIGFwcHJveGltYXRlIHRoZSBjdXJ2ZSAgICRmKHhfaTsgXGFscGhhLFxiZXRhLFxnYW1tYSkkICAgYnkgYSBzdW0gb2Ygc3RyYWlnaHQgbGluZXMgaW52b2x2aW5nICRcYWxwaGEsXGJldGEkIGFuZCAkXGdhbW1hJC4gIFRoYXQgaXMsIGEgICpsaW5lYXJpemVkKiAgYXBwcm94aW1hdGlvbiAgdG8gdGhlIGN1cnZlLiAgIA0KDQpUaGUgbWF0aGVtYXRpY2FsIGlkZWFzIGFyZSBpbiBhbiB3b3JkIGRvY3VtZW50IG9uIFN0cmVhbSwgYnV0IGFyZSBub3QgZXhhbWluYWJsZS4gDQoNClRoZSBtYWluIHBvaW50cyBhcmU6IA0KDQoxLglUaGUgcHJvY2VzcyBvZiBtb2RlbC1maXR0aW5nIGlzIGl0ZXJhdGl2ZS4gIFRoYXQgaXMsIGl0IGludm9sdmVzIGEgc2VyaWVzIG9mIHJlcGVhdGVkIGxpbmVhciByZWdyZXNzaW9ucyB3aXRoIHNsaWdodGx5IGRpZmZlcmVudCBkYXRhIGVhY2ggdGltZSwgY29udmVyZ2luZyB0byBhIGZpeGVkIGFuc3dlciBhcyB3ZSBnZXQgY2xvc2VyIGFuZCBjbG9zZXIgdG8gdGhlIG9wdGltYWwgZXN0aW1hdGVzLg0KDQoyLglUaGUgR2F1c3MtTmV3dG9uIG1ldGhvZCBpcyBndWFyYW50ZWVkIHRvIGNvbnZlcmdlIHF1aWNrbHkgdG8gdGhlIG9wdGltYWwgZXN0aW1hdGVzLCBwcm92aWRlZCB0aGF0IG91ciBpbml0aWFsIHN0YXJ0aW5nIHBvaW50IGlzIGNsb3NlIGVub3VnaC4NCg0KMy4JVGhlIG1ldGhvZCBhbHNvIHByb2R1Y2VzIGFwcHJveGltYXRlIGh5cG90aGVzaXMgdGVzdHMgYW5kIGNvbmZpZGVuY2UgaW50ZXJ2YWxzLiAgDQoNClRoZSBubHMoKSAgIG5vbmxpbmVhciBsZWFzdCBzcXVhcmVzIHByb2NlZHVyZSBpbXBsZW1lbnRzIHRoZSBHYXVzcy1OZXd0b24gYWxnb3JpdGhtLg0KDQpXZSBuZWVkIHRvIHNwZWNpZnkgdGhlIGZvcm11bGEsIGFuZCAocHJlZmVyYWJseSkgcmVhc29uYWJsZSBzdGFydGluZyB2YWx1ZXMuIA0KDQpgYGB7ciwgZWNobz1GfQ0Kcm0oYWxwaGEpDQpgYGANCg0KYGBge3IgZ2F1c3MtbmV3dG9uIDF9DQptYW5ob3Vycy5ubHMgPSBubHMoTWFuaG91cnN+YmV0YTAgKyBiZXRhMSooQ2FzZXNeYWxwaGEpLCBzdGFydD1saXN0KGFscGhhPTAuMjUsYmV0YTA9LTE2MDAwLGJldGExPTQ0MDApLCBkYXRhPW1hbmhvdXJzKSAgDQoNCnN1bW1hcnkobWFuaG91cnMubmxzKQ0KDQpwbG90KE1hbmhvdXJzfkNhc2VzKQ0KbGluZXMocHJlZGljdChtYW5ob3Vycy5ubHMpfiBDYXNlcykNCmBgYA0KDQpXZSBjYW4gY2FsY3VsYXRlIGFuIGFwcHJveGltYXRlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yICRcYWxwaGEkIHVzaW5nICAkJCBcaGF0XGFscGhhIFxwbSB0X3tuLXB9KDAuMDI1KSpTRShcaGF0XGFscGhhKSQkDQoNCmBgYHtyIGNvbmZpZGVuY2UgaW50ZXJ2YWx9DQpzdW1tYXJ5KG1hbmhvdXJzLm5scykkY29lZmZpY2llbnRzDQplc3Q9IHN1bW1hcnkobWFuaG91cnMubmxzKSRjb2VmZmljaWVudHNbMV0gIA0Kc2U9c3VtbWFyeShtYW5ob3Vycy5ubHMpJGNvZWZmaWNpZW50c1s0XQ0KbG93ZXI9IGVzdCArIHF0KDAuMDI1LGRmPTE1LTMpKnNlDQp1cHBlcj0gZXN0ICsgcXQoMC45NzUsZGY9MTUtMykqc2UNCmNiaW5kKGVzdCxzZSxsb3dlcix1cHBlcikNCg0KYGBgDQoNCldlIGNhbiBjb25jbHVkZSBmcm9tIHRoaXMgdGhhdCB3ZSBjb3VsZCBub3QgcmVqZWN0IGEgaHlwb3RoZXNpcyAgdGhhdCAkXGFscGhhJCBlcXVhbGxlZCBhbnkgdmFsdWUgYmV0d2VlbiAtMC4wNyBhbmQgMC42LiAgDQoNCkluIHBhcnRpY3VsYXIsIHdlIGNvdWxkIGhhdmUgc3R1Y2sgd2l0aCB0aGUgc3F1YXJlLXJvb3QgdHJhbnNmb3JtYXRpb24gKCRcYWxwaGE9MC41JCkgb3IgdXNlZCB0aGUgKmxvZyogdHJhbnNmb3JtYXRpb24gKHJlY2FsbCB0aGlzIHRha2VzIHRoZSBwbGFjZSBvZiAkXGFscGhhPTAkIGluIHRoZSBsYWRkZXIgb2YgcG93ZXJzKS4NCg0KIyMgRXhhbXBsZTogIEEgR3Jvd3RoIEN1cnZlIE1vZGVsIHdpdGggYW4gTWF4aW11bSAgDQoNClJlY2FsbCB0aGUgZmV2IGRhdGEgcmVsYXRpbmcgZm9yY2VkIGV4cGlyYXRpb24gdm9sdW1lIChGRVYpIHRvIGdpcmxzJyBhZ2VzLiBUaGlzIHNlZW1lZCB0byBmbGF0dGVuIG9mZiBhZnRlciBhYm91dCBhZ2UgMTAuIA0KDQpXZSB3aWxsIG1vZGVsIHRoZSBncm93dGggdXNpbmcgYSBsb2dpc3RpYyBjdXJ2ZSB3aXRoIHVua25vd24gbWF4aW11bS4NCg0KJCQgZih4X2k7IFxhbHBoYSxcYmV0YV8wLFxiZXRhXzEpID0gIFxhbHBoYX5+XGZyYWN7ZV57XGJldGFfMCsgXGJldGFfMSB4fX17IDErZV57XGJldGFfMCsgXGJldGFfMSB4fSB9ICArIFx2YXJlcHNpbG9uJCQNCg0KDQpgciB4ZnVuOjplbWJlZF9maWxlKCIuLi8uLi9kYXRhL0ZFVi5jc3YiKWAgDQoNCmBgYHtyIGdldEZldkRhdGEsIGV2YWw9LTEsIGVjaG89LTJ9DQpmZXYgPC0gcmVhZC5jc3YoZmlsZT0iZmV2LmNzdiIsIGhlYWRlcj1UUlVFKQ0KZmV2IDwtIHJlYWQuY3N2KGZpbGU9Ii4uLy4uL2RhdGEvZmV2LmNzdiIsIGhlYWRlcj1UUlVFKQ0KDQpGRVYgPSBmZXYkRkVWDQpBZ2U9IGZldiRBZ2UNCg0KcGxvdChGRVZ+QWdlICkNCg0KYGBgDQoNCg0KRnJvbSB0aGUgZ3JhcGggdGhlIG1heGltdW0gJFxhbHBoYSBcYXBwcm94IDMkLCBzbyBzZXR0aW5nIA0KJCRGRVYgXGFwcHJveCAzIH5+XGZyYWN7ZV57XGJldGFfMCArIFxiZXRhXzEgQWdlfX17IDErIGVee1xiZXRhXzAgKyBcYmV0YV8xIEFnZX19JCQgDQp3ZSBjYW4gcmVhcnJhbmdlIHRoaXMgYXMgIA0KJCQxKyBlXntcYmV0YV8wICsgXGJldGFfMSBBZ2V9ICAgXGFwcHJveCAgXGZyYWN7M317RkVWfSB+ICBlXntcYmV0YV8wICsgXGJldGFfMSBBZ2V9ICAkJCANCg0KR2F0aGVyIHVwIHRoZSAkZV57XGJldGFfMCArIFxiZXRhXzEgQWdlfSQgdGVybXMgDQoNCg0KJCQgXGZyYWN7MX17My9GRVYgLSAxfSBcYXBwcm94IGVee1xiZXRhXzAgKyBcYmV0YV8xIEFnZX0kJA0KYW5kIHRoZW4gaWYgd2UgdGFrZSBsb2dzIHdlIGNhbiB1c2UgdGhlIGxlZnQtaGFuZCBzaWRlIGFzIHRoZSAicmVzcG9uc2UiIGluIGEgcmVncmVzc2lvbg0KDQokJCBZX3tuZXd9ID0gXGxvZ1xsZWZ0KFxmcmFjezF9ezMvRkVWIC0gMX1ccmlnaHQpXGFwcHJveCAgXGJldGFfMCArIFxiZXRhXzEgQWdlICQkDQp0byBnaXZlIHVzIHN0YXJ0aW5nIHZhbHVlcyBmb3IgJFxiZXRhXzAkIGFuZCAkXGJldGFfMSQ6DQoNCmBgYHtyIHN0YXJ0fQ0Kc3RhcnQuY2FsYyA9bG0oIC1sb2coMy9GRVYgLTEpIH4gQWdlKQ0KDQpgYGANCg0KDQpgYGB7cn0NCnN1bW1hcnkoc3RhcnQuY2FsYykNCmBgYA0KDQpTbyB3ZSB3aWxsIHVzZSBzdGFydGluZyB2YWx1ZXMgJFxhbHBoYT0zJCwgJFxiZXRhXzA9IC0xLjMkIGFuZCAkXGJldGFfMT0wLjI4JA0KDQoNCmBgYHtyIGZldi5ubHN9DQpmZXYubmxzPSBubHMoICBGRVYgfmFscGhhKmV4cChiZXRhMCArIGJldGExKkFnZSkvKDErIGV4cChiZXRhMCArIGJldGExKkFnZSkpLCBzdGFydD1saXN0KGFscGhhPTMsYmV0YTA9IC0xLjMsYmV0YTE9IDAuMjgpICkgIA0Kc3VtbWFyeShmZXYubmxzKQ0KDQpwbG90KEZFVn5BZ2UgKQ0KbGluZXMoMzA6MjAwLzEwLHByZWRpY3QoZmV2Lm5scywgbmV3ZGF0YT1kYXRhLmZyYW1lKEFnZSA9MzA6MjAwLzEwKSApICAgLCBsdHk9MSxjb2w9Mixsd2Q9MykNCmBgYA0KDQoNClRoaXMgaGFzIGNvbnZlcmdlZC4gTm90ZSB0aGUgcmVzaWR1YWwgc3RhbmRhcmQgZXJyb3IgaXMgDQpgciByb3VuZChzdW1tYXJ5KGZldi5ubHMpJHNpZ21hLDQpYCwgd2hpY2ggaXMgIHNsaWdodGx5IGJpZ2dlciB0aGFuIGZvciB0aGUgcGllY2V3aXNlIG1vZGVsIGxvb2tlZCBhdCBlYXJsaWVyIChTPTAuMzkpIGJ1dCB0aGUgY3VydmUgc2VlbXMgbW9yZSByZWFzb25hYmxlIHRoYW4gaGF2aW5nIGEgc2hhcnAgY29ybmVyLiAgDQoNCldlIGNhbiBnZXQgYSBzbGlnaHRseSBiZXR0ZXIgZml0IGJ5IGFkZGluZyBhIHF1YWRyYXRpYyB0ZXJtIGluIEFnZS4gSW4gdGhlIGZvbGxvd2luZyB3ZSB1c2Ugc3RhcnRpbmcgdmFsdWUgJFxiZXRhXzI9MCQuDQoNCmBgYHtyIGZldi5ubHMyYX0NCmZldi5ubHMyPSBubHMoICBGRVYgfmFscGhhKmV4cChiZXRhMCArIGJldGExKkFnZSsgYmV0YTIqQWdlXjIpLygxKyBleHAoYmV0YTAgKyBiZXRhMSpBZ2UrIGJldGEyKkFnZV4yKSksIHN0YXJ0PWxpc3QoYWxwaGE9MyxiZXRhMD0gLTEuMyxiZXRhMT0wLjI4LCBiZXRhMj0wKSwgZGF0YT1mZXYpICANCnN1bW1hcnkoZmV2Lm5sczIpDQpwbG90KEZFVn5BZ2UpDQpsaW5lcygzMDoyMDAvMTAscHJlZGljdChmZXYubmxzMiwgbmV3ZGF0YT1kYXRhLmZyYW1lKEFnZSA9MzA6MjAwLzEwKSApICAgLCBsdHk9MSxjb2w9Mixsd2Q9MykNCg0KYGBgIA0KDQoNCg0KIyMgUGllY2V3aXNlIFJlZ3Jlc3Npb24gd2l0aCBhbiBVbmtub3duIEtub3QNCg0KUmVjYWxsIHRoYXQgc29tZXRpbWVzIHdlIHdpc2ggdG8gZml0IGEgcGllY2V3aXNlIHJlZ3Jlc3Npb24gbW9kZWwsIGJ1dCB0aGUgbG9jYXRpb24gb2YgdGhlIGtub3QgaXMgdW5rbm93bi4gDQoNClRoaXMgdGhlbiBpcyBiZWNvbWVzIGEgbm9ubGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwsIHdpdGggdGhlIGxvY2F0aW9uIG9mIHRoZSBrbm90IHRha2luZyB0aGUgcm9sZSBvZiB0aGUgYWxwaGEgcGFyYW1ldGVyIGFib3ZlIChub3QgbWVhbmluZyB0aGF0IGl0IGVudGVycyB0aGUgZXF1YXRpb24gdGhlIHNhbWUgd2F5LCBidXQgdGhhdCBpZiB0aGUga25vdCBpcyBrbm93biwgdGhlbiBldmVyeSBvdGhlciBwYXJhbWV0ZXIgY2FuIGJlIGZpdHRlZCBieSAgbGluZWFyIHJlZ3Jlc3Npb24pLiAgIA0KDQpTdHJpY3RseSBzcGVha2luZyB0aGUgIEdhdXNzLU5ld3RvbiBhbGdvcml0aG0gZG9lcyBub3QgcXVpdGUgd29yaywgYXMgdGhlIGtub3QgZ2l2ZXMgYSDigJhjb3JuZXLigJkgdG8gdGhlIGxpbmVzLCBvciBldmVuIGEganVtcCBpbiB0aGUgbGV2ZWwgb2YgdGhlIGxpbmVzLCBhbmQgdGhpcyBtZWFucyB0aGF0IHRoZSBjYWxjdWx1cyBvbiB3aGljaCB0aGUgbWV0aG9kIHdhcyBvcmlnaW5hbGx5IGJhc2VkIGJyZWFrcyBkb3duLiAgSG93ZXZlciB0aGUgbmxzKCkgcm91dGluZSBjYW4gYmUgbWFkZSB0byBjb3BlLiANCg0KIyMjIE5vbmxpbmVhciByZWdyZXNzaW9uIGZvciBhIGNoYW5nZS1zbG9wZSBtb2RlbDogdGhlIFRoYWkgQmFodC4NCg0KVGhlIGRhdGEgaGVyZSByZWZlciB0byB0aGUgVGhhaSBCYWh0ICBFeGNoYW5nZSBSYXRlICRZJCB2cyBUaW1lICAodGltZSBtZWFzdXJlZCBpbiBodW5kcmVkcyBvZiBkYXlzLCAgKmhEYXlzKi4gKSAgIFRoZSBncmFwaCBiZWxvdyBzaG93cyAkWSQgdnMgKmhEYXlzKiB3aXRoIGEgY3ViaWMgbW9kZWwuICBUaGUgbW9kZWwgbG9va3MgbGlrZSBpdCB3b3VsZCBiZSB1bnNhZmUgZm9yIGV4dHJhcG9sYXRpb24gb24gdGhlIHJpZ2h0Lg0KDQoNCmByIHhmdW46OmVtYmVkX2ZpbGUoIi4uLy4uL2RhdGEvYmFodC5jc3YiKWAgDQoNCmBgYHtyIGJhaHQsIGV2YWw9LTEsIGVjaG89LTJ9DQpiYWh0PSByZWFkLmNzdiggImJhaHQuY3N2IixoZWFkZXI9VFJVRSkNCmJhaHQ9IHJlYWQuY3N2KCAiLi4vLi4vZGF0YS9iYWh0LmNzdiIsaGVhZGVyPVRSVUUpDQoNClk9IGJhaHQkWQ0KaERheXMgPSBiYWh0JGhEYXlzIA0KDQpwbG90KFkgfmhEYXlzKSANCmN1YmljID0gbG0oWX5wb2x5KGhEYXlzLDMpKTsgICBzdW1tYXJ5KGN1YmljKSRzaWdtYQ0KbGluZXMocHJlZGljdChjdWJpYyl+aERheXMsIGNvbD0yLGx3ZD0yKQ0KYGBgDQoNCldlIGNvbnNpZGVyIGEgcGllY2V3aXNlIG1vZGVsIA0KJCQgWSA9IFxiZXRhXzAgKyBcYmV0YV8xLiBoRGF5cyArIFxiZXRhXzIgKGhEYXlzLWspXysgJCQNCndoZXJlICRrJCBpcyB1bmtub3duIGJ1dCBwcm9iYWJseSBuZWFyIDIuIFdlIHdpbGwgdXNlIHRoaXMgZ3Vlc3MgdG8gZ2l2ZSBzdGFydGluZyB2YWx1ZXMgZm9yIHRoZSBvdGhlciBwYXJhbWV0ZXJzLg0KDQpgYGB7ciBwaWVjZXdpc2V9DQpzdGFydC5ndWVzcyA9IGxtKCBZIH4gaERheXMgKyBJKCAoYWJzKGhEYXlzLTIpK2hEYXlzLTIpLzIpICkNCnN1bW1hcnkoc3RhcnQuZ3Vlc3MpDQpgYGANClRoaXMgZ2l2ZXMgc3RhcnRpbmcgZXN0aW1hdGVzICRcYmV0YV8wXGFwcHJveCAwLjM0LCB+fiBcYmV0YV8xIFxhcHByb3ggLTAuMDIsfn4gXGJldGFfMiBcYXBwcm94IDAuMDIyLCB+fms9MiQuDQoNClRoZSBmb2xsb3dpbmcgY29kZSBmaXRzIHRoZSBtb2RlbCwgYnV0IHNvbWUgaXRlcmF0aW9uIGNvbnRyb2wgcGFyYW1ldGVycyB3ZXJlIG5lZWRlZCwgcHJlc3VtYWJseSBiZWNhdXNlIG9mIHRoZSBjb3JuZXIuIA0KKE90aGVyIHN0YXRpc3RpY2FsIHNvZnR3YXJlIHN1Y2ggYXMgU1BTUyBhbmQgTWluaXRhYiAgaGF2ZSBsZXNzIHByb2JsZW1zIGZpdHRpbmcgdGhpcyBtb2RlbC4pDQoNCmBgYHtyIHBpZWNld2lzZS5ubHN9DQoNCmJhaHQubmxzID0gbmxzKCAgWSB+IGJldGEwICsgYmV0YTEqaERheXMgKyBiZXRhMipJKCAoYWJzKGhEYXlzLWspK2hEYXlzLWspLzIpICwgc3RhcnQ9bGlzdChiZXRhMD0wLjM0ICwgYmV0YTE9IC0wLjAyLGJldGEyPSAwLjAyMiwgaz0yKSwgZGF0YT1iYWh0LCBubHMuY29udHJvbCh3YXJuT25seT1UUlVFLCBtaW5GYWN0b3I9MS8yMDQ4KSkgIA0Kc3VtbWFyeShiYWh0Lm5scykNCnBsb3QoWX5oRGF5cykNCmxpbmVzKHByZWRpY3QoYmFodC5ubHMgKX5oRGF5cyAsICAgY29sPTIsbHdkPTMpDQoNCmBgYA0KDQpUaGUgZ3JhcGggc2VlbXMgbXVjaCBtb3JlIHJlYXNvbmFibGUuIE5vdGUgaXQgY2hhbmdlZCB0aGUgJGskIHRvIDEuNTcuIA0KDQpJbiB0ZXJtcyBvZiByZXNpZHVhbCBzdGFuZGFyZCBlcnJvciwgd2hpY2ggaXMgdGhlIGJldHRlci1maXR0aW5nIG1vZGVsLCB0aGUgY3ViaWMgb3IgdGhlIGNoYW5nZS1wb2ludCBtb2RlbD8gKEZyb20gYWJvdmUgdGhlIGN1YmljIGdyYXBoICBTID1gciByb3VuZChzdW1tYXJ5KGN1YmljKSRzaWdtYSwgNilgKS4NCg0K