function res = AVA(mode, learn, varargin)
% usage is one of the two following:
%   ava = AVA('train', @learner, X, Y, learner-args)
%   Y   = AVA('predict', @learner, ava, X)
  
  switch lower(mode),
    case 'name',
      res = 'AVA';
      
    case 'train',
      if nargin < 4,
        error('AVA training requires at least four arguments');
      end;
      
      [NX,DX] = size(varargin{1});
      [NY,DY] = size(varargin{2});

      if NX ~= NY,
        error('AVA: there must be the same number of data points in X and Y');
      end;
      if DY ~= 1,
        error('AVA: Y must have only one column');
      end

      res = AVAtrain(learn, varargin{1}, varargin{2}, varargin{3:end});
      
      
    case 'predict',
      % check arguments
      if nargin ~= 4,
        error('AVA: prediction requires three arguments: the learner, model, and X');
      end;
      ava   = varargin{1};
      X     = varargin{2};
    
      % compute predictions
      res = AVApredict(learn, ava, X);
  
    otherwise
      error('unknown mode');
  end;
      
  
function ava = AVAtrain(learn, X, Y, varargin)
  labels = myunique(Y);

  ava = {};
  % store all the class labels
  ava.labels = labels;
  ava.models = {};
  
  % create bogus importance weights
  W = ones(size(Y));    % Wei: Never use this W, as I set new W below.
  
  % need to consider all pairs of classes labels(ii)/labels(jj):
  for ii=1:length(labels),
    fprintf('%d...', ii); myflush;
    for jj=(ii+1):length(labels),
      % train a binary classifier for distinguishing between
      % Y==labels(ii) and Y==labels(jj).
      
      % Wei: extrace those samples only with labels ii or jj, and set label
      % jj to zero.
      subX = X(Y == labels(ii) | Y == labels(jj),:);
      subY = Y(Y == labels(ii) | Y == labels(jj));
      for k = 1:length(subY)
          if subY(k) == labels(ii)
              subY(k) = 1;
          else
              subY(k) = 0;
          end;
      end;
      W = ones(size(subY));
      model = feval(learn, 'train', subX, subY, W, varargin{:});
      ava.models{ii}{jj} = model;
    end;
  end;
  fprintf('\n');

function Y = AVApredict(learn, ava, X)
  N = size(X,1);
  Y = zeros(N,1);
  % store votes
  vote = zeros(N,length(ava.labels));
  for ii=1:length(ava.labels),
    for jj=(ii+1):length(ava.labels),
      % ask the ii/jj classifier which of the classes it prefers for all
      % of the data point
      yy = feval(learn, 'predict', ava.models{ii}{jj}, X);

      % increment votes: if ii is prefered, increment vote(n,ii); if jj
      % is prefered, increment vote(n,jj).
      for k = 1: N
          if yy(k) == 1
              vote(k, ii) = vote(k, ii) + 1;
          else
              vote(k, jj) = vote(k, jj) + 1;
          end;
      end;
    end;
  end;
  % compute the highest weighted vote for each class:
  for n=1:N,
    [v ii] = max(vote(n,:));
    Y(n) = ava.labels(ii);
  end;
  