MCMC is a general purpose technique for sampling from complex probabilistic models.

  1. Monte Carlo

  2. Markov Chain

  3. Metropolis Hasting

1. Motivation

If you need to calculate the mean or expectation of a function \(f(\theta)\) of a random variable \(\theta\) which has a complicated posterior pdf \(p(\theta |y)\).

\[E[f(\theta)] = \int p(\theta|y)f(\theta)d\theta\]

You might not know how to do the integration.

or you need to calculate the max of of a posterior probability distribution \(p(\theta)\).

\[\arg \max p(\theta|y)\]

One way of solving the expectation is to draw \(N\) random samples from \(p(\theta)\) and when \(N\) is sufficiently large we can approximate the expectation or the max by

\[E[f(\theta)] \approx \frac{1}{N} \sum\limits_{i=1}^{N}f(\theta_i)\]

apply the same strategy to finding \(\arg \max p(\theta|y)\) by sampling from \(p(\theta|y)\) and taking the max value in the set of samples.


1.1 Solution

1.1 simulate directly

1.2 inversed cdf

1.3 reject/accepte sampling

If we do not know exact/normalized pdf or it is very complicated, MCMC comes in handy.


1.3 Markov Chains & Detailed Balance

\[p(X_n|X_{n-1},X_{n-2},X_{n-3}...,X_{1}) = p(X_n|X_{n-1})\]

To simulate a Markov chain we must formulate a transition kernel, \(T(x_i,x_j)\). The transition kernel is the probability of moving from a state \(x_i\) to a state \(x_j\).

Convergence for a Markov Chain means that it has a stationary distribution, \(\pi\). A stationary distribution implies that if we run the Markov Chain repeatedly, for long enough time, the samples we get for each run will always form the same distribution.

Metropolis algorithm

Detailed balance is a sufficient but not necessary condition for a Markov chain to be stationary. Detailed balance essentially says that

the probability of being in state x and transitioning to state x' must be equal to the probability of being in state x' and transitioning to state x

\[{\displaystyle T(x'|x)P(x)=T(x|x')P(x')} \tag 1 \]

or

\[{\displaystyle {\frac {T(x'|x)}{T(x|x')}}={\frac {P(x')}{P(x)}}}.\]

The approach is to separate the transition in two sub-steps; the proposal and the acceptance-rejection.

Let \(q(x'|x)\) denote the candidate-generating density, we can adjust \(q\) by using a probability of move \(\alpha(x'|x)\).

The proposal distribution \({\displaystyle \displaystyle q(x'|x)}\) is the conditional probability of proposing a state \(x'\) given \(x\),

and the acceptance distribution \({ \alpha(x'|x)}\) the conditional probability to accept the proposed state \(x'\).

we design the acceptance probability function so that detailed balance is satisfied. 

The transition probability can be written as the product of them:

\[{\displaystyle T(x'|x)≡q(x'|x)A(x'|x)} . \]

Inserting this relation in the previous equation, we have

\[{\displaystyle {\frac {\alpha(x'|x)}{\alpha(x|x')}}={\frac {P(x')}{P(x)}}{\frac {q(x|x')}{q(x'|x)}}} .\]

The Metropolis-Hastings Algorithm - Jason Blevins

The choice of \(A\) follows the following logic. If

\[ P(x) q(x'|x)> P(x') q(x|x') \]

holds, then moves from \(x\) to \(x'\) are happening too often under \(q\). We should thus choose \(\alpha(x|x') = 1\). But then, in order to satisfy (1) Detailed balance, we must have

\[ P(x) q(x'|x) \alpha(x'|x) = P(x') q(x|x') \alpha(x|x') \]

\[ P(x) q(x'|x) \alpha(x'|x) = P(x') q(x|x') \]

The next step in the derivation is to choose an acceptance that fulfils the condition above. One common choice is the Metropolis choice:

\[{\displaystyle \alpha(x'|x)=\min \left(1,{\frac {P(x')}{P(x)}}{\frac {q(x|x')}{q(x'|x)}}\right)}\]

i.e., we always accept when the acceptance is bigger than 1, and we reject accordingly when the acceptance is smaller than 1. This is the required quantity for the algorithm. The Metropolis–Hastings algorithm thus consists in the following:

  1. Initialisation: pick an initial state x at random;

  2. randomly pick a new state \(x'\) according to \({ \displaystyle q(x'|x)}\);

3.accept the state according to \({ \displaystyle \alpha(x'|x)}\). If not accepted, transition doesn’t take place, and so there is no need to update anything. Else, the system transits to x’;

4.go to 2 until T states were generated;

5.save the state x, go to 2.

The saved states are in principle drawn from the distribution \({\displaystyle P(x)}\), as step 4 ensures they are de-correlated. The value of T must be chosen according to different factors such as the proposal distribution and, formally, it has to be of the order of the autocorrelation time of the Markov process.[13] It is important to notice that it is not clear, in a general problem, which distribution \({ \displaystyle q(x'|x)}\) one should use; it is a free parameter of the method which has to be adjusted to the particular problem in hand.


1.4 Property

Another interesting property of the Metropolis–Hastings algorithm that adds to its appeal is that it only depends on the ratios

\[ \frac{P(x')}{P(x)}\]

is the probability (e.g., Bayesian posterior) ratio between the proposed sample \({\displaystyle x'\,}\) , and the previous sample \({\displaystyle x_{t}\,}\), and

\[\frac{q(x|x')}{q(x'|x)}\]

is the ratio of the proposal density in two directions (from \({\displaystyle x_{t}\,}\), to \({\displaystyle x'\,}\) and vice versa). This is equal to 1 if the proposal density is symmetric.

Note that \(A(x'|x)\) does not require knowledge of the normalizing constant because it drops out of the ratio \(\frac{P(x')}{P(x)}\).

The Markov chain is started from an arbitrary initial value \({\displaystyle \displaystyle x_{0}}\) and the algorithm is run for many iterations until this initial state is “forgotten”. These samples, which are discarded, are known as burn-in. The remaining set of accepted values of \({\displaystyle x}\) represent a sample from the distribution \({\displaystyle P(x)}\)


1.5 Converge

If the domain explored by q (its support) is too small, compared with the range of f, the Markov chain will have difficulties in exploring this range and thus will converge very slowly (if at all for practical purposes).


2. A simple Metropolis sampler

2.1 A simple Metropolis-Hastings independence sampler

Let’s look at simulating from a gamma target distribution with arbitrary shape and scale parameters,using a Metropolis-Hastings independence sampling algorithm with normal proposal distribution with the same mean and variance as the desired gamma.

A function for the Metropolis-Hastings sampler for this problem is given below. The chain is initialised at zero, and at each stage a N(a/b,a/(b*b)) candidate is proposed.

\[q(x'|x) \sim N(a/b,a/(b*b))\]

Metropolis-Hastings independence sampler for a gamma based on normal candidates/instrumental/proposal/jumping distribution with the same mean and variance

  1. Start in some state \(x_t\). x in code.
  2. Propose a new state \(x^\prime\) candidate in code
  3. Compute the “acceptance probability” \[\alpha(x'|x) = \min\left[1, \frac{dgamma(x^\prime, a, b)*dnorm(x, mu, sig)}{dgamma(x, a, b)*dnorm(x^\prime, mu, sig)} \right]\]
  4. Draw some uniformly distributed random number \(u\) from \([0,1]\); if \(u < \alpha\) accept the point, setting \(x_{t+1} = x^\prime\). Otherwise reject it and set \(x_{t+1} = x_t\).
MH visualization

MH visualization

Figure: A visualization of the Metropolis-Hastings MCMC, from Hartig et al., 2011. (copyright see publisher)

set.seed(123)
gamm<-function (n, a, b){
        mu <- a/b
        sig <- sqrt(a/(b * b))
        vec <- vector("numeric", n)
        x <- 3*a/b
        vec[1] <- x
        for (i in 2:n) {
                can <- rnorm(1, mu, sig)
                aprob <- min(1, (dgamma(can, a, b)/dgamma(x, 
                        a, b))/(dnorm(can, mu, sig)/dnorm(x, 
                        mu, sig)))
                u <- runif(1)
                if (u < aprob) 
                        x <- can
                vec[i] <- x
        }
        return(vec)
}

2.2 Plots

Set parameters.

nrep<- 55000
burnin<- 5000
shape<- 2.3
rate<-2.7
vec<-gamm(nrep,shape, rate)

Modify the plots below so they apply only to the chain AFTER the burn-in period

vec=vec[-(1:burnin)]
#vec=vec[burnin:length(vec)]
par(mfrow=c(2,1)) # change main frame, how many graphs in one frame
plot(ts(vec), xlab="Chain", ylab="Draws")
abline(h = mean(vec), lwd="2", col="red" )
hist(vec,30, prob=TRUE, xlab="Red Line = mean", col="grey", main="Simulated Density")
abline(v = mean(vec), lwd="2", col="red" )
par(mfrow=c(1,1)) # go back to default


2.3 summary

summary(vec[-(1:burnin)]);  
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
0.007013 0.435600 0.724800 0.843300 1.133000 3.149000 
var(vec[-(1:burnin)])
[1] 0.2976507

2.4 Initial Value

The first sample in vec is the initial /start value for our chain. We can change it to see if the convergence changes.

        x <- 3*a/b
        vec[1] <- x

2.5 Choose proposal

The algorithm works best if the proposal density matches the shape of the target distribution \(P(x)\) from which direct sampling is difficult, that is \({\displaystyle q(x'|x_{t})\approx P(x')\,\!}\). If a Gaussian proposal density \({q}\) is used, the variance parameter \(\sigma^{2}\) has to be tuned during the burn-in period.

This is usually done by calculating the acceptance rate, which is the fraction of proposed samples that is accepted in a window of the last \(N\) samples.

The desired acceptance rate depends on the target distribution, however it has been shown theoretically that the ideal acceptance rate for a one-dimensional Gaussian distribution is approx 50%, decreasing to approx 23% for an \(N\)-dimensional Gaussian target distribution.

If \(\sigma^{2}\) is too small the chain will mix slowly (i.e., the acceptance rate will be high but successive samples will move around the space slowly and the chain will converge only slowly to \(\displaystyle P(x)\)).

On the other hand, if \(\displaystyle \sigma^{2}\) is too large the acceptance rate will be very low because the proposals are likely to land in regions of much lower probability density, so \(\displaystyle a_{1}\) will be very small and again the chain will converge very slowly.


3. Sample 2: Bayesian Estimation for Regression

  1. Check your understanding of the Metropolis-Hastings sampler that is being used here for Bayesian estimation of a regression model, using artificial data.

This code is a modified version of that presented at https://theoreticalecology.wordpress.com/2010/09/17/metropolis-hastings-mcmc-in-r/ (Modified by David Giles dgiles@uvic.ca, 21 March, 2016) This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.

\[y = Ax + B + u,\, u \sim N(0,sd)\]


3.1 Set parameter

trueA <- 5
trueB <- 0
trueSd <- 10
sampleSize <- 31

3.2 DGP and Plot

# create independent x-values, around zero
x <- (-(sampleSize-1)/2):((sampleSize-1)/2)
# create dependent values according to ax + b + N(0,sd)
y <-  trueA * x + trueB + rnorm(n=sampleSize,mean=0,sd=trueSd)
par(mfrow = c(1,1))
plot(x,y, main="Test Data")


3.3 likelihood from normal distribution

likelihood <- function(param){
    a = param[1]
    b = param[2]
    sd = param[3]
     
    pred = a*x + b
    singlelikelihoods = dnorm(y, mean = pred, sd = sd, log = T)
    sumll = sum(singlelikelihoods)
    return(sumll)   
}

3.4 Why work with logarithms

Return the logarithm of the probabilities in the likelihood function, which is also the reason why I sum the probabilities of all our datapoints (the logarithm of a product equals the sum of the logarithms).

Why do we do this? It’s strongly advisable because likelihoods, where a lot of small probabilities are multiplied, can get ridiculously small pretty fast (something like \(10^{-34}\)). At some stage, computer programs are getting into numerical rounding or underflow problems then.

So, bottom-line: when you program something with likelihoods, always use logarithms!!!


3.5 Example: plot the likelihood profile of the slope a

# Example: plot the likelihood profile of the slope a
slopevalues <- function(x){return(likelihood(c(x, trueB, trueSd)))}
slopelikelihoods <- lapply(seq(3, 7, by=.05), slopevalues )
plot (seq(3, 7, by=.05), slopelikelihoods , type="l", xlab = "values of slope parameter a", ylab = "Log likelihood")


3.6 Prior distribution

uniform distributions and normal distributions for all three parameters.

# Prior distribution
prior <- function(param){
    a = param[1]
    b = param[2]
    sd = param[3]
# CHANGE THE NEXT 3 LINES TO CHANGE THE PRIOR, log is True, so these are log density/likelihood
    aprior = dunif(a, min=0, max=10, log = T)
    bprior = dnorm(b, sd = 2, log = T)
    sdprior = dunif(sd, min=0, max=30, log = T)
    return(aprior+bprior+sdprior)
}

3.7 The posterior

The product of prior and likelihood is the actual quantity the MCMC will be working on. This function is called the posterior (or to be exact, it’s called the posterior after it’s normalized, which the MCMC will do for us, but let’s not be picky for the moment). Again, here we work with the sum because we work with logarithms.

posterior <- function(param){
   return (likelihood(param) + prior(param))
}

3.8 Metropolis algorithm

One of the most frequent applications of this algorithm (as in this example) is sampling from the posterior density in Bayesian statistics.

In principle, however, the algorithm may be used to sample from any integrable function. So, the aim of this algorithm is to jump around in parameter space, but in a way that the probability to be at a point is proportional to the function we sample from (this is usually called the target function).

In our case this is the posterior defined above.

  1. Starting at a random parameter value

  2. Choosing a new parameter value close to the old value based on some probability density that is called the proposal function

  3. Jumping to this new point with a probability p(new)/p(old), where p is the target function, and p>1 means jumping as well

  4. Note that we have a symmetric jumping/proposal distribution \(q(x'|x)\).

\[ q(x'|x) \sim N(x, c(0.1,0.5,0.3) )\]

The standard diveation \(\sigma\) are fixed. The \(q(x'|x) = q(x|x')\).

so the accept probablity equals to

\[\alpha(x'|x)=\min \left(1,\frac{P(x')}{P(x)} \right)\]

######## Metropolis algorithm ################
 
proposalfunction <- function(param){
    return(rnorm(3,mean = param, sd= c(0.1,0.5,0.3)))
}
 
run_metropolis_MCMC <- function(startvalue, iterations){
    chain = array(dim = c(iterations+1,3))
    chain[1,] = startvalue
    for (i in 1:iterations){
        proposal = proposalfunction(chain[i,])
         
        probab = exp(posterior(proposal) - posterior(chain[i,]))
        if (runif(1) < probab){
            chain[i+1,] = proposal
        }else{
            chain[i+1,] = chain[i,]
        }
    }
    return(chain)
}

Again, working with the logarithms of the posterior might be a bit confusing at first, in particular when you look at the line where the acceptance probability is calculated (probab = exp(posterior(proposal) - posterior(chain[i,]))). To understand why we do this, note that p1/p2 = exp[log(p1)-log(p2)].


3.9 Implementation

(e)Print the value of the quantity called acceptance, and interpret what it is telling you.

startvalue = c(4,0,10)
chain = run_metropolis_MCMC(startvalue, 55000)
#str(chain) 
burnIn = 5000
acceptance = 1-mean(duplicated(chain[-(1:burnIn),]))
#?duplicated

The first steps of the algorithm may be biased by the initial value, and are therefore usually discarded for the further analysis (burn-in time). An interesting output to look at is the acceptance rate: how often was a proposal rejected by the metropolis-hastings acceptance criterion? The acceptance rate can be influenced by the proposal function: generally, the closer the proposals are, the larger the acceptance rate. Very high acceptance rates, however, are usually not beneficial: this means that the algorithms is “staying” at the same point, which results in a suboptimal probing of the parameter space (mixing).

We also can change the intial / start value to see if it change the result/ if it change the convergence.

startvalue = c(4,0,10)

3.10 summary

summary(cbind(chain[-(1:burnIn),1],chain[-(1:burnIn),2],chain[-(1:burnIn),3]))
       V1              V2                V3        
 Min.   :4.068   Min.   :-6.7072   Min.   : 6.787  
 1st Qu.:4.913   1st Qu.:-2.6973   1st Qu.: 9.323  
 Median :5.052   Median :-1.7551   Median :10.178  
 Mean   :5.052   Mean   :-1.7377   Mean   :10.385  
 3rd Qu.:5.193   3rd Qu.:-0.8134   3rd Qu.:11.166  
 Max.   :5.989   Max.   : 4.8425   Max.   :19.223  
# for comparison:
summary(lm(y~x))

Call:
lm(formula = y ~ x)

Residuals:
    Min      1Q  Median      3Q     Max 
-22.259  -6.032  -1.718   6.955  19.892 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -3.1756     1.7566  -1.808    0.081 .  
x             5.0469     0.1964  25.697   <2e-16 ***
---
Signif. codes:  0 ?**?0.001 ?*?0.01 ??0.05 ??0.1 ??1

Residual standard error: 9.78 on 29 degrees of freedom
Multiple R-squared:  0.9579,    Adjusted R-squared:  0.9565 
F-statistic: 660.4 on 1 and 29 DF,  p-value: < 2.2e-16
summary(lm(y~x))$sigma
[1] 9.780494
coefficients(lm(y~x))[1]
(Intercept) 
  -3.175555 
coefficients(lm(y~x))[2]
       x 
5.046873 

3.11 Trace of chains:

### Summary: #######################
 
par(mfrow = c(2,3))
hist(chain[-(1:burnIn),1],prob=TRUE,nclass=30,col="109" , main="Posterior of a", xlab="Black=mean; Red=true; Magenta = MLE" )
abline(v = mean(chain[-(1:burnIn),1]), lwd="2")
abline(v = trueA, col="red", lwd="2" )
abline(v = coefficients(lm(y~x))[2], col="magenta", lwd="2" )
hist(chain[-(1:burnIn),2],prob=TRUE, nclass=30, col="green",main="Posterior of b", xlab="Black=mean; Red=true; Magenta = MLE")
abline(v = mean(chain[-(1:burnIn),2]), lwd="2")
abline(v = trueB, col="red", lwd="2" )
abline(v = coefficients(lm(y~x))[1], col="magenta", lwd="2" )
hist(chain[-(1:burnIn),3],prob=TRUE, nclass=30, col="yellow",main="Posterior of sd", xlab="Black=mean; Red=true; Magenta = MLE")
abline(v = mean(chain[-(1:burnIn),3]), lwd="2" )
abline(v = trueSd, col="red", lwd="2" )
abline(v = summary(lm(y~x))$sigma, col="magenta", lwd="2" )
plot(chain[-(1:burnIn),1], col="648",type = "l", xlab="True value = red line" , main = "Chain values of a" )
abline(h = trueA, col="red" )
plot(chain[-(1:burnIn),2], col="648",type = "l", xlab="True value = red line" , main = "Chain values of b" )
abline(h = trueB, col="red" )
plot(chain[-(1:burnIn),3], col="648",type = "l", xlab="True value = red line" , main = "Chain values of sd" )
abline(h = trueSd, col="red" )


Reference

CSC 446 Notes: Lecture 13

MCMC Design and Tricks

Why does the Metropolis-Hastings procedure satisfy the detailed balance criterion?

LS0tDQp0aXRsZTogIkVDT041NDYgTGFiMTAgTWV0cm9wb2xpcyBIYXN0aW5ncyINCmF1dGhvcjogIkpvbiBEdWFuIg0KZGF0ZTogIjIwMTctMDMtMjgiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpNQ01DIGlzIGEgZ2VuZXJhbCBwdXJwb3NlIHRlY2huaXF1ZSBmb3Igc2FtcGxpbmcgZnJvbSBjb21wbGV4IHByb2JhYmlsaXN0aWMgbW9kZWxzLg0KDQoxLiBNb250ZSBDYXJsbw0KDQoyLiBNYXJrb3YgQ2hhaW4NCg0KMy4gTWV0cm9wb2xpcyBIYXN0aW5nDQoNCg0KIyMgMS4gTW90aXZhdGlvbg0KDQoNCklmIHlvdSBuZWVkIHRvIGNhbGN1bGF0ZSB0aGUgbWVhbiBvciBleHBlY3RhdGlvbiBvZiBhIGZ1bmN0aW9uICRmKFx0aGV0YSkkIG9mIGEgcmFuZG9tIHZhcmlhYmxlICRcdGhldGEkIHdoaWNoIGhhcyBhIGNvbXBsaWNhdGVkIHBvc3RlcmlvciBwZGYgJHAoXHRoZXRhIHx5KSQuDQoNCiQkRVtmKFx0aGV0YSldID0gXGludCBwKFx0aGV0YXx5KWYoXHRoZXRhKWRcdGhldGEkJA0KDQpZb3UgbWlnaHQgbm90IGtub3cgaG93IHRvIGRvIHRoZSBpbnRlZ3JhdGlvbi4NCg0Kb3IgeW91IG5lZWQgdG8gY2FsY3VsYXRlIHRoZSBtYXggb2YgIG9mIGEgcG9zdGVyaW9yIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbiAgJHAoXHRoZXRhKSQuDQoNCiQkXGFyZyBcbWF4IHAoXHRoZXRhfHkpJCQNCg0KT25lIHdheSBvZiBzb2x2aW5nIHRoZSBleHBlY3RhdGlvbiBpcyB0byBkcmF3ICROJCByYW5kb20gc2FtcGxlcyBmcm9tICRwKFx0aGV0YSkkIGFuZCB3aGVuICROJCBpcyBzdWZmaWNpZW50bHkgbGFyZ2Ugd2UgY2FuIGFwcHJveGltYXRlIHRoZSBleHBlY3RhdGlvbiBvciB0aGUgbWF4IGJ5DQoNCiQkRVtmKFx0aGV0YSldIFxhcHByb3ggXGZyYWN7MX17Tn0gXHN1bVxsaW1pdHNfe2k9MX1ee059ZihcdGhldGFfaSkkJA0KDQphcHBseSB0aGUgc2FtZSBzdHJhdGVneSB0byBmaW5kaW5nICRcYXJnIFxtYXggcChcdGhldGF8eSkkIGJ5IHNhbXBsaW5nIGZyb20gJHAoXHRoZXRhfHkpJCBhbmQgdGFraW5nIHRoZSBtYXggdmFsdWUgaW4gdGhlIHNldCBvZiBzYW1wbGVzLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMS4xIFNvbHV0aW9uDQoNCjEuMSBzaW11bGF0ZSBkaXJlY3RseQ0KDQoxLjIgaW52ZXJzZWQgY2RmDQoNCjEuMyByZWplY3QvYWNjZXB0ZSBzYW1wbGluZw0KDQpJZiB3ZSBkbyBub3Qga25vdyBleGFjdC9ub3JtYWxpemVkIHBkZiBvciBpdCBpcyB2ZXJ5IGNvbXBsaWNhdGVkLCBNQ01DIGNvbWVzIGluIGhhbmR5Lg0KDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjIgVmlzdWFsaXphdGlvbg0KDQpbSW50cm9kdWN0aW9uIHRvIEJheWVzaWFuIFN0YXRpc3RpY3MsIHBhcnQgMjogTUNNQyBhbmQgdGhlIE1ldHJvcG9saXMgSGFzdGluZ3NdKGh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL3dhdGNoP3Y9T1RPMUR5Z0VMcFkmdD0yNHMpDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoNCiMjIyAxLjMgTWFya292IENoYWlucyAmIERldGFpbGVkIEJhbGFuY2UNCg0KDQoNCg0KJCRwKFhfbnxYX3tuLTF9LFhfe24tMn0sWF97bi0zfS4uLixYX3sxfSkgPSBwKFhfbnxYX3tuLTF9KSQkDQoNCg0KVG8gc2ltdWxhdGUgYSBNYXJrb3YgY2hhaW4gd2UgbXVzdCBmb3JtdWxhdGUgYSAqKnRyYW5zaXRpb24ga2VybmVsKiosICRUKHhfaSx4X2opJC4gVGhlIHRyYW5zaXRpb24ga2VybmVsIGlzIHRoZSBwcm9iYWJpbGl0eSBvZiBtb3ZpbmcgZnJvbSBhIHN0YXRlICR4X2kkIHRvIGEgc3RhdGUgJHhfaiQuDQoNCg0KKipDb252ZXJnZW5jZSoqIGZvciBhIE1hcmtvdiBDaGFpbiBtZWFucyB0aGF0IGl0IGhhcyBhIHN0YXRpb25hcnkgZGlzdHJpYnV0aW9uLCAkXHBpJC4gQSBzdGF0aW9uYXJ5IGRpc3RyaWJ1dGlvbiBpbXBsaWVzIHRoYXQgaWYgd2UgcnVuIHRoZSBNYXJrb3YgQ2hhaW4gcmVwZWF0ZWRseSwgZm9yIGxvbmcgZW5vdWdoIHRpbWUsIHRoZSBzYW1wbGVzIHdlIGdldCBmb3IgZWFjaCBydW4gd2lsbCBhbHdheXMgZm9ybSB0aGUgc2FtZSBkaXN0cmlidXRpb24uDQoNCg0KW01ldHJvcG9saXMgYWxnb3JpdGhtIF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTWV0cm9wb2xpcyVFMiU4MCU5M0hhc3RpbmdzX2FsZ29yaXRobSkNCg0KKipEZXRhaWxlZCBiYWxhbmNlKiogaXMgYSAqKnN1ZmZpY2llbnQgYnV0IG5vdCBuZWNlc3NhcnkgY29uZGl0aW9uKiogZm9yIGEgTWFya292IGNoYWluIHRvIGJlICoqc3RhdGlvbmFyeSoqLiBEZXRhaWxlZCBiYWxhbmNlIGVzc2VudGlhbGx5IHNheXMgdGhhdA0KDQogICAgdGhlIHByb2JhYmlsaXR5IG9mIGJlaW5nIGluIHN0YXRlIHggYW5kIHRyYW5zaXRpb25pbmcgdG8gc3RhdGUgeCcgbXVzdCBiZSBlcXVhbCB0byB0aGUgcHJvYmFiaWxpdHkgb2YgYmVpbmcgaW4gc3RhdGUgeCcgYW5kIHRyYW5zaXRpb25pbmcgdG8gc3RhdGUgeA0KDQoNCg0KDQokJHtcZGlzcGxheXN0eWxlIFQoeCd8eClQKHgpPVQoeHx4JylQKHgnKX0gXHRhZyAxICQkDQoNCm9yIA0KDQoNCiQke1xkaXNwbGF5c3R5bGUge1xmcmFjIHtUKHgnfHgpfXtUKHh8eCcpfX09e1xmcmFjIHtQKHgnKX17UCh4KX19fS4kJA0KDQoNClRoZSBhcHByb2FjaCBpcyB0byBzZXBhcmF0ZSB0aGUgdHJhbnNpdGlvbiBpbiB0d28gc3ViLXN0ZXBzOyB0aGUgcHJvcG9zYWwgYW5kIHRoZSBhY2NlcHRhbmNlLXJlamVjdGlvbi4gDQoNCg0KTGV0ICRxKHgnfHgpJCBkZW5vdGUgdGhlICoqY2FuZGlkYXRlLWdlbmVyYXRpbmcgZGVuc2l0eSoqLCB3ZSBjYW4gYWRqdXN0ICRxJCBieSB1c2luZyBhICoqcHJvYmFiaWxpdHkgb2YgbW92ZSoqICRcYWxwaGEoeCd8eCkkLiANCg0KVGhlICoqcHJvcG9zYWwgZGlzdHJpYnV0aW9uKiogJHtcZGlzcGxheXN0eWxlIFxkaXNwbGF5c3R5bGUgcSh4J3x4KX0kICBpcyB0aGUgY29uZGl0aW9uYWwgcHJvYmFiaWxpdHkgb2YgcHJvcG9zaW5nIGEgc3RhdGUgJHgnJCBnaXZlbiAkeCQsIA0KDQoNCmFuZCB0aGUgKiphY2NlcHRhbmNlIGRpc3RyaWJ1dGlvbioqICR7IFxhbHBoYSh4J3x4KX0kIHRoZSBjb25kaXRpb25hbCBwcm9iYWJpbGl0eSB0byBhY2NlcHQgdGhlIHByb3Bvc2VkIHN0YXRlICR4JyQuIA0KICAgIA0KICAgIHdlIGRlc2lnbiB0aGUgYWNjZXB0YW5jZSBwcm9iYWJpbGl0eSBmdW5jdGlvbiBzbyB0aGF0IGRldGFpbGVkIGJhbGFuY2UgaXMgc2F0aXNmaWVkLiANCiAgICANCg0KDQoNClRoZSAqKnRyYW5zaXRpb24gcHJvYmFiaWxpdHkqKiBjYW4gYmUgd3JpdHRlbiBhcyB0aGUgcHJvZHVjdCBvZiB0aGVtOg0KDQoNCg0KJCR7XGRpc3BsYXlzdHlsZSBUKHgnfHgp4omhcSh4J3x4KUEoeCd8eCl9IC4gJCQNCg0KSW5zZXJ0aW5nIHRoaXMgcmVsYXRpb24gaW4gdGhlIHByZXZpb3VzIGVxdWF0aW9uLCB3ZSBoYXZlDQoNCiQke1xkaXNwbGF5c3R5bGUge1xmcmFjIHtcYWxwaGEoeCd8eCl9e1xhbHBoYSh4fHgnKX19PXtcZnJhYyB7UCh4Jyl9e1AoeCl9fXtcZnJhYyB7cSh4fHgnKX17cSh4J3x4KX19fSAuJCQNCg0KDQpbVGhlIE1ldHJvcG9saXMtSGFzdGluZ3MgQWxnb3JpdGhtIC0gSmFzb24gQmxldmluc10oaHR0cDovL2pibGV2aW5zLm9yZy9ub3Rlcy9tZXRyb3BvbGlzLWhhc3RpbmdzKQ0KDQoNClRoZSBjaG9pY2Ugb2YgJEEkIGZvbGxvd3MgdGhlIGZvbGxvd2luZyBsb2dpYy4gSWYgDQoNCiQkIFAoeCkgcSh4J3x4KT4gUCh4JykgcSh4fHgnKSAgJCQNCg0KDQoNCmhvbGRzLCB0aGVuIG1vdmVzIGZyb20gJHgkIHRvICR4JyQgYXJlIGhhcHBlbmluZyB0b28gb2Z0ZW4gdW5kZXIgJHEkLiBXZSBzaG91bGQgdGh1cyBjaG9vc2UgICRcYWxwaGEoeHx4JykgPSAxJC4gQnV0IHRoZW4sIGluIG9yZGVyIHRvIHNhdGlzZnkgKDEpICoqRGV0YWlsZWQgYmFsYW5jZSoqLCB3ZSBtdXN0IGhhdmUNCg0KJCQgUCh4KSBxKHgnfHgpIFxhbHBoYSh4J3x4KSA9IFAoeCcpIHEoeHx4JykgXGFscGhhKHh8eCcpICAkJA0KDQoNCiQkIFAoeCkgcSh4J3x4KSBcYWxwaGEoeCd8eCkgPSBQKHgnKSBxKHh8eCcpICAgJCQNCg0KVGhlIG5leHQgc3RlcCBpbiB0aGUgZGVyaXZhdGlvbiBpcyB0byBjaG9vc2UgYW4gYWNjZXB0YW5jZSB0aGF0IGZ1bGZpbHMgdGhlIGNvbmRpdGlvbiBhYm92ZS4gT25lIGNvbW1vbiBjaG9pY2UgaXMgdGhlICoqTWV0cm9wb2xpcyBjaG9pY2UqKjoNCg0KJCR7XGRpc3BsYXlzdHlsZSBcYWxwaGEoeCd8eCk9XG1pbiBcbGVmdCgxLHtcZnJhYyB7UCh4Jyl9e1AoeCl9fXtcZnJhYyB7cSh4fHgnKX17cSh4J3x4KX19XHJpZ2h0KX0kJA0KDQoNCg0KaS5lLiwgd2UgYWx3YXlzIGFjY2VwdCB3aGVuIHRoZSBhY2NlcHRhbmNlIGlzIGJpZ2dlciB0aGFuIDEsIGFuZCB3ZSByZWplY3QgYWNjb3JkaW5nbHkgd2hlbiB0aGUgYWNjZXB0YW5jZSBpcyBzbWFsbGVyIHRoYW4gMS4gVGhpcyBpcyB0aGUgcmVxdWlyZWQgcXVhbnRpdHkgZm9yIHRoZSBhbGdvcml0aG0uDQpUaGUgTWV0cm9wb2xpc+KAk0hhc3RpbmdzIGFsZ29yaXRobSB0aHVzIGNvbnNpc3RzIGluIHRoZSBmb2xsb3dpbmc6DQoNCjEuIEluaXRpYWxpc2F0aW9uOiBwaWNrIGFuIGluaXRpYWwgc3RhdGUgeCBhdCByYW5kb207DQoNCjIuIHJhbmRvbWx5IHBpY2sgYSBuZXcgc3RhdGUgJHgnJCBhY2NvcmRpbmcgdG8gJHsgXGRpc3BsYXlzdHlsZSBxKHgnfHgpfSQ7DQoNCjMuYWNjZXB0IHRoZSBzdGF0ZSBhY2NvcmRpbmcgdG8gJHsgXGRpc3BsYXlzdHlsZSBcYWxwaGEoeCd8eCl9JC4gSWYgbm90IGFjY2VwdGVkLCB0cmFuc2l0aW9uIGRvZXNuJ3QgdGFrZSBwbGFjZSwgYW5kIHNvIHRoZXJlIGlzIG5vIG5lZWQgdG8gdXBkYXRlIGFueXRoaW5nLiBFbHNlLCB0aGUgc3lzdGVtIHRyYW5zaXRzIHRvIHgnOw0KDQo0LmdvIHRvIDIgdW50aWwgVCBzdGF0ZXMgd2VyZSBnZW5lcmF0ZWQ7DQoNCjUuc2F2ZSB0aGUgc3RhdGUgeCwgZ28gdG8gMi4NCg0KVGhlIHNhdmVkIHN0YXRlcyBhcmUgaW4gcHJpbmNpcGxlIGRyYXduIGZyb20gdGhlIGRpc3RyaWJ1dGlvbiAke1xkaXNwbGF5c3R5bGUgUCh4KX0kLCBhcyBzdGVwIDQgZW5zdXJlcyB0aGV5IGFyZSBkZS1jb3JyZWxhdGVkLiBUaGUgdmFsdWUgb2YgVCBtdXN0IGJlIGNob3NlbiBhY2NvcmRpbmcgdG8gZGlmZmVyZW50IGZhY3RvcnMgc3VjaCBhcyB0aGUgcHJvcG9zYWwgZGlzdHJpYnV0aW9uIGFuZCwgZm9ybWFsbHksIGl0IGhhcyB0byBiZSBvZiB0aGUgb3JkZXIgb2YgdGhlIGF1dG9jb3JyZWxhdGlvbiB0aW1lIG9mIHRoZSBNYXJrb3YgcHJvY2Vzcy5bMTNdDQpJdCBpcyBpbXBvcnRhbnQgdG8gbm90aWNlIHRoYXQgaXQgaXMgbm90IGNsZWFyLCBpbiBhIGdlbmVyYWwgcHJvYmxlbSwgd2hpY2ggZGlzdHJpYnV0aW9uICR7IFxkaXNwbGF5c3R5bGUgcSh4J3x4KX0kIG9uZSBzaG91bGQgdXNlOyBpdCBpcyBhIGZyZWUgcGFyYW1ldGVyIG9mIHRoZSBtZXRob2Qgd2hpY2ggaGFzIHRvIGJlIGFkanVzdGVkIHRvIHRoZSBwYXJ0aWN1bGFyIHByb2JsZW0gaW4gaGFuZC4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KICANCiMjIyAxLjQgUHJvcGVydHkNCg0KQW5vdGhlciBpbnRlcmVzdGluZyBwcm9wZXJ0eSBvZiB0aGUgTWV0cm9wb2xpc+KAk0hhc3RpbmdzIGFsZ29yaXRobSB0aGF0IGFkZHMgdG8gaXRzIGFwcGVhbCBpcyB0aGF0IGl0ICoqb25seSBkZXBlbmRzIG9uIHRoZSByYXRpb3MqKg0KDQoNCg0KJCQgXGZyYWN7UCh4Jyl9e1AoeCl9JCQNCg0KaXMgdGhlIHByb2JhYmlsaXR5IChlLmcuLCBCYXllc2lhbiBwb3N0ZXJpb3IpIHJhdGlvIGJldHdlZW4gdGhlIHByb3Bvc2VkIHNhbXBsZSAke1xkaXNwbGF5c3R5bGUgeCdcLH0kICwgYW5kIHRoZSBwcmV2aW91cyBzYW1wbGUgJHtcZGlzcGxheXN0eWxlIHhfe3R9XCx9JCwgYW5kDQoNCiQkXGZyYWN7cSh4fHgnKX17cSh4J3x4KX0kJA0KDQppcyB0aGUgcmF0aW8gb2YgdGhlIHByb3Bvc2FsIGRlbnNpdHkgaW4gdHdvIGRpcmVjdGlvbnMgKGZyb20gJHtcZGlzcGxheXN0eWxlIHhfe3R9XCx9JCwgdG8gJHtcZGlzcGxheXN0eWxlIHgnXCx9JCBhbmQgdmljZSB2ZXJzYSkuIFRoaXMgaXMgZXF1YWwgdG8gMSBpZiB0aGUgcHJvcG9zYWwgZGVuc2l0eSBpcyBzeW1tZXRyaWMuDQoNCg0KTm90ZSB0aGF0ICRBKHgnfHgpJCBkb2VzIG5vdCByZXF1aXJlIGtub3dsZWRnZSBvZiB0aGUgbm9ybWFsaXppbmcgY29uc3RhbnQgYmVjYXVzZSBpdCBkcm9wcyBvdXQgb2YgdGhlIHJhdGlvICRcZnJhY3tQKHgnKX17UCh4KX0kLg0KDQoNCg0KVGhlIE1hcmtvdiBjaGFpbiBpcyBzdGFydGVkIGZyb20gYW4gYXJiaXRyYXJ5IGluaXRpYWwgdmFsdWUgJHtcZGlzcGxheXN0eWxlIFxkaXNwbGF5c3R5bGUgeF97MH19JCAgYW5kIHRoZSBhbGdvcml0aG0gaXMgcnVuIGZvciBtYW55IGl0ZXJhdGlvbnMgdW50aWwgdGhpcyBpbml0aWFsIHN0YXRlIGlzICJmb3Jnb3R0ZW4iLiBUaGVzZSBzYW1wbGVzLCB3aGljaCBhcmUgZGlzY2FyZGVkLCBhcmUga25vd24gYXMgYnVybi1pbi4gVGhlIHJlbWFpbmluZyBzZXQgb2YgYWNjZXB0ZWQgdmFsdWVzIG9mICR7XGRpc3BsYXlzdHlsZSB4fSQgcmVwcmVzZW50IGEgc2FtcGxlIGZyb20gdGhlIGRpc3RyaWJ1dGlvbiAke1xkaXNwbGF5c3R5bGUgUCh4KX0kDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjUgQ29udmVyZ2UNCg0KSWYgdGhlIGRvbWFpbiBleHBsb3JlZCBieSBxIChpdHMgc3VwcG9ydCkgaXMgdG9vIHNtYWxsLCBjb21wYXJlZCB3aXRoIHRoZSByYW5nZSBvZiBmLCB0aGUgTWFya292IGNoYWluIHdpbGwgaGF2ZSBkaWZmaWN1bHRpZXMgaW4gZXhwbG9yaW5nIHRoaXMgcmFuZ2UgYW5kIHRodXMgd2lsbCBjb252ZXJnZSB2ZXJ5IHNsb3dseSAoaWYgYXQgYWxsIGZvciBwcmFjdGljYWwgcHVycG9zZXMpLg0KDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDIuIEEgc2ltcGxlIE1ldHJvcG9saXMgc2FtcGxlcg0KIyMjIDIuMSBBIHNpbXBsZSBNZXRyb3BvbGlzLUhhc3RpbmdzIGluZGVwZW5kZW5jZSBzYW1wbGVyDQoNCg0KTGV0J3MgbG9vayBhdCBzaW11bGF0aW5nIGZyb20gYSAqKmdhbW1hIHRhcmdldCBkaXN0cmlidXRpb24qKiB3aXRoIGFyYml0cmFyeSBzaGFwZSBhbmQgc2NhbGUgcGFyYW1ldGVycyx1c2luZyBhIE1ldHJvcG9saXMtSGFzdGluZ3MgaW5kZXBlbmRlbmNlIHNhbXBsaW5nIGFsZ29yaXRobSB3aXRoICoqbm9ybWFsIHByb3Bvc2FsIGRpc3RyaWJ1dGlvbioqIHdpdGggdGhlIHNhbWUgbWVhbiBhbmQgdmFyaWFuY2UgYXMgdGhlIGRlc2lyZWQgZ2FtbWEuDQoNCkEgZnVuY3Rpb24gZm9yIHRoZSBNZXRyb3BvbGlzLUhhc3RpbmdzIHNhbXBsZXIgZm9yIHRoaXMgcHJvYmxlbSBpcyBnaXZlbiBiZWxvdy4gVGhlIGNoYWluIGlzIGluaXRpYWxpc2VkIGF0IHplcm8sIGFuZCBhdCBlYWNoIHN0YWdlIGEgTihhL2IsYS8oYipiKSkgY2FuZGlkYXRlIGlzIHByb3Bvc2VkLg0KDQokJHEoeCd8eCkgXHNpbSBOKGEvYixhLyhiKmIpKSQkDQoNCk1ldHJvcG9saXMtSGFzdGluZ3MgaW5kZXBlbmRlbmNlIHNhbXBsZXIgZm9yIGEgZ2FtbWEgYmFzZWQgb24gbm9ybWFsIGNhbmRpZGF0ZXMvaW5zdHJ1bWVudGFsL3Byb3Bvc2FsL2p1bXBpbmcgZGlzdHJpYnV0aW9uIHdpdGggdGhlIHNhbWUgbWVhbiBhbmQgdmFyaWFuY2UNCg0KMS4gU3RhcnQgaW4gc29tZSBzdGF0ZSAkeF90JC4geCBpbiBjb2RlLg0KMi4gUHJvcG9zZSBhIG5ldyBzdGF0ZSAkeF5ccHJpbWUkIGNhbmRpZGF0ZSBpbiBjb2RlDQozLiBDb21wdXRlIHRoZSAiYWNjZXB0YW5jZSBwcm9iYWJpbGl0eSINCiQkXGFscGhhKHgnfHgpID0gXG1pblxsZWZ0WzEsIFxmcmFje2RnYW1tYSh4XlxwcmltZSwgYSwgYikqZG5vcm0oeCwgDQogICAgICAgICAgICAgICAgICAgICAgICBtdSwgc2lnKX17ZGdhbW1hKHgsIA0KICAgICAgICAgICAgICAgICAgICAgICAgYSwgYikqZG5vcm0oeF5ccHJpbWUsIG11LCBzaWcpfSBccmlnaHRdJCQNCjQuIERyYXcgc29tZSB1bmlmb3JtbHkgZGlzdHJpYnV0ZWQgcmFuZG9tIG51bWJlciAkdSQgZnJvbSAkWzAsMV0kOyBpZiAkdSA8IFxhbHBoYSQgYWNjZXB0IHRoZSBwb2ludCwgc2V0dGluZyAkeF97dCsxfSA9IHheXHByaW1lJC4NCk90aGVyd2lzZSByZWplY3QgaXQgYW5kIHNldCAkeF97dCsxfSA9IHhfdCQuDQoNCg0KDQohW01IIHZpc3VhbGl6YXRpb25dKGh0dHBzOi8vdGhlb3JldGljYWxlY29sb2d5LmZpbGVzLndvcmRwcmVzcy5jb20vMjAxMC8wOS9tZXRyb3BvbGlzLWhhc3RpbmdzLmdpZj93PTcwMCkNCg0KDQoNCkZpZ3VyZTogQSB2aXN1YWxpemF0aW9uIG9mIHRoZSBNZXRyb3BvbGlzLUhhc3RpbmdzIE1DTUMsIGZyb20gSGFydGlnIGV0IGFsLiwgMjAxMS4gKGNvcHlyaWdodCBzZWUgcHVibGlzaGVyKQ0KDQoNCg0KYGBge3IgZ2FtbX0NCnNldC5zZWVkKDEyMykNCmdhbW08LWZ1bmN0aW9uIChuLCBhLCBiKXsNCiAgICAgICAgbXUgPC0gYS9iDQogICAgICAgIHNpZyA8LSBzcXJ0KGEvKGIgKiBiKSkNCiAgICAgICAgdmVjIDwtIHZlY3RvcigibnVtZXJpYyIsIG4pDQogICAgICAgIHggPC0gMyphL2INCiAgICAgICAgdmVjWzFdIDwtIHgNCiAgICAgICAgZm9yIChpIGluIDI6bikgew0KICAgICAgICAgICAgICAgIGNhbiA8LSBybm9ybSgxLCBtdSwgc2lnKQ0KICAgICAgICAgICAgICAgIGFwcm9iIDwtIG1pbigxLCAoZGdhbW1hKGNhbiwgYSwgYikvZGdhbW1hKHgsIA0KICAgICAgICAgICAgICAgICAgICAgICAgYSwgYikpLyhkbm9ybShjYW4sIG11LCBzaWcpL2Rub3JtKHgsIA0KICAgICAgICAgICAgICAgICAgICAgICAgbXUsIHNpZykpKQ0KICAgICAgICAgICAgICAgIHUgPC0gcnVuaWYoMSkNCiAgICAgICAgICAgICAgICBpZiAodSA8IGFwcm9iKSANCiAgICAgICAgICAgICAgICAgICAgICAgIHggPC0gY2FuDQogICAgICAgICAgICAgICAgdmVjW2ldIDwtIHgNCiAgICAgICAgfQ0KICAgICAgICByZXR1cm4odmVjKQ0KfQ0KYGBgDQoNCg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDIuMiAgUGxvdHMNCg0KU2V0IHBhcmFtZXRlcnMuDQoNCmBgYHtyIHZlY30NCm5yZXA8LSA1NTAwMA0KYnVybmluPC0gNTAwMA0Kc2hhcGU8LSAyLjMNCnJhdGU8LTIuNw0KDQp2ZWM8LWdhbW0obnJlcCxzaGFwZSwgcmF0ZSkNCg0KYGBgDQoNCg0KDQoNCk1vZGlmeSB0aGUgcGxvdHMgYmVsb3cgc28gdGhleSBhcHBseSBvbmx5IHRvIHRoZSBjaGFpbiBBRlRFUiB0aGUgYnVybi1pbiBwZXJpb2QNCg0KDQoNCg0KYGBge3IgYnVybmlufQ0KdmVjPXZlY1stKDE6YnVybmluKV0NCiN2ZWM9dmVjW2J1cm5pbjpsZW5ndGgodmVjKV0NCmBgYA0KDQoNCg0KDQpgYGB7ciBwbG90fQ0KcGFyKG1mcm93PWMoMiwxKSkgIyBjaGFuZ2UgbWFpbiBmcmFtZSwgaG93IG1hbnkgZ3JhcGhzIGluIG9uZSBmcmFtZQ0KcGxvdCh0cyh2ZWMpLCB4bGFiPSJDaGFpbiIsIHlsYWI9IkRyYXdzIikNCmFibGluZShoID0gbWVhbih2ZWMpLCBsd2Q9IjIiLCBjb2w9InJlZCIgKQ0KaGlzdCh2ZWMsMzAsIHByb2I9VFJVRSwgeGxhYj0iUmVkIExpbmUgPSBtZWFuIiwgY29sPSJncmV5IiwgbWFpbj0iU2ltdWxhdGVkIERlbnNpdHkiKQ0KYWJsaW5lKHYgPSBtZWFuKHZlYyksIGx3ZD0iMiIsIGNvbD0icmVkIiApDQpwYXIobWZyb3c9YygxLDEpKSAjIGdvIGJhY2sgdG8gZGVmYXVsdA0KYGBgDQoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDIuMyBzdW1tYXJ5DQoNCmBgYHtyIHN1bW1hcnl9DQpzdW1tYXJ5KHZlY1stKDE6YnVybmluKV0pOyAgDQp2YXIodmVjWy0oMTpidXJuaW4pXSkNCmBgYA0KDQojIyMgMi40IEluaXRpYWwgVmFsdWUNCg0KVGhlIGZpcnN0IHNhbXBsZSBpbiBgdmVjYCBpcyB0aGUgaW5pdGlhbCAvc3RhcnQgdmFsdWUgZm9yIG91ciBjaGFpbi4gV2UgY2FuIGNoYW5nZSBpdCB0byBzZWUgaWYgdGhlIGNvbnZlcmdlbmNlIGNoYW5nZXMuDQoNCmBgYHINCiAgICAgICAgeCA8LSAzKmEvYg0KICAgICAgICB2ZWNbMV0gPC0geA0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMi41IENob29zZSBwcm9wb3NhbA0KDQpUaGUgYWxnb3JpdGhtIHdvcmtzIGJlc3QgaWYgdGhlIHByb3Bvc2FsIGRlbnNpdHkgbWF0Y2hlcyB0aGUgc2hhcGUgb2YgdGhlIHRhcmdldCBkaXN0cmlidXRpb24gJFAoeCkkIGZyb20gd2hpY2ggZGlyZWN0IHNhbXBsaW5nIGlzIGRpZmZpY3VsdCwgdGhhdCBpcyAke1xkaXNwbGF5c3R5bGUgcSh4J3x4X3t0fSlcYXBwcm94IFAoeCcpXCxcIX0kLiBJZiBhIEdhdXNzaWFuIHByb3Bvc2FsIGRlbnNpdHkgJHtxfSQgaXMgdXNlZCwgdGhlIHZhcmlhbmNlIHBhcmFtZXRlciAkXHNpZ21hXnsyfSQgaGFzIHRvIGJlIHR1bmVkIGR1cmluZyB0aGUgYnVybi1pbiBwZXJpb2QuIA0KDQpUaGlzIGlzIHVzdWFsbHkgZG9uZSBieSBjYWxjdWxhdGluZyB0aGUgYWNjZXB0YW5jZSByYXRlLCB3aGljaCBpcyB0aGUgZnJhY3Rpb24gb2YgcHJvcG9zZWQgc2FtcGxlcyB0aGF0IGlzIGFjY2VwdGVkIGluIGEgd2luZG93IG9mIHRoZSBsYXN0ICROJCBzYW1wbGVzLiANCg0KVGhlICoqZGVzaXJlZCBhY2NlcHRhbmNlIHJhdGUqKiBkZXBlbmRzIG9uIHRoZSB0YXJnZXQgZGlzdHJpYnV0aW9uLCBob3dldmVyIGl0IGhhcyBiZWVuIHNob3duIHRoZW9yZXRpY2FsbHkgdGhhdCB0aGUgKippZGVhbCBhY2NlcHRhbmNlIHJhdGUqKiBmb3IgYSBvbmUtZGltZW5zaW9uYWwgR2F1c3NpYW4gZGlzdHJpYnV0aW9uIGlzIGFwcHJveCA1MCUsIGRlY3JlYXNpbmcgdG8gYXBwcm94IDIzJSBmb3IgYW4gJE4kLWRpbWVuc2lvbmFsIEdhdXNzaWFuIHRhcmdldCBkaXN0cmlidXRpb24uDQoNCklmICRcc2lnbWFeezJ9JCBpcyB0b28gc21hbGwgdGhlIGNoYWluIHdpbGwgbWl4IHNsb3dseSAoaS5lLiwgdGhlIGFjY2VwdGFuY2UgcmF0ZSB3aWxsIGJlIGhpZ2ggYnV0IHN1Y2Nlc3NpdmUgc2FtcGxlcyB3aWxsIG1vdmUgYXJvdW5kIHRoZSBzcGFjZSBzbG93bHkgYW5kIHRoZSBjaGFpbiB3aWxsIGNvbnZlcmdlIG9ubHkgc2xvd2x5IHRvICRcZGlzcGxheXN0eWxlIFAoeCkkKS4gDQoNCk9uIHRoZSBvdGhlciBoYW5kLCBpZiAkXGRpc3BsYXlzdHlsZSBcc2lnbWFeezJ9JCBpcyB0b28gbGFyZ2UgdGhlIGFjY2VwdGFuY2UgcmF0ZSB3aWxsIGJlIHZlcnkgbG93IGJlY2F1c2UgdGhlIHByb3Bvc2FscyBhcmUgbGlrZWx5IHRvIGxhbmQgaW4gcmVnaW9ucyBvZiBtdWNoIGxvd2VyIHByb2JhYmlsaXR5IGRlbnNpdHksIHNvICRcZGlzcGxheXN0eWxlIGFfezF9JCB3aWxsIGJlIHZlcnkgc21hbGwgYW5kIGFnYWluIHRoZSBjaGFpbiB3aWxsIGNvbnZlcmdlIHZlcnkgc2xvd2x5Lg0KDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDMuIFNhbXBsZSAyOiBCYXllc2lhbiBFc3RpbWF0aW9uIGZvciBSZWdyZXNzaW9uIA0KDQoNCg0KDQoNCihhKSBDaGVjayB5b3VyIHVuZGVyc3RhbmRpbmcgb2YgdGhlIE1ldHJvcG9saXMtSGFzdGluZ3Mgc2FtcGxlciB0aGF0IGlzIGJlaW5nIHVzZWQgaGVyZSBmb3INCkJheWVzaWFuIGVzdGltYXRpb24gb2YgYSByZWdyZXNzaW9uIG1vZGVsLCB1c2luZyBhcnRpZmljaWFsIGRhdGEuDQoNClRoaXMgY29kZSBpcyBhIG1vZGlmaWVkIHZlcnNpb24gb2YgdGhhdCBwcmVzZW50ZWQgYXQNCiBodHRwczovL3RoZW9yZXRpY2FsZWNvbG9neS53b3JkcHJlc3MuY29tLzIwMTAvMDkvMTcvbWV0cm9wb2xpcy1oYXN0aW5ncy1tY21jLWluLXIvDQogKE1vZGlmaWVkIGJ5IERhdmlkIEdpbGVzIDxkZ2lsZXNAdXZpYy5jYT4sIDIxIE1hcmNoLCAyMDE2KQ0KVGhpcyB3b3JrIGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uLU5vbkNvbW1lcmNpYWwtU2hhcmVBbGlrZSAzLjAgVW5wb3J0ZWQgTGljZW5zZS4gDQogDQogDQogDQogDQogDQogDQokJHkgPSBBeCArIEIgKyB1LFwsIHUgXHNpbSBOKDAsc2QpJCQgDQoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoNCiMjIyAzLjEgU2V0IHBhcmFtZXRlcg0KDQpgYGB7ciBwYXJhfQ0KdHJ1ZUEgPC0gNQ0KdHJ1ZUIgPC0gMA0KdHJ1ZVNkIDwtIDEwDQpzYW1wbGVTaXplIDwtIDMxDQpgYGANCg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAzLjIgREdQIGFuZCBQbG90DQoNCg0KDQoNCmBgYHtyfQ0KIyBjcmVhdGUgaW5kZXBlbmRlbnQgeC12YWx1ZXMsIGFyb3VuZCB6ZXJvDQp4IDwtICgtKHNhbXBsZVNpemUtMSkvMik6KChzYW1wbGVTaXplLTEpLzIpDQojIGNyZWF0ZSBkZXBlbmRlbnQgdmFsdWVzIGFjY29yZGluZyB0byBheCArIGIgKyBOKDAsc2QpDQp5IDwtICB0cnVlQSAqIHggKyB0cnVlQiArIHJub3JtKG49c2FtcGxlU2l6ZSxtZWFuPTAsc2Q9dHJ1ZVNkKQ0KDQpwYXIobWZyb3cgPSBjKDEsMSkpDQpwbG90KHgseSwgbWFpbj0iVGVzdCBEYXRhIikNCg0KYGBgDQoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMy4zIGxpa2VsaWhvb2QgZnJvbSBub3JtYWwgZGlzdHJpYnV0aW9uDQoNCg0KDQoNCmBgYHtyIGxpa2VsaWhvb2R9DQpsaWtlbGlob29kIDwtIGZ1bmN0aW9uKHBhcmFtKXsNCiAgICBhID0gcGFyYW1bMV0NCiAgICBiID0gcGFyYW1bMl0NCiAgICBzZCA9IHBhcmFtWzNdDQogICAgIA0KICAgIHByZWQgPSBhKnggKyBiDQogICAgc2luZ2xlbGlrZWxpaG9vZHMgPSBkbm9ybSh5LCBtZWFuID0gcHJlZCwgc2QgPSBzZCwgbG9nID0gVCkNCiAgICBzdW1sbCA9IHN1bShzaW5nbGVsaWtlbGlob29kcykNCiAgICByZXR1cm4oc3VtbGwpICAgDQp9DQpgYGANCg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAzLjQgV2h5IHdvcmsgd2l0aCBsb2dhcml0aG1zDQoNClJldHVybiB0aGUgbG9nYXJpdGhtIG9mIHRoZSBwcm9iYWJpbGl0aWVzIGluIHRoZSBsaWtlbGlob29kIGZ1bmN0aW9uLCB3aGljaCBpcyBhbHNvIHRoZSByZWFzb24gd2h5IEkgc3VtIHRoZSBwcm9iYWJpbGl0aWVzIG9mIGFsbCBvdXIgZGF0YXBvaW50cyAodGhlIGxvZ2FyaXRobSBvZiBhIHByb2R1Y3QgZXF1YWxzIHRoZSBzdW0gb2YgdGhlIGxvZ2FyaXRobXMpLiANCg0KV2h5IGRvIHdlIGRvIHRoaXM/IEl0J3Mgc3Ryb25nbHkgYWR2aXNhYmxlIGJlY2F1c2UgbGlrZWxpaG9vZHMsIHdoZXJlIGEgbG90IG9mIHNtYWxsIHByb2JhYmlsaXRpZXMgYXJlIG11bHRpcGxpZWQsIGNhbiBnZXQgcmlkaWN1bG91c2x5IHNtYWxsIHByZXR0eSBmYXN0IChzb21ldGhpbmcgbGlrZSAkMTBeey0zNH0kKS4gQXQgc29tZSBzdGFnZSwgY29tcHV0ZXIgcHJvZ3JhbXMgYXJlIGdldHRpbmcgaW50byBudW1lcmljYWwgcm91bmRpbmcgb3IgdW5kZXJmbG93IHByb2JsZW1zIHRoZW4uIA0KDQpTbywgYm90dG9tLWxpbmU6IF9fd2hlbiB5b3UgcHJvZ3JhbSBzb21ldGhpbmcgd2l0aCBsaWtlbGlob29kcywgYWx3YXlzIHVzZSBsb2dhcml0aG1zISEhX18NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAzLjUgRXhhbXBsZTogcGxvdCB0aGUgbGlrZWxpaG9vZCBwcm9maWxlIG9mIHRoZSBzbG9wZSBhDQoNCmBgYHtyIHBsb3Rsa30NCiMgRXhhbXBsZTogcGxvdCB0aGUgbGlrZWxpaG9vZCBwcm9maWxlIG9mIHRoZSBzbG9wZSBhDQpzbG9wZXZhbHVlcyA8LSBmdW5jdGlvbih4KXtyZXR1cm4obGlrZWxpaG9vZChjKHgsIHRydWVCLCB0cnVlU2QpKSl9DQpzbG9wZWxpa2VsaWhvb2RzIDwtIGxhcHBseShzZXEoMywgNywgYnk9LjA1KSwgc2xvcGV2YWx1ZXMgKQ0KcGxvdCAoc2VxKDMsIDcsIGJ5PS4wNSksIHNsb3BlbGlrZWxpaG9vZHMgLCB0eXBlPSJsIiwgeGxhYiA9ICJ2YWx1ZXMgb2Ygc2xvcGUgcGFyYW1ldGVyIGEiLCB5bGFiID0gIkxvZyBsaWtlbGlob29kIikNCg0KYGBgDQoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDMuNiBQcmlvciBkaXN0cmlidXRpb24NCg0KdW5pZm9ybSBkaXN0cmlidXRpb25zIGFuZCBub3JtYWwgZGlzdHJpYnV0aW9ucyBmb3IgYWxsIHRocmVlIHBhcmFtZXRlcnMuDQoNCmBgYHtyIHByaW9yfQ0KIyBQcmlvciBkaXN0cmlidXRpb24NCnByaW9yIDwtIGZ1bmN0aW9uKHBhcmFtKXsNCiAgICBhID0gcGFyYW1bMV0NCiAgICBiID0gcGFyYW1bMl0NCiAgICBzZCA9IHBhcmFtWzNdDQojIENIQU5HRSBUSEUgTkVYVCAzIExJTkVTIFRPIENIQU5HRSBUSEUgUFJJT1IsIGxvZyBpcyBUcnVlLCBzbyB0aGVzZSBhcmUgbG9nIGRlbnNpdHkvbGlrZWxpaG9vZA0KICAgIGFwcmlvciA9IGR1bmlmKGEsIG1pbj0wLCBtYXg9MTAsIGxvZyA9IFQpDQogICAgYnByaW9yID0gZG5vcm0oYiwgc2QgPSAyLCBsb2cgPSBUKQ0KICAgIHNkcHJpb3IgPSBkdW5pZihzZCwgbWluPTAsIG1heD0zMCwgbG9nID0gVCkNCiAgICByZXR1cm4oYXByaW9yK2JwcmlvcitzZHByaW9yKQ0KfQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMy43IFRoZSBwb3N0ZXJpb3INCg0KVGhlIHByb2R1Y3Qgb2YgcHJpb3IgYW5kIGxpa2VsaWhvb2QgaXMgdGhlIGFjdHVhbCBxdWFudGl0eSB0aGUgTUNNQyB3aWxsIGJlIHdvcmtpbmcgb24uIFRoaXMgZnVuY3Rpb24gaXMgY2FsbGVkIHRoZSBwb3N0ZXJpb3IgKG9yIHRvIGJlIGV4YWN0LCBpdCdzIGNhbGxlZCB0aGUgcG9zdGVyaW9yIGFmdGVyIGl0J3Mgbm9ybWFsaXplZCwgd2hpY2ggdGhlIE1DTUMgd2lsbCBkbyBmb3IgdXMsIGJ1dCBsZXQncyBub3QgYmUgcGlja3kgZm9yIHRoZSBtb21lbnQpLiBBZ2FpbiwgaGVyZSB3ZSB3b3JrIHdpdGggdGhlIHN1bSBiZWNhdXNlIHdlIHdvcmsgd2l0aCBsb2dhcml0aG1zLg0KDQpgYGB7ciBwb3N0ZXJpb3J9DQpwb3N0ZXJpb3IgPC0gZnVuY3Rpb24ocGFyYW0pew0KICAgcmV0dXJuIChsaWtlbGlob29kKHBhcmFtKSArIHByaW9yKHBhcmFtKSkNCn0NCg0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDMuOCBNZXRyb3BvbGlzIGFsZ29yaXRobQ0KDQpPbmUgb2YgdGhlIG1vc3QgZnJlcXVlbnQgYXBwbGljYXRpb25zIG9mIHRoaXMgYWxnb3JpdGhtIChhcyBpbiB0aGlzIGV4YW1wbGUpIGlzICoqc2FtcGxpbmcgZnJvbSB0aGUgcG9zdGVyaW9yIGRlbnNpdHkqKiBpbiBCYXllc2lhbiBzdGF0aXN0aWNzLiANCg0KSW4gcHJpbmNpcGxlLCBob3dldmVyLCB0aGUgYWxnb3JpdGhtIG1heSBiZSB1c2VkIHRvIHNhbXBsZSBmcm9tIGFueSBpbnRlZ3JhYmxlIGZ1bmN0aW9uLiBTbywgdGhlIGFpbSBvZiB0aGlzIGFsZ29yaXRobSBpcyB0byAqKmp1bXAgYXJvdW5kIGluIHBhcmFtZXRlciBzcGFjZSwgYnV0IGluIGEgd2F5IHRoYXQgdGhlIHByb2JhYmlsaXR5IHRvIGJlIGF0IGEgcG9pbnQgaXMgcHJvcG9ydGlvbmFsIHRvIHRoZSBmdW5jdGlvbiB3ZSBzYW1wbGUgZnJvbSAodGhpcyBpcyB1c3VhbGx5IGNhbGxlZCB0aGUgdGFyZ2V0IGZ1bmN0aW9uKSoqLiANCg0KDQoNCg0KSW4gb3VyIGNhc2UgdGhpcyBpcyB0aGUgcG9zdGVyaW9yIGRlZmluZWQgYWJvdmUuDQoNCjEuIFN0YXJ0aW5nIGF0IGEgcmFuZG9tIHBhcmFtZXRlciB2YWx1ZQ0KDQoyLiBDaG9vc2luZyBhIG5ldyBwYXJhbWV0ZXIgdmFsdWUgY2xvc2UgdG8gdGhlIG9sZCB2YWx1ZSBiYXNlZCBvbiBzb21lIHByb2JhYmlsaXR5IGRlbnNpdHkgdGhhdCBpcyBjYWxsZWQgdGhlIHByb3Bvc2FsIGZ1bmN0aW9uDQoNCjMuIEp1bXBpbmcgdG8gdGhpcyBuZXcgcG9pbnQgd2l0aCBhIHByb2JhYmlsaXR5IHAobmV3KS9wKG9sZCksIHdoZXJlIHAgaXMgdGhlIHRhcmdldCBmdW5jdGlvbiwgYW5kIHA+MSBtZWFucyBqdW1waW5nIGFzIHdlbGwgDQoNCjQuIE5vdGUgdGhhdCB3ZSBoYXZlIGEgKipzeW1tZXRyaWMganVtcGluZy9wcm9wb3NhbCBkaXN0cmlidXRpb24qKiAkcSh4J3x4KSQuDQoNCiQkIHEoeCd8eCkgXHNpbSBOKHgsIGMoMC4xLDAuNSwwLjMpICkkJA0KDQpUaGUgc3RhbmRhcmQgZGl2ZWF0aW9uICRcc2lnbWEkIGFyZSBmaXhlZC4gVGhlICRxKHgnfHgpID0gcSh4fHgnKSQuDQoNCnNvIHRoZSBhY2NlcHQgcHJvYmFibGl0eSBlcXVhbHMgdG8gDQoNCiQkXGFscGhhKHgnfHgpPVxtaW4gXGxlZnQoMSxcZnJhY3tQKHgnKX17UCh4KX0gXHJpZ2h0KSQkDQoNCmBgYHtyIE1IfQ0KIyMjIyMjIyMgTWV0cm9wb2xpcyBhbGdvcml0aG0gIyMjIyMjIyMjIyMjIyMjIw0KIA0KcHJvcG9zYWxmdW5jdGlvbiA8LSBmdW5jdGlvbihwYXJhbSl7DQogICAgcmV0dXJuKHJub3JtKDMsbWVhbiA9IHBhcmFtLCBzZD0gYygwLjEsMC41LDAuMykpKQ0KDQp9DQogDQpydW5fbWV0cm9wb2xpc19NQ01DIDwtIGZ1bmN0aW9uKHN0YXJ0dmFsdWUsIGl0ZXJhdGlvbnMpew0KICAgIGNoYWluID0gYXJyYXkoZGltID0gYyhpdGVyYXRpb25zKzEsMykpDQogICAgY2hhaW5bMSxdID0gc3RhcnR2YWx1ZQ0KICAgIGZvciAoaSBpbiAxOml0ZXJhdGlvbnMpew0KICAgICAgICBwcm9wb3NhbCA9IHByb3Bvc2FsZnVuY3Rpb24oY2hhaW5baSxdKQ0KICAgICAgICAgDQogICAgICAgIHByb2JhYiA9IGV4cChwb3N0ZXJpb3IocHJvcG9zYWwpIC0gcG9zdGVyaW9yKGNoYWluW2ksXSkpDQogICAgICAgIGlmIChydW5pZigxKSA8IHByb2JhYil7DQogICAgICAgICAgICBjaGFpbltpKzEsXSA9IHByb3Bvc2FsDQogICAgICAgIH1lbHNlew0KICAgICAgICAgICAgY2hhaW5baSsxLF0gPSBjaGFpbltpLF0NCiAgICAgICAgfQ0KICAgIH0NCiAgICByZXR1cm4oY2hhaW4pDQp9DQpgYGANCg0KDQpBZ2Fpbiwgd29ya2luZyB3aXRoIHRoZSBsb2dhcml0aG1zIG9mIHRoZSBwb3N0ZXJpb3IgbWlnaHQgYmUgYSBiaXQgY29uZnVzaW5nIGF0IGZpcnN0LCBpbiBwYXJ0aWN1bGFyIHdoZW4geW91IGxvb2sgYXQgdGhlIGxpbmUgd2hlcmUgdGhlIGFjY2VwdGFuY2UgcHJvYmFiaWxpdHkgaXMgY2FsY3VsYXRlZCBgKHByb2JhYiA9IGV4cChwb3N0ZXJpb3IocHJvcG9zYWwpIC0gcG9zdGVyaW9yKGNoYWluW2ksXSkpKWAuIFRvIHVuZGVyc3RhbmQgd2h5IHdlIGRvIHRoaXMsIG5vdGUgdGhhdCBgcDEvcDIgPSBleHBbbG9nKHAxKS1sb2cocDIpXWAuDQoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAzLjkgSW1wbGVtZW50YXRpb24NCg0KDQooZSlQcmludCB0aGUgdmFsdWUgb2YgdGhlIHF1YW50aXR5IGNhbGxlZCBhY2NlcHRhbmNlLCBhbmQgaW50ZXJwcmV0IHdoYXQgaXQgaXMgdGVsbGluZyB5b3UuDQoNCg0KYGBge3IgcnVufQ0Kc3RhcnR2YWx1ZSA9IGMoNCwwLDEwKQ0KY2hhaW4gPSBydW5fbWV0cm9wb2xpc19NQ01DKHN0YXJ0dmFsdWUsIDU1MDAwKQ0KI3N0cihjaGFpbikgDQpidXJuSW4gPSA1MDAwDQphY2NlcHRhbmNlID0gMS1tZWFuKGR1cGxpY2F0ZWQoY2hhaW5bLSgxOmJ1cm5JbiksXSkpDQojP2R1cGxpY2F0ZWQNCmBgYA0KDQoNClRoZSBmaXJzdCBzdGVwcyBvZiB0aGUgYWxnb3JpdGhtIG1heSBiZSBiaWFzZWQgYnkgdGhlIGluaXRpYWwgdmFsdWUsIGFuZCBhcmUgdGhlcmVmb3JlIHVzdWFsbHkgZGlzY2FyZGVkIGZvciB0aGUgZnVydGhlciBhbmFseXNpcyAoYnVybi1pbiB0aW1lKS4gQW4gaW50ZXJlc3Rpbmcgb3V0cHV0IHRvIGxvb2sgYXQgaXMgdGhlIGFjY2VwdGFuY2UgcmF0ZTogaG93IG9mdGVuIHdhcyBhIHByb3Bvc2FsIHJlamVjdGVkIGJ5IHRoZSBtZXRyb3BvbGlzLWhhc3RpbmdzIGFjY2VwdGFuY2UgY3JpdGVyaW9uPyBUaGUgYWNjZXB0YW5jZSByYXRlIGNhbiBiZSBpbmZsdWVuY2VkIGJ5IHRoZSBwcm9wb3NhbCBmdW5jdGlvbjogZ2VuZXJhbGx5LCB0aGUgY2xvc2VyIHRoZSBwcm9wb3NhbHMgYXJlLCB0aGUgbGFyZ2VyIHRoZSBhY2NlcHRhbmNlIHJhdGUuIFZlcnkgaGlnaCBhY2NlcHRhbmNlIHJhdGVzLCBob3dldmVyLCBhcmUgdXN1YWxseSBub3QgYmVuZWZpY2lhbDogdGhpcyBtZWFucyB0aGF0IHRoZSBhbGdvcml0aG1zIGlzICJzdGF5aW5nIiBhdCB0aGUgc2FtZSBwb2ludCwgd2hpY2ggcmVzdWx0cyBpbiBhIHN1Ym9wdGltYWwgcHJvYmluZyBvZiB0aGUgcGFyYW1ldGVyIHNwYWNlIChtaXhpbmcpLg0KDQoNCldlIGFsc28gY2FuIGNoYW5nZSB0aGUgaW50aWFsIC8gc3RhcnQgdmFsdWUgdG8gc2VlIGlmIGl0IGNoYW5nZSB0aGUgcmVzdWx0LyBpZiBpdCBjaGFuZ2UgdGhlIGNvbnZlcmdlbmNlLg0KDQpgYGByDQpzdGFydHZhbHVlID0gYyg0LDAsMTApDQpgYGANCg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMy4xMCBzdW1tYXJ5DQoNCg0KDQoNCmBgYHtyIHN1bUNoYWlufQ0Kc3VtbWFyeShjYmluZChjaGFpblstKDE6YnVybkluKSwxXSxjaGFpblstKDE6YnVybkluKSwyXSxjaGFpblstKDE6YnVybkluKSwzXSkpDQoNCiMgZm9yIGNvbXBhcmlzb246DQpzdW1tYXJ5KGxtKHl+eCkpDQpzdW1tYXJ5KGxtKHl+eCkpJHNpZ21hDQpjb2VmZmljaWVudHMobG0oeX54KSlbMV0NCmNvZWZmaWNpZW50cyhsbSh5fngpKVsyXQ0KYGBgDQoNCg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMy4xMSBUcmFjZSBvZiBjaGFpbnM6DQoNCmBgYHtyIHBsb3RjaGFpbn0NCiMjIyBTdW1tYXJ5OiAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIA0KcGFyKG1mcm93ID0gYygyLDMpKQ0KaGlzdChjaGFpblstKDE6YnVybkluKSwxXSxwcm9iPVRSVUUsbmNsYXNzPTMwLGNvbD0iMTA5IiAsIG1haW49IlBvc3RlcmlvciBvZiBhIiwgeGxhYj0iQmxhY2s9bWVhbjsgUmVkPXRydWU7IE1hZ2VudGEgPSBNTEUiICkNCmFibGluZSh2ID0gbWVhbihjaGFpblstKDE6YnVybkluKSwxXSksIGx3ZD0iMiIpDQphYmxpbmUodiA9IHRydWVBLCBjb2w9InJlZCIsIGx3ZD0iMiIgKQ0KYWJsaW5lKHYgPSBjb2VmZmljaWVudHMobG0oeX54KSlbMl0sIGNvbD0ibWFnZW50YSIsIGx3ZD0iMiIgKQ0KDQpoaXN0KGNoYWluWy0oMTpidXJuSW4pLDJdLHByb2I9VFJVRSwgbmNsYXNzPTMwLCBjb2w9ImdyZWVuIixtYWluPSJQb3N0ZXJpb3Igb2YgYiIsIHhsYWI9IkJsYWNrPW1lYW47IFJlZD10cnVlOyBNYWdlbnRhID0gTUxFIikNCmFibGluZSh2ID0gbWVhbihjaGFpblstKDE6YnVybkluKSwyXSksIGx3ZD0iMiIpDQphYmxpbmUodiA9IHRydWVCLCBjb2w9InJlZCIsIGx3ZD0iMiIgKQ0KYWJsaW5lKHYgPSBjb2VmZmljaWVudHMobG0oeX54KSlbMV0sIGNvbD0ibWFnZW50YSIsIGx3ZD0iMiIgKQ0KDQpoaXN0KGNoYWluWy0oMTpidXJuSW4pLDNdLHByb2I9VFJVRSwgbmNsYXNzPTMwLCBjb2w9InllbGxvdyIsbWFpbj0iUG9zdGVyaW9yIG9mIHNkIiwgeGxhYj0iQmxhY2s9bWVhbjsgUmVkPXRydWU7IE1hZ2VudGEgPSBNTEUiKQ0KYWJsaW5lKHYgPSBtZWFuKGNoYWluWy0oMTpidXJuSW4pLDNdKSwgbHdkPSIyIiApDQphYmxpbmUodiA9IHRydWVTZCwgY29sPSJyZWQiLCBsd2Q9IjIiICkNCmFibGluZSh2ID0gc3VtbWFyeShsbSh5fngpKSRzaWdtYSwgY29sPSJtYWdlbnRhIiwgbHdkPSIyIiApDQoNCnBsb3QoY2hhaW5bLSgxOmJ1cm5JbiksMV0sIGNvbD0iNjQ4Iix0eXBlID0gImwiLCB4bGFiPSJUcnVlIHZhbHVlID0gcmVkIGxpbmUiICwgbWFpbiA9ICJDaGFpbiB2YWx1ZXMgb2YgYSIgKQ0KYWJsaW5lKGggPSB0cnVlQSwgY29sPSJyZWQiICkNCnBsb3QoY2hhaW5bLSgxOmJ1cm5JbiksMl0sIGNvbD0iNjQ4Iix0eXBlID0gImwiLCB4bGFiPSJUcnVlIHZhbHVlID0gcmVkIGxpbmUiICwgbWFpbiA9ICJDaGFpbiB2YWx1ZXMgb2YgYiIgKQ0KYWJsaW5lKGggPSB0cnVlQiwgY29sPSJyZWQiICkNCnBsb3QoY2hhaW5bLSgxOmJ1cm5JbiksM10sIGNvbD0iNjQ4Iix0eXBlID0gImwiLCB4bGFiPSJUcnVlIHZhbHVlID0gcmVkIGxpbmUiICwgbWFpbiA9ICJDaGFpbiB2YWx1ZXMgb2Ygc2QiICkNCmFibGluZShoID0gdHJ1ZVNkLCBjb2w9InJlZCIgKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIFJlZmVyZW5jZQ0KDQpbQ1NDIDQ0NiBOb3RlczogTGVjdHVyZSAxM10oaHR0cHM6Ly93d3cuY3Mucm9jaGVzdGVyLmVkdS9+Z2lsZGVhLzIwMTNfU3ByaW5nL05vdGVzL2NzYzQ0NmxlY3R1cmUxM25vdGVzLnBkZikNCg0KDQpbTUNNQyBEZXNpZ24gYW5kIFRyaWNrc10oaHR0cDovL3d3dy5zdGF0LnVjbGEuZWR1L35zY3podS9jb3Vyc2VzL3VjbGEvc3RhdF8yMzJiL2hhbmRvdXRzL2NoNF9kZXNpZ25fYW5kX3RyaWNrcy5wZGYpDQoNCg0KW1doeSBkb2VzIHRoZSBNZXRyb3BvbGlzLUhhc3RpbmdzIHByb2NlZHVyZSBzYXRpc2Z5IHRoZSBkZXRhaWxlZCBiYWxhbmNlIGNyaXRlcmlvbj9dKGh0dHA6Ly9wZW9wbGUuZHVrZS5lZHUvfmtoMjY5L3RlYWNoaW5nL25vdGVzL01ldHJvcG9saXNFeHBsYW5hdGlvbi5wZGYp