function gout = pdecolgroup(S)
%PDECOLGROUP Determine a column grouping by first-fit packing. 
%	G = PDECOLGROUP(S) returns a column vector G consisting of the group
%	number assigned to each column of matrix S.  Two columns may be
%	grouped together if their dot product is zero.  This helper function
%	is used by NUMJAC to reduce the number of function evaluations
%	required to compute sparse Jacobian matrices.
%	
%	PDECOLGROUP tries two different packings (first-fit and first-fit after
%	reverse colmmd ordering) and returns the one that requires fewer
%	groups. The result may not be optimal.  (Finding the smallest
%	packing is an NP-complete problem equivalent to K-coloring a graph.)
%	
%	See also PDENUMJAC, PDEODE15S, PDEODESET.

%	Mark W. Reichelt and Lawrence F. Shampine, 3-28-94
%	Copyright (c) 1984-95 by The MathWorks, Inc.
%       $Revision: 1.2.1.2 $  $Date: 1995/11/01 08:42:48 $

[m,n] = size(S);
[i,j] = find(S);
T = sparse(i,j,1,m,n);
TT = tril(T' * T);

% First-fit packing.
g = zeros(n,1);
groupnum = 0;
J = (1:n)';
while length(J)
  groupnum = groupnum + 1;
  g(J(1)) = groupnum;
  col = full(TT(:,J(1)));
  for k = J'
    if col(k) == 0
      col = col + TT(:,k);
      g(k) = groupnum;
    end
  end
  J = find(g == 0);
end

% Form the reverse column minimum-degree ordering.
p = colmmd(T);
p = p(n:-1:1);
T = T(:,p);
TT = tril(T' * T);

% First-fit packing.
g2 = zeros(n,1);
groupnum2 = 0;
J = (1:n)';
while length(J)
  groupnum2 = groupnum2 + 1;
  g2(J(1)) = groupnum2;
  col = full(TT(:,J(1)));
  for k = J'
    if col(k) == 0
      col = col + TT(:,k);
      g2(k) = groupnum2;
    end
  end
  J = find(g2 == 0);
end

% Use whichever packing required fewer groups.
if groupnum <= groupnum2
  gout = g;
else
  q(p) = 1:n; 				% invert the permutation
  gout = g2(q);
end
