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