module odesolve$  % Header for ordinary differential equation solver

% Authors: F. J. Wright and M. A. H. MacCallum
% Maintainer: F.J.Wright@maths.qmw.ac.uk, Time-stamp: <14 August 2001>

algebraic ODESolve_version := "ODESolve 1.065"$

global '(ODESolve!-subpackages!*)$

% Build needs repeating if this list is changed!

ODESolve!-subpackages!* := '(
   odeintfc   % User interface and condition code (FJW)
   odetop     % Top level ODESolve routines (FJW / MAHM)
   odelin     % Simple linear ODE solvers (MAHM / FJW)
   odespcfn   % Linear special function ODEs (FJW)
   odenon1    % Special form nonlinear ODEs of order 1 (MAHM / FJW)
   odenonn    % Special form nonlinear ODEs of order > 1 (FJW)
   odepatch   % Temporary REDUCE patches and extensions (FJW)
   )$

create!-package('odesolve . ODESolve!-subpackages!*, nil)$

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Common variable type declarations and macro definitions

% Switches to select solution form where possible -- off by default.
% Can also be set locally by options to odesolve.
switch odesolve_explicit$               % fully explicit
switch odesolve_expand$                 % expand roots of unity
switch odesolve_full$                   % fully explicit and expanded
switch odesolve_implicit$               % not parametric
switch odesolve_noint$                  % turn off selected integrations
switch noint$                           % turn off integration globally
switch odesolve_verbose$                % display ode & conditions
switch odesolve_basis$                  % output basis as linear ODE solution
switch odesolve_noswap$                 % do not swap variables
switch odesolve_norecurse$              % no recursion => noswap
% The `noswap' and `norecurse' switches are mainly for debugging.
switch odesolve_fast$                   % no heuristics => norecurse
% The `fast' switch disables all non-deterministic solution techniques
% (including most of those for nonlinear ODEs of order > 1).  it is
% useful if ODESolve is used as a service routine, including calling
% it recursively in a hook.  It makes ODESolve 1+ behave like the
% odesolve distributed with REDUCE versions up to and including 3.7,
% and so does not affect the odesolve.tst file.
switch odesolve_check$                  % check solution

%% switch odesolve_load_specfn$  !*odesolve_load_specfn := t$
% If on (the default) then autoload the specfn package if a solution
% is returned that involves special functions.  It can be turned off
% to save resources if ODE solutions will not be further manipulated,
% e.g. if conditions will NOT be imposed.

% Switches controlled by ODESolve:
fluid '(!*evallhseqp !*multiplicities !*div !*intstr
   !*exp !*mcd !*factor !*ifactor !*precise !*fullroots !*trigform)$

% REDUCE global variables manipulated by ODESolve:
fluid '(kord!* depl!*)$

% Common global ODESolve variables:
fluid '(!*odesolve!-solvable!-xy)$

symbolic operator member, delete, !*eqn2a, depends, smember, gensym$

symbolic smacro procedure eqnp u;
   eqcar(u, 'equal)$

symbolic smacro procedure ODESolve!-basisp soln;
   rlistp cadr soln and not eqnp cadadr soln$

% The following two statements are needed in case SOLVE has not been
% loaded before compiling this package.

global '(multiplicities!*)$  share multiplicities!*$

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Tracing support

switch trode$                           % trace the algorithms used
% Assign a numerical value to !*trode for extra algorithm tracing:
share !*trode$

% rlistat is not flagged eval, so for faslout ...
deflist('((traceode rlis) (traceode!* rlis) (traceode1 rlis)), 'stat)$

global '(TraceOde!-InputList)$

symbolic procedure traceode!-print u;
   %% Print sequence of elements and terminate line with linefeed.
   %% Returns nil.
   begin scalar alg;  % non-nil if any algebraic elements to print
      alg := u := revlis nconc(TraceOde!-InputList, u);
      TraceOde!-InputList := nil;
      while alg and atom car alg do alg := cdr alg;
      if alg then <<
         terpri!* t;
         for each el in u do maprin el;
         terpri!* t
      >> else <<
         for each el in u do prin2 el;
         terpri()
      >>
   end$

symbolic procedure traceode u;
   %% Print sequence of elements and terminate line with linefeed.
   %% Returns nil.
   if !*trode then traceode!-print u$

symbolic procedure traceode!* u;
   %% Print line WITHOUT linefeed:
   %% Returns nil.
   if !*trode then
      %% Assignment necessary when TraceOde!-InputList is null:
      begin TraceOde!-InputList := nconc(TraceOde!-InputList, u) end$

symbolic procedure traceode1 u;
   %% Extra tracing -- print line with linefeed:
   %% Returns nil.
   if !*trode = 1 then traceode!-print u$

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Arbitrary constants in solutions:

algebraic operator arbconst$
algebraic (!!arbconst := 0)$

algebraic procedure newarbconst();
   arbconst(!!arbconst := !!arbconst + 1)$


% General utilities:

algebraic procedure ode!-int(y, x);
   %% Currently used only in `ODESolve!-PI' in module `odelin', but
   %% should probably be used more widely, so moved here!
   int(trigsimp y, x)$

algebraic procedure ODESolve!-multi!-int(y, x, m);
   %% Integate y wrt x m times:
   %% REVISE TO INTEGRATE JUST ONCE (cf. trivial n'th order ODEs)?
   if m > 0 then ODESolve!-multi!-int(int(y,x), x, m-1) else y$

%% algebraic procedure odefailure(ode);
%%    <<
%%       %% This message moved to ODESolve!-nonlinear:
%%       %% traceode "This version of ODESOLVE cannot solve ",
%%       %%    "equations of the type given.";
%%       {ode=0}
%%    >>$

algebraic operator odesolve!-df$

endmodule$

end$
