function svm_model = train_svm(params, X, Y)

if exist(params.path, 'file')
    fprintf('Loading svm model from %s\n', params.path);
    load(params.path, 'svm_model');
    return
end

if (size(X, 1) ~= size(Y, 1)) || (size(Y, 2) > 1) 
    error('X should be M x feat_dim and Y should be M x 1');
end    

cost = params.svm_C(1);
train_option = '';
test_option = '';
weight_str = '';

if params.weight_classes
    class_ids = unique(Y)';
    weights = size(Y,1)./sum(bsxfun(@eq, repmat(Y, [1 length(class_ids)]), class_ids));
    weight_str = sprintf('-w%d %g ', [class_ids; weights]);
end

switch params.svm_package
  case 'liblinear'
    %% Liblinear
    % options
    %     -s 0   : L2 regularized logistic regression (primal)
    %     -s 2   : L2 regularized L2 loss (primal)
    %     -B 1   : use bias
    %     -c %f  : cost
    %     -q     : quiet mode
    
    if strcmp(params.learner, 'SVR')        
        train_option = '-s 0';
        test_option  = '-b 1';
    else
        train_option = '-s 2';
    end    
    train_option = [train_option ' -B 1 -q'];
    
    if length(params.svm_C) > 1
        [cost, ~, ~] = tune_cost(Y, X, train_option, ...
                                 params.svm_C, params.tune_subset);
    end

    train_option = sprintf('%s -c %f %s', train_option, cost, weight_str);

    fprintf(' [%s] training', datestr(now, 'yyyy-mm-dd HH:MM:SS'));
    svm_model = liblintrain(Y, sparse(X), train_option);
    fprintf(' [%s] ', datestr(now, 'yyyy-mm-dd HH:MM:SS'));
    
    [pred, acc, score] = predict(Y, X, svm_model, test_option);
        
  case 'libsvm'
    %% Libsvm 
    % options
    %     -t 0   : linear svm
    %     -c %f  : cost
    %     -e 0.1 : epsilon
    %     -h 1   : use shrinking heuristics
    %     -b 1   : SVR model for probability estimates

    train_option = '-t 0 -e 0.1 -h 1 -q';
    if strcmp(params.learner, 'SVR')        
        train_option = [train_option ' -b 1'];
        test_option = '-b 1';
    end    
    
    if length(params.svm_C) > 1
        [cost, ~, ~] = tune_cost(Y, X, train_option, ...
                                 params.svm_C, params.tune_subset);
    end

    train_option = sprintf('%s -c %f %s', train_option, cost, weight_str);
    svm_model = svmtrain(Y, X, train_option);
    [pred, acc, score] = svmpredict(Y, X, svm_model, test_option);

  otherwise 
    error('unknown svm package');
end

svm_model.cost = cost;
save(params.path, 'svm_model');
fprintf('\n---SVM model written to %s---\n', params.path);

end

%% Tune Cost
function [best_cost, best_ind, scores] = tune_cost(Y, X, option_str, svm_C, subset)

data_subset = (rand(size(Y)) <= subset);
scores = zeros(length(svm_C));

fprintf('Starting cross validation ...\n');
for i = 1:length(svm_C)
    fprintf('  [%s]', datestr(now, 'yyyy-mm-dd HH:MM:SS'));
    fprintf('  C = %04f : ', svm_C(i));
    scores(i) =  liblintrain(Y(data_subset), ...
                             sparse(X(data_subset, :)), ...
                             sprintf('%s -v 5 -c %f', option_str, svm_C(i)));
end

[best_score, best_ind] = max(scores(:));
best_cost = svm_C(best_ind);
fprintf('Best accuracy of %g with cost = %g\n', best_score, best_cost);
end
