test stack

function test_suite = test_stack
    % tests for cosmo_stack
    %
    % #   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_stack_samples

    ds = cosmo_synthetic_dataset();
    ds1 = cosmo_slice(ds, [2 1 4 3 6 5]);
    ds2 = cosmo_slice(ds, 6:-1:1, 2);

    % test first dimension
    s = cosmo_stack({ds, ds1});
    assertEqual(s.samples, [ds.samples; ds1.samples]);
    assertEqual(s.sa.targets, [ds.sa.targets; ds1.sa.targets]);
    assertEqual(s.fa, ds1.fa);

    s2 = cosmo_stack({ds, ds2}, 1, 'drop_nonunique');
    assertEqual(fieldnames(s2.fa), {'k'});
    assertEqual(s2.samples, [ds.samples; ds2.samples]);
    assertExceptionThrown(@()cosmo_stack({ds, ds2}, 1, 'unique'), '');
    assertExceptionThrown(@()cosmo_stack({ds, ds2}), '');

    s3 = cosmo_stack({ds, ds2}, 1, 'drop');
    assertTrue(isempty(fieldnames(s3.fa)));
    assertEqual(s3.sa, s2.sa);

    s4 = cosmo_stack({ds, ds2}, 1, 'drop_nonunique');
    assertEqual(s2, s4);

    s5 = cosmo_stack({ds, ds2}, 1, 2);
    assertEqual(s5.fa, ds2.fa);

    % should properly deal with NaNs
    ds.fa.i(2:4) = NaN;
    ds1.fa.i(2:4) = NaN;
    s = cosmo_stack({ds, ds1});
    assertEqual(s.fa, ds1.fa);

function test_stack_features
    ds = cosmo_synthetic_dataset();
    ds1 = cosmo_slice(ds, 6:-1:1, 2);
    ds2 = cosmo_slice(ds, [2 1 4 3 6 5]);

    % test second dimension
    s = cosmo_stack({ds, ds1}, 2, 'drop_nonunique');
    assertEqual(s.samples, [ds.samples ds1.samples]);
    assertEqual(s.fa.i, [ds.fa.i ds1.fa.i]);
    assertEqual(s.sa, ds1.sa);

    s2 = cosmo_stack({ds, ds2}, 2, 'drop_nonunique');
    assertEqual(fieldnames(s2.sa), {'chunks'});
    assertEqual(s2.samples, [ds.samples ds2.samples]);
    assertExceptionThrown(@()cosmo_stack({ds, ds2}, 2, 'unique'), '');
    assertExceptionThrown(@()cosmo_stack({ds, ds2}, 2), '');

    s3 = cosmo_stack({ds, ds2}, 2, 'drop');
    assertTrue(isempty(fieldnames(s3.sa)));
    assertEqual(s3.fa, s2.fa);

    s4 = cosmo_stack({ds, ds2}, 2, 'drop_nonunique');
    assertEqual(s2, s4);

    s5 = cosmo_stack({ds, ds2}, 2, 2);
    assertEqual(s5.sa, ds2.sa);

    s6 = cosmo_stack({ds}, 1);
    assertEqual(s6, ds);

    % should properly deal with NaNs
    ds.sa.targets(2:4) = NaN;
    ds1.sa.targets(2:4) = NaN;
    s = cosmo_stack({ds, ds}, 2);
    assertEqual(s.sa, ds1.sa);

function test_stack_exceptions
    % test exceptions
    ds = cosmo_synthetic_dataset();
    aet = @(varargin)assertExceptionThrown(@()cosmo_stack(varargin{:}), '');
    aet('foo');
    aet({ds}, 3);
    aet({ds}, 1, 'foo');

    % sample size mismatch
    ds1 = ds;
    ds1.samples = ones(1, 5);
    aet({ds, ds1});

    % .sa size mismatch
    ds1 = ds;
    ds1.sa.targets = 1;
    aet({ds, ds1});

    % non-matching elements
    ds1 = ds;
    ds2 = ds;
    ds2.sa.targets = ds2.sa.targets(end:-1:1);
    aet({ds1, ds2}, 2);

    ds2 = ds;
    ds2.fa.i = ds2.fa.i(end:-1:1);
    aet({ds1, ds2}, 1);