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

- 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.
/
- 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.
- 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+