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

- 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
- 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.
- 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
- 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.
- 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=