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.

The formula for an empirical distribution function

\[ F_n(x) =\frac{1}{n}\sum_{i=1}^n I(X_i \le x) = \frac{[\ \text{Number of } X_i \le x]}{n} \]

#Precomparsion with a histogram of fail_elec

#hist(fail_elec)

#ele_ecdf=ecdf(fail_elec)




hours = c(23, 45, 67, 89, 112, 156, 189, 245)

uniq.hours= sort(unique(hours))  #sorting the hours data as unique values


hour.ECDF = function(indat, outx) #create the ECDF as a function
{
#Building the ecdf function in R
  freq.table = table(indat)    
   uniq=as.numeric(names(freq.table))
   rep.hours = as.vector(freq.table)
   cum.rel.feq = cumsum(rep.hours)/sum(rep.hours)
 cum.prob <- NULL
  for (i in 1:length(outx)){
    intvl.id <- which(uniq <= outx[i]) # identify the index meeting the condition
    cum.prob[i] <- cum.rel.feq[max(intvl.id)] # extract the cumulative prob according to CDF 
  }
  cum.prob             
}

#Overall above creates a unique sorted dataset that can be counted as character to assess the frequency of each number type, captures those frequencies and identifies/collects them to be assessed into a distrubtuion


#Plotting ECDF
Hoursp=plot(uniq.hours, hour.ECDF(indat = hours, outx = uniq.hours), type = "s",
     
     main = "ECDF Electronic Failure Times",
     xlab= "Failure Times",
     ylab = "Cummulative Probability"
     )

hist(hours, prob=TRUE)

#lines(x=density(hours), col= "blue")

#Validate using R's ecdf function


  #Recdf=ecdf(hours)

  #hist(Recdf, prob=TRUE)
#ggplotly(Hoursp)
  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).

Yes, I agree with the colleague. The probability of failure before 100 hours is 0.5 based on the ECDF, hour.ECDF(indat = hours, outx = 100) . Currently about 50% of the sample fails before 100 hours. The values 23, 45, 67, and 89 fails prior to 100 hours, making 4 out of the 8 total sample size giving a probability of 0.5 from \({Number of T_i}/{n}\) = 4/8.

$$

  P(T \le t) = \hat{F}_{n}(100) = .5

$$

#Failure before 100 hours is 0.5.

hour.ECDF(indat = hours, outx = 100)
[1] 0.5
hours[hours<100] 
[1] 23 45 67 89
length(hours[hours<100]) #4 values less than 100
[1] 4
Fn=(length(hours[hours<100])) / (length(hours)) #equal to 0.5 #The number of values less than the 100 failure time / sample size --> Gives probability of 0.5

str(hours)
 num [1:8] 23 45 67 89 112 156 189 245
#output 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?

    The density of the 10-15 failure times bin is about .04. The density of the 15-20 failure time is about .08. The density of the 20-25 failure times bin is about .06. The density of the 25-30 failure times bin is about .02.

  • Describe the shape of the histogram’s distribution.

    The histogram shape appears normally distributed.

mech.sys=c(12.3, 14.7, 15.2, 16.8, 18.1, 19.4, 20.6, 22.3, 23.9, 25.4)
length(mech.sys) #n=10
[1] 10
hist(mech.sys, prob=TRUE, breaks= 3, xlab= "Failure times of mechanical system")

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

#making the function for gaussian distrubtion
my.kerf= function(in.data, h, out.x)
{
        n = length(in.data)
        den = numeric(length(out.x))
      for(i in seq_along(out.x))
      {
        
        den[i] = sum(dnorm(out.x[i], mean = in.data, sd = h)) /n #uses Gaussian kernel as a normal distribution (specific to gaussan kernel)
        
      }

  den
  }


xx = seq(min(mech.sys), max(mech.sys), length=200) ## allows density to be connected and smoothed to the eye


#Building a KDE with h =2
my.den = my.kerf(in.data = mech.sys, h=2, out.x=xx) #aaplies the summation to the formula

 
kde = density(mech.sys, kernel = "gaussian", bw= 2 ) #defines kernel type and bandwidth
kde.y = approx(kde$x, kde$y, xout = xx)$y #estimates the density values for the points generated in xx aka the points made for density smoothing

#Plot comparison

# Plot comparison
plot(xx, my.den, type = "l",
     main = "Comparing Kernel Density Estimators",
     xlab = "Mechanical System Failure Times",
     ylab = "Kernel Density",
     lwd = 2,
     col = "navy")

lines(xx, kde.y, lwd = 2, col = "orange")

legend("topright",
       legend = c("my.kerf()", "density()"), lty =c(1,2), lwd = rep(2,2),
       col = c("navy", "orange"), cex = 0.8, bty = "n")

The built and suggested gaussian kernel have the same bandwidth and relationship, as they overlap each other in the Kernel density graph.

/

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

#Building Epanechikov Kernel
  #bw = 2.34 *sd *n^(-1/5)

epan.kerf= function(in.data, h=2, out.x)
{
        n = length(in.data)
        den = numeric(length(out.x))
      for(i in seq_along(out.x))
      {
        
        u = (out.x[i] - in.data)/ h #1. calculate the weighted distance for u for all points      #        
         #(u is the standardized distance btwn point you   are              looking at and specific data point)
        
        epan_kern= ifelse(abs(u) <= 1, 0.75 * (1 - u^2), 0) #the kerenl epanechikov formula
        
        den[i] =sum(epan_kern)/ (n*h)
        
        
       # den[i] = sum(abs(out.x) <= 1, 0.75 * (1 - out.x^2), 0)  #wrong approach
        
      }

  den
}


epanden=density(mech.sys, kernel ="epanechnikov") #defulat density function



#epanechnikov.kernel <- ifelse(abs(out.x) <= 1, 0.75 * (1 - out.x^2), 0)

xx = seq(min(mech.sys), max(mech.sys), length = 200) #smoothing by connecting points together for smoothing
e.den= epan.kerf(in.data = mech.sys, h=2, out.x=xx)  #using the generated function  --> applies parabola Epanechnikov kernel weight to each piece of data 

gaussian_den = density(mech.sys, kernel ="gaussian") #comparison kernel 
kde_y_gaussian= approx(gaussian_den$x, gaussian_den$y, xout= xx)$y #estimates the points made during the xx smoothing steps


# Plot comparison
plot(xx, e.den, type = "l",
     main = "Comparing Kernel Density Estimators",
     xlab = "Mechanical System Failure Times",
     ylab = "Kernel Density",
     lwd = 2,
     col = "navy")

lines(xx, kde_y_gaussian, lwd = 2, col = "orange")

legend("topright",
       legend = c("Epanechnikov", "Gaussian"), lty =c(1,2), lwd = rep(2,2),
       col = c("navy", "orange"), cex = 0.8, bty = "n")

For part C we have a Epanechniov kernel at 2 compared to a gaussian kernel at default. Here we see the kernels take similar generalized direction, although the epanechnikov kernel may be more sensitive to aggregates in the data set.

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

Applying the h=1.5 to Epanechnikov and Gaussian is default

xx = seq(min(mech.sys), max(mech.sys), length = 200)
e.den= epan.kerf(in.data = mech.sys, h=1.5, out.x=xx)

gaussian_den = density(mech.sys, kernel ="gaussian") #comparison kernel
kde_y_gaussian= approx(gaussian_den$x, gaussian_den$y, xout= xx)$y


# Plot comparison
plot(xx, e.den, type = "l",
     main = "Comparing Kernel Density Estimators",
     xlab = "Mechanical System Failure Times",
     ylab = "Kernel Density",
     lwd = 2,
     col = "navy")

lines(xx, kde_y_gaussian, lwd = 2, col = "orange")

legend("topright",
       legend = c("Epanechnikov", "Gaussian"), lty =c(1,2), lwd = rep(2,2),
       col = c("navy", "orange"), cex = 0.8, bty = "n")

When comparing kernel density for a Epanechnikov kernel at 1.5 and a Gaussian set to default, we see similarities in the general curvature in both kernels. The epanenikov may prioritize local weighting while the Gaussian prioritizes smoothness.

The smaller h at 1.5 in comparison to the previous h at 2.0, presents a more in-focus view with a finer detail for individual changes.

Applying the h=2.5 to Epanechnikov and Gaussian is default

xx = seq(min(mech.sys), max(mech.sys), length = 200)
e.den= epan.kerf(in.data = mech.sys, h=2.5, out.x=xx)

gaussian_den = density(mech.sys, kernel ="gaussian") #comparison kernel
kde_y_gaussian= approx(gaussian_den$x, gaussian_den$y, xout= xx)$y


# Plot comparison
plot(xx, e.den, type = "l",
     main = "Comparing Kernel Density Estimators",
     xlab = "Mechanical System Failure Times",
     ylab = "Kernel Density",
     lwd = 2,
     col = "navy")

lines(xx, kde_y_gaussian, lwd = 2, col = "orange")

legend("topright",
       legend = c("Epanechnikov", "Gaussian"), lty =c(1,2), lwd = rep(2,2),
       col = c("navy", "orange"), cex = 0.8, bty = "n")

Applying the h=1.5 to Epanechnikov and Gaussian is h=1.5

xx = seq(min(mech.sys), max(mech.sys), length = 200)
e.den= epan.kerf(in.data = mech.sys, h=1.5, out.x=xx)

gaussian_den = density(mech.sys, kernel ="gaussian", bw=1.5) #comparison kernel
kde_y_gaussian= approx(gaussian_den$x, gaussian_den$y, xout= xx)$y


# Plot comparison
plot(xx, e.den, type = "l",
     main = "Comparing Kernel Density Estimators",
     xlab = "Mechanical System Failure Times",
     ylab = "Kernel Density",
     lwd = 2,
     col = "navy")

lines(xx, kde_y_gaussian, lwd = 2, col = "orange")

legend("topright",
       legend = c("Epanechnikov", "Gaussian"), lty =c(1,2), lwd = rep(2,2),
       col = c("navy", "orange"), cex = 0.8, bty = "n")

With Gaussian and Epanechnikov both at h =1.5, they share general overarching curature with the gaussian looking far smoother than the epanechnikov. The gaussian at 1.5 in comparison to default tor h=2 shows more curvature detail.

Applying the h=2.5 to Epanechnikov and Gaussian is h=2.5

xx = seq(min(mech.sys), max(mech.sys), length = 200)
e.den= epan.kerf(in.data = mech.sys, h=2.5, out.x=xx)

gaussian_den = density(mech.sys, kernel ="gaussian", bw=2.5) #comparison kernel
kde_y_gaussian= approx(gaussian_den$x, gaussian_den$y, xout= xx)$y


# Plot comparison
plot(xx, e.den, type = "l",
     main = "Comparing Kernel Density Estimators",
     xlab = "Mechanical System Failure Times",
     ylab = "Kernel Density",
     lwd = 2,
     col = "navy")

lines(xx, kde_y_gaussian, lwd = 2, col = "orange")

legend("topright",
       legend = c("Epanechnikov", "Gaussian"), lty =c(1,2), lwd = rep(2,2),
       col = c("navy", "orange"), cex = 0.8, bty = "n")

With both Gaussian and Epanechikov at h = 2.5 there is a wide view approach to the density estimator, presenting less noise and overfitting. The gaussian kernel is less curved in comparison to the h=1.5 and h=2 bandwidth kernels.
LS0tDQp0aXRsZTogIkFzc2lnbm1lbnQgMTogRXN0aW1hdGluZyBDREYgYW5kIFBERiINCmF1dGhvcjogIkV6YW5hIFJpdmVycyAiDQpkYXRlOiAiIER1ZTogMDIvMDMvMjAyNiAoUmVhbCkiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IG5vDQogICAgdG9jX2NvbGxhcHNlZDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgc21vb3RoX3Njcm9sbDogeWVzDQogICAgdGhlbWU6IGx1bWVuDQogIHBkZl9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICBmaWdfd2lkdGg6IDMNCiAgICBmaWdfaGVpZ2h0OiAzDQogIHdvcmRfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIGtlZXBfbWQ6IHllcw0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KYGBge2NzcywgZWNobyA9IEZBTFNFfQ0KI1RPQzo6YmVmb3JlIHsNCiAgY29udGVudDogIlRhYmxlIG9mIENvbnRlbnRzIjsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGZvbnQtc2l6ZTogMS4yZW07DQogIGRpc3BsYXk6IGJsb2NrOw0KICBjb2xvcjogbmF2eTsNCiAgbWFyZ2luLWJvdHRvbTogMTBweDsNCn0NCg0KDQpkaXYjVE9DIGxpIHsgICAgIC8qIHRhYmxlIG9mIGNvbnRlbnQgICovDQogICAgbGlzdC1zdHlsZTp1cHBlci1yb21hbjsNCiAgICBiYWNrZ3JvdW5kLWltYWdlOm5vbmU7DQogICAgYmFja2dyb3VuZC1yZXBlYXQ6bm9uZTsNCiAgICBiYWNrZ3JvdW5kLXBvc2l0aW9uOjA7DQp9DQoNCmgxLnRpdGxlIHsgICAgLyogbGV2ZWwgMSBoZWFkZXIgb2YgdGl0bGUgICovDQogIGZvbnQtc2l6ZTogMjJweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGNvbG9yOiBEYXJrUmVkOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtZmFtaWx5OiAiR2lsbCBTYW5zIiwgc2Fucy1zZXJpZjsNCn0NCg0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgZm9udC1zaXplOiAxNXB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IG5hdnk7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCg0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGZvbnQtZmFtaWx5OiAiR2lsbCBTYW5zIiwgc2Fucy1zZXJpZjsNCiAgY29sb3I6IERhcmtCbHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQoNCmgxIHsgLyogSGVhZGVyIDEgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDIwcHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KDQpoMiB7IC8qIEhlYWRlciAyIC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmgzIHsgLyogSGVhZGVyIDMgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDE2cHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDQgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMTRweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KLyogQWRkIGRvdHMgYWZ0ZXIgbnVtYmVyZWQgaGVhZGVycyAqLw0KLmhlYWRlci1zZWN0aW9uLW51bWJlcjo6YWZ0ZXIgew0KICBjb250ZW50OiAiLiI7DQoNCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCi5oaWdobGlnaHRtZSB7IGJhY2tncm91bmQtY29sb3I6eWVsbG93OyB9DQoNCnAgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCn0NCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCANCiMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLg0KaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpDQogICBsaWJyYXJ5KGtuaXRyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJwYW5kZXIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicGFuZGVyIikNCiAgIGxpYnJhcnkocGFuZGVyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnZ3Bsb3QyIikpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpDQogIGxpYnJhcnkoZ2dwbG90MikNCn0NCmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikNCiAgbGlicmFyeSh0aWR5dmVyc2UpDQp9DQoNCmlmICghcmVxdWlyZSgicGxvdGx5IikpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikNCiAgbGlicmFyeShwbG90bHkpDQp9DQojIyMjDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgICAgICMgaW5jbHVkZSBjb2RlIGNodW5rIGluIHRoZSBvdXRwdXQgZmlsZQ0KICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwgICAjIHNvbWV0aW1lcywgeW91IGNvZGUgbWF5IHByb2R1Y2Ugd2FybmluZyBtZXNzYWdlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhlIG91dHB1dCBmaWxlLiANCiAgICAgICAgICAgICAgICAgICAgICByZXN1bHRzID0gVFJVRSwgICAgIyB5b3UgY2FuIGFsc28gZGVjaWRlIHdoZXRoZXIgdG8gaW5jbHVkZSB0aGUgb3V0cHV0DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW4gdGhlIG91dHB1dCBmaWxlLg0KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gTkENCiAgICAgICAgICAgICAgICAgICAgICApICANCmBgYA0KIA0KIFwNCiANCiMjICoqQXNzaWdubWVudCBPYmplY3RpdmVzKiogDQoNCiogRGV2ZWxvcCBhIGNsZWFyIHRlY2huaWNhbCB1bmRlcnN0YW5kaW5nIG9mIG5vbnBhcmFtZXRyaWMgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gZnVuY3Rpb24gKENERikgZXN0aW1hdGlvbiBhbmQgdmFyaW91cyBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0b3JzLg0KDQoqIFRyYW5zbGF0ZSBtYXRoZW1hdGljYWwgZm9ybXVsYXMgaW50byBSIGZ1bmN0aW9ucyBhbmQgYXBwbHkgdGhlbSB0byBzb2x2ZSByZWxhdGVkIHByb2JsZW1zLg0KDQoqIENyZWF0ZSBlZmZlY3RpdmUgdmlzdWFsaXphdGlvbnMgdG8gZGVtb25zdHJhdGUgeW91ciB1bmRlcnN0YW5kaW5nIG9mIGtleSBjb25jZXB0cyBpbiB0aGUgZm9sbG93aW5nIHF1ZXN0aW9ucy4NCg0KDQoNClwNCg0KIyMgKipRdWVzdGlvbiAxOiBDdW11bGF0aXZlIERpc3RyaWJ1dGlvbiBGdW5jdGlvbiAoQ0RGKSBFc3RpbWF0aW9uKioNCg0KVGhlIGZvbGxvd2luZyBmYWlsdXJlIHRpbWVzIChpbiBob3Vycykgd2VyZSBvYnNlcnZlZCBmb3IgOCBlbGVjdHJvbmljIGNvbXBvbmVudHM6DQoNCjxjZW50ZXI+IDIzLCA0NSwgNjcsIDg5LCAxMTIsIDE1NiwgMTg5LCAyNDUgIDwvY2VudGVyPg0KDQphKSBXcml0ZSBhbiBSIGZ1bmN0aW9uIGltcGxlbWVudGluZyB0aGUgRUNERiAkXGhhdHtGfV9uKHQpJCBhY2NvcmRpbmcgdG8gaXRzIG1hdGhlbWF0aWNhbCBkZWZpbml0aW9uLiBWYWxpZGF0ZSB5b3VyIGltcGxlbWVudGF0aW9uIHVzaW5nIFIncyBlY2RmKCkgZnVuY3Rpb24gb24gdGhlIGdpdmVuIGRhdGEsIHdpdGggY29tcGFyaXNvbiBiYXNlZCBvbiB0aGVpciBzdGVwIGZ1bmN0aW9ucy4NCg0KDQoNCg0KVGhlIGZvcm11bGEgZm9yIGFuIGVtcGlyaWNhbCBkaXN0cmlidXRpb24gZnVuY3Rpb24NCg0KJCQNCkZfbih4KSA9XGZyYWN7MX17bn1cc3VtX3tpPTF9Xm4gSShYX2kgXGxlIHgpID0gXGZyYWN7W1wgXHRleHR7TnVtYmVyIG9mICB9IFhfaSBcbGUgeF19e259DQokJA0KDQpgYGB7ciBxMWExfQ0KDQojUHJlY29tcGFyc2lvbiB3aXRoIGEgaGlzdG9ncmFtIG9mIGZhaWxfZWxlYw0KDQojaGlzdChmYWlsX2VsZWMpDQoNCiNlbGVfZWNkZj1lY2RmKGZhaWxfZWxlYykNCg0KDQoNCg0KaG91cnMgPSBjKDIzLCA0NSwgNjcsIDg5LCAxMTIsIDE1NiwgMTg5LCAyNDUpDQoNCnVuaXEuaG91cnM9IHNvcnQodW5pcXVlKGhvdXJzKSkgICNzb3J0aW5nIHRoZSBob3VycyBkYXRhIGFzIHVuaXF1ZSB2YWx1ZXMNCg0KDQpob3VyLkVDREYgPSBmdW5jdGlvbihpbmRhdCwgb3V0eCkgI2NyZWF0ZSB0aGUgRUNERiBhcyBhIGZ1bmN0aW9uDQp7DQojQnVpbGRpbmcgdGhlIGVjZGYgZnVuY3Rpb24gaW4gUg0KICBmcmVxLnRhYmxlID0gdGFibGUoaW5kYXQpICAgIA0KICAgdW5pcT1hcy5udW1lcmljKG5hbWVzKGZyZXEudGFibGUpKQ0KICAgcmVwLmhvdXJzID0gYXMudmVjdG9yKGZyZXEudGFibGUpDQogICBjdW0ucmVsLmZlcSA9IGN1bXN1bShyZXAuaG91cnMpL3N1bShyZXAuaG91cnMpDQogY3VtLnByb2IgPC0gTlVMTA0KICBmb3IgKGkgaW4gMTpsZW5ndGgob3V0eCkpew0KICAgIGludHZsLmlkIDwtIHdoaWNoKHVuaXEgPD0gb3V0eFtpXSkgIyBpZGVudGlmeSB0aGUgaW5kZXggbWVldGluZyB0aGUgY29uZGl0aW9uDQogICAgY3VtLnByb2JbaV0gPC0gY3VtLnJlbC5mZXFbbWF4KGludHZsLmlkKV0gIyBleHRyYWN0IHRoZSBjdW11bGF0aXZlIHByb2IgYWNjb3JkaW5nIHRvIENERiANCiAgfQ0KICBjdW0ucHJvYiAgICAgICAgICAgICANCn0NCg0KI092ZXJhbGwgYWJvdmUgY3JlYXRlcyBhIHVuaXF1ZSBzb3J0ZWQgZGF0YXNldCB0aGF0IGNhbiBiZSBjb3VudGVkIGFzIGNoYXJhY3RlciB0byBhc3Nlc3MgdGhlIGZyZXF1ZW5jeSBvZiBlYWNoIG51bWJlciB0eXBlLCBjYXB0dXJlcyB0aG9zZSBmcmVxdWVuY2llcyBhbmQgaWRlbnRpZmllcy9jb2xsZWN0cyB0aGVtIHRvIGJlIGFzc2Vzc2VkIGludG8gYSBkaXN0cnVidHVpb24NCg0KDQojUGxvdHRpbmcgRUNERg0KSG91cnNwPXBsb3QodW5pcS5ob3VycywgaG91ci5FQ0RGKGluZGF0ID0gaG91cnMsIG91dHggPSB1bmlxLmhvdXJzKSwgdHlwZSA9ICJzIiwNCiAgICAgDQogICAgIG1haW4gPSAiRUNERiBFbGVjdHJvbmljIEZhaWx1cmUgVGltZXMiLA0KICAgICB4bGFiPSAiRmFpbHVyZSBUaW1lcyIsDQogICAgIHlsYWIgPSAiQ3VtbXVsYXRpdmUgUHJvYmFiaWxpdHkiDQogICAgICkNCg0KDQpoaXN0KGhvdXJzLCBwcm9iPVRSVUUpDQojbGluZXMoeD1kZW5zaXR5KGhvdXJzKSwgY29sPSAiYmx1ZSIpDQoNCiNWYWxpZGF0ZSB1c2luZyBSJ3MgZWNkZiBmdW5jdGlvbg0KDQoNCiAgI1JlY2RmPWVjZGYoaG91cnMpDQoNCiAgI2hpc3QoUmVjZGYsIHByb2I9VFJVRSkNCiNnZ3Bsb3RseShIb3Vyc3ApDQoNCmBgYA0KYikgQSBjb2xsZWFndWUgY2xhaW1zIHRoYXQgdGhlIHByb2JhYmlsaXR5IG9mIGZhaWx1cmUgYmVmb3JlIDEwMCBob3VycyBpcyAwLjUgYmFzZWQgb24gdGhlc2UgZGF0YS4gRG8geW91IGFncmVlPyBFeHBsYWluIHlvdXIgcmVhc29uaW5nIHVzaW5nIHRoZSBlbXBpcmljYWwgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gZnVuY3Rpb24gKEVDREYpLg0KDQpZZXMsIEkgYWdyZWUgd2l0aCB0aGUgY29sbGVhZ3VlLiBUaGUgcHJvYmFiaWxpdHkgb2YgZmFpbHVyZSBiZWZvcmUgMTAwIGhvdXJzIGlzIDAuNSBiYXNlZCBvbiB0aGUgRUNERiwgYGBob3VyLkVDREYoaW5kYXQgPSBob3Vycywgb3V0eCA9IDEwMClgYCAuIA0KQ3VycmVudGx5IGFib3V0IDUwJSBvZiB0aGUgc2FtcGxlIGZhaWxzIGJlZm9yZSAxMDAgaG91cnMuIFRoZSB2YWx1ZXMgMjMsIDQ1LCA2NywgYW5kIDg5IGZhaWxzIHByaW9yIHRvIDEwMCBob3VycywgbWFraW5nIDQgb3V0IG9mIHRoZSA4IHRvdGFsIHNhbXBsZSBzaXplIGdpdmluZyBhIHByb2JhYmlsaXR5IG9mIDAuNSBmcm9tICR7TnVtYmVyIG9mIFRfaX0ve259JCA9IDQvOC4NCg0KJCQNCg0KICAgICAgUChUIFxsZSB0KSA9IFxoYXR7Rn1fe259KDEwMCkgPSAuNQ0KDQokJA0KDQpgYGB7ciBxMWJ9DQoNCiNGYWlsdXJlIGJlZm9yZSAxMDAgaG91cnMgaXMgMC41Lg0KDQpob3VyLkVDREYoaW5kYXQgPSBob3Vycywgb3V0eCA9IDEwMCkNCg0KaG91cnNbaG91cnM8MTAwXSANCmxlbmd0aChob3Vyc1tob3VyczwxMDBdKSAjNCB2YWx1ZXMgbGVzcyB0aGFuIDEwMA0KDQpGbj0obGVuZ3RoKGhvdXJzW2hvdXJzPDEwMF0pKSAvIChsZW5ndGgoaG91cnMpKSAjZXF1YWwgdG8gMC41ICNUaGUgbnVtYmVyIG9mIHZhbHVlcyBsZXNzIHRoYW4gdGhlIDEwMCBmYWlsdXJlIHRpbWUgLyBzYW1wbGUgc2l6ZSAtLT4gR2l2ZXMgcHJvYmFiaWxpdHkgb2YgMC41DQoNCnN0cihob3VycykNCg0KDQojb3V0cHV0IGlzIFswLjVdDQoNCg0KDQpgYGANCg0KXA0KDQojIyAqKlF1ZXN0aW9uIDI6IERlbnNpdHkgRnVuY3Rpb24gRXN0aW1hdGlvbioqDQoNCkNvbnNpZGVyIHRoZSBmb2xsb3dpbmcgZmFpbHVyZSB0aW1lcyBmcm9tIGEgbWVjaGFuaWNhbCBzeXN0ZW06DQoNCjxjZW50ZXI+IDEyLjMsIDE0LjcsIDE1LjIsIDE2LjgsIDE4LjEsIDE5LjQsIDIwLjYsIDIyLjMsIDIzLjksIDI1LjQgPC9jZW50ZXI+DQoNCmEpIENyZWF0ZSBhIGhpc3RvZ3JhbSBvZiB0aGUgZGF0YSB1c2luZyAzIGVxdWFsbHkgc3BhY2VkIGJpbnMuDQoNCg0KKiBXaGF0IGlzIHRoZSBlc3RpbWF0ZWQgZGVuc2l0eSBpbiBlYWNoIGJpbj8NCg0KICBUaGUgZGVuc2l0eSBvZiB0aGUgMTAtMTUgZmFpbHVyZSB0aW1lcyBiaW4gaXMgYWJvdXQgLjA0Lg0KICBUaGUgZGVuc2l0eSBvZiB0aGUgMTUtMjAgZmFpbHVyZSB0aW1lIGlzIGFib3V0IC4wOC4NCiAgVGhlIGRlbnNpdHkgb2YgdGhlIDIwLTI1IGZhaWx1cmUgdGltZXMgYmluIGlzIGFib3V0IC4wNi4NCiAgVGhlIGRlbnNpdHkgb2YgdGhlIDI1LTMwIGZhaWx1cmUgdGltZXMgYmluIGlzIGFib3V0IC4wMi4NCg0KDQoqIERlc2NyaWJlIHRoZSBzaGFwZSBvZiB0aGUgaGlzdG9ncmFtJ3MgZGlzdHJpYnV0aW9uLg0KDQogIFRoZSBoaXN0b2dyYW0gc2hhcGUgYXBwZWFycyBub3JtYWxseSBkaXN0cmlidXRlZC4gDQoNCmBgYHtyIFEyfQ0KbWVjaC5zeXM9YygxMi4zLCAxNC43LCAxNS4yLCAxNi44LCAxOC4xLCAxOS40LCAyMC42LCAyMi4zLCAyMy45LCAyNS40KQ0KbGVuZ3RoKG1lY2guc3lzKSAjbj0xMA0KDQpoaXN0KG1lY2guc3lzLCBwcm9iPVRSVUUsIGJyZWFrcz0gMywgeGxhYj0gIkZhaWx1cmUgdGltZXMgb2YgbWVjaGFuaWNhbCBzeXN0ZW0iKQ0KDQoNCg0KYGBgDQoNCg0KDQpiKSBXcml0ZSBhbiBSIGZ1bmN0aW9uIHRoYXQgY29tcHV0ZXMga2VybmVsIGRlbnNpdHkgZXN0aW1hdGVzIHVzaW5nIGEgR2F1c3NpYW4ga2VybmVsIHdpdGggJGg9MiQuIFZhbGlkYXRlIHlvdXIgaW1wbGVtZW50YXRpb24gYWdhaW5zdCBSJ3MgYnVpbHQtaW4gZGVuc2l0eSgpIGZ1bmN0aW9uLg0KDQokJA0KXGhhdHtmfV9oKHQpID0gXGZyYWN7MX17bmh9XHN1bV97aT0xfV5uIEtcbGVmdCggXGZyYWN7dC10X2l9e2h9XHJpZ2h0KSwgXCBcIFx0ZXh0eyB3aGVyZSB9IFwgXCBLKHUpID0gXGZyYWN7MX17XHNxcnR7MlxwaX19IGVeey11XjIvMn0uDQokJA0KIA0KYGBge3Iga2VybjJ9DQoNCiNtYWtpbmcgdGhlIGZ1bmN0aW9uIGZvciBnYXVzc2lhbiBkaXN0cnVidGlvbg0KbXkua2VyZj0gZnVuY3Rpb24oaW4uZGF0YSwgaCwgb3V0LngpDQp7DQogICAgICAgIG4gPSBsZW5ndGgoaW4uZGF0YSkNCiAgICAgICAgZGVuID0gbnVtZXJpYyhsZW5ndGgob3V0LngpKQ0KICAgICAgZm9yKGkgaW4gc2VxX2Fsb25nKG91dC54KSkNCiAgICAgIHsNCiAgICAgICAgDQogICAgICAgIGRlbltpXSA9IHN1bShkbm9ybShvdXQueFtpXSwgbWVhbiA9IGluLmRhdGEsIHNkID0gaCkpIC9uICN1c2VzIEdhdXNzaWFuIGtlcm5lbCBhcyBhIG5vcm1hbCBkaXN0cmlidXRpb24gKHNwZWNpZmljIHRvIGdhdXNzYW4ga2VybmVsKQ0KICAgICAgICANCiAgICAgIH0NCg0KICBkZW4NCiAgfQ0KDQoNCnh4ID0gc2VxKG1pbihtZWNoLnN5cyksIG1heChtZWNoLnN5cyksIGxlbmd0aD0yMDApICMjIGFsbG93cyBkZW5zaXR5IHRvIGJlIGNvbm5lY3RlZCBhbmQgc21vb3RoZWQgdG8gdGhlIGV5ZQ0KDQoNCiNCdWlsZGluZyBhIEtERSB3aXRoIGggPTINCm15LmRlbiA9IG15LmtlcmYoaW4uZGF0YSA9IG1lY2guc3lzLCBoPTIsIG91dC54PXh4KSAjYWFwbGllcyB0aGUgc3VtbWF0aW9uIHRvIHRoZSBmb3JtdWxhDQoNCiANCmtkZSA9IGRlbnNpdHkobWVjaC5zeXMsIGtlcm5lbCA9ICJnYXVzc2lhbiIsIGJ3PSAyICkgI2RlZmluZXMga2VybmVsIHR5cGUgYW5kIGJhbmR3aWR0aA0Ka2RlLnkgPSBhcHByb3goa2RlJHgsIGtkZSR5LCB4b3V0ID0geHgpJHkgI2VzdGltYXRlcyB0aGUgZGVuc2l0eSB2YWx1ZXMgZm9yIHRoZSBwb2ludHMgZ2VuZXJhdGVkIGluIHh4IGFrYSB0aGUgcG9pbnRzIG1hZGUgZm9yIGRlbnNpdHkgc21vb3RoaW5nDQoNCiNQbG90IGNvbXBhcmlzb24NCg0KIyBQbG90IGNvbXBhcmlzb24NCnBsb3QoeHgsIG15LmRlbiwgdHlwZSA9ICJsIiwNCiAgICAgbWFpbiA9ICJDb21wYXJpbmcgS2VybmVsIERlbnNpdHkgRXN0aW1hdG9ycyIsDQogICAgIHhsYWIgPSAiTWVjaGFuaWNhbCBTeXN0ZW0gRmFpbHVyZSBUaW1lcyIsDQogICAgIHlsYWIgPSAiS2VybmVsIERlbnNpdHkiLA0KICAgICBsd2QgPSAyLA0KICAgICBjb2wgPSAibmF2eSIpDQoNCmxpbmVzKHh4LCBrZGUueSwgbHdkID0gMiwgY29sID0gIm9yYW5nZSIpDQoNCmxlZ2VuZCgidG9wcmlnaHQiLA0KICAgICAgIGxlZ2VuZCA9IGMoIm15LmtlcmYoKSIsICJkZW5zaXR5KCkiKSwgbHR5ID1jKDEsMiksIGx3ZCA9IHJlcCgyLDIpLA0KICAgICAgIGNvbCA9IGMoIm5hdnkiLCAib3JhbmdlIiksIGNleCA9IDAuOCwgYnR5ID0gIm4iKQ0KYGBgDQoNClRoZSAgYnVpbHQgYW5kIHN1Z2dlc3RlZCBnYXVzc2lhbiBrZXJuZWwgaGF2ZSB0aGUgc2FtZSBiYW5kd2lkdGggYW5kIHJlbGF0aW9uc2hpcCwgYXMgdGhleSBvdmVybGFwIGVhY2ggb3RoZXIgaW4gdGhlIEtlcm5lbCBkZW5zaXR5IGdyYXBoLg0KDQoNCi8NCg0KDQoNCmMpIFdyaXRlIGEgY3VzdG9tIFIgZnVuY3Rpb24gdGhhdCBjb21wdXRlcyBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZXMgdXNpbmcgdGhlIEVwYW5lY2huaWtvdiBrZXJuZWwgd2l0aCAkaD0yJC4gVmFsaWRhdGUgeW91ciBpbXBsZW1lbnRhdGlvbiBieSBjb21wYXJpbmcgcmVzdWx0cyB3aXRoIFIncyBidWlsdC1pbiBkZW5zaXR5KCkgZnVuY3Rpb24gZm9yIEdhdXNzaWFuIGtlcm5lbCBlc3RpbWF0aW9uLg0KDQokJA0KXGhhdHtmfV9oKHQpID0gXGZyYWN7MX17bmh9XHN1bV97aT0xfV5uIEtcbGVmdCggXGZyYWN7dC10X2l9e2h9XHJpZ2h0KSwgXCBcIFx0ZXh0eyB3aGVyZSB9IFwgXCBLKHUpID0gXGZyYWN7M317NH0oMSAtIHVeMikgXCBcIFx0ZXh0eyBmb3IgfSBcIFwgfHV8IFxsZSAxLg0KJCQNCg0KDQpgYGB7ciBxMlBjfQ0KDQojQnVpbGRpbmcgRXBhbmVjaGlrb3YgS2VybmVsDQogICNidyA9IDIuMzQgKnNkICpuXigtMS81KQ0KDQplcGFuLmtlcmY9IGZ1bmN0aW9uKGluLmRhdGEsIGg9Miwgb3V0LngpDQp7DQogICAgICAgIG4gPSBsZW5ndGgoaW4uZGF0YSkNCiAgICAgICAgZGVuID0gbnVtZXJpYyhsZW5ndGgob3V0LngpKQ0KICAgICAgZm9yKGkgaW4gc2VxX2Fsb25nKG91dC54KSkNCiAgICAgIHsNCiAgICAgICAgDQogICAgICAgIHUgPSAob3V0LnhbaV0gLSBpbi5kYXRhKS8gaCAjMS4gY2FsY3VsYXRlIHRoZSB3ZWlnaHRlZCBkaXN0YW5jZSBmb3IgdSBmb3IgYWxsIHBvaW50cyAgICAgICMgICAgICAgIA0KICAgICAgICAgIyh1IGlzIHRoZSBzdGFuZGFyZGl6ZWQgZGlzdGFuY2UgYnR3biBwb2ludCB5b3UgICBhcmUgICAgICAgICAgICAgIGxvb2tpbmcgYXQgYW5kIHNwZWNpZmljIGRhdGEgcG9pbnQpDQogICAgICAgIA0KICAgICAgICBlcGFuX2tlcm49IGlmZWxzZShhYnModSkgPD0gMSwgMC43NSAqICgxIC0gdV4yKSwgMCkgI3RoZSBrZXJlbmwgZXBhbmVjaGlrb3YgZm9ybXVsYQ0KICAgICAgICANCiAgICAgICAgZGVuW2ldID1zdW0oZXBhbl9rZXJuKS8gKG4qaCkNCiAgICAgICAgDQogICAgICAgIA0KICAgICAgICMgZGVuW2ldID0gc3VtKGFicyhvdXQueCkgPD0gMSwgMC43NSAqICgxIC0gb3V0LnheMiksIDApICAjd3JvbmcgYXBwcm9hY2gNCiAgICAgICAgDQogICAgICB9DQoNCiAgZGVuDQp9DQoNCg0KZXBhbmRlbj1kZW5zaXR5KG1lY2guc3lzLCBrZXJuZWwgPSJlcGFuZWNobmlrb3YiKSAjZGVmdWxhdCBkZW5zaXR5IGZ1bmN0aW9uDQoNCg0KDQojZXBhbmVjaG5pa292Lmtlcm5lbCA8LSBpZmVsc2UoYWJzKG91dC54KSA8PSAxLCAwLjc1ICogKDEgLSBvdXQueF4yKSwgMCkNCg0KeHggPSBzZXEobWluKG1lY2guc3lzKSwgbWF4KG1lY2guc3lzKSwgbGVuZ3RoID0gMjAwKSAjc21vb3RoaW5nIGJ5IGNvbm5lY3RpbmcgcG9pbnRzIHRvZ2V0aGVyIGZvciBzbW9vdGhpbmcNCmUuZGVuPSBlcGFuLmtlcmYoaW4uZGF0YSA9IG1lY2guc3lzLCBoPTIsIG91dC54PXh4KSAgI3VzaW5nIHRoZSBnZW5lcmF0ZWQgZnVuY3Rpb24gIC0tPiBhcHBsaWVzIHBhcmFib2xhIEVwYW5lY2huaWtvdiBrZXJuZWwgd2VpZ2h0IHRvIGVhY2ggcGllY2Ugb2YgZGF0YSANCg0KZ2F1c3NpYW5fZGVuID0gZGVuc2l0eShtZWNoLnN5cywga2VybmVsID0iZ2F1c3NpYW4iKSAjY29tcGFyaXNvbiBrZXJuZWwgDQprZGVfeV9nYXVzc2lhbj0gYXBwcm94KGdhdXNzaWFuX2RlbiR4LCBnYXVzc2lhbl9kZW4keSwgeG91dD0geHgpJHkgI2VzdGltYXRlcyB0aGUgcG9pbnRzIG1hZGUgZHVyaW5nIHRoZSB4eCBzbW9vdGhpbmcgc3RlcHMNCg0KDQojIFBsb3QgY29tcGFyaXNvbg0KcGxvdCh4eCwgZS5kZW4sIHR5cGUgPSAibCIsDQogICAgIG1haW4gPSAiQ29tcGFyaW5nIEtlcm5lbCBEZW5zaXR5IEVzdGltYXRvcnMiLA0KICAgICB4bGFiID0gIk1lY2hhbmljYWwgU3lzdGVtIEZhaWx1cmUgVGltZXMiLA0KICAgICB5bGFiID0gIktlcm5lbCBEZW5zaXR5IiwNCiAgICAgbHdkID0gMiwNCiAgICAgY29sID0gIm5hdnkiKQ0KDQpsaW5lcyh4eCwga2RlX3lfZ2F1c3NpYW4sIGx3ZCA9IDIsIGNvbCA9ICJvcmFuZ2UiKQ0KDQpsZWdlbmQoInRvcHJpZ2h0IiwNCiAgICAgICBsZWdlbmQgPSBjKCJFcGFuZWNobmlrb3YiLCAiR2F1c3NpYW4iKSwgbHR5ID1jKDEsMiksIGx3ZCA9IHJlcCgyLDIpLA0KICAgICAgIGNvbCA9IGMoIm5hdnkiLCAib3JhbmdlIiksIGNleCA9IDAuOCwgYnR5ID0gIm4iKQ0KDQoNCg0KDQoNCg0KDQpgYGANCjxwPiBGb3IgcGFydCBDIHdlIGhhdmUgYSBFcGFuZWNobmlvdiBrZXJuZWwgYXQgMiBjb21wYXJlZCB0byBhIGdhdXNzaWFuIGtlcm5lbCBhdCBkZWZhdWx0LiBIZXJlIHdlIHNlZSB0aGUga2VybmVscyB0YWtlIHNpbWlsYXIgZ2VuZXJhbGl6ZWQgZGlyZWN0aW9uLCBhbHRob3VnaCB0aGUgZXBhbmVjaG5pa292IGtlcm5lbCBtYXkgYmUgbW9yZSBzZW5zaXRpdmUgdG8gYWdncmVnYXRlcyBpbiB0aGUgZGF0YSBzZXQuDQoNCmQpIEhvdyBkb2VzIHRoZSBjaG9pY2Ugb2Yga2VybmVsIChHYXVzc2lhbiB2cy4gRXBhbmVjaG5pa292KSBhZmZlY3QgdGhlIGRlbnNpdHkgZXN0aW1hdGU/IEZvciBib3RoIGtlcm5lbCBlc3RpbWF0b3JzIGFwcGxpZWQgdG8gdGhpcyBkYXRhc2V0LCB3aGF0IGhhcHBlbnMgd2hlbiB3ZSBzZWxlY3QgJGg9MS41JCB2ZXJzdXMgJGg9Mi41JD8NCg0KDQoNCkFwcGx5aW5nIHRoZSBoPTEuNSB0byBFcGFuZWNobmlrb3YgYW5kIEdhdXNzaWFuIGlzIGRlZmF1bHQNCmBgYHtyIFEyZDEuNX0NCg0KDQp4eCA9IHNlcShtaW4obWVjaC5zeXMpLCBtYXgobWVjaC5zeXMpLCBsZW5ndGggPSAyMDApDQplLmRlbj0gZXBhbi5rZXJmKGluLmRhdGEgPSBtZWNoLnN5cywgaD0xLjUsIG91dC54PXh4KQ0KDQpnYXVzc2lhbl9kZW4gPSBkZW5zaXR5KG1lY2guc3lzLCBrZXJuZWwgPSJnYXVzc2lhbiIpICNjb21wYXJpc29uIGtlcm5lbA0Ka2RlX3lfZ2F1c3NpYW49IGFwcHJveChnYXVzc2lhbl9kZW4keCwgZ2F1c3NpYW5fZGVuJHksIHhvdXQ9IHh4KSR5DQoNCg0KIyBQbG90IGNvbXBhcmlzb24NCnBsb3QoeHgsIGUuZGVuLCB0eXBlID0gImwiLA0KICAgICBtYWluID0gIkNvbXBhcmluZyBLZXJuZWwgRGVuc2l0eSBFc3RpbWF0b3JzIiwNCiAgICAgeGxhYiA9ICJNZWNoYW5pY2FsIFN5c3RlbSBGYWlsdXJlIFRpbWVzIiwNCiAgICAgeWxhYiA9ICJLZXJuZWwgRGVuc2l0eSIsDQogICAgIGx3ZCA9IDIsDQogICAgIGNvbCA9ICJuYXZ5IikNCg0KbGluZXMoeHgsIGtkZV95X2dhdXNzaWFuLCBsd2QgPSAyLCBjb2wgPSAib3JhbmdlIikNCg0KbGVnZW5kKCJ0b3ByaWdodCIsDQogICAgICAgbGVnZW5kID0gYygiRXBhbmVjaG5pa292IiwgIkdhdXNzaWFuIiksIGx0eSA9YygxLDIpLCBsd2QgPSByZXAoMiwyKSwNCiAgICAgICBjb2wgPSBjKCJuYXZ5IiwgIm9yYW5nZSIpLCBjZXggPSAwLjgsIGJ0eSA9ICJuIikNCg0KDQpgYGANCg0KDQoNCg0KV2hlbiBjb21wYXJpbmcga2VybmVsIGRlbnNpdHkgZm9yIGEgRXBhbmVjaG5pa292IGtlcm5lbCBhdCAxLjUgYW5kIGEgR2F1c3NpYW4gc2V0IHRvIGRlZmF1bHQsIHdlIHNlZSBzaW1pbGFyaXRpZXMgaW4gdGhlIGdlbmVyYWwgY3VydmF0dXJlIGluIGJvdGgga2VybmVscy4gVGhlIGVwYW5lbmlrb3YgbWF5IHByaW9yaXRpemUgbG9jYWwgd2VpZ2h0aW5nIHdoaWxlIHRoZSBHYXVzc2lhbiBwcmlvcml0aXplcyBzbW9vdGhuZXNzLg0KDQpUaGUgc21hbGxlciBoIGF0IDEuNSBpbiBjb21wYXJpc29uIHRvIHRoZSBwcmV2aW91cyBoIGF0IDIuMCwgcHJlc2VudHMgYSBtb3JlIGluLWZvY3VzIHZpZXcgd2l0aCBhIGZpbmVyIGRldGFpbCBmb3IgaW5kaXZpZHVhbCBjaGFuZ2VzLg0KDQpBcHBseWluZyB0aGUgaD0yLjUgdG8gRXBhbmVjaG5pa292IGFuZCBHYXVzc2lhbiBpcyBkZWZhdWx0DQpgYGB7ciBRMmQyLjV9DQoNCg0KeHggPSBzZXEobWluKG1lY2guc3lzKSwgbWF4KG1lY2guc3lzKSwgbGVuZ3RoID0gMjAwKQ0KZS5kZW49IGVwYW4ua2VyZihpbi5kYXRhID0gbWVjaC5zeXMsIGg9Mi41LCBvdXQueD14eCkNCg0KZ2F1c3NpYW5fZGVuID0gZGVuc2l0eShtZWNoLnN5cywga2VybmVsID0iZ2F1c3NpYW4iKSAjY29tcGFyaXNvbiBrZXJuZWwNCmtkZV95X2dhdXNzaWFuPSBhcHByb3goZ2F1c3NpYW5fZGVuJHgsIGdhdXNzaWFuX2RlbiR5LCB4b3V0PSB4eCkkeQ0KDQoNCiMgUGxvdCBjb21wYXJpc29uDQpwbG90KHh4LCBlLmRlbiwgdHlwZSA9ICJsIiwNCiAgICAgbWFpbiA9ICJDb21wYXJpbmcgS2VybmVsIERlbnNpdHkgRXN0aW1hdG9ycyIsDQogICAgIHhsYWIgPSAiTWVjaGFuaWNhbCBTeXN0ZW0gRmFpbHVyZSBUaW1lcyIsDQogICAgIHlsYWIgPSAiS2VybmVsIERlbnNpdHkiLA0KICAgICBsd2QgPSAyLA0KICAgICBjb2wgPSAibmF2eSIpDQoNCmxpbmVzKHh4LCBrZGVfeV9nYXVzc2lhbiwgbHdkID0gMiwgY29sID0gIm9yYW5nZSIpDQoNCmxlZ2VuZCgidG9wcmlnaHQiLA0KICAgICAgIGxlZ2VuZCA9IGMoIkVwYW5lY2huaWtvdiIsICJHYXVzc2lhbiIpLCBsdHkgPWMoMSwyKSwgbHdkID0gcmVwKDIsMiksDQogICAgICAgY29sID0gYygibmF2eSIsICJvcmFuZ2UiKSwgY2V4ID0gMC44LCBidHkgPSAibiIpDQoNCg0KYGBgDQoNCg0KQXBwbHlpbmcgdGhlIGg9MS41IHRvIEVwYW5lY2huaWtvdiBhbmQgR2F1c3NpYW4gaXMgaD0xLjUNCmBgYHtyIFEyZGJvdGgxLjV9DQoNCg0KeHggPSBzZXEobWluKG1lY2guc3lzKSwgbWF4KG1lY2guc3lzKSwgbGVuZ3RoID0gMjAwKQ0KZS5kZW49IGVwYW4ua2VyZihpbi5kYXRhID0gbWVjaC5zeXMsIGg9MS41LCBvdXQueD14eCkNCg0KZ2F1c3NpYW5fZGVuID0gZGVuc2l0eShtZWNoLnN5cywga2VybmVsID0iZ2F1c3NpYW4iLCBidz0xLjUpICNjb21wYXJpc29uIGtlcm5lbA0Ka2RlX3lfZ2F1c3NpYW49IGFwcHJveChnYXVzc2lhbl9kZW4keCwgZ2F1c3NpYW5fZGVuJHksIHhvdXQ9IHh4KSR5DQoNCg0KIyBQbG90IGNvbXBhcmlzb24NCnBsb3QoeHgsIGUuZGVuLCB0eXBlID0gImwiLA0KICAgICBtYWluID0gIkNvbXBhcmluZyBLZXJuZWwgRGVuc2l0eSBFc3RpbWF0b3JzIiwNCiAgICAgeGxhYiA9ICJNZWNoYW5pY2FsIFN5c3RlbSBGYWlsdXJlIFRpbWVzIiwNCiAgICAgeWxhYiA9ICJLZXJuZWwgRGVuc2l0eSIsDQogICAgIGx3ZCA9IDIsDQogICAgIGNvbCA9ICJuYXZ5IikNCg0KbGluZXMoeHgsIGtkZV95X2dhdXNzaWFuLCBsd2QgPSAyLCBjb2wgPSAib3JhbmdlIikNCg0KbGVnZW5kKCJ0b3ByaWdodCIsDQogICAgICAgbGVnZW5kID0gYygiRXBhbmVjaG5pa292IiwgIkdhdXNzaWFuIiksIGx0eSA9YygxLDIpLCBsd2QgPSByZXAoMiwyKSwNCiAgICAgICBjb2wgPSBjKCJuYXZ5IiwgIm9yYW5nZSIpLCBjZXggPSAwLjgsIGJ0eSA9ICJuIikNCg0KDQpgYGANCg0KDQo8cD4gV2l0aCBHYXVzc2lhbiBhbmQgRXBhbmVjaG5pa292IGJvdGggYXQgaCA9MS41LCB0aGV5IHNoYXJlIGdlbmVyYWwgb3ZlcmFyY2hpbmcgY3VyYXR1cmUgd2l0aCB0aGUgZ2F1c3NpYW4gbG9va2luZyBmYXIgc21vb3RoZXIgdGhhbiB0aGUgZXBhbmVjaG5pa292LiBUaGUgZ2F1c3NpYW4gYXQgMS41IGluIGNvbXBhcmlzb24gdG8gZGVmYXVsdCB0b3IgaD0yIHNob3dzIG1vcmUgY3VydmF0dXJlIGRldGFpbC4NCg0KDQpBcHBseWluZyB0aGUgaD0yLjUgdG8gRXBhbmVjaG5pa292IGFuZCBHYXVzc2lhbiBpcyBoPTIuNQ0KYGBge3IgUTJkYm90aDIuNX0NCg0KDQp4eCA9IHNlcShtaW4obWVjaC5zeXMpLCBtYXgobWVjaC5zeXMpLCBsZW5ndGggPSAyMDApDQplLmRlbj0gZXBhbi5rZXJmKGluLmRhdGEgPSBtZWNoLnN5cywgaD0yLjUsIG91dC54PXh4KQ0KDQpnYXVzc2lhbl9kZW4gPSBkZW5zaXR5KG1lY2guc3lzLCBrZXJuZWwgPSJnYXVzc2lhbiIsIGJ3PTIuNSkgI2NvbXBhcmlzb24ga2VybmVsDQprZGVfeV9nYXVzc2lhbj0gYXBwcm94KGdhdXNzaWFuX2RlbiR4LCBnYXVzc2lhbl9kZW4keSwgeG91dD0geHgpJHkNCg0KDQojIFBsb3QgY29tcGFyaXNvbg0KcGxvdCh4eCwgZS5kZW4sIHR5cGUgPSAibCIsDQogICAgIG1haW4gPSAiQ29tcGFyaW5nIEtlcm5lbCBEZW5zaXR5IEVzdGltYXRvcnMiLA0KICAgICB4bGFiID0gIk1lY2hhbmljYWwgU3lzdGVtIEZhaWx1cmUgVGltZXMiLA0KICAgICB5bGFiID0gIktlcm5lbCBEZW5zaXR5IiwNCiAgICAgbHdkID0gMiwNCiAgICAgY29sID0gIm5hdnkiKQ0KDQpsaW5lcyh4eCwga2RlX3lfZ2F1c3NpYW4sIGx3ZCA9IDIsIGNvbCA9ICJvcmFuZ2UiKQ0KDQpsZWdlbmQoInRvcHJpZ2h0IiwNCiAgICAgICBsZWdlbmQgPSBjKCJFcGFuZWNobmlrb3YiLCAiR2F1c3NpYW4iKSwgbHR5ID1jKDEsMiksIGx3ZCA9IHJlcCgyLDIpLA0KICAgICAgIGNvbCA9IGMoIm5hdnkiLCAib3JhbmdlIiksIGNleCA9IDAuOCwgYnR5ID0gIm4iKQ0KDQoNCmBgYA0KDQo8Y2VudGVyPg0KDQpXaXRoIGJvdGggR2F1c3NpYW4gYW5kIEVwYW5lY2hpa292IGF0IGggPSAyLjUgdGhlcmUgaXMgYSB3aWRlIHZpZXcgYXBwcm9hY2ggdG8gdGhlIGRlbnNpdHkgZXN0aW1hdG9yLCBwcmVzZW50aW5nIGxlc3Mgbm9pc2UgYW5kIG92ZXJmaXR0aW5nLiBUaGUgZ2F1c3NpYW4ga2VybmVsIGlzIGxlc3MgY3VydmVkIGluIGNvbXBhcmlzb24gdG8gdGhlIGg9MS41IGFuZCBoPTIgYmFuZHdpZHRoIGtlcm5lbHMuIDxjZW50ZXI+