classdef Eat < handle
%
% PURPOSE:   Track daily calories and Weight Watcher (old system) points.
%
% INPUTS:    
%
% CHANGELOG: 1) 25 Dec 2015 rsm, initial.
%
% ToDo: 1) Put cals and accum cals columns next to each other.
%       2) Add a fn to write to eat.weight(). Have a varargin that is a
%          datenum for previous days.
%       3) Add weekend bars.  Use datetime?
%       4) Embed cereal amounts in description.
%       5) Generalize add() and addNew to take datenums.  Get rid of insert().
%       6) Generalize "delete" to delete data on previous days.
%       7) Add help.
%       8) Average and 1st order fit for selectable interval.
%       9) Generalize "cereal" so it can insert data.
%
  properties (SetAccess = private)
    startDateNum;
    len;              %Length of database array from Excel.
  end
  properties
    found;
    nfound;
    data;
    nweeks = 4;
    log;
    weightLog;
    todaysDate;
    indx;             %Index into today's items.
    indxDay;          %Index into the total for each day.
    indxW;            %Index into weekends.
    position;
    healthDataFig;
    thandle;
    ahandle;
    fileName;
    la;
    ra;
  end 
  methods
    function eat = Eat()
      eat.readData([]);
      eat.log = {'Food','Matlab Time','Grams','Calories','Points', ...
		       'Cum Cal', 'Cum Points'};
      eat.weightLog = { [], [] };
      eat.todaysDate = now;
      todaysDate = floor(now);
      eat.indx = find( todaysDate == floor(cell2mat(eat.log(2:end,2))) ) + 1;
      eat.fileName = [datestr(now,'yyyymmdd') '.mat'];
      eat.load;
      eat.sofar;
        
      co = [0 0 1;
	    0 0.5 0;
	    1 0 0;
	    0 0.75 0.75;
	    0.75 0 0.75;
	    0.75 0.75 0;
	    0.25 0.25 0.25];
      set(groot,'defaultAxesColorOrder',co)

      MonitorPositions = get(0,'MonitorPositions');
      position = MonitorPositions(end,:);
      eat.position(1:2) = position(1:2) + round([position(3)*.02 position(4)*.04]);
      eat.position(3:4) = round([position(3)*.95 position(4)*.90]);
    end
    %
    %
    function readData(eat, name)
      if( isempty(name) ) name = '~/Downloads/PointsChart.xlsx'; end
      if( exist(name,'file') )
	try
	  fprintf('Loading %s.\n', name);
	  %load(name);
	  [num txt eat.data]=xlsread(name);
	catch ME
%	  fprintf('Error opening or reading NtrlData: %s\n',...
%		  ME.ERRMSG);
	  return
	end
	eat.len = length(eat.data);
      else
	fprintf('Couldn''nt find the data.\n');
	return
      end
      fprintf('Read %d records from %s.\n',eat.len, name);
      ii=1;
      while(ii<=1:eat.len) 
	if( ~ischar(eat.data{ii,1} ) )
	  fprintf('Line %d of %s contains non-character data.  Deleting.\n',...
		  ii, name );
	  eat.data(ii,:) = [];
	  eat.len=eat.len-1;
	end
	ii = ii+1;
      end
    end
    function plot(eat)
      indx  = diff(floor(cell2mat(eat.log(3:end,2))));
      indxx = find(indx.*[1:length(indx)]')+2;     %Account for the header
      %indxW = isweekend(cell2mat(eat.log(3:end,2)));
      %indxW = find(indxW.*[1:length(indxW)]')+1;   %Account for the header
      %eat.indxW=indxW;
      %
      %Add yesterday's if there is no data for today in the log
      if( floor(cell2mat(eat.log(end,2))) ~= floor(now) )
       indxx = [indxx; length(eat.log)];  
      end
      eat.indxDay = indxx;

      if( isempty(eat.healthDataFig) )
	eat.healthDataFig = figure;
	set( eat.healthDataFig, 'Name', 'Weight Control Data', 'DeleteFcn', 'eat.healthDataFig=[];');
	set(gcf,'position',eat.position)
	fontsize = 20;
	fontweight = 'normal';
	set(gca,'fontsize',fontsize,'fontweigh',fontweight)
	set(gcf,'defaultaxesfontsize',fontsize,'defaulttextfontsize',fontsize)
      end
      figure(eat.healthDataFig);

      markersize=15;
      subplot(2,1,1)
      %plot(cell2mat(eat.log(indxx,2)),cell2mat(eat.log(indxx,6))/50,'ro-','markersize',markersize)
      useDateTime = 1;
      if(useDateTime)
	taxis = datetime(cell2mat(eat.log(indxx,2)), 'convertFrom', 'datenum');
      else
	taxis = cell2mat(eat.log(indxx,2));
      end
      plot(taxis, cell2mat(eat.log(indxx,6))/50,'ro-','markersize',markersize)
      hold on;
      grid on;
      %plot(cell2mat(eat.log(indxx,2)),cell2mat(eat.log(indxx,7)),'bx-','markersize',markersize)
      plot(taxis, cell2mat(eat.log(indxx,7)),'bx-','markersize',markersize)
      title('Weight Control Data');
      %ylabel('Calories/50, WW Points, Weight, lbf')
      ylabel('Calories/50, WW Points, Weight, lbf')
      legend('Calories/50','WW Points','location','best');
      %
      % Annotations:
      eat.thandle.Cienaga   = text(datenum(2016,3,11), 68, 'Cienaga');
      eat.thandle.Gville    = text(datenum(2016,3,18), 99, 'GeyserV');
      eat.thandle.Fremont   = text(datenum(2016,3,24), 67, 'Fremont');
      eat.thandle.HMB       = text(datenum(2016,4,1),  107, 'HMB');
      eat.thandle.Primavera = text(datenum(2016,4,16), 103, 'Primavera');
      eat.thandle.MCharlie  = text(datenum(2016,4,22), 90, 'Mtn. Charlie');
      eat.thandle.TB        = text(datenum(2016,4,29), 105, 'TB27');
      eat.thandle.TUC       = text(datenum(2016,5,6),  105, 'TUC22');
      eat.thandle.Diablo    = text(datenum(2016,5,13), 90,  'Diablo');
      eat.thandle.Gizditch  = text(datenum(2016,5,20), 88,  'Gizditch');
      eat.thandle.HamLDT    = text(datenum(2016,5,30), 77,  'Ham-LDT');
      eat.thandle.Sequoia   = text(datenum(2016,6,5),  103, 'Sequoia');
      eat.thandle.HamBack   = text(datenum(2016,7,5),  82,  'Ham-Back');
      eat.thandle.TomReyes  = text(datenum(2016,8,18), 82,  'Tomales-Reyes');
      eat.thandle.LaHGazos  = text(datenum(2016,9,3),  78,  'LaHonda-Gazos');

      if( ~useDateTime ) 
	datetick('x',1);
      end
      %
      % sampling period = 1 day, 1/2 sampling freq = 2 days.  10 day
      % cutoff frequency, thus 2/10 is normalized omegan.
      [bb aa]= butter(4,2/10);
      eat.la=subplot(2,1,2);
      plot(taxis, cell2mat(eat.log(indxx,6)),'ro-', 'markersize',markersize)
      hold on;
      %tcdays = cell2mat(eat.log(indxx,2));
      tcdays = taxis;
      tccals = cell2mat(eat.log(indxx,6));
      %
      % Remove Nans:
      nindx = ~isnan(tccals);
      tcdays = tcdays(nindx);
      tccals = tccals(nindx);
      %
      plot(eat.la, tcdays, filtfilt(bb,aa,tccals), 'g');
      %
      posn=get(eat.la,'position');
      xlim=get(eat.la,'xlim');
      if( useDateTime )
	%
	% Because xlim above chops off the last day of weightLog.
	xlim(2) = xlim(2)+3;
      end
      xlim(1) = 736330; % 1 Jan 2016
      eat.ra=axes('position',posn,'yaxislocation','right',...
		  'ylim',[170 176],'xlim',xlim);
      set(eat.ra,'color','none','xtick',[]);
      % [170 1;175 1]\[2500;4500] = [ 400.00;  -65500.00]
      %plot(eat.ra,cell2mat(eat.weightLog(:,1)), ...
      %	   cell2mat(eat.weightLog(:,2))*400-65500 ,'bd-')
      hold on
      plot(eat.ra,cell2mat(eat.weightLog(:,1)), ...
      	   cell2mat(eat.weightLog(:,2)),'bd-')
      %
      % Now filter weightLog:
      tdays = cell2mat(eat.weightLog(:,1));
      wdays = cell2mat(eat.weightLog(:,2));
      %
      % Remove Nans:
      nindx = ~isnan(wdays);
      tdays = tdays(nindx);
      wdays = wdays(nindx);
      plot(eat.ra, tdays, filtfilt(bb,aa,wdays), 'm');
      ylabel(eat.ra,'Weight, Blue')
      ylabel(eat.la,'Calories, Red')
      xlabel(eat.la,'Time, Days');
      set(eat.la,'ygrid', 'on', 'xgrid','on');
      if( ~useDateTime )
	datetick(eat.la,'x',1);
      end
      set(eat.ra,'xtick',[]);
      set(eat.la,'ygrid', 'on', 'xgrid','on');
      %legend('Calories','Weight','location','best');
      
      
      linkaxes(get(gcf,'children'),'x')
      
    end
    function find(eat, food)
      if( isempty(food) )
	fprintf('find(food) - Finds the string ''food'' in the database.\n');
	return
      end
      if( ~ischar(food) )
	fprintf('Add - Error: Please profide a character string.\n');
	return
      end
      jj=0;
      eat.found = [];
      for(ii=1:eat.len) 
	name = eat.data{ii,1};
	if( ~ischar(name) )
	  continue;
	end
	%if(regexp(char(eat.data{ii,1}),food))
	if(regexp(name,food))
	  jj = jj + 1;
	  eat.found(jj,1) = ii;
	  fprintf(['%s\n',...
		   '    Weight   = %.1f\n',...
		   '    Calories = %.1f\n',...
		   '    Points   = %.1f\n',...
		   '    Cal/100g = %.1f\n',... 
		   '    Points/100g = %.1f\n'],...
		  eat.data{ii,1}, eat.data{ii,2}, ...
		  eat.data{ii,3}, ...
		  eat.data{ii,9}, ...
		  eat.data{ii,8}, eat.data{ii,10});
	end
      end
      if( isempty(eat.found) )
	fprintf('%s not found.\n', food);
	eat.nfound = 0;
	return
      end
      eat.nfound = length(eat.found);
      if( eat.nfound > 1 )
	fprintf('Found %d of them.\n', eat.nfound);
      end
    end
    function add(eat, food, varargin)
      find(eat, food);
      if( eat.nfound > 1)
	fprintf('Please select only one.\n')
	return
      end
      if( length(varargin) == 0 )
	grams = eat.data{eat.found,2};
      elseif( length(varargin) == 1 )
	grams = varargin{1};
      else
	fprintf('Warning:  eat.add has %d arguments.\n', length(varargin));
      end
      addg(eat, grams);
      eat.sofar
    end
    function insertg(eat, food, varargin)
    %
    % 'Name', grams, datenum.
      find(eat, food);
      if( eat.nfound > 1)
	fprintf('Please select only one.\n')
	return
      elseif( eat.nfound == 0)
	return
      end
      when = now;
      if( length(varargin) == 0 )
	grams = eat.data{eat.found,2};
      elseif( length(varargin) == 1 )
	grams = varargin{1};
      elseif( length(varargin) == 2 )
	if( isempty(varargin{1}) )
	  grams = eat.data{eat.found,2};
	else
	  grams = varargin{1};
	end
	when = varargin{2};
	%
	% varargin{2} must be a datenum.
	locIndx = indxfn2(when,cell2mat(eat.log(2:end,2)))+1;
	newrow = {eat.data{eat.found,1}, when, grams, ...
		  eat.data{eat.found,8}*grams/100, ...
		  eat.data{eat.found,10}*grams/100, [] []};
	eat.log = [eat.log(1:locIndx,:); newrow; eat.log(locIndx+1:end,:)];
	indx = find( floor(when) == floor(cell2mat(eat.log(2:end,2))) ) + 1;
	lenIndx = length(indx);
	len = length(eat.log(:,1));
	if( len == 1 || lenIndx == 0 )
	  fprintf('No data yet.\n');
	  return
	end
	%
	% Recompute columns that accumulate:
	%
	if( len == 1 || lenIndx == 0 )
	  return
	elseif( len == 2 || lenIndx < 2 )
	  eat.log{indx,6} = eat.log{indx,4};
	  eat.log{indx,7} = eat.log{indx,5};
	  return
	end
	for( ii=2:lenIndx )       %Already deleted a row.
	  eat.log{indx(ii),6} = eat.log{indx(ii),4} + ...
	      eat.log{indx(ii-1),6};
	  eat.log{indx(ii),7} = eat.log{indx(ii),5} + ...
	      eat.log{indx(ii-1),7};
	end
      else
	fprintf('Warning:  eat.add has %d arguments.\n', length(varargin));
      end
      eat.sofar(when)
    end
    function insertRow(eat, food, when, grams, cal, pts)
    %
    % insertRow(eat, food, when, grams, cal, pts)
    %    Insert a new row into the log, in a past day.
    %        food    - A string describing the food.
    %        when    - The date and time, in Matlab's serial date form. 
    %        grams   - Weight of the food in grams, assuming
    %                  g=9.806~m/s^2. 
    %        cal     - Food calories (science kCals).
    %        pts     - Weight watcher points.
    %
      if( ~ischar(food) ) 
	fprintf('insertRow - Error.  Please enter a string.\n');
	return
      end
      newrow = {food, when, grams, cal, pts, [] []};
      locIndx = indxfn2(when,cell2mat(eat.log(2:end,2)))+1;
      eat.log = [eat.log(1:locIndx,:); newrow; eat.log(locIndx+1:end,:)];
      indx = find( floor(when) == floor(cell2mat(eat.log(2:end,2))) ) + 1;
      lenIndx = length(indx);
      len = length(eat.log(:,1));
      if( len == 1 || lenIndx == 0 )
	fprintf('No data in the log yet.\n');
	return
      end
      %
      % Recompute columns that accumulate:
      %
      if( len == 1 || lenIndx == 0 )
	return
      elseif( len == 2 || lenIndx < 2 )
	eat.log{indx,6} = eat.log{indx,4};
	eat.log{indx,7} = eat.log{indx,5};
	return
      end
      for( ii=2:lenIndx )       %Already deleted a row.
	eat.log{indx(ii),6} = eat.log{indx(ii),4} + ...
	    eat.log{indx(ii-1),6};
	eat.log{indx(ii),7} = eat.log{indx(ii),5} + ...
	    eat.log{indx(ii-1),7};
      end
    end
    function addg(eat, grams)
    %
    % Assumes "find" has already been called.
    %
      len = length(eat.log(:,1));
      len = len+1;
      eat.log{len,1} = eat.data{eat.found,1};
      %eat.log{len,2} = datestr(now,'HH:MM:SS');
      eat.log{len,2} = now;
      eat.log{len,3} = grams;
      eat.log{len,4} = eat.data{eat.found,8 }*grams/100; %Cal
      eat.log{len,5} = eat.data{eat.found,10}*grams/100; %Pts
      if( len == 2 || length(eat.indx) == 0 )
	eat.log{len,6} = eat.log{len,4};
	eat.log{len,7} = eat.log{len,5};
      else
	eat.log{len,6} = eat.log{len,4} + ...
	                       eat.log{len-1,6};
	eat.log{len,7} = eat.log{len,5} + ...
	                       eat.log{len-1,7};
      end
    end
    function addi(eat, food, varargin)
      find(eat, food);
      if( length(varargin) == 0 )
	grams = eat.data{eat.found,2};
      elseif( length(varargin) == 1 )
	grams = varargin{1}*eat.data{eat.found,2};
      end
      addg(eat, grams);
      if( length(varargin) == 1 )
	varargin{1}
	eat.log{end,1} = ['(',num2str(varargin{1}),') ',eat.log{end,1}];
      end
      eat.sofar
    end
    function addNew(eat, food, grams, cal, pts, varargin)
    %
    % addNew( food, grams, cal, pts, optional-datenum)
    %   food    - Text string describing the food.
    %   grams   - Weight in grams (assuming g=9.806 m/s^2).
    %   cal     - Calories per gram * 100.
    %   pts     - Points per gram * 100.
    %   datenum - (Optional) date and time to insert, if not now.
    %
      if( ~ischar(food) ) 
	fprintf('Please enter a string.\n');
	return
      end
      %
      % If the item is for today:
      if( length(varargin) == 0 )
	when = now;
	len = length(eat.log(:,1));
	len = len+1;
	eat.log{len,1} = food;
	eat.log{len,2} = when;
	eat.log{len,3} = grams;
	eat.log{len,4} = grams*cal/100; %Cal
	eat.log{len,5} = grams*pts/100; %Pts
	if( len == 2 || length(eat.indx) == 0 )
	  eat.log{len,6} = eat.log{len,4};
	  eat.log{len,7} = eat.log{len,5};
	else
	  eat.log{len,6} = eat.log{len,4} + ...
	      eat.log{len-1,6};
	  eat.log{len,7} = eat.log{len,5} + ...
	      eat.log{len-1,7};
	end
	% If you ate the item yesterday or before:
      elseif( length(varargin) == 1 )
	when = varargin{1};
	%
	% varargin{2} must be a datenum.
	insertRow( eat, food, when, grams, cal, pts );
	% locIndx = indxfn2(when,cell2mat(eat.log(2:end,2)))+1;
	% newrow = {food, when, grams, ...
	% 	  cal*grams/100, ...
	% 	  pts*grams/100, [] []};
	% eat.log = [eat.log(1:locIndx,:); newrow; eat.log(locIndx+1:end,:)];
	% indx = find( floor(when) == floor(cell2mat(eat.log(2:end,2))) ) + 1;
	% lenIndx = length(indx);
	% len = length(eat.log(:,1));
	% if( len == 1 || lenIndx == 0 )
	%   fprintf('No data yet.\n');
	%   return
	% elseif( len == 2 || lenIndx < 2 )
	%   eat.log{indx,6} = eat.log{indx,4};
	%   eat.log{indx,7} = eat.log{indx,5};
	%   return
	% end
	% for( ii=2:lenIndx )       %Already deleted a row.
	%   eat.log{indx(ii),6} = eat.log{indx(ii),4} + ...
	%       eat.log{indx(ii-1),6};
	%   eat.log{indx(ii),7} = eat.log{indx(ii),5} + ...
	%       eat.log{indx(ii-1),7};
	% end
      else
	fprintf('Warning:  eat.addNew has %d arguments.\n', length(varargin));
      end
      eat.sofar(when)
    end
    function sofar(eat, varargin)
      todaysDate = floor(now);
      notToday=0;
      eat.indx = find( todaysDate == floor(cell2mat(eat.log(2:end,2))) ) + 1;
      if( isempty(varargin) )
	indx = eat.indx;
      else
	notToday=1;
	if( ischar(varargin{1}) )
	  %
	  % Can't find "vectorized" way to do this, yet.
	  %indx     = find( (regexp(eat.log(:,1), varargin{1} )));
	  indxcell = regexp(eat.log(:,1), varargin{1} );
	  for( i=1:length(indxcell) )
	    if( ~isempty(indxcell{i}) )
	      indxcell{i} = i;
	    end
	  end
	  indx = cell2mat(indxcell);
	elseif( isnumeric(varargin{1}) )
	  indx = find( varargin{1} == floor(cell2mat(eat.log(2:end,2))) ) + 1;
	end
      end
      lenIndx = length(indx);
      if( isempty(lenIndx) || lenIndx == 0)
	fprintf('No data yet.\n');
	return
      end
      %fprintf('\n  %s\n\n',eat.todaysDate);
      if( notToday & isnumeric(varargin{1}) )
	datestring = datestr(floor(varargin{1}));
      else
	datestring = datestr(todaysDate);
      end
      fprintf('\n                    %s\n\n',datestring);
      fprintf('%3s %-25s %20s %10s %10s %10s %10s %10s\n',...
	      'No','      Food','Time      ','Weight','Calories','Points',...
	      'Cum Cals','Cum Pts');
      accumCals=0;
      accumPts =0;
      for( ii=1:lenIndx )
	name=eat.log{indx(ii),1};
	if( length(name) > 25 )
	  name = name(1:25);
	end
	if( notToday & ~isnumeric(varargin{1}) )
	  %datestring = datestr(eat.log{indx(ii),2},'yy-mmm-dd HH:MM:SS');
	  datestring = datestr(eat.log{indx(ii),2});
	  accumCals = accumCals + eat.log{indx(ii),4};
	  accumPts  = accumPts  + eat.log{indx(ii),5};
	else
	  datestring = datestr(eat.log{indx(ii),2},'HH:MM:SS');
	  accumCals = eat.log{indx(ii),6};
	  accumPts  = eat.log{indx(ii),7};
	end
	fprintf('%3d %-25s %20s %10d %10d %10.2f %10d %10.2f\n',...
	ii, ...                            %No
	name,...                           %Name
	datestring,...  %Time
	round(eat.log{indx(ii),3}),...     %Weight
	round(eat.log{indx(ii),4}),...     %Cals
	eat.log{indx(ii),5},...            %Pts
	round(accumCals),...
	accumPts);
	%round(eat.log{indx(ii),6}),...     %Accum Cals
	%eat.log{indx(ii),7});              %Accum Pts.
      end
      eat.save;
    end
    function save( eat )
      log = eat.log;
      weightLog = eat.weightLog;
      SLog=whos('log');
      SWLog=whos('weightLog');
      eat.fileName = [datestr(now,'yyyymmdd') '.mat'];
      save(['~/Documents/MATLAB/' eat.fileName],'log','weightLog');
      fprintf('The log has %d bytes, %d records.\n',...
	      SLog.bytes, length(log));
      fprintf('The weight log has %d bytes, %d records.\n',...
	      SWLog.bytes, length(weightLog));
      [stat str] = system(['du -h ~/Documents/MATLAB/' eat.fileName]);
      fprintf('The log file (both the above), consumes %s on disk.\n', strtok(str));
      if( exist('/Volumes/rob/rob/data') == 7 ) 
	tic
	save(['/Volumes/rob/rob/data/' eat.fileName],'log','weightLog');
	fprintf('The save to Atlas took %.1f seconds.\n',toc);
      end
    end
    function load( eat, varargin )
      if( isempty(varargin) )
        %fileName = datestr(now,'yyyymmdd');
	%if( exist([fileName,'.mat']) ~= 2 )
	fileName = eat.fileName;
	if( exist(eat.fileName) ~= 2 )
	  fileName = datestr(now-1,'yyyymmdd');
	  if( exist([fileName,'.mat']) ~= 2 )
	    fprintf('Can''t find yesterday''s data. Skip the load.\n');
	    return;
	  end
	  %
	  % Load yesterday's data:
	  fprintf('Loading yesterday''s data.\n');
	end
      else
	%
	% Assume the user inputs a datenum;
        fileName = varargin{1};
        fileName = [fileName,'.mat'];
      end
      load(fileName,'log');
      S=whos('log');
      eat.log=log;
      fprintf('The log has %d bytes.\n', S.bytes);
      if( ~isempty(whos('-file',fileName,'weightLog')))
	load(fileName,'weightLog');
	SW=whos('weightLog');
	eat.weightLog = weightLog;
	clear weightLog;
	fprintf('The weight log has %d bytes.\n', SW.bytes);
	clear weightLog;
      end
      clear log;
    end
    function delete( eat, row, varargin )
    %
    % delete( row, date )
    %   row    - Row number.
    %   date   - Matlab serial date number of the year, month and day.
    %            This is most easily computed with datenum().
    %
    %            If you omit the date, it assumes today.
    %
    % This is a companion function to sofar().  To use this, You must
    % first execute sofar() to find the row number of the item you want to
    % delete. Then pass "delete()" that row number and the serial date
    % number computed by datenum().  Hours, minutes and seconds are
    % ignored and instead the algorithm uses the row number.
    %
      %row = row+1;     %Make it correspond to the cell array.
      indx = eat.indx;
      lenIndx = length(indx);
      if( length(varargin) ~= 0 )
	pastDate = floor(varargin{1});
	indx = find( pastDate == floor(cell2mat(eat.log(2:end,2))) ) + 1;
	lenIndx = length(indx);
      end
      len = length(eat.log(:,1));
      if( len == 1 || lenIndx == 0 )
	fprintf('No data yet.\n');
	return
      end
      if( row > len || row < 1 || row > lenIndx )
	fprintf('Row %d is out of bounds.\n',row);
	return
      end
      eat.log(indx(row),:) = [];
      len=len-1;
      todaysDate = floor(now);
      eat.indx = find( todaysDate == floor(cell2mat(eat.log(2:end,2))) ) + 1;
      %
      % If deleting an item today:
      indx = eat.indx;
      lenIndx = length(indx);
      %
      % If the item is before today:
      if( length(varargin) ~= 0 )
	indx = find( pastDate == floor(cell2mat(eat.log(2:end,2))) ) + 1;
	lenIndx = length(indx);
      end
      %
      % Recompute columns that accumulate:
      %
      if( len == 1 || lenIndx == 0 )
	return
      elseif( len == 2 || lenIndx < 2 )
	eat.log{indx,6} = eat.log{indx,4};
	eat.log{indx,7} = eat.log{indx,5};
	return
      elseif( row == 1 )
	eat.log{indx(row),6} = eat.log{indx(row),4};
	eat.log{indx(row),7} = eat.log{indx(row),5};
      end
      for( ii=2:lenIndx )       %Already deleted a row.
        eat.log{indx(ii),6} = eat.log{indx(ii),4} + ...
	                      eat.log{indx(ii-1),6};
	eat.log{indx(ii),7} = eat.log{indx(ii),5} + ...
	                      eat.log{indx(ii-1),7};
      end
      if( length(varargin) == 0 )
	eat.sofar
      else
	fprintf('eat.delete - pastDate = %.2f\n',pastDate);
	eat.sofar(pastDate)
      end
    end
    function cereal( eat, bf, wg, m, bb )
    
      indx = eat.indx;
      lenIndx = length(indx);
    
      cbf = bf* 90/29;     pbf = bf*.036;       %Raleys
      cwg = wg* 50/13;     pwg = wg*.0554;      
      cm = m * 130/242.6;  pm  = m *.0116;      
      cbb = bb* 39/72;     pbb = bb*.0067;      
      total = pbf+pwg+pm+pbb;  
      totalCal = cbf+cwg+cm+cbb;
      totalMass = bf+wg+m+bb;

      fprintf('%3d bf   =  %5.1f cal = %3.1f ~= %d', ...
	      bf, cbf, pbf, eat.intPart(pbf));
      eat.printQ(pbf); fprintf('\n');
      fprintf('%3d wg   =  %5.1f cal = %3.1f ~= %d', ...
	      wg, cwg, pwg, eat.intPart(pwg));
      eat.printQ(pwg);fprintf('\n');
      fprintf('%3d milk =  %5.1f cal = %3.1f ~= %d', ...
	      m, cm, pm, eat.intPart(pm));
      eat.printQ(pm);fprintf('\n');
      fprintf('%3d blue =  %5.1f cal = %3.1f ~= %d', ...
	      bb, cbb, pbb, eat.intPart(pbb));
      eat.printQ(pbb);fprintf('\n');
      fprintf('    total=  %5.1f cal = %3.1f ~= %d', ...
	      totalCal, total, eat.intPart(total));
      eat.printQ(total);fprintf('\n');
      
      len = length(eat.log(:,1));
      len = len+1;
      if( bb == 0 || isempty(bb) )
	cerealStr = sprintf('Cereal(%d,%d,%d)',round(bf),round(wg), ...
			    round(m));
      else
	cerealStr = sprintf('Cereal(%d,%d,%d,%d)',round(bf),round(wg), ...
			    round(m),round(bb));
      end
      eat.log{len,1} = cerealStr;
      eat.log{len,2} = now;
      eat.log{len,3} = totalMass;
      eat.log{len,4} = totalCal;
      eat.log{len,5} = total;
      if( len == 2 || lenIndx == 0 )
	eat.log{len,6} = eat.log{len,4};
	eat.log{len,7} = eat.log{len,5};
      else
	eat.log{len,6} = eat.log{len,4} + ...
	                       eat.log{len-1,6};
	eat.log{len,7} = eat.log{len,5} + ...
	                       eat.log{len-1,7};
      end
      eat.sofar
    end
function nq=quarters(eat, value)
%
% Find the nearest 1/4
%
nq = mod(round(value*4),4);
end

function ip = intPart(eat, value)
%
ip = floor(value);
%
% Handle the case where the nearest quarter is the next higher integer,
% i.e. 5.9 -> 6.
%
if(value > 0 & eat.quarters(value) == 0)
    ip = round(value);
end
end

function printQ(eat,value)
%
nq = eat.quarters(value);
%
if(nq)
  if(nq == 2)
    fprintf(' 1/2');
  else
    fprintf(' %d/4', nq);
  end
else
  fprintf('    ');
end
end
    
    % function add(wb, stander)
    %   if( ~isempty(stander))
    % 	if(ischar(stander)) 
    % 	  wb.standers{end+1}=stander;
    % 	  wb.standers  = sort(wb.standers);
    % 	  wb.nstanders = length(wb.standers);
    % 	else
    % 	  fprintf('Add - Error: Please profide a character string.\n');
    % 	  return
    % 	end
    %   end
    % end
    %
    %
    function remove(wb, stander)
      if( ~isempty(stander) & ischar(stander) )
	indx=strcmp(wb.standers,stander);
	if( sum(indx)==0 )
	  fprintf('remove - Error: stander %s is not in the roster.\n', ...
		  stander);
	  return
	end
	wb.standers(indx) = [];
	wb.standers  = sort(wb.standers);
	wb.nstanders = length(wb.standers);
      else
	fprintf('remove - Error: Please profide a character string.\n');
	return
      end
    end
    %
    %
    function create(wb, varargin)
      if( ~isempty(varargin))
	if(ischar(varargin{1})) 
	  try
	    wb.startDateNum = datenum(varargin{1});
	  catch ME
	    fprintf('create - Error: Can''t read the start date %s, %s\n',...
		    varargin{1}, ME.ERRMSG);
	    return
	  end
	end
      end
      wb.nstanders = length(wb.standers);
      if( ~mod(length(wb.standers),3) )
	fprintf(['Watchbill:  The number of standers is a multiple of three.' ...
		 ' Implementing rotation.\n'])
	wb.rotate=1;   
      else
	wb.rotate=0;   
      end
      %
      % Ensure that the standers are alphabetical order:
      wb.standers = sort(wb.standers);
      %
      % Setup statistics array:
      wb.stats = cell(wb.nstanders+2,5);
      wb.stats{1,1} = 'Watch Standers';
      wb.stats{1,2} = 'Number of Day Shifts';
      wb.stats{1,3} = 'Number of Swing Shifts';
      wb.stats{1,4} = 'Number of Graveyard Shifts';
      wb.stats{1,5} = 'Total Number of Shifts';
      wb.stats(3:end,1)     = wb.standers;
      for( i=1:wb.nstanders )
	for(j=1:4)
	  wb.stats{i+2,j+1} = 0;
	end
      end
      %
      % Setup watch bill array:
      wb.bill = cell(wb.nrows*wb.nweeks,8);
      dayno = weekday(wb.startDateNum);
      for( i=0:wb.nweeks )
	wb.bill(i*wb.nrows+1,2:8) = ...
	    {'Sunday','Monday','Tuesday','Wednesday','Thursday',...
	     'Friday','Saturday'};
	wb.bill(i*wb.nrows+3,1) = {'Day  '};
	wb.bill(i*wb.nrows+4,1) = {'Swing'};
	wb.bill(i*wb.nrows+5,1) = {'Grave'};
	j=0;
	while( j<7 )
	  if( i == 0 & j == 0 ) 
	    j=dayno-1;
	    daycntr=wb.startDateNum-1;
	  end
	  daycntr = daycntr+1;
	  wb.bill{i*wb.nrows+2,j+2} = datestr(daycntr,1);
	  j=j+1;
	end
      end
      %
      rowset=0;
      k  = 0;
      %
      %i   = the watch number
      %ii  = corresponding row in the bill
      %kk  = the watchstander index, accounting for rotations.  It should
      %      always point to the same person.
      %k   = the number of rotations
      %j   = the watchstander array index.  When there are no rotations, j and
      %      kk are always the same.
      for( i=1:wb.nweeks*21 )                     
	ii = i+2-floor((i-1)/3)*3 + rowset;       
	j  = i-floor((i-1)/wb.nstanders)*wb.nstanders;
	wb.bill(ii,dayno+1)= wb.standers(j);
	kk = j+k;                        %index back to original list
	if( kk > wb.nstanders ) kk = kk-wb.nstanders;end
	wb.stats{kk+2,5} = wb.stats{kk+2,5} + 1;
	if( ~mod(i-1,3) ) wb.stats{kk+2,4} = wb.stats{kk+2,4} + 1;end  %Grave
	if( ~mod(i-2,3) ) wb.stats{kk+2,2} = wb.stats{kk+2,2} + 1;end  %Day
	if( ~mod(i-3,3) ) wb.stats{kk+2,3} = wb.stats{kk+2,3} + 1;end  %Swing
	if( ~mod(i,3) ) dayno=dayno+1;end
	if( dayno > 7 ) 
	  dayno=1;
	  rowset=rowset+wb.nrows;
	end
	if( wb.rotate && j == wb.nstanders )
	  temp = wb.standers(2:end);
	  temp(end+1) = wb.standers(1);
	  wb.standers=temp;
	  %
	  k=k+1;
	  if( k >= wb.nstanders ) k = 0;end
	end
      end
      %
      % Create cell array suitable for Excel.
      xlswrite('Watchbill.xls',wb.bill);
      xlswrite('WatchbillStats.xls',wb.stats);
    end
  end  %methods
end %classdef
