1. What is Zero Inflation?

In a wide variety of applications, we find data that has an excess of zeros. For example, consider the example from McElreath where monks working in a monastary to copy pages either 1) get drunk and don’t do anything, so they produce 0s or 2) work, and either produces 0, 1, 2, 3, or more manuscripts In such a dataset, you would expect an excess of zeros due to a structural property of the data generating process - namely that some zeroes are due to a probability of no pages being written, in this case. Let’s see how he simulates this.

library(rethinking)

# define parameters
prob_drink <- 0.2 # 20% of days
rate_work <- 1    # average 1 manuscript per day
# sample one year of production
N <- 365

# simulate days monks drink
set.seed(365)
drink <- rbinom( N , 1 , prob_drink )

# simulate manuscripts completed
y <- (1-drink)*rpois( N , rate_work )

Notice in the last piece that we FIRST get a random binomial of work or no work, THEN use a poisson to get the number of pages generated. Let’s plot this and see the different sources of zeroes.

monks <- data.frame(drink = drink, manuscripts = y)

ggplot(monks, aes(x = manuscripts, fill = factor(drink))) +
  geom_bar()

So, there are more zeroes than we’d expect from a poisson with a lambda of 1, AND we see that this is again a mixture distribution - a compounding of a binomial and a poisson.

There are many types of zero inflated - or zero augmented - distributions out there. Think of any distribution, but then assume some process is adding more zeroes! We’ll talk about a few, but you can dream up just about anything you’d like. Commonly you’ll hear about ZIPs, ZIBs, ZINBs, or even ZINs. We also run into Zero Augmented - or Hurdle - models like ZAGs, ZALNs (zero augmented log-normal distributions), and more.

1.1 Why are we accounting for zero inflation?

Quite simply, not accounting for an excess of zeros will inevitably downward bias your parameter estimates. I leave this to the reader to contemplate why.

1.2 Zero Inflation versus Hurdle Models

Zero Inflation and Zero Augmentation, or Hurdle, distributions mean two different things. The difference comes from two types of zeroes.

Structural Zeros come from some probabilistic process that prevents an outcome. The monks got drunk, so they produced no manuscripts! Or, you didn’t see that hippo because it was underwater when you were doing your surveys! Or, the cost of fish was too low, so fishermen just didn’t go to see to catch anything, and so had zero catch! There are a lot of possible causes of structural zeroes - and they can be modeled.

Sampling Zeros come from the data generating process that is generating your observations. So, a monk just didn’t finish any manuscripts that day. Or habitat was of poor quality, so you didn’t count any hippos. Or waves were too high, and fishermen didn’t catch anything.

Consider the example in the figure below of going to observe rhinos in the wild. If you count 0 because, perhaps they are there but you fail to detect them, that is a structural zero. You have the probability, p, of a structural zero. If your observation would have detected rhinos if they were there, but there just happen to be none, that’s a sampling zero.

Zero Inflated models comprise of a mix of structural and sampling issues. Hurdle models are ones where you only have structural zeros. We often find these cropping up with distributions which cannot have zeros in them, for example.

Let’s look at how we fit a few different zero augmented models to get a feel for them

2. The Zero Inflated Poisson

As we have the monk data at hand already, we can use it as a handy starting point for ZIP models.

2.1 ZIP with Likelihood

We can fit a ZIP model using a variety of packages in R. For example,

library(pscl)

monk_pscl <- zeroinfl(manuscripts ~ 1, data = monks)

summary(monk_pscl)
## 
## Call:
## zeroinfl(formula = manuscripts ~ 1, data = monks)
## 
## Pearson residuals:
##     Min      1Q  Median      3Q     Max 
## -0.7923 -0.7923 -0.7923  0.2332  3.3096 
## 
## Count model coefficients (poisson with log link):
##             Estimate Std. Error z value Pr(>|z|)
## (Intercept)  0.00346    0.09197   0.038     0.97
## 
## Zero-inflation model coefficients (binomial with logit link):
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  -1.2079     0.3291  -3.671 0.000242 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 
## 
## Number of iterations in BFGS optimization: 10 
## Log-likelihood: -435.6 on 2 Df

Here you can explicitly see that we’re estimating a binomial model with a logit link for the zero inflatino part. And as it’s a logit coefficient, we can see that the probability of no work is 0.2300728 By putting a | after the intercept, we could model the zero inflation process itself with another equation.

See also how the answer differs from glm

coef(glm(manuscripts ~ 1, data = monks, family = poisson))
## (Intercept) 
##  -0.2579903

Other packages also implement zero inflation. I inclde pscl as it wsa the most common one for some time. More recently, glmmTMB has implemented a pretty robust and flexible zero inflation and hurdle schema which I rather like.

library(glmmTMB)

monk_tmb <- glmmTMB(manuscripts ~ 1,
                    ziformula = ~ 1,
                    family = poisson,
                    data = monks)

summary(monk_tmb)
##  Family: poisson  ( log )
## Formula:          manuscripts ~ 1
## Zero inflation:               ~1
## Data: monks
## 
##      AIC      BIC   logLik deviance df.resid 
##    875.3    883.1   -435.6    871.3      363 
## 
## 
## Conditional model:
##             Estimate Std. Error z value Pr(>|z|)
## (Intercept)  0.00346    0.09197   0.038     0.97
## 
## Zero-inflation model:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  -1.2079     0.3291  -3.671 0.000242 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
fixef(monk_tmb)[2]$zi |> inv_logit()
## (Intercept) 
##   0.2300658

The results are the same, but, you can include whatever family you’d like - just include some zero inflation!

2.2 ZIP and Bayes

Rethinking provides some comfort here. It provides a dzipois density which takes a probability of a zero and a lambda.The trickier part is that we now have two relationships - one for the logit link of the drink/no drink and a second log link for manuscripts.

zip_mod <- alist(
  
  #likelihood
  manuscripts ~ dzipois(p, lambda),
  
  #data generating process
  logit(p) ~ a,
  log(lambda) ~ b,
  
  #priors
  a ~ dnorm(-1,2),
  b ~ dnorm(0,5)
)

zip_fit <- quap(zip_mod, data = monks)

This model produces very similar output with differences only due to priors

precis(zip_fit)
##           mean         sd       5.5%      94.5%
## a -1.202498558 0.32260061 -1.7180766 -0.6869205
## b  0.004533354 0.09118098 -0.1411915  0.1502582

Feel free to look at postcheck or other diagnostics here to see if we’re ok. And, note, when the two parts of this relationship become trickier, you will need to resort to ulam().

3. ZIB

Yes. It’s true. A binomial process can be due to structural and sampling zeros. It’s two coupled binomial models.

Let’s look at the fishing in state parks dataset from rethinking. We’ll apply two quick transformations with the idea of, we want to see if you’re more likely to catch a fish if you’re hanging out with more people or if having kid’s with you just throws the whole day to hell.

data(Fish)

Fish <- Fish %>%
  mutate(caught_anything = as.numeric(fish_caught>0),
         with_kids = as.factor(child>0))

FishPlot <- ggplot(Fish,
       aes(x = persons, y = caught_anything, color = with_kids)) +
  geom_jitter(position = position_jitter(width = 0.3, height = 0.1))

FishPlot

We can see a lot of zeros when people come with kids!

3.1 ZIBs and Likelihood

zib_tmb <- glmmTMB(caught_anything ~ persons,
                   ziformula = ~ with_kids+0,
                   family = binomial,
                   data = Fish)

What’s the chance you won’t catch fish if you bring kids?

inv_logit(fixef(zib_tmb)$zi[2])
## with_kidsTRUE 
##     0.6712779

Whoah. That’s big.

3.1 ZIBs and Bayes

We can take the model above and easily recode it into rethinking using flat priors using the dzibinom density, which will take two different logistics.

mod_zib <- alist(
  #Likelihood
  caught_anything ~ dzibinom(p_zero, size, prob_caught),
  
  #DGP
  logit(prob_caught) <- a + b*persons,
 logit(p_zero) <- b_kids[with_kids],
  
  #priors
  a ~ dnorm(0,10),

  b ~ dnorm(0,10),
  b_kids[with_kids] ~ dnorm(0,5)
)

fit_zib <- quap(mod_zib, 
               data = Fish |> 
                 mutate(size = 1, 
                        with_kids = as.numeric(with_kids)))

Which produces broadly similar results.

precis(fit_zib, depth = 2)
##                 mean        sd       5.5%      94.5%
## a         -1.8484417 0.4677124 -2.5959364 -1.1009470
## b          1.1989977 0.2637843  0.7774195  1.6205760
## b_kids[1] -4.1876389 2.1661546 -7.6495724 -0.7257055
## b_kids[2]  0.7256153 0.2354231  0.3493636  1.1018669

The only real difference is the probability of getting a 0 without kids. But if you inv_logit it, you can see it’s very small - as is the results from the glmmTMB fit above. And the zero inflation of having kids - 0.6738423 - is about the same. Ah, Kids.

3.1.1 ZIBs and Bayes

Given that the probability of catching a fish is the compounded probability of having kids and number of people, we can look at the resulting probabilities via simulation. I’m bumping the size up to 100 so we can get a more smooth estimate of probability at each data point from sim.

#input data frame
pred_df <- crossing(persons = seq(1,4,length.out=100), 
                    with_kids = c(1,2), 
                    size = 100)

#get predictions with error
pred_zib <- predicted_draws(fit_zib, pred_df)|>
  mutate(caught_anything = .prediction/100,
         with_kids = c(FALSE, TRUE)[with_kids])

#visualize
FishPlot +
   facet_wrap(~with_kids) +
  stat_lineribbon(data = pred_zib, 
                  color = "black", 
                  alpha = 0.2) +
  scale_fill_brewer(palette = "Blues")
## Warning: Combining variables of class <factor> and <logical> was deprecated in ggplot2
## 3.4.0.
## ℹ Please ensure your variables are compatible before plotting (location:
##   `combine_vars()`)
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: Combining variables of class <logical> and <factor> was deprecated in ggplot2
## 3.4.0.
## ℹ Please ensure your variables are compatible before plotting (location:
##   `combine_vars()`)
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

4. ZINB

Knowing that kids make things difficult (am I working out some issues here? Maybe I am!), we can notice that the dataset actually looks at the NUMBER of fish caught. Now, normally we’d look at this as a poisson (or a ZIP!) but, let’s look at the distribution of catch by number of people.

library(ggridges)
## 
## Attaching package: 'ggridges'
## The following objects are masked from 'package:tidybayes':
## 
##     scale_point_color_continuous, scale_point_color_discrete,
##     scale_point_colour_continuous, scale_point_colour_discrete,
##     scale_point_fill_continuous, scale_point_fill_discrete,
##     scale_point_size_continuous
ggplot(data = Fish, 
       mapping = aes(x = fish_caught, y = as.factor(persons), fill = as.factor(with_kids))) +
  geom_density_ridges(alpha = 0.7)
## Picking joint bandwidth of 2.2

Looking at this, we can see that the data is not only clearly 0 inflated, but, wow, that’s a long tail. A poisson is going to be a bad choice. Instead, a negative binomial is going to be the way to go.

The ZINB model is a fairly common one for this type of situation. Rather than belabor the point, here’s a glmmTMB example with a ZINB. Note that I’m using nbinom1 here. This is a great way to model overdispersion, as instead of the variance expanding with the square of the mean, it expands linearly with the mean. Again, a modeling choice!

zinb_tmb <- glmmTMB(fish_caught ~ persons,
                   ziformula = ~ with_kids+0,
                   family = nbinom1,
                   data = Fish)

And you can then work from there. What do you see here?

summary(zinb_tmb)
##  Family: nbinom1  ( log )
## Formula:          fish_caught ~ persons
## Zero inflation:               ~with_kids + 0
## Data: Fish
## 
##      AIC      BIC   logLik deviance df.resid 
##    877.5    895.1   -433.7    867.5      245 
## 
## 
## Dispersion parameter for nbinom1 family (): 10.8 
## 
## Conditional model:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept) -0.07753    0.28516  -0.272    0.786    
## persons      0.61363    0.08719   7.037 1.96e-12 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Zero-inflation model:
##                 Estimate Std. Error z value Pr(>|z|)  
## with_kidsFALSE  -20.1677  4277.7629  -0.005   0.9962  
## with_kidsTRUE     0.5694     0.2430   2.343   0.0191 *
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

5. Gone Fishing - Gama Hurdles

We can also use continuous data. This is most common with Gamma distributed data, as you cannot have a waiting time of 0. Let’s use a simulated data set looking at duration of offshore fishing trips in days given average wave height.

fishing <- read.csv("https://biol609.github.io/lectures/data/20/fishing_duration.csv")

#what's there
head(fishing)
##   days_at_sea    seas_m non_zero
## 1  2.04671364 3.1117604        1
## 2  1.07712028 2.8300817        1
## 3  0.14453844 1.7110089        1
## 4  0.00000000 2.3203441        0
## 5  0.05439326 2.5521608        1
## 6  0.00000000 0.9862452        0
#visualize the data
plot(days_at_sea ~ seas_m, data=fishing)

5.1. Exploratory Analysis Decisions

OK, let’s assume you have no a priori knowledge about this data save the variables in it. You are interested in just a bivariate relationship. Period.

Now, in building a model for this, you have to decide on three things.

1. What is the data generating process?
2. What is the error generating process - the distribution of the variation around predicted values?
3. As we’re using Bayes, do we have information that can allow us to provide weakly informative priors?

5.2 The data generating process

On the data generating process, we can look at the data and see, hey, we have a negative relationship, and its one that cannot be less than 0. This suggests a power or exponential relationship. An exponential (log link function) seems pretty reasonable.

5.3 The error generating process

This one is harder. We have made some decisions that are useful. We have no non-negative values. We have continuous data. So, no normal distribution. And no Poisson or other count distribution. But what are we left with. Log-normal? Gamma? Erlang? Weibull? Others (oh, and there are others).

We have a few options - we can plot histograms, plot densities at each predictor value (or bin of predictor), we can fit a gaussian model, know that it’s wrong but look at its residual distribution, we can stare at our scatterplot and ponder.

What we’re pondering is histomancy - the mystic ability to divine distributions from a histogram or density plot.

It is a terrible idea.

Instead, let’s actually use science and some thought. In this case, we’re looking at a distribution of durations (days at sea). There’s some underlying thing that determines the duration that someone stays at sea. This fits naturally to a Gamma distribution. Others could work here, too, but, a Gamma is also maximum entropy distribution for data from 0 to infinity where the variance increases with the mean. And a member of the exponential family, so fairly easy to work with. So, yes, you could choose others, but for biological plausibility, and ease of use, it’s an excellent first choice.

5.3.1 The Gamma

As a quick reminder of what the gamma is, it’s a distribution that can be defined with either a shape parameter and a rate of events happening (1/time to event - in this case, the decision to port) or the same shape parameter and a scale parameter which is 1/rate (so, time for 1 event to happen). This can lead to a little bit of confusion, as you’ll see it presented either way. Personally, I like rate, but either will work

So, below, as we increase the rate, we have more and more short wait times for 1 event.

library(tidyr)
library(dplyr)
library(ggplot2)

gamma_frame <- crossing(rate = seq(0.1, 2, length.out=5), 
                        shape=1, 
                        x=seq(0.01, 5, length.out=200)) %>%
  mutate(d = dgamma(x, shape = shape, rate = rate))

ggplot(gamma_frame, aes(x=x, y=d, color=factor(rate))) +
  geom_line()

To make things a bit easier, the mean of a gamma is mu = shape * scale. This leads to a few relationships/transformations which can become useful.

shape = mu/scale
rate = shape/mu
scale = mu/shape

It’s a bit of a pain, as depending on how you put things together, you can get slightly different results from models. But it should not matter to qualitative outcomes.

5.4 Priors

In this case, we don’t really have any prior information! So we want flat priors. We can use regularization - say, b ~ dnorm(0,3), but how much depends on you. We’re also going to want priors for scale or rate parameters. These can be normal, but often cauchy (for scale) or exponential (for rate) priors work well. YMMV.

5.5. Fitting with a Gamma

5.5.1 The model

So, let’s start with dgamma. By default, dgamma takes first a shape and then a rate argument. This presents a small challenge, as above we have shape = rate/mu. This means we would need to work with our mean value a bit. Rather than a straight log(mu) statement, we’re going to need to use the calculated value. Let’s put together a model.

library(rethinking)

fishing_gamma_mod <- alist(
  #likelihood
  #noting that rate = shape/exp(log_mu)
  days_at_sea ~ dgamma(shape, shape/exp(log_mu)),
  
  #data generating process
  log_mu <- a + b*seas_m,
  
  #priors
  a ~ dnorm(0,100),
  b ~ dnorm(0,100),
  shape ~ dexp(2)
)

McElreath has also put together a shortcut, dgamma2, but it tends to produce slightly different results, as it calculates shape as a function of the predictors. So I’m not a yuge fan.

fishing_gamma_mod2 <- alist(
  #likelihood
  days_at_sea ~ dgamma2(mu, scale),
  
  #data generating process
  log(mu) <- a + b*seas_m,

  #priors
  a ~ dnorm(0,100),
  b ~ dnorm(0,100),
  scale ~ dcauchy(0,2)
)

5.5.2 The fit

OK, let’s fit this thing using the first model!

fishing_gamma_fit <- map(fishing_gamma_mod, data=fishing)
## Error in map(fishing_gamma_mod, data = fishing): initial value in 'vmmin' is not finite
## The start values for the parameters were invalid. This could be caused by missing values (NA) in the data or by start values outside the parameter constraints. If there are no NA values in the data, try using explicit start values.

Uh oh! What’s wrong?

and now, we encounter mistake #1. Remember that we’re using a log link? Well, log(0) is not a number, so, of course our model fails! So we need to drop those 0s. They mean that a fisherman didn’t leave port at all, and are likely best conisdered as a separate process. Regardless, even if we want to fold them back in (see below), we would be greatly helped by building a model that does not require handling those details.

fishing_clean <- fishing %>% filter(days_at_sea != 0)

set.seed(609)
fishing_gamma_fit <- map(fishing_gamma_mod, data=fishing_clean)
## Error in map(fishing_gamma_mod, data = fishing_clean): non-finite finite-difference value [3]
## Start values for parameters may be too far from MAP.
## Try better priors or use explicit start values.
## If you sampled random start values, just trying again may work.
## Start values used in this attempt:
## a = -180.807609298684
## b = 29.0262239923922
## shape = 0.110016871757038

Oops! That didn’t work either! Well that’s not good. So, what does the error say? That start values were invalid. OK.

Welp, eyeballing the graph of the data, we see a positive intercept and negative slope. So, even with the same seed (other seed values allow for convergence), let’s re-try!

set.seed(609)
fishing_gamma_fit <- map(fishing_gamma_mod, data=fishing_clean,
                         start = list(a=1, b=-1))

Fit!

5.5.3 Model evaluation

Let’s check out our posterior!

pairs(fishing_gamma_fit)
## Warning in par(usr): argument 1 does not name a graphical parameter

## Warning in par(usr): argument 1 does not name a graphical parameter

## Warning in par(usr): argument 1 does not name a graphical parameter

Hey! Not bad. Indeed, I wouldn’t even go to STAN with a fit like this.

par(mfrow=c(2,3))
postcheck(fishing_gamma_fit)

par(mfrow=c(1,1), ask=F)

Looking pretty good! there are one or two points outside of the predictive CI, but, eh, that’s not bad considering the large number of data points we have. Worth investigating, but not losing too much sleep over.

5.5.4 What do the results say?

We can first look at the general results

precis(fishing_gamma_fit)
##             mean         sd       5.5%      94.5%
## a      2.2107901 0.30827198  1.7181119  2.7034682
## b     -0.6341226 0.11152433 -0.8123600 -0.4558852
## shape  0.4629509 0.05212386  0.3796469  0.5462549

So, a decline of -0.63, or, for every 1m wave, fishermen would spend 53% less time at sea (exp(-0.63)). We can’t say absolute days here, as this is a nonlinear function - what happens in absolute units going from 2 to 3 is different from 3 to 4. But we can judge it on a percentage scale. Try calculating it out if you don’t believe it.

We can also visualize the results

#start with a fresh data frame of predicted values
pred_df <- data.frame(seas_m = seq(0,5,length.out=200))

#OK, now get some sim and link output
sim_fishing <- sim(fishing_gamma_fit, data=pred_df)

#note, for link, we need to exponentiate
link_fishing <- exp(link(fishing_gamma_fit, data=pred_df))

#smoosh it all together
pred_df <- pred_df %>%
  mutate(days_at_sea = apply(link_fishing, 2, median),
         lwr_fit = apply(link_fishing, 2, HPDI)[1,],
         upr_fit = apply(link_fishing, 2, HPDI)[2,],
         lwr = apply(sim_fishing, 2, HPDI)[1,],
         upr = apply(sim_fishing, 2, HPDI)[2,]
         )

#a nice ggplot
ggplot(data=pred_df,
       mapping=aes(x=seas_m, y=days_at_sea)) +
  geom_ribbon(mapping=aes(ymin=lwr, ymax=upr), alpha=0.4) +
  geom_ribbon(mapping=aes(ymin=lwr_fit, ymax=upr_fit), fill="blue", alpha=0.4) +
  geom_line(lwd=1, color="red") +
  geom_point(data=fishing_clean) +
  theme_bw(base_size=17)

Not bad. We can see the results of a Gamma distribution - the mean variance scaling relationship. We can see we have some outliers to inspect, but, overall, pretty darned good.

6. About those Zeroes - Zero Augmented Gamma

6.1 Zero Inflation, augmentation, hurdles, and more

Excessive zeroes are not something to be scared of. Of which to be scared? Whatever, you guys know this on a deep level. Because a zero inflated or zero augmented distribution is nothing more than the lovechild of a binomial logistic regression and a GLM of your own choosing.

Let’s start by thinking about a Hurdle or Zero Augmented model. In these models, you cannot have any zeroes for reasons related to constrains of your data generating process or error generating process. Instead, Zeroes are generated by one data generating process, then another process determines the relationship with your response variable. So, you first split your model into one where you have either 0 or non-0 outcomes, and perform a logistic regression. With whatever predictors you want. Then, with all of the non-zero outcomes, you perform whatever GLM you want.

A Zero Inflated or Zero Augmented model is slightly different. Here, zeroes are generated either by some process that is binary - a thing happened or it didn’t - OR they are generated by whatever data generating process you are modeling.

Zero Inflated models come in many forms - Zero Inflated Poisson (uses dzipois(lambda, p) in rethinking), Zero Inflated Binomial (uses dzibinom(p_zero, size, p)), Zero Augmented Gamma (dzagamma2(prob, mu, scale)) and more which can be pulled from other libraries in R.

6.2 How do I know if it is zero inflated

There are two straightforward ways to see if something is zero inflated. First, what’s the distribution look like? Do you see a lot of zeros?

plot(density(fishing$days_at_sea))

OK - lots of zeroes - which, given that this is a Gamma, is super suspect. But, for this or other distributions, they might be OK. Sometimes you’ll even see two humps - one around zero, one somewhere else. Zero Inflation!

You can also look at a scatterplot and see if there are a suspicious number of zeroes across all of the values of a predictor variable. Sure, you might ahve a non-normal distribution, but that kind of pattern is a bit odd.

plot(days_at_sea ~ seas_m, data=fishing)

6.3 Using the ZAG

To work with our original data - zeroes and all - we can use a Zero Augmented Gamma Distribution. I’m still not a huge fan of dzagamma2 so let’s make our own using the rate formulation. To do this, I literally just entered dzamma2 in R, then rewrote the function a bit to use the rate formulation.

dzagamma <- function (x, prob, shape, rate, log = FALSE) 
{
  K <- as.data.frame(cbind(x = x, prob = prob, shape = shape, rate = rate))
  llg <- dgamma(x, shape = shape, rate = rate, log = TRUE)
  ll <- ifelse(K$x == 0, log(K$prob), log(1 - K$prob) + llg)
  if (log == FALSE) 
    ll <- exp(ll)
  ll
}

Let this be a less - you can put in whatever density function you want - if R doesn’t have it, write your own! Heck, this format is a really straightforward way to turn any distribution - normal, whatever - into a zero inflated distribution.

In this distribution, we’ll have a lot of extra 0s. What’s great is that we can appropriate our earlier model for use here.

fishing_zagamma_mod <- alist(
  #likelihood
  #noting that rate = shape/exp(log_mu)
  days_at_sea ~ dzagamma2(prob, mu, scale),
  
  #data generating process
  log(mu) <- a + b*seas_m,
  
  #priors
  a ~ dnorm(0,100),
  b ~ dnorm(0,100),
  scale ~ dexp(2),
  prob ~ dunif(0,1)
)

We have no info on the probability, so, we’re just putting in a flat uniform prior between 0 and 1. We could use a beta if we wanted, but, meh. We can then fit this, using our lessons about start values from before.

fishing_zagamma_fit <- quap(fishing_zagamma_mod, data=fishing,
                         start = list(a=1, b=-1))

6.4 Evaluation

Let’s look at those posteriors!

pairs(fishing_zagamma_fit)
## Warning in par(usr): argument 1 does not name a graphical parameter

## Warning in par(usr): argument 1 does not name a graphical parameter

## Warning in par(usr): argument 1 does not name a graphical parameter

## Warning in par(usr): argument 1 does not name a graphical parameter

The pairs plot looks nice and well behaved. Other checks and sim are a bit more difficult, as they require a random number generator which we currently… don’t have. So, let’s make one.

rzagamma2 <- function(n, prob, scale, rate){
  is_zero <- rbinom(n, 1, prob=1-prob)
  is_zero * rgamma(n, scale = scale, rate)
}

With this, we can use postcheck

par(mfrow=c(3,3))
postcheck(fishing_zagamma_fit)
par(mfrow=c(1,1), ask=F)

Hey - it’s all of the data! And it looks - good!

6.5 What do the results say?

We can first look at the general results

precis(fishing_zagamma_fit)
##             mean         sd       5.5%       94.5%
## a      1.1152591 0.19096871  0.8100543  1.42046401
## b     -0.1448523 0.06373614 -0.2467149 -0.04298964
## scale  4.7410432 0.67868115  3.6563796  5.82570675
## prob   0.2933332 0.03717393  0.2339221  0.35274431

No surprise, our coefficients are exactly the same as before! Wahoo! Except now we also know there’s a 30% chance on any given day that a fisherman will just say ‘meh’ and not go do sea.

We can also visualize the results. First, we will plot the prediction for JUST the non-zero results using the fitted model. Then we will contrast it to putting in the full 0’s prediction, which comes from the ZAG distribtion.

# a prediction frame
pred_zag <- predicted_draws(fishing_zagamma_fit,
                            data.frame(
                              seas_m = seq(0, 5,
                                           length.out = 100)),
                            value = "days_at_sea")


# a linpred frame
fit_zag <- linpred_draws(fishing_zagamma_fit,
                            data.frame(
                              seas_m = seq(0, 5,
                                           length.out = 100)),
                            value = "days_at_sea")

# fit interval of link - not with ZAG
ggplot(fit_zag,
       aes(x = seas_m, y = days_at_sea)) +
  stat_lineribbon() +
  scale_fill_brewer(palette = "Greens") +
  geom_point(data = fishing)

# prediction interval
ggplot(pred_zag,
       aes(x = seas_m, y = days_at_sea)) +
  stat_lineribbon() +
  scale_fill_brewer(palette = "Oranges") +
  geom_point(data = fishing)

Note the difference in CI width and downweighting of the line with just fit. There is a lot more going on here, and the full prediction interval gives us what we need to see.

LS0tCnRpdGxlOiAiWmVybyBJbmZsYXRpb24iCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShyZXRoaW5raW5nKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2xtbVRNQikKbGlicmFyeShwc2NsKQpsaWJyYXJ5KG1nY3YpCmxpYnJhcnkodGlkeWJheWVzKQpsaWJyYXJ5KHRpZHliYXllcy5yZXRoaW5raW5nKQp0aGVtZV9zZXQodGhlbWVfYncoYmFzZV9zaXplPTEyKSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCiMjIDEuIFdoYXQgaXMgWmVybyBJbmZsYXRpb24/CgpJbiBhIHdpZGUgdmFyaWV0eSBvZiBhcHBsaWNhdGlvbnMsIHdlIGZpbmQgZGF0YSB0aGF0IGhhcyBhbiBleGNlc3Mgb2YgemVyb3MuIEZvciBleGFtcGxlLCBjb25zaWRlciB0aGUgZXhhbXBsZSBmcm9tIE1jRWxyZWF0aCB3aGVyZSBtb25rcyB3b3JraW5nIGluIGEgbW9uYXN0YXJ5IHRvIGNvcHkgcGFnZXMgZWl0aGVyIDEpIGdldCBkcnVuayBhbmQgZG9uJ3QgZG8gYW55dGhpbmcsIHNvIHRoZXkgcHJvZHVjZSAwcyBvciAyKSB3b3JrLCBhbmQgZWl0aGVyIHByb2R1Y2VzIDAsIDEsIDIsIDMsIG9yIG1vcmUgbWFudXNjcmlwdHMgSW4gc3VjaCBhIGRhdGFzZXQsIHlvdSB3b3VsZCBleHBlY3QgYW4gZXhjZXNzIG9mIHplcm9zIGR1ZSB0byBhICpzdHJ1Y3R1cmFsKiBwcm9wZXJ0eSBvZiB0aGUgZGF0YSBnZW5lcmF0aW5nIHByb2Nlc3MgLSBuYW1lbHkgdGhhdCBzb21lIHplcm9lcyBhcmUgZHVlIHRvIGEgcHJvYmFiaWxpdHkgb2Ygbm8gcGFnZXMgYmVpbmcgd3JpdHRlbiwgaW4gdGhpcyBjYXNlLiBMZXQncyBzZWUgaG93IGhlIHNpbXVsYXRlcyB0aGlzLgoKYGBge3J9CmxpYnJhcnkocmV0aGlua2luZykKCiMgZGVmaW5lIHBhcmFtZXRlcnMKcHJvYl9kcmluayA8LSAwLjIgIyAyMCUgb2YgZGF5cwpyYXRlX3dvcmsgPC0gMSAgICAjIGF2ZXJhZ2UgMSBtYW51c2NyaXB0IHBlciBkYXkKIyBzYW1wbGUgb25lIHllYXIgb2YgcHJvZHVjdGlvbgpOIDwtIDM2NQoKIyBzaW11bGF0ZSBkYXlzIG1vbmtzIGRyaW5rCnNldC5zZWVkKDM2NSkKZHJpbmsgPC0gcmJpbm9tKCBOICwgMSAsIHByb2JfZHJpbmsgKQoKIyBzaW11bGF0ZSBtYW51c2NyaXB0cyBjb21wbGV0ZWQKeSA8LSAoMS1kcmluaykqcnBvaXMoIE4gLCByYXRlX3dvcmsgKQpgYGAKCgpOb3RpY2UgaW4gdGhlIGxhc3QgcGllY2UgdGhhdCB3ZSBGSVJTVCBnZXQgYSByYW5kb20gYmlub21pYWwgb2Ygd29yayBvciBubyB3b3JrLCBUSEVOIHVzZSBhIHBvaXNzb24gdG8gZ2V0IHRoZSBudW1iZXIgb2YgcGFnZXMgZ2VuZXJhdGVkLiBMZXQncyBwbG90IHRoaXMgYW5kIHNlZSB0aGUgZGlmZmVyZW50IHNvdXJjZXMgb2YgemVyb2VzLgoKYGBge3J9Cm1vbmtzIDwtIGRhdGEuZnJhbWUoZHJpbmsgPSBkcmluaywgbWFudXNjcmlwdHMgPSB5KQoKZ2dwbG90KG1vbmtzLCBhZXMoeCA9IG1hbnVzY3JpcHRzLCBmaWxsID0gZmFjdG9yKGRyaW5rKSkpICsKICBnZW9tX2JhcigpCmBgYAoKU28sIHRoZXJlIGFyZSBtb3JlIHplcm9lcyB0aGFuIHdlJ2QgZXhwZWN0IGZyb20gYSBwb2lzc29uIHdpdGggYSBsYW1iZGEgb2YgMSwgQU5EIHdlIHNlZSB0aGF0IHRoaXMgaXMgYWdhaW4gYSBtaXh0dXJlIGRpc3RyaWJ1dGlvbiAtIGEgY29tcG91bmRpbmcgb2YgYSBiaW5vbWlhbCBhbmQgYSBwb2lzc29uLgoKVGhlcmUgYXJlIG1hbnkgdHlwZXMgb2YgemVybyBpbmZsYXRlZCAtIG9yIHplcm8gYXVnbWVudGVkIC0gZGlzdHJpYnV0aW9ucyBvdXQgdGhlcmUuIFRoaW5rIG9mIGFueSBkaXN0cmlidXRpb24sIGJ1dCB0aGVuIGFzc3VtZSBzb21lIHByb2Nlc3MgaXMgYWRkaW5nIG1vcmUgemVyb2VzISBXZSdsbCB0YWxrIGFib3V0IGEgZmV3LCBidXQgeW91IGNhbiBkcmVhbSB1cCBqdXN0IGFib3V0IGFueXRoaW5nIHlvdSdkIGxpa2UuIENvbW1vbmx5IHlvdSdsbCBoZWFyIGFib3V0IFpJUHMsIFpJQnMsIFpJTkJzLCBvciBldmVuIFpJTnMuIFdlIGFsc28gcnVuIGludG8gWmVybyBBdWdtZW50ZWQgLSBvciBIdXJkbGUgLSBtb2RlbHMgbGlrZSBaQUdzLCBaQUxOcyAoemVybyBhdWdtZW50ZWQgbG9nLW5vcm1hbCBkaXN0cmlidXRpb25zKSwgYW5kIG1vcmUuCgoKCiMjIyAxLjEgV2h5IGFyZSB3ZSBhY2NvdW50aW5nIGZvciB6ZXJvIGluZmxhdGlvbj8KClF1aXRlIHNpbXBseSwgbm90IGFjY291bnRpbmcgZm9yIGFuIGV4Y2VzcyBvZiB6ZXJvcyB3aWxsIGluZXZpdGFibHkgZG93bndhcmQgYmlhcyB5b3VyIHBhcmFtZXRlciBlc3RpbWF0ZXMuIEkgbGVhdmUgdGhpcyB0byB0aGUgcmVhZGVyIHRvIGNvbnRlbXBsYXRlIHdoeS4gCgojIyMgMS4yIFplcm8gSW5mbGF0aW9uIHZlcnN1cyBIdXJkbGUgTW9kZWxzCgpaZXJvIEluZmxhdGlvbiBhbmQgWmVybyBBdWdtZW50YXRpb24sIG9yIEh1cmRsZSwgZGlzdHJpYnV0aW9ucyBtZWFuIHR3byBkaWZmZXJlbnQgdGhpbmdzLiBUaGUgZGlmZmVyZW5jZSBjb21lcyBmcm9tIHR3byB0eXBlcyBvZiB6ZXJvZXMuICAKXAoqKlN0cnVjdHVyYWwgWmVyb3MqKiBjb21lIGZyb20gc29tZSBwcm9iYWJpbGlzdGljIHByb2Nlc3MgdGhhdCBwcmV2ZW50cyBhbiBvdXRjb21lLiBUaGUgbW9ua3MgZ290IGRydW5rLCBzbyB0aGV5IHByb2R1Y2VkIG5vIG1hbnVzY3JpcHRzISBPciwgeW91IGRpZG4ndCBzZWUgdGhhdCBoaXBwbyBiZWNhdXNlIGl0IHdhcyB1bmRlcndhdGVyIHdoZW4geW91IHdlcmUgZG9pbmcgeW91ciBzdXJ2ZXlzISBPciwgdGhlIGNvc3Qgb2YgZmlzaCB3YXMgdG9vIGxvdywgc28gZmlzaGVybWVuIGp1c3QgZGlkbid0IGdvIHRvIHNlZSB0byBjYXRjaCBhbnl0aGluZywgYW5kIHNvIGhhZCB6ZXJvIGNhdGNoISBUaGVyZSBhcmUgYSBsb3Qgb2YgcG9zc2libGUgY2F1c2VzIG9mIHN0cnVjdHVyYWwgemVyb2VzIC0gYW5kIHRoZXkgY2FuIGJlIG1vZGVsZWQuICAKXAoqKlNhbXBsaW5nIFplcm9zKiogY29tZSBmcm9tIHRoZSBkYXRhIGdlbmVyYXRpbmcgcHJvY2VzcyB0aGF0IGlzIGdlbmVyYXRpbmcgeW91ciBvYnNlcnZhdGlvbnMuIFNvLCBhIG1vbmsganVzdCBkaWRuJ3QgZmluaXNoIGFueSBtYW51c2NyaXB0cyB0aGF0IGRheS4gT3IgaGFiaXRhdCB3YXMgb2YgcG9vciBxdWFsaXR5LCBzbyB5b3UgZGlkbid0IGNvdW50IGFueSBoaXBwb3MuIE9yIHdhdmVzIHdlcmUgdG9vIGhpZ2gsIGFuZCBmaXNoZXJtZW4gZGlkbid0IGNhdGNoIGFueXRoaW5nLiAgClwKCkNvbnNpZGVyIHRoZSBleGFtcGxlIGluIHRoZSBmaWd1cmUgYmVsb3cgb2YgZ29pbmcgdG8gb2JzZXJ2ZSByaGlub3MgaW4gdGhlIHdpbGQuIElmIHlvdSBjb3VudCAwIGJlY2F1c2UsIHBlcmhhcHMgdGhleSBhcmUgdGhlcmUgYnV0IHlvdSBmYWlsIHRvIGRldGVjdCB0aGVtLCB0aGF0IGlzIGEgKnN0cnVjdHVyYWwgemVybyouIFlvdSBoYXZlIHRoZSBwcm9iYWJpbGl0eSwgcCwgb2YgYSBzdHJ1Y3R1cmFsIHplcm8uIElmIHlvdXIgb2JzZXJ2YXRpb24gd291bGQgaGF2ZSBkZXRlY3RlZCByaGlub3MgaWYgdGhleSB3ZXJlIHRoZXJlLCBidXQgdGhlcmUganVzdCBoYXBwZW4gdG8gYmUgbm9uZSwgdGhhdCdzIGEgKipzYW1wbGluZyB6ZXJvKiouCgohW10oLi9pbWFnZXMvemluZmwvc3RydWN0dXJhbF9zYW1wbGluZ196ZXJvcy5qcGcpCgpaZXJvIEluZmxhdGVkIG1vZGVscyBjb21wcmlzZSBvZiBhIG1peCBvZiBzdHJ1Y3R1cmFsIGFuZCBzYW1wbGluZyBpc3N1ZXMuIEh1cmRsZSBtb2RlbHMgYXJlIG9uZXMgd2hlcmUgeW91ICpvbmx5IGhhdmUgc3RydWN0dXJhbCB6ZXJvcyouIFdlIG9mdGVuIGZpbmQgdGhlc2UgY3JvcHBpbmcgdXAgd2l0aCBkaXN0cmlidXRpb25zIHdoaWNoIGNhbm5vdCBoYXZlIHplcm9zIGluIHRoZW0sIGZvciBleGFtcGxlLgoKTGV0J3MgbG9vayBhdCBob3cgd2UgZml0IGEgZmV3IGRpZmZlcmVudCB6ZXJvIGF1Z21lbnRlZCBtb2RlbHMgdG8gZ2V0IGEgZmVlbCBmb3IgdGhlbQoKCiMjIDIuIFRoZSBaZXJvIEluZmxhdGVkIFBvaXNzb24KCkFzIHdlIGhhdmUgdGhlIG1vbmsgZGF0YSBhdCBoYW5kIGFscmVhZHksIHdlIGNhbiB1c2UgaXQgYXMgYSBoYW5keSBzdGFydGluZyBwb2ludCBmb3IgWklQIG1vZGVscy4KCiMjIyAyLjEgWklQIHdpdGggTGlrZWxpaG9vZAoKV2UgY2FuIGZpdCBhIFpJUCBtb2RlbCB1c2luZyBhIHZhcmlldHkgb2YgcGFja2FnZXMgaW4gUi4gRm9yIGV4YW1wbGUsCgpgYGB7ciB6aXAxfQpsaWJyYXJ5KHBzY2wpCgptb25rX3BzY2wgPC0gemVyb2luZmwobWFudXNjcmlwdHMgfiAxLCBkYXRhID0gbW9ua3MpCgpzdW1tYXJ5KG1vbmtfcHNjbCkKYGBgCgpIZXJlIHlvdSBjYW4gZXhwbGljaXRseSBzZWUgdGhhdCB3ZSdyZSBlc3RpbWF0aW5nIGEgYmlub21pYWwgbW9kZWwgd2l0aCBhIGxvZ2l0IGxpbmsgZm9yIHRoZSB6ZXJvIGluZmxhdGlubyBwYXJ0LiBBbmQgYXMgaXQncyBhIGxvZ2l0IGNvZWZmaWNpZW50LCB3ZSBjYW4gc2VlIHRoYXQgdGhlIHByb2JhYmlsaXR5IG9mIG5vIHdvcmsgaXMgYHIgaW52X2xvZ2l0KC0xLjIwNzkpYCBCeSBwdXR0aW5nIGEgYHxgIGFmdGVyIHRoZSBpbnRlcmNlcHQsIHdlIGNvdWxkIG1vZGVsIHRoZSB6ZXJvIGluZmxhdGlvbiBwcm9jZXNzIGl0c2VsZiB3aXRoIGFub3RoZXIgZXF1YXRpb24uCgpTZWUgYWxzbyBob3cgdGhlIGFuc3dlciBkaWZmZXJzIGZyb20gYGdsbWAKCmBgYHtyfQpjb2VmKGdsbShtYW51c2NyaXB0cyB+IDEsIGRhdGEgPSBtb25rcywgZmFtaWx5ID0gcG9pc3NvbikpCmBgYAoKT3RoZXIgcGFja2FnZXMgYWxzbyBpbXBsZW1lbnQgemVybyBpbmZsYXRpb24uIEkgaW5jbGRlIGBwc2NsYCBhcyBpdCB3c2EgdGhlIG1vc3QgY29tbW9uIG9uZSBmb3Igc29tZSB0aW1lLiBNb3JlIHJlY2VudGx5LCBgZ2xtbVRNQmAgaGFzIGltcGxlbWVudGVkIGEgcHJldHR5IHJvYnVzdCBhbmQgZmxleGlibGUgemVybyBpbmZsYXRpb24gYW5kIGh1cmRsZSBzY2hlbWEgd2hpY2ggSSByYXRoZXIgbGlrZS4KCmBgYHtyfQpsaWJyYXJ5KGdsbW1UTUIpCgptb25rX3RtYiA8LSBnbG1tVE1CKG1hbnVzY3JpcHRzIH4gMSwKICAgICAgICAgICAgICAgICAgICB6aWZvcm11bGEgPSB+IDEsCiAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gcG9pc3NvbiwKICAgICAgICAgICAgICAgICAgICBkYXRhID0gbW9ua3MpCgpzdW1tYXJ5KG1vbmtfdG1iKQoKZml4ZWYobW9ua190bWIpWzJdJHppIHw+IGludl9sb2dpdCgpCmBgYAoKVGhlIHJlc3VsdHMgYXJlIHRoZSBzYW1lLCBidXQsIHlvdSBjYW4gaW5jbHVkZSB3aGF0ZXZlciBmYW1pbHkgeW91J2QgbGlrZSAtIGp1c3QgaW5jbHVkZSBzb21lIHplcm8gaW5mbGF0aW9uISAKCiMjIyAyLjIgWklQIGFuZCBCYXllcwoKUmV0aGlua2luZyBwcm92aWRlcyBzb21lIGNvbWZvcnQgaGVyZS4gSXQgcHJvdmlkZXMgYSBgZHppcG9pc2AgZGVuc2l0eSB3aGljaCB0YWtlcyBhIHByb2JhYmlsaXR5IG9mIGEgemVybyBhbmQgYSBsYW1iZGEuVGhlIHRyaWNraWVyIHBhcnQgaXMgdGhhdCB3ZSBub3cgaGF2ZSB0d28gcmVsYXRpb25zaGlwcyAtIG9uZSBmb3IgdGhlIGxvZ2l0IGxpbmsgb2YgdGhlIGRyaW5rL25vIGRyaW5rIGFuZCBhIHNlY29uZCBsb2cgbGluayBmb3IgbWFudXNjcmlwdHMuCgoKYGBge3J9CnppcF9tb2QgPC0gYWxpc3QoCiAgCiAgI2xpa2VsaWhvb2QKICBtYW51c2NyaXB0cyB+IGR6aXBvaXMocCwgbGFtYmRhKSwKICAKICAjZGF0YSBnZW5lcmF0aW5nIHByb2Nlc3MKICBsb2dpdChwKSB+IGEsCiAgbG9nKGxhbWJkYSkgfiBiLAogIAogICNwcmlvcnMKICBhIH4gZG5vcm0oLTEsMiksCiAgYiB+IGRub3JtKDAsNSkKKQoKemlwX2ZpdCA8LSBxdWFwKHppcF9tb2QsIGRhdGEgPSBtb25rcykKYGBgCgpUaGlzIG1vZGVsIHByb2R1Y2VzIHZlcnkgc2ltaWxhciBvdXRwdXQgd2l0aCBkaWZmZXJlbmNlcyBvbmx5IGR1ZSB0byBwcmlvcnMKCmBgYHtyfQpwcmVjaXMoemlwX2ZpdCkKYGBgCgpGZWVsIGZyZWUgdG8gbG9vayBhdCBwb3N0Y2hlY2sgb3Igb3RoZXIgZGlhZ25vc3RpY3MgaGVyZSB0byBzZWUgaWYgd2UncmUgb2suIEFuZCwgbm90ZSwgd2hlbiB0aGUgdHdvIHBhcnRzIG9mIHRoaXMgcmVsYXRpb25zaGlwIGJlY29tZSB0cmlja2llciwgeW91IHdpbGwgbmVlZCB0byByZXNvcnQgdG8gYHVsYW0oKWAuCgojIyAzLiBaSUIKClllcy4gSXQncyB0cnVlLiBBIGJpbm9taWFsIHByb2Nlc3MgY2FuIGJlIGR1ZSB0byBzdHJ1Y3R1cmFsIGFuZCBzYW1wbGluZyB6ZXJvcy4gSXQncyB0d28gY291cGxlZCBiaW5vbWlhbCBtb2RlbHMuCgohW10oLi9pbWFnZXMvemluZmwveW9fZGF3Z196aWIuanBnKQoKTGV0J3MgbG9vayBhdCB0aGUgZmlzaGluZyBpbiBzdGF0ZSBwYXJrcyBkYXRhc2V0IGZyb20gcmV0aGlua2luZy4gV2UnbGwgYXBwbHkgdHdvIHF1aWNrIHRyYW5zZm9ybWF0aW9ucyB3aXRoIHRoZSBpZGVhIG9mLCB3ZSB3YW50IHRvIHNlZSBpZiB5b3UncmUgbW9yZSBsaWtlbHkgdG8gY2F0Y2ggYSBmaXNoIGlmIHlvdSdyZSBoYW5naW5nIG91dCB3aXRoIG1vcmUgcGVvcGxlIG9yIGlmIGhhdmluZyBraWQncyB3aXRoIHlvdSBqdXN0IHRocm93cyB0aGUgd2hvbGUgZGF5IHRvIGhlbGwuCgoKYGBge3J9CmRhdGEoRmlzaCkKCkZpc2ggPC0gRmlzaCAlPiUKICBtdXRhdGUoY2F1Z2h0X2FueXRoaW5nID0gYXMubnVtZXJpYyhmaXNoX2NhdWdodD4wKSwKICAgICAgICAgd2l0aF9raWRzID0gYXMuZmFjdG9yKGNoaWxkPjApKQoKRmlzaFBsb3QgPC0gZ2dwbG90KEZpc2gsCiAgICAgICBhZXMoeCA9IHBlcnNvbnMsIHkgPSBjYXVnaHRfYW55dGhpbmcsIGNvbG9yID0gd2l0aF9raWRzKSkgKwogIGdlb21faml0dGVyKHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gMC4zLCBoZWlnaHQgPSAwLjEpKQoKRmlzaFBsb3QKYGBgCgpXZSBjYW4gc2VlIGEgbG90IG9mIHplcm9zIHdoZW4gcGVvcGxlIGNvbWUgd2l0aCBraWRzISAKCiMjIDMuMSBaSUJzIGFuZCBMaWtlbGlob29kCgpgYGB7ciBnbG1tVE1CfQp6aWJfdG1iIDwtIGdsbW1UTUIoY2F1Z2h0X2FueXRoaW5nIH4gcGVyc29ucywKICAgICAgICAgICAgICAgICAgIHppZm9ybXVsYSA9IH4gd2l0aF9raWRzKzAsCiAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbCwKICAgICAgICAgICAgICAgICAgIGRhdGEgPSBGaXNoKQpgYGAKCldoYXQncyB0aGUgY2hhbmNlIHlvdSB3b24ndCBjYXRjaCBmaXNoIGlmIHlvdSBicmluZyBraWRzPwoKYGBge3J9Cmludl9sb2dpdChmaXhlZih6aWJfdG1iKSR6aVsyXSkKYGBgCgpXaG9haC4gVGhhdCdzIGJpZy4KCiMjIDMuMSBaSUJzIGFuZCBCYXllcwoKV2UgY2FuIHRha2UgdGhlIG1vZGVsIGFib3ZlIGFuZCBlYXNpbHkgcmVjb2RlIGl0IGludG8gcmV0aGlua2luZyB1c2luZyBmbGF0IHByaW9ycyB1c2luZyB0aGUgYGR6aWJpbm9tYCBkZW5zaXR5LCB3aGljaCB3aWxsIHRha2UgdHdvIGRpZmZlcmVudCBsb2dpc3RpY3MuCgpgYGB7cn0KbW9kX3ppYiA8LSBhbGlzdCgKICAjTGlrZWxpaG9vZAogIGNhdWdodF9hbnl0aGluZyB+IGR6aWJpbm9tKHBfemVybywgc2l6ZSwgcHJvYl9jYXVnaHQpLAogIAogICNER1AKICBsb2dpdChwcm9iX2NhdWdodCkgPC0gYSArIGIqcGVyc29ucywKIGxvZ2l0KHBfemVybykgPC0gYl9raWRzW3dpdGhfa2lkc10sCiAgCiAgI3ByaW9ycwogIGEgfiBkbm9ybSgwLDEwKSwKCiAgYiB+IGRub3JtKDAsMTApLAogIGJfa2lkc1t3aXRoX2tpZHNdIH4gZG5vcm0oMCw1KQopCgpmaXRfemliIDwtIHF1YXAobW9kX3ppYiwgCiAgICAgICAgICAgICAgIGRhdGEgPSBGaXNoIHw+IAogICAgICAgICAgICAgICAgIG11dGF0ZShzaXplID0gMSwgCiAgICAgICAgICAgICAgICAgICAgICAgIHdpdGhfa2lkcyA9IGFzLm51bWVyaWMod2l0aF9raWRzKSkpCmBgYAoKV2hpY2ggcHJvZHVjZXMgYnJvYWRseSBzaW1pbGFyIHJlc3VsdHMuCmBgYHtyfQpwcmVjaXMoZml0X3ppYiwgZGVwdGggPSAyKQpgYGAKClRoZSBvbmx5IHJlYWwgZGlmZmVyZW5jZSBpcyB0aGUgcHJvYmFiaWxpdHkgb2YgZ2V0dGluZyBhIDAgd2l0aG91dCBraWRzLiBCdXQgaWYgeW91IGBpbnZfbG9naXRgIGl0LCB5b3UgY2FuIHNlZSBpdCdzIHZlcnkgc21hbGwgLSBhcyBpcyB0aGUgcmVzdWx0cyBmcm9tIHRoZSBgZ2xtbVRNQmAgZml0IGFib3ZlLiBBbmQgdGhlIHplcm8gaW5mbGF0aW9uIG9mIGhhdmluZyBraWRzIC0gYHIgaW52X2xvZ2l0KGNvZWYoZml0X3ppYilbNF0pYCAtIGlzIGFib3V0IHRoZSBzYW1lLiBBaCwgS2lkcy4KCiMjIDMuMS4xIFpJQnMgYW5kIEJheWVzCgpHaXZlbiB0aGF0IHRoZSBwcm9iYWJpbGl0eSBvZiBjYXRjaGluZyBhIGZpc2ggaXMgdGhlIGNvbXBvdW5kZWQgcHJvYmFiaWxpdHkgb2YgaGF2aW5nIGtpZHMgYW5kIG51bWJlciBvZiBwZW9wbGUsIHdlIGNhbiBsb29rIGF0IHRoZSByZXN1bHRpbmcgcHJvYmFiaWxpdGllcyB2aWEgc2ltdWxhdGlvbi4gSSdtIGJ1bXBpbmcgdGhlIHNpemUgdXAgdG8gMTAwIHNvIHdlIGNhbiBnZXQgYSBtb3JlIHNtb290aCBlc3RpbWF0ZSBvZiBwcm9iYWJpbGl0eSBhdCBlYWNoIGRhdGEgcG9pbnQgZnJvbSBgc2ltYC4KCmBgYHtyIHppYl9iYXllc30KI2lucHV0IGRhdGEgZnJhbWUKcHJlZF9kZiA8LSBjcm9zc2luZyhwZXJzb25zID0gc2VxKDEsNCxsZW5ndGgub3V0PTEwMCksIAogICAgICAgICAgICAgICAgICAgIHdpdGhfa2lkcyA9IGMoMSwyKSwgCiAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEwMCkKCiNnZXQgcHJlZGljdGlvbnMgd2l0aCBlcnJvcgpwcmVkX3ppYiA8LSBwcmVkaWN0ZWRfZHJhd3MoZml0X3ppYiwgcHJlZF9kZil8PgogIG11dGF0ZShjYXVnaHRfYW55dGhpbmcgPSAucHJlZGljdGlvbi8xMDAsCiAgICAgICAgIHdpdGhfa2lkcyA9IGMoRkFMU0UsIFRSVUUpW3dpdGhfa2lkc10pCgojdmlzdWFsaXplCkZpc2hQbG90ICsKICAgZmFjZXRfd3JhcCh+d2l0aF9raWRzKSArCiAgc3RhdF9saW5lcmliYm9uKGRhdGEgPSBwcmVkX3ppYiwgCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgICAgICAgIGFscGhhID0gMC4yKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJCbHVlcyIpCiAKCgpgYGAKCiMjIDQuIFpJTkIKS25vd2luZyB0aGF0IGtpZHMgbWFrZSB0aGluZ3MgZGlmZmljdWx0IChhbSBJIHdvcmtpbmcgb3V0IHNvbWUgaXNzdWVzIGhlcmU/IE1heWJlIEkgYW0hKSwgd2UgY2FuIG5vdGljZSB0aGF0IHRoZSBkYXRhc2V0IGFjdHVhbGx5IGxvb2tzIGF0IHRoZSBOVU1CRVIgb2YgZmlzaCBjYXVnaHQuIE5vdywgbm9ybWFsbHkgd2UnZCBsb29rIGF0IHRoaXMgYXMgYSBwb2lzc29uIChvciBhIFpJUCEpIGJ1dCwgbGV0J3MgbG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGNhdGNoIGJ5IG51bWJlciBvZiBwZW9wbGUuCgpgYGB7cn0KbGlicmFyeShnZ3JpZGdlcykKZ2dwbG90KGRhdGEgPSBGaXNoLCAKICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGZpc2hfY2F1Z2h0LCB5ID0gYXMuZmFjdG9yKHBlcnNvbnMpLCBmaWxsID0gYXMuZmFjdG9yKHdpdGhfa2lkcykpKSArCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhhbHBoYSA9IDAuNykKCmBgYAoKTG9va2luZyBhdCB0aGlzLCB3ZSBjYW4gc2VlIHRoYXQgdGhlIGRhdGEgaXMgbm90IG9ubHkgY2xlYXJseSAwIGluZmxhdGVkLCBidXQsIHdvdywgdGhhdCdzIGEgbG9uZyB0YWlsLiBBIHBvaXNzb24gaXMgZ29pbmcgdG8gYmUgYSBiYWQgY2hvaWNlLiBJbnN0ZWFkLCBhIG5lZ2F0aXZlIGJpbm9taWFsIGlzIGdvaW5nIHRvIGJlIHRoZSB3YXkgdG8gZ28uCgpUaGUgWklOQiBtb2RlbCBpcyBhIGZhaXJseSBjb21tb24gb25lIGZvciB0aGlzIHR5cGUgb2Ygc2l0dWF0aW9uLiAgUmF0aGVyIHRoYW4gYmVsYWJvciB0aGUgcG9pbnQsIGhlcmUncyBhIGBnbG1tVE1CYCBleGFtcGxlIHdpdGggYSBaSU5CLiBOb3RlIHRoYXQgSSdtIHVzaW5nIG5iaW5vbTEgaGVyZS4gVGhpcyBpcyBhIGdyZWF0IHdheSB0byBtb2RlbCBvdmVyZGlzcGVyc2lvbiwgYXMgaW5zdGVhZCBvZiB0aGUgdmFyaWFuY2UgZXhwYW5kaW5nIHdpdGggdGhlIHNxdWFyZSBvZiB0aGUgbWVhbiwgaXQgZXhwYW5kcyBsaW5lYXJseSB3aXRoIHRoZSBtZWFuLiBBZ2FpbiwgYSBtb2RlbGluZyBjaG9pY2UhCgpgYGB7ciB6aW5ifQp6aW5iX3RtYiA8LSBnbG1tVE1CKGZpc2hfY2F1Z2h0IH4gcGVyc29ucywKICAgICAgICAgICAgICAgICAgIHppZm9ybXVsYSA9IH4gd2l0aF9raWRzKzAsCiAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBuYmlub20xLAogICAgICAgICAgICAgICAgICAgZGF0YSA9IEZpc2gpCmBgYAoKQW5kIHlvdSBjYW4gdGhlbiB3b3JrIGZyb20gdGhlcmUuIFdoYXQgZG8geW91IHNlZSBoZXJlPwoKYGBge3IgemluYl9zdW19CnN1bW1hcnkoemluYl90bWIpCmBgYAoKPCEtLQpTaW1pbGFybHksIHdlIGNhbiBkbyB0aGlzIGluIGEgQmF5ZXNpYW4gZnJhbWV3b3JrLgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KbW9kX3ppbmIgPC0gYWxpc3QoCiAgI0xpa2VsaWhvb2QKICBmaXNoX2NhdWdodCB+IGR6aWJpbm9tKHBfemVybywgc2l6ZSwgcHJvYiksCiAgCiAgI3RyYW5zZm9ybWF0aW9uIGZvciBuYmlub20KICAjcHJvYiA9IHNpemUvKHNpemUrbXUpLAogIHByb2IgPC0gc2l6ZS8oc2l6ZStlc3RfZmlzaF9jYXVnaHQpLAogIAogICNER1AKIGxvZyhlc3RfZmlzaF9jYXVnaHQpIDwtIGEgKyBiKnBlcnNvbnMsCiBsb2dpdChwX3plcm8pIDwtIGJfa2lkc1t3aXRoX2tpZHNdLAogIAogICNwcmlvcnMKICBhIH4gZG5vcm0oMywyKSwKICBzaXplIH4gZGV4cCgyKSwKCiAgYiB+IGRub3JtKDAsNSksCiAgYl9raWRzW3dpdGhfa2lkc10gfiBkbm9ybSgwLDUpCikKCmZpdF96aW5iIDwtIHF1YXAobW9kX3ppbmIsIAogICAgICAgICAgICAgICAgZGF0YSA9IEZpc2ggJT4lIG11dGF0ZShzaXplID0gMSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdpdGhfa2lkcyA9IGFzLm51bWVyaWMod2l0aF9raWRzKSkpCgpgYGAKCi0tPgojIyA1LiBHb25lIEZpc2hpbmcgLSBHYW1hIEh1cmRsZXMKCldlIGNhbiBhbHNvIHVzZSBjb250aW51b3VzIGRhdGEuIFRoaXMgaXMgbW9zdCBjb21tb24gd2l0aCBHYW1tYSBkaXN0cmlidXRlZCBkYXRhLCBhcyB5b3UgY2Fubm90IGhhdmUgYSB3YWl0aW5nIHRpbWUgb2YgMC4gTGV0J3MgdXNlIGEgc2ltdWxhdGVkIGRhdGEgc2V0IGxvb2tpbmcgYXQgZHVyYXRpb24gb2Ygb2Zmc2hvcmUgZmlzaGluZyB0cmlwcyBpbiBkYXlzIGdpdmVuIGF2ZXJhZ2Ugd2F2ZSBoZWlnaHQuCgpgYGB7ciBsb2FkX2RhdGF9CmZpc2hpbmcgPC0gcmVhZC5jc3YoImh0dHBzOi8vYmlvbDYwOS5naXRodWIuaW8vbGVjdHVyZXMvZGF0YS8yMC9maXNoaW5nX2R1cmF0aW9uLmNzdiIpCgojd2hhdCdzIHRoZXJlCmhlYWQoZmlzaGluZykKCiN2aXN1YWxpemUgdGhlIGRhdGEKcGxvdChkYXlzX2F0X3NlYSB+IHNlYXNfbSwgZGF0YT1maXNoaW5nKQpgYGAKCiMjIyA1LjEuIEV4cGxvcmF0b3J5IEFuYWx5c2lzIERlY2lzaW9ucwoKT0ssIGxldCdzIGFzc3VtZSB5b3UgaGF2ZSBubyBhIHByaW9yaSBrbm93bGVkZ2UgYWJvdXQgdGhpcyBkYXRhIHNhdmUgdGhlIHZhcmlhYmxlcyBpbiBpdC4gWW91IGFyZSBpbnRlcmVzdGVkIGluIGp1c3QgYSBiaXZhcmlhdGUgcmVsYXRpb25zaGlwLiBQZXJpb2QuICAKXApOb3csIGluIGJ1aWxkaW5nIGEgbW9kZWwgZm9yIHRoaXMsIHlvdSBoYXZlIHRvIGRlY2lkZSBvbiB0aHJlZSB0aGluZ3MuICAKXAoxLiBXaGF0IGlzIHRoZSBkYXRhIGdlbmVyYXRpbmcgcHJvY2Vzcz8gIAoyLiBXaGF0IGlzIHRoZSBlcnJvciBnZW5lcmF0aW5nIHByb2Nlc3MgLSB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSB2YXJpYXRpb24gYXJvdW5kIHByZWRpY3RlZCB2YWx1ZXM/ICAKMy4gQXMgd2UncmUgdXNpbmcgQmF5ZXMsIGRvIHdlIGhhdmUgaW5mb3JtYXRpb24gdGhhdCBjYW4gYWxsb3cgdXMgdG8gcHJvdmlkZSB3ZWFrbHkgaW5mb3JtYXRpdmUgcHJpb3JzPyAgCgojIyMgNS4yIFRoZSBkYXRhIGdlbmVyYXRpbmcgcHJvY2VzcwoKT24gdGhlIGRhdGEgZ2VuZXJhdGluZyBwcm9jZXNzLCB3ZSBjYW4gbG9vayBhdCB0aGUgZGF0YSBhbmQgc2VlLCBoZXksIHdlIGhhdmUgYSBuZWdhdGl2ZSByZWxhdGlvbnNoaXAsIGFuZCBpdHMgb25lIHRoYXQgY2Fubm90IGJlIGxlc3MgdGhhbiAwLiBUaGlzIHN1Z2dlc3RzIGEgcG93ZXIgb3IgZXhwb25lbnRpYWwgcmVsYXRpb25zaGlwLiBBbiBleHBvbmVudGlhbCAobG9nIGxpbmsgZnVuY3Rpb24pIHNlZW1zIHByZXR0eSByZWFzb25hYmxlLgoKIyMjIDUuMyBUaGUgZXJyb3IgZ2VuZXJhdGluZyBwcm9jZXNzCgpUaGlzIG9uZSBpcyBoYXJkZXIuIFdlIGhhdmUgbWFkZSBzb21lIGRlY2lzaW9ucyB0aGF0IGFyZSB1c2VmdWwuIFdlIGhhdmUgbm8gbm9uLW5lZ2F0aXZlIHZhbHVlcy4gV2UgaGF2ZSBjb250aW51b3VzIGRhdGEuIFNvLCBubyBub3JtYWwgZGlzdHJpYnV0aW9uLiBBbmQgbm8gUG9pc3NvbiBvciBvdGhlciBjb3VudCBkaXN0cmlidXRpb24uIEJ1dCB3aGF0IGFyZSB3ZSBsZWZ0IHdpdGguIExvZy1ub3JtYWw/IEdhbW1hPyBFcmxhbmc/IFdlaWJ1bGw/IE90aGVycyAob2gsIGFuZCB0aGVyZSBhcmUgb3RoZXJzKS4gIAoKV2UgaGF2ZSBhIGZldyBvcHRpb25zIC0gd2UgY2FuIHBsb3QgaGlzdG9ncmFtcywgcGxvdCBkZW5zaXRpZXMgYXQgZWFjaCBwcmVkaWN0b3IgdmFsdWUgKG9yIGJpbiBvZiBwcmVkaWN0b3IpLCB3ZSBjYW4gZml0IGEgZ2F1c3NpYW4gbW9kZWwsIGtub3cgdGhhdCBpdCdzIHdyb25nIGJ1dCBsb29rIGF0IGl0cyByZXNpZHVhbCBkaXN0cmlidXRpb24sIHdlIGNhbiBzdGFyZSBhdCBvdXIgc2NhdHRlcnBsb3QgYW5kIHBvbmRlci4gIAoKV2hhdCB3ZSdyZSBwb25kZXJpbmcgaXMgKmhpc3RvbWFuY3kqIC0gdGhlIG15c3RpYyBhYmlsaXR5IHRvIGRpdmluZSBkaXN0cmlidXRpb25zIGZyb20gYSBoaXN0b2dyYW0gb3IgZGVuc2l0eSBwbG90LiAgCgpJdCBpcyBhIHRlcnJpYmxlIGlkZWEuICAKCkluc3RlYWQsIGxldCdzIGFjdHVhbGx5IHVzZSBzY2llbmNlIGFuZCBzb21lIHRob3VnaHQuIEluIHRoaXMgY2FzZSwgd2UncmUgbG9va2luZyBhdCBhIGRpc3RyaWJ1dGlvbiBvZiBkdXJhdGlvbnMgKGRheXMgYXQgc2VhKS4gVGhlcmUncyBzb21lIHVuZGVybHlpbmcgdGhpbmcgdGhhdCBkZXRlcm1pbmVzIHRoZSBkdXJhdGlvbiB0aGF0IHNvbWVvbmUgc3RheXMgYXQgc2VhLiBUaGlzIGZpdHMgbmF0dXJhbGx5IHRvIGEgR2FtbWEgZGlzdHJpYnV0aW9uLiBPdGhlcnMgY291bGQgd29yayBoZXJlLCB0b28sIGJ1dCwgYSBHYW1tYSBpcyBhbHNvIG1heGltdW0gZW50cm9weSBkaXN0cmlidXRpb24gZm9yIGRhdGEgZnJvbSAwIHRvIGluZmluaXR5IHdoZXJlIHRoZSB2YXJpYW5jZSBpbmNyZWFzZXMgd2l0aCB0aGUgbWVhbi4gQW5kIGEgbWVtYmVyIG9mIHRoZSBleHBvbmVudGlhbCBmYW1pbHksIHNvIGZhaXJseSBlYXN5IHRvIHdvcmsgd2l0aC4gU28sIHllcywgeW91IGNvdWxkIGNob29zZSBvdGhlcnMsIGJ1dCBmb3IgYmlvbG9naWNhbCBwbGF1c2liaWxpdHksIGFuZCBlYXNlIG9mIHVzZSwgaXQncyBhbiBleGNlbGxlbnQgZmlyc3QgY2hvaWNlLgoKIyMjIyA1LjMuMSBUaGUgR2FtbWEKCiFbXSguL2ltYWdlcy8yMC9hbmNpZW50LWFsaWVucy1ndXkxMS13aHktdXNlLWdhbW1hLWdsbS1hbGllbnMuanBnKQoKQXMgYSBxdWljayByZW1pbmRlciBvZiB3aGF0IHRoZSBnYW1tYSBpcywgaXQncyBhIGRpc3RyaWJ1dGlvbiB0aGF0IGNhbiBiZSBkZWZpbmVkIHdpdGggZWl0aGVyIGEgc2hhcGUgcGFyYW1ldGVyIGFuZCBhIHJhdGUgb2YgZXZlbnRzIGhhcHBlbmluZyAoMS90aW1lIHRvIGV2ZW50IC0gaW4gdGhpcyBjYXNlLCB0aGUgZGVjaXNpb24gdG8gcG9ydCkgb3IgdGhlIHNhbWUgc2hhcGUgcGFyYW1ldGVyIGFuZCBhIHNjYWxlIHBhcmFtZXRlciB3aGljaCBpcyAxL3JhdGUgKHNvLCB0aW1lIGZvciAxIGV2ZW50IHRvIGhhcHBlbikuIFRoaXMgY2FuIGxlYWQgdG8gYSBsaXR0bGUgYml0IG9mIGNvbmZ1c2lvbiwgYXMgeW91J2xsIHNlZSBpdCBwcmVzZW50ZWQgZWl0aGVyIHdheS4gUGVyc29uYWxseSwgSSBsaWtlIHJhdGUsIGJ1dCBlaXRoZXIgd2lsbCB3b3JrCgpTbywgYmVsb3csIGFzIHdlIGluY3JlYXNlIHRoZSByYXRlLCB3ZSBoYXZlIG1vcmUgYW5kIG1vcmUgc2hvcnQgd2FpdCB0aW1lcyBmb3IgMSBldmVudC4KCmBgYHtyIGdhbW1hLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCgpnYW1tYV9mcmFtZSA8LSBjcm9zc2luZyhyYXRlID0gc2VxKDAuMSwgMiwgbGVuZ3RoLm91dD01KSwgCiAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlPTEsIAogICAgICAgICAgICAgICAgICAgICAgICB4PXNlcSgwLjAxLCA1LCBsZW5ndGgub3V0PTIwMCkpICU+JQogIG11dGF0ZShkID0gZGdhbW1hKHgsIHNoYXBlID0gc2hhcGUsIHJhdGUgPSByYXRlKSkKCmdncGxvdChnYW1tYV9mcmFtZSwgYWVzKHg9eCwgeT1kLCBjb2xvcj1mYWN0b3IocmF0ZSkpKSArCiAgZ2VvbV9saW5lKCkKCmBgYAoKVG8gbWFrZSB0aGluZ3MgYSBiaXQgZWFzaWVyLCB0aGUgbWVhbiBvZiBhIGdhbW1hIGlzIG11ID0gc2hhcGUgKiBzY2FsZS4gVGhpcyBsZWFkcyB0byBhIGZldyByZWxhdGlvbnNoaXBzL3RyYW5zZm9ybWF0aW9ucyB3aGljaCBjYW4gYmVjb21lIHVzZWZ1bC4gIAoKc2hhcGUgPSBtdS9zY2FsZSAgCnJhdGUgPSBzaGFwZS9tdSAgCnNjYWxlID0gbXUvc2hhcGUgIAoKSXQncyBhIGJpdCBvZiBhIHBhaW4sIGFzIGRlcGVuZGluZyBvbiBob3cgeW91IHB1dCB0aGluZ3MgdG9nZXRoZXIsIHlvdSBjYW4gZ2V0IHNsaWdodGx5IGRpZmZlcmVudCByZXN1bHRzIGZyb20gbW9kZWxzLiBCdXQgaXQgc2hvdWxkIG5vdCBtYXR0ZXIgdG8gcXVhbGl0YXRpdmUgb3V0Y29tZXMuCgojIyMgNS40IFByaW9ycwoKSW4gdGhpcyBjYXNlLCB3ZSBkb24ndCByZWFsbHkgaGF2ZSBhbnkgcHJpb3IgaW5mb3JtYXRpb24hIFNvIHdlIHdhbnQgZmxhdCBwcmlvcnMuIFdlIGNhbiB1c2UgcmVndWxhcml6YXRpb24gLSBzYXksIGIgfiBkbm9ybSgwLDMpLCBidXQgaG93IG11Y2ggZGVwZW5kcyBvbiB5b3UuIFdlJ3JlIGFsc28gZ29pbmcgdG8gd2FudCBwcmlvcnMgZm9yIHNjYWxlIG9yIHJhdGUgcGFyYW1ldGVycy4gVGhlc2UgY2FuIGJlIG5vcm1hbCwgYnV0IG9mdGVuIGNhdWNoeSAoZm9yIHNjYWxlKSBvciBleHBvbmVudGlhbCAoZm9yIHJhdGUpIHByaW9ycyB3b3JrIHdlbGwuIFlNTVYuCgojIyMgNS41LiBGaXR0aW5nIHdpdGggYSBHYW1tYQoKCiMjIyMgNS41LjEgVGhlIG1vZGVsClNvLCBsZXQncyBzdGFydCB3aXRoIGBkZ2FtbWFgLiBCeSBkZWZhdWx0LCBgZGdhbW1hYCB0YWtlcyBmaXJzdCBhIHNoYXBlIGFuZCB0aGVuIGEgcmF0ZSBhcmd1bWVudC4gVGhpcyBwcmVzZW50cyBhIHNtYWxsIGNoYWxsZW5nZSwgYXMgYWJvdmUgd2UgaGF2ZSBzaGFwZSA9IHJhdGUvbXUuIFRoaXMgbWVhbnMgd2Ugd291bGQgbmVlZCB0byB3b3JrIHdpdGggb3VyIG1lYW4gdmFsdWUgYSBiaXQuIFJhdGhlciB0aGFuIGEgc3RyYWlnaHQgYGxvZyhtdSlgIHN0YXRlbWVudCwgd2UncmUgZ29pbmcgdG8gbmVlZCB0byB1c2UgdGhlIGNhbGN1bGF0ZWQgdmFsdWUuICBMZXQncyBwdXQgdG9nZXRoZXIgYSBtb2RlbC4KCgpgYGB7ciBnYW1tYV9tb2QsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkocmV0aGlua2luZykKCmZpc2hpbmdfZ2FtbWFfbW9kIDwtIGFsaXN0KAogICNsaWtlbGlob29kCiAgI25vdGluZyB0aGF0IHJhdGUgPSBzaGFwZS9leHAobG9nX211KQogIGRheXNfYXRfc2VhIH4gZGdhbW1hKHNoYXBlLCBzaGFwZS9leHAobG9nX211KSksCiAgCiAgI2RhdGEgZ2VuZXJhdGluZyBwcm9jZXNzCiAgbG9nX211IDwtIGEgKyBiKnNlYXNfbSwKICAKICAjcHJpb3JzCiAgYSB+IGRub3JtKDAsMTAwKSwKICBiIH4gZG5vcm0oMCwxMDApLAogIHNoYXBlIH4gZGV4cCgyKQopCmBgYAoKCk1jRWxyZWF0aCBoYXMgYWxzbyBwdXQgdG9nZXRoZXIgYSBzaG9ydGN1dCwgYGRnYW1tYTJgLCBidXQgaXQgdGVuZHMgdG8gcHJvZHVjZSBzbGlnaHRseSBkaWZmZXJlbnQgcmVzdWx0cywgYXMgaXQgY2FsY3VsYXRlcyBzaGFwZSBhcyBhIGZ1bmN0aW9uIG9mIHRoZSBwcmVkaWN0b3JzLiBTbyBJJ20gbm90IGEgeXVnZSBmYW4uCgpgYGB7ciBnYW1tYV9tb2RfMn0KZmlzaGluZ19nYW1tYV9tb2QyIDwtIGFsaXN0KAogICNsaWtlbGlob29kCiAgZGF5c19hdF9zZWEgfiBkZ2FtbWEyKG11LCBzY2FsZSksCiAgCiAgI2RhdGEgZ2VuZXJhdGluZyBwcm9jZXNzCiAgbG9nKG11KSA8LSBhICsgYipzZWFzX20sCgogICNwcmlvcnMKICBhIH4gZG5vcm0oMCwxMDApLAogIGIgfiBkbm9ybSgwLDEwMCksCiAgc2NhbGUgfiBkY2F1Y2h5KDAsMikKKQpgYGAKCiMjIyMgNS41LjIgVGhlIGZpdAoKT0ssIGxldCdzIGZpdCB0aGlzIHRoaW5nIHVzaW5nIHRoZSBmaXJzdCBtb2RlbCEgIAoKYGBge3IgZml0X2dhbW1hLCBlcnJvcj1UUlVFfQpmaXNoaW5nX2dhbW1hX2ZpdCA8LSBtYXAoZmlzaGluZ19nYW1tYV9tb2QsIGRhdGE9ZmlzaGluZykKYGBgCgpVaCBvaCEgV2hhdCdzIHdyb25nPyAgCgphbmQgbm93LCB3ZSBlbmNvdW50ZXIgbWlzdGFrZSAjMS4gUmVtZW1iZXIgdGhhdCB3ZSdyZSB1c2luZyBhIGxvZyBsaW5rPyBXZWxsLCBsb2coMCkgaXMgbm90IGEgbnVtYmVyLCBzbywgb2YgY291cnNlIG91ciBtb2RlbCBmYWlscyEgU28gd2UgbmVlZCB0byBkcm9wIHRob3NlIDBzLiBUaGV5IG1lYW4gdGhhdCBhIGZpc2hlcm1hbiBkaWRuJ3QgbGVhdmUgcG9ydCBhdCBhbGwsIGFuZCBhcmUgbGlrZWx5IGJlc3QgY29uaXNkZXJlZCBhcyBhIHNlcGFyYXRlIHByb2Nlc3MuIFJlZ2FyZGxlc3MsIGV2ZW4gaWYgd2Ugd2FudCB0byBmb2xkIHRoZW0gYmFjayBpbiAoc2VlIGJlbG93KSwgd2Ugd291bGQgYmUgZ3JlYXRseSBoZWxwZWQgYnkgYnVpbGRpbmcgYSBtb2RlbCB0aGF0IGRvZXMgbm90IHJlcXVpcmUgaGFuZGxpbmcgdGhvc2UgZGV0YWlscy4KCmBgYHtyIGZpdF9nYW1tYV9nb29kLCBlcnJvcj1UUlVFfQpmaXNoaW5nX2NsZWFuIDwtIGZpc2hpbmcgJT4lIGZpbHRlcihkYXlzX2F0X3NlYSAhPSAwKQoKc2V0LnNlZWQoNjA5KQpmaXNoaW5nX2dhbW1hX2ZpdCA8LSBtYXAoZmlzaGluZ19nYW1tYV9tb2QsIGRhdGE9ZmlzaGluZ19jbGVhbikKYGBgCgpPb3BzISBUaGF0IGRpZG4ndCB3b3JrIGVpdGhlciEgV2VsbCB0aGF0J3Mgbm90IGdvb2QuIFNvLCB3aGF0IGRvZXMgdGhlIGVycm9yIHNheT8gVGhhdCBzdGFydCB2YWx1ZXMgd2VyZSBpbnZhbGlkLiAgT0suCgpXZWxwLCBleWViYWxsaW5nIHRoZSBncmFwaCBvZiB0aGUgZGF0YSwgd2Ugc2VlIGEgcG9zaXRpdmUgaW50ZXJjZXB0IGFuZCBuZWdhdGl2ZSBzbG9wZS4gU28sIGV2ZW4gd2l0aCB0aGUgc2FtZSBzZWVkIChvdGhlciBzZWVkIHZhbHVlcyBhbGxvdyBmb3IgY29udmVyZ2VuY2UpLCBsZXQncyByZS10cnkhCgpgYGB7ciBmaXRfZ2FtbWFfcmVhbGx5X2dvb2R9CnNldC5zZWVkKDYwOSkKZmlzaGluZ19nYW1tYV9maXQgPC0gbWFwKGZpc2hpbmdfZ2FtbWFfbW9kLCBkYXRhPWZpc2hpbmdfY2xlYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICBzdGFydCA9IGxpc3QoYT0xLCBiPS0xKSkKYGBgCgpGaXQhCgojIyMjIDUuNS4zIE1vZGVsIGV2YWx1YXRpb24KCkxldCdzIGNoZWNrIG91dCBvdXIgcG9zdGVyaW9yIQoKYGBge3IgcG9zdF9ub3JtYWx9CnBhaXJzKGZpc2hpbmdfZ2FtbWFfZml0KQpgYGAKCkhleSEgTm90IGJhZC4gSW5kZWVkLCBJIHdvdWxkbid0IGV2ZW4gZ28gdG8gU1RBTiB3aXRoIGEgZml0IGxpa2UgdGhpcy4KCmBgYHtyIHBvc3RjaGVjaywgZXJyb3I9VFJVRSwgcmVzdWx0cz0iaGlkZSIsIG1lc3NhZ2U9RkFMU0V9CnBhcihtZnJvdz1jKDIsMykpCnBvc3RjaGVjayhmaXNoaW5nX2dhbW1hX2ZpdCkKcGFyKG1mcm93PWMoMSwxKSwgYXNrPUYpCmBgYAoKTG9va2luZyBwcmV0dHkgZ29vZCEgdGhlcmUgYXJlIG9uZSBvciB0d28gcG9pbnRzIG91dHNpZGUgb2YgdGhlIHByZWRpY3RpdmUgQ0ksIGJ1dCwgZWgsIHRoYXQncyBub3QgYmFkIGNvbnNpZGVyaW5nIHRoZSBsYXJnZSBudW1iZXIgb2YgZGF0YSBwb2ludHMgd2UgaGF2ZS4gV29ydGggaW52ZXN0aWdhdGluZywgYnV0IG5vdCBsb3NpbmcgdG9vIG11Y2ggc2xlZXAgb3Zlci4KCiMjIyMgNS41LjQgV2hhdCBkbyB0aGUgcmVzdWx0cyBzYXk/CgpXZSBjYW4gZmlyc3QgbG9vayBhdCB0aGUgZ2VuZXJhbCByZXN1bHRzCgpgYGB7ciBwcmVjaXNfZ2FtbWF9CnByZWNpcyhmaXNoaW5nX2dhbW1hX2ZpdCkKYGBgCgpTbywgYSBkZWNsaW5lIG9mIC0wLjYzLCBvciwgZm9yIGV2ZXJ5IDFtIHdhdmUsIGZpc2hlcm1lbiB3b3VsZCBzcGVuZCA1MyUgbGVzcyB0aW1lIGF0IHNlYSAoYGV4cCgtMC42MylgKS4gV2UgY2FuJ3Qgc2F5IGFic29sdXRlIGRheXMgaGVyZSwgYXMgdGhpcyBpcyBhIG5vbmxpbmVhciBmdW5jdGlvbiAtIHdoYXQgaGFwcGVucyBpbiBhYnNvbHV0ZSB1bml0cyBnb2luZyBmcm9tIDIgdG8gMyBpcyBkaWZmZXJlbnQgZnJvbSAzIHRvIDQuIEJ1dCB3ZSBjYW4ganVkZ2UgaXQgb24gYSBwZXJjZW50YWdlIHNjYWxlLiBUcnkgY2FsY3VsYXRpbmcgaXQgb3V0IGlmIHlvdSBkb24ndCBiZWxpZXZlIGl0LiAgCgpXZSBjYW4gYWxzbyB2aXN1YWxpemUgdGhlIHJlc3VsdHMKCmBgYHtyIGZpdF9wbG90LCByZXN1bHRzPSJoaWRlIn0KI3N0YXJ0IHdpdGggYSBmcmVzaCBkYXRhIGZyYW1lIG9mIHByZWRpY3RlZCB2YWx1ZXMKcHJlZF9kZiA8LSBkYXRhLmZyYW1lKHNlYXNfbSA9IHNlcSgwLDUsbGVuZ3RoLm91dD0yMDApKQoKI09LLCBub3cgZ2V0IHNvbWUgc2ltIGFuZCBsaW5rIG91dHB1dApzaW1fZmlzaGluZyA8LSBzaW0oZmlzaGluZ19nYW1tYV9maXQsIGRhdGE9cHJlZF9kZikKCiNub3RlLCBmb3IgbGluaywgd2UgbmVlZCB0byBleHBvbmVudGlhdGUKbGlua19maXNoaW5nIDwtIGV4cChsaW5rKGZpc2hpbmdfZ2FtbWFfZml0LCBkYXRhPXByZWRfZGYpKQoKI3Ntb29zaCBpdCBhbGwgdG9nZXRoZXIKcHJlZF9kZiA8LSBwcmVkX2RmICU+JQogIG11dGF0ZShkYXlzX2F0X3NlYSA9IGFwcGx5KGxpbmtfZmlzaGluZywgMiwgbWVkaWFuKSwKICAgICAgICAgbHdyX2ZpdCA9IGFwcGx5KGxpbmtfZmlzaGluZywgMiwgSFBESSlbMSxdLAogICAgICAgICB1cHJfZml0ID0gYXBwbHkobGlua19maXNoaW5nLCAyLCBIUERJKVsyLF0sCiAgICAgICAgIGx3ciA9IGFwcGx5KHNpbV9maXNoaW5nLCAyLCBIUERJKVsxLF0sCiAgICAgICAgIHVwciA9IGFwcGx5KHNpbV9maXNoaW5nLCAyLCBIUERJKVsyLF0KICAgICAgICAgKQoKI2EgbmljZSBnZ3Bsb3QKZ2dwbG90KGRhdGE9cHJlZF9kZiwKICAgICAgIG1hcHBpbmc9YWVzKHg9c2Vhc19tLCB5PWRheXNfYXRfc2VhKSkgKwogIGdlb21fcmliYm9uKG1hcHBpbmc9YWVzKHltaW49bHdyLCB5bWF4PXVwciksIGFscGhhPTAuNCkgKwogIGdlb21fcmliYm9uKG1hcHBpbmc9YWVzKHltaW49bHdyX2ZpdCwgeW1heD11cHJfZml0KSwgZmlsbD0iYmx1ZSIsIGFscGhhPTAuNCkgKwogIGdlb21fbGluZShsd2Q9MSwgY29sb3I9InJlZCIpICsKICBnZW9tX3BvaW50KGRhdGE9ZmlzaGluZ19jbGVhbikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZT0xNykKYGBgCgpOb3QgYmFkLiBXZSBjYW4gc2VlIHRoZSByZXN1bHRzIG9mIGEgR2FtbWEgZGlzdHJpYnV0aW9uIC0gdGhlIG1lYW4gdmFyaWFuY2Ugc2NhbGluZyByZWxhdGlvbnNoaXAuIFdlIGNhbiBzZWUgd2UgaGF2ZSBzb21lIG91dGxpZXJzIHRvIGluc3BlY3QsIGJ1dCwgb3ZlcmFsbCwgcHJldHR5IGRhcm5lZCBnb29kLgoKIyMgNi4gQWJvdXQgdGhvc2UgWmVyb2VzIC0gWmVybyBBdWdtZW50ZWQgR2FtbWEKIVtdKC4vaW1hZ2VzLzIwL3doYXQtaWYtaS10b2xkLXlvdS13aGF0LWlmLWktdG9sZC15b3UteW91LWFscmVhZHkta25vdy16ZXJvLWluZmxhdGlvbi5qcGcpCgojIyMgNi4xIFplcm8gSW5mbGF0aW9uLCBhdWdtZW50YXRpb24sIGh1cmRsZXMsIGFuZCBtb3JlCgpFeGNlc3NpdmUgemVyb2VzIGFyZSBub3Qgc29tZXRoaW5nIHRvIGJlIHNjYXJlZCBvZi4gT2Ygd2hpY2ggdG8gYmUgc2NhcmVkPyBXaGF0ZXZlciwgeW91IGd1eXMga25vdyB0aGlzIG9uIGEgZGVlcCBsZXZlbC4gQmVjYXVzZSBhIHplcm8gaW5mbGF0ZWQgb3IgemVybyBhdWdtZW50ZWQgZGlzdHJpYnV0aW9uIGlzIG5vdGhpbmcgbW9yZSB0aGFuIHRoZSBsb3ZlY2hpbGQgb2YgYSBiaW5vbWlhbCBsb2dpc3RpYyByZWdyZXNzaW9uIGFuZCBhIEdMTSBvZiB5b3VyIG93biBjaG9vc2luZy4KCkxldCdzIHN0YXJ0IGJ5IHRoaW5raW5nIGFib3V0IGEgSHVyZGxlIG9yIFplcm8gQXVnbWVudGVkIG1vZGVsLiBJbiB0aGVzZSBtb2RlbHMsIHlvdSBjYW5ub3QgaGF2ZSBhbnkgemVyb2VzIGZvciByZWFzb25zIHJlbGF0ZWQgdG8gY29uc3RyYWlucyBvZiB5b3VyIGRhdGEgZ2VuZXJhdGluZyBwcm9jZXNzIG9yIGVycm9yIGdlbmVyYXRpbmcgcHJvY2Vzcy4gSW5zdGVhZCwgWmVyb2VzIGFyZSBnZW5lcmF0ZWQgYnkgb25lIGRhdGEgZ2VuZXJhdGluZyBwcm9jZXNzLCB0aGVuIGFub3RoZXIgcHJvY2VzcyBkZXRlcm1pbmVzIHRoZSByZWxhdGlvbnNoaXAgd2l0aCB5b3VyIHJlc3BvbnNlIHZhcmlhYmxlLiBTbywgeW91IGZpcnN0IHNwbGl0IHlvdXIgbW9kZWwgaW50byBvbmUgd2hlcmUgeW91IGhhdmUgZWl0aGVyIDAgb3Igbm9uLTAgb3V0Y29tZXMsIGFuZCBwZXJmb3JtIGEgbG9naXN0aWMgcmVncmVzc2lvbi4gV2l0aCB3aGF0ZXZlciBwcmVkaWN0b3JzIHlvdSB3YW50LiBUaGVuLCB3aXRoIGFsbCBvZiB0aGUgbm9uLXplcm8gb3V0Y29tZXMsIHlvdSBwZXJmb3JtIHdoYXRldmVyIEdMTSB5b3Ugd2FudC4KCkEgWmVybyBJbmZsYXRlZCBvciBaZXJvIEF1Z21lbnRlZCBtb2RlbCBpcyBzbGlnaHRseSBkaWZmZXJlbnQuIEhlcmUsIHplcm9lcyBhcmUgZ2VuZXJhdGVkIGVpdGhlciBieSBzb21lIHByb2Nlc3MgdGhhdCBpcyBiaW5hcnkgLSBhIHRoaW5nIGhhcHBlbmVkIG9yIGl0IGRpZG4ndCAtIE9SIHRoZXkgYXJlIGdlbmVyYXRlZCBieSB3aGF0ZXZlciAgZGF0YSBnZW5lcmF0aW5nIHByb2Nlc3MgeW91IGFyZSBtb2RlbGluZy4KCiFbXSguL2ltYWdlcy8yMC96aWdfemFnLmpwZykKClplcm8gSW5mbGF0ZWQgbW9kZWxzIGNvbWUgaW4gbWFueSBmb3JtcyAtIFplcm8gSW5mbGF0ZWQgUG9pc3NvbiAodXNlcyBgZHppcG9pcyhsYW1iZGEsIHApYCBpbiByZXRoaW5raW5nKSwgWmVybyBJbmZsYXRlZCBCaW5vbWlhbCAodXNlcyBgZHppYmlub20ocF96ZXJvLCBzaXplLCBwKWApLCBaZXJvIEF1Z21lbnRlZCBHYW1tYSAoYGR6YWdhbW1hMihwcm9iLCBtdSwgc2NhbGUpYCkgYW5kIG1vcmUgd2hpY2ggY2FuIGJlIHB1bGxlZCBmcm9tIG90aGVyIGxpYnJhcmllcyBpbiBSLgoKIyMjIDYuMiBIb3cgZG8gSSBrbm93IGlmIGl0IGlzIHplcm8gaW5mbGF0ZWQgCgpUaGVyZSBhcmUgdHdvIHN0cmFpZ2h0Zm9yd2FyZCB3YXlzIHRvIHNlZSBpZiBzb21ldGhpbmcgaXMgemVybyBpbmZsYXRlZC4gRmlyc3QsIHdoYXQncyB0aGUgZGlzdHJpYnV0aW9uIGxvb2sgbGlrZT8gRG8geW91IHNlZSBhIGxvdCBvZiB6ZXJvcz8KCmBgYHtyIHpkZW5zfQpwbG90KGRlbnNpdHkoZmlzaGluZyRkYXlzX2F0X3NlYSkpCmBgYAoKT0sgLSBsb3RzIG9mIHplcm9lcyAtIHdoaWNoLCBnaXZlbiB0aGF0IHRoaXMgaXMgYSBHYW1tYSwgaXMgc3VwZXIgc3VzcGVjdC4gQnV0LCBmb3IgdGhpcyBvciBvdGhlciBkaXN0cmlidXRpb25zLCB0aGV5IG1pZ2h0IGJlIE9LLiBTb21ldGltZXMgeW91J2xsIGV2ZW4gc2VlIHR3byBodW1wcyAtIG9uZSBhcm91bmQgemVybywgb25lIHNvbWV3aGVyZSBlbHNlLiBaZXJvIEluZmxhdGlvbiEKCllvdSBjYW4gYWxzbyBsb29rIGF0IGEgc2NhdHRlcnBsb3QgYW5kIHNlZSBpZiB0aGVyZSBhcmUgYSBzdXNwaWNpb3VzIG51bWJlciBvZiB6ZXJvZXMgYWNyb3NzIGFsbCBvZiB0aGUgdmFsdWVzIG9mIGEgcHJlZGljdG9yIHZhcmlhYmxlLiBTdXJlLCB5b3UgbWlnaHQgYWh2ZSBhIG5vbi1ub3JtYWwgZGlzdHJpYnV0aW9uLCBidXQgdGhhdCBraW5kIG9mIHBhdHRlcm4gaXMgYSBiaXQgb2RkLgoKYGBge3IgZnVsbF9zY2F0dGVyfQpwbG90KGRheXNfYXRfc2VhIH4gc2Vhc19tLCBkYXRhPWZpc2hpbmcpCmBgYAoKIyMjIDYuMyBVc2luZyB0aGUgWkFHCgpUbyB3b3JrIHdpdGggb3VyIG9yaWdpbmFsIGRhdGEgLSB6ZXJvZXMgYW5kIGFsbCAtIHdlIGNhbiB1c2UgYSBaZXJvIEF1Z21lbnRlZCBHYW1tYSBEaXN0cmlidXRpb24uIEknbSBzdGlsbCBub3QgYSBodWdlIGZhbiBvZiBgZHphZ2FtbWEyYCBzbyBsZXQncyBtYWtlIG91ciBvd24gdXNpbmcgdGhlIHJhdGUgZm9ybXVsYXRpb24uIFRvIGRvIHRoaXMsIEkgbGl0ZXJhbGx5IGp1c3QgZW50ZXJlZCBgZHphbW1hMmAgaW4gUiwgdGhlbiByZXdyb3RlIHRoZSBmdW5jdGlvbiBhIGJpdCB0byB1c2UgdGhlIHJhdGUgZm9ybXVsYXRpb24uCgpgYGB7ciBkemFnYW19CgpkemFnYW1tYSA8LSBmdW5jdGlvbiAoeCwgcHJvYiwgc2hhcGUsIHJhdGUsIGxvZyA9IEZBTFNFKSAKewogIEsgPC0gYXMuZGF0YS5mcmFtZShjYmluZCh4ID0geCwgcHJvYiA9IHByb2IsIHNoYXBlID0gc2hhcGUsIHJhdGUgPSByYXRlKSkKICBsbGcgPC0gZGdhbW1hKHgsIHNoYXBlID0gc2hhcGUsIHJhdGUgPSByYXRlLCBsb2cgPSBUUlVFKQogIGxsIDwtIGlmZWxzZShLJHggPT0gMCwgbG9nKEskcHJvYiksIGxvZygxIC0gSyRwcm9iKSArIGxsZykKICBpZiAobG9nID09IEZBTFNFKSAKICAgIGxsIDwtIGV4cChsbCkKICBsbAp9CmBgYAoKTGV0IHRoaXMgYmUgYSBsZXNzIC0geW91IGNhbiBwdXQgaW4gd2hhdGV2ZXIgZGVuc2l0eSBmdW5jdGlvbiB5b3Ugd2FudCAtIGlmIFIgZG9lc24ndCBoYXZlIGl0LCB3cml0ZSB5b3VyIG93biEgSGVjaywgdGhpcyBmb3JtYXQgaXMgYSByZWFsbHkgc3RyYWlnaHRmb3J3YXJkIHdheSB0byB0dXJuIGFueSBkaXN0cmlidXRpb24gLSBub3JtYWwsIHdoYXRldmVyIC0gaW50byBhIHplcm8gaW5mbGF0ZWQgZGlzdHJpYnV0aW9uLgoKSW4gdGhpcyBkaXN0cmlidXRpb24sIHdlJ2xsIGhhdmUgYSBsb3Qgb2YgZXh0cmEgMHMuIFdoYXQncyBncmVhdCBpcyB0aGF0IHdlIGNhbiBhcHByb3ByaWF0ZSBvdXIgZWFybGllciBtb2RlbCBmb3IgdXNlIGhlcmUuCgpgYGB7ciB6YWdfbW9kfQpmaXNoaW5nX3phZ2FtbWFfbW9kIDwtIGFsaXN0KAogICNsaWtlbGlob29kCiAgI25vdGluZyB0aGF0IHJhdGUgPSBzaGFwZS9leHAobG9nX211KQogIGRheXNfYXRfc2VhIH4gZHphZ2FtbWEyKHByb2IsIG11LCBzY2FsZSksCiAgCiAgI2RhdGEgZ2VuZXJhdGluZyBwcm9jZXNzCiAgbG9nKG11KSA8LSBhICsgYipzZWFzX20sCiAgCiAgI3ByaW9ycwogIGEgfiBkbm9ybSgwLDEwMCksCiAgYiB+IGRub3JtKDAsMTAwKSwKICBzY2FsZSB+IGRleHAoMiksCiAgcHJvYiB+IGR1bmlmKDAsMSkKKQpgYGAKCldlIGhhdmUgbm8gaW5mbyBvbiB0aGUgcHJvYmFiaWxpdHksIHNvLCB3ZSdyZSBqdXN0IHB1dHRpbmcgaW4gYSBmbGF0IHVuaWZvcm0gcHJpb3IgYmV0d2VlbiAwIGFuZCAxLiBXZSBjb3VsZCB1c2UgYSBiZXRhIGlmIHdlIHdhbnRlZCwgYnV0LCBtZWguIFdlIGNhbiB0aGVuIGZpdCB0aGlzLCB1c2luZyBvdXIgbGVzc29ucyBhYm91dCBzdGFydCB2YWx1ZXMgZnJvbSBiZWZvcmUuCgpgYGB7ciB6YWdfZml0fQpmaXNoaW5nX3phZ2FtbWFfZml0IDwtIHF1YXAoZmlzaGluZ196YWdhbW1hX21vZCwgZGF0YT1maXNoaW5nLAogICAgICAgICAgICAgICAgICAgICAgICAgc3RhcnQgPSBsaXN0KGE9MSwgYj0tMSkpCmBgYAoKIyMjIDYuNCBFdmFsdWF0aW9uCgpMZXQncyBsb29rIGF0IHRob3NlIHBvc3RlcmlvcnMhCgpgYGB7ciBwYWlyc196YWd9CnBhaXJzKGZpc2hpbmdfemFnYW1tYV9maXQpCmBgYAoKVGhlIHBhaXJzIHBsb3QgbG9va3MgbmljZSBhbmQgd2VsbCBiZWhhdmVkLiBPdGhlciBjaGVja3MgYW5kIGBzaW1gIGFyZSBhIGJpdCBtb3JlIGRpZmZpY3VsdCwgYXMgdGhleSByZXF1aXJlIGEgcmFuZG9tIG51bWJlciBnZW5lcmF0b3Igd2hpY2ggd2UgY3VycmVudGx5Li4uIGRvbid0IGhhdmUuIFNvLCBsZXQncyBtYWtlIG9uZS4KCmBgYHtyIHJ6YWd9CnJ6YWdhbW1hMiA8LSBmdW5jdGlvbihuLCBwcm9iLCBzY2FsZSwgcmF0ZSl7CiAgaXNfemVybyA8LSByYmlub20obiwgMSwgcHJvYj0xLXByb2IpCiAgaXNfemVybyAqIHJnYW1tYShuLCBzY2FsZSA9IHNjYWxlLCByYXRlKQp9CmBgYAoKV2l0aCB0aGlzLCB3ZSBjYW4gdXNlIHBvc3RjaGVjawoKYGBge3IgcG9zdGNoZWNrX3phZ30KcGFyKG1mcm93PWMoMywzKSkKcG9zdGNoZWNrKGZpc2hpbmdfemFnYW1tYV9maXQpCnBhcihtZnJvdz1jKDEsMSksIGFzaz1GKQpgYGAKCkhleSAtIGl0J3MgYWxsIG9mIHRoZSBkYXRhISBBbmQgaXQgbG9va3MgLSBnb29kISAKCgojIyMjIDYuNSBXaGF0IGRvIHRoZSByZXN1bHRzIHNheT8KCldlIGNhbiBmaXJzdCBsb29rIGF0IHRoZSBnZW5lcmFsIHJlc3VsdHMKCmBgYHtyIHByZWNpc196Z2FtbWF9CnByZWNpcyhmaXNoaW5nX3phZ2FtbWFfZml0KQpgYGAKCk5vIHN1cnByaXNlLCBvdXIgY29lZmZpY2llbnRzIGFyZSBleGFjdGx5IHRoZSBzYW1lIGFzIGJlZm9yZSEgV2Fob28hIEV4Y2VwdCBub3cgd2UgYWxzbyBrbm93IHRoZXJlJ3MgYSAzMCUgY2hhbmNlIG9uIGFueSBnaXZlbiBkYXkgdGhhdCBhIGZpc2hlcm1hbiB3aWxsIGp1c3Qgc2F5ICdtZWgnIGFuZCBub3QgZ28gZG8gc2VhLgoKV2UgY2FuIGFsc28gdmlzdWFsaXplIHRoZSByZXN1bHRzLiBGaXJzdCwgd2Ugd2lsbCBwbG90IHRoZSBwcmVkaWN0aW9uIGZvciBKVVNUIHRoZSBub24temVybyByZXN1bHRzIHVzaW5nIHRoZSBmaXR0ZWQgbW9kZWwuIFRoZW4gd2Ugd2lsbCBjb250cmFzdCBpdCB0byBwdXR0aW5nIGluIHRoZSBmdWxsIDAncyBwcmVkaWN0aW9uLCB3aGljaCBjb21lcyBmcm9tIHRoZSBaQUcgZGlzdHJpYnRpb24uCgpgYGB7cn0KIyBhIHByZWRpY3Rpb24gZnJhbWUKcHJlZF96YWcgPC0gcHJlZGljdGVkX2RyYXdzKGZpc2hpbmdfemFnYW1tYV9maXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhLmZyYW1lKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWFzX20gPSBzZXEoMCwgNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlbmd0aC5vdXQgPSAxMDApKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gImRheXNfYXRfc2VhIikKCgojIGEgbGlucHJlZCBmcmFtZQpmaXRfemFnIDwtIGxpbnByZWRfZHJhd3MoZmlzaGluZ196YWdhbW1hX2ZpdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEuZnJhbWUoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlYXNfbSA9IHNlcSgwLCA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoLm91dCA9IDEwMCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSAiZGF5c19hdF9zZWEiKQoKIyBmaXQgaW50ZXJ2YWwgb2YgbGluayAtIG5vdCB3aXRoIFpBRwpnZ3Bsb3QoZml0X3phZywKICAgICAgIGFlcyh4ID0gc2Vhc19tLCB5ID0gZGF5c19hdF9zZWEpKSArCiAgc3RhdF9saW5lcmliYm9uKCkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiR3JlZW5zIikgKwogIGdlb21fcG9pbnQoZGF0YSA9IGZpc2hpbmcpCgojIHByZWRpY3Rpb24gaW50ZXJ2YWwKZ2dwbG90KHByZWRfemFnLAogICAgICAgYWVzKHggPSBzZWFzX20sIHkgPSBkYXlzX2F0X3NlYSkpICsKICBzdGF0X2xpbmVyaWJib24oKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJPcmFuZ2VzIikgKwogIGdlb21fcG9pbnQoZGF0YSA9IGZpc2hpbmcpCmBgYAoKTm90ZSB0aGUgZGlmZmVyZW5jZSBpbiBDSSB3aWR0aCBhbmQgZG93bndlaWdodGluZyBvZiB0aGUgbGluZSB3aXRoIGp1c3QgZml0LiBUaGVyZSBpcyBhIGxvdCBtb3JlIGdvaW5nIG9uIGhlcmUsIGFuZCB0aGUgZnVsbCBwcmVkaWN0aW9uIGludGVydmFsIGdpdmVzIHVzIHdoYXQgd2UgbmVlZCB0byBzZWUuCgo8IS0tCgpgYGB7ciBmaXQsIHJlc3VsdHM9ImhpZGUifQojc3RhcnQgd2l0aCBhIGZyZXNoIGRhdGEgZnJhbWUgb2YgcHJlZGljdGVkIHZhbHVlcwpwcmVkX2RmX3ogPC0gZGF0YS5mcmFtZShzZWFzX20gPSBzZXEoMCw1LGxlbmd0aC5vdXQ9MjAwKSkKCiNPSywgbm93IGdldCBzb21lIHNpbSBhbmQgbGluayBvdXRwdXQKc2ltX2Zpc2hpbmdfeiA8LSBzaW0oZmlzaGluZ196YWdhbW1hX2ZpdCwgZGF0YT1wcmVkX2RmX3opCgojbm90ZSwgZm9yIGxpbmssIHdlIG5lZWQgdG8gZXhwb25lbnRpYXRlCmxpbmtfZmlzaGluZ196IDwtIGV4cChsaW5rKGZpc2hpbmdfemFnYW1tYV9maXQsIGRhdGE9cHJlZF9kZl96KSkKCiNzbW9vc2ggaXQgYWxsIHRvZ2V0aGVyCnByZWRfZGZfeiA8LSBwcmVkX2RmX3ogJT4lCiAgbXV0YXRlKGRheXNfYXRfc2VhID0gYXBwbHkobGlua19maXNoaW5nX3osIDIsIG1lZGlhbiksCiAgICAgICAgIGx3cl9maXQgPSBhcHBseShsaW5rX2Zpc2hpbmdfeiwgMiwgSFBESSlbMSxdLAogICAgICAgICB1cHJfZml0ID0gYXBwbHkobGlua19maXNoaW5nX3osIDIsIEhQREkpWzIsXSwKICAgICAgICAgbHdyID0gYXBwbHkoc2ltX2Zpc2hpbmdfeiwgMiwgSFBESSlbMSxdLAogICAgICAgICB1cHIgPSBhcHBseShzaW1fZmlzaGluZ196LCAyLCBIUERJKVsyLF0KICAgICAgICAgKQoKI2EgbmljZSBnZ3Bsb3QKZ2dwbG90KGRhdGE9cHJlZF9kZl96LAogICAgICAgbWFwcGluZz1hZXMoeD1zZWFzX20sIHk9ZGF5c19hdF9zZWEpKSArCiAgZ2VvbV9yaWJib24obWFwcGluZz1hZXMoeW1pbj1sd3IsIHltYXg9dXByKSwgYWxwaGE9MC40KSArCiAgZ2VvbV9yaWJib24obWFwcGluZz1hZXMoeW1pbj1sd3JfZml0LCB5bWF4PXVwcl9maXQpLCBmaWxsPSJibHVlIiwgYWxwaGE9MC40KSArCiAgZ2VvbV9saW5lKGx3ZD0xLCBjb2xvcj0icmVkIikgKwogIGdlb21fcG9pbnQoZGF0YT1maXNoaW5nKSArCiAgdGhlbWVfYncoYmFzZV9zaXplPTE3KQpgYGAKCkl0J3MgaGFyZCB0byBzZWUgdGhlIGRpZmZlcmVuY2UgcmVsYXRpdmUgdG8gdGhlIHByZXZpb3VzIHBsb3QsIGJ1dCwgaWYgeW91IHRha2UgYSBnYW5kZXIsIHRoZSBzY2FsZSBvZiB0aGUgcHJlZGljdGl2ZSBpbnRlcnZhbCBoYXMgc2hydW5rIGEgYml0LiBPciwgZ2V0cyBwdWxsZWQgdG93YXJkcyB6ZXJvLCBhcyBpdCB3ZXJlLCBhcyB3ZSBoYXZlIGEgbG90IG9mIHplcm9lcy4gTWFrZXMgc2Vuc2UhCi0tPg==