1 Introduction

In statistical inference, we often want to estimate unknown parameters of a probability distribution based on observed data. The likelihood function and maximum likelihood estimation (MLE) are fundamental concepts that provide a powerful framework for parameter estimation.

2 Likelihood Function

Given a set of independent and identically distributed (i.i.d.) observations \(\mathbf{x} = (x_1, x_2, \dots, x_n)\) from a probability distribution with probability density function (PDF) or probability mass function (PMF) \(f(x|\theta)\), where \(\theta\) is an unknown parameter, the likelihood function is defined as:

\[ L(\theta \mid x) = \prod_{i=1}^{n} f(x_i \mid \theta) \]

The likelihood function measures how “likely” the parameter \(\theta\) is, given the observed data \(\mathbf{x}\). In other words, likelihood function is

  • not a probability about the data: We already have the data; it’s fixed.

  • a function of the parameters: We use the data to make inferences about the unknown parameters.

The likelihood function looks identical to the product of the probability mass or density function evaluated at each individual data point, but the interpretation is different. The function is the same, but what is varying and what is fixed is reversed.

  • In \(\prod_{i=1}^n f(x | θ)\): θ is fixed, x varies. (Answer: “What is the probability of seeing different data points given a known parameter?”)

  • In \(L(θ | x)\): x is fixed, θ varies. (Answer: “How likely are different parameter values given my fixed, observed data?”)

For computational convenience, we often work with the log-likelihood function:

\[ \ell(\theta \mid x) = \log L(\theta \mid x) = \sum_{i=1}^{n} \log f(x_i \mid \theta) \]

The logarithm transforms the product into a sum, which is easier to differentiate and optimize.

3 Maximum Likelihood Estimation

The MLE method is incredibly intuitive: Find the parameter value that makes the observed data most probable. The idea originates from Ronald A. Fisher in a series of papers starting in the 1920s. The key paper is:

Fisher, R.A. (1922). On the mathematical foundations of theoretical statistics. Philosophical Transactions of the Royal Society of London, Series A, 222, 309–368.

In this paper, Fisher

  • Introduced the term likelihood (distinct from probability).

  • Proposed maximum likelihood as a general method for estimating parameters.

  • Explained that the most “reasonable” estimate of a parameter is the one that maximizes the likelihood of the observed data.

3.1 Likelihood Principle

All the evidence from an experiment about a model parameter \(\theta\) is contained in the likelihood function. Furthermore, two likelihood functions that are proportional to each other (i.e., they differ only by a constant multiplicative factor) contain the same information about \(\theta\).

This has two crucial implications:

  • The only thing that matters is the data you actually observed. The inference should not depend on data you might have observed but didn’t (i.e., the sample space). Procedures like p-values, which are based on the sample space, violate the Likelihood Principle.

  • The scaling of the likelihood function is irrelevant. Since a constant multiplier doesn’t change the location of the maximum or the ratios between different likelihoods, it doesn’t affect inference.

We will see that MLE is a Direct Application of the Likelihood Principle!

Interestingly, Fisher introduced the likelihood function (measuring how well parameter values examplin observed data) and maximum likelihood estimation (MLE, estimating parameters by maximizing likelihood - i.e., making the observed most probable) in 1922. However, the likelihood principle was formalized by Birnbaum 1962.

3.2 Finding the MLE

The maximum likelihood estimate (MLE) \(\hat{\theta}_{MLE}\) is the value of \(\theta\) that maximizes the likelihood function:

\[ \hat{\theta}_{\text{MLE}} = \arg\max_{\theta} L(\theta \mid x) = \arg \max_{\theta} \ell(\theta \mid x) \]

To find the MLE, we typically:

  • Write the likelihood function \(L(\theta|\mathbf{x})\) - a function of parameter(s) \(\theta\) since \(\mathbf{x}\) is observed.

  • Take the natural logarithm to get \(\ell(\theta|\mathbf{x})\) - converting multiplication to addition that make algebra easier.

  • Differentiate with respect to \(\theta\) - When differentiating with respect to parameters in a multiparameter setting, be sure to take partial derivatives separately for each of the two parameters. These derivatives are also called gradients and score functions.

  • Set the derivative equal to zero - also called score equation.

  • Solve for \(\theta\) - This typically requires numerical procedures to approximate the solution.

The equation \(\frac{\partial \ell(\theta|\mathbf{x})}{\partial \theta} = 0\) is called the likelihood equation.

4 Finding MLE with Examples

We will use several examples to demonstrate the mathematical derivations of MLEs. Similar to the method of moment estimates, not all MLE have closed expressions. In many cases, numerical algorithms are required to find the approximation of the MLE of the unknown parameters.

4.1 MLE of Normal Mean and Variance

Let \(X_1, X_2, \dots, X_n \sim N(\mu, \sigma^2)\). Both \(\mu\) and \(\sigma^2\) are unknown. We will derive the MLE of \(\mu\) and \(\sigma^2\). We will divide the process into the following steps:

Step 1: Likelihood Function: we first write the likelihood function of parameter based on the observed iid data.

\[ L(\mu \mid x) = \prod_{i=1}^{n} \frac{1}{\sqrt{2\pi\sigma^2}} \exp\!\left(-\frac{(x_i - \mu)^2}{2\sigma^2}\right) \]

Step 2: Log-Likelihood Function

\[ \ell(\mu \mid x) = -\frac{n}{2}\log(2\pi\sigma^2) - \frac{1}{2\sigma^2}\sum_{i=1}^{n}(x_i - \mu)^2 \]

Step 3: Differentiate and Set to Zero: taking partial derivatives with respect to \(\mu\) and \(\sigma^2\) (not \(\sigma\))

\[ \begin{align*} \frac{\partial \ell}{\partial \mu} & = \frac{1}{\sigma^2} \sum_{i=1}^{n} (x_i - \mu) = 0 \\ \frac{\partial \ell}{\partial \sigma^2}& =-\frac{n}{2\sigma^2} + \frac{1}{2(\sigma^2)^2} \sum_{i=1}^{n} (x_i - \mu)^2 = 0 \end{align*} \]

Step 4; Solve The System for MLE

\[ \begin{align*} \hat{\mu}_{MLE} & = \frac{1}{n} \sum_{i=1}^{n} x_i = \bar{x} \\ \hat{\sigma}_{MLE}^2 & = \frac{1}{n} \sum_{i=1}^{n} (x_i - \bar{x})^2 \end{align*} \]

4.2 MLE of Weibull Paramters

The Weibull distribution is defined by the probability density function (PDF):

\[ \begin{equation} f(t; \lambda, k) = \begin{cases} \frac{k}{\lambda} \left( \frac{t}{\lambda} \right)^{k-1} \exp\left[-\left( \frac{t}{\lambda} \right)^k \right], & t \geq 0 \\ 0, & t < 0 \end{cases} \end{equation} \]

where \(k > 0\) is the shape parameter and \(\lambda > 0\) is the scale parameter.

Likelihood Function

Given an independent and identically distributed (i.i.d.) sample \(\mathbf{t} = (t_1, t_2, \dots, t_n)\), the likelihood function is the product of the individual densities:

\[ \begin{align} L(k, \lambda; \mathbf{t}) &= \prod_{i=1}^{n} f(t_i; \lambda, k) \\ &= \prod_{i=1}^{n} \left[ \frac{k}{\lambda} \left( \frac{t_i}{\lambda} \right)^{k-1} \exp\left( -\left( \frac{t_i}{\lambda} \right)^k \right) \right] \end{align} \]

Log-Likelihood Function

It is easier to work with the log-likelihood function:

\[ \begin{align} \ell(k, \lambda; \mathbf{t}) &= \ln L(k, \lambda; \mathbf{t}) \\ &= \sum_{i=1}^{n} \ln \left[ \frac{k}{\lambda} \left( \frac{t_i}{\lambda} \right)^{k-1} \exp\left( -\left( \frac{t_i}{\lambda} \right)^k \right) \right] \\ &= \sum_{i=1}^{n} \left[ \ln k - \ln \lambda + (k-1)(\ln t_i - \ln \lambda) - \left( \frac{t_i}{\lambda} \right)^k \right] \\ &= \sum_{i=1}^{n} \left[ \ln k - k \ln \lambda + (k-1) \ln t_i - \left( \frac{t_i}{\lambda} \right)^k \right] \end{align} \]

Simplifying further:

\[ \begin{equation} \ell(k, \lambda) = n \ln k - n k \ln \lambda + (k-1) \sum_{i=1}^{n} \ln t_i - \lambda^{-k} \sum_{i=1}^{n} t_i^k \end{equation} \]

Maximum Likelihood Estimation

To find the MLEs \((\hat{k}, \hat{\lambda})\), we take partial derivatives with respect to \(\lambda\) and \(k\), set them to zero, and solve the resulting system of equations.

First, differentiate with respect to \(\lambda\):

\[ \begin{align} \frac{\partial \ell}{\partial \lambda} &= \frac{\partial}{\partial \lambda} \left[ -n k \ln \lambda - \lambda^{-k} \sum_{i=1}^{n} t_i^k \right] \\ &= -n k \frac{1}{\lambda} + k \lambda^{-k-1} \sum_{i=1}^{n} t_i^k \end{align} \]

Set the derivative to zero:

\[ \begin{align} \frac{\partial \ell}{\partial \lambda} = 0 &\Rightarrow -n k \frac{1}{\lambda} + k \lambda^{-k-1} \sum_{i=1}^{n} t_i^k = 0 \\ &\Rightarrow -n + \lambda^{-k} \sum_{i=1}^{n} t_i^k = 0 \\ &\Rightarrow \lambda^{-k} \sum_{i=1}^{n} t_i^k = n \\ &\Rightarrow \lambda^k = \frac{1}{n} \sum_{i=1}^{n} t_i^k \end{align} \]

Thus, we obtain the MLE for \(\lambda\) in terms of \(k\):

\[ \begin{equation} \hat{\lambda} = \left( \frac{1}{n} \sum_{i=1}^{n} t_i^{\hat{k}} \right)^{1/\hat{k}} \end{equation} \]

Now differentiate with respect to \(k\):

\[ \begin{align} \frac{\partial \ell}{\partial k} &= \frac{\partial}{\partial k} \left[ n \ln k - n k \ln \lambda + (k-1) \sum_{i=1}^{n} \ln t_i - \lambda^{-k} \sum_{i=1}^{n} t_i^k \right] \\ &= \frac{n}{k} - n \ln \lambda + \sum_{i=1}^{n} \ln t_i + \lambda^{-k} \ln \lambda \sum_{i=1}^{n} t_i^k - \lambda^{-k} \sum_{i=1}^{n} (t_i^k \ln t_i) \end{align} \]

Set this derivative to zero and substitute \(\lambda^k = \frac{1}{n} \sum_{i=1}^{n} t_i^k\):

\[ \begin{align} \frac{\partial \ell}{\partial k} = 0 &\Rightarrow \frac{n}{k} - n \ln \lambda + \sum_{i=1}^{n} \ln t_i + \lambda^{-k} \ln \lambda \sum_{i=1}^{n} t_i^k - \lambda^{-k} \sum_{i=1}^{n} (t_i^k \ln t_i) = 0 \end{align} \]

Using \(\lambda^{-k} \sum_{i=1}^{n} t_i^k = n\) from previous equation:

\[ \begin{align} &\Rightarrow \frac{n}{k} - n \ln \lambda + \sum_{i=1}^{n} \ln t_i + n \ln \lambda - \lambda^{-k} \sum_{i=1}^{n} (t_i^k \ln t_i) = 0 \\ &\Rightarrow \frac{n}{k} + \sum_{i=1}^{n} \ln t_i - \lambda^{-k} \sum_{i=1}^{n} (t_i^k \ln t_i) = 0 \end{align} \]

Substitute \(\lambda^{-k} = \frac{n}{\sum_{i=1}^{n} t_i^k}\):

\[ \begin{equation} \frac{n}{k} + \sum_{i=1}^{n} \ln t_i - \frac{n \sum_{i=1}^{n} (t_i^k \ln t_i)}{\sum_{i=1}^{n} t_i^k} = 0 \end{equation} \]

Rearranging terms:

\[ \begin{equation} \frac{1}{k} = \frac{\sum_{i=1}^{n} (t_i^k \ln t_i)}{\sum_{i=1}^{n} t_i^k} - \frac{1}{n} \sum_{i=1}^{n} \ln t_i \end{equation} \]

The MLEs \((\hat{k}, \hat{\lambda})\) are the solution to the following system:

\[ \begin{align} \hat{\lambda} &= \left( \frac{1}{n} \sum_{i=1}^{n} t_i^{\hat{k}} \right)^{1/\hat{k}} \quad \cdots\cdots\cdots\cdots\cdots\cdots\quad\text{(A)}\\ \frac{1}{\hat{k}} &= \frac{\sum_{i=1}^{n} (t_i^{\hat{k}} \ln t_i)}{\sum_{i=1}^{n} t_i^{\hat{k}}} - \frac{1}{n} \sum_{i=1}^{n} \ln t_i \quad \cdots\cdots\quad\text{(B)} \end{align} \]

Numerical Solution

This system cannot be solved analytically. The typical numerical procedure is:

  • Solve equation (B) for \(\hat{k}\) using numerical methods (e.g., Newton-Raphson, bisection)

  • Substitute \(\hat{k}\) into equation (A) to obtain \(\hat{\lambda}\)

The right-hand side of equation (24) is often denoted as \(g(k)\):

\[ \begin{equation} g(k) = \frac{\sum_{i=1}^{n} (t_i^k \ln t_i)}{\sum_{i=1}^{n} t_i^k} - \frac{1}{n} \sum_{i=1}^{n} \ln t_i \end{equation} \]

and we solve \(g(k) - 1/k = 0\) iteratively.

We have practiced solving this type of univariate nonlinear equation in previous assignments using various R built-in functions. In the following sections, we will use the more general-purpose built-in function optim() to solve systems of nonlinear equations.

5 Numerical Implementation Using R

If the MLE has a closed form, the evaluation of the MLE is straightforward. If there is no closed form for the MLE, we need to use numerical procedures to approximate it. We will use a simulated Weibull distribution with a given shape (\(k = 2.5\)) and scale (\(\lambda = 1.8\)) and then find MLE of \(k\) and \(\lambda\), denoted by \(\hat{k}\) and \(\hat{\lambda}\).

Since the Weibull distribution is widely used in practice, there is a generic built-in R function fitdistr() in the MASS library for estimating parameters in distributions such as beta, cauchy, chi-squared, exponential, gamma, geometric, log-normal, lognormal, logistic, negative binomial, normal, Poisson, t and weibull.

We will use fitdistr() and manual calculation is a generic optim() based on the derived log-likelihood and score functions to find the MLE of \(k\) and \(\lambda\).

5.1 Estimation with fitdistr()

fitdistr() uses Maximum-likelihood fitting of univariate distributions, allowing parameters to be held fixed if desired.

# Generate sample Weibull data
set.seed(123)
true.shape <- 2.5
true.scale <- 1.8
sample.data <- rweibull(1000, shape = true.shape, scale = true.scale)

# Using fitdistr from MASS package
weibull.fit.mass <- fitdistr(sample.data, "weibull")
#print("MASS::fitdistr results:")
print(weibull.fit.mass)
     shape        scale   
  2.51939096   1.80691546 
 (0.06193779) (0.02388561)

Note R function fitdist() in library fitdistrplus package allows various estimation methods including method of moment estimation and maximum likelihood estimation.

5.2 Manual Implementation of MLE

The following code implements the MLE from the objective function directly. The process of finding the MLE of population parameters applies to any new distributions that are not available in existing R libraries. The key optimization R function to be used is optim() in the base package.

5.2.1 Optimization with No Gradient

# Manual MLE implementation for Weibull distribution
# Weibull log-likelihood function
weibull.loglik <- function(params, data) {
  shape <- params[1]
  scale <- params[2]
  
  n <- length(data)
  
  # Log-likelihood for Weibull distribution
  loglik <- n * log(shape) - n * shape * log(scale) + 
            (shape - 1) * sum(log(data)) - 
            sum((data / scale)^shape)
  
  return(-loglik)  # Return negative for minimization
}

# Perform optimization
initial.params <- c(shape = 2.2, scale = 1.5)  # Reasonable starting values

# Using optim with Nelder-Mead method
mle.result <- optim(
  par = initial.params,
  fn = weibull.loglik,
  data = sample.data,
  hessian = TRUE
)
##
mle.result
$par
   shape    scale 
2.519292 1.806812 

$value
[1] 1014.398

$counts
function gradient 
      53       NA 

$convergence
[1] 0

$message
NULL

$hessian
          shape     scale
shape  289.1672 -235.3547
scale -235.3547 1944.5068

5.2.2 Optimization with Gradient

# Manual MLE implementation for Weibull distribution
# Weibull log-likelihood function
weibull.loglik <- function(params, data) {
  shape <- params[1]  # passing the parameters
  scale <- params[2]
  ##
  n <- length(data)   # sample size
  
  # Log-likelihood for Weibull distribution
  loglik <- n * log(shape) - n * shape * log(scale) + 
            (shape - 1) * sum(log(data)) - 
            sum((data / scale)^shape)
  
  return(loglik)    # Return negative for minimization
}


## Score (gradient) equation

weibull.score <- function(params, data) {
  shape <- params[1]
  scale <- params[2]
  n <- length(data)
  
  # Gradient for shape parameter
  grad_shape <- n/shape - n * log(scale) + 
                sum(log(data)) - 
                sum((data/scale)^shape * log(data/scale))
  
  # Gradient for scale parameter
  grad_scale <- -(n * shape)/scale + 
                (shape/scale) * sum((data/scale)^shape)
  
  return(c(grad_shape, grad_scale))
}


##
# Need to provide initial values for parameters
initial.params <- c(shape = 2.2, scale = 1.5)  # Reasonable starting values


# Using optim with Nelder-Mead method
mle.result <- optim(
  par = initial.params,
  fn = weibull.loglik,
  gr = weibull.score,
  data = sample.data,
  method = "L-BFGS-B",
  hessian = TRUE,
  control = list(trace = FALSE,
                 fnscale = -1,
                 maxit = 500,
                 abstol = 1e-8)
)
##
mle.result
$par
   shape    scale 
2.519393 1.806915 

$value
[1] -1014.398

$counts
function gradient 
       9        9 

$convergence
[1] 0

$message
[1] "CONVERGENCE: REL_REDUCTION_OF_F <= FACTR*EPSMCH"

$hessian
          shape      scale
shape -289.1196   235.1852
scale  235.1852 -1944.0939

Remarks: optim() is a powerful optimization function. While the previous implementation relies solely on the log-likelihood function, real-world scenarios often involve likelihood functions with numerous parameters and complex expressions. In such cases, gradient-based methods become advantageous. Fortunately, optim() offers several gradient-based optimization algorithms to choose from.

# Methods that REQUIRE gradients:
optim(par, fn, gr = gradient_fn, method = "BFGS")
optim(par, fn, gr = gradient_fn, method = "CG")  # Conjugate Gradient
optim(par, fn, gr = gradient_fn, method = "L-BFGS-B")

Examples of how to implement gradient-based optimization can be found in the optim() help documentation.

LS0tDQp0aXRsZTogIk1heGltdW0gTGlrZWxpaG9vZCBFc3RpbWF0aW9uIChNTEUpIg0KYXV0aG9yOiAiQ2hlbmcgUGVuZyINCmRhdGU6ICJXZXN0IENoZXN0ZXIgVW5pdmVyc2l0eSINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jX2NvbGxhcHNlZDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgc21vb3RoX3Njcm9sbDogeWVzDQogICAgaGlnaGxpZ2h0OiBtb25vY2hyb21lDQogICAgdGhlbWU6IHNwYWNlbGFiDQogIHBkZl9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICBmaWdfd2lkdGg6IDMNCiAgICBmaWdfaGVpZ2h0OiAzDQogIHdvcmRfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIGtlZXBfbWQ6IHllcw0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KYGBge2NzcywgZWNobyA9IEZBTFNFfQ0KI1RPQzo6YmVmb3JlIHsNCiAgY29udGVudDogIlRhYmxlIG9mIENvbnRlbnRzIjsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGZvbnQtc2l6ZTogMS4yZW07DQogIGRpc3BsYXk6IGJsb2NrOw0KICBjb2xvcjogbmF2eTsNCiAgbWFyZ2luLWJvdHRvbTogMTBweDsNCn0NCg0KDQpkaXYjVE9DIGxpIHsgICAgIC8qIHRhYmxlIG9mIGNvbnRlbnQgICovDQogICAgbGlzdC1zdHlsZTp1cHBlci1yb21hbjsNCiAgICBiYWNrZ3JvdW5kLWltYWdlOm5vbmU7DQogICAgYmFja2dyb3VuZC1yZXBlYXQ6bm9uZTsNCiAgICBiYWNrZ3JvdW5kLXBvc2l0aW9uOjA7DQp9DQoNCmgxLnRpdGxlIHsgICAgLyogbGV2ZWwgMSBoZWFkZXIgb2YgdGl0bGUgICovDQogIGZvbnQtc2l6ZTogMjJweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGNvbG9yOiBEYXJrUmVkOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtZmFtaWx5OiAiR2lsbCBTYW5zIiwgc2Fucy1zZXJpZjsNCn0NCg0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgZm9udC1zaXplOiAxNXB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IG5hdnk7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCg0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGZvbnQtZmFtaWx5OiAiR2lsbCBTYW5zIiwgc2Fucy1zZXJpZjsNCiAgY29sb3I6IERhcmtCbHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQoNCmgxIHsgLyogSGVhZGVyIDEgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDIwcHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KDQpoMiB7IC8qIEhlYWRlciAyIC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmgzIHsgLyogSGVhZGVyIDMgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDE2cHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDQgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMTRweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KLyogQWRkIGRvdHMgYWZ0ZXIgbnVtYmVyZWQgaGVhZGVycyAqLw0KLmhlYWRlci1zZWN0aW9uLW51bWJlcjo6YWZ0ZXIgew0KICBjb250ZW50OiAiLiI7DQoNCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCi5oaWdobGlnaHRtZSB7IGJhY2tncm91bmQtY29sb3I6eWVsbG93OyB9DQoNCnAgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCn0NCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCANCiMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLg0KaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpDQogICBsaWJyYXJ5KGtuaXRyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJwYW5kZXIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicGFuZGVyIikNCiAgIGxpYnJhcnkocGFuZGVyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnZ3Bsb3QyIikpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpDQogIGxpYnJhcnkoZ2dwbG90MikNCn0NCmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikNCiAgbGlicmFyeSh0aWR5dmVyc2UpDQp9DQoNCmlmICghcmVxdWlyZSgicGxvdGx5IikpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikNCiAgbGlicmFyeShwbG90bHkpDQp9DQppZiAoIXJlcXVpcmUoImZpdGRpc3RycGx1cyIpKSB7DQogIGluc3RhbGwucGFja2FnZXMoImZpdGRpc3RycGx1cyIpDQogIGxpYnJhcnkoZml0ZGlzdHJwbHVzKQ0KfQ0KIyMgbGlicmFyeShmaXRkaXN0cnBsdXMpDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgICAgICMgaW5jbHVkZSBjb2RlIGNodW5rIGluIHRoZSBvdXRwdXQgZmlsZQ0KICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwgICAjIHNvbWV0aW1lcywgeW91IGNvZGUgbWF5IHByb2R1Y2Ugd2FybmluZyBtZXNzYWdlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhlIG91dHB1dCBmaWxlLiANCiAgICAgICAgICAgICAgICAgICAgICByZXN1bHRzID0gVFJVRSwgICAgIyB5b3UgY2FuIGFsc28gZGVjaWRlIHdoZXRoZXIgdG8gaW5jbHVkZSB0aGUgb3V0cHV0DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW4gdGhlIG91dHB1dCBmaWxlLg0KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gTkENCiAgICAgICAgICAgICAgICAgICAgICApICANCmBgYA0KDQpcDQoNCg0KIyBJbnRyb2R1Y3Rpb24NCg0KSW4gc3RhdGlzdGljYWwgaW5mZXJlbmNlLCB3ZSBvZnRlbiB3YW50IHRvIGVzdGltYXRlIHVua25vd24gcGFyYW1ldGVycyBvZiBhIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbiBiYXNlZCBvbiBvYnNlcnZlZCBkYXRhLiBUaGUgbGlrZWxpaG9vZCBmdW5jdGlvbiBhbmQgbWF4aW11bSBsaWtlbGlob29kIGVzdGltYXRpb24gKE1MRSkgYXJlIGZ1bmRhbWVudGFsIGNvbmNlcHRzIHRoYXQgcHJvdmlkZSBhIHBvd2VyZnVsIGZyYW1ld29yayBmb3IgcGFyYW1ldGVyIGVzdGltYXRpb24uDQoNCiMgTGlrZWxpaG9vZCBGdW5jdGlvbg0KDQpHaXZlbiBhIHNldCBvZiBpbmRlcGVuZGVudCBhbmQgaWRlbnRpY2FsbHkgZGlzdHJpYnV0ZWQgKGkuaS5kLikgb2JzZXJ2YXRpb25zICRcbWF0aGJme3h9ID0gKHhfMSwgeF8yLCBcZG90cywgeF9uKSQgZnJvbSBhIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbiB3aXRoIHByb2JhYmlsaXR5IGRlbnNpdHkgZnVuY3Rpb24gKFBERikgb3IgcHJvYmFiaWxpdHkgbWFzcyBmdW5jdGlvbiAoUE1GKSAkZih4fFx0aGV0YSkkLCB3aGVyZSAkXHRoZXRhJCBpcyBhbiB1bmtub3duIHBhcmFtZXRlciwgdGhlIGxpa2VsaWhvb2QgZnVuY3Rpb24gaXMgZGVmaW5lZCBhczoNCg0KJCQNCkwoXHRoZXRhIFxtaWQgeCkgPSBccHJvZF97aT0xfV57bn0gZih4X2kgXG1pZCBcdGhldGEpDQokJA0KDQpUaGUgbGlrZWxpaG9vZCBmdW5jdGlvbiBtZWFzdXJlcyBob3cgImxpa2VseSIgdGhlIHBhcmFtZXRlciAkXHRoZXRhJCBpcywgZ2l2ZW4gdGhlIG9ic2VydmVkIGRhdGEgJFxtYXRoYmZ7eH0kLiBJbiBvdGhlciB3b3JkcywgKipsaWtlbGlob29kIGZ1bmN0aW9uKiogaXMNCg0KKiBub3QgYSBwcm9iYWJpbGl0eSBhYm91dCB0aGUgZGF0YTogV2UgYWxyZWFkeSBoYXZlIHRoZSBkYXRhOyBpdCdzIGZpeGVkLg0KDQoqIGEgZnVuY3Rpb24gb2YgdGhlIHBhcmFtZXRlcnM6IFdlIHVzZSB0aGUgZGF0YSB0byBtYWtlIGluZmVyZW5jZXMgYWJvdXQgdGhlIHVua25vd24gcGFyYW1ldGVycy4NCg0KVGhlIGxpa2VsaWhvb2QgZnVuY3Rpb24gbG9va3MgaWRlbnRpY2FsIHRvIHRoZSBwcm9kdWN0IG9mIHRoZSBwcm9iYWJpbGl0eSBtYXNzIG9yIGRlbnNpdHkgZnVuY3Rpb24gZXZhbHVhdGVkIGF0IGVhY2ggaW5kaXZpZHVhbCBkYXRhIHBvaW50LCBidXQgdGhlIGludGVycHJldGF0aW9uIGlzIGRpZmZlcmVudC4gVGhlIGZ1bmN0aW9uIGlzIHRoZSBzYW1lLCBidXQgd2hhdCBpcyB2YXJ5aW5nIGFuZCB3aGF0IGlzIGZpeGVkIGlzIHJldmVyc2VkLg0KDQoqIEluICRccHJvZF97aT0xfV5uIGYoeCB8IM64KSQ6IM64IGlzIGZpeGVkLCAqeCogdmFyaWVzLiAoQW5zd2VyOiAiV2hhdCBpcyB0aGUgcHJvYmFiaWxpdHkgb2Ygc2VlaW5nIGRpZmZlcmVudCBkYXRhIHBvaW50cyBnaXZlbiBhIGtub3duIHBhcmFtZXRlcj8iKQ0KDQoqIEluICRMKM64IHwgeCkkOiAqeCogaXMgZml4ZWQsIM64IHZhcmllcy4gKEFuc3dlcjogIkhvdyBsaWtlbHkgYXJlIGRpZmZlcmVudCBwYXJhbWV0ZXIgdmFsdWVzIGdpdmVuIG15IGZpeGVkLCBvYnNlcnZlZCBkYXRhPyIpDQoNCg0KRm9yIGNvbXB1dGF0aW9uYWwgY29udmVuaWVuY2UsIHdlIG9mdGVuIHdvcmsgd2l0aCB0aGUgbG9nLWxpa2VsaWhvb2QgZnVuY3Rpb246DQoNCiQkDQpcZWxsKFx0aGV0YSBcbWlkIHgpID0gXGxvZyBMKFx0aGV0YSBcbWlkIHgpID0gXHN1bV97aT0xfV57bn0gXGxvZyBmKHhfaSBcbWlkIFx0aGV0YSkNCiQkDQoNClRoZSBsb2dhcml0aG0gdHJhbnNmb3JtcyB0aGUgcHJvZHVjdCBpbnRvIGEgc3VtLCB3aGljaCBpcyBlYXNpZXIgdG8gZGlmZmVyZW50aWF0ZSBhbmQgb3B0aW1pemUuDQoNCg0KIyBNYXhpbXVtIExpa2VsaWhvb2QgRXN0aW1hdGlvbg0KDQpUaGUgTUxFIG1ldGhvZCBpcyBpbmNyZWRpYmx5IGludHVpdGl2ZTogRmluZCB0aGUgcGFyYW1ldGVyIHZhbHVlIHRoYXQgbWFrZXMgdGhlIG9ic2VydmVkIGRhdGEgbW9zdCBwcm9iYWJsZS4gVGhlIGlkZWEgb3JpZ2luYXRlcyBmcm9tIFJvbmFsZCBBLiBGaXNoZXIgaW4gYSBzZXJpZXMgb2YgcGFwZXJzIHN0YXJ0aW5nIGluIHRoZSAxOTIwcy4gVGhlIGtleSBwYXBlciBpczoNCg0KRmlzaGVyLCBSLkEuICgxOTIyKS4gT24gdGhlIG1hdGhlbWF0aWNhbCBmb3VuZGF0aW9ucyBvZiB0aGVvcmV0aWNhbCBzdGF0aXN0aWNzLiAqUGhpbG9zb3BoaWNhbCBUcmFuc2FjdGlvbnMgb2YgdGhlIFJveWFsIFNvY2lldHkgb2YgTG9uZG9uKiwgU2VyaWVzIEEsIDIyMiwgMzA54oCTMzY4Lg0KDQpJbiB0aGlzIHBhcGVyLCBGaXNoZXINCg0KKiBJbnRyb2R1Y2VkIHRoZSB0ZXJtIGxpa2VsaWhvb2QgKGRpc3RpbmN0IGZyb20gcHJvYmFiaWxpdHkpLg0KDQoqIFByb3Bvc2VkIG1heGltdW0gbGlrZWxpaG9vZCBhcyBhIGdlbmVyYWwgbWV0aG9kIGZvciBlc3RpbWF0aW5nIHBhcmFtZXRlcnMuDQoNCiogRXhwbGFpbmVkIHRoYXQgdGhlIG1vc3Qg4oCccmVhc29uYWJsZeKAnSBlc3RpbWF0ZSBvZiBhIHBhcmFtZXRlciBpcyB0aGUgb25lIHRoYXQgbWF4aW1pemVzIHRoZSBsaWtlbGlob29kIG9mIHRoZSBvYnNlcnZlZCBkYXRhLg0KDQoNCg0KIyMgTGlrZWxpaG9vZCBQcmluY2lwbGUNCg0KDQoqQWxsIHRoZSBldmlkZW5jZSBmcm9tIGFuIGV4cGVyaW1lbnQgYWJvdXQgYSBtb2RlbCBwYXJhbWV0ZXIgJFx0aGV0YSQgaXMgY29udGFpbmVkIGluIHRoZSBsaWtlbGlob29kIGZ1bmN0aW9uLiBGdXJ0aGVybW9yZSwgdHdvIGxpa2VsaWhvb2QgZnVuY3Rpb25zIHRoYXQgYXJlIHByb3BvcnRpb25hbCB0byBlYWNoIG90aGVyIChpLmUuLCB0aGV5IGRpZmZlciBvbmx5IGJ5IGEgY29uc3RhbnQgbXVsdGlwbGljYXRpdmUgZmFjdG9yKSBjb250YWluIHRoZSBzYW1lIGluZm9ybWF0aW9uIGFib3V0ICRcdGhldGEkLioNCg0KDQpUaGlzIGhhcyB0d28gY3J1Y2lhbCBpbXBsaWNhdGlvbnM6DQoNCiogKipUaGUgb25seSB0aGluZyB0aGF0IG1hdHRlcnMgaXMgdGhlIGRhdGEgeW91IGFjdHVhbGx5IG9ic2VydmVkKiouIFRoZSBpbmZlcmVuY2Ugc2hvdWxkIG5vdCBkZXBlbmQgb24gZGF0YSB5b3UgbWlnaHQgaGF2ZSBvYnNlcnZlZCBidXQgZGlkbid0IChpLmUuLCB0aGUgc2FtcGxlIHNwYWNlKS4gUHJvY2VkdXJlcyBsaWtlIHAtdmFsdWVzLCB3aGljaCBhcmUgYmFzZWQgb24gdGhlIHNhbXBsZSBzcGFjZSwgdmlvbGF0ZSB0aGUgTGlrZWxpaG9vZCBQcmluY2lwbGUuDQoNCiogKipUaGUgc2NhbGluZyBvZiB0aGUgbGlrZWxpaG9vZCBmdW5jdGlvbiBpcyBpcnJlbGV2YW50KiouIFNpbmNlIGEgY29uc3RhbnQgbXVsdGlwbGllciBkb2Vzbid0IGNoYW5nZSB0aGUgbG9jYXRpb24gb2YgdGhlIG1heGltdW0gb3IgdGhlIHJhdGlvcyBiZXR3ZWVuIGRpZmZlcmVudCBsaWtlbGlob29kcywgaXQgZG9lc24ndCBhZmZlY3QgaW5mZXJlbmNlLg0KDQpXZSB3aWxsIHNlZSB0aGF0ICoqTUxFIGlzIGEgRGlyZWN0IEFwcGxpY2F0aW9uIG9mIHRoZSBMaWtlbGlob29kIFByaW5jaXBsZSEqKg0KDQpJbnRlcmVzdGluZ2x5LCBGaXNoZXIgaW50cm9kdWNlZCB0aGUgbGlrZWxpaG9vZCBmdW5jdGlvbiAobWVhc3VyaW5nIGhvdyB3ZWxsIHBhcmFtZXRlciB2YWx1ZXMgZXhhbXBsaW4gb2JzZXJ2ZWQgZGF0YSkgYW5kIG1heGltdW0gbGlrZWxpaG9vZCBlc3RpbWF0aW9uIChNTEUsIGVzdGltYXRpbmcgcGFyYW1ldGVycyBieSBtYXhpbWl6aW5nIGxpa2VsaWhvb2QgLSBpLmUuLCBtYWtpbmcgdGhlIG9ic2VydmVkIG1vc3QgcHJvYmFibGUpIGluIDE5MjIuIEhvd2V2ZXIsIHRoZSBsaWtlbGlob29kIHByaW5jaXBsZSB3YXMgZm9ybWFsaXplZCBieSBCaXJuYmF1bSAxOTYyLg0KDQoNCiMjIEZpbmRpbmcgdGhlIE1MRQ0KDQpUaGUgbWF4aW11bSBsaWtlbGlob29kIGVzdGltYXRlIChNTEUpICRcaGF0e1x0aGV0YX1fe01MRX0kIGlzIHRoZSB2YWx1ZSBvZiAkXHRoZXRhJCB0aGF0IG1heGltaXplcyB0aGUgbGlrZWxpaG9vZCBmdW5jdGlvbjoNCg0KDQokJA0KXGhhdHtcdGhldGF9X3tcdGV4dHtNTEV9fSA9IFxhcmdcbWF4X3tcdGhldGF9ICBMKFx0aGV0YSBcbWlkIHgpID0gXGFyZyBcbWF4X3tcdGhldGF9IFxlbGwoXHRoZXRhIFxtaWQgeCkNCiQkDQoNClRvIGZpbmQgdGhlIE1MRSwgd2UgdHlwaWNhbGx5Og0KDQoqIFdyaXRlIHRoZSBsaWtlbGlob29kIGZ1bmN0aW9uICRMKFx0aGV0YXxcbWF0aGJme3h9KSQgLSA8Zm9udCBjb2xvciA9ICJibHVlIj5hIGZ1bmN0aW9uIG9mIHBhcmFtZXRlcihzKSAkXHRoZXRhJCBzaW5jZSAkXG1hdGhiZnt4fSQgaXMgb2JzZXJ2ZWQuPC9mb250Pg0KDQoqIFRha2UgdGhlIG5hdHVyYWwgbG9nYXJpdGhtIHRvIGdldCAkXGVsbChcdGhldGF8XG1hdGhiZnt4fSkkIC0gPGZvbnQgY29sb3IgPSAiYmx1ZSI+IGNvbnZlcnRpbmcgbXVsdGlwbGljYXRpb24gdG8gYWRkaXRpb24gdGhhdCBtYWtlIGFsZ2VicmEgZWFzaWVyLjwvZm9udD4NCg0KKiBEaWZmZXJlbnRpYXRlIHdpdGggcmVzcGVjdCB0byAkXHRoZXRhJCAtIDxmb250IGNvbG9yID0gImJsdWUiPiBXaGVuIGRpZmZlcmVudGlhdGluZyB3aXRoIHJlc3BlY3QgdG8gcGFyYW1ldGVycyBpbiBhIG11bHRpcGFyYW1ldGVyIHNldHRpbmcsIGJlIHN1cmUgdG8gdGFrZSBwYXJ0aWFsIGRlcml2YXRpdmVzIHNlcGFyYXRlbHkgZm9yIGVhY2ggb2YgdGhlIHR3byBwYXJhbWV0ZXJzLjwvZm9udD4gPGZvbnQgY29sb3IgPSAicmVkIj4gVGhlc2UgZGVyaXZhdGl2ZXMgYXJlIGFsc28gY2FsbGVkIGdyYWRpZW50cyBhbmQgc2NvcmUgZnVuY3Rpb25zLjwvZm9udD4NCg0KKiBTZXQgdGhlIGRlcml2YXRpdmUgZXF1YWwgdG8gemVybyAtIDxmb250IGNvbG9yID0gImJsdWUiPmFsc28gY2FsbGVkIHNjb3JlIGVxdWF0aW9uLjwvZm9udD4NCg0KKiBTb2x2ZSBmb3IgJFx0aGV0YSQgLSA8Zm9udCBjb2xvciA9ICJibHVlIj5UaGlzIHR5cGljYWxseSByZXF1aXJlcyBudW1lcmljYWwgcHJvY2VkdXJlcyB0byBhcHByb3hpbWF0ZSB0aGUgc29sdXRpb24uPC9mb250Pg0KDQpUaGUgZXF1YXRpb24gJFxmcmFje1xwYXJ0aWFsIFxlbGwoXHRoZXRhfFxtYXRoYmZ7eH0pfXtccGFydGlhbCBcdGhldGF9ID0gMCQgaXMgY2FsbGVkIHRoZSBsaWtlbGlob29kIGVxdWF0aW9uLg0KDQoNCiMgRmluZGluZyBNTEUgd2l0aCBFeGFtcGxlcw0KDQpXZSB3aWxsIHVzZSBzZXZlcmFsIGV4YW1wbGVzIHRvIGRlbW9uc3RyYXRlIHRoZSBtYXRoZW1hdGljYWwgZGVyaXZhdGlvbnMgb2YgTUxFcy4gU2ltaWxhciB0byB0aGUgbWV0aG9kIG9mIG1vbWVudCBlc3RpbWF0ZXMsIG5vdCBhbGwgTUxFIGhhdmUgY2xvc2VkIGV4cHJlc3Npb25zLiBJbiBtYW55IGNhc2VzLCBudW1lcmljYWwgYWxnb3JpdGhtcyBhcmUgcmVxdWlyZWQgdG8gZmluZCB0aGUgYXBwcm94aW1hdGlvbiBvZiB0aGUgTUxFIG9mIHRoZSB1bmtub3duIHBhcmFtZXRlcnMuDQoNCg0KIyMgTUxFIG9mIE5vcm1hbCBNZWFuIGFuZCBWYXJpYW5jZQ0KDQpMZXQgJFhfMSwgWF8yLCBcZG90cywgWF9uIFxzaW0gTihcbXUsIFxzaWdtYV4yKSQuIEJvdGggJFxtdSQgYW5kICRcc2lnbWFeMiQgYXJlIHVua25vd24uIFdlIHdpbGwgZGVyaXZlIHRoZSBNTEUgb2YgJFxtdSQgYW5kICRcc2lnbWFeMiQuIFdlIHdpbGwgZGl2aWRlIHRoZSBwcm9jZXNzIGludG8gdGhlIGZvbGxvd2luZyBzdGVwczoNCg0KKipTdGVwIDE6IExpa2VsaWhvb2QgRnVuY3Rpb24qKjogd2UgZmlyc3Qgd3JpdGUgdGhlIGxpa2VsaWhvb2QgZnVuY3Rpb24gb2YgcGFyYW1ldGVyIGJhc2VkIG9uIHRoZSBvYnNlcnZlZCBpaWQgZGF0YS4NCg0KJCQNCkwoXG11IFxtaWQgeCkgPSBccHJvZF97aT0xfV57bn0gXGZyYWN7MX17XHNxcnR7MlxwaVxzaWdtYV4yfX0gDQpcZXhwXCFcbGVmdCgtXGZyYWN7KHhfaSAtIFxtdSleMn17MlxzaWdtYV4yfVxyaWdodCkNCiQkDQoNCg0KKipTdGVwIDI6IExvZy1MaWtlbGlob29kIEZ1bmN0aW9uKioNCg0KJCQNClxlbGwoXG11IFxtaWQgeCkgPSAtXGZyYWN7bn17Mn1cbG9nKDJccGlcc2lnbWFeMikgDQotIFxmcmFjezF9ezJcc2lnbWFeMn1cc3VtX3tpPTF9XntufSh4X2kgLSBcbXUpXjINCiQkDQoNCioqU3RlcCAzOiBEaWZmZXJlbnRpYXRlIGFuZCBTZXQgdG8gWmVybyoqOiB0YWtpbmcgcGFydGlhbCBkZXJpdmF0aXZlcyB3aXRoIHJlc3BlY3QgdG8gJFxtdSQgYW5kICRcc2lnbWFeMiQgKG5vdCAkXHNpZ21hJCkNCg0KDQokJA0KXGJlZ2lue2FsaWduKn0NCiAgICAgICAgXGZyYWN7XHBhcnRpYWwgXGVsbH17XHBhcnRpYWwgXG11fSAmID0gIFxmcmFjezF9e1xzaWdtYV4yfSBcc3VtX3tpPTF9XntufSAoeF9pIC0gXG11KSA9IDAgXFwNCiAgICAgICAgXGZyYWN7XHBhcnRpYWwgXGVsbH17XHBhcnRpYWwgXHNpZ21hXjJ9JiA9LVxmcmFje259ezJcc2lnbWFeMn0gKyBcZnJhY3sxfXsyKFxzaWdtYV4yKV4yfSBcc3VtX3tpPTF9XntufSAoeF9pIC0gXG11KV4yID0gMA0KXGVuZHthbGlnbip9DQokJA0KDQoNCioqU3RlcCA0OyBTb2x2ZSBUaGUgU3lzdGVtIGZvciBNTEUqKg0KDQokJA0KXGJlZ2lue2FsaWduKn0NCiAgICAgICAgXGhhdHtcbXV9X3tNTEV9ICYgPSAgXGZyYWN7MX17bn0gXHN1bV97aT0xfV57bn0geF9pID0gXGJhcnt4fSAgIFxcDQogICAgICAgIFxoYXR7XHNpZ21hfV97TUxFfV4yICYgPSBcZnJhY3sxfXtufSBcc3VtX3tpPTF9XntufSAoeF9pIC0gXGJhcnt4fSleMg0KXGVuZHthbGlnbip9DQokJA0KDQoNCiMjIE1MRSBvZiBXZWlidWxsIFBhcmFtdGVycw0KDQpUaGUgV2VpYnVsbCBkaXN0cmlidXRpb24gaXMgZGVmaW5lZCBieSB0aGUgcHJvYmFiaWxpdHkgZGVuc2l0eSBmdW5jdGlvbiAoUERGKToNCg0KJCQNClxiZWdpbntlcXVhdGlvbn0NCiAgICBmKHQ7IFxsYW1iZGEsIGspID0gDQogICAgXGJlZ2lue2Nhc2VzfQ0KICAgICAgICBcZnJhY3trfXtcbGFtYmRhfSBcbGVmdCggXGZyYWN7dH17XGxhbWJkYX0gXHJpZ2h0KV57ay0xfSBcZXhwXGxlZnRbLVxsZWZ0KCBcZnJhY3t0fXtcbGFtYmRhfSBccmlnaHQpXmsgXHJpZ2h0XSwgJiB0IFxnZXEgMCBcXA0KICAgICAgICAwLCAmIHQgPCAwDQogICAgXGVuZHtjYXNlc30NClxlbmR7ZXF1YXRpb259DQokJA0KDQp3aGVyZSAkayA+IDAkIGlzIHRoZSAqKnNoYXBlKiogcGFyYW1ldGVyIGFuZCAkXGxhbWJkYSA+IDAkIGlzIHRoZSAqKnNjYWxlKiogcGFyYW1ldGVyLg0KDQoqKkxpa2VsaWhvb2QgRnVuY3Rpb24qKg0KDQpHaXZlbiBhbiBpbmRlcGVuZGVudCBhbmQgaWRlbnRpY2FsbHkgZGlzdHJpYnV0ZWQgKGkuaS5kLikgc2FtcGxlICRcbWF0aGJme3R9ID0gKHRfMSwgdF8yLCBcZG90cywgdF9uKSQsIHRoZSBsaWtlbGlob29kIGZ1bmN0aW9uIGlzIHRoZSBwcm9kdWN0IG9mIHRoZSBpbmRpdmlkdWFsIGRlbnNpdGllczoNCg0KJCQNClxiZWdpbnthbGlnbn0NCiAgICBMKGssIFxsYW1iZGE7IFxtYXRoYmZ7dH0pICY9IFxwcm9kX3tpPTF9XntufSBmKHRfaTsgXGxhbWJkYSwgaykgXFwNCiAgICAmPSBccHJvZF97aT0xfV57bn0gXGxlZnRbIFxmcmFje2t9e1xsYW1iZGF9IFxsZWZ0KCBcZnJhY3t0X2l9e1xsYW1iZGF9IFxyaWdodClee2stMX0gXGV4cFxsZWZ0KCAtXGxlZnQoIFxmcmFje3RfaX17XGxhbWJkYX0gXHJpZ2h0KV5rIFxyaWdodCkgXHJpZ2h0XQ0KXGVuZHthbGlnbn0NCiQkDQoNCg0KKipMb2ctTGlrZWxpaG9vZCBGdW5jdGlvbioqDQoNCkl0IGlzIGVhc2llciB0byB3b3JrIHdpdGggdGhlIGxvZy1saWtlbGlob29kIGZ1bmN0aW9uOg0KDQokJA0KXGJlZ2lue2FsaWdufQ0KICAgIFxlbGwoaywgXGxhbWJkYTsgXG1hdGhiZnt0fSkgJj0gXGxuIEwoaywgXGxhbWJkYTsgXG1hdGhiZnt0fSkgXFwNCiAgICAmPSBcc3VtX3tpPTF9XntufSBcbG4gXGxlZnRbIFxmcmFje2t9e1xsYW1iZGF9IFxsZWZ0KCBcZnJhY3t0X2l9e1xsYW1iZGF9IFxyaWdodClee2stMX0gXGV4cFxsZWZ0KCAtXGxlZnQoIFxmcmFje3RfaX17XGxhbWJkYX0gXHJpZ2h0KV5rIFxyaWdodCkgXHJpZ2h0XSBcXA0KICAgICY9IFxzdW1fe2k9MX1ee259IFxsZWZ0WyBcbG4gayAtIFxsbiBcbGFtYmRhICsgKGstMSkoXGxuIHRfaSAtIFxsbiBcbGFtYmRhKSAtIFxsZWZ0KCBcZnJhY3t0X2l9e1xsYW1iZGF9IFxyaWdodCleayBccmlnaHRdIFxcDQogICAgJj0gXHN1bV97aT0xfV57bn0gXGxlZnRbIFxsbiBrIC0gayBcbG4gXGxhbWJkYSArIChrLTEpIFxsbiB0X2kgLSBcbGVmdCggXGZyYWN7dF9pfXtcbGFtYmRhfSBccmlnaHQpXmsgXHJpZ2h0XQ0KXGVuZHthbGlnbn0NCiQkDQoNClNpbXBsaWZ5aW5nIGZ1cnRoZXI6DQoNCiQkDQpcYmVnaW57ZXF1YXRpb259DQogICAgXGVsbChrLCBcbGFtYmRhKSA9IG4gXGxuIGsgLSBuIGsgXGxuIFxsYW1iZGEgKyAoay0xKSBcc3VtX3tpPTF9XntufSBcbG4gdF9pIC0gXGxhbWJkYV57LWt9IFxzdW1fe2k9MX1ee259IHRfaV5rDQpcZW5ke2VxdWF0aW9ufQ0KJCQNCg0KKipNYXhpbXVtIExpa2VsaWhvb2QgRXN0aW1hdGlvbioqDQoNClRvIGZpbmQgdGhlIE1MRXMgJChcaGF0e2t9LCBcaGF0e1xsYW1iZGF9KSQsIHdlIHRha2UgcGFydGlhbCBkZXJpdmF0aXZlcyB3aXRoIHJlc3BlY3QgdG8gJFxsYW1iZGEkIGFuZCAkayQsIHNldCB0aGVtIHRvIHplcm8sIGFuZCBzb2x2ZSB0aGUgcmVzdWx0aW5nIHN5c3RlbSBvZiBlcXVhdGlvbnMuDQoNCg0KRmlyc3QsIGRpZmZlcmVudGlhdGUgd2l0aCByZXNwZWN0IHRvICRcbGFtYmRhJDoNCg0KJCQNClxiZWdpbnthbGlnbn0NCiAgICBcZnJhY3tccGFydGlhbCBcZWxsfXtccGFydGlhbCBcbGFtYmRhfSAmPSBcZnJhY3tccGFydGlhbH17XHBhcnRpYWwgXGxhbWJkYX0gXGxlZnRbIC1uIGsgXGxuIFxsYW1iZGEgLSBcbGFtYmRhXnsta30gXHN1bV97aT0xfV57bn0gdF9pXmsgXHJpZ2h0XSBcXA0KICAgICY9IC1uIGsgXGZyYWN7MX17XGxhbWJkYX0gKyBrIFxsYW1iZGFeey1rLTF9IFxzdW1fe2k9MX1ee259IHRfaV5rDQpcZW5ke2FsaWdufQ0KJCQNCg0KU2V0IHRoZSBkZXJpdmF0aXZlIHRvIHplcm86DQoNCiQkDQpcYmVnaW57YWxpZ259DQogICAgXGZyYWN7XHBhcnRpYWwgXGVsbH17XHBhcnRpYWwgXGxhbWJkYX0gPSAwICZcUmlnaHRhcnJvdyAtbiBrIFxmcmFjezF9e1xsYW1iZGF9ICsgayBcbGFtYmRhXnstay0xfSBcc3VtX3tpPTF9XntufSB0X2leayA9IDAgXFwNCiAgICAmXFJpZ2h0YXJyb3cgLW4gKyBcbGFtYmRhXnsta30gXHN1bV97aT0xfV57bn0gdF9pXmsgPSAwIFxcDQogICAgJlxSaWdodGFycm93IFxsYW1iZGFeey1rfSBcc3VtX3tpPTF9XntufSB0X2leayA9IG4gXFwNCiAgICAmXFJpZ2h0YXJyb3cgXGxhbWJkYV5rID0gXGZyYWN7MX17bn0gXHN1bV97aT0xfV57bn0gdF9pXmsNClxlbmR7YWxpZ259DQokJA0KDQoNClRodXMsIHdlIG9idGFpbiB0aGUgTUxFIGZvciAkXGxhbWJkYSQgaW4gdGVybXMgb2YgJGskOg0KDQokJA0KXGJlZ2lue2VxdWF0aW9ufQ0KICAgIFxoYXR7XGxhbWJkYX0gPSBcbGVmdCggXGZyYWN7MX17bn0gXHN1bV97aT0xfV57bn0gdF9pXntcaGF0e2t9fSBccmlnaHQpXnsxL1xoYXR7a319DQpcZW5ke2VxdWF0aW9ufQ0KJCQNCg0KTm93IGRpZmZlcmVudGlhdGUgd2l0aCByZXNwZWN0IHRvICRrJDoNCg0KJCQNClxiZWdpbnthbGlnbn0NCiAgICBcZnJhY3tccGFydGlhbCBcZWxsfXtccGFydGlhbCBrfSAmPSBcZnJhY3tccGFydGlhbH17XHBhcnRpYWwga30gXGxlZnRbIG4gXGxuIGsgLSBuIGsgXGxuIFxsYW1iZGEgKyAoay0xKSBcc3VtX3tpPTF9XntufSBcbG4gdF9pIC0gXGxhbWJkYV57LWt9IFxzdW1fe2k9MX1ee259IHRfaV5rIFxyaWdodF0gXFwNCiAgICAmPSBcZnJhY3tufXtrfSAtIG4gXGxuIFxsYW1iZGEgKyBcc3VtX3tpPTF9XntufSBcbG4gdF9pICsgXGxhbWJkYV57LWt9IFxsbiBcbGFtYmRhIFxzdW1fe2k9MX1ee259IHRfaV5rIC0gXGxhbWJkYV57LWt9IFxzdW1fe2k9MX1ee259ICh0X2leayBcbG4gdF9pKQ0KXGVuZHthbGlnbn0NCiQkDQoNCg0KU2V0IHRoaXMgZGVyaXZhdGl2ZSB0byB6ZXJvIGFuZCBzdWJzdGl0dXRlICRcbGFtYmRhXmsgPSBcZnJhY3sxfXtufSBcc3VtX3tpPTF9XntufSB0X2leayQ6DQoNCiQkDQpcYmVnaW57YWxpZ259DQogICAgXGZyYWN7XHBhcnRpYWwgXGVsbH17XHBhcnRpYWwga30gPSAwICZcUmlnaHRhcnJvdyBcZnJhY3tufXtrfSAtIG4gXGxuIFxsYW1iZGEgKyBcc3VtX3tpPTF9XntufSBcbG4gdF9pICsgXGxhbWJkYV57LWt9IFxsbiBcbGFtYmRhIFxzdW1fe2k9MX1ee259IHRfaV5rIC0gXGxhbWJkYV57LWt9IFxzdW1fe2k9MX1ee259ICh0X2leayBcbG4gdF9pKSA9IDANClxlbmR7YWxpZ259DQokJA0KDQoNClVzaW5nICRcbGFtYmRhXnsta30gXHN1bV97aT0xfV57bn0gdF9pXmsgPSBuJCBmcm9tIHByZXZpb3VzIGVxdWF0aW9uOg0KDQokJA0KXGJlZ2lue2FsaWdufQ0KICAgICZcUmlnaHRhcnJvdyBcZnJhY3tufXtrfSAtIG4gXGxuIFxsYW1iZGEgKyBcc3VtX3tpPTF9XntufSBcbG4gdF9pICsgbiBcbG4gXGxhbWJkYSAtIFxsYW1iZGFeey1rfSBcc3VtX3tpPTF9XntufSAodF9pXmsgXGxuIHRfaSkgPSAwIFxcDQogICAgJlxSaWdodGFycm93IFxmcmFje259e2t9ICsgXHN1bV97aT0xfV57bn0gXGxuIHRfaSAtIFxsYW1iZGFeey1rfSBcc3VtX3tpPTF9XntufSAodF9pXmsgXGxuIHRfaSkgPSAwDQpcZW5ke2FsaWdufQ0KJCQNCg0KU3Vic3RpdHV0ZSAkXGxhbWJkYV57LWt9ID0gXGZyYWN7bn17XHN1bV97aT0xfV57bn0gdF9pXmt9JDoNCg0KJCQNClxiZWdpbntlcXVhdGlvbn0NCiAgICBcZnJhY3tufXtrfSArIFxzdW1fe2k9MX1ee259IFxsbiB0X2kgLSBcZnJhY3tuIFxzdW1fe2k9MX1ee259ICh0X2leayBcbG4gdF9pKX17XHN1bV97aT0xfV57bn0gdF9pXmt9ID0gMA0KXGVuZHtlcXVhdGlvbn0NCiQkDQoNCg0KUmVhcnJhbmdpbmcgdGVybXM6DQoNCiQkDQpcYmVnaW57ZXF1YXRpb259DQogICAgXGZyYWN7MX17a30gPSBcZnJhY3tcc3VtX3tpPTF9XntufSAodF9pXmsgXGxuIHRfaSl9e1xzdW1fe2k9MX1ee259IHRfaV5rfSAtIFxmcmFjezF9e259IFxzdW1fe2k9MX1ee259IFxsbiB0X2kNClxlbmR7ZXF1YXRpb259DQokJA0KDQpUaGUgTUxFcyAkKFxoYXR7a30sIFxoYXR7XGxhbWJkYX0pJCBhcmUgdGhlIHNvbHV0aW9uIHRvIHRoZSBmb2xsb3dpbmcgc3lzdGVtOg0KDQokJA0KXGJlZ2lue2FsaWdufQ0KICAgIFxoYXR7XGxhbWJkYX0gJj0gXGxlZnQoIFxmcmFjezF9e259IFxzdW1fe2k9MX1ee259IHRfaV57XGhhdHtrfX0gXHJpZ2h0KV57MS9caGF0e2t9fSBccXVhZCBcY2RvdHNcY2RvdHNcY2RvdHNcY2RvdHNcY2RvdHNcY2RvdHNccXVhZFx0ZXh0eyhBKX1cXA0KICAgIFxmcmFjezF9e1xoYXR7a319ICY9IFxmcmFje1xzdW1fe2k9MX1ee259ICh0X2lee1xoYXR7a319IFxsbiB0X2kpfXtcc3VtX3tpPTF9XntufSB0X2lee1xoYXR7a319fSAtIFxmcmFjezF9e259IFxzdW1fe2k9MX1ee259IFxsbiB0X2kgXHF1YWQgXGNkb3RzXGNkb3RzXHF1YWRcdGV4dHsoQil9DQpcZW5ke2FsaWdufQ0KJCQNCg0KKipOdW1lcmljYWwgU29sdXRpb24qKg0KDQpUaGlzIHN5c3RlbSBjYW5ub3QgYmUgc29sdmVkIGFuYWx5dGljYWxseS4gVGhlIHR5cGljYWwgbnVtZXJpY2FsIHByb2NlZHVyZSBpczoNCg0KDQoqIFNvbHZlIGVxdWF0aW9uIChCKSBmb3IgJFxoYXR7a30kIHVzaW5nIG51bWVyaWNhbCBtZXRob2RzIChlLmcuLCBOZXd0b24tUmFwaHNvbiwgYmlzZWN0aW9uKQ0KDQoqIFN1YnN0aXR1dGUgJFxoYXR7a30kIGludG8gZXF1YXRpb24gKEEpIHRvIG9idGFpbiAkXGhhdHtcbGFtYmRhfSQNCg0KDQoNClRoZSByaWdodC1oYW5kIHNpZGUgb2YgZXF1YXRpb24gKDI0KSBpcyBvZnRlbiBkZW5vdGVkIGFzICRnKGspJDoNCg0KJCQNClxiZWdpbntlcXVhdGlvbn0NCiAgICBnKGspID0gXGZyYWN7XHN1bV97aT0xfV57bn0gKHRfaV5rIFxsbiB0X2kpfXtcc3VtX3tpPTF9XntufSB0X2lea30gLSBcZnJhY3sxfXtufSBcc3VtX3tpPTF9XntufSBcbG4gdF9pDQpcZW5ke2VxdWF0aW9ufQ0KJCQNCg0KDQphbmQgd2Ugc29sdmUgJGcoaykgLSAxL2sgPSAwJCBpdGVyYXRpdmVseS4NCg0KDQo8Zm9udCBjb2xvciA9ICJyZWQiPioqV2UgaGF2ZSBwcmFjdGljZWQgc29sdmluZyB0aGlzIHR5cGUgb2YgdW5pdmFyaWF0ZSBub25saW5lYXIgZXF1YXRpb24gaW4gcHJldmlvdXMgYXNzaWdubWVudHMgdXNpbmcgdmFyaW91cyBSIGJ1aWx0LWluIGZ1bmN0aW9ucy4gSW4gdGhlIGZvbGxvd2luZyBzZWN0aW9ucywgd2Ugd2lsbCB1c2UgdGhlIG1vcmUgZ2VuZXJhbC1wdXJwb3NlIGJ1aWx0LWluIGZ1bmN0aW9uIG9wdGltKCkgdG8gc29sdmUgc3lzdGVtcyBvZiBub25saW5lYXIgZXF1YXRpb25zLioqPC9mb250Pg0KDQoNCg0KDQojIE51bWVyaWNhbCBJbXBsZW1lbnRhdGlvbiBVc2luZyBSDQoNCg0KSWYgdGhlIE1MRSBoYXMgYSBjbG9zZWQgZm9ybSwgdGhlIGV2YWx1YXRpb24gb2YgdGhlIE1MRSBpcyBzdHJhaWdodGZvcndhcmQuIElmIHRoZXJlIGlzIG5vIGNsb3NlZCBmb3JtIGZvciB0aGUgTUxFLCB3ZSBuZWVkIHRvIHVzZSBudW1lcmljYWwgcHJvY2VkdXJlcyB0byBhcHByb3hpbWF0ZSBpdC4gV2Ugd2lsbCB1c2UgYSBzaW11bGF0ZWQgV2VpYnVsbCBkaXN0cmlidXRpb24gd2l0aCBhIGdpdmVuIHNoYXBlICgkayA9IDIuNSQpIGFuZCBzY2FsZSAoJFxsYW1iZGEgPSAxLjgkKSBhbmQgdGhlbiBmaW5kIE1MRSBvZiAkayQgYW5kICRcbGFtYmRhJCwgZGVub3RlZCBieSAkXGhhdHtrfSQgYW5kICRcaGF0e1xsYW1iZGF9JC4NCg0KU2luY2UgdGhlIFdlaWJ1bGwgZGlzdHJpYnV0aW9uIGlzIHdpZGVseSB1c2VkIGluIHByYWN0aWNlLCB0aGVyZSBpcyBhIGdlbmVyaWMgYnVpbHQtaW4gUiBmdW5jdGlvbiBgZml0ZGlzdHIoKWAgaW4gdGhlIE1BU1MgbGlicmFyeSBmb3IgZXN0aW1hdGluZyBwYXJhbWV0ZXJzIGluIGRpc3RyaWJ1dGlvbnMgc3VjaCBhcyBgYmV0YWAsIGBjYXVjaHlgLCBgY2hpLXNxdWFyZWRgLCBgZXhwb25lbnRpYWxgLCBgZ2FtbWFgLCBgZ2VvbWV0cmljYCwgYGxvZy1ub3JtYWxgLCBgbG9nbm9ybWFsYCwgYGxvZ2lzdGljYCwgYG5lZ2F0aXZlIGJpbm9taWFsYCwgYG5vcm1hbGAsIGBQb2lzc29uYCwgYHRgIGFuZCBgd2VpYnVsbGAuDQoNCldlIHdpbGwgdXNlIGBmaXRkaXN0cigpYCBhbmQgbWFudWFsIGNhbGN1bGF0aW9uIGlzIGEgZ2VuZXJpYyBgb3B0aW0oKWAgYmFzZWQgb24gdGhlIGRlcml2ZWQgbG9nLWxpa2VsaWhvb2QgYW5kIHNjb3JlIGZ1bmN0aW9ucyB0byBmaW5kIHRoZSBNTEUgb2YgJGskIGFuZCAkXGxhbWJkYSQuDQoNCiMjIEVzdGltYXRpb24gd2l0aCBgZml0ZGlzdHIoKWANCg0KYGZpdGRpc3RyKClgIHVzZXMgIE1heGltdW0tbGlrZWxpaG9vZCBmaXR0aW5nIG9mIHVuaXZhcmlhdGUgZGlzdHJpYnV0aW9ucywgYWxsb3dpbmcgcGFyYW1ldGVycyB0byBiZSBoZWxkIGZpeGVkIGlmIGRlc2lyZWQuDQoNCg0KYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9DQojIEdlbmVyYXRlIHNhbXBsZSBXZWlidWxsIGRhdGENCnNldC5zZWVkKDEyMykNCnRydWUuc2hhcGUgPC0gMi41DQp0cnVlLnNjYWxlIDwtIDEuOA0Kc2FtcGxlLmRhdGEgPC0gcndlaWJ1bGwoMTAwMCwgc2hhcGUgPSB0cnVlLnNoYXBlLCBzY2FsZSA9IHRydWUuc2NhbGUpDQoNCiMgVXNpbmcgZml0ZGlzdHIgZnJvbSBNQVNTIHBhY2thZ2UNCndlaWJ1bGwuZml0Lm1hc3MgPC0gZml0ZGlzdHIoc2FtcGxlLmRhdGEsICJ3ZWlidWxsIikNCiNwcmludCgiTUFTUzo6Zml0ZGlzdHIgcmVzdWx0czoiKQ0KcHJpbnQod2VpYnVsbC5maXQubWFzcykNCmBgYA0KDQoNCioqTm90ZSoqIFIgZnVuY3Rpb24gYGZpdGRpc3QoKWAgaW4gbGlicmFyeSAqKmZpdGRpc3RycGx1cyoqIHBhY2thZ2UgYWxsb3dzIHZhcmlvdXMgZXN0aW1hdGlvbiBtZXRob2RzIGluY2x1ZGluZyBtZXRob2Qgb2YgbW9tZW50IGVzdGltYXRpb24gYW5kIG1heGltdW0gbGlrZWxpaG9vZCBlc3RpbWF0aW9uLg0KDQoNCiMjIE1hbnVhbCBJbXBsZW1lbnRhdGlvbiBvZiBNTEUNCg0KVGhlIGZvbGxvd2luZyBjb2RlIGltcGxlbWVudHMgdGhlIE1MRSBmcm9tIHRoZSBvYmplY3RpdmUgZnVuY3Rpb24gZGlyZWN0bHkuIFRoZSBwcm9jZXNzIG9mIGZpbmRpbmcgdGhlIE1MRSBvZiBwb3B1bGF0aW9uIHBhcmFtZXRlcnMgYXBwbGllcyB0byBhbnkgbmV3IGRpc3RyaWJ1dGlvbnMgdGhhdCBhcmUgbm90IGF2YWlsYWJsZSBpbiBleGlzdGluZyBSIGxpYnJhcmllcy4gVGhlIGtleSBvcHRpbWl6YXRpb24gUiBmdW5jdGlvbiB0byBiZSB1c2VkIGlzIGBvcHRpbSgpYCBpbiB0aGUgYmFzZSBwYWNrYWdlLg0KDQojIyMgT3B0aW1pemF0aW9uIHdpdGggTm8gR3JhZGllbnQNCg0KYGBge3J9DQojIE1hbnVhbCBNTEUgaW1wbGVtZW50YXRpb24gZm9yIFdlaWJ1bGwgZGlzdHJpYnV0aW9uDQojIFdlaWJ1bGwgbG9nLWxpa2VsaWhvb2QgZnVuY3Rpb24NCndlaWJ1bGwubG9nbGlrIDwtIGZ1bmN0aW9uKHBhcmFtcywgZGF0YSkgew0KICBzaGFwZSA8LSBwYXJhbXNbMV0NCiAgc2NhbGUgPC0gcGFyYW1zWzJdDQogIA0KICBuIDwtIGxlbmd0aChkYXRhKQ0KICANCiAgIyBMb2ctbGlrZWxpaG9vZCBmb3IgV2VpYnVsbCBkaXN0cmlidXRpb24NCiAgbG9nbGlrIDwtIG4gKiBsb2coc2hhcGUpIC0gbiAqIHNoYXBlICogbG9nKHNjYWxlKSArIA0KICAgICAgICAgICAgKHNoYXBlIC0gMSkgKiBzdW0obG9nKGRhdGEpKSAtIA0KICAgICAgICAgICAgc3VtKChkYXRhIC8gc2NhbGUpXnNoYXBlKQ0KICANCiAgcmV0dXJuKC1sb2dsaWspICAjIFJldHVybiBuZWdhdGl2ZSBmb3IgbWluaW1pemF0aW9uDQp9DQoNCiMgUGVyZm9ybSBvcHRpbWl6YXRpb24NCmluaXRpYWwucGFyYW1zIDwtIGMoc2hhcGUgPSAyLjIsIHNjYWxlID0gMS41KSAgIyBSZWFzb25hYmxlIHN0YXJ0aW5nIHZhbHVlcw0KDQojIFVzaW5nIG9wdGltIHdpdGggTmVsZGVyLU1lYWQgbWV0aG9kDQptbGUucmVzdWx0IDwtIG9wdGltKA0KICBwYXIgPSBpbml0aWFsLnBhcmFtcywNCiAgZm4gPSB3ZWlidWxsLmxvZ2xpaywNCiAgZGF0YSA9IHNhbXBsZS5kYXRhLA0KICBoZXNzaWFuID0gVFJVRQ0KKQ0KIyMNCm1sZS5yZXN1bHQNCmBgYA0KDQoNCiMjIyBPcHRpbWl6YXRpb24gd2l0aCBHcmFkaWVudA0KDQpgYGB7cn0NCiMgTWFudWFsIE1MRSBpbXBsZW1lbnRhdGlvbiBmb3IgV2VpYnVsbCBkaXN0cmlidXRpb24NCiMgV2VpYnVsbCBsb2ctbGlrZWxpaG9vZCBmdW5jdGlvbg0Kd2VpYnVsbC5sb2dsaWsgPC0gZnVuY3Rpb24ocGFyYW1zLCBkYXRhKSB7DQogIHNoYXBlIDwtIHBhcmFtc1sxXSAgIyBwYXNzaW5nIHRoZSBwYXJhbWV0ZXJzDQogIHNjYWxlIDwtIHBhcmFtc1syXQ0KICAjIw0KICBuIDwtIGxlbmd0aChkYXRhKSAgICMgc2FtcGxlIHNpemUNCiAgDQogICMgTG9nLWxpa2VsaWhvb2QgZm9yIFdlaWJ1bGwgZGlzdHJpYnV0aW9uDQogIGxvZ2xpayA8LSBuICogbG9nKHNoYXBlKSAtIG4gKiBzaGFwZSAqIGxvZyhzY2FsZSkgKyANCiAgICAgICAgICAgIChzaGFwZSAtIDEpICogc3VtKGxvZyhkYXRhKSkgLSANCiAgICAgICAgICAgIHN1bSgoZGF0YSAvIHNjYWxlKV5zaGFwZSkNCiAgDQogIHJldHVybihsb2dsaWspICAgICMgUmV0dXJuIG5lZ2F0aXZlIGZvciBtaW5pbWl6YXRpb24NCn0NCg0KDQojIyBTY29yZSAoZ3JhZGllbnQpIGVxdWF0aW9uDQoNCndlaWJ1bGwuc2NvcmUgPC0gZnVuY3Rpb24ocGFyYW1zLCBkYXRhKSB7DQogIHNoYXBlIDwtIHBhcmFtc1sxXQ0KICBzY2FsZSA8LSBwYXJhbXNbMl0NCiAgbiA8LSBsZW5ndGgoZGF0YSkNCiAgDQogICMgR3JhZGllbnQgZm9yIHNoYXBlIHBhcmFtZXRlcg0KICBncmFkX3NoYXBlIDwtIG4vc2hhcGUgLSBuICogbG9nKHNjYWxlKSArIA0KICAgICAgICAgICAgICAgIHN1bShsb2coZGF0YSkpIC0gDQogICAgICAgICAgICAgICAgc3VtKChkYXRhL3NjYWxlKV5zaGFwZSAqIGxvZyhkYXRhL3NjYWxlKSkNCiAgDQogICMgR3JhZGllbnQgZm9yIHNjYWxlIHBhcmFtZXRlcg0KICBncmFkX3NjYWxlIDwtIC0obiAqIHNoYXBlKS9zY2FsZSArIA0KICAgICAgICAgICAgICAgIChzaGFwZS9zY2FsZSkgKiBzdW0oKGRhdGEvc2NhbGUpXnNoYXBlKQ0KICANCiAgcmV0dXJuKGMoZ3JhZF9zaGFwZSwgZ3JhZF9zY2FsZSkpDQp9DQoNCg0KIyMNCiMgTmVlZCB0byBwcm92aWRlIGluaXRpYWwgdmFsdWVzIGZvciBwYXJhbWV0ZXJzDQppbml0aWFsLnBhcmFtcyA8LSBjKHNoYXBlID0gMi4yLCBzY2FsZSA9IDEuNSkgICMgUmVhc29uYWJsZSBzdGFydGluZyB2YWx1ZXMNCg0KDQojIFVzaW5nIG9wdGltIHdpdGggTmVsZGVyLU1lYWQgbWV0aG9kDQptbGUucmVzdWx0IDwtIG9wdGltKA0KICBwYXIgPSBpbml0aWFsLnBhcmFtcywNCiAgZm4gPSB3ZWlidWxsLmxvZ2xpaywNCiAgZ3IgPSB3ZWlidWxsLnNjb3JlLA0KICBkYXRhID0gc2FtcGxlLmRhdGEsDQogIG1ldGhvZCA9ICJMLUJGR1MtQiIsDQogIGhlc3NpYW4gPSBUUlVFLA0KICBjb250cm9sID0gbGlzdCh0cmFjZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICBmbnNjYWxlID0gLTEsDQogICAgICAgICAgICAgICAgIG1heGl0ID0gNTAwLA0KICAgICAgICAgICAgICAgICBhYnN0b2wgPSAxZS04KQ0KKQ0KIyMNCm1sZS5yZXN1bHQNCmBgYA0KKipSZW1hcmtzKio6IGBvcHRpbSgpYCBpcyBhIHBvd2VyZnVsIG9wdGltaXphdGlvbiBmdW5jdGlvbi4gV2hpbGUgdGhlIHByZXZpb3VzIGltcGxlbWVudGF0aW9uIHJlbGllcyBzb2xlbHkgb24gdGhlIGxvZy1saWtlbGlob29kIGZ1bmN0aW9uLCByZWFsLXdvcmxkIHNjZW5hcmlvcyBvZnRlbiBpbnZvbHZlIGxpa2VsaWhvb2QgZnVuY3Rpb25zIHdpdGggbnVtZXJvdXMgcGFyYW1ldGVycyBhbmQgY29tcGxleCBleHByZXNzaW9ucy4gSW4gc3VjaCBjYXNlcywgZ3JhZGllbnQtYmFzZWQgbWV0aG9kcyBiZWNvbWUgYWR2YW50YWdlb3VzLiBGb3J0dW5hdGVseSwgYG9wdGltKClgIG9mZmVycyBzZXZlcmFsIGdyYWRpZW50LWJhc2VkIG9wdGltaXphdGlvbiBhbGdvcml0aG1zIHRvIGNob29zZSBmcm9tLg0KDQpgYGB7fQ0KIyBNZXRob2RzIHRoYXQgUkVRVUlSRSBncmFkaWVudHM6DQpvcHRpbShwYXIsIGZuLCBnciA9IGdyYWRpZW50X2ZuLCBtZXRob2QgPSAiQkZHUyIpDQpvcHRpbShwYXIsIGZuLCBnciA9IGdyYWRpZW50X2ZuLCBtZXRob2QgPSAiQ0ciKSAgIyBDb25qdWdhdGUgR3JhZGllbnQNCm9wdGltKHBhciwgZm4sIGdyID0gZ3JhZGllbnRfZm4sIG1ldGhvZCA9ICJMLUJGR1MtQiIpDQpgYGANCg0KRXhhbXBsZXMgb2YgaG93IHRvIGltcGxlbWVudCBncmFkaWVudC1iYXNlZCBvcHRpbWl6YXRpb24gY2FuIGJlIGZvdW5kIGluIHRoZSBgb3B0aW0oKWAgaGVscCBkb2N1bWVudGF0aW9uLg0KDQoNCg==