function Ud = som_unit_dists(topol,lattice,shape)

%SOM_UNIT_DISTS Distances between unit-locations on the map grid.
%
% Ud = som_unit_dists(topol,[lattice],[shape])
% 
%  Ud = som_unit_dists(sMap);
%  Ud = som_unit_dists(sMap.topol);
%  Ud = som_unit_dists(msize, 'hexa', 'cyl');
%  Ud = som_unit_dists([10 4 4], 'rect', 'toroid');
%
%  Input and output arguments ([]'s are optional): 
%   topol              topology of the SOM grid
%             (struct) topology or map struct
%             (vector) the 'msize' field of topology struct
%   [lattice] (string) map lattice, 'rect' by default
%   [shape]   (string) map shape, 'sheet' by default
%
%   Ud        (matrix, size [munits munits]) distance from each map unit 
%                      to each map unit
%
% For more help, try 'type som_unit_dists' or check out online documentation.
% See also SOM_UNIT_COORDS, SOM_UNIT_NEIGHS.

%%%%%%%%%%%%% DETAILED DESCRIPTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% som_unit_dists
%
% PURPOSE
%
% Returns interunit distances between the units of a Self-Organizing Map
% along the map grid.
%
% SYNTAX
%
%  Ud = som_unit_dists(sTopol);
%  Ud = som_unit_dists(sM.topol);
%  Ud = som_unit_dists(msize);
%  Ud = som_unit_dists(msize,'hexa');
%  Ud = som_unit_dists(msize,'rect','toroid');
%
% DESCRIPTION
%
% Calculates the distances between the units of a SOM based on the 
% given topology. The distance are euclidian and they are measured
% along the map grid (in the output space). 
%
% In case of 'sheet' shape, the distances can be measured directly
% from the unit coordinates given by SOM_UNIT_COORDS. 
%
% In case of 'cyl' and 'toroid' shapes this is not so. In these cases
% the coordinates are calculated as in the case of 'sheet' shape and
% the shape is then taken into account by shifting the map grid into
% different positions. 
%
% Consider, for example, a 4x3 map. The basic position of map units 
% is shown on the left (with '1' - 'C' each denoting one map unit). 
% In case of a 'cyl' shape, units on the left and right edges are
% neighbors, so for this purpose the map is copied on the left and
% right sides of the map, as on right. 
%
%    basic               left     basic    right
%    -------             -------  -------  -------
%    1  5  9             1  5  9  1  5  9  1  5  9
%    2  6  a             2  6  a  2  6  a  2  6  a  
%    3  7  b             3  7  b  3  7  b  3  7  b 
%    4  8  c             4  8  c  4  8  c  4  8  c 
% 
% For the 'toroid' shape a similar trick is done, except that the 
% copies are placed all around the basic position:
%
%             1  5  9  1  5  9  1  5  9
%             2  6  a  2  6  a  2  6  a  
%             3  7  b  3  7  b  3  7  b 
%             4  8  c  4  8  c  4  8  c 
%             1  5  9  1  5  9  1  5  9
%             2  6  a  2  6  a  2  6  a  
%             3  7  b  3  7  b  3  7  b 
%             4  8  c  4  8  c  4  8  c 
%             1  5  9  1  5  9  1  5  9
%             2  6  a  2  6  a  2  6  a  
%             3  7  b  3  7  b  3  7  b 
%             4  8  c  4  8  c  4  8  c 
%
% From this we can see that the distance from unit '1' is 1 to units
% '9','2','4' and '5', and sqrt(2) to units 'C','A','8' and '6'. Notice 
% that in the case of a 'hexa' lattice and 'toroid' shape, the size
% of the map in y-direction should be even. The reason can be clearly
% seen from the two figures below. On the left the basic positions for
% a 3x3 map. If the map is copied above itself, it can be seen that the
% lattice is broken (on the right):
%
%     basic positions                 example of broken lattice
%     ---------------                 -------------------------
%                                     1  4  7 
%                                      2  5  8
%                                     3  6  9
%     1  4  7                         1  4  7 
%      2  5  8                         2  5  8
%     3  6  9                         3  6  9
%
% 
% REQUIRED INPUT ARGUMENTS
% 
%  topol          Map grid dimensions.
%        (struct) topology struct or map struct, the topology 
%                 (msize, lattice, shape) of the map is taken from 
%                 the appropriate fields (see e.g. SOM_SET)
%        (vector) the vector which gives the size of the map grid
%                 (msize-field of the topology struct).
%  
% OPTIONAL INPUT ARGUMENTS 
% 
%  lattice (string) The map lattice, either 'rect' or 'hexa'. Default
%                   is 'rect'. 'hexa' can only be used with 1- or 
%                   2-dimensional map grids.
%  shape   (string) The map shape, either 'sheet', 'cyl' or 'toroid'. 
%                   Default is 'sheet'. 
%
% OUTPUT ARGUMENTS
%
%  Ud   (matrix) distances from each map unit to each other map unit,
%                size is [munits munits]
%
% EXAMPLES
%
% Simplest case:
%  Ud = som_unit_dists(sTopol);
%  Ud = som_unit_dists(sMap.topol);
%  Ud = som_unit_dists(msize);
%  Ud = som_unit_dists([10 10]);
%
% If topology is given as vector, lattice is 'rect' and shape is 'sheet'
% by default. To change these, you can use the optional arguments:
%  Ud = som_unit_dists(msize, 'hexa', 'toroid');
%
% The distances can also be calculated for high-dimensional grids:
%  Ud = som_unit_dists([4 4 4 4 4 4]);
%
% SEE ALSO
% 
%  som_unit_coords   Calculate grid coordinates.
%  som_unit_neighs   Calculate neighborhoods of map units.

% Copyright (c) 1997-2000 by the SOM toolbox programming team.
% http://www.cis.hut.fi/projects/somtoolbox/

% Version 1.0beta juuso 110997
% Version 2.0beta juuso 101199 170400 070600 130600

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Check arguments 

error(nargchk(1, 3, nargin));

% default values
sTopol = som_set('som_topol','lattice','rect');

% topol
if isstruct(topol), 
  switch topol.type, 
  case 'som_map', sTopol = topol.topol;
  case 'som_topol', sTopol = topol;
  end
elseif iscell(topol), 
  for i=1:length(topol), 
    if isnumeric(topol{i}), sTopol.msize = topol{i}; 
    elseif ischar(topol{i}),  
      switch topol{i}, 
      case {'rect','hexa'}, sTopol.lattice = topol{i}; 
      case {'sheet','cyl','toroid'}, sTopol.shape = topol{i}; 
      end
    end
  end
else
  sTopol.msize = topol;
end
if prod(sTopol.msize)==0, error('Map size is 0.'); end

% lattice
if nargin>1 & ~isempty(lattice) & ~isnan(lattice), sTopol.lattice = lattice; end

% shape 
if nargin>2 & ~isempty(shape) & ~isnan(shape), sTopol.shape = shape; end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Action

msize = sTopol.msize;
lattice = sTopol.lattice;
shape = sTopol.shape;

munits = prod(msize);
Ud = zeros(munits,munits);

% free topology
if strcmp(lattice,'free'),
  N1 = sTopol.connection; 
  Ud = som_neighborhood(N1,Inf);
end

% coordinates of map units when the grid is spread on a plane
Coords = som_unit_coords(msize,lattice,'sheet');

% width and height of the grid
dx = max(Coords(:,1))-min(Coords(:,1));
if msize(1)>1, dx = dx*msize(1)/(msize(1)-1); else dx = dx+1; end
dy = max(Coords(:,2))-min(Coords(:,2));
if msize(2)>1, dy = dy*msize(2)/(msize(2)-1); else dy = dy+1; end

% calculate distance from each location to each other location
switch shape, 
case 'sheet',
  for i=1:(munits-1), 
    inds = [(i+1):munits]; 
    Dco = (Coords(inds,:) - Coords(ones(munits-i,1)*i,:))'; 
    Ud(i,inds) = sqrt(sum(Dco.^2));
  end

case 'cyl', 
  for i=1:(munits-1), 
    inds = [(i+1):munits]; 
    Dco  = (Coords(inds,:) - Coords(ones(munits-i,1)*i,:))'; 
    dist = sum(Dco.^2);
    % The cylinder shape is taken into account by adding and substracting
    % the width of the map (dx) from the x-coordinate (ie. shifting the
    % map right and left).
    DcoS = Dco; DcoS(1,:) = DcoS(1,:) + dx;  %East (x+dx)
    dist = min(dist,sum(DcoS.^2));
    DcoS = Dco; DcoS(1,:) = DcoS(1,:) - dx;  %West (x-dx)
    dist = min(dist,sum(DcoS.^2));
    Ud(i,inds) = sqrt(dist);
  end

case 'toroid', 
  for i=1:(munits-1), 
    inds = [(i+1):munits]; 
    Dco  = (Coords(inds,:) - Coords(ones(munits-i,1)*i,:))'; 
    dist = sum(Dco.^2); 
    % The toroid shape is taken into account as the cylinder shape was 
    % (see above), except that the map is shifted also vertically.
    DcoS = Dco; DcoS(1,:) = DcoS(1,:) + dx;  %East (x+dx)
    dist = min(dist,sum(DcoS.^2));
    DcoS = Dco; DcoS(1,:) = DcoS(1,:) - dx;  %West (x+dx)
    dist = min(dist,sum(DcoS.^2));
    DcoS = Dco; DcoS(2,:) = DcoS(2,:) + dy;  %South (y+dy)
    dist = min(dist,sum(DcoS.^2));
    DcoS = Dco; DcoS(2,:) = DcoS(2,:) - dy;  %North (y-dy)
    dist = min(dist,sum(DcoS.^2));
    DcoS = Dco; DcoS(1,:) = DcoS(1,:) + dx; DcoS(2,:) = DcoS(2,:) - dy; %NorthEast (x+dx, y-dy)
    dist = min(dist,sum(DcoS.^2));
    DcoS = Dco; DcoS(1,:) = DcoS(1,:) + dx; DcoS(2,:) = DcoS(2,:) + dy; %SouthEast (x+dx, y+dy)
    dist = min(dist,sum(DcoS.^2));
    DcoS = Dco; DcoS(1,:) = DcoS(1,:) - dx; DcoS(2,:) = DcoS(2,:) + dy; %SouthWest (x-dx, y+dy)
    dist = min(dist,sum(DcoS.^2));
    DcoS = Dco; DcoS(1,:) = DcoS(1,:) - dx; DcoS(2,:) = DcoS(2,:) - dy; %NorthWest (x-dx, y-dy)
    dist = min(dist,sum(DcoS.^2));
    Ud(i,inds) = sqrt(dist);
  end

otherwise, 
  error (['Unknown shape: ', shape]);

end

Ud = Ud + Ud';

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
