function x0=iterate(varargin)
% iterate - Simulate recursive iteration of a function
%
% iterate :: 
%    (A=>(A | empty)) ~'function to iterate, can return [] to terminate',
%    A                ~'initial state',
%    options iterate_timed_options
% -> A                ~'final state'.
%
% iterate :: 
%    cell {
%       (A->(A | empty)) ~'function to iterate, can return [] to terminate',
%       A                ~'initial state'
%    }                   ~'iterated system as a cell array',
%    options iterate_options
% -> A                ~'final state'.
%
% iterate_options = {
%       its     :: natural /inf ~'Iteration limit';
%       quiet   :: bool    /1   ~'controls verbosity';
%       pre     :: A=>void      ~'something to do before each iteration';
%       post    :: A=>void      ~'something to do after each iteration';
%       save    :: natural /0   ~'iterations between state saving (0=off)';
%       id      :: string  /'iterstate@' ~'name of mat file to save to';
%       recover :: bool    /0   ~'recover from saved state';
%       drawnow :: bool    /1   ~'controls graphics updating';
%       pause   :: natural /0   ~'pause between iterations (in ms) or 1 for indefinite'
%    }.
%
% The function must return an empty array to force termination.

if iscell(varargin{1})
	[f,x]=varargin{1}{:};
	options=varargin(2:end);
else
	f=varargin{1};
	x=varargin{2};
	options=varargin(3:end);
end

opts=prefs('its',inf,'quiet',1,'save',0,'recover',0,'id','iterstate@',options{:});
pauserfn=pauser(opts);
post=isfield(opts,'post');
pre=isfield(opts,'pre');

i=0; 
if opts.recover
	if exist([opts.id,'.mat'],'file')
		fprintf('Attempting to recover from saved state %s.mat\n',opts.id);
		load(opts.id);
		fprintf('Continuing from iteration %d\n',i);
	else
		fprintf('Recovery file %s.mat does not exist.\n',opts.id);
	end
end

if ~post && ~pre && opts.quiet && opts.save==0
	while ~isempty(x) && i<opts.its,
		i=i+1;
		x0=x; x=f(x0); 
		pauserfn();
	end
else
	if opts.save>0
		fprintf('Will save every %d iterations to %s.mat\n',opts.save,opts.id);
	end

	if isfinite(opts.its), itformat=sprintf('%%%dd. ',ceil(log10(opts.its)));
	else itformat='%d. '; end


	while ~isempty(x) && i<opts.its,
		i=i+1;
		if ~opts.quiet, fprintf(itformat,i); end
		if pre, opts.pre(x); end
		x0=x; x=feval(f,x0); 
		if post, opts.post(x); end

		% save state every opts.save iterations
		if mod(i,opts.save)==0, 
			fprintf('\nsaving at %d.\n',i);
			save(opts.id,'x','i'); 
		end
		pauserfn();
	end
	if opts.save>0, 
		fprintf('Deleting recovery file %s.mat\n',opts.id);
		delete([opts.id '.mat']); 
	end
end
if ~isempty(x), x0=x; end 
	


