function KC = slcenkernel(K0, K, w)
%SLCENKERNEL Compute the centralized kernel matrix
%
% $ Syntax $
%   - KC = slcenkernel(K0)
%   - KC = slcenkernel(K0, [], w)
%   - KC = slcenkernel(K0, K)
%   - KC = slcenkernel(K0, K, w)
%
% $ Arguments $
%   - K0:       the gram matrix of the referenced samples
%   - K:        the kernel matrix for target samples
%   - w:        the weights for the referenced samples
%   - KC:       the centralized kernel matrix.
%
% $ Description $
%   - KC = slcenkernel(K0) compute the centralized kernel matrix from
%     the original kernel gram matrix K0.
% 
%   - KC = slcenkernel(K, [], w) compute the centralized kernel gram
%     matrix from the original kernel gram matrix K0. The mean feature
%     is obtained with the weights for referenced samples, given by w.
%
%   - KC = slcenkernel(K0, K) compute the centralized kernel matrix
%     for target samples, with the original gram matrix for referenced
%     samples K0 and the kernel matrix for target samples w.r.t the 
%     referenced samples K given.
%
%   - KC = slcenkernel(K0, K, w) compute the centralied kernel matrix
%     for target samples, with feature mean computed in a weighted 
%     manner.
%
% $ Remarks $
%   -# For original kernel matrix K, it is defined as 
%      K(i, j) = <phi(i), phi(j)>, given that phi(i) and phi(j) are
%      the feature map of the samples x(i) and x(j) respectively.
%      Then the centralized kernel matrix is defined as
%      KC(i, j) = <phi(i) - mean_phi, phi(j) - mean_phi>, where
%      mean_phi is the mean of all referenced feature maps. If w
%      is specified mean_phi is given by weighted mean. 
%      Kernel centralization plays an important role in many 
%      kernelized algorithms such as Kernel PCA.
%
%   -# Suppose the mean feature map is defined by 
%      mean_phi = sum_i w_i phi(i).
%      Then the centralized kernel gram matrix can be written as
%      KC = K - 1 * (w^T * K) - (K * w) * 1^T + 1 * (w^T * K * w) * 1^T.
%      It can be easily shown that the mean of columns (rows) of KC is
%      a zero vector. Thus, centralize the centralized kernel matrix
%      would keep the input unchanged. 
%
%   -# Instead of applying the formula given above, the function 
%      implements a more efficient computational routine by reducing
%      the redundant computations.
%
% $ History $
%   - Created by Dahua Lin on May 2nd, 2006
%   - Modified by Dahua Lin on Sep 10, 2006
%       - use sladdrowcols to replace sladd to increase efficiency
%

%% parse and verify input arguments

% for K0
n0 = size(K0, 1);
if ndims(K0) ~= 2 || size(K0, 2) ~= n0;
    error('sltoolbox:invaliddims', ...
        'K0 should be a 2D square matrix');
end

% for K
if nargin < 2 || isempty(K)
    K = K0;
else
    if ndims(K) ~= 2
        error('sltoolbox:invaliddims', ...
            'K should be a 2D matrix');
    end
    if size(K, 1) ~= n0
        error('sltoolbox:sizmismatch', ...
            'Size inconsistency between K0 and K');
    end
end
    
% for w    
if nargin < 3 || isempty(w)
    isweighted = false;
else
    if ~isequal(size(w), [1, n0])
        error('sltoolbox:sizmismatch', ...
            'Size inconsistency between K0 and w');
    end    
    isweighted = true;
end


%% compute

% Steps:
% 1. compute v1: mean row vector of K (1 x n)
% 2. compute v2: mean column vector of K0 (n x 1)
% 3. compute s3: mean value of of all elements of K0 (1 x 1)
% 4. KC = K - expand(v1) - expand(v2) + s3

if ~isweighted  % non-weighted case

    v1 = sum(K, 1) * (1 / n0);
    v2 = sum(K0, 2) * (1 / n0);
    s3 = sum(v2) * (1 / n0);
    
else            % weighted case
    
    w = w / sum(w);     % normalize the weights
    
    v1 = w * K;
    v2 = K0 * w';
    s3 = w * v2;
    
end

KC = sladdrowcols(K, -v1, -v2+s3);


