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 paper:
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).
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.
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.) \[
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.
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\]
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\]
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:
\(n\) transaction
opportunities
\(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.)
\(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)
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")
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")
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.
LS0tDQp0aXRsZTogIlR1dG9yaWFsIDc6IENMViAtIE5vbi1jb250cmFjdHVhbCBzZXR0aW5ncyINCmRhdGU6ICIyMDIzLTAxLTI2Ig0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgd2FybmluZyA9IEZBTFNFLA0KICBvdXQud2lkdGggPSAiMTAwJSIsDQogIGZpZy5hbGlnbiA9ICJjZW50ZXIiKQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KYGBgDQoNCiMgSW50cm9kdWN0aW9uOiBMYXRlbnQgQXR0cml0aW9uDQoNCkluIHRoaXMgc2VjdGlvbiwgd2UnbGwgZm9jdXMgb24gbm9uLWNvbnRyYWN0dWFsIHNldHRpbmdzLCB3aGVyZSB0aGUgdGltZSBhdCB3aGljaCBhIGN1c3RvbWVyIGRpZXMsIG9yIGJlY29tZXMgcGVybWFuZW50bHkgaW5hY3RpdmUsIGlzIG5vdCBvYnNlcnZlZCBieSB0aGUgZmlybS4gV2Ugd2lsbCBmb2N1cyBvbiBvbmUgY2xhc3Mgb2YgbW9kZWxzIGNhbGxlZCBsYXRlbnQgYXR0cml0aW9uIG9yICJCdXkgVGlsIFlvdSBEaWUiIG1vZGVscy4gQWxsIHRoZXNlIG1vZGVscyBzaGFyZSBhIGNvbW1vbiBmZWF0dXJlOg0KDQo+IFRoZSBjdXN0b21lcidzIHJlbGF0aW9uc2hpcCB3aXRoIGEgZmlybSBoYXMgKip0d28qKiBwaGFzZXM6IGEgY3VzdG9tZXIgaXMgYWN0aXZlIGZvciBzb21lIHBlcmlvZCBvZiB0aW1lLCB0aGVuIGJlY29tZXMgcGVybWFuZW50bHkgaW5hY3RpdmUuDQoNClRoZXNlIG1vZGVscyBhcmUgYSBzdWJzZXQgb2YgSGlkZGVuIE1hcmtvdiBtb2RlbHMgd2hlcmUgdGhlIHR3byBzdGF0ZXMtYWxpdmUgYW5kIGRlYWQtYXJlIG5vdCBkaXJlY3RseSBvYnNlcnZlZCwgaGVuY2UgaGlkZGVuLiBUaGUgY2xhc3NpYyBwYXBlciB0aGF0IGxhdW5jaGVkIHRoaXMgbGl0ZXJhdHVyZSBjcmVhdGVkIHRoZSBQYXJldG8vTkJEIG1vZGVsLCB3aGljaCBpcyBpbiBjb250aW51b3VzIHRpbWUuW14xXQ0KDQpbXjFdOiBTY2htaXR0bGVpbiwgRC4sIE1vcnJpc29uLCBELiwgJiBDb2xvbWJvLCBSLiAoMTk4NykuIENvdW50aW5nIFlvdXIgQ3VzdG9tZXJzOiBXaG8gQXJlIFRoZXkgYW5kIFdoYXQgV2lsbCBUaGV5IERvIE5leHQ/IE1hbmFnZW1lbnQgU2NpZW5jZSwgMzMoMSksIDEtMjQuDQoNCkEgYmV0dGVyIHBsYWNlIHRvIHN0YXJ0IGZvciB1cyBob3dldmVyIGlzIHRoZSBCZXRhLWdlb21ldHJpYy9CZXRhLWJpbm9taWFsIChCRy9CQikgbW9kZWwsIGJlY2F1c2UgaXQgaXMgaW4gKmRpc2NyZXRlIHRpbWUqIGFuZCBoYXMgYnVpbGRpbmcgYmxvY2tzIHNpbWlsYXIgdG8gdGhlIHNCRyBtb2RlbCB3ZSBjb3ZlcmVkIGluIHRoZSBsYXN0IHNlc3Npb24uDQoNCiMgVGhlIEJHL0JCIG1vZGVsDQoNClRoZSBiZXN0IHBsYWNlIHRvIHN0YXJ0IG9mZiBpcyB0aGUgQmV0YS1nZW9tZXRyaWMvQmV0YS1CaW5vbWlhbCBtb2RlbCAoQkcvQkIpLCB3aGljaCBpcyBpbiBkaXNjcmV0ZSB0aW1lLiBUaGlzIGlzIHRoZSBzYW1lIGRpc2NyZXRlIHRpbWUgd2UgdXNlZCB3aXRoIHRoZSBzQkcgKHNoaWZ0ZWQgQmV0YS1nZW9tZXRyaWMpIG1vZGVsIGZvciBjb250cmFjdHVhbCBzZXR0aW5ncy4gVGhlIGFzc3VtcHRpb25zIG9mIHRoZSBtb2RlbCBhcmUgZ2l2ZW4gaW4gdGhlIHJlc2VhcmNoIHBhcGVyW14yXToNCg0KW14yXTogRmFkZXIsIFBldGVyIFMuLCBCcnVjZSBHLlMuIEhhcmRpZSwgYW5kIEplbiBTaGFuZy4gIkN1c3RvbWVyLUJhc2UgQW5hbHlzaXMgaW4gYSBEaXNjcmV0ZS1UaW1lIE5vbmNvbnRyYWN0dWFsIFNldHRpbmcuIiAqTWFya2V0aW5nIFNjaWVuY2UqLCAqKjI5KDYpKiosIHBwLiAxMDg2LTExMDguIDIwMTAuIElORk9STVMuIFtsaW5rXShodHRwOi8vd3d3LmJydWNlaGFyZGllLmNvbS9wYXBlcnMvMDIwLykNCg0KMS4gIEEgY3VzdG9tZXIncyByZWxhdGlvbnNoaXAgd2l0aCB0aGUgZmlybSBoYXMgdHdvIHBoYXNlczogaGUgaXMgYWxpdmUgKEEpIGZvciBzb21lIHBlcmlvZCBvZiB0aW1lLCB0aGVuIGJlY29tZXMgcGVybWFuZW50bHkgaW5hY3RpdmUgKCJkaWVzIjsgRCkuDQoNCjIuICBXaGlsZSBhbGl2ZSwgYSBjdXN0b21lciBtYWtlcyBhIHB1cmNoYXNlIHdpdGggcHJvYmFiaWxpdHkgJHAkIGluIGFueSBnaXZlbiBwZXJpb2Q6ICQkDQogICAgUChZKHQpID0gMSBcbWlkIHAsIFx0ZXh0cm17YWxpdmUgYXR9IFw7IHQpID0gcCwgXHF1YWQgMCBcbGVxIHAgXGxlcSAxDQogICAgJCQgVGhpcyBpbXBsaWVzIHRoYXQgYSBjdXN0b21lciBhbGl2ZSBmb3IgJHMkIHBlcmlvZHMgbWFrZXMgYSBudW1iZXIgb2YgcHVyY2hhc2VzIGFjY29yZGluZyB0byBhIEJpbm9taWFsJChzLCBwKSQgZGlzdHJpYnV0aW9uLg0KDQozLiAgQSAibGl2aW5nIiBjdXN0b21lciBkaWVzIGF0IHRoZSBiZWdpbm5pbmcgb2YgYSB0cmFuc2FjdGlvbiBvcHBvcnR1bml0eSB3aXRoIHByb2JhYmlsaXR5IFx0aGV0YS4gKFRoaXMgaW1wbGllcyB0aGF0IHRoZSAodW5vYnNlcnZlZCkgbGlmZXRpbWUgb2YgYSBjdXN0b21lciBpcyBjaGFyYWN0ZXJpemVkIGJ5IGEgZ2VvbWV0cmljIGRpc3RyaWJ1dGlvbi5bXjNdKSAkJA0KICAgIFAoIFx0ZXh0cm17YWxpdmUgYXR9IFw7IHQgXG1pZCBcdGhldGEpPSBQKFQ+dCBcbWlkIFx0aGV0YSkgPSBTKHQgXG1pZCBcdGhldGEgKSA9ICgxLVx0aGV0YSledCBccXF1YWQgMCBcbGVxIFx0aGV0YSBcbGVxIDENCiAgICAkJCBBcyBhbiBhc2lkZSwgb3VyIG9uZS1zdGVwIE1hcmtvdiB0cmFuc2l0aW9uIG1hdHJpeCBmcm9tIGFsaXZlIHRvIGRlYWQgbG9va3MgbGlrZTogJCQNCiAgICBUID0gXGxlZnQoXGJlZ2lue2FycmF5fXtjY30gDQogICAgMS1cdGhldGEgJiBcdGhldGEgXFwgDQogICAgMCAmIDENCiAgICBcZW5ke2FycmF5fVxyaWdodCkNCiAgICAkJCBHaXZlbiB0aGF0IHRoZSBjdXN0b21lciB3YXMgYWxpdmUgbGFzdCBwZXJpb2QgKHRvcCByb3cpLCBhIGN1c3RvbWVyIHN0YXlzIGFsaXZlIGVhY2ggcGVyaW9kIHdpdGggcHJvYmFiaWxpdHkgJDEtXHRoZXRhJCwgYW5kIGRpZXMgd2l0aCBwcm9iYWJpbGl0eSAkXHRoZXRhJC4gSWYgdGhlIGN1c3RvbWVyIHdhcyBkZWFkIGxhc3QgcGVyaW9kIChib3R0b20gcm93KSwgaGUgb3Igc2hlIHJlbWFpbnMgZGVhZCB3aXRoIHByb2JhYmlsaXR5IDEuDQoNCjQuICBIZXRlcm9nZW5laXR5IGluICRwJCBmb2xsb3dzIGEgYmV0YSBkaXN0cmlidXRpb24gd2l0aCBwYXJhbWV0ZXJzICRcYWxwaGEkIGFuZCAkXGJldGEkLiAkJCBmKHAgXDsgfCBcOyBcYWxwaGEsXGJldGEpID0gXGZyYWN7cF57XGFscGhhLTF9ICgxLXApXntcYmV0YS0xfX17QihcYWxwaGEsXGJldGEpfSwgXHFxdWFkIFxhbHBoYT4wLCBcYmV0YT4wJCQNCg0KNS4gIEhldGVyb2dlbmVpdHkgaW4gJFx0aGV0YSQgZm9sbG93cyBhIGJldGEgZGlzdHJpYnV0aW9uIHdpdGggcGFyYW1ldGVycyAkXGdhbW1hJCBhbmQgJFxkZWx0YSQuICQkIGYoXHRoZXRhIFw7IHwgXDsgXGdhbW1hLFxkZWx0YSkgPSBcZnJhY3tcdGhldGFee1xnYW1tYS0xfSAoMS1cdGhldGEpXntcZGVsdGEtMX19e0IoXGdhbW1hLFxkZWx0YSl9LCBccXF1YWQgXGdhbW1hPjAsIFxkZWx0YT4wJCQNCg0KNi4gIFRoZSBwdXJjaGFzZSBwcm9iYWJpbGl0eSAkcCQgYW5kIHRoZSBkcm9wb3V0IHByb2JhYmlsaXR5ICRcdGhldGEkIHZhcnkgKippbmRlcGVuZGVudGx5KiogYWNyb3NzIGN1c3RvbWVycy4NCg0KW14zXTogSXQncyBub3Qgc2hpZnRlZCBiZWNhdXNlIDAgaXMgYSB2YWxpZCBvdXRjb21lLg0KDQojIyBMaWtlbGlob29kIEJHL0JCDQoNClRoZSBsaWtlbGlob29kIGlzIGEgZnVuY3Rpb24gb2YgdGhyZWUgc3VtbWFyeSBzdGF0aXN0aWNzOg0KDQoxLiAgJG4kIHRyYW5zYWN0aW9uIG9wcG9ydHVuaXRpZXMNCg0KMi4gICR0X3gkLCByZWNlbmN5LCB0aGUgcGVyaW9kIG9mIHRoZSBsYXN0IGRvbmF0aW9uIHJlbGF0aXZlIHRvIHRoZSBmaXJzdCBkb25hdGlvbi4gKFRoaXMgaXMgb3Bwb3NpdGUgdG8gaG93IHdlIHVzdWFsbHkgdGhpbmsgb2YgcmVjZW5jeSwgbW9yZSBiZWxvdy4pDQoNCjMuICAkeCQsIGZyZXF1ZW5jeSwgdGhlIHRvdGFsIG51bWJlciBvZiBkb25hdGlvbnMNCg0KRm9yIGVhY2ggJFx7biwgdF94LCB4XH0kLCB3ZSBzdW0gb3ZlciB0aGUgcG9zc2libGUgaGlkZGVuIHN0YXRlIHJlYWxpemF0aW9ucywgYWxpdmUgYW5kIGRlYWQ6DQoNCiQkDQpMKFxhbHBoYSwgXGJldGEsIFxnYW1tYSwgXGRlbHRhIFxtaWQgbiwgdF94LCB4KSA9IFx1bmRlcmJyYWNle1xmcmFje0IoXGFscGhhK3gsIFxiZXRhICsgbi14KX17QihcYWxwaGEsIFxiZXRhKX0gXGZyYWN7QihcZ2FtbWEsIFxkZWx0YSArIG4pfXtCKFxnYW1tYSwgXGRlbHRhKX19X1x0ZXh0e2FsaXZlIGFsbCBwZXJpb2RzfSArICBcdW5kZXJicmFjZXsgXHN1bV97aT0wfV57bi10X3gtMX0gXDsgXGZyYWN7QihcYWxwaGEreCwgXGJldGEgKyB0X3ggLXggKyBpICl9e0IoXGFscGhhLCBcYmV0YSl9IFxmcmFje0IoXGdhbW1hKzEsIFxkZWx0YSArIHRfeCtpKX17QihcZ2FtbWEsIFxkZWx0YSl9fV9cdGV4dHthbGwgcGF0aHMgd2l0aCBkZWF0aCBiZWZvcmUgZW5kfQ0KJCQgSWYgJHg9MCwgdF94PTAkLiBUaGlzIHJlY2VuY3kgaXMgbWVhc3VyZWQgYXMgdGhlIHRpbWUgb2YgbGFzdCBwdXJjaGFzZSBmcm9tIHRoZSBiZWdpbm5pbmcgb2YgdGhlIG9ic2VydmF0aW9uIHBlcmlvZDsgaW4gdGhlIHBhc3Qgd2UndmUgbWVhc3VyZWQgcmVjZW5jeSBhcyB0aGUgdGltZSBmcm9tIGxhc3QgcHVyY2hhc2UgdW50aWwgdGhlIGVuZCBvZiB0aGUgb2JzZXJ2YXRpb24gcGVyaW9kICgkbi10X3gkKS4NCg0KSWYgdGhlcmUgd2FzIGEgZG9uYXRpb24gaW4gdGhlIGxhc3QgcGVyaW9kLCAkdF94PW4kLCB0aGVuICRuLXRfeC0xPS0xJCwgbWVhbmluZyB0aGUgdXBwZXIgbGltaXQgb2YgdGhlIHN1bSBpbiB0aGUgc2Vjb25kIHRlcm0gaXMgbG93ZXIgdGhhbiB0aGUgbG93ZXIgbGltaXQsIGFuZCBieSBjb252ZW50aW9uLCB0aGUgdGVybSBkcm9wcyBvdXQuIFRoaXMgbWVhbnMgdGhhdCBoZS9zaGUgbXVzdCBiZSBhbGl2ZSBhdCB0aGUgZW5kIG9mIHRoZSBsYXN0IHBlcmlvZCwgc2luY2Ugb25seSBhbGl2ZSBjdXN0b21lcnMgY2FuIG1ha2UgZG9uYXRpb25zLiBJbiB0aGF0IGNhc2UsDQoNCiQkDQpMKFxhbHBoYSwgXGJldGEsIFxnYW1tYSwgXGRlbHRhIFxtaWQgbiwgdF94PW4sIHgpID0gXGZyYWN7QihcYWxwaGEreCwgXGJldGEgKyBuLXgpfXtCKFxhbHBoYSwgXGJldGEpfSBcZnJhY3tCKFxnYW1tYSwgXGRlbHRhICsgbil9e0IoXGdhbW1hLCBcZGVsdGEpfQ0KJCQNCg0KIyMgTG9hZGluZyB0aGUgZGF0YQ0KDQpGaXJzdCB3ZSBoYXZlIHRvIGxvYWQgdGhlIFIgcGFja2FnZSwgYEJUWURgIGFuZCBzb21lIGF1eGlsaWFyeSBwYWNrYWdlcyBhcyB3ZWxsLiBZb3UgY2FuIGRvd25sb2FkIHRoZSBwYWNrYWdlIGZyb20gaGVyZToNCg0KYGBge3IgZXZhbCA9IEZBTFNFfQ0KaW5zdGFsbC5wYWNrYWdlcygnaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvc3JjL2NvbnRyaWIvQXJjaGl2ZS9CVFlEL0JUWURfMi40LnRhci5neicsIHJlcG9zID0gTlVMTCwgdHlwZSA9ICJzb3VyY2UiKQ0KYGBgDQoNCllvdSBjYW4gbG9hZCB0aGUgcGFja2FnZToNCg0KYGBge3IgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkoIkJUWUQiKQ0Kb3B0aW9ucygic2NpcGVuIj0xMDAsICJkaWdpdHMiPTMsIHdpZHRoID0gMzAwKQ0KDQpgYGANCg0KTG9hZCB0aGUgZG9uYXRpb24gZGF0YSwgYW5kIGdldCB0aGUgdGhlIGNhbGlicmF0aW9uIHBlcmlvZCByZWNlbmN5LWZyZXF1ZW5jeSBtYXRyaXg6DQoNCmBgYHtyfQ0KZGF0YShkb25hdGlvbnNTdW1tYXJ5KQ0KDQpyZi5tYXRyaXggPC0gZG9uYXRpb25zU3VtbWFyeSRyZi5tYXRyaXgNCmhlYWQocmYubWF0cml4KSAlPiUgDQogIGtibCgpICU+JQ0KICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KDQpJZiAkZl9qJCBpcyB0aGUgbnVtYmVyIG9mIGN1c3RvbWVycyBpbiBlYWNoIG9mIHRoZSAkSiQgcmVjZW5jeS1mcmVxdWVuY3kgY2VsbHMgaW4gdGhlIHJmLm1hdHJpeCBhcyBhYm92ZSAoJGZfMSA9JCBgciByZi5tYXRyaXhbMSw0XWAsICRmXzIgPSQgYHIgcmYubWF0cml4WzIsNF1gLCAuLi4pIHRoZSBzYW1wbGUgbG9nIGxpa2VsaWhvb2QgaXMgdGhlbjoNCg0KJCQNCkxMKFxhbHBoYSwgXGJldGEsIFxnYW1tYSwgXGRlbHRhKSA9IFxzdW1fe2o9MX1eSiBmX2ogXGxvZ1tMKFxhbHBoYSwgXGJldGEsIFxnYW1tYSwgXGRlbHRhIFxtaWQgbiwgdF94LCB4KV0NCiQkDQoNCldlIG1heGltaXplIHRoaXMgdG8gZmluZCB0aGUgcGFyYW1ldGVycyAkXHtcYWxwaGEsIFxiZXRhLCBcZ2FtbWEsIFxkZWx0YVx9JC4NCg0KSGVyZSBpcyB0aGUgZG9uYXRpb24gZGF0YSB3ZSBtZW50aW9uZWQgaW4gdGhlIGxlY3R1cmUuIEFsbCBkb25vcnMgaW4gdGhpcyAqKmNvaG9ydCoqIG1hZGUgdGhlaXIgZmlyc3QgZG9uYXRpb24gaW4gMTk5NS4gV2hhdCB3ZSBoYXZlIGlzIHRoZWlyICoqcmVwZWF0KiogZG9uYXRpb24gaGlzdG9yeSAxOTk2LTIwMDYuIFdlIGZpdCB0aGUgbW9kZWwgdG8gb25seSB0aGUgKipyZXBlYXQqKiBkYXRhLCBub3QgdGhlICoqZmlyc3QgZG9uYXRpb24qKi4NCg0KYGBge3J9DQpwYXIobWZyb3c9YygxLDEpKQ0KcGFyKG1haT1jKC44LC44LC4yLC4yKSkNCnBsb3Qoc2VxKDE5OTYsMjAwNiwxKSxkb25hdGlvbnNTdW1tYXJ5JGFubnVhbC50cmFucywgdHlwZT0iYiIsIHlsYWI9IlRvdGFsIG51bWJlciBvZiByZXBlYXQgdHJhbnNhY3Rpb25zIiwgeGxhYj0iWWVhciIsIG1haW49IiIsIHhheHQ9J24nKQ0KeC50aWNrbWFya3MueXJzLmFsbCA8LSBjKCAiJzk2IiwiJzk3IiwiJzk4IiwiJzk5IiwiJzAwIiwiJzAxIiwiJzAyIiwiJzAzIiwiJzA0IiwiJzA1IiwiJzA2IiApDQojYXhpcygxLCBhdCA9IHNlcSgwLCAxMSwgYnkgPSAxKSkNCmF4aXMoMSwgYXQ9c2VxKDE5OTYsMjAwNiwxKSxsYWJlbHMgPSB4LnRpY2ttYXJrcy55cnMuYWxsKQ0KYWJsaW5lKHY9MjAwMS41LGNvbCA9ICJyZWQiLCBsd2QgPSAyKQ0KdGV4dCh4ID0gMTk5OSx5ID0gNTAwMCwiQ2FsaWJyYXRpb24iLCBjZXg9MSwgcG9zPTMsIGNvbD0iYmxhY2siLCBmb250ID0gMikNCnRleHQoeCA9IDIwMDQseSA9IDUwMDAsIlZhbGlkYXRpb24iLCBjZXg9MSwgcG9zPTMsIGNvbD0iYmxhY2siLCBmb250ID0gMikNCmBgYA0KDQpPdXIgY2FsaWJyYXRpb24gZGF0YSBpcyAxOTk2LTIwMDEsIHNvIGl0IGxhc3RzIDYgcGVyaW9kcyAoJG49NiQpLiBXZSB2YWxpZGF0ZSB0aGUgbW9kZWwgdXNpbmcgeWVhcnMgMjAwMi0yMDA2LiBBbGwgd2UgbmVlZCB0byBlc3RpbWF0ZSB0aGUgbW9kZWwgYXJlIHRoZSBzdWZmaWNpZW50IHN0YXRpc3RpY3M6DQoNCi0gICAqKiJyZXZlcnNlIiByZWNlbmN5ICgqKiR0X3gkKTogdGhlIGxhc3QgcGVyaW9kIGEgZG9uYXRpb24gb2NjdXJyZWQuIFNpbmNlIGFyZSBkYXRhIGNvbXByaXNlIDYgcGVyaW9kcywgdGhlIG1vc3QgcmVjZW50IGRvbmF0aW9uIGlzIDYsIG9yICR0X3g9NiQuIElmIG5vIHJlcGVhdCBkb25hdGlvbnMgd2VyZSBvYnNlcnZlZCwgJHRfeCA9IDAkLiAoVXN1YWxseSBtYXJrZXRlcnMgdGhpbmsgb2YgcmVjZW5jeSBhcyB0aGUgbnVtYmVyIG9mIHBlcmlvZHMgKipzaW5jZSoqIGxhc3QgcHVyY2hhc2UsICRuLXRfeCQuIEhlcmUgcmVjZW5jeSBpcyB0aW1lIGFmdGVyIGZpcnN0IHB1cmNoYXNlLikgTm90ZSBmdXJ0aGVyIHRoYXQgaWYgJHg9MCwgXDsgdF94PTAkLg0KLSAgICoqZnJlcXVlbmN5ICgqKiR4JCk6IHRoZSBudW1iZXIgb2YgcmVwZWF0IGRvbmF0aW9ucyBvYnNlcnZlZCBpbiB0aGUgc2l4IHN1YnNlcXVlbnQgcGVyaW9kcy4NCi0gICAqKm51bWJlciBvZiBwdXJjaGFzZSBvcHBvcnR1bml0aWVzICgqKiRuJCk6IHRoaXMgaXMgdXN1YWxseSB0aGUgc2FtZSBmb3IgZXZlcnlvbmUuIEluIHRoaXMgY2FzZSAkbj02JC4NCg0KVGhlcmUgYXJlIGByIG5yb3coZG9uYXRpb25zU3VtbWFyeSRyZi5tYXRyaXgpYCByZWNlbmN5LWZyZXF1ZW5jeSBjb21iaW5hdGlvbnMuIFRoZSBudW1iZXIgb2YgY3VzdG9tZXJzIGluIGVhY2ggY2VsbCBpcyBiZWxvdy4NCg0KWW91IGNhbiBzZWUsIGZvciBleGFtcGxlLCB0aGF0IHRoZXJlIGFyZSBgciByZi5tYXRyaXhbMSw0XWAgY3VzdG9tZXJzIHdobyBhcmUgIjYgZm9yIDYiLiBBbmQgdGhlcmUgYXJlIGByIHJmLm1hdHJpeFsyMiw0XWAgY3VzdG9tZXJzIHdobyBtYWRlIG5vIHJlcGVhdCBkb25hdGlvbnMsICIwIGZvciA2Ii4gVGhlIG1vZGVsIGlzIGdvaW5nIHRvIGhhdmUgdG8gYWNjb3VudCBmb3IgdGhlc2UgZGlmZmVyZW5jZXMuDQoNCiMjIEVzdGltYXRlIHBhcmFtZXRlcnMgZm9yIHRoZSBCRy9CQiBtb2RlbCBmcm9tIHRoZSByZWNlbmN5LWZyZXF1ZW5jeSBtYXRyaXg6DQoNCldlIGdpdmUgaW5pdGlhbCBndWVzc2VzIHRvIHRoZSBmb3VyIHBhcmFtZXRlcnMgaW4gcGFyLnN0YXJ0LiBiZ2JiLkVzdGltYXRlUGFyYW1ldGVycyBlc3RpbWF0ZXMgdGhlIHBhcmFtZXRlcnMuDQoNCmBgYHtyfQ0KIyAgICAgICAgICAgIGFscGhhIGJldGEgZ2FtbWEgZGVsdGENCnBhci5zdGFydCA8LSBjKDEsIC41LCAxLCAuNSkNCg0KcGFyYW1zIDwtIGJnYmIuRXN0aW1hdGVQYXJhbWV0ZXJzKHJmLm1hdHJpeCwgcGFyLnN0YXJ0KQ0KDQojIyBzdG9yZSBwYXJhbWV0ZXJzIG5leHQgdG8gbmFtZXMNCm5hbWVzKHBhcmFtcykgPC0gYygiYWxwaGEiLCAiYmV0YSIsICJnYW1tYSIsICJkZWx0YSIpOw0KDQpyb3VuZChwYXJhbXMsMikNCg0KIyMgQ2hlY2sgbG9nLWxpa2VsaWhvb2Qgb2YgdGhlIHBhcmFtczoNCkxMIDwtIGJnYmIucmYubWF0cml4LkxMKHBhcmFtcywgcmYubWF0cml4KQ0KDQpgYGANCg0KIyMjIFBhcmFtZXRlciBFc3RpbWF0ZXMgYW5kIERpc3RyaWJ1dGlvbnMNCg0KV2UgcGxvdCB0aGUgYmV0YSBkaXN0cmlidXRpb25zIGltcGxpZWQgYnkgdGhlIG1heGltdW0gbGlrZWxpaG9vZCBlc3RpbWF0ZXMuDQoNCmBgYHtyLCBvdXQud2lkdGggPSAnMTAwJSd9DQpwYXIobWZyb3c9YygxLDIpKQ0KcGFyKG1haT1jKC44LC44LC41LC4yKSkNCnRlbXAgPC0gYmdiYi5QbG90VHJhbnNhY3Rpb25SYXRlSGV0ZXJvZ2VuZWl0eShwYXJhbXMpDQpwYXIobWFpPWMoLjgsLjgsLjUsLjIpKQ0KdGVtcCA8LSBiZ2JiLlBsb3REcm9wb3V0UmF0ZUhldGVyb2dlbmVpdHkocGFyYW1zKQ0KYGBgDQoNClJlbWVtYmVyLCBpZiAkWCBcc2ltIFx0ZXh0cm17QmV0YX0oYSxiKSwgXDsgRVtYXSA9IFxmcmFje2F9e2ErYn0kLiBTbyB0aGUgbWVhbiBvZiB0aGUgdHJhbnNhY3Rpb24gcmF0ZSB3aGlsZSBhbGl2ZSBpcyBgciByb3VuZChwYXJhbXNbMV0vc3VtKHBhcmFtc1sxOjJdKSwyKWAgYW5kIHRoZSBtZWFuIG9mIHRoZSBkcm9wIG91dCBwcm9jZXNzIGlzIGByIHJvdW5kKHBhcmFtc1szXS9zdW0ocGFyYW1zWzM6NF0pLDIpYC4NCg0KYGBge3J9DQojIE1lYW4gb2YgdHJhbnNhY3Rpb24gcmF0ZSB3aGlsZSBhbGl2ZToNCnBhcmFtc1sxXS8ocGFyYW1zWzFdK3BhcmFtc1syXSkNCiMgTWVhbiBvZiBkcm9wLW91dCBwcm9jZXNzOg0KcGFyYW1zWzNdLyhwYXJhbXNbM10rcGFyYW1zWzRdKQ0KYGBgDQoNCiMjIE1vZGVsIGZpdA0KDQojIyMgQWdncmVnYXRlDQoNCldlIGNhbiBzZWUgaG93IHdlbGwgdGhlIG1vZGVsIGRvZXMgaW4gcHJlZGljdGluZyB0aGUgYWdncmVnYXRlIG51bWJlciBvZiBkb25hdGlvbnMgb3ZlciB5ZWFycy4gVGhpcyB1c2VzIGVxdWF0aW9uIDggaW4gdGhlIEZIUyAoMjAxMCkuDQoNCmBgYHtyfQ0KaW5jLmFubnVhbC50cmFucyA8LSBkb25hdGlvbnNTdW1tYXJ5JGFubnVhbC50cmFucyAgICMgaW5jcmVtZW50YWwgYW5udWFsIHRyYW5zYWN0aW9ucw0KDQpwYXIobWZyb3c9YygxLDEpKQ0KIyMgUGxvdCB0aGUgY29tcGFyaXNvbiBvZiBhY3R1YWwgYW5kIGV4cGVjdGVkIHRvdGFsIGluY3JlbWVudGFsIHRyYW5zYWN0aW9ucyBhY3Jvc3MNCiMjIGJvdGggdGhlIGNhbGlicmF0aW9uIGFuZCBob2xkb3V0IHBlcmlvZHM6DQpwYXIobWFpPWMoLjgsLjgsLjMsLjIpKQ0KDQpwcmVkIDwtIGJnYmIuUGxvdFRyYWNraW5nSW5jKHBhcmFtcywgcmYubWF0cml4LCBpbmMuYW5udWFsLnRyYW5zLCB4dGlja2xhYj14LnRpY2ttYXJrcy55cnMuYWxsKVsyLF0NCg0KdGV4dCh4ID0gNCx5ID0gNTAwMCwiQ2FsaWJyYXRpb24iLCBjZXg9MSwgcG9zPTMsIGNvbD0iYmxhY2siLCBmb250ID0gMikNCnRleHQoeCA9IDcseSA9IDUwMDAsIlZhbGlkYXRpb24iLCBjZXg9MSwgcG9zPTMsIGNvbD0iYmxhY2siLCBmb250ID0gMikNCg0KIyBUaGUgaW5jcmVtZW50YWwgdHJhbnNhY3Rpb25zIHVzaW5nIHRoZSBmb3JtdWxhLCBlcXVhdGlvbiA4LCBpbiB0aGUgcGFwZXIuICBkb25hdGlvbnMgc2hvdWxkIGVxdWFsIHByZWQgYWJvdmUuDQoNCmFsIDwtIHBhcmFtc1sxXQ0KYmUgPC0gcGFyYW1zWzJdDQpnYSA8LSBwYXJhbXNbM10NCmRlIDwtIHBhcmFtc1s0XQ0KDQpubiA8LSBzZXEoMSwxMSkNCk4gPC0gc3VtKHJmLm1hdHJpeFssNF0pDQpFcTggPC0gKGFsLyhhbCtiZSkpKihkZS8oZ2EtMSkpKigxLShnYW1tYShnYStkZSkqZ2FtbWEoMStkZStubikvKGdhbW1hKGdhK2RlK25uKSpnYW1tYSgxK2RlKSkpKQ0KDQpjdW1fZG9uYXRpb25zIDwtIE4qRXE4DQpkb25hdGlvbnMgPC0gYyhjdW1fZG9uYXRpb25zWzFdLGRpZmYoY3VtX2RvbmF0aW9ucykpDQoNCg0KYGBgDQoNCkhlcmUgaXMgZm9yIHRoZSBjdW11bGF0aXZlIG51bWJlciBvZiBkb25hdGlvbnMuDQoNCmBgYHtyfQ0KcGFyKG1haT1jKC44LC44LC4zLC4yKSkNCg0KcHJlZCA8LSBiZ2JiLlBsb3RUcmFja2luZ0N1bShwYXJhbXMsIHJmLm1hdHJpeCwgYWN0dWFsLmN1bS5yZXBlYXQudHJhbnNhY3Rpb25zID0gY3Vtc3VtKGRvbmF0aW9uc1N1bW1hcnkkYW5udWFsLnRyYW5zKSwgeHRpY2tsYWI9eC50aWNrbWFya3MueXJzLmFsbClbMixdDQoNCnRleHQoeCA9IDQseSA9IDUwMDAsIkNhbGlicmF0aW9uIiwgY2V4PTEsIHBvcz0zLCBjb2w9ImJsYWNrIiwgZm9udCA9IDIpDQp0ZXh0KHggPSA3LHkgPSA1MDAwLCJWYWxpZGF0aW9uIiwgY2V4PTEsIHBvcz0zLCBjb2w9ImJsYWNrIiwgZm9udCA9IDIpDQpgYGANCg0KIyMjIENvbmRpdGlvbmFsIEV4cGVjdGF0aW9ucw0KDQpBIHZlcnkgaW1wb3J0YW50IHRlc3Qgb2YgYSBtb2RlbCBpcyBob3cgd2VsbCBpdCBwcmVkaWN0cyAqKmF0IHRoZSBpbmRpdmlkdWFsIGxldmVsLCBhZnRlciBjb25kaXRpb25pbmcgb24gYSBwYXJ0aWN1bGFyIGluZGl2aWR1YWwgaGlzdG9yeSoqLiBHaXZlbiBhIGN1c3RvbWVyIHdpdGggaGlzdG9yeSAkKHgsIHRfeCwgbikkLCAoYSkgaG93IG1hbnkgcHVyY2hhc2VzIGRvIHdlIHByZWRpY3QgaW4gdGhlIG5leHQgJG5eKiQgcGVyaW9kcyBhbmQgKGIpIGhvdyB3ZWxsIGRvZXMgaXQgdHJhY2sgYWN0dWFsIGhvbGRvdXQgcHVyY2hhc2VzPw0KDQpXZSBmaXJzdCBkbyAoYSkuIFVzaW5nIGVxdWF0aW9uIDEzLCB3ZSBjYW4gY2FsY3VsYXRlIHRoZSBleHBlY3RlZCBudW1iZXIgb2YgcHVyY2hhc2VzIGZvciBlYWNoICQoeCwgdF94LCBuKSQgZ3JvdXAgaW4gdGhlIG5leHQgJG5eKiA9IDUkIHBlcmlvZHMuDQoNCmBgYHtyfQ0KcGFyKG1haT1jKC44LC44LC41LC4yKSkNCmNvbXAgPC0gYmdiYi5IZWF0bWFwSG9sZG91dEV4cGVjdGVkVHJhbnMocGFyYW1zLCBuLmNhbD02LCBuLnN0YXI9NSkNCmBgYA0KDQpgYGB7cn0NCiMgcm90YXRlIG1hdHJpeCBzbyBpdCdzIHRoZSBzYW1lIGRpcmVjdGlvbiBhcyB0aGUgaGVhdG1hcCwgdGhpcyBpcyBqdXN0IHRvIG1ha2UgdGhlIG51bWJlcnMgZWFzaWVyIHRvIHJlYWQNCnJvdGF0ZSA8LSBmdW5jdGlvbih4KSB0KGFwcGx5KHgsIDIsIHJldikpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQp0ZXN0IDwtIGthYmxlKHJvdGF0ZShyb3RhdGUocm90YXRlKHQocm91bmQoY29tcCwyKSkpKSksIGZvcm1hdCA9ICJwaXBlIiwgYm9va3RhYnM9RiwgYWxpZ24gPSAiYyIsIGNhcHRpb24gPSAiKipQcmVkaWN0ZWQgaG9sZG91dCBwdXJjaGFzZXMgKEJHL0JCIE1vZGVsKSBiYXNlZCBvbiBmcmVxdWVuY3kgKHJvd3MpIHggcmVjZW5jeSAoY29sdW1ucykqKiIpDQoNCmBgYA0KDQpBIGRvbm9yIHdobyBkb25hdGVkIGV2ZXJ5IHllYXIgZXhjZXB0IHRoZSBsYXN0ICQoeD01LCB0X3g9NSwgbj02KSQgaXMgcHJlZGljdGVkIHRvIG1ha2UgYHIgcm91bmQoY29tcFtbNiw2XV0sMilgIGluIHRoZSBuZXh0ICRuXio9NSQgcGVyaW9kcy4gWWV0IGEgZG9ub3Igd2l0aCBiZXR0ZXIgcmVjZW5jeSBidXQgbG93ZXIgZnJlcXVlbmN5ICQoeD00LHRfeD02LCBuPTYpJCBoYXMgYSBoaWdoZXIgZXhwZWN0ZWQgdHJhbnNhY3Rpb24gcmF0ZSwgYHIgcm91bmQoY29tcFtbNSw3XV0sMilgLiBBcyBtZW50aW9uZWQgaW4gRmFkZXIsIEhhcmRpZSBhbmQgU2hhbmcgKDIwMTApLCB0aGlzIGhpZ2hsaWdodHMgdGhlIGltcG9ydGFuY2Ugb2YgcmVjZW5jeS4NCg0KTm93IHdlIGRvIChiKS4gVGhlIGFjdHVhbCBudW1iZXIgb2YgdG90YWwgaG9sZG91dCBwdXJjaGFzZXMgYnkgY3VzdG9tZXJzIGluIGVhY2ggUkYgY2F0ZWdvcnkgaXMgZ2l2ZW4gaW4gdGhlIHZhcmlhYmxlIHguc3Rhci4gVGhlcmVmb3JlIHRoZSBhdmVyYWdlIG51bWJlciBvZiBob2xkb3V0IHB1cmNoYXNlcyBwZXIgUkYgY2F0ZWdvcnkgaXMgdGhlIHRvdGFsIGRpdmlkZWQgYnkgdGhlIG51bWJlciBvZiBjdXN0b21lcnMuIFdlIGFkZCB0aGlzIHRvIHRoZSBSRiBtYXRyaXggYW5kIHJlc2hhcGUuDQoNCmBgYHtyfQ0Kbi5zdGFyIDwtIDUgICAgICAgICAgICAgICAgICAgICAgICAjIE51bWJlciBvZiB0cmFuc2FjdGlvbiBvcHBvcnR1bml0aWVzIGluIHRoZSBob2xkb3V0IHBlcmlvZA0KeC5zdGFyIDwtIGRvbmF0aW9uc1N1bW1hcnkkeC5zdGFyICAjIFRyYW5zYWN0aW9ucyBtYWRlIGJ5IGVhY2ggY2FsaWJyYXRpb24gcGVyaW9kIGJpbiBpbiB0aGUgaG9sZG91dCBwZXJpb2QNCg0KWDwteC5zdGFyL3JmLm1hdHJpeFssImN1c3RzIl0NCg0KaG9sX3JmX3RyYW5zPC1hcy5kYXRhLmZyYW1lKGNiaW5kKHJmLm1hdHJpeFssMToyXSxYKSkNCg0KYWN0dWFsX3JmPC1yZXNoYXBlKGhvbF9yZl90cmFucywgaWR2YXI9InQueCIsIHRpbWV2YXI9IngiLCBkaXJlY3Rpb249IndpZGUiKQ0KDQojIGNoYW5nZSBOQSdzIHRvIDANCmFjdHVhbF9yZltpcy5uYShhY3R1YWxfcmYpXSA8LSAwDQoNCiMgcmUtb3JkZXIgY29sdW1ucyBhbmQgcm93cw0KYWN0dWFsX3JmPC1hY3R1YWxfcmZbb3JkZXIoYWN0dWFsX3JmWywxXSksb3JkZXIoYWN0dWFsX3JmWzEsXSldDQoNCiMgbWFrZSB0eCB0byByb3duYW1lDQpyb3duYW1lcyhhY3R1YWxfcmYpIDwtIGFjdHVhbF9yZlssOF0NCg0KIyBkZWxldGUgdHgNCmFjdHVhbF9yZjwtYWN0dWFsX3JmWyxjKC04KV0NCg0KI2FjdHVhbF9yZg0KDQojIHRvIG1ha2UgaXQgbG9vayBuaWNlIGFuZCByb3RhdGVkIGluIHNhbWUgd2F5IGFzIGhlYXRtYXANCmthYmxlKHJvdGF0ZShyb3RhdGUocm90YXRlKChyb3VuZChhY3R1YWxfcmYsMikpKSkpLCBmb3JtYXQgPSAicGlwZSIsIGJvb2t0YWJzPUYsIGFsaWduID0gImMiLCBjYXB0aW9uID0gIioqQWN0dWFsIGhvbGRvdXQgYXZlcmFnZSBwdXJjaGFzZXMgYmFzZWQgb24gZnJlcXVlbmN5IChyb3dzKSB4IHJlY2VuY3kgKGNvbHVtbnMpKioiKQ0KYGBgDQoNCiMjIyBDb25kaXRpb25pbmcgb24gZnJlcXVlbmN5ICYgcmVjZW5jeSBzZXBhcmF0ZWx5DQoNCk5leHQgd2UgY2FuIGhvdyB3ZWxsIHRoZSBtb2RlbCBkb2VzIGlmIHdlIGNvbmRpdGlvbiBvbiB0aGUgZnJlcXVlbmN5IG9mIHRyYW5zYWN0aW9ucyBpbiB0aGUgY2FsaWJyYXRpb24gcGVyaW9kLCBhdmVyYWdpbmcgb3ZlciByZWNlbmN5LiBJbiBvdGhlciB3b3Jkcywgd2UgdGFrZSBldmVyeW9uZSB3aG8gaGFzIGhhZCBhIGZyZXF1ZW5jeSBvZiAkeCQgdHJhbnNhY3Rpb25zIGluIHRoZSBjYWxpYnJhdGlvbiBwZXJpb2QsIGFuZCB3ZSBjYW4gY29tcGFyZSBob3cgbWFueSBhY3R1YWwgdHJhbnNhY3Rpb25zIHRoZXkgaGFkIGluIHRoZSB2YWxpZGF0aW9uIHBlcmlvZCB3aXRoIHRoZSBwcmVkaWN0aW9ucy4NCg0KYGBge3Igb3V0LndpZHRoID0gJzkwJScsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQ0KcGFyKG1haT1jKC44LC44LC41LC4yKSkNCg0KIyMgUGxvdCB0aGUgY29tcGFyaXNvbiBvZiBhY3R1YWwgYW5kIGNvbmRpdGlvbmFsIGV4cGVjdGVkIGhvbGRvdXQgcGVyaW9kIGZyZXF1ZW5jaWVzLA0KIyMgYmlubmVkIGFjY29yZGluZyB0byBjYWxpYnJhdGlvbiBwZXJpb2QgZnJlcXVlbmNpZXM6DQpmcmVxIDwtIGJnYmIuUGxvdEZyZXFWc0NvbmRpdGlvbmFsRXhwZWN0ZWRGcmVxdWVuY3kocGFyYW1zLCBuLnN0YXIsIHJmLm1hdHJpeCwgeC5zdGFyKQ0KDQpyb3duYW1lcyhmcmVxKSA8LSBjKCJhY3QiLCAiZXhwIiwgImJpbiIpDQpmcmVxDQpgYGANCg0KTW9kZWwgcHJlZGljdGlvbnMgY2xvc2VseSB0cmFjayBhY3R1YWwgZG9uYXRpb25zLiBEb25vcnMgd2hvIG1hZGUgemVybyBkb25hdGlvbnMgMTk5Ni0yMDAxLCBtYWRlIG9uIGF2ZXJhZ2UgYHIgcm91bmQoY29tcFtbMV1dLDIpYCBkb25hdGlvbnMgaW4gMjAwMi0yMDA2LiBUaGUgQkcvQkIgbW9kZWwgcHJlZGljdHMgc2xpZ2h0bHkgZmV3ZXIsIGByIHJvdW5kKGNvbXBbWzJdXSwyKWAuIERvbm9ycyB3aG8gbWFkZSBhIGRvbmF0aW9uIGV2ZXJ5IHllYXIsICI2IGZvciA2IiwgbWFkZSBgciByb3VuZChjb21wW1sxLDddXSwyKWAgZG9uYXRpb25zIGluIHRoZSBzdWJzZXF1ZW50IDUgeWVhcnMuIFRoZSBtb2RlbCBwcmVkaWN0aW9ucyBhcmUgbW9kZXN0bHkgaGlnaGVyLCBhdCBgciByb3VuZChjb21wW1syLDddXSwyKWAuIEl0J3MgaW50ZXJlc3RpbmcgdG8gbm90ZSB0aGF0IGEgbmFpdmUgcHJlZGljdGlvbiBvZiBhIGRvbm9yIHdobyBpcyAiNiBmb3IgNiIgc28gd291bGQgdGhlcmVmb3JlIGJlIGEgIjUgZm9yIDUiIGRvbm9yIGluIHRoZSB2YWxpZGF0aW9uIHBlcmlvZCB3b3VsZCBvdmVyZXN0aW1hdGUgZG9uYXRpb25zIGJ5IHF1aXRlIGEgbG90Lg0KDQpJbnN0ZWFkIG9mIGdyb3VwaW5nIGN1c3RvbWVycyBieSBmcmVxdWVuY3ksIHdlIGNhbiBhbHNvIGNvbmRpdGlvbiBvbiB0aGVpciByZWNlbmN5LCBpLmUuLCB0aGUgbGFzdCBwZXJpb2QgdGhleSBtYWRlIGEgZG9uYXRpb246DQoNCmBgYHtyIG91dC53aWR0aCA9ICc5MCUnLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0NCnBhcihtYWk9YyguOCwuOCwuNSwuMikpDQoNCnJlYzwtYmdiYi5QbG90UmVjVnNDb25kaXRpb25hbEV4cGVjdGVkRnJlcXVlbmN5KHBhcmFtcywgbi5zdGFyLCByZi5tYXRyaXgsIHguc3RhcikNCnJvd25hbWVzKHJlYykgPC0gYygiYWN0IiwgImV4cCIsICJiaW4iKQ0KcmVjDQpgYGANCg0KVGhlcmUgd2VyZSBgciBjb21wW1szLDddXWAgZG9ub3JzIHdpdGggbWF4aW11bSByZWNlbmN5LCBpLmUuLCBtYWtpbmcgYSBkb25hdGlvbiBpbiAyMDAxLiBUaG9zZSBkb25vcnMgbWFkZSBvbiBhdmVyYWdlIGByIHJvdW5kKGNvbXBbWzEsN11dLDEpYCBkb25hdGlvbnMgaW4gdGhlIHN1YnNlcXVlbnQgNSB5ZWFycywgYW5kIHRoZSBtb2RlbCBwcmVkaWN0cyB0aGF0IHRoZXkgd291bGQgbWFrZSBgciByb3VuZChjb21wW1syLDddXSwxKWAuIFRoZXJlIGlzIGEgc3RlZXAgZmFsbG9mZiBhcyByZWNlbmN5IGRpbWluaXNoZXMsIG1vdmluZyBmcm9tIHJpZ2h0IHRvIGxlZnQgaW4gdGhlIGdyYXBoIHRoYXQgaXMgY2FwdHVyZWQgYnkgdGhlIG1vZGVsLg0KDQojIyBQKEFsaXZlKQ0KDQpUaGUgcHJvYmFiaWxpdHkgdGhhdCBhIGN1c3RvbWVyIHdpdGggcHVyY2hhc2UgaGlzdG9yeSAkeCwgdF94IG4kIHdpbGwgYmUgYWxpdmUgYXQgdGhlIGJlZ2lubmluZyBvZiBwZXJpb2QgJG4gKyAxJCBpcyB0aGUgdGVybSBpbiB0aGUgbGlrZWxpaG9vZCB3aGVyZSB0aGUgY3VzdG9tZXIgaXMgYWxpdmUgdW50aWwgdGhlIGVuZCBkaXZpZGVkIGJ5IGFsbCB0aGUgcGF0aHM6ICQkDQpQKFx0ZXh0cm17QWxpdmUgYXR9IFw7IG4rMSB8IFw7IG4sIHgsIHRfeCkgPSBcZnJhY3tcZnJhY3tCKFxhbHBoYSt4LCBcYmV0YSArIG4teCl9e0IoXGFscGhhLCBcYmV0YSl9IFxmcmFje0IoXGdhbW1hLCBcZGVsdGEgKyBuKzEpfXtCKFxnYW1tYSwgXGRlbHRhKX19e0woXGFscGhhLCBcYmV0YSwgXGdhbW1hLCBcZGVsdGEgXG1pZCBuLCB0X3gsIHgpfQ0KJCQNCg0KV2UgY2FuIGNhbGN1bGF0ZSBmb3IgYWxsIHRoZSBwb3NzaWJsZSBjZWxscyBpbiBvdXIgcmVjZW5jeS1mcmVxdWVuY3kgbWF0cml4IHRoZSBwcm9iYWJpbGl0eSB0aGF0IHRoZSBjdXN0b21lciBpcyBhY3RpdmUuDQoNCmBgYHtyIG91dC53aWR0aCA9ICc5MCUnLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0NClBBbGl2ZSA8LSBiZ2JiLlBBbGl2ZShwYXJhbXMsIHggPSByZi5tYXRyaXhbLDFdLCB0LnggPSByZi5tYXRyaXhbLDJdLCBuLmNhbCA9IDYpDQpBbGl2ZV9tYXQ8LWNiaW5kKHJmLm1hdHJpeCxQQWxpdmUpDQpBbGl2ZV9tYXQ8LWRhdGEuZnJhbWUoQWxpdmVfbWF0KQ0Ka2FibGUoQWxpdmVfbWF0LCBmb3JtYXQ9InBpcGUiKQ0KYGBgDQoNCmBgYHtyIG91dC53aWR0aCA9ICc5MCUnLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0NCndpdGgoQWxpdmVfbWF0LCBoaXN0KHJlcCh4ID0gQWxpdmVfbWF0JFBBbGl2ZSwgdGltZXMgPSBBbGl2ZV9tYXQkY3VzdHMpLCB4YXh0PSduJywgeGxpbSA9IGMoMCwxKSwgeGxhYiA9ICJQKEFsaXZlIGF0IG4rMSkiLCB5bGFiPSJmcmVxdWVuY3kiLCBtYWluPSJIaXN0b2dyYW0gb2YgUChBbGl2ZSkiKSkNCmF4aXMoc2lkZT0xLCBhdD1zZXEoMCwxLCAuMTApLCBsYWJlbHM9c2VxKDAsMSwuMSkpDQoNCmBgYA0KDQpIb3cgbWFueSB0b3RhbCBhY3RpdmUgb3IgYWxpdmUgY3VzdG9tZXJzIGFyZSB0aGVyZSBhdCBwZXJpb2QgNz8gV2UgY2FuIHN1bSB1cCBhbGwgdGhlIFAoQWxpdmUpIGNlbGxzIGFuZCBudW1iZXIgb2YgY3VzdG9tZXJzIGluIGVhY2ggY2VsbCB0byBnZXQgdGhlIGFuc3dlcjoNCg0KYGBge3J9DQpzdW0oQWxpdmVfbWF0JFBBbGl2ZSpBbGl2ZV9tYXQkY3VzdHMpDQoNCmBgYA0KDQpUaGVyZSBhcmUgYHIgc3VtKEFsaXZlX21hdCRQQWxpdmUqQWxpdmVfbWF0JGN1c3RzKWAgb3V0IG9mIGByIHN1bShBbGl2ZV9tYXQkY3VzdHMpYCBjdXN0b21lcnMgc3RpbGwgYWN0aXZlLg0KDQojIyBJbmNyZWFzaW5nIEZyZXF1ZW5jeSBQYXJhZG94DQoNCkxldCdzIGltYWdpbmUgYSBjdXN0b21lciB3aG8gaGFzIG1hZGUgaGlzIG9yIGhlciBsYXN0IGRvbmF0aW9uIG9uIHBlcmlvZCAkdF94ID0gNCQsIGJ1dCBsZXQncyB2YXJ5IGhvdyBtYW55IHB1cmNoYXNlcyBzaGUgbWFrZXMuIEF0IG1vc3Qgc2hlIGNhbiBtYWtlIDQsIGFuZCBhdCBsZWFzdCAxLiBXZSBjYW4gYXNrIHdoYXQgdGhlIG1vZGVsIHByZWRpY3RzIGlzIHRoZSBudW1iZXIgb2YgcHVyY2hhc2VzIGV4cGVjdGVkIGluIHRoZSBzdWJzZXF1ZW50ICRuXio9NSQgcGVyaW9kcywgYSBjYWxjdWxhdGlvbiB3ZSBhbHJlYWR5IGRpZCBhYm92ZToNCg0KYGBge3IsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQ0KcGFyKG1mcm93PWMoMSwxKSxtYWk9YyguOCwuOCwuNSwuMikpDQpwbG90KGNvbXBbMjo1LDVdLCB5bGFiID0iRXhwZWN0ZWQgdHJhbnNhY3Rpb25zIGluIG5leHQgNSBwZXJpb2RzIiwgeGxhYj0iRnJlcXVlbmN5IGhvbGRpbmcgbGFzdCBkb25hdGlvbiBhdCA0IiwgdHlwZT0iYiIsIHhheHQ9Im4iKQ0KeHRpY2s8LXNlcSgxLCA0LCBieT0xKQ0KYXhpcyhzaWRlPTEsIGF0PXh0aWNrLCBsYWJlbHMgPSBUUlVFKQ0KDQpgYGANCg0KV2hhdCdzIGludGVyZXN0aW5nIGFib3V0IHRoaXMgY3VydmUgaXMgdGhhdCBjdXN0b21lciB3aXRoIHRoZSBsYXJnZXN0IGZyZXF1ZW5jeSBpcyAqKm5vdCoqIHRoZSBvbmUgd2l0aCB0aGUgaGlnaGVzdCBmdXR1cmUgcHJlZGljdGVkIHB1cmNoYXNlcy4gVGhpcyBpcyBzb21ldGhpbmcga25vd24gYXMgdGhlICoqaW5jcmVhc2luZyBmcmVxdWVuY3kgcGFyYWRveCoqLiBXaHk/IFRoZSBsaWtlbGlob29kIHRoYXQgaGUgb3Igc2hlIGlzIHN0aWxsIGFsaXZlIGRlY3JlYXNlcyBpbiAkeCQuDQoNCmBgYHtyLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0NCnBhcihtZnJvdz1jKDEsMSkpDQpwYXIobWFpPWMoLjgsLjgsLjUsLjIpKQ0KDQpwbG90KGJnYmIuUEFsaXZlKHBhcmFtcywgeD0xOjQsIHQueD00LCBuLmNhbD02KSwgeWxhYiA9IlByb2JhYmlsaXR5IHRoYXQgY3VzdG9tZXIgaXMgYWxpdmUgbmV4dCBwZXJpb2QiLCB4bGFiPSJGcmVxdWVuY3kgaG9sZGluZyBsYXN0IGRvbmF0aW9uIGF0IDQiLCB5bGltPWMoMCwxKSwgdHlwZT0iYiIsIHhheHQ9Im4iKQ0KeHRpY2s8LXNlcSgxLCA0LCBieT0xKQ0KYXhpcyhzaWRlPTEsIGF0PXh0aWNrLCBsYWJlbHMgPSBUUlVFKQ0KYGBgDQoNCk9uIHRoZSBvbmUgaGFuZCwgaGlnaGVyICR4JCBtZWFucyBhIGhpZ2hlciAkcCQgd2hpY2ggbWVhbnMgbW9yZSBleHBlY3RlZCB0cmFuc2FjdGlvbnMgaW4gdGhlIGZ1dHVyZS4gT24gdGhlIG90aGVyIGhhbmQsIGlmIHRoZSBsYXN0IHR3byBwZXJpb2RzIHdlcmUgbm8gcHVyY2hhc2VzLCBhIGhpZ2hlciAkeCQgbWVhbnMgdGhhdCAkUChhbGl2ZSkkIGlzIGxvd2VyLiBUaGlzIHNlY29uZCBlZmZlY3QgaXMgc3Ryb25nZXIgdGhhbiB0aGUgZmlyc3QgZWZmZWN0LCByZXN1bHRpbmcgaW4gYSBsb3dlciBleHBlY3RhdGlvbnMgd2hlbiAkeD00JCBjb21wYXJlZCB0byB3aGVuICR4PTIsMyQuDQoNCiMgQ0xWDQoNCkdpdmVuIG1vZGVsIGFzc3VtcHRpb25zIDIgYW5kIDMsIHdlIGtub3cgdGhhdCB0aGUgcHJvYmFiaWxpdHkgb2YgbWFraW5nIGEgcHVyY2hhc2UgaXMgZXF1YWwgdG8gdGhlIHByb2JhYmlsaXR5IHRoYXQgYSBjdXN0b21lciBpcyAqKmFsaXZlKiogdGltZXMgdGhlIHByb2JhYmlsaXR5IG9mIG1ha2luZyBhIHB1cmNoYXNlIGNvbmRpdGlvbmFsIG9uIGJlaW5nIGFsaXZlOiAkJA0KUChcLCBZKHQpID0gMSBcbWlkIHAsIFx0aGV0YSkgPSBwIFwsICgxLVx0aGV0YSledA0KJCQgV2UgaW50ZWdyYXRlICRwJCBhbmQgJFx0aGV0YSQgb3ZlciB0aGVpciBtaXhpbmcgZGlzdHJpYnV0aW9ucyB0byBnZXQgdGhlIHByb2FiaWxpdHkgZm9yIGEgcmFuZG9tbHkgY2hvc2VuIGN1c3RvbWVyOiAkJA0KXGJlZ2lue2FycmF5fXtjY2x9DQpQKFwsIFkodCkgPSAxIFxtaWQgXGFscGhhLCBcYmV0YSwgXGdhbW1hLCBcZGVsdGEpICY9JiBcZGlzcGxheXN0eWxlIFxpbnRfMF4xIFxpbnRfMF4xIFAoXCwgWSh0KSA9IDEgXG1pZCBwLCBcdGhldGEpIFwsIGYocCBcOyB8IFw7IFxhbHBoYSxcYmV0YSkgXCwgZihcdGhldGEgXDsgfCBcOyBcZ2FtbWEsXGRlbHRhKSBcLCBkcCBcLCBkXHRoZXRhIFxcDQomPSYgXGRpc3BsYXlzdHlsZSAgXGxlZnQoXGZyYWN7XGFscGhhfXtcYWxwaGErXGJldGF9XHJpZ2h0KSBcZnJhY3tCKFxnYW1tYSwgXGRlbHRhK3QpfXtCKFxnYW1tYSwgXGRlbHRhKX0NClxlbmR7YXJyYXl9DQokJA0KDQpDTFYgaXMgdGhlbiB0aGUgZGlzY291bnRlZCBzdW0gb2YgdGhlIHByb2JhYmlsaXR5IG9mIG1ha2luZyBhIHRyYW5zYWN0aW9uIHRpbWVzIHNvbWUgYXZlcmFnZSBhbW91bnQgcGVyIHRyYW5zYWN0aW9uICgkbSQpOg0KDQokJA0KXGJlZ2lue2FycmF5fXtjY2x9DQpFW0NMVl0gJiA9ICAmIG0gXDsgXGxlZnQoIDEgKyBcc3VtX3t0PTF9XntcaW5mdHl9IFAoXCwgWSh0KSA9IDEgXG1pZCBcYWxwaGEsIFxiZXRhLCBcZ2FtbWEsIFxkZWx0YSkgXGZyYWN7MX17KDErZCledH0gXHJpZ2h0KSBcXA0KJiA9ICYgbSAgXDsgXHRpbWVzIFx0ZXh0cm17REVUfQ0KXGVuZHthcnJheX0NCiQkDQoNCioqREVUKiogbWVhbnMgRGlzY291bnRlZCBFeHBlY3RlZCBUcmFuc2FjdGlvbnMuIEZvciBpbXBsZW1lbnRpbmcgdGhpcyBpbiBgUmAsIHdlIGhhdmUgdG8gY2hvb3NlIHNvbWUgdXBwZXIgYm91bmQgdG8gdGhlIHN1bSwgaS5lLiAkVD0yMDAkLg0KDQpgYGB7cn0NCkJHQkJDTFY8LWZ1bmN0aW9uKHBhcmFtcyxtLGQsVCkgew0KcGFyYW1zPC11bm5hbWUocGFyYW1zKQ0KYWw8LXBhcmFtc1sxXQ0KYmU8LXBhcmFtc1syXQ0KZ2E8LXBhcmFtc1szXQ0KZGU8LXBhcmFtc1s0XQ0KREVUPC0xICAgIyBhdCB0aW1lIHplcm8gdGhlcmUgaGFzIHRvIGJlIGEgcHVyY2hhc2UNCmZvciAoaSBpbiAxOlQpIHsNCiAgICBERVQ8LURFVCsoYWwvKGFsK2JlKSkqKGJldGEoZ2EsZGUraSkvYmV0YShnYSxkZSkpKjEvKDErZClee2l9DQp9DQpDTFY9bSpERVQgICMgY29udmVydCBkaXNjb3VudCBleHBlY3RlZCBwdXJjaGFzZXMgaW50byBleHBlY3RlZCB2YWx1ZQ0KcmV0dXJuKENMVikgICAgI3JldHVybiB0aGUgQ0xWDQp9DQpgYGANCg0KYGBge3J9DQpDTFYgPC0gQkdCQkNMVihwYXJhbXMgPSBwYXJhbXMsIG09NTAsZD0uMSxUPTIwMCkNCmBgYA0KDQpDTFYgZm9yIGEgcmFuZG9tIGN1c3RvbWVyIHdpdGggcGFyYW1ldGVycyBhcyBlc2ltYXRlZCwgJG094oKsNTAsIGQ9LjEsIFQ9MjAwJCBpcyDigqxgciByb3VuZChDTFYpYC4NCg0KIyMgUkxWDQoNCkxhc3RseSB3ZSBjYW4gY2FsY3VsYXRlIHRoZSByZXNpZHVhbCBsaWZldGltZSB2YWx1ZSBvZiBhIGRvbm9yIHdpdGggaGlzdG9yeSAkKHgsdF94LG4pJC4gVGhlIHJlc2lkdWFsIGxpZmV0aW1lIHZhbHVlIGlzIHRoZSBwcmVzZW50IHZhbHVlIG9mIHRoZSBleHBlY3RlZCBmdXR1cmUgdHJhbnNhY3Rpb24gc3RyZWFtIHN0YW5kaW5nIGF0IHRpbWUgJHQkLiAkJA0KXGJlZ2lue2FycmF5fXtjY2x9DQpFW1JMVl0gJiA9ICYgXGRpc3BsYXlzdHlsZSBtIFw7IFxsZWZ0ICggUChcdGV4dHJte2FsaXZlIGF0fSBcLCBuKSBcOyBcc3VtX3t0PW4rMX1ee1xpbmZ0eX0gXDsgUChZX3QgPSAxIFxtaWQgXHRleHRybXthbGl2ZSBhdH0gXCwgdCkgXGZyYWN7UChcdGV4dHJte2FsaXZlIGF0fSBcLCB0IFxtaWQgdD5uKX17KDErZClee3Qtbn19IFxyaWdodClcXA0KJiA9ICYgXGRpc3BsYXlzdHlsZSBtIFx0aW1lcyBcdGV4dHJte0RFUlR9DQpcZW5ke2FycmF5fQ0KJCQNCg0KKipERVJUKiogbWVhbnMgRGlzY291bnRlZCBleHBlY3RlZCByZXNpZHVhbCB0cmFuc2FjdGlvbnMuIEhlcmUgaXMgd2hhdCBpdCBsb29rcyBsaWtlIGZvciBvdXIgc2FtcGxlOg0KDQpgYGB7ciBvdXQud2lkdGggPSAnOTAlJywgZmlnLmFsaWduID0gImNlbnRlciJ9DQptIDwtIDUwDQpERVJUIDwtIGJnYmIucmYubWF0cml4LkRFUlQocGFyYW1zLCBkb25hdGlvbnNTdW1tYXJ5JHJmLm1hdHJpeCwgZD0wLjEpDQpSTFYgPC0gbSpERVJUDQpSTFZfbWF0IDwtIGNiaW5kKEFsaXZlX21hdCxERVJULCBSTFYpDQpSTFZfbWF0IDwtIGRhdGEuZnJhbWUoUkxWX21hdCkNCmthYmxlKFJMVl9tYXQsIGZvcm1hdD0icGlwZSIpDQpgYGANCg0KYGBge3Igb3V0LndpZHRoID0gJzkwJScsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQ0KbWF4cm91bmQ9cm91bmQobWF4KFJMViksLTIpDQpSTFZfbWF0JFJMVlsxXSANCg0Kd2l0aChSTFZfbWF0LCBoaXN0KHJlcCh4ID0gUkxWX21hdCRSTFYsIHRpbWVzID0gUkxWX21hdCRjdXN0cyksIHhheHQ9J24nLCB4bGltID0gYygwLG1heHJvdW5kKSwgeGxhYiA9ICJSTFYgKCQpIiwgeWxhYj0iZnJlcXVlbmN5IiwgbWFpbj0iSGlzdG9ncmFtIG9mIFJlc2lkdWFsIExpZmV0aW1lIFZhbHVlIChSTFYpIikpDQpheGlzKHNpZGU9MSwgYXQ9c2VxKDAsbWF4cm91bmQsIDUwKSwgbGFiZWxzPXNlcSgwLG1heHJvdW5kLCA1MCkpDQpgYGANCg0KSWYgd2UgYXNzdW1lICRtPTUwJCBpcyB0aGUgdmFsdWUgb2YgYSBkb25hdGlvbiBhbmQgd2UgdXNlIGEgeWVhcmx5IGRpc2NvdW50IHJhdGUgb2YgJGQ9MC4xJCwgdGhlIFJMViBvZiBhIGN1c3RvbWVyIHdobyBtYWtlcyAiNiBmb3IgNiIgcmVwZWF0IGRvbmF0aW9ucyBpcyDigqxgciByb3VuZChSTFZfbWF0JFJMVlsxXSwyKWAuDQo=