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.
# Failure times (in hours) were observed for 8 electronic components:
x <- c(23, 45, 67, 89, 112, 156, 189, 245)
n <- length(x)
# Implementation of ECDF
ecdf_manual <- function(t, x) {
  mean(x <= t)
}
# Validation of Implementation
ecdf_r <- ecdf(x)

t_vals <- seq(0, 260, by = 1)

plot(t_vals, sapply(t_vals, ecdf_manual, x = x),
     type = "s", col = "blue", lwd = 2, main = "Empirical CDF of Failure Times",ylab = "ECDF", xlab = "Failure Time (hours)")
lines(ecdf_r, col = "red", lwd = 2, lty = 2)
legend("bottomright", legend = c("Manual ECDF", "R's ecdf()"),
       col = c("blue", "red"), lty = c(1,2))

Both ECDF’s return identical values and is continuous from the right at every point and increases by 1/n = 0.125 for each observed failure time

  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).
# Observation of colleague claim using ECDF 
ecdf_manual(100, x)
[1] 0.5

The ECDF at 100 hours is 0.5, meaning 50% of observed failures occurred before 100 hours.


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.
# Failure times from a mechanical system
y <- c(12.3, 14.7, 15.2, 16.8, 18.1,
       19.4, 20.6, 22.3, 23.9, 25.4)

n <= length(y)
[1] TRUE
# Compute range and bin width
data_range <- range(y)
bin_width <- diff(data_range) / 3

# Define bin boundaries
breaks <- seq(from = data_range[1],
              to   = data_range[2],
              length.out = 4)

breaks
[1] 12.30000 16.66667 21.03333 25.40000
# Count observations in each bin
bin_counts <- table(cut(y, breaks = breaks, include.lowest = TRUE))
bin_counts

[12.3,16.7]   (16.7,21]   (21,25.4] 
          3           4           3 
# Compute estimated density in each bin
bin_densities <- as.numeric(bin_counts) / (n * bin_width)

bin_densities
[1] 0.08587786 0.11450382 0.08587786
# Present results
density_table <- data.frame(
  Bin = levels(cut(y, breaks = breaks, include.lowest = TRUE)),
  Count = as.numeric(bin_counts),
  Density = bin_densities
)

density_table
          Bin Count    Density
1 [12.3,16.7]     3 0.08587786
2   (16.7,21]     4 0.11450382
3   (21,25.4]     3 0.08587786
# Visualization 
hist(y, breaks = breaks, probability = TRUE,
     main = "Histogram with Estimated Bin Densities",
     xlab = "Failure Time")

The estimated density in each histogram bin is computed by dividing the number of observations in that bin by the product of the sample size and the bin width. The densities sum to 1 when multiplied by band width with the middle bin having the highest density where failure times are most concentrated.

The histogram is unimodal and symmetric, having a smooth distribution

  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}. \]

# Gaussian Kernel Density Estimation (h = 2)
kde_gaussian <- function(t, x, h) {
  n <- length(x)
  sapply(t, function(tt) {
    mean(dnorm((tt - x) / h)) / h
  })
}
# Validation against density()
t_grid <- seq(min(y) - 2, max(y) + 2, length = 200)

plot(t_grid, kde_gaussian(t_grid, y, h = 2),
     type = "l", lwd = 2, col = "blue",
     xlab = "t", ylab = "Density",
     main = "Gaussian KDE Comparison")

lines(density(y, bw = 2), col = "red", lty = 2)

legend("topright",
       legend = c("Manual KDE", "R density()"),
       col = c("blue", "red"), lty = c(1, 2))

The curves overlap correctly and confirms the correctness of the manuel KDE implementation

  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. \]

# Epanechnikov Kernel Density Estimation (h = 2)
kde_epanechnikov <- function(t, x, h) {
  n <- length(x)
  sapply(t, function(tt) {
    u <- (tt - x) / h
    mean(0.75 * (1 - u^2) * (abs(u) <= 1)) / h
  })
}
# Visualization 
plot(t_grid, kde_epanechnikov(t_grid, y, h = 2),
     type = "l", lwd = 2, col = "green",
     main = "Epanechnikov KDE (h = 2)",
     ylab = "Density", xlab = "Failure Time")
lines(density(y, bw = 2), col = "red", lty = 2)
legend("topright", c("Epanechnikov", "Gaussian"),
       col = c("green", "red"), lty = c(1,1))

The Epanechnikov KDE is more compact with limited support

  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\)?

Kernel choice

  • Gaussian: smoother tails, infinite support
  • Epanechnikov: compact support, sharper cutoff
  • Differences are small
density(y, bw = 1.5)

Call:
    density.default(x = y, bw = 1.5)

Data: y (10 obs.);  Bandwidth 'bw' = 1.5

       x               y           
 Min.   : 7.80   Min.   :0.000297  
 1st Qu.:13.32   1st Qu.:0.015141  
 Median :18.85   Median :0.055866  
 Mean   :18.85   Mean   :0.045149  
 3rd Qu.:24.38   3rd Qu.:0.071778  
 Max.   :29.90   Max.   :0.077948  
density(y, bw = 2.5)

Call:
    density.default(x = y, bw = 2.5)

Data: y (10 obs.);  Bandwidth 'bw' = 2.5

       x               y            
 Min.   : 4.80   Min.   :0.0001868  
 1st Qu.:11.82   1st Qu.:0.0057773  
 Median :18.85   Median :0.0343512  
 Mean   :18.85   Mean   :0.0355077  
 3rd Qu.:25.88   3rd Qu.:0.0644896  
 Max.   :32.90   Max.   :0.0743308  

Bandwidth effect

  • h = 1.5 → less smoothing, more bumps, higher variance
  • h = 2.5 → more smoothing, flatter curve, higher bias

Conclusion

  • Bandwidth has a much larger impact on the density estimate than kernel choice.
LS0tDQp0aXRsZTogIkFzc2lnbm1lbnQgMTogRXN0aW1hdGluZyBDREYgYW5kIFBERiINCmF1dGhvcjogIktpZXJhbiBIZWZmZXJhbiAiDQpkYXRlOiAiIER1ZTogMi8zLzI2ICINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogbm8NCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMNCiAgICB0aGVtZTogbHVtZW4NCiAgcGRmX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIGZpZ193aWR0aDogMw0KICAgIGZpZ19oZWlnaHQ6IDMNCiAgd29yZF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAga2VlcF9tZDogeWVzDQplZGl0b3Jfb3B0aW9uczogDQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCi0tLQ0KDQpgYGB7Y3NzLCBlY2hvID0gRkFMU0V9DQojVE9DOjpiZWZvcmUgew0KICBjb250ZW50OiAiVGFibGUgb2YgQ29udGVudHMiOw0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1zaXplOiAxLjJlbTsNCiAgZGlzcGxheTogYmxvY2s7DQogIGNvbG9yOiBuYXZ5Ow0KICBtYXJnaW4tYm90dG9tOiAxMHB4Ow0KfQ0KDQoNCmRpdiNUT0MgbGkgeyAgICAgLyogdGFibGUgb2YgY29udGVudCAgKi8NCiAgICBsaXN0LXN0eWxlOnVwcGVyLXJvbWFuOw0KICAgIGJhY2tncm91bmQtaW1hZ2U6bm9uZTsNCiAgICBiYWNrZ3JvdW5kLXJlcGVhdDpub25lOw0KICAgIGJhY2tncm91bmQtcG9zaXRpb246MDsNCn0NCg0KaDEudGl0bGUgeyAgICAvKiBsZXZlbCAxIGhlYWRlciBvZiB0aXRsZSAgKi8NCiAgZm9udC1zaXplOiAyMnB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgY29sb3I6IERhcmtSZWQ7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOw0KfQ0KDQpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICBmb250LXNpemU6IDE1cHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogbmF2eTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KDQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOw0KICBjb2xvcjogRGFya0JsdWU7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCg0KaDEgeyAvKiBIZWFkZXIgMSAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMjBweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQoNCmgyIHsgLyogSGVhZGVyIDIgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDMgeyAvKiBIZWFkZXIgMyAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMTZweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoNCB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogICAgZm9udC1zaXplOiAxNHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQovKiBBZGQgZG90cyBhZnRlciBudW1iZXJlZCBoZWFkZXJzICovDQouaGVhZGVyLXNlY3Rpb24tbnVtYmVyOjphZnRlciB7DQogIGNvbnRlbnQ6ICIuIjsNCg0KYm9keSB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0NCg0KcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KfQ0KYGBgDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IA0KIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuDQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoInBhbmRlciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwYW5kZXIiKQ0KICAgbGlicmFyeShwYW5kZXIpDQp9DQppZiAoIXJlcXVpcmUoImdncGxvdDIiKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikNCiAgbGlicmFyeShnZ3Bsb3QyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJ0aWR5dmVyc2UiKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KICBsaWJyYXJ5KHRpZHl2ZXJzZSkNCn0NCg0KaWYgKCFyZXF1aXJlKCJwbG90bHkiKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KICBsaWJyYXJ5KHBsb3RseSkNCn0NCiMjIyMNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCAgICMgc29tZXRpbWVzLCB5b3UgY29kZSBtYXkgcHJvZHVjZSB3YXJuaW5nIG1lc3NhZ2VzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHlvdSBjYW4gY2hvb3NlIHRvIGluY2x1ZGUgdGhlIHdhcm5pbmcgbWVzc2FnZXMgaW4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb3V0cHV0IGZpbGUuIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFLCAgICAjIHlvdSBjYW4gYWxzbyBkZWNpZGUgd2hldGhlciB0byBpbmNsdWRlIHRoZSBvdXRwdXQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBpbiB0aGUgb3V0cHV0IGZpbGUuDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQQ0KICAgICAgICAgICAgICAgICAgICAgICkgIA0KYGBgDQogDQogXA0KIA0KIyMgKipBc3NpZ25tZW50IE9iamVjdGl2ZXMqKiANCg0KKiBEZXZlbG9wIGEgY2xlYXIgdGVjaG5pY2FsIHVuZGVyc3RhbmRpbmcgb2Ygbm9ucGFyYW1ldHJpYyBjdW11bGF0aXZlIGRpc3RyaWJ1dGlvbiBmdW5jdGlvbiAoQ0RGKSBlc3RpbWF0aW9uIGFuZCB2YXJpb3VzIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRvcnMuDQoNCiogVHJhbnNsYXRlIG1hdGhlbWF0aWNhbCBmb3JtdWxhcyBpbnRvIFIgZnVuY3Rpb25zIGFuZCBhcHBseSB0aGVtIHRvIHNvbHZlIHJlbGF0ZWQgcHJvYmxlbXMuDQoNCiogQ3JlYXRlIGVmZmVjdGl2ZSB2aXN1YWxpemF0aW9ucyB0byBkZW1vbnN0cmF0ZSB5b3VyIHVuZGVyc3RhbmRpbmcgb2Yga2V5IGNvbmNlcHRzIGluIHRoZSBmb2xsb3dpbmcgcXVlc3Rpb25zLg0KDQpcDQoNCiMjICoqUXVlc3Rpb24gMTogQ3VtdWxhdGl2ZSBEaXN0cmlidXRpb24gRnVuY3Rpb24gKENERikgRXN0aW1hdGlvbioqDQoNClRoZSBmb2xsb3dpbmcgZmFpbHVyZSB0aW1lcyAoaW4gaG91cnMpIHdlcmUgb2JzZXJ2ZWQgZm9yIDggZWxlY3Ryb25pYyBjb21wb25lbnRzOg0KDQo8Y2VudGVyPiAyMywgNDUsIDY3LCA4OSwgMTEyLCAxNTYsIDE4OSwgMjQ1ICA8L2NlbnRlcj4NCg0KYSkgV3JpdGUgYW4gUiBmdW5jdGlvbiBpbXBsZW1lbnRpbmcgdGhlIEVDREYgJFxoYXR7Rn1fbih0KSQgYWNjb3JkaW5nIHRvIGl0cyBtYXRoZW1hdGljYWwgZGVmaW5pdGlvbi4gVmFsaWRhdGUgeW91ciBpbXBsZW1lbnRhdGlvbiB1c2luZyBSJ3MgZWNkZigpIGZ1bmN0aW9uIG9uIHRoZSBnaXZlbiBkYXRhLCB3aXRoIGNvbXBhcmlzb24gYmFzZWQgb24gdGhlaXIgc3RlcCBmdW5jdGlvbnMuDQpgYGB7cn0NCiMgRmFpbHVyZSB0aW1lcyAoaW4gaG91cnMpIHdlcmUgb2JzZXJ2ZWQgZm9yIDggZWxlY3Ryb25pYyBjb21wb25lbnRzOg0KeCA8LSBjKDIzLCA0NSwgNjcsIDg5LCAxMTIsIDE1NiwgMTg5LCAyNDUpDQpuIDwtIGxlbmd0aCh4KQ0KYGBgDQoNCmBgYHtyfQ0KIyBJbXBsZW1lbnRhdGlvbiBvZiBFQ0RGDQplY2RmX21hbnVhbCA8LSBmdW5jdGlvbih0LCB4KSB7DQogIG1lYW4oeCA8PSB0KQ0KfQ0KYGBgDQoNCmBgYHtyfQ0KIyBWYWxpZGF0aW9uIG9mIEltcGxlbWVudGF0aW9uDQplY2RmX3IgPC0gZWNkZih4KQ0KDQp0X3ZhbHMgPC0gc2VxKDAsIDI2MCwgYnkgPSAxKQ0KDQpwbG90KHRfdmFscywgc2FwcGx5KHRfdmFscywgZWNkZl9tYW51YWwsIHggPSB4KSwNCiAgICAgdHlwZSA9ICJzIiwgY29sID0gImJsdWUiLCBsd2QgPSAyLCBtYWluID0gIkVtcGlyaWNhbCBDREYgb2YgRmFpbHVyZSBUaW1lcyIseWxhYiA9ICJFQ0RGIiwgeGxhYiA9ICJGYWlsdXJlIFRpbWUgKGhvdXJzKSIpDQpsaW5lcyhlY2RmX3IsIGNvbCA9ICJyZWQiLCBsd2QgPSAyLCBsdHkgPSAyKQ0KbGVnZW5kKCJib3R0b21yaWdodCIsIGxlZ2VuZCA9IGMoIk1hbnVhbCBFQ0RGIiwgIlIncyBlY2RmKCkiKSwNCiAgICAgICBjb2wgPSBjKCJibHVlIiwgInJlZCIpLCBsdHkgPSBjKDEsMikpDQoNCmBgYA0KDQpCb3RoIEVDREYncyByZXR1cm4gaWRlbnRpY2FsIHZhbHVlcyBhbmQgaXMgY29udGludW91cyBmcm9tIHRoZSByaWdodCBhdCBldmVyeSBwb2ludCBhbmQgaW5jcmVhc2VzIGJ5IDEvbiA9IDAuMTI1IGZvciBlYWNoIG9ic2VydmVkIGZhaWx1cmUgdGltZQ0KDQoNCmIpIEEgY29sbGVhZ3VlIGNsYWltcyB0aGF0IHRoZSBwcm9iYWJpbGl0eSBvZiBmYWlsdXJlIGJlZm9yZSAxMDAgaG91cnMgaXMgMC41IGJhc2VkIG9uIHRoZXNlIGRhdGEuIERvIHlvdSBhZ3JlZT8gRXhwbGFpbiB5b3VyIHJlYXNvbmluZyB1c2luZyB0aGUgZW1waXJpY2FsIGN1bXVsYXRpdmUgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uIChFQ0RGKS4NCg0KYGBge3J9DQojIE9ic2VydmF0aW9uIG9mIGNvbGxlYWd1ZSBjbGFpbSB1c2luZyBFQ0RGIA0KZWNkZl9tYW51YWwoMTAwLCB4KQ0KYGBgDQoNClRoZSBFQ0RGIGF0IDEwMCBob3VycyBpcyAwLjUsIG1lYW5pbmcgNTAlIG9mIG9ic2VydmVkIGZhaWx1cmVzIG9jY3VycmVkIGJlZm9yZSAxMDAgaG91cnMuDQoNClwNCg0KIyMgKipRdWVzdGlvbiAyOiBEZW5zaXR5IEZ1bmN0aW9uIEVzdGltYXRpb24qKg0KDQpDb25zaWRlciB0aGUgZm9sbG93aW5nIGZhaWx1cmUgdGltZXMgZnJvbSBhIG1lY2hhbmljYWwgc3lzdGVtOg0KDQo8Y2VudGVyPiAxMi4zLCAxNC43LCAxNS4yLCAxNi44LCAxOC4xLCAxOS40LCAyMC42LCAyMi4zLCAyMy45LCAyNS40IDwvY2VudGVyPg0KDQphKSBDcmVhdGUgYSBoaXN0b2dyYW0gb2YgdGhlIGRhdGEgdXNpbmcgMyBlcXVhbGx5IHNwYWNlZCBiaW5zLiBXaGF0IGlzIHRoZSBlc3RpbWF0ZWQgZGVuc2l0eSBpbiBlYWNoIGJpbj8gRGVzY3JpYmUgdGhlIHNoYXBlIG9mIHRoZSBoaXN0b2dyYW0ncyBkaXN0cmlidXRpb24uDQoNCmBgYHtyfQ0KIyBGYWlsdXJlIHRpbWVzIGZyb20gYSBtZWNoYW5pY2FsIHN5c3RlbQ0KeSA8LSBjKDEyLjMsIDE0LjcsIDE1LjIsIDE2LjgsIDE4LjEsDQogICAgICAgMTkuNCwgMjAuNiwgMjIuMywgMjMuOSwgMjUuNCkNCg0KbiA8PSBsZW5ndGgoeSkNCmBgYA0KDQpgYGB7cn0NCiMgQ29tcHV0ZSByYW5nZSBhbmQgYmluIHdpZHRoDQpkYXRhX3JhbmdlIDwtIHJhbmdlKHkpDQpiaW5fd2lkdGggPC0gZGlmZihkYXRhX3JhbmdlKSAvIDMNCg0KIyBEZWZpbmUgYmluIGJvdW5kYXJpZXMNCmJyZWFrcyA8LSBzZXEoZnJvbSA9IGRhdGFfcmFuZ2VbMV0sDQogICAgICAgICAgICAgIHRvICAgPSBkYXRhX3JhbmdlWzJdLA0KICAgICAgICAgICAgICBsZW5ndGgub3V0ID0gNCkNCg0KYnJlYWtzDQpgYGANCg0KYGBge3J9DQojIENvdW50IG9ic2VydmF0aW9ucyBpbiBlYWNoIGJpbg0KYmluX2NvdW50cyA8LSB0YWJsZShjdXQoeSwgYnJlYWtzID0gYnJlYWtzLCBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpKQ0KYmluX2NvdW50cw0KYGBgDQoNCmBgYHtyfQ0KIyBDb21wdXRlIGVzdGltYXRlZCBkZW5zaXR5IGluIGVhY2ggYmluDQpiaW5fZGVuc2l0aWVzIDwtIGFzLm51bWVyaWMoYmluX2NvdW50cykgLyAobiAqIGJpbl93aWR0aCkNCg0KYmluX2RlbnNpdGllcw0KYGBgDQoNCmBgYHtyfQ0KIyBQcmVzZW50IHJlc3VsdHMNCmRlbnNpdHlfdGFibGUgPC0gZGF0YS5mcmFtZSgNCiAgQmluID0gbGV2ZWxzKGN1dCh5LCBicmVha3MgPSBicmVha3MsIGluY2x1ZGUubG93ZXN0ID0gVFJVRSkpLA0KICBDb3VudCA9IGFzLm51bWVyaWMoYmluX2NvdW50cyksDQogIERlbnNpdHkgPSBiaW5fZGVuc2l0aWVzDQopDQoNCmRlbnNpdHlfdGFibGUNCmBgYA0KDQpgYGB7cn0NCiMgVmlzdWFsaXphdGlvbiANCmhpc3QoeSwgYnJlYWtzID0gYnJlYWtzLCBwcm9iYWJpbGl0eSA9IFRSVUUsDQogICAgIG1haW4gPSAiSGlzdG9ncmFtIHdpdGggRXN0aW1hdGVkIEJpbiBEZW5zaXRpZXMiLA0KICAgICB4bGFiID0gIkZhaWx1cmUgVGltZSIpDQpgYGANCg0KVGhlIGVzdGltYXRlZCBkZW5zaXR5IGluIGVhY2ggaGlzdG9ncmFtIGJpbiBpcyBjb21wdXRlZCBieSBkaXZpZGluZyB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiB0aGF0IGJpbiBieSB0aGUgcHJvZHVjdCBvZiB0aGUgc2FtcGxlIHNpemUgYW5kIHRoZSBiaW4gd2lkdGguIFRoZSBkZW5zaXRpZXMgc3VtIHRvIDEgd2hlbiBtdWx0aXBsaWVkIGJ5IGJhbmQgd2lkdGggd2l0aCB0aGUgbWlkZGxlIGJpbiBoYXZpbmcgdGhlIGhpZ2hlc3QgZGVuc2l0eSB3aGVyZSBmYWlsdXJlIHRpbWVzIGFyZSBtb3N0IGNvbmNlbnRyYXRlZC4NCg0KVGhlIGhpc3RvZ3JhbSBpcyB1bmltb2RhbCBhbmQgc3ltbWV0cmljLCBoYXZpbmcgYSBzbW9vdGggZGlzdHJpYnV0aW9uDQoNCmIpIFdyaXRlIGFuIFIgZnVuY3Rpb24gdGhhdCBjb21wdXRlcyBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZXMgdXNpbmcgYSBHYXVzc2lhbiBrZXJuZWwgd2l0aCAkaD0yJC4gVmFsaWRhdGUgeW91ciBpbXBsZW1lbnRhdGlvbiBhZ2FpbnN0IFIncyBidWlsdC1pbiBkZW5zaXR5KCkgZnVuY3Rpb24uDQoNCiQkDQpcaGF0e2Z9X2godCkgPSBcZnJhY3sxfXtuaH1cc3VtX3tpPTF9Xm4gS1xsZWZ0KCBcZnJhY3t0LXRfaX17aH1ccmlnaHQpLCBcIFwgXHRleHR7IHdoZXJlIH0gXCBcIEsodSkgPSBcZnJhY3sxfXtcc3FydHsyXHBpfX0gZV57LXVeMi8yfS4NCiQkDQoNCmBgYHtyfQ0KIyBHYXVzc2lhbiBLZXJuZWwgRGVuc2l0eSBFc3RpbWF0aW9uIChoID0gMikNCmtkZV9nYXVzc2lhbiA8LSBmdW5jdGlvbih0LCB4LCBoKSB7DQogIG4gPC0gbGVuZ3RoKHgpDQogIHNhcHBseSh0LCBmdW5jdGlvbih0dCkgew0KICAgIG1lYW4oZG5vcm0oKHR0IC0geCkgLyBoKSkgLyBoDQogIH0pDQp9DQpgYGANCg0KYGBge3J9DQojIFZhbGlkYXRpb24gYWdhaW5zdCBkZW5zaXR5KCkNCnRfZ3JpZCA8LSBzZXEobWluKHkpIC0gMiwgbWF4KHkpICsgMiwgbGVuZ3RoID0gMjAwKQ0KDQpwbG90KHRfZ3JpZCwga2RlX2dhdXNzaWFuKHRfZ3JpZCwgeSwgaCA9IDIpLA0KICAgICB0eXBlID0gImwiLCBsd2QgPSAyLCBjb2wgPSAiYmx1ZSIsDQogICAgIHhsYWIgPSAidCIsIHlsYWIgPSAiRGVuc2l0eSIsDQogICAgIG1haW4gPSAiR2F1c3NpYW4gS0RFIENvbXBhcmlzb24iKQ0KDQpsaW5lcyhkZW5zaXR5KHksIGJ3ID0gMiksIGNvbCA9ICJyZWQiLCBsdHkgPSAyKQ0KDQpsZWdlbmQoInRvcHJpZ2h0IiwNCiAgICAgICBsZWdlbmQgPSBjKCJNYW51YWwgS0RFIiwgIlIgZGVuc2l0eSgpIiksDQogICAgICAgY29sID0gYygiYmx1ZSIsICJyZWQiKSwgbHR5ID0gYygxLCAyKSkNCmBgYA0KDQpUaGUgY3VydmVzIG92ZXJsYXAgY29ycmVjdGx5IGFuZCBjb25maXJtcyB0aGUgY29ycmVjdG5lc3Mgb2YgdGhlIG1hbnVlbCBLREUgaW1wbGVtZW50YXRpb24NCg0KYykgV3JpdGUgYSBjdXN0b20gUiBmdW5jdGlvbiB0aGF0IGNvbXB1dGVzIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRlcyB1c2luZyB0aGUgRXBhbmVjaG5pa292IGtlcm5lbCB3aXRoICRoPTIkLiBWYWxpZGF0ZSB5b3VyIGltcGxlbWVudGF0aW9uIGJ5IGNvbXBhcmluZyByZXN1bHRzIHdpdGggUidzIGJ1aWx0LWluIGRlbnNpdHkoKSBmdW5jdGlvbiBmb3IgR2F1c3NpYW4ga2VybmVsIGVzdGltYXRpb24uDQoNCg0KJCQNClxoYXR7Zn1faCh0KSA9IFxmcmFjezF9e25ofVxzdW1fe2k9MX1ebiBLXGxlZnQoIFxmcmFje3QtdF9pfXtofVxyaWdodCksIFwgXCBcdGV4dHsgd2hlcmUgfSBcIFwgSyh1KSA9IFxmcmFjezN9ezR9KDEgLSB1XjIpIFwgXCBcdGV4dHsgZm9yIH0gXCBcIHx1fCBcbGUgMS4NCiQkDQoNCmBgYHtyfQ0KIyBFcGFuZWNobmlrb3YgS2VybmVsIERlbnNpdHkgRXN0aW1hdGlvbiAoaCA9IDIpDQprZGVfZXBhbmVjaG5pa292IDwtIGZ1bmN0aW9uKHQsIHgsIGgpIHsNCiAgbiA8LSBsZW5ndGgoeCkNCiAgc2FwcGx5KHQsIGZ1bmN0aW9uKHR0KSB7DQogICAgdSA8LSAodHQgLSB4KSAvIGgNCiAgICBtZWFuKDAuNzUgKiAoMSAtIHVeMikgKiAoYWJzKHUpIDw9IDEpKSAvIGgNCiAgfSkNCn0NCmBgYA0KDQpgYGB7cn0NCiMgVmlzdWFsaXphdGlvbiANCnBsb3QodF9ncmlkLCBrZGVfZXBhbmVjaG5pa292KHRfZ3JpZCwgeSwgaCA9IDIpLA0KICAgICB0eXBlID0gImwiLCBsd2QgPSAyLCBjb2wgPSAiZ3JlZW4iLA0KICAgICBtYWluID0gIkVwYW5lY2huaWtvdiBLREUgKGggPSAyKSIsDQogICAgIHlsYWIgPSAiRGVuc2l0eSIsIHhsYWIgPSAiRmFpbHVyZSBUaW1lIikNCmxpbmVzKGRlbnNpdHkoeSwgYncgPSAyKSwgY29sID0gInJlZCIsIGx0eSA9IDIpDQpsZWdlbmQoInRvcHJpZ2h0IiwgYygiRXBhbmVjaG5pa292IiwgIkdhdXNzaWFuIiksDQogICAgICAgY29sID0gYygiZ3JlZW4iLCAicmVkIiksIGx0eSA9IGMoMSwxKSkNCmBgYA0KDQpUaGUgRXBhbmVjaG5pa292IEtERSBpcyBtb3JlIGNvbXBhY3Qgd2l0aCBsaW1pdGVkIHN1cHBvcnQNCg0KZCkgSG93IGRvZXMgdGhlIGNob2ljZSBvZiBrZXJuZWwgKEdhdXNzaWFuIHZzLiBFcGFuZWNobmlrb3YpIGFmZmVjdCB0aGUgZGVuc2l0eSBlc3RpbWF0ZT8gRm9yIGJvdGgga2VybmVsIGVzdGltYXRvcnMgYXBwbGllZCB0byB0aGlzIGRhdGFzZXQsIHdoYXQgaGFwcGVucyB3aGVuIHdlIHNlbGVjdCAkaD0xLjUkIHZlcnN1cyAkaD0yLjUkPw0KDQpLZXJuZWwgY2hvaWNlDQoNCi0gICBHYXVzc2lhbjogc21vb3RoZXIgdGFpbHMsIGluZmluaXRlIHN1cHBvcnQNCi0gICBFcGFuZWNobmlrb3Y6IGNvbXBhY3Qgc3VwcG9ydCwgc2hhcnBlciBjdXRvZmYNCi0gICBEaWZmZXJlbmNlcyBhcmUgc21hbGwNCg0KYGBge3J9DQpkZW5zaXR5KHksIGJ3ID0gMS41KQ0KZGVuc2l0eSh5LCBidyA9IDIuNSkNCmBgYA0KDQpCYW5kd2lkdGggZWZmZWN0DQoNCi0gICBoID0gMS41IOKGkiBsZXNzIHNtb290aGluZywgbW9yZSBidW1wcywgaGlnaGVyIHZhcmlhbmNlDQotICAgaCA9IDIuNSDihpIgbW9yZSBzbW9vdGhpbmcsIGZsYXR0ZXIgY3VydmUsIGhpZ2hlciBiaWFzDQoNCg0KQ29uY2x1c2lvbg0KDQotICAgQmFuZHdpZHRoIGhhcyBhIG11Y2ggbGFyZ2VyIGltcGFjdCBvbiB0aGUgZGVuc2l0eSBlc3RpbWF0ZSB0aGFuIGtlcm5lbCBjaG9pY2UuDQoNCg0KDQoNCg==