function [x,zmin,xuyvz] = quad_prog(G,c,A,b)
% usage: [xopt,zmin,xuyvz] = quad_prog(G,c,A,b)
% description: This code solves the quadratic program
%                 Min W = x'*G*x - c'*x
%                 Sbj
%                         A*x >= b, x >= 0.
% It assumes that A is mxn and that G is nxn,
% c is nx1 and b is mx1. This program calculates
% an approximate solution using a modified
% simplex method applied to the KKT conditions.
% Some theoretical conditions that ensure its validity
% are that G is positive semi-definite and either c is
% zero or the objective function is strictly convex,
% e.g., G is symmetric positive definite.

% First step is to convert it into a maximization problem:
%                Max Z = c'*x - x'*G*x
%                Sbj
%                    -A*x <= -b, x >= 0.
% The KKT conditions for this problem are computed to be
%              -(G+G')*x + A'*u + y = c
%                           A*x - v = b
%                       x'*y + u'*v = 0
%                       x, y, u, v >= 0.
% This system is solved by a modified simplex method that
% preserves the (non-linear) complementarity equation. No
% attempt to account for multiple solutions is made.

% programmer: t. shores      last rev: 2/24/07

% set some constants ... adjust these if tuning needed
tol = 1e2*eps; % approximate zero
maxiter = 100;
% some minimal bullet-proofing
[m,n] = size(A);
mpn = m+n;
mpnp1 = m+n+1;
if ( nargin < 4)
  error('quad_prog: arguments G,d,A,b are required')
end
if (size(b) ~= [m,1])
  error('quad_prog: b must be mx1, where A is mxn');
end
if (size(G) ~= [n,n])
  error('quad_prog: G must be nxn, where A is mxn');
end
if (size(c) ~= [n,1])
  error('quad_prog: d must be nx1, where A is mxn');
end
% set up the tableau with complementaries x,u,y,v
T = [-(G+G'),A',eye(n),zeros(n,m),c;A, zeros(m,mpn),-eye(m),b]
% determine basic variables
basicrows = [zeros(1,n),zeros(1,m),(c'>=0),(b'<=0)]
% make rhs nonnegative
T = diag([(c==0)+sign(c);(b==0)+sign(b)])*T;
% insert the artificial variables
if ((nav = sum(basicrows==1))< mpn)
  Id = eye(mpn);
  T = [T(:,1:2*mpn),Id(:,find(diag(T(1:mpn,mpnp1:2*mpn))==-1)),T(:,2*mpn+1)]
  basicrows = [basicrows,ones(1,mpn-nav)]
end
nav = mpn-nav
nend = 2*mpn+nav
% append objective function equation for solving system
T,basicrows

T = [T;basicrows,0]
basicrows(find(basicrows == 1))=1:mpn;
% in best of all possible worlds, we would balance,
% pivot, etc., but here's a cheap fix
tol = tol*norm(T,inf);
% initialize artificial variables to basic
T(mpn+1,:) = T(mpnp1,:)-sum(T(1:mpn,:));
% main loop
for ii = 1:maxiter
  % find elegible column
  complement = [basicrows(mpn+1:2*mpn),basicrows(1:mpn),zeros(1,nav)];
T(mpnp1,1:nend).*~complement;
  [val,colndx] = min(T(mpnp1,1:nend).*~complement);
  if (val >= -tol) % all done?
    xuyvz = zeros(nend,1); %yup
    tmpndx = find(basicrows ~= 0);
    xuyvz(tmpndx) = T(basicrows(tmpndx),nend+1);
    x = xuyvz(1:n);
    zmin =  x'*G*x -c'*x;
    % if solution exists, objective value should be 0
    if (abs(T(mpnp1,nend+1))>tol)
      disp('Warning: this problem may not have a solution.')
    end
    return;
  end
  x = T(basicrows(find(basicrows ~= 0)),nend+1);
  % find elegible row
  minndx = find(T(1:mpn,colndx)>=tol);
  [val,rowndx] = min(T(minndx,nend+1)./T(minndx,colndx));
  if (rowndx == []) % Houston, we have a problem ...
    error('quad_prog: program error...unbounded nonnegative minimum.');
  end
  % do Gaussian elimination step without loops
  rowndx = minndx(rowndx);
  tmprow = T(rowndx,:)/T(rowndx,colndx);
  T = T - T(:,colndx)*tmprow;
  T(rowndx,:) = tmprow;
  % do the updates
  basicrows(find(basicrows == rowndx)) = 0;
  basicrows(colndx) = rowndx;
end
disp('Iteration limit was exceeded')
xuyvz = zeros(nend,1);
tmpndx = find(basicrows ~= 0);
xuyvz(tmpndx) = T(basicrows(tmpndx),nend+1);
x = xuyvz(1:n);
zmin =  x'*G*x -c'*x;
