function [ err_norms, err, g ] = ...
    polynomialCircleTest(grid_size, which_polynomial, stencil_size, ns)
% polynomialCircleTest: Test curvature approximation on a polynomial function.
%
%   [ err_norms, err, g ] = 
%      polynomialCircleTest(grid_size, which_polynomial, stencil_size, ns)
%
% Run either of the two 2D polynomial test cases from Table 1 of
%
%    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.
%
% In this case, we construct an exact circle of sample points around each
% node in the grid, and sample the exact polynomial at the appropriate
% locations before finding the median.  Consequently, the errors shown by
% this test are optimistic with respect to what could actually be achieved
% on a computational grid, where the sample points do not lie precisely
% on a circle.
%
% Input Parameters:
%
%   grid_size: Positive Integer.  Minimum number of grid cells in each
%   dimension.
%
%   which_polynomial: Positive Integer.  Which polynomial to test
%   against.  Current choices are 1 or 2.  See Oberman table 1 for the
%   specification of the polynomials.
%
%   stencil_size: Positive Integer.  Passed directly to oneLaplacian.m
%
%   ns: Positive Integer.  Number of samples in the circle.
%
% Output Paramters:
%
%   err_norms: Structure.  Members are .inf, .one, and .two.  Each stores
%   the appropriate error metric over the grid of the approximation.
%
%   err: Array.  Stores the error in the approximation at each node.
%
%   g: Grid structure on which the approximation is constructed.

% 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/20/07

  run('../../addPathToKernel');

  %---------------------------------------------------------------------------
  % Create the grid.
  g.dim = 2;
  g.min = -1;
  g.max = +1;
  g.bdry = @addGhostExtrapolate;
  g.N = grid_size;
  g = processGrid(g);

  stencil_radius = stencil_size * g.dx(1);
  
  %---------------------------------------------------------------------------
  % These curvature solutions were computed using the symbolic math 
  % toolbox and polynomialSymbolic.m
  switch(which_polynomial)
   case 1
    polynomial = inline('6*x+5/4*y+9/4*x.^2+14/5*x.*y-13/5*y.^2', 'x', 'y');
    curvature = inline([ '-1/10*(888675+1499520*x+312400*y' ...
                         '+562320*x.^2+699776*x.*y-649792*y.^2)' ...
                         './(15025+24400*x+8240*y' ...
                         '+11236*x.^2-1568*x.*y+13952*y.^2)' ], 'x', 'y');
   case 2
    polynomial = inline('x-1/100*y+1/4*x.^2+31/10*x.*y-y.^2', 'x', 'y');
    curvature = inline([ '-1/2*(38759+424400*x-4244*y' ...
                         '+106100*x.^2+1315640*x.*y-424400*y.^2)' ...
                         './(10001+9380*x+62400*y' ...
                         '+98600*x.^2-93000*x.*y+136100*y.^2)' ], 'x', 'y');
    
   otherwise
    error('Unknown polynomial number: %s', which_polynomial);
  end
  
  %---------------------------------------------------------------------------
  % Analytic curvature.
  curv_analytic = feval(curvature, g.xs{1}, g.xs{2});
  poly_analytic = feval(polynomial, g.xs{1}, g.xs{2});
  
  %---------------------------------------------------------------------------
  % For each node in the grid, get a circle of sample points around it.
  % These sample points will NOT be on the grid.

  % First, points on a circle.
  angles = linspace(0, 2*pi, ns + 1);
  angles = angles(1:end-1);
  circle_x = stencil_radius * cos(angles);
  circle_y = stencil_radius * sin(angles);

  curv_approx_median = zeros(size(curv_analytic));
  
  for i = 1 : prod(g.N)
    xs = g.xs{1}(i) + circle_x;
    ys = g.xs{2}(i) + circle_y;
    poly = feval(polynomial, xs, ys);
    curv_approx_median(i) = ((median(poly) - poly_analytic(i)) ...
                             * 2 / stencil_radius^2);
  end
  
  %---------------------------------------------------------------------------
  % What does the error look like?
  abs_err = abs(curv_approx_median - curv_analytic);
  rel_err = abs_err ./ abs(curv_analytic);
  err = min(rel_err, abs_err);
  err = abs_err;
  
  err_norms.inf = max(err(:));
  err_norms.one = mean(err(:));
  err_norms.two = norm(err(:)) / sqrt(length(err(:)));

  figure;
  surf(g.xs{1}, g.xs{2}, err);
  xlabel('x');  ylabel('y');  zlabel('error');
  title({ [ 'Error in curvature approx for p' num2str(which_polynomial) ...
            ' on grid size ' num2str(grid_size) ...
            ' with stencil size ' num2str(stencil_size) ] });

  figure
  surf(g.xs{1}, g.xs{2}, curv_analytic);
  title({ [ 'Analytic curvature for p' num2str(which_polynomial) ...
            ' on grid size ' num2str(grid_size) ] });
  figure
  surf(g.xs{1}, g.xs{2}, curv_approx_median);
  title({ [ 'Median curvature approx for p' num2str(which_polynomial) ...
            ' on grid size ' num2str(grid_size) ...
            ' with stencil size ' num2str(stencil_size) ] });
