function g = fn2fm(f,form,sconds)
%FN2FM Convert to specified form.
%
%   FN2FM(F,FORM)  converts the function in F to the specified FORM.
%   Present choices for FORM are: 'pp', 'sp'='B-', 'bb'='BB', 'rp', 'rs'='rB'.
%   See the relevant ..mak commands for specifics of such a form.
%
%   If FORM is 'B-' (and F is in ppform), then the actual smoothness of the
%   function in F across each of its interior breaks has to be guessed. This is
%   done by looking, for each interior break, for the derivative of smallest
%   order whose jump across that break is not `small' compared to the size of 
%   that derivative nearby. The default tolerance used in this is 1.e-12. 
%   But the user can assist by supplying a tolerance (strictly between 0 and 1) 
%   in the optional third argument SCONDS in the command
%
%   FN2FM(F,FORM,SCONDS)
%
%   Alternatively, the user can supply, in SCONDS(i), the correct number of 
%   smoothness conditions to be used across the i-th *interior* break, but must
%   then do so for all interior breaks. If the function in F is a tensor
%   product, then SCONDS, if given, must be a cell array. 
%
%   Also, for a while, the choice 'ol' is allowed, for a reversion from the
%   structured form, pp or B-, to the corresponding former, nonstructured
%   way of storing the ppform or the B-form. Such a reversion is not
%   available for tensor products or any other form not part of the earlier
%   spline toolbox.  Since the MATLAB command spline now (beginning with R11) 
%   returns a spline in structured form, the former, temporary choice 'MA' 
%   for FORM is not needed anymore.
%
%   Finally,
%
%   FN2FM(F)  converts a possibly old version of a form into its present 
%   version.
%
%   For example, 
%   
%       sp = fn2fm(spline(x,y),'sp');
%
%   will give the interpolating cubic spline provided by MATLAB's SPLINE,
%   but in B-form (i.e., described as a linear combination of B-splines).
%
%   As another example,
%
%    p0 = ppmak([0 1],[3 0 0]); p1 = fn2fm(fn2fm(fnrfn(p0,[.4 .6]),'B-'),'pp');
%
%   gives p1 identical to p0 (up to round-off) since the spline has no
%   discontinuity in any derivative across the additional breaks introduced
%   by FNRFN, hence conversion to B-form ignores these additional breaks, and 
%   conversion to ppform does not retain any knot multiplicities (like 
%   the knot multiplicities introduced, by conversion to B-form, at the 
%   endpoints of the spline's basic interval).
%
%   As a final example, 
%
%     nurb = fn2fm(f,'rB');
%
%   will convert the (d+1)-variate spline in  f  into B-form if need be, then
%   convert to rBform, i.e., into a rational spline or nurb, designating its 
%   (d+1)st component as the denominator, provided d>0. 
%
%   See also PP2SP, SP2PP, SP2BB.

%   cb 26aug96, 1sep96, 14mar97, 8oct97, 17oct97, 16nov97, 23nov97, 22apr98,
%      24aug99, 09jan00
%   Copyright 1987-2000 by C. de Boor and The MathWorks, Inc.
%   $Revision: 1.10 $

%%%%%%%%%%%%%%%%%%%%  array versions of univariate forms (from FNBRK)
%   case 10, out1 = 'ppform, univariate';
%   case 11, out1 = 'B-form, univariate';
%   case 12, out1 = 'BBform, univariate';
%   case 15, out1 = 'polynomial in Newton form';

if nargin==1 % set FORM to convert the form in F into its current version
   if isstruct(f)
      form = f.form;
   else
      switch f(1)
      case 10,  form = 'pp';
      case 11,  form = 'B-';
      case 12,  form = 'BB';
      case 15,  form = 'NP';
      otherwise
            error('Input is of unknown (function) form.')
      end
   end
end

switch form
case 'pp'
   if isstruct(f)
      switch f.form
      case 'pp',          g = f;
      case 'rp',          g = f; g.form = 'pp'; g.dim = g.dim+1;
      case 'rB',          f.form = 'B-'; f.dim = f.dim+1; g = sp2pp(f);
      case {'B-','BB'},   g = sp2pp(f);
      otherwise
         error('Cannot convert the given function into ppform.')
      end   
   else
      switch f(1)
      case 10,      [b,c,l,k,d] = ppbrk(f); g = ppmak(b,c,d);
      case {11,12}, g = sp2pp(f);
      case 15
       error('Use NP2PP directly to convert from Newton form to ppform.')
      otherwise
         error('Cannot convert the given function into ppform.')
      end
   end
case {'sp','B-'}
   if isstruct(f)
      switch f.form
      case 'pp'
         if nargin<3, g = pp2sp(f);
         else         g = pp2sp(f,sconds);
         end
      case 'rp',      f.form = 'pp'; f.dim = f.dim+1; g = pp2sp(f);
      case 'rB',      g = f; g.form = 'B-'; g.dim = g.dim+1;
      case 'B-',      g = f;
      case 'BB',      g = pp2sp(sp2pp(f));
      otherwise
         error('Cannot convert the given function into B-form.')
      end   
   else
      switch f(1)
      case 10, g = pp2sp(f);
      case 11, [knots,coefs] = spbrk(f); g = spmak(knots,coefs);
      case 12, g = pp2sp(sp2pp(f));
      otherwise
         error('Cannot convert the given function into B-form.')
      end
   end
case {'bb','BB'}
   if isstruct(f)
      switch f.form
      case 'pp', g = sp2bb(pp2sp(f));          
      case 'rp', f.form = 'pp'; f.dim = f.dim+1; g = sp2bb(pp2sp(f));
      case 'rB', f.form = 'B-'; f.dim = f.dim+1; g = sp2bb(f);
      case 'B-', g = sp2bb(f);
      case 'BB', g = f;
      otherwise
         error('Cannot convert the given function into BBform.')
      end
   else
      switch f(1)
      case 10, g = sp2bb(pp2sp(f));
      case 11, g = sp2bb(f);
      case 12, [knots,coefs] = spbrk(f); g = spmak(knots,coefs); g.form = 'BB';
      otherwise
         error('Cannot convert the given function into BBform.')
      end
   end
case 'rp'  % switch first into ppform if need be
   if isstruct(f)
      switch f.form
      case 'pp',      g = f;
      case 'rp'
      case 'rB',      g = sp2pp(fn2fm(f,'B-'));
      case {'B-','BB'}, g = sp2pp(f);
      otherwise
         error('Cannot convert the given function into rpform.')
      end   
   else
      switch f(1)
      case 10,      [b,c,l,k,d] = ppbrk(f); g = ppmak(b,c,d);
      case {11,12}, g = sp2pp(f);
      otherwise
         error('Cannot convert the given function into rpform.')
      end
   end
   if exist('g')
      g.form = 'rp'; g.dim = g.dim-1;
      if g.dim<1
         error('A rational spline must have more than one component.')
      end
   else
      g = f;
   end
case 'rB'  % switch first into B-form if need be
   if isstruct(f)
      switch f.form
      case 'pp'
         if nargin<3, g = pp2sp(f);
         else         g = pp2sp(f,sconds);
         end
      case 'rp',      g = fn2fm(fn2fm(f,'pp'),'sp');
      case 'rB'
      case {'B-','BB'}, g = f;
      otherwise
         error('Cannot convert the given function into rBform.')
      end   
   else 
      switch f(1)
      case 10, g = pp2sp(f);
      case 11, g = spmak(fnbrk(f,'knots'),fnbrk(f,'coefs'));
      case 12, g = pp2sp(sp2pp(f));
      otherwise
         error('Cannot convert the given function into rBform.')
      end
   end
   if exist('g')
      g.form = 'rB'; g.dim = g.dim-1;
      if g.dim<1
         error('A rational spline must have more than one component.')
      end
   else
      g = f;
   end
case 'NP' % at present, this form only exists in the old version
   if isstruct(f)
      switch f.form
      case 'NP', g = f;
      otherwise
         error('Cannot convert the given function into NPform.')
      end
   else
      switch f(1)
      case 15, g = f;
      otherwise
         error('Cannot convert the given function into NPform.')
      end
   end
case 'MA'     % convert univariate spline to old, nonstructure ppform.
   if isstruct(f)&~iscell(f.order)
      switch f.form
      case 'pp' 
         g = [10 f.dim f.pieces f.breaks(:).' f.order f.coefs(:).'];
      case {'B-','BB'}
         f = sp2pp(f);
         g = [10 f.dim f.pieces f.breaks(:).' f.order f.coefs(:).'];
      otherwise
         error('Cannot convert the given function into MATLAB''s ppform.')
      end
   else
      g = f;
   end
case 'ol'    % convert univariate structured form to corresponding 
             % formerly used array form.
   if ~isstruct(f)
      error('When FORM=''ol'', F must be a structure.')
   else
      if length(f.order)>1
         error(['Reversion to array is only possible for',...
                ' univariate functions.'])
      else
         switch f.form
         case 'pp' 
            g = [10 f.dim f.pieces f.breaks(:).' f.order f.coefs(:).'];
         case {'B-','BB'}
            g = [11 f.dim f.number f.coefs(:).' f.order f.knots(:).'];
            if f.form=='BB', g(1) = 12; end
         otherwise
            error('Unknown function type encountered.')
         end
      end
   end
otherwise
   error(['The specified form, ''',form,''', is not (yet) recognized.']),
end
