Introduction: Latent Attrition

In this section, we’ll focus on non-contractual settings, where the time at which a customer dies, or becomes permanently inactive, is not observed by the firm. We will focus on one class of models called latent attrition or “Buy Til You Die” models. All these models share a common feature:

The customer’s relationship with a firm has two phases: a customer is active for some period of time, then becomes permanently inactive.

These models are a subset of Hidden Markov models where the two states-alive and dead-are not directly observed, hence hidden. The classic paper that launched this literature created the Pareto/NBD model, which is in continuous time.1

A better place to start for us however is the Beta-geometric/Beta-binomial (BG/BB) model, because it is in discrete time and has building blocks similar to the sBG model we covered in the last session.

The BG/BB model

The best place to start off is the Beta-geometric/Beta-Binomial model (BG/BB), which is in discrete time. This is the same discrete time we used with the sBG (shifted Beta-geometric) model for contractual settings. The assumptions of the model are given in the research paper2:

  1. A customer’s relationship with the firm has two phases: he is alive (A) for some period of time, then becomes permanently inactive (“dies”; D).

  2. While alive, a customer makes a purchase with probability \(p\) in any given period: \[ P(Y(t) = 1 \mid p, \textrm{alive at} \; t) = p, \quad 0 \leq p \leq 1 \] This implies that a customer alive for \(s\) periods makes a number of purchases according to a Binomial\((s, p)\) distribution.

  3. A “living” customer dies at the beginning of a transaction opportunity with probability . (This implies that the (unobserved) lifetime of a customer is characterized by a geometric distribution.3) \[ P( \textrm{alive at} \; t \mid \theta)= P(T>t \mid \theta) = S(t \mid \theta ) = (1-\theta)^t \qquad 0 \leq \theta \leq 1 \] As an aside, our one-step Markov transition matrix from alive to dead looks like: \[ T = \left(\begin{array}{cc} 1-\theta & \theta \\ 0 & 1 \end{array}\right) \] Given that the customer was alive last period (top row), a customer stays alive each period with probability \(1-\theta\), and dies with probability \(\theta\). If the customer was dead last period (bottom row), he or she remains dead with probability 1.

  4. Heterogeneity in \(p\) follows a beta distribution with parameters \(\alpha\) and \(\beta\). \[ f(p \; | \; \alpha,\beta) = \frac{p^{\alpha-1} (1-p)^{\beta-1}}{B(\alpha,\beta)}, \qquad \alpha>0, \beta>0\]

  5. Heterogeneity in \(\theta\) follows a beta distribution with parameters \(\gamma\) and \(\delta\). \[ f(\theta \; | \; \gamma,\delta) = \frac{\theta^{\gamma-1} (1-\theta)^{\delta-1}}{B(\gamma,\delta)}, \qquad \gamma>0, \delta>0\]

  6. The purchase probability \(p\) and the dropout probability \(\theta\) vary independently across customers.

Likelihood BG/BB

The likelihood is a function of three summary statistics:

  1. \(n\) transaction opportunities

  2. \(t_x\), recency, the period of the last donation relative to the first donation. (This is opposite to how we usually think of recency, more below.)

  3. \(x\), frequency, the total number of donations

For each \(\{n, t_x, x\}\), we sum over the possible hidden state realizations, alive and dead:

\[ L(\alpha, \beta, \gamma, \delta \mid n, t_x, x) = \underbrace{\frac{B(\alpha+x, \beta + n-x)}{B(\alpha, \beta)} \frac{B(\gamma, \delta + n)}{B(\gamma, \delta)}}_\text{alive all periods} + \underbrace{ \sum_{i=0}^{n-t_x-1} \; \frac{B(\alpha+x, \beta + t_x -x + i )}{B(\alpha, \beta)} \frac{B(\gamma+1, \delta + t_x+i)}{B(\gamma, \delta)}}_\text{all paths with death before end} \] If \(x=0, t_x=0\). This recency is measured as the time of last purchase from the beginning of the observation period; in the past we’ve measured recency as the time from last purchase until the end of the observation period (\(n-t_x\)).

If there was a donation in the last period, \(t_x=n\), then \(n-t_x-1=-1\), meaning the upper limit of the sum in the second term is lower than the lower limit, and by convention, the term drops out. This means that he/she must be alive at the end of the last period, since only alive customers can make donations. In that case,

\[ L(\alpha, \beta, \gamma, \delta \mid n, t_x=n, x) = \frac{B(\alpha+x, \beta + n-x)}{B(\alpha, \beta)} \frac{B(\gamma, \delta + n)}{B(\gamma, \delta)} \]

Loading the data

First we have to load the R package, BTYD and some auxiliary packages as well. You can download the package from here:

install.packages('https://cran.r-project.org/src/contrib/Archive/BTYD/BTYD_2.4.tar.gz', repos = NULL, type = "source")

You can load the package:

library("BTYD")
options("scipen"=100, "digits"=3, width = 300)

Load the donation data, and get the the calibration period recency-frequency matrix:

data(donationsSummary)

rf.matrix <- donationsSummary$rf.matrix
head(rf.matrix) %>% 
  kbl() %>%
  kable_styling()
x t.x n.cal custs
6 6 6 1203
5 6 6 728
4 6 6 512
3 6 6 357
2 6 6 234
1 6 6 129

If \(f_j\) is the number of customers in each of the \(J\) recency-frequency cells in the rf.matrix as above (\(f_1 =\) 1203, \(f_2 =\) 728, …) the sample log likelihood is then:

\[ LL(\alpha, \beta, \gamma, \delta) = \sum_{j=1}^J f_j \log[L(\alpha, \beta, \gamma, \delta \mid n, t_x, x)] \]

We maximize this to find the parameters \(\{\alpha, \beta, \gamma, \delta\}\).

Here is the donation data we mentioned in the lecture. All donors in this cohort made their first donation in 1995. What we have is their repeat donation history 1996-2006. We fit the model to only the repeat data, not the first donation.

par(mfrow=c(1,1))
par(mai=c(.8,.8,.2,.2))
plot(seq(1996,2006,1),donationsSummary$annual.trans, type="b", ylab="Total number of repeat transactions", xlab="Year", main="", xaxt='n')
x.tickmarks.yrs.all <- c( "'96","'97","'98","'99","'00","'01","'02","'03","'04","'05","'06" )
#axis(1, at = seq(0, 11, by = 1))
axis(1, at=seq(1996,2006,1),labels = x.tickmarks.yrs.all)
abline(v=2001.5,col = "red", lwd = 2)
text(x = 1999,y = 5000,"Calibration", cex=1, pos=3, col="black", font = 2)
text(x = 2004,y = 5000,"Validation", cex=1, pos=3, col="black", font = 2)

Our calibration data is 1996-2001, so it lasts 6 periods (\(n=6\)). We validate the model using years 2002-2006. All we need to estimate the model are the sufficient statistics:

  • “reverse” recency (\(t_x\)): the last period a donation occurred. Since are data comprise 6 periods, the most recent donation is 6, or \(t_x=6\). If no repeat donations were observed, \(t_x = 0\). (Usually marketers think of recency as the number of periods since last purchase, \(n-t_x\). Here recency is time after first purchase.) Note further that if \(x=0, \; t_x=0\).
  • frequency (\(x\)): the number of repeat donations observed in the six subsequent periods.
  • number of purchase opportunities (\(n\)): this is usually the same for everyone. In this case \(n=6\).

There are 22 recency-frequency combinations. The number of customers in each cell is below.

You can see, for example, that there are 1203 customers who are “6 for 6”. And there are 3464 customers who made no repeat donations, “0 for 6”. The model is going to have to account for these differences.

Estimate parameters for the BG/BB model from the recency-frequency matrix:

We give initial guesses to the four parameters in par.start. bgbb.EstimateParameters estimates the parameters.

#            alpha beta gamma delta
par.start <- c(1, .5, 1, .5)

params <- bgbb.EstimateParameters(rf.matrix, par.start)

## store parameters next to names
names(params) <- c("alpha", "beta", "gamma", "delta");

round(params,2)
## alpha  beta gamma delta 
##  1.20  0.75  0.66  2.78
## Check log-likelihood of the params:
LL <- bgbb.rf.matrix.LL(params, rf.matrix)

Parameter Estimates and Distributions

We plot the beta distributions implied by the maximum likelihood estimates.

par(mfrow=c(1,2))
par(mai=c(.8,.8,.5,.2))
temp <- bgbb.PlotTransactionRateHeterogeneity(params)
par(mai=c(.8,.8,.5,.2))
temp <- bgbb.PlotDropoutRateHeterogeneity(params)

Remember, if \(X \sim \textrm{Beta}(a,b), \; E[X] = \frac{a}{a+b}\). So the mean of the transaction rate while alive is 0.62 and the mean of the drop out process is 0.19.

# Mean of transaction rate while alive:
params[1]/(params[1]+params[2])
## alpha 
## 0.616
# Mean of drop-out process:
params[3]/(params[3]+params[4])
## gamma 
## 0.191

Model fit

Aggregate

We can see how well the model does in predicting the aggregate number of donations over years. This uses equation 8 in the FHS (2010).

inc.annual.trans <- donationsSummary$annual.trans   # incremental annual transactions

par(mfrow=c(1,1))
## Plot the comparison of actual and expected total incremental transactions across
## both the calibration and holdout periods:
par(mai=c(.8,.8,.3,.2))

pred <- bgbb.PlotTrackingInc(params, rf.matrix, inc.annual.trans, xticklab=x.tickmarks.yrs.all)[2,]

text(x = 4,y = 5000,"Calibration", cex=1, pos=3, col="black", font = 2)
text(x = 7,y = 5000,"Validation", cex=1, pos=3, col="black", font = 2)

# The incremental transactions using the formula, equation 8, in the paper.  donations should equal pred above.

al <- params[1]
be <- params[2]
ga <- params[3]
de <- params[4]

nn <- seq(1,11)
N <- sum(rf.matrix[,4])
Eq8 <- (al/(al+be))*(de/(ga-1))*(1-(gamma(ga+de)*gamma(1+de+nn)/(gamma(ga+de+nn)*gamma(1+de))))

cum_donations <- N*Eq8
donations <- c(cum_donations[1],diff(cum_donations))

Here is for the cumulative number of donations.

par(mai=c(.8,.8,.3,.2))

pred <- bgbb.PlotTrackingCum(params, rf.matrix, actual.cum.repeat.transactions = cumsum(donationsSummary$annual.trans), xticklab=x.tickmarks.yrs.all)[2,]

text(x = 4,y = 5000,"Calibration", cex=1, pos=3, col="black", font = 2)
text(x = 7,y = 5000,"Validation", cex=1, pos=3, col="black", font = 2)

Conditional Expectations

A very important test of a model is how well it predicts at the individual level, after conditioning on a particular individual history. Given a customer with history \((x, t_x, n)\), (a) how many purchases do we predict in the next \(n^*\) periods and (b) how well does it track actual holdout purchases?

We first do (a). Using equation 13, we can calculate the expected number of purchases for each \((x, t_x, n)\) group in the next \(n^* = 5\) periods.

par(mai=c(.8,.8,.5,.2))
comp <- bgbb.HeatmapHoldoutExpectedTrans(params, n.cal=6, n.star=5)
## layout: widths =  0.05 4 , heights =  0.25 4 ; lmat=
##      [,1] [,2]
## [1,]    0    3
## [2,]    2    1

# rotate matrix so it's the same direction as the heatmap, this is just to make the numbers easier to read
rotate <- function(x) t(apply(x, 2, rev))
library(kableExtra)
test <- kable(rotate(rotate(rotate(t(round(comp,2))))), format = "pipe", booktabs=F, align = "c", caption = "**Predicted holdout purchases (BG/BB Model) based on frequency (rows) x recency (columns)**")

A donor who donated every year except the last \((x=5, t_x=5, n=6)\) is predicted to make 1.81 in the next \(n^*=5\) periods. Yet a donor with better recency but lower frequency \((x=4,t_x=6, n=6)\) has a higher expected transaction rate, 2.71. As mentioned in Fader, Hardie and Shang (2010), this highlights the importance of recency.

Now we do (b). The actual number of total holdout purchases by customers in each RF category is given in the variable x.star. Therefore the average number of holdout purchases per RF category is the total divided by the number of customers. We add this to the RF matrix and reshape.

n.star <- 5                        # Number of transaction opportunities in the holdout period
x.star <- donationsSummary$x.star  # Transactions made by each calibration period bin in the holdout period

X<-x.star/rf.matrix[,"custs"]

hol_rf_trans<-as.data.frame(cbind(rf.matrix[,1:2],X))

actual_rf<-reshape(hol_rf_trans, idvar="t.x", timevar="x", direction="wide")

# change NA's to 0
actual_rf[is.na(actual_rf)] <- 0

# re-order columns and rows
actual_rf<-actual_rf[order(actual_rf[,1]),order(actual_rf[1,])]

# make tx to rowname
rownames(actual_rf) <- actual_rf[,8]

# delete tx
actual_rf<-actual_rf[,c(-8)]

#actual_rf

# to make it look nice and rotated in same way as heatmap
kable(rotate(rotate(rotate((round(actual_rf,2))))), format = "pipe", booktabs=F, align = "c", caption = "**Actual holdout average purchases based on frequency (rows) x recency (columns)**")
Actual holdout average purchases based on frequency (rows) x recency (columns)
0 1 2 3 4 5 6
X.6 0.00 0.00 0.00 0.00 0.00 0.00 3.53
X.5 0.00 0.00 0.00 0.00 0.00 1.74 3.06
X.4 0.00 0.00 0.00 0.00 0.84 1.91 2.72
X.3 0.00 0.00 0.00 0.46 0.94 1.66 2.29
X.2 0.00 0.00 0.40 0.46 0.74 1.41 1.89
X.1 0.00 0.22 0.37 0.60 0.56 1.14 1.47
X.0 0.17 0.00 0.00 0.00 0.00 0.00 0.00

Conditioning on frequency & recency separately

Next we can how well the model does if we condition on the frequency of transactions in the calibration period, averaging over recency. In other words, we take everyone who has had a frequency of \(x\) transactions in the calibration period, and we can compare how many actual transactions they had in the validation period with the predictions.

par(mai=c(.8,.8,.5,.2))

## Plot the comparison of actual and conditional expected holdout period frequencies,
## binned according to calibration period frequencies:
freq <- bgbb.PlotFreqVsConditionalExpectedFrequency(params, n.star, rf.matrix, x.star)

rownames(freq) <- c("act", "exp", "bin")
freq
##        freq.0   freq.1   freq.2  freq.3  freq.4  freq.5  freq.6
## act    0.1744    0.433    0.813    1.39    2.06    2.64    3.53
## exp    0.0729    0.325    0.709    1.33    2.03    2.78    3.75
## bin 3464.0000 1823.000 1430.000 1085.00 1036.00 1063.00 1203.00

Model predictions closely track actual donations. Donors who made zero donations 1996-2001, made on average 0.07 donations in 2002-2006. The BG/BB model predicts slightly fewer, 0. Donors who made a donation every year, “6 for 6”, made 0 donations in the subsequent 5 years. The model predictions are modestly higher, at 1.15. It’s interesting to note that a naive prediction of a donor who is “6 for 6” so would therefore be a “5 for 5” donor in the validation period would overestimate donations by quite a lot.

Instead of grouping customers by frequency, we can also condition on their recency, i.e., the last period they made a donation:

par(mai=c(.8,.8,.5,.2))

rec<-bgbb.PlotRecVsConditionalExpectedFrequency(params, n.star, rf.matrix, x.star)

rownames(rec) <- c("act", "exp", "bin")
rec
##         rec.0     rec.1  rec.2   rec.3   rec.4   rec.5   rec.6
## act    0.1744    0.2181   0.39   0.487   0.809    1.65    2.95
## exp    0.0729    0.0857   0.18   0.404   0.851    1.73    3.03
## bin 3464.0000 1091.0000 890.00 706.000 654.000 1136.00 3163.00

There were 1.669 donors with maximum recency, i.e., making a donation in 2001. Those donors made on average 0 donations in the subsequent 5 years, and the model predicts that they would make 1.1. There is a steep falloff as recency diminishes, moving from right to left in the graph that is captured by the model.

P(Alive)

The probability that a customer with purchase history \(x, t_x n\) will be alive at the beginning of period \(n + 1\) is the term in the likelihood where the customer is alive until the end divided by all the paths: \[ P(\textrm{Alive at} \; n+1 | \; n, x, t_x) = \frac{\frac{B(\alpha+x, \beta + n-x)}{B(\alpha, \beta)} \frac{B(\gamma, \delta + n+1)}{B(\gamma, \delta)}}{L(\alpha, \beta, \gamma, \delta \mid n, t_x, x)} \]

We can calculate for all the possible cells in our recency-frequency matrix the probability that the customer is active.

PAlive <- bgbb.PAlive(params, x = rf.matrix[,1], t.x = rf.matrix[,2], n.cal = 6)
Alive_mat<-cbind(rf.matrix,PAlive)
Alive_mat<-data.frame(Alive_mat)
kable(Alive_mat, format="pipe")
x t.x n.cal custs PAlive
6 6 6 1203 0.930
5 6 6 728 0.930
4 6 6 512 0.930
3 6 6 357 0.930
2 6 6 234 0.930
1 6 6 129 0.930
5 5 6 335 0.522
4 5 6 284 0.697
3 5 6 225 0.767
2 5 6 173 0.805
1 5 6 119 0.828
4 4 6 240 0.200
3 4 6 181 0.440
2 4 6 155 0.590
1 4 6 78 0.680
3 3 6 322 0.095
2 3 6 255 0.299
1 3 6 129 0.481
2 2 6 613 0.066
1 2 6 277 0.255
1 1 6 1091 0.069
0 0 6 3464 0.108
with(Alive_mat, hist(rep(x = Alive_mat$PAlive, times = Alive_mat$custs), xaxt='n', xlim = c(0,1), xlab = "P(Alive at n+1)", ylab="frequency", main="Histogram of P(Alive)"))
axis(side=1, at=seq(0,1, .10), labels=seq(0,1,.1))

How many total active or alive customers are there at period 7? We can sum up all the P(Alive) cells and number of customers in each cell to get the answer:

sum(Alive_mat$PAlive*Alive_mat$custs)
## [1] 4728

There are 4728.494 out of 11104 customers still active.

Increasing Frequency Paradox

Let’s imagine a customer who has made his or her last donation on period \(t_x = 4\), but let’s vary how many purchases she makes. At most she can make 4, and at least 1. We can ask what the model predicts is the number of purchases expected in the subsequent \(n^*=5\) periods, a calculation we already did above:

par(mfrow=c(1,1),mai=c(.8,.8,.5,.2))
plot(comp[2:5,5], ylab ="Expected transactions in next 5 periods", xlab="Frequency holding last donation at 4", type="b", xaxt="n")
xtick<-seq(1, 4, by=1)
axis(side=1, at=xtick, labels = TRUE)

What’s interesting about this curve is that customer with the largest frequency is not the one with the highest future predicted purchases. This is something known as the increasing frequency paradox. Why? The likelihood that he or she is still alive decreases in \(x\).

par(mfrow=c(1,1))
par(mai=c(.8,.8,.5,.2))

plot(bgbb.PAlive(params, x=1:4, t.x=4, n.cal=6), ylab ="Probability that customer is alive next period", xlab="Frequency holding last donation at 4", ylim=c(0,1), type="b", xaxt="n")
xtick<-seq(1, 4, by=1)
axis(side=1, at=xtick, labels = TRUE)

On the one hand, higher \(x\) means a higher \(p\) which means more expected transactions in the future. On the other hand, if the last two periods were no purchases, a higher \(x\) means that \(P(alive)\) is lower. This second effect is stronger than the first effect, resulting in a lower expectations when \(x=4\) compared to when \(x=2,3\).

CLV

Given model assumptions 2 and 3, we know that the probability of making a purchase is equal to the probability that a customer is alive times the probability of making a purchase conditional on being alive: \[ P(\, Y(t) = 1 \mid p, \theta) = p \, (1-\theta)^t \] We integrate \(p\) and \(\theta\) over their mixing distributions to get the proability for a randomly chosen customer: \[ \begin{array}{ccl} P(\, Y(t) = 1 \mid \alpha, \beta, \gamma, \delta) &=& \displaystyle \int_0^1 \int_0^1 P(\, Y(t) = 1 \mid p, \theta) \, f(p \; | \; \alpha,\beta) \, f(\theta \; | \; \gamma,\delta) \, dp \, d\theta \\ &=& \displaystyle \left(\frac{\alpha}{\alpha+\beta}\right) \frac{B(\gamma, \delta+t)}{B(\gamma, \delta)} \end{array} \]

CLV is then the discounted sum of the probability of making a transaction times some average amount per transaction (\(m\)):

\[ \begin{array}{ccl} E[CLV] & = & m \; \left( 1 + \sum_{t=1}^{\infty} P(\, Y(t) = 1 \mid \alpha, \beta, \gamma, \delta) \frac{1}{(1+d)^t} \right) \\ & = & m \; \times \textrm{DET} \end{array} \]

DET means Discounted Expected Transactions. For implementing this in R, we have to choose some upper bound to the sum, i.e. \(T=200\).

BGBBCLV<-function(params,m,d,T) {
params<-unname(params)
al<-params[1]
be<-params[2]
ga<-params[3]
de<-params[4]
DET<-1   # at time zero there has to be a purchase
for (i in 1:T) {
    DET<-DET+(al/(al+be))*(beta(ga,de+i)/beta(ga,de))*1/(1+d)^{i}
}
CLV=m*DET  # convert discount expected purchases into expected value
return(CLV)    #return the CLV
}
CLV <- BGBBCLV(params = params, m=50,d=.1,T=200)

CLV for a random customer with parameters as esimated, \(m=€50, d=.1, T=200\) is €185.

RLV

Lastly we can calculate the residual lifetime value of a donor with history \((x,t_x,n)\). The residual lifetime value is the present value of the expected future transaction stream standing at time \(t\). \[ \begin{array}{ccl} E[RLV] & = & \displaystyle m \; \left ( P(\textrm{alive at} \, n) \; \sum_{t=n+1}^{\infty} \; P(Y_t = 1 \mid \textrm{alive at} \, t) \frac{P(\textrm{alive at} \, t \mid t>n)}{(1+d)^{t-n}} \right)\\ & = & \displaystyle m \times \textrm{DERT} \end{array} \]

DERT means Discounted expected residual transactions. Here is what it looks like for our sample:

m <- 50
DERT <- bgbb.rf.matrix.DERT(params, donationsSummary$rf.matrix, d=0.1)
RLV <- m*DERT
RLV_mat <- cbind(Alive_mat,DERT, RLV)
RLV_mat <- data.frame(RLV_mat)
kable(RLV_mat, format="pipe")
x t.x n.cal custs PAlive DERT RLV
6 6 6 1203 0.930 5.910 295.48
5 6 6 728 0.930 5.089 254.46
4 6 6 512 0.930 4.269 213.44
3 6 6 357 0.930 3.448 172.42
2 6 6 234 0.930 2.628 131.40
1 6 6 129 0.930 1.808 90.38
5 5 6 335 0.522 2.855 142.74
4 5 6 284 0.697 3.197 159.84
3 5 6 225 0.767 2.842 142.10
2 5 6 173 0.805 2.272 113.62
1 5 6 119 0.828 1.609 80.44
4 4 6 240 0.200 0.918 45.92
3 4 6 181 0.440 1.629 81.46
2 4 6 155 0.590 1.665 83.27
1 4 6 78 0.680 1.322 66.09
3 3 6 322 0.095 0.352 17.60
2 3 6 255 0.299 0.844 42.21
1 3 6 129 0.481 0.935 46.76
2 2 6 613 0.066 0.188 9.38
1 2 6 277 0.255 0.495 24.74
1 1 6 1091 0.069 0.135 6.75
0 0 6 3464 0.108 0.115 5.74
maxround=round(max(RLV),-2)
RLV_mat$RLV[1] 
## [1] 295
with(RLV_mat, hist(rep(x = RLV_mat$RLV, times = RLV_mat$custs), xaxt='n', xlim = c(0,maxround), xlab = "RLV ($)", ylab="frequency", main="Histogram of Residual Lifetime Value (RLV)"))
axis(side=1, at=seq(0,maxround, 50), labels=seq(0,maxround, 50))

If we assume \(m=50\) is the value of a donation and we use a yearly discount rate of \(d=0.1\), the RLV of a customer who makes “6 for 6” repeat donations is €295.48.


  1. Schmittlein, D., Morrison, D., & Colombo, R. (1987). Counting Your Customers: Who Are They and What Will They Do Next? Management Science, 33(1), 1-24.↩︎

  2. Fader, Peter S., Bruce G.S. Hardie, and Jen Shang. “Customer-Base Analysis in a Discrete-Time Noncontractual Setting.” Marketing Science, 29(6), pp. 1086-1108. 2010. INFORMS. link↩︎

  3. It’s not shifted because 0 is a valid outcome.↩︎

LS0tDQp0aXRsZTogIlR1dG9yaWFsIDc6IENMViAtIE5vbi1jb250cmFjdHVhbCBzZXR0aW5ncyINCmRhdGU6ICIyMDIzLTAxLTI2Ig0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgd2FybmluZyA9IEZBTFNFLA0KICBvdXQud2lkdGggPSAiMTAwJSIsDQogIGZpZy5hbGlnbiA9ICJjZW50ZXIiKQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KYGBgDQoNCiMgSW50cm9kdWN0aW9uOiBMYXRlbnQgQXR0cml0aW9uDQoNCkluIHRoaXMgc2VjdGlvbiwgd2UnbGwgZm9jdXMgb24gbm9uLWNvbnRyYWN0dWFsIHNldHRpbmdzLCB3aGVyZSB0aGUgdGltZSBhdCB3aGljaCBhIGN1c3RvbWVyIGRpZXMsIG9yIGJlY29tZXMgcGVybWFuZW50bHkgaW5hY3RpdmUsIGlzIG5vdCBvYnNlcnZlZCBieSB0aGUgZmlybS4gV2Ugd2lsbCBmb2N1cyBvbiBvbmUgY2xhc3Mgb2YgbW9kZWxzIGNhbGxlZCBsYXRlbnQgYXR0cml0aW9uIG9yICJCdXkgVGlsIFlvdSBEaWUiIG1vZGVscy4gQWxsIHRoZXNlIG1vZGVscyBzaGFyZSBhIGNvbW1vbiBmZWF0dXJlOg0KDQo+IFRoZSBjdXN0b21lcidzIHJlbGF0aW9uc2hpcCB3aXRoIGEgZmlybSBoYXMgKip0d28qKiBwaGFzZXM6IGEgY3VzdG9tZXIgaXMgYWN0aXZlIGZvciBzb21lIHBlcmlvZCBvZiB0aW1lLCB0aGVuIGJlY29tZXMgcGVybWFuZW50bHkgaW5hY3RpdmUuDQoNClRoZXNlIG1vZGVscyBhcmUgYSBzdWJzZXQgb2YgSGlkZGVuIE1hcmtvdiBtb2RlbHMgd2hlcmUgdGhlIHR3byBzdGF0ZXMtYWxpdmUgYW5kIGRlYWQtYXJlIG5vdCBkaXJlY3RseSBvYnNlcnZlZCwgaGVuY2UgaGlkZGVuLiBUaGUgY2xhc3NpYyBwYXBlciB0aGF0IGxhdW5jaGVkIHRoaXMgbGl0ZXJhdHVyZSBjcmVhdGVkIHRoZSBQYXJldG8vTkJEIG1vZGVsLCB3aGljaCBpcyBpbiBjb250aW51b3VzIHRpbWUuW14xXQ0KDQpbXjFdOiBTY2htaXR0bGVpbiwgRC4sIE1vcnJpc29uLCBELiwgJiBDb2xvbWJvLCBSLiAoMTk4NykuIENvdW50aW5nIFlvdXIgQ3VzdG9tZXJzOiBXaG8gQXJlIFRoZXkgYW5kIFdoYXQgV2lsbCBUaGV5IERvIE5leHQ/IE1hbmFnZW1lbnQgU2NpZW5jZSwgMzMoMSksIDEtMjQuDQoNCkEgYmV0dGVyIHBsYWNlIHRvIHN0YXJ0IGZvciB1cyBob3dldmVyIGlzIHRoZSBCZXRhLWdlb21ldHJpYy9CZXRhLWJpbm9taWFsIChCRy9CQikgbW9kZWwsIGJlY2F1c2UgaXQgaXMgaW4gKmRpc2NyZXRlIHRpbWUqIGFuZCBoYXMgYnVpbGRpbmcgYmxvY2tzIHNpbWlsYXIgdG8gdGhlIHNCRyBtb2RlbCB3ZSBjb3ZlcmVkIGluIHRoZSBsYXN0IHNlc3Npb24uDQoNCiMgVGhlIEJHL0JCIG1vZGVsDQoNClRoZSBiZXN0IHBsYWNlIHRvIHN0YXJ0IG9mZiBpcyB0aGUgQmV0YS1nZW9tZXRyaWMvQmV0YS1CaW5vbWlhbCBtb2RlbCAoQkcvQkIpLCB3aGljaCBpcyBpbiBkaXNjcmV0ZSB0aW1lLiBUaGlzIGlzIHRoZSBzYW1lIGRpc2NyZXRlIHRpbWUgd2UgdXNlZCB3aXRoIHRoZSBzQkcgKHNoaWZ0ZWQgQmV0YS1nZW9tZXRyaWMpIG1vZGVsIGZvciBjb250cmFjdHVhbCBzZXR0aW5ncy4gVGhlIGFzc3VtcHRpb25zIG9mIHRoZSBtb2RlbCBhcmUgZ2l2ZW4gaW4gdGhlIHJlc2VhcmNoIHBhcGVyW14yXToNCg0KW14yXTogRmFkZXIsIFBldGVyIFMuLCBCcnVjZSBHLlMuIEhhcmRpZSwgYW5kIEplbiBTaGFuZy4gIkN1c3RvbWVyLUJhc2UgQW5hbHlzaXMgaW4gYSBEaXNjcmV0ZS1UaW1lIE5vbmNvbnRyYWN0dWFsIFNldHRpbmcuIiAqTWFya2V0aW5nIFNjaWVuY2UqLCAqKjI5KDYpKiosIHBwLiAxMDg2LTExMDguIDIwMTAuIElORk9STVMuIFtsaW5rXShodHRwOi8vd3d3LmJydWNlaGFyZGllLmNvbS9wYXBlcnMvMDIwLykNCg0KMS4gIEEgY3VzdG9tZXIncyByZWxhdGlvbnNoaXAgd2l0aCB0aGUgZmlybSBoYXMgdHdvIHBoYXNlczogaGUgaXMgYWxpdmUgKEEpIGZvciBzb21lIHBlcmlvZCBvZiB0aW1lLCB0aGVuIGJlY29tZXMgcGVybWFuZW50bHkgaW5hY3RpdmUgKCJkaWVzIjsgRCkuDQoNCjIuICBXaGlsZSBhbGl2ZSwgYSBjdXN0b21lciBtYWtlcyBhIHB1cmNoYXNlIHdpdGggcHJvYmFiaWxpdHkgJHAkIGluIGFueSBnaXZlbiBwZXJpb2Q6ICQkDQogICAgUChZKHQpID0gMSBcbWlkIHAsIFx0ZXh0cm17YWxpdmUgYXR9IFw7IHQpID0gcCwgXHF1YWQgMCBcbGVxIHAgXGxlcSAxDQogICAgJCQgVGhpcyBpbXBsaWVzIHRoYXQgYSBjdXN0b21lciBhbGl2ZSBmb3IgJHMkIHBlcmlvZHMgbWFrZXMgYSBudW1iZXIgb2YgcHVyY2hhc2VzIGFjY29yZGluZyB0byBhIEJpbm9taWFsJChzLCBwKSQgZGlzdHJpYnV0aW9uLg0KDQozLiAgQSAibGl2aW5nIiBjdXN0b21lciBkaWVzIGF0IHRoZSBiZWdpbm5pbmcgb2YgYSB0cmFuc2FjdGlvbiBvcHBvcnR1bml0eSB3aXRoIHByb2JhYmlsaXR5IFx0aGV0YS4gKFRoaXMgaW1wbGllcyB0aGF0IHRoZSAodW5vYnNlcnZlZCkgbGlmZXRpbWUgb2YgYSBjdXN0b21lciBpcyBjaGFyYWN0ZXJpemVkIGJ5IGEgZ2VvbWV0cmljIGRpc3RyaWJ1dGlvbi5bXjNdKSAkJA0KICAgIFAoIFx0ZXh0cm17YWxpdmUgYXR9IFw7IHQgXG1pZCBcdGhldGEpPSBQKFQ+dCBcbWlkIFx0aGV0YSkgPSBTKHQgXG1pZCBcdGhldGEgKSA9ICgxLVx0aGV0YSledCBccXF1YWQgMCBcbGVxIFx0aGV0YSBcbGVxIDENCiAgICAkJCBBcyBhbiBhc2lkZSwgb3VyIG9uZS1zdGVwIE1hcmtvdiB0cmFuc2l0aW9uIG1hdHJpeCBmcm9tIGFsaXZlIHRvIGRlYWQgbG9va3MgbGlrZTogJCQNCiAgICBUID0gXGxlZnQoXGJlZ2lue2FycmF5fXtjY30gDQogICAgMS1cdGhldGEgJiBcdGhldGEgXFwgDQogICAgMCAmIDENCiAgICBcZW5ke2FycmF5fVxyaWdodCkNCiAgICAkJCBHaXZlbiB0aGF0IHRoZSBjdXN0b21lciB3YXMgYWxpdmUgbGFzdCBwZXJpb2QgKHRvcCByb3cpLCBhIGN1c3RvbWVyIHN0YXlzIGFsaXZlIGVhY2ggcGVyaW9kIHdpdGggcHJvYmFiaWxpdHkgJDEtXHRoZXRhJCwgYW5kIGRpZXMgd2l0aCBwcm9iYWJpbGl0eSAkXHRoZXRhJC4gSWYgdGhlIGN1c3RvbWVyIHdhcyBkZWFkIGxhc3QgcGVyaW9kIChib3R0b20gcm93KSwgaGUgb3Igc2hlIHJlbWFpbnMgZGVhZCB3aXRoIHByb2JhYmlsaXR5IDEuDQoNCjQuICBIZXRlcm9nZW5laXR5IGluICRwJCBmb2xsb3dzIGEgYmV0YSBkaXN0cmlidXRpb24gd2l0aCBwYXJhbWV0ZXJzICRcYWxwaGEkIGFuZCAkXGJldGEkLiAkJCBmKHAgXDsgfCBcOyBcYWxwaGEsXGJldGEpID0gXGZyYWN7cF57XGFscGhhLTF9ICgxLXApXntcYmV0YS0xfX17QihcYWxwaGEsXGJldGEpfSwgXHFxdWFkIFxhbHBoYT4wLCBcYmV0YT4wJCQNCg0KNS4gIEhldGVyb2dlbmVpdHkgaW4gJFx0aGV0YSQgZm9sbG93cyBhIGJldGEgZGlzdHJpYnV0aW9uIHdpdGggcGFyYW1ldGVycyAkXGdhbW1hJCBhbmQgJFxkZWx0YSQuICQkIGYoXHRoZXRhIFw7IHwgXDsgXGdhbW1hLFxkZWx0YSkgPSBcZnJhY3tcdGhldGFee1xnYW1tYS0xfSAoMS1cdGhldGEpXntcZGVsdGEtMX19e0IoXGdhbW1hLFxkZWx0YSl9LCBccXF1YWQgXGdhbW1hPjAsIFxkZWx0YT4wJCQNCg0KNi4gIFRoZSBwdXJjaGFzZSBwcm9iYWJpbGl0eSAkcCQgYW5kIHRoZSBkcm9wb3V0IHByb2JhYmlsaXR5ICRcdGhldGEkIHZhcnkgKippbmRlcGVuZGVudGx5KiogYWNyb3NzIGN1c3RvbWVycy4NCg0KW14zXTogSXQncyBub3Qgc2hpZnRlZCBiZWNhdXNlIDAgaXMgYSB2YWxpZCBvdXRjb21lLg0KDQojIyBMaWtlbGlob29kIEJHL0JCDQoNClRoZSBsaWtlbGlob29kIGlzIGEgZnVuY3Rpb24gb2YgdGhyZWUgc3VtbWFyeSBzdGF0aXN0aWNzOg0KDQoxLiAgJG4kIHRyYW5zYWN0aW9uIG9wcG9ydHVuaXRpZXMNCg0KMi4gICR0X3gkLCByZWNlbmN5LCB0aGUgcGVyaW9kIG9mIHRoZSBsYXN0IGRvbmF0aW9uIHJlbGF0aXZlIHRvIHRoZSBmaXJzdCBkb25hdGlvbi4gKFRoaXMgaXMgb3Bwb3NpdGUgdG8gaG93IHdlIHVzdWFsbHkgdGhpbmsgb2YgcmVjZW5jeSwgbW9yZSBiZWxvdy4pDQoNCjMuICAkeCQsIGZyZXF1ZW5jeSwgdGhlIHRvdGFsIG51bWJlciBvZiBkb25hdGlvbnMNCg0KRm9yIGVhY2ggJFx7biwgdF94LCB4XH0kLCB3ZSBzdW0gb3ZlciB0aGUgcG9zc2libGUgaGlkZGVuIHN0YXRlIHJlYWxpemF0aW9ucywgYWxpdmUgYW5kIGRlYWQ6DQoNCiQkDQpMKFxhbHBoYSwgXGJldGEsIFxnYW1tYSwgXGRlbHRhIFxtaWQgbiwgdF94LCB4KSA9IFx1bmRlcmJyYWNle1xmcmFje0IoXGFscGhhK3gsIFxiZXRhICsgbi14KX17QihcYWxwaGEsIFxiZXRhKX0gXGZyYWN7QihcZ2FtbWEsIFxkZWx0YSArIG4pfXtCKFxnYW1tYSwgXGRlbHRhKX19X1x0ZXh0e2FsaXZlIGFsbCBwZXJpb2RzfSArICBcdW5kZXJicmFjZXsgXHN1bV97aT0wfV57bi10X3gtMX0gXDsgXGZyYWN7QihcYWxwaGEreCwgXGJldGEgKyB0X3ggLXggKyBpICl9e0IoXGFscGhhLCBcYmV0YSl9IFxmcmFje0IoXGdhbW1hKzEsIFxkZWx0YSArIHRfeCtpKX17QihcZ2FtbWEsIFxkZWx0YSl9fV9cdGV4dHthbGwgcGF0aHMgd2l0aCBkZWF0aCBiZWZvcmUgZW5kfQ0KJCQgSWYgJHg9MCwgdF94PTAkLiBUaGlzIHJlY2VuY3kgaXMgbWVhc3VyZWQgYXMgdGhlIHRpbWUgb2YgbGFzdCBwdXJjaGFzZSBmcm9tIHRoZSBiZWdpbm5pbmcgb2YgdGhlIG9ic2VydmF0aW9uIHBlcmlvZDsgaW4gdGhlIHBhc3Qgd2UndmUgbWVhc3VyZWQgcmVjZW5jeSBhcyB0aGUgdGltZSBmcm9tIGxhc3QgcHVyY2hhc2UgdW50aWwgdGhlIGVuZCBvZiB0aGUgb2JzZXJ2YXRpb24gcGVyaW9kICgkbi10X3gkKS4NCg0KSWYgdGhlcmUgd2FzIGEgZG9uYXRpb24gaW4gdGhlIGxhc3QgcGVyaW9kLCAkdF94PW4kLCB0aGVuICRuLXRfeC0xPS0xJCwgbWVhbmluZyB0aGUgdXBwZXIgbGltaXQgb2YgdGhlIHN1bSBpbiB0aGUgc2Vjb25kIHRlcm0gaXMgbG93ZXIgdGhhbiB0aGUgbG93ZXIgbGltaXQsIGFuZCBieSBjb252ZW50aW9uLCB0aGUgdGVybSBkcm9wcyBvdXQuIFRoaXMgbWVhbnMgdGhhdCBoZS9zaGUgbXVzdCBiZSBhbGl2ZSBhdCB0aGUgZW5kIG9mIHRoZSBsYXN0IHBlcmlvZCwgc2luY2Ugb25seSBhbGl2ZSBjdXN0b21lcnMgY2FuIG1ha2UgZG9uYXRpb25zLiBJbiB0aGF0IGNhc2UsDQoNCiQkDQpMKFxhbHBoYSwgXGJldGEsIFxnYW1tYSwgXGRlbHRhIFxtaWQgbiwgdF94PW4sIHgpID0gXGZyYWN7QihcYWxwaGEreCwgXGJldGEgKyBuLXgpfXtCKFxhbHBoYSwgXGJldGEpfSBcZnJhY3tCKFxnYW1tYSwgXGRlbHRhICsgbil9e0IoXGdhbW1hLCBcZGVsdGEpfQ0KJCQNCg0KIyMgTG9hZGluZyB0aGUgZGF0YQ0KDQpGaXJzdCB3ZSBoYXZlIHRvIGxvYWQgdGhlIFIgcGFja2FnZSwgYEJUWURgIGFuZCBzb21lIGF1eGlsaWFyeSBwYWNrYWdlcyBhcyB3ZWxsLiBZb3UgY2FuIGRvd25sb2FkIHRoZSBwYWNrYWdlIGZyb20gaGVyZToNCg0KYGBge3IgZXZhbCA9IEZBTFNFfQ0KaW5zdGFsbC5wYWNrYWdlcygnaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvc3JjL2NvbnRyaWIvQXJjaGl2ZS9CVFlEL0JUWURfMi40LnRhci5neicsIHJlcG9zID0gTlVMTCwgdHlwZSA9ICJzb3VyY2UiKQ0KYGBgDQoNCllvdSBjYW4gbG9hZCB0aGUgcGFja2FnZToNCg0KYGBge3IgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkoIkJUWUQiKQ0Kb3B0aW9ucygic2NpcGVuIj0xMDAsICJkaWdpdHMiPTMsIHdpZHRoID0gMzAwKQ0KDQpgYGANCg0KTG9hZCB0aGUgZG9uYXRpb24gZGF0YSwgYW5kIGdldCB0aGUgdGhlIGNhbGlicmF0aW9uIHBlcmlvZCByZWNlbmN5LWZyZXF1ZW5jeSBtYXRyaXg6DQoNCmBgYHtyfQ0KZGF0YShkb25hdGlvbnNTdW1tYXJ5KQ0KDQpyZi5tYXRyaXggPC0gZG9uYXRpb25zU3VtbWFyeSRyZi5tYXRyaXgNCmhlYWQocmYubWF0cml4KSAlPiUgDQogIGtibCgpICU+JQ0KICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KDQpJZiAkZl9qJCBpcyB0aGUgbnVtYmVyIG9mIGN1c3RvbWVycyBpbiBlYWNoIG9mIHRoZSAkSiQgcmVjZW5jeS1mcmVxdWVuY3kgY2VsbHMgaW4gdGhlIHJmLm1hdHJpeCBhcyBhYm92ZSAoJGZfMSA9JCBgciByZi5tYXRyaXhbMSw0XWAsICRmXzIgPSQgYHIgcmYubWF0cml4WzIsNF1gLCAuLi4pIHRoZSBzYW1wbGUgbG9nIGxpa2VsaWhvb2QgaXMgdGhlbjoNCg0KJCQNCkxMKFxhbHBoYSwgXGJldGEsIFxnYW1tYSwgXGRlbHRhKSA9IFxzdW1fe2o9MX1eSiBmX2ogXGxvZ1tMKFxhbHBoYSwgXGJldGEsIFxnYW1tYSwgXGRlbHRhIFxtaWQgbiwgdF94LCB4KV0NCiQkDQoNCldlIG1heGltaXplIHRoaXMgdG8gZmluZCB0aGUgcGFyYW1ldGVycyAkXHtcYWxwaGEsIFxiZXRhLCBcZ2FtbWEsIFxkZWx0YVx9JC4NCg0KSGVyZSBpcyB0aGUgZG9uYXRpb24gZGF0YSB3ZSBtZW50aW9uZWQgaW4gdGhlIGxlY3R1cmUuIEFsbCBkb25vcnMgaW4gdGhpcyAqKmNvaG9ydCoqIG1hZGUgdGhlaXIgZmlyc3QgZG9uYXRpb24gaW4gMTk5NS4gV2hhdCB3ZSBoYXZlIGlzIHRoZWlyICoqcmVwZWF0KiogZG9uYXRpb24gaGlzdG9yeSAxOTk2LTIwMDYuIFdlIGZpdCB0aGUgbW9kZWwgdG8gb25seSB0aGUgKipyZXBlYXQqKiBkYXRhLCBub3QgdGhlICoqZmlyc3QgZG9uYXRpb24qKi4NCg0KYGBge3J9DQpwYXIobWZyb3c9YygxLDEpKQ0KcGFyKG1haT1jKC44LC44LC4yLC4yKSkNCnBsb3Qoc2VxKDE5OTYsMjAwNiwxKSxkb25hdGlvbnNTdW1tYXJ5JGFubnVhbC50cmFucywgdHlwZT0iYiIsIHlsYWI9IlRvdGFsIG51bWJlciBvZiByZXBlYXQgdHJhbnNhY3Rpb25zIiwgeGxhYj0iWWVhciIsIG1haW49IiIsIHhheHQ9J24nKQ0KeC50aWNrbWFya3MueXJzLmFsbCA8LSBjKCAiJzk2IiwiJzk3IiwiJzk4IiwiJzk5IiwiJzAwIiwiJzAxIiwiJzAyIiwiJzAzIiwiJzA0IiwiJzA1IiwiJzA2IiApDQojYXhpcygxLCBhdCA9IHNlcSgwLCAxMSwgYnkgPSAxKSkNCmF4aXMoMSwgYXQ9c2VxKDE5OTYsMjAwNiwxKSxsYWJlbHMgPSB4LnRpY2ttYXJrcy55cnMuYWxsKQ0KYWJsaW5lKHY9MjAwMS41LGNvbCA9ICJyZWQiLCBsd2QgPSAyKQ0KdGV4dCh4ID0gMTk5OSx5ID0gNTAwMCwiQ2FsaWJyYXRpb24iLCBjZXg9MSwgcG9zPTMsIGNvbD0iYmxhY2siLCBmb250ID0gMikNCnRleHQoeCA9IDIwMDQseSA9IDUwMDAsIlZhbGlkYXRpb24iLCBjZXg9MSwgcG9zPTMsIGNvbD0iYmxhY2siLCBmb250ID0gMikNCmBgYA0KDQpPdXIgY2FsaWJyYXRpb24gZGF0YSBpcyAxOTk2LTIwMDEsIHNvIGl0IGxhc3RzIDYgcGVyaW9kcyAoJG49NiQpLiBXZSB2YWxpZGF0ZSB0aGUgbW9kZWwgdXNpbmcgeWVhcnMgMjAwMi0yMDA2LiBBbGwgd2UgbmVlZCB0byBlc3RpbWF0ZSB0aGUgbW9kZWwgYXJlIHRoZSBzdWZmaWNpZW50IHN0YXRpc3RpY3M6DQoNCi0gICAqKiJyZXZlcnNlIiByZWNlbmN5ICgqKiR0X3gkKTogdGhlIGxhc3QgcGVyaW9kIGEgZG9uYXRpb24gb2NjdXJyZWQuIFNpbmNlIGFyZSBkYXRhIGNvbXByaXNlIDYgcGVyaW9kcywgdGhlIG1vc3QgcmVjZW50IGRvbmF0aW9uIGlzIDYsIG9yICR0X3g9NiQuIElmIG5vIHJlcGVhdCBkb25hdGlvbnMgd2VyZSBvYnNlcnZlZCwgJHRfeCA9IDAkLiAoVXN1YWxseSBtYXJrZXRlcnMgdGhpbmsgb2YgcmVjZW5jeSBhcyB0aGUgbnVtYmVyIG9mIHBlcmlvZHMgKipzaW5jZSoqIGxhc3QgcHVyY2hhc2UsICRuLXRfeCQuIEhlcmUgcmVjZW5jeSBpcyB0aW1lIGFmdGVyIGZpcnN0IHB1cmNoYXNlLikgTm90ZSBmdXJ0aGVyIHRoYXQgaWYgJHg9MCwgXDsgdF94PTAkLg0KLSAgICoqZnJlcXVlbmN5ICgqKiR4JCk6IHRoZSBudW1iZXIgb2YgcmVwZWF0IGRvbmF0aW9ucyBvYnNlcnZlZCBpbiB0aGUgc2l4IHN1YnNlcXVlbnQgcGVyaW9kcy4NCi0gICAqKm51bWJlciBvZiBwdXJjaGFzZSBvcHBvcnR1bml0aWVzICgqKiRuJCk6IHRoaXMgaXMgdXN1YWxseSB0aGUgc2FtZSBmb3IgZXZlcnlvbmUuIEluIHRoaXMgY2FzZSAkbj02JC4NCg0KVGhlcmUgYXJlIGByIG5yb3coZG9uYXRpb25zU3VtbWFyeSRyZi5tYXRyaXgpYCByZWNlbmN5LWZyZXF1ZW5jeSBjb21iaW5hdGlvbnMuIFRoZSBudW1iZXIgb2YgY3VzdG9tZXJzIGluIGVhY2ggY2VsbCBpcyBiZWxvdy4NCg0KWW91IGNhbiBzZWUsIGZvciBleGFtcGxlLCB0aGF0IHRoZXJlIGFyZSBgciByZi5tYXRyaXhbMSw0XWAgY3VzdG9tZXJzIHdobyBhcmUgIjYgZm9yIDYiLiBBbmQgdGhlcmUgYXJlIGByIHJmLm1hdHJpeFsyMiw0XWAgY3VzdG9tZXJzIHdobyBtYWRlIG5vIHJlcGVhdCBkb25hdGlvbnMsICIwIGZvciA2Ii4gVGhlIG1vZGVsIGlzIGdvaW5nIHRvIGhhdmUgdG8gYWNjb3VudCBmb3IgdGhlc2UgZGlmZmVyZW5jZXMuDQoNCiMjIEVzdGltYXRlIHBhcmFtZXRlcnMgZm9yIHRoZSBCRy9CQiBtb2RlbCBmcm9tIHRoZSByZWNlbmN5LWZyZXF1ZW5jeSBtYXRyaXg6DQoNCldlIGdpdmUgaW5pdGlhbCBndWVzc2VzIHRvIHRoZSBmb3VyIHBhcmFtZXRlcnMgaW4gcGFyLnN0YXJ0LiBiZ2JiLkVzdGltYXRlUGFyYW1ldGVycyBlc3RpbWF0ZXMgdGhlIHBhcmFtZXRlcnMuDQoNCmBgYHtyfQ0KIyAgICAgICAgICAgIGFscGhhIGJldGEgZ2FtbWEgZGVsdGENCnBhci5zdGFydCA8LSBjKDEsIC41LCAxLCAuNSkNCg0KcGFyYW1zIDwtIGJnYmIuRXN0aW1hdGVQYXJhbWV0ZXJzKHJmLm1hdHJpeCwgcGFyLnN0YXJ0KQ0KDQojIyBzdG9yZSBwYXJhbWV0ZXJzIG5leHQgdG8gbmFtZXMNCm5hbWVzKHBhcmFtcykgPC0gYygiYWxwaGEiLCAiYmV0YSIsICJnYW1tYSIsICJkZWx0YSIpOw0KDQpyb3VuZChwYXJhbXMsMikNCg0KIyMgQ2hlY2sgbG9nLWxpa2VsaWhvb2Qgb2YgdGhlIHBhcmFtczoNCkxMIDwtIGJnYmIucmYubWF0cml4LkxMKHBhcmFtcywgcmYubWF0cml4KQ0KDQpgYGANCg0KIyMjIFBhcmFtZXRlciBFc3RpbWF0ZXMgYW5kIERpc3RyaWJ1dGlvbnMNCg0KV2UgcGxvdCB0aGUgYmV0YSBkaXN0cmlidXRpb25zIGltcGxpZWQgYnkgdGhlIG1heGltdW0gbGlrZWxpaG9vZCBlc3RpbWF0ZXMuDQoNCmBgYHtyLCBvdXQud2lkdGggPSAnMTAwJSd9DQpwYXIobWZyb3c9YygxLDIpKQ0KcGFyKG1haT1jKC44LC44LC41LC4yKSkNCnRlbXAgPC0gYmdiYi5QbG90VHJhbnNhY3Rpb25SYXRlSGV0ZXJvZ2VuZWl0eShwYXJhbXMpDQpwYXIobWFpPWMoLjgsLjgsLjUsLjIpKQ0KdGVtcCA8LSBiZ2JiLlBsb3REcm9wb3V0UmF0ZUhldGVyb2dlbmVpdHkocGFyYW1zKQ0KYGBgDQoNClJlbWVtYmVyLCBpZiAkWCBcc2ltIFx0ZXh0cm17QmV0YX0oYSxiKSwgXDsgRVtYXSA9IFxmcmFje2F9e2ErYn0kLiBTbyB0aGUgbWVhbiBvZiB0aGUgdHJhbnNhY3Rpb24gcmF0ZSB3aGlsZSBhbGl2ZSBpcyBgciByb3VuZChwYXJhbXNbMV0vc3VtKHBhcmFtc1sxOjJdKSwyKWAgYW5kIHRoZSBtZWFuIG9mIHRoZSBkcm9wIG91dCBwcm9jZXNzIGlzIGByIHJvdW5kKHBhcmFtc1szXS9zdW0ocGFyYW1zWzM6NF0pLDIpYC4NCg0KYGBge3J9DQojIE1lYW4gb2YgdHJhbnNhY3Rpb24gcmF0ZSB3aGlsZSBhbGl2ZToNCnBhcmFtc1sxXS8ocGFyYW1zWzFdK3BhcmFtc1syXSkNCiMgTWVhbiBvZiBkcm9wLW91dCBwcm9jZXNzOg0KcGFyYW1zWzNdLyhwYXJhbXNbM10rcGFyYW1zWzRdKQ0KYGBgDQoNCiMjIE1vZGVsIGZpdA0KDQojIyMgQWdncmVnYXRlDQoNCldlIGNhbiBzZWUgaG93IHdlbGwgdGhlIG1vZGVsIGRvZXMgaW4gcHJlZGljdGluZyB0aGUgYWdncmVnYXRlIG51bWJlciBvZiBkb25hdGlvbnMgb3ZlciB5ZWFycy4gVGhpcyB1c2VzIGVxdWF0aW9uIDggaW4gdGhlIEZIUyAoMjAxMCkuDQoNCmBgYHtyfQ0KaW5jLmFubnVhbC50cmFucyA8LSBkb25hdGlvbnNTdW1tYXJ5JGFubnVhbC50cmFucyAgICMgaW5jcmVtZW50YWwgYW5udWFsIHRyYW5zYWN0aW9ucw0KDQpwYXIobWZyb3c9YygxLDEpKQ0KIyMgUGxvdCB0aGUgY29tcGFyaXNvbiBvZiBhY3R1YWwgYW5kIGV4cGVjdGVkIHRvdGFsIGluY3JlbWVudGFsIHRyYW5zYWN0aW9ucyBhY3Jvc3MNCiMjIGJvdGggdGhlIGNhbGlicmF0aW9uIGFuZCBob2xkb3V0IHBlcmlvZHM6DQpwYXIobWFpPWMoLjgsLjgsLjMsLjIpKQ0KDQpwcmVkIDwtIGJnYmIuUGxvdFRyYWNraW5nSW5jKHBhcmFtcywgcmYubWF0cml4LCBpbmMuYW5udWFsLnRyYW5zLCB4dGlja2xhYj14LnRpY2ttYXJrcy55cnMuYWxsKVsyLF0NCg0KdGV4dCh4ID0gNCx5ID0gNTAwMCwiQ2FsaWJyYXRpb24iLCBjZXg9MSwgcG9zPTMsIGNvbD0iYmxhY2siLCBmb250ID0gMikNCnRleHQoeCA9IDcseSA9IDUwMDAsIlZhbGlkYXRpb24iLCBjZXg9MSwgcG9zPTMsIGNvbD0iYmxhY2siLCBmb250ID0gMikNCg0KIyBUaGUgaW5jcmVtZW50YWwgdHJhbnNhY3Rpb25zIHVzaW5nIHRoZSBmb3JtdWxhLCBlcXVhdGlvbiA4LCBpbiB0aGUgcGFwZXIuICBkb25hdGlvbnMgc2hvdWxkIGVxdWFsIHByZWQgYWJvdmUuDQoNCmFsIDwtIHBhcmFtc1sxXQ0KYmUgPC0gcGFyYW1zWzJdDQpnYSA8LSBwYXJhbXNbM10NCmRlIDwtIHBhcmFtc1s0XQ0KDQpubiA8LSBzZXEoMSwxMSkNCk4gPC0gc3VtKHJmLm1hdHJpeFssNF0pDQpFcTggPC0gKGFsLyhhbCtiZSkpKihkZS8oZ2EtMSkpKigxLShnYW1tYShnYStkZSkqZ2FtbWEoMStkZStubikvKGdhbW1hKGdhK2RlK25uKSpnYW1tYSgxK2RlKSkpKQ0KDQpjdW1fZG9uYXRpb25zIDwtIE4qRXE4DQpkb25hdGlvbnMgPC0gYyhjdW1fZG9uYXRpb25zWzFdLGRpZmYoY3VtX2RvbmF0aW9ucykpDQoNCg0KYGBgDQoNCkhlcmUgaXMgZm9yIHRoZSBjdW11bGF0aXZlIG51bWJlciBvZiBkb25hdGlvbnMuDQoNCmBgYHtyfQ0KcGFyKG1haT1jKC44LC44LC4zLC4yKSkNCg0KcHJlZCA8LSBiZ2JiLlBsb3RUcmFja2luZ0N1bShwYXJhbXMsIHJmLm1hdHJpeCwgYWN0dWFsLmN1bS5yZXBlYXQudHJhbnNhY3Rpb25zID0gY3Vtc3VtKGRvbmF0aW9uc1N1bW1hcnkkYW5udWFsLnRyYW5zKSwgeHRpY2tsYWI9eC50aWNrbWFya3MueXJzLmFsbClbMixdDQoNCnRleHQoeCA9IDQseSA9IDUwMDAsIkNhbGlicmF0aW9uIiwgY2V4PTEsIHBvcz0zLCBjb2w9ImJsYWNrIiwgZm9udCA9IDIpDQp0ZXh0KHggPSA3LHkgPSA1MDAwLCJWYWxpZGF0aW9uIiwgY2V4PTEsIHBvcz0zLCBjb2w9ImJsYWNrIiwgZm9udCA9IDIpDQpgYGANCg0KIyMjIENvbmRpdGlvbmFsIEV4cGVjdGF0aW9ucw0KDQpBIHZlcnkgaW1wb3J0YW50IHRlc3Qgb2YgYSBtb2RlbCBpcyBob3cgd2VsbCBpdCBwcmVkaWN0cyAqKmF0IHRoZSBpbmRpdmlkdWFsIGxldmVsLCBhZnRlciBjb25kaXRpb25pbmcgb24gYSBwYXJ0aWN1bGFyIGluZGl2aWR1YWwgaGlzdG9yeSoqLiBHaXZlbiBhIGN1c3RvbWVyIHdpdGggaGlzdG9yeSAkKHgsIHRfeCwgbikkLCAoYSkgaG93IG1hbnkgcHVyY2hhc2VzIGRvIHdlIHByZWRpY3QgaW4gdGhlIG5leHQgJG5eKiQgcGVyaW9kcyBhbmQgKGIpIGhvdyB3ZWxsIGRvZXMgaXQgdHJhY2sgYWN0dWFsIGhvbGRvdXQgcHVyY2hhc2VzPw0KDQpXZSBmaXJzdCBkbyAoYSkuIFVzaW5nIGVxdWF0aW9uIDEzLCB3ZSBjYW4gY2FsY3VsYXRlIHRoZSBleHBlY3RlZCBudW1iZXIgb2YgcHVyY2hhc2VzIGZvciBlYWNoICQoeCwgdF94LCBuKSQgZ3JvdXAgaW4gdGhlIG5leHQgJG5eKiA9IDUkIHBlcmlvZHMuDQoNCmBgYHtyfQ0KcGFyKG1haT1jKC44LC44LC41LC4yKSkNCmNvbXAgPC0gYmdiYi5IZWF0bWFwSG9sZG91dEV4cGVjdGVkVHJhbnMocGFyYW1zLCBuLmNhbD02LCBuLnN0YXI9NSkNCmBgYA0KDQpgYGB7cn0NCiMgcm90YXRlIG1hdHJpeCBzbyBpdCdzIHRoZSBzYW1lIGRpcmVjdGlvbiBhcyB0aGUgaGVhdG1hcCwgdGhpcyBpcyBqdXN0IHRvIG1ha2UgdGhlIG51bWJlcnMgZWFzaWVyIHRvIHJlYWQNCnJvdGF0ZSA8LSBmdW5jdGlvbih4KSB0KGFwcGx5KHgsIDIsIHJldikpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQp0ZXN0IDwtIGthYmxlKHJvdGF0ZShyb3RhdGUocm90YXRlKHQocm91bmQoY29tcCwyKSkpKSksIGZvcm1hdCA9ICJwaXBlIiwgYm9va3RhYnM9RiwgYWxpZ24gPSAiYyIsIGNhcHRpb24gPSAiKipQcmVkaWN0ZWQgaG9sZG91dCBwdXJjaGFzZXMgKEJHL0JCIE1vZGVsKSBiYXNlZCBvbiBmcmVxdWVuY3kgKHJvd3MpIHggcmVjZW5jeSAoY29sdW1ucykqKiIpDQoNCmBgYA0KDQpBIGRvbm9yIHdobyBkb25hdGVkIGV2ZXJ5IHllYXIgZXhjZXB0IHRoZSBsYXN0ICQoeD01LCB0X3g9NSwgbj02KSQgaXMgcHJlZGljdGVkIHRvIG1ha2UgYHIgcm91bmQoY29tcFtbNiw2XV0sMilgIGluIHRoZSBuZXh0ICRuXio9NSQgcGVyaW9kcy4gWWV0IGEgZG9ub3Igd2l0aCBiZXR0ZXIgcmVjZW5jeSBidXQgbG93ZXIgZnJlcXVlbmN5ICQoeD00LHRfeD02LCBuPTYpJCBoYXMgYSBoaWdoZXIgZXhwZWN0ZWQgdHJhbnNhY3Rpb24gcmF0ZSwgYHIgcm91bmQoY29tcFtbNSw3XV0sMilgLiBBcyBtZW50aW9uZWQgaW4gRmFkZXIsIEhhcmRpZSBhbmQgU2hhbmcgKDIwMTApLCB0aGlzIGhpZ2hsaWdodHMgdGhlIGltcG9ydGFuY2Ugb2YgcmVjZW5jeS4NCg0KTm93IHdlIGRvIChiKS4gVGhlIGFjdHVhbCBudW1iZXIgb2YgdG90YWwgaG9sZG91dCBwdXJjaGFzZXMgYnkgY3VzdG9tZXJzIGluIGVhY2ggUkYgY2F0ZWdvcnkgaXMgZ2l2ZW4gaW4gdGhlIHZhcmlhYmxlIHguc3Rhci4gVGhlcmVmb3JlIHRoZSBhdmVyYWdlIG51bWJlciBvZiBob2xkb3V0IHB1cmNoYXNlcyBwZXIgUkYgY2F0ZWdvcnkgaXMgdGhlIHRvdGFsIGRpdmlkZWQgYnkgdGhlIG51bWJlciBvZiBjdXN0b21lcnMuIFdlIGFkZCB0aGlzIHRvIHRoZSBSRiBtYXRyaXggYW5kIHJlc2hhcGUuDQoNCmBgYHtyfQ0Kbi5zdGFyIDwtIDUgICAgICAgICAgICAgICAgICAgICAgICAjIE51bWJlciBvZiB0cmFuc2FjdGlvbiBvcHBvcnR1bml0aWVzIGluIHRoZSBob2xkb3V0IHBlcmlvZA0KeC5zdGFyIDwtIGRvbmF0aW9uc1N1bW1hcnkkeC5zdGFyICAjIFRyYW5zYWN0aW9ucyBtYWRlIGJ5IGVhY2ggY2FsaWJyYXRpb24gcGVyaW9kIGJpbiBpbiB0aGUgaG9sZG91dCBwZXJpb2QNCg0KWDwteC5zdGFyL3JmLm1hdHJpeFssImN1c3RzIl0NCg0KaG9sX3JmX3RyYW5zPC1hcy5kYXRhLmZyYW1lKGNiaW5kKHJmLm1hdHJpeFssMToyXSxYKSkNCg0KYWN0dWFsX3JmPC1yZXNoYXBlKGhvbF9yZl90cmFucywgaWR2YXI9InQueCIsIHRpbWV2YXI9IngiLCBkaXJlY3Rpb249IndpZGUiKQ0KDQojIGNoYW5nZSBOQSdzIHRvIDANCmFjdHVhbF9yZltpcy5uYShhY3R1YWxfcmYpXSA8LSAwDQoNCiMgcmUtb3JkZXIgY29sdW1ucyBhbmQgcm93cw0KYWN0dWFsX3JmPC1hY3R1YWxfcmZbb3JkZXIoYWN0dWFsX3JmWywxXSksb3JkZXIoYWN0dWFsX3JmWzEsXSldDQoNCiMgbWFrZSB0eCB0byByb3duYW1lDQpyb3duYW1lcyhhY3R1YWxfcmYpIDwtIGFjdHVhbF9yZlssOF0NCg0KIyBkZWxldGUgdHgNCmFjdHVhbF9yZjwtYWN0dWFsX3JmWyxjKC04KV0NCg0KI2FjdHVhbF9yZg0KDQojIHRvIG1ha2UgaXQgbG9vayBuaWNlIGFuZCByb3RhdGVkIGluIHNhbWUgd2F5IGFzIGhlYXRtYXANCmthYmxlKHJvdGF0ZShyb3RhdGUocm90YXRlKChyb3VuZChhY3R1YWxfcmYsMikpKSkpLCBmb3JtYXQgPSAicGlwZSIsIGJvb2t0YWJzPUYsIGFsaWduID0gImMiLCBjYXB0aW9uID0gIioqQWN0dWFsIGhvbGRvdXQgYXZlcmFnZSBwdXJjaGFzZXMgYmFzZWQgb24gZnJlcXVlbmN5IChyb3dzKSB4IHJlY2VuY3kgKGNvbHVtbnMpKioiKQ0KYGBgDQoNCiMjIyBDb25kaXRpb25pbmcgb24gZnJlcXVlbmN5ICYgcmVjZW5jeSBzZXBhcmF0ZWx5DQoNCk5leHQgd2UgY2FuIGhvdyB3ZWxsIHRoZSBtb2RlbCBkb2VzIGlmIHdlIGNvbmRpdGlvbiBvbiB0aGUgZnJlcXVlbmN5IG9mIHRyYW5zYWN0aW9ucyBpbiB0aGUgY2FsaWJyYXRpb24gcGVyaW9kLCBhdmVyYWdpbmcgb3ZlciByZWNlbmN5LiBJbiBvdGhlciB3b3Jkcywgd2UgdGFrZSBldmVyeW9uZSB3aG8gaGFzIGhhZCBhIGZyZXF1ZW5jeSBvZiAkeCQgdHJhbnNhY3Rpb25zIGluIHRoZSBjYWxpYnJhdGlvbiBwZXJpb2QsIGFuZCB3ZSBjYW4gY29tcGFyZSBob3cgbWFueSBhY3R1YWwgdHJhbnNhY3Rpb25zIHRoZXkgaGFkIGluIHRoZSB2YWxpZGF0aW9uIHBlcmlvZCB3aXRoIHRoZSBwcmVkaWN0aW9ucy4NCg0KYGBge3Igb3V0LndpZHRoID0gJzkwJScsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQ0KcGFyKG1haT1jKC44LC44LC41LC4yKSkNCg0KIyMgUGxvdCB0aGUgY29tcGFyaXNvbiBvZiBhY3R1YWwgYW5kIGNvbmRpdGlvbmFsIGV4cGVjdGVkIGhvbGRvdXQgcGVyaW9kIGZyZXF1ZW5jaWVzLA0KIyMgYmlubmVkIGFjY29yZGluZyB0byBjYWxpYnJhdGlvbiBwZXJpb2QgZnJlcXVlbmNpZXM6DQpmcmVxIDwtIGJnYmIuUGxvdEZyZXFWc0NvbmRpdGlvbmFsRXhwZWN0ZWRGcmVxdWVuY3kocGFyYW1zLCBuLnN0YXIsIHJmLm1hdHJpeCwgeC5zdGFyKQ0KDQpyb3duYW1lcyhmcmVxKSA8LSBjKCJhY3QiLCAiZXhwIiwgImJpbiIpDQpmcmVxDQpgYGANCg0KTW9kZWwgcHJlZGljdGlvbnMgY2xvc2VseSB0cmFjayBhY3R1YWwgZG9uYXRpb25zLiBEb25vcnMgd2hvIG1hZGUgemVybyBkb25hdGlvbnMgMTk5Ni0yMDAxLCBtYWRlIG9uIGF2ZXJhZ2UgYHIgcm91bmQoY29tcFtbMV1dLDIpYCBkb25hdGlvbnMgaW4gMjAwMi0yMDA2LiBUaGUgQkcvQkIgbW9kZWwgcHJlZGljdHMgc2xpZ2h0bHkgZmV3ZXIsIGByIHJvdW5kKGNvbXBbWzJdXSwyKWAuIERvbm9ycyB3aG8gbWFkZSBhIGRvbmF0aW9uIGV2ZXJ5IHllYXIsICI2IGZvciA2IiwgbWFkZSBgciByb3VuZChjb21wW1sxLDddXSwyKWAgZG9uYXRpb25zIGluIHRoZSBzdWJzZXF1ZW50IDUgeWVhcnMuIFRoZSBtb2RlbCBwcmVkaWN0aW9ucyBhcmUgbW9kZXN0bHkgaGlnaGVyLCBhdCBgciByb3VuZChjb21wW1syLDddXSwyKWAuIEl0J3MgaW50ZXJlc3RpbmcgdG8gbm90ZSB0aGF0IGEgbmFpdmUgcHJlZGljdGlvbiBvZiBhIGRvbm9yIHdobyBpcyAiNiBmb3IgNiIgc28gd291bGQgdGhlcmVmb3JlIGJlIGEgIjUgZm9yIDUiIGRvbm9yIGluIHRoZSB2YWxpZGF0aW9uIHBlcmlvZCB3b3VsZCBvdmVyZXN0aW1hdGUgZG9uYXRpb25zIGJ5IHF1aXRlIGEgbG90Lg0KDQpJbnN0ZWFkIG9mIGdyb3VwaW5nIGN1c3RvbWVycyBieSBmcmVxdWVuY3ksIHdlIGNhbiBhbHNvIGNvbmRpdGlvbiBvbiB0aGVpciByZWNlbmN5LCBpLmUuLCB0aGUgbGFzdCBwZXJpb2QgdGhleSBtYWRlIGEgZG9uYXRpb246DQoNCmBgYHtyIG91dC53aWR0aCA9ICc5MCUnLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0NCnBhcihtYWk9YyguOCwuOCwuNSwuMikpDQoNCnJlYzwtYmdiYi5QbG90UmVjVnNDb25kaXRpb25hbEV4cGVjdGVkRnJlcXVlbmN5KHBhcmFtcywgbi5zdGFyLCByZi5tYXRyaXgsIHguc3RhcikNCnJvd25hbWVzKHJlYykgPC0gYygiYWN0IiwgImV4cCIsICJiaW4iKQ0KcmVjDQpgYGANCg0KVGhlcmUgd2VyZSBgciBjb21wW1szLDddXWAgZG9ub3JzIHdpdGggbWF4aW11bSByZWNlbmN5LCBpLmUuLCBtYWtpbmcgYSBkb25hdGlvbiBpbiAyMDAxLiBUaG9zZSBkb25vcnMgbWFkZSBvbiBhdmVyYWdlIGByIHJvdW5kKGNvbXBbWzEsN11dLDEpYCBkb25hdGlvbnMgaW4gdGhlIHN1YnNlcXVlbnQgNSB5ZWFycywgYW5kIHRoZSBtb2RlbCBwcmVkaWN0cyB0aGF0IHRoZXkgd291bGQgbWFrZSBgciByb3VuZChjb21wW1syLDddXSwxKWAuIFRoZXJlIGlzIGEgc3RlZXAgZmFsbG9mZiBhcyByZWNlbmN5IGRpbWluaXNoZXMsIG1vdmluZyBmcm9tIHJpZ2h0IHRvIGxlZnQgaW4gdGhlIGdyYXBoIHRoYXQgaXMgY2FwdHVyZWQgYnkgdGhlIG1vZGVsLg0KDQojIyBQKEFsaXZlKQ0KDQpUaGUgcHJvYmFiaWxpdHkgdGhhdCBhIGN1c3RvbWVyIHdpdGggcHVyY2hhc2UgaGlzdG9yeSAkeCwgdF94IG4kIHdpbGwgYmUgYWxpdmUgYXQgdGhlIGJlZ2lubmluZyBvZiBwZXJpb2QgJG4gKyAxJCBpcyB0aGUgdGVybSBpbiB0aGUgbGlrZWxpaG9vZCB3aGVyZSB0aGUgY3VzdG9tZXIgaXMgYWxpdmUgdW50aWwgdGhlIGVuZCBkaXZpZGVkIGJ5IGFsbCB0aGUgcGF0aHM6ICQkDQpQKFx0ZXh0cm17QWxpdmUgYXR9IFw7IG4rMSB8IFw7IG4sIHgsIHRfeCkgPSBcZnJhY3tcZnJhY3tCKFxhbHBoYSt4LCBcYmV0YSArIG4teCl9e0IoXGFscGhhLCBcYmV0YSl9IFxmcmFje0IoXGdhbW1hLCBcZGVsdGEgKyBuKzEpfXtCKFxnYW1tYSwgXGRlbHRhKX19e0woXGFscGhhLCBcYmV0YSwgXGdhbW1hLCBcZGVsdGEgXG1pZCBuLCB0X3gsIHgpfQ0KJCQNCg0KV2UgY2FuIGNhbGN1bGF0ZSBmb3IgYWxsIHRoZSBwb3NzaWJsZSBjZWxscyBpbiBvdXIgcmVjZW5jeS1mcmVxdWVuY3kgbWF0cml4IHRoZSBwcm9iYWJpbGl0eSB0aGF0IHRoZSBjdXN0b21lciBpcyBhY3RpdmUuDQoNCmBgYHtyIG91dC53aWR0aCA9ICc5MCUnLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0NClBBbGl2ZSA8LSBiZ2JiLlBBbGl2ZShwYXJhbXMsIHggPSByZi5tYXRyaXhbLDFdLCB0LnggPSByZi5tYXRyaXhbLDJdLCBuLmNhbCA9IDYpDQpBbGl2ZV9tYXQ8LWNiaW5kKHJmLm1hdHJpeCxQQWxpdmUpDQpBbGl2ZV9tYXQ8LWRhdGEuZnJhbWUoQWxpdmVfbWF0KQ0Ka2FibGUoQWxpdmVfbWF0LCBmb3JtYXQ9InBpcGUiKQ0KYGBgDQoNCmBgYHtyIG91dC53aWR0aCA9ICc5MCUnLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0NCndpdGgoQWxpdmVfbWF0LCBoaXN0KHJlcCh4ID0gQWxpdmVfbWF0JFBBbGl2ZSwgdGltZXMgPSBBbGl2ZV9tYXQkY3VzdHMpLCB4YXh0PSduJywgeGxpbSA9IGMoMCwxKSwgeGxhYiA9ICJQKEFsaXZlIGF0IG4rMSkiLCB5bGFiPSJmcmVxdWVuY3kiLCBtYWluPSJIaXN0b2dyYW0gb2YgUChBbGl2ZSkiKSkNCmF4aXMoc2lkZT0xLCBhdD1zZXEoMCwxLCAuMTApLCBsYWJlbHM9c2VxKDAsMSwuMSkpDQoNCmBgYA0KDQpIb3cgbWFueSB0b3RhbCBhY3RpdmUgb3IgYWxpdmUgY3VzdG9tZXJzIGFyZSB0aGVyZSBhdCBwZXJpb2QgNz8gV2UgY2FuIHN1bSB1cCBhbGwgdGhlIFAoQWxpdmUpIGNlbGxzIGFuZCBudW1iZXIgb2YgY3VzdG9tZXJzIGluIGVhY2ggY2VsbCB0byBnZXQgdGhlIGFuc3dlcjoNCg0KYGBge3J9DQpzdW0oQWxpdmVfbWF0JFBBbGl2ZSpBbGl2ZV9tYXQkY3VzdHMpDQoNCmBgYA0KDQpUaGVyZSBhcmUgYHIgc3VtKEFsaXZlX21hdCRQQWxpdmUqQWxpdmVfbWF0JGN1c3RzKWAgb3V0IG9mIGByIHN1bShBbGl2ZV9tYXQkY3VzdHMpYCBjdXN0b21lcnMgc3RpbGwgYWN0aXZlLg0KDQojIyBJbmNyZWFzaW5nIEZyZXF1ZW5jeSBQYXJhZG94DQoNCkxldCdzIGltYWdpbmUgYSBjdXN0b21lciB3aG8gaGFzIG1hZGUgaGlzIG9yIGhlciBsYXN0IGRvbmF0aW9uIG9uIHBlcmlvZCAkdF94ID0gNCQsIGJ1dCBsZXQncyB2YXJ5IGhvdyBtYW55IHB1cmNoYXNlcyBzaGUgbWFrZXMuIEF0IG1vc3Qgc2hlIGNhbiBtYWtlIDQsIGFuZCBhdCBsZWFzdCAxLiBXZSBjYW4gYXNrIHdoYXQgdGhlIG1vZGVsIHByZWRpY3RzIGlzIHRoZSBudW1iZXIgb2YgcHVyY2hhc2VzIGV4cGVjdGVkIGluIHRoZSBzdWJzZXF1ZW50ICRuXio9NSQgcGVyaW9kcywgYSBjYWxjdWxhdGlvbiB3ZSBhbHJlYWR5IGRpZCBhYm92ZToNCg0KYGBge3IsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQ0KcGFyKG1mcm93PWMoMSwxKSxtYWk9YyguOCwuOCwuNSwuMikpDQpwbG90KGNvbXBbMjo1LDVdLCB5bGFiID0iRXhwZWN0ZWQgdHJhbnNhY3Rpb25zIGluIG5leHQgNSBwZXJpb2RzIiwgeGxhYj0iRnJlcXVlbmN5IGhvbGRpbmcgbGFzdCBkb25hdGlvbiBhdCA0IiwgdHlwZT0iYiIsIHhheHQ9Im4iKQ0KeHRpY2s8LXNlcSgxLCA0LCBieT0xKQ0KYXhpcyhzaWRlPTEsIGF0PXh0aWNrLCBsYWJlbHMgPSBUUlVFKQ0KDQpgYGANCg0KV2hhdCdzIGludGVyZXN0aW5nIGFib3V0IHRoaXMgY3VydmUgaXMgdGhhdCBjdXN0b21lciB3aXRoIHRoZSBsYXJnZXN0IGZyZXF1ZW5jeSBpcyAqKm5vdCoqIHRoZSBvbmUgd2l0aCB0aGUgaGlnaGVzdCBmdXR1cmUgcHJlZGljdGVkIHB1cmNoYXNlcy4gVGhpcyBpcyBzb21ldGhpbmcga25vd24gYXMgdGhlICoqaW5jcmVhc2luZyBmcmVxdWVuY3kgcGFyYWRveCoqLiBXaHk/IFRoZSBsaWtlbGlob29kIHRoYXQgaGUgb3Igc2hlIGlzIHN0aWxsIGFsaXZlIGRlY3JlYXNlcyBpbiAkeCQuDQoNCmBgYHtyLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0NCnBhcihtZnJvdz1jKDEsMSkpDQpwYXIobWFpPWMoLjgsLjgsLjUsLjIpKQ0KDQpwbG90KGJnYmIuUEFsaXZlKHBhcmFtcywgeD0xOjQsIHQueD00LCBuLmNhbD02KSwgeWxhYiA9IlByb2JhYmlsaXR5IHRoYXQgY3VzdG9tZXIgaXMgYWxpdmUgbmV4dCBwZXJpb2QiLCB4bGFiPSJGcmVxdWVuY3kgaG9sZGluZyBsYXN0IGRvbmF0aW9uIGF0IDQiLCB5bGltPWMoMCwxKSwgdHlwZT0iYiIsIHhheHQ9Im4iKQ0KeHRpY2s8LXNlcSgxLCA0LCBieT0xKQ0KYXhpcyhzaWRlPTEsIGF0PXh0aWNrLCBsYWJlbHMgPSBUUlVFKQ0KYGBgDQoNCk9uIHRoZSBvbmUgaGFuZCwgaGlnaGVyICR4JCBtZWFucyBhIGhpZ2hlciAkcCQgd2hpY2ggbWVhbnMgbW9yZSBleHBlY3RlZCB0cmFuc2FjdGlvbnMgaW4gdGhlIGZ1dHVyZS4gT24gdGhlIG90aGVyIGhhbmQsIGlmIHRoZSBsYXN0IHR3byBwZXJpb2RzIHdlcmUgbm8gcHVyY2hhc2VzLCBhIGhpZ2hlciAkeCQgbWVhbnMgdGhhdCAkUChhbGl2ZSkkIGlzIGxvd2VyLiBUaGlzIHNlY29uZCBlZmZlY3QgaXMgc3Ryb25nZXIgdGhhbiB0aGUgZmlyc3QgZWZmZWN0LCByZXN1bHRpbmcgaW4gYSBsb3dlciBleHBlY3RhdGlvbnMgd2hlbiAkeD00JCBjb21wYXJlZCB0byB3aGVuICR4PTIsMyQuDQoNCiMgQ0xWDQoNCkdpdmVuIG1vZGVsIGFzc3VtcHRpb25zIDIgYW5kIDMsIHdlIGtub3cgdGhhdCB0aGUgcHJvYmFiaWxpdHkgb2YgbWFraW5nIGEgcHVyY2hhc2UgaXMgZXF1YWwgdG8gdGhlIHByb2JhYmlsaXR5IHRoYXQgYSBjdXN0b21lciBpcyAqKmFsaXZlKiogdGltZXMgdGhlIHByb2JhYmlsaXR5IG9mIG1ha2luZyBhIHB1cmNoYXNlIGNvbmRpdGlvbmFsIG9uIGJlaW5nIGFsaXZlOiAkJA0KUChcLCBZKHQpID0gMSBcbWlkIHAsIFx0aGV0YSkgPSBwIFwsICgxLVx0aGV0YSledA0KJCQgV2UgaW50ZWdyYXRlICRwJCBhbmQgJFx0aGV0YSQgb3ZlciB0aGVpciBtaXhpbmcgZGlzdHJpYnV0aW9ucyB0byBnZXQgdGhlIHByb2FiaWxpdHkgZm9yIGEgcmFuZG9tbHkgY2hvc2VuIGN1c3RvbWVyOiAkJA0KXGJlZ2lue2FycmF5fXtjY2x9DQpQKFwsIFkodCkgPSAxIFxtaWQgXGFscGhhLCBcYmV0YSwgXGdhbW1hLCBcZGVsdGEpICY9JiBcZGlzcGxheXN0eWxlIFxpbnRfMF4xIFxpbnRfMF4xIFAoXCwgWSh0KSA9IDEgXG1pZCBwLCBcdGhldGEpIFwsIGYocCBcOyB8IFw7IFxhbHBoYSxcYmV0YSkgXCwgZihcdGhldGEgXDsgfCBcOyBcZ2FtbWEsXGRlbHRhKSBcLCBkcCBcLCBkXHRoZXRhIFxcDQomPSYgXGRpc3BsYXlzdHlsZSAgXGxlZnQoXGZyYWN7XGFscGhhfXtcYWxwaGErXGJldGF9XHJpZ2h0KSBcZnJhY3tCKFxnYW1tYSwgXGRlbHRhK3QpfXtCKFxnYW1tYSwgXGRlbHRhKX0NClxlbmR7YXJyYXl9DQokJA0KDQpDTFYgaXMgdGhlbiB0aGUgZGlzY291bnRlZCBzdW0gb2YgdGhlIHByb2JhYmlsaXR5IG9mIG1ha2luZyBhIHRyYW5zYWN0aW9uIHRpbWVzIHNvbWUgYXZlcmFnZSBhbW91bnQgcGVyIHRyYW5zYWN0aW9uICgkbSQpOg0KDQokJA0KXGJlZ2lue2FycmF5fXtjY2x9DQpFW0NMVl0gJiA9ICAmIG0gXDsgXGxlZnQoIDEgKyBcc3VtX3t0PTF9XntcaW5mdHl9IFAoXCwgWSh0KSA9IDEgXG1pZCBcYWxwaGEsIFxiZXRhLCBcZ2FtbWEsIFxkZWx0YSkgXGZyYWN7MX17KDErZCledH0gXHJpZ2h0KSBcXA0KJiA9ICYgbSAgXDsgXHRpbWVzIFx0ZXh0cm17REVUfQ0KXGVuZHthcnJheX0NCiQkDQoNCioqREVUKiogbWVhbnMgRGlzY291bnRlZCBFeHBlY3RlZCBUcmFuc2FjdGlvbnMuIEZvciBpbXBsZW1lbnRpbmcgdGhpcyBpbiBgUmAsIHdlIGhhdmUgdG8gY2hvb3NlIHNvbWUgdXBwZXIgYm91bmQgdG8gdGhlIHN1bSwgaS5lLiAkVD0yMDAkLg0KDQpgYGB7cn0NCkJHQkJDTFY8LWZ1bmN0aW9uKHBhcmFtcyxtLGQsVCkgew0KcGFyYW1zPC11bm5hbWUocGFyYW1zKQ0KYWw8LXBhcmFtc1sxXQ0KYmU8LXBhcmFtc1syXQ0KZ2E8LXBhcmFtc1szXQ0KZGU8LXBhcmFtc1s0XQ0KREVUPC0xICAgIyBhdCB0aW1lIHplcm8gdGhlcmUgaGFzIHRvIGJlIGEgcHVyY2hhc2UNCmZvciAoaSBpbiAxOlQpIHsNCiAgICBERVQ8LURFVCsoYWwvKGFsK2JlKSkqKGJldGEoZ2EsZGUraSkvYmV0YShnYSxkZSkpKjEvKDErZClee2l9DQp9DQpDTFY9bSpERVQgICMgY29udmVydCBkaXNjb3VudCBleHBlY3RlZCBwdXJjaGFzZXMgaW50byBleHBlY3RlZCB2YWx1ZQ0KcmV0dXJuKENMVikgICAgI3JldHVybiB0aGUgQ0xWDQp9DQpgYGANCg0KYGBge3J9DQpDTFYgPC0gQkdCQkNMVihwYXJhbXMgPSBwYXJhbXMsIG09NTAsZD0uMSxUPTIwMCkNCmBgYA0KDQpDTFYgZm9yIGEgcmFuZG9tIGN1c3RvbWVyIHdpdGggcGFyYW1ldGVycyBhcyBlc2ltYXRlZCwgJG094oKsNTAsIGQ9LjEsIFQ9MjAwJCBpcyDigqxgciByb3VuZChDTFYpYC4NCg0KIyMgUkxWDQoNCkxhc3RseSB3ZSBjYW4gY2FsY3VsYXRlIHRoZSByZXNpZHVhbCBsaWZldGltZSB2YWx1ZSBvZiBhIGRvbm9yIHdpdGggaGlzdG9yeSAkKHgsdF94LG4pJC4gVGhlIHJlc2lkdWFsIGxpZmV0aW1lIHZhbHVlIGlzIHRoZSBwcmVzZW50IHZhbHVlIG9mIHRoZSBleHBlY3RlZCBmdXR1cmUgdHJhbnNhY3Rpb24gc3RyZWFtIHN0YW5kaW5nIGF0IHRpbWUgJHQkLiAkJA0KXGJlZ2lue2FycmF5fXtjY2x9DQpFW1JMVl0gJiA9ICYgXGRpc3BsYXlzdHlsZSBtIFw7IFxsZWZ0ICggUChcdGV4dHJte2FsaXZlIGF0fSBcLCBuKSBcOyBcc3VtX3t0PW4rMX1ee1xpbmZ0eX0gXDsgUChZX3QgPSAxIFxtaWQgXHRleHRybXthbGl2ZSBhdH0gXCwgdCkgXGZyYWN7UChcdGV4dHJte2FsaXZlIGF0fSBcLCB0IFxtaWQgdD5uKX17KDErZClee3Qtbn19IFxyaWdodClcXA0KJiA9ICYgXGRpc3BsYXlzdHlsZSBtIFx0aW1lcyBcdGV4dHJte0RFUlR9DQpcZW5ke2FycmF5fQ0KJCQNCg0KKipERVJUKiogbWVhbnMgRGlzY291bnRlZCBleHBlY3RlZCByZXNpZHVhbCB0cmFuc2FjdGlvbnMuIEhlcmUgaXMgd2hhdCBpdCBsb29rcyBsaWtlIGZvciBvdXIgc2FtcGxlOg0KDQpgYGB7ciBvdXQud2lkdGggPSAnOTAlJywgZmlnLmFsaWduID0gImNlbnRlciJ9DQptIDwtIDUwDQpERVJUIDwtIGJnYmIucmYubWF0cml4LkRFUlQocGFyYW1zLCBkb25hdGlvbnNTdW1tYXJ5JHJmLm1hdHJpeCwgZD0wLjEpDQpSTFYgPC0gbSpERVJUDQpSTFZfbWF0IDwtIGNiaW5kKEFsaXZlX21hdCxERVJULCBSTFYpDQpSTFZfbWF0IDwtIGRhdGEuZnJhbWUoUkxWX21hdCkNCmthYmxlKFJMVl9tYXQsIGZvcm1hdD0icGlwZSIpDQpgYGANCg0KYGBge3Igb3V0LndpZHRoID0gJzkwJScsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQ0KbWF4cm91bmQ9cm91bmQobWF4KFJMViksLTIpDQpSTFZfbWF0JFJMVlsxXSANCg0Kd2l0aChSTFZfbWF0LCBoaXN0KHJlcCh4ID0gUkxWX21hdCRSTFYsIHRpbWVzID0gUkxWX21hdCRjdXN0cyksIHhheHQ9J24nLCB4bGltID0gYygwLG1heHJvdW5kKSwgeGxhYiA9ICJSTFYgKCQpIiwgeWxhYj0iZnJlcXVlbmN5IiwgbWFpbj0iSGlzdG9ncmFtIG9mIFJlc2lkdWFsIExpZmV0aW1lIFZhbHVlIChSTFYpIikpDQpheGlzKHNpZGU9MSwgYXQ9c2VxKDAsbWF4cm91bmQsIDUwKSwgbGFiZWxzPXNlcSgwLG1heHJvdW5kLCA1MCkpDQpgYGANCg0KSWYgd2UgYXNzdW1lICRtPTUwJCBpcyB0aGUgdmFsdWUgb2YgYSBkb25hdGlvbiBhbmQgd2UgdXNlIGEgeWVhcmx5IGRpc2NvdW50IHJhdGUgb2YgJGQ9MC4xJCwgdGhlIFJMViBvZiBhIGN1c3RvbWVyIHdobyBtYWtlcyAiNiBmb3IgNiIgcmVwZWF0IGRvbmF0aW9ucyBpcyDigqxgciByb3VuZChSTFZfbWF0JFJMVlsxXSwyKWAuDQo=