classdef dmap
	properties (GetAccess=private, SetAccess=immutable)
		cardr_
		map_rn
		map_ni
		domain_
	end

	methods
		% dmap - Create discretisation map
		%
		% dmap :: 
		%    N:natural~'range of discretisation function will be 1..N',
		%    (real->natural) ~'discretisation function itself',
		%    (natural->[[2]]) ~'reverse map, returns upper and lower bin edges'
		% -> dmap.
		%
		% The map consists of a mapping between the integers 1..N and a set
		% of consectutive half-open intervals. The discretisation is performed
		% by mapping a real number to the index of the half-open interval in
		% which it lies.
		%
		% The map be applied using parentheses (function application), eg
		%
		%    m = linmap(-2,10,24);
		%    i = m(2.45);
		%
		% If the input is outside, the map returns -Inf or Inf. 
		%
		% METHODS 
		%    bins  - return some or all of the intervals in the map
		%    cardr - number of intervals in a map
		%    centres - return the centres of some or all of the intervals
		%    domain  - return the interval which is union of all the intervals
		%    edges   - return the set of all the interval endpoints.
		%    range   - the set 1..N
		%    map_clamped - apply map with output clamped to valid range
		%    feval   - apply map to real numbers
		%
		%    dmap also implements subsref, tostring and display
		%
		%    linmap, binmap, edgemap - create convenient discretisation maps

		function M=dmap(card,mapfn,revmap)
			if nargin==0, M=dmap(1,@floor,@(i)[i-1;i]); % dummy map
			elseif isa(card,'dmap'), M=card
			else
				M.cardr_=card; 
				M.map_rn=mapfn;
				M.map_ni=revmap;

				intervals=revmap([1 M.cardr_]);
				M.domain_=[intervals(1,1) intervals(2,2)];
			end
		end

		% bins - return bin edges of discretisation map
		%
		% bins :: dmap(N) -> [[2,N]]~'all the bins'.
		% bins :: dmap(N), [[M]->[N]]~'M bin indices'-> [[2,M]]~'selected bins'.
		function X=bins(M,I), 
			if nargin<2,I=range(M); end
			X=M.map_ni(I);
		end

		% cardr - Cardinality of range (ie size)
		%
		% cardr :: dmap(N) -> N:natural.
		function N=cardr(M), N=M.cardr_; end

		% centres - Return centres of discretisation bins
		%
		% centres :: dmap(N) -> [[N]] ~'centres of all bins'.
		% centres :: dmap(N), [[M]->[N]] -> [[M]] ~'centres of selected bins'.
		function X=centres(M,I) 
			if nargin<2,I=range(M); end
			X=mean(M.map_ni(I),1);
		end

		% tostring - string describing dmap
		%
		% tostring :: dmap(N) -> string.
		function s=tostring(M)
			s=sprintf('dmap::%g--%g->[%d]',M.domain_(1),M.domain_(2),M.cardr_);
		end

		% DISPLAY - Display dmap object as string
		function display(c), disp(tostring(c)); end

		% domain - return domain of a dmap
		%
		% domain :: dmap(N) -> [[2]]
		function X=domain(M), X=M.domain_; end

		% edges - return edges of discretisation map
		%
		% edges :: dmap(N) -> [[N+1]].
		% edges :: dmap(N) [[M]->[N]-> [[M+1]].
		function X=edges(M,I), 
			if nargin<2,I=range(M); end
			Y=M.map_ni(I);
			X=[Y(1,1) Y(2,:)];
		end

		function I=feval(M,X), I=M.map_rn(X); end
%			if isa(M,'dmap'), I=M.map_rn(X);
%			else I=builtin('feval',M,X); end
%		end

		% map_clamped - Apply clamped discretisation map
		%
		% map_clamped :: dmap(N), real -> [N].
		function I=map_clamped(M,x)
			I=M.map_rn(x);
			I(I==-inf)=1;
			I(I==inf) =M.cardr_;
		end

		% range - range of dmap (set of natural numbers)
		%
		% range :: dmap(N) -> 1..N:[[N]->natural].
		function R=range(M), R=1:M.cardr_; end

		% SUBSREF - Subscript referencing for DMAP objects
		%
		% I=MAP(X) - Apply real-to-natural map to all elements of X
		function I=subsref(M,S)
			s=S(1);
			switch s.type
				case '()'
					I=M.map_rn(s.subs{1});
			end
		end
	end
end

