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

Newton’s method is an iterative method for finding the roots of a differentiable function F, which are solutions to the equation \(F(x) = 0.\) In optimization, Newton’s method is applied to the derivative \(f\) ′ of a twice-differentiable function \(f\) to find the roots of the derivative (solutions to \(f ′(x) = 0\), also known as the stationary points of \(f\). These solutions may be minima, maxima, or saddle points. The central problem of optimization is minimization of functions. Let us first consider the case of univariate functions, i.e., functions of a single real variable. We will later consider the more general and more practically useful multivariate case.

Given a twice differentiable function \({\displaystyle f:\mathbb {R} \to \mathbb {R} }{\displaystyle f:\mathbb {R} \to \mathbb {R} },\) we seek to solve the optimization problem

\({\displaystyle \min _{x\in \mathbb {R} }f(x).}{\displaystyle \min _{x\in \mathbb {R} }f(x).}\)

Newton’s method attempts to solve this problem by constructing a sequence \({\displaystyle \{x_{k}\}}\) from an initial guess (starting point) \({\displaystyle x_{0}\in \mathbb {R} }\) that converges towards a minimizer \(x_*\) of \(f\) by using a sequence of second-order Taylor approximations of \({\displaystyle f}\) around the iterates. The second-order Taylor expansion of f around \(x_k\) is

\[{\displaystyle f(x_{k}+t)\approx f(x_{k})+f'(x_{k})t+{\frac {1}{2}}f''(x_{k})t^{2}.}{\displaystyle f(x_{k}+t)\approx f(x_{k})+f'(x_{k})t+{\frac {1}{2}}f''(x_{k})t^{2}.}\] The next iterate \({\displaystyle x_{k+1}}\) is defined so as to minimize this quadratic approximation in \({\displaystyle t}\), and setting \({\displaystyle x_{k+1}=x_{k}+t}\). If the second derivative is positive, the quadratic approximation is a convex function of \({\displaystyle t}\), and its minimum can be found by setting the derivative to zero. Since

\[{\displaystyle \displaystyle 0={\frac {\rm {d}}{{\rm {d}}t}}\left(f(x_{k})+f'(x_{k})t+{\frac {1}{2}}f''(x_{k})t^{2}\right)=f'(x_{k})+f''(x_{k})t,}\] the minimum is achieved for

\[{\displaystyle t=-{\frac {f'(x_{k})}{f''(x_{k})}}.}\] Putting everything together, Newton’s method performs the iteration

\[{\displaystyle x_{k+1}=x_{k}+t=x_{k}-{\frac {f'(x_{k})}{f''(x_{k})}}.}\]

3.1 Newton in Visualization

A comparison of gradient descent (green) and Newton’s method (red) for minimizing a function (with small step sizes). Newton’s method uses curvature information to take a more direct route.

3.2 Geometric interpretation

The geometric interpretation of Newton’s method is that at each iteration, it amounts to the fitting of a paraboloid to the surface of \({\displaystyle f(x)}\) at the trial value \(x_{k}\), having the same slopes and curvature as the surface at that point, and then proceeding to the maximum or minimum of that paraboloid (in higher dimensions, this may also be a saddle point).

3.3 Higher dimensions

The above iterative scheme can be generalized to \(d\) dimensions by replacing the derivative with the gradient (different authors use different notation for the gradient, including \({\displaystyle f'(x)=\nabla f(x)=g_{f}(x)\in \mathbb {R} ^{d}}\), and the reciprocal of the second derivative with the inverse of the Hessian matrix (different authors use different notation for the Hessian, including \({\displaystyle f''(x)=\nabla ^{2}f(x)=H_{f}(x)\in \mathbb {R} ^{d\times d}}\). One thus obtains the iterative scheme \({\displaystyle x_{k+1}=x_{k}-[f''(x_{k})]^{-1}f'(x_{k}),k\geq 0.}\) Often Newton’s method is modified to include a small step size \({\displaystyle 0<\gamma \leq 1}\) instead of \({\displaystyle \gamma =1}\) \[{\displaystyle x_{k+1}=x_{k}-\gamma [f''(x_{k})]^{-1}f'(x_{k}).}\] This is often done to ensure that the Wolfe conditions are satisfied at each step of the method. For step sizes other than 1, the method is often referred to as the relaxed or damped Newton’s method.

3.4 Convergence

If \(f\) is a strongly convex function with Lipschitz Hessian, then provided that \(x_{0}\) is close enough to \({\displaystyle x_{*}=\arg \min f(x)}\), the sequence \({\displaystyle x_{0},x_{1},x_{2},\dots }\) generated by Newton’s method will converge to the (necessarily unique) minimizer \(x_{*}\) of \(f\) quadratically fast That is, \[{\displaystyle \|x_{k+1}-x_{*}\|\leq {\frac {1}{2}}\|x_{k}-x_{*}\|^{2},\qquad \forall k\geq 0.}\]

3.5 Computing the Newton direction

Finding the inverse of the Hessian in high dimensions to compute the Newton direction \(h=-(f''(x_{k}))^{-1}f'(x_{k})\) can be an expensive operation. In such cases, instead of directly inverting the Hessian, it’s better to calculate the vector \(h\) as the solution to the system of linear equations \[{\displaystyle [f''(x_{k})]h=-f'(x_{k})}\] which may be solved by various factorizations or approximately (but to great accuracy) using iterative methods. Many of these methods are only applicable to certain types of equations, for example the Cholesky factorization and conjugate gradient will only work if \(f''(x_{k})\) is a positive definite matrix. While this may seem like a limitation, it’s often a useful indicator of something gone wrong; for example if a minimization problem is being approached and \(f''(x_{k})\) is not positive definite, then the iterations are converging to a saddle point and not a minimum. On the other hand, if a constrained optimization is done (for example, with Lagrange multipliers), the problem may become one of saddle point finding, in which case the Hessian will be symmetric indefinite and the solution of \(x_{k+1}\) will need to be done with a method that will work for such, such as the \(LDL\) variant of Cholesky factorization or the conjugate residual method. There also exist various quasi-Newton methods, where an approximation for the Hessian (or its inverse directly) is built up from changes in the gradient. If the Hessian is close to a non-invertible matrix, the inverted Hessian can be numerically unstable and the solution may diverge. In this case, certain workarounds have been tried in the past, which have varied success with certain problems. One can, for example, modify the Hessian by adding a correction matrix \(B_k\) so as to make \(f''(x_{k})\)+\(B_{k}\) positive definite. One approach is to diagonalize the Hessian and choose \(B_{k}\) so that \(f''(x_{k})\)+\(B_k\) has the same eigenvectors as the Hessian, but with each negative eigenvalue replaced by \({\epsilon >0}\) An approach exploited in the Levenberg–Marquardt algorithm (which uses an approximate Hessian) is to add a scaled identity matrix to the Hessian, \({\mu I}\), with the scale adjusted at every iteration as needed. For large \({\mu }\) and small Hessian, the iterations will behave like gradient descent with step size \({1/\mu }\) . This results in slower but more reliable convergence where the Hessian doesn’t provide useful information.

3.6 Examples of Newton

Before proceeding to an implementation of the Newton-Raphson method in R, it is worth working through some examples to get an understanding of the definitions and equations above. The NR method can be used to approximate square roots such as \(√10.\) The square root of 10 is about three, so we can use that as a good starting value. It often helps to plot the function to see where the roots occur. The function is first rearranged to be an expression of \(f(x)\) before plotting. \[x=√10\] \[x−√10=0\] \[f(x)=x2−10\] Plotting the function yields the graph below.

It can be seen from the graph the function crosses the x-axis at \(√10\) on an interval [3, 4]. The derivative of the function is \(2x\). And so on until the xn estimates are within a particular level of tolerance. This example converges in three iterations. Thus 3.162278 is the estimated root of the function. This result can be verified by simply taking the square root of 10.

## [1] 3.162278

3.7 Newton Method in R

The \(uniroot\) function in R provides an implementation of Newton-Raphson for finding the root of an equation. The function is only capable of finding one root in the given interval. The \(rootSolve\) package features the uniroot.all function which extends the uniroot routine to detect multiple roots should they exist.

The equation for which we wish to find the root is: \[f(x)=x^3−2x−5\] First, write a function to express the equation above.

Plot the function to visualize how the equation behaves and where any roots may be located.

It looks like the function equals 0 when y is about 2. To find the root of the equation, use the uniroot function with a starting value of 2 and upper bound of 3.

## $root
## [1] 2.094541
## 
## $f.root
## [1] -0.0001147009
## 
## $iter
## [1] 5
## 
## $init.it
## [1] NA
## 
## $estim.prec
## [1] 6.103516e-05

As suspected, the root of the function is very close to 2 at 2.0945412. The iterations can be written as follows:

\[x_{n+1}=x_n-{\frac {f(x_{n})}{f'(x_{n})}}=x_n-{\frac {f(x^3_{n}-2x-5)}{3x^2_n-2}}\] \[x_1={\frac {(2)^3-2(2)-5}{3(2)^2-2}}=1.281239\] \[x_2={\frac {(1.281239)^3-2(1.281239)-5}{3(1.281239)^2-2}}=3.147821\] \[x_3={\frac {(3.147821)^3-2(3.147821)-5}{3(3.147821)^2-2}}=2.430257\] \[x_4={\frac {(2.430257)^3-2(2.430257)-5}{3(2.430257)^2-2}}=2.144418\] \[x_5={\frac {(2.144418)^3-2(2.144418)-5}{3(2.144418)^2-2}}=2.095897\] \[x_5={\frac {(2.095897)^3-2(2.095897)-5}{3(2.095897)^2-2}}=2.094552\] The manual calculations equal the output of the function to four decimals. The minuscule difference between results is likely the cause of roundoff and significance errors in the manual calculation. A quick and dirty function to perform the method in R can be implemented to further verify our understanding of the Newton-Raphson method. The numDeriv package is used to compute the derivative \(f′(x)\), though this could also be done by taking the limit with a sufficiently small \(h\).

Computing the root of the above equation with the newton.raphson function yields:

## $`root approximation`
## [1] 2.094551
## 
## $iterations
## [1] 2.100000 2.094568 2.094551 2.094551

The custom Newton-Raphson function is slightly off from the uniroot result, however; they are equal up to 4 decimals and is adequate for our purposes.

#Summary The Newton-Raphson method is a powerful and relatively straightforward method for finding the roots of an equation. It has many advantages but suffers from several drawbacks such as a readily calculated derivative, inconsistencies when \(f′(x)=0\) and so on. As explored in the post, it often makes sense first to plot the function in question to visualize how it behaves before attempting to locate the \(root(s)\) as bad starting values can be detrimental to results. Other numerical methods for estimating roots of equations such as the Bisection, Secant and Brent’s methods will be examined in future posts.

LS0tDQp0aXRsZTogIkxhYjU6IEdlbmVyYWwgT3B0aW1pemF0aW9uIg0KYXV0aG9yOiAiV2lkaSB5YW50aWgiDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCksICclQiAlZCwgJVknKWAiDQpvdXRwdXQ6IA0KICBodG1sX2RvY3VtZW50OiANCiAgICBoaWdobGlnaHQ6IG1vbm9jaHJvbWUNCiAgICB0aGVtZTogc3BhY2VsYWINCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCi0tLQ0KDQpgYGB7ciBMb2dvLCBlY2hvPUZBTFNFLGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoID0gJzQwJSd9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaHR0cHM6Ly9naXRodWIuY29tL0Jha3RpLVNpcmVnYXIvaW1hZ2VzL2Jsb2IvbWFzdGVyL2xvZ28ucG5nP3Jhdz10cnVlIikNCmBgYA0KDQojIEdlbmVyYWwgT3B0aW1pemF0aW9uDQoNCkluIG1hdGhlbWF0aWNzLCBub25saW5lYXIgcHJvZ3JhbW1pbmcgKE5MUCkgaXMgdGhlIHByb2Nlc3Mgb2Ygc29sdmluZyBhbiBvcHRpbWl6YXRpb24gcHJvYmxlbSB3aGVyZSBzb21lIG9mIHRoZSBjb25zdHJhaW50cyBvciB0aGUgb2JqZWN0aXZlIGZ1bmN0aW9uIGFyZSBub25saW5lYXIuIEFuIG9wdGltaXphdGlvbiBwcm9ibGVtIGlzIG9uZSBvZiBjYWxjdWxhdGlvbiBvZiB0aGUgZXh0cmVtYSAobWF4aW1hLCBtaW5pbWEgb3Igc3RhdGlvbmFyeSBwb2ludHMpIG9mIGFuIG9iamVjdGl2ZSBmdW5jdGlvbiBvdmVyIGEgc2V0IG9mIHVua25vd24gcmVhbCB2YXJpYWJsZXMgYW5kIGNvbmRpdGlvbmFsIHRvIHRoZSBzYXRpc2ZhY3Rpb24gb2YgYSBzeXN0ZW0gb2YgZXF1YWxpdGllcyBhbmQgaW5lcXVhbGl0aWVzLCBjb2xsZWN0aXZlbHkgdGVybWVkIGNvbnN0cmFpbnRzLiBJdCBpcyB0aGUgc3ViLWZpZWxkIG9mIG1hdGhlbWF0aWNhbCBvcHRpbWl6YXRpb24gdGhhdCBkZWFscyB3aXRoIHByb2JsZW1zIHRoYXQgYXJlIG5vdCBsaW5lYXIuIA0KDQojIyBUd28tZGltZW5zaW9uYWwgZnVuY3Rpb24NCg0KVGhlIGJsdWUgcmVnaW9uIChzaG93biBpbiB0aGUgZGlhZ3JhbSBiZWxvdykgaXMgdGhlIGZlYXNpYmxlIHJlZ2lvbi4gVGhlIHRhbmdlbmN5IG9mIHRoZSBsaW5lIHdpdGggdGhlIGZlYXNpYmxlIHJlZ2lvbiByZXByZXNlbnRzIHRoZSBzb2x1dGlvbi4gVGhlIGxpbmUgaXMgdGhlIGJlc3QgYWNoaWV2YWJsZSBjb250b3VyIGxpbmUgKGFyZWEgd2l0aCBhIGdpdmVuIHZhbHVlIG9mIHRoZSBvYmplY3RpdmUgZnVuY3Rpb24pLiANCg0KYGBge3IsIGVjaG89RkFMU0UsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9IjItZGltZW5zaW9uYWwgZXhhbXBsZSIsIG91dC53aWR0aCA9ICc1MCUnfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9CYWt0aS1TaXJlZ2FyL2ltYWdlcy9tYXN0ZXIvTm9ubGluZWFyX3Byb2dyYW1taW5nLnN2ZyIpDQpgYGANCg0KVGhpcyBzaW1wbGUgcHJvYmxlbSBjYW4gYmUgZGVmaW5lZCBieSB0aGUgY29uc3RyYWludHM6DQoNCiQkDQpcYmVnaW57ZXFuYXJyYXl9DQp4XzEgJiBcZ2UgJiAwIFxcDQp4XzIgJiBcZ2UgJiAwIFxcDQp4XzFeMiArIHhfMl4yICYgXGdlICYgMSBcXA0KeF8xXjIgKyB4XzJeMiAmIFxsZSAmIDINClxlbmR7ZXFuYXJyYXl9DQokJA0Kd2l0aCBhbiBvYmplY3RpdmUgZnVuY3Rpb24gdG8gYmUgbWF4aW1pemVkDQoNCiQkDQpmKHgpID0geF8xICsgeF8yICwgXHRleHR7d2hlcmUgfSB4ID0gKHhfMSwgeF8yKS4NCiQkDQoNCiMjIFRocmVlLWRpbWVuc2lvbmFsIGZ1bmN0aW9uDQoNClRoZSB0YW5nZW5jeSAoc2VlIGRpYWdyYW0gYmVsbG93KSBvZiB0aGUgdG9wIHN1cmZhY2Ugd2l0aCB0aGUgY29uc3RyYWluZWQgc3BhY2UgaW4gdGhlIGNlbnRlciByZXByZXNlbnRzIHRoZSBzb2x1dGlvbi4NCg0KYGBge3IsIGVjaG89RkFMU0UsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9IjMtZGltZW5zaW9uYWwgZXhhbXBsZSIsIG91dC53aWR0aCA9ICc1MCUnfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9CYWt0aS1TaXJlZ2FyL2ltYWdlcy9tYXN0ZXIvTm9ubGluZWFyX3Byb2dyYW1taW5nXzNELnN2ZyIpDQpgYGANCg0KVGhpcyBzaW1wbGUgcHJvYmxlbSBjYW4gYmUgZGVmaW5lZCBieSB0aGUgY29uc3RyYWludHM6DQoNCiQkDQpcYmVnaW57ZXFuYXJyYXl9DQp4XzFeMiDiiJIgeF8yXjIgKyB4XzNeMiBcbGUgMiBcXA0KeF8xXjIgKyB4XzJeMiArIHhfM14yIFxsZSAxMCBcXA0KXGVuZHtlcW5hcnJheX0NCiQkDQoNCndpdGggYW4gb2JqZWN0aXZlIGZ1bmN0aW9uIHRvIGJlIG1heGltaXplZA0KDQokJGYoeCkgPSB4XzF4XzIgKyB4XzJ4XzMsIFx0ZXh0e3doZXJlIH0geCA9ICh4XzEsIHhfMiwgeF8zKS4kJA0KDQoNCiMgU3RlZXBlc3QgRGVzY2VudCAoU0QpDQoNClRoZSBtZXRob2Qgb2Ygc3RlZXBlc3QgZGVzY2VudCB3b3JrcyBvbiBmdW5jdGlvbnMgd2hpY2ggaGF2ZSBhIHNpbmdsZSBkZXJpdmF0aXZlLiBJdCBpcyB1c2VkIG1vc3Qgb2Z0ZW4gaW4gcHJvYmxlbXMgaW52b2x2aW5nIG1vcmUgdGhhbiAxIHZhcmlhYmxlLiBUaGUgZXNzZW50aWFsIGlkZWEgb2Ygc3RlZXBlc3QgZGVzY2VudCBpcyB0aGF0IHRoZSBmdW5jdGlvbiBkZWNyZWFzZXMgbW9zdCBxdWlja2x5IGluIHRoZSBkaXJlY3Rpb24gb2YgdGhlIG5lZ2F0aXZlIGdyYWRpZW50LiBMZXQncyBhc3N1bWUgd2UgaGF2ZSB0aGUgZm9sbG93aW5nIGZ1bmN0aW9uOg0KDQokJGYoeCk9Zih4XzEseF8yLFxjZG90cywgeF9uKSQkDQoNClRoZSBvYmplY3RpdmUgaXMgdG8gZmluZCB0aGUgbWF4aW11bSBvciBtaW5pbXVtIHZhbHVlIChhY2NvcmRpbmcgdG8gb3VyIHB1cnBvc2UpLg0KDQoNCiMjIFNEIEFsZ29yaXRobQ0KDQoqIFRoZSBtZXRob2Qgc3RhcnRzIGF0IGFuIGluaXRpYWwgZ3Vlc3MgJHgkLg0KKiBUaGUgbmV4dCBndWVzcyBpcyBtYWRlIGJ5IG1vdmluZyBpbiB0aGUgZGlyZWN0aW9uIG9mIHRoZSBuZWdhdGl2ZSBncmFkaWVudC4gVGhlIGxvY2F0aW9uIG9mIHRoZSBtaW5pbXVtIGFsb25nIHRoaXMgbGluZSBjYW4gdGhlbiBiZSBmb3VuZCBieSB1c2luZyBhIG9uZS1kaW1lbnNpb25hbCBzZWFyY2ggYWxnb3JpdGhtIHN1Y2ggYXMgZ29sZGVuIHNlY3Rpb24gc2VhcmNoLg0KKiBUaGUgbnRoIHVwZGF0ZSBpcyB0aGVuDQoNCiQkeF9uPXhfe27iiJIxfeKIklxhbHBoYSBmJyh4X3tu4oiSMX0pJCQNCg0Kd2hlcmUgJFxhbHBoYSQgaXMgY2hvc2VuIHRvIG1pbmltaXplIHRoZSBvbmUtZGltZW5zaW9uYWwgZnVuY3Rpb246DQoNCiQkZyhcYWxwaGEpPWYoeF97buKIkjF94oiSXGFscGhhIGYnKHhfe27iiJIxfSkpJCQNCg0KSW4gb3JkZXIgdG8gdXNlIGdvbGRlbiBzZWN0aW9uLCB3ZSBuZWVkIHRvIGFzc3VtZSB0aGF0ICRcYWxwaGEkIGlzIGluIGFuIGludGVydmFsLiBTbyBpbiB0aGlzIGNhc2UsIHdlIHRha2UgdGhlIGludGVydmFsIHRvIGJlICRbMCxoXSQgd2hlcmUgJGgkIGlzIGEgdmFsdWUgdGhhdCB3ZSBuZWVkIHRvIGNob29zZS4NCg0KDQojIyBTRCBBbGdvcml0aG0gaW4gUg0KDQpgYGB7ciBlY2hvPVRSVUV9DQpzdGVlcGVzdGRlc2NlbnQgPC0gZnVuY3Rpb24oZiwgZnByaW1lLCBzdGFydCwgaCwNCiB0b2w9MWUtNywgbWF4aXRlcj0xMDApIHsNCiB4IDwtIHN0YXJ0DQogZyA8LSBmdW5jdGlvbihhbHBoYSkgeyBmKHggLSBhbHBoYSpmcHgpIH0NCiBuaXRlciA8LSAwDQogd2hpbGUobml0ZXIgPCBtYXhpdGVyICYgc3VtKGFicyhmcHJpbWUoeCkpKSA+IHRvbCkgew0KIGZweCA8LSBmcHJpbWUoeCkNCiBhbHBoYSA8LSBnb2xkZW4oZywgMCwgaCkNCiB4IDwtIHggLSBhbHBoYSpmcHgNCiBuaXRlciA8LSBuaXRlciArIDENCiB9DQogaWYgKG5pdGVyID09IG1heGl0ZXIpIHsNCiBwcmludCgiV2FybmluZzogTWF4aW11bSBudW1iZXIgb2YgaXRlcmF0aW9ucyByZWFjaGVkIikNCiB9DQogYygiTWluaW1pemVyIiA9IHgpDQogfQ0KYGBgDQoNCg0KIyMgU0QgQWxnb3JpdGhtIGluIFZpc3VhbGl6YXRpb24NCg0KWW91IGNhbiBzZWUgdGhlIHZpc3VhbGl6YXRpb24gcHJvY2VzcyBvZiBTdGVlcGVzdCBEZXNjZW50IGluIHRoZSBmb2xsb3dpbmcgZ3JhcGhpYywgb3IgY2xpY2sgW2hlcmVdKGh0dHBzOi8vd3d3Lmdlb2dlYnJhLm9yZy9jbGFzc2ljL2VxN3J1dXFwKSB0byBzZWUgYW5vdGhlciBleGFtcGxlIG9mIFN0ZWVwZXN0IGRlc2NlbnQgbWV0aG9kLCBzcGVjaWZpY2FsbHkgZm9yIGEgcXVhZHJhdGljIGZ1bmN0aW9uLiBbbW9yZV0oaHR0cHM6Ly93d3cuMTIwMDAub3JnL215X25vdGVzL2FuaW1hdGVfc2VhcmNoL2luc3UyLmh0bSkNCg0KYGBge3IsIGVjaG89RkFMU0UsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9IjMtZGltZW5zaW9uYWwgZXhhbXBsZSIsIG91dC53aWR0aCA9ICc4MCUnfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImh0dHBzOi8vZ2l0aHViLmNvbS9CYWt0aS1TaXJlZ2FyL2ltYWdlcy9ibG9iL21hc3Rlci9TdGVlcGVzdC1kZXNjZW50LmdpZj9yYXc9dHJ1ZSIpDQpgYGANCg0KIyMgVHdvLWRpbWVuc2lvbmFsIEV4YW1wbGUNCg0KQSBzaW1wbGUgZXhhbXBsZSBvZiBhIGZ1bmN0aW9uIG9mIDIgdmFyaWFibGVzIHRvIGJlIG1pbmltaXplZCBpcw0KDQokJGYoeCk9Zih4XzEseF8yKT17KDLiiJJ4XzEpXjJcb3ZlciAyeF8yXjJ9ICsgeygz4oiSeF8xKV4yIFxvdmVyIDJ4XzJeMn0rbG9nKHhfMikkJA0KDQpOb3RlIHRoYXQgJHhfMiQgc2hvdWxkIGJlIHBvc2l0aXZlLCBzbyB3ZSBtaWdodCBuZWVkIHRvIHByb3RlY3QgYWdhaW5zdCBuZWdhdGl2ZSB2YWx1ZXMgb2YgJHhfMiQuDQoNCiogRmlyc3QgOiBXZSBuZWVkIGZ1bmN0aW9ucyBmb3IgYm90aCB0aGUgZnVuY3Rpb24gYW5kIHRoZSBncmFkaWVudA0KDQpgYGB7ciwgZWNobz1UUlVFfQ0KZjEgPC0gZnVuY3Rpb24oeCkgew0KICgyLXhbMV0pXjIvKDIqeFsyXV4yKSArKDMteFsxXSleMi8oMip4WzJdXjIpICsgbG9nKHhbMl0pDQogfQ0KIGYxcHJpbWUgPC0gZnVuY3Rpb24oeCkgew0KIGMoLSgyLXhbMV0pL3hbMl1eMiAtICgzLXhbMV0pL3hbMl1eMiwtKDIteFsxXSleMi94WzJdXjMgLSgzLXhbMV0pXjIveFsyXV4zICsgMS94WzJdKQ0KIH0NCmBgYA0KDQoNCiogU2Vjb25kOiBUaGUgbG9jYXRpb24gb2YgdGhlIG1pbmltdW0gYWxvbmcgdGhpcyBsaW5lIGNhbiB0aGVuIGJlIGZvdW5kIGJ5IHVzaW5nIGEgb25lLWRpbWVuc2lvbmFsIHNlYXJjaCBhbGdvcml0aG0gc3VjaCBhcyBnb2xkZW4gc2VjdGlvbiBzZWFyY2guDQoNCmBgYHtyfQ0KZ29sZGVuIDwtIGZ1bmN0aW9uIChmLCBhLCBiLCB0b2wgPSAwLjAwMDAwMDEpDQogew0KIHJhdGlvIDwtIDIgLyAoc3FydCg1KSArIDEpDQogeDEgPC0gYiAtIHJhdGlvICogKGIgLSBhKQ0KIHgyIDwtIGEgKyByYXRpbyAqIChiIC0gYSkNCiBmMSA8LSBmKHgxKQ0KIGYyIDwtIGYoeDIpDQoNCiB3aGlsZShhYnMoYiAtIGEpID4gdG9sKSB7DQogaWYgKGYyID4gZjEpIHsNCiBiIDwtIHgyDQogeDIgPC0geDENCiBmMiA8LSBmMQ0KIHgxIDwtIGIgLSByYXRpbyAqIChiIC0gYSkNCiBmMSA8LSBmKHgxKQ0KIH0gZWxzZSB7DQogYSA8LSB4MQ0KIHgxIDwtIHgyDQogZjEgPC0gZjINCiB4MiA8LSBhICsgcmF0aW8gKiAoYiAtIGEpDQogZjIgPC0gZih4MikNCiB9DQogfQ0KIHJldHVybigoYSArIGIpIC8gMikNCiB9DQpgYGANCg0KDQoNCiogVGhpcmQ6IExldCdzIHRyeSBhIHN0YXJ0aW5nIHZhbHVlIG9mIHg9KC4xLC4xKS4NCg0KYGBge3J9DQpzdGVlcGVzdGRlc2NlbnQoZjEsIGYxcHJpbWUsIHN0YXJ0PWMoLjEsLjEpLCBoPS4xKQ0KYGBgDQoNCldlIGhhdmVuJ3QgY29udmVyZ2VkIHlldC4NCg0KDQoqIEZvdXJ0aDogT25lIHBvc3NpYmlsaXR5IGlzIHRvIHJ1biB0aGUgcHJvY2VkdXJlIGFnYWluLCB1c2luZyB0aGUgbW9zdCByZWNlbnQgcmVzdWx0IGFzIG91ciBzdGFydGluZyBndWVzcy4NCg0KYGBge3J9DQpzdGVlcGVzdGRlc2NlbnQoZjEsIGYxcHJpbWUsc3RhcnQ9YygyLjQ5OTI5NjcsIDAuNzEyMzY3NSksIGg9LjEpDQpgYGANCg0KYGBge3J9DQpzdGVlcGVzdGRlc2NlbnQoZjEsIGYxcHJpbWUsIHN0YXJ0PWMoLjEsIC4xKSwgaD0uMSxtYXhpdGVyPTIwMCkNCmBgYA0KDQpEb25lLiBUaGUgdmFsdWUgaGFzIGJlZW4gY29udmVyZ2VkLg0KDQojIyBNdWx0aXZhcmlhdGUgTm9ybWFsIA0KDQpPbmUgY2FuIHVzZSBzdGVlcGVzdCBkZXNjZW50IHRvIGNvbXB1dGUgdGhlIG1heGltdW0gbGlrZWxpaG9vZCBlc3RpbWF0ZSBvZiB0aGUgbWVhbiBpbiBhIG11bHRpdmFyaWF0ZSBOb3JtYWwgZGVuc2l0eSwgZ2l2ZW4gYSBzYW1wbGUgb2YgZGF0YS4gSG93ZXZlciwgd2hlbiB0aGUgZGF0YSBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQsIGFzIHRoZXkgYXJlIGluIHRoZSBzaW11bGF0ZWQgZXhhbXBsZSBiZWxvdywgdGhlIGxvZy1saWtlbGlob29kIHN1cmZhY2UgY2FuIGJlIGNvbWUgZGlmZmljdWx0IHRvIG9wdGltaXplLiBJbiBzdWNoIGNhc2VzLCBhIHZlcnkgbmFycm93IHJpZGdlIGRldmVsb3BzIGluIHRoZSBsb2ctbGlrZWxpaG9vZCB0aGF0IGNhbiBiZSBkaWZmaWN1bHQgZm9yIHRoZSBzdGVlcGVzdCBkZXNjZW50IGFsZ29yaXRobSB0byBuYXZpZ2F0ZS4NCg0KSW4gdGhlIGV4YW1wbGUgYmVsb3csIHdlIGFjdHVhbGx5IGNvbXB1dGUgdGhlIG5lZ2F0aXZlIGxvZy1saWtlbGlob29kIGJlY2F1c2UgdGhlIGFsZ29yaXRobSBpcyBkZXNpZ25lZCB0byBtaW5pbWl6ZSBmdW5jdGlvbnMuDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMjAyMC0wOS0zMCkNCm11IDwtIGMoMSwgMikNClMgPC0gcmJpbmQoYygxLCAuOSksIGMoLjksIDEpKQ0KeCA8LSBNQVNTOjptdnJub3JtKDUwMCwgbXUsIFMpDQpubG9nbGlrZSA8LSBmdW5jdGlvbihtdTEsIG11Mikgew0KICAgICAgICBkbXYgPC0gbXZ0bm9ybTo6ZG12bm9ybSh4LCBjKG11MSwgbXUyKSwgUywgbG9nID0gVFJVRSkNCiAgICAgICAgLXN1bShkbXYpDQp9DQpubG9nbGlrZSA8LSBWZWN0b3JpemUobmxvZ2xpa2UsIGMoIm11MSIsICJtdTIiKSkNCm54IDwtIDQwDQpueSA8LSA0MA0KeGcgPC0gc2VxKC01LCA1LCBsZW4gPSBueCkNCnlnIDwtIHNlcSgtNSwgNiwgbGVuID0gbnkpDQpnIDwtIGV4cGFuZC5ncmlkKHhnLCB5ZykNCm5MTCA8LSBubG9nbGlrZShnWywgMV0sIGdbLCAyXSkNCnogPC0gbWF0cml4KG5MTCwgbngsIG55KQ0KcGFyKG1hciA9IGMoNC41LCA0LjUsIDEsIDEpKQ0KY29udG91cih4ZywgeWcsIHosIG5sZXZlbHMgPSA0MCwgeGxhYiA9IGV4cHJlc3Npb24obXVbMV0pLCANCiAgICAgICAgeWxhYiA9IGV4cHJlc3Npb24obXVbMl0pKQ0KYWJsaW5lKGggPSAwLCB2ID0gMCwgbHR5ID0gMikNCmBgYA0KDQpOb3RlIHRoYXQgaW4gdGhlIGZpZ3VyZSBhYm92ZSB0aGUgc3VyZmFjZSBpcyBoaWdobHkgc3RyZXRjaGVkIGFuZCB0aGF0IHRoZSBtaW5pbXVtICgxLDIpIGxpZXMgaW4gdGhlIG1pZGRsZSBvZiBhIG5hcnJvdyB2YWxsZXkuIEZvciB0aGUgc3RlZXBlc3QgZGVzY2VudCBhbGdvcml0aG0gd2Ugd2lsbCBzdGFydCBhdCB0aGUgcG9pbnQgKOKIkjUs4oiSMikgYW5kIHRyYWNrIHRoZSBwYXRoIG9mIHRoZSBhbGdvcml0aG0uDQogIA0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyLCB3YXJuLmNvbmZsaWN0cyA9IEZBTFNFKQ0Kbm9ybSA8LSBmdW5jdGlvbih4KSB4IC8gc3FydChzdW0oeF4yKSkNClNpbnYgPC0gc29sdmUoUykgICAgICAgICAgICAgICAgICAgICAgICAgICMjIEkga25vdyBJIHNhaWQgbm90IHRvIGRvIHRoaXMhDQpzdGVwMSA8LSBmdW5jdGlvbihtdSwgYWxwaGEgPSAxKSB7DQogICAgICAgIEQgPC0gc3dlZXAoeCwgMiwgbXUsICItIikNCiAgICAgICAgc2NvcmUgPC0gY29sU3VtcyhEKSAlPiUgbm9ybQ0KICAgICAgICBtdSArIGFscGhhICogZHJvcChTaW52ICUqJSBzY29yZSkNCn0NCnN0ZWVwIDwtIGZ1bmN0aW9uKG11LCBuID0gMTAsIC4uLikgew0KICAgICAgICByZXN1bHRzIDwtIHZlY3RvcigibGlzdCIsIGxlbmd0aCA9IG4pDQogICAgICAgIGZvcihpIGluIHNlcV9sZW4obikpIHsNCiAgICAgICAgICAgICAgICByZXN1bHRzW1tpXV0gPC0gc3RlcDEobXUsIC4uLikNCiAgICAgICAgICAgICAgICBtdSA8LSByZXN1bHRzW1tpXV0NCiAgICAgICAgfQ0KICAgICAgICByZXN1bHRzDQp9DQptIDwtIGRvLmNhbGwoInJiaW5kIiwgc3RlZXAoYygtNSwgLTIpLCA4KSkNCm0gPC0gcmJpbmQoYygtNSwgLTIpLCBtKQ0KDQpwYXIobWFyID0gYyg0LjUsIDQuNSwgMSwgMSkpDQpjb250b3VyKHhnLCB5ZywgeiwgbmxldmVscyA9IDQwLCB4bGFiID0gZXhwcmVzc2lvbihtdVsxXSksIA0KICAgICAgICB5bGFiID0gZXhwcmVzc2lvbihtdVsyXSkpDQphYmxpbmUoaCA9IDAsIHYgPSAwLCBsdHkgPSAyKQ0KcG9pbnRzKG0sIHBjaCA9IDIwLCB0eXBlID0gImIiKQ0KYGBgDQoNCiAgDQpXZSBjYW4gc2VlIHRoYXQgdGhlIHBhdGggb2YgdGhlIGFsZ29ydGhtIGlzIHJhdGhlciB3aW5kaW5nIGFzIGl0IHRyYXZlcnNlcyB0aGUgbmFycm93IHZhbGxleS4gTm93LCB3ZSBoYXZlIGZpeGVkIHRoZSBzdGVwLWxlbmd0aCBpbiB0aGlzIGNhc2UsIHdoaWNoIGlzIHByb2JhYmx5IG5vdCBvcHRpbWFsLiBIb3dldmVyLCBvbmUgY2FuIHN0aWxsIHNlZSB0aGF0IHRoZSBhbGdvcml0aG0gaGFzIHNvbWUgZGlmZmljdWx0eSBuYXZpZ2F0aW5nIHRoZSBzdXJmYWNlIGJlY2F1c2UgdGhlIGRpcmVjdGlvbiBvZiBzdGVlcGVzdCBkZXNjZW50IGRvZXMgbm90IHRha2Ugb25lIGRpcmVjdGx5IHRvd2FyZHMgdGhlIG1pbmltdW0gZXZlci4NCg0KIyBUaGUgTmV3dG9uIERpcmVjdGlvbg0KDQpOZXd0b24ncyBtZXRob2QgaXMgYW4gaXRlcmF0aXZlIG1ldGhvZCBmb3IgZmluZGluZyB0aGUgcm9vdHMgb2YgYSBkaWZmZXJlbnRpYWJsZSBmdW5jdGlvbiBGLCB3aGljaCBhcmUgc29sdXRpb25zIHRvIHRoZSBlcXVhdGlvbiAkRih4KSA9IDAuJCBJbiBvcHRpbWl6YXRpb24sIE5ld3RvbidzIG1ldGhvZCBpcyBhcHBsaWVkIHRvIHRoZSBkZXJpdmF0aXZlICRmJCDigLIgb2YgYSB0d2ljZS1kaWZmZXJlbnRpYWJsZSBmdW5jdGlvbiAkZiQgdG8gZmluZCB0aGUgcm9vdHMgb2YgdGhlIGRlcml2YXRpdmUgKHNvbHV0aW9ucyB0byAkZiDigLIoeCkgPSAwJCwgYWxzbyBrbm93biBhcyB0aGUgc3RhdGlvbmFyeSBwb2ludHMgb2YgJGYkLiBUaGVzZSBzb2x1dGlvbnMgbWF5IGJlIG1pbmltYSwgbWF4aW1hLCBvciBzYWRkbGUgcG9pbnRzLg0KVGhlIGNlbnRyYWwgcHJvYmxlbSBvZiBvcHRpbWl6YXRpb24gaXMgbWluaW1pemF0aW9uIG9mIGZ1bmN0aW9ucy4gTGV0IHVzIGZpcnN0IGNvbnNpZGVyIHRoZSBjYXNlIG9mIHVuaXZhcmlhdGUgZnVuY3Rpb25zLCBpLmUuLCBmdW5jdGlvbnMgb2YgYSBzaW5nbGUgcmVhbCB2YXJpYWJsZS4gV2Ugd2lsbCBsYXRlciBjb25zaWRlciB0aGUgbW9yZSBnZW5lcmFsIGFuZCBtb3JlIHByYWN0aWNhbGx5IHVzZWZ1bCBtdWx0aXZhcmlhdGUgY2FzZS4NCg0KR2l2ZW4gYSB0d2ljZSBkaWZmZXJlbnRpYWJsZSBmdW5jdGlvbiAke1xkaXNwbGF5c3R5bGUgZjpcbWF0aGJiIHtSfSBcdG8gXG1hdGhiYiB7Un0gfXtcZGlzcGxheXN0eWxlIGY6XG1hdGhiYiB7Un0gXHRvIFxtYXRoYmIge1J9IH0sJCB3ZSBzZWVrIHRvIHNvbHZlIHRoZSBvcHRpbWl6YXRpb24gcHJvYmxlbQ0KDQoke1xkaXNwbGF5c3R5bGUgXG1pbiBfe3hcaW4gXG1hdGhiYiB7Un0gfWYoeCkufXtcZGlzcGxheXN0eWxlIFxtaW4gX3t4XGluIFxtYXRoYmIge1J9IH1mKHgpLn0kDQoNCk5ld3RvbidzIG1ldGhvZCBhdHRlbXB0cyB0byBzb2x2ZSB0aGlzIHByb2JsZW0gYnkgY29uc3RydWN0aW5nIGEgc2VxdWVuY2UgJHtcZGlzcGxheXN0eWxlIFx7eF97a31cfX0kIGZyb20gYW4gaW5pdGlhbCBndWVzcyAoc3RhcnRpbmcgcG9pbnQpICR7XGRpc3BsYXlzdHlsZSB4X3swfVxpbiBcbWF0aGJiIHtSfSB9JCB0aGF0IGNvbnZlcmdlcyB0b3dhcmRzIGEgbWluaW1pemVyICR4XyokIG9mICRmJCBieSB1c2luZyBhIHNlcXVlbmNlIG9mIHNlY29uZC1vcmRlciBUYXlsb3IgYXBwcm94aW1hdGlvbnMgb2YgJHtcZGlzcGxheXN0eWxlIGZ9JCBhcm91bmQgdGhlIGl0ZXJhdGVzLiBUaGUgc2Vjb25kLW9yZGVyIFRheWxvciBleHBhbnNpb24gb2YgZiBhcm91bmQgJHhfayQgaXMgDQoNCiQke1xkaXNwbGF5c3R5bGUgZih4X3trfSt0KVxhcHByb3ggZih4X3trfSkrZicoeF97a30pdCt7XGZyYWMgezF9ezJ9fWYnJyh4X3trfSl0XnsyfS59e1xkaXNwbGF5c3R5bGUgZih4X3trfSt0KVxhcHByb3ggZih4X3trfSkrZicoeF97a30pdCt7XGZyYWMgezF9ezJ9fWYnJyh4X3trfSl0XnsyfS59JCQNClRoZSBuZXh0IGl0ZXJhdGUgJHtcZGlzcGxheXN0eWxlIHhfe2srMX19JCBpcyBkZWZpbmVkIHNvIGFzIHRvIG1pbmltaXplIHRoaXMgcXVhZHJhdGljIGFwcHJveGltYXRpb24gaW4gJHtcZGlzcGxheXN0eWxlIHR9JCwgYW5kIHNldHRpbmcgJHtcZGlzcGxheXN0eWxlIHhfe2srMX09eF97a30rdH0kLiBJZiB0aGUgc2Vjb25kIGRlcml2YXRpdmUgaXMgcG9zaXRpdmUsIHRoZSBxdWFkcmF0aWMgYXBwcm94aW1hdGlvbiBpcyBhIGNvbnZleCBmdW5jdGlvbiBvZiAke1xkaXNwbGF5c3R5bGUgdH0kLCBhbmQgaXRzIG1pbmltdW0gY2FuIGJlIGZvdW5kIGJ5IHNldHRpbmcgdGhlIGRlcml2YXRpdmUgdG8gemVyby4gU2luY2UNCg0KJCR7XGRpc3BsYXlzdHlsZSBcZGlzcGxheXN0eWxlIDA9e1xmcmFjIHtccm0ge2R9fXt7XHJtIHtkfX10fX1cbGVmdChmKHhfe2t9KStmJyh4X3trfSl0K3tcZnJhYyB7MX17Mn19ZicnKHhfe2t9KXReezJ9XHJpZ2h0KT1mJyh4X3trfSkrZicnKHhfe2t9KXQsfSQkDQp0aGUgbWluaW11bSBpcyBhY2hpZXZlZCBmb3INCg0KJCR7XGRpc3BsYXlzdHlsZSB0PS17XGZyYWMge2YnKHhfe2t9KX17ZicnKHhfe2t9KX19Ln0kJA0KUHV0dGluZyBldmVyeXRoaW5nIHRvZ2V0aGVyLCBOZXd0b24ncyBtZXRob2QgcGVyZm9ybXMgdGhlIGl0ZXJhdGlvbg0KDQokJHtcZGlzcGxheXN0eWxlIHhfe2srMX09eF97a30rdD14X3trfS17XGZyYWMge2YnKHhfe2t9KX17ZicnKHhfe2t9KX19Ln0kJA0KDQojIyBOZXd0b24gaW4gVmlzdWFsaXphdGlvbg0KQSBjb21wYXJpc29uIG9mIGdyYWRpZW50IGRlc2NlbnQgKGdyZWVuKSBhbmQgTmV3dG9uJ3MgbWV0aG9kIChyZWQpIGZvciBtaW5pbWl6aW5nIGEgZnVuY3Rpb24gKHdpdGggc21hbGwgc3RlcCBzaXplcykuIE5ld3RvbidzIG1ldGhvZCB1c2VzIGN1cnZhdHVyZSBpbmZvcm1hdGlvbiB0byB0YWtlIGEgbW9yZSBkaXJlY3Qgcm91dGUuDQpgYGB7cn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJuZXd0b24ucG5nIikNCmBgYA0KDQojIyBHZW9tZXRyaWMgaW50ZXJwcmV0YXRpb24NClRoZSBnZW9tZXRyaWMgaW50ZXJwcmV0YXRpb24gb2YgTmV3dG9uJ3MgbWV0aG9kIGlzIHRoYXQgYXQgZWFjaCBpdGVyYXRpb24sIGl0IGFtb3VudHMgdG8gdGhlIGZpdHRpbmcgb2YgYSBwYXJhYm9sb2lkIHRvIHRoZSBzdXJmYWNlIG9mICR7XGRpc3BsYXlzdHlsZSBmKHgpfSQgYXQgdGhlIHRyaWFsIHZhbHVlICR4X3trfSQsIGhhdmluZyB0aGUgc2FtZSBzbG9wZXMgYW5kIGN1cnZhdHVyZSBhcyB0aGUgc3VyZmFjZSBhdCB0aGF0IHBvaW50LCBhbmQgdGhlbiBwcm9jZWVkaW5nIHRvIHRoZSBtYXhpbXVtIG9yIG1pbmltdW0gb2YgdGhhdCBwYXJhYm9sb2lkIChpbiBoaWdoZXIgZGltZW5zaW9ucywgdGhpcyBtYXkgYWxzbyBiZSBhIHNhZGRsZSBwb2ludCkuDQoNCiMjIEhpZ2hlciBkaW1lbnNpb25zDQpUaGUgYWJvdmUgaXRlcmF0aXZlIHNjaGVtZSBjYW4gYmUgZ2VuZXJhbGl6ZWQgdG8gJGQkIGRpbWVuc2lvbnMgYnkgcmVwbGFjaW5nIHRoZSBkZXJpdmF0aXZlIHdpdGggdGhlIGdyYWRpZW50IChkaWZmZXJlbnQgYXV0aG9ycyB1c2UgZGlmZmVyZW50IG5vdGF0aW9uIGZvciB0aGUgZ3JhZGllbnQsIGluY2x1ZGluZyAke1xkaXNwbGF5c3R5bGUgZicoeCk9XG5hYmxhIGYoeCk9Z197Zn0oeClcaW4gXG1hdGhiYiB7Un0gXntkfX0kLCBhbmQgdGhlIHJlY2lwcm9jYWwgb2YgdGhlIHNlY29uZCBkZXJpdmF0aXZlIHdpdGggdGhlIGludmVyc2Ugb2YgdGhlIEhlc3NpYW4gbWF0cml4IChkaWZmZXJlbnQgYXV0aG9ycyB1c2UgZGlmZmVyZW50IG5vdGF0aW9uIGZvciB0aGUgSGVzc2lhbiwgaW5jbHVkaW5nICR7XGRpc3BsYXlzdHlsZSBmJycoeCk9XG5hYmxhIF57Mn1mKHgpPUhfe2Z9KHgpXGluIFxtYXRoYmIge1J9IF57ZFx0aW1lcyBkfX0kLiBPbmUgdGh1cyBvYnRhaW5zIHRoZSBpdGVyYXRpdmUgIHNjaGVtZSAke1xkaXNwbGF5c3R5bGUgeF97aysxfT14X3trfS1bZicnKHhfe2t9KV1eey0xfWYnKHhfe2t9KSxrXGdlcSAwLn0kDQpPZnRlbiBOZXd0b24ncyBtZXRob2QgaXMgbW9kaWZpZWQgdG8gaW5jbHVkZSBhIHNtYWxsIHN0ZXAgc2l6ZSAke1xkaXNwbGF5c3R5bGUgMDxcZ2FtbWEgXGxlcSAxfSQgaW5zdGVhZCBvZiAke1xkaXNwbGF5c3R5bGUgXGdhbW1hID0xfSQNCiQke1xkaXNwbGF5c3R5bGUgeF97aysxfT14X3trfS1cZ2FtbWEgW2YnJyh4X3trfSldXnstMX1mJyh4X3trfSkufSQkIFRoaXMgaXMgb2Z0ZW4gZG9uZSB0byBlbnN1cmUgdGhhdCB0aGUgV29sZmUgY29uZGl0aW9ucyBhcmUgc2F0aXNmaWVkIGF0IGVhY2ggc3RlcCBvZiB0aGUgbWV0aG9kLiBGb3Igc3RlcCBzaXplcyBvdGhlciB0aGFuIDEsIHRoZSBtZXRob2QgaXMgb2Z0ZW4gcmVmZXJyZWQgdG8gYXMgdGhlIHJlbGF4ZWQgb3IgZGFtcGVkIE5ld3RvbidzIG1ldGhvZC4NCg0KIyMgQ29udmVyZ2VuY2UNCklmICRmJCBpcyBhIHN0cm9uZ2x5IGNvbnZleCBmdW5jdGlvbiB3aXRoIExpcHNjaGl0eiBIZXNzaWFuLCB0aGVuIHByb3ZpZGVkIHRoYXQgJHhfezB9JCBpcyBjbG9zZSBlbm91Z2ggdG8gJHtcZGlzcGxheXN0eWxlIHhfeyp9PVxhcmcgXG1pbiBmKHgpfSQsIHRoZSBzZXF1ZW5jZSAke1xkaXNwbGF5c3R5bGUgeF97MH0seF97MX0seF97Mn0sXGRvdHMgfSQgIGdlbmVyYXRlZCBieSBOZXd0b24ncyBtZXRob2Qgd2lsbCBjb252ZXJnZSB0byB0aGUgKG5lY2Vzc2FyaWx5IHVuaXF1ZSkgbWluaW1pemVyICR4X3sqfSQgb2YgJGYkIHF1YWRyYXRpY2FsbHkgZmFzdCBUaGF0IGlzLA0KJCR7XGRpc3BsYXlzdHlsZSBcfHhfe2srMX0teF97Kn1cfFxsZXEge1xmcmFjIHsxfXsyfX1cfHhfe2t9LXhfeyp9XHxeezJ9LFxxcXVhZCBcZm9yYWxsIGtcZ2VxIDAufSQkDQoNCiMjIENvbXB1dGluZyB0aGUgTmV3dG9uIGRpcmVjdGlvbg0KRmluZGluZyB0aGUgaW52ZXJzZSBvZiB0aGUgSGVzc2lhbiBpbiBoaWdoIGRpbWVuc2lvbnMgdG8gY29tcHV0ZSB0aGUgTmV3dG9uIGRpcmVjdGlvbiAkaD0tKGYnJyh4X3trfSkpXnstMX1mJyh4X3trfSkkIGNhbiBiZSBhbiBleHBlbnNpdmUgb3BlcmF0aW9uLiBJbiBzdWNoIGNhc2VzLCBpbnN0ZWFkIG9mIGRpcmVjdGx5IGludmVydGluZyB0aGUgSGVzc2lhbiwgaXQncyBiZXR0ZXIgdG8gY2FsY3VsYXRlIHRoZSB2ZWN0b3IgJGgkIGFzIHRoZSBzb2x1dGlvbiB0byB0aGUgc3lzdGVtIG9mIGxpbmVhciBlcXVhdGlvbnMgJCR7XGRpc3BsYXlzdHlsZSBbZicnKHhfe2t9KV1oPS1mJyh4X3trfSl9JCQNCndoaWNoIG1heSBiZSBzb2x2ZWQgYnkgdmFyaW91cyBmYWN0b3JpemF0aW9ucyBvciBhcHByb3hpbWF0ZWx5IChidXQgdG8gZ3JlYXQgYWNjdXJhY3kpIHVzaW5nIGl0ZXJhdGl2ZSBtZXRob2RzLiBNYW55IG9mIHRoZXNlIG1ldGhvZHMgYXJlIG9ubHkgYXBwbGljYWJsZSB0byBjZXJ0YWluIHR5cGVzIG9mIGVxdWF0aW9ucywgZm9yIGV4YW1wbGUgdGhlIENob2xlc2t5IGZhY3Rvcml6YXRpb24gYW5kIGNvbmp1Z2F0ZSBncmFkaWVudCB3aWxsIG9ubHkgd29yayBpZiAkZicnKHhfe2t9KSQgaXMgYSBwb3NpdGl2ZSBkZWZpbml0ZSBtYXRyaXguIFdoaWxlIHRoaXMgbWF5IHNlZW0gbGlrZSBhIGxpbWl0YXRpb24sIGl0J3Mgb2Z0ZW4gYSB1c2VmdWwgaW5kaWNhdG9yIG9mIHNvbWV0aGluZyBnb25lIHdyb25nOyBmb3IgZXhhbXBsZSBpZiBhIG1pbmltaXphdGlvbiBwcm9ibGVtIGlzIGJlaW5nIGFwcHJvYWNoZWQgYW5kICRmJycoeF97a30pJCBpcyBub3QgcG9zaXRpdmUgZGVmaW5pdGUsIHRoZW4gdGhlIGl0ZXJhdGlvbnMgYXJlIGNvbnZlcmdpbmcgdG8gYSBzYWRkbGUgcG9pbnQgYW5kIG5vdCBhIG1pbmltdW0uDQpPbiB0aGUgb3RoZXIgaGFuZCwgaWYgYSBjb25zdHJhaW5lZCBvcHRpbWl6YXRpb24gaXMgZG9uZSAoZm9yIGV4YW1wbGUsIHdpdGggTGFncmFuZ2UgbXVsdGlwbGllcnMpLCB0aGUgcHJvYmxlbSBtYXkgYmVjb21lIG9uZSBvZiBzYWRkbGUgcG9pbnQgZmluZGluZywgaW4gd2hpY2ggY2FzZSB0aGUgSGVzc2lhbiB3aWxsIGJlIHN5bW1ldHJpYyBpbmRlZmluaXRlIGFuZCB0aGUgc29sdXRpb24gb2YgJHhfe2srMX0kICB3aWxsIG5lZWQgdG8gYmUgZG9uZSB3aXRoIGEgbWV0aG9kIHRoYXQgd2lsbCB3b3JrIGZvciBzdWNoLCBzdWNoIGFzIHRoZSAkTERMJCB2YXJpYW50IG9mIENob2xlc2t5IGZhY3Rvcml6YXRpb24gb3IgdGhlIGNvbmp1Z2F0ZSByZXNpZHVhbCBtZXRob2QuIFRoZXJlIGFsc28gZXhpc3QgdmFyaW91cyBxdWFzaS1OZXd0b24gbWV0aG9kcywgd2hlcmUgYW4gYXBwcm94aW1hdGlvbiBmb3IgdGhlIEhlc3NpYW4gKG9yIGl0cyBpbnZlcnNlIGRpcmVjdGx5KSBpcyBidWlsdCB1cCBmcm9tIGNoYW5nZXMgaW4gdGhlIGdyYWRpZW50LiBJZiB0aGUgSGVzc2lhbiBpcyBjbG9zZSB0byBhIG5vbi1pbnZlcnRpYmxlIG1hdHJpeCwgdGhlIGludmVydGVkIEhlc3NpYW4gY2FuIGJlIG51bWVyaWNhbGx5IHVuc3RhYmxlIGFuZCB0aGUgc29sdXRpb24gbWF5IGRpdmVyZ2UuIEluIHRoaXMgY2FzZSwgY2VydGFpbiB3b3JrYXJvdW5kcyBoYXZlIGJlZW4gdHJpZWQgaW4gdGhlIHBhc3QsIHdoaWNoIGhhdmUgdmFyaWVkIHN1Y2Nlc3Mgd2l0aCBjZXJ0YWluIHByb2JsZW1zLiBPbmUgY2FuLCBmb3IgZXhhbXBsZSwgbW9kaWZ5IHRoZSBIZXNzaWFuIGJ5IGFkZGluZyBhIGNvcnJlY3Rpb24gbWF0cml4ICRCX2skIHNvIGFzIHRvIG1ha2UgJGYnJyh4X3trfSkkKyRCX3trfSQgcG9zaXRpdmUgZGVmaW5pdGUuIE9uZSBhcHByb2FjaCBpcyB0byBkaWFnb25hbGl6ZSB0aGUgSGVzc2lhbiBhbmQgY2hvb3NlICRCX3trfSQgc28gdGhhdCAkZicnKHhfe2t9KSQrJEJfayQgaGFzIHRoZSBzYW1lIGVpZ2VudmVjdG9ycyBhcyB0aGUgSGVzc2lhbiwgYnV0IHdpdGggZWFjaCBuZWdhdGl2ZSBlaWdlbnZhbHVlIHJlcGxhY2VkIGJ5ICR7XGVwc2lsb24gPjB9JA0KQW4gYXBwcm9hY2ggZXhwbG9pdGVkIGluIHRoZSBMZXZlbmJlcmfigJNNYXJxdWFyZHQgYWxnb3JpdGhtICh3aGljaCB1c2VzIGFuIGFwcHJveGltYXRlIEhlc3NpYW4pIGlzIHRvIGFkZCBhIHNjYWxlZCBpZGVudGl0eSBtYXRyaXggdG8gdGhlIEhlc3NpYW4sICR7XG11IEl9JCwgd2l0aCB0aGUgc2NhbGUgYWRqdXN0ZWQgYXQgZXZlcnkgaXRlcmF0aW9uIGFzIG5lZWRlZC4gRm9yIGxhcmdlICR7XG11IH0kICBhbmQgc21hbGwgSGVzc2lhbiwgdGhlIGl0ZXJhdGlvbnMgd2lsbCBiZWhhdmUgbGlrZSBncmFkaWVudCBkZXNjZW50IHdpdGggc3RlcCBzaXplICAkezEvXG11IH0kIC4gVGhpcyByZXN1bHRzIGluIHNsb3dlciBidXQgbW9yZSByZWxpYWJsZSBjb252ZXJnZW5jZSB3aGVyZSB0aGUgSGVzc2lhbiBkb2Vzbid0IHByb3ZpZGUgdXNlZnVsIGluZm9ybWF0aW9uLg0KDQojIyBFeGFtcGxlcyBvZiBOZXd0b24NCkJlZm9yZSBwcm9jZWVkaW5nIHRvIGFuIGltcGxlbWVudGF0aW9uIG9mIHRoZSBOZXd0b24tUmFwaHNvbiBtZXRob2QgaW4gUiwgaXQgaXMgd29ydGggd29ya2luZyB0aHJvdWdoIHNvbWUgZXhhbXBsZXMgdG8gZ2V0IGFuIHVuZGVyc3RhbmRpbmcgb2YgdGhlIGRlZmluaXRpb25zIGFuZCBlcXVhdGlvbnMgYWJvdmUuIFRoZSBOUiBtZXRob2QgY2FuIGJlIHVzZWQgdG8gYXBwcm94aW1hdGUgc3F1YXJlIHJvb3RzIHN1Y2ggYXMgJOKImjEwLiQgVGhlIHNxdWFyZSByb290IG9mIDEwIGlzIGFib3V0IHRocmVlLCBzbyB3ZSBjYW4gdXNlIHRoYXQgYXMgYSBnb29kIHN0YXJ0aW5nIHZhbHVlLiBJdCBvZnRlbiBoZWxwcyB0byBwbG90IHRoZSBmdW5jdGlvbiB0byBzZWUgd2hlcmUgdGhlIHJvb3RzIG9jY3VyLiBUaGUgZnVuY3Rpb24gaXMgZmlyc3QgcmVhcnJhbmdlZCB0byBiZSBhbiBleHByZXNzaW9uIG9mICRmKHgpJCBiZWZvcmUgcGxvdHRpbmcuICQkeD3iiJoxMCQkDQokJHjiiJLiiJoxMD0wJCQNCiQkZih4KT14MuKIkjEwJCQNClBsb3R0aW5nIHRoZSBmdW5jdGlvbiB5aWVsZHMgdGhlIGdyYXBoIGJlbG93Lg0KYGBge3J9DQpmdW5jIDwtIGZ1bmN0aW9uKHgpIHt4XjIgLSAxMH0NCmN1cnZlKGZ1bmMsIGNvbCA9ICdyZWQnLCBsd2QgPSAyLCBmcm9tID0gMCwgbiA9IDEwMCwgeGxpbT1jKDAsIDUpLCB5bGFiPSdmKHgpJykNCmFibGluZShhPTAsIGI9MCwgbHR5ID0gNSkNCmBgYA0KSXQgY2FuIGJlIHNlZW4gZnJvbSB0aGUgZ3JhcGggdGhlIGZ1bmN0aW9uIGNyb3NzZXMgdGhlIHgtYXhpcyBhdCAk4oiaMTAkIG9uIGFuIGludGVydmFsIFszLCA0XS4gVGhlIGRlcml2YXRpdmUgb2YgdGhlIGZ1bmN0aW9uIGlzICQyeCQuIEFuZCBzbyBvbiB1bnRpbCB0aGUgeG4gZXN0aW1hdGVzIGFyZSB3aXRoaW4gYSBwYXJ0aWN1bGFyIGxldmVsIG9mIHRvbGVyYW5jZS4gVGhpcyBleGFtcGxlIGNvbnZlcmdlcyBpbiB0aHJlZSBpdGVyYXRpb25zLiBUaHVzIDMuMTYyMjc4IGlzIHRoZSBlc3RpbWF0ZWQgcm9vdCBvZiB0aGUgZnVuY3Rpb24uIFRoaXMgcmVzdWx0IGNhbiBiZSB2ZXJpZmllZCBieSBzaW1wbHkgdGFraW5nIHRoZSBzcXVhcmUgcm9vdCBvZiAxMC4NCmBgYHtyfQ0Kc3FydCgxMCkNCmBgYA0KIyMgTmV3dG9uIE1ldGhvZCBpbiBSDQoNClRoZSAkdW5pcm9vdCQgZnVuY3Rpb24gaW4gUiBwcm92aWRlcyBhbiBpbXBsZW1lbnRhdGlvbiBvZiBOZXd0b24tUmFwaHNvbiBmb3IgZmluZGluZyB0aGUgcm9vdCBvZiBhbiBlcXVhdGlvbi4gVGhlIGZ1bmN0aW9uIGlzIG9ubHkgY2FwYWJsZSBvZiBmaW5kaW5nIG9uZSByb290IGluIHRoZSBnaXZlbiBpbnRlcnZhbC4gVGhlICRyb290U29sdmUkIHBhY2thZ2UgZmVhdHVyZXMgdGhlIHVuaXJvb3QuYWxsIGZ1bmN0aW9uIHdoaWNoIGV4dGVuZHMgdGhlIHVuaXJvb3Qgcm91dGluZSB0byBkZXRlY3QgbXVsdGlwbGUgcm9vdHMgc2hvdWxkIHRoZXkgZXhpc3QuDQoNClRoZSBlcXVhdGlvbiBmb3Igd2hpY2ggd2Ugd2lzaCB0byBmaW5kIHRoZSByb290IGlzOg0KJCRmKHgpPXheM+KIkjJ44oiSNSQkDQpGaXJzdCwgd3JpdGUgYSBmdW5jdGlvbiB0byBleHByZXNzIHRoZSBlcXVhdGlvbiBhYm92ZS4NCmBgYHtyfQ0KZnVuYzIgPC0gZnVuY3Rpb24oeCkge3heMyAtIDIqIHggLSA1fQ0KYGBgDQpQbG90IHRoZSBmdW5jdGlvbiB0byB2aXN1YWxpemUgaG93IHRoZSBlcXVhdGlvbiBiZWhhdmVzIGFuZCB3aGVyZSBhbnkgcm9vdHMgbWF5IGJlIGxvY2F0ZWQuDQpgYGB7cn0NCmN1cnZlKGZ1bmMyLCB4bGltPWMoLTUsNSksIGNvbD0nYmx1ZScsIGx3ZD0yLCBsdHk9MiwgeWxhYj0nZih4KScpDQphYmxpbmUoaD0wKQ0KYWJsaW5lKHY9MCkNCmBgYA0KSXQgbG9va3MgbGlrZSB0aGUgZnVuY3Rpb24gZXF1YWxzIDAgd2hlbiB5IGlzIGFib3V0IDIuIFRvIGZpbmQgdGhlIHJvb3Qgb2YgdGhlIGVxdWF0aW9uLCB1c2UgdGhlIHVuaXJvb3QgZnVuY3Rpb24gd2l0aCBhIHN0YXJ0aW5nIHZhbHVlIG9mIDIgYW5kIHVwcGVyIGJvdW5kIG9mIDMuDQpgYGB7cn0NCnVuaXJvb3QoZnVuYzIsIGMoMiwzKSkNCmBgYA0KQXMgc3VzcGVjdGVkLCB0aGUgcm9vdCBvZiB0aGUgZnVuY3Rpb24gaXMgdmVyeSBjbG9zZSB0byAyIGF0IDIuMDk0NTQxMi4gVGhlIGl0ZXJhdGlvbnMgY2FuIGJlIHdyaXR0ZW4gYXMgZm9sbG93czoNCg0KJCR4X3tuKzF9PXhfbi17XGZyYWMge2YoeF97bn0pfXtmJyh4X3tufSl9fT14X24te1xmcmFjIHtmKHheM197bn0tMngtNSl9ezN4XjJfbi0yfX0kJA0KJCR4XzE9e1xmcmFjIHsoMileMy0yKDIpLTV9ezMoMileMi0yfX09MS4yODEyMzkkJA0KJCR4XzI9e1xmcmFjIHsoMS4yODEyMzkpXjMtMigxLjI4MTIzOSktNX17MygxLjI4MTIzOSleMi0yfX09My4xNDc4MjEkJA0KJCR4XzM9e1xmcmFjIHsoMy4xNDc4MjEpXjMtMigzLjE0NzgyMSktNX17MygzLjE0NzgyMSleMi0yfX09Mi40MzAyNTckJA0KJCR4XzQ9e1xmcmFjIHsoMi40MzAyNTcpXjMtMigyLjQzMDI1NyktNX17MygyLjQzMDI1NyleMi0yfX09Mi4xNDQ0MTgkJA0KJCR4XzU9e1xmcmFjIHsoMi4xNDQ0MTgpXjMtMigyLjE0NDQxOCktNX17MygyLjE0NDQxOCleMi0yfX09Mi4wOTU4OTckJA0KJCR4XzU9e1xmcmFjIHsoMi4wOTU4OTcpXjMtMigyLjA5NTg5NyktNX17MygyLjA5NTg5NyleMi0yfX09Mi4wOTQ1NTIkJA0KVGhlIG1hbnVhbCBjYWxjdWxhdGlvbnMgZXF1YWwgdGhlIG91dHB1dCBvZiB0aGUgZnVuY3Rpb24gdG8gZm91ciBkZWNpbWFscy4gVGhlIG1pbnVzY3VsZSBkaWZmZXJlbmNlIGJldHdlZW4gcmVzdWx0cyBpcyBsaWtlbHkgdGhlIGNhdXNlIG9mIHJvdW5kb2ZmIGFuZCBzaWduaWZpY2FuY2UgZXJyb3JzIGluIHRoZSBtYW51YWwgY2FsY3VsYXRpb24uIEEgcXVpY2sgYW5kIGRpcnR5IGZ1bmN0aW9uIHRvIHBlcmZvcm0gdGhlIG1ldGhvZCBpbiBSIGNhbiBiZSBpbXBsZW1lbnRlZCB0byBmdXJ0aGVyIHZlcmlmeSBvdXIgdW5kZXJzdGFuZGluZyBvZiB0aGUgTmV3dG9uLVJhcGhzb24gbWV0aG9kLiBUaGUgbnVtRGVyaXYgcGFja2FnZSBpcyB1c2VkIHRvIGNvbXB1dGUgdGhlIGRlcml2YXRpdmUgJGbigLIoeCkkLCB0aG91Z2ggdGhpcyBjb3VsZCBhbHNvIGJlIGRvbmUgYnkgdGFraW5nIHRoZSBsaW1pdCB3aXRoIGEgc3VmZmljaWVudGx5IHNtYWxsICRoJC4NCmBgYHtyfQ0KbmV3dG9uLnJhcGhzb24gPC0gZnVuY3Rpb24oZiwgYSwgYiwgdG9sID0gMWUtNSwgbiA9IDEwMDApIHsNCiAgcmVxdWlyZShudW1EZXJpdikgIyBQYWNrYWdlIGZvciBjb21wdXRpbmcgZicoeCkNCiAgDQogIHgwIDwtIGEgIyBTZXQgc3RhcnQgdmFsdWUgdG8gc3VwcGxpZWQgbG93ZXIgYm91bmQNCiAgayA8LSBuICMgSW5pdGlhbGl6ZSBmb3IgaXRlcmF0aW9uIHJlc3VsdHMNCiAgDQogICMgQ2hlY2sgdGhlIHVwcGVyIGFuZCBsb3dlciBib3VuZHMgdG8gc2VlIGlmIGFwcHJveGltYXRpb25zIHJlc3VsdCBpbiAwDQogIGZhIDwtIGYoYSkNCiAgaWYgKGZhID09IDAuMCkgew0KICAgIHJldHVybihhKQ0KICB9DQogIA0KICBmYiA8LSBmKGIpDQogIGlmIChmYiA9PSAwLjApIHsNCiAgICByZXR1cm4oYikNCiAgfQ0KDQogIGZvciAoaSBpbiAxOm4pIHsNCiAgICBkeCA8LSBnZW5EKGZ1bmMgPSBmLCB4ID0geDApJERbMV0gIyBGaXJzdC1vcmRlciBkZXJpdmF0aXZlIGYnKHgwKQ0KICAgIHgxIDwtIHgwIC0gKGYoeDApIC8gZHgpICMgQ2FsY3VsYXRlIG5leHQgdmFsdWUgeDENCiAgICBrW2ldIDwtIHgxICMgU3RvcmUgeDENCiAgICAjIE9uY2UgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB4MCBhbmQgeDEgYmVjb21lcyBzdWZmaWNpZW50bHkgc21hbGwsIG91dHB1dCB0aGUgcmVzdWx0cy4NCiAgICBpZiAoYWJzKHgxIC0geDApIDwgdG9sKSB7DQogICAgICByb290LmFwcHJveCA8LSB0YWlsKGssIG49MSkNCiAgICAgIHJlcyA8LSBsaXN0KCdyb290IGFwcHJveGltYXRpb24nID0gcm9vdC5hcHByb3gsICdpdGVyYXRpb25zJyA9IGspDQogICAgICByZXR1cm4ocmVzKQ0KICAgIH0NCiAgICAjIElmIE5ld3Rvbi1SYXBoc29uIGhhcyBub3QgeWV0IHJlYWNoZWQgY29udmVyZ2VuY2Ugc2V0IHgxIGFzIHgwIGFuZCBjb250aW51ZQ0KICAgIHgwIDwtIHgxDQogIH0NCiAgcHJpbnQoJ1RvbyBtYW55IGl0ZXJhdGlvbnMgaW4gbWV0aG9kJykNCn0NCmBgYA0KQ29tcHV0aW5nIHRoZSByb290IG9mIHRoZSBhYm92ZSBlcXVhdGlvbiB3aXRoIHRoZSBuZXd0b24ucmFwaHNvbiBmdW5jdGlvbiB5aWVsZHM6DQpgYGB7cn0NCmxpYnJhcnkgKG51bURlcml2KQ0KbmV3dG9uLnJhcGhzb24oZnVuYzIsIDIsIDMpDQpgYGANClRoZSBjdXN0b20gTmV3dG9uLVJhcGhzb24gZnVuY3Rpb24gaXMgc2xpZ2h0bHkgb2ZmIGZyb20gdGhlIHVuaXJvb3QgcmVzdWx0LCBob3dldmVyOyB0aGV5IGFyZSBlcXVhbCB1cCB0byA0IGRlY2ltYWxzIGFuZCBpcyBhZGVxdWF0ZSBmb3Igb3VyIHB1cnBvc2VzLg0KDQojU3VtbWFyeQ0KVGhlIE5ld3Rvbi1SYXBoc29uIG1ldGhvZCBpcyBhIHBvd2VyZnVsIGFuZCByZWxhdGl2ZWx5IHN0cmFpZ2h0Zm9yd2FyZCBtZXRob2QgZm9yIGZpbmRpbmcgdGhlIHJvb3RzIG9mIGFuIGVxdWF0aW9uLiBJdCBoYXMgbWFueSBhZHZhbnRhZ2VzIGJ1dCBzdWZmZXJzIGZyb20gc2V2ZXJhbCBkcmF3YmFja3Mgc3VjaCBhcyBhIHJlYWRpbHkgY2FsY3VsYXRlZCBkZXJpdmF0aXZlLCBpbmNvbnNpc3RlbmNpZXMgd2hlbiAkZuKAsih4KT0wJCBhbmQgc28gb24uIEFzIGV4cGxvcmVkIGluIHRoZSBwb3N0LCBpdCBvZnRlbiBtYWtlcyBzZW5zZSBmaXJzdCB0byBwbG90IHRoZSBmdW5jdGlvbiBpbiBxdWVzdGlvbiB0byB2aXN1YWxpemUgaG93IGl0IGJlaGF2ZXMgYmVmb3JlIGF0dGVtcHRpbmcgdG8gbG9jYXRlIHRoZSAkcm9vdChzKSQgYXMgYmFkIHN0YXJ0aW5nIHZhbHVlcyBjYW4gYmUgZGV0cmltZW50YWwgdG8gcmVzdWx0cy4gT3RoZXIgbnVtZXJpY2FsIG1ldGhvZHMgZm9yIGVzdGltYXRpbmcgcm9vdHMgb2YgZXF1YXRpb25zIHN1Y2ggYXMgdGhlIEJpc2VjdGlvbiwgU2VjYW50IGFuZCBCcmVudOKAmXMgbWV0aG9kcyB3aWxsIGJlIGV4YW1pbmVkIGluIGZ1dHVyZSBwb3N0cy4NCg0KDQo=