function latout(fname, c, rgb, tracein)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% function latout(fname, c, rgb, tracein)
%
% Writes lattice values---such as those associated with a two-dimensional
% cellular automaton---in the format required by xflat2d_rgb.
%
% INPUT ARGUMENTS
%
%   fname (character string): Name of output file.
%
%   c (3D array, dimensions nx x ny x nt): Array of lattice values. 
%     Lattice is nx x ny, and nt time steps are stored.  All values
%     must be integers in the range minstate : maxstate, so that the 
%     total number of states is nstate = maxstate - minstate + 1.  
%
%   rgb (2D array, dimensions nstate x 3): Defines RGB colour
%     that will be used for each cell.  (rgb(i,1), rgb(i,2), rgb(i,3))
%     defines the RGB triple, (red, green, blue), for state i.  
%     Each RGB value must be in the range 0.0 to 1.0.  More than
%     nstate colours can be specified, but excess ones will be ignored.
%
%   tracein (OPTIONAL: integer scalar): If supplied and non-zero, then
%     diagnostic output will be written to terminal.  Tracing will 
%     occur every 'tracein' steps in main time loop.
%
% OUTPUT ARGUMENTS
%
%   None
%
% WARNING! This routine will overwrite the file 'fname' if it already
% exists, so be sure to rename/move the existing file, should you 
% wish to save its contents.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

   % Function name: used in diagnostic messages, and expedites reuse 
   % of code in other routines ...
   S = 'latout';

   % Gets set to 1 if there are one or more errors detected in input
   % arguments ...
   err = 0;

   % Check that minimum number or arguments have been supplied ...
   if nargin < 3
      fprintf('%s: Function needs at least three input arguments.\n', S);
      fprintf('%s: Type ''help %s'' for usage information.\n', S, S);
      return;
   end

   % Check for optional tracing argument ...
   trace = 0;
   if nargin > 3
      trace = tracein;
   end

   % Check that fname is a string ...
   if ~ ischar(fname)
      fprintf('%s: Input argument [fname] must be a string.\n', S);
      err = 1;
   end

   % Check that state array is 3-dimensional ...
   rankc = length(size(c));
   if rankc ~= 3
      fprintf('%s: Input argument [c]  must be a three-dimensional array.\n', S);
      err = 1;
   end

   % Determine minimum state, maximum state, number of states and check 
   % that all values of state array are positive integers ...
   minstate = min(min(min(c)));
   maxstate = max(max(max(c)));
   nstate = maxstate - minstate + 1;
   if trace
      fprintf('%s: minstate=%g maxstate=%g nstate=%g\n', ...
         S, minstate, maxstate, nstate);
   end

   if (minstate < 0) | (sum(sum(fix(c)-c)) ~= 0)
      fprintf('%s: Values of input argument [c] should be non-negative integers\n', S);
      fprintf('%s: and at least some are apparently not.\n', S);
      return;
   end

   % Check for validity of rgb argument ...
   sizergb = size(rgb);
   if length(sizergb) ~= 2
      fprintf('%s: Input argument [rgb] must be a two-dimensional array.\n', S);
      err = 1;
   else
      if sizergb(2) ~= 3
         fprintf('%s: Second dimension of input argument [rgb] must be 3 but is %d.\n', S, sizergb(2));
         err = 1;
      end
      if sizergb(1) < nstate
         fprintf('%s: Input argument [rgb] defines too few RGB values.\n', S);
         fprintf('%s: Need at least %d, but only %d supplied.\n', S, nstate, sizergb(1));
         err = 1;
      else
         if min(min(rgb)) < 0.0 | max(max(rgb)) > 1.0
            fprintf('%s: [rgb] values must be in range 0.0 ... 1.0\n', S);
            fprintf('%s: [rgb] dump follows\n', S);
            for i = 1 : sizergb(1)
               fprintf('  state=%d  r=%g  g=%g  b=%g\n', minstate + i -1, ...
                  rgb(i,1), rgb(i,2), rgb(i,3));
            end
            err = 1;
         else
            if trace
               fprintf('%s: Number of colours in RGB=%d\n', S, sizergb(1));
            end
         end
      end
   end

   % Bail out if any errors were detected in input arguments ... 
   if err == 1
      return
   end

   [nx ny nt] = size(c);
   if trace
      fprintf('%s: nx=%d ny=%d nt=%d\n', S, nx, ny, nt);
   end

   % Open file for write ...
   fid = fopen(fname, 'w');
   if( fid < 0 ) 
      fprintf('%s: Error opening ''%s'' for write.\n', S, fname);
      return;
   end

   % Offset to convert state value to rgb index
   o = -minstate + 1;

   % Write nx, ny 
   fprintf(fid, '%d %d\n', nx, ny);

   cout = zeros(4, nx, ny);

   % For each time step ...
   for n = 1 : nt

      % Write time ...
      fprintf(fid, '%d\n', n);

      % Convert state values to array in format needed by
      % xflat2d_rgb and write ..
      cout(1, :, :) = c(:, :, n);
      red = rgb(:,1);
      cout(2, :, :) = red(c(:, :, n) + o);
      blue = rgb(:,2);
      cout(3, :, :) = blue(c(:, :, n) + o);
      green = rgb(:,3);
      cout(4, :, :) = green(c(:, :, n) + o);
      fprintf(fid, '%g ', cout);
      fprintf(fid, '\n\n', n);
      if ~mod(n,trace) 
         fprintf('%s: Wrote %d of %d\n', S, n, nt);
      end
   end
   if trace
      fprintf('%s: Wrote nx=%d ny=%d nt=%d to file=%s \n', ...
         S, nx, ny, nt, fname);
   end

   % Close file and return ...
   fclose(fid);
end
