cosmo randperm

function rp = cosmo_randperm(n, varargin)
    % generate random permutation of integers
    %
    % cosmo_randperm(n[,count][,'seed',seed])
    %
    % Inputs:
    %   n                       Maximum value of output
    %   count                   (optional) number of elements to return; count
    %                           must not be larger than n.
    %                           Default: count=n
    %   'seed',seed             (optional) use seed for deterministic
    %                           pseudo-random number generation. If provided
    %                           then subsequent calls to this function with the
    %                           same input arguments will always give the same
    %                           output. If not provided, then subsequent calls
    %                           will almost always give different outputs
    %
    % Output:
    %   rp                      row vector with count elements, with all
    %                           numbers in the range 1:n. Numbers are sampled
    %                           from 1:n without replacement, in other words rp
    %                           does not contain repeats.
    %

    [n, k, seed] = process_input(n, varargin{:});
    if isempty(seed)
        seed_arg = {};
    else
        seed_arg = {'seed', seed};
    end

    [unused, rp] = sort(cosmo_rand(1, n, seed_arg{:}));
    if ~isempty(k)
        if k > n
            error('second argument cannot be larger than first argument');
        end

        rp = rp(1:k);
    end

function [n, k, seed] = process_input(n, varargin)
    k = [];
    seed = [];

    ensure_is_int(n, 'n');

    % progress remaining arguments
    n_arg = numel(varargin);
    j = 0;
    while j < n_arg
        j = j + 1;
        arg = varargin{j};
        if isnumeric(arg)
            ensure_is_int(arg, 'count');
            if ~isempty(k)
                error('count argument provided multiple times');
            end
            k = arg;

        elseif ischar(arg)
            if j + 1 > n_arg
                error('missing value after ''%s'' argument', arg);
            end

            switch arg
                case 'seed'
                    if ~isempty(seed)
                        error('seed argument provided multiple times');
                    end
                    j = j + 1;
                    value = varargin{j};
                    ensure_is_int(value, arg);
                    seed = value;
                otherwise
                    error('illegal keyword ''%s''', arg);
            end

        else
            error('illegal argument type %s at position %d', ...
                  class(arg), j);
        end
    end

function ensure_is_int(value, label)
    if ~(isnumeric(value) && ...
         isscalar(value) && ...
         value >= 0 && ...
         round(value) == value)
        error('''%s'' argument must be non-negative integer', label);
    end