test tail

function test_suite = test_tail
    % tests for cosmo_tail
    %
    % #   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_tail_basics
    nsamples = ceil(15 + rand() * 5);
    nselect = floor(rand() * nsamples);
    ratio = nselect / nsamples;

    x = randn(nsamples, 1);

    [xs, idxs] = sort(x);
    mp = zeros(nsamples, 1);
    mp(idxs) = 1:nsamples;

    s = cellfun(@(x)char(x), num2cell(70 + mp), ...
                'UniformOutput', false);
    ss = sort(s);

    [v, i] = cosmo_tail(x, nselect);
    assertEqual(v, xs(end + (0:-1:-(nselect - 1))));
    assertEqual(i, idxs(end + (0:-1:-(nselect - 1))));
    [v2, i2] = cosmo_tail(x, ratio);
    assertEqual(v, v2);
    assertEqual(i, i2);
    [v3, i3] = cosmo_tail(s, nselect);
    assertEqual(v3, ss(end + (0:-1:-(nselect - 1))));
    assertEqual(i, i3);
    [v4, i4] = cosmo_tail(s, ratio);
    assertEqual(v3, v4);
    assertEqual(i, i4);

    [v, i] = cosmo_tail(x, -nselect);
    assertEqual(v, xs(1:nselect));
    assertEqual(i, idxs(1:nselect));
    [v2, i2] = cosmo_tail(x, -ratio);
    assertEqual(v, v2);
    assertEqual(i, i2);
    [v3, i3] = cosmo_tail(s, -nselect);
    assertEqual(v3, ss(1:nselect));
    assertEqual(i, i3);
    [v4, i4] = cosmo_tail(s, -ratio);
    assertEqual(v3, v4);
    assertEqual(i, i4);

function test_tail_left_right()
    n = ceil(10 * rand() + 10);
    xs = randn(n, 1);

    n_keep = ceil(rand() * 3 + 2);
    res = cosmo_tail(xs, n_keep);
    assert(numel(res) == n_keep);

    % ensure results match
    s = sort(xs);
    assertEqual(s(end - (0:(n_keep - 1))), res);

    % reverse tail and results
    res_inv = cosmo_tail(xs, -n_keep);
    assertEqual(s(1:n_keep), res_inv);

function test_tail_exceptions
    aet = @(varargin)assertExceptionThrown(@() ...
                                           cosmo_tail(varargin{:}), '');

    % not ok with non-cellstring / non-vector input
    aet(struct(), 0);
    aet({1, 2}, 0);

    % matrix input not ok
    aet(randn(4), 2);
    aet({'a', 'b'; 'c', 'd'}, 1);

    % outside of range not ok
    aet(1:4, 5);
    aet(1:4, -5);

    % second argument must be numeric scaler
    aet(1:4, [1 2]);
    aet(1:4, struct);