2024
1.(a) Create a vector x = (1,2,2,3,3,3,4,4,4,4,5,5,5,5,5) by using
rep() and find multiplication of x.
x <- rep(1:5, c(1,2,3,4,5))
x
[1] 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5
1.(b) Create vector y = N * e^x / x!, where N = 1:length(x). Find sum
of y.
N <- 1:length(x)
y <- N*exp(x)/factorial(x)
y
[1] 2.718282 7.389056 11.083584 13.390358 16.737947 20.085537
[7] 15.924460 18.199383 20.474306 22.749229 13.604540 14.841316
[13] 16.078092 17.314869 18.551645
y_sum <- sum(y)
y_sum
[1] 229.1426
1.(c) Find index positions where 10 < y < 15. Find the sum of
values.
indices <- which(y>10 & y<15)
indices
[1] 3 4 11 12
val_sum <- sum(y[indices])
val_sum
[1] 52.9198
1.(d) Create int.y as nearest integer of y, count divisible by 4
int.y <- round(y)
int.y
[1] 3 7 11 13 17 20 16 18 20 23 14 15 16 17 19
n <- length(which(int.y%%4==0))
n
[1] 4
1.(e) Function to create y and calculate mean
calc_mean <- function(x){
N <- 1:length(x)
y <- N*exp(x)/factorial(x)
am <- mean(y)
if (any(y<=0)){
gm <- NA
hm <- NA
} else {
gm <- exp(mean(log(y)))
hm <- 1/mean(1/y)
}
return(list(AM=am,GM=gm,HM=hm))
}
1.(f) Evaluate the function for X.
means_result <- calc_mean(x)
cat("Arithmetic mean :", means_result$AM, "\n")
Arithmetic mean : 15.27617
cat("Geometric mean :", means_result$GM, "\n")
Geometric mean : 13.90898
cat("Harmonic mean :", means_result$HM, "\n")
Harmonic mean : 11.5125
2.(a) Using seed 100, create three stdnormal random vectors of length
100 (x, x1,x2).
Create vectors y,z [y=x+x1, z=y+x2] and Evaluate
sum((x-xbar)(y-ybar)), sum((x-xbar)(z-zbar)),
sum((y-ybar)(z-zbar))
create matrix A(100x3) where each column is filled by each vector
x,y,z. B=(A^T A) then B^-1 = ? Find sum of diagonal elements of
B.
# (i)
set.seed(100)
x <- rnorm(100)
x1 <- rnorm(100)
x2 <- rnorm(100)
y <- x+x1
z <- y+x2
spxy <- sum((x-mean(x))*(y-mean(y)))
spxz <- sum((x-mean(x))*(z-mean(z)))
spxy
[1] 92.27519
spxz
[1] 103.4904
# (ii)
A <- matrix(c(x,y,z), byrow = FALSE, ncol = 3)
head(A)
[,1] [,2] [,3]
[1,] -0.50219235 -0.83511570 -0.8069439
[2,] 0.13153117 1.49464487 1.1379415
[3,] -0.07891709 -0.54806443 0.3045619
[4,] 0.88678481 1.72966044 2.2430257
[5,] 0.11697127 -1.34102245 -0.3228195
[6,] 0.31863009 -0.08167583 -1.1031549
B <- t(A) %*% A
B
[,1] [,2] [,3]
[1,] 103.14396 92.27928 103.4982
[2,] 92.27928 144.18476 133.6212
[3,] 103.49823 133.62118 230.0049
n <- nrow(B)
diag_sum <- 0
# sum(diag(B))
for (i in 1:n){
diag_sum <- diag_sum + B[i,i]
}
diag_sum
[1] 477.3336
2.(b) Function to count entries > k in each column of a
matrix.
count_gt <- function(mat, k) {
apply(mat, 2, function(col) sum(col > k))
}
2.(c) Evaluate the func with (A,2)
count_gt(A,2)
[1] 3 4 8
3.(a) Gamma distribution calculations (shape=3, scale=0.25) (i) P(X ≥
50) (ii) P(20 < X < 40) (iii) Find k such that P(X > k) =
0.1
#(i)
p50_ <- 1-pgamma(50, shape=3, scale=0.25)
p50_
[1] 0
#(ii)
p20_40 <- pgamma(40, shape=3, scale=0.25)-pgamma(20, shape=3, scale=0.25)
p20_40
[1] 0
#(iii)
k <- qgamma(0.9, shape = 3, scale = 0.25) # Since P(X > k) = 0.1 => P(X ≤ k) = 0.9
k
[1] 1.33058
3.(b) Generate 5000 observations from gamma(3,0.25) [seed 101] (i)
Estimate P(X ≥ 50) (ii) Estimate P(20 < X < 40) (iii) Find deciles
(10%, 20%, …, 90%)
set.seed(101)
x<-rgamma(5000, shape=3, scale=0.25)
#(i)
est_px50_ <- mean(x>=50)
est_px50_
[1] 0
#(ii)
est_px20_50 <- mean(x>20 & x<40)
est_px20_50
[1] 0
#(iii)
deciles <- quantile(x, probs = seq(0.1, 0.9, by = 0.1))
deciles
10% 20% 30% 40% 50% 60% 70%
0.2838650 0.3900256 0.4802828 0.5736065 0.6662751 0.7708639 0.9059050
80% 90%
1.0696102 1.3150904
Question 4
(a) Finding the minimum value of the function [ l(x)
= x^4 + 4x^3 + 2x^2 + 12x + 5] requires solving the transcendental
equation [ f(x) = 0] where [ f(x) = 4x^3 + 12x^2 + 4x + 12] and [ f’(x)
= 12x^2 + 24x + 4]
(i) Draw the functions ( l(x) ) and ( f(x) ) on the
same graph for the interval [ -4 < x < 1]
(ii) Write a function to find the solution of the
transcendental equation ( f(x) = 0 ). The function should return:
- the estimated solution,
- the value of the function at the estimated solution, and
- the number of iterations required.
(b) Write a function that sums the terms of the
given series.
Workflow for Question (a)
Step 1: Define the functions
- Define the original function [ l(x) = x^4 + 4x^3 + 2x^2 + 12x +
5]
- Define its first derivative [ f(x) = l’(x) = 4x^3 + 12x^2 + 4x +
12]
- Define the derivative of ( f(x) ) [ f’(x) = 12x^2 + 24x + 4]
Step 2: Visualization
Choose the interval ( -4 < x < 1 )
Plot:
Draw both curves on the same graph
Use the plot to understand where the minimum of ( l(x) ) may
occur
Step 3: Set up the optimization condition
- Recall that the minimum of ( l(x) ) occurs when: [ f(x) = 0]
- This equation cannot be solved easily by hand, so a
numerical method is required
Step 4: Write a root-finding function
Step 5: Output requirements
Your function must return:
- The estimated root of ( f(x) = 0 )
- The value of ( f(x) ) at the estimated root
- The number of iterations used
Workflow for Question (b)
Step 6: Series computation
One-line workflow summary
Define → Visualize → Differentiate → Solve numerically →
Report results → Compute series sum
# 4.(a) Draw functions l(x) and f(x) on interval -4 < x < 1
l_func <- function(x) x^4 + 4*x^3 + 2*x^2 + 12*x + 5
f_func <- function(x) 4*x^3 + 12*x^2 + 4*x + 12
f_prime <- function(x) 12*x^2 + 24*x + 4
# Create sequence for plotting
x_vals <- seq(-4, 1, length.out = 1000)
# Plot both functions
plot(x_vals, l_func(x_vals), type = "l", col = "blue",
xlab = "x", ylab = "Function Value",
main = "Functions l(x) and f(x)",
ylim = range(c(l_func(x_vals), f_func(x_vals))))
lines(x_vals, f_func(x_vals), col = "red")
abline(h = 0, lty = 2, col = "gray")
legend("topright", legend = c("l(x)", "f(x)"),
col = c("blue", "red"), lty = 1)

cat("4.(a) Plot created.\n\n")
4.(a) Plot created.
# 4.(a)(ii) Function to solve transcendental equation using Newton's method
solve_transcendental <- function(x0 = 0, tol = 1e-10, max_iter = 1000) {
x <- x0
iterations <- 0
for(i in 1:max_iter) {
fx <- f_func(x)
fpx <- f_prime(x)
# Newton update: x_new = x - f(x)/f'(x)
x_new <- x - fx / fpx
# Check convergence
if(abs(x_new - x) < tol && abs(fx) < tol) {
break
}
x <- x_new
iterations <- i
}
return(list(
solution = x,
function_value = f_func(x),
iterations = iterations
))
}
# Find solution (try different starting points)
result <- solve_transcendental(x0 = -2)
cat("4.(a)(ii) Solution using Newton's method:\n")
4.(a)(ii) Solution using Newton's method:
cat("Estimated solution:", result$solution, "\n")
Estimated solution: -3
cat("Function value at estimate:", result$function_value, "\n")
Function value at estimate: -6.583178e-12
cat("Number of iterations:", result$iterations, "\n\n")
Number of iterations: 8
# 4.(b) Function to sum series for log(1+x)
log_series <- function(x, t0 = 1e-10, max_terms = 1000) {
# Check validity: -1 < x < 1
if(x <= -1 || x >= 1) {
stop("x must be in the interval (-1, 1)")
}
term <- x
sum_series <- term
n <- 1
while(abs(term) >= t0 && n < max_terms) {
n <- n + 1
term <- (-1)^(n-1) * x^n / n
sum_series <- sum_series + term
}
return(list(
x = x,
series_sum = sum_series,
true_value = log(1 + x),
terms_used = n,
absolute_error = abs(sum_series - log(1 + x))
))
}
# Test the function
cat("4.(b) Testing log series function:\n")
4.(b) Testing log series function:
test_values <- c(0.2, -0.3, 0.9)
for(val in test_values) {
result <- tryCatch({
log_series(val)
}, error = function(e) {
list(x = val, error = e$message)
})
if("error" %in% names(result)) {
cat("x =", val, ":", result$error, "\n")
} else {
cat("x =", val, ":\n")
cat("Series sum:", result$series_sum, "\n")
cat("True log(1+x):", result$true_value, "\n")
cat("Terms used:", result$terms_used, "\n")
cat("Absolute error:", result$absolute_error, "\n")
}
}
x = 0.2 :
Series sum: 0.1823216
True log(1+x): 0.1823216
Terms used: 13
Absolute error: 9.863138e-12
x = -0.3 :
Series sum: -0.3566749
True log(1+x): -0.3566749
Terms used: 17
Absolute error: 3.008227e-11
x = 0.9 :
Series sum: 0.6418539
True log(1+x): 0.6418539
Terms used: 170
Absolute error: 4.623013e-11
- Use iris dataset to compute:
- means of the numeric columns
- the means of the numeric columns by Species.
# (i) Means of numeric columns
numeric_cols <- iris[, 1:4]
col_means <- colMeans(numeric_cols)
cat("(i) Means of numeric columns:\n")
(i) Means of numeric columns:
cat("Sepal Length:", col_means[1], "\n")
Sepal Length: 5.843333
cat("Sepal Width:", col_means[2], "\n")
Sepal Width: 3.057333
cat("Petal Length:", col_means[3], "\n")
Petal Length: 3.758
cat("Petal Width:", col_means[4], "\n\n")
Petal Width: 1.199333
# (ii) Means by Species
cat("(ii) Means by Species:\n")
(ii) Means by Species:
species_means <- aggregate(. ~ Species, data = iris, FUN = mean)
print(species_means)
print(iris)
# 5. Iris dataset analysis
# (i) Overall means
cat("(i) Overall means of numeric columns:\n")
(i) Overall means of numeric columns:
cat("-------------------------------------\n")
-------------------------------------
overall <- colMeans(iris[1:4])
names(overall) <- c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width")
print(overall)
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.843333 3.057333 3.758000 1.199333
# (ii) Means by Species
cat("(ii) Means by Species:\n")
(ii) Means by Species:
cat("----------------------\n")
----------------------
species <- c("setosa", "versicolor", "virginica")
for(sp in species) {
cat(sp, ":\n")
subset_means <- colMeans(iris[iris$Species == sp, 1:4]) #iris[rows, columns]
print(subset_means)
}
setosa :
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.006 3.428 1.462 0.246
versicolor :
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.936 2.770 4.260 1.326
virginica :
Sepal.Length Sepal.Width Petal.Length Petal.Width
6.588 2.974 5.552 2.026
2023
1.(a) Make 3 vectors: (no loops)
Vector A: (3, 3.1, …, 6)
Vector B: (2, 2²/2, 2³/3, …, 2²⁵/25)
Vector C: Repeat (4,6,3) to get 10 total 4s Sum each vector.
#A
A <- seq(3,6,0.1)
A
[1] 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 4.0 4.1 4.2 4.3 4.4 4.5 4.6
[18] 4.7 4.8 4.9 5.0 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 6.0
#B
n <- 1:25
B <- (2^n) / n
B
[1] 2.000000e+00 2.000000e+00 2.666667e+00 4.000000e+00 6.400000e+00
[6] 1.066667e+01 1.828571e+01 3.200000e+01 5.688889e+01 1.024000e+02
[11] 1.861818e+02 3.413333e+02 6.301538e+02 1.170286e+03 2.184533e+03
[16] 4.096000e+03 7.710118e+03 1.456356e+04 2.759411e+04 5.242880e+04
[21] 9.986438e+04 1.906502e+05 3.647221e+05 6.990507e+05 1.342177e+06
#C
C <- rep(c(4,6,3), 10)
C
[1] 4 6 3 4 6 3 4 6 3 4 6 3 4 6 3 4 6 3 4 6 3 4 6 3 4 6 3 4 6 3
1.(b) Generate xVec, yVec with seed 50. Calculate: [No loops] xVec
<- sample(0:999, 250, replace=T) yVec <- sample(0:999, 250,
replace=T) x,y belongs to X,Y i) Sum(y − x) ii) Root over (Average of
x²) iii) Sum of exp(−y_(i+1))/((x_i)+10) for i=1 to n−1
set.seed(50)
xVec <- sample(0:999, 250, replace=T)
yVec <- sample(0:999, 250, replace=T)
#(i)
ans_i <- sum(yVec-xVec)
ans_i
[1] -12273
#(ii)
ans_ii <- sqrt(mean(xVec^2))
ans_ii
[1] 600.7198
#(iii)
n <- length(xVec)
# Use 2:n for y (i+1) and 1:(n-1) for x (i)
ans_iii <- sum(exp(-yVec[2:n]) / (xVec[1:(n-1)] + 10))
ans_iii
[1] 0.004409429
2.(a) With seed 209: Generate X ~ Binomial(20, 0.5) and ε ~ N(0, 3),
100 each. Compute Y = 20 + 1.5X + ε. Save as x, y.
set.seed(209)
x <- rbinom(100, 20, 0.5)
ε <- rnorm(100, 0, 3)
y <- 20 + 1.5*x + ε
2.(b) Write function: input x, y → output matrix[a, b]ᵀ (col
matrix…not transpose). b = (Σxy − (ΣxΣy)/n)/(Σx2-((Σx)2)/n) a
= ybar - b*xbar Store as mat1. & Compare to [20, 1.5]ᵀ which is the
matrix of regression parameters of (a).
# --- Part 2.(b) ---
# Define the function
get_coefficients <- function(x, y) {
n <- length(x)
# Calculate necessary sums
sum_x <- sum(x)
sum_y <- sum(y)
sum_xy <- sum(x * y)
sum_x_sq <- sum(x^2)
# Calculate slope b using the provided formula
# Numerator: Σxy - (ΣxΣy)/n
numerator <- sum_xy - (sum_x * sum_y) / n
# Denominator: Σx^2 - (Σx)^2/n
denominator <- sum_x_sq - (sum_x^2) / n
b <- numerator / denominator
# Calculate intercept a
# a = ybar - b * xbar
a <- mean(y) - b * mean(x)
# Return as a column matrix (2 rows, 1 column)
return(matrix(c(a, b), nrow = 2, ncol = 1))
}
# Store the result as mat1
mat1 <- get_coefficients(x, y)
# Define the theoretical parameters [20, 1.5] for comparison
theoretical <- matrix(c(20, 1.5), nrow = 2, ncol = 1)
# Display the results
print("Estimated Matrix (mat1):")
[1] "Estimated Matrix (mat1):"
print(mat1)
[,1]
[1,] 20.695560
[2,] 1.457999
print("Theoretical Matrix:")
[1] "Theoretical Matrix:"
print(theoretical)
[,1]
[1,] 20.0
[2,] 1.5
print("Difference (Estimated - Theoretical):")
[1] "Difference (Estimated - Theoretical):"
print(mat1 - theoretical)
[,1]
[1,] 0.69556007
[2,] -0.04200109
2.(c) Create D = [1(column full of ones), x] and a column matrix M of
1000 rows using vector y. Compute (DᵀD)⁻¹DᵀM → store as mat2. Compare to
[20, 1.5]ᵀ.
D <- cbind(1, x) # First column = 1, second column = X
M <- matrix(rep(y, length.out = 1000), ncol = 1)
# Calculate (D^T D)^{-1} D^T M
# But D is 100x2, M is 1000x1 - dimensions don't match!
# This seems like an error. Let me assume M should be based on y (100x1)
M_correct <- matrix(y, ncol = 1) # 100x1 matrix
mat2 <- solve(t(D) %*% D) %*% t(D) %*% M_correct
cat("Estimated parameters (mat2):\n")
Estimated parameters (mat2):
print(mat2)
[,1]
20.695560
x 1.457999
cat("Difference from true parameters:\n")
Difference from true parameters:
print(mat2 - matrix(c(20, 1.5), nrow = 2, ncol = 1))
[,1]
0.69556007
x -0.04200109
2.(d) X <- matrix(c(0.63, 0.75, 1.53, -1.70, -0.93, 1.23, 0.80,
1.11, 2.84), nrow = 3, byrow = TRUE) Replace: Row 2 with (0.47, 2.33,
87) Row1Col3=63, Row3Col3=75 Find column means and SDs.
x <- matrix(c(0.63, 0.75, 1.53,
-1.70, -0.93, 1.23,
0.80, 1.11, 2.84), nrow = 3, byrow = TRUE)
x[2,] <- c(0.47, 2.33, 87)
x[1,3] = 63
x[3,3] = 75
x
[,1] [,2] [,3]
[1,] 0.63 0.75 63
[2,] 0.47 2.33 87
[3,] 0.80 1.11 75
col_means <- apply(x, 2, mean)
col_means
[1] 0.6333333 1.3966667 75.0000000
col_sds <- apply(x, 2, sd)
col_sds
[1] 0.1650253 0.8280902 12.0000000
- X ~ Poisson(λ=2.7). Find:
- P(X > 3)
- P(2 < X < 8)
- Smallest k: P(X ≤ k) ≥ 0.5
#(i)
px3_ <- 1-ppois(3,2.7)
px3_
[1] 0.2859078
#(ii)
px2_8 <- sum(dpois(3:7,2.7))
px2_8
[1] 0.4997543
#(iii)
k <- qpois(0.5, 2.7)
k
[1] 3
- Generate 3000 obs from Poisson(λ=1.5) with seed 101. Estimate:
- P(X > 3)
- P(2 < X < 8)
- p(x<=3)
set.seed(101)
sim_x <- rpois(3000, 1.5)
# (i) Estimate P(X > 3)
prob_i <- mean(sim_x > 3)
print(paste("Estimate for P(X > 3):", prob_i))
[1] "Estimate for P(X > 3): 0.071"
# (ii) Estimate P(2 < X < 8)
prob_ii <- mean(sim_x > 2 & sim_x < 8)
print(paste("Estimate for P(2 < X < 8):", prob_ii))
[1] "Estimate for P(2 < X < 8): 0.193666666666667"
# (iii) Estimate P(X <= 3)
prob_iii <- mean(sim_x <= 3)
print(paste("Estimate for P(X <= 3):", prob_iii))
[1] "Estimate for P(X <= 3): 0.929"
- X ~ Exponential(mean=1). Find:
- P(0.5 < X < 1.5)
- k such that P(X < k) = 0.8
px0.5_1.5 <- pexp(1.5, rate=1)-pexp(0.5, rate=1) #rate = 1/mean = 1/lambda
px0.5_1.5
[1] 0.3834005
k <- qexp(0.8, rate=1)
k
[1] 1.609438
- Generate 5000 obs from Exp(1) with seed 102. Estimate:
- P(0.5 < X < 1.5)
- P(X > 1.61)
set.seed(102)
# Note: Since mean = 1, rate = 1/mean = 1
sim_x_exp <- rexp(5000, rate = 1)
# (i) Estimate P(0.5 < X < 1.5)
prob_i_exp <- mean(sim_x_exp > 0.5 & sim_x_exp < 1.5)
print(paste("Estimate for P(0.5 < X < 1.5):", prob_i_exp))
[1] "Estimate for P(0.5 < X < 1.5): 0.3832"
# (ii) Estimate P(X > 1.61)
prob_ii_exp <- mean(sim_x_exp > 1.61)
print(paste("Estimate for P(X > 1.61):", prob_ii_exp))
[1] "Estimate for P(X > 1.61): 0.1924"
- Write function: Add two vectors by truncating longer to match
shorter length.
trunc_add <- function(v1, v2) {
n <- min(length(v1), length(v2))
return(v1[1:n] + v2[1:n])
}
- Write A.sort (sorts ascending) and own.med (finds median using
A.sort). With seed 200, generate 500 obs from N(10,10), find median
using own.med.
# 1. Define Functions
A.sort <- function(x) {
return(sort(x)) # Uses R's built-in sort (ascending)
}
own.med <- function(x) {
sorted_x <- A.sort(x)
n <- length(sorted_x)
if (n %% 2 == 1) {
return(sorted_x[(n + 1) / 2])
} else {
return(mean(sorted_x[c(n / 2, n / 2 + 1)]))
}
}
# 2. Generate Data and Test
set.seed(200)
# Assuming N(10, 10) means mean=10, sd=10 based on previous context
data <- rnorm(500, mean = 10, sd = 10)
result <- own.med(data)
print(paste("Median:", result))
[1] "Median: 10.1536202073796"
- Plot f(x)=x³−2x²−4. Use Newton-Raphson to solve f(x)=0. Function
inputs: x0, tolerance t0. Outputs: root, f(root), iteration count.
# 1. Define the function f(x) and its derivative df(x)
f <- function(x) x^3 - 2*x^2 - 4
df <- function(x) 3*x^2 - 4*x
# 2. Plot f(x)
curve(f, from = -2, to = 4, main = "Plot of f(x)")
abline(h = 0, col = "red") # Shows the x-axis

# 3. Newton-Raphson Function
newton_solve <- function(x0, t0) {
iter <- 0
x <- x0
while (abs(f(x)) > t0) {
x <- x - f(x) / df(x)
iter <- iter + 1
}
return(list(root = x, value = f(x), iterations = iter))
}
# Example Usage (Guessing 2)
newton_solve(2, 0.0001)
$root
[1] 2.594318
$value
[1] 4.882565e-05
$iterations
[1] 4
- In birthwt dataset: treat low, race, smoke, ht, ui as categorical.
Find mean of numeric columns.
# Load data
library(MASS)
data(birthwt)
birthwt$low <- factor(birthwt$low) #Factor converts numeric/text data into categories (groups).
birthwt$race <- factor(birthwt$race)
birthwt$smoke <- factor(birthwt$smoke)
birthwt$ht <- factor(birthwt$ht)
birthwt$ui <- factor(birthwt$ui)
numeric_cols <- c("age", "lwt", "bwt", "ptl", "ftv")
numeric_means <- colMeans(birthwt[numeric_cols])
cat("(a) Means of numeric columns:\n")
(a) Means of numeric columns:
print(numeric_means)
age lwt bwt ptl ftv
23.2380952 129.8148148 2944.5873016 0.1957672 0.7936508
- Write function: For bwt variable, compute mean and SD for each race
group.
bwt_stats_by_race <- function(data) {
races <- levels(data$race)
results <- data.frame()
for(r in races) {
bwt_values <- data$bwt[data$race == r]
mean_val <- mean(bwt_values)
sd_val <- sd(bwt_values)
results <- rbind(results, data.frame(Race = r, Mean = mean_val, SD = sd_val))
}
return(results)
}
cat("\n(b) BWT statistics by race:\n")
(b) BWT statistics by race:
print(bwt_stats_by_race(birthwt))
LS0tDQp0aXRsZTogIkZpbmFsIFllYXIgU29sdmUgMjQtMjMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KIyAyMDI0DQoNCjEuKGEpIENyZWF0ZSBhIHZlY3RvciB4ID0gKDEsMiwyLDMsMywzLDQsNCw0LDQsNSw1LDUsNSw1KSBieSB1c2luZyByZXAoKSBhbmQgZmluZCBtdWx0aXBsaWNhdGlvbiBvZiB4Lg0KYGBge3J9DQp4IDwtIHJlcCgxOjUsIGMoMSwyLDMsNCw1KSkNCngNCmBgYA0KMS4oYikgQ3JlYXRlIHZlY3RvciB5ID0gTiAqIGVeeCAvIHghLCB3aGVyZSBOID0gMTpsZW5ndGgoeCkuIEZpbmQgc3VtIG9mIHkuDQpgYGB7cn0NCk4gPC0gMTpsZW5ndGgoeCkNCnkgPC0gTipleHAoeCkvZmFjdG9yaWFsKHgpDQp5DQp5X3N1bSA8LSBzdW0oeSkNCnlfc3VtDQpgYGANCjEuKGMpIEZpbmQgaW5kZXggcG9zaXRpb25zIHdoZXJlIDEwIDwgeSA8IDE1LiBGaW5kIHRoZSBzdW0gb2YgdmFsdWVzLg0KYGBge3J9DQppbmRpY2VzIDwtIHdoaWNoKHk+MTAgJiB5PDE1KQ0KaW5kaWNlcw0KdmFsX3N1bSA8LSBzdW0oeVtpbmRpY2VzXSkNCnZhbF9zdW0NCmBgYA0KMS4oZCkgQ3JlYXRlIGludC55IGFzIG5lYXJlc3QgaW50ZWdlciBvZiB5LCBjb3VudCBkaXZpc2libGUgYnkgNA0KYGBge3J9DQppbnQueSA8LSByb3VuZCh5KQ0KaW50LnkNCm4gPC0gbGVuZ3RoKHdoaWNoKGludC55JSU0PT0wKSkNCm4NCmBgYA0KMS4oZSkgRnVuY3Rpb24gdG8gY3JlYXRlIHkgYW5kIGNhbGN1bGF0ZSBtZWFuDQpgYGB7cn0NCmNhbGNfbWVhbiA8LSBmdW5jdGlvbih4KXsNCiAgTiA8LSAxOmxlbmd0aCh4KQ0KICB5IDwtIE4qZXhwKHgpL2ZhY3RvcmlhbCh4KQ0KICBhbSA8LSBtZWFuKHkpDQogIGlmIChhbnkoeTw9MCkpew0KICAgIGdtIDwtIE5BDQogICAgaG0gPC0gTkENCiAgfSBlbHNlIHsNCiAgICBnbSA8LSBleHAobWVhbihsb2coeSkpKQ0KICAgIGhtIDwtIDEvbWVhbigxL3kpDQogIH0NCiAgcmV0dXJuKGxpc3QoQU09YW0sR009Z20sSE09aG0pKQ0KfQ0KDQpgYGANCjEuKGYpIEV2YWx1YXRlIHRoZSBmdW5jdGlvbiBmb3IgWC4gDQpgYGB7cn0NCm1lYW5zX3Jlc3VsdCA8LSBjYWxjX21lYW4oeCkNCmNhdCgiQXJpdGhtZXRpYyBtZWFuIDoiLCBtZWFuc19yZXN1bHQkQU0sICJcbiIpDQpjYXQoIkdlb21ldHJpYyBtZWFuICA6IiwgbWVhbnNfcmVzdWx0JEdNLCAiXG4iKQ0KY2F0KCJIYXJtb25pYyBtZWFuICAgOiIsIG1lYW5zX3Jlc3VsdCRITSwgIlxuIikNCmBgYA0KMi4oYSkgVXNpbmcgc2VlZCAxMDAsIGNyZWF0ZSB0aHJlZSBzdGRub3JtYWwgcmFuZG9tIHZlY3RvcnMgb2YgbGVuZ3RoIDEwMCAoeCwgeDEseDIpLg0KDQooaSkgQ3JlYXRlIHZlY3RvcnMgeSx6IFt5PXgreDEsIHo9eSt4Ml0gYW5kIA0KRXZhbHVhdGUgc3VtKCh4LXhiYXIpKHkteWJhcikpLCBzdW0oKHgteGJhcikoei16YmFyKSksIHN1bSgoeS15YmFyKSh6LXpiYXIpKQ0KDQooaWkpIGNyZWF0ZSBtYXRyaXggQSgxMDB4Mykgd2hlcmUgZWFjaCBjb2x1bW4gaXMgZmlsbGVkIGJ5IGVhY2ggdmVjdG9yIHgseSx6LiBCPShBXlQgQSkgdGhlbiBCXi0xID0gPyBGaW5kIHN1bSBvZiBkaWFnb25hbCBlbGVtZW50cyBvZiBCLg0KYGBge3J9DQojIChpKQ0Kc2V0LnNlZWQoMTAwKQ0KeCA8LSBybm9ybSgxMDApDQp4MSA8LSBybm9ybSgxMDApDQp4MiA8LSBybm9ybSgxMDApDQoNCnkgPC0geCt4MQ0KeiA8LSB5K3gyDQpzcHh5IDwtIHN1bSgoeC1tZWFuKHgpKSooeS1tZWFuKHkpKSkNCnNweHogPC0gc3VtKCh4LW1lYW4oeCkpKih6LW1lYW4oeikpKQ0Kc3B4eQ0Kc3B4eg0KIyAoaWkpDQpBIDwtIG1hdHJpeChjKHgseSx6KSwgYnlyb3cgPSBGQUxTRSwgbmNvbCA9IDMpDQpoZWFkKEEpDQpCIDwtIHQoQSkgJSolIEENCkINCm4gPC0gbnJvdyhCKQ0KZGlhZ19zdW0gPC0gMA0KIyBzdW0oZGlhZyhCKSkNCmZvciAoaSBpbiAxOm4pew0KICAgIGRpYWdfc3VtIDwtIGRpYWdfc3VtICsgQltpLGldDQp9DQpkaWFnX3N1bQ0KYGBgDQoyLihiKSBGdW5jdGlvbiB0byBjb3VudCBlbnRyaWVzID4gayBpbiBlYWNoIGNvbHVtbiBvZiBhIG1hdHJpeC4NCmBgYHtyfQ0KY291bnRfZ3QgPC0gZnVuY3Rpb24obWF0LCBrKSB7DQogIGFwcGx5KG1hdCwgMiwgZnVuY3Rpb24oY29sKSBzdW0oY29sID4gaykpDQp9DQpgYGANCjIuKGMpIEV2YWx1YXRlIHRoZSBmdW5jIHdpdGggKEEsMikNCmBgYHtyfQ0KY291bnRfZ3QoQSwyKQ0KYGBgDQozLihhKSBHYW1tYSBkaXN0cmlidXRpb24gY2FsY3VsYXRpb25zIChzaGFwZT0zLCBzY2FsZT0wLjI1KQ0KKGkpIFAoWCDiiaUgNTApDQooaWkpIFAoMjAgPCBYIDwgNDApDQooaWlpKSBGaW5kIGsgc3VjaCB0aGF0IFAoWCA+IGspID0gMC4xDQpgYGB7cn0NCiMoaSkNCnA1MF8gPC0gMS1wZ2FtbWEoNTAsIHNoYXBlPTMsIHNjYWxlPTAuMjUpDQpwNTBfDQojKGlpKQ0KcDIwXzQwIDwtIHBnYW1tYSg0MCwgc2hhcGU9Mywgc2NhbGU9MC4yNSktcGdhbW1hKDIwLCBzaGFwZT0zLCBzY2FsZT0wLjI1KQ0KcDIwXzQwDQojKGlpaSkNCmsgPC0gcWdhbW1hKDAuOSwgc2hhcGUgPSAzLCBzY2FsZSA9IDAuMjUpICAjIFNpbmNlIFAoWCA+IGspID0gMC4xID0+IFAoWCDiiaQgaykgPSAwLjkNCmsNCmBgYA0KMy4oYikgR2VuZXJhdGUgNTAwMCBvYnNlcnZhdGlvbnMgZnJvbSBnYW1tYSgzLDAuMjUpIFtzZWVkIDEwMV0NCihpKSBFc3RpbWF0ZSBQKFgg4omlIDUwKQ0KKGlpKSBFc3RpbWF0ZSBQKDIwIDwgWCA8IDQwKQ0KKGlpaSkgRmluZCBkZWNpbGVzICgxMCUsIDIwJSwgLi4uLCA5MCUpDQpgYGB7cn0NCnNldC5zZWVkKDEwMSkNCng8LXJnYW1tYSg1MDAwLCBzaGFwZT0zLCBzY2FsZT0wLjI1KQ0KIyhpKQ0KZXN0X3B4NTBfIDwtIG1lYW4oeD49NTApDQplc3RfcHg1MF8NCiMoaWkpDQplc3RfcHgyMF81MCA8LSBtZWFuKHg+MjAgJiB4PDQwKQ0KZXN0X3B4MjBfNTANCiMoaWlpKQ0KZGVjaWxlcyA8LSBxdWFudGlsZSh4LCBwcm9icyA9IHNlcSgwLjEsIDAuOSwgYnkgPSAwLjEpKQ0KZGVjaWxlcw0KYGBgDQotLS0NCg0KIyMjICoqUXVlc3Rpb24gNCoqDQoNCioqKGEpKiogRmluZGluZyB0aGUgbWluaW11bSB2YWx1ZSBvZiB0aGUgZnVuY3Rpb24NClsNCmwoeCkgPSB4XjQgKyA0eF4zICsgMnheMiArIDEyeCArIDUNCl0NCnJlcXVpcmVzIHNvbHZpbmcgdGhlIHRyYW5zY2VuZGVudGFsIGVxdWF0aW9uDQpbDQpmKHgpID0gMA0KXQ0Kd2hlcmUNClsNCmYoeCkgPSA0eF4zICsgMTJ4XjIgKyA0eCArIDEyDQpdDQphbmQNClsNCmYnKHgpID0gMTJ4XjIgKyAyNHggKyA0DQpdDQoNCioqKGkpKiogRHJhdyB0aGUgZnVuY3Rpb25zICggbCh4KSApIGFuZCAoIGYoeCkgKSBvbiB0aGUgc2FtZSBncmFwaCBmb3IgdGhlIGludGVydmFsDQpbDQotNCA8IHggPCAxDQpdDQoNCioqKGlpKSoqIFdyaXRlIGEgZnVuY3Rpb24gdG8gZmluZCB0aGUgc29sdXRpb24gb2YgdGhlIHRyYW5zY2VuZGVudGFsIGVxdWF0aW9uICggZih4KSA9IDAgKS4NClRoZSBmdW5jdGlvbiBzaG91bGQgcmV0dXJuOg0KDQoqIHRoZSBlc3RpbWF0ZWQgc29sdXRpb24sDQoqIHRoZSB2YWx1ZSBvZiB0aGUgZnVuY3Rpb24gYXQgdGhlIGVzdGltYXRlZCBzb2x1dGlvbiwgYW5kDQoqIHRoZSBudW1iZXIgb2YgaXRlcmF0aW9ucyByZXF1aXJlZC4NCg0KLS0tDQoNCioqKGIpKiogV3JpdGUgYSBmdW5jdGlvbiB0aGF0IHN1bXMgdGhlIHRlcm1zIG9mIHRoZSBnaXZlbiBzZXJpZXMuDQoNCi0tLQ0KDQotLS0NCg0KIyMgV29ya2Zsb3cgZm9yIFF1ZXN0aW9uIChhKQ0KDQojIyMgKipTdGVwIDE6IERlZmluZSB0aGUgZnVuY3Rpb25zKioNCg0KKiBEZWZpbmUgdGhlIG9yaWdpbmFsIGZ1bmN0aW9uDQogIFsNCiAgbCh4KSA9IHheNCArIDR4XjMgKyAyeF4yICsgMTJ4ICsgNQ0KICBdDQoqIERlZmluZSBpdHMgZmlyc3QgZGVyaXZhdGl2ZQ0KICBbDQogIGYoeCkgPSBsJyh4KSA9IDR4XjMgKyAxMnheMiArIDR4ICsgMTINCiAgXQ0KKiBEZWZpbmUgdGhlIGRlcml2YXRpdmUgb2YgKCBmKHgpICkNCiAgWw0KICBmJyh4KSA9IDEyeF4yICsgMjR4ICsgNA0KICBdDQoNCi0tLQ0KDQojIyMgKipTdGVwIDI6IFZpc3VhbGl6YXRpb24qKg0KDQoqIENob29zZSB0aGUgaW50ZXJ2YWwgKCAtNCA8IHggPCAxICkNCiogUGxvdDoNCg0KICAqICggbCh4KSApDQogICogKCBmKHgpICkNCiogRHJhdyBib3RoIGN1cnZlcyBvbiB0aGUgKipzYW1lIGdyYXBoKioNCiogVXNlIHRoZSBwbG90IHRvIHVuZGVyc3RhbmQgd2hlcmUgdGhlIG1pbmltdW0gb2YgKCBsKHgpICkgbWF5IG9jY3VyDQoNCi0tLQ0KDQojIyMgKipTdGVwIDM6IFNldCB1cCB0aGUgb3B0aW1pemF0aW9uIGNvbmRpdGlvbioqDQoNCiogUmVjYWxsIHRoYXQgdGhlIG1pbmltdW0gb2YgKCBsKHgpICkgb2NjdXJzIHdoZW46DQogIFsNCiAgZih4KSA9IDANCiAgXQ0KKiBUaGlzIGVxdWF0aW9uIGNhbm5vdCBiZSBzb2x2ZWQgZWFzaWx5IGJ5IGhhbmQsIHNvIGEgKipudW1lcmljYWwgbWV0aG9kKiogaXMgcmVxdWlyZWQNCg0KLS0tDQoNCiMjIyAqKlN0ZXAgNDogV3JpdGUgYSByb290LWZpbmRpbmcgZnVuY3Rpb24qKg0KDQoqIFdyaXRlIGEgZnVuY3Rpb24gdGhhdCBudW1lcmljYWxseSBzb2x2ZXM6DQogIFsNCiAgZih4KSA9IDANCiAgXQ0KKiBUaGUgZnVuY3Rpb24gc2hvdWxkOg0KDQogIDEuIFN0YXJ0IGZyb20gYW4gaW5pdGlhbCBndWVzcw0KICAyLiBJdGVyYXRpdmVseSB1cGRhdGUgdGhlIGVzdGltYXRlDQogIDMuIFN0b3Agd2hlbiBjb252ZXJnZW5jZSBpcyByZWFjaGVkDQoNCi0tLQ0KDQojIyMgKipTdGVwIDU6IE91dHB1dCByZXF1aXJlbWVudHMqKg0KDQpZb3VyIGZ1bmN0aW9uIG11c3QgcmV0dXJuOg0KDQoqIFRoZSAqKmVzdGltYXRlZCByb290Kiogb2YgKCBmKHgpID0gMCApDQoqIFRoZSAqKnZhbHVlIG9mICggZih4KSApKiogYXQgdGhlIGVzdGltYXRlZCByb290DQoqIFRoZSAqKm51bWJlciBvZiBpdGVyYXRpb25zKiogdXNlZA0KDQotLS0NCg0KIyMgV29ya2Zsb3cgZm9yIFF1ZXN0aW9uIChiKQ0KDQojIyMgKipTdGVwIDY6IFNlcmllcyBjb21wdXRhdGlvbioqDQoNCiogSWRlbnRpZnkgdGhlIGdpdmVuIHNlcmllcw0KKiBXcml0ZSBhIGZ1bmN0aW9uIHRoYXQ6DQoNCiAgKiBDb21wdXRlcyB0aGUgc3VtIG9mIHRoZSBzZXJpZXMNCiAgKiBVc2VzIGEgc3BlY2lmaWVkIG51bWJlciBvZiB0ZXJtcyBvciBhIHN0b3BwaW5nIHJ1bGUNCiogUmV0dXJuIHRoZSBmaW5hbCBzdW0NCg0KLS0tDQoNCiMjIE9uZS1saW5lIHdvcmtmbG93IHN1bW1hcnkNCg0KPiAqKkRlZmluZSDihpIgVmlzdWFsaXplIOKGkiBEaWZmZXJlbnRpYXRlIOKGkiBTb2x2ZSBudW1lcmljYWxseSDihpIgUmVwb3J0IHJlc3VsdHMg4oaSIENvbXB1dGUgc2VyaWVzIHN1bSoqDQoNCi0tLQ0KYGBge3J9DQojIDQuKGEpIERyYXcgZnVuY3Rpb25zIGwoeCkgYW5kIGYoeCkgb24gaW50ZXJ2YWwgLTQgPCB4IDwgMQ0KbF9mdW5jIDwtIGZ1bmN0aW9uKHgpIHheNCArIDQqeF4zICsgMip4XjIgKyAxMip4ICsgNQ0KZl9mdW5jIDwtIGZ1bmN0aW9uKHgpIDQqeF4zICsgMTIqeF4yICsgNCp4ICsgMTINCmZfcHJpbWUgPC0gZnVuY3Rpb24oeCkgMTIqeF4yICsgMjQqeCArIDQNCg0KIyBDcmVhdGUgc2VxdWVuY2UgZm9yIHBsb3R0aW5nDQp4X3ZhbHMgPC0gc2VxKC00LCAxLCBsZW5ndGgub3V0ID0gMTAwMCkNCg0KIyBQbG90IGJvdGggZnVuY3Rpb25zDQpwbG90KHhfdmFscywgbF9mdW5jKHhfdmFscyksIHR5cGUgPSAibCIsIGNvbCA9ICJibHVlIiwgDQogICAgIHhsYWIgPSAieCIsIHlsYWIgPSAiRnVuY3Rpb24gVmFsdWUiLA0KICAgICBtYWluID0gIkZ1bmN0aW9ucyBsKHgpIGFuZCBmKHgpIiwNCiAgICAgeWxpbSA9IHJhbmdlKGMobF9mdW5jKHhfdmFscyksIGZfZnVuYyh4X3ZhbHMpKSkpDQpsaW5lcyh4X3ZhbHMsIGZfZnVuYyh4X3ZhbHMpLCBjb2wgPSAicmVkIikNCmFibGluZShoID0gMCwgbHR5ID0gMiwgY29sID0gImdyYXkiKQ0KbGVnZW5kKCJ0b3ByaWdodCIsIGxlZ2VuZCA9IGMoImwoeCkiLCAiZih4KSIpLCANCiAgICAgICBjb2wgPSBjKCJibHVlIiwgInJlZCIpLCBsdHkgPSAxKQ0KY2F0KCI0LihhKSBQbG90IGNyZWF0ZWQuXG5cbiIpDQoNCiMgNC4oYSkoaWkpIEZ1bmN0aW9uIHRvIHNvbHZlIHRyYW5zY2VuZGVudGFsIGVxdWF0aW9uIHVzaW5nIE5ld3RvbidzIG1ldGhvZA0Kc29sdmVfdHJhbnNjZW5kZW50YWwgPC0gZnVuY3Rpb24oeDAgPSAwLCB0b2wgPSAxZS0xMCwgbWF4X2l0ZXIgPSAxMDAwKSB7DQogIHggPC0geDANCiAgaXRlcmF0aW9ucyA8LSAwDQogIA0KICBmb3IoaSBpbiAxOm1heF9pdGVyKSB7DQogICAgZnggPC0gZl9mdW5jKHgpDQogICAgZnB4IDwtIGZfcHJpbWUoeCkNCiAgICANCiAgICAjIE5ld3RvbiB1cGRhdGU6IHhfbmV3ID0geCAtIGYoeCkvZicoeCkNCiAgICB4X25ldyA8LSB4IC0gZnggLyBmcHgNCiAgICANCiAgICAjIENoZWNrIGNvbnZlcmdlbmNlDQogICAgaWYoYWJzKHhfbmV3IC0geCkgPCB0b2wgJiYgYWJzKGZ4KSA8IHRvbCkgew0KICAgICAgYnJlYWsNCiAgICB9DQogICAgDQogICAgeCA8LSB4X25ldw0KICAgIGl0ZXJhdGlvbnMgPC0gaQ0KICB9DQogIA0KICByZXR1cm4obGlzdCgNCiAgICBzb2x1dGlvbiA9IHgsDQogICAgZnVuY3Rpb25fdmFsdWUgPSBmX2Z1bmMoeCksDQogICAgaXRlcmF0aW9ucyA9IGl0ZXJhdGlvbnMNCiAgKSkNCn0NCg0KIyBGaW5kIHNvbHV0aW9uICh0cnkgZGlmZmVyZW50IHN0YXJ0aW5nIHBvaW50cykNCnJlc3VsdCA8LSBzb2x2ZV90cmFuc2NlbmRlbnRhbCh4MCA9IC0yKQ0KY2F0KCI0LihhKShpaSkgU29sdXRpb24gdXNpbmcgTmV3dG9uJ3MgbWV0aG9kOlxuIikNCmNhdCgiRXN0aW1hdGVkIHNvbHV0aW9uOiIsIHJlc3VsdCRzb2x1dGlvbiwgIlxuIikNCmNhdCgiRnVuY3Rpb24gdmFsdWUgYXQgZXN0aW1hdGU6IiwgcmVzdWx0JGZ1bmN0aW9uX3ZhbHVlLCAiXG4iKQ0KY2F0KCJOdW1iZXIgb2YgaXRlcmF0aW9uczoiLCByZXN1bHQkaXRlcmF0aW9ucywgIlxuXG4iKQ0KDQojIDQuKGIpIEZ1bmN0aW9uIHRvIHN1bSBzZXJpZXMgZm9yIGxvZygxK3gpDQpsb2dfc2VyaWVzIDwtIGZ1bmN0aW9uKHgsIHQwID0gMWUtMTAsIG1heF90ZXJtcyA9IDEwMDApIHsNCiAgIyBDaGVjayB2YWxpZGl0eTogLTEgPCB4IDwgMQ0KICBpZih4IDw9IC0xIHx8IHggPj0gMSkgew0KICAgIHN0b3AoInggbXVzdCBiZSBpbiB0aGUgaW50ZXJ2YWwgKC0xLCAxKSIpDQogIH0NCiAgDQogIHRlcm0gPC0geA0KICBzdW1fc2VyaWVzIDwtIHRlcm0NCiAgbiA8LSAxDQogIA0KICB3aGlsZShhYnModGVybSkgPj0gdDAgJiYgbiA8IG1heF90ZXJtcykgew0KICAgIG4gPC0gbiArIDENCiAgICB0ZXJtIDwtICgtMSleKG4tMSkgKiB4Xm4gLyBuDQogICAgc3VtX3NlcmllcyA8LSBzdW1fc2VyaWVzICsgdGVybQ0KICB9DQogIA0KICByZXR1cm4obGlzdCgNCiAgICB4ID0geCwNCiAgICBzZXJpZXNfc3VtID0gc3VtX3NlcmllcywNCiAgICB0cnVlX3ZhbHVlID0gbG9nKDEgKyB4KSwNCiAgICB0ZXJtc191c2VkID0gbiwNCiAgICBhYnNvbHV0ZV9lcnJvciA9IGFicyhzdW1fc2VyaWVzIC0gbG9nKDEgKyB4KSkNCiAgKSkNCn0NCg0KIyBUZXN0IHRoZSBmdW5jdGlvbg0KY2F0KCI0LihiKSBUZXN0aW5nIGxvZyBzZXJpZXMgZnVuY3Rpb246XG4iKQ0KdGVzdF92YWx1ZXMgPC0gYygwLjIsIC0wLjMsIDAuOSkNCg0KZm9yKHZhbCBpbiB0ZXN0X3ZhbHVlcykgew0KICByZXN1bHQgPC0gdHJ5Q2F0Y2goew0KICAgIGxvZ19zZXJpZXModmFsKQ0KICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiAgICBsaXN0KHggPSB2YWwsIGVycm9yID0gZSRtZXNzYWdlKQ0KICB9KQ0KICANCiAgaWYoImVycm9yIiAlaW4lIG5hbWVzKHJlc3VsdCkpIHsNCiAgICBjYXQoInggPSIsIHZhbCwgIjoiLCByZXN1bHQkZXJyb3IsICJcbiIpDQogIH0gZWxzZSB7DQogICAgY2F0KCJ4ID0iLCB2YWwsICI6XG4iKQ0KICAgIGNhdCgiU2VyaWVzIHN1bToiLCByZXN1bHQkc2VyaWVzX3N1bSwgIlxuIikNCiAgICBjYXQoIlRydWUgbG9nKDEreCk6IiwgcmVzdWx0JHRydWVfdmFsdWUsICJcbiIpDQogICAgY2F0KCJUZXJtcyB1c2VkOiIsIHJlc3VsdCR0ZXJtc191c2VkLCAiXG4iKQ0KICAgIGNhdCgiQWJzb2x1dGUgZXJyb3I6IiwgcmVzdWx0JGFic29sdXRlX2Vycm9yLCAiXG4iKQ0KICB9DQp9DQpgYGANCjUuIFVzZSBpcmlzIGRhdGFzZXQgdG8gY29tcHV0ZToNCihpKSBtZWFucyBvZiB0aGUgbnVtZXJpYyBjb2x1bW5zDQooaWkpIHRoZSBtZWFucyBvZiB0aGUgbnVtZXJpYyBjb2x1bW5zIGJ5IFNwZWNpZXMuDQpgYGB7cn0NCiMgKGkpIE1lYW5zIG9mIG51bWVyaWMgY29sdW1ucw0KbnVtZXJpY19jb2xzIDwtIGlyaXNbLCAxOjRdDQpjb2xfbWVhbnMgPC0gY29sTWVhbnMobnVtZXJpY19jb2xzKQ0KY2F0KCIoaSkgTWVhbnMgb2YgbnVtZXJpYyBjb2x1bW5zOlxuIikNCmNhdCgiU2VwYWwgTGVuZ3RoOiIsIGNvbF9tZWFuc1sxXSwgIlxuIikNCmNhdCgiU2VwYWwgV2lkdGg6IiwgY29sX21lYW5zWzJdLCAiXG4iKQ0KY2F0KCJQZXRhbCBMZW5ndGg6IiwgY29sX21lYW5zWzNdLCAiXG4iKQ0KY2F0KCJQZXRhbCBXaWR0aDoiLCBjb2xfbWVhbnNbNF0sICJcblxuIikNCg0KIyAoaWkpIE1lYW5zIGJ5IFNwZWNpZXMNCmNhdCgiKGlpKSBNZWFucyBieSBTcGVjaWVzOlxuIikNCnNwZWNpZXNfbWVhbnMgPC0gYWdncmVnYXRlKC4gfiBTcGVjaWVzLCBkYXRhID0gaXJpcywgRlVOID0gbWVhbikNCnByaW50KHNwZWNpZXNfbWVhbnMpDQpgYGANCmBgYHtyfQ0KcHJpbnQoaXJpcykNCmBgYA0KDQpgYGB7cn0NCiMgNS4gSXJpcyBkYXRhc2V0IGFuYWx5c2lzDQojIChpKSBPdmVyYWxsIG1lYW5zDQpjYXQoIihpKSBPdmVyYWxsIG1lYW5zIG9mIG51bWVyaWMgY29sdW1uczpcbiIpDQpjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQpvdmVyYWxsIDwtIGNvbE1lYW5zKGlyaXNbMTo0XSkNCm5hbWVzKG92ZXJhbGwpIDwtIGMoIlNlcGFsLkxlbmd0aCIsICJTZXBhbC5XaWR0aCIsICJQZXRhbC5MZW5ndGgiLCAiUGV0YWwuV2lkdGgiKQ0KcHJpbnQob3ZlcmFsbCkNCg0KIyAoaWkpIE1lYW5zIGJ5IFNwZWNpZXMNCmNhdCgiKGlpKSBNZWFucyBieSBTcGVjaWVzOlxuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCnNwZWNpZXMgPC0gYygic2V0b3NhIiwgInZlcnNpY29sb3IiLCAidmlyZ2luaWNhIikNCg0KZm9yKHNwIGluIHNwZWNpZXMpIHsNCiAgY2F0KHNwLCAiOlxuIikNCiAgc3Vic2V0X21lYW5zIDwtIGNvbE1lYW5zKGlyaXNbaXJpcyRTcGVjaWVzID09IHNwLCAxOjRdKSAjaXJpc1tyb3dzLCBjb2x1bW5zXQ0KICBwcmludChzdWJzZXRfbWVhbnMpDQp9DQpgYGANCiMgMjAyMw0KDQoxLihhKSBNYWtlIDMgdmVjdG9yczogKG5vIGxvb3BzKQ0KDQpWZWN0b3IgQTogKDMsIDMuMSwgLi4uLCA2KQ0KDQpWZWN0b3IgQjogKDIsIDLCsi8yLCAywrMvMywg4oCmLCAywrLigbUvMjUpDQoNClZlY3RvciBDOiBSZXBlYXQgKDQsNiwzKSB0byBnZXQgMTAgdG90YWwgNHMNClN1bSBlYWNoIHZlY3Rvci4NCmBgYHtyfQ0KI0ENCkEgPC0gc2VxKDMsNiwwLjEpDQpBDQojQg0KbiA8LSAxOjI1DQpCIDwtICgyXm4pIC8gbg0KQg0KI0MNCkMgPC0gcmVwKGMoNCw2LDMpLCAxMCkNCkMNCmBgYA0KMS4oYikgR2VuZXJhdGUgeFZlYywgeVZlYyB3aXRoIHNlZWQgNTAuIENhbGN1bGF0ZTogW05vIGxvb3BzXQ0KeFZlYyA8LSBzYW1wbGUoMDo5OTksIDI1MCwgcmVwbGFjZT1UKQ0KeVZlYyA8LSBzYW1wbGUoMDo5OTksIDI1MCwgcmVwbGFjZT1UKQ0KeCx5IGJlbG9uZ3MgdG8gWCxZDQppKSBTdW0oeSDiiJIgeCkNCmlpKSBSb290IG92ZXIgKEF2ZXJhZ2Ugb2YgeMKyKQ0KaWlpKSBTdW0gb2YgZXhwKOKIknlfKGkrMSkpLygoeF9pKSsxMCkgZm9yIGk9MSB0byBu4oiSMQ0KYGBge3J9DQpzZXQuc2VlZCg1MCkNCnhWZWMgPC0gc2FtcGxlKDA6OTk5LCAyNTAsIHJlcGxhY2U9VCkNCnlWZWMgPC0gc2FtcGxlKDA6OTk5LCAyNTAsIHJlcGxhY2U9VCkNCiMoaSkNCmFuc19pIDwtIHN1bSh5VmVjLXhWZWMpDQphbnNfaQ0KIyhpaSkNCmFuc19paSA8LSBzcXJ0KG1lYW4oeFZlY14yKSkNCmFuc19paQ0KIyhpaWkpDQpuIDwtIGxlbmd0aCh4VmVjKQ0KIyBVc2UgMjpuIGZvciB5IChpKzEpIGFuZCAxOihuLTEpIGZvciB4IChpKQ0KYW5zX2lpaSA8LSBzdW0oZXhwKC15VmVjWzI6bl0pIC8gKHhWZWNbMToobi0xKV0gKyAxMCkpDQphbnNfaWlpDQpgYGANCjIuKGEpIFdpdGggc2VlZCAyMDk6DQpHZW5lcmF0ZSBYIH4gQmlub21pYWwoMjAsIDAuNSkgYW5kIM61IH4gTigwLCAzKSwgMTAwIGVhY2guDQpDb21wdXRlIFkgPSAyMCArIDEuNVggKyDOtS4gU2F2ZSBhcyB4LCB5Lg0KYGBge3J9DQpzZXQuc2VlZCgyMDkpDQp4IDwtIHJiaW5vbSgxMDAsIDIwLCAwLjUpDQrOtSA8LSBybm9ybSgxMDAsIDAsIDMpDQp5IDwtIDIwICsgMS41KnggKyDOtQ0KYGBgDQoyLihiKSANCldyaXRlIGZ1bmN0aW9uOiBpbnB1dCB4LCB5IOKGkiBvdXRwdXQgbWF0cml4W2EsIGJd4bWADQooY29sIG1hdHJpeC4uLm5vdCB0cmFuc3Bvc2UpLg0KYiA9ICjOo3h5IOKIkiAozqN4zqN5KS9uKS8ozqN4XjItKCjOo3gpXjIpL24pDQphID0geWJhciAtIGIqeGJhcg0KU3RvcmUgYXMgbWF0MS4gJiBDb21wYXJlIHRvIFsyMCwgMS41XeG1gCANCndoaWNoIGlzIHRoZSBtYXRyaXggb2YgcmVncmVzc2lvbiBwYXJhbWV0ZXJzIG9mIChhKS4NCmBgYHtyfQ0KIyAtLS0gUGFydCAyLihiKSAtLS0NCiMgRGVmaW5lIHRoZSBmdW5jdGlvbg0KZ2V0X2NvZWZmaWNpZW50cyA8LSBmdW5jdGlvbih4LCB5KSB7DQogIG4gPC0gbGVuZ3RoKHgpDQogICMgQ2FsY3VsYXRlIG5lY2Vzc2FyeSBzdW1zDQogIHN1bV94IDwtIHN1bSh4KQ0KICBzdW1feSA8LSBzdW0oeSkNCiAgc3VtX3h5IDwtIHN1bSh4ICogeSkNCiAgc3VtX3hfc3EgPC0gc3VtKHheMikNCiAgIyBDYWxjdWxhdGUgc2xvcGUgYiB1c2luZyB0aGUgcHJvdmlkZWQgZm9ybXVsYQ0KICAjIE51bWVyYXRvcjogzqN4eSAtICjOo3jOo3kpL24NCiAgbnVtZXJhdG9yIDwtIHN1bV94eSAtIChzdW1feCAqIHN1bV95KSAvIG4NCiAgIyBEZW5vbWluYXRvcjogzqN4XjIgLSAozqN4KV4yL24NCiAgZGVub21pbmF0b3IgPC0gc3VtX3hfc3EgLSAoc3VtX3heMikgLyBuDQogIGIgPC0gbnVtZXJhdG9yIC8gZGVub21pbmF0b3INCiAgIyBDYWxjdWxhdGUgaW50ZXJjZXB0IGENCiAgIyBhID0geWJhciAtIGIgKiB4YmFyDQogIGEgPC0gbWVhbih5KSAtIGIgKiBtZWFuKHgpDQogICMgUmV0dXJuIGFzIGEgY29sdW1uIG1hdHJpeCAoMiByb3dzLCAxIGNvbHVtbikNCiAgcmV0dXJuKG1hdHJpeChjKGEsIGIpLCBucm93ID0gMiwgbmNvbCA9IDEpKQ0KfQ0KDQojIFN0b3JlIHRoZSByZXN1bHQgYXMgbWF0MQ0KbWF0MSA8LSBnZXRfY29lZmZpY2llbnRzKHgsIHkpDQoNCiMgRGVmaW5lIHRoZSB0aGVvcmV0aWNhbCBwYXJhbWV0ZXJzIFsyMCwgMS41XSBmb3IgY29tcGFyaXNvbg0KdGhlb3JldGljYWwgPC0gbWF0cml4KGMoMjAsIDEuNSksIG5yb3cgPSAyLCBuY29sID0gMSkNCg0KIyBEaXNwbGF5IHRoZSByZXN1bHRzDQpwcmludCgiRXN0aW1hdGVkIE1hdHJpeCAobWF0MSk6IikNCnByaW50KG1hdDEpDQoNCnByaW50KCJUaGVvcmV0aWNhbCBNYXRyaXg6IikNCnByaW50KHRoZW9yZXRpY2FsKQ0KDQpwcmludCgiRGlmZmVyZW5jZSAoRXN0aW1hdGVkIC0gVGhlb3JldGljYWwpOiIpDQpwcmludChtYXQxIC0gdGhlb3JldGljYWwpDQpgYGANCjIuKGMpIENyZWF0ZSBEID0gWzEoY29sdW1uIGZ1bGwgb2Ygb25lcyksIHhdIGFuZCBhIGNvbHVtbiBtYXRyaXggTSBvZiAxMDAwIHJvd3MgdXNpbmcgdmVjdG9yIHkuIENvbXB1dGUgKEThtYBEKeKBu8K5ROG1gE0g4oaSIHN0b3JlIGFzIG1hdDIuIENvbXBhcmUgdG8gWzIwLCAxLjVd4bWALg0KYGBge3J9DQpEIDwtIGNiaW5kKDEsIHgpICAjIEZpcnN0IGNvbHVtbiA9IDEsIHNlY29uZCBjb2x1bW4gPSBYDQpNIDwtIG1hdHJpeChyZXAoeSwgbGVuZ3RoLm91dCA9IDEwMDApLCBuY29sID0gMSkNCg0KIyBDYWxjdWxhdGUgKEReVCBEKV57LTF9IEReVCBNDQojIEJ1dCBEIGlzIDEwMHgyLCBNIGlzIDEwMDB4MSAtIGRpbWVuc2lvbnMgZG9uJ3QgbWF0Y2ghDQojIFRoaXMgc2VlbXMgbGlrZSBhbiBlcnJvci4gTGV0IG1lIGFzc3VtZSBNIHNob3VsZCBiZSBiYXNlZCBvbiB5ICgxMDB4MSkNCk1fY29ycmVjdCA8LSBtYXRyaXgoeSwgbmNvbCA9IDEpICAjIDEwMHgxIG1hdHJpeA0KDQptYXQyIDwtIHNvbHZlKHQoRCkgJSolIEQpICUqJSB0KEQpICUqJSBNX2NvcnJlY3QNCmNhdCgiRXN0aW1hdGVkIHBhcmFtZXRlcnMgKG1hdDIpOlxuIikNCnByaW50KG1hdDIpDQpjYXQoIkRpZmZlcmVuY2UgZnJvbSB0cnVlIHBhcmFtZXRlcnM6XG4iKQ0KcHJpbnQobWF0MiAtIG1hdHJpeChjKDIwLCAxLjUpLCBucm93ID0gMiwgbmNvbCA9IDEpKQ0KYGBgDQoyLihkKSANClggPC0gbWF0cml4KGMoMC42MywgMC43NSwgMS41MywNCiAgICAgICAgICAgICAgICAgICAgLTEuNzAsIC0wLjkzLCAxLjIzLA0KICAgICAgICAgICAgICAgICAgICAgMC44MCwgMS4xMSwgMi44NCksIG5yb3cgPSAzLCBieXJvdyA9IFRSVUUpDQpSZXBsYWNlOg0KUm93IDIgd2l0aCAoMC40NywgMi4zMywgODcpDQpSb3cxQ29sMz02MywgDQpSb3czQ29sMz03NQ0KRmluZCBjb2x1bW4gbWVhbnMgYW5kIFNEcy4NCmBgYHtyfQ0KeCA8LSBtYXRyaXgoYygwLjYzLCAwLjc1LCAxLjUzLA0KICAgICAgICAgICAgICAgICAgICAtMS43MCwgLTAuOTMsIDEuMjMsDQogICAgICAgICAgICAgICAgICAgICAwLjgwLCAxLjExLCAyLjg0KSwgbnJvdyA9IDMsIGJ5cm93ID0gVFJVRSkNCnhbMixdIDwtIGMoMC40NywgMi4zMywgODcpDQp4WzEsM10gPSA2Mw0KeFszLDNdID0gNzUNCngNCmNvbF9tZWFucyA8LSBhcHBseSh4LCAyLCBtZWFuKQ0KY29sX21lYW5zDQpjb2xfc2RzIDwtIGFwcGx5KHgsIDIsIHNkKQ0KY29sX3Nkcw0KYGBgDQozLg0KKGEpIFggfiBQb2lzc29uKM67PTIuNykuIEZpbmQ6DQppKSBQKFggPiAzKQ0KaWkpIFAoMiA8IFggPCA4KQ0KaWlpKSBTbWFsbGVzdCBrOiBQKFgg4omkIGspIOKJpSAwLjUNCmBgYHtyfQ0KIyhpKQ0KcHgzXyA8LSAxLXBwb2lzKDMsMi43KQ0KcHgzXw0KIyhpaSkNCnB4Ml84IDwtIHN1bShkcG9pcygzOjcsMi43KSkNCnB4Ml84DQojKGlpaSkNCmsgPC0gcXBvaXMoMC41LCAyLjcpDQprDQpgYGANCihiKSBHZW5lcmF0ZSAzMDAwIG9icyBmcm9tIFBvaXNzb24ozrs9MS41KSB3aXRoIHNlZWQgMTAxLiANCkVzdGltYXRlOiANCmkpIFAoWCA+IDMpDQppaSkgUCgyIDwgWCA8IDgpDQppaWkpIHAoeDw9MykNCmBgYHtyfQ0Kc2V0LnNlZWQoMTAxKQ0Kc2ltX3ggPC0gcnBvaXMoMzAwMCwgMS41KQ0KDQojIChpKSBFc3RpbWF0ZSBQKFggPiAzKQ0KcHJvYl9pIDwtIG1lYW4oc2ltX3ggPiAzKQ0KcHJpbnQocGFzdGUoIkVzdGltYXRlIGZvciBQKFggPiAzKToiLCBwcm9iX2kpKQ0KIyAoaWkpIEVzdGltYXRlIFAoMiA8IFggPCA4KQ0KcHJvYl9paSA8LSBtZWFuKHNpbV94ID4gMiAmIHNpbV94IDwgOCkNCnByaW50KHBhc3RlKCJFc3RpbWF0ZSBmb3IgUCgyIDwgWCA8IDgpOiIsIHByb2JfaWkpKQ0KIyAoaWlpKSBFc3RpbWF0ZSBQKFggPD0gMykNCnByb2JfaWlpIDwtIG1lYW4oc2ltX3ggPD0gMykNCnByaW50KHBhc3RlKCJFc3RpbWF0ZSBmb3IgUChYIDw9IDMpOiIsIHByb2JfaWlpKSkNCmBgYA0KKGMpIFggfiBFeHBvbmVudGlhbChtZWFuPTEpLiBGaW5kOg0KaSkgUCgwLjUgPCBYIDwgMS41KQ0KaWkpIGsgc3VjaCB0aGF0IFAoWCA8IGspID0gMC44DQpgYGB7cn0NCnB4MC41XzEuNSA8LSBwZXhwKDEuNSwgcmF0ZT0xKS1wZXhwKDAuNSwgcmF0ZT0xKSAjcmF0ZSA9IDEvbWVhbiA9IDEvbGFtYmRhDQpweDAuNV8xLjUNCmsgPC0gcWV4cCgwLjgsIHJhdGU9MSkNCmsNCmBgYA0KKGQpIEdlbmVyYXRlIDUwMDAgb2JzIGZyb20gRXhwKDEpIHdpdGggc2VlZCAxMDIuIA0KRXN0aW1hdGU6DQppKSBQKDAuNSA8IFggPCAxLjUpDQppaSkgUChYID4gMS42MSkNCmBgYHtyfQ0Kc2V0LnNlZWQoMTAyKQ0KIyBOb3RlOiBTaW5jZSBtZWFuID0gMSwgcmF0ZSA9IDEvbWVhbiA9IDENCnNpbV94X2V4cCA8LSByZXhwKDUwMDAsIHJhdGUgPSAxKQ0KIyAoaSkgRXN0aW1hdGUgUCgwLjUgPCBYIDwgMS41KQ0KcHJvYl9pX2V4cCA8LSBtZWFuKHNpbV94X2V4cCA+IDAuNSAmIHNpbV94X2V4cCA8IDEuNSkNCnByaW50KHBhc3RlKCJFc3RpbWF0ZSBmb3IgUCgwLjUgPCBYIDwgMS41KToiLCBwcm9iX2lfZXhwKSkNCiMgKGlpKSBFc3RpbWF0ZSBQKFggPiAxLjYxKQ0KcHJvYl9paV9leHAgPC0gbWVhbihzaW1feF9leHAgPiAxLjYxKQ0KcHJpbnQocGFzdGUoIkVzdGltYXRlIGZvciBQKFggPiAxLjYxKToiLCBwcm9iX2lpX2V4cCkpDQpgYGANCjQuDQooYSkgV3JpdGUgZnVuY3Rpb246IEFkZCB0d28gdmVjdG9ycyBieSB0cnVuY2F0aW5nIGxvbmdlciB0byBtYXRjaCBzaG9ydGVyIGxlbmd0aC4NCmBgYHtyfQ0KdHJ1bmNfYWRkIDwtIGZ1bmN0aW9uKHYxLCB2Mikgew0KICBuIDwtIG1pbihsZW5ndGgodjEpLCBsZW5ndGgodjIpKQ0KICByZXR1cm4odjFbMTpuXSArIHYyWzE6bl0pDQp9DQpgYGANCihiKSBXcml0ZSBBLnNvcnQgKHNvcnRzIGFzY2VuZGluZykgYW5kIG93bi5tZWQgKGZpbmRzIG1lZGlhbiB1c2luZyBBLnNvcnQpLg0KV2l0aCBzZWVkIDIwMCwgZ2VuZXJhdGUgNTAwIG9icyBmcm9tIE4oMTAsMTApLCBmaW5kIG1lZGlhbiB1c2luZyBvd24ubWVkLg0KYGBge3J9DQojIDEuIERlZmluZSBGdW5jdGlvbnMNCkEuc29ydCA8LSBmdW5jdGlvbih4KSB7DQogIHJldHVybihzb3J0KHgpKSAjIFVzZXMgUidzIGJ1aWx0LWluIHNvcnQgKGFzY2VuZGluZykNCn0NCg0Kb3duLm1lZCA8LSBmdW5jdGlvbih4KSB7DQogIHNvcnRlZF94IDwtIEEuc29ydCh4KQ0KICBuIDwtIGxlbmd0aChzb3J0ZWRfeCkNCiAgaWYgKG4gJSUgMiA9PSAxKSB7DQogICAgcmV0dXJuKHNvcnRlZF94WyhuICsgMSkgLyAyXSkNCiAgfSBlbHNlIHsNCiAgICByZXR1cm4obWVhbihzb3J0ZWRfeFtjKG4gLyAyLCBuIC8gMiArIDEpXSkpDQogIH0NCn0NCg0KIyAyLiBHZW5lcmF0ZSBEYXRhIGFuZCBUZXN0DQpzZXQuc2VlZCgyMDApDQojIEFzc3VtaW5nIE4oMTAsIDEwKSBtZWFucyBtZWFuPTEwLCBzZD0xMCBiYXNlZCBvbiBwcmV2aW91cyBjb250ZXh0DQpkYXRhIDwtIHJub3JtKDUwMCwgbWVhbiA9IDEwLCBzZCA9IDEwKSANCnJlc3VsdCA8LSBvd24ubWVkKGRhdGEpDQpwcmludChwYXN0ZSgiTWVkaWFuOiIsIHJlc3VsdCkpDQpgYGANCihjKSBQbG90IGYoeCk9eMKz4oiSMnjCsuKIkjQuIFVzZSBOZXd0b24tUmFwaHNvbiB0byBzb2x2ZSBmKHgpPTAuDQpGdW5jdGlvbiBpbnB1dHM6IHgwLCB0b2xlcmFuY2UgdDAuDQpPdXRwdXRzOiByb290LCBmKHJvb3QpLCBpdGVyYXRpb24gY291bnQuDQpgYGB7cn0NCiMgMS4gRGVmaW5lIHRoZSBmdW5jdGlvbiBmKHgpIGFuZCBpdHMgZGVyaXZhdGl2ZSBkZih4KQ0KZiA8LSBmdW5jdGlvbih4KSB4XjMgLSAyKnheMiAtIDQNCmRmIDwtIGZ1bmN0aW9uKHgpIDMqeF4yIC0gNCp4DQoNCiMgMi4gUGxvdCBmKHgpDQpjdXJ2ZShmLCBmcm9tID0gLTIsIHRvID0gNCwgbWFpbiA9ICJQbG90IG9mIGYoeCkiKQ0KYWJsaW5lKGggPSAwLCBjb2wgPSAicmVkIikgIyBTaG93cyB0aGUgeC1heGlzDQoNCiMgMy4gTmV3dG9uLVJhcGhzb24gRnVuY3Rpb24NCm5ld3Rvbl9zb2x2ZSA8LSBmdW5jdGlvbih4MCwgdDApIHsNCiAgaXRlciA8LSAwDQogIHggPC0geDANCiAgd2hpbGUgKGFicyhmKHgpKSA+IHQwKSB7DQogICAgeCA8LSB4IC0gZih4KSAvIGRmKHgpDQogICAgaXRlciA8LSBpdGVyICsgMQ0KICB9DQogIHJldHVybihsaXN0KHJvb3QgPSB4LCB2YWx1ZSA9IGYoeCksIGl0ZXJhdGlvbnMgPSBpdGVyKSkNCn0NCg0KIyBFeGFtcGxlIFVzYWdlIChHdWVzc2luZyAyKQ0KbmV3dG9uX3NvbHZlKDIsIDAuMDAwMSkNCmBgYA0KNS4NCihhKSBJbiBiaXJ0aHd0IGRhdGFzZXQ6IHRyZWF0IGxvdywgcmFjZSwgc21va2UsIGh0LCB1aSBhcyBjYXRlZ29yaWNhbC4gRmluZCBtZWFuIG9mIG51bWVyaWMgY29sdW1ucy4NCmBgYHtyfQ0KIyBMb2FkIGRhdGENCmxpYnJhcnkoTUFTUykNCmRhdGEoYmlydGh3dCkNCg0KYmlydGh3dCRsb3cgPC0gZmFjdG9yKGJpcnRod3QkbG93KSAjRmFjdG9yIGNvbnZlcnRzIG51bWVyaWMvdGV4dCBkYXRhIGludG8gY2F0ZWdvcmllcyAoZ3JvdXBzKS4NCmJpcnRod3QkcmFjZSA8LSBmYWN0b3IoYmlydGh3dCRyYWNlKQ0KYmlydGh3dCRzbW9rZSA8LSBmYWN0b3IoYmlydGh3dCRzbW9rZSkNCmJpcnRod3QkaHQgPC0gZmFjdG9yKGJpcnRod3QkaHQpDQpiaXJ0aHd0JHVpIDwtIGZhY3RvcihiaXJ0aHd0JHVpKQ0KDQpudW1lcmljX2NvbHMgPC0gYygiYWdlIiwgImx3dCIsICJid3QiLCAicHRsIiwgImZ0diIpDQpudW1lcmljX21lYW5zIDwtIGNvbE1lYW5zKGJpcnRod3RbbnVtZXJpY19jb2xzXSkNCg0KY2F0KCIoYSkgTWVhbnMgb2YgbnVtZXJpYyBjb2x1bW5zOlxuIikNCnByaW50KG51bWVyaWNfbWVhbnMpDQoNCmBgYA0KKGIpIFdyaXRlIGZ1bmN0aW9uOiBGb3IgYnd0IHZhcmlhYmxlLCBjb21wdXRlIG1lYW4gYW5kIFNEIGZvciBlYWNoIHJhY2UgZ3JvdXAuDQpgYGB7cn0NCmJ3dF9zdGF0c19ieV9yYWNlIDwtIGZ1bmN0aW9uKGRhdGEpIHsNCiAgcmFjZXMgPC0gbGV2ZWxzKGRhdGEkcmFjZSkNCiAgcmVzdWx0cyA8LSBkYXRhLmZyYW1lKCkNCiAgDQogIGZvcihyIGluIHJhY2VzKSB7DQogICAgYnd0X3ZhbHVlcyA8LSBkYXRhJGJ3dFtkYXRhJHJhY2UgPT0gcl0NCiAgICBtZWFuX3ZhbCA8LSBtZWFuKGJ3dF92YWx1ZXMpDQogICAgc2RfdmFsIDwtIHNkKGJ3dF92YWx1ZXMpDQogICAgcmVzdWx0cyA8LSByYmluZChyZXN1bHRzLCBkYXRhLmZyYW1lKFJhY2UgPSByLCBNZWFuID0gbWVhbl92YWwsIFNEID0gc2RfdmFsKSkNCiAgfQ0KICANCiAgcmV0dXJuKHJlc3VsdHMpDQp9DQoNCmNhdCgiXG4oYikgQldUIHN0YXRpc3RpY3MgYnkgcmFjZTpcbiIpDQpwcmludChid3Rfc3RhdHNfYnlfcmFjZShiaXJ0aHd0KSkNCmBgYA0K