Part 1

Lets first import the data:

bone <- read.csv("bone.csv", as.is=T)

** a) Begin with a plot of spnbmd against age, colorcoded by gender. **

plot (bone$age, bone$spnbmd,  col=ifelse(bone$gender=="male","blue","red"), pch = 8)

Does it appear that there are differences in bone mineral density (BMD) trajectories between males and females?

There does not appear to be an extreme difference between the BMD trajectories between males and females. But there is a bit more variablility of bone density accross the different ages for males compared to females. At a younger age the males seem to have lower BMD, and at around age 15 they seem to have a higer BMD compared to females. When becoming older though, at ages above 20, they appear to have the same trajectories.

** fit a series of models to spnbmd using a polynomial of age for each gender separately. Use knots at c1 and c2, where c1 and c2 are the 33rd and 67th percentiles of the data, respectively.**

install.packages("lsr")
Installing package into <U+393C><U+3E31>C:/Users/Nicole Salomons/Documents/R/win-library/3.3<U+393C><U+3E32>
(as <U+393C><U+3E31>lib<U+393C><U+3E32> is unspecified)
trying URL 'https://cran.rstudio.com/bin/windows/contrib/3.3/lsr_0.5.zip'
Content type 'application/zip' length 140820 bytes (137 KB)
downloaded 137 KB
package ‘lsr’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\Nicole Salomons\AppData\Local\Temp\RtmpUj5Bik\downloaded_packages
library(splines)
library(ISLR)
library(lsr)
bones2 = bone
bones2$agegrp <- quantileCut(bones2$age, 3)
labs <- levels(bones2$agegrp)
bds <- cbind(lower = as.numeric( sub("\\((.+),.*", "\\1", labs) ),
      upper = as.numeric( sub("[^,]*,([^]]*)\\]", "\\1", labs) ))
bds <- c(bds[,1], bds[nrow(bds),2])
bones.male = subset(bones2, gender == "male")
bones.female = subset(bones2, gender == "female")

** 1) Construct the model **

model.quadratic.male <- lm(spnbmd ~ agegrp*poly(age, 2, raw=T), data=bones.male)
model.quadratic.female <- lm(spnbmd ~ agegrp*poly(age, 2, raw=T), data=bones.female)

** 2) State the number of coeficients fitted, including the intercept **

length(coef(model.quadratic.male))
[1] 9
length(coef(model.quadratic.female))
[1] 9

The number of coeficients is always the same for both male and female. For a quadratic model there are 9 coeficients. A quadratic equation has 3 coeficentes. And each of the 3 age groups has its own quadratic model. So there is 3*3 = 9 coeficients.

** 3) Show a plot with the fitted models superimposed **

plot (bone$age, bone$spnbmd,  col=ifelse(bone$gender=="male","blue","red"), pch = 8)
abline(v=bds, lwd=2, lty=3, col="blue")
curve(predict(model.quadratic.male, data.frame(agegrp=cut(x, breaks=bds),
                              age=x)), lwd=2, col="blue", add=TRUE)
curve(predict(model.quadratic.female, data.frame(agegrp=cut(x, breaks=bds),
                              age=x)), lwd=2, col="red", add=TRUE)

The plot seems to show a lot of variabiity between the groups and jumps at the knots. This is not great because we would like to create a smoother model, which also does not have these extreme jumps at knots. Therefore the continuous models might be better options. The plot does show however that there is a clear difference between males and females before around the age of 18.

Continuous piecewise quadratic

** 1) Construct the model **

#bds
v <- seq(1, 10, by=.1)
#pmax(v-3, 0) 
model.quadratic.continuous.male <- lm(spnbmd ~ poly(age,2, raw=T) + 
            poly(pmax(I(age-bds[2]),0), 2, raw=T) +
            poly(pmax(I(age-bds[3]),0), 2, raw=T), data=bones.male)
model.quadratic.continuous.female <- lm(spnbmd ~ poly(age,3, raw=T) + 
            poly(pmax(I(age-bds[2]),0), 2, raw=T) +
            poly(pmax(I(age-bds[3]),0), 2, raw=T), data=bones.female)

** 2) State the number of coeficients fitted, including the intercept **

length(coef(model.quadratic.continuous.male))
[1] 7

Here the knots are continuous. So we lose 2 coeficients from the model, both at the knots. So instead of 9, now we have 7 coeficients.

** 3) Show a plot with the fitted models superimposed **

plot (bone$age, bone$spnbmd,  col=ifelse(bone$gender=="male","blue","red"), pch = 8)
abline(v=bds, lwd=2, lty=3, col="blue")
curve(predict(model.quadratic.continuous.male, data.frame(agegrp=cut(x, breaks=bds),
                              age=x)), lwd=2, col="blue", add=TRUE)
curve(predict(model.quadratic.continuous.female, data.frame(agegrp=cut(x, breaks=bds),
                              age=x)), lwd=2, col="red", add=TRUE)

This is a bit nicer, because it is continuous at the knots. But There are still sharp edges at the knots, which make it seem that at the ages of the knots drastic changes in spnbmd occur, which is not true. So the following models will take derivates, to have smoother knots.

Continuous piecewise quadratic with continuous first derivative

** 1) Construct the model **

model.quadratic.continuous.1derivative.male <- lm(spnbmd ~ poly(age,2, raw=T) + pmax(I(age-bds[2])^2,0) + pmax(I(age-bds[3])^2,0), data=bones.male)
model.quadratic.continuous.1derivative.female <- lm(spnbmd ~ poly(age,2, raw=T) + pmax(I(age-bds[2])^2,0) + pmax(I(age-bds[3])^2,0), data=bones.female)

** 2) State the number of coeficients fitted, including the intercept **

length(coef(model.quadratic.continuous.1derivative.female))
[1] 5

Taking the first derivate of a quadratic equation, means we lose the first terms for all but the first spline. So no we have 9-2 = 5 coeficients.

** 3) Show a plot with the fitted models superimposed **

plot (bone$age, bone$spnbmd,  col=ifelse(bone$gender=="male","blue","red"), pch = 8)
abline(v=bds, lwd=2, lty=3, col="blue")
curve(predict(model.quadratic.continuous.1derivative.male, data.frame(agegrp=cut(x, breaks=bds),
                              age=x)), lwd=2, col="blue", add=TRUE)
prediction from a rank-deficient fit may be misleading
curve(predict(model.quadratic.continuous.1derivative.female, data.frame(agegrp=cut(x, breaks=bds),
                              age=x)), lwd=2, col="red", add=TRUE)
prediction from a rank-deficient fit may be misleading

This model looks much nicer than the previous ones, it looks consistent accross the different groups. But maybe just a quadratic model is not detailed enough, so below there is a cubic continuous spline model.

Continuous piecewise cubic with continuous first and second derivatives (cubic spline)

** 1) Construct the model **

model.cubic.spline.male <- lm(spnbmd ~ poly(age,3, raw=T) + pmax(I(age-bds[2])^3,0) + 
            pmax(I(age-bds[3])^3,0), data=bones.male)
model.cubic.spline.female <- lm(spnbmd ~ poly(age,3, raw=T) + pmax(I(age-bds[2])^3,0) + 
            pmax(I(age-bds[3])^3,0), data=bones.female)

** 2) State the number of coeficients fitted, including the intercept **

length(coef(model.cubic.spline.male))
[1] 6

We fit cubic models for each of the groups, which has a total of 12 coeficients. But for all but the first group we only use the last cubic coefiecient. So there is a total of 4(first group) + 2 = 6.

** 3) Show a plot with the fitted models superimposed **

plot (bone$age, bone$spnbmd,  col=ifelse(bone$gender=="male","blue","red"), pch = 8)
abline(v=bds, lwd=2, lty=3, col="blue")
curve(predict(model.cubic.spline.male, data.frame(agegrp=cut(x, breaks=bds),
                              age=x)), lwd=2, col="blue", add=TRUE)
curve(predict(model.cubic.spline.female, data.frame(agegrp=cut(x, breaks=bds),
                              age=x)), lwd=2, col="red", add=TRUE)

This model is more complex than the previous models because it is cubic. But it looks like it represents the data well, and it is continuous at the knots.

Part 3

** Describe which model in Part 2 appears most suited to the data, based on visual inspection. Are there aspects of this fitted model that seem concerning? **

Both models for the Continuous piecewise quadratic with continuous first derivative(c) and the cubic spline(d) seem like good models. If a more detailed model is needed maybe d might be a better choice. If a simpler and easier to interpret model is needed, then c might be better.

Personally I think d is a better model. It seems to capture the data well, and create good models for both male and female data points. It also clearly shows the differences of models that occurr do to the different regions of age.

An aspect that is concerning about this model is the start and end ages, where it seems to quicly go up or down, which would not be realistic with the data. For example if we tried to predict a new female data point with age 8. It probably would be around -0.2, which is probalby very incorrect, because it cant be negative. For both male and female, predicitions of lower ages than 10, would probalby not yield good results.

** For this best approach, implement 5-fold cross-validation to select between using 2, 3, 4, 5, 6, or 7 knots, spaced out at uniform percentiles of the data. **

for ( j in 2:7)
{
  mse.male = 0
  mse.female = 0
  bones3 = bone
  bones3$agegrp <- quantileCut(bones3$age, j+1)
  bones.male = subset(bones3, gender == "male")
  bones.female = subset(bones3, gender == "female")
  labs <- levels(bones3$agegrp)
  bds <- cbind(lower = as.numeric( sub("\\((.+),.*", "\\1", labs) ),
      upper = as.numeric( sub("[^,]*,([^]]*)\\]", "\\1", labs) ))
  bds <- c(bds[,1], bds[nrow(bds),2])
  folds.female <- as.numeric(cut(1:nrow(bones.female), 5))
  folds.male <- as.numeric(cut(1:nrow(bones.male), 5))
  
  for (i in 1:5)
  {
    train.female <- bones.female[folds.female !=i,]
    val.female <- bones.female[folds.female == i,]
    train.male <- bones.male[folds.male !=i,]
    val.male <- bones.male[folds.male == i,]
    model.female=lm(spnbmd ~ bs(age ,knots = bds ),data=train.female) 
    model.male=lm(spnbmd ~ bs(age ,knots = bds ),data=train.male) 
    pred.female = predict(model.female)
    pred.male = predict(model.male)
    mse.female = mse.female + mean((pred.female - train.female$spnbmd)^2)
    mse.male = mse.male + mean((pred.male - train.male$spnbmd)^2)
  }
  cat(sprintf ("female - %s - s%f\n", j,mse.female))
  cat(sprintf ("male - %s - s%f\n", j,mse.male))
}
female - 2 - s0.006190
male - 2 - s0.008183
female - 3 - s0.005904
male - 3 - s0.008170
female - 4 - s0.005860
male - 4 - s0.008153
female - 5 - s0.005869
male - 5 - s0.008111
female - 6 - s0.005845
male - 6 - s0.008103
female - 7 - s0.005831
male - 7 - s0.008049
    

** What choice of knots works best? Refit a model on all of the data with this number of knots and plot the model. **

The best model used 4 knots (5 different groups), where the error was 0.005860. I chose this model because it has a low error rate, and it does not have too many knots, separating the data too much, and creating too many different groups.

Bellow is the plot of the final model, separated by gender.

bones2 = bone
bones2$agegrp <- quantileCut(bones2$age, 5)
labs <- levels(bones2$agegrp)
bds <- cbind(lower = as.numeric( sub("\\((.+),.*", "\\1", labs) ),
      upper = as.numeric( sub("[^,]*,([^]]*)\\]", "\\1", labs) ))
bds <- c(bds[,1], bds[nrow(bds),2])
bones.male = subset(bones2, gender == "male")
bones.female = subset(bones2, gender == "female")
final.male <- lm(spnbmd ~ bs(age ,knots = bds ),data=train.male) 
final.female <- lm(spnbmd ~ bs(age ,knots = bds ),data=train.female) 
plot (bone$age, bone$spnbmd,  col=ifelse(bone$gender=="male","blue","red"), pch = 8)
abline(v=bds, lwd=2, lty=3, col="blue")
curve(predict(final.male, data.frame(agegrp=cut(x, breaks=bds),
                              age=x)), lwd=2, col="blue", add=TRUE)
some 'x' values beyond boundary knots may cause ill-conditioned basesprediction from a rank-deficient fit may be misleading
curve(predict(final.female, data.frame(agegrp=cut(x, breaks=bds),
                              age=x)), lwd=2, col="red", add=TRUE)
some 'x' values beyond boundary knots may cause ill-conditioned basesprediction from a rank-deficient fit may be misleading

This final model with 4 knots captures the data very well. It shows the curve for women at a low age, were their spnbmd values are higher, then drops and stays relatively consistent after age 15. The peak for men is a bit later, at around age 15, and at around age 20 it stabilizes, being the same as the womans spnbmd.

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojUGFydCAxDQoNCkxldHMgZmlyc3QgaW1wb3J0IHRoZSBkYXRhOg0KYGBge3J9DQpib25lIDwtIHJlYWQuY3N2KCJib25lLmNzdiIsIGFzLmlzPVQpDQpgYGANCg0KDQoqKiBhKSBCZWdpbiB3aXRoIGEgcGxvdCBvZiBzcG5ibWQgYWdhaW5zdCBhZ2UsIGNvbG9yY29kZWQgYnkgZ2VuZGVyLiAqKiANCmBgYHtyfQ0KcGxvdCAoYm9uZSRhZ2UsIGJvbmUkc3BuYm1kLCAgY29sPWlmZWxzZShib25lJGdlbmRlcj09Im1hbGUiLCJibHVlIiwicmVkIiksIHBjaCA9IDgpDQpgYGANCioqRG9lcyBpdCBhcHBlYXIgdGhhdCB0aGVyZSBhcmUgZGlmZmVyZW5jZXMgaW4gYm9uZSBtaW5lcmFsIGRlbnNpdHkgKEJNRCkgdHJhamVjdG9yaWVzIGJldHdlZW4gbWFsZXMgYW5kIGZlbWFsZXM/ICoqDQoNClRoZXJlIGRvZXMgbm90IGFwcGVhciB0byBiZSBhbiBleHRyZW1lIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgQk1EIHRyYWplY3RvcmllcyBiZXR3ZWVuIG1hbGVzIGFuZCBmZW1hbGVzLiBCdXQgdGhlcmUgaXMgYSBiaXQgbW9yZSB2YXJpYWJsaWxpdHkgb2YgYm9uZSBkZW5zaXR5IGFjY3Jvc3MgdGhlIGRpZmZlcmVudCBhZ2VzIGZvciBtYWxlcyBjb21wYXJlZCB0byBmZW1hbGVzLiBBdCBhIHlvdW5nZXIgYWdlIHRoZSBtYWxlcyBzZWVtIHRvIGhhdmUgbG93ZXIgQk1ELCBhbmQgYXQgYXJvdW5kIGFnZSAxNSB0aGV5IHNlZW0gdG8gaGF2ZSBhIGhpZ2VyIEJNRCBjb21wYXJlZCB0byBmZW1hbGVzLiBXaGVuIGJlY29taW5nIG9sZGVyIHRob3VnaCwgYXQgYWdlcyBhYm92ZSAyMCwgdGhleSBhcHBlYXIgdG8gaGF2ZSB0aGUgc2FtZSB0cmFqZWN0b3JpZXMuDQoNCg0KKiogZml0IGEgc2VyaWVzIG9mIG1vZGVscyB0byBzcG5ibWQgdXNpbmcgYSBwb2x5bm9taWFsIG9mIGFnZSBmb3IgZWFjaCBnZW5kZXIgc2VwYXJhdGVseS4gVXNlIGtub3RzIGF0IGMxIGFuZCBjMiwgd2hlcmUgYzEgYW5kIGMyIGFyZSB0aGUgMzNyZCBhbmQgNjd0aCBwZXJjZW50aWxlcyBvZiB0aGUgZGF0YSwgcmVzcGVjdGl2ZWx5LioqDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoImxzciIpDQpsaWJyYXJ5KHNwbGluZXMpDQpsaWJyYXJ5KElTTFIpDQpsaWJyYXJ5KGxzcikNCmBgYA0KDQoNCmBgYHtyfQ0KYm9uZXMyID0gYm9uZQ0KDQpib25lczIkYWdlZ3JwIDwtIHF1YW50aWxlQ3V0KGJvbmVzMiRhZ2UsIDMpDQpsYWJzIDwtIGxldmVscyhib25lczIkYWdlZ3JwKQ0KYmRzIDwtIGNiaW5kKGxvd2VyID0gYXMubnVtZXJpYyggc3ViKCJcXCgoLispLC4qIiwgIlxcMSIsIGxhYnMpICksDQogICAgICB1cHBlciA9IGFzLm51bWVyaWMoIHN1YigiW14sXSosKFteXV0qKVxcXSIsICJcXDEiLCBsYWJzKSApKQ0KYmRzIDwtIGMoYmRzWywxXSwgYmRzW25yb3coYmRzKSwyXSkNCg0KYm9uZXMubWFsZSA9IHN1YnNldChib25lczIsIGdlbmRlciA9PSAibWFsZSIpDQpib25lcy5mZW1hbGUgPSBzdWJzZXQoYm9uZXMyLCBnZW5kZXIgPT0gImZlbWFsZSIpDQpgYGANCg0KDQoqKiAxKSBDb25zdHJ1Y3QgdGhlIG1vZGVsICoqDQoNCmBgYHtyfQ0KbW9kZWwucXVhZHJhdGljLm1hbGUgPC0gbG0oc3BuYm1kIH4gYWdlZ3JwKnBvbHkoYWdlLCAyLCByYXc9VCksIGRhdGE9Ym9uZXMubWFsZSkNCm1vZGVsLnF1YWRyYXRpYy5mZW1hbGUgPC0gbG0oc3BuYm1kIH4gYWdlZ3JwKnBvbHkoYWdlLCAyLCByYXc9VCksIGRhdGE9Ym9uZXMuZmVtYWxlKQ0KYGBgDQoNCg0KKiogMikgU3RhdGUgdGhlIG51bWJlciBvZiBjb2VmaWNpZW50cyBmaXR0ZWQsIGluY2x1ZGluZyB0aGUgaW50ZXJjZXB0ICoqDQoNCmBgYHtyfQ0KbGVuZ3RoKGNvZWYobW9kZWwucXVhZHJhdGljLm1hbGUpKQ0KbGVuZ3RoKGNvZWYobW9kZWwucXVhZHJhdGljLmZlbWFsZSkpDQpgYGANCg0KVGhlIG51bWJlciBvZiBjb2VmaWNpZW50cyBpcyBhbHdheXMgdGhlIHNhbWUgZm9yIGJvdGggbWFsZSBhbmQgZmVtYWxlLiBGb3IgYSBxdWFkcmF0aWMgbW9kZWwgdGhlcmUgYXJlIDkgY29lZmljaWVudHMuIEEgcXVhZHJhdGljIGVxdWF0aW9uIGhhcyAzIGNvZWZpY2VudGVzLiBBbmQgZWFjaCBvZiB0aGUgMyBhZ2UgZ3JvdXBzIGhhcyBpdHMgb3duIHF1YWRyYXRpYyBtb2RlbC4gU28gdGhlcmUgaXMgMyozID0gOSBjb2VmaWNpZW50cy4NCg0KKiogMykgU2hvdyBhIHBsb3Qgd2l0aCB0aGUgZml0dGVkIG1vZGVscyBzdXBlcmltcG9zZWQgKioNCg0KYGBge3J9DQpwbG90IChib25lJGFnZSwgYm9uZSRzcG5ibWQsICBjb2w9aWZlbHNlKGJvbmUkZ2VuZGVyPT0ibWFsZSIsImJsdWUiLCJyZWQiKSwgcGNoID0gOCkNCmFibGluZSh2PWJkcywgbHdkPTIsIGx0eT0zLCBjb2w9ImJsdWUiKQ0KDQpjdXJ2ZShwcmVkaWN0KG1vZGVsLnF1YWRyYXRpYy5tYWxlLCBkYXRhLmZyYW1lKGFnZWdycD1jdXQoeCwgYnJlYWtzPWJkcyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9eCkpLCBsd2Q9MiwgY29sPSJibHVlIiwgYWRkPVRSVUUpDQpjdXJ2ZShwcmVkaWN0KG1vZGVsLnF1YWRyYXRpYy5mZW1hbGUsIGRhdGEuZnJhbWUoYWdlZ3JwPWN1dCh4LCBicmVha3M9YmRzKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT14KSksIGx3ZD0yLCBjb2w9InJlZCIsIGFkZD1UUlVFKQ0KDQpgYGANCg0KVGhlIHBsb3Qgc2VlbXMgdG8gc2hvdyBhIGxvdCBvZiB2YXJpYWJpaXR5IGJldHdlZW4gdGhlIGdyb3VwcyBhbmQganVtcHMgYXQgdGhlIGtub3RzLiBUaGlzIGlzIG5vdCBncmVhdCBiZWNhdXNlIHdlIHdvdWxkIGxpa2UgdG8gY3JlYXRlIGEgc21vb3RoZXIgbW9kZWwsIHdoaWNoIGFsc28gZG9lcyBub3QgaGF2ZSB0aGVzZSBleHRyZW1lIGp1bXBzIGF0IGtub3RzLiBUaGVyZWZvcmUgdGhlIGNvbnRpbnVvdXMgbW9kZWxzIG1pZ2h0IGJlIGJldHRlciBvcHRpb25zLiBUaGUgcGxvdCBkb2VzIHNob3cgaG93ZXZlciB0aGF0IHRoZXJlIGlzIGEgY2xlYXIgZGlmZmVyZW5jZSBiZXR3ZWVuIG1hbGVzIGFuZCBmZW1hbGVzIGJlZm9yZSBhcm91bmQgdGhlIGFnZSBvZiAxOC4NCg0KIyMgQ29udGludW91cyBwaWVjZXdpc2UgcXVhZHJhdGljDQoNCioqIDEpIENvbnN0cnVjdCB0aGUgbW9kZWwgKioNCg0KYGBge3J9DQojYmRzDQp2IDwtIHNlcSgxLCAxMCwgYnk9LjEpDQojcG1heCh2LTMsIDApIA0KbW9kZWwucXVhZHJhdGljLmNvbnRpbnVvdXMubWFsZSA8LSBsbShzcG5ibWQgfiBwb2x5KGFnZSwyLCByYXc9VCkgKyANCiAgICAgICAgICAgIHBvbHkocG1heChJKGFnZS1iZHNbMl0pLDApLCAyLCByYXc9VCkgKw0KICAgICAgICAgICAgcG9seShwbWF4KEkoYWdlLWJkc1szXSksMCksIDIsIHJhdz1UKSwgZGF0YT1ib25lcy5tYWxlKQ0KDQptb2RlbC5xdWFkcmF0aWMuY29udGludW91cy5mZW1hbGUgPC0gbG0oc3BuYm1kIH4gcG9seShhZ2UsMywgcmF3PVQpICsgDQogICAgICAgICAgICBwb2x5KHBtYXgoSShhZ2UtYmRzWzJdKSwwKSwgMiwgcmF3PVQpICsNCiAgICAgICAgICAgIHBvbHkocG1heChJKGFnZS1iZHNbM10pLDApLCAyLCByYXc9VCksIGRhdGE9Ym9uZXMuZmVtYWxlKQ0KYGBgDQoNCg0KKiogMikgU3RhdGUgdGhlIG51bWJlciBvZiBjb2VmaWNpZW50cyBmaXR0ZWQsIGluY2x1ZGluZyB0aGUgaW50ZXJjZXB0ICoqDQoNCmBgYHtyfQ0KbGVuZ3RoKGNvZWYobW9kZWwucXVhZHJhdGljLmNvbnRpbnVvdXMubWFsZSkpDQpgYGANCkhlcmUgdGhlIGtub3RzIGFyZSBjb250aW51b3VzLiBTbyB3ZSBsb3NlIDIgY29lZmljaWVudHMgZnJvbSB0aGUgbW9kZWwsIGJvdGggYXQgdGhlIGtub3RzLiBTbyBpbnN0ZWFkIG9mIDksIG5vdyB3ZSBoYXZlIDcgY29lZmljaWVudHMuDQoNCioqIDMpIFNob3cgYSBwbG90IHdpdGggdGhlIGZpdHRlZCBtb2RlbHMgc3VwZXJpbXBvc2VkICoqDQoNCmBgYHtyfQ0KcGxvdCAoYm9uZSRhZ2UsIGJvbmUkc3BuYm1kLCAgY29sPWlmZWxzZShib25lJGdlbmRlcj09Im1hbGUiLCJibHVlIiwicmVkIiksIHBjaCA9IDgpDQphYmxpbmUodj1iZHMsIGx3ZD0yLCBsdHk9MywgY29sPSJibHVlIikNCg0KY3VydmUocHJlZGljdChtb2RlbC5xdWFkcmF0aWMuY29udGludW91cy5tYWxlLCBkYXRhLmZyYW1lKGFnZWdycD1jdXQoeCwgYnJlYWtzPWJkcyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9eCkpLCBsd2Q9MiwgY29sPSJibHVlIiwgYWRkPVRSVUUpDQpjdXJ2ZShwcmVkaWN0KG1vZGVsLnF1YWRyYXRpYy5jb250aW51b3VzLmZlbWFsZSwgZGF0YS5mcmFtZShhZ2VncnA9Y3V0KHgsIGJyZWFrcz1iZHMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWdlPXgpKSwgbHdkPTIsIGNvbD0icmVkIiwgYWRkPVRSVUUpDQpgYGANCg0KVGhpcyBpcyBhIGJpdCBuaWNlciwgYmVjYXVzZSBpdCBpcyBjb250aW51b3VzIGF0IHRoZSBrbm90cy4gQnV0IFRoZXJlIGFyZSBzdGlsbCBzaGFycCBlZGdlcyBhdCB0aGUga25vdHMsIHdoaWNoIG1ha2UgaXQgc2VlbSB0aGF0IGF0IHRoZSBhZ2VzIG9mIHRoZSBrbm90cyBkcmFzdGljIGNoYW5nZXMgaW4gc3BuYm1kIG9jY3VyLCB3aGljaCBpcyBub3QgdHJ1ZS4gU28gdGhlIGZvbGxvd2luZyBtb2RlbHMgd2lsbCB0YWtlIGRlcml2YXRlcywgdG8gaGF2ZSBzbW9vdGhlciBrbm90cy4NCg0KIyMgQ29udGludW91cyBwaWVjZXdpc2UgcXVhZHJhdGljIHdpdGggY29udGludW91cyBmaXJzdCBkZXJpdmF0aXZlDQoNCioqIDEpIENvbnN0cnVjdCB0aGUgbW9kZWwgKioNCg0KYGBge3J9DQptb2RlbC5xdWFkcmF0aWMuY29udGludW91cy4xZGVyaXZhdGl2ZS5tYWxlIDwtIGxtKHNwbmJtZCB+IHBvbHkoYWdlLDIsIHJhdz1UKSArIHBtYXgoSShhZ2UtYmRzWzJdKV4yLDApICsgcG1heChJKGFnZS1iZHNbM10pXjIsMCksIGRhdGE9Ym9uZXMubWFsZSkNCm1vZGVsLnF1YWRyYXRpYy5jb250aW51b3VzLjFkZXJpdmF0aXZlLmZlbWFsZSA8LSBsbShzcG5ibWQgfiBwb2x5KGFnZSwyLCByYXc9VCkgKyBwbWF4KEkoYWdlLWJkc1syXSleMiwwKSArIHBtYXgoSShhZ2UtYmRzWzNdKV4yLDApLCBkYXRhPWJvbmVzLmZlbWFsZSkNCmBgYA0KDQoNCioqIDIpIFN0YXRlIHRoZSBudW1iZXIgb2YgY29lZmljaWVudHMgZml0dGVkLCBpbmNsdWRpbmcgdGhlIGludGVyY2VwdCAqKg0KDQpgYGB7cn0NCmxlbmd0aChjb2VmKG1vZGVsLnF1YWRyYXRpYy5jb250aW51b3VzLjFkZXJpdmF0aXZlLmZlbWFsZSkpDQpgYGANClRha2luZyB0aGUgZmlyc3QgZGVyaXZhdGUgb2YgYSBxdWFkcmF0aWMgZXF1YXRpb24sIG1lYW5zIHdlIGxvc2UgdGhlIGZpcnN0IHRlcm1zIGZvciBhbGwgYnV0IHRoZSBmaXJzdCBzcGxpbmUuIFNvIG5vIHdlIGhhdmUgOS0yID0gNSBjb2VmaWNpZW50cy4NCg0KKiogMykgU2hvdyBhIHBsb3Qgd2l0aCB0aGUgZml0dGVkIG1vZGVscyBzdXBlcmltcG9zZWQgKioNCg0KYGBge3J9DQpwbG90IChib25lJGFnZSwgYm9uZSRzcG5ibWQsICBjb2w9aWZlbHNlKGJvbmUkZ2VuZGVyPT0ibWFsZSIsImJsdWUiLCJyZWQiKSwgcGNoID0gOCkNCmFibGluZSh2PWJkcywgbHdkPTIsIGx0eT0zLCBjb2w9ImJsdWUiKQ0KY3VydmUocHJlZGljdChtb2RlbC5xdWFkcmF0aWMuY29udGludW91cy4xZGVyaXZhdGl2ZS5tYWxlLCBkYXRhLmZyYW1lKGFnZWdycD1jdXQoeCwgYnJlYWtzPWJkcyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9eCkpLCBsd2Q9MiwgY29sPSJibHVlIiwgYWRkPVRSVUUpDQpjdXJ2ZShwcmVkaWN0KG1vZGVsLnF1YWRyYXRpYy5jb250aW51b3VzLjFkZXJpdmF0aXZlLmZlbWFsZSwgZGF0YS5mcmFtZShhZ2VncnA9Y3V0KHgsIGJyZWFrcz1iZHMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWdlPXgpKSwgbHdkPTIsIGNvbD0icmVkIiwgYWRkPVRSVUUpDQpgYGANCg0KVGhpcyBtb2RlbCBsb29rcyBtdWNoIG5pY2VyIHRoYW4gdGhlIHByZXZpb3VzIG9uZXMsIGl0IGxvb2tzIGNvbnNpc3RlbnQgYWNjcm9zcyB0aGUgZGlmZmVyZW50IGdyb3Vwcy4gQnV0IG1heWJlIGp1c3QgYSBxdWFkcmF0aWMgbW9kZWwgaXMgbm90IGRldGFpbGVkIGVub3VnaCwgc28gYmVsb3cgdGhlcmUgaXMgYSBjdWJpYyBjb250aW51b3VzIHNwbGluZSBtb2RlbC4NCg0KIyMgQ29udGludW91cyBwaWVjZXdpc2UgY3ViaWMgd2l0aCBjb250aW51b3VzIGZpcnN0IGFuZCBzZWNvbmQgZGVyaXZhdGl2ZXMgKGN1YmljIHNwbGluZSkNCg0KKiogMSkgQ29uc3RydWN0IHRoZSBtb2RlbCAqKg0KDQpgYGB7cn0NCm1vZGVsLmN1YmljLnNwbGluZS5tYWxlIDwtIGxtKHNwbmJtZCB+IHBvbHkoYWdlLDMsIHJhdz1UKSArIHBtYXgoSShhZ2UtYmRzWzJdKV4zLDApICsgDQogICAgICAgICAgICBwbWF4KEkoYWdlLWJkc1szXSleMywwKSwgZGF0YT1ib25lcy5tYWxlKQ0KbW9kZWwuY3ViaWMuc3BsaW5lLmZlbWFsZSA8LSBsbShzcG5ibWQgfiBwb2x5KGFnZSwzLCByYXc9VCkgKyBwbWF4KEkoYWdlLWJkc1syXSleMywwKSArIA0KICAgICAgICAgICAgcG1heChJKGFnZS1iZHNbM10pXjMsMCksIGRhdGE9Ym9uZXMuZmVtYWxlKQ0KYGBgDQoNCg0KKiogMikgU3RhdGUgdGhlIG51bWJlciBvZiBjb2VmaWNpZW50cyBmaXR0ZWQsIGluY2x1ZGluZyB0aGUgaW50ZXJjZXB0ICoqDQoNCmBgYHtyfQ0KbGVuZ3RoKGNvZWYobW9kZWwuY3ViaWMuc3BsaW5lLm1hbGUpKQ0KYGBgDQoNCldlIGZpdCBjdWJpYyBtb2RlbHMgZm9yIGVhY2ggb2YgdGhlIGdyb3Vwcywgd2hpY2ggaGFzIGEgdG90YWwgb2YgMTIgY29lZmljaWVudHMuIEJ1dCBmb3IgYWxsIGJ1dCB0aGUgZmlyc3QgZ3JvdXAgd2Ugb25seSB1c2UgdGhlIGxhc3QgY3ViaWMgY29lZmllY2llbnQuIFNvIHRoZXJlIGlzIGEgdG90YWwgb2YgNChmaXJzdCBncm91cCkgKyAyID0gNi4NCg0KKiogMykgU2hvdyBhIHBsb3Qgd2l0aCB0aGUgZml0dGVkIG1vZGVscyBzdXBlcmltcG9zZWQgKioNCg0KYGBge3J9DQpwbG90IChib25lJGFnZSwgYm9uZSRzcG5ibWQsICBjb2w9aWZlbHNlKGJvbmUkZ2VuZGVyPT0ibWFsZSIsImJsdWUiLCJyZWQiKSwgcGNoID0gOCkNCmFibGluZSh2PWJkcywgbHdkPTIsIGx0eT0zLCBjb2w9ImJsdWUiKQ0KY3VydmUocHJlZGljdChtb2RlbC5jdWJpYy5zcGxpbmUubWFsZSwgZGF0YS5mcmFtZShhZ2VncnA9Y3V0KHgsIGJyZWFrcz1iZHMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWdlPXgpKSwgbHdkPTIsIGNvbD0iYmx1ZSIsIGFkZD1UUlVFKQ0KY3VydmUocHJlZGljdChtb2RlbC5jdWJpYy5zcGxpbmUuZmVtYWxlLCBkYXRhLmZyYW1lKGFnZWdycD1jdXQoeCwgYnJlYWtzPWJkcyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9eCkpLCBsd2Q9MiwgY29sPSJyZWQiLCBhZGQ9VFJVRSkNCmBgYA0KDQpUaGlzIG1vZGVsIGlzIG1vcmUgY29tcGxleCB0aGFuIHRoZSBwcmV2aW91cyBtb2RlbHMgYmVjYXVzZSBpdCBpcyBjdWJpYy4gQnV0IGl0IGxvb2tzIGxpa2UgaXQgcmVwcmVzZW50cyB0aGUgZGF0YSB3ZWxsLCBhbmQgaXQgaXMgY29udGludW91cyBhdCB0aGUga25vdHMuDQoNCiMgUGFydCAzDQoNCioqIERlc2NyaWJlIHdoaWNoIG1vZGVsIGluIFBhcnQgMiBhcHBlYXJzIG1vc3Qgc3VpdGVkIHRvIHRoZSBkYXRhLCBiYXNlZCBvbiB2aXN1YWwgaW5zcGVjdGlvbi4gQXJlIHRoZXJlIGFzcGVjdHMgb2YgdGhpcyBmaXR0ZWQgbW9kZWwgdGhhdCBzZWVtIGNvbmNlcm5pbmc/ICoqDQoNCkJvdGggbW9kZWxzIGZvciB0aGUgQ29udGludW91cyBwaWVjZXdpc2UgcXVhZHJhdGljIHdpdGggY29udGludW91cyBmaXJzdCBkZXJpdmF0aXZlKGMpIGFuZCB0aGUgY3ViaWMgc3BsaW5lKGQpIHNlZW0gbGlrZSBnb29kIG1vZGVscy4gSWYgYSBtb3JlIGRldGFpbGVkIG1vZGVsIGlzIG5lZWRlZCBtYXliZSBkIG1pZ2h0IGJlIGEgYmV0dGVyIGNob2ljZS4gSWYgYSBzaW1wbGVyIGFuZCBlYXNpZXIgdG8gaW50ZXJwcmV0IG1vZGVsIGlzIG5lZWRlZCwgdGhlbiBjIG1pZ2h0IGJlIGJldHRlci4gDQoNClBlcnNvbmFsbHkgSSB0aGluayBkIGlzIGEgYmV0dGVyIG1vZGVsLiBJdCBzZWVtcyB0byBjYXB0dXJlIHRoZSBkYXRhIHdlbGwsIGFuZCBjcmVhdGUgZ29vZCBtb2RlbHMgZm9yIGJvdGggbWFsZSBhbmQgZmVtYWxlIGRhdGEgcG9pbnRzLiBJdCBhbHNvIGNsZWFybHkgc2hvd3MgdGhlIGRpZmZlcmVuY2VzIG9mIG1vZGVscyB0aGF0IG9jY3VyciBkbyB0byB0aGUgZGlmZmVyZW50IHJlZ2lvbnMgb2YgYWdlLg0KDQpBbiBhc3BlY3QgdGhhdCBpcyBjb25jZXJuaW5nIGFib3V0IHRoaXMgbW9kZWwgaXMgdGhlIHN0YXJ0IGFuZCBlbmQgYWdlcywgd2hlcmUgaXQgc2VlbXMgdG8gcXVpY2x5IGdvIHVwIG9yIGRvd24sIHdoaWNoIHdvdWxkIG5vdCBiZSByZWFsaXN0aWMgd2l0aCB0aGUgZGF0YS4gRm9yIGV4YW1wbGUgaWYgd2UgdHJpZWQgdG8gcHJlZGljdCBhIG5ldyBmZW1hbGUgZGF0YSBwb2ludCB3aXRoIGFnZSA4LiBJdCBwcm9iYWJseSB3b3VsZCBiZSBhcm91bmQgLTAuMiwgd2hpY2ggaXMgcHJvYmFsYnkgdmVyeSBpbmNvcnJlY3QsIGJlY2F1c2UgaXQgY2FudCBiZSBuZWdhdGl2ZS4gRm9yIGJvdGggbWFsZSBhbmQgZmVtYWxlLCBwcmVkaWNpdGlvbnMgb2YgbG93ZXIgYWdlcyB0aGFuIDEwLCB3b3VsZCBwcm9iYWxieSBub3QgeWllbGQgZ29vZCByZXN1bHRzLg0KDQoNCioqIEZvciB0aGlzIGJlc3QgYXBwcm9hY2gsIGltcGxlbWVudCA1LWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiB0byBzZWxlY3QgYmV0d2VlbiB1c2luZyAyLCAzLCA0LCA1LCA2LCBvciA3IGtub3RzLCBzcGFjZWQgb3V0IGF0IHVuaWZvcm0gcGVyY2VudGlsZXMgb2YgdGhlIGRhdGEuICoqDQoNCg0KYGBge3J9DQpmb3IgKCBqIGluIDI6NykNCnsNCiAgbXNlLm1hbGUgPSAwDQogIG1zZS5mZW1hbGUgPSAwDQogIGJvbmVzMyA9IGJvbmUNCiAgYm9uZXMzJGFnZWdycCA8LSBxdWFudGlsZUN1dChib25lczMkYWdlLCBqKzEpDQogIGJvbmVzLm1hbGUgPSBzdWJzZXQoYm9uZXMzLCBnZW5kZXIgPT0gIm1hbGUiKQ0KICBib25lcy5mZW1hbGUgPSBzdWJzZXQoYm9uZXMzLCBnZW5kZXIgPT0gImZlbWFsZSIpDQoNCiAgbGFicyA8LSBsZXZlbHMoYm9uZXMzJGFnZWdycCkNCiAgYmRzIDwtIGNiaW5kKGxvd2VyID0gYXMubnVtZXJpYyggc3ViKCJcXCgoLispLC4qIiwgIlxcMSIsIGxhYnMpICksDQogICAgICB1cHBlciA9IGFzLm51bWVyaWMoIHN1YigiW14sXSosKFteXV0qKVxcXSIsICJcXDEiLCBsYWJzKSApKQ0KICBiZHMgPC0gYyhiZHNbLDFdLCBiZHNbbnJvdyhiZHMpLDJdKQ0KDQogIGZvbGRzLmZlbWFsZSA8LSBhcy5udW1lcmljKGN1dCgxOm5yb3coYm9uZXMuZmVtYWxlKSwgNSkpDQogIGZvbGRzLm1hbGUgPC0gYXMubnVtZXJpYyhjdXQoMTpucm93KGJvbmVzLm1hbGUpLCA1KSkNCiAgDQogIGZvciAoaSBpbiAxOjUpDQogIHsNCiAgICB0cmFpbi5mZW1hbGUgPC0gYm9uZXMuZmVtYWxlW2ZvbGRzLmZlbWFsZSAhPWksXQ0KICAgIHZhbC5mZW1hbGUgPC0gYm9uZXMuZmVtYWxlW2ZvbGRzLmZlbWFsZSA9PSBpLF0NCiAgICB0cmFpbi5tYWxlIDwtIGJvbmVzLm1hbGVbZm9sZHMubWFsZSAhPWksXQ0KICAgIHZhbC5tYWxlIDwtIGJvbmVzLm1hbGVbZm9sZHMubWFsZSA9PSBpLF0NCg0KICAgIG1vZGVsLmZlbWFsZT1sbShzcG5ibWQgfiBicyhhZ2UgLGtub3RzID0gYmRzICksZGF0YT10cmFpbi5mZW1hbGUpIA0KICAgIG1vZGVsLm1hbGU9bG0oc3BuYm1kIH4gYnMoYWdlICxrbm90cyA9IGJkcyApLGRhdGE9dHJhaW4ubWFsZSkgDQogICAgcHJlZC5mZW1hbGUgPSBwcmVkaWN0KG1vZGVsLmZlbWFsZSkNCiAgICBwcmVkLm1hbGUgPSBwcmVkaWN0KG1vZGVsLm1hbGUpDQogICAgbXNlLmZlbWFsZSA9IG1zZS5mZW1hbGUgKyBtZWFuKChwcmVkLmZlbWFsZSAtIHRyYWluLmZlbWFsZSRzcG5ibWQpXjIpDQogICAgbXNlLm1hbGUgPSBtc2UubWFsZSArIG1lYW4oKHByZWQubWFsZSAtIHRyYWluLm1hbGUkc3BuYm1kKV4yKQ0KICB9DQogIGNhdChzcHJpbnRmICgiZmVtYWxlIC0gJXMgLSBzJWZcbiIsIGosbXNlLmZlbWFsZSkpDQogIGNhdChzcHJpbnRmICgibWFsZSAtICVzIC0gcyVmXG4iLCBqLG1zZS5tYWxlKSkNCn0NCiAgICANCmBgYA0KDQoNCioqIFdoYXQgY2hvaWNlIG9mIGtub3RzIHdvcmtzIGJlc3Q/IFJlZml0IGEgbW9kZWwgb24gYWxsIG9mIHRoZSBkYXRhIHdpdGggdGhpcyBudW1iZXIgb2Yga25vdHMgYW5kIHBsb3QgdGhlIG1vZGVsLiAqKg0KDQpUaGUgYmVzdCBtb2RlbCB1c2VkIDQga25vdHMgKDUgZGlmZmVyZW50IGdyb3VwcyksIHdoZXJlIHRoZSBlcnJvciB3YXMgMC4wMDU4NjAuIEkgY2hvc2UgdGhpcyBtb2RlbCBiZWNhdXNlIGl0IGhhcyBhIGxvdyBlcnJvciByYXRlLCBhbmQgaXQgZG9lcyBub3QgaGF2ZSB0b28gbWFueSBrbm90cywgc2VwYXJhdGluZyB0aGUgZGF0YSB0b28gbXVjaCwgYW5kIGNyZWF0aW5nIHRvbyBtYW55IGRpZmZlcmVudCBncm91cHMuDQoNCkJlbGxvdyBpcyB0aGUgcGxvdCBvZiB0aGUgZmluYWwgbW9kZWwsIHNlcGFyYXRlZCBieSBnZW5kZXIuDQoNCmBgYHtyfQ0KYm9uZXMyID0gYm9uZQ0KDQpib25lczIkYWdlZ3JwIDwtIHF1YW50aWxlQ3V0KGJvbmVzMiRhZ2UsIDUpDQpsYWJzIDwtIGxldmVscyhib25lczIkYWdlZ3JwKQ0KYmRzIDwtIGNiaW5kKGxvd2VyID0gYXMubnVtZXJpYyggc3ViKCJcXCgoLispLC4qIiwgIlxcMSIsIGxhYnMpICksDQogICAgICB1cHBlciA9IGFzLm51bWVyaWMoIHN1YigiW14sXSosKFteXV0qKVxcXSIsICJcXDEiLCBsYWJzKSApKQ0KYmRzIDwtIGMoYmRzWywxXSwgYmRzW25yb3coYmRzKSwyXSkNCg0KYm9uZXMubWFsZSA9IHN1YnNldChib25lczIsIGdlbmRlciA9PSAibWFsZSIpDQpib25lcy5mZW1hbGUgPSBzdWJzZXQoYm9uZXMyLCBnZW5kZXIgPT0gImZlbWFsZSIpDQoNCmZpbmFsLm1hbGUgPC0gbG0oc3BuYm1kIH4gYnMoYWdlICxrbm90cyA9IGJkcyApLGRhdGE9dHJhaW4ubWFsZSkgDQpmaW5hbC5mZW1hbGUgPC0gbG0oc3BuYm1kIH4gYnMoYWdlICxrbm90cyA9IGJkcyApLGRhdGE9dHJhaW4uZmVtYWxlKSANCg0KcGxvdCAoYm9uZSRhZ2UsIGJvbmUkc3BuYm1kLCAgY29sPWlmZWxzZShib25lJGdlbmRlcj09Im1hbGUiLCJibHVlIiwicmVkIiksIHBjaCA9IDgpDQphYmxpbmUodj1iZHMsIGx3ZD0yLCBsdHk9MywgY29sPSJibHVlIikNCmN1cnZlKHByZWRpY3QoZmluYWwubWFsZSwgZGF0YS5mcmFtZShhZ2VncnA9Y3V0KHgsIGJyZWFrcz1iZHMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWdlPXgpKSwgbHdkPTIsIGNvbD0iYmx1ZSIsIGFkZD1UUlVFKQ0KY3VydmUocHJlZGljdChmaW5hbC5mZW1hbGUsIGRhdGEuZnJhbWUoYWdlZ3JwPWN1dCh4LCBicmVha3M9YmRzKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT14KSksIGx3ZD0yLCBjb2w9InJlZCIsIGFkZD1UUlVFKQ0KYGBgDQoNCg0KVGhpcyBmaW5hbCBtb2RlbCB3aXRoIDQga25vdHMgY2FwdHVyZXMgdGhlIGRhdGEgdmVyeSB3ZWxsLiBJdCBzaG93cyB0aGUgY3VydmUgZm9yIHdvbWVuIGF0IGEgbG93IGFnZSwgd2VyZSB0aGVpciBzcG5ibWQgdmFsdWVzIGFyZSBoaWdoZXIsIHRoZW4gZHJvcHMgYW5kIHN0YXlzIHJlbGF0aXZlbHkgY29uc2lzdGVudCBhZnRlciBhZ2UgMTUuIFRoZSBwZWFrIGZvciBtZW4gaXMgYSBiaXQgbGF0ZXIsIGF0IGFyb3VuZCBhZ2UgMTUsIGFuZCBhdCBhcm91bmQgYWdlIDIwIGl0IHN0YWJpbGl6ZXMsIGJlaW5nIHRoZSBzYW1lIGFzIHRoZSB3b21hbnMgc3BuYm1kLg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==