function progress_line=cosmo_show_progress(clock_start, ratio_done, msg, prev_progress_line)
% Shows a progress bar, and time elapsed and expected to complete.
%
% progress_line=cosmo_show_progress(clock_start, progress[, msg[, prev_progress_line]])
%
% Inputs:
% clock_start The time the task started (from clock()).
% ratio_done 0 <= ratio_done <= 1, where 0 means nothing
% completed and 1 means fully completed.
% msg String with a message to be shown next to the
% progress bar (optional).
% prev_progress_line The output from the previous call to this
% function, if applicable (optional). If provided
% then invoking this function prefixes the output
% with numel(prev_progress_msg) backspace characters,
% which deletes the output from the previous call
% from the console. In other words, this allows for
% showing a progress message at a fixed location in
% the console window.
%
% Output:
% progress_line String indicating current progress using a bar,
% with time elapsed and expected time to complete
% (using linear extrapolation).
%
% Notes:
% - As a side effect of this function, progress_msg is written to standard
% out (the console).
% - The use of prev_progress_line may not work properly if output is
% written to standard out without using this function.
%
% Example:
% % this code takes just over 3 seconds to run, and fills a progress bar.
% prev_msg='';
% clock_start=clock();
% for k=1:100
% pause(.03);
% status=sprintf('done %.1f%%', k);
% ratio_done=k/100;
% prev_msg=cosmo_show_progress(clock_start,ratio_done,status,prev_msg);
% end
% % output:
% > +00:00:03 [####################] -00:00:00 done 100.0%
%
% # For CoSMoMVPA's copyright information and license terms, #
% # see the COPYING file distributed with CoSMoMVPA. #
if nargin<4 || isempty(prev_progress_line)
delete_count=0; % nothing to delete
elseif ischar(prev_progress_line)
delete_count=numel(prev_progress_line); % count the characters
end % if not a string, die ungracefully
if nargin<3 || isempty(msg)
msg='';
end
if ratio_done<0 || ratio_done>1
error('illegal progress %d: should be between 0 and 1', ratio_done);
end
if ratio_done==0
ratio_to_do=Inf;
else
ratio_to_do=(1-ratio_done)/ratio_done;
end
clock_now=clock();
took=etime(clock_now, clock_start);
eta=ratio_to_do*took; % 'estimated time of arrival'
% set number of backspace characters
delete_str=repmat(sprintf('\b'),1,delete_count);
% define the bar
bar_width=20;
bar_done=round(ratio_done*bar_width);
bar_eta=bar_width-bar_done;
bar_str=[repmat('#',1,bar_done) repmat('-',1,bar_eta)];
% because msg may contain the '%' character (which is not to be
% interpolated) care is needed to ensure that neither building the
% progress line nor printing it to standard out applies interpolation.
progress_line=[sprintf('+%s [%s] -%s ', secs2str(took), bar_str, ...
secs2str(-eta)),...
msg];
if ratio_done==1
postfix=sprintf('\n');
else
postfix='';
end
fprintf('%s%s%s',delete_str,progress_line,postfix);
function [m,d]=moddiv(x,y)
% helper function that does mod and div together so that m+d*y==x
m=mod(x,y);
d=(x-m)/y;
function str=secs2str(secs)
% helper function that formats the number of seconds as
% human-readable string
% make secs positive (calling function should add '+' or '-')
secs=abs(secs);
if ~isfinite(secs)
str='oo'; % attempt to look like 'infinity' symbol
return
end
secs=round(secs); % do not provide sub-second precision
% compute number of seconds, minutes, hours, and days
[s,secs]=moddiv(secs,60);
[m,secs]=moddiv(secs,60);
[h,d]=moddiv(secs,24);
% add prefix for day, if secs represents at least one day
if d>0
daypf=sprintf('%dd+',d);
else
daypf='';
end
str=sprintf('%s%02d:%02d:%02d', daypf, h, m, s);