function out = seconds2human(secs, varargin)
%SECONDS2HUMAN( seconds )   Converts the given number of seconds into a 
%                           human-readable string.
%
%   str = SECONDS2HUMAN(seconds) returns a human-readable string from a
%   given (usually large) amount of seconds. For example, 
%
%       str = seconds2human(1463456.3)
%
%       str = 
%       'About 2 weeks and 2 days.'
%
%   You may also call the function with a second input argument; either
%   'short' (the default) or 'full'. This determines the level of detail
%   returned in the string:
%
%       str = seconds2human(1463456.3, 'full')
%   
%       str =
%       '2 weeks, 2 days, 22 hours, 30 minutes, 56 seconds.'
%
%   The 'short' format returns only the two largest units of time.
%
%   [secs] may be an NxM-matrix, in which case the output is an NxM cell 
%   array of the corresponding strings. 
%
%   NOTE: SECONDS2HUMAN() defines one month as an "average" month, which 
%   means that the string 'month' indicates 30.471 days. 
%
%   See also datestr, datenum, etime. 


%   Author: Rody P.S. Oldenhuis
%   Delft University of Technology
%   E-mail: oldnhuis@dds.nl
%   Last edited 11/Feb/2010.

    % default error
    error(nargchk(1,2,nargin));%#ok

    % define some intuitive variables
    Seconds   = round(1                 );
    Minutes   = round(60     * Seconds  ); 
    Hours     = round(60     * Minutes  );
    Days      = round(24     * Hours    ); 
    Weeks     = round(7      * Days     ); 
    Months    = round(30.471 * Days     );
    Years     = round(365.26 * Days     );
    Centuries = round(100    * Years    );
    Millennia = round(10     * Centuries);

    % put these into an array, and define associated strings
    units   = [Millennia, Centuries, Years, Months, Weeks, ...
               Days, Hours, Minutes, Seconds];
    singles = {'millennium'; 'century'; 'year'; 'month'; ...
               'week'; 'day'; 'hour'; 'minute'; 'second'};
    plurals = {'millennia' ; 'centuries'; 'years'; 'months'; ...
               'weeks'; 'days'; 'hours'; 'minutes'; 'seconds'};

    % cut off all decimals from the given number of seconds
    assert(isnumeric(secs), 'seconds2human:seconds_mustbe_numeric', ...
        'The argument ''secs'' must be a scalar or matrix.');
    secs = round(secs);   
    
    % parse second argument
    short = true; 
    if (nargin > 1)
        % extract argument
        short = varargin{1};
        % check its type
        assert(ischar(short), 'seconds2human:argument_type_incorrect', ...
            'The second argument must be either ''short'' or ''full''.');
        % check its contents
        switch lower(short)
            case 'full' , short = false;
            case 'short', short = true;
            otherwise
                error('seconds2human:short_format_incorrect',...
                    'The second argument must be either ''short'' or ''full''.');
        end
    end
    
    % pre-allocate appropriate output-type
    numstrings = numel(secs);    
    if (numstrings > 1), out = cell(size(secs)); end
    
    % build (all) output string(s)    
    for j = 1:numstrings
                
        % initialize nested loop
        secsj   = secs(j);
        counter = 0;       
        if short, string = 'About ';
        else      string = '';
        end
        
        % possibly quick exit
        if (secsj < 1), string = 'Less than one second.'; end
        
        % build string for j-th amount of seconds
        for i = 1:length(units)
            
            % amount of this unit
            amount = fix(secsj/units(i));
            
            % include this unit in the output string
            if amount > 0
                
                % increase counter
                counter = counter + 1;
                                
                % append (single or plural) unit of time to string
                if (amount > 1)
                    string = [string, num2str(amount), ' ', plurals{i}];%#ok
                else
                    string = [string, num2str(amount), ' ', singles{i}];%#ok
                end
                                
                % Finish the string after two units if short format is requested
                if (counter > 1 && short), string = [string, '.']; break, end%#ok
                
                % determine whether the ending should be a period (.) or a comma (,)
                if (rem(secsj, units(i)) > 0)
                    if short, ending = ' and ';
                    else ending = ', ';
                    end
                else ending = '.';
                end
                string = [string, ending];%#ok
                
            end
            
            % subtract this step from given amount of seconds
            secsj = secsj - amount*units(i);
        end
        
        % insert in output cell, or set output string
        if (numstrings > 1)
            out{j} = string;
        else
            out = string;
        end        
    end % for
    
end % seconds2human
