function [ ydot, stepBound, schemeData ] = termCurvatureByMedian(t, y, schemeData)
% termCurvatureByMean: approximate a motion by mean curvature term in an HJ PDE.
%
% [ ydot, stepBound, schemeData ] = termCurvatureByMedian(t, y, schemeData)
%
% Computes an approximation of motion by mean curvature for a
% Hamilton-Jacobi PDE.  This is a second order equation that simplifies to a
% heat equation if the function is a signed distance function.
% Specifically:
%
%                 D_t \phi - b(x,t) \kappa(x) \| \grad \phi \| = 0.
%
% where \kappa(x) is the mean curvature.
%
% This code is designed to be a replacement for the termCurvature routine in
% ToolboxLS (which uses centered differences and is therefore potentially
% non-monotone).
%
% The scheme used is taken from
%
%    Adam M. Oberman, "A Convergent Monotone Difference Scheme for Motion of
%    Level Sets by Mean Curvature," Numerische Mathematik, 99: 365-379
%    (2004), DOI: 10.1007/s00211-004-0566-1.
%
% It requires no finite difference.  Instead, the median of all nodes a
% fixed distance from the node to be updated is used as the update.
%
% Input Parameters:
%
%   t: Time at beginning of timestep.
%
%   y: Data array in vector form.
%
%   schemeData: A structure (see below).
%
% Output Parameters:
%
%   ydot: Change in the data array, in vector form.
%
%   stepBound: CFL bound on timestep for stability.
%
%   schemeData: The same as the input argument (unmodified).
%
% schemeData is a structure containing data specific to this type of 
% term approximation.  For this function it contains the field(s)
%
%   .grid: Grid structure (see processGrid.m for details).
%
%   .b: Multiplier (should be non-negative for well-posedness); b may be a
%   scalar constant or an array of size(data).  See below.
%
%   .stencil_size: Positive Integer.  Distance (in grid cells) to look for
%   the set of nodes among which the median is found.  Not a precise
%   measure; for example, stencil_size == 1 corresponds in 2D to examining
%   the 8 neighbour nodes (including diagonals).  In grids with variable
%   dx, the stencil_size is multiplied by the largest dx.  If dx varies
%   wildly, this scaling may result in enormous stencils and very high
%   computational cost.
%
%   .stencil_shape: String.  Shape of the stencil.  Current choices are
%   'circle' or 'square'.  The former is the shape whose convergence is
%   proved in Oberman.  The latter is faster and may even be more accurate
%   if interpolation is used, but there is no proof of its consistency.
%   Optional.  Default is 'circle'.
%
%   .interpolate: Boolean.  For nodes in the stencil that are not precisely
%   at the desired distance (given by stencil_size), we can use linear
%   interpolation to estimate the value at the desired distance.  Otherwise,
%   we can just use the node value directly.  Optional.  Default is 0 (use
%   the node value directly with no interpolation).
%   
%   .passVLS: An optional boolean used for vector level sets (see below).
%   Default is 0 (ie ignore vector level sets).
%
% It may contain additional fields at the user's discretion.
%
% schemeData.b can provide the multiplier in one of two ways:
%
%   1) For time invariant multipliers, a scalar or an array the same 
%      size as data.
%
%   2) For general multipliers, a function handle to a function with prototype
%      b = scalarGridFunc(t, data, schemeData), where the output b is the
%      scalar/array from (1) and the input arguments are the same as those
%      of this function (except that data = y has been reshaped to its
%      original size).  In this case, it may be useful to include additional
%      fields in schemeData.  
%
% For evolving vector level sets, y may be a cell vector.  If y is a cell
% vector, schemeData may be a cell vector of equal length.  In this case all
% the elements of y (and schemeData if necessary) are ignored except the
% first.  As a consequence, if schemeData.b is a function handle the call to
% scalarGridFunc will be performed with a regular data array and a single
% schemeData structure (as if no vector level set was present).
%
% This default behavior of ignoring the vector level set in the call to
% scalarGridFunc may be overridden by setting schemeData.passVLS = 1.  In
% this case the data argument (and schemeData argument, if necessary) in the
% call to scalarGridFunc will be the full cell vectors.  The current data
% array (and schemeData structure, if necessary) will be the first element
% of these cell vectors.  In order to properly reshape the other elements of
% y, the corresponding schemeData structures must contain an appropriate
% grid structure.

% Copyright 2007 Ian M. Mitchell (mitchell@cs.ubc.ca).
% This software is used, copied and distributed under the licensing 
%   agreement contained in the file LICENSE in the top directory of 
%   the distribution.
%
% Ian Mitchell 1/15/07.

  %---------------------------------------------------------------------------
  % Deal with potential for vector level set data.
  if(iscell(schemeData))
    thisSchemeData = schemeData{1};
  else
    thisSchemeData = schemeData;
  end

  checkStructureFields(thisSchemeData, 'grid', 'b', 'stencil_size');

  grid = thisSchemeData.grid;
  stencil_size = thisSchemeData.stencil_size;

  if(iscell(y))
    data = reshape(y{1}, grid.shape);    
  else
    data = reshape(y, grid.shape);
  end

  %---------------------------------------------------------------------------
  % Shape of stencil has defaults or can be user defined.
  if isfield(thisSchemeData, 'stencil_shape')
    switch thisSchemeData.stencil_shape
     case { 'square', 'circle' }
      % These are accepted shapes.
      stencil_shape = thisSchemeData.stencil_shape;
  
     otherwise
      error('Unknown stencil_shape: %s', thisSchemeData.stencil_shape);
    end
  
  else
    % Default.
    stencil_shape = 'circle';
  end

  %---------------------------------------------------------------------------
  % Whether to interpolate or not has default or can be user defined.
  if isfield(thisSchemeData, 'interpolate')
    interpolate = thisSchemeData.interpolate;
  else
    % Default.
    interpolate = 0;
  end

  %---------------------------------------------------------------------------
  % Get multiplier from whatever format it is supplied.
  if(isa(thisSchemeData.b, 'double'))
    b = thisSchemeData.b;

  elseif(isa(thisSchemeData.b, 'function_handle'))

    if(iscell(y))
      % If there is a vector level set.

      if(isfield(thisSchemeData, 'passVLS') && thisSchemeData.passVLS)
        % Pass the vector level set information through.
        numY = length(y);
        dataV = cell(numY, 1);
        for i = 1 : numY
          if(iscell(schemeData))
            dataV{i} = reshape(y{i}, schemeData{i}.grid.shape);
          else
            dataV{i} = reshape(y{i}, schemeData.grid.shape);
          end
        end
        b = feval(thisSchemeData.b, t, dataV, schemeData);

      else
        % Ignore any vector level set.
        b = feval(thisSchemeData.b, t, data, thisSchemeData);
      end

    else
      % There is no vector level set.
      b = feval(thisSchemeData.b, t, data, thisSchemeData);
    end


  else
    error('schemeData.b must be a scalar, array or function handle');

  end

  
  %---------------------------------------------------------------------------
  % Get the one Laplacian.
  [ one_laplacian, stencil_radius ] = ...
      oneLaplacian(grid, data, stencil_size, stencil_shape, interpolate);

  % Determine the update.
  delta = -b .* one_laplacian;
  
  % Benefit of a larger stencil is larger timestep limits.
  stepBound = stencil_radius^2 / (2 * max(b(:)));
  
  % Reshape output into vector format and negate for RHS of ODE.
  ydot = -delta(:);
