MCMC is a general purpose technique for sampling from complex probabilistic models.
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:
Initialisation: pick an initial state x at random;
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
- Start in some state \(x_t\). x in code.
- Propose a new state \(x^\prime\) candidate in code
- 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]\]
- 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\).
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
- 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.
Starting at a random parameter value
Choosing a new parameter value close to the old value based on some probability density that is called the proposal function
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
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" )

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