function K = kernelize(feaTrn, opt, feaTst)

% Compute kernel data matrix
%
% Description:
%
%  Compute the kernel matrix of training/testing data
%
% IN: 
%   feaTrn: Ntr x F training data matrix
%   feaTst: Nts x F testing data matrix
%   opts : options
%     .kerntype: string declaring kernel type ('linear', 'gaussian',
%        'polynomial', 'polyplus)
%     .r: parameter value for Gaussian, polynomial or polyplus kernel
%
% OUT:
%  K: kernel matrix
%
% Author: Nikolaos Gkalelis - CERTH-ITI
% Email: gkalelis@iti.gr
%
% First creation (Version 0.0) 1 Apr 2014.
% Version 0.1, 19 Jul 2017.
%

%% validate arguments
if nargin == 1
    opt.kerntype = 'linear'; % default
    fprintf('kernelize>> Default kernel type linear is used\n');
elseif nargin == 2
    fprintf('kernelize>> Computing kernel matrix of train data\n');
elseif nargin == 3
    fprintf('kernelize>> Computing kernel matrix of test data\n');
else
    error('Too many arguments!!!');
end

%% validate options
if sum(strcmp(opt.kerntype, {'linear', 'gaussian', 'polynomial', 'polyplus'})) == 0
    error('Unknoun kernel type!!!');
end

if sum(strcmp(opt.kerntype, {'gaussian', 'polynomial', 'polyplus'})) == 0 ...
        && isfield(opt, 'r') == false
    fprintf('kernelize>> Default kernel value 2 is used\n');
    opt.r = 2;
end

if nargin == 1 || nargin == 2
    if strcmp(opt.kerntype, 'gaussian')
        D = sqdistance(feaTrn');
    else % 'linear', 'polynomial', 'polyplus'
        D = full(feaTrn * feaTrn');
    end
elseif nargin == 3  % compute kernel matrix of test data
    if strcmp(opt.kerntype, 'gaussian')
        D = sqdistance(feaTrn', feaTst');
    else % 'linear', 'polynomial', 'polyplus'
        D = full(feaTrn * feaTst');
    end
else
    error('Unexpected number of arguments!!!');
end

if strcmp(opt.kerntype, 'linear')
    K = D;
elseif strcmp(opt.kerntype, 'gaussian')
    K = exp( -D / (2 * opt.r^2 ) );
elseif strcmp(opt.kerntype, 'polynomial')
    K = D .^ opt.r;
elseif strcmp(opt.kerntype, 'polyplus')
    K = (D + 1) .^ opt.r;
else
    error('Unexpected kernel type!!!');
end

clear D;

if nargin < 3
    K = max(K, K');
end

% illegal operation that provide complex values, e.g., (-1)^(0.1) when
% polynomial kernel is used
if ~isreal(K)
    error('kernelize>>> Kernel matrix contains complex values !!!');
end
