%mathpiper,def="Newton"

Function("Newton",{function,variable,initial,accuracy})
[	// since we call a function with HoldArgument(), we need to evaluate some variables by hand
  `Newton(@function,@variable,initial,accuracy,-Infinity,Infinity);
];

Function("Newton",{function,variable,initial,accuracy,min,max})
[
  Local(result,adjust,delta,requiredPrec);
  MacroLocal(variable);
  requiredPrec := BuiltinPrecisionGet();
  accuracy:=N((accuracy/10)*10); // Making sure accuracy is rounded correctly
  BuiltinPrecisionSet(requiredPrec+2);
  function:=N(function);
  adjust:= -function/Apply("Differentiate",{variable,function});
  delta:=10000;
  result:=initial;
  While (result > min And result < max
      // avoid numerical underflow due to fixed point math, FIXME when have real floating math
      And N(Eval( Maximum(Re(delta), -Re(delta), Im(delta), -Im(delta)) ) ) > accuracy)
  [
    MacroBind(variable,result);
    delta:=N(Eval(adjust));
    result:=result+delta;
  ];

  BuiltinPrecisionSet(requiredPrec);
  result:=N(Eval((result/10)*10)); // making sure result is rounded to correct precision
  if (result <= min Or result >= max) [result := Fail;];
  result;
];

%/mathpiper



%mathpiper_docs,name="Newton",categories="User Functions;Solvers (Numeric)
*CMD Newton --- solve an equation numerically with Newton's method
*STD
*CALL
	Newton(expr, var, initial, accuracy)
	Newton(expr, var, initial, accuracy,min,max)

*PARMS

{expr} -- an expression to find a zero for

{var} -- free variable to adjust to find a zero

{initial} -- initial value for "var" to use in the search

{accuracy} -- minimum required accuracy of the result

{min} -- minimum value for "var" to use in the search

{max} -- maximum value for "var" to use in the search

*DESC

This function tries to numerically find a zero of the expression
{expr}, which should depend only on the variable {var}. It uses
the value {initial} as an initial guess.

The function will iterate using Newton's method until it estimates
that it has come within a distance {accuracy} of the correct
solution, and then it will return its best guess. In particular, it
may loop forever if the algorithm does not converge.

When {min} and {max} are supplied, the Newton iteration takes them
into account by returning {Fail} if it failed to find a root in
the given range. Note this doesn't mean there isn't a root, just
that this algorithm failed to find it due to the trial values
going outside of the bounds.

*E.G.

In> Newton(Sin(x),x,3,0.0001)
Result: 3.1415926535;
In> Newton(x^2-1,x,2,0.0001,-5,5)
Result: 1;
In> Newton(x^2+1,x,2,0.0001,-5,5)
Result: Fail;

*SEE Solve, NewtonNum
%/mathpiper_docs