This is a reproduction from https://www.r-bloggers.com/2020/04/bayesian-linear-regression/ for personal study purpose.

1. Introduction

Frequentist

For statistical inferences we have tow general approaches or frameworks:

  • Frequentist approach in which the data sampled from the population is considered as random and the population parameter values, known as null hypothesis, as fixed (but unknown). To estimate thus this null hypothesis we look for the sample parameters that maximize the likelihood of the data. However, the data at hand, even it is sampled randomly from the population, it is fixed now, so how can we consider this data as random. The answer is that we assume that the population distribution is known and we work out the maximum likelihood of the data using this distribution. Or we repeat the study many times with different samples then we average the results. So if we get very small value for the likelihood of the data which is known as p-value we tend to reject the null hypothesis. The main problem, however, is the misunderstanding and misusing of this p-value when we decide to reject the null hypothesis based on some threshold, from which we wrongly interpreting it as the probability of rejecting the null hypothesis. For more detail about p-value click here.

  • Bayesian approach, in contrast, provides true probabilities to quantify the uncertainty about a certain hypothesis, but requires the use of a first belief about how likely this hypothesis is true, known as prior, to be able to derive the probability of this hypothesis after seeing the data known as posterior probability. This approach called bayesian because it is based on the bayes’ theorem, for instance if a have population parameter to estimate 𝜃 , and we have some data sampled randomly from this population 𝐷, the posterior probability thus will be \[\overbrace{p(\theta/D)}^{Posterior}=\frac{\overbrace{p(D/\theta)}^{Likelihood}.\overbrace{p(\theta)}^{Prior}}{\underbrace{p(D)}_{Evidence}}\] The Evidence is the probability of the data at hand regardless the parameter 𝜃

2. Data preparation

For simplicity we use the BostonHousing data from mlbench package, For more detail about this data run this command ?BostonHousing after calling the package. But first Let’s call all the packages that we need throughout this article.

suppressPackageStartupMessages(library(mlbench))
suppressPackageStartupMessages(library(rstanarm))
suppressPackageStartupMessages(library(bayestestR))
suppressPackageStartupMessages(library(bayesplot))
suppressPackageStartupMessages(library(insight))
suppressPackageStartupMessages(library(broom))
data("BostonHousing")
str(BostonHousing)
'data.frame':   506 obs. of  14 variables:
 $ crim   : num  0.00632 0.02731 0.02729 0.03237 0.06905 ...
 $ zn     : num  18 0 0 0 0 0 12.5 12.5 12.5 12.5 ...
 $ indus  : num  2.31 7.07 7.07 2.18 2.18 2.18 7.87 7.87 7.87 7.87 ...
 $ chas   : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
 $ nox    : num  0.538 0.469 0.469 0.458 0.458 0.458 0.524 0.524 0.524 0.524 ...
 $ rm     : num  6.58 6.42 7.18 7 7.15 ...
 $ age    : num  65.2 78.9 61.1 45.8 54.2 58.7 66.6 96.1 100 85.9 ...
 $ dis    : num  4.09 4.97 4.97 6.06 6.06 ...
 $ rad    : num  1 2 2 3 3 3 5 5 5 5 ...
 $ tax    : num  296 242 242 222 222 222 311 311 311 311 ...
 $ ptratio: num  15.3 17.8 17.8 18.7 18.7 18.7 15.2 15.2 15.2 15.2 ...
 $ b      : num  397 397 393 395 397 ...
 $ lstat  : num  4.98 9.14 4.03 2.94 5.33 ...
 $ medv   : num  24 21.6 34.7 33.4 36.2 28.7 22.9 27.1 16.5 18.9 ...

To well understand how the bayesian regression works we keep only three features, two numeric variables age, dis and one categorical chas, with the target variable medv the median value of owner-occupied homes.

bost <- BostonHousing[,c("medv","age","dis","chas")]
head(bost)
  medv  age    dis chas
1 24.0 65.2 4.0900    0
2 21.6 78.9 4.9671    0
3 34.7 61.1 4.9671    0
4 33.4 45.8 6.0622    0
5 36.2 54.2 6.0622    0
6 28.7 58.7 6.0622    0
str(bost)
'data.frame':   506 obs. of  4 variables:
 $ medv: num  24 21.6 34.7 33.4 36.2 28.7 22.9 27.1 16.5 18.9 ...
 $ age : num  65.2 78.9 61.1 45.8 54.2 58.7 66.6 96.1 100 85.9 ...
 $ dis : num  4.09 4.97 4.97 6.06 6.06 ...
 $ chas: Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...

3. Classical linear regression model

To highlight the difference between the Bayesian regression and the traditional linear regression (frequentist approach), Let’s first fit the latter to our data.

model_freq<-lm(medv~., data=bost)
tidy(model_freq)

Checking the p.value of each regressor tells all the regressors are significant except “dis” variable. Since the variable “chas” is categorical by two levels, the coefficient of “chas1” is the difference between the median price of houses on the bounds Charles River and that of the others, so the median price of the former is about 7.513.

4. Bayesian regression

To fit a Bayesian regression, we use the function stan_glm from the rstanarm package. This function as the above lm function requires providing the formula and the data that will be used, and leave all the following arguments with their default values:

model_bayes <- stan_glm(medv~., data=bost, seed=111)

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 1).
Chain 1: 
Chain 1: Gradient evaluation took 3.7e-05 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 0.37 seconds.
Chain 1: Adjust your expectations accordingly!
Chain 1: 
Chain 1: 
Chain 1: Iteration:    1 / 2000 [  0%]  (Warmup)
Chain 1: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 1: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 1: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 1: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 1: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 1: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 1: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 1: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 1: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 1: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 1: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 1: 
Chain 1:  Elapsed Time: 0.108939 seconds (Warm-up)
Chain 1:                0.152252 seconds (Sampling)
Chain 1:                0.261191 seconds (Total)
Chain 1: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 2).
Chain 2: 
Chain 2: Gradient evaluation took 2.2e-05 seconds
Chain 2: 1000 transitions using 10 leapfrog steps per transition would take 0.22 seconds.
Chain 2: Adjust your expectations accordingly!
Chain 2: 
Chain 2: 
Chain 2: Iteration:    1 / 2000 [  0%]  (Warmup)
Chain 2: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 2: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 2: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 2: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 2: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 2: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 2: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 2: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 2: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 2: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 2: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 2: 
Chain 2:  Elapsed Time: 0.09926 seconds (Warm-up)
Chain 2:                0.136321 seconds (Sampling)
Chain 2:                0.235581 seconds (Total)
Chain 2: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 3).
Chain 3: 
Chain 3: Gradient evaluation took 2e-05 seconds
Chain 3: 1000 transitions using 10 leapfrog steps per transition would take 0.2 seconds.
Chain 3: Adjust your expectations accordingly!
Chain 3: 
Chain 3: 
Chain 3: Iteration:    1 / 2000 [  0%]  (Warmup)
Chain 3: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 3: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 3: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 3: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 3: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 3: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 3: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 3: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 3: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 3: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 3: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 3: 
Chain 3:  Elapsed Time: 0.100463 seconds (Warm-up)
Chain 3:                0.150563 seconds (Sampling)
Chain 3:                0.251026 seconds (Total)
Chain 3: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 4).
Chain 4: 
Chain 4: Gradient evaluation took 2.4e-05 seconds
Chain 4: 1000 transitions using 10 leapfrog steps per transition would take 0.24 seconds.
Chain 4: Adjust your expectations accordingly!
Chain 4: 
Chain 4: 
Chain 4: Iteration:    1 / 2000 [  0%]  (Warmup)
Chain 4: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 4: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 4: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 4: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 4: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 4: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 4: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 4: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 4: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 4: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 4: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 4: 
Chain 4:  Elapsed Time: 0.104112 seconds (Warm-up)
Chain 4:                0.142341 seconds (Sampling)
Chain 4:                0.246453 seconds (Total)
Chain 4: 
print(model_bayes, digits = 3)
stan_glm
 family:       gaussian [identity]
 formula:      medv ~ .
 observations: 506
 predictors:   4
------
            Median MAD_SD
(Intercept) 32.834  2.285
age         -0.143  0.020
dis         -0.258  0.257
chas1        7.543  1.432

Auxiliary parameter(s):
      Median MAD_SD
sigma 8.324  0.260 

------
* For help interpreting the printed output see ?print.stanreg
* For info on the priors used see ?prior_summary.stanreg

The Median estimate is the median computed from the MCMC simulation, and MAD_SD is the median absolute deviation computed from the same simulation. To well understand how getting these outputs, we plot the MCMC simulation of each predictor using bayesplot

mcmc_dens(model_bayes, pars = c("age"))+
  vline_at(-0.143, col="red")

As can be seen, the point estimate of “age” falls on the median of the distribution (red line). The same thing is true for “dis”" and “chas” predictors.

mcmc_dens(model_bayes, pars=c("dis"))+
  vline_at(-0.244, col="red")

mcmc_dens(model_bayes, pars=c("chas1"))+
  vline_at(7.496, col="red")

How do we evaluate the model parameters? By analyzing the posteriors using some specific statistics. We make use of the function describe_posterior provided by bayestestR package.

describe_posterior(model_bayes)
Possible multicollinearity between dis and age (r = 0.76). This might lead to inappropriate results. See 'Details' in '?rope'.
# Description of Posterior Distributions

Parameter   | Median |           89% CI |    pd |        89% ROPE | % in ROPE |  Rhat |      ESS
------------------------------------------------------------------------------------------------
(Intercept) | 32.834 | [29.218, 36.295] | 1.000 | [-0.920, 0.920] |         0 | 1.002 | 2029.279
age         | -0.143 | [-0.175, -0.112] | 1.000 | [-0.920, 0.920] |       100 | 1.001 | 2052.155
dis         | -0.258 | [-0.667,  0.179] | 0.819 | [-0.920, 0.920] |       100 | 1.002 | 2115.192
chas1       |  7.543 | [ 5.159,  9.813] | 1.000 | [-0.920, 0.920] |         0 | 1.000 | 3744.403

Aternatively, we can get the coefficeient estimates (which are the medians by default) separatly by using the package insight.

post <- get_parameters(model_bayes)
print(purrr::map_dbl(post,median),digits = 3)
(Intercept)         age         dis       chas1 
     32.834      -0.143      -0.258       7.543 

We can also compute the Maximum A posteriori (MAP), and the mean as follows:

print(purrr::map_dbl(post, map_estimate),digits = 3)
(Intercept)         age         dis       chas1 
     33.025      -0.145      -0.295       7.573 
print(purrr::map_dbl(post, mean),digits = 3)
(Intercept)         age         dis       chas1 
     32.761      -0.143      -0.248       7.523 

As we see, the values are closer to each other due to the like normality of the distribution of the posteriors where all the central statistics (mean, median, mode) are closer to each other. Using the following plot to visualize the age coefficient using different statistics as follows:

mcmc_dens(model_bayes, pars=c("age"))+
  vline_at(median(post$age), col="red")+
  vline_at(mean(post$age), col="yellow")+
  vline_at(map_estimate(post$age), col="green")

As expected they are approximately on top of each other.

5. Bayesian inferences

As we do with classical regression (frequentist), we can test the significance of the bayesian regression coefficients by checking whether the corresponding credible interval contains zero or not, if no then this coefficient is significant. Let’s go back to our model and check the significance of each coefficient (using credible based on the default hdi).

hdi(model_bayes)
# Highest Density Interval

Parameter   |        89% HDI
----------------------------
(Intercept) | [29.22, 36.29]
age         | [-0.18, -0.11]
dis         | [-0.67,  0.18]
chas1       | [ 5.16,  9.81]
eti(model_bayes)
# Equal-Tailed Interval

Parameter   |        89% ETI
----------------------------
(Intercept) | [29.20, 36.28]
age         | [-0.17, -0.11]
dis         | [-0.67,  0.18]
chas1       | [ 5.17,  9.83]

Using both methods, the only non-significant coefficient is dis variable, which is in line with the classical regression.

Note: this similar result between frequentist and bayesian regression may due to the normality assumption for the former that is well satisfied which gives satisfied results and due to the normal prior used in the latter. However, in real world it is less often to be sure about the normality assumption which may give contradict conclusions between the two approaches.

Another way to test the significance by checking the part of the credible interval that falls inside the ROPE interval. we can get this by calling the rope from bayestestR package.

rope(post$age)
# Proportion of samples inside the ROPE [-0.10, 0.10]:

inside ROPE
-----------
0.00 %     

For age almost all the credible interval (HDI) is outside the ROPE range, which means that coefficient is highly significant.

rope(post$chas1)
# Proportion of samples inside the ROPE [-0.10, 0.10]:

inside ROPE
-----------
0.00 %     
rope(post$`(Intercept)`)
# Proportion of samples inside the ROPE [-0.10, 0.10]:

inside ROPE
-----------
0.00 %     

The same thing is true for the “chas” and “intercept” variable.

rope(post$dis)
# Proportion of samples inside the ROPE [-0.10, 0.10]:

inside ROPE
-----------
20.02 %    

In contrast, almost the quarter of the credible interval of “dis” variable is inside the ROPE interval. In other words, the probability of this coefficient to be zero is 23.28%.

rope_range(model_bayes)
[1] -0.9197104  0.9197104

6. PD and P-value

Sometimes we are only interested in checking the direction of the coefficient (positive or negative). This is the role of pd statistic in the above table, high value means that the associated effect is concentrated on the same side as the median. For our model, since pd is equal to 1, almost all the posteriors of the two variables “age” and “chas1” and the “intercept” are on the same side (if median negative all other values are negatives). However, it should be noted that this statistic does not assess the significance of the effect. Something more important to mention is that there exists a strong relation between this probability and the p-value approximated as follows: 𝑝−𝑣𝑎𝑙𝑢𝑒=1−𝑝𝑑. let’s check this with our variables.

df1 <-dplyr::select(tidy(model_freq), c(term,p.value))
df1$p.value <- round(df1$p.value, digits = 3)
df2 <- 1- purrr::map_dbl(post, p_direction)
df <- cbind(df1,df2)
df
                   term p.value     df2
(Intercept) (Intercept)   0.000 0.00000
age                 age   0.000 0.00000
dis                 dis   0.354 0.18075
chas1             chas1   0.000 0.00000

Conclusion

Within the last decade more practitioners, specially in some fields such as medicine and psychology, are turning towards bayesian analysis since almost every thing can be interpreted straightforwardly with a probabilistic manner. However, the bayesian analysis has also some drawback, like the subjective way to define the priors (which play an important role to compute the posterior), or for problems that do not have conjugate prior, not always the mcmc alghoritm converges easily to the right values (specially with complex data).

LS0tCnRpdGxlOiAiQmF5ZXNpYW4gbGluZWFyIHJlZ3Jlc3Npb24iCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgaHRtbF9kb2N1bWVudDogZGVmYXVsdAotLS0KVGhpcyBpcyBhIHJlcHJvZHVjdGlvbiBmcm9tIGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tLzIwMjAvMDQvYmF5ZXNpYW4tbGluZWFyLXJlZ3Jlc3Npb24vCmZvciBwZXJzb25hbCBzdHVkeSBwdXJwb3NlLgoKKiAxLiBJbnRyb2R1Y3Rpb24KKiAyLiBEYXRhIHByZXBhcmF0aW9uCiogMy4gQ2xhc3NpY2FsIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsCiogNC4gQmF5ZXNpYW4gcmVncmVzc2lvbgoqIDUuIEJheWVzaWFuIGluZmVyZW5jZXMKKiA2LiBQRCBhbmQgUC12YWx1ZQoKIyAxLiBJbnRyb2R1Y3Rpb24KCiMjIEZyZXF1ZW50aXN0CgpGb3Igc3RhdGlzdGljYWwgaW5mZXJlbmNlcyB3ZSBoYXZlIHRvdyBnZW5lcmFsIGFwcHJvYWNoZXMgb3IgZnJhbWV3b3JrczoKCi0gRnJlcXVlbnRpc3QgYXBwcm9hY2ggaW4gd2hpY2ggdGhlIGRhdGEgc2FtcGxlZCBmcm9tIHRoZSBwb3B1bGF0aW9uIGlzIGNvbnNpZGVyZWQgYXMgcmFuZG9tIGFuZCB0aGUgcG9wdWxhdGlvbiBwYXJhbWV0ZXIgdmFsdWVzLCBrbm93biBhcyBudWxsIGh5cG90aGVzaXMsIGFzIGZpeGVkIChidXQgdW5rbm93bikuIFRvIGVzdGltYXRlIHRodXMgdGhpcyBudWxsIGh5cG90aGVzaXMgd2UgbG9vayBmb3IgdGhlIHNhbXBsZSBwYXJhbWV0ZXJzIHRoYXQgbWF4aW1pemUgdGhlIGxpa2VsaWhvb2Qgb2YgdGhlIGRhdGEuIEhvd2V2ZXIsIHRoZSBkYXRhIGF0IGhhbmQsIGV2ZW4gaXQgaXMgc2FtcGxlZCByYW5kb21seSBmcm9tIHRoZSBwb3B1bGF0aW9uLCBpdCBpcyBmaXhlZCBub3csIHNvIGhvdyBjYW4gd2UgY29uc2lkZXIgdGhpcyBkYXRhIGFzIHJhbmRvbS4gVGhlIGFuc3dlciBpcyB0aGF0IHdlIGFzc3VtZSB0aGF0IHRoZSBwb3B1bGF0aW9uIGRpc3RyaWJ1dGlvbiBpcyBrbm93biBhbmQgd2Ugd29yayBvdXQgdGhlIG1heGltdW0gbGlrZWxpaG9vZCBvZiB0aGUgZGF0YSB1c2luZyB0aGlzIGRpc3RyaWJ1dGlvbi4gT3Igd2UgcmVwZWF0IHRoZSBzdHVkeSBtYW55IHRpbWVzIHdpdGggZGlmZmVyZW50IHNhbXBsZXMgdGhlbiB3ZSBhdmVyYWdlIHRoZSByZXN1bHRzLiBTbyBpZiB3ZSBnZXQgdmVyeSBzbWFsbCB2YWx1ZSBmb3IgdGhlIGxpa2VsaWhvb2Qgb2YgdGhlIGRhdGEgd2hpY2ggaXMga25vd24gYXMgcC12YWx1ZSB3ZSB0ZW5kIHRvIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzLiBUaGUgbWFpbiBwcm9ibGVtLCBob3dldmVyLCBpcyB0aGUgbWlzdW5kZXJzdGFuZGluZyBhbmQgbWlzdXNpbmcgb2YgdGhpcyBwLXZhbHVlIHdoZW4gd2UgZGVjaWRlIHRvIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIGJhc2VkIG9uIHNvbWUgdGhyZXNob2xkLCBmcm9tIHdoaWNoIHdlIHdyb25nbHkgaW50ZXJwcmV0aW5nIGl0IGFzIHRoZSBwcm9iYWJpbGl0eSBvZiByZWplY3RpbmcgdGhlIG51bGwgaHlwb3RoZXNpcy4gRm9yIG1vcmUgZGV0YWlsIGFib3V0IHAtdmFsdWUgY2xpY2sgaGVyZS4KCi0gQmF5ZXNpYW4gYXBwcm9hY2gsIGluIGNvbnRyYXN0LCBwcm92aWRlcyB0cnVlIHByb2JhYmlsaXRpZXMgdG8gcXVhbnRpZnkgdGhlIHVuY2VydGFpbnR5IGFib3V0IGEgY2VydGFpbiBoeXBvdGhlc2lzLCBidXQgcmVxdWlyZXMgdGhlIHVzZSBvZiBhIGZpcnN0IGJlbGllZiBhYm91dCBob3cgbGlrZWx5IHRoaXMgaHlwb3RoZXNpcyBpcyB0cnVlLCBrbm93biBhcyBwcmlvciwgdG8gYmUgYWJsZSB0byBkZXJpdmUgdGhlIHByb2JhYmlsaXR5IG9mIHRoaXMgaHlwb3RoZXNpcyBhZnRlciBzZWVpbmcgdGhlIGRhdGEga25vd24gYXMgcG9zdGVyaW9yIHByb2JhYmlsaXR5LiBUaGlzIGFwcHJvYWNoIGNhbGxlZCBiYXllc2lhbiBiZWNhdXNlIGl0IGlzIGJhc2VkIG9uIHRoZSBiYXllc+KAmSB0aGVvcmVtLCBmb3IgaW5zdGFuY2UgaWYgYSBoYXZlIHBvcHVsYXRpb24gcGFyYW1ldGVyIHRvIGVzdGltYXRlIPCdnIMgLCBhbmQgd2UgaGF2ZSBzb21lIGRhdGEgc2FtcGxlZCByYW5kb21seSBmcm9tIHRoaXMgcG9wdWxhdGlvbiDwnZC3LCB0aGUgcG9zdGVyaW9yIHByb2JhYmlsaXR5IHRodXMgd2lsbCBiZQokJFxvdmVyYnJhY2V7cChcdGhldGEvRCl9XntQb3N0ZXJpb3J9PVxmcmFje1xvdmVyYnJhY2V7cChEL1x0aGV0YSl9XntMaWtlbGlob29kfS5cb3ZlcmJyYWNle3AoXHRoZXRhKX1ee1ByaW9yfX17XHVuZGVyYnJhY2V7cChEKX1fe0V2aWRlbmNlfX0kJApUaGUgRXZpZGVuY2UgaXMgdGhlIHByb2JhYmlsaXR5IG9mIHRoZSBkYXRhIGF0IGhhbmQgcmVnYXJkbGVzcyB0aGUgcGFyYW1ldGVyIPCdnIMKCiMgMi4gRGF0YSBwcmVwYXJhdGlvbgpGb3Igc2ltcGxpY2l0eSB3ZSB1c2UgdGhlIEJvc3RvbkhvdXNpbmcgZGF0YSBmcm9tIG1sYmVuY2ggcGFja2FnZSwgRm9yIG1vcmUgZGV0YWlsIGFib3V0IHRoaXMgZGF0YSBydW4gdGhpcyBjb21tYW5kID9Cb3N0b25Ib3VzaW5nIGFmdGVyIGNhbGxpbmcgdGhlIHBhY2thZ2UuIEJ1dCBmaXJzdCBMZXTigJlzIGNhbGwgYWxsIHRoZSBwYWNrYWdlcyB0aGF0IHdlIG5lZWQgdGhyb3VnaG91dCB0aGlzIGFydGljbGUuCmBgYHtyfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShtbGJlbmNoKSkKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkocnN0YW5hcm0pKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShiYXllc3Rlc3RSKSkKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoYmF5ZXNwbG90KSkKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoaW5zaWdodCkpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KGJyb29tKSkKZGF0YSgiQm9zdG9uSG91c2luZyIpCnN0cihCb3N0b25Ib3VzaW5nKQpgYGAKVG8gd2VsbCB1bmRlcnN0YW5kIGhvdyB0aGUgYmF5ZXNpYW4gcmVncmVzc2lvbiB3b3JrcyB3ZSBrZWVwIG9ubHkgdGhyZWUgZmVhdHVyZXMsIHR3byBudW1lcmljIHZhcmlhYmxlcyBhZ2UsIGRpcyBhbmQgb25lIGNhdGVnb3JpY2FsIGNoYXMsIHdpdGggdGhlIHRhcmdldCB2YXJpYWJsZSBtZWR2IHRoZSBtZWRpYW4gdmFsdWUgb2Ygb3duZXItb2NjdXBpZWQgaG9tZXMuCmBgYHtyfQpib3N0IDwtIEJvc3RvbkhvdXNpbmdbLGMoIm1lZHYiLCJhZ2UiLCJkaXMiLCJjaGFzIildCmhlYWQoYm9zdCkKc3RyKGJvc3QpCmBgYAojIDMuIENsYXNzaWNhbCBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbApUbyBoaWdobGlnaHQgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgQmF5ZXNpYW4gcmVncmVzc2lvbiBhbmQgdGhlIHRyYWRpdGlvbmFsIGxpbmVhciByZWdyZXNzaW9uIChmcmVxdWVudGlzdCBhcHByb2FjaCksIExldOKAmXMgZmlyc3QgZml0IHRoZSBsYXR0ZXIgdG8gb3VyIGRhdGEuCmBgYHtyfQptb2RlbF9mcmVxPC1sbShtZWR2fi4sIGRhdGE9Ym9zdCkKdGlkeShtb2RlbF9mcmVxKQpgYGAKQ2hlY2tpbmcgdGhlIHAudmFsdWUgb2YgZWFjaCByZWdyZXNzb3IgdGVsbHMgYWxsIHRoZSByZWdyZXNzb3JzIGFyZSBzaWduaWZpY2FudCBleGNlcHQgImRpcyIgdmFyaWFibGUuIFNpbmNlIHRoZSB2YXJpYWJsZSAiY2hhcyIgaXMgY2F0ZWdvcmljYWwgYnkgdHdvIGxldmVscywgdGhlIGNvZWZmaWNpZW50IG9mICJjaGFzMSIgaXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgbWVkaWFuIHByaWNlIG9mIGhvdXNlcyBvbiB0aGUgYm91bmRzIENoYXJsZXMgUml2ZXIgYW5kIHRoYXQgb2YgdGhlIG90aGVycywgc28gdGhlIG1lZGlhbiBwcmljZSBvZiB0aGUgZm9ybWVyIGlzIGFib3V0IDcuNTEzLgoKIyA0LiBCYXllc2lhbiByZWdyZXNzaW9uClRvIGZpdCBhIEJheWVzaWFuIHJlZ3Jlc3Npb24sIHdlIHVzZSB0aGUgZnVuY3Rpb24gKnN0YW5fZ2xtKiBmcm9tIHRoZSAqcnN0YW5hcm0qIHBhY2thZ2UuIFRoaXMgZnVuY3Rpb24gYXMgdGhlIGFib3ZlICpsbSogZnVuY3Rpb24gcmVxdWlyZXMgcHJvdmlkaW5nIHRoZSBmb3JtdWxhIGFuZCB0aGUgZGF0YSB0aGF0IHdpbGwgYmUgdXNlZCwgYW5kIGxlYXZlIGFsbCB0aGUgZm9sbG93aW5nIGFyZ3VtZW50cyB3aXRoIHRoZWlyIGRlZmF1bHQgdmFsdWVzOgoKLSBmYW1pbHkgOiBieSBkZWZhdWx0IHRoaXMgZnVuY3Rpb24gdXNlcyB0aGUgZ2F1c3NpYW4gZGlzdHJpYnV0aW9uIGFzIHdlIGRvIHdpdGggdGhlIGNsYXNzaWNhbCAqZ2xtKiBmdW5jdGlvbiB0byBwZXJmb3JtICpsbSogbW9kZWwuCi0gcHJpb3IgOiBUaGUgcHJpb3IgZGlzdHJpYnV0aW9uIGZvciB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMsIEJ5IGRlZmF1bHQgdGhlIG5vcm1hbCBwcmlvciBpcyB1c2VkLiBUaGVyZSBhcmUgc3Vic2V0IG9mIGZ1bmN0aW9ucyB1c2VkIGZvciB0aGUgcHJpb3IgcHJvdmlkZWQgYnkgcnN0YW5hcm0gbGlrZSAsIHN0dWRlbnQgdCBmYW1pbHksIGxhcGxhY2UgZmFtaWx54oCmZWN0LiBUbyBnZXQgdGhlIGZ1bGwgbGlzdCB3aXRoIGFsbCB0aGUgZGV0YWlscyBydW4gdGhpcyBjb21tYW5kICo/cHJpb3JzKi4gSWYgd2Ugd2FudCBhIGZsYXQgdW5pZm9ybSBwcmlvciB3ZSBzZXQgdGhpcyB0byBOVUxMLgotIHByaW9yX2ludGVyY2VwdDogcHJpb3IgZm9yIHRoZSBpbnRlcmNlcHQsIGNhbiBiZSBub3JtYWwsIHN0dWRlbnRfdCAsIG9yIGNhdWNoeS4gSWYgd2Ugd2FudCBhIGZsYXQgdW5pZm9ybSBwcmlvciB3ZSBzZXQgdGhpcyB0byBOVUxMLgotIHByaW9yX2F1eDogcHJpb3IgZm8gYXV4aWxpYXJ5IHBhcmFtZXRlcnMgc3VjaCBhcyB0aGUgZXJyb3Igc3RhbmRhcmQgZGV2aWF0aW9uIGZvciB0aGUgZ2F1c3Npb24gZmFtaWx5LgotIGFsZ29yaXRobTogVGhlIGVzdGltYXRpbmcgYXBwcm9hY2ggdG8gdXNlLiBUaGUgZGVmYXVsdCBpcyAic2FtcGxpbmcgTUNNQzEuCi0gUVI6IEZBTFNFIGJ5IGRlZmF1bHQsIGlmIHRydWUgUVIgZGVjb21wb3NpdGlvbiBhcHBsaWVkIG9uIHRoZSBkZXNpZ24gbWF0cml4IGlmIHdlIGhhdmUgbGFyZ2UgbnVtYmVyIG9mIHByZWRpY3RvcnMuCi0gaXRlciA6IGlzIHRoZSBudW1iZXIgb2YgaXRlcmF0aW9ucyBpZiB0aGUgTUNNQyBtZXRob2QgaXMgdXNlZCwgdGhlIGRlZmF1bHQgaXMgMjAwMC4KLSBjaGFpbnMgOiB0aGUgbnVtYmVyIG9mIE1hcmtvdiBjaGFpbnMsIHRoZSBkZWZhdWx0IGlzIDQuCi0gd2FybXVwIDogYWxzbyBrbm93biBhcyBidXJuaW4sIHRoZSBudW1iZXIgb2YgaXRlcmF0aW9ucyB1c2VkIGZvciBhZGFwdGF0aW9uLCBhbmQgc2hvdWxkIG5vdCBiZSB1c2VkIGZvciBpbmZlcmVuY2UuIEJ5IGRlZmF1bHQgaXQgaXMgaGFsZiBvZiB0aGUgaXRlcmF0aW9ucy4KCmBgYHtyfQptb2RlbF9iYXllcyA8LSBzdGFuX2dsbShtZWR2fi4sIGRhdGE9Ym9zdCwgc2VlZD0xMTEpCnByaW50KG1vZGVsX2JheWVzLCBkaWdpdHMgPSAzKQpgYGAKVGhlIE1lZGlhbiBlc3RpbWF0ZSBpcyB0aGUgbWVkaWFuIGNvbXB1dGVkIGZyb20gdGhlIE1DTUMgc2ltdWxhdGlvbiwgYW5kIE1BRF9TRCBpcyB0aGUgbWVkaWFuIGFic29sdXRlIGRldmlhdGlvbiBjb21wdXRlZCBmcm9tIHRoZSBzYW1lIHNpbXVsYXRpb24uIFRvIHdlbGwgdW5kZXJzdGFuZCBob3cgZ2V0dGluZyB0aGVzZSBvdXRwdXRzLCB3ZSBwbG90IHRoZSBNQ01DIHNpbXVsYXRpb24gb2YgZWFjaCBwcmVkaWN0b3IgdXNpbmcgKmJheWVzcGxvdCoKYGBge3J9Cm1jbWNfZGVucyhtb2RlbF9iYXllcywgcGFycyA9IGMoImFnZSIpKSsKICB2bGluZV9hdCgtMC4xNDMsIGNvbD0icmVkIikKYGBgCkFzIGNhbiBiZSBzZWVuLCB0aGUgcG9pbnQgZXN0aW1hdGUgb2YgImFnZSIgZmFsbHMgb24gdGhlIG1lZGlhbiBvZiB0aGUgZGlzdHJpYnV0aW9uIChyZWQgbGluZSkuIFRoZSBzYW1lIHRoaW5nIGlzIHRydWUgZm9yICJkaXMiIiBhbmQgImNoYXMiIHByZWRpY3RvcnMuCmBgYHtyfQptY21jX2RlbnMobW9kZWxfYmF5ZXMsIHBhcnM9YygiZGlzIikpKwogIHZsaW5lX2F0KC0wLjI0NCwgY29sPSJyZWQiKQptY21jX2RlbnMobW9kZWxfYmF5ZXMsIHBhcnM9YygiY2hhczEiKSkrCiAgdmxpbmVfYXQoNy40OTYsIGNvbD0icmVkIikKYGBgCkhvdyBkbyB3ZSBldmFsdWF0ZSB0aGUgbW9kZWwgcGFyYW1ldGVycz8gQnkgYW5hbHl6aW5nIHRoZSBwb3N0ZXJpb3JzIHVzaW5nIHNvbWUgc3BlY2lmaWMgc3RhdGlzdGljcy4gV2UgbWFrZSB1c2Ugb2YgdGhlIGZ1bmN0aW9uICpkZXNjcmliZV9wb3N0ZXJpb3IqIHByb3ZpZGVkIGJ5ICpiYXllc3Rlc3RSKiBwYWNrYWdlLgpgYGB7cn0KZGVzY3JpYmVfcG9zdGVyaW9yKG1vZGVsX2JheWVzKQpgYGAKLSBDSSA6IENyZWRpYmxlIEludGVydmFsLCBpdCBpcyB1c2VkIHRvIHF1YW50aWZ5IHRoZSB1bmNlcnRhaW50eSBhYm91dCB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMuIFRoZXJlIGFyZSB0d28gbWV0aG9kcyB0byBjb21wdXRlIENJLCB0aGUgaGlnaGVzdCBkZW5zaXR5IGludGVydmFsIEhESSB3aGljaCBpcyB0aGUgZGVmYXVsdCwgYW5kIHRoZSBFcXVhbC10YWlsZWQgSW50ZXJ2YWwgRVRJLiB3aXRoIDg5JSBwcm9iYWJpbGl0eSAoZ2l2ZW4gdGhlIGRhdGEpIHRoYXQgYSBjb2VmZmljaWVudCBsaWVzIGFib3ZlIHRoZSBDSV9sb3cgdmFsdWUgYW5kIHVuZGVyIENJX2hpZ2ggdmFsdWUuIFRoaXMgc3RyaWdodGZvcndhcmQgcHJvYmFiaWxpc3RpYyBpbnRlcnByZXRhdGlvbiBpcyBjb21wbGV0ZWx5IGRpZmZyZW50IGZyb20gdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgdXNlZCBpbiBjbGFzc2ljYWwgbGluZWFyIHJlZ3Jlc3Npb24gd2hlcmUgdGhlIGNvZWZmaWNpZW50IGZhbGwgaW5zaWRlIHRoaXMgY29uZmlkZW5jZSBpbnRlcnZhbCAoaWYgd2UgY2hvb3NlIDk1JSBvZiBjb25maWRlbmNlKSA5NSB0aW1lcyBpZiB3ZSByZXBlYXQgdGhlIHN0dWR5IDEwMCB0aW1lcy4KLSBwZCA6IFByb2JhYmlsaXR5IG9mIERpcmVjdGlvbiAsIHdoaWNoIGlzIHRoZSBwcm9iYWJpbGl0eSB0aGF0IHRoZSBlZmZlY3QgZ29lcyB0byB0aGUgcG9zaXRpdmUgb3IgdG8gdGhlIG5lZ2F0aXZlIGRpcmVjdGlvbiwgYW5kIGl0IGlzIGNvbnNpZGVyZWQgYXMgdGhlIGJlc3QgZXF1aXZhbGVudCBmb3IgdGhlIHAtdmFsdWUuCi0gUk9QRV9DSTogUmVnaW9uIG9mIFByYWN0aWNhbCBFcXVpdmFsZW5jZSwgc2luY2UgYmF5ZXMgbWV0aG9kIGRlYWxzIHdpdGggdHJ1ZSBwcm9iYWJpbGl0aWVzLCBpdCBkb2VzIG5vdCBtYWtlIHNlbnNlIHRvIGNvbXB1dGUgdGhlIHByb2JhYmlsaXR5IG9mIGdldHRpbmcgdGhlIGVmZmVjdCBlcXVhbHMgemVybyAodGhlIG51bGwgaHlwb3RoZXNpcykgYXMgYSBwb2ludCAocHJvYmFiaWxpdHkgb2YgYSBwb2ludCBpbiBjb250aW51b3VzIGludGVydmFscyBlcXVhbCB6ZXJvICkuIFRodXMsIHdlIGRlZmluZSBpbnN0ZWFkIGEgc21hbGwgcmFuZ2UgYXJvdW5kIHplcm8gd2hpY2ggY2FuIGJlIGNvbnNpZGVyZWQgcHJhY3RpY2FsbHkgdGhlIHNhbWUgYXMgbm8gZWZmZWN0ICh6ZXJvKSwgdGhpcyByYW5nZSB0aGVyZWZvcmUgaXMgY2FsbGVkIFJPUEUuIEJ5IGRlZmF1bHQgKGFjY29yZGluZyB0byBDb2hlbiwgMTk4OCkgVGhlIFJvcGUgaXMgWy0wLjEsMC4xXSBmcm9tIHRoZSBzdGFuZGFyZGl6ZWQgY29lZmZpY2llbnRzLgotIFJoYXQ6IHNjYWxlIHJlZHVjdGlvbiBmYWN0b3Ig8J2RhcyCICwgaXQgaXMgY29tcHV0ZWQgZm9yIGVhY2ggc2NhbGFyIHF1YW50aXR5IG9mIGludGVyZXN0LCBhcyB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoYXQgcXVhbnRpdHkgZnJvbSBhbGwgdGhlIGNoYWlucyBpbmNsdWRlZCB0b2dldGhlciwgZGl2aWRlZCBieSB0aGUgcm9vdCBtZWFuIHNxdWFyZSBvZiB0aGUgc2VwYXJhdGUgd2l0aGluLWNoYWluIHN0YW5kYXJkIGRldmlhdGlvbnMuIFdoZW4gdGhpcyB2YWx1ZSBpcyBjbG9zZSB0byAxIHdlIGRvIG5vdCBoYXZlIGFueSBjb252ZXJnZW5jZSBwcm9ibGVtIHdpdGggTUNNQy4KLSBFU1M6IGVmZmVjdGl2ZSBzYW1wbGUgc2l6ZSwgaXQgY2FwdHVyZXMgaG93IG1hbnkgaW5kZXBlbmRlbnQgZHJhd3MgY29udGFpbiB0aGUgc2FtZSBhbW91bnQgb2YgaW5mb3JtYXRpb24gYXMgdGhlIGRlcGVuZGVudCBzYW1wbGUgb2J0YWluZWQgYnkgdGhlIE1DTUMgYWxnb3JpdGhtLCB0aGUgaGlnaGVyIHRoZSBFU1MgdGhlIGJldHRlci4gVGhlIHRocmVzaG9sZCB1c2VkIGluIHByYWN0aWNlIGlzIDQwMC4KCkF0ZXJuYXRpdmVseSwgd2UgY2FuIGdldCB0aGUgY29lZmZpY2VpZW50IGVzdGltYXRlcyAod2hpY2ggYXJlIHRoZSBtZWRpYW5zIGJ5IGRlZmF1bHQpIHNlcGFyYXRseSBieSB1c2luZyB0aGUgcGFja2FnZSAqaW5zaWdodCouCmBgYHtyfQpwb3N0IDwtIGdldF9wYXJhbWV0ZXJzKG1vZGVsX2JheWVzKQpwcmludChwdXJycjo6bWFwX2RibChwb3N0LG1lZGlhbiksZGlnaXRzID0gMykKYGBgCldlIGNhbiBhbHNvIGNvbXB1dGUgdGhlIE1heGltdW0gQSBwb3N0ZXJpb3JpIChNQVApLCBhbmQgdGhlIG1lYW4gYXMgZm9sbG93czoKYGBge3J9CnByaW50KHB1cnJyOjptYXBfZGJsKHBvc3QsIG1hcF9lc3RpbWF0ZSksZGlnaXRzID0gMykKcHJpbnQocHVycnI6Om1hcF9kYmwocG9zdCwgbWVhbiksZGlnaXRzID0gMykKYGBgCkFzIHdlIHNlZSwgdGhlIHZhbHVlcyBhcmUgY2xvc2VyIHRvIGVhY2ggb3RoZXIgZHVlIHRvIHRoZSBsaWtlIG5vcm1hbGl0eSBvZiB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBwb3N0ZXJpb3JzIHdoZXJlIGFsbCB0aGUgY2VudHJhbCBzdGF0aXN0aWNzIChtZWFuLCBtZWRpYW4sIG1vZGUpIGFyZSBjbG9zZXIgdG8gZWFjaCBvdGhlci4KVXNpbmcgdGhlIGZvbGxvd2luZyBwbG90IHRvIHZpc3VhbGl6ZSB0aGUgYWdlIGNvZWZmaWNpZW50IHVzaW5nIGRpZmZlcmVudCBzdGF0aXN0aWNzIGFzIGZvbGxvd3M6CmBgYHtyfQptY21jX2RlbnMobW9kZWxfYmF5ZXMsIHBhcnM9YygiYWdlIikpKwogIHZsaW5lX2F0KG1lZGlhbihwb3N0JGFnZSksIGNvbD0icmVkIikrCiAgdmxpbmVfYXQobWVhbihwb3N0JGFnZSksIGNvbD0ieWVsbG93IikrCiAgdmxpbmVfYXQobWFwX2VzdGltYXRlKHBvc3QkYWdlKSwgY29sPSJncmVlbiIpCmBgYApBcyBleHBlY3RlZCB0aGV5IGFyZSBhcHByb3hpbWF0ZWx5IG9uIHRvcCBvZiBlYWNoIG90aGVyLgoKIyA1LiBCYXllc2lhbiBpbmZlcmVuY2VzCkFzIHdlIGRvIHdpdGggY2xhc3NpY2FsIHJlZ3Jlc3Npb24gKGZyZXF1ZW50aXN0KSwgd2UgY2FuIHRlc3QgdGhlIHNpZ25pZmljYW5jZSBvZiB0aGUgYmF5ZXNpYW4gcmVncmVzc2lvbiBjb2VmZmljaWVudHMgYnkgY2hlY2tpbmcgd2hldGhlciB0aGUgY29ycmVzcG9uZGluZyBjcmVkaWJsZSBpbnRlcnZhbCBjb250YWlucyB6ZXJvIG9yIG5vdCwgaWYgbm8gdGhlbiB0aGlzIGNvZWZmaWNpZW50IGlzIHNpZ25pZmljYW50LiBMZXTigJlzIGdvIGJhY2sgdG8gb3VyIG1vZGVsIGFuZCBjaGVjayB0aGUgc2lnbmlmaWNhbmNlIG9mIGVhY2ggY29lZmZpY2llbnQgKHVzaW5nIGNyZWRpYmxlIGJhc2VkIG9uIHRoZSBkZWZhdWx0ICpoZGkqKS4KYGBge3J9CmhkaShtb2RlbF9iYXllcykKZXRpKG1vZGVsX2JheWVzKQpgYGAKVXNpbmcgYm90aCBtZXRob2RzLCB0aGUgb25seSBub24tc2lnbmlmaWNhbnQgY29lZmZpY2llbnQgaXMgZGlzIHZhcmlhYmxlLCB3aGljaCBpcyBpbiBsaW5lIHdpdGggdGhlIGNsYXNzaWNhbCByZWdyZXNzaW9uLgoKX19Ob3RlX186IHRoaXMgc2ltaWxhciByZXN1bHQgYmV0d2VlbiBmcmVxdWVudGlzdCBhbmQgYmF5ZXNpYW4gcmVncmVzc2lvbiBtYXkgZHVlIHRvIHRoZSBub3JtYWxpdHkgYXNzdW1wdGlvbiBmb3IgdGhlIGZvcm1lciB0aGF0IGlzIHdlbGwgc2F0aXNmaWVkIHdoaWNoIGdpdmVzIHNhdGlzZmllZCByZXN1bHRzIGFuZCBkdWUgdG8gdGhlIG5vcm1hbCBwcmlvciB1c2VkIGluIHRoZSBsYXR0ZXIuIEhvd2V2ZXIsIGluIHJlYWwgd29ybGQgaXQgaXMgbGVzcyBvZnRlbiB0byBiZSBzdXJlIGFib3V0IHRoZSBub3JtYWxpdHkgYXNzdW1wdGlvbiB3aGljaCBtYXkgZ2l2ZSBjb250cmFkaWN0IGNvbmNsdXNpb25zIGJldHdlZW4gdGhlIHR3byBhcHByb2FjaGVzLgoKQW5vdGhlciB3YXkgdG8gdGVzdCB0aGUgc2lnbmlmaWNhbmNlIGJ5IGNoZWNraW5nIHRoZSBwYXJ0IG9mIHRoZSBjcmVkaWJsZSBpbnRlcnZhbCB0aGF0IGZhbGxzIGluc2lkZSB0aGUgKlJPUEUqIGludGVydmFsLiB3ZSBjYW4gZ2V0IHRoaXMgYnkgY2FsbGluZyB0aGUgcm9wZSBmcm9tICpiYXllc3Rlc3RSKiBwYWNrYWdlLgpgYGB7cn0Kcm9wZShwb3N0JGFnZSkKYGBgCkZvciBhZ2UgYWxtb3N0IGFsbCB0aGUgY3JlZGlibGUgaW50ZXJ2YWwgKEhESSkgaXMgb3V0c2lkZSB0aGUgKlJPUEUqIHJhbmdlLCB3aGljaCBtZWFucyB0aGF0IGNvZWZmaWNpZW50IGlzIGhpZ2hseSBzaWduaWZpY2FudC4KYGBge3J9CnJvcGUocG9zdCRjaGFzMSkKcm9wZShwb3N0JGAoSW50ZXJjZXB0KWApCmBgYApUaGUgc2FtZSB0aGluZyBpcyB0cnVlIGZvciB0aGUgImNoYXMiIGFuZCAiaW50ZXJjZXB0IiB2YXJpYWJsZS4KYGBge3J9CnJvcGUocG9zdCRkaXMpCmBgYApJbiBjb250cmFzdCwgYWxtb3N0IHRoZSBxdWFydGVyIG9mIHRoZSBjcmVkaWJsZSBpbnRlcnZhbCBvZiAiZGlzIiB2YXJpYWJsZSBpcyBpbnNpZGUgdGhlIFJPUEUgaW50ZXJ2YWwuIEluIG90aGVyIHdvcmRzLCB0aGUgcHJvYmFiaWxpdHkgb2YgdGhpcyBjb2VmZmljaWVudCB0byBiZSB6ZXJvIGlzIDIzLjI4JS4KYGBge3J9CnJvcGVfcmFuZ2UobW9kZWxfYmF5ZXMpCmBgYAoKIyA2LiBQRCBhbmQgUC12YWx1ZQpTb21ldGltZXMgd2UgYXJlIG9ubHkgaW50ZXJlc3RlZCBpbiBjaGVja2luZyB0aGUgZGlyZWN0aW9uIG9mIHRoZSBjb2VmZmljaWVudCAocG9zaXRpdmUgb3IgbmVnYXRpdmUpLiBUaGlzIGlzIHRoZSByb2xlIG9mICpwZCogc3RhdGlzdGljIGluIHRoZSBhYm92ZSB0YWJsZSwgaGlnaCB2YWx1ZSBtZWFucyB0aGF0IHRoZSBhc3NvY2lhdGVkIGVmZmVjdCBpcyBjb25jZW50cmF0ZWQgb24gdGhlIHNhbWUgc2lkZSBhcyB0aGUgbWVkaWFuLiBGb3Igb3VyIG1vZGVsLCBzaW5jZSBwZCBpcyBlcXVhbCB0byAxLCBhbG1vc3QgYWxsIHRoZSBwb3N0ZXJpb3JzIG9mIHRoZSB0d28gdmFyaWFibGVzICJhZ2UiIGFuZCAiY2hhczEiIGFuZCB0aGUgImludGVyY2VwdCIgYXJlIG9uIHRoZSBzYW1lIHNpZGUgKGlmIG1lZGlhbiBuZWdhdGl2ZSBhbGwgb3RoZXIgdmFsdWVzIGFyZSBuZWdhdGl2ZXMpLiBIb3dldmVyLCBpdCBzaG91bGQgYmUgbm90ZWQgdGhhdCB0aGlzIHN0YXRpc3RpYyBkb2VzIG5vdCBhc3Nlc3MgdGhlIHNpZ25pZmljYW5jZSBvZiB0aGUgZWZmZWN0LgpTb21ldGhpbmcgbW9yZSBpbXBvcnRhbnQgdG8gbWVudGlvbiBpcyB0aGF0IHRoZXJlIGV4aXN0cyBhIHN0cm9uZyByZWxhdGlvbiBiZXR3ZWVuIHRoaXMgcHJvYmFiaWxpdHkgYW5kIHRoZSAqcCotdmFsdWUgYXBwcm94aW1hdGVkIGFzIGZvbGxvd3M6IPCdkZ3iiJLwnZGj8J2RjvCdkZnwnZGi8J2Rkj0x4oiS8J2RnfCdkZEuIGxldOKAmXMgY2hlY2sgdGhpcyB3aXRoIG91ciB2YXJpYWJsZXMuCmBgYHtyfQpkZjEgPC1kcGx5cjo6c2VsZWN0KHRpZHkobW9kZWxfZnJlcSksIGModGVybSxwLnZhbHVlKSkKZGYxJHAudmFsdWUgPC0gcm91bmQoZGYxJHAudmFsdWUsIGRpZ2l0cyA9IDMpCmRmMiA8LSAxLSBwdXJycjo6bWFwX2RibChwb3N0LCBwX2RpcmVjdGlvbikKZGYgPC0gY2JpbmQoZGYxLGRmMikKZGYKYGBgCiMjIENvbmNsdXNpb24KCldpdGhpbiB0aGUgbGFzdCBkZWNhZGUgbW9yZSBwcmFjdGl0aW9uZXJzLCBzcGVjaWFsbHkgaW4gc29tZSBmaWVsZHMgc3VjaCBhcyBtZWRpY2luZSBhbmQgcHN5Y2hvbG9neSwgYXJlIHR1cm5pbmcgdG93YXJkcyBiYXllc2lhbiBhbmFseXNpcyBzaW5jZSBhbG1vc3QgZXZlcnkgdGhpbmcgY2FuIGJlIGludGVycHJldGVkIHN0cmFpZ2h0Zm9yd2FyZGx5IHdpdGggYSBwcm9iYWJpbGlzdGljIG1hbm5lci4gSG93ZXZlciwgdGhlIGJheWVzaWFuIGFuYWx5c2lzIGhhcyBhbHNvIHNvbWUgZHJhd2JhY2ssIGxpa2UgdGhlIHN1YmplY3RpdmUgd2F5IHRvIGRlZmluZSB0aGUgcHJpb3JzICh3aGljaCBwbGF5IGFuIGltcG9ydGFudCByb2xlIHRvIGNvbXB1dGUgdGhlIHBvc3RlcmlvciksIG9yIGZvciBwcm9ibGVtcyB0aGF0IGRvIG5vdCBoYXZlIGNvbmp1Z2F0ZSBwcmlvciwgbm90IGFsd2F5cyB0aGUgKm1jbWMqIGFsZ2hvcml0bSBjb252ZXJnZXMgZWFzaWx5IHRvIHRoZSByaWdodCB2YWx1ZXMgKHNwZWNpYWxseSB3aXRoIGNvbXBsZXggZGF0YSkuCgo=