Regression models
Much of our focus to date has been on linear regression models
wherein the predictor variables are continuous. Specifically, for a
model with \(n\) predictors, we have
\[
\mathbf{y} = \mathbf{X} \boldsymbol{\beta} + \mathbf{e} \\
\Downarrow \\
\begin{bmatrix}
y_1 \\ y_2 \\ \vdots \\ y_n
\end{bmatrix}
=
\begin{bmatrix}
1 & x_{1,1} & \cdots & x_{n,1} \\
1 & x_{1,2} & \cdots & x_{n,2} \\
\vdots & \vdots & \ddots & \vdots \\
1 & x_{1,n} & \cdots & x_{n,n}
\end{bmatrix}
\begin{bmatrix}
\beta_0 \\ \beta_1 \\ \vdots \\ \beta_n
\end{bmatrix}
+
\begin{bmatrix}
e_1 \\ e_2 \\ \vdots \\ e_n
\end{bmatrix}
\]
The first column of \(\mathbf{X}\)
contains all 1’s, and is used to estimate the intercept \((\beta_0)\). Columns 2 - \(n\) contain the measured predictor
variables corresponding to each of the observations in \(\mathbf{y}\), from which we estimate their
effects as \(\beta_1\) through \(\beta_n\).
Power functions
We have talked a bit about how to include nonlinear relationships in
linear models. For example, we might have a model like
\[
\log (y_i) = \beta_0 + \beta_1 x_{1, i} + e_i
\]
which we could redefine as
\[
z_i = \beta_0 + \beta_1 x_{1, i} + e_i \\
z_i = \log (y_i)
\]
Another case we discussed was power functions, such as using a
squared term to capture a nonlinear effect of day-of-year \((d)\)
\[
y_i = \beta_0 + \beta_1 d_{i} + \beta_2 d_{i}^2 + e_i
\]
Similar to above, we can just define a new variable and rewrite our
equation as
\[
z_i = \beta_0 + \beta_1 d_{i} + \beta_2 z_{i} + e_i \\
z_i = d_i^2
\]
We have two options for doing this in R. The first
is to explicitly create the predictor \(z\) as above and then include it in the
design matrix. For example,
## some random x
xx <- rnorm(10)
## create x^2
x2 <- xx^2
## create design matrix
## option 1
(XX <- cbind(rep(1, 10), xx, x2))
## xx x2
## [1,] 1 -0.65778998 0.432687658
## [2,] 1 -0.77140201 0.595061054
## [3,] 1 -1.07147312 1.148054656
## [4,] 1 -0.85625543 0.733173368
## [5,] 1 0.60674885 0.368144167
## [6,] 1 -0.17827580 0.031782262
## [7,] 1 -0.27456540 0.075386161
## [8,] 1 0.05122797 0.002624305
## [9,] 1 0.60063832 0.360766388
## [10,] 1 1.27314663 1.620902331
## option 2
(XX <- model.matrix(~ xx + x2))
## (Intercept) xx x2
## 1 1 -0.65778998 0.432687658
## 2 1 -0.77140201 0.595061054
## 3 1 -1.07147312 1.148054656
## 4 1 -0.85625543 0.733173368
## 5 1 0.60674885 0.368144167
## 6 1 -0.17827580 0.031782262
## 7 1 -0.27456540 0.075386161
## 8 1 0.05122797 0.002624305
## 9 1 0.60063832 0.360766388
## 10 1 1.27314663 1.620902331
## attr(,"assign")
## [1] 0 1 2
The second option is to use model.matrix() combined with
the “AsIs” function I(). Because lm() uses
several mathematical operators (eg, + and *)
as formula constructors, I() allows us to use them in their
true sense. So, for a model as above with a quadratic effect, we could
instead use
## create design matrix
(XX <- model.matrix(~ xx + I(xx^2)))
## (Intercept) xx I(xx^2)
## 1 1 -0.65778998 0.432687658
## 2 1 -0.77140201 0.595061054
## 3 1 -1.07147312 1.148054656
## 4 1 -0.85625543 0.733173368
## 5 1 0.60674885 0.368144167
## 6 1 -0.17827580 0.031782262
## 7 1 -0.27456540 0.075386161
## 8 1 0.05122797 0.002624305
## 9 1 0.60063832 0.360766388
## 10 1 1.27314663 1.620902331
## attr(,"assign")
## [1] 0 1 2
Indicators in regression models
We have only briefly discussed options for encoding indicators in
regression models. One of the most common is the case where we want to
estimate some “trend” over time or space. Here we mean trend to include
any systematic change in the response per unit change in the predictor.
Most people think of these as linear trends, but we might also be
interested in nonlinear trends such as quadratic or periodic
signals.
Linear trends over time
Let’s begin with a simple model that treats the response as a
function of an intercept and linear trend over time \((t)\). For example, let’s imagine we want
to estimate the annual increase in \(CO_2\) concentration measured in the
atmosphere above Mauna Loa, Hawai’i from 1971 through 2000. Our model
is
\[
[CO_2]_t = \beta_0 + \beta_1 t + e_t.
\]
Here we require a sequence of numbers to represent time (years),
which could be as simple as the years themselves (eg, 1971, 1972, …,
2000), such that
\[
\begin{bmatrix}
[CO_2]_{1971} \\ [CO_2]_{1972} \\ [CO_2]_{1973} \\ \vdots \\
[CO_2]_{2000}
\end{bmatrix}
=
\begin{bmatrix}
1 & 1971 \\
1 & 1972 \\
1 & 1973 \\
\vdots & \vdots \\
1 & 2000
\end{bmatrix}
\begin{bmatrix}
\beta_0 \\ \beta_1
\end{bmatrix}
+
\begin{bmatrix}
e_{1971} \\ e_{1972} \\ e_{1973} \\ \vdots \\ e_{2000}
\end{bmatrix}.
\]
We could also use a sequence of integers (eg, 1, 2, …, 30), such
that
\[
\begin{bmatrix}
[CO_2]_{1971} \\ [CO_2]_{1972} \\ [CO_2]_{1973} \\ \vdots \\
[CO_2]_{2000}
\end{bmatrix}
=
\begin{bmatrix}
1 & 1 \\
1 & 2 \\
1 & 3 \\
\vdots & \vdots \\
1 & n
\end{bmatrix}
\begin{bmatrix}
\beta_0 \\ \beta_1
\end{bmatrix}
+
\begin{bmatrix}
e_{1971} \\ e_{1972} \\ e_{1973} \\ \vdots \\ e_{2000}
\end{bmatrix}.
\]
Let’s try it for ourselves. These data come from NOAA’s Global Monitoring
Lab and are freely available to anyone. I have selected 30 years and
saved them in the accompanying file mauna_loa_co2.csv.
## get the data
co2 <- read.csv("mauna_loa_co2.csv")
## plot the data
par(mai = c(0.9 ,0.9, 0.6, 0.1))
plot(co2$year, co2$CO2, pch = 16, las = 1,
xlab = "Year", ylab = expression(CO[2]~(ppm)))

Now let’s fit a linear regression model to these data. For the moment
we’ll skip the design matrix and just use lm().
## fit model
co2_fit <- lm(CO2 ~ year, data = co2)
## inspect fit
summary(co2_fit)
##
## Call:
## lm(formula = CO2 ~ year, data = co2)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.1989 -0.4507 -0.2010 0.5733 1.2937
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -2.622e+03 2.790e+01 -94.01 <2e-16 ***
## year 1.496e+00 1.405e-02 106.45 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.6661 on 28 degrees of freedom
## Multiple R-squared: 0.9975, Adjusted R-squared: 0.9974
## F-statistic: 1.133e+04 on 1 and 28 DF, p-value: < 2.2e-16
This analysis indicates that \([CO_2]\) is increasing at ~1.5 ppm per
year. Now let’s fit the model with an integer index for time (year).
## create index from 1:30
index <- seq(length(co2$CO2))
## fit model
co2_fit_2 <- lm(co2$CO2 ~ index)
## inspect fit
summary(co2_fit_2)
##
## Call:
## lm(formula = co2$CO2 ~ index)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.1989 -0.4507 -0.2010 0.5733 1.2937
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 323.89936 0.24943 1298.5 <2e-16 ***
## index 1.49563 0.01405 106.4 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.6661 on 28 degrees of freedom
## Multiple R-squared: 0.9975, Adjusted R-squared: 0.9974
## F-statistic: 1.133e+04 on 1 and 28 DF, p-value: < 2.2e-16
These results are identical to those with year as a predictor.
Periodic trends over time
Ecological studies commonly occur over multiple time periods that
have some intrinsic periodicity to them (eg, tidal cycles over 24 hours,
months in a year). We can also use continuous indicator variables to
model theses regularly occurring, non-linear fluctuations. In
particular, sine and cosine waves make a great choice. As a reminder,
these waves are written as function of amplitude \((A)\), frequency \((f)\), and phase \((\phi)\), such that
\[
y(t) = A \sin (2 \pi f t - \phi) \\
y(t) = A \cos (2 \pi f t - \phi).
\]
For our purposes, the amplitude and phase are less of a concern than
the frequency, which should reflect the periodicity in the interval of
interest (eg, hourly data imply \(f\) =
1/24 and monthly data imply \(f\) =
1/12).
We can create these waves in R and have a look.
## amplitude
A <- 2
## frequency for monthly data
f <- 1/12
## phase
phi <- 0
## time index
t <- seq(240)/10
## sine wave
sine <- A*sin(2*pi*f*t - phi)
## cosine wave
cosine <- A*cos(2*pi*f*t - phi)
## plot them
par(mai = c(0.9 ,0.9, 0.6, 0.1))
plot.ts(sine, las = 1, col = "red", lwd = 2,
ylab = "Sine or Cosine")
lines(cosine, col = "blue", lwd = 2)

As an example, let’s consider the monthly change in temperature in
Lake Washington. We can find these data in the MARSS
package as lakeWAplanktonRaw.
## get the data
data(lakeWAplankton, package = "MARSS")
dat <- as.data.frame(lakeWAplanktonRaw)
tmp <- ts(dat$Temp, start = c(1962,1), frequency = 12)
## impute 1 missing value
ii <- which(is.na(tmp))
tmp[ii] <- (tmp[ii-1] + tmp[ii+1]) / 2
## plot them
par(mai = c(0.9 ,0.9, 0.6, 0.1))
plot.ts(tmp, las = 1, col = "blue",
ylab = "Temperature (C)")

Let’s model these data with a sine wave to capture the seasonal
variation in temperature.
## length of time series
TT <- length(tmp)
## create sine wave
sw <- sin(2*pi*(1/12)*seq(TT))
## create design matrix
XX <- cbind(rep(1, TT), sw)
head(XX)
## sw
## [1,] 1 5.000000e-01
## [2,] 1 8.660254e-01
## [3,] 1 1.000000e+00
## [4,] 1 8.660254e-01
## [5,] 1 5.000000e-01
## [6,] 1 1.224647e-16
## fit model
lwa_fit <- lm(tmp ~ XX - 1)
summary(lwa_fit)
##
## Call:
## lm(formula = tmp ~ XX - 1)
##
## Residuals:
## Min 1Q Median 3Q Max
## -4.3270 -1.5241 -0.1854 1.5236 5.1902
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## XX 11.44923 0.09555 119.82 <2e-16 ***
## XXsw -4.22119 0.13513 -31.24 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.901 on 394 degrees of freedom
## Multiple R-squared: 0.9749, Adjusted R-squared: 0.9748
## F-statistic: 7666 on 2 and 394 DF, p-value: < 2.2e-16
## get fitted values
fts <- ts(fitted(lwa_fit), start = c(1962,1), frequency = 12)
## plot fitted values
par(mai = c(0.9 ,0.9, 0.6, 0.1))
plot.ts(tmp, las = 1, col = "blue",
ylab = "Temperature (C)")
lines(fts, col = "orange", lwd = 2)

Linear and periodic trends
We can also combine periodic and linear trends in the same model.
This is useful in cases where seasonal signals exist and there is a
general increase or decrease as well. For example, let’s now consider
the average monthly \(CO_2\)
concentration at Mauna Loa, which come from the same source.
## get the data
co2m <- read.csv("mauna_loa_co2_mon.csv")
## plot the data
par(mai = c(0.9 ,0.9, 0.6, 0.1))
plot(co2m$dyear, co2m$CO2, pch = 16, cex = 0.6, las = 1,
xlab = "Year", ylab = expression(CO[2]~(ppm)))

## length of time series
TT <- nrow(co2m)
## create time index from 1:TT
index <- seq(TT)
## create sine wave
sw <- sin(2*pi*(1/12)*index)
## create design matrix
XX <- cbind(rep(1, TT), index, sw)
head(XX)
## index sw
## [1,] 1 1 5.000000e-01
## [2,] 1 2 8.660254e-01
## [3,] 1 3 1.000000e+00
## [4,] 1 4 8.660254e-01
## [5,] 1 5 5.000000e-01
## [6,] 1 6 1.224647e-16
## fit model
co2m_fit <- lm(co2m$CO2 ~ XX - 1)
summary(co2m_fit)
##
## Call:
## lm(formula = co2m$CO2 ~ XX - 1)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.6717 -1.2557 -0.1716 1.2636 3.7392
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## XX 3.246e+02 1.594e-01 2035.95 <2e-16 ***
## XXindex 1.246e-01 7.655e-04 162.75 <2e-16 ***
## XXsw 2.297e+00 1.125e-01 20.41 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.509 on 357 degrees of freedom
## Multiple R-squared: 1, Adjusted R-squared: 1
## F-statistic: 6.358e+06 on 3 and 357 DF, p-value: < 2.2e-16
## get fitted values
fts <- fitted(co2m_fit)
## plot fitted values
par(mai = c(0.9 ,0.9, 0.6, 0.1))
plot(co2m$dyear, co2m$CO2, pch = 16, cex = 0.6, las = 1,
xlab = "Year", ylab = expression(CO[2]~(ppm)))
lines(co2m$dyear, fts, col = "orange", lwd = 2)

Analysis of variance (ANOVA) models
ANOVA was popularized by Ronald Fisher
~100 years ago when he was studying the variance of genetic traits among
commercial crops. We use ANOVA models to analyze differences among
group means. Recall our analysis of fish growth as a function of
ration, wherein we initially had three groups of juvenile fish that were
fed different sized rations. Here are the “data”.
set.seed(514)
## sample size
nn <- 30
## groups
pp <- 3
## global intercept
alpha <- 5
## offsets
beta_1 <- c(1,2,3)*5
## slope
beta_2 <- 2
## vector of linear parameters
BETA <- matrix(c(alpha, beta_1, beta_2), ncol = 1)
## global mean
x_avg <- rep(1, nn*pp)
## offsets
grps <- factor(rep(seq(3), ea = nn))
x_int <- model.matrix(~ grps + 0)
## slope
x_cov <- c(runif(nn, 0, 4), runif(nn, 4, 8), runif(nn, 8, 12))
x_cov <- sample(x_cov, nn*pp)
## groups for anova
i1 <- x_cov <= 4
i2 <- x_cov > 4 & x_cov <= 8
i3 <- x_cov > 8
ration <- cbind(i1, i2, i3) * 1
colnames(ration) <- c("_1", "_2", "_3")
## matrix of predictors
xx <- cbind(x_avg, x_int, x_cov)
## Gaussian errors
ee <- rnorm(nn*pp, 0, 2)
## simulated data
yy <- xx %*% BETA + ee
## plot all data
par(mai = c(0.9,0.9,0.1,0.1),
cex = 1.1)
## low
plot(rep(1, nn), yy[i1], pch = 16, col = "red", las = 1,
xlim = c(0.5,3.5), ylim = range(yy),
xaxt = "n",
xlab = "Ration size (g)", ylab = "Growth (mm)")
# points(1, mean(yy[i1]), col = "red", pch = "-", cex = 5)
## med
points(rep(2, nn), yy[i2], pch = 16, col = "blue")
# points(2, mean(yy[i2]), col = "blue", pch = "-", cex = 5)
## high
points(rep(3, nn), yy[i3], pch = 16, col = "orange")
# points(3, mean(yy[i3]), col = "orange", pch = "-", cex = 5)
axis(1, at = seq(3), labels = c("Low (2)", "Med (6)", "High (10)"))

Model 1: Group means
Here we want to know if the mean growth of fish varies among the
three ration sizes, such that
\[
\bar{g}_{\text{ration}_1} \overset{?}{=} \bar{g}_{\text{ration}_2}
\overset{?}{=} \bar{g}_{\text{ration}_3}
\]
How would we write the model for this? Our model for a single
observation \(y_i\) is something
like
\[
y_i = \mu_i + e_i \\
~ \\
\mu_i =
\left\{
\begin{matrix}
\mu_1 ~ \text{if fed ration 1} \\
\mu_2 ~ \text{if fed ration 2} \\
\mu_3 ~ \text{if fed ration 3}
\end{matrix}
\right.
\]
but there is no straightforward way to code this model in terms of a
design matrix. Instead, we can use binary 0/1 coding to represent
if/then constructs along the lines of
\[
y_i = \mu_1 x_{1,i} + \mu_2 x_{2,i} + \mu_3 x_{3,i} + e_i \\
~ \\
x_{1,i} = 1 ~ \text{if fed ration 1 and 0 otherwise} \\
x_{2,i} = 1 ~ \text{if fed ration 2 and 0 otherwise} \\
x_{3,i} = 1 ~ \text{if fed ration 3 and 0 otherwise}
\]
Now we need to specify the design matrix \(\mathbf{X}\) for this model. Let’s rewrite
our model as
\[
y_i = \beta_1 x_{1,i} + \beta_2 x_{2,i} + \beta_3 x_{3,i} + e_i \\
\Downarrow \\
\mathbf{y} = \mathbf{X} \boldsymbol{\beta} + \mathbf{e}
\]
And define \(\mathbf{X}\) as
\[
\mathbf{X} =
\begin{bmatrix}
x_{1,1} & x_{2,1} & x_{3,1} \\
x_{1,2} & x_{2,2} & x_{3,2} \\
\vdots & \vdots & \vdots \\
x_{1,n} & x_{2,n} & x_{3,n}
\end{bmatrix}
\]
Let’s now re-order all of the observations into their three groups,
such that \(j_1 + j_2 + j_3 = n\).
\[
\mathbf{y} =
\begin{bmatrix}
y_{1,1} \\
\vdots \\
y_{1,j_1} \\ \hline
y_{2,1} \\
\vdots \\
y_{2,j_2} \\ \hline
y_{3,1} \\
\vdots \\
y_{3,j_3}
\end{bmatrix}
\]
We can now define \(\mathbf{X}\) and
\(\boldsymbol{\beta}\) as
\[
\mathbf{X} =
\begin{bmatrix}
1 & 0 & 0 \\
\vdots & \vdots & \vdots \\
1 & 0 & 0 \\ \hline
0 & 1 & 0 \\
\vdots & \vdots & \vdots \\
0 & 1 & 0 \\ \hline
0 & 0 & 1 \\
\vdots & \vdots & \vdots \\
0 & 0 & 1
\end{bmatrix}
~~~
\boldsymbol{\beta} =
\begin{bmatrix}
\beta_1 \\
\beta_2 \\
\beta_3
\end{bmatrix}
\]
## we defined the design matrix above; let's inspect it
head(ration)
## _1 _2 _3
## [1,] 1 0 0
## [2,] 1 0 0
## [3,] 0 1 0
## [4,] 0 1 0
## [5,] 0 1 0
## [6,] 0 0 1
Wait–this doesn’t look like the \(\mathbf{X}\) that we defined above! That’s
because I had to use some indexing for the group ID’s to simulate the
full ANCOVA model. Nevertheless, this will work because the observations
in \(\mathbf{y}\) match the same
ordering.
Let’s fit our ANOVA model and examine the results.
## fit ANOVA w/ `- 1` to remove intercept
m1 <- lm(yy ~ ration - 1)
coef(m1)
## ration_1 ration_2 ration_3
## 19.15319 26.66798 36.18701
Now let’s estimate the mean growth rates of our 3 groups of fish to
confirm that we have fit a model of means.
## mean of grp 1
round(mean(yy[i1]), 1)
## [1] 19.2
## mean of grp 2
round(mean(yy[i2]), 1)
## [1] 26.7
## mean of grp 3
round(mean(yy[i3]), 1)
## [1] 36.2
It looks like everything is correct. Let’s now overlay the estimates
of the \(\hat{\beta}_i\) onto our plot
of the data.
## plot all data
par(mai = c(0.9,0.9,0.6,0.1),
omi = c(0, 0, 0, 0.2),
cex = 1.1)
## low
plot(rep(1, nn), yy[i1], pch = 16, col = "red", las = 1,
xlim = c(0.5,3.5), ylim = range(yy),
xaxt = "n",
xlab = "Ration size (g)", ylab = "Growth (mm)")
abline(h = mean(yy[i1]), col = "red", lty = "dashed", cex = 5)
## med
points(rep(2, nn), yy[i2], pch = 16, col = "blue")
abline(h = mean(yy[i2]), col = "blue", lty = "dashed", cex = 5)
## high
points(rep(3, nn), yy[i3], pch = 16, col = "orange")
abline(h = mean(yy[i3]), col = "orange", lty = "dashed", cex = 5)
## labels
text(x = 1.03 * par("usr")[2], y = mean(yy[i1]),
expression(beta[1]), xpd = NA, col = "red")
text(x = 1.03 * par("usr")[2], y = mean(yy[i2]),
expression(beta[2]), xpd = NA, col = "blue")
text(x = 1.03 * par("usr")[2], y = mean(yy[i3]),
expression(beta[3]), xpd = NA, col = "orange")
axis(1, at = seq(3), labels = c("Low (2)", "Med (6)", "High (10)"))

Model 2: Grand mean
Now let’s reframe our model to instead include the effect of ration
relative to the overall “grand mean” for growth rate \((\mu)\)
\[
y_i = \mu + \beta_1 x_{1,i} + \beta_2 x_{2,i} + \beta_3 x_{3,i} + e_i
\]
and calculate the groups means as
\[
\bar{y}_{j=1} = \mu + \beta_1 \\
\bar{y}_{j=2} = \mu + \beta_2 \\
\bar{y}_{j=3} = \mu + \beta_3
\]
We would then define \(\mathbf{X}\)
and \(\boldsymbol{\beta}\) as
\[
\mathbf{X} =
\begin{bmatrix}
1 & 1 & 0 & 0 \\
\vdots & \vdots & \vdots & \vdots \\
1 & 1 & 0 & 0 \\ \hline
1 & 0 & 1 & 0 \\
\vdots & \vdots & \vdots & \vdots \\
1 & 0 & 1 & 0 \\ \hline
1 & 0 & 0 & 1 \\
\vdots & \vdots & \vdots & \vdots \\
1 & 0 & 0 & 1
\end{bmatrix}
~~~
\boldsymbol{\beta} =
\begin{bmatrix}
\mu \\
\beta_1 \\
\beta_2 \\
\beta_3
\end{bmatrix}
\]
And here are the results of our ANOVA model
## design matrix
X <- cbind(rep(1,nn*pp), ration)
## fit ANOVA w/ `- 1` to remove intercept
m2 <- lm(yy ~ X - 1)
coef(m2)
## X X_1 X_2 X_3
## 36.187013 -17.033820 -9.519033 NA
Oops, something went wrong here. Can you spot the problem in our
design matrix?
\[
\mathbf{X} =
\begin{bmatrix}
1 & 1 & 0 & 0 \\
\vdots & \vdots & \vdots & \vdots \\
1 & 1 & 0 & 0 \\ \hline
1 & 0 & 1 & 0 \\
\vdots & \vdots & \vdots & \vdots \\
1 & 0 & 1 & 0 \\ \hline
1 & 0 & 0 & 1 \\
\vdots & \vdots & \vdots & \vdots \\
1 & 0 & 0 & 1
\end{bmatrix}
\]
Let’s try to estimate the parameters in \(\boldsymbol{\beta}\) by hand and see what
went wrong.
## solve for beta by hand
beta <- solve(t(X) %*% X) %*% t(X) %*% yy
Error in solve.default(t(X) %*% X) : system is computationally singular:
reciprocal condition number = 1.4803e-17
It turns out that \(\mathbf{X}\) is
not full rank, in that the first column is a linear combination
of columns 2-4.
\[
\mathbf{X} =
\begin{bmatrix}
1 & 1 & 0 & 0 \\
\vdots & \vdots & \vdots & \vdots \\
1 & 1 & 0 & 0 \\ \hline
1 & 0 & 1 & 0 \\
\vdots & \vdots & \vdots & \vdots \\
1 & 0 & 1 & 0 \\ \hline
1 & 0 & 0 & 1 \\
\vdots & \vdots & \vdots & \vdots \\
1 & 0 & 0 & 1
\end{bmatrix}
\]
OK, let’s think about our model again from the viewpoint of the
overall mean of \(\mathbf{y}\) in terms
of the group means
\[
\bar{y} = \frac{\bar{y}_{j=1} + \bar{y}_{j=2} + \bar{y}_{j=3}}{3} \\
\Downarrow \\
\mu = \frac{(\mu + \beta_1) + (\mu + \beta_2) + (\mu + \beta_3)}{3} \\
\Downarrow \\
3 \mu = (\mu + \beta_1) + (\mu + \beta_2) + (\mu + \beta_3) \\
\Downarrow \\
3 \mu = 3 \mu + (\beta_1 + \beta_2 + \beta_3) \\
\Downarrow \\
\beta_1 + \beta_2 + \beta_3 = 0
\]
This tells us that all of the \(\beta_i\) must sum to 1. Now we can rewrite
our model as
\[
y_i = \mu + \beta_1 x_{1,i} + \beta_2 x_{2,i} + (\text{-} \beta_1 +
\text{-} \beta_2) x_{3,i} + e_i
\]
and calculate the group means as
\[
\begin{aligned}
\bar{y}_{j=1} &= \mu + \beta_1 \\
\bar{y}_{j=2} &= \mu + \beta_2 \\
\bar{y}_{j=3} &= \mu - (\beta_1 + \beta_2)
\end{aligned}
\]
We would then define \(\mathbf{X}\)
and \(\boldsymbol{\beta}\) as
\[
\mathbf{X} =
\begin{bmatrix}
1 & 1 & 0 \\
\vdots & \vdots & \vdots \\
1 & 1 & 0 \\ \hline
1 & 0 & 1 \\
\vdots & \vdots & \vdots \\
1 & 0 & 1 \\ \hline
1 & -1 & -1 \\
\vdots & \vdots & \vdots \\
1 & -1 & -1
\end{bmatrix}
~~~
\boldsymbol{\beta} =
\begin{bmatrix}
\mu \\
\beta_1 \\
\beta_2
\end{bmatrix}
\]
Let’s fit our model and check our estimates.
## empty design matrix
XX <- matrix(NA, nn*pp, pp)
## for mu
XX[i1,] <- matrix(c(1, 1, 0), nn, pp, byrow = TRUE)
## for beta_1
XX[i2,] <- matrix(c(1, 0, 1), nn, pp, byrow = TRUE)
## for beta_2
XX[i3,] <- matrix(c(1, -1, -1), nn, pp, byrow = TRUE)
## fit model & get parameters
Bvec <- coef(lm(yy ~ XX - 1))
names(Bvec) <- c("mu", "beta_1", "beta_2")
Bvec
## mu beta_1 beta_2
## 27.3360624 -8.1828690 -0.6680819
From these \(\hat{\beta}_i\) we can
now estimate the means for each group.
## mean of ration 1
Bvec["mu"] + Bvec["beta_1"]
## mean of ration 2
Bvec["mu"] + Bvec["beta_2"]
## mean of ration 3
Bvec["mu"] - (Bvec["beta_1"] + Bvec["beta_2"])
## mu
## 19.15319
## mu
## 26.66798
## mu
## 36.18701
Removing the mean
We saw in lecture that we could also fit our grand mean model after
some simple algebra, such that
\[
y_i = \mu + \beta_1 x_{1,i} + \beta_2 x_{2,i} + \beta_3 x_{3,i} + e_i \\
\Downarrow \\
y_i - \mu = \beta_1 x_{1,i} + \beta_2 x_{2,i} + \beta_3 x_{3,i} + e_i \\
\Downarrow \\
y_i - \bar{y} = \beta_1 x_{1,i} + \beta_2 x_{2,i} + \beta_3 x_{3,i} +
e_i
\]
Here it is in R
## fit anova with implicit grand mean
m2 <- lm((yy - mean(yy)) ~ ration - 1)
coef(m2)
## ration_1 ration_2 ration_3
## -8.1828690 -0.6680819 8.8509509
and here are the estimates of the group means
## do we recover our means?
coef(m2) + mean(yy)
## ration_1 ration_2 ration_3
## 19.15319 26.66798 36.18701
coef(m1)
## ration_1 ration_2 ration_3
## 19.15319 26.66798 36.18701
Let’s overlay these estimates of the \(\hat{\beta}_i\) onto our plot of the
data
## plot all data
par(mai = c(0.9,0.9,0.6,0.1),
omi = c(0, 0, 0, 0.2),
cex = 1.1)
## plot space
plot(rep(1, nn), yy[i1], type = "n", las = 1,
xlim = c(0.5,3.5), ylim = range(yy),
xaxt = "n",
xlab = "Ration size (g)", ylab = "Growth (mm)")
## grand mean
abline(h = mean(yy), lty = "dashed")
## low
points(rep(1, nn), yy[i1], pch = 16, col = "red")
abline(h = mean(yy[i1]), col = "red", lty = "dashed")
segments(1.2, mean(yy), 1.2, coef(m1)[1], col = "red", lwd = 2)
## med
points(rep(2, nn), yy[i2], pch = 16, col = "blue")
abline(h = mean(yy[i2]), col = "blue", lty = "dashed")
points(x = 2.36, y = mean(yy) + 0.5*coef(m2)[2],
pch = 19, col = "white", cex = 3)
segments(2.2, mean(yy), 2.2, coef(m1)[2], col = "blue", lwd = 2)
## high
points(rep(3, nn), yy[i3], pch = 16, col = "orange")
abline(h = mean(yy[i3]), col = "orange", lty = "dashed")
segments(3.2, mean(yy), 3.2, coef(m1)[3], col = "orange", lwd = 2)
## labels
text(x = 1.15, y = mean(yy) + 0.5*coef(m2)[1], pos = 4,
expression(beta[1]), xpd = NA, col = "red")
text(x = 2.15, y = mean(yy) + 0.5*coef(m2)[2], pos = 4,
expression(beta[2]), xpd = NA, col = "blue")
text(x = 3.15, y = mean(yy) + 0.5*coef(m2)[3], pos = 4,
expression(beta[3]), xpd = NA, col = "orange")
text(x = 1.03 * par("usr")[2], y = mean(yy),
expression(mu), xpd = NA)
axis(1, at = seq(3), labels = c("Low (2)", "Med (6)", "High (10)"))

Analysis of covariance (ANCOVA)
Now let’s turn our attention to ANCOVA model, which combine
categorical and continuous predictors. For example, let’s assume the
data from the fish growth experiment really look like this:
## plot all data
par(mai = c(0.9,0.9,0.1,0.1),
omi = c(0, 0, 0, 0.2),
cex = 1.1)
## plot all data
plot(x_cov[1:nn], yy[1:nn], pch = 16, col = "red", ylim = range(yy),
las = 1, xlab = "Ration size (g)", ylab = "Growth (mm)")
points(x_cov[1:nn+nn], yy[1:nn+nn], pch = 16, col = "blue")
points(x_cov[1:nn+nn*2], yy[1:nn+nn*2], pch = 16, col = "orange")

Here we want to fit a model with the categorical effect of lineage
(designated by the three colors) & the continuous effect of
ration
\[
\text{growth}_i = \alpha + \beta_{1,\text{lineage}} + \beta_2
\text{ration}_i + \epsilon_i
\]
We’ll drop the global intercept \((\alpha)\) & write out the lineage
effects to get
\[
\text{growth}_i = \underbrace{\beta_1 x_{1,i} + \beta_2 x_{2,i} +
\beta_3 x_{3,i}}_{\text{lineage}} + \underbrace{\beta_4
x_{4,i}}_{\text{ration}} + e_i
\]
From this we would then define \(\mathbf{X}\) and \(\boldsymbol{\beta}\) as (where again the
\(j_i\) denote the number of fish in
group \(i\) and \(\sum j_i = n\)).
\[
\mathbf{X} =
\begin{bmatrix}
1 & 0 & 0 & r_1 \\
\vdots & \vdots & \vdots & \vdots \\
1 & 0 & 0 & r_{j_1} \\ \hline
0 & 1 & 0 & r_{j_1+1} \\
\vdots & \vdots & \vdots & \vdots \\
0 & 1 & 0 & r_{j_2+j_2} \\ \hline
0 & 0 & 1 & r_{j_1+j_2+1} \\
\vdots & \vdots & \vdots & \vdots \\
0 & 0 & 1 & r_n
\end{bmatrix}
~~~
\boldsymbol{\beta} =
\begin{bmatrix}
\beta_1 \\
\beta_2 \\
\beta_3 \\
\beta_4
\end{bmatrix}
\]
Now we can build our design matrix.
## create design matrix
XX <- cbind(L1 = rep(c(1,0,0), ea = nn), # effect of lineage 1
L2 = rep(c(0,1,0), ea = nn), # effect of lineage 2
L3 = rep(c(0,0,1), ea = nn), # effect of lineage 3
RA = x_cov) # effect of ration
## fit model
Bvec <- coef(lm(yy ~ XX - 1))
names(Bvec) <- c("beta_1", "beta_2", "beta_3", "beta_4")
Bvec
## beta_1 beta_2 beta_3 beta_4
## 9.936056 14.732702 19.688984 2.076631
and plot the fits with our data.
## plot all data
par(mai = c(0.9,0.9,0.1,0.1),
omi = c(0, 0, 0, 0.2),
cex = 1.1)
## blank plot
plot(x_cov[1:nn], yy[1:nn], type = "n", ylim = range(yy),
las = 1, xlab = "Ration size (g)", ylab = "Growth (mm)")
## add fits
abline(a = Bvec[1], b = Bvec[4], col = "red")
abline(a = Bvec[2], b = Bvec[4], col = "blue")
abline(a = Bvec[3], b = Bvec[4], col = "orange")
## add intercepts
abline(h = Bvec[1], lty = "dashed", col = "red")
abline(h = Bvec[2], lty = "dashed", col = "blue")
abline(h = Bvec[3], lty = "dashed", col = "orange")
## add data
points(x_cov[1:nn], yy[1:nn], pch = 16, col = "red")
points(x_cov[1:nn+nn], yy[1:nn+nn], pch = 16, col = "blue")
points(x_cov[1:nn+nn*2], yy[1:nn+nn*2], pch = 16, col = "orange")
## add labels
text(x = 1.03 * par("usr")[2], y = Bvec[1],
expression(beta[1]), xpd = NA, col = "red")
text(x = 1.03 * par("usr")[2], y = Bvec[2],
expression(beta[2]), xpd = NA, col = "blue")
text(x = 1.03 * par("usr")[2], y = Bvec[3],
expression(beta[3]), xpd = NA, col = "orange")

Using model.matrix()
Here are a number of examples of using factor() with
model.matrix() to create appropriate design matrices for
use in our linear models. Recall that factor(x) tells
R to treat x as a categorical variable
rather than continuous.
## 2 groups with 2 obs each
groups <- factor(c(1, 1, 2, 2))
## inspect them
groups
## [1] 1 1 2 2
## Levels: 1 2
Also recall that model.matrix(~ x) uses a right-hand
side formula construct ~ x
## create design matrix from `groups`
model.matrix(~ groups)
## (Intercept) groups2
## 1 1 0
## 2 1 0
## 3 1 1
## 4 1 1
## attr(,"assign")
## [1] 0 1
## attr(,"contrasts")
## attr(,"contrasts")$groups
## [1] "contr.treatment"
What if we don’t use factor()?
## 2 groups with 2 obs each
groups <- c(1, 1, 2, 2)
## create design matrix from `groups`
model.matrix(~ groups)
## (Intercept) groups
## 1 1 1
## 2 1 1
## 3 1 2
## 4 1 2
## attr(,"assign")
## [1] 0 1
You can drop the intercept term by including - 1 in the
call to model.natrix().
## 2 groups with 2 obs each
groups <- factor(c(1, 1, 2, 2))
## create design matrix from `groups`
model.matrix(~ groups - 1)
## groups1 groups2
## 1 1 0
## 2 1 0
## 3 0 1
## 4 0 1
## attr(,"assign")
## [1] 1 1
## attr(,"contrasts")
## attr(,"contrasts")$groups
## [1] "contr.treatment"
Note that the names or categories are irrelevant for
factor(), in that it will convert characters into
integers.
## 2 groups with 2 obs each
groups <- factor(c("ref", "ref", "exp", "exp"))
## create design matrix from `groups`
model.matrix(~ groups)
## (Intercept) groupsref
## 1 1 1
## 2 1 1
## 3 1 0
## 4 1 0
## attr(,"assign")
## [1] 0 1
## attr(,"contrasts")
## attr(,"contrasts")$groups
## [1] "contr.treatment"
By default, R assigns factors in alphabetical order,
such that the reference category is first in this example:
## 2 groups with 2 obs each
groups <- factor(c("ref", "ref", "exp", "exp"))
## create design matrix from `groups`
model.matrix(~ groups)
## (Intercept) groupsref
## 1 1 1
## 2 1 1
## 3 1 0
## 4 1 0
## attr(,"assign")
## [1] 0 1
## attr(,"contrasts")
## attr(,"contrasts")$groups
## [1] "contr.treatment"
We can change reorder cases with relevel(), which tells
R which group to assign the 0 category
to
## 2 groups with 2 obs each
groups <- relevel(groups, "ref")
## create design matrix from `groups`
model.matrix(~ groups)
## (Intercept) groupsexp
## 1 1 0
## 2 1 0
## 3 1 1
## 4 1 1
## attr(,"assign")
## [1] 0 1
## attr(,"contrasts")
## attr(,"contrasts")$groups
## [1] "contr.treatment"
For ANOVA designs with more that one factor, we can add multiple
factors with + inside the call to
model.matrix().
diet <- factor(c(1, 1, 2, 2))
sex <- factor(c("f", "m", "f", "m"))
model.matrix(~ diet + sex)
## (Intercept) diet2 sexm
## 1 1 0 0
## 2 1 0 1
## 3 1 1 0
## 4 1 1 1
## attr(,"assign")
## [1] 0 1 2
## attr(,"contrasts")
## attr(,"contrasts")$diet
## [1] "contr.treatment"
##
## attr(,"contrasts")$sex
## [1] "contr.treatment"
LS0tCnRpdGxlOiAiQ3JlYXRpbmcgZGVzaWduIG1hdHJpY2VzIgphdXRob3I6ICJNYXJrIFNjaGV1ZXJlbGwiCmRhdGU6ICIyNCBBcHJpbCAyMDI2IgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRoZW1lOiAKICAgICAgYm9vdHN3YXRjaDogam91cm5hbAogICAgICBwcmltYXJ5OiAiIzMyMDA2ZSIKICAgIGhpZ2hsaWdodDogdGV4dG1hdGUKICAgIGNzczogLi4vbGVjdHVyZV9pbnN0LmNzcwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2RlcHRoOiA0Ci0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFKQpgYGAKCiMgQmFja2dyb3VuZAoKV2UgaGF2ZSBzZXZlcmFsIGV4YW1wbGVzIGluIGxlY3R1cmUgYW5kIGxhYiBvZiBob3cgd2UgY2FuIGZvcm1hdCBsaW5lYXIgbW9kZWxzIGluIG1hdHJpeCBmb3JtLiBJbiBwYXJ0aWN1bGFyLCB1bmRlcnN0YW5kaW5nIGhvdyB0byBjcmVhdGUgYW5kIGludGVycHJldCB0aGUgZGVzaWduIG1hdHJpeCAkKFxtYXRoYmZ7WH0kIGlzIGFuIGltcG9ydGFudCBwYXJ0IG9mIGZpdHRpbmcgbW9kZWxzIGFuZCByZWFsaXppbmcgdGhlaXIgbGltaXRhdGlvbnMuIEEgY29tbW9uIG1pc3VuZGVyc3RhbmRpbmcgaXMgdGhhdCB0aGUgY2hvaWNlIG9mIGRlc2lnbiBmb2xsb3dzIGRpcmVjdGx5IGZyb20gYSBkZXNjcmlwdGlvbiBvZiB3aGljaCBzYW1wbGVzIGFyZSB0byBiZSBpbmNsdWRlZCBpbiB0aGUgYW5hbHlzaXMuIEFzIHdlIHNhdyBpbiBsZWN0dXJlLCB0aGlzIGlzIG5vdCB0aGUgY2FzZS4gUmF0aGVyLCB3ZSBzYXcgaG93IGRpZmZlcmVudCBkZXNpZ24gbWF0cmljZXMgY2FuIGJlIHVzZWQgdG8gYWRkcmVzcyB0aGUgc2FtZSBwcm9ibGVtLiBXZSBhbHNvIHNhdyBob3cgdG8gYXNzZW1ibGUgZGVzaWduIG1hdHJpY2VzIGZyb20gdGhlaXIgY29tcG9uZW50IHBpZWNlcywgd2hlcmUgdGhlIGNvbHVtbnMgd2VyZSBkaWZmZXJlbnQgcHJlZGljdG9yIHZhcmlhYmxlcywgYXMgd2VsbCBhcyBob3cgdG8gdXNlIHRoZSBmdW5jdGlvbiBgbW9kZWwubWF0cml4KClgIGFzIGEgc2hvcnRjdXQgZm9yIGRvaW5nIHNvLiBUaGlzIGxhYiBpcyBpbnRlbmRlZCB0byBmYW1pbGlhcml6ZSB5b3Ugd2l0aCB0aGVzZSBjb25jZXB0cyBhbmQgcHJhY3RpY2VzLgoKIyBSZWdyZXNzaW9uIG1vZGVscwoKTXVjaCBvZiBvdXIgZm9jdXMgdG8gZGF0ZSBoYXMgYmVlbiBvbiBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbHMgd2hlcmVpbiB0aGUgcHJlZGljdG9yIHZhcmlhYmxlcyBhcmUgY29udGludW91cy4gU3BlY2lmaWNhbGx5LCBmb3IgYSBtb2RlbCB3aXRoICRuJCBwcmVkaWN0b3JzLCB3ZSBoYXZlCiQkClxtYXRoYmZ7eX0gPSBcbWF0aGJme1h9IFxib2xkc3ltYm9se1xiZXRhfSArIFxtYXRoYmZ7ZX0gXFwKXERvd25hcnJvdyBcXApcYmVnaW57Ym1hdHJpeH0KeV8xIFxcIHlfMiBcXCBcdmRvdHMgXFwgeV9uClxlbmR7Ym1hdHJpeH0KPSAKXGJlZ2lue2JtYXRyaXh9CjEgJiB4X3sxLDF9ICYgXGNkb3RzICYgeF97biwxfSBcXAoxICYgeF97MSwyfSAmIFxjZG90cyAmIHhfe24sMn0gXFwKXHZkb3RzICYgXHZkb3RzICYgXGRkb3RzICYgXHZkb3RzIFxcCjEgJiB4X3sxLG59ICYgXGNkb3RzICYgeF97bixufSAKXGVuZHtibWF0cml4fQpcYmVnaW57Ym1hdHJpeH0KXGJldGFfMCBcXCBcYmV0YV8xIFxcIFx2ZG90cyBcXCBcYmV0YV9uClxlbmR7Ym1hdHJpeH0KKwpcYmVnaW57Ym1hdHJpeH0KZV8xIFxcIGVfMiBcXCBcdmRvdHMgXFwgZV9uClxlbmR7Ym1hdHJpeH0KJCQKClRoZSBmaXJzdCBjb2x1bW4gb2YgJFxtYXRoYmZ7WH0kIGNvbnRhaW5zIGFsbCAxJ3MsIGFuZCBpcyB1c2VkIHRvIGVzdGltYXRlIHRoZSBpbnRlcmNlcHQgJChcYmV0YV8wKSQuIENvbHVtbnMgMiAtICRuJCBjb250YWluIHRoZSBtZWFzdXJlZCBwcmVkaWN0b3IgdmFyaWFibGVzIGNvcnJlc3BvbmRpbmcgdG8gZWFjaCBvZiB0aGUgb2JzZXJ2YXRpb25zIGluICRcbWF0aGJme3l9JCwgZnJvbSB3aGljaCB3ZSBlc3RpbWF0ZSB0aGVpciBlZmZlY3RzIGFzICRcYmV0YV8xJCB0aHJvdWdoICRcYmV0YV9uJC4KCiMjIFBvd2VyIGZ1bmN0aW9ucwoKV2UgaGF2ZSB0YWxrZWQgYSBiaXQgYWJvdXQgaG93IHRvIGluY2x1ZGUgbm9ubGluZWFyIHJlbGF0aW9uc2hpcHMgaW4gbGluZWFyIG1vZGVscy4gRm9yIGV4YW1wbGUsIHdlIG1pZ2h0IGhhdmUgYSBtb2RlbCBsaWtlCgokJApcbG9nICh5X2kpID0gXGJldGFfMCArIFxiZXRhXzEgeF97MSwgaX0gKyBlX2kKJCQKCndoaWNoIHdlIGNvdWxkIHJlZGVmaW5lIGFzCgokJAp6X2kgPSBcYmV0YV8wICsgXGJldGFfMSB4X3sxLCBpfSArIGVfaSBcXAp6X2kgPSBcbG9nICh5X2kpCiQkCgpBbm90aGVyIGNhc2Ugd2UgZGlzY3Vzc2VkIHdhcyBwb3dlciBmdW5jdGlvbnMsIHN1Y2ggYXMgdXNpbmcgYSBzcXVhcmVkIHRlcm0gdG8gY2FwdHVyZSBhIG5vbmxpbmVhciBlZmZlY3Qgb2YgZGF5LW9mLXllYXIgJChkKSQKCiQkCnlfaSA9IFxiZXRhXzAgKyBcYmV0YV8xIGRfe2l9ICsgXGJldGFfMiBkX3tpfV4yICsgZV9pCiQkCgpTaW1pbGFyIHRvIGFib3ZlLCB3ZSBjYW4ganVzdCBkZWZpbmUgYSBuZXcgdmFyaWFibGUgYW5kIHJld3JpdGUgb3VyIGVxdWF0aW9uIGFzCgokJAp6X2kgPSBcYmV0YV8wICsgXGJldGFfMSBkX3tpfSArIFxiZXRhXzIgel97aX0gKyBlX2kgXFwKel9pID0gZF9pXjIKJCQKCldlIGhhdmUgdHdvIG9wdGlvbnMgZm9yIGRvaW5nIHRoaXMgaW4gKipSKiouIFRoZSBmaXJzdCBpcyB0byBleHBsaWNpdGx5IGNyZWF0ZSB0aGUgcHJlZGljdG9yICR6JCBhcyBhYm92ZSBhbmQgdGhlbiBpbmNsdWRlIGl0IGluIHRoZSBkZXNpZ24gbWF0cml4LiBGb3IgZXhhbXBsZSwKCmBgYHtyIHF1YWRyYXRpY190ZXJtX2hhbmR9CiMjIHNvbWUgcmFuZG9tIHgKeHggPC0gcm5vcm0oMTApCiMjIGNyZWF0ZSB4XjIKeDIgPC0geHheMgojIyBjcmVhdGUgZGVzaWduIG1hdHJpeAojIyBvcHRpb24gMQooWFggPC0gY2JpbmQocmVwKDEsIDEwKSwgeHgsIHgyKSkKIyMgb3B0aW9uIDIKKFhYIDwtIG1vZGVsLm1hdHJpeCh+IHh4ICsgeDIpKQpgYGAKClRoZSBzZWNvbmQgb3B0aW9uIGlzIHRvIHVzZSBgbW9kZWwubWF0cml4KClgIGNvbWJpbmVkIHdpdGggdGhlICJBc0lzIiBmdW5jdGlvbiBgSSgpYC4gQmVjYXVzZSBgbG0oKWAgdXNlcyBzZXZlcmFsIG1hdGhlbWF0aWNhbCBvcGVyYXRvcnMgKGVnLCBgK2AgYW5kIGAqYCkgYXMgZm9ybXVsYSBjb25zdHJ1Y3RvcnMsIGBJKClgIGFsbG93cyB1cyB0byB1c2UgdGhlbSBpbiB0aGVpciB0cnVlIHNlbnNlLiBTbywgZm9yIGEgbW9kZWwgYXMgYWJvdmUgd2l0aCBhIHF1YWRyYXRpYyBlZmZlY3QsIHdlIGNvdWxkIGluc3RlYWQgdXNlCgpgYGB7ciBxdWFkcmF0aWNfdGVybV9JfQojIyBjcmVhdGUgZGVzaWduIG1hdHJpeAooWFggPC0gbW9kZWwubWF0cml4KH4geHggKyBJKHh4XjIpKSkKYGBgCgo8YnI+CgojIyBJbmRpY2F0b3JzIGluIHJlZ3Jlc3Npb24gbW9kZWxzCgpXZSBoYXZlIG9ubHkgYnJpZWZseSBkaXNjdXNzZWQgb3B0aW9ucyBmb3IgZW5jb2RpbmcgaW5kaWNhdG9ycyBpbiByZWdyZXNzaW9uIG1vZGVscy4gT25lIG9mIHRoZSBtb3N0IGNvbW1vbiBpcyB0aGUgY2FzZSB3aGVyZSB3ZSB3YW50IHRvIGVzdGltYXRlIHNvbWUgInRyZW5kIiBvdmVyIHRpbWUgb3Igc3BhY2UuIEhlcmUgd2UgbWVhbiB0cmVuZCB0byBpbmNsdWRlIGFueSBzeXN0ZW1hdGljIGNoYW5nZSBpbiB0aGUgcmVzcG9uc2UgcGVyIHVuaXQgY2hhbmdlIGluIHRoZSBwcmVkaWN0b3IuIE1vc3QgcGVvcGxlIHRoaW5rIG9mIHRoZXNlIGFzIGxpbmVhciB0cmVuZHMsIGJ1dCB3ZSBtaWdodCBhbHNvIGJlIGludGVyZXN0ZWQgaW4gbm9ubGluZWFyIHRyZW5kcyBzdWNoIGFzIHF1YWRyYXRpYyBvciBwZXJpb2RpYyBzaWduYWxzLgoKIyMjIExpbmVhciB0cmVuZHMgb3ZlciB0aW1lCgpMZXQncyBiZWdpbiB3aXRoIGEgc2ltcGxlIG1vZGVsIHRoYXQgdHJlYXRzIHRoZSByZXNwb25zZSBhcyBhIGZ1bmN0aW9uIG9mIGFuIGludGVyY2VwdCBhbmQgbGluZWFyIHRyZW5kIG92ZXIgdGltZSAkKHQpJC4gRm9yIGV4YW1wbGUsIGxldCdzIGltYWdpbmUgd2Ugd2FudCB0byBlc3RpbWF0ZSB0aGUgYW5udWFsIGluY3JlYXNlIGluICRDT18yJCBjb25jZW50cmF0aW9uIG1lYXN1cmVkIGluIHRoZSBhdG1vc3BoZXJlIGFib3ZlIE1hdW5hIExvYSwgSGF3YWknaSBmcm9tIDE5NzEgdGhyb3VnaCAyMDAwLiBPdXIgbW9kZWwgaXMKCiQkCltDT18yXV90ID0gXGJldGFfMCArIFxiZXRhXzEgdCArIGVfdC4KJCQKCkhlcmUgd2UgcmVxdWlyZSBhIHNlcXVlbmNlIG9mIG51bWJlcnMgdG8gcmVwcmVzZW50IHRpbWUgKHllYXJzKSwgd2hpY2ggY291bGQgYmUgYXMgc2ltcGxlIGFzIHRoZSB5ZWFycyB0aGVtc2VsdmVzIChlZywgMTk3MSwgMTk3MiwgLi4uLCAyMDAwKSwgc3VjaCB0aGF0IAoKJCQKXGJlZ2lue2JtYXRyaXh9CltDT18yXV97MTk3MX0gXFwgW0NPXzJdX3sxOTcyfSBcXCBbQ09fMl1fezE5NzN9IFxcIFx2ZG90cyBcXCBbQ09fMl1fezIwMDB9ClxlbmR7Ym1hdHJpeH0KPSAKXGJlZ2lue2JtYXRyaXh9CjEgJiAxOTcxICBcXAoxICYgMTk3MiAgXFwKMSAmIDE5NzMgIFxcClx2ZG90cyAmIFx2ZG90cyBcXAoxICYgMjAwMCAgClxlbmR7Ym1hdHJpeH0KXGJlZ2lue2JtYXRyaXh9ClxiZXRhXzAgXFwgXGJldGFfMQpcZW5ke2JtYXRyaXh9CisKXGJlZ2lue2JtYXRyaXh9CmVfezE5NzF9IFxcIGVfezE5NzJ9IFxcIGVfezE5NzN9IFxcIFx2ZG90cyBcXCBlX3syMDAwfQpcZW5ke2JtYXRyaXh9LgokJAoKV2UgY291bGQgYWxzbyB1c2UgYSBzZXF1ZW5jZSBvZiBpbnRlZ2VycyAoZWcsIDEsIDIsIC4uLiwgMzApLCBzdWNoIHRoYXQKCiQkClxiZWdpbntibWF0cml4fQpbQ09fMl1fezE5NzF9IFxcIFtDT18yXV97MTk3Mn0gXFwgW0NPXzJdX3sxOTczfSBcXCBcdmRvdHMgXFwgW0NPXzJdX3syMDAwfQpcZW5ke2JtYXRyaXh9Cj0gClxiZWdpbntibWF0cml4fQoxICYgMSAgXFwKMSAmIDIgIFxcCjEgJiAzICBcXApcdmRvdHMgJiBcdmRvdHMgXFwKMSAmIG4gIApcZW5ke2JtYXRyaXh9ClxiZWdpbntibWF0cml4fQpcYmV0YV8wIFxcIFxiZXRhXzEKXGVuZHtibWF0cml4fQorClxiZWdpbntibWF0cml4fQplX3sxOTcxfSBcXCBlX3sxOTcyfSBcXCBlX3sxOTczfSBcXCBcdmRvdHMgXFwgZV97MjAwMH0KXGVuZHtibWF0cml4fS4KJCQKCkxldCdzIHRyeSBpdCBmb3Igb3Vyc2VsdmVzLiBUaGVzZSBkYXRhIGNvbWUgZnJvbSBOT0FBJ3MgW0dsb2JhbCBNb25pdG9yaW5nIExhYl0oaHR0cHM6Ly93d3cuZXNybC5ub2FhLmdvdi9nbWQvY2NnZy90cmVuZHMvKSBhbmQgYXJlIGZyZWVseSBhdmFpbGFibGUgdG8gYW55b25lLiBJIGhhdmUgc2VsZWN0ZWQgMzAgeWVhcnMgYW5kIHNhdmVkIHRoZW0gaW4gdGhlIGFjY29tcGFueWluZyBmaWxlIGBtYXVuYV9sb2FfY28yLmNzdmAuCgpgYGB7ciBtYXVuYV9sb2FfY28yLCBlY2hvID0gVFJVRSwgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcuaGVpZ2h0PTQuNSwgZmlnLndpZHRoPTV9CiMjIGdldCB0aGUgZGF0YQpjbzIgPC0gcmVhZC5jc3YoIm1hdW5hX2xvYV9jbzIuY3N2IikKIyMgcGxvdCB0aGUgZGF0YQpwYXIobWFpID0gYygwLjkgLDAuOSwgMC42LCAwLjEpKQpwbG90KGNvMiR5ZWFyLCBjbzIkQ08yLCBwY2ggPSAxNiwgbGFzID0gMSwKICAgICB4bGFiID0gIlllYXIiLCB5bGFiID0gZXhwcmVzc2lvbihDT1syXX4ocHBtKSkpCmBgYAoKTm93IGxldCdzIGZpdCBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHRvIHRoZXNlIGRhdGEuIEZvciB0aGUgbW9tZW50IHdlJ2xsIHNraXAgdGhlIGRlc2lnbiBtYXRyaXggYW5kIGp1c3QgdXNlIGBsbSgpYC4KCmBgYHtyIGZpdF9jbzJfeWVhcn0KIyMgZml0IG1vZGVsCmNvMl9maXQgPC0gbG0oQ08yIH4geWVhciwgZGF0YSA9IGNvMikKIyMgaW5zcGVjdCBmaXQKc3VtbWFyeShjbzJfZml0KQpgYGAKClRoaXMgYW5hbHlzaXMgaW5kaWNhdGVzIHRoYXQgJFtDT18yXSQgaXMgaW5jcmVhc2luZyBhdCB+MS41IHBwbSBwZXIgeWVhci4gTm93IGxldCdzIGZpdCB0aGUgbW9kZWwgd2l0aCBhbiBpbnRlZ2VyIGluZGV4IGZvciB0aW1lICh5ZWFyKS4KCmBgYHtyIGZpdF9jbzJfaW5kZXh9CiMjIGNyZWF0ZSBpbmRleCBmcm9tIDE6MzAKaW5kZXggPC0gc2VxKGxlbmd0aChjbzIkQ08yKSkKIyMgZml0IG1vZGVsCmNvMl9maXRfMiA8LSBsbShjbzIkQ08yIH4gaW5kZXgpCiMjIGluc3BlY3QgZml0CnN1bW1hcnkoY28yX2ZpdF8yKQpgYGAKClRoZXNlIHJlc3VsdHMgYXJlIGlkZW50aWNhbCB0byB0aG9zZSB3aXRoIHllYXIgYXMgYSBwcmVkaWN0b3IuCgojIyMgUGVyaW9kaWMgdHJlbmRzIG92ZXIgdGltZQoKRWNvbG9naWNhbCBzdHVkaWVzIGNvbW1vbmx5IG9jY3VyIG92ZXIgbXVsdGlwbGUgdGltZSBwZXJpb2RzIHRoYXQgaGF2ZSBzb21lIGludHJpbnNpYyBwZXJpb2RpY2l0eSB0byB0aGVtIChlZywgdGlkYWwgY3ljbGVzIG92ZXIgMjQgaG91cnMsIG1vbnRocyBpbiBhIHllYXIpLiBXZSBjYW4gYWxzbyB1c2UgY29udGludW91cyBpbmRpY2F0b3IgdmFyaWFibGVzIHRvIG1vZGVsIHRoZXNlcyByZWd1bGFybHkgb2NjdXJyaW5nLCBub24tbGluZWFyIGZsdWN0dWF0aW9ucy4gSW4gcGFydGljdWxhciwgc2luZSBhbmQgY29zaW5lIHdhdmVzIG1ha2UgYSBncmVhdCBjaG9pY2UuIEFzIGEgcmVtaW5kZXIsIHRoZXNlIHdhdmVzIGFyZSB3cml0dGVuIGFzIGZ1bmN0aW9uIG9mIGFtcGxpdHVkZSAkKEEpJCwgZnJlcXVlbmN5ICQoZikkLCBhbmQgcGhhc2UgJChccGhpKSQsIHN1Y2ggdGhhdAoKJCQKeSh0KSA9IEEgXHNpbiAoMiBccGkgZiB0IC0gXHBoaSkgXFwKeSh0KSA9IEEgXGNvcyAoMiBccGkgZiB0IC0gXHBoaSkuCiQkCgpGb3Igb3VyIHB1cnBvc2VzLCB0aGUgYW1wbGl0dWRlIGFuZCBwaGFzZSBhcmUgbGVzcyBvZiBhIGNvbmNlcm4gdGhhbiB0aGUgZnJlcXVlbmN5LCB3aGljaCBzaG91bGQgcmVmbGVjdCB0aGUgcGVyaW9kaWNpdHkgaW4gdGhlIGludGVydmFsIG9mIGludGVyZXN0IChlZywgaG91cmx5IGRhdGEgaW1wbHkgJGYkID0gMS8yNCBhbmQgbW9udGhseSBkYXRhIGltcGx5ICRmJCA9IDEvMTIpLgoKV2UgY2FuIGNyZWF0ZSB0aGVzZSB3YXZlcyBpbiAqKlIqKiBhbmQgaGF2ZSBhIGxvb2suCgpgYGB7ciBwbG90X3NpbmUsIGVjaG8gPSBUUlVFLCBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy5oZWlnaHQ9NC41LCBmaWcud2lkdGg9NX0KIyMgYW1wbGl0dWRlCkEgPC0gMgojIyBmcmVxdWVuY3kgZm9yIG1vbnRobHkgZGF0YQpmIDwtIDEvMTIKIyMgcGhhc2UKcGhpIDwtIDAKIyMgdGltZSBpbmRleAp0IDwtIHNlcSgyNDApLzEwCiMjIHNpbmUgd2F2ZQpzaW5lIDwtIEEqc2luKDIqcGkqZip0IC0gcGhpKQojIyBjb3NpbmUgd2F2ZQpjb3NpbmUgPC0gQSpjb3MoMipwaSpmKnQgLSBwaGkpCiMjIHBsb3QgdGhlbQpwYXIobWFpID0gYygwLjkgLDAuOSwgMC42LCAwLjEpKQpwbG90LnRzKHNpbmUsIGxhcyA9IDEsIGNvbCA9ICJyZWQiLCBsd2QgPSAyLAogICAgICAgIHlsYWIgPSAiU2luZSBvciBDb3NpbmUiKQpsaW5lcyhjb3NpbmUsIGNvbCA9ICJibHVlIiwgbHdkID0gMikKYGBgCgpBcyBhbiBleGFtcGxlLCBsZXQncyBjb25zaWRlciB0aGUgbW9udGhseSBjaGFuZ2UgaW4gdGVtcGVyYXR1cmUgaW4gTGFrZSBXYXNoaW5ndG9uLiBXZSBjYW4gZmluZCB0aGVzZSBkYXRhIGluIHRoZSAqKk1BUlNTKiogcGFja2FnZSBhcyBgbGFrZVdBcGxhbmt0b25SYXdgLgoKYGBge3IgbHdhX3RlbXAsIGVjaG8gPSBUUlVFLCBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy5oZWlnaHQ9NC41LCBmaWcud2lkdGg9Nn0KIyMgZ2V0IHRoZSBkYXRhCmRhdGEobGFrZVdBcGxhbmt0b24sIHBhY2thZ2UgPSAiTUFSU1MiKQpkYXQgPC0gYXMuZGF0YS5mcmFtZShsYWtlV0FwbGFua3RvblJhdykKdG1wIDwtIHRzKGRhdCRUZW1wLCBzdGFydCA9IGMoMTk2MiwxKSwgZnJlcXVlbmN5ID0gMTIpCiMjIGltcHV0ZSAxIG1pc3NpbmcgdmFsdWUKaWkgPC0gd2hpY2goaXMubmEodG1wKSkKdG1wW2lpXSA8LSAodG1wW2lpLTFdICsgdG1wW2lpKzFdKSAvIDIKIyMgcGxvdCB0aGVtCnBhcihtYWkgPSBjKDAuOSAsMC45LCAwLjYsIDAuMSkpCnBsb3QudHModG1wLCBsYXMgPSAxLCBjb2wgPSAiYmx1ZSIsCiAgICAgICAgeWxhYiA9ICJUZW1wZXJhdHVyZSAoQykiKQpgYGAKCkxldCdzIG1vZGVsIHRoZXNlIGRhdGEgd2l0aCBhIHNpbmUgd2F2ZSB0byBjYXB0dXJlIHRoZSBzZWFzb25hbCB2YXJpYXRpb24gaW4gdGVtcGVyYXR1cmUuCgpgYGB7ciBmaXRfbHdhX3RlbXAsIGVjaG8gPSBUUlVFLCBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy5oZWlnaHQ9NC41LCBmaWcud2lkdGg9Nn0KIyMgbGVuZ3RoIG9mIHRpbWUgc2VyaWVzClRUIDwtIGxlbmd0aCh0bXApCiMjIGNyZWF0ZSBzaW5lIHdhdmUKc3cgPC0gc2luKDIqcGkqKDEvMTIpKnNlcShUVCkpCiMjIGNyZWF0ZSBkZXNpZ24gbWF0cml4ClhYIDwtIGNiaW5kKHJlcCgxLCBUVCksIHN3KQpoZWFkKFhYKQojIyBmaXQgbW9kZWwKbHdhX2ZpdCA8LSBsbSh0bXAgfiBYWCAtIDEpCnN1bW1hcnkobHdhX2ZpdCkKIyMgZ2V0IGZpdHRlZCB2YWx1ZXMKZnRzIDwtIHRzKGZpdHRlZChsd2FfZml0KSwgc3RhcnQgPSBjKDE5NjIsMSksIGZyZXF1ZW5jeSA9IDEyKQojIyBwbG90IGZpdHRlZCB2YWx1ZXMKcGFyKG1haSA9IGMoMC45ICwwLjksIDAuNiwgMC4xKSkKcGxvdC50cyh0bXAsIGxhcyA9IDEsIGNvbCA9ICJibHVlIiwKICAgICAgICB5bGFiID0gIlRlbXBlcmF0dXJlIChDKSIpCmxpbmVzKGZ0cywgY29sID0gIm9yYW5nZSIsIGx3ZCA9IDIpCmBgYAoKIyMjIExpbmVhciBhbmQgcGVyaW9kaWMgdHJlbmRzCgpXZSBjYW4gYWxzbyBjb21iaW5lIHBlcmlvZGljIGFuZCBsaW5lYXIgdHJlbmRzIGluIHRoZSBzYW1lIG1vZGVsLiBUaGlzIGlzIHVzZWZ1bCBpbiBjYXNlcyB3aGVyZSBzZWFzb25hbCBzaWduYWxzIGV4aXN0IGFuZCB0aGVyZSBpcyBhIGdlbmVyYWwgaW5jcmVhc2Ugb3IgZGVjcmVhc2UgYXMgd2VsbC4gRm9yIGV4YW1wbGUsIGxldCdzIG5vdyBjb25zaWRlciB0aGUgYXZlcmFnZSBtb250aGx5ICRDT18yJCBjb25jZW50cmF0aW9uIGF0IE1hdW5hIExvYSwgd2hpY2ggY29tZSBmcm9tIHRoZSBzYW1lIHNvdXJjZS4KCmBgYHtyIG1hdW5hX2xvYV9jbzJfbW9uLCBlY2hvID0gVFJVRSwgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcuaGVpZ2h0PTQuNSwgZmlnLndpZHRoPTZ9CiMjIGdldCB0aGUgZGF0YQpjbzJtIDwtIHJlYWQuY3N2KCJtYXVuYV9sb2FfY28yX21vbi5jc3YiKQojIyBwbG90IHRoZSBkYXRhCnBhcihtYWkgPSBjKDAuOSAsMC45LCAwLjYsIDAuMSkpCnBsb3QoY28ybSRkeWVhciwgY28ybSRDTzIsIHBjaCA9IDE2LCBjZXggPSAwLjYsIGxhcyA9IDEsCiAgICAgeGxhYiA9ICJZZWFyIiwgeWxhYiA9IGV4cHJlc3Npb24oQ09bMl1+KHBwbSkpKQpgYGAKCmBgYHtyIGZpdF9jbzJfbW9uLCBlY2hvID0gVFJVRSwgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcuaGVpZ2h0PTQuNSwgZmlnLndpZHRoPTZ9CiMjIGxlbmd0aCBvZiB0aW1lIHNlcmllcwpUVCA8LSBucm93KGNvMm0pCiMjIGNyZWF0ZSB0aW1lIGluZGV4IGZyb20gMTpUVAppbmRleCA8LSBzZXEoVFQpCiMjIGNyZWF0ZSBzaW5lIHdhdmUKc3cgPC0gc2luKDIqcGkqKDEvMTIpKmluZGV4KQojIyBjcmVhdGUgZGVzaWduIG1hdHJpeApYWCA8LSBjYmluZChyZXAoMSwgVFQpLCBpbmRleCwgc3cpCmhlYWQoWFgpCiMjIGZpdCBtb2RlbApjbzJtX2ZpdCA8LSBsbShjbzJtJENPMiB+IFhYIC0gMSkKc3VtbWFyeShjbzJtX2ZpdCkKIyMgZ2V0IGZpdHRlZCB2YWx1ZXMKZnRzIDwtIGZpdHRlZChjbzJtX2ZpdCkKIyMgcGxvdCBmaXR0ZWQgdmFsdWVzCnBhcihtYWkgPSBjKDAuOSAsMC45LCAwLjYsIDAuMSkpCnBsb3QoY28ybSRkeWVhciwgY28ybSRDTzIsIHBjaCA9IDE2LCBjZXggPSAwLjYsIGxhcyA9IDEsCiAgICAgeGxhYiA9ICJZZWFyIiwgeWxhYiA9IGV4cHJlc3Npb24oQ09bMl1+KHBwbSkpKQpsaW5lcyhjbzJtJGR5ZWFyLCBmdHMsIGNvbCA9ICJvcmFuZ2UiLCBsd2QgPSAyKQpgYGAKCgojIEFuYWx5c2lzIG9mIHZhcmlhbmNlIChBTk9WQSkgbW9kZWxzCgpBTk9WQSB3YXMgcG9wdWxhcml6ZWQgYnkgW1JvbmFsZCBGaXNoZXJdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1JvbmFsZF9GaXNoZXIpIH4xMDAgeWVhcnMgYWdvIHdoZW4gaGUgd2FzIHN0dWR5aW5nIHRoZSB2YXJpYW5jZSBvZiBnZW5ldGljIHRyYWl0cyBhbW9uZyBjb21tZXJjaWFsIGNyb3BzLiBXZSB1c2UgQU5PVkEgbW9kZWxzIHRvIGFuYWx5emUgKmRpZmZlcmVuY2VzIGFtb25nIGdyb3VwIG1lYW5zKi4gUmVjYWxsIG91ciBhbmFseXNpcyBvZiBmaXNoIGdyb3d0aCBhcyBhIGZ1bmN0aW9uIG9mIHJhdGlvbiwgd2hlcmVpbiB3ZSBpbml0aWFsbHkgaGFkIHRocmVlIGdyb3VwcyBvZiBqdXZlbmlsZSBmaXNoIHRoYXQgd2VyZSBmZWQgZGlmZmVyZW50IHNpemVkIHJhdGlvbnMuIEhlcmUgYXJlIHRoZSAiZGF0YSIuCgpgYGB7ciBzaW1fZGF0YSwgZWNobyA9IFRSVUUsIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9NX0Kc2V0LnNlZWQoNTE0KQojIyBzYW1wbGUgc2l6ZQpubiA8LSAzMAojIyBncm91cHMKcHAgPC0gMwojIyBnbG9iYWwgaW50ZXJjZXB0CmFscGhhIDwtIDUKIyMgb2Zmc2V0cwpiZXRhXzEgPC0gYygxLDIsMykqNQojIyBzbG9wZQpiZXRhXzIgPC0gMgojIyB2ZWN0b3Igb2YgbGluZWFyIHBhcmFtZXRlcnMKQkVUQSA8LSBtYXRyaXgoYyhhbHBoYSwgYmV0YV8xLCBiZXRhXzIpLCBuY29sID0gMSkKIyMgZ2xvYmFsIG1lYW4KeF9hdmcgPC0gcmVwKDEsIG5uKnBwKQojIyBvZmZzZXRzCmdycHMgPC0gZmFjdG9yKHJlcChzZXEoMyksIGVhID0gbm4pKQp4X2ludCA8LSBtb2RlbC5tYXRyaXgofiBncnBzICsgMCkKIyMgc2xvcGUKeF9jb3YgPC0gYyhydW5pZihubiwgMCwgNCksIHJ1bmlmKG5uLCA0LCA4KSwgcnVuaWYobm4sIDgsIDEyKSkKeF9jb3YgPC0gc2FtcGxlKHhfY292LCBubipwcCkKIyMgZ3JvdXBzIGZvciBhbm92YQppMSA8LSB4X2NvdiA8PSA0CmkyIDwtIHhfY292ID4gNCAmIHhfY292IDw9IDgKaTMgPC0geF9jb3YgPiA4CnJhdGlvbiA8LSBjYmluZChpMSwgaTIsIGkzKSAqIDEKY29sbmFtZXMocmF0aW9uKSA8LSBjKCJfMSIsICJfMiIsICJfMyIpCiMjIG1hdHJpeCBvZiBwcmVkaWN0b3JzCnh4IDwtIGNiaW5kKHhfYXZnLCB4X2ludCwgeF9jb3YpCiMjIEdhdXNzaWFuIGVycm9ycwplZSA8LSBybm9ybShubipwcCwgMCwgMikKIyMgc2ltdWxhdGVkIGRhdGEKeXkgPC0geHggJSolIEJFVEEgKyBlZQojIyBwbG90IGFsbCBkYXRhCnBhcihtYWkgPSBjKDAuOSwwLjksMC4xLDAuMSksCiAgICBjZXggPSAxLjEpCiMjIGxvdwpwbG90KHJlcCgxLCBubiksIHl5W2kxXSwgcGNoID0gMTYsIGNvbCA9ICJyZWQiLCBsYXMgPSAxLAogICAgIHhsaW0gPSBjKDAuNSwzLjUpLCB5bGltID0gcmFuZ2UoeXkpLAogICAgIHhheHQgPSAibiIsCiAgICAgeGxhYiA9ICJSYXRpb24gc2l6ZSAoZykiLCB5bGFiID0gIkdyb3d0aCAobW0pIikKIyBwb2ludHMoMSwgbWVhbih5eVtpMV0pLCBjb2wgPSAicmVkIiwgcGNoID0gIi0iLCBjZXggPSA1KQojIyBtZWQKcG9pbnRzKHJlcCgyLCBubiksIHl5W2kyXSwgcGNoID0gMTYsIGNvbCA9ICJibHVlIikKIyBwb2ludHMoMiwgbWVhbih5eVtpMl0pLCBjb2wgPSAiYmx1ZSIsIHBjaCA9ICItIiwgY2V4ID0gNSkKIyMgaGlnaApwb2ludHMocmVwKDMsIG5uKSwgeXlbaTNdLCBwY2ggPSAxNiwgY29sID0gIm9yYW5nZSIpCiMgcG9pbnRzKDMsIG1lYW4oeXlbaTNdKSwgY29sID0gIm9yYW5nZSIsIHBjaCA9ICItIiwgY2V4ID0gNSkKYXhpcygxLCBhdCA9IHNlcSgzKSwgbGFiZWxzID0gYygiTG93ICgyKSIsICJNZWQgKDYpIiwgIkhpZ2ggKDEwKSIpKQpgYGAKCgojIyBNb2RlbCAxOiBHcm91cCBtZWFucwoKSGVyZSB3ZSB3YW50IHRvIGtub3cgaWYgdGhlIG1lYW4gZ3Jvd3RoIG9mIGZpc2ggdmFyaWVzIGFtb25nIHRoZSB0aHJlZSByYXRpb24gc2l6ZXMsIHN1Y2ggdGhhdAoKJCQKXGJhcntnfV97XHRleHR7cmF0aW9ufV8xfSBcb3ZlcnNldHs/fXs9fSBcYmFye2d9X3tcdGV4dHtyYXRpb259XzJ9IFxvdmVyc2V0ez99ez19IFxiYXJ7Z31fe1x0ZXh0e3JhdGlvbn1fM30KJCQKCkhvdyB3b3VsZCB3ZSB3cml0ZSB0aGUgbW9kZWwgZm9yIHRoaXM/IE91ciBtb2RlbCBmb3IgYSBzaW5nbGUgb2JzZXJ2YXRpb24gJHlfaSQgaXMgc29tZXRoaW5nIGxpa2UKCiQkCnlfaSA9IFxtdV9pICsgZV9pIFxcCn4gXFwKXG11X2kgPSAKXGxlZnRcewpcYmVnaW57bWF0cml4fQpcbXVfMSB+IFx0ZXh0e2lmIGZlZCByYXRpb24gMX0gXFwKXG11XzIgfiBcdGV4dHtpZiBmZWQgcmF0aW9uIDJ9IFxcClxtdV8zIH4gXHRleHR7aWYgZmVkIHJhdGlvbiAzfQpcZW5ke21hdHJpeH0KXHJpZ2h0LgokJAoKYnV0IHRoZXJlIGlzIG5vIHN0cmFpZ2h0Zm9yd2FyZCB3YXkgdG8gY29kZSB0aGlzIG1vZGVsIGluIHRlcm1zIG9mIGEgZGVzaWduIG1hdHJpeC4gSW5zdGVhZCwgd2UgY2FuIHVzZSBiaW5hcnkgMC8xIGNvZGluZyB0byByZXByZXNlbnQgaWYvdGhlbiBjb25zdHJ1Y3RzIGFsb25nIHRoZSBsaW5lcyBvZgoKJCQKeV9pID0gXG11XzEgeF97MSxpfSArIFxtdV8yIHhfezIsaX0gKyBcbXVfMyB4X3szLGl9ICsgZV9pIFxcCn4gXFwKeF97MSxpfSA9IDEgfiBcdGV4dHtpZiBmZWQgcmF0aW9uIDEgYW5kIDAgb3RoZXJ3aXNlfSBcXAp4X3syLGl9ID0gMSB+IFx0ZXh0e2lmIGZlZCByYXRpb24gMiBhbmQgMCBvdGhlcndpc2V9IFxcCnhfezMsaX0gPSAxIH4gXHRleHR7aWYgZmVkIHJhdGlvbiAzIGFuZCAwIG90aGVyd2lzZX0KJCQKCk5vdyB3ZSBuZWVkIHRvIHNwZWNpZnkgdGhlIGRlc2lnbiBtYXRyaXggJFxtYXRoYmZ7WH0kIGZvciB0aGlzIG1vZGVsLiBMZXQncyByZXdyaXRlIG91ciBtb2RlbCBhcwoKJCQKeV9pID0gXGJldGFfMSB4X3sxLGl9ICsgXGJldGFfMiB4X3syLGl9ICsgXGJldGFfMyB4X3szLGl9ICsgZV9pIFxcClxEb3duYXJyb3cgXFwKXG1hdGhiZnt5fSA9IFxtYXRoYmZ7WH0gXGJvbGRzeW1ib2x7XGJldGF9ICsgXG1hdGhiZntlfQokJAoKQW5kIGRlZmluZSAkXG1hdGhiZntYfSQgYXMKCiQkClxtYXRoYmZ7WH0gPSAKXGJlZ2lue2JtYXRyaXh9CnhfezEsMX0gJiB4X3syLDF9ICYgeF97MywxfSBcXAp4X3sxLDJ9ICYgeF97MiwyfSAmIHhfezMsMn0gXFwKXHZkb3RzICYgXHZkb3RzICYgXHZkb3RzIFxcCnhfezEsbn0gJiB4X3syLG59ICYgeF97MyxufSAKXGVuZHtibWF0cml4fQokJAoKTGV0J3Mgbm93IHJlLW9yZGVyIGFsbCBvZiB0aGUgb2JzZXJ2YXRpb25zIGludG8gdGhlaXIgdGhyZWUgZ3JvdXBzLCBzdWNoIHRoYXQgJGpfMSArIGpfMiArIGpfMyA9IG4kLgoKJCQKXG1hdGhiZnt5fSA9IApcYmVnaW57Ym1hdHJpeH0KeV97MSwxfSBcXApcdmRvdHMgIFxcCnlfezEsal8xfSBcXCBcaGxpbmUKeV97MiwxfSBcXApcdmRvdHMgIFxcCnlfezIsal8yfSBcXCBcaGxpbmUKeV97MywxfSBcXApcdmRvdHMgIFxcCnlfezMsal8zfSAKXGVuZHtibWF0cml4fQokJAoKV2UgY2FuIG5vdyBkZWZpbmUgJFxtYXRoYmZ7WH0kIGFuZCAkXGJvbGRzeW1ib2x7XGJldGF9JCBhcwoKJCQKXG1hdGhiZntYfSA9IApcYmVnaW57Ym1hdHJpeH0KMSAmIDAgJiAwIFxcClx2ZG90cyAmIFx2ZG90cyAmIFx2ZG90cyBcXAoxICYgMCAmIDAgXFwgXGhsaW5lCjAgJiAxICYgMCBcXApcdmRvdHMgJiBcdmRvdHMgJiBcdmRvdHMgXFwKMCAmIDEgJiAwIFxcIFxobGluZQowICYgMCAmIDEgXFwKXHZkb3RzICYgXHZkb3RzICYgXHZkb3RzIFxcCjAgJiAwICYgMSAKXGVuZHtibWF0cml4fQp+fn4KXGJvbGRzeW1ib2x7XGJldGF9ID0KXGJlZ2lue2JtYXRyaXh9ClxiZXRhXzEgXFwKXGJldGFfMiBcXApcYmV0YV8zClxlbmR7Ym1hdHJpeH0KJCQKCmBgYHtyIGluc3BlY3RfZGVzaWduLCBlY2hvID0gVFJVRX0KIyMgd2UgZGVmaW5lZCB0aGUgZGVzaWduIG1hdHJpeCBhYm92ZTsgbGV0J3MgaW5zcGVjdCBpdApoZWFkKHJhdGlvbikKYGBgCgpXYWl0LS10aGlzIGRvZXNuJ3QgbG9vayBsaWtlIHRoZSAkXG1hdGhiZntYfSQgdGhhdCB3ZSBkZWZpbmVkIGFib3ZlISBUaGF0J3MgYmVjYXVzZSBJIGhhZCB0byB1c2Ugc29tZSBpbmRleGluZyBmb3IgdGhlIGdyb3VwIElEJ3MgdG8gc2ltdWxhdGUgdGhlIGZ1bGwgQU5DT1ZBIG1vZGVsLiBOZXZlcnRoZWxlc3MsIHRoaXMgd2lsbCB3b3JrIGJlY2F1c2UgdGhlIG9ic2VydmF0aW9ucyBpbiAkXG1hdGhiZnt5fSQgbWF0Y2ggdGhlIHNhbWUgb3JkZXJpbmcuCgpMZXQncyBmaXQgb3VyIEFOT1ZBIG1vZGVsIGFuZCBleGFtaW5lIHRoZSByZXN1bHRzLgoKYGBge3IgZml0X2Fub3ZhLCBlY2hvID0gVFJVRX0KIyMgZml0IEFOT1ZBIHcvIGAtIDFgIHRvIHJlbW92ZSBpbnRlcmNlcHQKbTEgPC0gbG0oeXkgfiByYXRpb24gLSAxKQpjb2VmKG0xKQpgYGAKCk5vdyBsZXQncyBlc3RpbWF0ZSB0aGUgbWVhbiBncm93dGggcmF0ZXMgb2Ygb3VyIDMgZ3JvdXBzIG9mIGZpc2ggdG8gY29uZmlybSB0aGF0IHdlIGhhdmUgZml0IGEgbW9kZWwgb2YgbWVhbnMuCgpgYGB7ciBjaGVja19tZWFucywgZWNobyA9IFRSVUV9CiMjIG1lYW4gb2YgZ3JwIDEKcm91bmQobWVhbih5eVtpMV0pLCAxKQojIyBtZWFuIG9mIGdycCAyCnJvdW5kKG1lYW4oeXlbaTJdKSwgMSkKIyMgbWVhbiBvZiBncnAgMwpyb3VuZChtZWFuKHl5W2kzXSksIDEpCmBgYAoKSXQgbG9va3MgbGlrZSBldmVyeXRoaW5nIGlzIGNvcnJlY3QuIExldCdzIG5vdyBvdmVybGF5IHRoZSBlc3RpbWF0ZXMgb2YgdGhlICRcaGF0e1xiZXRhfV9pJCBvbnRvIG91ciBwbG90IG9mIHRoZSBkYXRhLgoKYGBge3IgcGxvdF9tZWFuc19vbmx5LCBlY2hvID0gVFJVRSwgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcuaGVpZ2h0PTQuNSwgZmlnLndpZHRoPTV9CiMjIHBsb3QgYWxsIGRhdGEKcGFyKG1haSA9IGMoMC45LDAuOSwwLjYsMC4xKSwKICAgIG9taSA9IGMoMCwgMCwgMCwgMC4yKSwKICAgIGNleCA9IDEuMSkKIyMgbG93CnBsb3QocmVwKDEsIG5uKSwgeXlbaTFdLCBwY2ggPSAxNiwgY29sID0gInJlZCIsIGxhcyA9IDEsCiAgICAgeGxpbSA9IGMoMC41LDMuNSksIHlsaW0gPSByYW5nZSh5eSksCiAgICAgeGF4dCA9ICJuIiwKICAgICB4bGFiID0gIlJhdGlvbiBzaXplIChnKSIsIHlsYWIgPSAiR3Jvd3RoIChtbSkiKQphYmxpbmUoaCA9IG1lYW4oeXlbaTFdKSwgY29sID0gInJlZCIsIGx0eSA9ICJkYXNoZWQiLCBjZXggPSA1KQojIyBtZWQKcG9pbnRzKHJlcCgyLCBubiksIHl5W2kyXSwgcGNoID0gMTYsIGNvbCA9ICJibHVlIikKYWJsaW5lKGggPSBtZWFuKHl5W2kyXSksIGNvbCA9ICJibHVlIiwgbHR5ID0gImRhc2hlZCIsIGNleCA9IDUpCiMjIGhpZ2gKcG9pbnRzKHJlcCgzLCBubiksIHl5W2kzXSwgcGNoID0gMTYsIGNvbCA9ICJvcmFuZ2UiKQphYmxpbmUoaCA9IG1lYW4oeXlbaTNdKSwgY29sID0gIm9yYW5nZSIsIGx0eSA9ICJkYXNoZWQiLCBjZXggPSA1KQojIyBsYWJlbHMKdGV4dCh4ID0gMS4wMyAqIHBhcigidXNyIilbMl0sIHkgPSBtZWFuKHl5W2kxXSksCiAgICAgZXhwcmVzc2lvbihiZXRhWzFdKSwgeHBkID0gTkEsIGNvbCA9ICJyZWQiKQp0ZXh0KHggPSAxLjAzICogcGFyKCJ1c3IiKVsyXSwgeSA9IG1lYW4oeXlbaTJdKSwKICAgICBleHByZXNzaW9uKGJldGFbMl0pLCB4cGQgPSBOQSwgY29sID0gImJsdWUiKQp0ZXh0KHggPSAxLjAzICogcGFyKCJ1c3IiKVsyXSwgeSA9IG1lYW4oeXlbaTNdKSwKICAgICBleHByZXNzaW9uKGJldGFbM10pLCB4cGQgPSBOQSwgY29sID0gIm9yYW5nZSIpCmF4aXMoMSwgYXQgPSBzZXEoMyksIGxhYmVscyA9IGMoIkxvdyAoMikiLCAiTWVkICg2KSIsICJIaWdoICgxMCkiKSkKYGBgCgoKIyMgTW9kZWwgMjogR3JhbmQgbWVhbgoKTm93IGxldCdzIHJlZnJhbWUgb3VyIG1vZGVsIHRvIGluc3RlYWQgaW5jbHVkZSB0aGUgZWZmZWN0IG9mIHJhdGlvbiByZWxhdGl2ZSB0byB0aGUgb3ZlcmFsbCAiZ3JhbmQgbWVhbiIgZm9yIGdyb3d0aCByYXRlICQoXG11KSQKCiQkCnlfaSA9IFxtdSArIFxiZXRhXzEgeF97MSxpfSArIFxiZXRhXzIgeF97MixpfSArIFxiZXRhXzMgeF97MyxpfSArIGVfaQokJAoKYW5kIGNhbGN1bGF0ZSB0aGUgZ3JvdXBzIG1lYW5zIGFzCgokJApcYmFye3l9X3tqPTF9ID0gXG11ICsgXGJldGFfMSBcXApcYmFye3l9X3tqPTJ9ID0gXG11ICsgXGJldGFfMiBcXApcYmFye3l9X3tqPTN9ID0gXG11ICsgXGJldGFfMwokJAoKV2Ugd291bGQgdGhlbiBkZWZpbmUgJFxtYXRoYmZ7WH0kIGFuZCAkXGJvbGRzeW1ib2x7XGJldGF9JCBhcwoKJCQKXG1hdGhiZntYfSA9IApcYmVnaW57Ym1hdHJpeH0KMSAmIDEgJiAwICYgMCBcXApcdmRvdHMgJiBcdmRvdHMgJiBcdmRvdHMgJiBcdmRvdHMgXFwKMSAmIDEgJiAwICYgMCBcXCBcaGxpbmUKMSAmIDAgJiAxICYgMCBcXApcdmRvdHMgJiBcdmRvdHMgJiBcdmRvdHMgJiBcdmRvdHMgXFwKMSAmIDAgJiAxICYgMCBcXCBcaGxpbmUKMSAmIDAgJiAwICYgMSBcXApcdmRvdHMgJiBcdmRvdHMgJiBcdmRvdHMgJiBcdmRvdHMgXFwKMSAmIDAgJiAwICYgMSAKXGVuZHtibWF0cml4fQp+fn4KXGJvbGRzeW1ib2x7XGJldGF9ID0KXGJlZ2lue2JtYXRyaXh9ClxtdSBcXApcYmV0YV8xIFxcClxiZXRhXzIgXFwKXGJldGFfMwpcZW5ke2JtYXRyaXh9CiQkCgpBbmQgaGVyZSBhcmUgdGhlIHJlc3VsdHMgb2Ygb3VyIEFOT1ZBIG1vZGVsCgpgYGB7ciBmaXRfYW5vdmFfZ20sIGVjaG8gPSBUUlVFfQojIyBkZXNpZ24gbWF0cml4ClggPC0gY2JpbmQocmVwKDEsbm4qcHApLCByYXRpb24pCiMjIGZpdCBBTk9WQSB3LyBgLSAxYCB0byByZW1vdmUgaW50ZXJjZXB0Cm0yIDwtIGxtKHl5IH4gWCAtIDEpCmNvZWYobTIpCmBgYAoKT29wcywgc29tZXRoaW5nIHdlbnQgd3JvbmcgaGVyZS4gQ2FuIHlvdSBzcG90IHRoZSBwcm9ibGVtIGluIG91ciBkZXNpZ24gbWF0cml4PwoKJCQKXG1hdGhiZntYfSA9IApcYmVnaW57Ym1hdHJpeH0KMSAmIDEgJiAwICYgMCBcXApcdmRvdHMgJiBcdmRvdHMgJiBcdmRvdHMgJiBcdmRvdHMgXFwKMSAmIDEgJiAwICYgMCBcXCBcaGxpbmUKMSAmIDAgJiAxICYgMCBcXApcdmRvdHMgJiBcdmRvdHMgJiBcdmRvdHMgJiBcdmRvdHMgXFwKMSAmIDAgJiAxICYgMCBcXCBcaGxpbmUKMSAmIDAgJiAwICYgMSBcXApcdmRvdHMgJiBcdmRvdHMgJiBcdmRvdHMgJiBcdmRvdHMgXFwKMSAmIDAgJiAwICYgMSAKXGVuZHtibWF0cml4fQokJAoKTGV0J3MgdHJ5IHRvIGVzdGltYXRlIHRoZSBwYXJhbWV0ZXJzIGluICRcYm9sZHN5bWJvbHtcYmV0YX0kIGJ5IGhhbmQgYW5kIHNlZSB3aGF0IHdlbnQgd3JvbmcuCgpgYGB7ciBzaW5ndWxhcl9YLCBlY2hvID0gVFJVRSwgZXZhbCA9IEZBTFNFfQojIyBzb2x2ZSBmb3IgYmV0YSBieSBoYW5kCmJldGEgPC0gc29sdmUodChYKSAlKiUgWCkgJSolIHQoWCkgJSolIHl5CmBgYAoKYGBgCkVycm9yIGluIHNvbHZlLmRlZmF1bHQodChYKSAlKiUgWCkgOiBzeXN0ZW0gaXMgY29tcHV0YXRpb25hbGx5IHNpbmd1bGFyOgogcmVjaXByb2NhbCBjb25kaXRpb24gbnVtYmVyID0gMS40ODAzZS0xNwpgYGAKCkl0IHR1cm5zIG91dCB0aGF0ICRcbWF0aGJme1h9JCBpcyBub3QgKmZ1bGwgcmFuayosIGluIHRoYXQgdGhlIGZpcnN0IGNvbHVtbiBpcyBhIGxpbmVhciBjb21iaW5hdGlvbiBvZiBjb2x1bW5zIDItNC4KCiQkClxtYXRoYmZ7WH0gPSAKXGJlZ2lue2JtYXRyaXh9CjEgJiAxICYgMCAmIDAgXFwKXHZkb3RzICYgXHZkb3RzICYgXHZkb3RzICYgXHZkb3RzIFxcCjEgJiAxICYgMCAmIDAgXFwgXGhsaW5lCjEgJiAwICYgMSAmIDAgXFwKXHZkb3RzICYgXHZkb3RzICYgXHZkb3RzICYgXHZkb3RzIFxcCjEgJiAwICYgMSAmIDAgXFwgXGhsaW5lCjEgJiAwICYgMCAmIDEgXFwKXHZkb3RzICYgXHZkb3RzICYgXHZkb3RzICYgXHZkb3RzIFxcCjEgJiAwICYgMCAmIDEgClxlbmR7Ym1hdHJpeH0KJCQKCk9LLCBsZXQncyB0aGluayBhYm91dCBvdXIgbW9kZWwgYWdhaW4gZnJvbSB0aGUgdmlld3BvaW50IG9mIHRoZSBvdmVyYWxsIG1lYW4gb2YgJFxtYXRoYmZ7eX0kIGluIHRlcm1zIG9mIHRoZSBncm91cCBtZWFucwoKJCQKXGJhcnt5fSA9IFxmcmFje1xiYXJ7eX1fe2o9MX0gKyBcYmFye3l9X3tqPTJ9ICsgXGJhcnt5fV97aj0zfX17M30gXFwKXERvd25hcnJvdyBcXApcbXUgPSBcZnJhY3soXG11ICsgXGJldGFfMSkgKyAoXG11ICsgXGJldGFfMikgKyAoXG11ICsgXGJldGFfMyl9ezN9IFxcClxEb3duYXJyb3cgXFwKMyBcbXUgPSAoXG11ICsgXGJldGFfMSkgKyAoXG11ICsgXGJldGFfMikgKyAoXG11ICsgXGJldGFfMykgXFwKXERvd25hcnJvdyBcXAozIFxtdSA9IDMgXG11ICsgKFxiZXRhXzEgKyBcYmV0YV8yICsgXGJldGFfMykgXFwKXERvd25hcnJvdyBcXApcYmV0YV8xICsgXGJldGFfMiArIFxiZXRhXzMgPSAwCiQkCgpUaGlzIHRlbGxzIHVzIHRoYXQgYWxsIG9mIHRoZSAkXGJldGFfaSQgbXVzdCBzdW0gdG8gMS4gTm93IHdlIGNhbiByZXdyaXRlIG91ciBtb2RlbCBhcwoKJCQKeV9pID0gXG11ICsgXGJldGFfMSB4X3sxLGl9ICsgXGJldGFfMiB4X3syLGl9ICsgKFx0ZXh0ey19IFxiZXRhXzEgKyBcdGV4dHstfSBcYmV0YV8yKSB4X3szLGl9ICsgZV9pCiQkCgphbmQgY2FsY3VsYXRlIHRoZSBncm91cCBtZWFucyBhcwoKJCQKXGJlZ2lue2FsaWduZWR9ClxiYXJ7eX1fe2o9MX0gJj0gXG11ICsgXGJldGFfMSBcXApcYmFye3l9X3tqPTJ9ICY9IFxtdSArIFxiZXRhXzIgXFwKXGJhcnt5fV97aj0zfSAmPSBcbXUgLSAoXGJldGFfMSArIFxiZXRhXzIpClxlbmR7YWxpZ25lZH0KJCQKCldlIHdvdWxkIHRoZW4gZGVmaW5lICRcbWF0aGJme1h9JCBhbmQgJFxib2xkc3ltYm9se1xiZXRhfSQgYXMKCiQkClxtYXRoYmZ7WH0gPSAKXGJlZ2lue2JtYXRyaXh9CjEgJiAxICYgMCBcXApcdmRvdHMgJiBcdmRvdHMgJiBcdmRvdHMgXFwKMSAmIDEgJiAwIFxcIFxobGluZQoxICYgMCAmIDEgXFwKXHZkb3RzICYgXHZkb3RzICYgXHZkb3RzIFxcCjEgJiAwICYgMSBcXCBcaGxpbmUKMSAmIC0xICYgLTEgXFwKXHZkb3RzICYgXHZkb3RzICYgXHZkb3RzIFxcCjEgJiAtMSAmIC0xIApcZW5ke2JtYXRyaXh9Cn5+fgpcYm9sZHN5bWJvbHtcYmV0YX0gPQpcYmVnaW57Ym1hdHJpeH0KXG11IFxcClxiZXRhXzEgXFwKXGJldGFfMgpcZW5ke2JtYXRyaXh9CiQkCgpMZXQncyBmaXQgb3VyIG1vZGVsIGFuZCBjaGVjayBvdXIgZXN0aW1hdGVzLgoKYGBge3IgZml0X2dsb2JhbF9tZWFuLCBlY2hvID0gVFJVRX0KIyMgZW1wdHkgZGVzaWduIG1hdHJpeApYWCA8LSBtYXRyaXgoTkEsIG5uKnBwLCBwcCkKIyMgZm9yIG11ClhYW2kxLF0gPC0gbWF0cml4KGMoMSwgIDEsICAwKSwgbm4sIHBwLCBieXJvdyA9IFRSVUUpCiMjIGZvciBiZXRhXzEKWFhbaTIsXSA8LSBtYXRyaXgoYygxLCAgMCwgIDEpLCBubiwgcHAsIGJ5cm93ID0gVFJVRSkKIyMgZm9yIGJldGFfMgpYWFtpMyxdIDwtIG1hdHJpeChjKDEsIC0xLCAtMSksIG5uLCBwcCwgYnlyb3cgPSBUUlVFKQojIyBmaXQgbW9kZWwgJiBnZXQgcGFyYW1ldGVycwpCdmVjIDwtIGNvZWYobG0oeXkgfiBYWCAtIDEpKQpuYW1lcyhCdmVjKSA8LSBjKCJtdSIsICJiZXRhXzEiLCAiYmV0YV8yIikKQnZlYwpgYGAKCkZyb20gdGhlc2UgJFxoYXR7XGJldGF9X2kkIHdlIGNhbiBub3cgZXN0aW1hdGUgdGhlIG1lYW5zIGZvciBlYWNoIGdyb3VwLgoKYGBge3IgZ2xvYmFsX21lYW5fZXN0LCBlY2hvID0gVFJVRSwgcmVzdWx0cz0naG9sZCd9CiMjIG1lYW4gb2YgcmF0aW9uIDEKQnZlY1sibXUiXSArIEJ2ZWNbImJldGFfMSJdCiMjIG1lYW4gb2YgcmF0aW9uIDIKQnZlY1sibXUiXSArIEJ2ZWNbImJldGFfMiJdCiMjIG1lYW4gb2YgcmF0aW9uIDMKQnZlY1sibXUiXSAtIChCdmVjWyJiZXRhXzEiXSArIEJ2ZWNbImJldGFfMiJdKQpgYGAKCiMjIyBSZW1vdmluZyB0aGUgbWVhbgoKV2Ugc2F3IGluIGxlY3R1cmUgdGhhdCB3ZSBjb3VsZCBhbHNvIGZpdCBvdXIgZ3JhbmQgbWVhbiBtb2RlbCBhZnRlciBzb21lIHNpbXBsZSBhbGdlYnJhLCBzdWNoIHRoYXQKCiQkCnlfaSA9IFxtdSArIFxiZXRhXzEgeF97MSxpfSArIFxiZXRhXzIgeF97MixpfSArIFxiZXRhXzMgeF97MyxpfSArIGVfaSBcXApcRG93bmFycm93IFxcCnlfaSAtIFxtdSA9IFxiZXRhXzEgeF97MSxpfSArIFxiZXRhXzIgeF97MixpfSArIFxiZXRhXzMgeF97MyxpfSArIGVfaSBcXApcRG93bmFycm93IFxcCnlfaSAtIFxiYXJ7eX0gPSBcYmV0YV8xIHhfezEsaX0gKyBcYmV0YV8yIHhfezIsaX0gKyBcYmV0YV8zIHhfezMsaX0gKyBlX2kKJCQKCkhlcmUgaXQgaXMgaW4gKipSKioKCmBgYHtyIGdyYW5kX21lYW5fYW5vdmEsIGVjaG8gPSBUUlVFfQojIyBmaXQgYW5vdmEgd2l0aCBpbXBsaWNpdCBncmFuZCBtZWFuCm0yIDwtIGxtKCh5eSAtIG1lYW4oeXkpKSB+IHJhdGlvbiAtIDEpCmNvZWYobTIpCmBgYAoKYW5kIGhlcmUgYXJlIHRoZSBlc3RpbWF0ZXMgb2YgdGhlIGdyb3VwIG1lYW5zCgpgYGB7ciBzb2x2ZV9ncmFuZF9tZWFuLCBlY2hvID0gVFJVRX0KIyMgZG8gd2UgcmVjb3ZlciBvdXIgbWVhbnM/CmNvZWYobTIpICsgbWVhbih5eSkKY29lZihtMSkKYGBgCgpMZXQncyBvdmVybGF5IHRoZXNlIGVzdGltYXRlcyBvZiB0aGUgJFxoYXR7XGJldGF9X2kkIG9udG8gb3VyIHBsb3Qgb2YgdGhlIGRhdGEKCmBgYHtyIHBsb3RfYW5vdmFfZ20sIGVjaG8gPSBUUlVFLCBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy5oZWlnaHQ9NC41LCBmaWcud2lkdGg9NX0KIyMgcGxvdCBhbGwgZGF0YQpwYXIobWFpID0gYygwLjksMC45LDAuNiwwLjEpLAogICAgb21pID0gYygwLCAwLCAwLCAwLjIpLAogICAgY2V4ID0gMS4xKQojIyBwbG90IHNwYWNlCnBsb3QocmVwKDEsIG5uKSwgeXlbaTFdLCB0eXBlID0gIm4iLCBsYXMgPSAxLAogICAgIHhsaW0gPSBjKDAuNSwzLjUpLCB5bGltID0gcmFuZ2UoeXkpLAogICAgIHhheHQgPSAibiIsCiAgICAgeGxhYiA9ICJSYXRpb24gc2l6ZSAoZykiLCB5bGFiID0gIkdyb3d0aCAobW0pIikKIyMgZ3JhbmQgbWVhbgphYmxpbmUoaCA9IG1lYW4oeXkpLCBsdHkgPSAiZGFzaGVkIikKIyMgbG93CnBvaW50cyhyZXAoMSwgbm4pLCB5eVtpMV0sIHBjaCA9IDE2LCBjb2wgPSAicmVkIikKYWJsaW5lKGggPSBtZWFuKHl5W2kxXSksIGNvbCA9ICJyZWQiLCBsdHkgPSAiZGFzaGVkIikKc2VnbWVudHMoMS4yLCBtZWFuKHl5KSwgMS4yLCBjb2VmKG0xKVsxXSwgY29sID0gInJlZCIsIGx3ZCA9IDIpCiMjIG1lZApwb2ludHMocmVwKDIsIG5uKSwgeXlbaTJdLCBwY2ggPSAxNiwgY29sID0gImJsdWUiKQphYmxpbmUoaCA9IG1lYW4oeXlbaTJdKSwgY29sID0gImJsdWUiLCBsdHkgPSAiZGFzaGVkIikKcG9pbnRzKHggPSAyLjM2LCB5ID0gbWVhbih5eSkgKyAwLjUqY29lZihtMilbMl0sCiAgICAgICBwY2ggPSAxOSwgY29sID0gIndoaXRlIiwgY2V4ID0gMykKc2VnbWVudHMoMi4yLCBtZWFuKHl5KSwgMi4yLCBjb2VmKG0xKVsyXSwgY29sID0gImJsdWUiLCBsd2QgPSAyKQojIyBoaWdoCnBvaW50cyhyZXAoMywgbm4pLCB5eVtpM10sIHBjaCA9IDE2LCBjb2wgPSAib3JhbmdlIikKYWJsaW5lKGggPSBtZWFuKHl5W2kzXSksIGNvbCA9ICJvcmFuZ2UiLCBsdHkgPSAiZGFzaGVkIikKc2VnbWVudHMoMy4yLCBtZWFuKHl5KSwgMy4yLCBjb2VmKG0xKVszXSwgY29sID0gIm9yYW5nZSIsIGx3ZCA9IDIpCiMjIGxhYmVscwp0ZXh0KHggPSAxLjE1LCB5ID0gbWVhbih5eSkgKyAwLjUqY29lZihtMilbMV0sIHBvcyA9IDQsCiAgICAgZXhwcmVzc2lvbihiZXRhWzFdKSwgeHBkID0gTkEsIGNvbCA9ICJyZWQiKQp0ZXh0KHggPSAyLjE1LCB5ID0gbWVhbih5eSkgKyAwLjUqY29lZihtMilbMl0sIHBvcyA9IDQsCiAgICAgZXhwcmVzc2lvbihiZXRhWzJdKSwgeHBkID0gTkEsIGNvbCA9ICJibHVlIikKdGV4dCh4ID0gMy4xNSwgeSA9IG1lYW4oeXkpICsgMC41KmNvZWYobTIpWzNdLCBwb3MgPSA0LAogICAgIGV4cHJlc3Npb24oYmV0YVszXSksIHhwZCA9IE5BLCBjb2wgPSAib3JhbmdlIikKdGV4dCh4ID0gMS4wMyAqIHBhcigidXNyIilbMl0sIHkgPSBtZWFuKHl5KSwKICAgICBleHByZXNzaW9uKG11KSwgeHBkID0gTkEpCmF4aXMoMSwgYXQgPSBzZXEoMyksIGxhYmVscyA9IGMoIkxvdyAoMikiLCAiTWVkICg2KSIsICJIaWdoICgxMCkiKSkKYGBgCgojIEFuYWx5c2lzIG9mIGNvdmFyaWFuY2UgKEFOQ09WQSkKCk5vdyBsZXQncyB0dXJuIG91ciBhdHRlbnRpb24gdG8gQU5DT1ZBIG1vZGVsLCB3aGljaCBjb21iaW5lIGNhdGVnb3JpY2FsIGFuZCBjb250aW51b3VzIHByZWRpY3RvcnMuIEZvciBleGFtcGxlLCBsZXQncyBhc3N1bWUgdGhlIGRhdGEgZnJvbSB0aGUgZmlzaCBncm93dGggZXhwZXJpbWVudCByZWFsbHkgbG9vayBsaWtlIHRoaXM6CgpgYGB7ciBwbG90X2FuY292YSwgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD01fQojIyBwbG90IGFsbCBkYXRhCnBhcihtYWkgPSBjKDAuOSwwLjksMC4xLDAuMSksCiAgICBvbWkgPSBjKDAsIDAsIDAsIDAuMiksCiAgICBjZXggPSAxLjEpCiMjIHBsb3QgYWxsIGRhdGEKcGxvdCh4X2NvdlsxOm5uXSwgeXlbMTpubl0sIHBjaCA9IDE2LCBjb2wgPSAicmVkIiwgeWxpbSA9IHJhbmdlKHl5KSwKICAgICBsYXMgPSAxLCB4bGFiID0gIlJhdGlvbiBzaXplIChnKSIsIHlsYWIgPSAiR3Jvd3RoIChtbSkiKQpwb2ludHMoeF9jb3ZbMTpubitubl0sIHl5WzE6bm4rbm5dLCBwY2ggPSAxNiwgY29sID0gImJsdWUiKQpwb2ludHMoeF9jb3ZbMTpubitubioyXSwgeXlbMTpubitubioyXSwgcGNoID0gMTYsIGNvbCA9ICJvcmFuZ2UiKQpgYGAKCkhlcmUgd2Ugd2FudCB0byBmaXQgYSBtb2RlbCB3aXRoIHRoZSBjYXRlZ29yaWNhbCBlZmZlY3Qgb2YgbGluZWFnZSAoZGVzaWduYXRlZCBieSB0aGUgdGhyZWUgY29sb3JzKSAmIHRoZSBjb250aW51b3VzIGVmZmVjdCBvZiByYXRpb24KCiQkClx0ZXh0e2dyb3d0aH1faSA9IFxhbHBoYSArIFxiZXRhX3sxLFx0ZXh0e2xpbmVhZ2V9fSArIFxiZXRhXzIgXHRleHR7cmF0aW9ufV9pICsgXGVwc2lsb25faQokJAoKV2UnbGwgZHJvcCB0aGUgZ2xvYmFsIGludGVyY2VwdCAkKFxhbHBoYSkkICYgd3JpdGUgb3V0IHRoZSBsaW5lYWdlIGVmZmVjdHMgdG8gZ2V0CgokJApcdGV4dHtncm93dGh9X2kgPSBcdW5kZXJicmFjZXtcYmV0YV8xIHhfezEsaX0gKyBcYmV0YV8yIHhfezIsaX0gKyBcYmV0YV8zIHhfezMsaX19X3tcdGV4dHtsaW5lYWdlfX0gKyBcdW5kZXJicmFjZXtcYmV0YV80IHhfezQsaX19X3tcdGV4dHtyYXRpb259fSArIGVfaQokJAoKRnJvbSB0aGlzIHdlIHdvdWxkIHRoZW4gZGVmaW5lICRcbWF0aGJme1h9JCBhbmQgJFxib2xkc3ltYm9se1xiZXRhfSQgYXMgKHdoZXJlIGFnYWluIHRoZSAkal9pJCBkZW5vdGUgdGhlIG51bWJlciBvZiBmaXNoIGluIGdyb3VwICRpJCBhbmQgJFxzdW0gal9pID0gbiQpLgoKJCQKXG1hdGhiZntYfSA9IApcYmVnaW57Ym1hdHJpeH0KMSAmIDAgJiAwICYgcl8xIFxcClx2ZG90cyAmIFx2ZG90cyAmIFx2ZG90cyAmIFx2ZG90cyBcXAoxICYgMCAmIDAgJiByX3tqXzF9IFxcIFxobGluZQowICYgMSAmIDAgJiByX3tqXzErMX0gXFwKXHZkb3RzICYgXHZkb3RzICYgXHZkb3RzICYgXHZkb3RzIFxcCjAgJiAxICYgMCAmIHJfe2pfMitqXzJ9IFxcIFxobGluZQowICYgMCAmIDEgJiByX3tqXzEral8yKzF9IFxcClx2ZG90cyAmIFx2ZG90cyAmIFx2ZG90cyAmIFx2ZG90cyBcXAowICYgMCAmIDEgJiByX24gClxlbmR7Ym1hdHJpeH0Kfn5+Clxib2xkc3ltYm9se1xiZXRhfSA9ClxiZWdpbntibWF0cml4fQpcYmV0YV8xIFxcClxiZXRhXzIgXFwKXGJldGFfMyBcXApcYmV0YV80ClxlbmR7Ym1hdHJpeH0KJCQKCk5vdyB3ZSBjYW4gYnVpbGQgb3VyIGRlc2lnbiBtYXRyaXguCgpgYGB7ciBmaXRfYW5jb3ZhLCBlY2hvID0gVFJVRX0KIyMgY3JlYXRlIGRlc2lnbiBtYXRyaXgKWFggPC0gY2JpbmQoTDEgPSByZXAoYygxLDAsMCksIGVhID0gbm4pLCAjIGVmZmVjdCBvZiBsaW5lYWdlIDEKICAgICAgICAgICAgTDIgPSByZXAoYygwLDEsMCksIGVhID0gbm4pLCAjIGVmZmVjdCBvZiBsaW5lYWdlIDIKICAgICAgICAgICAgTDMgPSByZXAoYygwLDAsMSksIGVhID0gbm4pLCAjIGVmZmVjdCBvZiBsaW5lYWdlIDMKICAgICAgICAgICAgUkEgPSB4X2NvdikgICAgICAgICAgICAgICAgICAjIGVmZmVjdCBvZiByYXRpb24KIyMgZml0IG1vZGVsCkJ2ZWMgPC0gY29lZihsbSh5eSB+IFhYIC0gMSkpCm5hbWVzKEJ2ZWMpIDwtIGMoImJldGFfMSIsICJiZXRhXzIiLCAiYmV0YV8zIiwgImJldGFfNCIpCkJ2ZWMKYGBgCgphbmQgcGxvdCB0aGUgZml0cyB3aXRoIG91ciBkYXRhLgoKYGBge3IgcGxvdF9hbmNvdmFfZml0LCBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTV9CiMjIHBsb3QgYWxsIGRhdGEKcGFyKG1haSA9IGMoMC45LDAuOSwwLjEsMC4xKSwKICAgIG9taSA9IGMoMCwgMCwgMCwgMC4yKSwKICAgIGNleCA9IDEuMSkKIyMgYmxhbmsgcGxvdApwbG90KHhfY292WzE6bm5dLCB5eVsxOm5uXSwgdHlwZSA9ICJuIiwgeWxpbSA9IHJhbmdlKHl5KSwKICAgICBsYXMgPSAxLCB4bGFiID0gIlJhdGlvbiBzaXplIChnKSIsIHlsYWIgPSAiR3Jvd3RoIChtbSkiKQojIyBhZGQgZml0cwphYmxpbmUoYSA9IEJ2ZWNbMV0sIGIgPSBCdmVjWzRdLCBjb2wgPSAicmVkIikKYWJsaW5lKGEgPSBCdmVjWzJdLCBiID0gQnZlY1s0XSwgY29sID0gImJsdWUiKQphYmxpbmUoYSA9IEJ2ZWNbM10sIGIgPSBCdmVjWzRdLCBjb2wgPSAib3JhbmdlIikKIyMgYWRkIGludGVyY2VwdHMKYWJsaW5lKGggPSBCdmVjWzFdLCBsdHkgPSAiZGFzaGVkIiwgY29sID0gInJlZCIpCmFibGluZShoID0gQnZlY1syXSwgbHR5ID0gImRhc2hlZCIsIGNvbCA9ICJibHVlIikKYWJsaW5lKGggPSBCdmVjWzNdLCBsdHkgPSAiZGFzaGVkIiwgY29sID0gIm9yYW5nZSIpCiMjIGFkZCBkYXRhCnBvaW50cyh4X2NvdlsxOm5uXSwgeXlbMTpubl0sIHBjaCA9IDE2LCBjb2wgPSAicmVkIikKcG9pbnRzKHhfY292WzE6bm4rbm5dLCB5eVsxOm5uK25uXSwgcGNoID0gMTYsIGNvbCA9ICJibHVlIikKcG9pbnRzKHhfY292WzE6bm4rbm4qMl0sIHl5WzE6bm4rbm4qMl0sIHBjaCA9IDE2LCBjb2wgPSAib3JhbmdlIikKIyMgYWRkIGxhYmVscwp0ZXh0KHggPSAxLjAzICogcGFyKCJ1c3IiKVsyXSwgeSA9IEJ2ZWNbMV0sCiAgICAgZXhwcmVzc2lvbihiZXRhWzFdKSwgeHBkID0gTkEsIGNvbCA9ICJyZWQiKQp0ZXh0KHggPSAxLjAzICogcGFyKCJ1c3IiKVsyXSwgeSA9IEJ2ZWNbMl0sCiAgICAgZXhwcmVzc2lvbihiZXRhWzJdKSwgeHBkID0gTkEsIGNvbCA9ICJibHVlIikKdGV4dCh4ID0gMS4wMyAqIHBhcigidXNyIilbMl0sIHkgPSBCdmVjWzNdLAogICAgIGV4cHJlc3Npb24oYmV0YVszXSksIHhwZCA9IE5BLCBjb2wgPSAib3JhbmdlIikKYGBgCgoKIyBVc2luZyBgbW9kZWwubWF0cml4KClgCgpIZXJlIGFyZSBhIG51bWJlciBvZiBleGFtcGxlcyBvZiB1c2luZyBgZmFjdG9yKClgIHdpdGggYG1vZGVsLm1hdHJpeCgpYCB0byBjcmVhdGUgYXBwcm9wcmlhdGUgZGVzaWduIG1hdHJpY2VzIGZvciB1c2UgaW4gb3VyIGxpbmVhciBtb2RlbHMuIFJlY2FsbCB0aGF0IGBmYWN0b3IoeClgIHRlbGxzICoqUioqIHRvIHRyZWF0IGB4YCBhcyBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlIHJhdGhlciB0aGFuIGNvbnRpbnVvdXMuCgpgYGB7ciBmYWN0b3JfMSwgZWNobyA9IFRSVUV9CiMjIDIgZ3JvdXBzIHdpdGggMiBvYnMgZWFjaApncm91cHMgPC0gZmFjdG9yKGMoMSwgMSwgMiwgMikpCiMjIGluc3BlY3QgdGhlbQpncm91cHMKYGBgCgpBbHNvIHJlY2FsbCB0aGF0IGBtb2RlbC5tYXRyaXgofiB4KWAgdXNlcyBhIHJpZ2h0LWhhbmQgc2lkZSBmb3JtdWxhIGNvbnN0cnVjdCBgfiB4YAoKYGBge3IgbW9kX21hdF8xLCBlY2hvID0gVFJVRX0KIyMgY3JlYXRlIGRlc2lnbiBtYXRyaXggZnJvbSBgZ3JvdXBzYAptb2RlbC5tYXRyaXgofiBncm91cHMpCmBgYAoKV2hhdCBpZiB3ZSBkb24ndCB1c2UgYGZhY3RvcigpYD8KCmBgYHtyIG1vZF9tYXRfMiwgZWNobyA9IFRSVUV9CiMjIDIgZ3JvdXBzIHdpdGggMiBvYnMgZWFjaApncm91cHMgPC0gYygxLCAxLCAyLCAyKQojIyBjcmVhdGUgZGVzaWduIG1hdHJpeCBmcm9tIGBncm91cHNgCm1vZGVsLm1hdHJpeCh+IGdyb3VwcykKYGBgCgpZb3UgY2FuIGRyb3AgdGhlIGludGVyY2VwdCB0ZXJtIGJ5IGluY2x1ZGluZyBgLSAxYCBpbiB0aGUgY2FsbCB0byBgbW9kZWwubmF0cml4KClgLgoKYGBge3IgbW9kX21hdF8zLCBlY2hvID0gVFJVRX0KIyMgMiBncm91cHMgd2l0aCAyIG9icyBlYWNoCmdyb3VwcyA8LSBmYWN0b3IoYygxLCAxLCAyLCAyKSkKIyMgY3JlYXRlIGRlc2lnbiBtYXRyaXggZnJvbSBgZ3JvdXBzYAptb2RlbC5tYXRyaXgofiBncm91cHMgLSAxKQpgYGAKCk5vdGUgdGhhdCB0aGUgbmFtZXMgb3IgY2F0ZWdvcmllcyBhcmUgaXJyZWxldmFudCBmb3IgYGZhY3RvcigpYCwgaW4gdGhhdCBpdCB3aWxsIGNvbnZlcnQgY2hhcmFjdGVycyBpbnRvIGludGVnZXJzLgoKYGBge3IgbW9kX21hdF80LCBlY2hvID0gVFJVRX0KIyMgMiBncm91cHMgd2l0aCAyIG9icyBlYWNoCmdyb3VwcyA8LSBmYWN0b3IoYygicmVmIiwgInJlZiIsICJleHAiLCAiZXhwIikpCiMjIGNyZWF0ZSBkZXNpZ24gbWF0cml4IGZyb20gYGdyb3Vwc2AKbW9kZWwubWF0cml4KH4gZ3JvdXBzKQpgYGAKCkJ5IGRlZmF1bHQsICoqUioqIGFzc2lnbnMgZmFjdG9ycyBpbiBhbHBoYWJldGljYWwgb3JkZXIsIHN1Y2ggdGhhdCB0aGUgKnJlZmVyZW5jZSogY2F0ZWdvcnkgaXMgZmlyc3QgaW4gdGhpcyBleGFtcGxlOgoKYGBge3IgbW9kX21hdF81LCBlY2hvID0gVFJVRX0KIyMgMiBncm91cHMgd2l0aCAyIG9icyBlYWNoCmdyb3VwcyA8LSBmYWN0b3IoYygicmVmIiwgInJlZiIsICJleHAiLCAiZXhwIikpCiMjIGNyZWF0ZSBkZXNpZ24gbWF0cml4IGZyb20gYGdyb3Vwc2AKbW9kZWwubWF0cml4KH4gZ3JvdXBzKQpgYGAKCldlIGNhbiBjaGFuZ2UgcmVvcmRlciBjYXNlcyB3aXRoIGByZWxldmVsKClgLCB3aGljaCB0ZWxscyAqKlIqKiB3aGljaCBncm91cCB0byBhc3NpZ24gdGhlIGAwYCBjYXRlZ29yeSB0bwoKYGBge3IgbW9kX21hdF82LCBlY2hvID0gVFJVRX0KIyMgMiBncm91cHMgd2l0aCAyIG9icyBlYWNoCmdyb3VwcyA8LSByZWxldmVsKGdyb3VwcywgInJlZiIpCiMjIGNyZWF0ZSBkZXNpZ24gbWF0cml4IGZyb20gYGdyb3Vwc2AKbW9kZWwubWF0cml4KH4gZ3JvdXBzKQpgYGAKCkZvciBBTk9WQSBkZXNpZ25zIHdpdGggbW9yZSB0aGF0IG9uZSBmYWN0b3IsIHdlIGNhbiBhZGQgbXVsdGlwbGUgZmFjdG9ycyB3aXRoIGArYCBpbnNpZGUgdGhlIGNhbGwgdG8gYG1vZGVsLm1hdHJpeCgpYC4KCmBgYHtyIG1vZF9tYXRfNywgZWNobyA9IFRSVUV9CmRpZXQgPC0gZmFjdG9yKGMoMSwgMSwgMiwgMikpCnNleCA8LSBmYWN0b3IoYygiZiIsICJtIiwgImYiLCAibSIpKQptb2RlbC5tYXRyaXgofiBkaWV0ICsgc2V4KQpgYGAKCgoKCgo=