function c=cosmo_corr(x,y,corr_type)
% Computes correlation - faster than than matlab's "corr" for Pearson.
%
% c=comso_corr(x[,y[,corr_type]])
%
% Inputs:
%   x          PxM matrix.
%   y          PxN matrix (optional). If omitted then y=x.
%   corr_type  'Pearson' or 'Spearman' or 'Kendall' (optional). If omitted
%              then corrtype='Pearson' and the computation time is
%              significantly reduced for small matrices x and y (with
%              /tiny/ numerical imprecisions) by the use of a custom
%              implementation.
%              Using 'Kendall' required the matlab stats
%              toolbox and is currently not supported in Octave.
% Output:
%   c          MxN matrix with c(i,j)=corr(x(:,i),y(:,j),'type',corr_type).
%
% Notes:
%  - this function does not compute probability values.
%  - Using 'Kendall' for corr_type requires the matlab stats toolbox.
%
% Example:
%   % generate some pseudo-random data.
%   x=reshape(mod(2:7:100,41),5,[]);
%   y=reshape(mod(1:7:100,37),5,[]);
%   % compute builtin corr with cosmo_corr
%   % call the function first to avoid lookup delays; then measure time
%   c=corr(x,y);
%   cc=cosmo_corr(x,y);
%   % compute differences in output
%   delta=c-cc;
%   max_delta=max(abs(delta(:)));
%   fprintf('difference not greater than eps: %d\n',max_delta<=eps);
%   > difference not greater than eps: 1
%
% See also: corr
%
% #   For CoSMoMVPA's copyright information and license terms,   #
% #   see the COPYING file distributed with CoSMoMVPA.           #

    y_as_x=false;

    if nargin<2
        corr_type='Pearson';
        y_as_x=true;
    elseif ischar(y)
        corr_type=y;
        y_as_x=true;
    elseif nargin<3
        corr_type='Pearson';
    end

    if y_as_x
        y=x;
    end

    switch corr_type
        case 'Pearson'
            c=corr_pearson(x,y,y_as_x);

        case 'Spearman'
            x_ranks=cosmo_tiedrank(x);
            y_ranks=cosmo_tiedrank(y);

            c=corr_pearson(x_ranks,y_ranks,y_as_x);

        otherwise
            % fall-back: use Matlab's function
            % will puke if no Matlab stat toolbox
            c=corr(x,y,'type',corr_type);
    end


function c=corr_pearson(x,y,y_as_x)

    % speed-optimized version
    nx=size(x,1);
    ny=size(y,1);

    % subtract mean
    xd=bsxfun(@minus,x,sum(x,1)/nx);
    yd=bsxfun(@minus,y,sum(y,1)/ny);

    % normalization
    n=1/(size(x,1)-1);

    % standard deviation
    xs=(n*sum(xd .^ 2)).^-0.5;
    ys=(n*sum(yd .^ 2)).^-0.5;

    % compute correlations
    c=n * (xd' * yd) .* (xs' * ys);

    if y_as_x
        % ensure diagonal elements are 1
        c=(c+c')*.5;
        dc=diag(c);
        c=(c-diag(dc))+eye(numel(dc));
    end