Theory
Let’s start with the theory part. The simplex method is a very
popular algorithm to solve constrained linear
optimization (maximization or
minimization) problems. It can be thought of as
optimizing a linear objective function of the
decision variables, so that the (linear) constraints
remain satisfied, by traveling in the direction of increase in the value
of the objective function (or equivalently decrease in the value of
objective, in case of a minimization problem), from one corner to the
another corner of a convex polytope formed by the constraints (each
corner represents a basic feasible solution), until the
optimal solution is obtained (assuming the problem is
bounded and feasible) and stopping as
soon as the optimal solution is obtained.
The iterative algorithm starts with a basic feasible solution and
with each iteration it improves (e.g., for a maximization problem
increases) the objective function value by changing a nonbasic variable
to a basic one. First the linear program needs to be converted to the
standard form. At each iteration the nonbasic variable corresponding to
the most negative value in the reduced costs (allowing
the highest possible increase) enters the
basis, which constitutes the pivot
column, Now, the basic variable which is first supposed to violate a
constraint leaves the basis,
determined by the minimum non-negative finite value in the
ratio test. The algorithm terminates when all values
are non-negative in the reduced costs, since there is
no further scope of increase in value for the objective function.
The next figure describes how the simplex method can be thought of in
matrix way. Note that the classic tableau can be
computed using the matrix method at any step, although it’s not
required.

Implementation
The next R code snippet implements the above
algorithm in matrix way, at every iterative step of the simplex method,
the function simplex.matrix.iter() needs to get called.
Note that we only need to consider the pivot column to determine which
basic variable leaves the basis, using the ratio test and not the entier
matrix.
library(tidyverse)
library(knitr)
print.matrices <- function(A, b, c, X_B, X_N, A_B, A_N, c_B, c_N, x, z, r_c, opt) {
X <- 1:(ncol(A))
dimnames(A) <- list(NULL, paste0('x', X)) #[[2]]
print(A %>% kable(caption='**A**'))
print(b %>% kable(caption='**b**'))
c_t <- matrix(c, nrow=1)
dimnames(c_t)[[2]] <- paste0('x', X)
print(c_t %>% kable(caption='$$c^T$$'))
X_B_t <- matrix(X_B, nrow=1)
dimnames(X_B_t)[[2]] <- paste0('x', X_B)
print(X_B_t %>% kable(caption='$$X_B$$'))
X_N_t <- matrix(X_N, nrow=1)
dimnames(X_N_t)[[2]] <- paste0('x', X_N)
print(X_N_t %>% kable(caption='$$X_N$$'))
c_B_t <- matrix(c_B, nrow=1)
dimnames(c_B_t)[[2]] <- paste0('x', X_B)
print(c_B_t %>% kable(caption='$$c_B^T$$'))
c_N_t <- matrix(c_N, nrow=1)
dimnames(c_N_t)[[2]] <- paste0('x', X_N)
print(c_N_t %>% kable(caption='$$c_N^T$$'))
dimnames(A_B) <- list(NULL, paste0('x', X_B))
dimnames(A_N) <- list(NULL, paste0('x', X_N))
print(A_B %>% kable(caption='$$A_B$$'))
print(A_N %>% kable(caption='$$A_N$$'))
x_t <- matrix(x, nrow=1)
dimnames(x_t)[[2]] <- paste0('x', X)
print(x_t %>% kable(caption='**x (solution)**'))
print(z %>% kable(caption='**z (objective value)**'))
print(opt %>% kable(caption='**Optimal**'))
r_c <- matrix(r_c, nrow=1)
dimnames(r_c) <- list(NULL, paste0('x', X_N))
print(r_c %>% kable(caption='**reduced cost** = $$c_B^TA_B^{-1}A_N-c_N^T$$'))
}
simplex.matrix.iter <- function(A, b, c, X_B) {
X <- 1:(ncol(A))
X_N <- setdiff(X, X_B)
A_B <- A[,X_B]
A_N <- A[,X_N]
c_B <- c[X_B]
c_N <- c[X_N]
A_B_1 <- solve(A_B)
#A_B_1A <- A_B_1 %*% A
A_B_1b <- solve(A_B, b)
c_BA_B_1 <- c_B %*% A_B_1
c_BA_B_1A_N_c_N <- c_BA_B_1%*%A_N - c_N
x <- rep(0, ncol(A))
x[X_B] <- A_B_1b
z <- c_BA_B_1 %*% b
opt <- all(c_BA_B_1A_N_c_N >= 0) #all(c(c_BB_1, c_BB_1A_c) >= 0)
print.matrices(A, b, c, X_B, X_N, A_B, A_N, c_B, c_N, x, z, c_BA_B_1A_N_c_N, opt)
res <- list()
m <- min(c_BA_B_1A_N_c_N)
if (m < 0) {
e_ix <- X_N[which.min(c_BA_B_1A_N_c_N)]
A_B_1A_e <- A_B_1 %*% A[,e_ix]
m <- Inf
l_ix <- -1
for (i in 1:length(A_B_1b)) {
if ((A_B_1A_e[i] > 0) & (A_B_1b[i] / A_B_1A_e[i] < m)) {
m <- A_B_1b[i] / A_B_1A_e[i]
l_ix <- i
}
}
if (l_ix < 0) {
res$unbounded <- TRUE
return(res)
}
print(data.frame(step=paste0('x', e_ix, ' enters the basis')) %>% kable())
print(A_B_1A_e %>% kable(caption=paste0('$$A_B^{-1}A$$', e_ix)))
print(A_B_1b %>% kable(caption='$$A_B^{-1}b$$'))
r <- A_B_1b / A_B_1A_e
dimnames(r) <- list(paste0('x', X_B), NULL)
print(r %>% kable(caption='**ratio**'))
print(data.frame(step=paste0('x', X_B[l_ix], ' leaves the basis')) %>% kable())
X_B[l_ix] <- e_ix
} else {
res$opt <- TRUE
}
res$X_B <- X_B # vars
res$x <- x # values
res$z <- z # objective
res$opt <- opt
res$unbounded <- FALSE
return(res)
}
Examples
Now, let’s consider a few examples to demonstrate the above algorithm
implementation step by step.
Example 1

Using the above theory, here’s how the simplex method works, starting
from the basis \[X_B=(x_1,x_4,x_5)\]:

Now, let’s obtain the same result using the implementation above:
A <- matrix(c(2,2,0,-1,1,1), ncol=2)
m <- nrow(A)
n <- ncol(A)
b <- matrix(c(4,8,3), ncol=1)
c <- c(1,0)
A <- cbind(A, diag(m)) # add slack columns
c <- c(c, rep(0, m))
X_B <- c(1,4,5)
opt <- FALSE
i <- 1
while (!opt) {
print(data.frame(Iteration=i) %>% kable())
res <- simplex.matrix.iter(A, b, c, X_B)
X_B <- res$X_B
opt <- res$opt
#print(res)
i <- i + 1
}
A
| 2 |
-1 |
1 |
0 |
0 |
| 2 |
1 |
0 |
1 |
0 |
| 0 |
1 |
0 |
0 |
1 |
reduced cost = cBTAB−1AN − cNT
| -0.5 |
0.5 |
A
| 2 |
-1 |
1 |
0 |
0 |
| 2 |
1 |
0 |
1 |
0 |
| 0 |
1 |
0 |
0 |
1 |
reduced cost = cBTAB−1AN − cNT
| 0.25 |
0.25 |
Example 2
This time let’s start with \[X_B=(x_3,x_4)\], i.e. the slack
variables \[(s_1,s_2)\] as
the basic variables.

Now, lets demonstrate with our implementation with the simplex matrix
method:
A <- matrix(c(1,1,1,2), ncol=2)
m <- nrow(A)
n <- ncol(A)
b <- matrix(c(4,6), ncol=1)
c <- c(2,3)
A <- cbind(A, diag(m)) # add slack columns
c <- c(c, rep(0, m))
X_B <- (n+1):(n+m)
opt <- FALSE
i <- 1
while (!opt) {
print(data.frame(Iteration=i) %>% kable())
res <- simplex.matrix.iter(A, b, c, X_B)
X_B <- res$X_B
opt <- res$opt
#print(res)
i <- i + 1
}
reduced cost = cBTAB−1AN − cNT
| -2 |
-3 |
reduced cost = cBTAB−1AN − cNT
| -0.5 |
1.5 |
reduced cost = cBTAB−1AN − cNT
| 1 |
1 |
Verify the soltion obtained with R package’s
implementation:
library(lpSolve)
objective.fn <- c
const.mat <- A
const.dir <- rep("<=", nrow(A))
const.rhs <- b
lp.solution <- lp("max", objective.fn, const.mat,
const.dir, const.rhs, compute.sens=TRUE)
#lp.solution$objective
print(lp.solution$solution)
[1] 2 2 0 0
print(lp.solution$objval)
[1] 10
Example 3

Again, let’s start with \[X_B=(x_3,x_4,x_5)\], i.e. the
slack variables as the basic variables.
A <- matrix(c(-1,-1,3,1,2,1), ncol=2)
m <- nrow(A)
n <- ncol(A)
b <- matrix(c(3,8,18), ncol=1)
c <- c(1,3)
A <- cbind(A, diag(m)) # add slack columns
c <- c(c, rep(0, m))
X_B <- (n+1):(n+m)
opt <- FALSE
i <- 1
while (!opt) {
print(data.frame(Iteration=i) %>% kable())
res <- simplex.matrix.iter(A, b, c, X_B)
X_B <- res$X_B
opt <- res$opt
#print(res)
i <- i + 1
}
A
| -1 |
1 |
1 |
0 |
0 |
| -1 |
2 |
0 |
1 |
0 |
| 3 |
1 |
0 |
0 |
1 |
reduced cost = cBTAB−1AN − cNT
| -1 |
-3 |
A
| -1 |
1 |
1 |
0 |
0 |
| -1 |
2 |
0 |
1 |
0 |
| 3 |
1 |
0 |
0 |
1 |
reduced cost = cBTAB−1AN − cNT
| -4 |
3 |
ratio
| x2 |
-3.00 |
| x4 |
2.00 |
| x5 |
3.75 |
A
| -1 |
1 |
1 |
0 |
0 |
| -1 |
2 |
0 |
1 |
0 |
| 3 |
1 |
0 |
0 |
1 |
reduced cost = cBTAB−1AN − cNT
| -5 |
4 |
A
| -1 |
1 |
1 |
0 |
0 |
| -1 |
2 |
0 |
1 |
0 |
| 3 |
1 |
0 |
0 |
1 |
reduced cost = cBTAB−1AN − cNT
| 1.142857 |
0.7142857 |
Let’s compare the above solution obtained using the function
lp() from R package lpSolve.
Note that exactly same solution is obtained.
library(lpSolve)
objective.fn <- c
const.mat <- A
const.dir <- rep("<=", nrow(A))
const.rhs <- b
lp.solution <- lp("max", objective.fn, const.mat,
const.dir, const.rhs, compute.sens=TRUE)
print(lp.solution$solution)
[1] 4 6 0 0 0
print(lp.solution$objval)
[1] 22
Example 4

A <- matrix(c(1,2,0,0,2,1,3,0,5,1,-3,2), nrow=3)
m <- nrow(A)
n <- ncol(A)
b <- matrix(c(15,18,20), ncol=1)
c <- c(4,3,2,3)
A <- cbind(A, diag(m)) # add slack columns
c <- c(c, rep(0, m))
X_B <- (n+1):(n+m)
opt <- FALSE
i <- 1
while (!opt) {
print(data.frame(Iteration=i) %>% kable())
res <- simplex.matrix.iter(A, b, c, X_B)
X_B <- res$X_B
opt <- res$opt
#print(res)
i <- i + 1
}
A
| 1 |
0 |
3 |
1 |
1 |
0 |
0 |
| 2 |
2 |
0 |
-3 |
0 |
1 |
0 |
| 0 |
1 |
5 |
2 |
0 |
0 |
1 |
AN
| 1 |
0 |
3 |
1 |
| 2 |
2 |
0 |
-3 |
| 0 |
1 |
5 |
2 |
x (solution)
| 0 |
0 |
0 |
0 |
15 |
18 |
20 |
reduced cost = cBTAB−1AN − cNT
| -4 |
-3 |
-2 |
-3 |
A
| 1 |
0 |
3 |
1 |
1 |
0 |
0 |
| 2 |
2 |
0 |
-3 |
0 |
1 |
0 |
| 0 |
1 |
5 |
2 |
0 |
0 |
1 |
AN
| 0 |
3 |
1 |
0 |
| 2 |
0 |
-3 |
1 |
| 1 |
5 |
2 |
0 |
x (solution)
| 9 |
0 |
0 |
0 |
6 |
0 |
20 |
reduced cost = cBTAB−1AN − cNT
| 1 |
-2 |
-9 |
2 |
ratio
| x5 |
2.4 |
| x1 |
-6.0 |
| x7 |
10.0 |
A
| 1 |
0 |
3 |
1 |
1 |
0 |
0 |
| 2 |
2 |
0 |
-3 |
0 |
1 |
0 |
| 0 |
1 |
5 |
2 |
0 |
0 |
1 |
AN
| 0 |
3 |
1 |
0 |
| 2 |
0 |
0 |
1 |
| 1 |
5 |
0 |
0 |
x (solution)
| 12.6 |
0 |
0 |
2.4 |
0 |
0 |
15.2 |
reduced cost = cBTAB−1AN − cNT
| -2.6 |
8.8 |
3.6 |
0.2 |
ratio
| x4 |
-6.000000 |
| x1 |
31.500000 |
| x7 |
8.444444 |
A
| 1 |
0 |
3 |
1 |
1 |
0 |
0 |
| 2 |
2 |
0 |
-3 |
0 |
1 |
0 |
| 0 |
1 |
5 |
2 |
0 |
0 |
1 |
AN
| 3 |
1 |
0 |
0 |
| 0 |
0 |
1 |
0 |
| 5 |
0 |
0 |
1 |
x (solution)
| 9.222222 |
8.444444 |
0 |
5.777778 |
0 |
0 |
0 |
z (objective value)
| 79.55556 |
reduced cost = cBTAB−1AN − cNT
| 12.55556 |
2.444444 |
0.7777778 |
1.444444 |
Verification with lpSolve:
library(lpSolve)
objective.fn <- c
const.mat <- A
const.dir <- rep("<=", nrow(A))
const.rhs <- b
lp.solution <- lp("max", objective.fn, const.mat,
const.dir, const.rhs, compute.sens=TRUE)
#lp.solution$objective
print(lp.solution$solution)
[1] 9.222222 8.444444 0.000000 5.777778 0.000000 0.000000 0.000000
print(lp.solution$objval)
[1] 79.55556
Example 5

A <- matrix(c(2,1,2,1,2,2,1,3,1), nrow=3)
m <- nrow(A)
n <- ncol(A)
b <- matrix(c(2,5,6), ncol=1)
c <- c(3,1,3)
A <- cbind(A, diag(m)) # add slack columns
c <- c(c, rep(0, m))
X_B <- (n+1):(n+m)
opt <- FALSE
i <- 1
while (!opt) {
print(data.frame(Iteration=i) %>% kable())
res <- simplex.matrix.iter(A, b, c, X_B)
X_B <- res$X_B
opt <- res$opt
#print(res)
i <- i + 1
}
A
| 2 |
1 |
1 |
1 |
0 |
0 |
| 1 |
2 |
3 |
0 |
1 |
0 |
| 2 |
2 |
1 |
0 |
0 |
1 |
reduced cost = cBTAB−1AN − cNT
| -3 |
-1 |
-3 |
A
| 2 |
1 |
1 |
1 |
0 |
0 |
| 1 |
2 |
3 |
0 |
1 |
0 |
| 2 |
2 |
1 |
0 |
0 |
1 |
reduced cost = cBTAB−1AN − cNT
| 0.5 |
-1.5 |
1.5 |
ratio
| x1 |
2.0 |
| x5 |
1.6 |
| x6 |
Inf |
A
| 2 |
1 |
1 |
1 |
0 |
0 |
| 1 |
2 |
3 |
0 |
1 |
0 |
| 2 |
2 |
1 |
0 |
0 |
1 |
x (solution)
| 0.2 |
0 |
1.6 |
0 |
0 |
4 |
reduced cost = cBTAB−1AN − cNT
| 1.4 |
1.2 |
0.6 |
Verification with lpSolve:
library(lpSolve)
objective.fn <- c
const.mat <- A
const.dir <- rep("<=", nrow(A))
const.rhs <- b
lp.solution <- lp("max", objective.fn, const.mat,
const.dir, const.rhs, compute.sens=TRUE)
#lp.solution$objective
print(lp.solution$solution)
[1] 0.2 0.0 1.6 0.0 0.0 0.0
print(lp.solution$objval)
[1] 5.4
LS0tDQp0aXRsZTogIkltcGxlbWVudGluZyB0aGUgU2ltcGxleCBNZXRob2QgaW4gdGhlIE1hdHJpeCBXYXkiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyMgU2FuZGlwYW4gRGV5DQoNCiMjIEludHJvZHVjdGlvbg0KDQpUaGUgZ29hbCBvZiB0aGlzIGFydGljbGUgaXMgdG8gZXhwbGFpbiwgaW1wbGVtZW50IGFuZCBkZW1vbnN0cmF0ZSBob3cgdGhlIHBvcHVsYXIgKipzaW1wbGV4KiogYWxnb3JpdGhtIGZvciBzb2x2aW5nIGEgKipsaW5lYXIgcHJvZ3JhbSoqIGNhbiBiZSByZXZpc2VkIHRvIGZvcm0gYSBtYXRyaXgtYmFzZWQgYWxnb3JpdGhtIChpbnN0ZWFkIG9mIHRhYmxlYXUtYmFzZWQpLCBzbyB0aGF0IHRoZSBhbGdvcml0aG0gaXMga25vd24gYXMgKipyZXZpc2VkIHNpbXBsZXgqKiBhbGdvcml0aG0uIFRoaXMgYWxnb3JpdGhtIGhhcyBiZWVuIHRhdWdodCBpbiB0aGUgKipjb3Vyc2VyYSoqIGNvdXJzZSAqKk9wZXJhdGlvbnMgUmVzZWFyY2ggVGhlb3J5KiogYnkgdGhlICoqTmF0aW9uYWwgVGFpd2FuIFVuaXZlcnNpdHkqKi4gVGhlIHRoZW9yeSBwYXJ0IHdpbGwgYmUgZGVzY3JpYmVkIGFzIGlzIGZyb20gdGhlIGNvdXJzZSBpdHNlbGYsIGFsb25nIHdpdGggYW4gYXR0ZW1wdCAgdG8gaW1wbGVtZW50IHRoZSAqKm1hdHJpeC1iYXNlZCBzaW1wbGV4IG1ldGhvZCoqIHdpdGggKipSKiogKGZyb20gc2NyYXRjaCkgYW5kIGRlbW9uc3RyYXRlIHdpdGggZXhhbXBsZXMuDQoNCiMjIFRoZW9yeQ0KDQpMZXQncyBzdGFydCB3aXRoIHRoZSB0aGVvcnkgcGFydC4gVGhlIHNpbXBsZXggbWV0aG9kIGlzIGEgdmVyeSBwb3B1bGFyIGFsZ29yaXRobSB0byBzb2x2ZSAqKmNvbnN0cmFpbmVkIGxpbmVhciBvcHRpbWl6YXRpb24qKiAoKiptYXhpbWl6YXRpb24qKiBvciAqKm1pbmltaXphdGlvbioqKSBwcm9ibGVtcy4gSXQgY2FuIGJlIHRob3VnaHQgb2YgYXMgb3B0aW1pemluZyBhIGxpbmVhciAqKm9iamVjdGl2ZSBmdW5jdGlvbioqIG9mIHRoZSAqKmRlY2lzaW9uKiogdmFyaWFibGVzLCBzbyB0aGF0IHRoZSAobGluZWFyKSBjb25zdHJhaW50cyByZW1haW4gc2F0aXNmaWVkLCBieSB0cmF2ZWxpbmcgaW4gdGhlIGRpcmVjdGlvbiBvZiBpbmNyZWFzZSBpbiB0aGUgdmFsdWUgb2YgdGhlIG9iamVjdGl2ZSBmdW5jdGlvbiAob3IgZXF1aXZhbGVudGx5IGRlY3JlYXNlIGluIHRoZSB2YWx1ZSBvZiBvYmplY3RpdmUsIGluIGNhc2Ugb2YgYSBtaW5pbWl6YXRpb24gcHJvYmxlbSksIGZyb20gb25lIGNvcm5lciB0byB0aGUgYW5vdGhlciBjb3JuZXIgb2YgYSBjb252ZXggcG9seXRvcGUgZm9ybWVkIGJ5IHRoZSBjb25zdHJhaW50cyAoZWFjaCBjb3JuZXIgcmVwcmVzZW50cyBhICoqYmFzaWMgZmVhc2libGUgc29sdXRpb24qKiksIHVudGlsIHRoZSBvcHRpbWFsIHNvbHV0aW9uIGlzIG9idGFpbmVkIChhc3N1bWluZyB0aGUgcHJvYmxlbSBpcyAqKmJvdW5kZWQqKiBhbmQgKipmZWFzaWJsZSoqKSBhbmQgc3RvcHBpbmcgYXMgc29vbiBhcyB0aGUgb3B0aW1hbCBzb2x1dGlvbiBpcyBvYnRhaW5lZC4NCg0KVGhlIGl0ZXJhdGl2ZSBhbGdvcml0aG0gc3RhcnRzIHdpdGggYSBiYXNpYyBmZWFzaWJsZSBzb2x1dGlvbiBhbmQgd2l0aCBlYWNoIGl0ZXJhdGlvbiBpdCBpbXByb3ZlcyAoZS5nLiwgZm9yIGEgbWF4aW1pemF0aW9uIHByb2JsZW0gaW5jcmVhc2VzKSB0aGUgb2JqZWN0aXZlIGZ1bmN0aW9uIHZhbHVlIGJ5IGNoYW5naW5nIGEgbm9uYmFzaWMgdmFyaWFibGUgdG8gYSBiYXNpYyBvbmUuIEZpcnN0IHRoZSBsaW5lYXIgcHJvZ3JhbSBuZWVkcyB0byBiZSBjb252ZXJ0ZWQgdG8gdGhlIHN0YW5kYXJkIGZvcm0uIEF0IGVhY2ggaXRlcmF0aW9uIHRoZSBub25iYXNpYyB2YXJpYWJsZSBjb3JyZXNwb25kaW5nIHRvIHRoZSBtb3N0IG5lZ2F0aXZlIHZhbHVlIGluIHRoZSAqKnJlZHVjZWQgY29zdHMqKiAoYWxsb3dpbmcgdGhlIGhpZ2hlc3QgcG9zc2libGUgaW5jcmVhc2UpICoqZW50ZXJzKiogdGhlICoqYmFzaXMqKiwgd2hpY2ggY29uc3RpdHV0ZXMgdGhlICoqcGl2b3QqKiBjb2x1bW4sIE5vdywgdGhlIGJhc2ljIHZhcmlhYmxlIHdoaWNoIGlzIGZpcnN0IHN1cHBvc2VkIHRvIHZpb2xhdGUgYSBjb25zdHJhaW50ICoqbGVhdmVzKiogdGhlICoqYmFzaXMqKiwgZGV0ZXJtaW5lZCBieSB0aGUgbWluaW11bSBub24tbmVnYXRpdmUgZmluaXRlIHZhbHVlIGluIHRoZSAqKnJhdGlvKiogdGVzdC4gVGhlIGFsZ29yaXRobSB0ZXJtaW5hdGVzIHdoZW4gYWxsIHZhbHVlcyBhcmUgbm9uLW5lZ2F0aXZlIGluIHRoZSAqKnJlZHVjZWQgY29zdHMqKiwgc2luY2UgdGhlcmUgaXMgbm8gZnVydGhlciBzY29wZSBvZiBpbmNyZWFzZSBpbiB2YWx1ZSBmb3IgdGhlIG9iamVjdGl2ZSBmdW5jdGlvbi4NCg0KVGhlIG5leHQgZmlndXJlIGRlc2NyaWJlcyBob3cgdGhlIHNpbXBsZXggbWV0aG9kIGNhbiBiZSB0aG91Z2h0IG9mIGluIG1hdHJpeCB3YXkuIE5vdGUgdGhhdCB0aGUgY2xhc3NpYyAqKnRhYmxlYXUqKiBjYW4gYmUgY29tcHV0ZWQgdXNpbmcgdGhlIG1hdHJpeCBtZXRob2QgYXQgYW55IHN0ZXAsIGFsdGhvdWdoIGl0J3Mgbm90IHJlcXVpcmVkLg0KDQohW10odGhlb3J5LnBuZykNCg0KIyMgSW1wbGVtZW50YXRpb24NCg0KVGhlIG5leHQgKipSKiogY29kZSBzbmlwcGV0IGltcGxlbWVudHMgdGhlIGFib3ZlIGFsZ29yaXRobSBpbiBtYXRyaXggd2F5LCBhdCBldmVyeSBpdGVyYXRpdmUgc3RlcCBvZiB0aGUgc2ltcGxleCBtZXRob2QsIHRoZSBmdW5jdGlvbiBgc2ltcGxleC5tYXRyaXguaXRlcigpYCBuZWVkcyB0byBnZXQgY2FsbGVkLiBOb3RlIHRoYXQgd2Ugb25seSBuZWVkIHRvIGNvbnNpZGVyIHRoZSBwaXZvdCBjb2x1bW4gdG8gZGV0ZXJtaW5lIHdoaWNoIGJhc2ljIHZhcmlhYmxlIGxlYXZlcyB0aGUgYmFzaXMsIHVzaW5nIHRoZSByYXRpbyB0ZXN0IGFuZCBub3QgdGhlIGVudGllciBtYXRyaXguDQoNCmBgYHtyIHJlc3VsdHM9J2FzaXMnLCBudWxsX3ByZWZpeD1UUlVFLCBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGtuaXRyKQ0KDQpwcmludC5tYXRyaWNlcyA8LSBmdW5jdGlvbihBLCBiLCBjLCBYX0IsIFhfTiwgQV9CLCBBX04sIGNfQiwgY19OLCB4LCB6LCByX2MsIG9wdCkgew0KDQogIFggPC0gMToobmNvbChBKSkNCiAgZGltbmFtZXMoQSkgPC0gbGlzdChOVUxMLCBwYXN0ZTAoJ3gnLCBYKSkgI1tbMl1dDQogIHByaW50KEEgJT4lICBrYWJsZShjYXB0aW9uPScqKkEqKicpKQ0KICBwcmludChiICU+JSBrYWJsZShjYXB0aW9uPScqKmIqKicpKSANCiAgY190IDwtIG1hdHJpeChjLCBucm93PTEpDQogIGRpbW5hbWVzKGNfdClbWzJdXSA8LSBwYXN0ZTAoJ3gnLCBYKQ0KICBwcmludChjX3QgJT4lIGthYmxlKGNhcHRpb249JyQkY15UJCQnKSkNCiAgWF9CX3QgPC0gbWF0cml4KFhfQiwgbnJvdz0xKQ0KICBkaW1uYW1lcyhYX0JfdClbWzJdXSA8LSBwYXN0ZTAoJ3gnLCBYX0IpDQogIHByaW50KFhfQl90ICU+JSBrYWJsZShjYXB0aW9uPSckJFhfQiQkJykpDQogIFhfTl90IDwtIG1hdHJpeChYX04sIG5yb3c9MSkNCiAgZGltbmFtZXMoWF9OX3QpW1syXV0gPC0gcGFzdGUwKCd4JywgWF9OKQ0KICBwcmludChYX05fdCAlPiUga2FibGUoY2FwdGlvbj0nJCRYX04kJCcpKQ0KICBjX0JfdCA8LSBtYXRyaXgoY19CLCBucm93PTEpDQogIGRpbW5hbWVzKGNfQl90KVtbMl1dIDwtIHBhc3RlMCgneCcsIFhfQikNCiAgcHJpbnQoY19CX3QgJT4lIGthYmxlKGNhcHRpb249JyQkY19CXlQkJCcpKQ0KICBjX05fdCA8LSBtYXRyaXgoY19OLCBucm93PTEpDQogIGRpbW5hbWVzKGNfTl90KVtbMl1dIDwtIHBhc3RlMCgneCcsIFhfTikNCiAgcHJpbnQoY19OX3QgJT4lIGthYmxlKGNhcHRpb249JyQkY19OXlQkJCcpKQ0KICBkaW1uYW1lcyhBX0IpIDwtIGxpc3QoTlVMTCwgcGFzdGUwKCd4JywgWF9CKSkNCiAgZGltbmFtZXMoQV9OKSA8LSBsaXN0KE5VTEwsIHBhc3RlMCgneCcsIFhfTikpDQogIHByaW50KEFfQiAlPiUga2FibGUoY2FwdGlvbj0nJCRBX0IkJCcpKQ0KICBwcmludChBX04gJT4lIGthYmxlKGNhcHRpb249JyQkQV9OJCQnKSkNCiAgeF90IDwtIG1hdHJpeCh4LCBucm93PTEpDQogIGRpbW5hbWVzKHhfdClbWzJdXSA8LSBwYXN0ZTAoJ3gnLCBYKQ0KICBwcmludCh4X3QgJT4lIGthYmxlKGNhcHRpb249JyoqeCAoc29sdXRpb24pKionKSkNCiAgcHJpbnQoeiAlPiUga2FibGUoY2FwdGlvbj0nKip6IChvYmplY3RpdmUgdmFsdWUpKionKSkNCiAgcHJpbnQob3B0ICU+JSBrYWJsZShjYXB0aW9uPScqKk9wdGltYWwqKicpKQ0KICByX2MgPC0gbWF0cml4KHJfYywgbnJvdz0xKQ0KICBkaW1uYW1lcyhyX2MpIDwtIGxpc3QoTlVMTCwgcGFzdGUwKCd4JywgWF9OKSkNCiAgcHJpbnQocl9jICU+JSBrYWJsZShjYXB0aW9uPScqKnJlZHVjZWQgY29zdCoqID0gJCRjX0JeVEFfQl57LTF9QV9OLWNfTl5UJCQnKSkNCn0NCg0Kc2ltcGxleC5tYXRyaXguaXRlciA8LSBmdW5jdGlvbihBLCBiLCBjLCBYX0IpIHsNCiAgWCA8LSAxOihuY29sKEEpKQ0KICBYX04gPC0gc2V0ZGlmZihYLCBYX0IpDQogIEFfQiA8LSBBWyxYX0JdDQogIEFfTiA8LSBBWyxYX05dDQogIGNfQiA8LSBjW1hfQl0NCiAgY19OIDwtIGNbWF9OXQ0KICBBX0JfMSA8LSBzb2x2ZShBX0IpDQogICNBX0JfMUEgPC0gQV9CXzEgJSolIEENCiAgQV9CXzFiIDwtIHNvbHZlKEFfQiwgYikNCiAgY19CQV9CXzEgPC0gY19CICUqJSBBX0JfMQ0KICBjX0JBX0JfMUFfTl9jX04gPC0gY19CQV9CXzElKiVBX04gLSBjX04NCiAgeCA8LSByZXAoMCwgbmNvbChBKSkNCiAgeFtYX0JdIDwtIEFfQl8xYg0KICB6IDwtIGNfQkFfQl8xICUqJSBiDQogIG9wdCA8LSBhbGwoY19CQV9CXzFBX05fY19OID49IDApICNhbGwoYyhjX0JCXzEsIGNfQkJfMUFfYykgPj0gMCkNCiAgcHJpbnQubWF0cmljZXMoQSwgYiwgYywgWF9CLCBYX04sIEFfQiwgQV9OLCBjX0IsIGNfTiwgeCwgeiwgY19CQV9CXzFBX05fY19OLCBvcHQpDQoNCiAgcmVzIDwtIGxpc3QoKQ0KICBtIDwtIG1pbihjX0JBX0JfMUFfTl9jX04pDQogIGlmIChtIDwgMCkgew0KICAgIGVfaXggPC0gWF9OW3doaWNoLm1pbihjX0JBX0JfMUFfTl9jX04pXQ0KICAgIEFfQl8xQV9lIDwtIEFfQl8xICUqJSBBWyxlX2l4XQ0KICAgIG0gPC0gSW5mDQogICAgbF9peCA8LSAtMQ0KICAgIGZvciAoaSBpbiAxOmxlbmd0aChBX0JfMWIpKSB7DQogICAgICAgaWYgKChBX0JfMUFfZVtpXSA+IDApICYgKEFfQl8xYltpXSAvIEFfQl8xQV9lW2ldIDwgbSkpIHsNCiAgICAgICAgIG0gPC0gQV9CXzFiW2ldIC8gQV9CXzFBX2VbaV0NCiAgICAgICAgIGxfaXggPC0gaQ0KICAgICAgfQ0KICAgIH0NCiAgICBpZiAobF9peCA8IDApIHsNCiAgICAgIHJlcyR1bmJvdW5kZWQgPC0gVFJVRQ0KICAgICAgcmV0dXJuKHJlcykNCiAgICB9DQogICAgcHJpbnQoZGF0YS5mcmFtZShzdGVwPXBhc3RlMCgneCcsIGVfaXgsICcgZW50ZXJzIHRoZSBiYXNpcycpKSAlPiUga2FibGUoKSkNCiAgICBwcmludChBX0JfMUFfZSAlPiUgIGthYmxlKGNhcHRpb249cGFzdGUwKCckJEFfQl57LTF9QSQkJywgZV9peCkpKQ0KICAgIHByaW50KEFfQl8xYiAlPiUgIGthYmxlKGNhcHRpb249JyQkQV9CXnstMX1iJCQnKSkNCiAgICByIDwtIEFfQl8xYiAvIEFfQl8xQV9lDQogICAgZGltbmFtZXMocikgPC0gbGlzdChwYXN0ZTAoJ3gnLCBYX0IpLCBOVUxMKQ0KICAgIHByaW50KHIgJT4lIGthYmxlKGNhcHRpb249JyoqcmF0aW8qKicpKQ0KICAgIHByaW50KGRhdGEuZnJhbWUoc3RlcD1wYXN0ZTAoJ3gnLCBYX0JbbF9peF0sICcgbGVhdmVzIHRoZSBiYXNpcycpKSAlPiUga2FibGUoKSkNCiAgICBYX0JbbF9peF0gPC0gZV9peA0KICB9IGVsc2Ugew0KICAgIHJlcyRvcHQgPC0gVFJVRQ0KICB9DQogIHJlcyRYX0IgPC0gWF9CICAjIHZhcnMNCiAgcmVzJHggPC0geCAgICAgIyB2YWx1ZXMNCiAgcmVzJHogPC0geiAgICAjIG9iamVjdGl2ZQ0KICByZXMkb3B0IDwtIG9wdA0KICByZXMkdW5ib3VuZGVkIDwtIEZBTFNFDQogIHJldHVybihyZXMpDQp9DQpgYGANCg0KIyMgRXhhbXBsZXMNCg0KTm93LCBsZXQncyBjb25zaWRlciBhIGZldyBleGFtcGxlcyB0byBkZW1vbnN0cmF0ZSB0aGUgYWJvdmUgYWxnb3JpdGhtIGltcGxlbWVudGF0aW9uIHN0ZXAgYnkgc3RlcC4NCg0KIyMjIEV4YW1wbGUgMQ0KDQohW10ob3B0MS5wbmcpDQoNClVzaW5nIHRoZSBhYm92ZSB0aGVvcnksIGhlcmUncyBob3cgdGhlIHNpbXBsZXggbWV0aG9kIHdvcmtzLCBzdGFydGluZyBmcm9tIHRoZSBiYXNpcyAkJFhfQj0oeF8xLHhfNCx4XzUpJCQ6DQoNCiFbXShvcHQxXzEucG5nKQ0KIVtdKG9wdDFfMi5wbmcpDQohW10ob3B0MV8zLnBuZykNCg0KTm93LCBsZXQncyBvYnRhaW4gdGhlIHNhbWUgcmVzdWx0IHVzaW5nIHRoZSBpbXBsZW1lbnRhdGlvbiBhYm92ZToNCg0KYGBge3IgbWVzc2FnZT1GQUxTRX0NCkEgPC0gbWF0cml4KGMoMiwyLDAsLTEsMSwxKSwgbmNvbD0yKSANCm0gPC0gbnJvdyhBKSANCm4gPC0gbmNvbChBKSANCmIgPC0gbWF0cml4KGMoNCw4LDMpLCBuY29sPTEpIA0KYyA8LSBjKDEsMCkNCg0KQSA8LSBjYmluZChBLCBkaWFnKG0pKSAjIGFkZCBzbGFjayBjb2x1bW5zDQpjIDwtIGMoYywgcmVwKDAsIG0pKQ0KDQpYX0IgPC0gYygxLDQsNSkNCm9wdCA8LSBGQUxTRQ0KaSA8LSAxDQp3aGlsZSAoIW9wdCkgeyANCiAgcHJpbnQoZGF0YS5mcmFtZShJdGVyYXRpb249aSkgJT4lIGthYmxlKCkpIA0KICByZXMgPC0gc2ltcGxleC5tYXRyaXguaXRlcihBLCBiLCBjLCBYX0IpIA0KICBYX0IgPC0gcmVzJFhfQiANCiAgb3B0IDwtIHJlcyRvcHQNCiAgI3ByaW50KHJlcykNCiAgaSA8LSBpICsgMQ0KfQ0KYGBgDQoNCiMjIyBFeGFtcGxlIDINCg0KVGhpcyB0aW1lIGxldCdzIHN0YXJ0IHdpdGggJCRYX0I9KHhfMyx4XzQpJCQsIGkuZS4gdGhlICoqc2xhY2sgdmFyaWFibGVzKiogJCQoc18xLHNfMikkJCBhcyB0aGUgYmFzaWMgdmFyaWFibGVzLg0KDQohW10ob3B0Mi5wbmcpDQoNCk5vdywgbGV0cyBkZW1vbnN0cmF0ZSB3aXRoIG91ciBpbXBsZW1lbnRhdGlvbiB3aXRoIHRoZSBzaW1wbGV4IG1hdHJpeCBtZXRob2Q6DQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0V9DQpBIDwtIG1hdHJpeChjKDEsMSwxLDIpLCBuY29sPTIpIA0KbSA8LSBucm93KEEpIA0KbiA8LSBuY29sKEEpIA0KYiA8LSBtYXRyaXgoYyg0LDYpLCBuY29sPTEpIA0KYyA8LSBjKDIsMykNCg0KQSA8LSBjYmluZChBLCBkaWFnKG0pKSAjIGFkZCBzbGFjayBjb2x1bW5zDQpjIDwtIGMoYywgcmVwKDAsIG0pKQ0KDQpYX0IgPC0gKG4rMSk6KG4rbSkNCm9wdCA8LSBGQUxTRQ0KaSA8LSAxDQp3aGlsZSAoIW9wdCkgeyANCiAgcHJpbnQoZGF0YS5mcmFtZShJdGVyYXRpb249aSkgJT4lIGthYmxlKCkpIA0KICByZXMgPC0gc2ltcGxleC5tYXRyaXguaXRlcihBLCBiLCBjLCBYX0IpIA0KICBYX0IgPC0gcmVzJFhfQiANCiAgb3B0IDwtIHJlcyRvcHQNCiAgI3ByaW50KHJlcykNCiAgaSA8LSBpICsgMQ0KfQ0KYGBgDQoNClZlcmlmeSB0aGUgc29sdGlvbiBvYnRhaW5lZCB3aXRoICoqUioqIHBhY2thZ2UncyBpbXBsZW1lbnRhdGlvbjoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkobHBTb2x2ZSkNCm9iamVjdGl2ZS5mbiA8LSBjDQpjb25zdC5tYXQgPC0gQQ0KY29uc3QuZGlyIDwtIHJlcCgiPD0iLCBucm93KEEpKQ0KY29uc3QucmhzIDwtIGINCmxwLnNvbHV0aW9uIDwtIGxwKCJtYXgiLCBvYmplY3RpdmUuZm4sIGNvbnN0Lm1hdCwgDQogICAgICAgICAgICAgICAgICBjb25zdC5kaXIsIGNvbnN0LnJocywgY29tcHV0ZS5zZW5zPVRSVUUpDQojbHAuc29sdXRpb24kb2JqZWN0aXZlDQpwcmludChscC5zb2x1dGlvbiRzb2x1dGlvbikNCnByaW50KGxwLnNvbHV0aW9uJG9ianZhbCkNCmBgYA0KIyMjIEV4YW1wbGUgMw0KDQohW10ob3B0My5wbmcpDQoNCkFnYWluLCBsZXQncyBzdGFydCB3aXRoICQkWF9CPSh4XzMseF80LHhfNSkkJCwgaS5lLiB0aGUgKipzbGFjayB2YXJpYWJsZXMqKiBhcyB0aGUgYmFzaWMgdmFyaWFibGVzLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFfQ0KQSA8LSBtYXRyaXgoYygtMSwtMSwzLDEsMiwxKSwgbmNvbD0yKSANCm0gPC0gbnJvdyhBKSANCm4gPC0gbmNvbChBKSANCmIgPC0gbWF0cml4KGMoMyw4LDE4KSwgbmNvbD0xKSANCmMgPC0gYygxLDMpDQoNCkEgPC0gY2JpbmQoQSwgZGlhZyhtKSkgIyBhZGQgc2xhY2sgY29sdW1ucw0KYyA8LSBjKGMsIHJlcCgwLCBtKSkNCg0KWF9CIDwtIChuKzEpOihuK20pDQpvcHQgPC0gRkFMU0UNCmkgPC0gMQ0Kd2hpbGUgKCFvcHQpIHsgDQogIHByaW50KGRhdGEuZnJhbWUoSXRlcmF0aW9uPWkpICU+JSBrYWJsZSgpKSANCiAgcmVzIDwtIHNpbXBsZXgubWF0cml4Lml0ZXIoQSwgYiwgYywgWF9CKSANCiAgWF9CIDwtIHJlcyRYX0IgDQogIG9wdCA8LSByZXMkb3B0DQogICNwcmludChyZXMpDQogIGkgPC0gaSArIDENCn0NCmBgYA0KDQpMZXQncyBjb21wYXJlIHRoZSBhYm92ZSBzb2x1dGlvbiBvYnRhaW5lZCB1c2luZyB0aGUgZnVuY3Rpb24gYGxwKClgIGZyb20gKipSKiogcGFja2FnZSBgbHBTb2x2ZWAuIE5vdGUgdGhhdCBleGFjdGx5IHNhbWUgc29sdXRpb24gaXMgb2J0YWluZWQuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KGxwU29sdmUpDQpvYmplY3RpdmUuZm4gPC0gYw0KY29uc3QubWF0IDwtIEENCmNvbnN0LmRpciA8LSByZXAoIjw9IiwgbnJvdyhBKSkNCmNvbnN0LnJocyA8LSBiDQpscC5zb2x1dGlvbiA8LSBscCgibWF4Iiwgb2JqZWN0aXZlLmZuLCBjb25zdC5tYXQsIA0KICAgICAgICAgICAgICAgICAgY29uc3QuZGlyLCBjb25zdC5yaHMsIGNvbXB1dGUuc2Vucz1UUlVFKQ0KcHJpbnQobHAuc29sdXRpb24kc29sdXRpb24pDQpwcmludChscC5zb2x1dGlvbiRvYmp2YWwpDQpgYGANCg0KIyMjIEV4YW1wbGUgNA0KDQohW10ob3B0NC5wbmcpDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0V9DQpBIDwtIG1hdHJpeChjKDEsMiwwLDAsMiwxLDMsMCw1LDEsLTMsMiksIG5yb3c9MykgDQptIDwtIG5yb3coQSkgDQpuIDwtIG5jb2woQSkgDQpiIDwtIG1hdHJpeChjKDE1LDE4LDIwKSwgbmNvbD0xKSANCmMgPC0gYyg0LDMsMiwzKSANCg0KQSA8LSBjYmluZChBLCBkaWFnKG0pKSAjIGFkZCBzbGFjayBjb2x1bW5zDQpjIDwtIGMoYywgcmVwKDAsIG0pKQ0KWF9CIDwtIChuKzEpOihuK20pDQoNCm9wdCA8LSBGQUxTRQ0KaSA8LSAxDQp3aGlsZSAoIW9wdCkgeyANCiAgcHJpbnQoZGF0YS5mcmFtZShJdGVyYXRpb249aSkgJT4lIGthYmxlKCkpIA0KICByZXMgPC0gc2ltcGxleC5tYXRyaXguaXRlcihBLCBiLCBjLCBYX0IpIA0KICBYX0IgPC0gcmVzJFhfQiANCiAgb3B0IDwtIHJlcyRvcHQNCiAgI3ByaW50KHJlcykNCiAgaSA8LSBpICsgMQ0KfQ0KYGBgDQoNClZlcmlmaWNhdGlvbiB3aXRoIGBscFNvbHZlYDoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkobHBTb2x2ZSkNCm9iamVjdGl2ZS5mbiA8LSBjDQpjb25zdC5tYXQgPC0gQQ0KY29uc3QuZGlyIDwtIHJlcCgiPD0iLCBucm93KEEpKQ0KY29uc3QucmhzIDwtIGINCmxwLnNvbHV0aW9uIDwtIGxwKCJtYXgiLCBvYmplY3RpdmUuZm4sIGNvbnN0Lm1hdCwgDQogICAgICAgICAgICAgICAgICBjb25zdC5kaXIsIGNvbnN0LnJocywgY29tcHV0ZS5zZW5zPVRSVUUpDQojbHAuc29sdXRpb24kb2JqZWN0aXZlDQpwcmludChscC5zb2x1dGlvbiRzb2x1dGlvbikNCnByaW50KGxwLnNvbHV0aW9uJG9ianZhbCkNCmBgYA0KDQojIyMgRXhhbXBsZSA1DQoNCiFbXShvcHQ1LnBuZykNCg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFfQ0KQSA8LSBtYXRyaXgoYygyLDEsMiwxLDIsMiwxLDMsMSksIG5yb3c9MykgDQptIDwtIG5yb3coQSkgDQpuIDwtIG5jb2woQSkgDQpiIDwtIG1hdHJpeChjKDIsNSw2KSwgbmNvbD0xKSANCmMgPC0gYygzLDEsMykgDQoNCkEgPC0gY2JpbmQoQSwgZGlhZyhtKSkgIyBhZGQgc2xhY2sgY29sdW1ucw0KYyA8LSBjKGMsIHJlcCgwLCBtKSkNClhfQiA8LSAobisxKToobittKQ0KDQpvcHQgPC0gRkFMU0UNCmkgPC0gMQ0Kd2hpbGUgKCFvcHQpIHsgDQogIHByaW50KGRhdGEuZnJhbWUoSXRlcmF0aW9uPWkpICU+JSBrYWJsZSgpKSANCiAgcmVzIDwtIHNpbXBsZXgubWF0cml4Lml0ZXIoQSwgYiwgYywgWF9CKSANCiAgWF9CIDwtIHJlcyRYX0IgDQogIG9wdCA8LSByZXMkb3B0DQogICNwcmludChyZXMpDQogIGkgPC0gaSArIDENCn0NCmBgYA0KDQpWZXJpZmljYXRpb24gd2l0aCBgbHBTb2x2ZWA6DQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KGxwU29sdmUpDQpvYmplY3RpdmUuZm4gPC0gYw0KY29uc3QubWF0IDwtIEENCmNvbnN0LmRpciA8LSByZXAoIjw9IiwgbnJvdyhBKSkNCmNvbnN0LnJocyA8LSBiDQpscC5zb2x1dGlvbiA8LSBscCgibWF4Iiwgb2JqZWN0aXZlLmZuLCBjb25zdC5tYXQsIA0KICAgICAgICAgICAgICAgICAgY29uc3QuZGlyLCBjb25zdC5yaHMsIGNvbXB1dGUuc2Vucz1UUlVFKQ0KI2xwLnNvbHV0aW9uJG9iamVjdGl2ZQ0KcHJpbnQobHAuc29sdXRpb24kc29sdXRpb24pDQpwcmludChscC5zb2x1dGlvbiRvYmp2YWwpDQpgYGA=