%mathpiper,def="NewtonNum"

/// coded by Serge Winitzki. See essays documentation for algorithms.

//////////////////////////////////////////////////
/// Numerical method: Newton-like superconvergent iteration
//////////////////////////////////////////////////

// Newton's method, generalized, with precision control and diagnostics

/// auxiliary utility: compute the number of common decimal digits of x and y (using relative precision)
Common'digits(x,y) :=
[
	Local(diff);
	diff := Abs(x-y);
	If(
		diff=0,
		Infinity,
		// use approximation Ln(2)/Ln(10) > 351/1166
		Quotient(IntLog(FloorN(DivideN(Maximum(Abs(x), Abs(y)), diff)), 2)*351, 1166)
	); 	// this many decimal digits in common
];

///interface
NewtonNum(_func, _x0) <-- NewtonNum(func, x0, 5);	// default prec0
NewtonNum(_func, _x0, _prec0) <-- NewtonNum(func, x0, prec0, 2);

// func is the function to iterate, i.e. x' = func(x).
// prec0 is the initial precision necessary to get convergence started.
// order is the order of convergence of the given sequence (e.g. 2 or 3).
// x0 must be close enough so that x1 has a few common digits with x0 after at most 5 iterations.
NewtonNum(_func, _x'init, _prec0, _order) <--
[
	Check(prec0>=4, "Argument", "NewtonNum: Error: initial precision must be at least 4");
	Check(IsInteger(order) And order>1, "Argument", "NewtonNum: Error: convergence order must be an integer and at least 2");
	Local(x0, x1, prec, exact'digits, int'part, initial'tries);
  N([
    x0 := x'init;
    prec := BuiltinPrecisionGet();
    int'part := IntLog(Ceil(Abs(x0)), 10);	// how many extra digits for numbers like 100.2223
    // int'part must be set to 0 if we have true floating-point semantics of BuiltinPrecisionSet()
    BuiltinPrecisionSet(2+prec0-int'part);	// 2 guard digits
    x1 := (func @ x0);	// let's run one more iteration by hand
    // first, we get prec0 exact digits
    exact'digits := 0;
    initial'tries := 5;	// stop the loop the the initial value is not good
    While(exact'digits*order < prec0 And initial'tries>0)
    [
      initial'tries--;
      x0 := x1;
      x1 := (func @ x0);
      exact'digits := Common'digits(x0, x1);
  //		If(InVerboseMode(), Echo("NewtonNum: Info: got", exact'digits, "exact digits at prec. ", BuiltinPrecisionGet()));
    ];
    // need to check that the initial precision is achieved
    If(
      Assert("value", {"NewtonNum: Error: need a more accurate initial value than", x'init})
        exact'digits >= 1,
    [
    exact'digits :=Minimum(exact'digits, prec0+2);
    // run until get prec/order exact digits
    int'part := IntLog(Ceil(Abs(x1)), 10);	// how many extra digits for numbers like 100.2223
    While(exact'digits*order <= prec)
    [
      exact'digits := exact'digits*order;
      BuiltinPrecisionSet(2+Minimum(exact'digits, Quotient(prec,order)+1)-int'part);
      x0 := x1;
      x1 := (func @ x0);
  //		If(InVerboseMode(), Echo("NewtonNum: Info: got", Common'digits(x0, x1), "exact digits at prec. ", BuiltinPrecisionGet()));
    ];
    // last iteration by hand
    BuiltinPrecisionSet(2+prec);
    x1 := RoundTo( (func @ x1), prec);
    ],
    // did not get a good initial value, so return what we were given
    x1 := x'init
    );
    BuiltinPrecisionSet(prec);
  ]);
	x1;
];


/*
example: logarithm function using cubically convergent Newton iteration for
Exp(x/2)-a*Exp(-x/2)=0:

x' := x - 2 * (Exp(x)-a) / (Exp(x)+a)

LN(x_IsNumber)_(x>1 ) <--
	LocalSymbols(y)
[
// initial guess is obtained as Ln(x^2)/Ln(2) * (Ln(2)/2)
	NewtonNum({{y},4*x/(Exp(y)+x)-2+y}, N(794/2291*IntLog(Floor(x*x),2),5), 10, 3);
];
*/

%/mathpiper



%mathpiper_docs,name="NewtonNum",categories="User Functions;Solvers (Numeric)
*CMD NewtonNum --- low-level optimized Newton's iterations
*STD
*CALL
	NewtonNum(func, x0, prec0, order)
	NewtonNum(func, x0, prec0)
	NewtonNum(func, x0)

*PARMS

{func} -- a function specifying the iteration sequence

{x0} -- initial value (must be close enough to the root)

{prec0} -- initial precision (at least 4, default 5)

{order} -- convergence order (typically 2 or 3, default 2)

*DESC

This function is an optimized interface for computing Newton's
iteration sequences for numerical solution of equations in arbitrary precision.

{NewtonNum} will iterate the given function starting from the initial
value, until the sequence converges within current precision.
Initially, up to 5 iterations at the initial precision {prec0} is
performed (the low precision is set for speed). The initial value {x0}
must be close enough to the root so that the initial iterations
converge. If the sequence does not produce even a single correct digit
of the root after these initial iterations, an error message is
printed. The default value of the initial precision is 5.

The {order} parameter should give the convergence order of the scheme.
Normally, Newton iteration converges quadratically (so the default
value is {order}=2) but some schemes converge faster and you can speed
up this function by specifying the correct order. (Caution: if you give
{order}=3 but the sequence is actually quadratic, the result will be
silently incorrect. It is safe to use {order}=2.)

*REM
The verbose option {V} can be used to monitor the convergence. The
achieved exact digits should roughly form a geometric progression.

*E.G.
In> BuiltinPrecisionSet(20)
Result: True;
In> NewtonNum({{x}, x+Sin(x)}, 3, 5, 3)
Result: 3.14159265358979323846;

*SEE Newton
%/mathpiper_docs