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.
x <- c(23, 45, 67, 89, 112, 156, 189, 245)

my_ecdf_f <- function(x){
  n <- length(x)
  function(t){
    vapply(t, function(tt) sum(x <= tt) / n, numeric(1))
  }
}

F_r <- ecdf(x)
F_mine <- my_ecdf_f(x)

t_grid <- seq(min(x) - 10, max(x) + 10, length.out = 1000)

plot_df <- data.frame(
  t = t_grid,
  R_ecdf = F_r(t_grid),
  My_ecdf = F_mine(t_grid)
)

plot_df_long <- plot_df %>%
  pivot_longer(
    cols = c(R_ecdf, My_ecdf),
    names_to = "Type",
    values_to = "CDF"
  )

Fn.plt <- ggplot(plot_df_long, aes(x = t, y = CDF, color = Type)) +
  geom_step(linewidth = 1) +
  scale_color_manual(values = c("R_ecdf" = "blue", "My_ecdf" = "red")) +
  labs(
    title = "Empirical CDF: My Implementation vs R ecdf()",
    subtitle = paste("Sample size n =", length(x)),
    x = "t",
    y = "F̂(t)"
  ) +
  theme(
    plot.title = element_text(hjust = 0.5),
    plot.margin = margin(t = 35, r = 20, b = 30, l = 30, unit = "pt")
  )

ggplotly(Fn.plt)

The ECDF computed using my custom ECDF implementation closely matches the estimate produced by R’s ecdf() function, indicating that the implementation is correct.

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

I agree with the claim because the ECDF evaluated at 100 hours is 0.5, meaning that about 50% of the observed failures occurred before 100 hours. Therefore, the ECDF supports this claim.

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.
x <- c(12.3, 14.7, 15.2, 16.8, 18.1, 19.4, 20.6, 22.3, 23.9, 25.4)
h <- hist(x, breaks = seq(min(x), max(x), length.out = 4), freq = FALSE)

The estimated density of each bin in order is 0.0687023, 0.0916031, 0.0687023. The distribution is roughly symmetrical and unimodal.

  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_kde <- function(x,h){
  n <- length(x)
  function(t){
    vapply(t, function(xi) 1/(n*h) * sum(dnorm((xi - x)/h)), numeric(1))
  }
}

F_mine <- my_kde(x, h = 2)
F_r <- density(x, bw = 2)

t_grid <- F_r$x

plot_df <- data.frame(
  t = t_grid,
  R_density = F_r$y,
  My_density = F_mine(t_grid)
)

plot_df_long <- plot_df %>%
  pivot_longer(
    cols = c(R_density, My_density),
    names_to = "Type",
    values_to = "Density"
  )

kde_plt <- ggplot(plot_df_long, aes(x = t, y = Density, color = Type)) +
  geom_line(linewidth = 1) +
  labs(
    title = "KDE: My Implementation vs R density()",
    subtitle = paste("Gaussian kernel, h =", 2, ", n =", length(x)),
    x = "t",
    y = "Density"
  ) +
  theme(plot.title = element_text(hjust = 0.5))

ggplotly(kde_plt)

The KDE computed using my custom Gaussian kernel implementation closely matches the estimate produced by R’s density() function, indicating that the implementation is correct.

  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_kde <- function(x,h){
  n <- length(x)
  function(t){
    vapply(t, function(tt) {
      u <- (tt - x)/h
      K <- ifelse(abs(u) <= 1, (3/4)*(1 - u^2), 0)
      1/(n*h) * sum(K)}, numeric(1))
  }
}

F_mine <- my_kde(x, h = 2)
F_r <- density(x, bw = 2, kernel = "epanechnikov")

t_grid <- F_r$x

plot_df <- data.frame(
  t = t_grid,
  R_density = F_r$y,
  My_density = F_mine(t_grid)
)

plot_df_long <- plot_df %>%
  pivot_longer(
    cols = c(R_density, My_density),
    names_to = "Type",
    values_to = "Density"
  )

kde_plt <- ggplot(plot_df_long, aes(x = t, y = Density, color = Type)) +
  geom_line(linewidth = 1) +
  labs(
    title = "KDE: My Implementation vs R density()",
    subtitle = paste("Gaussian kernel, h =", 2, ", n =", length(x)),
    x = "t",
    y = "Density"
  ) +
  theme(plot.title = element_text(hjust = 0.5))

ggplotly(kde_plt)

The KDE computed using my custom Epanechnikov kernel implementation closely matches the estimate produced by R’s density() function, indicating that the implementation is correct. However, the two curves don’t completely overlap this time, I’m assuming the density() function must go above and beyond just the mathematical equation.

  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 Guassian curve is much more smooth than the Epanechnikov curve. Epanechnikov densities are allowed to be zero at the tails, this doesn’t seem to be true for Gaussian densities. For both kernels, the density curves become more rigid when h is decreased to 1.5 and more smooth when its increased to 2.5.

LS0tCnRpdGxlOiAiQXNzaWdubWVudCAxOiBFc3RpbWF0aW5nIENERiBhbmQgUERGIgphdXRob3I6ICJDaGFybGllIE1vcmdhbiIKZGF0ZTogIiBEdWU6IDAyLzAyLzI2IgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDogCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA0CiAgICB0b2NfZmxvYXQ6IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiBubwogICAgdG9jX2NvbGxhcHNlZDogeWVzCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGNvZGVfZG93bmxvYWQ6IHllcwogICAgc21vb3RoX3Njcm9sbDogeWVzCiAgICB0aGVtZTogbHVtZW4KICBwZGZfZG9jdW1lbnQ6IAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgZmlnX2NhcHRpb246IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIGZpZ193aWR0aDogMwogICAgZmlnX2hlaWdodDogMwogIHdvcmRfZG9jdW1lbnQ6IAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgZmlnX2NhcHRpb246IHllcwogICAga2VlcF9tZDogeWVzCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKYGBge2NzcywgZWNobyA9IEZBTFNFfQojVE9DOjpiZWZvcmUgewogIGNvbnRlbnQ6ICJUYWJsZSBvZiBDb250ZW50cyI7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgZm9udC1zaXplOiAxLjJlbTsKICBkaXNwbGF5OiBibG9jazsKICBjb2xvcjogbmF2eTsKICBtYXJnaW4tYm90dG9tOiAxMHB4Owp9CgoKZGl2I1RPQyBsaSB7ICAgICAvKiB0YWJsZSBvZiBjb250ZW50ICAqLwogICAgbGlzdC1zdHlsZTp1cHBlci1yb21hbjsKICAgIGJhY2tncm91bmQtaW1hZ2U6bm9uZTsKICAgIGJhY2tncm91bmQtcmVwZWF0Om5vbmU7CiAgICBiYWNrZ3JvdW5kLXBvc2l0aW9uOjA7Cn0KCmgxLnRpdGxlIHsgICAgLyogbGV2ZWwgMSBoZWFkZXIgb2YgdGl0bGUgICovCiAgZm9udC1zaXplOiAyMnB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGNvbG9yOiBEYXJrUmVkOwogIHRleHQtYWxpZ246IGNlbnRlcjsKICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7Cn0KCmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovCiAgZm9udC1zaXplOiAxNXB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7CiAgY29sb3I6IG5hdnk7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9CgpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8KICBmb250LXNpemU6IDE4cHg7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOwogIGNvbG9yOiBEYXJrQmx1ZTsKICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KCmgxIHsgLyogSGVhZGVyIDEgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8KICAgIGZvbnQtc2l6ZTogMjBweDsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogZGFya3JlZDsKICAgIHRleHQtYWxpZ246IGNlbnRlcjsKfQoKaDIgeyAvKiBIZWFkZXIgMiAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLwogICAgZm9udC1zaXplOiAxOHB4OwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogbGVmdDsKfQoKaDMgeyAvKiBIZWFkZXIgMyAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLwogICAgZm9udC1zaXplOiAxNnB4OwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogbGVmdDsKfQoKaDQgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLwogICAgZm9udC1zaXplOiAxNHB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogZGFya3JlZDsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCi8qIEFkZCBkb3RzIGFmdGVyIG51bWJlcmVkIGhlYWRlcnMgKi8KLmhlYWRlci1zZWN0aW9uLW51bWJlcjo6YWZ0ZXIgewogIGNvbnRlbnQ6ICIuIjsKCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9CgouaGlnaGxpZ2h0bWUgeyBiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgfQoKcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0KCn0KYGBgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IAojIHdpbGwgYmUgaW5jbHVkZWQgaW4gdGhlIG91dHB1dCBmaWxlcy4KaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikKICAgbGlicmFyeShrbml0cikKfQppZiAoIXJlcXVpcmUoInBhbmRlciIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInBhbmRlciIpCiAgIGxpYnJhcnkocGFuZGVyKQp9CmlmICghcmVxdWlyZSgiZ2dwbG90MiIpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpCiAgbGlicmFyeShnZ3Bsb3QyKQp9CmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQogIGxpYnJhcnkodGlkeXZlcnNlKQp9CgppZiAoIXJlcXVpcmUoInBsb3RseSIpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikKICBsaWJyYXJ5KHBsb3RseSkKfQojIyMjCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsICAgIyBzb21ldGltZXMsIHlvdSBjb2RlIG1heSBwcm9kdWNlIHdhcm5pbmcgbWVzc2FnZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb3V0cHV0IGZpbGUuIAogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IFRSVUUsICAgICMgeW91IGNhbiBhbHNvIGRlY2lkZSB3aGV0aGVyIHRvIGluY2x1ZGUgdGhlIG91dHB1dAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW4gdGhlIG91dHB1dCBmaWxlLgogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BCiAgICAgICAgICAgICAgICAgICAgICApICAKYGBgCiAKIFwKIAojIyAqKkFzc2lnbm1lbnQgT2JqZWN0aXZlcyoqIAoKKiBEZXZlbG9wIGEgY2xlYXIgdGVjaG5pY2FsIHVuZGVyc3RhbmRpbmcgb2Ygbm9ucGFyYW1ldHJpYyBjdW11bGF0aXZlIGRpc3RyaWJ1dGlvbiBmdW5jdGlvbiAoQ0RGKSBlc3RpbWF0aW9uIGFuZCB2YXJpb3VzIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRvcnMuCgoqIFRyYW5zbGF0ZSBtYXRoZW1hdGljYWwgZm9ybXVsYXMgaW50byBSIGZ1bmN0aW9ucyBhbmQgYXBwbHkgdGhlbSB0byBzb2x2ZSByZWxhdGVkIHByb2JsZW1zLgoKKiBDcmVhdGUgZWZmZWN0aXZlIHZpc3VhbGl6YXRpb25zIHRvIGRlbW9uc3RyYXRlIHlvdXIgdW5kZXJzdGFuZGluZyBvZiBrZXkgY29uY2VwdHMgaW4gdGhlIGZvbGxvd2luZyBxdWVzdGlvbnMuCgoKClwKCiMjICoqUXVlc3Rpb24gMTogQ3VtdWxhdGl2ZSBEaXN0cmlidXRpb24gRnVuY3Rpb24gKENERikgRXN0aW1hdGlvbioqCgpUaGUgZm9sbG93aW5nIGZhaWx1cmUgdGltZXMgKGluIGhvdXJzKSB3ZXJlIG9ic2VydmVkIGZvciA4IGVsZWN0cm9uaWMgY29tcG9uZW50czoKCjxjZW50ZXI+IDIzLCA0NSwgNjcsIDg5LCAxMTIsIDE1NiwgMTg5LCAyNDUgIDwvY2VudGVyPgoKYSkgV3JpdGUgYW4gUiBmdW5jdGlvbiBpbXBsZW1lbnRpbmcgdGhlIEVDREYgJFxoYXR7Rn1fbih0KSQgYWNjb3JkaW5nIHRvIGl0cyBtYXRoZW1hdGljYWwgZGVmaW5pdGlvbi4gVmFsaWRhdGUgeW91ciBpbXBsZW1lbnRhdGlvbiB1c2luZyBSJ3MgZWNkZigpIGZ1bmN0aW9uIG9uIHRoZSBnaXZlbiBkYXRhLCB3aXRoIGNvbXBhcmlzb24gYmFzZWQgb24gdGhlaXIgc3RlcCBmdW5jdGlvbnMuCgpgYGB7cn0KeCA8LSBjKDIzLCA0NSwgNjcsIDg5LCAxMTIsIDE1NiwgMTg5LCAyNDUpCgpteV9lY2RmX2YgPC0gZnVuY3Rpb24oeCl7CiAgbiA8LSBsZW5ndGgoeCkKICBmdW5jdGlvbih0KXsKICAgIHZhcHBseSh0LCBmdW5jdGlvbih0dCkgc3VtKHggPD0gdHQpIC8gbiwgbnVtZXJpYygxKSkKICB9Cn0KCkZfciA8LSBlY2RmKHgpCkZfbWluZSA8LSBteV9lY2RmX2YoeCkKCnRfZ3JpZCA8LSBzZXEobWluKHgpIC0gMTAsIG1heCh4KSArIDEwLCBsZW5ndGgub3V0ID0gMTAwMCkKCnBsb3RfZGYgPC0gZGF0YS5mcmFtZSgKICB0ID0gdF9ncmlkLAogIFJfZWNkZiA9IEZfcih0X2dyaWQpLAogIE15X2VjZGYgPSBGX21pbmUodF9ncmlkKQopCgpwbG90X2RmX2xvbmcgPC0gcGxvdF9kZiAlPiUKICBwaXZvdF9sb25nZXIoCiAgICBjb2xzID0gYyhSX2VjZGYsIE15X2VjZGYpLAogICAgbmFtZXNfdG8gPSAiVHlwZSIsCiAgICB2YWx1ZXNfdG8gPSAiQ0RGIgogICkKCkZuLnBsdCA8LSBnZ3Bsb3QocGxvdF9kZl9sb25nLCBhZXMoeCA9IHQsIHkgPSBDREYsIGNvbG9yID0gVHlwZSkpICsKICBnZW9tX3N0ZXAobGluZXdpZHRoID0gMSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJSX2VjZGYiID0gImJsdWUiLCAiTXlfZWNkZiIgPSAicmVkIikpICsKICBsYWJzKAogICAgdGl0bGUgPSAiRW1waXJpY2FsIENERjogTXkgSW1wbGVtZW50YXRpb24gdnMgUiBlY2RmKCkiLAogICAgc3VidGl0bGUgPSBwYXN0ZSgiU2FtcGxlIHNpemUgbiA9IiwgbGVuZ3RoKHgpKSwKICAgIHggPSAidCIsCiAgICB5ID0gIkbMgih0KSIKICApICsKICB0aGVtZSgKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4odCA9IDM1LCByID0gMjAsIGIgPSAzMCwgbCA9IDMwLCB1bml0ID0gInB0IikKICApCgpnZ3Bsb3RseShGbi5wbHQpCmBgYAoKVGhlIEVDREYgY29tcHV0ZWQgdXNpbmcgbXkgY3VzdG9tIEVDREYgaW1wbGVtZW50YXRpb24gY2xvc2VseSBtYXRjaGVzIHRoZSBlc3RpbWF0ZSBwcm9kdWNlZCBieSBS4oCZcyBlY2RmKCkgZnVuY3Rpb24sIGluZGljYXRpbmcgdGhhdCB0aGUgaW1wbGVtZW50YXRpb24gaXMgY29ycmVjdC4KCmIpIEEgY29sbGVhZ3VlIGNsYWltcyB0aGF0IHRoZSBwcm9iYWJpbGl0eSBvZiBmYWlsdXJlIGJlZm9yZSAxMDAgaG91cnMgaXMgMC41IGJhc2VkIG9uIHRoZXNlIGRhdGEuIERvIHlvdSBhZ3JlZT8gRXhwbGFpbiB5b3VyIHJlYXNvbmluZyB1c2luZyB0aGUgZW1waXJpY2FsIGN1bXVsYXRpdmUgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uIChFQ0RGKS4KYGBge3IsIGluY2x1ZGU9RkFMU0V9CkZfcigxMDApCmBgYApJIGFncmVlIHdpdGggdGhlIGNsYWltIGJlY2F1c2UgdGhlIEVDREYgZXZhbHVhdGVkIGF0IDEwMCBob3VycyBpcyBgciBGX3IoMTAwKWAsIG1lYW5pbmcgdGhhdCBhYm91dCBgciBGX3IoMTAwKSoxMDBgJSBvZiB0aGUgb2JzZXJ2ZWQgZmFpbHVyZXMgb2NjdXJyZWQgYmVmb3JlIDEwMCBob3Vycy4gVGhlcmVmb3JlLCB0aGUgRUNERiBzdXBwb3J0cyB0aGlzIGNsYWltLgpcCgojIyAqKlF1ZXN0aW9uIDI6IERlbnNpdHkgRnVuY3Rpb24gRXN0aW1hdGlvbioqCgpDb25zaWRlciB0aGUgZm9sbG93aW5nIGZhaWx1cmUgdGltZXMgZnJvbSBhIG1lY2hhbmljYWwgc3lzdGVtOgoKPGNlbnRlcj4gMTIuMywgMTQuNywgMTUuMiwgMTYuOCwgMTguMSwgMTkuNCwgMjAuNiwgMjIuMywgMjMuOSwgMjUuNCA8L2NlbnRlcj4KCmEpIENyZWF0ZSBhIGhpc3RvZ3JhbSBvZiB0aGUgZGF0YSB1c2luZyAzIGVxdWFsbHkgc3BhY2VkIGJpbnMuIFdoYXQgaXMgdGhlIGVzdGltYXRlZCBkZW5zaXR5IGluIGVhY2ggYmluPyBEZXNjcmliZSB0aGUgc2hhcGUgb2YgdGhlIGhpc3RvZ3JhbSdzIGRpc3RyaWJ1dGlvbi4KYGBge3J9CnggPC0gYygxMi4zLCAxNC43LCAxNS4yLCAxNi44LCAxOC4xLCAxOS40LCAyMC42LCAyMi4zLCAyMy45LCAyNS40KQpoIDwtIGhpc3QoeCwgYnJlYWtzID0gc2VxKG1pbih4KSwgbWF4KHgpLCBsZW5ndGgub3V0ID0gNCksIGZyZXEgPSBGQUxTRSkKYGBgCgpUaGUgZXN0aW1hdGVkIGRlbnNpdHkgb2YgZWFjaCBiaW4gaW4gb3JkZXIgaXMgYHIgaCRkZW5zaXR5YC4gVGhlIGRpc3RyaWJ1dGlvbiBpcyByb3VnaGx5IHN5bW1ldHJpY2FsIGFuZCB1bmltb2RhbC4KCmIpIFdyaXRlIGFuIFIgZnVuY3Rpb24gdGhhdCBjb21wdXRlcyBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZXMgdXNpbmcgYSBHYXVzc2lhbiBrZXJuZWwgd2l0aCAkaD0yJC4gVmFsaWRhdGUgeW91ciBpbXBsZW1lbnRhdGlvbiBhZ2FpbnN0IFIncyBidWlsdC1pbiBkZW5zaXR5KCkgZnVuY3Rpb24uCgokJApcaGF0e2Z9X2godCkgPSBcZnJhY3sxfXtuaH1cc3VtX3tpPTF9Xm4gS1xsZWZ0KCBcZnJhY3t0LXRfaX17aH1ccmlnaHQpLCBcIFwgXHRleHR7IHdoZXJlIH0gXCBcIEsodSkgPSBcZnJhY3sxfXtcc3FydHsyXHBpfX0gZV57LXVeMi8yfS4KJCQKYGBge3J9Cm15X2tkZSA8LSBmdW5jdGlvbih4LGgpewogIG4gPC0gbGVuZ3RoKHgpCiAgZnVuY3Rpb24odCl7CiAgICB2YXBwbHkodCwgZnVuY3Rpb24oeGkpIDEvKG4qaCkgKiBzdW0oZG5vcm0oKHhpIC0geCkvaCkpLCBudW1lcmljKDEpKQogIH0KfQoKRl9taW5lIDwtIG15X2tkZSh4LCBoID0gMikKRl9yIDwtIGRlbnNpdHkoeCwgYncgPSAyKQoKdF9ncmlkIDwtIEZfciR4CgpwbG90X2RmIDwtIGRhdGEuZnJhbWUoCiAgdCA9IHRfZ3JpZCwKICBSX2RlbnNpdHkgPSBGX3IkeSwKICBNeV9kZW5zaXR5ID0gRl9taW5lKHRfZ3JpZCkKKQoKcGxvdF9kZl9sb25nIDwtIHBsb3RfZGYgJT4lCiAgcGl2b3RfbG9uZ2VyKAogICAgY29scyA9IGMoUl9kZW5zaXR5LCBNeV9kZW5zaXR5KSwKICAgIG5hbWVzX3RvID0gIlR5cGUiLAogICAgdmFsdWVzX3RvID0gIkRlbnNpdHkiCiAgKQoKa2RlX3BsdCA8LSBnZ3Bsb3QocGxvdF9kZl9sb25nLCBhZXMoeCA9IHQsIHkgPSBEZW5zaXR5LCBjb2xvciA9IFR5cGUpKSArCiAgZ2VvbV9saW5lKGxpbmV3aWR0aCA9IDEpICsKICBsYWJzKAogICAgdGl0bGUgPSAiS0RFOiBNeSBJbXBsZW1lbnRhdGlvbiB2cyBSIGRlbnNpdHkoKSIsCiAgICBzdWJ0aXRsZSA9IHBhc3RlKCJHYXVzc2lhbiBrZXJuZWwsIGggPSIsIDIsICIsIG4gPSIsIGxlbmd0aCh4KSksCiAgICB4ID0gInQiLAogICAgeSA9ICJEZW5zaXR5IgogICkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKZ2dwbG90bHkoa2RlX3BsdCkKYGBgCgpUaGUgS0RFIGNvbXB1dGVkIHVzaW5nIG15IGN1c3RvbSBHYXVzc2lhbiBrZXJuZWwgaW1wbGVtZW50YXRpb24gY2xvc2VseSBtYXRjaGVzIHRoZSBlc3RpbWF0ZSBwcm9kdWNlZCBieSBS4oCZcyBkZW5zaXR5KCkgZnVuY3Rpb24sIGluZGljYXRpbmcgdGhhdCB0aGUgaW1wbGVtZW50YXRpb24gaXMgY29ycmVjdC4KCmMpIFdyaXRlIGEgY3VzdG9tIFIgZnVuY3Rpb24gdGhhdCBjb21wdXRlcyBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZXMgdXNpbmcgdGhlIEVwYW5lY2huaWtvdiBrZXJuZWwgd2l0aCAkaD0yJC4gVmFsaWRhdGUgeW91ciBpbXBsZW1lbnRhdGlvbiBieSBjb21wYXJpbmcgcmVzdWx0cyB3aXRoIFIncyBidWlsdC1pbiBkZW5zaXR5KCkgZnVuY3Rpb24gZm9yIEdhdXNzaWFuIGtlcm5lbCBlc3RpbWF0aW9uLgoKJCQKXGhhdHtmfV9oKHQpID0gXGZyYWN7MX17bmh9XHN1bV97aT0xfV5uIEtcbGVmdCggXGZyYWN7dC10X2l9e2h9XHJpZ2h0KSwgXCBcIFx0ZXh0eyB3aGVyZSB9IFwgXCBLKHUpID0gXGZyYWN7M317NH0oMSAtIHVeMikgXCBcIFx0ZXh0eyBmb3IgfSBcIFwgfHV8IFxsZSAxLgokJAoKYGBge3J9Cm15X2tkZSA8LSBmdW5jdGlvbih4LGgpewogIG4gPC0gbGVuZ3RoKHgpCiAgZnVuY3Rpb24odCl7CiAgICB2YXBwbHkodCwgZnVuY3Rpb24odHQpIHsKICAgICAgdSA8LSAodHQgLSB4KS9oCiAgICAgIEsgPC0gaWZlbHNlKGFicyh1KSA8PSAxLCAoMy80KSooMSAtIHVeMiksIDApCiAgICAgIDEvKG4qaCkgKiBzdW0oSyl9LCBudW1lcmljKDEpKQogIH0KfQoKRl9taW5lIDwtIG15X2tkZSh4LCBoID0gMikKRl9yIDwtIGRlbnNpdHkoeCwgYncgPSAyLCBrZXJuZWwgPSAiZXBhbmVjaG5pa292IikKCnRfZ3JpZCA8LSBGX3IkeAoKcGxvdF9kZiA8LSBkYXRhLmZyYW1lKAogIHQgPSB0X2dyaWQsCiAgUl9kZW5zaXR5ID0gRl9yJHksCiAgTXlfZGVuc2l0eSA9IEZfbWluZSh0X2dyaWQpCikKCnBsb3RfZGZfbG9uZyA8LSBwbG90X2RmICU+JQogIHBpdm90X2xvbmdlcigKICAgIGNvbHMgPSBjKFJfZGVuc2l0eSwgTXlfZGVuc2l0eSksCiAgICBuYW1lc190byA9ICJUeXBlIiwKICAgIHZhbHVlc190byA9ICJEZW5zaXR5IgogICkKCmtkZV9wbHQgPC0gZ2dwbG90KHBsb3RfZGZfbG9uZywgYWVzKHggPSB0LCB5ID0gRGVuc2l0eSwgY29sb3IgPSBUeXBlKSkgKwogIGdlb21fbGluZShsaW5ld2lkdGggPSAxKSArCiAgbGFicygKICAgIHRpdGxlID0gIktERTogTXkgSW1wbGVtZW50YXRpb24gdnMgUiBkZW5zaXR5KCkiLAogICAgc3VidGl0bGUgPSBwYXN0ZSgiR2F1c3NpYW4ga2VybmVsLCBoID0iLCAyLCAiLCBuID0iLCBsZW5ndGgoeCkpLAogICAgeCA9ICJ0IiwKICAgIHkgPSAiRGVuc2l0eSIKICApICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCmdncGxvdGx5KGtkZV9wbHQpCmBgYAoKVGhlIEtERSBjb21wdXRlZCB1c2luZyBteSBjdXN0b20gRXBhbmVjaG5pa292IGtlcm5lbCBpbXBsZW1lbnRhdGlvbiBjbG9zZWx5IG1hdGNoZXMgdGhlIGVzdGltYXRlIHByb2R1Y2VkIGJ5IFLigJlzIGRlbnNpdHkoKSBmdW5jdGlvbiwgaW5kaWNhdGluZyB0aGF0IHRoZSBpbXBsZW1lbnRhdGlvbiBpcyBjb3JyZWN0LiBIb3dldmVyLCB0aGUgdHdvIGN1cnZlcyBkb24ndCBjb21wbGV0ZWx5IG92ZXJsYXAgdGhpcyB0aW1lLCBJJ20gYXNzdW1pbmcgdGhlIGRlbnNpdHkoKSBmdW5jdGlvbiBtdXN0IGdvIGFib3ZlIGFuZCBiZXlvbmQganVzdCB0aGUgbWF0aGVtYXRpY2FsIGVxdWF0aW9uLgoKZCkgSG93IGRvZXMgdGhlIGNob2ljZSBvZiBrZXJuZWwgKEdhdXNzaWFuIHZzLiBFcGFuZWNobmlrb3YpIGFmZmVjdCB0aGUgZGVuc2l0eSBlc3RpbWF0ZT8gRm9yIGJvdGgga2VybmVsIGVzdGltYXRvcnMgYXBwbGllZCB0byB0aGlzIGRhdGFzZXQsIHdoYXQgaGFwcGVucyB3aGVuIHdlIHNlbGVjdCAkaD0xLjUkIHZlcnN1cyAkaD0yLjUkPwoKVGhlIEd1YXNzaWFuIGN1cnZlIGlzIG11Y2ggbW9yZSBzbW9vdGggdGhhbiB0aGUgRXBhbmVjaG5pa292IGN1cnZlLiBFcGFuZWNobmlrb3YgZGVuc2l0aWVzIGFyZSBhbGxvd2VkIHRvIGJlIHplcm8gYXQgdGhlIHRhaWxzLCB0aGlzIGRvZXNuJ3Qgc2VlbSB0byBiZSB0cnVlIGZvciBHYXVzc2lhbiBkZW5zaXRpZXMuIEZvciBib3RoIGtlcm5lbHMsIHRoZSBkZW5zaXR5IGN1cnZlcyBiZWNvbWUgbW9yZSByaWdpZCB3aGVuIGggaXMgZGVjcmVhc2VkIHRvIDEuNSBhbmQgbW9yZSBzbW9vdGggd2hlbiBpdHMgaW5jcmVhc2VkIHRvIDIuNS4KCgoKCg==