(* :Title: Newton *)


(* :Context: ProgrammingInMathematica`Newton` *)

(* :Author: Roman E. Maeder *)

(* :Summary:
   Newton iteration for zeroes and fixed points of functions
 *)

(* :Copyright: © 1989-1996 by Roman E. Maeder *)

(* :Package Version: 2.0 *)

(* :Mathematica Version: 3.0 *)

(* :History:
   2.0 for Programming in Mathematica, 3rd ed.
   1.1 for Programming in Mathematica, 2nd ed.
   1.0 for Programming in Mathematica, 1st ed.
*)

(* :Keywords: Newton iteration, root of function, fixed point *)

(* :Sources:
   Roman E. Maeder. Programming in Mathematica, 3rd ed. Addison-Wesley, 1996.
*)

(* :Discussion:
   See Section 7.2 of "Programming in Mathematica"
*)

BeginPackage["ProgrammingInMathematica`Newton`"]

NewtonZero::usage = "NewtonZero[f, x0] finds a zero of the function f using
	the initial guess x0 to start the iteration. NewtonZero[expr, x, x0]
	finds a zero of expr as a function of x. The recursion limit
	determines the maximum number of iteration steps that are performed."

NewtonFixedPoint::usage = "NewtonFixedPoint[f, x0] finds a fixed point
	of the function f using the initial guess x0 to start the iteration.
	NewtonFixedPoint[expr, x, x0] finds a fixed point of expr as a
	function of x."

Options[NewtonZero] = Options[NewtonFixedPoint] = {
	MaxIterations :> $RecursionLimit,
	AccuracyGoal -> Automatic,
	WorkingPrecision -> Automatic
}

Newton::noconv = "Iteration did not converge in `1` steps."

Begin["`Private`"]

extraPrecision = 10 (* the extra working precision *)

NewtonZero[ f_, x0_, opts___?OptionQ ] :=
    Module[{res, maxiter, accugoal, workprec, x = x0},
      {maxiter, accugoal, workprec} =
          {MaxIterations, AccuracyGoal, WorkingPrecision} /.
              Flatten[{opts}] /. Options[NewtonZero];
      With[{fp = f'},
        If[ accugoal === Automatic,
            accugoal = Max[Precision[x0], $MachinePrecision] ];
        If[ accugoal == Infinity, (* exact *)
            res = FixedPoint[(# - f[#]/fp[#])&, x, maxiter];
            If [ !TrueQ[f[res] === 0], Message[Newton::noconv, maxiter] ];
          , (* else approximate *)
            If[ workprec === Automatic, workprec = accugoal;
              If[ accugoal > $MachinePrecision, workprec += extraPrecision ];
            ];
            x = SetPrecision[x, workprec];
            If[x === 0, x = SetAccuracy[x, workprec]];
            Block[{$MaxPrecision = workprec},
              res = FixedPoint[(# - f[#]/fp[#])&, x, maxiter] ];
            If [ !TrueQ[Abs[f[res]] <= 10.0^-accugoal],
                 Message[Newton::noconv, maxiter] ];
        ];
        res
    ] ]

NewtonZero[ expr_, x_, x0_, opts___?OptionQ ] :=
	NewtonZero[ Function[x, expr], x0, opts ]

optnames = First /@ Options[ NewtonFixedPoint ]

NewtonFixedPoint[ f_, x0_, opts___?OptionQ ] :=
    Module[{optvals},
        optvals = optnames /. Flatten[{opts}] /. Options[NewtonFixedPoint];
        NewtonZero[ (f[#] - #)&, x0, Thread[optnames -> optvals]]
    ]

NewtonFixedPoint[ expr_, x_, x0_, opts___?OptionQ ] :=
	NewtonFixedPoint[ Function[x, expr], x0, opts ]

End[ ]

Protect[ NewtonZero, NewtonFixedPoint ]

EndPackage[ ]
