Back to Incite! Decision Technologies.
Imagine that we have a population of something composed of two subset populations that, while distinct from each other, share a common characteristic that can be measured along some kind of scale. Furthermore, let’s assume that each subset population expresses this characteristic with a frequency distribution unique to each. In other words, along the scale of measurement for the characteristic, each subset displays varying levels of the characteristic among its members. Now, we choose a specimen from the larger population in an unbiased manner and measure this characteristic for this specific individual. Are we justified in inferring the subset membership of the specimen based on this measurement alone? Baye’s rule (or theorem), something you may have heard about in this age of exploding data analytics, tells us that we can be so justified as long as we assign a probability (or degree of belief) to our inference. The following discussion provides an interesting way of understanding the process for doing this. More importantly, I present how Baye’s theorem helps us overcome a common thinking failure associated with making inferences from an incomplete treatment of all the information we should use. I’ll use a bit of a fanciful example to convey this understanding along with showing the associated calculations in the R programming language.
Note: If you don't care about the R code at all and just want to follow the
basic reasoning, you can hide all the code at once by selecting Code/Hide All
Code from the drop down list in the top right corner of this page. If you want
to play around with the R code yourself, you can download the code by selecting
Code/Download Rmd.
Suppose we are aliens from another planet conducting scientific research on this strange group of bipedal organisms called humans. Humans are sexually dimorphic, presenting themselves as female and male genders. Each gender expresses its height across a distribution, and the means of each gender population are distinct. Years of collecting data about the humans reveals that the mean height for adult females is 5.33 bliks; males, 5.83 bliks. (“Bliks” are our unit distance measurement, like feet or meters.) Both genders show a similar bell-shaped distributed (i.e., Normal distribution) variation around their mean heights with a standard deviation of 0.2.
gender <- c("Female", "Male")
adult.m <- c(5.33, 5.83) # Mean adult height by gender.
adult.sd <- 0.2 # Adult height standard deviation. Constant across gender.
Another piece of information that we have collected about the human population is the base rate frequency, or the proportion, of each subpopulation in the total population. These proportions provide some valuable information, particularly if the proportion of one subpopulation is is larger than the others. For example, among the Zargons, chartreuse gills show up in 85% of the population compared to 15% for those with magenta gills. If we randomly select a member of the Zargon population for our zoo exhibit, we most likely will select one with chartreuse gills. In the language of Bayesian reasoning, we can call this base rate the prior probability (or degree of belief) that a randomly selected individual from the total population should be classified as a member of one category or another. In the generic notation, this would be Prob(Hypothesis), but in the context of our current situation, we might write Prob(Gender). Hold on to this concept, as we will return to it in just two shakes of a proton dislocator.
Unfortunately, a recent viral epidemic nearly wiped out the population of males, leaving the females over-represented as 90% of the population as compared to the historically approximately even distribution of females to males.
# Set up a vector to contain the current Prob(Gender).
base.rate.freq.fem <- 0.9 # The female base rate frequency.
base.rate.freq <- c(base.rate.freq.fem, 1 - base.rate.freq.fem) # (females, males)
Imagine that on an exploratory mission to Earth, we discover an injured human to whom we intend to apply medical attention before we ship it off to our galactic zoo; however, due to its injuries and its extreme emotional state, we can’t determine its gender. The only data we can obtain is its height (Yes, we can travel across vast distances of space, but we have to use this crude means to make a measurement. Bear with me. This is just an example, not a sci-fi novel.), which we observe to be 5.65 bliks. Based on this one sample of measured information, we must infer the gender of the specimen before we proceed. How can we use the information we have on hand to make a rational inference?
# The measured height of our unidentified specimen.
# units = Bliks
specimen.height <- 5.65
First, we need to determine the probable height of any member of a gender based on our current characterization of the gender populations. Again, in the language of Bayesian reasoning, we call this the likelihood function, which tells us the probability of observing some kind of evidence, like a measurement, that is conditional on the selection of a given population or conditional on a given hypothesis being the case. We usually write this as Prob(Evidence|Hypothesis). In the context of our problem, we might write Prob(Height|Gender).
Using the dnorm(...)
function, we can calculate the probability distribution across the height domain for each gender. Note that the dnorm(...)
function produces the probability density at a given point.
# Here we set up an index for the height domain, with a step size of
# 0.04 bliks.
step.size <- .04
height <- seq(4, 7, by = step.size)
# Calculate the height probability distribution for each gender across the
# height domain. In this case, the result will be an array of shape (2 x
# height). We apply the transpose funtion to this array to reorient it to
# (height x 2).
likelihood.height <- t(sapply(height, function(h)
dnorm(h, adult.m, adult.sd)))
# Recast the array as a data frame in preparation for plotting in ggplot.
df.likelihood.height <- data.frame(height, likelihood.height)
colnames(df.likelihood.height) <- c("Height", gender)
If we plot this function, we observe the probability distribution across the height domain. Again, we also refer to this as the likelihood function.
# Recast the rectangular data frame to a relational format such that the height
# index is the leftmost variable.
melt.df.likelihood.height <- melt(df.likelihood.height,
id.vars = "Height",
variable.name = "Gender",
value.name = "Probability")
height.distr <- ggplot(melt.df.likelihood.height,
aes(
x = Height,
y = Probability,
group = Gender,
colour = Gender
)) +
geom_line(aes(group = Gender, colour = Gender)) +
geom_vline(xintercept = specimen.height) +
xlab("Height [bliks]") +
ylab("Probability Density") +
ggtitle("Height Distribution of Human Adults", subtitle = "Prob(Height|Gender)")
print(height.distr)
It’s tempting to look at these distributions and conclude that the proper inference about the gender of our specimen would be the population with the greatest probability at the specimen’s measured height. After all, the height of adult humans is one of the most easily observable and available aspects of this strange species that tends to cover up all the distinguishing dimorphic characteristics with something they call “clothes.” A quick reference to our intuition might tell us that height is a good measure to make an inference about gender. In this case, we measured the height of our specimen at 5.65
bliks (the vertical black line). Based on this height compared to the distributions, we might infer that the specimen is male since the likelihood at 5.65
bliks is higher for males than females.
Unfortunately, this approach leaves out the information we have about the current prevalence (or the base rate frequency or prior degree of belief) of the genders in the population. Remember the example of the Zargons? We typically could infer the subpopulation based on the observation of a dominantly expressed characteristic. But in this case, we have two characteristcs we could consider, and they seem to work against each other; that is, a randomly selected adult human after the viral post-apocalypse is now mostly likely female, but the height characteristic information we have seems to lead us to infer that we have a male. Can we combine both pieces of information to adjust the strength of our inclination in a reasonable manner? Fortunately, all that we have to do to include both pieces of information in our judgment is to weight the likelihood of height for a given population proportionally to the prevalence of the population. We can achieve this by multiplying the liklihood function by the base rate probability.
likelihood.height.gender <- t(base.rate.freq * t(likelihood.height))
# Recast the array as a data frame in preparation for plotting in ggplot.
df.likelihood.height.gender <- data.frame(height, likelihood.height.gender)
colnames(df.likelihood.height.gender) <- c("Height", gender)
# Recast the rectangular data frame to a relational format such that the height
# index is the leftmost variable.
melt.df.likelihood.height.gender <- melt(df.likelihood.height.gender,
id.vars = "Height",
variable.name = "Gender",
value.name = "Probability")
height.distr.gender <- ggplot(melt.df.likelihood.height.gender,
aes(
x = Height,
y = Probability,
group = Gender,
colour = Gender
)) +
geom_line(aes(group = Gender, colour = Gender)) +
geom_vline(xintercept = specimen.height) +
xlab("Height [bliks]") +
ylab("Probability Density") +
ggtitle("Probability-Weighted Height Distribution of Human Adults", subtitle = "Prob(Height|Gender) * Prob(Gender)")
print(height.distr.gender)
Now we observe that the probability weighted height distributions most likely ought to change our inference about the gender of our specimen. All we need to do now is find the conditional likelihoods at the measured height and normalize them with the marginal probability at that height, where the marginal probability is just the sum of the conditional likelihoods across genders at the measured height.
conditional.likelihood <-
base.rate.freq * dnorm(specimen.height, adult.m, adult.sd)
# cast into an array.
conditional.likelihood <- array(conditional.likelihood, dim = c(1, 2))
colnames(conditional.likelihood) <- gender
The conditional likelihoods for each gender at the measured height is:
Female Male
0.50 0.13
# The marginal probability is the total probability of the product of the prior
# and the likelihood at the specified measured observation.
marginal.prob <- sum(conditional.likelihood)
The marginal probability, which is just the sum of the two probabilities in our conditional likelihood is:
[1] 0.63
Now we divide the conditional likelihood values by the marginal probability value.
# The probability (rational inference) of gender associated with sampled height.
posterior.prob.gender <- conditional.likelihood / marginal.prob
We should accept now as our updated belief (i.e., posterior probability) about the gender of our specimen to be:
Female Male
0.79 0.21
Our final observation is that Female
should be our rational inference for the gender of our specimen based on the prior probability weighted likelihood of height as a function of gender.
In short, what we calculated was prob(Gender|Height) = prob(Gender) * prob(Height|Gender)/(marginal probability)
where the marginal probability is sum(prob(Gender) * prob(Height|Gender))
Then we selected the gender argument with the highest probability.
The term sum(prob(Gender) * prob(Height|Gender))
is a normalizing factor. Once the quotient has been found, the sum of probabilities should equal to 1.
With regard to our example, this exercise has shown us that tall females among the population of all adult females are somewhat rare. However, tall females among the entire post-apocalyptic population of adult humans (in which females are the most prevalent subpopulation) are more prevalent than just-below-average height males, even though such males would be more prevalent than tall females if the the subpopulations were more equal in size. Therefore, finding a tall individual should lead us to infer, given no other information about them, that the individual is most likely female. Combining both pieces of information allows us to temper our intuition that would have been based on only one of the available pieces of information.
An interesting question that might arise from these insights is: what is the base rate frequency of the populations of females and males that might instill complete ambiguity in us about the gender of the specimen based on the measured height and the likelihood height functions of each gender? The answer is simple. All we have to do is recognize that
base.rate.freq.fem * Female_Likelihood|specimen.height = (1 - base.rate.freq.fem) * Male_Likelihood|specimen.height
and then solve for base.rate.freq.fem
. Let’s call this special base rate frequency indif.base.rate.freq.fem
. Rearranging we get
Likelihood.ratio = Male_Likelihood|specimen.height / Female_Likelihood|specimen.height
indif.base.rate.freq.fem = Likelihood.ratio / (1 + Likelihood.ratio)
# reform example equations into R statements
likelihood.ratio <- dnorm(specimen.height, adult.m[2], adult.sd) / dnorm(specimen.height, adult.m[1], adult.sd)
indif.base.rate.freq.fem <- likelihood.ratio / (1 + likelihood.ratio)
Female Male
0.71 0.29
Therefore, if we find ourselves in a situation in which the proportion of females and males matches those in our indifference calculation above, we really won’t know how which gender would most likely be indicated from our height measurement alone. More invasive tests might be required. :O
In general, what this example shows us is that combining background information can play a significant role in helping us make less biased judgments. Instead of thinking about just the distribution of a characteristic within a subpopulation with which we are most familiar, we need also to think about the distribution of the subpopulations that exhibit the characteristic we might choose to measure in order to make an inference about which subpopulation a specimen might belong. Not taking the full context into account of a situation is what often leads us to make bigoted socially toxic judgments, entertain superstitions, or allocate marketing and sales resources to markets that are least receptive to our product messaging.
In the next few installments on this discussion, I plan to dive a little more deeply in to the Bayesian reasoning process with increasingly more complex examples that will remain still rather simple to implement in R code. If you don’t come for the code, I do hope you entertain the thinking process that can help improve executive judgment and decision making.
Back to Incite! Decision Technologies.
LS0tCnRpdGxlOiAiQmF5ZXNpYW4gUmVhc29uaW5nOiBHZW5kZXIgSW5mZXJlbmNlIGZyb20gYSBTcGVjaW1lbiBNZWFzdXJlbWVudCIKYXV0aG9yOiAiUm9iZXJ0IEQuIEJyb3duIElJSSIKZGF0ZTogIjgvNC8yMDE3IgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKLS0tCgpgYGB7ciBsaWJyYXJpZXMsIGVjaG8gPSBGQUxTRX0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHJlc2hhcGUyKQpgYGAKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKQmFjayB0byBbSW5jaXRlISBEZWNpc2lvbiBUZWNobm9sb2dpZXNdKGh0dHA6Ly93d3cuaW5jaXRlZGVjaXNpb250ZWNoLmNvbSkuCgpJbWFnaW5lIHRoYXQgd2UgaGF2ZSBhIHBvcHVsYXRpb24gb2Ygc29tZXRoaW5nIGNvbXBvc2VkIG9mIHR3byBzdWJzZXQgCnBvcHVsYXRpb25zIHRoYXQsIHdoaWxlIGRpc3RpbmN0IGZyb20gZWFjaCBvdGhlciwgc2hhcmUgYSBjb21tb24gY2hhcmFjdGVyaXN0aWMgCnRoYXQgY2FuIGJlIG1lYXN1cmVkIGFsb25nIHNvbWUga2luZCBvZiBzY2FsZS4gRnVydGhlcm1vcmUsIGxldCdzIGFzc3VtZSB0aGF0IAplYWNoIHN1YnNldCBwb3B1bGF0aW9uIGV4cHJlc3NlcyB0aGlzIGNoYXJhY3RlcmlzdGljIHdpdGggYSBmcmVxdWVuY3kgCmRpc3RyaWJ1dGlvbiB1bmlxdWUgdG8gZWFjaC4gSW4gb3RoZXIgd29yZHMsIGFsb25nIHRoZSBzY2FsZSBvZiBtZWFzdXJlbWVudCBmb3IgCnRoZSBjaGFyYWN0ZXJpc3RpYywgZWFjaCBzdWJzZXQgZGlzcGxheXMgdmFyeWluZyBsZXZlbHMgb2YgdGhlIGNoYXJhY3RlcmlzdGljIAphbW9uZyBpdHMgbWVtYmVycy4gTm93LCB3ZSBjaG9vc2UgYSBzcGVjaW1lbiBmcm9tIHRoZSBsYXJnZXIgcG9wdWxhdGlvbiBpbiBhbiAKdW5iaWFzZWQgbWFubmVyIGFuZCBtZWFzdXJlIHRoaXMgY2hhcmFjdGVyaXN0aWMgZm9yIHRoaXMgc3BlY2lmaWMgaW5kaXZpZHVhbC4gCkFyZSB3ZSBqdXN0aWZpZWQgaW4gaW5mZXJyaW5nIHRoZSBzdWJzZXQgbWVtYmVyc2hpcCBvZiB0aGUgc3BlY2ltZW4gYmFzZWQgb24gCnRoaXMgbWVhc3VyZW1lbnQgYWxvbmU/IEJheWUncyBydWxlIChvciB0aGVvcmVtKSwgc29tZXRoaW5nIHlvdSBtYXkgaGF2ZSBoZWFyZCAKYWJvdXQgaW4gdGhpcyBhZ2Ugb2YgZXhwbG9kaW5nIGRhdGEgYW5hbHl0aWNzLCB0ZWxscyB1cyB0aGF0IHdlIGNhbiBiZSBzbyAKanVzdGlmaWVkIGFzIGxvbmcgYXMgd2UgYXNzaWduIGEgcHJvYmFiaWxpdHkgKG9yIGRlZ3JlZSBvZiBiZWxpZWYpIHRvIG91ciAKaW5mZXJlbmNlLiBUaGUgZm9sbG93aW5nIGRpc2N1c3Npb24gcHJvdmlkZXMgYW4gaW50ZXJlc3Rpbmcgd2F5IG9mIHVuZGVyc3RhbmRpbmcKdGhlIHByb2Nlc3MgZm9yIGRvaW5nIHRoaXMuIE1vcmUgaW1wb3J0YW50bHksIEkgcHJlc2VudCBob3cgQmF5ZSdzIHRoZW9yZW0gaGVscHMKdXMgb3ZlcmNvbWUgYSBjb21tb24gdGhpbmtpbmcgZmFpbHVyZSBhc3NvY2lhdGVkIHdpdGggbWFraW5nIGluZmVyZW5jZXMgZnJvbSBhbiAKaW5jb21wbGV0ZSB0cmVhdG1lbnQgb2YgYWxsIHRoZSBpbmZvcm1hdGlvbiB3ZSBzaG91bGQgdXNlLiBJJ2xsIHVzZSBhIGJpdCBvZiBhIApmYW5jaWZ1bCBleGFtcGxlIHRvIGNvbnZleSB0aGlzIHVuZGVyc3RhbmRpbmcgYWxvbmcgd2l0aCBzaG93aW5nIHRoZSBhc3NvY2lhdGVkIApjYWxjdWxhdGlvbnMgaW4gdGhlIFIgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UuCgpgYGB7fQpOb3RlOiBJZiB5b3UgZG9uJ3QgY2FyZSBhYm91dCB0aGUgUiBjb2RlIGF0IGFsbCBhbmQganVzdCB3YW50IHRvIGZvbGxvdyB0aGUKYmFzaWMgcmVhc29uaW5nLCB5b3UgY2FuIGhpZGUgYWxsIHRoZSBjb2RlIGF0IG9uY2UgYnkgc2VsZWN0aW5nIENvZGUvSGlkZSBBbGwKQ29kZSBmcm9tIHRoZSBkcm9wIGRvd24gbGlzdCBpbiB0aGUgdG9wIHJpZ2h0IGNvcm5lciBvZiB0aGlzIHBhZ2UuIElmIHlvdSB3YW50CnRvIHBsYXkgYXJvdW5kIHdpdGggdGhlIFIgY29kZSB5b3Vyc2VsZiwgeW91IGNhbiBkb3dubG9hZCB0aGUgY29kZSBieSBzZWxlY3RpbmcKQ29kZS9Eb3dubG9hZCBSbWQuCmBgYAoKU3VwcG9zZSB3ZSBhcmUgYWxpZW5zIGZyb20gYW5vdGhlciBwbGFuZXQgY29uZHVjdGluZyBzY2llbnRpZmljIHJlc2VhcmNoIG9uIHRoaXMKc3RyYW5nZSBncm91cCBvZiBiaXBlZGFsIG9yZ2FuaXNtcyBjYWxsZWQgaHVtYW5zLiBIdW1hbnMgYXJlIHNleHVhbGx5IGRpbW9ycGhpYywKcHJlc2VudGluZyB0aGVtc2VsdmVzIGFzIGZlbWFsZSBhbmQgbWFsZSBnZW5kZXJzLiBFYWNoIGdlbmRlciBleHByZXNzZXMgaXRzIApoZWlnaHQgYWNyb3NzIGEgZGlzdHJpYnV0aW9uLCBhbmQgdGhlIG1lYW5zIG9mIGVhY2ggZ2VuZGVyIHBvcHVsYXRpb24gYXJlIApkaXN0aW5jdC4gWWVhcnMgb2YgY29sbGVjdGluZyBkYXRhIGFib3V0IHRoZSBodW1hbnMgcmV2ZWFscyB0aGF0IHRoZSBtZWFuIGhlaWdodApmb3IgYWR1bHQgZmVtYWxlcyBpcyA1LjMzIGJsaWtzOyBtYWxlcywgNS44MyBibGlrcy4gKCJCbGlrcyIgYXJlIG91ciB1bml0IApkaXN0YW5jZSBtZWFzdXJlbWVudCwgbGlrZSBmZWV0IG9yIG1ldGVycy4pIEJvdGggZ2VuZGVycyBzaG93IGEgc2ltaWxhciAKYmVsbC1zaGFwZWQgZGlzdHJpYnV0ZWQgKGkuZS4sIE5vcm1hbCBkaXN0cmlidXRpb24pIHZhcmlhdGlvbiBhcm91bmQgdGhlaXIgbWVhbiAKaGVpZ2h0cyB3aXRoIGEgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIDAuMi4KCmBgYHtyfQpnZW5kZXIgPC0gYygiRmVtYWxlIiwgIk1hbGUiKQphZHVsdC5tIDwtIGMoNS4zMywgNS44MykgIyBNZWFuIGFkdWx0IGhlaWdodCBieSBnZW5kZXIuCmFkdWx0LnNkIDwtIDAuMiAjIEFkdWx0IGhlaWdodCBzdGFuZGFyZCBkZXZpYXRpb24uIENvbnN0YW50IGFjcm9zcyBnZW5kZXIuCmBgYAoKQW5vdGhlciBwaWVjZSBvZiBpbmZvcm1hdGlvbiB0aGF0IHdlIGhhdmUgY29sbGVjdGVkIGFib3V0IHRoZSBodW1hbiBwb3B1bGF0aW9uIAppcyB0aGUgYmFzZSByYXRlIGZyZXF1ZW5jeSwgb3IgdGhlIHByb3BvcnRpb24sIG9mIGVhY2ggc3VicG9wdWxhdGlvbiBpbiB0aGUgCnRvdGFsIHBvcHVsYXRpb24uIFRoZXNlIHByb3BvcnRpb25zIHByb3ZpZGUgc29tZSB2YWx1YWJsZSBpbmZvcm1hdGlvbiwgCnBhcnRpY3VsYXJseSBpZiB0aGUgcHJvcG9ydGlvbiBvZiBvbmUgc3VicG9wdWxhdGlvbiBpcyBpcyBsYXJnZXIgdGhhbiB0aGUgCm90aGVycy4gRm9yIGV4YW1wbGUsIGFtb25nIHRoZSBaYXJnb25zLCBjaGFydHJldXNlIGdpbGxzIHNob3cgdXAgaW4gODUlIG9mIHRoZSAKcG9wdWxhdGlvbiBjb21wYXJlZCB0byAxNSUgZm9yIHRob3NlIHdpdGggbWFnZW50YSBnaWxscy4gSWYgd2UgcmFuZG9tbHkgc2VsZWN0IGEKbWVtYmVyIG9mIHRoZSBaYXJnb24gcG9wdWxhdGlvbiBmb3Igb3VyIHpvbyBleGhpYml0LCB3ZSBtb3N0IGxpa2VseSB3aWxsIHNlbGVjdCAKb25lIHdpdGggY2hhcnRyZXVzZSBnaWxscy4gSW4gdGhlIGxhbmd1YWdlIG9mIEJheWVzaWFuIHJlYXNvbmluZywgd2UgY2FuIGNhbGwgCnRoaXMgYmFzZSByYXRlIHRoZSBwcmlvciBwcm9iYWJpbGl0eSAob3IgZGVncmVlIG9mIGJlbGllZikgdGhhdCBhIHJhbmRvbWx5IApzZWxlY3RlZCBpbmRpdmlkdWFsIGZyb20gdGhlIHRvdGFsIHBvcHVsYXRpb24gc2hvdWxkIGJlIGNsYXNzaWZpZWQgYXMgYSBtZW1iZXIgCm9mIG9uZSBjYXRlZ29yeSBvciBhbm90aGVyLiBJbiB0aGUgZ2VuZXJpYyBub3RhdGlvbiwgdGhpcyB3b3VsZCBiZQpQcm9iKEh5cG90aGVzaXMpLCBidXQgaW4gdGhlIGNvbnRleHQgb2Ygb3VyIGN1cnJlbnQgc2l0dWF0aW9uLCB3ZSBtaWdodCB3cml0ZQpQcm9iKEdlbmRlcikuIEhvbGQgb24gdG8gdGhpcyBjb25jZXB0LCBhcyB3ZSB3aWxsIHJldHVybiB0byBpdCBpbiBqdXN0IHR3bwpzaGFrZXMgb2YgYSBwcm90b24gZGlzbG9jYXRvci4KClVuZm9ydHVuYXRlbHksIGEgcmVjZW50IHZpcmFsIGVwaWRlbWljIG5lYXJseSB3aXBlZCBvdXQgdGhlIHBvcHVsYXRpb24gb2YgbWFsZXMsIApsZWF2aW5nIHRoZSBmZW1hbGVzIG92ZXItcmVwcmVzZW50ZWQgYXMgOTAlIG9mIHRoZSBwb3B1bGF0aW9uIGFzIGNvbXBhcmVkIHRvIHRoZQpoaXN0b3JpY2FsbHkgYXBwcm94aW1hdGVseSBldmVuIGRpc3RyaWJ1dGlvbiBvZiBmZW1hbGVzIHRvIG1hbGVzLgoKYGBge3J9CiMgU2V0IHVwIGEgdmVjdG9yIHRvIGNvbnRhaW4gdGhlIGN1cnJlbnQgUHJvYihHZW5kZXIpLgpiYXNlLnJhdGUuZnJlcS5mZW0gPC0gMC45ICMgVGhlIGZlbWFsZSBiYXNlIHJhdGUgZnJlcXVlbmN5LgpiYXNlLnJhdGUuZnJlcSA8LSBjKGJhc2UucmF0ZS5mcmVxLmZlbSwgMSAtIGJhc2UucmF0ZS5mcmVxLmZlbSkgIyAoZmVtYWxlcywgbWFsZXMpCmBgYAoKSW1hZ2luZSB0aGF0IG9uIGFuIGV4cGxvcmF0b3J5IG1pc3Npb24gdG8gRWFydGgsIHdlIGRpc2NvdmVyIGFuIGluanVyZWQgaHVtYW4gdG8Kd2hvbSB3ZSBpbnRlbmQgdG8gYXBwbHkgbWVkaWNhbCBhdHRlbnRpb24gYmVmb3JlIHdlIHNoaXAgaXQgb2ZmIHRvIG91ciBnYWxhY3RpYyAKem9vOyBob3dldmVyLCBkdWUgdG8gaXRzIGluanVyaWVzIGFuZCBpdHMgZXh0cmVtZSBlbW90aW9uYWwgc3RhdGUsIHdlIGNhbid0IApkZXRlcm1pbmUgaXRzIGdlbmRlci4gVGhlIG9ubHkgZGF0YSB3ZSBjYW4gb2J0YWluIGlzIGl0cyBoZWlnaHQgKFllcywgd2UgY2FuCnRyYXZlbCBhY3Jvc3MgdmFzdCBkaXN0YW5jZXMgb2Ygc3BhY2UsIGJ1dCB3ZSBoYXZlIHRvIHVzZSB0aGlzIGNydWRlIG1lYW5zIHRvCm1ha2UgYSBtZWFzdXJlbWVudC4gQmVhciB3aXRoIG1lLiBUaGlzIGlzIGp1c3QgYW4gZXhhbXBsZSwgbm90IGEgc2NpLWZpIG5vdmVsLiksCndoaWNoIHdlIG9ic2VydmUgdG8gYmUgNS42NSBibGlrcy4gQmFzZWQgb24gdGhpcyBvbmUgc2FtcGxlIG9mIG1lYXN1cmVkCmluZm9ybWF0aW9uLCB3ZSBtdXN0IGluZmVyIHRoZSBnZW5kZXIgb2YgdGhlIHNwZWNpbWVuIGJlZm9yZSB3ZSBwcm9jZWVkLiBIb3cgY2FuCndlIHVzZSB0aGUgaW5mb3JtYXRpb24gd2UgaGF2ZSBvbiBoYW5kIHRvIG1ha2UgYSByYXRpb25hbCBpbmZlcmVuY2U/CgpgYGB7cn0KIyBUaGUgbWVhc3VyZWQgaGVpZ2h0IG9mIG91ciB1bmlkZW50aWZpZWQgc3BlY2ltZW4uCiMgdW5pdHMgPSBCbGlrcwpzcGVjaW1lbi5oZWlnaHQgPC0gNS42NQpgYGAKCkZpcnN0LCB3ZSBuZWVkIHRvIGRldGVybWluZSB0aGUgcHJvYmFibGUgaGVpZ2h0IG9mIGFueSBtZW1iZXIgb2YgYSBnZW5kZXIgYmFzZWQgCm9uIG91ciBjdXJyZW50IGNoYXJhY3Rlcml6YXRpb24gb2YgdGhlIGdlbmRlciBwb3B1bGF0aW9ucy4gQWdhaW4sIGluIHRoZSAKbGFuZ3VhZ2Ugb2YgQmF5ZXNpYW4gcmVhc29uaW5nLCB3ZSBjYWxsIHRoaXMgdGhlIGxpa2VsaWhvb2QgZnVuY3Rpb24sIHdoaWNoIAp0ZWxscyB1cyB0aGUgcHJvYmFiaWxpdHkgb2Ygb2JzZXJ2aW5nIHNvbWUga2luZCBvZiBldmlkZW5jZSwgbGlrZSBhIG1lYXN1cmVtZW50LAp0aGF0IGlzIGNvbmRpdGlvbmFsIG9uIHRoZSBzZWxlY3Rpb24gb2YgYSBnaXZlbiBwb3B1bGF0aW9uIG9yIGNvbmRpdGlvbmFsIG9uIGEgCmdpdmVuIGh5cG90aGVzaXMgYmVpbmcgdGhlIGNhc2UuIFdlIHVzdWFsbHkgd3JpdGUgdGhpcyBhcyAKUHJvYihFdmlkZW5jZXxIeXBvdGhlc2lzKS4gSW4gdGhlIGNvbnRleHQgb2Ygb3VyIHByb2JsZW0sIHdlIG1pZ2h0IHdyaXRlIApQcm9iKEhlaWdodHxHZW5kZXIpLgoKVXNpbmcgdGhlIGBgYGRub3JtKC4uLilgYGAgZnVuY3Rpb24sIHdlIGNhbiBjYWxjdWxhdGUgdGhlIHByb2JhYmlsaXR5CmRpc3RyaWJ1dGlvbiBhY3Jvc3MgdGhlIGhlaWdodCBkb21haW4gZm9yIGVhY2ggZ2VuZGVyLiBOb3RlIHRoYXQgdGhlCmBgYGRub3JtKC4uLilgYGAgZnVuY3Rpb24gcHJvZHVjZXMgdGhlIHByb2JhYmlsaXR5IGRlbnNpdHkgYXQgYSBnaXZlbiBwb2ludC4KCmBgYHtyfQojIEhlcmUgd2Ugc2V0IHVwIGFuIGluZGV4IGZvciB0aGUgaGVpZ2h0IGRvbWFpbiwgd2l0aCBhIHN0ZXAgc2l6ZSBvZgojIDAuMDQgYmxpa3MuCnN0ZXAuc2l6ZSA8LSAuMDQKaGVpZ2h0IDwtIHNlcSg0LCA3LCBieSA9IHN0ZXAuc2l6ZSkKCiMgQ2FsY3VsYXRlIHRoZSBoZWlnaHQgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uIGZvciBlYWNoIGdlbmRlciBhY3Jvc3MgdGhlCiMgaGVpZ2h0IGRvbWFpbi4gSW4gdGhpcyBjYXNlLCB0aGUgcmVzdWx0IHdpbGwgYmUgYW4gYXJyYXkgb2Ygc2hhcGUgKDIgeAojIGhlaWdodCkuIFdlIGFwcGx5IHRoZSB0cmFuc3Bvc2UgZnVudGlvbiB0byB0aGlzIGFycmF5IHRvIHJlb3JpZW50IGl0IHRvCiMgKGhlaWdodCB4IDIpLgpsaWtlbGlob29kLmhlaWdodCA8LSB0KHNhcHBseShoZWlnaHQsIGZ1bmN0aW9uKGgpCiAgZG5vcm0oaCwgYWR1bHQubSwgYWR1bHQuc2QpKSkKICAgICAgICAgICAgICAgICAgICAgCiMgUmVjYXN0IHRoZSBhcnJheSBhcyBhIGRhdGEgZnJhbWUgaW4gcHJlcGFyYXRpb24gZm9yIHBsb3R0aW5nIGluIGdncGxvdC4KZGYubGlrZWxpaG9vZC5oZWlnaHQgPC0gZGF0YS5mcmFtZShoZWlnaHQsIGxpa2VsaWhvb2QuaGVpZ2h0KQpjb2xuYW1lcyhkZi5saWtlbGlob29kLmhlaWdodCkgPC0gYygiSGVpZ2h0IiwgZ2VuZGVyKQpgYGAKCklmIHdlIHBsb3QgdGhpcyBmdW5jdGlvbiwgd2Ugb2JzZXJ2ZSB0aGUgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uIGFjcm9zcyB0aGUKaGVpZ2h0IGRvbWFpbi4gQWdhaW4sIHdlIGFsc28gcmVmZXIgdG8gdGhpcyBhcyB0aGUgbGlrZWxpaG9vZCBmdW5jdGlvbi4KCmBgYHtyfQojIFJlY2FzdCB0aGUgcmVjdGFuZ3VsYXIgZGF0YSBmcmFtZSB0byBhIHJlbGF0aW9uYWwgZm9ybWF0IHN1Y2ggdGhhdCB0aGUgaGVpZ2h0CiMgaW5kZXggaXMgdGhlIGxlZnRtb3N0IHZhcmlhYmxlLgptZWx0LmRmLmxpa2VsaWhvb2QuaGVpZ2h0IDwtIG1lbHQoZGYubGlrZWxpaG9vZC5oZWlnaHQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWQudmFycyA9ICJIZWlnaHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLm5hbWUgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZS5uYW1lID0gIlByb2JhYmlsaXR5IikKCmhlaWdodC5kaXN0ciA8LSBnZ3Bsb3QobWVsdC5kZi5saWtlbGlob29kLmhlaWdodCwKICAgICAgICAgICAgICAgICAgICAgICBhZXMoCiAgICAgICAgICAgICAgICAgICAgICAgeCA9IEhlaWdodCwKICAgICAgICAgICAgICAgICAgICAgICB5ID0gUHJvYmFiaWxpdHksCiAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBHZW5kZXIsCiAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyID0gR2VuZGVyCiAgICAgICAgICAgICAgICAgICAgICAgKSkgKwogIGdlb21fbGluZShhZXMoZ3JvdXAgPSBHZW5kZXIsIGNvbG91ciA9IEdlbmRlcikpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBzcGVjaW1lbi5oZWlnaHQpICsKICB4bGFiKCJIZWlnaHQgW2JsaWtzXSIpICsKICB5bGFiKCJQcm9iYWJpbGl0eSBEZW5zaXR5IikgKwogIGdndGl0bGUoIkhlaWdodCBEaXN0cmlidXRpb24gb2YgSHVtYW4gQWR1bHRzIiwgc3VidGl0bGUgPSAiUHJvYihIZWlnaHR8R2VuZGVyKSIpCnByaW50KGhlaWdodC5kaXN0cikKYGBgCgpJdCdzIHRlbXB0aW5nIHRvIGxvb2sgYXQgdGhlc2UgZGlzdHJpYnV0aW9ucyBhbmQgY29uY2x1ZGUgdGhhdCB0aGUgcHJvcGVyIAppbmZlcmVuY2UgYWJvdXQgdGhlIGdlbmRlciBvZiBvdXIgc3BlY2ltZW4gd291bGQgYmUgdGhlIHBvcHVsYXRpb24gd2l0aCB0aGUgCmdyZWF0ZXN0IHByb2JhYmlsaXR5IGF0IHRoZSBzcGVjaW1lbidzIG1lYXN1cmVkIGhlaWdodC4gQWZ0ZXIgYWxsLCB0aGUgaGVpZ2h0IG9mCmFkdWx0IGh1bWFucyBpcyBvbmUgb2YgdGhlIG1vc3QgZWFzaWx5IG9ic2VydmFibGUgYW5kIGF2YWlsYWJsZSBhc3BlY3RzIG9mIHRoaXMgCnN0cmFuZ2Ugc3BlY2llcyB0aGF0IHRlbmRzIHRvIGNvdmVyIHVwIGFsbCB0aGUgZGlzdGluZ3Vpc2hpbmcgZGltb3JwaGljIApjaGFyYWN0ZXJpc3RpY3Mgd2l0aCBzb21ldGhpbmcgdGhleSBjYWxsICJjbG90aGVzLiIgQSBxdWljayByZWZlcmVuY2UgdG8gb3VyCmludHVpdGlvbiBtaWdodCB0ZWxsIHVzIHRoYXQgaGVpZ2h0IGlzIGEgZ29vZCBtZWFzdXJlIHRvIG1ha2UgYW4gaW5mZXJlbmNlIGFib3V0CmdlbmRlci4gSW4gdGhpcyBjYXNlLCB3ZSBtZWFzdXJlZCB0aGUgaGVpZ2h0IG9mIG91ciBzcGVjaW1lbiBhdCBgYGByIHNwZWNpbWVuLmhlaWdodGBgYApibGlrcyAodGhlIHZlcnRpY2FsIGJsYWNrIGxpbmUpLiBCYXNlZCBvbiB0aGlzIGhlaWdodCBjb21wYXJlZCB0byB0aGUgZGlzdHJpYnV0aW9ucywgd2UgbWlnaHQgaW5mZXIgdGhhdCB0aGUgc3BlY2ltZW4gaXMgbWFsZSBzaW5jZSB0aGUgbGlrZWxpaG9vZCBhdCBgYGByIHNwZWNpbWVuLmhlaWdodGBgYApibGlrcyBpcyBoaWdoZXIgZm9yIG1hbGVzIHRoYW4gZmVtYWxlcy4KClVuZm9ydHVuYXRlbHksIHRoaXMgYXBwcm9hY2ggbGVhdmVzIG91dCB0aGUgaW5mb3JtYXRpb24gd2UgaGF2ZSBhYm91dCB0aGUgCmN1cnJlbnQgcHJldmFsZW5jZSAob3IgdGhlIGJhc2UgcmF0ZSBmcmVxdWVuY3kgb3IgcHJpb3IgZGVncmVlIG9mIGJlbGllZikgb2YgdGhlCmdlbmRlcnMgaW4gdGhlIHBvcHVsYXRpb24uIFJlbWVtYmVyIHRoZSBleGFtcGxlIG9mIHRoZSBaYXJnb25zPyBXZSB0eXBpY2FsbHkKY291bGQgaW5mZXIgdGhlIHN1YnBvcHVsYXRpb24gYmFzZWQgb24gdGhlIG9ic2VydmF0aW9uIG9mIGEgZG9taW5hbnRseSBleHByZXNzZWQKY2hhcmFjdGVyaXN0aWMuIEJ1dCBpbiB0aGlzIGNhc2UsIHdlIGhhdmUgdHdvIGNoYXJhY3RlcmlzdGNzIHdlIGNvdWxkIGNvbnNpZGVyLAphbmQgdGhleSBzZWVtIHRvIHdvcmsgYWdhaW5zdCBlYWNoIG90aGVyOyB0aGF0IGlzLCBhIHJhbmRvbWx5IHNlbGVjdGVkIGFkdWx0Cmh1bWFuIGFmdGVyIHRoZSB2aXJhbCBwb3N0LWFwb2NhbHlwc2UgaXMgbm93IG1vc3RseSBsaWtlbHkgZmVtYWxlLCBidXQgdGhlCmhlaWdodCBjaGFyYWN0ZXJpc3RpYyBpbmZvcm1hdGlvbiB3ZSBoYXZlIHNlZW1zIHRvIGxlYWQgdXMgdG8gaW5mZXIgdGhhdCB3ZSBoYXZlCmEgbWFsZS4gQ2FuIHdlIGNvbWJpbmUgYm90aCBwaWVjZXMgb2YgaW5mb3JtYXRpb24gdG8gYWRqdXN0IHRoZSBzdHJlbmd0aCBvZiBvdXIKaW5jbGluYXRpb24gaW4gYSByZWFzb25hYmxlIG1hbm5lcj8gRm9ydHVuYXRlbHksIGFsbCB0aGF0IHdlIGhhdmUgdG8gZG8gdG8KaW5jbHVkZSBib3RoIHBpZWNlcyBvZiBpbmZvcm1hdGlvbiBpbiBvdXIganVkZ21lbnQgaXMgdG8gd2VpZ2h0IHRoZSBsaWtlbGlob29kCm9mIGhlaWdodCBmb3IgYSBnaXZlbiBwb3B1bGF0aW9uIHByb3BvcnRpb25hbGx5IHRvIHRoZSBwcmV2YWxlbmNlIG9mIHRoZQpwb3B1bGF0aW9uLiBXZSBjYW4gYWNoaWV2ZSB0aGlzIGJ5IG11bHRpcGx5aW5nIHRoZSBsaWtsaWhvb2QgZnVuY3Rpb24gYnkgdGhlIApiYXNlIHJhdGUgcHJvYmFiaWxpdHkuCgpgYGB7cn0KbGlrZWxpaG9vZC5oZWlnaHQuZ2VuZGVyIDwtIHQoYmFzZS5yYXRlLmZyZXEgKiB0KGxpa2VsaWhvb2QuaGVpZ2h0KSkKCiMgUmVjYXN0IHRoZSBhcnJheSBhcyBhIGRhdGEgZnJhbWUgaW4gcHJlcGFyYXRpb24gZm9yIHBsb3R0aW5nIGluIGdncGxvdC4KZGYubGlrZWxpaG9vZC5oZWlnaHQuZ2VuZGVyIDwtIGRhdGEuZnJhbWUoaGVpZ2h0LCBsaWtlbGlob29kLmhlaWdodC5nZW5kZXIpCmNvbG5hbWVzKGRmLmxpa2VsaWhvb2QuaGVpZ2h0LmdlbmRlcikgPC0gYygiSGVpZ2h0IiwgZ2VuZGVyKQpgYGAKCmBgYHtyfQojIFJlY2FzdCB0aGUgcmVjdGFuZ3VsYXIgZGF0YSBmcmFtZSB0byBhIHJlbGF0aW9uYWwgZm9ybWF0IHN1Y2ggdGhhdCB0aGUgaGVpZ2h0CiMgaW5kZXggaXMgdGhlIGxlZnRtb3N0IHZhcmlhYmxlLgptZWx0LmRmLmxpa2VsaWhvb2QuaGVpZ2h0LmdlbmRlciA8LSBtZWx0KGRmLmxpa2VsaWhvb2QuaGVpZ2h0LmdlbmRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZC52YXJzID0gIkhlaWdodCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUubmFtZSA9ICJHZW5kZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlLm5hbWUgPSAiUHJvYmFiaWxpdHkiKQoKaGVpZ2h0LmRpc3RyLmdlbmRlciA8LSBnZ3Bsb3QobWVsdC5kZi5saWtlbGlob29kLmhlaWdodC5nZW5kZXIsCiAgICAgICAgICAgICAgICAgICAgICAgYWVzKAogICAgICAgICAgICAgICAgICAgICAgIHggPSBIZWlnaHQsCiAgICAgICAgICAgICAgICAgICAgICAgeSA9IFByb2JhYmlsaXR5LAogICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gR2VuZGVyLAogICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IEdlbmRlcgogICAgICAgICAgICAgICAgICAgICAgICkpICsKICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gR2VuZGVyLCBjb2xvdXIgPSBHZW5kZXIpKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gc3BlY2ltZW4uaGVpZ2h0KSArCiAgeGxhYigiSGVpZ2h0IFtibGlrc10iKSArCiAgeWxhYigiUHJvYmFiaWxpdHkgRGVuc2l0eSIpICsKICBnZ3RpdGxlKCJQcm9iYWJpbGl0eS1XZWlnaHRlZCBIZWlnaHQgRGlzdHJpYnV0aW9uIG9mIEh1bWFuIEFkdWx0cyIsIHN1YnRpdGxlID0gIlByb2IoSGVpZ2h0fEdlbmRlcikgKiBQcm9iKEdlbmRlcikiKQpwcmludChoZWlnaHQuZGlzdHIuZ2VuZGVyKQpgYGAKCk5vdyB3ZSBvYnNlcnZlIHRoYXQgdGhlIHByb2JhYmlsaXR5IHdlaWdodGVkIGhlaWdodCBkaXN0cmlidXRpb25zIG1vc3QgbGlrZWx5IApvdWdodCB0byBjaGFuZ2Ugb3VyIGluZmVyZW5jZSBhYm91dCB0aGUgZ2VuZGVyIG9mIG91ciBzcGVjaW1lbi4gQWxsIHdlIG5lZWQgdG8gCmRvIG5vdyBpcyBmaW5kIHRoZSBjb25kaXRpb25hbCBsaWtlbGlob29kcyBhdCB0aGUgbWVhc3VyZWQgaGVpZ2h0IGFuZCBub3JtYWxpemUgCnRoZW0gd2l0aCB0aGUgbWFyZ2luYWwgcHJvYmFiaWxpdHkgYXQgdGhhdCBoZWlnaHQsIHdoZXJlIHRoZSBtYXJnaW5hbCBwcm9iYWJpbGl0eSBpcyBqdXN0IHRoZSBzdW0gb2YgdGhlIGNvbmRpdGlvbmFsIGxpa2VsaWhvb2RzIGFjcm9zcyBnZW5kZXJzIGF0IHRoZSBtZWFzdXJlZCBoZWlnaHQuCgpgYGB7cn0KY29uZGl0aW9uYWwubGlrZWxpaG9vZCA8LQogIGJhc2UucmF0ZS5mcmVxICogZG5vcm0oc3BlY2ltZW4uaGVpZ2h0LCBhZHVsdC5tLCBhZHVsdC5zZCkKCiMgY2FzdCBpbnRvIGFuIGFycmF5Lgpjb25kaXRpb25hbC5saWtlbGlob29kIDwtIGFycmF5KGNvbmRpdGlvbmFsLmxpa2VsaWhvb2QsIGRpbSA9IGMoMSwgMikpCmNvbG5hbWVzKGNvbmRpdGlvbmFsLmxpa2VsaWhvb2QpIDwtIGdlbmRlcgoKYGBgCgpUaGUgY29uZGl0aW9uYWwgbGlrZWxpaG9vZHMgZm9yIGVhY2ggZ2VuZGVyIGF0IHRoZSBtZWFzdXJlZCBoZWlnaHQgaXM6CgpgYGB7ciwgZWNobz1GQUxTRX0KcHJpbnQocm91bmQoY29uZGl0aW9uYWwubGlrZWxpaG9vZFsxLCBdLCAyKSkKYGBgCgpgYGB7cn0KIyBUaGUgbWFyZ2luYWwgcHJvYmFiaWxpdHkgaXMgdGhlIHRvdGFsIHByb2JhYmlsaXR5IG9mIHRoZSBwcm9kdWN0IG9mIHRoZSBwcmlvcgojIGFuZCB0aGUgbGlrZWxpaG9vZCBhdCB0aGUgc3BlY2lmaWVkIG1lYXN1cmVkIG9ic2VydmF0aW9uLgptYXJnaW5hbC5wcm9iIDwtIHN1bShjb25kaXRpb25hbC5saWtlbGlob29kKQpgYGAKClRoZSBtYXJnaW5hbCBwcm9iYWJpbGl0eSwgd2hpY2ggaXMganVzdCB0aGUgc3VtIG9mIHRoZSB0d28gcHJvYmFiaWxpdGllcyBpbiBvdXIgY29uZGl0aW9uYWwgbGlrZWxpaG9vZCBpczoKCmBgYHtyLCBlY2hvPUZBTFNFfQpwcmludChyb3VuZChtYXJnaW5hbC5wcm9iLCAyKSkKYGBgCgpOb3cgd2UgZGl2aWRlIHRoZSBjb25kaXRpb25hbCBsaWtlbGlob29kIHZhbHVlcyBieSB0aGUgbWFyZ2luYWwgcHJvYmFiaWxpdHkgdmFsdWUuCgpgYGB7cn0KIyBUaGUgcHJvYmFiaWxpdHkgKHJhdGlvbmFsIGluZmVyZW5jZSkgb2YgZ2VuZGVyIGFzc29jaWF0ZWQgd2l0aCBzYW1wbGVkIGhlaWdodC4KcG9zdGVyaW9yLnByb2IuZ2VuZGVyIDwtIGNvbmRpdGlvbmFsLmxpa2VsaWhvb2QgLyBtYXJnaW5hbC5wcm9iCmBgYAoKV2Ugc2hvdWxkIGFjY2VwdCBub3cgYXMgb3VyIHVwZGF0ZWQgYmVsaWVmIChpLmUuLCBwb3N0ZXJpb3IgcHJvYmFiaWxpdHkpIGFib3V0IHRoZSBnZW5kZXIgb2Ygb3VyIHNwZWNpbWVuIHRvIGJlOgoKYGBge3IsIGVjaG89RkFMU0V9CnByaW50KHJvdW5kKHBvc3Rlcmlvci5wcm9iLmdlbmRlclsxLCBdLCAyKSkKYGBgCgpPdXIgZmluYWwgb2JzZXJ2YXRpb24gaXMgdGhhdCBgYGByIGdlbmRlclt3aGljaC5tYXgocG9zdGVyaW9yLnByb2IuZ2VuZGVyKV1gYGAgCnNob3VsZCBiZSBvdXIgcmF0aW9uYWwgaW5mZXJlbmNlIGZvciB0aGUgZ2VuZGVyIG9mIG91ciBzcGVjaW1lbiBiYXNlZCBvbiB0aGUgCnByaW9yIHByb2JhYmlsaXR5IHdlaWdodGVkIGxpa2VsaWhvb2Qgb2YgaGVpZ2h0IGFzIGEgZnVuY3Rpb24gb2YgZ2VuZGVyLgoKSW4gc2hvcnQsIHdoYXQgd2UgY2FsY3VsYXRlZCB3YXMKYGBgcHJvYihHZW5kZXJ8SGVpZ2h0KSA9IHByb2IoR2VuZGVyKSAqIHByb2IoSGVpZ2h0fEdlbmRlcikvKG1hcmdpbmFsIHByb2JhYmlsaXR5KWBgYAp3aGVyZSB0aGUgbWFyZ2luYWwgcHJvYmFiaWxpdHkgaXMKYGBgc3VtKHByb2IoR2VuZGVyKSAqIHByb2IoSGVpZ2h0fEdlbmRlcikpYGBgClRoZW4gd2Ugc2VsZWN0ZWQgdGhlIGdlbmRlciBhcmd1bWVudCB3aXRoIHRoZSBoaWdoZXN0IHByb2JhYmlsaXR5LgoKVGhlIHRlcm0KYGBgc3VtKHByb2IoR2VuZGVyKSAqIHByb2IoSGVpZ2h0fEdlbmRlcikpYGBgCmlzIGEgbm9ybWFsaXppbmcgZmFjdG9yLiBPbmNlIHRoZSBxdW90aWVudCBoYXMgYmVlbiBmb3VuZCwgdGhlIHN1bSBvZgpwcm9iYWJpbGl0aWVzIHNob3VsZCBlcXVhbCB0byAxLgoKV2l0aCByZWdhcmQgdG8gb3VyIGV4YW1wbGUsIHRoaXMgZXhlcmNpc2UgaGFzIHNob3duIHVzIHRoYXQgdGFsbCBmZW1hbGVzIGFtb25nIAp0aGUgcG9wdWxhdGlvbiBvZiBhbGwgYWR1bHQgZmVtYWxlcyBhcmUgc29tZXdoYXQgcmFyZS4gSG93ZXZlciwgdGFsbCBmZW1hbGVzIAphbW9uZyB0aGUgZW50aXJlIHBvc3QtYXBvY2FseXB0aWMgcG9wdWxhdGlvbiBvZiBhZHVsdCBodW1hbnMgKGluIHdoaWNoIGZlbWFsZXMKYXJlIHRoZSBtb3N0IHByZXZhbGVudCBzdWJwb3B1bGF0aW9uKSBhcmUgbW9yZSBwcmV2YWxlbnQgdGhhbiBqdXN0LWJlbG93LWF2ZXJhZ2UKaGVpZ2h0IG1hbGVzLCBldmVuIHRob3VnaCBzdWNoIG1hbGVzIHdvdWxkIGJlIG1vcmUgcHJldmFsZW50IHRoYW4gdGFsbCBmZW1hbGVzCmlmIHRoZSB0aGUgc3VicG9wdWxhdGlvbnMgd2VyZSBtb3JlIGVxdWFsIGluIHNpemUuIFRoZXJlZm9yZSwgZmluZGluZyBhIHRhbGwKaW5kaXZpZHVhbCBzaG91bGQgbGVhZCB1cyB0byBpbmZlciwgZ2l2ZW4gbm8gb3RoZXIgaW5mb3JtYXRpb24gYWJvdXQgdGhlbSwgdGhhdAp0aGUgaW5kaXZpZHVhbCBpcyBtb3N0IGxpa2VseSBmZW1hbGUuIENvbWJpbmluZyBib3RoIHBpZWNlcyBvZiBpbmZvcm1hdGlvbgphbGxvd3MgdXMgdG8gdGVtcGVyIG91ciBpbnR1aXRpb24gdGhhdCB3b3VsZCBoYXZlIGJlZW4gYmFzZWQgb24gb25seSBvbmUgb2YgdGhlCmF2YWlsYWJsZSBwaWVjZXMgb2YgaW5mb3JtYXRpb24uCgpBbiBpbnRlcmVzdGluZyBxdWVzdGlvbiB0aGF0IG1pZ2h0IGFyaXNlIGZyb20gdGhlc2UgaW5zaWdodHMgaXM6IHdoYXQgaXMgdGhlCmJhc2UgcmF0ZSBmcmVxdWVuY3kgb2YgdGhlIHBvcHVsYXRpb25zIG9mIGZlbWFsZXMgYW5kIG1hbGVzIHRoYXQgbWlnaHQgaW5zdGlsbApjb21wbGV0ZSBhbWJpZ3VpdHkgaW4gdXMgYWJvdXQgdGhlIGdlbmRlciBvZiB0aGUgc3BlY2ltZW4gYmFzZWQgb24gdGhlIG1lYXN1cmVkCmhlaWdodCBhbmQgdGhlIGxpa2VsaWhvb2QgaGVpZ2h0IGZ1bmN0aW9ucyBvZiBlYWNoIGdlbmRlcj8gVGhlIGFuc3dlciBpcyBzaW1wbGUuCkFsbCB3ZSBoYXZlIHRvIGRvIGlzIHJlY29nbml6ZSB0aGF0CgpgYGB7fQpiYXNlLnJhdGUuZnJlcS5mZW0gKiBGZW1hbGVfTGlrZWxpaG9vZHxzcGVjaW1lbi5oZWlnaHQgPSAoMSAtIGJhc2UucmF0ZS5mcmVxLmZlbSkgKiBNYWxlX0xpa2VsaWhvb2R8c3BlY2ltZW4uaGVpZ2h0CmBgYAoKYW5kIHRoZW4gc29sdmUgZm9yIGBgYGJhc2UucmF0ZS5mcmVxLmZlbWBgYC4gTGV0J3MgY2FsbCB0aGlzIHNwZWNpYWwgYmFzZSByYXRlCmZyZXF1ZW5jeSBgYGBpbmRpZi5iYXNlLnJhdGUuZnJlcS5mZW1gYGAuIFJlYXJyYW5naW5nIHdlIGdldAoKYGBge30KTGlrZWxpaG9vZC5yYXRpbyA9IE1hbGVfTGlrZWxpaG9vZHxzcGVjaW1lbi5oZWlnaHQgLyBGZW1hbGVfTGlrZWxpaG9vZHxzcGVjaW1lbi5oZWlnaHQKaW5kaWYuYmFzZS5yYXRlLmZyZXEuZmVtID0gTGlrZWxpaG9vZC5yYXRpbyAvICgxICsgTGlrZWxpaG9vZC5yYXRpbykKYGBgCgpgYGB7cn0KIyByZWZvcm0gZXhhbXBsZSBlcXVhdGlvbnMgaW50byBSIHN0YXRlbWVudHMKbGlrZWxpaG9vZC5yYXRpbyA8LSBkbm9ybShzcGVjaW1lbi5oZWlnaHQsIGFkdWx0Lm1bMl0sIGFkdWx0LnNkKSAvIGRub3JtKHNwZWNpbWVuLmhlaWdodCwgYWR1bHQubVsxXSwgYWR1bHQuc2QpCmluZGlmLmJhc2UucmF0ZS5mcmVxLmZlbSA8LSBsaWtlbGlob29kLnJhdGlvIC8gKDEgKyBsaWtlbGlob29kLnJhdGlvKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQojIGNhc3QgaW50byBhbiBhcnJheS4KaW5kaWYuYmFzZS5yYXRlLmZyZXEgPC0gYXJyYXkoYyhpbmRpZi5iYXNlLnJhdGUuZnJlcS5mZW0sIDEgLSBpbmRpZi5iYXNlLnJhdGUuZnJlcS5mZW0pLCBkaW0gPSBjKDEsIDIpKQpjb2xuYW1lcyhpbmRpZi5iYXNlLnJhdGUuZnJlcSkgPC0gZ2VuZGVyCgpwcmludChyb3VuZChpbmRpZi5iYXNlLnJhdGUuZnJlcVsxLCBdLCAyKSkKYGBgClRoZXJlZm9yZSwgaWYgd2UgZmluZCBvdXJzZWx2ZXMgaW4gYSBzaXR1YXRpb24gaW4gd2hpY2ggdGhlIHByb3BvcnRpb24gb2YKZmVtYWxlcyBhbmQgbWFsZXMgbWF0Y2hlcyB0aG9zZSBpbiBvdXIgaW5kaWZmZXJlbmNlIGNhbGN1bGF0aW9uIGFib3ZlLCB3ZSByZWFsbHkKd29uJ3Qga25vdyBob3cgd2hpY2ggZ2VuZGVyIHdvdWxkIG1vc3QgbGlrZWx5IGJlIGluZGljYXRlZCBmcm9tIG91ciBoZWlnaHQKbWVhc3VyZW1lbnQgYWxvbmUuIE1vcmUgaW52YXNpdmUgdGVzdHMgbWlnaHQgYmUgcmVxdWlyZWQuIDpPCgpJbiBnZW5lcmFsLCB3aGF0IHRoaXMgZXhhbXBsZSBzaG93cyB1cyBpcyB0aGF0IGNvbWJpbmluZyBiYWNrZ3JvdW5kIGluZm9ybWF0aW9uIApjYW4gcGxheSBhIHNpZ25pZmljYW50IHJvbGUgaW4gaGVscGluZyB1cyBtYWtlIGxlc3MgYmlhc2VkIGp1ZGdtZW50cy4gSW5zdGVhZCBvZgp0aGlua2luZyBhYm91dCBqdXN0IHRoZSBkaXN0cmlidXRpb24gb2YgYSBjaGFyYWN0ZXJpc3RpYyB3aXRoaW4gYSBzdWJwb3B1bGF0aW9uIAp3aXRoIHdoaWNoIHdlIGFyZSBtb3N0IGZhbWlsaWFyLCB3ZSBuZWVkIGFsc28gdG8gdGhpbmsgYWJvdXQgdGhlIGRpc3RyaWJ1dGlvbiBvZgp0aGUgc3VicG9wdWxhdGlvbnMgdGhhdCBleGhpYml0IHRoZSBjaGFyYWN0ZXJpc3RpYyB3ZSBtaWdodCBjaG9vc2UgdG8gbWVhc3VyZSBpbgpvcmRlciB0byBtYWtlIGFuIGluZmVyZW5jZSBhYm91dCB3aGljaCBzdWJwb3B1bGF0aW9uIGEgc3BlY2ltZW4gbWlnaHQgYmVsb25nLiAKTm90IHRha2luZyB0aGUgZnVsbCBjb250ZXh0IGludG8gYWNjb3VudCBvZiBhIHNpdHVhdGlvbiBpcyB3aGF0IG9mdGVuIGxlYWRzIHVzIAp0byBtYWtlIGJpZ290ZWQgc29jaWFsbHkgdG94aWMganVkZ21lbnRzLCBlbnRlcnRhaW4gc3VwZXJzdGl0aW9ucywgb3IgYWxsb2NhdGUKbWFya2V0aW5nIGFuZCBzYWxlcyByZXNvdXJjZXMgdG8gbWFya2V0cyB0aGF0IGFyZSBsZWFzdCByZWNlcHRpdmUgdG8gb3VyIHByb2R1Y3QKbWVzc2FnaW5nLgoKSW4gdGhlIG5leHQgZmV3IGluc3RhbGxtZW50cyBvbiB0aGlzIGRpc2N1c3Npb24sIEkgcGxhbiB0byBkaXZlIGEgbGl0dGxlIG1vcmUgCmRlZXBseSBpbiB0byB0aGUgQmF5ZXNpYW4gcmVhc29uaW5nIHByb2Nlc3Mgd2l0aCBpbmNyZWFzaW5nbHkgbW9yZSBjb21wbGV4IApleGFtcGxlcyB0aGF0IHdpbGwgcmVtYWluIHN0aWxsIHJhdGhlciBzaW1wbGUgdG8gaW1wbGVtZW50IGluIFIgY29kZS4gSWYgeW91CmRvbid0IGNvbWUgZm9yIHRoZSBjb2RlLCBJIGRvIGhvcGUgeW91IGVudGVydGFpbiB0aGUgdGhpbmtpbmcgcHJvY2VzcyB0aGF0IGNhbgpoZWxwIGltcHJvdmUgZXhlY3V0aXZlIGp1ZGdtZW50IGFuZCBkZWNpc2lvbiBtYWtpbmcuCgpCYWNrIHRvIFtJbmNpdGUhIERlY2lzaW9uIFRlY2hub2xvZ2llc10oaHR0cDovL3d3dy5pbmNpdGVkZWNpc2lvbnRlY2guY29tKS4K