function svmmodel = liblinear_train ( labels, feat, settings )
%
% BRIEF
%   A simple wrapper to provide training of 1-vs-all-classification for LIBLINEAR. No
%   further settings are adjustable currently.
% 
% INPUT
%  labels   -- multi-class labels (#sample x 1)
%  feat     -- features for training images (#samples x # dimensions)
%  settings -- struct for configuring the svm model training, e.g., via
%              'b_verbose', 'f_svm_C', ...
% 
% OUTPUT:
%  svmmodel -- cell ( #classes x 1 ), every model entry is obtained via
%              svmtrain of the corresponding 1-vs-all-problem
%
% date: 30-04-2014 ( dd-mm-yyyy )
% last modified: 22-10-2015
% author: Alexander Freytag, Christoph Käding

    if ( nargin < 3 ) 
        settings = [];
    end
    
    
    liblinear_options = '';
    
    % outputs for training
    if ( ~ getFieldWithDefault ( settings, 'b_verbose', false ) )
        liblinear_options = sprintf('%s -q', liblinear_options);
    end
    
    % cost parameter
    f_svm_C = getFieldWithDefault ( settings, 'f_svm_C', 1);
    liblinear_options = sprintf('%s -c %f', liblinear_options, f_svm_C);    
    
    % do we want to use an offset for the hyperplane?
    if ( getFieldWithDefault ( settings, 'b_addOffset', false) )
        liblinear_options = sprintf('%s -B 1', liblinear_options);    
    end
    
    % add multithreading
    % NOTE: - requires liblinear-multicore
    %       - supports only -s 0, -s 2, or -s 11 (so far)
    i_numThreads = getFieldWithDefault ( settings, 'i_numThreads', 1);
    if i_numThreads > 1
        liblinear_options = sprintf('%s -n %d', liblinear_options, i_numThreads);
    end
        
    % which solver to use
    % copied from the liblinear manual:
%       for multi-class classification
%          0 -- L2-regularized logistic regression (primal)
%          1 -- L2-regularized L2-loss support vector classification (dual)
%          2 -- L2-regularized L2-loss support vector classification (primal)
%          3 -- L2-regularized L1-loss support vector classification (dual)
%          4 -- support vector classification by Crammer and Singer
%          5 -- L1-regularized L2-loss support vector classification
%          6 -- L1-regularized logistic regression
%          7 -- L2-regularized logistic regression (dual)    
    i_svmSolver = getFieldWithDefault ( settings, 'i_svmSolver', 1);
    liblinear_options = sprintf('%s -s %d', liblinear_options, i_svmSolver);    

    
    % increase penalty for positive samples according to invers ratio of
    % their number, i.e., if 1/3 is ratio of positive to negative samples, then
    % impact of positives is 3 the times of negatives
    % 
    b_weightBalancing = getFieldWithDefault ( settings, 'b_weightBalancing', false);
    
    % increase penalty for positive samples according to invers ratio of
    % their number, i.e., if 1/3 is ratio of positive to negative samples, then
    % impact of positives is 3 the times of negatives
    % 
    b_cross_val = getFieldWithDefault ( settings, 'b_cross_val', false);   
    if ( b_cross_val && (length(unique(labels)) ~=2 ) )
        i_num_folds = getFieldWithDefault ( settings, 'i_num_folds', 10);  
        liblinear_options = sprintf('%s -v %d', liblinear_options, i_num_folds ); 
    end
    
    
  
    uniqueLabels = unique ( labels );
    i_numClasses = size ( uniqueLabels,1);
    
	
    %# train one-against-all models
    
    if ( ~b_weightBalancing)    
        if ( b_cross_val && (length(unique(labels)) ==2 ) )
            
            % measure of accuracy during cross validation is auc   
            svmmodel = do_binary_cross_validation( labels, feat, liblinear_options, getFieldWithDefault ( settings, 'i_num_folds', 10) );
        else
            svmmodel = train( labels, feat, liblinear_options );
        end
    else
        svmmodel = cell( i_numClasses,1);
        for k=1:i_numClasses
            yBin        = 2*double( labels == uniqueLabels( k ) )-1;
            
            fraction = double(sum(yBin==1))/double(numel(yBin));
            liblinear_optionsLocal = sprintf('%s -w1 %f', liblinear_options, 1.0/fraction);
            svmmodel{ k } = train( yBin, feat, liblinear_optionsLocal );
            
            %store the unique class label for later evaluations.
            svmmodel{ k }.uniqueLabel = uniqueLabels( k );
        end         
    end
       
end