Sandipan Dey

Introduction

The goal of this article is to explain, implement and demonstrate how the popular simplex algorithm for solving a linear program can be revised to form a matrix-based algorithm (instead of tableau-based), so that the algorithm is known as revised simplex algorithm. This algorithm has been taught in the coursera course Operations Research Theory by the National Taiwan University. The theory part will be described as is from the course itself, along with an attempt to implement the matrix-based simplex method with R (from scratch) and demonstrate with examples.

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
}
Iteration
1
A
x1 x2 x3 x4 x5
2 -1 1 0 0
2 1 0 1 0
0 1 0 0 1
b
4
8
3
cT
x1 x2 x3 x4 x5
1 0 0 0 0
XB
x1 x4 x5
1 4 5
XN
x2 x3
2 3
cBT
x1 x4 x5
1 0 0
cNT
x2 x3
0 0
AB
x1 x4 x5
2 0 0
2 1 0
0 0 1
AN
x2 x3
-1 1
1 0
1 0
x (solution)
x1 x2 x3 x4 x5
2 0 0 4 3
z (objective value)
2
Optimal
x
FALSE
reduced cost = cBTAB−1AN − cNT
x2 x3
-0.5 0.5
step
x2 enters the basis
AB−1A2
-0.5
2.0
1.0
AB−1b
2
4
3
ratio
x1 -4
x4 2
x5 3
step
x4 leaves the basis
Iteration
2
A
x1 x2 x3 x4 x5
2 -1 1 0 0
2 1 0 1 0
0 1 0 0 1
b
4
8
3
cT
x1 x2 x3 x4 x5
1 0 0 0 0
XB
x1 x2 x5
1 2 5
XN
x3 x4
3 4
cBT
x1 x2 x5
1 0 0
cNT
x3 x4
0 0
AB
x1 x2 x5
2 -1 0
2 1 0
0 1 1
AN
x3 x4
1 0
0 1
0 0
x (solution)
x1 x2 x3 x4 x5
3 2 0 0 1
z (objective value)
3
Optimal
x
TRUE
reduced cost = cBTAB−1AN − cNT
x3 x4
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
}
Iteration
1
A
x1 x2 x3 x4
1 1 1 0
1 2 0 1
b
4
6
cT
x1 x2 x3 x4
2 3 0 0
XB
x3 x4
3 4
XN
x1 x2
1 2
cBT
x3 x4
0 0
cNT
x1 x2
2 3
AB
x3 x4
1 0
0 1
AN
x1 x2
1 1
1 2
x (solution)
x1 x2 x3 x4
0 0 4 6
z (objective value)
0
Optimal
x
FALSE
reduced cost = cBTAB−1AN − cNT
x1 x2
-2 -3
step
x2 enters the basis
AB−1A2
1
2
AB−1b
4
6
ratio
x3 4
x4 3
step
x4 leaves the basis
Iteration
2
A
x1 x2 x3 x4
1 1 1 0
1 2 0 1
b
4
6
cT
x1 x2 x3 x4
2 3 0 0
XB
x3 x2
3 2
XN
x1 x4
1 4
cBT
x3 x2
0 3
cNT
x1 x4
2 0
AB
x3 x2
1 1
0 2
AN
x1 x4
1 0
1 1
x (solution)
x1 x2 x3 x4
0 3 1 0
z (objective value)
9
Optimal
x
FALSE
reduced cost = cBTAB−1AN − cNT
x1 x4
-0.5 1.5
step
x1 enters the basis
AB−1A1
0.5
0.5
AB−1b
1
3
ratio
x3 2
x2 6
step
x3 leaves the basis
Iteration
3
A
x1 x2 x3 x4
1 1 1 0
1 2 0 1
b
4
6
cT
x1 x2 x3 x4
2 3 0 0
XB
x1 x2
1 2
XN
x3 x4
3 4
cBT
x1 x2
2 3
cNT
x3 x4
0 0
AB
x1 x2
1 1
1 2
AN
x3 x4
1 0
0 1
x (solution)
x1 x2 x3 x4
2 2 0 0
z (objective value)
10
Optimal
x
TRUE
reduced cost = cBTAB−1AN − cNT
x3 x4
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
}
Iteration
1
A
x1 x2 x3 x4 x5
-1 1 1 0 0
-1 2 0 1 0
3 1 0 0 1
b
3
8
18
cT
x1 x2 x3 x4 x5
1 3 0 0 0
XB
x3 x4 x5
3 4 5
XN
x1 x2
1 2
cBT
x3 x4 x5
0 0 0
cNT
x1 x2
1 3
AB
x3 x4 x5
1 0 0
0 1 0
0 0 1
AN
x1 x2
-1 1
-1 2
3 1
x (solution)
x1 x2 x3 x4 x5
0 0 3 8 18
z (objective value)
0
Optimal
x
FALSE
reduced cost = cBTAB−1AN − cNT
x1 x2
-1 -3
step
x2 enters the basis
AB−1A2
1
2
1
AB−1b
3
8
18
ratio
x3 3
x4 4
x5 18
step
x3 leaves the basis
Iteration
2
A
x1 x2 x3 x4 x5
-1 1 1 0 0
-1 2 0 1 0
3 1 0 0 1
b
3
8
18
cT
x1 x2 x3 x4 x5
1 3 0 0 0
XB
x2 x4 x5
2 4 5
XN
x1 x3
1 3
cBT
x2 x4 x5
3 0 0
cNT
x1 x3
1 0
AB
x2 x4 x5
1 0 0
2 1 0
1 0 1
AN
x1 x3
-1 1
-1 0
3 0
x (solution)
x1 x2 x3 x4 x5
0 3 0 2 15
z (objective value)
9
Optimal
x
FALSE
reduced cost = cBTAB−1AN − cNT
x1 x3
-4 3
step
x1 enters the basis
AB−1A1
-1
1
4
AB−1b
3
2
15
ratio
x2 -3.00
x4 2.00
x5 3.75
step
x4 leaves the basis
Iteration
3
A
x1 x2 x3 x4 x5
-1 1 1 0 0
-1 2 0 1 0
3 1 0 0 1
b
3
8
18
cT
x1 x2 x3 x4 x5
1 3 0 0 0
XB
x2 x1 x5
2 1 5
XN
x3 x4
3 4
cBT
x2 x1 x5
3 1 0
cNT
x3 x4
0 0
AB
x2 x1 x5
1 -1 0
2 -1 0
1 3 1
AN
x3 x4
1 0
0 1
0 0
x (solution)
x1 x2 x3 x4 x5
2 5 0 0 7
z (objective value)
17
Optimal
x
FALSE
reduced cost = cBTAB−1AN − cNT
x3 x4
-5 4
step
x3 enters the basis
AB−1A3
-1
-2
7
AB−1b
5
2
7
ratio
x2 -5
x1 -1
x5 1
step
x5 leaves the basis
Iteration
4
A
x1 x2 x3 x4 x5
-1 1 1 0 0
-1 2 0 1 0
3 1 0 0 1
b
3
8
18
cT
x1 x2 x3 x4 x5
1 3 0 0 0
XB
x2 x1 x3
2 1 3
XN
x4 x5
4 5
cBT
x2 x1 x3
3 1 0
cNT
x4 x5
0 0
AB
x2 x1 x3
1 -1 1
2 -1 0
1 3 0
AN
x4 x5
0 0
1 0
0 1
x (solution)
x1 x2 x3 x4 x5
4 6 1 0 0
z (objective value)
22
Optimal
x
TRUE
reduced cost = cBTAB−1AN − cNT
x4 x5
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
}
Iteration
1
A
x1 x2 x3 x4 x5 x6 x7
1 0 3 1 1 0 0
2 2 0 -3 0 1 0
0 1 5 2 0 0 1
b
15
18
20
cT
x1 x2 x3 x4 x5 x6 x7
4 3 2 3 0 0 0
XB
x5 x6 x7
5 6 7
XN
x1 x2 x3 x4
1 2 3 4
cBT
x5 x6 x7
0 0 0
cNT
x1 x2 x3 x4
4 3 2 3
AB
x5 x6 x7
1 0 0
0 1 0
0 0 1
AN
x1 x2 x3 x4
1 0 3 1
2 2 0 -3
0 1 5 2
x (solution)
x1 x2 x3 x4 x5 x6 x7
0 0 0 0 15 18 20
z (objective value)
0
Optimal
x
FALSE
reduced cost = cBTAB−1AN − cNT
x1 x2 x3 x4
-4 -3 -2 -3
step
x1 enters the basis
AB−1A1
1
2
0
AB−1b
15
18
20
ratio
x5 15
x6 9
x7 Inf
step
x6 leaves the basis
Iteration
2
A
x1 x2 x3 x4 x5 x6 x7
1 0 3 1 1 0 0
2 2 0 -3 0 1 0
0 1 5 2 0 0 1
b
15
18
20
cT
x1 x2 x3 x4 x5 x6 x7
4 3 2 3 0 0 0
XB
x5 x1 x7
5 1 7
XN
x2 x3 x4 x6
2 3 4 6
cBT
x5 x1 x7
0 4 0
cNT
x2 x3 x4 x6
3 2 3 0
AB
x5 x1 x7
1 1 0
0 2 0
0 0 1
AN
x2 x3 x4 x6
0 3 1 0
2 0 -3 1
1 5 2 0
x (solution)
x1 x2 x3 x4 x5 x6 x7
9 0 0 0 6 0 20
z (objective value)
36
Optimal
x
FALSE
reduced cost = cBTAB−1AN − cNT
x2 x3 x4 x6
1 -2 -9 2
step
x4 enters the basis
AB−1A4
2.5
-1.5
2.0
AB−1b
6
9
20
ratio
x5 2.4
x1 -6.0
x7 10.0
step
x5 leaves the basis
Iteration
3
A
x1 x2 x3 x4 x5 x6 x7
1 0 3 1 1 0 0
2 2 0 -3 0 1 0
0 1 5 2 0 0 1
b
15
18
20
cT
x1 x2 x3 x4 x5 x6 x7
4 3 2 3 0 0 0
XB
x4 x1 x7
4 1 7
XN
x2 x3 x5 x6
2 3 5 6
cBT
x4 x1 x7
3 4 0
cNT
x2 x3 x5 x6
3 2 0 0
AB
x4 x1 x7
1 1 0
-3 2 0
2 0 1
AN
x2 x3 x5 x6
0 3 1 0
2 0 0 1
1 5 0 0
x (solution)
x1 x2 x3 x4 x5 x6 x7
12.6 0 0 2.4 0 0 15.2
z (objective value)
57.6
Optimal
x
FALSE
reduced cost = cBTAB−1AN − cNT
x2 x3 x5 x6
-2.6 8.8 3.6 0.2
step
x2 enters the basis
AB−1A2
-0.4
0.4
1.8
AB−1b
2.4
12.6
15.2
ratio
x4 -6.000000
x1 31.500000
x7 8.444444
step
x7 leaves the basis
Iteration
4
A
x1 x2 x3 x4 x5 x6 x7
1 0 3 1 1 0 0
2 2 0 -3 0 1 0
0 1 5 2 0 0 1
b
15
18
20
cT
x1 x2 x3 x4 x5 x6 x7
4 3 2 3 0 0 0
XB
x4 x1 x2
4 1 2
XN
x3 x5 x6 x7
3 5 6 7
cBT
x4 x1 x2
3 4 3
cNT
x3 x5 x6 x7
2 0 0 0
AB
x4 x1 x2
1 1 0
-3 2 2
2 0 1
AN
x3 x5 x6 x7
3 1 0 0
0 0 1 0
5 0 0 1
x (solution)
x1 x2 x3 x4 x5 x6 x7
9.222222 8.444444 0 5.777778 0 0 0
z (objective value)
79.55556
Optimal
x
TRUE
reduced cost = cBTAB−1AN − cNT
x3 x5 x6 x7
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
}
Iteration
1
A
x1 x2 x3 x4 x5 x6
2 1 1 1 0 0
1 2 3 0 1 0
2 2 1 0 0 1
b
2
5
6
cT
x1 x2 x3 x4 x5 x6
3 1 3 0 0 0
XB
x4 x5 x6
4 5 6
XN
x1 x2 x3
1 2 3
cBT
x4 x5 x6
0 0 0
cNT
x1 x2 x3
3 1 3
AB
x4 x5 x6
1 0 0
0 1 0
0 0 1
AN
x1 x2 x3
2 1 1
1 2 3
2 2 1
x (solution)
x1 x2 x3 x4 x5 x6
0 0 0 2 5 6
z (objective value)
0
Optimal
x
FALSE
reduced cost = cBTAB−1AN − cNT
x1 x2 x3
-3 -1 -3
step
x1 enters the basis
AB−1A1
2
1
2
AB−1b
2
5
6
ratio
x4 1
x5 5
x6 3
step
x4 leaves the basis
Iteration
2
A
x1 x2 x3 x4 x5 x6
2 1 1 1 0 0
1 2 3 0 1 0
2 2 1 0 0 1
b
2
5
6
cT
x1 x2 x3 x4 x5 x6
3 1 3 0 0 0
XB
x1 x5 x6
1 5 6
XN
x2 x3 x4
2 3 4
cBT
x1 x5 x6
3 0 0
cNT
x2 x3 x4
1 3 0
AB
x1 x5 x6
2 0 0
1 1 0
2 0 1
AN
x2 x3 x4
1 1 1
2 3 0
2 1 0
x (solution)
x1 x2 x3 x4 x5 x6
1 0 0 0 4 4
z (objective value)
3
Optimal
x
FALSE
reduced cost = cBTAB−1AN − cNT
x2 x3 x4
0.5 -1.5 1.5
step
x3 enters the basis
AB−1A3
0.5
2.5
0.0
AB−1b
1
4
4
ratio
x1 2.0
x5 1.6
x6 Inf
step
x5 leaves the basis
Iteration
3
A
x1 x2 x3 x4 x5 x6
2 1 1 1 0 0
1 2 3 0 1 0
2 2 1 0 0 1
b
2
5
6
cT
x1 x2 x3 x4 x5 x6
3 1 3 0 0 0
XB
x1 x3 x6
1 3 6
XN
x2 x4 x5
2 4 5
cBT
x1 x3 x6
3 3 0
cNT
x2 x4 x5
1 0 0
AB
x1 x3 x6
2 1 0
1 3 0
2 1 1
AN
x2 x4 x5
1 1 0
2 0 1
2 0 0
x (solution)
x1 x2 x3 x4 x5 x6
0.2 0 1.6 0 0 4
z (objective value)
5.4
Optimal
x
TRUE
reduced cost = cBTAB−1AN − cNT
x2 x4 x5
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=