Here we will perform Bayesian MCMC inference of the probability of heads based on coin tosses. We will investigate convergence of the MCMC chains in various conditions.
We have some data providing the results of 300 coin tosses. We would like to estimate how fair the coin is, i.e. what is the probability of getting heads (1).
data = c(1,0,1,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,0,1,1,0,1,1,0,1,1,0,1,1,1,0,1,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,0,1,1,0,1,1,0,1,1,0,1,1,1,0,1,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,0,1,1,0,1,1,0,1,1,0,1,1,1,0,1,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,0,1,1,0,1,1,0,1,1,0,1,1,1,0,1,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,0,1,1,0,1,1,0,1,1,0,1,1,1,0,1,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,0,1,1,0,1,1,0,1,1,0,1,1,1,0,1,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,0,1,1,0,1,1,0,1,1,0,1,1,1,0,1,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,0,1,1,0,1,1,0,1,1,0,1,1,1,0,1,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,0,1,1,0,1,1,0,1,1,0,1,1,1,0,1,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,0,1,1,0,1,1,0,1,1,0,1,1)
sum(data)
[1] 190
From our data, we see that we obtained 190 heads out of 300 tosses.
The ML estimate of the probability of heads is simply the ratio of heads over all tosses:
sum(data)/length(data)
[1] 0.6333333
We build a probabilistic model of coin tossing.
All coin tosses are supposed to be independent tosses of the same coin, which always have the same probability of returning a head. We want to perform Bayesian inference, therefore we need a prior. For inference, we will be using Metropolis MCMC.
Defining a prior
We need to put some prior probability on the fairness of the coin. For this, a beta distribution seems appropriate, as it is a continuous distribution between 0 and 1. We choose to use a=4, b=4. This is a somewhat informative prior that the coin should be fair, given our past experience with coins.
x <- seq(-0, 1, length=100)
colors <- c("red", "purple", "blue", "darkgreen", "gold")
shape1s = c(1,2,4,1,0.2)
shape2s = c(1,2,4,4,0.2)
a=4
b=4
Building of the model
We build the functions necessary to compute the likelihood and the prior probability for a given value of the parameter.
# Function to compute the likelihood P(D|M)
# We use the binomial formula.
likelihood <-function (numHeads, numTosses, parameter){
p = choose(numTosses, numHeads)*parameter^numHeads * (1-parameter)^(numTosses-numHeads)
return (p)
}
# Function to compute the prior P(M)
prior <- function (parameter, shape1, shape2){
return (dbeta(parameter, shape1=shape1, shape2=shape2) )
}
# Function to compute the un-normalized posterior P(D|M) * P(M)
unnormalized_posterior <-function (numHeads, numTosses, parameter, shape1, shape2) {
return (likelihood(numHeads, numTosses, parameter) * prior(parameter, shape1, shape2))
}
Inference of the fairness of the coin using MCMC
We build a MCMC chain to estimate the probability of heads for this coin. First we define the model, with the prior, the likelihood and the posterior probability, then we implement a Metropolis MCMC inference mechanism.
Implementing the MCMC algorithm
# Number of iterations for the MCMC algorithm
num_iterations = 50000
# Function to propose a new parameter value, randomly drawn between 0 and 1
propose_new_parameter_value <-function() {
return (runif(1))
}
# Function to run Metropolis MCMC inference
MetropolisMCMC <- function (numHeads, numTosses, shape1, shape2, number_iterations) {
current_parameter_value <- propose_new_parameter_value()
record_parameter <- c()
record_parameter <- c(record_parameter,current_parameter_value)
#print(paste("Initial parameter value for the MCMC: ", current_parameter_value, sep=""))
current_posterior <- unnormalized_posterior(numHeads, numTosses, current_parameter_value, shape1, shape2)
#print(paste("Initial probability of the model: ", current_posterior, sep=""))
record_posterior <- c()
record_posterior <- c(record_posterior, current_posterior)
# We also record the likelihood:
current_likelihood <- likelihood(numHeads, numTosses, current_parameter_value)
record_likelihood <- c()
record_likelihood <- c(record_likelihood, current_likelihood)
for (i in 1:number_iterations) {
acceptance_threshold <- runif(1)
proposed_parameter_value <- propose_new_parameter_value()
proposed_posterior = unnormalized_posterior(numHeads , numTosses, proposed_parameter_value, shape1, shape2)
if (proposed_posterior / current_posterior > acceptance_threshold) {
current_parameter_value <- proposed_parameter_value
current_posterior <- proposed_posterior
current_likelihood <- likelihood(numHeads, numTosses, current_parameter_value)
}
record_parameter <- c(record_parameter, current_parameter_value)
record_posterior <-c(record_posterior, current_posterior)
record_likelihood <- c(record_likelihood, current_likelihood)
}
return (list(record_parameter, record_posterior, record_likelihood))
}
Running the MCMC
result = MetropolisMCMC(sum(data), length(data), a, b, num_iterations)
Comparison of the posterior distributions with respect to the number of iterations
We plot in different colors the posterior distributions.
par(mfrow=c(6,1), mar=c(0,4,0,2)+0.1)
plot(density(result[[1]][1:10]), col=colors[1], lwd=2, main="", ylab="Density", xlab="", ylim=c(0,24), xlim=c(0,1), xaxt="n")
legend("topleft", legend="10 iterations")
plot(density(result[[1]][1:50]), col=colors[2], lwd=2, main="", ylab="Density", xlab="", ylim=c(0,24), xlim=c(0,1), xaxt="n")
legend("topleft", legend="50 iterations")
plot(density(result[[1]][1:100]), col=colors[3], lwd=2, main="", ylab="Density", xlab="", ylim=c(0,24), xlim=c(0,1), xaxt="n")
legend("topleft", legend="100 iterations")
plot(density(result[[1]][1:1000]), col=colors[4], lwd=2, main="", ylab="Density", xlab="", ylim=c(0,24), xlim=c(0,1), xaxt="n")
legend("topleft", legend="1000 iterations")
plot(density(result[[1]][1:20000]), col=colors[5], lwd=2, main="", ylab="Density", xlab="", ylim=c(0,24), xlim=c(0,1), xaxt="n")
legend("topleft", legend="20 000 iterations")
plot(density(result[[1]]), col="black", lwd=2, main="", ylab="Density", xlab="Probability of Heads", ylim=c(0,24), xlim=c(0,1))
legend("topleft", legend="50 000 iterations")

Looking at summary statistics provides the same information:
summary(result[[1]][1:10])
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.7084 0.7439 0.7439 0.7404 0.7440 0.7440
summary(result[[1]][1:50])
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.6361 0.6361 0.6404 0.6655 0.6719 0.7440
summary(result[[1]][1:100])
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.6016 0.6115 0.6361 0.6456 0.6493 0.7440
summary(result[[1]][1:1000])
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.5735 0.6182 0.6361 0.6379 0.6554 0.7440
summary(result[[1]][1:20000])
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.5363 0.6115 0.6308 0.6304 0.6490 0.7440
summary(result[[1]])
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.5318 0.6118 0.6303 0.6300 0.6485 0.7440
As the number of iterations increases, the estimates converge.
Better moves for better convergence
In the Metropolis MCMC above, a new state is proposed according to a Uniform distribution. This is probably suboptimal: after all, if at iteration \(n\) in the chain we have a parameter value with a high posterior probability, it’s probably a good idea to propose a new value for iteration \(n+1\) in the vicinity of the parameter value instead of completely randomly over \([0:1]\). Therefore, we implement a new move, the Scale move.
Scale move
# Function to propose a new parameter value, according to a scaling move centered on the current value
scale_move_propose_new_parameter_value <-function(current_parameter_value, lambda_scaling_parameter) {
# Propose a new value of p
scaling_factor <- exp( lambda_scaling_parameter * ( runif(1) - 0.5 ) )
p_prime <- current_parameter_value * scaling_factor
Hastings_ratio <- scaling_factor
return(list(p_prime, Hastings_ratio))
}
The scale move is non-symmetric: it is more likely to propose smaller values than larger values. Therefore a Hastings ratio needs to be computed. In the case of the scale move, the Hastings ratio turns out to be simple to compute, and is the scaling factor used on the current parameter value to propose the new value.
MCMC with the scale move
We implement a new MCMC function that will use this Scale move instead of the previous uniform move.
# Function to run Metropolis MCMC inference
MHMCMC_ScaleMove <- function (numHeads, numTosses, shape1, shape2, number_iterations, lambda_scaling_parameter) {
current_parameter_value <- propose_new_parameter_value()
record_parameter <- c()
record_parameter <- c(record_parameter,current_parameter_value)
#print(paste("Initial parameter value for the MCMC: ", current_parameter_value, sep=""))
current_posterior <- unnormalized_posterior(numHeads, numTosses, current_parameter_value, shape1, shape2)
#print(paste("Initial probability of the model: ", current_posterior, sep=""))
record_posterior <- c()
record_posterior <- c(record_posterior, current_posterior)
# We also record the likelihood:
current_likelihood <- likelihood(numHeads, numTosses, current_parameter_value)
record_likelihood <- c()
record_likelihood <- c(record_likelihood, current_likelihood)
for (i in 1:number_iterations) {
acceptance_threshold <- runif(1)
value_and_HastingsRatio <- scale_move_propose_new_parameter_value(current_parameter_value, lambda_scaling_parameter = lambda_scaling_parameter)
proposed_parameter_value <- value_and_HastingsRatio[[1]]
Hastings_ratio <- value_and_HastingsRatio[[2]]
if (proposed_parameter_value > 0 & proposed_parameter_value < 1 ) {
proposed_posterior = unnormalized_posterior(numHeads , numTosses, proposed_parameter_value, shape1, shape2) * Hastings_ratio
if (proposed_posterior / current_posterior > acceptance_threshold) {
current_parameter_value <- proposed_parameter_value
current_posterior <- proposed_posterior
current_likelihood <- likelihood(numHeads, numTosses, current_parameter_value)
}
}
record_parameter <- c(record_parameter, current_parameter_value)
record_posterior <-c(record_posterior, current_posterior)
record_likelihood <- c(record_likelihood, current_likelihood)
}
return (list(record_parameter, record_posterior, record_likelihood))
}
Running the MCMC with the scale move
We use different lambda scaling parameters.
results_ScaleMove01 <- MHMCMC_ScaleMove(sum(data), length(data), a, b, num_iterations, 0.1)
results_ScaleMove1 <- MHMCMC_ScaleMove(sum(data), length(data), a, b, num_iterations, 1)
results_ScaleMove10 <- MHMCMC_ScaleMove(sum(data), length(data), a, b, num_iterations, 10)
Let’s look at the sampled parameters
summary(results_ScaleMove01[[1]])
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.5196 0.6107 0.6295 0.6291 0.6478 0.8698
summary(results_ScaleMove1[[1]])
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.5201 0.6118 0.6305 0.6301 0.6489 0.7434
summary(results_ScaleMove10[[1]])
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.5491 0.6116 0.6300 0.6303 0.6471 0.7097
par(mfrow=c(3,1), mar=c(0,4,0,2)+0.1)
plot(density(results_ScaleMove01[[1]]), col=colors[1], lwd=2, main="", ylab="Density", xlab="", ylim=c(0,24), xlim=c(0,1), xaxt="n")
legend("topleft", legend="scaling parameter=0.1")
plot(density(results_ScaleMove1[[1]]), col=colors[2], lwd=2, main="", ylab="Density", xlab="", ylim=c(0,24), xlim=c(0,1), xaxt="n")
legend("topleft", legend="scaling parameter=1")
plot(density(results_ScaleMove10[[1]]), col=colors[3], lwd=2, main="", ylab="Density", xlab="", ylim=c(0,24), xlim=c(0,1), xaxt="n")
legend("topleft", legend="scaling parameter=10")

Could we have sampling problems? Investigating the number of sampled values
Let’s count the number of different parameter values sampled during the MCMC. If the MCMC does not move well through parameter space, it will either: - rarely accept changes because the proposal moves too far, therefore the MCMC always samples the same values - always accept changes because the proposal moves very close, therefore the MCMC moves very slowly through parameter space.
# Number of different values sampled
length(unique(results_ScaleMove01[[1]]))
[1] 38739
length(unique(results_ScaleMove1[[1]]))
[1] 7019
length(unique(results_ScaleMove10[[1]]))
[1] 712
# Ratio of the number to the total number of iterations
length(unique(results_ScaleMove01[[1]]))/num_iterations
[1] 0.77478
length(unique(results_ScaleMove1[[1]]))/num_iterations
[1] 0.14038
length(unique(results_ScaleMove10[[1]]))/num_iterations
[1] 0.01424
The first scaling factor produces moves that are very often accepted, probably too often; the second scaling factor seems quite good, and the last one produces moves that are too rarely accepted. In the latter case, it is likely that in lots of cases, values outside of \([0,1]\) have been proposed and rejected.
We could look for even better sampling characteristics: numerical studies have shown that the optimal asymptotic acceptance rate is 0.234 for multi-dimensional models, and 0.44 for unidimensional models. However, as long as a move has an acceptance probability between \(0.15\) and \(0.5\), one can consider that the chain mixes well, because it is at least \(80%\) efficient compared to optimality (Yang and Rosenthal, 2016, http://probability.ca/jeff/ftpdir/jinyoung1.pdf).
A better scaling parameter for the scaling move
So let’s run another chain, with a scaling parameter that should give better acceptance rates.
results_ScaleMove04 <- MHMCMC_ScaleMove(sum(data), length(data), a, b, num_iterations, 0.4)
length(unique(results_ScaleMove04[[1]]))
[1] 17094
length(unique(results_ScaleMove04[[1]]))/num_iterations
[1] 0.34188
Let’s compare to our Metropolis MCMC which was proposing values uniformly over \([0,1]\):
length(unique(result[[1]]))
[1] 4443
length(unique(result[[1]]))/num_iterations
[1] 0.08886
So the MCMC with a scaling move can have a much better sampling rate than our Metropolis MCMC.
Investigating autocorrelation
However, it could be navigating the parameter space not very efficiently: subsequent samples could be very correlated. To look into this, we can compute the autocorrelation in our samples using the \(acf\) function, which computes the autocorrelation after 1, 2, 3, … n iterations.
par(mfrow=c(5,1), mar=c(0,4,0,2)+0.1)
acf(results_ScaleMove01[[1]], xaxt="n")
legend("topright", "Scaling 0.1")
acf(results_ScaleMove04[[1]], xaxt="n")
legend("topright", "Scaling 0.4")
acf(results_ScaleMove1[[1]], xaxt="n")
legend("topright", "Scaling 1")
acf(results_ScaleMove10[[1]], xaxt="n")
legend("topright", "Scaling 10")
acf(result[[1]])
legend("topright", "Uniform")

The scaling move with scaling parameter 0.4 seems to have the best behavior. The autocorrelation with the scaling parameter 0.1 was a bit worse than that with the parameter 1.0, which confirms that the moves were too narrow.
The effective sample size
It is possible to summarize both the number of different values and the autocorrelation with one single number, the Effective Sample Size (ESS). This is obtained with the following formula:
\[
ESS = \frac{number~of~samples}{1+2\sum_{k=1}^{\infty}\rho(k)}
\]
where \(\rho(k)\) is the autocorrelation plotted in the \(acf\) plots above.
Let’s build an ESS function:
ess <- function (values) {
x <- acf(values, plot=F)
list_of_rhos <- x$acf[2:length(x$acf)]
return (length(values) / (1 + 2*sum(list_of_rhos)) )
}
And we use it on all our chains:
ess(results_ScaleMove01[[1]])
[1] 3764.796
ess(results_ScaleMove04[[1]])
[1] 11204
ess(results_ScaleMove1[[1]])
[1] 4631.386
ess(results_ScaleMove10[[1]])
[1] 812.992
ess(result[[1]])
[1] 3219.739
This confirms that the scaling move with scaling parameter 0.4 is much better than the other MCMCs.
Therefore our best estimate of the distribution should be:
par(mfrow=c(1,1), mar=c(4,4,2,2)+0.1)
plot(density(results_ScaleMove04[[1]]), col="orange", lwd=2, main="", ylab="Density", xlab="Probability of heads", ylim=c(0,24), xlim=c(0,1))
legend("topleft", legend="Distribution obtained with the scaling move, lambda=0.4", lwd=2, col="orange")

LS0tCnRpdGxlOiAiQ29pbiBmbGlwIGFuYWx5c2lzIGJ5IE1DTUM6IGNvbnZlcmdlbmNlIGFuZCBtb3ZlcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKSGVyZSB3ZSB3aWxsIHBlcmZvcm0gQmF5ZXNpYW4gTUNNQyBpbmZlcmVuY2Ugb2YgdGhlIHByb2JhYmlsaXR5IG9mIGhlYWRzIGJhc2VkIG9uIGNvaW4gdG9zc2VzLgpXZSB3aWxsIGludmVzdGlnYXRlIGNvbnZlcmdlbmNlIG9mIHRoZSBNQ01DIGNoYWlucyBpbiB2YXJpb3VzIGNvbmRpdGlvbnMuCgpXZSBoYXZlIHNvbWUgZGF0YSBwcm92aWRpbmcgdGhlIHJlc3VsdHMgb2YgMzAwIGNvaW4gdG9zc2VzLiBXZSB3b3VsZCBsaWtlIHRvIGVzdGltYXRlIGhvdyBmYWlyIHRoZSBjb2luIGlzLCBpLmUuIHdoYXQgaXMgdGhlIHByb2JhYmlsaXR5IG9mIGdldHRpbmcgaGVhZHMgKDEpLgpgYGB7cn0KZGF0YSA9IGMoMSwwLDEsMCwwLDEsMSwxLDAsMCwxLDEsMSwwLDEsMSwxLDAsMCwxLDEsMCwxLDEsMCwxLDEsMCwxLDEsMSwwLDEsMCwwLDEsMSwxLDAsMCwxLDEsMSwwLDEsMSwxLDAsMCwxLDEsMCwxLDEsMCwxLDEsMCwxLDEsMSwwLDEsMCwwLDEsMSwxLDAsMCwxLDEsMSwwLDEsMSwxLDAsMCwxLDEsMCwxLDEsMCwxLDEsMCwxLDEsMSwwLDEsMCwwLDEsMSwxLDAsMCwxLDEsMSwwLDEsMSwxLDAsMCwxLDEsMCwxLDEsMCwxLDEsMCwxLDEsMSwwLDEsMCwwLDEsMSwxLDAsMCwxLDEsMSwwLDEsMSwxLDAsMCwxLDEsMCwxLDEsMCwxLDEsMCwxLDEsMSwwLDEsMCwwLDEsMSwxLDAsMCwxLDEsMSwwLDEsMSwxLDAsMCwxLDEsMCwxLDEsMCwxLDEsMCwxLDEsMSwwLDEsMCwwLDEsMSwxLDAsMCwxLDEsMSwwLDEsMSwxLDAsMCwxLDEsMCwxLDEsMCwxLDEsMCwxLDEsMSwwLDEsMCwwLDEsMSwxLDAsMCwxLDEsMSwwLDEsMSwxLDAsMCwxLDEsMCwxLDEsMCwxLDEsMCwxLDEsMSwwLDEsMCwwLDEsMSwxLDAsMCwxLDEsMSwwLDEsMSwxLDAsMCwxLDEsMCwxLDEsMCwxLDEsMCwxLDEsMSwwLDEsMCwwLDEsMSwxLDAsMCwxLDEsMSwwLDEsMSwxLDAsMCwxLDEsMCwxLDEsMCwxLDEsMCwxLDEpCnN1bShkYXRhKQpgYGAKRnJvbSBvdXIgZGF0YSwgd2Ugc2VlIHRoYXQgd2Ugb2J0YWluZWQgMTkwIGhlYWRzIG91dCBvZiAzMDAgdG9zc2VzLgoKVGhlIE1MIGVzdGltYXRlIG9mIHRoZSBwcm9iYWJpbGl0eSBvZiBoZWFkcyBpcyBzaW1wbHkgdGhlIHJhdGlvIG9mIGhlYWRzIG92ZXIgYWxsIHRvc3NlczogCgpgYGB7cn0Kc3VtKGRhdGEpL2xlbmd0aChkYXRhKQpgYGAKCiMgV2UgYnVpbGQgYSBwcm9iYWJpbGlzdGljIG1vZGVsIG9mIGNvaW4gdG9zc2luZy4KCkFsbCBjb2luIHRvc3NlcyBhcmUgc3VwcG9zZWQgdG8gYmUgaW5kZXBlbmRlbnQgdG9zc2VzIG9mIHRoZSBzYW1lIGNvaW4sIHdoaWNoIGFsd2F5cyBoYXZlIHRoZSBzYW1lIHByb2JhYmlsaXR5IG9mIHJldHVybmluZyBhIGhlYWQuIFdlIHdhbnQgdG8gcGVyZm9ybSBCYXllc2lhbiBpbmZlcmVuY2UsIHRoZXJlZm9yZSB3ZSBuZWVkIGEgcHJpb3IuIEZvciBpbmZlcmVuY2UsIHdlIHdpbGwgYmUgdXNpbmcgTWV0cm9wb2xpcyBNQ01DLgoKCiMjIERlZmluaW5nIGEgcHJpb3IKCldlIG5lZWQgdG8gcHV0IHNvbWUgcHJpb3IgcHJvYmFiaWxpdHkgb24gdGhlIGZhaXJuZXNzIG9mIHRoZSBjb2luLiBGb3IgdGhpcywgYSBiZXRhIGRpc3RyaWJ1dGlvbiBzZWVtcyBhcHByb3ByaWF0ZSwgYXMgaXQgaXMgYSBjb250aW51b3VzIGRpc3RyaWJ1dGlvbiBiZXR3ZWVuIDAgYW5kIDEuIFdlIGNob29zZSB0byB1c2UgYT00LCBiPTQuIFRoaXMgaXMgYSBzb21ld2hhdCBpbmZvcm1hdGl2ZSBwcmlvciB0aGF0IHRoZSBjb2luIHNob3VsZCBiZSBmYWlyLCBnaXZlbiBvdXIgcGFzdCBleHBlcmllbmNlIHdpdGggY29pbnMuCgoKYGBge3J9CnggPC0gc2VxKC0wLCAxLCBsZW5ndGg9MTAwKQpjb2xvcnMgPC0gYygicmVkIiwgInB1cnBsZSIsICJibHVlIiwgImRhcmtncmVlbiIsICJnb2xkIikKc2hhcGUxcyA9IGMoMSwyLDQsMSwwLjIpCnNoYXBlMnMgPSBjKDEsMiw0LDQsMC4yKQphPTQKYj00CmBgYAoKCgojIyBCdWlsZGluZyBvZiB0aGUgbW9kZWwKV2UgYnVpbGQgdGhlIGZ1bmN0aW9ucyBuZWNlc3NhcnkgdG8gY29tcHV0ZSB0aGUgbGlrZWxpaG9vZCBhbmQgdGhlIHByaW9yIHByb2JhYmlsaXR5IGZvciBhIGdpdmVuIHZhbHVlIG9mIHRoZSBwYXJhbWV0ZXIuCgpgYGB7cn0KIyBGdW5jdGlvbiB0byBjb21wdXRlIHRoZSBsaWtlbGlob29kIFAoRHxNKQojIFdlIHVzZSB0aGUgYmlub21pYWwgZm9ybXVsYS4KbGlrZWxpaG9vZCA8LWZ1bmN0aW9uIChudW1IZWFkcywgbnVtVG9zc2VzLCBwYXJhbWV0ZXIpewogICAgcCA9IGNob29zZShudW1Ub3NzZXMsIG51bUhlYWRzKSpwYXJhbWV0ZXJebnVtSGVhZHMgKiAoMS1wYXJhbWV0ZXIpXihudW1Ub3NzZXMtbnVtSGVhZHMpCiAgICByZXR1cm4gKHApCn0KCiMgRnVuY3Rpb24gdG8gY29tcHV0ZSB0aGUgcHJpb3IgUChNKQpwcmlvciA8LSBmdW5jdGlvbiAocGFyYW1ldGVyLCBzaGFwZTEsIHNoYXBlMil7CiAgICByZXR1cm4gKGRiZXRhKHBhcmFtZXRlciwgc2hhcGUxPXNoYXBlMSwgc2hhcGUyPXNoYXBlMikgKQp9CiMgRnVuY3Rpb24gdG8gY29tcHV0ZSB0aGUgdW4tbm9ybWFsaXplZCBwb3N0ZXJpb3IgUChEfE0pICogUChNKQp1bm5vcm1hbGl6ZWRfcG9zdGVyaW9yIDwtZnVuY3Rpb24gKG51bUhlYWRzLCBudW1Ub3NzZXMsIHBhcmFtZXRlciwgc2hhcGUxLCBzaGFwZTIpIHsKICAgIHJldHVybiAobGlrZWxpaG9vZChudW1IZWFkcywgbnVtVG9zc2VzLCBwYXJhbWV0ZXIpICogcHJpb3IocGFyYW1ldGVyLCBzaGFwZTEsIHNoYXBlMikpCn0KYGBgCgoKIyMgSW5mZXJlbmNlIG9mIHRoZSBmYWlybmVzcyBvZiB0aGUgY29pbiB1c2luZyBNQ01DCgpXZSBidWlsZCBhIE1DTUMgY2hhaW4gdG8gZXN0aW1hdGUgdGhlIHByb2JhYmlsaXR5IG9mIGhlYWRzIGZvciB0aGlzIGNvaW4uIEZpcnN0IHdlIGRlZmluZSB0aGUgbW9kZWwsIHdpdGggdGhlIHByaW9yLCB0aGUgbGlrZWxpaG9vZCBhbmQgdGhlIHBvc3RlcmlvciBwcm9iYWJpbGl0eSwgdGhlbiB3ZSBpbXBsZW1lbnQgYSBNZXRyb3BvbGlzIE1DTUMgaW5mZXJlbmNlIG1lY2hhbmlzbS4KCgojIyMgSW1wbGVtZW50aW5nIHRoZSBNQ01DIGFsZ29yaXRobQoKYGBge3J9CiMgTnVtYmVyIG9mIGl0ZXJhdGlvbnMgZm9yIHRoZSBNQ01DIGFsZ29yaXRobQpudW1faXRlcmF0aW9ucyA9IDUwMDAwCgojIEZ1bmN0aW9uIHRvIHByb3Bvc2UgYSBuZXcgcGFyYW1ldGVyIHZhbHVlLCByYW5kb21seSBkcmF3biBiZXR3ZWVuIDAgYW5kIDEKcHJvcG9zZV9uZXdfcGFyYW1ldGVyX3ZhbHVlIDwtZnVuY3Rpb24oKSB7CiAgICByZXR1cm4gKHJ1bmlmKDEpKQp9CgojIEZ1bmN0aW9uIHRvIHJ1biBNZXRyb3BvbGlzIE1DTUMgaW5mZXJlbmNlCk1ldHJvcG9saXNNQ01DIDwtIGZ1bmN0aW9uIChudW1IZWFkcywgbnVtVG9zc2VzLCBzaGFwZTEsIHNoYXBlMiwgbnVtYmVyX2l0ZXJhdGlvbnMpIHsKICAgIGN1cnJlbnRfcGFyYW1ldGVyX3ZhbHVlIDwtIHByb3Bvc2VfbmV3X3BhcmFtZXRlcl92YWx1ZSgpCiAgICByZWNvcmRfcGFyYW1ldGVyIDwtIGMoKQogICAgcmVjb3JkX3BhcmFtZXRlciA8LSBjKHJlY29yZF9wYXJhbWV0ZXIsY3VycmVudF9wYXJhbWV0ZXJfdmFsdWUpCiAgICAjcHJpbnQocGFzdGUoIkluaXRpYWwgcGFyYW1ldGVyIHZhbHVlIGZvciB0aGUgTUNNQzogIiwgY3VycmVudF9wYXJhbWV0ZXJfdmFsdWUsIHNlcD0iIikpCiAgICBjdXJyZW50X3Bvc3RlcmlvciA8LSB1bm5vcm1hbGl6ZWRfcG9zdGVyaW9yKG51bUhlYWRzLCBudW1Ub3NzZXMsIGN1cnJlbnRfcGFyYW1ldGVyX3ZhbHVlLCBzaGFwZTEsIHNoYXBlMikKICAgICNwcmludChwYXN0ZSgiSW5pdGlhbCBwcm9iYWJpbGl0eSBvZiB0aGUgbW9kZWw6ICIsIGN1cnJlbnRfcG9zdGVyaW9yLCBzZXA9IiIpKQogICAgcmVjb3JkX3Bvc3RlcmlvciA8LSBjKCkKICAgIHJlY29yZF9wb3N0ZXJpb3IgPC0gYyhyZWNvcmRfcG9zdGVyaW9yLCBjdXJyZW50X3Bvc3RlcmlvcikKICAgICMgV2UgYWxzbyByZWNvcmQgdGhlIGxpa2VsaWhvb2Q6CiAgICBjdXJyZW50X2xpa2VsaWhvb2QgPC0gbGlrZWxpaG9vZChudW1IZWFkcywgbnVtVG9zc2VzLCBjdXJyZW50X3BhcmFtZXRlcl92YWx1ZSkKICAgIHJlY29yZF9saWtlbGlob29kIDwtIGMoKQogICAgcmVjb3JkX2xpa2VsaWhvb2QgPC0gYyhyZWNvcmRfbGlrZWxpaG9vZCwgY3VycmVudF9saWtlbGlob29kKQogICAgZm9yIChpIGluIDE6bnVtYmVyX2l0ZXJhdGlvbnMpIHsKICAgICAgICBhY2NlcHRhbmNlX3RocmVzaG9sZCA8LSBydW5pZigxKQogICAgICAgIHByb3Bvc2VkX3BhcmFtZXRlcl92YWx1ZSA8LSBwcm9wb3NlX25ld19wYXJhbWV0ZXJfdmFsdWUoKQogICAgICAgIHByb3Bvc2VkX3Bvc3RlcmlvciA9IHVubm9ybWFsaXplZF9wb3N0ZXJpb3IobnVtSGVhZHMgLCBudW1Ub3NzZXMsIHByb3Bvc2VkX3BhcmFtZXRlcl92YWx1ZSwgc2hhcGUxLCBzaGFwZTIpCiAgICAgICAgaWYgKHByb3Bvc2VkX3Bvc3RlcmlvciAvIGN1cnJlbnRfcG9zdGVyaW9yID4gYWNjZXB0YW5jZV90aHJlc2hvbGQpIHsKICAgICAgICAgICAgY3VycmVudF9wYXJhbWV0ZXJfdmFsdWUgPC0gcHJvcG9zZWRfcGFyYW1ldGVyX3ZhbHVlCiAgICAgICAgICAgIGN1cnJlbnRfcG9zdGVyaW9yIDwtIHByb3Bvc2VkX3Bvc3RlcmlvcgogICAgICAgICAgICBjdXJyZW50X2xpa2VsaWhvb2QgPC0gbGlrZWxpaG9vZChudW1IZWFkcywgbnVtVG9zc2VzLCBjdXJyZW50X3BhcmFtZXRlcl92YWx1ZSkKICAgICAgICB9CiAgICAgICAgcmVjb3JkX3BhcmFtZXRlciA8LSBjKHJlY29yZF9wYXJhbWV0ZXIsIGN1cnJlbnRfcGFyYW1ldGVyX3ZhbHVlKQogICAgICAgIHJlY29yZF9wb3N0ZXJpb3IgPC1jKHJlY29yZF9wb3N0ZXJpb3IsIGN1cnJlbnRfcG9zdGVyaW9yKQogICAgICAgIHJlY29yZF9saWtlbGlob29kIDwtIGMocmVjb3JkX2xpa2VsaWhvb2QsIGN1cnJlbnRfbGlrZWxpaG9vZCkKICAgIH0KICAgIHJldHVybiAobGlzdChyZWNvcmRfcGFyYW1ldGVyLCByZWNvcmRfcG9zdGVyaW9yLCByZWNvcmRfbGlrZWxpaG9vZCkpCn0KYGBgCgojIyMgUnVubmluZyB0aGUgTUNNQwoKYGBge3J9CnJlc3VsdCA9IE1ldHJvcG9saXNNQ01DKHN1bShkYXRhKSwgbGVuZ3RoKGRhdGEpLCBhLCBiLCBudW1faXRlcmF0aW9ucykKCmBgYAoKIyMgQ29tcGFyaXNvbiBvZiB0aGUgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbnMgd2l0aCByZXNwZWN0IHRvIHRoZSBudW1iZXIgb2YgaXRlcmF0aW9ucwoKV2UgcGxvdCBpbiBkaWZmZXJlbnQgY29sb3JzIHRoZSBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9ucy4KCmBgYHtyfQpwYXIobWZyb3c9Yyg2LDEpLCBtYXI9YygwLDQsMCwyKSswLjEpCnBsb3QoZGVuc2l0eShyZXN1bHRbWzFdXVsxOjEwXSksIGNvbD1jb2xvcnNbMV0sIGx3ZD0yLCBtYWluPSIiLCB5bGFiPSJEZW5zaXR5IiwgeGxhYj0iIiwgeWxpbT1jKDAsMjQpLCB4bGltPWMoMCwxKSwgeGF4dD0ibiIpCmxlZ2VuZCgidG9wbGVmdCIsIGxlZ2VuZD0iMTAgaXRlcmF0aW9ucyIpCnBsb3QoZGVuc2l0eShyZXN1bHRbWzFdXVsxOjUwXSksIGNvbD1jb2xvcnNbMl0sIGx3ZD0yLCBtYWluPSIiLCB5bGFiPSJEZW5zaXR5IiwgeGxhYj0iIiwgeWxpbT1jKDAsMjQpLCB4bGltPWMoMCwxKSwgeGF4dD0ibiIpCmxlZ2VuZCgidG9wbGVmdCIsIGxlZ2VuZD0iNTAgaXRlcmF0aW9ucyIpCnBsb3QoZGVuc2l0eShyZXN1bHRbWzFdXVsxOjEwMF0pLCBjb2w9Y29sb3JzWzNdLCBsd2Q9MiwgbWFpbj0iIiwgeWxhYj0iRGVuc2l0eSIsIHhsYWI9IiIsIHlsaW09YygwLDI0KSwgeGxpbT1jKDAsMSksIHhheHQ9Im4iKQpsZWdlbmQoInRvcGxlZnQiLCBsZWdlbmQ9IjEwMCBpdGVyYXRpb25zIikKcGxvdChkZW5zaXR5KHJlc3VsdFtbMV1dWzE6MTAwMF0pLCBjb2w9Y29sb3JzWzRdLCBsd2Q9MiwgbWFpbj0iIiwgeWxhYj0iRGVuc2l0eSIsIHhsYWI9IiIsIHlsaW09YygwLDI0KSwgeGxpbT1jKDAsMSksIHhheHQ9Im4iKQpsZWdlbmQoInRvcGxlZnQiLCBsZWdlbmQ9IjEwMDAgaXRlcmF0aW9ucyIpCnBsb3QoZGVuc2l0eShyZXN1bHRbWzFdXVsxOjIwMDAwXSksIGNvbD1jb2xvcnNbNV0sIGx3ZD0yLCBtYWluPSIiLCB5bGFiPSJEZW5zaXR5IiwgeGxhYj0iIiwgeWxpbT1jKDAsMjQpLCB4bGltPWMoMCwxKSwgeGF4dD0ibiIpCmxlZ2VuZCgidG9wbGVmdCIsIGxlZ2VuZD0iMjAgMDAwIGl0ZXJhdGlvbnMiKQpwbG90KGRlbnNpdHkocmVzdWx0W1sxXV0pLCBjb2w9ImJsYWNrIiwgbHdkPTIsIG1haW49IiIsIHlsYWI9IkRlbnNpdHkiLCB4bGFiPSJQcm9iYWJpbGl0eSBvZiBIZWFkcyIsIHlsaW09YygwLDI0KSwgeGxpbT1jKDAsMSkpCmxlZ2VuZCgidG9wbGVmdCIsIGxlZ2VuZD0iNTAgMDAwIGl0ZXJhdGlvbnMiKQpgYGAKCgpMb29raW5nIGF0IHN1bW1hcnkgc3RhdGlzdGljcyBwcm92aWRlcyB0aGUgc2FtZSBpbmZvcm1hdGlvbjoKYGBge3J9CnN1bW1hcnkocmVzdWx0W1sxXV1bMToxMF0pCnN1bW1hcnkocmVzdWx0W1sxXV1bMTo1MF0pCnN1bW1hcnkocmVzdWx0W1sxXV1bMToxMDBdKQpzdW1tYXJ5KHJlc3VsdFtbMV1dWzE6MTAwMF0pCnN1bW1hcnkocmVzdWx0W1sxXV1bMToyMDAwMF0pCnN1bW1hcnkocmVzdWx0W1sxXV0pCmBgYAoKQXMgdGhlIG51bWJlciBvZiBpdGVyYXRpb25zIGluY3JlYXNlcywgdGhlIGVzdGltYXRlcyBjb252ZXJnZS4KCiMjIEJldHRlciBtb3ZlcyBmb3IgYmV0dGVyIGNvbnZlcmdlbmNlCkluIHRoZSBNZXRyb3BvbGlzIE1DTUMgYWJvdmUsIGEgbmV3IHN0YXRlIGlzIHByb3Bvc2VkIGFjY29yZGluZyB0byBhIFVuaWZvcm0gZGlzdHJpYnV0aW9uLiBUaGlzIGlzIHByb2JhYmx5IHN1Ym9wdGltYWw6IGFmdGVyIGFsbCwgaWYgYXQgaXRlcmF0aW9uICRuJCBpbiB0aGUgY2hhaW4gd2UgaGF2ZSBhIHBhcmFtZXRlciB2YWx1ZSB3aXRoIGEgaGlnaCBwb3N0ZXJpb3IgcHJvYmFiaWxpdHksIGl0J3MgcHJvYmFibHkgYSBnb29kIGlkZWEgdG8gcHJvcG9zZSBhIG5ldyB2YWx1ZSBmb3IgaXRlcmF0aW9uICRuKzEkIGluIHRoZSB2aWNpbml0eSBvZiB0aGUgcGFyYW1ldGVyIHZhbHVlIGluc3RlYWQgb2YgY29tcGxldGVseSByYW5kb21seSBvdmVyICRbMDoxXSQuClRoZXJlZm9yZSwgd2UgaW1wbGVtZW50IGEgbmV3IG1vdmUsIHRoZSBTY2FsZSBtb3ZlLgoKIyMjIFNjYWxlIG1vdmUKYGBge3J9CiMgRnVuY3Rpb24gdG8gcHJvcG9zZSBhIG5ldyBwYXJhbWV0ZXIgdmFsdWUsIGFjY29yZGluZyB0byBhIHNjYWxpbmcgbW92ZSBjZW50ZXJlZCBvbiB0aGUgY3VycmVudCB2YWx1ZQpzY2FsZV9tb3ZlX3Byb3Bvc2VfbmV3X3BhcmFtZXRlcl92YWx1ZSA8LWZ1bmN0aW9uKGN1cnJlbnRfcGFyYW1ldGVyX3ZhbHVlLCBsYW1iZGFfc2NhbGluZ19wYXJhbWV0ZXIpIHsKICAjIFByb3Bvc2UgYSBuZXcgdmFsdWUgb2YgcAogIHNjYWxpbmdfZmFjdG9yIDwtIGV4cCggbGFtYmRhX3NjYWxpbmdfcGFyYW1ldGVyICogKCBydW5pZigxKSAtIDAuNSApICkKICBwX3ByaW1lIDwtIGN1cnJlbnRfcGFyYW1ldGVyX3ZhbHVlICogc2NhbGluZ19mYWN0b3IKICBIYXN0aW5nc19yYXRpbyA8LSBzY2FsaW5nX2ZhY3RvcgogIHJldHVybihsaXN0KHBfcHJpbWUsIEhhc3RpbmdzX3JhdGlvKSkKfQoKYGBgClRoZSBzY2FsZSBtb3ZlIGlzIG5vbi1zeW1tZXRyaWM6IGl0IGlzIG1vcmUgbGlrZWx5IHRvIHByb3Bvc2Ugc21hbGxlciB2YWx1ZXMgdGhhbiBsYXJnZXIgdmFsdWVzLiBUaGVyZWZvcmUgYSBIYXN0aW5ncyByYXRpbyBuZWVkcyB0byBiZSBjb21wdXRlZC4gSW4gdGhlIGNhc2Ugb2YgdGhlIHNjYWxlIG1vdmUsIHRoZSBIYXN0aW5ncyByYXRpbyB0dXJucyBvdXQgdG8gYmUgc2ltcGxlIHRvIGNvbXB1dGUsIGFuZCBpcyB0aGUgc2NhbGluZyBmYWN0b3IgdXNlZCBvbiB0aGUgY3VycmVudCBwYXJhbWV0ZXIgdmFsdWUgdG8gcHJvcG9zZSB0aGUgbmV3IHZhbHVlLgoKIyMjIE1DTUMgd2l0aCB0aGUgc2NhbGUgbW92ZQpXZSBpbXBsZW1lbnQgYSBuZXcgTUNNQyBmdW5jdGlvbiB0aGF0IHdpbGwgdXNlIHRoaXMgU2NhbGUgbW92ZSBpbnN0ZWFkIG9mIHRoZSBwcmV2aW91cyB1bmlmb3JtIG1vdmUuCgpgYGB7cn0KIyBGdW5jdGlvbiB0byBydW4gTWV0cm9wb2xpcyBNQ01DIGluZmVyZW5jZQpNSE1DTUNfU2NhbGVNb3ZlIDwtIGZ1bmN0aW9uIChudW1IZWFkcywgbnVtVG9zc2VzLCBzaGFwZTEsIHNoYXBlMiwgbnVtYmVyX2l0ZXJhdGlvbnMsIGxhbWJkYV9zY2FsaW5nX3BhcmFtZXRlcikgewogICAgY3VycmVudF9wYXJhbWV0ZXJfdmFsdWUgPC0gcHJvcG9zZV9uZXdfcGFyYW1ldGVyX3ZhbHVlKCkKICAgIHJlY29yZF9wYXJhbWV0ZXIgPC0gYygpCiAgICByZWNvcmRfcGFyYW1ldGVyIDwtIGMocmVjb3JkX3BhcmFtZXRlcixjdXJyZW50X3BhcmFtZXRlcl92YWx1ZSkKICAgICNwcmludChwYXN0ZSgiSW5pdGlhbCBwYXJhbWV0ZXIgdmFsdWUgZm9yIHRoZSBNQ01DOiAiLCBjdXJyZW50X3BhcmFtZXRlcl92YWx1ZSwgc2VwPSIiKSkKICAgIGN1cnJlbnRfcG9zdGVyaW9yIDwtIHVubm9ybWFsaXplZF9wb3N0ZXJpb3IobnVtSGVhZHMsIG51bVRvc3NlcywgY3VycmVudF9wYXJhbWV0ZXJfdmFsdWUsIHNoYXBlMSwgc2hhcGUyKQogICAgI3ByaW50KHBhc3RlKCJJbml0aWFsIHByb2JhYmlsaXR5IG9mIHRoZSBtb2RlbDogIiwgY3VycmVudF9wb3N0ZXJpb3IsIHNlcD0iIikpCiAgICByZWNvcmRfcG9zdGVyaW9yIDwtIGMoKQogICAgcmVjb3JkX3Bvc3RlcmlvciA8LSBjKHJlY29yZF9wb3N0ZXJpb3IsIGN1cnJlbnRfcG9zdGVyaW9yKQogICAgIyBXZSBhbHNvIHJlY29yZCB0aGUgbGlrZWxpaG9vZDoKICAgIGN1cnJlbnRfbGlrZWxpaG9vZCA8LSBsaWtlbGlob29kKG51bUhlYWRzLCBudW1Ub3NzZXMsIGN1cnJlbnRfcGFyYW1ldGVyX3ZhbHVlKQogICAgcmVjb3JkX2xpa2VsaWhvb2QgPC0gYygpCiAgICByZWNvcmRfbGlrZWxpaG9vZCA8LSBjKHJlY29yZF9saWtlbGlob29kLCBjdXJyZW50X2xpa2VsaWhvb2QpCiAgICBmb3IgKGkgaW4gMTpudW1iZXJfaXRlcmF0aW9ucykgewogICAgICAgIGFjY2VwdGFuY2VfdGhyZXNob2xkIDwtIHJ1bmlmKDEpCiAgICAgICAgIAogICAgICAgIHZhbHVlX2FuZF9IYXN0aW5nc1JhdGlvIDwtIHNjYWxlX21vdmVfcHJvcG9zZV9uZXdfcGFyYW1ldGVyX3ZhbHVlKGN1cnJlbnRfcGFyYW1ldGVyX3ZhbHVlLCBsYW1iZGFfc2NhbGluZ19wYXJhbWV0ZXIgPSBsYW1iZGFfc2NhbGluZ19wYXJhbWV0ZXIpCiAgICAgICAgcHJvcG9zZWRfcGFyYW1ldGVyX3ZhbHVlIDwtIHZhbHVlX2FuZF9IYXN0aW5nc1JhdGlvW1sxXV0KICAgICAgICBIYXN0aW5nc19yYXRpbyA8LSB2YWx1ZV9hbmRfSGFzdGluZ3NSYXRpb1tbMl1dCiAgICAgICAgaWYgKHByb3Bvc2VkX3BhcmFtZXRlcl92YWx1ZSA+IDAgJiBwcm9wb3NlZF9wYXJhbWV0ZXJfdmFsdWUgPCAxICkgewogICAgICAgICAgcHJvcG9zZWRfcG9zdGVyaW9yID0gdW5ub3JtYWxpemVkX3Bvc3RlcmlvcihudW1IZWFkcyAsIG51bVRvc3NlcywgcHJvcG9zZWRfcGFyYW1ldGVyX3ZhbHVlLCBzaGFwZTEsIHNoYXBlMikgKiBIYXN0aW5nc19yYXRpbwogICAgICAgICAgaWYgKHByb3Bvc2VkX3Bvc3RlcmlvciAvIGN1cnJlbnRfcG9zdGVyaW9yID4gYWNjZXB0YW5jZV90aHJlc2hvbGQpIHsKICAgICAgICAgICAgICBjdXJyZW50X3BhcmFtZXRlcl92YWx1ZSA8LSBwcm9wb3NlZF9wYXJhbWV0ZXJfdmFsdWUKICAgICAgICAgICAgICBjdXJyZW50X3Bvc3RlcmlvciA8LSBwcm9wb3NlZF9wb3N0ZXJpb3IKICAgICAgICAgICAgICBjdXJyZW50X2xpa2VsaWhvb2QgPC0gbGlrZWxpaG9vZChudW1IZWFkcywgbnVtVG9zc2VzLCBjdXJyZW50X3BhcmFtZXRlcl92YWx1ZSkKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgcmVjb3JkX3BhcmFtZXRlciA8LSBjKHJlY29yZF9wYXJhbWV0ZXIsIGN1cnJlbnRfcGFyYW1ldGVyX3ZhbHVlKQogICAgICAgIHJlY29yZF9wb3N0ZXJpb3IgPC1jKHJlY29yZF9wb3N0ZXJpb3IsIGN1cnJlbnRfcG9zdGVyaW9yKQogICAgICAgIHJlY29yZF9saWtlbGlob29kIDwtIGMocmVjb3JkX2xpa2VsaWhvb2QsIGN1cnJlbnRfbGlrZWxpaG9vZCkKICAgIH0KICAgIHJldHVybiAobGlzdChyZWNvcmRfcGFyYW1ldGVyLCByZWNvcmRfcG9zdGVyaW9yLCByZWNvcmRfbGlrZWxpaG9vZCkpCn0KYGBgCgoKIyMjIFJ1bm5pbmcgdGhlIE1DTUMgd2l0aCB0aGUgc2NhbGUgbW92ZQoKV2UgdXNlIGRpZmZlcmVudCBsYW1iZGEgc2NhbGluZyBwYXJhbWV0ZXJzLgpgYGB7cn0KcmVzdWx0c19TY2FsZU1vdmUwMSA8LSBNSE1DTUNfU2NhbGVNb3ZlKHN1bShkYXRhKSwgbGVuZ3RoKGRhdGEpLCBhLCBiLCBudW1faXRlcmF0aW9ucywgMC4xKQpyZXN1bHRzX1NjYWxlTW92ZTEgPC0gTUhNQ01DX1NjYWxlTW92ZShzdW0oZGF0YSksIGxlbmd0aChkYXRhKSwgYSwgYiwgbnVtX2l0ZXJhdGlvbnMsIDEpCnJlc3VsdHNfU2NhbGVNb3ZlMTAgPC0gTUhNQ01DX1NjYWxlTW92ZShzdW0oZGF0YSksIGxlbmd0aChkYXRhKSwgYSwgYiwgbnVtX2l0ZXJhdGlvbnMsIDEwKQoKYGBgCgojIyMgTGV0J3MgbG9vayBhdCB0aGUgc2FtcGxlZCBwYXJhbWV0ZXJzCmBgYHtyfQpzdW1tYXJ5KHJlc3VsdHNfU2NhbGVNb3ZlMDFbWzFdXSkKc3VtbWFyeShyZXN1bHRzX1NjYWxlTW92ZTFbWzFdXSkgCnN1bW1hcnkocmVzdWx0c19TY2FsZU1vdmUxMFtbMV1dKSAKCmBgYApgYGB7cn0KcGFyKG1mcm93PWMoMywxKSwgbWFyPWMoMCw0LDAsMikrMC4xKQpwbG90KGRlbnNpdHkocmVzdWx0c19TY2FsZU1vdmUwMVtbMV1dKSwgY29sPWNvbG9yc1sxXSwgbHdkPTIsIG1haW49IiIsIHlsYWI9IkRlbnNpdHkiLCB4bGFiPSIiLCB5bGltPWMoMCwyNCksIHhsaW09YygwLDEpLCB4YXh0PSJuIikKbGVnZW5kKCJ0b3BsZWZ0IiwgbGVnZW5kPSJzY2FsaW5nIHBhcmFtZXRlcj0wLjEiKQpwbG90KGRlbnNpdHkocmVzdWx0c19TY2FsZU1vdmUxW1sxXV0pLCBjb2w9Y29sb3JzWzJdLCBsd2Q9MiwgbWFpbj0iIiwgeWxhYj0iRGVuc2l0eSIsIHhsYWI9IiIsIHlsaW09YygwLDI0KSwgeGxpbT1jKDAsMSksIHhheHQ9Im4iKQpsZWdlbmQoInRvcGxlZnQiLCBsZWdlbmQ9InNjYWxpbmcgcGFyYW1ldGVyPTEiKQpwbG90KGRlbnNpdHkocmVzdWx0c19TY2FsZU1vdmUxMFtbMV1dKSwgY29sPWNvbG9yc1szXSwgbHdkPTIsIG1haW49IiIsIHlsYWI9IkRlbnNpdHkiLCB4bGFiPSIiLCB5bGltPWMoMCwyNCksIHhsaW09YygwLDEpLCB4YXh0PSJuIikKbGVnZW5kKCJ0b3BsZWZ0IiwgbGVnZW5kPSJzY2FsaW5nIHBhcmFtZXRlcj0xMCIpCgpgYGAKCiMjIyBDb3VsZCB3ZSBoYXZlIHNhbXBsaW5nIHByb2JsZW1zPyBJbnZlc3RpZ2F0aW5nIHRoZSBudW1iZXIgb2Ygc2FtcGxlZCB2YWx1ZXMKCkxldCdzIGNvdW50IHRoZSBudW1iZXIgb2YgZGlmZmVyZW50IHBhcmFtZXRlciB2YWx1ZXMgc2FtcGxlZCBkdXJpbmcgdGhlIE1DTUMuIElmIHRoZSBNQ01DIGRvZXMgbm90IG1vdmUgd2VsbCB0aHJvdWdoIHBhcmFtZXRlciBzcGFjZSwgaXQgd2lsbCBlaXRoZXI6Ci0gcmFyZWx5IGFjY2VwdCBjaGFuZ2VzIGJlY2F1c2UgdGhlIHByb3Bvc2FsIG1vdmVzIHRvbyBmYXIsIHRoZXJlZm9yZSB0aGUgTUNNQyBhbHdheXMgc2FtcGxlcyB0aGUgc2FtZSB2YWx1ZXMKLSBhbHdheXMgYWNjZXB0IGNoYW5nZXMgYmVjYXVzZSB0aGUgcHJvcG9zYWwgbW92ZXMgdmVyeSBjbG9zZSwgdGhlcmVmb3JlIHRoZSBNQ01DIG1vdmVzIHZlcnkgc2xvd2x5IHRocm91Z2ggcGFyYW1ldGVyIHNwYWNlLgoKYGBge3J9CiMgTnVtYmVyIG9mIGRpZmZlcmVudCB2YWx1ZXMgc2FtcGxlZApsZW5ndGgodW5pcXVlKHJlc3VsdHNfU2NhbGVNb3ZlMDFbWzFdXSkpCmxlbmd0aCh1bmlxdWUocmVzdWx0c19TY2FsZU1vdmUxW1sxXV0pKQpsZW5ndGgodW5pcXVlKHJlc3VsdHNfU2NhbGVNb3ZlMTBbWzFdXSkpCgojIFJhdGlvIG9mIHRoZSBudW1iZXIgdG8gdGhlIHRvdGFsIG51bWJlciBvZiBpdGVyYXRpb25zCmxlbmd0aCh1bmlxdWUocmVzdWx0c19TY2FsZU1vdmUwMVtbMV1dKSkvbnVtX2l0ZXJhdGlvbnMKbGVuZ3RoKHVuaXF1ZShyZXN1bHRzX1NjYWxlTW92ZTFbWzFdXSkpL251bV9pdGVyYXRpb25zCmxlbmd0aCh1bmlxdWUocmVzdWx0c19TY2FsZU1vdmUxMFtbMV1dKSkvbnVtX2l0ZXJhdGlvbnMKYGBgCgpUaGUgZmlyc3Qgc2NhbGluZyBmYWN0b3IgcHJvZHVjZXMgbW92ZXMgdGhhdCBhcmUgdmVyeSBvZnRlbiBhY2NlcHRlZCwgcHJvYmFibHkgdG9vIG9mdGVuOyB0aGUgc2Vjb25kIHNjYWxpbmcgZmFjdG9yIHNlZW1zIHF1aXRlIGdvb2QsIGFuZCB0aGUgbGFzdCBvbmUgcHJvZHVjZXMgbW92ZXMgdGhhdCBhcmUgdG9vIHJhcmVseSBhY2NlcHRlZC4gSW4gdGhlIGxhdHRlciBjYXNlLCBpdCBpcyBsaWtlbHkgdGhhdCBpbiBsb3RzIG9mIGNhc2VzLCB2YWx1ZXMgb3V0c2lkZSBvZiAkWzAsMV0kIGhhdmUgYmVlbiBwcm9wb3NlZCBhbmQgcmVqZWN0ZWQuCgpXZSBjb3VsZCBsb29rIGZvciBldmVuIGJldHRlciBzYW1wbGluZyBjaGFyYWN0ZXJpc3RpY3M6IG51bWVyaWNhbCBzdHVkaWVzIGhhdmUgc2hvd24gdGhhdCB0aGUgb3B0aW1hbCBhc3ltcHRvdGljIGFjY2VwdGFuY2UgcmF0ZSBpcyAwLjIzNCBmb3IgbXVsdGktZGltZW5zaW9uYWwgbW9kZWxzLCBhbmQgMC40NCBmb3IgdW5pZGltZW5zaW9uYWwgbW9kZWxzLiBIb3dldmVyLCBhcyBsb25nIGFzIGEgbW92ZSBoYXMgYW4gYWNjZXB0YW5jZSBwcm9iYWJpbGl0eSBiZXR3ZWVuICQwLjE1JCBhbmQgJDAuNSQsIG9uZSBjYW4gY29uc2lkZXIgdGhhdCB0aGUgY2hhaW4gbWl4ZXMgd2VsbCwgYmVjYXVzZSBpdCBpcyBhdCBsZWFzdCAkODAlJCBlZmZpY2llbnQgY29tcGFyZWQgdG8gb3B0aW1hbGl0eSAoWWFuZyBhbmQgUm9zZW50aGFsLCAyMDE2LCBodHRwOi8vcHJvYmFiaWxpdHkuY2EvamVmZi9mdHBkaXIvamlueW91bmcxLnBkZikuCgojIyMgQSBiZXR0ZXIgc2NhbGluZyBwYXJhbWV0ZXIgZm9yIHRoZSBzY2FsaW5nIG1vdmUKU28gbGV0J3MgcnVuIGFub3RoZXIgY2hhaW4sIHdpdGggYSBzY2FsaW5nIHBhcmFtZXRlciB0aGF0IHNob3VsZCBnaXZlIGJldHRlciBhY2NlcHRhbmNlIHJhdGVzLgoKYGBge3J9CnJlc3VsdHNfU2NhbGVNb3ZlMDQgPC0gTUhNQ01DX1NjYWxlTW92ZShzdW0oZGF0YSksIGxlbmd0aChkYXRhKSwgYSwgYiwgbnVtX2l0ZXJhdGlvbnMsIDAuNCkKYGBgCgoKYGBge3J9Cmxlbmd0aCh1bmlxdWUocmVzdWx0c19TY2FsZU1vdmUwNFtbMV1dKSkKbGVuZ3RoKHVuaXF1ZShyZXN1bHRzX1NjYWxlTW92ZTA0W1sxXV0pKS9udW1faXRlcmF0aW9ucwpgYGAKCkxldCdzIGNvbXBhcmUgdG8gb3VyIE1ldHJvcG9saXMgTUNNQyB3aGljaCB3YXMgcHJvcG9zaW5nIHZhbHVlcyB1bmlmb3JtbHkgb3ZlciAkWzAsMV0kOgoKYGBge3J9Cmxlbmd0aCh1bmlxdWUocmVzdWx0W1sxXV0pKQpsZW5ndGgodW5pcXVlKHJlc3VsdFtbMV1dKSkvbnVtX2l0ZXJhdGlvbnMKYGBgCgpTbyB0aGUgTUNNQyB3aXRoIGEgc2NhbGluZyBtb3ZlIGNhbiBoYXZlIGEgbXVjaCBiZXR0ZXIgc2FtcGxpbmcgcmF0ZSB0aGFuIG91ciBNZXRyb3BvbGlzIE1DTUMuCgoKIyMjIEludmVzdGlnYXRpbmcgYXV0b2NvcnJlbGF0aW9uCkhvd2V2ZXIsIGl0IGNvdWxkIGJlIG5hdmlnYXRpbmcgdGhlIHBhcmFtZXRlciBzcGFjZSBub3QgdmVyeSBlZmZpY2llbnRseTogc3Vic2VxdWVudCBzYW1wbGVzIGNvdWxkIGJlIHZlcnkgY29ycmVsYXRlZC4gVG8gbG9vayBpbnRvIHRoaXMsIHdlIGNhbiBjb21wdXRlIHRoZSBhdXRvY29ycmVsYXRpb24gaW4gb3VyIHNhbXBsZXMgdXNpbmcgdGhlICRhY2YkIGZ1bmN0aW9uLCB3aGljaCBjb21wdXRlcyB0aGUgYXV0b2NvcnJlbGF0aW9uIGFmdGVyIDEsIDIsIDMsIC4uLiBuIGl0ZXJhdGlvbnMuCgoKYGBge3J9CnBhcihtZnJvdz1jKDUsMSksIG1hcj1jKDAsNCwwLDIpKzAuMSkKYWNmKHJlc3VsdHNfU2NhbGVNb3ZlMDFbWzFdXSwgeGF4dD0ibiIpCmxlZ2VuZCgidG9wcmlnaHQiLCAiU2NhbGluZyAwLjEiKQphY2YocmVzdWx0c19TY2FsZU1vdmUwNFtbMV1dLCB4YXh0PSJuIikKbGVnZW5kKCJ0b3ByaWdodCIsICJTY2FsaW5nIDAuNCIpCmFjZihyZXN1bHRzX1NjYWxlTW92ZTFbWzFdXSwgeGF4dD0ibiIpCmxlZ2VuZCgidG9wcmlnaHQiLCAiU2NhbGluZyAxIikKYWNmKHJlc3VsdHNfU2NhbGVNb3ZlMTBbWzFdXSwgeGF4dD0ibiIpCmxlZ2VuZCgidG9wcmlnaHQiLCAiU2NhbGluZyAxMCIpCmFjZihyZXN1bHRbWzFdXSkKbGVnZW5kKCJ0b3ByaWdodCIsICJVbmlmb3JtIikKCmBgYAoKClRoZSBzY2FsaW5nIG1vdmUgd2l0aCBzY2FsaW5nIHBhcmFtZXRlciAwLjQgc2VlbXMgdG8gaGF2ZSB0aGUgYmVzdCBiZWhhdmlvci4gVGhlIGF1dG9jb3JyZWxhdGlvbiB3aXRoIHRoZSBzY2FsaW5nIHBhcmFtZXRlciAwLjEgd2FzIGEgYml0IHdvcnNlIHRoYW4gdGhhdCB3aXRoIHRoZSBwYXJhbWV0ZXIgMS4wLCB3aGljaCBjb25maXJtcyB0aGF0IHRoZSBtb3ZlcyB3ZXJlIHRvbyBuYXJyb3cuCgoKIyMjIFRoZSBlZmZlY3RpdmUgc2FtcGxlIHNpemUKSXQgaXMgcG9zc2libGUgdG8gc3VtbWFyaXplIGJvdGggdGhlIG51bWJlciBvZiBkaWZmZXJlbnQgdmFsdWVzIGFuZCB0aGUgYXV0b2NvcnJlbGF0aW9uIHdpdGggb25lIHNpbmdsZSBudW1iZXIsIHRoZSBFZmZlY3RpdmUgU2FtcGxlIFNpemUgKEVTUykuIFRoaXMgaXMgb2J0YWluZWQgd2l0aCB0aGUgZm9sbG93aW5nIGZvcm11bGE6CgokJApFU1MgPSBcZnJhY3tudW1iZXJ+b2Z+c2FtcGxlc317MSsyXHN1bV97az0xfV57XGluZnR5fVxyaG8oayl9CiQkCgp3aGVyZSAkXHJobyhrKSQgaXMgdGhlIGF1dG9jb3JyZWxhdGlvbiBwbG90dGVkIGluIHRoZSAkYWNmJCBwbG90cyBhYm92ZS4KCkxldCdzIGJ1aWxkIGFuIEVTUyBmdW5jdGlvbjoKYGBge3J9CmVzcyA8LSBmdW5jdGlvbiAodmFsdWVzKSB7CiAgeCA8LSBhY2YodmFsdWVzLCBwbG90PUYpCiAgbGlzdF9vZl9yaG9zIDwtIHgkYWNmWzI6bGVuZ3RoKHgkYWNmKV0KICByZXR1cm4gKGxlbmd0aCh2YWx1ZXMpIC8gKDEgKyAyKnN1bShsaXN0X29mX3Job3MpKSApCn0KYGBgCgpBbmQgd2UgdXNlIGl0IG9uIGFsbCBvdXIgY2hhaW5zOgoKYGBge3J9CmVzcyhyZXN1bHRzX1NjYWxlTW92ZTAxW1sxXV0pCmVzcyhyZXN1bHRzX1NjYWxlTW92ZTA0W1sxXV0pCmVzcyhyZXN1bHRzX1NjYWxlTW92ZTFbWzFdXSkKZXNzKHJlc3VsdHNfU2NhbGVNb3ZlMTBbWzFdXSkKZXNzKHJlc3VsdFtbMV1dKQpgYGAKClRoaXMgY29uZmlybXMgdGhhdCB0aGUgc2NhbGluZyBtb3ZlIHdpdGggc2NhbGluZyBwYXJhbWV0ZXIgMC40IGlzIG11Y2ggYmV0dGVyIHRoYW4gdGhlIG90aGVyIE1DTUNzLgoKVGhlcmVmb3JlIG91ciBiZXN0IGVzdGltYXRlIG9mIHRoZSBkaXN0cmlidXRpb24gc2hvdWxkIGJlOgpgYGB7cn0KcGFyKG1mcm93PWMoMSwxKSwgbWFyPWMoNCw0LDIsMikrMC4xKQpwbG90KGRlbnNpdHkocmVzdWx0c19TY2FsZU1vdmUwNFtbMV1dKSwgY29sPSJvcmFuZ2UiLCBsd2Q9MiwgbWFpbj0iIiwgeWxhYj0iRGVuc2l0eSIsIHhsYWI9IlByb2JhYmlsaXR5IG9mIGhlYWRzIiwgeWxpbT1jKDAsMjQpLCB4bGltPWMoMCwxKSkKbGVnZW5kKCJ0b3BsZWZ0IiwgbGVnZW5kPSJEaXN0cmlidXRpb24gb2J0YWluZWQgd2l0aCB0aGUgc2NhbGluZyBtb3ZlLCBsYW1iZGE9MC40IiwgbHdkPTIsIGNvbD0ib3JhbmdlIikKYGBgCgoKCg==