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.
# Create a vector to store the 8 failure times/observations
failure_times <- c(23, 45, 67, 89, 112, 156, 189, 245)

# Simplified R function
my_ecdf <- function(data, t) {
  n <- length(data)
  results <- numeric(length(t)) # Create an empty vector to store answers
  
  for (i in 1:length(t)) {
    # Count how many data points are less than or equal to current t[i]
    count <- sum(data <= t[i])
    
    # Divide by total number of points and find cumulative probability
    results[i] <- count / n
  }
  
  return(results)
}
# Create a sequence of time values to test
test_vals <- seq(0, 250, by = 10)

# 1. Run custom function
my_results <- my_ecdf(failure_times, test_vals)

# 2. Run R's built-in ecdf function and use it directly on the observation data set (8 failure times)
r_builtin_func <- ecdf(failure_times)
r_results <- r_builtin_func(test_vals) #use the same 26 test values to find heights on ecdf through built in R function

# 3. Conduct a comparison of the results from both methods. 
comparison <- data.frame(
  Time = test_vals,
  Custom = my_results,
  BuiltIn = r_results,
  Equal = (my_results == r_results)
)

print(comparison)
   Time Custom BuiltIn Equal
1     0  0.000   0.000  TRUE
2    10  0.000   0.000  TRUE
3    20  0.000   0.000  TRUE
4    30  0.125   0.125  TRUE
5    40  0.125   0.125  TRUE
6    50  0.250   0.250  TRUE
7    60  0.250   0.250  TRUE
8    70  0.375   0.375  TRUE
9    80  0.375   0.375  TRUE
10   90  0.500   0.500  TRUE
11  100  0.500   0.500  TRUE
12  110  0.500   0.500  TRUE
13  120  0.625   0.625  TRUE
14  130  0.625   0.625  TRUE
15  140  0.625   0.625  TRUE
16  150  0.625   0.625  TRUE
17  160  0.750   0.750  TRUE
18  170  0.750   0.750  TRUE
19  180  0.750   0.750  TRUE
20  190  0.875   0.875  TRUE
21  200  0.875   0.875  TRUE
22  210  0.875   0.875  TRUE
23  220  0.875   0.875  TRUE
24  230  0.875   0.875  TRUE
25  240  0.875   0.875  TRUE
26  250  1.000   1.000  TRUE
# Plot  results as a step line
plot(test_vals, my_results, type = "s", col = "darkgreen", lwd = 4,
     main = "Validation: My ECDF vs R's ECDF", xlab = "Hours", ylab = "Fn(t)")

# Overlay R results as dashed line for overlap analysis
lines(test_vals, r_results, type = "s", col = "white", lty = 2, lwd = 2)

  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).
#Can now use the ecdf function created above to validate this claim
prob_100 <- my_ecdf(data = failure_times, t = 100)
print(prob_100)
[1] 0.5
#Can also test this with the r built-in ecdf 
r_100 <- r_builtin_func(100)
print(r_100)
[1] 0.5
#I agree that the probability of failure before 100 hours is 0.5 - the ecdf returns the cumulative height of the fourth step of the function, as this is where '100' falls. The fourth step represents 4/8 of the data which is 0.5


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.
times <- c(12.3, 14.7, 15.2, 16.8, 18.1, 19.4, 20.6, 22.3, 23.9, 25.4)

# 1. Create the histogram and store it
# freq = FALSE is required to get density instead of counts
hist_output <- hist(times, breaks = 3, freq = FALSE, plot = TRUE)

# 2. Print the density values
cat("The estimated densities for the 3 bins are:\n")
The estimated densities for the 3 bins are:
print(hist_output$density)
[1] 0.04 0.08 0.06 0.02
# 3. To see which density belongs to which range:
cat("\nThe bin breaks are:\n")

The bin breaks are:
print(hist_output$breaks)
[1] 10 15 20 25 30
#The histogram is approximately symmetric. 
  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  Gaussian kde
# x_in:  raw failure data
# h: bandwidth 
# x_out: points where we want to estimate the density
calc_kde <- function(x_in, h, x_out) {
  n <- length(x_in)
  
  # Set up a vector of zeros 
  densities <- numeric(length(x_out))
  
  for (j in 1:length(x_out)) {
    # Calculate the average height of  Gaussian kernels at point x_out[j]
    # dnorm - mean is the data point; sd is bandwidth h
    kernel_heights <- dnorm(x_out[j], mean = x_in, sd = h)
    densities[j] <- sum(kernel_heights) / n
  }
  
  return(densities)
}
# 1. Setup data using observed failure times
times <- c(12.3, 14.7, 15.2, 16.8, 18.1, 19.4, 20.6, 22.3, 23.9, 25.4)
h_val <- 2
test_points <- seq(10, 30, length.out = 10)

# 2. Run my function
my_kde_results <- calc_kde(times, h_val, test_points)

# 3. Run R's built-in density

r_kde_obj <- density(
  times,
  bw = h_val,
  kernel = "gaussian",
  from = min(test_points),
  to   = max(test_points),
  n    = length(test_points),
  cut  = 0
)
r_kde_results <- approx(r_kde_obj$x, r_kde_obj$y, xout = test_points)$y

# 4. Compare
comparison <- data.frame(
  Time = test_points,
  My_KDE = my_kde_results,
  R_KDE = r_kde_results,
  Difference = my_kde_results - r_kde_results
)

print(comparison)
       Time      My_KDE       R_KDE    Difference
1  10.00000 0.012304290 0.012308026 -3.735623e-06
2  12.22222 0.037529552 0.037530006 -4.542322e-07
3  14.44444 0.064417678 0.064412699  4.979023e-06
4  16.66667 0.075847963 0.075845001  2.961556e-06
5  18.88889 0.074730398 0.074730618 -2.200108e-07
6  21.11111 0.068149670 0.068151732 -2.062049e-06
7  23.33333 0.059789442 0.059787067  2.375001e-06
8  25.55556 0.040472328 0.040469959  2.369062e-06
9  27.77778 0.013387688 0.013389752 -2.063463e-06
10 30.00000 0.001619231 0.001621044 -1.813352e-06
  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_epan_kde <- function(x_in, h, x_out) {
  n <- length(x_in)
  densities <- numeric(length(x_out))
  
  for (i in 1:length(x_out)) {
    # 1. Calculate distance (u)
    u <- (x_out[i] - x_in) / h
    
    # 2. Apply parabola formula: 3/4 * (1 - u^2)
    k_values <- (3/4) * (1 - u^2)
    
    # 3. if distance |u| is > 1, the result must be 0
    # This replaces any negative or "out of bounds" values with 0
    k_values[abs(u) > 1] <- 0
    
    # 4. Final Sum
    densities[i] <- sum(k_values) / (n * h)
  }
  return(densities)
}

h_val <- 2
test_pts <- seq(10, 30, length.out = 10)

# my epanechnikov
my_epan_results <- my_epan_kde(times, h_val, test_pts)

# R's built-in Epanechnikov
r_epan_obj <- density(times, bw = h_val, kernel = "epanechnikov")
r_epan_results <- approx(r_epan_obj$x, r_epan_obj$y, xout = test_pts)$y

# Compare
data.frame(Time = test_pts, Custom = my_epan_results, BuiltIn = r_epan_results)
       Time     Custom    BuiltIn
1  10.00000 0.00000000 0.01233304
2  12.22222 0.03744329 0.03771678
3  14.44444 0.06903588 0.06359744
4  16.66667 0.07414583 0.07536935
5  18.88889 0.07676736 0.07464258
6  21.11111 0.06935069 0.06838575
7  23.33333 0.06197917 0.05985608
8  25.55556 0.04907755 0.03910093
9  27.77778 0.00000000 0.01618696
10 30.00000 0.00000000 0.00000000
#To validate the implementation, the custom Epanechnikov KDE was compared to R’s density() using the Epanechnikov kernel, showing close agreement. For completeness, a Gaussian kernel estimate was also examined, which exhibits smoother tails as expected

#In the custom my_epan_kde() function, observations with ∣x0−xi∣> hare assigned zero weight via k_values[abs(u) > 1] <- 0, reflecting the compact support of the Epanechnikov kernel. As a result, the density estimate is exactly zero at evaluation points where no observations lie within h = 2.
  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\)?

The kernel choice affects the smoothness and tail behavior of the estimate. The Gaussian kernel produces smoother curves/non-zero tails and the Epanechnikov kerenel yields localized estimates. Selecting h = 1.5 for the bandwith results in an estimable that is more variable; h = 2.5 reduces varianceand produces a smoother curve with more bias.

LS0tDQp0aXRsZTogIkFzc2lnbm1lbnQgMTogRXN0aW1hdGluZyBDREYgYW5kIFBERiINCmF1dGhvcjogIlJhbXlhIFBpbm5pbnRpIg0KZGF0ZTogIiBEdWU6IEZlYnJ1YXJ5IDMsIDIwMjYiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IG5vDQogICAgdG9jX2NvbGxhcHNlZDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgc21vb3RoX3Njcm9sbDogeWVzDQogICAgdGhlbWU6IGx1bWVuDQogIHBkZl9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICBmaWdfd2lkdGg6IDMNCiAgICBmaWdfaGVpZ2h0OiAzDQogIHdvcmRfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIGtlZXBfbWQ6IHllcw0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KYGBge2NzcywgZWNobyA9IEZBTFNFfQ0KI1RPQzo6YmVmb3JlIHsNCiAgY29udGVudDogIlRhYmxlIG9mIENvbnRlbnRzIjsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGZvbnQtc2l6ZTogMS4yZW07DQogIGRpc3BsYXk6IGJsb2NrOw0KICBjb2xvcjogbmF2eTsNCiAgbWFyZ2luLWJvdHRvbTogMTBweDsNCn0NCg0KDQpkaXYjVE9DIGxpIHsgICAgIC8qIHRhYmxlIG9mIGNvbnRlbnQgICovDQogICAgbGlzdC1zdHlsZTp1cHBlci1yb21hbjsNCiAgICBiYWNrZ3JvdW5kLWltYWdlOm5vbmU7DQogICAgYmFja2dyb3VuZC1yZXBlYXQ6bm9uZTsNCiAgICBiYWNrZ3JvdW5kLXBvc2l0aW9uOjA7DQp9DQoNCmgxLnRpdGxlIHsgICAgLyogbGV2ZWwgMSBoZWFkZXIgb2YgdGl0bGUgICovDQogIGZvbnQtc2l6ZTogMjJweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGNvbG9yOiBEYXJrUmVkOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtZmFtaWx5OiAiR2lsbCBTYW5zIiwgc2Fucy1zZXJpZjsNCn0NCg0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgZm9udC1zaXplOiAxNXB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IG5hdnk7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCg0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGZvbnQtZmFtaWx5OiAiR2lsbCBTYW5zIiwgc2Fucy1zZXJpZjsNCiAgY29sb3I6IERhcmtCbHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQoNCmgxIHsgLyogSGVhZGVyIDEgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDIwcHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KDQpoMiB7IC8qIEhlYWRlciAyIC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmgzIHsgLyogSGVhZGVyIDMgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDE2cHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDQgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMTRweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KLyogQWRkIGRvdHMgYWZ0ZXIgbnVtYmVyZWQgaGVhZGVycyAqLw0KLmhlYWRlci1zZWN0aW9uLW51bWJlcjo6YWZ0ZXIgew0KICBjb250ZW50OiAiLiI7DQoNCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCi5oaWdobGlnaHRtZSB7IGJhY2tncm91bmQtY29sb3I6eWVsbG93OyB9DQoNCnAgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCn0NCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCANCiMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLg0KaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpDQogICBsaWJyYXJ5KGtuaXRyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJwYW5kZXIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicGFuZGVyIikNCiAgIGxpYnJhcnkocGFuZGVyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnZ3Bsb3QyIikpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpDQogIGxpYnJhcnkoZ2dwbG90MikNCn0NCmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikNCiAgbGlicmFyeSh0aWR5dmVyc2UpDQp9DQoNCmlmICghcmVxdWlyZSgicGxvdGx5IikpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikNCiAgbGlicmFyeShwbG90bHkpDQp9DQojIyMjDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgICAgICMgaW5jbHVkZSBjb2RlIGNodW5rIGluIHRoZSBvdXRwdXQgZmlsZQ0KICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwgICAjIHNvbWV0aW1lcywgeW91IGNvZGUgbWF5IHByb2R1Y2Ugd2FybmluZyBtZXNzYWdlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhlIG91dHB1dCBmaWxlLiANCiAgICAgICAgICAgICAgICAgICAgICByZXN1bHRzID0gVFJVRSwgICAgIyB5b3UgY2FuIGFsc28gZGVjaWRlIHdoZXRoZXIgdG8gaW5jbHVkZSB0aGUgb3V0cHV0DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW4gdGhlIG91dHB1dCBmaWxlLg0KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gTkENCiAgICAgICAgICAgICAgICAgICAgICApICANCmBgYA0KIA0KIFwNCiANCiMjICoqQXNzaWdubWVudCBPYmplY3RpdmVzKiogDQoNCiogRGV2ZWxvcCBhIGNsZWFyIHRlY2huaWNhbCB1bmRlcnN0YW5kaW5nIG9mIG5vbnBhcmFtZXRyaWMgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gZnVuY3Rpb24gKENERikgZXN0aW1hdGlvbiBhbmQgdmFyaW91cyBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0b3JzLg0KDQoqIFRyYW5zbGF0ZSBtYXRoZW1hdGljYWwgZm9ybXVsYXMgaW50byBSIGZ1bmN0aW9ucyBhbmQgYXBwbHkgdGhlbSB0byBzb2x2ZSByZWxhdGVkIHByb2JsZW1zLg0KDQoqIENyZWF0ZSBlZmZlY3RpdmUgdmlzdWFsaXphdGlvbnMgdG8gZGVtb25zdHJhdGUgeW91ciB1bmRlcnN0YW5kaW5nIG9mIGtleSBjb25jZXB0cyBpbiB0aGUgZm9sbG93aW5nIHF1ZXN0aW9ucy4NCg0KDQoNClwNCg0KIyMgKipRdWVzdGlvbiAxOiBDdW11bGF0aXZlIERpc3RyaWJ1dGlvbiBGdW5jdGlvbiAoQ0RGKSBFc3RpbWF0aW9uKioNCg0KVGhlIGZvbGxvd2luZyBmYWlsdXJlIHRpbWVzIChpbiBob3Vycykgd2VyZSBvYnNlcnZlZCBmb3IgOCBlbGVjdHJvbmljIGNvbXBvbmVudHM6DQoNCjxjZW50ZXI+IDIzLCA0NSwgNjcsIDg5LCAxMTIsIDE1NiwgMTg5LCAyNDUgIDwvY2VudGVyPg0KDQphKSBXcml0ZSBhbiBSIGZ1bmN0aW9uIGltcGxlbWVudGluZyB0aGUgRUNERiAkXGhhdHtGfV9uKHQpJCBhY2NvcmRpbmcgdG8gaXRzIG1hdGhlbWF0aWNhbCBkZWZpbml0aW9uLiBWYWxpZGF0ZSB5b3VyIGltcGxlbWVudGF0aW9uIHVzaW5nIFIncyBlY2RmKCkgZnVuY3Rpb24gb24gdGhlIGdpdmVuIGRhdGEsIHdpdGggY29tcGFyaXNvbiBiYXNlZCBvbiB0aGVpciBzdGVwIGZ1bmN0aW9ucy4NCg0KYGBge3J9IA0KIyBDcmVhdGUgYSB2ZWN0b3IgdG8gc3RvcmUgdGhlIDggZmFpbHVyZSB0aW1lcy9vYnNlcnZhdGlvbnMNCmZhaWx1cmVfdGltZXMgPC0gYygyMywgNDUsIDY3LCA4OSwgMTEyLCAxNTYsIDE4OSwgMjQ1KQ0KDQojIFNpbXBsaWZpZWQgUiBmdW5jdGlvbg0KbXlfZWNkZiA8LSBmdW5jdGlvbihkYXRhLCB0KSB7DQogIG4gPC0gbGVuZ3RoKGRhdGEpDQogIHJlc3VsdHMgPC0gbnVtZXJpYyhsZW5ndGgodCkpICMgQ3JlYXRlIGFuIGVtcHR5IHZlY3RvciB0byBzdG9yZSBhbnN3ZXJzDQogIA0KICBmb3IgKGkgaW4gMTpsZW5ndGgodCkpIHsNCiAgICAjIENvdW50IGhvdyBtYW55IGRhdGEgcG9pbnRzIGFyZSBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gY3VycmVudCB0W2ldDQogICAgY291bnQgPC0gc3VtKGRhdGEgPD0gdFtpXSkNCiAgICANCiAgICAjIERpdmlkZSBieSB0b3RhbCBudW1iZXIgb2YgcG9pbnRzIGFuZCBmaW5kIGN1bXVsYXRpdmUgcHJvYmFiaWxpdHkNCiAgICByZXN1bHRzW2ldIDwtIGNvdW50IC8gbg0KICB9DQogIA0KICByZXR1cm4ocmVzdWx0cykNCn0NCiMgQ3JlYXRlIGEgc2VxdWVuY2Ugb2YgdGltZSB2YWx1ZXMgdG8gdGVzdA0KdGVzdF92YWxzIDwtIHNlcSgwLCAyNTAsIGJ5ID0gMTApDQoNCiMgMS4gUnVuIGN1c3RvbSBmdW5jdGlvbg0KbXlfcmVzdWx0cyA8LSBteV9lY2RmKGZhaWx1cmVfdGltZXMsIHRlc3RfdmFscykNCg0KIyAyLiBSdW4gUidzIGJ1aWx0LWluIGVjZGYgZnVuY3Rpb24gYW5kIHVzZSBpdCBkaXJlY3RseSBvbiB0aGUgb2JzZXJ2YXRpb24gZGF0YSBzZXQgKDggZmFpbHVyZSB0aW1lcykNCnJfYnVpbHRpbl9mdW5jIDwtIGVjZGYoZmFpbHVyZV90aW1lcykNCnJfcmVzdWx0cyA8LSByX2J1aWx0aW5fZnVuYyh0ZXN0X3ZhbHMpICN1c2UgdGhlIHNhbWUgMjYgdGVzdCB2YWx1ZXMgdG8gZmluZCBoZWlnaHRzIG9uIGVjZGYgdGhyb3VnaCBidWlsdCBpbiBSIGZ1bmN0aW9uDQoNCiMgMy4gQ29uZHVjdCBhIGNvbXBhcmlzb24gb2YgdGhlIHJlc3VsdHMgZnJvbSBib3RoIG1ldGhvZHMuIA0KY29tcGFyaXNvbiA8LSBkYXRhLmZyYW1lKA0KICBUaW1lID0gdGVzdF92YWxzLA0KICBDdXN0b20gPSBteV9yZXN1bHRzLA0KICBCdWlsdEluID0gcl9yZXN1bHRzLA0KICBFcXVhbCA9IChteV9yZXN1bHRzID09IHJfcmVzdWx0cykNCikNCg0KcHJpbnQoY29tcGFyaXNvbikNCiMgUGxvdCAgcmVzdWx0cyBhcyBhIHN0ZXAgbGluZQ0KcGxvdCh0ZXN0X3ZhbHMsIG15X3Jlc3VsdHMsIHR5cGUgPSAicyIsIGNvbCA9ICJkYXJrZ3JlZW4iLCBsd2QgPSA0LA0KICAgICBtYWluID0gIlZhbGlkYXRpb246IE15IEVDREYgdnMgUidzIEVDREYiLCB4bGFiID0gIkhvdXJzIiwgeWxhYiA9ICJGbih0KSIpDQoNCiMgT3ZlcmxheSBSIHJlc3VsdHMgYXMgZGFzaGVkIGxpbmUgZm9yIG92ZXJsYXAgYW5hbHlzaXMNCmxpbmVzKHRlc3RfdmFscywgcl9yZXN1bHRzLCB0eXBlID0gInMiLCBjb2wgPSAid2hpdGUiLCBsdHkgPSAyLCBsd2QgPSAyKQ0KYGBgDQoNCmBgYHtyfSANCg0KYGBgDQpiKSBBIGNvbGxlYWd1ZSBjbGFpbXMgdGhhdCB0aGUgcHJvYmFiaWxpdHkgb2YgZmFpbHVyZSBiZWZvcmUgMTAwIGhvdXJzIGlzIDAuNSBiYXNlZCBvbiB0aGVzZSBkYXRhLiBEbyB5b3UgYWdyZWU/IEV4cGxhaW4geW91ciByZWFzb25pbmcgdXNpbmcgdGhlIGVtcGlyaWNhbCBjdW11bGF0aXZlIGRpc3RyaWJ1dGlvbiBmdW5jdGlvbiAoRUNERikuDQpgYGB7cn0NCiNDYW4gbm93IHVzZSB0aGUgZWNkZiBmdW5jdGlvbiBjcmVhdGVkIGFib3ZlIHRvIHZhbGlkYXRlIHRoaXMgY2xhaW0NCnByb2JfMTAwIDwtIG15X2VjZGYoZGF0YSA9IGZhaWx1cmVfdGltZXMsIHQgPSAxMDApDQpwcmludChwcm9iXzEwMCkNCg0KI0NhbiBhbHNvIHRlc3QgdGhpcyB3aXRoIHRoZSByIGJ1aWx0LWluIGVjZGYgDQpyXzEwMCA8LSByX2J1aWx0aW5fZnVuYygxMDApDQpwcmludChyXzEwMCkNCg0KI0kgYWdyZWUgdGhhdCB0aGUgcHJvYmFiaWxpdHkgb2YgZmFpbHVyZSBiZWZvcmUgMTAwIGhvdXJzIGlzIDAuNSAtIHRoZSBlY2RmIHJldHVybnMgdGhlIGN1bXVsYXRpdmUgaGVpZ2h0IG9mIHRoZSBmb3VydGggc3RlcCBvZiB0aGUgZnVuY3Rpb24sIGFzIHRoaXMgaXMgd2hlcmUgJzEwMCcgZmFsbHMuIFRoZSBmb3VydGggc3RlcCByZXByZXNlbnRzIDQvOCBvZiB0aGUgZGF0YSB3aGljaCBpcyAwLjUNCmBgYA0KDQpcDQoNCiMjICoqUXVlc3Rpb24gMjogRGVuc2l0eSBGdW5jdGlvbiBFc3RpbWF0aW9uKioNCg0KQ29uc2lkZXIgdGhlIGZvbGxvd2luZyBmYWlsdXJlIHRpbWVzIGZyb20gYSBtZWNoYW5pY2FsIHN5c3RlbToNCg0KPGNlbnRlcj4gMTIuMywgMTQuNywgMTUuMiwgMTYuOCwgMTguMSwgMTkuNCwgMjAuNiwgMjIuMywgMjMuOSwgMjUuNCA8L2NlbnRlcj4NCg0KYSkgQ3JlYXRlIGEgaGlzdG9ncmFtIG9mIHRoZSBkYXRhIHVzaW5nIDMgZXF1YWxseSBzcGFjZWQgYmlucy4gV2hhdCBpcyB0aGUgZXN0aW1hdGVkIGRlbnNpdHkgaW4gZWFjaCBiaW4/IERlc2NyaWJlIHRoZSBzaGFwZSBvZiB0aGUgaGlzdG9ncmFtJ3MgZGlzdHJpYnV0aW9uLg0KYGBge3J9DQp0aW1lcyA8LSBjKDEyLjMsIDE0LjcsIDE1LjIsIDE2LjgsIDE4LjEsIDE5LjQsIDIwLjYsIDIyLjMsIDIzLjksIDI1LjQpDQoNCiMgMS4gQ3JlYXRlIHRoZSBoaXN0b2dyYW0gYW5kIHN0b3JlIGl0DQojIGZyZXEgPSBGQUxTRSBpcyByZXF1aXJlZCB0byBnZXQgZGVuc2l0eSBpbnN0ZWFkIG9mIGNvdW50cw0KaGlzdF9vdXRwdXQgPC0gaGlzdCh0aW1lcywgYnJlYWtzID0gMywgZnJlcSA9IEZBTFNFLCBwbG90ID0gVFJVRSkNCg0KIyAyLiBQcmludCB0aGUgZGVuc2l0eSB2YWx1ZXMNCmNhdCgiVGhlIGVzdGltYXRlZCBkZW5zaXRpZXMgZm9yIHRoZSAzIGJpbnMgYXJlOlxuIikNCnByaW50KGhpc3Rfb3V0cHV0JGRlbnNpdHkpDQoNCiMgMy4gVG8gc2VlIHdoaWNoIGRlbnNpdHkgYmVsb25ncyB0byB3aGljaCByYW5nZToNCmNhdCgiXG5UaGUgYmluIGJyZWFrcyBhcmU6XG4iKQ0KcHJpbnQoaGlzdF9vdXRwdXQkYnJlYWtzKQ0KDQojVGhlIGhpc3RvZ3JhbSBpcyBhcHByb3hpbWF0ZWx5IHN5bW1ldHJpYy4gDQpgYGANCg0KYikgV3JpdGUgYW4gUiBmdW5jdGlvbiB0aGF0IGNvbXB1dGVzIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRlcyB1c2luZyBhIEdhdXNzaWFuIGtlcm5lbCB3aXRoICRoPTIkLiBWYWxpZGF0ZSB5b3VyIGltcGxlbWVudGF0aW9uIGFnYWluc3QgUidzIGJ1aWx0LWluIGRlbnNpdHkoKSBmdW5jdGlvbi4NCg0KJCQNClxoYXR7Zn1faCh0KSA9IFxmcmFjezF9e25ofVxzdW1fe2k9MX1ebiBLXGxlZnQoIFxmcmFje3QtdF9pfXtofVxyaWdodCksIFwgXCBcdGV4dHsgd2hlcmUgfSBcIFwgSyh1KSA9IFxmcmFjezF9e1xzcXJ0ezJccGl9fSBlXnstdV4yLzJ9Lg0KJCQNCmBgYHtyfQ0KIyBNeSAgR2F1c3NpYW4ga2RlDQojIHhfaW46ICByYXcgZmFpbHVyZSBkYXRhDQojIGg6IGJhbmR3aWR0aCANCiMgeF9vdXQ6IHBvaW50cyB3aGVyZSB3ZSB3YW50IHRvIGVzdGltYXRlIHRoZSBkZW5zaXR5DQpjYWxjX2tkZSA8LSBmdW5jdGlvbih4X2luLCBoLCB4X291dCkgew0KICBuIDwtIGxlbmd0aCh4X2luKQ0KICANCiAgIyBTZXQgdXAgYSB2ZWN0b3Igb2YgemVyb3MgDQogIGRlbnNpdGllcyA8LSBudW1lcmljKGxlbmd0aCh4X291dCkpDQogIA0KICBmb3IgKGogaW4gMTpsZW5ndGgoeF9vdXQpKSB7DQogICAgIyBDYWxjdWxhdGUgdGhlIGF2ZXJhZ2UgaGVpZ2h0IG9mICBHYXVzc2lhbiBrZXJuZWxzIGF0IHBvaW50IHhfb3V0W2pdDQogICAgIyBkbm9ybSAtIG1lYW4gaXMgdGhlIGRhdGEgcG9pbnQ7IHNkIGlzIGJhbmR3aWR0aCBoDQogICAga2VybmVsX2hlaWdodHMgPC0gZG5vcm0oeF9vdXRbal0sIG1lYW4gPSB4X2luLCBzZCA9IGgpDQogICAgZGVuc2l0aWVzW2pdIDwtIHN1bShrZXJuZWxfaGVpZ2h0cykgLyBuDQogIH0NCiAgDQogIHJldHVybihkZW5zaXRpZXMpDQp9DQojIDEuIFNldHVwIGRhdGEgdXNpbmcgb2JzZXJ2ZWQgZmFpbHVyZSB0aW1lcw0KdGltZXMgPC0gYygxMi4zLCAxNC43LCAxNS4yLCAxNi44LCAxOC4xLCAxOS40LCAyMC42LCAyMi4zLCAyMy45LCAyNS40KQ0KaF92YWwgPC0gMg0KdGVzdF9wb2ludHMgPC0gc2VxKDEwLCAzMCwgbGVuZ3RoLm91dCA9IDEwKQ0KDQojIDIuIFJ1biBteSBmdW5jdGlvbg0KbXlfa2RlX3Jlc3VsdHMgPC0gY2FsY19rZGUodGltZXMsIGhfdmFsLCB0ZXN0X3BvaW50cykNCg0KIyAzLiBSdW4gUidzIGJ1aWx0LWluIGRlbnNpdHkNCg0Kcl9rZGVfb2JqIDwtIGRlbnNpdHkoDQogIHRpbWVzLA0KICBidyA9IGhfdmFsLA0KICBrZXJuZWwgPSAiZ2F1c3NpYW4iLA0KICBmcm9tID0gbWluKHRlc3RfcG9pbnRzKSwNCiAgdG8gICA9IG1heCh0ZXN0X3BvaW50cyksDQogIG4gICAgPSBsZW5ndGgodGVzdF9wb2ludHMpLA0KICBjdXQgID0gMA0KKQ0Kcl9rZGVfcmVzdWx0cyA8LSBhcHByb3gocl9rZGVfb2JqJHgsIHJfa2RlX29iaiR5LCB4b3V0ID0gdGVzdF9wb2ludHMpJHkNCg0KIyA0LiBDb21wYXJlDQpjb21wYXJpc29uIDwtIGRhdGEuZnJhbWUoDQogIFRpbWUgPSB0ZXN0X3BvaW50cywNCiAgTXlfS0RFID0gbXlfa2RlX3Jlc3VsdHMsDQogIFJfS0RFID0gcl9rZGVfcmVzdWx0cywNCiAgRGlmZmVyZW5jZSA9IG15X2tkZV9yZXN1bHRzIC0gcl9rZGVfcmVzdWx0cw0KKQ0KDQpwcmludChjb21wYXJpc29uKQ0KYGBgDQpjKSBXcml0ZSBhIGN1c3RvbSBSIGZ1bmN0aW9uIHRoYXQgY29tcHV0ZXMga2VybmVsIGRlbnNpdHkgZXN0aW1hdGVzIHVzaW5nIHRoZSBFcGFuZWNobmlrb3Yga2VybmVsIHdpdGggJGg9MiQuIFZhbGlkYXRlIHlvdXIgaW1wbGVtZW50YXRpb24gYnkgY29tcGFyaW5nIHJlc3VsdHMgd2l0aCBSJ3MgYnVpbHQtaW4gZGVuc2l0eSgpIGZ1bmN0aW9uIGZvciBHYXVzc2lhbiBrZXJuZWwgZXN0aW1hdGlvbi4NCg0KJCQNClxoYXR7Zn1faCh0KSA9IFxmcmFjezF9e25ofVxzdW1fe2k9MX1ebiBLXGxlZnQoIFxmcmFje3QtdF9pfXtofVxyaWdodCksIFwgXCBcdGV4dHsgd2hlcmUgfSBcIFwgSyh1KSA9IFxmcmFjezN9ezR9KDEgLSB1XjIpIFwgXCBcdGV4dHsgZm9yIH0gXCBcIHx1fCBcbGUgMS4NCiQkDQpgYGB7cn0NCm15X2VwYW5fa2RlIDwtIGZ1bmN0aW9uKHhfaW4sIGgsIHhfb3V0KSB7DQogIG4gPC0gbGVuZ3RoKHhfaW4pDQogIGRlbnNpdGllcyA8LSBudW1lcmljKGxlbmd0aCh4X291dCkpDQogIA0KICBmb3IgKGkgaW4gMTpsZW5ndGgoeF9vdXQpKSB7DQogICAgIyAxLiBDYWxjdWxhdGUgZGlzdGFuY2UgKHUpDQogICAgdSA8LSAoeF9vdXRbaV0gLSB4X2luKSAvIGgNCiAgICANCiAgICAjIDIuIEFwcGx5IHBhcmFib2xhIGZvcm11bGE6IDMvNCAqICgxIC0gdV4yKQ0KICAgIGtfdmFsdWVzIDwtICgzLzQpICogKDEgLSB1XjIpDQogICAgDQogICAgIyAzLiBpZiBkaXN0YW5jZSB8dXwgaXMgPiAxLCB0aGUgcmVzdWx0IG11c3QgYmUgMA0KICAgICMgVGhpcyByZXBsYWNlcyBhbnkgbmVnYXRpdmUgb3IgIm91dCBvZiBib3VuZHMiIHZhbHVlcyB3aXRoIDANCiAgICBrX3ZhbHVlc1thYnModSkgPiAxXSA8LSAwDQogICAgDQogICAgIyA0LiBGaW5hbCBTdW0NCiAgICBkZW5zaXRpZXNbaV0gPC0gc3VtKGtfdmFsdWVzKSAvIChuICogaCkNCiAgfQ0KICByZXR1cm4oZGVuc2l0aWVzKQ0KfQ0KDQpoX3ZhbCA8LSAyDQp0ZXN0X3B0cyA8LSBzZXEoMTAsIDMwLCBsZW5ndGgub3V0ID0gMTApDQoNCiMgbXkgZXBhbmVjaG5pa292DQpteV9lcGFuX3Jlc3VsdHMgPC0gbXlfZXBhbl9rZGUodGltZXMsIGhfdmFsLCB0ZXN0X3B0cykNCg0KIyBSJ3MgYnVpbHQtaW4gRXBhbmVjaG5pa292DQpyX2VwYW5fb2JqIDwtIGRlbnNpdHkodGltZXMsIGJ3ID0gaF92YWwsIGtlcm5lbCA9ICJlcGFuZWNobmlrb3YiKQ0Kcl9lcGFuX3Jlc3VsdHMgPC0gYXBwcm94KHJfZXBhbl9vYmokeCwgcl9lcGFuX29iaiR5LCB4b3V0ID0gdGVzdF9wdHMpJHkNCg0KIyBDb21wYXJlDQpkYXRhLmZyYW1lKFRpbWUgPSB0ZXN0X3B0cywgQ3VzdG9tID0gbXlfZXBhbl9yZXN1bHRzLCBCdWlsdEluID0gcl9lcGFuX3Jlc3VsdHMpDQoNCiNUbyB2YWxpZGF0ZSB0aGUgaW1wbGVtZW50YXRpb24sIHRoZSBjdXN0b20gRXBhbmVjaG5pa292IEtERSB3YXMgY29tcGFyZWQgdG8gUuKAmXMgZGVuc2l0eSgpIHVzaW5nIHRoZSBFcGFuZWNobmlrb3Yga2VybmVsLCBzaG93aW5nIGNsb3NlIGFncmVlbWVudC4gRm9yIGNvbXBsZXRlbmVzcywgYSBHYXVzc2lhbiBrZXJuZWwgZXN0aW1hdGUgd2FzIGFsc28gZXhhbWluZWQsIHdoaWNoIGV4aGliaXRzIHNtb290aGVyIHRhaWxzIGFzIGV4cGVjdGVkDQoNCiNJbiB0aGUgY3VzdG9tIG15X2VwYW5fa2RlKCkgZnVuY3Rpb24sIG9ic2VydmF0aW9ucyB3aXRoIOKIo3gw4oiSeGniiKM+IGhhcmUgYXNzaWduZWQgemVybyB3ZWlnaHQgdmlhIGtfdmFsdWVzW2Ficyh1KSA+IDFdIDwtIDAsIHJlZmxlY3RpbmcgdGhlIGNvbXBhY3Qgc3VwcG9ydCBvZiB0aGUgRXBhbmVjaG5pa292IGtlcm5lbC4gQXMgYSByZXN1bHQsIHRoZSBkZW5zaXR5IGVzdGltYXRlIGlzIGV4YWN0bHkgemVybyBhdCBldmFsdWF0aW9uIHBvaW50cyB3aGVyZSBubyBvYnNlcnZhdGlvbnMgbGllIHdpdGhpbiBoID0gMi4NCmBgYA0KZCkgSG93IGRvZXMgdGhlIGNob2ljZSBvZiBrZXJuZWwgKEdhdXNzaWFuIHZzLiBFcGFuZWNobmlrb3YpIGFmZmVjdCB0aGUgZGVuc2l0eSBlc3RpbWF0ZT8gRm9yIGJvdGgga2VybmVsIGVzdGltYXRvcnMgYXBwbGllZCB0byB0aGlzIGRhdGFzZXQsIHdoYXQgaGFwcGVucyB3aGVuIHdlIHNlbGVjdCAkaD0xLjUkIHZlcnN1cyAkaD0yLjUkPw0KDQojDQpUaGUga2VybmVsIGNob2ljZSBhZmZlY3RzIHRoZSBzbW9vdGhuZXNzIGFuZCB0YWlsIGJlaGF2aW9yIG9mIHRoZSBlc3RpbWF0ZS4gVGhlIEdhdXNzaWFuIGtlcm5lbCBwcm9kdWNlcyBzbW9vdGhlciBjdXJ2ZXMvbm9uLXplcm8gdGFpbHMgYW5kIHRoZSBFcGFuZWNobmlrb3Yga2VyZW5lbCB5aWVsZHMgbG9jYWxpemVkIGVzdGltYXRlcy4gU2VsZWN0aW5nIGggPSAxLjUgZm9yIHRoZSBiYW5kd2l0aCByZXN1bHRzIGluIGFuIGVzdGltYWJsZSB0aGF0IGlzIG1vcmUgdmFyaWFibGU7IGggPSAyLjUgcmVkdWNlcyB2YXJpYW5jZWFuZCBwcm9kdWNlcyBhIHNtb290aGVyIGN1cnZlIHdpdGggbW9yZSBiaWFzLg0KDQoNCg0KDQo=