Overview
The basic idea underlying Approximate Bayesian Computation (ABC) is deviously simple. In abstract terms the process is roughly the following:
- Specify the model (i.e. regression equation, etc)
- Specify prior distribution over parameters
- Sample candidates from prior
- Assess model fit, reject candidate?
- Take non-rejected candidates as posterior
Of critical importance is that this approach grew out of a need to abandon direct evaluation of the likelihood function as in classical statistics, whether Frequentist or Bayesian. Being recent, and growing out of technical fields, guidance for the methodology is largely impenetrable without a great deal of effort. This is, in part, because particular concerns – such as the selection of sufficient summary statistics – are somewhat unintuitive or unattainable although guidance exists (c.f. the recent handbook or this 2013 review).
This gap will likely close with time, and the methodology is in a position to spread, but before these things become lucidly clear it can be helpful to examine the approach as not doing anything fundamentally different than more orthodox approaches. This is especially relevant, perhaps unsurprisingly, for Bayesian estimation for which
A number of authors have argued that the Bayesian perspective needs to be incorporated into basic statistical training. This training is easily accomplished, insofar as Bayesian analysis can be carried out using conventional (frequentist) formulas for stratified analysis: one may use either information (inverse-variance) weighted averaging of current-data results with a prior distribution, or representation of the prior as a prior-data stratum (data augmentation). Thus, Bayesian analysis does not require posterior sampling or other special software, and does not even require explicit use of Bayes theorem. (Greenland 2007)
The same is true of ABC – all one needs to do the thing, at a base level, is know how to sample from a distribution. To facilitate this understanding of ABC, we will consider a very simple measure of fit – the root mean squared error of a linear model – which, by definition, is minimized when the residual sum of squares is minimized. Rather than explicitly minimizing this objective function to arrive at the “best” parameter values we will instead retain parameter vectors for which the fit is “not too bad.” And so rather than the uncertainty in the model being driven by sampling variability it will be uncertainty – on the part of the researcher – regarding how well the systematic component of the model should fit. This is relevant, of course, because as shown by Shmueli (2010, Appendix) the “true” model is not necessarily the best predictive model. And so, in some sense, ABC can be seen as a reflection of the fact that we don’t entirely believe the implicit likelihood function and so are more interested in parameters “around the maximum” rather than the maximum itself. In other terms, we might worry that our model is overfitting the data.
So, in short, ABC is a somewhat new and interesting way to think about Bayesian statistics. I don’t believe that it is the right approach for every problem – for simple models it is worse in almost every way – but it provides yet another alternative to classical approaches and is endowed with a great deal of intuition. In particular, it draws attention to the central role of the design matrix in a way which conventional approaches obscure and clarifies why we might want to just maximize the likelihood when able in the first place. By merit of being a likelihood-free method, it also enables a unique aproach to the problem of trying to identify more parameters than there are observations – a problem in which the most basic techniques naturally fail.
Easy as ABC
Let’s begin, as always, by simulating some data such that we know the truth. Here we will consider a simple non-linear relationship but later will treat the non-linear term as if it were just another covariate. We will also later turn to more complicated examples involving multilevel and inferential network models.
set.seed(8675309)
x <- rnorm(100,0,1)
y <- rnorm(100,2*x + 3*x^2,1)
data <- data.frame(y=y,x=x)
mod1 <- lm(y~x + I(x^2))
summary(mod1)
##
## Call:
## lm(formula = y ~ x + I(x^2))
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.46176 -0.66013 0.07491 0.71666 2.04074
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.1325 0.1221 -1.085 0.281
## x 2.0663 0.1074 19.238 <2e-16 ***
## I(x^2) 3.0912 0.0827 37.380 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.9929 on 97 degrees of freedom
## Multiple R-squared: 0.9486, Adjusted R-squared: 0.9475
## F-statistic: 894.6 on 2 and 97 DF, p-value: < 2.2e-16
Nothing surprising here. Now suppose, for whatever reason, we weren’t able to simply run a linear model – perhaps we are dealing with a situation in which the likelihood function is costly to evaluate or whatever. What can we do?
First, we will define a function. The specification is somewhat “hackish” in that we take as an input a fitted model object, but that is just because it is easier to extract the design matrix – the only thing that really matters here. We will also assume that the prior beliefs over parameters are identically distributed such that a realization from the priors is a coordinate uniformly drawn from a hypercube. Of course priors are important for the resulting posterior distribution, but even within orthodox Bayesianism priors are frequently used for their computational rather than theoretical properties. Naturally, if the “truth” is outside of this hypercube then we will fail to find it – that said it is relatively straightforward to imagine an adaptive “warm-up” phase similar to MCMC/HMC/etc where we “move the cube” to cover a desired posterior. Or one can also scale their variables such that the coefficients can be reasonably expected to fall within a range. We will revisit this momentarily.
Anyway, here is the function we will be working with:
flat_abc_draw <- function(model,lims=c(-10,10)){
y <- model$model[,1]
X <- as.matrix(model.matrix(model))
betas <- matrix(runif(ncol(model.matrix(model)),lims[1],lims[2]),ncol=1)
rmse <- sqrt(mean((y - X %*% betas)^2))
betas <- data.frame(t(betas))
names(betas) <- colnames(model.matrix(model))
data.frame(rmse,
sdy = sd(y),
betas)
}
After extracting the outcome and design matrix we simulate a vector of coefficients, make a prediction from that model, calculate the RMSE, and save the results. Note that all coefficients, including the intercept, are randomly sampled from the prior space. Here is the output from one draw:
flat_abc_draw(mod1)
## rmse sdy X.Intercept. x I.x.2.
## 1 11.25093 4.333733 -0.7041323 -9.04709 0.6466968
Here we can see that, also unsurprisingly, a randomly drawn vector of parameter estimates isn’t greate at predicting the outcome.
We will also define a function for viewing the posterior distribution for a term under a particular rejection rule. It won’t be the prettiest thing in the world, but gets the job done.
library(ggplot2)
abc_plot <- function(output,var,tol=0.05,type="rank"){
if(type == "rank"){
output$type <- ifelse(output$rank < round(length(output$rank)*tol),"Accept","Reject")
}
if(type == "prop_max"){
output$type <- ifelse(output$prop_max > 1-tol,"Accept","Reject")
}
output$type <- relevel(as.factor(output$type),"Reject")
bw <- (max(output[,var]) - min(output[,var]))/100
ggplot(output,aes(x=output[,var],fill=type)) +
geom_histogram(position="identity",binwidth=bw)
}
Great. Now let’s define one last utility function for taking the draws and processing the results.
library(pbapply)
library(dplyr)
flat_abc <- function(model,ndraws=10000,lims=c(-10,10),cl=NULL){
out <- pblapply(1:ndraws,function(x)flat_abc_draw(model,lims),cl=cl)
out <- do.call("rbind",out)
tibble(out[order(out$rmse),]) %>%
mutate(rank = 1:nrow(out), prop_max = min(rmse)/rmse) -> out
out
}
Great now let’s do the thing and take a look.
abc1 <- flat_abc(mod1)
ggplot(abc1,aes(y=prop_max,x=x)) +
geom_point() +
geom_vline(xintercept = 2, color="red", lwd=1.5)

In the above plot we have a measure of fit on the y-axis – larger RMSE leads to lower values with the smallest RMSE simulated model normalized to 1. As one might expect, the truth here – indicated in red – is “pointed towards” by better fitting models. Again, this is because minimizing the sum of squared errors (or maximizing the likelihood) minimizes the “loss” and maximizes the “fit” explicitly.
So how do we get a posterior distribution? Well, we first remember that if we keep all of the above draws our posterior will just return our sampled prior – here if we retain all observations the distribution of x parameters would be uniform. What ABC says is “keep only those parameters that generate data similar to that which is observed.” So let’s only keep those parameters which are above 0.25 on the y-axis within this example.
data.frame(abc1) %>% abc_plot("x",0.75,type="prop_max")

I told you it wouldn’t be the prettiest thing in the world. What it does show is how we go from a uniform prior across the pre-selected range and reduce downwards to a non-uniform distribution by what I will call ex-post rejection sampling. We can also see, as intuited above, that accepting or rejecting based upon fit results in a distribution centered around the likelihood estimate yet distributed according to the uncertainty we are directly expressing – here that every model above that threshold is equally likely. I’ll leave it to the reader to imagine how subsampling and resampling, for example, could be used to generate more custom posterior distributions. The same goes for the rule for acceptance or rejection; it’s your beliefs!
For example, suppose that we wanted to look only at the top 10% of models in terms of fit. We can do that like this:
data.frame(abc1) %>% abc_plot("x",0.1,type="rank")

Readers will note that the distribution, while still naturally centered on the same thing, is somewhat wider than the above distribution. That is because the restriction is simply less informative.
Of course, if we wanted to we could also just plot the accepted draws as well:
subset(data.frame(abc1),prop_max > 0.25) %>%
ggplot(aes(x=x)) +
geom_histogram(binwidth = 0.25,fill="blue",col="black")

Or take a summary of sum sort, like the mean and quantiles:
abc1 %>% filter(rank <= 100) %>%
summarise(n=max(rank),
mean=mean(x),
lower95=quantile(x,0.025),
upper95=quantile(x,0.975),
pct_pos = sum(x>0))
## # A tibble: 1 x 5
## n mean lower95 upper95 pct_pos
## <int> <dbl> <dbl> <dbl> <int>
## 1 100 2.26 -0.222 4.51 95
And so we can see that the posterior mean of the top 100 models is 1.8; fairly close to the truth of 2. We can also see that despite the 95% credible crossing 0, 89% of the top 100 draws are positively signed.
Effects Plots
One nice thing is that it is relatively straightforward to construct effects plots. The strategy we will take is, for each accepted parameter draw, re-calculate the predictions of the model and take the conditional mean of that as the expected effect. To get partial plots we will leverage FWL theorem.
First, we will keep only those parameters within the top 1% of fitted models and take a look at the posterior means for good measure:
sub <- abc1[which(abc1$rank < nrow(abc1)*0.01),]
apply(sub[,3:5],2,mean)
## X.Intercept. x I.x.2.
## -0.1849047 2.2381116 3.0060915
Not bad! Now let’s get predicted values and plot.
design <- as.matrix(model.matrix(mod1))
betas <- sub[,3:5]
prop_max <- sub$prop_max
v <- list()
for(i in 1:nrow(betas)){
pred_y <- design %*% t(as.matrix(betas[i,]))
pred_y_noint <- pred_y - design[,c(1)] %*% t(as.matrix(betas[i,c(1)]))
pred_y_fwl <- pred_y - design[,c(1,3)] %*% t(as.matrix(betas[i,c(1,3)]))
v[[i]] <- data.frame(pred_y = pred_y,
pred_y_fwl = pred_y_fwl,
pred_y_noint = pred_y_noint,
design,
prop_max = prop_max[i],
pars=i)
}
preds <- do.call("rbind",v)
ggplot(preds,aes(x=x,y=pred_y,color=1-prop_max)) +
geom_point() +
geom_smooth(method = "gam", formula = y ~ s(x, bs = "cs"),col="orange",lwd=1.5) +
geom_point(aes(x=x,y=2*x + 3*x^2),color="red") +
labs(y="ABC Predictions",color="Fit Loss") +
ggtitle("Full Predictions and CEF")

Here the blue dots are predictions from the posterior distribution, shaded by their fit (with darker being better). The red dots are “true predictions” while the orange line is the conditional mean of the predictions. Not bad at all!
Again we might imagine that rather than keeping only these values we subsample or resample in some way. Here is effectively an unweighted bootstrap version of the above:
pred_sub <- preds[sample(1:nrow(preds),1000,replace=T),]
ggplot(pred_sub,aes(x=x,y=pred_y,color=1-prop_max)) +
geom_point() +
geom_smooth(method = "gam", formula = y ~ s(x, bs = "cs"),col="orange",lwd=1.5) +
geom_point(aes(x=x,y=2*x + 3*x^2),color="red") +
labs(y="ABC Predictions",color="Fit Loss") +
ggtitle("Re-Sampled Predictions and CEF")

Here there is very little difference, but we might imagine contexts in which we would prefer to give more weight to better fitting models than lower fitting models. Again, I leave this to the reader to explore further.
Instead, we can take a look at effects plots for specific terms by tacitly applying FWL theorem and removing contributions to the prediction. So, forget that the true relationship is quadratic for a moment and pretend that the terms are actually just different variables.
ggplot(preds,aes(x=x,y=pred_y - pred_y_noint + pred_y_fwl,color=1-prop_max)) +
geom_point() +
geom_smooth(method = "gam", formula = y ~ s(x, bs = "cs"),color="orange",lwd=1.5) +
geom_point(aes(x=x,y=2*x),color="red") +
ggtitle("FWLish Partial Effect")

Not bad! Also note that the plots naturally depict the density of the x-variable of interest. Such is a natural benefit of simulation based approaches.
A little bit curvier now
One might wonder whether or not the same technique applies to functions with a greater deal of non-linearity. Let’s simulate a more subtle relationship (shamelessly taken from example code from ICPSR’s Regression III)
n <- 1000
x <- 0:(n-1)/(n-1)
f <- 0.2*x^11*(10*(1-x))^6+10*(10*x)^3*(1-x)^10
y <- f + rnorm(n, 0, sd = 2)
ggplot(mapping=aes(x=x)) +
geom_point(aes(y=y), pch=1) +
geom_line(aes(y=f), size=1) +
theme_bw() +
labs(x="x", y="y") -> g1
g1

We will model this, somewhat crudely, with splines. One might do something like the following when confronted with this data:
dave_dat <- data.frame(y=y,x=x)
library(splines)
lin_mod <- lm(y ~ bs(x,df=10),data = dave_dat)
g1 + geom_point(aes(y=lin_mod$fitted.values), col="orange")

Not awful, but not great. Splines are one example in which we might not “believe” the model we are estimating; rather we are trying to capture a relationship we are uncertain of utilizing statistical machinery as a tool towards that end.
Let’s see how ABC does. Again we will exploit the ability to fit the model object as a convinience to extract the appropriate terms; all you need is the appropriate design matrix. For splines this is simply additional functions of the x variable of interest. In this case, the model look like this:
colnames(model.matrix(lin_mod))
## [1] "(Intercept)" "bs(x, df = 10)1" "bs(x, df = 10)2" "bs(x, df = 10)3"
## [5] "bs(x, df = 10)4" "bs(x, df = 10)5" "bs(x, df = 10)6" "bs(x, df = 10)7"
## [9] "bs(x, df = 10)8" "bs(x, df = 10)9" "bs(x, df = 10)10"
This time we will crank up the number of draws to 50,000 and retain only the top 1% of models. Of note, one weakness of naive ABC – like that discussed here – is that the dimensionality of the prior space grows with the dimensionality of the model. More things with parameters means that more samples need to be drawn to get well-fitting models and “explore the space.” This motivates more advanced approaches, not discussed here.
Anyway, first let’s take a look at only the top 3 models to see what is going on:
out <- pblapply(1:50000,function(x)flat_abc_draw(lin_mod,lims=c(-5,15)))
out <- do.call("rbind",out)
tibble(out[order(out$rmse),]) %>%
mutate(rank = 1:nrow(out), prop_max = min(rmse)/rmse) -> out
# Subset down to close to truth
sub <- out[which(out$rank <= 3),]
design <- as.matrix(model.matrix(lin_mod))
betas <- sub[,3:(2+ncol(design))]
prop_max <- sub$prop_max
v <- list()
for(i in 1:nrow(betas)){
pred_y <- design %*% t(as.matrix(betas[i,]))
v[[i]] <- data.frame(pred_y = pred_y,
design,
prop_max = prop_max[i],
pars=i)
}
preds <- do.call("rbind",v)
preds$x <- dave_dat$x
preds$f <- with(preds,0.2*x^11*(10*(1-x))^6+10*(10*x)^3*(1-x)^10)
ggplot(preds,aes(x=x,y=pred_y,color=1-prop_max)) +
geom_point() +
geom_smooth(method = "gam", formula = y ~ s(x, bs = "cs"), color="orange", lwd=2) +
labs(y="ABC Predictions",color="Fit Loss") +
ggtitle("Getting Curvy") +
geom_point(aes(y=f),color="red")

Again, shades of blue are model predictions with darker being better fit. Red is the truth and orange is the conditional mean of the plotted predictions. Much like in the fitted spline model above, none of the models does a perfect job in capturing the relationship but none of them are completely worthless. What this sort of ABC plot does is that it says “we will average the expectation of the models we believe,” a process which leads to somewhat better fit. And before you say it, if this is starting to sound like machine learning then we are on the same page.
Let’s add more models to consideration; the top 1%. We will also resample predictions, here only to speed up the plot and to emphasize again that you can do such a thing.
sub <- out[which(out$rank <= 0.01*nrow(out)),]
design <- as.matrix(model.matrix(lin_mod))
betas <- sub[,3:(2+ncol(design))]
prop_max <- sub$prop_max
v <- list()
for(i in 1:nrow(betas)){
pred_y <- design %*% t(as.matrix(betas[i,]))
v[[i]] <- data.frame(pred_y = pred_y,
design,
prop_max = prop_max[i],
pars=i)
}
preds <- do.call("rbind",v)
preds$x <- dave_dat$x
preds$f <- with(preds,0.2*x^11*(10*(1-x))^6+10*(10*x)^3*(1-x)^10)
pred_sub <- preds[sample(1:nrow(preds),10*nrow(dave_dat),replace=T),]
ggplot(pred_sub,aes(x=x,y=pred_y,color=1-prop_max)) +
geom_point() +
geom_smooth(method = "gam", formula = y ~ s(x, bs = "cs"), color="orange", lwd=2) +
labs(y="ABC Predictions",color="Fit Loss") +
ggtitle("Re-Sampled Predictions and CEF") +
geom_point(aes(y=f),color="red")

Not awful, but not perfect either. But then again, we have taken an incredibly brute force approach to the problem – simulate parameters draws, evaluate fit, reject if fit is too poor, predict from accepted, then average. For areas towards the center of the data which doesn’t experience a great deal of change even this simple quite frankly slapped-together model approximates the truth quite well. It also adequately represents our naive uncertainty in the task.
Multilevel Models
And of course you can do the same thing for multilevel models as well. Let’s do a moderately complex setting with both group varying intercepts and slopes.
set.seed(1234)
nobs <- 10000
n_groups <- 10
grp <- rep_len(1:n_groups,nobs)
labels <- letters[grp]
grp_means <- rnorm(n_groups,0,2)
grp_ints <- rnorm(n_groups,0,2)
grp_slps <- rnorm(n_groups,0,2)
x <- rnorm(nobs,grp_means[grp])
err <- rnorm(nobs)
y <- grp_slps[grp]*x + grp_ints[grp] + err
data <- data.frame(y,x,grp,grp_means[grp],labels)
ggplot(data,aes(x=x,y=y,color=labels)) +
geom_point() +
geom_smooth(method="lm", formula=y~x)

We will once more fit a simple linear model so simplify constructing the design matrix.
multilevel <- lm(y~x*labels-1,data=data)
truth <- data.frame(labels = labels,
true_int = grp_ints,
true_slp = grp_slps)[1:n_groups,]
summary(multilevel)
##
## Call:
## lm(formula = y ~ x * labels - 1, data = data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -4.1165 -0.6947 0.0095 0.6844 3.7499
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## x 0.28003 0.03140 8.918 < 2e-16 ***
## labelsa -0.93317 0.08090 -11.535 < 2e-16 ***
## labelsb -1.93551 0.03628 -53.344 < 2e-16 ***
## labelsc -1.66773 0.07684 -21.705 < 2e-16 ***
## labelsd 0.12549 0.15443 0.813 0.416
## labelse 1.88688 0.04255 44.343 < 2e-16 ***
## labelsf -0.22498 0.04483 -5.018 5.31e-07 ***
## labelsg -1.06526 0.04814 -22.129 < 2e-16 ***
## labelsh -1.87891 0.04849 -38.750 < 2e-16 ***
## labelsi -1.69039 0.04907 -34.445 < 2e-16 ***
## labelsj 4.86969 0.06617 73.597 < 2e-16 ***
## x:labelsb -1.27632 0.04477 -28.505 < 2e-16 ***
## x:labelsc -1.11372 0.04491 -24.800 < 2e-16 ***
## x:labelsd 0.64750 0.04494 14.407 < 2e-16 ***
## x:labelse -1.62008 0.04484 -36.127 < 2e-16 ***
## x:labelsf -3.16468 0.04461 -70.943 < 2e-16 ***
## x:labelsg 0.84244 0.04517 18.650 < 2e-16 ***
## x:labelsh -2.35664 0.04541 -51.895 < 2e-16 ***
## x:labelsi -0.28676 0.04616 -6.212 5.42e-10 ***
## x:labelsj -2.12480 0.04506 -47.150 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.007 on 9980 degrees of freedom
## Multiple R-squared: 0.935, Adjusted R-squared: 0.9349
## F-statistic: 7177 on 20 and 9980 DF, p-value: < 2.2e-16
And take a quick look at the true slopes and intercepts:
truth
## labels true_int true_slp
## 1 a -0.9543854 0.2681764
## 2 b -1.9967729 -0.9813718
## 3 c -1.5525078 -0.8810957
## 4 d 0.1289176 0.9191789
## 5 e 1.9189881 -1.3874405
## 6 f -0.2205710 -2.8964098
## 7 g -1.0220190 1.1495114
## 8 h -1.8223908 -2.0473114
## 9 i -1.6743434 -0.0302766
## 10 j 4.8316704 -1.8718972
Baller. For ABC we will skip graphical matters and simply extract the posterior means of the top 0.01% of 1,000,000 models. Other decision rules will lead to different results.
library(parallel)
cl <- makeCluster(detectCores())
limz <- c(-10,10)
clusterExport(cl=cl,varlist=c("flat_abc_draw","multilevel","limz"))
abc_multi <- flat_abc(multilevel,1000000,limz,cl=cl)
stopCluster(cl)
best <- abc_multi[abc_multi$rank <= 0.0001*nrow(abc_multi),]
post_means <- apply(best[,3:(ncol(best)-2)],2,mean)
data.frame(truth,
post_int = post_means[2:(n_groups+1)],
post_slp = post_means[-c(2:(n_groups+1))]) -> results
ggplot(results) +
geom_point(aes(true_int,post_int),color="red") +
geom_point(aes(true_slp,post_slp),color="blue") +
labs(x="Truth",y="Posterior Mean") +
geom_hline(yintercept = 0) +
geom_vline(xintercept = 0) +
geom_abline(slope=1,intercept=0)

We can notice a few things. First, compared to simpler examples we are doing a bit worse. Why is this? Well, for one, we are starting to enter the boundaries of what we can reasonably expect from the particularly simple implimentation of ABC above – hence cranking up the number of draws. Remember, all we are doing is sampling from a hypercube of dimension equal to the number of terms on the right hand side of the regression we are conducting. Here that is 20 terms – it’s a large space to explore and we aren’t taking a ton of draws.
That said, there are few misleading inferences – nearly everything takes on the same sign as the truth, here, and all but one of these have a truth very close to zero. On the opposite end, the one “big coefficient” of around 5 is underestimated. Overall, however it ain’t bad!
Unidentified Models
The last thing of note is that it is feasible to estimate models with more parameters than observations in an ABC framework. The reason for this is that we aren’t estimating parameters so much as simulating them; we don’t run into the issue of more unknowns than equations.
Let’s illustrate this is a relatively simple case. We will have 10 covariates that matter and 10 which don’t, plus the intercept to estimate but only 30 observations. The case is obviously lame, but you might imagine a setting such as dynamic networks or a fully saturated model where one may wany to estimate coefficients in such a setting moreso than conduct variable selection.
library(MASS)
library(texreg)
obs <- 20
X <- mvrnorm(obs,rep(0,obs),Sigma = diag(1,obs))
beta <- c(rep(1,10),rep(0,obs-10))
y <- X %*% beta + rnorm(obs)
d <- data.frame(y=y,X)
unidentified <- lm(y~.,data=d)
identified <- lm(y~.,data=d[,1:15])
htmlreg(list(identified,unidentified),doctype = F)
Statistical models
|
|
Model 1
|
Model 2
|
|
(Intercept)
|
-0.06
|
-0.15
|
|
|
(0.35)
|
|
|
X1
|
1.61**
|
1.73
|
|
|
(0.35)
|
|
|
X2
|
1.32
|
1.04
|
|
|
(0.73)
|
|
|
X3
|
1.39
|
1.56
|
|
|
(0.60)
|
|
|
X4
|
1.30*
|
2.33
|
|
|
(0.37)
|
|
|
X5
|
0.74
|
0.63
|
|
|
(0.31)
|
|
|
X6
|
1.40**
|
1.89
|
|
|
(0.24)
|
|
|
X7
|
0.21
|
0.00
|
|
|
(0.49)
|
|
|
X8
|
0.71
|
0.56
|
|
|
(0.48)
|
|
|
X9
|
0.95
|
0.91
|
|
|
(0.45)
|
|
|
X10
|
1.91*
|
2.08
|
|
|
(0.58)
|
|
|
X11
|
0.11
|
-0.15
|
|
|
(0.54)
|
|
|
X12
|
0.03
|
-0.33
|
|
|
(0.40)
|
|
|
X13
|
0.76
|
1.66
|
|
|
(0.56)
|
|
|
X14
|
-0.20
|
0.48
|
|
|
(0.56)
|
|
|
X15
|
|
0.24
|
|
|
|
|
|
X16
|
|
-0.34
|
|
|
|
|
|
X17
|
|
0.85
|
|
|
|
|
|
X18
|
|
-0.15
|
|
|
|
|
|
X19
|
|
1.25
|
|
|
|
|
|
R2
|
0.99
|
1.00
|
|
Adj. R2
|
0.95
|
|
|
Num. obs.
|
20
|
20
|
|
RMSE
|
0.86
|
|
|
p < 0.001, p < 0.01, p < 0.05
|
Zero degrees of freedom sucks! No summary of uncertainty, and not all parameters get a value. There are existing techniques to deal with this sort of problem (ridge and lasso are both still popular, etc).
But we can still use the exact same machinery introduced above. The basic idea is simple. Since we generated the data, we know that the correct model can be generated. Here is its RMSE:
sqrt(mean((y-X%*%beta)^2))
## [1] 0.9160364
Just like before, the problem is that we don’t know the elements of the parameter vector. So, just like before: sample, calculate loss, reject those that are too bad, and summarize the rest. Insofar as the entries of the parameter vector are within the prior space then, with enough samples, we can point in that direction.
cl <- makeCluster(detectCores())
limz <- c(-2,4)
clusterExport(cl=cl,varlist=c("flat_abc_draw","unidentified","limz"))
abc_unid <- flat_abc(unidentified,500000,c(-1,3),cl=cl)
stopCluster(cl)
sub <- abc_unid[abc_unid$rank <= 250,]
means <- apply(sub[,3:(ncol(sub)-2)],2,mean)
ggplot(data.frame(truth=c(0,beta),post=means),aes(x=truth,y=post)) +
geom_point() +
labs(x="Truth",y="Posterior Means")

As we can see above, the approach doesn’t do too terribly but there is room for improvement. We are able to more or less separate the positive coefficients from the null coefficients; more samples would be mean more accuracy. Importantly, we also get a posterior distribution for summarizing our uncertainty.
ggplot(sub,aes(x=X1)) +
geom_histogram(binwidth = 0.1, col="black",fill="blue") +
ggtitle("Posterior of X1 (Truth = 1")

ggplot(sub,aes(x=X12)) +
geom_histogram(binwidth = 0.1, col="black",fill="blue") +
ggtitle("Posterior of X1 (Truth = 0")

What about more complicated sampling?
Look at the last posterior distribution plot – do you notice anything? It is truncated, and hence asymetric, due to the sampling algorithm not looking below -1 in this case.
This leads to two areas in which the above – perhaps the simplest and dumbest way of going about ABC – can be improved and to a discussion of how it is done in practice. There are two primary considerations:
Adaptive Search – even if we believe that the truth resides within the hypercube used above, we can be wrong. This motivates more advanced sampling algorithms which, frequently MCMC based, which find regions with a higher probability of acceptance. This also helps improve the efficiency of the sampling; less draws are needed. This is incredibly important to fight against the curse of dimensionality.
Rejection Sampling – in the above we used what I called ex post rejection sampling. Professional algorithms have the rejection rule embedded into the search, usually based around sufficient statistics, to help guide the algorithm (you can’t adapt to increase acceptance probability in a region without knowing how you will reject) and increase the fit of the model (RMSE, used above, is incredibly crude for capturing whether the outcome is actually being reproduced).
These concerns aside, even the really dumb implimentation examined above works surprisingly well in simple applied problems.
ABC is neat!
LS0tDQp0aXRsZTogIk5vdGVzIG9uIEFwcHJveGltYXRlIEJheWVzaWFuIENvbXB1dGF0aW9uIg0KYXV0aG9yOiAiQ2hyaXN0b3BoZXIgU2Nod2FyeiAoTllVIFBvbGl0aWNzKSINCmRhdGU6ICI4LzExLzIwMjAiDQpwYWdlczoNCiAgZXh0cmE6IHRydWUNCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiAyDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KLS0tDQoNCiMgT3ZlcnZpZXcNCg0KVGhlIGJhc2ljIGlkZWEgdW5kZXJseWluZyBBcHByb3hpbWF0ZSBCYXllc2lhbiBDb21wdXRhdGlvbiAoQUJDKSBpcyBkZXZpb3VzbHkgc2ltcGxlLiAgSW4gYWJzdHJhY3QgdGVybXMgdGhlIHByb2Nlc3MgaXMgcm91Z2hseSB0aGUgZm9sbG93aW5nOg0KDQoxKSBTcGVjaWZ5IHRoZSBtb2RlbCAoaS5lLiByZWdyZXNzaW9uIGVxdWF0aW9uLCBldGMpDQoyKSBTcGVjaWZ5IHByaW9yIGRpc3RyaWJ1dGlvbiBvdmVyIHBhcmFtZXRlcnMNCjMpIFNhbXBsZSBjYW5kaWRhdGVzIGZyb20gcHJpb3INCjQpIEFzc2VzcyBtb2RlbCBmaXQsIHJlamVjdCBjYW5kaWRhdGU/DQo1KSBUYWtlIG5vbi1yZWplY3RlZCBjYW5kaWRhdGVzIGFzIHBvc3Rlcmlvcg0KDQpPZiBjcml0aWNhbCBpbXBvcnRhbmNlIGlzIHRoYXQgdGhpcyBhcHByb2FjaCBncmV3IG91dCBvZiBhIG5lZWQgdG8gYWJhbmRvbiBkaXJlY3QgZXZhbHVhdGlvbiBvZiB0aGUgbGlrZWxpaG9vZCBmdW5jdGlvbiBhcyBpbiBjbGFzc2ljYWwgc3RhdGlzdGljcywgd2hldGhlciBGcmVxdWVudGlzdCBvciBCYXllc2lhbi4gIEJlaW5nIHJlY2VudCwgYW5kIGdyb3dpbmcgb3V0IG9mIHRlY2huaWNhbCBmaWVsZHMsIGd1aWRhbmNlIGZvciB0aGUgbWV0aG9kb2xvZ3kgaXMgbGFyZ2VseSBpbXBlbmV0cmFibGUgd2l0aG91dCBhIGdyZWF0IGRlYWwgb2YgZWZmb3J0LiAgVGhpcyBpcywgaW4gcGFydCwgYmVjYXVzZSBwYXJ0aWN1bGFyIGNvbmNlcm5zIC0tIHN1Y2ggYXMgdGhlIHNlbGVjdGlvbiBvZiBzdWZmaWNpZW50IHN1bW1hcnkgc3RhdGlzdGljcyAtLSBhcmUgc29tZXdoYXQgdW5pbnR1aXRpdmUgb3IgdW5hdHRhaW5hYmxlIGFsdGhvdWdoIGd1aWRhbmNlIGV4aXN0cyBbKGMuZi4gdGhlIHJlY2VudCBoYW5kYm9va10oaHR0cHM6Ly9ib29rcy5nb29nbGUuY29tL2Jvb2tzP2lkPUE4MkdEd0FBUUJBSiZkcT1oYW5kYm9vaytvZithcHByb3hpbWF0ZStiYXllc2lhbitjb21wdXRhdGlvbiZscj0pIG9yIFt0aGlzIDIwMTMgcmV2aWV3KV0oaHR0cHM6Ly9hcnhpdi5vcmcvcGRmLzEyMDIuMzgxOS5wZGYpLiAgDQoNClRoaXMgZ2FwIHdpbGwgbGlrZWx5IGNsb3NlIHdpdGggdGltZSwgYW5kIHRoZSBtZXRob2RvbG9neSBpcyBpbiBhIHBvc2l0aW9uIHRvIHNwcmVhZCwgYnV0IGJlZm9yZSB0aGVzZSB0aGluZ3MgYmVjb21lIGx1Y2lkbHkgY2xlYXIgaXQgY2FuIGJlIGhlbHBmdWwgdG8gZXhhbWluZSB0aGUgYXBwcm9hY2ggYXMgbm90IGRvaW5nIGFueXRoaW5nIGZ1bmRhbWVudGFsbHkgZGlmZmVyZW50IHRoYW4gbW9yZSBvcnRob2RveCBhcHByb2FjaGVzLiAgVGhpcyBpcyBlc3BlY2lhbGx5IHJlbGV2YW50LCBwZXJoYXBzIHVuc3VycHJpc2luZ2x5LCBmb3IgQmF5ZXNpYW4gZXN0aW1hdGlvbiBmb3Igd2hpY2gNCg0KPiBBIG51bWJlciBvZiBhdXRob3JzIGhhdmUgYXJndWVkIHRoYXQgdGhlIEJheWVzaWFuIHBlcnNwZWN0aXZlIG5lZWRzIHRvIGJlIGluY29ycG9yYXRlZCBpbnRvIGJhc2ljIHN0YXRpc3RpY2FsIHRyYWluaW5nLiBUaGlzIHRyYWluaW5nIGlzIGVhc2lseSBhY2NvbXBsaXNoZWQsIGluc29mYXIgYXMgQmF5ZXNpYW4gYW5hbHlzaXMgY2FuIGJlIGNhcnJpZWQgb3V0IHVzaW5nIGNvbnZlbnRpb25hbCAoZnJlcXVlbnRpc3QpIGZvcm11bGFzIGZvciBzdHJhdGlmaWVkIGFuYWx5c2lzOiBvbmUgbWF5IHVzZSBlaXRoZXIgaW5mb3JtYXRpb24gKGludmVyc2UtdmFyaWFuY2UpIHdlaWdodGVkIGF2ZXJhZ2luZyBvZiBjdXJyZW50LWRhdGEgcmVzdWx0cyB3aXRoIGEgcHJpb3IgZGlzdHJpYnV0aW9uLCBvciByZXByZXNlbnRhdGlvbiBvZiB0aGUgcHJpb3IgYXMgYSBwcmlvci1kYXRhIHN0cmF0dW0gKGRhdGEgYXVnbWVudGF0aW9uKS4gVGh1cywgQmF5ZXNpYW4gYW5hbHlzaXMgZG9lcyBub3QgcmVxdWlyZSBwb3N0ZXJpb3Igc2FtcGxpbmcgb3Igb3RoZXIgc3BlY2lhbCBzb2Z0d2FyZSwgYW5kIGRvZXMgbm90IGV2ZW4gcmVxdWlyZSBleHBsaWNpdCB1c2Ugb2YgQmF5ZXMgdGhlb3JlbS4gWyhHcmVlbmxhbmQgMjAwNyldKGh0dHBzOi8vYWNhZGVtaWMub3VwLmNvbS9pamUvYXJ0aWNsZS8zNi8xLzE5NS82Njk5MTMpDQoNClRoZSBzYW1lIGlzIHRydWUgb2YgQUJDIC0tIGFsbCBvbmUgbmVlZHMgdG8gZG8gdGhlIHRoaW5nLCBhdCBhIGJhc2UgbGV2ZWwsIGlzIGtub3cgaG93IHRvIHNhbXBsZSBmcm9tIGEgZGlzdHJpYnV0aW9uLiAgVG8gZmFjaWxpdGF0ZSB0aGlzIHVuZGVyc3RhbmRpbmcgb2YgQUJDLCB3ZSB3aWxsIGNvbnNpZGVyIGEgdmVyeSBzaW1wbGUgbWVhc3VyZSBvZiBmaXQgLS0gdGhlIHJvb3QgbWVhbiBzcXVhcmVkIGVycm9yIG9mIGEgbGluZWFyIG1vZGVsIC0tIHdoaWNoLCBieSBkZWZpbml0aW9uLCBpcyBtaW5pbWl6ZWQgd2hlbiB0aGUgcmVzaWR1YWwgc3VtIG9mIHNxdWFyZXMgaXMgbWluaW1pemVkLiAgUmF0aGVyIHRoYW4gZXhwbGljaXRseSBtaW5pbWl6aW5nIHRoaXMgb2JqZWN0aXZlIGZ1bmN0aW9uIHRvIGFycml2ZSBhdCB0aGUgImJlc3QiIHBhcmFtZXRlciB2YWx1ZXMgd2Ugd2lsbCBpbnN0ZWFkIHJldGFpbiBwYXJhbWV0ZXIgdmVjdG9ycyBmb3Igd2hpY2ggdGhlIGZpdCBpcyAibm90IHRvbyBiYWQuIiAgQW5kIHNvIHJhdGhlciB0aGFuIHRoZSB1bmNlcnRhaW50eSBpbiB0aGUgbW9kZWwgYmVpbmcgZHJpdmVuIGJ5IHNhbXBsaW5nIHZhcmlhYmlsaXR5IGl0IHdpbGwgYmUgdW5jZXJ0YWludHkgLS0gb24gdGhlIHBhcnQgb2YgdGhlIHJlc2VhcmNoZXIgLS0gcmVnYXJkaW5nIGhvdyB3ZWxsIHRoZSBzeXN0ZW1hdGljIGNvbXBvbmVudCBvZiB0aGUgbW9kZWwgc2hvdWxkIGZpdC4gIFRoaXMgaXMgcmVsZXZhbnQsIG9mIGNvdXJzZSwgYmVjYXVzZSBhcyBzaG93biBieSBbU2htdWVsaSAoMjAxMCwgQXBwZW5kaXgpXShodHRwczovL3d3dy5zdGF0LmJlcmtlbGV5LmVkdS9+YWxkb3VzLzE1Ny9QYXBlcnMvc2htdWVsaS5wZGYpIHRoZSAidHJ1ZSIgbW9kZWwgaXMgbm90IG5lY2Vzc2FyaWx5IHRoZSBiZXN0IHByZWRpY3RpdmUgbW9kZWwuICBBbmQgc28sIGluIHNvbWUgc2Vuc2UsIEFCQyBjYW4gYmUgc2VlbiBhcyBhIHJlZmxlY3Rpb24gb2YgdGhlIGZhY3QgdGhhdCB3ZSBkb24ndCBlbnRpcmVseSBiZWxpZXZlIHRoZSBpbXBsaWNpdCBsaWtlbGlob29kIGZ1bmN0aW9uIGFuZCBzbyBhcmUgbW9yZSBpbnRlcmVzdGVkIGluIHBhcmFtZXRlcnMgImFyb3VuZCB0aGUgbWF4aW11bSIgcmF0aGVyIHRoYW4gdGhlIG1heGltdW0gaXRzZWxmLiAgSW4gb3RoZXIgdGVybXMsIHdlIG1pZ2h0IHdvcnJ5IHRoYXQgb3VyIG1vZGVsIGlzIG92ZXJmaXR0aW5nIHRoZSBkYXRhLg0KDQpTbywgaW4gc2hvcnQsIEFCQyBpcyBhIHNvbWV3aGF0IG5ldyBhbmQgaW50ZXJlc3Rpbmcgd2F5IHRvIHRoaW5rIGFib3V0IEJheWVzaWFuIHN0YXRpc3RpY3MuICBJIGRvbid0IGJlbGlldmUgdGhhdCBpdCBpcyB0aGUgcmlnaHQgYXBwcm9hY2ggZm9yIGV2ZXJ5IHByb2JsZW0gLS0gZm9yIHNpbXBsZSBtb2RlbHMgaXQgaXMgd29yc2UgaW4gYWxtb3N0IGV2ZXJ5IHdheSAtLSBidXQgaXQgcHJvdmlkZXMgeWV0IGFub3RoZXIgYWx0ZXJuYXRpdmUgdG8gY2xhc3NpY2FsIGFwcHJvYWNoZXMgYW5kIGlzIGVuZG93ZWQgd2l0aCBhIGdyZWF0IGRlYWwgb2YgaW50dWl0aW9uLiAgSW4gcGFydGljdWxhciwgaXQgZHJhd3MgYXR0ZW50aW9uIHRvIHRoZSBjZW50cmFsIHJvbGUgb2YgdGhlIGRlc2lnbiBtYXRyaXggaW4gYSB3YXkgd2hpY2ggY29udmVudGlvbmFsIGFwcHJvYWNoZXMgb2JzY3VyZSBhbmQgY2xhcmlmaWVzIHdoeSB3ZSBtaWdodCB3YW50IHRvIGp1c3QgbWF4aW1pemUgdGhlIGxpa2VsaWhvb2Qgd2hlbiBhYmxlIGluIHRoZSBmaXJzdCBwbGFjZS4gIEJ5IG1lcml0IG9mIGJlaW5nIGEgbGlrZWxpaG9vZC1mcmVlIG1ldGhvZCwgaXQgYWxzbyBlbmFibGVzIGEgdW5pcXVlIGFwcm9hY2ggdG8gdGhlIHByb2JsZW0gb2YgdHJ5aW5nIHRvIGlkZW50aWZ5IG1vcmUgcGFyYW1ldGVycyB0aGFuIHRoZXJlIGFyZSBvYnNlcnZhdGlvbnMgLS0gYSBwcm9ibGVtIGluIHdoaWNoIHRoZSBtb3N0IGJhc2ljIHRlY2huaXF1ZXMgbmF0dXJhbGx5IGZhaWwuDQoNCiMgRWFzeSBhcyBBQkMNCg0KTGV0J3MgYmVnaW4sIGFzIGFsd2F5cywgYnkgc2ltdWxhdGluZyBzb21lIGRhdGEgc3VjaCB0aGF0IHdlIGtub3cgdGhlIHRydXRoLiAgSGVyZSB3ZSB3aWxsIGNvbnNpZGVyIGEgc2ltcGxlIG5vbi1saW5lYXIgcmVsYXRpb25zaGlwIGJ1dCBsYXRlciB3aWxsIHRyZWF0IHRoZSBub24tbGluZWFyIHRlcm0gYXMgaWYgaXQgd2VyZSBqdXN0IGFub3RoZXIgY292YXJpYXRlLiAgV2Ugd2lsbCBhbHNvIGxhdGVyIHR1cm4gdG8gbW9yZSBjb21wbGljYXRlZCBleGFtcGxlcyBpbnZvbHZpbmcgbXVsdGlsZXZlbCBhbmQgaW5mZXJlbnRpYWwgbmV0d29yayBtb2RlbHMuDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoODY3NTMwOSkNCg0KeCA8LSBybm9ybSgxMDAsMCwxKQ0KeSA8LSBybm9ybSgxMDAsMip4ICsgMyp4XjIsMSkNCg0KZGF0YSA8LSBkYXRhLmZyYW1lKHk9eSx4PXgpDQoNCm1vZDEgPC0gbG0oeX54ICsgSSh4XjIpKQ0KDQpzdW1tYXJ5KG1vZDEpDQpgYGANCg0KTm90aGluZyBzdXJwcmlzaW5nIGhlcmUuICBOb3cgc3VwcG9zZSwgZm9yIHdoYXRldmVyIHJlYXNvbiwgd2Ugd2VyZW4ndCBhYmxlIHRvIHNpbXBseSBydW4gYSBsaW5lYXIgbW9kZWwgLS0gcGVyaGFwcyB3ZSBhcmUgZGVhbGluZyB3aXRoIGEgc2l0dWF0aW9uIGluIHdoaWNoIHRoZSBsaWtlbGlob29kIGZ1bmN0aW9uIGlzIGNvc3RseSB0byBldmFsdWF0ZSBvciB3aGF0ZXZlci4gIFdoYXQgY2FuIHdlIGRvPw0KDQpGaXJzdCwgd2Ugd2lsbCBkZWZpbmUgYSBmdW5jdGlvbi4gIFRoZSBzcGVjaWZpY2F0aW9uIGlzIHNvbWV3aGF0ICJoYWNraXNoIiBpbiB0aGF0IHdlIHRha2UgYXMgYW4gaW5wdXQgYSBmaXR0ZWQgbW9kZWwgb2JqZWN0LCBidXQgdGhhdCBpcyBqdXN0IGJlY2F1c2UgaXQgaXMgZWFzaWVyIHRvIGV4dHJhY3QgdGhlIGRlc2lnbiBtYXRyaXggLS0gdGhlIG9ubHkgdGhpbmcgdGhhdCByZWFsbHkgbWF0dGVycyBoZXJlLiAgV2Ugd2lsbCBhbHNvIGFzc3VtZSB0aGF0IHRoZSBwcmlvciBiZWxpZWZzIG92ZXIgcGFyYW1ldGVycyBhcmUgaWRlbnRpY2FsbHkgZGlzdHJpYnV0ZWQgc3VjaCB0aGF0IGEgcmVhbGl6YXRpb24gZnJvbSB0aGUgcHJpb3JzIGlzIGEgY29vcmRpbmF0ZSB1bmlmb3JtbHkgZHJhd24gZnJvbSBhIGh5cGVyY3ViZS4gIE9mIGNvdXJzZSBwcmlvcnMgYXJlIGltcG9ydGFudCBmb3IgdGhlIHJlc3VsdGluZyBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uLCBidXQgZXZlbiB3aXRoaW4gb3J0aG9kb3ggQmF5ZXNpYW5pc20gcHJpb3JzIGFyZSBmcmVxdWVudGx5IHVzZWQgZm9yIHRoZWlyIGNvbXB1dGF0aW9uYWwgcmF0aGVyIHRoYW4gdGhlb3JldGljYWwgcHJvcGVydGllcy4gIE5hdHVyYWxseSwgaWYgdGhlICJ0cnV0aCIgaXMgb3V0c2lkZSBvZiB0aGlzIGh5cGVyY3ViZSB0aGVuIHdlIHdpbGwgZmFpbCB0byBmaW5kIGl0IC0tIHRoYXQgc2FpZCBpdCBpcyByZWxhdGl2ZWx5IHN0cmFpZ2h0Zm9yd2FyZCB0byBpbWFnaW5lIGFuIGFkYXB0aXZlICJ3YXJtLXVwIiBwaGFzZSBzaW1pbGFyIHRvIE1DTUMvSE1DL2V0YyB3aGVyZSB3ZSAibW92ZSB0aGUgY3ViZSIgdG8gY292ZXIgYSBkZXNpcmVkIHBvc3Rlcmlvci4gIE9yIG9uZSBjYW4gYWxzbyBzY2FsZSB0aGVpciB2YXJpYWJsZXMgc3VjaCB0aGF0IHRoZSBjb2VmZmljaWVudHMgY2FuIGJlIHJlYXNvbmFibHkgZXhwZWN0ZWQgdG8gZmFsbCB3aXRoaW4gYSByYW5nZS4gIFdlIHdpbGwgcmV2aXNpdCB0aGlzIG1vbWVudGFyaWx5Lg0KDQpBbnl3YXksIGhlcmUgaXMgdGhlIGZ1bmN0aW9uIHdlIHdpbGwgYmUgd29ya2luZyB3aXRoOg0KDQpgYGB7cn0NCmZsYXRfYWJjX2RyYXcgPC0gZnVuY3Rpb24obW9kZWwsbGltcz1jKC0xMCwxMCkpew0KICAgIA0KICAgIHkgPC0gbW9kZWwkbW9kZWxbLDFdDQogICAgWCA8LSBhcy5tYXRyaXgobW9kZWwubWF0cml4KG1vZGVsKSkNCiAgICBiZXRhcyA8LSBtYXRyaXgocnVuaWYobmNvbChtb2RlbC5tYXRyaXgobW9kZWwpKSxsaW1zWzFdLGxpbXNbMl0pLG5jb2w9MSkNCiAgICBybXNlIDwtIHNxcnQobWVhbigoeSAtIFggJSolIGJldGFzKV4yKSkNCiAgICBiZXRhcyA8LSBkYXRhLmZyYW1lKHQoYmV0YXMpKQ0KICAgIG5hbWVzKGJldGFzKSA8LSBjb2xuYW1lcyhtb2RlbC5tYXRyaXgobW9kZWwpKQ0KICAgIA0KICAgIGRhdGEuZnJhbWUocm1zZSwNCiAgICAgICAgICAgICAgIHNkeSA9IHNkKHkpLA0KICAgICAgICAgICAgICAgYmV0YXMpDQp9DQpgYGANCg0KQWZ0ZXIgZXh0cmFjdGluZyB0aGUgb3V0Y29tZSBhbmQgZGVzaWduIG1hdHJpeCB3ZSBzaW11bGF0ZSBhIHZlY3RvciBvZiBjb2VmZmljaWVudHMsIG1ha2UgYSBwcmVkaWN0aW9uIGZyb20gdGhhdCBtb2RlbCwgY2FsY3VsYXRlIHRoZSBSTVNFLCBhbmQgc2F2ZSB0aGUgcmVzdWx0cy4gIE5vdGUgdGhhdCBhbGwgY29lZmZpY2llbnRzLCBpbmNsdWRpbmcgdGhlIGludGVyY2VwdCwgYXJlIHJhbmRvbWx5IHNhbXBsZWQgZnJvbSB0aGUgcHJpb3Igc3BhY2UuICBIZXJlIGlzIHRoZSBvdXRwdXQgZnJvbSBvbmUgZHJhdzoNCg0KYGBge3J9DQpmbGF0X2FiY19kcmF3KG1vZDEpDQpgYGANCg0KSGVyZSB3ZSBjYW4gc2VlIHRoYXQsIGFsc28gdW5zdXJwcmlzaW5nbHksIGEgcmFuZG9tbHkgZHJhd24gdmVjdG9yIG9mIHBhcmFtZXRlciBlc3RpbWF0ZXMgaXNuJ3QgZ3JlYXRlIGF0IHByZWRpY3RpbmcgdGhlIG91dGNvbWUuDQoNCldlIHdpbGwgYWxzbyBkZWZpbmUgYSBmdW5jdGlvbiBmb3Igdmlld2luZyB0aGUgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbiBmb3IgYSB0ZXJtIHVuZGVyIGEgcGFydGljdWxhciByZWplY3Rpb24gcnVsZS4gIEl0IHdvbid0IGJlIHRoZSBwcmV0dGllc3QgdGhpbmcgaW4gdGhlIHdvcmxkLCBidXQgZ2V0cyB0aGUgam9iIGRvbmUuDQoNCmBgYHtyLCB3YXJuaW5nPUYsIG1lc3NhZ2U9Rn0NCmxpYnJhcnkoZ2dwbG90MikNCg0KYWJjX3Bsb3QgPC0gZnVuY3Rpb24ob3V0cHV0LHZhcix0b2w9MC4wNSx0eXBlPSJyYW5rIil7DQogIA0KICBpZih0eXBlID09ICJyYW5rIil7DQogICAgb3V0cHV0JHR5cGUgPC0gaWZlbHNlKG91dHB1dCRyYW5rIDwgcm91bmQobGVuZ3RoKG91dHB1dCRyYW5rKSp0b2wpLCJBY2NlcHQiLCJSZWplY3QiKQ0KICB9DQogIGlmKHR5cGUgPT0gInByb3BfbWF4Iil7DQogICAgb3V0cHV0JHR5cGUgPC0gaWZlbHNlKG91dHB1dCRwcm9wX21heCA+IDEtdG9sLCJBY2NlcHQiLCJSZWplY3QiKQ0KICB9DQogIA0KICBvdXRwdXQkdHlwZSA8LSByZWxldmVsKGFzLmZhY3RvcihvdXRwdXQkdHlwZSksIlJlamVjdCIpDQogIGJ3IDwtIChtYXgob3V0cHV0Wyx2YXJdKSAtIG1pbihvdXRwdXRbLHZhcl0pKS8xMDANCiAgDQogIGdncGxvdChvdXRwdXQsYWVzKHg9b3V0cHV0Wyx2YXJdLGZpbGw9dHlwZSkpICsgDQogICAgZ2VvbV9oaXN0b2dyYW0ocG9zaXRpb249ImlkZW50aXR5IixiaW53aWR0aD1idykgDQogIA0KfQ0KYGBgDQoNCkdyZWF0LiAgTm93IGxldCdzIGRlZmluZSBvbmUgbGFzdCB1dGlsaXR5IGZ1bmN0aW9uIGZvciB0YWtpbmcgdGhlIGRyYXdzIGFuZCBwcm9jZXNzaW5nIHRoZSByZXN1bHRzLg0KDQpgYGB7ciwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQpsaWJyYXJ5KHBiYXBwbHkpDQpsaWJyYXJ5KGRwbHlyKQ0KDQpmbGF0X2FiYyA8LSBmdW5jdGlvbihtb2RlbCxuZHJhd3M9MTAwMDAsbGltcz1jKC0xMCwxMCksY2w9TlVMTCl7DQogICAgICBvdXQgPC0gcGJsYXBwbHkoMTpuZHJhd3MsZnVuY3Rpb24oeClmbGF0X2FiY19kcmF3KG1vZGVsLGxpbXMpLGNsPWNsKQ0KICAgICAgb3V0IDwtIGRvLmNhbGwoInJiaW5kIixvdXQpDQogICAgICB0aWJibGUob3V0W29yZGVyKG91dCRybXNlKSxdKSAlPiUgDQogICAgICAgIG11dGF0ZShyYW5rID0gMTpucm93KG91dCksIHByb3BfbWF4ID0gbWluKHJtc2UpL3Jtc2UpIC0+IG91dA0KICAgICAgb3V0DQp9DQpgYGANCg0KR3JlYXQgbm93IGxldCdzIGRvIHRoZSB0aGluZyBhbmQgdGFrZSBhIGxvb2suDQoNCmBgYHtyfQ0KYWJjMSA8LSBmbGF0X2FiYyhtb2QxKQ0KDQpnZ3Bsb3QoYWJjMSxhZXMoeT1wcm9wX21heCx4PXgpKSArIA0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAyLCBjb2xvcj0icmVkIiwgbHdkPTEuNSkNCmBgYA0KDQpJbiB0aGUgYWJvdmUgcGxvdCB3ZSBoYXZlIGEgbWVhc3VyZSBvZiBmaXQgb24gdGhlIHktYXhpcyAtLSBsYXJnZXIgUk1TRSBsZWFkcyB0byBsb3dlciB2YWx1ZXMgd2l0aCB0aGUgc21hbGxlc3QgUk1TRSBzaW11bGF0ZWQgbW9kZWwgbm9ybWFsaXplZCB0byAxLiAgQXMgb25lIG1pZ2h0IGV4cGVjdCwgdGhlIHRydXRoIGhlcmUgLS0gaW5kaWNhdGVkIGluIHJlZCAtLSBpcyAicG9pbnRlZCB0b3dhcmRzIiBieSBiZXR0ZXIgZml0dGluZyBtb2RlbHMuICBBZ2FpbiwgdGhpcyBpcyBiZWNhdXNlIG1pbmltaXppbmcgdGhlIHN1bSBvZiBzcXVhcmVkIGVycm9ycyAob3IgbWF4aW1pemluZyB0aGUgbGlrZWxpaG9vZCkgbWluaW1pemVzIHRoZSAibG9zcyIgYW5kIG1heGltaXplcyB0aGUgImZpdCIgZXhwbGljaXRseS4NCg0KU28gaG93IGRvIHdlIGdldCBhIHBvc3RlcmlvciBkaXN0cmlidXRpb24/ICBXZWxsLCB3ZSBmaXJzdCByZW1lbWJlciB0aGF0IGlmIHdlIGtlZXAgYWxsIG9mIHRoZSBhYm92ZSBkcmF3cyBvdXIgcG9zdGVyaW9yIHdpbGwganVzdCByZXR1cm4gb3VyIHNhbXBsZWQgcHJpb3IgLS0gaGVyZSBpZiB3ZSByZXRhaW4gYWxsIG9ic2VydmF0aW9ucyB0aGUgZGlzdHJpYnV0aW9uIG9mIHggcGFyYW1ldGVycyB3b3VsZCBiZSB1bmlmb3JtLiAgV2hhdCBBQkMgc2F5cyBpcyAia2VlcCBvbmx5IHRob3NlIHBhcmFtZXRlcnMgdGhhdCBnZW5lcmF0ZSBkYXRhIHNpbWlsYXIgdG8gdGhhdCB3aGljaCBpcyBvYnNlcnZlZC4iICBTbyBsZXQncyBvbmx5IGtlZXAgdGhvc2UgcGFyYW1ldGVycyB3aGljaCBhcmUgYWJvdmUgMC4yNSBvbiB0aGUgeS1heGlzIHdpdGhpbiB0aGlzIGV4YW1wbGUuDQoNCmBgYHtyfQ0KZGF0YS5mcmFtZShhYmMxKSAlPiUgYWJjX3Bsb3QoIngiLDAuNzUsdHlwZT0icHJvcF9tYXgiKQ0KYGBgDQoNCkkgdG9sZCB5b3UgaXQgd291bGRuJ3QgYmUgdGhlIHByZXR0aWVzdCB0aGluZyBpbiB0aGUgd29ybGQuICBXaGF0IGl0IGRvZXMgc2hvdyBpcyBob3cgd2UgZ28gZnJvbSBhIHVuaWZvcm0gcHJpb3IgYWNyb3NzIHRoZSBwcmUtc2VsZWN0ZWQgcmFuZ2UgYW5kIHJlZHVjZSBkb3dud2FyZHMgdG8gYSBub24tdW5pZm9ybSBkaXN0cmlidXRpb24gYnkgd2hhdCBJIHdpbGwgY2FsbCBleC1wb3N0IHJlamVjdGlvbiBzYW1wbGluZy4gIFdlIGNhbiBhbHNvIHNlZSwgYXMgaW50dWl0ZWQgYWJvdmUsIHRoYXQgYWNjZXB0aW5nIG9yIHJlamVjdGluZyBiYXNlZCB1cG9uIGZpdCByZXN1bHRzIGluIGEgZGlzdHJpYnV0aW9uIGNlbnRlcmVkIGFyb3VuZCB0aGUgbGlrZWxpaG9vZCBlc3RpbWF0ZSB5ZXQgZGlzdHJpYnV0ZWQgYWNjb3JkaW5nIHRvIHRoZSB1bmNlcnRhaW50eSB3ZSBhcmUgZGlyZWN0bHkgZXhwcmVzc2luZyAtLSBoZXJlIHRoYXQgZXZlcnkgbW9kZWwgYWJvdmUgdGhhdCB0aHJlc2hvbGQgaXMgZXF1YWxseSBsaWtlbHkuICBJJ2xsIGxlYXZlIGl0IHRvIHRoZSByZWFkZXIgdG8gaW1hZ2luZSBob3cgc3Vic2FtcGxpbmcgYW5kIHJlc2FtcGxpbmcsIGZvciBleGFtcGxlLCBjb3VsZCBiZSB1c2VkIHRvIGdlbmVyYXRlIG1vcmUgY3VzdG9tIHBvc3RlcmlvciBkaXN0cmlidXRpb25zLiAgVGhlIHNhbWUgZ29lcyBmb3IgdGhlIHJ1bGUgZm9yIGFjY2VwdGFuY2Ugb3IgcmVqZWN0aW9uOyBpdCdzIHlvdXIgYmVsaWVmcyENCg0KRm9yIGV4YW1wbGUsIHN1cHBvc2UgdGhhdCB3ZSB3YW50ZWQgdG8gbG9vayBvbmx5IGF0IHRoZSB0b3AgMTAlIG9mIG1vZGVscyBpbiB0ZXJtcyBvZiBmaXQuICBXZSBjYW4gZG8gdGhhdCBsaWtlIHRoaXM6DQoNCmBgYHtyfQ0KZGF0YS5mcmFtZShhYmMxKSAlPiUgYWJjX3Bsb3QoIngiLDAuMSx0eXBlPSJyYW5rIikNCmBgYA0KDQpSZWFkZXJzIHdpbGwgbm90ZSB0aGF0IHRoZSBkaXN0cmlidXRpb24sIHdoaWxlIHN0aWxsIG5hdHVyYWxseSBjZW50ZXJlZCBvbiB0aGUgc2FtZSB0aGluZywgaXMgc29tZXdoYXQgd2lkZXIgdGhhbiB0aGUgYWJvdmUgZGlzdHJpYnV0aW9uLiAgVGhhdCBpcyBiZWNhdXNlIHRoZSByZXN0cmljdGlvbiBpcyBzaW1wbHkgbGVzcyBpbmZvcm1hdGl2ZS4NCg0KT2YgY291cnNlLCBpZiB3ZSB3YW50ZWQgdG8gd2UgY291bGQgYWxzbyBqdXN0IHBsb3QgdGhlIGFjY2VwdGVkIGRyYXdzIGFzIHdlbGw6DQoNCmBgYHtyfQ0Kc3Vic2V0KGRhdGEuZnJhbWUoYWJjMSkscHJvcF9tYXggPiAwLjI1KSAlPiUgDQogIGdncGxvdChhZXMoeD14KSkgKyANCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjI1LGZpbGw9ImJsdWUiLGNvbD0iYmxhY2siKQ0KYGBgDQoNCk9yIHRha2UgYSBzdW1tYXJ5IG9mIHN1bSBzb3J0LCBsaWtlIHRoZSBtZWFuIGFuZCBxdWFudGlsZXM6DQoNCmBgYHtyfQ0KYWJjMSAlPiUgZmlsdGVyKHJhbmsgPD0gMTAwKSAlPiUNCiAgc3VtbWFyaXNlKG49bWF4KHJhbmspLA0KICAgICAgICAgICAgbWVhbj1tZWFuKHgpLA0KICAgICAgICAgICAgbG93ZXI5NT1xdWFudGlsZSh4LDAuMDI1KSwNCiAgICAgICAgICAgIHVwcGVyOTU9cXVhbnRpbGUoeCwwLjk3NSksDQogICAgICAgICAgICBwY3RfcG9zID0gc3VtKHg+MCkpDQpgYGANCg0KQW5kIHNvIHdlIGNhbiBzZWUgdGhhdCB0aGUgcG9zdGVyaW9yIG1lYW4gb2YgdGhlIHRvcCAxMDAgbW9kZWxzIGlzIDEuODsgZmFpcmx5IGNsb3NlIHRvIHRoZSB0cnV0aCBvZiAyLiAgV2UgY2FuIGFsc28gc2VlIHRoYXQgZGVzcGl0ZSB0aGUgOTUlIGNyZWRpYmxlIGNyb3NzaW5nIDAsIDg5JSBvZiB0aGUgdG9wIDEwMCBkcmF3cyBhcmUgcG9zaXRpdmVseSBzaWduZWQuDQoNCiMgRWZmZWN0cyBQbG90cw0KDQpPbmUgbmljZSB0aGluZyBpcyB0aGF0IGl0IGlzIHJlbGF0aXZlbHkgc3RyYWlnaHRmb3J3YXJkIHRvIGNvbnN0cnVjdCBlZmZlY3RzIHBsb3RzLiAgVGhlIHN0cmF0ZWd5IHdlIHdpbGwgdGFrZSBpcywgZm9yIGVhY2ggYWNjZXB0ZWQgcGFyYW1ldGVyIGRyYXcsIHJlLWNhbGN1bGF0ZSB0aGUgcHJlZGljdGlvbnMgb2YgdGhlIG1vZGVsIGFuZCB0YWtlIHRoZSBjb25kaXRpb25hbCBtZWFuIG9mIHRoYXQgYXMgdGhlIGV4cGVjdGVkIGVmZmVjdC4gIFRvIGdldCBwYXJ0aWFsIHBsb3RzIHdlIHdpbGwgbGV2ZXJhZ2UgRldMIHRoZW9yZW0uDQoNCkZpcnN0LCB3ZSB3aWxsIGtlZXAgb25seSB0aG9zZSBwYXJhbWV0ZXJzIHdpdGhpbiB0aGUgdG9wIDElIG9mIGZpdHRlZCBtb2RlbHMgYW5kIHRha2UgYSBsb29rIGF0IHRoZSBwb3N0ZXJpb3IgbWVhbnMgZm9yIGdvb2QgbWVhc3VyZToNCg0KYGBge3J9DQpzdWIgPC0gYWJjMVt3aGljaChhYmMxJHJhbmsgPCBucm93KGFiYzEpKjAuMDEpLF0NCmFwcGx5KHN1YlssMzo1XSwyLG1lYW4pDQpgYGANCg0KTm90IGJhZCEgIE5vdyBsZXQncyBnZXQgcHJlZGljdGVkIHZhbHVlcyBhbmQgcGxvdC4NCg0KYGBge3J9DQpkZXNpZ24gPC0gYXMubWF0cml4KG1vZGVsLm1hdHJpeChtb2QxKSkNCmJldGFzIDwtIHN1YlssMzo1XQ0KcHJvcF9tYXggPC0gc3ViJHByb3BfbWF4DQoNCnYgPC0gbGlzdCgpDQpmb3IoaSBpbiAxOm5yb3coYmV0YXMpKXsNCiAgcHJlZF95IDwtIGRlc2lnbiAlKiUgdChhcy5tYXRyaXgoYmV0YXNbaSxdKSkNCiAgcHJlZF95X25vaW50IDwtIHByZWRfeSAtIGRlc2lnblssYygxKV0gJSolIHQoYXMubWF0cml4KGJldGFzW2ksYygxKV0pKQ0KICBwcmVkX3lfZndsIDwtIHByZWRfeSAtIGRlc2lnblssYygxLDMpXSAlKiUgdChhcy5tYXRyaXgoYmV0YXNbaSxjKDEsMyldKSkNCiAgdltbaV1dIDwtIGRhdGEuZnJhbWUocHJlZF95ID0gcHJlZF95LA0KICAgICAgICAgICAgICAgICAgICAgICBwcmVkX3lfZndsID0gcHJlZF95X2Z3bCwNCiAgICAgICAgICAgICAgICAgICAgICAgcHJlZF95X25vaW50ID0gcHJlZF95X25vaW50LA0KICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24sDQogICAgICAgICAgICAgICAgICAgICAgIHByb3BfbWF4ID0gcHJvcF9tYXhbaV0sDQogICAgICAgICAgICAgICAgICAgICAgIHBhcnM9aSkNCn0NCg0KcHJlZHMgPC0gZG8uY2FsbCgicmJpbmQiLHYpDQoNCmdncGxvdChwcmVkcyxhZXMoeD14LHk9cHJlZF95LGNvbG9yPTEtcHJvcF9tYXgpKSArIA0KICBnZW9tX3BvaW50KCkgKyANCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImdhbSIsIGZvcm11bGEgPSB5IH4gcyh4LCBicyA9ICJjcyIpLGNvbD0ib3JhbmdlIixsd2Q9MS41KSArDQogIGdlb21fcG9pbnQoYWVzKHg9eCx5PTIqeCArIDMqeF4yKSxjb2xvcj0icmVkIikgKw0KICBsYWJzKHk9IkFCQyBQcmVkaWN0aW9ucyIsY29sb3I9IkZpdCBMb3NzIikgKw0KICBnZ3RpdGxlKCJGdWxsIFByZWRpY3Rpb25zIGFuZCBDRUYiKQ0KYGBgDQoNCkhlcmUgdGhlIGJsdWUgZG90cyBhcmUgcHJlZGljdGlvbnMgZnJvbSB0aGUgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbiwgc2hhZGVkIGJ5IHRoZWlyIGZpdCAod2l0aCBkYXJrZXIgYmVpbmcgYmV0dGVyKS4gIFRoZSByZWQgZG90cyBhcmUgInRydWUgcHJlZGljdGlvbnMiIHdoaWxlIHRoZSBvcmFuZ2UgbGluZSBpcyB0aGUgY29uZGl0aW9uYWwgbWVhbiBvZiB0aGUgcHJlZGljdGlvbnMuICBOb3QgYmFkIGF0IGFsbCENCg0KQWdhaW4gd2UgbWlnaHQgaW1hZ2luZSB0aGF0IHJhdGhlciB0aGFuIGtlZXBpbmcgb25seSB0aGVzZSB2YWx1ZXMgd2Ugc3Vic2FtcGxlIG9yIHJlc2FtcGxlIGluIHNvbWUgd2F5LiAgSGVyZSBpcyBlZmZlY3RpdmVseSBhbiB1bndlaWdodGVkIGJvb3RzdHJhcCB2ZXJzaW9uIG9mIHRoZSBhYm92ZToNCg0KYGBge3J9DQpwcmVkX3N1YiA8LSBwcmVkc1tzYW1wbGUoMTpucm93KHByZWRzKSwxMDAwLHJlcGxhY2U9VCksXQ0KDQpnZ3Bsb3QocHJlZF9zdWIsYWVzKHg9eCx5PXByZWRfeSxjb2xvcj0xLXByb3BfbWF4KSkgKyANCiAgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJnYW0iLCBmb3JtdWxhID0geSB+IHMoeCwgYnMgPSAiY3MiKSxjb2w9Im9yYW5nZSIsbHdkPTEuNSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXgseT0yKnggKyAzKnheMiksY29sb3I9InJlZCIpICsNCiAgbGFicyh5PSJBQkMgUHJlZGljdGlvbnMiLGNvbG9yPSJGaXQgTG9zcyIpICsNCiAgZ2d0aXRsZSgiUmUtU2FtcGxlZCBQcmVkaWN0aW9ucyBhbmQgQ0VGIikNCg0KYGBgDQoNCkhlcmUgdGhlcmUgaXMgdmVyeSBsaXR0bGUgZGlmZmVyZW5jZSwgYnV0IHdlIG1pZ2h0IGltYWdpbmUgY29udGV4dHMgaW4gd2hpY2ggd2Ugd291bGQgcHJlZmVyIHRvIGdpdmUgbW9yZSB3ZWlnaHQgdG8gYmV0dGVyIGZpdHRpbmcgbW9kZWxzIHRoYW4gbG93ZXIgZml0dGluZyBtb2RlbHMuICBBZ2FpbiwgSSBsZWF2ZSB0aGlzIHRvIHRoZSByZWFkZXIgdG8gZXhwbG9yZSBmdXJ0aGVyLg0KDQpJbnN0ZWFkLCB3ZSBjYW4gdGFrZSBhIGxvb2sgYXQgZWZmZWN0cyBwbG90cyBmb3Igc3BlY2lmaWMgdGVybXMgYnkgdGFjaXRseSBhcHBseWluZyBGV0wgdGhlb3JlbSBhbmQgcmVtb3ZpbmcgY29udHJpYnV0aW9ucyB0byB0aGUgcHJlZGljdGlvbi4gIFNvLCBmb3JnZXQgdGhhdCB0aGUgdHJ1ZSByZWxhdGlvbnNoaXAgaXMgcXVhZHJhdGljIGZvciBhIG1vbWVudCBhbmQgcHJldGVuZCB0aGF0IHRoZSB0ZXJtcyBhcmUgYWN0dWFsbHkganVzdCBkaWZmZXJlbnQgdmFyaWFibGVzLg0KDQpgYGB7cn0NCmdncGxvdChwcmVkcyxhZXMoeD14LHk9cHJlZF95IC0gcHJlZF95X25vaW50ICsgcHJlZF95X2Z3bCxjb2xvcj0xLXByb3BfbWF4KSkgKyANCiAgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJnYW0iLCBmb3JtdWxhID0geSB+IHMoeCwgYnMgPSAiY3MiKSxjb2xvcj0ib3JhbmdlIixsd2Q9MS41KSArDQogIGdlb21fcG9pbnQoYWVzKHg9eCx5PTIqeCksY29sb3I9InJlZCIpICsNCiAgZ2d0aXRsZSgiRldMaXNoIFBhcnRpYWwgRWZmZWN0IikNCmBgYA0KDQpOb3QgYmFkISAgQWxzbyBub3RlIHRoYXQgdGhlIHBsb3RzIG5hdHVyYWxseSBkZXBpY3QgdGhlIGRlbnNpdHkgb2YgdGhlIHgtdmFyaWFibGUgb2YgaW50ZXJlc3QuICBTdWNoIGlzIGEgbmF0dXJhbCBiZW5lZml0IG9mIHNpbXVsYXRpb24gYmFzZWQgYXBwcm9hY2hlcy4NCg0KIyBBIGxpdHRsZSBiaXQgY3VydmllciBub3cNCg0KT25lIG1pZ2h0IHdvbmRlciB3aGV0aGVyIG9yIG5vdCB0aGUgc2FtZSB0ZWNobmlxdWUgYXBwbGllcyB0byBmdW5jdGlvbnMgd2l0aCBhIGdyZWF0ZXIgZGVhbCBvZiBub24tbGluZWFyaXR5LiAgTGV0J3Mgc2ltdWxhdGUgYSBtb3JlIHN1YnRsZSByZWxhdGlvbnNoaXAgKHNoYW1lbGVzc2x5IHRha2VuIGZyb20gZXhhbXBsZSBjb2RlIGZyb20gSUNQU1IncyBSZWdyZXNzaW9uIElJSSkNCg0KYGBge3J9DQpuIDwtIDEwMDANCnggPC0gMDoobi0xKS8obi0xKQ0KZiA8LSAwLjIqeF4xMSooMTAqKDEteCkpXjYrMTAqKDEwKngpXjMqKDEteCleMTANCnkgPC0gZiArIHJub3JtKG4sIDAsIHNkID0gMikNCg0KZ2dwbG90KG1hcHBpbmc9YWVzKHg9eCkpICsgDQogIGdlb21fcG9pbnQoYWVzKHk9eSksIHBjaD0xKSArIA0KICBnZW9tX2xpbmUoYWVzKHk9ZiksIHNpemU9MSkgKyANCiAgdGhlbWVfYncoKSArIA0KICBsYWJzKHg9IngiLCB5PSJ5IikgLT4gZzENCmcxDQpgYGANCg0KV2Ugd2lsbCBtb2RlbCB0aGlzLCBzb21ld2hhdCBjcnVkZWx5LCB3aXRoIHNwbGluZXMuICBPbmUgbWlnaHQgZG8gc29tZXRoaW5nIGxpa2UgdGhlIGZvbGxvd2luZyB3aGVuIGNvbmZyb250ZWQgd2l0aCB0aGlzIGRhdGE6DQoNCmBgYHtyLCBtZXNzYWdlPUYsd2FybmluZz1GfQ0KZGF2ZV9kYXQgPC0gZGF0YS5mcmFtZSh5PXkseD14KQ0KbGlicmFyeShzcGxpbmVzKQ0KbGluX21vZCA8LSBsbSh5IH4gYnMoeCxkZj0xMCksZGF0YSA9IGRhdmVfZGF0KQ0KZzEgKyBnZW9tX3BvaW50KGFlcyh5PWxpbl9tb2QkZml0dGVkLnZhbHVlcyksIGNvbD0ib3JhbmdlIikNCmBgYA0KDQpOb3QgYXdmdWwsIGJ1dCBub3QgZ3JlYXQuICBTcGxpbmVzIGFyZSBvbmUgZXhhbXBsZSBpbiB3aGljaCB3ZSBtaWdodCBub3QgImJlbGlldmUiIHRoZSBtb2RlbCB3ZSBhcmUgZXN0aW1hdGluZzsgcmF0aGVyIHdlIGFyZSB0cnlpbmcgdG8gY2FwdHVyZSBhIHJlbGF0aW9uc2hpcCB3ZSBhcmUgdW5jZXJ0YWluIG9mIHV0aWxpemluZyBzdGF0aXN0aWNhbCBtYWNoaW5lcnkgYXMgYSB0b29sIHRvd2FyZHMgdGhhdCBlbmQuDQoNCkxldCdzIHNlZSBob3cgQUJDIGRvZXMuICBBZ2FpbiB3ZSB3aWxsIGV4cGxvaXQgdGhlIGFiaWxpdHkgdG8gZml0IHRoZSBtb2RlbCBvYmplY3QgYXMgYSBjb252aW5pZW5jZSB0byBleHRyYWN0IHRoZSBhcHByb3ByaWF0ZSB0ZXJtczsgYWxsIHlvdSBuZWVkIGlzIHRoZSBhcHByb3ByaWF0ZSBkZXNpZ24gbWF0cml4LiAgRm9yIHNwbGluZXMgdGhpcyBpcyBzaW1wbHkgYWRkaXRpb25hbCBmdW5jdGlvbnMgb2YgdGhlIHggdmFyaWFibGUgb2YgaW50ZXJlc3QuICBJbiB0aGlzIGNhc2UsIHRoZSBtb2RlbCBsb29rIGxpa2UgdGhpczoNCg0KYGBge3J9DQpjb2xuYW1lcyhtb2RlbC5tYXRyaXgobGluX21vZCkpDQpgYGANCg0KVGhpcyB0aW1lIHdlIHdpbGwgY3JhbmsgdXAgdGhlIG51bWJlciBvZiBkcmF3cyB0byA1MCwwMDAgYW5kIHJldGFpbiBvbmx5IHRoZSB0b3AgMSUgb2YgbW9kZWxzLiAgT2Ygbm90ZSwgb25lIHdlYWtuZXNzIG9mIG5haXZlIEFCQyAtLSBsaWtlIHRoYXQgZGlzY3Vzc2VkIGhlcmUgLS0gaXMgdGhhdCB0aGUgZGltZW5zaW9uYWxpdHkgb2YgdGhlIHByaW9yIHNwYWNlIGdyb3dzIHdpdGggdGhlIGRpbWVuc2lvbmFsaXR5IG9mIHRoZSBtb2RlbC4gIE1vcmUgdGhpbmdzIHdpdGggcGFyYW1ldGVycyBtZWFucyB0aGF0IG1vcmUgc2FtcGxlcyBuZWVkIHRvIGJlIGRyYXduIHRvIGdldCB3ZWxsLWZpdHRpbmcgbW9kZWxzIGFuZCAiZXhwbG9yZSB0aGUgc3BhY2UuIiAgVGhpcyBtb3RpdmF0ZXMgbW9yZSBhZHZhbmNlZCBhcHByb2FjaGVzLCBub3QgZGlzY3Vzc2VkIGhlcmUuDQoNCkFueXdheSwgZmlyc3QgbGV0J3MgdGFrZSBhIGxvb2sgYXQgb25seSB0aGUgdG9wIDMgbW9kZWxzIHRvIHNlZSB3aGF0IGlzIGdvaW5nIG9uOg0KDQpgYGB7cn0NCm91dCA8LSBwYmxhcHBseSgxOjUwMDAwLGZ1bmN0aW9uKHgpZmxhdF9hYmNfZHJhdyhsaW5fbW9kLGxpbXM9YygtNSwxNSkpKQ0Kb3V0IDwtIGRvLmNhbGwoInJiaW5kIixvdXQpDQp0aWJibGUob3V0W29yZGVyKG91dCRybXNlKSxdKSAlPiUgDQogIG11dGF0ZShyYW5rID0gMTpucm93KG91dCksIHByb3BfbWF4ID0gbWluKHJtc2UpL3Jtc2UpIC0+IG91dA0KDQojIFN1YnNldCBkb3duIHRvIGNsb3NlIHRvIHRydXRoDQpzdWIgPC0gb3V0W3doaWNoKG91dCRyYW5rIDw9IDMpLF0NCg0KZGVzaWduIDwtIGFzLm1hdHJpeChtb2RlbC5tYXRyaXgobGluX21vZCkpDQpiZXRhcyA8LSBzdWJbLDM6KDIrbmNvbChkZXNpZ24pKV0NCnByb3BfbWF4IDwtIHN1YiRwcm9wX21heA0KDQp2IDwtIGxpc3QoKQ0KZm9yKGkgaW4gMTpucm93KGJldGFzKSl7DQogIHByZWRfeSA8LSBkZXNpZ24gJSolIHQoYXMubWF0cml4KGJldGFzW2ksXSkpDQogIHZbW2ldXSA8LSBkYXRhLmZyYW1lKHByZWRfeSA9IHByZWRfeSwNCiAgICAgICAgICAgICAgICAgICAgICAgZGVzaWduLA0KICAgICAgICAgICAgICAgICAgICAgICBwcm9wX21heCA9IHByb3BfbWF4W2ldLA0KICAgICAgICAgICAgICAgICAgICAgICBwYXJzPWkpDQp9DQoNCnByZWRzIDwtIGRvLmNhbGwoInJiaW5kIix2KQ0KcHJlZHMkeCA8LSBkYXZlX2RhdCR4DQpwcmVkcyRmIDwtIHdpdGgocHJlZHMsMC4yKnheMTEqKDEwKigxLXgpKV42KzEwKigxMCp4KV4zKigxLXgpXjEwKQ0KDQpnZ3Bsb3QocHJlZHMsYWVzKHg9eCx5PXByZWRfeSxjb2xvcj0xLXByb3BfbWF4KSkgKyANCiAgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJnYW0iLCBmb3JtdWxhID0geSB+IHMoeCwgYnMgPSAiY3MiKSwgY29sb3I9Im9yYW5nZSIsIGx3ZD0yKSArDQogIGxhYnMoeT0iQUJDIFByZWRpY3Rpb25zIixjb2xvcj0iRml0IExvc3MiKSArDQogIGdndGl0bGUoIkdldHRpbmcgQ3VydnkiKSArDQogIGdlb21fcG9pbnQoYWVzKHk9ZiksY29sb3I9InJlZCIpDQpgYGANCg0KQWdhaW4sIHNoYWRlcyBvZiBibHVlIGFyZSBtb2RlbCBwcmVkaWN0aW9ucyB3aXRoIGRhcmtlciBiZWluZyBiZXR0ZXIgZml0LiAgUmVkIGlzIHRoZSB0cnV0aCBhbmQgb3JhbmdlIGlzIHRoZSBjb25kaXRpb25hbCBtZWFuIG9mIHRoZSBwbG90dGVkIHByZWRpY3Rpb25zLiAgTXVjaCBsaWtlIGluIHRoZSBmaXR0ZWQgc3BsaW5lIG1vZGVsIGFib3ZlLCBub25lIG9mIHRoZSBtb2RlbHMgZG9lcyBhIHBlcmZlY3Qgam9iIGluIGNhcHR1cmluZyB0aGUgcmVsYXRpb25zaGlwIGJ1dCBub25lIG9mIHRoZW0gYXJlIGNvbXBsZXRlbHkgd29ydGhsZXNzLiAgV2hhdCB0aGlzIHNvcnQgb2YgQUJDIHBsb3QgZG9lcyBpcyB0aGF0IGl0IHNheXMgIndlIHdpbGwgYXZlcmFnZSB0aGUgZXhwZWN0YXRpb24gb2YgdGhlIG1vZGVscyB3ZSBiZWxpZXZlLCIgYSBwcm9jZXNzIHdoaWNoIGxlYWRzIHRvIHNvbWV3aGF0IGJldHRlciBmaXQuICBBbmQgYmVmb3JlIHlvdSBzYXkgaXQsIGlmIHRoaXMgaXMgc3RhcnRpbmcgdG8gc291bmQgbGlrZSBtYWNoaW5lIGxlYXJuaW5nIHRoZW4gd2UgYXJlIG9uIHRoZSBzYW1lIHBhZ2UuDQoNCkxldCdzIGFkZCBtb3JlIG1vZGVscyB0byBjb25zaWRlcmF0aW9uOyB0aGUgdG9wIDElLiAgV2Ugd2lsbCBhbHNvIHJlc2FtcGxlIHByZWRpY3Rpb25zLCBoZXJlIG9ubHkgdG8gc3BlZWQgdXAgdGhlIHBsb3QgYW5kIHRvIGVtcGhhc2l6ZSBhZ2FpbiB0aGF0IHlvdSBjYW4gZG8gc3VjaCBhIHRoaW5nLg0KDQpgYGB7cn0NCnN1YiA8LSBvdXRbd2hpY2gob3V0JHJhbmsgPD0gMC4wMSpucm93KG91dCkpLF0NCg0KZGVzaWduIDwtIGFzLm1hdHJpeChtb2RlbC5tYXRyaXgobGluX21vZCkpDQpiZXRhcyA8LSBzdWJbLDM6KDIrbmNvbChkZXNpZ24pKV0NCnByb3BfbWF4IDwtIHN1YiRwcm9wX21heA0KDQp2IDwtIGxpc3QoKQ0KZm9yKGkgaW4gMTpucm93KGJldGFzKSl7DQogIHByZWRfeSA8LSBkZXNpZ24gJSolIHQoYXMubWF0cml4KGJldGFzW2ksXSkpDQogIHZbW2ldXSA8LSBkYXRhLmZyYW1lKHByZWRfeSA9IHByZWRfeSwNCiAgICAgICAgICAgICAgICAgICAgICAgZGVzaWduLA0KICAgICAgICAgICAgICAgICAgICAgICBwcm9wX21heCA9IHByb3BfbWF4W2ldLA0KICAgICAgICAgICAgICAgICAgICAgICBwYXJzPWkpDQp9DQoNCnByZWRzIDwtIGRvLmNhbGwoInJiaW5kIix2KQ0KcHJlZHMkeCA8LSBkYXZlX2RhdCR4DQpwcmVkcyRmIDwtIHdpdGgocHJlZHMsMC4yKnheMTEqKDEwKigxLXgpKV42KzEwKigxMCp4KV4zKigxLXgpXjEwKQ0KDQpwcmVkX3N1YiA8LSBwcmVkc1tzYW1wbGUoMTpucm93KHByZWRzKSwxMCpucm93KGRhdmVfZGF0KSxyZXBsYWNlPVQpLF0NCg0KZ2dwbG90KHByZWRfc3ViLGFlcyh4PXgseT1wcmVkX3ksY29sb3I9MS1wcm9wX21heCkpICsgDQogIGdlb21fcG9pbnQoKSArIA0KICBnZW9tX3Ntb290aChtZXRob2QgPSAiZ2FtIiwgZm9ybXVsYSA9IHkgfiBzKHgsIGJzID0gImNzIiksIGNvbG9yPSJvcmFuZ2UiLCBsd2Q9MikgKw0KICBsYWJzKHk9IkFCQyBQcmVkaWN0aW9ucyIsY29sb3I9IkZpdCBMb3NzIikgKw0KICBnZ3RpdGxlKCJSZS1TYW1wbGVkIFByZWRpY3Rpb25zIGFuZCBDRUYiKSArDQogIGdlb21fcG9pbnQoYWVzKHk9ZiksY29sb3I9InJlZCIpDQpgYGANCg0KTm90IGF3ZnVsLCBidXQgbm90IHBlcmZlY3QgZWl0aGVyLiAgQnV0IHRoZW4gYWdhaW4sIHdlIGhhdmUgdGFrZW4gYW4gaW5jcmVkaWJseSBicnV0ZSBmb3JjZSBhcHByb2FjaCB0byB0aGUgcHJvYmxlbSAtLSBzaW11bGF0ZSBwYXJhbWV0ZXJzIGRyYXdzLCBldmFsdWF0ZSBmaXQsIHJlamVjdCBpZiBmaXQgaXMgdG9vIHBvb3IsIHByZWRpY3QgZnJvbSBhY2NlcHRlZCwgdGhlbiBhdmVyYWdlLiAgRm9yIGFyZWFzIHRvd2FyZHMgdGhlIGNlbnRlciBvZiB0aGUgZGF0YSB3aGljaCBkb2Vzbid0IGV4cGVyaWVuY2UgYSBncmVhdCBkZWFsIG9mIGNoYW5nZSBldmVuIHRoaXMgc2ltcGxlIHF1aXRlIGZyYW5rbHkgc2xhcHBlZC10b2dldGhlciBtb2RlbCBhcHByb3hpbWF0ZXMgdGhlIHRydXRoIHF1aXRlIHdlbGwuICBJdCBhbHNvIGFkZXF1YXRlbHkgcmVwcmVzZW50cyBvdXIgbmFpdmUgdW5jZXJ0YWludHkgaW4gdGhlIHRhc2suDQoNCiMgTXVsdGlsZXZlbCBNb2RlbHMNCg0KQW5kIG9mIGNvdXJzZSB5b3UgY2FuIGRvIHRoZSBzYW1lIHRoaW5nIGZvciBtdWx0aWxldmVsIG1vZGVscyBhcyB3ZWxsLiAgTGV0J3MgZG8gYSBtb2RlcmF0ZWx5IGNvbXBsZXggc2V0dGluZyB3aXRoIGJvdGggZ3JvdXAgdmFyeWluZyBpbnRlcmNlcHRzIGFuZCBzbG9wZXMuDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzNCkNCm5vYnMgPC0gMTAwMDANCm5fZ3JvdXBzIDwtIDEwDQoNCmdycCA8LSByZXBfbGVuKDE6bl9ncm91cHMsbm9icykNCmxhYmVscyA8LSBsZXR0ZXJzW2dycF0NCmdycF9tZWFucyA8LSBybm9ybShuX2dyb3VwcywwLDIpDQpncnBfaW50cyA8LSBybm9ybShuX2dyb3VwcywwLDIpDQpncnBfc2xwcyA8LSBybm9ybShuX2dyb3VwcywwLDIpDQoNCnggPC0gcm5vcm0obm9icyxncnBfbWVhbnNbZ3JwXSkNCmVyciA8LSBybm9ybShub2JzKQ0KDQp5IDwtIGdycF9zbHBzW2dycF0qeCArIGdycF9pbnRzW2dycF0gKyBlcnINCg0KZGF0YSA8LSBkYXRhLmZyYW1lKHkseCxncnAsZ3JwX21lYW5zW2dycF0sbGFiZWxzKQ0KDQpnZ3Bsb3QoZGF0YSxhZXMoeD14LHk9eSxjb2xvcj1sYWJlbHMpKSArIA0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIiwgZm9ybXVsYT15fngpDQpgYGANCg0KV2Ugd2lsbCBvbmNlIG1vcmUgZml0IGEgc2ltcGxlIGxpbmVhciBtb2RlbCBzbyBzaW1wbGlmeSBjb25zdHJ1Y3RpbmcgdGhlIGRlc2lnbiBtYXRyaXguDQoNCmBgYHtyfQ0KbXVsdGlsZXZlbCA8LSBsbSh5fngqbGFiZWxzLTEsZGF0YT1kYXRhKQ0KdHJ1dGggPC0gZGF0YS5mcmFtZShsYWJlbHMgPSBsYWJlbHMsDQogICAgICAgICAgICAgICAgICAgIHRydWVfaW50ID0gZ3JwX2ludHMsDQogICAgICAgICAgICAgICAgICAgIHRydWVfc2xwID0gZ3JwX3NscHMpWzE6bl9ncm91cHMsXQ0Kc3VtbWFyeShtdWx0aWxldmVsKQ0KYGBgDQoNCkFuZCB0YWtlIGEgcXVpY2sgbG9vayBhdCB0aGUgdHJ1ZSBzbG9wZXMgYW5kIGludGVyY2VwdHM6DQpgYGB7cn0NCnRydXRoDQpgYGANCg0KQmFsbGVyLiAgRm9yIEFCQyB3ZSB3aWxsIHNraXAgZ3JhcGhpY2FsIG1hdHRlcnMgYW5kIHNpbXBseSBleHRyYWN0IHRoZSBwb3N0ZXJpb3IgbWVhbnMgb2YgdGhlIHRvcCAwLjAxJSBvZiAxLDAwMCwwMDAgbW9kZWxzLiAgT3RoZXIgZGVjaXNpb24gcnVsZXMgd2lsbCBsZWFkIHRvIGRpZmZlcmVudCByZXN1bHRzLg0KDQpgYGB7ciwgd2FybmluZz1GLG1lc3NhZ2U9Rn0NCmxpYnJhcnkocGFyYWxsZWwpDQpjbCA8LSBtYWtlQ2x1c3RlcihkZXRlY3RDb3JlcygpKQ0KbGlteiA8LSBjKC0xMCwxMCkNCmNsdXN0ZXJFeHBvcnQoY2w9Y2wsdmFybGlzdD1jKCJmbGF0X2FiY19kcmF3IiwibXVsdGlsZXZlbCIsImxpbXoiKSkNCg0KYWJjX211bHRpIDwtIGZsYXRfYWJjKG11bHRpbGV2ZWwsMTAwMDAwMCxsaW16LGNsPWNsKQ0Kc3RvcENsdXN0ZXIoY2wpDQpiZXN0IDwtIGFiY19tdWx0aVthYmNfbXVsdGkkcmFuayA8PSAwLjAwMDEqbnJvdyhhYmNfbXVsdGkpLF0NCg0KcG9zdF9tZWFucyA8LSBhcHBseShiZXN0WywzOihuY29sKGJlc3QpLTIpXSwyLG1lYW4pDQpkYXRhLmZyYW1lKHRydXRoLA0KICAgICAgICAgICBwb3N0X2ludCA9IHBvc3RfbWVhbnNbMjoobl9ncm91cHMrMSldLA0KICAgICAgICAgICBwb3N0X3NscCA9IHBvc3RfbWVhbnNbLWMoMjoobl9ncm91cHMrMSkpXSkgLT4gcmVzdWx0cw0KDQpnZ3Bsb3QocmVzdWx0cykgKw0KICBnZW9tX3BvaW50KGFlcyh0cnVlX2ludCxwb3N0X2ludCksY29sb3I9InJlZCIpICsNCiAgZ2VvbV9wb2ludChhZXModHJ1ZV9zbHAscG9zdF9zbHApLGNvbG9yPSJibHVlIikgKw0KICBsYWJzKHg9IlRydXRoIix5PSJQb3N0ZXJpb3IgTWVhbiIpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwKSArDQogIGdlb21fYWJsaW5lKHNsb3BlPTEsaW50ZXJjZXB0PTApDQpgYGANCg0KV2UgY2FuIG5vdGljZSBhIGZldyB0aGluZ3MuICBGaXJzdCwgY29tcGFyZWQgdG8gc2ltcGxlciBleGFtcGxlcyB3ZSBhcmUgZG9pbmcgYSBiaXQgd29yc2UuICBXaHkgaXMgdGhpcz8gIFdlbGwsIGZvciBvbmUsIHdlIGFyZSBzdGFydGluZyB0byBlbnRlciB0aGUgYm91bmRhcmllcyBvZiB3aGF0IHdlIGNhbiByZWFzb25hYmx5IGV4cGVjdCBmcm9tIHRoZSBwYXJ0aWN1bGFybHkgc2ltcGxlIGltcGxpbWVudGF0aW9uIG9mIEFCQyBhYm92ZSAtLSBoZW5jZSBjcmFua2luZyB1cCB0aGUgbnVtYmVyIG9mIGRyYXdzLiAgUmVtZW1iZXIsIGFsbCB3ZSBhcmUgZG9pbmcgaXMgc2FtcGxpbmcgZnJvbSBhIGh5cGVyY3ViZSBvZiBkaW1lbnNpb24gZXF1YWwgdG8gdGhlIG51bWJlciBvZiB0ZXJtcyBvbiB0aGUgcmlnaHQgaGFuZCBzaWRlIG9mIHRoZSByZWdyZXNzaW9uIHdlIGFyZSBjb25kdWN0aW5nLiAgSGVyZSB0aGF0IGlzIDIwIHRlcm1zIC0tIGl0J3MgYSBsYXJnZSBzcGFjZSB0byBleHBsb3JlIGFuZCB3ZSBhcmVuJ3QgdGFraW5nIGEgdG9uIG9mIGRyYXdzLg0KDQpUaGF0IHNhaWQsIHRoZXJlIGFyZSBmZXcgbWlzbGVhZGluZyBpbmZlcmVuY2VzIC0tIG5lYXJseSBldmVyeXRoaW5nIHRha2VzIG9uIHRoZSBzYW1lIHNpZ24gYXMgdGhlIHRydXRoLCBoZXJlLCBhbmQgYWxsIGJ1dCBvbmUgb2YgdGhlc2UgaGF2ZSBhIHRydXRoIHZlcnkgY2xvc2UgdG8gemVyby4gIE9uIHRoZSBvcHBvc2l0ZSBlbmQsIHRoZSBvbmUgImJpZyBjb2VmZmljaWVudCIgb2YgYXJvdW5kIDUgaXMgdW5kZXJlc3RpbWF0ZWQuICBPdmVyYWxsLCBob3dldmVyIGl0IGFpbid0IGJhZCENCg0KIyBVbmlkZW50aWZpZWQgTW9kZWxzDQoNClRoZSBsYXN0IHRoaW5nIG9mIG5vdGUgaXMgdGhhdCBpdCBpcyBmZWFzaWJsZSB0byBlc3RpbWF0ZSBtb2RlbHMgd2l0aCBtb3JlIHBhcmFtZXRlcnMgdGhhbiBvYnNlcnZhdGlvbnMgaW4gYW4gQUJDIGZyYW1ld29yay4gIFRoZSByZWFzb24gZm9yIHRoaXMgaXMgdGhhdCB3ZSBhcmVuJ3QgZXN0aW1hdGluZyBwYXJhbWV0ZXJzIHNvIG11Y2ggYXMgc2ltdWxhdGluZyB0aGVtOyB3ZSBkb24ndCBydW4gaW50byB0aGUgaXNzdWUgb2YgbW9yZSB1bmtub3ducyB0aGFuIGVxdWF0aW9ucy4NCg0KTGV0J3MgaWxsdXN0cmF0ZSB0aGlzIGlzIGEgcmVsYXRpdmVseSBzaW1wbGUgY2FzZS4gIFdlIHdpbGwgaGF2ZSAxMCBjb3ZhcmlhdGVzIHRoYXQgbWF0dGVyIGFuZCAxMCB3aGljaCBkb24ndCwgcGx1cyB0aGUgaW50ZXJjZXB0IHRvIGVzdGltYXRlIGJ1dCBvbmx5IDMwIG9ic2VydmF0aW9ucy4gIFRoZSBjYXNlIGlzIG9idmlvdXNseSBsYW1lLCBidXQgeW91IG1pZ2h0IGltYWdpbmUgYSBzZXR0aW5nIHN1Y2ggYXMgZHluYW1pYyBuZXR3b3JrcyBvciBhIGZ1bGx5IHNhdHVyYXRlZCBtb2RlbCB3aGVyZSBvbmUgbWF5IHdhbnkgdG8gZXN0aW1hdGUgY29lZmZpY2llbnRzIGluIHN1Y2ggYSBzZXR0aW5nIG1vcmVzbyB0aGFuIGNvbmR1Y3QgdmFyaWFibGUgc2VsZWN0aW9uLg0KDQpgYGB7ciwgd2FybmluZz1GLCBtZXNzYWdlPUYsIHJlc3VsdHM9ImFzaXMifQ0KbGlicmFyeShNQVNTKQ0KbGlicmFyeSh0ZXhyZWcpDQoNCm9icyA8LSAyMA0KWCA8LSBtdnJub3JtKG9icyxyZXAoMCxvYnMpLFNpZ21hID0gZGlhZygxLG9icykpDQpiZXRhIDwtIGMocmVwKDEsMTApLHJlcCgwLG9icy0xMCkpDQp5IDwtIFggJSolIGJldGEgKyBybm9ybShvYnMpDQpkIDwtIGRhdGEuZnJhbWUoeT15LFgpDQoNCnVuaWRlbnRpZmllZCA8LSBsbSh5fi4sZGF0YT1kKQ0KaWRlbnRpZmllZCA8LSBsbSh5fi4sZGF0YT1kWywxOjE1XSkNCg0KaHRtbHJlZyhsaXN0KGlkZW50aWZpZWQsdW5pZGVudGlmaWVkKSxkb2N0eXBlID0gRikNCmBgYA0KDQpaZXJvIGRlZ3JlZXMgb2YgZnJlZWRvbSBzdWNrcyEgIE5vIHN1bW1hcnkgb2YgdW5jZXJ0YWludHksIGFuZCBub3QgYWxsIHBhcmFtZXRlcnMgZ2V0IGEgdmFsdWUuICBUaGVyZSBhcmUgZXhpc3RpbmcgdGVjaG5pcXVlcyB0byBkZWFsIHdpdGggdGhpcyBzb3J0IG9mIHByb2JsZW0gKHJpZGdlIGFuZCBsYXNzbyBhcmUgYm90aCBzdGlsbCBwb3B1bGFyLCBldGMpLiAgDQoNCkJ1dCB3ZSBjYW4gc3RpbGwgdXNlIHRoZSBleGFjdCBzYW1lIG1hY2hpbmVyeSBpbnRyb2R1Y2VkIGFib3ZlLiAgVGhlIGJhc2ljIGlkZWEgaXMgc2ltcGxlLiAgU2luY2Ugd2UgZ2VuZXJhdGVkIHRoZSBkYXRhLCB3ZSBrbm93IHRoYXQgdGhlIGNvcnJlY3QgbW9kZWwgY2FuIGJlIGdlbmVyYXRlZC4gIEhlcmUgaXMgaXRzIFJNU0U6DQoNCmBgYHtyfQ0Kc3FydChtZWFuKCh5LVglKiViZXRhKV4yKSkNCmBgYA0KDQpKdXN0IGxpa2UgYmVmb3JlLCB0aGUgcHJvYmxlbSBpcyB0aGF0IHdlIGRvbid0IGtub3cgdGhlIGVsZW1lbnRzIG9mIHRoZSBwYXJhbWV0ZXIgdmVjdG9yLiAgU28sIGp1c3QgbGlrZSBiZWZvcmU6IHNhbXBsZSwgY2FsY3VsYXRlIGxvc3MsIHJlamVjdCB0aG9zZSB0aGF0IGFyZSB0b28gYmFkLCBhbmQgc3VtbWFyaXplIHRoZSByZXN0LiAgSW5zb2ZhciBhcyB0aGUgZW50cmllcyBvZiB0aGUgcGFyYW1ldGVyIHZlY3RvciBhcmUgd2l0aGluIHRoZSBwcmlvciBzcGFjZSB0aGVuLCB3aXRoIGVub3VnaCBzYW1wbGVzLCB3ZSBjYW4gcG9pbnQgaW4gdGhhdCBkaXJlY3Rpb24uDQoNCmBgYHtyfQ0KY2wgPC0gbWFrZUNsdXN0ZXIoZGV0ZWN0Q29yZXMoKSkNCmxpbXogPC0gYygtMiw0KQ0KY2x1c3RlckV4cG9ydChjbD1jbCx2YXJsaXN0PWMoImZsYXRfYWJjX2RyYXciLCJ1bmlkZW50aWZpZWQiLCJsaW16IikpDQphYmNfdW5pZCA8LSBmbGF0X2FiYyh1bmlkZW50aWZpZWQsNTAwMDAwLGMoLTEsMyksY2w9Y2wpDQpzdG9wQ2x1c3RlcihjbCkNCg0Kc3ViIDwtIGFiY191bmlkW2FiY191bmlkJHJhbmsgPD0gMjUwLF0NCm1lYW5zIDwtIGFwcGx5KHN1YlssMzoobmNvbChzdWIpLTIpXSwyLG1lYW4pDQpnZ3Bsb3QoZGF0YS5mcmFtZSh0cnV0aD1jKDAsYmV0YSkscG9zdD1tZWFucyksYWVzKHg9dHJ1dGgseT1wb3N0KSkgKyANCiAgZ2VvbV9wb2ludCgpICsNCiAgbGFicyh4PSJUcnV0aCIseT0iUG9zdGVyaW9yIE1lYW5zIikNCmBgYA0KDQpBcyB3ZSBjYW4gc2VlIGFib3ZlLCB0aGUgYXBwcm9hY2ggZG9lc24ndCBkbyB0b28gdGVycmlibHkgYnV0IHRoZXJlIGlzIHJvb20gZm9yIGltcHJvdmVtZW50LiAgV2UgYXJlIGFibGUgdG8gbW9yZSBvciBsZXNzIHNlcGFyYXRlIHRoZSBwb3NpdGl2ZSBjb2VmZmljaWVudHMgZnJvbSB0aGUgbnVsbCBjb2VmZmljaWVudHM7IG1vcmUgc2FtcGxlcyB3b3VsZCBiZSBtZWFuIG1vcmUgYWNjdXJhY3kuICBJbXBvcnRhbnRseSwgd2UgYWxzbyBnZXQgYSBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uIGZvciBzdW1tYXJpemluZyBvdXIgdW5jZXJ0YWludHkuDQoNCmBgYHtyfQ0KZ2dwbG90KHN1YixhZXMoeD1YMSkpICsgDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4xLCBjb2w9ImJsYWNrIixmaWxsPSJibHVlIikgKw0KICBnZ3RpdGxlKCJQb3N0ZXJpb3Igb2YgWDEgKFRydXRoID0gMSIpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3Qoc3ViLGFlcyh4PVgxMikpICsgDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4xLCBjb2w9ImJsYWNrIixmaWxsPSJibHVlIikgKw0KICBnZ3RpdGxlKCJQb3N0ZXJpb3Igb2YgWDEgKFRydXRoID0gMCIpDQpgYGANCg0KIyBXaGF0IGFib3V0IG1vcmUgY29tcGxpY2F0ZWQgc2FtcGxpbmc/DQoNCkxvb2sgYXQgdGhlIGxhc3QgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbiBwbG90IC0tIGRvIHlvdSBub3RpY2UgYW55dGhpbmc/ICBJdCBpcyB0cnVuY2F0ZWQsIGFuZCBoZW5jZSBhc3ltZXRyaWMsIGR1ZSB0byB0aGUgc2FtcGxpbmcgYWxnb3JpdGhtIG5vdCBsb29raW5nIGJlbG93IC0xIGluIHRoaXMgY2FzZS4NCg0KVGhpcyBsZWFkcyB0byB0d28gYXJlYXMgaW4gd2hpY2ggdGhlIGFib3ZlIC0tIHBlcmhhcHMgdGhlIHNpbXBsZXN0IGFuZCBkdW1iZXN0IHdheSBvZiBnb2luZyBhYm91dCBBQkMgLS0gY2FuIGJlIGltcHJvdmVkIGFuZCB0byBhIGRpc2N1c3Npb24gb2YgaG93IGl0IGlzIGRvbmUgaW4gcHJhY3RpY2UuICBUaGVyZSBhcmUgdHdvIHByaW1hcnkgY29uc2lkZXJhdGlvbnM6DQoNCjEpIEFkYXB0aXZlIFNlYXJjaCAtLSBldmVuIGlmIHdlIGJlbGlldmUgdGhhdCB0aGUgdHJ1dGggcmVzaWRlcyB3aXRoaW4gdGhlIGh5cGVyY3ViZSB1c2VkIGFib3ZlLCB3ZSBjYW4gYmUgd3JvbmcuICBUaGlzIG1vdGl2YXRlcyBtb3JlIGFkdmFuY2VkIHNhbXBsaW5nIGFsZ29yaXRobXMgd2hpY2gsIGZyZXF1ZW50bHkgTUNNQyBiYXNlZCwgd2hpY2ggZmluZCByZWdpb25zIHdpdGggYSBoaWdoZXIgcHJvYmFiaWxpdHkgb2YgYWNjZXB0YW5jZS4gIFRoaXMgYWxzbyBoZWxwcyBpbXByb3ZlIHRoZSBlZmZpY2llbmN5IG9mIHRoZSBzYW1wbGluZzsgbGVzcyBkcmF3cyBhcmUgbmVlZGVkLiAgVGhpcyBpcyBpbmNyZWRpYmx5IGltcG9ydGFudCB0byBmaWdodCBhZ2FpbnN0IHRoZSBjdXJzZSBvZiBkaW1lbnNpb25hbGl0eS4NCg0KMikgUmVqZWN0aW9uIFNhbXBsaW5nIC0tIGluIHRoZSBhYm92ZSB3ZSB1c2VkIHdoYXQgSSBjYWxsZWQgZXggcG9zdCByZWplY3Rpb24gc2FtcGxpbmcuICBQcm9mZXNzaW9uYWwgYWxnb3JpdGhtcyBoYXZlIHRoZSByZWplY3Rpb24gcnVsZSBlbWJlZGRlZCBpbnRvIHRoZSBzZWFyY2gsIHVzdWFsbHkgYmFzZWQgYXJvdW5kIHN1ZmZpY2llbnQgc3RhdGlzdGljcywgdG8gaGVscCBndWlkZSB0aGUgYWxnb3JpdGhtICh5b3UgY2FuJ3QgYWRhcHQgdG8gaW5jcmVhc2UgYWNjZXB0YW5jZSBwcm9iYWJpbGl0eSBpbiBhIHJlZ2lvbiB3aXRob3V0IGtub3dpbmcgaG93IHlvdSB3aWxsIHJlamVjdCkgYW5kIGluY3JlYXNlIHRoZSBmaXQgb2YgdGhlIG1vZGVsIChSTVNFLCB1c2VkIGFib3ZlLCBpcyBpbmNyZWRpYmx5IGNydWRlIGZvciBjYXB0dXJpbmcgd2hldGhlciB0aGUgb3V0Y29tZSBpcyBhY3R1YWxseSBiZWluZyByZXByb2R1Y2VkKS4NCg0KVGhlc2UgY29uY2VybnMgYXNpZGUsIGV2ZW4gdGhlIHJlYWxseSBkdW1iIGltcGxpbWVudGF0aW9uIGV4YW1pbmVkIGFib3ZlIHdvcmtzIHN1cnByaXNpbmdseSB3ZWxsIGluIHNpbXBsZSBhcHBsaWVkIHByb2JsZW1zLg0KDQpBQkMgaXMgbmVhdCE=