%MCLASSC Computation of multi-class classifier from 2-class discriminants
%
%  W = MCLASSC(A,CLASSF,MODE)
%  W = A*MCLASSC([],CLASSF,MODE)
%  W = A*MCLASSC(CLASSF,MODE)
%
% INPUT
%   A       Dataset
%   CLASSF  Untrained classifier
%   MODE    Type of handling multi-class problems (optional; default: 'single')
%
% OUTPUT
%   W       Combined classifier
%
% DESCRIPTION
% For default MODE = 'single', the untrained classifier CLASSF is called to
% compute C classifiers between each of the C classes in the dataset A and
% the remaining C-1 classes. The result is stored into the combined
% classifier W.
%
% For MODE = 'multi' the untrained classifier CLASSF is trained between all
% pairs of classes as well as between each class and all other classes.
% This total set of C classes is combined by MINC. The use of soft labels
% is supported.
%
% EXAMPLES
% W = MCLASSC(GENDATM(100),QDC,'MULTI');
%
% SEE ALSO (<a href="http://37steps.com/prtools">PRTools Guide</a>)
% DATASETS, MAPPINGS, MINC.

% Copyright: R.P.W. Duin, r.p.w.duin@37steps.com
% Faculty EWI, Delft University of Technology
% P.O. Box 5031, 2600 GA Delft, The Netherlands

% $Id: mclassc.m,v 1.9 2009/08/18 23:09:01 duin Exp $

function [w,varargout] = mclassc(varargin)

  varargout = repmat({[]},[1, max((nargout-1),0)]);
  argin = shiftargin(varargin,'prmapping');
  argin = setdefaults(argin,[],[],'single');
  
  if mapping_task(argin,'definition')
    w = define_mapping(argin,'untrained');
    return
  end
    
  [a,classf,mode] = deal(argin{:});	
	if ~isa(classf,'prmapping') || ~isuntrained(classf)
		error('Second parameter should be untrained mapping')
	end

	islabtype(a,'crisp','soft');
	%isvaldfile(a,1,2); % at least 1 object per class, 2 classes
  %RD this should be handled by the base classifier

	[m,k,c] = getsize(a);
	
	if c == 2
		[w,varargout{:}] = prmap(a,classf);
		return
  end
  
  % restore prtime after interupt
  prt = prtime;
  finishup = onCleanup(@() prtime(prt));
  
	varout = {};
  p       = getprior(a);
  nlab    = getnlab(a);
  lablist = getlablist(a);
	s = sprintf('Multi-class: %4i classifiers: ',c); 
	prwaitbar(c,s);

	switch mode

	 case 'single'
	  w = [];
    L = find(nlab > 0);        % labeled samples
    N = find(nlab == 0);       % unlabeled smaples
    prtime(prt/c);
	  for i=1:c
			prwaitbar(c,i,[s int2str(i)]);
		  if islabtype(a,'crisp')
        % create a two-class problem
			  M = double(2 - (nlab(L) == i));
        aa = setlabels(a,1);       % make sure new labels are numeric
        aa = setlablist(aa,[1 2]');% make sure we have two-clas problem
			  aa = setlabels(aa,M,L); % new, 2-class labels
        aa = setnlab(aa,0,N);      % set unlabeled samples
				if ~isempty(a,'prior')
					aa = setprior(aa,[p(i),1-p(i)]');
				end
		  elseif islabtype(a,'soft')
			  atargets = gettargets(a);
			  targets = [atargets(:,i) 1-atargets(:,i)]; %assumes soft labels sum to one???
			  aa = prdataset(+a,nlab,targets,'lablist',[1 2]');
		  end
			varo = varargout;
      [v,varo{:}] = prmap(aa,classf);
			varout = [varout; varo];
			w = [w,setlabels(v(:,1),lablist(i,:))];
    end

	 case 'multi'
	  w = [];
		nclassf = 0;
	  w = [];
    L = find(nlab > 0);        % labeled samples
    N = find(nlab == 0);       % unlabeled smaples
	  for i1=1:c
			prwaitbar(c,i1,[s int2str(i1)]);
		  lab = lablist(i1,:);
		  J1 = find(nlab==i1);
		  if islabtype(a,'crisp')
        aa = setlabels(a,1);       % make sure new labels are numeric
        aa = setlablist(aa,[0 1]');% make sure we have two-clas problem
			  mlab = ones(m,1);
			  mlab(J1) = zeros(length(J1),1);
			  aa = setlabels(aa,mlab);
        aa = setnlab(aa,0,N);
				%aa = remclass(aa); % remove empty classes
		  else
			  problab = gettargets(a);
			  mlab = [problab(:,i1) sum(problab,2)-problab(:,i1)];
			  aa = settargets(a,mlab,[1 2]');
		  end		
		  I1 = [1:c]; I1(i1) = [];
			varo = varargout;
      [v,varo{:}] = prmap(aa,classf);
			varout = [varout; varo];
      w = [w,setlabels(v(:,1),lab)];
		  for i2 = I1 
			  if islabtype(a,'crisp')
				  J2 = find(nlab==i2);
				  v = aa([J1;J2],:)*classf;
			  else
				  mlab2 = problab(:,[i1 i2]);
				  v = setlabels(aa,mlab2)*classf;
			  end
			  w = [w,setlabels(v(:,1),lab)];
				nclassf = nclassf+1;
		  end
	  end
	  w = minc(w);

	 otherwise
	  error('Unknown mode')
	end
	prwaitbar(0);	
	
	w = setname(w,getname(classf));
	w = setsize(w,[k,c]);
	w = setcost(w,a);

	if ~isempty(varout)
    varargout = num2cell(varout',2)';
  end
  
	return
