1 Introduction

A common task in statistics is maximizing (or minimizing) complex univariate or multivariate functions. This is typically done in the context of maximizing likelihood functions with respect to a vector of unknown parameters, given the observed data.

The term “parameter estimation” may be one that you may have commonly heard in the past during discussions of fitting statistical models to data. More generally, this task pertains to an “optimization” problem, where we seek to find a set of parameter values that minimizes or maximizes some pre-defined objective function. When this objective function is a log likelihood function, this reduces to the problem of maximum likelihood estimation.

As we will see in this lecture, there are many potential approaches to use for optimization in this context. How best to optimize a function of interest depends on the nature of the function to be optimized, as well as practical concerns regarding a candidate procedure to be used. As such, another goal of this lecture is to explain the pros and cons of different approaches to help narrow down which approach may best for your particular problem.

Our discussion of optimization in this lecture will give a broad overview of existing approaches for unconstrained optimization, touching the many of the special cases that may have been covered in previous coursework. In a later lecture we will discuss the topic of “constrained” optimization, where the potential set of allowable solutions is no longer unrestricted. We focus on the class of problems where the function to be optimized is smooth, real valued, and is differentiable.

2 Likeliood Function Optimization

2.1 Example data

Let us assume that we have obtained a random sample of 100 individuals from WCU. Let us define \(\mathbf{y} = (y_1, \ldots, y_{100})\), where \(y_i\) pertains to the measured height of the \(i\)th individual in inches, for \(i = 1, \ldots, 100\).

Let us further assume that the distribution of these heights is distributed normally with some unknown mean \(\mu\) and known variance \(\sigma^2 = 10\). That is, we assume that \(y_i \sim N(\mu, \sigma^2)\), where \(\sigma^2 = 10\).

A common question in this situation is what is the “best” estimate of \(\mu\) given the data, and how do we obtain it? Answering this question can depend on the domain of the question being asked.

From introductory statistical courses, one may remember that an estimator for \(\mu\), say \(\hat{\mu}\), in this setting may be \(\bar{y}\), or the mean of the observed heights. In more advanced statistical courses, you may have shown why this is the case via analytically deriving the maximum likelihood estimator for \(\mu\), and proving its optimality relative to other potential estimators.

We can also re-express the original form of this problem as an intercept-only linear regression model: \(y_i = X_i\beta + \epsilon_i\) where \(\epsilon_i \sim N(0, \sigma^2)\), \(X_i = 1\), and \(\beta = \mu\), a scalar parameter in this setting. Again, \(\sigma^2\) is known in this case. Given this, we can show that an estimate for \(\beta\) (\(\mu\)) is also \(\bar{y}\). Based on the normal equations from the linear model framework, we have closed form solution \(\hat{\beta} = (X'X)^{-1}X'y\), which, plugging in \(X\) and \(y\) from this situation, also yields \(\bar{y}\).

Regardless of the approach used here, we can show that we can obtain \(\hat{\mu}\) analytically via optimizing some function of the data with respect to the parameter of interest \(\mu\). In the linear regression case, we are minimizing the residual sum of squares of the model with respect to \(\beta\) (\(\mu\)), and in the latter, we are maximizing the likelihood function assumed for the data with respect to \(\mu\).

2.2 Likelihood Function

The likelihood function \(L(\theta)\), given the assumptions we made, for this sample can be written as

\[ L(\theta) = f(y_1 \mid \mu, \sigma^2) f(y_2 \mid \mu, \sigma^2) \dots f(y_{100} \mid \mu, \sigma^2) = \prod_{i=1}^{100} f(y_i \mid \mu, \sigma^2) = \frac{1}{(2\pi\sigma^2)^{n/2}} e^{-\frac{1}{2\sigma^2} \sum_{i=1}^{100} (y_i - \mu)^2} \]

Here, \(\theta = \mu\) since we are assuming that \(\sigma^2\) is known. Taking logs of both sides, we can write the optimization of \(L(\theta)\) above in terms of the optimization of \(l(\theta)\), the log-likelihood, as it is a monotonic transformation of the original likelihood function.

Why does this matter? This implies that the maximum of one function will be the maximum of the other function, as the log transformation is a one-to-one mapping from the likelihood to the log-likelihood (example in figure below). Using approaches from prior courses, we can show that this function is smooth and convex in \(\theta\), and therefore the maximum of the log-likelihood (and hence the likelihood) will be attained at the value of \(\theta\) where the first derivative of \(l(\theta)\) is equal to zero.

In the current problem, the MLE for \(\mu\), \(\hat{\mu} = \bar{y}\), is then obtained by setting the 1st derivative of the log-likelihood (also known as the score function) equal to 0 and then solving for \(\mu\). In other words, we find the root of the score function with respect to \(\mu\). For the regression based approach, we are simply minimizing the residual sum of squares (RSS), defined as \(\sum_{i=1}^{100} (y_i - \mu)^2\).

We know that this log transformation often facilitates the calculation of derivatives when analytically solving for \(\mu\). This approach of using another function as a proxy of the original function to simplify maximization can be found in several examples of optimization we will cover this semester.

2.3 Visualizing the Optimization Problem for \(\mu\)

We can illustrate this approach using the following simulation example:

mu <- 67
sigma2 <- 10
n <- 100

# generate random normally distributed samples
set.seed(123) # for reproducibility
y <- rnorm(n, mu, sd = sqrt(sigma2))

# print MLE of mu, mu_hat
cat("mean of y\n")
mean of y
mean(y)
[1] 67.28589
# print estimate of mu in linear model framework (intercept only model)
cat("\nbeta_hat from intercept only linear model\n")

beta_hat from intercept only linear model
lm(y ~ 1)$coef
(Intercept) 
   67.28589 

Now, lets write some functions to help generate some figures in the next code chunk. These functions will help plot the likelihood, log likelihood, and RSS with respect to \(\mu\) based on the setup above.

# returns a vector of likelihood values over a given range of mu values
# helps for plotting later
lik <- function(mu, sigma2, y) {
  # create empty vector
  likelihood.vector <- rep(NA, length(mu))
  
  # for each mu, calculate the likelihood value
  for (i in 1:length(likelihood.vector)) {
    likelihood.vector[i] <- prod(dnorm(y, mu[i], sqrt(sigma2)))
  }
  
  # return vector of likelihood values spanning range of mu
  return(likelihood.vector)
}

# define the log-likelihood function with respect to mu
loglik <- function(mu, sigma2, y) {
  # create empty vector
  log.likelihood.vector <- rep(NA, length(mu))
  
  # calculate log likelihood
  for (i in 1:length(log.likelihood.vector)) {
    log.likelihood.vector[i] <- sum(dnorm(y, mu[i], sqrt(sigma2), log = TRUE))
  }
  
  return(log.likelihood.vector)
}

# define the residual sum of squares function with respect to mu
RSS <- function(mu, sigma2, y) {
  # create empty vector
  RSS.vector <- rep(NA, length(mu))
  
  # calculate log likelihood
  for (i in 1:length(RSS.vector)) {
    RSS.vector[i] <- sum((y - mu[i]) ^ 2)
  }
  
  return(RSS.vector)
}

Now that we got that out of the way, lets plot each of these functions with respect to \(\mu\):

# range of mu to plot over
range <- seq(45, 85, length.out = 1000)

# arrange the two plots in 1 row, three columns
par(mfrow = c(1, 3))

# likelihood plot
plot(
  range,
  lik(mu = range, sigma2 = sigma2, y = y),
  type = "l",
  ylab = "likelihood",
  xlab = "mu",
  main = "Likelihood Function"
)

# add vertical line at the mean of y
abline(v = mean(y), lty = 2, col = "grey")

# log likelihood plot
plot(
  range,
  loglik(mu = range, sigma2 = sigma2, y = y),
  type = "l",
  ylab = "log likelihood",
  xlab = "mu",
  main = "Log-Likelihood Function"
)

# vertical line at the mean of y
abline(v = mean(y), lty = 2, col = "grey")

# RSS plot
plot(
  range,
  RSS(mu = range, sigma2 = sigma2, y = y),
  type = "l",
  ylab = "RSS",
  xlab = "mu",
  main = "Residual Sum of Squares"
)

# vertical line at the mean of y
abline(v = mean(y), lty = 2, col = "grey")

# reset par
par(mfrow = c(1, 1))

We can clearly see that the maximum of the likelihood, log-likelihood, and RSS functions occurs at the MLE of μ, \(\hat{\mu} = r round(mean(y), 5)\). At the MLE, we can see that the derivative of each of the functions are equal to zero, following the intuition of the procedures used to solve for \(\hat{\mu}\). We will see that many of the algorithms proposed in this section are based upon this principle when trying to find the minimum/maximum of a function when no closed form solutions are available.

2.4 MLE of \(\lambda\) in Poisson

We know that the PMF for a Poisson random variable \(Y\) with mean \(\lambda\) may be written as \(\text{Po}(Y=y|\lambda) = \frac{e^{-\lambda}\lambda^y}{y!}\), where \(y\) is a draw from this distribution. Suppose we draw a sample of \(n\) observations from this distribution and we wish to determine the MLE of \(\lambda\), \(\hat{\lambda}\).

As discussed in prior courses, the MLE can be obtained by simply taking the first derivative of the likelihood (or more easily, the log likelihood), setting it equal to 0, and then solving for the parameter of interest.

The log likelihood here is \(l(\lambda) = -n\lambda + \left(\sum_{i=1}^n y_i\right)\log(\lambda) - \sum_{i=1}^n \log(y_i!)\). Then, taking the first derivative of the log likelihood, also known as the score function, we then solve the following: \(0 = -n + (\sum_{i=1}^n y_i)/\lambda\) which gives the MLE \(\hat{\lambda} = (\sum_{i=1}^n y_i)/n\).

In this example, as in the prior one, we can derive closed form solutions to obtain a parameter estimate for \(\lambda\) by maximizing log-likelihood function. You can imagine the task of optimization as not too different from that of root finding. In the context of maximum likelihood estimation, this is akin to finding the root of the score equation.

However, what if we do not have such nice closed-form solutions for the model at hand?

3 Univariate Optimization

In this module we will discuss some fundamental methods for the optimization of smooth nonlinear functions (Givens and Hoeting Chapter 2), beginning with single univariate functions and then discussing extensions to the multivariate case. Combinatorial optimization (Givens and Hoeting Chapter 3) will not be emphasized here but students can read this chapter if interested. We will also cover the EM algorithm (Givens and Hoeting Chapter 4) in more detail in a later lecture. There we will discuss the optimization of functions with missing data and also how the algorithm has been widely applied to general problems in statistics.

Let us consider the numerical optimization of the following function \(f(\theta)\), which we seek to maximize with respect to \(\theta\):

\[ f(\theta) = \frac{\log(\theta)}{1+\theta} \].

The traditional approach to maximizing \(f(\theta)\), such as the one described in the previous example, is difficult to apply here as the analytical solution for \(\theta\) after setting \(f'(\theta) = 0\) is not easy to determine.

Here,

\[ f'(\theta) = \frac{1+\theta - \theta\log(\theta)}{\theta(1+\theta)^2} \].

So…not easy to solve for.

Let’s first plot this function to determine its shape:

# define function
f1 <- function(theta) {
  log(theta) / (1 + theta)
}

# make a plot with this function, where the x-axis ranges from 0 to 10
plot(
  f1,
  from = 0.1,  # start from 0.1 to avoid log(0)
  to = 10,
  ylab = "f(theta)",
  xlab = "theta",
  main = expression(f(theta) == frac(log(theta), 1 + theta)),
  lwd = 2
)

# add grid for better visualization
grid()

Plotting this function, the maximum of \(f(\theta)\) appears to be between 3 and 4. So how exactly do we find the maximum of this function, and the value of \(\theta\) that pertains to the maximum, \(\hat{\theta}\)?

3.1 Basics of Numerical Optimization

Most numerical optimization methods are iterative and rely on the concept of successive approximations to find the value of \(\theta\) pertaining to the minimum or maximum of the function of interest. Such algorithms require a starting value \(\theta^{(0)}\), an initial guess for \(\hat{\theta}\).

From this point, an algorithm will iteratively update its guess for \(\hat{\theta}\) based on a predefined updating formula. Each update may be referred to an “iteration”.

Ideally, the value of \(\hat{\theta} \to \theta^*\) as \(t \to \infty\), where \(t\) is the iteration number such that \(t > 0\) and \(\theta^*\) is the “true” value of \(\theta\) where the maximum of \(f(\theta)\) is achieved. That is, as the number of iterations increases, we hope that \(\hat{\theta}\) will approach its true optimum \(\theta^*\), or converge to \(\theta^*\).

However, given the iterative updating nature of these algorithms, \(\hat{\theta}\) will never equal the value of \(\theta^*\) exactly, and will only approach \(\theta^*\) as \(t\) increases. As a result, we need to define criteria or stopping rules indicating that the algorithm has converged, meaning that \(\hat{\theta}\) is reasonably close to the prior estimate of \(\hat{\theta}\) within some tolerance or distance.

In some cases, the algorithm may converge to some value other than \(\theta^*\), or may not converge at all. We will discuss these situations later in this lecture.

3.1.1 Graphical Illustration

To illustrate an application of numerical optimization to \(f(\theta)\) (introduced earlier), see the following plot. Here, we utilized the commonly used Newton-Raphson algorithm to optimize \(f(\theta)\). In it, we choose a starting value \(\theta^{(0)} = 0.25\) to initialize the algorithm.

include_graphics("GraphicalIllustration01.gif")

You can see that the algorithm appears to converge in approximately 12 iterations to 3.59112 (the value of \(\theta\) pertaining to the true maximum of \(f(\theta)\)), where the difference between the value of \(\hat{\theta}\) at the 11th step (\(\hat{\theta}^{(11)}\)) and the 12th step (\(\hat{\theta}^{(12)}\)) is very small, and our final estimate for \(\hat{\theta}^{(11)}\) is 3.59112.

3.1.2 Criteria for convergence

If we define our tolerance for convergence, or convergence threshold, at step \(t\) as some small fixed value \(\epsilon\), we may formally terminate the algorithm when \(|\hat{\theta}^{(t)} - \hat{\theta}^{(t-1)}| < \epsilon\). Here, the optimal value of \(\theta\) is \(\theta^* = 3.59112\).

Clearly, larger values of \(\epsilon\) will result in convergence in fewer iterations; however, the resulting \(\hat{\theta}\) may be less accurate (further away from \(\theta^*\)). Depending on the application, a higher or lower convergence threshold may be warranted, where in some cases 10 decimal places of accuracy (and potentially requiring many more iterations for meeting the convergence threshold) may not be necessary.

Other criteria for determining convergence exist. You may notice from the definition above that the algorithm converges if the raw difference in \(\theta^{(t+1)}\) and \(\theta^{(t)}\) becomes small. However, if \(\theta^*\) is generally large in scale (say, typically \(> 10000\) in value), an \(\epsilon\) of \(10^{-6}\) may be too restrictive or difficult to reach, whereas if \(\theta^*\) is in the range of \(10^{-12}\)\(10^{-16}\), an \(\epsilon\) of \(10^{-6}\) may simply be too liberal. All in all, the threshold’s stringency may depend on the scale of \(\theta^*\).

For these reasons, some may prefer a relative convergence threshold, such as

\[ \frac{|\theta^{(t+1)} - \theta^{(t)}|}{|\theta^{(t)}|} < \epsilon, \]

where convergence is now met if the change relative to \(\theta^{(t)}\) is smaller than some proportion \(\epsilon\). For example, we may instead want to terminate the algorithm when the change is less than, say, 1% of the previous value, rather than specifying an exact value of the difference. However, relative convergence criteria can become problematic if \(\theta^{(t)}\) or \(\theta^*\) is very close to zero. In that case, an alternative criterion

\[ \frac{|\theta^{(t+1)} - \theta^{(t)}|}{|\theta^{(t)}| + \epsilon} < \epsilon \]

may help prevent instability in the criterion.

It is also helpful to add a maximum iteration limit to prevent runaway divergence or to halt poorly converging algorithms (sometimes indicative of bad starting points). This limit may also be reached if your convergence threshold is too stringent, and/or the iteration limit is too low. Printing a warning message when this limit is reached is common among algorithms.

In summary, when implementing optimization algorithms, we need criteria to determine when to stop the iterations. Common criteria include:

  • Absolute convergence: Stop when \(|\theta^{(k+1)} - \theta^{(k)}| < \epsilon\), where \(\epsilon\) is a small tolerance.

  • Relative convergence: Stop when \(\frac{|\theta^{(k+1)} - \theta^{(k)}|}{|\theta^{(k)}|} < \epsilon\).

  • Function value convergence: Stop when \(|f(\theta^{(k+1)}) - f(\theta^{(k)})| < \epsilon\).

3.1.3 Impact of starting values on convergence

The choice of starting values can significantly impact convergence. Let’s demonstrate this with a simple example:

Lets now examine the impact of a different starting point for the algorithm, \(\theta^{(0)} = 1\)

\(\theta^{(0)} = 4\)

\(\theta^{(0)} = 8\)

In some cases, the end result of the algorithm is the same regardless of the chosen starting point of the algorithm; however, fewer iterations may be required for \(\theta^{(t)} \to \theta^*\) (due to a better starting point). In other cases, you can see that the model may converge to a value other than \(\theta^*\), representing a local optimum rather than the global optimum. Or, the algorithm may not converge at all (diverges), as in the last example above.

In general, utilizing a starting value closer to \(\theta^*\) results in convergence in fewer iterations. This illustrates the importance of choosing an informative starting point for the algorithm, and there are several strategies to do this—strategies that can vary by application.

Evaluating several starting points (perhaps chosen randomly) and choosing the starting point that ends in the best value of the objective function is the best way to avoid getting trapped in a local optimum or hitting convergence failures. Other strategies include:

  • Graphing/plotting the objective function

  • Preliminary estimates (based on simpler models, or method-of-moments estimates)

  • Educated guesses derived from domain knowledge

  • Trial and error to find reasonable initial values

3.1.4 Basics of Numerical Optimization Summary

To summarize, the following concepts are important for understanding the set of optimization algorithms covered in the rest of this module:

  • Optimization algorithms are iterative

  • They require starting values

  • Convergence criteria determine when to stop

  • The choice of algorithm depends on the problem characteristics

  • For univariate optimization, methods like golden section search, parabolic interpolation, or gradient-based methods can be used

In the next few sections we will go into detail regarding various approaches for numerical optimization and their pros and cons for use. We will also discuss algorithm-specific aspects of computational complexity and convergence speed of each approach to help you decide which algorithm may be better to utilize for your specific application.

3.2 Newton-Raphson

In the previous illustration we used a common technique called Newton–Raphson (NR) to optimize the function of interest with respect to \(\theta\). This approach is also referred to as Newton’s Method, and is an iterative updating scheme to arrive at \(\hat{\theta}\).

One notable requirement of this method is that expressions for the first and second derivatives of the function of interest must be given. In some situations such derivatives may be difficult to derive or evaluate in practice. We will introduce several alternative numerical optimization procedures to handle these situations later in this lecture.

Let \(\theta^{(0)}\) denote the starting value for the estimate of \(\theta\) and assume that we wish to maximize some function \(f(\theta)\). Then, based on this scheme, we update our estimate of \(\theta\) at the \(t\)-th step of the algorithm (\(\theta^{(t)}\)) using the following expression:

\[ \theta^{(t+1)} = \theta^{(t)} + h^{(t)}, \]

where

\[ h^{(t)} = -\frac{f'\!\bigl(\theta^{(t)}\bigr)}{f''\!\bigl(\theta^{(t)}\bigr)}, \]

\(f'(\theta)\) is the first derivative of \(f(\theta)\) with respect to \(\theta\) evaluated at \(\theta^{(t)}\), and \(f''(\theta)\) is the second derivative of \(f(\theta)\) with respect to \(\theta\) evaluated at \(\theta^{(t)}\).

If we sought to instead minimize \(f(\theta)\), the same update would apply. After choosing an appropriate starting point for the algorithm and stopping rule, we can apply the updating rule above as we had in the previous example to arrive at \(\hat{\theta}\).


A few obvious questions:

  • How did we get this expression?

  • Why would we even expect \(\theta^{(t)} \to \theta^*\)?

  • Under what conditions do we expect the algorithm to work well or not work well?

  • How quickly would we expect \(\theta^{(t)} \to \theta^*\) in \(t\)?

There may be situations where the algorithm may not converge or do so in a suitable timeframe. Understanding the rationale behind this approach and its properties may help navigate these situations and provide clues to what alternative approaches may work.

3.2.1 Rationale and Derivation

Let us suppose that \(f'(\theta)\) is continually differentiable, \(f''(\theta)\) exists, and that \(f''(\theta) \neq 0\) (suggesting that the first derivative is non-constant). At a given iteration \(t\), \(f'(\theta^*)\) can be approximated by a linear Taylor Series Expansion about \(\theta^*\), the unknown true value of \(\theta\) that maximizes \(f(\theta)\). Yes, we do not know what \(\theta^*\) is in reality, but this term is important when we talk about the conditions that may impact the performance of this approach later on.

Under this approximation, we have

\[ 0 = f'(\theta^*) \;\approx\; f'\bigl(\theta^{(t)}\bigr) + \bigl(\theta^* - \theta^{(t)}\bigr) \, f''\bigl(\theta^{(t)}\bigr). \]

Let’s unpack what this means for one second. If you recall, most analytic approaches to optimizing algorithms start with setting the 1st derivative to 0 and solving for the value of \(\theta\) satisfying this equality. In this setting, we assume that such an analytic solution does not exist. Therefore, we approximate this derivative using this linear Taylor Series approximation around \(\theta^*\), and then set this approximation to zero. Here, \(f'(\theta^*)\) is approximated by the tangent line at \(\theta^{(t)}\) (see figure below).

include_graphics("NewtonRaphson.png")

Example of Newton–Raphson in the context of the previous illustration. The algorithm approximates \(g'\) by its tangent line at \(\theta^{(t)}\), whose root \(\theta^{(t+1)}\) serves as the next approximation of the true root \(\theta^*\).

Since we are approximating \(f'(\theta^*)\) with its tangent line at \(\theta^{(t)}\), it makes sense that we can approximate the root of \(f'(\theta^*)\) (the point at which it equals 0) with the root of the tangent line at \(\theta^{(t)}\) (see example in the figure above at step 1). Under this rationale, we solve for \(\theta^*\) in the linear Taylor Series approximation above. Doing this, we have

\[ \theta^* \;=\; \theta^{(t)} - \frac{f'\bigl(\theta^{(t)}\bigr)}{f''\bigl(\theta^{(t)}\bigr)} \;=\; \theta^{(t)} + h^{(t)}. \]

In other words, the value for \(\theta^*\) at iteration \(t\) is approximated using the current guess \(\theta^{(t)}\) and a refinement step \(h^{(t)}\). Successive iterations of this approach will yield closer and closer approximations to \(\theta^*\).

3.2.2 Updating Equation

Using the above rationale, we can define the updating strategy as

\[ \theta^{(t+1)} = \theta^{(t)} - \frac{f'\bigl(\theta^{(t)}\bigr)}{f''\bigl(\theta^{(t)}\bigr)} = \theta^{(t)} + h^{(t)}. \]

When the optimization of \(f\) corresponds to maximum likelihood estimation, where \(\hat{\theta}\) is the solution to \(l'(\theta) = 0\), the updating equation is given as

\[ \theta^{(t+1)} = \theta^{(t)} - \frac{l'\bigl(\theta^{(t)}\bigr)}{l''\bigl(\theta^{(t)}\bigr)}. \]

3.2.3 Requirements and Convergence

But how do we know that successive iterations using the updating equation above will result in convergence to \(\theta^*\), rather than diverging or converging to some other value? In practice, this depends on the shape of the function you are maximizing as well as your chosen starting value.

As we can see from the prior examples, choosing starting values too far away from \(\theta^*\) results in slower convergence or even divergence. Choosing values closer to \(\theta^*\) results in quicker convergence.

We can show that if \(f'''(\theta)\) is continuous and \(\theta^*\) is a simple root of \(f'(\theta)\) (implying \(f'(\theta^*) = 0\)), then there exists a neighborhood around \(\theta^*\) for which NR converges to \(\theta^*\) from any starting value \(\theta^{(0)}\) within that neighborhood (GH 2.1.1, eq. 2.18). The definition of “neighborhood” in this sense is somewhat arbitrary, but is meant to indicate that convergence is likely if you are in some close vicinity of \(\theta^*\). GH provides this proof using arguments based upon the error of a quadratic Taylor Series approximation for \(f'(\theta)\) in Section 2.1.1.

More specifically, if \(f'(\theta)\) is twice differentiable, is convex, and has a root, then NR will converge from any point! These conditions are typically met by common likelihood functions in statistics (but not always).

If you are starting in some arbitrary interval \([a,b]\), you can check the following conditions to determine whether the NR algorithm will converge from any \(\theta^{(0)}\) in that interval:

  1. \(f''(\theta) \neq 0\) on \([a,b]\)

  2. \(f'''(\theta)\) does not change sign on \([a,b]\)

  3. \(f'(a) f'(b) < 0\), and

  4. \(|f'(a) / f''(a)| < b - a\) and \(|f'(b) / f''(b)| < b - a\)

These statements follow from the proof given in GH 2.1.1. In practice, most do not manually evaluate these statements before applying NR, but in cases where problems arise, they can help explain convergence difficulties.

Rephrased, we can translate these conditions into the following:

  • Condition 1: The 2nd derivative is never equal to zero in the interval → \(f'(\theta)\) is never flat on \([a,b]\)\(f'(\theta)\) can potentially intersect 0 in the interval → \(f'(\theta)\) has the potential to have a root in \([a,b]\).

  • Condition 2: The third derivative does not change sign on \([a,b]\) → the sign of the second derivative stays the same in the interval on \([a,b]\) → no change in convexity/concavity in the interval.

  • Condition 3: The 1st derivative at \(a\) is positive and negative at \(b\) (or vice versa) → \(f'(\theta)\) changes sign at some point in \([a,b]\) → implies a root for \(f'(\theta)\) must exist in \([a,b]\).

  • Condition 4: The absolute value of \(h\) at \(a\) or at \(b\) is less than the length of the interval → the step size (refinement) assuming \(\theta^{(0)} = a\) or \(\theta^{(0)} = b\) is smaller than the length of the interval being considered. If this is not true, the update will definitely not be contained in \([a,b]\).

Using similar arguments, we can show that the convergence order for NR is quadratic, meaning the accuracy of the solution will double with each iteration \(t\). Higher convergence order implies that accurate approximations for the parameter(s) of interest are achieved in fewer iterations.

However, we will see that algorithms with higher convergence orders may also be less robust to different conditions and may fail more frequently than slower algorithms. For example, NR is relatively more sensitive to the starting value compared to other methods.

3.2.4 Pros and Cons

  • Pros

    • Speed: Extremely fast (quadratic) convergence
  • Cons

    • Requires derivation and evaluation of 1st and 2nd derivatives

    • Relatively more sensitive to the choice of starting value

    • In the multivariate setting, need checks on the Hessian to avoid singularities and other issues

    • If the derivatives and/or likelihood are complicated, NR may not be an attractive choice

    • Additional evaluations of derivatives may be costly (e.g., when the likelihood involves numerical integration)

With respect to NR, John Nash in Nonlinear parameter optimization using R tools summarizes:

There are lots of dangerous beasts in the night-time optimization forest.

The apparent difficulties with NR in the multivariate setting are why it was not included in the original R general-purpose optimization functions optim and optimx.

3.2.5 Example of Newton Raphson

Let’s determine the 1st and 2nd derivatives of the simple Poisson example from earlier. This is the same approach that we will apply to the Poisson Regression example, but that will be left for a later section.

Here

\[ l'(\lambda) = -n + \frac{\sum_{i=1}^n y_i}{\lambda} \]

and the second derivative is

\[ l''(\lambda) = -\frac{\sum_{i=1}^n y_i}{\lambda^2}. \]

Now, let’s generate some data and apply NR:

# generate 100 poisson(10) samples
set.seed(10)
n = 100
lambda = 10
y = rpois(n, lambda)
mean(y)
[1] 10.27

Now lets get our functions in order

# log likelihood function
logLik = function(lambda, y) {
  value = sum(dpois(y, lambda = lambda, log = T))
  return(value)
}

# lets write a function for the first derivative
d1 = function(lambda, y) {
  first = -length(y) + sum(y) / lambda
  return(first)
}

# now the second
d2 = function(lambda, y) {
  second = -sum(y) / lambda ^ 2
  return(second)
}

Now lets apply NR. We pick a tolerance level (tol) and starting value (lam). Here the choice of starting value is arbitrary. We can vary this to see its impact on the final value later. It’s also helpful to specify a maximum number of iterations (maxit) to halt the algorithm so it does not loop infinitely if there is a convergence issue. We can also add conditions to check whether \(\lambda (t)>0\).

# now lets apply NR. First lets set the tolerance and choose a starting value
# the start
tol = 10^-4
lam = 0.1
maxit = 50
iter = 0 # iteration counter
eps = Inf # keeps track of current difference
start = Sys.time() # record start time

while (eps > tol & iter < maxit) {
  # save the previous value
  lam0 = lam
  
  # calculate h, the increment
  h = -d1(lambda = lam, y = y) / d2(lambda = lam, y = y)
  
  # update lambda
  lam = lam + h
  
  # update the log likelihood
  logL = logLik(lambda = lam, y = y)
  
  # calculate the diff in lambda, could also use the log likelihood if we wanted
  eps  = abs(lam - lam0)
  
  # update the iteration number
  iter = iter + 1
  if (iter == maxit)
    warning("Iteration limit reached without convergence")
  
  # print out info to keep track
  cat(sprintf("Iter: %d logL: %f lam: %f h: %f eps:%f\n", iter, logL, lam, h, eps))
}
Iter: 1 logL: -3287.764559 lam: 0.199026 h: 0.099026 eps:0.099026
Iter: 2 logL: -2605.419174 lam: 0.394196 h: 0.195169 eps:0.195169
Iter: 3 logL: -1951.364892 lam: 0.773261 h: 0.379065 eps:0.379065
Iter: 4 logL: -1350.416275 lam: 1.488300 h: 0.715039 eps:0.715039
Iter: 5 logL: -843.064849 lam: 2.760920 h: 1.272620 eps:1.272620
Iter: 6 logL: -481.321114 lam: 4.779612 h: 2.018692 eps:2.018692
Iter: 7 logL: -297.005267 lam: 7.334814 h: 2.555202 eps:2.555202
Iter: 8 logL: -248.465739 lam: 9.431118 h: 2.096304 eps:2.096304
Iter: 9 logL: -244.863791 lam: 10.201478 h: 0.770360 eps:0.770360
Iter: 10 logL: -244.840830 lam: 10.269543 h: 0.068065 eps:0.068065
Iter: 11 logL: -244.840829 lam: 10.270000 h: 0.000457 eps:0.000457
Iter: 12 logL: -244.840829 lam: 10.270000 h: 0.000000 eps:0.000000
end = Sys.time()
print(end - start)
Time difference of 0.03506804 secs

We can also visualize this below

3.3 Fisher Scoring

Fisher Scoring is an alternative to NR when performing maximum likelihood estimation, where we simply replace \(l''\!\bigl(\theta^{(t)}\bigr)\) with \(I\!\bigl(\theta^{(t)}\bigr)\), the expected Fisher Information matrix at iteration \(t\). We can show that \(-l''(\theta)\) — the observed Fisher Information — is an approximation for \(I(\theta)\) (GH 1.4), so it is not surprising that the asymptotic properties for NR and FS are similar.

The updating equation is given as

\[ \theta^{(t+1)} = \theta^{(t)} + I\!\bigl(\theta^{(t)}\bigr)^{-1} \, l'\!\bigl(\theta^{(t)}\bigr). \]

Given the particular problem at hand, NR or FS may be easier to derive analytically, where the latter only needs knowledge of the 1st derivatives and avoids computation of the 2nd derivatives. According to GH, FS may be used in early iterations for rapid improvements, and NR can be used to make better refinements near the end.

  • Pros

    • Avoids derivation and computation of the 2nd derivative

    • Similar asymptotic properties to NR

    • In the multivariate setting, may be more stable than NR (will talk about this later)

    • Often works better in the beginning to make rapid improvements

  • Cons

    • May not be as fast for refinement near the end

3.4 Secant Method

This approach is similar to NR, except that the second derivative in the updating equation is replaced with a finite‑difference approximation, where now the updating equation is

\[ \theta^{(t+1)} = \theta^{(t)} - f'\!\bigl(\theta^{(t)}\bigr) \, \frac{\theta^{(t)} - \theta^{(t-1)}}{f'\!\bigl(\theta^{(t)}\bigr) - f'\!\bigl(\theta^{(t-1)}\bigr)} . \]

Conditions for convergence are similar to NR, but we can show through similar arguments that the convergence order for the secant method is \(\phi \approx 1.618\) (the golden ratio) instead of 2.

To initialize this approach, two starting values have to be provided. Typically the first value is picked in a manner not dissimilar to the other approaches described in this lecture, and the second value is picked relatively close to the first one. Then we may compute the first update and proposal for \(\theta^{(1)}\) and start the algorithm.

  • Pros

    • Avoids derivation and computation of the 2nd derivative

    • Similar asymptotic properties to NR

  • Cons

    • Slower convergence relative to NR (order ~1.618 vs. quadratic)

3.5 Examples

Let’s apply FS to our simple Poisson problem. To do this, let’s first calculate the expected information \(I(\theta)\), which in the univariate setting is

\[ I(\theta) = \mathbb{E}\!\bigl[l'(\theta)^2\bigr] = \mathbb{E}\!\bigl[-l''(\theta)\bigr]. \]

This expression can be calculated by brute force using the following steps:

\[ \begin{aligned} \mathbb{E}\!\bigl[l'(\theta)^2\bigr] &= \mathbb{E}\!\left[ \left(-n + \frac{\sum_{i=1}^n y_i}{\lambda}\right)^2 \right] \\[4pt] &= \mathbb{E}\!\left[ \frac{\bigl(\sum_{i=1}^n y_i\bigr)^2}{\lambda^2} \;-\; \frac{2n \bigl(\sum_{i=1}^n y_i\bigr)}{\lambda} \;+\; n^2 \right] \\[4pt] &= \frac{\mathbb{E}\!\bigl[(\sum_{i=1}^n y_i)^2\bigr]}{\lambda^2} \;-\; \frac{2n \sum_{i=1}^n \mathbb{E}[y_i]}{\lambda} \;+\; n^2 \\[4pt] &= \frac{n\lambda + (n\lambda)^2}{\lambda^2} \;-\; \frac{2n \bigl(\sum_{i=1}^n \lambda\bigr)}{\lambda} \;+\; n^2 \\[4pt] &= \frac{n}{\lambda} + n^2 \;-\; 2n^2 \;+\; n^2 \\[4pt] &= \frac{n}{\lambda}. \end{aligned} \]

We can see this has a nice form and avoids having to analytically determine a second derivative.

# now lets apply FS using same framework as before
# the start
tol = 10 ^ -4
lam = 0.1
maxit = 50
iter = 0
eps = Inf

start = Sys.time()
while (eps > tol & iter < maxit) {
  # save the previous value
  lam0 = lam
  
  # calculate h, the increment. 
  h = d1(lambda = lam, y = y) / (n / lam)
  
  # update lambda
  lam = lam + h
  
  # lambda cant be less than or equal to 0, so put a check for this
  if (lam <= 0)
    stop("lambda leq 0")
  
  # update the log likelihood
  logL = logLik(lambda = lam, y = y)
  
  # calculate the diff, could also use the log likelihood if we wanted
  eps  = abs(lam - lam0)
  
  # update the iteration number
  iter = iter + 1
  if (iter == maxit)
    warning("Iteration limit reached without convergence")
  
  # print out info to keep track
  cat(sprintf("Iter: %d logL: %f lam: %f h: %f eps:%f\n", iter, logL, lam, h, eps))
}
Iter: 1 logL: -244.840829 lam: 10.270000 h: 10.170000 eps:10.170000
Iter: 2 logL: -244.840829 lam: 10.270000 h: 0.000000 eps:0.000000
end = Sys.time()
print(end - start)
Time difference of 0.03265619 secs

4 Multivariate Optimization

The approaches that we have described also extend to the multivariate setting, where we seek to optimize a function with respect to more than one parameter at the same time. More formally, we wish to find the optimum of some real‑valued function \(f(\boldsymbol{\theta})\), where \(\boldsymbol{\theta}\) is now a \(p\)-dimensional vector of parameters such that

\[ \boldsymbol{\theta} = (\theta_1, \dots, \theta_p)^T. \]

Similar to before, the estimate of \(\boldsymbol{\theta}\) at step \(t\) will be denoted as \(\boldsymbol{\theta}^{(t)}\). Most of the prior concepts covered in univariate maximization—such as the choice of starting points, iterative updating, and specification of convergence criteria for termination—also apply here.

4.1 Convergence in the multivariate setting

Obviously in the multivariate setting we cannot directly utilize the same convergence criteria defined earlier. To extend this to the multivariate setting, we may use distance‑based measures for convergence, for example based on the sum of the absolute differences between iterations

\[ D(\mathbf{u},\mathbf{v}) = \sum_{i=1}^p |u_i - v_i|, \]

or the Euclidean distance between iterations

\[ D(\mathbf{u},\mathbf{v}) = \sqrt{\sum_{i=1}^p (u_i - v_i)^2 }. \]

The output of these functions are scalars, so given a particular choice of \(D(\mathbf{u},\mathbf{v})\), we can define an absolute convergence threshold \(\epsilon\) to terminate the algorithm such that

\[ D\!\bigl(\boldsymbol{\theta}^{(t+1)}, \boldsymbol{\theta}^{(t)}\bigr) < \epsilon. \]

We can similarly define a relative convergence threshold \(\epsilon\) such that

\[ \frac{D\!\bigl(\boldsymbol{\theta}^{(t+1)}, \boldsymbol{\theta}^{(t)}\bigr)}{D\!\bigl(\boldsymbol{\theta}^{(t)}, \mathbf{0}\bigr)} < \epsilon. \]

As before, we may also decide to use the value of the objective function itself as the basis for our criterion for convergence (the estimated likelihood at iteration \(t\), for example). In some cases, particularly when the dimension of the parameter space is high, this approach may be simpler to utilize.

We will now discuss multivariate extensions to the methods introduced in the previous section.

4.2 Newton-Raphson and Fisher Scoring

Using the rationale described in the univariate setting, we can define the NR updating algorithm in this setting using a quadratic Taylor Series expansion around \(f(\boldsymbol{\theta}^*)\):

\[ f(\boldsymbol{\theta}^*) = f\bigl(\boldsymbol{\theta}^{(t)}\bigr) + \bigl(\boldsymbol{\theta}^* - \boldsymbol{\theta}^{(t)}\bigr)^T f'\bigl(\boldsymbol{\theta}^{(t)}\bigr) + \frac{1}{2} \bigl(\boldsymbol{\theta}^* - \boldsymbol{\theta}^{(t)}\bigr)^T f''\bigl(\boldsymbol{\theta}^{(t)}\bigr) \bigl(\boldsymbol{\theta}^* - \boldsymbol{\theta}^{(t)}\bigr). \]

If we take the derivative of this expansion and set it equal to zero, we get

\[ 0 = f'\bigl(\boldsymbol{\theta}^{(t)}\bigr) + f''\bigl(\boldsymbol{\theta}^{(t)}\bigr) \bigl(\boldsymbol{\theta}^* - \boldsymbol{\theta}^{(t)}\bigr), \]

which provides the update

\[ \boldsymbol{\theta}^{(t+1)} = \boldsymbol{\theta}^{(t)} - f''\bigl(\boldsymbol{\theta}^{(t)}\bigr)^{-1} f'\bigl(\boldsymbol{\theta}^{(t)}\bigr). \]

As a side note: if you recall, in our derivation from the univariate setting we started with a linear Taylor Series expansion of \(f'(\theta^*\). Doing the same here would allow us to arrive at the same expression given above (here we take the derivative of the expansion around \(f(\boldsymbol{\theta}^*)\)).

Thus, our increment \(\mathbf{h}^{(t)}\) is simply

\[ \mathbf{h}^{(t)} = - f''\bigl(\boldsymbol{\theta}^{(t)}\bigr)^{-1} f'\bigl(\boldsymbol{\theta}^{(t)}\bigr). \]

Fisher scoring in this case also has a similar updating function:

\[ \boldsymbol{\theta}^{(t+1)} = \boldsymbol{\theta}^{(t)} + I\bigl(\boldsymbol{\theta}^{(t)}\bigr)^{-1} l'\bigl(\boldsymbol{\theta}^{(t)}\bigr). \]

The properties of these algorithms will be similar to those described in the univariate section.

We will illustrate these multivariate results with an example in the next section.

4.2.1 GLMs: NR, FS, and IRLS

Generalized Linear Models (GLMs) encompass a large family of models including linear regression, logistic regression (binary responses), Poisson regression (count responses), and many others. In contrast to linear regression, the class of models encompassed by GLMs can handle response variables of different distributions, where such distributions are members of what we call the “exponential family” of distributions.

The exponential family has the general form

\[ f(y;\theta,\phi) = \exp\!\left\{ \frac{y\theta - b(\theta)}{a(\phi)} + c(y,\phi) \right\}, \]

where \(\theta\) is called the “natural” or “canonical” parameter and \(\phi\) is a fixed dispersion parameter. The following distributions can be factored into this form, and therefore belong to the exponential family:

  • Bernoulli
  • Poisson
  • Normal
  • Exponential
  • Gamma
  • Chi‑Squared
  • Beta
  • Dirichlet
  • Wishart
  • Geometric
  • Binomial (fixed number of trials)
  • Negative Binomial (fixed number of failures or fixed overdispersion parameter)
  • Multinomial (fixed number of trials)

Note: The last three distributions have conditional membership to this family, which in some cases impacts how likelihood functions based on these distributions are maximized (an example of this will be given later).

For these distributions, we can factor their densities into the form above to determine \(b(\theta)\), \(a(\phi)\), and \(c(y,\phi)\). We can then utilize these quantities to estimate the parameters of interest in our model in a general unified framework. For example, we can show that

\[ \mathbb{E}[Y] = b'(\theta) \quad \text{and} \quad \operatorname{Var}[Y] = b''(\theta)\,a(\phi). \]

Similar to linear regression, we often wish to model \(Y_i\) with respect to some vector of covariates \(\mathbf{X}_i\) (the \(i\)-th row of an \(n \times p\) matrix of covariates \(\mathbf{X}\)). Let us define \(\mu_i = \mathbb{E}[Y_i \mid \mathbf{X}_i]\). In a GLM, we assume that the relationship between \(\mathbf{X}_i\) and \(\mu_i\) is given by

\[ g(\mu_i) = \mathbf{X}_i\boldsymbol{\beta} = \eta_i, \]

where

  • \(g\) is called the link function,
  • \(\boldsymbol{\beta}\) is a vector of unknown parameters to be estimated, and
  • \(\eta_i\) is called the linear predictor.

The inverse link function, \(g^{-1}(\eta_i) = \mu_i\), can be used to back‑transform the linear predictor for subject \(i\) to obtain \(\mu_i\). That is, we model some function of \(\mathbb{E}[Y_i \mid \mathbf{X}_i]\) with a set of regression coefficients, where this function is the selected link \(g\).

For example, in linear regression \(g\) is simply the identity link where \(\mathbb{E}[Y_i \mid \mathbf{X}_i] = \mu_i = \mathbf{X}_i\boldsymbol{\beta}\), and no transformation is performed. For each member of the exponential family, different link functions may be utilized, although some may be more mathematically convenient than others, as we will see.

In general, if we find that \(b'(\theta_i) = g^{-1}(\theta_i)\), then this implies \(\theta_i = \eta_i\) and that \(g(\cdot)\) is the canonical link function, an example of such a mathematically convenient link. If this is new to you, don’t worry. You will learn GLMs in some specialized courses. We will not dive deep in the GLM.


4.3 Newton-like methods

A wide variety of “Newton‑Like” or “Quasi‑Newton” approaches exist, where the updating equation takes on a form similar to NR but usually approximates the matrix of 2nd derivatives (Hessian). Here, expressions for only the first derivatives of the function of interest are needed and analytical forms for the second derivatives are not required.

This is helpful when the second derivatives may be difficult to derive, have a complicated form, or are computationally expensive to evaluate in practice.

In this class of methods we may write the updating equation as

\[ \boldsymbol{\theta}^{(t+1)} = \boldsymbol{\theta}^{(t)} - \bigl(\mathbf{M}^{(t)}\bigr)^{-1} f'(\boldsymbol{\theta}), \]

where \(\mathbf{M}^{(t)}\) is a \(p \times p\) matrix approximating the Hessian \(f''(\boldsymbol{\theta})\) (assuming \(\boldsymbol{\theta}\) is a \(p\)-dimensional vector).

We may choose to do this because either

  1. evaluating the Hessian is too computationally expensive, or

  2. the steps selected by NR may not go uphill (if maximizing), as it is not guaranteed that \(f\bigl(\boldsymbol{\theta}^{(t+1)}\bigr) > f\bigl(\boldsymbol{\theta}^{(t)}\bigr)\) when we are maximizing some function \(f(\boldsymbol{\theta})\) (see NR divergence example from earlier). We will talk about the method of “steepest ascent/descent” later that tries to ensure improvements in the objective function with each update.

Alternatively, we can choose an \(\mathbf{M}^{(t)}\) that can guarantee \(f\bigl(\boldsymbol{\theta}^{(t+1)}\bigr) > f\bigl(\boldsymbol{\theta}^{(t)}\bigr)\) (ascent).

Due to time constraints, we will not cover Discrete Newton, Fixed Point Methods, or Gauss‑Newton in detail. However, if interested you can find these topics covered in GH 2.2.2. We will instead cover more commonly used algorithms such as BFGS (which in general has better performance than the aforementioned methods), Nelder‑Mead, gradient descent/ascent, and stochastic gradient descent/ascent.

In general, one may try to avoid calculation of the Hessian matrix by approximating it using an approach akin to the secant‑based finite‑difference approaches detailed in the univariate maximization portion of this lecture (discrete Newton or fixed point methods also do this). However, this can become computationally burdensome especially if \(\mathbf{M}\) is of larger dimension and because \(\mathbf{M}^{(t)}\) may have to be updated at each iteration to ensure faster convergence.

4.3.1 Quasi-Newton Methods: BFGS

Instead, we can use Quasi‑Newton methods where \(\mathbf{M}^{(t)}\) is updated with knowledge of the curvature of \(f\) in the direction of the proposed step \(\mathbf{h}^{(t)}\) near \(\boldsymbol{\theta}^{(t)}\) while we are performing the update

\[ \boldsymbol{\theta}^{(t+1)} = \boldsymbol{\theta}^{(t)} + \mathbf{h}^{(t)} . \]

We would like to avoid computing the approximation of each element of the Hessian matrix one‑by‑one to reduce computational burden. At the same time, we would also like to retain a similar secant‑type condition where

\[ f'\!\bigl(\boldsymbol{\theta}^{(t+1)}\bigr) - f'\!\bigl(\boldsymbol{\theta}^{(t)}\bigr) = \mathbf{M}^{(t+1)}\bigl(\boldsymbol{\theta}^{(t+1)} - \boldsymbol{\theta}^{(t)}\bigr), \]

essentially approximating the finite‑difference approach using \(\mathbf{M}^{(t+1)}\). This equation implies that the specification of some matrix \(\mathbf{M}^{(t+1)}\) times the difference in the estimate of \(\boldsymbol{\theta}\) between iterations is equal to the finite‑difference approximation, preserving the secant condition.

Essentially, we need an approach to generate \(\mathbf{M}^{(t+1)}\) from \(\mathbf{M}^{(t)}\) that minimizes the number of calculations needed and preserves the above condition.

The question is: how do we obtain an \(\mathbf{M}^{(t+1)}\) that satisfies this condition and can be determined in a computationally efficient way? Quasi‑Newton (or “variable step”) methods are one such approach that can do this when approximating the Hessian at each iteration. The update to the Hessian has the following form

\[ \begin{aligned} \mathbf{M}^{(t+1)} = \mathbf{M}^{(t)} &-\frac{\mathbf{M}^{(t)}\mathbf{z}^{(t)}\bigl(\mathbf{M}^{(t)}\mathbf{z}^{(t)}\bigr)^T} {\bigl(\mathbf{z}^{(t)}\bigr)^T\mathbf{M}^{(t)}\mathbf{z}^{(t)}} +\frac{\mathbf{y}^{(t)}\bigl(\mathbf{y}^{(t)}\bigr)^T} {\bigl(\mathbf{z}^{(t)}\bigr)^T\mathbf{y}^{(t)}} \\ &+\delta^{(t)}\bigl(\bigl(\mathbf{z}^{(t)}\bigr)^T\mathbf{M}^{(t)}\mathbf{z}^{(t)}\bigr) \mathbf{d}^{(t)}\bigl(\mathbf{d}^{(t)}\bigr)^T, \end{aligned} \]

where

\[ \mathbf{d}^{(t)} = \frac{\mathbf{y}^{(t)}}{\bigl(\mathbf{z}^{(t)}\bigr)^T\mathbf{y}^{(t)}} -\frac{\mathbf{M}^{(t)}\mathbf{z}^{(t)}}{\bigl(\mathbf{z}^{(t)}\bigr)^T\mathbf{M}^{(t)}\mathbf{z}^{(t)}}, \]

with \(\mathbf{z}^{(t)} = \boldsymbol{\theta}^{(t+1)} - \boldsymbol{\theta}^{(t)}\) and \(\mathbf{y}^{(t)} = f'\!\bigl(\boldsymbol{\theta}^{(t+1)}\bigr) - f'\!\bigl(\boldsymbol{\theta}^{(t)}\bigr)\).

This class of algorithms is indexed by the value of \(\delta^{(t)}\), where \(\delta^{(t)} = 0\) represents the popular BFGS update. BFGS is generally regarded as the best performing method in this class and is commonly used. In general, through the use of backtracking (which we will discuss in more detail in the next section) during the calculation of \(\mathbf{M}^{(t)}\), we can ensure ascent.

Another nice property of BFGS is that the positive definiteness of the Hessian approximation is also ensured, meaning that we can take the inverse of this matrix, leading to increased robustness of the method. If you recall, one of the downsides of NR in the multivariate setting is the lack of this guarantee, leading to potential instability of the approach (especially as the dimension grows). We will touch more on this later.

We won’t worry about the specific details regarding how this update was derived. Many off‑the‑shelf methods are available implementing this procedure, given a known likelihood function and 1st derivative (we will give an example of this later), so no manual implementation of this approach is necessary. Convergence is slower than NR (order is between \(1\) and \(2\)), however BFGS may be faster in terms of overall time, depending on how expensive it is to calculate the Hessian in NR.

Lastly, you may more commonly see L‑BFGS‑B in applications instead of BFGS, which is a variant of BFGS that

  1. approximates the Hessian update using a limited amount of computer memory, and

  2. can place box constraints (bounds) on the parameters being estimated.

The latter is helpful in cases where you want to prevent the algorithm from proposing updates to the parameters that are out of the range of the support for that parameter (for example, values less than or equal to zero for \(\lambda\) from our simple Poisson example).

4.3.2 Performance and stability improvement

Performance of BFGS is sensitive to the starting value of \(\mathbf{M}^{(0)}\).
- For maximum likelihood estimation, it is good to set \(\mathbf{M}^{(0)} = -I\!\bigl(\boldsymbol{\theta}^{(0)}\bigr)\), the negative expected Fisher Information Matrix.
- For other problems, setting \(\mathbf{M}^{(0)} = -\mathbf{I}_{p \times p}\) (the negative identity matrix) may work well if all parameters are on similar scales.

Rescaling parameters is generally helpful if parameters are on very different scales – for example, if you have a function

\[ f(\theta_1,\theta_2) = \exp(1 + \theta_1) + \theta_2 . \]

Clearly, similar changes in \(\theta_1\) and \(\theta_2\) will have very different impacts on \(f(\theta_1,\theta_2)\). Rescaling can also prevent the stopping criterion from being dominated by the variables that have the largest units or the most influence on the function to be maximized.

In some cases it may be easier to work with the log or exp of a parameter rather than the original scale of the parameter itself (reparameterization). Some transformations also impose a natural lower bound on the parameter space, reducing the need for box constraints. We will go into more detail on how to do this in the section on optimx in R. More information is given in Chapter 16 of the Nash book.

4.4 Gradient Descent

Depending on who you talk to, gradient descent may also be referred to as “steepest descent” (as defined in GH). Likewise, it may be called gradient ascent or “steepest ascent”, depending on whether the objective function is being maximized rather than minimized. In other circles, it may refer to a class of methods that include steepest descent/ascent as a special case.

Simply put, steepest ascent replaces \(\mathbf{M}\) in the quasi‑Newton update with the identity matrix \(\mathbf{I}\) (or \(-\mathbf{I}\) for descent), and does away with using the Hessian or any approximation of it completely. In this manner we completely avoid any computational expense of computing/approximating/storing/inverting the Hessian, which can become prohibitive in high dimensions.

That is, the update in this setting is simply

\[ \mathbf{x}^{(t+1)} = \mathbf{x}^{(t)} - \alpha^{(t)} f'\!\bigl(\mathbf{x}^{(t)}\bigr), \]

where \(f(\mathbf{x})\) is the function to be minimized. In this manner, the update takes the direction of steepest descent at \(\mathbf{x}^{(t)}\). The step size may be scaled by some value \(\alpha^{(t)} > 0\); if the direction taken at \(\mathbf{x}^{(t)}\) is negative, \(\alpha^{(t)}\) may be halved repeatedly to decrease the step size until a sufficiently small step size finds a downhill route (\(\alpha^{(t)}=1\) initially), where “downhill” is determined by the change in the objective function from the original position. This approach is called backtracking. A more general version of this approach are line search methods, where such methods ensure the change in the objective function is greater than a certain amount and other helpful qualities (detailed in GH 2.2.2.1).

For maximization, flip the sign to \(+\alpha^{(t)} f'\!\bigl(\mathbf{x}^{(t)}\bigr)\) in the above expression.

The intuition behind this approach may be described as trying to find one’s way down a hill when blindfolded. The only information available is how steep the hill is at the point where you are standing, and you move in the direction where the slope appears the steepest. We do not have an idea of the curvature (change in the slope) around that point, so we just follow the slope instead.

Gradient Descent, depending on the context, may be thought of as a more general form of steepest descent that instead fixes \(\alpha^{(t)}\) (sometimes called the “learning rate”) rather than letting it vary within each iteration. In other cases, it is referred to as a class of methods where both \(\alpha^{(t)}\) and the gradient may be scaled with each iteration.

Regardless, the idea is similar in that it avoids computing the Hessian and uses the gradient to choose the direction for the next update. For the same reasons, gradient descent is very popular for machine learning methods, particularly for high‑dimensional problems (no need to store or approximate a \(p \times p\) Hessian).

However, for very large‑sample problems the computation may become prohibitive. Standard gradient descent described above can be called batch gradient descent, where all data points are utilized for computing the next search direction. For example, in the Poisson regression model we wish to estimate \(\boldsymbol{\beta}\):

\[ \boldsymbol{\beta}^{(t+1)} = \boldsymbol{\beta}^{(t)} + \alpha^{(t)} \, l'\!\bigl(\boldsymbol{\beta}^{(t)}\bigr). \]

One thing that is clear is that

\[ l'(\boldsymbol{\beta}) = \sum_{i=1}^n \bigl( y_i - \exp(\mathbf{X}_i\boldsymbol{\beta}) \bigr) \mathbf{X}_i^T, \]

which is computed from \(n\) observations. When \(n\) is very large, these updates can become computationally prohibitive.

An important note is that the input features are often scaled in GD/SGD to put model parameters on similar ranges, because there is no inverse Hessian (or its approximation) to cancel out variations in scale seen in the gradient function due to differences in scale in the original parameters. Instead, the gradient is multiplied by a constant (the learning rate), which is the same for all elements of the gradient. This is somewhat similar to why covariates are often standardized prior to the application of penalized likelihood approaches – that way the same penalty value will approximately mean the same for different coefficients in the model.

4.5 Stochastic Gradient Descent (SGD)

Stochastic gradient descent instead randomly subsamples the data and performs an update for \(\boldsymbol{\beta}\) with a random observation drawn from the data. Rather than updating \(\boldsymbol{\beta}\) with a full pass over the data, we instead update \(\boldsymbol{\beta}\) with a single draw from the data, such that now

\[ \boldsymbol{\beta}^{(s+1)} = \boldsymbol{\beta}^{(s)} - \alpha \, l'_{i(s)}\!\bigl(\boldsymbol{\beta}^{(s)}\bigr), \]

where \(s > 0\) is the subsample index, \(i(s)\) is the index of the sample that is randomly drawn in the \(s\)-th subsample, and \(l'_{i(s)}\!\bigl(\boldsymbol{\beta}^{(s)}\bigr)\) is the value of the gradient computed from the \(i\)-th subject.

One continues to draw samples until convergence is observed in \(\boldsymbol{\beta}^{(s+1)}\), which may take multiple loops over the entire data set. Other variants include in‑order looping across observations rather than random sampling.

Modifications of this approach include mini‑batching, where instead of a single sample drawn from the dataset, a group of \(m\) samples is drawn. This reduces variability between iterations and assists with convergence. While the theoretical convergence rate of SGD is lower than that of GD (SGD is said to be sub‑linear), in practice for large‑\(n\) problems it may be much faster, as the per‑iteration cost for SGD is much lower than GD. In any case, SGD is helpful in those situations when one cannot read all of the data into memory.

This is also why GD (as defined in the previous section) is sometimes called Batch Gradient Descent, as it requires looping over the entire dataset for each update rather than a subsample (mini‑batch).

Given the variability from sample to sample, \(\alpha^{(t)}\) is often held fixed rather than varied; however, the convergence and speed depend heavily on the optimal selection of this fixed value. In deep learning and other methods, this value is often determined through hyperparameter tuning (tuning parameter selection). Too large a learning rate may lead to problems with convergence, and too small may lead to slow convergence.

Other approaches may automatically adjust \(\alpha^{(t)}\) as the algorithm runs, decreasing \(\alpha^{(t)}\) by some preset schedule (sometimes called a “learning rate schedule”). This approach may address some of the shortcomings of fixing \(\alpha^{(t)}\), but it is highly dependent on the parameters that determine the rate of decrease, as they are not data‑adaptive.

To address these issues and others, more sophisticated methods for determining \(\alpha^{(t)}\) or scaling the gradient during GD/SGD have been proposed, such as the ADAM and ADAGRAD optimizers. More detail on these approaches can be found in the references (https://arxiv.org/abs/1609.04747).

4.6 Nelder-Mead

In certain situations we may find that neither the first nor the second derivative may be easily derived or computationally easy to evaluate. Nelder–Mead, sometimes called the “Simplex Search Method”, is one such algorithm that only requires the specification and evaluation of the objective function \(f(\boldsymbol{\theta})\) in order to perform optimization. Hence, this algorithm falls into the class of iterative direct search algorithms because it only evaluates the function at possible solutions to suggest a better place for the algorithm to move in the parameter space.

In the maximum likelihood estimation context, this means that only the likelihood function is evaluated during optimization. This method may also be applied in more general situations where an objective function exists that you wish to maximize or minimize but where clear analytical derivatives are not available.

Prior descriptions of this algorithm describe it as “amoeba-like”, where the algorithm sequentially updates the “best” set of model parameters, crawling uphill or downhill on the objective function surface until it reaches an optimal set of parameters. The specific algorithm and its development are somewhat out of the scope of this course, but we will cover it briefly here.

The main idea behind this approach is similar to the prior two classes of algorithms covered so far: the algorithm starts from an initial set of guesses for the parameter estimates and then iteratively updates until some convergence criterion is met.

The basic idea behind this algorithm is based upon the definition of the simplex. The simplex contains \(p+1\) vertices, each a \(p\)-dimensional vector representing a particular combination of the \(p\) parameter values. The method begins with an initial simplex constructed from a starting point \(\boldsymbol{\theta}^{(0)}\) through a predefined initialization rule. These vertices are then ranked from best to worst in terms of their function value, and in each iteration the worst-ranked vertex is updated.

In general, the worst vertex is moved toward the direction of the better vertices in the simplex. In this iterative fashion, the vertices slowly move toward regions of higher (or lower) objective‑function value until no further update yields an appreciable improvement. The simplex may also shrink in size to better encapsulate promising regions when proposed moves do not result in significant gains.

Overall, at each iteration the algorithm attempts to improve the worst point in the simplex (which corresponds to one particular parameter vector in the original model).

The details of this updating procedure are given in GH 2.2.4. (https://github.com/OscarRunsCode/Comp-Stat/blob/main/Computational%20Statistics%2C%20Second%20Edition-Geof%20H.%20Givens%2C%20Jennifer%20A.%20Hoeting.pdf) We illustrate the process graphically in the video below, which shows the optimization of a two‑parameter model.

LS0tDQp0aXRsZTogIk9wdGltaXphdGlvbiBUZWNobmlxdWVzOiBOdW1lcmljYWwgTWV0aG9kcyINCmF1dGhvcjogIkNoZW5nIFBlbmciDQpkYXRlOiAiV2VzdCBDaGVzdGVyIFVuaXZlcnNpdHkiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvY19jb2xsYXBzZWQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHNtb290aF9zY3JvbGw6IHllcw0KICAgIGhpZ2hsaWdodDogbW9ub2Nocm9tZQ0KICAgIHRoZW1lOiBzcGFjZWxhYg0KICBwZGZfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgZmlnX3dpZHRoOiAzDQogICAgZmlnX2hlaWdodDogMw0KICB3b3JkX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBrZWVwX21kOiB5ZXMNCmVkaXRvcl9vcHRpb25zOiANCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCmBgYHtjc3MsIGVjaG8gPSBGQUxTRX0NCiNUT0M6OmJlZm9yZSB7DQogIGNvbnRlbnQ6ICJUYWJsZSBvZiBDb250ZW50cyI7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBmb250LXNpemU6IDEuMmVtOw0KICBkaXNwbGF5OiBibG9jazsNCiAgY29sb3I6IG5hdnk7DQogIG1hcmdpbi1ib3R0b206IDEwcHg7DQp9DQoNCg0KZGl2I1RPQyBsaSB7ICAgICAvKiB0YWJsZSBvZiBjb250ZW50ICAqLw0KICAgIGxpc3Qtc3R5bGU6dXBwZXItcm9tYW47DQogICAgYmFja2dyb3VuZC1pbWFnZTpub25lOw0KICAgIGJhY2tncm91bmQtcmVwZWF0Om5vbmU7DQogICAgYmFja2dyb3VuZC1wb3NpdGlvbjowOw0KfQ0KDQpoMS50aXRsZSB7ICAgIC8qIGxldmVsIDEgaGVhZGVyIG9mIHRpdGxlICAqLw0KICBmb250LXNpemU6IDIycHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBjb2xvcjogRGFya1JlZDsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7DQp9DQoNCmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogIGZvbnQtc2l6ZTogMTVweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBuYXZ5Ow0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQoNCmg0LmRhdGUgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICBmb250LXNpemU6IDE4cHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7DQogIGNvbG9yOiBEYXJrQmx1ZTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KDQpoMSB7IC8qIEhlYWRlciAxIC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogICAgZm9udC1zaXplOiAyMHB4Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBkYXJrcmVkOw0KICAgIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCg0KaDIgeyAvKiBIZWFkZXIgMiAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoMyB7IC8qIEhlYWRlciAzIC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogICAgZm9udC1zaXplOiAxNnB4Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmg0IHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDE0cHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBkYXJrcmVkOw0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCi8qIEFkZCBkb3RzIGFmdGVyIG51bWJlcmVkIGhlYWRlcnMgKi8NCi5oZWFkZXItc2VjdGlvbi1udW1iZXI6OmFmdGVyIHsNCiAgY29udGVudDogIi4iOw0KDQpib2R5IHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KDQouaGlnaGxpZ2h0bWUgeyBiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgfQ0KDQpwIHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KDQp9DQpgYGANCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQojIGNvZGUgY2h1bmsgc3BlY2lmaWVzIHdoZXRoZXIgdGhlIFIgY29kZSwgd2FybmluZ3MsIGFuZCBvdXRwdXQgDQojIHdpbGwgYmUgaW5jbHVkZWQgaW4gdGhlIG91dHB1dCBmaWxlcy4NCmlmICghcmVxdWlyZSgia25pdHIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KICAgbGlicmFyeShrbml0cikNCn0NCmlmICghcmVxdWlyZSgicGFuZGVyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBhbmRlciIpDQogICBsaWJyYXJ5KHBhbmRlcikNCn0NCmlmICghcmVxdWlyZSgiZ2dwbG90MiIpKSB7DQogIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQ0KICBsaWJyYXJ5KGdncGxvdDIpDQp9DQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7DQogIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQogIGxpYnJhcnkodGlkeXZlcnNlKQ0KfQ0KDQppZiAoIXJlcXVpcmUoInBsb3RseSIpKSB7DQogIGluc3RhbGwucGFja2FnZXMoInBsb3RseSIpDQogIGxpYnJhcnkocGxvdGx5KQ0KfQ0KaWYgKCFyZXF1aXJlKCJmaXRkaXN0cnBsdXMiKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJmaXRkaXN0cnBsdXMiKQ0KICBsaWJyYXJ5KGZpdGRpc3RycGx1cykNCn0NCiMjIGxpYnJhcnkoZml0ZGlzdHJwbHVzKQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCAgICAgICAjIGluY2x1ZGUgY29kZSBjaHVuayBpbiB0aGUgb3V0cHV0IGZpbGUNCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsICAgIyBzb21ldGltZXMsIHlvdSBjb2RlIG1heSBwcm9kdWNlIHdhcm5pbmcgbWVzc2FnZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgeW91IGNhbiBjaG9vc2UgdG8gaW5jbHVkZSB0aGUgd2FybmluZyBtZXNzYWdlcyBpbg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRoZSBvdXRwdXQgZmlsZS4gDQogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IFRSVUUsICAgICMgeW91IGNhbiBhbHNvIGRlY2lkZSB3aGV0aGVyIHRvIGluY2x1ZGUgdGhlIG91dHB1dA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluIHRoZSBvdXRwdXQgZmlsZS4NCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BDQogICAgICAgICAgICAgICAgICAgICAgKSAgDQpgYGANCg0KXA0KDQoNCiMgSW50cm9kdWN0aW9uDQoNCkEgY29tbW9uIHRhc2sgaW4gc3RhdGlzdGljcyBpcyBtYXhpbWl6aW5nIChvciBtaW5pbWl6aW5nKSBjb21wbGV4IHVuaXZhcmlhdGUgb3IgbXVsdGl2YXJpYXRlIGZ1bmN0aW9ucy4gVGhpcyBpcyB0eXBpY2FsbHkgZG9uZSBpbiB0aGUgY29udGV4dCBvZiBtYXhpbWl6aW5nIGxpa2VsaWhvb2QgZnVuY3Rpb25zIHdpdGggcmVzcGVjdCB0byBhIHZlY3RvciBvZiB1bmtub3duIHBhcmFtZXRlcnMsIGdpdmVuIHRoZSBvYnNlcnZlZCBkYXRhLg0KDQpUaGUgdGVybSAicGFyYW1ldGVyIGVzdGltYXRpb24iIG1heSBiZSBvbmUgdGhhdCB5b3UgbWF5IGhhdmUgY29tbW9ubHkgaGVhcmQgaW4gdGhlIHBhc3QgZHVyaW5nIGRpc2N1c3Npb25zIG9mIGZpdHRpbmcgc3RhdGlzdGljYWwgbW9kZWxzIHRvIGRhdGEuIE1vcmUgZ2VuZXJhbGx5LCB0aGlzIHRhc2sgcGVydGFpbnMgdG8gYW4gIm9wdGltaXphdGlvbiIgcHJvYmxlbSwgd2hlcmUgd2Ugc2VlayB0byBmaW5kIGEgc2V0IG9mIHBhcmFtZXRlciB2YWx1ZXMgdGhhdCBtaW5pbWl6ZXMgb3IgbWF4aW1pemVzIHNvbWUgcHJlLWRlZmluZWQgb2JqZWN0aXZlIGZ1bmN0aW9uLiBXaGVuIHRoaXMgb2JqZWN0aXZlIGZ1bmN0aW9uIGlzIGEgbG9nIGxpa2VsaWhvb2QgZnVuY3Rpb24sIHRoaXMgcmVkdWNlcyB0byB0aGUgcHJvYmxlbSBvZiBtYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdGlvbi4NCg0KQXMgd2Ugd2lsbCBzZWUgaW4gdGhpcyBsZWN0dXJlLCB0aGVyZSBhcmUgbWFueSBwb3RlbnRpYWwgYXBwcm9hY2hlcyB0byB1c2UgZm9yIG9wdGltaXphdGlvbiBpbiB0aGlzIGNvbnRleHQuIEhvdyBiZXN0IHRvIG9wdGltaXplIGEgZnVuY3Rpb24gb2YgaW50ZXJlc3QgZGVwZW5kcyBvbiB0aGUgbmF0dXJlIG9mIHRoZSBmdW5jdGlvbiB0byBiZSBvcHRpbWl6ZWQsIGFzIHdlbGwgYXMgcHJhY3RpY2FsIGNvbmNlcm5zIHJlZ2FyZGluZyBhIGNhbmRpZGF0ZSBwcm9jZWR1cmUgdG8gYmUgdXNlZC4gQXMgc3VjaCwgYW5vdGhlciBnb2FsIG9mIHRoaXMgbGVjdHVyZSBpcyB0byBleHBsYWluIHRoZSBwcm9zIGFuZCBjb25zIG9mIGRpZmZlcmVudCBhcHByb2FjaGVzIHRvIGhlbHAgbmFycm93IGRvd24gd2hpY2ggYXBwcm9hY2ggbWF5IGJlc3QgZm9yIHlvdXIgcGFydGljdWxhciBwcm9ibGVtLg0KDQpPdXIgZGlzY3Vzc2lvbiBvZiBvcHRpbWl6YXRpb24gaW4gdGhpcyBsZWN0dXJlIHdpbGwgZ2l2ZSBhIGJyb2FkIG92ZXJ2aWV3IG9mIGV4aXN0aW5nIGFwcHJvYWNoZXMgZm9yIHVuY29uc3RyYWluZWQgb3B0aW1pemF0aW9uLCB0b3VjaGluZyB0aGUgbWFueSBvZiB0aGUgc3BlY2lhbCBjYXNlcyB0aGF0IG1heSBoYXZlIGJlZW4gY292ZXJlZCBpbiBwcmV2aW91cyBjb3Vyc2V3b3JrLiBJbiBhIGxhdGVyIGxlY3R1cmUgd2Ugd2lsbCBkaXNjdXNzIHRoZSB0b3BpYyBvZiAiY29uc3RyYWluZWQiIG9wdGltaXphdGlvbiwgd2hlcmUgdGhlIHBvdGVudGlhbCBzZXQgb2YgYWxsb3dhYmxlIHNvbHV0aW9ucyBpcyBubyBsb25nZXIgdW5yZXN0cmljdGVkLiBXZSBmb2N1cyBvbiB0aGUgY2xhc3Mgb2YgcHJvYmxlbXMgd2hlcmUgdGhlIGZ1bmN0aW9uIHRvIGJlIG9wdGltaXplZCBpcyBzbW9vdGgsIHJlYWwgdmFsdWVkLCBhbmQgaXMgZGlmZmVyZW50aWFibGUuDQoNCg0KIyBMaWtlbGlvb2QgRnVuY3Rpb24gT3B0aW1pemF0aW9uDQoNCg0KIyMgRXhhbXBsZSBkYXRhDQoNCkxldCB1cyBhc3N1bWUgdGhhdCB3ZSBoYXZlIG9idGFpbmVkIGEgcmFuZG9tIHNhbXBsZSBvZiAxMDAgaW5kaXZpZHVhbHMgZnJvbSBXQ1UuIExldCB1cyBkZWZpbmUgJFxtYXRoYmZ7eX0gPSAoeV8xLCBcbGRvdHMsIHlfezEwMH0pJCwgd2hlcmUgJHlfaSQgcGVydGFpbnMgdG8gdGhlIG1lYXN1cmVkIGhlaWdodCBvZiB0aGUgJGkkdGggaW5kaXZpZHVhbCBpbiBpbmNoZXMsIGZvciAkaSA9IDEsIFxsZG90cywgMTAwJC4NCg0KTGV0IHVzIGZ1cnRoZXIgYXNzdW1lIHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGVzZSBoZWlnaHRzIGlzIGRpc3RyaWJ1dGVkIG5vcm1hbGx5IHdpdGggc29tZSB1bmtub3duIG1lYW4gJFxtdSQgYW5kIGtub3duIHZhcmlhbmNlICRcc2lnbWFeMiA9IDEwJC4gVGhhdCBpcywgd2UgYXNzdW1lIHRoYXQgJHlfaSBcc2ltIE4oXG11LCBcc2lnbWFeMikkLCB3aGVyZSAkXHNpZ21hXjIgPSAxMCQuDQoNCg0KQSBjb21tb24gcXVlc3Rpb24gaW4gdGhpcyBzaXR1YXRpb24gaXMgd2hhdCBpcyB0aGUgImJlc3QiIGVzdGltYXRlIG9mICRcbXUkIGdpdmVuIHRoZSBkYXRhLCBhbmQgaG93IGRvIHdlIG9idGFpbiBpdD8gQW5zd2VyaW5nIHRoaXMgcXVlc3Rpb24gY2FuIGRlcGVuZCBvbiB0aGUgZG9tYWluIG9mIHRoZSBxdWVzdGlvbiBiZWluZyBhc2tlZC4NCg0KDQpGcm9tIGludHJvZHVjdG9yeSBzdGF0aXN0aWNhbCBjb3Vyc2VzLCBvbmUgbWF5IHJlbWVtYmVyIHRoYXQgYW4gZXN0aW1hdG9yIGZvciAkXG11JCwgc2F5ICRcaGF0e1xtdX0kLCBpbiB0aGlzIHNldHRpbmcgbWF5IGJlICRcYmFye3l9JCwgb3IgdGhlIG1lYW4gb2YgdGhlIG9ic2VydmVkIGhlaWdodHMuIEluIG1vcmUgYWR2YW5jZWQgc3RhdGlzdGljYWwgY291cnNlcywgeW91IG1heSBoYXZlIHNob3duIHdoeSB0aGlzIGlzIHRoZSBjYXNlIHZpYSBhbmFseXRpY2FsbHkgZGVyaXZpbmcgdGhlIG1heGltdW0gbGlrZWxpaG9vZCBlc3RpbWF0b3IgZm9yICRcbXUkLCBhbmQgcHJvdmluZyBpdHMgb3B0aW1hbGl0eSByZWxhdGl2ZSB0byBvdGhlciBwb3RlbnRpYWwgZXN0aW1hdG9ycy4NCg0KDQpXZSBjYW4gYWxzbyByZS1leHByZXNzIHRoZSBvcmlnaW5hbCBmb3JtIG9mIHRoaXMgcHJvYmxlbSBhcyBhbiBpbnRlcmNlcHQtb25seSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbDogJHlfaSA9IFhfaVxiZXRhICsgXGVwc2lsb25faSQgd2hlcmUgJFxlcHNpbG9uX2kgXHNpbSBOKDAsIFxzaWdtYV4yKSQsICRYX2kgPSAxJCwgYW5kICRcYmV0YSA9IFxtdSQsIGEgc2NhbGFyIHBhcmFtZXRlciBpbiB0aGlzIHNldHRpbmcuIEFnYWluLCAkXHNpZ21hXjIkIGlzIGtub3duIGluIHRoaXMgY2FzZS4gR2l2ZW4gdGhpcywgd2UgY2FuIHNob3cgdGhhdCBhbiBlc3RpbWF0ZSBmb3IgJFxiZXRhJCAoJFxtdSQpIGlzIGFsc28gJFxiYXJ7eX0kLiBCYXNlZCBvbiB0aGUgbm9ybWFsIGVxdWF0aW9ucyBmcm9tIHRoZSBsaW5lYXIgbW9kZWwgZnJhbWV3b3JrLCB3ZSBoYXZlIGNsb3NlZCBmb3JtIHNvbHV0aW9uICRcaGF0e1xiZXRhfSA9IChYJ1gpXnstMX1YJ3kkLCB3aGljaCwgcGx1Z2dpbmcgaW4gJFgkIGFuZCAkeSQgZnJvbSB0aGlzIHNpdHVhdGlvbiwgYWxzbyB5aWVsZHMgJFxiYXJ7eX0kLg0KDQpSZWdhcmRsZXNzIG9mIHRoZSBhcHByb2FjaCB1c2VkIGhlcmUsIHdlIGNhbiBzaG93IHRoYXQgd2UgY2FuIG9idGFpbiAkXGhhdHtcbXV9JCBhbmFseXRpY2FsbHkgdmlhIG9wdGltaXppbmcgc29tZSBmdW5jdGlvbiBvZiB0aGUgZGF0YSB3aXRoIHJlc3BlY3QgdG8gdGhlIHBhcmFtZXRlciBvZiBpbnRlcmVzdCAkXG11JC4gSW4gdGhlIGxpbmVhciByZWdyZXNzaW9uIGNhc2UsIHdlIGFyZSBtaW5pbWl6aW5nIHRoZSByZXNpZHVhbCBzdW0gb2Ygc3F1YXJlcyBvZiB0aGUgbW9kZWwgd2l0aCByZXNwZWN0IHRvICRcYmV0YSQgKCRcbXUkKSwgYW5kIGluIHRoZSBsYXR0ZXIsIHdlIGFyZSBtYXhpbWl6aW5nIHRoZSBsaWtlbGlob29kIGZ1bmN0aW9uIGFzc3VtZWQgZm9yIHRoZSBkYXRhIHdpdGggcmVzcGVjdCB0byAkXG11JC4NCg0KIyMgTGlrZWxpaG9vZCBGdW5jdGlvbg0KDQpUaGUgbGlrZWxpaG9vZCBmdW5jdGlvbiAkTChcdGhldGEpJCwgZ2l2ZW4gdGhlIGFzc3VtcHRpb25zIHdlIG1hZGUsIGZvciB0aGlzIHNhbXBsZSBjYW4gYmUgd3JpdHRlbiBhcw0KDQokJA0KTChcdGhldGEpID0gZih5XzEgXG1pZCBcbXUsIFxzaWdtYV4yKSBmKHlfMiBcbWlkIFxtdSwgXHNpZ21hXjIpIFxkb3RzIGYoeV97MTAwfSBcbWlkIFxtdSwgXHNpZ21hXjIpDQo9IFxwcm9kX3tpPTF9XnsxMDB9IGYoeV9pIFxtaWQgXG11LCBcc2lnbWFeMikNCj0gXGZyYWN7MX17KDJccGlcc2lnbWFeMilee24vMn19IGVeey1cZnJhY3sxfXsyXHNpZ21hXjJ9IFxzdW1fe2k9MX1eezEwMH0gKHlfaSAtIFxtdSleMn0NCiQkDQoNCkhlcmUsICRcdGhldGEgPSBcbXUkIHNpbmNlIHdlIGFyZSBhc3N1bWluZyB0aGF0ICRcc2lnbWFeMiQgaXMga25vd24uIFRha2luZyBsb2dzIG9mIGJvdGggc2lkZXMsIHdlIGNhbiB3cml0ZSB0aGUgb3B0aW1pemF0aW9uIG9mICRMKFx0aGV0YSkkIGFib3ZlIGluIHRlcm1zIG9mIHRoZSBvcHRpbWl6YXRpb24gb2YgJGwoXHRoZXRhKSQsIHRoZSBsb2ctbGlrZWxpaG9vZCwgYXMgaXQgaXMgYSBtb25vdG9uaWMgdHJhbnNmb3JtYXRpb24gb2YgdGhlIG9yaWdpbmFsIGxpa2VsaWhvb2QgZnVuY3Rpb24uDQoNCioqV2h5IGRvZXMgdGhpcyBtYXR0ZXI/KiogVGhpcyBpbXBsaWVzIHRoYXQgdGhlIG1heGltdW0gb2Ygb25lIGZ1bmN0aW9uIHdpbGwgYmUgdGhlIG1heGltdW0gb2YgdGhlIG90aGVyIGZ1bmN0aW9uLCBhcyB0aGUgbG9nIHRyYW5zZm9ybWF0aW9uIGlzIGEgb25lLXRvLW9uZSBtYXBwaW5nIGZyb20gdGhlIGxpa2VsaWhvb2QgdG8gdGhlIGxvZy1saWtlbGlob29kIChleGFtcGxlIGluIGZpZ3VyZSBiZWxvdykuIFVzaW5nIGFwcHJvYWNoZXMgZnJvbSBwcmlvciBjb3Vyc2VzLCB3ZSBjYW4gc2hvdyB0aGF0IHRoaXMgZnVuY3Rpb24gaXMgc21vb3RoIGFuZCBjb252ZXggaW4gJFx0aGV0YSQsIGFuZCB0aGVyZWZvcmUgdGhlIG1heGltdW0gb2YgdGhlIGxvZy1saWtlbGlob29kIChhbmQgaGVuY2UgdGhlIGxpa2VsaWhvb2QpIHdpbGwgYmUgYXR0YWluZWQgYXQgdGhlIHZhbHVlIG9mICRcdGhldGEkIHdoZXJlIHRoZSBmaXJzdCBkZXJpdmF0aXZlIG9mICRsKFx0aGV0YSkkIGlzIGVxdWFsIHRvIHplcm8uDQoNCkluIHRoZSBjdXJyZW50IHByb2JsZW0sIHRoZSBNTEUgZm9yICRcbXUkLCAkXGhhdHtcbXV9ID0gXGJhcnt5fSQsIGlzIHRoZW4gb2J0YWluZWQgYnkgc2V0dGluZyB0aGUgMXN0IGRlcml2YXRpdmUgb2YgdGhlIGxvZy1saWtlbGlob29kIChhbHNvIGtub3duIGFzIHRoZSAqKnNjb3JlIGZ1bmN0aW9uKiopIGVxdWFsIHRvIDAgYW5kIHRoZW4gc29sdmluZyBmb3IgJFxtdSQuIEluIG90aGVyIHdvcmRzLCB3ZSBmaW5kIHRoZSByb290IG9mIHRoZSBzY29yZSBmdW5jdGlvbiB3aXRoIHJlc3BlY3QgdG8gJFxtdSQuIEZvciB0aGUgcmVncmVzc2lvbiBiYXNlZCBhcHByb2FjaCwgd2UgYXJlIHNpbXBseSBtaW5pbWl6aW5nIHRoZSByZXNpZHVhbCBzdW0gb2Ygc3F1YXJlcyAoUlNTKSwgZGVmaW5lZCBhcyAkXHN1bV97aT0xfV57MTAwfSAoeV9pIC0gXG11KV4yJC4NCg0KV2Uga25vdyB0aGF0IHRoaXMgbG9nIHRyYW5zZm9ybWF0aW9uIG9mdGVuIGZhY2lsaXRhdGVzIHRoZSBjYWxjdWxhdGlvbiBvZiBkZXJpdmF0aXZlcyB3aGVuIGFuYWx5dGljYWxseSBzb2x2aW5nIGZvciAkXG11JC4gVGhpcyBhcHByb2FjaCBvZiB1c2luZyBhbm90aGVyIGZ1bmN0aW9uIGFzIGEgcHJveHkgb2YgdGhlIG9yaWdpbmFsIGZ1bmN0aW9uIHRvIHNpbXBsaWZ5IG1heGltaXphdGlvbiBjYW4gYmUgZm91bmQgaW4gc2V2ZXJhbCBleGFtcGxlcyBvZiBvcHRpbWl6YXRpb24gd2Ugd2lsbCBjb3ZlciB0aGlzIHNlbWVzdGVyLg0KDQojIyBWaXN1YWxpemluZyB0aGUgT3B0aW1pemF0aW9uIFByb2JsZW0gZm9yICRcbXUkDQoNCg0KV2UgY2FuIGlsbHVzdHJhdGUgdGhpcyBhcHByb2FjaCB1c2luZyB0aGUgZm9sbG93aW5nIHNpbXVsYXRpb24gZXhhbXBsZToNCg0KYGBge3J9DQptdSA8LSA2Nw0Kc2lnbWEyIDwtIDEwDQpuIDwtIDEwMA0KDQojIGdlbmVyYXRlIHJhbmRvbSBub3JtYWxseSBkaXN0cmlidXRlZCBzYW1wbGVzDQpzZXQuc2VlZCgxMjMpICMgZm9yIHJlcHJvZHVjaWJpbGl0eQ0KeSA8LSBybm9ybShuLCBtdSwgc2QgPSBzcXJ0KHNpZ21hMikpDQoNCiMgcHJpbnQgTUxFIG9mIG11LCBtdV9oYXQNCmNhdCgibWVhbiBvZiB5XG4iKQ0KbWVhbih5KQ0KDQojIHByaW50IGVzdGltYXRlIG9mIG11IGluIGxpbmVhciBtb2RlbCBmcmFtZXdvcmsgKGludGVyY2VwdCBvbmx5IG1vZGVsKQ0KY2F0KCJcbmJldGFfaGF0IGZyb20gaW50ZXJjZXB0IG9ubHkgbGluZWFyIG1vZGVsXG4iKQ0KbG0oeSB+IDEpJGNvZWYNCmBgYA0KDQpOb3csIGxldHMgd3JpdGUgc29tZSBmdW5jdGlvbnMgdG8gaGVscCBnZW5lcmF0ZSBzb21lIGZpZ3VyZXMgaW4gdGhlIG5leHQgY29kZSBjaHVuay4gVGhlc2UgZnVuY3Rpb25zIHdpbGwgaGVscCBwbG90IHRoZSBsaWtlbGlob29kLCBsb2cgbGlrZWxpaG9vZCwgYW5kIFJTUyB3aXRoIHJlc3BlY3QgdG8gJFxtdSQgYmFzZWQgb24gdGhlIHNldHVwIGFib3ZlLg0KDQpgYGB7cn0NCiMgcmV0dXJucyBhIHZlY3RvciBvZiBsaWtlbGlob29kIHZhbHVlcyBvdmVyIGEgZ2l2ZW4gcmFuZ2Ugb2YgbXUgdmFsdWVzDQojIGhlbHBzIGZvciBwbG90dGluZyBsYXRlcg0KbGlrIDwtIGZ1bmN0aW9uKG11LCBzaWdtYTIsIHkpIHsNCiAgIyBjcmVhdGUgZW1wdHkgdmVjdG9yDQogIGxpa2VsaWhvb2QudmVjdG9yIDwtIHJlcChOQSwgbGVuZ3RoKG11KSkNCiAgDQogICMgZm9yIGVhY2ggbXUsIGNhbGN1bGF0ZSB0aGUgbGlrZWxpaG9vZCB2YWx1ZQ0KICBmb3IgKGkgaW4gMTpsZW5ndGgobGlrZWxpaG9vZC52ZWN0b3IpKSB7DQogICAgbGlrZWxpaG9vZC52ZWN0b3JbaV0gPC0gcHJvZChkbm9ybSh5LCBtdVtpXSwgc3FydChzaWdtYTIpKSkNCiAgfQ0KICANCiAgIyByZXR1cm4gdmVjdG9yIG9mIGxpa2VsaWhvb2QgdmFsdWVzIHNwYW5uaW5nIHJhbmdlIG9mIG11DQogIHJldHVybihsaWtlbGlob29kLnZlY3RvcikNCn0NCg0KIyBkZWZpbmUgdGhlIGxvZy1saWtlbGlob29kIGZ1bmN0aW9uIHdpdGggcmVzcGVjdCB0byBtdQ0KbG9nbGlrIDwtIGZ1bmN0aW9uKG11LCBzaWdtYTIsIHkpIHsNCiAgIyBjcmVhdGUgZW1wdHkgdmVjdG9yDQogIGxvZy5saWtlbGlob29kLnZlY3RvciA8LSByZXAoTkEsIGxlbmd0aChtdSkpDQogIA0KICAjIGNhbGN1bGF0ZSBsb2cgbGlrZWxpaG9vZA0KICBmb3IgKGkgaW4gMTpsZW5ndGgobG9nLmxpa2VsaWhvb2QudmVjdG9yKSkgew0KICAgIGxvZy5saWtlbGlob29kLnZlY3RvcltpXSA8LSBzdW0oZG5vcm0oeSwgbXVbaV0sIHNxcnQoc2lnbWEyKSwgbG9nID0gVFJVRSkpDQogIH0NCiAgDQogIHJldHVybihsb2cubGlrZWxpaG9vZC52ZWN0b3IpDQp9DQoNCiMgZGVmaW5lIHRoZSByZXNpZHVhbCBzdW0gb2Ygc3F1YXJlcyBmdW5jdGlvbiB3aXRoIHJlc3BlY3QgdG8gbXUNClJTUyA8LSBmdW5jdGlvbihtdSwgc2lnbWEyLCB5KSB7DQogICMgY3JlYXRlIGVtcHR5IHZlY3Rvcg0KICBSU1MudmVjdG9yIDwtIHJlcChOQSwgbGVuZ3RoKG11KSkNCiAgDQogICMgY2FsY3VsYXRlIGxvZyBsaWtlbGlob29kDQogIGZvciAoaSBpbiAxOmxlbmd0aChSU1MudmVjdG9yKSkgew0KICAgIFJTUy52ZWN0b3JbaV0gPC0gc3VtKCh5IC0gbXVbaV0pIF4gMikNCiAgfQ0KICANCiAgcmV0dXJuKFJTUy52ZWN0b3IpDQp9DQpgYGANCg0KDQpOb3cgdGhhdCB3ZSBnb3QgdGhhdCBvdXQgb2YgdGhlIHdheSwgbGV0cyBwbG90IGVhY2ggb2YgdGhlc2UgZnVuY3Rpb25zIHdpdGggcmVzcGVjdCB0byAkXG11JDoNCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD00fQ0KIyByYW5nZSBvZiBtdSB0byBwbG90IG92ZXINCnJhbmdlIDwtIHNlcSg0NSwgODUsIGxlbmd0aC5vdXQgPSAxMDAwKQ0KDQojIGFycmFuZ2UgdGhlIHR3byBwbG90cyBpbiAxIHJvdywgdGhyZWUgY29sdW1ucw0KcGFyKG1mcm93ID0gYygxLCAzKSkNCg0KIyBsaWtlbGlob29kIHBsb3QNCnBsb3QoDQogIHJhbmdlLA0KICBsaWsobXUgPSByYW5nZSwgc2lnbWEyID0gc2lnbWEyLCB5ID0geSksDQogIHR5cGUgPSAibCIsDQogIHlsYWIgPSAibGlrZWxpaG9vZCIsDQogIHhsYWIgPSAibXUiLA0KICBtYWluID0gIkxpa2VsaWhvb2QgRnVuY3Rpb24iDQopDQoNCiMgYWRkIHZlcnRpY2FsIGxpbmUgYXQgdGhlIG1lYW4gb2YgeQ0KYWJsaW5lKHYgPSBtZWFuKHkpLCBsdHkgPSAyLCBjb2wgPSAiZ3JleSIpDQoNCiMgbG9nIGxpa2VsaWhvb2QgcGxvdA0KcGxvdCgNCiAgcmFuZ2UsDQogIGxvZ2xpayhtdSA9IHJhbmdlLCBzaWdtYTIgPSBzaWdtYTIsIHkgPSB5KSwNCiAgdHlwZSA9ICJsIiwNCiAgeWxhYiA9ICJsb2cgbGlrZWxpaG9vZCIsDQogIHhsYWIgPSAibXUiLA0KICBtYWluID0gIkxvZy1MaWtlbGlob29kIEZ1bmN0aW9uIg0KKQ0KDQojIHZlcnRpY2FsIGxpbmUgYXQgdGhlIG1lYW4gb2YgeQ0KYWJsaW5lKHYgPSBtZWFuKHkpLCBsdHkgPSAyLCBjb2wgPSAiZ3JleSIpDQoNCiMgUlNTIHBsb3QNCnBsb3QoDQogIHJhbmdlLA0KICBSU1MobXUgPSByYW5nZSwgc2lnbWEyID0gc2lnbWEyLCB5ID0geSksDQogIHR5cGUgPSAibCIsDQogIHlsYWIgPSAiUlNTIiwNCiAgeGxhYiA9ICJtdSIsDQogIG1haW4gPSAiUmVzaWR1YWwgU3VtIG9mIFNxdWFyZXMiDQopDQoNCiMgdmVydGljYWwgbGluZSBhdCB0aGUgbWVhbiBvZiB5DQphYmxpbmUodiA9IG1lYW4oeSksIGx0eSA9IDIsIGNvbCA9ICJncmV5IikNCg0KIyByZXNldCBwYXINCnBhcihtZnJvdyA9IGMoMSwgMSkpDQoNCmBgYA0KDQoNCldlIGNhbiBjbGVhcmx5IHNlZSB0aGF0IHRoZSBtYXhpbXVtIG9mIHRoZSBsaWtlbGlob29kLCBsb2ctbGlrZWxpaG9vZCwgYW5kIFJTUyBmdW5jdGlvbnMgb2NjdXJzIGF0IHRoZSBNTEUgb2YgzrwsICRcaGF0e1xtdX0gPSByIHJvdW5kKG1lYW4oeSksIDUpJC4gQXQgdGhlIE1MRSwgd2UgY2FuIHNlZSB0aGF0IHRoZSBkZXJpdmF0aXZlIG9mIGVhY2ggb2YgdGhlIGZ1bmN0aW9ucyBhcmUgZXF1YWwgdG8gemVybywgZm9sbG93aW5nIHRoZSBpbnR1aXRpb24gb2YgdGhlIHByb2NlZHVyZXMgdXNlZCB0byBzb2x2ZSBmb3IgJFxoYXR7XG11fSQuIFdlIHdpbGwgc2VlIHRoYXQgbWFueSBvZiB0aGUgYWxnb3JpdGhtcyBwcm9wb3NlZCBpbiB0aGlzIHNlY3Rpb24gYXJlIGJhc2VkIHVwb24gdGhpcyBwcmluY2lwbGUgd2hlbiB0cnlpbmcgdG8gZmluZCB0aGUgbWluaW11bS9tYXhpbXVtIG9mIGEgZnVuY3Rpb24gd2hlbiBubyBjbG9zZWQgZm9ybSBzb2x1dGlvbnMgYXJlIGF2YWlsYWJsZS4NCg0KDQoNCiMjIE1MRSBvZiAkXGxhbWJkYSQgaW4gUG9pc3Nvbg0KDQpXZSBrbm93IHRoYXQgdGhlIFBNRiBmb3IgYSBQb2lzc29uIHJhbmRvbSB2YXJpYWJsZSAkWSQgd2l0aCBtZWFuICRcbGFtYmRhJCBtYXkgYmUgd3JpdHRlbiBhcyAkXHRleHR7UG99KFk9eXxcbGFtYmRhKSA9IFxmcmFje2Veey1cbGFtYmRhfVxsYW1iZGFeeX17eSF9JCwgd2hlcmUgJHkkIGlzIGEgZHJhdyBmcm9tIHRoaXMgZGlzdHJpYnV0aW9uLiBTdXBwb3NlIHdlIGRyYXcgYSBzYW1wbGUgb2YgJG4kIG9ic2VydmF0aW9ucyBmcm9tIHRoaXMgZGlzdHJpYnV0aW9uIGFuZCB3ZSB3aXNoIHRvIGRldGVybWluZSB0aGUgTUxFIG9mICRcbGFtYmRhJCwgJFxoYXR7XGxhbWJkYX0kLg0KDQpBcyBkaXNjdXNzZWQgaW4gcHJpb3IgY291cnNlcywgdGhlIE1MRSBjYW4gYmUgb2J0YWluZWQgYnkgc2ltcGx5IHRha2luZyB0aGUgZmlyc3QgZGVyaXZhdGl2ZSBvZiB0aGUgbGlrZWxpaG9vZCAob3IgbW9yZSBlYXNpbHksIHRoZSBsb2cgbGlrZWxpaG9vZCksIHNldHRpbmcgaXQgZXF1YWwgdG8gMCwgYW5kIHRoZW4gc29sdmluZyBmb3IgdGhlIHBhcmFtZXRlciBvZiBpbnRlcmVzdC4NCg0KVGhlIGxvZyBsaWtlbGlob29kIGhlcmUgaXMgJGwoXGxhbWJkYSkgPSAtblxsYW1iZGEgKyBcbGVmdChcc3VtX3tpPTF9Xm4geV9pXHJpZ2h0KVxsb2coXGxhbWJkYSkgLSBcc3VtX3tpPTF9Xm4gXGxvZyh5X2khKSQuIFRoZW4sIHRha2luZyB0aGUgZmlyc3QgZGVyaXZhdGl2ZSBvZiB0aGUgbG9nIGxpa2VsaWhvb2QsIGFsc28ga25vd24gYXMgdGhlIHNjb3JlIGZ1bmN0aW9uLCB3ZSB0aGVuIHNvbHZlIHRoZSBmb2xsb3dpbmc6ICQwID0gLW4gKyAoXHN1bV97aT0xfV5uIHlfaSkvXGxhbWJkYSQgd2hpY2ggZ2l2ZXMgdGhlIE1MRSAkXGhhdHtcbGFtYmRhfSA9IChcc3VtX3tpPTF9Xm4geV9pKS9uJC4NCg0KSW4gdGhpcyBleGFtcGxlLCBhcyBpbiB0aGUgcHJpb3Igb25lLCB3ZSBjYW4gZGVyaXZlIGNsb3NlZCBmb3JtIHNvbHV0aW9ucyB0byBvYnRhaW4gYSBwYXJhbWV0ZXIgZXN0aW1hdGUgZm9yICRcbGFtYmRhJCBieSBtYXhpbWl6aW5nIGxvZy1saWtlbGlob29kIGZ1bmN0aW9uLiBZb3UgY2FuIGltYWdpbmUgdGhlIHRhc2sgb2Ygb3B0aW1pemF0aW9uIGFzIG5vdCB0b28gZGlmZmVyZW50IGZyb20gdGhhdCBvZiByb290IGZpbmRpbmcuIEluIHRoZSBjb250ZXh0IG9mIG1heGltdW0gbGlrZWxpaG9vZCBlc3RpbWF0aW9uLCB0aGlzIGlzIGFraW4gdG8gZmluZGluZyB0aGUgcm9vdCBvZiB0aGUgc2NvcmUgZXF1YXRpb24uDQoNCkhvd2V2ZXIsIHdoYXQgaWYgd2UgZG8gbm90IGhhdmUgc3VjaCBuaWNlIGNsb3NlZC1mb3JtIHNvbHV0aW9ucyBmb3IgdGhlIG1vZGVsIGF0IGhhbmQ/DQoNCg0KIyBVbml2YXJpYXRlIE9wdGltaXphdGlvbg0KDQoNCkluIHRoaXMgbW9kdWxlIHdlIHdpbGwgZGlzY3VzcyBzb21lIGZ1bmRhbWVudGFsIG1ldGhvZHMgZm9yIHRoZSBvcHRpbWl6YXRpb24gb2Ygc21vb3RoIG5vbmxpbmVhciBmdW5jdGlvbnMgKEdpdmVucyBhbmQgSG9ldGluZyBDaGFwdGVyIDIpLCBiZWdpbm5pbmcgd2l0aCBzaW5nbGUgdW5pdmFyaWF0ZSBmdW5jdGlvbnMgYW5kIHRoZW4gZGlzY3Vzc2luZyBleHRlbnNpb25zIHRvIHRoZSBtdWx0aXZhcmlhdGUgY2FzZS4gQ29tYmluYXRvcmlhbCBvcHRpbWl6YXRpb24gKEdpdmVucyBhbmQgSG9ldGluZyBDaGFwdGVyIDMpIHdpbGwgbm90IGJlIGVtcGhhc2l6ZWQgaGVyZSBidXQgc3R1ZGVudHMgY2FuIHJlYWQgdGhpcyBjaGFwdGVyIGlmIGludGVyZXN0ZWQuIFdlIHdpbGwgYWxzbyBjb3ZlciB0aGUgRU0gYWxnb3JpdGhtIChHaXZlbnMgYW5kIEhvZXRpbmcgQ2hhcHRlciA0KSBpbiBtb3JlIGRldGFpbCBpbiBhIGxhdGVyIGxlY3R1cmUuIFRoZXJlIHdlIHdpbGwgZGlzY3VzcyB0aGUgb3B0aW1pemF0aW9uIG9mIGZ1bmN0aW9ucyB3aXRoIG1pc3NpbmcgZGF0YSBhbmQgYWxzbyBob3cgdGhlIGFsZ29yaXRobSBoYXMgYmVlbiB3aWRlbHkgYXBwbGllZCB0byBnZW5lcmFsIHByb2JsZW1zIGluIHN0YXRpc3RpY3MuDQoNCkxldCB1cyBjb25zaWRlciB0aGUgbnVtZXJpY2FsIG9wdGltaXphdGlvbiBvZiB0aGUgZm9sbG93aW5nIGZ1bmN0aW9uICRmKFx0aGV0YSkkLCB3aGljaCB3ZSBzZWVrIHRvIG1heGltaXplIHdpdGggcmVzcGVjdCB0byAkXHRoZXRhJDogDQoNCiQkDQpmKFx0aGV0YSkgPSBcZnJhY3tcbG9nKFx0aGV0YSl9ezErXHRoZXRhfQ0KJCQuIA0KDQoNClRoZSB0cmFkaXRpb25hbCBhcHByb2FjaCB0byBtYXhpbWl6aW5nICRmKFx0aGV0YSkkLCBzdWNoIGFzIHRoZSBvbmUgZGVzY3JpYmVkIGluIHRoZSBwcmV2aW91cyBleGFtcGxlLCBpcyBkaWZmaWN1bHQgdG8gYXBwbHkgaGVyZSBhcyB0aGUgYW5hbHl0aWNhbCBzb2x1dGlvbiBmb3IgJFx0aGV0YSQgYWZ0ZXIgc2V0dGluZyAkZicoXHRoZXRhKSA9IDAkIGlzIG5vdCBlYXN5IHRvIGRldGVybWluZS4NCg0KSGVyZSwgDQoNCg0KJCQNCmYnKFx0aGV0YSkgPSBcZnJhY3sxK1x0aGV0YSAtIFx0aGV0YVxsb2coXHRoZXRhKX17XHRoZXRhKDErXHRoZXRhKV4yfQ0KJCQuIA0KDQpTby4uLm5vdCBlYXN5IHRvIHNvbHZlIGZvci4NCg0KTGV0J3MgZmlyc3QgcGxvdCB0aGlzIGZ1bmN0aW9uIHRvIGRldGVybWluZSBpdHMgc2hhcGU6DQoNCmBgYHtyfQ0KIyBkZWZpbmUgZnVuY3Rpb24NCmYxIDwtIGZ1bmN0aW9uKHRoZXRhKSB7DQogIGxvZyh0aGV0YSkgLyAoMSArIHRoZXRhKQ0KfQ0KDQojIG1ha2UgYSBwbG90IHdpdGggdGhpcyBmdW5jdGlvbiwgd2hlcmUgdGhlIHgtYXhpcyByYW5nZXMgZnJvbSAwIHRvIDEwDQpwbG90KA0KICBmMSwNCiAgZnJvbSA9IDAuMSwgICMgc3RhcnQgZnJvbSAwLjEgdG8gYXZvaWQgbG9nKDApDQogIHRvID0gMTAsDQogIHlsYWIgPSAiZih0aGV0YSkiLA0KICB4bGFiID0gInRoZXRhIiwNCiAgbWFpbiA9IGV4cHJlc3Npb24oZih0aGV0YSkgPT0gZnJhYyhsb2codGhldGEpLCAxICsgdGhldGEpKSwNCiAgbHdkID0gMg0KKQ0KDQojIGFkZCBncmlkIGZvciBiZXR0ZXIgdmlzdWFsaXphdGlvbg0KZ3JpZCgpDQpgYGANCg0KDQpQbG90dGluZyB0aGlzIGZ1bmN0aW9uLCB0aGUgbWF4aW11bSBvZiAkZihcdGhldGEpJCBhcHBlYXJzIHRvIGJlIGJldHdlZW4gMyBhbmQgNC4gU28gaG93IGV4YWN0bHkgZG8gd2UgZmluZCB0aGUgbWF4aW11bSBvZiB0aGlzIGZ1bmN0aW9uLCBhbmQgdGhlIHZhbHVlIG9mICRcdGhldGEkIHRoYXQgcGVydGFpbnMgdG8gdGhlIG1heGltdW0sICRcaGF0e1x0aGV0YX0kPw0KDQoNCiMjIEJhc2ljcyBvZiBOdW1lcmljYWwgT3B0aW1pemF0aW9uDQoNCk1vc3QgbnVtZXJpY2FsIG9wdGltaXphdGlvbiBtZXRob2RzIGFyZSBpdGVyYXRpdmUgYW5kIHJlbHkgb24gdGhlIGNvbmNlcHQgb2Ygc3VjY2Vzc2l2ZSBhcHByb3hpbWF0aW9ucyB0byBmaW5kIHRoZSB2YWx1ZSBvZiAkXHRoZXRhJCBwZXJ0YWluaW5nIHRvIHRoZSBtaW5pbXVtIG9yIG1heGltdW0gb2YgdGhlIGZ1bmN0aW9uIG9mIGludGVyZXN0LiBTdWNoIGFsZ29yaXRobXMgcmVxdWlyZSBhIHN0YXJ0aW5nIHZhbHVlICRcdGhldGFeeygwKX0kLCBhbiBpbml0aWFsIGd1ZXNzIGZvciAkXGhhdHtcdGhldGF9JC4NCg0KRnJvbSB0aGlzIHBvaW50LCBhbiBhbGdvcml0aG0gd2lsbCBpdGVyYXRpdmVseSB1cGRhdGUgaXRzIGd1ZXNzIGZvciAkXGhhdHtcdGhldGF9JCBiYXNlZCBvbiBhIHByZWRlZmluZWQgdXBkYXRpbmcgZm9ybXVsYS4gRWFjaCB1cGRhdGUgbWF5IGJlIHJlZmVycmVkIHRvIGFuICJpdGVyYXRpb24iLg0KDQoNCklkZWFsbHksIHRoZSB2YWx1ZSBvZiAkXGhhdHtcdGhldGF9IFx0byBcdGhldGFeKiQgYXMgJHQgXHRvIFxpbmZ0eSQsIHdoZXJlICR0JCBpcyB0aGUgaXRlcmF0aW9uIG51bWJlciBzdWNoIHRoYXQgJHQgPiAwJCBhbmQgJFx0aGV0YV4qJCBpcyB0aGUg4oCcdHJ1ZeKAnSB2YWx1ZSBvZiAkXHRoZXRhJCB3aGVyZSB0aGUgbWF4aW11bSBvZiAkZihcdGhldGEpJCBpcyBhY2hpZXZlZC4gVGhhdCBpcywgYXMgdGhlIG51bWJlciBvZiBpdGVyYXRpb25zIGluY3JlYXNlcywgd2UgaG9wZSB0aGF0ICRcaGF0e1x0aGV0YX0kIHdpbGwgYXBwcm9hY2ggaXRzIHRydWUgb3B0aW11bSAkXHRoZXRhXiokLCBvciBjb252ZXJnZSB0byAkXHRoZXRhXiokLg0KDQpIb3dldmVyLCBnaXZlbiB0aGUgaXRlcmF0aXZlIHVwZGF0aW5nIG5hdHVyZSBvZiB0aGVzZSBhbGdvcml0aG1zLCAkXGhhdHtcdGhldGF9JCB3aWxsIG5ldmVyIGVxdWFsIHRoZSB2YWx1ZSBvZiAkXHRoZXRhXiokIGV4YWN0bHksIGFuZCB3aWxsIG9ubHkgYXBwcm9hY2ggJFx0aGV0YV4qJCBhcyAkdCQgaW5jcmVhc2VzLiBBcyBhIHJlc3VsdCwgd2UgbmVlZCB0byBkZWZpbmUgY3JpdGVyaWEgb3Igc3RvcHBpbmcgcnVsZXMgaW5kaWNhdGluZyB0aGF0IHRoZSBhbGdvcml0aG0gaGFzIGNvbnZlcmdlZCwgbWVhbmluZyB0aGF0ICRcaGF0e1x0aGV0YX0kIGlzIHJlYXNvbmFibHkgY2xvc2UgdG8gdGhlIHByaW9yIGVzdGltYXRlIG9mICRcaGF0e1x0aGV0YX0kIHdpdGhpbiBzb21lIHRvbGVyYW5jZSBvciBkaXN0YW5jZS4NCg0KSW4gc29tZSBjYXNlcywgdGhlIGFsZ29yaXRobSBtYXkgY29udmVyZ2UgdG8gc29tZSB2YWx1ZSBvdGhlciB0aGFuICRcdGhldGFeKiQsIG9yIG1heSBub3QgY29udmVyZ2UgYXQgYWxsLiBXZSB3aWxsIGRpc2N1c3MgdGhlc2Ugc2l0dWF0aW9ucyBsYXRlciBpbiB0aGlzIGxlY3R1cmUuDQoNCg0KIyMjIEdyYXBoaWNhbCBJbGx1c3RyYXRpb24NCg0KVG8gaWxsdXN0cmF0ZSBhbiBhcHBsaWNhdGlvbiBvZiBudW1lcmljYWwgb3B0aW1pemF0aW9uIHRvICRmKFx0aGV0YSkkIChpbnRyb2R1Y2VkIGVhcmxpZXIpLCBzZWUgdGhlIGZvbGxvd2luZyBwbG90LiBIZXJlLCB3ZSB1dGlsaXplZCB0aGUgY29tbW9ubHkgdXNlZCBOZXd0b24tUmFwaHNvbiBhbGdvcml0aG0gdG8gb3B0aW1pemUgJGYoXHRoZXRhKSQuIEluIGl0LCB3ZSBjaG9vc2UgYSBzdGFydGluZyB2YWx1ZSAkXHRoZXRhXnsoMCl9ID0gMC4yNSQgdG8gaW5pdGlhbGl6ZSB0aGUgYWxnb3JpdGhtLg0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aD0iODAlIn0NCmluY2x1ZGVfZ3JhcGhpY3MoIkdyYXBoaWNhbElsbHVzdHJhdGlvbjAxLmdpZiIpDQpgYGANCg0KWW91IGNhbiBzZWUgdGhhdCB0aGUgYWxnb3JpdGhtIGFwcGVhcnMgdG8gY29udmVyZ2UgaW4gYXBwcm94aW1hdGVseSAxMiBpdGVyYXRpb25zIHRvIGAzLjU5MTEyYCAodGhlIHZhbHVlIG9mICRcdGhldGEkIHBlcnRhaW5pbmcgdG8gdGhlIHRydWUgbWF4aW11bSBvZiAkZihcdGhldGEpJCksIHdoZXJlIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHZhbHVlIG9mICRcaGF0e1x0aGV0YX0kIGF0IHRoZSAxMXRoIHN0ZXAgKCRcaGF0e1x0aGV0YX1eeygxMSl9JCkgYW5kIHRoZSAxMnRoIHN0ZXAgKCRcaGF0e1x0aGV0YX1eeygxMil9JCkgaXMgdmVyeSBzbWFsbCwgYW5kIG91ciBmaW5hbCBlc3RpbWF0ZSBmb3IgJFxoYXR7XHRoZXRhfV57KDExKX0kIGlzIGAzLjU5MTEyYC4NCg0KDQoNCg0KIyMjIENyaXRlcmlhIGZvciBjb252ZXJnZW5jZQ0KDQpJZiB3ZSBkZWZpbmUgb3VyIHRvbGVyYW5jZSBmb3IgY29udmVyZ2VuY2UsIG9yIGNvbnZlcmdlbmNlIHRocmVzaG9sZCwgYXQgc3RlcCAkdCQgYXMgc29tZSBzbWFsbCBmaXhlZCB2YWx1ZSAkXGVwc2lsb24kLCB3ZSBtYXkgZm9ybWFsbHkgdGVybWluYXRlIHRoZSBhbGdvcml0aG0gd2hlbiAkfFxoYXR7XHRoZXRhfV57KHQpfSAtIFxoYXR7XHRoZXRhfV57KHQtMSl9fCA8IFxlcHNpbG9uJC4gSGVyZSwgdGhlIG9wdGltYWwgdmFsdWUgb2YgJFx0aGV0YSQgaXMgJFx0aGV0YV4qID0gMy41OTExMiQuDQoNCkNsZWFybHksIGxhcmdlciB2YWx1ZXMgb2YgJFxlcHNpbG9uJCB3aWxsIHJlc3VsdCBpbiBjb252ZXJnZW5jZSBpbiBmZXdlciBpdGVyYXRpb25zOyBob3dldmVyLCB0aGUgcmVzdWx0aW5nICRcaGF0e1x0aGV0YX0kIG1heSBiZSBsZXNzIGFjY3VyYXRlIChmdXJ0aGVyIGF3YXkgZnJvbSAkXHRoZXRhXiokKS4gRGVwZW5kaW5nIG9uIHRoZSBhcHBsaWNhdGlvbiwgYSBoaWdoZXIgb3IgbG93ZXIgY29udmVyZ2VuY2UgdGhyZXNob2xkIG1heSBiZSB3YXJyYW50ZWQsIHdoZXJlIGluIHNvbWUgY2FzZXMgMTAgZGVjaW1hbCBwbGFjZXMgb2YgYWNjdXJhY3kgKGFuZCBwb3RlbnRpYWxseSByZXF1aXJpbmcgbWFueSBtb3JlIGl0ZXJhdGlvbnMgZm9yIG1lZXRpbmcgdGhlIGNvbnZlcmdlbmNlIHRocmVzaG9sZCkgbWF5IG5vdCBiZSBuZWNlc3NhcnkuDQoNCk90aGVyIGNyaXRlcmlhIGZvciBkZXRlcm1pbmluZyBjb252ZXJnZW5jZSBleGlzdC4gWW91IG1heSBub3RpY2UgZnJvbSB0aGUgZGVmaW5pdGlvbiBhYm92ZSB0aGF0IHRoZSBhbGdvcml0aG0gY29udmVyZ2VzIGlmIHRoZSByYXcgZGlmZmVyZW5jZSBpbiAkXHRoZXRhXnsodCsxKX0kIGFuZCAkXHRoZXRhXnsodCl9JCBiZWNvbWVzIHNtYWxsLiBIb3dldmVyLCBpZiAkXHRoZXRhXiokIGlzIGdlbmVyYWxseSBsYXJnZSBpbiBzY2FsZSAoc2F5LCB0eXBpY2FsbHkgJD4gMTAwMDAkIGluIHZhbHVlKSwgYW4gJFxlcHNpbG9uJCBvZiAkMTBeey02fSQgbWF5IGJlIHRvbyByZXN0cmljdGl2ZSBvciBkaWZmaWN1bHQgdG8gcmVhY2gsIHdoZXJlYXMgaWYgJFx0aGV0YV4qJCBpcyBpbiB0aGUgcmFuZ2Ugb2YgJDEwXnstMTJ9JOKAkyQxMF57LTE2fSQsIGFuICRcZXBzaWxvbiQgb2YgJDEwXnstNn0kIG1heSBzaW1wbHkgYmUgdG9vIGxpYmVyYWwuIEFsbCBpbiBhbGwsIHRoZSB0aHJlc2hvbGTigJlzIHN0cmluZ2VuY3kgbWF5IGRlcGVuZCBvbiB0aGUgc2NhbGUgb2YgJFx0aGV0YV4qJC4NCg0KRm9yIHRoZXNlIHJlYXNvbnMsIHNvbWUgbWF5IHByZWZlciBhIHJlbGF0aXZlIGNvbnZlcmdlbmNlIHRocmVzaG9sZCwgc3VjaCBhcw0KDQokJA0KXGZyYWN7fFx0aGV0YV57KHQrMSl9IC0gXHRoZXRhXnsodCl9fH17fFx0aGV0YV57KHQpfXx9IDwgXGVwc2lsb24sDQokJA0KDQp3aGVyZSBjb252ZXJnZW5jZSBpcyBub3cgbWV0IGlmIHRoZSBjaGFuZ2UgcmVsYXRpdmUgdG8gJFx0aGV0YV57KHQpfSQgaXMgc21hbGxlciB0aGFuIHNvbWUgcHJvcG9ydGlvbiAkXGVwc2lsb24kLiBGb3IgZXhhbXBsZSwgd2UgbWF5IGluc3RlYWQgd2FudCB0byB0ZXJtaW5hdGUgdGhlIGFsZ29yaXRobSB3aGVuIHRoZSBjaGFuZ2UgaXMgbGVzcyB0aGFuLCBzYXksIDElIG9mIHRoZSBwcmV2aW91cyB2YWx1ZSwgcmF0aGVyIHRoYW4gc3BlY2lmeWluZyBhbiBleGFjdCB2YWx1ZSBvZiB0aGUgZGlmZmVyZW5jZS4gSG93ZXZlciwgcmVsYXRpdmUgY29udmVyZ2VuY2UgY3JpdGVyaWEgY2FuIGJlY29tZSBwcm9ibGVtYXRpYyBpZiAkXHRoZXRhXnsodCl9JCBvciAkXHRoZXRhXiokIGlzIHZlcnkgY2xvc2UgdG8gemVyby4gSW4gdGhhdCBjYXNlLCBhbiBhbHRlcm5hdGl2ZSBjcml0ZXJpb24NCg0KJCQNClxmcmFje3xcdGhldGFeeyh0KzEpfSAtIFx0aGV0YV57KHQpfXx9e3xcdGhldGFeeyh0KX18ICsgXGVwc2lsb259IDwgXGVwc2lsb24NCiQkDQoNCm1heSBoZWxwIHByZXZlbnQgaW5zdGFiaWxpdHkgaW4gdGhlIGNyaXRlcmlvbi4NCg0KSXQgaXMgYWxzbyBoZWxwZnVsIHRvIGFkZCBhIG1heGltdW0gaXRlcmF0aW9uIGxpbWl0IHRvIHByZXZlbnQgcnVuYXdheSBkaXZlcmdlbmNlIG9yIHRvIGhhbHQgcG9vcmx5IGNvbnZlcmdpbmcgYWxnb3JpdGhtcyAoc29tZXRpbWVzIGluZGljYXRpdmUgb2YgYmFkIHN0YXJ0aW5nIHBvaW50cykuIFRoaXMgbGltaXQgbWF5IGFsc28gYmUgcmVhY2hlZCBpZiB5b3VyIGNvbnZlcmdlbmNlIHRocmVzaG9sZCBpcyB0b28gc3RyaW5nZW50LCBhbmQvb3IgdGhlIGl0ZXJhdGlvbiBsaW1pdCBpcyB0b28gbG93LiBQcmludGluZyBhIHdhcm5pbmcgbWVzc2FnZSB3aGVuIHRoaXMgbGltaXQgaXMgcmVhY2hlZCBpcyBjb21tb24gYW1vbmcgYWxnb3JpdGhtcy4NCg0KDQpJbiBzdW1tYXJ5LCB3aGVuIGltcGxlbWVudGluZyBvcHRpbWl6YXRpb24gYWxnb3JpdGhtcywgd2UgbmVlZCBjcml0ZXJpYSB0byBkZXRlcm1pbmUgd2hlbiB0byBzdG9wIHRoZSBpdGVyYXRpb25zLiBDb21tb24gY3JpdGVyaWEgaW5jbHVkZToNCg0KKiAqKkFic29sdXRlIGNvbnZlcmdlbmNlKio6IFN0b3Agd2hlbiAkfFx0aGV0YV57KGsrMSl9IC0gXHRoZXRhXnsoayl9fCA8IFxlcHNpbG9uJCwgd2hlcmUgJFxlcHNpbG9uJCBpcyBhIHNtYWxsIHRvbGVyYW5jZS4NCg0KKiAqKlJlbGF0aXZlIGNvbnZlcmdlbmNlKio6IFN0b3Agd2hlbiAkXGZyYWN7fFx0aGV0YV57KGsrMSl9IC0gXHRoZXRhXnsoayl9fH17fFx0aGV0YV57KGspfXx9IDwgXGVwc2lsb24kLg0KDQoqICoqRnVuY3Rpb24gdmFsdWUgY29udmVyZ2VuY2UqKjogU3RvcCB3aGVuICR8ZihcdGhldGFeeyhrKzEpfSkgLSBmKFx0aGV0YV57KGspfSl8IDwgXGVwc2lsb24kLg0KDQoNCiMjIyBJbXBhY3Qgb2Ygc3RhcnRpbmcgdmFsdWVzIG9uIGNvbnZlcmdlbmNlDQoNClRoZSBjaG9pY2Ugb2Ygc3RhcnRpbmcgdmFsdWVzIGNhbiBzaWduaWZpY2FudGx5IGltcGFjdCBjb252ZXJnZW5jZS4gTGV0J3MgZGVtb25zdHJhdGUgdGhpcyB3aXRoIGEgc2ltcGxlIGV4YW1wbGU6DQoNCkxldHMgbm93IGV4YW1pbmUgdGhlIGltcGFjdCBvZiBhIGRpZmZlcmVudCBzdGFydGluZyBwb2ludCBmb3IgdGhlIGFsZ29yaXRobSwgJFx0aGV0YV57KDApfSA9IDEkDQoNCmBgYHtyIGVjaG8gPSBGQUxTRSwgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGg9IjgwJSJ9DQppbmNsdWRlX2dyYXBoaWNzKCJ0aGV0YW9lcTEuZ2lmIikNCmBgYA0KDQokXHRoZXRhXnsoMCl9ID0gNCQNCg0KYGBge3IgZWNobyA9IEZBTFNFLCBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aD0iODAlIn0NCmluY2x1ZGVfZ3JhcGhpY3MoInRoZXRhb2VxNC5naWYiKQ0KYGBgDQoNCiRcdGhldGFeeygwKX0gPSA4JA0KDQpgYGB7ciBlY2hvID0gRkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoPSI4MCUifQ0KaW5jbHVkZV9ncmFwaGljcygidGhldGFvZXE4LmdpZiIpDQpgYGANCg0KDQoNCkluIHNvbWUgY2FzZXMsIHRoZSBlbmQgcmVzdWx0IG9mIHRoZSBhbGdvcml0aG0gaXMgdGhlIHNhbWUgcmVnYXJkbGVzcyBvZiB0aGUgY2hvc2VuIHN0YXJ0aW5nIHBvaW50IG9mIHRoZSBhbGdvcml0aG07IGhvd2V2ZXIsIGZld2VyIGl0ZXJhdGlvbnMgbWF5IGJlIHJlcXVpcmVkIGZvciAkXHRoZXRhXnsodCl9IFx0byBcdGhldGFeKiQgKGR1ZSB0byBhIGJldHRlciBzdGFydGluZyBwb2ludCkuIEluIG90aGVyIGNhc2VzLCB5b3UgY2FuIHNlZSB0aGF0IHRoZSBtb2RlbCBtYXkgY29udmVyZ2UgdG8gYSB2YWx1ZSBvdGhlciB0aGFuICRcdGhldGFeKiQsIHJlcHJlc2VudGluZyBhICpsb2NhbCBvcHRpbXVtKiByYXRoZXIgdGhhbiB0aGUgKmdsb2JhbCBvcHRpbXVtKi4gT3IsIHRoZSBhbGdvcml0aG0gbWF5IG5vdCBjb252ZXJnZSBhdCBhbGwgKGRpdmVyZ2VzKSwgYXMgaW4gdGhlIGxhc3QgZXhhbXBsZSBhYm92ZS4NCg0KSW4gZ2VuZXJhbCwgdXRpbGl6aW5nIGEgc3RhcnRpbmcgdmFsdWUgY2xvc2VyIHRvICRcdGhldGFeKiQgcmVzdWx0cyBpbiBjb252ZXJnZW5jZSBpbiBmZXdlciBpdGVyYXRpb25zLiBUaGlzIGlsbHVzdHJhdGVzIHRoZSBpbXBvcnRhbmNlIG9mIGNob29zaW5nIGFuIGluZm9ybWF0aXZlIHN0YXJ0aW5nIHBvaW50IGZvciB0aGUgYWxnb3JpdGhtLCBhbmQgdGhlcmUgYXJlIHNldmVyYWwgc3RyYXRlZ2llcyB0byBkbyB0aGlz4oCUc3RyYXRlZ2llcyB0aGF0IGNhbiB2YXJ5IGJ5IGFwcGxpY2F0aW9uLg0KDQpFdmFsdWF0aW5nIHNldmVyYWwgc3RhcnRpbmcgcG9pbnRzIChwZXJoYXBzIGNob3NlbiByYW5kb21seSkgYW5kIGNob29zaW5nIHRoZSBzdGFydGluZyBwb2ludCB0aGF0IGVuZHMgaW4gdGhlIGJlc3QgdmFsdWUgb2YgdGhlIG9iamVjdGl2ZSBmdW5jdGlvbiBpcyB0aGUgYmVzdCB3YXkgdG8gYXZvaWQgZ2V0dGluZyB0cmFwcGVkIGluIGEgbG9jYWwgb3B0aW11bSBvciBoaXR0aW5nIGNvbnZlcmdlbmNlIGZhaWx1cmVzLiBPdGhlciBzdHJhdGVnaWVzIGluY2x1ZGU6ICANCg0KLSAqKkdyYXBoaW5nL3Bsb3R0aW5nKiogdGhlIG9iamVjdGl2ZSBmdW5jdGlvbiAgDQoNCi0gKipQcmVsaW1pbmFyeSBlc3RpbWF0ZXMqKiAoYmFzZWQgb24gc2ltcGxlciBtb2RlbHMsIG9yIG1ldGhvZC1vZi1tb21lbnRzIGVzdGltYXRlcykgDQoNCi0gKipFZHVjYXRlZCBndWVzc2VzKiogZGVyaXZlZCBmcm9tIGRvbWFpbiBrbm93bGVkZ2UgIA0KDQotICoqVHJpYWwgYW5kIGVycm9yKiogdG8gZmluZCByZWFzb25hYmxlIGluaXRpYWwgdmFsdWVzDQoNCg0KDQoNCiMjIyBCYXNpY3Mgb2YgTnVtZXJpY2FsIE9wdGltaXphdGlvbiBTdW1tYXJ5DQoNClRvIHN1bW1hcml6ZSwgdGhlIGZvbGxvd2luZyBjb25jZXB0cyBhcmUgaW1wb3J0YW50IGZvciB1bmRlcnN0YW5kaW5nIHRoZSBzZXQgb2Ygb3B0aW1pemF0aW9uIGFsZ29yaXRobXMgY292ZXJlZCBpbiB0aGUgcmVzdCBvZiB0aGlzIG1vZHVsZToNCg0KKiBPcHRpbWl6YXRpb24gYWxnb3JpdGhtcyBhcmUgaXRlcmF0aXZlDQoNCiogVGhleSByZXF1aXJlIHN0YXJ0aW5nIHZhbHVlcw0KDQoqIENvbnZlcmdlbmNlIGNyaXRlcmlhIGRldGVybWluZSB3aGVuIHRvIHN0b3ANCg0KKiBUaGUgY2hvaWNlIG9mIGFsZ29yaXRobSBkZXBlbmRzIG9uIHRoZSBwcm9ibGVtIGNoYXJhY3RlcmlzdGljcw0KDQoqIEZvciB1bml2YXJpYXRlIG9wdGltaXphdGlvbiwgbWV0aG9kcyBsaWtlIGdvbGRlbiBzZWN0aW9uIHNlYXJjaCwgcGFyYWJvbGljIGludGVycG9sYXRpb24sIG9yIGdyYWRpZW50LWJhc2VkIG1ldGhvZHMgY2FuIGJlIHVzZWQNCg0KSW4gdGhlIG5leHQgZmV3IHNlY3Rpb25zIHdlIHdpbGwgZ28gaW50byBkZXRhaWwgcmVnYXJkaW5nIHZhcmlvdXMgYXBwcm9hY2hlcyBmb3IgbnVtZXJpY2FsIG9wdGltaXphdGlvbiBhbmQgdGhlaXIgcHJvcyBhbmQgY29ucyBmb3IgdXNlLiBXZSB3aWxsIGFsc28gZGlzY3VzcyBhbGdvcml0aG0tc3BlY2lmaWMgYXNwZWN0cyBvZiBjb21wdXRhdGlvbmFsIGNvbXBsZXhpdHkgYW5kIGNvbnZlcmdlbmNlIHNwZWVkIG9mIGVhY2ggYXBwcm9hY2ggdG8gaGVscCB5b3UgZGVjaWRlIHdoaWNoIGFsZ29yaXRobSBtYXkgYmUgYmV0dGVyIHRvIHV0aWxpemUgZm9yIHlvdXIgc3BlY2lmaWMgYXBwbGljYXRpb24uDQoNCg0KDQojIyBOZXd0b24tUmFwaHNvbg0KDQpJbiB0aGUgcHJldmlvdXMgaWxsdXN0cmF0aW9uIHdlIHVzZWQgYSBjb21tb24gdGVjaG5pcXVlIGNhbGxlZCAqKk5ld3RvbuKAk1JhcGhzb24gKE5SKSoqIHRvIG9wdGltaXplIHRoZSBmdW5jdGlvbiBvZiBpbnRlcmVzdCB3aXRoIHJlc3BlY3QgdG8gJFx0aGV0YSQuIFRoaXMgYXBwcm9hY2ggaXMgYWxzbyByZWZlcnJlZCB0byBhcyAqKk5ld3RvbuKAmXMgTWV0aG9kKiosIGFuZCBpcyBhbiBpdGVyYXRpdmUgdXBkYXRpbmcgc2NoZW1lIHRvIGFycml2ZSBhdCAkXGhhdHtcdGhldGF9JC4NCg0KT25lIG5vdGFibGUgcmVxdWlyZW1lbnQgb2YgdGhpcyBtZXRob2QgaXMgdGhhdCBleHByZXNzaW9ucyBmb3IgdGhlIGZpcnN0IGFuZCBzZWNvbmQgZGVyaXZhdGl2ZXMgb2YgdGhlIGZ1bmN0aW9uIG9mIGludGVyZXN0IG11c3QgYmUgZ2l2ZW4uIEluIHNvbWUgc2l0dWF0aW9ucyBzdWNoIGRlcml2YXRpdmVzIG1heSBiZSBkaWZmaWN1bHQgdG8gZGVyaXZlIG9yIGV2YWx1YXRlIGluIHByYWN0aWNlLiBXZSB3aWxsIGludHJvZHVjZSBzZXZlcmFsIGFsdGVybmF0aXZlIG51bWVyaWNhbCBvcHRpbWl6YXRpb24gcHJvY2VkdXJlcyB0byBoYW5kbGUgdGhlc2Ugc2l0dWF0aW9ucyBsYXRlciBpbiB0aGlzIGxlY3R1cmUuDQoNCkxldCAkXHRoZXRhXnsoMCl9JCBkZW5vdGUgdGhlIHN0YXJ0aW5nIHZhbHVlIGZvciB0aGUgZXN0aW1hdGUgb2YgJFx0aGV0YSQgYW5kIGFzc3VtZSB0aGF0IHdlIHdpc2ggdG8gbWF4aW1pemUgc29tZSBmdW5jdGlvbiAkZihcdGhldGEpJC4gVGhlbiwgYmFzZWQgb24gdGhpcyBzY2hlbWUsIHdlIHVwZGF0ZSBvdXIgZXN0aW1hdGUgb2YgJFx0aGV0YSQgYXQgdGhlICR0JC10aCBzdGVwIG9mIHRoZSBhbGdvcml0aG0gKCRcdGhldGFeeyh0KX0kKSB1c2luZyB0aGUgZm9sbG93aW5nIGV4cHJlc3Npb246DQoNCiQkDQpcdGhldGFeeyh0KzEpfSA9IFx0aGV0YV57KHQpfSArIGheeyh0KX0sDQokJA0KDQp3aGVyZSANCg0KJCQNCmheeyh0KX0gPSAtXGZyYWN7ZidcIVxiaWdsKFx0aGV0YV57KHQpfVxiaWdyKX17ZicnXCFcYmlnbChcdGhldGFeeyh0KX1cYmlncil9LA0KJCQNCg0KJGYnKFx0aGV0YSkkIGlzIHRoZSBmaXJzdCBkZXJpdmF0aXZlIG9mICRmKFx0aGV0YSkkIHdpdGggcmVzcGVjdCB0byAkXHRoZXRhJCBldmFsdWF0ZWQgYXQgJFx0aGV0YV57KHQpfSQsIGFuZCAkZicnKFx0aGV0YSkkIGlzIHRoZSBzZWNvbmQgZGVyaXZhdGl2ZSBvZiAkZihcdGhldGEpJCB3aXRoIHJlc3BlY3QgdG8gJFx0aGV0YSQgZXZhbHVhdGVkIGF0ICRcdGhldGFeeyh0KX0kLg0KDQpJZiB3ZSBzb3VnaHQgdG8gaW5zdGVhZCAqbWluaW1pemUqICRmKFx0aGV0YSkkLCB0aGUgc2FtZSB1cGRhdGUgd291bGQgYXBwbHkuIEFmdGVyIGNob29zaW5nIGFuIGFwcHJvcHJpYXRlIHN0YXJ0aW5nIHBvaW50IGZvciB0aGUgYWxnb3JpdGhtIGFuZCBzdG9wcGluZyBydWxlLCB3ZSBjYW4gYXBwbHkgdGhlIHVwZGF0aW5nIHJ1bGUgYWJvdmUgYXMgd2UgaGFkIGluIHRoZSBwcmV2aW91cyBleGFtcGxlIHRvIGFycml2ZSBhdCAkXGhhdHtcdGhldGF9JC4NCg0KLS0tDQoNCioqQSBmZXcgb2J2aW91cyBxdWVzdGlvbnM6KioNCg0KLSBIb3cgZGlkIHdlIGdldCB0aGlzIGV4cHJlc3Npb24/ICANCg0KLSBXaHkgd291bGQgd2UgZXZlbiBleHBlY3QgJFx0aGV0YV57KHQpfSBcdG8gXHRoZXRhXiokPyAgDQoNCi0gVW5kZXIgd2hhdCBjb25kaXRpb25zIGRvIHdlIGV4cGVjdCB0aGUgYWxnb3JpdGhtIHRvIHdvcmsgd2VsbCBvciBub3Qgd29yayB3ZWxsPyAgDQoNCi0gSG93IHF1aWNrbHkgd291bGQgd2UgZXhwZWN0ICRcdGhldGFeeyh0KX0gXHRvIFx0aGV0YV4qJCBpbiAkdCQ/ICANCg0KVGhlcmUgbWF5IGJlIHNpdHVhdGlvbnMgd2hlcmUgdGhlIGFsZ29yaXRobSBtYXkgbm90IGNvbnZlcmdlIG9yIGRvIHNvIGluIGEgc3VpdGFibGUgdGltZWZyYW1lLiBVbmRlcnN0YW5kaW5nIHRoZSByYXRpb25hbGUgYmVoaW5kIHRoaXMgYXBwcm9hY2ggYW5kIGl0cyBwcm9wZXJ0aWVzIG1heSBoZWxwIG5hdmlnYXRlIHRoZXNlIHNpdHVhdGlvbnMgYW5kIHByb3ZpZGUgY2x1ZXMgdG8gd2hhdCBhbHRlcm5hdGl2ZSBhcHByb2FjaGVzIG1heSB3b3JrLg0KDQoNCiMjIyBSYXRpb25hbGUgYW5kIERlcml2YXRpb24NCg0KTGV0IHVzIHN1cHBvc2UgdGhhdCAkZicoXHRoZXRhKSQgaXMgY29udGludWFsbHkgZGlmZmVyZW50aWFibGUsICRmJycoXHRoZXRhKSQgZXhpc3RzLCBhbmQgdGhhdCAkZicnKFx0aGV0YSkgXG5lcSAwJCAoc3VnZ2VzdGluZyB0aGF0IHRoZSBmaXJzdCBkZXJpdmF0aXZlIGlzIG5vbi1jb25zdGFudCkuIEF0IGEgZ2l2ZW4gaXRlcmF0aW9uICR0JCwgJGYnKFx0aGV0YV4qKSQgY2FuIGJlIGFwcHJveGltYXRlZCBieSBhIGxpbmVhciBUYXlsb3IgU2VyaWVzIEV4cGFuc2lvbiBhYm91dCAkXHRoZXRhXiokLCB0aGUgdW5rbm93biB0cnVlIHZhbHVlIG9mICRcdGhldGEkIHRoYXQgbWF4aW1pemVzICRmKFx0aGV0YSkkLiBZZXMsIHdlIGRvIG5vdCBrbm93IHdoYXQgJFx0aGV0YV4qJCBpcyBpbiByZWFsaXR5LCBidXQgdGhpcyB0ZXJtIGlzIGltcG9ydGFudCB3aGVuIHdlIHRhbGsgYWJvdXQgdGhlIGNvbmRpdGlvbnMgdGhhdCBtYXkgaW1wYWN0IHRoZSBwZXJmb3JtYW5jZSBvZiB0aGlzIGFwcHJvYWNoIGxhdGVyIG9uLg0KDQpVbmRlciB0aGlzIGFwcHJveGltYXRpb24sIHdlIGhhdmUNCg0KJCQNCjAgPSBmJyhcdGhldGFeKikgXDtcYXBwcm94XDsgZidcYmlnbChcdGhldGFeeyh0KX1cYmlncikgKyBcYmlnbChcdGhldGFeKiAtIFx0aGV0YV57KHQpfVxiaWdyKSBcLCBmJydcYmlnbChcdGhldGFeeyh0KX1cYmlncikuDQokJA0KDQpMZXQncyB1bnBhY2sgd2hhdCB0aGlzIG1lYW5zIGZvciBvbmUgc2Vjb25kLiBJZiB5b3UgcmVjYWxsLCBtb3N0IGFuYWx5dGljIGFwcHJvYWNoZXMgdG8gb3B0aW1pemluZyBhbGdvcml0aG1zIHN0YXJ0IHdpdGggc2V0dGluZyB0aGUgMXN0IGRlcml2YXRpdmUgdG8gMCBhbmQgc29sdmluZyBmb3IgdGhlIHZhbHVlIG9mICRcdGhldGEkIHNhdGlzZnlpbmcgdGhpcyBlcXVhbGl0eS4gSW4gdGhpcyBzZXR0aW5nLCB3ZSBhc3N1bWUgdGhhdCBzdWNoIGFuIGFuYWx5dGljIHNvbHV0aW9uIGRvZXMgbm90IGV4aXN0LiBUaGVyZWZvcmUsIHdlIGFwcHJveGltYXRlIHRoaXMgZGVyaXZhdGl2ZSB1c2luZyB0aGlzIGxpbmVhciBUYXlsb3IgU2VyaWVzIGFwcHJveGltYXRpb24gYXJvdW5kICRcdGhldGFeKiQsIGFuZCB0aGVuIHNldCB0aGlzIGFwcHJveGltYXRpb24gdG8gemVyby4gSGVyZSwgJGYnKFx0aGV0YV4qKSQgaXMgYXBwcm94aW1hdGVkIGJ5IHRoZSB0YW5nZW50IGxpbmUgYXQgJFx0aGV0YV57KHQpfSQgKHNlZSBmaWd1cmUgYmVsb3cpLg0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aD0iODAlIn0NCmluY2x1ZGVfZ3JhcGhpY3MoIk5ld3RvblJhcGhzb24ucG5nIikNCmBgYA0KDQoqRXhhbXBsZSBvZiBOZXd0b27igJNSYXBoc29uIGluIHRoZSBjb250ZXh0IG9mIHRoZSBwcmV2aW91cyBpbGx1c3RyYXRpb24uKiBUaGUgYWxnb3JpdGhtIGFwcHJveGltYXRlcyAkZyckIGJ5IGl0cyB0YW5nZW50IGxpbmUgYXQgJFx0aGV0YV57KHQpfSQsIHdob3NlIHJvb3QgJFx0aGV0YV57KHQrMSl9JCBzZXJ2ZXMgYXMgdGhlIG5leHQgYXBwcm94aW1hdGlvbiBvZiB0aGUgdHJ1ZSByb290ICRcdGhldGFeKiQuDQoNClNpbmNlIHdlIGFyZSBhcHByb3hpbWF0aW5nICRmJyhcdGhldGFeKikkIHdpdGggaXRzIHRhbmdlbnQgbGluZSBhdCAkXHRoZXRhXnsodCl9JCwgaXQgbWFrZXMgc2Vuc2UgdGhhdCB3ZSBjYW4gYXBwcm94aW1hdGUgdGhlICpyb290KiBvZiAkZicoXHRoZXRhXiopJCAodGhlIHBvaW50IGF0IHdoaWNoIGl0IGVxdWFscyAwKSB3aXRoIHRoZSByb290IG9mIHRoZSB0YW5nZW50IGxpbmUgYXQgJFx0aGV0YV57KHQpfSQgKHNlZSBleGFtcGxlIGluIHRoZSBmaWd1cmUgYWJvdmUgYXQgc3RlcCAxKS4gVW5kZXIgdGhpcyByYXRpb25hbGUsIHdlIHNvbHZlIGZvciAkXHRoZXRhXiokIGluIHRoZSBsaW5lYXIgVGF5bG9yIFNlcmllcyBhcHByb3hpbWF0aW9uIGFib3ZlLiBEb2luZyB0aGlzLCB3ZSBoYXZlDQoNCiQkDQpcdGhldGFeKiBcOz1cOyBcdGhldGFeeyh0KX0gLSBcZnJhY3tmJ1xiaWdsKFx0aGV0YV57KHQpfVxiaWdyKX17ZicnXGJpZ2woXHRoZXRhXnsodCl9XGJpZ3IpfSBcOz1cOyBcdGhldGFeeyh0KX0gKyBoXnsodCl9Lg0KJCQNCg0KSW4gb3RoZXIgd29yZHMsIHRoZSB2YWx1ZSBmb3IgJFx0aGV0YV4qJCBhdCBpdGVyYXRpb24gJHQkIGlzIGFwcHJveGltYXRlZCB1c2luZyB0aGUgY3VycmVudCBndWVzcyAkXHRoZXRhXnsodCl9JCBhbmQgYSByZWZpbmVtZW50IHN0ZXAgJGheeyh0KX0kLiBTdWNjZXNzaXZlIGl0ZXJhdGlvbnMgb2YgdGhpcyBhcHByb2FjaCB3aWxsIHlpZWxkIGNsb3NlciBhbmQgY2xvc2VyIGFwcHJveGltYXRpb25zIHRvICRcdGhldGFeKiQuDQoNCg0KIyMjIFVwZGF0aW5nIEVxdWF0aW9uDQoNClVzaW5nIHRoZSBhYm92ZSByYXRpb25hbGUsIHdlIGNhbiBkZWZpbmUgdGhlIHVwZGF0aW5nIHN0cmF0ZWd5IGFzDQoNCiQkDQpcdGhldGFeeyh0KzEpfSA9IFx0aGV0YV57KHQpfSAtIFxmcmFje2YnXGJpZ2woXHRoZXRhXnsodCl9XGJpZ3IpfXtmJydcYmlnbChcdGhldGFeeyh0KX1cYmlncil9ID0gXHRoZXRhXnsodCl9ICsgaF57KHQpfS4NCiQkDQoNCldoZW4gdGhlIG9wdGltaXphdGlvbiBvZiAkZiQgY29ycmVzcG9uZHMgdG8gbWF4aW11bSBsaWtlbGlob29kIGVzdGltYXRpb24sIHdoZXJlICRcaGF0e1x0aGV0YX0kIGlzIHRoZSBzb2x1dGlvbiB0byAkbCcoXHRoZXRhKSA9IDAkLCB0aGUgdXBkYXRpbmcgZXF1YXRpb24gaXMgZ2l2ZW4gYXMNCg0KJCQNClx0aGV0YV57KHQrMSl9ID0gXHRoZXRhXnsodCl9IC0gXGZyYWN7bCdcYmlnbChcdGhldGFeeyh0KX1cYmlncil9e2wnJ1xiaWdsKFx0aGV0YV57KHQpfVxiaWdyKX0uDQokJA0KDQoNCiMjIyBSZXF1aXJlbWVudHMgYW5kIENvbnZlcmdlbmNlDQoNCkJ1dCBob3cgZG8gd2Uga25vdyB0aGF0IHN1Y2Nlc3NpdmUgaXRlcmF0aW9ucyB1c2luZyB0aGUgdXBkYXRpbmcgZXF1YXRpb24gYWJvdmUgd2lsbCByZXN1bHQgaW4gY29udmVyZ2VuY2UgdG8gJFx0aGV0YV4qJCwgcmF0aGVyIHRoYW4gZGl2ZXJnaW5nIG9yIGNvbnZlcmdpbmcgdG8gc29tZSBvdGhlciB2YWx1ZT8gSW4gcHJhY3RpY2UsIHRoaXMgZGVwZW5kcyBvbiB0aGUgc2hhcGUgb2YgdGhlIGZ1bmN0aW9uIHlvdSBhcmUgbWF4aW1pemluZyBhcyB3ZWxsIGFzIHlvdXIgY2hvc2VuIHN0YXJ0aW5nIHZhbHVlLg0KDQpBcyB3ZSBjYW4gc2VlIGZyb20gdGhlIHByaW9yIGV4YW1wbGVzLCBjaG9vc2luZyBzdGFydGluZyB2YWx1ZXMgdG9vIGZhciBhd2F5IGZyb20gJFx0aGV0YV4qJCByZXN1bHRzIGluIHNsb3dlciBjb252ZXJnZW5jZSBvciBldmVuIGRpdmVyZ2VuY2UuIENob29zaW5nIHZhbHVlcyBjbG9zZXIgdG8gJFx0aGV0YV4qJCByZXN1bHRzIGluIHF1aWNrZXIgY29udmVyZ2VuY2UuDQoNCldlIGNhbiBzaG93IHRoYXQgaWYgJGYnJycoXHRoZXRhKSQgaXMgY29udGludW91cyBhbmQgJFx0aGV0YV4qJCBpcyBhIHNpbXBsZSByb290IG9mICRmJyhcdGhldGEpJCAoaW1wbHlpbmcgJGYnKFx0aGV0YV4qKSA9IDAkKSwgdGhlbiB0aGVyZSBleGlzdHMgYSBuZWlnaGJvcmhvb2QgYXJvdW5kICRcdGhldGFeKiQgZm9yIHdoaWNoIE5SIGNvbnZlcmdlcyB0byAkXHRoZXRhXiokIGZyb20gYW55IHN0YXJ0aW5nIHZhbHVlICRcdGhldGFeeygwKX0kIHdpdGhpbiB0aGF0IG5laWdoYm9yaG9vZCAoR0ggMi4xLjEsIGVxLiAyLjE4KS4gVGhlIGRlZmluaXRpb24gb2Yg4oCcbmVpZ2hib3Job29k4oCdIGluIHRoaXMgc2Vuc2UgaXMgc29tZXdoYXQgYXJiaXRyYXJ5LCBidXQgaXMgbWVhbnQgdG8gaW5kaWNhdGUgdGhhdCBjb252ZXJnZW5jZSBpcyBsaWtlbHkgaWYgeW91IGFyZSBpbiBzb21lIGNsb3NlIHZpY2luaXR5IG9mICRcdGhldGFeKiQuIEdIIHByb3ZpZGVzIHRoaXMgcHJvb2YgdXNpbmcgYXJndW1lbnRzIGJhc2VkIHVwb24gdGhlIGVycm9yIG9mIGEgcXVhZHJhdGljIFRheWxvciBTZXJpZXMgYXBwcm94aW1hdGlvbiBmb3IgJGYnKFx0aGV0YSkkIGluIFNlY3Rpb24gMi4xLjEuDQoNCk1vcmUgc3BlY2lmaWNhbGx5LCBpZiAkZicoXHRoZXRhKSQgaXMgdHdpY2UgZGlmZmVyZW50aWFibGUsIGlzIGNvbnZleCwgYW5kIGhhcyBhIHJvb3QsIHRoZW4gTlIgd2lsbCBjb252ZXJnZSBmcm9tIGFueSBwb2ludCEgVGhlc2UgY29uZGl0aW9ucyBhcmUgdHlwaWNhbGx5IG1ldCBieSBjb21tb24gbGlrZWxpaG9vZCBmdW5jdGlvbnMgaW4gc3RhdGlzdGljcyAoYnV0IG5vdCBhbHdheXMpLg0KDQpJZiB5b3UgYXJlIHN0YXJ0aW5nIGluIHNvbWUgYXJiaXRyYXJ5IGludGVydmFsICRbYSxiXSQsIHlvdSBjYW4gY2hlY2sgdGhlIGZvbGxvd2luZyBjb25kaXRpb25zIHRvIGRldGVybWluZSB3aGV0aGVyIHRoZSBOUiBhbGdvcml0aG0gd2lsbCBjb252ZXJnZSBmcm9tIGFueSAkXHRoZXRhXnsoMCl9JCBpbiB0aGF0IGludGVydmFsOg0KDQoNCjEuICRmJycoXHRoZXRhKSBcbmVxIDAkIG9uICRbYSxiXSQgIA0KDQoyLiAkZicnJyhcdGhldGEpJCBkb2VzIG5vdCBjaGFuZ2Ugc2lnbiBvbiAkW2EsYl0kICANCg0KMy4gJGYnKGEpIGYnKGIpIDwgMCQsIGFuZCAgDQoNCjQuICR8ZicoYSkgLyBmJycoYSl8IDwgYiAtIGEkIGFuZCAkfGYnKGIpIC8gZicnKGIpfCA8IGIgLSBhJA0KDQoNClRoZXNlIHN0YXRlbWVudHMgZm9sbG93IGZyb20gdGhlIHByb29mIGdpdmVuIGluIEdIIDIuMS4xLiBJbiBwcmFjdGljZSwgbW9zdCBkbyBub3QgbWFudWFsbHkgZXZhbHVhdGUgdGhlc2Ugc3RhdGVtZW50cyBiZWZvcmUgYXBwbHlpbmcgTlIsIGJ1dCBpbiBjYXNlcyB3aGVyZSBwcm9ibGVtcyBhcmlzZSwgdGhleSBjYW4gaGVscCBleHBsYWluIGNvbnZlcmdlbmNlIGRpZmZpY3VsdGllcy4NCg0KUmVwaHJhc2VkLCB3ZSBjYW4gdHJhbnNsYXRlIHRoZXNlIGNvbmRpdGlvbnMgaW50byB0aGUgZm9sbG93aW5nOg0KDQotICoqQ29uZGl0aW9uIDEqKjogVGhlIDJuZCBkZXJpdmF0aXZlIGlzIG5ldmVyIGVxdWFsIHRvIHplcm8gaW4gdGhlIGludGVydmFsIOKGkiAkZicoXHRoZXRhKSQgaXMgbmV2ZXIgZmxhdCBvbiAkW2EsYl0kIOKGkiAkZicoXHRoZXRhKSQgY2FuIHBvdGVudGlhbGx5IGludGVyc2VjdCAwIGluIHRoZSBpbnRlcnZhbCDihpIgJGYnKFx0aGV0YSkkIGhhcyB0aGUgcG90ZW50aWFsIHRvIGhhdmUgYSByb290IGluICRbYSxiXSQuDQoNCi0gKipDb25kaXRpb24gMioqOiBUaGUgdGhpcmQgZGVyaXZhdGl2ZSBkb2VzIG5vdCBjaGFuZ2Ugc2lnbiBvbiAkW2EsYl0kIOKGkiB0aGUgc2lnbiBvZiB0aGUgc2Vjb25kIGRlcml2YXRpdmUgc3RheXMgdGhlIHNhbWUgaW4gdGhlIGludGVydmFsIG9uICRbYSxiXSQg4oaSIG5vIGNoYW5nZSBpbiBjb252ZXhpdHkvY29uY2F2aXR5IGluIHRoZSBpbnRlcnZhbC4NCg0KLSAqKkNvbmRpdGlvbiAzKio6IFRoZSAxc3QgZGVyaXZhdGl2ZSBhdCAkYSQgaXMgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIGF0ICRiJCAob3IgdmljZSB2ZXJzYSkg4oaSICRmJyhcdGhldGEpJCBjaGFuZ2VzIHNpZ24gYXQgc29tZSBwb2ludCBpbiAkW2EsYl0kIOKGkiBpbXBsaWVzIGEgcm9vdCBmb3IgJGYnKFx0aGV0YSkkIG11c3QgZXhpc3QgaW4gJFthLGJdJC4NCg0KLSAqKkNvbmRpdGlvbiA0Kio6IFRoZSBhYnNvbHV0ZSB2YWx1ZSBvZiAkaCQgYXQgJGEkIG9yIGF0ICRiJCBpcyBsZXNzIHRoYW4gdGhlIGxlbmd0aCBvZiB0aGUgaW50ZXJ2YWwg4oaSIHRoZSBzdGVwIHNpemUgKHJlZmluZW1lbnQpIGFzc3VtaW5nICRcdGhldGFeeygwKX0gPSBhJCBvciAkXHRoZXRhXnsoMCl9ID0gYiQgaXMgc21hbGxlciB0aGFuIHRoZSBsZW5ndGggb2YgdGhlIGludGVydmFsIGJlaW5nIGNvbnNpZGVyZWQuIElmIHRoaXMgaXMgbm90IHRydWUsIHRoZSB1cGRhdGUgd2lsbCBkZWZpbml0ZWx5IG5vdCBiZSBjb250YWluZWQgaW4gJFthLGJdJC4NCg0KVXNpbmcgc2ltaWxhciBhcmd1bWVudHMsIHdlIGNhbiBzaG93IHRoYXQgdGhlIGNvbnZlcmdlbmNlIG9yZGVyIGZvciBOUiBpcyAqKnF1YWRyYXRpYyoqLCBtZWFuaW5nIHRoZSBhY2N1cmFjeSBvZiB0aGUgc29sdXRpb24gd2lsbCBkb3VibGUgd2l0aCBlYWNoIGl0ZXJhdGlvbiAkdCQuIEhpZ2hlciBjb252ZXJnZW5jZSBvcmRlciBpbXBsaWVzIHRoYXQgYWNjdXJhdGUgYXBwcm94aW1hdGlvbnMgZm9yIHRoZSBwYXJhbWV0ZXIocykgb2YgaW50ZXJlc3QgYXJlIGFjaGlldmVkIGluIGZld2VyIGl0ZXJhdGlvbnMuDQoNCkhvd2V2ZXIsIHdlIHdpbGwgc2VlIHRoYXQgYWxnb3JpdGhtcyB3aXRoIGhpZ2hlciBjb252ZXJnZW5jZSBvcmRlcnMgbWF5IGFsc28gYmUgbGVzcyByb2J1c3QgdG8gZGlmZmVyZW50IGNvbmRpdGlvbnMgYW5kIG1heSBmYWlsIG1vcmUgZnJlcXVlbnRseSB0aGFuIHNsb3dlciBhbGdvcml0aG1zLiBGb3IgZXhhbXBsZSwgTlIgaXMgcmVsYXRpdmVseSBtb3JlIHNlbnNpdGl2ZSB0byB0aGUgc3RhcnRpbmcgdmFsdWUgY29tcGFyZWQgdG8gb3RoZXIgbWV0aG9kcy4NCg0KDQojIyMgUHJvcyBhbmQgQ29ucw0KDQoNCiogKipQcm9zKioNCiAgKyAgKipTcGVlZDoqKiBFeHRyZW1lbHkgZmFzdCAocXVhZHJhdGljKSBjb252ZXJnZW5jZQ0KDQoqICoqQ29ucyoqDQoNCiAgKyBSZXF1aXJlcyBkZXJpdmF0aW9uIGFuZCBldmFsdWF0aW9uIG9mIDFzdCBhbmQgMm5kIGRlcml2YXRpdmVzDQoNCiAgKyBSZWxhdGl2ZWx5IG1vcmUgc2Vuc2l0aXZlIHRvIHRoZSBjaG9pY2Ugb2Ygc3RhcnRpbmcgdmFsdWUNCg0KICArIEluIHRoZSBtdWx0aXZhcmlhdGUgc2V0dGluZywgbmVlZCBjaGVja3Mgb24gdGhlIEhlc3NpYW4gdG8gYXZvaWQgc2luZ3VsYXJpdGllcyBhbmQgb3RoZXIgaXNzdWVzDQoNCiAgKyBJZiB0aGUgZGVyaXZhdGl2ZXMgYW5kL29yIGxpa2VsaWhvb2QgYXJlIGNvbXBsaWNhdGVkLCBOUiBtYXkgbm90IGJlIGFuIGF0dHJhY3RpdmUgY2hvaWNlDQoNCiAgKyBBZGRpdGlvbmFsIGV2YWx1YXRpb25zIG9mIGRlcml2YXRpdmVzIG1heSBiZSBjb3N0bHkgKGUuZy4sIHdoZW4gdGhlIGxpa2VsaWhvb2QgaW52b2x2ZXMgbnVtZXJpY2FsIGludGVncmF0aW9uKQ0KDQpXaXRoIHJlc3BlY3QgdG8gTlIsIEpvaG4gTmFzaCBpbiAqTm9ubGluZWFyIHBhcmFtZXRlciBvcHRpbWl6YXRpb24gdXNpbmcgUiB0b29scyogc3VtbWFyaXplczoNCg0KPiBUaGVyZSBhcmUgbG90cyBvZiBkYW5nZXJvdXMgYmVhc3RzIGluIHRoZSBuaWdodC10aW1lIG9wdGltaXphdGlvbiBmb3Jlc3QuDQoNClRoZSBhcHBhcmVudCBkaWZmaWN1bHRpZXMgd2l0aCBOUiBpbiB0aGUgbXVsdGl2YXJpYXRlIHNldHRpbmcgYXJlIHdoeSBpdCB3YXMgbm90IGluY2x1ZGVkIGluIHRoZSBvcmlnaW5hbCBSIGdlbmVyYWwtcHVycG9zZSBvcHRpbWl6YXRpb24gZnVuY3Rpb25zIGBvcHRpbWAgYW5kIGBvcHRpbXhgLg0KDQoNCg0KIyMjIEV4YW1wbGUgb2YgTmV3dG9uIFJhcGhzb24gDQoNCkxldCdzIGRldGVybWluZSB0aGUgMXN0IGFuZCAybmQgZGVyaXZhdGl2ZXMgb2YgdGhlIHNpbXBsZSBQb2lzc29uIGV4YW1wbGUgZnJvbSBlYXJsaWVyLiBUaGlzIGlzIHRoZSBzYW1lIGFwcHJvYWNoIHRoYXQgd2Ugd2lsbCBhcHBseSB0byB0aGUgUG9pc3NvbiBSZWdyZXNzaW9uIGV4YW1wbGUsIGJ1dCB0aGF0IHdpbGwgYmUgbGVmdCBmb3IgYSBsYXRlciBzZWN0aW9uLg0KDQpIZXJlDQoNCiQkDQpsJyhcbGFtYmRhKSA9IC1uICsgXGZyYWN7XHN1bV97aT0xfV5uIHlfaX17XGxhbWJkYX0NCiQkDQoNCmFuZCB0aGUgc2Vjb25kIGRlcml2YXRpdmUgaXMNCg0KJCQNCmwnJyhcbGFtYmRhKSA9IC1cZnJhY3tcc3VtX3tpPTF9Xm4geV9pfXtcbGFtYmRhXjJ9Lg0KJCQNCg0KTm93LCBsZXQncyBnZW5lcmF0ZSBzb21lIGRhdGEgYW5kIGFwcGx5IE5SOg0KDQoNCmBgYHtyfQ0KIyBnZW5lcmF0ZSAxMDAgcG9pc3NvbigxMCkgc2FtcGxlcw0Kc2V0LnNlZWQoMTApDQpuID0gMTAwDQpsYW1iZGEgPSAxMA0KeSA9IHJwb2lzKG4sIGxhbWJkYSkNCm1lYW4oeSkNCmBgYA0KDQpOb3cgbGV0cyBnZXQgb3VyIGZ1bmN0aW9ucyBpbiBvcmRlcg0KDQpgYGB7cn0NCiMgbG9nIGxpa2VsaWhvb2QgZnVuY3Rpb24NCmxvZ0xpayA9IGZ1bmN0aW9uKGxhbWJkYSwgeSkgew0KICB2YWx1ZSA9IHN1bShkcG9pcyh5LCBsYW1iZGEgPSBsYW1iZGEsIGxvZyA9IFQpKQ0KICByZXR1cm4odmFsdWUpDQp9DQoNCiMgbGV0cyB3cml0ZSBhIGZ1bmN0aW9uIGZvciB0aGUgZmlyc3QgZGVyaXZhdGl2ZQ0KZDEgPSBmdW5jdGlvbihsYW1iZGEsIHkpIHsNCiAgZmlyc3QgPSAtbGVuZ3RoKHkpICsgc3VtKHkpIC8gbGFtYmRhDQogIHJldHVybihmaXJzdCkNCn0NCg0KIyBub3cgdGhlIHNlY29uZA0KZDIgPSBmdW5jdGlvbihsYW1iZGEsIHkpIHsNCiAgc2Vjb25kID0gLXN1bSh5KSAvIGxhbWJkYSBeIDINCiAgcmV0dXJuKHNlY29uZCkNCn0NCmBgYA0KDQoNCk5vdyBsZXRzIGFwcGx5IE5SLiBXZSBwaWNrIGEgdG9sZXJhbmNlIGxldmVsICh0b2wpIGFuZCBzdGFydGluZyB2YWx1ZSAobGFtKS4gSGVyZSB0aGUgY2hvaWNlIG9mIHN0YXJ0aW5nIHZhbHVlIGlzIGFyYml0cmFyeS4gV2UgY2FuIHZhcnkgdGhpcyB0byBzZWUgaXRzIGltcGFjdCBvbiB0aGUgZmluYWwgdmFsdWUgbGF0ZXIuIEl04oCZcyBhbHNvIGhlbHBmdWwgdG8gc3BlY2lmeSBhIG1heGltdW0gbnVtYmVyIG9mIGl0ZXJhdGlvbnMgKG1heGl0KSB0byBoYWx0IHRoZSBhbGdvcml0aG0gc28gaXQgZG9lcyBub3QgbG9vcCBpbmZpbml0ZWx5IGlmIHRoZXJlIGlzIGEgY29udmVyZ2VuY2UgaXNzdWUuIFdlIGNhbiBhbHNvIGFkZCBjb25kaXRpb25zIHRvIGNoZWNrIHdoZXRoZXIgJFxsYW1iZGEgKHQpPjAkLg0KDQpgYGB7cn0NCiMgbm93IGxldHMgYXBwbHkgTlIuIEZpcnN0IGxldHMgc2V0IHRoZSB0b2xlcmFuY2UgYW5kIGNob29zZSBhIHN0YXJ0aW5nIHZhbHVlDQojIHRoZSBzdGFydA0KdG9sID0gMTBeLTQNCmxhbSA9IDAuMQ0KbWF4aXQgPSA1MA0KaXRlciA9IDAgIyBpdGVyYXRpb24gY291bnRlcg0KZXBzID0gSW5mICMga2VlcHMgdHJhY2sgb2YgY3VycmVudCBkaWZmZXJlbmNlDQpzdGFydCA9IFN5cy50aW1lKCkgIyByZWNvcmQgc3RhcnQgdGltZQ0KDQp3aGlsZSAoZXBzID4gdG9sICYgaXRlciA8IG1heGl0KSB7DQogICMgc2F2ZSB0aGUgcHJldmlvdXMgdmFsdWUNCiAgbGFtMCA9IGxhbQ0KICANCiAgIyBjYWxjdWxhdGUgaCwgdGhlIGluY3JlbWVudA0KICBoID0gLWQxKGxhbWJkYSA9IGxhbSwgeSA9IHkpIC8gZDIobGFtYmRhID0gbGFtLCB5ID0geSkNCiAgDQogICMgdXBkYXRlIGxhbWJkYQ0KICBsYW0gPSBsYW0gKyBoDQogIA0KICAjIHVwZGF0ZSB0aGUgbG9nIGxpa2VsaWhvb2QNCiAgbG9nTCA9IGxvZ0xpayhsYW1iZGEgPSBsYW0sIHkgPSB5KQ0KICANCiAgIyBjYWxjdWxhdGUgdGhlIGRpZmYgaW4gbGFtYmRhLCBjb3VsZCBhbHNvIHVzZSB0aGUgbG9nIGxpa2VsaWhvb2QgaWYgd2Ugd2FudGVkDQogIGVwcyAgPSBhYnMobGFtIC0gbGFtMCkNCiAgDQogICMgdXBkYXRlIHRoZSBpdGVyYXRpb24gbnVtYmVyDQogIGl0ZXIgPSBpdGVyICsgMQ0KICBpZiAoaXRlciA9PSBtYXhpdCkNCiAgICB3YXJuaW5nKCJJdGVyYXRpb24gbGltaXQgcmVhY2hlZCB3aXRob3V0IGNvbnZlcmdlbmNlIikNCiAgDQogICMgcHJpbnQgb3V0IGluZm8gdG8ga2VlcCB0cmFjaw0KICBjYXQoc3ByaW50ZigiSXRlcjogJWQgbG9nTDogJWYgbGFtOiAlZiBoOiAlZiBlcHM6JWZcbiIsIGl0ZXIsIGxvZ0wsIGxhbSwgaCwgZXBzKSkNCn0NCmBgYA0KDQpgYGB7cn0NCmVuZCA9IFN5cy50aW1lKCkNCnByaW50KGVuZCAtIHN0YXJ0KQ0KYGBgDQoNCldlIGNhbiBhbHNvIHZpc3VhbGl6ZSB0aGlzIGJlbG93DQoNCmBgYHtyIGVjaG8gPSBGQUxTRSwgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGg9IjgwJSJ9DQppbmNsdWRlX2dyYXBoaWNzKCJOZXd0b25SYXBoc29uLmdpZiIpDQpgYGANCg0KDQojIyBGaXNoZXIgU2NvcmluZw0KDQpGaXNoZXIgU2NvcmluZyBpcyBhbiBhbHRlcm5hdGl2ZSB0byBOUiB3aGVuIHBlcmZvcm1pbmcgbWF4aW11bSBsaWtlbGlob29kIGVzdGltYXRpb24sIHdoZXJlIHdlIHNpbXBseSByZXBsYWNlICRsJydcIVxiaWdsKFx0aGV0YV57KHQpfVxiaWdyKSQgd2l0aCAkSVwhXGJpZ2woXHRoZXRhXnsodCl9XGJpZ3IpJCwgdGhlIGV4cGVjdGVkIEZpc2hlciBJbmZvcm1hdGlvbiBtYXRyaXggYXQgaXRlcmF0aW9uICR0JC4gV2UgY2FuIHNob3cgdGhhdCAkLWwnJyhcdGhldGEpJCDigJQgdGhlIG9ic2VydmVkIEZpc2hlciBJbmZvcm1hdGlvbiDigJQgaXMgYW4gYXBwcm94aW1hdGlvbiBmb3IgJEkoXHRoZXRhKSQgKEdIIDEuNCksIHNvIGl0IGlzIG5vdCBzdXJwcmlzaW5nIHRoYXQgdGhlIGFzeW1wdG90aWMgcHJvcGVydGllcyBmb3IgTlIgYW5kIEZTIGFyZSBzaW1pbGFyLg0KDQpUaGUgdXBkYXRpbmcgZXF1YXRpb24gaXMgZ2l2ZW4gYXMNCg0KJCQNClx0aGV0YV57KHQrMSl9ID0gXHRoZXRhXnsodCl9ICsgSVwhXGJpZ2woXHRoZXRhXnsodCl9XGJpZ3IpXnstMX0gXCwgbCdcIVxiaWdsKFx0aGV0YV57KHQpfVxiaWdyKS4NCiQkDQoNCkdpdmVuIHRoZSBwYXJ0aWN1bGFyIHByb2JsZW0gYXQgaGFuZCwgTlIgb3IgRlMgbWF5IGJlIGVhc2llciB0byBkZXJpdmUgYW5hbHl0aWNhbGx5LCB3aGVyZSB0aGUgbGF0dGVyIG9ubHkgbmVlZHMga25vd2xlZGdlIG9mIHRoZSAxc3QgZGVyaXZhdGl2ZXMgYW5kIGF2b2lkcyBjb21wdXRhdGlvbiBvZiB0aGUgMm5kIGRlcml2YXRpdmVzLiBBY2NvcmRpbmcgdG8gR0gsIEZTIG1heSBiZSB1c2VkIGluIGVhcmx5IGl0ZXJhdGlvbnMgZm9yIHJhcGlkIGltcHJvdmVtZW50cywgYW5kIE5SIGNhbiBiZSB1c2VkIHRvIG1ha2UgYmV0dGVyIHJlZmluZW1lbnRzIG5lYXIgdGhlIGVuZC4NCg0KKiAqKlByb3MqKg0KDQogICsgQXZvaWRzIGRlcml2YXRpb24gYW5kIGNvbXB1dGF0aW9uIG9mIHRoZSAybmQgZGVyaXZhdGl2ZQ0KDQogICsgU2ltaWxhciBhc3ltcHRvdGljIHByb3BlcnRpZXMgdG8gTlINCg0KICArIEluIHRoZSBtdWx0aXZhcmlhdGUgc2V0dGluZywgbWF5IGJlIG1vcmUgc3RhYmxlIHRoYW4gTlIgKHdpbGwgdGFsayBhYm91dCB0aGlzIGxhdGVyKQ0KDQogICsgT2Z0ZW4gd29ya3MgYmV0dGVyIGluIHRoZSBiZWdpbm5pbmcgdG8gbWFrZSByYXBpZCBpbXByb3ZlbWVudHMNCg0KKiAqKkNvbnMqKg0KDQogICsgTWF5IG5vdCBiZSBhcyBmYXN0IGZvciByZWZpbmVtZW50IG5lYXIgdGhlIGVuZA0KDQoNCg0KIyMgU2VjYW50IE1ldGhvZA0KDQpUaGlzIGFwcHJvYWNoIGlzIHNpbWlsYXIgdG8gTlIsIGV4Y2VwdCB0aGF0IHRoZSBzZWNvbmQgZGVyaXZhdGl2ZSBpbiB0aGUgdXBkYXRpbmcgZXF1YXRpb24gaXMgcmVwbGFjZWQgd2l0aCBhIGZpbml0ZeKAkWRpZmZlcmVuY2UgYXBwcm94aW1hdGlvbiwgd2hlcmUgbm93IHRoZSB1cGRhdGluZyBlcXVhdGlvbiBpcyAgDQoNCiQkDQpcdGhldGFeeyh0KzEpfSA9IFx0aGV0YV57KHQpfSAtIGYnXCFcYmlnbChcdGhldGFeeyh0KX1cYmlncikgXCwNClxmcmFje1x0aGV0YV57KHQpfSAtIFx0aGV0YV57KHQtMSl9fXtmJ1whXGJpZ2woXHRoZXRhXnsodCl9XGJpZ3IpIC0gZidcIVxiaWdsKFx0aGV0YV57KHQtMSl9XGJpZ3IpfSAuDQokJA0KDQpDb25kaXRpb25zIGZvciBjb252ZXJnZW5jZSBhcmUgc2ltaWxhciB0byBOUiwgYnV0IHdlIGNhbiBzaG93IHRocm91Z2ggc2ltaWxhciBhcmd1bWVudHMgdGhhdCB0aGUgY29udmVyZ2VuY2Ugb3JkZXIgZm9yIHRoZSBzZWNhbnQgbWV0aG9kIGlzICRccGhpIFxhcHByb3ggMS42MTgkICh0aGUgZ29sZGVuIHJhdGlvKSBpbnN0ZWFkIG9mIDIuICANCg0KVG8gaW5pdGlhbGl6ZSB0aGlzIGFwcHJvYWNoLCAqKnR3byBzdGFydGluZyB2YWx1ZXMqKiBoYXZlIHRvIGJlIHByb3ZpZGVkLiBUeXBpY2FsbHkgdGhlIGZpcnN0IHZhbHVlIGlzIHBpY2tlZCBpbiBhIG1hbm5lciBub3QgZGlzc2ltaWxhciB0byB0aGUgb3RoZXIgYXBwcm9hY2hlcyBkZXNjcmliZWQgaW4gdGhpcyBsZWN0dXJlLCBhbmQgdGhlIHNlY29uZCB2YWx1ZSBpcyBwaWNrZWQgcmVsYXRpdmVseSBjbG9zZSB0byB0aGUgZmlyc3Qgb25lLiBUaGVuIHdlIG1heSBjb21wdXRlIHRoZSBmaXJzdCB1cGRhdGUgYW5kIHByb3Bvc2FsIGZvciAkXHRoZXRhXnsoMSl9JCBhbmQgc3RhcnQgdGhlIGFsZ29yaXRobS4NCg0KKiAqKlByb3MqKg0KDQogICsgQXZvaWRzIGRlcml2YXRpb24gYW5kIGNvbXB1dGF0aW9uIG9mIHRoZSAybmQgZGVyaXZhdGl2ZQ0KICANCiAgKyBTaW1pbGFyIGFzeW1wdG90aWMgcHJvcGVydGllcyB0byBOUg0KDQoqICoqQ29ucyoqDQoNCiAgKyBTbG93ZXIgY29udmVyZ2VuY2UgcmVsYXRpdmUgdG8gTlIgKG9yZGVyIH4xLjYxOCB2cy4gcXVhZHJhdGljKQ0KDQoNCiMjIEV4YW1wbGVzDQoNCkxldOKAmXMgYXBwbHkgRlMgdG8gb3VyIHNpbXBsZSBQb2lzc29uIHByb2JsZW0uIFRvIGRvIHRoaXMsIGxldOKAmXMgZmlyc3QgY2FsY3VsYXRlIHRoZSBleHBlY3RlZCBpbmZvcm1hdGlvbiBcKEkoXHRoZXRhKVwpLCB3aGljaCBpbiB0aGUgdW5pdmFyaWF0ZSBzZXR0aW5nIGlzICANCg0KJCQNCkkoXHRoZXRhKSA9IFxtYXRoYmJ7RX1cIVxiaWdsW2wnKFx0aGV0YSleMlxiaWdyXSA9IFxtYXRoYmJ7RX1cIVxiaWdsWy1sJycoXHRoZXRhKVxiaWdyXS4NCiQkDQoNClRoaXMgZXhwcmVzc2lvbiBjYW4gYmUgY2FsY3VsYXRlZCBieSBicnV0ZSBmb3JjZSB1c2luZyB0aGUgZm9sbG93aW5nIHN0ZXBzOg0KDQokJA0KXGJlZ2lue2FsaWduZWR9DQpcbWF0aGJie0V9XCFcYmlnbFtsJyhcdGhldGEpXjJcYmlncl0NCiY9IFxtYXRoYmJ7RX1cIVxsZWZ0WyBcbGVmdCgtbiArIFxmcmFje1xzdW1fe2k9MX1ebiB5X2l9e1xsYW1iZGF9XHJpZ2h0KV4yIFxyaWdodF0gXFxbNHB0XQ0KJj0gXG1hdGhiYntFfVwhXGxlZnRbIFxmcmFje1xiaWdsKFxzdW1fe2k9MX1ebiB5X2lcYmlncileMn17XGxhbWJkYV4yfQ0KICAgXDstXDsgXGZyYWN7Mm4gXGJpZ2woXHN1bV97aT0xfV5uIHlfaVxiaWdyKX17XGxhbWJkYX0NCiAgIFw7K1w7IG5eMiBccmlnaHRdIFxcWzRwdF0NCiY9IFxmcmFje1xtYXRoYmJ7RX1cIVxiaWdsWyhcc3VtX3tpPTF9Xm4geV9pKV4yXGJpZ3JdfXtcbGFtYmRhXjJ9DQogICBcOy1cOyBcZnJhY3sybiBcc3VtX3tpPTF9Xm4gXG1hdGhiYntFfVt5X2ldfXtcbGFtYmRhfQ0KICAgXDsrXDsgbl4yIFxcWzRwdF0NCiY9IFxmcmFje25cbGFtYmRhICsgKG5cbGFtYmRhKV4yfXtcbGFtYmRhXjJ9DQogICBcOy1cOyBcZnJhY3sybiBcYmlnbChcc3VtX3tpPTF9Xm4gXGxhbWJkYVxiaWdyKX17XGxhbWJkYX0NCiAgIFw7K1w7IG5eMiBcXFs0cHRdDQomPSBcZnJhY3tufXtcbGFtYmRhfSArIG5eMiBcOy1cOyAybl4yIFw7K1w7IG5eMiBcXFs0cHRdDQomPSBcZnJhY3tufXtcbGFtYmRhfS4NClxlbmR7YWxpZ25lZH0NCiQkDQoNCldlIGNhbiBzZWUgdGhpcyBoYXMgYSBuaWNlIGZvcm0gYW5kIGF2b2lkcyBoYXZpbmcgdG8gYW5hbHl0aWNhbGx5IGRldGVybWluZSBhIHNlY29uZCBkZXJpdmF0aXZlLg0KDQpgYGB7cn0NCiMgbm93IGxldHMgYXBwbHkgRlMgdXNpbmcgc2FtZSBmcmFtZXdvcmsgYXMgYmVmb3JlDQojIHRoZSBzdGFydA0KdG9sID0gMTAgXiAtNA0KbGFtID0gMC4xDQptYXhpdCA9IDUwDQppdGVyID0gMA0KZXBzID0gSW5mDQoNCnN0YXJ0ID0gU3lzLnRpbWUoKQ0Kd2hpbGUgKGVwcyA+IHRvbCAmIGl0ZXIgPCBtYXhpdCkgew0KICAjIHNhdmUgdGhlIHByZXZpb3VzIHZhbHVlDQogIGxhbTAgPSBsYW0NCiAgDQogICMgY2FsY3VsYXRlIGgsIHRoZSBpbmNyZW1lbnQuIA0KICBoID0gZDEobGFtYmRhID0gbGFtLCB5ID0geSkgLyAobiAvIGxhbSkNCiAgDQogICMgdXBkYXRlIGxhbWJkYQ0KICBsYW0gPSBsYW0gKyBoDQogIA0KICAjIGxhbWJkYSBjYW50IGJlIGxlc3MgdGhhbiBvciBlcXVhbCB0byAwLCBzbyBwdXQgYSBjaGVjayBmb3IgdGhpcw0KICBpZiAobGFtIDw9IDApDQogICAgc3RvcCgibGFtYmRhIGxlcSAwIikNCiAgDQogICMgdXBkYXRlIHRoZSBsb2cgbGlrZWxpaG9vZA0KICBsb2dMID0gbG9nTGlrKGxhbWJkYSA9IGxhbSwgeSA9IHkpDQogIA0KICAjIGNhbGN1bGF0ZSB0aGUgZGlmZiwgY291bGQgYWxzbyB1c2UgdGhlIGxvZyBsaWtlbGlob29kIGlmIHdlIHdhbnRlZA0KICBlcHMgID0gYWJzKGxhbSAtIGxhbTApDQogIA0KICAjIHVwZGF0ZSB0aGUgaXRlcmF0aW9uIG51bWJlcg0KICBpdGVyID0gaXRlciArIDENCiAgaWYgKGl0ZXIgPT0gbWF4aXQpDQogICAgd2FybmluZygiSXRlcmF0aW9uIGxpbWl0IHJlYWNoZWQgd2l0aG91dCBjb252ZXJnZW5jZSIpDQogIA0KICAjIHByaW50IG91dCBpbmZvIHRvIGtlZXAgdHJhY2sNCiAgY2F0KHNwcmludGYoIkl0ZXI6ICVkIGxvZ0w6ICVmIGxhbTogJWYgaDogJWYgZXBzOiVmXG4iLCBpdGVyLCBsb2dMLCBsYW0sIGgsIGVwcykpDQp9DQpgYGANCg0KDQpgYGB7cn0NCmVuZCA9IFN5cy50aW1lKCkNCnByaW50KGVuZCAtIHN0YXJ0KQ0KYGBgDQoNCg0KIyBNdWx0aXZhcmlhdGUgT3B0aW1pemF0aW9uDQoNClRoZSBhcHByb2FjaGVzIHRoYXQgd2UgaGF2ZSBkZXNjcmliZWQgYWxzbyBleHRlbmQgdG8gdGhlIG11bHRpdmFyaWF0ZSBzZXR0aW5nLCB3aGVyZSB3ZSBzZWVrIHRvIG9wdGltaXplIGEgZnVuY3Rpb24gd2l0aCByZXNwZWN0IHRvIG1vcmUgdGhhbiBvbmUgcGFyYW1ldGVyIGF0IHRoZSBzYW1lIHRpbWUuIE1vcmUgZm9ybWFsbHksIHdlIHdpc2ggdG8gZmluZCB0aGUgb3B0aW11bSBvZiBzb21lIHJlYWzigJF2YWx1ZWQgZnVuY3Rpb24gJGYoXGJvbGRzeW1ib2x7XHRoZXRhfSkkLCB3aGVyZSAkXGJvbGRzeW1ib2x7XHRoZXRhfSQgaXMgbm93IGEgJHAkLWRpbWVuc2lvbmFsIHZlY3RvciBvZiBwYXJhbWV0ZXJzIHN1Y2ggdGhhdCAgDQoNCiQkDQpcYm9sZHN5bWJvbHtcdGhldGF9ID0gKFx0aGV0YV8xLCBcZG90cywgXHRoZXRhX3ApXlQuDQokJA0KDQpTaW1pbGFyIHRvIGJlZm9yZSwgdGhlIGVzdGltYXRlIG9mICRcYm9sZHN5bWJvbHtcdGhldGF9JCBhdCBzdGVwICR0JCB3aWxsIGJlIGRlbm90ZWQgYXMgJFxib2xkc3ltYm9se1x0aGV0YX1eeyh0KX0kLiBNb3N0IG9mIHRoZSBwcmlvciBjb25jZXB0cyBjb3ZlcmVkIGluIHVuaXZhcmlhdGUgbWF4aW1pemF0aW9u4oCUc3VjaCBhcyB0aGUgY2hvaWNlIG9mIHN0YXJ0aW5nIHBvaW50cywgaXRlcmF0aXZlIHVwZGF0aW5nLCBhbmQgc3BlY2lmaWNhdGlvbiBvZiBjb252ZXJnZW5jZSBjcml0ZXJpYSBmb3IgdGVybWluYXRpb27igJRhbHNvIGFwcGx5IGhlcmUuDQoNCg0KIyMgQ29udmVyZ2VuY2UgaW4gdGhlIG11bHRpdmFyaWF0ZSBzZXR0aW5nDQoNCk9idmlvdXNseSBpbiB0aGUgbXVsdGl2YXJpYXRlIHNldHRpbmcgd2UgY2Fubm90IGRpcmVjdGx5IHV0aWxpemUgdGhlIHNhbWUgY29udmVyZ2VuY2UgY3JpdGVyaWEgZGVmaW5lZCBlYXJsaWVyLiBUbyBleHRlbmQgdGhpcyB0byB0aGUgbXVsdGl2YXJpYXRlIHNldHRpbmcsIHdlIG1heSB1c2UgZGlzdGFuY2XigJFiYXNlZCBtZWFzdXJlcyBmb3IgY29udmVyZ2VuY2UsIGZvciBleGFtcGxlIGJhc2VkIG9uIHRoZSBzdW0gb2YgdGhlIGFic29sdXRlIGRpZmZlcmVuY2VzIGJldHdlZW4gaXRlcmF0aW9ucyAgDQoNCiQkDQpEKFxtYXRoYmZ7dX0sXG1hdGhiZnt2fSkgPSBcc3VtX3tpPTF9XnAgfHVfaSAtIHZfaXwsDQokJA0KDQpvciB0aGUgRXVjbGlkZWFuIGRpc3RhbmNlIGJldHdlZW4gaXRlcmF0aW9ucyAgDQoNCiQkDQpEKFxtYXRoYmZ7dX0sXG1hdGhiZnt2fSkgPSBcc3FydHtcc3VtX3tpPTF9XnAgKHVfaSAtIHZfaSleMiB9Lg0KJCQNCg0KVGhlIG91dHB1dCBvZiB0aGVzZSBmdW5jdGlvbnMgYXJlIHNjYWxhcnMsIHNvIGdpdmVuIGEgcGFydGljdWxhciBjaG9pY2Ugb2YgJEQoXG1hdGhiZnt1fSxcbWF0aGJme3Z9KSQsIHdlIGNhbiBkZWZpbmUgYW4gYWJzb2x1dGUgY29udmVyZ2VuY2UgdGhyZXNob2xkICRcZXBzaWxvbiQgdG8gdGVybWluYXRlIHRoZSBhbGdvcml0aG0gc3VjaCB0aGF0ICANCg0KJCQNCkRcIVxiaWdsKFxib2xkc3ltYm9se1x0aGV0YX1eeyh0KzEpfSwgXGJvbGRzeW1ib2x7XHRoZXRhfV57KHQpfVxiaWdyKSA8IFxlcHNpbG9uLg0KJCQNCg0KV2UgY2FuIHNpbWlsYXJseSBkZWZpbmUgYSByZWxhdGl2ZSBjb252ZXJnZW5jZSB0aHJlc2hvbGQgJFxlcHNpbG9uJCBzdWNoIHRoYXQgIA0KDQokJA0KXGZyYWN7RFwhXGJpZ2woXGJvbGRzeW1ib2x7XHRoZXRhfV57KHQrMSl9LCBcYm9sZHN5bWJvbHtcdGhldGF9XnsodCl9XGJpZ3IpfXtEXCFcYmlnbChcYm9sZHN5bWJvbHtcdGhldGF9XnsodCl9LCBcbWF0aGJmezB9XGJpZ3IpfSA8IFxlcHNpbG9uLg0KJCQNCg0KQXMgYmVmb3JlLCB3ZSBtYXkgYWxzbyBkZWNpZGUgdG8gdXNlIHRoZSB2YWx1ZSBvZiB0aGUgb2JqZWN0aXZlIGZ1bmN0aW9uIGl0c2VsZiBhcyB0aGUgYmFzaXMgZm9yIG91ciBjcml0ZXJpb24gZm9yIGNvbnZlcmdlbmNlICh0aGUgZXN0aW1hdGVkIGxpa2VsaWhvb2QgYXQgaXRlcmF0aW9uICR0JCwgZm9yIGV4YW1wbGUpLiBJbiBzb21lIGNhc2VzLCBwYXJ0aWN1bGFybHkgd2hlbiB0aGUgZGltZW5zaW9uIG9mIHRoZSBwYXJhbWV0ZXIgc3BhY2UgaXMgaGlnaCwgdGhpcyBhcHByb2FjaCBtYXkgYmUgc2ltcGxlciB0byB1dGlsaXplLg0KDQpXZSB3aWxsIG5vdyBkaXNjdXNzIG11bHRpdmFyaWF0ZSBleHRlbnNpb25zIHRvIHRoZSBtZXRob2RzIGludHJvZHVjZWQgaW4gdGhlIHByZXZpb3VzIHNlY3Rpb24uDQoNCg0KIyMgTmV3dG9uLVJhcGhzb24gYW5kIEZpc2hlciBTY29yaW5nDQoNClVzaW5nIHRoZSByYXRpb25hbGUgZGVzY3JpYmVkIGluIHRoZSB1bml2YXJpYXRlIHNldHRpbmcsIHdlIGNhbiBkZWZpbmUgdGhlIE5SIHVwZGF0aW5nIGFsZ29yaXRobSBpbiB0aGlzIHNldHRpbmcgdXNpbmcgYSBxdWFkcmF0aWMgVGF5bG9yIFNlcmllcyBleHBhbnNpb24gYXJvdW5kICRmKFxib2xkc3ltYm9se1x0aGV0YX1eKikkOg0KDQokJA0KZihcYm9sZHN5bWJvbHtcdGhldGF9XiopID0gZlxiaWdsKFxib2xkc3ltYm9se1x0aGV0YX1eeyh0KX1cYmlncikgDQorIFxiaWdsKFxib2xkc3ltYm9se1x0aGV0YX1eKiAtIFxib2xkc3ltYm9se1x0aGV0YX1eeyh0KX1cYmlncileVCANCiAgZidcYmlnbChcYm9sZHN5bWJvbHtcdGhldGF9XnsodCl9XGJpZ3IpDQorIFxmcmFjezF9ezJ9IFxiaWdsKFxib2xkc3ltYm9se1x0aGV0YX1eKiAtIFxib2xkc3ltYm9se1x0aGV0YX1eeyh0KX1cYmlncileVCANCiAgZicnXGJpZ2woXGJvbGRzeW1ib2x7XHRoZXRhfV57KHQpfVxiaWdyKSANCiAgXGJpZ2woXGJvbGRzeW1ib2x7XHRoZXRhfV4qIC0gXGJvbGRzeW1ib2x7XHRoZXRhfV57KHQpfVxiaWdyKS4NCiQkDQoNCklmIHdlIHRha2UgdGhlIGRlcml2YXRpdmUgb2YgdGhpcyBleHBhbnNpb24gYW5kIHNldCBpdCBlcXVhbCB0byB6ZXJvLCB3ZSBnZXQNCg0KJCQNCjAgPSBmJ1xiaWdsKFxib2xkc3ltYm9se1x0aGV0YX1eeyh0KX1cYmlncikgDQogICAgKyBmJydcYmlnbChcYm9sZHN5bWJvbHtcdGhldGF9XnsodCl9XGJpZ3IpIA0KICAgICAgXGJpZ2woXGJvbGRzeW1ib2x7XHRoZXRhfV4qIC0gXGJvbGRzeW1ib2x7XHRoZXRhfV57KHQpfVxiaWdyKSwNCiQkDQoNCndoaWNoIHByb3ZpZGVzIHRoZSB1cGRhdGUNCg0KJCQNClxib2xkc3ltYm9se1x0aGV0YX1eeyh0KzEpfSA9IFxib2xkc3ltYm9se1x0aGV0YX1eeyh0KX0gDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLSBmJydcYmlnbChcYm9sZHN5bWJvbHtcdGhldGF9XnsodCl9XGJpZ3IpXnstMX0gDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmJ1xiaWdsKFxib2xkc3ltYm9se1x0aGV0YX1eeyh0KX1cYmlncikuDQokJA0KDQpBcyBhIHNpZGUgbm90ZTogaWYgeW91IHJlY2FsbCwgaW4gb3VyIGRlcml2YXRpb24gZnJvbSB0aGUgdW5pdmFyaWF0ZSBzZXR0aW5nIHdlIHN0YXJ0ZWQgd2l0aCBhIGxpbmVhciBUYXlsb3IgU2VyaWVzIGV4cGFuc2lvbiBvZiAkZicoXHRoZXRhXiokLiBEb2luZyB0aGUgc2FtZSBoZXJlIHdvdWxkIGFsbG93IHVzIHRvIGFycml2ZSBhdCB0aGUgc2FtZSBleHByZXNzaW9uIGdpdmVuIGFib3ZlIChoZXJlIHdlIHRha2UgdGhlIGRlcml2YXRpdmUgb2YgdGhlIGV4cGFuc2lvbiBhcm91bmQgJGYoXGJvbGRzeW1ib2x7XHRoZXRhfV4qKSQpLiAgDQoNClRodXMsIG91ciBpbmNyZW1lbnQgJFxtYXRoYmZ7aH1eeyh0KX0kIGlzIHNpbXBseSAgDQoNCiQkDQpcbWF0aGJme2h9XnsodCl9ID0gLSBmJydcYmlnbChcYm9sZHN5bWJvbHtcdGhldGF9XnsodCl9XGJpZ3IpXnstMX0gDQogICAgICAgICAgICAgICAgICAgICBmJ1xiaWdsKFxib2xkc3ltYm9se1x0aGV0YX1eeyh0KX1cYmlncikuDQokJA0KDQpGaXNoZXIgc2NvcmluZyBpbiB0aGlzIGNhc2UgYWxzbyBoYXMgYSBzaW1pbGFyIHVwZGF0aW5nIGZ1bmN0aW9uOg0KDQokJA0KXGJvbGRzeW1ib2x7XHRoZXRhfV57KHQrMSl9ID0gXGJvbGRzeW1ib2x7XHRoZXRhfV57KHQpfSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICArIElcYmlnbChcYm9sZHN5bWJvbHtcdGhldGF9XnsodCl9XGJpZ3IpXnstMX0gDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsJ1xiaWdsKFxib2xkc3ltYm9se1x0aGV0YX1eeyh0KX1cYmlncikuDQokJA0KDQpUaGUgcHJvcGVydGllcyBvZiB0aGVzZSBhbGdvcml0aG1zIHdpbGwgYmUgc2ltaWxhciB0byB0aG9zZSBkZXNjcmliZWQgaW4gdGhlIHVuaXZhcmlhdGUgc2VjdGlvbi4NCg0KV2Ugd2lsbCBpbGx1c3RyYXRlIHRoZXNlIG11bHRpdmFyaWF0ZSByZXN1bHRzIHdpdGggYW4gZXhhbXBsZSBpbiB0aGUgbmV4dCBzZWN0aW9uLg0KDQoNCiMjIyBHTE1zOiBOUiwgRlMsIGFuZCBJUkxTDQoNCkdlbmVyYWxpemVkIExpbmVhciBNb2RlbHMgKEdMTXMpIGVuY29tcGFzcyBhIGxhcmdlIGZhbWlseSBvZiBtb2RlbHMgaW5jbHVkaW5nIGxpbmVhciByZWdyZXNzaW9uLCBsb2dpc3RpYyByZWdyZXNzaW9uIChiaW5hcnkgcmVzcG9uc2VzKSwgUG9pc3NvbiByZWdyZXNzaW9uIChjb3VudCByZXNwb25zZXMpLCBhbmQgbWFueSBvdGhlcnMuIEluIGNvbnRyYXN0IHRvIGxpbmVhciByZWdyZXNzaW9uLCB0aGUgY2xhc3Mgb2YgbW9kZWxzIGVuY29tcGFzc2VkIGJ5IEdMTXMgY2FuIGhhbmRsZSByZXNwb25zZSB2YXJpYWJsZXMgb2YgZGlmZmVyZW50IGRpc3RyaWJ1dGlvbnMsIHdoZXJlIHN1Y2ggZGlzdHJpYnV0aW9ucyBhcmUgbWVtYmVycyBvZiB3aGF0IHdlIGNhbGwgdGhlICoq4oCcZXhwb25lbnRpYWwgZmFtaWx54oCdKiogb2YgZGlzdHJpYnV0aW9ucy4NCg0KVGhlIGV4cG9uZW50aWFsIGZhbWlseSBoYXMgdGhlIGdlbmVyYWwgZm9ybSAgDQoNCiQkDQpmKHk7XHRoZXRhLFxwaGkpID0gXGV4cFwhXGxlZnRceyBcZnJhY3t5XHRoZXRhIC0gYihcdGhldGEpfXthKFxwaGkpfSArIGMoeSxccGhpKSBccmlnaHRcfSwNCiQkDQoNCndoZXJlICRcdGhldGEkIGlzIGNhbGxlZCB0aGUgKirigJxuYXR1cmFs4oCdKiogb3IgKirigJxjYW5vbmljYWzigJ0qKiBwYXJhbWV0ZXIgYW5kICRccGhpJCBpcyBhIGZpeGVkICoqZGlzcGVyc2lvbiBwYXJhbWV0ZXIqKi4gVGhlIGZvbGxvd2luZyBkaXN0cmlidXRpb25zIGNhbiBiZSBmYWN0b3JlZCBpbnRvIHRoaXMgZm9ybSwgYW5kIHRoZXJlZm9yZSBiZWxvbmcgdG8gdGhlIGV4cG9uZW50aWFsIGZhbWlseToNCg0KLSBCZXJub3VsbGkNCi0gUG9pc3Nvbg0KLSBOb3JtYWwNCi0gRXhwb25lbnRpYWwNCi0gR2FtbWENCi0gQ2hp4oCRU3F1YXJlZA0KLSBCZXRhDQotIERpcmljaGxldA0KLSBXaXNoYXJ0DQotIEdlb21ldHJpYw0KLSBCaW5vbWlhbCAoZml4ZWQgbnVtYmVyIG9mIHRyaWFscykNCi0gTmVnYXRpdmUgQmlub21pYWwgKGZpeGVkIG51bWJlciBvZiBmYWlsdXJlcyBvciBmaXhlZCBvdmVyZGlzcGVyc2lvbiBwYXJhbWV0ZXIpDQotIE11bHRpbm9taWFsIChmaXhlZCBudW1iZXIgb2YgdHJpYWxzKQ0KDQo+ICoqTm90ZToqKiBUaGUgbGFzdCB0aHJlZSBkaXN0cmlidXRpb25zIGhhdmUgKmNvbmRpdGlvbmFsKiBtZW1iZXJzaGlwIHRvIHRoaXMgZmFtaWx5LCB3aGljaCBpbiBzb21lIGNhc2VzIGltcGFjdHMgaG93IGxpa2VsaWhvb2QgZnVuY3Rpb25zIGJhc2VkIG9uIHRoZXNlIGRpc3RyaWJ1dGlvbnMgYXJlIG1heGltaXplZCAoYW4gZXhhbXBsZSBvZiB0aGlzIHdpbGwgYmUgZ2l2ZW4gbGF0ZXIpLg0KDQpGb3IgdGhlc2UgZGlzdHJpYnV0aW9ucywgd2UgY2FuIGZhY3RvciB0aGVpciBkZW5zaXRpZXMgaW50byB0aGUgZm9ybSBhYm92ZSB0byBkZXRlcm1pbmUgJGIoXHRoZXRhKSQsICRhKFxwaGkpJCwgYW5kICRjKHksXHBoaSkkLiBXZSBjYW4gdGhlbiB1dGlsaXplIHRoZXNlIHF1YW50aXRpZXMgdG8gZXN0aW1hdGUgdGhlIHBhcmFtZXRlcnMgb2YgaW50ZXJlc3QgaW4gb3VyIG1vZGVsIGluIGEgZ2VuZXJhbCB1bmlmaWVkIGZyYW1ld29yay4gRm9yIGV4YW1wbGUsIHdlIGNhbiBzaG93IHRoYXQgIA0KDQokJA0KXG1hdGhiYntFfVtZXSA9IGInKFx0aGV0YSkgXHF1YWQgXHRleHR7YW5kfSBccXVhZCBcb3BlcmF0b3JuYW1le1Zhcn1bWV0gPSBiJycoXHRoZXRhKVwsYShccGhpKS4NCiQkDQoNClNpbWlsYXIgdG8gbGluZWFyIHJlZ3Jlc3Npb24sIHdlIG9mdGVuIHdpc2ggdG8gbW9kZWwgJFlfaSQgd2l0aCByZXNwZWN0IHRvIHNvbWUgdmVjdG9yIG9mIGNvdmFyaWF0ZXMgJFxtYXRoYmZ7WH1faSQgKHRoZSAkaSQtdGggcm93IG9mIGFuICRuIFx0aW1lcyBwJCBtYXRyaXggb2YgY292YXJpYXRlcyAkXG1hdGhiZntYfSQpLiBMZXQgdXMgZGVmaW5lICRcbXVfaSA9IFxtYXRoYmJ7RX1bWV9pIFxtaWQgXG1hdGhiZntYfV9pXSQuIEluIGEgR0xNLCB3ZSBhc3N1bWUgdGhhdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gJFxtYXRoYmZ7WH1faSQgYW5kICRcbXVfaSQgaXMgZ2l2ZW4gYnkgIA0KDQokJA0KZyhcbXVfaSkgPSBcbWF0aGJme1h9X2lcYm9sZHN5bWJvbHtcYmV0YX0gPSBcZXRhX2ksDQokJA0KDQp3aGVyZSANCg0KLSAkZyQgaXMgY2FsbGVkIHRoZSAqKmxpbmsgZnVuY3Rpb24qKiwgIA0KLSAkXGJvbGRzeW1ib2x7XGJldGF9JCBpcyBhIHZlY3RvciBvZiB1bmtub3duIHBhcmFtZXRlcnMgdG8gYmUgZXN0aW1hdGVkLCBhbmQgIA0KLSAkXGV0YV9pJCBpcyBjYWxsZWQgdGhlICoqbGluZWFyIHByZWRpY3RvcioqLiAgDQoNClRoZSBpbnZlcnNlIGxpbmsgZnVuY3Rpb24sICRnXnstMX0oXGV0YV9pKSA9IFxtdV9pJCwgY2FuIGJlIHVzZWQgdG8gYmFja+KAkXRyYW5zZm9ybSB0aGUgbGluZWFyIHByZWRpY3RvciBmb3Igc3ViamVjdCAkaSQgdG8gb2J0YWluICRcbXVfaSQuIFRoYXQgaXMsIHdlIG1vZGVsIHNvbWUgZnVuY3Rpb24gb2YgJFxtYXRoYmJ7RX1bWV9pIFxtaWQgXG1hdGhiZntYfV9pXSQgd2l0aCBhIHNldCBvZiByZWdyZXNzaW9uIGNvZWZmaWNpZW50cywgd2hlcmUgdGhpcyBmdW5jdGlvbiBpcyB0aGUgc2VsZWN0ZWQgbGluayAkZyQuDQoNCkZvciBleGFtcGxlLCBpbiBsaW5lYXIgcmVncmVzc2lvbiAkZyQgaXMgc2ltcGx5IHRoZSAqKmlkZW50aXR5IGxpbmsqKiB3aGVyZSAkXG1hdGhiYntFfVtZX2kgXG1pZCBcbWF0aGJme1h9X2ldID0gXG11X2kgPSBcbWF0aGJme1h9X2lcYm9sZHN5bWJvbHtcYmV0YX0kLCBhbmQgbm8gdHJhbnNmb3JtYXRpb24gaXMgcGVyZm9ybWVkLiBGb3IgZWFjaCBtZW1iZXIgb2YgdGhlIGV4cG9uZW50aWFsIGZhbWlseSwgZGlmZmVyZW50IGxpbmsgZnVuY3Rpb25zIG1heSBiZSB1dGlsaXplZCwgYWx0aG91Z2ggc29tZSBtYXkgYmUgbW9yZSBtYXRoZW1hdGljYWxseSBjb252ZW5pZW50IHRoYW4gb3RoZXJzLCBhcyB3ZSB3aWxsIHNlZS4NCg0KSW4gZ2VuZXJhbCwgaWYgd2UgZmluZCB0aGF0ICRiJyhcdGhldGFfaSkgPSBnXnstMX0oXHRoZXRhX2kpJCwgdGhlbiB0aGlzIGltcGxpZXMgJFx0aGV0YV9pID0gXGV0YV9pJCBhbmQgdGhhdCAkZyhcY2RvdCkkIGlzIHRoZSAqKmNhbm9uaWNhbCBsaW5rIGZ1bmN0aW9uKiosIGFuIGV4YW1wbGUgb2Ygc3VjaCBhIG1hdGhlbWF0aWNhbGx5IGNvbnZlbmllbnQgbGluay4gSWYgdGhpcyBpcyBuZXcgdG8geW91LCBkb27igJl0IHdvcnJ5LiBZb3Ugd2lsbCBsZWFybiBHTE1zIGluIHNvbWUgc3BlY2lhbGl6ZWQgY291cnNlcy4gV2Ugd2lsbCBub3QgZGl2ZSBkZWVwIGluIHRoZSBHTE0uDQoNClwNCg0KIyMgTmV3dG9uLWxpa2UgbWV0aG9kcw0KDQpBIHdpZGUgdmFyaWV0eSBvZiDigJxOZXd0b27igJFMaWtl4oCdIG9yIOKAnFF1YXNp4oCRTmV3dG9u4oCdIGFwcHJvYWNoZXMgZXhpc3QsIHdoZXJlIHRoZSB1cGRhdGluZyBlcXVhdGlvbiB0YWtlcyBvbiBhIGZvcm0gc2ltaWxhciB0byBOUiBidXQgdXN1YWxseSAqKmFwcHJveGltYXRlcyB0aGUgbWF0cml4IG9mIDJuZCBkZXJpdmF0aXZlcyAoSGVzc2lhbikqKi4gSGVyZSwgZXhwcmVzc2lvbnMgZm9yIG9ubHkgdGhlIGZpcnN0IGRlcml2YXRpdmVzIG9mIHRoZSBmdW5jdGlvbiBvZiBpbnRlcmVzdCBhcmUgbmVlZGVkIGFuZCBhbmFseXRpY2FsIGZvcm1zIGZvciB0aGUgc2Vjb25kIGRlcml2YXRpdmVzIGFyZSBub3QgcmVxdWlyZWQuDQoNClRoaXMgaXMgaGVscGZ1bCB3aGVuIHRoZSBzZWNvbmQgZGVyaXZhdGl2ZXMgbWF5IGJlIGRpZmZpY3VsdCB0byBkZXJpdmUsIGhhdmUgYSBjb21wbGljYXRlZCBmb3JtLCBvciBhcmUgY29tcHV0YXRpb25hbGx5IGV4cGVuc2l2ZSB0byBldmFsdWF0ZSBpbiBwcmFjdGljZS4NCg0KSW4gdGhpcyBjbGFzcyBvZiBtZXRob2RzIHdlIG1heSB3cml0ZSB0aGUgdXBkYXRpbmcgZXF1YXRpb24gYXMgIA0KDQokJA0KXGJvbGRzeW1ib2x7XHRoZXRhfV57KHQrMSl9ID0gXGJvbGRzeW1ib2x7XHRoZXRhfV57KHQpfSAtIFxiaWdsKFxtYXRoYmZ7TX1eeyh0KX1cYmlncileey0xfSBmJyhcYm9sZHN5bWJvbHtcdGhldGF9KSwNCiQkDQoNCndoZXJlICRcbWF0aGJme019XnsodCl9JCBpcyBhICRwIFx0aW1lcyBwJCBtYXRyaXggYXBwcm94aW1hdGluZyB0aGUgSGVzc2lhbiAkZicnKFxib2xkc3ltYm9se1x0aGV0YX0pJCAoYXNzdW1pbmcgJFxib2xkc3ltYm9se1x0aGV0YX0kIGlzIGEgJHAkLWRpbWVuc2lvbmFsIHZlY3RvcikuDQoNCldlIG1heSBjaG9vc2UgdG8gZG8gdGhpcyBiZWNhdXNlIGVpdGhlciAgDQoNCjEuIGV2YWx1YXRpbmcgdGhlIEhlc3NpYW4gaXMgdG9vIGNvbXB1dGF0aW9uYWxseSBleHBlbnNpdmUsIG9yICANCg0KMi4gdGhlIHN0ZXBzIHNlbGVjdGVkIGJ5IE5SIG1heSBub3QgZ28gdXBoaWxsIChpZiBtYXhpbWl6aW5nKSwgYXMgaXQgaXMgbm90IGd1YXJhbnRlZWQgdGhhdCAkZlxiaWdsKFxib2xkc3ltYm9se1x0aGV0YX1eeyh0KzEpfVxiaWdyKSA+IGZcYmlnbChcYm9sZHN5bWJvbHtcdGhldGF9XnsodCl9XGJpZ3IpJCB3aGVuIHdlIGFyZSBtYXhpbWl6aW5nIHNvbWUgZnVuY3Rpb24gJGYoXGJvbGRzeW1ib2x7XHRoZXRhfSkkIChzZWUgTlIgZGl2ZXJnZW5jZSBleGFtcGxlIGZyb20gZWFybGllcikuIFdlIHdpbGwgdGFsayBhYm91dCB0aGUgbWV0aG9kIG9mIOKAnHN0ZWVwZXN0IGFzY2VudC9kZXNjZW504oCdIGxhdGVyIHRoYXQgdHJpZXMgdG8gZW5zdXJlIGltcHJvdmVtZW50cyBpbiB0aGUgb2JqZWN0aXZlIGZ1bmN0aW9uIHdpdGggZWFjaCB1cGRhdGUuDQoNCkFsdGVybmF0aXZlbHksIHdlIGNhbiBjaG9vc2UgYW4gJFxtYXRoYmZ7TX1eeyh0KX0kIHRoYXQgY2FuIGd1YXJhbnRlZSAkZlxiaWdsKFxib2xkc3ltYm9se1x0aGV0YX1eeyh0KzEpfVxiaWdyKSA+IGZcYmlnbChcYm9sZHN5bWJvbHtcdGhldGF9XnsodCl9XGJpZ3IpJCAoYXNjZW50KS4gIA0KDQpEdWUgdG8gdGltZSBjb25zdHJhaW50cywgd2Ugd2lsbCBub3QgY292ZXIgRGlzY3JldGUgTmV3dG9uLCBGaXhlZCBQb2ludCBNZXRob2RzLCBvciBHYXVzc+KAkU5ld3RvbiBpbiBkZXRhaWwuIEhvd2V2ZXIsIGlmIGludGVyZXN0ZWQgeW91IGNhbiBmaW5kIHRoZXNlIHRvcGljcyBjb3ZlcmVkIGluIEdIIDIuMi4yLiBXZSB3aWxsIGluc3RlYWQgY292ZXIgbW9yZSBjb21tb25seSB1c2VkIGFsZ29yaXRobXMgc3VjaCBhcyAqKkJGR1MqKiAod2hpY2ggaW4gZ2VuZXJhbCBoYXMgYmV0dGVyIHBlcmZvcm1hbmNlIHRoYW4gdGhlIGFmb3JlbWVudGlvbmVkIG1ldGhvZHMpLCAqKk5lbGRlcuKAkU1lYWQqKiwgKipncmFkaWVudCBkZXNjZW50L2FzY2VudCoqLCBhbmQgKipzdG9jaGFzdGljIGdyYWRpZW50IGRlc2NlbnQvYXNjZW50KiouDQoNCkluIGdlbmVyYWwsIG9uZSBtYXkgdHJ5IHRvIGF2b2lkIGNhbGN1bGF0aW9uIG9mIHRoZSBIZXNzaWFuIG1hdHJpeCBieSBhcHByb3hpbWF0aW5nIGl0IHVzaW5nIGFuIGFwcHJvYWNoIGFraW4gdG8gdGhlIHNlY2FudOKAkWJhc2VkIGZpbml0ZeKAkWRpZmZlcmVuY2UgYXBwcm9hY2hlcyBkZXRhaWxlZCBpbiB0aGUgdW5pdmFyaWF0ZSBtYXhpbWl6YXRpb24gcG9ydGlvbiBvZiB0aGlzIGxlY3R1cmUgKGRpc2NyZXRlIE5ld3RvbiBvciBmaXhlZCBwb2ludCBtZXRob2RzIGFsc28gZG8gdGhpcykuIEhvd2V2ZXIsIHRoaXMgY2FuIGJlY29tZSBjb21wdXRhdGlvbmFsbHkgYnVyZGVuc29tZSBlc3BlY2lhbGx5IGlmICRcbWF0aGJme019JCBpcyBvZiBsYXJnZXIgZGltZW5zaW9uIGFuZCBiZWNhdXNlICRcbWF0aGJme019XnsodCl9JCBtYXkgaGF2ZSB0byBiZSB1cGRhdGVkIGF0IGVhY2ggaXRlcmF0aW9uIHRvIGVuc3VyZSBmYXN0ZXIgY29udmVyZ2VuY2UuDQoNCg0KDQojIyMgUXVhc2ktTmV3dG9uIE1ldGhvZHM6IEJGR1MNCg0KSW5zdGVhZCwgd2UgY2FuIHVzZSAqKlF1YXNp4oCRTmV3dG9uKiogbWV0aG9kcyB3aGVyZSAkXG1hdGhiZntNfV57KHQpfSQgaXMgdXBkYXRlZCB3aXRoIGtub3dsZWRnZSBvZiB0aGUgY3VydmF0dXJlIG9mICRmJCBpbiB0aGUgZGlyZWN0aW9uIG9mIHRoZSBwcm9wb3NlZCBzdGVwICRcbWF0aGJme2h9XnsodCl9JCBuZWFyICRcYm9sZHN5bWJvbHtcdGhldGF9XnsodCl9JCB3aGlsZSB3ZSBhcmUgcGVyZm9ybWluZyB0aGUgdXBkYXRlICANCg0KJCQNClxib2xkc3ltYm9se1x0aGV0YX1eeyh0KzEpfSA9IFxib2xkc3ltYm9se1x0aGV0YX1eeyh0KX0gKyBcbWF0aGJme2h9XnsodCl9IC4NCiQkDQoNCldlIHdvdWxkIGxpa2UgdG8gYXZvaWQgY29tcHV0aW5nIHRoZSBhcHByb3hpbWF0aW9uIG9mIGVhY2ggZWxlbWVudCBvZiB0aGUgSGVzc2lhbiBtYXRyaXggb25l4oCRYnnigJFvbmUgdG8gcmVkdWNlIGNvbXB1dGF0aW9uYWwgYnVyZGVuLiBBdCB0aGUgc2FtZSB0aW1lLCB3ZSB3b3VsZCBhbHNvIGxpa2UgdG8gcmV0YWluIGEgc2ltaWxhciBzZWNhbnTigJF0eXBlIGNvbmRpdGlvbiB3aGVyZSAgDQoNCiQkDQpmJ1whXGJpZ2woXGJvbGRzeW1ib2x7XHRoZXRhfV57KHQrMSl9XGJpZ3IpIC0gZidcIVxiaWdsKFxib2xkc3ltYm9se1x0aGV0YX1eeyh0KX1cYmlncikgPSBcbWF0aGJme019XnsodCsxKX1cYmlnbChcYm9sZHN5bWJvbHtcdGhldGF9XnsodCsxKX0gLSBcYm9sZHN5bWJvbHtcdGhldGF9XnsodCl9XGJpZ3IpLA0KJCQNCg0KZXNzZW50aWFsbHkgYXBwcm94aW1hdGluZyB0aGUgZmluaXRl4oCRZGlmZmVyZW5jZSBhcHByb2FjaCB1c2luZyAkXG1hdGhiZntNfV57KHQrMSl9JC4gVGhpcyBlcXVhdGlvbiBpbXBsaWVzIHRoYXQgdGhlIHNwZWNpZmljYXRpb24gb2Ygc29tZSBtYXRyaXggJFxtYXRoYmZ7TX1eeyh0KzEpfSQgdGltZXMgdGhlIGRpZmZlcmVuY2UgaW4gdGhlIGVzdGltYXRlIG9mICRcYm9sZHN5bWJvbHtcdGhldGF9JCBiZXR3ZWVuIGl0ZXJhdGlvbnMgaXMgZXF1YWwgdG8gdGhlIGZpbml0ZeKAkWRpZmZlcmVuY2UgYXBwcm94aW1hdGlvbiwgcHJlc2VydmluZyB0aGUgc2VjYW50IGNvbmRpdGlvbi4gIA0KDQpFc3NlbnRpYWxseSwgd2UgbmVlZCBhbiBhcHByb2FjaCB0byBnZW5lcmF0ZSAkXG1hdGhiZntNfV57KHQrMSl9JCBmcm9tICRcbWF0aGJme019XnsodCl9JCB0aGF0IG1pbmltaXplcyB0aGUgbnVtYmVyIG9mIGNhbGN1bGF0aW9ucyBuZWVkZWQgYW5kIHByZXNlcnZlcyB0aGUgYWJvdmUgY29uZGl0aW9uLg0KDQpUaGUgcXVlc3Rpb24gaXM6IGhvdyBkbyB3ZSBvYnRhaW4gYW4gJFxtYXRoYmZ7TX1eeyh0KzEpfSQgdGhhdCBzYXRpc2ZpZXMgdGhpcyBjb25kaXRpb24gYW5kIGNhbiBiZSBkZXRlcm1pbmVkIGluIGEgY29tcHV0YXRpb25hbGx5IGVmZmljaWVudCB3YXk/IFF1YXNp4oCRTmV3dG9uIChvciDigJx2YXJpYWJsZSBzdGVw4oCdKSBtZXRob2RzIGFyZSBvbmUgc3VjaCBhcHByb2FjaCB0aGF0IGNhbiBkbyB0aGlzIHdoZW4gYXBwcm94aW1hdGluZyB0aGUgSGVzc2lhbiBhdCBlYWNoIGl0ZXJhdGlvbi4gVGhlIHVwZGF0ZSB0byB0aGUgSGVzc2lhbiBoYXMgdGhlIGZvbGxvd2luZyBmb3JtICANCg0KJCQNClxiZWdpbnthbGlnbmVkfQ0KXG1hdGhiZntNfV57KHQrMSl9ID0gXG1hdGhiZntNfV57KHQpfQ0KJi1cZnJhY3tcbWF0aGJme019XnsodCl9XG1hdGhiZnt6fV57KHQpfVxiaWdsKFxtYXRoYmZ7TX1eeyh0KX1cbWF0aGJme3p9XnsodCl9XGJpZ3IpXlR9DQogICAgICB7XGJpZ2woXG1hdGhiZnt6fV57KHQpfVxiaWdyKV5UXG1hdGhiZntNfV57KHQpfVxtYXRoYmZ7en1eeyh0KX19DQorXGZyYWN7XG1hdGhiZnt5fV57KHQpfVxiaWdsKFxtYXRoYmZ7eX1eeyh0KX1cYmlncileVH0NCiAgICAgIHtcYmlnbChcbWF0aGJme3p9XnsodCl9XGJpZ3IpXlRcbWF0aGJme3l9XnsodCl9fSBcXA0KJitcZGVsdGFeeyh0KX1cYmlnbChcYmlnbChcbWF0aGJme3p9XnsodCl9XGJpZ3IpXlRcbWF0aGJme019XnsodCl9XG1hdGhiZnt6fV57KHQpfVxiaWdyKQ0KICAgXG1hdGhiZntkfV57KHQpfVxiaWdsKFxtYXRoYmZ7ZH1eeyh0KX1cYmlncileVCwNClxlbmR7YWxpZ25lZH0NCiQkDQoNCndoZXJlICANCg0KJCQNClxtYXRoYmZ7ZH1eeyh0KX0gPSBcZnJhY3tcbWF0aGJme3l9XnsodCl9fXtcYmlnbChcbWF0aGJme3p9XnsodCl9XGJpZ3IpXlRcbWF0aGJme3l9XnsodCl9fQ0KICAgICAgICAgICAgICAgICAgLVxmcmFje1xtYXRoYmZ7TX1eeyh0KX1cbWF0aGJme3p9XnsodCl9fXtcYmlnbChcbWF0aGJme3p9XnsodCl9XGJpZ3IpXlRcbWF0aGJme019XnsodCl9XG1hdGhiZnt6fV57KHQpfX0sDQokJA0KDQp3aXRoICRcbWF0aGJme3p9XnsodCl9ID0gXGJvbGRzeW1ib2x7XHRoZXRhfV57KHQrMSl9IC0gXGJvbGRzeW1ib2x7XHRoZXRhfV57KHQpfSQgYW5kICRcbWF0aGJme3l9XnsodCl9ID0gZidcIVxiaWdsKFxib2xkc3ltYm9se1x0aGV0YX1eeyh0KzEpfVxiaWdyKSAtIGYnXCFcYmlnbChcYm9sZHN5bWJvbHtcdGhldGF9XnsodCl9XGJpZ3IpJC4NCg0KVGhpcyBjbGFzcyBvZiBhbGdvcml0aG1zIGlzIGluZGV4ZWQgYnkgdGhlIHZhbHVlIG9mICRcZGVsdGFeeyh0KX0kLCB3aGVyZSAkXGRlbHRhXnsodCl9ID0gMCQgcmVwcmVzZW50cyB0aGUgcG9wdWxhciAqKkJGR1MqKiB1cGRhdGUuIEJGR1MgaXMgZ2VuZXJhbGx5IHJlZ2FyZGVkIGFzIHRoZSBiZXN0IHBlcmZvcm1pbmcgbWV0aG9kIGluIHRoaXMgY2xhc3MgYW5kIGlzIGNvbW1vbmx5IHVzZWQuIEluIGdlbmVyYWwsIHRocm91Z2ggdGhlIHVzZSBvZiAqKmJhY2t0cmFja2luZyoqICh3aGljaCB3ZSB3aWxsIGRpc2N1c3MgaW4gbW9yZSBkZXRhaWwgaW4gdGhlIG5leHQgc2VjdGlvbikgZHVyaW5nIHRoZSBjYWxjdWxhdGlvbiBvZiAkXG1hdGhiZntNfV57KHQpfSQsIHdlIGNhbiBlbnN1cmUgYXNjZW50Lg0KDQpBbm90aGVyIG5pY2UgcHJvcGVydHkgb2YgQkZHUyBpcyB0aGF0IHRoZSAqKnBvc2l0aXZlIGRlZmluaXRlbmVzcyoqIG9mIHRoZSBIZXNzaWFuIGFwcHJveGltYXRpb24gaXMgYWxzbyBlbnN1cmVkLCBtZWFuaW5nIHRoYXQgd2UgY2FuIHRha2UgdGhlIGludmVyc2Ugb2YgdGhpcyBtYXRyaXgsIGxlYWRpbmcgdG8gaW5jcmVhc2VkIHJvYnVzdG5lc3Mgb2YgdGhlIG1ldGhvZC4gSWYgeW91IHJlY2FsbCwgb25lIG9mIHRoZSBkb3duc2lkZXMgb2YgTlIgaW4gdGhlIG11bHRpdmFyaWF0ZSBzZXR0aW5nIGlzIHRoZSBsYWNrIG9mIHRoaXMgZ3VhcmFudGVlLCBsZWFkaW5nIHRvIHBvdGVudGlhbCBpbnN0YWJpbGl0eSBvZiB0aGUgYXBwcm9hY2ggKGVzcGVjaWFsbHkgYXMgdGhlIGRpbWVuc2lvbiBncm93cykuIFdlIHdpbGwgdG91Y2ggbW9yZSBvbiB0aGlzIGxhdGVyLg0KDQpXZSB3b27igJl0IHdvcnJ5IGFib3V0IHRoZSBzcGVjaWZpYyBkZXRhaWxzIHJlZ2FyZGluZyBob3cgdGhpcyB1cGRhdGUgd2FzIGRlcml2ZWQuIE1hbnkgb2Zm4oCRdGhl4oCRc2hlbGYgbWV0aG9kcyBhcmUgYXZhaWxhYmxlIGltcGxlbWVudGluZyB0aGlzIHByb2NlZHVyZSwgZ2l2ZW4gYSBrbm93biBsaWtlbGlob29kIGZ1bmN0aW9uIGFuZCAxc3QgZGVyaXZhdGl2ZSAod2Ugd2lsbCBnaXZlIGFuIGV4YW1wbGUgb2YgdGhpcyBsYXRlciksIHNvIG5vIG1hbnVhbCBpbXBsZW1lbnRhdGlvbiBvZiB0aGlzIGFwcHJvYWNoIGlzIG5lY2Vzc2FyeS4gQ29udmVyZ2VuY2UgaXMgc2xvd2VyIHRoYW4gTlIgKG9yZGVyIGlzIGJldHdlZW4gJDEkIGFuZCAkMiQpLCBob3dldmVyIEJGR1MgbWF5IGJlIGZhc3RlciBpbiB0ZXJtcyBvZiBvdmVyYWxsIHRpbWUsIGRlcGVuZGluZyBvbiBob3cgZXhwZW5zaXZlIGl0IGlzIHRvIGNhbGN1bGF0ZSB0aGUgSGVzc2lhbiBpbiBOUi4NCg0KTGFzdGx5LCB5b3UgbWF5IG1vcmUgY29tbW9ubHkgc2VlICoqTOKAkUJGR1PigJFCKiogaW4gYXBwbGljYXRpb25zIGluc3RlYWQgb2YgQkZHUywgd2hpY2ggaXMgYSB2YXJpYW50IG9mIEJGR1MgdGhhdCAgDQoNCjEuIGFwcHJveGltYXRlcyB0aGUgSGVzc2lhbiB1cGRhdGUgdXNpbmcgYSBsaW1pdGVkIGFtb3VudCBvZiBjb21wdXRlciBtZW1vcnksIGFuZCAgDQoNCjIuIGNhbiBwbGFjZSAqKmJveCBjb25zdHJhaW50cyoqIChib3VuZHMpIG9uIHRoZSBwYXJhbWV0ZXJzIGJlaW5nIGVzdGltYXRlZC4NCg0KVGhlIGxhdHRlciBpcyBoZWxwZnVsIGluIGNhc2VzIHdoZXJlIHlvdSB3YW50IHRvIHByZXZlbnQgdGhlIGFsZ29yaXRobSBmcm9tIHByb3Bvc2luZyB1cGRhdGVzIHRvIHRoZSBwYXJhbWV0ZXJzIHRoYXQgYXJlIG91dCBvZiB0aGUgcmFuZ2Ugb2YgdGhlIHN1cHBvcnQgZm9yIHRoYXQgcGFyYW1ldGVyIChmb3IgZXhhbXBsZSwgdmFsdWVzIGxlc3MgdGhhbiBvciBlcXVhbCB0byB6ZXJvIGZvciAkXGxhbWJkYSQgZnJvbSBvdXIgc2ltcGxlIFBvaXNzb24gZXhhbXBsZSkuDQoNCg0KIyMjIFBlcmZvcm1hbmNlIGFuZCBzdGFiaWxpdHkgaW1wcm92ZW1lbnQNCg0KUGVyZm9ybWFuY2Ugb2YgQkZHUyBpcyBzZW5zaXRpdmUgdG8gdGhlIHN0YXJ0aW5nIHZhbHVlIG9mICRcbWF0aGJme019XnsoMCl9JC4gIA0KLSBGb3IgbWF4aW11bSBsaWtlbGlob29kIGVzdGltYXRpb24sIGl0IGlzIGdvb2QgdG8gc2V0ICRcbWF0aGJme019XnsoMCl9ID0gLUlcIVxiaWdsKFxib2xkc3ltYm9se1x0aGV0YX1eeygwKX1cYmlncikkLCB0aGUgbmVnYXRpdmUgZXhwZWN0ZWQgRmlzaGVyIEluZm9ybWF0aW9uIE1hdHJpeC4gIA0KLSBGb3Igb3RoZXIgcHJvYmxlbXMsIHNldHRpbmcgJFxtYXRoYmZ7TX1eeygwKX0gPSAtXG1hdGhiZntJfV97cCBcdGltZXMgcH0kICh0aGUgbmVnYXRpdmUgaWRlbnRpdHkgbWF0cml4KSBtYXkgd29yayB3ZWxsIGlmIGFsbCBwYXJhbWV0ZXJzIGFyZSBvbiBzaW1pbGFyIHNjYWxlcy4NCg0KKipSZXNjYWxpbmcgcGFyYW1ldGVycyoqIGlzIGdlbmVyYWxseSBoZWxwZnVsIGlmIHBhcmFtZXRlcnMgYXJlIG9uIHZlcnkgZGlmZmVyZW50IHNjYWxlcyDigJMgZm9yIGV4YW1wbGUsIGlmIHlvdSBoYXZlIGEgZnVuY3Rpb24gIA0KDQokJA0KZihcdGhldGFfMSxcdGhldGFfMikgPSBcZXhwKDEgKyBcdGhldGFfMSkgKyBcdGhldGFfMiAuDQokJA0KDQpDbGVhcmx5LCBzaW1pbGFyIGNoYW5nZXMgaW4gJFx0aGV0YV8xJCBhbmQgJFx0aGV0YV8yJCB3aWxsIGhhdmUgdmVyeSBkaWZmZXJlbnQgaW1wYWN0cyBvbiAkZihcdGhldGFfMSxcdGhldGFfMikkLiBSZXNjYWxpbmcgY2FuIGFsc28gcHJldmVudCB0aGUgc3RvcHBpbmcgY3JpdGVyaW9uIGZyb20gYmVpbmcgZG9taW5hdGVkIGJ5IHRoZSB2YXJpYWJsZXMgdGhhdCBoYXZlIHRoZSBsYXJnZXN0IHVuaXRzIG9yIHRoZSBtb3N0IGluZmx1ZW5jZSBvbiB0aGUgZnVuY3Rpb24gdG8gYmUgbWF4aW1pemVkLg0KDQpJbiBzb21lIGNhc2VzIGl0IG1heSBiZSBlYXNpZXIgdG8gd29yayB3aXRoIHRoZSAqKmxvZyoqIG9yICoqZXhwKiogb2YgYSBwYXJhbWV0ZXIgcmF0aGVyIHRoYW4gdGhlIG9yaWdpbmFsIHNjYWxlIG9mIHRoZSBwYXJhbWV0ZXIgaXRzZWxmIChyZXBhcmFtZXRlcml6YXRpb24pLiBTb21lIHRyYW5zZm9ybWF0aW9ucyBhbHNvIGltcG9zZSBhIG5hdHVyYWwgbG93ZXIgYm91bmQgb24gdGhlIHBhcmFtZXRlciBzcGFjZSwgcmVkdWNpbmcgdGhlIG5lZWQgZm9yIGJveCBjb25zdHJhaW50cy4gV2Ugd2lsbCBnbyBpbnRvIG1vcmUgZGV0YWlsIG9uIGhvdyB0byBkbyB0aGlzIGluIHRoZSBzZWN0aW9uIG9uIGBvcHRpbXhgIGluIFIuIE1vcmUgaW5mb3JtYXRpb24gaXMgZ2l2ZW4gaW4gQ2hhcHRlciAxNiBvZiB0aGUgTmFzaCBib29rLg0KDQoNCiMjIEdyYWRpZW50IERlc2NlbnQNCg0KRGVwZW5kaW5nIG9uIHdobyB5b3UgdGFsayB0bywgKipncmFkaWVudCBkZXNjZW50KiogbWF5IGFsc28gYmUgcmVmZXJyZWQgdG8gYXMgKirigJxzdGVlcGVzdCBkZXNjZW504oCdKiogKGFzIGRlZmluZWQgaW4gR0gpLiBMaWtld2lzZSwgaXQgbWF5IGJlIGNhbGxlZCAqKmdyYWRpZW50IGFzY2VudCoqIG9yICoq4oCcc3RlZXBlc3QgYXNjZW504oCdKiosIGRlcGVuZGluZyBvbiB3aGV0aGVyIHRoZSBvYmplY3RpdmUgZnVuY3Rpb24gaXMgYmVpbmcgbWF4aW1pemVkIHJhdGhlciB0aGFuIG1pbmltaXplZC4gSW4gb3RoZXIgY2lyY2xlcywgaXQgbWF5IHJlZmVyIHRvIGEgY2xhc3Mgb2YgbWV0aG9kcyB0aGF0IGluY2x1ZGUgc3RlZXBlc3QgZGVzY2VudC9hc2NlbnQgYXMgYSBzcGVjaWFsIGNhc2UuDQoNClNpbXBseSBwdXQsIHN0ZWVwZXN0IGFzY2VudCByZXBsYWNlcyAkXG1hdGhiZntNfSQgaW4gdGhlIHF1YXNp4oCRTmV3dG9uIHVwZGF0ZSB3aXRoIHRoZSBpZGVudGl0eSBtYXRyaXggJFxtYXRoYmZ7SX0kIChvciAkLVxtYXRoYmZ7SX0kIGZvciBkZXNjZW50KSwgYW5kIGRvZXMgYXdheSB3aXRoIHVzaW5nIHRoZSBIZXNzaWFuIG9yIGFueSBhcHByb3hpbWF0aW9uIG9mIGl0IGNvbXBsZXRlbHkuIEluIHRoaXMgbWFubmVyIHdlIGNvbXBsZXRlbHkgYXZvaWQgYW55IGNvbXB1dGF0aW9uYWwgZXhwZW5zZSBvZiBjb21wdXRpbmcvYXBwcm94aW1hdGluZy9zdG9yaW5nL2ludmVydGluZyB0aGUgSGVzc2lhbiwgd2hpY2ggY2FuIGJlY29tZSBwcm9oaWJpdGl2ZSBpbiBoaWdoIGRpbWVuc2lvbnMuDQoNClRoYXQgaXMsIHRoZSB1cGRhdGUgaW4gdGhpcyBzZXR0aW5nIGlzIHNpbXBseSAgDQoNCiQkDQpcbWF0aGJme3h9XnsodCsxKX0gPSBcbWF0aGJme3h9XnsodCl9IC0gXGFscGhhXnsodCl9IGYnXCFcYmlnbChcbWF0aGJme3h9XnsodCl9XGJpZ3IpLA0KJCQNCg0Kd2hlcmUgJGYoXG1hdGhiZnt4fSkkIGlzIHRoZSBmdW5jdGlvbiB0byBiZSAqKm1pbmltaXplZCoqLiBJbiB0aGlzIG1hbm5lciwgdGhlIHVwZGF0ZSB0YWtlcyB0aGUgZGlyZWN0aW9uIG9mIHN0ZWVwZXN0IGRlc2NlbnQgYXQgJFxtYXRoYmZ7eH1eeyh0KX0kLiBUaGUgc3RlcCBzaXplIG1heSBiZSBzY2FsZWQgYnkgc29tZSB2YWx1ZSAkXGFscGhhXnsodCl9ID4gMCQ7IGlmIHRoZSBkaXJlY3Rpb24gdGFrZW4gYXQgJFxtYXRoYmZ7eH1eeyh0KX0kIGlzIG5lZ2F0aXZlLCAkXGFscGhhXnsodCl9JCBtYXkgYmUgaGFsdmVkIHJlcGVhdGVkbHkgdG8gZGVjcmVhc2UgdGhlIHN0ZXAgc2l6ZSB1bnRpbCBhIHN1ZmZpY2llbnRseSBzbWFsbCBzdGVwIHNpemUgZmluZHMgYSBkb3duaGlsbCByb3V0ZSAoJFxhbHBoYV57KHQpfT0xJCBpbml0aWFsbHkpLCB3aGVyZSDigJxkb3duaGlsbOKAnSBpcyBkZXRlcm1pbmVkIGJ5IHRoZSBjaGFuZ2UgaW4gdGhlIG9iamVjdGl2ZSBmdW5jdGlvbiBmcm9tIHRoZSBvcmlnaW5hbCBwb3NpdGlvbi4gVGhpcyBhcHByb2FjaCBpcyBjYWxsZWQgKipiYWNrdHJhY2tpbmcqKi4gQSBtb3JlIGdlbmVyYWwgdmVyc2lvbiBvZiB0aGlzIGFwcHJvYWNoIGFyZSAqKmxpbmUgc2VhcmNoKiogbWV0aG9kcywgd2hlcmUgc3VjaCBtZXRob2RzIGVuc3VyZSB0aGUgY2hhbmdlIGluIHRoZSBvYmplY3RpdmUgZnVuY3Rpb24gaXMgZ3JlYXRlciB0aGFuIGEgY2VydGFpbiBhbW91bnQgYW5kIG90aGVyIGhlbHBmdWwgcXVhbGl0aWVzIChkZXRhaWxlZCBpbiBHSCAyLjIuMi4xKS4gIA0KDQpGb3IgKiptYXhpbWl6YXRpb24qKiwgZmxpcCB0aGUgc2lnbiB0byAkK1xhbHBoYV57KHQpfSBmJ1whXGJpZ2woXG1hdGhiZnt4fV57KHQpfVxiaWdyKSQgaW4gdGhlIGFib3ZlIGV4cHJlc3Npb24uDQoNClRoZSBpbnR1aXRpb24gYmVoaW5kIHRoaXMgYXBwcm9hY2ggbWF5IGJlIGRlc2NyaWJlZCBhcyB0cnlpbmcgdG8gZmluZCBvbmXigJlzIHdheSBkb3duIGEgaGlsbCB3aGVuIGJsaW5kZm9sZGVkLiBUaGUgb25seSBpbmZvcm1hdGlvbiBhdmFpbGFibGUgaXMgaG93IHN0ZWVwIHRoZSBoaWxsIGlzIGF0IHRoZSBwb2ludCB3aGVyZSB5b3UgYXJlIHN0YW5kaW5nLCBhbmQgeW91IG1vdmUgaW4gdGhlIGRpcmVjdGlvbiB3aGVyZSB0aGUgc2xvcGUgYXBwZWFycyB0aGUgc3RlZXBlc3QuIFdlIGRvIG5vdCBoYXZlIGFuIGlkZWEgb2YgdGhlIGN1cnZhdHVyZSAoY2hhbmdlIGluIHRoZSBzbG9wZSkgYXJvdW5kIHRoYXQgcG9pbnQsIHNvIHdlIGp1c3QgZm9sbG93IHRoZSBzbG9wZSBpbnN0ZWFkLg0KDQoqKkdyYWRpZW50IERlc2NlbnQqKiwgZGVwZW5kaW5nIG9uIHRoZSBjb250ZXh0LCBtYXkgYmUgdGhvdWdodCBvZiBhcyBhIG1vcmUgZ2VuZXJhbCBmb3JtIG9mIHN0ZWVwZXN0IGRlc2NlbnQgdGhhdCBpbnN0ZWFkIGZpeGVzICRcYWxwaGFeeyh0KX0kIChzb21ldGltZXMgY2FsbGVkIHRoZSAqKuKAnGxlYXJuaW5nIHJhdGXigJ0qKikgcmF0aGVyIHRoYW4gbGV0dGluZyBpdCB2YXJ5IHdpdGhpbiBlYWNoIGl0ZXJhdGlvbi4gSW4gb3RoZXIgY2FzZXMsIGl0IGlzIHJlZmVycmVkIHRvIGFzIGEgY2xhc3Mgb2YgbWV0aG9kcyB3aGVyZSBib3RoICRcYWxwaGFeeyh0KX0kIGFuZCB0aGUgZ3JhZGllbnQgbWF5IGJlIHNjYWxlZCB3aXRoIGVhY2ggaXRlcmF0aW9uLg0KDQpSZWdhcmRsZXNzLCB0aGUgaWRlYSBpcyBzaW1pbGFyIGluIHRoYXQgaXQgYXZvaWRzIGNvbXB1dGluZyB0aGUgSGVzc2lhbiBhbmQgdXNlcyB0aGUgZ3JhZGllbnQgdG8gY2hvb3NlIHRoZSBkaXJlY3Rpb24gZm9yIHRoZSBuZXh0IHVwZGF0ZS4gRm9yIHRoZSBzYW1lIHJlYXNvbnMsIGdyYWRpZW50IGRlc2NlbnQgaXMgdmVyeSBwb3B1bGFyIGZvciBtYWNoaW5lIGxlYXJuaW5nIG1ldGhvZHMsIHBhcnRpY3VsYXJseSBmb3IgaGlnaOKAkWRpbWVuc2lvbmFsIHByb2JsZW1zIChubyBuZWVkIHRvIHN0b3JlIG9yIGFwcHJveGltYXRlIGEgJHAgXHRpbWVzIHAkIEhlc3NpYW4pLg0KDQpIb3dldmVyLCBmb3IgdmVyeSBsYXJnZeKAkXNhbXBsZSBwcm9ibGVtcyB0aGUgY29tcHV0YXRpb24gbWF5IGJlY29tZSBwcm9oaWJpdGl2ZS4gU3RhbmRhcmQgZ3JhZGllbnQgZGVzY2VudCBkZXNjcmliZWQgYWJvdmUgY2FuIGJlIGNhbGxlZCAqKmJhdGNoIGdyYWRpZW50IGRlc2NlbnQqKiwgd2hlcmUgYWxsIGRhdGEgcG9pbnRzIGFyZSB1dGlsaXplZCBmb3IgY29tcHV0aW5nIHRoZSBuZXh0IHNlYXJjaCBkaXJlY3Rpb24uIEZvciBleGFtcGxlLCBpbiB0aGUgUG9pc3NvbiByZWdyZXNzaW9uIG1vZGVsIHdlIHdpc2ggdG8gZXN0aW1hdGUgJFxib2xkc3ltYm9se1xiZXRhfSQ6DQoNCiQkDQpcYm9sZHN5bWJvbHtcYmV0YX1eeyh0KzEpfSA9IFxib2xkc3ltYm9se1xiZXRhfV57KHQpfSArIFxhbHBoYV57KHQpfSBcLCBsJ1whXGJpZ2woXGJvbGRzeW1ib2x7XGJldGF9XnsodCl9XGJpZ3IpLg0KJCQNCg0KT25lIHRoaW5nIHRoYXQgaXMgY2xlYXIgaXMgdGhhdCAgDQoNCiQkDQpsJyhcYm9sZHN5bWJvbHtcYmV0YX0pID0gXHN1bV97aT0xfV5uIFxiaWdsKCB5X2kgLSBcZXhwKFxtYXRoYmZ7WH1faVxib2xkc3ltYm9se1xiZXRhfSkgXGJpZ3IpIFxtYXRoYmZ7WH1faV5ULA0KJCQNCg0Kd2hpY2ggaXMgY29tcHV0ZWQgZnJvbSAkbiQgb2JzZXJ2YXRpb25zLiBXaGVuICRuJCBpcyB2ZXJ5IGxhcmdlLCB0aGVzZSB1cGRhdGVzIGNhbiBiZWNvbWUgY29tcHV0YXRpb25hbGx5IHByb2hpYml0aXZlLg0KDQpBbiBpbXBvcnRhbnQgbm90ZSBpcyB0aGF0IHRoZSBpbnB1dCBmZWF0dXJlcyBhcmUgb2Z0ZW4gKipzY2FsZWQqKiBpbiBHRC9TR0QgdG8gcHV0IG1vZGVsIHBhcmFtZXRlcnMgb24gc2ltaWxhciByYW5nZXMsIGJlY2F1c2UgdGhlcmUgaXMgbm8gaW52ZXJzZSBIZXNzaWFuIChvciBpdHMgYXBwcm94aW1hdGlvbikgdG8gY2FuY2VsIG91dCB2YXJpYXRpb25zIGluIHNjYWxlIHNlZW4gaW4gdGhlIGdyYWRpZW50IGZ1bmN0aW9uIGR1ZSB0byBkaWZmZXJlbmNlcyBpbiBzY2FsZSBpbiB0aGUgb3JpZ2luYWwgcGFyYW1ldGVycy4gSW5zdGVhZCwgdGhlIGdyYWRpZW50IGlzIG11bHRpcGxpZWQgYnkgYSBjb25zdGFudCAodGhlIGxlYXJuaW5nIHJhdGUpLCB3aGljaCBpcyB0aGUgc2FtZSBmb3IgYWxsIGVsZW1lbnRzIG9mIHRoZSBncmFkaWVudC4gVGhpcyBpcyBzb21ld2hhdCBzaW1pbGFyIHRvIHdoeSBjb3ZhcmlhdGVzIGFyZSBvZnRlbiBzdGFuZGFyZGl6ZWQgcHJpb3IgdG8gdGhlIGFwcGxpY2F0aW9uIG9mIHBlbmFsaXplZCBsaWtlbGlob29kIGFwcHJvYWNoZXMg4oCTIHRoYXQgd2F5IHRoZSBzYW1lIHBlbmFsdHkgdmFsdWUgd2lsbCBhcHByb3hpbWF0ZWx5IG1lYW4gdGhlIHNhbWUgZm9yIGRpZmZlcmVudCBjb2VmZmljaWVudHMgaW4gdGhlIG1vZGVsLg0KDQoNCiMjIFN0b2NoYXN0aWMgR3JhZGllbnQgRGVzY2VudCAoU0dEKQ0KDQpTdG9jaGFzdGljIGdyYWRpZW50IGRlc2NlbnQgaW5zdGVhZCByYW5kb21seSBzdWJzYW1wbGVzIHRoZSBkYXRhIGFuZCBwZXJmb3JtcyBhbiB1cGRhdGUgZm9yICRcYm9sZHN5bWJvbHtcYmV0YX0kIHdpdGggYSByYW5kb20gb2JzZXJ2YXRpb24gZHJhd24gZnJvbSB0aGUgZGF0YS4gUmF0aGVyIHRoYW4gdXBkYXRpbmcgJFxib2xkc3ltYm9se1xiZXRhfSQgd2l0aCBhIGZ1bGwgcGFzcyBvdmVyIHRoZSBkYXRhLCB3ZSBpbnN0ZWFkIHVwZGF0ZSAkXGJvbGRzeW1ib2x7XGJldGF9JCB3aXRoIGEgc2luZ2xlIGRyYXcgZnJvbSB0aGUgZGF0YSwgc3VjaCB0aGF0IG5vdyAgDQoNCiQkDQpcYm9sZHN5bWJvbHtcYmV0YX1eeyhzKzEpfSA9IFxib2xkc3ltYm9se1xiZXRhfV57KHMpfSAtIFxhbHBoYSBcLCBsJ197aShzKX1cIVxiaWdsKFxib2xkc3ltYm9se1xiZXRhfV57KHMpfVxiaWdyKSwNCiQkDQoNCndoZXJlICRzID4gMCQgaXMgdGhlICoqc3Vic2FtcGxlIGluZGV4KiosICRpKHMpJCBpcyB0aGUgaW5kZXggb2YgdGhlIHNhbXBsZSB0aGF0IGlzIHJhbmRvbWx5IGRyYXduIGluIHRoZSAkcyQtdGggc3Vic2FtcGxlLCBhbmQgJGwnX3tpKHMpfVwhXGJpZ2woXGJvbGRzeW1ib2x7XGJldGF9Xnsocyl9XGJpZ3IpJCBpcyB0aGUgdmFsdWUgb2YgdGhlIGdyYWRpZW50IGNvbXB1dGVkIGZyb20gdGhlICRpJC10aCBzdWJqZWN0LiAgDQoNCk9uZSBjb250aW51ZXMgdG8gZHJhdyBzYW1wbGVzIHVudGlsIGNvbnZlcmdlbmNlIGlzIG9ic2VydmVkIGluICRcYm9sZHN5bWJvbHtcYmV0YX1eeyhzKzEpfSQsIHdoaWNoIG1heSB0YWtlIG11bHRpcGxlIGxvb3BzIG92ZXIgdGhlIGVudGlyZSBkYXRhIHNldC4gT3RoZXIgdmFyaWFudHMgaW5jbHVkZSBpbuKAkW9yZGVyIGxvb3BpbmcgYWNyb3NzIG9ic2VydmF0aW9ucyByYXRoZXIgdGhhbiByYW5kb20gc2FtcGxpbmcuDQoNCk1vZGlmaWNhdGlvbnMgb2YgdGhpcyBhcHByb2FjaCBpbmNsdWRlICoqbWluaeKAkWJhdGNoaW5nKiosIHdoZXJlIGluc3RlYWQgb2YgYSBzaW5nbGUgc2FtcGxlIGRyYXduIGZyb20gdGhlIGRhdGFzZXQsIGEgZ3JvdXAgb2YgJG0kIHNhbXBsZXMgaXMgZHJhd24uIFRoaXMgcmVkdWNlcyB2YXJpYWJpbGl0eSBiZXR3ZWVuIGl0ZXJhdGlvbnMgYW5kIGFzc2lzdHMgd2l0aCBjb252ZXJnZW5jZS4gV2hpbGUgdGhlIHRoZW9yZXRpY2FsIGNvbnZlcmdlbmNlIHJhdGUgb2YgU0dEIGlzIGxvd2VyIHRoYW4gdGhhdCBvZiBHRCAoU0dEIGlzIHNhaWQgdG8gYmUgKipzdWLigJFsaW5lYXIqKiksIGluIHByYWN0aWNlIGZvciBsYXJnZeKAkSRuJCBwcm9ibGVtcyBpdCBtYXkgYmUgbXVjaCBmYXN0ZXIsIGFzIHRoZSBwZXLigJFpdGVyYXRpb24gY29zdCBmb3IgU0dEIGlzIG11Y2ggbG93ZXIgdGhhbiBHRC4gSW4gYW55IGNhc2UsIFNHRCBpcyBoZWxwZnVsIGluIHRob3NlIHNpdHVhdGlvbnMgd2hlbiBvbmUgY2Fubm90IHJlYWQgYWxsIG9mIHRoZSBkYXRhIGludG8gbWVtb3J5Lg0KDQpUaGlzIGlzIGFsc28gd2h5IEdEIChhcyBkZWZpbmVkIGluIHRoZSBwcmV2aW91cyBzZWN0aW9uKSBpcyBzb21ldGltZXMgY2FsbGVkICoqQmF0Y2ggR3JhZGllbnQgRGVzY2VudCoqLCBhcyBpdCByZXF1aXJlcyBsb29waW5nIG92ZXIgdGhlIGVudGlyZSBkYXRhc2V0IGZvciBlYWNoIHVwZGF0ZSByYXRoZXIgdGhhbiBhIHN1YnNhbXBsZSAobWluaeKAkWJhdGNoKS4NCg0KR2l2ZW4gdGhlIHZhcmlhYmlsaXR5IGZyb20gc2FtcGxlIHRvIHNhbXBsZSwgJFxhbHBoYV57KHQpfSQgaXMgb2Z0ZW4gaGVsZCAqKmZpeGVkKiogcmF0aGVyIHRoYW4gdmFyaWVkOyBob3dldmVyLCB0aGUgY29udmVyZ2VuY2UgYW5kIHNwZWVkIGRlcGVuZCBoZWF2aWx5IG9uIHRoZSBvcHRpbWFsIHNlbGVjdGlvbiBvZiB0aGlzIGZpeGVkIHZhbHVlLiBJbiBkZWVwIGxlYXJuaW5nIGFuZCBvdGhlciBtZXRob2RzLCB0aGlzIHZhbHVlIGlzIG9mdGVuIGRldGVybWluZWQgdGhyb3VnaCAqKmh5cGVycGFyYW1ldGVyIHR1bmluZyoqICh0dW5pbmcgcGFyYW1ldGVyIHNlbGVjdGlvbikuIFRvbyBsYXJnZSBhIGxlYXJuaW5nIHJhdGUgbWF5IGxlYWQgdG8gcHJvYmxlbXMgd2l0aCBjb252ZXJnZW5jZSwgYW5kIHRvbyBzbWFsbCBtYXkgbGVhZCB0byBzbG93IGNvbnZlcmdlbmNlLiAgDQoNCk90aGVyIGFwcHJvYWNoZXMgbWF5IGF1dG9tYXRpY2FsbHkgYWRqdXN0ICRcYWxwaGFeeyh0KX0kIGFzIHRoZSBhbGdvcml0aG0gcnVucywgZGVjcmVhc2luZyAkXGFscGhhXnsodCl9JCBieSBzb21lIHByZXNldCBzY2hlZHVsZSAoc29tZXRpbWVzIGNhbGxlZCBhICoq4oCcbGVhcm5pbmcgcmF0ZSBzY2hlZHVsZeKAnSoqKS4gVGhpcyBhcHByb2FjaCBtYXkgYWRkcmVzcyBzb21lIG9mIHRoZSBzaG9ydGNvbWluZ3Mgb2YgZml4aW5nICRcYWxwaGFeeyh0KX0kLCBidXQgaXQgaXMgaGlnaGx5IGRlcGVuZGVudCBvbiB0aGUgcGFyYW1ldGVycyB0aGF0IGRldGVybWluZSB0aGUgcmF0ZSBvZiBkZWNyZWFzZSwgYXMgdGhleSBhcmUgbm90IGRhdGHigJFhZGFwdGl2ZS4gIA0KDQpUbyBhZGRyZXNzIHRoZXNlIGlzc3VlcyBhbmQgb3RoZXJzLCBtb3JlIHNvcGhpc3RpY2F0ZWQgbWV0aG9kcyBmb3IgZGV0ZXJtaW5pbmcgJFxhbHBoYV57KHQpfSQgb3Igc2NhbGluZyB0aGUgZ3JhZGllbnQgZHVyaW5nIEdEL1NHRCBoYXZlIGJlZW4gcHJvcG9zZWQsIHN1Y2ggYXMgdGhlICoqQURBTSoqIGFuZCAqKkFEQUdSQUQqKiBvcHRpbWl6ZXJzLiBNb3JlIGRldGFpbCBvbiB0aGVzZSBhcHByb2FjaGVzIGNhbiBiZSBmb3VuZCBpbiB0aGUgcmVmZXJlbmNlcyAoaHR0cHM6Ly9hcnhpdi5vcmcvYWJzLzE2MDkuMDQ3NDcpLg0KDQojIyBOZWxkZXItTWVhZA0KDQpJbiBjZXJ0YWluIHNpdHVhdGlvbnMgd2UgbWF5IGZpbmQgdGhhdCBuZWl0aGVyIHRoZSBmaXJzdCBub3IgdGhlIHNlY29uZCBkZXJpdmF0aXZlIG1heSBiZSBlYXNpbHkgZGVyaXZlZCBvciBjb21wdXRhdGlvbmFsbHkgZWFzeSB0byBldmFsdWF0ZS4gKipOZWxkZXLigJNNZWFkKiosIHNvbWV0aW1lcyBjYWxsZWQgdGhlICoq4oCcU2ltcGxleCBTZWFyY2ggTWV0aG9k4oCdKiosIGlzIG9uZSBzdWNoIGFsZ29yaXRobSB0aGF0IG9ubHkgcmVxdWlyZXMgdGhlIHNwZWNpZmljYXRpb24gYW5kIGV2YWx1YXRpb24gb2YgdGhlIG9iamVjdGl2ZSBmdW5jdGlvbiAkZihcYm9sZHN5bWJvbHtcdGhldGF9KSQgaW4gb3JkZXIgdG8gcGVyZm9ybSBvcHRpbWl6YXRpb24uIEhlbmNlLCB0aGlzIGFsZ29yaXRobSBmYWxscyBpbnRvIHRoZSBjbGFzcyBvZiBpdGVyYXRpdmUgKipkaXJlY3Qgc2VhcmNoKiogYWxnb3JpdGhtcyBiZWNhdXNlIGl0IG9ubHkgZXZhbHVhdGVzIHRoZSBmdW5jdGlvbiBhdCBwb3NzaWJsZSBzb2x1dGlvbnMgdG8gc3VnZ2VzdCBhIGJldHRlciBwbGFjZSBmb3IgdGhlIGFsZ29yaXRobSB0byBtb3ZlIGluIHRoZSBwYXJhbWV0ZXIgc3BhY2UuDQoNCkluIHRoZSBtYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdGlvbiBjb250ZXh0LCB0aGlzIG1lYW5zIHRoYXQgb25seSB0aGUgKipsaWtlbGlob29kIGZ1bmN0aW9uKiogaXMgZXZhbHVhdGVkIGR1cmluZyBvcHRpbWl6YXRpb24uIFRoaXMgbWV0aG9kIG1heSBhbHNvIGJlIGFwcGxpZWQgaW4gbW9yZSBnZW5lcmFsIHNpdHVhdGlvbnMgd2hlcmUgYW4gb2JqZWN0aXZlIGZ1bmN0aW9uIGV4aXN0cyB0aGF0IHlvdSB3aXNoIHRvIG1heGltaXplIG9yIG1pbmltaXplIGJ1dCB3aGVyZSBjbGVhciBhbmFseXRpY2FsIGRlcml2YXRpdmVzIGFyZSBub3QgYXZhaWxhYmxlLg0KDQpQcmlvciBkZXNjcmlwdGlvbnMgb2YgdGhpcyBhbGdvcml0aG0gZGVzY3JpYmUgaXQgYXMgKirigJxhbW9lYmEtbGlrZeKAnSoqLCB3aGVyZSB0aGUgYWxnb3JpdGhtIHNlcXVlbnRpYWxseSB1cGRhdGVzIHRoZSDigJxiZXN04oCdIHNldCBvZiBtb2RlbCBwYXJhbWV0ZXJzLCBjcmF3bGluZyB1cGhpbGwgb3IgZG93bmhpbGwgb24gdGhlIG9iamVjdGl2ZSBmdW5jdGlvbiBzdXJmYWNlIHVudGlsIGl0IHJlYWNoZXMgYW4gb3B0aW1hbCBzZXQgb2YgcGFyYW1ldGVycy4gVGhlIHNwZWNpZmljIGFsZ29yaXRobSBhbmQgaXRzIGRldmVsb3BtZW50IGFyZSBzb21ld2hhdCBvdXQgb2YgdGhlIHNjb3BlIG9mIHRoaXMgY291cnNlLCBidXQgd2Ugd2lsbCBjb3ZlciBpdCBicmllZmx5IGhlcmUuDQoNClRoZSBtYWluIGlkZWEgYmVoaW5kIHRoaXMgYXBwcm9hY2ggaXMgc2ltaWxhciB0byB0aGUgcHJpb3IgdHdvIGNsYXNzZXMgb2YgYWxnb3JpdGhtcyBjb3ZlcmVkIHNvIGZhcjogdGhlIGFsZ29yaXRobSBzdGFydHMgZnJvbSBhbiBpbml0aWFsIHNldCBvZiBndWVzc2VzIGZvciB0aGUgcGFyYW1ldGVyIGVzdGltYXRlcyBhbmQgdGhlbiBpdGVyYXRpdmVseSB1cGRhdGVzIHVudGlsIHNvbWUgY29udmVyZ2VuY2UgY3JpdGVyaW9uIGlzIG1ldC4NCg0KDQpUaGUgYmFzaWMgaWRlYSBiZWhpbmQgdGhpcyBhbGdvcml0aG0gaXMgYmFzZWQgdXBvbiB0aGUgZGVmaW5pdGlvbiBvZiB0aGUgKipzaW1wbGV4KiouIFRoZSBzaW1wbGV4IGNvbnRhaW5zICRwKzEkIHZlcnRpY2VzLCBlYWNoIGEgJHAkLWRpbWVuc2lvbmFsIHZlY3RvciByZXByZXNlbnRpbmcgYSBwYXJ0aWN1bGFyIGNvbWJpbmF0aW9uIG9mIHRoZSAkcCQgcGFyYW1ldGVyIHZhbHVlcy4gVGhlIG1ldGhvZCBiZWdpbnMgd2l0aCBhbiBpbml0aWFsIHNpbXBsZXggY29uc3RydWN0ZWQgZnJvbSBhIHN0YXJ0aW5nIHBvaW50ICRcYm9sZHN5bWJvbHtcdGhldGF9XnsoMCl9JCB0aHJvdWdoIGEgcHJlZGVmaW5lZCBpbml0aWFsaXphdGlvbiBydWxlLiBUaGVzZSB2ZXJ0aWNlcyBhcmUgdGhlbiAqKnJhbmtlZCBmcm9tIGJlc3QgdG8gd29yc3QqKiBpbiB0ZXJtcyBvZiB0aGVpciBmdW5jdGlvbiB2YWx1ZSwgYW5kIGluIGVhY2ggaXRlcmF0aW9uIHRoZSB3b3JzdC1yYW5rZWQgdmVydGV4IGlzIHVwZGF0ZWQuDQoNCkluIGdlbmVyYWwsIHRoZSB3b3JzdCB2ZXJ0ZXggaXMgbW92ZWQgdG93YXJkIHRoZSBkaXJlY3Rpb24gb2YgdGhlIGJldHRlciB2ZXJ0aWNlcyBpbiB0aGUgc2ltcGxleC4gSW4gdGhpcyBpdGVyYXRpdmUgZmFzaGlvbiwgdGhlIHZlcnRpY2VzIHNsb3dseSBtb3ZlIHRvd2FyZCByZWdpb25zIG9mIGhpZ2hlciAob3IgbG93ZXIpIG9iamVjdGl2ZeKAkWZ1bmN0aW9uIHZhbHVlIHVudGlsIG5vIGZ1cnRoZXIgdXBkYXRlIHlpZWxkcyBhbiBhcHByZWNpYWJsZSBpbXByb3ZlbWVudC4gVGhlIHNpbXBsZXggbWF5IGFsc28gKipzaHJpbmsqKiBpbiBzaXplIHRvIGJldHRlciBlbmNhcHN1bGF0ZSBwcm9taXNpbmcgcmVnaW9ucyB3aGVuIHByb3Bvc2VkIG1vdmVzIGRvIG5vdCByZXN1bHQgaW4gc2lnbmlmaWNhbnQgZ2FpbnMuDQoNCk92ZXJhbGwsIGF0IGVhY2ggaXRlcmF0aW9uIHRoZSBhbGdvcml0aG0gYXR0ZW1wdHMgdG8gaW1wcm92ZSB0aGUgd29yc3QgcG9pbnQgaW4gdGhlIHNpbXBsZXggKHdoaWNoIGNvcnJlc3BvbmRzIHRvIG9uZSBwYXJ0aWN1bGFyIHBhcmFtZXRlciB2ZWN0b3IgaW4gdGhlIG9yaWdpbmFsIG1vZGVsKS4NCg0KVGhlIGRldGFpbHMgb2YgdGhpcyB1cGRhdGluZyBwcm9jZWR1cmUgYXJlIGdpdmVuIGluIEdIIDIuMi40LiAoaHR0cHM6Ly9naXRodWIuY29tL09zY2FyUnVuc0NvZGUvQ29tcC1TdGF0L2Jsb2IvbWFpbi9Db21wdXRhdGlvbmFsJTIwU3RhdGlzdGljcyUyQyUyMFNlY29uZCUyMEVkaXRpb24tR2VvZiUyMEguJTIwR2l2ZW5zJTJDJTIwSmVubmlmZXIlMjBBLiUyMEhvZXRpbmcucGRmKSBXZSBpbGx1c3RyYXRlIHRoZSBwcm9jZXNzIGdyYXBoaWNhbGx5IGluIHRoZSB2aWRlbyBiZWxvdywgd2hpY2ggc2hvd3MgdGhlIG9wdGltaXphdGlvbiBvZiBhIHR3b+KAkXBhcmFtZXRlciBtb2RlbC4NCg0KIyBUd28gS2V5IFJlZmVyZW5jZXMNCg0KVHdvIG1ham9yIHJlZmVyZW5jZXMgY292ZXIgdGhlIHRvcGljcyBpbiB0aGlzIG5vdGU6DQoNCjEuIEdpdmVucywgRy4gSC4sICYgSG9ldGluZywgSi4gQS4gKDIwMTIpLiBDb21wdXRhdGlvbmFsIHN0YXRpc3RpY3MuIEpvaG4gV2lsZXkgJiBTb25zLiAoaHR0cHM6Ly9naXRodWIuY29tL09zY2FyUnVuc0NvZGUvQ29tcC1TdGF0L2Jsb2IvbWFpbi9Db21wdXRhdGlvbmFsJTIwU3RhdGlzdGljcyUyQyUyMFNlY29uZCUyMEVkaXRpb24tR2VvZiUyMEguJTIwR2l2ZW5zJTJDJTIwSmVubmlmZXIlMjBBLiUyMEhvZXRpbmcucGRmKQ0KDQoyLiBOYXNoLCBKLiBDLiAoMjAxNCkuIE5vbmxpbmVhciBwYXJhbWV0ZXIgb3B0aW1pemF0aW9uIHVzaW5nIFIgdG9vbHMuIEpvaG4gV2lsZXkgJiBTb25zLiAoQWNjZXNzaWJsZSB0aHJvdWdoIFdDVSdzIGxpYnJhcnk6IGh0dHBzOi8va2xucGEtd2N1LnByaW1vLmV4bGlicmlzZ3JvdXAuY29tL2Rpc2NvdmVyeS9mdWxsZGlzcGxheT9kb2NpZD1hbG1hOTkxODg0NjQwNzUwMzU3MyZjb250ZXh0PUwmdmlkPTAxU1NIRUxDT19XQ0hFU1RFUjpWRSZsYW5nPWVuJnNlYXJjaF9zY29wZT1NeUluc3RfYW5kX0NJJmFkYXB0b3I9TG9jYWwlMjBTZWFyY2glMjBFbmdpbmUmdGFiPUV2ZXJ5dGhpbmcmcXVlcnk9YW55LGNvbnRhaW5zLE5vbmxpbmVhciUyMFBhcmFtZXRlciUyME9wdGltaXphdGlvbiUyMFVzaW5nJTIwUiUyMFRvb2xzJTIwKEpvaG4lMjBOYXNoKSkNCg0K