Assignment Objectives

  • Develop a clear technical understanding of nonparametric cumulative distribution function (CDF) estimation and various kernel density estimators.

  • Translate mathematical formulas into R functions and apply them to solve related problems.

  • Create effective visualizations to demonstrate your understanding of key concepts in the following questions.


Question 1: Cumulative Distribution Function (CDF) Estimation

The following failure times (in hours) were observed for 8 electronic components:

23, 45, 67, 89, 112, 156, 189, 245
  1. Write an R function implementing the ECDF \(\hat{F}_n(t)\) according to its mathematical definition. Validate your implementation using R’s ecdf() function on the given data, with comparison based on their step functions.
times <- c(23, 45, 67, 89, 112, 156, 189, 245)
uniq.time <- sort(unique(times))  

my.ECDF <- function(indat, outx){
  
  freq.table <- table(indat)                         
  uniq <- as.numeric(names(freq.table))          
  rep.time <- as.vector(freq.table)                   
  cum.rel.feq <- cumsum(rep.time)/sum(rep.time)      
  cum.prob <- NULL
  for (i in 1:length(outx)){
    intvl.id <- which(uniq <= outx[i])      
    cum.prob[i] <- cum.rel.feq[max(intvl.id)] 
  }
  cum.prob             
}

my.ECDF_def <- function(indat, t){
  n <- length(indat)
  sum(indat <= t)/n
}
Fn <- ecdf(times)
grid <- seq(min(times)-10, max(times)+10, by = 1)

plot(Fn, verticals = TRUE, do.points = TRUE,
     main = "ECDF comparison: built-in ecdf() vs my.ECDF()",
     xlab = "Failure time (hours)", ylab = "Empirical CDF")


lines(grid, my.ECDF(indat = times, outx = grid), type = "s", lty = 2, lwd = 2)

legend("bottomright",
       legend = c("ecdf()", "my.ECDF()"),
       lty = c(1,2), lwd = c(1,2), bty = "n")

  1. A colleague claims that the probability of failure before 100 hours is 0.5 based on these data. Do you agree? Explain your reasoning using the empirical cumulative distribution function (ECDF).
my.ECDF_def(times, 100)
[1] 0.5
Fn(100)
[1] 0.5

Solution: By using the empirical distribution function, the estimated probability of failure before 100 hours is equal to 0.5. Therefore, I agree with my colleague that the probability of failure before 100 hours is 0.5 based on these small sample data.


Question 2: Density Function Estimation

Consider the following failure times from a mechanical system:

12.3, 14.7, 15.2, 16.8, 18.1, 19.4, 20.6, 22.3, 23.9, 25.4
  1. Create a histogram of the data using 3 equally spaced bins. What is the estimated density in each bin? Describe the shape of the histogram’s distribution.
fail <- c(12.3, 14.7, 15.2, 16.8, 18.1, 19.4, 20.6, 22.3, 23.9, 25.4)
hisfail<-hist(fail, breaks = 3, freq = FALSE,
      main = "Histogram with 3 equally spaced bins",
      xlab = "Failure time")

hisfail$density
[1] 0.04 0.08 0.06 0.02

Solution:The histogram uses 3 equal-width bins, and the estimated densities are the bin heights shown in the table above.

Solution:Using three equally spaced bins, the histogram suggests an unimodal distribution and approximately symmetric to mildly right-skewed.

  1. Write an R function that computes kernel density estimates using a Gaussian kernel with \(h=2\). Validate your implementation against R’s built-in density() function.

\[ \hat{f}_h(t) = \frac{1}{nh}\sum_{i=1}^n K\left( \frac{t-t_i}{h}\right), \ \ \text{ where } \ \ K(u) = \frac{1}{\sqrt{2\pi}} e^{-u^2/2}. \]

my.kerf.gauss <- function(in.data, h, out.x){
  n <- length(in.data)
  den <- numeric(length(out.x))
  for (i in 1:length(out.x)){
    den[i] <- sum(dnorm(out.x[i], mean = in.data, sd = h))/n
  }
  den
}

xx <- seq(min(fail)-3, max(fail)+3, length = 300)

my.den <- my.kerf.gauss(in.data = fail, h = 2, out.x = xx)

kde <- density(fail, bw = 2, kernel = "gaussian")
kde.y <- approx(kde$x, kde$y, xout = xx)$y

plot(xx, my.den, type = "l", lwd = 2,col= "blue",
     main = "Gaussian KDE (h=2): my.kerf.gauss vs density()",
     xlab = "Failure time", ylab = "Density")
lines(xx, kde.y, lty = 2, lwd = 2, col="yellow")
legend("topright", c("my.kerf.gauss()", "density()"),
       lty = c(1,2), lwd = 2, bty = "n")

  1. Write a custom R function that computes kernel density estimates using the Epanechnikov kernel with \(h=2\). Validate your implementation by comparing results with R’s built-in density() function for Gaussian kernel estimation.

\[ \hat{f}_h(t) = \frac{1}{nh}\sum_{i=1}^n K\left( \frac{t-t_i}{h}\right), \ \ \text{ where } \ \ K(u) = \frac{3}{4}(1 - u^2) \ \ \text{ for } \ \ |u| \le 1. \]

my.kerf.epan <- function(in.data, h, out.x){
  n <- length(in.data)
  den <- numeric(length(out.x))
  for (i in 1:length(out.x)){
    u <- (out.x[i] - in.data)/h
    K <- ifelse(abs(u) <= 1, 0.75*(1 - u^2), 0)
    den[i] <- sum(K)/(n*h)
  }
  den
}

den.g2 <- my.kerf.gauss(fail, h = 2, out.x = xx)
den.e2 <- my.kerf.epan(fail,  h = 2, out.x = xx)

plot(xx, den.g2, type="l", lwd=2,col="gold",
     main="Kernel comparison (h=2)",
     xlab="Failure time", ylab="Density")
lines(xx, den.e2, lty=2, lwd=2)
legend("topright", c("Gaussian", "Epanechnikov"),
       lty=c(1,2), lwd=2, bty="n")

  1. How does the choice of kernel (Gaussian vs. Epanechnikov) affect the density estimate? For both kernel estimators applied to this dataset, what happens when we select \(h=1.5\) versus \(h=2.5\)?
den.g15 <- my.kerf.gauss(fail, h = 1.5, out.x = xx)
den.g25 <- my.kerf.gauss(fail, h = 2.5, out.x = xx)

plot(xx, den.g15, type="l", lwd=2,col="navy",
     main="Gaussian KDE: h=1.5 vs h=2.5",
     xlab="Failure time", ylab="Density")
lines(xx, den.g25, lty=2, lwd=2)
legend("topright", c("h=1.5", "h=2.5"),
       lty=c(1,2), lwd=2, bty="n")

den.e15 <- my.kerf.epan(fail, h = 1.5, out.x = xx)
den.e25 <- my.kerf.epan(fail, h = 2.5, out.x = xx)

plot(xx, den.e15, type="l", lwd=2, col="green",
     main="Epanechnikov KDE: h=1.5 vs h=2.5",
     xlab="Failure time", ylab="Density")
lines(xx, den.e25, lty=2, lwd=2)
legend("topright", c("h=1.5", "h=2.5"),
       lty=c(1,2), lwd=2, bty="n")

Solution:Effect of Kernel Choice (Gaussian vs. Epanechnikov), With the same bandwidth, the Gaussian kernel gives a smoother curve with longer tails, while the Epanechnikov kernel focuses more on nearby observations because it has a limited range. In practice, the difference between kernels is usually small compared to the effect of the bandwidth.

Solution:The bandwidth controls how smooth the density estimate is. A smaller bandwidth (ℎ=1.5) produces a less smooth curve with more local fluctuations, while a larger bandwidth (ℎ=2.5) gives a smoother curve but may hide some details of the data.

LS0tDQp0aXRsZTogIkFzc2lnbm1lbnQgMTogRXN0aW1hdGluZyBDREYgYW5kIFBERiINCmF1dGhvcjogIlhpYW95aW5nIE1hICINCmRhdGU6ICIgRHVlOiAwMi8wMy8yMDI2Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIHRvY19mbG9hdDogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiBubw0KICAgIHRvY19jb2xsYXBzZWQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHNtb290aF9zY3JvbGw6IHllcw0KICAgIHRoZW1lOiBsdW1lbg0KICBwZGZfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgZmlnX3dpZHRoOiAzDQogICAgZmlnX2hlaWdodDogMw0KICB3b3JkX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBrZWVwX21kOiB5ZXMNCmVkaXRvcl9vcHRpb25zOiANCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IA0KIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuDQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoInBhbmRlciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwYW5kZXIiKQ0KICAgbGlicmFyeShwYW5kZXIpDQp9DQppZiAoIXJlcXVpcmUoImdncGxvdDIiKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikNCiAgbGlicmFyeShnZ3Bsb3QyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJ0aWR5dmVyc2UiKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KICBsaWJyYXJ5KHRpZHl2ZXJzZSkNCn0NCg0KaWYgKCFyZXF1aXJlKCJwbG90bHkiKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KICBsaWJyYXJ5KHBsb3RseSkNCn0NCiMjIyMNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCAgICMgc29tZXRpbWVzLCB5b3UgY29kZSBtYXkgcHJvZHVjZSB3YXJuaW5nIG1lc3NhZ2VzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHlvdSBjYW4gY2hvb3NlIHRvIGluY2x1ZGUgdGhlIHdhcm5pbmcgbWVzc2FnZXMgaW4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb3V0cHV0IGZpbGUuIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFLCAgICAjIHlvdSBjYW4gYWxzbyBkZWNpZGUgd2hldGhlciB0byBpbmNsdWRlIHRoZSBvdXRwdXQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBpbiB0aGUgb3V0cHV0IGZpbGUuDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQQ0KICAgICAgICAgICAgICAgICAgICAgICkgIA0KYGBgDQoNCg0KYGBge2NzcywgZWNobyA9IEZBTFNFfQ0KI1RPQzo6YmVmb3JlIHsNCiAgY29udGVudDogIlRhYmxlIG9mIENvbnRlbnRzIjsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGZvbnQtc2l6ZTogMS4yZW07DQogIGRpc3BsYXk6IGJsb2NrOw0KICBjb2xvcjogbmF2eTsNCiAgbWFyZ2luLWJvdHRvbTogMTBweDsNCn0NCg0KDQpkaXYjVE9DIGxpIHsgICAgIC8qIHRhYmxlIG9mIGNvbnRlbnQgICovDQogICAgbGlzdC1zdHlsZTp1cHBlci1yb21hbjsNCiAgICBiYWNrZ3JvdW5kLWltYWdlOm5vbmU7DQogICAgYmFja2dyb3VuZC1yZXBlYXQ6bm9uZTsNCiAgICBiYWNrZ3JvdW5kLXBvc2l0aW9uOjA7DQp9DQoNCmgxLnRpdGxlIHsgICAgLyogbGV2ZWwgMSBoZWFkZXIgb2YgdGl0bGUgICovDQogIGZvbnQtc2l6ZTogMjJweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGNvbG9yOiBEYXJrUmVkOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtZmFtaWx5OiAiR2lsbCBTYW5zIiwgc2Fucy1zZXJpZjsNCn0NCg0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgZm9udC1zaXplOiAxNXB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IG5hdnk7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCg0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGZvbnQtZmFtaWx5OiAiR2lsbCBTYW5zIiwgc2Fucy1zZXJpZjsNCiAgY29sb3I6IERhcmtCbHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQoNCmgxIHsgLyogSGVhZGVyIDEgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDIwcHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KDQpoMiB7IC8qIEhlYWRlciAyIC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmgzIHsgLyogSGVhZGVyIDMgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDE2cHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDQgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMTRweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KLyogQWRkIGRvdHMgYWZ0ZXIgbnVtYmVyZWQgaGVhZGVycyAqLw0KLmhlYWRlci1zZWN0aW9uLW51bWJlcjo6YWZ0ZXIgew0KICBjb250ZW50OiAiLiI7DQp9DQoNCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCi5oaWdobGlnaHRtZSB7IGJhY2tncm91bmQtY29sb3I6eWVsbG93OyB9DQoNCnAgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCg0KYGBgDQogDQogXA0KIA0KIyMgKipBc3NpZ25tZW50IE9iamVjdGl2ZXMqKiANCg0KKiBEZXZlbG9wIGEgY2xlYXIgdGVjaG5pY2FsIHVuZGVyc3RhbmRpbmcgb2Ygbm9ucGFyYW1ldHJpYyBjdW11bGF0aXZlIGRpc3RyaWJ1dGlvbiBmdW5jdGlvbiAoQ0RGKSBlc3RpbWF0aW9uIGFuZCB2YXJpb3VzIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRvcnMuDQoNCiogVHJhbnNsYXRlIG1hdGhlbWF0aWNhbCBmb3JtdWxhcyBpbnRvIFIgZnVuY3Rpb25zIGFuZCBhcHBseSB0aGVtIHRvIHNvbHZlIHJlbGF0ZWQgcHJvYmxlbXMuDQoNCiogQ3JlYXRlIGVmZmVjdGl2ZSB2aXN1YWxpemF0aW9ucyB0byBkZW1vbnN0cmF0ZSB5b3VyIHVuZGVyc3RhbmRpbmcgb2Yga2V5IGNvbmNlcHRzIGluIHRoZSBmb2xsb3dpbmcgcXVlc3Rpb25zLg0KDQoNCg0KXA0KDQojIyAqKlF1ZXN0aW9uIDE6IEN1bXVsYXRpdmUgRGlzdHJpYnV0aW9uIEZ1bmN0aW9uIChDREYpIEVzdGltYXRpb24qKg0KDQpUaGUgZm9sbG93aW5nIGZhaWx1cmUgdGltZXMgKGluIGhvdXJzKSB3ZXJlIG9ic2VydmVkIGZvciA4IGVsZWN0cm9uaWMgY29tcG9uZW50czoNCg0KPGNlbnRlcj4gMjMsIDQ1LCA2NywgODksIDExMiwgMTU2LCAxODksIDI0NSAgPC9jZW50ZXI+DQoNCmEpIFdyaXRlIGFuIFIgZnVuY3Rpb24gaW1wbGVtZW50aW5nIHRoZSBFQ0RGICRcaGF0e0Z9X24odCkkIGFjY29yZGluZyB0byBpdHMgbWF0aGVtYXRpY2FsIGRlZmluaXRpb24uIFZhbGlkYXRlIHlvdXIgaW1wbGVtZW50YXRpb24gdXNpbmcgUidzIGVjZGYoKSBmdW5jdGlvbiBvbiB0aGUgZ2l2ZW4gZGF0YSwgd2l0aCBjb21wYXJpc29uIGJhc2VkIG9uIHRoZWlyIHN0ZXAgZnVuY3Rpb25zLg0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTR9DQp0aW1lcyA8LSBjKDIzLCA0NSwgNjcsIDg5LCAxMTIsIDE1NiwgMTg5LCAyNDUpDQp1bmlxLnRpbWUgPC0gc29ydCh1bmlxdWUodGltZXMpKSAgDQoNCm15LkVDREYgPC0gZnVuY3Rpb24oaW5kYXQsIG91dHgpew0KICANCiAgZnJlcS50YWJsZSA8LSB0YWJsZShpbmRhdCkgICAgICAgICAgICAgICAgICAgICAgICAgDQogIHVuaXEgPC0gYXMubnVtZXJpYyhuYW1lcyhmcmVxLnRhYmxlKSkgICAgICAgICAgDQogIHJlcC50aW1lIDwtIGFzLnZlY3RvcihmcmVxLnRhYmxlKSAgICAgICAgICAgICAgICAgICANCiAgY3VtLnJlbC5mZXEgPC0gY3Vtc3VtKHJlcC50aW1lKS9zdW0ocmVwLnRpbWUpICAgICAgDQogIGN1bS5wcm9iIDwtIE5VTEwNCiAgZm9yIChpIGluIDE6bGVuZ3RoKG91dHgpKXsNCiAgICBpbnR2bC5pZCA8LSB3aGljaCh1bmlxIDw9IG91dHhbaV0pICAgICAgDQogICAgY3VtLnByb2JbaV0gPC0gY3VtLnJlbC5mZXFbbWF4KGludHZsLmlkKV0gDQogIH0NCiAgY3VtLnByb2IgICAgICAgICAgICAgDQp9DQoNCm15LkVDREZfZGVmIDwtIGZ1bmN0aW9uKGluZGF0LCB0KXsNCiAgbiA8LSBsZW5ndGgoaW5kYXQpDQogIHN1bShpbmRhdCA8PSB0KS9uDQp9DQoNCmBgYA0KDQpgYGB7cn0NCkZuIDwtIGVjZGYodGltZXMpDQpncmlkIDwtIHNlcShtaW4odGltZXMpLTEwLCBtYXgodGltZXMpKzEwLCBieSA9IDEpDQoNCnBsb3QoRm4sIHZlcnRpY2FscyA9IFRSVUUsIGRvLnBvaW50cyA9IFRSVUUsDQogICAgIG1haW4gPSAiRUNERiBjb21wYXJpc29uOiBidWlsdC1pbiBlY2RmKCkgdnMgbXkuRUNERigpIiwNCiAgICAgeGxhYiA9ICJGYWlsdXJlIHRpbWUgKGhvdXJzKSIsIHlsYWIgPSAiRW1waXJpY2FsIENERiIpDQoNCg0KbGluZXMoZ3JpZCwgbXkuRUNERihpbmRhdCA9IHRpbWVzLCBvdXR4ID0gZ3JpZCksIHR5cGUgPSAicyIsIGx0eSA9IDIsIGx3ZCA9IDIpDQoNCmxlZ2VuZCgiYm90dG9tcmlnaHQiLA0KICAgICAgIGxlZ2VuZCA9IGMoImVjZGYoKSIsICJteS5FQ0RGKCkiKSwNCiAgICAgICBsdHkgPSBjKDEsMiksIGx3ZCA9IGMoMSwyKSwgYnR5ID0gIm4iKQ0KDQpgYGANCg0KYikgQSBjb2xsZWFndWUgY2xhaW1zIHRoYXQgdGhlIHByb2JhYmlsaXR5IG9mIGZhaWx1cmUgYmVmb3JlIDEwMCBob3VycyBpcyAwLjUgYmFzZWQgb24gdGhlc2UgZGF0YS4gRG8geW91IGFncmVlPyBFeHBsYWluIHlvdXIgcmVhc29uaW5nIHVzaW5nIHRoZSBlbXBpcmljYWwgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gZnVuY3Rpb24gKEVDREYpLg0KDQpgYGB7cn0NCm15LkVDREZfZGVmKHRpbWVzLCAxMDApDQpGbigxMDApDQpgYGANCioqU29sdXRpb24qKjogQnkgdXNpbmcgdGhlIGVtcGlyaWNhbCBkaXN0cmlidXRpb24gZnVuY3Rpb24sIHRoZSBlc3RpbWF0ZWQgcHJvYmFiaWxpdHkgb2YgZmFpbHVyZSBiZWZvcmUgMTAwIGhvdXJzIGlzIGVxdWFsIHRvIDAuNS4gVGhlcmVmb3JlLCBJIGFncmVlIHdpdGggbXkgY29sbGVhZ3VlIHRoYXQgdGhlIHByb2JhYmlsaXR5IG9mIGZhaWx1cmUgYmVmb3JlIDEwMCBob3VycyBpcyAwLjUgYmFzZWQgb24gdGhlc2Ugc21hbGwgc2FtcGxlIGRhdGEuDQoNClwNCg0KIyMgKipRdWVzdGlvbiAyOiBEZW5zaXR5IEZ1bmN0aW9uIEVzdGltYXRpb24qKg0KDQpDb25zaWRlciB0aGUgZm9sbG93aW5nIGZhaWx1cmUgdGltZXMgZnJvbSBhIG1lY2hhbmljYWwgc3lzdGVtOg0KDQo8Y2VudGVyPiAxMi4zLCAxNC43LCAxNS4yLCAxNi44LCAxOC4xLCAxOS40LCAyMC42LCAyMi4zLCAyMy45LCAyNS40IDwvY2VudGVyPg0KDQphKSBDcmVhdGUgYSBoaXN0b2dyYW0gb2YgdGhlIGRhdGEgdXNpbmcgMyBlcXVhbGx5IHNwYWNlZCBiaW5zLiBXaGF0IGlzIHRoZSBlc3RpbWF0ZWQgZGVuc2l0eSBpbiBlYWNoIGJpbj8gRGVzY3JpYmUgdGhlIHNoYXBlIG9mIHRoZSBoaXN0b2dyYW0ncyBkaXN0cmlidXRpb24uDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJ30NCmZhaWwgPC0gYygxMi4zLCAxNC43LCAxNS4yLCAxNi44LCAxOC4xLCAxOS40LCAyMC42LCAyMi4zLCAyMy45LCAyNS40KQ0KYGBgDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJ30NCmhpc2ZhaWw8LWhpc3QoZmFpbCwgYnJlYWtzID0gMywgZnJlcSA9IEZBTFNFLA0KICAgICAgbWFpbiA9ICJIaXN0b2dyYW0gd2l0aCAzIGVxdWFsbHkgc3BhY2VkIGJpbnMiLA0KICAgICAgeGxhYiA9ICJGYWlsdXJlIHRpbWUiKQ0KDQpoaXNmYWlsJGRlbnNpdHkNCg0KYGBgDQoNCg0KKipTb2x1dGlvbioqOlRoZSBoaXN0b2dyYW0gdXNlcyAzIGVxdWFsLXdpZHRoIGJpbnMsIGFuZCB0aGUgZXN0aW1hdGVkIGRlbnNpdGllcyBhcmUgdGhlIGJpbiBoZWlnaHRzIHNob3duIGluIHRoZSB0YWJsZSBhYm92ZS4NCg0KKipTb2x1dGlvbioqOlVzaW5nIHRocmVlIGVxdWFsbHkgc3BhY2VkIGJpbnMsIHRoZSBoaXN0b2dyYW0gc3VnZ2VzdHMgYW4gdW5pbW9kYWwgZGlzdHJpYnV0aW9uIGFuZCBhcHByb3hpbWF0ZWx5IHN5bW1ldHJpYyB0byBtaWxkbHkgcmlnaHQtc2tld2VkLg0KDQoNCg0KYikgV3JpdGUgYW4gUiBmdW5jdGlvbiB0aGF0IGNvbXB1dGVzIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRlcyB1c2luZyBhIEdhdXNzaWFuIGtlcm5lbCB3aXRoICRoPTIkLiBWYWxpZGF0ZSB5b3VyIGltcGxlbWVudGF0aW9uIGFnYWluc3QgUidzIGJ1aWx0LWluIGRlbnNpdHkoKSBmdW5jdGlvbi4NCg0KJCQNClxoYXR7Zn1faCh0KSA9IFxmcmFjezF9e25ofVxzdW1fe2k9MX1ebiBLXGxlZnQoIFxmcmFje3QtdF9pfXtofVxyaWdodCksIFwgXCBcdGV4dHsgd2hlcmUgfSBcIFwgSyh1KSA9IFxmcmFjezF9e1xzcXJ0ezJccGl9fSBlXnstdV4yLzJ9Lg0KJCQNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJ30NCm15LmtlcmYuZ2F1c3MgPC0gZnVuY3Rpb24oaW4uZGF0YSwgaCwgb3V0Lngpew0KICBuIDwtIGxlbmd0aChpbi5kYXRhKQ0KICBkZW4gPC0gbnVtZXJpYyhsZW5ndGgob3V0LngpKQ0KICBmb3IgKGkgaW4gMTpsZW5ndGgob3V0LngpKXsNCiAgICBkZW5baV0gPC0gc3VtKGRub3JtKG91dC54W2ldLCBtZWFuID0gaW4uZGF0YSwgc2QgPSBoKSkvbg0KICB9DQogIGRlbg0KfQ0KDQp4eCA8LSBzZXEobWluKGZhaWwpLTMsIG1heChmYWlsKSszLCBsZW5ndGggPSAzMDApDQoNCm15LmRlbiA8LSBteS5rZXJmLmdhdXNzKGluLmRhdGEgPSBmYWlsLCBoID0gMiwgb3V0LnggPSB4eCkNCg0Ka2RlIDwtIGRlbnNpdHkoZmFpbCwgYncgPSAyLCBrZXJuZWwgPSAiZ2F1c3NpYW4iKQ0Ka2RlLnkgPC0gYXBwcm94KGtkZSR4LCBrZGUkeSwgeG91dCA9IHh4KSR5DQoNCnBsb3QoeHgsIG15LmRlbiwgdHlwZSA9ICJsIiwgbHdkID0gMixjb2w9ICJibHVlIiwNCiAgICAgbWFpbiA9ICJHYXVzc2lhbiBLREUgKGg9Mik6IG15LmtlcmYuZ2F1c3MgdnMgZGVuc2l0eSgpIiwNCiAgICAgeGxhYiA9ICJGYWlsdXJlIHRpbWUiLCB5bGFiID0gIkRlbnNpdHkiKQ0KbGluZXMoeHgsIGtkZS55LCBsdHkgPSAyLCBsd2QgPSAyLCBjb2w9InllbGxvdyIpDQpsZWdlbmQoInRvcHJpZ2h0IiwgYygibXkua2VyZi5nYXVzcygpIiwgImRlbnNpdHkoKSIpLA0KICAgICAgIGx0eSA9IGMoMSwyKSwgbHdkID0gMiwgYnR5ID0gIm4iKQ0KYGBgDQoNCg0KYykgV3JpdGUgYSBjdXN0b20gUiBmdW5jdGlvbiB0aGF0IGNvbXB1dGVzIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRlcyB1c2luZyB0aGUgRXBhbmVjaG5pa292IGtlcm5lbCB3aXRoICRoPTIkLiBWYWxpZGF0ZSB5b3VyIGltcGxlbWVudGF0aW9uIGJ5IGNvbXBhcmluZyByZXN1bHRzIHdpdGggUidzIGJ1aWx0LWluIGRlbnNpdHkoKSBmdW5jdGlvbiBmb3IgR2F1c3NpYW4ga2VybmVsIGVzdGltYXRpb24uDQoNCiQkDQpcaGF0e2Z9X2godCkgPSBcZnJhY3sxfXtuaH1cc3VtX3tpPTF9Xm4gS1xsZWZ0KCBcZnJhY3t0LXRfaX17aH1ccmlnaHQpLCBcIFwgXHRleHR7IHdoZXJlIH0gXCBcIEsodSkgPSBcZnJhY3szfXs0fSgxIC0gdV4yKSBcIFwgXHRleHR7IGZvciB9IFwgXCB8dXwgXGxlIDEuDQokJA0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInfQ0KbXkua2VyZi5lcGFuIDwtIGZ1bmN0aW9uKGluLmRhdGEsIGgsIG91dC54KXsNCiAgbiA8LSBsZW5ndGgoaW4uZGF0YSkNCiAgZGVuIDwtIG51bWVyaWMobGVuZ3RoKG91dC54KSkNCiAgZm9yIChpIGluIDE6bGVuZ3RoKG91dC54KSl7DQogICAgdSA8LSAob3V0LnhbaV0gLSBpbi5kYXRhKS9oDQogICAgSyA8LSBpZmVsc2UoYWJzKHUpIDw9IDEsIDAuNzUqKDEgLSB1XjIpLCAwKQ0KICAgIGRlbltpXSA8LSBzdW0oSykvKG4qaCkNCiAgfQ0KICBkZW4NCn0NCg0KZGVuLmcyIDwtIG15LmtlcmYuZ2F1c3MoZmFpbCwgaCA9IDIsIG91dC54ID0geHgpDQpkZW4uZTIgPC0gbXkua2VyZi5lcGFuKGZhaWwsICBoID0gMiwgb3V0LnggPSB4eCkNCg0KcGxvdCh4eCwgZGVuLmcyLCB0eXBlPSJsIiwgbHdkPTIsY29sPSJnb2xkIiwNCiAgICAgbWFpbj0iS2VybmVsIGNvbXBhcmlzb24gKGg9MikiLA0KICAgICB4bGFiPSJGYWlsdXJlIHRpbWUiLCB5bGFiPSJEZW5zaXR5IikNCmxpbmVzKHh4LCBkZW4uZTIsIGx0eT0yLCBsd2Q9MikNCmxlZ2VuZCgidG9wcmlnaHQiLCBjKCJHYXVzc2lhbiIsICJFcGFuZWNobmlrb3YiKSwNCiAgICAgICBsdHk9YygxLDIpLCBsd2Q9MiwgYnR5PSJuIikNCmBgYA0KDQpkKSBIb3cgZG9lcyB0aGUgY2hvaWNlIG9mIGtlcm5lbCAoR2F1c3NpYW4gdnMuIEVwYW5lY2huaWtvdikgYWZmZWN0IHRoZSBkZW5zaXR5IGVzdGltYXRlPyBGb3IgYm90aCBrZXJuZWwgZXN0aW1hdG9ycyBhcHBsaWVkIHRvIHRoaXMgZGF0YXNldCwgd2hhdCBoYXBwZW5zIHdoZW4gd2Ugc2VsZWN0ICRoPTEuNSQgdmVyc3VzICRoPTIuNSQ/DQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJ30NCmRlbi5nMTUgPC0gbXkua2VyZi5nYXVzcyhmYWlsLCBoID0gMS41LCBvdXQueCA9IHh4KQ0KZGVuLmcyNSA8LSBteS5rZXJmLmdhdXNzKGZhaWwsIGggPSAyLjUsIG91dC54ID0geHgpDQoNCnBsb3QoeHgsIGRlbi5nMTUsIHR5cGU9ImwiLCBsd2Q9Mixjb2w9Im5hdnkiLA0KICAgICBtYWluPSJHYXVzc2lhbiBLREU6IGg9MS41IHZzIGg9Mi41IiwNCiAgICAgeGxhYj0iRmFpbHVyZSB0aW1lIiwgeWxhYj0iRGVuc2l0eSIpDQpsaW5lcyh4eCwgZGVuLmcyNSwgbHR5PTIsIGx3ZD0yKQ0KbGVnZW5kKCJ0b3ByaWdodCIsIGMoImg9MS41IiwgImg9Mi41IiksDQogICAgICAgbHR5PWMoMSwyKSwgbHdkPTIsIGJ0eT0ibiIpDQoNCg0KZGVuLmUxNSA8LSBteS5rZXJmLmVwYW4oZmFpbCwgaCA9IDEuNSwgb3V0LnggPSB4eCkNCmRlbi5lMjUgPC0gbXkua2VyZi5lcGFuKGZhaWwsIGggPSAyLjUsIG91dC54ID0geHgpDQoNCnBsb3QoeHgsIGRlbi5lMTUsIHR5cGU9ImwiLCBsd2Q9MiwgY29sPSJncmVlbiIsDQogICAgIG1haW49IkVwYW5lY2huaWtvdiBLREU6IGg9MS41IHZzIGg9Mi41IiwNCiAgICAgeGxhYj0iRmFpbHVyZSB0aW1lIiwgeWxhYj0iRGVuc2l0eSIpDQpsaW5lcyh4eCwgZGVuLmUyNSwgbHR5PTIsIGx3ZD0yKQ0KbGVnZW5kKCJ0b3ByaWdodCIsIGMoImg9MS41IiwgImg9Mi41IiksDQogICAgICAgbHR5PWMoMSwyKSwgbHdkPTIsIGJ0eT0ibiIpDQoNCmBgYA0KDQoNCioqU29sdXRpb24qKjpFZmZlY3Qgb2YgS2VybmVsIENob2ljZSAoR2F1c3NpYW4gdnMuIEVwYW5lY2huaWtvdiksIFdpdGggdGhlIHNhbWUgYmFuZHdpZHRoLCB0aGUgR2F1c3NpYW4ga2VybmVsIGdpdmVzIGEgc21vb3RoZXIgY3VydmUgd2l0aCBsb25nZXIgdGFpbHMsIHdoaWxlIHRoZSBFcGFuZWNobmlrb3Yga2VybmVsIGZvY3VzZXMgbW9yZSBvbiBuZWFyYnkgb2JzZXJ2YXRpb25zIGJlY2F1c2UgaXQgaGFzIGEgbGltaXRlZCByYW5nZS4gSW4gcHJhY3RpY2UsIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4ga2VybmVscyBpcyB1c3VhbGx5IHNtYWxsIGNvbXBhcmVkIHRvIHRoZSBlZmZlY3Qgb2YgdGhlIGJhbmR3aWR0aC4NCg0KKipTb2x1dGlvbioqOlRoZSBiYW5kd2lkdGggY29udHJvbHMgaG93IHNtb290aCB0aGUgZGVuc2l0eSBlc3RpbWF0ZSBpcy4gQSBzbWFsbGVyIGJhbmR3aWR0aCAo4oSOPTEuNSkgcHJvZHVjZXMgYSBsZXNzIHNtb290aCBjdXJ2ZSB3aXRoIG1vcmUgbG9jYWwgZmx1Y3R1YXRpb25zLCB3aGlsZSBhIGxhcmdlciBiYW5kd2lkdGggKOKEjj0yLjUpIGdpdmVzIGEgc21vb3RoZXIgY3VydmUgYnV0IG1heSBoaWRlIHNvbWUgZGV0YWlscyBvZiB0aGUgZGF0YS4=