 %STAMP_MAP Stamping and storing of mappings for fast reusage
%
%		C = STAMP_MAP(A,W) or C = A*W
%		U = STAMP_MAP(V,W) or U = V*W
%
% This routine is equivalent to MAP except that it stores previous
% results (C or U) and retrieves them when the same inputs (A and W
% or V and W) are given. This is especially good in case of combining
% classifier studies when the same mappings have to be computed multiple
% times.
%
% Note that only the highest level of calls to MAP are stored by STAMP_MAP.
% If multiple calls to lower level calls (e.g. somewhere included in a
% complicated combined classifier) are expected, they should be called
% first explicitely.
% 
% STAMP_MAP is automatically called inside MAP (and thereby by every
% overloaded * operation between datasets and mappings) controlled by
% the following settings:
%
% STAMP_MAP(0)  - disables all storage and retrieval of mappings.
%                 This should be called when no duplicates are expected
%                 as it prevents the unnecessary checking of inputs.
% STAMP_MAP(1)  - Retrieving of stored results only only.
%                 No new results are stored.
% STAMP_MAP(2)  - Enables retrieval as well as storage of results.
%
% SEE ALSO (<a href="http://37steps.com/prtools">PRTools Guide</a>)
% DATASETS, MAPPINGS, MAP 

% Copyright: R.P.W. Duin, r.p.w.duin@37steps.com
% Faculty EWI, Delft University of Technology
% P.O. Box 5031, 2600 GA Delft, The Netherlands

function c = stamp_map(a,b)

	persistent CHECK_STAMPS nstamps cell_stamp stamps
	
	if isempty(CHECK_STAMPS)
		CHECK_STAMPS = 0;
		nstamps = 0;
		stamps = zeros(1000,3);
		cell_stamp = cell(1,1000);
	end
	
	if nargin == 0
		c = CHECK_STAMPS;
	elseif nargin == 1
		if a < 3
			CHECK_STAMPS = a;
			if nargout > 0 
				c = a;
			end
		else
			disp(nstamps)
			for j=1:nstamps
				fprintf('STAMPS: %10.6e %10.6e: ',stamps(j,:));
				cell_stamp{j}
			end
		end
		
	else 
			
		if CHECK_STAMPS == 0 % checking is off, no stamp_mapping
		
			c = []; % execution call: try to pass this routine
			
		elseif iscell(a) || iscell(b) % don't handle cell constructs
			
			c = [];
		
		elseif CHECK_STAMPS == 1 || CHECK_STAMPS == 2  % checking is on
			
			sa = getstamp(a); 
			sb = getstamp(b);
			Ja = find(stamps(1:nstamps,1)==sa);
			J = [];
			if ~isempty(Ja)
				Jb = find(stamps(Ja,2)==sb);
				if ~isempty(Jb)   % found, we have it!
					J = Ja(Jb);
					if ~isempty(J) && length(J) > 1
						error('Stamps are not unique')
					end 
					c = cell_stamp{J}; % use it!
					stamps(J,3) = stamps(J,3)+1;
					%disp(['found: ' getname(a) '..,..' getname(b)])
				end
			end
			if isempty(J)                       % not found, let map do the work
				checkstamps = CHECK_STAMPS;       % Take care that we skip stamp_map
				CHECK_STAMPS = checkstamps+2;     % when we call map
				c = prmap(a,b); % during execution of map CHECK_STAMPS will be reset
				CHECK_STAMPS = checkstamps;       % so this is in fact not needed
				if CHECK_STAMPS == 2        % store as well
					%disp(['stored: ' getname(a) '..,..' getname(b)])
					nstamps = nstamps + 1;
					if nstamps > length(cell_stamp)
						stamps = [stamps; zeros(1000,3)];
						cell_stamp = [cell_stamp cell(1,1000)];
					end 
					stamps(nstamps,1:2) = [sa,sb];
					cell_stamp{nstamps} = c; 
				end
			end
			
		elseif CHECK_STAMPS == 3 || CHECK_STAMPS == 4
			
			CHECK_STAMPS = CHECK_STAMPS-2;  % reset of computation of map
			c = [];
			
		else
			
			;
		
		end
		
	end
	
return 

%GETSTAMP Compute unique stamp for data
%
% S = GETSTAMP(A)
%
%Computes 64-bit stamp S of input A

function s = getstamp(a)

s = 0;
type = class(a);
	
switch(type)
	
	case 'char'
		a = abs(a);
		n = length(a(:));
		s = s + abs(sin(1:n))*(a(:));
		
	case 'double'
		n = length(a(:));
		s = s + abs(sin(1:n)*a(:));
		
	case {'prmapping','prdataset','prdatafile'}
		a = struct2cell(struct(a));
		s = s + getstamp(a);
		
	case 'struct'
		a = struct2cell(a);
		s = s + getstamp(a);
		
	case 'cell'
		for j=1:length(a(:))
			s = s + getstamp(a{j});
		end
		
	otherwise
		try
			n = length(a(:));
			s = s + abs(sin(1:n))*abs(double(a(:)));
		catch
			;
		end 
		
end