Likeliood Function
Optimization
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\).
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.
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
[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
(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.
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?
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}\)?
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.
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.
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\).
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
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.
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.
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^*\).
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)}.
\]
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:
\(f''(\theta) \neq 0\)
on \([a,b]\)
\(f'''(\theta)\)
does not change sign on \([a,b]\)
\(f'(a) f'(b) < 0\),
and
\(|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.
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.
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

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
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
Cons
- Slower convergence relative to NR (order ~1.618 vs. quadratic)
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
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.
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.
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.
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.
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
evaluating the Hessian is too computationally expensive,
or
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.
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
approximates the Hessian update using a limited amount of
computer memory, and
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).
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.
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).
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