function disptable(M, col_strings, row_strings, fmt, spaces)
%DISPTABLE Displays a matrix with per-column or per-row labels.
%   DISPTABLE(M, COL_STRINGS, ROW_STRINGS)
%   Displays matrix or vector M with per-column or per-row labels,
%   specified in COL_STRINGS and ROW_STRINGS, respectively. These can be
%   cell arrays of strings, or strings delimited by the pipe character (|).
%   Either COL_STRINGS or ROW_STRINGS can be ommitted or empty.
%   
%   DISPTABLE(M, COL_STRINGS, ROW_STRINGS, FMT, SPACES)
%   FMT is an optional format string or number of significant digits, as
%   used in NUM2STR. It can also be the string 'int', as a shorthand to
%   specify that the values should be displayed as integers.
%   SPACES is an optional number of spaces to separate columns, which
%   defaults to 1.
%   
%   Example:
%     disptable(magic(3)*10-30, 'A|B|C', 'a|b|c')
%   
%   Outputs:
%        A    B    C
%    a  50  -20   30
%    b   0   20   40
%    c  10   60  -10
%   
%   Author: Joo F. Henriques, April 2010


	%parse and validate inputs

	if nargin < 2, col_strings = []; end
	if nargin < 3, row_strings = []; end
	if nargin < 4, fmt = 4; end
	if nargin < 5, spaces = 2; end
	
	if strcmp(fmt, 'int'), fmt = '%.0f'; end  %shorthand for displaying integer values
	
	assert(ndims(M) <= 2, 'Can only display a vector or two-dimensional matrix.')
	
	num_rows = size(M,1);
	num_cols = size(M,2);

	use_col_strings = true;
	if ischar(col_strings),  %convert "|"-delimited string to cell array of strings
		col_strings = textscan(col_strings, '%s', 'delimiter','|');
		col_strings = col_strings{1};
		
	elseif isempty(col_strings),  %empty input; have one empty string per column for consistency
		col_strings = cell(num_cols,1);
		use_col_strings = false;
		
	elseif ~iscellstr(col_strings),
		error('COL_STRINGS must be a cell array of strings, or a string with "|" as a delimiter.');
	end

	use_row_strings = true;
	if ischar(row_strings),  %convert "|"-delimited string to cell array of strings
		row_strings = textscan(row_strings, '%s', 'delimiter','|');
		row_strings = row_strings{1};
		
	elseif isempty(row_strings),  %empty input; have one empty string per row for consistency
		row_strings = cell(num_rows,1);
		use_row_strings = false;
		
	elseif ~iscellstr(row_strings),
		error('ROW_STRINGS must be a cell array of strings, or a string with "|" as a delimiter.');
	end
	
	assert(~use_col_strings || numel(col_strings) == num_cols, ...
		'COL_STRINGS must have one string per column of M, or be empty.')
	
	assert(~use_row_strings || numel(row_strings) == num_rows, ...
		'ROW_STRINGS must have one string per column of M, or be empty.')
	
	assert(isscalar(fmt) || (isvector(fmt) && ischar(fmt)), ...
		'Format must be a format string or the number of significant digits (as in NUM2STR).')
	
	
	
	%format the table for display
	
	col_text = cell(num_cols,1);  %the text of each column
	
	%spaces to separate columns
	if use_col_strings,
		blank_column = repmat(' ', num_rows + 1, spaces);
	else
		blank_column = repmat(' ', num_rows, spaces);
	end
	
	for col = 1:num_cols,
		%convert this column of the matrix to its string representation
		str = num2str(M(:,col), fmt);
		
		%add the column header on top and automatically pad, returning a
		%character array
		if use_col_strings,
			str = char(col_strings{col}, str);
		end
		
		%right-justify and add blanks to separate from previous column
		col_text{col} = [blank_column, strjust(str, 'right')];
	end
	
	%turn the row labels into a character array, with a blank line on top
	if use_col_strings,
		left_text = char('', row_strings{:});
	else
		left_text = char(row_strings{:});
	end
	
	%concatenate horizontally the character arrays and display
	disp([left_text, col_text{:}])
% 	disp(' ')
	
end

