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);