test index unique

function test_suite = test_index_unique
% tests for cosmo_index_unique
%
% #   For CoSMoMVPA's copyright information and license terms,   #
% #   see the COPYING file distributed with CoSMoMVPA.           #
    try % assignment of 'localfunctions' is necessary in Matlab >= 2016
        test_functions=localfunctions();
    catch % no problem; early Matlab versions can use initTestSuite fine
    end
    initTestSuite;


function test_index_unique_()
    n=100;
    a=round(rand(n,1)*2)+1;
    b=round(rand(n,1)*2)+1;
    c=round(rand(n,1)*2)+1;

    abc=[a b c];
    [unq1,unused,idx1]=unique(abc,'rows');
    [idx2,vals2]=cosmo_index_unique({a,b,c});

    assertEqual(unq1,[vals2{:}]);

    nunq=size(unq1,1);
    assert(nunq==numel(idx2));

    % test some random elements
    rp=randperm(nunq);
    rp=rp(1:10);
    for j=rp
        assertEqual(find(idx1==j),idx2{j})
    end

    % test with some string labels
    labels={'a','bb','ccc'}';

    [idx3,vals3]=cosmo_index_unique({labels(a),b,labels(c)});
    assertEqual(idx3,idx2);
    v2to3={labels(vals2{1}),vals2{2},labels(vals2{3})};
    assertEqual(vals3,v2to3)

    % test with matrix input
    [idx4,vals4]=cosmo_index_unique(abc);
    assertEqual(idx4,idx2);
    assertEqual(vals4,[vals2{:}]);

    % test vector input
    rp=randperm(10);
    [idx5,vals5]=cosmo_index_unique(rp+20);
    assertEqual(idx5,{1});
    assertEqual(vals5,rp+20);

    [idx6,vals6]=cosmo_index_unique(rp'+20);
    assertEqual(cell2mat(idx6(rp))',1:numel(rp));
    assertEqual(vals6,(1:numel(rp))'+20);

    % test empty input

    % test wrong inputs
    n=100;
    a=round(rand(n,1)*2)+1;
    b=round(rand(n,1)*2)+1;
    c=round(rand(n,1)*2)+1;

    abc=[a b c];
    [unq1,unused,idx1]=unique(abc,'rows');
    [idx2,vals2]=cosmo_index_unique({a,b,c});

    assertEqual(unq1,[vals2{:}]);

    nunq=size(unq1,1);
    assert(nunq==numel(idx2));

    % test some random elements
    rp=randperm(nunq);
    rp=rp(1:10);
    for j=rp
        assertEqual(find(idx1==j),idx2{j})
    end

    % test with some string labels
    labels={'a','bb','ccc'}';

    [idx3,vals3]=cosmo_index_unique({labels(a),b,labels(c)});
    assertEqual(idx3,idx2);
    v2to3={labels(vals2{1}),vals2{2},labels(vals2{3})};
    assertEqual(vals3,v2to3)

    % test with matrix input
    [idx4,vals4]=cosmo_index_unique(abc);
    assertEqual(idx4,idx2);
    assertEqual(vals4,[vals2{:}]);

    % test vector input
    rp=randperm(10);
    [idx5,vals5]=cosmo_index_unique(rp+20);
    assertEqual(idx5,{1});
    assertEqual(vals5,rp+20);

    [idx6,vals6]=cosmo_index_unique(rp'+20);
    assertEqual(cell2mat(idx6(rp))',1:numel(rp));
    assertEqual(vals6,(1:numel(rp))'+20);

    % test empty input

    % test wrong inputs
    aet=@(x)assertExceptionThrown(@()cosmo_index_unique(x),'');
    aetp=@(x,m)assertExceptionThrown(@()cosmo_index_unique(x),m);
    aet({[1,2],[3]});
    aet({[1,2],'a'});

    if cosmo_wtf('is_matlab')
        v=cosmo_wtf('version');
        is_prior_to_2012b=str2num(v(1))<=7;

        if is_prior_to_2012b
            id_different_classes='MATLAB:CELL:UNIQUE:InputClass';
        else
            id_different_classes='MATLAB:UNIQUE:InputClass';
        end
    else
        id_different_classes='';
    end
    aetp({[1,2],{1,2}},id_different_classes);
    aet(ones([2 2 2]));


function test_unique_with_nans
    n_rows_half=100+ceil(rand()*20);
    %n_rows_half=2;
    n_rows=n_rows_half*2;
    x=ceil(rand(n_rows,2)*sqrt(n_rows_half));

    rp=get_non_identity_randperm(n_rows);

    rp1=rp(1:n_rows_half);
    rp2=rp(n_rows_half+(1:n_rows_half));

    % first column, half of the rows become NaN
    x(rp1,1)=NaN;
    x(rp1,2)=1:n_rows_half;
    x(rp2,1)=1:n_rows_half;
    x(rp2,2)=1:n_rows_half;


    rp_y=get_non_identity_randperm(n_rows);

    y=x(rp_y,:);

    % test with numeric input
    [x_idx,x_unq]=cosmo_index_unique(x);
    [y_idx,y_unq]=cosmo_index_unique(y);

    assertFalse(isequal(x_unq,y_unq));
    % due to NaNs, the indices must be different
    assertFalse(isequal([x_idx{:}],rp_y([y_idx{:}])));

    % test with cell input
    [xx_idx,xx_unq]=cosmo_index_unique({x(:,1),x(:,2)});
    [yy_idx,yy_unq]=cosmo_index_unique({y(:,1),y(:,2)});

    assertFalse(isequal(xx_unq,yy_unq));
    % due to NaNs, the indices must be different
    assertFalse(isequal([xx_idx{:}],rp_y([yy_idx{:}])));


function test_index_unique_empty()
    empty_args={{},{[],{}},{[]},{{},{}}};
    for k=1:numel(empty_args)
        empty_arg=empty_args{k};

        [idxs,unq]=cosmo_index_unique(empty_arg);
        assertTrue(iscell(idxs));
        assertTrue(numel(idxs)==1);
        assertEqual(numel(unq),numel(empty_arg));
        assertTrue(all(cellfun(@isempty,unq)));
    end


function rp=get_non_identity_randperm(n)
    while true
        rp=randperm(n);
        if ~isequal(rp,1:n)
            break;
        end
    end