#!/usr/bin/octave -q
# http://people.ofset.org/~ckhung/b/ma/lademo.m

1;

function rv = pivot(A, r, c, varargin)
# Pivots matrix A around the element A(r,c), but only operates
# on the rows specified by range. If range is an illegal index
# (such as inf, nan, or 0), then operates on the entire matrix.
    s = size(A);
    # see http://wiki.octave.org/wiki.pl?VariableLengthArgumentLists
    if (nargin > 3)
	range = varargin{1};
    else
	range = [1:r-1,r+1:s(1)];
    endif
    A(r,:) = A(r,:) / A(r,c);
    for i = range
	if (i == r); error("pivot row must not occur in range"); endif
	A(i,:) = A(i,:) - A(r,:) * A(i,c);
    endfor;
    rv = A;
endfunction

global epsilon;
epsilon = 1e-5;

function rv = r_r_echelon(A)
# Computes reduced row echelon form of A
    global epsilon;

    s = size(A);
    r = 1;
    for c = 1:s(2)
	p = 0;
	for i = r:s(1)
	    if (abs(A(i,c)) > epsilon); p = i; break; endif
	endfor
	if (p <= 0); continue; endif
	A([r,p],:) = A([p,r],:);
	A = pivot(A, r, c, r+1:s(1));
	r = r + 1;
    endfor
    for r = r-1:-1:2
	p = 0;
	for i = 1:s(2)
	    if (abs(A(r,i)) > epsilon); p = i; break; endif
	endfor
	if (p <= 0); continue; endif
	A = pivot(A, r, p, 1:r-1);
    endfor
    rv = A;
endfunction

function rv = rand_permute(v)
# Returns a random permutation of the vector v (or of [1,2,...v] if
# v is a scalar) Can be useful too, if you need random combinations.
# (Just pick the first k elements)
    s = size(v);
    n = s(1) * s(2)
    if (n == 1); n = v; v = 1:v; endif
    for k = n:-1:2
	t = floor(rand() * k) + 1;
	v([t,k]) = v([k,t]);
    endfor
    rv = v;
endfunction

function rv = rand_mat(m, n, r)
# Returns a random matrix of size m*n and of rank r
    if (m > n); error("matrix width must be >= matrix height"); endif
    A = zeros(m,n);
    p = rand_permute(n);
    d = [sort(p(1:r)), inf];
    i = 1;
    for j = 1:n
	if (j == d(i))
	    A(i,j) = 1;
	    ++i;
	else
	    A(1:i-1,j) = rand(i-1,1)*2 - 1;
	endif
    endfor
    rv = (rand(m,m)*2-1) * A;
endfunction

function rv = simplex(obj, A)
    global epsilon;
    T = [A; -obj];
    s = size(T);
    T = [T(:,1:s(2)-1), eye(s(1),s(1)), T(:,s(2))];
    s = size(T);
    nr = s(1) - 1;
    nc = s(2) - 1;
    while (1)
	bad = mini(T(1:nr,nc+1));
	if (T(bad,nc+1) >= 0); break; endif
	pc = mini(T(bad,1:nc));
	if (T(bad,pc) >= 0)
	    T
	    error("no solution");
	endif
	ratio = T(1:nr,nc+1) ./ T(1:nr,pc);
	pr = mini(ratio + (ratio <= 0) / epsilon);
	T = pivot(T, pr, pc);
    endwhile
    while (1)
	pc = mini(T(nr+1,1:nc));
	if (T(nr+1,pc) >= 0); break; endif
	ratio = T(1:nr,nc+1) ./ T(1:nr,pc);
	bad = T(1:nr,pc)<=0;
	pr = mini(ratio + bad / epsilon);
	if (bad(pr)); break; endif
	T = pivot(T, pr, pc);
    endwhile
    rv = T;
endfunction

function rv = mini(c)
    rv = 1;
    for i = 2:prod(size(c))
	if (c(i) < c(rv))
	    rv = i;
	endif
    endfor
endfunction

# test
rand("seed", 7);
T = rand_mat(5,6,4) * 10
pivot(T,3,4,[2,5])
A = r_r_echelon(T)

simplex([-2,-3,-375], [1,0,30; 0,1,25; -1,-1,-15;1,1,45])
simplex([13,4,0], [-2,-1,-11; 1,1,10; 1/3,1,6; -1/4,-1,-4])

