1 General Optimization

In mathematics, nonlinear programming (NLP) is the process of solving an optimization problem where some of the constraints or the objective function are nonlinear. An optimization problem is one of calculation of the extrema (maxima, minima or stationary points) of an objective function over a set of unknown real variables and conditional to the satisfaction of a system of equalities and inequalities, collectively termed constraints. It is the sub-field of mathematical optimization that deals with problems that are not linear.

1.1 Two-dimensional function

The blue region (shown in the diagram below) is the feasible region. The tangency of the line with the feasible region represents the solution. The line is the best achievable contour line (area with a given value of the objective function).

2-dimensional example

2-dimensional example

This simple problem can be defined by the constraints:

\[ \begin{eqnarray} x_1 & \ge & 0 \\ x_2 & \ge & 0 \\ x_1^2 + x_2^2 & \ge & 1 \\ x_1^2 + x_2^2 & \le & 2 \end{eqnarray} \] with an objective function to be maximized

\[ f(x) = x_1 + x_2 , \text{where } x = (x_1, x_2). \]

1.2 Three-dimensional function

The tangency (see diagram bellow) of the top surface with the constrained space in the center represents the solution.

3-dimensional example

3-dimensional example

This simple problem can be defined by the constraints:

\[ \begin{eqnarray} x_1^2 − x_2^2 + x_3^2 \le 2 \\ x_1^2 + x_2^2 + x_3^2 \le 10 \\ \end{eqnarray} \]

with an objective function to be maximized

\[f(x) = x_1x_2 + x_2x_3, \text{where } x = (x_1, x_2, x_3).\]

2 Steepest Descent (SD)

The method of steepest descent works on functions which have a single derivative. It is used most often in problems involving more than 1 variable. The essential idea of steepest descent is that the function decreases most quickly in the direction of the negative gradient. Let’s assume we have the following function:

\[f(x)=f(x_1,x_2,\cdots, x_n)\]

The objective is to find the maximum or minimum value (according to our purpose).

2.1 SD Algorithm

  • The method starts at an initial guess \(x\).
  • The next guess is made by moving in the direction of the negative gradient. The location of the minimum along this line can then be found by using a one-dimensional search algorithm such as golden section search.
  • The nth update is then

\[x_n=x_{n−1}−\alpha f'(x_{n−1})\]

where \(\alpha\) is chosen to minimize the one-dimensional function:

\[g(\alpha)=f(x_{n−1}−\alpha f'(x_{n−1}))\]

In order to use golden section, we need to assume that \(\alpha\) is in an interval. So in this case, we take the interval to be \([0,h]\) where \(h\) is a value that we need to choose.

2.3 SD Algorithm in Visualization

You can see the visualization process of Steepest Descent in the following graphic, or click here to see another example of Steepest descent method, specifically for a quadratic function. more

3-dimensional example

3-dimensional example

2.4 Two-dimensional Example

A simple example of a function of 2 variables to be minimized is

\[f(x)=f(x_1,x_2)={(2−x_1)^2\over 2x_2^2} + {(3−x_1)^2 \over 2x_2^2}+log(x_2)\]

Note that \(x_2\) should be positive, so we might need to protect against negative values of \(x_2\).

  • First : We need functions for both the function and the gradient
  • Second: The location of the minimum along this line can then be found by using a one-dimensional search algorithm such as golden section search.
  • Third: Let’s try a starting value of x=(.1,.1).
## [1] "Warning: Maximum number of iterations reached"
## Minimizer1 Minimizer2 
##  2.4992967  0.7123675

We haven’t converged yet.

  • Fourth: One possibility is to run the procedure again, using the most recent result as our starting guess.
## Minimizer1 Minimizer2 
##  2.5000000  0.7071068
## Minimizer1 Minimizer2 
##  2.5000000  0.7071068

Done. The value has been converged.

2.5 Multivariate Normal

One can use steepest descent to compute the maximum likelihood estimate of the mean in a multivariate Normal density, given a sample of data. However, when the data are highly correlated, as they are in the simulated example below, the log-likelihood surface can be come difficult to optimize. In such cases, a very narrow ridge develops in the log-likelihood that can be difficult for the steepest descent algorithm to navigate.

In the example below, we actually compute the negative log-likelihood because the algorithm is designed to minimize functions.

Note that in the figure above the surface is highly stretched and that the minimum (1,2) lies in the middle of a narrow valley. For the steepest descent algorithm we will start at the point (−5,−2) and track the path of the algorithm.

We can see that the path of the algorthm is rather winding as it traverses the narrow valley. Now, we have fixed the step-length in this case, which is probably not optimal. However, one can still see that the algorithm has some difficulty navigating the surface because the direction of steepest descent does not take one directly towards the minimum ever.

3 The Newton Direction

Given a current best estimate \(x_n\), we can approximate \(f\) with a quadratic ploynomial. For some small \(p\), \[f(x_n+P)\approx f(x_n)+p'f'(x_n)+\frac{1}{2}p'f''(x_n)p.\] If we mninimize the right hand side with respect to \(p\), we obtain \[p_n=f''(x_n)^{-1}[-f'(x_n)]\] Which we can think of as the steepest descent direction “twisted” by the inverse of the Hessian matrix \(f''(x_n)^{-1}\). Newton’s method has a “natural” step length of 1, so that the updating procedure is \[x_{n+1}=x_n-f''(x_n)^{-1}f'(x_n)\].

Newton’s method makes a quadratic approximation to target function \(f\) at each step of the algorithm. This follows the “optimizatiom transfer” principle mentioned earlier, whereby we take a complex function \(f\). replace it with a simpler function \(g\) that is easier to optimize and then optimize the simpler function repeatedly until convergence to the situation.

We can visualize how Newton’s method makes its quadratic approximation to the target function easily in one dimension.

In the figure above, the next iterate, \(x_{n+1}\) is actually further away from the minimum than our previous iterate \(x_n\). The quadratic approximation that Newton’s method makes to \(f\) is not guaranteeed to be good at every point of the function.

This shows an important “feature” of Newton’s method, which is that it is not monotone. The successive iterations that Newton’s method produces are not guaranteed to be improvements in the sense that each iterate us closer to the truth. The tradeoff here is that while Newton’s method is very fast (quadratic convergence), it can be unstable at times. Monotone alrgorithms (like the EM algorithm that will be discussed later) that always produce improvements, are more stable but generally converge at slower rates.

In the next figure, however, we can see that the solution provided by the next approximation, \(x_{n+2}\), is indeed quite close to the true minimum.

It is worth noting that in the rare event that \(f\) is in fact a quadratic polynomial, Newton’s method will converge in a single step because the quadratic approximation that it makes to \(f\) will be exact.

3.0.1 Generelized Linear Models

The generalized linear model is an extenstion of the standard linear model to allow for non-Normal response distributions. The distributions used typically come from an exponential family whose density functions share some common characteristics. With a GLM, we typical present it as \(y_i \sim p(y_i|\mu_i)\), where \(p\) is an exponential family distribution, \(\mathbb{E}[y_i]=\mu_i\), \[g(\mu_i)=x'_i\beta,\]

where \(g\) is a nonlinear lin function, and \(Var(y_i)=V(\mu)\) where \(V\) is a known variance function.

Unlike the standard linear model, the maximum likehood estimate of the parameter vector \(\beta\) cannot be obtained in closed form, so an iterative algorithm must be used to obtain the estimate. The traditional algoritm used is the Fisher scoring algorithm. This algorithm uses a linear approximation to nonlinear link function \(g\), which can be written as \[g(y_i)\approx g(\mu_i)+(y_i-\mu_i)g'(\mu_i).\]

The typical notation of GLMs refers to \(z_i=g(\mu_i)+(y_i-\mu_i)g'(\mu_i)\) as the working response. The Fisher scoring algorithm then works as follows. 1. Start with \(\hat{\mu_i}\), some initial value. 2. Compute \(z_i=g(\mu_i)+(y_i-\mu_i)g'(\mu_i)\). 3. Given the \(n \times 1\) vector of working respinses \(z\) and the \(n \times p\) predictor matrix \(X\) we compute a weighted regression of \(z\) on \(X\) to get \[\beta_n=(X'WX)^{-1}X'Wz\] where \(W\) is a diagonal matrix with diagonal elements \[w_{ii}=[g'(\mu_i)^2V(\mu_i)]^{-1}\]. 4. Given \(\beta_n,\) we can recompute \(\hat{\mu_i}=g^{-1}(x'_i\beta_n)\) and go to 2.

Note that in Step 3 above, the weights are simply the inverses of the variance of \(z_i\), i.e. \[\begin {align*} Var(z_i) = Var(g(\mu)+(y_i-\mu_i)g'(\mu_i))\\ =Var((y_i-\mu_i)g'(\mu_i))\\ V(\mu_i)g'(\mu_i)^2 \end{align*}\]

Naturally, when doing a weighted regression, we would weight by the inverse of the variances.

3.0.1.1 Example: Poisson Regression

For a Poisson regression, we have \(y_i\sim Poisson (\mu_i)\) where \(g(\mu)=log \space \mu_i=x'_i\beta\) because the log is the canonical link function for the Poisson distribution, We also have \(g'(\mu_i)=\frac{1}{\mu_i}\) and \(V(\mu_i)=\mu_i.\) Therefore, the Fisher scoring algorithm is 1. Initialize \(\hat{\mu_i},\) perhaps using \(y_i+1\) (to avoid zeros). 2. Let \(z_i=log \space \hat{\mu_i}+(y_i-\hat{\mu_i})\frac{1}{\hat{\mu_i}}\) 3. Regression \(z\) on \(X\) using the weights \[w_{ii}=[\frac{1}{\hat{\mu_i^2}}\hat{\mu_i}]^{-1}=\hat{\mu_i}.\] Using the Poisson regression example, we can draw a connection between the usual Fisher scoring algorithm for fitting GLMs and Newton’s method. Recall that if \(l(\beta)\) is the log-likelihood as a function of the regression parameters \(\beta\), then the Newton updating scheme is \[\beta_{n+1}=\beta_n+l''(\beta_n)^{-1}[-l'(\beta_n)].\]

The log-likelihppd for a Poiaaon regression model can be written in vector/matrix form as \[l(\beta)=y'X\beta- exp(X\beta)'1\] where the exponential is taked component-wise on the vector \(X\beta\). The gradient function is \[l'(\beta)=X'y-X'exp(X\beta)=X'(y-\mu)\] and the Hessian is \[l''(\beta)=-X'WX\] where \(W\) is a diagonal matrix with the values \(w_{ii}=exp(x'_i\beta)\) on the diagonal. The Newton iteration is then \[\begin {eqnarray*} \beta_{n+1}=\beta_n+(-X'WX)^{-1}(-X'(y-\mu))\\ =\beta_n+(X'WX)^{-1}XW(z-X\beta_n)\\ =(X'WX)^{-1}X'Wz+\beta_n-(X'WX)^{-1}X'WX\beta_n\\ =(X'WX)^{-1}X'Wz \end {eqnarray*}\]

Therefore the iteration is exactly the same as the Fisher scoring algorithm in this case. In general, Newton’s method and Fisher scoring will coincide with any generalized linear model using an exponential family with a canonical link function.

3.0.2 Newton’s Method in R

The nlm() function in R implements Newton’s method for minimizing a function given a vector of stating values. By default, one does not need to supply the gradient or Hessian functions; they will be estimated numerically by the algorithm. However, for the purposes of improving accuracy of the algorithm, both the gradient and Hessian can be supplied as attributes of the target function.

As an example, we will use the nlm() function to fit a simple logistic regression model for binary data. This model specifies that \(y_i \sim \space Bernoulli(p_i)\) where \[log \frac{p_i}{1-p_i}=\beta_0+x_i\beta_1\] and the goal is to estimate \(\beta\) via maximum likelihood. Given the assumed Bernoulli distribution, we can write the log-likelihood for a single observation as

\[\begin{eqnarray*} \log L(\beta) & = & \log\left\{\prod_{i=1}^n p_i^{y_i}(1-p_i)^{1-y_i}\right\}\\ & = & \sum_{i=1}^n y_i\log p_i + (1-y_i)\log(1-p_i)\\ & = & \sum_{i=1}^n y_i\log\frac{p_i}{1-p_i}+\log(1-p_i)\\ & = & \sum_{i=1}^n y_i(\beta_0 + x_i\beta_1) + \log\left(\frac{1}{1+e^{(\beta_0 + x_i\beta_1)}}\right)\\ & = & \sum_{i=1}^n y_i(\beta_0 + x_i\beta_1) -\log\left(1+e^{(\beta_0 + x_i\beta_1)}\right) \end{eqnarray*}\]

If we take the very last line of the above derivation and take a single element inside the sum, we have

\[\ell_i(\beta)=y_i(\beta_0 + x_i\beta_1) -\log\left(1+e^{(\beta_0 + x_i\beta_1)}\right)\]

We will need the gradient and Hessian of this with respect to \(\beta\). Because the sum and the derivative are exchangeable, we can then sum each of the individual gradients and Hessians to get the full gradient and Hessian for the entire sample, so that \[\ell^\prime(\beta) = \sum_{i=1}^n\ell_i^\prime(\beta)\] and \[\ell^\prime(\beta) = \sum_{i=1}^n\ell_i^{\prime\prime}(\beta)\] Now, taking the gradient and Hessian of the above expression may be mildly inconvenient, but it is far from impossible. Nevertheless, R provides an automated way to do symbolic differentiation so that manual work can be avoided. The deriv() function computes the gradient and Hessian of an expression symbolically so that it can be used in minimization routines. It cannot compute gradients of arbitrary expressions, but it it does support a wide range of common statistical functions.

4 Quasi-Newton

5 Conjugate Gradient

6 Coordinate Descent

LS0tDQp0aXRsZTogIkxhYjU6IEdlbmVyYWwgT3B0aW1pemF0aW9uIg0KYXV0aG9yOiAiSmVycmVsIg0KZGF0ZTogImByIGZvcm1hdChTeXMuRGF0ZSgpLCAnJUIgJWQsICVZJylgIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDogDQogICAgaGlnaGxpZ2h0OiBtb25vY2hyb21lDQogICAgdGhlbWU6IHNwYWNlbGFiDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQotLS0NCg0KYGBge3IgTG9nbywgZWNobz1GQUxTRSxmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aCA9ICc0MCUnfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImh0dHBzOi8vZ2l0aHViLmNvbS9CYWt0aS1TaXJlZ2FyL2ltYWdlcy9ibG9iL21hc3Rlci9sb2dvLnBuZz9yYXc9dHJ1ZSIpDQpgYGANCg0KIyBHZW5lcmFsIE9wdGltaXphdGlvbg0KDQpJbiBtYXRoZW1hdGljcywgbm9ubGluZWFyIHByb2dyYW1taW5nIChOTFApIGlzIHRoZSBwcm9jZXNzIG9mIHNvbHZpbmcgYW4gb3B0aW1pemF0aW9uIHByb2JsZW0gd2hlcmUgc29tZSBvZiB0aGUgY29uc3RyYWludHMgb3IgdGhlIG9iamVjdGl2ZSBmdW5jdGlvbiBhcmUgbm9ubGluZWFyLiBBbiBvcHRpbWl6YXRpb24gcHJvYmxlbSBpcyBvbmUgb2YgY2FsY3VsYXRpb24gb2YgdGhlIGV4dHJlbWEgKG1heGltYSwgbWluaW1hIG9yIHN0YXRpb25hcnkgcG9pbnRzKSBvZiBhbiBvYmplY3RpdmUgZnVuY3Rpb24gb3ZlciBhIHNldCBvZiB1bmtub3duIHJlYWwgdmFyaWFibGVzIGFuZCBjb25kaXRpb25hbCB0byB0aGUgc2F0aXNmYWN0aW9uIG9mIGEgc3lzdGVtIG9mIGVxdWFsaXRpZXMgYW5kIGluZXF1YWxpdGllcywgY29sbGVjdGl2ZWx5IHRlcm1lZCBjb25zdHJhaW50cy4gSXQgaXMgdGhlIHN1Yi1maWVsZCBvZiBtYXRoZW1hdGljYWwgb3B0aW1pemF0aW9uIHRoYXQgZGVhbHMgd2l0aCBwcm9ibGVtcyB0aGF0IGFyZSBub3QgbGluZWFyLiANCg0KIyMgVHdvLWRpbWVuc2lvbmFsIGZ1bmN0aW9uDQoNClRoZSBibHVlIHJlZ2lvbiAoc2hvd24gaW4gdGhlIGRpYWdyYW0gYmVsb3cpIGlzIHRoZSBmZWFzaWJsZSByZWdpb24uIFRoZSB0YW5nZW5jeSBvZiB0aGUgbGluZSB3aXRoIHRoZSBmZWFzaWJsZSByZWdpb24gcmVwcmVzZW50cyB0aGUgc29sdXRpb24uIFRoZSBsaW5lIGlzIHRoZSBiZXN0IGFjaGlldmFibGUgY29udG91ciBsaW5lIChhcmVhIHdpdGggYSBnaXZlbiB2YWx1ZSBvZiB0aGUgb2JqZWN0aXZlIGZ1bmN0aW9uKS4gDQoNCmBgYHtyLCBlY2hvPUZBTFNFLGZpZy5hbGlnbj0nY2VudGVyJyxmaWcuY2FwPSIyLWRpbWVuc2lvbmFsIGV4YW1wbGUiLCBvdXQud2lkdGggPSAnNTAlJ30NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vQmFrdGktU2lyZWdhci9pbWFnZXMvbWFzdGVyL05vbmxpbmVhcl9wcm9ncmFtbWluZy5zdmciKQ0KYGBgDQoNClRoaXMgc2ltcGxlIHByb2JsZW0gY2FuIGJlIGRlZmluZWQgYnkgdGhlIGNvbnN0cmFpbnRzOg0KDQokJA0KXGJlZ2lue2VxbmFycmF5fQ0KeF8xICYgXGdlICYgMCBcXA0KeF8yICYgXGdlICYgMCBcXA0KeF8xXjIgKyB4XzJeMiAmIFxnZSAmIDEgXFwNCnhfMV4yICsgeF8yXjIgJiBcbGUgJiAyDQpcZW5ke2VxbmFycmF5fQ0KJCQNCndpdGggYW4gb2JqZWN0aXZlIGZ1bmN0aW9uIHRvIGJlIG1heGltaXplZA0KDQokJA0KZih4KSA9IHhfMSArIHhfMiAsIFx0ZXh0e3doZXJlIH0geCA9ICh4XzEsIHhfMikuDQokJA0KDQojIyBUaHJlZS1kaW1lbnNpb25hbCBmdW5jdGlvbg0KDQpUaGUgdGFuZ2VuY3kgKHNlZSBkaWFncmFtIGJlbGxvdykgb2YgdGhlIHRvcCBzdXJmYWNlIHdpdGggdGhlIGNvbnN0cmFpbmVkIHNwYWNlIGluIHRoZSBjZW50ZXIgcmVwcmVzZW50cyB0aGUgc29sdXRpb24uDQoNCmBgYHtyLCBlY2hvPUZBTFNFLGZpZy5hbGlnbj0nY2VudGVyJyxmaWcuY2FwPSIzLWRpbWVuc2lvbmFsIGV4YW1wbGUiLCBvdXQud2lkdGggPSAnNTAlJ30NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vQmFrdGktU2lyZWdhci9pbWFnZXMvbWFzdGVyL05vbmxpbmVhcl9wcm9ncmFtbWluZ18zRC5zdmciKQ0KYGBgDQoNClRoaXMgc2ltcGxlIHByb2JsZW0gY2FuIGJlIGRlZmluZWQgYnkgdGhlIGNvbnN0cmFpbnRzOg0KDQokJA0KXGJlZ2lue2VxbmFycmF5fQ0KeF8xXjIg4oiSIHhfMl4yICsgeF8zXjIgXGxlIDIgXFwNCnhfMV4yICsgeF8yXjIgKyB4XzNeMiBcbGUgMTAgXFwNClxlbmR7ZXFuYXJyYXl9DQokJA0KDQp3aXRoIGFuIG9iamVjdGl2ZSBmdW5jdGlvbiB0byBiZSBtYXhpbWl6ZWQNCg0KJCRmKHgpID0geF8xeF8yICsgeF8yeF8zLCBcdGV4dHt3aGVyZSB9IHggPSAoeF8xLCB4XzIsIHhfMykuJCQNCg0KDQojIFN0ZWVwZXN0IERlc2NlbnQgKFNEKQ0KDQpUaGUgbWV0aG9kIG9mIHN0ZWVwZXN0IGRlc2NlbnQgd29ya3Mgb24gZnVuY3Rpb25zIHdoaWNoIGhhdmUgYSBzaW5nbGUgZGVyaXZhdGl2ZS4gSXQgaXMgdXNlZCBtb3N0IG9mdGVuIGluIHByb2JsZW1zIGludm9sdmluZyBtb3JlIHRoYW4gMSB2YXJpYWJsZS4gVGhlIGVzc2VudGlhbCBpZGVhIG9mIHN0ZWVwZXN0IGRlc2NlbnQgaXMgdGhhdCB0aGUgZnVuY3Rpb24gZGVjcmVhc2VzIG1vc3QgcXVpY2tseSBpbiB0aGUgZGlyZWN0aW9uIG9mIHRoZSBuZWdhdGl2ZSBncmFkaWVudC4gTGV0J3MgYXNzdW1lIHdlIGhhdmUgdGhlIGZvbGxvd2luZyBmdW5jdGlvbjoNCg0KJCRmKHgpPWYoeF8xLHhfMixcY2RvdHMsIHhfbikkJA0KDQpUaGUgb2JqZWN0aXZlIGlzIHRvIGZpbmQgdGhlIG1heGltdW0gb3IgbWluaW11bSB2YWx1ZSAoYWNjb3JkaW5nIHRvIG91ciBwdXJwb3NlKS4NCg0KDQojIyBTRCBBbGdvcml0aG0NCg0KKiBUaGUgbWV0aG9kIHN0YXJ0cyBhdCBhbiBpbml0aWFsIGd1ZXNzICR4JC4NCiogVGhlIG5leHQgZ3Vlc3MgaXMgbWFkZSBieSBtb3ZpbmcgaW4gdGhlIGRpcmVjdGlvbiBvZiB0aGUgbmVnYXRpdmUgZ3JhZGllbnQuIFRoZSBsb2NhdGlvbiBvZiB0aGUgbWluaW11bSBhbG9uZyB0aGlzIGxpbmUgY2FuIHRoZW4gYmUgZm91bmQgYnkgdXNpbmcgYSBvbmUtZGltZW5zaW9uYWwgc2VhcmNoIGFsZ29yaXRobSBzdWNoIGFzIGdvbGRlbiBzZWN0aW9uIHNlYXJjaC4NCiogVGhlIG50aCB1cGRhdGUgaXMgdGhlbg0KDQokJHhfbj14X3tu4oiSMX3iiJJcYWxwaGEgZicoeF97buKIkjF9KSQkDQoNCndoZXJlICRcYWxwaGEkIGlzIGNob3NlbiB0byBtaW5pbWl6ZSB0aGUgb25lLWRpbWVuc2lvbmFsIGZ1bmN0aW9uOg0KDQokJGcoXGFscGhhKT1mKHhfe27iiJIxfeKIklxhbHBoYSBmJyh4X3tu4oiSMX0pKSQkDQoNCkluIG9yZGVyIHRvIHVzZSBnb2xkZW4gc2VjdGlvbiwgd2UgbmVlZCB0byBhc3N1bWUgdGhhdCAkXGFscGhhJCBpcyBpbiBhbiBpbnRlcnZhbC4gU28gaW4gdGhpcyBjYXNlLCB3ZSB0YWtlIHRoZSBpbnRlcnZhbCB0byBiZSAkWzAsaF0kIHdoZXJlICRoJCBpcyBhIHZhbHVlIHRoYXQgd2UgbmVlZCB0byBjaG9vc2UuDQoNCg0KIyMgU0QgQWxnb3JpdGhtIGluIFINCg0KYGBge3IgZWNobz1UUlVFfQ0Kc3RlZXBlc3RkZXNjZW50IDwtIGZ1bmN0aW9uKGYsIGZwcmltZSwgc3RhcnQsIGgsDQogdG9sPTFlLTcsIG1heGl0ZXI9MTAwKSB7DQogeCA8LSBzdGFydA0KIGcgPC0gZnVuY3Rpb24oYWxwaGEpIHsgZih4IC0gYWxwaGEqZnB4KSB9DQogbml0ZXIgPC0gMA0KIHdoaWxlKG5pdGVyIDwgbWF4aXRlciAmIHN1bShhYnMoZnByaW1lKHgpKSkgPiB0b2wpIHsNCiBmcHggPC0gZnByaW1lKHgpDQogYWxwaGEgPC0gZ29sZGVuKGcsIDAsIGgpDQogeCA8LSB4IC0gYWxwaGEqZnB4DQogbml0ZXIgPC0gbml0ZXIgKyAxDQogfQ0KIGlmIChuaXRlciA9PSBtYXhpdGVyKSB7DQogcHJpbnQoIldhcm5pbmc6IE1heGltdW0gbnVtYmVyIG9mIGl0ZXJhdGlvbnMgcmVhY2hlZCIpDQogfQ0KIGMoIk1pbmltaXplciIgPSB4KQ0KIH0NCmBgYA0KDQoNCiMjIFNEIEFsZ29yaXRobSBpbiBWaXN1YWxpemF0aW9uDQoNCllvdSBjYW4gc2VlIHRoZSB2aXN1YWxpemF0aW9uIHByb2Nlc3Mgb2YgU3RlZXBlc3QgRGVzY2VudCBpbiB0aGUgZm9sbG93aW5nIGdyYXBoaWMsIG9yIGNsaWNrIFtoZXJlXShodHRwczovL3d3dy5nZW9nZWJyYS5vcmcvY2xhc3NpYy9lcTdydXVxcCkgdG8gc2VlIGFub3RoZXIgZXhhbXBsZSBvZiBTdGVlcGVzdCBkZXNjZW50IG1ldGhvZCwgc3BlY2lmaWNhbGx5IGZvciBhIHF1YWRyYXRpYyBmdW5jdGlvbi4gW21vcmVdKGh0dHBzOi8vd3d3LjEyMDAwLm9yZy9teV9ub3Rlcy9hbmltYXRlX3NlYXJjaC9pbnN1Mi5odG0pDQoNCmBgYHtyLCBlY2hvPUZBTFNFLGZpZy5hbGlnbj0nY2VudGVyJyxmaWcuY2FwPSIzLWRpbWVuc2lvbmFsIGV4YW1wbGUiLCBvdXQud2lkdGggPSAnODAlJ30NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJodHRwczovL2dpdGh1Yi5jb20vQmFrdGktU2lyZWdhci9pbWFnZXMvYmxvYi9tYXN0ZXIvU3RlZXBlc3QtZGVzY2VudC5naWY/cmF3PXRydWUiKQ0KYGBgDQoNCiMjIFR3by1kaW1lbnNpb25hbCBFeGFtcGxlDQoNCkEgc2ltcGxlIGV4YW1wbGUgb2YgYSBmdW5jdGlvbiBvZiAyIHZhcmlhYmxlcyB0byBiZSBtaW5pbWl6ZWQgaXMNCg0KJCRmKHgpPWYoeF8xLHhfMik9eygy4oiSeF8xKV4yXG92ZXIgMnhfMl4yfSArIHsoM+KIknhfMSleMiBcb3ZlciAyeF8yXjJ9K2xvZyh4XzIpJCQNCg0KTm90ZSB0aGF0ICR4XzIkIHNob3VsZCBiZSBwb3NpdGl2ZSwgc28gd2UgbWlnaHQgbmVlZCB0byBwcm90ZWN0IGFnYWluc3QgbmVnYXRpdmUgdmFsdWVzIG9mICR4XzIkLg0KDQoqIEZpcnN0IDogV2UgbmVlZCBmdW5jdGlvbnMgZm9yIGJvdGggdGhlIGZ1bmN0aW9uIGFuZCB0aGUgZ3JhZGllbnQNCg0KYGBge3IsIGVjaG89VFJVRX0NCmYxIDwtIGZ1bmN0aW9uKHgpIHsNCiAoMi14WzFdKV4yLygyKnhbMl1eMikgKygzLXhbMV0pXjIvKDIqeFsyXV4yKSArIGxvZyh4WzJdKQ0KIH0NCiBmMXByaW1lIDwtIGZ1bmN0aW9uKHgpIHsNCiBjKC0oMi14WzFdKS94WzJdXjIgLSAoMy14WzFdKS94WzJdXjIsLSgyLXhbMV0pXjIveFsyXV4zIC0oMy14WzFdKV4yL3hbMl1eMyArIDEveFsyXSkNCiB9DQpgYGANCg0KDQoqIFNlY29uZDogVGhlIGxvY2F0aW9uIG9mIHRoZSBtaW5pbXVtIGFsb25nIHRoaXMgbGluZSBjYW4gdGhlbiBiZSBmb3VuZCBieSB1c2luZyBhIG9uZS1kaW1lbnNpb25hbCBzZWFyY2ggYWxnb3JpdGhtIHN1Y2ggYXMgZ29sZGVuIHNlY3Rpb24gc2VhcmNoLg0KDQpgYGB7cn0NCmdvbGRlbiA8LSBmdW5jdGlvbiAoZiwgYSwgYiwgdG9sID0gMC4wMDAwMDAxKQ0KIHsNCiByYXRpbyA8LSAyIC8gKHNxcnQoNSkgKyAxKQ0KIHgxIDwtIGIgLSByYXRpbyAqIChiIC0gYSkNCiB4MiA8LSBhICsgcmF0aW8gKiAoYiAtIGEpDQogZjEgPC0gZih4MSkNCiBmMiA8LSBmKHgyKQ0KDQogd2hpbGUoYWJzKGIgLSBhKSA+IHRvbCkgew0KIGlmIChmMiA+IGYxKSB7DQogYiA8LSB4Mg0KIHgyIDwtIHgxDQogZjIgPC0gZjENCiB4MSA8LSBiIC0gcmF0aW8gKiAoYiAtIGEpDQogZjEgPC0gZih4MSkNCiB9IGVsc2Ugew0KIGEgPC0geDENCiB4MSA8LSB4Mg0KIGYxIDwtIGYyDQogeDIgPC0gYSArIHJhdGlvICogKGIgLSBhKQ0KIGYyIDwtIGYoeDIpDQogfQ0KIH0NCiByZXR1cm4oKGEgKyBiKSAvIDIpDQogfQ0KYGBgDQoNCg0KDQoqIFRoaXJkOiBMZXQncyB0cnkgYSBzdGFydGluZyB2YWx1ZSBvZiB4PSguMSwuMSkuDQoNCmBgYHtyfQ0Kc3RlZXBlc3RkZXNjZW50KGYxLCBmMXByaW1lLCBzdGFydD1jKC4xLC4xKSwgaD0uMSkNCmBgYA0KDQpXZSBoYXZlbid0IGNvbnZlcmdlZCB5ZXQuDQoNCg0KKiBGb3VydGg6IE9uZSBwb3NzaWJpbGl0eSBpcyB0byBydW4gdGhlIHByb2NlZHVyZSBhZ2FpbiwgdXNpbmcgdGhlIG1vc3QgcmVjZW50IHJlc3VsdCBhcyBvdXIgc3RhcnRpbmcgZ3Vlc3MuDQoNCmBgYHtyfQ0Kc3RlZXBlc3RkZXNjZW50KGYxLCBmMXByaW1lLHN0YXJ0PWMoMi40OTkyOTY3LCAwLjcxMjM2NzUpLCBoPS4xKQ0KYGBgDQoNCmBgYHtyfQ0Kc3RlZXBlc3RkZXNjZW50KGYxLCBmMXByaW1lLCBzdGFydD1jKC4xLCAuMSksIGg9LjEsbWF4aXRlcj0yMDApDQpgYGANCg0KRG9uZS4gVGhlIHZhbHVlIGhhcyBiZWVuIGNvbnZlcmdlZC4NCg0KIyMgTXVsdGl2YXJpYXRlIE5vcm1hbCANCg0KT25lIGNhbiB1c2Ugc3RlZXBlc3QgZGVzY2VudCB0byBjb21wdXRlIHRoZSBtYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdGUgb2YgdGhlIG1lYW4gaW4gYSBtdWx0aXZhcmlhdGUgTm9ybWFsIGRlbnNpdHksIGdpdmVuIGEgc2FtcGxlIG9mIGRhdGEuIEhvd2V2ZXIsIHdoZW4gdGhlIGRhdGEgYXJlIGhpZ2hseSBjb3JyZWxhdGVkLCBhcyB0aGV5IGFyZSBpbiB0aGUgc2ltdWxhdGVkIGV4YW1wbGUgYmVsb3csIHRoZSBsb2ctbGlrZWxpaG9vZCBzdXJmYWNlIGNhbiBiZSBjb21lIGRpZmZpY3VsdCB0byBvcHRpbWl6ZS4gSW4gc3VjaCBjYXNlcywgYSB2ZXJ5IG5hcnJvdyByaWRnZSBkZXZlbG9wcyBpbiB0aGUgbG9nLWxpa2VsaWhvb2QgdGhhdCBjYW4gYmUgZGlmZmljdWx0IGZvciB0aGUgc3RlZXBlc3QgZGVzY2VudCBhbGdvcml0aG0gdG8gbmF2aWdhdGUuDQoNCkluIHRoZSBleGFtcGxlIGJlbG93LCB3ZSBhY3R1YWxseSBjb21wdXRlIHRoZSBuZWdhdGl2ZSBsb2ctbGlrZWxpaG9vZCBiZWNhdXNlIHRoZSBhbGdvcml0aG0gaXMgZGVzaWduZWQgdG8gbWluaW1pemUgZnVuY3Rpb25zLg0KDQpgYGB7cn0NCnNldC5zZWVkKDIwMjAtMDktMzApDQptdSA8LSBjKDEsIDIpDQpTIDwtIHJiaW5kKGMoMSwgLjkpLCBjKC45LCAxKSkNCnggPC0gTUFTUzo6bXZybm9ybSg1MDAsIG11LCBTKQ0KbmxvZ2xpa2UgPC0gZnVuY3Rpb24obXUxLCBtdTIpIHsNCiAgICAgICAgZG12IDwtIG12dG5vcm06OmRtdm5vcm0oeCwgYyhtdTEsIG11MiksIFMsIGxvZyA9IFRSVUUpDQogICAgICAgIC1zdW0oZG12KQ0KfQ0KbmxvZ2xpa2UgPC0gVmVjdG9yaXplKG5sb2dsaWtlLCBjKCJtdTEiLCAibXUyIikpDQpueCA8LSA0MA0KbnkgPC0gNDANCnhnIDwtIHNlcSgtNSwgNSwgbGVuID0gbngpDQp5ZyA8LSBzZXEoLTUsIDYsIGxlbiA9IG55KQ0KZyA8LSBleHBhbmQuZ3JpZCh4ZywgeWcpDQpuTEwgPC0gbmxvZ2xpa2UoZ1ssIDFdLCBnWywgMl0pDQp6IDwtIG1hdHJpeChuTEwsIG54LCBueSkNCnBhcihtYXIgPSBjKDQuNSwgNC41LCAxLCAxKSkNCmNvbnRvdXIoeGcsIHlnLCB6LCBubGV2ZWxzID0gNDAsIHhsYWIgPSBleHByZXNzaW9uKG11WzFdKSwgDQogICAgICAgIHlsYWIgPSBleHByZXNzaW9uKG11WzJdKSkNCmFibGluZShoID0gMCwgdiA9IDAsIGx0eSA9IDIpDQpgYGANCg0KTm90ZSB0aGF0IGluIHRoZSBmaWd1cmUgYWJvdmUgdGhlIHN1cmZhY2UgaXMgaGlnaGx5IHN0cmV0Y2hlZCBhbmQgdGhhdCB0aGUgbWluaW11bSAoMSwyKSBsaWVzIGluIHRoZSBtaWRkbGUgb2YgYSBuYXJyb3cgdmFsbGV5LiBGb3IgdGhlIHN0ZWVwZXN0IGRlc2NlbnQgYWxnb3JpdGhtIHdlIHdpbGwgc3RhcnQgYXQgdGhlIHBvaW50ICjiiJI1LOKIkjIpIGFuZCB0cmFjayB0aGUgcGF0aCBvZiB0aGUgYWxnb3JpdGhtLg0KICANCmBgYHtyfQ0KbGlicmFyeShkcGx5ciwgd2Fybi5jb25mbGljdHMgPSBGQUxTRSkNCm5vcm0gPC0gZnVuY3Rpb24oeCkgeCAvIHNxcnQoc3VtKHheMikpDQpTaW52IDwtIHNvbHZlKFMpICAgICAgICAgICAgICAgICAgICAgICAgICAjIyBJIGtub3cgSSBzYWlkIG5vdCB0byBkbyB0aGlzIQ0Kc3RlcDEgPC0gZnVuY3Rpb24obXUsIGFscGhhID0gMSkgew0KICAgICAgICBEIDwtIHN3ZWVwKHgsIDIsIG11LCAiLSIpDQogICAgICAgIHNjb3JlIDwtIGNvbFN1bXMoRCkgJT4lIG5vcm0NCiAgICAgICAgbXUgKyBhbHBoYSAqIGRyb3AoU2ludiAlKiUgc2NvcmUpDQp9DQpzdGVlcCA8LSBmdW5jdGlvbihtdSwgbiA9IDEwLCAuLi4pIHsNCiAgICAgICAgcmVzdWx0cyA8LSB2ZWN0b3IoImxpc3QiLCBsZW5ndGggPSBuKQ0KICAgICAgICBmb3IoaSBpbiBzZXFfbGVuKG4pKSB7DQogICAgICAgICAgICAgICAgcmVzdWx0c1tbaV1dIDwtIHN0ZXAxKG11LCAuLi4pDQogICAgICAgICAgICAgICAgbXUgPC0gcmVzdWx0c1tbaV1dDQogICAgICAgIH0NCiAgICAgICAgcmVzdWx0cw0KfQ0KbSA8LSBkby5jYWxsKCJyYmluZCIsIHN0ZWVwKGMoLTUsIC0yKSwgOCkpDQptIDwtIHJiaW5kKGMoLTUsIC0yKSwgbSkNCg0KcGFyKG1hciA9IGMoNC41LCA0LjUsIDEsIDEpKQ0KY29udG91cih4ZywgeWcsIHosIG5sZXZlbHMgPSA0MCwgeGxhYiA9IGV4cHJlc3Npb24obXVbMV0pLCANCiAgICAgICAgeWxhYiA9IGV4cHJlc3Npb24obXVbMl0pKQ0KYWJsaW5lKGggPSAwLCB2ID0gMCwgbHR5ID0gMikNCnBvaW50cyhtLCBwY2ggPSAyMCwgdHlwZSA9ICJiIikNCmBgYA0KDQogIA0KV2UgY2FuIHNlZSB0aGF0IHRoZSBwYXRoIG9mIHRoZSBhbGdvcnRobSBpcyByYXRoZXIgd2luZGluZyBhcyBpdCB0cmF2ZXJzZXMgdGhlIG5hcnJvdyB2YWxsZXkuIE5vdywgd2UgaGF2ZSBmaXhlZCB0aGUgc3RlcC1sZW5ndGggaW4gdGhpcyBjYXNlLCB3aGljaCBpcyBwcm9iYWJseSBub3Qgb3B0aW1hbC4gSG93ZXZlciwgb25lIGNhbiBzdGlsbCBzZWUgdGhhdCB0aGUgYWxnb3JpdGhtIGhhcyBzb21lIGRpZmZpY3VsdHkgbmF2aWdhdGluZyB0aGUgc3VyZmFjZSBiZWNhdXNlIHRoZSBkaXJlY3Rpb24gb2Ygc3RlZXBlc3QgZGVzY2VudCBkb2VzIG5vdCB0YWtlIG9uZSBkaXJlY3RseSB0b3dhcmRzIHRoZSBtaW5pbXVtIGV2ZXIuDQoNCg0KIyBUaGUgTmV3dG9uIERpcmVjdGlvbg0KDQpHaXZlbiBhIGN1cnJlbnQgYmVzdCBlc3RpbWF0ZSAkeF9uJCwgd2UgY2FuIGFwcHJveGltYXRlICRmJCB3aXRoIGEgcXVhZHJhdGljIHBsb3lub21pYWwuIEZvciBzb21lIHNtYWxsICRwJCwNCiQkZih4X24rUClcYXBwcm94IGYoeF9uKStwJ2YnKHhfbikrXGZyYWN7MX17Mn1wJ2YnJyh4X24pcC4kJA0KSWYgd2UgbW5pbmltaXplIHRoZSByaWdodCBoYW5kIHNpZGUgd2l0aCByZXNwZWN0IHRvICRwJCwgd2Ugb2J0YWluDQokJHBfbj1mJycoeF9uKV57LTF9Wy1mJyh4X24pXSQkDQpXaGljaCB3ZSBjYW4gdGhpbmsgb2YgYXMgdGhlIHN0ZWVwZXN0IGRlc2NlbnQgZGlyZWN0aW9uICJ0d2lzdGVkIiBieSB0aGUgaW52ZXJzZSBvZiB0aGUgSGVzc2lhbiBtYXRyaXggJGYnJyh4X24pXnstMX0kLiBOZXd0b24ncyBtZXRob2QgaGFzIGEgIm5hdHVyYWwiIHN0ZXAgbGVuZ3RoIG9mIDEsIHNvIHRoYXQgdGhlIHVwZGF0aW5nIHByb2NlZHVyZSBpcw0KJCR4X3tuKzF9PXhfbi1mJycoeF9uKV57LTF9ZicoeF9uKSQkLg0KDQpOZXd0b24ncyBtZXRob2QgbWFrZXMgYSBxdWFkcmF0aWMgYXBwcm94aW1hdGlvbiB0byB0YXJnZXQgZnVuY3Rpb24gJGYkIGF0IGVhY2ggc3RlcCBvZiB0aGUgYWxnb3JpdGhtLiBUaGlzIGZvbGxvd3MgdGhlICJvcHRpbWl6YXRpb20gdHJhbnNmZXIiIHByaW5jaXBsZSBtZW50aW9uZWQgZWFybGllciwgd2hlcmVieSB3ZSB0YWtlIGEgY29tcGxleCBmdW5jdGlvbiAkZiQuIHJlcGxhY2UgaXQgd2l0aCBhIHNpbXBsZXIgZnVuY3Rpb24gJGckIHRoYXQgaXMgZWFzaWVyIHRvIG9wdGltaXplIGFuZCB0aGVuIG9wdGltaXplIHRoZSBzaW1wbGVyIGZ1bmN0aW9uIHJlcGVhdGVkbHkgdW50aWwgY29udmVyZ2VuY2UgdG8gdGhlIHNpdHVhdGlvbi4NCg0KV2UgY2FuIHZpc3VhbGl6ZSBob3cgTmV3dG9uJ3MgbWV0aG9kIG1ha2VzIGl0cyBxdWFkcmF0aWMgYXBwcm94aW1hdGlvbiB0byB0aGUgdGFyZ2V0IGZ1bmN0aW9uIGVhc2lseSBpbiBvbmUgZGltZW5zaW9uLg0KDQpgYGB7cn0NCmN1cnZlKC1kbm9ybSh4KSwgLTIsIDMsIGx3ZCA9IDIsIHlsaW0gPSBjKC0wLjU1LCAuMSkpDQp4biAgICA8LSAtMS4yDQphYmxpbmUodiA9IHhuLCBsdHkgPSAyKQ0KYXhpcygzLCB4biwgZXhwcmVzc2lvbih4W25dKSkNCmcgICAgIDwtIGZ1bmN0aW9uKHgpIHsNCiAgICAgICAgLWRub3JtKHhuKSArICh4LXhuKSAqIHhuICogZG5vcm0oeG4pIC0gMC41ICogICAgICAgICAgICAgICAgKHgteG4pXjIgKiAoZG5vcm0oeG4pIC0geG4gKiAoeG4gKiBkbm9ybSh4bikpKQ0KfQ0KY3VydmUoZywgLTIsIDMsIGFkZCA9IFRSVUUsIGNvbCA9IDQpDQpvcCAgICA8LSBvcHRpbWl6ZShnLCBjKDAsIDMpKQ0KYWJsaW5lKHYgPSBvcCRtaW5pbXVtLCBsdHkgPSAyKQ0KYXhpcygzLCBvcCRtaW5pbXVtLCBleHByZXNzaW9uKHhbbisxXSkpDQpgYGANCg0KSW4gdGhlIGZpZ3VyZSBhYm92ZSwgdGhlIG5leHQgaXRlcmF0ZSwgJHhfe24rMX0kIGlzIGFjdHVhbGx5IGZ1cnRoZXIgYXdheSBmcm9tIHRoZSBtaW5pbXVtIHRoYW4gb3VyIHByZXZpb3VzIGl0ZXJhdGUgJHhfbiQuIFRoZSBxdWFkcmF0aWMgYXBwcm94aW1hdGlvbiB0aGF0IE5ld3RvbidzIG1ldGhvZCBtYWtlcyB0byAkZiQgaXMgbm90IGd1YXJhbnRlZWVkIHRvIGJlIGdvb2QgYXQgZXZlcnkgcG9pbnQgb2YgdGhlIGZ1bmN0aW9uLg0KDQpUaGlzIHNob3dzIGFuIGltcG9ydGFudCAiZmVhdHVyZSIgb2YgTmV3dG9uJ3MgbWV0aG9kLCB3aGljaCBpcyB0aGF0IGl0IGlzIG5vdCAqbW9ub3RvbmUqLiBUaGUgc3VjY2Vzc2l2ZSBpdGVyYXRpb25zIHRoYXQgTmV3dG9uJ3MgbWV0aG9kIHByb2R1Y2VzIGFyZSBub3QgZ3VhcmFudGVlZCB0byBiZSBpbXByb3ZlbWVudHMgaW4gdGhlIHNlbnNlIHRoYXQgZWFjaCBpdGVyYXRlIHVzIGNsb3NlciB0byB0aGUgdHJ1dGguIFRoZSB0cmFkZW9mZiBoZXJlIGlzIHRoYXQgd2hpbGUgTmV3dG9uJ3MgbWV0aG9kIGlzIHZlcnkgZmFzdCAocXVhZHJhdGljIGNvbnZlcmdlbmNlKSwgaXQgY2FuIGJlIHVuc3RhYmxlIGF0IHRpbWVzLiBNb25vdG9uZSBhbHJnb3JpdGhtcyAobGlrZSB0aGUgRU0gYWxnb3JpdGhtIHRoYXQgd2lsbCBiZSBkaXNjdXNzZWQgbGF0ZXIpIHRoYXQgYWx3YXlzIHByb2R1Y2UgaW1wcm92ZW1lbnRzLCBhcmUgbW9yZSBzdGFibGUgYnV0IGdlbmVyYWxseSBjb252ZXJnZSBhdCBzbG93ZXIgcmF0ZXMuDQoNCkluIHRoZSBuZXh0IGZpZ3VyZSwgaG93ZXZlciwgd2UgY2FuIHNlZSB0aGF0IHRoZSBzb2x1dGlvbiBwcm92aWRlZCBieSB0aGUgbmV4dCBhcHByb3hpbWF0aW9uLCAkeF97bisyfSQsIGlzIGluZGVlZCBxdWl0ZSBjbG9zZSB0byB0aGUgdHJ1ZSBtaW5pbXVtLg0KDQpgYGB7cn0NCmN1cnZlKC1kbm9ybSh4KSwgLTIsIDMsIGx3ZCA9IDIsIHlsaW0gPSBjKC0wLjU1LCAuMSkpDQp4biAgIDwtIC0xLjINCm9wICAgPC0gb3B0aW1pemUoZywgYygwLCAzKSkNCmFibGluZSh2ID0gb3AkbWluaW11bSwgbHR5ID0gMikNCmF4aXMoMywgb3AkbWluaW11bSwgZXhwcmVzc2lvbih4W24rMV0pKQ0KDQp4biAgIDwtIG9wJG1pbmltdW0NCmN1cnZlKGcsIC0yLCAzLCBhZGQgPSBUUlVFLCBjb2wgPSA0KQ0Kb3AgICA8LSBvcHRpbWl6ZShnLCBjKDAsIDMpKQ0KYWJsaW5lKHYgPSBvcCRtaW5pbXVtLCBsdHkgPSAyKQ0KYXhpcygzLCBvcCRtaW5pbXVtLCBleHByZXNzaW9uKHhbbisyXSkpDQpgYGANCg0KSXQgaXMgd29ydGggbm90aW5nIHRoYXQgaW4gdGhlIHJhcmUgZXZlbnQgdGhhdCAkZiQgaXMgaW4gZmFjdCBhIHF1YWRyYXRpYyBwb2x5bm9taWFsLCBOZXd0b24ncyBtZXRob2Qgd2lsbCBjb252ZXJnZSBpbiBhIHNpbmdsZSBzdGVwIGJlY2F1c2UgdGhlIHF1YWRyYXRpYyBhcHByb3hpbWF0aW9uIHRoYXQgaXQgbWFrZXMgdG8gJGYkIHdpbGwgYmUgZXhhY3QuDQoNCiMjIyBHZW5lcmVsaXplZCBMaW5lYXIgTW9kZWxzDQoNClRoZSBnZW5lcmFsaXplZCBsaW5lYXIgbW9kZWwgaXMgYW4gZXh0ZW5zdGlvbiBvZiB0aGUgc3RhbmRhcmQgbGluZWFyIG1vZGVsIHRvIGFsbG93IGZvciBub24tTm9ybWFsIHJlc3BvbnNlIGRpc3RyaWJ1dGlvbnMuIFRoZSBkaXN0cmlidXRpb25zIHVzZWQgdHlwaWNhbGx5IGNvbWUgZnJvbSBhbiBleHBvbmVudGlhbCBmYW1pbHkgd2hvc2UgZGVuc2l0eSBmdW5jdGlvbnMgc2hhcmUgc29tZSBjb21tb24gY2hhcmFjdGVyaXN0aWNzLiBXaXRoIGEgR0xNLCB3ZSB0eXBpY2FsIHByZXNlbnQgaXQgYXMgJHlfaSBcc2ltIHAoeV9pfFxtdV9pKSQsIHdoZXJlICRwJCBpcyBhbiBleHBvbmVudGlhbCBmYW1pbHkgZGlzdHJpYnV0aW9uLCAkXG1hdGhiYntFfVt5X2ldPVxtdV9pJCwNCiQkZyhcbXVfaSk9eCdfaVxiZXRhLCQkDQoNCndoZXJlICRnJCBpcyBhIG5vbmxpbmVhciBsaW4gZnVuY3Rpb24sIGFuZCAkVmFyKHlfaSk9VihcbXUpJCB3aGVyZSAkViQgaXMgYSBrbm93biB2YXJpYW5jZSBmdW5jdGlvbi4NCg0KVW5saWtlIHRoZSBzdGFuZGFyZCBsaW5lYXIgbW9kZWwsIHRoZSBtYXhpbXVtIGxpa2Vob29kIGVzdGltYXRlIG9mIHRoZSBwYXJhbWV0ZXIgdmVjdG9yICRcYmV0YSQgY2Fubm90IGJlIG9idGFpbmVkIGluIGNsb3NlZCBmb3JtLCBzbyBhbiBpdGVyYXRpdmUgYWxnb3JpdGhtIG11c3QgYmUgdXNlZCB0byBvYnRhaW4gdGhlIGVzdGltYXRlLiBUaGUgdHJhZGl0aW9uYWwgYWxnb3JpdG0gdXNlZCBpcyB0aGUgRmlzaGVyIHNjb3JpbmcgYWxnb3JpdGhtLiBUaGlzIGFsZ29yaXRobSB1c2VzIGEgbGluZWFyIGFwcHJveGltYXRpb24gdG8gbm9ubGluZWFyIGxpbmsgZnVuY3Rpb24gJGckLCB3aGljaCBjYW4gYmUgd3JpdHRlbiBhcw0KJCRnKHlfaSlcYXBwcm94IGcoXG11X2kpKyh5X2ktXG11X2kpZycoXG11X2kpLiQkDQoNClRoZSB0eXBpY2FsIG5vdGF0aW9uIG9mIEdMTXMgcmVmZXJzIHRvICR6X2k9ZyhcbXVfaSkrKHlfaS1cbXVfaSlnJyhcbXVfaSkkIGFzIHRoZSAqd29ya2luZyByZXNwb25zZS4qIFRoZSBGaXNoZXIgc2NvcmluZyBhbGdvcml0aG0gdGhlbiB3b3JrcyBhcyBmb2xsb3dzLg0KMS4gU3RhcnQgd2l0aCAkXGhhdHtcbXVfaX0kLCBzb21lIGluaXRpYWwgdmFsdWUuDQoyLiBDb21wdXRlICR6X2k9ZyhcbXVfaSkrKHlfaS1cbXVfaSlnJyhcbXVfaSkkLg0KMy4gR2l2ZW4gdGhlICRuIFx0aW1lcyAxJCB2ZWN0b3Igb2Ygd29ya2luZyByZXNwaW5zZXMgJHokIGFuZCB0aGUgJG4gXHRpbWVzIHAkIHByZWRpY3RvciBtYXRyaXggJFgkIHdlIGNvbXB1dGUgYSB3ZWlnaHRlZCByZWdyZXNzaW9uIG9mICR6JCBvbiAkWCQgdG8gZ2V0DQokJFxiZXRhX249KFgnV1gpXnstMX1YJ1d6JCQNCndoZXJlICRXJCBpcyBhIGRpYWdvbmFsIG1hdHJpeCB3aXRoIGRpYWdvbmFsIGVsZW1lbnRzIA0KJCR3X3tpaX09W2cnKFxtdV9pKV4yVihcbXVfaSldXnstMX0kJC4NCjQuIEdpdmVuICRcYmV0YV9uLCQgd2UgY2FuIHJlY29tcHV0ZSAkXGhhdHtcbXVfaX09Z157LTF9KHgnX2lcYmV0YV9uKSQgYW5kIGdvIHRvIDIuDQoNCk5vdGUgdGhhdCBpbiBTdGVwIDMgYWJvdmUsIHRoZSB3ZWlnaHRzIGFyZSBzaW1wbHkgdGhlIGludmVyc2VzIG9mIHRoZSB2YXJpYW5jZSBvZiAkel9pJCwgaS5lLg0KJCRcYmVnaW4ge2FsaWduKn0gVmFyKHpfaSkgPSBWYXIoZyhcbXUpKyh5X2ktXG11X2kpZycoXG11X2kpKVxcDQo9VmFyKCh5X2ktXG11X2kpZycoXG11X2kpKVxcDQpWKFxtdV9pKWcnKFxtdV9pKV4yIFxlbmR7YWxpZ24qfSQkDQoNCk5hdHVyYWxseSwgd2hlbiBkb2luZyBhIHdlaWdodGVkIHJlZ3Jlc3Npb24sIHdlIHdvdWxkIHdlaWdodCBieSB0aGUgaW52ZXJzZSBvZiB0aGUgdmFyaWFuY2VzLg0KDQojIyMjIEV4YW1wbGU6IFBvaXNzb24gUmVncmVzc2lvbg0KDQpGb3IgYSBQb2lzc29uIHJlZ3Jlc3Npb24sIHdlIGhhdmUgJHlfaVxzaW0gUG9pc3NvbiAoXG11X2kpJCB3aGVyZSAkZyhcbXUpPWxvZyBcc3BhY2UgXG11X2k9eCdfaVxiZXRhJCBiZWNhdXNlIHRoZSBsb2cgaXMgdGhlIGNhbm9uaWNhbCBsaW5rIGZ1bmN0aW9uIGZvciB0aGUgUG9pc3NvbiBkaXN0cmlidXRpb24sIFdlIGFsc28gaGF2ZSAkZycoXG11X2kpPVxmcmFjezF9e1xtdV9pfSQgYW5kICRWKFxtdV9pKT1cbXVfaS4kDQpUaGVyZWZvcmUsIHRoZSBGaXNoZXIgc2NvcmluZyBhbGdvcml0aG0gaXMNCjEuIEluaXRpYWxpemUgJFxoYXR7XG11X2l9LCQgcGVyaGFwcyB1c2luZyAkeV9pKzEkICh0byBhdm9pZCB6ZXJvcykuDQoyLiBMZXQgJHpfaT1sb2cgXHNwYWNlIFxoYXR7XG11X2l9Kyh5X2ktXGhhdHtcbXVfaX0pXGZyYWN7MX17XGhhdHtcbXVfaX19JA0KMy4gUmVncmVzc2lvbiAkeiQgb24gJFgkIHVzaW5nIHRoZSB3ZWlnaHRzIA0KJCR3X3tpaX09W1xmcmFjezF9e1xoYXR7XG11X2leMn19XGhhdHtcbXVfaX1dXnstMX09XGhhdHtcbXVfaX0uJCQNClVzaW5nIHRoZSBQb2lzc29uIHJlZ3Jlc3Npb24gZXhhbXBsZSwgd2UgY2FuIGRyYXcgYSBjb25uZWN0aW9uIGJldHdlZW4gdGhlIHVzdWFsIEZpc2hlciBzY29yaW5nIGFsZ29yaXRobSBmb3IgZml0dGluZyBHTE1zIGFuZCBOZXd0b24ncyBtZXRob2QuIFJlY2FsbCB0aGF0IGlmICRsKFxiZXRhKSQgaXMgdGhlIGxvZy1saWtlbGlob29kIGFzIGEgZnVuY3Rpb24gb2YgdGhlIHJlZ3Jlc3Npb24gcGFyYW1ldGVycyAkXGJldGEkLCB0aGVuIHRoZSBOZXd0b24gdXBkYXRpbmcgc2NoZW1lIGlzIA0KJCRcYmV0YV97bisxfT1cYmV0YV9uK2wnJyhcYmV0YV9uKV57LTF9Wy1sJyhcYmV0YV9uKV0uJCQNCg0KVGhlIGxvZy1saWtlbGlocHBkIGZvciBhIFBvaWFhb24gcmVncmVzc2lvbiBtb2RlbCBjYW4gYmUgd3JpdHRlbiBpbiB2ZWN0b3IvbWF0cml4IGZvcm0gYXMgDQokJGwoXGJldGEpPXknWFxiZXRhLSBleHAoWFxiZXRhKScxJCQNCndoZXJlIHRoZSBleHBvbmVudGlhbCBpcyB0YWtlZCBjb21wb25lbnQtd2lzZSBvbiB0aGUgdmVjdG9yICRYXGJldGEkLiBUaGUgZ3JhZGllbnQgZnVuY3Rpb24gaXMgDQokJGwnKFxiZXRhKT1YJ3ktWCdleHAoWFxiZXRhKT1YJyh5LVxtdSkkJA0KYW5kIHRoZSBIZXNzaWFuIGlzDQokJGwnJyhcYmV0YSk9LVgnV1gkJA0Kd2hlcmUgJFckIGlzIGEgZGlhZ29uYWwgbWF0cml4IHdpdGggdGhlIHZhbHVlcyAkd197aWl9PWV4cCh4J19pXGJldGEpJCBvbiB0aGUgZGlhZ29uYWwuIFRoZSBOZXd0b24gaXRlcmF0aW9uIGlzIHRoZW4NCiQkXGJlZ2luIHtlcW5hcnJheSp9IFxiZXRhX3tuKzF9PVxiZXRhX24rKC1YJ1dYKV57LTF9KC1YJyh5LVxtdSkpXFwNCj1cYmV0YV9uKyhYJ1dYKV57LTF9WFcoei1YXGJldGFfbilcXA0KPShYJ1dYKV57LTF9WCdXeitcYmV0YV9uLShYJ1dYKV57LTF9WCdXWFxiZXRhX25cXA0KPShYJ1dYKV57LTF9WCdXeg0KXGVuZCB7ZXFuYXJyYXkqfSQkDQoNClRoZXJlZm9yZSB0aGUgaXRlcmF0aW9uIGlzIGV4YWN0bHkgdGhlIHNhbWUgYXMgdGhlIEZpc2hlciBzY29yaW5nIGFsZ29yaXRobSBpbiB0aGlzIGNhc2UuIEluIGdlbmVyYWwsIE5ld3RvbidzIG1ldGhvZCBhbmQgRmlzaGVyIHNjb3Jpbmcgd2lsbCBjb2luY2lkZSB3aXRoIGFueSBnZW5lcmFsaXplZCBsaW5lYXIgbW9kZWwgdXNpbmcgYW4gZXhwb25lbnRpYWwgZmFtaWx5IHdpdGggYSBjYW5vbmljYWwgbGluayBmdW5jdGlvbi4NCg0KIyMjIE5ld3RvbidzIE1ldGhvZCBpbiBSDQpUaGUgYG5sbSgpYCBmdW5jdGlvbiBpbiBSIGltcGxlbWVudHMgTmV3dG9uJ3MgbWV0aG9kIGZvciBtaW5pbWl6aW5nIGEgZnVuY3Rpb24gZ2l2ZW4gYSB2ZWN0b3Igb2Ygc3RhdGluZyB2YWx1ZXMuIEJ5IGRlZmF1bHQsIG9uZSBkb2VzIG5vdCBuZWVkIHRvIHN1cHBseSB0aGUgZ3JhZGllbnQgb3IgSGVzc2lhbiBmdW5jdGlvbnM7IHRoZXkgd2lsbCBiZSBlc3RpbWF0ZWQgbnVtZXJpY2FsbHkgYnkgdGhlIGFsZ29yaXRobS4gSG93ZXZlciwgZm9yIHRoZSBwdXJwb3NlcyBvZiBpbXByb3ZpbmcgYWNjdXJhY3kgb2YgdGhlIGFsZ29yaXRobSwgYm90aCB0aGUgZ3JhZGllbnQgYW5kIEhlc3NpYW4gY2FuIGJlIHN1cHBsaWVkIGFzIGF0dHJpYnV0ZXMgb2YgdGhlIHRhcmdldCBmdW5jdGlvbi4NCg0KQXMgYW4gZXhhbXBsZSwgd2Ugd2lsbCB1c2UgdGhlIGBubG0oKWAgZnVuY3Rpb24gdG8gZml0IGEgc2ltcGxlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgZm9yIGJpbmFyeSBkYXRhLiBUaGlzIG1vZGVsIHNwZWNpZmllcyB0aGF0ICR5X2kgXHNpbSBcc3BhY2UgQmVybm91bGxpKHBfaSkkIHdoZXJlDQokJGxvZyBcZnJhY3twX2l9ezEtcF9pfT1cYmV0YV8wK3hfaVxiZXRhXzEkJA0KYW5kIHRoZSBnb2FsIGlzIHRvIGVzdGltYXRlICRcYmV0YSQgdmlhIG1heGltdW0gbGlrZWxpaG9vZC4gR2l2ZW4gdGhlIGFzc3VtZWQgQmVybm91bGxpIGRpc3RyaWJ1dGlvbiwgd2UgY2FuIHdyaXRlIHRoZSBsb2ctbGlrZWxpaG9vZCBmb3IgYSBzaW5nbGUgb2JzZXJ2YXRpb24gYXMNCg0KJCRcYmVnaW57ZXFuYXJyYXkqfQ0KXGxvZyBMKFxiZXRhKSAmID0gJiBcbG9nXGxlZnRce1xwcm9kX3tpPTF9Xm4gcF9pXnt5X2l9KDEtcF9pKV57MS15X2l9XHJpZ2h0XH1cXA0KJiA9ICYgDQpcc3VtX3tpPTF9Xm4geV9pXGxvZyBwX2kgKyAoMS15X2kpXGxvZygxLXBfaSlcXA0KJiA9ICYgDQpcc3VtX3tpPTF9Xm4geV9pXGxvZ1xmcmFje3BfaX17MS1wX2l9K1xsb2coMS1wX2kpXFwNCiYgPSAmDQpcc3VtX3tpPTF9Xm4geV9pKFxiZXRhXzAgKyB4X2lcYmV0YV8xKSArIFxsb2dcbGVmdChcZnJhY3sxfXsxK2VeeyhcYmV0YV8wICsgeF9pXGJldGFfMSl9fVxyaWdodClcXA0KJiA9ICYgDQpcc3VtX3tpPTF9Xm4geV9pKFxiZXRhXzAgKyB4X2lcYmV0YV8xKSAtXGxvZ1xsZWZ0KDErZV57KFxiZXRhXzAgKyB4X2lcYmV0YV8xKX1ccmlnaHQpDQpcZW5ke2VxbmFycmF5Kn0kJA0KDQpJZiB3ZSB0YWtlIHRoZSB2ZXJ5IGxhc3QgbGluZSBvZiB0aGUgYWJvdmUgZGVyaXZhdGlvbiBhbmQgdGFrZSBhIHNpbmdsZSBlbGVtZW50IGluc2lkZSB0aGUgc3VtLCB3ZSBoYXZlDQoNCiQkXGVsbF9pKFxiZXRhKT15X2koXGJldGFfMCArIHhfaVxiZXRhXzEpIC1cbG9nXGxlZnQoMStlXnsoXGJldGFfMCArIHhfaVxiZXRhXzEpfVxyaWdodCkkJA0KDQpXZSB3aWxsIG5lZWQgdGhlIGdyYWRpZW50IGFuZCBIZXNzaWFuIG9mIHRoaXMgd2l0aCByZXNwZWN0IHRvICRcYmV0YSQuIEJlY2F1c2UgdGhlIHN1bSBhbmQgdGhlIGRlcml2YXRpdmUgYXJlIGV4Y2hhbmdlYWJsZSwgd2UgY2FuIHRoZW4gc3VtIGVhY2ggb2YgdGhlIGluZGl2aWR1YWwgZ3JhZGllbnRzIGFuZCBIZXNzaWFucyB0byBnZXQgdGhlIGZ1bGwgZ3JhZGllbnQgYW5kIEhlc3NpYW4gZm9yIHRoZSBlbnRpcmUgc2FtcGxlLCBzbyB0aGF0DQokJFxlbGxeXHByaW1lKFxiZXRhKSA9IFxzdW1fe2k9MX1eblxlbGxfaV5ccHJpbWUoXGJldGEpJCQNCmFuZA0KJCRcZWxsXlxwcmltZShcYmV0YSkgPSBcc3VtX3tpPTF9Xm5cZWxsX2lee1xwcmltZVxwcmltZX0oXGJldGEpJCQNCk5vdywgdGFraW5nIHRoZSBncmFkaWVudCBhbmQgSGVzc2lhbiBvZiB0aGUgYWJvdmUgZXhwcmVzc2lvbiBtYXkgYmUgbWlsZGx5IGluY29udmVuaWVudCwgYnV0IGl0IGlzIGZhciBmcm9tIGltcG9zc2libGUuIE5ldmVydGhlbGVzcywgUiBwcm92aWRlcyBhbiBhdXRvbWF0ZWQgd2F5IHRvIGRvIHN5bWJvbGljIGRpZmZlcmVudGlhdGlvbiBzbyB0aGF0IG1hbnVhbCB3b3JrIGNhbiBiZSBhdm9pZGVkLiBUaGUgYGRlcml2KClgIGZ1bmN0aW9uIGNvbXB1dGVzIHRoZSBncmFkaWVudCBhbmQgSGVzc2lhbiBvZiBhbiBleHByZXNzaW9uIHN5bWJvbGljYWxseSBzbyB0aGF0IGl0IGNhbiBiZSB1c2VkIGluIG1pbmltaXphdGlvbiByb3V0aW5lcy4gSXQgY2Fubm90IGNvbXB1dGUgZ3JhZGllbnRzIG9mIGFyYml0cmFyeSBleHByZXNzaW9ucywgYnV0IGl0IGl0IGRvZXMgc3VwcG9ydCBhIHdpZGUgcmFuZ2Ugb2YgY29tbW9uIHN0YXRpc3RpY2FsIGZ1bmN0aW9ucy4NCg0KIyMjIyBFeGFtcGxlOiBUcmVuZHMgaW4gcC12YWx1ZXMgT3ZlciBUaW1lDQpUaGUgYHRpZHlwdmFsc2BwYWNrYWdlIHdyaXR0ZW4gYnkgSmVmZiBMZWVrIGNvbnRhaW5zIGRhdGFzZXRzIHRha2VuIGZyb20gdGhlIGxpdGVyYXR1cmUgY29sbGVjdGluZyBwLXZhbHVlcyBhc3NvY2lhdGVkIHdpdGggdmFyaW91cyBwdWJsaWNhdGlvbnMgYWxvbmcgd2l0aCBzb21lIGluZm9ybWF0aW9uIGFib3V0IHRob3NlIHB1YmxpY2F0aW9ucyAoaS5lLiBqb3VybmFsLCB5ZWFyLCBET0kpLiBPbmUgcXVlc3Rpb24gdGhhdCBjb21lcyB1cCBpcyB3aGV0aGVyIHRoZXJlIGhhcyBiZWVuIGFueSB0cmVuZCBvdmVyIHRpbWUgaW4gdGhlIGNsYWltZWQgc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlIG9mIHB1YmxpY2F0aW9ucywgd2hlcmUg4oCcc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNl4oCdIGlzIGRlZmluZWQgYXMgaGF2aW5nIGEgcC12YWx1ZSBsZXNzIHRoYW4gMC4wNS4NCg0KVGhlIGB0aWR5cHZhbHNgIHBhY2thZ2UgaXMgYXZhaWxhYmxlIGZyb20gR2l0SHViIGFuZCBjYW4gYmUgaW5zdGFsbGVkIHVzaW5nIHRoZSBgaW5zdGFsbF9naXRodWIoKWAgZnVuY3Rpb24gaW4gdGhlIGByZW1vdGVzYCBwYWNrYWdlLg0KDQpgYGB7cn0NCnJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJqdGxlZWsvdGlkeXB2YWxzIikNCmBgYA0KDQpPbmNlIGluc3RhbGxlZCwgd2Ugd2lsbCBtYWtlIHVzZSBvZiB0aGVgamFnZXIyMDE0YCBkYXRhc2V0LiBJbiBwYXJ0aWN1bGFyLCB3ZSBhcmUgaW50ZXJzZXRlZCBpbiBjcmVhdGluZyBhbiBpbmRpY2F0b3Igb2Ygd2hldGhlciBhIHAtdmFsdWUgaXMgbGVzcyB0aGFuIDAuMDUgYW5kIHJlZ3Jlc3NpbmcgaXQgb24gdGhlIGB5ZWFyYCB2YXJpYWJsZS4NCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHlwdmFscykNCmxpYnJhcnkoZHBseXIpDQpqYWdlciA8LSBtdXRhdGUodGlkeXB2YWxzOjpqYWdlcjIwMTQsIA0KICAgICAgICAgICAgICAgIHB2YWx1ZSA9IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHB2YWx1ZSkpLA0KICAgICAgICAgICAgICAgIHkgPSBpZmVsc2UocHZhbHVlIDwgMC4wNSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgKHB2YWx1ZSA9PSAwLjA1ICYgb3BlcmF0b3IgPT0gImxlc3N0aGFuIiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgMSwgMCksDQogICAgICAgICAgICAgICAgeCA9IHllYXIgLSAyMDAwKSAlPiUNCiAgICAgICAgdGJsX2RmDQpgYGANCg0KTm90ZSBoZXJlIHRoYXQgd2UgaGF2ZSBzdWJ0cmFjdGVkIHRoZSB5ZWFyIDIwMDAgb2ZmIG9mIHRoZSBgeWVhcmAgdmFyaWFibGUgc28gdGhhdCAkeD0wJCBjb3JyZXNwb25kcyB0byBgeWVhcj09MjAwMGAuDQoNCk5leHQgd2UgY29tcHV0ZSB0aGUgZ3JhZGllbnQgYW5kIEhlc3NpYW4gb2YgdGhlIG5lZ2F0aXZlIGxvZy1saWtlbGlob29kIHdpdGggcmVzcGVjdCB0byAkXGJldGFfMCQgYW5kICRcYmV0YV8xJCB1c2luZyB0aGUgYGRlcml2KClgIGZ1bmN0aW9uLiBCZWxvdywgd2Ugc3BlY2lmeSBgZnVuY3Rpb24uYXJnPVRSVUVgIGluIHRoZSBjYWxsIHRvIGBkZXJpdigpYCBiZWNhdXNlIHdlIHdhbnQgYGRlcml2KClgIHRvIHJldHVybiBhICpmdW5jdGlvbiogd2hvc2UgYXJndW1lbnRzIGFyZSBgYjBgIGFuZCBgYjFgLg0KDQpgYGB7cn0NCm5sbF9vbmUgPC0gZGVyaXYofiAtKHkgKiAoYjAgKyB4ICogYjEpIC0gbG9nKDEgKyBleHAoYjAgKyBiMSAqIHgpKSksDQogICAgICAgICAgICAgYygiYjAiLCAiYjEiKSwgZnVuY3Rpb24uYXJnID0gVFJVRSwgaGVzc2lhbiA9IFRSVUUpDQpgYGANCg0KSGVyZSdzIHdoYXQgdGhlIGZ1bmN0aW9uIGxvb2tzIGxpa2UuDQoNCmBgYHtyfQ0KbmxsX29uZQ0KYGBgDQoNClRoZSBmdW5jdGlvbiBgbmxsX29uZSgpYCBwcm9kdWNlZCBieSBgZGVyaXYoKWAgZXZhbHVhdGVzIHRoZSBuZWdhdGl2ZSBsb2ctbGlrZWxpaG9vZCBmb3IgZWFjaCBkYXRhIHBvaW50LiBUaGUgb3V0cHV0IGZyb20gYG5sbF9vbmUoKWAgd2lsbCBoYXZlIGF0dHJpYnV0ZXMgYCJncmFkaWVudCJgIGFuZCBgImhlc3NpYW4iYCB3aGljaCByZXByZXNlbnQgdGhlIGdyYWRpZW50IGFuZCBIZXNzaWFuLCByZXNwZWN0aXZlbHkuIEZvciBleGFtcGxlLCB1c2luZyB0aGUgZGF0YSBmcm9tIHRoZSBgamFnZXJgIGRhdGFzZXQsIHdlIGNhbiBldmFsdWF0ZSB0aGUgbmVnYXRpdmUgbG9nLWxpa2VsaWhvb2QgYXQgJFxiZXRhXzA9MCxcYmV0YV8xPTAuJA0KDQpgYGB7cn0NCnggPC0gamFnZXIkeA0KeSA8LSBqYWdlciR5DQpzdHIobmxsX29uZSgwLCAwKSkNCmBgYA0KDQpUaGUgYG5sbF9vbmUoKWAgZnVuY3Rpb24gZXZhbHVhdGVzIHRoZSBuZWdhdGl2ZSBsb2ctbGlrZWxpaG9vZCBhdCBlYWNoIGRhdGEgcG9pbnQsIGJ1dCBkb2VzIG5vdCBzdW0gdGhlIHBvaW50cyB1cCBhcyB3b3VsZCBiZSByZXF1aXJlZCB0byBldmFsdWF0ZSB0aGUgZnVsbCBuZWdhdGl2ZSBsb2ctbGlrZWxpaG9vZC4gVGhlcmVmb3JlLCB3ZSB3aWxsIHdyaXRlIGEgc2VwYXJhdGUgZnVuY3Rpb24gdGhhdCBkb2VzIHRoYXQgZm9yIHRoZSBuZWdhdGl2ZSBsb2ctbGlrZWxpaG9vZCwgZ3JhZGllbnQsIGFuZCBIZXNzaWFuLg0KDQpgYGB7cn0NCm5sbCA8LSBmdW5jdGlvbihiKSB7DQogICAgICAgIHYgPC0gbmxsX29uZShiWzFdLCBiWzJdKQ0KICAgICAgICBmIDwtIHN1bSh2KSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIyBsb2ctbGlrZWxpaG9vZA0KICAgICAgICBnciA8LSBjb2xTdW1zKGF0dHIodiwgImdyYWRpZW50IikpICAgICAgICAgICAgICAjIyBncmFkaWVudCB2ZWN0b3INCiAgICAgICAgaGVzcyA8LSBhcHBseShhdHRyKHYsICJoZXNzaWFuIiksIGMoMiwgMyksIHN1bSkgIyMgSGVzc2lhbiBtYXRyaXgNCiAgICAgICAgYXR0cmlidXRlcyhmKSA8LSBsaXN0KGdyYWRpZW50ID0gZ3IsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVzc2lhbiA9IGhlc3MpDQogICAgICAgIGYNCn0NCmBgYA0KDQpOb3csIHdlIGNhbiBldmFsdWF0ZSB0aGUgZnVsbCBuZWdhdGl2ZSBsb2ctbGlrZWxpaG9vZCB3aXRoIHRoZSBgbmxsKClgIGZ1bmN0aW9uLiBOb3RlIHRoYXQgYG5sbCgpYCB0YWtlcyBhIHNpbmdsZSBudW1lcmljIHZlY3RvciBhcyBpbnB1dCBhcyB0aGlzIGlzIHdoYXQgdGhlIGBubG0oKWAgZnVuY3Rpb24gaXMgZXhwZWN0aW5nLg0KDQpgYGB7cn0NCm5sbChjKDAsIDApKQ0KYGBgDQoNClVzaW5nICRcYmV0YV8wPTAsXGJldGFfMT0wJCBhcyB0aGUgaW5pdGlhbCB2YWx1ZW0gd2UgY2FuIGNhbGwgYG5sbSgpYCB0byBtaW5pbWl6ZSB0aGUgbmVnYXRpdmUgbG9nLWxpa2VsaWhvb2QuDQpgYGB7cn0NCnJlcyA8LSBubG0obmxsLCBjKDAsIDApKQ0KcmVzDQpgYGANCg0KTm90ZSBmaXJzdCBpbiB0aGUgb3V0cHV0IHRoYXQgdGhlcmUgaXMgYSBgY29kZWAgd2l0aCB0aGUgdmFsdWUgYDRgIGFuZCB0aGF0IHRoZSBudW1iZXIgb2YgaXRlcmF0aW9ucyBpcyAxMDAuIFdoZW5ldmVyIHRoZSBudW1iZXIgb2YgaXRlcmF0aW9ucyBpbiBhbiBvcHRpbWl6YXRpb24gYWxnb3JpdGhtIGlzIGEgbmljZSByb3VuZCBudW1iZXIsIHRoZSBjaGFuY2VzIGFyZSBnb29kIHRoYXQgaXQgaXQgc29tZSBwcmVzZXQgaXRlcmF0aW9uIGxpbWl0LiBUaGlzIGluIHR1cm4gdXN1YWxseSBtZWFucyB0aGUgYWxnb3JpdGhtIGRpZG7igJl0IGNvbnZlcmdlLg0KDQpJbiB0aGUgaGVscCBmb3IgYG5sbSgpYCB3ZSBhbHNvIGxlYXJuIHRoYXQgdGhlIGBjb2RlYCB2YWx1ZSBvZiA0IG1lYW5zIOKAnGl0ZXJhdGlvbiBsaW1pdCBleGNlZWRlZOKAnSwgd2hpY2ggaXMgZ2VuZXJhbGx5IG5vdCBnb29kLiBMdWNraWx5LCB0aGUgc29sdXRpb24gaXMgc2ltcGxlOiB3ZSBjYW4gaW5jcmVhc2UgdGhlIGl0ZXJhdGlvbiBsaW1pdCBhbmQgbGV0IHRoZSBhbGdvcml0aG0gcnVuIGxvbmdlci4NCmBgYHtyfQ0KcmVzIDwtIG5sbShubGwsIGMoMCwgMCksIGl0ZXJsaW0gPSAxMDAwKQ0KcmVzDQpgYGANCg0KSGVyZSB3ZSBzZWUgdGhhdCB0aGUgbnVtYmVyIG9mIGl0ZXJhdGlvbnMgdXNlZCB3YXMgMjYwLCB3aGljaCBpcyB3ZWxsIGJlbG93IHRoZSBpdGVyYXRpb24gbGltaXQuIE5vdyB3ZSBnZXQgYGNvZGVgIGVxdWFsIHRvIGAyYCB3aGljaCBtZWFucyB0aGF0IOKAnHN1Y2Nlc3NpdmUgaXRlcmF0ZXMgd2l0aGluIHRvbGVyYW5jZSwgY3VycmVudCBpdGVyYXRlIGlzIHByb2JhYmx5IHNvbHV0aW9u4oCdLiBTb3VuZHMgbGlrZSBnb29kIG5ld3MhDQoNCkxhc3RseSwgbW9zdCBvcHRpbWl6YXRpb24gYWxnb3JpdGhtcyBoYXZlIGFuIG9wdGlvbiB0byBzY2FsZSB5b3VyIHBhcmFtZXRlciB2YWx1ZXMgc28gdGhhdCB0aGV5IHJvdWdobHkgdmFyeSBvbiB0aGUgc2FtZSBzY2FsZS4gSWYgeW91ciB0YXJnZXQgZnVuY3Rpb24gaGFzIHBhcmFtdGVycyB0aGF0IHZhcnkgb24gd2lsZGx5IGRpZmZlcmVudCBzY2FsZXMsIHRoaXMgY2FuIGNhdXNlIGEgcHJhY3RpY2FsIHByb2JsZW0gZm9yIHRoZSBjb21wdXRlciAoaXTigJlzIG5vdCBhIHByb2JsZW0gZm9yIHRoZSB0aGVvcnkpLiBUaGUgd2F5IHRvIGRlYWwgd2l0aCB0aGlzIGluIGBubG0oKWAgaXMgdG8gdXNlIHRoZSBgdHlwc2l6ZWAgYXJndWVtbnQsIHdoaWNoIGlzIGEgdmVjdG9yIGVxdWFsIGluIGxlbmd0aCB0byB0aGUgcGFyYW1ldGVyIHZlY3RvciB3aGljaCBwcm92aWRlcyB0aGUgcmVsYXRpdmUgc2l6ZXMgb2YgdGhlIHBhcmFtZXRlcnMuDQoNCkhlcmUsIEkgZ2l2ZSBgdHlwc2l6ZSA9IGMoMSwgMC4xKWAsIHdoaWNoIGluZGljYXRlcyB0byBgbmxtKClgIHRoYXQgdGhlIGZpcnN0IHBhcmFtdGVyLCRcYmV0YV8wJCwgc2hvdWxkIGJlIHJvdWdobHkgMTAgdGltZXMgbGFyZ2VyIHRoYW4gdGhlIHNlY29uZCBwYXJhbWV0ZXIsICRcYmV0YV8xJCB3aGVuIHRoZSB0YXJnZXQgZnVuY3Rpb24gaXMgYXQgaXRzIG1pbmltdW0uDQpgYGB7cn0NCnJlcyA8LSBubG0obmxsLCBjKDAsIDApLCBpdGVybGltID0gMTAwMCwNCiAgICAgICAgICAgdHlwc2l6ZSA9IGMoMSwgMC4xKSkNCnJlcw0KYGBgDQoNClJ1bm5pbmcgdGhpcyBjYWxsIHRvIGBubG0oKWAgeW914oCZbGwgbm90aWNlIHRoYXQgdGhlIHNvbHV0aW9uIGlzIHRoZSBzYW1lIGJ1dCB0aGUgbnVtYmVyIG9mIGl0ZXJhdGlvbnMgaXMgYWN0dWFsbHkgbXVjaCBsZXNzIHRoYW4gYmVmb3JlICg0IGl0ZXJhdGlvbnMpIHdoaWNoIG1lYW5zIHRoZSBhbGdvcml0aG0gcmFuIGZhc3Rlci4gR2VuZXJhbGx5IHNwZWFraW5nLCBzY2FsaW5nIHRoZSBwYXJhbWV0ZXIgdmVjdG9yIGFwcHJvcHJpYXRlbHkgKGlmIHBvc3NpYmxlKSBpbXByb3ZlcyB0aGUgcGVyZm9ybWFuY2Ugb2YgYWxsIG9wdGltaXphdGlvbiBhbGdvcml0aG1zIGFuZCBpbiBteSBleHBlcmllbmNlIGlzIGFsbW9zdCBhbHdheXMgYSBnb29kIGlkZWEuIFRoZSBzcGVjaWZpYyB2YWx1ZXMgZ2l2ZW4gdG8gdGhlIGB0eXBzaXplYCBhcmd1bWVudCBhcmUgbm90IGltcG9ydGFudDsgcmF0aGVyIHRoZWlyIHJlbGF0aW9uc2hpcHMgdG8gZWFjaCBvdGhlciAoaS5lLiBvcmRlcnMgb2YgbWFnbml0dWRlKSBhcmUgd2hhdCBtYXR0ZXIuDQoNCiMgUXVhc2ktTmV3dG9uDQoNCiMgQ29uanVnYXRlIEdyYWRpZW50DQoNCiMgQ29vcmRpbmF0ZSBEZXNjZW50DQoNCg0K